netlink_packet_route/tc/actions/
action.rs

1// SPDX-License-Identifier: MIT
2
3use anyhow::Context;
4use byteorder::{ByteOrder, NativeEndian};
5use netlink_packet_utils::nla::NLA_F_NESTED;
6use netlink_packet_utils::{
7    nla::{DefaultNla, Nla, NlaBuffer, NlasIterator},
8    parsers::{parse_string, parse_u32},
9    traits::{Emitable, Parseable, ParseableParametrized},
10    DecodeError,
11};
12
13use crate::tc::TcStats2;
14
15use super::{
16    TcActionMirror, TcActionMirrorOption, TcActionNat, TcActionNatOption,
17    TcActionTunnelKey, TcActionTunnelKeyOption,
18};
19
20/// TODO: determine when and why to use this as opposed to the buffer's `kind`.
21const TCA_ACT_TAB: u16 = 1;
22
23/// [`TcAction`] is a netlink message attribute that describes a [tc-action].
24///
25/// [tc-action]: https://man7.org/linux/man-pages/man8/tc-actions.8.html
26#[derive(Debug, PartialEq, Eq, Clone)]
27#[non_exhaustive]
28pub struct TcAction {
29    /// Table id.
30    /// Corresponds to the [`Kind`] of the action.
31    ///
32    /// [`Kind`]: crate::tc::TcActionAttribute::Kind
33    pub tab: u16,
34    /// Attributes of the action.
35    pub attributes: Vec<TcActionAttribute>,
36}
37
38impl Default for TcAction {
39    fn default() -> Self {
40        Self {
41            tab: TCA_ACT_TAB,
42            attributes: Vec::new(),
43        }
44    }
45}
46
47impl Nla for TcAction {
48    fn value_len(&self) -> usize {
49        self.attributes.as_slice().buffer_len()
50    }
51
52    fn emit_value(&self, buffer: &mut [u8]) {
53        self.attributes.as_slice().emit(buffer);
54    }
55
56    fn kind(&self) -> u16 {
57        self.tab
58    }
59}
60
61impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for TcAction {
62    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
63        // We need to find the `Kind` attribute before we can parse the others,
64        // as kind is used in calls to parse_with_param for the other
65        // attributes.
66        // Messages of this type which do not specify [`Kind`], or which specify
67        // `Kind` more than once are malformed and should be rejected.
68        // We cannot ensure that `Kind` will be the first attribute in the
69        // `attributes` `Vec` (although it usually is).
70        // As a result, we need to determine `Kind` first, then parse the rest
71        // of the attributes.
72        let kind = match NlasIterator::new(buf.value())
73            .filter_map(|nla| {
74                let nla = match nla {
75                    Ok(nla) => nla,
76                    Err(e) => {
77                        return Some(
78                            Err(e).context("failed to parse action nla"),
79                        )
80                    }
81                };
82                match nla.kind() {
83                    TCA_ACT_KIND => Some(
84                        parse_string(nla.value())
85                            .context("failed to parse TCA_ACT_KIND"),
86                    ),
87                    _ => None,
88                }
89            })
90            .collect::<Result<Vec<_>, _>>()
91        {
92            Ok(kinds) => {
93                if kinds.is_empty() {
94                    return Err(DecodeError::from("Missing TCA_ACT_KIND"));
95                }
96                if kinds.len() > 1 {
97                    return Err(DecodeError::from("Duplicate TCA_ACT_KIND"));
98                }
99                kinds[0].clone()
100            }
101            Err(e) => return Err(DecodeError::from(e.to_string())),
102        };
103
104        let attributes = NlasIterator::new(buf.value())
105            .map(|nla| {
106                TcActionAttribute::parse_with_param(&nla?, kind.as_str())
107            })
108            .collect::<Result<Vec<_>, _>>()?;
109
110        Ok(Self {
111            tab: buf.kind(),
112            attributes,
113        })
114    }
115}
116
117const TCA_ACT_KIND: u16 = 1;
118const TCA_ACT_OPTIONS: u16 = 2;
119const TCA_ACT_INDEX: u16 = 3;
120const TCA_ACT_STATS: u16 = 4;
121// const TCA_ACT_PAD: u16 = 5;
122const TCA_ACT_COOKIE: u16 = 6;
123// const TCA_ACT_FLAGS: u16 = 7;
124// const TCA_ACT_HW_STATS: u16 = 8;
125// const TCA_ACT_USED_HW_STATS: u16 = 9;
126const TCA_ACT_IN_HW_COUNT: u16 = 10;
127
128/// Attributes of a traffic control action.
129#[derive(Debug, PartialEq, Eq, Clone)]
130#[non_exhaustive]
131pub enum TcActionAttribute {
132    /// The [`Kind`] (general type or class) of the action (e.g. "mirred",
133    /// "nat").
134    ///
135    /// [`Kind`]: #variant.Kind
136    Kind(String),
137    /// Parameters of the action.
138    Options(Vec<TcActionOption>),
139    /// Index of the action.
140    ///
141    /// This is used to identify the action in the kernel.
142    /// Each action [`Kind`] has a unique table of actions.
143    /// That is, each action [`Kind`] has its own set of [`Index`] values.
144    ///
145    /// If [`Index`] is zero on action creation,
146    /// the kernel will assign a unique index to the new action.
147    /// The combination of [`Kind`] and [`Index`] can then be used to identify
148    /// and interact with the action in the future.
149    ///
150    /// For example, one action can be used by multiple different filters by
151    /// referencing the action's [`Index`] when creating that filter.
152    /// Such multiply referenced actions will aggregate their statistics.
153    ///
154    /// The kernel will reject attempts to delete an action if it is in use by
155    /// a filter.
156    /// Remove all referencing filters before deleting the action.
157    ///
158    /// [`Kind`]: #variant.Kind
159    /// [`Index`]: #variant.Index
160    Index(u32),
161    /// Statistics about the action (e.g., number of bytes and or packets
162    /// processed).
163    Stats(Vec<TcStats2>),
164    /// [`Cookie`] is an attribute which _is not interpreted by the kernel at
165    /// all_ and may be used to store up to 16 bytes of arbitrary data on
166    /// an action in the kernel.
167    /// Userspace processes may then use this data to store additional
168    /// information about the action or to correlate actions with other
169    /// data.
170    ///
171    /// [`Cookie`]: #variant.Cookie
172    Cookie(Vec<u8>),
173    /// Number of times the action has been installed in hardware.
174    InHwCount(u32),
175    /// Other attributes unknown at the time of writing or not yet supported by
176    /// this library.
177    Other(DefaultNla),
178}
179
180impl Nla for TcActionAttribute {
181    fn value_len(&self) -> usize {
182        match self {
183            Self::Cookie(bytes) => bytes.len(),
184            Self::Kind(k) => k.len() + 1,
185            Self::Options(opt) => opt.as_slice().buffer_len(),
186            Self::Index(_) | Self::InHwCount(_) => 4,
187            Self::Stats(s) => s.as_slice().buffer_len(),
188            Self::Other(attr) => attr.value_len(),
189        }
190    }
191    fn emit_value(&self, buffer: &mut [u8]) {
192        match self {
193            Self::Cookie(bytes) => buffer.copy_from_slice(bytes.as_slice()),
194            Self::Kind(string) => {
195                buffer[..string.len()].copy_from_slice(string.as_bytes());
196                buffer[string.len()] = 0;
197            }
198            Self::Options(opt) => opt.as_slice().emit(buffer),
199            Self::Index(value) | Self::InHwCount(value) => {
200                NativeEndian::write_u32(buffer, *value);
201            }
202            Self::Stats(s) => s.as_slice().emit(buffer),
203            Self::Other(attr) => attr.emit_value(buffer),
204        }
205    }
206    fn kind(&self) -> u16 {
207        match self {
208            Self::Kind(_) => TCA_ACT_KIND,
209            Self::Options(_) => TCA_ACT_OPTIONS | NLA_F_NESTED,
210            Self::Index(_) => TCA_ACT_INDEX,
211            Self::Stats(_) => TCA_ACT_STATS,
212            Self::Cookie(_) => TCA_ACT_COOKIE,
213            Self::InHwCount(_) => TCA_ACT_IN_HW_COUNT,
214            Self::Other(nla) => nla.kind(),
215        }
216    }
217}
218
219impl<'a, T, P> ParseableParametrized<NlaBuffer<&'a T>, P> for TcActionAttribute
220where
221    T: AsRef<[u8]> + ?Sized,
222    P: AsRef<str>,
223{
224    fn parse_with_param(
225        buf: &NlaBuffer<&'a T>,
226        kind: P,
227    ) -> Result<Self, DecodeError> {
228        Ok(match buf.kind() {
229            TCA_ACT_KIND => {
230                let buf_value = buf.value();
231                TcActionAttribute::Kind(
232                    parse_string(buf_value)
233                        .context("failed to parse TCA_ACT_KIND")?,
234                )
235            }
236            TCA_ACT_OPTIONS => TcActionAttribute::Options(
237                NlasIterator::new(buf.value())
238                    .map(|nla| {
239                        let nla = nla.context("invalid TCA_ACT_OPTIONS")?;
240                        TcActionOption::parse_with_param(&nla, kind.as_ref())
241                            .context("failed to parse TCA_ACT_OPTIONS")
242                    })
243                    .collect::<Result<Vec<_>, _>>()?,
244            ),
245            TCA_ACT_INDEX => TcActionAttribute::Index(
246                parse_u32(buf.value())
247                    .context("failed to parse TCA_ACT_INDEX")?,
248            ),
249            TCA_ACT_STATS => TcActionAttribute::Stats(
250                NlasIterator::new(buf.value())
251                    .map(|nla| {
252                        let nla = nla.context("invalid TCA_ACT_STATS")?;
253                        TcStats2::parse_with_param(&nla, kind.as_ref())
254                            .context("failed to parse TCA_ACT_STATS")
255                    })
256                    .collect::<Result<Vec<_>, _>>()?,
257            ),
258            TCA_ACT_COOKIE => TcActionAttribute::Cookie(buf.value().to_vec()),
259            TCA_ACT_IN_HW_COUNT => TcActionAttribute::InHwCount(
260                parse_u32(buf.value())
261                    .context("failed to parse TCA_ACT_IN_HW_COUNT")?,
262            ),
263            _ => TcActionAttribute::Other(
264                DefaultNla::parse(buf).context("failed to parse action nla")?,
265            ),
266        })
267    }
268}
269
270/// [`TcActionOption`] is a netlink message attribute that describes an option
271/// of a [tc-actions] action.
272///
273/// This enum is non-exhaustive as new action types may be added to the kernel
274/// at any time.
275/// Only a small subset of possible actions are currently supported.
276///
277/// [tc-actions]: https://man7.org/linux/man-pages/man8/tc-actions.8.html
278#[derive(Debug, PartialEq, Eq, Clone)]
279#[non_exhaustive]
280pub enum TcActionOption {
281    /// Mirror options.
282    ///
283    /// These options can be used to mirror (copy) or redirect frames / packets
284    /// to another network interface.
285    Mirror(TcActionMirrorOption),
286    /// NAT options.
287    ///
288    /// These options type can be used to perform network address translation.
289    Nat(TcActionNatOption),
290    /// Tunnel key options.
291    ///
292    /// These options type can be used to assign encapsulation properties to
293    /// the packet.
294    TunnelKey(TcActionTunnelKeyOption),
295    /// Other action types not yet supported by this library.
296    Other(DefaultNla),
297}
298
299impl Nla for TcActionOption {
300    fn value_len(&self) -> usize {
301        match self {
302            Self::Mirror(nla) => nla.value_len(),
303            Self::Nat(nla) => nla.value_len(),
304            Self::TunnelKey(nla) => nla.value_len(),
305            Self::Other(nla) => nla.value_len(),
306        }
307    }
308
309    fn emit_value(&self, buffer: &mut [u8]) {
310        match self {
311            Self::Mirror(nla) => nla.emit_value(buffer),
312            Self::Nat(nla) => nla.emit_value(buffer),
313            Self::TunnelKey(nla) => nla.emit_value(buffer),
314            Self::Other(nla) => nla.emit_value(buffer),
315        }
316    }
317
318    fn kind(&self) -> u16 {
319        match self {
320            Self::Mirror(nla) => nla.kind(),
321            Self::Nat(nla) => nla.kind(),
322            Self::TunnelKey(nla) => nla.kind(),
323            Self::Other(nla) => nla.kind(),
324        }
325    }
326}
327
328impl<'a, T, S> ParseableParametrized<NlaBuffer<&'a T>, S> for TcActionOption
329where
330    T: AsRef<[u8]> + ?Sized,
331    S: AsRef<str>,
332{
333    fn parse_with_param(
334        buf: &NlaBuffer<&'a T>,
335        kind: S,
336    ) -> Result<Self, DecodeError> {
337        Ok(match kind.as_ref() {
338            TcActionMirror::KIND => Self::Mirror(
339                TcActionMirrorOption::parse(buf)
340                    .context("failed to parse mirror action")?,
341            ),
342            TcActionNat::KIND => Self::Nat(
343                TcActionNatOption::parse(buf)
344                    .context("failed to parse nat action")?,
345            ),
346            TcActionTunnelKey::KIND => Self::TunnelKey(
347                TcActionTunnelKeyOption::parse(buf)
348                    .context("failed to parse tunnel_key action")?,
349            ),
350            _ => Self::Other(
351                DefaultNla::parse(buf)
352                    .context("failed to parse action options")?,
353            ),
354        })
355    }
356}
357
358/// Generic traffic control action parameters.
359///
360/// This structure is used to describe attributes common to all traffic control
361/// actions.
362///
363/// See [`#define tc_gen` in `linux/pkt_cls.h`][`tc_gen`].
364///
365/// [`tc_gen`]: https://elixir.bootlin.com/linux/v6.8.9/source/include/uapi/linux/pkt_cls.h#L179
366#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
367#[non_exhaustive]
368pub struct TcActionGeneric {
369    /// The [`index`] of the action is a unique identifier used to track
370    /// actions installed in the kernel.
371    ///
372    /// Each action type (e.g. [`mirror`] or [`nat`]) has its own independent
373    /// [`index`] space.
374    /// If you assign the [`index`] field to `0` when creating an action, the
375    /// kernel will assign a unique [`index`] to the new action.
376    ///
377    /// [`mirror`]: struct.TcActionMirror.html
378    /// [`nat`]: struct.TcActionNat.html
379    /// [`index`]: #structfield.index
380    pub index: u32,
381    /// NOTE: I cannot find any documentation on this field nor any place
382    /// where it is used in iproute2 or the Linux kernel.
383    /// The [`capab`] field is part of the [`#define tc_gen`] in the kernel,
384    /// and that `#define` is used in many places,
385    /// but I don't see any place using the [`capab`] field in any way.
386    /// I may be looking in the wrong place or missing something.
387    ///
388    /// [`#define tc_gen`]: https://elixir.bootlin.com/linux/v6.8.9/source/include/uapi/linux/pkt_cls.h#L179
389    /// [`capab`]: #structfield.capab
390    pub capab: u32,
391    /// Action type.
392    pub action: TcActionType,
393    /// Reference count of this action.
394    ///
395    /// This refers to the number of times this action is referenced within the
396    /// kernel.
397    /// Actions are cleaned up (deleted) when [`refcnt`] reaches 0.
398    ///
399    /// If you create an action on its own (i.e., not associated with a
400    /// filter), the [`refcnt`] will be 1.
401    /// If that action is then associated with a filter, the [`refcnt`] will be
402    /// 2.
403    /// If you then delete that filter, the [`refcnt`] will be 1 and the action
404    /// will remain until you explicitly delete it (which is only possible
405    /// when the [`refcnt`] is 1 and the [`bindcnt`] is 0).
406    ///
407    /// If you were to create an action indirectly (e.g., as part of creating a
408    /// filter) then the [`refcnt`] will still be 1 (along with the
409    /// [`bindcnt`]).
410    /// If you then create another filter that references the same action, the
411    /// [`refcnt`] will be 2 (along with the [`bindcnt`]).
412    ///
413    /// If you then deleted both of those actions,
414    /// the [`refcnt`] would be 0 and the action would be removed from the
415    /// kernel.
416    ///
417    /// [`refcnt`]: #structfield.refcnt
418    /// [`bindcnt`]: #structfield.bindcnt
419    pub refcnt: i32,
420    /// Bind count of this action.
421    ///
422    /// The number of filters that reference (bind to) this action.
423    pub bindcnt: i32,
424}
425
426impl TcActionGeneric {
427    pub(crate) const BUF_LEN: usize = 20;
428}
429
430buffer!(TcActionGenericBuffer(TcActionGeneric::BUF_LEN) {
431    index: (u32, 0..4),
432    capab: (u32, 4..8),
433    action: (i32, 8..12),
434    refcnt: (i32, 12..16),
435    bindcnt: (i32, 16..20),
436});
437
438impl Emitable for TcActionGeneric {
439    fn buffer_len(&self) -> usize {
440        Self::BUF_LEN
441    }
442
443    fn emit(&self, buffer: &mut [u8]) {
444        let mut packet = TcActionGenericBuffer::new(buffer);
445        packet.set_index(self.index);
446        packet.set_capab(self.capab);
447        packet.set_action(self.action.into());
448        packet.set_refcnt(self.refcnt);
449        packet.set_bindcnt(self.bindcnt);
450    }
451}
452
453impl<T: AsRef<[u8]>> Parseable<TcActionGenericBuffer<T>> for TcActionGeneric {
454    fn parse(buf: &TcActionGenericBuffer<T>) -> Result<Self, DecodeError> {
455        Ok(Self {
456            index: buf.index(),
457            capab: buf.capab(),
458            action: buf.action().into(),
459            refcnt: buf.refcnt(),
460            bindcnt: buf.bindcnt(),
461        })
462    }
463}
464
465const TC_ACT_UNSPEC: i32 = -1;
466const TC_ACT_OK: i32 = 0;
467const TC_ACT_RECLASSIFY: i32 = 1;
468const TC_ACT_SHOT: i32 = 2;
469const TC_ACT_PIPE: i32 = 3;
470const TC_ACT_STOLEN: i32 = 4;
471const TC_ACT_QUEUED: i32 = 5;
472const TC_ACT_REPEAT: i32 = 6;
473const TC_ACT_REDIRECT: i32 = 7;
474const TC_ACT_TRAP: i32 = 8;
475
476/// Generic traffic control action types.
477///
478/// These are the possible "outcomes" for a packet after an action is applied to
479/// it.
480///
481/// This enum is non-exhaustive as new action types may be added to the kernel
482/// at any time.
483#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
484#[non_exhaustive]
485pub enum TcActionType {
486    /// No specific outcome specified (i.e., take the default for that action).
487    #[default]
488    Unspec,
489    /// Terminates packet processing and allows the packet to proceed.
490    Ok,
491    /// Terminates packet processing and restart packet classification.
492    Reclassify,
493    /// Drop the packet.
494    Shot,
495    /// Pipe the packet to the next action (if any).
496    Pipe,
497    /// The packet is removed from this processing pipeline and returned to
498    /// another.
499    /// This happens, for example, when using the "mirred" redirect action.
500    Stolen,
501    /// Queue the packet for later processing.
502    Queued,
503    /// Repeat the action.
504    ///
505    /// > TODO: confirm this. I have not used this action before and its
506    /// > semantics are unclear.
507    Repeat,
508    /// Redirect the packet.
509    ///
510    /// > TODO: confirm semantics of this action. It is unclear how
511    /// > [`Redirect`] differs from [`Stolen`].
512    ///
513    /// [`Stolen`]: #variant.Stolen
514    /// [`Redirect`]: #variant.Redirect
515    Redirect,
516    /// Transition packet processing from the hardware to software.
517    ///
518    /// If this action is encountered by in software, it is equivalent to
519    /// [`Shot`].
520    ///
521    /// [`Shot`]: #variant.Shot
522    Trap,
523    /// Other action types not known at the time of writing or not yet
524    /// supported by this library.
525    Other(i32),
526}
527
528impl From<i32> for TcActionType {
529    fn from(d: i32) -> Self {
530        match d {
531            TC_ACT_UNSPEC => Self::Unspec,
532            TC_ACT_OK => Self::Ok,
533            TC_ACT_RECLASSIFY => Self::Reclassify,
534            TC_ACT_SHOT => Self::Shot,
535            TC_ACT_PIPE => Self::Pipe,
536            TC_ACT_STOLEN => Self::Stolen,
537            TC_ACT_QUEUED => Self::Queued,
538            TC_ACT_REPEAT => Self::Repeat,
539            TC_ACT_REDIRECT => Self::Redirect,
540            TC_ACT_TRAP => Self::Trap,
541            _ => Self::Other(d),
542        }
543    }
544}
545
546impl From<TcActionType> for i32 {
547    fn from(v: TcActionType) -> i32 {
548        match v {
549            TcActionType::Unspec => TC_ACT_UNSPEC,
550            TcActionType::Ok => TC_ACT_OK,
551            TcActionType::Reclassify => TC_ACT_RECLASSIFY,
552            TcActionType::Shot => TC_ACT_SHOT,
553            TcActionType::Pipe => TC_ACT_PIPE,
554            TcActionType::Stolen => TC_ACT_STOLEN,
555            TcActionType::Queued => TC_ACT_QUEUED,
556            TcActionType::Repeat => TC_ACT_REPEAT,
557            TcActionType::Redirect => TC_ACT_REDIRECT,
558            TcActionType::Trap => TC_ACT_TRAP,
559            TcActionType::Other(d) => d,
560        }
561    }
562}
563
564pub const TC_TCF_BUF_LEN: usize = 32;
565
566#[derive(Debug, PartialEq, Eq, Clone, Default)]
567pub struct Tcf {
568    pub install: u64,
569    pub lastuse: u64,
570    pub expires: u64,
571    pub firstuse: u64,
572}
573
574// kernel struct `tcf_t`
575buffer!(TcfBuffer(TC_TCF_BUF_LEN) {
576    install: (u64, 0..8),
577    lastuse: (u64, 8..16),
578    expires: (u64, 16..24),
579    firstuse: (u64, 24..32),
580});
581
582impl<T: AsRef<[u8]> + ?Sized> Parseable<TcfBuffer<&T>> for Tcf {
583    fn parse(buf: &TcfBuffer<&T>) -> Result<Self, DecodeError> {
584        Ok(Self {
585            install: buf.install(),
586            lastuse: buf.lastuse(),
587            expires: buf.expires(),
588            firstuse: buf.firstuse(),
589        })
590    }
591}
592
593impl Emitable for Tcf {
594    fn buffer_len(&self) -> usize {
595        TC_TCF_BUF_LEN
596    }
597
598    fn emit(&self, buffer: &mut [u8]) {
599        let mut packet = TcfBuffer::new(buffer);
600        packet.set_install(self.install);
601        packet.set_lastuse(self.lastuse);
602        packet.set_expires(self.expires);
603        packet.set_firstuse(self.firstuse);
604    }
605}