netlink_packet_route/tc/
options.rs

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