rtnetlink/addr/
get.rs

1// SPDX-License-Identifier: MIT
2
3use futures::{
4    future::{self, Either},
5    stream::{StreamExt, TryStream, TryStreamExt},
6    FutureExt,
7};
8use std::net::IpAddr;
9
10use netlink_packet_core::{NetlinkMessage, NLM_F_DUMP, NLM_F_REQUEST};
11use netlink_packet_route::{nlas::address::Nla, AddressMessage, RtnlMessage};
12
13use crate::{try_rtnl, Error, Handle};
14
15pub struct AddressGetRequest {
16    handle: Handle,
17    message: AddressMessage,
18    filter_builder: AddressFilterBuilder,
19}
20
21impl AddressGetRequest {
22    pub(crate) fn new(handle: Handle) -> Self {
23        AddressGetRequest {
24            handle,
25            message: AddressMessage::default(),
26            filter_builder: AddressFilterBuilder::new(),
27        }
28    }
29
30    pub fn message_mut(&mut self) -> &mut AddressMessage {
31        &mut self.message
32    }
33
34    pub fn execute(self) -> impl TryStream<Ok = AddressMessage, Error = Error> {
35        let AddressGetRequest {
36            mut handle,
37            message,
38            filter_builder,
39        } = self;
40
41        let mut req = NetlinkMessage::from(RtnlMessage::GetAddress(message));
42        req.header.flags = NLM_F_REQUEST | NLM_F_DUMP;
43
44        let filter = filter_builder.build();
45        match handle.request(req) {
46            Ok(response) => Either::Left(
47                response
48                    .map(move |msg| Ok(try_rtnl!(msg, RtnlMessage::NewAddress)))
49                    .try_filter(move |msg| future::ready(filter(msg))),
50            ),
51            Err(e) => Either::Right(
52                future::err::<AddressMessage, Error>(e).into_stream(),
53            ),
54        }
55    }
56
57    /// Return only the addresses of the given interface.
58    pub fn set_link_index_filter(mut self, index: u32) -> Self {
59        self.filter_builder.index = Some(index);
60        self
61    }
62
63    /// Return only the addresses of the given prefix length.
64    pub fn set_prefix_length_filter(mut self, prefix: u8) -> Self {
65        self.filter_builder.prefix_len = Some(prefix);
66        self
67    }
68
69    /// Return only the addresses of the given prefix length.
70    pub fn set_address_filter(mut self, address: IpAddr) -> Self {
71        self.filter_builder.address = Some(address);
72        self
73    }
74}
75
76// The reason for having filters, is that we cannot retrieve addresses
77// that match the given message, like we do for links.
78//
79// See:
80// https://lists.infradead.org/pipermail/libnl/2013-June/001014.html
81// https://patchwork.ozlabs.org/patch/133440/
82#[derive(Default)]
83struct AddressFilterBuilder {
84    index: Option<u32>,
85    address: Option<IpAddr>,
86    prefix_len: Option<u8>,
87}
88
89impl AddressFilterBuilder {
90    fn new() -> Self {
91        Default::default()
92    }
93
94    fn build(self) -> impl Fn(&AddressMessage) -> bool {
95        use Nla::*;
96
97        move |msg: &AddressMessage| {
98            if let Some(index) = self.index {
99                if msg.header.index != index {
100                    return false;
101                }
102            }
103
104            if let Some(prefix_len) = self.prefix_len {
105                if msg.header.prefix_len != prefix_len {
106                    return false;
107                }
108            }
109
110            if let Some(address) = self.address {
111                for nla in msg.nlas.iter() {
112                    if let Unspec(x) | Address(x) | Local(x) | Multicast(x)
113                    | Anycast(x) = nla
114                    {
115                        let is_match = match address {
116                            IpAddr::V4(address) => {
117                                x[..] == address.octets()[..]
118                            }
119                            IpAddr::V6(address) => {
120                                x[..] == address.octets()[..]
121                            }
122                        };
123                        if is_match {
124                            return true;
125                        }
126                    }
127                }
128                return false;
129            }
130            true
131        }
132    }
133}