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}