rtnetlink/link/
add.rs

1// SPDX-License-Identifier: MIT
2
3use futures::stream::StreamExt;
4
5use crate::{
6    packet::{
7        nlas::link::{Info, InfoData, InfoKind, InfoMacVlan, InfoVlan, InfoVxlan, Nla, VethInfo},
8        LinkMessage,
9        NetlinkMessage,
10        RtnlMessage,
11        IFF_UP,
12        NLM_F_ACK,
13        NLM_F_CREATE,
14        NLM_F_EXCL,
15        NLM_F_REPLACE,
16        NLM_F_REQUEST,
17    },
18    try_nl,
19    Error,
20    Handle,
21};
22
23/// A request to create a new vxlan link.
24///  This is equivalent to `ip link add NAME vxlan id ID ...` commands.
25/// It provides methods to customize the creation of the vxlan interface
26/// It provides almost all parameters that are listed by `man ip link`.
27pub struct VxlanAddRequest {
28    request: LinkAddRequest,
29    info_data: Vec<InfoVxlan>,
30}
31
32impl VxlanAddRequest {
33    /// Execute the request.
34    pub async fn execute(self) -> Result<(), Error> {
35        let s = self
36            .request
37            .link_info(InfoKind::Vxlan, Some(InfoData::Vxlan(self.info_data)));
38        s.execute().await
39    }
40
41    /// Sets the interface up
42    /// This is equivalent to `ip link set up dev NAME`.
43    pub fn up(mut self) -> Self {
44        self.request = self.request.up();
45        self
46    }
47
48    /// Adds the `dev` attribute to the VXLAN
49    /// This is equivalent to `ip link add name NAME type vxlan id VNI dev LINK`,
50    ///  dev LINK - specifies the physical device to use
51    ///  for tunnel endpoint communication.
52    /// But instead of specifing a link name (`LINK`), we specify a link index.
53    pub fn link(mut self, index: u32) -> Self {
54        self.info_data.push(InfoVxlan::Link(index));
55        self
56    }
57
58    /// Adds the `dstport` attribute to the VXLAN
59    /// This is equivalent to `ip link add name NAME type vxlan id VNI dstport PORT`.
60    /// dstport PORT - specifies the UDP destination port to
61    /// communicate to the remote VXLAN tunnel endpoint.
62    pub fn port(mut self, port: u16) -> Self {
63        self.info_data.push(InfoVxlan::Port(port));
64        self
65    }
66
67    /// Adds the `group` attribute to the VXLAN
68    /// This is equivalent to `ip link add name NAME type vxlan id VNI group IPADDR`,
69    /// group IPADDR - specifies the multicast IP address to join.
70    /// This function takes an IPv4 address
71    /// WARNING: only one between `remote` and `group` can be present.
72    pub fn group(mut self, addr: std::net::Ipv4Addr) -> Self {
73        self.info_data
74            .push(InfoVxlan::Group(addr.octets().to_vec()));
75        self
76    }
77
78    /// Adds the `group` attribute to the VXLAN
79    /// This is equivalent to `ip link add name NAME type vxlan id VNI group IPADDR`,
80    /// group IPADDR - specifies the multicast IP address to join.
81    /// This function takes an IPv6 address
82    /// WARNING: only one between `remote` and `group` can be present.
83    pub fn group6(mut self, addr: std::net::Ipv6Addr) -> Self {
84        self.info_data
85            .push(InfoVxlan::Group6(addr.octets().to_vec()));
86        self
87    }
88
89    /// Adds the `remote` attribute to the VXLAN
90    /// This is equivalent to `ip link add name NAME type vxlan id VNI remote IPADDR`,
91    /// remote IPADDR - specifies the unicast destination IP
92    /// address to use in outgoing packets when the
93    /// destination link layer address is not known in the
94    /// VXLAN device forwarding database.
95    /// This function takes an IPv4 address.
96    /// WARNING: only one between `remote` and `group` can be present.
97    pub fn remote(self, addr: std::net::Ipv4Addr) -> Self {
98        self.group(addr)
99    }
100
101    /// Adds the `remote` attribute to the VXLAN
102    /// This is equivalent to `ip link add name NAME type vxlan id VNI remote IPADDR`,
103    /// remote IPADDR - specifies the unicast destination IP
104    /// address to use in outgoing packets when the
105    /// destination link layer address is not known in the
106    /// VXLAN device forwarding database.
107    /// This function takes an IPv6 address.
108    /// WARNING: only one between `remote` and `group` can be present.
109    pub fn remote6(self, addr: std::net::Ipv6Addr) -> Self {
110        self.group6(addr)
111    }
112
113    /// Adds the `local` attribute to the VXLAN
114    /// This is equivalent to `ip link add name NAME type vxlan id VNI local IPADDR`,
115    /// local IPADDR - specifies the source IP address to use in outgoing packets.
116    /// This function takes an IPv4 address.
117    pub fn local(mut self, addr: std::net::Ipv4Addr) -> Self {
118        self.info_data
119            .push(InfoVxlan::Local(addr.octets().to_vec()));
120        self
121    }
122
123    /// Adds the `local` attribute to the VXLAN
124    /// This is equivalent to `ip link add name NAME type vxlan id VNI local IPADDR`,
125    /// local IPADDR - specifies the source IP address to use in outgoing packets.
126    /// This function takes an IPv6 address.
127    pub fn local6(mut self, addr: std::net::Ipv6Addr) -> Self {
128        self.info_data
129            .push(InfoVxlan::Local6(addr.octets().to_vec()));
130        self
131    }
132
133    /// Adds the `tos` attribute to the VXLAN
134    /// This is equivalent to `ip link add name NAME type vxlan id VNI tos TOS`.
135    /// tos TOS - specifies the TOS value to use in outgoing packets.
136    pub fn tos(mut self, tos: u8) -> Self {
137        self.info_data.push(InfoVxlan::Tos(tos));
138        self
139    }
140
141    /// Adds the `ttl` attribute to the VXLAN
142    /// This is equivalent to `ip link add name NAME type vxlan id VNI ttl TTL`.
143    /// ttl TTL - specifies the TTL value to use in outgoing packets.
144    pub fn ttl(mut self, ttl: u8) -> Self {
145        self.info_data.push(InfoVxlan::Ttl(ttl));
146        self
147    }
148
149    /// Adds the `flowlabel` attribute to the VXLAN
150    /// This is equivalent to `ip link add name NAME type vxlan id VNI flowlabel LABEL`.
151    /// flowlabel LABEL - specifies the flow label to use in outgoing packets.
152    pub fn label(mut self, label: u32) -> Self {
153        self.info_data.push(InfoVxlan::Label(label));
154        self
155    }
156
157    /// Adds the `learning` attribute to the VXLAN
158    /// This is equivalent to `ip link add name NAME type vxlan id VNI [no]learning`.
159    /// [no]learning - specifies if unknown source link layer
160    /// addresses and IP addresses are entered into the VXLAN
161    /// device forwarding database.
162    pub fn learning(mut self, learning: u8) -> Self {
163        self.info_data.push(InfoVxlan::Learning(learning));
164        self
165    }
166
167    /// Adds the `ageing` attribute to the VXLAN
168    /// This is equivalent to `ip link add name NAME type vxlan id VNI ageing SECONDS`.
169    /// ageing SECONDS - specifies the lifetime in seconds of
170    /// FDB entries learnt by the kernel.
171    pub fn ageing(mut self, seconds: u32) -> Self {
172        self.info_data.push(InfoVxlan::Ageing(seconds));
173        self
174    }
175
176    /// Adds the `maxaddress` attribute to the VXLAN
177    /// This is equivalent to `ip link add name NAME type vxlan id VNI maxaddress LIMIT`.
178    /// maxaddress LIMIT - specifies the maximum number of
179    /// FDB entries.
180    pub fn limit(mut self, limit: u32) -> Self {
181        self.info_data.push(InfoVxlan::Limit(limit));
182        self
183    }
184
185    /// Adds the `srcport` attribute to the VXLAN
186    /// This is equivalent to `ip link add name NAME type vxlan id VNI srcport MIN MAX`.
187    /// srcport MIN MAX - specifies the range of port numbers
188    /// to use as UDP source ports to communicate to the
189    /// remote VXLAN tunnel endpoint.
190    pub fn port_range(mut self, min: u16, max: u16) -> Self {
191        self.info_data.push(InfoVxlan::PortRange((min, max)));
192        self
193    }
194
195    /// Adds the `proxy` attribute to the VXLAN
196    /// This is equivalent to `ip link add name NAME type vxlan id VNI [no]proxy`.
197    /// [no]proxy - specifies ARP proxy is turned on.
198    pub fn proxy(mut self, proxy: u8) -> Self {
199        self.info_data.push(InfoVxlan::Proxy(proxy));
200        self
201    }
202
203    /// Adds the `rsc` attribute to the VXLAN
204    /// This is equivalent to `ip link add name NAME type vxlan id VNI [no]rsc`.
205    /// [no]rsc - specifies if route short circuit is turned on.
206    pub fn rsc(mut self, rsc: u8) -> Self {
207        self.info_data.push(InfoVxlan::Rsc(rsc));
208        self
209    }
210
211    // Adds the `l2miss` attribute to the VXLAN
212    /// This is equivalent to `ip link add name NAME type vxlan id VNI [no]l2miss`.
213    /// [no]l2miss - specifies if netlink LLADDR miss notifications are generated.
214    pub fn l2miss(mut self, l2miss: u8) -> Self {
215        self.info_data.push(InfoVxlan::L2Miss(l2miss));
216        self
217    }
218
219    // Adds the `l3miss` attribute to the VXLAN
220    /// This is equivalent to `ip link add name NAME type vxlan id VNI [no]l3miss`.
221    /// [no]l3miss - specifies if netlink IP ADDR miss notifications are generated.
222    pub fn l3miss(mut self, l3miss: u8) -> Self {
223        self.info_data.push(InfoVxlan::L3Miss(l3miss));
224        self
225    }
226
227    pub fn collect_metadata(mut self, collect_metadata: u8) -> Self {
228        self.info_data
229            .push(InfoVxlan::CollectMetadata(collect_metadata));
230        self
231    }
232
233    // Adds the `udp_csum` attribute to the VXLAN
234    /// This is equivalent to `ip link add name NAME type vxlan id VNI [no]udp_csum`.
235    /// [no]udpcsum - specifies if UDP checksum is calculated for transmitted packets over IPv4.
236    pub fn udp_csum(mut self, udp_csum: u8) -> Self {
237        self.info_data.push(InfoVxlan::UDPCsum(udp_csum));
238        self
239    }
240}
241
242/// A request to create a new link. This is equivalent to the `ip link add` commands.
243///
244/// A few methods for common actions (creating a veth pair, creating a vlan interface, etc.) are
245/// provided, but custom requests can be made using the [`message_mut()`](#method.message_mut)
246/// accessor.
247pub struct LinkAddRequest {
248    handle: Handle,
249    message: LinkMessage,
250    replace: bool,
251}
252
253impl LinkAddRequest {
254    pub(crate) fn new(handle: Handle) -> Self {
255        LinkAddRequest {
256            handle,
257            message: LinkMessage::default(),
258            replace: false,
259        }
260    }
261
262    /// Execute the request.
263    pub async fn execute(self) -> Result<(), Error> {
264        let LinkAddRequest {
265            mut handle,
266            message,
267            replace,
268        } = self;
269        let mut req = NetlinkMessage::from(RtnlMessage::NewLink(message));
270        let replace = if replace { NLM_F_REPLACE } else { NLM_F_EXCL };
271        req.header.flags = NLM_F_REQUEST | NLM_F_ACK | replace | NLM_F_CREATE;
272
273        let mut response = handle.request(req)?;
274        while let Some(message) = response.next().await {
275            try_nl!(message);
276        }
277        Ok(())
278    }
279
280    /// Return a mutable reference to the request message.
281    ///
282    /// # Example
283    ///
284    /// Let's say we want to create a vlan interface on a link with id 6. By default, the
285    /// [`vlan()`](#method.vlan) method would create a request with the `IFF_UP` link set, so that the
286    /// interface is up after creation. If we want to create a interface tha tis down by default we
287    /// could do:
288    ///
289    /// ```rust,no_run
290    /// use futures::Future;
291    /// use rtnetlink::{Handle, new_connection, packet::IFF_UP};
292    ///
293    /// async fn run(handle: Handle) -> Result<(), String> {
294    ///     let vlan_id = 100;
295    ///     let link_id = 6;
296    ///     let mut request = handle.link().add().vlan("my-vlan-itf".into(), link_id, vlan_id);
297    ///     // unset the IFF_UP flag before sending the request
298    ///     request.message_mut().header.flags &= !IFF_UP;
299    ///     request.message_mut().header.change_mask &= !IFF_UP;
300    ///     // send the request
301    ///     request.execute().await.map_err(|e| format!("{}", e))
302    /// }
303    pub fn message_mut(&mut self) -> &mut LinkMessage {
304        &mut self.message
305    }
306
307    /// Create a dummy link.
308    /// This is equivalent to `ip link add NAME type dummy`.
309    pub fn dummy(self, name: String) -> Self {
310        self.name(name).link_info(InfoKind::Dummy, None).up()
311    }
312
313    /// Create a veth pair.
314    /// This is equivalent to `ip link add NAME1 type veth peer name NAME2`.
315    pub fn veth(self, name: String, peer_name: String) -> Self {
316        // NOTE: `name` is the name of the peer in the netlink message (ie the link created via the
317        // VethInfo::Peer attribute, and `peer_name` is the name in the main netlink message.
318        // This is a bit weird, but it's all hidden from the user.
319
320        let mut peer = LinkMessage::default();
321        // FIXME: we get a -107 (ENOTCONN) (???) when trying to set `name` up.
322        // peer.header.flags = LinkFlags::from(IFF_UP);
323        // peer.header.change_mask = LinkFlags::from(IFF_UP);
324        peer.nlas.push(Nla::IfName(name));
325        let link_info_data = InfoData::Veth(VethInfo::Peer(peer));
326        self.name(peer_name)
327            .up() // iproute2 does not set this one up
328            .link_info(InfoKind::Veth, Some(link_info_data))
329    }
330
331    /// Create VLAN on a link.
332    /// This is equivalent to `ip link add link LINK name NAME type vlan id VLAN_ID`,
333    /// but instead of specifying a link name (`LINK`), we specify a link index.
334    pub fn vlan(self, name: String, index: u32, vlan_id: u16) -> Self {
335        self.name(name)
336            .link_info(
337                InfoKind::Vlan,
338                Some(InfoData::Vlan(vec![InfoVlan::Id(vlan_id)])),
339            )
340            .append_nla(Nla::Link(index))
341            .up()
342    }
343
344    /// Create macvlan on a link.
345    /// This is equivalent to `ip link add name NAME link LINK type macvlan mode MACVLAN_MODE`,
346    ///   but instead of specifying a link name (`LINK`), we specify a link index.
347    /// The MACVLAN_MODE is an integer consisting of flags from MACVLAN_MODE (netlink-packet-route/src/rtnl/constants.rs)
348    ///   being: _PRIVATE, _VEPA, _BRIDGE, _PASSTHRU, _SOURCE, which can be *combined*.
349    pub fn macvlan(self, name: String, index: u32, mode: u32) -> Self {
350        self.name(name)
351            .link_info(
352                InfoKind::MacVlan,
353                Some(InfoData::MacVlan(vec![InfoMacVlan::Mode(mode)])),
354            )
355            .append_nla(Nla::Link(index))
356            .up()
357    }
358
359    /// Create a VxLAN
360    /// This is equivalent to `ip link add name NAME type vxlan id VNI`,
361    /// it returns a VxlanAddRequest to further customize the vxlan
362    /// interface creation.
363    pub fn vxlan(self, name: String, vni: u32) -> VxlanAddRequest {
364        let s = self.name(name);
365        VxlanAddRequest {
366            request: s,
367            info_data: vec![InfoVxlan::Id(vni)],
368        }
369    }
370
371    /// Create a new bridge.
372    /// This is equivalent to `ip link add link NAME type bridge`.
373    pub fn bridge(self, name: String) -> Self {
374        self.name(name.clone())
375            .link_info(InfoKind::Bridge, None)
376            .append_nla(Nla::IfName(name))
377    }
378
379    /// Replace existing matching link.
380    pub fn replace(self) -> Self {
381        Self {
382            replace: true,
383            ..self
384        }
385    }
386
387    fn up(mut self) -> Self {
388        self.message.header.flags = IFF_UP;
389        self.message.header.change_mask = IFF_UP;
390        self
391    }
392
393    fn link_info(self, kind: InfoKind, data: Option<InfoData>) -> Self {
394        let mut link_info_nlas = vec![Info::Kind(kind)];
395        if let Some(data) = data {
396            link_info_nlas.push(Info::Data(data));
397        }
398        self.append_nla(Nla::Info(link_info_nlas))
399    }
400
401    fn name(mut self, name: String) -> Self {
402        self.message.nlas.push(Nla::IfName(name));
403        self
404    }
405
406    fn append_nla(mut self, nla: Nla) -> Self {
407        self.message.nlas.push(nla);
408        self
409    }
410}