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