// Copyright (C) 2020 - Will Glozer. All rights reserved. use std::convert::TryFrom; use std::env::args; use std::net::IpAddr; use std::os::raw::c_int; use anyhow::{anyhow, Result}; use nell::{Family, Netlink, Socket}; use nell::api::{self, Link, Route}; use nell::ffi::*; use nell::sys::{attr, message}; fn main() -> Result<()> { let mut sock = Socket::new(Family::ROUTE)?; let addr = args().nth(1); let addr = addr.as_ref().map(String::as_str); let addr = addr.unwrap_or("1.1.1.1").parse()?; let route = match route(&mut sock, addr)? { Some(route) => route, None => return Err(anyhow!("no route to {}", addr)), }; let link = link(&mut sock, route.oif)?.unwrap(); let via = route.gateway.map(|gw| { format!("via {}", gw) }).unwrap_or_else(String::new); println!("{} dev {} {}", addr, link.name, via); Ok(()) } fn route(sock: &mut Socket, dst: IpAddr) -> Result> { let mut buf = [0u8; 64]; let (octets, family) = match dst { IpAddr::V4(ip) => (ip.octets().to_vec(), AF_INET ), IpAddr::V6(ip) => (ip.octets().to_vec(), AF_INET6), }; let mut rtmsg = rtmsg::default(); rtmsg.rtm_family = family; rtmsg.rtm_dst_len = u8::try_from(octets.len())?; sock.send(&message(RTM_GETROUTE, rtmsg, &[ attr(RTA_DST, &octets[..]), ]).flags(NLM_F_REQUEST).build(&mut buf)?)?; match sock.recv::()? { Netlink::Msg(msg) => Ok(Some(api::route(&msg)?)), _ => Ok(None), } } fn link(sock: &mut Socket, index: u32) -> Result> { let flags = NLM_F_REQUEST; sock.send(&message(RTM_GETLINK, ifinfomsg { ifi_family: AF_UNSPEC, ifi_index: c_int::try_from(index)?, ..Default::default() }, &[]).flags(flags).build(&mut [])?)?; match sock.recv::()? { Netlink::Msg(msg) => Ok(Some(api::link(&msg)?)), _ => Ok(None), } }