From 65b32a4be5e99d458b3b8b23b4863bc8dbe0297d Mon Sep 17 00:00:00 2001 From: Xnoe Date: Tue, 25 Oct 2022 23:10:16 +0100 Subject: [PATCH] dhcpclient now listens to netlink for link addition and removal --- Cargo.lock | 4 +- Cargo.toml | 2 +- src/main.rs | 271 +++++++++++++++++++++++++++++++++---------- src/rtnetlink.rs | 292 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 504 insertions(+), 65 deletions(-) create mode 100644 src/rtnetlink.rs diff --git a/Cargo.lock b/Cargo.lock index f6398b3..1479831 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,9 +31,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "dhcprs" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5849fa01b515c56a6247af5121fa38bb22988e2c0f7216d93e5623417a57ea" +version = "0.1.3" dependencies = [ "eui48", ] diff --git a/Cargo.toml b/Cargo.toml index 1fe5efd..94b03ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dhcprs = "0.1.2" +dhcprs = {version = "0.1.3", path = "../rust-dhcprs"} nix = "0.25.0" libc = "0.2.133" eui48 = "1.1.0" diff --git a/src/main.rs b/src/main.rs index 36bb551..1fb36d2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,22 @@ -use nix::sys::socket::*; -use std::net::Ipv4Addr; +pub mod rtnetlink; + use eui48::MacAddress; +use nix::sys::socket::*; use rand::prelude::*; +use std::net::Ipv4Addr; -use dhcprs::dhcp::DHCPOption; use dhcprs::dhcp::DHCPMessageType; +use dhcprs::dhcp::DHCPOption; -fn create_dhcp_packet(xid: u32, mac: MacAddress, source_ip: Option, dest_ip: Option, options: Vec) -> dhcprs::udpbuilder::RawUDPPacket { +use std::collections::HashMap; + +fn create_dhcp_packet( + xid: u32, + mac: MacAddress, + source_ip: Option, + dest_ip: Option, + options: Vec, +) -> dhcprs::udpbuilder::RawUDPPacket { let options_bytes = dhcprs::dhcp::DHCPOption::to_bytes(options); let mut vend = [0; 312]; vend[..options_bytes.len()].copy_from_slice(&options_bytes); @@ -24,15 +34,18 @@ fn create_dhcp_packet(xid: u32, mac: MacAddress, source_ip: Option, de mac, [0; 64], [0; 128], - vend + vend, ); let udppacket = dhcprs::udpbuilder::UDPPacket::new( - source_ip.unwrap_or(Ipv4Addr::new(0,0,0,0)), - dest_ip.unwrap_or(Ipv4Addr::new(255,255,255,255)), + source_ip.unwrap_or(Ipv4Addr::new(0, 0, 0, 0)), + dest_ip.unwrap_or(Ipv4Addr::new(255, 255, 255, 255)), 68, 67, - dhcprs::bootp::RawBOOTPPacket::from(bootppacket).as_bytes().try_into().unwrap() + dhcprs::bootp::RawBOOTPPacket::from(bootppacket) + .as_bytes() + .try_into() + .unwrap(), ); return dhcprs::udpbuilder::RawUDPPacket::from(udppacket); @@ -43,33 +56,46 @@ enum DHCPTransactionState { WaitingAfterDiscover, Request, WaitAfterRequest, - + Renew, + WaitAfterRenew, + Rebind, + WaitafterRebind, } -fn dhcp_client(name: String, mac: LinkAddr) { +fn dhcp_client(name: String, index: i32, mac: [u8; 6]) { let mut rng = rand::thread_rng(); // Before we can do anything else, we need to construct an LinkAddr for the mac ff:ff:ff:ff:ff:ff - println!("Starting DHCP on interface {}", name); - let sockaddr_ll = libc::sockaddr_ll { sll_family: libc::AF_PACKET as u16, - sll_halen: mac.halen() as u8, - sll_hatype: mac.hatype(), - sll_ifindex: mac.ifindex() as i32, + sll_halen: 6, + sll_hatype: 1, + sll_ifindex: index, sll_addr: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0], - sll_pkttype: mac.pkttype(), - sll_protocol: 0x0008 + sll_pkttype: 0, + sll_protocol: 0x0008, }; - let mut linkaddr = unsafe {LinkAddr::from_raw(&sockaddr_ll as *const libc::sockaddr_ll as *const libc::sockaddr, Some(20)).expect("Failed to create linkaddr!")}; + let mut linkaddr = unsafe { + LinkAddr::from_raw( + &sockaddr_ll as *const libc::sockaddr_ll as *const libc::sockaddr, + Some(20), + ) + .expect("Failed to create linkaddr!") + }; // Create and bind the socket - let socket = socket(AddressFamily::Packet, SockType::Datagram, SockFlag::empty(), SockProtocol::Udp).expect("Failed to create socket! Permission issue?"); + let socket = socket( + AddressFamily::Packet, + SockType::Datagram, + SockFlag::empty(), + SockProtocol::Udp, + ) + .expect("Failed to create socket! Permission issue?"); assert!(setsockopt(socket, sockopt::Broadcast, &true).is_ok()); assert!(bind(socket, &linkaddr).is_ok()); - let client_mac = MacAddress::from_bytes(&mac.addr().unwrap()).unwrap(); + let client_mac = MacAddress::from_bytes(&mac).unwrap(); let mut client_addr: Option = None; let mut server_addr: Option = None; @@ -84,10 +110,30 @@ fn dhcp_client(name: String, mac: LinkAddr) { match &dhcp_state { DHCPTransactionState::Discover => { println!("Sent DHCPDiscover on {}", name); - let _ = sendto(socket, create_dhcp_packet(xid, client_mac, None, None, vec![ - DHCPOption::DHCPMessageType(DHCPMessageType::DHCPDiscover), - DHCPOption::End - ]).as_bytes(), &linkaddr, MsgFlags::empty()).unwrap(); + loop { + match sendto( + socket, + create_dhcp_packet( + xid, + client_mac, + None, + None, + vec![ + DHCPOption::DHCPMessageType(DHCPMessageType::DHCPDiscover), + DHCPOption::End, + ], + ) + .as_bytes(), + &linkaddr, + MsgFlags::empty(), + ) { + Ok(_) => break, + Err(nix::errno::Errno::ENETDOWN) => { + unsafe { libc::sleep(3) }; + } + Err(_) => panic!("") + } + } dhcp_state = DHCPTransactionState::WaitingAfterDiscover; } @@ -97,15 +143,17 @@ fn dhcp_client(name: String, mac: LinkAddr) { println!("Received {} bytes on {}", bytes, name); - let udppacet_raw: dhcprs::udpbuilder::RawUDPPacket = unsafe {std::ptr::read(packet.as_ptr() as *const _)}; - let udppacket: dhcprs::udpbuilder::UDPPacket = udppacet_raw.into(); - + let udppacket_raw: dhcprs::udpbuilder::RawUDPPacket = + unsafe { std::ptr::read(packet.as_ptr() as *const _) }; + let udppacket: dhcprs::udpbuilder::UDPPacket = udppacket_raw.into(); + if udppacket.source_port != 67 { // Not a BOOTP reply, get the next packet. continue 'dhcp_message_loop; }; - let bootppacket_raw: dhcprs::bootp::RawBOOTPPacket = unsafe {std::ptr::read(udppacket.get_data().as_ptr() as *const _)}; + let bootppacket_raw: dhcprs::bootp::RawBOOTPPacket = + unsafe { std::ptr::read(udppacket.get_data().as_ptr() as *const _) }; let bootppacket: dhcprs::bootp::BOOTPPacket = bootppacket_raw.into(); if bootppacket.xid != xid { @@ -113,9 +161,20 @@ fn dhcp_client(name: String, mac: LinkAddr) { continue 'dhcp_message_loop; }; - let options = dhcprs::dhcp::DHCPOption::from_bytes(&bootppacket.get_vend()[4..]); + let options = + dhcprs::dhcp::DHCPOption::from_bytes(&bootppacket.get_vend()[4..]); - if !options.iter().find(|&x| if let DHCPOption::DHCPMessageType(DHCPMessageType::DHCPOffer) = x {true} else {false}).is_some() { + if !options + .iter() + .find(|&x| { + if let DHCPOption::DHCPMessageType(DHCPMessageType::DHCPOffer) = x { + true + } else { + false + } + }) + .is_some() + { // We got a response but it wasn't the expected message, try again with another transaction. continue 'dhcp_transaction; } @@ -125,8 +184,14 @@ fn dhcp_client(name: String, mac: LinkAddr) { client_addr = bootppacket.yiaddr; server_addr = bootppacket.siaddr; - println!("Got client address: {}", client_addr.unwrap_or(Ipv4Addr::new(0,0,0,0))); - println!("Got server address: {}", server_addr.unwrap_or(Ipv4Addr::new(0,0,0,0))); + println!( + "Got client address: {}", + client_addr.unwrap_or(Ipv4Addr::new(0, 0, 0, 0)) + ); + println!( + "Got server address: {}", + server_addr.unwrap_or(Ipv4Addr::new(0, 0, 0, 0)) + ); // Update the linkaddr to be the server's actual hardware address rather than the broadcast MAC. let mut mac: [u8; 8] = [0; 8]; @@ -138,21 +203,41 @@ fn dhcp_client(name: String, mac: LinkAddr) { sll_ifindex: sockaddr_ll.sll_ifindex, sll_addr: mac, sll_pkttype: sockaddr_ll.sll_pkttype, - sll_protocol: sockaddr_ll.sll_protocol + sll_protocol: sockaddr_ll.sll_protocol, }; - linkaddr = unsafe {LinkAddr::from_raw(&new_sockaddr_ll as *const libc::sockaddr_ll as *const libc::sockaddr, Some(20)).expect("Failed to update linkaddr to server's hardware address!")}; + linkaddr = unsafe { + LinkAddr::from_raw( + &new_sockaddr_ll as *const libc::sockaddr_ll as *const libc::sockaddr, + Some(20), + ) + .expect("Failed to update linkaddr to server's hardware address!") + }; dhcp_state = DHCPTransactionState::Request; } DHCPTransactionState::Request => { println!("Sent DHCPRequest on {}", name); - let _ = sendto(socket, create_dhcp_packet(xid, client_mac, client_addr, server_addr, vec![ - DHCPOption::DHCPMessageType(DHCPMessageType::DHCPRequest), - DHCPOption::ParameterRequest(vec![1, 3, 6, 28]), - DHCPOption::End - ]).as_bytes(), &linkaddr, MsgFlags::empty()); + let _ = sendto( + socket, + create_dhcp_packet( + xid, + client_mac, + None, + None, + vec![ + DHCPOption::DHCPMessageType(DHCPMessageType::DHCPRequest), + DHCPOption::RequestIPAddress(client_addr.unwrap()), + DHCPOption::ServerIdentifier(server_addr.unwrap()), + DHCPOption::ParameterRequest(vec![1, 3, 6, 28, 121]), + DHCPOption::End, + ], + ) + .as_bytes(), + &linkaddr, + MsgFlags::empty(), + ); dhcp_state = DHCPTransactionState::WaitAfterRequest; } @@ -161,15 +246,17 @@ fn dhcp_client(name: String, mac: LinkAddr) { let (bytes, _) = recvfrom::(socket, &mut packet).unwrap(); println!("Received {} bytes on {}", bytes, name); - let udppacet_raw: dhcprs::udpbuilder::RawUDPPacket = unsafe {std::ptr::read(packet.as_ptr() as *const _)}; - let udppacket: dhcprs::udpbuilder::UDPPacket = udppacet_raw.into(); - + let udppacket_raw: dhcprs::udpbuilder::RawUDPPacket = + unsafe { std::ptr::read(packet.as_ptr() as *const _) }; + let udppacket: dhcprs::udpbuilder::UDPPacket = udppacket_raw.into(); + if udppacket.source_port != 67 { // Not a BOOTP reply, get the next packet. continue 'dhcp_message_loop; }; - let bootppacket_raw: dhcprs::bootp::RawBOOTPPacket = unsafe {std::ptr::read(udppacket.get_data().as_ptr() as *const _)}; + let bootppacket_raw: dhcprs::bootp::RawBOOTPPacket = + unsafe { std::ptr::read(udppacket.get_data().as_ptr() as *const _) }; let bootppacket: dhcprs::bootp::BOOTPPacket = bootppacket_raw.into(); if bootppacket.xid != xid { @@ -177,9 +264,20 @@ fn dhcp_client(name: String, mac: LinkAddr) { continue 'dhcp_message_loop; }; - let options = dhcprs::dhcp::DHCPOption::from_bytes(&bootppacket.get_vend()[4..]); + let options = + dhcprs::dhcp::DHCPOption::from_bytes(&bootppacket.get_vend()[4..]); - if !options.iter().find(|&x| if let DHCPOption::DHCPMessageType(DHCPMessageType::DHCPACK) = x {true} else {false}).is_some() { + if !options + .iter() + .find(|&x| { + if let DHCPOption::DHCPMessageType(DHCPMessageType::DHCPACK) = x { + true + } else { + false + } + }) + .is_some() + { // Wasn't an ACK, probably NAK, try again. println!("Did not receive ACK from DHCPRequest, sleeping for 10secs before continuing."); std::thread::sleep(core::time::Duration::new(10, 0)); @@ -192,11 +290,9 @@ fn dhcp_client(name: String, mac: LinkAddr) { println!("Received: {:?}", option); match option { - DHCPOption::IPAddressLeaseTime(n) => { - sleep_time = n - } + DHCPOption::IPAddressLeaseTime(n) => sleep_time = n, - _ => () + _ => (), } } @@ -204,22 +300,75 @@ fn dhcp_client(name: String, mac: LinkAddr) { std::thread::sleep(core::time::Duration::new(sleep_time.into(), 0)); } - _ => () + _ => panic!("Fail!"), } - } + } } } +fn zascii(slice: &[libc::c_char]) -> String { + String::from_iter( + slice + .iter() + .take_while(|c| **c != 0) + .map(|c| *c as u8 as char), + ) +} + fn main() { - let threads: Vec> = nix::ifaddrs::getifaddrs().unwrap() - .filter(|x| x.address.is_some()) - .filter(|x| x.address.unwrap().as_link_addr().is_some()) - .filter(|x| x.address.unwrap().as_link_addr().unwrap().hatype() == 1) // Only perform DHCP on Ethernet interfaces. - .map(|x| (x.interface_name, *x.address.unwrap().as_link_addr().unwrap())) - .map(|(name, mac)| std::thread::spawn(move || { dhcp_client(name, mac)} )) - .collect(); - - for thread in threads { - let _ = thread.join(); + let mut thread_map: HashMap<[u8; 6], libc::c_int> = HashMap::new(); + let iterator = rtnetlink::new_interface_iterator().unwrap(); + + for message in iterator { + let interface: rtnetlink::InterfaceDetails; + + match message { + rtnetlink::MessageType::NewLink(i) => { + interface = i; + let name = zascii(&interface.name); + println!("New Link"); + + if let None = thread_map.get(&interface.hwaddr) { + println!("Starting DHCP on {}", name); + unsafe { + match libc::fork() { + x if x < 0 => panic!("Failed to fork!"), + 0 => dhcp_client(name, interface.index, interface.hwaddr), + pid => { + println!("Started DHCP on {} PID {}", name, pid); + thread_map.insert(interface.hwaddr, pid); + } + } + } + } + } + rtnetlink::MessageType::DelLink(i) => { + interface = i; + let name = zascii(&interface.name); + println!("Del Link"); + + if let Some(pid) = thread_map.get(&interface.hwaddr) { + println!("Stopping DHCP on {}", name); + unsafe { + libc::kill(*pid, libc::SIGTERM); + } + thread_map.remove(&interface.hwaddr); + } + } + } + + let name = zascii(&interface.name); + + println!("Name: {}", name); + println!( + "MAC: {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}", + interface.hwaddr[0], + interface.hwaddr[1], + interface.hwaddr[2], + interface.hwaddr[3], + interface.hwaddr[4], + interface.hwaddr[5] + ); + println!("Index: {}", interface.index); } } \ No newline at end of file diff --git a/src/rtnetlink.rs b/src/rtnetlink.rs new file mode 100644 index 0000000..341b54a --- /dev/null +++ b/src/rtnetlink.rs @@ -0,0 +1,292 @@ +use eui48::MacAddress; + +#[repr(C)] +struct ifinfomsg { + ifi_family: libc::__u8, + ifi_pad: libc::__u8, + ifi_type: libc::__u16, + ifi_index: libc::c_int, + ifi_flags: libc::__u32, + ifi_change: libc::__u32, +} + +const nlmsg_alignto: u32 = 4; + +macro_rules! nlmsg_align { + ($len:expr) => { + ($len as u32 + (nlmsg_alignto - 1)) & !(nlmsg_alignto - 1) + }; +} + +const nlmsg_hdrlen: usize = nlmsg_align!(std::mem::size_of::()) as usize; + +macro_rules! nlmsg_length { + ($len:expr) => { + $len as u32 + nlmsg_hdrlen as u32 + }; +} + +macro_rules! nlmsg_space { + ($len:expr) => { + nlmsg_align!(nlmsg_length!($len)) + }; +} + +#[inline] +unsafe fn nlmsg_data(nlh: *const libc::nlmsghdr) -> *const u8 { + return (nlh as *const u8).offset(nlmsg_hdrlen as isize); +} + +#[inline] +unsafe fn nlmsg_next(nlh: *const libc::nlmsghdr, len: &mut u32) -> *const libc::nlmsghdr { + *len -= nlmsg_align!((*nlh).nlmsg_len); + return (nlh as *const u8).offset(nlmsg_align!((*nlh).nlmsg_len) as isize) + as *const libc::nlmsghdr; +} + +#[inline] +unsafe fn nlmsg_ok(nlh: *const libc::nlmsghdr, len: &mut u32) -> bool { + return *len >= std::mem::size_of::() as u32 + && (*nlh).nlmsg_len >= std::mem::size_of::() as u32 + && (*nlh).nlmsg_len <= *len; +} + +#[inline] +unsafe fn nlmsg_payload(nlh: *const libc::nlmsghdr, len: &mut u32) -> u32 { + return (*nlh).nlmsg_len - nlmsg_space!(*len); +} + +#[repr(C)] +struct rtattr { + rta_len: libc::c_ushort, + rta_type: libc::c_ushort, +} + +const rta_alignto: u32 = 4; + +macro_rules! rta_align { + ($len:expr) => { + ($len as u32 + (rta_alignto - 1)) & !(rta_alignto - 1) + }; +} + +#[inline] +unsafe fn rta_ok(rta: *const rtattr, len: &mut u32) -> bool { + return *len >= std::mem::size_of::() as u32 + && (*rta).rta_len >= std::mem::size_of::() as libc::c_ushort + && (*rta).rta_len <= *len as libc::c_ushort; +} + +#[inline] +unsafe fn rta_next(rta: *const rtattr, len: &mut u32) -> *const rtattr { + *len -= rta_align!((*rta).rta_len); + return (rta as *const u8).offset(nlmsg_align!((*rta).rta_len) as isize) as *const rtattr; +} + +#[inline] +fn rta_length(len: u32) -> u32 { + return std::mem::size_of::() as u32 + len; +} + +#[inline] +fn rta_space(len: u32) -> u32 { + rta_align!(rta_length(len)) +} + +#[inline] +unsafe fn rta_data(rta: *const rtattr) -> *const u8 { + return (rta as *const u8).offset(rta_length(0) as isize); +} + +struct RtAttrs { + current_attr: *const rtattr, + current_size: u32, +} + +impl Iterator for RtAttrs { + type Item = *const rtattr; + + fn next(&mut self) -> Option { + unsafe { + if rta_ok(self.current_attr, &mut self.current_size) { + let hdr = self.current_attr; + self.current_attr = rta_next(self.current_attr, &mut self.current_size); + return Some(hdr); + } else { + None + } + } + } +} + +#[inline] +unsafe fn ifla_rta(r: *const ifinfomsg) -> *const rtattr { + return (r as *const u8).offset(nlmsg_align!(std::mem::size_of::()) as isize) + as *const rtattr; +} + +pub struct InterfaceIterator { + socket: libc::c_int, + src_addr: libc::sockaddr_nl, + dest_addr: libc::sockaddr_nl, + subscribed: bool, + + buffer: [u8; 4096], + current_header: Option<*const libc::nlmsghdr>, + current_len: u32, +} + +pub fn new_interface_iterator() -> Result { + let socket; + + let mut src_addr = unsafe { std::mem::zeroed::() }; + let mut dest_addr = unsafe { std::mem::zeroed::() }; + + src_addr.nl_family = libc::AF_NETLINK as u16; + src_addr.nl_groups = libc::RTMGRP_LINK as u32; + src_addr.nl_pid = unsafe { libc::getpid() } as u32; + + dest_addr.nl_family = libc::AF_NETLINK as u16; + + unsafe { + socket = libc::socket(libc::AF_NETLINK, libc::SOCK_RAW, libc::NETLINK_ROUTE); + if socket == -1 { + return Err("Failed to create AF_NETLINK socket!".to_string()); + } + + if libc::bind( + socket, + &src_addr as *const libc::sockaddr_nl as *const libc::sockaddr, + std::mem::size_of::() as u32, + ) == -1 + { + return Err("Failed to bind to AF_NETLINK socket!".to_string()); + } + } + + let mut request = unsafe { std::mem::zeroed::<(libc::nlmsghdr, ifinfomsg)>() }; + request.0.nlmsg_len = nlmsg_length!(std::mem::size_of::()); + request.0.nlmsg_type = libc::RTM_GETLINK; + request.0.nlmsg_flags = (libc::NLM_F_REQUEST | libc::NLM_F_DUMP) as u16; + request.1.ifi_family = libc::AF_NETLINK as u8; + + let mut iov = libc::iovec { + iov_base: &mut request as *mut _ as *mut libc::c_void, + iov_len: request.0.nlmsg_len as usize, + }; + + let mut msg = unsafe { std::mem::zeroed::() }; + msg.msg_name = &mut dest_addr as *mut _ as *mut libc::c_void; + msg.msg_namelen = std::mem::size_of::() as u32; + msg.msg_iov = &mut iov as *mut libc::iovec; + msg.msg_iovlen = 1; + + if unsafe { libc::sendmsg(socket, &msg as *const libc::msghdr, 0) } < 0 { + return Err("Failed to send interface request!".to_string()); + } + + Ok(InterfaceIterator { + socket: socket, + src_addr: src_addr, + dest_addr: dest_addr, + subscribed: true, + + buffer: [0; 4096], + current_header: None, + current_len: 0, + }) +} + +pub struct InterfaceDetails { + pub index: i32, + pub name: [libc::c_char; libc::IFNAMSIZ], + pub hwaddr: [u8; 6], +} + +pub enum MessageType { + NewLink(InterfaceDetails), + DelLink(InterfaceDetails), +} + +impl Iterator for InterfaceIterator { + type Item = MessageType; + fn next(&mut self) -> Option { + loop { + if let None = self.current_header { + let mut recv_iov = libc::iovec { + iov_base: &mut self.buffer as *mut _ as *mut libc::c_void, + iov_len: 4096, + }; + + let mut msg = unsafe { std::mem::zeroed::() }; + msg.msg_name = &mut self.dest_addr as *mut _ as *mut libc::c_void; + msg.msg_namelen = std::mem::size_of::() as u32; + msg.msg_iov = &mut recv_iov as *mut libc::iovec; + msg.msg_iovlen = 1; + + let n = unsafe { libc::recvmsg(self.socket, &mut msg as *mut libc::msghdr, 0) }; + if n < 0 { + panic!("Failed to receive on AF_NETLINK socket!"); + } + + self.current_header = Some(&self.buffer as *const _ as *const libc::nlmsghdr); + self.current_len = n as u32; + } + + if unsafe { !nlmsg_ok(self.current_header.unwrap(), &mut self.current_len) } { + self.current_header = None; + continue; + } + + if unsafe { (*self.current_header.unwrap()).nlmsg_type } == libc::NLMSG_DONE as u16 { + self.current_header = None; + continue; + } + + let hdr = self.current_header.unwrap(); + self.current_header = + Some(unsafe { nlmsg_next(self.current_header.unwrap(), &mut self.current_len) }); + + let ifi = unsafe { std::ptr::read(nlmsg_data(hdr) as *const ifinfomsg) }; + + if ifi.ifi_type != 1 { + continue; + } + + let mut addr: [u8; 6] = [0; 6]; + let mut name: [libc::c_char; libc::IFNAMSIZ] = [0; libc::IFNAMSIZ]; + + for attr in (RtAttrs { + current_attr: unsafe { ifla_rta(nlmsg_data(hdr) as *const ifinfomsg) }, + current_size: unsafe { (*hdr).nlmsg_len } + - std::mem::size_of::() as u32, + }) { + match unsafe { (*attr).rta_type } { + libc::IFLA_ADDRESS => { + addr = unsafe { std::ptr::read(rta_data(attr) as *const [u8; 6]) }; + } + + libc::IFLA_IFNAME => { + name = unsafe { + std::ptr::read(rta_data(attr) as *const [libc::c_char; libc::IFNAMSIZ]) + }; + } + + _ => (), + } + } + + let interfacedetails = InterfaceDetails { + index: ifi.ifi_index, + name: name, + hwaddr: addr, + }; + + match unsafe { (*hdr).nlmsg_type } { + libc::RTM_NEWLINK => return Some(MessageType::NewLink(interfacedetails)), + libc::RTM_DELLINK => return Some(MessageType::DelLink(interfacedetails)), + _ => panic!("Unknown Message Received! {}", unsafe { (*hdr).nlmsg_type }), + } + } + } +}