bpfjit_sys/
lib.rs

1// src/lib.rs
2
3#![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(); // pcap_compile() in libpcap <1.8 is not thread safe
84
85    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, // PCAP_NETMASK_UNKNOWN
93    );
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 {}