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