netlink_packet_route/link/link_info/
bond.rs

1// SPDX-License-Identifier: MIT
2
3use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
4use std::ops::Deref;
5
6use anyhow::Context;
7use byteorder::{ByteOrder, NativeEndian};
8use netlink_packet_utils::nla::{DefaultNla, Nla, NlaBuffer, NlasIterator};
9use netlink_packet_utils::parsers::{parse_ip, parse_mac, parse_u16, parse_u32, parse_u8};
10use netlink_packet_utils::traits::{Emitable, Parseable};
11use netlink_packet_utils::DecodeError;
12
13const IFLA_BOND_AD_INFO_AGGREGATOR: u16 = 1;
14const IFLA_BOND_AD_INFO_NUM_PORTS: u16 = 2;
15const IFLA_BOND_AD_INFO_ACTOR_KEY: u16 = 3;
16const IFLA_BOND_AD_INFO_PARTNER_KEY: u16 = 4;
17const IFLA_BOND_AD_INFO_PARTNER_MAC: u16 = 5;
18
19const IFLA_BOND_MODE: u16 = 1;
20const IFLA_BOND_ACTIVE_PORT: u16 = 2;
21const IFLA_BOND_MIIMON: u16 = 3;
22const IFLA_BOND_UPDELAY: u16 = 4;
23const IFLA_BOND_DOWNDELAY: u16 = 5;
24const IFLA_BOND_USE_CARRIER: u16 = 6;
25const IFLA_BOND_ARP_INTERVAL: u16 = 7;
26const IFLA_BOND_ARP_IP_TARGET: u16 = 8;
27const IFLA_BOND_ARP_VALIDATE: u16 = 9;
28const IFLA_BOND_ARP_ALL_TARGETS: u16 = 10;
29const IFLA_BOND_PRIMARY: u16 = 11;
30const IFLA_BOND_PRIMARY_RESELECT: u16 = 12;
31const IFLA_BOND_FAIL_OVER_MAC: u16 = 13;
32const IFLA_BOND_XMIT_HASH_POLICY: u16 = 14;
33const IFLA_BOND_RESEND_IGMP: u16 = 15;
34const IFLA_BOND_NUM_PEER_NOTIF: u16 = 16;
35const IFLA_BOND_ALL_PORTS_ACTIVE: u16 = 17;
36const IFLA_BOND_MIN_LINKS: u16 = 18;
37const IFLA_BOND_LP_INTERVAL: u16 = 19;
38const IFLA_BOND_PACKETS_PER_PORT: u16 = 20;
39const IFLA_BOND_AD_LACP_RATE: u16 = 21;
40const IFLA_BOND_AD_SELECT: u16 = 22;
41const IFLA_BOND_AD_INFO: u16 = 23;
42const IFLA_BOND_AD_ACTOR_SYS_PRIO: u16 = 24;
43const IFLA_BOND_AD_USER_PORT_KEY: u16 = 25;
44const IFLA_BOND_AD_ACTOR_SYSTEM: u16 = 26;
45const IFLA_BOND_TLB_DYNAMIC_LB: u16 = 27;
46const IFLA_BOND_PEER_NOTIF_DELAY: u16 = 28;
47const IFLA_BOND_AD_LACP_ACTIVE: u16 = 29;
48const IFLA_BOND_MISSED_MAX: u16 = 30;
49const IFLA_BOND_NS_IP6_TARGET: u16 = 31;
50
51const BOND_MODE_ROUNDROBIN: u8 = 0;
52const BOND_MODE_ACTIVEBACKUP: u8 = 1;
53const BOND_MODE_XOR: u8 = 2;
54const BOND_MODE_BROADCAST: u8 = 3;
55const BOND_MODE_8023AD: u8 = 4;
56const BOND_MODE_TLB: u8 = 5;
57const BOND_MODE_ALB: u8 = 6;
58
59#[derive(Debug, Clone, Eq, PartialEq)]
60#[non_exhaustive]
61pub enum BondAdInfo {
62    Aggregator(u16),
63    NumPorts(u16),
64    ActorKey(u16),
65    PartnerKey(u16),
66    PartnerMac([u8; 6]),
67    Other(DefaultNla),
68}
69
70impl Nla for BondAdInfo {
71    fn value_len(&self) -> usize {
72        match self {
73            Self::Aggregator(_) | Self::NumPorts(_) | Self::ActorKey(_) | Self::PartnerKey(_) => 2,
74            Self::PartnerMac(_) => 6,
75            Self::Other(v) => v.value_len(),
76        }
77    }
78
79    fn kind(&self) -> u16 {
80        match self {
81            Self::Aggregator(_) => IFLA_BOND_AD_INFO_AGGREGATOR,
82            Self::NumPorts(_) => IFLA_BOND_AD_INFO_NUM_PORTS,
83            Self::ActorKey(_) => IFLA_BOND_AD_INFO_ACTOR_KEY,
84            Self::PartnerKey(_) => IFLA_BOND_AD_INFO_PARTNER_KEY,
85            Self::PartnerMac(_) => IFLA_BOND_AD_INFO_PARTNER_MAC,
86            Self::Other(v) => v.kind(),
87        }
88    }
89
90    fn emit_value(&self, buffer: &mut [u8]) {
91        match self {
92            Self::Aggregator(d) | Self::NumPorts(d) | Self::ActorKey(d) | Self::PartnerKey(d) => {
93                NativeEndian::write_u16(buffer, *d)
94            }
95            Self::PartnerMac(mac) => buffer.copy_from_slice(mac),
96            Self::Other(v) => v.emit_value(buffer),
97        }
98    }
99}
100
101impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for BondAdInfo {
102    type Error = DecodeError;
103    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
104        let payload = buf.value();
105        Ok(match buf.kind() {
106            IFLA_BOND_AD_INFO_AGGREGATOR => Self::Aggregator(
107                parse_u16(payload).context("invalid IFLA_BOND_AD_INFO_AGGREGATOR value")?,
108            ),
109            IFLA_BOND_AD_INFO_NUM_PORTS => Self::NumPorts(
110                parse_u16(payload).context("invalid IFLA_BOND_AD_INFO_NUM_PORTS value")?,
111            ),
112            IFLA_BOND_AD_INFO_ACTOR_KEY => Self::ActorKey(
113                parse_u16(payload).context("invalid IFLA_BOND_AD_INFO_ACTOR_KEY value")?,
114            ),
115            IFLA_BOND_AD_INFO_PARTNER_KEY => Self::PartnerKey(
116                parse_u16(payload).context("invalid IFLA_BOND_AD_INFO_PARTNER_KEY value")?,
117            ),
118            IFLA_BOND_AD_INFO_PARTNER_MAC => Self::PartnerMac(
119                parse_mac(payload).context("invalid IFLA_BOND_AD_INFO_PARTNER_MAC value")?,
120            ),
121            _ => Self::Other(
122                DefaultNla::parse(buf)
123                    .context(format!("invalid NLA for {}: {payload:?}", buf.kind()))?,
124            ),
125        })
126    }
127}
128
129#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
130#[non_exhaustive]
131pub enum BondMode {
132    #[default]
133    BalanceRr,
134    ActiveBackup,
135    BalanceXor,
136    Broadcast,
137    Ieee8023Ad,
138    BalanceTlb,
139    BalanceAlb,
140    Other(u8),
141}
142
143impl From<u8> for BondMode {
144    fn from(d: u8) -> Self {
145        match d {
146            BOND_MODE_ROUNDROBIN => Self::BalanceRr,
147            BOND_MODE_ACTIVEBACKUP => Self::ActiveBackup,
148            BOND_MODE_XOR => Self::BalanceXor,
149            BOND_MODE_BROADCAST => Self::Broadcast,
150            BOND_MODE_8023AD => Self::Ieee8023Ad,
151            BOND_MODE_TLB => Self::BalanceTlb,
152            BOND_MODE_ALB => Self::BalanceAlb,
153            _ => Self::Other(d),
154        }
155    }
156}
157
158impl From<BondMode> for u8 {
159    fn from(d: BondMode) -> Self {
160        match d {
161            BondMode::BalanceRr => BOND_MODE_ROUNDROBIN,
162            BondMode::ActiveBackup => BOND_MODE_ACTIVEBACKUP,
163            BondMode::BalanceXor => BOND_MODE_XOR,
164            BondMode::Broadcast => BOND_MODE_BROADCAST,
165            BondMode::Ieee8023Ad => BOND_MODE_8023AD,
166            BondMode::BalanceTlb => BOND_MODE_TLB,
167            BondMode::BalanceAlb => BOND_MODE_ALB,
168            BondMode::Other(d) => d,
169        }
170    }
171}
172
173impl std::fmt::Display for BondMode {
174    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
175        let kernel_name = match self {
176            BondMode::BalanceRr => "balance-rr",
177            BondMode::ActiveBackup => "active-backup",
178            BondMode::BalanceXor => "balance-xor",
179            BondMode::Broadcast => "broadcast",
180            BondMode::Ieee8023Ad => "802.3ad",
181            BondMode::BalanceTlb => "balance-tlb",
182            BondMode::BalanceAlb => "balance-alb",
183            BondMode::Other(d) => return write!(f, "unknown-variant ({d})"),
184        };
185
186        f.write_str(kernel_name)
187    }
188}
189
190// Some attributes (ARP_IP_TARGET, NS_IP6_TARGET) contain a nested
191// list of IP addresses, where each element uses the index as NLA kind
192// and the address as value. InfoBond exposes vectors of IP addresses,
193// and we use this struct for serialization.
194struct BondIpAddrNla {
195    index: u16,
196    addr: IpAddr,
197}
198
199struct BondIpAddrNlaList(Vec<BondIpAddrNla>);
200
201impl Deref for BondIpAddrNlaList {
202    type Target = Vec<BondIpAddrNla>;
203
204    fn deref(&self) -> &Self::Target {
205        &self.0
206    }
207}
208
209impl From<&Vec<Ipv4Addr>> for BondIpAddrNlaList {
210    fn from(addrs: &Vec<Ipv4Addr>) -> Self {
211        let mut nlas = Vec::new();
212        for (i, addr) in addrs.iter().enumerate() {
213            let nla = BondIpAddrNla { index: i as u16, addr: IpAddr::V4(*addr) };
214            nlas.push(nla);
215        }
216        BondIpAddrNlaList(nlas)
217    }
218}
219
220impl From<&Vec<Ipv6Addr>> for BondIpAddrNlaList {
221    fn from(addrs: &Vec<Ipv6Addr>) -> Self {
222        let mut nlas = Vec::new();
223        for (i, addr) in addrs.iter().enumerate() {
224            let nla = BondIpAddrNla { index: i as u16, addr: IpAddr::V6(*addr) };
225            nlas.push(nla);
226        }
227        BondIpAddrNlaList(nlas)
228    }
229}
230
231impl Nla for BondIpAddrNla {
232    fn value_len(&self) -> usize {
233        if self.addr.is_ipv4() {
234            4
235        } else {
236            16
237        }
238    }
239    fn emit_value(&self, buffer: &mut [u8]) {
240        match self.addr {
241            IpAddr::V4(addr) => buffer.copy_from_slice(&addr.octets()),
242            IpAddr::V6(addr) => buffer.copy_from_slice(&addr.octets()),
243        }
244    }
245    fn kind(&self) -> u16 {
246        self.index
247    }
248}
249
250#[derive(Debug, PartialEq, Eq, Clone)]
251#[non_exhaustive]
252pub enum InfoBond {
253    Mode(BondMode),
254    ActivePort(u32),
255    MiiMon(u32),
256    UpDelay(u32),
257    DownDelay(u32),
258    UseCarrier(u8),
259    ArpInterval(u32),
260    ArpIpTarget(Vec<Ipv4Addr>),
261    ArpValidate(u32),
262    ArpAllTargets(u32),
263    Primary(u32),
264    PrimaryReselect(u8),
265    FailOverMac(u8),
266    XmitHashPolicy(u8),
267    ResendIgmp(u32),
268    NumPeerNotif(u8),
269    AllPortsActive(u8),
270    MinLinks(u32),
271    LpInterval(u32),
272    PacketsPerPort(u32),
273    AdLacpRate(u8),
274    AdSelect(u8),
275    AdInfo(Vec<BondAdInfo>),
276    AdActorSysPrio(u16),
277    AdUserPortKey(u16),
278    AdActorSystem([u8; 6]),
279    TlbDynamicLb(u8),
280    PeerNotifDelay(u32),
281    AdLacpActive(u8),
282    MissedMax(u8),
283    NsIp6Target(Vec<Ipv6Addr>),
284    Other(DefaultNla),
285}
286
287impl Nla for InfoBond {
288    fn value_len(&self) -> usize {
289        match self {
290            Self::Mode(_)
291            | Self::UseCarrier(_)
292            | Self::PrimaryReselect(_)
293            | Self::FailOverMac(_)
294            | Self::XmitHashPolicy(_)
295            | Self::NumPeerNotif(_)
296            | Self::AllPortsActive(_)
297            | Self::AdLacpActive(_)
298            | Self::AdLacpRate(_)
299            | Self::AdSelect(_)
300            | Self::TlbDynamicLb(_)
301            | Self::MissedMax(_) => 1,
302            Self::AdActorSysPrio(_) | Self::AdUserPortKey(_) => 2,
303            Self::ActivePort(_)
304            | Self::MiiMon(_)
305            | Self::UpDelay(_)
306            | Self::DownDelay(_)
307            | Self::ArpInterval(_)
308            | Self::ArpValidate(_)
309            | Self::ArpAllTargets(_)
310            | Self::Primary(_)
311            | Self::ResendIgmp(_)
312            | Self::MinLinks(_)
313            | Self::LpInterval(_)
314            | Self::PacketsPerPort(_)
315            | Self::PeerNotifDelay(_) => 4,
316            Self::ArpIpTarget(ref addrs) => BondIpAddrNlaList::from(addrs).as_slice().buffer_len(),
317            Self::NsIp6Target(ref addrs) => BondIpAddrNlaList::from(addrs).as_slice().buffer_len(),
318            Self::AdActorSystem(_) => 6,
319            Self::AdInfo(infos) => infos.as_slice().buffer_len(),
320            Self::Other(v) => v.value_len(),
321        }
322    }
323
324    fn emit_value(&self, buffer: &mut [u8]) {
325        match self {
326            Self::Mode(value) => buffer[0] = (*value).into(),
327            Self::UseCarrier(value)
328            | Self::PrimaryReselect(value)
329            | Self::FailOverMac(value)
330            | Self::XmitHashPolicy(value)
331            | Self::NumPeerNotif(value)
332            | Self::AllPortsActive(value)
333            | Self::AdLacpActive(value)
334            | Self::AdLacpRate(value)
335            | Self::AdSelect(value)
336            | Self::TlbDynamicLb(value)
337            | Self::MissedMax(value) => buffer[0] = *value,
338            Self::AdActorSysPrio(value) | Self::AdUserPortKey(value) => {
339                NativeEndian::write_u16(buffer, *value)
340            }
341            Self::ActivePort(value)
342            | Self::MiiMon(value)
343            | Self::UpDelay(value)
344            | Self::DownDelay(value)
345            | Self::ArpInterval(value)
346            | Self::ArpValidate(value)
347            | Self::ArpAllTargets(value)
348            | Self::Primary(value)
349            | Self::ResendIgmp(value)
350            | Self::MinLinks(value)
351            | Self::LpInterval(value)
352            | Self::PacketsPerPort(value)
353            | Self::PeerNotifDelay(value) => NativeEndian::write_u32(buffer, *value),
354            Self::AdActorSystem(bytes) => buffer.copy_from_slice(bytes),
355            Self::ArpIpTarget(addrs) => BondIpAddrNlaList::from(addrs).as_slice().emit(buffer),
356            Self::NsIp6Target(addrs) => BondIpAddrNlaList::from(addrs).as_slice().emit(buffer),
357            Self::AdInfo(infos) => infos.as_slice().emit(buffer),
358            Self::Other(v) => v.emit_value(buffer),
359        }
360    }
361
362    fn kind(&self) -> u16 {
363        match self {
364            Self::Mode(_) => IFLA_BOND_MODE,
365            Self::ActivePort(_) => IFLA_BOND_ACTIVE_PORT,
366            Self::MiiMon(_) => IFLA_BOND_MIIMON,
367            Self::UpDelay(_) => IFLA_BOND_UPDELAY,
368            Self::DownDelay(_) => IFLA_BOND_DOWNDELAY,
369            Self::UseCarrier(_) => IFLA_BOND_USE_CARRIER,
370            Self::ArpInterval(_) => IFLA_BOND_ARP_INTERVAL,
371            Self::ArpIpTarget(_) => IFLA_BOND_ARP_IP_TARGET,
372            Self::ArpValidate(_) => IFLA_BOND_ARP_VALIDATE,
373            Self::ArpAllTargets(_) => IFLA_BOND_ARP_ALL_TARGETS,
374            Self::Primary(_) => IFLA_BOND_PRIMARY,
375            Self::PrimaryReselect(_) => IFLA_BOND_PRIMARY_RESELECT,
376            Self::FailOverMac(_) => IFLA_BOND_FAIL_OVER_MAC,
377            Self::XmitHashPolicy(_) => IFLA_BOND_XMIT_HASH_POLICY,
378            Self::ResendIgmp(_) => IFLA_BOND_RESEND_IGMP,
379            Self::NumPeerNotif(_) => IFLA_BOND_NUM_PEER_NOTIF,
380            Self::AllPortsActive(_) => IFLA_BOND_ALL_PORTS_ACTIVE,
381            Self::MinLinks(_) => IFLA_BOND_MIN_LINKS,
382            Self::LpInterval(_) => IFLA_BOND_LP_INTERVAL,
383            Self::PacketsPerPort(_) => IFLA_BOND_PACKETS_PER_PORT,
384            Self::AdLacpRate(_) => IFLA_BOND_AD_LACP_RATE,
385            Self::AdSelect(_) => IFLA_BOND_AD_SELECT,
386            Self::AdInfo(_) => IFLA_BOND_AD_INFO,
387            Self::AdActorSysPrio(_) => IFLA_BOND_AD_ACTOR_SYS_PRIO,
388            Self::AdUserPortKey(_) => IFLA_BOND_AD_USER_PORT_KEY,
389            Self::AdActorSystem(_) => IFLA_BOND_AD_ACTOR_SYSTEM,
390            Self::TlbDynamicLb(_) => IFLA_BOND_TLB_DYNAMIC_LB,
391            Self::PeerNotifDelay(_) => IFLA_BOND_PEER_NOTIF_DELAY,
392            Self::AdLacpActive(_) => IFLA_BOND_AD_LACP_ACTIVE,
393            Self::MissedMax(_) => IFLA_BOND_MISSED_MAX,
394            Self::NsIp6Target(_) => IFLA_BOND_NS_IP6_TARGET,
395            Self::Other(v) => v.kind(),
396        }
397    }
398}
399
400impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for InfoBond {
401    type Error = DecodeError;
402    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
403        let payload = buf.value();
404        Ok(match buf.kind() {
405            IFLA_BOND_MODE => {
406                Self::Mode(parse_u8(payload).context("invalid IFLA_BOND_MODE value")?.into())
407            }
408            IFLA_BOND_ACTIVE_PORT => {
409                Self::ActivePort(parse_u32(payload).context("invalid IFLA_BOND_ACTIVE_PORT value")?)
410            }
411            IFLA_BOND_MIIMON => {
412                Self::MiiMon(parse_u32(payload).context("invalid IFLA_BOND_MIIMON value")?)
413            }
414            IFLA_BOND_UPDELAY => {
415                Self::UpDelay(parse_u32(payload).context("invalid IFLA_BOND_UPDELAY value")?)
416            }
417            IFLA_BOND_DOWNDELAY => {
418                Self::DownDelay(parse_u32(payload).context("invalid IFLA_BOND_DOWNDELAY value")?)
419            }
420            IFLA_BOND_USE_CARRIER => {
421                Self::UseCarrier(parse_u8(payload).context("invalid IFLA_BOND_USE_CARRIER value")?)
422            }
423            IFLA_BOND_ARP_INTERVAL => Self::ArpInterval(
424                parse_u32(payload).context("invalid IFLA_BOND_ARP_INTERVAL value")?,
425            ),
426            IFLA_BOND_ARP_IP_TARGET => {
427                let mut addrs = Vec::<Ipv4Addr>::new();
428                for nla in NlasIterator::new(payload) {
429                    let nla = &nla.context("invalid IFLA_BOND_ARP_IP_TARGET value")?;
430                    if let Ok(IpAddr::V4(addr)) = parse_ip(nla.value()) {
431                        addrs.push(addr);
432                    }
433                }
434                Self::ArpIpTarget(addrs)
435            }
436            IFLA_BOND_ARP_VALIDATE => Self::ArpValidate(
437                parse_u32(payload).context("invalid IFLA_BOND_ARP_VALIDATE value")?,
438            ),
439            IFLA_BOND_ARP_ALL_TARGETS => Self::ArpAllTargets(
440                parse_u32(payload).context("invalid IFLA_BOND_ARP_ALL_TARGETS value")?,
441            ),
442            IFLA_BOND_PRIMARY => {
443                Self::Primary(parse_u32(payload).context("invalid IFLA_BOND_PRIMARY value")?)
444            }
445            IFLA_BOND_PRIMARY_RESELECT => Self::PrimaryReselect(
446                parse_u8(payload).context("invalid IFLA_BOND_PRIMARY_RESELECT value")?,
447            ),
448            IFLA_BOND_FAIL_OVER_MAC => Self::FailOverMac(
449                parse_u8(payload).context("invalid IFLA_BOND_FAIL_OVER_MAC value")?,
450            ),
451            IFLA_BOND_XMIT_HASH_POLICY => Self::XmitHashPolicy(
452                parse_u8(payload).context("invalid IFLA_BOND_XMIT_HASH_POLICY value")?,
453            ),
454            IFLA_BOND_RESEND_IGMP => {
455                Self::ResendIgmp(parse_u32(payload).context("invalid IFLA_BOND_RESEND_IGMP value")?)
456            }
457            IFLA_BOND_NUM_PEER_NOTIF => Self::NumPeerNotif(
458                parse_u8(payload).context("invalid IFLA_BOND_NUM_PEER_NOTIF value")?,
459            ),
460            IFLA_BOND_ALL_PORTS_ACTIVE => Self::AllPortsActive(
461                parse_u8(payload).context("invalid IFLA_BOND_ALL_PORTS_ACTIVE value")?,
462            ),
463            IFLA_BOND_MIN_LINKS => {
464                Self::MinLinks(parse_u32(payload).context("invalid IFLA_BOND_MIN_LINKS value")?)
465            }
466            IFLA_BOND_LP_INTERVAL => {
467                Self::LpInterval(parse_u32(payload).context("invalid IFLA_BOND_LP_INTERVAL value")?)
468            }
469            IFLA_BOND_PACKETS_PER_PORT => Self::PacketsPerPort(
470                parse_u32(payload).context("invalid IFLA_BOND_PACKETS_PER_PORT value")?,
471            ),
472            IFLA_BOND_AD_LACP_RATE => {
473                Self::AdLacpRate(parse_u8(payload).context("invalid IFLA_BOND_AD_LACP_RATE value")?)
474            }
475            IFLA_BOND_AD_SELECT => {
476                Self::AdSelect(parse_u8(payload).context("invalid IFLA_BOND_AD_SELECT value")?)
477            }
478            IFLA_BOND_AD_INFO => {
479                let mut infos = Vec::new();
480                let err = "failed to parse IFLA_BOND_AD_INFO";
481                for nla in NlasIterator::new(payload) {
482                    let nla = &nla.context(err)?;
483                    let info = BondAdInfo::parse(nla).context(err)?;
484                    infos.push(info);
485                }
486                Self::AdInfo(infos)
487            }
488            IFLA_BOND_AD_ACTOR_SYS_PRIO => Self::AdActorSysPrio(
489                parse_u16(payload).context("invalid IFLA_BOND_AD_ACTOR_SYS_PRIO value")?,
490            ),
491            IFLA_BOND_AD_USER_PORT_KEY => Self::AdUserPortKey(
492                parse_u16(payload).context("invalid IFLA_BOND_AD_USER_PORT_KEY value")?,
493            ),
494            IFLA_BOND_AD_ACTOR_SYSTEM => Self::AdActorSystem(
495                parse_mac(payload).context("invalid IFLA_BOND_AD_ACTOR_SYSTEM value")?,
496            ),
497            IFLA_BOND_TLB_DYNAMIC_LB => Self::TlbDynamicLb(
498                parse_u8(payload).context("invalid IFLA_BOND_TLB_DYNAMIC_LB value")?,
499            ),
500            IFLA_BOND_PEER_NOTIF_DELAY => Self::PeerNotifDelay(
501                parse_u32(payload).context("invalid IFLA_BOND_PEER_NOTIF_DELAY value")?,
502            ),
503            IFLA_BOND_AD_LACP_ACTIVE => Self::AdLacpActive(
504                parse_u8(payload).context("invalid IFLA_BOND_AD_LACP_ACTIVE value")?,
505            ),
506            IFLA_BOND_MISSED_MAX => {
507                Self::MissedMax(parse_u8(payload).context("invalid IFLA_BOND_MISSED_MAX value")?)
508            }
509            IFLA_BOND_NS_IP6_TARGET => {
510                let mut addrs = Vec::<Ipv6Addr>::new();
511                for nla in NlasIterator::new(payload) {
512                    let nla = &nla.context("invalid IFLA_BOND_NS_IP6_TARGET value")?;
513                    if let Ok(IpAddr::V6(addr)) = parse_ip(nla.value()) {
514                        addrs.push(addr);
515                    }
516                }
517                Self::NsIp6Target(addrs)
518            }
519            _ => Self::Other(
520                DefaultNla::parse(buf)
521                    .context(format!("invalid NLA for {}: {payload:?}", buf.kind()))?,
522            ),
523        })
524    }
525}