Skip to content

Commit 65f699a

Browse files
daniel-nolandcathay4t
authored andcommitted
Support for tc-actions
# Summary of feature This is easiest to explain by way of iproute2. `tc` allows actions to be created independently of filters. Once created, these actions may then 1. be associated with _zero or more_ filters, 2. live updated (and updates will be seen by all filters using that action), 3. be deleted (only after all filters using that action have been deleted). For example, consider the following : ```bash for i in x y z; do ip link add dev "$i" type dummy tc qdisc add dev "$i" clsact done tc actions add action mirred egress redirect dev y tc actions add action gact drop ``` At this point, we could 1. list the `mirred` actions ```bash $ tc actions list action mirred total acts 1 action order 0: mirred (Egress Redirect to device y) stolen index 1 ref 1 bind 0 not_in_hw used_hw_stats disabled ``` 2. list the `gact` actions ```bash $ tc actions list action gact total acts 1 action order 0: gact action drop random type none pass val 0 index 1 ref 1 bind 0 not_in_hw used_hw_stats disabled ``` 3. create any number of filters using either or both of these actions by index ```bash tc filter add dev x ingress pref 1000 proto ip flower dst_ip 8.8.8.8 action mirred index 1 tc filter add dev z ingress pref 1000 proto ip flower dst_ip 8.8.8.8 action mirred index 1 action gact index 1 ``` 4. display those filters as normal (with per-action statistics) ```bash $ tc -s filter show dev z ingress filter protocol ip pref 1000 flower chain 0 filter protocol ip pref 1000 flower chain 0 handle 0x1 eth_type ipv4 dst_ip 8.8.8.8 not_in_hw action order 1: mirred (Egress Redirect to device y) stolen index 1 ref 3 bind 2 installed 599 sec used 599 sec Action statistics: Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0 action order 2: gact action drop random type none pass val 0 index 1 ref 2 bind 1 installed 599 sec used 599 sec Action statistics: Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0 ``` 5. centrally update those actions (e.g., change `drop` to `pass`) ```bash $ tc actions change action gact pass index 1 $ tc -s filter show dev z ingress filter protocol ip pref 1000 flower chain 0 filter protocol ip pref 1000 flower chain 0 handle 0x1 eth_type ipv4 dst_ip 8.8.8.8 not_in_hw action order 1: mirred (Egress Redirect to device y) stolen index 1 ref 3 bind 2 installed 838 sec used 838 sec Action statistics: Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0 action order 2: gact action pass random type none pass val 0 index 1 ref 2 bind 1 installed 838 sec used 838 sec Action statistics: Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0 ``` 6. attempts to delete those actions while in use will be rejected (albeit with a buggy error message from `iproute2`/`tc`) ```bash $ tc actions delete action gact index 1 Error: Failed to delete TC action. We have an error talking to the kernel Command "action" is unknown, try "tc actions help" ``` 7. Removing all filters that use an action will allow the action to be deleted ```bash $ tc filter del dev z ingress pref 1000 $ tc actions delete action gact index 1 $ tc filter del dev x ingress pref 1000 $ tc actions delete action mirred index 1 ```
1 parent 064ddc8 commit 65f699a

16 files changed

+1611
-242
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,6 @@ netlink-packet-utils = { version = "0.5.2" }
2727
name = "dump_packet_links"
2828

2929
[dev-dependencies]
30+
hex = "0.4.3"
3031
netlink-sys = { version = "0.8.5" }
3132
pretty_assertions = "0.7.2"

src/message.rs

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
// SPDX-License-Identifier: MIT
22

33
use anyhow::Context;
4-
use netlink_packet_utils::{
5-
DecodeError, Emitable, Parseable, ParseableParametrized,
6-
};
7-
84
use netlink_packet_core::{
95
NetlinkDeserializable, NetlinkHeader, NetlinkPayload, NetlinkSerializable,
106
};
7+
use netlink_packet_utils::{
8+
DecodeError, Emitable, Parseable, ParseableParametrized,
9+
};
1110

