Skip to content

Commit 7e54054

Browse files
committed
feat: Allow conversions to Program from ProgramInfo
Allow for a ProgramInfo to be converted into one of the program types that we support. This allows for a user of Aya access to reattach, pin or unload a program that was either, previously loaded, or was loaded by another process. Signed-off-by: Dave Tucker <[email protected]>
1 parent 65489e1 commit 7e54054

File tree

5 files changed

+329
-15
lines changed

5 files changed

+329
-15
lines changed

aya/src/bpf.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -609,15 +609,15 @@ impl<'a> EbpfLoader<'a> {
609609
}
610610
ProgramSection::CgroupSkb => Program::CgroupSkb(CgroupSkb {
611611
data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
612-
expected_attach_type: None,
612+
attach_type: None,
613613
}),
614614
ProgramSection::CgroupSkbIngress => Program::CgroupSkb(CgroupSkb {
615615
data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
616-
expected_attach_type: Some(CgroupSkbAttachType::Ingress),
616+
attach_type: Some(CgroupSkbAttachType::Ingress),
617617
}),
618618
ProgramSection::CgroupSkbEgress => Program::CgroupSkb(CgroupSkb {
619619
data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
620-
expected_attach_type: Some(CgroupSkbAttachType::Egress),
620+
attach_type: Some(CgroupSkbAttachType::Egress),
621621
}),
622622
ProgramSection::CgroupSockAddr { attach_type, .. } => {
623623
Program::CgroupSockAddr(CgroupSockAddr {

aya/src/programs/cgroup_skb.rs

+7-9
Original file line numberDiff line numberDiff line change
@@ -57,18 +57,16 @@ use crate::{
5757
#[doc(alias = "BPF_PROG_TYPE_CGROUP_SKB")]
5858
pub struct CgroupSkb {
5959
pub(crate) data: ProgramData<CgroupSkbLink>,
60-
pub(crate) expected_attach_type: Option<CgroupSkbAttachType>,
60+
pub(crate) attach_type: Option<CgroupSkbAttachType>,
6161
}
6262

6363
impl CgroupSkb {
6464
/// Loads the program inside the kernel.
6565
pub fn load(&mut self) -> Result<(), ProgramError> {
66-
self.data.expected_attach_type =
67-
self.expected_attach_type
68-
.map(|attach_type| match attach_type {
69-
CgroupSkbAttachType::Ingress => BPF_CGROUP_INET_INGRESS,
70-
CgroupSkbAttachType::Egress => BPF_CGROUP_INET_EGRESS,
71-
});
66+
self.data.expected_attach_type = self.attach_type.map(|attach_type| match attach_type {
67+
CgroupSkbAttachType::Ingress => BPF_CGROUP_INET_INGRESS,
68+
CgroupSkbAttachType::Egress => BPF_CGROUP_INET_EGRESS,
69+
});
7270
load_program(BPF_PROG_TYPE_CGROUP_SKB, &mut self.data)
7371
}
7472

@@ -79,7 +77,7 @@ impl CgroupSkb {
7977
/// method returns `None` for programs defined with the generic section
8078
/// `cgroup/skb`.
8179
pub fn expected_attach_type(&self) -> &Option<CgroupSkbAttachType> {
82-
&self.expected_attach_type
80+
&self.attach_type
8381
}
8482

8583
/// Attaches the program to the given cgroup.
@@ -138,7 +136,7 @@ impl CgroupSkb {
138136
let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?;
139137
Ok(Self {
140138
data,
141-
expected_attach_type: Some(expected_attach_type),
139+
attach_type: Some(expected_attach_type),
142140
})
143141
}
144142
}

aya/src/programs/mod.rs

+207
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ use aya_obj::{
8383
VerifierLog,
8484
btf::BtfError,
8585
generated::{bpf_attach_type, bpf_link_info, bpf_prog_info, bpf_prog_type},
86+
programs::XdpAttachType,
8687
};
8788
use info::impl_info;
8889
pub use info::{ProgramInfo, ProgramType, loaded_programs};
@@ -991,6 +992,212 @@ impl_from_pin!(
991992
Iter,
992993
);
993994

995+
macro_rules! impl_from_prog_info {
996+
($(($struct_name:ident, $prog_type:expr, $unsafe:ident $(, $var:ident, $var_ty:ty)?)),* $(,)?) => {
997+
$(
998+
impl_from_prog_info!($struct_name, $prog_type, $unsafe $(, $var, $var_ty)?);
999+
)*
1000+
};
1001+
($struct_name:ident, $prog_type:expr, false) => {
1002+
impl $struct_name {
1003+
/// Constructs an instance of a Program from a [`ProgramInfo`].
1004+
///
1005+
/// This allows the caller to get a handle to an already loaded
1006+
/// program from the kernel without having to load it again.
1007+
///
1008+
/// # Errors
1009+
///
1010+
/// - If the program type reported by the kernel does not match
1011+
/// that of the type you're converting to.
1012+
/// - If the file descriptor of the program cannot be cloned.
1013+
pub fn from_program_info(
1014+
name: Option<&'static str>,
1015+
info: ProgramInfo,
1016+
) -> Result<Self, ProgramError> {
1017+
if info.program_type()? != $prog_type {
1018+
return Err(ProgramError::UnexpectedProgramType {});
1019+
}
1020+
let ProgramInfo { 0: bpf_progam_info} = info;
1021+
let fd = info.fd()?;
1022+
let fd = fd.as_fd().try_clone_to_owned()?;
1023+
1024+
Ok(Self {
1025+
data: ProgramData::from_bpf_prog_info(
1026+
name.map(|n| Cow::Borrowed(n)),
1027+
crate::MockableFd::from_fd(fd),
1028+
Path::new(""),
1029+
bpf_progam_info,
1030+
VerifierLogLevel::default(),
1031+
)?,
1032+
})
1033+
}
1034+
}
1035+
};
1036+
($struct_name:ident, $prog_type:expr, true) => {
1037+
impl $struct_name {
1038+
/// Constructs an instance of a Program from a [`ProgramInfo`].
1039+
///
1040+
/// This allows the caller to get a handle to an already loaded
1041+
/// program from the kernel without having to load it again.
1042+
///
1043+
/// # Errors
1044+
///
1045+
/// - If the program type reported by the kernel does not match
1046+
/// that of the type you're converting to.
1047+
/// - If the file descriptor of the program cannot be cloned.
1048+
///
1049+
/// # Safety
1050+
///
1051+
/// We can't safely cast to `ProgramInfo` since we don't know the
1052+
/// concrete type of the program. It's up to the caller to ensure
1053+
/// that the program type matches the type you're converting to.
1054+
/// Otherwise, the behavior is undefined.
1055+
pub unsafe fn from_program_info(
1056+
name: Option<&'static str>,
1057+
info: ProgramInfo,
1058+
) -> Result<Self, ProgramError> {
1059+
if info.program_type()? != $prog_type {
1060+
return Err(ProgramError::UnexpectedProgramType {});
1061+
}
1062+
let ProgramInfo { 0: bpf_progam_info} = info;
1063+
let fd = info.fd()?;
1064+
let fd = fd.as_fd().try_clone_to_owned()?;
1065+
1066+
Ok(Self {
1067+
data: ProgramData::from_bpf_prog_info(
1068+
name.map(|n| Cow::Borrowed(n)),
1069+
crate::MockableFd::from_fd(fd),
1070+
Path::new(""),
1071+
bpf_progam_info,
1072+
VerifierLogLevel::default(),
1073+
)?,
1074+
})
1075+
}
1076+
}
1077+
};
1078+
($struct_name:ident, $prog_type:expr, false, $var:ident, $var_ty:ty) => {
1079+
impl $struct_name {
1080+
/// Constructs an instance of a Program from a [`ProgramInfo`].
1081+
///
1082+
/// This allows the caller to get a handle to an already loaded
1083+
/// program from the kernel without having to load it again.
1084+
///
1085+
/// # Errors
1086+
///
1087+
/// - If the program type reported by the kernel does not match
1088+
/// that of the type you're converting to.
1089+
/// - If the file descriptor of the program cannot be cloned.
1090+
pub fn from_program_info(
1091+
name: Option<&'static str>,
1092+
info: ProgramInfo,
1093+
$var: $var_ty,
1094+
) -> Result<Self, ProgramError> {
1095+
if info.program_type()? != $prog_type {
1096+
return Err(ProgramError::UnexpectedProgramType {});
1097+
}
1098+
let ProgramInfo { 0: bpf_progam_info} = info;
1099+
let fd = info.fd()?;
1100+
let fd = fd.as_fd().try_clone_to_owned()?;
1101+
1102+
Ok(Self {
1103+
data: ProgramData::from_bpf_prog_info(
1104+
name.map(|n| Cow::Borrowed(n)),
1105+
crate::MockableFd::from_fd(fd),
1106+
Path::new(""),
1107+
bpf_progam_info,
1108+
VerifierLogLevel::default(),
1109+
)?,
1110+
$var,
1111+
})
1112+
}
1113+
}
1114+
};
1115+
($struct_name:ident, $prog_type:expr, true, $var:ident, $var_ty:ty) => {
1116+
impl $struct_name {
1117+
/// Constructs an instance of a Program from a [`ProgramInfo`].
1118+
///
1119+
/// This allows the caller to get a handle to an already loaded
1120+
/// program from the kernel without having to load it again.
1121+
///
1122+
/// # Errors
1123+
///
1124+
/// - If the program type reported by the kernel does not match
1125+
/// that of the type you're converting to.
1126+
/// - If the file descriptor of the program cannot be cloned.
1127+
///
1128+
/// # Safety
1129+
///
1130+
/// We can't safely cast to `ProgramInfo` since we don't know the
1131+
/// concrete type of the program. It's up to the caller to ensure
1132+
/// that the program type matches the type you're converting to.
1133+
/// Otherwise, the behavior is undefined.
1134+
pub unsafe fn from_program_info(
1135+
name: Option<&'static str>,
1136+
info: ProgramInfo,
1137+
$var: $var_ty,
1138+
) -> Result<Self, ProgramError> {
1139+
if info.program_type()? != $prog_type {
1140+
return Err(ProgramError::UnexpectedProgramType {});
1141+
}
1142+
let ProgramInfo { 0: bpf_progam_info} = info;
1143+
let fd = info.fd()?;
1144+
let fd = fd.as_fd().try_clone_to_owned()?;
1145+
1146+
Ok(Self {
1147+
data: ProgramData::from_bpf_prog_info(
1148+
name.map(|n| Cow::Borrowed(n)),
1149+
crate::MockableFd::from_fd(fd),
1150+
Path::new(""),
1151+
bpf_progam_info,
1152+
VerifierLogLevel::default(),
1153+
)?,
1154+
$var,
1155+
})
1156+
}
1157+
}
1158+
};
1159+
}
1160+
1161+
// Order of arguments is as follows:
1162+
// - Program, bpf_prog_type, unsafe
1163+
// - Program, bpf_prog_type, unsafe, additional variable, variable type
1164+
impl_from_prog_info!(
1165+
(KProbe, ProgramType::KProbe, true, kind, ProbeKind),
1166+
(UProbe, ProgramType::KProbe, true, kind, ProbeKind),
1167+
(TracePoint, ProgramType::TracePoint, false),
1168+
(SocketFilter, ProgramType::SocketFilter, false),
1169+
(Xdp, ProgramType::Xdp, false, attach_type, XdpAttachType),
1170+
(SkMsg, ProgramType::SkMsg, false),
1171+
(SkSkb, ProgramType::SkSkb, false, kind, SkSkbKind),
1172+
(SockOps, ProgramType::SockOps, false),
1173+
(SchedClassifier, ProgramType::SchedClassifier, false),
1174+
(
1175+
CgroupSkb,
1176+
ProgramType::CgroupSkb,
1177+
false,
1178+
attach_type,
1179+
Option<CgroupSkbAttachType>
1180+
),
1181+
(CgroupSysctl, ProgramType::CgroupSysctl, false),
1182+
(
1183+
CgroupSockopt,
1184+
ProgramType::CgroupSockopt,
1185+
false,
1186+
attach_type,
1187+
CgroupSockoptAttachType
1188+
),
1189+
(LircMode2, ProgramType::LircMode2, false),
1190+
(PerfEvent, ProgramType::PerfEvent, false),
1191+
(Lsm, ProgramType::Lsm, false),
1192+
(RawTracePoint, ProgramType::RawTracePoint, false),
1193+
(BtfTracePoint, ProgramType::Tracing, true),
1194+
(FEntry, ProgramType::Tracing, true),
1195+
(FExit, ProgramType::Tracing, true),
1196+
(Extension, ProgramType::Extension, false),
1197+
(SkLookup, ProgramType::SkLookup, false),
1198+
(CgroupDevice, ProgramType::CgroupDevice, false),
1199+
);
1200+
9941201
macro_rules! impl_try_from_program {
9951202
($($ty:ident),+ $(,)?) => {
9961203
$(

test/integration-test/src/tests/info.rs

+24-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::{fs, panic, path::Path, time::SystemTime};
1010
use aya::{
1111
Ebpf,
1212
maps::{Array, HashMap, IterableMap as _, MapError, MapType, loaded_maps},
13-
programs::{ProgramError, ProgramType, SocketFilter, TracePoint, loaded_programs},
13+
programs::{ProgramError, ProgramType, SocketFilter, TracePoint, UProbe, loaded_programs},
1414
sys::enable_stats,
1515
util::KernelVersion,
1616
};
@@ -25,8 +25,8 @@ const BPF_STATS_ENABLED: &str = "/proc/sys/kernel/bpf_stats_enabled";
2525
fn test_loaded_programs() {
2626
// Load a program.
2727
// Since we are only testing the programs for their metadata, there is no need to "attach" them.
28-
let mut bpf = Ebpf::load(crate::SIMPLE_PROG).unwrap();
29-
let prog: &mut SocketFilter = bpf.program_mut("simple_prog").unwrap().try_into().unwrap();
28+
let mut bpf = Ebpf::load(crate::TEST).unwrap();
29+
let prog: &mut UProbe = bpf.program_mut("test_uprobe").unwrap().try_into().unwrap();
3030
prog.load().unwrap();
3131
let test_prog = prog.info().unwrap();
3232

@@ -55,6 +55,27 @@ fn test_loaded_programs() {
5555
programs.any(|prog| prog.id() == test_prog.id()),
5656
KernelVersion::new(4, 13, 0)
5757
);
58+
59+
// Iterate through loaded programs to exercise `from_program_info()`.
60+
for program in loaded_programs() {
61+
let program = program.unwrap();
62+
let mut p: UProbe = unsafe {
63+
UProbe::from_program_info(
64+
Some("simple_prog"),
65+
program,
66+
aya::programs::ProbeKind::UProbe,
67+
)
68+
.unwrap()
69+
};
70+
71+
// Ensure we can perform basic operations on the re-created program.
72+
let res = p
73+
.attach("uprobe_function", "/proc/self/exe", None, None)
74+
.unwrap();
75+
76+
// Ensure the program can be detached
77+
p.detach(res).unwrap();
78+
}
5879
}
5980

6081
#[test]

0 commit comments

Comments
 (0)