diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index e410d15f..0d219ac1 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -24,8 +24,8 @@ use crate::{ programs::{ BtfTracePoint, CgroupSkb, CgroupSkbAttachType, CgroupSockAddr, CgroupSockopt, CgroupSysctl, Extension, FEntry, FExit, KProbe, LircMode2, Lsm, PerfEvent, ProbeKind, Program, - ProgramData, ProgramError, RawTracePoint, SchedClassifier, SkMsg, SkSkb, SkSkbKind, - SockOps, SocketFilter, TracePoint, UProbe, Xdp, + ProgramData, ProgramError, RawTracePoint, SchedClassifier, SkLookup, SkMsg, SkSkb, + SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp, }, sys::{ bpf_load_btf, bpf_map_freeze, bpf_map_update_elem_ptr, is_btf_datasec_supported, @@ -521,6 +521,9 @@ impl<'a> BpfLoader<'a> { ProgramSection::Extension { .. } => Program::Extension(Extension { data: ProgramData::new(prog_name, obj, btf_fd), }), + ProgramSection::SkLookup { .. } => Program::SkLookup(SkLookup { + data: ProgramData::new(prog_name, obj, btf_fd), + }), } }; (name, program) diff --git a/aya/src/obj/mod.rs b/aya/src/obj/mod.rs index d71e3205..df69ec30 100644 --- a/aya/src/obj/mod.rs +++ b/aya/src/obj/mod.rs @@ -185,6 +185,9 @@ pub enum ProgramSection { Extension { name: String, }, + SkLookup { + name: String, + }, } impl ProgramSection { @@ -216,6 +219,7 @@ impl ProgramSection { ProgramSection::FEntry { name } => name, ProgramSection::FExit { name } => name, ProgramSection::Extension { name } => name, + ProgramSection::SkLookup { name } => name, } } } @@ -361,6 +365,7 @@ impl FromStr for ProgramSection { "fentry" => FEntry { name }, "fexit" => FExit { name }, "freplace" => Extension { name }, + "sk_lookup" => SkLookup { name }, _ => { return Err(ParseError::InvalidProgramSection { section: section.to_owned(), diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index ff3dc7b1..a148a8e2 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -50,11 +50,12 @@ pub mod lsm; pub mod perf_attach; pub mod perf_event; mod probe; -pub mod raw_trace_point; -pub mod sk_msg; -pub mod sk_skb; -pub mod sock_ops; -pub mod socket_filter; +mod raw_trace_point; +mod sk_lookup; +mod sk_msg; +mod sk_skb; +mod sock_ops; +mod socket_filter; pub mod tc; pub mod tp_btf; pub mod trace_point; @@ -88,6 +89,7 @@ use perf_attach::*; pub use perf_event::{PerfEvent, PerfEventScope, PerfTypeId, SamplePolicy}; pub use probe::ProbeKind; pub use raw_trace_point::RawTracePoint; +pub use sk_lookup::SkLookup; pub use sk_msg::SkMsg; pub use sk_skb::{SkSkb, SkSkbKind}; pub use sock_ops::SockOps; @@ -261,6 +263,8 @@ pub enum Program { FExit(FExit), /// A [`Extension`] program Extension(Extension), + /// A [`SkLookup`] program + SkLookup(SkLookup), } impl Program { @@ -289,6 +293,7 @@ impl Program { Program::FExit(_) => BPF_PROG_TYPE_TRACING, Program::Extension(_) => BPF_PROG_TYPE_EXT, Program::CgroupSockAddr(_) => BPF_PROG_TYPE_CGROUP_SOCK_ADDR, + Program::SkLookup(_) => BPF_PROG_TYPE_SK_LOOKUP, } } @@ -316,6 +321,7 @@ impl Program { Program::FExit(p) => p.data.pin(path), Program::Extension(p) => p.data.pin(path), Program::CgroupSockAddr(p) => p.data.pin(path), + Program::SkLookup(p) => p.data.pin(path), } } } @@ -523,6 +529,7 @@ impl ProgramFd for Program { Program::FExit(p) => p.data.fd, Program::Extension(p) => p.data.fd, Program::CgroupSockAddr(p) => p.data.fd, + Program::SkLookup(p) => p.data.fd, } } } @@ -572,6 +579,7 @@ impl_program_fd!( FExit, Extension, CgroupSockAddr, + SkLookup, ); macro_rules! impl_try_from_program { @@ -624,6 +632,7 @@ impl_try_from_program!( FExit, Extension, CgroupSockAddr, + SkLookup, ); /// Provides information about a loaded program, like name, id and statistics diff --git a/aya/src/programs/sk_lookup.rs b/aya/src/programs/sk_lookup.rs new file mode 100644 index 00000000..4f8e2f03 --- /dev/null +++ b/aya/src/programs/sk_lookup.rs @@ -0,0 +1,103 @@ +use std::os::unix::prelude::{AsRawFd, RawFd}; + +use crate::{ + generated::{bpf_attach_type::BPF_SK_LOOKUP, bpf_prog_type::BPF_PROG_TYPE_SK_LOOKUP}, + programs::{define_link_wrapper, load_program, FdLinkId, OwnedLink, ProgramData, ProgramError}, + sys::bpf_link_create, +}; + +use super::links::FdLink; + +/// A program used to redirect incoming packets to a local socket. +/// +/// [`SkLookup`] programs are attached to network namespaces to provide programmable +/// socket lookup for TCP/UDP when a packet is to be delievered locally. +/// +/// You may attach multiple programs to the same namespace and they are executed +/// in the order they were attached. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 5.9. +/// +/// # 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(&[])?; +/// use std::fs::File; +/// use std::convert::TryInto; +/// use aya::programs::SkLookup; +/// +/// let file = File::open("/var/run/netns/test")?; +/// let program: &mut SkLookup = bpf.program_mut("sk_lookup").unwrap().try_into()?; +/// program.load()?; +/// program.attach(file)?; +/// # Ok::<(), Error>(()) +/// ``` +#[derive(Debug)] +#[doc(alias = "BPF_PROG_TYPE_SK_LOOKUP")] +pub struct SkLookup { + pub(crate) data: ProgramData, +} + +impl SkLookup { + /// Loads the program inside the kernel. + pub fn load(&mut self) -> Result<(), ProgramError> { + self.data.expected_attach_type = Some(BPF_SK_LOOKUP); + load_program(BPF_PROG_TYPE_SK_LOOKUP, &mut self.data) + } + + /// Attaches the program to the given network namespace. + /// + /// The returned value can be used to detach, see [SkLookup::detach]. + pub fn attach(&mut self, netns: T) -> Result { + let prog_fd = self.data.fd_or_err()?; + let netns_fd = netns.as_raw_fd(); + + let link_fd = bpf_link_create(prog_fd, netns_fd, BPF_SK_LOOKUP, None, 0).map_err( + |(_, io_error)| ProgramError::SyscallError { + call: "bpf_link_create".to_owned(), + io_error, + }, + )? as RawFd; + self.data.links.insert(SkLookupLink(FdLink::new(link_fd))) + } + + /// Takes ownership of the link referenced by the provided link_id. + /// + /// The link will be detached on `Drop` and the caller is now responsible + /// for managing its lifetime. + pub fn forget_link( + &mut self, + link_id: SkLookupLinkId, + ) -> Result, ProgramError> { + Ok(OwnedLink::new(self.data.forget_link(link_id)?)) + } + + /// Detaches the program. + /// + /// See [SkLookup::attach]. + pub fn detach(&mut self, link_id: SkLookupLinkId) -> Result<(), ProgramError> { + self.data.links.remove(link_id) + } +} + +define_link_wrapper!( + /// The link used by [SkLookup] programs. + SkLookupLink, + /// The type returned by [SkLookup::attach]. Can be passed to [SkLookup::detach]. + SkLookupLinkId, + FdLink, + FdLinkId +); diff --git a/bpf/aya-bpf-macros/src/expand.rs b/bpf/aya-bpf-macros/src/expand.rs index 77877140..8a047558 100644 --- a/bpf/aya-bpf-macros/src/expand.rs +++ b/bpf/aya-bpf-macros/src/expand.rs @@ -743,6 +743,38 @@ impl FExit { } } +pub struct SkLookup { + item: ItemFn, + name: Option, +} + +impl SkLookup { + pub fn from_syn(mut args: Args, item: ItemFn) -> Result { + let name = name_arg(&mut args)?; + + Ok(SkLookup { item, name }) + } + + pub fn expand(&self) -> Result { + let section_name = if let Some(name) = &self.name { + format!("sk_lookup/{}", name) + } else { + "sk_lookup".to_owned() + }; + let fn_name = &self.item.sig.ident; + let item = &self.item; + Ok(quote! { + #[no_mangle] + #[link_section = #section_name] + fn #fn_name(ctx: *mut ::aya_bpf::bindings::bpf_sk_lookup) -> u32 { + return #fn_name(::aya_bpf::programs::SkLookupContext::new(ctx)); + + #item + } + }) + } +} + #[cfg(test)] mod tests { use syn::parse_quote; diff --git a/bpf/aya-bpf-macros/src/lib.rs b/bpf/aya-bpf-macros/src/lib.rs index 853b50a8..a22e9d25 100644 --- a/bpf/aya-bpf-macros/src/lib.rs +++ b/bpf/aya-bpf-macros/src/lib.rs @@ -2,8 +2,8 @@ mod expand; use expand::{ Args, BtfTracePoint, CgroupSkb, CgroupSockAddr, CgroupSockopt, CgroupSysctl, FEntry, FExit, - Lsm, Map, PerfEvent, Probe, ProbeKind, RawTracePoint, SchedClassifier, SkMsg, SkSkb, SkSkbKind, - SockAddrArgs, SockOps, SocketFilter, SockoptArgs, TracePoint, Xdp, + Lsm, Map, PerfEvent, Probe, ProbeKind, RawTracePoint, SchedClassifier, SkLookup, SkMsg, SkSkb, + SkSkbKind, SockAddrArgs, SockOps, SocketFilter, SockoptArgs, TracePoint, Xdp, }; use proc_macro::TokenStream; use syn::{parse_macro_input, ItemFn, ItemStatic}; @@ -467,3 +467,32 @@ pub fn fexit(attrs: TokenStream, item: TokenStream) -> TokenStream { .unwrap_or_else(|err| err.to_compile_error()) .into() } + +/// Marks a function as an eBPF Socket Lookup program that can be attached to +/// a network namespace. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 5.9 +/// +/// # Examples +/// +/// ```no_run +/// use aya_bpf::{macros::sk_lookup, programs::SkLookupContext}; +/// +/// #[sk_lookup(name = "redirect")] +/// pub fn accept_all(_ctx: SkLookupContext) -> u32 { +/// // use sk_assign to redirect +/// return 0 +/// } +/// ``` +#[proc_macro_attribute] +pub fn sk_lookup(attrs: TokenStream, item: TokenStream) -> TokenStream { + let args = parse_macro_input!(attrs as Args); + let item = parse_macro_input!(item as ItemFn); + + SkLookup::from_syn(args, item) + .and_then(|u| u.expand()) + .unwrap_or_else(|err| err.to_compile_error()) + .into() +} diff --git a/bpf/aya-bpf/src/maps/sock_hash.rs b/bpf/aya-bpf/src/maps/sock_hash.rs index 3148ee24..cd3d0c04 100644 --- a/bpf/aya-bpf/src/maps/sock_hash.rs +++ b/bpf/aya-bpf/src/maps/sock_hash.rs @@ -4,9 +4,12 @@ use aya_bpf_cty::c_void; use crate::{ bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_SOCKHASH, bpf_sock_ops}, - helpers::{bpf_msg_redirect_hash, bpf_sk_redirect_hash, bpf_sock_hash_update}, + helpers::{ + bpf_map_lookup_elem, bpf_msg_redirect_hash, bpf_sk_assign, bpf_sk_redirect_hash, + bpf_sk_release, bpf_sock_hash_update, + }, maps::PinningType, - programs::{SkBuffContext, SkMsgContext}, + programs::{SkBuffContext, SkLookupContext, SkMsgContext}, BpfContext, }; @@ -85,4 +88,24 @@ impl SockHash { ) } } + + pub fn redirect_sk_lookup( + &mut self, + ctx: &SkLookupContext, + key: K, + flags: u64, + ) -> Result<(), u32> { + unsafe { + let sk = bpf_map_lookup_elem( + &mut self.def as *mut _ as *mut _, + &key as *const _ as *const c_void, + ); + if sk.is_null() { + return Err(1); + } + let ret = bpf_sk_assign(ctx.as_ptr() as *mut _, sk, flags); + bpf_sk_release(sk); + (ret >= 0).then(|| ()).ok_or(1) + } + } } diff --git a/bpf/aya-bpf/src/maps/sock_map.rs b/bpf/aya-bpf/src/maps/sock_map.rs index 7d546419..907d288c 100644 --- a/bpf/aya-bpf/src/maps/sock_map.rs +++ b/bpf/aya-bpf/src/maps/sock_map.rs @@ -4,9 +4,12 @@ use aya_bpf_cty::c_void; use crate::{ bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_SOCKMAP, bpf_sock_ops}, - helpers::{bpf_msg_redirect_map, bpf_sk_redirect_map, bpf_sock_map_update}, + helpers::{ + bpf_map_lookup_elem, bpf_msg_redirect_map, bpf_sk_assign, bpf_sk_redirect_map, + bpf_sk_release, bpf_sock_map_update, + }, maps::PinningType, - programs::{SkBuffContext, SkMsgContext}, + programs::{SkBuffContext, SkLookupContext, SkMsgContext}, BpfContext, }; @@ -80,4 +83,24 @@ impl SockMap { flags, ) } + + pub fn redirect_sk_lookup( + &mut self, + ctx: &SkLookupContext, + index: u32, + flags: u64, + ) -> Result<(), u32> { + unsafe { + let sk = bpf_map_lookup_elem( + &mut self.def as *mut _ as *mut _, + &index as *const _ as *const c_void, + ); + if sk.is_null() { + return Err(1); + } + let ret = bpf_sk_assign(ctx.as_ptr() as *mut _, sk, flags); + bpf_sk_release(sk); + (ret >= 0).then(|| ()).ok_or(1) + } + } } diff --git a/bpf/aya-bpf/src/programs/mod.rs b/bpf/aya-bpf/src/programs/mod.rs index ceaed5f8..c2c8034c 100644 --- a/bpf/aya-bpf/src/programs/mod.rs +++ b/bpf/aya-bpf/src/programs/mod.rs @@ -5,6 +5,7 @@ pub mod perf_event; pub mod probe; pub mod raw_tracepoint; pub mod sk_buff; +pub mod sk_lookup; pub mod sk_msg; pub mod sock_addr; pub mod sock_ops; @@ -21,6 +22,7 @@ pub use perf_event::PerfEventContext; pub use probe::ProbeContext; pub use raw_tracepoint::RawTracePointContext; pub use sk_buff::SkBuffContext; +pub use sk_lookup::SkLookupContext; pub use sk_msg::SkMsgContext; pub use sock_addr::SockAddrContext; pub use sock_ops::SockOpsContext; diff --git a/bpf/aya-bpf/src/programs/sk_lookup.rs b/bpf/aya-bpf/src/programs/sk_lookup.rs new file mode 100644 index 00000000..5c37066e --- /dev/null +++ b/bpf/aya-bpf/src/programs/sk_lookup.rs @@ -0,0 +1,19 @@ +use core::ffi::c_void; + +use crate::{bindings::bpf_sk_lookup, BpfContext}; + +pub struct SkLookupContext { + pub lookup: *mut bpf_sk_lookup, +} + +impl SkLookupContext { + pub fn new(lookup: *mut bpf_sk_lookup) -> SkLookupContext { + SkLookupContext { lookup } + } +} + +impl BpfContext for SkLookupContext { + fn as_ptr(&self) -> *mut c_void { + self.lookup as *mut _ + } +}