use std::{
env, mem, process,
time::{SystemTime, UNIX_EPOCH},
};
extern crate libc;
extern crate errno;
use errno::Errno;
extern crate rsmnl as mnl;
use mnl::{AttrTbl, CbResult, CbStatus, MsgVec, Msghdr, Socket};
extern crate rsmnl_linux as linux;
use linux::rtnetlink::{RtattrTypeTbl, Rtmsg};
fn attributes_show_ip(family: i32, tb: &RtattrTypeTbl) -> Result<(), Errno> {
tb.table()?.map(|x| print!("table={} ", x));
if family == libc::AF_INET {
tb.v4dst()?.map(|x| print!("dst={} ", x));
tb.v4src()?.map(|x| print!("src={} ", x));
} else if family == libc::AF_INET6 {
tb.v6dst()?.map(|x| print!("dst={} ", x));
tb.v6src()?.map(|x| print!("src={} ", x));
}
tb.oif()?.map(|x| print!("oif={} ", x));
tb.flow()?.map(|x| print!("flow={} ", x));
if family == libc::AF_INET {
tb.v4prefsrc()?.map(|x| print!("prefsrc={} ", x));
tb.v4gateway()?.map(|x| print!("gw={} ", x));
} else if family == libc::AF_INET6 {
tb.v6prefsrc()?.map(|x| print!("prefsrc={} ", x));
tb.v6gateway()?.map(|x| print!("gw={} ", x));
}
tb.priority()?.map(|x| print!("prio={} ", x));
if let Some(xtb) = tb.metrics()? {
print!("\n metrics: ");
xtb.lock()?.map(|x| print!("lock={} ", x));
xtb.mtu()?.map(|x| print!("mtu={} ", x));
xtb.window()?.map(|x| print!("window={} ", x));
xtb.rtt()?.map(|x| print!("rtt={} ", x));
xtb.rttvar()?.map(|x| print!("rttvar={} ", x));
xtb.ssthresh()?.map(|x| print!("ssthresh={} ", x));
xtb.cwnd()?.map(|x| print!("cwnd={} ", x));
xtb.advmss()?.map(|x| print!("advmss={} ", x));
xtb.reordering()?.map(|x| print!("reordering={} ", x));
xtb.hoplimit()?.map(|x| print!("hoplimit={} ", x));
xtb.initcwnd()?.map(|x| print!("initcwnd={} ", x));
xtb.features()?.map(|x| print!("features={} ", x));
xtb.rto_min()?.map(|x| print!("rto_min={} ", x));
xtb.initrwnd()?.map(|x| print!("initrwnd={} ", x));
xtb.quickack()?.map(|x| print!("quickack={} ", x));
xtb.cc_algo()?.map(|x| print!("cc_algo={} ", x));
xtb.fastopen_no_cookie()?
.map(|x| print!("fastopen_no_cookie={} ", x));
}
Ok(())
}
fn data_cb(nlh: &Msghdr) -> CbResult {
let rm = nlh.payload::<Rtmsg>()?;
print!("family={} ", rm.rtm_family);
print!("dst_len={} ", rm.rtm_dst_len);
print!("src_len={} ", rm.rtm_src_len);
print!("tos={} ", rm.rtm_tos);
print!("table={} ", rm.rtm_table);
print!("type={} ", rm.rtm_type);
print!("scope={} ", rm.rtm_scope);
print!("proto={} ", rm.rtm_protocol);
print!("flags={:x} ", rm.rtm_flags);
attributes_show_ip(
rm.rtm_family as i32,
&RtattrTypeTbl::from_nlmsg(mem::size_of::<Rtmsg>(), nlh)?,
)?;
println!("");
Ok(CbStatus::Ok)
}
fn main() -> Result<(), String> {
let args: Vec<_> = env::args().collect();
if args.len() != 2 {
println!("Usage: {} <inet|inet6>", args[0]);
process::exit(libc::EXIT_FAILURE);
}
let mut nlv = MsgVec::new();
let mut nlh = nlv.put_header();
nlh.nlmsg_type = libc::RTM_GETROUTE;
nlh.nlmsg_flags = (libc::NLM_F_REQUEST | libc::NLM_F_DUMP) as u16;
let seq = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs() as u32;
nlh.nlmsg_seq = seq;
let rtm = nlv.put_extra_header::<Rtmsg>().unwrap();
if args[1] == "inet" {
rtm.rtm_family = libc::AF_INET as u8;
} else if args[1] == "inet6" {
rtm.rtm_family = libc::AF_INET6 as u8;
}
let mut nl = Socket::open(libc::NETLINK_ROUTE, 0)
.map_err(|errno| format!("mnl_socket_open: {}", errno))?;
nl.bind(0, mnl::SOCKET_AUTOPID)
.map_err(|errno| format!("mnl_socket_bind: {}", errno))?;
let portid = nl.portid();
nl.sendto(&nlv)
.map_err(|errno| format!("mnl_socket_sendto: {}", errno))?;
let mut buf = mnl::dump_buffer();
loop {
let nrecv = nl
.recvfrom(&mut buf)
.map_err(|errno| format!("mnl_socket_recvfrom: {}", errno))?;
match mnl::cb_run(&buf[0..nrecv], seq, portid, Some(data_cb)) {
Ok(CbStatus::Ok) => continue,
Ok(CbStatus::Stop) => break,
Err(errno) => return Err(format!("mnl_cb_run: {}", errno)),
}
}
Ok(())
}