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