Przeglądaj źródła

Merge pull request #366 from dave-tucker/pin-redux-2

aya: Fix Link Pinning
Dave Tucker 2 lat temu
rodzic
commit
4826bf7f74

+ 3 - 7
aya/src/programs/cgroup_skb.rs

@@ -10,8 +10,7 @@ use crate::{
         bpf_prog_type::BPF_PROG_TYPE_CGROUP_SKB,
     },
     programs::{
-        define_link_wrapper, load_program, FdLink, Link, OwnedLink, ProgAttachLink, ProgramData,
-        ProgramError,
+        define_link_wrapper, load_program, FdLink, Link, ProgAttachLink, ProgramData, ProgramError,
     },
     sys::{bpf_link_create, bpf_prog_attach, kernel_version},
 };
@@ -127,11 +126,8 @@ impl CgroupSkb {
     ///
     /// The link will be detached on `Drop` and the caller is now responsible
     /// for managing its lifetime.
-    pub fn take_link(
-        &mut self,
-        link_id: CgroupSkbLinkId,
-    ) -> Result<OwnedLink<CgroupSkbLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    pub fn take_link(&mut self, link_id: CgroupSkbLinkId) -> Result<CgroupSkbLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 
     /// Detaches the program.

+ 3 - 7
aya/src/programs/cgroup_sock.rs

@@ -10,8 +10,7 @@ use std::{
 use crate::{
     generated::bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCK,
     programs::{
-        define_link_wrapper, load_program, FdLink, Link, OwnedLink, ProgAttachLink, ProgramData,
-        ProgramError,
+        define_link_wrapper, load_program, FdLink, Link, ProgAttachLink, ProgramData, ProgramError,
     },
     sys::{bpf_link_create, bpf_prog_attach, kernel_version},
 };
@@ -105,11 +104,8 @@ impl CgroupSock {
     ///
     /// The link will be detached on `Drop` and the caller is now responsible
     /// for managing its lifetime.
-    pub fn take_link(
-        &mut self,
-        link_id: CgroupSockLinkId,
-    ) -> Result<OwnedLink<CgroupSockLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    pub fn take_link(&mut self, link_id: CgroupSockLinkId) -> Result<CgroupSockLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 
     /// Detaches the program.

+ 3 - 4
aya/src/programs/cgroup_sock_addr.rs

@@ -10,8 +10,7 @@ use std::{
 use crate::{
     generated::bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
     programs::{
-        define_link_wrapper, load_program, FdLink, Link, OwnedLink, ProgAttachLink, ProgramData,
-        ProgramError,
+        define_link_wrapper, load_program, FdLink, Link, ProgAttachLink, ProgramData, ProgramError,
     },
     sys::{bpf_link_create, bpf_prog_attach, kernel_version},
 };
@@ -109,8 +108,8 @@ impl CgroupSockAddr {
     pub fn take_link(
         &mut self,
         link_id: CgroupSockAddrLinkId,
-    ) -> Result<OwnedLink<CgroupSockAddrLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    ) -> Result<CgroupSockAddrLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 
     /// Detaches the program.

+ 4 - 4
aya/src/programs/cgroup_sockopt.rs

@@ -9,8 +9,8 @@ use std::{
 use crate::{
     generated::bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCKOPT,
     programs::{
-        bpf_attach_type, define_link_wrapper, load_program, FdLink, Link, OwnedLink,
-        ProgAttachLink, ProgramData, ProgramError,
+        bpf_attach_type, define_link_wrapper, load_program, FdLink, Link, ProgAttachLink,
+        ProgramData, ProgramError,
     },
     sys::{bpf_link_create, bpf_prog_attach, kernel_version},
 };
@@ -105,8 +105,8 @@ impl CgroupSockopt {
     pub fn take_link(
         &mut self,
         link_id: CgroupSockoptLinkId,
-    ) -> Result<OwnedLink<CgroupSockoptLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    ) -> Result<CgroupSockoptLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 
     /// Detaches the program.

+ 3 - 4
aya/src/programs/cgroup_sysctl.rs

@@ -7,8 +7,7 @@ use std::{
 use crate::{
     generated::{bpf_attach_type::BPF_CGROUP_SYSCTL, bpf_prog_type::BPF_PROG_TYPE_CGROUP_SYSCTL},
     programs::{
-        define_link_wrapper, load_program, FdLink, Link, OwnedLink, ProgAttachLink, ProgramData,
-        ProgramError,
+        define_link_wrapper, load_program, FdLink, Link, ProgAttachLink, ProgramData, ProgramError,
     },
     sys::{bpf_link_create, bpf_prog_attach, kernel_version},
 };
@@ -101,8 +100,8 @@ impl CgroupSysctl {
     pub fn take_link(
         &mut self,
         link_id: CgroupSysctlLinkId,
-    ) -> Result<OwnedLink<CgroupSysctlLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    ) -> Result<CgroupSysctlLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 
     /// Detaches the program.

+ 3 - 7
aya/src/programs/extension.rs

@@ -8,8 +8,7 @@ use crate::{
     generated::{bpf_attach_type::BPF_CGROUP_INET_INGRESS, bpf_prog_type::BPF_PROG_TYPE_EXT},
     obj::btf::BtfKind,
     programs::{
-        define_link_wrapper, load_program, FdLink, FdLinkId, OwnedLink, ProgramData, ProgramError,
-        ProgramFd,
+        define_link_wrapper, load_program, FdLink, FdLinkId, ProgramData, ProgramError, ProgramFd,
     },
     sys::{self, bpf_link_create},
     Btf,
@@ -139,11 +138,8 @@ impl Extension {
     ///
     /// The link will be detached on `Drop` and the caller is now responsible
     /// for managing its lifetime.
-    pub fn take_link(
-        &mut self,
-        link_id: ExtensionLinkId,
-    ) -> Result<OwnedLink<ExtensionLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    pub fn take_link(&mut self, link_id: ExtensionLinkId) -> Result<ExtensionLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 }
 

+ 3 - 6
aya/src/programs/fentry.rs

@@ -5,7 +5,7 @@ use crate::{
     obj::btf::{Btf, BtfKind},
     programs::{
         define_link_wrapper, load_program, utils::attach_raw_tracepoint, FdLink, FdLinkId,
-        OwnedLink, ProgramData, ProgramError,
+        ProgramData, ProgramError,
     },
 };
 
@@ -79,11 +79,8 @@ impl FEntry {
     ///
     /// The link will be detached on `Drop` and the caller is now responsible
     /// for managing its lifetime.
-    pub fn take_link(
-        &mut self,
-        link_id: FEntryLinkId,
-    ) -> Result<OwnedLink<FEntryLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    pub fn take_link(&mut self, link_id: FEntryLinkId) -> Result<FEntryLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 }
 

+ 3 - 6
aya/src/programs/fexit.rs

@@ -5,7 +5,7 @@ use crate::{
     obj::btf::{Btf, BtfKind},
     programs::{
         define_link_wrapper, load_program, utils::attach_raw_tracepoint, FdLink, FdLinkId,
-        OwnedLink, ProgramData, ProgramError,
+        ProgramData, ProgramError,
     },
 };
 
@@ -79,11 +79,8 @@ impl FExit {
     ///
     /// The link will be detached on `Drop` and the caller is now responsible
     /// for managing its lifetime.
-    pub fn take_link(
-        &mut self,
-        link_id: FExitLinkId,
-    ) -> Result<OwnedLink<FExitLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    pub fn take_link(&mut self, link_id: FExitLinkId) -> Result<FExitLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 }
 

+ 3 - 6
aya/src/programs/kprobe.rs

@@ -8,7 +8,7 @@ use crate::{
         define_link_wrapper, load_program,
         perf_attach::{PerfLink, PerfLinkId},
         probe::{attach, ProbeKind},
-        OwnedLink, ProgramData, ProgramError,
+        ProgramData, ProgramError,
     },
 };
 
@@ -80,11 +80,8 @@ impl KProbe {
     ///
     /// The link will be detached on `Drop` and the caller is now responsible
     /// for managing its lifetime.
-    pub fn take_link(
-        &mut self,
-        link_id: KProbeLinkId,
-    ) -> Result<OwnedLink<KProbeLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    pub fn take_link(&mut self, link_id: KProbeLinkId) -> Result<KProbeLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 }
 

+ 113 - 69
aya/src/programs/links.rs

@@ -1,13 +1,13 @@
 //! Program links.
 use libc::{close, dup};
+use thiserror::Error;
 
 use std::{
-    borrow::Borrow,
     collections::{hash_map::Entry, HashMap},
     ffi::CString,
-    ops::Deref,
+    io,
     os::unix::prelude::RawFd,
-    path::Path,
+    path::{Path, PathBuf},
 };
 
 use crate::{
@@ -25,37 +25,10 @@ pub trait Link: std::fmt::Debug + 'static {
     /// Returns the link id
     fn id(&self) -> Self::Id;
 
-    /// Detaches the Link
+    /// Detaches the LinkOwnedLink is gone... but this doesn't work :(
     fn detach(self) -> Result<(), ProgramError>;
 }
 
-/// An owned link that automatically detaches the inner link when dropped.
-pub struct OwnedLink<T: Link> {
-    inner: Option<T>,
-}
-
-impl<T: Link> OwnedLink<T> {
-    pub(crate) fn new(inner: T) -> Self {
-        Self { inner: Some(inner) }
-    }
-}
-
-impl<T: Link> Deref for OwnedLink<T> {
-    type Target = T;
-
-    fn deref(&self) -> &Self::Target {
-        self.inner.borrow().as_ref().unwrap()
-    }
-}
-
-impl<T: Link> Drop for OwnedLink<T> {
-    fn drop(&mut self) {
-        if let Some(link) = self.inner.take() {
-            link.detach().unwrap();
-        }
-    }
-}
-
 #[derive(Debug)]
 pub(crate) struct LinkMap<T: Link> {
     links: HashMap<T::Id, T>,
@@ -106,37 +79,63 @@ impl<T: Link> Drop for LinkMap<T> {
 
 /// The identifier of an `FdLink`.
 #[derive(Debug, Hash, Eq, PartialEq)]
-pub struct FdLinkId(pub(crate) RawFd);
+pub struct FdLinkId(pub(crate) Option<RawFd>);
 
 /// A file descriptor link.
 #[derive(Debug)]
 pub struct FdLink {
-    pub(crate) fd: RawFd,
+    pub(crate) fd: Option<RawFd>,
 }
 
 impl FdLink {
     pub(crate) fn new(fd: RawFd) -> FdLink {
-        FdLink { fd }
+        FdLink { fd: Some(fd) }
     }
 
-    /// Pins the FdLink to a BPF filesystem.
+    /// Pins the link to a BPF file system.
     ///
-    /// When a BPF object is pinned to a BPF filesystem it will remain attached after
-    /// Aya has detached the link.
-    /// To remove the attachment, the file on the BPF filesystem must be removed.
-    /// Any directories in the the path provided should have been created by the caller.
-    pub fn pin<P: AsRef<Path>>(&self, path: P) -> Result<(), PinError> {
+    /// When a link is pinned it will remain attached even after the link instance is dropped,
+    /// and will only be detached once the pinned file is removed. To unpin, see [PinnedFd::unpin].
+    ///
+    /// The parent directories in the provided path must already exist before calling this method,
+    /// and must be on a BPF file system (bpffs).
+    ///
+    /// # Example
+    /// ```no_run
+    /// # use aya::programs::{links::FdLink, Extension};
+    /// # use std::convert::TryInto;
+    /// # #[derive(thiserror::Error, Debug)]
+    /// # enum Error {
+    /// #     #[error(transparent)]
+    /// #     Bpf(#[from] aya::BpfError),
+    /// #     #[error(transparent)]
+    /// #     Pin(#[from] aya::pin::PinError),
+    /// #     #[error(transparent)]
+    /// #     Program(#[from] aya::programs::ProgramError)
+    /// # }
+    /// # let mut bpf = aya::Bpf::load(&[])?;
+    /// # let prog: &mut Extension = bpf.program_mut("example").unwrap().try_into()?;
+    /// let link_id = prog.attach()?;
+    /// let owned_link = prog.take_link(link_id)?;
+    /// let fd_link: FdLink = owned_link.into();
+    /// let pinned_link = fd_link.pin("/sys/fs/bpf/example")?;
+    /// # Ok::<(), Error>(())
+    /// ```
+    pub fn pin<P: AsRef<Path>>(mut self, path: P) -> Result<PinnedLink, PinError> {
+        let fd = self.fd.take().ok_or_else(|| PinError::NoFd {
+            name: "link".to_string(),
+        })?;
         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(self.fd, &path_string).map_err(|(_, io_error)| PinError::SyscallError {
+        bpf_pin_object(fd, &path_string).map_err(|(_, io_error)| PinError::SyscallError {
             name: "BPF_OBJ_PIN".to_string(),
             io_error,
         })?;
-        Ok(())
+        Ok(PinnedLink::new(PathBuf::from(path.as_ref()), fd))
     }
 }
 
@@ -148,11 +147,47 @@ impl Link for FdLink {
     }
 
     fn detach(self) -> Result<(), ProgramError> {
-        unsafe { close(self.fd) };
+        // detach is a noop since it consumes self. once self is consumed,
+        // drop will be triggered and the link will be detached.
         Ok(())
     }
 }
 
+impl Drop for FdLink {
+    fn drop(&mut self) {
+        if let Some(fd) = self.fd.take() {
+            // Safety: libc
+            unsafe { close(fd) };
+        }
+    }
+}
+
+/// A pinned file descriptor link.
+///
+/// This link has been pinned to the BPF filesystem. On drop, the file descriptor that backs
+/// this link will be closed. Whether or not the program remains attached is dependent
+/// on the presence of the file in BPFFS.
+#[derive(Debug)]
+pub struct PinnedLink {
+    inner: FdLink,
+    path: PathBuf,
+}
+
+impl PinnedLink {
+    fn new(path: PathBuf, fd: RawFd) -> Self {
+        PinnedLink {
+            inner: FdLink::new(fd),
+            path,
+        }
+    }
+
+    /// Removes the pinned link from the filesystem and returns an [`FdLink`].
+    pub fn unpin(self) -> Result<FdLink, io::Error> {
+        std::fs::remove_file(self.path)?;
+        Ok(self.inner)
+    }
+}
+
 /// The identifier of a `ProgAttachLink`.
 #[derive(Debug, Hash, Eq, PartialEq)]
 pub struct ProgAttachLinkId(RawFd, RawFd, bpf_attach_type);
@@ -220,18 +255,32 @@ macro_rules! define_link_wrapper {
                 $wrapper(b)
             }
         }
+
+        impl From<$wrapper> for $base {
+            fn from(w: $wrapper) -> $base {
+                w.into()
+            }
+        }
     };
 }
 
 pub(crate) use define_link_wrapper;
 
+#[derive(Error, Debug)]
+/// Errors from operations on links.
+pub enum LinkError {
+    /// Invalid link.
+    #[error("Invalid link")]
+    InvalidLink,
+}
+
 #[cfg(test)]
 mod tests {
-    use std::{cell::RefCell, rc::Rc};
+    use std::{cell::RefCell, env, fs::File, mem, os::unix::io::AsRawFd, rc::Rc};
 
-    use crate::programs::{OwnedLink, ProgramError};
+    use crate::{programs::ProgramError, sys::override_syscall};
 
-    use super::{Link, LinkMap};
+    use super::{FdLink, Link, LinkMap};
 
     #[derive(Debug, Hash, Eq, PartialEq)]
     struct TestLinkId(u8, u8);
@@ -363,28 +412,23 @@ mod tests {
     }
 
     #[test]
-    fn test_owned_drop() {
-        let l1 = TestLink::new(1, 2);
-        let l1_detached = Rc::clone(&l1.detached);
-        let l2 = TestLink::new(1, 3);
-        let l2_detached = Rc::clone(&l2.detached);
-
-        {
-            let mut links = LinkMap::new();
-            let id1 = links.insert(l1).unwrap();
-            links.insert(l2).unwrap();
-
-            // manually forget one link and wrap in OwnedLink
-            let _ = OwnedLink {
-                inner: Some(links.forget(id1).unwrap()),
-            };
-
-            // OwnedLink was dropped in the statement above
-            assert!(*l1_detached.borrow() == 1);
-            assert!(*l2_detached.borrow() == 0);
-        };
-
-        assert!(*l1_detached.borrow() == 1);
-        assert!(*l2_detached.borrow() == 1);
+    #[cfg_attr(miri, ignore)]
+    fn test_pin() {
+        let dir = env::temp_dir();
+        let f1 = File::create(dir.join("f1")).expect("unable to create file in tmpdir");
+        let fd_link = FdLink::new(f1.as_raw_fd());
+
+        // leak the fd, it will get closed when our pinned link is dropped
+        mem::forget(f1);
+
+        // override syscall to allow for pin to happen in our tmpdir
+        override_syscall(|_| Ok(0));
+        // create the file that would have happened as a side-effect of a real pin operation
+        File::create(dir.join("f1-pin")).expect("unable to create file in tmpdir");
+        assert!(dir.join("f1-pin").exists());
+
+        let pinned_link = fd_link.pin(dir.join("f1-pin")).expect("pin failed");
+        pinned_link.unpin().expect("unpin failed");
+        assert!(!dir.join("f1-pin").exists());
     }
 }

+ 3 - 3
aya/src/programs/lirc_mode2.rs

@@ -3,7 +3,7 @@ use std::os::unix::prelude::{AsRawFd, RawFd};
 
 use crate::{
     generated::{bpf_attach_type::BPF_LIRC_MODE2, bpf_prog_type::BPF_PROG_TYPE_LIRC_MODE2},
-    programs::{load_program, query, Link, OwnedLink, ProgramData, ProgramError, ProgramInfo},
+    programs::{load_program, query, Link, ProgramData, ProgramError, ProgramInfo},
     sys::{bpf_obj_get_info_by_fd, bpf_prog_attach, bpf_prog_detach, bpf_prog_get_fd_by_id},
 };
 
@@ -85,8 +85,8 @@ impl LircMode2 {
     ///
     /// The link will be detached on `Drop` and the caller is now responsible
     /// for managing its lifetime.
-    pub fn take_link(&mut self, link_id: LircLinkId) -> Result<OwnedLink<LircLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    pub fn take_link(&mut self, link_id: LircLinkId) -> Result<LircLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 
     /// Queries the lirc device for attached programs.

+ 3 - 3
aya/src/programs/lsm.rs

@@ -4,7 +4,7 @@ use crate::{
     obj::btf::{Btf, BtfKind},
     programs::{
         define_link_wrapper, load_program, utils::attach_raw_tracepoint, FdLink, FdLinkId,
-        OwnedLink, ProgramData, ProgramError,
+        ProgramData, ProgramError,
     },
 };
 
@@ -84,8 +84,8 @@ impl Lsm {
     ///
     /// The link will be detached on `Drop` and the caller is now responsible
     /// for managing its lifetime.
-    pub fn take_link(&mut self, link_id: LsmLinkId) -> Result<OwnedLink<LsmLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    pub fn take_link(&mut self, link_id: LsmLinkId) -> Result<LsmLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 }
 

+ 1 - 1
aya/src/programs/mod.rs

@@ -81,8 +81,8 @@ pub use extension::{Extension, ExtensionError};
 pub use fentry::FEntry;
 pub use fexit::FExit;
 pub use kprobe::{KProbe, KProbeError};
+pub use links::Link;
 use links::*;
-pub use links::{Link, OwnedLink};
 pub use lirc_mode2::LircMode2;
 pub use lsm::Lsm;
 use perf_attach::*;

+ 3 - 3
aya/src/programs/perf_event.rs

@@ -14,7 +14,7 @@ use crate::{
     programs::{
         load_program, perf_attach,
         perf_attach::{PerfLink, PerfLinkId},
-        OwnedLink, ProgramData, ProgramError,
+        ProgramData, ProgramError,
     },
     sys::perf_event_open,
 };
@@ -181,7 +181,7 @@ impl PerfEvent {
     ///
     /// The link will be detached on `Drop` and the caller is now responsible
     /// for managing its lifetime.
-    pub fn take_link(&mut self, link_id: PerfLinkId) -> Result<OwnedLink<PerfLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    pub fn take_link(&mut self, link_id: PerfLinkId) -> Result<PerfLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 }

+ 3 - 3
aya/src/programs/raw_trace_point.rs

@@ -5,7 +5,7 @@ use crate::{
     generated::bpf_prog_type::BPF_PROG_TYPE_RAW_TRACEPOINT,
     programs::{
         define_link_wrapper, load_program, utils::attach_raw_tracepoint, FdLink, FdLinkId,
-        OwnedLink, ProgramData, ProgramError,
+        ProgramData, ProgramError,
     },
 };
 
@@ -66,8 +66,8 @@ impl RawTracePoint {
     pub fn take_link(
         &mut self,
         link_id: RawTracePointLinkId,
-    ) -> Result<OwnedLink<RawTracePointLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    ) -> Result<RawTracePointLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 }
 

+ 3 - 6
aya/src/programs/sk_lookup.rs

@@ -2,7 +2,7 @@ use std::os::unix::prelude::{AsRawFd, RawFd};
 
 use crate::{
     generated::{bpf_attach_type::BPF_SK_LOOKUP, bpf_prog_type::BPF_PROG_TYPE_SK_LOOKUP},
-    programs::{define_link_wrapper, load_program, FdLinkId, OwnedLink, ProgramData, ProgramError},
+    programs::{define_link_wrapper, load_program, FdLinkId, ProgramData, ProgramError},
     sys::bpf_link_create,
 };
 
@@ -77,11 +77,8 @@ impl SkLookup {
     ///
     /// The link will be detached on `Drop` and the caller is now responsible
     /// for managing its lifetime.
-    pub fn take_link(
-        &mut self,
-        link_id: SkLookupLinkId,
-    ) -> Result<OwnedLink<SkLookupLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    pub fn take_link(&mut self, link_id: SkLookupLinkId) -> Result<SkLookupLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 
     /// Detaches the program.

+ 4 - 7
aya/src/programs/sk_msg.rs

@@ -3,8 +3,8 @@ use crate::{
     generated::{bpf_attach_type::BPF_SK_MSG_VERDICT, bpf_prog_type::BPF_PROG_TYPE_SK_MSG},
     maps::sock::SocketMap,
     programs::{
-        define_link_wrapper, load_program, OwnedLink, ProgAttachLink, ProgAttachLinkId,
-        ProgramData, ProgramError,
+        define_link_wrapper, load_program, ProgAttachLink, ProgAttachLinkId, ProgramData,
+        ProgramError,
     },
     sys::bpf_prog_attach,
 };
@@ -99,11 +99,8 @@ impl SkMsg {
     ///
     /// The link will be detached on `Drop` and the caller is now responsible
     /// for managing its lifetime.
-    pub fn take_link(
-        &mut self,
-        link_id: SkMsgLinkId,
-    ) -> Result<OwnedLink<SkMsgLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    pub fn take_link(&mut self, link_id: SkMsgLinkId) -> Result<SkMsgLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 }
 

+ 4 - 7
aya/src/programs/sk_skb.rs

@@ -6,8 +6,8 @@ use crate::{
     },
     maps::sock::SocketMap,
     programs::{
-        define_link_wrapper, load_program, OwnedLink, ProgAttachLink, ProgAttachLinkId,
-        ProgramData, ProgramError,
+        define_link_wrapper, load_program, ProgAttachLink, ProgAttachLinkId, ProgramData,
+        ProgramError,
     },
     sys::bpf_prog_attach,
 };
@@ -94,11 +94,8 @@ impl SkSkb {
     ///
     /// The link will be detached on `Drop` and the caller is now responsible
     /// for managing its lifetime.
-    pub fn take_link(
-        &mut self,
-        link_id: SkSkbLinkId,
-    ) -> Result<OwnedLink<SkSkbLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    pub fn take_link(&mut self, link_id: SkSkbLinkId) -> Result<SkSkbLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 }
 

+ 4 - 7
aya/src/programs/sock_ops.rs

@@ -4,8 +4,8 @@ use std::os::unix::io::AsRawFd;
 use crate::{
     generated::{bpf_attach_type::BPF_CGROUP_SOCK_OPS, bpf_prog_type::BPF_PROG_TYPE_SOCK_OPS},
     programs::{
-        define_link_wrapper, load_program, OwnedLink, ProgAttachLink, ProgAttachLinkId,
-        ProgramData, ProgramError,
+        define_link_wrapper, load_program, ProgAttachLink, ProgAttachLinkId, ProgramData,
+        ProgramError,
     },
     sys::bpf_prog_attach,
 };
@@ -86,11 +86,8 @@ impl SockOps {
     ///
     /// The link will be detached on `Drop` and the caller is now responsible
     /// for managing its lifetime.
-    pub fn take_link(
-        &mut self,
-        link_id: SockOpsLinkId,
-    ) -> Result<OwnedLink<SockOpsLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    pub fn take_link(&mut self, link_id: SockOpsLinkId) -> Result<SockOpsLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 }
 

+ 3 - 3
aya/src/programs/socket_filter.rs

@@ -8,7 +8,7 @@ use thiserror::Error;
 
 use crate::{
     generated::{bpf_prog_type::BPF_PROG_TYPE_SOCKET_FILTER, SO_ATTACH_BPF, SO_DETACH_BPF},
-    programs::{load_program, Link, OwnedLink, ProgramData, ProgramError},
+    programs::{load_program, Link, ProgramData, ProgramError},
 };
 
 /// The type returned when attaching a [`SocketFilter`] fails.
@@ -109,8 +109,8 @@ impl SocketFilter {
     pub fn take_link(
         &mut self,
         link_id: SocketFilterLinkId,
-    ) -> Result<OwnedLink<SocketFilterLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    ) -> Result<SocketFilterLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 }
 

+ 3 - 3
aya/src/programs/tc.rs

@@ -10,7 +10,7 @@ use crate::{
     generated::{
         bpf_prog_type::BPF_PROG_TYPE_SCHED_CLS, TC_H_CLSACT, TC_H_MIN_EGRESS, TC_H_MIN_INGRESS,
     },
-    programs::{define_link_wrapper, load_program, Link, OwnedLink, ProgramData, ProgramError},
+    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,
@@ -154,8 +154,8 @@ impl SchedClassifier {
     pub fn take_link(
         &mut self,
         link_id: SchedClassifierLinkId,
-    ) -> Result<OwnedLink<SchedClassifierLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    ) -> Result<SchedClassifierLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 }
 

+ 3 - 3
aya/src/programs/tp_btf.rs

@@ -4,7 +4,7 @@ use crate::{
     obj::btf::{Btf, BtfKind},
     programs::{
         define_link_wrapper, load_program, utils::attach_raw_tracepoint, FdLink, FdLinkId,
-        OwnedLink, ProgramData, ProgramError,
+        ProgramData, ProgramError,
     },
 };
 
@@ -85,8 +85,8 @@ impl BtfTracePoint {
     pub fn take_link(
         &mut self,
         link_id: BtfTracePointLinkId,
-    ) -> Result<OwnedLink<BtfTracePointLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    ) -> Result<BtfTracePointLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 }
 

+ 3 - 6
aya/src/programs/trace_point.rs

@@ -7,7 +7,7 @@ use crate::{
     programs::{
         define_link_wrapper, load_program,
         perf_attach::{perf_attach, PerfLink, PerfLinkId},
-        OwnedLink, ProgramData, ProgramError,
+        ProgramData, ProgramError,
     },
     sys::perf_event_open_trace_point,
 };
@@ -99,11 +99,8 @@ impl TracePoint {
     ///
     /// The link will be detached on `Drop` and the caller is now responsible
     /// for managing its lifetime.
-    pub fn take_link(
-        &mut self,
-        link_id: TracePointLinkId,
-    ) -> Result<OwnedLink<TracePointLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    pub fn take_link(&mut self, link_id: TracePointLinkId) -> Result<TracePointLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 }
 

+ 3 - 6
aya/src/programs/uprobe.rs

@@ -19,7 +19,7 @@ use crate::{
         define_link_wrapper, load_program,
         perf_attach::{PerfLink, PerfLinkId},
         probe::{attach, ProbeKind},
-        OwnedLink, ProgramData, ProgramError,
+        ProgramData, ProgramError,
     },
 };
 
@@ -133,11 +133,8 @@ impl UProbe {
     ///
     /// The link will be detached on `Drop` and the caller is now responsible
     /// for managing its lifetime.
-    pub fn take_link(
-        &mut self,
-        link_id: UProbeLinkId,
-    ) -> Result<OwnedLink<UProbeLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    pub fn take_link(&mut self, link_id: UProbeLinkId) -> Result<UProbeLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 }
 

+ 18 - 6
aya/src/programs/xdp.rs

@@ -1,7 +1,7 @@
 //! eXpress Data Path (XDP) programs.
 use bitflags;
 use libc::if_nametoindex;
-use std::{ffi::CString, hash::Hash, io, mem, os::unix::io::RawFd};
+use std::{convert::TryFrom, ffi::CString, hash::Hash, io, mem, os::unix::io::RawFd};
 use thiserror::Error;
 
 use crate::{
@@ -12,7 +12,7 @@ use crate::{
         XDP_FLAGS_UPDATE_IF_NOEXIST,
     },
     programs::{
-        define_link_wrapper, load_program, FdLink, Link, OwnedLink, ProgramData, ProgramError,
+        define_link_wrapper, load_program, FdLink, Link, LinkError, ProgramData, ProgramError,
     },
     sys::{bpf_link_create, bpf_link_update, kernel_version, netlink_set_xdp_fd},
 };
@@ -137,18 +137,18 @@ impl Xdp {
     ///
     /// The link will be detached on `Drop` and the caller is now responsible
     /// for managing its lifetime.
-    pub fn take_link(&mut self, link_id: XdpLinkId) -> Result<OwnedLink<XdpLink>, ProgramError> {
-        Ok(OwnedLink::new(self.data.take_link(link_id)?))
+    pub fn take_link(&mut self, link_id: XdpLinkId) -> Result<XdpLink, ProgramError> {
+        self.data.take_link(link_id)
     }
 
     /// Atomically replaces the program referenced by the provided link.
     ///
     /// Ownership of the link will transfer to this program.
-    pub fn attach_to_link(&mut self, link: OwnedLink<XdpLink>) -> Result<XdpLinkId, ProgramError> {
+    pub fn attach_to_link(&mut self, link: XdpLink) -> Result<XdpLinkId, ProgramError> {
         let prog_fd = self.data.fd_or_err()?;
         match &link.0 {
             XdpLinkInner::FdLink(fd_link) => {
-                let link_fd = fd_link.fd;
+                let link_fd = fd_link.fd.unwrap();
                 bpf_link_update(link_fd, prog_fd, None, 0).map_err(|(_, io_error)| {
                     ProgramError::SyscallError {
                         call: "bpf_link_update".to_string(),
@@ -238,6 +238,18 @@ impl Link for XdpLinkInner {
     }
 }
 
+impl TryFrom<XdpLink> for FdLink {
+    type Error = LinkError;
+
+    fn try_from(value: XdpLink) -> Result<Self, Self::Error> {
+        if let XdpLinkInner::FdLink(fd) = value.0 {
+            Ok(fd)
+        } else {
+            Err(LinkError::InvalidLink)
+        }
+    }
+}
+
 define_link_wrapper!(
     /// The link used by [Xdp] programs.
     XdpLink,

+ 31 - 1
test/integration-test/src/tests/load.rs

@@ -3,7 +3,7 @@ use std::{process::Command, thread, time};
 use aya::{
     include_bytes_aligned,
     maps::{Array, MapRefMut},
-    programs::{TracePoint, Xdp, XdpFlags},
+    programs::{links::FdLink, TracePoint, Xdp, XdpFlags},
     Bpf,
 };
 
@@ -99,3 +99,33 @@ fn unload() -> anyhow::Result<()> {
     assert_loaded(false);
     Ok(())
 }
+
+#[integration_test]
+fn pin_link() -> anyhow::Result<()> {
+    let bytes = include_bytes_aligned!("../../../../target/bpfel-unknown-none/debug/test");
+    let mut bpf = Bpf::load(bytes)?;
+    let prog: &mut Xdp = bpf.program_mut("test_unload").unwrap().try_into().unwrap();
+    prog.load().unwrap();
+    let link_id = prog.attach("lo", XdpFlags::default()).unwrap();
+    let link = prog.take_link(link_id)?;
+    assert_loaded(true);
+
+    let fd_link: FdLink = link.try_into()?;
+    let pinned = fd_link.pin("/sys/fs/bpf/aya-xdp-test-lo")?;
+
+    // because of the pin, the program is still attached
+    prog.unload()?;
+    assert_loaded(true);
+
+    // delete the pin, but the program is still attached
+    let new_link = pinned.unpin()?;
+    println!("third assert");
+    assert_loaded(true);
+
+    // finally when new_link is dropped we're detached
+    drop(new_link);
+    println!("final assert");
+    assert_loaded(false);
+
+    Ok(())
+}