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