Support filtering using Linux Socket Filters --- extensions/libxt_bpf.c | 225 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 225 insertions(+), 0 deletions(-) create mode 100644 extensions/libxt_bpf.c diff --git a/extensions/libxt_bpf.c b/extensions/libxt_bpf.c new file mode 100644 index 0000000..4c02402 --- /dev/null +++ b/extensions/libxt_bpf.c @@ -0,0 +1,225 @@ +/* + * Xtables BPF extension + * + * Written by Willem de Bruijn (willemb@xxxxxxxxxx) + * Copyright Google, Inc. 2012 + * Licensed under the GNU General Public License version 2 (GPLv2) +*/ + +#include <linux/netfilter/xt_bpf.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <xtables.h> + +#define BCODE_FILE_MAX_LEN_B 1024 + +enum { O_BCODE_STDIN = 0, + O_BCODE_FILE}; + +static void bpf_help(void) +{ + printf( +"bpf match options:\n" +"--bytecode-file <file> : a bpf program as generated by\n" +" `tcpdump -i any -ddd <expr> > file\n" +"--bytecode <program> : a bpf program as generated by\n" +" `tcpdump -i any -ddd <expr> | tr '\\n' ','`\n"); +} + +static const struct xt_option_entry bpf_opts[] = { + {.name = "bytecode", .id = O_BCODE_STDIN, .type = XTTYPE_STRING}, + {.name = "bytecode-file", .id = O_BCODE_FILE, .type = XTTYPE_STRING}, + XTOPT_TABLEEND, +}; + +/* Q: make a generic libxtables call? */ +static void bpf_resize_match_struct(struct xt_option_call *cb, int head_len, + int elem_len, int num_elem) +{ + const int match_len = sizeof(struct xt_entry_match); + struct xt_entry_match *new_match; + unsigned long new_match_size; + + new_match_size = match_len + head_len + (elem_len * num_elem); + new_match = malloc(new_match_size); + if (!new_match) { + xtables_error(PARAMETER_PROBLEM, + "bpf: allocation failed"); + return; + } + + memcpy(new_match, *cb->match, match_len + head_len); + new_match->u.match_size = new_match_size; + + free(*cb->match); + *cb->match = new_match; + cb->data = ((void *) new_match) + match_len; +} + +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"); + + /* adjust match struct to be able to store all instructions. */ + bpf_resize_match_struct(cb, + sizeof(struct xt_bpf_info), + sizeof(struct sock_filter), + bi->bpf_program_num_elem); + bi = cb->data; + + /* 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: program length: parameter"); + return; + } + 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); + return; + } + i++; + } + + if (i != bi->bpf_program_num_elem) { + xtables_error(PARAMETER_PROBLEM, + "bpf: program length: parsing"); + return; + } +} + +static void bpf_parse_file(struct xt_option_call *cb, const char *filepath) +{ + struct stat stats; + char *contents; + int fd, len = 0, ret; + + if (access(filepath, F_OK)) + xtables_error(PARAMETER_PROBLEM, "bpf: no such file"); + if (access(filepath, R_OK)) + xtables_error(PARAMETER_PROBLEM, "bpf: no read permissions"); + + fd = open(filepath, O_RDONLY); + if (fd == -1) + xtables_error(PARAMETER_PROBLEM, "bpf: error opening file"); + if (fstat(fd, &stats)) + xtables_error(PARAMETER_PROBLEM, "bpf: error reading metadata"); + if (stats.st_size >= BCODE_FILE_MAX_LEN_B) + xtables_error(PARAMETER_PROBLEM, "bpf: file too large"); + + contents = malloc(stats.st_size + 1); + if (!contents) + xtables_error(PARAMETER_PROBLEM, "bpf: allocation failed"); + contents[stats.st_size] = 0; + + while (len < stats.st_size) { + ret = read(fd, contents + len, stats.st_size - len); + if (ret == -1) { + if (errno == EAGAIN) + continue; + xtables_error(PARAMETER_PROBLEM, "bpf: read failed"); + } + if (!ret) + xtables_error(PARAMETER_PROBLEM, "bpf: unexpected EOF"); + len += ret; + } + + bpf_parse_string(cb, contents, '\n'); + + free(contents); + close(fd); +} + +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; + case O_BCODE_FILE: + bpf_parse_file(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; 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); +} + +static void bpf_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_bpf_info *info = (void *) match->data; + + printf("bpf --bytecode %hu,", info->bpf_program_num_elem); + bpf_print_code(ip, match); +} + +static void bpf_fcheck(struct xt_fcheck_call *cb) +{ + if (((!!(cb->xflags & (1 << O_BCODE_STDIN))) ^ + (!!(cb->xflags & (1 << O_BCODE_FILE)))) == 0) + xtables_error(PARAMETER_PROBLEM, + "bpf: pass bytecode or bytecode-file, not both"); +} + +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)), + .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); +} -- 1.7.7.3 -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html