use std::io;
use std::mem;
use std::ptr;
use std::slice;
use std::ffi::CString;
use super::{errno, c_char, RawFd, socket, sockaddr, sockaddr_in};
use ip::{LlAddr, IpAddrV4, Udp};
use libc;
const IFNAMSIZ: usize = 16;
const SIOCGIFFLAGS: i32 = 0x8913;
const SIOCGIFADDR: i32 = 0x8915;
const SIOCGIFMTU: i32 = 0x8921;
const SIOCGIFHWADDR: i32 = 0x8927;
const SIOCGIFINDEX: i32 = 0x8933;
#[repr(C)]
struct ifreq {
ifr_name: [c_char; IFNAMSIZ],
union: [u8; 24],
}
pub struct Ifreq {
ifr: ifreq,
fd: RawFd,
}
impl Ifreq {
#[allow(dead_code)]
pub fn new<T: Into<Vec<u8>>>(ifname: T) -> io::Result<Ifreq> {
match CString::new(ifname) {
Ok(ref s) if s.as_bytes().len() < IFNAMSIZ => {
let fd = try!(socket(&Udp::v4()));
let mut ifr = Ifreq {
ifr: unsafe { mem::uninitialized() },
fd: fd,
};
let src = s.as_bytes_with_nul();
let dst = unsafe { slice::from_raw_parts_mut(ifr.ifr.ifr_name.as_mut_ptr() as *mut u8, src.len()) };
dst.clone_from_slice(src);
Ok(ifr)
}
_ =>
Err(io::Error::new(io::ErrorKind::Other, "Unsupported interface-name"))
}
}
#[allow(dead_code)]
pub fn from_index(ifindex: u32) -> io::Result<Ifreq> {
let mut ifr: Ifreq = unsafe { mem::uninitialized() };
if unsafe { libc::if_indextoname(ifindex, ifr.ifr.ifr_name.as_mut_ptr()) } == ptr::null_mut() {
return Err(io::Error::from_raw_os_error(errno()));
}
ifr.fd = try!(socket(&Udp::v4()));
Ok(ifr)
}
unsafe fn to_i16(&self) -> i16 {
*(self.ifr.union.as_ptr() as *const i16)
}
unsafe fn to_i32(&self) -> i32 {
*(self.ifr.union.as_ptr() as *const i32)
}
unsafe fn to_ipaddr(&self) -> IpAddrV4 {
let sin = &*(self.ifr.union.as_ptr() as *const sockaddr_in);
IpAddrV4::from_bytes(mem::transmute((&*sin).sin_addr))
}
unsafe fn to_lladdr(&self) -> LlAddr {
let sa = &*(self.ifr.union.as_ptr() as *const sockaddr);
LlAddr::new(sa.sa_data[0] as u8, sa.sa_data[1] as u8, sa.sa_data[2] as u8,
sa.sa_data[3] as u8, sa.sa_data[4] as u8, sa.sa_data[5] as u8)
}
unsafe fn ioctl(&self, name: i32) -> i32 {
libc::ioctl(self.fd, name as u64, &self.ifr)
}
#[allow(dead_code)]
pub fn get_hwaddr(&self) -> io::Result<LlAddr> {
let _ = libc_try!(self.ioctl(SIOCGIFHWADDR));
Ok(unsafe { self.to_lladdr() })
}
#[allow(dead_code)]
pub fn get_ipaddr(&self) -> io::Result<IpAddrV4> {
let _ = libc_try!(self.ioctl(SIOCGIFADDR));
Ok(unsafe { self.to_ipaddr() })
}
#[allow(dead_code)]
pub fn get_flags(&self) -> io::Result<i16> {
let _ = libc_try!(self.ioctl(SIOCGIFFLAGS));
Ok(unsafe { self.to_i16() })
}
#[allow(dead_code)]
pub fn get_mtu(&self) -> io::Result<i32> {
let _ = libc_try!(self.ioctl(SIOCGIFMTU));
Ok(unsafe { self.to_i32() })
}
pub fn get_index(&self) -> io::Result<u32> {
let _ = libc_try!(self.ioctl(SIOCGIFINDEX));
Ok(unsafe { self.to_i32() } as u32)
}
}
impl Drop for Ifreq {
fn drop(&mut self) {
let _ = unsafe { libc::close(self.fd) };
}
}
#[test]
fn test_ifreq() {
let ifr = Ifreq::new("lo").unwrap();
assert_eq!(ifr.get_hwaddr().unwrap(), LlAddr::new(0,0,0,0,0,0));
assert_eq!(ifr.get_ipaddr().unwrap(), IpAddrV4::loopback());
assert_eq!(ifr.get_mtu().unwrap(), 65536);
assert_eq!(ifr.get_index().unwrap(), unsafe { libc::if_nametoindex(CString::new("lo").unwrap().as_ptr()) });
}