Signed-off-by: Dave Tucker <dave@dtucker.co.uk>
pull/990/head
Dave Tucker 2 months ago
parent 104f449c0f
commit b9f2b6cf66

@ -13,6 +13,7 @@ edition.workspace = true
[dependencies] [dependencies]
aya = { path = "../aya", version = "^0.12.0", features = ["async_tokio"] } 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 } aya-log-common = { path = "../aya-log-common", version = "^0.1.14", default-features = false }
bytes = { workspace = true } bytes = { workspace = true }
log = { workspace = true } log = { workspace = true }

@ -72,6 +72,7 @@ use aya::{
use aya_log_common::{ use aya_log_common::{
Argument, DisplayHint, Level, LogValueLength, RecordField, LOG_BUF_CAPACITY, LOG_FIELDS, Argument, DisplayHint, Level, LogValueLength, RecordField, LOG_BUF_CAPACITY, LOG_FIELDS,
}; };
use aya_obj::Features;
use bytes::BytesMut; use bytes::BytesMut;
use log::{error, Log, Record}; use log::{error, Log, Record};
use thiserror::Error; 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 /// 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]. /// pinned program. The log records will be written to the default logger. See [log::logger].
pub fn init_from_id(program_id: u32) -> Result<EbpfLogger, Error> { pub fn init_from_id(program_id: u32, features: Features) -> Result<EbpfLogger, Error> {
Self::init_from_id_with_logger(program_id, log::logger()) 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. /// 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<T: Log + 'static>( pub fn init_from_id_with_logger<T: Log + 'static>(
program_id: u32, program_id: u32,
logger: T, logger: T,
features: Features,
) -> Result<EbpfLogger, Error> { ) -> Result<EbpfLogger, Error> {
let program_info = loaded_programs() let program_info = loaded_programs()
.filter_map(|info| info.ok()) .filter_map(|info| info.ok())
@ -149,7 +151,7 @@ impl EbpfLogger {
None => false, None => false,
}) })
.ok_or(Error::MapNotFound)?; .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)?; Self::read_logs_async(Map::PerfEventArray(map), logger)?;

@ -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<BpfAttachType> 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,
}
}
}

@ -160,7 +160,7 @@ pub enum BtfError {
} }
/// Available BTF features /// Available BTF features
#[derive(Default, Debug)] #[derive(Default, Debug, Clone)]
#[allow(missing_docs)] #[allow(missing_docs)]
pub struct BtfFeatures { pub struct BtfFeatures {
btf_func: bool, btf_func: bool,

@ -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<BpfCommand> 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,
}
}
}

@ -85,8 +85,11 @@ mod std {
} }
} }
pub mod attach;
pub mod btf; pub mod btf;
pub mod cmd;
pub mod generated; pub mod generated;
pub mod links;
pub mod maps; pub mod maps;
pub mod obj; pub mod obj;
pub mod programs; pub mod programs;

@ -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<BpfLinkType> 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,
}
}
}

