use std:: { mem, time:: { SystemTime, UNIX_EPOCH } }; extern crate libc; extern crate errno; use errno::Errno; extern crate rsmnl as mnl; use mnl:: { Socket, MsgVec, Msghdr, Attr, CbResult, CbStatus, AttrTbl, Result, }; extern crate rsmnl_linux as linux; use linux:: { netlink:: { self, Family }, netfilter:: { nfnetlink as nfnl, nfnetlink::Nfgenmsg, nfnetlink_conntrack:: { CtnlMsgTypes, CtattrTypeTbl, }, }, }; // _I know_ enum ctattr_type is less than 64 // and all of each child is less than 16 #[derive(Debug, PartialEq, Eq, Hash)] struct CtBitset { root: u32, children: Vec, } impl CtBitset { pub fn from_nlmsg(nlh: &Msghdr) -> Result { let mut tid = CtBitset { root: 0, children: Vec::new() }; nlh.parse(mem::size_of::(), root_cb(&mut tid)) .map_err(|err| { if let Some(e) = err.downcast_ref::() { *e } else { unreachable!() } })?; Ok(tid) } } fn child_cb<'a>(cur: &'a mut u16, tagid: &'a mut CtBitset) -> impl FnMut(&Attr) -> CbResult + 'a { move |attr: &Attr| { let nla_type = attr.atype(); assert!(nla_type < 16); *cur |= 1 << attr.atype(); if attr.nla_type & libc::NLA_F_NESTED as u16 != 0 { let mut b = 0u16; attr.parse_nested(child_cb(&mut b, tagid))?; tagid.children.push(b); } Ok(CbStatus::Ok) } } fn root_cb(tagid: &mut CtBitset) -> impl FnMut(&Attr) -> CbResult + '_ { move |attr: &Attr| { let nla_type = attr.atype(); assert!(nla_type < 64); tagid.root |= 1 << nla_type; if attr.nla_type & libc::NLA_F_NESTED as u16 != 0 { let mut b = 0u16; attr.parse_nested(child_cb(&mut b, tagid))?; tagid.children.push(b); } Ok(CbStatus::Ok) } } fn data_cb(nlh: &Msghdr) -> CbResult { let tid = CtBitset::from_nlmsg(nlh)?; println!("{:?}", tid); let tb = CtattrTypeTbl::from_nlmsg(mem::size_of::(), nlh)?; print!(" "); if let Some(tuple_tb) = tb.tuple_orig()? { if let Some(ip_tb) = tuple_tb.ip()? { ip_tb.v4src()?.map(|x| print!("src={} ", x)); ip_tb.v4dst()?.map(|x| print!("dst={} ", x)); ip_tb.v6src()?.map(|x| print!("src={} ", x)); ip_tb.v6dst()?.map(|x| print!("dst={} ", x)); } if let Some(proto_tb) = tuple_tb.proto()? { proto_tb.num()?.map(|x| print!("proto={} ", x)); proto_tb.src_port()?.map(|x| print!("sport={} ", u16::from_be(*x))); proto_tb.dst_port()?.map(|x| print!("dport={} ", u16::from_be(*x))); proto_tb.icmp_id()?.map(|x| print!("id={} ", u16::from_be(*x))); proto_tb.icmp_type()?.map(|x| print!("type={} ", x)); proto_tb.icmp_code()?.map(|x| print!("code={} ", x)); proto_tb.icmpv6_id()?.map(|x| print!("id={} ", u16::from_be(*x))); proto_tb.icmpv6_type()?.map(|x| print!("type={} ", x)); proto_tb.icmpv6_code()?.map(|x| print!("code={} ", x)); } } tb.mark()?.map(|x| print!("mark={} ", u32::from_be(*x))); tb.secmark()?.map(|x| print!("secmark={} ", u32::from_be(*x))); // obsolete? if let Some(cntb) = tb.counters_orig()? { print!("original "); cntb.packets()?.map(|x| print!("packets={} ", u64::from_be(*x))); cntb.bytes()?.map(|x| print!("bytes={} ", u64::from_be(*x))); } if let Some(cntb) = tb.counters_reply()? { print!("reply "); cntb.packets()?.map(|x| print!("packets={} ", u64::from_be(*x))); cntb.bytes()?.map(|x| print!("bytes={} ", u64::from_be(*x))); } println!(""); Ok(CbStatus::Ok) } fn main() { let mut nl = Socket::open(Family::Netfilter, 0) .unwrap_or_else(|errno| panic!("mnl_socket_open: {}", errno)); nl.bind(0, mnl::SOCKET_AUTOPID) .unwrap_or_else(|errno| panic!("mnl_socket_bind: {}", errno)); let seq = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as u32; let mut nlv = MsgVec::new(); let mut nlh = nlv.put_header(); nlh.nlmsg_type = (nfnl::NFNL_SUBSYS_CTNETLINK << 8) | CtnlMsgTypes::Get as u16; nlh.nlmsg_flags = netlink::NLM_F_REQUEST | netlink::NLM_F_DUMP; nlh.nlmsg_seq = seq; let nfh = nlv.put_extra_header::().unwrap(); nfh.nfgen_family = libc::AF_INET as u8; nfh.version = nfnl::NFNETLINK_V0; nfh.res_id = 0; nl.sendto(&nlv) .unwrap_or_else(|errno| panic!("mnl_socket_sendto: {}", errno)); let mut buf = mnl::dump_buffer(); let portid = nl.portid(); loop { let nrecv = nl.recvfrom(&mut buf) .unwrap_or_else(|errno| panic!("mnl_socket_recvfrom: {}", errno)); match mnl::cb_run(&buf[..nrecv], seq, portid, Some(data_cb)) { Ok(CbStatus::Ok) => continue, Ok(CbStatus::Stop) => break, Err(errno) => panic!("mnl_cb_run: {}", errno), } } }