Skip to content

DRAFT: Add support for tc-actions #64

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

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
2 changes: 2 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[env]
RUST_TEST_THREADS = "1"
8 changes: 8 additions & 0 deletions src/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use netlink_proto::{sys::SocketAddr, ConnectionHandle};

use crate::{
AddressHandle, Error, LinkHandle, NeighbourHandle, RouteHandle, RuleHandle,
TrafficActionHandle,
};
#[cfg(not(target_os = "freebsd"))]
use crate::{
Expand Down Expand Up @@ -98,4 +99,11 @@ impl Handle {
pub fn traffic_chain(&self, ifindex: i32) -> TrafficChainHandle {
TrafficChainHandle::new(self.clone(), ifindex)
}

/// Create a new handle, specifically for traffic control action requests
/// (equivalent to `tc actions list action <action_name>` commands)
#[cfg(not(target_os = "freebsd"))]
pub fn traffic_action(&self) -> TrafficActionHandle {
TrafficActionHandle::new(self.clone())
}
}
65 changes: 65 additions & 0 deletions src/traffic_control/add_action.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: MIT

use futures::future::Either;
use futures::{future, FutureExt, StreamExt, TryStream};
use netlink_packet_core::{
NetlinkMessage, NLM_F_ACK, NLM_F_EXCL, NLM_F_REQUEST,
};
use netlink_packet_route::tc::{
TcAction, TcActionMessage, TcActionMessageAttribute,
};
use netlink_packet_route::RouteNetlinkMessage;
use nix::libc::RTM_NEWACTION;

use crate::{try_rtnl, Error, Handle};

/// A request to add a new traffic control action
#[derive(Debug, Clone)]
#[must_use = "builder"]
pub struct TrafficActionNewRequest {
handle: Handle,
message: TcActionMessage,
}

impl TrafficActionNewRequest {
pub(crate) fn new(handle: Handle) -> Self {
Self {
handle,
message: TcActionMessage::default(),
}
}

/// Specifies the action to add
pub fn action(mut self, action: TcAction) -> Self {
self.message
.attributes
.push(TcActionMessageAttribute::Actions(vec![action]));
self
}

/// Execute the request
#[must_use = "builder"]
pub fn execute(
self,
) -> impl TryStream<Ok = TcActionMessage, Error = Error> {
let Self {
mut handle,
message,
} = self;

let mut req = NetlinkMessage::from(
RouteNetlinkMessage::NewTrafficAction(message),
);
req.header.message_type = RTM_NEWACTION;
req.header.flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL;

match handle.request(req) {
Ok(response) => Either::Left(response.map(move |msg| {
Ok(try_rtnl!(msg, RouteNetlinkMessage::NewTrafficAction))
})),
Err(err) => Either::Right(
future::err::<TcActionMessage, Error>(err).into_stream(),
),
}
}
}
58 changes: 58 additions & 0 deletions src/traffic_control/del_action.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: MIT

use futures::StreamExt;
use netlink_packet_core::{NetlinkMessage, NLM_F_ACK, NLM_F_REQUEST};
use netlink_packet_route::tc::{
TcAction, TcActionMessage, TcActionMessageAttribute,
};
use netlink_packet_route::RouteNetlinkMessage;

use crate::{try_nl, Error, Handle};

/// A request to delete a traffic control action
#[must_use]
pub struct TrafficActionDelRequest {
handle: Handle,
message: TcActionMessage,
}

impl TrafficActionDelRequest {
pub(crate) fn new(handle: Handle) -> Self {
TrafficActionDelRequest {
handle,
message: TcActionMessage::default(),
}
}

/// Specifies the action to delete
pub fn action(mut self, action: TcAction) -> Self {
self.message
.attributes
.push(TcActionMessageAttribute::Actions(vec![action]));
self
}

/// Execute the request
pub async fn execute(self) -> Result<(), Error> {
let TrafficActionDelRequest {
mut handle,
message,
} = self;

let mut req = NetlinkMessage::from(
RouteNetlinkMessage::DelTrafficAction(message),
);
req.header.flags = NLM_F_REQUEST | NLM_F_ACK;

let mut response = handle.request(req)?;
while let Some(message) = response.next().await {
try_nl!(message)
}
Ok(())
}

/// Return a mutable reference to the request
pub fn message_mut(&mut self) -> &mut TcActionMessage {
&mut self.message
}
}
105 changes: 103 additions & 2 deletions src/traffic_control/get.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
// SPDX-License-Identifier: MIT

use futures::{
future::{self, Either},
stream::{StreamExt, TryStream},
FutureExt,
};
use netlink_packet_core::{NetlinkMessage, NLM_F_DUMP, NLM_F_REQUEST};
use netlink_packet_route::tc::{
TcAction, TcActionAttribute, TcActionMessage, TcActionMessageAttribute,
TcActionMessageFlags, TcActionMessageFlagsWithSelector,
};
use netlink_packet_route::{
tc::{TcHandle, TcMessage},
RouteNetlinkMessage,
AddressFamily, RouteNetlinkMessage,
};

use crate::{try_rtnl, Error, Handle};
Expand Down Expand Up @@ -168,3 +171,101 @@ impl TrafficChainGetRequest {
}
}
}

/// Request to retrieve traffic actions from the kernel.
/// Equivalent to
///
/// ```bash
/// tc actions list action $action_type
/// ```
#[derive(Debug, Clone)]
#[must_use]
pub struct TrafficActionGetRequest {
handle: Handle,
message: TcActionMessage,
}

