1use std::collections::HashMap;
19
20use libc::sock_filter;
21use crate::bpf::*;
22use crate::errors::{Error, Result};
23
24
25#[derive(Eq, PartialEq, Debug)]
26pub enum Operation<'a> {
27 Label(&'a str),
28 Load(Mode, u32),
29 LoadIdx(Mode, u32),
30 Store(Mode, u32),
31 StoreIdx(Mode, u32),
32 Alu(AluOp, Src, u32),
33 Return(Src, u32),
34 JumpTo(&'a str), Jump(JmpOp, u32, Option<&'a str>, Option<&'a str>),
36}
37use Operation::*;
38
39type Program<'a> = [Operation<'a>];
40
41
42fn map_labels<'a>(prog: &'a Program) -> Result<HashMap<&'a str, usize>> {
43 let mut line = 0;
45 let mut labels: HashMap<&'a str, usize> = HashMap::new();
46 for op in prog {
47 match op {
48 Label(l) => {
49 labels.insert(l, line);
50 },
51 _ => {
52 line += 1;
53 }
54 }
55 }
56
57 Ok(labels)
58}
59
60fn jmp_calc(labels: &HashMap<&str, usize>, label: &Option<&str>, curr: usize) -> Result<usize> {
61
62 let jmp_off = match label {
63 Some(l) => {
64 let off = labels.get(l)
65 .ok_or(Error::UnknownLabelReference(l.to_string()))?;
66 *off - curr - 1
67 },
68 None => 0,
69 };
70 Ok(jmp_off)
71}
72
73fn to_sock_filter(linenum: usize, op: &Operation, labels: &HashMap<&str, usize>) -> Result<sock_filter> {
74 let sf = match op {
75 JumpTo(l) => {
76 let targetline = jmp_calc(labels, &Some(l), linenum)?;
77 bpf_jmp(JmpOp::JA, targetline as u32, 0, 0)
78 },
79 Jump(op, cmp, ltrue, lfalse) => {
80 let lt = jmp_calc(labels, ltrue, linenum)?;
81 let lf = jmp_calc(labels, lfalse, linenum)?;
82 bpf_jmp(*op, *cmp, lt as u8, lf as u8)
83 },
84 Load(mode, val) => bpf_ld(*mode, *val),
85 LoadIdx(mode, val) => bpf_ldx(*mode, *val),
86 Store(mode, val) => bpf_st(*mode, *val),
87 StoreIdx(mode, val) => bpf_stx(*mode, *val),
88 Alu(op, src, val) => bpf_alu(*op, *src, *val),
89 Return(src, val) => bpf_ret(*src, *val),
90 Label(l) => return Err(Error::UnknownLabelReference(l.to_string())),
91 };
92
93 Ok(sf)
94}
95
96
97pub fn compile(prog: &Program) -> Result<Vec<sock_filter>> {
98 let labels = map_labels(prog)?;
99
100 let opcodes = prog.into_iter()
104 .filter(|op| !matches!(op, Label(_)))
105 .enumerate()
106 .map(|(linenum, op)| to_sock_filter(linenum, op, &labels))
107 .collect();
108
109 opcodes
110}
111
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116 use libc;
117 use test_log;
118 use crate::vm::{any_to_data, BpfVM};
119
120 #[test_log::test]
121 fn test_simple_jump() {
122 let asm = vec![
123 JumpTo("FAIL"),
124 Label("OK"), Return(Src::Const, 0),
125 Label("FAIL"), Return(Src::Const, 99),
126 ];
127 let prog = compile(&asm).unwrap();
128
129 let mut vm = BpfVM::new(&prog).unwrap();
130 let data = vec![];
131 let ret = vm.run(&data).unwrap();
132 assert!(ret == 99);
133 }
134
135 #[test_log::test]
136 fn test_ld_gt_ret() {
137 let asm = vec![
138 Load(Mode::IMM, 99),
139 Jump(JmpOp::JGT, 98, Some("ret_acc"), Some("ret999")),
140 Label("ret999"),
142 Load(Mode::IMM, 999),
143 Label("ret_acc"),
144 Return(Src::Acc, 0),
145 ];
146 let prog = compile(&asm).unwrap();
147
148 let mut vm = BpfVM::new(&prog).unwrap();
149 let data = vec![];
150 let ret = vm.run(&data).unwrap();
151 assert!(ret == 99);
152 }
153
154
155 #[ignore]
180 #[test_log::test]
181 fn test_cosmo_socket_filter() {
182 use JmpOp::*;
183 use Mode::*;
184 use Src::*;
185 use crate::seccomp::FieldOffset::*;
186
187 let asm = vec![
188 Load(ABS, Syscall.offset()),
189 Jump(JEQ, libc::SYS_socket as u32, None, Some("REJECT")),
190
191 Load(ABS, ArgLower(0).offset()),
197 Jump(JEQ, libc::AF_INET as u32, Some("Type_Check"), None),
198 Jump(JEQ, libc::AF_INET6 as u32, Some("Type_Check"), Some("REJECT")),
199
200 Label("Type_Check"),
211 Load(ABS, ArgLower(1).offset()),
212 Alu(AluOp::AND, Const, !0x80800),
213 Jump(JEQ, libc::SOCK_STREAM as u32, Some("Proto_Check"), None),
214 Jump(JEQ, libc::SOCK_DGRAM as u32, Some("Proto_Check"), Some("REJECT")),
215
216 Label("Proto_Check"),
224 Load(ABS, ArgLower(1).offset()),
225 Jump(JEQ, 0, Some("ALLOW"), None),
226 Jump(JEQ, libc::IPPROTO_ICMP as u32, Some("ALLOW"), None),
227 Jump(JEQ, libc::IPPROTO_TCP as u32, Some("ALLOW"), None),
228 Jump(JEQ, libc::IPPROTO_UDP as u32, Some("ALLOW"), None),
229
230 JumpTo("REJECT"),
231
232 Label("ALLOW"),
233 Return(Const, 0),
234
235 Label("REJECT"),
236 Return(Const, 99),
237 ];
238 let prog = compile(&asm).unwrap();
239 let mut vm = BpfVM::new(&prog).unwrap();
240
241 let sc_data = libc::seccomp_data {
242 nr: libc::SYS_open as i32,
243 arch: 0,
244 instruction_pointer: 0,
245 args: [0; 6],
246 };
247 let data = any_to_data(&sc_data);
248 let ret = vm.run(&data).unwrap();
249 assert!(ret == 99);
250
251 let sc_data = libc::seccomp_data {
252 nr: libc::SYS_socket as i32,
253 arch: 0,
254 instruction_pointer: 0,
255 args: [
256 libc::AF_INET as u64,
257 (libc::SOCK_STREAM | libc::SOCK_NONBLOCK) as u64,
258 libc::IPPROTO_TCP as u64,
259 0, 0, 0
260 ],
261 };
262 let data = any_to_data(&sc_data);
263 let ret = vm.run(&data).unwrap();
264 assert!(ret == 0);
265 }
266
267}