1use fidl_fuchsia_ebpf as febpf;
6use fidl_fuchsia_net_debug as fnet_debug;
7
8use std::ffi::{CString, NulError};
9use std::os::raw::c_int;
10use thiserror::Error;
11
12use crate::bindings::{bpf_program, pcap_close, pcap_compile, pcap_freecode, pcap_open_dead};
13
14#[derive(Error, Debug)]
16pub enum CompilationError {
17 #[error("pcap_open_dead failed")]
19 OpenDeadFailed,
20
21 #[error("pcap_compile failed")]
23 CompileFailed,
24
25 #[error("filter string contained a null byte")]
27 NullByteInFilter,
28}
29
30const PCAP_NETMASK_UNKNOWN: u32 = 0xffffffff;
34
35pub fn compile_filter(filter: &str) -> Result<febpf::VerifiedProgram, CompilationError> {
39 let c_filter =
40 CString::new(filter).map_err(|_: NulError| CompilationError::NullByteInFilter)?;
41
42 let link_type: c_int = u16::from(crate::LinkType::Ethernet).try_into().expect("fits in c_int");
43 let snap_len: c_int = fnet_debug::DEFAULT_SNAP_LEN.try_into().expect("fits in c_int");
44
45 let handle = unsafe { pcap_open_dead(link_type, snap_len) };
48 if handle.is_null() {
49 return Err(CompilationError::OpenDeadFailed);
50 }
51
52 let mut program = bpf_program { bf_len: 0, bf_insns: std::ptr::null_mut() };
53
54 let res = unsafe {
59 pcap_compile(
60 handle,
61 &mut program,
62 c_filter.as_ptr(),
63 1, PCAP_NETMASK_UNKNOWN,
65 )
66 };
67 if res != 0 {
68 unsafe {
70 pcap_close(handle);
71 }
72 return Err(CompilationError::CompileFailed);
73 }
74 let insns = unsafe { std::slice::from_raw_parts(program.bf_insns, program.bf_len as usize) };
79 let cbpf_insns: &[linux_uapi::sock_filter] = zerocopy::transmute_ref!(insns);
81
82 let verified = ebpf::converter::convert_and_verify_cbpf(
83 cbpf_insns,
84 ebpf_api::SOCKET_FILTER_SK_BUF_TYPE.clone(),
85 &ebpf_api::SOCKET_FILTER_CBPF_CONFIG,
86 )
87 .expect("failed to convert cBPF to eBPF");
88 assert_eq!(verified.struct_access_instructions(), &[]);
91 assert_eq!(verified.maps(), &[]);
92
93 unsafe {
97 pcap_freecode(&mut program);
98 pcap_close(handle);
99 }
100
101 Ok(febpf::VerifiedProgram {
102 code: Some(verified.code().iter().map(ebpf::EbpfInstruction::get).collect()),
103 struct_access_instructions: Some(Vec::new()),
104 maps: Some(Vec::new()),
105 ..Default::default()
106 })
107}