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
545#[derive(Debug, PartialEq, Eq, Clone)]
546#[non_exhaustive]
547pub enum InfoBond {
548    Mode(BondMode),
549    ActivePort(u32),
550    MiiMon(u32),
551    UpDelay(u32),
552    DownDelay(u32),
553    UseCarrier(u8),
554    ArpInterval(u32),
555    ArpIpTarget(Vec<Ipv4Addr>),
556    ArpValidate(BondArpValidate),
557    ArpAllTargets(BondArpAllTargets),
558    Primary(u32),
559    PrimaryReselect(BondPrimaryReselect),
560    FailOverMac(BondFailOverMac),
561    XmitHashPolicy(BondXmitHashPolicy),
562    ResendIgmp(u32),
563    NumPeerNotif(u8),
564    AllPortsActive(u8),
565    MinLinks(u32),
566    LpInterval(u32),
567    PacketsPerPort(u32),
568    AdLacpRate(u8),
569    AdSelect(u8),
570    AdInfo(Vec<BondAdInfo>),
571    AdActorSysPrio(u16),
572    AdUserPortKey(u16),
573    AdActorSystem([u8; 6]),
574    TlbDynamicLb(u8),
575    PeerNotifDelay(u32),
576    AdLacpActive(u8),
577    MissedMax(u8),
578    NsIp6Target(Vec<Ipv6Addr>),
579    Other(DefaultNla),
580}
581
582impl Nla for InfoBond {
583    fn value_len(&self) -> usize {
584        match self {
585            Self::Mode(_)
586            | Self::UseCarrier(_)
587            | Self::PrimaryReselect(_)
588            | Self::FailOverMac(_)
589            | Self::XmitHashPolicy(_)
590            | Self::NumPeerNotif(_)
591            | Self::AllPortsActive(_)
592            | Self::AdLacpActive(_)
593            | Self::AdLacpRate(_)
594            | Self::AdSelect(_)
595            | Self::TlbDynamicLb(_)
596            | Self::MissedMax(_) => 1,
597            Self::AdActorSysPrio(_) | Self::AdUserPortKey(_) => 2,
598            Self::ActivePort(_)
599            | Self::MiiMon(_)
600            | Self::UpDelay(_)
601            | Self::DownDelay(_)
602            | Self::ArpInterval(_)
603            | Self::ArpValidate(_)
604            | Self::ArpAllTargets(_)
605            | Self::Primary(_)
606            | Self::ResendIgmp(_)
607            | Self::MinLinks(_)
608            | Self::LpInterval(_)
609            | Self::PacketsPerPort(_)
610            | Self::PeerNotifDelay(_) => 4,
611            Self::ArpIpTarget(ref addrs) => {
612                BondIpAddrNlaList::from(addrs).as_slice().buffer_len()
613            }
614            Self::NsIp6Target(ref addrs) => {
615                BondIpAddrNlaList::from(addrs).as_slice().buffer_len()
616            }
617            Self::AdActorSystem(_) => 6,
618            Self::AdInfo(infos) => infos.as_slice().buffer_len(),
619            Self::Other(v) => v.value_len(),
620        }
621    }
622
623    fn emit_value(&self, buffer: &mut [u8]) {
624        match self {
625            Self::Mode(value) => buffer[0] = (*value).into(),
626            Self::XmitHashPolicy(value) => buffer[0] = (*value).into(),
627            Self::PrimaryReselect(value) => buffer[0] = (*value).into(),
628            Self::UseCarrier(value)
629            | Self::NumPeerNotif(value)
630            | Self::AllPortsActive(value)
631            | Self::AdLacpActive(value)
632            | Self::AdLacpRate(value)
633            | Self::AdSelect(value)
634            | Self::TlbDynamicLb(value)
635            | Self::MissedMax(value) => buffer[0] = *value,
636            Self::FailOverMac(value) => buffer[0] = (*value).into(),
637            Self::AdActorSysPrio(value) | Self::AdUserPortKey(value) => {
638                emit_u16(buffer, *value).unwrap()
639            }
640            Self::ArpValidate(value) => {
641                emit_u32(buffer, (*value).into()).unwrap()
642            }
643            Self::ArpAllTargets(value) => {
644                emit_u32(buffer, (*value).into()).unwrap()
645            }
646            Self::ActivePort(value)
647            | Self::MiiMon(value)
648            | Self::UpDelay(value)
649            | Self::DownDelay(value)
650            | Self::ArpInterval(value)
651            | Self::Primary(value)
652            | Self::ResendIgmp(value)
653            | Self::MinLinks(value)
654            | Self::LpInterval(value)
655            | Self::PacketsPerPort(value)
656            | Self::PeerNotifDelay(value) => emit_u32(buffer, *value).unwrap(),
657            Self::AdActorSystem(bytes) => buffer.copy_from_slice(bytes),
658            Self::ArpIpTarget(addrs) => {
659                BondIpAddrNlaList::from(addrs).as_slice().emit(buffer)
660            }
661            Self::NsIp6Target(addrs) => {
662                BondIpAddrNlaList::from(addrs).as_slice().emit(buffer)
663            }
664            Self::AdInfo(infos) => infos.as_slice().emit(buffer),
665            Self::Other(v) => v.emit_value(buffer),
666        }
667    }
668
669    fn kind(&self) -> u16 {
670        match self {
671            Self::Mode(_) => IFLA_BOND_MODE,
672            Self::ActivePort(_) => IFLA_BOND_ACTIVE_PORT,
673            Self::MiiMon(_) => IFLA_BOND_MIIMON,
674            Self::UpDelay(_) => IFLA_BOND_UPDELAY,
675            Self::DownDelay(_) => IFLA_BOND_DOWNDELAY,
676            Self::UseCarrier(_) => IFLA_BOND_USE_CARRIER,
677            Self::ArpInterval(_) => IFLA_BOND_ARP_INTERVAL,
678            Self::ArpIpTarget(_) => IFLA_BOND_ARP_IP_TARGET,
679            Self::ArpValidate(_) => IFLA_BOND_ARP_VALIDATE,
680            Self::ArpAllTargets(_) => IFLA_BOND_ARP_ALL_TARGETS,
681            Self::Primary(_) => IFLA_BOND_PRIMARY,
682            Self::PrimaryReselect(_) => IFLA_BOND_PRIMARY_RESELECT,
683            Self::FailOverMac(_) => IFLA_BOND_FAIL_OVER_MAC,
684            Self::XmitHashPolicy(_) => IFLA_BOND_XMIT_HASH_POLICY,
685            Self::ResendIgmp(_) => IFLA_BOND_RESEND_IGMP,
686            Self::NumPeerNotif(_) => IFLA_BOND_NUM_PEER_NOTIF,
687            Self::AllPortsActive(_) => IFLA_BOND_ALL_PORTS_ACTIVE,
688            Self::MinLinks(_) => IFLA_BOND_MIN_LINKS,
689            Self::LpInterval(_) => IFLA_BOND_LP_INTERVAL,
690            Self::PacketsPerPort(_) => IFLA_BOND_PACKETS_PER_PORT,
691            Self::AdLacpRate(_) => IFLA_BOND_AD_LACP_RATE,
692            Self::AdSelect(_) => IFLA_BOND_AD_SELECT,
693            Self::AdInfo(_) => IFLA_BOND_AD_INFO,
694            Self::AdActorSysPrio(_) => IFLA_BOND_AD_ACTOR_SYS_PRIO,
695            Self::AdUserPortKey(_) => IFLA_BOND_AD_USER_PORT_KEY,
696            Self::AdActorSystem(_) => IFLA_BOND_AD_ACTOR_SYSTEM,
697            Self::TlbDynamicLb(_) => IFLA_BOND_TLB_DYNAMIC_LB,
698            Self::PeerNotifDelay(_) => IFLA_BOND_PEER_NOTIF_DELAY,
699            Self::AdLacpActive(_) => IFLA_BOND_AD_LACP_ACTIVE,
700            Self::MissedMax(_) => IFLA_BOND_MISSED_MAX,
701            Self::NsIp6Target(_) => IFLA_BOND_NS_IP6_TARGET,
702            Self::Other(v) => v.kind(),
703        }
704    }
705}
706
707impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for InfoBond {
708    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
709        let payload = buf.value();
710        Ok(match buf.kind() {
711            IFLA_BOND_MODE => Self::Mode(
712                parse_u8(payload)
713                    .context("invalid IFLA_BOND_MODE value")?
714                    .into(),
715            ),
716            IFLA_BOND_ACTIVE_PORT => Self::ActivePort(
717                parse_u32(payload)
718                    .context("invalid IFLA_BOND_ACTIVE_PORT value")?,
719            ),
720            IFLA_BOND_MIIMON => Self::MiiMon(
721                parse_u32(payload).context("invalid IFLA_BOND_MIIMON value")?,
722            ),
723            IFLA_BOND_UPDELAY => Self::UpDelay(
724                parse_u32(payload)
725                    .context("invalid IFLA_BOND_UPDELAY value")?,
726            ),
727            IFLA_BOND_DOWNDELAY => Self::DownDelay(
728                parse_u32(payload)
729                    .context("invalid IFLA_BOND_DOWNDELAY value")?,
730            ),
731            IFLA_BOND_USE_CARRIER => Self::UseCarrier(
732                parse_u8(payload)
733                    .context("invalid IFLA_BOND_USE_CARRIER value")?,
734            ),
735            IFLA_BOND_ARP_INTERVAL => Self::ArpInterval(
736                parse_u32(payload)
737                    .context("invalid IFLA_BOND_ARP_INTERVAL value")?,
738            ),
739            IFLA_BOND_ARP_IP_TARGET => {
740                let mut addrs = Vec::<Ipv4Addr>::new();
741                for nla in NlasIterator::new(payload) {
742                    let nla =
743                        &nla.context("invalid IFLA_BOND_ARP_IP_TARGET value")?;
744                    if let Ok(IpAddr::V4(addr)) = parse_ip(nla.value()) {
745                        addrs.push(addr);
746                    }
747                }
748                Self::ArpIpTarget(addrs)
749            }
750            IFLA_BOND_ARP_VALIDATE => Self::ArpValidate(
751                parse_u32(payload)
752                    .context("invalid IFLA_BOND_ARP_VALIDATE value")?
753                    .into(),
754            ),
755            IFLA_BOND_ARP_ALL_TARGETS => Self::ArpAllTargets(
756                parse_u32(payload)
757                    .context("invalid IFLA_BOND_ARP_ALL_TARGETS value")?
758                    .into(),
759            ),
760            IFLA_BOND_PRIMARY => Self::Primary(
761                parse_u32(payload)
762                    .context("invalid IFLA_BOND_PRIMARY value")?,
763            ),
764            IFLA_BOND_PRIMARY_RESELECT => Self::PrimaryReselect(
765                parse_u8(payload)
766                    .context("invalid IFLA_BOND_PRIMARY_RESELECT value")?
767                    .into(),
768            ),
769            IFLA_BOND_FAIL_OVER_MAC => Self::FailOverMac(
770                parse_u8(payload)
771                    .context("invalid IFLA_BOND_FAIL_OVER_MAC value")?
772                    .into(),
773            ),
774            IFLA_BOND_XMIT_HASH_POLICY => Self::XmitHashPolicy(
775                parse_u8(payload)
776                    .context("invalid IFLA_BOND_XMIT_HASH_POLICY value")?
777                    .into(),
778            ),
779            IFLA_BOND_RESEND_IGMP => Self::ResendIgmp(
780                parse_u32(payload)
781                    .context("invalid IFLA_BOND_RESEND_IGMP value")?,
782            ),
783            IFLA_BOND_NUM_PEER_NOTIF => Self::NumPeerNotif(
784                parse_u8(payload)
785                    .context("invalid IFLA_BOND_NUM_PEER_NOTIF value")?,
786            ),
787            IFLA_BOND_ALL_PORTS_ACTIVE => Self::AllPortsActive(
788                parse_u8(payload)
789                    .context("invalid IFLA_BOND_ALL_PORTS_ACTIVE value")?,
790            ),
791            IFLA_BOND_MIN_LINKS => Self::MinLinks(
792                parse_u32(payload)
793                    .context("invalid IFLA_BOND_MIN_LINKS value")?,
794            ),
795            IFLA_BOND_LP_INTERVAL => Self::LpInterval(
796                parse_u32(payload)
797                    .context("invalid IFLA_BOND_LP_INTERVAL value")?,
798            ),
799            IFLA_BOND_PACKETS_PER_PORT => Self::PacketsPerPort(
800                parse_u32(payload)
801                    .context("invalid IFLA_BOND_PACKETS_PER_PORT value")?,
802            ),
803            IFLA_BOND_AD_LACP_RATE => Self::AdLacpRate(
804                parse_u8(payload)
805                    .context("invalid IFLA_BOND_AD_LACP_RATE value")?,
806            ),
807            IFLA_BOND_AD_SELECT => Self::AdSelect(
808                parse_u8(payload)
809                    .context("invalid IFLA_BOND_AD_SELECT value")?,
810            ),
811            IFLA_BOND_AD_INFO => {
812                let mut infos = Vec::new();
813                let err = "failed to parse IFLA_BOND_AD_INFO";
814                for nla in NlasIterator::new(payload) {
815                    let nla = &nla.context(err)?;
816                    let info = BondAdInfo::parse(nla).context(err)?;
817                    infos.push(info);
818                }
819                Self::AdInfo(infos)
820            }
821            IFLA_BOND_AD_ACTOR_SYS_PRIO => Self::AdActorSysPrio(
822                parse_u16(payload)
823                    .context("invalid IFLA_BOND_AD_ACTOR_SYS_PRIO value")?,
824            ),
825            IFLA_BOND_AD_USER_PORT_KEY => Self::AdUserPortKey(
826                parse_u16(payload)
827                    .context("invalid IFLA_BOND_AD_USER_PORT_KEY value")?,
828            ),
829            IFLA_BOND_AD_ACTOR_SYSTEM => Self::AdActorSystem(
830                parse_mac(payload)
831                    .context("invalid IFLA_BOND_AD_ACTOR_SYSTEM value")?,
832            ),
833            IFLA_BOND_TLB_DYNAMIC_LB => Self::TlbDynamicLb(
834                parse_u8(payload)
835                    .context("invalid IFLA_BOND_TLB_DYNAMIC_LB value")?,
836            ),
837            IFLA_BOND_PEER_NOTIF_DELAY => Self::PeerNotifDelay(
838                parse_u32(payload)
839                    .context("invalid IFLA_BOND_PEER_NOTIF_DELAY value")?,
840            ),
841            IFLA_BOND_AD_LACP_ACTIVE => Self::AdLacpActive(
842                parse_u8(payload)
843                    .context("invalid IFLA_BOND_AD_LACP_ACTIVE value")?,
844            ),
845            IFLA_BOND_MISSED_MAX => Self::MissedMax(
846                parse_u8(payload)
847                    .context("invalid IFLA_BOND_MISSED_MAX value")?,
848            ),
849            IFLA_BOND_NS_IP6_TARGET => {
850                let mut addrs = Vec::<Ipv6Addr>::new();
851                for nla in NlasIterator::new(payload) {
852                    let nla =
853                        &nla.context("invalid IFLA_BOND_NS_IP6_TARGET value")?;
854                    if let Ok(IpAddr::V6(addr)) = parse_ip(nla.value()) {
855                        addrs.push(addr);
856                    }
857                }
858                Self::NsIp6Target(addrs)
859            }
860            _ => Self::Other(DefaultNla::parse(buf).context(format!(
861                "invalid NLA for {}: {payload:?}",
862                buf.kind()
863            ))?),
864        })
865    }
866}