use std::{ mem, net::{Ipv4Addr, Ipv6Addr}, }; extern crate libc; extern crate rsmnl as mnl; use mnl::{Attr, CbResult, CbStatus, Msghdr, Socket}; mod linux_bindings; use linux_bindings as linux; fn data_attr_cb2<'a, 'b>( tb: &'b mut [Option<&'a Attr<'a>>], ) -> impl FnMut(&'a Attr) -> CbResult + 'b { move |attr: &Attr| { let atype = attr.atype() as usize; // skip unsupported attribute in user-space if atype >= tb.len() { return Ok(CbStatus::Ok); } tb[atype] = Some(attr); Ok(CbStatus::Ok) } } fn attributes_show_ipv4(tb: &[Option<&Attr>]) -> Result<(), Box> { if let Some(attr) = tb[libc::RTA_TABLE as usize] { print!("table={} ", attr.value_ref::()?); } if let Some(attr) = tb[libc::RTA_DST as usize] { print!("dst={} ", attr.value_ref::()?); } if let Some(attr) = tb[libc::RTA_SRC as usize] { print!("src={} ", attr.value_ref::()?); } if let Some(attr) = tb[libc::RTA_OIF as usize] { print!("oif={} ", attr.value_ref::()?); } if let Some(attr) = tb[libc::RTA_FLOW as usize] { print!("flow={} ", attr.value_ref::()?); } if let Some(attr) = tb[libc::RTA_PREFSRC as usize] { print!("prefsrc={} ", attr.value_ref::()?); } if let Some(attr) = tb[libc::RTA_GATEWAY as usize] { print!("gw={} ", attr.value_ref::()?); } if let Some(attr) = tb[libc::RTA_PRIORITY as usize] { print!("prio={} ", attr.value_ref::()?); } if let Some(attr) = tb[libc::RTA_METRICS as usize] { let mut tbx: [Option<&Attr>; linux::__RTAX_MAX as usize] = [None; linux::__RTAX_MAX as usize]; attr.parse_nested(data_attr_cb2(&mut tbx))?; for i in 0..linux::__RTAX_MAX - 1 { if let Some(a) = tbx[i as usize] { print!("metrics[{}]={} ", i, a.value_ref::()?); } } } Ok(()) } fn attributes_show_ipv6(tb: &[Option<&Attr>]) -> Result<(), Box> { if let Some(attr) = tb[libc::RTA_TABLE as usize] { print!("table={} ", attr.value_ref::()?); } if let Some(attr) = tb[libc::RTA_DST as usize] { print!("dst={} ", attr.value_ref::()?); } if let Some(attr) = tb[libc::RTA_SRC as usize] { print!("src={} ", attr.value_ref::()?); } if let Some(attr) = tb[libc::RTA_OIF as usize] { print!("oif={} ", attr.value_ref::()?); } if let Some(attr) = tb[libc::RTA_FLOW as usize] { print!("flow={} ", attr.value_ref::()?); } if let Some(attr) = tb[libc::RTA_PREFSRC as usize] { print!("prefsrc={} ", attr.value_ref::()?); } if let Some(attr) = tb[libc::RTA_GATEWAY as usize] { print!("gw={} ", attr.value_ref::()?); } if let Some(attr) = tb[libc::RTA_PRIORITY as usize] { print!("prio={} ", attr.value_ref::()?); } if let Some(attr) = tb[libc::RTA_METRICS as usize] { let mut tbx: [Option<&Attr>; linux::__RTAX_MAX as usize] = [None; linux::__RTAX_MAX as usize]; attr.parse_nested(data_attr_cb2(&mut tbx))?; for i in 0..linux::__RTAX_MAX - 1 { if let Some(a) = tbx[i as usize] { print!("metrics[{}]={} ", i, a.value_ref::()?); } } } Ok(()) } fn data_cb(nlh: &Msghdr) -> CbResult { let rm = nlh.payload::()?; match nlh.nlmsg_type { libc::RTM_NEWROUTE => print!("[NEW] "), libc::RTM_DELROUTE => print!("[DEL] "), _ => {} } // protocol family = AF_INET | AF_INET6 // print!("family={} ", rm.rtm_family); // destination CIDR, eg. 24 or 32 for IPv4 print!("dst_len={} ", rm.rtm_dst_len); // source CIDR print!("src_len={} ", rm.rtm_src_len); // type of service (TOS), eg. 0 print!("tos={} ", rm.rtm_tos); // table id: // RT_TABLE_UNSPEC = 0 // // ... user defined values ... // // RT_TABLE_COMPAT = 252 // RT_TABLE_DEFAULT = 253 // RT_TABLE_MAIN = 254 // RT_TABLE_LOCAL = 255 // RT_TABLE_MAX = 0xFFFFFFFF // // Synonimous attribute: RTA_TABLE. print!("table={} ", rm.rtm_table); // type: // RTN_UNSPEC = 0 // RTN_UNICAST = 1 // RTN_LOCAL = 2 // RTN_BROADCAST = 3 // RTN_ANYCAST = 4 // RTN_MULTICAST = 5 // RTN_BLACKHOLE = 6 // RTN_UNREACHABLE = 7 // RTN_PROHIBIT = 8 // RTN_THROW = 9 // RTN_NAT = 10 // RTN_XRESOLVE = 11 // __RTN_MAX = 12 print!("type={} ", rm.rtm_type); // scope: // RT_SCOPE_UNIVERSE = 0 : everywhere in the universe // // ... user defined values ... // // RT_SCOPE_SITE = 200 // RT_SCOPE_LINK = 253 : destination attached to link // RT_SCOPE_HOST = 254 : local address // RT_SCOPE_NOWHERE = 255 : not existing destination print!("scope={} ", rm.rtm_scope); // protocol: // RTPROT_UNSPEC = 0 // RTPROT_REDIRECT = 1 // RTPROT_KERNEL = 2 : route installed by kernel // RTPROT_BOOT = 3 : route installed during boot // RTPROT_STATIC = 4 : route installed by administrator // // Values >= RTPROT_STATIC are not interpreted by kernel, they are // just user-defined. print!("proto={} ", rm.rtm_protocol); // flags: // RTM_F_NOTIFY = 0x100: notify user of route change // RTM_F_CLONED = 0x200: this route is cloned // RTM_F_EQUALIZE = 0x400: Multipath equalizer: NI // RTM_F_PREFIX = 0x800: Prefix addresses print!("flags={:x} ", rm.rtm_flags); let mut tb: [Option<&Attr>; linux::rtattr_type_t___RTA_MAX as usize] = [None; linux::rtattr_type_t___RTA_MAX as usize]; nlh.parse(mem::size_of::(), data_attr_cb2(&mut tb))?; match rm.rtm_family as i32 { libc::AF_INET => attributes_show_ipv4(&mut tb)?, libc::AF_INET6 => attributes_show_ipv6(&mut tb)?, i @ _ => print!("unknown address family: {}", i), } println!(""); Ok(CbStatus::Ok) } fn main() -> Result<(), String> { let mut nl = Socket::open(libc::NETLINK_ROUTE, 0) .map_err(|errno| format!("mnl_socket_open: {}", errno))?; nl.bind( linux::RTMGRP_IPV4_ROUTE | linux::RTMGRP_IPV6_ROUTE, mnl::SOCKET_AUTOPID, ) .map_err(|errno| format!("mnl_socket_bind: {}", errno))?; let mut buf = mnl::default_buffer(); loop { let nrecv = nl .recvfrom(&mut buf) .map_err(|errno| format!("mnl_socket_recvfrom: {}", errno))?; match mnl::cb_run(&buf[0..nrecv], 0, 0, Some(data_cb)) { Ok(CbStatus::Ok) => continue, Ok(CbStatus::Stop) => break, Err(errno) => return Err(format!("mnl_cb_run: {}", errno)), } } Ok(()) }