@ -5,7 +5,7 @@ use core::mem;
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use crate::std; use crate::std;
use crate::EbpfSectionKind; use crate::{generated::bpf_map_type, EbpfSectionKind};
/// Invalid map type encontered /// Invalid map type encontered
pub struct InvalidMapTypeError { pub struct InvalidMapTypeError {
@ -13,6 +13,124 @@ pub struct InvalidMapTypeError {
pub map_type: u32, 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<BpfMapType> 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<u32> for crate::generated::bpf_map_type { impl TryFrom<u32> for crate::generated::bpf_map_type {
type Error = InvalidMapTypeError; type Error = InvalidMapTypeError;

@ -37,7 +37,7 @@ use crate::{
const KERNEL_VERSION_ANY: u32 = 0xFFFF_FFFE; const KERNEL_VERSION_ANY: u32 = 0xFFFF_FFFE;
/// Features implements BPF and BTF feature detection /// Features implements BPF and BTF feature detection
#[derive(Default, Debug)] #[derive(Default, Debug, Clone)]
#[allow(missing_docs)] #[allow(missing_docs)]
pub struct Features { pub struct Features {
bpf_name: bool, bpf_name: bool,

@ -1,5 +1,7 @@
//! Program struct and type bindings. //! Program struct and type bindings.
use crate::generated::bpf_prog_type;
pub mod cgroup_sock; pub mod cgroup_sock;
pub mod cgroup_sock_addr; pub mod cgroup_sock_addr;
pub mod cgroup_sockopt; pub mod cgroup_sockopt;
@ -9,3 +11,116 @@ pub use cgroup_sock::CgroupSockAttachType;
pub use cgroup_sock_addr::CgroupSockAddrAttachType; pub use cgroup_sock_addr::CgroupSockAddrAttachType;
pub use cgroup_sockopt::CgroupSockoptAttachType; pub use cgroup_sockopt::CgroupSockoptAttachType;
pub use xdp::XdpAttachType; 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<BpfProgType> 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,
}
}
}

@ -1,10 +1,12 @@
use std::{ use std::{
borrow::Cow, borrow::Cow,
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
ffi::CString,
fs, io, fs, io,
os::{ os::{
fd::{AsFd as _, AsRawFd as _, OwnedFd}, fd::{AsFd as _, AsRawFd as _, BorrowedFd, FromRawFd, OwnedFd},
raw::c_int, raw::c_int,
unix::ffi::OsStrExt as _,
}, },
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::Arc, sync::Arc,
@ -16,6 +18,7 @@ use aya_obj::{
relocation::EbpfRelocationError, relocation::EbpfRelocationError,
EbpfSectionKind, Features, EbpfSectionKind, Features,
}; };
use libc::{O_CLOEXEC, O_DIRECTORY, O_RDWR};
use log::{debug, warn}; use log::{debug, warn};
use thiserror::Error; use thiserror::Error;
@ -36,7 +39,7 @@ use crate::{
SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp,
}, },
sys::{ 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_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_float_supported, is_btf_func_global_supported, is_btf_func_supported,
is_btf_supported, is_btf_type_tag_supported, is_perf_link_supported, is_btf_supported, is_btf_type_tag_supported, is_perf_link_supported,
@ -70,32 +73,28 @@ unsafe impl<T: Pod, const N: usize> Pod for [T; N] {}
pub use aya_obj::maps::{bpf_map_def, PinningType}; pub use aya_obj::maps::{bpf_map_def, PinningType};
lazy_static::lazy_static! { fn detect_features(token_fd: Option<BorrowedFd<'_>>) -> Features {
pub(crate) static ref FEATURES: Features = detect_features(); let btf = if is_btf_supported(token_fd) {
}
fn detect_features() -> Features {
let btf = if is_btf_supported() {
Some(BtfFeatures::new( Some(BtfFeatures::new(
is_btf_func_supported(), is_btf_func_supported(token_fd),
is_btf_func_global_supported(), is_btf_func_global_supported(token_fd),
is_btf_datasec_supported(), is_btf_datasec_supported(token_fd),
is_btf_float_supported(), is_btf_float_supported(token_fd),
is_btf_decl_tag_supported(), is_btf_decl_tag_supported(token_fd),
is_btf_type_tag_supported(), is_btf_type_tag_supported(token_fd),
is_btf_enum64_supported(), is_btf_enum64_supported(token_fd),
)) ))
} else { } else {
None None
}; };
let f = Features::new( let f = Features::new(
is_prog_name_supported(), is_prog_name_supported(token_fd),
is_probe_read_kernel_supported(), is_probe_read_kernel_supported(token_fd),
is_perf_link_supported(), is_perf_link_supported(token_fd),
is_bpf_global_data_supported(), is_bpf_global_data_supported(token_fd),
is_bpf_cookie_supported(), is_bpf_cookie_supported(token_fd),
is_prog_id_supported(BPF_MAP_TYPE_CPUMAP), is_prog_id_supported(BPF_MAP_TYPE_CPUMAP, token_fd),
is_prog_id_supported(BPF_MAP_TYPE_DEVMAP), is_prog_id_supported(BPF_MAP_TYPE_DEVMAP, token_fd),
btf, btf,
); );
debug!("BPF Feature Detection: {:#?}", f); debug!("BPF Feature Detection: {:#?}", f);
@ -103,8 +102,8 @@ fn detect_features() -> Features {
} }
/// Returns a reference to the detected BPF features. /// Returns a reference to the detected BPF features.
pub fn features() -> &'static Features { pub fn features(token_fd: Option<BorrowedFd<'_>>) -> Features {
&FEATURES detect_features(token_fd)
} }
/// Builder style API for advanced loading of eBPF programs. /// Builder style API for advanced loading of eBPF programs.
@ -132,6 +131,7 @@ pub fn features() -> &'static Features {
pub struct EbpfLoader<'a> { pub struct EbpfLoader<'a> {
btf: Option<Cow<'a, Btf>>, btf: Option<Cow<'a, Btf>>,
map_pin_path: Option<PathBuf>, map_pin_path: Option<PathBuf>,
token_path: PathBuf,
globals: HashMap<&'a str, (&'a [u8], bool)>, globals: HashMap<&'a str, (&'a [u8], bool)>,
max_entries: HashMap<&'a str, u32>, max_entries: HashMap<&'a str, u32>,
extensions: HashSet<&'a str>, extensions: HashSet<&'a str>,
@ -170,6 +170,7 @@ impl<'a> EbpfLoader<'a> {
Self { Self {
btf: Btf::from_sys_fs().ok().map(Cow::Owned), btf: Btf::from_sys_fs().ok().map(Cow::Owned),
map_pin_path: None, map_pin_path: None,
token_path: Path::new("/sys/fs/bpf").to_owned(),
globals: HashMap::new(), globals: HashMap::new(),
max_entries: HashMap::new(), max_entries: HashMap::new(),
extensions: HashSet::new(), extensions: HashSet::new(),
@ -355,6 +356,23 @@ impl<'a> EbpfLoader<'a> {
self 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<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
self.token_path = path.as_ref().to_path_buf();
self
}
/// Loads eBPF bytecode from a file. /// Loads eBPF bytecode from a file.
/// ///
/// # Examples /// # Examples
@ -389,18 +407,23 @@ impl<'a> EbpfLoader<'a> {
let Self { let Self {
btf, btf,
map_pin_path, map_pin_path,
token_path,
globals, globals,
max_entries, max_entries,
extensions, extensions,
verifier_log_level, verifier_log_level,
allow_unsupported_maps, allow_unsupported_maps,
} = self; } = 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)?; let mut obj = Object::parse(data)?;
obj.patch_map_data(globals.clone())?; 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)? { 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)), Ok(btf_fd) => Some(Arc::new(btf_fd)),
// Only report an error here if the BTF is truly needed, otherwise proceed without. // Only report an error here if the BTF is truly needed, otherwise proceed without.
Err(err) => { Err(err) => {
@ -461,7 +484,7 @@ impl<'a> EbpfLoader<'a> {
let mut maps = HashMap::new(); let mut maps = HashMap::new();
for (name, mut obj) in obj.maps.drain() { for (name, mut obj) in obj.maps.drain() {
if let (false, EbpfSectionKind::Bss | EbpfSectionKind::Data | EbpfSectionKind::Rodata) = if let (false, EbpfSectionKind::Bss | EbpfSectionKind::Data | EbpfSectionKind::Rodata) =
(FEATURES.bpf_global_data(), obj.section_kind()) (features.bpf_global_data(), obj.section_kind())
{ {
continue; continue;
} }
@ -485,16 +508,18 @@ impl<'a> EbpfLoader<'a> {
} }
match obj.map_type().try_into() { match obj.map_type().try_into() {
Ok(BPF_MAP_TYPE_CPUMAP) => { 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) => { 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 btf_fd = btf_fd.as_deref().map(|fd| fd.as_fd());
let mut map = match obj.pinning() { 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 => { PinningType::ByName => {
// pin maps in /sys/fs/bpf by default to align with libbpf // 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. // behavior https://github.com/libbpf/libbpf/blob/v1.2.2/src/libbpf.c#L2161.
@ -502,7 +527,14 @@ impl<'a> EbpfLoader<'a> {
.as_deref() .as_deref()
.unwrap_or_else(|| Path::new("/sys/fs/bpf")); .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()?; map.finalize()?;
@ -521,7 +553,7 @@ impl<'a> EbpfLoader<'a> {
&text_sections, &text_sections,
)?; )?;
obj.relocate_calls(&text_sections)?; obj.relocate_calls(&text_sections)?;
obj.sanitize_functions(&FEATURES); obj.sanitize_functions(&features);
let programs = obj let programs = obj
.programs .programs
@ -529,7 +561,7 @@ impl<'a> EbpfLoader<'a> {
.map(|(name, prog_obj)| { .map(|(name, prog_obj)| {
let function_obj = obj.functions.get(&prog_obj.function_key()).unwrap().clone(); 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()) Some(name.clone())
} else { } else {
None None
@ -538,23 +570,51 @@ impl<'a> EbpfLoader<'a> {
let obj = (prog_obj, function_obj); let obj = (prog_obj, function_obj);
let btf_fd = btf_fd.clone(); let btf_fd = btf_fd.clone();
let token_fd = token_fd.clone();
let program = if extensions.contains(name.as_str()) { let program = if extensions.contains(name.as_str()) {
Program::Extension(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(),
),
}) })
} else { } else {
match &section { match &section {
ProgramSection::KProbe => Program::KProbe(KProbe { 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, kind: ProbeKind::KProbe,
}), }),
ProgramSection::KRetProbe => Program::KProbe(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, kind: ProbeKind::KRetProbe,
}), }),
ProgramSection::UProbe { sleepable } => { ProgramSection::UProbe { sleepable } => {
let mut data = let mut data = ProgramData::new(
ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level); prog_name,
obj,
btf_fd,
*verifier_log_level,
token_fd,
features.clone(),
);
if *sleepable { if *sleepable {
data.flags = BPF_F_SLEEPABLE; data.flags = BPF_F_SLEEPABLE;
} }
@ -564,8 +624,14 @@ impl<'a> EbpfLoader<'a> {
}) })
} }
ProgramSection::URetProbe { sleepable } => { ProgramSection::URetProbe { sleepable } => {
let mut data = let mut data = ProgramData::new(
ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level); prog_name,
obj,
btf_fd,
*verifier_log_level,
token_fd,
features.clone(),
);
if *sleepable { if *sleepable {
data.flags = BPF_F_SLEEPABLE; data.flags = BPF_F_SLEEPABLE;
} }
@ -575,16 +641,36 @@ impl<'a> EbpfLoader<'a> {
}) })
} }
ProgramSection::TracePoint => Program::TracePoint(TracePoint { 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 { 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 { ProgramSection::Xdp {
frags, attach_type, .. frags, attach_type, ..
} => { } => {
let mut data = let mut data = ProgramData::new(
ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level); prog_name,
obj,
btf_fd,
*verifier_log_level,
token_fd,
features.clone(),
);
if *frags { if *frags {
data.flags = BPF_F_XDP_HAS_FRAGS; data.flags = BPF_F_XDP_HAS_FRAGS;
} }
@ -594,101 +680,252 @@ impl<'a> EbpfLoader<'a> {
}) })
} }
ProgramSection::SkMsg => Program::SkMsg(SkMsg { 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 { 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, .. } => { ProgramSection::CgroupSockopt { attach_type, .. } => {
Program::CgroupSockopt(CgroupSockopt { 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, attach_type: *attach_type,
}) })
} }
ProgramSection::SkSkbStreamParser => Program::SkSkb(SkSkb { 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, kind: SkSkbKind::StreamParser,
}), }),
ProgramSection::SkSkbStreamVerdict => Program::SkSkb(SkSkb { 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, kind: SkSkbKind::StreamVerdict,
}), }),
ProgramSection::SockOps => Program::SockOps(SockOps { 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 => { ProgramSection::SchedClassifier => {
Program::SchedClassifier(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 { 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, expected_attach_type: None,
}), }),
ProgramSection::CgroupSkbIngress => Program::CgroupSkb(CgroupSkb { 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), expected_attach_type: Some(CgroupSkbAttachType::Ingress),
}), }),
ProgramSection::CgroupSkbEgress => Program::CgroupSkb(CgroupSkb { 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), expected_attach_type: Some(CgroupSkbAttachType::Egress),
}), }),
ProgramSection::CgroupSockAddr { attach_type, .. } => { ProgramSection::CgroupSockAddr { attach_type, .. } => {
Program::CgroupSockAddr(CgroupSockAddr { 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, attach_type: *attach_type,
}) })
} }
ProgramSection::LircMode2 => Program::LircMode2(LircMode2 { 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 { 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 { 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 } => { ProgramSection::Lsm { sleepable } => {
let mut data = let mut data = ProgramData::new(
ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level); prog_name,
obj,
btf_fd,
*verifier_log_level,
token_fd,
features.clone(),
);
if *sleepable { if *sleepable {
data.flags = BPF_F_SLEEPABLE; data.flags = BPF_F_SLEEPABLE;
} }
Program::Lsm(Lsm { data }) Program::Lsm(Lsm { data })
} }
ProgramSection::BtfTracePoint => Program::BtfTracePoint(BtfTracePoint { 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 } => { ProgramSection::FEntry { sleepable } => {
let mut data = let mut data = ProgramData::new(
ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level); prog_name,
obj,
btf_fd,
*verifier_log_level,
token_fd,
features.clone(),
);
if *sleepable { if *sleepable {
data.flags = BPF_F_SLEEPABLE; data.flags = BPF_F_SLEEPABLE;
} }
Program::FEntry(FEntry { data }) Program::FEntry(FEntry { data })
} }
ProgramSection::FExit { sleepable } => { ProgramSection::FExit { sleepable } => {
let mut data = let mut data = ProgramData::new(
ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level); prog_name,
obj,
btf_fd,
*verifier_log_level,
token_fd,
features.clone(),
);
if *sleepable { if *sleepable {
data.flags = BPF_F_SLEEPABLE; data.flags = BPF_F_SLEEPABLE;
} }
Program::FExit(FExit { data }) Program::FExit(FExit { data })
} }
ProgramSection::Extension => Program::Extension(Extension { 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 { 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, .. } => { ProgramSection::CgroupSock { attach_type, .. } => {
Program::CgroupSock(CgroupSock { 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, attach_type: *attach_type,
}) })
} }
ProgramSection::CgroupDevice => Program::CgroupDevice(CgroupDevice { 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`]. /// The error type returned by [`Ebpf::load_file`] and [`Ebpf::load`].
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum EbpfError { 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 loading file
#[error("error loading {path}")] #[error("error loading {path}")]
FileError { FileError {
@ -1123,9 +1371,13 @@ pub enum EbpfError {
#[deprecated(since = "0.13.0", note = "use `EbpfError` instead")] #[deprecated(since = "0.13.0", note = "use `EbpfError` instead")]
pub type BpfError = EbpfError; pub type BpfError = EbpfError;
fn load_btf(raw_btf: Vec<u8>, verifier_log_level: VerifierLogLevel) -> Result<OwnedFd, BtfError> { fn load_btf(
raw_btf: Vec<u8>,
verifier_log_level: VerifierLogLevel,
token_fd: Option<BorrowedFd<'_>>,
) -> Result<OwnedFd, BtfError> {
let (ret, verifier_log) = retry_with_verifier_logs(10, |logger| { 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 { ret.map_err(|(_, io_error)| BtfError::LoadError {
io_error, 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<P: AsRef<Path>>(path: P) -> Result<OwnedFd, EbpfError> {
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,
})
}

@ -94,6 +94,7 @@ pub use obj::btf::{Btf, BtfError};
pub use object::Endianness; pub use object::Endianness;
#[doc(hidden)] #[doc(hidden)]
pub use sys::netlink_set_link_up; 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 // 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. // process when we try to close a fake file descriptor.

@ -57,8 +57,10 @@ use std::{
os::fd::{AsFd, BorrowedFd, OwnedFd}, os::fd::{AsFd, BorrowedFd, OwnedFd},
path::Path, path::Path,
ptr, ptr,
sync::Arc,
}; };
use aya_obj::Features;
use libc::{getrlimit, rlim_t, rlimit, RLIMIT_MEMLOCK, RLIM_INFINITY}; use libc::{getrlimit, rlim_t, rlimit, RLIMIT_MEMLOCK, RLIM_INFINITY};
use log::warn; use log::warn;
use obj::maps::InvalidMapTypeError; use obj::maps::InvalidMapTypeError;
@ -546,6 +548,7 @@ pub(crate) fn check_v_size<V>(map: &MapData) -> Result<(), MapError> {
pub struct MapData { pub struct MapData {
obj: obj::Map, obj: obj::Map,
fd: MapFd, fd: MapFd,
features: Features,
} }
impl MapData { impl MapData {
@ -554,6 +557,8 @@ impl MapData {
obj: obj::Map, obj: obj::Map,
name: &str, name: &str,
btf_fd: Option<BorrowedFd<'_>>, btf_fd: Option<BorrowedFd<'_>>,
token_fd: Option<Arc<OwnedFd>>,
features: Features,
) -> Result<Self, MapError> { ) -> Result<Self, MapError> {
let c_name = CString::new(name).map_err(|_| MapError::InvalidName { name: name.into() })?; let c_name = CString::new(name).map_err(|_| MapError::InvalidName { name: name.into() })?;
@ -561,8 +566,14 @@ impl MapData {
let kernel_version = KernelVersion::current().unwrap(); let kernel_version = KernelVersion::current().unwrap();
#[cfg(test)] #[cfg(test)]
let kernel_version = KernelVersion::new(0xff, 0xff, 0xff); let kernel_version = KernelVersion::new(0xff, 0xff, 0xff);
let fd = let fd = bpf_create_map(
bpf_create_map(&c_name, &obj, btf_fd, kernel_version).map_err(|(code, io_error)| { &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) { if kernel_version < KernelVersion::new(5, 11, 0) {
maybe_warn_rlimit(); maybe_warn_rlimit();
} }
@ -576,6 +587,7 @@ impl MapData {
Ok(Self { Ok(Self {
obj, obj,
fd: MapFd::from_fd(fd), fd: MapFd::from_fd(fd),
features,
}) })
} }
@ -584,6 +596,8 @@ impl MapData {
obj: obj::Map, obj: obj::Map,
name: &str, name: &str,
btf_fd: Option<BorrowedFd<'_>>, btf_fd: Option<BorrowedFd<'_>>,
token_fd: Option<Arc<OwnedFd>>,
features: Features,
) -> Result<Self, MapError> { ) -> Result<Self, MapError> {
use std::os::unix::ffi::OsStrExt as _; use std::os::unix::ffi::OsStrExt as _;
@ -605,9 +619,10 @@ impl MapData {
Ok(fd) => Ok(Self { Ok(fd) => Ok(Self {
obj, obj,
fd: MapFd::from_fd(fd), fd: MapFd::from_fd(fd),
features,
}), }),
Err(_) => { 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 { map.pin(&path).map_err(|error| MapError::PinError {
name: Some(name.into()), name: Some(name.into()),
error, error,
@ -618,7 +633,11 @@ impl MapData {
} }
pub(crate) fn finalize(&mut self) -> Result<(), MapError> { 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 { 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) bpf_map_update_elem_ptr(fd.as_fd(), &0 as *const _, obj.data_mut().as_mut_ptr(), 0)
.map_err(|(_, io_error)| SyscallError { .map_err(|(_, io_error)| SyscallError {
@ -639,7 +658,7 @@ impl MapData {
} }
/// Loads a map from a pinned path in bpffs. /// Loads a map from a pinned path in bpffs.
pub fn from_pin<P: AsRef<Path>>(path: P) -> Result<Self, MapError> { pub fn from_pin<P: AsRef<Path>>(path: P, features: Features) -> Result<Self, MapError> {
use std::os::unix::ffi::OsStrExt as _; use std::os::unix::ffi::OsStrExt as _;
let path = path.as_ref(); let path = path.as_ref();
@ -657,13 +676,13 @@ impl MapData {
io_error, io_error,
})?; })?;
Self::from_fd(fd) Self::from_fd(fd, features)
} }
/// Loads a map from a map id. /// Loads a map from a map id.
pub fn from_id(id: u32) -> Result<Self, MapError> { pub fn from_id(id: u32, features: Features) -> Result<Self, MapError> {
let fd = bpf_map_get_fd_by_id(id)?; 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. /// 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). /// 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. /// 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. /// For example, you received an FD over Unix Domain Socket.
pub fn from_fd(fd: OwnedFd) -> Result<Self, MapError> { pub fn from_fd(fd: OwnedFd, features: Features) -> Result<Self, MapError> {
let MapInfo(info) = MapInfo::new_from_fd(fd.as_fd())?; let MapInfo(info) = MapInfo::new_from_fd(fd.as_fd())?;
Ok(Self { Ok(Self {
obj: parse_map_info(info, PinningType::None), obj: parse_map_info(info, PinningType::None),
fd: MapFd::from_fd(fd), fd: MapFd::from_fd(fd),
features,
}) })
} }
@ -706,7 +726,11 @@ impl MapData {
pub fn pin<P: AsRef<Path>>(&self, path: P) -> Result<(), PinError> { pub fn pin<P: AsRef<Path>>(&self, path: P) -> Result<(), PinError> {
use std::os::unix::ffi::OsStrExt as _; 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 = path.as_ref();
let path_string = CString::new(path.as_os_str().as_bytes()).map_err(|error| { let path_string = CString::new(path.as_os_str().as_bytes()).map_err(|error| {
PinError::InvalidPinPath { PinError::InvalidPinPath {
@ -723,12 +747,20 @@ impl MapData {
/// Returns the file descriptor of the map. /// Returns the file descriptor of the map.
pub fn fd(&self) -> &MapFd { pub fn fd(&self) -> &MapFd {
let Self { obj: _, fd } = self; let Self {
obj: _,
fd,
features: _,
} = self;
fd fd
} }
pub(crate) fn obj(&self) -> &obj::Map { pub(crate) fn obj(&self) -> &obj::Map {
let Self { obj, fd: _ } = self; let Self {
obj,
fd: _,
features: _,
} = self;
obj obj
} }
@ -1039,6 +1071,8 @@ pub fn loaded_maps() -> impl Iterator<Item = Result<MapInfo, MapError>> {
#[cfg(test)] #[cfg(test)]
mod test_utils { mod test_utils {
use aya_obj::Features;
use crate::{ use crate::{
bpf_map_def, bpf_map_def,
generated::{bpf_cmd, bpf_map_type}, generated::{bpf_cmd, bpf_map_type},
@ -1055,7 +1089,7 @@ mod test_utils {
} => Ok(crate::MockableFd::mock_signed_fd().into()), } => Ok(crate::MockableFd::mock_signed_fd().into()),
call => panic!("unexpected syscall {:?}", call), 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<K>(map_type: bpf_map_type) -> obj::Map { pub(super) fn new_obj_map<K>(map_type: bpf_map_type) -> obj::Map {
@ -1119,10 +1153,11 @@ mod tests {
}); });
assert_matches!( assert_matches!(
MapData::from_id(1234), MapData::from_id(1234, Features::default()),
Ok(MapData { Ok(MapData {
obj: _, obj: _,
fd, fd,
features: _,
}) => assert_eq!(fd.as_fd().as_raw_fd(), crate::MockableFd::mock_signed_fd()) }) => assert_eq!(fd.as_fd().as_raw_fd(), crate::MockableFd::mock_signed_fd())
); );
} }
@ -1138,10 +1173,11 @@ mod tests {
}); });
assert_matches!( assert_matches!(
MapData::create(new_obj_map(), "foo", None), MapData::create(new_obj_map(), "foo", None, None, Features::default()),
Ok(MapData { Ok(MapData {
obj: _, obj: _,
fd, fd,
features: _,
}) => assert_eq!(fd.as_fd().as_raw_fd(), crate::MockableFd::mock_signed_fd()) }) => 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))), _ => 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()); 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)))); override_syscall(|_| Err((-42, io::Error::from_raw_os_error(EFAULT))));
assert_matches!( 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 }) => { Err(MapError::CreateError { name, code, io_error }) => {
assert_eq!(name, "foo"); assert_eq!(name, "foo");
assert_eq!(code, -42); assert_eq!(code, -42);

@ -13,7 +13,7 @@ use crate::{
maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError}, maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError},
programs::ProgramFd, programs::ProgramFd,
sys::{bpf_map_lookup_elem, bpf_map_update_elem, SyscallError}, sys::{bpf_map_lookup_elem, bpf_map_update_elem, SyscallError},
Pod, FEATURES, Pod,
}; };
/// An array of available CPUs. /// An array of available CPUs.
@ -56,8 +56,7 @@ pub struct CpuMap<T> {
impl<T: Borrow<MapData>> CpuMap<T> { impl<T: Borrow<MapData>> CpuMap<T> {
pub(crate) fn new(map: T) -> Result<Self, MapError> { pub(crate) fn new(map: T) -> Result<Self, MapError> {
let data = map.borrow(); let data = map.borrow();
if data.features.cpumap_prog_id() {
if FEATURES.cpumap_prog_id() {
check_kv_size::<u32, bpf_cpumap_val>(data)?; check_kv_size::<u32, bpf_cpumap_val>(data)?;
} else { } else {
check_kv_size::<u32, u32>(data)?; check_kv_size::<u32, u32>(data)?;
@ -83,8 +82,7 @@ impl<T: Borrow<MapData>> CpuMap<T> {
let data = self.inner.borrow(); let data = self.inner.borrow();
check_bounds(data, cpu_index)?; check_bounds(data, cpu_index)?;
let fd = data.fd().as_fd(); let fd = data.fd().as_fd();
let value = if data.features.cpumap_prog_id() {
let value = if FEATURES.cpumap_prog_id() {
bpf_map_lookup_elem::<_, bpf_cpumap_val>(fd, &cpu_index, flags).map(|value| { bpf_map_lookup_elem::<_, bpf_cpumap_val>(fd, &cpu_index, flags).map(|value| {
value.map(|value| CpuMapValue { value.map(|value| CpuMapValue {
queue_size: value.qsize, queue_size: value.qsize,
@ -146,7 +144,7 @@ impl<T: BorrowMut<MapData>> CpuMap<T> {
check_bounds(data, cpu_index)?; check_bounds(data, cpu_index)?;
let fd = data.fd().as_fd(); 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::<bpf_cpumap_val>() }; let mut value = unsafe { std::mem::zeroed::<bpf_cpumap_val>() };
value.qsize = queue_size; value.qsize = queue_size;
// Default is valid as the kernel will only consider fd > 0: // Default is valid as the kernel will only consider fd > 0:

@ -13,7 +13,7 @@ use crate::{
maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError}, maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError},
programs::ProgramFd, programs::ProgramFd,
sys::{bpf_map_lookup_elem, bpf_map_update_elem, SyscallError}, sys::{bpf_map_lookup_elem, bpf_map_update_elem, SyscallError},
Pod, FEATURES, Pod,
}; };
/// An array of network devices. /// An array of network devices.
@ -48,8 +48,7 @@ pub struct DevMap<T> {
impl<T: Borrow<MapData>> DevMap<T> { impl<T: Borrow<MapData>> DevMap<T> {
pub(crate) fn new(map: T) -> Result<Self, MapError> { pub(crate) fn new(map: T) -> Result<Self, MapError> {
let data = map.borrow(); let data = map.borrow();
if data.features.devmap_prog_id() {
if FEATURES.devmap_prog_id() {
check_kv_size::<u32, bpf_devmap_val>(data)?; check_kv_size::<u32, bpf_devmap_val>(data)?;
} else { } else {
check_kv_size::<u32, u32>(data)?; check_kv_size::<u32, u32>(data)?;
@ -75,8 +74,7 @@ impl<T: Borrow<MapData>> DevMap<T> {
let data = self.inner.borrow(); let data = self.inner.borrow();
check_bounds(data, index)?; check_bounds(data, index)?;
let fd = data.fd().as_fd(); let fd = data.fd().as_fd();
let value = if data.features.devmap_prog_id() {
let value = if FEATURES.devmap_prog_id() {
bpf_map_lookup_elem::<_, bpf_devmap_val>(fd, &index, flags).map(|value| { bpf_map_lookup_elem::<_, bpf_devmap_val>(fd, &index, flags).map(|value| {
value.map(|value| DevMapValue { value.map(|value| DevMapValue {
if_index: value.ifindex, if_index: value.ifindex,
@ -136,8 +134,7 @@ impl<T: BorrowMut<MapData>> DevMap<T> {
let data = self.inner.borrow_mut(); let data = self.inner.borrow_mut();
check_bounds(data, index)?; check_bounds(data, index)?;
let fd = data.fd().as_fd(); let fd = data.fd().as_fd();
let res = if data.features.devmap_prog_id() {
let res = if FEATURES.devmap_prog_id() {
let mut value = unsafe { std::mem::zeroed::<bpf_devmap_val>() }; let mut value = unsafe { std::mem::zeroed::<bpf_devmap_val>() };
value.ifindex = target_if_index; value.ifindex = target_if_index;
// Default is valid as the kernel will only consider fd > 0: // Default is valid as the kernel will only consider fd > 0:

@ -13,7 +13,6 @@ use crate::{
maps::{check_kv_size, hash_map, IterableMap, MapData, MapError, MapIter, MapKeys}, maps::{check_kv_size, hash_map, IterableMap, MapData, MapError, MapIter, MapKeys},
programs::ProgramFd, programs::ProgramFd,
sys::{bpf_map_lookup_elem, SyscallError}, sys::{bpf_map_lookup_elem, SyscallError},
FEATURES,
}; };
/// An hashmap of network devices. /// An hashmap of network devices.
@ -49,7 +48,7 @@ impl<T: Borrow<MapData>> DevMapHash<T> {
pub(crate) fn new(map: T) -> Result<Self, MapError> { pub(crate) fn new(map: T) -> Result<Self, MapError> {
let data = map.borrow(); let data = map.borrow();
if FEATURES.devmap_prog_id() { if data.features.devmap_prog_id() {
check_kv_size::<u32, bpf_devmap_val>(data)?; check_kv_size::<u32, bpf_devmap_val>(data)?;
} else { } else {
check_kv_size::<u32, u32>(data)?; check_kv_size::<u32, u32>(data)?;
@ -65,8 +64,9 @@ impl<T: Borrow<MapData>> DevMapHash<T> {
/// Returns [`MapError::SyscallError`] if `bpf_map_lookup_elem` fails. /// Returns [`MapError::SyscallError`] if `bpf_map_lookup_elem` fails.
pub fn get(&self, key: u32, flags: u64) -> Result<DevMapValue, MapError> { pub fn get(&self, key: u32, flags: u64) -> Result<DevMapValue, MapError> {
let fd = self.inner.borrow().fd().as_fd(); 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| { bpf_map_lookup_elem::<_, bpf_devmap_val>(fd, &key, flags).map(|value| {
value.map(|value| DevMapValue { value.map(|value| DevMapValue {
if_index: value.ifindex, if_index: value.ifindex,
@ -127,7 +127,8 @@ impl<T: BorrowMut<MapData>> DevMapHash<T> {
program: Option<&ProgramFd>, program: Option<&ProgramFd>,
flags: u64, flags: u64,
) -> Result<(), XdpMapError> { ) -> 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::<bpf_devmap_val>() }; let mut value = unsafe { std::mem::zeroed::<bpf_devmap_val>() };
value.ifindex = target_if_index; value.ifindex = target_if_index;
// Default is valid as the kernel will only consider fd > 0: // Default is valid as the kernel will only consider fd > 0:

@ -1,6 +1,13 @@
//! Cgroup skb programs. //! 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::{ use crate::{
generated::{ generated::{
@ -140,8 +147,11 @@ impl CgroupSkb {
pub fn from_pin<P: AsRef<Path>>( pub fn from_pin<P: AsRef<Path>>(
path: P, path: P,
expected_attach_type: CgroupSkbAttachType, expected_attach_type: CgroupSkbAttachType,
token_fd: Option<Arc<OwnedFd>>,
features: Features,
) -> Result<Self, ProgramError> { ) -> Result<Self, ProgramError> {
let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; let data =
ProgramData::from_pinned_path(path, VerifierLogLevel::default(), token_fd, features)?;
Ok(Self { Ok(Self {
data, data,
expected_attach_type: Some(expected_attach_type), expected_attach_type: Some(expected_attach_type),

@ -1,8 +1,14 @@
//! Cgroup socket programs. //! 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; pub use aya_obj::programs::CgroupSockAttachType;
use aya_obj::Features;
use crate::{ use crate::{
generated::bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCK, generated::bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCK,
@ -115,8 +121,11 @@ impl CgroupSock {
pub fn from_pin<P: AsRef<Path>>( pub fn from_pin<P: AsRef<Path>>(
path: P, path: P,
attach_type: CgroupSockAttachType, attach_type: CgroupSockAttachType,
token_fd: Option<Arc<OwnedFd>>,
features: Features,
) -> Result<Self, ProgramError> { ) -> Result<Self, ProgramError> {
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 }) Ok(Self { data, attach_type })
} }
} }

@ -1,8 +1,14 @@
//! Cgroup socket address programs. //! 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; pub use aya_obj::programs::CgroupSockAddrAttachType;
use aya_obj::Features;
use crate::{ use crate::{
generated::bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCK_ADDR, generated::bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
@ -119,8 +125,11 @@ impl CgroupSockAddr {
pub fn from_pin<P: AsRef<Path>>( pub fn from_pin<P: AsRef<Path>>(
path: P, path: P,
attach_type: CgroupSockAddrAttachType, attach_type: CgroupSockAddrAttachType,
token_fd: Option<Arc<OwnedFd>>,
features: Features,
) -> Result<Self, ProgramError> { ) -> Result<Self, ProgramError> {
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 }) Ok(Self { data, attach_type })
} }
} }

@ -1,8 +1,14 @@
//! Cgroup socket option programs. //! 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; pub use aya_obj::programs::CgroupSockoptAttachType;
use aya_obj::Features;
use crate::{ use crate::{
generated::bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCKOPT, generated::bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCKOPT,
@ -118,8 +124,11 @@ impl CgroupSockopt {
pub fn from_pin<P: AsRef<Path>>( pub fn from_pin<P: AsRef<Path>>(
path: P, path: P,
attach_type: CgroupSockoptAttachType, attach_type: CgroupSockoptAttachType,
token_fd: Option<Arc<OwnedFd>>,
features: Features,
) -> Result<Self, ProgramError> { ) -> Result<Self, ProgramError> {
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 }) Ok(Self { data, attach_type })
} }
} }

@ -2,10 +2,12 @@
use std::{ use std::{
ffi::OsStr, ffi::OsStr,
io, io,
os::fd::AsFd as _, os::fd::{AsFd as _, OwnedFd},
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::Arc,
}; };
use aya_obj::Features;
use thiserror::Error; use thiserror::Error;
use crate::{ use crate::{
@ -78,7 +80,15 @@ impl KProbe {
fn_name: T, fn_name: T,
offset: u64, offset: u64,
) -> Result<KProbeLinkId, ProgramError> { ) -> Result<KProbeLinkId, ProgramError> {
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. /// 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 /// 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. /// the program being unloaded from the kernel if it is still pinned.
pub fn from_pin<P: AsRef<Path>>(path: P, kind: ProbeKind) -> Result<Self, ProgramError> { pub fn from_pin<P: AsRef<Path>>(
let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; path: P,
kind: ProbeKind,
token_fd: Option<Arc<OwnedFd>>,
features: Features,
) -> Result<Self, ProgramError> {
let data =
ProgramData::from_pinned_path(path, VerifierLogLevel::default(), token_fd, features)?;
Ok(Self { data, kind }) Ok(Self { data, kind })
} }
} }

@ -78,6 +78,7 @@ use std::{
time::{Duration, SystemTime}, time::{Duration, SystemTime},
}; };
use aya_obj::Features;
use libc::ENOSPC; use libc::ENOSPC;
use thiserror::Error; use thiserror::Error;
@ -464,6 +465,8 @@ pub(crate) struct ProgramData<T: Link> {
pub(crate) attach_btf_id: Option<u32>, pub(crate) attach_btf_id: Option<u32>,
pub(crate) attach_prog_fd: Option<ProgramFd>, pub(crate) attach_prog_fd: Option<ProgramFd>,
pub(crate) btf_fd: Option<Arc<OwnedFd>>, pub(crate) btf_fd: Option<Arc<OwnedFd>>,
pub(crate) token_fd: Option<Arc<OwnedFd>>,
pub(crate) features: Features,
pub(crate) verifier_log_level: VerifierLogLevel, pub(crate) verifier_log_level: VerifierLogLevel,
pub(crate) path: Option<PathBuf>, pub(crate) path: Option<PathBuf>,
pub(crate) flags: u32, pub(crate) flags: u32,
@ -475,6 +478,8 @@ impl<T: Link> ProgramData<T> {
obj: (obj::Program, obj::Function), obj: (obj::Program, obj::Function),
btf_fd: Option<Arc<OwnedFd>>, btf_fd: Option<Arc<OwnedFd>>,
verifier_log_level: VerifierLogLevel, verifier_log_level: VerifierLogLevel,
token_fd: Option<Arc<OwnedFd>>,
features: Features,
) -> Self { ) -> Self {
Self { Self {
name, name,
@ -489,6 +494,8 @@ impl<T: Link> ProgramData<T> {
verifier_log_level, verifier_log_level,
path: None, path: None,
flags: 0, flags: 0,
token_fd,
features,
} }
} }
@ -498,6 +505,8 @@ impl<T: Link> ProgramData<T> {
path: &Path, path: &Path,
info: bpf_prog_info, info: bpf_prog_info,
verifier_log_level: VerifierLogLevel, verifier_log_level: VerifierLogLevel,
token_fd: Option<Arc<OwnedFd>>,
features: Features,
) -> Result<Self, ProgramError> { ) -> Result<Self, ProgramError> {
let attach_btf_id = if info.attach_btf_id > 0 { let attach_btf_id = if info.attach_btf_id > 0 {
Some(info.attach_btf_id) Some(info.attach_btf_id)
@ -521,12 +530,16 @@ impl<T: Link> ProgramData<T> {
verifier_log_level, verifier_log_level,
path: Some(path.to_path_buf()), path: Some(path.to_path_buf()),
flags: 0, flags: 0,
token_fd,
features,
}) })
} }
pub(crate) fn from_pinned_path<P: AsRef<Path>>( pub(crate) fn from_pinned_path<P: AsRef<Path>>(
path: P, path: P,
verifier_log_level: VerifierLogLevel, verifier_log_level: VerifierLogLevel,
token_fd: Option<Arc<OwnedFd>>,
features: Features,
) -> Result<Self, ProgramError> { ) -> Result<Self, ProgramError> {
use std::os::unix::ffi::OsStrExt as _; use std::os::unix::ffi::OsStrExt as _;
@ -539,7 +552,15 @@ impl<T: Link> ProgramData<T> {
let info = ProgramInfo::new_from_fd(fd.as_fd())?; let info = ProgramInfo::new_from_fd(fd.as_fd())?;
let name = info.name_as_str().map(|s| s.to_string()); 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<T: Link>(
verifier_log_level, verifier_log_level,
path: _, path: _,
flags, flags,
token_fd,
features: _,
} = data; } = data;
if fd.is_some() { if fd.is_some() {
return Err(ProgramError::AlreadyLoaded); return Err(ProgramError::AlreadyLoaded);
@ -660,7 +683,12 @@ fn load_program<T: Link>(
}; };
let (ret, verifier_log) = retry_with_verifier_logs(10, |logger| { 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 { 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 /// 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. /// the program being unloaded from the kernel if it is still pinned.
pub fn from_pin<P: AsRef<Path>>(path: P) -> Result<Self, ProgramError> { pub fn from_pin<P: AsRef<Path>>(path: P, token_fd: Option<Arc<OwnedFd>>, features: Features) -> Result<Self, ProgramError> {
let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default(), token_fd, features)?;
Ok(Self { data }) Ok(Self { data })
} }
} }

@ -1,6 +1,8 @@
//! Perf attach links. //! Perf attach links.
use std::os::fd::{AsFd as _, AsRawFd as _, BorrowedFd, OwnedFd, RawFd}; use std::os::fd::{AsFd as _, AsRawFd as _, BorrowedFd, OwnedFd, RawFd};
use aya_obj::Features;
use crate::{ use crate::{
generated::bpf_attach_type::BPF_PERF_EVENT, generated::bpf_attach_type::BPF_PERF_EVENT,
programs::{ programs::{
@ -8,7 +10,7 @@ use crate::{
FdLink, Link, ProgramError, FdLink, Link, ProgramError,
}, },
sys::{bpf_link_create, perf_event_ioctl, LinkTarget, SysResult, SyscallError}, 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)] #[derive(Debug, Hash, Eq, PartialEq)]
@ -73,8 +75,9 @@ impl Link for PerfLink {
pub(crate) fn perf_attach( pub(crate) fn perf_attach(
prog_fd: BorrowedFd<'_>, prog_fd: BorrowedFd<'_>,
fd: OwnedFd, fd: OwnedFd,
features: &Features,
) -> Result<PerfLinkInner, ProgramError> { ) -> Result<PerfLinkInner, ProgramError> {
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) let link_fd = bpf_link_create(prog_fd, LinkTarget::Fd(fd.as_fd()), BPF_PERF_EVENT, None, 0)
.map_err(|(_, io_error)| SyscallError { .map_err(|(_, io_error)| SyscallError {
call: "bpf_link_create", call: "bpf_link_create",

@ -180,7 +180,7 @@ impl PerfEvent {
io_error, 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)) self.data.links.insert(PerfEventLink::new(link))
} }

@ -9,6 +9,7 @@ use std::{
sync::atomic::{AtomicUsize, Ordering}, sync::atomic::{AtomicUsize, Ordering},
}; };
use aya_obj::Features;
use libc::pid_t; use libc::pid_t;
use crate::{ use crate::{
@ -112,6 +113,7 @@ pub(crate) fn attach<T: Link + From<PerfLinkInner>>(
fn_name: &OsStr, fn_name: &OsStr,
offset: u64, offset: u64,
pid: Option<pid_t>, pid: Option<pid_t>,
features: &Features,
) -> Result<T::Id, ProgramError> { ) -> Result<T::Id, ProgramError> {
// https://github.com/torvalds/linux/commit/e12f03d7031a977356e3d7b75a68c2185ff8d155 // https://github.com/torvalds/linux/commit/e12f03d7031a977356e3d7b75a68c2185ff8d155
// Use debugfs to create probe // Use debugfs to create probe
@ -122,7 +124,7 @@ pub(crate) fn attach<T: Link + From<PerfLinkInner>>(
perf_attach_debugfs(prog_fd, fd, ProbeEvent { kind, event_alias }) perf_attach_debugfs(prog_fd, fd, ProbeEvent { kind, event_alias })
} else { } else {
let fd = create_as_probe(kind, fn_name, offset, pid)?; 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)) program_data.links.insert(T::from(link))
} }

@ -1,6 +1,12 @@
//! Skskb programs. //! 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::{ use crate::{
generated::{ generated::{
@ -116,8 +122,14 @@ impl SkSkb {
/// ///
/// On drop, any managed links are detached and the program is unloaded. This will not result in /// 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. /// the program being unloaded from the kernel if it is still pinned.
pub fn from_pin<P: AsRef<Path>>(path: P, kind: SkSkbKind) -> Result<Self, ProgramError> { pub fn from_pin<P: AsRef<Path>>(
let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; path: P,
kind: SkSkbKind,
token_fd: Option<Arc<OwnedFd>>,
features: Features,
) -> Result<Self, ProgramError> {
let data =
ProgramData::from_pinned_path(path, VerifierLogLevel::default(), token_fd, features)?;
Ok(Self { data, kind }) Ok(Self { data, kind })
} }
} }

@ -2,10 +2,12 @@
use std::{ use std::{
ffi::{CStr, CString}, ffi::{CStr, CString},
io, io,
os::fd::AsFd as _, os::fd::{AsFd as _, OwnedFd},
path::Path, path::Path,
sync::Arc,
}; };
use aya_obj::Features;
use thiserror::Error; use thiserror::Error;
use crate::{ 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 /// 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. /// the program being unloaded from the kernel if it is still pinned.
pub fn from_pin<P: AsRef<Path>>(path: P) -> Result<Self, ProgramError> { pub fn from_pin<P: AsRef<Path>>(
let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; path: P,
token_fd: Option<Arc<OwnedFd>>,
features: Features,
) -> Result<Self, ProgramError> {
let data =
ProgramData::from_pinned_path(path, VerifierLogLevel::default(), token_fd, features)?;
Ok(Self { data }) Ok(Self { data })
} }
} }

@ -89,7 +89,7 @@ impl TracePoint {
io_error, 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)) self.data.links.insert(TracePointLink::new(link))
} }

@ -6,11 +6,16 @@ use std::{
fs, fs,
io::{self, BufRead, Cursor, Read}, io::{self, BufRead, Cursor, Read},
mem, 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}, path::{Path, PathBuf},
sync::Arc, sync::Arc,
}; };
use aya_obj::Features;
use libc::pid_t; use libc::pid_t;
use object::{Object, ObjectSection, ObjectSymbol, Symbol}; use object::{Object, ObjectSection, ObjectSymbol, Symbol};
use thiserror::Error; use thiserror::Error;
@ -96,7 +101,15 @@ impl UProbe {
}; };
let path = path.as_os_str(); 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. /// 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 /// 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. /// the program being unloaded from the kernel if it is still pinned.
pub fn from_pin<P: AsRef<Path>>(path: P, kind: ProbeKind) -> Result<Self, ProgramError> { pub fn from_pin<P: AsRef<Path>>(
let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; path: P,
kind: ProbeKind,
token_fd: Option<Arc<OwnedFd>>,
features: Features,
) -> Result<Self, ProgramError> {
let data =
ProgramData::from_pinned_path(path, VerifierLogLevel::default(), token_fd, features)?;
Ok(Self { data, kind }) Ok(Self { data, kind })
} }
} }

@ -4,10 +4,12 @@ use std::{
ffi::CString, ffi::CString,
hash::Hash, hash::Hash,
io, io,
os::fd::{AsFd as _, AsRawFd as _, BorrowedFd, RawFd}, os::fd::{AsFd as _, AsRawFd as _, BorrowedFd, OwnedFd, RawFd},
path::Path, path::Path,
sync::Arc,
}; };
use aya_obj::Features;
use libc::if_nametoindex; use libc::if_nametoindex;
use thiserror::Error; use thiserror::Error;
@ -183,8 +185,11 @@ impl Xdp {
pub fn from_pin<P: AsRef<Path>>( pub fn from_pin<P: AsRef<Path>>(
path: P, path: P,
attach_type: XdpAttachType, attach_type: XdpAttachType,
token_fd: Option<Arc<OwnedFd>>,
features: Features,
) -> Result<Self, ProgramError> { ) -> Result<Self, ProgramError> {
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()); data.expected_attach_type = Some(attach_type.into());
Ok(Self { data, attach_type }) Ok(Self { data, attach_type })
} }

@ -3,12 +3,21 @@ use std::{
ffi::{c_char, CStr, CString}, ffi::{c_char, CStr, CString},
io, iter, io, iter,
mem::{self, MaybeUninit}, 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, slice,
}; };
use assert_matches::assert_matches; 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::{ use obj::{
btf::{BtfEnum64, Enum64}, btf::{BtfEnum64, Enum64},
maps::{bpf_map_def, LegacyMap}, maps::{bpf_map_def, LegacyMap},
@ -39,6 +48,7 @@ pub(crate) fn bpf_create_map(
def: &obj::Map, def: &obj::Map,
btf_fd: Option<BorrowedFd<'_>>, btf_fd: Option<BorrowedFd<'_>>,
kernel_version: KernelVersion, kernel_version: KernelVersion,
token_fd: Option<BorrowedFd<'_>>,
) -> SysResult<OwnedFd> { ) -> SysResult<OwnedFd> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() }; let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
@ -48,6 +58,10 @@ pub(crate) fn bpf_create_map(
u.value_size = def.value_size(); u.value_size = def.value_size();
u.max_entries = def.max_entries(); u.max_entries = def.max_entries();
u.map_flags = def.map_flags(); 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 { if let obj::Map::Btf(m) = def {
use bpf_map_type::*; use bpf_map_type::*;
@ -134,6 +148,7 @@ pub(crate) fn bpf_load_program(
aya_attr: &EbpfLoadProgramAttrs<'_>, aya_attr: &EbpfLoadProgramAttrs<'_>,
log_buf: &mut [u8], log_buf: &mut [u8],
verifier_log_level: VerifierLogLevel, verifier_log_level: VerifierLogLevel,
token_fd: Option<BorrowedFd<'_>>,
) -> SysResult<OwnedFd> { ) -> SysResult<OwnedFd> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() }; let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
@ -158,6 +173,10 @@ pub(crate) fn bpf_load_program(
u.insn_cnt = aya_attr.insns.len() as u32; u.insn_cnt = aya_attr.insns.len() as u32;
u.license = aya_attr.license.as_ptr() as u64; u.license = aya_attr.license.as_ptr() as u64;
u.kern_version = aya_attr.kernel_version; 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 // these must be allocated here to ensure the slice outlives the pointer
// so .as_ptr below won't point to garbage // so .as_ptr below won't point to garbage
@ -617,6 +636,7 @@ pub(crate) fn bpf_load_btf(
raw_btf: &[u8], raw_btf: &[u8],
log_buf: &mut [u8], log_buf: &mut [u8],
verifier_log_level: VerifierLogLevel, verifier_log_level: VerifierLogLevel,
token_fd: Option<BorrowedFd<'_>>,
) -> SysResult<OwnedFd> { ) -> SysResult<OwnedFd> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() }; let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_7 }; 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_buf = log_buf.as_mut_ptr() as u64;
u.btf_log_size = log_buf.len() as u32; 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. // SAFETY: `BPF_BTF_LOAD` returns a newly created fd.
unsafe { fd_sys_bpf(bpf_cmd::BPF_BTF_LOAD, &mut attr) } unsafe { fd_sys_bpf(bpf_cmd::BPF_BTF_LOAD, &mut attr) }
} }
pub(crate) fn bpf_token_create(bpf_fs_fd: BorrowedFd<'_>, flags: u32) -> SysResult<OwnedFd> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
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. // 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<OwnedFd> { unsafe fn fd_sys_bpf(cmd: bpf_cmd, attr: &mut bpf_attr) -> SysResult<OwnedFd> {
let fd = sys_bpf(cmd, attr)?; let fd = sys_bpf(cmd, attr)?;
@ -660,7 +691,7 @@ pub(crate) fn bpf_btf_get_fd_by_id(id: u32) -> Result<OwnedFd, SyscallError> {
}) })
} }
pub(crate) fn is_prog_name_supported() -> bool { pub(crate) fn is_prog_name_supported(token_fd: Option<BorrowedFd<'_>>) -> bool {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() }; let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_3 }; let u = unsafe { &mut attr.__bindgen_anon_3 };
let mut name: [c_char; 16] = [0; 16]; 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.insn_cnt = insns.len() as u32;
u.insns = insns.as_ptr() as u64; u.insns = insns.as_ptr() as u64;
u.prog_type = bpf_prog_type::BPF_PROG_TYPE_SOCKET_FILTER as u32; 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() 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<BorrowedFd<'_>>) -> bool {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() }; let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_3 }; 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.insn_cnt = insns.len() as u32;
u.insns = insns.as_ptr() as u64; u.insns = insns.as_ptr() as u64;
u.prog_type = bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT as u32; 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() 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<BorrowedFd<'_>>) -> bool {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() }; let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_3 }; 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.insn_cnt = insns.len() as u32;
u.insns = insns.as_ptr() as u64; u.insns = insns.as_ptr() as u64;
u.prog_type = bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT as u32; 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) { if let Ok(fd) = bpf_prog_load(&mut attr) {
let fd = crate::MockableFd::from_fd(fd); let fd = crate::MockableFd::from_fd(fd);
let fd = fd.as_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<BorrowedFd<'_>>) -> bool {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() }; let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_3 }; let u = unsafe { &mut attr.__bindgen_anon_3 };
@ -773,6 +813,8 @@ pub(crate) fn is_bpf_global_data_supported() -> bool {
}), }),
"aya_global", "aya_global",
None, None,
None,
Features::default(),
); );
if let Ok(map) = map { 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.insn_cnt = insns.len() as u32;
u.insns = insns.as_ptr() as u64; u.insns = insns.as_ptr() as u64;
u.prog_type = bpf_prog_type::BPF_PROG_TYPE_SOCKET_FILTER as u32; 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() bpf_prog_load(&mut attr).is_ok()
} else { } else {
false false
} }
} }
pub(crate) fn is_bpf_cookie_supported() -> bool { pub(crate) fn is_bpf_cookie_supported(token_fd: Option<BorrowedFd<'_>>) -> bool {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() }; let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_3 }; 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.insn_cnt = insns.len() as u32;
u.insns = insns.as_ptr() as u64; u.insns = insns.as_ptr() as u64;
u.prog_type = bpf_prog_type::BPF_PROG_TYPE_KPROBE as u32; 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() bpf_prog_load(&mut attr).is_ok()
} }
/// Tests whether CpuMap, DevMap and DevMapHash support program ids /// 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<BorrowedFd<'_>>,
) -> bool {
assert_matches!( assert_matches!(
map_type, map_type,
bpf_map_type::BPF_MAP_TYPE_CPUMAP 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.max_entries = 1;
u.map_flags = 0; 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. // SAFETY: BPF_MAP_CREATE returns a new file descriptor.
let fd = unsafe { fd_sys_bpf(bpf_cmd::BPF_MAP_CREATE, &mut attr) }; let fd = unsafe { fd_sys_bpf(bpf_cmd::BPF_MAP_CREATE, &mut attr) };
let fd = fd.map(crate::MockableFd::from_fd); let fd = fd.map(crate::MockableFd::from_fd);
fd.is_ok() fd.is_ok()
} }
pub(crate) fn is_btf_supported() -> bool { pub(crate) fn is_btf_supported(token_fd: Option<BorrowedFd<'_>>) -> bool {
let mut btf = Btf::new(); let mut btf = Btf::new();
let name_offset = btf.add_string("int"); let name_offset = btf.add_string("int");
let int_type = BtfType::Int(Int::new(name_offset, 4, IntEncoding::Signed, 0)); let int_type = BtfType::Int(Int::new(name_offset, 4, IntEncoding::Signed, 0));
btf.add_type(int_type); btf.add_type(int_type);
let btf_bytes = btf.to_bytes(); 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<BorrowedFd<'_>>) -> bool {
let mut btf = Btf::new(); let mut btf = Btf::new();
let name_offset = btf.add_string("int"); let name_offset = btf.add_string("int");
let int_type = BtfType::Int(Int::new(name_offset, 4, IntEncoding::Signed, 0)); 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(); 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<BorrowedFd<'_>>) -> bool {
let mut btf = Btf::new(); let mut btf = Btf::new();
let name_offset = btf.add_string("int"); let name_offset = btf.add_string("int");
let int_type = BtfType::Int(Int::new(name_offset, 4, IntEncoding::Signed, 0)); 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(); 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<BorrowedFd<'_>>) -> bool {
let mut btf = Btf::new(); let mut btf = Btf::new();
let name_offset = btf.add_string("int"); let name_offset = btf.add_string("int");
let int_type = BtfType::Int(Int::new(name_offset, 4, IntEncoding::Signed, 0)); 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(); 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<BorrowedFd<'_>>) -> bool {
let mut btf = Btf::new(); let mut btf = Btf::new();
let name_offset = btf.add_string("enum64"); 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(); 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<BorrowedFd<'_>>) -> bool {
let mut btf = Btf::new(); let mut btf = Btf::new();
let name_offset = btf.add_string("float"); let name_offset = btf.add_string("float");
let float_type = BtfType::Float(Float::new(name_offset, 16)); 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(); 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<BorrowedFd<'_>>) -> bool {
let mut btf = Btf::new(); let mut btf = Btf::new();
let name_offset = btf.add_string("int"); let name_offset = btf.add_string("int");
let int_type = BtfType::Int(Int::new(name_offset, 4, IntEncoding::Signed, 0)); 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(); 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<BorrowedFd<'_>>) -> bool {
let mut btf = Btf::new(); let mut btf = Btf::new();
let int_type = BtfType::Int(Int::new(0, 4, IntEncoding::Signed, 0)); 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(); 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<OwnedFd> { fn bpf_prog_load(attr: &mut bpf_attr) -> SysResult<OwnedFd> {
@ -999,6 +1055,274 @@ fn sys_bpf(cmd: bpf_cmd, attr: &mut bpf_attr) -> SysResult<i64> {
syscall(Syscall::Ebpf { cmd, attr }) 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<BpfCommand>,
progs: Vec<BpfProgType>,
maps: Vec<BpfMapType>,
attach: Vec<BpfAttachType>,
}
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<P: AsRef<Path>>(
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( fn bpf_obj_get_next_id(
id: u32, id: u32,
cmd: bpf_cmd, cmd: bpf_cmd,
@ -1105,7 +1429,7 @@ mod tests {
} => Err((-1, io::Error::from_raw_os_error(EBADF))), } => Err((-1, io::Error::from_raw_os_error(EBADF))),
_ => Ok(crate::MockableFd::mock_signed_fd().into()), _ => Ok(crate::MockableFd::mock_signed_fd().into()),
}); });
let supported = is_perf_link_supported(); let supported = is_perf_link_supported(None);
assert!(supported); assert!(supported);
override_syscall(|call| match call { override_syscall(|call| match call {
@ -1115,7 +1439,7 @@ mod tests {
} => Err((-1, io::Error::from_raw_os_error(EINVAL))), } => Err((-1, io::Error::from_raw_os_error(EINVAL))),
_ => Ok(crate::MockableFd::mock_signed_fd().into()), _ => Ok(crate::MockableFd::mock_signed_fd().into()),
}); });
let supported = is_perf_link_supported(); let supported = is_perf_link_supported(None);
assert!(!supported); assert!(!supported);
} }
@ -1124,15 +1448,15 @@ mod tests {
override_syscall(|_call| Ok(crate::MockableFd::mock_signed_fd().into())); override_syscall(|_call| Ok(crate::MockableFd::mock_signed_fd().into()));
// Ensure that the three map types we can check are accepted // 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); 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); 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); assert!(supported);
override_syscall(|_call| Err((-1, io::Error::from_raw_os_error(EINVAL)))); 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); 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 | #[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`"] bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH`"]
fn test_prog_id_supported_reject_types() { 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();
} }
} }

