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"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"cc",
|
|
||||||
"criterion",
|
"criterion",
|
||||||
"log",
|
"log",
|
||||||
"lqos_config",
|
"lqos_config",
|
||||||
|
@ -17,9 +17,6 @@ tokio = { version = "1", features = [ "rt", "macros", "net", "io-util", "time" ]
|
|||||||
log = "0"
|
log = "0"
|
||||||
nix = "0"
|
nix = "0"
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
cc = "1.0"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = { version = "0", features = [ "html_reports", "async_tokio"] }
|
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 log::error;
|
||||||
|
use lqos_utils::hex_string::read_hex_string;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::ffi::CString;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
/// Provides consistent handling of TC handle types.
|
/// Provides consistent handling of TC handle types.
|
||||||
@ -9,20 +9,9 @@ use thiserror::Error;
|
|||||||
)]
|
)]
|
||||||
pub struct TcHandle(u32);
|
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;
|
const TC_H_ROOT: u32 = 4294967295;
|
||||||
#[allow(dead_code)]
|
|
||||||
const TC_H_UNSPEC: u32 = 0;
|
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 {
|
impl TcHandle {
|
||||||
/// Returns the TC handle as two values, indicating major and minor
|
/// Returns the TC handle as two values, indicating major and minor
|
||||||
/// TC handle values.
|
/// TC handle values.
|
||||||
@ -36,23 +25,26 @@ impl TcHandle {
|
|||||||
/// Build a TC handle from a string. This is actually a complicated
|
/// Build a TC handle from a string. This is actually a complicated
|
||||||
/// operation, since it has to handle "root" and other strings as well
|
/// 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.
|
/// as simple "1:2" mappings. Calls a C function to handle this gracefully.
|
||||||
pub fn from_string<S: ToString>(
|
pub fn from_string(
|
||||||
handle: S,
|
handle: &str,
|
||||||
) -> Result<Self, TcHandleParseError> {
|
) -> Result<Self, TcHandleParseError> {
|
||||||
let mut tc_handle: __u32 = 0;
|
let handle = handle.trim();
|
||||||
let str = CString::new(handle.to_string());
|
match handle {
|
||||||
if str.is_err() {
|
"root" => Ok(Self(TC_H_ROOT)),
|
||||||
error!("Unable to convert {} to a C-String.", handle.to_string());
|
"none" => Ok(Self(TC_H_UNSPEC)),
|
||||||
return Err(TcHandleParseError::CString);
|
_ => {
|
||||||
}
|
if !handle.contains(':') {
|
||||||
let str = str.unwrap();
|
error!("Unable to parse TC handle {handle}. Must contain a colon.");
|
||||||
let handle_pointer: *mut __u32 = &mut tc_handle;
|
return Err(TcHandleParseError::InvalidInput(handle.to_string()));
|
||||||
let result = unsafe { get_tc_classid(handle_pointer, str.as_ptr()) };
|
}
|
||||||
if result != 0 {
|
let parts: Vec<&str> = handle.split(':').collect();
|
||||||
error!("Unable to parse {} as a valid TC handle", handle.to_string());
|
let major = read_hex_string(parts[0]).map_err(|_| TcHandleParseError::InvalidInput(handle.to_string()))?;
|
||||||
Err(TcHandleParseError::InvalidInput)
|
let minor = read_hex_string(parts[1]).map_err(|_| TcHandleParseError::InvalidInput(handle.to_string()))?;
|
||||||
} else {
|
if major >= (1<<16) || minor >= (1<<16) {
|
||||||
Ok(Self(tc_handle))
|
return Err(TcHandleParseError::InvalidInput(handle.to_string()));
|
||||||
|
}
|
||||||
|
Ok(Self((major << 16) | minor))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +78,7 @@ pub enum TcHandleParseError {
|
|||||||
)]
|
)]
|
||||||
CString,
|
CString,
|
||||||
#[error("Invalid input")]
|
#[error("Invalid input")]
|
||||||
InvalidInput,
|
InvalidInput(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[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);
|
/// assert_eq!(read_hex_string("0x12AD").unwrap(), 4781);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn read_hex_string(s: &str) -> Result<u32, HexParseError> {
|
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);
|
let result = u32::from_str_radix(&s.replace("0x", ""), 16);
|
||||||
match result {
|
match result {
|
||||||
Ok(data) => Ok(data),
|
Ok(data) => Ok(data),
|
||||||
|
Loading…
Reference in New Issue
Block a user