use std::collections::BTreeMap;
use super::{
action::ActionConfig,
builder::MessageBuilder,
message::NLMSG_HDRLEN,
tc::{ClassConfig, QdiscConfig},
};
pub(crate) type AttrMap = BTreeMap<u16, Vec<u8>>;
pub(crate) fn parse_attrs(mut input: &[u8]) -> AttrMap {
let mut out = AttrMap::new();
while input.len() >= 4 {
let len = u16::from_ne_bytes(input[0..2].try_into().unwrap()) as usize;
let ty = u16::from_ne_bytes(input[2..4].try_into().unwrap()) & 0x3FFF;
assert!(
(4..=input.len()).contains(&len),
"attr {ty}: bogus nla_len {len} with {} bytes left",
input.len(),
);
out.insert(ty, input[4..len].to_vec());
input = &input[len.next_multiple_of(4).min(input.len())..];
}
out
}
pub(crate) fn builder_attrs(builder: &MessageBuilder) -> AttrMap {
parse_attrs(&builder.as_bytes()[NLMSG_HDRLEN..])
}
fn encode(f: impl FnOnce(&mut MessageBuilder) -> crate::Result<()>) -> AttrMap {
let mut builder = MessageBuilder::new(0, 0);
f(&mut builder).expect("write_options failed");
builder_attrs(&builder)
}
pub(crate) fn qdisc_attrs(cfg: &impl QdiscConfig) -> AttrMap {
encode(|b| cfg.write_options(b))
}
pub(crate) fn class_attrs(cfg: &impl ClassConfig) -> AttrMap {
encode(|b| cfg.write_options(b))
}
pub(crate) fn action_attrs(cfg: &impl ActionConfig) -> AttrMap {
encode(|b| cfg.write_options(b))
}
pub(crate) fn u32_at(attrs: &AttrMap, attr_type: u16, off: usize) -> u32 {
let payload = attrs
.get(&attr_type)
.unwrap_or_else(|| panic!("attribute {attr_type} not emitted; got {:?}", attrs.keys()));
let end = off + 4;
assert!(
end <= payload.len(),
"attribute {attr_type}: want u32 at {off}..{end}, payload is only {} bytes",
payload.len(),
);
u32::from_ne_bytes(payload[off..end].try_into().unwrap())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_attrs_splits_a_padded_stream() {
let mut b = MessageBuilder::new(0, 0);
b.append_attr(1, &[0xaa]); b.append_attr(2, &7u32.to_ne_bytes());
let attrs = builder_attrs(&b);
assert_eq!(attrs.len(), 2);
assert_eq!(attrs[&1], vec![0xaa]);
assert_eq!(u32_at(&attrs, 2, 0), 7);
}
#[test]
fn parse_attrs_masks_the_nested_flag() {
let mut b = MessageBuilder::new(0, 0);
let nest = b.nest_start(3);
b.append_attr(1, &1u32.to_ne_bytes());
b.nest_end(nest);
let attrs = builder_attrs(&b);
let inner = parse_attrs(&attrs[&3]);
assert_eq!(u32_at(&inner, 1, 0), 1);
}
}