netlink_packet_route/rtnl/link/nlas/
mod.rs

1// SPDX-License-Identifier: MIT
2
3mod inet;
4pub use self::inet::*;
5
6mod inet6;
7pub use self::inet6::*;
8
9mod af_spec_inet;
10pub use self::af_spec_inet::*;
11
12mod af_spec_bridge;
13pub use self::af_spec_bridge::*;
14
15mod link_infos;
16pub use self::link_infos::*;
17
18mod bond;
19pub use self::bond::*;
20
21mod bond_port;
22pub use self::bond_port::*;
23
24mod bridge;
25pub use self::bridge::*;
26
27mod prop_list;
28pub use self::prop_list::*;
29
30mod map;
31pub use self::map::*;
32
33mod stats;
34pub use self::stats::*;
35
36mod stats64;
37pub use self::stats64::*;
38
39mod link_state;
40pub use self::link_state::*;
41
42mod link_xdp;
43pub use self::link_xdp::*;
44
45#[cfg(test)]
46mod tests;
47
48use std::os::unix::io::RawFd;
49
50use anyhow::Context;
51use byteorder::{ByteOrder, NativeEndian};
52use netlink_packet_utils::{
53    nla::{self, DefaultNla, NlaBuffer, NlasIterator, NLA_F_NESTED},
54    parsers::{parse_i32, parse_string, parse_u32, parse_u8},
55    traits::{Emitable, Parseable, ParseableParametrized},
56    DecodeError,
57};
58
59use crate::constants::*;
60
61#[derive(Debug, PartialEq, Eq, Clone)]
62#[non_exhaustive]
63pub enum Nla {
64    // Vec<u8>
65    Unspec(Vec<u8>),
66    Cost(Vec<u8>),
67    Priority(Vec<u8>),
68    Weight(Vec<u8>),
69    VfInfoList(Vec<u8>),
70    VfPorts(Vec<u8>),
71    PortSelf(Vec<u8>),
72    PhysPortId(Vec<u8>),
73    PhysSwitchId(Vec<u8>),
74    Pad(Vec<u8>),
75    Xdp(Vec<Xdp>),
76    Event(Vec<u8>),
77    NewNetnsId(Vec<u8>),
78    IfNetnsId(Vec<u8>),
79    CarrierUpCount(Vec<u8>),
80    CarrierDownCount(Vec<u8>),
81    NewIfIndex(Vec<u8>),
82    Info(Vec<Info>),
83    Wireless(Vec<u8>),
84    ProtoInfo(Vec<u8>),
85    /// A list of properties for the device. For additional context see the
86    /// related linux kernel threads<sup>[1][1],[2][2]</sup>. In particular
87    /// see [this message][defining message] from the first thread
88    /// describing the design.
89    ///
90    /// [1]: https://lwn.net/ml/netdev/20190719110029.29466-1-jiri@resnulli.us/
91    /// [2]: https://lwn.net/ml/netdev/20190930094820.11281-1-jiri@resnulli.us/
92    /// [defining message]: https://lwn.net/ml/netdev/20190913145012.GB2276@nanopsycho.orion/
93    PropList(Vec<Prop>),
94    /// `protodown` is a mechanism that allows protocols to hold an interface
95    /// down. This field is used to specify the reason why it is held down.
96    /// For additional context see the related linux kernel
97    /// threads<sup>[1][1],[2][2]</sup>.
98    ///
99    /// [1]: https://lwn.net/ml/netdev/1595877677-45849-1-git-send-email-roopa%40cumulusnetworks.com/
100    /// [2]: https://lwn.net/ml/netdev/1596242041-14347-1-git-send-email-roopa%40cumulusnetworks.com/
101    ProtoDownReason(Vec<u8>),
102    // mac address (use to be [u8; 6] but it turns out MAC != HW address, for
103    // instance for IP over GRE where it's an IPv4!)
104    Address(Vec<u8>),
105    Broadcast(Vec<u8>),
106    /// Permanent hardware address of the device. The provides the same
107    /// information as the ethtool ioctl interface.
108    PermAddress(Vec<u8>),
109
110    // string
111    // FIXME: for empty string, should we encode the NLA as \0 or should we
112    // not set a payload? It seems that for certain attriutes, this
113    // matter: https://elixir.bootlin.com/linux/v4.17-rc5/source/net/core/rtnetlink.c#L1660
114    IfName(String),
115    Qdisc(String),
116    IfAlias(String),
117    PhysPortName(String),
118    /// Alternate name for the device.
119    /// For additional context see the related linux kernel
120    /// threads<sup>[1][1],[2][2]</sup>.
121    ///
122    /// [1]: https://lwn.net/ml/netdev/20190719110029.29466-1-jiri@resnulli.us/
123    /// [2]: https://lwn.net/ml/netdev/20190930094820.11281-1-jiri@resnulli.us/
124    AltIfName(String),
125    // byte
126    Mode(u8),
127    Carrier(u8),
128    ProtoDown(u8),
129    // u32
130    Mtu(u32),
131    Link(u32),
132    Master(u32),
133    TxQueueLen(u32),
134    NetNsPid(u32),
135    NumVf(u32),
136    Group(u32),
137    NetNsFd(RawFd),
138    ExtMask(u32),
139    Promiscuity(u32),
140    NumTxQueues(u32),
141    NumRxQueues(u32),
142    CarrierChanges(u32),
143    GsoMaxSegs(u32),
144    GsoMaxSize(u32),
145    /// The minimum MTU for the device.
146    /// For additional context see the related [linux kernel message][1].
147    ///
148    /// [1]: https://lwn.net/ml/netdev/20180727204323.19408-3-sthemmin%40microsoft.com/
149    MinMtu(u32),
150    /// The maximum MTU for the device.
151    /// For additional context see the related [linux kernel message][1].
152    ///
153    /// [1]: https://lwn.net/ml/netdev/20180727204323.19408-3-sthemmin%40microsoft.com/
154    MaxMtu(u32),
155    // i32
156    NetnsId(i32),
157    // custom
158    OperState(State),
159    Stats(Vec<u8>),
160    Stats64(Vec<u8>),
161    Map(Vec<u8>),
162    // AF_SPEC (the type of af_spec depends on the interface family of the
163    // message)
164    AfSpecInet(Vec<AfSpecInet>),
165    AfSpecBridge(Vec<AfSpecBridge>),
166    //AfSpecBridge(Vec<u8>),
167    AfSpecUnknown(Vec<u8>),
168    Other(DefaultNla),
169}
170
171impl nla::Nla for Nla {
172    #[rustfmt::skip]
173    fn value_len(&self) -> usize {
174        use self::Nla::*;
175        match *self {
176            // Vec<u8>
177            Unspec(ref bytes)
178                | Cost(ref bytes)
179                | Priority(ref bytes)
180                | Weight(ref bytes)
181                | VfInfoList(ref bytes)
182                | VfPorts(ref bytes)
183                | PortSelf(ref bytes)
184                | PhysPortId(ref bytes)
185                | PhysSwitchId(ref bytes)
186                | Pad(ref bytes)
187                | Event(ref bytes)
188                | NewNetnsId(ref bytes)
189                | IfNetnsId(ref bytes)
190                | Wireless(ref bytes)
191                | ProtoInfo(ref bytes)
192                | CarrierUpCount(ref bytes)
193                | CarrierDownCount(ref bytes)
194                | NewIfIndex(ref bytes)
195                | Address(ref bytes)
196                | Broadcast(ref bytes)
197                | PermAddress(ref bytes)
198                | AfSpecUnknown(ref bytes)
199                | Map(ref bytes)
200                | ProtoDownReason(ref bytes)
201                => bytes.len(),
202
203            // strings: +1 because we need to append a nul byte
204            IfName(ref string)
205                | Qdisc(ref string)
206                | IfAlias(ref string)
207                | PhysPortName(ref string)
208                | AltIfName(ref string)
209                => string.as_bytes().len() + 1,
210
211            // u8
212            Mode(_)
213                | Carrier(_)
214                | ProtoDown(_)
215                => 1,
216
217            // u32 and i32
218            Mtu(_)
219                | Link(_)
220                | Master(_)
221                | TxQueueLen(_)
222                | NetNsPid(_)
223                | NumVf(_)
224                | Group(_)
225                | NetNsFd(_)
226                | ExtMask(_)
227                | Promiscuity(_)
228                | NumTxQueues(_)
229                | NumRxQueues(_)
230                | CarrierChanges(_)
231                | GsoMaxSegs(_)
232                | GsoMaxSize(_)
233                | NetnsId(_)
234                | MinMtu(_)
235                | MaxMtu(_) => 4,
236
237            // Defaults
238            OperState(_) => 1,
239            Stats(_) => LINK_STATS_LEN,
240            Stats64(_) => LINK_STATS64_LEN,
241            Info(ref nlas) => nlas.as_slice().buffer_len(),
242            Xdp(ref nlas) => nlas.as_slice().buffer_len(),
243            PropList(ref nlas) => nlas.as_slice().buffer_len(),
244            AfSpecInet(ref nlas) => nlas.as_slice().buffer_len(),
245            AfSpecBridge(ref nlas) => nlas.as_slice().buffer_len(),
246            Other(ref attr)  => attr.value_len(),
247        }
248    }
249
250    #[rustfmt::skip]
251    fn emit_value(&self, buffer: &mut [u8]) {
252        use self::Nla::*;
253        match *self {
254            // Vec<u8>
255            Unspec(ref bytes)
256                | Cost(ref bytes)
257                | Priority(ref bytes)
258                | Weight(ref bytes)
259                | VfInfoList(ref bytes)
260                | VfPorts(ref bytes)
261                | PortSelf(ref bytes)
262                | PhysPortId(ref bytes)
263                | PhysSwitchId(ref bytes)
264                | Wireless(ref bytes)
265                | ProtoInfo(ref bytes)
266                | Pad(ref bytes)
267                | Event(ref bytes)
268                | NewNetnsId(ref bytes)
269                | IfNetnsId(ref bytes)
270                | CarrierUpCount(ref bytes)
271                | CarrierDownCount(ref bytes)
272                | NewIfIndex(ref bytes)
273                // mac address (could be [u8; 6] or [u8; 4] for example. Not sure if we should have
274                // a separate type for them
275                | Address(ref bytes)
276                | Broadcast(ref bytes)
277                | PermAddress(ref bytes)
278                | AfSpecUnknown(ref bytes)
279                | Stats(ref bytes)
280                | Stats64(ref bytes)
281                | Map(ref bytes)
282                | ProtoDownReason(ref bytes)
283                => buffer.copy_from_slice(bytes.as_slice()),
284
285            // String
286            IfName(ref string)
287                | Qdisc(ref string)
288                | IfAlias(ref string)
289                | PhysPortName(ref string)
290                | AltIfName(ref string)
291                => {
292                    buffer[..string.len()].copy_from_slice(string.as_bytes());
293                    buffer[string.len()] = 0;
294                }
295
296            // u8
297            Mode(ref val)
298                | Carrier(ref val)
299                | ProtoDown(ref val)
300                => buffer[0] = *val,
301
302            // u32
303            Mtu(ref value)
304                | Link(ref value)
305                | Master(ref value)
306                | TxQueueLen(ref value)
307                | NetNsPid(ref value)
308                | NumVf(ref value)
309                | Group(ref value)
310                | ExtMask(ref value)
311                | Promiscuity(ref value)
312                | NumTxQueues(ref value)
313                | NumRxQueues(ref value)
314                | CarrierChanges(ref value)
315                | GsoMaxSegs(ref value)
316                | GsoMaxSize(ref value)
317                | MinMtu(ref value)
318                | MaxMtu(ref value)
319                => NativeEndian::write_u32(buffer, *value),
320
321            NetnsId(ref value)
322                | NetNsFd(ref value)
323                => NativeEndian::write_i32(buffer, *value),
324
325            OperState(state) => buffer[0] = state.into(),
326            Info(ref nlas) => nlas.as_slice().emit(buffer),
327            Xdp(ref nlas) => nlas.as_slice().emit(buffer),
328            PropList(ref nlas) => nlas.as_slice().emit(buffer),
329            AfSpecInet(ref nlas) => nlas.as_slice().emit(buffer),
330            AfSpecBridge(ref nlas) => nlas.as_slice().emit(buffer),
331            // default nlas
332            Other(ref attr) => attr.emit_value(buffer),
333        }
334    }
335
336    fn kind(&self) -> u16 {
337        use self::Nla::*;
338        match *self {
339            // Vec<u8>
340            Unspec(_) => IFLA_UNSPEC,
341            Cost(_) => IFLA_COST,
342            Priority(_) => IFLA_PRIORITY,
343            Weight(_) => IFLA_WEIGHT,
344            VfInfoList(_) => IFLA_VFINFO_LIST,
345            VfPorts(_) => IFLA_VF_PORTS,
346            PortSelf(_) => IFLA_PORT_SELF,
347            PhysPortId(_) => IFLA_PHYS_PORT_ID,
348            PhysSwitchId(_) => IFLA_PHYS_SWITCH_ID,
349            Info(_) => IFLA_LINKINFO,
350            Wireless(_) => IFLA_WIRELESS,
351            ProtoInfo(_) => IFLA_PROTINFO,
352            Pad(_) => IFLA_PAD,
353            Xdp(_) => IFLA_XDP,
354            Event(_) => IFLA_EVENT,
355            NewNetnsId(_) => IFLA_NEW_NETNSID,
356            IfNetnsId(_) => IFLA_IF_NETNSID,
357            CarrierUpCount(_) => IFLA_CARRIER_UP_COUNT,
358            CarrierDownCount(_) => IFLA_CARRIER_DOWN_COUNT,
359            NewIfIndex(_) => IFLA_NEW_IFINDEX,
360            PropList(_) => IFLA_PROP_LIST | NLA_F_NESTED,
361            ProtoDownReason(_) => IFLA_PROTO_DOWN_REASON,
362            // Mac address
363            Address(_) => IFLA_ADDRESS,
364            Broadcast(_) => IFLA_BROADCAST,
365            PermAddress(_) => IFLA_PERM_ADDRESS,
366            // String
367            IfName(_) => IFLA_IFNAME,
368            Qdisc(_) => IFLA_QDISC,
369            IfAlias(_) => IFLA_IFALIAS,
370            PhysPortName(_) => IFLA_PHYS_PORT_NAME,
371            AltIfName(_) => IFLA_ALT_IFNAME,
372            // u8
373            Mode(_) => IFLA_LINKMODE,
374            Carrier(_) => IFLA_CARRIER,
375            ProtoDown(_) => IFLA_PROTO_DOWN,
376            // u32
377            Mtu(_) => IFLA_MTU,
378            Link(_) => IFLA_LINK,
379            Master(_) => IFLA_MASTER,
380            TxQueueLen(_) => IFLA_TXQLEN,
381            NetNsPid(_) => IFLA_NET_NS_PID,
382            NumVf(_) => IFLA_NUM_VF,
383            Group(_) => IFLA_GROUP,
384            NetNsFd(_) => IFLA_NET_NS_FD,
385            ExtMask(_) => IFLA_EXT_MASK,
386            Promiscuity(_) => IFLA_PROMISCUITY,
387            NumTxQueues(_) => IFLA_NUM_TX_QUEUES,
388            NumRxQueues(_) => IFLA_NUM_RX_QUEUES,
389            CarrierChanges(_) => IFLA_CARRIER_CHANGES,
390            GsoMaxSegs(_) => IFLA_GSO_MAX_SEGS,
391            GsoMaxSize(_) => IFLA_GSO_MAX_SIZE,
392            MinMtu(_) => IFLA_MIN_MTU,
393            MaxMtu(_) => IFLA_MAX_MTU,
394            // i32
395            NetnsId(_) => IFLA_LINK_NETNSID,
396            // custom
397            OperState(_) => IFLA_OPERSTATE,
398            Map(_) => IFLA_MAP,
399            Stats(_) => IFLA_STATS,
400            Stats64(_) => IFLA_STATS64,
401            AfSpecInet(_) | AfSpecBridge(_) | AfSpecUnknown(_) => IFLA_AF_SPEC,
402            Other(ref attr) => attr.kind(),
403        }
404    }
405}
406
407impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized<NlaBuffer<&'a T>, u16>
408    for Nla
409{
410    fn parse_with_param(
411        buf: &NlaBuffer<&'a T>,
412        interface_family: u16,
413    ) -> Result<Self, DecodeError> {
414        use Nla::*;
415        let payload = buf.value();
416        Ok(match buf.kind() {
417            // Vec<u8>
418            IFLA_UNSPEC => Unspec(payload.to_vec()),
419            IFLA_COST => Cost(payload.to_vec()),
420            IFLA_PRIORITY => Priority(payload.to_vec()),
421            IFLA_WEIGHT => Weight(payload.to_vec()),
422            IFLA_VFINFO_LIST => VfInfoList(payload.to_vec()),
423            IFLA_VF_PORTS => VfPorts(payload.to_vec()),
424            IFLA_PORT_SELF => PortSelf(payload.to_vec()),
425            IFLA_PHYS_PORT_ID => PhysPortId(payload.to_vec()),
426            IFLA_PHYS_SWITCH_ID => PhysSwitchId(payload.to_vec()),
427            IFLA_WIRELESS => Wireless(payload.to_vec()),
428            IFLA_PROTINFO => ProtoInfo(payload.to_vec()),
429            IFLA_PAD => Pad(payload.to_vec()),
430            IFLA_EVENT => Event(payload.to_vec()),
431            IFLA_NEW_NETNSID => NewNetnsId(payload.to_vec()),
432            IFLA_IF_NETNSID => IfNetnsId(payload.to_vec()),
433            IFLA_CARRIER_UP_COUNT => CarrierUpCount(payload.to_vec()),
434            IFLA_CARRIER_DOWN_COUNT => CarrierDownCount(payload.to_vec()),
435            IFLA_NEW_IFINDEX => NewIfIndex(payload.to_vec()),
436            IFLA_PROP_LIST => {
437                let error_msg = "invalid IFLA_PROP_LIST value";
438                let mut nlas = vec![];
439                for nla in NlasIterator::new(payload) {
440                    let nla = &nla.context(error_msg)?;
441                    let parsed = Prop::parse(nla).context(error_msg)?;
442                    nlas.push(parsed);
443                }
444                PropList(nlas)
445            }
446            IFLA_PROTO_DOWN_REASON => ProtoDownReason(payload.to_vec()),
447            // HW address (we parse them as Vec for now, because for IP over
448            // GRE, the HW address is an IP instead of a MAC for
449            // example
450            IFLA_ADDRESS => Address(payload.to_vec()),
451            IFLA_BROADCAST => Broadcast(payload.to_vec()),
452            IFLA_PERM_ADDRESS => PermAddress(payload.to_vec()),
453            // String
454            IFLA_IFNAME => IfName(
455                parse_string(payload).context("invalid IFLA_IFNAME value")?,
456            ),
457            IFLA_QDISC => Qdisc(
458                parse_string(payload).context("invalid IFLA_QDISC value")?,
459            ),
460            IFLA_IFALIAS => IfAlias(
461                parse_string(payload).context("invalid IFLA_IFALIAS value")?,
462            ),
463            IFLA_PHYS_PORT_NAME => PhysPortName(
464                parse_string(payload)
465                    .context("invalid IFLA_PHYS_PORT_NAME value")?,
466            ),
467            IFLA_ALT_IFNAME => AltIfName(
468                parse_string(payload)
469                    .context("invalid IFLA_ALT_IFNAME value")?,
470            ),
471
472            // u8
473            IFLA_LINKMODE => {
474                Mode(parse_u8(payload).context("invalid IFLA_LINKMODE value")?)
475            }
476            IFLA_CARRIER => Carrier(
477                parse_u8(payload).context("invalid IFLA_CARRIER value")?,
478            ),
479            IFLA_PROTO_DOWN => ProtoDown(
480                parse_u8(payload).context("invalid IFLA_PROTO_DOWN value")?,
481            ),
482
483            IFLA_MTU => {
484                Mtu(parse_u32(payload).context("invalid IFLA_MTU value")?)
485            }
486            IFLA_LINK => {
487                Link(parse_u32(payload).context("invalid IFLA_LINK value")?)
488            }
489            IFLA_MASTER => {
490                Master(parse_u32(payload).context("invalid IFLA_MASTER value")?)
491            }
492            IFLA_TXQLEN => TxQueueLen(
493                parse_u32(payload).context("invalid IFLA_TXQLEN value")?,
494            ),
495            IFLA_NET_NS_PID => NetNsPid(
496                parse_u32(payload).context("invalid IFLA_NET_NS_PID value")?,
497            ),
498            IFLA_NUM_VF => {
499                NumVf(parse_u32(payload).context("invalid IFLA_NUM_VF value")?)
500            }
501            IFLA_GROUP => {
502                Group(parse_u32(payload).context("invalid IFLA_GROUP value")?)
503            }
504            IFLA_NET_NS_FD => NetNsFd(
505                parse_i32(payload).context("invalid IFLA_NET_NS_FD value")?,
506            ),
507            IFLA_EXT_MASK => ExtMask(
508                parse_u32(payload).context("invalid IFLA_EXT_MASK value")?,
509            ),
510            IFLA_PROMISCUITY => Promiscuity(
511                parse_u32(payload).context("invalid IFLA_PROMISCUITY value")?,
512            ),
513            IFLA_NUM_TX_QUEUES => NumTxQueues(
514                parse_u32(payload)
515                    .context("invalid IFLA_NUM_TX_QUEUES value")?,
516            ),
517            IFLA_NUM_RX_QUEUES => NumRxQueues(
518                parse_u32(payload)
519                    .context("invalid IFLA_NUM_RX_QUEUES value")?,
520            ),
521            IFLA_CARRIER_CHANGES => CarrierChanges(
522                parse_u32(payload)
523                    .context("invalid IFLA_CARRIER_CHANGES value")?,
524            ),
525            IFLA_GSO_MAX_SEGS => GsoMaxSegs(
526                parse_u32(payload)
527                    .context("invalid IFLA_GSO_MAX_SEGS value")?,
528            ),
529            IFLA_GSO_MAX_SIZE => GsoMaxSize(
530                parse_u32(payload)
531                    .context("invalid IFLA_GSO_MAX_SIZE value")?,
532            ),
533            IFLA_MIN_MTU => MinMtu(
534                parse_u32(payload).context("invalid IFLA_MIN_MTU value")?,
535            ),
536            IFLA_MAX_MTU => MaxMtu(
537                parse_u32(payload).context("invalid IFLA_MAX_MTU value")?,
538            ),
539            IFLA_LINK_NETNSID => NetnsId(
540                parse_i32(payload)
541                    .context("invalid IFLA_LINK_NETNSID value")?,
542            ),
543            IFLA_OPERSTATE => OperState(
544                parse_u8(payload)
545                    .context("invalid IFLA_OPERSTATE value")?
546                    .into(),
547            ),
548            IFLA_MAP => Map(payload.to_vec()),
549            IFLA_STATS => Stats(payload.to_vec()),
550            IFLA_STATS64 => Stats64(payload.to_vec()),
551            IFLA_AF_SPEC => match interface_family {
552                AF_INET | AF_INET6 | AF_UNSPEC => {
553                    let mut nlas = vec![];
554                    let err = "invalid IFLA_AF_SPEC value";
555                    for nla in NlasIterator::new(payload) {
556                        let nla = nla.context(err)?;
557                        nlas.push(
558                            af_spec_inet::AfSpecInet::parse(&nla)
559                                .context(err)?,
560                        );
561                    }
562                    AfSpecInet(nlas)
563                }
564                AF_BRIDGE => {
565                    let mut nlas = vec![];
566                    let err = "invalid IFLA_AF_SPEC value for AF_BRIDGE";
567                    for nla in NlasIterator::new(payload) {
568                        let nla = nla.context(err)?;
569                        nlas.push(
570                            af_spec_bridge::AfSpecBridge::parse(&nla)
571                                .context(err)?,
572                        );
573                    }
574                    AfSpecBridge(nlas)
575                }
576                _ => AfSpecUnknown(payload.to_vec()),
577            },
578            IFLA_LINKINFO => {
579                let err = "invalid IFLA_LINKINFO value";
580                let buf = NlaBuffer::new_checked(payload).context(err)?;
581                Info(VecInfo::parse(&buf).context(err)?.0)
582            }
583            IFLA_XDP => {
584                let err = "invalid IFLA_XDP value";
585                let buf = NlaBuffer::new_checked(payload).context(err)?;
586                Xdp(VecXdp::parse(&buf).context(err)?.0)
587            }
588
589            kind => Other(
590                DefaultNla::parse(buf)
591                    .context(format!("unknown NLA type {kind}"))?,
592            ),
593        })
594    }
595}