pull/728/merge
MJ Pooladkhay 5 days ago committed by GitHub
commit 9c1d9a76cf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -24,8 +24,8 @@ use crate::{
sys::{ sys::{
BpfLinkCreateArgs, LinkTarget, NetlinkError, ProgQueryTarget, SyscallError, BpfLinkCreateArgs, LinkTarget, NetlinkError, ProgQueryTarget, SyscallError,
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_clsact_qdisc_exists, netlink_find_filter_with_name, netlink_qdisc_add_clsact,
netlink_qdisc_detach, netlink_qdisc_attach, netlink_qdisc_detach,
}, },
util::{KernelVersion, ifindex_from_ifname, tc_handler_make}, util::{KernelVersion, ifindex_from_ifname, tc_handler_make},
}; };
@ -602,3 +602,9 @@ fn qdisc_detach_program_fast(
Ok(()) Ok(())
} }
/// Check if the `clasct` qdisc exists on the given interface.
pub fn clsact_qdisc_exists(if_name: &str) -> Result<bool, TcError> {
let if_index = ifindex_from_ifname(if_name)?;
unsafe { netlink_clsact_qdisc_exists(if_index as i32).map_err(TcError::NetlinkError) }
}

@ -15,8 +15,8 @@ use libc::{
AF_NETLINK, AF_UNSPEC, ETH_P_ALL, IFF_UP, IFLA_XDP, NETLINK_CAP_ACK, NETLINK_EXT_ACK, AF_NETLINK, AF_UNSPEC, ETH_P_ALL, IFF_UP, IFLA_XDP, NETLINK_CAP_ACK, NETLINK_EXT_ACK,
NETLINK_ROUTE, NLA_ALIGNTO, NLA_F_NESTED, NLA_TYPE_MASK, NLM_F_ACK, NLM_F_CREATE, NLM_F_DUMP, NETLINK_ROUTE, NLA_ALIGNTO, NLA_F_NESTED, NLA_TYPE_MASK, NLM_F_ACK, NLM_F_CREATE, NLM_F_DUMP,
NLM_F_ECHO, NLM_F_EXCL, NLM_F_MULTI, NLM_F_REQUEST, NLMSG_DONE, NLMSG_ERROR, RTM_DELTFILTER, NLM_F_ECHO, NLM_F_EXCL, NLM_F_MULTI, NLM_F_REQUEST, NLMSG_DONE, NLMSG_ERROR, RTM_DELTFILTER,
RTM_GETTFILTER, RTM_NEWQDISC, RTM_NEWTFILTER, RTM_SETLINK, SOCK_RAW, SOL_NETLINK, getsockname, RTM_GETQDISC, RTM_GETTFILTER, RTM_NEWQDISC, RTM_NEWTFILTER, RTM_SETLINK, SOCK_RAW, SOL_NETLINK,
nlattr, nlmsgerr, nlmsghdr, recv, send, setsockopt, sockaddr_nl, socket, getsockname, nlattr, nlmsgerr, nlmsghdr, recv, send, setsockopt, sockaddr_nl, socket,
}; };
use thiserror::Error; use thiserror::Error;
@ -105,6 +105,47 @@ pub(crate) unsafe fn netlink_set_xdp_fd(
Ok(()) Ok(())
} }
pub(crate) unsafe fn netlink_clsact_qdisc_exists(if_index: i32) -> Result<bool, NetlinkError> {
let sock = NetlinkSocket::open()?;
let mut req = unsafe { mem::zeroed::<TcRequest>() };
let nlmsg_len = mem::size_of::<nlmsghdr>() + mem::size_of::<tcmsg>();
req.header = nlmsghdr {
nlmsg_len: nlmsg_len as u32,
nlmsg_flags: (NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP) as u16,
nlmsg_type: RTM_GETQDISC,
nlmsg_pid: 0,
nlmsg_seq: 1,
};
req.tc_info.tcm_family = AF_UNSPEC as u8;
sock.send(&bytes_of(&req)[..req.header.nlmsg_len as usize])?;
for msg in sock.recv()? {
if msg.header.nlmsg_type != RTM_NEWQDISC {
continue;
}
let tc_msg = unsafe { ptr::read_unaligned(msg.data.as_ptr() as *const tcmsg) };
if tc_msg.tcm_ifindex != if_index {
continue;
}
let attrs = parse_attrs(&msg.data[mem::size_of::<tcmsg>()..])
.map_err(|e| NetlinkError(NetlinkErrorInternal::NlAttrError(e)))?;
if let Some(opts) = attrs.get(&(TCA_KIND as u16)) {
if opts.data == b"clsact\0" {
return Ok(true);
}
}
}
Ok(false)
}
pub(crate) unsafe fn netlink_qdisc_add_clsact(if_index: i32) -> Result<(), NetlinkError> { pub(crate) unsafe fn netlink_qdisc_add_clsact(if_index: i32) -> Result<(), NetlinkError> {
let sock = NetlinkSocket::open()?; let sock = NetlinkSocket::open()?;
@ -341,7 +382,13 @@ struct Request {
struct TcRequest { struct TcRequest {
header: nlmsghdr, header: nlmsghdr,
tc_info: tcmsg, tc_info: tcmsg,
attrs: [u8; 64], // The buffer for attributes should be sized to hold at least 256 bytes,
// based on `CLS_BPF_NAME_LEN = 256` from the kernel:
// https://github.com/torvalds/linux/blob/02aee814/net/sched/cls_bpf.c#L28
// We currently use around ~30 bytes of attributes in addition to name.
// Rather than picking a "right sized buffer" for the payload (which is of
// varying length anyway) we use the next largest power of 2.
attrs: [u8; 512],
} }
struct NetlinkSocket { struct NetlinkSocket {

@ -91,3 +91,11 @@ path = "src/xdp_sec.rs"
[[bin]] [[bin]]
name = "uprobe_cookie" name = "uprobe_cookie"
path = "src/uprobe_cookie.rs" path = "src/uprobe_cookie.rs"
[[bin]]
name = "tc_name_limit"
path = "src/tc_name_limit.rs"
[[bin]]
name = "tc_name_limit_exceeded"
path = "src/tc_name_limit_exceeded.rs"

@ -0,0 +1,22 @@
#![no_std]
#![no_main]
use aya_ebpf::{macros::classifier, programs::TcContext};
// A function with a 256-byte-long name (all 'a's) to be used as the name of
// the ebpf program. This name must match the name passed to userspace side
// of the program (i.e. test/integration-test/src/tests/load.rs).
// 256 is the maximum length allowed by the kernel, so this test should pass.
// https://github.com/torvalds/linux/blob/02aee814/net/sched/cls_bpf.c#L28
#[classifier]
pub fn aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(
_ctx: TcContext,
) -> i32 {
0
}
#[cfg(not(test))]
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}

@ -0,0 +1,22 @@
#![no_std]
#![no_main]
use aya_ebpf::{macros::classifier, programs::TcContext};
// A function with a 257-byte-long name (all 'a's) to be used as the name of
// the ebpf program. This name must match the name passed to userspace side
// of the program (i.e. test/integration-test/src/tests/load.rs).
// 256 is the maximum length allowed by the kernel, so this test should fail.
// https://github.com/torvalds/linux/blob/02aee814/net/sched/cls_bpf.c#L28
#[classifier]
pub fn aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(
_ctx: TcContext,
) -> i32 {
0
}
#[cfg(not(test))]
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}

@ -55,6 +55,8 @@ bpf_file!(
TWO_PROGS => "two_progs", TWO_PROGS => "two_progs",
XDP_SEC => "xdp_sec", XDP_SEC => "xdp_sec",
UPROBE_COOKIE => "uprobe_cookie", UPROBE_COOKIE => "uprobe_cookie",
TC_NAME_LIMIT_TEST => "tc_name_limit",
TC_NAME_LIMIT_EXCEEDED_TEST => "tc_name_limit_exceeded",
); );
#[cfg(test)] #[cfg(test)]

@ -1,12 +1,15 @@
use std::{convert::TryInto as _, fs::remove_file, path::Path, thread, time::Duration}; use std::{convert::TryInto as _, fs::remove_file, path::Path, thread, time::Duration};
use assert_matches::assert_matches;
use aya::{ use aya::{
Ebpf, Ebpf,
maps::Array, maps::Array,
programs::{ programs::{
FlowDissector, KProbe, TracePoint, UProbe, Xdp, XdpFlags, FlowDissector, KProbe, ProgramError, SchedClassifier, TcAttachType, TcError, TracePoint,
UProbe, Xdp, XdpFlags,
links::{FdLink, PinnedLink}, links::{FdLink, PinnedLink},
loaded_links, loaded_programs, loaded_links, loaded_programs,
tc::{clsact_qdisc_exists, qdisc_add_clsact},
}, },
util::KernelVersion, util::KernelVersion,
}; };
@ -605,3 +608,51 @@ fn pin_lifecycle_uprobe() {
// Make sure the function isn't optimized out. // Make sure the function isn't optimized out.
uprobe_function(); uprobe_function();
} }
#[test]
fn tc_name_limit() {
let clsact_exists = clsact_qdisc_exists("lo").unwrap();
if !clsact_exists {
qdisc_add_clsact("lo").unwrap();
}
let mut bpf = Ebpf::load(crate::TC_NAME_LIMIT_TEST).unwrap();
let long_name = "a".repeat(256);
let program: &mut SchedClassifier = bpf
.program_mut(long_name.as_str())
.unwrap()
.try_into()
.unwrap();
program.load().unwrap();
program.attach("lo", TcAttachType::Ingress).unwrap();
}
#[test]
fn tc_name_limit_exceeded() {
let clsact_exists = clsact_qdisc_exists("lo").unwrap();
if !clsact_exists {
qdisc_add_clsact("lo").unwrap();
}
let mut bpf = Ebpf::load(crate::TC_NAME_LIMIT_EXCEEDED_TEST).unwrap();
let long_name = "a".repeat(257);
let program: &mut SchedClassifier = bpf
.program_mut(long_name.as_str())
.unwrap()
.try_into()
.unwrap();
program.load().unwrap();
assert_matches!(
program.attach("lo", TcAttachType::Ingress),
Err(ProgramError::TcError(TcError::NetlinkError())) => {
// An invalid argument error (EINVAL) with code 22 should occur.
// The invalid argument is the tc program name which is too long.
assert_eq!(io_error.raw_os_error(), Some(22))
}
);
}

@ -6477,6 +6477,7 @@ 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::clsact_qdisc_exists(if_name: &str) -> core::result::Result<bool, aya::programs::tc::TcError>
pub fn aya::programs::tc::qdisc_add_clsact(if_name: &str) -> core::result::Result<(), aya::programs::tc::TcError> 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<(), 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<(), aya::programs::tc::TcError>
pub mod aya::programs::tp_btf pub mod aya::programs::tp_btf
@ -10429,4 +10430,4 @@ impl<T: aya::Pod, const N: usize> aya::Pod for [T; N]
pub fn aya::features() -> &'static aya_obj::obj::Features pub fn aya::features() -> &'static aya_obj::obj::Features
pub type aya::Bpf = aya::Ebpf pub type aya::Bpf = aya::Ebpf
pub type aya::BpfError = aya::EbpfError pub type aya::BpfError = aya::EbpfError
pub type aya::BpfLoader<'a> = aya::EbpfLoader<'a> pub type aya::BpfLoader<'a> = aya::EbpfLoader<'a>
Loading…
Cancel
Save