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<IpAddr, String>,
rtm: Nlmsghdr<NlTypeWrapper, Rtmsg>,
) -> Result<(), RouterError<u16, Buffer>> {
if let Some(payload) = rtm.get_payload() {
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<IpAddr> {
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(())
}
fn main() -> Result<(), Box<dyn Error>> {
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::<String>(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(())
}