Skip to content
This repository has been archived by the owner on Oct 26, 2022. It is now read-only.

Commit

Permalink
Add tc qdisc support
Browse files Browse the repository at this point in the history
Currently only qdisc ingress is supported.

Signed-off-by: wllenyj <wllenyj@linux.alibaba.com>
  • Loading branch information
wllenyj committed Feb 21, 2022
1 parent d2a5109 commit 921a936
Show file tree
Hide file tree
Showing 14 changed files with 781 additions and 46 deletions.
94 changes: 94 additions & 0 deletions netlink-packet-route/src/rtnl/tc/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: MIT

/// Handles
pub const TC_H_MAJ_MASK: u32 = 0xFFFF0000;
pub const TC_H_MIN_MASK: u32 = 0x0000FFFF;

#[macro_export]
macro_rules! TC_H_MAKE {
($maj: expr, $min: expr) => {
($maj & TC_H_MAJ_MASK) | ($min & TC_H_MIN_MASK)
};
}

pub const TC_H_UNSPEC: u32 = 0;
pub const TC_H_ROOT: u32 = 0xFFFFFFFF;
pub const TC_H_INGRESS: u32 = 0xFFFFFFF1;
pub const TC_H_CLSACT: u32 = TC_H_INGRESS;

pub const TC_H_MIN_PRIORITY: u32 = 0xFFE0;
pub const TC_H_MIN_INGRESS: u32 = 0xFFF2;
pub const TC_H_MIN_EGRESS: u32 = 0xFFF3;

/// U32 filters
pub const TCA_U32_UNSPEC: u16 = 0;
pub const TCA_U32_CLASSID: u16 = 1;
pub const TCA_U32_HASH: u16 = 2;
pub const TCA_U32_LINK: u16 = 3;
pub const TCA_U32_DIVISOR: u16 = 4;
pub const TCA_U32_SEL: u16 = 5;
pub const TCA_U32_POLICE: u16 = 6;
pub const TCA_U32_ACT: u16 = 7;
pub const TCA_U32_INDEV: u16 = 8;
pub const TCA_U32_PCNT: u16 = 9;
pub const TCA_U32_MARK: u16 = 10;
pub const TCA_U32_FLAGS: u16 = 11;
pub const TCA_U32_PAD: u16 = 12;
pub const TCA_U32_MAX: u16 = TCA_U32_PAD;

/// U32 Flags
pub const TC_U32_TERMINAL: u8 = 1;
pub const TC_U32_OFFSET: u8 = 2;
pub const TC_U32_VAROFFSET: u8 = 4;
pub const TC_U32_EAT: u8 = 8;
pub const TC_U32_MAXDEPTH: u8 = 8;

/// Action attributes
pub const TCA_ACT_UNSPEC: u16 = 0;
pub const TCA_ACT_KIND: u16 = 1;
pub const TCA_ACT_OPTIONS: u16 = 2;
pub const TCA_ACT_INDEX: u16 = 3;
pub const TCA_ACT_STATS: u16 = 4;
pub const TCA_ACT_PAD: u16 = 5;
pub const TCA_ACT_COOKIE: u16 = 6;

//TODO(wllenyj): Why not subtract 1? See `linux/pkt_cls.h` for original definition.
pub const TCA_ACT_MAX: u16 = 7;
pub const TCA_OLD_COMPAT: u16 = TCA_ACT_MAX + 1;
pub const TCA_ACT_MAX_PRIO: u16 = 32;
pub const TCA_ACT_BIND: u16 = 1;
pub const TCA_ACT_NOBIND: u16 = 0;
pub const TCA_ACT_UNBIND: u16 = 1;
pub const TCA_ACT_NOUNBIND: u16 = 0;
pub const TCA_ACT_REPLACE: u16 = 1;
pub const TCA_ACT_NOREPLACE: u16 = 0;

pub const TC_ACT_UNSPEC: i32 = -1;
pub const TC_ACT_OK: i32 = 0;
pub const TC_ACT_RECLASSIFY: i32 = 1;
pub const TC_ACT_SHOT: i32 = 2;
pub const TC_ACT_PIPE: i32 = 3;
pub const TC_ACT_STOLEN: i32 = 4;
pub const TC_ACT_QUEUED: i32 = 5;
pub const TC_ACT_REPEAT: i32 = 6;
pub const TC_ACT_REDIRECT: i32 = 7;
pub const TC_ACT_TRAP: i32 = 8;

pub const TC_ACT_VALUE_MAX: i32 = TC_ACT_TRAP;

pub const TC_ACT_JUMP: i32 = 0x10000000;

pub const TCA_ACT_TAB: u16 = 1; // TCA_ROOT_TAB
pub const TCAA_MAX: u16 = 1;

/// Mirred action attr
pub const TCA_MIRRED_UNSPEC: u16 = 0;
pub const TCA_MIRRED_TM: u16 = 1;
pub const TCA_MIRRED_PARMS: u16 = 2;
pub const TCA_MIRRED_PAD: u16 = 3;
pub const TCA_MIRRED_MAX: u16 = TCA_MIRRED_PAD;

