use std::{ collections::HashMap, error::Error, io::Read, net::{IpAddr, Ipv4Addr, Ipv6Addr}, }; use neli::{ consts::{nl::*, rtnl::*, socket::*}, err::{MsgError, RouterError}, nl::{NlPayload, Nlmsghdr}, router::synchronous::NlRouter, rtnl::*, types::Buffer, utils::Groups, }; fn parse_route_table( ifs: &HashMap, rtm: Nlmsghdr, ) -> Result<(), RouterError> { if let Some(payload) = rtm.get_payload() { // This sample is only interested in the main table. if payload.rtm_table() == &RtTable::Main { let mut src = None; let mut dst = None; let mut gateway = None; for attr in payload.rtattrs().iter() { fn to_addr(b: &[u8]) -> Option { if let Ok(tup) = <&[u8; 4]>::try_from(b) { Some(IpAddr::from(*tup)) } else if let Ok(tup) = <&[u8; 16]>::try_from(b) { Some(IpAddr::from(*tup)) } else { None } } match attr.rta_type() { Rta::Dst => dst = to_addr(attr.rta_payload().as_ref()), Rta::Prefsrc => src = to_addr(attr.rta_payload().as_ref()), Rta::Gateway => gateway = to_addr(attr.rta_payload().as_ref()), _ => (), } } if let Some(dst) = dst { print!("{}/{} ", dst, payload.rtm_dst_len()); } else { print!("default "); if let Some(gateway) = gateway { print!("via {gateway} "); } } if let Some(src) = src { print!("dev {}", ifs.get(&src).expect("Should be present")); } if payload.rtm_scope() != &RtScope::Universe { print!( " proto {:?} scope {:?} ", payload.rtm_protocol(), payload.rtm_scope() ) } if let Some(src) = src { print!(" src {src} "); } println!(); } } Ok(()) } /// This sample is a simple imitation of the `ip route` command, to demonstrate interaction /// with the rtnetlink subsystem. fn main() -> Result<(), Box> { env_logger::init(); let (socket, _) = NlRouter::connect(NlFamily::Route, None, Groups::empty()).unwrap(); let ifmsg = IfaddrmsgBuilder::default() .ifa_family(RtAddrFamily::Unspecified) .ifa_prefixlen(0) .ifa_scope(RtScope::Universe) .ifa_index(0) .build()?; let recv = socket.send::<_, _, NlTypeWrapper, _>( Rtm::Getaddr, NlmF::DUMP, NlPayload::Payload(ifmsg), )?; let mut ifs = HashMap::new(); for msg in recv { let msg = msg?; if let NlPayload::<_, Ifaddrmsg>::Payload(p) = msg.nl_payload() { let handle = p.rtattrs().get_attr_handle(); let addr = { if let Ok(mut ip_bytes) = handle.get_attr_payload_as_with_len_borrowed::<&[u8]>(Ifa::Address) { if ip_bytes.len() == 4 { let mut bytes = [0u8; 4]; ip_bytes.read_exact(&mut bytes)?; Some(IpAddr::from(Ipv4Addr::from( u32::from_ne_bytes(bytes).to_be(), ))) } else if ip_bytes.len() == 16 { let mut bytes = [0u8; 16]; ip_bytes.read_exact(&mut bytes)?; Some(IpAddr::from(Ipv6Addr::from( u128::from_ne_bytes(bytes).to_be(), ))) } else { return Err(Box::new(MsgError::new(format!( "Unrecognized address length of {} found", ip_bytes.len() )))); } } else { None } }; let name = handle .get_attr_payload_as_with_len::(Ifa::Label) .ok(); if let (Some(addr), Some(name)) = (addr, name) { ifs.insert(addr, name); } } } let rtmsg = RtmsgBuilder::default() .rtm_family(RtAddrFamily::Inet) .rtm_dst_len(0) .rtm_src_len(0) .rtm_tos(0) .rtm_table(RtTable::Unspec) .rtm_protocol(Rtprot::Unspec) .rtm_scope(RtScope::Universe) .rtm_type(Rtn::Unspec) .build()?; let recv = socket.send(Rtm::Getroute, NlmF::DUMP, NlPayload::Payload(rtmsg))?; for rtm_result in recv { let rtm = rtm_result?; if let NlTypeWrapper::Rtm(_) = rtm.nl_type() { parse_route_table(&ifs, rtm)?; } } Ok(()) }