From b3ae7786d335fd0294a6ddecf3f31ef28d56af9d Mon Sep 17 00:00:00 2001 From: Alessandro Decina Date: Wed, 14 Dec 2022 17:24:29 +1100 Subject: [PATCH] aya: fix detaching links on drop https://github.com/aya-rs/aya/pull/366 broke detaching links on drop for everything but FdLink. This restores detach on drop for all links. --- aya/src/programs/cgroup_device.rs | 8 +-- aya/src/programs/cgroup_skb.rs | 6 +- aya/src/programs/cgroup_sock.rs | 4 +- aya/src/programs/cgroup_sock_addr.rs | 14 ++-- aya/src/programs/cgroup_sockopt.rs | 8 +-- aya/src/programs/cgroup_sysctl.rs | 8 +-- aya/src/programs/extension.rs | 8 ++- aya/src/programs/links.rs | 90 ++++++++++++++++--------- aya/src/programs/sk_lookup.rs | 4 +- aya/src/programs/sk_msg.rs | 2 +- aya/src/programs/sk_skb.rs | 8 ++- aya/src/programs/sock_ops.rs | 2 +- aya/src/programs/tc.rs | 2 +- aya/src/programs/xdp.rs | 43 ++++++------ test/integration-ebpf/src/test.rs | 14 +++- test/integration-test/src/tests/load.rs | 67 ++++++++++++++---- 16 files changed, 188 insertions(+), 100 deletions(-) diff --git a/aya/src/programs/cgroup_device.rs b/aya/src/programs/cgroup_device.rs index be52a26e..b4a9a3c4 100644 --- a/aya/src/programs/cgroup_device.rs +++ b/aya/src/programs/cgroup_device.rs @@ -72,9 +72,9 @@ impl CgroupDevice { )? as RawFd; self.data .links - .insert(CgroupDeviceLink(CgroupDeviceLinkInner::Fd(FdLink::new( - link_fd, - )))) + .insert(CgroupDeviceLink::new(CgroupDeviceLinkInner::Fd( + FdLink::new(link_fd), + ))) } else { bpf_prog_attach(prog_fd, cgroup_fd, BPF_CGROUP_DEVICE).map_err(|(_, io_error)| { ProgramError::SyscallError { @@ -84,7 +84,7 @@ impl CgroupDevice { })?; self.data .links - .insert(CgroupDeviceLink(CgroupDeviceLinkInner::ProgAttach( + .insert(CgroupDeviceLink::new(CgroupDeviceLinkInner::ProgAttach( ProgAttachLink::new(prog_fd, cgroup_fd, BPF_CGROUP_DEVICE), ))) } diff --git a/aya/src/programs/cgroup_skb.rs b/aya/src/programs/cgroup_skb.rs index 17d69063..3911a3c3 100644 --- a/aya/src/programs/cgroup_skb.rs +++ b/aya/src/programs/cgroup_skb.rs @@ -105,7 +105,9 @@ impl CgroupSkb { )? as RawFd; self.data .links - .insert(CgroupSkbLink(CgroupSkbLinkInner::Fd(FdLink::new(link_fd)))) + .insert(CgroupSkbLink::new(CgroupSkbLinkInner::Fd(FdLink::new( + link_fd, + )))) } else { bpf_prog_attach(prog_fd, cgroup_fd, attach_type).map_err(|(_, io_error)| { ProgramError::SyscallError { @@ -116,7 +118,7 @@ impl CgroupSkb { self.data .links - .insert(CgroupSkbLink(CgroupSkbLinkInner::ProgAttach( + .insert(CgroupSkbLink::new(CgroupSkbLinkInner::ProgAttach( ProgAttachLink::new(prog_fd, cgroup_fd, attach_type), ))) } diff --git a/aya/src/programs/cgroup_sock.rs b/aya/src/programs/cgroup_sock.rs index 17bc68ee..2a87a91d 100644 --- a/aya/src/programs/cgroup_sock.rs +++ b/aya/src/programs/cgroup_sock.rs @@ -81,7 +81,7 @@ impl CgroupSock { )? as RawFd; self.data .links - .insert(CgroupSockLink(CgroupSockLinkInner::Fd(FdLink::new( + .insert(CgroupSockLink::new(CgroupSockLinkInner::Fd(FdLink::new( link_fd, )))) } else { @@ -94,7 +94,7 @@ impl CgroupSock { self.data .links - .insert(CgroupSockLink(CgroupSockLinkInner::ProgAttach( + .insert(CgroupSockLink::new(CgroupSockLinkInner::ProgAttach( ProgAttachLink::new(prog_fd, cgroup_fd, attach_type), ))) } diff --git a/aya/src/programs/cgroup_sock_addr.rs b/aya/src/programs/cgroup_sock_addr.rs index aa8b71fd..2f8aac7d 100644 --- a/aya/src/programs/cgroup_sock_addr.rs +++ b/aya/src/programs/cgroup_sock_addr.rs @@ -82,7 +82,7 @@ impl CgroupSockAddr { )? as RawFd; self.data .links - .insert(CgroupSockAddrLink(CgroupSockAddrLinkInner::Fd( + .insert(CgroupSockAddrLink::new(CgroupSockAddrLinkInner::Fd( FdLink::new(link_fd), ))) } else { @@ -93,11 +93,13 @@ impl CgroupSockAddr { } })?; - self.data - .links - .insert(CgroupSockAddrLink(CgroupSockAddrLinkInner::ProgAttach( - ProgAttachLink::new(prog_fd, cgroup_fd, attach_type), - ))) + self.data.links.insert(CgroupSockAddrLink::new( + CgroupSockAddrLinkInner::ProgAttach(ProgAttachLink::new( + prog_fd, + cgroup_fd, + attach_type, + )), + )) } } diff --git a/aya/src/programs/cgroup_sockopt.rs b/aya/src/programs/cgroup_sockopt.rs index 14df5fc6..669e4f99 100644 --- a/aya/src/programs/cgroup_sockopt.rs +++ b/aya/src/programs/cgroup_sockopt.rs @@ -79,9 +79,9 @@ impl CgroupSockopt { )? as RawFd; self.data .links - .insert(CgroupSockoptLink(CgroupSockoptLinkInner::Fd(FdLink::new( - link_fd, - )))) + .insert(CgroupSockoptLink::new(CgroupSockoptLinkInner::Fd( + FdLink::new(link_fd), + ))) } else { bpf_prog_attach(prog_fd, cgroup_fd, attach_type).map_err(|(_, io_error)| { ProgramError::SyscallError { @@ -92,7 +92,7 @@ impl CgroupSockopt { self.data .links - .insert(CgroupSockoptLink(CgroupSockoptLinkInner::ProgAttach( + .insert(CgroupSockoptLink::new(CgroupSockoptLinkInner::ProgAttach( ProgAttachLink::new(prog_fd, cgroup_fd, attach_type), ))) } diff --git a/aya/src/programs/cgroup_sysctl.rs b/aya/src/programs/cgroup_sysctl.rs index f4a3f352..7c39a1e3 100644 --- a/aya/src/programs/cgroup_sysctl.rs +++ b/aya/src/programs/cgroup_sysctl.rs @@ -74,9 +74,9 @@ impl CgroupSysctl { )? as RawFd; self.data .links - .insert(CgroupSysctlLink(CgroupSysctlLinkInner::Fd(FdLink::new( - link_fd, - )))) + .insert(CgroupSysctlLink::new(CgroupSysctlLinkInner::Fd( + FdLink::new(link_fd), + ))) } else { bpf_prog_attach(prog_fd, cgroup_fd, BPF_CGROUP_SYSCTL).map_err(|(_, io_error)| { ProgramError::SyscallError { @@ -87,7 +87,7 @@ impl CgroupSysctl { self.data .links - .insert(CgroupSysctlLink(CgroupSysctlLinkInner::ProgAttach( + .insert(CgroupSysctlLink::new(CgroupSysctlLinkInner::ProgAttach( ProgAttachLink::new(prog_fd, cgroup_fd, BPF_CGROUP_SYSCTL), ))) } diff --git a/aya/src/programs/extension.rs b/aya/src/programs/extension.rs index 75296244..263b7b67 100644 --- a/aya/src/programs/extension.rs +++ b/aya/src/programs/extension.rs @@ -95,7 +95,9 @@ impl Extension { call: "bpf_link_create".to_owned(), io_error, })? as RawFd; - self.data.links.insert(ExtensionLink(FdLink::new(link_fd))) + self.data + .links + .insert(ExtensionLink::new(FdLink::new(link_fd))) } /// Attaches the extension to another program. @@ -123,7 +125,9 @@ impl Extension { call: "bpf_link_create".to_owned(), io_error, })? as RawFd; - self.data.links.insert(ExtensionLink(FdLink::new(link_fd))) + self.data + .links + .insert(ExtensionLink::new(FdLink::new(link_fd))) } /// Detaches the extension. diff --git a/aya/src/programs/links.rs b/aya/src/programs/links.rs index 58d0ed12..79aa3c07 100644 --- a/aya/src/programs/links.rs +++ b/aya/src/programs/links.rs @@ -79,17 +79,17 @@ impl Drop for LinkMap { /// The identifier of an `FdLink`. #[derive(Debug, Hash, Eq, PartialEq)] -pub struct FdLinkId(pub(crate) Option); +pub struct FdLinkId(pub(crate) RawFd); /// A file descriptor link. #[derive(Debug)] pub struct FdLink { - pub(crate) fd: Option, + pub(crate) fd: RawFd, } impl FdLink { pub(crate) fn new(fd: RawFd) -> FdLink { - FdLink { fd: Some(fd) } + FdLink { fd } } /// Pins the link to a BPF file system. @@ -121,21 +121,18 @@ impl FdLink { /// let pinned_link = fd_link.pin("/sys/fs/bpf/example")?; /// # Ok::<(), Error>(()) /// ``` - pub fn pin>(mut self, path: P) -> Result { - let fd = self.fd.take().ok_or_else(|| PinError::NoFd { - name: "link".to_string(), - })?; + pub fn pin>(self, path: P) -> Result { let path_string = CString::new(path.as_ref().to_string_lossy().into_owned()).map_err(|e| { PinError::InvalidPinPath { error: e.to_string(), } })?; - bpf_pin_object(fd, &path_string).map_err(|(_, io_error)| PinError::SyscallError { + bpf_pin_object(self.fd, &path_string).map_err(|(_, io_error)| PinError::SyscallError { name: "BPF_OBJ_PIN".to_string(), io_error, })?; - Ok(PinnedLink::new(PathBuf::from(path.as_ref()), fd)) + Ok(PinnedLink::new(PathBuf::from(path.as_ref()), self)) } } @@ -147,27 +144,27 @@ impl Link for FdLink { } fn detach(self) -> Result<(), ProgramError> { - // detach is a noop since it consumes self. once self is consumed, - // drop will be triggered and the link will be detached. + // detach is a noop since it consumes self. once self is consumed, drop will be triggered + // and the link will be detached. + // + // Other links don't need to do this since they use define_link_wrapper!, but FdLink is a + // bit special in that it defines a custom ::new() so it can't use the macro. Ok(()) } } -impl Drop for FdLink { - fn drop(&mut self) { - if let Some(fd) = self.fd.take() { - // Safety: libc - unsafe { close(fd) }; - } - } -} - impl From for FdLink { fn from(p: PinnedLink) -> Self { p.inner } } +impl Drop for FdLink { + fn drop(&mut self) { + unsafe { close(self.fd) }; + } +} + /// A pinned file descriptor link. /// /// This link has been pinned to the BPF filesystem. On drop, the file descriptor that backs @@ -180,11 +177,8 @@ pub struct PinnedLink { } impl PinnedLink { - fn new(path: PathBuf, fd: RawFd) -> Self { - PinnedLink { - inner: FdLink::new(fd), - path, - } + fn new(path: PathBuf, link: FdLink) -> Self { + PinnedLink { inner: link, path } } /// Creates a [`PinnedLink`] from a valid path on bpffs. @@ -196,7 +190,10 @@ impl PinnedLink { code, io_error, })? as RawFd; - Ok(PinnedLink::new(path.as_ref().to_path_buf(), fd)) + Ok(PinnedLink::new( + path.as_ref().to_path_buf(), + FdLink::new(fd), + )) } /// Removes the pinned link from the filesystem and returns an [`FdLink`]. @@ -254,29 +251,56 @@ macro_rules! define_link_wrapper { #[$doc1] #[derive(Debug)] - pub struct $wrapper($base); + pub struct $wrapper(Option<$base>); + + #[allow(dead_code)] + // allow dead code since currently XDP is the only consumer of inner and + // into_inner + impl $wrapper { + fn new(base: $base) -> $wrapper { + $wrapper(Some(base)) + } + + fn inner(&self) -> &$base { + self.0.as_ref().unwrap() + } + + fn into_inner(mut self) -> $base { + self.0.take().unwrap() + } + } + + impl Drop for $wrapper { + fn drop(&mut self) { + use crate::programs::links::Link; + + if let Some(base) = self.0.take() { + let _ = base.detach(); + } + } + } impl crate::programs::Link for $wrapper { type Id = $wrapper_id; fn id(&self) -> Self::Id { - $wrapper_id(self.0.id()) + $wrapper_id(self.0.as_ref().unwrap().id()) } - fn detach(self) -> Result<(), ProgramError> { - self.0.detach() + fn detach(mut self) -> Result<(), ProgramError> { + self.0.take().unwrap().detach() } } impl From<$base> for $wrapper { fn from(b: $base) -> $wrapper { - $wrapper(b) + $wrapper(Some(b)) } } impl From<$wrapper> for $base { - fn from(w: $wrapper) -> $base { - w.0 + fn from(mut w: $wrapper) -> $base { + w.0.take().unwrap() } } }; diff --git a/aya/src/programs/sk_lookup.rs b/aya/src/programs/sk_lookup.rs index a64e062a..8d95e9ec 100644 --- a/aya/src/programs/sk_lookup.rs +++ b/aya/src/programs/sk_lookup.rs @@ -70,7 +70,9 @@ impl SkLookup { io_error, }, )? as RawFd; - self.data.links.insert(SkLookupLink(FdLink::new(link_fd))) + self.data + .links + .insert(SkLookupLink::new(FdLink::new(link_fd))) } /// Takes ownership of the link referenced by the provided link_id. diff --git a/aya/src/programs/sk_msg.rs b/aya/src/programs/sk_msg.rs index 2cf4d51a..a60346d8 100644 --- a/aya/src/programs/sk_msg.rs +++ b/aya/src/programs/sk_msg.rs @@ -88,7 +88,7 @@ impl SkMsg { io_error, } })?; - self.data.links.insert(SkMsgLink(ProgAttachLink::new( + self.data.links.insert(SkMsgLink::new(ProgAttachLink::new( prog_fd, map_fd, BPF_SK_MSG_VERDICT, diff --git a/aya/src/programs/sk_skb.rs b/aya/src/programs/sk_skb.rs index 79e741fd..a0470506 100644 --- a/aya/src/programs/sk_skb.rs +++ b/aya/src/programs/sk_skb.rs @@ -84,9 +84,11 @@ impl SkSkb { io_error, } })?; - self.data - .links - .insert(SkSkbLink(ProgAttachLink::new(prog_fd, map_fd, attach_type))) + self.data.links.insert(SkSkbLink::new(ProgAttachLink::new( + prog_fd, + map_fd, + attach_type, + ))) } /// Detaches the program. diff --git a/aya/src/programs/sock_ops.rs b/aya/src/programs/sock_ops.rs index c46540e4..9021b312 100644 --- a/aya/src/programs/sock_ops.rs +++ b/aya/src/programs/sock_ops.rs @@ -68,7 +68,7 @@ impl SockOps { io_error, } })?; - self.data.links.insert(SockOpsLink(ProgAttachLink::new( + self.data.links.insert(SockOpsLink::new(ProgAttachLink::new( prog_fd, cgroup_fd, BPF_CGROUP_SOCK_OPS, diff --git a/aya/src/programs/tc.rs b/aya/src/programs/tc.rs index 9b14858d..d1e90be1 100644 --- a/aya/src/programs/tc.rs +++ b/aya/src/programs/tc.rs @@ -165,7 +165,7 @@ impl SchedClassifier { } .map_err(|io_error| TcError::NetlinkError { io_error })?; - self.data.links.insert(SchedClassifierLink(TcLink { + self.data.links.insert(SchedClassifierLink::new(TcLink { if_index: if_index as i32, attach_type, priority, diff --git a/aya/src/programs/xdp.rs b/aya/src/programs/xdp.rs index a979bc08..d36d5f27 100644 --- a/aya/src/programs/xdp.rs +++ b/aya/src/programs/xdp.rs @@ -117,16 +117,18 @@ impl Xdp { )? as RawFd; self.data .links - .insert(XdpLink(XdpLinkInner::FdLink(FdLink::new(link_fd)))) + .insert(XdpLink::new(XdpLinkInner::FdLink(FdLink::new(link_fd)))) } else { unsafe { netlink_set_xdp_fd(if_index, prog_fd, None, flags.bits) } .map_err(|io_error| XdpError::NetlinkError { io_error })?; - self.data.links.insert(XdpLink(XdpLinkInner::NlLink(NlLink { - if_index, - prog_fd, - flags, - }))) + self.data + .links + .insert(XdpLink::new(XdpLinkInner::NlLink(NlLink { + if_index, + prog_fd, + flags, + }))) } } @@ -150,9 +152,9 @@ impl Xdp { /// Ownership of the link will transfer to this program. pub fn attach_to_link(&mut self, link: XdpLink) -> Result { let prog_fd = self.data.fd_or_err()?; - match &link.0 { + match link.inner() { XdpLinkInner::FdLink(fd_link) => { - let link_fd = fd_link.fd.unwrap(); + let link_fd = fd_link.fd; bpf_link_update(link_fd, prog_fd, None, 0).map_err(|(_, io_error)| { ProgramError::SyscallError { call: "bpf_link_update".to_string(), @@ -163,7 +165,7 @@ impl Xdp { mem::forget(link); self.data .links - .insert(XdpLink(XdpLinkInner::FdLink(FdLink::new(link_fd)))) + .insert(XdpLink::new(XdpLinkInner::FdLink(FdLink::new(link_fd)))) } XdpLinkInner::NlLink(nl_link) => { let if_index = nl_link.if_index; @@ -176,11 +178,13 @@ impl Xdp { } // dispose of link and avoid detach on drop mem::forget(link); - self.data.links.insert(XdpLink(XdpLinkInner::NlLink(NlLink { - if_index, - prog_fd, - flags, - }))) + self.data + .links + .insert(XdpLink::new(XdpLinkInner::NlLink(NlLink { + if_index, + prog_fd, + flags, + }))) } } } @@ -246,7 +250,7 @@ impl TryFrom for FdLink { type Error = LinkError; fn try_from(value: XdpLink) -> Result { - if let XdpLinkInner::FdLink(fd) = value.0 { + if let XdpLinkInner::FdLink(fd) = value.into_inner() { Ok(fd) } else { Err(LinkError::InvalidLink) @@ -259,15 +263,14 @@ impl TryFrom for XdpLink { fn try_from(fd_link: FdLink) -> Result { // unwrap of fd_link.fd will not panic since it's only None when being dropped. - let info = bpf_link_get_info_by_fd(fd_link.fd.unwrap()).map_err(|io_error| { - LinkError::SyscallError { + let info = + bpf_link_get_info_by_fd(fd_link.fd).map_err(|io_error| LinkError::SyscallError { call: "BPF_OBJ_GET_INFO_BY_FD".to_string(), code: 0, io_error, - } - })?; + })?; if info.type_ == (bpf_link_type::BPF_LINK_TYPE_XDP as u32) { - return Ok(XdpLink(XdpLinkInner::FdLink(fd_link))); + return Ok(XdpLink::new(XdpLinkInner::FdLink(fd_link))); } Err(LinkError::InvalidLink) } diff --git a/test/integration-ebpf/src/test.rs b/test/integration-ebpf/src/test.rs index d0ff7b32..8d40af01 100644 --- a/test/integration-ebpf/src/test.rs +++ b/test/integration-ebpf/src/test.rs @@ -1,9 +1,13 @@ #![no_std] #![no_main] -use aya_bpf::{bindings::xdp_action, macros::xdp, programs::XdpContext}; +use aya_bpf::{ + bindings::xdp_action, + macros::{kprobe, xdp}, + programs::{ProbeContext, XdpContext}, +}; -#[xdp(name = "test_unload")] +#[xdp(name = "test_unload_xdp")] pub fn pass(ctx: XdpContext) -> u32 { match unsafe { try_pass(ctx) } { Ok(ret) => ret, @@ -15,6 +19,12 @@ unsafe fn try_pass(_ctx: XdpContext) -> Result { Ok(xdp_action::XDP_PASS) } +#[kprobe] +// truncated name to match bpftool output +pub fn test_unload_kpr(_ctx: ProbeContext) -> u32 { + 0 +} + #[panic_handler] fn panic(_info: &core::panic::PanicInfo) -> ! { unsafe { core::hint::unreachable_unchecked() } diff --git a/test/integration-test/src/tests/load.rs b/test/integration-test/src/tests/load.rs index f9a88ff9..e593d9b5 100644 --- a/test/integration-test/src/tests/load.rs +++ b/test/integration-test/src/tests/load.rs @@ -5,7 +5,7 @@ use aya::{ maps::Array, programs::{ links::{FdLink, PinnedLink}, - TracePoint, Xdp, XdpFlags, + KProbe, TracePoint, Xdp, XdpFlags, }, Bpf, }; @@ -84,28 +84,63 @@ macro_rules! assert_loaded { } #[integration_test] -fn unload() { +fn unload_xdp() { let bytes = include_bytes_aligned!("../../../../target/bpfel-unknown-none/debug/test"); let mut bpf = Bpf::load(bytes).unwrap(); - let prog: &mut Xdp = bpf.program_mut("test_unload").unwrap().try_into().unwrap(); + let prog: &mut Xdp = bpf + .program_mut("test_unload_xdp") + .unwrap() + .try_into() + .unwrap(); prog.load().unwrap(); + assert_loaded!("test_unload_xdp", true); let link = prog.attach("lo", XdpFlags::default()).unwrap(); { - let _link_owned = prog.take_link(link); + let _link_owned = prog.take_link(link).unwrap(); prog.unload().unwrap(); - assert_loaded!("test_unload", true); + assert_loaded!("test_unload_xdp", true); }; - assert_loaded!("test_unload", false); + assert_loaded!("test_unload_xdp", false); prog.load().unwrap(); - assert_loaded!("test_unload", true); + assert_loaded!("test_unload_xdp", true); prog.attach("lo", XdpFlags::default()).unwrap(); - assert_loaded!("test_unload", true); + assert_loaded!("test_unload_xdp", true); + prog.unload().unwrap(); + + assert_loaded!("test_unload_xdp", false); +} + +#[integration_test] +fn unload_kprobe() { + let bytes = include_bytes_aligned!("../../../../target/bpfel-unknown-none/debug/test"); + let mut bpf = Bpf::load(bytes).unwrap(); + let prog: &mut KProbe = bpf + .program_mut("test_unload_kpr") + .unwrap() + .try_into() + .unwrap(); + prog.load().unwrap(); + assert_loaded!("test_unload_kpr", true); + let link = prog.attach("try_to_wake_up", 0).unwrap(); + { + let _link_owned = prog.take_link(link).unwrap(); + prog.unload().unwrap(); + assert_loaded!("test_unload_kpr", true); + }; + + assert_loaded!("test_unload_kpr", false); + prog.load().unwrap(); + + assert_loaded!("test_unload_kpr", true); + prog.attach("try_to_wake_up", 0).unwrap(); + + assert_loaded!("test_unload_kpr", true); prog.unload().unwrap(); - assert_loaded!("test_unload", false); + assert_loaded!("test_unload_kpr", false); } #[integration_test] @@ -117,26 +152,30 @@ fn pin_link() { let bytes = include_bytes_aligned!("../../../../target/bpfel-unknown-none/debug/test"); let mut bpf = Bpf::load(bytes).unwrap(); - let prog: &mut Xdp = bpf.program_mut("test_unload").unwrap().try_into().unwrap(); + let prog: &mut Xdp = bpf + .program_mut("test_unload_xdp") + .unwrap() + .try_into() + .unwrap(); prog.load().unwrap(); let link_id = prog.attach("lo", XdpFlags::default()).unwrap(); let link = prog.take_link(link_id).unwrap(); - assert_loaded!("test_unload", true); + assert_loaded!("test_unload_xdp", true); let fd_link: FdLink = link.try_into().unwrap(); let pinned = fd_link.pin("/sys/fs/bpf/aya-xdp-test-lo").unwrap(); // because of the pin, the program is still attached prog.unload().unwrap(); - assert_loaded!("test_unload", true); + assert_loaded!("test_unload_xdp", true); // delete the pin, but the program is still attached let new_link = pinned.unpin().unwrap(); - assert_loaded!("test_unload", true); + assert_loaded!("test_unload_xdp", true); // finally when new_link is dropped we're detached drop(new_link); - assert_loaded!("test_unload", false); + assert_loaded!("test_unload_xdp", false); } #[integration_test]