use crate::macros::{GenlMessage, NetlinkAttrs, __rt};
use crate::netlink::attr::{AttrIter, NLA_F_NESTED};
use crate::netlink::MessageBuilder;
use crate::{Error, Result};
use super::types::{
NetShaperAttr, NetShaperCapsAttr, NetShaperCmd, NetShaperHandleAttr, NetShaperMetric,
NetShaperScope,
};
fn emit_handle_nest(b: &mut MessageBuilder, attr: u16, h: &NetShaperHandle) -> Result<()> {
let nest = b.nest_start(attr | NLA_F_NESTED);
h.write_attrs(b)?;
b.nest_end(nest);
Ok(())
}
#[derive(NetlinkAttrs, Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct NetShaperHandle {
#[genl_attr(NetShaperHandleAttr::Scope, repr = "u32")]
pub scope: Option<NetShaperScope>,
#[genl_attr(NetShaperHandleAttr::Id)]
pub id: Option<u32>,
}
impl NetShaperHandle {
pub fn new(scope: NetShaperScope, id: u32) -> Self {
Self {
scope: Some(scope),
id: Some(id),
}
}
pub const fn netdev() -> Self {
Self {
scope: Some(NetShaperScope::Netdev),
id: Some(0),
}
}
pub fn queue(index: u32) -> Self {
Self::new(NetShaperScope::Queue, index)
}
pub fn node(id: u32) -> Self {
Self::new(NetShaperScope::Node, id)
}
}
#[derive(GenlMessage, Debug, Default, Clone)]
#[genl_message(cmd = NetShaperCmd::Get)]
pub struct NetShaperGetRequest {
#[genl_attr(NetShaperAttr::Ifindex)]
pub ifindex: u32,
#[genl_attr(NetShaperAttr::Handle, nested)]
pub handle: Option<NetShaperHandle>,
}
impl NetShaperGetRequest {
pub fn by_handle(ifindex: u32, handle: NetShaperHandle) -> Self {
Self {
ifindex,
handle: Some(handle),
}
}
pub fn dump(ifindex: u32) -> Self {
Self {
ifindex,
handle: None,
}
}
}
#[derive(GenlMessage, Debug, Default, Clone)]
#[genl_message(cmd = NetShaperCmd::Get)]
#[non_exhaustive]
pub struct NetShaperReply {
#[genl_attr(NetShaperAttr::Ifindex)]
pub ifindex: u32,
#[genl_attr(NetShaperAttr::Handle, nested)]
pub handle: Option<NetShaperHandle>,
#[genl_attr(NetShaperAttr::Parent, nested)]
pub parent: Option<NetShaperHandle>,
#[genl_attr(NetShaperAttr::Metric, repr = "u32")]
pub metric: Option<NetShaperMetric>,
#[genl_attr(NetShaperAttr::BwMin)]
pub bw_min: Option<u64>,
#[genl_attr(NetShaperAttr::BwMax)]
pub bw_max: Option<u64>,
#[genl_attr(NetShaperAttr::Burst)]
pub burst: Option<u64>,
#[genl_attr(NetShaperAttr::Priority)]
pub priority: Option<u32>,
#[genl_attr(NetShaperAttr::Weight)]
pub weight: Option<u32>,
}
#[derive(GenlMessage, Debug, Default, Clone)]
#[genl_message(cmd = NetShaperCmd::Set)]
pub struct NetShaperSetRequest {
#[genl_attr(NetShaperAttr::Ifindex)]
pub ifindex: u32,
#[genl_attr(NetShaperAttr::Handle, nested)]
pub handle: Option<NetShaperHandle>,
#[genl_attr(NetShaperAttr::Metric, repr = "u32")]
pub metric: Option<NetShaperMetric>,
#[genl_attr(NetShaperAttr::BwMin)]
pub bw_min: Option<u64>,
#[genl_attr(NetShaperAttr::BwMax)]
pub bw_max: Option<u64>,
#[genl_attr(NetShaperAttr::Burst)]
pub burst: Option<u64>,
#[genl_attr(NetShaperAttr::Priority)]
pub priority: Option<u32>,
#[genl_attr(NetShaperAttr::Weight)]
pub weight: Option<u32>,
}
impl NetShaperSetRequest {
pub fn new(ifindex: u32, handle: NetShaperHandle) -> Self {
Self {
ifindex,
handle: Some(handle),
..Self::default()
}
}
#[must_use]
pub fn metric(mut self, metric: NetShaperMetric) -> Self {
self.metric = Some(metric);
self
}
#[must_use]
pub fn bw_min(mut self, bw: u64) -> Self {
self.bw_min = Some(bw);
self
}
#[must_use]
pub fn bw_max(mut self, bw: u64) -> Self {
self.bw_max = Some(bw);
self
}
#[must_use]
pub fn burst(mut self, bytes: u64) -> Self {
self.burst = Some(bytes);
self
}
#[must_use]
pub fn priority(mut self, prio: u32) -> Self {
self.priority = Some(prio);
self
}
#[must_use]
pub fn weight(mut self, weight: u32) -> Self {
self.weight = Some(weight);
self
}
}
#[derive(GenlMessage, Debug, Default, Clone)]
#[genl_message(cmd = NetShaperCmd::Delete)]
pub struct NetShaperDeleteRequest {
#[genl_attr(NetShaperAttr::Ifindex)]
pub ifindex: u32,
#[genl_attr(NetShaperAttr::Handle, nested)]
pub handle: Option<NetShaperHandle>,
}
impl NetShaperDeleteRequest {
pub fn new(ifindex: u32, handle: NetShaperHandle) -> Self {
Self {
ifindex,
handle: Some(handle),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct NetShaperLeaf {
pub handle: NetShaperHandle,
pub priority: Option<u32>,
pub weight: Option<u32>,
}
impl NetShaperLeaf {
pub fn new(handle: NetShaperHandle) -> Self {
Self {
handle,
..Self::default()
}
}
pub fn queue(index: u32) -> Self {
Self::new(NetShaperHandle::queue(index))
}
#[must_use]
pub fn priority(mut self, priority: u32) -> Self {
self.priority = Some(priority);
self
}
#[must_use]
pub fn weight(mut self, weight: u32) -> Self {
self.weight = Some(weight);
self
}
fn write_leaf(&self, b: &mut MessageBuilder) -> Result<()> {
let nest = b.nest_start(NetShaperAttr::Leaves as u16 | NLA_F_NESTED);
emit_handle_nest(b, NetShaperAttr::Handle as u16, &self.handle)?;
if let Some(p) = self.priority {
__rt::emit_u32_attr(b, NetShaperAttr::Priority as u16, p);
}
if let Some(w) = self.weight {
__rt::emit_u32_attr(b, NetShaperAttr::Weight as u16, w);
}
b.nest_end(nest);
Ok(())
}
}
#[derive(Debug, Clone, Default)]
pub struct NetShaperGroupRequest {
pub ifindex: u32,
pub parent: Option<NetShaperHandle>,
pub handle: Option<NetShaperHandle>,
pub metric: Option<NetShaperMetric>,
pub bw_min: Option<u64>,
pub bw_max: Option<u64>,
pub burst: Option<u64>,
pub priority: Option<u32>,
pub weight: Option<u32>,
pub leaves: Vec<NetShaperLeaf>,
}
impl NetShaperGroupRequest {
pub fn new(ifindex: u32) -> Self {
Self {
ifindex,
..Self::default()
}
}
#[must_use]
pub fn node(mut self, handle: NetShaperHandle) -> Self {
self.handle = Some(handle);
self
}
#[must_use]
pub fn parent(mut self, parent: NetShaperHandle) -> Self {
self.parent = Some(parent);
self
}
#[must_use]
pub fn metric(mut self, metric: NetShaperMetric) -> Self {
self.metric = Some(metric);
self
}
#[must_use]
pub fn bw_min(mut self, bw: u64) -> Self {
self.bw_min = Some(bw);
self
}
#[must_use]
pub fn bw_max(mut self, bw: u64) -> Self {
self.bw_max = Some(bw);
self
}
#[must_use]
pub fn burst(mut self, bytes: u64) -> Self {
self.burst = Some(bytes);
self
}
#[must_use]
pub fn priority(mut self, prio: u32) -> Self {
self.priority = Some(prio);
self
}
#[must_use]
pub fn weight(mut self, weight: u32) -> Self {
self.weight = Some(weight);
self
}
#[must_use]
pub fn leaf(mut self, leaf: NetShaperLeaf) -> Self {
self.leaves.push(leaf);
self
}
#[must_use]
pub fn leaves(mut self, leaves: impl IntoIterator<Item = NetShaperLeaf>) -> Self {
self.leaves.extend(leaves);
self
}
}
impl GenlMessage for NetShaperGroupRequest {
const CMD: u8 = NetShaperCmd::Group as u8;
fn to_bytes(&self, b: &mut MessageBuilder) -> Result<()> {
__rt::emit_u32_attr(b, NetShaperAttr::Ifindex as u16, self.ifindex);
if let Some(parent) = &self.parent {
emit_handle_nest(b, NetShaperAttr::Parent as u16, parent)?;
}
if let Some(handle) = &self.handle {
emit_handle_nest(b, NetShaperAttr::Handle as u16, handle)?;
}
if let Some(metric) = self.metric {
__rt::emit_u32_attr(b, NetShaperAttr::Metric as u16, metric as u32);
}
if let Some(v) = self.bw_min {
__rt::emit_u64_attr(b, NetShaperAttr::BwMin as u16, v);
}
if let Some(v) = self.bw_max {
__rt::emit_u64_attr(b, NetShaperAttr::BwMax as u16, v);
}
if let Some(v) = self.burst {
__rt::emit_u64_attr(b, NetShaperAttr::Burst as u16, v);
}
if let Some(v) = self.priority {
__rt::emit_u32_attr(b, NetShaperAttr::Priority as u16, v);
}
if let Some(v) = self.weight {
__rt::emit_u32_attr(b, NetShaperAttr::Weight as u16, v);
}
for leaf in &self.leaves {
leaf.write_leaf(b)?;
}
Ok(())
}
fn from_bytes(_payload: &[u8]) -> Result<Self> {
Err(Error::InvalidMessage(
"NetShaperGroupRequest is a request type; the group reply is parsed as NetShaperReply"
.into(),
))
}
}
#[derive(GenlMessage, Debug, Default, Clone)]
#[genl_message(cmd = NetShaperCmd::CapGet)]
pub struct NetShaperCapsGetRequest {
#[genl_attr(NetShaperCapsAttr::Ifindex)]
pub ifindex: u32,
#[genl_attr(NetShaperCapsAttr::Scope, repr = "u32")]
pub scope: Option<NetShaperScope>,
}
impl NetShaperCapsGetRequest {
pub fn for_scope(ifindex: u32, scope: NetShaperScope) -> Self {
Self {
ifindex,
scope: Some(scope),
}
}
pub fn dump(ifindex: u32) -> Self {
Self {
ifindex,
scope: None,
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct NetShaperCapsReply {
pub ifindex: u32,
pub scope: Option<NetShaperScope>,
pub support_metric_bps: bool,
pub support_metric_pps: bool,
pub support_nesting: bool,
pub support_bw_min: bool,
pub support_bw_max: bool,
pub support_burst: bool,
pub support_priority: bool,
pub support_weight: bool,
}
impl GenlMessage for NetShaperCapsReply {
const CMD: u8 = NetShaperCmd::CapGet as u8;
fn to_bytes(&self, _builder: &mut MessageBuilder) -> Result<()> {
Err(Error::InvalidMessage(
"NetShaperCapsReply is read-only; build a NetShaperCapsGetRequest to query caps".into(),
))
}
fn from_bytes(payload: &[u8]) -> Result<Self> {
let mut out = Self::default();
for (ty, body) in AttrIter::new(payload) {
if ty == NetShaperCapsAttr::Ifindex as u16 {
out.ifindex = __rt::parse_u32_attr(body)?;
} else if ty == NetShaperCapsAttr::Scope as u16 {
let raw = __rt::parse_u32_attr(body)?;
out.scope = NetShaperScope::try_from(raw).ok();
} else if ty == NetShaperCapsAttr::SupportMetricBps as u16 {
out.support_metric_bps = true;
} else if ty == NetShaperCapsAttr::SupportMetricPps as u16 {
out.support_metric_pps = true;
} else if ty == NetShaperCapsAttr::SupportNesting as u16 {
out.support_nesting = true;
} else if ty == NetShaperCapsAttr::SupportBwMin as u16 {
out.support_bw_min = true;
} else if ty == NetShaperCapsAttr::SupportBwMax as u16 {
out.support_bw_max = true;
} else if ty == NetShaperCapsAttr::SupportBurst as u16 {
out.support_burst = true;
} else if ty == NetShaperCapsAttr::SupportPriority as u16 {
out.support_priority = true;
} else if ty == NetShaperCapsAttr::SupportWeight as u16 {
out.support_weight = true;
}
}
Ok(out)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::macros::__rt;
fn payload(write: impl FnOnce(&mut MessageBuilder)) -> Vec<u8> {
let mut b = MessageBuilder::new(0, 0);
let start = b.len();
write(&mut b);
b.as_bytes()[start..].to_vec()
}
#[test]
fn get_request_by_handle_emits_ifindex_and_nested_handle() {
let req = NetShaperGetRequest::by_handle(3, NetShaperHandle::queue(7));
let body = payload(|b| req.to_bytes(b).unwrap());
let attrs: Vec<u16> = __rt::attr_iter(&body).map(|(ty, _)| ty).collect();
assert!(attrs.contains(&(NetShaperAttr::Ifindex as u16)));
assert!(attrs.contains(&(NetShaperAttr::Handle as u16)));
assert_eq!(attrs.len(), 2);
}
#[test]
fn get_request_dump_emits_only_ifindex() {
let req = NetShaperGetRequest::dump(2);
let body = payload(|b| req.to_bytes(b).unwrap());
let attrs: Vec<u16> = __rt::attr_iter(&body).map(|(ty, _)| ty).collect();
assert_eq!(attrs, vec![NetShaperAttr::Ifindex as u16]);
}
#[test]
fn set_request_builder_chains_optional_fields() {
let req = NetShaperSetRequest::new(4, NetShaperHandle::queue(1))
.metric(NetShaperMetric::Bps)
.bw_max(1_000_000_000)
.burst(1 << 16)
.priority(5);
let body = payload(|b| req.to_bytes(b).unwrap());
let attrs: Vec<u16> = __rt::attr_iter(&body).map(|(ty, _)| ty).collect();
assert_eq!(attrs.len(), 6);
for required in [
NetShaperAttr::Ifindex,
NetShaperAttr::Handle,
NetShaperAttr::Metric,
NetShaperAttr::BwMax,
NetShaperAttr::Burst,
NetShaperAttr::Priority,
] {
assert!(attrs.contains(&(required as u16)), "missing {required:?}");
}
assert!(!attrs.contains(&(NetShaperAttr::BwMin as u16)));
assert!(!attrs.contains(&(NetShaperAttr::Weight as u16)));
}
#[test]
fn delete_request_emits_two_attrs() {
let req = NetShaperDeleteRequest::new(1, NetShaperHandle::queue(0));
let body = payload(|b| req.to_bytes(b).unwrap());
let attrs: Vec<u16> = __rt::attr_iter(&body).map(|(ty, _)| ty).collect();
assert_eq!(attrs.len(), 2);
}
#[test]
fn caps_reply_parses_flag_presence() {
let body = payload(|b| {
__rt::emit_u32_attr(b, NetShaperCapsAttr::Ifindex as u16, 7);
__rt::emit_u32_attr(
b,
NetShaperCapsAttr::Scope as u16,
NetShaperScope::Queue as u32,
);
__rt::emit_flag_attr(b, NetShaperCapsAttr::SupportMetricBps as u16);
__rt::emit_flag_attr(b, NetShaperCapsAttr::SupportBwMax as u16);
__rt::emit_flag_attr(b, NetShaperCapsAttr::SupportPriority as u16);
});
let reply = NetShaperCapsReply::from_bytes(&body).expect("parse");
assert_eq!(reply.ifindex, 7);
assert_eq!(reply.scope, Some(NetShaperScope::Queue));
assert!(reply.support_metric_bps);
assert!(!reply.support_metric_pps);
assert!(reply.support_bw_max);
assert!(!reply.support_bw_min);
assert!(reply.support_priority);
assert!(!reply.support_weight);
assert!(!reply.support_nesting);
assert!(!reply.support_burst);
}
#[test]
fn caps_reply_to_bytes_is_read_only() {
let r = NetShaperCapsReply::default();
let mut b = MessageBuilder::new(0, 0);
assert!(r.to_bytes(&mut b).is_err());
}
#[test]
fn handle_helpers_set_expected_scope_and_id() {
let h = NetShaperHandle::netdev();
assert_eq!(h.scope, Some(NetShaperScope::Netdev));
assert_eq!(h.id, Some(0));
let q = NetShaperHandle::queue(5);
assert_eq!(q.scope, Some(NetShaperScope::Queue));
assert_eq!(q.id, Some(5));
let n = NetShaperHandle::node(42);
assert_eq!(n.scope, Some(NetShaperScope::Node));
assert_eq!(n.id, Some(42));
}
#[test]
fn reply_parses_full_state() {
let mut handle_payload = MessageBuilder::new(0, 0);
let h_start = handle_payload.len();
__rt::emit_u32_attr(
&mut handle_payload,
NetShaperHandleAttr::Scope as u16,
NetShaperScope::Queue as u32,
);
__rt::emit_u32_attr(&mut handle_payload, NetShaperHandleAttr::Id as u16, 3);
let handle_bytes = handle_payload.as_bytes()[h_start..].to_vec();
let body = payload(|b| {
__rt::emit_u32_attr(b, NetShaperAttr::Ifindex as u16, 9);
__rt::emit_bytes_attr(b, NetShaperAttr::Handle as u16, &handle_bytes);
__rt::emit_u32_attr(
b,
NetShaperAttr::Metric as u16,
NetShaperMetric::Bps as u32,
);
__rt::emit_u64_attr(b, NetShaperAttr::BwMax as u16, 5_000_000_000);
__rt::emit_u32_attr(b, NetShaperAttr::Priority as u16, 1);
});
let reply = NetShaperReply::from_bytes(&body).expect("parse");
assert_eq!(reply.ifindex, 9);
assert_eq!(
reply.handle,
Some(NetShaperHandle::new(NetShaperScope::Queue, 3))
);
assert_eq!(reply.parent, None);
assert_eq!(reply.metric, Some(NetShaperMetric::Bps));
assert_eq!(reply.bw_max, Some(5_000_000_000));
assert_eq!(reply.priority, Some(1));
assert_eq!(reply.weight, None);
}
#[test]
fn leaf_builder_sets_priority_and_weight() {
let leaf = NetShaperLeaf::queue(3).priority(7).weight(11);
assert_eq!(leaf.handle, NetShaperHandle::queue(3));
assert_eq!(leaf.priority, Some(7));
assert_eq!(leaf.weight, Some(11));
}
#[test]
fn group_request_emits_node_attrs_and_one_leaves_nest_per_leaf() {
let req = NetShaperGroupRequest::new(9)
.bw_max(1_000_000_000)
.metric(NetShaperMetric::Bps)
.leaf(NetShaperLeaf::queue(0))
.leaf(NetShaperLeaf::queue(1).priority(2));
let body = payload(|b| req.to_bytes(b).unwrap());
let attrs: Vec<u16> = __rt::attr_iter(&body).map(|(ty, _)| ty).collect();
assert!(attrs.contains(&(NetShaperAttr::Ifindex as u16)));
assert!(attrs.contains(&(NetShaperAttr::BwMax as u16)));
assert!(attrs.contains(&(NetShaperAttr::Metric as u16)));
assert!(!attrs.contains(&(NetShaperAttr::Handle as u16)));
let leaves = attrs
.iter()
.filter(|&&t| t == NetShaperAttr::Leaves as u16)
.count();
assert_eq!(leaves, 2, "one LEAVES nest per leaf");
}
#[test]
fn group_request_leaf_nest_carries_handle_and_scheduling() {
let req = NetShaperGroupRequest::new(1).leaf(NetShaperLeaf::queue(5).priority(3).weight(9));
let body = payload(|b| req.to_bytes(b).unwrap());
let leaf_payload = __rt::attr_iter(&body)
.find(|(ty, _)| *ty == NetShaperAttr::Leaves as u16)
.map(|(_, p)| p.to_vec())
.expect("a LEAVES nest");
let mut saw_handle = false;
let mut saw_priority = None;
let mut saw_weight = None;
for (ty, p) in __rt::attr_iter(&leaf_payload) {
if ty == NetShaperAttr::Handle as u16 {
saw_handle = true;
let inner: Vec<u16> = __rt::attr_iter(p).map(|(t, _)| t).collect();
assert!(inner.contains(&(NetShaperHandleAttr::Scope as u16)));
assert!(inner.contains(&(NetShaperHandleAttr::Id as u16)));
} else if ty == NetShaperAttr::Priority as u16 {
saw_priority = Some(__rt::parse_u32_attr(p).unwrap());
} else if ty == NetShaperAttr::Weight as u16 {
saw_weight = Some(__rt::parse_u32_attr(p).unwrap());
}
}
assert!(saw_handle, "leaf carries a nested handle");
assert_eq!(saw_priority, Some(3));
assert_eq!(saw_weight, Some(9));
}
#[test]
fn group_request_pins_node_handle_and_parent_when_set() {
let req = NetShaperGroupRequest::new(2)
.node(NetShaperHandle::node(7))
.parent(NetShaperHandle::netdev())
.leaf(NetShaperLeaf::queue(0));
let body = payload(|b| req.to_bytes(b).unwrap());
let attrs: Vec<u16> = __rt::attr_iter(&body).map(|(ty, _)| ty).collect();
assert!(attrs.contains(&(NetShaperAttr::Handle as u16)));
assert!(attrs.contains(&(NetShaperAttr::Parent as u16)));
}
#[test]
fn group_request_from_bytes_is_request_only() {
assert!(NetShaperGroupRequest::from_bytes(&[]).is_err());
}
}