4 * This program is free software; you can distribute it and/or modify
5 * it under the terms of the GNU General Public License as published
6 * by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * Syntax kept close to:
11 * Steven McCanne and Van Jacobson. 1993. The BSD packet filter: a new
12 * architecture for user-level packet capture. In Proceedings of the
13 * USENIX Winter 1993 Conference Proceedings on USENIX Winter 1993
14 * Conference Proceedings (USENIX'93). USENIX Association, Berkeley,
17 * Copyright 2013 Daniel Borkmann <borkmann@redhat.com>
18 * Licensed under the GNU General Public License, version 2.0 (GPLv2)
31 #include <linux/filter.h>
33 #include "bpf_exp.yacc.h"
35 enum jmp_type
{ JTL
, JFL
, JKL
};
39 extern
int yylex(void);
40 extern
void yyerror(const char *str
);
42 extern
void bpf_asm_compile
(FILE *fp
, bool cstyle
);
43 static void bpf_set_curr_instr
(uint16_t op
, uint8_t jt
, uint8_t jf
, uint32_t k
);
44 static void bpf_set_curr_label
(char *label
);
45 static void bpf_set_jmp_label
(char *label
, enum jmp_type type
);
54 %token OP_LDB OP_LDH OP_LD OP_LDX OP_ST OP_STX OP_JMP OP_JEQ OP_JGT OP_JGE
55 %token OP_JSET OP_ADD OP_SUB OP_MUL OP_DIV OP_AND OP_OR OP_XOR OP_LSH OP_RSH
56 %token OP_RET OP_TAX OP_TXA OP_LDXB OP_MOD OP_NEG OP_JNEQ OP_JLT OP_JLE OP_LDI
61 %token
':' ',' '[' ']' '(' ')' 'x' 'a' '+' 'M' '*' '&' '#' '%'
63 %token extension number label
66 %type
<number
> extension
119 : label
':' { bpf_set_curr_label
($1); }
123 : OP_LDB
'[' 'x' '+' number
']' {
124 bpf_set_curr_instr
(BPF_LD | BPF_B | BPF_IND
, 0, 0, $5); }
125 | OP_LDB
'[' '%' 'x' '+' number
']' {
126 bpf_set_curr_instr
(BPF_LD | BPF_B | BPF_IND
, 0, 0, $6); }
127 | OP_LDB
'[' number
']' {
128 bpf_set_curr_instr
(BPF_LD | BPF_B | BPF_ABS
, 0, 0, $3); }
130 bpf_set_curr_instr
(BPF_LD | BPF_B | BPF_ABS
, 0, 0,
135 : OP_LDH
'[' 'x' '+' number
']' {
136 bpf_set_curr_instr
(BPF_LD | BPF_H | BPF_IND
, 0, 0, $5); }
137 | OP_LDH
'[' '%' 'x' '+' number
']' {
138 bpf_set_curr_instr
(BPF_LD | BPF_H | BPF_IND
, 0, 0, $6); }
139 | OP_LDH
'[' number
']' {
140 bpf_set_curr_instr
(BPF_LD | BPF_H | BPF_ABS
, 0, 0, $3); }
142 bpf_set_curr_instr
(BPF_LD | BPF_H | BPF_ABS
, 0, 0,
147 : OP_LDI
'#' number
{
148 bpf_set_curr_instr
(BPF_LD | BPF_IMM
, 0, 0, $3); }
150 bpf_set_curr_instr
(BPF_LD | BPF_IMM
, 0, 0, $2); }
155 bpf_set_curr_instr
(BPF_LD | BPF_IMM
, 0, 0, $3); }
157 bpf_set_curr_instr
(BPF_LD | BPF_W | BPF_LEN
, 0, 0, 0); }
159 bpf_set_curr_instr
(BPF_LD | BPF_W | BPF_ABS
, 0, 0,
161 | OP_LD
'M' '[' number
']' {
162 bpf_set_curr_instr
(BPF_LD | BPF_MEM
, 0, 0, $4); }
163 | OP_LD
'[' 'x' '+' number
']' {
164 bpf_set_curr_instr
(BPF_LD | BPF_W | BPF_IND
, 0, 0, $5); }
165 | OP_LD
'[' '%' 'x' '+' number
']' {
166 bpf_set_curr_instr
(BPF_LD | BPF_W | BPF_IND
, 0, 0, $6); }
167 | OP_LD
'[' number
']' {
168 bpf_set_curr_instr
(BPF_LD | BPF_W | BPF_ABS
, 0, 0, $3); }
172 : OP_LDXI
'#' number
{
173 bpf_set_curr_instr
(BPF_LDX | BPF_IMM
, 0, 0, $3); }
175 bpf_set_curr_instr
(BPF_LDX | BPF_IMM
, 0, 0, $2); }
179 : OP_LDX
'#' number
{
180 bpf_set_curr_instr
(BPF_LDX | BPF_IMM
, 0, 0, $3); }
182 bpf_set_curr_instr
(BPF_LDX | BPF_W | BPF_LEN
, 0, 0, 0); }
183 | OP_LDX
'M' '[' number
']' {
184 bpf_set_curr_instr
(BPF_LDX | BPF_MEM
, 0, 0, $4); }
185 | OP_LDXB number
'*' '(' '[' number
']' '&' number
')' {
186 if
($2 != 4 ||
$9 != 0xf) {
187 fprintf
(stderr
, "ldxb offset not supported!\n");
190 bpf_set_curr_instr
(BPF_LDX | BPF_MSH | BPF_B
, 0, 0, $6); } }
191 | OP_LDX number
'*' '(' '[' number
']' '&' number
')' {
192 if
($2 != 4 ||
$9 != 0xf) {
193 fprintf
(stderr
, "ldxb offset not supported!\n");
196 bpf_set_curr_instr
(BPF_LDX | BPF_MSH | BPF_B
, 0, 0, $6); } }
200 : OP_ST
'M' '[' number
']' {
201 bpf_set_curr_instr
(BPF_ST
, 0, 0, $4); }
205 : OP_STX
'M' '[' number
']' {
206 bpf_set_curr_instr
(BPF_STX
, 0, 0, $4); }
211 bpf_set_jmp_label
($2, JKL
);
212 bpf_set_curr_instr
(BPF_JMP | BPF_JA
, 0, 0, 0); }
216 : OP_JEQ
'#' number
',' label
',' label
{
217 bpf_set_jmp_label
($5, JTL
);
218 bpf_set_jmp_label
($7, JFL
);
219 bpf_set_curr_instr
(BPF_JMP | BPF_JEQ | BPF_K
, 0, 0, $3); }
220 | OP_JEQ
'x' ',' label
',' label
{
221 bpf_set_jmp_label
($4, JTL
);
222 bpf_set_jmp_label
($6, JFL
);
223 bpf_set_curr_instr
(BPF_JMP | BPF_JEQ | BPF_X
, 0, 0, 0); }
224 | OP_JEQ
'%' 'x' ',' label
',' label
{
225 bpf_set_jmp_label
($5, JTL
);
226 bpf_set_jmp_label
($7, JFL
);
227 bpf_set_curr_instr
(BPF_JMP | BPF_JEQ | BPF_X
, 0, 0, 0); }
228 | OP_JEQ
'#' number
',' label
{
229 bpf_set_jmp_label
($5, JTL
);
230 bpf_set_curr_instr
(BPF_JMP | BPF_JEQ | BPF_K
, 0, 0, $3); }
231 | OP_JEQ
'x' ',' label
{
232 bpf_set_jmp_label
($4, JTL
);
233 bpf_set_curr_instr
(BPF_JMP | BPF_JEQ | BPF_X
, 0, 0, 0); }
234 | OP_JEQ
'%' 'x' ',' label
{
235 bpf_set_jmp_label
($5, JTL
);
236 bpf_set_curr_instr
(BPF_JMP | BPF_JEQ | BPF_X
, 0, 0, 0); }
240 : OP_JNEQ
'#' number
',' label
{
241 bpf_set_jmp_label
($5, JFL
);
242 bpf_set_curr_instr
(BPF_JMP | BPF_JEQ | BPF_K
, 0, 0, $3); }
243 | OP_JNEQ
'x' ',' label
{
244 bpf_set_jmp_label
($4, JFL
);
245 bpf_set_curr_instr
(BPF_JMP | BPF_JEQ | BPF_X
, 0, 0, 0); }
246 | OP_JNEQ
'%' 'x' ',' label
{
247 bpf_set_jmp_label
($5, JFL
);
248 bpf_set_curr_instr
(BPF_JMP | BPF_JEQ | BPF_X
, 0, 0, 0); }
252 : OP_JLT
'#' number
',' label
{
253 bpf_set_jmp_label
($5, JFL
);
254 bpf_set_curr_instr
(BPF_JMP | BPF_JGE | BPF_K
, 0, 0, $3); }
255 | OP_JLT
'x' ',' label
{
256 bpf_set_jmp_label
($4, JFL
);
257 bpf_set_curr_instr
(BPF_JMP | BPF_JGE | BPF_X
, 0, 0, 0); }
258 | OP_JLT
'%' 'x' ',' label
{
259 bpf_set_jmp_label
($5, JFL
);
260 bpf_set_curr_instr
(BPF_JMP | BPF_JGE | BPF_X
, 0, 0, 0); }
264 : OP_JLE
'#' number
',' label
{
265 bpf_set_jmp_label
($5, JFL
);
266 bpf_set_curr_instr
(BPF_JMP | BPF_JGT | BPF_K
, 0, 0, $3); }
267 | OP_JLE
'x' ',' label
{
268 bpf_set_jmp_label
($4, JFL
);
269 bpf_set_curr_instr
(BPF_JMP | BPF_JGT | BPF_X
, 0, 0, 0); }
270 | OP_JLE
'%' 'x' ',' label
{
271 bpf_set_jmp_label
($5, JFL
);
272 bpf_set_curr_instr
(BPF_JMP | BPF_JGT | BPF_X
, 0, 0, 0); }
276 : OP_JGT
'#' number
',' label
',' label
{
277 bpf_set_jmp_label
($5, JTL
);
278 bpf_set_jmp_label
($7, JFL
);
279 bpf_set_curr_instr
(BPF_JMP | BPF_JGT | BPF_K
, 0, 0, $3); }
280 | OP_JGT
'x' ',' label
',' label
{
281 bpf_set_jmp_label
($4, JTL
);
282 bpf_set_jmp_label
($6, JFL
);
283 bpf_set_curr_instr
(BPF_JMP | BPF_JGT | BPF_X
, 0, 0, 0); }
284 | OP_JGT
'%' 'x' ',' label
',' label
{
285 bpf_set_jmp_label
($5, JTL
);
286 bpf_set_jmp_label
($7, JFL
);
287 bpf_set_curr_instr
(BPF_JMP | BPF_JGT | BPF_X
, 0, 0, 0); }
288 | OP_JGT
'#' number
',' label
{
289 bpf_set_jmp_label
($5, JTL
);
290 bpf_set_curr_instr
(BPF_JMP | BPF_JGT | BPF_K
, 0, 0, $3); }
291 | OP_JGT
'x' ',' label
{
292 bpf_set_jmp_label
($4, JTL
);
293 bpf_set_curr_instr
(BPF_JMP | BPF_JGT | BPF_X
, 0, 0, 0); }
294 | OP_JGT
'%' 'x' ',' label
{
295 bpf_set_jmp_label
($5, JTL
);
296 bpf_set_curr_instr
(BPF_JMP | BPF_JGT | BPF_X
, 0, 0, 0); }
300 : OP_JGE
'#' number
',' label
',' label
{
301 bpf_set_jmp_label
($5, JTL
);
302 bpf_set_jmp_label
($7, JFL
);
303 bpf_set_curr_instr
(BPF_JMP | BPF_JGE | BPF_K
, 0, 0, $3); }
304 | OP_JGE
'x' ',' label
',' label
{
305 bpf_set_jmp_label
($4, JTL
);
306 bpf_set_jmp_label
($6, JFL
);
307 bpf_set_curr_instr
(BPF_JMP | BPF_JGE | BPF_X
, 0, 0, 0); }
308 | OP_JGE
'%' 'x' ',' label
',' label
{
309 bpf_set_jmp_label
($5, JTL
);
310 bpf_set_jmp_label
($7, JFL
);
311 bpf_set_curr_instr
(BPF_JMP | BPF_JGE | BPF_X
, 0, 0, 0); }
312 | OP_JGE
'#' number
',' label
{
313 bpf_set_jmp_label
($5, JTL
);
314 bpf_set_curr_instr
(BPF_JMP | BPF_JGE | BPF_K
, 0, 0, $3); }
315 | OP_JGE
'x' ',' label
{
316 bpf_set_jmp_label
($4, JTL
);
317 bpf_set_curr_instr
(BPF_JMP | BPF_JGE | BPF_X
, 0, 0, 0); }
318 | OP_JGE
'%' 'x' ',' label
{
319 bpf_set_jmp_label
($5, JTL
);
320 bpf_set_curr_instr
(BPF_JMP | BPF_JGE | BPF_X
, 0, 0, 0); }
324 : OP_JSET
'#' number
',' label
',' label
{
325 bpf_set_jmp_label
($5, JTL
);
326 bpf_set_jmp_label
($7, JFL
);
327 bpf_set_curr_instr
(BPF_JMP | BPF_JSET | BPF_K
, 0, 0, $3); }
328 | OP_JSET
'x' ',' label
',' label
{
329 bpf_set_jmp_label
($4, JTL
);
330 bpf_set_jmp_label
($6, JFL
);
331 bpf_set_curr_instr
(BPF_JMP | BPF_JSET | BPF_X
, 0, 0, 0); }
332 | OP_JSET
'%' 'x' ',' label
',' label
{
333 bpf_set_jmp_label
($5, JTL
);
334 bpf_set_jmp_label
($7, JFL
);
335 bpf_set_curr_instr
(BPF_JMP | BPF_JSET | BPF_X
, 0, 0, 0); }
336 | OP_JSET
'#' number
',' label
{
337 bpf_set_jmp_label
($5, JTL
);
338 bpf_set_curr_instr
(BPF_JMP | BPF_JSET | BPF_K
, 0, 0, $3); }
339 | OP_JSET
'x' ',' label
{
340 bpf_set_jmp_label
($4, JTL
);
341 bpf_set_curr_instr
(BPF_JMP | BPF_JSET | BPF_X
, 0, 0, 0); }
342 | OP_JSET
'%' 'x' ',' label
{
343 bpf_set_jmp_label
($5, JTL
);
344 bpf_set_curr_instr
(BPF_JMP | BPF_JSET | BPF_X
, 0, 0, 0); }
348 : OP_ADD
'#' number
{
349 bpf_set_curr_instr
(BPF_ALU | BPF_ADD | BPF_K
, 0, 0, $3); }
351 bpf_set_curr_instr
(BPF_ALU | BPF_ADD | BPF_X
, 0, 0, 0); }
353 bpf_set_curr_instr
(BPF_ALU | BPF_ADD | BPF_X
, 0, 0, 0); }
357 : OP_SUB
'#' number
{
358 bpf_set_curr_instr
(BPF_ALU | BPF_SUB | BPF_K
, 0, 0, $3); }
360 bpf_set_curr_instr
(BPF_ALU | BPF_SUB | BPF_X
, 0, 0, 0); }
362 bpf_set_curr_instr
(BPF_ALU | BPF_SUB | BPF_X
, 0, 0, 0); }
366 : OP_MUL
'#' number
{
367 bpf_set_curr_instr
(BPF_ALU | BPF_MUL | BPF_K
, 0, 0, $3); }
369 bpf_set_curr_instr
(BPF_ALU | BPF_MUL | BPF_X
, 0, 0, 0); }
371 bpf_set_curr_instr
(BPF_ALU | BPF_MUL | BPF_X
, 0, 0, 0); }
375 : OP_DIV
'#' number
{
376 bpf_set_curr_instr
(BPF_ALU | BPF_DIV | BPF_K
, 0, 0, $3); }
378 bpf_set_curr_instr
(BPF_ALU | BPF_DIV | BPF_X
, 0, 0, 0); }
380 bpf_set_curr_instr
(BPF_ALU | BPF_DIV | BPF_X
, 0, 0, 0); }
384 : OP_MOD
'#' number
{
385 bpf_set_curr_instr
(BPF_ALU | BPF_MOD | BPF_K
, 0, 0, $3); }
387 bpf_set_curr_instr
(BPF_ALU | BPF_MOD | BPF_X
, 0, 0, 0); }
389 bpf_set_curr_instr
(BPF_ALU | BPF_MOD | BPF_X
, 0, 0, 0); }
394 bpf_set_curr_instr
(BPF_ALU | BPF_NEG
, 0, 0, 0); }
398 : OP_AND
'#' number
{
399 bpf_set_curr_instr
(BPF_ALU | BPF_AND | BPF_K
, 0, 0, $3); }
401 bpf_set_curr_instr
(BPF_ALU | BPF_AND | BPF_X
, 0, 0, 0); }
403 bpf_set_curr_instr
(BPF_ALU | BPF_AND | BPF_X
, 0, 0, 0); }
408 bpf_set_curr_instr
(BPF_ALU | BPF_OR | BPF_K
, 0, 0, $3); }
410 bpf_set_curr_instr
(BPF_ALU | BPF_OR | BPF_X
, 0, 0, 0); }
412 bpf_set_curr_instr
(BPF_ALU | BPF_OR | BPF_X
, 0, 0, 0); }
416 : OP_XOR
'#' number
{
417 bpf_set_curr_instr
(BPF_ALU | BPF_XOR | BPF_K
, 0, 0, $3); }
419 bpf_set_curr_instr
(BPF_ALU | BPF_XOR | BPF_X
, 0, 0, 0); }
421 bpf_set_curr_instr
(BPF_ALU | BPF_XOR | BPF_X
, 0, 0, 0); }
425 : OP_LSH
'#' number
{
426 bpf_set_curr_instr
(BPF_ALU | BPF_LSH | BPF_K
, 0, 0, $3); }
428 bpf_set_curr_instr
(BPF_ALU | BPF_LSH | BPF_X
, 0, 0, 0); }
430 bpf_set_curr_instr
(BPF_ALU | BPF_LSH | BPF_X
, 0, 0, 0); }
434 : OP_RSH
'#' number
{
435 bpf_set_curr_instr
(BPF_ALU | BPF_RSH | BPF_K
, 0, 0, $3); }
437 bpf_set_curr_instr
(BPF_ALU | BPF_RSH | BPF_X
, 0, 0, 0); }
439 bpf_set_curr_instr
(BPF_ALU | BPF_RSH | BPF_X
, 0, 0, 0); }
444 bpf_set_curr_instr
(BPF_RET | BPF_A
, 0, 0, 0); }
446 bpf_set_curr_instr
(BPF_RET | BPF_A
, 0, 0, 0); }
448 bpf_set_curr_instr
(BPF_RET | BPF_X
, 0, 0, 0); }
450 bpf_set_curr_instr
(BPF_RET | BPF_X
, 0, 0, 0); }
451 | OP_RET
'#' number
{
452 bpf_set_curr_instr
(BPF_RET | BPF_K
, 0, 0, $3); }
457 bpf_set_curr_instr
(BPF_MISC | BPF_TAX
, 0, 0, 0); }
462 bpf_set_curr_instr
(BPF_MISC | BPF_TXA
, 0, 0, 0); }
467 static int curr_instr
= 0;
468 static struct sock_filter out
[BPF_MAXINSNS
];
469 static char **labels
, **labels_jt
, **labels_jf
, **labels_k
;
471 static void bpf_assert_max
(void)
473 if
(curr_instr
>= BPF_MAXINSNS
) {
474 fprintf
(stderr
, "only max %u insns allowed!\n", BPF_MAXINSNS
);
479 static void bpf_set_curr_instr
(uint16_t code
, uint8_t jt
, uint8_t jf
,
483 out
[curr_instr
].code
= code
;
484 out
[curr_instr
].jt
= jt
;
485 out
[curr_instr
].jf
= jf
;
486 out
[curr_instr
].k
= k
;
490 static void bpf_set_curr_label
(char *label
)
493 labels
[curr_instr
] = label
;
496 static void bpf_set_jmp_label
(char *label
, enum jmp_type type
)
501 labels_jt
[curr_instr
] = label
;
504 labels_jf
[curr_instr
] = label
;
507 labels_k
[curr_instr
] = label
;
512 static int bpf_find_insns_offset
(const char *label
)
514 int i
, max
= curr_instr
, ret
= -ENOENT
;
516 for
(i
= 0; i
< max
; i
++) {
517 if
(labels
[i
] && !strcmp
(label
, labels
[i
])) {
523 if
(ret
== -ENOENT
) {
524 fprintf
(stderr
, "no such label \'%s\'!\n", label
);
531 static void bpf_stage_1_insert_insns
(void)
536 static void bpf_reduce_k_jumps
(void)
540 for
(i
= 0; i
< curr_instr
; i
++) {
542 int off
= bpf_find_insns_offset
(labels_k
[i
]);
543 out
[i
].k
= (uint32_t) (off
- i
- 1);
548 static void bpf_reduce_jt_jumps
(void)
552 for
(i
= 0; i
< curr_instr
; i
++) {
554 int off
= bpf_find_insns_offset
(labels_jt
[i
]);
555 out
[i
].jt
= (uint8_t) (off
- i
-1);
560 static void bpf_reduce_jf_jumps
(void)
564 for
(i
= 0; i
< curr_instr
; i
++) {
566 int off
= bpf_find_insns_offset
(labels_jf
[i
]);
567 out
[i
].jf
= (uint8_t) (off
- i
- 1);
572 static void bpf_stage_2_reduce_labels
(void)
574 bpf_reduce_k_jumps
();
575 bpf_reduce_jt_jumps
();
576 bpf_reduce_jf_jumps
();
579 static void bpf_pretty_print_c
(void)
583 for
(i
= 0; i
< curr_instr
; i
++)
584 printf
("{ %#04x, %2u, %2u, %#010x },\n", out
[i
].code
,
585 out
[i
].jt
, out
[i
].jf
, out
[i
].k
);
588 static void bpf_pretty_print
(void)
592 printf
("%u,", curr_instr
);
593 for
(i
= 0; i
< curr_instr
; i
++)
594 printf
("%u %u %u %u,", out
[i
].code
,
595 out
[i
].jt
, out
[i
].jf
, out
[i
].k
);
599 static void bpf_init
(void)
601 memset
(out
, 0, sizeof
(out
));
603 labels
= calloc
(BPF_MAXINSNS
, sizeof
(*labels
));
605 labels_jt
= calloc
(BPF_MAXINSNS
, sizeof
(*labels_jt
));
607 labels_jf
= calloc
(BPF_MAXINSNS
, sizeof
(*labels_jf
));
609 labels_k
= calloc
(BPF_MAXINSNS
, sizeof
(*labels_k
));
613 static void bpf_destroy_labels
(void)
617 for
(i
= 0; i
< curr_instr
; i
++) {
625 static void bpf_destroy
(void)
627 bpf_destroy_labels
();
634 void bpf_asm_compile
(FILE *fp
, bool cstyle
)
639 bpf_stage_1_insert_insns
();
640 bpf_stage_2_reduce_labels
();
644 bpf_pretty_print_c
();
652 void yyerror(const char *str
)
654 fprintf
(stderr
, "error: %s at line %d\n", str
, yylineno
);