netlink_packet_route/tc/actions/
message.rs

1// SPDX-License-Identifier: MIT
2
3use anyhow::Context;
4use netlink_packet_utils::nla::{DefaultNla, Nla, NlaBuffer, NlasIterator};
5use netlink_packet_utils::{DecodeError, Emitable, Parseable};
6use std::convert::TryInto as _;
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 { flags, selector: flags }
88    }
89
90    /// Create a new [`TcActionMessageFlagsWithSelector`] with the given
91    /// [`flags`] and [`selector`].
92    ///
93    /// [`flags`]: #structfield.flags
94    /// [`selector`]: #structfield.selector
95    #[must_use]
96    pub fn new_with_selector(flags: TcActionMessageFlags, selector: TcActionMessageFlags) -> Self {
97        Self { flags, selector }
98    }
99}
100
101impl<'a, T: AsRef<[u8]> + 'a + ?Sized> Parseable<NlaBuffer<&'a T>>
102    for TcActionMessageFlagsWithSelector
103{
104    type Error = DecodeError;
105    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
106        let value = buf.value();
107        if value.len() != 8 {
108            return Err(DecodeError::from("invalid length"));
109        }
110        let flags = TcActionMessageFlags::from_bits(u32::from_ne_bytes(
111            value[0..4].try_into().context("invalid length")?,
112        ))
113        .ok_or_else(|| DecodeError::from("invalid flags"))?;
114        let selector = TcActionMessageFlags::from_bits(u32::from_ne_bytes(
115            value[4..].try_into().context("invalid length")?,
116        ))
117        .ok_or_else(|| DecodeError::from("invalid flags selector"))?;
118        Ok(Self::new_with_selector(flags, selector))
119    }
120}
121
122const TCA_ACT_TAB: u16 = 1;
123const TCA_ROOT_FLAGS: u16 = 2;
124const TCA_ROOT_COUNT: u16 = 3;
125const TCA_ROOT_TIME_DELTA: u16 = 4;
126const TCA_ROOT_EXT_WARN_MSG: u16 = 5;
127
128/// This enum is used to represent the different types of attributes that can be
129/// part of a [`TcActionMessage`].
130///
131/// This enum is non-exhaustive, additional variants may be added in the future.
132#[derive(Debug, PartialEq, Eq, Clone)]
133#[non_exhaustive]
134pub enum TcActionMessageAttribute {
135    /// Collection of `TcActions`.
136    Actions(Vec<TcAction>),
137    /// Flags to configure action dumps (list operations).
138    Flags(TcActionMessageFlagsWithSelector),
139    /// Number of actions being dumped.
140    RootCount(u32),
141    /// Time delta.
142    RootTimeDelta(u32),
143    /// Extended warning message.
144    RootExtWarnMsg(String),
145    /// Other attributes unknown at the time of writing.
146    Other(DefaultNla),
147}
148
149impl<'a, T: AsRef<[u8]> + 'a + ?Sized> Parseable<NlaBuffer<&'a T>> for TcActionMessageAttribute {
150    type Error = DecodeError;
151    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
152        Ok(match buf.kind() {
153            TCA_ACT_TAB => {
154                let actions = NlasIterator::new(buf.value())
155                    .map(|nla| TcAction::parse(&nla?))
156                    .collect::<Result<Vec<_>, _>>()
157                    .map_err(|err| DecodeError::Other(err.into()))?;
158                Self::Actions(actions)
159            }
160            TCA_ROOT_FLAGS => Self::Flags(TcActionMessageFlagsWithSelector::parse(buf)?),
161            TCA_ROOT_COUNT => {
162                let count = u32::from_ne_bytes(buf.value().try_into().context("invalid length")?);
163                Self::RootCount(count)
164            }
165            TCA_ROOT_TIME_DELTA => {
166                let delta = u32::from_be_bytes(buf.value().try_into().context("invalid length")?);
167                Self::RootTimeDelta(delta)
168            }
169            TCA_ROOT_EXT_WARN_MSG => {
170                let msg = String::from_utf8(buf.value().to_vec()).context("invalid utf8")?;
171                Self::RootExtWarnMsg(msg)
172            }
173            _ => Self::Other(DefaultNla::parse(buf)?),
174        })
175    }
176}
177
178impl Nla for TcActionMessageAttribute {
179    fn value_len(&self) -> usize {
180        match self {
181            Self::Actions(actions) => actions.as_slice().buffer_len(),
182            Self::Flags(_) => 8,
183            Self::RootCount(_) => 4,
184            Self::RootTimeDelta(_) => 4,
185            Self::RootExtWarnMsg(msg) => msg.len(),
186            Self::Other(nla) => nla.value_len(),
187        }
188    }
189
190    fn kind(&self) -> u16 {
191        match self {
192            Self::Actions(_) => TCA_ACT_TAB,
193            Self::Flags(_) => TCA_ROOT_FLAGS,
194            Self::RootCount(_) => TCA_ROOT_COUNT,
195            Self::RootTimeDelta(_) => TCA_ROOT_TIME_DELTA,
196            Self::RootExtWarnMsg(_) => TCA_ROOT_EXT_WARN_MSG,
197            Self::Other(nla) => nla.kind(),
198        }
199    }
200
201    fn emit_value(&self, buffer: &mut [u8]) {
202        match self {
203            Self::Actions(actions) => actions.as_slice().emit(buffer),
204            Self::Flags(flags) => {
205                flags.emit_value(buffer);
206            }
207            Self::RootCount(count) => {
208                buffer.copy_from_slice(&count.to_ne_bytes());
209            }
210            Self::RootTimeDelta(delta) => {
211                buffer.copy_from_slice(&delta.to_be_bytes());
212            }
213            Self::RootExtWarnMsg(msg) => buffer.copy_from_slice(msg.as_bytes()),
214            Self::Other(nla) => nla.emit_value(buffer),
215        }
216    }
217}
218
219impl<'a, T: AsRef<[u8]> + 'a + ?Sized> Parseable<TcActionMessageBuffer<&'a T>> for TcActionMessage {
220    type Error = DecodeError;
221    fn parse(buf: &TcActionMessageBuffer<&'a T>) -> Result<Self, DecodeError> {
222        let attrs: Result<Vec<_>, DecodeError> = buf
223            .attributes()
224            .map(|attr| TcActionMessageAttribute::parse(&attr?))
225            .collect::<Result<Vec<_>, _>>();
226
227        Ok(Self {
228            header: TcActionMessageHeader::parse(buf)
229                .context("failed to parse tc message header")?,
230            attributes: attrs?,
231        })
232    }
233}
234
235impl Emitable for TcActionMessage {
236    fn buffer_len(&self) -> usize {
237        self.header.buffer_len() + self.attributes.as_slice().buffer_len()
238    }
239
240    fn emit(&self, buffer: &mut [u8]) {
241        self.header.emit(buffer);
242        self.attributes.as_slice().emit(&mut buffer[self.header.buffer_len()..]);
243    }
244}