@ -6,15 +6,19 @@ mod perf_event;
mod fake; mod fake;
use std::{ use std::{
ffi::{c_int, c_void}, ffi::{c_int, c_uint, c_void},
io, mem, io, mem,
os::fd::{AsRawFd as _, BorrowedFd}, os::fd::{AsRawFd as _, BorrowedFd},
}; };
pub(crate) use bpf::*; pub(crate) use bpf::*;
pub use bpf::{create_bpf_filesystem, FilesystemPermissions, FilesystemPermissionsBuilder};
#[cfg(test)] #[cfg(test)]
pub(crate) use fake::*; 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)] #[doc(hidden)]
pub use netlink::netlink_set_link_up; pub use netlink::netlink_set_link_up;
pub(crate) use netlink::*; pub(crate) use netlink::*;
@ -42,6 +46,29 @@ pub(crate) enum Syscall<'a> {
request: c_int, request: c_int,
arg: 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)] #[derive(Debug, Error)]
@ -82,6 +109,49 @@ impl std::fmt::Debug for Syscall<'_> {
.field("request", request) .field("request", request)
.field("arg", arg) .field("arg", arg)
.finish(), .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<i64> {
#[allow(clippy::useless_conversion)] #[allow(clippy::useless_conversion)]
ret.into() 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,
),
} }
}; };

