Skip to content

Commit

Permalink
Support for tc-actions
Browse files Browse the repository at this point in the history
# 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
   ```
  • Loading branch information
daniel-noland committed May 10, 2024
1 parent 186747e commit 8f2d6ec
Show file tree
Hide file tree
Showing 16 changed files with 1,611 additions and 242 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ netlink-packet-utils = { version = "0.5.2" }
name = "dump_packet_links"

[dev-dependencies]
hex = "0.4.3"
netlink-sys = { version = "0.8.5" }
pretty_assertions = "0.7.2"
61 changes: 52 additions & 9 deletions src/message.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// SPDX-License-Identifier: MIT

use anyhow::Context;
use netlink_packet_utils::{
DecodeError, Emitable, Parseable, ParseableParametrized,
};

use netlink_packet_core::{
NetlinkDeserializable, NetlinkHeader, NetlinkPayload, NetlinkSerializable,
};
use netlink_packet_utils::{
DecodeError, Emitable, Parseable, ParseableParametrized,
};

use crate::tc::{TcActionMessage, TcActionMessageBuffer};
use crate::{
address::{AddressHeader, AddressMessage, AddressMessageBuffer},
link::{LinkMessage, LinkMessageBuffer},
Expand Down Expand Up @@ -46,9 +46,9 @@ const RTM_GETTCLASS: u16 = 42;
const RTM_NEWTFILTER: u16 = 44;
const RTM_DELTFILTER: u16 = 45;
const RTM_GETTFILTER: u16 = 46;
// const RTM_NEWACTION: u16 = 48;
// const RTM_DELACTION: u16 = 49;
// const RTM_GETACTION: u16 = 50;
const RTM_NEWACTION: u16 = 48;
const RTM_DELACTION: u16 = 49;
const RTM_GETACTION: u16 = 50;
const RTM_NEWPREFIX: u16 = 52;
// const RTM_GETMULTICAST: u16 = 58;
// const RTM_GETANYCAST: u16 = 62;
Expand Down Expand Up @@ -291,6 +291,21 @@ impl<'a, T: AsRef<[u8]> + ?Sized>
}
}

RTM_NEWACTION | RTM_DELACTION | RTM_GETACTION => {
let err = "invalid tc action message";
let msg = TcActionMessage::parse(
&TcActionMessageBuffer::new_checked(&buf.inner())
.context(err)?,
)
.context(err)?;
match message_type {
RTM_NEWACTION => RouteNetlinkMessage::NewTrafficAction(msg),
RTM_DELACTION => RouteNetlinkMessage::DelTrafficAction(msg),
RTM_GETACTION => RouteNetlinkMessage::GetTrafficAction(msg),
_ => unreachable!(),
}
}

// ND ID Messages
RTM_NEWNSID | RTM_GETNSID | RTM_DELNSID => {
let err = "invalid nsid message";
Expand Down Expand Up @@ -348,6 +363,9 @@ pub enum RouteNetlinkMessage {
NewTrafficFilter(TcMessage),
DelTrafficFilter(TcMessage),
GetTrafficFilter(TcMessage),
NewTrafficAction(TcActionMessage),
DelTrafficAction(TcActionMessage),
GetTrafficAction(TcActionMessage),
NewTrafficChain(TcMessage),
DelTrafficChain(TcMessage),
GetTrafficChain(TcMessage),
Expand Down Expand Up @@ -460,6 +478,18 @@ impl RouteNetlinkMessage {
matches!(self, RouteNetlinkMessage::GetTrafficFilter(_))
}

pub fn is_new_action(&self) -> bool {
matches!(self, RouteNetlinkMessage::NewTrafficAction(_))
}

pub fn is_del_action(&self) -> bool {
matches!(self, RouteNetlinkMessage::DelTrafficAction(_))
}

pub fn is_get_action(&self) -> bool {
matches!(self, RouteNetlinkMessage::GetTrafficAction(_))
}

pub fn is_new_chain(&self) -> bool {
matches!(self, RouteNetlinkMessage::NewTrafficChain(_))
}
Expand Down Expand Up @@ -528,6 +558,9 @@ impl RouteNetlinkMessage {
NewTrafficFilter(_) => RTM_NEWTFILTER,
DelTrafficFilter(_) => RTM_DELTFILTER,
GetTrafficFilter(_) => RTM_GETTFILTER,
NewTrafficAction(_) => RTM_NEWACTION,
DelTrafficAction(_) => RTM_DELACTION,
GetTrafficAction(_) => RTM_GETACTION,
NewTrafficChain(_) => RTM_NEWCHAIN,
DelTrafficChain(_) => RTM_DELCHAIN,
GetTrafficChain(_) => RTM_GETCHAIN,
Expand Down Expand Up @@ -598,7 +631,12 @@ impl Emitable for RouteNetlinkMessage {
| NewRule(ref msg)
| DelRule(ref msg)
| GetRule(ref msg)
=> msg.buffer_len()
=> msg.buffer_len(),

| NewTrafficAction(ref msg)
| DelTrafficAction(ref msg)
| GetTrafficAction(ref msg)
=> msg.buffer_len(),
}
}

Expand Down Expand Up @@ -658,7 +696,12 @@ impl Emitable for RouteNetlinkMessage {
| NewRule(ref msg)
| DelRule(ref msg)
| GetRule(ref msg)
=> msg.emit(buffer)
=> msg.emit(buffer),

| NewTrafficAction(ref msg)
| DelTrafficAction(ref msg)
| GetTrafficAction(ref msg)
=> msg.emit(buffer),
}
}
}
Expand Down
Loading

0 comments on commit 8f2d6ec

Please sign in to comment.