use std::net::Ipv4Addr;
use super::{
builder::MessageBuilder,
error::{Error, Result},
types::tc::action::{
self, TcGen, connmark, csum, ct, gact, mirred, nat, pedit, police, sample, tunnel_key, vlan,
},
};
pub trait ActionConfig: Send + Sync {
fn kind(&self) -> &'static str;
fn write_options(&self, builder: &mut MessageBuilder) -> Result<()>;
}
#[derive(Debug, Clone)]
pub struct GactAction {
action: i32,
prob_type: u16,
prob_val: u16,
prob_action: i32,
}
impl GactAction {
pub fn new(action: i32) -> Self {
Self {
action,
prob_type: gact::PGACT_NONE,
prob_val: 0,
prob_action: action::TC_ACT_OK,
}
}
pub fn drop() -> Self {
Self::new(action::TC_ACT_SHOT)
}
pub fn pass() -> Self {
Self::new(action::TC_ACT_OK)
}
pub fn pipe() -> Self {
Self::new(action::TC_ACT_PIPE)
}
pub fn reclassify() -> Self {
Self::new(action::TC_ACT_RECLASSIFY)
}
pub fn stolen() -> Self {
Self::new(action::TC_ACT_STOLEN)
}
pub fn goto_chain(chain: u32) -> Self {
use super::types::tc::action::tc_act_goto_chain;
Self::new(tc_act_goto_chain(chain))
}
pub fn random(mut self, percent: u16, action: i32) -> Self {
self.prob_type = gact::PGACT_NETRAND;
self.prob_val = ((percent as u32 * 65535) / 100).min(65535) as u16;
self.prob_action = action;
self
}
pub fn random_drop(self, percent: u16) -> Self {
self.random(percent, action::TC_ACT_SHOT)
}
pub fn deterministic(mut self, one_in_n: u16, action: i32) -> Self {
self.prob_type = gact::PGACT_DETERM;
self.prob_val = one_in_n;
self.prob_action = action;
self
}
pub fn build(self) -> Self {
self
}
}
impl ActionConfig for GactAction {
fn kind(&self) -> &'static str {
"gact"
}
fn write_options(&self, builder: &mut MessageBuilder) -> Result<()> {
let parms = gact::TcGact::new(self.action);
builder.append_attr(gact::TCA_GACT_PARMS, parms.as_bytes());
if self.prob_type != gact::PGACT_NONE {
let prob = gact::TcGactP::new(self.prob_type, self.prob_val, self.prob_action);
builder.append_attr(gact::TCA_GACT_PROB, prob.as_bytes());
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct MirredAction {
eaction: i32,
ifindex: u32,
action: i32,
}
impl MirredAction {
fn new_with_ifindex(eaction: i32, ifindex: u32) -> Self {
let action = if eaction == mirred::TCA_EGRESS_REDIR || eaction == mirred::TCA_INGRESS_REDIR
{
action::TC_ACT_STOLEN
} else {
action::TC_ACT_PIPE
};
Self {
eaction,
ifindex,
action,
}
}
pub fn redirect_by_index(ifindex: u32) -> Self {
Self::new_with_ifindex(mirred::TCA_EGRESS_REDIR, ifindex)
}
pub fn mirror_by_index(ifindex: u32) -> Self {
Self::new_with_ifindex(mirred::TCA_EGRESS_MIRROR, ifindex)
}
pub fn ingress_redirect_by_index(ifindex: u32) -> Self {
Self::new_with_ifindex(mirred::TCA_INGRESS_REDIR, ifindex)
}
pub fn ingress_mirror_by_index(ifindex: u32) -> Self {
Self::new_with_ifindex(mirred::TCA_INGRESS_MIRROR, ifindex)
}
pub fn action(mut self, action: i32) -> Self {
self.action = action;
self
}
pub fn pipe(mut self) -> Self {
self.action = action::TC_ACT_PIPE;
self
}
pub fn build(self) -> Self {
self
}
}
impl ActionConfig for MirredAction {
fn kind(&self) -> &'static str {
"mirred"
}
fn write_options(&self, builder: &mut MessageBuilder) -> Result<()> {
let parms = mirred::TcMirred::new(self.eaction, self.ifindex, self.action);
builder.append_attr(mirred::TCA_MIRRED_PARMS, parms.as_bytes());
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct PoliceAction {
rate: u64,
peakrate: Option<u64>,
burst: u32,
mtu: u32,
conform_action: i32,
exceed_action: i32,
avrate: Option<u32>,
}
impl Default for PoliceAction {
fn default() -> Self {
Self::new()
}
}
impl PoliceAction {
pub fn new() -> Self {
Self {
rate: 0,
peakrate: None,
burst: 0,
mtu: 2047,
conform_action: action::TC_ACT_OK,
exceed_action: action::TC_ACT_SHOT,
avrate: None,
}
}
pub fn rate(mut self, bytes_per_sec: u64) -> Self {
self.rate = bytes_per_sec;
self
}
pub fn rate_bps(mut self, bits_per_sec: u64) -> Self {
self.rate = bits_per_sec / 8;
self
}
pub fn peakrate(mut self, bytes_per_sec: u64) -> Self {
self.peakrate = Some(bytes_per_sec);
self
}
pub fn burst(mut self, bytes: u32) -> Self {
self.burst = bytes;
self
}
pub fn mtu(mut self, mtu: u32) -> Self {
self.mtu = mtu;
self
}
pub fn conform(mut self, action: i32) -> Self {
self.conform_action = action;
self
}
pub fn conform_pass(mut self) -> Self {
self.conform_action = action::TC_ACT_OK;
self
}
pub fn conform_pipe(mut self) -> Self {
self.conform_action = action::TC_ACT_PIPE;
self
}
pub fn exceed(mut self, action: i32) -> Self {
self.exceed_action = action;
self
}
pub fn exceed_drop(mut self) -> Self {
self.exceed_action = action::TC_ACT_SHOT;
self
}
pub fn exceed_reclassify(mut self) -> Self {
self.exceed_action = action::TC_ACT_RECLASSIFY;
self
}
pub fn exceed_pipe(mut self) -> Self {
self.exceed_action = action::TC_ACT_PIPE;
self
}
pub fn avrate(mut self, bytes_per_sec: u32) -> Self {
self.avrate = Some(bytes_per_sec);
self
}
pub fn build(self) -> Self {
self
}
}
impl ActionConfig for PoliceAction {
fn kind(&self) -> &'static str {
"police"
}
fn write_options(&self, builder: &mut MessageBuilder) -> Result<()> {
use super::types::tc::qdisc::TcRateSpec;
let peakrate = match self.peakrate {
Some(pr) => TcRateSpec::new(pr.min(u32::MAX as u64) as u32),
None => TcRateSpec::default(),
};
let parms = police::TcPolice {
rate: TcRateSpec::new(self.rate.min(u32::MAX as u64) as u32),
burst: self.burst,
mtu: self.mtu,
action: self.exceed_action,
peakrate,
..Default::default()
};
builder.append_attr(police::TCA_POLICE_TBF, parms.as_bytes());
if self.rate > u32::MAX as u64 {
builder.append_attr(police::TCA_POLICE_RATE64, &self.rate.to_ne_bytes());
}
if let Some(pr) = self.peakrate
&& pr > u32::MAX as u64
{
builder.append_attr(police::TCA_POLICE_PEAKRATE64, &pr.to_ne_bytes());
}
builder.append_attr_u32(police::TCA_POLICE_RESULT, self.conform_action as u32);
if let Some(avrate) = self.avrate {
builder.append_attr_u32(police::TCA_POLICE_AVRATE, avrate);
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct VlanAction {
v_action: i32,
vlan_id: Option<u16>,
vlan_prio: Option<u8>,
vlan_proto: u16,
action: i32,
}
impl VlanAction {
pub fn pop() -> Self {
Self {
v_action: vlan::TCA_VLAN_ACT_POP,
vlan_id: None,
vlan_prio: None,
vlan_proto: vlan::ETH_P_8021Q,
action: action::TC_ACT_PIPE,
}
}
pub fn push(vlan_id: u16) -> Self {
Self {
v_action: vlan::TCA_VLAN_ACT_PUSH,
vlan_id: Some(vlan_id),
vlan_prio: None,
vlan_proto: vlan::ETH_P_8021Q,
action: action::TC_ACT_PIPE,
}
}
pub fn modify(vlan_id: u16) -> Self {
Self {
v_action: vlan::TCA_VLAN_ACT_MODIFY,
vlan_id: Some(vlan_id),
vlan_prio: None,
vlan_proto: vlan::ETH_P_8021Q,
action: action::TC_ACT_PIPE,
}
}
pub fn priority(mut self, prio: u8) -> Self {
self.vlan_prio = Some(prio);
self
}
pub fn qinq(mut self) -> Self {
self.vlan_proto = vlan::ETH_P_8021AD;
self
}
pub fn action(mut self, action: i32) -> Self {
self.action = action;
self
}
pub fn build(self) -> Self {
self
}
}
impl ActionConfig for VlanAction {
fn kind(&self) -> &'static str {
"vlan"
}
fn write_options(&self, builder: &mut MessageBuilder) -> Result<()> {
let parms = vlan::TcVlan::new(self.v_action, self.action);
builder.append_attr(vlan::TCA_VLAN_PARMS, parms.as_bytes());
if let Some(id) = self.vlan_id {
builder.append_attr(vlan::TCA_VLAN_PUSH_VLAN_ID, &id.to_ne_bytes());
}
if let Some(prio) = self.vlan_prio {
builder.append_attr(vlan::TCA_VLAN_PUSH_VLAN_PRIORITY, &[prio]);
}
builder.append_attr(
vlan::TCA_VLAN_PUSH_VLAN_PROTOCOL,
&self.vlan_proto.to_be_bytes(),
);
Ok(())
}
}
#[derive(Debug, Clone, Default)]
pub struct SkbeditAction {
queue_mapping: Option<u16>,
priority: Option<u32>,
mark: Option<u32>,
mark_mask: Option<u32>,
action: i32,
}
mod skbedit {
pub const TCA_SKBEDIT_PARMS: u16 = 2;
pub const TCA_SKBEDIT_PRIORITY: u16 = 3;
pub const TCA_SKBEDIT_QUEUE_MAPPING: u16 = 4;
pub const TCA_SKBEDIT_MARK: u16 = 5;
pub const TCA_SKBEDIT_MASK: u16 = 8;
}
impl SkbeditAction {
pub fn new() -> Self {
Self {
action: action::TC_ACT_PIPE,
..Default::default()
}
}
pub fn queue_mapping(mut self, queue: u16) -> Self {
self.queue_mapping = Some(queue);
self
}
pub fn priority(mut self, prio: u32) -> Self {
self.priority = Some(prio);
self
}
pub fn mark(mut self, mark: u32) -> Self {
self.mark = Some(mark);
self
}
pub fn mark_with_mask(mut self, mark: u32, mask: u32) -> Self {
self.mark = Some(mark);
self.mark_mask = Some(mask);
self
}
pub fn action(mut self, action: i32) -> Self {
self.action = action;
self
}
pub fn build(self) -> Self {
self
}
}
impl ActionConfig for SkbeditAction {
fn kind(&self) -> &'static str {
"skbedit"
}
fn write_options(&self, builder: &mut MessageBuilder) -> Result<()> {
let parms = TcGen::new(self.action);
builder.append_attr(skbedit::TCA_SKBEDIT_PARMS, parms.as_bytes());
if let Some(queue) = self.queue_mapping {
builder.append_attr(skbedit::TCA_SKBEDIT_QUEUE_MAPPING, &queue.to_ne_bytes());
}
if let Some(prio) = self.priority {
builder.append_attr(skbedit::TCA_SKBEDIT_PRIORITY, &prio.to_ne_bytes());
}
if let Some(mark) = self.mark {
builder.append_attr(skbedit::TCA_SKBEDIT_MARK, &mark.to_ne_bytes());
}
if let Some(mask) = self.mark_mask {
builder.append_attr(skbedit::TCA_SKBEDIT_MASK, &mask.to_ne_bytes());
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct NatAction {
old_addr: Ipv4Addr,
new_addr: Ipv4Addr,
prefix_len: u8,
egress: bool,
action: i32,
}
impl NatAction {
pub fn snat(old_addr: Ipv4Addr, new_addr: Ipv4Addr) -> Self {
Self {
old_addr,
new_addr,
prefix_len: 32,
egress: true,
action: action::TC_ACT_OK,
}
}
pub fn dnat(old_addr: Ipv4Addr, new_addr: Ipv4Addr) -> Self {
Self {
old_addr,
new_addr,
prefix_len: 32,
egress: false,
action: action::TC_ACT_OK,
}
}
pub fn prefix(mut self, prefix_len: u8) -> Self {
self.prefix_len = prefix_len.min(32);
self
}
pub fn action(mut self, action: i32) -> Self {
self.action = action;
self
}
pub fn pipe(mut self) -> Self {
self.action = action::TC_ACT_PIPE;
self
}
pub fn build(self) -> Self {
self
}
fn prefix_to_mask(prefix_len: u8) -> u32 {
if prefix_len == 0 {
0
} else if prefix_len >= 32 {
0xFFFFFFFF
} else {
!((1u32 << (32 - prefix_len)) - 1)
}
}
}
impl ActionConfig for NatAction {
fn kind(&self) -> &'static str {
"nat"
}
fn write_options(&self, builder: &mut MessageBuilder) -> Result<()> {
let mask = Self::prefix_to_mask(self.prefix_len);
let flags = if self.egress {
nat::TCA_NAT_FLAG_EGRESS
} else {
0
};
let parms = nat::TcNat::new(
u32::from_be_bytes(self.old_addr.octets()),
u32::from_be_bytes(self.new_addr.octets()),
u32::from_be_bytes(mask.to_be_bytes()),
flags,
self.action,
);
builder.append_attr(nat::TCA_NAT_PARMS, parms.as_bytes());
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct TunnelKeyAction {
t_action: i32,
src_ipv4: Option<Ipv4Addr>,
dst_ipv4: Option<Ipv4Addr>,
src_ipv6: Option<std::net::Ipv6Addr>,
dst_ipv6: Option<std::net::Ipv6Addr>,
key_id: Option<u32>,
dst_port: Option<u16>,
tos: Option<u8>,
ttl: Option<u8>,
no_csum: bool,
no_frag: bool,
action: i32,
}
impl TunnelKeyAction {
pub fn set() -> Self {
Self {
t_action: tunnel_key::TCA_TUNNEL_KEY_ACT_SET,
src_ipv4: None,
dst_ipv4: None,
src_ipv6: None,
dst_ipv6: None,
key_id: None,
dst_port: None,
tos: None,
ttl: None,
no_csum: false,
no_frag: false,
action: action::TC_ACT_PIPE,
}
}
pub fn release() -> Self {
Self {
t_action: tunnel_key::TCA_TUNNEL_KEY_ACT_RELEASE,
src_ipv4: None,
dst_ipv4: None,
src_ipv6: None,
dst_ipv6: None,
key_id: None,
dst_port: None,
tos: None,
ttl: None,
no_csum: false,
no_frag: false,
action: action::TC_ACT_PIPE,
}
}
pub fn src(mut self, addr: Ipv4Addr) -> Self {
self.src_ipv4 = Some(addr);
self.src_ipv6 = None;
self
}
pub fn dst(mut self, addr: Ipv4Addr) -> Self {
self.dst_ipv4 = Some(addr);
self.dst_ipv6 = None;
self
}
pub fn src6(mut self, addr: std::net::Ipv6Addr) -> Self {
self.src_ipv6 = Some(addr);
self.src_ipv4 = None;
self
}
pub fn dst6(mut self, addr: std::net::Ipv6Addr) -> Self {
self.dst_ipv6 = Some(addr);
self.dst_ipv4 = None;
self
}
pub fn key_id(mut self, id: u32) -> Self {
self.key_id = Some(id);
self
}
pub fn dst_port(mut self, port: u16) -> Self {
self.dst_port = Some(port);
self
}
pub fn tos(mut self, tos: u8) -> Self {
self.tos = Some(tos);
self
}
pub fn ttl(mut self, ttl: u8) -> Self {
self.ttl = Some(ttl);
self
}
pub fn no_csum(mut self) -> Self {
self.no_csum = true;
self
}
pub fn no_frag(mut self) -> Self {
self.no_frag = true;
self
}
pub fn action(mut self, action: i32) -> Self {
self.action = action;
self
}
pub fn build(self) -> Self {
self
}
}
impl ActionConfig for TunnelKeyAction {
fn kind(&self) -> &'static str {
"tunnel_key"
}
fn write_options(&self, builder: &mut MessageBuilder) -> Result<()> {
let parms = tunnel_key::TcTunnelKey::new(self.t_action, self.action);
builder.append_attr(tunnel_key::TCA_TUNNEL_KEY_PARMS, parms.as_bytes());
if self.t_action == tunnel_key::TCA_TUNNEL_KEY_ACT_SET {
if let Some(addr) = self.src_ipv4 {
builder.append_attr(tunnel_key::TCA_TUNNEL_KEY_ENC_IPV4_SRC, &addr.octets());
}
if let Some(addr) = self.dst_ipv4 {
builder.append_attr(tunnel_key::TCA_TUNNEL_KEY_ENC_IPV4_DST, &addr.octets());
}
if let Some(addr) = self.src_ipv6 {
builder.append_attr(tunnel_key::TCA_TUNNEL_KEY_ENC_IPV6_SRC, &addr.octets());
}
if let Some(addr) = self.dst_ipv6 {
builder.append_attr(tunnel_key::TCA_TUNNEL_KEY_ENC_IPV6_DST, &addr.octets());
}
if let Some(id) = self.key_id {
let id_be64 = (id as u64).to_be_bytes();
builder.append_attr(tunnel_key::TCA_TUNNEL_KEY_ENC_KEY_ID, &id_be64);
}
if let Some(port) = self.dst_port {
builder.append_attr_u16_be(tunnel_key::TCA_TUNNEL_KEY_ENC_DST_PORT, port);
}
if let Some(tos) = self.tos {
builder.append_attr_u8(tunnel_key::TCA_TUNNEL_KEY_ENC_TOS, tos);
}
if let Some(ttl) = self.ttl {
builder.append_attr_u8(tunnel_key::TCA_TUNNEL_KEY_ENC_TTL, ttl);
}
builder.append_attr_u8(
tunnel_key::TCA_TUNNEL_KEY_NO_CSUM,
if self.no_csum { 1 } else { 0 },
);
if self.no_frag {
builder.append_attr_empty(tunnel_key::TCA_TUNNEL_KEY_NO_FRAG);
}
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct ConnmarkAction {
zone: u16,
action: i32,
}
impl ConnmarkAction {
pub fn new() -> Self {
Self {
zone: 0,
action: action::TC_ACT_PIPE,
}
}
pub fn with_zone(zone: u16) -> Self {
Self {
zone,
action: action::TC_ACT_PIPE,
}
}
pub fn zone(mut self, zone: u16) -> Self {
self.zone = zone;
self
}
pub fn action(mut self, action: i32) -> Self {
self.action = action;
self
}
pub fn build(self) -> Self {
self
}
}
impl Default for ConnmarkAction {
fn default() -> Self {
Self::new()
}
}
impl ActionConfig for ConnmarkAction {
fn kind(&self) -> &'static str {
"connmark"
}
fn write_options(&self, builder: &mut MessageBuilder) -> Result<()> {
let parms = connmark::TcConnmark::new(self.zone, self.action);
builder.append_attr(connmark::TCA_CONNMARK_PARMS, parms.as_bytes());
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct CsumAction {
update_flags: u32,
action: i32,
}
impl CsumAction {
pub fn new() -> Self {
Self {
update_flags: 0,
action: action::TC_ACT_OK,
}
}
pub fn iph(mut self) -> Self {
self.update_flags |= csum::TCA_CSUM_UPDATE_FLAG_IPV4HDR;
self
}
pub fn icmp(mut self) -> Self {
self.update_flags |= csum::TCA_CSUM_UPDATE_FLAG_ICMP;
self
}
pub fn igmp(mut self) -> Self {
self.update_flags |= csum::TCA_CSUM_UPDATE_FLAG_IGMP;
self
}
pub fn tcp(mut self) -> Self {
self.update_flags |= csum::TCA_CSUM_UPDATE_FLAG_TCP;
self
}
pub fn udp(mut self) -> Self {
self.update_flags |= csum::TCA_CSUM_UPDATE_FLAG_UDP;
self
}
pub fn udplite(mut self) -> Self {
self.update_flags |= csum::TCA_CSUM_UPDATE_FLAG_UDPLITE;
self
}
pub fn sctp(mut self) -> Self {
self.update_flags |= csum::TCA_CSUM_UPDATE_FLAG_SCTP;
self
}
pub fn action(mut self, action: i32) -> Self {
self.action = action;
self
}
pub fn build(self) -> Self {
self
}
}
impl Default for CsumAction {
fn default() -> Self {
Self::new()
}
}
impl ActionConfig for CsumAction {
fn kind(&self) -> &'static str {
"csum"
}
fn write_options(&self, builder: &mut MessageBuilder) -> Result<()> {
let parms = csum::TcCsum::new(self.update_flags, self.action);
builder.append_attr(csum::TCA_CSUM_PARMS, parms.as_bytes());
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct SampleAction {
rate: u32,
group: u32,
trunc_size: Option<u32>,
action: i32,
}
impl SampleAction {
pub fn new(rate: u32, group: u32) -> Self {
Self {
rate,
group,
trunc_size: None,
action: action::TC_ACT_PIPE,
}
}
pub fn trunc(mut self, size: u32) -> Self {
self.trunc_size = Some(size);
self
}
pub fn action(mut self, action: i32) -> Self {
self.action = action;
self
}
pub fn build(self) -> Self {
self
}
}
impl ActionConfig for SampleAction {
fn kind(&self) -> &'static str {
"sample"
}
fn write_options(&self, builder: &mut MessageBuilder) -> Result<()> {
let parms = sample::TcSample::new(self.action);
builder.append_attr(sample::TCA_SAMPLE_PARMS, parms.as_bytes());
builder.append_attr_u32(sample::TCA_SAMPLE_RATE, self.rate);
builder.append_attr_u32(sample::TCA_SAMPLE_PSAMPLE_GROUP, self.group);
if let Some(trunc) = self.trunc_size {
builder.append_attr_u32(sample::TCA_SAMPLE_TRUNC_SIZE, trunc);
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct CtAction {
ct_action: u16,
zone: Option<u16>,
mark: Option<u32>,
mark_mask: Option<u32>,
labels: Option<[u8; 16]>,
labels_mask: Option<[u8; 16]>,
nat_ipv4_min: Option<Ipv4Addr>,
nat_ipv4_max: Option<Ipv4Addr>,
nat_ipv6_min: Option<std::net::Ipv6Addr>,
nat_ipv6_max: Option<std::net::Ipv6Addr>,
nat_port_min: Option<u16>,
nat_port_max: Option<u16>,
helper_name: Option<String>,
helper_family: Option<u8>,
helper_proto: Option<u8>,
action: i32,
}
impl Default for CtAction {
fn default() -> Self {
Self::new()
}
}
impl CtAction {
pub fn new() -> Self {
Self {
ct_action: 0,
zone: None,
mark: None,
mark_mask: None,
labels: None,
labels_mask: None,
nat_ipv4_min: None,
nat_ipv4_max: None,
nat_ipv6_min: None,
nat_ipv6_max: None,
nat_port_min: None,
nat_port_max: None,
helper_name: None,
helper_family: None,
helper_proto: None,
action: action::TC_ACT_PIPE,
}
}
pub fn commit() -> Self {
let mut s = Self::new();
s.ct_action = ct::TCA_CT_ACT_COMMIT;
s
}
pub fn clear() -> Self {
let mut s = Self::new();
s.ct_action = ct::TCA_CT_ACT_CLEAR;
s
}
pub fn force(mut self) -> Self {
self.ct_action |= ct::TCA_CT_ACT_FORCE;
self
}
pub fn zone(mut self, zone: u16) -> Self {
self.zone = Some(zone);
self
}
pub fn mark(mut self, mark: u32, mask: u32) -> Self {
self.mark = Some(mark);
self.mark_mask = Some(mask);
self
}
pub fn labels(mut self, labels: [u8; 16], mask: [u8; 16]) -> Self {
self.labels = Some(labels);
self.labels_mask = Some(mask);
self
}
pub fn nat_src(mut self, min: Ipv4Addr, max: Ipv4Addr) -> Self {
self.ct_action |= ct::TCA_CT_ACT_NAT | ct::TCA_CT_ACT_NAT_SRC;
self.nat_ipv4_min = Some(min);
self.nat_ipv4_max = Some(max);
self
}
pub fn nat_src_single(mut self, addr: Ipv4Addr) -> Self {
self.ct_action |= ct::TCA_CT_ACT_NAT | ct::TCA_CT_ACT_NAT_SRC;
self.nat_ipv4_min = Some(addr);
self.nat_ipv4_max = Some(addr);
self
}
pub fn nat_dst(mut self, min: Ipv4Addr, max: Ipv4Addr) -> Self {
self.ct_action |= ct::TCA_CT_ACT_NAT | ct::TCA_CT_ACT_NAT_DST;
self.nat_ipv4_min = Some(min);
self.nat_ipv4_max = Some(max);
self
}
pub fn nat_dst_single(mut self, addr: Ipv4Addr) -> Self {
self.ct_action |= ct::TCA_CT_ACT_NAT | ct::TCA_CT_ACT_NAT_DST;
self.nat_ipv4_min = Some(addr);
self.nat_ipv4_max = Some(addr);
self
}
pub fn nat_src6(mut self, min: std::net::Ipv6Addr, max: std::net::Ipv6Addr) -> Self {
self.ct_action |= ct::TCA_CT_ACT_NAT | ct::TCA_CT_ACT_NAT_SRC;
self.nat_ipv6_min = Some(min);
self.nat_ipv6_max = Some(max);
self
}
pub fn nat_dst6(mut self, min: std::net::Ipv6Addr, max: std::net::Ipv6Addr) -> Self {
self.ct_action |= ct::TCA_CT_ACT_NAT | ct::TCA_CT_ACT_NAT_DST;
self.nat_ipv6_min = Some(min);
self.nat_ipv6_max = Some(max);
self
}
pub fn nat_port_range(mut self, min: u16, max: u16) -> Self {
self.nat_port_min = Some(min);
self.nat_port_max = Some(max);
self
}
pub fn helper(mut self, name: &str, family: u8, proto: u8) -> Self {
self.helper_name = Some(name.to_string());
self.helper_family = Some(family);
self.helper_proto = Some(proto);
self
}
pub fn action(mut self, action: i32) -> Self {
self.action = action;
self
}
pub fn build(self) -> Self {
self
}
}
impl ActionConfig for CtAction {
fn kind(&self) -> &'static str {
"ct"
}
fn write_options(&self, builder: &mut MessageBuilder) -> Result<()> {
let parms = ct::TcCt::new(self.action);
builder.append_attr(ct::TCA_CT_PARMS, parms.as_bytes());
if self.ct_action != 0 {
builder.append_attr(ct::TCA_CT_ACTION, &self.ct_action.to_ne_bytes());
}
if let Some(zone) = self.zone {
builder.append_attr(ct::TCA_CT_ZONE, &zone.to_ne_bytes());
}
if let Some(mark) = self.mark {
builder.append_attr(ct::TCA_CT_MARK, &mark.to_ne_bytes());
}
if let Some(mask) = self.mark_mask {
builder.append_attr(ct::TCA_CT_MARK_MASK, &mask.to_ne_bytes());
}
if let Some(labels) = &self.labels {
builder.append_attr(ct::TCA_CT_LABELS, labels);
}
if let Some(mask) = &self.labels_mask {
builder.append_attr(ct::TCA_CT_LABELS_MASK, mask);
}
if let Some(addr) = self.nat_ipv4_min {
builder.append_attr(ct::TCA_CT_NAT_IPV4_MIN, &addr.octets());
}
if let Some(addr) = self.nat_ipv4_max {
builder.append_attr(ct::TCA_CT_NAT_IPV4_MAX, &addr.octets());
}
if let Some(addr) = self.nat_ipv6_min {
builder.append_attr(ct::TCA_CT_NAT_IPV6_MIN, &addr.octets());
}
if let Some(addr) = self.nat_ipv6_max {
builder.append_attr(ct::TCA_CT_NAT_IPV6_MAX, &addr.octets());
}
if let Some(port) = self.nat_port_min {
builder.append_attr(ct::TCA_CT_NAT_PORT_MIN, &port.to_be_bytes());
}
if let Some(port) = self.nat_port_max {
builder.append_attr(ct::TCA_CT_NAT_PORT_MAX, &port.to_be_bytes());
}
if let Some(name) = &self.helper_name {
builder.append_attr_str(ct::TCA_CT_HELPER_NAME, name);
}
if let Some(family) = self.helper_family {
builder.append_attr(ct::TCA_CT_HELPER_FAMILY, &[family]);
}
if let Some(proto) = self.helper_proto {
builder.append_attr(ct::TCA_CT_HELPER_PROTO, &[proto]);
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct PeditAction {
keys: Vec<PeditKey>,
action: i32,
}
#[derive(Debug, Clone)]
struct PeditKey {
htype: u16,
cmd: u16,
mask: u32,
val: u32,
off: u32,
}
impl Default for PeditAction {
fn default() -> Self {
Self::new()
}
}
impl PeditAction {
pub fn new() -> Self {
Self {
keys: Vec::new(),
action: action::TC_ACT_PIPE,
}
}
pub fn set_ipv4_src(mut self, addr: Ipv4Addr) -> Self {
self.keys.push(PeditKey {
htype: pedit::TCA_PEDIT_KEY_EX_HDR_TYPE_IP4,
cmd: pedit::TCA_PEDIT_KEY_EX_CMD_SET,
mask: 0,
val: u32::from_be_bytes(addr.octets()),
off: 12, });
self
}
pub fn set_ipv4_dst(mut self, addr: Ipv4Addr) -> Self {
self.keys.push(PeditKey {
htype: pedit::TCA_PEDIT_KEY_EX_HDR_TYPE_IP4,
cmd: pedit::TCA_PEDIT_KEY_EX_CMD_SET,
mask: 0,
val: u32::from_be_bytes(addr.octets()),
off: 16, });
self
}
pub fn set_ipv4_tos(mut self, tos: u8) -> Self {
self.keys.push(PeditKey {
htype: pedit::TCA_PEDIT_KEY_EX_HDR_TYPE_IP4,
cmd: pedit::TCA_PEDIT_KEY_EX_CMD_SET,
mask: 0x00ff_ffff,
val: (tos as u32) << 24,
off: 0, });
self
}
pub fn set_ipv4_ttl(mut self, ttl: u8) -> Self {
self.keys.push(PeditKey {
htype: pedit::TCA_PEDIT_KEY_EX_HDR_TYPE_IP4,
cmd: pedit::TCA_PEDIT_KEY_EX_CMD_SET,
mask: 0xffff_00ff,
val: (ttl as u32) << 8,
off: 8, });
self
}
pub fn set_tcp_sport(mut self, port: u16) -> Self {
self.keys.push(PeditKey {
htype: pedit::TCA_PEDIT_KEY_EX_HDR_TYPE_TCP,
cmd: pedit::TCA_PEDIT_KEY_EX_CMD_SET,
mask: 0x0000_ffff,
val: (port as u32) << 16,
off: 0, });
self
}
pub fn set_tcp_dport(mut self, port: u16) -> Self {
self.keys.push(PeditKey {
htype: pedit::TCA_PEDIT_KEY_EX_HDR_TYPE_TCP,
cmd: pedit::TCA_PEDIT_KEY_EX_CMD_SET,
mask: 0xffff_0000,
val: port as u32,
off: 0, });
self
}
pub fn set_udp_sport(mut self, port: u16) -> Self {
self.keys.push(PeditKey {
htype: pedit::TCA_PEDIT_KEY_EX_HDR_TYPE_UDP,
cmd: pedit::TCA_PEDIT_KEY_EX_CMD_SET,
mask: 0x0000_ffff,
val: (port as u32) << 16,
off: 0,
});
self
}
pub fn set_udp_dport(mut self, port: u16) -> Self {
self.keys.push(PeditKey {
htype: pedit::TCA_PEDIT_KEY_EX_HDR_TYPE_UDP,
cmd: pedit::TCA_PEDIT_KEY_EX_CMD_SET,
mask: 0xffff_0000,
val: port as u32,
off: 0,
});
self
}
pub fn set_eth_src(mut self, mac: [u8; 6]) -> Self {
self.keys.push(PeditKey {
htype: pedit::TCA_PEDIT_KEY_EX_HDR_TYPE_ETH,
cmd: pedit::TCA_PEDIT_KEY_EX_CMD_SET,
mask: 0,
val: u32::from_be_bytes([mac[0], mac[1], mac[2], mac[3]]),
off: 6, });
self.keys.push(PeditKey {
htype: pedit::TCA_PEDIT_KEY_EX_HDR_TYPE_ETH,
cmd: pedit::TCA_PEDIT_KEY_EX_CMD_SET,
mask: 0x0000_ffff,
val: ((mac[4] as u32) << 24) | ((mac[5] as u32) << 16),
off: 10,
});
self
}
pub fn set_eth_dst(mut self, mac: [u8; 6]) -> Self {
self.keys.push(PeditKey {
htype: pedit::TCA_PEDIT_KEY_EX_HDR_TYPE_ETH,
cmd: pedit::TCA_PEDIT_KEY_EX_CMD_SET,
mask: 0,
val: u32::from_be_bytes([mac[0], mac[1], mac[2], mac[3]]),
off: 0, });
self.keys.push(PeditKey {
htype: pedit::TCA_PEDIT_KEY_EX_HDR_TYPE_ETH,
cmd: pedit::TCA_PEDIT_KEY_EX_CMD_SET,
mask: 0x0000_ffff,
val: ((mac[4] as u32) << 24) | ((mac[5] as u32) << 16),
off: 4,
});
self
}
pub fn set_raw(mut self, htype: u16, off: u32, val: u32, mask: u32) -> Self {
self.keys.push(PeditKey {
htype,
cmd: pedit::TCA_PEDIT_KEY_EX_CMD_SET,
mask,
val,
off,
});
self
}
pub fn add_raw(mut self, htype: u16, off: u32, val: u32, mask: u32) -> Self {
self.keys.push(PeditKey {
htype,
cmd: pedit::TCA_PEDIT_KEY_EX_CMD_ADD,
mask,
val,
off,
});
self
}
pub fn action(mut self, action: i32) -> Self {
self.action = action;
self
}
pub fn build(self) -> Self {
self
}
}
impl ActionConfig for PeditAction {
fn kind(&self) -> &'static str {
"pedit"
}
fn write_options(&self, builder: &mut MessageBuilder) -> Result<()> {
if self.keys.is_empty() {
return Err(Error::InvalidMessage(
"pedit requires at least one key".into(),
));
}
let sel = pedit::TcPeditSel::new(self.action, self.keys.len() as u8);
let mut parms_data = Vec::new();
parms_data.extend_from_slice(sel.as_bytes());
for key in &self.keys {
let k = pedit::TcPeditKey::new(key.mask, key.val, key.off);
parms_data.extend_from_slice(k.as_bytes());
}
builder.append_attr(pedit::TCA_PEDIT_PARMS_EX, &parms_data);
let keys_ex_token = builder.nest_start(pedit::TCA_PEDIT_KEYS_EX);
for key in &self.keys {
let key_ex_token = builder.nest_start(pedit::TCA_PEDIT_KEY_EX);
builder.append_attr(pedit::TCA_PEDIT_KEY_EX_HTYPE, &key.htype.to_ne_bytes());
builder.append_attr(pedit::TCA_PEDIT_KEY_EX_CMD, &key.cmd.to_ne_bytes());
builder.nest_end(key_ex_token);
}
builder.nest_end(keys_ex_token);
Ok(())
}
}
#[derive(Debug, Clone, Default)]
pub struct ActionList {
actions: Vec<Box<dyn ActionConfigDyn>>,
}
pub trait ActionConfigDyn: Send + Sync + std::fmt::Debug {
fn kind(&self) -> &'static str;
fn write_options(&self, builder: &mut MessageBuilder) -> Result<()>;
fn clone_box(&self) -> Box<dyn ActionConfigDyn>;
}
impl<T: ActionConfig + Clone + std::fmt::Debug + 'static> ActionConfigDyn for T {
fn kind(&self) -> &'static str {
ActionConfig::kind(self)
}
fn write_options(&self, builder: &mut MessageBuilder) -> Result<()> {
ActionConfig::write_options(self, builder)
}
fn clone_box(&self) -> Box<dyn ActionConfigDyn> {
Box::new(self.clone())
}
}
impl Clone for Box<dyn ActionConfigDyn> {
fn clone(&self) -> Self {
self.clone_box()
}
}
impl ActionList {
pub fn new() -> Self {
Self::default()
}
pub fn with<T: ActionConfig + Clone + std::fmt::Debug + 'static>(mut self, action: T) -> Self {
self.actions.push(Box::new(action));
self
}
pub fn is_empty(&self) -> bool {
self.actions.is_empty()
}
pub fn len(&self) -> usize {
self.actions.len()
}
pub fn write_to(&self, builder: &mut MessageBuilder) -> Result<()> {
for (i, action) in self.actions.iter().enumerate() {
let act_token = builder.nest_start((i + 1) as u16);
builder.append_attr_str(action::TCA_ACT_KIND, action.kind());
let opt_token = builder.nest_start(action::TCA_ACT_OPTIONS);
action.write_options(builder)?;
builder.nest_end(opt_token);
builder.nest_end(act_token);
}
Ok(())
}
pub fn build(self) -> Self {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gact_action() {
let drop = GactAction::drop();
assert_eq!(drop.action, action::TC_ACT_SHOT);
assert_eq!(ActionConfig::kind(&drop), "gact");
let pass = GactAction::pass();
assert_eq!(pass.action, action::TC_ACT_OK);
let random = GactAction::new(action::TC_ACT_OK).random_drop(10).build();
assert_eq!(random.prob_type, gact::PGACT_NETRAND);
}
#[test]
fn test_police_action() {
let police = PoliceAction::new()
.rate(1_000_000)
.burst(32 * 1024)
.exceed_drop()
.build();
assert_eq!(police.rate, 1_000_000);
assert_eq!(police.burst, 32 * 1024);
assert_eq!(police.exceed_action, action::TC_ACT_SHOT);
assert_eq!(ActionConfig::kind(&police), "police");
}
#[test]
fn test_vlan_action() {
let pop = VlanAction::pop();
assert_eq!(pop.v_action, vlan::TCA_VLAN_ACT_POP);
assert_eq!(ActionConfig::kind(&pop), "vlan");
let push = VlanAction::push(100).priority(3).build();
assert_eq!(push.v_action, vlan::TCA_VLAN_ACT_PUSH);
assert_eq!(push.vlan_id, Some(100));
assert_eq!(push.vlan_prio, Some(3));
}
#[test]
fn test_skbedit_action() {
let edit = SkbeditAction::new().priority(7).mark(100).build();
assert_eq!(edit.priority, Some(7));
assert_eq!(edit.mark, Some(100));
assert_eq!(ActionConfig::kind(&edit), "skbedit");
}
#[test]
fn test_action_list() {
let list = ActionList::new().with(GactAction::drop()).build();
assert_eq!(list.len(), 1);
assert!(!list.is_empty());
}
#[test]
fn test_nat_action() {
let snat = NatAction::snat(Ipv4Addr::new(10, 0, 0, 0), Ipv4Addr::new(192, 168, 1, 1))
.prefix(8)
.build();
assert_eq!(snat.old_addr, Ipv4Addr::new(10, 0, 0, 0));
assert_eq!(snat.new_addr, Ipv4Addr::new(192, 168, 1, 1));
assert_eq!(snat.prefix_len, 8);
assert!(snat.egress);
assert_eq!(ActionConfig::kind(&snat), "nat");
let dnat = NatAction::dnat(Ipv4Addr::new(192, 168, 1, 1), Ipv4Addr::new(10, 0, 0, 1));
assert!(!dnat.egress);
}
#[test]
fn test_nat_prefix_to_mask() {
assert_eq!(NatAction::prefix_to_mask(0), 0);
assert_eq!(NatAction::prefix_to_mask(8), 0xFF000000);
assert_eq!(NatAction::prefix_to_mask(16), 0xFFFF0000);
assert_eq!(NatAction::prefix_to_mask(24), 0xFFFFFF00);
assert_eq!(NatAction::prefix_to_mask(32), 0xFFFFFFFF);
}
#[test]
fn test_tunnel_key_action() {
let set = TunnelKeyAction::set()
.src(Ipv4Addr::new(192, 168, 1, 1))
.dst(Ipv4Addr::new(192, 168, 1, 2))
.key_id(100)
.dst_port(4789)
.ttl(64)
.no_csum()
.build();
assert_eq!(set.t_action, tunnel_key::TCA_TUNNEL_KEY_ACT_SET);
assert_eq!(set.src_ipv4, Some(Ipv4Addr::new(192, 168, 1, 1)));
assert_eq!(set.dst_ipv4, Some(Ipv4Addr::new(192, 168, 1, 2)));
assert_eq!(set.key_id, Some(100));
assert_eq!(set.dst_port, Some(4789));
assert_eq!(set.ttl, Some(64));
assert!(set.no_csum);
assert_eq!(ActionConfig::kind(&set), "tunnel_key");
let release = TunnelKeyAction::release();
assert_eq!(release.t_action, tunnel_key::TCA_TUNNEL_KEY_ACT_RELEASE);
}
#[test]
fn test_connmark_action() {
let mark = ConnmarkAction::new();
assert_eq!(mark.zone, 0);
assert_eq!(ActionConfig::kind(&mark), "connmark");
let mark_zone = ConnmarkAction::with_zone(5);
assert_eq!(mark_zone.zone, 5);
let mark_custom = ConnmarkAction::new()
.zone(10)
.action(action::TC_ACT_OK)
.build();
assert_eq!(mark_custom.zone, 10);
assert_eq!(mark_custom.action, action::TC_ACT_OK);
}
#[test]
fn test_csum_action() {
let csum_action = CsumAction::new().iph().tcp().udp().build();
assert_eq!(
csum_action.update_flags,
csum::TCA_CSUM_UPDATE_FLAG_IPV4HDR
| csum::TCA_CSUM_UPDATE_FLAG_TCP
| csum::TCA_CSUM_UPDATE_FLAG_UDP
);
assert_eq!(ActionConfig::kind(&csum_action), "csum");
let all = CsumAction::new()
.iph()
.icmp()
.igmp()
.tcp()
.udp()
.udplite()
.sctp()
.build();
assert_eq!(all.update_flags, 0x7F); }
#[test]
fn test_sample_action() {
let sample = SampleAction::new(100, 5);
assert_eq!(sample.rate, 100);
assert_eq!(sample.group, 5);
assert_eq!(sample.trunc_size, None);
assert_eq!(ActionConfig::kind(&sample), "sample");
let sample_trunc = SampleAction::new(50, 10).trunc(128).build();
assert_eq!(sample_trunc.rate, 50);
assert_eq!(sample_trunc.group, 10);
assert_eq!(sample_trunc.trunc_size, Some(128));
}
#[test]
fn test_ct_action() {
let ct = CtAction::new();
assert_eq!(ct.ct_action, 0);
assert_eq!(ActionConfig::kind(&ct), "ct");
let commit = CtAction::commit();
assert_eq!(commit.ct_action, ct::TCA_CT_ACT_COMMIT);
let force = CtAction::commit().force();
assert_eq!(
force.ct_action,
ct::TCA_CT_ACT_COMMIT | ct::TCA_CT_ACT_FORCE
);
let clear = CtAction::clear();
assert_eq!(clear.ct_action, ct::TCA_CT_ACT_CLEAR);
let snat = CtAction::commit()
.nat_src_single(Ipv4Addr::new(10, 0, 0, 1))
.zone(1)
.mark(0x100, 0xffffffff)
.build();
assert!(snat.ct_action & ct::TCA_CT_ACT_NAT != 0);
assert!(snat.ct_action & ct::TCA_CT_ACT_NAT_SRC != 0);
assert_eq!(snat.zone, Some(1));
assert_eq!(snat.mark, Some(0x100));
assert_eq!(snat.nat_ipv4_min, Some(Ipv4Addr::new(10, 0, 0, 1)));
}
#[test]
fn test_pedit_action() {
let pedit = PeditAction::new()
.set_ipv4_src(Ipv4Addr::new(10, 0, 0, 1))
.build();
assert_eq!(pedit.keys.len(), 1);
assert_eq!(ActionConfig::kind(&pedit), "pedit");
let pedit_multi = PeditAction::new()
.set_ipv4_src(Ipv4Addr::new(10, 0, 0, 1))
.set_ipv4_dst(Ipv4Addr::new(10, 0, 0, 2))
.set_tcp_dport(8080)
.build();
assert_eq!(pedit_multi.keys.len(), 3);
let pedit_eth = PeditAction::new()
.set_eth_src([0x00, 0x11, 0x22, 0x33, 0x44, 0x55])
.build();
assert_eq!(pedit_eth.keys.len(), 2);
}
}