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

Commit 921a936

Browse files
committed
Add tc qdisc support
Currently only qdisc ingress is supported. Signed-off-by: wllenyj <[email protected]>
1 parent d2a5109 commit 921a936

File tree

14 files changed

+781
-46
lines changed

14 files changed

+781
-46
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
/// Handles
4+
pub const TC_H_MAJ_MASK: u32 = 0xFFFF0000;
5+
pub const TC_H_MIN_MASK: u32 = 0x0000FFFF;
6+
7+
#[macro_export]
8+
macro_rules! TC_H_MAKE {
9+
($maj: expr, $min: expr) => {
10+
($maj & TC_H_MAJ_MASK) | ($min & TC_H_MIN_MASK)
11+
};
12+
}
13+
14+
pub const TC_H_UNSPEC: u32 = 0;
15+
pub const TC_H_ROOT: u32 = 0xFFFFFFFF;
16+
pub const TC_H_INGRESS: u32 = 0xFFFFFFF1;
17+
pub const TC_H_CLSACT: u32 = TC_H_INGRESS;
18+
19+
pub const TC_H_MIN_PRIORITY: u32 = 0xFFE0;
20+
pub const TC_H_MIN_INGRESS: u32 = 0xFFF2;
21+
pub const TC_H_MIN_EGRESS: u32 = 0xFFF3;
22+
23+
/// U32 filters
24+
pub const TCA_U32_UNSPEC: u16 = 0;
25+
pub const TCA_U32_CLASSID: u16 = 1;
26+
pub const TCA_U32_HASH: u16 = 2;
27+
pub const TCA_U32_LINK: u16 = 3;
28+
pub const TCA_U32_DIVISOR: u16 = 4;
29+
pub const TCA_U32_SEL: u16 = 5;
30+
pub const TCA_U32_POLICE: u16 = 6;
31+
pub const TCA_U32_ACT: u16 = 7;
32+
pub const TCA_U32_INDEV: u16 = 8;
33+
pub const TCA_U32_PCNT: u16 = 9;
34+
pub const TCA_U32_MARK: u16 = 10;
35+
pub const TCA_U32_FLAGS: u16 = 11;
36+
pub const TCA_U32_PAD: u16 = 12;
37+
pub const TCA_U32_MAX: u16 = TCA_U32_PAD;
38+
39+
/// U32 Flags
40+
pub const TC_U32_TERMINAL: u8 = 1;
41+
pub const TC_U32_OFFSET: u8 = 2;
42+
pub const TC_U32_VAROFFSET: u8 = 4;
43+
pub const TC_U32_EAT: u8 = 8;
44+
pub const TC_U32_MAXDEPTH: u8 = 8;
45+
46+
/// Action attributes
47+
pub const TCA_ACT_UNSPEC: u16 = 0;
48+
pub const TCA_ACT_KIND: u16 = 1;
49+
pub const TCA_ACT_OPTIONS: u16 = 2;
50+
pub const TCA_ACT_INDEX: u16 = 3;
51+
pub const TCA_ACT_STATS: u16 = 4;
52+
pub const TCA_ACT_PAD: u16 = 5;
53+
pub const TCA_ACT_COOKIE: u16 = 6;
54+
55+
//TODO(wllenyj): Why not subtract 1? See `linux/pkt_cls.h` for original definition.
56+
pub const TCA_ACT_MAX: u16 = 7;
57+
pub const TCA_OLD_COMPAT: u16 = TCA_ACT_MAX + 1;
58+
pub const TCA_ACT_MAX_PRIO: u16 = 32;
59+
pub const TCA_ACT_BIND: u16 = 1;
60+
pub const TCA_ACT_NOBIND: u16 = 0;
61+
pub const TCA_ACT_UNBIND: u16 = 1;
62+
pub const TCA_ACT_NOUNBIND: u16 = 0;
63+
pub const TCA_ACT_REPLACE: u16 = 1;
64+
pub const TCA_ACT_NOREPLACE: u16 = 0;
65+
66+
pub const TC_ACT_UNSPEC: i32 = -1;
67+
pub const TC_ACT_OK: i32 = 0;
68+
pub const TC_ACT_RECLASSIFY: i32 = 1;
69+
pub const TC_ACT_SHOT: i32 = 2;
70+
pub const TC_ACT_PIPE: i32 = 3;
71+
pub const TC_ACT_STOLEN: i32 = 4;
72+
pub const TC_ACT_QUEUED: i32 = 5;
73+
pub const TC_ACT_REPEAT: i32 = 6;
74+
pub const TC_ACT_REDIRECT: i32 = 7;
75+
pub const TC_ACT_TRAP: i32 = 8;
76+
77+
pub const TC_ACT_VALUE_MAX: i32 = TC_ACT_TRAP;
78+
79+
pub const TC_ACT_JUMP: i32 = 0x10000000;
80+
81+
pub const TCA_ACT_TAB: u16 = 1; // TCA_ROOT_TAB
82+
pub const TCAA_MAX: u16 = 1;
83+
84+
/// Mirred action attr
85+
pub const TCA_MIRRED_UNSPEC: u16 = 0;
86+
pub const TCA_MIRRED_TM: u16 = 1;
87+
pub const TCA_MIRRED_PARMS: u16 = 2;
88+
pub const TCA_MIRRED_PAD: u16 = 3;
89+
pub const TCA_MIRRED_MAX: u16 = TCA_MIRRED_PAD;
90+
91+
pub const TCA_EGRESS_REDIR: i32 = 1; /* packet redirect to EGRESS */
92+
pub const TCA_EGRESS_MIRROR: i32 = 2; /* mirror packet to EGRESS */
93+
pub const TCA_INGRESS_REDIR: i32 = 3; /* packet redirect to INGRESS */
94+
pub const TCA_INGRESS_MIRROR: i32 = 4; /* mirror packet to INGRESS */

