1 use std::fmt::Debug;
2 use std::os::unix::io::AsFd;
3 use std::os::unix::io::BorrowedFd;
4 use std::path::Path;
5 use std::path::PathBuf;
6 use std::ptr::NonNull;
7 
8 use crate::util;
9 use crate::util::validate_bpf_ret;
10 use crate::AsRawLibbpf;
11 use crate::ErrorExt as _;
12 use crate::Program;
13 use crate::Result;
14 
15 /// Represents an attached [`Program`].
16 ///
17 /// This struct is used to model ownership. The underlying program will be detached
18 /// when this object is dropped if nothing else is holding a reference count.
19 #[derive(Debug)]
20 pub struct Link {
21     ptr: NonNull<libbpf_sys::bpf_link>,
22 }
23 
24 impl Link {
25     /// Create a new [`Link`] from a [`libbpf_sys::bpf_link`].
26     ///
27     /// # Safety
28     ///
29     /// `ptr` must point to a correctly initialized [`libbpf_sys::bpf_link`].
new(ptr: NonNull<libbpf_sys::bpf_link>) -> Self30     pub(crate) unsafe fn new(ptr: NonNull<libbpf_sys::bpf_link>) -> Self {
31         Link { ptr }
32     }
33 
34     /// Create link from BPF FS file.
open<P: AsRef<Path>>(path: P) -> Result<Self>35     pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
36         let path_c = util::path_to_cstring(path)?;
37         let path_ptr = path_c.as_ptr();
38         let ptr = unsafe { libbpf_sys::bpf_link__open(path_ptr) };
39         let ptr = validate_bpf_ret(ptr).context("failed to open link")?;
40         let slf = unsafe { Self::new(ptr) };
41         Ok(slf)
42     }
43 
44     /// Takes ownership from pointer.
45     ///
46     /// # Safety
47     ///
48     /// It is not safe to manipulate `ptr` after this operation.
from_ptr(ptr: NonNull<libbpf_sys::bpf_link>) -> Self49     pub unsafe fn from_ptr(ptr: NonNull<libbpf_sys::bpf_link>) -> Self {
50         unsafe { Self::new(ptr) }
51     }
52 
53     /// Replace the underlying prog with `prog`.
update_prog(&mut self, prog: &Program<'_>) -> Result<()>54     pub fn update_prog(&mut self, prog: &Program<'_>) -> Result<()> {
55         let ret =
56             unsafe { libbpf_sys::bpf_link__update_program(self.ptr.as_ptr(), prog.ptr.as_ptr()) };
57         util::parse_ret(ret)
58     }
59 
60     /// Release "ownership" of underlying BPF resource (typically, a BPF program
61     /// attached to some BPF hook, e.g., tracepoint, kprobe, etc). Disconnected
62     /// links, when destructed through bpf_link__destroy() call won't attempt to
63     /// detach/unregistered that BPF resource. This is useful in situations where,
64     /// say, attached BPF program has to outlive userspace program that attached it
65     /// in the system. Depending on type of BPF program, though, there might be
66     /// additional steps (like pinning BPF program in BPF FS) necessary to ensure
67     /// exit of userspace program doesn't trigger automatic detachment and clean up
68     /// inside the kernel.
disconnect(&mut self)69     pub fn disconnect(&mut self) {
70         unsafe { libbpf_sys::bpf_link__disconnect(self.ptr.as_ptr()) }
71     }
72 
73     /// [Pin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs)
74     /// this link to bpffs.
pin<P: AsRef<Path>>(&mut self, path: P) -> Result<()>75     pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
76         let path_c = util::path_to_cstring(path)?;
77         let path_ptr = path_c.as_ptr();
78 
79         let ret = unsafe { libbpf_sys::bpf_link__pin(self.ptr.as_ptr(), path_ptr) };
80         util::parse_ret(ret)
81     }
82 
83     /// [Unpin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs)
84     /// from bpffs
unpin(&mut self) -> Result<()>85     pub fn unpin(&mut self) -> Result<()> {
86         let ret = unsafe { libbpf_sys::bpf_link__unpin(self.ptr.as_ptr()) };
87         util::parse_ret(ret)
88     }
89 
90     /// Returns path to BPF FS file or `None` if not pinned.
pin_path(&self) -> Option<PathBuf>91     pub fn pin_path(&self) -> Option<PathBuf> {
92         let path_ptr = unsafe { libbpf_sys::bpf_link__pin_path(self.ptr.as_ptr()) };
93         if path_ptr.is_null() {
94             return None;
95         }
96 
97         let path = match util::c_ptr_to_string(path_ptr) {
98             Ok(p) => p,
99             Err(_) => return None,
100         };
101 
102         Some(PathBuf::from(path.as_str()))
103     }
104 
105     /// Detach the link.
detach(&self) -> Result<()>106     pub fn detach(&self) -> Result<()> {
107         let ret = unsafe { libbpf_sys::bpf_link__detach(self.ptr.as_ptr()) };
108         util::parse_ret(ret)
109     }
110 }
111 
112 impl AsRawLibbpf for Link {
113     type LibbpfType = libbpf_sys::bpf_link;
114 
115     /// Retrieve the underlying [`libbpf_sys::bpf_link`].
as_libbpf_object(&self) -> NonNull<Self::LibbpfType>116     fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
117         self.ptr
118     }
119 }
120 
121 // SAFETY: `bpf_link` objects can safely be sent to a different thread.
122 unsafe impl Send for Link {}
123 
124 impl AsFd for Link {
125     #[inline]
as_fd(&self) -> BorrowedFd<'_>126     fn as_fd(&self) -> BorrowedFd<'_> {
127         let fd = unsafe { libbpf_sys::bpf_link__fd(self.ptr.as_ptr()) };
128         // SAFETY: `bpf_link__fd` always returns a valid fd and the underlying
129         //         libbpf object is not destroyed until the object is dropped,
130         //         which means the fd remains valid as well.
131         unsafe { BorrowedFd::borrow_raw(fd) }
132     }
133 }
134 
135 impl Drop for Link {
drop(&mut self)136     fn drop(&mut self) {
137         let _ = unsafe { libbpf_sys::bpf_link__destroy(self.ptr.as_ptr()) };
138     }
139 }
140