Browse Source

Merge pull request #343 from dave-tucker/pinning-redux

Small Pinning API improvements
Dave Tucker 2 years ago
parent
commit
8e6c9ad0d2
6 changed files with 195 additions and 92 deletions
  1. 12 2
      aya/src/bpf.rs
  2. 1 0
      aya/src/lib.rs
  3. 25 31
      aya/src/maps/mod.rs
  4. 35 0
      aya/src/pin.rs
  5. 28 1
      aya/src/programs/links.rs
  6. 94 58
      aya/src/programs/mod.rs

+ 12 - 2
aya/src/bpf.rs

@@ -400,7 +400,10 @@ impl<'a> BpfLoader<'a> {
                         }
                         Err(_) => {
                             let fd = map.create(&name)?;
-                            map.pin(&name, path)?;
+                            map.pin(&name, path).map_err(|error| MapError::PinError {
+                                name: name.to_string(),
+                                error,
+                            })?;
                             fd
                         }
                     }
@@ -774,12 +777,19 @@ impl Bpf {
     /// # Examples
     /// ```no_run
     /// # use std::path::Path;
+    /// # #[derive(thiserror::Error, Debug)]
+    /// # enum Error {
+    /// #     #[error(transparent)]
+    /// #     Bpf(#[from] aya::BpfError),
+    /// #     #[error(transparent)]
+    /// #     Pin(#[from] aya::pin::PinError)
+    /// # }
     /// # let mut bpf = aya::Bpf::load(&[])?;
     /// # let pin_path = Path::new("/tmp/pin_path");
     /// for (_, program) in bpf.programs_mut() {
     ///     program.pin(pin_path)?;
     /// }
-    /// # Ok::<(), aya::BpfError>(())
+    /// # Ok::<(), Error>(())
     /// ```
     pub fn programs_mut(&mut self) -> impl Iterator<Item = (&str, &mut Program)> {
         self.programs.iter_mut().map(|(s, p)| (s.as_str(), p))

+ 1 - 0
aya/src/lib.rs

@@ -48,6 +48,7 @@ mod bpf;
 mod generated;
 pub mod maps;
 mod obj;
+pub mod pin;
 pub mod programs;
 mod sys;
 pub mod util;

+ 25 - 31
aya/src/maps/mod.rs

@@ -51,6 +51,7 @@ use thiserror::Error;
 use crate::{
     generated::bpf_map_type,
     obj,
+    pin::PinError,
     sys::{bpf_create_map, bpf_get_object, bpf_map_get_next_key, bpf_pin_object, kernel_version},
     util::nr_cpus,
     Pod,
@@ -119,13 +120,6 @@ pub enum MapError {
         name: String,
     },
 
-    /// The map has already been pinned
-    #[error("the map `{name}` has already been pinned")]
-    AlreadyPinned {
-        /// Map name
-        name: String,
-    },
-
     /// Failed to create map
     #[error("failed to create map `{name}` with code {code}")]
     CreateError {
@@ -138,18 +132,6 @@ pub enum MapError {
         io_error: io::Error,
     },
 
-    /// Failed to pin map
-    #[error("failed to pin map `{name}` with code {code}")]
-    PinError {
-        /// Map Name
-        name: String,
-        /// Error code
-        code: libc::c_long,
-        #[source]
-        /// Original io::Error
-        io_error: io::Error,
-    },
-
     /// Invalid key size
     #[error("invalid key size {size}, expected {expected}")]
     InvalidKeySize {
@@ -214,6 +196,16 @@ pub enum MapError {
         /// Map name
         name: String,
     },
+
+    /// Could not pin map by name
+    #[error("map `{name}` requested pinning by name. pinning failed")]
+    PinError {
+        /// The map name
+        name: String,
+        /// The reason for the failure
+        #[source]
+        error: PinError,
+    },
 }
 
 /// A map file descriptor.
@@ -316,11 +308,12 @@ impl Map {
                 })
             }
         };
-        let fd = bpf_get_object(&path_string).map_err(|(code, io_error)| MapError::PinError {
-            name: name.into(),
-            code,
-            io_error,
-        })? as RawFd;
+        let fd =
+            bpf_get_object(&path_string).map_err(|(code, io_error)| MapError::SyscallError {
+                call: "BPF_OBJ_GET".to_string(),
+                code,
+                io_error,
+            })? as RawFd;
 
         self.fd = Some(fd);
 
@@ -336,20 +329,21 @@ impl Map {
         self.fd.ok_or(MapError::NotCreated)
     }
 
-    pub(crate) fn pin<P: AsRef<Path>>(&mut self, name: &str, path: P) -> Result<(), MapError> {
+    pub(crate) fn pin<P: AsRef<Path>>(&mut self, name: &str, path: P) -> Result<(), PinError> {
         if self.pinned {
-            return Err(MapError::AlreadyPinned { name: name.into() });
+            return Err(PinError::AlreadyPinned { name: name.into() });
         }
         let map_path = path.as_ref().join(name);
-        let fd = self.fd_or_err()?;
+        let fd = self.fd.ok_or(PinError::NoFd {
+            name: name.to_string(),
+        })?;
         let path_string = CString::new(map_path.to_string_lossy().into_owned()).map_err(|e| {
-            MapError::InvalidPinPath {
+            PinError::InvalidPinPath {
                 error: e.to_string(),
             }
         })?;
-        bpf_pin_object(fd, &path_string).map_err(|(code, io_error)| MapError::SyscallError {
-            call: "BPF_OBJ_PIN".to_string(),
-            code,
+        bpf_pin_object(fd, &path_string).map_err(|(_, io_error)| PinError::SyscallError {
+            name: "BPF_OBJ_GET".to_string(),
             io_error,
         })?;
         self.pinned = true;

+ 35 - 0
aya/src/pin.rs

@@ -0,0 +1,35 @@
+//! Pinning BPF objects to the BPF filesystem.
+use std::io;
+use thiserror::Error;
+
+/// An error ocurred working with a pinned BPF object.
+#[derive(Error, Debug)]
+pub enum PinError {
+    /// The object has already been pinned.
+    #[error("the BPF object `{name}` has already been pinned")]
+    AlreadyPinned {
+        /// Object name.
+        name: String,
+    },
+    /// The object FD is not known by Aya.
+    #[error("the BPF object `{name}`'s FD is not known")]
+    NoFd {
+        /// Object name.
+        name: String,
+    },
+    /// The path for the BPF object is not valid.
+    #[error("invalid pin path `{error}`")]
+    InvalidPinPath {
+        /// The error message.
+        error: String,
+    },
+    /// An error ocurred making a syscall.
+    #[error("{name} failed")]
+    SyscallError {
+        /// The syscall name.
+        name: String,
+        /// The [`io::Error`] returned by the syscall.
+        #[source]
+        io_error: io::Error,
+    },
+}

+ 28 - 1
aya/src/programs/links.rs

@@ -4,11 +4,18 @@ use libc::{close, dup};
 use std::{
     borrow::Borrow,
     collections::{hash_map::Entry, HashMap},
+    ffi::CString,
     ops::Deref,
     os::unix::prelude::RawFd,
+    path::Path,
 };
 
-use crate::{generated::bpf_attach_type, programs::ProgramError, sys::bpf_prog_detach};
+use crate::{
+    generated::bpf_attach_type,
+    pin::PinError,
+    programs::ProgramError,
+    sys::{bpf_pin_object, bpf_prog_detach},
+};
 
 /// A Link.
 pub trait Link: std::fmt::Debug + 'static {
@@ -111,6 +118,26 @@ impl FdLink {
     pub(crate) fn new(fd: RawFd) -> FdLink {
         FdLink { fd }
     }
+
+    /// Pins the FdLink to a BPF filesystem.
+    ///
+    /// 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> {
+        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 {
+            name: "BPF_OBJ_PIN".to_string(),
+            io_error,
+        })?;
+        Ok(())
+    }
 }
 
 impl Link for FdLink {

+ 94 - 58
aya/src/programs/mod.rs

@@ -106,6 +106,7 @@ use crate::{
     generated::{bpf_attach_type, bpf_prog_info, bpf_prog_type},
     maps::MapError,
     obj::{self, btf::BtfError, Function, KernelVersion},
+    pin::PinError,
     sys::{
         bpf_get_object, bpf_load_program, bpf_obj_get_info_by_fd, bpf_pin_object,
         bpf_prog_get_fd_by_id, bpf_prog_query, retry_with_verifier_logs, BpfLoadProgramAttrs,
@@ -163,13 +164,6 @@ pub enum ProgramError {
     #[error("unexpected program type")]
     UnexpectedProgramType,
 
-    /// Invalid pin path
-    #[error("invalid pin path `{error}`")]
-    InvalidPinPath {
-        /// the error message
-        error: String,
-    },
-
     /// A map error occurred while loading or attaching a program.
     #[error(transparent)]
     MapError(#[from] MapError),
@@ -307,31 +301,31 @@ impl Program {
     }
 
     /// Pin the program to the provided path
-    pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<(), ProgramError> {
+    pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<(), PinError> {
         match self {
-            Program::KProbe(p) => p.data.pin(path),
-            Program::UProbe(p) => p.data.pin(path),
-            Program::TracePoint(p) => p.data.pin(path),
-            Program::SocketFilter(p) => p.data.pin(path),
-            Program::Xdp(p) => p.data.pin(path),
-            Program::SkMsg(p) => p.data.pin(path),
-            Program::SkSkb(p) => p.data.pin(path),
-            Program::SockOps(p) => p.data.pin(path),
-            Program::SchedClassifier(p) => p.data.pin(path),
-            Program::CgroupSkb(p) => p.data.pin(path),
-            Program::CgroupSysctl(p) => p.data.pin(path),
-            Program::CgroupSockopt(p) => p.data.pin(path),
-            Program::LircMode2(p) => p.data.pin(path),
-            Program::PerfEvent(p) => p.data.pin(path),
-            Program::RawTracePoint(p) => p.data.pin(path),
-            Program::Lsm(p) => p.data.pin(path),
-            Program::BtfTracePoint(p) => p.data.pin(path),
-            Program::FEntry(p) => p.data.pin(path),
-            Program::FExit(p) => p.data.pin(path),
-            Program::Extension(p) => p.data.pin(path),
-            Program::CgroupSockAddr(p) => p.data.pin(path),
-            Program::SkLookup(p) => p.data.pin(path),
-            Program::CgroupSock(p) => p.data.pin(path),
+            Program::KProbe(p) => p.pin(path),
+            Program::UProbe(p) => p.pin(path),
+            Program::TracePoint(p) => p.pin(path),
+            Program::SocketFilter(p) => p.pin(path),
+            Program::Xdp(p) => p.pin(path),
+            Program::SkMsg(p) => p.pin(path),
+            Program::SkSkb(p) => p.pin(path),
+            Program::SockOps(p) => p.pin(path),
+            Program::SchedClassifier(p) => p.pin(path),
+            Program::CgroupSkb(p) => p.pin(path),
+            Program::CgroupSysctl(p) => p.pin(path),
+            Program::CgroupSockopt(p) => p.pin(path),
+            Program::LircMode2(p) => p.pin(path),
+            Program::PerfEvent(p) => p.pin(path),
+            Program::RawTracePoint(p) => p.pin(path),
+            Program::Lsm(p) => p.pin(path),
+            Program::BtfTracePoint(p) => p.pin(path),
+            Program::FEntry(p) => p.pin(path),
+            Program::FExit(p) => p.pin(path),
+            Program::Extension(p) => p.pin(path),
+            Program::CgroupSockAddr(p) => p.pin(path),
+            Program::SkLookup(p) => p.pin(path),
+            Program::CgroupSock(p) => p.pin(path),
         }
     }
 
@@ -441,23 +435,6 @@ impl<T: Link> ProgramData<T> {
         self.fd.ok_or(ProgramError::NotLoaded)
     }
 
-    pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<(), ProgramError> {
-        let fd = self.fd_or_err()?;
-        let path_string =
-            CString::new(path.as_ref().to_string_lossy().into_owned()).map_err(|e| {
-                MapError::InvalidPinPath {
-                    error: e.to_string(),
-                }
-            })?;
-        bpf_pin_object(fd, &path_string).map_err(|(_code, io_error)| {
-            ProgramError::SyscallError {
-                call: "BPF_OBJ_PIN".to_string(),
-                io_error,
-            }
-        })?;
-        Ok(())
-    }
-
     pub(crate) fn take_link(&mut self, link_id: T::Id) -> Result<T, ProgramError> {
         self.links.forget(link_id)
     }
@@ -472,6 +449,29 @@ fn unload_program<T: Link>(data: &mut ProgramData<T>) -> Result<(), ProgramError
     Ok(())
 }
 
+fn pin_program<T: Link, P: AsRef<Path>>(
+    data: &mut ProgramData<T>,
+    path: P,
+) -> Result<(), PinError> {
+    let fd = data.fd.ok_or(PinError::NoFd {
+        name: data
+            .name
+            .as_ref()
+            .unwrap_or(&"<unknown program>".to_string())
+            .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(fd, &path_string).map_err(|(_, io_error)| PinError::SyscallError {
+        name: "BPF_OBJ_PIN".to_string(),
+        io_error,
+    })?;
+    Ok(())
+}
+
 fn load_program<T: Link>(
     prog_type: bpf_prog_type,
     data: &mut ProgramData<T>,
@@ -673,6 +673,50 @@ impl_fd!(
     CgroupSock,
 );
 
+macro_rules! impl_program_pin{
+    ($($struct_name:ident),+ $(,)?) => {
+        $(
+            impl $struct_name {
+                /// Pins the program to a BPF filesystem.
+                ///
+                /// When a BPF object is pinned to a BPF filesystem it will remain loaded after
+                /// Aya has unloaded the program.
+                /// To remove the program, 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>>(&mut self, path: P) -> Result<(), PinError> {
+                    pin_program(&mut self.data, path)
+                }
+            }
+        )+
+    }
+}
+
+impl_program_pin!(
+    KProbe,
+    UProbe,
+    TracePoint,
+    SocketFilter,
+    Xdp,
+    SkMsg,
+    SkSkb,
+    SchedClassifier,
+    CgroupSkb,
+    CgroupSysctl,
+    CgroupSockopt,
+    LircMode2,
+    PerfEvent,
+    Lsm,
+    RawTracePoint,
+    BtfTracePoint,
+    FEntry,
+    FExit,
+    Extension,
+    CgroupSockAddr,
+    SkLookup,
+    SockOps,
+    CgroupSock,
+);
+
 macro_rules! impl_try_from_program {
     ($($ty:ident),+ $(,)?) => {
         $(
@@ -770,17 +814,10 @@ impl ProgramInfo {
 
     /// Loads a program from a pinned path in bpffs.
     pub fn from_pinned<P: AsRef<Path>>(path: P) -> Result<ProgramInfo, ProgramError> {
-        let path_string = match CString::new(path.as_ref().to_str().unwrap()) {
-            Ok(s) => s,
-            Err(e) => {
-                return Err(ProgramError::InvalidPinPath {
-                    error: e.to_string(),
-                })
-            }
-        };
+        let path_string = CString::new(path.as_ref().to_str().unwrap()).unwrap();
         let fd =
             bpf_get_object(&path_string).map_err(|(_, io_error)| ProgramError::SyscallError {
-                call: "bpf_obj_get".to_owned(),
+                call: "BPF_OBJ_GET".to_owned(),
                 io_error,
             })? as RawFd;
 
@@ -788,7 +825,6 @@ impl ProgramInfo {
             call: "bpf_obj_get_info_by_fd".to_owned(),
             io_error,
         })?;
-
         unsafe {
             libc::close(fd);
         }