/// The kind of traffic action.
///
/// This is a list of known traffic actions.
/// If the kernel returns an unknown action, it will be represented as
/// `Other(String)`.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum TrafficActionKind {
/// Used for mirroring and redirecting packets.
Mirror,
/// Used for network address translation.
Nat,
/// Other action type not yet directly supported by this library.
Other(String),
}

impl<T: AsRef<str>> From<T> for TrafficActionKind {
fn from(kind: T) -> Self {
match kind.as_ref() {
"mirred" => TrafficActionKind::Mirror,
"nat" => TrafficActionKind::Nat,
_ => TrafficActionKind::Other(kind.as_ref().into()),
}
}
}

impl From<TrafficActionKind> for String {
fn from(kind: TrafficActionKind) -> Self {
match kind {
TrafficActionKind::Mirror => "mirred".into(),
TrafficActionKind::Nat => "nat".into(),
TrafficActionKind::Other(kind) => kind,
}
}
}

impl TrafficActionGetRequest {
pub(crate) fn new(handle: Handle) -> Self {
let mut message = TcActionMessage::default();
message.header.family = AddressFamily::Unspec;
let flags = TcActionMessageAttribute::Flags(
TcActionMessageFlagsWithSelector::new(
TcActionMessageFlags::LargeDump,
),
);
message.attributes.push(flags);
Self { handle, message }
}

/// Specify the kind of the action to retrieve.
pub fn kind<T: Into<TrafficActionKind>>(mut self, kind: T) -> Self {
let mut tc_action = TcAction::default();
tc_action
.attributes
.push(TcActionAttribute::Kind(String::from(kind.into())));
let acts = TcActionMessageAttribute::Actions(vec![tc_action]);
self.message.attributes.push(acts);
self
}

/// Execute the request
#[must_use]
pub fn execute(
self,
) -> impl TryStream<Ok = TcActionMessage, Error = Error> {
let Self {
mut handle,
message,
} = self;

let mut req = NetlinkMessage::from(
RouteNetlinkMessage::GetTrafficAction(message),
);
req.header.flags = NLM_F_REQUEST | NLM_F_DUMP;

match handle.request(req) {
Ok(response) => Either::Left(response.map(move |msg| {
Ok(try_rtnl!(msg, RouteNetlinkMessage::GetTrafficAction))
})),
Err(e) => Either::Right(
future::err::<TcActionMessage, Error>(e).into_stream(),
),
}
}
}
55 changes: 51 additions & 4 deletions src/traffic_control/handle.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
// SPDX-License-Identifier: MIT

use netlink_packet_core::{NLM_F_CREATE, NLM_F_EXCL, NLM_F_REPLACE};
use netlink_packet_route::tc::TcMessage;

use crate::{
Handle, TrafficActionDelRequest, TrafficActionGetRequest,
TrafficActionNewRequest,
};

use super::{
QDiscDelRequest, QDiscGetRequest, QDiscNewRequest, TrafficChainGetRequest,
TrafficClassGetRequest, TrafficFilterGetRequest, TrafficFilterNewRequest,
};

use crate::Handle;
use netlink_packet_core::{NLM_F_CREATE, NLM_F_EXCL, NLM_F_REPLACE};
use netlink_packet_route::tc::TcMessage;

pub struct QDiscHandle(Handle);

impl QDiscHandle {
Expand Down Expand Up @@ -134,3 +138,46 @@ impl TrafficChainHandle {
TrafficChainGetRequest::new(self.handle.clone(), self.ifindex)
}
}

#[non_exhaustive]
pub struct TrafficActionHandle {
handle: Handle,
}

impl TrafficActionHandle {
/// Create a new traffic action manipulation handle.
pub fn new(handle: Handle) -> Self {
TrafficActionHandle { handle }
}

/// Retrieve the list of actions.
///
/// Equivalent to
///
/// ```bash
/// tc action list actions $act_type
/// ```
pub fn get(&mut self) -> TrafficActionGetRequest {
TrafficActionGetRequest::new(self.handle.clone())
}

/// Add a new action.
///
/// Equivalent to
/// ```bash
/// tc action add action $act_type $act_options index $index
/// ```
pub fn add(&mut self) -> TrafficActionNewRequest {
TrafficActionNewRequest::new(self.handle.clone())
}

/// Delete an action.
///
/// Equivalent to
/// ```bash
/// tc action del action $act_type index $index
/// ```
pub fn del(&mut self) -> TrafficActionDelRequest {
TrafficActionDelRequest::new(self.handle.clone())
}
}
22 changes: 17 additions & 5 deletions src/traffic_control/mod.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
// SPDX-License-Identifier: MIT

mod handle;
//! Traffic control manipulation utilities.
//! See [`tc`].
//!
//! [`tc`]: https://man7.org/linux/man-pages/man8/tc.8.html

pub use self::add_action::*;
pub use self::add_filter::*;
pub use self::add_qdisc::*;
pub use self::del_action::*;
pub use self::del_qdisc::*;
pub use self::get::*;
pub use self::handle::*;

mod handle;

mod get;
pub use self::get::*;

mod add_qdisc;
pub use self::add_qdisc::*;

mod del_qdisc;
pub use self::del_qdisc::*;

mod add_filter;
pub use self::add_filter::*;

mod del_action;

mod add_action;

#[cfg(test)]
mod test;
Loading