Skip to content

Commit 9f8366c

Browse files
committed
Add support for tc-actions
1 parent 6896bae commit 9f8366c

File tree

8 files changed

+709
-7
lines changed

8 files changed

+709
-7
lines changed

.cargo/config.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[env]
2+
RUST_TEST_THREADS = "1"

src/handle.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use netlink_proto::{sys::SocketAddr, ConnectionHandle};
77

88
use crate::{
99
AddressHandle, Error, LinkHandle, NeighbourHandle, RouteHandle, RuleHandle,
10+
TrafficActionHandle,
1011
};
1112
#[cfg(not(target_os = "freebsd"))]
1213
use crate::{
@@ -98,4 +99,11 @@ impl Handle {
9899
pub fn traffic_chain(&self, ifindex: i32) -> TrafficChainHandle {
99100
TrafficChainHandle::new(self.clone(), ifindex)
100101
}
102+
103+
/// Create a new handle, specifically for traffic control action requests
104+
/// (equivalent to `tc actions list action <action_name>` commands)
105+
#[cfg(not(target_os = "freebsd"))]
106+
pub fn traffic_action(&self) -> TrafficActionHandle {
107+
TrafficActionHandle::new(self.clone())
108+
}
101109
}

src/traffic_control/add_action.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use futures::future::Either;
4+
use futures::{future, FutureExt, StreamExt, TryStream};
5+
use netlink_packet_core::{
6+
NetlinkMessage, NLM_F_ACK, NLM_F_EXCL, NLM_F_REQUEST,
7+
};
8+
use netlink_packet_route::tc::{
9+
TcAction, TcActionMessage, TcActionMessageAttribute,
10+
};
11+
use netlink_packet_route::RouteNetlinkMessage;
12+
use nix::libc::RTM_NEWACTION;
13+
14+
use crate::{try_rtnl, Error, Handle};
15+
16+
#[derive(Debug, Clone)]
17+
pub struct TrafficActionNewRequest {
18+
handle: Handle,
19+
message: TcActionMessage,
20+
}
21+
22+
impl TrafficActionNewRequest {
23+
pub(crate) fn new(handle: Handle) -> Self {
24+
Self {
25+
handle,
26+
message: TcActionMessage::default(),
27+
}
28+
}
29+
30+
pub fn action(mut self, action: TcAction) -> Self {
31+
self.message
32+
.attributes
33+
.push(TcActionMessageAttribute::Actions(vec![action]));
34+
self
35+
}
36+
37+
/// Execute the request
38+
pub async fn execute(
39+
self,
40+
) -> impl TryStream<Ok = TcActionMessage, Error = Error> {
41+
let Self {
42+
mut handle,
43+
message,
44+
} = self;
45+
46+
let mut req = NetlinkMessage::from(
47+
RouteNetlinkMessage::NewTrafficAction(message),
48+
);
49+
req.header.message_type = RTM_NEWACTION;
50+
req.header.flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL;
51+
52+
match handle.request(req) {
53+
Ok(response) => Either::Left(response.map(move |msg| {
54+
Ok(try_rtnl!(msg, RouteNetlinkMessage::NewTrafficAction))
55+
})),
56+
Err(err) => Either::Right(
57+
future::err::<TcActionMessage, Error>(err).into_stream(),
58+
),
59+
}
60+
}
61+
}

src/traffic_control/del_action.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use futures::StreamExt;
4+
use netlink_packet_core::{NetlinkMessage, NLM_F_ACK, NLM_F_REQUEST};
5+
use netlink_packet_route::tc::{
6+
TcAction, TcActionMessage, TcActionMessageAttribute,
7+
};
8+
use netlink_packet_route::RouteNetlinkMessage;
9+
10+
use crate::{try_nl, Error, Handle};
11+
12+
pub struct TrafficActionDelRequest {
13+
handle: Handle,
14+
message: TcActionMessage,
15+
}
16+
17+
impl TrafficActionDelRequest {
18+
pub(crate) fn new(handle: Handle) -> Self {
19+
TrafficActionDelRequest {
20+
handle,
21+
message: TcActionMessage::default(),
22+
}
23+
}
24+
25+
pub fn action(mut self, action: TcAction) -> Self {
26+
self.message
27+
.attributes
28+
.push(TcActionMessageAttribute::Actions(vec![action]));
29+
self
30+
}
31+
32+
// Execute the request
33+
pub async fn execute(self) -> Result<(), Error> {
34+
let TrafficActionDelRequest {
35+
mut handle,
36+
message,
37+
} = self;
38+
39+
let mut req = NetlinkMessage::from(
40+
RouteNetlinkMessage::DelTrafficAction(message),
41+
);
42+
req.header.flags = NLM_F_REQUEST | NLM_F_ACK;
43+
44+
let mut response = handle.request(req)?;
45+
while let Some(message) = response.next().await {
46+
try_nl!(message)
47+
}
48+
Ok(())
49+
}
50+
51+
/// Return a mutable reference to the request
52+
pub fn message_mut(&mut self) -> &mut TcActionMessage {
53+
&mut self.message
54+
}
55+
}

src/traffic_control/get.rs

Lines changed: 103 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
// SPDX-License-Identifier: MIT
2-
32
use futures::{
43
future::{self, Either},
54
stream::{StreamExt, TryStream},
65
FutureExt,
76
};
87
use netlink_packet_core::{NetlinkMessage, NLM_F_DUMP, NLM_F_REQUEST};
8+
use netlink_packet_route::tc::{
9+
TcAction, TcActionAttribute, TcActionMessage, TcActionMessageAttribute,
10+
TcActionMessageFlags, TcActionMessageFlagsWithSelector,
11+
};
912
use netlink_packet_route::{
1013
tc::{TcHandle, TcMessage},
11-
RouteNetlinkMessage,
14+
AddressFamily, RouteNetlinkMessage,
1215
};
1316

1417
use crate::{try_rtnl, Error, Handle};
@@ -168,3 +171,101 @@ impl TrafficChainGetRequest {
168171
}
169172
}
170173
}
174+
175+
/// Request to retrieve traffic actions from the kernel.
176+
/// Equivalent to
177+
///
178+
/// ```bash
179+
/// tc actions list action $action_type
180+
/// ```
181+
#[derive(Debug, Clone)]
182+
#[must_use]
183+
pub struct TrafficActionGetRequest {
184+
handle: Handle,
185+
message: TcActionMessage,
186+
}
187+
188+
/// The kind of traffic action.
189+
///
190+
/// This is a list of known traffic actions.
191+
/// If the kernel returns an unknown action, it will be represented as
192+
/// `Other(String)`.
193+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
194+
pub enum TrafficActionKind {
195+
/// Used for mirroring and redirecting packets.
196+
Mirror,
197+
/// Used for network address translation.
198+
Nat,
199+
/// Other action type not yet directly supported by this library.
200+
Other(String),
201+
}
202+
203+
impl<T: AsRef<str>> From<T> for TrafficActionKind {
204+
fn from(kind: T) -> Self {
205+
match kind.as_ref() {
206+
"mirred" => TrafficActionKind::Mirror,
207+
"nat" => TrafficActionKind::Nat,
208+
_ => TrafficActionKind::Other(kind.as_ref().into()),
209+
}
210+
}
211+
}
212+
213+
impl From<TrafficActionKind> for String {
214+
fn from(kind: TrafficActionKind) -> Self {
215+
match kind {
216+
TrafficActionKind::Mirror => "mirred".into(),
217+
TrafficActionKind::Nat => "nat".into(),
218+
TrafficActionKind::Other(kind) => kind,
219+
}
220+
}
221+
}
222+
223+
impl TrafficActionGetRequest {
224+
pub(crate) fn new(handle: Handle) -> Self {
225+
let mut message = TcActionMessage::default();
226+
message.header.family = AddressFamily::Unspec;
227+
let flags = TcActionMessageAttribute::Flags(
228+
TcActionMessageFlagsWithSelector::new(
229+
TcActionMessageFlags::LargeDump,
230+
),
231+
);
232+
message.attributes.push(flags);
233+
Self { handle, message }
234+
}
235+
236+
/// Specify the kind of the action to retrieve.
237+
pub fn kind<T: Into<TrafficActionKind>>(mut self, kind: T) -> Self {
238+
let mut tc_action = TcAction::default();
239+
tc_action
240+
.attributes
241+
.push(TcActionAttribute::Kind(String::from(kind.into())));
242+
let acts = TcActionMessageAttribute::Actions(vec![tc_action]);
243+
self.message.attributes.push(acts);
244+
self
245+
}
246+
247+
/// Execute the request
248+
#[must_use]
249+
pub fn execute(
250+
self,
251+
) -> impl TryStream<Ok = TcActionMessage, Error = Error> {
252+
let Self {
253+
mut handle,
254+
message,
255+
} = self;
256+
257+
let mut req = NetlinkMessage::from(
258+
RouteNetlinkMessage::GetTrafficAction(message),
259+
);
260+
req.header.flags = NLM_F_REQUEST | NLM_F_DUMP;
261+
262+
match handle.request(req) {
263+
Ok(response) => Either::Left(response.map(move |msg| {
264+
Ok(try_rtnl!(msg, RouteNetlinkMessage::GetTrafficAction))
265+
})),
266+
Err(e) => Either::Right(
267+
future::err::<TcActionMessage, Error>(e).into_stream(),
268+
),
269+
}
270+
}
271+
}

