rtnetlink/link/add.rs
1// SPDX-License-Identifier: MIT
2
3use std::{
4 iter::empty,
5 net::{Ipv4Addr, Ipv6Addr},
6};
7
8use futures::stream::StreamExt;
9use netlink_packet_core::{
10 NetlinkMessage, NLM_F_ACK, NLM_F_CREATE, NLM_F_EXCL, NLM_F_REPLACE,
11 NLM_F_REQUEST,
12};
13
14use netlink_packet_route::{
15 link::nlas::{
16 Info, InfoBond, InfoData, InfoKind, InfoMacVlan, InfoMacVtap, InfoVlan,
17 InfoVxlan, InfoXfrmTun, Nla, VethInfo, VlanQosMapping,
18 },
19 LinkMessage, RtnlMessage, IFF_UP,
20};
21
22use crate::{try_nl, Error, Handle};
23
24pub struct BondAddRequest {
25 request: LinkAddRequest,
26 info_data: Vec<InfoBond>,
27}
28
29impl BondAddRequest {
30 /// Execute the request.
31 pub async fn execute(self) -> Result<(), Error> {
32 let s = self
33 .request
34 .link_info(InfoKind::Bond, Some(InfoData::Bond(self.info_data)));
35 s.execute().await
36 }
37
38 /// Sets the interface up
39 /// This is equivalent to `ip link set up dev NAME`.
40 pub fn up(mut self) -> Self {
41 self.request = self.request.up();
42 self
43 }
44
45 /// Adds the `mode` attribute to the bond
46 /// This is equivalent to `ip link add name NAME type bond mode MODE`.
47 pub fn mode(mut self, mode: u8) -> Self {
48 self.info_data.push(InfoBond::Mode(mode));
49 self
50 }
51
52 #[deprecated(note = "Please use `active_port` instead")]
53 pub fn active_slave(mut self, active_port: u32) -> Self {
54 self.info_data.push(InfoBond::ActivePort(active_port));
55 self
56 }
57
58 /// Adds the `active_port` attribute to the bond, where `active_port`
59 /// is the ifindex of an interface attached to the bond.
60 /// This is equivalent to `ip link add name NAME type bond active_slave
61 /// ACTIVE_PORT_NAME`.
62 pub fn active_port(mut self, active_port: u32) -> Self {
63 self.info_data.push(InfoBond::ActivePort(active_port));
64 self
65 }
66
67 /// Adds the `miimon` attribute to the bond
68 /// This is equivalent to `ip link add name NAME type bond miimon MIIMON`.
69 pub fn miimon(mut self, miimon: u32) -> Self {
70 self.info_data.push(InfoBond::MiiMon(miimon));
71 self
72 }
73
74 /// Adds the `updelay` attribute to the bond
75 /// This is equivalent to `ip link add name NAME type bond updelay UPDELAY`.
76 pub fn updelay(mut self, updelay: u32) -> Self {
77 self.info_data.push(InfoBond::UpDelay(updelay));
78 self
79 }
80
81 /// Adds the `downdelay` attribute to the bond
82 /// This is equivalent to `ip link add name NAME type bond downdelay
83 /// DOWNDELAY`.
84 pub fn downdelay(mut self, downdelay: u32) -> Self {
85 self.info_data.push(InfoBond::DownDelay(downdelay));
86 self
87 }
88
89 /// Adds the `use_carrier` attribute to the bond
90 /// This is equivalent to `ip link add name NAME type bond use_carrier
91 /// USE_CARRIER`.
92 pub fn use_carrier(mut self, use_carrier: u8) -> Self {
93 self.info_data.push(InfoBond::UseCarrier(use_carrier));
94 self
95 }
96
97 /// Adds the `arp_interval` attribute to the bond
98 /// This is equivalent to `ip link add name NAME type bond arp_interval
99 /// ARP_INTERVAL`.
100 pub fn arp_interval(mut self, arp_interval: u32) -> Self {
101 self.info_data.push(InfoBond::ArpInterval(arp_interval));
102 self
103 }
104
105 /// Adds the `arp_validate` attribute to the bond
106 /// This is equivalent to `ip link add name NAME type bond arp_validate
107 /// ARP_VALIDATE`.
108 pub fn arp_validate(mut self, arp_validate: u32) -> Self {
109 self.info_data.push(InfoBond::ArpValidate(arp_validate));
110 self
111 }
112
113 /// Adds the `arp_all_targets` attribute to the bond
114 /// This is equivalent to `ip link add name NAME type bond arp_all_targets
115 /// ARP_ALL_TARGETS`
116 pub fn arp_all_targets(mut self, arp_all_targets: u32) -> Self {
117 self.info_data
118 .push(InfoBond::ArpAllTargets(arp_all_targets));
119 self
120 }
121
122 /// Adds the `primary` attribute to the bond, where `primary` is the ifindex
123 /// of an interface.
124 /// This is equivalent to `ip link add name NAME type bond primary
125 /// PRIMARY_NAME`
126 pub fn primary(mut self, primary: u32) -> Self {
127 self.info_data.push(InfoBond::Primary(primary));
128 self
129 }
130
131 /// Adds the `primary_reselect` attribute to the bond
132 /// This is equivalent to `ip link add name NAME type bond primary_reselect
133 /// PRIMARY_RESELECT`.
134 pub fn primary_reselect(mut self, primary_reselect: u8) -> Self {
135 self.info_data
136 .push(InfoBond::PrimaryReselect(primary_reselect));
137 self
138 }
139
140 /// Adds the `fail_over_mac` attribute to the bond
141 /// This is equivalent to `ip link add name NAME type bond fail_over_mac
142 /// FAIL_OVER_MAC`.
143 pub fn fail_over_mac(mut self, fail_over_mac: u8) -> Self {
144 self.info_data.push(InfoBond::FailOverMac(fail_over_mac));
145 self
146 }
147
148 /// Adds the `xmit_hash_policy` attribute to the bond
149 /// This is equivalent to `ip link add name NAME type bond xmit_hash_policy
150 /// XMIT_HASH_POLICY`.
151 pub fn xmit_hash_policy(mut self, xmit_hash_policy: u8) -> Self {
152 self.info_data
153 .push(InfoBond::XmitHashPolicy(xmit_hash_policy));
154 self
155 }
156
157 /// Adds the `resend_igmp` attribute to the bond
158 /// This is equivalent to `ip link add name NAME type bond resend_igmp
159 /// RESEND_IGMP`.
160 pub fn resend_igmp(mut self, resend_igmp: u32) -> Self {
161 self.info_data.push(InfoBond::ResendIgmp(resend_igmp));
162 self
163 }
164
165 /// Adds the `num_peer_notif` attribute to the bond
166 /// This is equivalent to `ip link add name NAME type bond num_peer_notif
167 /// NUM_PEER_NOTIF`.
168 pub fn num_peer_notif(mut self, num_peer_notif: u8) -> Self {
169 self.info_data.push(InfoBond::NumPeerNotif(num_peer_notif));
170 self
171 }
172
173 #[deprecated(note = "Please use `all_ports_active` instead")]
174 pub fn all_slaves_active(mut self, all_ports_active: u8) -> Self {
175 self.info_data
176 .push(InfoBond::AllPortsActive(all_ports_active));
177 self
178 }
179
180 /// Adds the `all_ports_active` attribute to the bond
181 /// This is equivalent to `ip link add name NAME type bond all_slaves_active
182 /// ALL_PORTS_ACTIVE`.
183 pub fn all_ports_active(mut self, all_ports_active: u8) -> Self {
184 self.info_data
185 .push(InfoBond::AllPortsActive(all_ports_active));
186 self
187 }
188
189 /// Adds the `min_links` attribute to the bond
190 /// This is equivalent to `ip link add name NAME type bond min_links
191 /// MIN_LINKS`.
192 pub fn min_links(mut self, min_links: u32) -> Self {
193 self.info_data.push(InfoBond::MinLinks(min_links));
194 self
195 }
196
197 /// Adds the `lp_interval` attribute to the bond
198 /// This is equivalent to `ip link add name NAME type bond lp_interval
199 /// LP_INTERVAL`.
200 pub fn lp_interval(mut self, lp_interval: u32) -> Self {
201 self.info_data.push(InfoBond::LpInterval(lp_interval));
202 self
203 }
204
205 /// Adds the `packets_per_port` attribute to the bond
206 /// This is equivalent to `ip link add name NAME type bond packets_per_slave
207 /// PACKETS_PER_PORT`.
208 pub fn packets_per_port(mut self, packets_per_port: u32) -> Self {
209 self.info_data
210 .push(InfoBond::PacketsPerPort(packets_per_port));
211 self
212 }
213
214 /// Adds the `ad_lacp_rate` attribute to the bond
215 /// This is equivalent to `ip link add name NAME type bond ad_lacp_rate
216 /// AD_LACP_RATE`.
217 pub fn ad_lacp_rate(mut self, ad_lacp_rate: u8) -> Self {
218 self.info_data.push(InfoBond::AdLacpRate(ad_lacp_rate));
219 self
220 }
221
222 /// Adds the `ad_select` attribute to the bond
223 /// This is equivalent to `ip link add name NAME type bond ad_select
224 /// AD_SELECT`.
225 pub fn ad_select(mut self, ad_select: u8) -> Self {
226 self.info_data.push(InfoBond::AdSelect(ad_select));
227 self
228 }
229
230 /// Adds the `ad_actor_sys_prio` attribute to the bond
231 /// This is equivalent to `ip link add name NAME type bond ad_actor_sys_prio
232 /// AD_ACTOR_SYS_PRIO`.
233 pub fn ad_actor_sys_prio(mut self, ad_actor_sys_prio: u16) -> Self {
234 self.info_data
235 .push(InfoBond::AdActorSysPrio(ad_actor_sys_prio));
236 self
237 }
238
239 /// Adds the `ad_user_port_key` attribute to the bond
240 /// This is equivalent to `ip link add name NAME type bond ad_user_port_key
241 /// AD_USER_PORT_KEY`.
242 pub fn ad_user_port_key(mut self, ad_user_port_key: u16) -> Self {
243 self.info_data
244 .push(InfoBond::AdUserPortKey(ad_user_port_key));
245 self
246 }
247
248 /// Adds the `ad_actor_system` attribute to the bond
249 /// This is equivalent to `ip link add name NAME type bond ad_actor_system
250 /// AD_ACTOR_SYSTEM`.
251 pub fn ad_actor_system(mut self, ad_actor_system: [u8; 6]) -> Self {
252 self.info_data
253 .push(InfoBond::AdActorSystem(ad_actor_system));
254 self
255 }
256
257 /// Adds the `tlb_dynamic_lb` attribute to the bond
258 /// This is equivalent to `ip link add name NAME type bond tlb_dynamic_lb
259 /// TLB_DYNAMIC_LB`.
260 pub fn tlb_dynamic_lb(mut self, tlb_dynamic_lb: u8) -> Self {
261 self.info_data.push(InfoBond::TlbDynamicLb(tlb_dynamic_lb));
262 self
263 }
264
265 /// Adds the `peer_notif_delay` attribute to the bond
266 /// This is equivalent to `ip link add name NAME type bond peer_notif_delay
267 /// PEER_NOTIF_DELAY`.
268 pub fn peer_notif_delay(mut self, peer_notif_delay: u32) -> Self {
269 self.info_data
270 .push(InfoBond::PeerNotifDelay(peer_notif_delay));
271 self
272 }
273
274 /// Adds the `ad_lacp_active` attribute to the bond
275 /// This is equivalent to `ip link add name NAME type bond ad_lacp_active
276 /// AD_LACP_ACTIVE`.
277 pub fn ad_lacp_active(mut self, ad_lacp_active: u8) -> Self {
278 self.info_data.push(InfoBond::AdLacpActive(ad_lacp_active));
279 self
280 }
281
282 /// Adds the `missed_max` attribute to the bond
283 /// This is equivalent to `ip link add name NAME type bond missed_max
284 /// MISSED_MAX`.
285 pub fn missed_max(mut self, missed_max: u8) -> Self {
286 self.info_data.push(InfoBond::MissedMax(missed_max));
287 self
288 }
289
290 /// Adds the `arp_ip_target` attribute to the bond
291 /// This is equivalent to `ip link add name NAME type bond arp_ip_target
292 /// LIST`.
293 pub fn arp_ip_target(mut self, arp_ip_target: Vec<Ipv4Addr>) -> Self {
294 self.info_data.push(InfoBond::ArpIpTarget(arp_ip_target));
295 self
296 }
297
298 /// Adds the `ns_ip6_target` attribute to the bond
299 /// This is equivalent to `ip link add name NAME type bond ns_ip6_target
300 /// LIST`.
301 pub fn ns_ip6_target(mut self, ns_ip6_target: Vec<Ipv6Addr>) -> Self {
302 self.info_data.push(InfoBond::NsIp6Target(ns_ip6_target));
303 self
304 }
305}
306
307/// A request to create a new vxlan link.
308/// This is equivalent to `ip link add NAME vxlan id ID ...` commands.
309/// It provides methods to customize the creation of the vxlan interface
310/// It provides almost all parameters that are listed by `man ip link`.
311pub struct VxlanAddRequest {
312 request: LinkAddRequest,
313 info_data: Vec<InfoVxlan>,
314}
315
316impl VxlanAddRequest {
317 /// Execute the request.
318 pub async fn execute(self) -> Result<(), Error> {
319 let s = self
320 .request
321 .link_info(InfoKind::Vxlan, Some(InfoData::Vxlan(self.info_data)));
322 s.execute().await
323 }
324
325 /// Sets the interface up
326 /// This is equivalent to `ip link set up dev NAME`.
327 pub fn up(mut self) -> Self {
328 self.request = self.request.up();
329 self
330 }
331
332 /// Adds the `dev` attribute to the VXLAN
333 /// This is equivalent to `ip link add name NAME type vxlan id VNI dev
334 /// LINK`, dev LINK - specifies the physical device to use
335 /// for tunnel endpoint communication.
336 /// But instead of specifing a link name (`LINK`), we specify a link index.
337 pub fn link(mut self, index: u32) -> Self {
338 self.info_data.push(InfoVxlan::Link(index));
339 self
340 }
341
342 /// Adds the `dstport` attribute to the VXLAN
343 /// This is equivalent to `ip link add name NAME type vxlan id VNI dstport
344 /// PORT`. dstport PORT - specifies the UDP destination port to
345 /// communicate to the remote VXLAN tunnel endpoint.
346 pub fn port(mut self, port: u16) -> Self {
347 self.info_data.push(InfoVxlan::Port(port));
348 self
349 }
350
351 /// Adds the `group` attribute to the VXLAN
352 /// This is equivalent to `ip link add name NAME type vxlan id VNI group
353 /// IPADDR`, group IPADDR - specifies the multicast IP address to join.
354 /// This function takes an IPv4 address
355 /// WARNING: only one between `remote` and `group` can be present.
356 pub fn group(mut self, addr: std::net::Ipv4Addr) -> Self {
357 self.info_data
358 .push(InfoVxlan::Group(addr.octets().to_vec()));
359 self
360 }
361
362 /// Adds the `group` attribute to the VXLAN
363 /// This is equivalent to `ip link add name NAME type vxlan id VNI group
364 /// IPADDR`, group IPADDR - specifies the multicast IP address to join.
365 /// This function takes an IPv6 address
366 /// WARNING: only one between `remote` and `group` can be present.
367 pub fn group6(mut self, addr: std::net::Ipv6Addr) -> Self {
368 self.info_data
369 .push(InfoVxlan::Group6(addr.octets().to_vec()));
370 self
371 }
372
373 /// Adds the `remote` attribute to the VXLAN
374 /// This is equivalent to `ip link add name NAME type vxlan id VNI remote
375 /// IPADDR`, remote IPADDR - specifies the unicast destination IP
376 /// address to use in outgoing packets when the
377 /// destination link layer address is not known in the
378 /// VXLAN device forwarding database.
379 /// This function takes an IPv4 address.
380 /// WARNING: only one between `remote` and `group` can be present.
381 pub fn remote(self, addr: std::net::Ipv4Addr) -> Self {
382 self.group(addr)
383 }
384
385 /// Adds the `remote` attribute to the VXLAN
386 /// This is equivalent to `ip link add name NAME type vxlan id VNI remote
387 /// IPADDR`, remote IPADDR - specifies the unicast destination IP
388 /// address to use in outgoing packets when the
389 /// destination link layer address is not known in the
390 /// VXLAN device forwarding database.
391 /// This function takes an IPv6 address.
392 /// WARNING: only one between `remote` and `group` can be present.
393 pub fn remote6(self, addr: std::net::Ipv6Addr) -> Self {
394 self.group6(addr)
395 }
396
397 /// Adds the `local` attribute to the VXLAN
398 /// This is equivalent to `ip link add name NAME type vxlan id VNI local
399 /// IPADDR`, local IPADDR - specifies the source IP address to use in
400 /// outgoing packets. This function takes an IPv4 address.
401 pub fn local(mut self, addr: std::net::Ipv4Addr) -> Self {
402 self.info_data
403 .push(InfoVxlan::Local(addr.octets().to_vec()));
404 self
405 }
406
407 /// Adds the `local` attribute to the VXLAN
408 /// This is equivalent to `ip link add name NAME type vxlan id VNI local
409 /// IPADDR`, local IPADDR - specifies the source IP address to use in
410 /// outgoing packets. This function takes an IPv6 address.
411 pub fn local6(mut self, addr: std::net::Ipv6Addr) -> Self {
412 self.info_data
413 .push(InfoVxlan::Local6(addr.octets().to_vec()));
414 self
415 }
416
417 /// Adds the `tos` attribute to the VXLAN
418 /// This is equivalent to `ip link add name NAME type vxlan id VNI tos TOS`.
419 /// tos TOS - specifies the TOS value to use in outgoing packets.
420 pub fn tos(mut self, tos: u8) -> Self {
421 self.info_data.push(InfoVxlan::Tos(tos));
422 self
423 }
424
425 /// Adds the `ttl` attribute to the VXLAN
426 /// This is equivalent to `ip link add name NAME type vxlan id VNI ttl TTL`.
427 /// ttl TTL - specifies the TTL value to use in outgoing packets.
428 pub fn ttl(mut self, ttl: u8) -> Self {
429 self.info_data.push(InfoVxlan::Ttl(ttl));
430 self
431 }
432
433 /// Adds the `flowlabel` attribute to the VXLAN
434 /// This is equivalent to `ip link add name NAME type vxlan id VNI flowlabel
435 /// LABEL`. flowlabel LABEL - specifies the flow label to use in
436 /// outgoing packets.
437 pub fn label(mut self, label: u32) -> Self {
438 self.info_data.push(InfoVxlan::Label(label));
439 self
440 }
441
442 /// Adds the `learning` attribute to the VXLAN
443 /// This is equivalent to `ip link add name NAME type vxlan id VNI
444 /// [no]learning`. [no]learning - specifies if unknown source link layer
445 /// addresses and IP addresses are entered into the VXLAN
446 /// device forwarding database.
447 pub fn learning(mut self, learning: u8) -> Self {
448 self.info_data.push(InfoVxlan::Learning(learning));
449 self
450 }
451
452 /// Adds the `ageing` attribute to the VXLAN
453 /// This is equivalent to `ip link add name NAME type vxlan id VNI ageing
454 /// SECONDS`. ageing SECONDS - specifies the lifetime in seconds of
455 /// FDB entries learnt by the kernel.
456 pub fn ageing(mut self, seconds: u32) -> Self {
457 self.info_data.push(InfoVxlan::Ageing(seconds));
458 self
459 }
460
461 /// Adds the `maxaddress` attribute to the VXLAN
462 /// This is equivalent to `ip link add name NAME type vxlan id VNI
463 /// maxaddress LIMIT`. maxaddress LIMIT - specifies the maximum number
464 /// of FDB entries.
465 pub fn limit(mut self, limit: u32) -> Self {
466 self.info_data.push(InfoVxlan::Limit(limit));
467 self
468 }
469
470 /// Adds the `srcport` attribute to the VXLAN
471 /// This is equivalent to `ip link add name NAME type vxlan id VNI srcport
472 /// MIN MAX`. srcport MIN MAX - specifies the range of port numbers
473 /// to use as UDP source ports to communicate to the
474 /// remote VXLAN tunnel endpoint.
475 pub fn port_range(mut self, min: u16, max: u16) -> Self {
476 self.info_data.push(InfoVxlan::PortRange((min, max)));
477 self
478 }
479
480 /// Adds the `proxy` attribute to the VXLAN
481 /// This is equivalent to `ip link add name NAME type vxlan id VNI
482 /// [no]proxy`. [no]proxy - specifies ARP proxy is turned on.
483 pub fn proxy(mut self, proxy: u8) -> Self {
484 self.info_data.push(InfoVxlan::Proxy(proxy));
485 self
486 }
487
488 /// Adds the `rsc` attribute to the VXLAN
489 /// This is equivalent to `ip link add name NAME type vxlan id VNI [no]rsc`.
490 /// [no]rsc - specifies if route short circuit is turned on.
491 pub fn rsc(mut self, rsc: u8) -> Self {
492 self.info_data.push(InfoVxlan::Rsc(rsc));
493 self
494 }
495
496 // Adds the `l2miss` attribute to the VXLAN
497 /// This is equivalent to `ip link add name NAME type vxlan id VNI
498 /// [no]l2miss`. [no]l2miss - specifies if netlink LLADDR miss
499 /// notifications are generated.
500 pub fn l2miss(mut self, l2miss: u8) -> Self {
501 self.info_data.push(InfoVxlan::L2Miss(l2miss));
502 self
503 }
504
505 // Adds the `l3miss` attribute to the VXLAN
506 /// This is equivalent to `ip link add name NAME type vxlan id VNI
507 /// [no]l3miss`. [no]l3miss - specifies if netlink IP ADDR miss
508 /// notifications are generated.
509 pub fn l3miss(mut self, l3miss: u8) -> Self {
510 self.info_data.push(InfoVxlan::L3Miss(l3miss));
511 self
512 }
513
514 pub fn collect_metadata(mut self, collect_metadata: u8) -> Self {
515 self.info_data
516 .push(InfoVxlan::CollectMetadata(collect_metadata));
517 self
518 }
519
520 // Adds the `udp_csum` attribute to the VXLAN
521 /// This is equivalent to `ip link add name NAME type vxlan id VNI
522 /// [no]udp_csum`. [no]udpcsum - specifies if UDP checksum is calculated
523 /// for transmitted packets over IPv4.
524 pub fn udp_csum(mut self, udp_csum: u8) -> Self {
525 self.info_data.push(InfoVxlan::UDPCsum(udp_csum));
526 self
527 }
528}
529
530/// A request to create a new link. This is equivalent to the `ip link add`
531/// commands.
532///
533/// A few methods for common actions (creating a veth pair, creating a vlan
534/// interface, etc.) are provided, but custom requests can be made using the
535/// [`message_mut()`](#method.message_mut) accessor.
536pub struct LinkAddRequest {
537 handle: Handle,
538 message: LinkMessage,
539 replace: bool,
540}
541
542/// A quality-of-service mapping between the internal priority `from` to the
543/// external vlan priority `to`.
544#[derive(Clone, Copy, Debug, PartialEq, Eq)]
545pub struct QosMapping {
546 pub from: u32,
547 pub to: u32,
548}
549
550impl From<QosMapping> for VlanQosMapping {
551 fn from(QosMapping { from, to }: QosMapping) -> Self {
552 Self::Mapping { from, to }
553 }
554}
555
556impl LinkAddRequest {
557 pub(crate) fn new(handle: Handle) -> Self {
558 LinkAddRequest {
559 handle,
560 message: LinkMessage::default(),
561 replace: false,
562 }
563 }
564
565 /// Execute the request.
566 pub async fn execute(self) -> Result<(), Error> {
567 let LinkAddRequest {
568 mut handle,
569 message,
570 replace,
571 } = self;
572 let mut req = NetlinkMessage::from(RtnlMessage::NewLink(message));
573 let replace = if replace { NLM_F_REPLACE } else { NLM_F_EXCL };
574 req.header.flags = NLM_F_REQUEST | NLM_F_ACK | replace | NLM_F_CREATE;
575
576 let mut response = handle.request(req)?;
577 while let Some(message) = response.next().await {
578 try_nl!(message);
579 }
580 Ok(())
581 }
582
583 /// Return a mutable reference to the request message.
584 ///
585 /// # Example
586 ///
587 /// Let's say we want to create a vlan interface on a link with id 6. By
588 /// default, the [`vlan()`](#method.vlan) method would create a request
589 /// with the `IFF_UP` link set, so that the interface is up after
590 /// creation. If we want to create a interface that is down by default we
591 /// could do:
592 ///
593 /// ```rust,no_run
594 /// use futures::Future;
595 /// use netlink_packet_route::IFF_UP;
596 /// use rtnetlink::{Handle, new_connection};
597 ///
598 /// async fn run(handle: Handle) -> Result<(), String> {
599 /// let vlan_id = 100;
600 /// let link_id = 6;
601 /// let mut request = handle.link().add().vlan("my-vlan-itf".into(), link_id, vlan_id);
602 /// // unset the IFF_UP flag before sending the request
603 /// request.message_mut().header.flags &= !IFF_UP;
604 /// request.message_mut().header.change_mask &= !IFF_UP;
605 /// // send the request
606 /// request.execute().await.map_err(|e| format!("{}", e))
607 /// }
608 pub fn message_mut(&mut self) -> &mut LinkMessage {
609 &mut self.message
610 }
611
612 /// Create a dummy link.
613 /// This is equivalent to `ip link add NAME type dummy`.
614 pub fn dummy(self, name: String) -> Self {
615 self.name(name).link_info(InfoKind::Dummy, None).up()
616 }
617
618 /// Create a veth pair.
619 /// This is equivalent to `ip link add NAME1 type veth peer name NAME2`.
620 pub fn veth(self, name: String, peer_name: String) -> Self {
621 // NOTE: `name` is the name of the peer in the netlink message (ie the
622 // link created via the VethInfo::Peer attribute, and
623 // `peer_name` is the name in the main netlink message.
624 // This is a bit weird, but it's all hidden from the user.
625
626 let mut peer = LinkMessage::default();
627 // FIXME: we get a -107 (ENOTCONN) (???) when trying to set `name` up.
628 // peer.header.flags = LinkFlags::from(IFF_UP);
629 // peer.header.change_mask = LinkFlags::from(IFF_UP);
630 peer.nlas.push(Nla::IfName(name));
631 let link_info_data = InfoData::Veth(VethInfo::Peer(peer));
632 self.name(peer_name)
633 .up() // iproute2 does not set this one up
634 .link_info(InfoKind::Veth, Some(link_info_data))
635 }
636
637 /// Create VLAN on a link.
638 /// This is equivalent to `ip link add link LINK name NAME type vlan id
639 /// VLAN_ID`, but instead of specifying a link name (`LINK`), we specify
640 /// a link index.
641 pub fn vlan(self, name: String, index: u32, vlan_id: u16) -> Self {
642 self.vlan_with_qos(name, index, vlan_id, empty(), empty())
643 }
644
645 /// Create VLAN on a link with ingress and egress qos mappings.
646 /// This is equivalent to `ip link add link LINK name NAME type vlan id
647 /// VLAN_ID ingress-qos-mapping INGRESS_QOS egress-qos-mapping EGRESS_QOS`,
648 /// but instead of specifying a link name (`LINK`), we specify a link index.
649 pub fn vlan_with_qos<
650 I: IntoIterator<Item = QosMapping>,
651 E: IntoIterator<Item = QosMapping>,
652 >(
653 self,
654 name: String,
655 index: u32,
656 vlan_id: u16,
657 ingress_qos: I,
658 egress_qos: E,
659 ) -> Self {
660 let mut info = vec![InfoVlan::Id(vlan_id)];
661
662 let ingress: Vec<_> =
663 ingress_qos.into_iter().map(VlanQosMapping::from).collect();
664 if !ingress.is_empty() {
665 info.push(InfoVlan::IngressQos(ingress));
666 }
667
668 let egress: Vec<_> =
669 egress_qos.into_iter().map(VlanQosMapping::from).collect();
670 if !egress.is_empty() {
671 info.push(InfoVlan::EgressQos(egress));
672 }
673
674 self.name(name)
675 .link_info(InfoKind::Vlan, Some(InfoData::Vlan(info)))
676 .append_nla(Nla::Link(index))
677 .up()
678 }
679
680 /// Create macvlan on a link.
681 /// This is equivalent to `ip link add name NAME link LINK type macvlan mode
682 /// MACVLAN_MODE`, but instead of specifying a link name (`LINK`), we
683 /// specify a link index. The MACVLAN_MODE is an integer consisting of
684 /// flags from MACVLAN_MODE (netlink-packet-route/src/rtnl/constants.rs)
685 /// being: _PRIVATE, _VEPA, _BRIDGE, _PASSTHRU, _SOURCE, which can be
686 /// *combined*.
687 pub fn macvlan(self, name: String, index: u32, mode: u32) -> Self {
688 self.name(name)
689 .link_info(
690 InfoKind::MacVlan,
691 Some(InfoData::MacVlan(vec![InfoMacVlan::Mode(mode)])),
692 )
693 .append_nla(Nla::Link(index))
694 .up()
695 }
696
697 /// Create macvtap on a link.
698 /// This is equivalent to `ip link add name NAME link LINK type macvtap mode
699 /// MACVTAP_MODE`, but instead of specifying a link name (`LINK`), we
700 /// specify a link index. The MACVTAP_MODE is an integer consisting of
701 /// flags from MACVTAP_MODE (netlink-packet-route/src/rtnl/constants.rs)
702 /// being: _PRIVATE, _VEPA, _BRIDGE, _PASSTHRU, _SOURCE, which can be
703 /// *combined*.
704 pub fn macvtap(self, name: String, index: u32, mode: u32) -> Self {
705 self.name(name)
706 .link_info(
707 InfoKind::MacVtap,
708 Some(InfoData::MacVtap(vec![InfoMacVtap::Mode(mode)])),
709 )
710 .append_nla(Nla::Link(index))
711 .up()
712 }
713
714 /// Create a VxLAN
715 /// This is equivalent to `ip link add name NAME type vxlan id VNI`,
716 /// it returns a VxlanAddRequest to further customize the vxlan
717 /// interface creation.
718 pub fn vxlan(self, name: String, vni: u32) -> VxlanAddRequest {
719 let s = self.name(name);
720 VxlanAddRequest {
721 request: s,
722 info_data: vec![InfoVxlan::Id(vni)],
723 }
724 }
725
726 /// Create xfrm tunnel
727 /// This is equivalent to `ip link add name NAME type xfrm if_id NUMBER`,
728 /// The NUMBER is a XFRM if_id which may be connected to IPsec policy
729 pub fn xfrmtun(self, name: String, ifid: u32) -> Self {
730 self.name(name)
731 .link_info(
732 InfoKind::Xfrm,
733 Some(InfoData::Xfrm(vec![InfoXfrmTun::IfId(ifid)])),
734 )
735 .up()
736 }
737
738 /// Create a new bond.
739 /// This is equivalent to `ip link add link NAME type bond`.
740 pub fn bond(self, name: String) -> BondAddRequest {
741 let s = self.name(name);
742 BondAddRequest {
743 request: s,
744 info_data: vec![],
745 }
746 }
747
748 /// Create a new bridge.
749 /// This is equivalent to `ip link add link NAME type bridge`.
750 pub fn bridge(self, name: String) -> Self {
751 self.name(name.clone())
752 .link_info(InfoKind::Bridge, None)
753 .append_nla(Nla::IfName(name))
754 }
755
756 /// Replace existing matching link.
757 pub fn replace(self) -> Self {
758 Self {
759 replace: true,
760 ..self
761 }
762 }
763
764 fn up(mut self) -> Self {
765 self.message.header.flags = IFF_UP;
766 self.message.header.change_mask = IFF_UP;
767 self
768 }
769
770 fn link_info(self, kind: InfoKind, data: Option<InfoData>) -> Self {
771 let mut link_info_nlas = vec![Info::Kind(kind)];
772 if let Some(data) = data {
773 link_info_nlas.push(Info::Data(data));
774 }
775 self.append_nla(Nla::Info(link_info_nlas))
776 }
777
778 fn name(mut self, name: String) -> Self {
779 self.message.nlas.push(Nla::IfName(name));
780 self
781 }
782
783 fn append_nla(mut self, nla: Nla) -> Self {
784 self.message.nlas.push(nla);
785 self
786 }
787}