Bladeren bron

aya: refactor tc code a bit and add docs

Alessandro Decina 3 jaren geleden
bovenliggende
commit
ad58e171ff
4 gewijzigde bestanden met toevoegingen van 129 en 96 verwijderingen
  1. 2 2
      aya/src/programs/mod.rs
  2. 85 37
      aya/src/programs/tc.rs
  3. 27 31
      aya/src/sys/netlink.rs
  4. 15 26
      aya/src/util.rs

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

@@ -44,7 +44,7 @@ mod sk_msg;
 mod sk_skb;
 mod sock_ops;
 mod socket_filter;
-mod tc;
+pub mod tc;
 mod trace_point;
 mod uprobe;
 mod xdp;
@@ -61,7 +61,7 @@ pub use sk_msg::SkMsg;
 pub use sk_skb::{SkSkb, SkSkbKind};
 pub use sock_ops::SockOps;
 pub use socket_filter::{SocketFilter, SocketFilterError};
-pub use tc::{SchedClassifier, TcAttachPoint, TcError};
+pub use tc::{SchedClassifier, TcAttachType, TcError};
 pub use trace_point::{TracePoint, TracePointError};
 pub use uprobe::{UProbe, UProbeError};
 pub use xdp::{Xdp, XdpError, XdpFlags};

+ 85 - 37
aya/src/programs/tc.rs

@@ -1,25 +1,63 @@
+//! Network traffic control programs.
 use thiserror::Error;
 
 use std::{io, os::unix::io::RawFd};
 
 use crate::{
     generated::{
-        TC_H_CLSACT, TC_H_MIN_INGRESS, TC_H_MIN_EGRESS,
-        bpf_prog_type::BPF_PROG_TYPE_SCHED_CLS,
+        bpf_prog_type::BPF_PROG_TYPE_SCHED_CLS, TC_H_CLSACT, TC_H_MIN_EGRESS, TC_H_MIN_INGRESS,
     },
-    programs::{Link, LinkRef, load_program, ProgramData, ProgramError},
+    programs::{load_program, Link, LinkRef, ProgramData, ProgramError},
     sys::{netlink_qdisc_add_clsact, netlink_qdisc_attach, netlink_qdisc_detach},
     util::{ifindex_from_ifname, tc_handler_make},
 };
 
+/// Traffic control attach type.
 #[derive(Debug, Clone, Copy)]
