cleanup netlink impl

main
dignifiedquire 2 years ago
parent 7b1545ebd8
commit fba8877f4a
  1. 340
      src/interface/android.rs

@ -56,166 +56,238 @@ mod netlink {
LinkMessage, RtnlMessage, LinkMessage, RtnlMessage,
}; };
use netlink_sys::{protocols::NETLINK_ROUTE, Socket}; use netlink_sys::{protocols::NETLINK_ROUTE, Socket};
use std::io;
use std::net::{Ipv4Addr, Ipv6Addr}; use std::net::{Ipv4Addr, Ipv6Addr};
use crate::interface::{Interface, InterfaceType, Ipv4Net, Ipv6Net, MacAddr}; use crate::interface::{Interface, InterfaceType, Ipv4Net, Ipv6Net, MacAddr};
pub fn unix_interfaces() -> Vec<Interface> { pub fn unix_interfaces() -> Vec<Interface> {
let socket = Socket::new(NETLINK_ROUTE).unwrap();
let mut ifaces = Vec::new(); let mut ifaces = Vec::new();
enumerate_netlink( if let Ok(socket) = Socket::new(NETLINK_ROUTE) {
&socket, if let Err(err) = enumerate_netlink(
RtnlMessage::GetLink(LinkMessage::default()), &socket,
&mut ifaces, RtnlMessage::GetLink(LinkMessage::default()),
); &mut ifaces,
enumerate_netlink( handle_new_link,
&socket, ) {
RtnlMessage::GetAddress(AddressMessage::default()), eprintln!("unable to list interfaces: {:?}", err);
&mut ifaces, };
); if let Err(err) = enumerate_netlink(
&socket,
RtnlMessage::GetAddress(AddressMessage::default()),
&mut ifaces,
handle_new_addr,
) {
eprintln!("unable to list addresses: {:?}", err);
}
}
ifaces ifaces
} }
fn enumerate_netlink(socket: &Socket, msg: RtnlMessage, ifaces: &mut Vec<Interface>) { fn handle_new_link(ifaces: &mut Vec<Interface>, msg: RtnlMessage) -> io::Result<()> {
let mut packet = NetlinkMessage::new(NetlinkHeader::default(), NetlinkPayload::from(msg)); match msg {
packet.header.flags = NLM_F_DUMP | NLM_F_REQUEST; RtnlMessage::NewLink(link_msg) => {
packet.header.sequence_number = 1; let mut interface: Interface = Interface {
packet.finalize(); index: link_msg.header.index,
name: String::new(),
friendly_name: None,
description: None,
if_type: InterfaceType::try_from(link_msg.header.link_layer_type as u32)
.unwrap_or(InterfaceType::Unknown),
mac_addr: None,
ipv4: Vec::new(),
ipv6: Vec::new(),
flags: link_msg.header.flags,
transmit_speed: None,
receive_speed: None,
gateway: None,
};
for nla in link_msg.nlas {
match nla {
LinkNla::IfName(name) => {
interface.name = name;
}
LinkNla::Address(addr) => {
match addr.len() {
6 => {
interface.mac_addr =
Some(MacAddr::new(addr.try_into().unwrap()));
}
4 => {
let ip = Ipv4Addr::from(<[u8; 4]>::try_from(addr).unwrap());
interface
.ipv4
.push(Ipv4Net::new_with_netmask(ip, Ipv4Addr::UNSPECIFIED));
}
_ => {
// unclear what these would be
}
}
}
_ => {}
}
}
ifaces.push(interface);
}
_ => {}
}
Ok(())
}
fn handle_new_addr(ifaces: &mut Vec<Interface>, msg: RtnlMessage) -> io::Result<()> {
match msg {
RtnlMessage::NewAddress(addr_msg) => {
if let Some(interface) =
ifaces.iter_mut().find(|i| i.index == addr_msg.header.index)
{
for nla in addr_msg.nlas {
match nla {
AddressNla::Address(addr) => match addr.len() {
4 => {
let ip = Ipv4Addr::from(<[u8; 4]>::try_from(addr).unwrap());
interface
.ipv4
.push(Ipv4Net::new(ip, addr_msg.header.prefix_len));
}
16 => {
let ip = Ipv6Addr::from(<[u8; 16]>::try_from(addr).unwrap());
interface
.ipv6
.push(Ipv6Net::new(ip, addr_msg.header.prefix_len));
}
_ => {
// what else?
}
},
_ => {}
}
}
} else {
eprintln!(
"found unknown interface with index: {}",
addr_msg.header.index
);
}
}
_ => {}
}
Ok(())
}
let mut buf = vec![0; packet.header.length as usize]; struct NetlinkIter<'a> {
socket: &'a Socket,
/// Buffer for received data.
buf: Vec<u8>,
/// Size of the data available in `buf`.
size: usize,
/// Offset into the data currently in `buf`.
offset: usize,
/// Are we don iterating?
done: bool,
}
// TODO: gracefully handle error impl<'a> NetlinkIter<'a> {
assert!(buf.len() == packet.buffer_len()); fn new(socket: &'a Socket, msg: RtnlMessage) -> io::Result<Self> {
packet.serialize(&mut buf[..]); let mut packet =
NetlinkMessage::new(NetlinkHeader::default(), NetlinkPayload::from(msg));
packet.header.flags = NLM_F_DUMP | NLM_F_REQUEST;
packet.header.sequence_number = 1;
packet.finalize();
socket.send(&buf[..], 0).unwrap(); let mut buf = vec![0; packet.header.length as usize];
assert_eq!(buf.len(), packet.buffer_len());
packet.serialize(&mut buf[..]);
socket.send(&buf[..], 0)?;
let mut receive_buffer = vec![0; 4096]; Ok(NetlinkIter {
let mut offset = 0; socket,
offset: 0,
size: 0,
buf: vec![0u8; 4096],
done: false,
})
}
}
loop { impl<'a> Iterator for NetlinkIter<'a> {
let size = socket.recv(&mut &mut receive_buffer[..], 0).unwrap(); type Item = io::Result<RtnlMessage>;
loop { fn next(&mut self) -> Option<Self::Item> {
let bytes = &receive_buffer[offset..]; if self.done {
let rx_packet: NetlinkMessage<RtnlMessage> = return None;
NetlinkMessage::deserialize(bytes).unwrap(); }
match rx_packet.payload { while !self.done {
NetlinkPayload::Done => { // Outer loop
return; if self.size == 0 {
} match self.socket.recv(&mut &mut self.buf[..], 0) {
NetlinkPayload::Error(err) => { Ok(size) => {
eprintln!("Error: {:?}", err); self.size = size;
return; self.offset = 0;
}
Err(err) => {
self.done = true;
return Some(Err(err));
}
} }
NetlinkPayload::InnerMessage(msg) => { }
match msg {
RtnlMessage::NewLink(link_msg) => { let bytes = &self.buf[self.offset..];
let mut interface: Interface = Interface { match NetlinkMessage::<RtnlMessage>::deserialize(bytes) {
index: link_msg.header.index, Ok(packet) => {
name: String::new(), self.offset += packet.header.length as usize;
friendly_name: None, if packet.header.length == 0 || self.offset == self.size {
description: None, // mark this message as fully read
if_type: InterfaceType::try_from( self.size = 0;
link_msg.header.link_layer_type as u32, }
) match packet.payload {
.unwrap_or(InterfaceType::Unknown), NetlinkPayload::Done => {
mac_addr: None, self.done = true;
ipv4: Vec::new(), return None;
ipv6: Vec::new(),
flags: link_msg.header.flags,
transmit_speed: None,
receive_speed: None,
gateway: None,
};
for nla in link_msg.nlas {
match nla {
LinkNla::IfName(name) => {
interface.name = name;
}
LinkNla::Address(addr) => {
match addr.len() {
6 => {
interface.mac_addr = Some(MacAddr::new(
addr.try_into().unwrap(),
));
}
4 => {
let ip = Ipv4Addr::from(
<[u8; 4]>::try_from(addr).unwrap(),
);
interface.ipv4.push(Ipv4Net::new_with_netmask(
ip,
Ipv4Addr::UNSPECIFIED,
));
}
_ => {
// unclear what these would be
}
}
}
_ => {}
}
}
ifaces.push(interface);
} }
RtnlMessage::NewAddress(addr_msg) => { NetlinkPayload::Error(err) => {
if let Some(interface) = self.done = true;
ifaces.iter_mut().find(|i| i.index == addr_msg.header.index) return Some(Err(io::Error::new(
{ io::ErrorKind::Other,
for nla in addr_msg.nlas { err.to_string(),
match nla { )));
AddressNla::Address(addr) => match addr.len() {
4 => {
let ip = Ipv4Addr::from(
<[u8; 4]>::try_from(addr).unwrap(),
);
interface.ipv4.push(Ipv4Net::new(
ip,
addr_msg.header.prefix_len,
));
}
16 => {
let ip = Ipv6Addr::from(
<[u8; 16]>::try_from(addr).unwrap(),
);
interface.ipv6.push(Ipv6Net::new(
ip,
addr_msg.header.prefix_len,
));
}
_ => {
// what else?
}
},
_ => {}
}
}
} else {
eprintln!(
"found unknown interface with index: {}",
addr_msg.header.index
);
}
} }
NetlinkPayload::InnerMessage(msg) => return Some(Ok(msg)),
_ => { _ => {
// not expecting other messages continue;
} }
} }
} }
_ => {} Err(err) => {
} self.done = true;
offset += rx_packet.header.length as usize; return Some(Err(io::Error::new(io::ErrorKind::Other, err.to_string())));
if offset == size || rx_packet.header.length == 0 { }
offset = 0;
break;
} }
} }
None
}
}
fn enumerate_netlink<F>(
socket: &Socket,
msg: RtnlMessage,
ifaces: &mut Vec<Interface>,
cb: F,
) -> io::Result<()>
where
F: Fn(&mut Vec<Interface>, RtnlMessage) -> io::Result<()>,
{
let iter = NetlinkIter::new(socket, msg)?;
for msg in iter {
let msg = msg?;
cb(ifaces, msg)?;
} }
Ok(())
} }
#[cfg(test)] #[cfg(test)]

Loading…
Cancel
Save