netlink_packet_route/rtnl/link/nlas/
bond.rs

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