1#![allow(non_camel_case_types)]
4
5use std::error;
6use std::ffi;
7use std::mem;
8use std::str::FromStr;
9use std::sync;
10
11use lazy_static::lazy_static;
12use libc::*;
13
14#[repr(C)]
15#[derive(Debug, Copy, Clone)]
16struct bpf_insn_t {
17 pub code: c_ushort,
18 pub jt: c_uchar,
19 pub jf: c_uchar,
20 pub k: c_uint,
21}
22
23#[repr(C)]
24#[derive(Debug)]
25struct bpf_program_t {
26 pub bf_len: c_uint,
27 pub bf_insns: *mut bpf_insn_t,
28}
29
30#[repr(C)]
31#[derive(Debug)]
32struct bpf_args_t {
33 pub pkt: *const c_uchar,
34 pub wirelen: size_t,
35 pub buflen: size_t,
36 pub mem: *mut c_uint,
37 pub arg: *mut ffi::c_void,
38}
39
40#[repr(C)]
41#[derive(Debug)]
42struct bpf_ctx_t {
43 pub copfuncs: *const ffi::c_void,
44 pub nfuncs: size_t,
45 pub extwords: size_t,
46 pub preinited: c_uint,
47}
48
49type bpfjit_func_t = unsafe extern "C" fn(ctx: *const bpf_ctx_t, args: *mut bpf_args_t) -> c_uint;
50
51#[link(name = "pcap")]
52extern "C" {
53 fn pcap_open_dead(linktype: c_int, snaplen: c_int) -> *mut ffi::c_void;
54
55 fn pcap_compile(
56 p: *mut ffi::c_void,
57 fp: *mut bpf_program_t,
58 str: *const c_char,
59 optimize: c_int,
60 netmask: c_uint,
61 ) -> c_int;
62
63 fn pcap_close(p: *mut ffi::c_void);
64
65 fn pcap_freecode(fp: *mut bpf_program_t);
66
67 fn pcap_geterr(p: *mut ffi::c_void) -> *const c_char;
68}
69
70extern "C" {
71 fn bpfjit_generate_code(ctx: *const bpf_ctx_t, insns: *const bpf_insn_t, user: size_t) -> Option<bpfjit_func_t>;
72
73 fn bpfjit_free_code(func: bpfjit_func_t);
74}
75
76lazy_static! {
77 static ref BIGLOCK: sync::Mutex<()> = sync::Mutex::new(());
78}
79
80unsafe fn compile(filter: &str, linktype: c_int, snaplen: c_int) -> Result<Vec<Opcode>, Box<dyn error::Error>> {
81 let mut bpf_program: bpf_program_t = mem::zeroed();
82
83 let lock = BIGLOCK.lock().unwrap(); let pcap = pcap_open_dead(linktype, snaplen);
86
87 let compiled = pcap_compile(
88 pcap,
89 &mut bpf_program,
90 ffi::CString::new(filter)?.as_ptr(),
91 1,
92 0xffff_ffff, );
94
95 if compiled != 0 {
96 return Err(Box::from(format!(
97 "could not compile cBPF expression: {}",
98 ffi::CStr::from_ptr(pcap_geterr(pcap)).to_str().unwrap()
99 )));
100 }
101
102 pcap_close(pcap);
103
104 drop(lock);
105
106 let mut result: Vec<Opcode> = vec![];
107
108 for i in 0isize..(bpf_program.bf_len as isize) {
109 let insn = *bpf_program.bf_insns.offset(i);
110 result.push(insn.into());
111 }
112
113 pcap_freecode(&mut bpf_program);
114
115 Ok(result)
116}
117
118unsafe fn jit(program: &bpf_program_t) -> Result<(bpf_ctx_t, bpfjit_func_t), Box<dyn error::Error>> {
119 let ctx: bpf_ctx_t = mem::zeroed();
120 let cb = bpfjit_generate_code(&ctx, program.bf_insns, program.bf_len as size_t);
121 match cb {
122 Some(cb) => Ok((ctx, cb)),
123 None => Err(Box::from("could not JIT cBPF bytecode")),
124 }
125}
126
127#[derive(Debug, Copy, Clone)]
128pub struct Opcode(pub u16, pub u8, pub u8, pub u32);
129
130impl From<bpf_insn_t> for Opcode {
131 fn from(insn: bpf_insn_t) -> Self {
132 Opcode(insn.code, insn.jt, insn.jf, insn.k)
133 }
134}
135
136impl FromStr for Opcode {
137 type Err = Box<dyn error::Error>;
138
139 fn from_str(s: &str) -> Result<Self, Self::Err> {
140 let parts: Vec<&str> = s.split_ascii_whitespace().collect();
141 if parts.len() != 4 {
142 return Err("'s' must be 4 numbers separated by whitespace".into());
143 }
144 let code: u16 = parts[0].parse::<u16>()?;
145 let jt: u8 = parts[1].parse::<u8>()?;
146 let jf: u8 = parts[2].parse::<u8>()?;
147 let k: u32 = parts[3].parse::<u32>()?;
148 Ok(Opcode(code, jt, jf, k))
149 }
150}
151
152impl From<Vec<Opcode>> for bpf_program_t {
153 fn from(opcodes: Vec<Opcode>) -> Self {
154 let mut insns = vec![];
155 for opcode in opcodes {
156 insns.push(bpf_insn_t { code: opcode.0, jt: opcode.1, jf: opcode.2, k: opcode.3 });
157 }
158 let bf_len = insns.len() as c_uint;
159 let bf_insns = insns.as_mut_ptr();
160 mem::forget(insns);
161 bpf_program_t { bf_len, bf_insns }
162 }
163}
164
165pub enum Linktype {
166 Other(i32),
167 Ethernet,
168 Ip,
169}
170
171impl From<Linktype> for c_int {
172 fn from(linktype: Linktype) -> Self {
173 match linktype {
174 Linktype::Other(linktype) => linktype as c_int,
175 Linktype::Ethernet => 1,
176 Linktype::Ip => 12,
177 }
178 }
179}
180
181pub struct BpfJit {
182 prog: Vec<Opcode>,
183 ctx: bpf_ctx_t,
184 cb: bpfjit_func_t,
185}
186
187impl BpfJit {
188 pub fn new(filter: &str, linktype: Linktype) -> Result<Self, Box<dyn error::Error>> {
189 unsafe {
190 let prog = compile(filter, linktype.into(), 0xFFFF)?;
191 let (ctx, cb) = jit(&prog.clone().into())?;
192 Ok(BpfJit { prog, ctx, cb })
193 }
194 }
195
196 pub fn raw(opcodes: &[Opcode]) -> Result<Self, Box<dyn error::Error>> {
197 unsafe {
198 let prog = opcodes.to_vec();
199 let (ctx, cb) = jit(&prog.clone().into())?;
200 Ok(BpfJit { prog, ctx, cb })
201 }
202 }
203
204 pub fn value(&self, data: &[u8]) -> u32 {
205 unsafe {
206 let mut bpf_args: bpf_args_t = mem::zeroed();
207 bpf_args.pkt = data.as_ptr();
208 bpf_args.wirelen = data.len();
209 bpf_args.buflen = data.len();
210 (self.cb)(&self.ctx, &mut bpf_args)
211 }
212 }
213
214 pub fn matches(&self, data: &[u8]) -> bool {
215 self.value(data) != 0
216 }
217}
218
219impl Clone for BpfJit {
220 fn clone(&self) -> Self {
221 unsafe {
222 let prog = self.prog.clone();
223 let (ctx, cb) = jit(&prog.clone().into()).unwrap();
224 BpfJit { prog, ctx, cb }
225 }
226 }
227}
228
229impl Drop for BpfJit {
230 fn drop(&mut self) {
231 unsafe {
232 bpfjit_free_code(self.cb);
233 }
234 }
235}
236
237unsafe impl Send for BpfJit {}
238unsafe impl Sync for BpfJit {}