-#[repr(u32)]
-pub enum TcAttachPoint {
-    Ingress = TC_H_MIN_INGRESS,
-    Egress = TC_H_MIN_EGRESS,
-    Custom,
+pub enum TcAttachType {
+    /// Attach to ingress.
+    Ingress,
+    /// Attach to egress.
+    Egress,
+    /// Attach to custom parent.
+    Custom(u32),
 }
 
+/// A network traffic control classifier.
+///
+/// [`SchedClassifier`] programs can be used to inspect, filter or redirect
+/// network packets in both ingress and egress. They are executed as part of the
+/// linux network traffic control system. See
+/// [https://man7.org/linux/man-pages/man8/tc-bpf.8.html](https://man7.org/linux/man-pages/man8/tc-bpf.8.html).
+///
+/// # Example
+///
+/// ```no_run
+/// ##[derive(Debug, thiserror::Error)]
+/// # enum Error {
+/// #     #[error(transparent)]
+/// #     IO(#[from] std::io::Error),
+/// #     #[error(transparent)]
+/// #     Map(#[from] aya::maps::MapError),
+/// #     #[error(transparent)]
+/// #     Program(#[from] aya::programs::ProgramError),
+/// #     #[error(transparent)]
+/// #     Bpf(#[from] aya::BpfError)
+/// # }
+/// # let mut bpf = aya::Bpf::load(&[], None)?;
+/// use std::convert::TryInto;
+/// use aya::programs::{tc, SchedClassifier, TcAttachType};
+///
+/// // the clsact qdisc needs to be added before SchedClassifier programs can be
+/// // attached
+/// tc::qdisc_add_clsact("eth0")?;
+///
+/// let prog: &mut SchedClassifier = bpf.program_mut("redirect_ingress")?.try_into()?;
+/// prog.load()?;
+/// prog.attach("eth0", TcAttachType::Ingress)?;
+///
+/// # Ok::<(), Error>(())
+/// ```
 #[derive(Debug)]
 pub struct SchedClassifier {
     pub(crate) data: ProgramData,
@@ -39,21 +77,17 @@ pub enum TcError {
 #[derive(Debug)]
 struct TcLink {
     if_index: i32,
-    attach_point: TcAttachPoint,
+    attach_type: TcAttachType,
     prog_fd: Option<RawFd>,
     priority: u32,
 }
 
-impl TcAttachPoint {
-    pub fn tcm_parent(&self, parent: u32) -> Result<u32, io::Error> {
-        match *self {
-            TcAttachPoint::Custom => {
-                if parent == 0 {
-                    return Err(io::Error::new(io::ErrorKind::Other, "Parent must be non-zero for Custom attach points"));
-                }
-                Ok(parent)
-            }
-            _ => Ok(tc_handler_make(TC_H_CLSACT, (*self).clone() as u32))
+impl TcAttachType {
+    pub(crate) fn parent(&self) -> u32 {
+        match self {
+            TcAttachType::Custom(parent) => *parent,
+            TcAttachType::Ingress => tc_handler_make(TC_H_CLSACT, TC_H_MIN_INGRESS),
+            TcAttachType::Egress => tc_handler_make(TC_H_CLSACT, TC_H_MIN_EGRESS),
         }
     }
 }
@@ -71,31 +105,33 @@ impl SchedClassifier {
         self.data.name.to_string()
     }
 
-    /// Attaches the program to the given `interface` and `attach-point`
-    pub fn attach(&mut self, interface: &str, attach_point: TcAttachPoint) -> Result<LinkRef, ProgramError> {
+    /// Attaches the program to the given `interface`.
+    ///
+    /// # Errors
+    ///
+    /// [`TcError::NetlinkError`] is returned if attaching fails. A common cause
+    /// of failure is not having added the `clsact` qdisc to the given
+    /// interface, see [`qdisc_add_clsact`]
+    ///
+    pub fn attach(
+        &mut self,
+        interface: &str,
+        attach_type: TcAttachType,
+    ) -> Result<LinkRef, ProgramError> {
         let prog_fd = self.data.fd_or_err()?;
-        let if_index = unsafe { ifindex_from_ifname(interface) }
-                         .map_err(|io_error| TcError::NetlinkError { io_error })?;
-        let prog_name = self.name();
-        let priority = unsafe { netlink_qdisc_attach(if_index as i32, &attach_point, prog_fd, &prog_name[..]) }
+        let if_index = ifindex_from_ifname(interface)
             .map_err(|io_error| TcError::NetlinkError { io_error })?;
+        let prog_name = self.name();
+        let priority =
+            unsafe { netlink_qdisc_attach(if_index as i32, &attach_type, prog_fd, &prog_name[..]) }
+                .map_err(|io_error| TcError::NetlinkError { io_error })?;
         Ok(self.data.link(TcLink {
             if_index: if_index as i32,
-            attach_point,
+            attach_type,
             prog_fd: Some(prog_fd),
             priority,
         }))
     }
-    
-    /// Add "clasct" qdisc to an interface
-    pub fn qdisc_add_clsact_to_interface(if_name: &str) -> Result<(), ProgramError> {
-        // unsafe wrapper
-        let if_index = unsafe { ifindex_from_ifname(if_name) }
-                           .map_err(|_| ProgramError::UnknownInterface {name: if_name.to_string()})?;
-        unsafe { netlink_qdisc_add_clsact(if_index as i32) }
-                           .map_err(|io_error| TcError::NetlinkError { io_error })?;
-        Ok(())
-    }
 }
 
 impl Drop for TcLink {
@@ -107,7 +143,7 @@ impl Drop for TcLink {
 impl Link for TcLink {
     fn detach(&mut self) -> Result<(), ProgramError> {
         if let Some(_) = self.prog_fd.take() {
-            unsafe { netlink_qdisc_detach(self.if_index, &self.attach_point, self.priority) }
+            unsafe { netlink_qdisc_detach(self.if_index, &self.attach_type, self.priority) }
                 .map_err(|io_error| TcError::NetlinkError { io_error })?;
             Ok(())
         } else {
@@ -116,3 +152,15 @@ impl Link for TcLink {
     }
 }
 
+/// Add the `clasct` qdisc to the given interface.
+///
+/// The `clsact` qdisc must be added to an interface before [`SchedClassifier`]
+/// programs can be attached.
+pub fn qdisc_add_clsact(if_name: &str) -> Result<(), ProgramError> {
+    let if_index = ifindex_from_ifname(if_name).map_err(|_| ProgramError::UnknownInterface {
+        name: if_name.to_string(),
+    })?;
+    unsafe { netlink_qdisc_add_clsact(if_index as i32) }
+        .map_err(|io_error| TcError::NetlinkError { io_error })?;
+    Ok(())
+}

+ 27 - 31
aya/src/sys/netlink.rs

@@ -1,28 +1,23 @@
-use std::{
-    io,
-    mem,
-    os::unix::io::RawFd, 
-    ptr,
-    slice,
-};
+use std::{io, mem, os::unix::io::RawFd, ptr, slice};
 
 use libc::{
     c_int, close, getsockname, nlattr, nlmsgerr, nlmsghdr, recv, send, setsockopt, sockaddr_nl,
-    socket, AF_NETLINK, AF_UNSPEC, IFLA_XDP, NETLINK_ROUTE, NLA_ALIGNTO, NLA_F_NESTED, NLMSG_DONE,
-    NLMSG_ERROR, NLM_F_ACK, NLM_F_EXCL, NLM_F_ECHO, NLM_F_CREATE, NLM_F_MULTI, NLM_F_REQUEST, RTM_SETLINK, SOCK_RAW, 
-    SOL_NETLINK, RTM_NEWQDISC, RTM_NEWTFILTER, RTM_DELTFILTER, ETH_P_ALL,
+    socket, AF_NETLINK, AF_UNSPEC, ETH_P_ALL, IFLA_XDP, NETLINK_ROUTE, NLA_ALIGNTO, NLA_F_NESTED,
+    NLMSG_DONE, NLMSG_ERROR, NLM_F_ACK, NLM_F_CREATE, NLM_F_ECHO, NLM_F_EXCL, NLM_F_MULTI,
+    NLM_F_REQUEST, RTM_DELTFILTER, RTM_NEWQDISC, RTM_NEWTFILTER, RTM_SETLINK, SOCK_RAW,
+    SOL_NETLINK,
 };
 
 use crate::{
     generated::{
-    _bindgen_ty_79::{IFLA_XDP_EXPECTED_FD, IFLA_XDP_FD, IFLA_XDP_FLAGS},
-    _bindgen_ty_91::{TCA_KIND, TCA_OPTIONS},
-    _bindgen_ty_133::{TCA_BPF_FD, TCA_BPF_NAME, TCA_BPF_FLAGS},
-    ifinfomsg, tcmsg, NLMSG_ALIGNTO, XDP_FLAGS_REPLACE,
-    TC_H_UNSPEC, TCA_BPF_FLAG_ACT_DIRECT, TC_H_CLSACT, TC_H_INGRESS, TC_H_MAJ_MASK,
+        _bindgen_ty_133::{TCA_BPF_FD, TCA_BPF_FLAGS, TCA_BPF_NAME},
+        _bindgen_ty_79::{IFLA_XDP_EXPECTED_FD, IFLA_XDP_FD, IFLA_XDP_FLAGS},
+        _bindgen_ty_91::{TCA_KIND, TCA_OPTIONS},
+        ifinfomsg, tcmsg, NLMSG_ALIGNTO, TCA_BPF_FLAG_ACT_DIRECT, TC_H_CLSACT, TC_H_INGRESS,
+        TC_H_MAJ_MASK, TC_H_UNSPEC, XDP_FLAGS_REPLACE,
     },
-    util::{htons, tc_handler_make},
-    programs::TcAttachPoint,
+    programs::TcAttachType,
+    util::tc_handler_make,
 };
 
 const NETLINK_EXT_ACK: c_int = 11;
@@ -130,9 +125,7 @@ pub(crate) unsafe fn netlink_set_xdp_fd(
     Ok(())
 }
 
-pub(crate) unsafe fn netlink_qdisc_add_clsact(
-    if_index: i32
-) -> Result<(), io::Error> {
+pub(crate) unsafe fn netlink_qdisc_add_clsact(if_index: i32) -> Result<(), io::Error> {
     let sock = NetlinkSocket::open()?;
 
     let seq = 1;
@@ -191,14 +184,14 @@ pub(crate) unsafe fn netlink_qdisc_add_clsact(
 
 pub(crate) unsafe fn netlink_qdisc_detach(
     if_index: i32,
-    attach_point: &TcAttachPoint,
+    attach_type: &TcAttachType,
     priority: u32,
 ) -> Result<(), io::Error> {
-        let sock = NetlinkSocket::open()?;
+    let sock = NetlinkSocket::open()?;
     let seq = 1;
     let mut req = mem::zeroed::<QdiscRequest>();
 
-    req.header = nlmsghdr{
+    req.header = nlmsghdr {
         nlmsg_len: (mem::size_of::<nlmsghdr>() + mem::size_of::<tcmsg>()) as u32,
         nlmsg_flags: (NLM_F_REQUEST | NLM_F_ACK) as u16,
         nlmsg_type: RTM_DELTFILTER,
@@ -209,7 +202,7 @@ pub(crate) unsafe fn netlink_qdisc_detach(
     req.tc_info.tcm_family = AF_UNSPEC as u8;
     req.tc_info.tcm_handle = 0; // auto-assigned, if not provided
     req.tc_info.tcm_info = tc_handler_make(priority << 16, htons(ETH_P_ALL as u16) as u32);
-    req.tc_info.tcm_parent = attach_point.tcm_parent(0)?;
+    req.tc_info.tcm_parent = attach_type.parent();
     req.tc_info.tcm_ifindex = if_index;
 
     if send(
@@ -229,7 +222,7 @@ pub(crate) unsafe fn netlink_qdisc_detach(
 
 pub(crate) unsafe fn netlink_qdisc_attach(
     if_index: i32,
-    attach_point: &TcAttachPoint,
+    attach_type: &TcAttachType,
     prog_fd: RawFd,
     prog_name: &str,
 ) -> Result<u32, io::Error> {
@@ -238,7 +231,7 @@ pub(crate) unsafe fn netlink_qdisc_attach(
     let priority = 0;
     let mut req = mem::zeroed::<QdiscRequest>();
 
-    req.header = nlmsghdr{
+    req.header = nlmsghdr {
         nlmsg_len: (mem::size_of::<nlmsghdr>() + mem::size_of::<tcmsg>()) as u32,
         nlmsg_flags: (NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE | NLM_F_ECHO) as u16,
         nlmsg_type: RTM_NEWTFILTER,
@@ -246,9 +239,9 @@ pub(crate) unsafe fn netlink_qdisc_attach(
         nlmsg_seq: seq,
     };
     req.tc_info.tcm_family = AF_UNSPEC as u8;
-    req.tc_info.tcm_handle = 0;  // auto-assigned, if not provided
+    req.tc_info.tcm_handle = 0; // auto-assigned, if not provided
     req.tc_info.tcm_ifindex = if_index;
-    req.tc_info.tcm_parent = attach_point.tcm_parent(0)?;
+    req.tc_info.tcm_parent = attach_type.parent();
 
     req.tc_info.tcm_info = tc_handler_make(priority << 16, htons(ETH_P_ALL as u16) as u32);
 
@@ -279,10 +272,10 @@ pub(crate) unsafe fn netlink_qdisc_attach(
     let nested_tca_options_start = nla_len;
     let nested_attr_offset = offset;
     // now write the nested portion
-    
+
     let mut nested_attr = nlattr {
         nla_type: TCA_OPTIONS as u16 | NLA_F_NESTED as u16,
-        nla_len: nla_hdr_len as u16,  // no data
+        nla_len: nla_hdr_len as u16, // no data
     };
 
     offset += nla_hdr_len;
@@ -329,7 +322,7 @@ pub(crate) unsafe fn netlink_qdisc_attach(
     ptr::write(offset as *mut u32, bpf_flags);
     nla_len += attr.nla_len;
 
-    // now write the NESTED nlattr 
+    // now write the NESTED nlattr
     nested_attr.nla_len = nla_len - nested_tca_options_start;
     ptr::write(nested_attr_offset as *mut nlattr, nested_attr);
     req.header.nlmsg_len += align_to(nla_len as usize, NLA_ALIGNTO as usize) as u32;
@@ -504,3 +497,6 @@ fn align_to(v: usize, align: usize) -> usize {
     (v + (align - 1)) & !(align - 1)
 }
 
+fn htons(u: u16) -> u16 {
+    u.to_be()
+}

+ 15 - 26
aya/src/util.rs

@@ -4,7 +4,6 @@ use std::{
     ffi::CString,
     fs::{self, File},
     io::{self, BufReader},
-    os::raw::c_char,
     str::FromStr,
 };
 
@@ -35,10 +34,6 @@ pub fn nr_cpus() -> Result<usize, io::Error> {
     Ok(possible_cpus()?.len())
 }
 
-pub(crate) fn tc_handler_make(major: u32, minor: u32) -> u32 {
-    (major & TC_H_MAJ_MASK) | (minor & TC_H_MIN_MASK)
-}
-
 /// Get the list of possible cpus.
 ///
 /// See `/sys/devices/system/cpu/possible`.
@@ -73,27 +68,6 @@ fn parse_cpu_ranges(data: &str) -> Result<Vec<u32>, ()> {
     Ok(cpus)
 }
 
-/// Gets interface index from interface name.
-pub unsafe fn ifindex_from_ifname(if_name: &str) -> Result<u32, io::Error> {
-    let c_str_if_name = CString::new(if_name)?;
-    let c_if_name: *const c_char = c_str_if_name.as_ptr() as *const c_char;
-    // unsafe libc wrapper
-    let if_index = if_nametoindex(c_if_name);
-    if if_index == 0 {
-        return Err(io::Error::last_os_error());
-    }
-    Ok(if_index)
-}
-
-/// htons and ntohs util functions
-pub fn htons(u: u16) -> u16 {
-    u.to_be()
-}
-
-pub fn ntohs(u: u16) -> u16 {
-    u16::from_be(u)
-}
-
 /// Loads kernel symbols from `/proc/kallsyms`.
 ///
 /// The symbols can be passed to [`StackTrace::resolve`](crate::maps::stack_trace::StackTrace::resolve).
@@ -117,6 +91,21 @@ fn parse_kernel_symbols(reader: &mut dyn BufRead) -> Result<BTreeMap<u64, String
     Ok(syms)
 }
 
+pub(crate) fn ifindex_from_ifname(if_name: &str) -> Result<u32, io::Error> {
+    let c_str_if_name = CString::new(if_name)?;
+    let c_if_name = c_str_if_name.as_ptr();
+    // Safety: libc wrapper
+    let if_index = unsafe { if_nametoindex(c_if_name) };
+    if if_index == 0 {
+        return Err(io::Error::last_os_error());
+    }
+    Ok(if_index)
+}
+
+pub(crate) fn tc_handler_make(major: u32, minor: u32) -> u32 {
+    (major & TC_H_MAJ_MASK) | (minor & TC_H_MIN_MASK)
+}
+
 #[cfg(test)]
 mod tests {
     use std::iter::FromIterator;