From b9f2b6cf66348ce67aeaf0db7f16d127ce95aa8d Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Fri, 12 Jul 2024 14:08:50 +0100 Subject: [PATCH] wip Signed-off-by: Dave Tucker --- aya-log/Cargo.toml | 1 + aya-log/src/lib.rs | 8 +- aya-obj/src/attach.rs | 185 +++++++++++ aya-obj/src/btf/btf.rs | 2 +- aya-obj/src/cmd.rs | 126 +++++++ aya-obj/src/lib.rs | 3 + aya-obj/src/links.rs | 57 ++++ aya-obj/src/maps.rs | 120 ++++++- aya-obj/src/obj.rs | 2 +- aya-obj/src/programs/mod.rs | 115 +++++++ aya/src/bpf.rs | 419 ++++++++++++++++++++---- aya/src/lib.rs | 1 + aya/src/maps/mod.rs | 89 +++-- aya/src/maps/xdp/cpu_map.rs | 10 +- aya/src/maps/xdp/dev_map.rs | 11 +- aya/src/maps/xdp/dev_map_hash.rs | 9 +- aya/src/programs/cgroup_skb.rs | 14 +- aya/src/programs/cgroup_sock.rs | 13 +- aya/src/programs/cgroup_sock_addr.rs | 13 +- aya/src/programs/cgroup_sockopt.rs | 13 +- aya/src/programs/kprobe.rs | 24 +- aya/src/programs/mod.rs | 36 +- aya/src/programs/perf_attach.rs | 7 +- aya/src/programs/perf_event.rs | 2 +- aya/src/programs/probe.rs | 4 +- aya/src/programs/sk_skb.rs | 18 +- aya/src/programs/tc.rs | 13 +- aya/src/programs/trace_point.rs | 2 +- aya/src/programs/uprobe.rs | 27 +- aya/src/programs/xdp.rs | 9 +- aya/src/sys/bpf.rs | 417 +++++++++++++++++++++-- aya/src/sys/mod.rs | 101 +++++- test/integration-test/src/tests/load.rs | 52 ++- 33 files changed, 1725 insertions(+), 198 deletions(-) create mode 100644 aya-obj/src/attach.rs create mode 100644 aya-obj/src/cmd.rs create mode 100644 aya-obj/src/links.rs diff --git a/aya-log/Cargo.toml b/aya-log/Cargo.toml index 43fc762b..9ff57b7b 100644 --- a/aya-log/Cargo.toml +++ b/aya-log/Cargo.toml @@ -13,6 +13,7 @@ edition.workspace = true [dependencies] aya = { path = "../aya", version = "^0.12.0", features = ["async_tokio"] } +aya-obj = { path = "../aya-obj", version = "^0.1.0" } aya-log-common = { path = "../aya-log-common", version = "^0.1.14", default-features = false } bytes = { workspace = true } log = { workspace = true } diff --git a/aya-log/src/lib.rs b/aya-log/src/lib.rs index 8c89c115..e84767fe 100644 --- a/aya-log/src/lib.rs +++ b/aya-log/src/lib.rs @@ -72,6 +72,7 @@ use aya::{ use aya_log_common::{ Argument, DisplayHint, Level, LogValueLength, RecordField, LOG_BUF_CAPACITY, LOG_FIELDS, }; +use aya_obj::Features; use bytes::BytesMut; use log::{error, Log, Record}; use thiserror::Error; @@ -123,8 +124,8 @@ impl EbpfLogger { /// /// Attaches to the logs produced by `program_id`. Can be used to read logs generated by a /// pinned program. The log records will be written to the default logger. See [log::logger]. - pub fn init_from_id(program_id: u32) -> Result { - Self::init_from_id_with_logger(program_id, log::logger()) + pub fn init_from_id(program_id: u32, features: Features) -> Result { + Self::init_from_id_with_logger(program_id, log::logger(), features) } /// Attaches to an existing `aya-log-ebpf` instance and logs with the given logger. @@ -134,6 +135,7 @@ impl EbpfLogger { pub fn init_from_id_with_logger( program_id: u32, logger: T, + features: Features, ) -> Result { let program_info = loaded_programs() .filter_map(|info| info.ok()) @@ -149,7 +151,7 @@ impl EbpfLogger { None => false, }) .ok_or(Error::MapNotFound)?; - let map = MapData::from_id(map.id()).map_err(Error::MapError)?; + let map = MapData::from_id(map.id(), features.clone()).map_err(Error::MapError)?; Self::read_logs_async(Map::PerfEventArray(map), logger)?; diff --git a/aya-obj/src/attach.rs b/aya-obj/src/attach.rs new file mode 100644 index 00000000..82f27294 --- /dev/null +++ b/aya-obj/src/attach.rs @@ -0,0 +1,185 @@ +//! Link types for BPFFS Permissions + +use crate::generated::bpf_attach_type; + +/// The type of BPF link +#[derive(Copy, Clone, Debug)] +pub enum BpfAttachType { + /// Cgroup Inet Ingress + CgroupInetIngress, + /// Cgroup Inet Egress + CgroupInetEgress, + /// Cgroup Inet Sock Create + CgroupInetSockCreate, + /// Cgroup Sock Ops + CgroupSockOps, + /// Sk Skb Stream Parser + SkSkbStreamParser, + /// Sk Skb Stream Verdict + SkSkbStreamVerdict, + /// Cgroup Device + CgroupDevice, + /// Sk Msg Verdict + SkMsgVerdict, + /// Cgroup Inet4 Bind + CgroupInet4Bind, + /// Cgroup Inet6 Bind + CgroupInet6Bind, + /// Cgroup Inet4 Connect + CgroupInet4Connect, + /// Cgroup Inet6 Connect + CgroupInet6Connect, + /// Cgroup Inet4 Post Bind + CgroupInet4PostBind, + /// Cgroup Inet6 Post Bind + CgroupInet6PostBind, + /// Cgroup Udp4 Sendmsg + CgroupUdp4Sendmsg, + /// Cgroup Udp6 Sendmsg + CgroupUdp6Sendmsg, + /// Lirc Mode2 + LircMode2, + /// Flow Dissector + FlowDissector, + /// Cgroup Sysctl + CgroupSysctl, + /// Cgroup Udp4 Recvmsg + CgroupUdp4Recvmsg, + /// Cgroup Udp6 Recvmsg + CgroupUdp6Recvmsg, + /// Cgroup Getsockopt + CgroupGetsockopt, + /// Cgroup Setsockopt + CgroupSetsockopt, + /// Trace Raw Tp + TraceRawTp, + /// Trace Fentry + TraceFentry, + /// Trace Fexit + TraceFexit, + /// Modify Return + ModifyReturn, + /// Lsm Mac + LsmMac, + /// Trace Iter + TraceIter, + /// Cgroup Inet4 Getpeername + CgroupInet4Getpeername, + /// Cgroup Inet6 Getpeername + CgroupInet6Getpeername, + /// Cgroup Inet4 Getsockname + CgroupInet4Getsockname, + /// Cgroup Inet6 Getsockname + CgroupInet6Getsockname, + /// Xdp Devmap + XdpDevmap, + /// Cgroup Inet Sock Release + CgroupInetSockRelease, + /// Xdp Cpumap + XdpCpumap, + /// Sk Lookup + SkLookup, + /// Xdp + Xdp, + /// Sk Skb Verdict + SkSkbVerdict, + /// Sk Reuseport Select + SkReuseportSelect, + /// Sk Reuseport Select Or Migrate + SkReuseportSelectOrMigrate, + /// Perf Event + PerfEvent, + /// Trace Kprobe Multi + TraceKprobeMulti, + /// Lsm Cgroup + LsmCgroup, + /// Struct Ops + StructOps, + /// Netfilter + Netfilter, + /// Tcx Ingress + TcxIngress, + /// Tcx Egress + TcxEgress, + /// Trace Uprobe Multi + TraceUprobeMulti, + /// Cgroup Unix Connect + CgroupUnixConnect, + /// Cgroup Unix Sendmsg + CgroupUnixSendmsg, + /// Cgroup Unix Recvmsg + CgroupUnixRecvmsg, + /// Cgroup Unix Getpeername + CgroupUnixGetpeername, + /// Cgroup Unix Getsockname + CgroupUnixGetsockname, + /// Netkit Primary + NetkitPrimary, + /// Netkit Peer + NetkitPeer, +} + +impl From for bpf_attach_type { + fn from(attach_type: BpfAttachType) -> Self { + match attach_type { + BpfAttachType::CgroupInetIngress => bpf_attach_type::BPF_CGROUP_INET_INGRESS, + BpfAttachType::CgroupInetEgress => bpf_attach_type::BPF_CGROUP_INET_EGRESS, + BpfAttachType::CgroupInetSockCreate => bpf_attach_type::BPF_CGROUP_INET_SOCK_CREATE, + BpfAttachType::CgroupSockOps => bpf_attach_type::BPF_CGROUP_SOCK_OPS, + BpfAttachType::SkSkbStreamParser => bpf_attach_type::BPF_SK_SKB_STREAM_PARSER, + BpfAttachType::SkSkbStreamVerdict => bpf_attach_type::BPF_SK_SKB_STREAM_VERDICT, + BpfAttachType::CgroupDevice => bpf_attach_type::BPF_CGROUP_DEVICE, + BpfAttachType::SkMsgVerdict => bpf_attach_type::BPF_SK_MSG_VERDICT, + BpfAttachType::CgroupInet4Bind => bpf_attach_type::BPF_CGROUP_INET4_BIND, + BpfAttachType::CgroupInet6Bind => bpf_attach_type::BPF_CGROUP_INET6_BIND, + BpfAttachType::CgroupInet4Connect => bpf_attach_type::BPF_CGROUP_INET4_CONNECT, + BpfAttachType::CgroupInet6Connect => bpf_attach_type::BPF_CGROUP_INET6_CONNECT, + BpfAttachType::CgroupInet4PostBind => bpf_attach_type::BPF_CGROUP_INET4_POST_BIND, + BpfAttachType::CgroupInet6PostBind => bpf_attach_type::BPF_CGROUP_INET6_POST_BIND, + BpfAttachType::CgroupUdp4Sendmsg => bpf_attach_type::BPF_CGROUP_UDP4_SENDMSG, + BpfAttachType::CgroupUdp6Sendmsg => bpf_attach_type::BPF_CGROUP_UDP6_SENDMSG, + BpfAttachType::LircMode2 => bpf_attach_type::BPF_LIRC_MODE2, + BpfAttachType::FlowDissector => bpf_attach_type::BPF_FLOW_DISSECTOR, + BpfAttachType::CgroupSysctl => bpf_attach_type::BPF_CGROUP_SYSCTL, + BpfAttachType::CgroupUdp4Recvmsg => bpf_attach_type::BPF_CGROUP_UDP4_RECVMSG, + BpfAttachType::CgroupUdp6Recvmsg => bpf_attach_type::BPF_CGROUP_UDP6_RECVMSG, + BpfAttachType::CgroupGetsockopt => bpf_attach_type::BPF_CGROUP_GETSOCKOPT, + BpfAttachType::CgroupSetsockopt => bpf_attach_type::BPF_CGROUP_SETSOCKOPT, + BpfAttachType::TraceRawTp => bpf_attach_type::BPF_TRACE_RAW_TP, + BpfAttachType::TraceFentry => bpf_attach_type::BPF_TRACE_FENTRY, + BpfAttachType::TraceFexit => bpf_attach_type::BPF_TRACE_FEXIT, + BpfAttachType::ModifyReturn => bpf_attach_type::BPF_MODIFY_RETURN, + BpfAttachType::LsmMac => bpf_attach_type::BPF_LSM_MAC, + BpfAttachType::TraceIter => bpf_attach_type::BPF_TRACE_ITER, + BpfAttachType::CgroupInet4Getpeername => bpf_attach_type::BPF_CGROUP_INET4_GETPEERNAME, + BpfAttachType::CgroupInet6Getpeername => bpf_attach_type::BPF_CGROUP_INET6_GETPEERNAME, + BpfAttachType::CgroupInet4Getsockname => bpf_attach_type::BPF_CGROUP_INET4_GETSOCKNAME, + BpfAttachType::CgroupInet6Getsockname => bpf_attach_type::BPF_CGROUP_INET6_GETSOCKNAME, + BpfAttachType::XdpDevmap => bpf_attach_type::BPF_XDP_DEVMAP, + BpfAttachType::CgroupInetSockRelease => bpf_attach_type::BPF_CGROUP_INET_SOCK_RELEASE, + BpfAttachType::XdpCpumap => bpf_attach_type::BPF_XDP_CPUMAP, + BpfAttachType::SkLookup => bpf_attach_type::BPF_SK_LOOKUP, + BpfAttachType::Xdp => bpf_attach_type::BPF_XDP, + BpfAttachType::SkSkbVerdict => bpf_attach_type::BPF_SK_SKB_VERDICT, + BpfAttachType::SkReuseportSelect => bpf_attach_type::BPF_SK_REUSEPORT_SELECT, + BpfAttachType::SkReuseportSelectOrMigrate => { + bpf_attach_type::BPF_SK_REUSEPORT_SELECT_OR_MIGRATE + } + BpfAttachType::PerfEvent => bpf_attach_type::BPF_PERF_EVENT, + BpfAttachType::TraceKprobeMulti => bpf_attach_type::BPF_TRACE_KPROBE_MULTI, + BpfAttachType::LsmCgroup => bpf_attach_type::BPF_LSM_CGROUP, + BpfAttachType::StructOps => bpf_attach_type::BPF_STRUCT_OPS, + BpfAttachType::Netfilter => bpf_attach_type::BPF_NETFILTER, + BpfAttachType::TcxIngress => bpf_attach_type::BPF_TCX_INGRESS, + BpfAttachType::TcxEgress => bpf_attach_type::BPF_TCX_EGRESS, + BpfAttachType::TraceUprobeMulti => bpf_attach_type::BPF_TRACE_UPROBE_MULTI, + BpfAttachType::CgroupUnixConnect => bpf_attach_type::BPF_CGROUP_UNIX_CONNECT, + BpfAttachType::CgroupUnixSendmsg => bpf_attach_type::BPF_CGROUP_UNIX_SENDMSG, + BpfAttachType::CgroupUnixRecvmsg => bpf_attach_type::BPF_CGROUP_UNIX_RECVMSG, + BpfAttachType::CgroupUnixGetpeername => bpf_attach_type::BPF_CGROUP_UNIX_GETPEERNAME, + BpfAttachType::CgroupUnixGetsockname => bpf_attach_type::BPF_CGROUP_UNIX_GETSOCKNAME, + BpfAttachType::NetkitPrimary => bpf_attach_type::BPF_NETKIT_PRIMARY, + BpfAttachType::NetkitPeer => bpf_attach_type::BPF_NETKIT_PEER, + } + } +} diff --git a/aya-obj/src/btf/btf.rs b/aya-obj/src/btf/btf.rs index ef7217ad..5b2cde50 100644 --- a/aya-obj/src/btf/btf.rs +++ b/aya-obj/src/btf/btf.rs @@ -160,7 +160,7 @@ pub enum BtfError { } /// Available BTF features -#[derive(Default, Debug)] +#[derive(Default, Debug, Clone)] #[allow(missing_docs)] pub struct BtfFeatures { btf_func: bool, diff --git a/aya-obj/src/cmd.rs b/aya-obj/src/cmd.rs new file mode 100644 index 00000000..003bb62b --- /dev/null +++ b/aya-obj/src/cmd.rs @@ -0,0 +1,126 @@ +//! Command types for BPFFS Permissions + +use crate::generated::bpf_cmd; + +/// The type of BPF link +#[derive(Copy, Clone, Debug)] +pub enum BpfCommand { + /// Map Create + MapCreate, + /// Map Lookup Element + MapLookupElem, + /// Map Update Element + MapUpdateElem, + /// Map Delete Element + MapDeleteElem, + /// Map Get Next Key + MapGetNextKey, + /// Program Load + ProgLoad, + /// Object Pin + ObjPin, + /// Object Get + ObjGet, + /// Program Attach + ProgAttach, + /// Program Detach + ProgDetach, + /// Program Test Run + ProgTestRun, + /// Program Get Next Id + ProgGetNextId, + /// Map Get Next Id + MapGetNextId, + /// Program Get FD By Id + ProgGetFdById, + /// Map Get FD By Id + MapGetFdById, + /// Object Get Info By FD + ObjGetInfoByFd, + /// Program Query + ProgQuery, + /// Raw Tracepoint Open + RawTracepointOpen, + /// BTF Load + BtfLoad, + /// BTF Get FD By Id + BtfGetFdById, + /// Task FD Query + TaskFdQuery, + /// Map Lookup And Delete Element + MapLookupAndDeleteElem, + /// Map Freeze + MapFreeze, + /// BTF Get Next Id + BtfGetNextId, + /// Map Lookup Batch + MapLookupBatch, + /// Map Lookup And Delete Batch + MapLookupAndDeleteBatch, + /// Map Update Batch + MapUpdateBatch, + /// Map Delete Batch + MapDeleteBatch, + /// Link Create + LinkCreate, + /// Link Update + LinkUpdate, + /// Link Get FD By Id + LinkGetFdById, + /// Link Get Next Id + LinkGetNextId, + /// Enable Stats + EnableStats, + /// Iter Create + IterCreate, + /// Link Detach + LinkDetach, + /// Program Bind Map + ProgBindMap, + /// Token Create + TokenCreate, +} + +impl From for bpf_cmd { + fn from(value: BpfCommand) -> Self { + match value { + BpfCommand::MapCreate => bpf_cmd::BPF_MAP_CREATE, + BpfCommand::MapLookupElem => bpf_cmd::BPF_MAP_LOOKUP_ELEM, + BpfCommand::MapUpdateElem => bpf_cmd::BPF_MAP_UPDATE_ELEM, + BpfCommand::MapDeleteElem => bpf_cmd::BPF_MAP_DELETE_ELEM, + BpfCommand::MapGetNextKey => bpf_cmd::BPF_MAP_GET_NEXT_KEY, + BpfCommand::ProgLoad => bpf_cmd::BPF_PROG_LOAD, + BpfCommand::ObjPin => bpf_cmd::BPF_OBJ_PIN, + BpfCommand::ObjGet => bpf_cmd::BPF_OBJ_GET, + BpfCommand::ProgAttach => bpf_cmd::BPF_PROG_ATTACH, + BpfCommand::ProgDetach => bpf_cmd::BPF_PROG_DETACH, + BpfCommand::ProgTestRun => bpf_cmd::BPF_PROG_TEST_RUN, + BpfCommand::ProgGetNextId => bpf_cmd::BPF_PROG_GET_NEXT_ID, + BpfCommand::MapGetNextId => bpf_cmd::BPF_MAP_GET_NEXT_ID, + BpfCommand::ProgGetFdById => bpf_cmd::BPF_PROG_GET_FD_BY_ID, + BpfCommand::MapGetFdById => bpf_cmd::BPF_MAP_GET_FD_BY_ID, + BpfCommand::ObjGetInfoByFd => bpf_cmd::BPF_OBJ_GET_INFO_BY_FD, + BpfCommand::ProgQuery => bpf_cmd::BPF_PROG_QUERY, + BpfCommand::RawTracepointOpen => bpf_cmd::BPF_RAW_TRACEPOINT_OPEN, + BpfCommand::BtfLoad => bpf_cmd::BPF_BTF_LOAD, + BpfCommand::BtfGetFdById => bpf_cmd::BPF_BTF_GET_FD_BY_ID, + BpfCommand::TaskFdQuery => bpf_cmd::BPF_TASK_FD_QUERY, + BpfCommand::MapLookupAndDeleteElem => bpf_cmd::BPF_MAP_LOOKUP_AND_DELETE_ELEM, + BpfCommand::MapFreeze => bpf_cmd::BPF_MAP_FREEZE, + BpfCommand::BtfGetNextId => bpf_cmd::BPF_BTF_GET_NEXT_ID, + BpfCommand::MapLookupBatch => bpf_cmd::BPF_MAP_LOOKUP_BATCH, + BpfCommand::MapLookupAndDeleteBatch => bpf_cmd::BPF_MAP_LOOKUP_AND_DELETE_BATCH, + BpfCommand::MapUpdateBatch => bpf_cmd::BPF_MAP_UPDATE_BATCH, + BpfCommand::MapDeleteBatch => bpf_cmd::BPF_MAP_DELETE_BATCH, + BpfCommand::LinkCreate => bpf_cmd::BPF_LINK_CREATE, + BpfCommand::LinkUpdate => bpf_cmd::BPF_LINK_UPDATE, + BpfCommand::LinkGetFdById => bpf_cmd::BPF_LINK_GET_FD_BY_ID, + BpfCommand::LinkGetNextId => bpf_cmd::BPF_LINK_GET_NEXT_ID, + BpfCommand::EnableStats => bpf_cmd::BPF_ENABLE_STATS, + BpfCommand::IterCreate => bpf_cmd::BPF_ITER_CREATE, + BpfCommand::LinkDetach => bpf_cmd::BPF_LINK_DETACH, + BpfCommand::ProgBindMap => bpf_cmd::BPF_PROG_BIND_MAP, + BpfCommand::TokenCreate => bpf_cmd::BPF_TOKEN_CREATE, + } + } +} diff --git a/aya-obj/src/lib.rs b/aya-obj/src/lib.rs index ea0e5670..b83e4f37 100644 --- a/aya-obj/src/lib.rs +++ b/aya-obj/src/lib.rs @@ -85,8 +85,11 @@ mod std { } } +pub mod attach; pub mod btf; +pub mod cmd; pub mod generated; +pub mod links; pub mod maps; pub mod obj; pub mod programs; diff --git a/aya-obj/src/links.rs b/aya-obj/src/links.rs new file mode 100644 index 00000000..79ab7f66 --- /dev/null +++ b/aya-obj/src/links.rs @@ -0,0 +1,57 @@ +//! Link types for BPFFS Permissions + +use crate::generated::bpf_link_type; + +/// The type of BPF link +#[derive(Copy, Clone, Debug)] +pub enum BpfLinkType { + /// Not Specified + Unspecified, + /// Raw Tracepoint + RawTracepoint, + /// Tracing + Tracing, + /// Cgroup + Cgroup, + /// Iter + Iter, + /// Netns + Netns, + /// Xdp + Xdp, + /// Perf Event + PerfEvent, + /// Kprobe Multi + KprobeMulti, + /// Struct Ops + StructOps, + /// Netfilter + Netfilter, + /// Tcx + Tcx, + /// Uprobe Multi + UprobeMulti, + /// Netkit + Netkit, +} + +impl From for bpf_link_type { + fn from(value: BpfLinkType) -> Self { + match value { + BpfLinkType::Unspecified => bpf_link_type::BPF_LINK_TYPE_UNSPEC, + BpfLinkType::RawTracepoint => bpf_link_type::BPF_LINK_TYPE_RAW_TRACEPOINT, + BpfLinkType::Tracing => bpf_link_type::BPF_LINK_TYPE_TRACING, + BpfLinkType::Cgroup => bpf_link_type::BPF_LINK_TYPE_CGROUP, + BpfLinkType::Iter => bpf_link_type::BPF_LINK_TYPE_ITER, + BpfLinkType::Netns => bpf_link_type::BPF_LINK_TYPE_NETNS, + BpfLinkType::Xdp => bpf_link_type::BPF_LINK_TYPE_XDP, + BpfLinkType::PerfEvent => bpf_link_type::BPF_LINK_TYPE_PERF_EVENT, + BpfLinkType::KprobeMulti => bpf_link_type::BPF_LINK_TYPE_KPROBE_MULTI, + BpfLinkType::StructOps => bpf_link_type::BPF_LINK_TYPE_STRUCT_OPS, + BpfLinkType::Netfilter => bpf_link_type::BPF_LINK_TYPE_NETFILTER, + BpfLinkType::Tcx => bpf_link_type::BPF_LINK_TYPE_TCX, + BpfLinkType::UprobeMulti => bpf_link_type::BPF_LINK_TYPE_UPROBE_MULTI, + BpfLinkType::Netkit => bpf_link_type::BPF_LINK_TYPE_NETKIT, + } + } +} diff --git a/aya-obj/src/maps.rs b/aya-obj/src/maps.rs index ca85bba7..553881a6 100644 --- a/aya-obj/src/maps.rs +++ b/aya-obj/src/maps.rs @@ -5,7 +5,7 @@ use core::mem; #[cfg(not(feature = "std"))] use crate::std; -use crate::EbpfSectionKind; +use crate::{generated::bpf_map_type, EbpfSectionKind}; /// Invalid map type encontered pub struct InvalidMapTypeError { @@ -13,6 +13,124 @@ pub struct InvalidMapTypeError { pub map_type: u32, } +/// The type of BPF Map +#[derive(Copy, Clone, Debug)] +pub enum BpfMapType { + /// Not Specified + Unspecified, + /// Hash + Hash, + /// Array + Array, + /// Prog Array + ProgArray, + /// Perf Event Array + PerfEventArray, + /// Per-CPU Hash + PerCpuHash, + /// Per-CPU Array + PerCpuArray, + /// Stack Trace + StackTrace, + /// Cgroup Array + CgroupArray, + /// LRU Hash + LruHash, + /// LRU Per-CPU Hash + LruPerCpuHash, + /// LPM Trie + LpmTrie, + /// Array of Maps + ArrayOfMaps, + /// Hash of Maps + HashOfMaps, + /// Devmap + Devmap, + /// Sockmap + Sockmap, + /// Cpumap + Cpumap, + /// Xskmap + Xskmap, + /// Sockhash + Sockhash, + /// Cgroup Storage (deprecated) + CgroupStorageDeprecated, + /// Reuseport Sockarray + ReuseportSockarray, + /// Per-CPU Cgroup Storage (deprecated) + PerCpuCgroupStorageDeprecated, + /// Queue + Queue, + /// Stack + Stack, + /// Sk Storage + SkStorage, + /// Devmap Hash + DevmapHash, + /// Struct Ops + StructOps, + /// Ringbuf + Ringbuf, + /// Inode Storage + InodeStorage, + /// Task Storage + TaskStorage, + /// Bloom Filter + BloomFilter, + /// User Ringbuf + UserRingbuf, + /// Cgroup Storage + CgroupStorage, + /// Arena + Arena, +} + +impl From for bpf_map_type { + fn from(value: BpfMapType) -> Self { + match value { + BpfMapType::Unspecified => bpf_map_type::BPF_MAP_TYPE_UNSPEC, + BpfMapType::Hash => bpf_map_type::BPF_MAP_TYPE_HASH, + BpfMapType::Array => bpf_map_type::BPF_MAP_TYPE_ARRAY, + BpfMapType::ProgArray => bpf_map_type::BPF_MAP_TYPE_PROG_ARRAY, + BpfMapType::PerfEventArray => bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY, + BpfMapType::PerCpuHash => bpf_map_type::BPF_MAP_TYPE_PERCPU_HASH, + BpfMapType::PerCpuArray => bpf_map_type::BPF_MAP_TYPE_PERCPU_ARRAY, + BpfMapType::StackTrace => bpf_map_type::BPF_MAP_TYPE_STACK_TRACE, + BpfMapType::CgroupArray => bpf_map_type::BPF_MAP_TYPE_CGROUP_ARRAY, + BpfMapType::LruHash => bpf_map_type::BPF_MAP_TYPE_LRU_HASH, + BpfMapType::LruPerCpuHash => bpf_map_type::BPF_MAP_TYPE_LRU_PERCPU_HASH, + BpfMapType::LpmTrie => bpf_map_type::BPF_MAP_TYPE_LPM_TRIE, + BpfMapType::ArrayOfMaps => bpf_map_type::BPF_MAP_TYPE_ARRAY_OF_MAPS, + BpfMapType::HashOfMaps => bpf_map_type::BPF_MAP_TYPE_HASH_OF_MAPS, + BpfMapType::Devmap => bpf_map_type::BPF_MAP_TYPE_DEVMAP, + BpfMapType::Sockmap => bpf_map_type::BPF_MAP_TYPE_SOCKMAP, + BpfMapType::Cpumap => bpf_map_type::BPF_MAP_TYPE_CPUMAP, + BpfMapType::Xskmap => bpf_map_type::BPF_MAP_TYPE_XSKMAP, + BpfMapType::Sockhash => bpf_map_type::BPF_MAP_TYPE_SOCKHASH, + BpfMapType::CgroupStorageDeprecated => { + bpf_map_type::BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED + } + BpfMapType::ReuseportSockarray => bpf_map_type::BPF_MAP_TYPE_REUSEPORT_SOCKARRAY, + BpfMapType::PerCpuCgroupStorageDeprecated => { + bpf_map_type::BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED + } + BpfMapType::Queue => bpf_map_type::BPF_MAP_TYPE_QUEUE, + BpfMapType::Stack => bpf_map_type::BPF_MAP_TYPE_STACK, + BpfMapType::SkStorage => bpf_map_type::BPF_MAP_TYPE_SK_STORAGE, + BpfMapType::DevmapHash => bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH, + BpfMapType::StructOps => bpf_map_type::BPF_MAP_TYPE_STRUCT_OPS, + BpfMapType::Ringbuf => bpf_map_type::BPF_MAP_TYPE_RINGBUF, + BpfMapType::InodeStorage => bpf_map_type::BPF_MAP_TYPE_INODE_STORAGE, + BpfMapType::TaskStorage => bpf_map_type::BPF_MAP_TYPE_TASK_STORAGE, + BpfMapType::BloomFilter => bpf_map_type::BPF_MAP_TYPE_BLOOM_FILTER, + BpfMapType::UserRingbuf => bpf_map_type::BPF_MAP_TYPE_USER_RINGBUF, + BpfMapType::CgroupStorage => bpf_map_type::BPF_MAP_TYPE_CGRP_STORAGE, + BpfMapType::Arena => bpf_map_type::BPF_MAP_TYPE_ARENA, + } + } +} + impl TryFrom for crate::generated::bpf_map_type { type Error = InvalidMapTypeError; diff --git a/aya-obj/src/obj.rs b/aya-obj/src/obj.rs index f6f4fc8b..652cb710 100644 --- a/aya-obj/src/obj.rs +++ b/aya-obj/src/obj.rs @@ -37,7 +37,7 @@ use crate::{ const KERNEL_VERSION_ANY: u32 = 0xFFFF_FFFE; /// Features implements BPF and BTF feature detection -#[derive(Default, Debug)] +#[derive(Default, Debug, Clone)] #[allow(missing_docs)] pub struct Features { bpf_name: bool, diff --git a/aya-obj/src/programs/mod.rs b/aya-obj/src/programs/mod.rs index 6b66b005..fc5477b1 100644 --- a/aya-obj/src/programs/mod.rs +++ b/aya-obj/src/programs/mod.rs @@ -1,5 +1,7 @@ //! Program struct and type bindings. +use crate::generated::bpf_prog_type; + pub mod cgroup_sock; pub mod cgroup_sock_addr; pub mod cgroup_sockopt; @@ -9,3 +11,116 @@ pub use cgroup_sock::CgroupSockAttachType; pub use cgroup_sock_addr::CgroupSockAddrAttachType; pub use cgroup_sockopt::CgroupSockoptAttachType; pub use xdp::XdpAttachType; + +/// The type of BPF statistic to enable. +#[derive(Copy, Clone, Debug)] +pub enum BpfProgType { + /// Not Specified + Unspecified, + /// Socket Filter + SocketFilter, + /// Kprobe + Kprobe, + /// Sched Cls + SchedCls, + /// Sched Act + SchedAct, + /// Tracepoint + Tracepoint, + /// XDP + Xdp, + /// Perf Event + PerfEvent, + /// Cgroup Skb + CgroupSkb, + /// Cgroup Sock + CgroupSock, + /// Lwt In + LwtIn, + /// Lwt Out + LwtOut, + /// Lwt Xmit + LwtXmit, + /// Sock Ops + SockOps, + /// Sk Skb + SkSkb, + /// Cgroup Device + CgroupDevice, + /// Sk Msg + SkMsg, + /// Raw Tracepoint + RawTracepoint, + /// Cgroup Sock Addr + CgroupSockAddr, + /// Lwt Seg6 Local + LwtSeg6Local, + /// Lirc Mode2 + LircMode2, + /// Sk Reuseport + SkReuseport, + /// Flow Dissector + FlowDissector, + /// Cgroup Sysctl + CgroupSysctl, + /// Raw Tracepoint Writable + RawTracepointWritable, + /// Cgroup Sockopt + CgroupSockopt, + /// Tracing + Tracing, + /// Struct Ops + StructOps, + /// Ext + Ext, + /// Lsm + Lsm, + /// Sk Lookup + SkLookup, + /// Syscall + Syscall, + /// Netfilter + Netfilter, +} + +impl From for bpf_prog_type { + fn from(value: BpfProgType) -> Self { + match value { + BpfProgType::Unspecified => bpf_prog_type::BPF_PROG_TYPE_UNSPEC, + BpfProgType::SocketFilter => bpf_prog_type::BPF_PROG_TYPE_SOCKET_FILTER, + BpfProgType::Kprobe => bpf_prog_type::BPF_PROG_TYPE_KPROBE, + BpfProgType::SchedCls => bpf_prog_type::BPF_PROG_TYPE_SCHED_CLS, + BpfProgType::SchedAct => bpf_prog_type::BPF_PROG_TYPE_SCHED_ACT, + BpfProgType::Tracepoint => bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT, + BpfProgType::Xdp => bpf_prog_type::BPF_PROG_TYPE_XDP, + BpfProgType::PerfEvent => bpf_prog_type::BPF_PROG_TYPE_PERF_EVENT, + BpfProgType::CgroupSkb => bpf_prog_type::BPF_PROG_TYPE_CGROUP_SKB, + BpfProgType::CgroupSock => bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCK, + BpfProgType::LwtIn => bpf_prog_type::BPF_PROG_TYPE_LWT_IN, + BpfProgType::LwtOut => bpf_prog_type::BPF_PROG_TYPE_LWT_OUT, + BpfProgType::LwtXmit => bpf_prog_type::BPF_PROG_TYPE_LWT_XMIT, + BpfProgType::SockOps => bpf_prog_type::BPF_PROG_TYPE_SOCK_OPS, + BpfProgType::SkSkb => bpf_prog_type::BPF_PROG_TYPE_SK_SKB, + BpfProgType::CgroupDevice => bpf_prog_type::BPF_PROG_TYPE_CGROUP_DEVICE, + BpfProgType::SkMsg => bpf_prog_type::BPF_PROG_TYPE_SK_MSG, + BpfProgType::RawTracepoint => bpf_prog_type::BPF_PROG_TYPE_RAW_TRACEPOINT, + BpfProgType::CgroupSockAddr => bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCK_ADDR, + BpfProgType::LwtSeg6Local => bpf_prog_type::BPF_PROG_TYPE_LWT_SEG6LOCAL, + BpfProgType::LircMode2 => bpf_prog_type::BPF_PROG_TYPE_LIRC_MODE2, + BpfProgType::SkReuseport => bpf_prog_type::BPF_PROG_TYPE_SK_REUSEPORT, + BpfProgType::FlowDissector => bpf_prog_type::BPF_PROG_TYPE_FLOW_DISSECTOR, + BpfProgType::CgroupSysctl => bpf_prog_type::BPF_PROG_TYPE_CGROUP_SYSCTL, + BpfProgType::RawTracepointWritable => { + bpf_prog_type::BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE + } + BpfProgType::CgroupSockopt => bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCKOPT, + BpfProgType::Tracing => bpf_prog_type::BPF_PROG_TYPE_TRACING, + BpfProgType::StructOps => bpf_prog_type::BPF_PROG_TYPE_STRUCT_OPS, + BpfProgType::Ext => bpf_prog_type::BPF_PROG_TYPE_EXT, + BpfProgType::Lsm => bpf_prog_type::BPF_PROG_TYPE_LSM, + BpfProgType::SkLookup => bpf_prog_type::BPF_PROG_TYPE_SK_LOOKUP, + BpfProgType::Syscall => bpf_prog_type::BPF_PROG_TYPE_SYSCALL, + BpfProgType::Netfilter => bpf_prog_type::BPF_PROG_TYPE_NETFILTER, + } + } +} diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index 42a9a489..4faa3938 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -1,10 +1,12 @@ use std::{ borrow::Cow, collections::{HashMap, HashSet}, + ffi::CString, fs, io, os::{ - fd::{AsFd as _, AsRawFd as _, OwnedFd}, + fd::{AsFd as _, AsRawFd as _, BorrowedFd, FromRawFd, OwnedFd}, raw::c_int, + unix::ffi::OsStrExt as _, }, path::{Path, PathBuf}, sync::Arc, @@ -16,6 +18,7 @@ use aya_obj::{ relocation::EbpfRelocationError, EbpfSectionKind, Features, }; +use libc::{O_CLOEXEC, O_DIRECTORY, O_RDWR}; use log::{debug, warn}; use thiserror::Error; @@ -36,7 +39,7 @@ use crate::{ SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp, }, sys::{ - bpf_load_btf, is_bpf_cookie_supported, is_bpf_global_data_supported, + bpf_load_btf, bpf_token_create, is_bpf_cookie_supported, is_bpf_global_data_supported, is_btf_datasec_supported, is_btf_decl_tag_supported, is_btf_enum64_supported, is_btf_float_supported, is_btf_func_global_supported, is_btf_func_supported, is_btf_supported, is_btf_type_tag_supported, is_perf_link_supported, @@ -70,32 +73,28 @@ unsafe impl Pod for [T; N] {} pub use aya_obj::maps::{bpf_map_def, PinningType}; -lazy_static::lazy_static! { - pub(crate) static ref FEATURES: Features = detect_features(); -} - -fn detect_features() -> Features { - let btf = if is_btf_supported() { +fn detect_features(token_fd: Option>) -> Features { + let btf = if is_btf_supported(token_fd) { Some(BtfFeatures::new( - is_btf_func_supported(), - is_btf_func_global_supported(), - is_btf_datasec_supported(), - is_btf_float_supported(), - is_btf_decl_tag_supported(), - is_btf_type_tag_supported(), - is_btf_enum64_supported(), + is_btf_func_supported(token_fd), + is_btf_func_global_supported(token_fd), + is_btf_datasec_supported(token_fd), + is_btf_float_supported(token_fd), + is_btf_decl_tag_supported(token_fd), + is_btf_type_tag_supported(token_fd), + is_btf_enum64_supported(token_fd), )) } else { None }; let f = Features::new( - is_prog_name_supported(), - is_probe_read_kernel_supported(), - is_perf_link_supported(), - is_bpf_global_data_supported(), - is_bpf_cookie_supported(), - is_prog_id_supported(BPF_MAP_TYPE_CPUMAP), - is_prog_id_supported(BPF_MAP_TYPE_DEVMAP), + is_prog_name_supported(token_fd), + is_probe_read_kernel_supported(token_fd), + is_perf_link_supported(token_fd), + is_bpf_global_data_supported(token_fd), + is_bpf_cookie_supported(token_fd), + is_prog_id_supported(BPF_MAP_TYPE_CPUMAP, token_fd), + is_prog_id_supported(BPF_MAP_TYPE_DEVMAP, token_fd), btf, ); debug!("BPF Feature Detection: {:#?}", f); @@ -103,8 +102,8 @@ fn detect_features() -> Features { } /// Returns a reference to the detected BPF features. -pub fn features() -> &'static Features { - &FEATURES +pub fn features(token_fd: Option>) -> Features { + detect_features(token_fd) } /// Builder style API for advanced loading of eBPF programs. @@ -132,6 +131,7 @@ pub fn features() -> &'static Features { pub struct EbpfLoader<'a> { btf: Option>, map_pin_path: Option, + token_path: PathBuf, globals: HashMap<&'a str, (&'a [u8], bool)>, max_entries: HashMap<&'a str, u32>, extensions: HashSet<&'a str>, @@ -170,6 +170,7 @@ impl<'a> EbpfLoader<'a> { Self { btf: Btf::from_sys_fs().ok().map(Cow::Owned), map_pin_path: None, + token_path: Path::new("/sys/fs/bpf").to_owned(), globals: HashMap::new(), max_entries: HashMap::new(), extensions: HashSet::new(), @@ -355,6 +356,23 @@ impl<'a> EbpfLoader<'a> { self } + /// Sets the path of the BPFFS to use for obtaining a token. + /// + /// # Example + /// + /// ```no_run + /// use aya::EbpfLoader; + /// + /// let bpf = EbpfLoader::new() + /// .token_path("/sys/fs/bpf") + /// .load_file("file.o")?; + /// # Ok::<(), aya::EbpfError>(()) + /// ``` + pub fn token_path>(&mut self, path: P) -> &mut Self { + self.token_path = path.as_ref().to_path_buf(); + self + } + /// Loads eBPF bytecode from a file. /// /// # Examples @@ -389,18 +407,23 @@ impl<'a> EbpfLoader<'a> { let Self { btf, map_pin_path, + token_path, globals, max_entries, extensions, verifier_log_level, allow_unsupported_maps, } = self; + + let token_fd = create_token(token_path).ok().map(Arc::new); + let borrow_token_fd = token_fd.as_ref().map(|x| x.as_fd()); + let features = detect_features(borrow_token_fd); let mut obj = Object::parse(data)?; obj.patch_map_data(globals.clone())?; - let btf_fd = if let Some(features) = &FEATURES.btf() { + let btf_fd = if let Some(features) = features.btf() { if let Some(btf) = obj.fixup_and_sanitize_btf(features)? { - match load_btf(btf.to_bytes(), *verifier_log_level) { + match load_btf(btf.to_bytes(), *verifier_log_level, borrow_token_fd) { Ok(btf_fd) => Some(Arc::new(btf_fd)), // Only report an error here if the BTF is truly needed, otherwise proceed without. Err(err) => { @@ -461,7 +484,7 @@ impl<'a> EbpfLoader<'a> { let mut maps = HashMap::new(); for (name, mut obj) in obj.maps.drain() { if let (false, EbpfSectionKind::Bss | EbpfSectionKind::Data | EbpfSectionKind::Rodata) = - (FEATURES.bpf_global_data(), obj.section_kind()) + (features.bpf_global_data(), obj.section_kind()) { continue; } @@ -485,16 +508,18 @@ impl<'a> EbpfLoader<'a> { } match obj.map_type().try_into() { Ok(BPF_MAP_TYPE_CPUMAP) => { - obj.set_value_size(if FEATURES.cpumap_prog_id() { 8 } else { 4 }) + obj.set_value_size(if features.cpumap_prog_id() { 8 } else { 4 }) } Ok(BPF_MAP_TYPE_DEVMAP | BPF_MAP_TYPE_DEVMAP_HASH) => { - obj.set_value_size(if FEATURES.devmap_prog_id() { 8 } else { 4 }) + obj.set_value_size(if features.devmap_prog_id() { 8 } else { 4 }) } _ => (), } let btf_fd = btf_fd.as_deref().map(|fd| fd.as_fd()); let mut map = match obj.pinning() { - PinningType::None => MapData::create(obj, &name, btf_fd)?, + PinningType::None => { + MapData::create(obj, &name, btf_fd, token_fd.clone(), features.clone())? + } PinningType::ByName => { // pin maps in /sys/fs/bpf by default to align with libbpf // behavior https://github.com/libbpf/libbpf/blob/v1.2.2/src/libbpf.c#L2161. @@ -502,7 +527,14 @@ impl<'a> EbpfLoader<'a> { .as_deref() .unwrap_or_else(|| Path::new("/sys/fs/bpf")); - MapData::create_pinned_by_name(path, obj, &name, btf_fd)? + MapData::create_pinned_by_name( + path, + obj, + &name, + btf_fd, + token_fd.clone(), + features.clone(), + )? } }; map.finalize()?; @@ -521,7 +553,7 @@ impl<'a> EbpfLoader<'a> { &text_sections, )?; obj.relocate_calls(&text_sections)?; - obj.sanitize_functions(&FEATURES); + obj.sanitize_functions(&features); let programs = obj .programs @@ -529,7 +561,7 @@ impl<'a> EbpfLoader<'a> { .map(|(name, prog_obj)| { let function_obj = obj.functions.get(&prog_obj.function_key()).unwrap().clone(); - let prog_name = if FEATURES.bpf_name() { + let prog_name = if features.bpf_name() { Some(name.clone()) } else { None @@ -538,23 +570,51 @@ impl<'a> EbpfLoader<'a> { let obj = (prog_obj, function_obj); let btf_fd = btf_fd.clone(); + let token_fd = token_fd.clone(); let program = if extensions.contains(name.as_str()) { Program::Extension(Extension { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), }) } else { match §ion { ProgramSection::KProbe => Program::KProbe(KProbe { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), kind: ProbeKind::KProbe, }), ProgramSection::KRetProbe => Program::KProbe(KProbe { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), kind: ProbeKind::KRetProbe, }), ProgramSection::UProbe { sleepable } => { - let mut data = - ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level); + let mut data = ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ); if *sleepable { data.flags = BPF_F_SLEEPABLE; } @@ -564,8 +624,14 @@ impl<'a> EbpfLoader<'a> { }) } ProgramSection::URetProbe { sleepable } => { - let mut data = - ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level); + let mut data = ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ); if *sleepable { data.flags = BPF_F_SLEEPABLE; } @@ -575,16 +641,36 @@ impl<'a> EbpfLoader<'a> { }) } ProgramSection::TracePoint => Program::TracePoint(TracePoint { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), }), ProgramSection::SocketFilter => Program::SocketFilter(SocketFilter { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), }), ProgramSection::Xdp { frags, attach_type, .. } => { - let mut data = - ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level); + let mut data = ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ); if *frags { data.flags = BPF_F_XDP_HAS_FRAGS; } @@ -594,101 +680,252 @@ impl<'a> EbpfLoader<'a> { }) } ProgramSection::SkMsg => Program::SkMsg(SkMsg { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), }), ProgramSection::CgroupSysctl => Program::CgroupSysctl(CgroupSysctl { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), }), ProgramSection::CgroupSockopt { attach_type, .. } => { Program::CgroupSockopt(CgroupSockopt { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), attach_type: *attach_type, }) } ProgramSection::SkSkbStreamParser => Program::SkSkb(SkSkb { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), kind: SkSkbKind::StreamParser, }), ProgramSection::SkSkbStreamVerdict => Program::SkSkb(SkSkb { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), kind: SkSkbKind::StreamVerdict, }), ProgramSection::SockOps => Program::SockOps(SockOps { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), }), ProgramSection::SchedClassifier => { Program::SchedClassifier(SchedClassifier { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), }) } ProgramSection::CgroupSkb => Program::CgroupSkb(CgroupSkb { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), expected_attach_type: None, }), ProgramSection::CgroupSkbIngress => Program::CgroupSkb(CgroupSkb { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), expected_attach_type: Some(CgroupSkbAttachType::Ingress), }), ProgramSection::CgroupSkbEgress => Program::CgroupSkb(CgroupSkb { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), expected_attach_type: Some(CgroupSkbAttachType::Egress), }), ProgramSection::CgroupSockAddr { attach_type, .. } => { Program::CgroupSockAddr(CgroupSockAddr { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), attach_type: *attach_type, }) } ProgramSection::LircMode2 => Program::LircMode2(LircMode2 { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), }), ProgramSection::PerfEvent => Program::PerfEvent(PerfEvent { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), }), ProgramSection::RawTracePoint => Program::RawTracePoint(RawTracePoint { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), }), ProgramSection::Lsm { sleepable } => { - let mut data = - ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level); + let mut data = ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ); if *sleepable { data.flags = BPF_F_SLEEPABLE; } Program::Lsm(Lsm { data }) } ProgramSection::BtfTracePoint => Program::BtfTracePoint(BtfTracePoint { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), }), ProgramSection::FEntry { sleepable } => { - let mut data = - ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level); + let mut data = ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ); if *sleepable { data.flags = BPF_F_SLEEPABLE; } Program::FEntry(FEntry { data }) } ProgramSection::FExit { sleepable } => { - let mut data = - ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level); + let mut data = ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ); if *sleepable { data.flags = BPF_F_SLEEPABLE; } Program::FExit(FExit { data }) } ProgramSection::Extension => Program::Extension(Extension { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), }), ProgramSection::SkLookup => Program::SkLookup(SkLookup { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), }), ProgramSection::CgroupSock { attach_type, .. } => { Program::CgroupSock(CgroupSock { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), attach_type: *attach_type, }) } ProgramSection::CgroupDevice => Program::CgroupDevice(CgroupDevice { - data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + data: ProgramData::new( + prog_name, + obj, + btf_fd, + *verifier_log_level, + token_fd, + features.clone(), + ), }), } }; @@ -1073,6 +1310,17 @@ impl Ebpf { /// The error type returned by [`Ebpf::load_file`] and [`Ebpf::load`]. #[derive(Debug, Error)] pub enum EbpfError { + /// An IOError occurred + #[error(transparent)] + IOError(#[from] io::Error), + + #[error("{path} is not a valid path")] + /// Invalid path + InvalidPath { + /// The file path + path: PathBuf, + }, + /// Error loading file #[error("error loading {path}")] FileError { @@ -1123,9 +1371,13 @@ pub enum EbpfError { #[deprecated(since = "0.13.0", note = "use `EbpfError` instead")] pub type BpfError = EbpfError; -fn load_btf(raw_btf: Vec, verifier_log_level: VerifierLogLevel) -> Result { +fn load_btf( + raw_btf: Vec, + verifier_log_level: VerifierLogLevel, + token_fd: Option>, +) -> Result { let (ret, verifier_log) = retry_with_verifier_logs(10, |logger| { - bpf_load_btf(raw_btf.as_slice(), logger, verifier_log_level) + bpf_load_btf(raw_btf.as_slice(), logger, verifier_log_level, token_fd) }); ret.map_err(|(_, io_error)| BtfError::LoadError { io_error, @@ -1157,3 +1409,28 @@ impl<'a, T: Pod> From<&'a T> for GlobalData<'a> { } } } + +/// Creates an eBPF Token at the given path. +/// +/// The token is used to control access to the eBPF syscalls +/// from unprivileged userspace programs. +#[allow(dead_code)] +pub(crate) fn create_token>(path: P) -> Result { + let path = path.as_ref(); + let path_string = CString::new(path.as_os_str().as_bytes()).map_err(|_| { + EbpfError::IOError(io::Error::new( + io::ErrorKind::InvalidInput, + "path contains a null byte", + )) + })?; + let bpffs_fd: i32 = unsafe { libc::open(path_string.as_ptr(), O_DIRECTORY, O_RDWR, O_CLOEXEC) }; + if bpffs_fd < 0 { + return Err(EbpfError::IOError(io::Error::last_os_error())); + } + let bpffs_fd = unsafe { OwnedFd::from_raw_fd(bpffs_fd) }; + + bpf_token_create(bpffs_fd.as_fd(), 0).map_err(|(_, error)| EbpfError::FileError { + path: path.to_owned(), + error, + }) +} diff --git a/aya/src/lib.rs b/aya/src/lib.rs index ccceb9e0..ee9a560d 100644 --- a/aya/src/lib.rs +++ b/aya/src/lib.rs @@ -94,6 +94,7 @@ pub use obj::btf::{Btf, BtfError}; pub use object::Endianness; #[doc(hidden)] pub use sys::netlink_set_link_up; +pub use sys::{create_bpf_filesystem, FilesystemPermissions, FilesystemPermissionsBuilder}; // See https://github.com/rust-lang/rust/pull/124210; this structure exists to avoid crashing the // process when we try to close a fake file descriptor. diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index 82467aee..e4a14851 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -57,8 +57,10 @@ use std::{ os::fd::{AsFd, BorrowedFd, OwnedFd}, path::Path, ptr, + sync::Arc, }; +use aya_obj::Features; use libc::{getrlimit, rlim_t, rlimit, RLIMIT_MEMLOCK, RLIM_INFINITY}; use log::warn; use obj::maps::InvalidMapTypeError; @@ -546,6 +548,7 @@ pub(crate) fn check_v_size(map: &MapData) -> Result<(), MapError> { pub struct MapData { obj: obj::Map, fd: MapFd, + features: Features, } impl MapData { @@ -554,6 +557,8 @@ impl MapData { obj: obj::Map, name: &str, btf_fd: Option>, + token_fd: Option>, + features: Features, ) -> Result { let c_name = CString::new(name).map_err(|_| MapError::InvalidName { name: name.into() })?; @@ -561,21 +566,28 @@ impl MapData { let kernel_version = KernelVersion::current().unwrap(); #[cfg(test)] let kernel_version = KernelVersion::new(0xff, 0xff, 0xff); - let fd = - bpf_create_map(&c_name, &obj, btf_fd, kernel_version).map_err(|(code, io_error)| { - if kernel_version < KernelVersion::new(5, 11, 0) { - maybe_warn_rlimit(); - } + let fd = bpf_create_map( + &c_name, + &obj, + btf_fd, + kernel_version, + token_fd.as_ref().map(|fd| fd.as_fd()), + ) + .map_err(|(code, io_error)| { + if kernel_version < KernelVersion::new(5, 11, 0) { + maybe_warn_rlimit(); + } - MapError::CreateError { - name: name.into(), - code, - io_error, - } - })?; + MapError::CreateError { + name: name.into(), + code, + io_error, + } + })?; Ok(Self { obj, fd: MapFd::from_fd(fd), + features, }) } @@ -584,6 +596,8 @@ impl MapData { obj: obj::Map, name: &str, btf_fd: Option>, + token_fd: Option>, + features: Features, ) -> Result { use std::os::unix::ffi::OsStrExt as _; @@ -605,9 +619,10 @@ impl MapData { Ok(fd) => Ok(Self { obj, fd: MapFd::from_fd(fd), + features, }), Err(_) => { - let map = Self::create(obj, name, btf_fd)?; + let map = Self::create(obj, name, btf_fd, token_fd, features)?; map.pin(&path).map_err(|error| MapError::PinError { name: Some(name.into()), error, @@ -618,7 +633,11 @@ impl MapData { } pub(crate) fn finalize(&mut self) -> Result<(), MapError> { - let Self { obj, fd } = self; + let Self { + obj, + fd, + features: _, + } = self; if !obj.data().is_empty() && obj.section_kind() != EbpfSectionKind::Bss { bpf_map_update_elem_ptr(fd.as_fd(), &0 as *const _, obj.data_mut().as_mut_ptr(), 0) .map_err(|(_, io_error)| SyscallError { @@ -639,7 +658,7 @@ impl MapData { } /// Loads a map from a pinned path in bpffs. - pub fn from_pin>(path: P) -> Result { + pub fn from_pin>(path: P, features: Features) -> Result { use std::os::unix::ffi::OsStrExt as _; let path = path.as_ref(); @@ -657,13 +676,13 @@ impl MapData { io_error, })?; - Self::from_fd(fd) + Self::from_fd(fd, features) } /// Loads a map from a map id. - pub fn from_id(id: u32) -> Result { + pub fn from_id(id: u32, features: Features) -> Result { let fd = bpf_map_get_fd_by_id(id)?; - Self::from_fd(fd) + Self::from_fd(fd, features) } /// Loads a map from a file descriptor. @@ -671,11 +690,12 @@ impl MapData { /// If loading from a BPF Filesystem (bpffs) you should use [`Map::from_pin`](crate::maps::MapData::from_pin). /// This API is intended for cases where you have received a valid BPF FD from some other means. /// For example, you received an FD over Unix Domain Socket. - pub fn from_fd(fd: OwnedFd) -> Result { + pub fn from_fd(fd: OwnedFd, features: Features) -> Result { let MapInfo(info) = MapInfo::new_from_fd(fd.as_fd())?; Ok(Self { obj: parse_map_info(info, PinningType::None), fd: MapFd::from_fd(fd), + features, }) } @@ -706,7 +726,11 @@ impl MapData { pub fn pin>(&self, path: P) -> Result<(), PinError> { use std::os::unix::ffi::OsStrExt as _; - let Self { fd, obj: _ } = self; + let Self { + fd, + obj: _, + features: _, + } = self; let path = path.as_ref(); let path_string = CString::new(path.as_os_str().as_bytes()).map_err(|error| { PinError::InvalidPinPath { @@ -723,12 +747,20 @@ impl MapData { /// Returns the file descriptor of the map. pub fn fd(&self) -> &MapFd { - let Self { obj: _, fd } = self; + let Self { + obj: _, + fd, + features: _, + } = self; fd } pub(crate) fn obj(&self) -> &obj::Map { - let Self { obj, fd: _ } = self; + let Self { + obj, + fd: _, + features: _, + } = self; obj } @@ -1039,6 +1071,8 @@ pub fn loaded_maps() -> impl Iterator> { #[cfg(test)] mod test_utils { + use aya_obj::Features; + use crate::{ bpf_map_def, generated::{bpf_cmd, bpf_map_type}, @@ -1055,7 +1089,7 @@ mod test_utils { } => Ok(crate::MockableFd::mock_signed_fd().into()), call => panic!("unexpected syscall {:?}", call), }); - MapData::create(obj, "foo", None).unwrap() + MapData::create(obj, "foo", None, None, Features::default()).unwrap() } pub(super) fn new_obj_map(map_type: bpf_map_type) -> obj::Map { @@ -1119,10 +1153,11 @@ mod tests { }); assert_matches!( - MapData::from_id(1234), + MapData::from_id(1234, Features::default()), Ok(MapData { obj: _, fd, + features: _, }) => assert_eq!(fd.as_fd().as_raw_fd(), crate::MockableFd::mock_signed_fd()) ); } @@ -1138,10 +1173,11 @@ mod tests { }); assert_matches!( - MapData::create(new_obj_map(), "foo", None), + MapData::create(new_obj_map(), "foo", None, None, Features::default()), Ok(MapData { obj: _, fd, + features: _, }) => assert_eq!(fd.as_fd().as_raw_fd(), crate::MockableFd::mock_signed_fd()) ); } @@ -1178,7 +1214,8 @@ mod tests { _ => Err((-1, io::Error::from_raw_os_error(EFAULT))), }); - let map_data = MapData::create(new_obj_map(), TEST_NAME, None).unwrap(); + let map_data = + MapData::create(new_obj_map(), TEST_NAME, None, None, Features::default()).unwrap(); assert_eq!(TEST_NAME, map_data.info().unwrap().name_as_str().unwrap()); } @@ -1256,7 +1293,7 @@ mod tests { override_syscall(|_| Err((-42, io::Error::from_raw_os_error(EFAULT)))); assert_matches!( - MapData::create(new_obj_map(), "foo", None), + MapData::create(new_obj_map(), "foo", None, None, Features::default()), Err(MapError::CreateError { name, code, io_error }) => { assert_eq!(name, "foo"); assert_eq!(code, -42); diff --git a/aya/src/maps/xdp/cpu_map.rs b/aya/src/maps/xdp/cpu_map.rs index b5c0727c..963a7831 100644 --- a/aya/src/maps/xdp/cpu_map.rs +++ b/aya/src/maps/xdp/cpu_map.rs @@ -13,7 +13,7 @@ use crate::{ maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError}, programs::ProgramFd, sys::{bpf_map_lookup_elem, bpf_map_update_elem, SyscallError}, - Pod, FEATURES, + Pod, }; /// An array of available CPUs. @@ -56,8 +56,7 @@ pub struct CpuMap { impl> CpuMap { pub(crate) fn new(map: T) -> Result { let data = map.borrow(); - - if FEATURES.cpumap_prog_id() { + if data.features.cpumap_prog_id() { check_kv_size::(data)?; } else { check_kv_size::(data)?; @@ -83,8 +82,7 @@ impl> CpuMap { let data = self.inner.borrow(); check_bounds(data, cpu_index)?; let fd = data.fd().as_fd(); - - let value = if FEATURES.cpumap_prog_id() { + let value = if data.features.cpumap_prog_id() { bpf_map_lookup_elem::<_, bpf_cpumap_val>(fd, &cpu_index, flags).map(|value| { value.map(|value| CpuMapValue { queue_size: value.qsize, @@ -146,7 +144,7 @@ impl> CpuMap { check_bounds(data, cpu_index)?; let fd = data.fd().as_fd(); - let res = if FEATURES.cpumap_prog_id() { + let res = if data.features.cpumap_prog_id() { let mut value = unsafe { std::mem::zeroed::() }; value.qsize = queue_size; // Default is valid as the kernel will only consider fd > 0: diff --git a/aya/src/maps/xdp/dev_map.rs b/aya/src/maps/xdp/dev_map.rs index 44062df5..e47270fd 100644 --- a/aya/src/maps/xdp/dev_map.rs +++ b/aya/src/maps/xdp/dev_map.rs @@ -13,7 +13,7 @@ use crate::{ maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError}, programs::ProgramFd, sys::{bpf_map_lookup_elem, bpf_map_update_elem, SyscallError}, - Pod, FEATURES, + Pod, }; /// An array of network devices. @@ -48,8 +48,7 @@ pub struct DevMap { impl> DevMap { pub(crate) fn new(map: T) -> Result { let data = map.borrow(); - - if FEATURES.devmap_prog_id() { + if data.features.devmap_prog_id() { check_kv_size::(data)?; } else { check_kv_size::(data)?; @@ -75,8 +74,7 @@ impl> DevMap { let data = self.inner.borrow(); check_bounds(data, index)?; let fd = data.fd().as_fd(); - - let value = if FEATURES.devmap_prog_id() { + let value = if data.features.devmap_prog_id() { bpf_map_lookup_elem::<_, bpf_devmap_val>(fd, &index, flags).map(|value| { value.map(|value| DevMapValue { if_index: value.ifindex, @@ -136,8 +134,7 @@ impl> DevMap { let data = self.inner.borrow_mut(); check_bounds(data, index)?; let fd = data.fd().as_fd(); - - let res = if FEATURES.devmap_prog_id() { + let res = if data.features.devmap_prog_id() { let mut value = unsafe { std::mem::zeroed::() }; value.ifindex = target_if_index; // Default is valid as the kernel will only consider fd > 0: diff --git a/aya/src/maps/xdp/dev_map_hash.rs b/aya/src/maps/xdp/dev_map_hash.rs index 1b941bc4..e18d4026 100644 --- a/aya/src/maps/xdp/dev_map_hash.rs +++ b/aya/src/maps/xdp/dev_map_hash.rs @@ -13,7 +13,6 @@ use crate::{ maps::{check_kv_size, hash_map, IterableMap, MapData, MapError, MapIter, MapKeys}, programs::ProgramFd, sys::{bpf_map_lookup_elem, SyscallError}, - FEATURES, }; /// An hashmap of network devices. @@ -49,7 +48,7 @@ impl> DevMapHash { pub(crate) fn new(map: T) -> Result { let data = map.borrow(); - if FEATURES.devmap_prog_id() { + if data.features.devmap_prog_id() { check_kv_size::(data)?; } else { check_kv_size::(data)?; @@ -65,8 +64,9 @@ impl> DevMapHash { /// Returns [`MapError::SyscallError`] if `bpf_map_lookup_elem` fails. pub fn get(&self, key: u32, flags: u64) -> Result { let fd = self.inner.borrow().fd().as_fd(); + let data = self.inner.borrow(); - let value = if FEATURES.devmap_prog_id() { + let value = if data.features.devmap_prog_id() { bpf_map_lookup_elem::<_, bpf_devmap_val>(fd, &key, flags).map(|value| { value.map(|value| DevMapValue { if_index: value.ifindex, @@ -127,7 +127,8 @@ impl> DevMapHash { program: Option<&ProgramFd>, flags: u64, ) -> Result<(), XdpMapError> { - if FEATURES.devmap_prog_id() { + let data = self.inner.borrow(); + if data.features.devmap_prog_id() { let mut value = unsafe { std::mem::zeroed::() }; value.ifindex = target_if_index; // Default is valid as the kernel will only consider fd > 0: diff --git a/aya/src/programs/cgroup_skb.rs b/aya/src/programs/cgroup_skb.rs index 84749423..0006db9a 100644 --- a/aya/src/programs/cgroup_skb.rs +++ b/aya/src/programs/cgroup_skb.rs @@ -1,6 +1,13 @@ //! Cgroup skb programs. -use std::{hash::Hash, os::fd::AsFd, path::Path}; +use std::{ + hash::Hash, + os::fd::{AsFd, OwnedFd}, + path::Path, + sync::Arc, +}; + +use aya_obj::Features; use crate::{ generated::{ @@ -140,8 +147,11 @@ impl CgroupSkb { pub fn from_pin>( path: P, expected_attach_type: CgroupSkbAttachType, + token_fd: Option>, + features: Features, ) -> Result { - let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; + let data = + ProgramData::from_pinned_path(path, VerifierLogLevel::default(), token_fd, features)?; Ok(Self { data, expected_attach_type: Some(expected_attach_type), diff --git a/aya/src/programs/cgroup_sock.rs b/aya/src/programs/cgroup_sock.rs index 24f07869..e8cd376b 100644 --- a/aya/src/programs/cgroup_sock.rs +++ b/aya/src/programs/cgroup_sock.rs @@ -1,8 +1,14 @@ //! Cgroup socket programs. -use std::{hash::Hash, os::fd::AsFd, path::Path}; +use std::{ + hash::Hash, + os::fd::{AsFd, OwnedFd}, + path::Path, + sync::Arc, +}; pub use aya_obj::programs::CgroupSockAttachType; +use aya_obj::Features; use crate::{ generated::bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCK, @@ -115,8 +121,11 @@ impl CgroupSock { pub fn from_pin>( path: P, attach_type: CgroupSockAttachType, + token_fd: Option>, + features: Features, ) -> Result { - let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; + let data = + ProgramData::from_pinned_path(path, VerifierLogLevel::default(), token_fd, features)?; Ok(Self { data, attach_type }) } } diff --git a/aya/src/programs/cgroup_sock_addr.rs b/aya/src/programs/cgroup_sock_addr.rs index 79b607ca..eb44f2b7 100644 --- a/aya/src/programs/cgroup_sock_addr.rs +++ b/aya/src/programs/cgroup_sock_addr.rs @@ -1,8 +1,14 @@ //! Cgroup socket address programs. -use std::{hash::Hash, os::fd::AsFd, path::Path}; +use std::{ + hash::Hash, + os::fd::{AsFd, OwnedFd}, + path::Path, + sync::Arc, +}; pub use aya_obj::programs::CgroupSockAddrAttachType; +use aya_obj::Features; use crate::{ generated::bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCK_ADDR, @@ -119,8 +125,11 @@ impl CgroupSockAddr { pub fn from_pin>( path: P, attach_type: CgroupSockAddrAttachType, + token_fd: Option>, + features: Features, ) -> Result { - let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; + let data = + ProgramData::from_pinned_path(path, VerifierLogLevel::default(), token_fd, features)?; Ok(Self { data, attach_type }) } } diff --git a/aya/src/programs/cgroup_sockopt.rs b/aya/src/programs/cgroup_sockopt.rs index 414fece6..c109804f 100644 --- a/aya/src/programs/cgroup_sockopt.rs +++ b/aya/src/programs/cgroup_sockopt.rs @@ -1,8 +1,14 @@ //! Cgroup socket option programs. -use std::{hash::Hash, os::fd::AsFd, path::Path}; +use std::{ + hash::Hash, + os::fd::{AsFd, OwnedFd}, + path::Path, + sync::Arc, +}; pub use aya_obj::programs::CgroupSockoptAttachType; +use aya_obj::Features; use crate::{ generated::bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCKOPT, @@ -118,8 +124,11 @@ impl CgroupSockopt { pub fn from_pin>( path: P, attach_type: CgroupSockoptAttachType, + token_fd: Option>, + features: Features, ) -> Result { - let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; + let data = + ProgramData::from_pinned_path(path, VerifierLogLevel::default(), token_fd, features)?; Ok(Self { data, attach_type }) } } diff --git a/aya/src/programs/kprobe.rs b/aya/src/programs/kprobe.rs index 81e830e9..8c83846b 100644 --- a/aya/src/programs/kprobe.rs +++ b/aya/src/programs/kprobe.rs @@ -2,10 +2,12 @@ use std::{ ffi::OsStr, io, - os::fd::AsFd as _, + os::fd::{AsFd as _, OwnedFd}, path::{Path, PathBuf}, + sync::Arc, }; +use aya_obj::Features; use thiserror::Error; use crate::{ @@ -78,7 +80,15 @@ impl KProbe { fn_name: T, offset: u64, ) -> Result { - attach(&mut self.data, self.kind, fn_name.as_ref(), offset, None) + let features = self.data.features.clone(); + attach( + &mut self.data, + self.kind, + fn_name.as_ref(), + offset, + None, + &features, + ) } /// Detaches the program. @@ -102,8 +112,14 @@ impl KProbe { /// /// On drop, any managed links are detached and the program is unloaded. This will not result in /// the program being unloaded from the kernel if it is still pinned. - pub fn from_pin>(path: P, kind: ProbeKind) -> Result { - let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; + pub fn from_pin>( + path: P, + kind: ProbeKind, + token_fd: Option>, + features: Features, + ) -> Result { + let data = + ProgramData::from_pinned_path(path, VerifierLogLevel::default(), token_fd, features)?; Ok(Self { data, kind }) } } diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index 675e36f0..e3f4d056 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -78,6 +78,7 @@ use std::{ time::{Duration, SystemTime}, }; +use aya_obj::Features; use libc::ENOSPC; use thiserror::Error; @@ -464,6 +465,8 @@ pub(crate) struct ProgramData { pub(crate) attach_btf_id: Option, pub(crate) attach_prog_fd: Option, pub(crate) btf_fd: Option>, + pub(crate) token_fd: Option>, + pub(crate) features: Features, pub(crate) verifier_log_level: VerifierLogLevel, pub(crate) path: Option, pub(crate) flags: u32, @@ -475,6 +478,8 @@ impl ProgramData { obj: (obj::Program, obj::Function), btf_fd: Option>, verifier_log_level: VerifierLogLevel, + token_fd: Option>, + features: Features, ) -> Self { Self { name, @@ -489,6 +494,8 @@ impl ProgramData { verifier_log_level, path: None, flags: 0, + token_fd, + features, } } @@ -498,6 +505,8 @@ impl ProgramData { path: &Path, info: bpf_prog_info, verifier_log_level: VerifierLogLevel, + token_fd: Option>, + features: Features, ) -> Result { let attach_btf_id = if info.attach_btf_id > 0 { Some(info.attach_btf_id) @@ -521,12 +530,16 @@ impl ProgramData { verifier_log_level, path: Some(path.to_path_buf()), flags: 0, + token_fd, + features, }) } pub(crate) fn from_pinned_path>( path: P, verifier_log_level: VerifierLogLevel, + token_fd: Option>, + features: Features, ) -> Result { use std::os::unix::ffi::OsStrExt as _; @@ -539,7 +552,15 @@ impl ProgramData { let info = ProgramInfo::new_from_fd(fd.as_fd())?; let name = info.name_as_str().map(|s| s.to_string()); - Self::from_bpf_prog_info(name, fd, path.as_ref(), info.0, verifier_log_level) + Self::from_bpf_prog_info( + name, + fd, + path.as_ref(), + info.0, + verifier_log_level, + token_fd, + features, + ) } } @@ -601,6 +622,8 @@ fn load_program( verifier_log_level, path: _, flags, + token_fd, + features: _, } = data; if fd.is_some() { return Err(ProgramError::AlreadyLoaded); @@ -660,7 +683,12 @@ fn load_program( }; let (ret, verifier_log) = retry_with_verifier_logs(10, |logger| { - bpf_load_program(&attr, logger, *verifier_log_level) + bpf_load_program( + &attr, + logger, + *verifier_log_level, + token_fd.as_ref().map(|fd| fd.as_fd()), + ) }); match ret { @@ -869,8 +897,8 @@ macro_rules! impl_from_pin { /// /// On drop, any managed links are detached and the program is unloaded. This will not result in /// the program being unloaded from the kernel if it is still pinned. - pub fn from_pin>(path: P) -> Result { - let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; + pub fn from_pin>(path: P, token_fd: Option>, features: Features) -> Result { + let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default(), token_fd, features)?; Ok(Self { data }) } } diff --git a/aya/src/programs/perf_attach.rs b/aya/src/programs/perf_attach.rs index 8cf04871..02925df7 100644 --- a/aya/src/programs/perf_attach.rs +++ b/aya/src/programs/perf_attach.rs @@ -1,6 +1,8 @@ //! Perf attach links. use std::os::fd::{AsFd as _, AsRawFd as _, BorrowedFd, OwnedFd, RawFd}; +use aya_obj::Features; + use crate::{ generated::bpf_attach_type::BPF_PERF_EVENT, programs::{ @@ -8,7 +10,7 @@ use crate::{ FdLink, Link, ProgramError, }, sys::{bpf_link_create, perf_event_ioctl, LinkTarget, SysResult, SyscallError}, - FEATURES, PERF_EVENT_IOC_DISABLE, PERF_EVENT_IOC_ENABLE, PERF_EVENT_IOC_SET_BPF, + PERF_EVENT_IOC_DISABLE, PERF_EVENT_IOC_ENABLE, PERF_EVENT_IOC_SET_BPF, }; #[derive(Debug, Hash, Eq, PartialEq)] @@ -73,8 +75,9 @@ impl Link for PerfLink { pub(crate) fn perf_attach( prog_fd: BorrowedFd<'_>, fd: OwnedFd, + features: &Features, ) -> Result { - if FEATURES.bpf_perf_link() { + if features.bpf_perf_link() { let link_fd = bpf_link_create(prog_fd, LinkTarget::Fd(fd.as_fd()), BPF_PERF_EVENT, None, 0) .map_err(|(_, io_error)| SyscallError { call: "bpf_link_create", diff --git a/aya/src/programs/perf_event.rs b/aya/src/programs/perf_event.rs index 00ac4b38..53ab575e 100644 --- a/aya/src/programs/perf_event.rs +++ b/aya/src/programs/perf_event.rs @@ -180,7 +180,7 @@ impl PerfEvent { io_error, })?; - let link = perf_attach(prog_fd, fd)?; + let link = perf_attach(prog_fd, fd, &self.data.features)?; self.data.links.insert(PerfEventLink::new(link)) } diff --git a/aya/src/programs/probe.rs b/aya/src/programs/probe.rs index 85be3b3c..303e5dd4 100644 --- a/aya/src/programs/probe.rs +++ b/aya/src/programs/probe.rs @@ -9,6 +9,7 @@ use std::{ sync::atomic::{AtomicUsize, Ordering}, }; +use aya_obj::Features; use libc::pid_t; use crate::{ @@ -112,6 +113,7 @@ pub(crate) fn attach>( fn_name: &OsStr, offset: u64, pid: Option, + features: &Features, ) -> Result { // https://github.com/torvalds/linux/commit/e12f03d7031a977356e3d7b75a68c2185ff8d155 // Use debugfs to create probe @@ -122,7 +124,7 @@ pub(crate) fn attach>( perf_attach_debugfs(prog_fd, fd, ProbeEvent { kind, event_alias }) } else { let fd = create_as_probe(kind, fn_name, offset, pid)?; - perf_attach(prog_fd, fd) + perf_attach(prog_fd, fd, features) }?; program_data.links.insert(T::from(link)) } diff --git a/aya/src/programs/sk_skb.rs b/aya/src/programs/sk_skb.rs index 4d571dcb..7fe09f84 100644 --- a/aya/src/programs/sk_skb.rs +++ b/aya/src/programs/sk_skb.rs @@ -1,6 +1,12 @@ //! Skskb programs. -use std::{os::fd::AsFd as _, path::Path}; +use std::{ + os::fd::{AsFd as _, OwnedFd}, + path::Path, + sync::Arc, +}; + +use aya_obj::Features; use crate::{ generated::{ @@ -116,8 +122,14 @@ impl SkSkb { /// /// On drop, any managed links are detached and the program is unloaded. This will not result in /// the program being unloaded from the kernel if it is still pinned. - pub fn from_pin>(path: P, kind: SkSkbKind) -> Result { - let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; + pub fn from_pin>( + path: P, + kind: SkSkbKind, + token_fd: Option>, + features: Features, + ) -> Result { + let data = + ProgramData::from_pinned_path(path, VerifierLogLevel::default(), token_fd, features)?; Ok(Self { data, kind }) } } diff --git a/aya/src/programs/tc.rs b/aya/src/programs/tc.rs index c2dd5a13..508f6ef3 100644 --- a/aya/src/programs/tc.rs +++ b/aya/src/programs/tc.rs @@ -2,10 +2,12 @@ use std::{ ffi::{CStr, CString}, io, - os::fd::AsFd as _, + os::fd::{AsFd as _, OwnedFd}, path::Path, + sync::Arc, }; +use aya_obj::Features; use thiserror::Error; use crate::{ @@ -230,8 +232,13 @@ impl SchedClassifier { /// /// On drop, any managed links are detached and the program is unloaded. This will not result in /// the program being unloaded from the kernel if it is still pinned. - pub fn from_pin>(path: P) -> Result { - let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; + pub fn from_pin>( + path: P, + token_fd: Option>, + features: Features, + ) -> Result { + let data = + ProgramData::from_pinned_path(path, VerifierLogLevel::default(), token_fd, features)?; Ok(Self { data }) } } diff --git a/aya/src/programs/trace_point.rs b/aya/src/programs/trace_point.rs index 9d304204..c7c959bd 100644 --- a/aya/src/programs/trace_point.rs +++ b/aya/src/programs/trace_point.rs @@ -89,7 +89,7 @@ impl TracePoint { io_error, })?; - let link = perf_attach(prog_fd, fd)?; + let link = perf_attach(prog_fd, fd, &self.data.features)?; self.data.links.insert(TracePointLink::new(link)) } diff --git a/aya/src/programs/uprobe.rs b/aya/src/programs/uprobe.rs index 28d6749f..9042c68b 100644 --- a/aya/src/programs/uprobe.rs +++ b/aya/src/programs/uprobe.rs @@ -6,11 +6,16 @@ use std::{ fs, io::{self, BufRead, Cursor, Read}, mem, - os::{fd::AsFd as _, raw::c_char, unix::ffi::OsStrExt}, + os::{ + fd::{AsFd as _, OwnedFd}, + raw::c_char, + unix::ffi::OsStrExt, + }, path::{Path, PathBuf}, sync::Arc, }; +use aya_obj::Features; use libc::pid_t; use object::{Object, ObjectSection, ObjectSymbol, Symbol}; use thiserror::Error; @@ -96,7 +101,15 @@ impl UProbe { }; let path = path.as_os_str(); - attach(&mut self.data, self.kind, path, sym_offset + offset, pid) + let features = self.data.features.clone(); + attach( + &mut self.data, + self.kind, + path, + sym_offset + offset, + pid, + &features, + ) } /// Detaches the program. @@ -120,8 +133,14 @@ impl UProbe { /// /// On drop, any managed links are detached and the program is unloaded. This will not result in /// the program being unloaded from the kernel if it is still pinned. - pub fn from_pin>(path: P, kind: ProbeKind) -> Result { - let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; + pub fn from_pin>( + path: P, + kind: ProbeKind, + token_fd: Option>, + features: Features, + ) -> Result { + let data = + ProgramData::from_pinned_path(path, VerifierLogLevel::default(), token_fd, features)?; Ok(Self { data, kind }) } } diff --git a/aya/src/programs/xdp.rs b/aya/src/programs/xdp.rs index 5d49f4f1..2b9a14a3 100644 --- a/aya/src/programs/xdp.rs +++ b/aya/src/programs/xdp.rs @@ -4,10 +4,12 @@ use std::{ ffi::CString, hash::Hash, io, - os::fd::{AsFd as _, AsRawFd as _, BorrowedFd, RawFd}, + os::fd::{AsFd as _, AsRawFd as _, BorrowedFd, OwnedFd, RawFd}, path::Path, + sync::Arc, }; +use aya_obj::Features; use libc::if_nametoindex; use thiserror::Error; @@ -183,8 +185,11 @@ impl Xdp { pub fn from_pin>( path: P, attach_type: XdpAttachType, + token_fd: Option>, + features: Features, ) -> Result { - let mut data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; + let mut data = + ProgramData::from_pinned_path(path, VerifierLogLevel::default(), token_fd, features)?; data.expected_attach_type = Some(attach_type.into()); Ok(Self { data, attach_type }) } diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs index 1ae1dc95..5625493f 100644 --- a/aya/src/sys/bpf.rs +++ b/aya/src/sys/bpf.rs @@ -3,12 +3,21 @@ use std::{ ffi::{c_char, CStr, CString}, io, iter, mem::{self, MaybeUninit}, - os::fd::{AsFd as _, AsRawFd as _, BorrowedFd, FromRawFd as _, OwnedFd, RawFd}, + os::{ + fd::{AsFd as _, AsRawFd as _, BorrowedFd, FromRawFd as _, OwnedFd, RawFd}, + unix::ffi::OsStrExt as _, + }, + path::Path, + ptr::null, slice, }; use assert_matches::assert_matches; -use libc::{ENOENT, ENOSPC}; +use aya_obj::{ + attach::BpfAttachType, cmd::BpfCommand, generated::BPF_F_TOKEN_FD, maps::BpfMapType, + programs::BpfProgType, Features, +}; +use libc::{c_void, AT_FDCWD, ENOENT, ENOSPC, MOVE_MOUNT_F_EMPTY_PATH}; use obj::{ btf::{BtfEnum64, Enum64}, maps::{bpf_map_def, LegacyMap}, @@ -39,6 +48,7 @@ pub(crate) fn bpf_create_map( def: &obj::Map, btf_fd: Option>, kernel_version: KernelVersion, + token_fd: Option>, ) -> SysResult { let mut attr = unsafe { mem::zeroed::() }; @@ -48,6 +58,10 @@ pub(crate) fn bpf_create_map( u.value_size = def.value_size(); u.max_entries = def.max_entries(); u.map_flags = def.map_flags(); + if let Some(v) = token_fd { + u.map_token_fd = v.as_raw_fd(); + u.map_flags |= BPF_F_TOKEN_FD; + } if let obj::Map::Btf(m) = def { use bpf_map_type::*; @@ -134,6 +148,7 @@ pub(crate) fn bpf_load_program( aya_attr: &EbpfLoadProgramAttrs<'_>, log_buf: &mut [u8], verifier_log_level: VerifierLogLevel, + token_fd: Option>, ) -> SysResult { let mut attr = unsafe { mem::zeroed::() }; @@ -158,6 +173,10 @@ pub(crate) fn bpf_load_program( u.insn_cnt = aya_attr.insns.len() as u32; u.license = aya_attr.license.as_ptr() as u64; u.kern_version = aya_attr.kernel_version; + if let Some(v) = token_fd { + u.prog_token_fd = v.as_raw_fd(); + u.prog_flags |= BPF_F_TOKEN_FD; + } // these must be allocated here to ensure the slice outlives the pointer // so .as_ptr below won't point to garbage @@ -617,6 +636,7 @@ pub(crate) fn bpf_load_btf( raw_btf: &[u8], log_buf: &mut [u8], verifier_log_level: VerifierLogLevel, + token_fd: Option>, ) -> SysResult { let mut attr = unsafe { mem::zeroed::() }; let u = unsafe { &mut attr.__bindgen_anon_7 }; @@ -627,10 +647,21 @@ pub(crate) fn bpf_load_btf( u.btf_log_buf = log_buf.as_mut_ptr() as u64; u.btf_log_size = log_buf.len() as u32; } + if let Some(v) = token_fd { + u.btf_token_fd = v.as_raw_fd(); + u.btf_flags |= BPF_F_TOKEN_FD; + } // SAFETY: `BPF_BTF_LOAD` returns a newly created fd. unsafe { fd_sys_bpf(bpf_cmd::BPF_BTF_LOAD, &mut attr) } } +pub(crate) fn bpf_token_create(bpf_fs_fd: BorrowedFd<'_>, flags: u32) -> SysResult { + let mut attr = unsafe { mem::zeroed::() }; + attr.token_create.bpffs_fd = bpf_fs_fd.as_raw_fd() as u32; + attr.token_create.flags = flags; + unsafe { fd_sys_bpf(bpf_cmd::BPF_TOKEN_CREATE, &mut attr) } +} + // SAFETY: only use for bpf_cmd that return a new file descriptor on success. unsafe fn fd_sys_bpf(cmd: bpf_cmd, attr: &mut bpf_attr) -> SysResult { let fd = sys_bpf(cmd, attr)?; @@ -660,7 +691,7 @@ pub(crate) fn bpf_btf_get_fd_by_id(id: u32) -> Result { }) } -pub(crate) fn is_prog_name_supported() -> bool { +pub(crate) fn is_prog_name_supported(token_fd: Option>) -> bool { let mut attr = unsafe { mem::zeroed::() }; let u = unsafe { &mut attr.__bindgen_anon_3 }; let mut name: [c_char; 16] = [0; 16]; @@ -684,11 +715,14 @@ pub(crate) fn is_prog_name_supported() -> bool { u.insn_cnt = insns.len() as u32; u.insns = insns.as_ptr() as u64; u.prog_type = bpf_prog_type::BPF_PROG_TYPE_SOCKET_FILTER as u32; - + if let Some(v) = token_fd { + u.prog_token_fd = v.as_raw_fd(); + u.prog_flags |= BPF_F_TOKEN_FD; + } bpf_prog_load(&mut attr).is_ok() } -pub(crate) fn is_probe_read_kernel_supported() -> bool { +pub(crate) fn is_probe_read_kernel_supported(token_fd: Option>) -> bool { let mut attr = unsafe { mem::zeroed::() }; let u = unsafe { &mut attr.__bindgen_anon_3 }; @@ -708,11 +742,14 @@ pub(crate) fn is_probe_read_kernel_supported() -> bool { u.insn_cnt = insns.len() as u32; u.insns = insns.as_ptr() as u64; u.prog_type = bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT as u32; - + if let Some(v) = token_fd { + u.prog_token_fd = v.as_raw_fd(); + u.prog_flags |= BPF_F_TOKEN_FD; + } bpf_prog_load(&mut attr).is_ok() } -pub(crate) fn is_perf_link_supported() -> bool { +pub(crate) fn is_perf_link_supported(token_fd: Option>) -> bool { let mut attr = unsafe { mem::zeroed::() }; let u = unsafe { &mut attr.__bindgen_anon_3 }; @@ -728,7 +765,10 @@ pub(crate) fn is_perf_link_supported() -> bool { u.insn_cnt = insns.len() as u32; u.insns = insns.as_ptr() as u64; u.prog_type = bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT as u32; - + if let Some(v) = token_fd { + u.prog_token_fd = v.as_raw_fd(); + u.prog_flags |= BPF_F_TOKEN_FD; + } if let Ok(fd) = bpf_prog_load(&mut attr) { let fd = crate::MockableFd::from_fd(fd); let fd = fd.as_fd(); @@ -743,7 +783,7 @@ pub(crate) fn is_perf_link_supported() -> bool { } } -pub(crate) fn is_bpf_global_data_supported() -> bool { +pub(crate) fn is_bpf_global_data_supported(token_fd: Option>) -> bool { let mut attr = unsafe { mem::zeroed::() }; let u = unsafe { &mut attr.__bindgen_anon_3 }; @@ -773,6 +813,8 @@ pub(crate) fn is_bpf_global_data_supported() -> bool { }), "aya_global", None, + None, + Features::default(), ); if let Ok(map) = map { @@ -783,14 +825,17 @@ pub(crate) fn is_bpf_global_data_supported() -> bool { u.insn_cnt = insns.len() as u32; u.insns = insns.as_ptr() as u64; u.prog_type = bpf_prog_type::BPF_PROG_TYPE_SOCKET_FILTER as u32; - + if let Some(v) = token_fd { + u.prog_token_fd = v.as_raw_fd(); + u.prog_flags |= BPF_F_TOKEN_FD; + } bpf_prog_load(&mut attr).is_ok() } else { false } } -pub(crate) fn is_bpf_cookie_supported() -> bool { +pub(crate) fn is_bpf_cookie_supported(token_fd: Option>) -> bool { let mut attr = unsafe { mem::zeroed::() }; let u = unsafe { &mut attr.__bindgen_anon_3 }; @@ -806,12 +851,18 @@ pub(crate) fn is_bpf_cookie_supported() -> bool { u.insn_cnt = insns.len() as u32; u.insns = insns.as_ptr() as u64; u.prog_type = bpf_prog_type::BPF_PROG_TYPE_KPROBE as u32; - + if let Some(v) = token_fd { + u.prog_token_fd = v.as_raw_fd(); + u.prog_flags |= BPF_F_TOKEN_FD; + } bpf_prog_load(&mut attr).is_ok() } /// Tests whether CpuMap, DevMap and DevMapHash support program ids -pub(crate) fn is_prog_id_supported(map_type: bpf_map_type) -> bool { +pub(crate) fn is_prog_id_supported( + map_type: bpf_map_type, + token_fd: Option>, +) -> bool { assert_matches!( map_type, bpf_map_type::BPF_MAP_TYPE_CPUMAP @@ -828,22 +879,27 @@ pub(crate) fn is_prog_id_supported(map_type: bpf_map_type) -> bool { u.max_entries = 1; u.map_flags = 0; + if let Some(v) = token_fd { + u.map_token_fd = v.as_raw_fd(); + u.map_flags |= BPF_F_TOKEN_FD; + } + // SAFETY: BPF_MAP_CREATE returns a new file descriptor. let fd = unsafe { fd_sys_bpf(bpf_cmd::BPF_MAP_CREATE, &mut attr) }; let fd = fd.map(crate::MockableFd::from_fd); fd.is_ok() } -pub(crate) fn is_btf_supported() -> bool { +pub(crate) fn is_btf_supported(token_fd: Option>) -> bool { let mut btf = Btf::new(); let name_offset = btf.add_string("int"); let int_type = BtfType::Int(Int::new(name_offset, 4, IntEncoding::Signed, 0)); btf.add_type(int_type); let btf_bytes = btf.to_bytes(); - bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default()).is_ok() + bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default(), token_fd).is_ok() } -pub(crate) fn is_btf_func_supported() -> bool { +pub(crate) fn is_btf_func_supported(token_fd: Option>) -> bool { let mut btf = Btf::new(); let name_offset = btf.add_string("int"); let int_type = BtfType::Int(Int::new(name_offset, 4, IntEncoding::Signed, 0)); @@ -870,10 +926,10 @@ pub(crate) fn is_btf_func_supported() -> bool { let btf_bytes = btf.to_bytes(); - bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default()).is_ok() + bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default(), token_fd).is_ok() } -pub(crate) fn is_btf_func_global_supported() -> bool { +pub(crate) fn is_btf_func_global_supported(token_fd: Option>) -> bool { let mut btf = Btf::new(); let name_offset = btf.add_string("int"); let int_type = BtfType::Int(Int::new(name_offset, 4, IntEncoding::Signed, 0)); @@ -900,10 +956,10 @@ pub(crate) fn is_btf_func_global_supported() -> bool { let btf_bytes = btf.to_bytes(); - bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default()).is_ok() + bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default(), token_fd).is_ok() } -pub(crate) fn is_btf_datasec_supported() -> bool { +pub(crate) fn is_btf_datasec_supported(token_fd: Option>) -> bool { let mut btf = Btf::new(); let name_offset = btf.add_string("int"); let int_type = BtfType::Int(Int::new(name_offset, 4, IntEncoding::Signed, 0)); @@ -924,10 +980,10 @@ pub(crate) fn is_btf_datasec_supported() -> bool { let btf_bytes = btf.to_bytes(); - bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default()).is_ok() + bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default(), token_fd).is_ok() } -pub(crate) fn is_btf_enum64_supported() -> bool { +pub(crate) fn is_btf_enum64_supported(token_fd: Option>) -> bool { let mut btf = Btf::new(); let name_offset = btf.add_string("enum64"); @@ -940,10 +996,10 @@ pub(crate) fn is_btf_enum64_supported() -> bool { let btf_bytes = btf.to_bytes(); - bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default()).is_ok() + bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default(), token_fd).is_ok() } -pub(crate) fn is_btf_float_supported() -> bool { +pub(crate) fn is_btf_float_supported(token_fd: Option>) -> bool { let mut btf = Btf::new(); let name_offset = btf.add_string("float"); let float_type = BtfType::Float(Float::new(name_offset, 16)); @@ -951,10 +1007,10 @@ pub(crate) fn is_btf_float_supported() -> bool { let btf_bytes = btf.to_bytes(); - bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default()).is_ok() + bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default(), token_fd).is_ok() } -pub(crate) fn is_btf_decl_tag_supported() -> bool { +pub(crate) fn is_btf_decl_tag_supported(token_fd: Option>) -> bool { let mut btf = Btf::new(); let name_offset = btf.add_string("int"); let int_type = BtfType::Int(Int::new(name_offset, 4, IntEncoding::Signed, 0)); @@ -970,10 +1026,10 @@ pub(crate) fn is_btf_decl_tag_supported() -> bool { let btf_bytes = btf.to_bytes(); - bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default()).is_ok() + bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default(), token_fd).is_ok() } -pub(crate) fn is_btf_type_tag_supported() -> bool { +pub(crate) fn is_btf_type_tag_supported(token_fd: Option>) -> bool { let mut btf = Btf::new(); let int_type = BtfType::Int(Int::new(0, 4, IntEncoding::Signed, 0)); @@ -987,7 +1043,7 @@ pub(crate) fn is_btf_type_tag_supported() -> bool { let btf_bytes = btf.to_bytes(); - bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default()).is_ok() + bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default(), token_fd).is_ok() } fn bpf_prog_load(attr: &mut bpf_attr) -> SysResult { @@ -999,6 +1055,274 @@ fn sys_bpf(cmd: bpf_cmd, attr: &mut bpf_attr) -> SysResult { syscall(Syscall::Ebpf { cmd, attr }) } +/// The permissions of an eBPF filesystem. +/// +/// By default, all commands, programs, maps and attach types are allowed. +/// You can restrict the permissions by adding specific commands, programs, maps +/// and attach types. +#[derive(Debug, Clone)] +pub struct FilesystemPermissions { + cmds: CString, + progs: CString, + maps: CString, + attach: CString, +} + +impl Default for FilesystemPermissions { + fn default() -> Self { + Self { + cmds: CString::new("any").unwrap(), + progs: CString::new("any").unwrap(), + maps: CString::new("any").unwrap(), + attach: CString::new("any").unwrap(), + } + } +} + +impl FilesystemPermissions { + /// Create a new set of permissions using a `[`FilesystemPermissionsBuilder`]`. + pub fn builder() -> FilesystemPermissionsBuilder { + FilesystemPermissionsBuilder::default() + } +} + +/// Builder for [`FilesystemPermissions`]. +#[derive(Debug, Clone, Default)] +pub struct FilesystemPermissionsBuilder { + cmds: Vec, + progs: Vec, + maps: Vec, + attach: Vec, +} + +impl FilesystemPermissionsBuilder { + /// Add a command to the permissions. + pub fn cmd(&mut self, cmd: BpfCommand) -> &mut Self { + self.cmds.push(cmd); + self + } + + /// Add a program type to the permissions. + pub fn prog(&mut self, prog: BpfProgType) -> &mut Self { + self.progs.push(prog); + self + } + + /// Add a map type to the permissions. + pub fn map(&mut self, map: BpfMapType) -> &mut Self { + self.maps.push(map); + self + } + + /// Add an attach type to the permissions. + pub fn attach(&mut self, attach: BpfAttachType) -> &mut Self { + self.attach.push(attach); + self + } + + /// Build the permissions. + pub fn build(&mut self) -> FilesystemPermissions { + let cmds_mask = if self.cmds.is_empty() { + CString::new("any").unwrap() + } else { + CString::new(format!( + "0x{:#x}", + self.cmds + .iter() + .map(|cmd| 1u64 << bpf_cmd::from(*cmd) as u64) + .fold(0, |acc, cmd| acc | cmd) + )) + .unwrap() + }; + let progs_mask = if self.progs.is_empty() { + CString::new("any").unwrap() + } else { + CString::new(format!( + "0x{:#x}", + self.progs + .iter() + .map(|prog| 1u64 << bpf_prog_type::from(*prog) as u64) + .fold(0, |acc, prog| acc | prog) + )) + .unwrap() + }; + let maps_mask = if self.maps.is_empty() { + CString::new("any").unwrap() + } else { + CString::new(format!( + "0x{:#x}", + self.maps + .iter() + .map(|map| 1u64 << bpf_map_type::from(*map) as u64) + .fold(0, |acc, map| acc | map) + )) + .unwrap() + }; + let attach_mask = if self.attach.is_empty() { + CString::new("any").unwrap() + } else { + CString::new(format!( + "0x{:#x}", + self.attach + .iter() + .map(|attach| 1u64 << bpf_attach_type::from(*attach) as u64) + .fold(0, |acc, attach| acc | attach) + )) + .unwrap() + }; + FilesystemPermissions { + cmds: cmds_mask, + progs: progs_mask, + maps: maps_mask, + attach: attach_mask, + } + } +} + +const FSCONFIG_SET_STRING: u32 = 1; +const FSCONFIG_CMD_CREATE: u32 = 6; + +/// Create a new eBPF filesystem at the given path. +/// +/// # Example +/// +/// ```no_run +/// use aya_obj::{cmd::BpfCommand, maps::BpfMapType, programs::BpfProgType}; +/// use aya::{FilesystemPermissionsBuilder, create_bpf_filesystem}; +/// +/// let path = "/sys/fs/bpf"; +/// let perms = FilesystemPermissionsBuilder::default() +/// .cmd(BpfCommand::ProgLoad) +/// .prog(BpfProgType::SocketFilter) +/// .map(BpfMapType::Array) +/// .build(); +/// create_bpf_filesystem(path, perms).unwrap(); +/// ``` +pub fn create_bpf_filesystem>( + path: P, + perms: FilesystemPermissions, +) -> Result<(), SyscallError> { + let path = path.as_ref(); + + let path_string = CString::new(path.as_os_str().as_bytes()).map_err(|_| SyscallError { + call: "create_bpffs", + io_error: io::Error::new(io::ErrorKind::InvalidInput, "path contains a null byte"), + })?; + + let fd = syscall(Syscall::FsOpen { + fsname: c"bpf".as_ptr(), + flags: 0, + }) + .map_err(|(_, io_error)| SyscallError { + call: "fsopen", + io_error, + })?; + + let fd = fd.try_into().map_err(|_| SyscallError { + call: "fsopen", + io_error: io::Error::new( + io::ErrorKind::InvalidData, + format!("create_bpffs: invalid fd returned: {fd}"), + ), + })?; + + let fd = unsafe { OwnedFd::from_raw_fd(fd) }; + + let _ = syscall(Syscall::FsConfig { + fd: fd.as_fd(), + cmd: FSCONFIG_SET_STRING, + key: c"delegate_cmds".as_ptr(), + value: perms.cmds.as_ptr() as *const c_void, + aux: 0, + }) + .map_err(|(_, io_error)| SyscallError { + call: "fsconfig", + io_error, + })?; + + let _ = syscall(Syscall::FsConfig { + fd: fd.as_fd(), + cmd: FSCONFIG_SET_STRING, + key: c"delegate_maps".as_ptr(), + value: perms.maps.as_ptr() as *const c_void, + aux: 0, + }) + .map_err(|(_, io_error)| SyscallError { + call: "fsconfig", + io_error, + })?; + + let _ = syscall(Syscall::FsConfig { + fd: fd.as_fd(), + cmd: FSCONFIG_SET_STRING, + key: c"delegate_progs".as_ptr(), + value: perms.progs.as_ptr() as *const c_void, + aux: 0, + }) + .map_err(|(_, io_error)| SyscallError { + call: "fsconfig", + io_error, + })?; + + let _ = syscall(Syscall::FsConfig { + fd: fd.as_fd(), + cmd: FSCONFIG_SET_STRING, + key: c"delegate_attachs".as_ptr(), + value: perms.attach.as_ptr() as *const c_void, + aux: 0, + }) + .map_err(|(_, io_error)| SyscallError { + call: "fsconfig", + io_error, + })?; + + let _ = syscall(Syscall::FsConfig { + fd: fd.as_fd(), + cmd: FSCONFIG_CMD_CREATE, + key: null(), + value: null(), + aux: 0, + }) + .map_err(|(_, io_error)| SyscallError { + call: "fsconfig", + io_error, + })?; + + let mount_fd = syscall(Syscall::FsMount { + fd: fd.as_fd(), + flags: 0, + mount_attrs: 0, + }) + .map_err(|(_, io_error)| SyscallError { + call: "fsmount", + io_error, + })?; + + let mount_fd = mount_fd.try_into().map_err(|_| SyscallError { + call: "fsopen", + io_error: io::Error::new( + io::ErrorKind::InvalidData, + format!("create_bpffs: invalid fd returned: {mount_fd}"), + ), + })?; + + let mount_fd = unsafe { OwnedFd::from_raw_fd(mount_fd) }; + + let _ = syscall(Syscall::MoveMount { + from_dirfd: mount_fd.as_fd(), + from_pathname: c"".as_ptr(), + to_dirfd: unsafe { BorrowedFd::borrow_raw(AT_FDCWD) }, + to_pathname: path_string.as_ptr(), + flags: MOVE_MOUNT_F_EMPTY_PATH, + }) + .map_err(|(_, io_error)| SyscallError { + call: "fsmount", + io_error, + })?; + + Ok(()) +} + fn bpf_obj_get_next_id( id: u32, cmd: bpf_cmd, @@ -1105,7 +1429,7 @@ mod tests { } => Err((-1, io::Error::from_raw_os_error(EBADF))), _ => Ok(crate::MockableFd::mock_signed_fd().into()), }); - let supported = is_perf_link_supported(); + let supported = is_perf_link_supported(None); assert!(supported); override_syscall(|call| match call { @@ -1115,7 +1439,7 @@ mod tests { } => Err((-1, io::Error::from_raw_os_error(EINVAL))), _ => Ok(crate::MockableFd::mock_signed_fd().into()), }); - let supported = is_perf_link_supported(); + let supported = is_perf_link_supported(None); assert!(!supported); } @@ -1124,15 +1448,15 @@ mod tests { override_syscall(|_call| Ok(crate::MockableFd::mock_signed_fd().into())); // Ensure that the three map types we can check are accepted - let supported = is_prog_id_supported(bpf_map_type::BPF_MAP_TYPE_CPUMAP); + let supported = is_prog_id_supported(bpf_map_type::BPF_MAP_TYPE_CPUMAP, None); assert!(supported); - let supported = is_prog_id_supported(bpf_map_type::BPF_MAP_TYPE_DEVMAP); + let supported = is_prog_id_supported(bpf_map_type::BPF_MAP_TYPE_DEVMAP, None); assert!(supported); - let supported = is_prog_id_supported(bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH); + let supported = is_prog_id_supported(bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH, None); assert!(supported); override_syscall(|_call| Err((-1, io::Error::from_raw_os_error(EINVAL)))); - let supported = is_prog_id_supported(bpf_map_type::BPF_MAP_TYPE_CPUMAP); + let supported = is_prog_id_supported(bpf_map_type::BPF_MAP_TYPE_CPUMAP, None); assert!(!supported); } @@ -1140,6 +1464,27 @@ mod tests { #[should_panic = "assertion failed: `BPF_MAP_TYPE_HASH` does not match `bpf_map_type::BPF_MAP_TYPE_CPUMAP | bpf_map_type::BPF_MAP_TYPE_DEVMAP | bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH`"] fn test_prog_id_supported_reject_types() { - is_prog_id_supported(bpf_map_type::BPF_MAP_TYPE_HASH); + is_prog_id_supported(bpf_map_type::BPF_MAP_TYPE_HASH, None); + } + + #[test] + fn test_bpf_create_filesystem() { + override_syscall(|call| match call { + Syscall::FsOpen { .. } => Ok(crate::MockableFd::mock_signed_fd().into()), + Syscall::FsConfig { .. } => Ok(0), + Syscall::FsMount { + flags, mount_attrs, .. + } => { + assert_eq!(flags, 0); + assert_eq!(mount_attrs, 0); + Ok(crate::MockableFd::mock_signed_fd().into()) + } + Syscall::MoveMount { flags, .. } => { + assert_eq!(flags, MOVE_MOUNT_F_EMPTY_PATH); + Ok(0) + } + _ => Ok(crate::MockableFd::mock_signed_fd().into()), + }); + create_bpf_filesystem("/foo", FilesystemPermissions::default()).unwrap(); } } diff --git a/aya/src/sys/mod.rs b/aya/src/sys/mod.rs index ab486195..a0dd7076 100644 --- a/aya/src/sys/mod.rs +++ b/aya/src/sys/mod.rs @@ -6,15 +6,19 @@ mod perf_event; mod fake; use std::{ - ffi::{c_int, c_void}, + ffi::{c_int, c_uint, c_void}, io, mem, os::fd::{AsRawFd as _, BorrowedFd}, }; pub(crate) use bpf::*; +pub use bpf::{create_bpf_filesystem, FilesystemPermissions, FilesystemPermissionsBuilder}; #[cfg(test)] pub(crate) use fake::*; -use libc::{pid_t, SYS_bpf, SYS_perf_event_open}; +use libc::{ + c_char, pid_t, SYS_bpf, SYS_fsconfig, SYS_fsmount, SYS_fsopen, SYS_move_mount, + SYS_perf_event_open, +}; #[doc(hidden)] pub use netlink::netlink_set_link_up; pub(crate) use netlink::*; @@ -42,6 +46,29 @@ pub(crate) enum Syscall<'a> { request: c_int, arg: c_int, }, + FsOpen { + fsname: *const c_char, + flags: u32, + }, + FsMount { + fd: BorrowedFd<'a>, + flags: c_uint, + mount_attrs: c_uint, + }, + FsConfig { + fd: BorrowedFd<'a>, + cmd: c_uint, + key: *const c_char, + value: *const c_void, + aux: c_int, + }, + MoveMount { + from_dirfd: BorrowedFd<'a>, + from_pathname: *const c_char, + to_dirfd: BorrowedFd<'a>, + to_pathname: *const c_char, + flags: u32, + }, } #[derive(Debug, Error)] @@ -82,6 +109,49 @@ impl std::fmt::Debug for Syscall<'_> { .field("request", request) .field("arg", arg) .finish(), + Self::FsOpen { fsname, flags } => f + .debug_struct("Syscall::FsOpen") + .field("fsname", fsname) + .field("flags", flags) + .finish(), + Self::FsMount { + fd, + flags, + mount_attrs, + } => f + .debug_struct("Syscall::FsMount") + .field("fd", fd) + .field("flags", flags) + .field("mount_attrs", mount_attrs) + .finish(), + Self::FsConfig { + fd, + cmd, + key, + value, + aux, + } => f + .debug_struct("Syscall::FsConfig") + .field("fd", fd) + .field("cmd", cmd) + .field("key", key) + .field("value", value) + .field("aux", aux) + .finish(), + Self::MoveMount { + from_dirfd, + from_pathname, + to_dirfd, + to_pathname, + flags, + } => f + .debug_struct("Syscall::MoveMount") + .field("from_dirfd", from_dirfd) + .field("from_pathname", from_pathname) + .field("to_dirfd", to_dirfd) + .field("to_pathname", to_pathname) + .field("flags", flags) + .finish(), } } } @@ -110,6 +180,33 @@ fn syscall(call: Syscall<'_>) -> SysResult { #[allow(clippy::useless_conversion)] ret.into() } + Syscall::FsOpen { fsname, flags } => libc::syscall(SYS_fsopen, fsname, flags), + Syscall::FsMount { + fd, + flags, + mount_attrs, + } => libc::syscall(SYS_fsmount, fd.as_raw_fd(), flags, mount_attrs), + Syscall::FsConfig { + fd, + cmd, + key, + value, + aux, + } => libc::syscall(SYS_fsconfig, fd.as_raw_fd(), cmd, key, value, aux), + Syscall::MoveMount { + from_dirfd, + from_pathname, + to_dirfd, + to_pathname, + flags, + } => libc::syscall( + SYS_move_mount, + from_dirfd.as_raw_fd(), + from_pathname, + to_dirfd.as_raw_fd(), + to_pathname, + flags, + ), } }; diff --git a/test/integration-test/src/tests/load.rs b/test/integration-test/src/tests/load.rs index 8ff13757..bce623e5 100644 --- a/test/integration-test/src/tests/load.rs +++ b/test/integration-test/src/tests/load.rs @@ -7,6 +7,7 @@ use std::{ }; use aya::{ + features, maps::Array, programs::{ links::{FdLink, PinnedLink}, @@ -385,6 +386,7 @@ fn pin_link() { #[test] fn pin_lifecycle() { + let features = features(None); let kernel_version = KernelVersion::current().unwrap(); if kernel_version < KernelVersion::new(5, 18, 0) { eprintln!("skipping test on kernel {kernel_version:?}, support for BPF_F_XDP_HAS_FRAGS was added in 5.18.0; see https://github.com/torvalds/linux/commit/c2f2cdb"); @@ -404,7 +406,13 @@ fn pin_lifecycle() { // 2. Load program from bpffs but don't attach it { - let _ = Xdp::from_pin("/sys/fs/bpf/aya-xdp-test-prog", XdpAttachType::Interface).unwrap(); + let _ = Xdp::from_pin( + "/sys/fs/bpf/aya-xdp-test-prog", + XdpAttachType::Interface, + None, + features.clone(), + ) + .unwrap(); } // should still be loaded since prog was pinned @@ -412,8 +420,13 @@ fn pin_lifecycle() { // 3. Load program from bpffs and attach { - let mut prog = - Xdp::from_pin("/sys/fs/bpf/aya-xdp-test-prog", XdpAttachType::Interface).unwrap(); + let mut prog = Xdp::from_pin( + "/sys/fs/bpf/aya-xdp-test-prog", + XdpAttachType::Interface, + None, + features.clone(), + ) + .unwrap(); let link_id = prog.attach("lo", XdpFlags::default()).unwrap(); let link = prog.take_link(link_id).unwrap(); let fd_link: FdLink = link.try_into().unwrap(); @@ -446,6 +459,7 @@ fn pin_lifecycle() { #[test] fn pin_lifecycle_tracepoint() { + let features = features(None); // 1. Load Program and Pin { let mut bpf = Ebpf::load(crate::TEST).unwrap(); @@ -463,7 +477,12 @@ fn pin_lifecycle_tracepoint() { // 2. Load program from bpffs but don't attach it { - let _ = TracePoint::from_pin("/sys/fs/bpf/aya-tracepoint-test-prog").unwrap(); + let _ = TracePoint::from_pin( + "/sys/fs/bpf/aya-tracepoint-test-prog", + None, + features.clone(), + ) + .unwrap(); } // should still be loaded since prog was pinned @@ -471,7 +490,8 @@ fn pin_lifecycle_tracepoint() { // 3. Load program from bpffs and attach { - let mut prog = TracePoint::from_pin("/sys/fs/bpf/aya-tracepoint-test-prog").unwrap(); + let mut prog = + TracePoint::from_pin("/sys/fs/bpf/aya-tracepoint-test-prog", None, features).unwrap(); let link_id = prog.attach("syscalls", "sys_enter_kill").unwrap(); let link = prog.take_link(link_id).unwrap(); let fd_link: FdLink = link.try_into().unwrap(); @@ -500,6 +520,7 @@ fn pin_lifecycle_tracepoint() { #[test] fn pin_lifecycle_kprobe() { + let features = features(None); // 1. Load Program and Pin { let mut bpf = Ebpf::load(crate::TEST).unwrap(); @@ -516,6 +537,8 @@ fn pin_lifecycle_kprobe() { let _ = KProbe::from_pin( "/sys/fs/bpf/aya-kprobe-test-prog", aya::programs::ProbeKind::KProbe, + None, + features.clone(), ) .unwrap(); } @@ -528,6 +551,8 @@ fn pin_lifecycle_kprobe() { let mut prog = KProbe::from_pin( "/sys/fs/bpf/aya-kprobe-test-prog", aya::programs::ProbeKind::KProbe, + None, + features.clone(), ) .unwrap(); let link_id = prog.attach("try_to_wake_up", 0).unwrap(); @@ -564,6 +589,7 @@ extern "C" fn uprobe_function() { #[test] fn pin_lifecycle_uprobe() { + let features = features(None); const FIRST_PIN_PATH: &str = "/sys/fs/bpf/aya-uprobe-test-prog-1"; const SECOND_PIN_PATH: &str = "/sys/fs/bpf/aya-uprobe-test-prog-2"; @@ -580,7 +606,13 @@ fn pin_lifecycle_uprobe() { // 2. Load program from bpffs but don't attach it { - let _ = UProbe::from_pin(FIRST_PIN_PATH, aya::programs::ProbeKind::UProbe).unwrap(); + let _ = UProbe::from_pin( + FIRST_PIN_PATH, + aya::programs::ProbeKind::UProbe, + None, + features.clone(), + ) + .unwrap(); } // should still be loaded since prog was pinned @@ -588,7 +620,13 @@ fn pin_lifecycle_uprobe() { // 3. Load program from bpffs and attach { - let mut prog = UProbe::from_pin(FIRST_PIN_PATH, aya::programs::ProbeKind::UProbe).unwrap(); + let mut prog = UProbe::from_pin( + FIRST_PIN_PATH, + aya::programs::ProbeKind::UProbe, + None, + features.clone(), + ) + .unwrap(); let link_id = prog .attach(Some("uprobe_function"), 0, "/proc/self/exe", None) .unwrap();