Skip to content

Commit dc12a10

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 73a34e1 commit dc12a10

File tree

8 files changed

+368
-15
lines changed

8 files changed

+368
-15
lines changed

aya/src/bpf.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -613,15 +613,15 @@ impl<'a> EbpfLoader<'a> {
613613
}
614614
ProgramSection::CgroupSkb => Program::CgroupSkb(CgroupSkb {
615615
data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
616-
expected_attach_type: None,
616+
attach_type: None,
617617
}),
618618
ProgramSection::CgroupSkbIngress => Program::CgroupSkb(CgroupSkb {
619619
data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
620-
expected_attach_type: Some(CgroupSkbAttachType::Ingress),
620+
attach_type: Some(CgroupSkbAttachType::Ingress),
621621
}),
622622
ProgramSection::CgroupSkbEgress => Program::CgroupSkb(CgroupSkb {
623623
data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
624-
expected_attach_type: Some(CgroupSkbAttachType::Egress),
624+
attach_type: Some(CgroupSkbAttachType::Egress),
625625
}),
626626
ProgramSection::CgroupSockAddr { attach_type, .. } => {
627627
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/kprobe.rs

+59-1
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ use std::{
99
use aya_obj::generated::{bpf_link_type, bpf_prog_type::BPF_PROG_TYPE_KPROBE};
1010
use thiserror::Error;
1111

12+
use super::ProgramType;
1213
use crate::{
1314
VerifierLogLevel,
1415
programs::{
15-
FdLink, LinkError, ProgramData, ProgramError, define_link_wrapper, load_program,
16+
FdLink, LinkError, ProgramData, ProgramError, ProgramInfo, define_link_wrapper,
17+
load_program,
1618
perf_attach::{PerfLinkIdInner, PerfLinkInner},
1719
probe::{ProbeKind, attach},
1820
},
@@ -97,6 +99,62 @@ impl KProbe {
9799
let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?;
98100
Ok(Self { data, kind })
99101
}
102+
103+
/// Constructs an instance of [KProbe] from a [ProgramInfo].
104+
///
105+
/// This allows you to get a handle to an already loaded program
106+
/// from the kernel without having to load it again.
107+
///
108+
/// # Safety
109+
///
110+
/// The type of program in the [ProgramInfo] may not be of
111+
/// [`ProgramType::KProbe`]. This function is unable to
112+
/// check since the kernel doesn't expose this information.
113+
/// The caller is responsible for ensuring that the program
114+
/// type is correct (i.e they previously loaded it) otherwise
115+
/// the behavior is undefined.
116+
///
117+
/// # Errors
118+
///
119+
/// - If the program type is not [`ProgramType::KProbe`]
120+
/// - If the file descriptor of the program cannot be cloned
121+
///
122+
/// # Example
123+
/// ```no_run
124+
/// # use aya::programs::loaded_programs;
125+
/// # use aya::programs::{KProbe, ProbeKind, ProgramInfo, ProgramType};
126+
///
127+
/// for program in loaded_programs() {
128+
/// let program = program?;
129+
/// if program.program_type()? == ProgramType::KProbe {
130+
/// let p = unsafe { KProbe::from_program_info(None, program, ProbeKind::KProbe)? };
131+
/// // do something with the program
132+
/// }
133+
/// }
134+
/// # Ok::<(), aya::programs::ProgramError>(())
135+
/// ```
136+
pub unsafe fn from_program_info(
137+
name: Option<String>,
138+
info: ProgramInfo,
139+
kind: ProbeKind,
140+
) -> Result<Self, ProgramError> {
141+
if info.program_type()? != ProgramType::KProbe {
142+
return Err(ProgramError::UnexpectedProgramType {});
143+
}
144+
if kind != ProbeKind::KProbe && kind != ProbeKind::KRetProbe {
145+
return Err(ProgramError::UnexpectedProgramType {});
146+
}
147+
Ok(Self {
148+
data: ProgramData::from_bpf_prog_info(
149+
name,
150+
crate::MockableFd::from_fd(info.fd()?.as_fd().try_clone_to_owned()?),
151+
Path::new(""),
152+
info.0,
153+
VerifierLogLevel::default(),
154+
)?,
155+
kind,
156+
})
157+
}
100158
}
101159

102160
define_link_wrapper!(

aya/src/programs/mod.rs

+151
Original file line numberDiff line numberDiff line change
@@ -990,6 +990,157 @@ impl_from_pin!(
990990
Iter,
991991
);
992992

993+
macro_rules! impl_from_prog_info {
994+
($($struct_name:ident),+ $(,)?) => {
995+
$(
996+
impl $struct_name {
997+
/// Consructs a program from a [ProgramInfo].
998+
///
999+
/// This allows you to get a handle to an already loaded program
1000+
/// from the kernel without having to load it again.
1001+
///
1002+
/// # Errors
1003+
///
1004+
/// - If the program type is not a match
1005+
/// - If the file descriptor of the program cannot be cloned
1006+
pub fn from_program_info(
1007+
name: Option<String>,
1008+
info: ProgramInfo,
1009+
) -> Result<$struct_name, ProgramError> {
1010+
if info.program_type()? != ProgramType::$struct_name {
1011+
return Err(ProgramError::UnexpectedProgramType {});
1012+
}
1013+
Ok($struct_name {
1014+
data: ProgramData::from_bpf_prog_info(
1015+
name,
1016+
crate::MockableFd::from_fd(info.fd()?.as_fd().try_clone_to_owned()?),
1017+
Path::new(""),
1018+
info.0,
1019+
VerifierLogLevel::default(),
1020+
)?,
1021+
})
1022+
}
1023+
}
1024+
)+
1025+
}
1026+
}
1027+
1028+
macro_rules! impl_from_prog_info_attach_type {
1029+
($($struct_name:ident, $attach_type:ty),+ $(,)?) => {
1030+
$(
1031+
impl $struct_name {
1032+
1033+
/// Consructs a program from a [ProgramInfo].
1034+
///
1035+
/// This allows you to get a handle to an already loaded program
1036+
/// from the kernel without having to load it again.
1037+
///
1038+
/// # Errors
1039+
///
1040+
/// - If the program type is not a match
1041+
/// - If the file descriptor of the program cannot be cloned
1042+
pub fn from_program_info(
1043+
name: Option<String>,
1044+
info: ProgramInfo,
1045+
attach_type: $attach_type,
1046+
) -> Result<$struct_name, ProgramError> {
1047+
if info.program_type()? != ProgramType::$struct_name {
1048+
return Err(ProgramError::UnexpectedProgramType {});
1049+
}
1050+
Ok($struct_name {
1051+
data: ProgramData::from_bpf_prog_info(
1052+
name,
1053+
crate::MockableFd::from_fd(info.fd()?.as_fd().try_clone_to_owned()?),
1054+
Path::new(""),
1055+
info.0,
1056+
VerifierLogLevel::default(),
1057+
)?,
1058+
attach_type,
1059+
})
1060+
}
1061+
}
1062+
)+
1063+
}
1064+
}
1065+
1066+
macro_rules! impl_from_prog_info_unsafe {
1067+
($($struct_name:ident, $prog_type:expr),+ $(,)?) => {
1068+
$(
1069+
impl $struct_name {
1070+
1071+
/// Consructs a program from a [ProgramInfo].
1072+
///
1073+
/// This allows you to get a handle to an already loaded program
1074+
/// from the kernel without having to load it again.
1075+
///
1076+
/// # Errors
1077+
///
1078+
/// - If the program type is not a match
1079+
/// - If the file descriptor of the program cannot be cloned
1080+
///
1081+
/// # Safety
1082+
///
1083+
/// The type of program in the [ProgramInfo] may be
1084+
/// ambiguous due to missing information in the kernel.
1085+
/// The caller is responsible for ensuring that the program
1086+
/// type is correct otherwise the behavior is undefined.
1087+
pub unsafe fn from_program_info(
1088+
name: Option<String>,
1089+
info: ProgramInfo,
1090+
) -> Result<$struct_name, ProgramError> {
1091+
if info.program_type()? != $prog_type {
1092+
return Err(ProgramError::UnexpectedProgramType {});
1093+
}
1094+
Ok($struct_name {
1095+
data: ProgramData::from_bpf_prog_info(
1096+
name,
1097+
crate::MockableFd::from_fd(info.fd()?.as_fd().try_clone_to_owned()?),
1098+
Path::new(""),
1099+
info.0,
1100+
VerifierLogLevel::default(),
1101+
)?,
1102+
})
1103+
}
1104+
}
1105+
)+
1106+
}
1107+
}
1108+
1109+
impl_from_prog_info_unsafe!(
1110+
BtfTracePoint,
1111+
ProgramType::Tracing,
1112+
FEntry,
1113+
ProgramType::Tracing,
1114+
FExit,
1115+
ProgramType::Tracing,
1116+
);
1117+
1118+
impl_from_prog_info!(
1119+
CgroupDevice,
1120+
CgroupSysctl,
1121+
Extension,
1122+
LircMode2,
1123+
Lsm,
1124+
PerfEvent,
1125+
RawTracePoint,
1126+
SkLookup,
1127+
SkMsg,
1128+
SockOps,
1129+
SocketFilter,
1130+
SchedClassifier,
1131+
);
1132+
1133+
impl_from_prog_info_attach_type!(
1134+
CgroupSkb,
1135+
Option<CgroupSkbAttachType>,
1136+
CgroupSockAddr,
1137+
CgroupSockAddrAttachType,
1138+
CgroupSock,
1139+
CgroupSockAttachType,
1140+
CgroupSockopt,
1141+
CgroupSockoptAttachType,
1142+
);
1143+
9931144
macro_rules! impl_try_from_program {
9941145
($($ty:ident),+ $(,)?) => {
9951146
$(

aya/src/programs/probe.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use crate::{
2424
static PROBE_NAME_INDEX: AtomicUsize = AtomicUsize::new(0);
2525

2626
/// Kind of probe program
27-
#[derive(Debug, Copy, Clone)]
27+
#[derive(Debug, Copy, Clone, PartialEq)]
2828
pub enum ProbeKind {
2929
/// Kernel probe
3030
KProbe,

aya/src/programs/uprobe.rs

+58-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ use thiserror::Error;
1818
use crate::{
1919
VerifierLogLevel,
2020
programs::{
21-
FdLink, LinkError, ProgramData, ProgramError, define_link_wrapper, load_program,
21+
FdLink, LinkError, ProgramData, ProgramError, ProgramInfo, ProgramType,
22+
define_link_wrapper, load_program,
2223
perf_attach::{PerfLinkIdInner, PerfLinkInner},
2324
probe::{OsStringExt as _, ProbeKind, attach},
2425
},
@@ -139,6 +140,62 @@ impl UProbe {
139140
let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?;
140141
Ok(Self { data, kind })
141142
}
143+
144+
/// Constructs an instance of [UProbe] from a [ProgramInfo].
145+
///
146+
/// This allows you to get a handle to an already loaded program
147+
/// from the kernel without having to load it again.
148+
///
149+
/// # Safety
150+
///
151+
/// The type of program in the [ProgramInfo] may not be of
152+
/// [`ProgramType::KProbe`]. This function is unable to
153+
/// check since the kernel doesn't expose this information.
154+
/// The caller is responsible for ensuring that the program
155+
/// type is correct (i.e they previously loaded it) otherwise
156+
/// the behavior is undefined.
157+
///
158+
/// # Errors
159+
///
160+
/// - If the program type is not [`ProgramType::KProbe`]
161+
/// - If the file descriptor of the program cannot be cloned
162+
///
163+
/// # Example
164+
/// ```no_run
165+
/// # use aya::programs::loaded_programs;
166+
/// # use aya::programs::{UProbe, ProbeKind, ProgramInfo, ProgramType};
167+
///
168+
/// for program in loaded_programs() {
169+
/// let program = program?;
170+
/// if program.program_type()? == ProgramType::KProbe {
171+
/// let p = unsafe { UProbe::from_program_info(None, program, ProbeKind::UProbe)? };
172+
/// // do something with the program
173+
/// }
174+
/// }
175+
/// # Ok::<(), aya::programs::ProgramError>(())
176+
/// ```
177+
pub unsafe fn from_program_info(
178+
name: Option<String>,
179+
info: ProgramInfo,
180+
kind: ProbeKind,
181+
) -> Result<Self, ProgramError> {
182+
if info.program_type()? != ProgramType::KProbe {
183+
return Err(ProgramError::UnexpectedProgramType {});
184+
}
185+
if kind != ProbeKind::UProbe && kind != ProbeKind::URetProbe {
186+
return Err(ProgramError::UnexpectedProgramType {});
187+
}
188+
Ok(Self {
189+
data: ProgramData::from_bpf_prog_info(
190+
name,
191+
crate::MockableFd::from_fd(info.fd()?.as_fd().try_clone_to_owned()?),
192+
Path::new(""),
193+
info.0,
194+
VerifierLogLevel::default(),
195+
)?,
196+
kind,
197+
})
198+
}
142199
}
143200

144201
fn resolve_attach_path<'a, 'b, 'c, T>(

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

+6
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ fn test_loaded_programs() {
5555
programs.any(|prog| prog.id() == test_prog.id()),
5656
KernelVersion::new(4, 13, 0)
5757
);
58+
59+
for program in programs {
60+
let mut p: SocketFilter =
61+
SocketFilter::from_program_info(Some("simple_prog".to_string()), program).unwrap();
62+
p.unload().unwrap();
63+
}
5864
}
5965

6066
#[test]

0 commit comments

Comments
 (0)