use std::io;
use std::mem;
use std::marker::PhantomData;
use std::convert::From;
use std::ffi::CString;
use backbone::{RawFd, AsRawFd, AF_INET, c_void, c_char, sockaddr, sockaddr_in, close, ioctl, socket};
use IoControl;
use ip::{LlAddr, IpAddrV4, Udp};
const IFNAMSIZ: usize = 16;
const SIOCGIFNAME: i32 = 0x8910;
const SIOCGIFFLAGS: i32 = 0x8913;
const SIOCSIFFLAGS: i32 = 0x8914;
const SIOCGIFADDR: i32 = 0x8915;
const SIOCGIFDSTADDR: i32 = 0x8917;
const SIOCGIFBRDADDR: i32 = 0x8919;
const SIOCGIFNETMASK: i32 = 0x891b;
const SIOCGIFMTU: i32 = 0x8921;
const SIOCSIFMTU: i32 = 0x8922;
const SIOCSIFHWADDR: i32 = 0x8924;
const SIOCGIFHWADDR: i32 = 0x8927;
const SIOCGIFINDEX: i32 = 0x8933;
pub trait IfreqName : Sized {
fn name() -> i32;
}
#[repr(C)]
struct ifreq {
ifr_name: [c_char; IFNAMSIZ],
union: [u8; 24],
}
pub struct Ifreq<T> {
ifr: ifreq,
marker: PhantomData<T>,
}
impl<T> Ifreq<T> {
pub fn new<U: Into<Vec<u8>>>(name: U) -> io::Result<Ifreq<T>> {
match CString::new(name.into()) {
Ok(s) => {
let bytes = s.as_bytes();
if bytes.len() >= IFNAMSIZ {
return Err(io::Error::new(io::ErrorKind::Other, "too long name"));
}
let mut ifr: ifreq = unsafe { mem::zeroed() };
ifr.ifr_name[..bytes.len()].copy_from_slice(unsafe { mem::transmute(bytes) });
Ok(Ifreq {
ifr: ifr,
marker: PhantomData,
})
},
Err(err) => Err(io::Error::from(err)),
}
}
pub fn from_index(index: u32) -> io::Result<Ifreq<T>> {
let soc = try!(IfreqSocket::new());
let mut ifr: Ifreq<IfreqGetNameT> = Ifreq {
ifr: unsafe { mem::zeroed() },
marker: PhantomData,
};
ifr.set_index(index);
try!(soc.io_control(&mut ifr));
Ok(ifr.into())
}
pub fn into<U>(self) -> Ifreq<U> {
Ifreq {
ifr: self.ifr,
marker: PhantomData,
}
}
fn get_i16(&self) -> i16 {
unsafe { *(self.ifr.union.as_ptr() as *const i16) }
}
fn set_i16(&mut self, data: i16) {
unsafe { *(self.ifr.union.as_mut_ptr() as *mut i16) = data }
}
fn get_i32(&self) -> i32 {
unsafe { *(self.ifr.union.as_ptr() as *const i32) }
}
fn set_i32(&mut self, data: i32) {
unsafe { *(self.ifr.union.as_mut_ptr() as *mut i32) = data }
}
fn _get_hwaddr(&self) -> LlAddr {
let sa: &sockaddr = unsafe { mem::transmute(&self.ifr.union) };
LlAddr::from_bytes(unsafe { *(sa.sa_data.as_ptr() as *const [u8; 6]) })
}
fn _set_hwaddr(&mut self, data: LlAddr) {
let sa: &mut sockaddr = unsafe { mem::transmute(&mut self.ifr.union) };
sa.sa_family = 0x0304u16.to_be();
unsafe { *(sa.sa_data.as_mut_ptr() as *mut [u8; 6]) = *data.as_bytes() };
}
fn _get_ipaddr(&self) -> IpAddrV4 {
let sin: &sockaddr_in = unsafe { mem::transmute(&self.ifr.union) };
IpAddrV4::from_bytes(unsafe { mem::transmute(sin.sin_addr) })
}
fn _set_ipaddr(&mut self, data: IpAddrV4) {
let sin: &mut sockaddr_in = unsafe { mem::transmute(&mut self.ifr.union) };
sin.sin_family = AF_INET as u16;
sin.sin_addr = unsafe { mem::transmute(data) };
}
}
impl<T: IfreqName> IoControl for Ifreq<T> {
type Data = c_void;
fn name(&self) -> i32 {
T::name()
}
fn data(&mut self) -> &mut Self::Data {
unsafe { mem::transmute(&mut self.ifr) }
}
}
#[doc(hidden)]
pub struct IfreqGetIndexT;
impl IfreqName for IfreqGetIndexT {
fn name() -> i32 { SIOCGIFINDEX }
}
impl Ifreq<IfreqGetIndexT> {
pub fn get_index(&self) -> u32 {
self.get_i32() as u32
}
}
pub type IfreqGetIndex = Ifreq<IfreqGetIndexT>;
struct IfreqGetNameT;
impl IfreqName for IfreqGetNameT {
fn name() -> i32 { SIOCGIFNAME }
}
impl Ifreq<IfreqGetNameT> {
pub fn set_index(&mut self, data: u32) {
self.set_i32(data as i32)
}
}
#[doc(hidden)]
pub struct IfreqGetFlagsT;
impl IfreqName for IfreqGetFlagsT {
fn name() -> i32 { SIOCGIFFLAGS }
}
impl Ifreq<IfreqGetFlagsT> {
pub fn get_flags(&self) -> i16 {
self.get_i16()
}
}
pub type IfreqGetFlags = Ifreq<IfreqGetFlagsT>;
#[doc(hidden)]
pub struct IfreqSetFlagsT;
impl IfreqName for IfreqSetFlagsT {
fn name() -> i32 { SIOCSIFFLAGS }
}
impl Ifreq<IfreqSetFlagsT> {
pub fn set_flags(&mut self, flags: i16) {
self.set_i16(flags)
}
}
pub type IfreqSetFlags = Ifreq<IfreqSetFlagsT>;
#[doc(hidden)]
pub struct IfreqGetMTUSizeT;
impl IfreqName for IfreqGetMTUSizeT {
fn name() -> i32 { SIOCGIFMTU }
}
impl Ifreq<IfreqGetMTUSizeT> {
pub fn get_mtu_size(&self) -> i32 {
self.get_i32()
}
}
pub type IfreqGetMTUSize = Ifreq<IfreqGetMTUSizeT>;
#[doc(hidden)]
pub struct IfreqSetMTUSizeT;
impl IfreqName for IfreqSetMTUSizeT {
fn name() -> i32 { SIOCSIFMTU }
}
impl Ifreq<IfreqSetMTUSizeT> {
pub fn set_mtu_size(&mut self, data: i32) {
self.set_i32(data)
}
}
pub type IfreqSetMTUSize = Ifreq<IfreqSetMTUSizeT>;
#[doc(hidden)]
pub struct IfreqGetHwAddrT;
impl IfreqName for IfreqGetHwAddrT {
fn name() -> i32 { SIOCGIFHWADDR }
}
impl Ifreq<IfreqGetHwAddrT> {
pub fn get_hwaddr(&self) -> LlAddr {
self._get_hwaddr()
}
}
pub type IfreqGetHwAddr = Ifreq<IfreqGetHwAddrT>;
#[doc(hidden)]
pub struct IfreqSetHwAddrT;
impl IfreqName for IfreqSetHwAddrT {
fn name() -> i32 { SIOCSIFHWADDR }
}
impl Ifreq<IfreqSetHwAddrT> {
pub fn set_hwaddr(&mut self, data: LlAddr) {
self._set_hwaddr(data)
}
}
pub type IfreqSetHwAddr = Ifreq<IfreqSetHwAddrT>;
#[doc(hidden)]
pub struct IfreqAddrT;
impl IfreqName for IfreqAddrT {
fn name() -> i32 { SIOCGIFADDR }
}
impl Ifreq<IfreqAddrT> {
pub fn get_ipaddr(&self) -> IpAddrV4 {
self._get_ipaddr()
}
}
pub type IfreqAddr = Ifreq<IfreqAddrT>;
#[doc(hidden)]
pub struct IfreqNetmaskT;
impl IfreqName for IfreqNetmaskT {
fn name() -> i32 { SIOCGIFNETMASK }
}
impl Ifreq<IfreqNetmaskT> {
pub fn get_netmask(&self) -> IpAddrV4 {
self._get_ipaddr()
}
}
pub type IfreqNetmask = Ifreq<IfreqNetmaskT>;
#[doc(hidden)]
pub struct IfreqBroadcastT;
impl IfreqName for IfreqBroadcastT {
fn name() -> i32 { SIOCGIFBRDADDR }
}
impl Ifreq<IfreqBroadcastT> {
pub fn get_broadcast(&self) -> IpAddrV4 {
self._get_ipaddr()
}
}
pub type IfreqBroadcast = Ifreq<IfreqBroadcastT>;
#[doc(hidden)]
pub struct IfreqDestinateT;
impl IfreqName for IfreqDestinateT {
fn name() -> i32 { SIOCGIFDSTADDR }
}
impl Ifreq<IfreqDestinateT> {
pub fn get_destinate(&self) -> IpAddrV4 {
self._get_ipaddr()
}
}
pub type IfreqDestinate = Ifreq<IfreqDestinateT>;
pub struct IfreqSocket {
fd: RawFd,
}
impl IfreqSocket {
pub fn new() -> io::Result<IfreqSocket> {
let fd = try!(socket(&Udp::v4()));
Ok(IfreqSocket {
fd: fd,
})
}
pub fn io_control<T: IfreqName>(&self, cmd: &mut Ifreq<T>) -> io::Result<()> {
ioctl(self, cmd)
}
}
impl AsRawFd for IfreqSocket {
fn as_raw_fd(&self) -> RawFd {
self.fd
}
}
impl Drop for IfreqSocket {
fn drop(&mut self) {
close(self)
}
}
#[test]
fn test_ifreq() {
use libc;
let mut ifr = IfreqGetHwAddr::new("lo").unwrap();
let soc = IfreqSocket::new().unwrap();
soc.io_control(&mut ifr).unwrap();
assert_eq!(ifr.get_hwaddr(), LlAddr::new(0,0,0,0,0,0));
let mut ifr: IfreqAddr = ifr.into();
soc.io_control(&mut ifr).unwrap();
assert_eq!(ifr.get_ipaddr(), IpAddrV4::loopback());
let mut ifr: IfreqGetMTUSize = ifr.into();
soc.io_control(&mut ifr).unwrap();
assert_eq!(ifr.get_mtu_size(), 65536);
let mut ifr: IfreqGetIndex = ifr.into();
soc.io_control(&mut ifr).unwrap();
assert_eq!(ifr.get_index(), unsafe { libc::if_nametoindex(CString::new("lo").unwrap().as_ptr()) });
}