rtnetlink/route/
get.rs

1// SPDX-License-Identifier: MIT
2
3use futures::{
4    future::{self, Either},
5    stream::{StreamExt, TryStream},
6    FutureExt,
7};
8
9use netlink_packet_route::{constants::*, NetlinkMessage, RouteMessage, RtnlMessage};
10
11use crate::{try_rtnl, Error, Handle};
12
13pub struct RouteGetRequest {
14    handle: Handle,
15    message: RouteMessage,
16}
17
18/// Internet Protocol (IP) version.
19#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)]
20pub enum IpVersion {
21    /// IPv4
22    V4,
23    /// IPv6
24    V6,
25}
26
27impl IpVersion {
28    pub(crate) fn family(self) -> u8 {
29        match self {
30            IpVersion::V4 => AF_INET as u8,
31            IpVersion::V6 => AF_INET6 as u8,
32        }
33    }
34}
35
36impl RouteGetRequest {
37    pub(crate) fn new(handle: Handle, ip_version: IpVersion) -> Self {
38        let mut message = RouteMessage::default();
39        message.header.address_family = ip_version.family();
40
41        // As per rtnetlink(7) documentation, setting the following
42        // fields to 0 gets us all the routes from all the tables
43        //
44        // > For RTM_GETROUTE, setting rtm_dst_len and rtm_src_len to 0
45        // > means you get all entries for the specified routing table.
46        // > For the other fields, except rtm_table and rtm_protocol, 0
47        // > is the wildcard.
48        message.header.destination_prefix_length = 0;
49        message.header.source_prefix_length = 0;
50        message.header.scope = RT_SCOPE_UNIVERSE;
51        message.header.kind = RTN_UNSPEC;
52
53        // I don't know if these two fields matter
54        message.header.table = RT_TABLE_UNSPEC;
55        message.header.protocol = RTPROT_UNSPEC;
56
57        RouteGetRequest { handle, message }
58    }
59
60    pub fn message_mut(&mut self) -> &mut RouteMessage {
61        &mut self.message
62    }
63
64    pub fn execute(self) -> impl TryStream<Ok = RouteMessage, Error = Error> {
65        let RouteGetRequest {
66            mut handle,
67            message,
68        } = self;
69
70        let mut req = NetlinkMessage::from(RtnlMessage::GetRoute(message));
71        req.header.flags = NLM_F_REQUEST | NLM_F_DUMP;
72
73        match handle.request(req) {
74            Ok(response) => {
75                Either::Left(response.map(move |msg| Ok(try_rtnl!(msg, RtnlMessage::NewRoute))))
76            }
77            Err(e) => Either::Right(future::err::<RouteMessage, Error>(e).into_stream()),
78        }
79    }
80}