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}