rtnetlink/neighbour/
add.rs

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