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:
Herbert Wolverson 2023-03-20 17:40:03 +00:00
parent 6fe9748ea4
commit beead5a303
6 changed files with 30 additions and 82 deletions

1
src/rust/Cargo.lock generated
View File

@ -1327,7 +1327,6 @@ name = "lqos_bus"
version = "0.1.0"
dependencies = [
"bincode",
"cc",
"criterion",
"log",
"lqos_config",

View File

@ -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"] }

View File

@ -1,3 +0,0 @@
fn main() {
cc::Build::new().file("src/tc_handle_parser.c").compile("tc_handle_parse.o");
}

View File

@ -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");
}
}

View File

@ -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;
}

View File

@ -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),