Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tc-flower(8) support #135

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions src/tc/actions/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use crate::tc::TcStats2;

use super::{
TcActionMirror, TcActionMirrorOption, TcActionNat, TcActionNatOption,
TcActionTunnelKey, TcActionTunnelKeyOption,
};

/// TODO: determine when and why to use this as opposed to the buffer's `kind`.
Expand Down Expand Up @@ -287,6 +288,11 @@ pub enum TcActionOption {
///
/// These options type can be used to perform network address translation.
Nat(TcActionNatOption),
/// Tunnel key options.
///
/// These options type can be used to assign encapsulation properties to
/// the packet.
TunnelKey(TcActionTunnelKeyOption),
/// Other action types not yet supported by this library.
Other(DefaultNla),
}
Expand All @@ -296,6 +302,7 @@ impl Nla for TcActionOption {
match self {
Self::Mirror(nla) => nla.value_len(),
Self::Nat(nla) => nla.value_len(),
Self::TunnelKey(nla) => nla.value_len(),
Self::Other(nla) => nla.value_len(),
}
}
Expand All @@ -304,6 +311,7 @@ impl Nla for TcActionOption {
match self {
Self::Mirror(nla) => nla.emit_value(buffer),
Self::Nat(nla) => nla.emit_value(buffer),
Self::TunnelKey(nla) => nla.emit_value(buffer),
Self::Other(nla) => nla.emit_value(buffer),
}
}
Expand All @@ -312,6 +320,7 @@ impl Nla for TcActionOption {
match self {
Self::Mirror(nla) => nla.kind(),
Self::Nat(nla) => nla.kind(),
Self::TunnelKey(nla) => nla.kind(),
Self::Other(nla) => nla.kind(),
}
}
Expand All @@ -335,6 +344,10 @@ where
TcActionNatOption::parse(buf)
.context("failed to parse nat action")?,
),
TcActionTunnelKey::KIND => Self::TunnelKey(
TcActionTunnelKeyOption::parse(buf)
.context("failed to parse tunnel_key action")?,
),
_ => Self::Other(
DefaultNla::parse(buf)
.context("failed to parse action options")?,
Expand Down Expand Up @@ -548,3 +561,46 @@ impl From<TcActionType> for i32 {
}
}
}

pub const TC_TCF_BUF_LEN: usize = 32;

#[derive(Debug, PartialEq, Eq, Clone, Default)]
pub struct Tcf {
pub install: u64,
pub lastuse: u64,
pub expires: u64,
pub firstuse: u64,
}

// kernel struct `tcf_t`
buffer!(TcfBuffer(TC_TCF_BUF_LEN) {
install: (u64, 0..8),
lastuse: (u64, 8..16),
expires: (u64, 16..24),
firstuse: (u64, 24..32),
});

impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<TcfBuffer<&'a T>> for Tcf {
fn parse(buf: &TcfBuffer<&T>) -> Result<Self, DecodeError> {
Ok(Self {
install: buf.install(),
lastuse: buf.lastuse(),
expires: buf.expires(),
firstuse: buf.firstuse(),
})
}
}

impl Emitable for Tcf {
fn buffer_len(&self) -> usize {
TC_TCF_BUF_LEN
}

fn emit(&self, buffer: &mut [u8]) {
let mut packet = TcfBuffer::new(buffer);
packet.set_install(self.install);
packet.set_lastuse(self.lastuse);
packet.set_expires(self.expires);
packet.set_firstuse(self.firstuse);
}
}
16 changes: 10 additions & 6 deletions src/tc/actions/mirror.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ use netlink_packet_utils::{
DecodeError,
};

use super::{TcActionGeneric, TcActionGenericBuffer};
use super::{
TcActionGeneric, TcActionGenericBuffer, Tcf, TcfBuffer, TC_TCF_BUF_LEN,
};

