diff --git a/.gitignore b/.gitignore index d194918f..0248fbf2 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ libbpf/ !.vscode/settings.json site/ header.html +.idea/ diff --git a/aya/src/programs/tc.rs b/aya/src/programs/tc.rs index 00a82f06..7748a845 100644 --- a/aya/src/programs/tc.rs +++ b/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 { 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(()) diff --git a/aya/src/sys/netlink.rs b/aya/src/sys/netlink.rs index 47fce1cf..b0526cc3 100644 --- a/aya/src/sys/netlink.rs +++ b/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 { + handle: u32, +) -> Result<(u16, u32), io::Error> { let sock = NetlinkSocket::open()?; let mut req = mem::zeroed::(); @@ -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::(); @@ -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, io::Error> { +) -> Result, io::Error> { let mut req = mem::zeroed::(); let nlmsg_len = mem::size_of::() + mem::size_of::(); @@ -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)]