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}