netlink_packet_route/tc/
options.rs

1// SPDX-License-Identifier: MIT
2
3use netlink_packet_core::{
4    DecodeError, DefaultNla, ErrorContext, Nla, NlaBuffer, NlasIterator,
5    Parseable, ParseableParametrized,
6};
7
8use super::{
9    TcFilterFlower, TcFilterFlowerOption, TcFilterMatchAll,
10    TcFilterMatchAllOption, TcFilterU32, TcFilterU32Option, TcQdiscFqCodel,
11    TcQdiscFqCodelOption, TcQdiscIngress, TcQdiscIngressOption,
12};
13
14#[derive(Debug, PartialEq, Eq, Clone)]
15#[non_exhaustive]
16pub enum TcOption {
17    FqCodel(TcQdiscFqCodelOption),
18    // Qdisc specific options
19    Ingress(TcQdiscIngressOption),
20    // Filter specific options
21    Flower(TcFilterFlowerOption),
22    U32(TcFilterU32Option),
23    // matchall options
24    MatchAll(TcFilterMatchAllOption),
25    // Other options
26    Other(DefaultNla),
27}
28
29impl Nla for TcOption {
30    fn value_len(&self) -> usize {
31        match self {
32            Self::FqCodel(u) => u.value_len(),
33            Self::Ingress(u) => u.value_len(),
34            Self::U32(u) => u.value_len(),
35            Self::Flower(u) => u.value_len(),
36            Self::MatchAll(m) => m.value_len(),
37            Self::Other(o) => o.value_len(),
38        }
39    }
40
41    fn emit_value(&self, buffer: &mut [u8]) {
42        match self {
43            Self::FqCodel(u) => u.emit_value(buffer),
44            Self::Ingress(u) => u.emit_value(buffer),
45            Self::Flower(u) => u.emit_value(buffer),
46            Self::U32(u) => u.emit_value(buffer),
47            Self::MatchAll(m) => m.emit_value(buffer),
48            Self::Other(o) => o.emit_value(buffer),
49        }
50    }
51
52    fn kind(&self) -> u16 {
53        match self {
54            Self::FqCodel(u) => u.kind(),
55            Self::Ingress(u) => u.kind(),
56            Self::Flower(u) => u.kind(),
57            Self::U32(u) => u.kind(),
58            Self::MatchAll(m) => m.kind(),
59            Self::Other(o) => o.kind(),
60        }
61    }
62}
63
64impl<'a, T> ParseableParametrized<NlaBuffer<&'a T>, &str> for TcOption
65where
66    T: AsRef<[u8]> + ?Sized,
67{
68    fn parse_with_param(
69        buf: &NlaBuffer<&'a T>,
70        kind: &str,
71    ) -> Result<Self, DecodeError> {
72        Ok(match kind {
73            TcQdiscIngress::KIND => {
74                Self::Ingress(TcQdiscIngressOption::parse(buf).context(
75                    "failed to parse ingress TCA_OPTIONS attributes",
76                )?)
77            }
78            TcFilterFlower::KIND => Self::Flower(
79                TcFilterFlowerOption::parse(buf)
80                    .context("failed to parse flower TCA_OPTIONS attributes")?,
81            ),
82            TcQdiscFqCodel::KIND => {
83                Self::FqCodel(TcQdiscFqCodelOption::parse(buf).context(
84                    "failed to parse fq_codel TCA_OPTIONS attributes",
85                )?)
86            }
87            TcFilterU32::KIND => Self::U32(
88                TcFilterU32Option::parse(buf)
89                    .context("failed to parse u32 TCA_OPTIONS attributes")?,
90            ),
91            TcFilterMatchAll::KIND => {
92                Self::MatchAll(TcFilterMatchAllOption::parse(buf).context(
93                    "failed to parse matchall TCA_OPTIONS attributes",
94                )?)
95            }
96            _ => Self::Other(DefaultNla::parse(buf)?),
97        })
98    }
99}
100
101pub(crate) struct VecTcOption(pub(crate) Vec<TcOption>);
102
103impl<'a, T> ParseableParametrized<NlaBuffer<&'a T>, &str> for VecTcOption
104where
105    T: AsRef<[u8]> + ?Sized,
106{
107    fn parse_with_param(
108        buf: &NlaBuffer<&'a T>,
109        kind: &str,
110    ) -> Result<VecTcOption, DecodeError> {
111        Ok(match kind {
112            TcFilterU32::KIND
113            | TcFilterMatchAll::KIND
114            | TcFilterFlower::KIND
115            | TcQdiscIngress::KIND
116            | TcQdiscFqCodel::KIND => {
117                let mut nlas = vec![];
118                for nla in NlasIterator::new(buf.value()) {
119                    let nla = nla.context(format!(
120                        "Invalid TCA_OPTIONS for kind: {kind}",
121                    ))?;
122                    nlas.push(
123                        TcOption::parse_with_param(&nla, kind).context(
124                            format!(
125                                "Failed to parse TCA_OPTIONS for kind: {kind}",
126                            ),
127                        )?,
128                    )
129                }
130                Self(nlas)
131            }
132            // Kernel has no guide line or code indicate the scheduler
133            // should place a nla_nest here. The `sfq` qdisc kernel code is
134            // using single NLA instead nested ones. Hence we are storing
135            // unknown Nla as Vec with single item.
136            _ => Self(vec![TcOption::Other(DefaultNla::parse(buf)?)]),
137        })
138    }
139}