netlink_packet_route/link/link_info/
bond.rs

1// SPDX-License-Identifier: MIT
2
3use std::{
4    net::{IpAddr, Ipv4Addr, Ipv6Addr},
5    ops::Deref,
6};
7
8use netlink_packet_core::{
9    emit_u16, emit_u32, parse_ip, parse_mac, parse_u16, parse_u32, parse_u8,
10    DecodeError, DefaultNla, Emitable, ErrorContext, Nla, NlaBuffer,
11    NlasIterator, Parseable,
12};
13
14const IFLA_BOND_AD_INFO_AGGREGATOR: u16 = 1;
15const IFLA_BOND_AD_INFO_NUM_PORTS: u16 = 2;
16const IFLA_BOND_AD_INFO_ACTOR_KEY: u16 = 3;
17const IFLA_BOND_AD_INFO_PARTNER_KEY: u16 = 4;
18const IFLA_BOND_AD_INFO_PARTNER_MAC: u16 = 5;
19
20const IFLA_BOND_MODE: u16 = 1;
21const IFLA_BOND_ACTIVE_PORT: u16 = 2;
22const IFLA_BOND_MIIMON: u16 = 3;
23const IFLA_BOND_UPDELAY: u16 = 4;
24const IFLA_BOND_DOWNDELAY: u16 = 5;
25const IFLA_BOND_USE_CARRIER: u16 = 6;
26const IFLA_BOND_ARP_INTERVAL: u16 = 7;
27const IFLA_BOND_ARP_IP_TARGET: u16 = 8;
28const IFLA_BOND_ARP_VALIDATE: u16 = 9;
29const IFLA_BOND_ARP_ALL_TARGETS: u16 = 10;
30const IFLA_BOND_PRIMARY: u16 = 11;
31const IFLA_BOND_PRIMARY_RESELECT: u16 = 12;
32const IFLA_BOND_FAIL_OVER_MAC: u16 = 13;
33const IFLA_BOND_XMIT_HASH_POLICY: u16 = 14;
34const IFLA_BOND_RESEND_IGMP: u16 = 15;
35const IFLA_BOND_NUM_PEER_NOTIF: u16 = 16;
36const IFLA_BOND_ALL_PORTS_ACTIVE: u16 = 17;
37const IFLA_BOND_MIN_LINKS: u16 = 18;
38const IFLA_BOND_LP_INTERVAL: u16 = 19;
39const IFLA_BOND_PACKETS_PER_PORT: u16 = 20;
40const IFLA_BOND_AD_LACP_RATE: u16 = 21;
41const IFLA_BOND_AD_SELECT: u16 = 22;
42const IFLA_BOND_AD_INFO: u16 = 23;
43const IFLA_BOND_AD_ACTOR_SYS_PRIO: u16 = 24;
44const IFLA_BOND_AD_USER_PORT_KEY: u16 = 25;
45const IFLA_BOND_AD_ACTOR_SYSTEM: u16 = 26;
46const IFLA_BOND_TLB_DYNAMIC_LB: u16 = 27;
47const IFLA_BOND_PEER_NOTIF_DELAY: u16 = 28;
48const IFLA_BOND_AD_LACP_ACTIVE: u16 = 29;
49const IFLA_BOND_MISSED_MAX: u16 = 30;
50const IFLA_BOND_NS_IP6_TARGET: u16 = 31;
51
52const BOND_MODE_ROUNDROBIN: u8 = 0;
53const BOND_MODE_ACTIVEBACKUP: u8 = 1;
54const BOND_MODE_XOR: u8 = 2;
55const BOND_MODE_BROADCAST: u8 = 3;
56const BOND_MODE_8023AD: u8 = 4;
57const BOND_MODE_TLB: u8 = 5;
58const BOND_MODE_ALB: u8 = 6;
59
60const BOND_STATE_ACTIVE: u8 = 0;
61const BOND_STATE_BACKUP: u8 = 1;
62
63const BOND_ARP_VALIDATE_NONE: u32 = 0;
64const BOND_ARP_VALIDATE_ACTIVE: u32 = 1 << BOND_STATE_ACTIVE as u32;
65const BOND_ARP_VALIDATE_BACKUP: u32 = 1 << BOND_STATE_BACKUP as u32;
66const BOND_ARP_VALIDATE_ALL: u32 =
67    BOND_ARP_VALIDATE_ACTIVE | BOND_ARP_VALIDATE_BACKUP;
68const BOND_ARP_FILTER: u32 = BOND_ARP_VALIDATE_ALL + 1;
69const BOND_ARP_FILTER_ACTIVE: u32 = BOND_ARP_FILTER | BOND_ARP_VALIDATE_ACTIVE;
70const BOND_ARP_FILTER_BACKUP: u32 = BOND_ARP_FILTER | BOND_ARP_VALIDATE_BACKUP;
71const BOND_XMIT_POLICY_LAYER2: u8 = 0;
72const BOND_XMIT_POLICY_LAYER34: u8 = 1;
73const BOND_XMIT_POLICY_LAYER23: u8 = 2;
74const BOND_XMIT_POLICY_ENCAP23: u8 = 3;
75const BOND_XMIT_POLICY_ENCAP34: u8 = 4;
76const BOND_XMIT_POLICY_VLAN_SRCMAC: u8 = 5;
77const BOND_OPT_ARP_ALL_TARGETS_ANY: u32 = 0;
78const BOND_OPT_ARP_ALL_TARGETS_ALL: u32 = 1;
79const BOND_PRI_RESELECT_ALWAYS: u8 = 0;
80const BOND_PRI_RESELECT_BETTER: u8 = 1;
81const BOND_PRI_RESELECT_FAILURE: u8 = 2;
82const BOND_FOM_NONE: u8 = 0;
83const BOND_FOM_ACTIVE: u8 = 1;
84const BOND_FOM_FOLLOW: u8 = 2;
85
86#[derive(Debug, Clone, Eq, PartialEq)]
87#[non_exhaustive]
88pub enum BondAdInfo {
89    Aggregator(u16),
90    NumPorts(u16),
91    ActorKey(u16),
92    PartnerKey(u16),
93    PartnerMac([u8; 6]),
94    Other(DefaultNla),
95}
96
97impl Nla for BondAdInfo {
98    fn value_len(&self) -> usize {
99        match self {
100            Self::Aggregator(_)
101            | Self::NumPorts(_)
102            | Self::ActorKey(_)
103            | Self::PartnerKey(_) => 2,
104            Self::PartnerMac(_) => 6,
105            Self::Other(v) => v.value_len(),
106        }
107    }
108
109    fn kind(&self) -> u16 {
110        match self {
111            Self::Aggregator(_) => IFLA_BOND_AD_INFO_AGGREGATOR,
112            Self::NumPorts(_) => IFLA_BOND_AD_INFO_NUM_PORTS,
113            Self::ActorKey(_) => IFLA_BOND_AD_INFO_ACTOR_KEY,
114            Self::PartnerKey(_) => IFLA_BOND_AD_INFO_PARTNER_KEY,
115            Self::PartnerMac(_) => IFLA_BOND_AD_INFO_PARTNER_MAC,
116            Self::Other(v) => v.kind(),
117        }
118    }
119
120    fn emit_value(&self, buffer: &mut [u8]) {
121        match self {
122            Self::Aggregator(d)
123            | Self::NumPorts(d)
124            | Self::ActorKey(d)
125            | Self::PartnerKey(d) => emit_u16(buffer, *d).unwrap(),
126            Self::PartnerMac(mac) => buffer.copy_from_slice(mac),
127            Self::Other(v) => v.emit_value(buffer),
128        }
129    }
130}
131
132impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for BondAdInfo {
133    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
134        let payload = buf.value();
135        Ok(match buf.kind() {
136            IFLA_BOND_AD_INFO_AGGREGATOR => Self::Aggregator(
137                parse_u16(payload)
138                    .context("invalid IFLA_BOND_AD_INFO_AGGREGATOR value")?,
139            ),
140            IFLA_BOND_AD_INFO_NUM_PORTS => Self::NumPorts(
141                parse_u16(payload)
142                    .context("invalid IFLA_BOND_AD_INFO_NUM_PORTS value")?,
143            ),
144            IFLA_BOND_AD_INFO_ACTOR_KEY => Self::ActorKey(
145                parse_u16(payload)
146                    .context("invalid IFLA_BOND_AD_INFO_ACTOR_KEY value")?,
147            ),
148            IFLA_BOND_AD_INFO_PARTNER_KEY => Self::PartnerKey(
149                parse_u16(payload)
150                    .context("invalid IFLA_BOND_AD_INFO_PARTNER_KEY value")?,
151            ),
152            IFLA_BOND_AD_INFO_PARTNER_MAC => Self::PartnerMac(
153                parse_mac(payload)
154                    .context("invalid IFLA_BOND_AD_INFO_PARTNER_MAC value")?,
155            ),
156            _ => Self::Other(DefaultNla::parse(buf).context(format!(
157                "invalid NLA for {}: {payload:?}",
158                buf.kind()
159            ))?),
160        })
161    }
162}
163
164#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
165#[non_exhaustive]
166pub enum BondMode {
167    #[default]
168    BalanceRr,
169    ActiveBackup,
170    BalanceXor,
171    Broadcast,
172    Ieee8023Ad,
173    BalanceTlb,
174    BalanceAlb,
175    Other(u8),
176}
177
178impl From<u8> for BondMode {
179    fn from(d: u8) -> Self {
180        match d {
181            BOND_MODE_ROUNDROBIN => Self::BalanceRr,
182            BOND_MODE_ACTIVEBACKUP => Self::ActiveBackup,
183            BOND_MODE_XOR => Self::BalanceXor,
184            BOND_MODE_BROADCAST => Self::Broadcast,
185            BOND_MODE_8023AD => Self::Ieee8023Ad,
186            BOND_MODE_TLB => Self::BalanceTlb,
187            BOND_MODE_ALB => Self::BalanceAlb,
188            _ => Self::Other(d),
189        }
190    }
191}
192
193impl From<BondMode> for u8 {
194    fn from(d: BondMode) -> Self {
195        match d {
196            BondMode::BalanceRr => BOND_MODE_ROUNDROBIN,
197            BondMode::ActiveBackup => BOND_MODE_ACTIVEBACKUP,
198            BondMode::BalanceXor => BOND_MODE_XOR,
199            BondMode::Broadcast => BOND_MODE_BROADCAST,
200            BondMode::Ieee8023Ad => BOND_MODE_8023AD,
201            BondMode::BalanceTlb => BOND_MODE_TLB,
202            BondMode::BalanceAlb => BOND_MODE_ALB,
203            BondMode::Other(d) => d,
204        }
205    }
206}
207
208impl std::fmt::Display for BondMode {
209    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210        let kernel_name = match self {
211            BondMode::BalanceRr => "balance-rr",
212            BondMode::ActiveBackup => "active-backup",
213            BondMode::BalanceXor => "balance-xor",
214            BondMode::Broadcast => "broadcast",
215            BondMode::Ieee8023Ad => "802.3ad",
216            BondMode::BalanceTlb => "balance-tlb",
217            BondMode::BalanceAlb => "balance-alb",
218            BondMode::Other(d) => return write!(f, "unknown-variant ({d})"),
219        };
220
221        f.write_str(kernel_name)
222    }
223}
224
225#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
226#[non_exhaustive]
227pub enum BondArpValidate {
228    #[default]
229    None,
230    Active,
231    Backup,
232    All,
233    Filter,
234    FilterActive,
235    FilterBackup,
236    Other(u32),
237}
238
239impl From<BondArpValidate> for u32 {
240    fn from(value: BondArpValidate) -> Self {
241        match value {
242            BondArpValidate::None => BOND_ARP_VALIDATE_NONE,
243            BondArpValidate::Active => BOND_ARP_VALIDATE_ACTIVE,
244            BondArpValidate::Backup => BOND_ARP_VALIDATE_BACKUP,
245            BondArpValidate::All => BOND_ARP_VALIDATE_ALL,
246            BondArpValidate::Filter => BOND_ARP_FILTER,
247            BondArpValidate::FilterActive => BOND_ARP_FILTER_ACTIVE,
248            BondArpValidate::FilterBackup => BOND_ARP_FILTER_BACKUP,
249            BondArpValidate::Other(d) => d,
250        }
251    }
252}
253
254impl From<u32> for BondArpValidate {
255    fn from(value: u32) -> Self {
256        match value {
257            BOND_ARP_VALIDATE_NONE => BondArpValidate::None,
258            BOND_ARP_VALIDATE_ACTIVE => BondArpValidate::Active,
259            BOND_ARP_VALIDATE_BACKUP => BondArpValidate::Backup,
260            BOND_ARP_VALIDATE_ALL => BondArpValidate::All,
261            BOND_ARP_FILTER => BondArpValidate::Filter,
262            BOND_ARP_FILTER_ACTIVE => BondArpValidate::FilterActive,
263            BOND_ARP_FILTER_BACKUP => BondArpValidate::FilterBackup,
264            d => BondArpValidate::Other(d),
265        }
266    }
267}
268
269impl std::fmt::Display for BondArpValidate {
270    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
271        let kernel_name = match self {
272            BondArpValidate::None => "none",
273            BondArpValidate::Active => "active",
274            BondArpValidate::Backup => "backup",
275            BondArpValidate::All => "all",
276            BondArpValidate::Filter => "filter",
277            BondArpValidate::FilterActive => "filter_active",
278            BondArpValidate::FilterBackup => "filter_backup",
279            BondArpValidate::Other(d) => {
280                return write!(f, "unknown-variant ({d})")
281            }
282        };
283        f.write_str(kernel_name)
284    }
285}
286
287#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
288#[non_exhaustive]
289pub enum BondPrimaryReselect {
290    #[default]
291    Always,
292    Better,
293    Failure,
294    Other(u8),
295}
296
297impl From<BondPrimaryReselect> for u8 {
298    fn from(value: BondPrimaryReselect) -> Self {
299        match value {
300            BondPrimaryReselect::Always => BOND_PRI_RESELECT_ALWAYS,
301            BondPrimaryReselect::Better => BOND_PRI_RESELECT_BETTER,
302            BondPrimaryReselect::Failure => BOND_PRI_RESELECT_FAILURE,
303            BondPrimaryReselect::Other(d) => d,
304        }
305    }
306}
307
308impl From<u8> for BondPrimaryReselect {
309    fn from(value: u8) -> Self {
310        match value {
311            BOND_PRI_RESELECT_ALWAYS => BondPrimaryReselect::Always,
312            BOND_PRI_RESELECT_BETTER => BondPrimaryReselect::Better,
313            BOND_PRI_RESELECT_FAILURE => BondPrimaryReselect::Failure,
314            d => BondPrimaryReselect::Other(d),
315        }
316    }
317}
318
319impl std::fmt::Display for BondPrimaryReselect {
320    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
321        let kernel_name = match self {
322            BondPrimaryReselect::Always => "always",
323            BondPrimaryReselect::Better => "better",
324            BondPrimaryReselect::Failure => "failure",
325            BondPrimaryReselect::Other(d) => {
326                return write!(f, "unknown-variant ({d})")
327            }
328        };
329        f.write_str(kernel_name)
330    }
331}
332
333#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
334#[non_exhaustive]
335pub enum BondXmitHashPolicy {
336    #[default]
337    Layer2,
338    Layer34,
339    Layer23,
340    Encap23,
341    Encap34,
342    VlanSrcMac,
343    Other(u8),
344}
345
346impl From<BondXmitHashPolicy> for u8 {
347    fn from(value: BondXmitHashPolicy) -> Self {
348        match value {
349            BondXmitHashPolicy::Layer2 => BOND_XMIT_POLICY_LAYER2,
350            BondXmitHashPolicy::Layer34 => BOND_XMIT_POLICY_LAYER34,
351            BondXmitHashPolicy::Layer23 => BOND_XMIT_POLICY_LAYER23,
352            BondXmitHashPolicy::Encap23 => BOND_XMIT_POLICY_ENCAP23,
353            BondXmitHashPolicy::Encap34 => BOND_XMIT_POLICY_ENCAP34,
354            BondXmitHashPolicy::VlanSrcMac => BOND_XMIT_POLICY_VLAN_SRCMAC,
355            BondXmitHashPolicy::Other(d) => d,
356        }
357    }
358}
359
360impl From<u8> for BondXmitHashPolicy {
361    fn from(value: u8) -> Self {
362        match value {
363            BOND_XMIT_POLICY_LAYER2 => BondXmitHashPolicy::Layer2,
364            BOND_XMIT_POLICY_LAYER34 => BondXmitHashPolicy::Layer34,
365            BOND_XMIT_POLICY_LAYER23 => BondXmitHashPolicy::Layer23,
366            BOND_XMIT_POLICY_ENCAP23 => BondXmitHashPolicy::Encap23,
367            BOND_XMIT_POLICY_ENCAP34 => BondXmitHashPolicy::Encap34,
368            BOND_XMIT_POLICY_VLAN_SRCMAC => BondXmitHashPolicy::VlanSrcMac,
369            d => BondXmitHashPolicy::Other(d),
370        }
371    }
372}
373
374impl std::fmt::Display for BondXmitHashPolicy {
375    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
376        let kernel_name = match self {
377            BondXmitHashPolicy::Layer2 => "layer2",
378            BondXmitHashPolicy::Layer34 => "layer34",
379            BondXmitHashPolicy::Layer23 => "layer23",
380            BondXmitHashPolicy::Encap23 => "encap23",
381            BondXmitHashPolicy::Encap34 => "encap34",
382            BondXmitHashPolicy::VlanSrcMac => "vlan-src-mac",
383            BondXmitHashPolicy::Other(d) => {
384                return write!(f, "unknown-variant ({d})")
385            }
386        };
387        f.write_str(kernel_name)
388    }
389}
390
391#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
392#[non_exhaustive]
393pub enum BondArpAllTargets {
394    #[default]
395    Any,
396    All,
397    Other(u32),
398}
399
400impl From<BondArpAllTargets> for u32 {
401    fn from(value: BondArpAllTargets) -> Self {
402        match value {
403            BondArpAllTargets::All => BOND_OPT_ARP_ALL_TARGETS_ALL,
404            BondArpAllTargets::Any => BOND_OPT_ARP_ALL_TARGETS_ANY,
405            BondArpAllTargets::Other(d) => d,
406        }
407    }
408}
409
410impl From<u32> for BondArpAllTargets {
411    fn from(value: u32) -> Self {
412        match value {
413            BOND_OPT_ARP_ALL_TARGETS_ANY => BondArpAllTargets::Any,
414            BOND_OPT_ARP_ALL_TARGETS_ALL => BondArpAllTargets::All,
415            d => BondArpAllTargets::Other(d),
416        }
417    }
418}
419
420impl std::fmt::Display for BondArpAllTargets {
421    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
422        let kernel_name = match self {
423            BondArpAllTargets::Any => "any",
424            BondArpAllTargets::All => "all",
425            BondArpAllTargets::Other(d) => {
426                return write!(f, "unknown-variant ({d})")
427            }
428        };
429        f.write_str(kernel_name)
430    }
431}
432
433#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
434#[non_exhaustive]
435pub enum BondFailOverMac {
436    #[default]
437    None,
438    Active,
439    Follow,
440    Other(u8),
441}
442
443impl From<BondFailOverMac> for u8 {
444    fn from(value: BondFailOverMac) -> Self {
445        match value {
446            BondFailOverMac::None => BOND_FOM_NONE,
447            BondFailOverMac::Active => BOND_FOM_ACTIVE,
448            BondFailOverMac::Follow => BOND_FOM_FOLLOW,
449            BondFailOverMac::Other(d) => d,
450        }
451    }
452}
453
454impl From<u8> for BondFailOverMac {
455    fn from(value: u8) -> Self {
456        match value {
457            BOND_FOM_NONE => BondFailOverMac::None,
458            BOND_FOM_ACTIVE => BondFailOverMac::Active,
459            BOND_FOM_FOLLOW => BondFailOverMac::Follow,
460            d => BondFailOverMac::Other(d),
461        }
462    }
463}
464
465impl std::fmt::Display for BondFailOverMac {
466    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
467        let kernel_name = match self {
468            BondFailOverMac::None => "none",
469            BondFailOverMac::Active => "active",
470            BondFailOverMac::Follow => "follow",
471            BondFailOverMac::Other(d) => {
472                return write!(f, "unknown-variant ({d})")
473            }
474        };
475        f.write_str(kernel_name)
476    }
477}
478
479// Some attributes (ARP_IP_TARGET, NS_IP6_TARGET) contain a nested
480// list of IP addresses, where each element uses the index as NLA kind
481// and the address as value. InfoBond exposes vectors of IP addresses,
482// and we use this struct for serialization.
483struct BondIpAddrNla {
484    index: u16,
485    addr: IpAddr,
486}
487
488struct BondIpAddrNlaList(Vec<BondIpAddrNla>);
489
490impl Deref for BondIpAddrNlaList {
491    type Target = Vec<BondIpAddrNla>;
492
493    fn deref(&self) -> &Self::Target {
494        &self.0
495    }
496}
497
498impl From<&Vec<Ipv4Addr>> for BondIpAddrNlaList {
499    fn from(addrs: &Vec<Ipv4Addr>) -> Self {
500        let mut nlas = Vec::new();
501        for (i, addr) in addrs.iter().enumerate() {
502            let nla = BondIpAddrNla {
503                index: i as u16,
504                addr: IpAddr::V4(*addr),
505            };
506            nlas.push(nla);
507        }
508        BondIpAddrNlaList(nlas)
509    }
510}
511
512impl From<&Vec<Ipv6Addr>> for BondIpAddrNlaList {
513    fn from(addrs: &Vec<Ipv6Addr>) -> Self {
514        let mut nlas = Vec::new();
515        for (i, addr) in addrs.iter().enumerate() {
516            let nla = BondIpAddrNla {
517                index: i as u16,
518                addr: IpAddr::V6(*addr),
519            };
520            nlas.push(nla);
521        }
522        BondIpAddrNlaList(nlas)
523    }
524}
525
526impl Nla for BondIpAddrNla {
527    fn value_len(&self) -> usize {
528        if self.addr.is_ipv4() {
529            4
530        } else {
531            16
532        }
533    }
534    fn emit_value(&self, buffer: &mut [u8]) {
535        match self.addr {
536            IpAddr::V4(addr) => buffer.copy_from_slice(&addr.octets()),
537            IpAddr::V6(addr) => buffer.copy_from_slice(&addr.octets()),
538        }
539    }
540    fn kind(&self) -> u16 {
541        self.index
542    }
543}
544
545const BOND_ALL_PORT_ACTIVE_DROPPED: u8 = 0;
546const BOND_ALL_PORT_ACTIVE_DELIEVERD: u8 = 1;
547
548/// Specifies that duplicate frames (received on inactive ports) should be
549/// dropped (0) or delivered (1).
550#[derive(Debug, PartialEq, Eq, Clone, Copy)]
551pub enum BondAllPortActive {
552    Dropped,
553    Delivered,
554    Other(u8),
555}
556
557impl From<u8> for BondAllPortActive {
558    fn from(d: u8) -> Self {
559        match d {
560            BOND_ALL_PORT_ACTIVE_DROPPED => Self::Dropped,
561            BOND_ALL_PORT_ACTIVE_DELIEVERD => Self::Delivered,
562            _ => Self::Other(d),
563        }
564    }
565}
566
567impl From<BondAllPortActive> for u8 {
568    fn from(v: BondAllPortActive) -> u8 {
569        match v {
570            BondAllPortActive::Dropped => BOND_ALL_PORT_ACTIVE_DROPPED,
571            BondAllPortActive::Delivered => BOND_ALL_PORT_ACTIVE_DELIEVERD,
572            BondAllPortActive::Other(d) => d,
573        }
574    }
575}
576
577const AD_LACP_SLOW: u8 = 0;
578const AD_LACP_FAST: u8 = 1;
579
580/// Specifies that duplicate frames (received on inactive ports) should be
581/// dropped (0) or delivered (1).
582#[derive(Debug, PartialEq, Eq, Clone, Copy)]
583pub enum BondLacpRate {
584    Slow,
585    Fast,
586    Other(u8),
587}
588
589impl From<u8> for BondLacpRate {
590    fn from(d: u8) -> Self {
591        match d {
592            AD_LACP_SLOW => Self::Slow,
593            AD_LACP_FAST => Self::Fast,
594            _ => Self::Other(d),
595        }
596    }
597}
598
599impl From<BondLacpRate> for u8 {
600    fn from(v: BondLacpRate) -> u8 {
601        match v {
602            BondLacpRate::Slow => AD_LACP_SLOW,
603            BondLacpRate::Fast => AD_LACP_FAST,
604            BondLacpRate::Other(d) => d,
605        }
606    }
607}
608
609#[derive(Debug, PartialEq, Eq, Clone)]
610#[non_exhaustive]
611pub enum InfoBond {
612    Mode(BondMode),
613    ActivePort(u32),
614    MiiMon(u32),
615    UpDelay(u32),
616    DownDelay(u32),
617    /// Specifies whether or not miimon should use MII or ETHTOOL ioctls vs.
618    /// `netif_carrier_ok()` to determine the link status.
619    UseCarrier(bool),
620    ArpInterval(u32),
621    ArpIpTarget(Vec<Ipv4Addr>),
622    ArpValidate(BondArpValidate),
623    ArpAllTargets(BondArpAllTargets),
624    Primary(u32),
625    PrimaryReselect(BondPrimaryReselect),
626    FailOverMac(BondFailOverMac),
627    XmitHashPolicy(BondXmitHashPolicy),
628    ResendIgmp(u32),
629    NumPeerNotif(u8),
630    /// Specifies that duplicate frames (received on inactive ports) should be
631    /// dropped or delivered.
632    AllPortsActive(BondAllPortActive),
633    MinLinks(u32),
634    LpInterval(u32),
635    PacketsPerPort(u32),
636    /// The rate in which we'll ask our link partner to transmit LACPDU packets
637    /// in 802.3ad mode.
638    AdLacpRate(BondLacpRate),
639    AdSelect(u8),
640    AdInfo(Vec<BondAdInfo>),
641    AdActorSysPrio(u16),
642    AdUserPortKey(u16),
643    AdActorSystem([u8; 6]),
644    /// Specifies if dynamic shuffling of flows is enabled in
645    /// [BondMode::BalanceTlb] mode.
646    TlbDynamicLb(bool),
647    PeerNotifDelay(u32),
648    /// Whether to send LACPDU frames periodically
649    AdLacpActive(bool),
650    MissedMax(u8),
651    NsIp6Target(Vec<Ipv6Addr>),
652    Other(DefaultNla),
653}
654
655impl Nla for InfoBond {
656    fn value_len(&self) -> usize {
657        match self {
658            Self::Mode(_)
659            | Self::UseCarrier(_)
660            | Self::PrimaryReselect(_)
661            | Self::FailOverMac(_)
662            | Self::XmitHashPolicy(_)
663            | Self::NumPeerNotif(_)
664            | Self::AllPortsActive(_)
665            | Self::AdLacpActive(_)
666            | Self::AdLacpRate(_)
667            | Self::AdSelect(_)
668            | Self::TlbDynamicLb(_)
669            | Self::MissedMax(_) => 1,
670            Self::AdActorSysPrio(_) | Self::AdUserPortKey(_) => 2,
671            Self::ActivePort(_)
672            | Self::MiiMon(_)
673            | Self::UpDelay(_)
674            | Self::DownDelay(_)
675            | Self::ArpInterval(_)
676            | Self::ArpValidate(_)
677            | Self::ArpAllTargets(_)
678            | Self::Primary(_)
679            | Self::ResendIgmp(_)
680            | Self::MinLinks(_)
681            | Self::LpInterval(_)
682            | Self::PacketsPerPort(_)
683            | Self::PeerNotifDelay(_) => 4,
684            Self::ArpIpTarget(ref addrs) => {
685                BondIpAddrNlaList::from(addrs).as_slice().buffer_len()
686            }
687            Self::NsIp6Target(ref addrs) => {
688                BondIpAddrNlaList::from(addrs).as_slice().buffer_len()
689            }
690            Self::AdActorSystem(_) => 6,
691            Self::AdInfo(infos) => infos.as_slice().buffer_len(),
692            Self::Other(v) => v.value_len(),
693        }
694    }
695
696    fn emit_value(&self, buffer: &mut [u8]) {
697        match self {
698            Self::Mode(value) => buffer[0] = (*value).into(),
699            Self::XmitHashPolicy(value) => buffer[0] = (*value).into(),
700            Self::PrimaryReselect(value) => buffer[0] = (*value).into(),
701            Self::NumPeerNotif(value)
702            | Self::AdSelect(value)
703            | Self::MissedMax(value) => buffer[0] = *value,
704            Self::UseCarrier(value)
705            | Self::AdLacpActive(value)
706            | Self::TlbDynamicLb(value) => buffer[0] = (*value).into(),
707            Self::AdLacpRate(value) => buffer[0] = (*value).into(),
708            Self::AllPortsActive(value) => buffer[0] = (*value).into(),
709            Self::FailOverMac(value) => buffer[0] = (*value).into(),
710            Self::AdActorSysPrio(value) | Self::AdUserPortKey(value) => {
711                emit_u16(buffer, *value).unwrap()
712            }
713            Self::ArpValidate(value) => {
714                emit_u32(buffer, (*value).into()).unwrap()
715            }
716            Self::ArpAllTargets(value) => {
717                emit_u32(buffer, (*value).into()).unwrap()
718            }
719            Self::ActivePort(value)
720            | Self::MiiMon(value)
721            | Self::UpDelay(value)
722            | Self::DownDelay(value)
723            | Self::ArpInterval(value)
724            | Self::Primary(value)
725            | Self::ResendIgmp(value)
726            | Self::MinLinks(value)
727            | Self::LpInterval(value)
728            | Self::PacketsPerPort(value)
729            | Self::PeerNotifDelay(value) => emit_u32(buffer, *value).unwrap(),
730            Self::AdActorSystem(bytes) => buffer.copy_from_slice(bytes),
731            Self::ArpIpTarget(addrs) => {
732                BondIpAddrNlaList::from(addrs).as_slice().emit(buffer)
733            }
734            Self::NsIp6Target(addrs) => {
735                BondIpAddrNlaList::from(addrs).as_slice().emit(buffer)
736            }
737            Self::AdInfo(infos) => infos.as_slice().emit(buffer),
738            Self::Other(v) => v.emit_value(buffer),
739        }
740    }
741
742    fn kind(&self) -> u16 {
743        match self {
744            Self::Mode(_) => IFLA_BOND_MODE,
745            Self::ActivePort(_) => IFLA_BOND_ACTIVE_PORT,
746            Self::MiiMon(_) => IFLA_BOND_MIIMON,
747            Self::UpDelay(_) => IFLA_BOND_UPDELAY,
748            Self::DownDelay(_) => IFLA_BOND_DOWNDELAY,
749            Self::UseCarrier(_) => IFLA_BOND_USE_CARRIER,
750            Self::ArpInterval(_) => IFLA_BOND_ARP_INTERVAL,
751            Self::ArpIpTarget(_) => IFLA_BOND_ARP_IP_TARGET,
752            Self::ArpValidate(_) => IFLA_BOND_ARP_VALIDATE,
753            Self::ArpAllTargets(_) => IFLA_BOND_ARP_ALL_TARGETS,
754            Self::Primary(_) => IFLA_BOND_PRIMARY,
755            Self::PrimaryReselect(_) => IFLA_BOND_PRIMARY_RESELECT,
756            Self::FailOverMac(_) => IFLA_BOND_FAIL_OVER_MAC,
757            Self::XmitHashPolicy(_) => IFLA_BOND_XMIT_HASH_POLICY,
758            Self::ResendIgmp(_) => IFLA_BOND_RESEND_IGMP,
759            Self::NumPeerNotif(_) => IFLA_BOND_NUM_PEER_NOTIF,
760            Self::AllPortsActive(_) => IFLA_BOND_ALL_PORTS_ACTIVE,
761            Self::MinLinks(_) => IFLA_BOND_MIN_LINKS,
762            Self::LpInterval(_) => IFLA_BOND_LP_INTERVAL,
763            Self::PacketsPerPort(_) => IFLA_BOND_PACKETS_PER_PORT,
764            Self::AdLacpRate(_) => IFLA_BOND_AD_LACP_RATE,
765            Self::AdSelect(_) => IFLA_BOND_AD_SELECT,
766            Self::AdInfo(_) => IFLA_BOND_AD_INFO,
767            Self::AdActorSysPrio(_) => IFLA_BOND_AD_ACTOR_SYS_PRIO,
768            Self::AdUserPortKey(_) => IFLA_BOND_AD_USER_PORT_KEY,
769            Self::AdActorSystem(_) => IFLA_BOND_AD_ACTOR_SYSTEM,
770            Self::TlbDynamicLb(_) => IFLA_BOND_TLB_DYNAMIC_LB,
771            Self::PeerNotifDelay(_) => IFLA_BOND_PEER_NOTIF_DELAY,
772            Self::AdLacpActive(_) => IFLA_BOND_AD_LACP_ACTIVE,
773            Self::MissedMax(_) => IFLA_BOND_MISSED_MAX,
774            Self::NsIp6Target(_) => IFLA_BOND_NS_IP6_TARGET,
775            Self::Other(v) => v.kind(),
776        }
777    }
778}
779
780impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for InfoBond {
781    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
782        let payload = buf.value();
783        Ok(match buf.kind() {
784            IFLA_BOND_MODE => Self::Mode(
785                parse_u8(payload)
786                    .context("invalid IFLA_BOND_MODE value")?
787                    .into(),
788            ),
789            IFLA_BOND_ACTIVE_PORT => Self::ActivePort(
790                parse_u32(payload)
791                    .context("invalid IFLA_BOND_ACTIVE_PORT value")?,
792            ),
793            IFLA_BOND_MIIMON => Self::MiiMon(
794                parse_u32(payload).context("invalid IFLA_BOND_MIIMON value")?,
795            ),
796            IFLA_BOND_UPDELAY => Self::UpDelay(
797                parse_u32(payload)
798                    .context("invalid IFLA_BOND_UPDELAY value")?,
799            ),
800            IFLA_BOND_DOWNDELAY => Self::DownDelay(
801                parse_u32(payload)
802                    .context("invalid IFLA_BOND_DOWNDELAY value")?,
803            ),
804            IFLA_BOND_USE_CARRIER => Self::UseCarrier(
805                parse_u8(payload)
806                    .context("invalid IFLA_BOND_USE_CARRIER value")?
807                    > 0,
808            ),
809            IFLA_BOND_ARP_INTERVAL => Self::ArpInterval(
810                parse_u32(payload)
811                    .context("invalid IFLA_BOND_ARP_INTERVAL value")?,
812            ),
813            IFLA_BOND_ARP_IP_TARGET => {
814                let mut addrs = Vec::<Ipv4Addr>::new();
815                for nla in NlasIterator::new(payload) {
816                    let nla =
817                        &nla.context("invalid IFLA_BOND_ARP_IP_TARGET value")?;
818                    if let Ok(IpAddr::V4(addr)) = parse_ip(nla.value()) {
819                        addrs.push(addr);
820                    }
821                }
822                Self::ArpIpTarget(addrs)
823            }
824            IFLA_BOND_ARP_VALIDATE => Self::ArpValidate(
825                parse_u32(payload)
826                    .context("invalid IFLA_BOND_ARP_VALIDATE value")?
827                    .into(),
828            ),
829            IFLA_BOND_ARP_ALL_TARGETS => Self::ArpAllTargets(
830                parse_u32(payload)
831                    .context("invalid IFLA_BOND_ARP_ALL_TARGETS value")?
832                    .into(),
833            ),
834            IFLA_BOND_PRIMARY => Self::Primary(
835                parse_u32(payload)
836                    .context("invalid IFLA_BOND_PRIMARY value")?,
837            ),
838            IFLA_BOND_PRIMARY_RESELECT => Self::PrimaryReselect(
839                parse_u8(payload)
840                    .context("invalid IFLA_BOND_PRIMARY_RESELECT value")?
841                    .into(),
842            ),
843            IFLA_BOND_FAIL_OVER_MAC => Self::FailOverMac(
844                parse_u8(payload)
845                    .context("invalid IFLA_BOND_FAIL_OVER_MAC value")?
846                    .into(),
847            ),
848            IFLA_BOND_XMIT_HASH_POLICY => Self::XmitHashPolicy(
849                parse_u8(payload)
850                    .context("invalid IFLA_BOND_XMIT_HASH_POLICY value")?
851                    .into(),
852            ),
853            IFLA_BOND_RESEND_IGMP => Self::ResendIgmp(
854                parse_u32(payload)
855                    .context("invalid IFLA_BOND_RESEND_IGMP value")?,
856            ),
857            IFLA_BOND_NUM_PEER_NOTIF => Self::NumPeerNotif(
858                parse_u8(payload)
859                    .context("invalid IFLA_BOND_NUM_PEER_NOTIF value")?,
860            ),
861            IFLA_BOND_ALL_PORTS_ACTIVE => Self::AllPortsActive(
862                parse_u8(payload)
863                    .context("invalid IFLA_BOND_ALL_PORTS_ACTIVE value")?
864                    .into(),
865            ),
866            IFLA_BOND_MIN_LINKS => Self::MinLinks(
867                parse_u32(payload)
868                    .context("invalid IFLA_BOND_MIN_LINKS value")?,
869            ),
870            IFLA_BOND_LP_INTERVAL => Self::LpInterval(
871                parse_u32(payload)
872                    .context("invalid IFLA_BOND_LP_INTERVAL value")?,
873            ),
874            IFLA_BOND_PACKETS_PER_PORT => Self::PacketsPerPort(
875                parse_u32(payload)
876                    .context("invalid IFLA_BOND_PACKETS_PER_PORT value")?,
877            ),
878            IFLA_BOND_AD_LACP_RATE => Self::AdLacpRate(
879                parse_u8(payload)
880                    .context("invalid IFLA_BOND_AD_LACP_RATE value")?
881                    .into(),
882            ),
883            IFLA_BOND_AD_SELECT => Self::AdSelect(
884                parse_u8(payload)
885                    .context("invalid IFLA_BOND_AD_SELECT value")?,
886            ),
887            IFLA_BOND_AD_INFO => {
888                let mut infos = Vec::new();
889                let err = "failed to parse IFLA_BOND_AD_INFO";
890                for nla in NlasIterator::new(payload) {
891                    let nla = &nla.context(err)?;
892                    let info = BondAdInfo::parse(nla).context(err)?;
893                    infos.push(info);
894                }
895                Self::AdInfo(infos)
896            }
897            IFLA_BOND_AD_ACTOR_SYS_PRIO => Self::AdActorSysPrio(
898                parse_u16(payload)
899                    .context("invalid IFLA_BOND_AD_ACTOR_SYS_PRIO value")?,
900            ),
901            IFLA_BOND_AD_USER_PORT_KEY => Self::AdUserPortKey(
902                parse_u16(payload)
903                    .context("invalid IFLA_BOND_AD_USER_PORT_KEY value")?,
904            ),
905            IFLA_BOND_AD_ACTOR_SYSTEM => Self::AdActorSystem(
906                parse_mac(payload)
907                    .context("invalid IFLA_BOND_AD_ACTOR_SYSTEM value")?,
908            ),
909            IFLA_BOND_TLB_DYNAMIC_LB => Self::TlbDynamicLb(
910                parse_u8(payload)
911                    .context("invalid IFLA_BOND_TLB_DYNAMIC_LB value")?
912                    > 0,
913            ),
914            IFLA_BOND_PEER_NOTIF_DELAY => Self::PeerNotifDelay(
915                parse_u32(payload)
916                    .context("invalid IFLA_BOND_PEER_NOTIF_DELAY value")?,
917            ),
918            IFLA_BOND_AD_LACP_ACTIVE => Self::AdLacpActive(
919                parse_u8(payload)
920                    .context("invalid IFLA_BOND_AD_LACP_ACTIVE value")?
921                    > 0,
922            ),
923            IFLA_BOND_MISSED_MAX => Self::MissedMax(
924                parse_u8(payload)
925                    .context("invalid IFLA_BOND_MISSED_MAX value")?,
926            ),
927            IFLA_BOND_NS_IP6_TARGET => {
928                let mut addrs = Vec::<Ipv6Addr>::new();
929                for nla in NlasIterator::new(payload) {
930                    let nla =
931                        &nla.context("invalid IFLA_BOND_NS_IP6_TARGET value")?;
932                    if let Ok(IpAddr::V6(addr)) = parse_ip(nla.value()) {
933                        addrs.push(addr);
934                    }
935                }
936                Self::NsIp6Target(addrs)
937            }
938            _ => Self::Other(DefaultNla::parse(buf).context(format!(
939                "invalid NLA for {}: {payload:?}",
940                buf.kind()
941            ))?),
942        })
943    }
944}