rtnetlink/addr/
get.rs

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