netlink_packet_route/tc/
attribute.rs

1// SPDX-License-Identifier: MIT
2
3use netlink_packet_core::{
4    emit_u32, parse_string, parse_u32, parse_u8, DecodeError, DefaultNla,
5    Emitable, ErrorContext, Nla, NlaBuffer, NlasIterator, Parseable,
6    ParseableParametrized,
7};
8
9use super::{
10    TcOption, TcStats, TcStats2, TcStatsBuffer, TcXstats, VecTcOption,
11};
12
13const TCA_KIND: u16 = 1;
14const TCA_OPTIONS: u16 = 2;
15const TCA_STATS: u16 = 3;
16const TCA_XSTATS: u16 = 4;
17const TCA_RATE: u16 = 5;
18const TCA_FCNT: u16 = 6;
19const TCA_STATS2: u16 = 7;
20const TCA_STAB: u16 = 8;
21// const TCA_PAD: u16 = 9;
22const TCA_DUMP_INVISIBLE: u16 = 10;
23const TCA_CHAIN: u16 = 11;
24const TCA_HW_OFFLOAD: u16 = 12;
25// const TCA_INGRESS_BLOCK: u16 = 13; // TODO
26// const TCA_EGRESS_BLOCK: u16 = 14; // TODO
27
28#[derive(Debug, PartialEq, Eq, Clone)]
29#[non_exhaustive]
30pub enum TcAttribute {
31    /// Name of queueing discipline
32    Kind(String),
33    /// Options follow
34    Options(Vec<TcOption>),
35    /// Statistics
36    Stats(TcStats),
37    /// Module-specific statistics
38    Xstats(TcXstats),
39    /// Rate limit
40    Rate(Vec<u8>),
41    Fcnt(Vec<u8>),
42    Stats2(Vec<TcStats2>),
43    Stab(Vec<u8>),
44    Chain(u32),
45    HwOffload(u8),
46    DumpInvisible(bool),
47    Other(DefaultNla),
48}
49
50impl Nla for TcAttribute {
51    fn value_len(&self) -> usize {
52        match *self {
53            Self::Rate(ref bytes)
54            | Self::Fcnt(ref bytes)
55            | Self::Stab(ref bytes) => bytes.len(),
56            Self::Chain(_) => 4,
57            Self::Xstats(ref v) => v.buffer_len(),
58            Self::HwOffload(_) => 1,
59            Self::Stats2(ref v) => v.as_slice().buffer_len(),
60            Self::Stats(ref v) => v.buffer_len(),
61            Self::Kind(ref string) => string.len() + 1,
62            Self::Options(ref opt) => opt.as_slice().buffer_len(),
63            Self::DumpInvisible(_) => 0, // The existence of NLA means true
64            Self::Other(ref attr) => attr.value_len(),
65        }
66    }
67
68    fn emit_value(&self, buffer: &mut [u8]) {
69        match *self {
70            Self::Rate(ref bytes)
71            | Self::Fcnt(ref bytes)
72            | Self::Stab(ref bytes) => buffer.copy_from_slice(bytes.as_slice()),
73            Self::Chain(v) => emit_u32(buffer, v).unwrap(),
74            Self::Xstats(ref v) => v.emit(buffer),
75            Self::HwOffload(ref val) => buffer[0] = *val,
76            Self::Stats2(ref stats) => stats.as_slice().emit(buffer),
77            Self::Stats(ref stats) => stats.emit(buffer),
78            Self::Kind(ref string) => {
79                buffer[..string.len()].copy_from_slice(string.as_bytes());
80                buffer[string.len()] = 0;
81            }
82            Self::Options(ref opt) => opt.as_slice().emit(buffer),
83            Self::DumpInvisible(_) => (),
84            Self::Other(ref attr) => attr.emit_value(buffer),
85        }
86    }
87
88    fn kind(&self) -> u16 {
89        match *self {
90            Self::Kind(_) => TCA_KIND,
91            Self::Options(_) => TCA_OPTIONS,
92            Self::Stats(_) => TCA_STATS,
93            Self::Xstats(_) => TCA_XSTATS,
94            Self::Rate(_) => TCA_RATE,
95            Self::Fcnt(_) => TCA_FCNT,
96            Self::Stats2(_) => TCA_STATS2,
97            Self::Stab(_) => TCA_STAB,
98            Self::Chain(_) => TCA_CHAIN,
99            Self::HwOffload(_) => TCA_HW_OFFLOAD,
100            Self::DumpInvisible(_) => TCA_DUMP_INVISIBLE,
101            Self::Other(ref nla) => nla.kind(),
102        }
103    }
104}
105
106impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized<NlaBuffer<&'a T>, &str>
107    for TcAttribute
108{
109    fn parse_with_param(
110        buf: &NlaBuffer<&'a T>,
111        kind: &str,
112    ) -> Result<Self, DecodeError> {
113        let payload = buf.value();
114        Ok(match buf.kind() {
115            TCA_KIND => TcAttribute::Kind(
116                parse_string(payload).context("invalid TCA_KIND")?,
117            ),
118            TCA_OPTIONS => TcAttribute::Options(
119                VecTcOption::parse_with_param(buf, kind)
120                    .context(format!("Invalid TCA_OPTIONS for kind: {kind}"))?
121                    .0,
122            ),
123            TCA_STATS => TcAttribute::Stats(
124                TcStats::parse(
125                    &TcStatsBuffer::new_checked(payload)
126                        .context("invalid TCA_STATS")?,
127                )
128                .context("failed to parse TCA_STATS")?,
129            ),
130            TCA_XSTATS => TcAttribute::Xstats(
131                TcXstats::parse_with_param(buf, kind)
132                    .context("invalid TCA_XSTATS")?,
133            ),
134            TCA_RATE => TcAttribute::Rate(payload.to_vec()),
135            TCA_FCNT => TcAttribute::Fcnt(payload.to_vec()),
136            TCA_STATS2 => {
137                let mut nlas = vec![];
138                for nla in NlasIterator::new(payload) {
139                    let nla = nla.context("invalid TCA_STATS2")?;
140                    nlas.push(TcStats2::parse_with_param(&nla, kind).context(
141                        format!("failed to parse TCA_STATS2 for kind {kind}"),
142                    )?);
143                }
144                TcAttribute::Stats2(nlas)
145            }
146            TCA_STAB => TcAttribute::Stab(payload.to_vec()),
147            TCA_CHAIN => TcAttribute::Chain(
148                parse_u32(payload).context("failed to parse TCA_CHAIN")?,
149            ),
150            TCA_HW_OFFLOAD => TcAttribute::HwOffload(
151                parse_u8(payload).context("failed to parse TCA_HW_OFFLOAD")?,
152            ),
153            TCA_DUMP_INVISIBLE => TcAttribute::DumpInvisible(true),
154            _ => TcAttribute::Other(
155                DefaultNla::parse(buf).context("failed to parse tc nla")?,
156            ),
157        })
158    }
159}