src/traffic_control/handle.rs

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

3+
use netlink_packet_core::{NLM_F_CREATE, NLM_F_EXCL, NLM_F_REPLACE};
4+
use netlink_packet_route::tc::TcMessage;
5+
6+
use crate::{
7+
Handle, TrafficActionDelRequest, TrafficActionGetRequest,
8+
TrafficActionNewRequest,
9+
};
10+
311
use super::{
412
QDiscDelRequest, QDiscGetRequest, QDiscNewRequest, TrafficChainGetRequest,
513
TrafficClassGetRequest, TrafficFilterGetRequest, TrafficFilterNewRequest,
614
};
715

8-
use crate::Handle;
9-
use netlink_packet_core::{NLM_F_CREATE, NLM_F_EXCL, NLM_F_REPLACE};
10-
use netlink_packet_route::tc::TcMessage;
11-
1216
pub struct QDiscHandle(Handle);
1317

1418
impl QDiscHandle {
@@ -134,3 +138,26 @@ impl TrafficChainHandle {
134138
TrafficChainGetRequest::new(self.handle.clone(), self.ifindex)
135139
}
136140
}
141+
142+
#[non_exhaustive]
143+
pub struct TrafficActionHandle {
144+
handle: Handle,
145+
}
146+
147+
impl TrafficActionHandle {
148+
pub fn new(handle: Handle) -> Self {
149+
TrafficActionHandle { handle }
150+
}
151+
152+
pub fn get(&mut self) -> TrafficActionGetRequest {
153+
TrafficActionGetRequest::new(self.handle.clone())
154+
}
155+
156+
pub fn add(&mut self) -> TrafficActionNewRequest {
157+
TrafficActionNewRequest::new(self.handle.clone())
158+
}
159+
160+
pub fn del(&mut self) -> TrafficActionDelRequest {
161+
TrafficActionDelRequest::new(self.handle.clone())
162+
}
163+
}

src/traffic_control/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,11 @@ pub use self::del_qdisc::*;
1515
mod add_filter;
1616
pub use self::add_filter::*;
1717

18+
mod del_action;
19+
pub use self::del_action::*;
20+
21+
mod add_action;
22+
pub use self::add_action::*;
23+
1824
#[cfg(test)]
1925
mod test;

0 commit comments

Comments
 (0)