use std::{
io,
os::fd::{AsFd, AsRawFd as _},
path::Path,
ptr,
};
use aya_obj::generated::{
SO_ATTACH_BPF, SO_DETACH_BPF, bpf_prog_type::BPF_PROG_TYPE_SOCKET_FILTER,
};
use libc::{SOL_SOCKET, setsockopt};
use thiserror::Error;
use crate::{
VerifierLogLevel,
programs::{
ProgramData, ProgramError, ProgramType, links::FdLink, load_program_without_attach_type,
},
};
macro_rules! setsockopt_socket_filter {
($socket:expr, $option:ident, $value:expr) => {{
let value = $value;
let ret = unsafe {
setsockopt(
$socket,
SOL_SOCKET,
$option as libc::c_int,
ptr::from_ref(value).cast(),
size_of_val(value) as libc::socklen_t,
)
};
if ret < 0 {
Err(SocketFilterError::SetsockoptError {
option: stringify!($option),
io_error: io::Error::last_os_error(),
})
} else {
Ok(())
}
}};
}
#[derive(Debug, Error)]
pub enum SocketFilterError {
#[error("setsockopt {option} failed")]
SetsockoptError {
option: &'static str,
#[source]
io_error: io::Error,
},
}
#[derive(Debug)]
#[doc(alias = "BPF_PROG_TYPE_SOCKET_FILTER")]
pub struct SocketFilter {
pub(crate) data: ProgramData<FdLink>,
}
impl SocketFilter {
pub const PROGRAM_TYPE: ProgramType = ProgramType::SocketFilter;
pub fn load(&mut self) -> Result<(), ProgramError> {
let Self { data } = self;
load_program_without_attach_type(BPF_PROG_TYPE_SOCKET_FILTER, data)
}
pub fn attach<T: AsFd>(&self, socket: T) -> Result<(), ProgramError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd().as_raw_fd();
let socket = socket.as_fd().as_raw_fd();
setsockopt_socket_filter!(socket, SO_ATTACH_BPF, &prog_fd)?;
Ok(())
}
pub fn detach<T: AsFd>(socket: T) -> Result<(), ProgramError> {
let socket = socket.as_fd().as_raw_fd();
let dummy: libc::c_int = 0;
setsockopt_socket_filter!(socket, SO_DETACH_BPF, &dummy)?;
Ok(())
}
pub fn from_pin<P: AsRef<Path>>(path: P) -> Result<Self, ProgramError> {
let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?;
Ok(Self { data })
}
}