From e0a0dd703b3448f0f07fc59b7232bf1f1cce7b86 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Wed, 23 Jan 2013 16:00:58 +0000 Subject: extensions: add libxt_bpf extension Add user-space code to support the new BPF iptables extension. Pablo has mangled the original patch to: * include a copy of include/linux/netfilter/xt_bpf.h in the tree. * I have also remove the --bytecode-file option. The original proposal was to accept BPF code in a file in human readable format. Now, with the nfbpf_compile utility, it's very easy to generate the filter using tcpdump-like syntax. * I have remove the trailing comma in the backtick format, the parser works just fine for me here. * Fix error message if --bytecode is missing. Signed-off-by: Willem de Bruijn Signed-off-by: Pablo Neira Ayuso --- extensions/libxt_bpf.c | 152 +++++++++++++++++++++++++++++++++++++++ extensions/libxt_bpf.man | 34 +++++++++ include/linux/netfilter/xt_bpf.h | 17 +++++ 3 files changed, 203 insertions(+) create mode 100644 extensions/libxt_bpf.c create mode 100644 extensions/libxt_bpf.man create mode 100644 include/linux/netfilter/xt_bpf.h diff --git a/extensions/libxt_bpf.c b/extensions/libxt_bpf.c new file mode 100644 index 00000000..dca97d70 --- /dev/null +++ b/extensions/libxt_bpf.c @@ -0,0 +1,152 @@ +/* + * Xtables BPF extension + * + * Written by Willem de Bruijn (willemb@google.com) + * Copyright Google, Inc. 2013 + * Licensed under the GNU General Public License version 2 (GPLv2) +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BCODE_FILE_MAX_LEN_B 1024 + +enum { + O_BCODE_STDIN = 0, +}; + +static void bpf_help(void) +{ + printf( +"bpf match options:\n" +"--bytecode : a bpf program as generated by\n" +" `nfbpf_compiler RAW `\n"); +} + +static const struct xt_option_entry bpf_opts[] = { + {.name = "bytecode", .id = O_BCODE_STDIN, .type = XTTYPE_STRING}, + XTOPT_TABLEEND, +}; + +static void bpf_parse_string(struct xt_option_call *cb, const char *bpf_program, + const char separator) +{ + struct xt_bpf_info *bi = (void *) cb->data; + const char *token; + char sp; + int i; + + /* parse head: length. */ + if (sscanf(bpf_program, "%hu%c", &bi->bpf_program_num_elem, &sp) != 2 || + sp != separator) + xtables_error(PARAMETER_PROBLEM, + "bpf: error parsing program length"); + if (!bi->bpf_program_num_elem) + xtables_error(PARAMETER_PROBLEM, + "bpf: illegal zero length program"); + if (bi->bpf_program_num_elem > XT_BPF_MAX_NUM_INSTR) + xtables_error(PARAMETER_PROBLEM, + "bpf: number of instructions exceeds maximum"); + + /* parse instructions. */ + i = 0; + token = bpf_program; + while ((token = strchr(token, separator)) && (++token)[0]) { + if (i >= bi->bpf_program_num_elem) + xtables_error(PARAMETER_PROBLEM, + "bpf: real program length exceeds" + " the encoded length parameter"); + if (sscanf(token, "%hu %hhu %hhu %u,", + &bi->bpf_program[i].code, + &bi->bpf_program[i].jt, + &bi->bpf_program[i].jf, + &bi->bpf_program[i].k) != 4) + xtables_error(PARAMETER_PROBLEM, + "bpf: error at instr %d", i); + i++; + } + + if (i != bi->bpf_program_num_elem) + xtables_error(PARAMETER_PROBLEM, + "bpf: parsed program length is less than the" + " encoded length parameter"); +} + +static void bpf_parse(struct xt_option_call *cb) +{ + xtables_option_parse(cb); + switch (cb->entry->id) { + case O_BCODE_STDIN: + bpf_parse_string(cb, cb->arg, ','); + break; + default: + xtables_error(PARAMETER_PROBLEM, "bpf: unknown option"); + } +} + +static void bpf_print_code(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_bpf_info *info = (void *) match->data; + int i; + + for (i = 0; i < info->bpf_program_num_elem-1; i++) + printf("%hu %hhu %hhu %u,", info->bpf_program[i].code, + info->bpf_program[i].jt, + info->bpf_program[i].jf, + info->bpf_program[i].k); + + printf("%hu %hhu %hhu %u", info->bpf_program[i].code, + info->bpf_program[i].jt, + info->bpf_program[i].jf, + info->bpf_program[i].k); +} + +static void bpf_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_bpf_info *info = (void *) match->data; + + printf(" --bytecode \"%hu,", info->bpf_program_num_elem); + bpf_print_code(ip, match); + printf("\""); +} + +static void bpf_fcheck(struct xt_fcheck_call *cb) +{ + if (!(cb->xflags & (1 << O_BCODE_STDIN))) + xtables_error(PARAMETER_PROBLEM, + "bpf: missing --bytecode parameter"); +} + +static void bpf_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + printf("match bpf "); + return bpf_print_code(ip, match); +} + +static struct xtables_match bpf_match = { + .family = NFPROTO_UNSPEC, + .name = "bpf", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_bpf_info)), + .userspacesize = XT_ALIGN(offsetof(struct xt_bpf_info, filter)), + .help = bpf_help, + .print = bpf_print, + .save = bpf_save, + .x6_parse = bpf_parse, + .x6_fcheck = bpf_fcheck, + .x6_options = bpf_opts, +}; + +void _init(void) +{ + xtables_register_match(&bpf_match); +} diff --git a/extensions/libxt_bpf.man b/extensions/libxt_bpf.man new file mode 100644 index 00000000..4120a23d --- /dev/null +++ b/extensions/libxt_bpf.man @@ -0,0 +1,34 @@ +Match using Linux Socket Filter. Expects a BPF program in decimal format. This +is the format generated by the \fBnfbpf_compile\fP utility. +.TP +\fB\-\-bytecode\fP \fIcode\fP +Pass the code in backtick format as argument. +.PP +The code format is similar to the output of the tcpdump -ddd command: one line +that stores the number of instructions, followed by one line for each +instruction. Instruction lines follow the pattern 'u16 u8 u8 u32' in decimal +notation. Fields encode the operation, jump offset if true, jump offset if +false and generic multiuse field 'K'. Comments are not supported. +.PP +For example, to read only packets matching 'ip proto 6', insert the following, +without the comments or trailing whitespace: +.IP +4 # number of instructions +.br +48 0 0 9 # load byte ip->proto +.br +21 0 1 6 # jump equal IPPROTO_TCP +.br +6 0 0 1 # return pass (non-zero) +.br +6 0 0 0 # return fail (zero) +.PP +You can pass this filter to the bpf match with the following command: +.IP +iptables \-A OUTPUT \-m bpf \-\-bytecode '4,48 0 0 9,21 0 1 6,6 0 0 1,6 0 0 0' \-j ACCEPT +.PP +Or instead, you can invoke the nfbpf_compile utility. +.IP +iptables \-A OUTPUT \-m bpf \-\-bytecode "`nfbpf_compile RAW 'ip proto 6'`" \-j ACCEPT +.PP +You may want to learn more about BPF from FreeBSD's bpf(4) manpage. diff --git a/include/linux/netfilter/xt_bpf.h b/include/linux/netfilter/xt_bpf.h new file mode 100644 index 00000000..5dda450e --- /dev/null +++ b/include/linux/netfilter/xt_bpf.h @@ -0,0 +1,17 @@ +#ifndef _XT_BPF_H +#define _XT_BPF_H + +#include +#include + +#define XT_BPF_MAX_NUM_INSTR 64 + +struct xt_bpf_info { + __u16 bpf_program_num_elem; + struct sock_filter bpf_program[XT_BPF_MAX_NUM_INSTR]; + + /* only used in the kernel */ + struct sk_filter *filter __attribute__((aligned(8))); +}; + +#endif /*_XT_BPF_H */ -- cgit v1.2.3