From f46fd17cc31f7f900f01d8e97baebe6445c468d4 Mon Sep 17 00:00:00 2001 From: Andre Fredette Date: Sun, 23 Oct 2022 18:33:59 -0400 Subject: [PATCH 01/11] Support reconstruction of `SchedClassifierLink` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the proposed solution for Step 2 of issue #414 “For cases where you have done program.take_link() to manage ownership of TcLink we need an API similar to PinnedLink::from_pin that can reconstruct a TcLink” As long as a user application continues to run after executing `take_link()`, the `SchedClassifierLink` returned can be used to detach the program. However, if we want to handle cases where the application exits or crashes, we need a way to save and reconstruct the link, and to do that, we also need to know the information required for the reconstruction -- namely, the `interface`, `attach_type`, `priority`, and `handle`. The user knows the first two because they are required to execute `attach()` in the first place; however, the user will not know the others if they let the system choose them. This pr solves the problems by adding an `impl` for `SchedClassifierLink` with an accessor for `tc_options` and a `new()` function. Signed-off-by: Andre Fredette --- aya/src/programs/tc.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/aya/src/programs/tc.rs b/aya/src/programs/tc.rs index d1e90be1..6ff044dc 100644 --- a/aya/src/programs/tc.rs +++ b/aya/src/programs/tc.rs @@ -228,6 +228,33 @@ define_link_wrapper!( TcLinkId ); +impl SchedClassifierLink { + /// Creates a new SchedClassifierLink instance + pub fn new( + interface: &str, + attach_type: TcAttachType, + priority: u16, + handle: u32, + ) -> Result { + let if_index = ifindex_from_ifname(interface) + .map_err(|io_error| TcError::NetlinkError { io_error })?; + Ok(SchedClassifierLink(TcLink { + if_index: if_index as i32, + attach_type, + priority, + handle, + })) + } + + /// Returns options for a SchedClassifierLink + pub fn tc_options(&self) -> TcOptions { + TcOptions { + priority: self.0.priority, + handle: self.0.handle, + } + } +} + /// Add the `clasct` qdisc to the given interface. /// /// The `clsact` qdisc must be added to an interface before [`SchedClassifier`] From c3a8400e4d219eb72167ccaeb500d36ad924873d Mon Sep 17 00:00:00 2001 From: Andre Fredette Date: Mon, 21 Nov 2022 16:08:45 -0500 Subject: [PATCH 02/11] Add example for SchedClassifierLink::new() Also modified the impl a bit to work as described in the example. Signed-off-by: Andre Fredette --- aya/src/programs/tc.rs | 69 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 10 deletions(-) diff --git a/aya/src/programs/tc.rs b/aya/src/programs/tc.rs index 6ff044dc..dfef6e99 100644 --- a/aya/src/programs/tc.rs +++ b/aya/src/programs/tc.rs @@ -228,16 +228,63 @@ define_link_wrapper!( TcLinkId ); +/// # Examples +/// +/// ```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(&[])?; +/// # tc::qdisc_add_clsact("eth0")?; +/// # let prog: &mut SchedClassifier = bpf.program_mut("redirect_ingress").unwrap().try_into()?; +/// # prog.load()?; +/// +/// use aya::programs::tc::{SchedClassifierLink}; +/// use aya::programs::{tc, Link, SchedClassifier, TcAttachType}; +/// +/// // SchedClassifier::attach returns a SchedClassifierLinkId that can be used to +/// // detach the program. +/// let tc_link_id = prog.attach("eth0", TcAttachType::Ingress)?; +/// +/// // The user may take ownership of the lifetime of a link using +/// // SchedClassifier::take_link, which returns a SchedClassifierLink +/// let tc_link = prog.take_link(tc_link_id)?; +/// +/// // Once ownership is taken, SchedClassifierLink::detach can be used to detach the +/// // link. If needed, the link can be reconstructed via the SchedClassifierLink::new +/// // using the if_name, attach_type, priority, and handle. The user knows the first +/// // two because they were required to execute attach(); however, the user may not +/// // know the priority, and/or handle if they let the system choose them as happens +/// // with SchedClassifier::attach. If needed, These items may be retrieved as follows: +/// let priority = tc_link.priority(); +/// let handle = tc_link.handle(); +/// +/// // Then, the link can be reconstructed as follows: +/// let tc_link = SchedClassifierLink::new("eth0", TcAttachType::Ingress, priority, handle)?; +/// +/// // And, the user can then detatch the link as follows: +/// tc_link.detach()?; +/// +/// # Ok::<(), Error>(()) +/// ``` impl SchedClassifierLink { - /// Creates a new SchedClassifierLink instance + /// Creates a new `SchedClassifierLink` instance pub fn new( - interface: &str, + if_name: &str, attach_type: TcAttachType, priority: u16, handle: u32, ) -> Result { - let if_index = ifindex_from_ifname(interface) - .map_err(|io_error| TcError::NetlinkError { io_error })?; + let if_index = + ifindex_from_ifname(if_name).map_err(|io_error| TcError::NetlinkError { io_error })?; Ok(SchedClassifierLink(TcLink { if_index: if_index as i32, attach_type, @@ -246,12 +293,14 @@ impl SchedClassifierLink { })) } - /// Returns options for a SchedClassifierLink - pub fn tc_options(&self) -> TcOptions { - TcOptions { - priority: self.0.priority, - handle: self.0.handle, - } + /// Returns the `priority` for a `SchedClassifierLink` + pub fn priority(&self) -> u16 { + self.0.priority + } + + /// Returns the `handle` for a `SchedClassifierLink` + pub fn handle(&self) -> u32 { + self.0.handle } } From 6563e6cc065d01270aad50d9c3449f9deb9f04d6 Mon Sep 17 00:00:00 2001 From: Andre Fredette Date: Tue, 22 Nov 2022 09:23:52 -0500 Subject: [PATCH 03/11] Combine updates to SchedClassifierLink example made by Dave Tucker Co-authored-by: Dave Tucker Signed-off-by: Andre Fredette --- aya/src/programs/tc.rs | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/aya/src/programs/tc.rs b/aya/src/programs/tc.rs index dfef6e99..650fe22f 100644 --- a/aya/src/programs/tc.rs +++ b/aya/src/programs/tc.rs @@ -228,6 +228,8 @@ define_link_wrapper!( TcLinkId ); +/// SchedClassifier link is the link type used by [`SchedClassifier`] programs. +/// /// # Examples /// /// ```no_run @@ -246,37 +248,27 @@ define_link_wrapper!( /// # tc::qdisc_add_clsact("eth0")?; /// # let prog: &mut SchedClassifier = bpf.program_mut("redirect_ingress").unwrap().try_into()?; /// # prog.load()?; +/// # +/// # use aya::programs::tc::{SchedClassifierLink}; +/// # use aya::programs::{tc, Link, SchedClassifier, TcAttachType}; /// -/// use aya::programs::tc::{SchedClassifierLink}; -/// use aya::programs::{tc, Link, SchedClassifier, TcAttachType}; -/// -/// // SchedClassifier::attach returns a SchedClassifierLinkId that can be used to -/// // detach the program. /// let tc_link_id = prog.attach("eth0", TcAttachType::Ingress)?; /// -/// // The user may take ownership of the lifetime of a link using -/// // SchedClassifier::take_link, which returns a SchedClassifierLink /// let tc_link = prog.take_link(tc_link_id)?; /// -/// // Once ownership is taken, SchedClassifierLink::detach can be used to detach the -/// // link. If needed, the link can be reconstructed via the SchedClassifierLink::new -/// // using the if_name, attach_type, priority, and handle. The user knows the first -/// // two because they were required to execute attach(); however, the user may not -/// // know the priority, and/or handle if they let the system choose them as happens -/// // with SchedClassifier::attach. If needed, These items may be retrieved as follows: +/// // The priority and handle assigned during attach can be accessed as follows: /// let priority = tc_link.priority(); /// let handle = tc_link.handle(); /// -/// // Then, the link can be reconstructed as follows: +/// // A new SchedClassifierLink can be constructed to access an existing attachment /// let tc_link = SchedClassifierLink::new("eth0", TcAttachType::Ingress, priority, handle)?; /// -/// // And, the user can then detatch the link as follows: /// tc_link.detach()?; /// /// # Ok::<(), Error>(()) /// ``` impl SchedClassifierLink { - /// Creates a new `SchedClassifierLink` instance + /// Creates a new link from the provided values. pub fn new( if_name: &str, attach_type: TcAttachType, @@ -293,12 +285,12 @@ impl SchedClassifierLink { })) } - /// Returns the `priority` for a `SchedClassifierLink` + /// Returns the allocated priority. This may be different to what was provided to [`SchedClassifier::attach`]. pub fn priority(&self) -> u16 { self.0.priority } - /// Returns the `handle` for a `SchedClassifierLink` + /// Returns the assigned handle. This was auto-generated if none was supplied to [`SchedClassifier::attach`]. pub fn handle(&self) -> u32 { self.0.handle } From 67efc33414df049853247e344dfaa37eeeafe602 Mon Sep 17 00:00:00 2001 From: Andre Fredette Date: Tue, 22 Nov 2022 14:11:33 -0500 Subject: [PATCH 04/11] Additional edits to SchedClassifierLink documentation. Signed-off-by: Andre Fredette --- aya/src/programs/tc.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/aya/src/programs/tc.rs b/aya/src/programs/tc.rs index 650fe22f..5185bb8d 100644 --- a/aya/src/programs/tc.rs +++ b/aya/src/programs/tc.rs @@ -228,7 +228,7 @@ define_link_wrapper!( TcLinkId ); -/// SchedClassifier link is the link type used by [`SchedClassifier`] programs. +/// [`SchedClassifierLink`] is the link type used by [`SchedClassifier`] programs. /// /// # Examples /// @@ -251,7 +251,6 @@ define_link_wrapper!( /// # /// # use aya::programs::tc::{SchedClassifierLink}; /// # use aya::programs::{tc, Link, SchedClassifier, TcAttachType}; -/// /// let tc_link_id = prog.attach("eth0", TcAttachType::Ingress)?; /// /// let tc_link = prog.take_link(tc_link_id)?; @@ -261,14 +260,25 @@ define_link_wrapper!( /// let handle = tc_link.handle(); /// /// // A new SchedClassifierLink can be constructed to access an existing attachment -/// let tc_link = SchedClassifierLink::new("eth0", TcAttachType::Ingress, priority, handle)?; +/// let new_tc_link = SchedClassifierLink::new("eth0", TcAttachType::Ingress, priority, handle)?; /// -/// tc_link.detach()?; +/// new_tc_link.detach()?; /// /// # Ok::<(), Error>(()) /// ``` impl SchedClassifierLink { /// Creates a new link from the provided values. + /// + /// This API is used to construct a [`SchedClassifierLink`] where the `if_name`, `attach_type`, + /// `priority` and `handle` are already known. This may have been found from a link created by + /// [SchedClassifier::attach], the output of the `tc filter` command or from the output of + /// another BPF loader. + /// + /// # Warnings + /// - If a program is not attached with the provided parameters, calls to + /// [`SchedClassifierLink::detach`] will return a [`TcError::NetlinkError`] + /// - If you create a link for a program that you do not own, detaching it may have unintended + /// consequences. pub fn new( if_name: &str, attach_type: TcAttachType, @@ -285,12 +295,12 @@ impl SchedClassifierLink { })) } - /// Returns the allocated priority. This may be different to what was provided to [`SchedClassifier::attach`]. + /// Returns the allocated priority. If none was provided at attach time, this was allocated for you. pub fn priority(&self) -> u16 { self.0.priority } - /// Returns the assigned handle. This was auto-generated if none was supplied to [`SchedClassifier::attach`]. + /// Returns the assigned handle. If none was provided at attach time, this was allocated for you. pub fn handle(&self) -> u32 { self.0.handle } From 849796c4208b520cd12a7ac5de28857dc885c026 Mon Sep 17 00:00:00 2001 From: Andre Fredette Date: Sun, 18 Dec 2022 10:42:36 -0500 Subject: [PATCH 05/11] rename SchedClassifierLink:new() to new_tc_link() Avoids name confilct with pr #462 Signed-off-by: Andre Fredette --- aya/src/programs/tc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aya/src/programs/tc.rs b/aya/src/programs/tc.rs index 5185bb8d..e83fc633 100644 --- a/aya/src/programs/tc.rs +++ b/aya/src/programs/tc.rs @@ -279,7 +279,7 @@ impl SchedClassifierLink { /// [`SchedClassifierLink::detach`] will return a [`TcError::NetlinkError`] /// - If you create a link for a program that you do not own, detaching it may have unintended /// consequences. - pub fn new( + pub fn new_tc_link( if_name: &str, attach_type: TcAttachType, priority: u16, From 65f5b76593f35b8ca09f5d868a4195086ddca831 Mon Sep 17 00:00:00 2001 From: Andre Fredette Date: Mon, 19 Dec 2022 06:52:04 -0500 Subject: [PATCH 06/11] Address review comments Signed-off-by: Andre Fredette --- aya/src/programs/tc.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/aya/src/programs/tc.rs b/aya/src/programs/tc.rs index e83fc633..4d021489 100644 --- a/aya/src/programs/tc.rs +++ b/aya/src/programs/tc.rs @@ -251,6 +251,7 @@ define_link_wrapper!( /// # /// # use aya::programs::tc::{SchedClassifierLink}; /// # use aya::programs::{tc, Link, SchedClassifier, TcAttachType}; +/// # use std::mem; /// let tc_link_id = prog.attach("eth0", TcAttachType::Ingress)?; /// /// let tc_link = prog.take_link(tc_link_id)?; @@ -259,9 +260,16 @@ define_link_wrapper!( /// let priority = tc_link.priority(); /// let handle = tc_link.handle(); /// -/// // A new SchedClassifierLink can be constructed to access an existing attachment -/// let new_tc_link = SchedClassifierLink::new("eth0", TcAttachType::Ingress, priority, handle)?; +/// // The following call "forgets" about tc_link so that it is not detached on drop. +/// mem::forget(tc_link); /// +/// // The link can be re-instantiated and detached as shown below. +/// let new_tc_link = SchedClassifierLink::new_tc_link( +/// "eth0", +/// TcAttachType::Ingress, +/// priority, +/// handle, +/// )?; /// new_tc_link.detach()?; /// /// # Ok::<(), Error>(()) @@ -274,19 +282,19 @@ impl SchedClassifierLink { /// [SchedClassifier::attach], the output of the `tc filter` command or from the output of /// another BPF loader. /// - /// # Warnings + /// Note: If you create a link for a program that you do not own, detaching it may have + /// unintended consequences. + /// + /// # Errors /// - If a program is not attached with the provided parameters, calls to /// [`SchedClassifierLink::detach`] will return a [`TcError::NetlinkError`] - /// - If you create a link for a program that you do not own, detaching it may have unintended - /// consequences. pub fn new_tc_link( if_name: &str, attach_type: TcAttachType, priority: u16, handle: u32, - ) -> Result { - let if_index = - ifindex_from_ifname(if_name).map_err(|io_error| TcError::NetlinkError { io_error })?; + ) -> Result { + let if_index = ifindex_from_ifname(if_name)?; Ok(SchedClassifierLink(TcLink { if_index: if_index as i32, attach_type, From 2972d462a505aaba8d9e40ddf2c6110e497283db Mon Sep 17 00:00:00 2001 From: Andre Fredette Date: Thu, 22 Dec 2022 07:51:35 -0500 Subject: [PATCH 07/11] Address review comments - Rename `new_tc_link` to `attached` - Simplified example for `attached` The following was not requested in reviews: - Moved example from `SchedClassifierLink` tor `SchedClassifierLink::attached' since we're only showing an example of that one method Signed-off-by: Andre Fredette --- aya/src/programs/tc.rs | 69 +++++++++++++----------------------------- 1 file changed, 21 insertions(+), 48 deletions(-) diff --git a/aya/src/programs/tc.rs b/aya/src/programs/tc.rs index 4d021489..7bf536e7 100644 --- a/aya/src/programs/tc.rs +++ b/aya/src/programs/tc.rs @@ -229,54 +229,7 @@ define_link_wrapper!( ); /// [`SchedClassifierLink`] is the link type used by [`SchedClassifier`] programs. -/// -/// # Examples -/// -/// ```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(&[])?; -/// # tc::qdisc_add_clsact("eth0")?; -/// # let prog: &mut SchedClassifier = bpf.program_mut("redirect_ingress").unwrap().try_into()?; -/// # prog.load()?; -/// # -/// # use aya::programs::tc::{SchedClassifierLink}; -/// # use aya::programs::{tc, Link, SchedClassifier, TcAttachType}; -/// # use std::mem; -/// let tc_link_id = prog.attach("eth0", TcAttachType::Ingress)?; -/// -/// let tc_link = prog.take_link(tc_link_id)?; -/// -/// // The priority and handle assigned during attach can be accessed as follows: -/// let priority = tc_link.priority(); -/// let handle = tc_link.handle(); -/// -/// // The following call "forgets" about tc_link so that it is not detached on drop. -/// mem::forget(tc_link); -/// -/// // The link can be re-instantiated and detached as shown below. -/// let new_tc_link = SchedClassifierLink::new_tc_link( -/// "eth0", -/// TcAttachType::Ingress, -/// priority, -/// handle, -/// )?; -/// new_tc_link.detach()?; -/// -/// # Ok::<(), Error>(()) -/// ``` impl SchedClassifierLink { - /// Creates a new link from the provided values. - /// /// This API is used to construct a [`SchedClassifierLink`] where the `if_name`, `attach_type`, /// `priority` and `handle` are already known. This may have been found from a link created by /// [SchedClassifier::attach], the output of the `tc filter` command or from the output of @@ -288,7 +241,27 @@ impl SchedClassifierLink { /// # Errors /// - If a program is not attached with the provided parameters, calls to /// [`SchedClassifierLink::detach`] will return a [`TcError::NetlinkError`] - pub fn new_tc_link( + /// + /// # Examples + /// ```no_run + /// # use aya::programs::tc::SchedClassifierLink; + /// # use aya::programs::TcAttachType; + /// # #[derive(Debug, thiserror::Error)] + /// # enum Error { + /// # #[error(transparent)] + /// # IO(#[from] std::io::Error), + /// # } + /// # fn read_persisted_link_details() -> (String, TcAttachType, u16, u32) { + /// # ("eth0".to_string(), TcAttachType::Ingress, 50, 1) + /// # } + /// // Get the link parameters from some external source. Where and how the parameters are + /// // persisted is up to your application. + /// let (if_name, attach_type, priority, handle) = read_persisted_link_details(); + /// let new_tc_link = SchedClassifierLink::attached(&if_name, attach_type, priority, handle)?; + /// + /// # Ok::<(), Error>(()) + /// ``` + pub fn attached( if_name: &str, attach_type: TcAttachType, priority: u16, From 6766532341803ab70501e0c522afc0656385e002 Mon Sep 17 00:00:00 2001 From: Andre Fredette Date: Thu, 22 Dec 2022 09:28:40 -0500 Subject: [PATCH 08/11] Remove SchedClassifierLink description It was redundant with the one provided in define_link_wrapper Signed-off-by: Andre Fredette --- aya/src/programs/tc.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/aya/src/programs/tc.rs b/aya/src/programs/tc.rs index 7bf536e7..105911f5 100644 --- a/aya/src/programs/tc.rs +++ b/aya/src/programs/tc.rs @@ -228,7 +228,6 @@ define_link_wrapper!( TcLinkId ); -/// [`SchedClassifierLink`] is the link type used by [`SchedClassifier`] programs. impl SchedClassifierLink { /// This API is used to construct a [`SchedClassifierLink`] where the `if_name`, `attach_type`, /// `priority` and `handle` are already known. This may have been found from a link created by From d43879d99177c33c5d33827d8a3c7572841dd9df Mon Sep 17 00:00:00 2001 From: Andre Fredette Date: Wed, 25 Jan 2023 15:12:21 -0500 Subject: [PATCH 09/11] Updates after rebase due to changes in define_link_wrapper Signed-off-by: Andre Fredette --- aya/src/programs/tc.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aya/src/programs/tc.rs b/aya/src/programs/tc.rs index 105911f5..e803dc68 100644 --- a/aya/src/programs/tc.rs +++ b/aya/src/programs/tc.rs @@ -267,22 +267,22 @@ impl SchedClassifierLink { handle: u32, ) -> Result { let if_index = ifindex_from_ifname(if_name)?; - Ok(SchedClassifierLink(TcLink { + Ok(SchedClassifierLink(Some(TcLink { if_index: if_index as i32, attach_type, priority, handle, - })) + }))) } /// Returns the allocated priority. If none was provided at attach time, this was allocated for you. pub fn priority(&self) -> u16 { - self.0.priority + self.inner().priority } /// Returns the assigned handle. If none was provided at attach time, this was allocated for you. pub fn handle(&self) -> u32 { - self.0.handle + self.inner().handle } } From 7c24296b5df73a5d9d07872a3832cf4e9aa9c76f Mon Sep 17 00:00:00 2001 From: Andre Fredette Date: Mon, 30 Jan 2023 10:03:38 -0500 Subject: [PATCH 10/11] Address review comments from @alessandrod Signed-off-by: Andre Fredette --- aya/src/programs/tc.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aya/src/programs/tc.rs b/aya/src/programs/tc.rs index e803dc68..cece3232 100644 --- a/aya/src/programs/tc.rs +++ b/aya/src/programs/tc.rs @@ -229,7 +229,7 @@ define_link_wrapper!( ); impl SchedClassifierLink { - /// This API is used to construct a [`SchedClassifierLink`] where the `if_name`, `attach_type`, + /// Constructs a [`SchedClassifierLink`] where the `if_name`, `attach_type`, /// `priority` and `handle` are already known. This may have been found from a link created by /// [SchedClassifier::attach], the output of the `tc filter` command or from the output of /// another BPF loader. @@ -238,8 +238,8 @@ impl SchedClassifierLink { /// unintended consequences. /// /// # Errors - /// - If a program is not attached with the provided parameters, calls to - /// [`SchedClassifierLink::detach`] will return a [`TcError::NetlinkError`] + /// Returns [`io::Error`] if `if_name` is invalid. If the other parameters are invalid this call + /// will succeed, but calling [`SchedClassifierLink::detach`] will return [`TcError::NetlinkError`]. /// /// # Examples /// ```no_run