rtnetlink/neighbour/
add.rs

1// SPDX-License-Identifier: MIT
2
3use std::net::IpAddr;
4
5use futures_util::stream::StreamExt;
6use netlink_packet_core::{
7    NetlinkMessage, NetlinkPayload, NLM_F_ACK, NLM_F_CREATE, NLM_F_EXCL,
8    NLM_F_REPLACE, NLM_F_REQUEST,
9};
10use netlink_packet_route::{
11    neighbour::{
12        NeighbourAddress, NeighbourAttribute, NeighbourFlags, NeighbourMessage,
13        NeighbourState,
14    },
15    route::RouteType,
16    AddressFamily, RouteNetlinkMessage,
17};
18
19use crate::{Error, Handle};
20
21pub struct NeighbourAddRequest {
22    handle: Handle,
23    message: NeighbourMessage,
24    replace: bool,
25}
26
27impl NeighbourAddRequest {
28    pub(crate) fn new(handle: Handle, index: u32, destination: IpAddr) -> Self {
29        let mut message = NeighbourMessage::default();
30
31        message.header.family = match destination {
32            IpAddr::V4(_) => AddressFamily::Inet,
33            IpAddr::V6(_) => AddressFamily::Inet6,
34        };
35
36        message.header.ifindex = index;
37        message.header.state = NeighbourState::Permanent;
38        message.header.kind = RouteType::Unspec;
39
40        message.attributes.push(NeighbourAttribute::Destination(
41            match destination {
42                IpAddr::V4(v4) => NeighbourAddress::Inet(v4),
43                IpAddr::V6(v6) => NeighbourAddress::Inet6(v6),
44            },
45        ));
46
47        NeighbourAddRequest {
48            handle,
49            message,
50            replace: false,
51        }
52    }
53
54    #[cfg(not(target_os = "freebsd"))]
55    pub(crate) fn new_bridge(handle: Handle, index: u32, lla: &[u8]) -> Self {
56        let mut message = NeighbourMessage::default();
57
58        message.header.family = AddressFamily::Bridge;
59        message.header.ifindex = index;
60        message.header.state = NeighbourState::Permanent;
61        message.header.kind = RouteType::Unspec;
62
63        message
64            .attributes
65            .push(NeighbourAttribute::LinkLocalAddress(lla.to_vec()));
66
67        NeighbourAddRequest {
68            handle,
69            message,
70            replace: false,
71        }
72    }
73
74    /// Set a bitmask of states for the neighbor cache entry.
75    /// It should be a combination of `NUD_*` constants.
76    pub fn state(mut self, state: NeighbourState) -> Self {
77        self.message.header.state = state;
78        self
79    }
80
81    /// Set flags for the neighbor cache entry.
82    /// It should be a combination of `NTF_*` constants.
83    pub fn flags(mut self, flags: NeighbourFlags) -> Self {
84        self.message.header.flags = flags;
85        self
86    }
87
88    /// Set attributes applicable to the the neighbor cache entry.
89    /// It should be one of `NDA_*` constants.
90    pub fn kind(mut self, kind: RouteType) -> Self {
91        self.message.header.kind = kind;
92        self
93    }
94
95    /// Set a neighbor cache link layer address (see `NDA_LLADDR` for details).
96    pub fn link_local_address(mut self, addr: &[u8]) -> Self {
97        let lla =
98            self.message
99                .attributes
100                .iter_mut()
101                .find_map(|nla| match nla {
102                    NeighbourAttribute::LinkLocalAddress(lla) => Some(lla),
103                    _ => None,
104                });
105
106        if let Some(lla) = lla {
107            *lla = addr.to_vec();
108        } else {
109            self.message
110                .attributes
111                .push(NeighbourAttribute::LinkLocalAddress(addr.to_vec()));
112        }
113
114        self
115    }
116
117    /// Set the destination address for the neighbour (see `NDA_DST` for
118    /// details).
119    pub fn destination(mut self, addr: IpAddr) -> Self {
120        let dst =
121            self.message
122                .attributes
123                .iter_mut()
124                .find_map(|nla| match nla {
125                    NeighbourAttribute::Destination(dst) => Some(dst),
126                    _ => None,
127                });
128
129        let addr = match addr {
130            IpAddr::V4(v4) => NeighbourAddress::Inet(v4),
131            IpAddr::V6(v6) => NeighbourAddress::Inet6(v6),
132        };
133
134        if let Some(dst) = dst {
135            *dst = addr;
136        } else {
137            self.message
138                .attributes
139                .push(NeighbourAttribute::Destination(addr));
140        }
141
142        self
143    }
144
145    /// Replace existing matching neighbor.
146    pub fn replace(self) -> Self {
147        Self {
148            replace: true,
149            ..self
150        }
151    }
152
153    /// Execute the request.
154    pub async fn execute(self) -> Result<(), Error> {
155        let NeighbourAddRequest {
156            mut handle,
157            message,
158            replace,
159        } = self;
160
161        let mut req =
162            NetlinkMessage::from(RouteNetlinkMessage::NewNeighbour(message));
163        let replace = if replace { NLM_F_REPLACE } else { NLM_F_EXCL };
164        req.header.flags = NLM_F_REQUEST | NLM_F_ACK | replace | NLM_F_CREATE;
165
166        let mut response = handle.request(req)?;
167        while let Some(message) = response.next().await {
168            if let NetlinkPayload::Error(err) = message.payload {
169                return Err(Error::NetlinkError(err));
170            }
171        }
172
173        Ok(())
174    }
175
176    /// Return a mutable reference to the request message.
177    pub fn message_mut(&mut self) -> &mut NeighbourMessage {
178        &mut self.message
179    }
180}