use std::io;
use std::mem::size_of;
use std::os::unix::io::AsRawFd;
use std::os::unix::io::BorrowedFd;
use crate::Error;
use crate::Result;
pub type TcAttachPoint = libbpf_sys::bpf_tc_attach_point;
pub const TC_INGRESS: TcAttachPoint = libbpf_sys::BPF_TC_INGRESS;
pub const TC_EGRESS: TcAttachPoint = libbpf_sys::BPF_TC_EGRESS;
pub const TC_CUSTOM: TcAttachPoint = libbpf_sys::BPF_TC_CUSTOM;
pub type TcFlags = libbpf_sys::bpf_tc_flags;
pub const BPF_TC_F_REPLACE: TcFlags = libbpf_sys::BPF_TC_F_REPLACE;
#[expect(missing_docs)]
pub const TC_H_INGRESS: u32 = 0xFFFFFFF1;
#[expect(missing_docs)]
pub const TC_H_CLSACT: u32 = TC_H_INGRESS;
#[expect(missing_docs)]
pub const TC_H_MIN_INGRESS: u32 = 0xFFF2;
#[expect(missing_docs)]
pub const TC_H_MIN_EGRESS: u32 = 0xFFF3;
#[allow(missing_docs)]
pub const TC_H_MAJ_MASK: u32 = 0xFFFF0000;
#[allow(missing_docs)]
pub const TC_H_MIN_MASK: u32 = 0x0000FFFF;
#[derive(Clone, Copy, Debug)]
pub struct TcHook {
hook: libbpf_sys::bpf_tc_hook,
opts: libbpf_sys::bpf_tc_opts,
}
impl TcHook {
pub fn new(fd: BorrowedFd<'_>) -> Self {
let mut tc_hook = Self {
hook: libbpf_sys::bpf_tc_hook::default(),
opts: libbpf_sys::bpf_tc_opts::default(),
};
tc_hook.hook.sz = size_of::<libbpf_sys::bpf_tc_hook>() as libbpf_sys::size_t;
tc_hook.opts.sz = size_of::<libbpf_sys::bpf_tc_opts>() as libbpf_sys::size_t;
tc_hook.opts.prog_fd = fd.as_raw_fd();
tc_hook
}
pub fn create(&mut self) -> Result<Self> {
let err = unsafe { libbpf_sys::bpf_tc_hook_create(&mut self.hook as *mut _) };
if err != 0 {
let err = io::Error::from_raw_os_error(-err);
if err.kind() == io::ErrorKind::AlreadyExists {
Ok(*self)
} else {
Err(Error::from(err))
}
} else {
Ok(*self)
}
}
pub fn ifindex(&mut self, idx: i32) -> &mut Self {
self.hook.ifindex = idx;
self
}
pub fn attach_point(&mut self, ap: TcAttachPoint) -> &mut Self {
self.hook.attach_point = ap;
self
}
pub fn parent(&mut self, maj: u32, min: u32) -> &mut Self {
let parent = (maj & TC_H_MAJ_MASK) | (min & TC_H_MIN_MASK);
self.hook.parent = parent;
self
}
pub fn replace(&mut self, replace: bool) -> &mut Self {
if replace {
self.opts.flags = BPF_TC_F_REPLACE;
} else {
self.opts.flags = 0;
}
self
}
pub fn handle(&mut self, handle: u32) -> &mut Self {
self.opts.handle = handle;
self
}
pub fn get_handle(&self) -> u32 {
self.opts.handle
}
pub fn priority(&mut self, priority: u32) -> &mut Self {
self.opts.priority = priority;
self
}
pub fn get_priority(&self) -> u32 {
self.opts.priority
}
pub fn query(&mut self) -> Result<u32> {
let mut opts = self.opts;
opts.prog_id = 0;
opts.prog_fd = 0;
opts.flags = 0;
let err = unsafe { libbpf_sys::bpf_tc_query(&self.hook as *const _, &mut opts as *mut _) };
if err != 0 {
Err(Error::from(io::Error::last_os_error()))
} else {
Ok(opts.prog_id)
}
}
pub fn attach(&mut self) -> Result<Self> {
self.opts.prog_id = 0;
let err =
unsafe { libbpf_sys::bpf_tc_attach(&self.hook as *const _, &mut self.opts as *mut _) };
if err != 0 {
Err(Error::from(io::Error::last_os_error()))
} else {
Ok(*self)
}
}
pub fn detach(&mut self) -> Result<()> {
let mut opts = self.opts;
opts.prog_id = 0;
opts.prog_fd = 0;
opts.flags = 0;
let err = unsafe { libbpf_sys::bpf_tc_detach(&self.hook as *const _, &opts as *const _) };
if err != 0 {
Err(Error::from_raw_os_error(-err))
} else {
self.opts.prog_id = 0;
Ok(())
}
}
pub fn destroy(&mut self) -> Result<()> {
let err = unsafe { libbpf_sys::bpf_tc_hook_destroy(&mut self.hook as *mut _) };
if err != 0 {
Err(Error::from_raw_os_error(-err))
} else {
Ok(())
}
}
}
#[derive(Debug)]
pub struct TcHookBuilder<'fd> {
fd: BorrowedFd<'fd>,
ifindex: i32,
parent_maj: u32,
parent_min: u32,
replace: bool,
handle: u32,
priority: u32,
}
impl<'fd> TcHookBuilder<'fd> {
pub fn new(fd: BorrowedFd<'fd>) -> Self {
TcHookBuilder {
fd,
ifindex: 0,
parent_maj: 0,
parent_min: 0,
replace: false,
handle: 0,
priority: 0,
}
}
pub fn ifindex(&mut self, ifindex: i32) -> &mut Self {
self.ifindex = ifindex;
self
}
pub fn parent(&mut self, maj: u32, min: u32) -> &mut Self {
self.parent_maj = maj;
self.parent_min = min;
self
}
pub fn replace(&mut self, replace: bool) -> &mut Self {
self.replace = replace;
self
}
pub fn handle(&mut self, handle: u32) -> &mut Self {
self.handle = handle;
self
}
pub fn priority(&mut self, priority: u32) -> &mut Self {
self.priority = priority;
self
}
pub fn hook(&self, attach_point: TcAttachPoint) -> TcHook {
let mut hook = TcHook::new(self.fd);
hook.ifindex(self.ifindex)
.handle(self.handle)
.priority(self.priority)
.parent(self.parent_maj, self.parent_min)
.replace(self.replace)
.attach_point(attach_point);
hook
}
}