rtnetlink/neighbour/
add.rs

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