@ -7,6 +7,7 @@ use std::{
}; };
use aya::{ use aya::{
features,
maps::Array, maps::Array,
programs::{ programs::{
links::{FdLink, PinnedLink}, links::{FdLink, PinnedLink},
@ -385,6 +386,7 @@ fn pin_link() {
#[test] #[test]
fn pin_lifecycle() { fn pin_lifecycle() {
let features = features(None);
let kernel_version = KernelVersion::current().unwrap(); let kernel_version = KernelVersion::current().unwrap();
if kernel_version < KernelVersion::new(5, 18, 0) { 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"); 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 // 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 // should still be loaded since prog was pinned
@ -412,8 +420,13 @@ fn pin_lifecycle() {
// 3. Load program from bpffs and attach // 3. Load program from bpffs and attach
{ {
let mut prog = let mut prog = Xdp::from_pin(
Xdp::from_pin("/sys/fs/bpf/aya-xdp-test-prog", XdpAttachType::Interface).unwrap(); "/sys/fs/bpf/aya-xdp-test-prog",
XdpAttachType::Interface,
None,
features.clone(),
)
.unwrap();
let link_id = prog.attach("lo", XdpFlags::default()).unwrap(); let link_id = prog.attach("lo", XdpFlags::default()).unwrap();
let link = prog.take_link(link_id).unwrap(); let link = prog.take_link(link_id).unwrap();
let fd_link: FdLink = link.try_into().unwrap(); let fd_link: FdLink = link.try_into().unwrap();
@ -446,6 +459,7 @@ fn pin_lifecycle() {
#[test] #[test]
fn pin_lifecycle_tracepoint() { fn pin_lifecycle_tracepoint() {
let features = features(None);
// 1. Load Program and Pin // 1. Load Program and Pin
{ {
let mut bpf = Ebpf::load(crate::TEST).unwrap(); 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 // 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 // should still be loaded since prog was pinned
@ -471,7 +490,8 @@ fn pin_lifecycle_tracepoint() {
// 3. Load program from bpffs and attach // 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_id = prog.attach("syscalls", "sys_enter_kill").unwrap();
let link = prog.take_link(link_id).unwrap(); let link = prog.take_link(link_id).unwrap();
let fd_link: FdLink = link.try_into().unwrap(); let fd_link: FdLink = link.try_into().unwrap();
@ -500,6 +520,7 @@ fn pin_lifecycle_tracepoint() {
#[test] #[test]
fn pin_lifecycle_kprobe() { fn pin_lifecycle_kprobe() {
let features = features(None);
// 1. Load Program and Pin // 1. Load Program and Pin
{ {
let mut bpf = Ebpf::load(crate::TEST).unwrap(); let mut bpf = Ebpf::load(crate::TEST).unwrap();
@ -516,6 +537,8 @@ fn pin_lifecycle_kprobe() {
let _ = KProbe::from_pin( let _ = KProbe::from_pin(
"/sys/fs/bpf/aya-kprobe-test-prog", "/sys/fs/bpf/aya-kprobe-test-prog",
aya::programs::ProbeKind::KProbe, aya::programs::ProbeKind::KProbe,
None,
features.clone(),
) )
.unwrap(); .unwrap();
} }
@ -528,6 +551,8 @@ fn pin_lifecycle_kprobe() {
let mut prog = KProbe::from_pin( let mut prog = KProbe::from_pin(
"/sys/fs/bpf/aya-kprobe-test-prog", "/sys/fs/bpf/aya-kprobe-test-prog",
aya::programs::ProbeKind::KProbe, aya::programs::ProbeKind::KProbe,
None,
features.clone(),
) )
.unwrap(); .unwrap();
let link_id = prog.attach("try_to_wake_up", 0).unwrap(); let link_id = prog.attach("try_to_wake_up", 0).unwrap();
@ -564,6 +589,7 @@ extern "C" fn uprobe_function() {
#[test] #[test]
fn pin_lifecycle_uprobe() { fn pin_lifecycle_uprobe() {
let features = features(None);
const FIRST_PIN_PATH: &str = "/sys/fs/bpf/aya-uprobe-test-prog-1"; 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"; 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 // 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 // should still be loaded since prog was pinned
@ -588,7 +620,13 @@ fn pin_lifecycle_uprobe() {
// 3. Load program from bpffs and attach // 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 let link_id = prog
.attach(Some("uprobe_function"), 0, "/proc/self/exe", None) .attach(Some("uprobe_function"), 0, "/proc/self/exe", None)
.unwrap(); .unwrap();

Loading…
Cancel
Save