netlink_packet_route/tc/actions/
action.rs

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