Skip to content

Commit ca1a798

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

File tree

8 files changed

+747
-12
lines changed

8 files changed

+747
-12
lines changed

.cargo/config.toml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[env]
2+
RUST_TEST_THREADS = "1"

src/handle.rs

+8
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

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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+
/// A request to add a new traffic control action
17+
#[derive(Debug, Clone)]
18+
#[must_use = "builder"]
19+
pub struct TrafficActionNewRequest {
20+
handle: Handle,
21+
message: TcActionMessage,
22+
}
23+
24+
impl TrafficActionNewRequest {
25+
pub(crate) fn new(handle: Handle) -> Self {
26+
Self {
27+
handle,
28+
message: TcActionMessage::default(),
29+
}
30+
}
31+
32+
/// Specifies the action to add
33+
pub fn action(mut self, action: TcAction) -> Self {
34+
self.message
35+
.attributes
36+
.push(TcActionMessageAttribute::Actions(vec![action]));
37+
self
38+
}
39+
40+
/// Execute the request
41+
#[must_use = "builder"]
42+
pub fn execute(
43+
self,
44+
) -> impl TryStream<Ok = TcActionMessage, Error = Error> {
45+
let Self {
46+
mut handle,
47+
message,
48+
} = self;
49+
50+
let mut req = NetlinkMessage::from(
51+
RouteNetlinkMessage::NewTrafficAction(message),
52+
);
53+
req.header.message_type = RTM_NEWACTION;
54+
req.header.flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL;
55+
56+
match handle.request(req) {
57+
Ok(response) => Either::Left(response.map(move |msg| {
58+
Ok(try_rtnl!(msg, RouteNetlinkMessage::NewTrafficAction))
59+
})),
60+
Err(err) => Either::Right(
61+
future::err::<TcActionMessage, Error>(err).into_stream(),
62+
),
63+
}
64+
}
65+
}

src/traffic_control/del_action.rs

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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+
/// A request to delete a traffic control action
13+
#[must_use]
14+
pub struct TrafficActionDelRequest {
15+
handle: Handle,
16+
message: TcActionMessage,
17+
}
18+
19+
impl TrafficActionDelRequest {
20+
pub(crate) fn new(handle: Handle) -> Self {
21+
TrafficActionDelRequest {
22+
handle,
23+
message: TcActionMessage::default(),
24+
}
25+
}
26+
27+
/// Specifies the action to delete
28+
pub fn action(mut self, action: TcAction) -> Self {
29+
self.message
30+
.attributes
31+
.push(TcActionMessageAttribute::Actions(vec![action]));
32+
self
33+
}
34+
35+
/// Execute the request
36+
pub async fn execute(self) -> Result<(), Error> {
37+
let TrafficActionDelRequest {
38+
mut handle,
39+
message,
40+
} = self;
41+
42+
let mut req = NetlinkMessage::from(
43+
RouteNetlinkMessage::DelTrafficAction(message),
44+
);
45+
req.header.flags = NLM_F_REQUEST | NLM_F_ACK;
46+
47+
let mut response = handle.request(req)?;
48+
while let Some(message) = response.next().await {
49+
try_nl!(message)
50+
}
51+
Ok(())
52+
}
53+
54+
/// Return a mutable reference to the request
55+
pub fn message_mut(&mut self) -> &mut TcActionMessage {
56+
&mut self.message
57+
}
58+
}

src/traffic_control/get.rs

+103-2
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

+51-4
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,46 @@ 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+
/// Create a new traffic action manipulation handle.
149+
pub fn new(handle: Handle) -> Self {
150+
TrafficActionHandle { handle }
151+
}
152+
153+
/// Retrieve the list of actions.
154+
///
155+
/// Equivalent to
156+
///
157+
/// ```bash
158+
/// tc action list actions $act_type
159+
/// ```
160+
pub fn get(&mut self) -> TrafficActionGetRequest {
161+
TrafficActionGetRequest::new(self.handle.clone())
162+
}
163+
164+
/// Add a new action.
165+
///
166+
/// Equivalent to
167+
/// ```bash
168+
/// tc action add action $act_type $act_options index $index
169+
/// ```
170+
pub fn add(&mut self) -> TrafficActionNewRequest {
171+
TrafficActionNewRequest::new(self.handle.clone())
172+
}
173+
174+
/// Delete an action.
175+
///
176+
/// Equivalent to
177+
/// ```bash
178+
/// tc action del action $act_type index $index
179+
/// ```
180+
pub fn del(&mut self) -> TrafficActionDelRequest {
181+
TrafficActionDelRequest::new(self.handle.clone())
182+
}
183+
}

src/traffic_control/mod.rs

+17-5
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,31 @@
11
// SPDX-License-Identifier: MIT
22

3-
mod handle;
3+
//! Traffic control manipulation utilities.
4+
//! See [`tc`].
5+
//!
6+
//! [`tc`]: https://man7.org/linux/man-pages/man8/tc.8.html
7+
8+
pub use self::add_action::*;
9+
pub use self::add_filter::*;
10+
pub use self::add_qdisc::*;
11+
pub use self::del_action::*;
12+
pub use self::del_qdisc::*;
13+
pub use self::get::*;
414
pub use self::handle::*;
515

16+
mod handle;
17+
618
mod get;
7-
pub use self::get::*;
819

920
mod add_qdisc;
10-
pub use self::add_qdisc::*;
1121

1222
mod del_qdisc;
13-
pub use self::del_qdisc::*;
1423

1524
mod add_filter;
16-
pub use self::add_filter::*;
25+
26+
mod del_action;
27+
28+
mod add_action;
1729

1830
#[cfg(test)]
1931
mod test;

0 commit comments

Comments
 (0)