Browse Source

Support using handle in tc programs

Implements step 1 of https://github.com/aya-rs/aya/issues/414.

- Adds handle to the SchedClassifier attach API
- Saves handle in the TcLink sruct and uses it when detaching programs

NOTE: this changes the API, so it will require a bump in the Aya version.

Signed-off-by: Andre Fredette <[email protected]>
Andre Fredette 2 years ago
parent
commit
ac07608b79
3 changed files with 44 additions and 28 deletions
  1. 1 0
      .gitignore
  2. 26 11
      aya/src/programs/tc.rs
  3. 17 17
      aya/src/sys/netlink.rs

+ 1 - 0
.gitignore

@@ -5,3 +5,4 @@ libbpf/
 !.vscode/settings.json
 site/
 header.html
+.idea/

+ 26 - 11
aya/src/programs/tc.rs

@@ -63,7 +63,7 @@ pub enum TcAttachType {
 ///
 /// let prog: &mut SchedClassifier = bpf.program_mut("redirect_ingress").unwrap().try_into()?;
 /// prog.load()?;
-/// prog.attach("eth0", TcAttachType::Ingress, 0)?;
+/// prog.attach("eth0", TcAttachType::Ingress, 0, 0)?;
 ///
 /// # Ok::<(), Error>(())
 /// ```
@@ -111,6 +111,9 @@ impl SchedClassifier {
     /// 0 means let the system choose the next highest priority, or 49152 if no filters exist yet.
     /// All other values in the range are taken as an explicit priority setting (aka "preference").
     ///
+    /// `handle` is used to uniquely identify a program at a given priority level.  
+    /// If set to 0, the system will choose a handle.
+    ///
     /// The returned value can be used to detach, see [SchedClassifier::detach].
     ///
     /// # Errors
@@ -124,12 +127,20 @@ impl SchedClassifier {
         interface: &str,
         attach_type: TcAttachType,
         priority: u16,
+        handle: u32,
     ) -> Result<SchedClassifierLinkId, ProgramError> {
         let prog_fd = self.data.fd_or_err()?;
         let if_index = ifindex_from_ifname(interface)
             .map_err(|io_error| TcError::NetlinkError { io_error })?;
-        let priority = unsafe {
-            netlink_qdisc_attach(if_index as i32, &attach_type, prog_fd, &self.name, priority)
+        let (priority, handle) = unsafe {
+            netlink_qdisc_attach(
+                if_index as i32,
+                &attach_type,
+                prog_fd,
+                &self.name,
+                priority,
+                handle,
+            )
         }
         .map_err(|io_error| TcError::NetlinkError { io_error })?;
 
@@ -137,6 +148,7 @@ impl SchedClassifier {
             if_index: if_index as i32,
             attach_type,
             priority,
+            handle,
         }))
     }
 
@@ -160,25 +172,28 @@ impl SchedClassifier {
 }
 
 #[derive(Debug, Hash, Eq, PartialEq)]
-pub(crate) struct TcLinkId(i32, TcAttachType, u16);
+pub(crate) struct TcLinkId(i32, TcAttachType, u16, u32);
 
 #[derive(Debug)]
 struct TcLink {
     if_index: i32,
     attach_type: TcAttachType,
     priority: u16,
+    handle: u32,
 }
 
 impl Link for TcLink {
     type Id = TcLinkId;
 
     fn id(&self) -> Self::Id {
-        TcLinkId(self.if_index, self.attach_type, self.priority)
+        TcLinkId(self.if_index, self.attach_type, self.priority, self.handle)
     }
 
     fn detach(self) -> Result<(), ProgramError> {
-        unsafe { netlink_qdisc_detach(self.if_index, &self.attach_type, self.priority) }
-            .map_err(|io_error| TcError::NetlinkError { io_error })?;
+        unsafe {
+            netlink_qdisc_detach(self.if_index, &self.attach_type, self.priority, self.handle)
+        }
+        .map_err(|io_error| TcError::NetlinkError { io_error })?;
         Ok(())
     }
 }
@@ -233,16 +248,16 @@ fn qdisc_detach_program_fast(
 ) -> Result<(), io::Error> {
     let if_index = ifindex_from_ifname(if_name)? as i32;
 
-    let prios = unsafe { netlink_find_filter_with_name(if_index, attach_type, name)? };
-    if prios.is_empty() {
+    let filter_info = unsafe { netlink_find_filter_with_name(if_index, attach_type, name)? };
+    if filter_info.is_empty() {
         return Err(io::Error::new(
             io::ErrorKind::NotFound,
             name.to_string_lossy(),
         ));
     }
 
-    for prio in prios {
-        unsafe { netlink_qdisc_detach(if_index, &attach_type, prio)? };
+    for (prio, handle) in filter_info {
+        unsafe { netlink_qdisc_detach(if_index, &attach_type, prio, handle)? };
     }
 
     Ok(())

+ 17 - 17
aya/src/sys/netlink.rs

@@ -104,7 +104,8 @@ pub(crate) unsafe fn netlink_qdisc_attach(
     prog_fd: RawFd,
     prog_name: &CStr,
     priority: u16,
-) -> Result<u16, io::Error> {
+    handle: u32,
+) -> Result<(u16, u32), io::Error> {
     let sock = NetlinkSocket::open()?;
     let mut req = mem::zeroed::<TcRequest>();
 
@@ -117,7 +118,7 @@ pub(crate) unsafe fn netlink_qdisc_attach(
         nlmsg_seq: 1,
     };
     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 = handle; // auto-assigned, if zero
     req.tc_info.tcm_ifindex = if_index;
     req.tc_info.tcm_parent = attach_type.parent();
     req.tc_info.tcm_info = tc_handler_make((priority as u32) << 16, htons(ETH_P_ALL as u16) as u32);
@@ -138,17 +139,14 @@ pub(crate) unsafe fn netlink_qdisc_attach(
     req.header.nlmsg_len += align_to(kind_len + options_len, NLA_ALIGNTO as usize) as u32;
     sock.send(&bytes_of(&req)[..req.header.nlmsg_len as usize])?;
 
-    // find the RTM_NEWTFILTER reply and read the tcm_info field which we'll
-    // need to detach
-    let tc_info = match sock
+    // find the RTM_NEWTFILTER reply and read the tcm_info and tcm_handle fields
+    // which we'll need to detach
+    let tc_msg = match sock
         .recv()?
         .iter()
         .find(|reply| reply.header.nlmsg_type == RTM_NEWTFILTER)
     {
-        Some(reply) => {
-            let msg = ptr::read_unaligned(reply.data.as_ptr() as *const tcmsg);
-            msg.tcm_info
-        }
+        Some(reply) => ptr::read_unaligned(reply.data.as_ptr() as *const tcmsg),
         None => {
             // if sock.recv() succeeds we should never get here unless there's a
             // bug in the kernel
@@ -159,14 +157,15 @@ pub(crate) unsafe fn netlink_qdisc_attach(
         }
     };
 
-    let priority = ((tc_info & TC_H_MAJ_MASK) >> 16) as u16;
-    Ok(priority)
+    let priority = ((tc_msg.tcm_info & TC_H_MAJ_MASK) >> 16) as u16;
+    Ok((priority, tc_msg.tcm_handle))
 }
 
 pub(crate) unsafe fn netlink_qdisc_detach(
     if_index: i32,
     attach_type: &TcAttachType,
     priority: u16,
+    handle: u32,
 ) -> Result<(), io::Error> {
     let sock = NetlinkSocket::open()?;
     let mut req = mem::zeroed::<TcRequest>();
@@ -180,7 +179,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_handle = handle; // auto-assigned, if zero
     req.tc_info.tcm_info = tc_handler_make((priority as u32) << 16, htons(ETH_P_ALL as u16) as u32);
     req.tc_info.tcm_parent = attach_type.parent();
     req.tc_info.tcm_ifindex = if_index;
@@ -192,11 +191,12 @@ pub(crate) unsafe fn netlink_qdisc_detach(
     Ok(())
 }
 
+// Returns a vector of tuple (priority, handle) for filters matching the provided parameters
 pub(crate) unsafe fn netlink_find_filter_with_name(
     if_index: i32,
     attach_type: TcAttachType,
     name: &CStr,
-) -> Result<Vec<u16>, io::Error> {
+) -> Result<Vec<(u16, u32)>, io::Error> {
     let mut req = mem::zeroed::<TcRequest>();
 
     let nlmsg_len = mem::size_of::<nlmsghdr>() + mem::size_of::<tcmsg>();
@@ -208,14 +208,14 @@ pub(crate) unsafe fn netlink_find_filter_with_name(
         nlmsg_seq: 1,
     };
     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 zero
     req.tc_info.tcm_ifindex = if_index;
     req.tc_info.tcm_parent = attach_type.parent();
 
     let sock = NetlinkSocket::open()?;
     sock.send(&bytes_of(&req)[..req.header.nlmsg_len as usize])?;
 
-    let mut prios = Vec::new();
+    let mut filter_info = Vec::new();
     for msg in sock.recv()? {
         if msg.header.nlmsg_type != RTM_NEWTFILTER {
             continue;
@@ -230,14 +230,14 @@ pub(crate) unsafe fn netlink_find_filter_with_name(
             if let Some(f_name) = opts.get(&(TCA_BPF_NAME as u16)) {
                 if let Ok(f_name) = CStr::from_bytes_with_nul(f_name.data) {
                     if name == f_name {
-                        prios.push(priority);
+                        filter_info.push((priority, tc_msg.tcm_handle));
                     }
                 }
             }
         }
     }
 
-    Ok(prios)
+    Ok(filter_info)
 }
 
 #[repr(C)]