netlink_packet_route/tc/
options.rs

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