use rtnetlink::packet_core::{
NLM_F_ACK, NLM_F_CREATE, NLM_F_EXCL, NLM_F_REPLACE, NLM_F_REQUEST, NetlinkMessage,
};
use rtnetlink::packet_route::{
RouteNetlinkMessage,
tc::{TcAttribute, TcHandle, TcMessage},
};
use super::core::TICK_IN_USEC;
use super::handle::QdiscRequestInner;
use super::nla::{build_nested_options, build_nla};
use super::tbf::TcRateSpec;
const TCA_HTB_PARMS: u16 = 1;
const TCA_HTB_INIT: u16 = 2;
const TCA_HTB_CTAB: u16 = 3;
const TCA_HTB_RTAB: u16 = 4;
const HTB_VERSION: u32 = 3;
const HTB_RATE2QUANTUM: u32 = 10;
const HTB_DEFAULT_CLASS: u32 = 1;
const HTB_UNLIMITED_RATE_BPS: u32 = 1_250_000_000;
const HTB_DEFAULT_BURST_BYTES: u32 = 1024 * 1024;
const DEFAULT_RATE_TABLE: [u8; 1024] = [0u8; 1024];
#[derive(Debug, Clone, Copy)]
struct HtbGlob {
version: u32,
rate2quantum: u32,
defcls: u32,
debug: u32,
direct_pkts: u32,
}
impl HtbGlob {
fn as_bytes(self) -> Vec<u8> {
let mut vec = Vec::with_capacity(20);
vec.extend_from_slice(&self.version.to_ne_bytes());
vec.extend_from_slice(&self.rate2quantum.to_ne_bytes());
vec.extend_from_slice(&self.defcls.to_ne_bytes());
vec.extend_from_slice(&self.debug.to_ne_bytes());
vec.extend_from_slice(&self.direct_pkts.to_ne_bytes());
vec
}
}
impl Default for HtbGlob {
fn default() -> Self {
Self {
version: HTB_VERSION,
rate2quantum: HTB_RATE2QUANTUM,
defcls: HTB_DEFAULT_CLASS,
debug: 0,
direct_pkts: 0,
}
}
}
#[derive(Debug, Clone, Copy)]
struct HtbOpt {
rate: TcRateSpec,
ceil: TcRateSpec,
buffer: u32,
cbuffer: u32,
quantum: u32,
level: u32,
prio: u32,
}
impl HtbOpt {
fn as_bytes(self) -> Vec<u8> {
let mut vec = Vec::with_capacity(44); vec.extend_from_slice(&self.rate.to_bytes());
vec.extend_from_slice(&self.ceil.to_bytes());
vec.extend_from_slice(&self.buffer.to_ne_bytes());
vec.extend_from_slice(&self.cbuffer.to_ne_bytes());
vec.extend_from_slice(&self.quantum.to_ne_bytes());
vec.extend_from_slice(&self.level.to_ne_bytes());
vec.extend_from_slice(&self.prio.to_ne_bytes());
vec
}
}
fn compute_buffer_ticks() -> u32 {
let tick_in_usec = *TICK_IN_USEC;
(HTB_DEFAULT_BURST_BYTES as f64 * tick_in_usec * 1_000_000.0
/ HTB_UNLIMITED_RATE_BPS as f64) as u32
}
#[derive(Debug, Clone)]
pub struct QdiscHtbRequest {
pub inner: QdiscRequestInner,
}
impl QdiscHtbRequest {
pub fn new(inner: QdiscRequestInner) -> Self {
Self { inner }
}
pub fn build(self) -> NetlinkMessage<RouteNetlinkMessage> {
let mut tc_message = TcMessage::with_index(self.inner.interface_index);
tc_message.header.parent = TcHandle::ROOT;
tc_message.header.handle = TcHandle::from(0x0001_0000);
tc_message.attributes.push(TcAttribute::Kind("htb".to_string()));
let glob = HtbGlob::default();
let init_nla = build_nla(TCA_HTB_INIT, &glob.as_bytes());
tc_message.attributes.push(TcAttribute::Other(build_nested_options(init_nla)));
let mut nl_req = NetlinkMessage::from(RouteNetlinkMessage::NewQueueDiscipline(tc_message));
nl_req.header.flags = NLM_F_CREATE | NLM_F_REPLACE | NLM_F_REQUEST | NLM_F_ACK;
nl_req
}
}
#[derive(Debug, Clone)]
pub struct HtbClassRequest {
pub inner: QdiscRequestInner,
pub replace: bool,
}
impl HtbClassRequest {
pub fn new(inner: QdiscRequestInner) -> Self {
Self { inner, replace: false }
}
pub fn with_replace(mut self, replace: bool) -> Self {
self.replace = replace;
self
}
pub fn build(self) -> NetlinkMessage<RouteNetlinkMessage> {
let mut tc_message = TcMessage::with_index(self.inner.interface_index);
tc_message.header.parent = self.inner.parent;
tc_message.header.handle = self.inner.handle;
tc_message.attributes.push(TcAttribute::Kind("htb".to_string()));
let buffer_ticks = compute_buffer_ticks();
let rate_spec = TcRateSpec {
rate: HTB_UNLIMITED_RATE_BPS,
linklayer: 1, cell_align: -1,
..Default::default()
};
let opt = HtbOpt {
rate: rate_spec,
ceil: rate_spec,
buffer: buffer_ticks,
cbuffer: buffer_ticks,
quantum: 0, level: 0, prio: 0, };
let parms_nla = build_nla(TCA_HTB_PARMS, &opt.as_bytes());
let rtab_nla = build_nla(TCA_HTB_RTAB, &DEFAULT_RATE_TABLE);
let ctab_nla = build_nla(TCA_HTB_CTAB, &DEFAULT_RATE_TABLE);
let mut combined = parms_nla;
combined.extend(rtab_nla);
combined.extend(ctab_nla);
tc_message.attributes.push(TcAttribute::Other(build_nested_options(combined)));
let mut nl_req = NetlinkMessage::from(RouteNetlinkMessage::NewTrafficClass(tc_message));
nl_req.header.flags = if self.replace {
NLM_F_CREATE | NLM_F_REPLACE | NLM_F_REQUEST | NLM_F_ACK
} else {
NLM_F_CREATE | NLM_F_EXCL | NLM_F_REQUEST | NLM_F_ACK
};
nl_req
}
}