1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use std::fmt::Debug;
use std::os::unix::io::AsFd;
use std::os::unix::io::BorrowedFd;
use std::path::Path;
use std::path::PathBuf;
use std::ptr::NonNull;

use crate::libbpf_sys;
use crate::util;
use crate::Program;
use crate::Result;

/// Represents an attached [`Program`].
///
/// This struct is used to model ownership. The underlying program will be detached
/// when this object is dropped if nothing else is holding a reference count.
#[derive(Debug)]
pub struct Link {
    ptr: NonNull<libbpf_sys::bpf_link>,
}

impl Link {
    /// Create a new [`Link`] from a [`libbpf_sys::bpf_link`].
    ///
    /// # Safety
    ///
    /// `ptr` must point to a correctly initialized [`libbpf_sys::bpf_link`].
    pub(crate) unsafe fn new(ptr: NonNull<libbpf_sys::bpf_link>) -> Self {
        Link { ptr }
    }

    /// Create link from BPF FS file.
    pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
        let path_c = util::path_to_cstring(path)?;
        let path_ptr = path_c.as_ptr();
        util::create_bpf_entity_checked(|| unsafe { libbpf_sys::bpf_link__open(path_ptr) })
            .map(|ptr| unsafe { Self::new(ptr) })
    }

    /// Takes ownership from pointer.
    ///
    /// # Safety
    ///
    /// It is not safe to manipulate `ptr` after this operation.
    pub unsafe fn from_ptr(ptr: NonNull<libbpf_sys::bpf_link>) -> Self {
        unsafe { Self::new(ptr) }
    }

    /// Replace the underlying prog with `prog`.
    pub fn update_prog(&mut self, prog: &Program) -> Result<()> {
        let ret =
            unsafe { libbpf_sys::bpf_link__update_program(self.ptr.as_ptr(), prog.ptr.as_ptr()) };
        util::parse_ret(ret)
    }

    /// Release "ownership" of underlying BPF resource (typically, a BPF program
    /// attached to some BPF hook, e.g., tracepoint, kprobe, etc). Disconnected
    /// links, when destructed through bpf_link__destroy() call won't attempt to
    /// detach/unregistered that BPF resource. This is useful in situations where,
    /// say, attached BPF program has to outlive userspace program that attached it
    /// in the system. Depending on type of BPF program, though, there might be
    /// additional steps (like pinning BPF program in BPF FS) necessary to ensure
    /// exit of userspace program doesn't trigger automatic detachment and clean up
    /// inside the kernel.
    pub fn disconnect(&mut self) {
        unsafe { libbpf_sys::bpf_link__disconnect(self.ptr.as_ptr()) }
    }

    /// [Pin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs)
    /// this link to bpffs.
    pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
        let path_c = util::path_to_cstring(path)?;
        let path_ptr = path_c.as_ptr();

        let ret = unsafe { libbpf_sys::bpf_link__pin(self.ptr.as_ptr(), path_ptr) };
        util::parse_ret(ret)
    }

    /// [Unpin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs)
    /// from bpffs
    pub fn unpin(&mut self) -> Result<()> {
        let ret = unsafe { libbpf_sys::bpf_link__unpin(self.ptr.as_ptr()) };
        util::parse_ret(ret)
    }

    /// Returns path to BPF FS file or `None` if not pinned.
    pub fn pin_path(&self) -> Option<PathBuf> {
        let path_ptr = unsafe { libbpf_sys::bpf_link__pin_path(self.ptr.as_ptr()) };
        if path_ptr.is_null() {
            return None;
        }

        let path = match util::c_ptr_to_string(path_ptr) {
            Ok(p) => p,
            Err(_) => return None,
        };

        Some(PathBuf::from(path.as_str()))
    }

    /// Detach the link.
    pub fn detach(&self) -> Result<()> {
        let ret = unsafe { libbpf_sys::bpf_link__detach(self.ptr.as_ptr()) };
        util::parse_ret(ret)
    }
}

impl AsFd for Link {
    #[inline]
    fn as_fd(&self) -> BorrowedFd<'_> {
        let fd = unsafe { libbpf_sys::bpf_link__fd(self.ptr.as_ptr()) };
        // SAFETY: `bpf_link__fd` always returns a valid fd and the underlying
        //         libbpf object is not destroyed until the object is dropped,
        //         which means the fd remains valid as well.
        unsafe { BorrowedFd::borrow_raw(fd) }
    }
}

impl Drop for Link {
    fn drop(&mut self) {
        let _ = unsafe { libbpf_sys::bpf_link__destroy(self.ptr.as_ptr()) };
    }
}