| /* | 
 |  * 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 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 K_PROTO K_TYPE K_NLATTR K_NLATTR_NEST K_MARK K_QUEUE K_HATYPE | 
 | %token K_RXHASH K_CPU K_IFIDX K_VLANT K_VLANP K_POFF | 
 |  | 
 | %token ':' ',' '[' ']' '(' ')' 'x' 'a' '+' 'M' '*' '&' '#' '%' | 
 |  | 
 | %token number label | 
 |  | 
 | %type <label> label | 
 | %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 K_PROTO { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_PROTOCOL); } | 
 | 	| OP_LDB K_TYPE { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_PKTTYPE); } | 
 | 	| OP_LDB K_IFIDX { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_IFINDEX); } | 
 | 	| OP_LDB K_NLATTR { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_NLATTR); } | 
 | 	| OP_LDB K_NLATTR_NEST { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_NLATTR_NEST); } | 
 | 	| OP_LDB K_MARK { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_MARK); } | 
 | 	| OP_LDB K_QUEUE { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_QUEUE); } | 
 | 	| OP_LDB K_HATYPE { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_HATYPE); } | 
 | 	| OP_LDB K_RXHASH { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_RXHASH); } | 
 | 	| OP_LDB K_CPU { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_CPU); } | 
 | 	| OP_LDB K_VLANT { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_VLAN_TAG); } | 
 | 	| OP_LDB K_VLANP { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT); } | 
 | 	| OP_LDB K_POFF { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_PAY_OFFSET); } | 
 | 	; | 
 |  | 
 | 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 K_PROTO { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_PROTOCOL); } | 
 | 	| OP_LDH K_TYPE { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_PKTTYPE); } | 
 | 	| OP_LDH K_IFIDX { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_IFINDEX); } | 
 | 	| OP_LDH K_NLATTR { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_NLATTR); } | 
 | 	| OP_LDH K_NLATTR_NEST { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_NLATTR_NEST); } | 
 | 	| OP_LDH K_MARK { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_MARK); } | 
 | 	| OP_LDH K_QUEUE { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_QUEUE); } | 
 | 	| OP_LDH K_HATYPE { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_HATYPE); } | 
 | 	| OP_LDH K_RXHASH { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_RXHASH); } | 
 | 	| OP_LDH K_CPU { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_CPU); } | 
 | 	| OP_LDH K_VLANT { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_VLAN_TAG); } | 
 | 	| OP_LDH K_VLANP { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT); } | 
 | 	| OP_LDH K_POFF { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_PAY_OFFSET); } | 
 | 	; | 
 |  | 
 | 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 K_PROTO { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_PROTOCOL); } | 
 | 	| OP_LD K_TYPE { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_PKTTYPE); } | 
 | 	| OP_LD K_IFIDX { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_IFINDEX); } | 
 | 	| OP_LD K_NLATTR { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_NLATTR); } | 
 | 	| OP_LD K_NLATTR_NEST { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_NLATTR_NEST); } | 
 | 	| OP_LD K_MARK { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_MARK); } | 
 | 	| OP_LD K_QUEUE { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_QUEUE); } | 
 | 	| OP_LD K_HATYPE { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_HATYPE); } | 
 | 	| OP_LD K_RXHASH { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_RXHASH); } | 
 | 	| OP_LD K_CPU { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_CPU); } | 
 | 	| OP_LD K_VLANT { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_VLAN_TAG); } | 
 | 	| OP_LD K_VLANP { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT); } | 
 | 	| OP_LD K_POFF { | 
 | 		bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0, | 
 | 				   SKF_AD_OFF + SKF_AD_PAY_OFFSET); } | 
 | 	| 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 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 = (uint8_t) (off - i -1); | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | 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 = (uint8_t) (off - i - 1); | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | 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) | 
 | { | 
 | 	exit(1); | 
 | } |