Skip to main content

ip_nlroute/route/
get.rs

1use crate::NetlinkRouteHandle;
2use crate::error::Error;
3use crate::route::{Route, RouteGetResponse, RouteProtocol};
4#[cfg(all(target_os = "linux", feature = "netlink"))]
5use crate::util::mappers::ip::rtattr_to_ipv4;
6
7#[cfg(all(target_os = "linux", feature = "netlink"))]
8use neli::{
9    attr::Attribute,
10    consts::{
11        nl::NlmF,
12        rtnl::{RtAddrFamily, RtScope, RtTable, Rta, Rtm, Rtn, Rtprot},
13    },
14    nl::{NlPayload, Nlmsghdr},
15    router::synchronous::NlRouterReceiverHandle,
16    rtnl::{Rtmsg, RtmsgBuilder},
17};
18
19pub struct RouteGetRequest;
20
21impl Default for RouteGetRequest {
22    fn default() -> Self {
23        Self::new()
24    }
25}
26
27impl RouteGetRequest {
28    pub fn new() -> Self {
29        RouteGetRequest
30    }
31
32    #[cfg(not(all(target_os = "linux", feature = "netlink")))]
33    pub fn send(&self, _h: &mut NetlinkRouteHandle) -> Result<RouteGetResponse, Error> {
34        Err(Error::NotImplemented)
35    }
36    #[cfg(all(target_os = "linux", feature = "netlink"))]
37    pub fn send(&self, h: &mut NetlinkRouteHandle) -> Result<RouteGetResponse, Error> {
38        use nix::net::if_::if_indextoname;
39        let rtmsg = RtmsgBuilder::default()
40            .rtm_family(RtAddrFamily::Inet)
41            .rtm_dst_len(0)
42            .rtm_src_len(0)
43            .rtm_tos(0)
44            .rtm_table(RtTable::Unspec)
45            .rtm_protocol(Rtprot::Unspec)
46            .rtm_scope(RtScope::Universe)
47            .rtm_type(Rtn::Unspec)
48            .build()?;
49
50        let recv: NlRouterReceiverHandle<Rtm, Rtmsg> = h
51            .rtnl
52            .send(Rtm::Getroute, NlmF::DUMP, NlPayload::Payload(rtmsg))
53            .map_err(|e| Error::Send(Box::new(e)))?;
54
55        let mut routes = Vec::new();
56
57        for response in recv {
58            let header: Nlmsghdr<Rtm, Rtmsg> = response.map_err(|e| Error::Receive(Box::new(e)))?;
59            if let NlPayload::Payload(p) = header.nl_payload() {
60                if header.nl_type() != &Rtm::Newroute {
61                    return Err(Error::UnexpectedNlType {
62                        expected: format!("{:?}", Rtm::Newroute),
63                        actual: format!("{:?}", header.nl_type()),
64                    });
65                }
66
67                if p.rtm_table() != &RtTable::Main {
68                    continue;
69                }
70
71                let dst_prefix_len = *p.rtm_dst_len();
72                let protocol: RouteProtocol = (*p.rtm_protocol()).into();
73                let scope = (*p.rtm_scope()).into();
74                let route_type = (*p.rtm_type()).into();
75                let flags = (*p.rtm_flags()).into();
76
77                let mut dst = None;
78                let mut gateway = None;
79                let mut prefsrc = None;
80                let mut oif: Option<u32> = None;
81                let mut metric = None;
82
83                for rtattr in p.rtattrs().iter() {
84                    match *rtattr.rta_type() {
85                        Rta::Dst => dst = Some(rtattr_to_ipv4(rtattr)?),
86                        Rta::Gateway => gateway = Some(rtattr_to_ipv4(rtattr)?),
87                        Rta::Prefsrc => prefsrc = Some(rtattr_to_ipv4(rtattr)?),
88                        Rta::Oif => {
89                            oif = Some(rtattr.get_payload_as::<u32>().map_err(|e| {
90                                Error::Deserialise {
91                                    what: "output interface index",
92                                    source: Box::new(e),
93                                }
94                            })?)
95                        }
96                        Rta::Priority => {
97                            metric = Some(rtattr.get_payload_as::<u32>().map_err(|e| {
98                                Error::Deserialise {
99                                    what: "route metric",
100                                    source: Box::new(e),
101                                }
102                            })?)
103                        }
104                        _ => {}
105                    }
106                }
107
108                let oif_name =
109                    oif.and_then(|idx| if_indextoname(idx).ok().and_then(|n| n.into_string().ok()));
110
111                routes.push(Route {
112                    dst,
113                    dst_prefix_len,
114                    gateway,
115                    prefsrc,
116                    oif_name,
117                    protocol,
118                    scope,
119                    route_type,
120                    metric,
121                    flags,
122                });
123            }
124        }
125
126        Ok(RouteGetResponse { routes })
127    }
128}