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}