From 5d608c5485f3cd3396deacf53c8a8f4062a03374 Mon Sep 17 00:00:00 2001 From: Mohammad Javad Pooladkhay Date: Wed, 23 Aug 2023 17:12:14 +0100 Subject: [PATCH] aya: add netlink_clsact_qdisc_exists function This function is used to check if the clasct qdisc exists on a given interface. This check should be performed before adding the mentioned qdisc. --- aya/src/programs/tc.rs | 10 ++++- aya/src/sys/netlink.rs | 53 +++++++++++++++++++++++-- test/integration-test/src/tests/load.rs | 7 +++- 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/aya/src/programs/tc.rs b/aya/src/programs/tc.rs index e289e7d0..f0da530c 100644 --- a/aya/src/programs/tc.rs +++ b/aya/src/programs/tc.rs @@ -14,8 +14,8 @@ use crate::{ }, programs::{define_link_wrapper, load_program, Link, ProgramData, ProgramError}, sys::{ - netlink_find_filter_with_name, netlink_qdisc_add_clsact, netlink_qdisc_attach, - netlink_qdisc_detach, + netlink_clsact_qdisc_exists, netlink_find_filter_with_name, netlink_qdisc_add_clsact, + netlink_qdisc_attach, netlink_qdisc_detach, }, util::{ifindex_from_ifname, tc_handler_make}, VerifierLogLevel, @@ -314,6 +314,12 @@ pub fn qdisc_add_clsact(if_name: &str) -> Result<(), io::Error> { unsafe { netlink_qdisc_add_clsact(if_index as i32) } } +/// Check if the `clasct` qdisc exists on the given interface. +pub fn clsact_qdisc_exists(if_name: &str) -> Result { + let if_index = ifindex_from_ifname(if_name)?; + unsafe { netlink_clsact_qdisc_exists(if_index as i32) } +} + /// Detaches the programs with the given name. /// /// # Errors diff --git a/aya/src/sys/netlink.rs b/aya/src/sys/netlink.rs index 9c5e815a..b003d86f 100644 --- a/aya/src/sys/netlink.rs +++ b/aya/src/sys/netlink.rs @@ -1,12 +1,19 @@ -use std::{collections::HashMap, ffi::CStr, io, mem, os::fd::RawFd, ptr, slice}; +use std::{ + collections::HashMap, + ffi::CStr, + io, + mem::{self}, + os::fd::RawFd, + ptr, slice, +}; use thiserror::Error; use libc::{ close, 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, NLA_ALIGNTO, NLA_F_NESTED, NLA_TYPE_MASK, NLMSG_DONE, NLMSG_ERROR, NLM_F_ACK, NLM_F_CREATE, - NLM_F_DUMP, NLM_F_ECHO, NLM_F_EXCL, NLM_F_MULTI, NLM_F_REQUEST, RTM_DELTFILTER, RTM_GETTFILTER, - RTM_NEWQDISC, RTM_NEWTFILTER, RTM_SETLINK, SOCK_RAW, SOL_NETLINK, + NLM_F_DUMP, NLM_F_ECHO, NLM_F_EXCL, NLM_F_MULTI, NLM_F_REQUEST, RTM_DELTFILTER, RTM_GETQDISC, + RTM_GETTFILTER, RTM_NEWQDISC, RTM_NEWTFILTER, RTM_SETLINK, SOCK_RAW, SOL_NETLINK, }; use crate::{ @@ -68,6 +75,46 @@ pub(crate) unsafe fn netlink_set_xdp_fd( Ok(()) } +pub(crate) unsafe fn netlink_clsact_qdisc_exists(if_index: i32) -> Result { + let sock = NetlinkSocket::open()?; + + let mut req = mem::zeroed::(); + + let nlmsg_len = mem::size_of::() + mem::size_of::(); + + 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 = 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::()..])?; + + 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<(), io::Error> { let sock = NetlinkSocket::open()?; diff --git a/test/integration-test/src/tests/load.rs b/test/integration-test/src/tests/load.rs index 5c6373c3..d7ffaf89 100644 --- a/test/integration-test/src/tests/load.rs +++ b/test/integration-test/src/tests/load.rs @@ -5,7 +5,7 @@ use aya::{ programs::{ links::{FdLink, PinnedLink}, loaded_links, loaded_programs, - tc::qdisc_add_clsact, + tc::{clsact_qdisc_exists, qdisc_add_clsact}, KProbe, SchedClassifier, TcAttachType, TracePoint, UProbe, Xdp, XdpFlags, }, util::KernelVersion, @@ -17,7 +17,10 @@ const RETRY_DURATION: time::Duration = time::Duration::from_millis(10); #[test] fn tc_name_limit() { - let _ = qdisc_add_clsact("lo"); + let clsact_exists = clsact_qdisc_exists("lo").unwrap(); + if !clsact_exists { + qdisc_add_clsact("lo").unwrap(); + } let mut bpf = Bpf::load(crate::TC_NAME_LIMIT_TEST).unwrap();