use crate::netlink::Error;
pub mod constants {
pub const ROOT: u32 = 0xFFFFFFFF;
pub const INGRESS: u32 = 0xFFFFFFF1;
pub const CLSACT: u32 = 0xFFFFFFF2;
pub const UNSPEC: u32 = 0;
}
pub use constants::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Handle {
pub major: u16,
pub minor: u16,
}
impl Handle {
pub const fn new(major: u16, minor: u16) -> Self {
Self { major, minor }
}
pub const fn from_raw(raw: u32) -> Self {
Self {
major: (raw >> 16) as u16,
minor: (raw & 0xFFFF) as u16,
}
}
pub const fn to_raw(self) -> u32 {
((self.major as u32) << 16) | (self.minor as u32)
}
pub const fn is_root(self) -> bool {
self.to_raw() == ROOT
}
pub const fn is_ingress(self) -> bool {
self.to_raw() == INGRESS
}
pub const fn is_clsact(self) -> bool {
self.to_raw() == CLSACT
}
pub const fn is_unspec(self) -> bool {
self.to_raw() == UNSPEC
}
}
impl From<u32> for Handle {
fn from(raw: u32) -> Self {
Self::from_raw(raw)
}
}
impl From<Handle> for u32 {
fn from(handle: Handle) -> Self {
handle.to_raw()
}
}
pub const fn make(major: u16, minor: u16) -> u32 {
((major as u32) << 16) | (minor as u32)
}
pub const fn major(handle: u32) -> u16 {
(handle >> 16) as u16
}
pub const fn minor(handle: u32) -> u16 {
(handle & 0xFFFF) as u16
}
pub fn format_handle(handle: u32) -> String {
match handle {
ROOT => "root".to_string(),
INGRESS => "ingress".to_string(),
CLSACT => "clsact".to_string(),
UNSPEC => "none".to_string(),
_ => {
let maj = major(handle);
let min = minor(handle);
if min == 0 {
format!("{:x}:", maj)
} else {
format!("{:x}:{:x}", maj, min)
}
}
}
}
pub fn parse_handle(s: &str) -> Option<u32> {
match s {
"root" => Some(ROOT),
"ingress" => Some(INGRESS),
"clsact" => Some(CLSACT),
"none" => Some(UNSPEC),
_ => {
let parts: Vec<&str> = s.split(':').collect();
if parts.len() == 2 {
let major = u16::from_str_radix(parts[0], 16).ok()?;
let minor = if parts[1].is_empty() {
0
} else {
u16::from_str_radix(parts[1], 16).ok()?
};
Some(make(major, minor))
} else {
None
}
}
}
}
pub fn parse_handle_or_err(s: &str, context: &str) -> Result<u32, Error> {
parse_handle(s)
.ok_or_else(|| Error::InvalidMessage(format!("invalid {} handle: {}", context, s)))
}
pub fn parse_parent(parent: Option<&str>) -> Result<u32, Error> {
match parent {
Some(s) => parse_handle_or_err(s, "parent"),
None => Ok(ROOT),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_make_and_split() {
let handle = make(1, 0);
assert_eq!(major(handle), 1);
assert_eq!(minor(handle), 0);
assert_eq!(handle, 0x00010000);
let handle = make(0x10, 0x20);
assert_eq!(major(handle), 0x10);
assert_eq!(minor(handle), 0x20);
}
#[test]
fn test_parse_handle() {
assert_eq!(parse_handle("root"), Some(ROOT));
assert_eq!(parse_handle("ingress"), Some(INGRESS));
assert_eq!(parse_handle("clsact"), Some(CLSACT));
assert_eq!(parse_handle("none"), Some(UNSPEC));
assert_eq!(parse_handle("1:"), Some(make(1, 0)));
assert_eq!(parse_handle("1:0"), Some(make(1, 0)));
assert_eq!(parse_handle("10:20"), Some(make(0x10, 0x20)));
assert_eq!(parse_handle("ffff:ffff"), Some(make(0xffff, 0xffff)));
assert_eq!(parse_handle("invalid"), None);
assert_eq!(parse_handle("1"), None);
}
#[test]
fn test_format_handle() {
assert_eq!(format_handle(ROOT), "root");
assert_eq!(format_handle(INGRESS), "ingress");
assert_eq!(format_handle(CLSACT), "clsact");
assert_eq!(format_handle(UNSPEC), "none");
assert_eq!(format_handle(make(1, 0)), "1:");
assert_eq!(format_handle(make(0x10, 0x20)), "10:20");
}
#[test]
fn test_handle_struct() {
let h = Handle::new(1, 0);
assert_eq!(h.to_raw(), 0x00010000);
assert!(!h.is_root());
let h = Handle::from_raw(ROOT);
assert!(h.is_root());
let h: Handle = 0x00010000u32.into();
assert_eq!(h.major, 1);
assert_eq!(h.minor, 0);
let raw: u32 = h.into();
assert_eq!(raw, 0x00010000);
}
}