netlink-packet-route/src/rtnl/tc/message.rs

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@
33
use anyhow::Context;
44

55
use crate::{
6-
nlas::tc::Nla,
7-
traits::{Emitable, Parseable},
6+
constants::*,
7+
nlas::{
8+
tc::{Nla, Stats, Stats2, StatsBuffer, TcOpt},
9+
DefaultNla,
10+
NlasIterator,
11+
},
12+
parsers::{parse_string, parse_u8},
13+
traits::{Emitable, Parseable, ParseableParametrized},
814
DecodeError,
915
TcMessageBuffer,
1016
TC_HEADER_LEN,
@@ -24,6 +30,17 @@ impl TcMessage {
2430
pub fn from_parts(header: TcHeader, nlas: Vec<Nla>) -> Self {
2531
TcMessage { header, nlas }
2632
}
33+
34+
/// Create a new `TcMessage` with the given index
35+
pub fn with_index(index: i32) -> Self {
36+
Self {
37+
header: TcHeader {
38+
index,
39+
..Default::default()
40+
},
41+
nlas: Vec::new(),
42+
}
43+
}
2744
}
2845

2946
#[derive(Debug, PartialEq, Eq, Clone, Default)]
@@ -90,8 +107,52 @@ impl<'a, T: AsRef<[u8]> + 'a> Parseable<TcMessageBuffer<&'a T>> for TcMessage {
90107
impl<'a, T: AsRef<[u8]> + 'a> Parseable<TcMessageBuffer<&'a T>> for Vec<Nla> {
91108
fn parse(buf: &TcMessageBuffer<&'a T>) -> Result<Self, DecodeError> {
92109
let mut nlas = vec![];
110+
let mut kind = String::new();
111+
93112
for nla_buf in buf.nlas() {
94-
nlas.push(Nla::parse(&nla_buf?)?);
113+
let buf = nla_buf.context("invalid tc nla")?;
114+
let payload = buf.value();
115+
let nla = match buf.kind() {
116+
TCA_UNSPEC => Nla::Unspec(payload.to_vec()),
117+
TCA_KIND => {
118+
kind = parse_string(payload).context("invalid TCA_KIND")?;
119+
Nla::Kind(kind.clone())
120+
}
121+
TCA_OPTIONS => {
122+
let mut nlas = vec![];
123+
for nla in NlasIterator::new(payload) {
124+
let nla = nla.context("invalid TCA_OPTIONS")?;
125+
nlas.push(
126+
TcOpt::parse_with_param(&nla, &kind)
127+
.context("failed to parse TCA_OPTIONS")?,
128+
)
129+
}
130+
Nla::Options(nlas)
131+
}
132+
TCA_STATS => Nla::Stats(
133+
Stats::parse(&StatsBuffer::new_checked(payload).context("invalid TCA_STATS")?)
134+
.context("failed to parse TCA_STATS")?,
135+
),
136+
TCA_XSTATS => Nla::XStats(payload.to_vec()),
137+
TCA_RATE => Nla::Rate(payload.to_vec()),
138+
TCA_FCNT => Nla::Fcnt(payload.to_vec()),
139+
TCA_STATS2 => {
140+
let mut nlas = vec![];
141+
for nla in NlasIterator::new(payload) {
142+
let nla = nla.context("invalid TCA_STATS2")?;
143+
nlas.push(Stats2::parse(&nla).context("failed to parse TCA_STATS2")?);
144+
}
145+
Nla::Stats2(nlas)
146+
}
147+
TCA_STAB => Nla::Stab(payload.to_vec()),
148+
TCA_CHAIN => Nla::Chain(payload.to_vec()),
149+
TCA_HW_OFFLOAD => {
150+
Nla::HwOffload(parse_u8(payload).context("failed to parse TCA_HW_OFFLOAD")?)
151+
}
152+
_ => Nla::Other(DefaultNla::parse(&buf).context("failed to parse tc nla")?),
153+
};
154+
155+
nlas.push(nla);
95156
}
96157
Ok(nlas)
97158
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
// SPDX-License-Identifier: MIT
22

33
mod buffer;
4+
pub mod constants;
45
mod message;
56
pub mod nlas;
67

78
pub use self::{buffer::*, message::*, nlas::*};
9+
10+
#[cfg(test)]
11+
mod test;

netlink-packet-route/src/rtnl/tc/nlas/mod.rs

Lines changed: 15 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,15 @@ pub use self::stats_queue::*;
99
mod stats_basic;
1010
pub use self::stats_basic::*;
1111

12+
mod options;
13+
pub use self::options::*;
14+
15+
mod qdisc;
16+
pub use self::qdisc::*;
17+
1218
use crate::{
1319
constants::*,
14-
nlas::{self, DefaultNla, NlaBuffer, NlasIterator},
15-
parsers::{parse_string, parse_u8},
20+
nlas::{self, DefaultNla, NlaBuffer},
1621
traits::{Emitable, Parseable},
1722
DecodeError,
1823
};
@@ -23,9 +28,9 @@ pub enum Nla {
2328
Unspec(Vec<u8>),
2429
/// Name of queueing discipline
2530
Kind(String),
26-
/// Qdisc-specific options follow
27-
Options(Vec<u8>),
28-
/// Qdisc statistics
31+
/// Options follow
32+
Options(Vec<TcOpt>),
33+
/// Statistics
2934
Stats(Stats),
3035
/// Module-specific statistics
3136
XStats(Vec<u8>),
@@ -45,20 +50,15 @@ impl nlas::Nla for Nla {
4550
use self::Nla::*;
4651
match *self {
4752
// Vec<u8>
48-
Unspec(ref bytes)
49-
| Options(ref bytes)
50-
| XStats(ref bytes)
51-
| Rate(ref bytes)
52-
| Fcnt(ref bytes)
53-
| Stab(ref bytes)
54-
| Chain(ref bytes) => bytes.len(),
53+
Unspec(ref bytes) | XStats(ref bytes) | Rate(ref bytes) | Fcnt(ref bytes)
54+
| Stab(ref bytes) | Chain(ref bytes) => bytes.len(),
5555
HwOffload(_) => 1,
5656
Stats2(ref thing) => thing.as_slice().buffer_len(),
5757
Stats(_) => STATS_LEN,
5858
Kind(ref string) => string.as_bytes().len() + 1,
59-
59+
Options(ref opt) => opt.as_slice().buffer_len(),
6060
// Defaults
61-
Other(ref attr) => attr.value_len(),
61+
Other(ref attr) => attr.value_len(),
6262
}
6363
}
6464

@@ -68,7 +68,6 @@ impl nlas::Nla for Nla {
6868
match *self {
6969
// Vec<u8>
7070
Unspec(ref bytes)
71-
| Options(ref bytes)
7271
| XStats(ref bytes)
7372
| Rate(ref bytes)
7473
| Fcnt(ref bytes)
@@ -83,6 +82,7 @@ impl nlas::Nla for Nla {
8382
buffer[..string.as_bytes().len()].copy_from_slice(string.as_bytes());
8483
buffer[string.as_bytes().len()] = 0;
8584
}
85+
Options(ref opt) => opt.as_slice().emit(buffer),
8686

8787
// Default
8888
Other(ref attr) => attr.emit_value(buffer),
@@ -108,32 +108,6 @@ impl nlas::Nla for Nla {
108108
}
109109
}
110110

111-
impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for Nla {
112-
fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
113-
let payload = buf.value();
114-
Ok(match buf.kind() {
115-
TCA_UNSPEC => Self::Unspec(payload.to_vec()),
116-
TCA_KIND => Self::Kind(parse_string(payload)?),
117-
TCA_OPTIONS => Self::Options(payload.to_vec()),
118-
TCA_STATS => Self::Stats(Stats::parse(&StatsBuffer::new_checked(payload)?)?),
119-
TCA_XSTATS => Self::XStats(payload.to_vec()),
120-
TCA_RATE => Self::Rate(payload.to_vec()),
121-
TCA_FCNT => Self::Fcnt(payload.to_vec()),
122-
TCA_STATS2 => {
123-
let mut nlas = vec![];
124-
for nla in NlasIterator::new(payload) {
125-
nlas.push(Stats2::parse(&(nla?))?);
126-
}
127-
Self::Stats2(nlas)
128-
}
129-
TCA_STAB => Self::Stab(payload.to_vec()),
130-
TCA_CHAIN => Self::Chain(payload.to_vec()),
131-
TCA_HW_OFFLOAD => Self::HwOffload(parse_u8(payload)?),
132-
_ => Self::Other(DefaultNla::parse(buf)?),
133-
})
134-
}
135-
}
136-
137111
#[derive(Debug, PartialEq, Eq, Clone)]
138112
pub enum Stats2 {
139113
StatsApp(Vec<u8>),
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// SPDX-License-Identifier: MIT
2+
use crate::{
3+
nlas::{self, DefaultNla, NlaBuffer},
4+
tc::ingress,
5+
traits::{Parseable, ParseableParametrized},
6+
DecodeError,
7+
};
8+
9+
#[derive(Debug, PartialEq, Eq, Clone)]
10+
pub enum TcOpt {
11+
// Qdisc specific options
12+
Ingress,
13+
// Other options
14+
Other(DefaultNla),
15+
}
16+
17+
impl nlas::Nla for TcOpt {
18+
fn value_len(&self) -> usize {
19+
match self {
20+
Self::Ingress => 0,
21+
Self::Other(o) => o.value_len(),
22+
}
23+
}
24+
25+
fn emit_value(&self, buffer: &mut [u8]) {
26+
match self {
27+
Self::Ingress => unreachable!(),
28+
Self::Other(o) => o.emit_value(buffer),
29+
}
30+
}
31+
32+
fn kind(&self) -> u16 {
33+
match self {
34+
Self::Ingress => unreachable!(),
35+
Self::Other(o) => o.kind(),
36+
}
37+
}
38+
}
39+
40+
impl<'a, T, S> ParseableParametrized<NlaBuffer<&'a T>, S> for TcOpt
41+
where
42+
T: AsRef<[u8]> + ?Sized,
43+
S: AsRef<str>,
44+
{
45+
fn parse_with_param(buf: &NlaBuffer<&'a T>, kind: S) -> Result<Self, DecodeError> {
46+
Ok(match kind.as_ref() {
47+
ingress::KIND => TcOpt::Ingress,
48+
_ => Self::Other(DefaultNla::parse(buf)?),
49+
})
50+
}
51+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pub mod ingress {
4+
pub const KIND: &str = "ingress";
5+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
//#[derive(Debug, PartialEq, Eq, Clone)]
4+
//pub enum Qdisc {
5+
// Prio(Prio),
6+
// Ingress,
7+
//}
8+
//
9+
//pub const TC_PRIO_MAX: usize = 15;
10+
//#[derive(Debug, PartialEq, Eq, Clone)]
11+
//pub struct Prio {
12+
// // Number of bands
13+
// bands: i32,
14+
// // Map: logical priority -> PRIO band
15+
// priomap: [u8; TC_PRIO_MAX + 1],
16+
//}

0 commit comments

Comments
 (0)