From 895f96e971db56bed0692dda4f5bb633d94937c8 Mon Sep 17 00:00:00 2001 From: Michal Rostecki Date: Thu, 28 Jul 2022 10:24:39 +0200 Subject: [PATCH] ebpf: Add TcContext for classifier programs This change separates the previous `SkBuffContext` into three structs: * `SkBuff` which is a wrapper around `__sk_buff` which contains all possible methods operating on it. * `SkBuffContext` which is a program context for programs which **cannot** access `__sk_buff` directly and instead can only use `load_bytes`. * `TcContext` which is a classifier context which can access `__sk_buff` directly, hence exposes `data` and `data_end`. Signed-off-by: Michal Rostecki --- aya-bpf-macros/src/expand.rs | 2 +- bpf/aya-bpf/src/programs/mod.rs | 2 + bpf/aya-bpf/src/programs/sk_buff.rs | 221 ++++++++++++++++++++++------ bpf/aya-bpf/src/programs/tc.rs | 184 +++++++++++++++++++++++ 4 files changed, 359 insertions(+), 50 deletions(-) create mode 100644 bpf/aya-bpf/src/programs/tc.rs diff --git a/aya-bpf-macros/src/expand.rs b/aya-bpf-macros/src/expand.rs index d96f0ee6..42088969 100644 --- a/aya-bpf-macros/src/expand.rs +++ b/aya-bpf-macros/src/expand.rs @@ -250,7 +250,7 @@ impl SchedClassifier { #[no_mangle] #[link_section = #section_name] fn #fn_name(ctx: *mut ::aya_bpf::bindings::__sk_buff) -> i32 { - return #fn_name(::aya_bpf::programs::SkBuffContext::new(ctx)); + return #fn_name(::aya_bpf::programs::TcContext::new(ctx)); #item } diff --git a/bpf/aya-bpf/src/programs/mod.rs b/bpf/aya-bpf/src/programs/mod.rs index c16160cb..edd69397 100644 --- a/bpf/aya-bpf/src/programs/mod.rs +++ b/bpf/aya-bpf/src/programs/mod.rs @@ -12,6 +12,7 @@ pub mod sock_addr; pub mod sock_ops; pub mod sockopt; pub mod sysctl; +pub mod tc; pub mod tp_btf; pub mod tracepoint; pub mod xdp; @@ -30,6 +31,7 @@ pub use sock_addr::SockAddrContext; pub use sock_ops::SockOpsContext; pub use sockopt::SockoptContext; pub use sysctl::SysctlContext; +pub use tc::TcContext; pub use tp_btf::BtfTracePointContext; pub use tracepoint::TracePointContext; pub use xdp::XdpContext; diff --git a/bpf/aya-bpf/src/programs/sk_buff.rs b/bpf/aya-bpf/src/programs/sk_buff.rs index 597c715f..38b0177d 100644 --- a/bpf/aya-bpf/src/programs/sk_buff.rs +++ b/bpf/aya-bpf/src/programs/sk_buff.rs @@ -13,13 +13,13 @@ use aya_bpf_cty::c_long; use crate::{bindings::__sk_buff, BpfContext}; -pub struct SkBuffContext { +pub struct SkBuff { pub skb: *mut __sk_buff, } -impl SkBuffContext { - pub fn new(skb: *mut __sk_buff) -> SkBuffContext { - SkBuffContext { skb } +impl SkBuff { + pub fn new(skb: *mut __sk_buff) -> SkBuff { + SkBuff { skb } } #[allow(clippy::len_without_is_empty)] @@ -28,6 +28,16 @@ impl SkBuffContext { unsafe { *self.skb }.len } + #[inline] + pub(crate) fn data(&self) -> usize { + unsafe { (*self.skb).data as usize } + } + + #[inline] + pub(crate) fn data_end(&self) -> usize { + unsafe { (*self.skb).data_end as usize } + } + #[inline] pub fn set_mark(&mut self, mark: u32) { unsafe { *self.skb }.mark = mark; @@ -76,43 +86,6 @@ impl SkBuffContext { /// # Examples /// /// Read into a `PerCpuArray`. - /// - /// ```no_run - /// use core::mem; - /// - /// use aya_bpf::{bindings::TC_ACT_PIPE, macros::map, maps::PerCpuArray, programs::SkBuffContext}; - /// # #[allow(non_camel_case_types)] - /// # struct ethhdr {}; - /// # #[allow(non_camel_case_types)] - /// # struct iphdr {}; - /// # #[allow(non_camel_case_types)] - /// # struct tcphdr {}; - /// - /// const ETH_HDR_LEN: usize = mem::size_of::(); - /// const IP_HDR_LEN: usize = mem::size_of::(); - /// const TCP_HDR_LEN: usize = mem::size_of::(); - /// - /// #[repr(C)] - /// pub struct Buf { - /// pub buf: [u8; 1500], - /// } - /// - /// #[map] - /// pub static mut BUF: PerCpuArray = PerCpuArray::with_max_entries(1, 0); - /// - /// fn try_classifier(ctx: SkBuffContext) -> Result { - /// let buf = unsafe { - /// let ptr = BUF.get_ptr_mut(0).ok_or(TC_ACT_PIPE)?; - /// &mut *ptr - /// }; - /// let offset = ETH_HDR_LEN + IP_HDR_LEN + TCP_HDR_LEN; - /// ctx.load_bytes(offset, &mut buf.buf).map_err(|_| TC_ACT_PIPE)?; - /// - /// // do something with `buf` - /// - /// Ok(TC_ACT_PIPE) - /// } - /// ``` #[inline(always)] pub fn load_bytes(&self, offset: usize, dst: &mut [u8]) -> Result { if offset >= self.len() as usize { @@ -226,6 +199,161 @@ impl SkBuffContext { } } + /// Pulls in non-linear data in case the skb is non-linear. + /// + /// Make len bytes from skb readable and writable. If a zero value is passed for + /// `len`, then the whole length of the skb is pulled. This helper is only needed + /// for reading and writing with direct packet access. + #[inline(always)] + pub fn pull_data(&self, len: u32) -> Result<(), c_long> { + let ret = unsafe { bpf_skb_pull_data(self.as_ptr() as *mut _, len) }; + if ret == 0 { + Ok(()) + } else { + Err(ret) + } + } + + pub(crate) fn as_ptr(&self) -> *mut c_void { + self.skb as *mut _ + } +} + +pub struct SkBuffContext { + pub skb: SkBuff, +} + +impl SkBuffContext { + pub fn new(skb: *mut __sk_buff) -> SkBuffContext { + let skb = SkBuff { skb }; + SkBuffContext { skb } + } + + #[allow(clippy::len_without_is_empty)] + #[inline] + pub fn len(&self) -> u32 { + self.skb.len() + } + + #[inline] + pub fn set_mark(&mut self, mark: u32) { + self.skb.set_mark(mark) + } + + #[inline] + pub fn cb(&self) -> &[u32] { + self.skb.cb() + } + + #[inline] + pub fn cb_mut(&mut self) -> &mut [u32] { + self.skb.cb_mut() + } + + /// Returns the owner UID of the socket associated to the SKB context. + #[inline] + pub fn get_socket_uid(&self) -> u32 { + self.skb.get_socket_uid() + } + + #[inline] + pub fn load(&self, offset: usize) -> Result { + self.skb.load(offset) + } + + /// Reads some bytes from the packet into the specified buffer, returning + /// how many bytes were read. + /// + /// Starts reading at `offset` and reads at most `dst.len()` or + /// `self.len() - offset` bytes, depending on which one is smaller. + /// + /// # Examples + /// + /// Read into a `PerCpuArray`. + /// + /// ```no_run + /// use core::mem; + /// + /// use aya_bpf::{bindings::TC_ACT_PIPE, macros::map, maps::PerCpuArray, programs::SkBuffContext}; + /// # #[allow(non_camel_case_types)] + /// # struct ethhdr {}; + /// # #[allow(non_camel_case_types)] + /// # struct iphdr {}; + /// # #[allow(non_camel_case_types)] + /// # struct tcphdr {}; + /// + /// const ETH_HDR_LEN: usize = mem::size_of::(); + /// const IP_HDR_LEN: usize = mem::size_of::(); + /// const TCP_HDR_LEN: usize = mem::size_of::(); + /// + /// #[repr(C)] + /// pub struct Buf { + /// pub buf: [u8; 1500], + /// } + /// + /// #[map] + /// pub static mut BUF: PerCpuArray = PerCpuArray::with_max_entries(1, 0); + /// + /// fn try_cgroup_skb(ctx: SkBuffContext) -> Result { + /// let buf = unsafe { + /// let ptr = BUF.get_ptr_mut(0).ok_or(TC_ACT_PIPE)?; + /// &mut *ptr + /// }; + /// let offset = ETH_HDR_LEN + IP_HDR_LEN + TCP_HDR_LEN; + /// ctx.load_bytes(offset, &mut buf.buf).map_err(|_| TC_ACT_PIPE)?; + /// + /// // do something with `buf` + /// + /// Ok(TC_ACT_PIPE) + /// } + /// ``` + #[inline(always)] + pub fn load_bytes(&self, offset: usize, dst: &mut [u8]) -> Result { + self.skb.load_bytes(offset, dst) + } + + #[inline] + pub fn store(&mut self, offset: usize, v: &T, flags: u64) -> Result<(), c_long> { + self.skb.store(offset, v, flags) + } + + #[inline] + pub fn l3_csum_replace( + &self, + offset: usize, + from: u64, + to: u64, + size: u64, + ) -> Result<(), c_long> { + self.skb.l3_csum_replace(offset, from, to, size) + } + + #[inline] + pub fn l4_csum_replace( + &self, + offset: usize, + from: u64, + to: u64, + flags: u64, + ) -> Result<(), c_long> { + self.skb.l4_csum_replace(offset, from, to, flags) + } + + #[inline] + pub fn adjust_room(&self, len_diff: i32, mode: u32, flags: u64) -> Result<(), c_long> { + self.skb.adjust_room(len_diff, mode, flags) + } + + #[inline] + pub fn clone_redirect(&self, if_index: u32, flags: u64) -> Result<(), c_long> { + self.skb.clone_redirect(if_index, flags) + } + + #[inline] + pub fn change_type(&self, ty: u32) -> Result<(), c_long> { + self.skb.change_type(ty) + } + /// Pulls in non-linear data in case the skb is non-linear. /// /// Make len bytes from skb readable and writable. If a zero value is passed for @@ -242,7 +370,7 @@ impl SkBuffContext { /// const IP_HLEN: usize = core::mem::size_of::(); /// const UDP_HLEN: usize = core::mem::size_of::(); /// - /// fn try_classifier(ctx: SkBuffContext) -> Result { + /// fn try_cgroup_skb(ctx: SkBuffContext) -> Result { /// let len = ETH_HLEN + IP_HLEN + UDP_HLEN; /// match ctx.pull_data(len as u32) { /// Ok(_) => return Ok(0), @@ -252,17 +380,12 @@ impl SkBuffContext { /// ``` #[inline(always)] pub fn pull_data(&self, len: u32) -> Result<(), c_long> { - let ret = unsafe { bpf_skb_pull_data(self.as_ptr() as *mut _, len) }; - if ret == 0 { - Ok(()) - } else { - Err(ret) - } + self.skb.pull_data(len) } } impl BpfContext for SkBuffContext { fn as_ptr(&self) -> *mut c_void { - self.skb as *mut _ + self.skb.as_ptr() } } diff --git a/bpf/aya-bpf/src/programs/tc.rs b/bpf/aya-bpf/src/programs/tc.rs new file mode 100644 index 00000000..1d3133d5 --- /dev/null +++ b/bpf/aya-bpf/src/programs/tc.rs @@ -0,0 +1,184 @@ +use aya_bpf_cty::{c_long, c_void}; + +use crate::{bindings::__sk_buff, programs::sk_buff::SkBuff, BpfContext}; + +pub struct TcContext { + pub skb: SkBuff, +} + +impl TcContext { + pub fn new(skb: *mut __sk_buff) -> TcContext { + let skb = SkBuff { skb }; + TcContext { skb } + } + + #[allow(clippy::len_without_is_empty)] + #[inline] + pub fn len(&self) -> u32 { + self.skb.len() + } + + #[inline] + pub fn data(&self) -> usize { + self.skb.data() + } + + #[inline] + pub fn data_end(&self) -> usize { + self.skb.data_end() + } + + #[inline] + pub fn set_mark(&mut self, mark: u32) { + self.skb.set_mark(mark) + } + + #[inline] + pub fn cb(&self) -> &[u32] { + self.skb.cb() + } + + #[inline] + pub fn cb_mut(&mut self) -> &mut [u32] { + self.skb.cb_mut() + } + + /// Returns the owner UID of the socket associated to the SKB context. + #[inline] + pub fn get_socket_uid(&self) -> u32 { + self.skb.get_socket_uid() + } + + #[inline] + pub fn load(&self, offset: usize) -> Result { + self.skb.load(offset) + } + + /// Reads some bytes from the packet into the specified buffer, returning + /// how many bytes were read. + /// + /// Starts reading at `offset` and reads at most `dst.len()` or + /// `self.len() - offset` bytes, depending on which one is smaller. + /// + /// # Examples + /// + /// Read into a `PerCpuArray`. + /// + /// ```no_run + /// use core::mem; + /// + /// use aya_bpf::{bindings::TC_ACT_PIPE, macros::map, maps::PerCpuArray, programs::TcContext}; + /// # #[allow(non_camel_case_types)] + /// # struct ethhdr {}; + /// # #[allow(non_camel_case_types)] + /// # struct iphdr {}; + /// # #[allow(non_camel_case_types)] + /// # struct tcphdr {}; + /// + /// const ETH_HDR_LEN: usize = mem::size_of::(); + /// const IP_HDR_LEN: usize = mem::size_of::(); + /// const TCP_HDR_LEN: usize = mem::size_of::(); + /// + /// #[repr(C)] + /// pub struct Buf { + /// pub buf: [u8; 1500], + /// } + /// + /// #[map] + /// pub static mut BUF: PerCpuArray = PerCpuArray::with_max_entries(1, 0); + /// + /// fn try_classifier(ctx: TcContext) -> Result { + /// let buf = unsafe { + /// let ptr = BUF.get_ptr_mut(0).ok_or(TC_ACT_PIPE)?; + /// &mut *ptr + /// }; + /// let offset = ETH_HDR_LEN + IP_HDR_LEN + TCP_HDR_LEN; + /// ctx.load_bytes(offset, &mut buf.buf).map_err(|_| TC_ACT_PIPE)?; + /// + /// // do something with `buf` + /// + /// Ok(TC_ACT_PIPE) + /// } + /// ``` + #[inline(always)] + pub fn load_bytes(&self, offset: usize, dst: &mut [u8]) -> Result { + self.skb.load_bytes(offset, dst) + } + + #[inline] + pub fn store(&mut self, offset: usize, v: &T, flags: u64) -> Result<(), c_long> { + self.skb.store(offset, v, flags) + } + + #[inline] + pub fn l3_csum_replace( + &self, + offset: usize, + from: u64, + to: u64, + size: u64, + ) -> Result<(), c_long> { + self.skb.l3_csum_replace(offset, from, to, size) + } + + #[inline] + pub fn l4_csum_replace( + &self, + offset: usize, + from: u64, + to: u64, + flags: u64, + ) -> Result<(), c_long> { + self.skb.l4_csum_replace(offset, from, to, flags) + } + + #[inline] + pub fn adjust_room(&self, len_diff: i32, mode: u32, flags: u64) -> Result<(), c_long> { + self.skb.adjust_room(len_diff, mode, flags) + } + + #[inline] + pub fn clone_redirect(&self, if_index: u32, flags: u64) -> Result<(), c_long> { + self.skb.clone_redirect(if_index, flags) + } + + #[inline] + pub fn change_type(&self, ty: u32) -> Result<(), c_long> { + self.skb.change_type(ty) + } + + /// Pulls in non-linear data in case the skb is non-linear. + /// + /// Make len bytes from skb readable and writable. If a zero value is passed for + /// `len`, then the whole length of the skb is pulled. This helper is only needed + /// for reading and writing with direct packet access. + /// + /// # Examples + /// + /// ```no_run + /// mod bindings; + /// use bindings::{ethhdr, iphdr, udphdr}; + /// + /// const ETH_HLEN: usize = core::mem::size_of::(); + /// const IP_HLEN: usize = core::mem::size_of::(); + /// const UDP_HLEN: usize = core::mem::size_of::(); + /// + /// fn try_classifier(ctx: TcContext) -> Result { + /// let len = ETH_HLEN + IP_HLEN + UDP_HLEN; + /// match ctx.pull_data(len as u32) { + /// Ok(_) => return Ok(0), + /// Err(ret) => return Err(ret as i32), + /// } + /// } + /// ``` + #[inline(always)] + pub fn pull_data(&self, len: u32) -> Result<(), c_long> { + self.skb.pull_data(len) + } +} + +impl BpfContext for TcContext { + fn as_ptr(&self) -> *mut c_void { + self.skb.as_ptr() + } +}