netlink_packet_route/tc/actions/
message.rs

1// SPDX-License-Identifier: MIT
2
3use anyhow::Context;
4use netlink_packet_utils::nla::{DefaultNla, NlaBuffer};
5use netlink_packet_utils::nla::{Nla, NlasIterator};
6use netlink_packet_utils::{DecodeError, Emitable, Parseable};
7
8use crate::tc::actions::{TcActionMessageBuffer, TcActionMessageHeader};
9use crate::tc::TcAction;
10
11/// Message to describe [tc-actions]
12///
13/// [tc-actions]: https://man7.org/linux/man-pages/man8/tc-actions.8.html
14#[derive(Debug, PartialEq, Eq, Clone, Default)]
15#[non_exhaustive]
16pub struct TcActionMessage {
17    /// Header of the message.
18    pub header: TcActionMessageHeader,
19    /// Attributes of the message.
20    pub attributes: Vec<TcActionMessageAttribute>,
21}
22
23const TCA_ACT_FLAG_LARGE_DUMP_ON: u32 = 1 << 0;
24const TCA_ACT_FLAG_TERSE_DUMP: u32 = 1 << 1;
25
26bitflags! {
27    /// Flags to configure action dumps (list operations).
28    #[derive(Debug, PartialEq, Eq, Clone, Copy, Default, PartialOrd, Ord, Hash)]
29    #[non_exhaustive]
30    pub struct TcActionMessageFlags: u32 {
31        /// If set, this flag enables more than `TCA_ACT_MAX_PRIO` actions in a single
32        /// actions listing operation.
33        const LargeDump = TCA_ACT_FLAG_LARGE_DUMP_ON;
34        /// If set, this flag restricts an action dump to only include essential
35        /// details.
36        const TerseDump = TCA_ACT_FLAG_TERSE_DUMP;
37        const _ = !0;
38    }
39}
40
41/// [`TcActionMessageFlagsWithSelector`] sets the [`TcActionMessageFlags`] which
42/// are to be included in an operation, based on the accompanying [`flags`] and
43/// [`selector`] fields.
44///
45/// [`flags`]: #structfield.flags
46/// [`selector`]: #structfield.selector
47#[derive(Debug, PartialEq, Eq, Clone, Copy, Default, PartialOrd, Ord, Hash)]
48pub struct TcActionMessageFlagsWithSelector {
49    /// A bitmask of [`TcActionMessageFlags`] to be associated with an
50    /// operation.
51    pub flags: TcActionMessageFlags,
52    /// A bitmask to determine which flags are to be included in an operation.
53    ///
54    /// Any flags which are set in the [`flags`] field but which are not set in
55    /// the [`selector`] field will be ignored.
56    ///
57    /// [`flags`]: #structfield.flags
58    /// [`selector`]: #structfield.selector
59    pub selector: TcActionMessageFlags,
60}
61
62impl Nla for TcActionMessageFlagsWithSelector {
63    fn value_len(&self) -> usize {
64        8
65    }
66
67    fn kind(&self) -> u16 {
68        TCA_ROOT_FLAGS
69    }
70
71    fn emit_value(&self, buffer: &mut [u8]) {
72        buffer[..4].copy_from_slice(&self.flags.bits().to_ne_bytes());
73        buffer[4..8].copy_from_slice(&self.selector.bits().to_ne_bytes());
74    }
75}
76
77impl TcActionMessageFlagsWithSelector {
78    /// Create a new [`TcActionMessageFlagsWithSelector`] with the given
79    /// [`flags`].
80    /// The [`selector`] field is set to the same value as [`flags`] (i.e., none
81    /// of the [`flags`] will be ignored).
82    ///
83    /// [`flags`]: #structfield.flags
84    /// [`selector`]: #structfield.selector
85    #[must_use]
86    pub fn new(flags: TcActionMessageFlags) -> Self {
87        Self {
88            flags,
89            selector: flags,
90        }
91    }
92
93    /// Create a new [`TcActionMessageFlagsWithSelector`] with the given
94    /// [`flags`] and [`selector`].
95    ///
96    /// [`flags`]: #structfield.flags
97    /// [`selector`]: #structfield.selector
98    #[must_use]
99    pub fn new_with_selector(
100        flags: TcActionMessageFlags,
101        selector: TcActionMessageFlags,
102    ) -> Self {
103        Self { flags, selector }
104    }
105}
106
107impl<'a, T: AsRef<[u8]> + 'a + ?Sized> Parseable<NlaBuffer<&'a T>>
108    for TcActionMessageFlagsWithSelector
109{
110    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
111        let value = buf.value();
112        if value.len() != 8 {
113            return Err(DecodeError::from("invalid length"));
114        }
115        let flags = TcActionMessageFlags::from_bits(u32::from_ne_bytes(
116            value[0..4].try_into().context("invalid length")?,
117        ))
118        .ok_or_else(|| DecodeError::from("invalid flags"))?;
119        let selector = TcActionMessageFlags::from_bits(u32::from_ne_bytes(
120            value[4..].try_into().context("invalid length")?,
121        ))
122        .ok_or_else(|| DecodeError::from("invalid flags selector"))?;
123        Ok(Self::new_with_selector(flags, selector))
124    }
125}
126
127const TCA_ACT_TAB: u16 = 1;
128const TCA_ROOT_FLAGS: u16 = 2;
129const TCA_ROOT_COUNT: u16 = 3;
130const TCA_ROOT_TIME_DELTA: u16 = 4;
131const TCA_ROOT_EXT_WARN_MSG: u16 = 5;
132
133/// This enum is used to represent the different types of attributes that can be
134/// part of a [`TcActionMessage`].
135///
136/// This enum is non-exhaustive, additional variants may be added in the future.
137#[derive(Debug, PartialEq, Eq, Clone)]
138#[non_exhaustive]
139pub enum TcActionMessageAttribute {
140    /// Collection of `TcActions`.
141    Actions(Vec<TcAction>),
142    /// Flags to configure action dumps (list operations).
143    Flags(TcActionMessageFlagsWithSelector),
144    /// Number of actions being dumped.
145    RootCount(u32),
146    /// Time delta.
147    RootTimeDelta(u32),
148    /// Extended warning message.
149    RootExtWarnMsg(String),
150    /// Other attributes unknown at the time of writing.
151    Other(DefaultNla),
152}
153
154impl<'a, T: AsRef<[u8]> + 'a + ?Sized> Parseable<NlaBuffer<&'a T>>
155    for TcActionMessageAttribute
156{
157    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
158        Ok(match buf.kind() {
159            TCA_ACT_TAB => {
160                let actions = NlasIterator::new(buf.value())
161                    .map(|nla| TcAction::parse(&nla?))
162                    .collect::<Result<Vec<_>, _>>()?;
163                Self::Actions(actions)
164            }
165            TCA_ROOT_FLAGS => {
166                Self::Flags(TcActionMessageFlagsWithSelector::parse(buf)?)
167            }
168            TCA_ROOT_COUNT => {
169                let count = u32::from_ne_bytes(
170                    buf.value().try_into().context("invalid length")?,
171                );
172                Self::RootCount(count)
173            }
174            TCA_ROOT_TIME_DELTA => {
175                let delta = u32::from_be_bytes(
176                    buf.value().try_into().context("invalid length")?,
177                );
178                Self::RootTimeDelta(delta)
179            }
180            TCA_ROOT_EXT_WARN_MSG => {
181                let msg = String::from_utf8(buf.value().to_vec())
182                    .context("invalid utf8")?;
183                Self::RootExtWarnMsg(msg)
184            }
185            _ => Self::Other(DefaultNla::parse(buf)?),
186        })
187    }
188}
189
190impl Nla for TcActionMessageAttribute {
191    fn value_len(&self) -> usize {
192        match self {
193            Self::Actions(actions) => actions.as_slice().buffer_len(),
194            Self::Flags(_) => 8,
195            Self::RootCount(_) => 4,
196            Self::RootTimeDelta(_) => 4,
197            Self::RootExtWarnMsg(msg) => msg.len(),
198            Self::Other(nla) => nla.value_len(),
199        }
200    }
201
202    fn kind(&self) -> u16 {
203        match self {
204            Self::Actions(_) => TCA_ACT_TAB,
205            Self::Flags(_) => TCA_ROOT_FLAGS,
206            Self::RootCount(_) => TCA_ROOT_COUNT,
207            Self::RootTimeDelta(_) => TCA_ROOT_TIME_DELTA,
208            Self::RootExtWarnMsg(_) => TCA_ROOT_EXT_WARN_MSG,
209            Self::Other(nla) => nla.kind(),
210        }
211    }
212
213    fn emit_value(&self, buffer: &mut [u8]) {
214        match self {
215            Self::Actions(actions) => actions.as_slice().emit(buffer),
216            Self::Flags(flags) => {
217                flags.emit_value(buffer);
218            }
219            Self::RootCount(count) => {
220                buffer.copy_from_slice(&count.to_ne_bytes());
221            }
222            Self::RootTimeDelta(delta) => {
223                buffer.copy_from_slice(&delta.to_be_bytes());
224            }
225            Self::RootExtWarnMsg(msg) => buffer.copy_from_slice(msg.as_bytes()),
226            Self::Other(nla) => nla.emit_value(buffer),
227        }
228    }
229}
230
231impl<'a, T: AsRef<[u8]> + 'a + ?Sized> Parseable<TcActionMessageBuffer<&'a T>>
232    for TcActionMessage
233{
234    fn parse(buf: &TcActionMessageBuffer<&'a T>) -> Result<Self, DecodeError> {
235        let attrs: Result<Vec<_>, DecodeError> = buf
236            .attributes()
237            .map(|attr| TcActionMessageAttribute::parse(&attr?))
238            .collect::<Result<Vec<_>, _>>();
239
240        Ok(Self {
241            header: TcActionMessageHeader::parse(buf)
242                .context("failed to parse tc message header")?,
243            attributes: attrs?,
244        })
245    }
246}
247
248impl Emitable for TcActionMessage {
249    fn buffer_len(&self) -> usize {
250        self.header.buffer_len() + self.attributes.as_slice().buffer_len()
251    }
252
253    fn emit(&self, buffer: &mut [u8]) {
254        self.header.emit(buffer);
255        self.attributes
256            .as_slice()
257            .emit(&mut buffer[self.header.buffer_len()..]);
258    }
259}