feat(aya): Return error messages from netlink

This returns error strings from netlink since they are more informative
than the raw os error. For example:

"Device or Resource Busy" vs. "XDP program already attached".

Signed-off-by: Dave Tucker <dave@dtucker.co.uk>
pull/690/head
Dave Tucker 2 years ago
parent 0865e08dcf
commit 39cf6c12f2

@ -123,7 +123,8 @@ use crate::{
sys::{ sys::{
bpf_btf_get_fd_by_id, bpf_get_object, bpf_link_get_fd_by_id, bpf_link_get_info_by_fd, bpf_btf_get_fd_by_id, bpf_get_object, bpf_link_get_fd_by_id, bpf_link_get_info_by_fd,
bpf_load_program, bpf_pin_object, bpf_prog_get_fd_by_id, bpf_prog_query, iter_link_ids, bpf_load_program, bpf_pin_object, bpf_prog_get_fd_by_id, bpf_prog_query, iter_link_ids,
retry_with_verifier_logs, EbpfLoadProgramAttrs, ProgQueryTarget, SyscallError, retry_with_verifier_logs, EbpfLoadProgramAttrs, NetlinkError, ProgQueryTarget,
SyscallError,
}, },
util::KernelVersion, util::KernelVersion,
VerifierLogLevel, VerifierLogLevel,
@ -223,6 +224,10 @@ pub enum ProgramError {
/// Providing an attach cookie is not supported. /// Providing an attach cookie is not supported.
#[error("providing an attach cookie is not supported")] #[error("providing an attach cookie is not supported")]
AttachCookieNotSupported, AttachCookieNotSupported,
/// An error occurred while working with Netlink.
#[error(transparent)]
NetlinkError(#[from] NetlinkError),
} }
/// A [`Program`] file descriptor. /// A [`Program`] file descriptor.

@ -23,7 +23,8 @@ use crate::{
sys::{ sys::{
bpf_link_create, bpf_link_get_info_by_fd, bpf_link_update, bpf_prog_get_fd_by_id, bpf_link_create, bpf_link_get_info_by_fd, bpf_link_update, bpf_prog_get_fd_by_id,
netlink_find_filter_with_name, netlink_qdisc_add_clsact, netlink_qdisc_attach, netlink_find_filter_with_name, netlink_qdisc_add_clsact, netlink_qdisc_attach,
netlink_qdisc_detach, BpfLinkCreateArgs, LinkTarget, ProgQueryTarget, SyscallError, netlink_qdisc_detach, BpfLinkCreateArgs, LinkTarget, NetlinkError, ProgQueryTarget,
SyscallError,
}, },
util::{ifindex_from_ifname, tc_handler_make, KernelVersion}, util::{ifindex_from_ifname, tc_handler_make, KernelVersion},
VerifierLogLevel, VerifierLogLevel,
@ -63,6 +64,8 @@ pub enum TcAttachType {
/// # #[error(transparent)] /// # #[error(transparent)]
/// # Program(#[from] aya::programs::ProgramError), /// # Program(#[from] aya::programs::ProgramError),
/// # #[error(transparent)] /// # #[error(transparent)]
/// # Tc(#[from] aya::programs::tc::TcError),
/// # #[error(transparent)]
/// # Ebpf(#[from] aya::EbpfError) /// # Ebpf(#[from] aya::EbpfError)
/// # } /// # }
/// # let mut bpf = aya::Ebpf::load(&[])?; /// # let mut bpf = aya::Ebpf::load(&[])?;
@ -87,20 +90,22 @@ pub struct SchedClassifier {
/// Errors from TC programs /// Errors from TC programs
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum TcError { pub enum TcError {
/// netlink error while attaching ebpf program /// a netlink error occurred.
#[error("netlink error while attaching ebpf program to tc")] #[error(transparent)]
NetlinkError { NetlinkError(#[from] NetlinkError),
/// the [`io::Error`] from the netlink call /// the provided string contains a nul byte.
#[source] #[error(transparent)]
io_error: io::Error, NulError(#[from] std::ffi::NulError),
}, /// an IO error occurred.
/// the clsact qdisc is already attached #[error(transparent)]
IoError(#[from] io::Error),
/// the clsact qdisc is already attached.
#[error("the clsact qdisc is already attached")] #[error("the clsact qdisc is already attached")]
AlreadyAttached, AlreadyAttached,
/// tcx links can only be attached to ingress or egress, custom attachment is not supported /// tcx links can only be attached to ingress or egress, custom attachment is not supported.
#[error("tcx links can only be attached to ingress or egress, custom attachment: {0} is not supported")] #[error("tcx links can only be attached to ingress or egress, custom attachment: {0} is not supported")]
InvalidTcxAttach(u32), InvalidTcxAttach(u32),
/// operation not supported for programs loaded via tcx /// operation not supported for programs loaded via tcx.
#[error("operation not supported for programs loaded via tcx")] #[error("operation not supported for programs loaded via tcx")]
InvalidLinkOperation, InvalidLinkOperation,
} }
@ -209,8 +214,7 @@ impl SchedClassifier {
attach_type: TcAttachType, attach_type: TcAttachType,
options: TcAttachOptions, options: TcAttachOptions,
) -> Result<SchedClassifierLinkId, ProgramError> { ) -> Result<SchedClassifierLinkId, ProgramError> {
let if_index = ifindex_from_ifname(interface) let if_index = ifindex_from_ifname(interface).map_err(TcError::IoError)?;
.map_err(|io_error| TcError::NetlinkError { io_error })?;
self.do_attach(if_index, attach_type, options, true) self.do_attach(if_index, attach_type, options, true)
} }
@ -281,7 +285,7 @@ impl SchedClassifier {
create, create,
) )
} }
.map_err(|io_error| TcError::NetlinkError { io_error })?; .map_err(TcError::NetlinkError)?;
self.data self.data
.links .links
@ -343,8 +347,7 @@ impl SchedClassifier {
interface: &str, interface: &str,
attach_type: TcAttachType, attach_type: TcAttachType,
) -> Result<(u64, Vec<ProgramInfo>), ProgramError> { ) -> Result<(u64, Vec<ProgramInfo>), ProgramError> {
let if_index = ifindex_from_ifname(interface) let if_index = ifindex_from_ifname(interface).map_err(TcError::IoError)?;
.map_err(|io_error| TcError::NetlinkError { io_error })?;
let (revision, prog_ids) = query( let (revision, prog_ids) = query(
ProgQueryTarget::IfIndex(if_index), ProgQueryTarget::IfIndex(if_index),
@ -393,7 +396,7 @@ impl Link for NlLink {
self.handle, self.handle,
) )
} }
.map_err(|io_error| TcError::NetlinkError { io_error })?; .map_err(ProgramError::NetlinkError)?;
Ok(()) Ok(())
} }
} }
@ -557,9 +560,9 @@ impl SchedClassifierLink {
/// ///
/// The `clsact` qdisc must be added to an interface before [`SchedClassifier`] /// The `clsact` qdisc must be added to an interface before [`SchedClassifier`]
/// programs can be attached. /// programs can be attached.
pub fn qdisc_add_clsact(if_name: &str) -> Result<(), io::Error> { pub fn qdisc_add_clsact(if_name: &str) -> Result<(), TcError> {
let if_index = ifindex_from_ifname(if_name)?; let if_index = ifindex_from_ifname(if_name)?;
unsafe { netlink_qdisc_add_clsact(if_index as i32) } unsafe { netlink_qdisc_add_clsact(if_index as i32).map_err(TcError::NetlinkError) }
} }
/// Detaches the programs with the given name. /// Detaches the programs with the given name.
@ -573,8 +576,8 @@ pub fn qdisc_detach_program(
if_name: &str, if_name: &str,
attach_type: TcAttachType, attach_type: TcAttachType,
name: &str, name: &str,
) -> Result<(), io::Error> { ) -> Result<(), TcError> {
let cstr = CString::new(name)?; let cstr = CString::new(name).map_err(TcError::NulError)?;
qdisc_detach_program_fast(if_name, attach_type, &cstr) qdisc_detach_program_fast(if_name, attach_type, &cstr)
} }
@ -591,15 +594,15 @@ fn qdisc_detach_program_fast(
if_name: &str, if_name: &str,
attach_type: TcAttachType, attach_type: TcAttachType,
name: &CStr, name: &CStr,
) -> Result<(), io::Error> { ) -> Result<(), TcError> {
let if_index = ifindex_from_ifname(if_name)? as i32; let if_index = ifindex_from_ifname(if_name)? as i32;
let filter_info = unsafe { netlink_find_filter_with_name(if_index, attach_type, name)? }; let filter_info = unsafe { netlink_find_filter_with_name(if_index, attach_type, name)? };
if filter_info.is_empty() { if filter_info.is_empty() {
return Err(io::Error::new( return Err(TcError::IoError(io::Error::new(
io::ErrorKind::NotFound, io::ErrorKind::NotFound,
name.to_string_lossy(), name.to_string_lossy(),
)); )));
} }
for (prio, handle) in filter_info { for (prio, handle) in filter_info {

@ -3,7 +3,6 @@
use std::{ use std::{
ffi::CString, ffi::CString,
hash::Hash, hash::Hash,
io,
os::fd::{AsFd as _, AsRawFd as _, BorrowedFd, RawFd}, os::fd::{AsFd as _, AsRawFd as _, BorrowedFd, RawFd},
path::Path, path::Path,
}; };
@ -23,22 +22,18 @@ use crate::{
}, },
sys::{ sys::{
bpf_link_create, bpf_link_get_info_by_fd, bpf_link_update, netlink_set_xdp_fd, LinkTarget, bpf_link_create, bpf_link_get_info_by_fd, bpf_link_update, netlink_set_xdp_fd, LinkTarget,
SyscallError, NetlinkError, SyscallError,
}, },
util::KernelVersion, util::KernelVersion,
VerifierLogLevel, VerifierLogLevel,
}; };
/// The type returned when attaching an [`Xdp`] program fails on kernels `< 5.9`. /// An error that occurred while working with an XDP program.
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum XdpError { pub enum XdpError {
/// netlink error while attaching XDP program /// A netlink error occurred.
#[error("netlink error while attaching XDP program")] #[error(transparent)]
NetlinkError { NetlinkError(#[from] NetlinkError),
/// the [`io::Error`] from the netlink call
#[source]
io_error: io::Error,
},
} }
bitflags::bitflags! { bitflags::bitflags! {
@ -162,7 +157,7 @@ impl Xdp {
} else { } else {
let if_index = if_index as i32; let if_index = if_index as i32;
unsafe { netlink_set_xdp_fd(if_index, Some(prog_fd), None, flags.bits()) } unsafe { netlink_set_xdp_fd(if_index, Some(prog_fd), None, flags.bits()) }
.map_err(|io_error| XdpError::NetlinkError { io_error })?; .map_err(XdpError::NetlinkError)?;
let prog_fd = prog_fd.as_raw_fd(); let prog_fd = prog_fd.as_raw_fd();
self.data self.data
@ -224,7 +219,7 @@ impl Xdp {
Some(old_prog_fd), Some(old_prog_fd),
replace_flags.bits(), replace_flags.bits(),
) )
.map_err(|io_error| XdpError::NetlinkError { io_error })?; .map_err(XdpError::NetlinkError)?;
} }
let prog_fd = prog_fd.as_raw_fd(); let prog_fd = prog_fd.as_raw_fd();

@ -8,18 +8,19 @@ use std::{
use libc::{ use libc::{
getsockname, nlattr, nlmsgerr, nlmsghdr, recv, send, setsockopt, sockaddr_nl, socket, getsockname, nlattr, nlmsgerr, nlmsghdr, recv, send, setsockopt, sockaddr_nl, socket,
AF_NETLINK, AF_UNSPEC, ETH_P_ALL, IFF_UP, IFLA_XDP, NETLINK_EXT_ACK, NETLINK_ROUTE, AF_NETLINK, AF_UNSPEC, ETH_P_ALL, IFF_UP, IFLA_XDP, NETLINK_CAP_ACK, NETLINK_EXT_ACK,
NLA_ALIGNTO, NLA_F_NESTED, NLA_TYPE_MASK, NLMSG_DONE, NLMSG_ERROR, NLM_F_ACK, NLM_F_CREATE, NETLINK_ROUTE, NLA_ALIGNTO, NLA_F_NESTED, NLA_TYPE_MASK, NLMSG_DONE, NLMSG_ERROR, NLM_F_ACK,
NLM_F_DUMP, NLM_F_ECHO, NLM_F_EXCL, NLM_F_MULTI, NLM_F_REQUEST, RTM_DELTFILTER, RTM_GETTFILTER, NLM_F_CREATE, NLM_F_DUMP, NLM_F_ECHO, NLM_F_EXCL, NLM_F_MULTI, NLM_F_REQUEST, RTM_DELTFILTER,
RTM_NEWQDISC, RTM_NEWTFILTER, RTM_SETLINK, SOCK_RAW, SOL_NETLINK, RTM_GETTFILTER, RTM_NEWQDISC, RTM_NEWTFILTER, RTM_SETLINK, SOCK_RAW, SOL_NETLINK,
}; };
use thiserror::Error; use thiserror::Error;
use crate::{ use crate::{
generated::{ generated::{
ifinfomsg, tcmsg, IFLA_XDP_EXPECTED_FD, IFLA_XDP_FD, IFLA_XDP_FLAGS, NLMSG_ALIGNTO, ifinfomsg, nlmsgerr_attrs::NLMSGERR_ATTR_MSG, tcmsg, IFLA_XDP_EXPECTED_FD, IFLA_XDP_FD,
TCA_BPF_FD, TCA_BPF_FLAGS, TCA_BPF_FLAG_ACT_DIRECT, TCA_BPF_NAME, TCA_KIND, TCA_OPTIONS, IFLA_XDP_FLAGS, NLMSG_ALIGNTO, TCA_BPF_FD, TCA_BPF_FLAGS, TCA_BPF_FLAG_ACT_DIRECT,
TC_H_CLSACT, TC_H_INGRESS, TC_H_MAJ_MASK, TC_H_UNSPEC, XDP_FLAGS_REPLACE, TCA_BPF_NAME, TCA_KIND, TCA_OPTIONS, TC_H_CLSACT, TC_H_INGRESS, TC_H_MAJ_MASK, TC_H_UNSPEC,
XDP_FLAGS_REPLACE,
}, },
programs::TcAttachType, programs::TcAttachType,
util::tc_handler_make, util::tc_handler_make,
@ -27,6 +28,28 @@ use crate::{
const NLA_HDR_LEN: usize = align_to(mem::size_of::<nlattr>(), NLA_ALIGNTO as usize); const NLA_HDR_LEN: usize = align_to(mem::size_of::<nlattr>(), NLA_ALIGNTO as usize);
/// A private error type for internal use in this module.
#[derive(Error, Debug)]
pub(crate) enum NetlinkErrorInternal {
#[error("netlink error: {message}")]
Error {
message: String,
#[source]
source: io::Error,
},
#[error(transparent)]
IoError(#[from] io::Error),
#[error(transparent)]
NulError(#[from] std::ffi::NulError),
#[error(transparent)]
NlAttrError(#[from] NlAttrError),
}
/// An error occurred during a netlink operation.
#[derive(Error, Debug)]
#[error(transparent)]
pub struct NetlinkError(#[from] NetlinkErrorInternal);
// Safety: marking this as unsafe overall because of all the pointer math required to comply with // Safety: marking this as unsafe overall because of all the pointer math required to comply with
// netlink alignments // netlink alignments
pub(crate) unsafe fn netlink_set_xdp_fd( pub(crate) unsafe fn netlink_set_xdp_fd(
@ -34,7 +57,7 @@ pub(crate) unsafe fn netlink_set_xdp_fd(
fd: Option<BorrowedFd<'_>>, fd: Option<BorrowedFd<'_>>,
old_fd: Option<BorrowedFd<'_>>, old_fd: Option<BorrowedFd<'_>>,
flags: u32, flags: u32,
) -> Result<(), io::Error> { ) -> Result<(), NetlinkError> {
let sock = NetlinkSocket::open()?; let sock = NetlinkSocket::open()?;
// Safety: Request is POD so this is safe // Safety: Request is POD so this is safe
@ -54,33 +77,39 @@ pub(crate) unsafe fn netlink_set_xdp_fd(
// write the attrs // write the attrs
let attrs_buf = request_attributes(&mut req, nlmsg_len); let attrs_buf = request_attributes(&mut req, nlmsg_len);
let mut attrs = NestedAttrs::new(attrs_buf, IFLA_XDP); let mut attrs = NestedAttrs::new(attrs_buf, IFLA_XDP);
attrs.write_attr( attrs
IFLA_XDP_FD as u16, .write_attr(
fd.map(|fd| fd.as_raw_fd()).unwrap_or(-1), IFLA_XDP_FD as u16,
)?; fd.map(|fd| fd.as_raw_fd()).unwrap_or(-1),
)
.map_err(|e| NetlinkError(NetlinkErrorInternal::IoError(e)))?;
if flags > 0 { if flags > 0 {
attrs.write_attr(IFLA_XDP_FLAGS as u16, flags)?; attrs
.write_attr(IFLA_XDP_FLAGS as u16, flags)
.map_err(|e| NetlinkError(NetlinkErrorInternal::IoError(e)))?;
} }
if flags & XDP_FLAGS_REPLACE != 0 { if flags & XDP_FLAGS_REPLACE != 0 {
attrs.write_attr( attrs
IFLA_XDP_EXPECTED_FD as u16, .write_attr(
old_fd.map(|fd| fd.as_raw_fd()).unwrap(), IFLA_XDP_EXPECTED_FD as u16,
)?; old_fd.map(|fd| fd.as_raw_fd()).unwrap(),
)
.map_err(|e| NetlinkError(NetlinkErrorInternal::IoError(e)))?;
} }
let nla_len = attrs.finish()?; let nla_len = attrs
.finish()
.map_err(|e| NetlinkError(NetlinkErrorInternal::IoError(e)))?;
req.header.nlmsg_len += align_to(nla_len, NLA_ALIGNTO as usize) as u32; req.header.nlmsg_len += align_to(nla_len, NLA_ALIGNTO as usize) as u32;
sock.send(&bytes_of(&req)[..req.header.nlmsg_len as usize])?; sock.send(&bytes_of(&req)[..req.header.nlmsg_len as usize])?;
sock.recv()?; sock.recv()?;
Ok(()) Ok(())
} }
pub(crate) unsafe fn netlink_qdisc_add_clsact(if_index: i32) -> Result<(), io::Error> { pub(crate) unsafe fn netlink_qdisc_add_clsact(if_index: i32) -> Result<(), NetlinkError> {
let sock = NetlinkSocket::open()?; let sock = NetlinkSocket::open()?;
let mut req = mem::zeroed::<TcRequest>(); let mut req = mem::zeroed::<TcRequest>();
@ -101,7 +130,8 @@ pub(crate) unsafe fn netlink_qdisc_add_clsact(if_index: i32) -> Result<(), io::E
// add the TCA_KIND attribute // add the TCA_KIND attribute
let attrs_buf = request_attributes(&mut req, nlmsg_len); let attrs_buf = request_attributes(&mut req, nlmsg_len);
let attr_len = write_attr_bytes(attrs_buf, 0, TCA_KIND as u16, b"clsact\0")?; let attr_len = write_attr_bytes(attrs_buf, 0, TCA_KIND as u16, b"clsact\0")
.map_err(|e| NetlinkError(NetlinkErrorInternal::IoError(e)))?;
req.header.nlmsg_len += align_to(attr_len, NLA_ALIGNTO as usize) as u32; req.header.nlmsg_len += align_to(attr_len, NLA_ALIGNTO as usize) as u32;
sock.send(&bytes_of(&req)[..req.header.nlmsg_len as usize])?; sock.send(&bytes_of(&req)[..req.header.nlmsg_len as usize])?;
@ -118,7 +148,7 @@ pub(crate) unsafe fn netlink_qdisc_attach(
priority: u16, priority: u16,
handle: u32, handle: u32,
create: bool, create: bool,
) -> Result<(u16, u32), io::Error> { ) -> Result<(u16, u32), NetlinkError> {
let sock = NetlinkSocket::open()?; let sock = NetlinkSocket::open()?;
let mut req = mem::zeroed::<TcRequest>(); let mut req = mem::zeroed::<TcRequest>();
@ -152,15 +182,24 @@ pub(crate) unsafe fn netlink_qdisc_attach(
let attrs_buf = request_attributes(&mut req, nlmsg_len); let attrs_buf = request_attributes(&mut req, nlmsg_len);
// add TCA_KIND // add TCA_KIND
let kind_len = write_attr_bytes(attrs_buf, 0, TCA_KIND as u16, b"bpf\0")?; let kind_len = write_attr_bytes(attrs_buf, 0, TCA_KIND as u16, b"bpf\0")
.map_err(|e| NetlinkError(NetlinkErrorInternal::IoError(e)))?;
// add TCA_OPTIONS which includes TCA_BPF_FD, TCA_BPF_NAME and TCA_BPF_FLAGS // add TCA_OPTIONS which includes TCA_BPF_FD, TCA_BPF_NAME and TCA_BPF_FLAGS
let mut options = NestedAttrs::new(&mut attrs_buf[kind_len..], TCA_OPTIONS as u16); let mut options = NestedAttrs::new(&mut attrs_buf[kind_len..], TCA_OPTIONS as u16);
options.write_attr(TCA_BPF_FD as u16, prog_fd)?; options
options.write_attr_bytes(TCA_BPF_NAME as u16, prog_name.to_bytes_with_nul())?; .write_attr(TCA_BPF_FD as u16, prog_fd)
.map_err(|e| NetlinkError(NetlinkErrorInternal::IoError(e)))?;
options
.write_attr_bytes(TCA_BPF_NAME as u16, prog_name.to_bytes_with_nul())
.map_err(|e| NetlinkError(NetlinkErrorInternal::IoError(e)))?;
let flags: u32 = TCA_BPF_FLAG_ACT_DIRECT; let flags: u32 = TCA_BPF_FLAG_ACT_DIRECT;
options.write_attr(TCA_BPF_FLAGS as u16, flags)?; options
let options_len = options.finish()?; .write_attr(TCA_BPF_FLAGS as u16, flags)
.map_err(|e| NetlinkError(NetlinkErrorInternal::IoError(e)))?;
let options_len = options
.finish()
.map_err(|e| NetlinkError(NetlinkErrorInternal::IoError(e)))?;
req.header.nlmsg_len += align_to(kind_len + options_len, NLA_ALIGNTO as usize) as u32; req.header.nlmsg_len += align_to(kind_len + options_len, NLA_ALIGNTO as usize) as u32;
sock.send(&bytes_of(&req)[..req.header.nlmsg_len as usize])?; sock.send(&bytes_of(&req)[..req.header.nlmsg_len as usize])?;
@ -176,10 +215,10 @@ pub(crate) unsafe fn netlink_qdisc_attach(
None => { None => {
// if sock.recv() succeeds we should never get here unless there's a // if sock.recv() succeeds we should never get here unless there's a
// bug in the kernel // bug in the kernel
return Err(io::Error::new( return Err(NetlinkError(NetlinkErrorInternal::IoError(io::Error::new(
io::ErrorKind::Other, io::ErrorKind::Other,
"no RTM_NEWTFILTER reply received, this is a bug.", "no RTM_NEWTFILTER reply received, this is a bug.",
)); ))));
} }
}; };
@ -192,7 +231,7 @@ pub(crate) unsafe fn netlink_qdisc_detach(
attach_type: &TcAttachType, attach_type: &TcAttachType,
priority: u16, priority: u16,
handle: u32, handle: u32,
) -> Result<(), io::Error> { ) -> Result<(), NetlinkError> {
let sock = NetlinkSocket::open()?; let sock = NetlinkSocket::open()?;
let mut req = mem::zeroed::<TcRequest>(); let mut req = mem::zeroed::<TcRequest>();
@ -222,7 +261,7 @@ pub(crate) unsafe fn netlink_find_filter_with_name(
if_index: i32, if_index: i32,
attach_type: TcAttachType, attach_type: TcAttachType,
name: &CStr, name: &CStr,
) -> Result<Vec<(u16, u32)>, io::Error> { ) -> Result<Vec<(u16, u32)>, NetlinkError> {
let mut req = mem::zeroed::<TcRequest>(); let mut req = mem::zeroed::<TcRequest>();
let nlmsg_len = mem::size_of::<nlmsghdr>() + mem::size_of::<tcmsg>(); let nlmsg_len = mem::size_of::<nlmsghdr>() + mem::size_of::<tcmsg>();
@ -249,10 +288,12 @@ pub(crate) unsafe fn netlink_find_filter_with_name(
let tc_msg = ptr::read_unaligned(msg.data.as_ptr() as *const tcmsg); let tc_msg = ptr::read_unaligned(msg.data.as_ptr() as *const tcmsg);
let priority = (tc_msg.tcm_info >> 16) as u16; let priority = (tc_msg.tcm_info >> 16) as u16;
let attrs = parse_attrs(&msg.data[mem::size_of::<tcmsg>()..])?; let attrs = parse_attrs(&msg.data[mem::size_of::<tcmsg>()..])
.map_err(|e| NetlinkError(NetlinkErrorInternal::NlAttrError(e)))?;
if let Some(opts) = attrs.get(&(TCA_OPTIONS as u16)) { if let Some(opts) = attrs.get(&(TCA_OPTIONS as u16)) {
let opts = parse_attrs(opts.data)?; let opts = parse_attrs(opts.data)
.map_err(|e| NetlinkError(NetlinkErrorInternal::NlAttrError(e)))?;
if let Some(f_name) = opts.get(&(TCA_BPF_NAME as u16)) { if let Some(f_name) = opts.get(&(TCA_BPF_NAME as u16)) {
if let Ok(f_name) = CStr::from_bytes_with_nul(f_name.data) { if let Ok(f_name) = CStr::from_bytes_with_nul(f_name.data) {
if name == f_name { if name == f_name {
@ -267,7 +308,7 @@ pub(crate) unsafe fn netlink_find_filter_with_name(
} }
#[doc(hidden)] #[doc(hidden)]
pub unsafe fn netlink_set_link_up(if_index: i32) -> Result<(), io::Error> { pub unsafe fn netlink_set_link_up(if_index: i32) -> Result<(), NetlinkError> {
let sock = NetlinkSocket::open()?; let sock = NetlinkSocket::open()?;
// Safety: Request is POD so this is safe // Safety: Request is POD so this is safe
@ -312,11 +353,11 @@ struct NetlinkSocket {
} }
impl NetlinkSocket { impl NetlinkSocket {
fn open() -> Result<Self, io::Error> { fn open() -> Result<Self, NetlinkErrorInternal> {
// Safety: libc wrapper // Safety: libc wrapper
let sock = unsafe { socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) }; let sock = unsafe { socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) };
if sock < 0 { if sock < 0 {
return Err(io::Error::last_os_error()); return Err(NetlinkErrorInternal::IoError(io::Error::last_os_error()));
} }
// SAFETY: `socket` returns a file descriptor. // SAFETY: `socket` returns a file descriptor.
let sock = unsafe { crate::MockableFd::from_raw_fd(sock) }; let sock = unsafe { crate::MockableFd::from_raw_fd(sock) };
@ -324,13 +365,29 @@ impl NetlinkSocket {
let enable = 1i32; let enable = 1i32;
// Safety: libc wrapper // Safety: libc wrapper
unsafe { unsafe {
setsockopt( // Set NETLINK_EXT_ACK to get extended attributes.
if setsockopt(
sock.as_raw_fd(), sock.as_raw_fd(),
SOL_NETLINK, SOL_NETLINK,
NETLINK_EXT_ACK, NETLINK_EXT_ACK,
&enable as *const _ as *const _, &enable as *const _ as *const _,
mem::size_of::<i32>() as u32, mem::size_of::<i32>() as u32,
) ) < 0
{
return Err(NetlinkErrorInternal::IoError(io::Error::last_os_error()));
};
// Set NETLINK_CAP_ACK to avoid getting copies of request payload.
if setsockopt(
sock.as_raw_fd(),
SOL_NETLINK,
NETLINK_CAP_ACK,
&enable as *const _ as *const _,
mem::size_of::<i32>() as u32,
) < 0
{
return Err(NetlinkErrorInternal::IoError(io::Error::last_os_error()));
};
}; };
// Safety: sockaddr_nl is POD so this is safe // Safety: sockaddr_nl is POD so this is safe
@ -346,7 +403,7 @@ impl NetlinkSocket {
) )
} < 0 } < 0
{ {
return Err(io::Error::last_os_error()); return Err(NetlinkErrorInternal::IoError(io::Error::last_os_error()));
} }
Ok(Self { Ok(Self {
@ -355,7 +412,7 @@ impl NetlinkSocket {
}) })
} }
fn send(&self, msg: &[u8]) -> Result<(), io::Error> { fn send(&self, msg: &[u8]) -> Result<(), NetlinkErrorInternal> {
if unsafe { if unsafe {
send( send(
self.sock.as_raw_fd(), self.sock.as_raw_fd(),
@ -365,12 +422,12 @@ impl NetlinkSocket {
) )
} < 0 } < 0
{ {
return Err(io::Error::last_os_error()); return Err(NetlinkErrorInternal::IoError(io::Error::last_os_error()));
} }
Ok(()) Ok(())
} }
fn recv(&self) -> Result<Vec<NetlinkMessage>, io::Error> { fn recv(&self) -> Result<Vec<NetlinkMessage>, NetlinkErrorInternal> {
let mut buf = [0u8; 4096]; let mut buf = [0u8; 4096];
let mut messages = Vec::new(); let mut messages = Vec::new();
let mut multipart = true; let mut multipart = true;
@ -386,7 +443,7 @@ impl NetlinkSocket {
) )
}; };
if len < 0 { if len < 0 {
return Err(io::Error::last_os_error()); return Err(NetlinkErrorInternal::IoError(io::Error::last_os_error()));
} }
if len == 0 { if len == 0 {
break; break;
@ -405,7 +462,22 @@ impl NetlinkSocket {
// this is an ACK // this is an ACK
continue; continue;
} }
return Err(io::Error::from_raw_os_error(-err.error)); let attrs = parse_attrs(&message.data)?;
let err_msg = attrs.get(&(NLMSGERR_ATTR_MSG as u16)).and_then(|msg| {
CStr::from_bytes_with_nul(msg.data)
.ok()
.map(|s| s.to_string_lossy().into_owned())
});
let e = match err_msg {
Some(err_msg) => NetlinkErrorInternal::Error {
message: err_msg,
source: io::Error::from_raw_os_error(-err.error),
},
None => NetlinkErrorInternal::IoError(io::Error::from_raw_os_error(
-err.error,
)),
};
return Err(e);
} }
NLMSG_DONE => break 'out, NLMSG_DONE => break 'out,
_ => messages.push(message), _ => messages.push(message),
@ -444,7 +516,7 @@ impl NetlinkMessage {
return Err(io::Error::new(io::ErrorKind::Other, "need more data")); return Err(io::Error::new(io::ErrorKind::Other, "need more data"));
} }
let (data, error) = if header.nlmsg_type == NLMSG_ERROR as u16 { let (rest, error) = if header.nlmsg_type == NLMSG_ERROR as u16 {
if data_offset + mem::size_of::<nlmsgerr>() > buf.len() { if data_offset + mem::size_of::<nlmsgerr>() > buf.len() {
return Err(io::Error::new( return Err(io::Error::new(
io::ErrorKind::Other, io::ErrorKind::Other,
@ -452,19 +524,19 @@ impl NetlinkMessage {
)); ));
} }
( (
Vec::new(), &buf[data_offset + mem::size_of::<nlmsgerr>()..msg_len],
// Safety: nlmsgerr is POD so read is safe // Safety: nlmsgerr is POD so read is safe
Some(unsafe { Some(unsafe {
ptr::read_unaligned(buf[data_offset..].as_ptr() as *const nlmsgerr) ptr::read_unaligned(buf[data_offset..].as_ptr() as *const nlmsgerr)
}), }),
) )
} else { } else {
(buf[data_offset..msg_len].to_vec(), None) (&buf[data_offset..msg_len], None)
}; };
Ok(Self { Ok(Self {
header, header,
data, data: rest.to_vec(),
error, error,
}) })
} }
@ -628,7 +700,7 @@ struct NlAttr<'a> {
} }
#[derive(Debug, Error, PartialEq, Eq)] #[derive(Debug, Error, PartialEq, Eq)]
enum NlAttrError { pub(crate) enum NlAttrError {
#[error("invalid buffer size `{size}`, expected `{expected}`")] #[error("invalid buffer size `{size}`, expected `{expected}`")]
InvalidBufferLength { size: usize, expected: usize }, InvalidBufferLength { size: usize, expected: usize },

@ -6060,10 +6060,15 @@ pub enum aya::programs::tc::TcError
pub aya::programs::tc::TcError::AlreadyAttached pub aya::programs::tc::TcError::AlreadyAttached
pub aya::programs::tc::TcError::InvalidLinkOperation pub aya::programs::tc::TcError::InvalidLinkOperation
pub aya::programs::tc::TcError::InvalidTcxAttach(u32) pub aya::programs::tc::TcError::InvalidTcxAttach(u32)
pub aya::programs::tc::TcError::NetlinkError pub aya::programs::tc::TcError::IoError(std::io::error::Error)
pub aya::programs::tc::TcError::NetlinkError::io_error: std::io::error::Error pub aya::programs::tc::TcError::NetlinkError(aya::sys::netlink::NetlinkError)
pub aya::programs::tc::TcError::NulError(alloc::ffi::c_str::NulError)
impl core::convert::From<alloc::ffi::c_str::NulError> for aya::programs::tc::TcError
pub fn aya::programs::tc::TcError::from(source: alloc::ffi::c_str::NulError) -> Self
impl core::convert::From<aya::programs::tc::TcError> for aya::programs::ProgramError impl core::convert::From<aya::programs::tc::TcError> for aya::programs::ProgramError
pub fn aya::programs::ProgramError::from(source: aya::programs::tc::TcError) -> Self pub fn aya::programs::ProgramError::from(source: aya::programs::tc::TcError) -> Self
impl core::convert::From<std::io::error::Error> for aya::programs::tc::TcError
pub fn aya::programs::tc::TcError::from(source: std::io::error::Error) -> Self
impl core::error::Error for aya::programs::tc::TcError impl core::error::Error for aya::programs::tc::TcError
pub fn aya::programs::tc::TcError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> pub fn aya::programs::tc::TcError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)>
impl core::fmt::Debug for aya::programs::tc::TcError impl core::fmt::Debug for aya::programs::tc::TcError
@ -6276,8 +6281,8 @@ impl<T> core::borrow::BorrowMut<T> for aya::programs::tc::SchedClassifierLinkId
pub fn aya::programs::tc::SchedClassifierLinkId::borrow_mut(&mut self) -> &mut T pub fn aya::programs::tc::SchedClassifierLinkId::borrow_mut(&mut self) -> &mut T
impl<T> core::convert::From<T> for aya::programs::tc::SchedClassifierLinkId impl<T> core::convert::From<T> for aya::programs::tc::SchedClassifierLinkId
pub fn aya::programs::tc::SchedClassifierLinkId::from(t: T) -> T pub fn aya::programs::tc::SchedClassifierLinkId::from(t: T) -> T
pub fn aya::programs::tc::qdisc_add_clsact(if_name: &str) -> core::result::Result<(), std::io::error::Error> pub fn aya::programs::tc::qdisc_add_clsact(if_name: &str) -> core::result::Result<(), aya::programs::tc::TcError>
pub fn aya::programs::tc::qdisc_detach_program(if_name: &str, attach_type: aya::programs::tc::TcAttachType, name: &str) -> core::result::Result<(), std::io::error::Error> pub fn aya::programs::tc::qdisc_detach_program(if_name: &str, attach_type: aya::programs::tc::TcAttachType, name: &str) -> core::result::Result<(), aya::programs::tc::TcError>
pub mod aya::programs::tp_btf pub mod aya::programs::tp_btf
pub struct aya::programs::tp_btf::BtfTracePoint pub struct aya::programs::tp_btf::BtfTracePoint
impl aya::programs::tp_btf::BtfTracePoint impl aya::programs::tp_btf::BtfTracePoint
@ -6783,8 +6788,7 @@ impl<T> core::convert::From<T> for aya::programs::uprobe::UProbeLinkId
pub fn aya::programs::uprobe::UProbeLinkId::from(t: T) -> T pub fn aya::programs::uprobe::UProbeLinkId::from(t: T) -> T
pub mod aya::programs::xdp pub mod aya::programs::xdp
pub enum aya::programs::xdp::XdpError pub enum aya::programs::xdp::XdpError
pub aya::programs::xdp::XdpError::NetlinkError pub aya::programs::xdp::XdpError::NetlinkError(aya::sys::netlink::NetlinkError)
pub aya::programs::xdp::XdpError::NetlinkError::io_error: std::io::error::Error
impl core::convert::From<aya::programs::xdp::XdpError> for aya::programs::ProgramError impl core::convert::From<aya::programs::xdp::XdpError> for aya::programs::ProgramError
pub fn aya::programs::ProgramError::from(source: aya::programs::xdp::XdpError) -> Self pub fn aya::programs::ProgramError::from(source: aya::programs::xdp::XdpError) -> Self
impl core::error::Error for aya::programs::xdp::XdpError impl core::error::Error for aya::programs::xdp::XdpError
@ -7548,6 +7552,7 @@ pub aya::programs::ProgramError::LoadError
pub aya::programs::ProgramError::LoadError::io_error: std::io::error::Error pub aya::programs::ProgramError::LoadError::io_error: std::io::error::Error
pub aya::programs::ProgramError::LoadError::verifier_log: aya_obj::VerifierLog pub aya::programs::ProgramError::LoadError::verifier_log: aya_obj::VerifierLog
pub aya::programs::ProgramError::MapError(aya::maps::MapError) pub aya::programs::ProgramError::MapError(aya::maps::MapError)
pub aya::programs::ProgramError::NetlinkError(aya::sys::netlink::NetlinkError)
pub aya::programs::ProgramError::NotAttached pub aya::programs::ProgramError::NotAttached
pub aya::programs::ProgramError::NotLoaded pub aya::programs::ProgramError::NotLoaded
pub aya::programs::ProgramError::SocketFilterError(aya::programs::socket_filter::SocketFilterError) pub aya::programs::ProgramError::SocketFilterError(aya::programs::socket_filter::SocketFilterError)
@ -7841,10 +7846,15 @@ pub enum aya::programs::TcError
pub aya::programs::TcError::AlreadyAttached pub aya::programs::TcError::AlreadyAttached
pub aya::programs::TcError::InvalidLinkOperation pub aya::programs::TcError::InvalidLinkOperation
pub aya::programs::TcError::InvalidTcxAttach(u32) pub aya::programs::TcError::InvalidTcxAttach(u32)
pub aya::programs::TcError::NetlinkError pub aya::programs::TcError::IoError(std::io::error::Error)
pub aya::programs::TcError::NetlinkError::io_error: std::io::error::Error pub aya::programs::TcError::NetlinkError(aya::sys::netlink::NetlinkError)
pub aya::programs::TcError::NulError(alloc::ffi::c_str::NulError)
impl core::convert::From<alloc::ffi::c_str::NulError> for aya::programs::tc::TcError
pub fn aya::programs::tc::TcError::from(source: alloc::ffi::c_str::NulError) -> Self
impl core::convert::From<aya::programs::tc::TcError> for aya::programs::ProgramError impl core::convert::From<aya::programs::tc::TcError> for aya::programs::ProgramError
pub fn aya::programs::ProgramError::from(source: aya::programs::tc::TcError) -> Self pub fn aya::programs::ProgramError::from(source: aya::programs::tc::TcError) -> Self
impl core::convert::From<std::io::error::Error> for aya::programs::tc::TcError
pub fn aya::programs::tc::TcError::from(source: std::io::error::Error) -> Self
impl core::error::Error for aya::programs::tc::TcError impl core::error::Error for aya::programs::tc::TcError
pub fn aya::programs::tc::TcError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> pub fn aya::programs::tc::TcError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)>
impl core::fmt::Debug for aya::programs::tc::TcError impl core::fmt::Debug for aya::programs::tc::TcError
@ -7955,8 +7965,7 @@ pub fn aya::programs::uprobe::UProbeError::borrow_mut(&mut self) -> &mut T
impl<T> core::convert::From<T> for aya::programs::uprobe::UProbeError impl<T> core::convert::From<T> for aya::programs::uprobe::UProbeError
pub fn aya::programs::uprobe::UProbeError::from(t: T) -> T pub fn aya::programs::uprobe::UProbeError::from(t: T) -> T
pub enum aya::programs::XdpError pub enum aya::programs::XdpError
pub aya::programs::XdpError::NetlinkError pub aya::programs::XdpError::NetlinkError(aya::sys::netlink::NetlinkError)
pub aya::programs::XdpError::NetlinkError::io_error: std::io::error::Error
impl core::convert::From<aya::programs::xdp::XdpError> for aya::programs::ProgramError impl core::convert::From<aya::programs::xdp::XdpError> for aya::programs::ProgramError
pub fn aya::programs::ProgramError::from(source: aya::programs::xdp::XdpError) -> Self pub fn aya::programs::ProgramError::from(source: aya::programs::xdp::XdpError) -> Self
impl core::error::Error for aya::programs::xdp::XdpError impl core::error::Error for aya::programs::xdp::XdpError

Loading…
Cancel
Save