/// Traffic control action used to mirror or redirect packets.
#[derive(Debug, PartialEq, Eq, Clone)]
Expand All @@ -30,8 +32,8 @@ const TCA_MIRRED_PARMS: u16 = 2;
#[derive(Debug, PartialEq, Eq, Clone)]
#[non_exhaustive]
pub enum TcActionMirrorOption {
/// TODO: document this after we make it something better than `Vec<u8>`
Tm(Vec<u8>),
/// Rule installation and usage time
Tm(Tcf),
/// Parameters for the mirred action.
Parms(TcMirror),
/// Other attributes unknown at the time of writing.
Expand All @@ -41,15 +43,15 @@ pub enum TcActionMirrorOption {
impl Nla for TcActionMirrorOption {
fn value_len(&self) -> usize {
match self {
Self::Tm(bytes) => bytes.len(),
Self::Tm(_) => TC_TCF_BUF_LEN,
Self::Parms(_) => TC_MIRRED_BUF_LEN,
Self::Other(attr) => attr.value_len(),
}
}

fn emit_value(&self, buffer: &mut [u8]) {
match self {
Self::Tm(bytes) => buffer.copy_from_slice(bytes.as_slice()),
Self::Tm(p) => p.emit(buffer),
Self::Parms(p) => p.emit(buffer),
Self::Other(attr) => attr.emit_value(buffer),
}
Expand All @@ -69,7 +71,9 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
let payload = buf.value();
Ok(match buf.kind() {
TCA_MIRRED_TM => Self::Tm(payload.to_vec()),
TCA_MIRRED_TM => {
Self::Tm(Tcf::parse(&TcfBuffer::new_checked(payload)?)?)
}
TCA_MIRRED_PARMS => Self::Parms(TcMirror::parse(
&TcMirrorBuffer::new_checked(payload)?,
)?),
Expand Down
6 changes: 5 additions & 1 deletion src/tc/actions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub use nat_flag::TcNatFlags;

pub use self::action::{
TcAction, TcActionAttribute, TcActionGeneric, TcActionGenericBuffer,
TcActionOption, TcActionType,
TcActionOption, TcActionType, Tcf, TcfBuffer, TC_TCF_BUF_LEN,
};
pub use self::header::{TcActionMessageBuffer, TcActionMessageHeader};
pub use self::message::{
Expand All @@ -16,13 +16,17 @@ pub use self::mirror::{
TcMirrorBuffer,
};
pub use self::nat::{TcActionNat, TcActionNatOption, TcNat, TcNatBuffer};
pub use self::tunnel_key::{
TcActionTunnelKey, TcActionTunnelKeyOption, TcTunnelKey,
};

mod action;
mod header;
mod message;
mod mirror;
mod nat;
mod nat_flag;
mod tunnel_key;

#[cfg(test)]
pub mod tests;
17 changes: 11 additions & 6 deletions src/tc/actions/nat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ use netlink_packet_utils::{
DecodeError,
};

use super::{nat_flag::TcNatFlags, TcActionGeneric, TcActionGenericBuffer};
use super::{
nat_flag::TcNatFlags, TcActionGeneric, TcActionGenericBuffer, Tcf,
TcfBuffer, TC_TCF_BUF_LEN,
};

const TCA_NAT_PARMS: u16 = 1;
const TCA_NAT_TM: u16 = 2;
Expand All @@ -29,8 +32,8 @@ impl TcActionNat {
#[derive(Debug, PartialEq, Eq, Clone)]
#[non_exhaustive]
pub enum TcActionNatOption {
/// TODO: document this after we make it something better than `Vec<u8>`
Tm(Vec<u8>),
/// Rule installation and usage time
Tm(Tcf),
/// Parameters for the nat action.
Parms(TcNat),
/// Other attributes unknown at the time of writing.
Expand All @@ -40,15 +43,15 @@ pub enum TcActionNatOption {
impl Nla for TcActionNatOption {
fn value_len(&self) -> usize {
match self {
Self::Tm(bytes) => bytes.len(),
Self::Tm(_) => TC_TCF_BUF_LEN,
Self::Parms(v) => v.buffer_len(),
Self::Other(attr) => attr.value_len(),
}
}

fn emit_value(&self, buffer: &mut [u8]) {
match self {
Self::Tm(bytes) => buffer.copy_from_slice(bytes.as_slice()),
Self::Tm(p) => p.emit(buffer),
Self::Parms(p) => p.emit(buffer),
Self::Other(attr) => attr.emit_value(buffer),
}
Expand All @@ -68,7 +71,9 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
let payload = buf.value();
Ok(match buf.kind() {
TCA_NAT_TM => Self::Tm(payload.to_vec()),
TCA_NAT_TM => {
Self::Tm(Tcf::parse(&TcfBuffer::new_checked(payload)?)?)
}
TCA_NAT_PARMS => {
Self::Parms(TcNat::parse(&TcNatBuffer::new_checked(payload)?)?)
}
Expand Down
24 changes: 13 additions & 11 deletions src/tc/actions/tests/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ mod mirror {
use crate::tc::TcMirrorActionType::{EgressRedir, IngressMirror};
use crate::tc::TcStats2::{Basic, BasicHw, Queue};
use crate::tc::{
TcAction, TcActionGeneric, TcMirror, TcStatsBasic, TcStatsQueue,
TcAction, TcActionGeneric, TcMirror, TcStatsBasic, TcStatsQueue, Tcf,
};
use crate::AddressFamily;

Expand Down Expand Up @@ -223,11 +223,12 @@ mod mirror {
eaction: EgressRedir,
ifindex: 1,
})),
Mirror(Tm(vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0,
])),
Mirror(Tm(Tcf {
install: 0,
lastuse: 0,
expires: 0,
firstuse: 0,
})),
]),
],
},
Expand Down Expand Up @@ -269,11 +270,12 @@ mod mirror {
eaction: IngressMirror,
ifindex: 1,
})),
Mirror(Tm(vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0,
])),
Mirror(Tm(Tcf {
install: 0,
lastuse: 0,
expires: 0,
firstuse: 0,
})),
]),
],
},
Expand Down
92 changes: 74 additions & 18 deletions src/tc/actions/tests/mirror.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ use netlink_packet_utils::nla::NlaBuffer;
use netlink_packet_utils::{Emitable, Parseable};

use crate::tc::{
TcActionGeneric, TcActionGenericBuffer, TcActionMirrorOption, TcActionType,
TcMirror, TcMirrorActionType, TcMirrorBuffer,
TcAction, TcActionAttribute, TcActionGeneric, TcActionGenericBuffer,
TcActionMirrorOption, TcActionOption, TcActionType, TcMirror,
TcMirrorActionType, TcMirrorBuffer, TcStats2, TcStatsBasic, TcStatsQueue,
Tcf,
};

#[test]
Expand Down Expand Up @@ -64,22 +66,76 @@ fn tc_mirror_example_parse_back() {
assert_eq!(orig, parsed);
}

// > act actions add action mirred egress redirect dev veth1
// > tools/nl_dump.py dump_actions mirred
// Note: 5.15 and 6.8 kernels do NOT set NLA_F_NESTED for TCA_ACT_OPTIONS
//
#[test]
fn tc_mirror_tm_default_parse_back() {
let mirror_option = TcActionMirrorOption::Tm(vec![]);
let mut buffer = vec![0; mirror_option.buffer_len()];
mirror_option.emit(&mut buffer);
let nla_buf = NlaBuffer::new_checked(&buffer).unwrap();
let parsed = TcActionMirrorOption::parse(&nla_buf).unwrap();
assert_eq!(mirror_option, parsed);
}
fn get_mirred_eggress_redirect_action() {
let raw = vec![
0x9C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x01, 0x00, 0x6D, 0x69, 0x72, 0x72,
0x65, 0x64, 0x00, 0x00, 0x44, 0x00, 0x04, 0x00, 0x14, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x48, 0x00, 0x02, 0x80, 0x20, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x24, 0x00, 0x01, 0x00, 0x68, 0x3D, 0xB6, 0x02, 0x00, 0x00, 0x00, 0x00,
0x68, 0x3D, 0xB6, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];

#[test]
fn tc_mirror_tm_example_parse_back() {
let mirror_option = TcActionMirrorOption::Tm(vec![1, 2, 3]);
let mut buffer = vec![0; mirror_option.buffer_len()];
mirror_option.emit(&mut buffer);
let nla_buf = NlaBuffer::new_checked(&buffer).unwrap();
let parsed = TcActionMirrorOption::parse(&nla_buf).unwrap();
assert_eq!(mirror_option, parsed);
let expected = TcAction {
tab: 0,
attributes: vec![
TcActionAttribute::Kind("mirred".to_string()),
TcActionAttribute::Stats(vec![
TcStats2::Basic(TcStatsBasic {
bytes: 0,
packets: 0,
}),
TcStats2::BasicHw(TcStatsBasic {
bytes: 0,
packets: 0,
}),
TcStats2::Queue(TcStatsQueue {
qlen: 0,
backlog: 0,
drops: 0,
requeues: 0,
overlimits: 0,
}),
]),
TcActionAttribute::Options(vec![
TcActionOption::Mirror(TcActionMirrorOption::Parms(TcMirror {
generic: TcActionGeneric {
index: 1,
capab: 0,
action: TcActionType::Ok,
refcnt: 2,
bindcnt: 2,
},
eaction: TcMirrorActionType::EgressRedir,
ifindex: 3,
})),
TcActionOption::Mirror(TcActionMirrorOption::Tm(Tcf {
install: 45497704,
lastuse: 45497704,
expires: 0,
firstuse: 0,
})),
]),
],
};

assert_eq!(expected, TcAction::parse(&NlaBuffer::new(&raw)).unwrap());

let mut buf = vec![0; expected.buffer_len()];

expected.emit(&mut buf);

assert_eq!(buf, raw);
}
1 change: 1 addition & 0 deletions src/tc/actions/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ pub mod header;
pub mod message;
pub mod mirror;
pub mod nat;
pub mod tunnel_key;
Loading
Loading