use std::{
net::{IpAddr, SocketAddr},
time::Duration,
};
use netlink_bindings::{
inet_diag::{self, BytecodeOp, BytecodeOpCode, Hostcond, Sockid, TcpState},
utils::{self, FormatEnum},
};
use netlink_socket2::NetlinkSocket;
#[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)]
#[cfg_attr(feature = "tokio", tokio::main(flavor = "current_thread"))]
#[cfg_attr(feature = "smol", macro_rules_attribute::apply(smol_macros::main))]
async fn main() {
let sock = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let addr = sock.local_addr().unwrap();
std::thread::spawn(move || {
let _conn = std::net::TcpStream::connect(addr).unwrap();
std::thread::sleep(Duration::MAX);
});
let (conn, peer_addr) = sock.accept().unwrap();
let sockaddr = conn.local_addr().unwrap();
let header = inet_diag::ReqV2 {
family: libc::AF_INET as u8,
protocol: libc::IPPROTO_TCP as u8,
states: 1 << TcpState::Established as u32,
ext: u8::MAX,
sockid: inet_diag::Sockid {
_sport_be: sockaddr.port().to_be(), _dport_be: peer_addr.port().to_be(),
..Default::default()
},
..Default::default()
};
let mut bytecode = Vec::new();
{
let mut saddr_cond = BytecodeOp {
code: BytecodeOpCode::SaddrCond as u8,
yes: 0, no: 0, };
let hc = Hostcond {
family: libc::AF_INET as u8,
prefix_len: u32::BITS as u8, port: sockaddr.port() as i32, ..Default::default()
};
let start = bytecode.len();
bytecode.extend(saddr_cond.as_slice());
bytecode.extend(hc.as_slice());
utils::encode_ip(&mut bytecode, sockaddr.ip());
let len = bytecode.len();
saddr_cond.yes = len as u8; saddr_cond.no = (len + 4) as u16; bytecode[start..(start + BytecodeOp::len())].clone_from_slice(saddr_cond.as_slice());
}
let mut request = inet_diag::Request::new() .op_tcp_diag_dump(&header);
request.encode().push_bytecode(&bytecode);
let mut sock = NetlinkSocket::new();
let mut iter = sock.request(&request).await.unwrap();
while let Some(res) = iter.recv().await {
let (header, attrs) = res.unwrap();
let (s, d) = decode_sockid(header.family, &header.sockid);
print!(
"{s} -> {d} state={:?}",
FormatEnum(header.state as u64, TcpState::from_value)
);
if let Ok(tcp) = attrs.get_tcp_info() {
print!(" rtt={}ms, rttvar={}ms", to_ms(tcp.rtt), to_ms(tcp.rttvar));
}
println!();
}
}
fn to_ms(micros: u32) -> f32 {
micros as f32 / 1000.0
}
fn decode_sockid(family: u8, sockid: &Sockid) -> (SocketAddr, SocketAddr) {
(
SocketAddr::new(decode_sockid_ip(family, &sockid.src), sockid.sport()),
SocketAddr::new(decode_sockid_ip(family, &sockid.dst), sockid.dport()),
)
}
fn decode_sockid_ip(family: u8, buf: &[u8]) -> IpAddr {
if family == libc::AF_INET as u8 {
utils::parse_ipv4(&buf[..4]).unwrap().into()
} else {
utils::parse_ipv6(&buf[..4]).unwrap().into()
}
}