use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use rtnetlink::packet_core::{NLM_F_ACK, NLM_F_CREATE, NLM_F_EXCL, NLM_F_REQUEST, NetlinkMessage};
use rtnetlink::packet_route::tc::TcFilterFlowerOption;
use rtnetlink::packet_route::{
RouteNetlinkMessage,
tc::{TcAttribute, TcHandle, TcMessage, TcOption},
};
use super::handle::QdiscRequestInner;
use super::nla::{build_nested_options, build_nla};
const ETH_P_IP: u16 = nix::libc::ETH_P_IP as u16;
const ETH_P_IPV6: u16 = nix::libc::ETH_P_IPV6 as u16;
const ETH_P_ALL: u16 = nix::libc::ETH_P_ALL as u16;
fn ipv4_mask(prefix_len: u8) -> Ipv4Addr {
if prefix_len == 0 {
return Ipv4Addr::new(0, 0, 0, 0);
}
let mask_u32 = u32::MAX << (32 - prefix_len);
Ipv4Addr::from(mask_u32)
}
fn ipv6_mask(prefix_len: u8) -> Ipv6Addr {
if prefix_len == 0 {
return Ipv6Addr::from(0u128);
}
let mask_u128 = u128::MAX << (128 - prefix_len);
Ipv6Addr::from(mask_u128)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FlowerFilterRequest {
pub inner: QdiscRequestInner,
pub destination: IpAddr,
pub mask: u8,
pub class_id: u32,
}
impl FlowerFilterRequest {
pub fn new(inner: QdiscRequestInner, destination: IpAddr) -> Self {
let default_mask = match destination {
IpAddr::V4(_) => 32,
IpAddr::V6(_) => 128,
};
Self {
inner,
destination,
mask: default_mask,
class_id: u32::MAX, }
}
pub fn with_prefix(mut self, prefix: u8) -> Self {
self.mask = prefix;
self
}
pub fn with_class_id(mut self, class_id: u32) -> Self {
self.class_id = class_id;
self
}
pub fn build(self) -> NetlinkMessage<RouteNetlinkMessage> {
let (proto_ethertype, match_opts): (u16, Vec<TcOption>) = match self.destination {
IpAddr::V4(v4) => {
let mask = ipv4_mask(self.mask);
(
ETH_P_IP,
vec![
TcOption::Flower(TcFilterFlowerOption::Ipv4Dst(v4)),
TcOption::Flower(TcFilterFlowerOption::Ipv4DstMask(mask)),
],
)
}
IpAddr::V6(v6) => {
let mask = ipv6_mask(self.mask);
(
ETH_P_IPV6,
vec![
TcOption::Flower(TcFilterFlowerOption::Ipv6Dst(v6)),
TcOption::Flower(TcFilterFlowerOption::Ipv6DstMask(mask)),
],
)
}
};
let mut tc_msg = TcMessage::with_index(self.inner.interface_index);
tc_msg.header.parent = self.inner.parent;
tc_msg.header.handle = TcHandle::from(0u32);
tc_msg.header.info = proto_ethertype.to_be() as u32;
tc_msg.attributes.push(TcAttribute::Kind("flower".to_string()));
let opts: Vec<TcOption> = [
vec![
TcOption::Flower(TcFilterFlowerOption::ClassId(self.class_id)),
TcOption::Flower(TcFilterFlowerOption::Flags(0)),
TcOption::Flower(TcFilterFlowerOption::EthType(proto_ethertype)),
],
match_opts,
]
.concat();
tc_msg.attributes.push(TcAttribute::Options(opts));
let mut nl_req = NetlinkMessage::from(RouteNetlinkMessage::NewTrafficFilter(tc_msg));
nl_req.header.flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL;
nl_req
}
}
const TCA_U32_CLASSID: u16 = 1;
const TCA_U32_SEL: u16 = 5;
const TC_U32_TERMINAL: u8 = 1;
#[derive(Debug, Clone, Copy, Default)]
struct TcU32Sel {
flags: u8,
offshift: u8,
nkeys: u8,
offmask: u16,
off: u16,
offoff: i16,
hoff: i16,
hmask: u32,
}
impl TcU32Sel {
#[allow(dead_code)]
fn zero() -> Self {
Self { flags: 0, offmask: 0, offshift: 0, nkeys: 0, off: 0, offoff: 0, hoff: 0, hmask: 0 }
}
}
#[derive(Debug, Clone, Copy, Default)]
struct TcU32Key {
mask: u32,
val: u32,
off: i32,
offmask: i32,
}
impl TcU32Key {
fn zero() -> Self {
Self { mask: 0, val: 0, off: 0, offmask: 0 }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct U32CatchallFilterRequest {
pub inner: QdiscRequestInner,
pub class_id: u32,
pub priority: u16,
}
impl U32CatchallFilterRequest {
pub fn new(inner: QdiscRequestInner) -> Self {
Self {
inner,
class_id: u32::MAX, priority: 65535, }
}
pub fn with_class_id(mut self, class_id: u32) -> Self {
self.class_id = class_id;
self
}
pub fn with_priority(mut self, priority: u16) -> Self {
self.priority = priority;
self
}
pub fn build(self) -> NetlinkMessage<RouteNetlinkMessage> {
let mut tc_msg = TcMessage::with_index(self.inner.interface_index);
tc_msg.header.parent = self.inner.parent;
tc_msg.header.handle = TcHandle::from(0u32);
tc_msg.header.info = ((self.priority as u32) << 16) | (ETH_P_ALL.to_be() as u32);
tc_msg.attributes.push(TcAttribute::Kind("u32".to_string()));
let sel = TcU32Sel {
flags: TC_U32_TERMINAL, nkeys: 1, ..Default::default()
};
let key = TcU32Key::zero();
let mut sel_bytes = Vec::with_capacity(size_of::<TcU32Sel>() + size_of::<TcU32Key>());
sel_bytes.push(sel.flags);
sel_bytes.push(sel.offshift);
sel_bytes.push(sel.nkeys);
sel_bytes.push(0); sel_bytes.extend_from_slice(&sel.offmask.to_ne_bytes());
sel_bytes.extend_from_slice(&sel.off.to_ne_bytes());
sel_bytes.extend_from_slice(&sel.offoff.to_ne_bytes());
sel_bytes.extend_from_slice(&sel.hoff.to_ne_bytes());
sel_bytes.extend_from_slice(&sel.hmask.to_ne_bytes());
sel_bytes.extend_from_slice(&key.mask.to_ne_bytes());
sel_bytes.extend_from_slice(&key.val.to_ne_bytes());
sel_bytes.extend_from_slice(&key.off.to_ne_bytes());
sel_bytes.extend_from_slice(&key.offmask.to_ne_bytes());
let classid_nla = build_nla(TCA_U32_CLASSID, &self.class_id.to_ne_bytes());
let sel_nla = build_nla(TCA_U32_SEL, &sel_bytes);
let mut options = classid_nla;
options.extend(sel_nla);
tc_msg.attributes.push(TcAttribute::Other(build_nested_options(options)));
let mut nl_req = NetlinkMessage::from(RouteNetlinkMessage::NewTrafficFilter(tc_msg));
nl_req.header.flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL;
nl_req
}
}