| /* | 
|  * BPF asm code parser | 
|  * | 
|  * This program is free software; you can distribute it and/or modify | 
|  * it under the terms of the GNU General Public License as published | 
|  * by the Free Software Foundation; either version 2 of the License, | 
|  * or (at your option) any later version. | 
|  * | 
|  * Syntax kept close to: | 
|  * | 
|  * Steven McCanne and Van Jacobson. 1993. The BSD packet filter: a new | 
|  * architecture for user-level packet capture. In Proceedings of the | 
|  * USENIX Winter 1993 Conference Proceedings on USENIX Winter 1993 | 
|  * Conference Proceedings (USENIX'93). USENIX Association, Berkeley, | 
|  * CA, USA, 2-2. | 
|  * | 
|  * Copyright 2013 Daniel Borkmann <borkmann@redhat.com> | 
|  * Licensed under the GNU General Public License, version 2.0 (GPLv2) | 
|  */ | 
|   | 
| %{ | 
|   | 
| #include <stdio.h> | 
| #include <string.h> | 
| #include <stdint.h> | 
| #include <stdlib.h> | 
| #include <stdbool.h> | 
| #include <unistd.h> | 
| #include <errno.h> | 
| #include <assert.h> | 
| #include <linux/filter.h> | 
|   | 
| #include "bpf_exp.yacc.h" | 
|   | 
| enum jmp_type { JTL, JFL, JKL }; | 
|   | 
| extern FILE *yyin; | 
| extern int yylineno; | 
| extern int yylex(void); | 
| extern void yyerror(const char *str); | 
|   | 
| extern void bpf_asm_compile(FILE *fp, bool cstyle); | 
| static void bpf_set_curr_instr(uint16_t op, uint8_t jt, uint8_t jf, uint32_t k); | 
| static void bpf_set_curr_label(char *label); | 
| static void bpf_set_jmp_label(char *label, enum jmp_type type); | 
|   | 
| %} | 
|   | 
| %union { | 
|     char *label; | 
|     uint32_t number; | 
| } | 
|   | 
| %token OP_LDB OP_LDH OP_LD OP_LDX OP_ST OP_STX OP_JMP OP_JEQ OP_JGT OP_JGE | 
| %token OP_JSET OP_ADD OP_SUB OP_MUL OP_DIV OP_AND OP_OR OP_XOR OP_LSH OP_RSH | 
| %token OP_RET OP_TAX OP_TXA OP_LDXB OP_MOD OP_NEG OP_JNEQ OP_JLT OP_JLE OP_LDI | 
| %token OP_LDXI | 
|   | 
| %token K_PKT_LEN | 
|   | 
| %token ':' ',' '[' ']' '(' ')' 'x' 'a' '+' 'M' '*' '&' '#' '%' | 
|   | 
| %token extension number label | 
|   | 
| %type <label> label | 
| %type <number> extension | 
| %type <number> number | 
|   | 
| %% | 
|   | 
| prog | 
|     : line | 
|     | prog line | 
|     ; | 
|   | 
| line | 
|     : instr | 
|     | labelled_instr | 
|     ; | 
|   | 
| labelled_instr | 
|     : labelled instr | 
|     ; | 
|   | 
| instr | 
|     : ldb | 
|     | ldh | 
|     | ld | 
|     | ldi | 
|     | ldx | 
|     | ldxi | 
|     | st | 
|     | stx | 
|     | jmp | 
|     | jeq | 
|     | jneq | 
|     | jlt | 
|     | jle | 
|     | jgt | 
|     | jge | 
|     | jset | 
|     | add | 
|     | sub | 
|     | mul | 
|     | div | 
|     | mod | 
|     | neg | 
|     | and | 
|     | or | 
|     | xor | 
|     | lsh | 
|     | rsh | 
|     | ret | 
|     | tax | 
|     | txa | 
|     ; | 
|   | 
| labelled | 
|     : label ':' { bpf_set_curr_label($1); } | 
|     ; | 
|   | 
| ldb | 
|     : OP_LDB '[' 'x' '+' number ']' { | 
|         bpf_set_curr_instr(BPF_LD | BPF_B | BPF_IND, 0, 0, $5); } | 
|     | OP_LDB '[' '%' 'x' '+' number ']' { | 
|         bpf_set_curr_instr(BPF_LD | BPF_B | BPF_IND, 0, 0, $6); } | 
|     | OP_LDB '[' number ']' { | 
|         bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0, $3); } | 
|     | OP_LDB extension { | 
|         bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0, | 
|                    SKF_AD_OFF + $2); } | 
|     ; | 
|   | 
| ldh | 
|     : OP_LDH '[' 'x' '+' number ']' { | 
|         bpf_set_curr_instr(BPF_LD | BPF_H | BPF_IND, 0, 0, $5); } | 
|     | OP_LDH '[' '%' 'x' '+' number ']' { | 
|         bpf_set_curr_instr(BPF_LD | BPF_H | BPF_IND, 0, 0, $6); } | 
|     | OP_LDH '[' number ']' { | 
|         bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0, $3); } | 
|     | OP_LDH extension { | 
|         bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0, | 
|                    SKF_AD_OFF + $2); } | 
|     ; | 
|   | 
| ldi | 
|     : OP_LDI '#' number { | 
|         bpf_set_curr_instr(BPF_LD | BPF_IMM, 0, 0, $3); } | 
|     | OP_LDI number { | 
|         bpf_set_curr_instr(BPF_LD | BPF_IMM, 0, 0, $2); } | 
|     ; | 
|   | 
| ld | 
|     : OP_LD '#' number { | 
|         bpf_set_curr_instr(BPF_LD | BPF_IMM, 0, 0, $3); } | 
|     | OP_LD K_PKT_LEN { | 
|         bpf_set_curr_instr(BPF_LD | BPF_W | BPF_LEN, 0, 0, 0); } | 
|     | OP_LD extension { | 
|         bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0, | 
|                    SKF_AD_OFF + $2); } | 
|     | OP_LD 'M' '[' number ']' { | 
|         bpf_set_curr_instr(BPF_LD | BPF_MEM, 0, 0, $4); } | 
|     | OP_LD '[' 'x' '+' number ']' { | 
|         bpf_set_curr_instr(BPF_LD | BPF_W | BPF_IND, 0, 0, $5); } | 
|     | OP_LD '[' '%' 'x' '+' number ']' { | 
|         bpf_set_curr_instr(BPF_LD | BPF_W | BPF_IND, 0, 0, $6); } | 
|     | OP_LD '[' number ']' { | 
|         bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0, $3); } | 
|     ; | 
|   | 
| ldxi | 
|     : OP_LDXI '#' number { | 
|         bpf_set_curr_instr(BPF_LDX | BPF_IMM, 0, 0, $3); } | 
|     | OP_LDXI number { | 
|         bpf_set_curr_instr(BPF_LDX | BPF_IMM, 0, 0, $2); } | 
|     ; | 
|   | 
| ldx | 
|     : OP_LDX '#' number { | 
|         bpf_set_curr_instr(BPF_LDX | BPF_IMM, 0, 0, $3); } | 
|     | OP_LDX K_PKT_LEN { | 
|         bpf_set_curr_instr(BPF_LDX | BPF_W | BPF_LEN, 0, 0, 0); } | 
|     | OP_LDX 'M' '[' number ']' { | 
|         bpf_set_curr_instr(BPF_LDX | BPF_MEM, 0, 0, $4); } | 
|     | OP_LDXB number '*' '(' '[' number ']' '&' number ')' { | 
|         if ($2 != 4 || $9 != 0xf) { | 
|             fprintf(stderr, "ldxb offset not supported!\n"); | 
|             exit(0); | 
|         } else { | 
|             bpf_set_curr_instr(BPF_LDX | BPF_MSH | BPF_B, 0, 0, $6); } } | 
|     | OP_LDX number '*' '(' '[' number ']' '&' number ')' { | 
|         if ($2 != 4 || $9 != 0xf) { | 
|             fprintf(stderr, "ldxb offset not supported!\n"); | 
|             exit(0); | 
|         } else { | 
|             bpf_set_curr_instr(BPF_LDX | BPF_MSH | BPF_B, 0, 0, $6); } } | 
|     ; | 
|   | 
| st | 
|     : OP_ST 'M' '[' number ']' { | 
|         bpf_set_curr_instr(BPF_ST, 0, 0, $4); } | 
|     ; | 
|   | 
| stx | 
|     : OP_STX 'M' '[' number ']' { | 
|         bpf_set_curr_instr(BPF_STX, 0, 0, $4); } | 
|     ; | 
|   | 
| jmp | 
|     : OP_JMP label { | 
|         bpf_set_jmp_label($2, JKL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JA, 0, 0, 0); } | 
|     ; | 
|   | 
| jeq | 
|     : OP_JEQ '#' number ',' label ',' label { | 
|         bpf_set_jmp_label($5, JTL); | 
|         bpf_set_jmp_label($7, JFL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, $3); } | 
|     | OP_JEQ 'x' ',' label ',' label { | 
|         bpf_set_jmp_label($4, JTL); | 
|         bpf_set_jmp_label($6, JFL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JEQ | BPF_X, 0, 0, 0); } | 
|     | OP_JEQ '%' 'x' ',' label ',' label { | 
|         bpf_set_jmp_label($5, JTL); | 
|         bpf_set_jmp_label($7, JFL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JEQ | BPF_X, 0, 0, 0); } | 
|     | OP_JEQ '#' number ',' label { | 
|         bpf_set_jmp_label($5, JTL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, $3); } | 
|     | OP_JEQ 'x' ',' label { | 
|         bpf_set_jmp_label($4, JTL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JEQ | BPF_X, 0, 0, 0); } | 
|     | OP_JEQ '%' 'x' ',' label { | 
|         bpf_set_jmp_label($5, JTL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JEQ | BPF_X, 0, 0, 0); } | 
|     ; | 
|   | 
| jneq | 
|     : OP_JNEQ '#' number ',' label { | 
|         bpf_set_jmp_label($5, JFL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, $3); } | 
|     | OP_JNEQ 'x' ',' label { | 
|         bpf_set_jmp_label($4, JFL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JEQ | BPF_X, 0, 0, 0); } | 
|     | OP_JNEQ '%' 'x' ',' label { | 
|         bpf_set_jmp_label($5, JFL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JEQ | BPF_X, 0, 0, 0); } | 
|     ; | 
|   | 
| jlt | 
|     : OP_JLT '#' number ',' label { | 
|         bpf_set_jmp_label($5, JFL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JGE | BPF_K, 0, 0, $3); } | 
|     | OP_JLT 'x' ',' label { | 
|         bpf_set_jmp_label($4, JFL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JGE | BPF_X, 0, 0, 0); } | 
|     | OP_JLT '%' 'x' ',' label { | 
|         bpf_set_jmp_label($5, JFL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JGE | BPF_X, 0, 0, 0); } | 
|     ; | 
|   | 
| jle | 
|     : OP_JLE '#' number ',' label { | 
|         bpf_set_jmp_label($5, JFL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JGT | BPF_K, 0, 0, $3); } | 
|     | OP_JLE 'x' ',' label { | 
|         bpf_set_jmp_label($4, JFL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JGT | BPF_X, 0, 0, 0); } | 
|     | OP_JLE '%' 'x' ',' label { | 
|         bpf_set_jmp_label($5, JFL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JGT | BPF_X, 0, 0, 0); } | 
|     ; | 
|   | 
| jgt | 
|     : OP_JGT '#' number ',' label ',' label { | 
|         bpf_set_jmp_label($5, JTL); | 
|         bpf_set_jmp_label($7, JFL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JGT | BPF_K, 0, 0, $3); } | 
|     | OP_JGT 'x' ',' label ',' label { | 
|         bpf_set_jmp_label($4, JTL); | 
|         bpf_set_jmp_label($6, JFL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JGT | BPF_X, 0, 0, 0); } | 
|     | OP_JGT '%' 'x' ',' label ',' label { | 
|         bpf_set_jmp_label($5, JTL); | 
|         bpf_set_jmp_label($7, JFL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JGT | BPF_X, 0, 0, 0); } | 
|     | OP_JGT '#' number ',' label { | 
|         bpf_set_jmp_label($5, JTL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JGT | BPF_K, 0, 0, $3); } | 
|     | OP_JGT 'x' ',' label { | 
|         bpf_set_jmp_label($4, JTL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JGT | BPF_X, 0, 0, 0); } | 
|     | OP_JGT '%' 'x' ',' label { | 
|         bpf_set_jmp_label($5, JTL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JGT | BPF_X, 0, 0, 0); } | 
|     ; | 
|   | 
| jge | 
|     : OP_JGE '#' number ',' label ',' label { | 
|         bpf_set_jmp_label($5, JTL); | 
|         bpf_set_jmp_label($7, JFL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JGE | BPF_K, 0, 0, $3); } | 
|     | OP_JGE 'x' ',' label ',' label { | 
|         bpf_set_jmp_label($4, JTL); | 
|         bpf_set_jmp_label($6, JFL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JGE | BPF_X, 0, 0, 0); } | 
|     | OP_JGE '%' 'x' ',' label ',' label { | 
|         bpf_set_jmp_label($5, JTL); | 
|         bpf_set_jmp_label($7, JFL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JGE | BPF_X, 0, 0, 0); } | 
|     | OP_JGE '#' number ',' label { | 
|         bpf_set_jmp_label($5, JTL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JGE | BPF_K, 0, 0, $3); } | 
|     | OP_JGE 'x' ',' label { | 
|         bpf_set_jmp_label($4, JTL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JGE | BPF_X, 0, 0, 0); } | 
|     | OP_JGE '%' 'x' ',' label { | 
|         bpf_set_jmp_label($5, JTL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JGE | BPF_X, 0, 0, 0); } | 
|     ; | 
|   | 
| jset | 
|     : OP_JSET '#' number ',' label ',' label { | 
|         bpf_set_jmp_label($5, JTL); | 
|         bpf_set_jmp_label($7, JFL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JSET | BPF_K, 0, 0, $3); } | 
|     | OP_JSET 'x' ',' label ',' label { | 
|         bpf_set_jmp_label($4, JTL); | 
|         bpf_set_jmp_label($6, JFL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JSET | BPF_X, 0, 0, 0); } | 
|     | OP_JSET '%' 'x' ',' label ',' label { | 
|         bpf_set_jmp_label($5, JTL); | 
|         bpf_set_jmp_label($7, JFL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JSET | BPF_X, 0, 0, 0); } | 
|     | OP_JSET '#' number ',' label { | 
|         bpf_set_jmp_label($5, JTL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JSET | BPF_K, 0, 0, $3); } | 
|     | OP_JSET 'x' ',' label { | 
|         bpf_set_jmp_label($4, JTL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JSET | BPF_X, 0, 0, 0); } | 
|     | OP_JSET '%' 'x' ',' label { | 
|         bpf_set_jmp_label($5, JTL); | 
|         bpf_set_curr_instr(BPF_JMP | BPF_JSET | BPF_X, 0, 0, 0); } | 
|     ; | 
|   | 
| add | 
|     : OP_ADD '#' number { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_ADD | BPF_K, 0, 0, $3); } | 
|     | OP_ADD 'x' { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_ADD | BPF_X, 0, 0, 0); } | 
|     | OP_ADD '%' 'x' { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_ADD | BPF_X, 0, 0, 0); } | 
|     ; | 
|   | 
| sub | 
|     : OP_SUB '#' number { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_SUB | BPF_K, 0, 0, $3); } | 
|     | OP_SUB 'x' { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_SUB | BPF_X, 0, 0, 0); } | 
|     | OP_SUB '%' 'x' { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_SUB | BPF_X, 0, 0, 0); } | 
|     ; | 
|   | 
| mul | 
|     : OP_MUL '#' number { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_MUL | BPF_K, 0, 0, $3); } | 
|     | OP_MUL 'x' { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_MUL | BPF_X, 0, 0, 0); } | 
|     | OP_MUL '%' 'x' { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_MUL | BPF_X, 0, 0, 0); } | 
|     ; | 
|   | 
| div | 
|     : OP_DIV '#' number { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_DIV | BPF_K, 0, 0, $3); } | 
|     | OP_DIV 'x' { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_DIV | BPF_X, 0, 0, 0); } | 
|     | OP_DIV '%' 'x' { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_DIV | BPF_X, 0, 0, 0); } | 
|     ; | 
|   | 
| mod | 
|     : OP_MOD '#' number { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_MOD | BPF_K, 0, 0, $3); } | 
|     | OP_MOD 'x' { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_MOD | BPF_X, 0, 0, 0); } | 
|     | OP_MOD '%' 'x' { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_MOD | BPF_X, 0, 0, 0); } | 
|     ; | 
|   | 
| neg | 
|     : OP_NEG { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_NEG, 0, 0, 0); } | 
|     ; | 
|   | 
| and | 
|     : OP_AND '#' number { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_AND | BPF_K, 0, 0, $3); } | 
|     | OP_AND 'x' { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_AND | BPF_X, 0, 0, 0); } | 
|     | OP_AND '%' 'x' { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_AND | BPF_X, 0, 0, 0); } | 
|     ; | 
|   | 
| or | 
|     : OP_OR '#' number { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_OR | BPF_K, 0, 0, $3); } | 
|     | OP_OR 'x' { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_OR | BPF_X, 0, 0, 0); } | 
|     | OP_OR '%' 'x' { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_OR | BPF_X, 0, 0, 0); } | 
|     ; | 
|   | 
| xor | 
|     : OP_XOR '#' number { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_XOR | BPF_K, 0, 0, $3); } | 
|     | OP_XOR 'x' { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_XOR | BPF_X, 0, 0, 0); } | 
|     | OP_XOR '%' 'x' { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_XOR | BPF_X, 0, 0, 0); } | 
|     ; | 
|   | 
| lsh | 
|     : OP_LSH '#' number { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_LSH | BPF_K, 0, 0, $3); } | 
|     | OP_LSH 'x' { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_LSH | BPF_X, 0, 0, 0); } | 
|     | OP_LSH '%' 'x' { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_LSH | BPF_X, 0, 0, 0); } | 
|     ; | 
|   | 
| rsh | 
|     : OP_RSH '#' number { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_RSH | BPF_K, 0, 0, $3); } | 
|     | OP_RSH 'x' { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_RSH | BPF_X, 0, 0, 0); } | 
|     | OP_RSH '%' 'x' { | 
|         bpf_set_curr_instr(BPF_ALU | BPF_RSH | BPF_X, 0, 0, 0); } | 
|     ; | 
|   | 
| ret | 
|     : OP_RET 'a' { | 
|         bpf_set_curr_instr(BPF_RET | BPF_A, 0, 0, 0); } | 
|     | OP_RET '%' 'a' { | 
|         bpf_set_curr_instr(BPF_RET | BPF_A, 0, 0, 0); } | 
|     | OP_RET 'x' { | 
|         bpf_set_curr_instr(BPF_RET | BPF_X, 0, 0, 0); } | 
|     | OP_RET '%' 'x' { | 
|         bpf_set_curr_instr(BPF_RET | BPF_X, 0, 0, 0); } | 
|     | OP_RET '#' number { | 
|         bpf_set_curr_instr(BPF_RET | BPF_K, 0, 0, $3); } | 
|     ; | 
|   | 
| tax | 
|     : OP_TAX { | 
|         bpf_set_curr_instr(BPF_MISC | BPF_TAX, 0, 0, 0); } | 
|     ; | 
|   | 
| txa | 
|     : OP_TXA { | 
|         bpf_set_curr_instr(BPF_MISC | BPF_TXA, 0, 0, 0); } | 
|     ; | 
|   | 
| %% | 
|   | 
| static int curr_instr = 0; | 
| static struct sock_filter out[BPF_MAXINSNS]; | 
| static char **labels, **labels_jt, **labels_jf, **labels_k; | 
|   | 
| static void bpf_assert_max(void) | 
| { | 
|     if (curr_instr >= BPF_MAXINSNS) { | 
|         fprintf(stderr, "only max %u insns allowed!\n", BPF_MAXINSNS); | 
|         exit(0); | 
|     } | 
| } | 
|   | 
| static void bpf_set_curr_instr(uint16_t code, uint8_t jt, uint8_t jf, | 
|                    uint32_t k) | 
| { | 
|     bpf_assert_max(); | 
|     out[curr_instr].code = code; | 
|     out[curr_instr].jt = jt; | 
|     out[curr_instr].jf = jf; | 
|     out[curr_instr].k = k; | 
|     curr_instr++; | 
| } | 
|   | 
| static void bpf_set_curr_label(char *label) | 
| { | 
|     bpf_assert_max(); | 
|     labels[curr_instr] = label; | 
| } | 
|   | 
| static void bpf_set_jmp_label(char *label, enum jmp_type type) | 
| { | 
|     bpf_assert_max(); | 
|     switch (type) { | 
|     case JTL: | 
|         labels_jt[curr_instr] = label; | 
|         break; | 
|     case JFL: | 
|         labels_jf[curr_instr] = label; | 
|         break; | 
|     case JKL: | 
|         labels_k[curr_instr] = label; | 
|         break; | 
|     } | 
| } | 
|   | 
| static int bpf_find_insns_offset(const char *label) | 
| { | 
|     int i, max = curr_instr, ret = -ENOENT; | 
|   | 
|     for (i = 0; i < max; i++) { | 
|         if (labels[i] && !strcmp(label, labels[i])) { | 
|             ret = i; | 
|             break; | 
|         } | 
|     } | 
|   | 
|     if (ret == -ENOENT) { | 
|         fprintf(stderr, "no such label \'%s\'!\n", label); | 
|         exit(0); | 
|     } | 
|   | 
|     return ret; | 
| } | 
|   | 
| static void bpf_stage_1_insert_insns(void) | 
| { | 
|     yyparse(); | 
| } | 
|   | 
| static void bpf_reduce_k_jumps(void) | 
| { | 
|     int i; | 
|   | 
|     for (i = 0; i < curr_instr; i++) { | 
|         if (labels_k[i]) { | 
|             int off = bpf_find_insns_offset(labels_k[i]); | 
|             out[i].k = (uint32_t) (off - i - 1); | 
|         } | 
|     } | 
| } | 
|   | 
| static uint8_t bpf_encode_jt_jf_offset(int off, int i) | 
| { | 
|     int delta = off - i - 1; | 
|   | 
|     if (delta < 0 || delta > 255) | 
|         fprintf(stderr, "warning: insn #%d jumps to insn #%d, " | 
|                 "which is out of range\n", i, off); | 
|     return (uint8_t) delta; | 
| } | 
|   | 
| static void bpf_reduce_jt_jumps(void) | 
| { | 
|     int i; | 
|   | 
|     for (i = 0; i < curr_instr; i++) { | 
|         if (labels_jt[i]) { | 
|             int off = bpf_find_insns_offset(labels_jt[i]); | 
|             out[i].jt = bpf_encode_jt_jf_offset(off, i); | 
|         } | 
|     } | 
| } | 
|   | 
| static void bpf_reduce_jf_jumps(void) | 
| { | 
|     int i; | 
|   | 
|     for (i = 0; i < curr_instr; i++) { | 
|         if (labels_jf[i]) { | 
|             int off = bpf_find_insns_offset(labels_jf[i]); | 
|             out[i].jf = bpf_encode_jt_jf_offset(off, i); | 
|         } | 
|     } | 
| } | 
|   | 
| static void bpf_stage_2_reduce_labels(void) | 
| { | 
|     bpf_reduce_k_jumps(); | 
|     bpf_reduce_jt_jumps(); | 
|     bpf_reduce_jf_jumps(); | 
| } | 
|   | 
| static void bpf_pretty_print_c(void) | 
| { | 
|     int i; | 
|   | 
|     for (i = 0; i < curr_instr; i++) | 
|         printf("{ %#04x, %2u, %2u, %#010x },\n", out[i].code, | 
|                out[i].jt, out[i].jf, out[i].k); | 
| } | 
|   | 
| static void bpf_pretty_print(void) | 
| { | 
|     int i; | 
|   | 
|     printf("%u,", curr_instr); | 
|     for (i = 0; i < curr_instr; i++) | 
|         printf("%u %u %u %u,", out[i].code, | 
|                out[i].jt, out[i].jf, out[i].k); | 
|     printf("\n"); | 
| } | 
|   | 
| static void bpf_init(void) | 
| { | 
|     memset(out, 0, sizeof(out)); | 
|   | 
|     labels = calloc(BPF_MAXINSNS, sizeof(*labels)); | 
|     assert(labels); | 
|     labels_jt = calloc(BPF_MAXINSNS, sizeof(*labels_jt)); | 
|     assert(labels_jt); | 
|     labels_jf = calloc(BPF_MAXINSNS, sizeof(*labels_jf)); | 
|     assert(labels_jf); | 
|     labels_k = calloc(BPF_MAXINSNS, sizeof(*labels_k)); | 
|     assert(labels_k); | 
| } | 
|   | 
| static void bpf_destroy_labels(void) | 
| { | 
|     int i; | 
|   | 
|     for (i = 0; i < curr_instr; i++) { | 
|         free(labels_jf[i]); | 
|         free(labels_jt[i]); | 
|         free(labels_k[i]); | 
|         free(labels[i]); | 
|     } | 
| } | 
|   | 
| static void bpf_destroy(void) | 
| { | 
|     bpf_destroy_labels(); | 
|     free(labels_jt); | 
|     free(labels_jf); | 
|     free(labels_k); | 
|     free(labels); | 
| } | 
|   | 
| void bpf_asm_compile(FILE *fp, bool cstyle) | 
| { | 
|     yyin = fp; | 
|   | 
|     bpf_init(); | 
|     bpf_stage_1_insert_insns(); | 
|     bpf_stage_2_reduce_labels(); | 
|     bpf_destroy(); | 
|   | 
|     if (cstyle) | 
|         bpf_pretty_print_c(); | 
|     else | 
|         bpf_pretty_print(); | 
|   | 
|     if (fp != stdin) | 
|         fclose(yyin); | 
| } | 
|   | 
| void yyerror(const char *str) | 
| { | 
|     fprintf(stderr, "error: %s at line %d\n", str, yylineno); | 
|     exit(1); | 
| } |