11+
use crate::tc::{TcActionMessage, TcActionMessageBuffer};
1212
use crate::{
1313
address::{AddressHeader, AddressMessage, AddressMessageBuffer},
1414
link::{LinkMessage, LinkMessageBuffer},
@@ -46,9 +46,9 @@ const RTM_GETTCLASS: u16 = 42;
4646
const RTM_NEWTFILTER: u16 = 44;
4747
const RTM_DELTFILTER: u16 = 45;
4848
const RTM_GETTFILTER: u16 = 46;
49-
// const RTM_NEWACTION: u16 = 48;
50-
// const RTM_DELACTION: u16 = 49;
51-
// const RTM_GETACTION: u16 = 50;
49+
const RTM_NEWACTION: u16 = 48;
50+
const RTM_DELACTION: u16 = 49;
51+
const RTM_GETACTION: u16 = 50;
5252
const RTM_NEWPREFIX: u16 = 52;
5353
// const RTM_GETMULTICAST: u16 = 58;
5454
// const RTM_GETANYCAST: u16 = 62;
@@ -291,6 +291,21 @@ impl<'a, T: AsRef<[u8]> + ?Sized>
291291
}
292292
}
293293

294+
RTM_NEWACTION | RTM_DELACTION | RTM_GETACTION => {
295+
let err = "invalid tc action message";
296+
let msg = TcActionMessage::parse(
297+
&TcActionMessageBuffer::new_checked(&buf.inner())
298+
.context(err)?,
299+
)
300+
.context(err)?;
301+
match message_type {
302+
RTM_NEWACTION => RouteNetlinkMessage::NewTrafficAction(msg),
303+
RTM_DELACTION => RouteNetlinkMessage::DelTrafficAction(msg),
304+
RTM_GETACTION => RouteNetlinkMessage::GetTrafficAction(msg),
305+
_ => unreachable!(),
306+
}
307+
}
308+
294309
// ND ID Messages
295310
RTM_NEWNSID | RTM_GETNSID | RTM_DELNSID => {
296311
let err = "invalid nsid message";
@@ -348,6 +363,9 @@ pub enum RouteNetlinkMessage {
348363
NewTrafficFilter(TcMessage),
349364
DelTrafficFilter(TcMessage),
350365
GetTrafficFilter(TcMessage),
366+
NewTrafficAction(TcActionMessage),
367+
DelTrafficAction(TcActionMessage),
368+
GetTrafficAction(TcActionMessage),
351369
NewTrafficChain(TcMessage),
352370
DelTrafficChain(TcMessage),
353371
GetTrafficChain(TcMessage),
@@ -460,6 +478,18 @@ impl RouteNetlinkMessage {
460478
matches!(self, RouteNetlinkMessage::GetTrafficFilter(_))
461479
}
462480

481+
pub fn is_new_action(&self) -> bool {
482+
matches!(self, RouteNetlinkMessage::NewTrafficAction(_))
483+
}
484+
485+
pub fn is_del_action(&self) -> bool {
486+
matches!(self, RouteNetlinkMessage::DelTrafficAction(_))
487+
}
488+
489+
pub fn is_get_action(&self) -> bool {
490+
matches!(self, RouteNetlinkMessage::GetTrafficAction(_))
491+
}
492+
463493
pub fn is_new_chain(&self) -> bool {
464494
matches!(self, RouteNetlinkMessage::NewTrafficChain(_))
465495
}
@@ -528,6 +558,9 @@ impl RouteNetlinkMessage {
528558
NewTrafficFilter(_) => RTM_NEWTFILTER,
529559
DelTrafficFilter(_) => RTM_DELTFILTER,
530560
GetTrafficFilter(_) => RTM_GETTFILTER,
561+
NewTrafficAction(_) => RTM_NEWACTION,
562+
DelTrafficAction(_) => RTM_DELACTION,
563+
GetTrafficAction(_) => RTM_GETACTION,
531564
NewTrafficChain(_) => RTM_NEWCHAIN,
532565
DelTrafficChain(_) => RTM_DELCHAIN,
533566
GetTrafficChain(_) => RTM_GETCHAIN,
@@ -598,7 +631,12 @@ impl Emitable for RouteNetlinkMessage {
598631
| NewRule(ref msg)
599632
| DelRule(ref msg)
600633
| GetRule(ref msg)
601-
=> msg.buffer_len()
634+
=> msg.buffer_len(),
635+
636+
| NewTrafficAction(ref msg)
637+
| DelTrafficAction(ref msg)
638+
| GetTrafficAction(ref msg)
639+
=> msg.buffer_len(),
602640
}
603641
}
604642

@@ -658,7 +696,12 @@ impl Emitable for RouteNetlinkMessage {
658696
| NewRule(ref msg)
659697
| DelRule(ref msg)
660698
| GetRule(ref msg)
661-
=> msg.emit(buffer)
699+
=> msg.emit(buffer),
700+
701+
| NewTrafficAction(ref msg)
702+
| DelTrafficAction(ref msg)
703+
| GetTrafficAction(ref msg)
704+
=> msg.emit(buffer),
662705
}
663706
}
664707
}

0 commit comments

Comments
 (0)