pub const TCA_EGRESS_REDIR: i32 = 1; /* packet redirect to EGRESS */
pub const TCA_EGRESS_MIRROR: i32 = 2; /* mirror packet to EGRESS */
pub const TCA_INGRESS_REDIR: i32 = 3; /* packet redirect to INGRESS */
pub const TCA_INGRESS_MIRROR: i32 = 4; /* mirror packet to INGRESS */
67 changes: 64 additions & 3 deletions netlink-packet-route/src/rtnl/tc/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@
use anyhow::Context;

use crate::{
nlas::tc::Nla,
traits::{Emitable, Parseable},
constants::*,
nlas::{
tc::{Nla, Stats, Stats2, StatsBuffer, TcOpt},
DefaultNla,
NlasIterator,
},
parsers::{parse_string, parse_u8},
traits::{Emitable, Parseable, ParseableParametrized},
DecodeError,
TcMessageBuffer,
TC_HEADER_LEN,
Expand All @@ -24,6 +30,17 @@ impl TcMessage {
pub fn from_parts(header: TcHeader, nlas: Vec<Nla>) -> Self {
TcMessage { header, nlas }
}

/// Create a new `TcMessage` with the given index
pub fn with_index(index: i32) -> Self {
Self {
header: TcHeader {
index,
..Default::default()
},
nlas: Vec::new(),
}
}
}

#[derive(Debug, PartialEq, Eq, Clone, Default)]
Expand Down Expand Up @@ -90,8 +107,52 @@ impl<'a, T: AsRef<[u8]> + 'a> Parseable<TcMessageBuffer<&'a T>> for TcMessage {
impl<'a, T: AsRef<[u8]> + 'a> Parseable<TcMessageBuffer<&'a T>> for Vec<Nla> {
fn parse(buf: &TcMessageBuffer<&'a T>) -> Result<Self, DecodeError> {
let mut nlas = vec![];
let mut kind = String::new();

for nla_buf in buf.nlas() {
nlas.push(Nla::parse(&nla_buf?)?);
let buf = nla_buf.context("invalid tc nla")?;
let payload = buf.value();
let nla = match buf.kind() {
TCA_UNSPEC => Nla::Unspec(payload.to_vec()),
TCA_KIND => {
kind = parse_string(payload).context("invalid TCA_KIND")?;
Nla::Kind(kind.clone())
}
TCA_OPTIONS => {
let mut nlas = vec![];
for nla in NlasIterator::new(payload) {
let nla = nla.context("invalid TCA_OPTIONS")?;
nlas.push(
TcOpt::parse_with_param(&nla, &kind)
.context("failed to parse TCA_OPTIONS")?,
)
}
Nla::Options(nlas)
}
TCA_STATS => Nla::Stats(
Stats::parse(&StatsBuffer::new_checked(payload).context("invalid TCA_STATS")?)
.context("failed to parse TCA_STATS")?,
),
TCA_XSTATS => Nla::XStats(payload.to_vec()),
TCA_RATE => Nla::Rate(payload.to_vec()),
TCA_FCNT => Nla::Fcnt(payload.to_vec()),
TCA_STATS2 => {
let mut nlas = vec![];
for nla in NlasIterator::new(payload) {
let nla = nla.context("invalid TCA_STATS2")?;
nlas.push(Stats2::parse(&nla).context("failed to parse TCA_STATS2")?);
}
Nla::Stats2(nlas)
}
TCA_STAB => Nla::Stab(payload.to_vec()),
TCA_CHAIN => Nla::Chain(payload.to_vec()),
TCA_HW_OFFLOAD => {
Nla::HwOffload(parse_u8(payload).context("failed to parse TCA_HW_OFFLOAD")?)
}
_ => Nla::Other(DefaultNla::parse(&buf).context("failed to parse tc nla")?),
};

nlas.push(nla);
}
Ok(nlas)
}
Expand Down
4 changes: 4 additions & 0 deletions netlink-packet-route/src/rtnl/tc/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
// SPDX-License-Identifier: MIT

mod buffer;
pub mod constants;
mod message;
pub mod nlas;

pub use self::{buffer::*, message::*, nlas::*};

#[cfg(test)]
mod test;
56 changes: 15 additions & 41 deletions netlink-packet-route/src/rtnl/tc/nlas/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,15 @@ pub use self::stats_queue::*;
mod stats_basic;
pub use self::stats_basic::*;

mod options;
pub use self::options::*;

mod qdisc;
pub use self::qdisc::*;

use crate::{
constants::*,
nlas::{self, DefaultNla, NlaBuffer, NlasIterator},
parsers::{parse_string, parse_u8},
nlas::{self, DefaultNla, NlaBuffer},
traits::{Emitable, Parseable},
DecodeError,
};
Expand All @@ -23,9 +28,9 @@ pub enum Nla {
Unspec(Vec<u8>),
/// Name of queueing discipline
Kind(String),
/// Qdisc-specific options follow
Options(Vec<u8>),
/// Qdisc statistics
/// Options follow
Options(Vec<TcOpt>),
/// Statistics
Stats(Stats),
/// Module-specific statistics
XStats(Vec<u8>),
Expand All @@ -45,20 +50,15 @@ impl nlas::Nla for Nla {
use self::Nla::*;
match *self {
// Vec<u8>
Unspec(ref bytes)
| Options(ref bytes)
| XStats(ref bytes)
| Rate(ref bytes)
| Fcnt(ref bytes)
| Stab(ref bytes)
| Chain(ref bytes) => bytes.len(),
Unspec(ref bytes) | XStats(ref bytes) | Rate(ref bytes) | Fcnt(ref bytes)
| Stab(ref bytes) | Chain(ref bytes) => bytes.len(),
HwOffload(_) => 1,
Stats2(ref thing) => thing.as_slice().buffer_len(),
Stats(_) => STATS_LEN,
Kind(ref string) => string.as_bytes().len() + 1,

Options(ref opt) => opt.as_slice().buffer_len(),
// Defaults
Other(ref attr) => attr.value_len(),
Other(ref attr) => attr.value_len(),
}
}

Expand All @@ -68,7 +68,6 @@ impl nlas::Nla for Nla {
match *self {
// Vec<u8>
Unspec(ref bytes)
| Options(ref bytes)
| XStats(ref bytes)
| Rate(ref bytes)
| Fcnt(ref bytes)
Expand All @@ -83,6 +82,7 @@ impl nlas::Nla for Nla {
buffer[..string.as_bytes().len()].copy_from_slice(string.as_bytes());
buffer[string.as_bytes().len()] = 0;
}
Options(ref opt) => opt.as_slice().emit(buffer),

// Default
Other(ref attr) => attr.emit_value(buffer),
Expand All @@ -108,32 +108,6 @@ impl nlas::Nla for Nla {
}
}

impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for Nla {
fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
let payload = buf.value();
Ok(match buf.kind() {
TCA_UNSPEC => Self::Unspec(payload.to_vec()),
TCA_KIND => Self::Kind(parse_string(payload)?),
TCA_OPTIONS => Self::Options(payload.to_vec()),
TCA_STATS => Self::Stats(Stats::parse(&StatsBuffer::new_checked(payload)?)?),
TCA_XSTATS => Self::XStats(payload.to_vec()),
TCA_RATE => Self::Rate(payload.to_vec()),
TCA_FCNT => Self::Fcnt(payload.to_vec()),
TCA_STATS2 => {
let mut nlas = vec![];
for nla in NlasIterator::new(payload) {
nlas.push(Stats2::parse(&(nla?))?);
}
Self::Stats2(nlas)
}
TCA_STAB => Self::Stab(payload.to_vec()),
TCA_CHAIN => Self::Chain(payload.to_vec()),
TCA_HW_OFFLOAD => Self::HwOffload(parse_u8(payload)?),
_ => Self::Other(DefaultNla::parse(buf)?),
})
}
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Stats2 {
StatsApp(Vec<u8>),
Expand Down
51 changes: 51 additions & 0 deletions netlink-packet-route/src/rtnl/tc/nlas/options.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: MIT
use crate::{
nlas::{self, DefaultNla, NlaBuffer},
tc::ingress,
traits::{Parseable, ParseableParametrized},
DecodeError,
};

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum TcOpt {
// Qdisc specific options
Ingress,
// Other options
Other(DefaultNla),
}

impl nlas::Nla for TcOpt {
fn value_len(&self) -> usize {
match self {
Self::Ingress => 0,
Self::Other(o) => o.value_len(),
}
}

fn emit_value(&self, buffer: &mut [u8]) {
match self {
Self::Ingress => unreachable!(),
Self::Other(o) => o.emit_value(buffer),
}
}

fn kind(&self) -> u16 {
match self {
Self::Ingress => unreachable!(),
Self::Other(o) => o.kind(),
}
}
}

impl<'a, T, S> ParseableParametrized<NlaBuffer<&'a T>, S> for TcOpt
where
T: AsRef<[u8]> + ?Sized,
S: AsRef<str>,
{
fn parse_with_param(buf: &NlaBuffer<&'a T>, kind: S) -> Result<Self, DecodeError> {
Ok(match kind.as_ref() {
ingress::KIND => TcOpt::Ingress,
_ => Self::Other(DefaultNla::parse(buf)?),
})
}
}
5 changes: 5 additions & 0 deletions netlink-packet-route/src/rtnl/tc/nlas/qdisc/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// SPDX-License-Identifier: MIT

pub mod ingress {
pub const KIND: &str = "ingress";
}
16 changes: 16 additions & 0 deletions netlink-packet-route/src/rtnl/tc/nlas/qdisc/prio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: MIT

//#[derive(Debug, PartialEq, Eq, Clone)]
//pub enum Qdisc {
// Prio(Prio),
// Ingress,
//}
//
//pub const TC_PRIO_MAX: usize = 15;
//#[derive(Debug, PartialEq, Eq, Clone)]
//pub struct Prio {
// // Number of bands
// bands: i32,
// // Map: logical priority -> PRIO band
// priomap: [u8; TC_PRIO_MAX + 1],
//}
Loading

0 comments on commit 921a936

Please sign in to comment.