mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2025-02-25 18:55:32 -06:00
Backport the TC_HANDLE parser to main.
Replace C code with native Rust, including unit tests to find the edge-cases.
This commit is contained in:
parent
6fe9748ea4
commit
beead5a303
1
src/rust/Cargo.lock
generated
1
src/rust/Cargo.lock
generated
@ -1327,7 +1327,6 @@ name = "lqos_bus"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"cc",
|
||||
"criterion",
|
||||
"log",
|
||||
"lqos_config",
|
||||
|
@ -17,9 +17,6 @@ tokio = { version = "1", features = [ "rt", "macros", "net", "io-util", "time" ]
|
||||
log = "0"
|
||||
nix = "0"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0", features = [ "html_reports", "async_tokio"] }
|
||||
|
||||
|
@ -1,3 +0,0 @@
|
||||
fn main() {
|
||||
cc::Build::new().file("src/tc_handle_parser.c").compile("tc_handle_parse.o");
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use log::error;
|
||||
use lqos_utils::hex_string::read_hex_string;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::ffi::CString;
|
||||
use thiserror::Error;
|
||||
|
||||
/// Provides consistent handling of TC handle types.
|
||||
@ -9,20 +9,9 @@ use thiserror::Error;
|
||||
)]
|
||||
pub struct TcHandle(u32);
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
type __u32 = ::std::os::raw::c_uint;
|
||||
#[allow(dead_code)]
|
||||
const TC_H_ROOT: u32 = 4294967295;
|
||||
#[allow(dead_code)]
|
||||
const TC_H_UNSPEC: u32 = 0;
|
||||
|
||||
extern "C" {
|
||||
pub fn get_tc_classid(
|
||||
h: *mut __u32,
|
||||
str_: *const ::std::os::raw::c_char,
|
||||
) -> ::std::os::raw::c_int;
|
||||
}
|
||||
|
||||
impl TcHandle {
|
||||
/// Returns the TC handle as two values, indicating major and minor
|
||||
/// TC handle values.
|
||||
@ -36,23 +25,26 @@ impl TcHandle {
|
||||
/// Build a TC handle from a string. This is actually a complicated
|
||||
/// operation, since it has to handle "root" and other strings as well
|
||||
/// as simple "1:2" mappings. Calls a C function to handle this gracefully.
|
||||
pub fn from_string<S: ToString>(
|
||||
handle: S,
|
||||
pub fn from_string(
|
||||
handle: &str,
|
||||
) -> Result<Self, TcHandleParseError> {
|
||||
let mut tc_handle: __u32 = 0;
|
||||
let str = CString::new(handle.to_string());
|
||||
if str.is_err() {
|
||||
error!("Unable to convert {} to a C-String.", handle.to_string());
|
||||
return Err(TcHandleParseError::CString);
|
||||
}
|
||||
let str = str.unwrap();
|
||||
let handle_pointer: *mut __u32 = &mut tc_handle;
|
||||
let result = unsafe { get_tc_classid(handle_pointer, str.as_ptr()) };
|
||||
if result != 0 {
|
||||
error!("Unable to parse {} as a valid TC handle", handle.to_string());
|
||||
Err(TcHandleParseError::InvalidInput)
|
||||
} else {
|
||||
Ok(Self(tc_handle))
|
||||
let handle = handle.trim();
|
||||
match handle {
|
||||
"root" => Ok(Self(TC_H_ROOT)),
|
||||
"none" => Ok(Self(TC_H_UNSPEC)),
|
||||
_ => {
|
||||
if !handle.contains(':') {
|
||||
error!("Unable to parse TC handle {handle}. Must contain a colon.");
|
||||
return Err(TcHandleParseError::InvalidInput(handle.to_string()));
|
||||
}
|
||||
let parts: Vec<&str> = handle.split(':').collect();
|
||||
let major = read_hex_string(parts[0]).map_err(|_| TcHandleParseError::InvalidInput(handle.to_string()))?;
|
||||
let minor = read_hex_string(parts[1]).map_err(|_| TcHandleParseError::InvalidInput(handle.to_string()))?;
|
||||
if major >= (1<<16) || minor >= (1<<16) {
|
||||
return Err(TcHandleParseError::InvalidInput(handle.to_string()));
|
||||
}
|
||||
Ok(Self((major << 16) | minor))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,7 +78,7 @@ pub enum TcHandleParseError {
|
||||
)]
|
||||
CString,
|
||||
#[error("Invalid input")]
|
||||
InvalidInput,
|
||||
InvalidInput(String),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -151,4 +143,10 @@ mod test {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blank_minor() {
|
||||
let tc = TcHandle::from_string("7FFF:").unwrap();
|
||||
assert_eq!(tc.to_string().to_uppercase(), "7FFF:0");
|
||||
}
|
||||
}
|
||||
|
@ -1,46 +0,0 @@
|
||||
// Imported from https://github.com/thebracket/cpumap-pping/blob/master/src/xdp_iphash_to_cpu_cmdline.c
|
||||
// Because it uses strtoul and is based on the TC source, including it directly
|
||||
// seemed like the path of least resistance.
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/pkt_sched.h> /* TC macros */
|
||||
|
||||
/* Handle classid parsing based on iproute source */
|
||||
int get_tc_classid(__u32 *h, const char *str)
|
||||
{
|
||||
__u32 major, minor;
|
||||
char *p;
|
||||
|
||||
major = TC_H_ROOT;
|
||||
if (strcmp(str, "root") == 0)
|
||||
goto ok;
|
||||
major = TC_H_UNSPEC;
|
||||
if (strcmp(str, "none") == 0)
|
||||
goto ok;
|
||||
major = strtoul(str, &p, 16);
|
||||
if (p == str) {
|
||||
major = 0;
|
||||
if (*p != ':')
|
||||
return -1;
|
||||
}
|
||||
if (*p == ':') {
|
||||
if (major >= (1<<16))
|
||||
return -1;
|
||||
major <<= 16;
|
||||
str = p+1;
|
||||
minor = strtoul(str, &p, 16);
|
||||
if (*p != 0)
|
||||
return -1;
|
||||
if (minor >= (1<<16))
|
||||
return -1;
|
||||
major |= minor;
|
||||
} else if (*p != 0)
|
||||
return -1;
|
||||
|
||||
ok:
|
||||
*h = major;
|
||||
return 0;
|
||||
}
|
@ -19,6 +19,9 @@ use thiserror::Error;
|
||||
/// assert_eq!(read_hex_string("0x12AD").unwrap(), 4781);
|
||||
/// ```
|
||||
pub fn read_hex_string(s: &str) -> Result<u32, HexParseError> {
|
||||
if s.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
let result = u32::from_str_radix(&s.replace("0x", ""), 16);
|
||||
match result {
|
||||
Ok(data) => Ok(data),
|
||||
|
Loading…
Reference in New Issue
Block a user