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}