1use crate::error::RvfError;
8
9pub const EBPF_MAGIC: u32 = 0x5256_4250;
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15#[repr(u8)]
16pub enum EbpfProgramType {
17 XdpDistance = 0x00,
19 TcFilter = 0x01,
21 SocketFilter = 0x02,
23 Tracepoint = 0x03,
25 Kprobe = 0x04,
27 CgroupSkb = 0x05,
29 Custom = 0xFF,
31}
32
33impl TryFrom<u8> for EbpfProgramType {
34 type Error = RvfError;
35
36 fn try_from(value: u8) -> Result<Self, Self::Error> {
37 match value {
38 0x00 => Ok(Self::XdpDistance),
39 0x01 => Ok(Self::TcFilter),
40 0x02 => Ok(Self::SocketFilter),
41 0x03 => Ok(Self::Tracepoint),
42 0x04 => Ok(Self::Kprobe),
43 0x05 => Ok(Self::CgroupSkb),
44 0xFF => Ok(Self::Custom),
45 _ => Err(RvfError::InvalidEnumValue {
46 type_name: "EbpfProgramType",
47 value: value as u64,
48 }),
49 }
50 }
51}
52
53#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
55#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
56#[repr(u8)]
57pub enum EbpfAttachType {
58 XdpIngress = 0x00,
60 TcIngress = 0x01,
62 TcEgress = 0x02,
64 SocketFilter = 0x03,
66 CgroupIngress = 0x04,
68 CgroupEgress = 0x05,
70 None = 0xFF,
72}
73
74impl TryFrom<u8> for EbpfAttachType {
75 type Error = RvfError;
76
77 fn try_from(value: u8) -> Result<Self, Self::Error> {
78 match value {
79 0x00 => Ok(Self::XdpIngress),
80 0x01 => Ok(Self::TcIngress),
81 0x02 => Ok(Self::TcEgress),
82 0x03 => Ok(Self::SocketFilter),
83 0x04 => Ok(Self::CgroupIngress),
84 0x05 => Ok(Self::CgroupEgress),
85 0xFF => Ok(Self::None),
86 _ => Err(RvfError::InvalidEnumValue {
87 type_name: "EbpfAttachType",
88 value: value as u64,
89 }),
90 }
91 }
92}
93
94#[derive(Clone, Copy, Debug)]
99#[repr(C)]
100pub struct EbpfHeader {
101 pub ebpf_magic: u32,
103 pub header_version: u16,
105 pub program_type: u8,
107 pub attach_type: u8,
109 pub program_flags: u32,
111 pub insn_count: u16,
113 pub max_dimension: u16,
115 pub program_size: u64,
117 pub map_count: u32,
119 pub btf_size: u32,
121 pub program_hash: [u8; 32],
123}
124
125const _: () = assert!(core::mem::size_of::<EbpfHeader>() == 64);
127
128impl EbpfHeader {
129 pub fn to_bytes(&self) -> [u8; 64] {
131 let mut buf = [0u8; 64];
132 buf[0x00..0x04].copy_from_slice(&self.ebpf_magic.to_le_bytes());
133 buf[0x04..0x06].copy_from_slice(&self.header_version.to_le_bytes());
134 buf[0x06] = self.program_type;
135 buf[0x07] = self.attach_type;
136 buf[0x08..0x0C].copy_from_slice(&self.program_flags.to_le_bytes());
137 buf[0x0C..0x0E].copy_from_slice(&self.insn_count.to_le_bytes());
138 buf[0x0E..0x10].copy_from_slice(&self.max_dimension.to_le_bytes());
139 buf[0x10..0x18].copy_from_slice(&self.program_size.to_le_bytes());
140 buf[0x18..0x1C].copy_from_slice(&self.map_count.to_le_bytes());
141 buf[0x1C..0x20].copy_from_slice(&self.btf_size.to_le_bytes());
142 buf[0x20..0x40].copy_from_slice(&self.program_hash);
143 buf
144 }
145
146 pub fn from_bytes(data: &[u8; 64]) -> Result<Self, RvfError> {
148 let magic = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
149 if magic != EBPF_MAGIC {
150 return Err(RvfError::BadMagic {
151 expected: EBPF_MAGIC,
152 got: magic,
153 });
154 }
155
156 Ok(Self {
157 ebpf_magic: magic,
158 header_version: u16::from_le_bytes([data[0x04], data[0x05]]),
159 program_type: data[0x06],
160 attach_type: data[0x07],
161 program_flags: u32::from_le_bytes([data[0x08], data[0x09], data[0x0A], data[0x0B]]),
162 insn_count: u16::from_le_bytes([data[0x0C], data[0x0D]]),
163 max_dimension: u16::from_le_bytes([data[0x0E], data[0x0F]]),
164 program_size: u64::from_le_bytes([
165 data[0x10], data[0x11], data[0x12], data[0x13],
166 data[0x14], data[0x15], data[0x16], data[0x17],
167 ]),
168 map_count: u32::from_le_bytes([data[0x18], data[0x19], data[0x1A], data[0x1B]]),
169 btf_size: u32::from_le_bytes([data[0x1C], data[0x1D], data[0x1E], data[0x1F]]),
170 program_hash: {
171 let mut h = [0u8; 32];
172 h.copy_from_slice(&data[0x20..0x40]);
173 h
174 },
175 })
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182
183 fn sample_header() -> EbpfHeader {
184 EbpfHeader {
185 ebpf_magic: EBPF_MAGIC,
186 header_version: 1,
187 program_type: EbpfProgramType::XdpDistance as u8,
188 attach_type: EbpfAttachType::XdpIngress as u8,
189 program_flags: 0,
190 insn_count: 256,
191 max_dimension: 1536,
192 program_size: 4096,
193 map_count: 2,
194 btf_size: 512,
195 program_hash: [0xDE; 32],
196 }
197 }
198
199 #[test]
200 fn header_size_is_64() {
201 assert_eq!(core::mem::size_of::<EbpfHeader>(), 64);
202 }
203
204 #[test]
205 fn magic_bytes_match_ascii() {
206 let bytes_be = EBPF_MAGIC.to_be_bytes();
207 assert_eq!(&bytes_be, b"RVBP");
208 }
209
210 #[test]
211 fn round_trip_serialization() {
212 let original = sample_header();
213 let bytes = original.to_bytes();
214 let decoded = EbpfHeader::from_bytes(&bytes).expect("from_bytes should succeed");
215
216 assert_eq!(decoded.ebpf_magic, EBPF_MAGIC);
217 assert_eq!(decoded.header_version, 1);
218 assert_eq!(decoded.program_type, EbpfProgramType::XdpDistance as u8);
219 assert_eq!(decoded.attach_type, EbpfAttachType::XdpIngress as u8);
220 assert_eq!(decoded.program_flags, 0);
221 assert_eq!(decoded.insn_count, 256);
222 assert_eq!(decoded.max_dimension, 1536);
223 assert_eq!(decoded.program_size, 4096);
224 assert_eq!(decoded.map_count, 2);
225 assert_eq!(decoded.btf_size, 512);
226 assert_eq!(decoded.program_hash, [0xDE; 32]);
227 }
228
229 #[test]
230 fn bad_magic_returns_error() {
231 let mut bytes = sample_header().to_bytes();
232 bytes[0] = 0x00; let err = EbpfHeader::from_bytes(&bytes).unwrap_err();
234 match err {
235 RvfError::BadMagic { expected, .. } => assert_eq!(expected, EBPF_MAGIC),
236 other => panic!("expected BadMagic, got {other:?}"),
237 }
238 }
239
240 #[test]
241 fn field_offsets() {
242 let h = sample_header();
243 let base = &h as *const _ as usize;
244
245 assert_eq!(&h.ebpf_magic as *const _ as usize - base, 0x00);
246 assert_eq!(&h.header_version as *const _ as usize - base, 0x04);
247 assert_eq!(&h.program_type as *const _ as usize - base, 0x06);
248 assert_eq!(&h.attach_type as *const _ as usize - base, 0x07);
249 assert_eq!(&h.program_flags as *const _ as usize - base, 0x08);
250 assert_eq!(&h.insn_count as *const _ as usize - base, 0x0C);
251 assert_eq!(&h.max_dimension as *const _ as usize - base, 0x0E);
252 assert_eq!(&h.program_size as *const _ as usize - base, 0x10);
253 assert_eq!(&h.map_count as *const _ as usize - base, 0x18);
254 assert_eq!(&h.btf_size as *const _ as usize - base, 0x1C);
255 assert_eq!(&h.program_hash as *const _ as usize - base, 0x20);
256 }
257
258 #[test]
259 fn ebpf_program_type_try_from() {
260 assert_eq!(EbpfProgramType::try_from(0x00), Ok(EbpfProgramType::XdpDistance));
261 assert_eq!(EbpfProgramType::try_from(0x01), Ok(EbpfProgramType::TcFilter));
262 assert_eq!(EbpfProgramType::try_from(0x02), Ok(EbpfProgramType::SocketFilter));
263 assert_eq!(EbpfProgramType::try_from(0x03), Ok(EbpfProgramType::Tracepoint));
264 assert_eq!(EbpfProgramType::try_from(0x04), Ok(EbpfProgramType::Kprobe));
265 assert_eq!(EbpfProgramType::try_from(0x05), Ok(EbpfProgramType::CgroupSkb));
266 assert_eq!(EbpfProgramType::try_from(0xFF), Ok(EbpfProgramType::Custom));
267 assert!(EbpfProgramType::try_from(0x06).is_err());
268 assert!(EbpfProgramType::try_from(0x80).is_err());
269 }
270
271 #[test]
272 fn ebpf_attach_type_try_from() {
273 assert_eq!(EbpfAttachType::try_from(0x00), Ok(EbpfAttachType::XdpIngress));
274 assert_eq!(EbpfAttachType::try_from(0x01), Ok(EbpfAttachType::TcIngress));
275 assert_eq!(EbpfAttachType::try_from(0x02), Ok(EbpfAttachType::TcEgress));
276 assert_eq!(EbpfAttachType::try_from(0x03), Ok(EbpfAttachType::SocketFilter));
277 assert_eq!(EbpfAttachType::try_from(0x04), Ok(EbpfAttachType::CgroupIngress));
278 assert_eq!(EbpfAttachType::try_from(0x05), Ok(EbpfAttachType::CgroupEgress));
279 assert_eq!(EbpfAttachType::try_from(0xFF), Ok(EbpfAttachType::None));
280 assert!(EbpfAttachType::try_from(0x06).is_err());
281 assert!(EbpfAttachType::try_from(0x80).is_err());
282 }
283
284 #[test]
285 fn max_dimension_round_trip() {
286 let mut h = sample_header();
287 h.max_dimension = 2048;
288 let bytes = h.to_bytes();
289 let decoded = EbpfHeader::from_bytes(&bytes).unwrap();
290 assert_eq!(decoded.max_dimension, 2048);
291 }
292
293 #[test]
294 fn large_program_size_round_trip() {
295 let mut h = sample_header();
296 h.program_size = 1_048_576; h.insn_count = 65535;
298 let bytes = h.to_bytes();
299 let decoded = EbpfHeader::from_bytes(&bytes).unwrap();
300 assert_eq!(decoded.program_size, 1_048_576);
301 assert_eq!(decoded.insn_count, 65535);
302 }
303}