From patchwork Thu Jan 10 00:12:07 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Willem de Bruijn X-Patchwork-Id: 210900 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 0C8442C020B for ; Thu, 10 Jan 2013 11:12:13 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758165Ab3AJAMM (ORCPT ); Wed, 9 Jan 2013 19:12:12 -0500 Received: from mail-qc0-f202.google.com ([209.85.216.202]:65403 "EHLO mail-qc0-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1758148Ab3AJAML (ORCPT ); Wed, 9 Jan 2013 19:12:11 -0500 Received: by mail-qc0-f202.google.com with SMTP id s25so325242qcq.5 for ; Wed, 09 Jan 2013 16:12:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references; bh=Fg63Wzi/8NGtRWPzERPuxrmdB3fT2MDAncPoMWD2dWw=; b=WH4hpv5qZOYJP9YXeR1uy8kohkpzjAJtSeu2fTb+Yx7wYcJXpyKbp8ZOKLu9GTi5Jp 8zK917/FEyZwV65l1duTglCj1yFv+uvGiIvJQInkDjzXP+V8Ggx1g9f5mHQUelv+7Mra 4RoWg9M5eYtMVHqfrbmGg2f1019h/4i7tet1RonXj9mEDwAyGqsxKhwtnRGJElIzTYfa s3McMoV3NVrqAxhj1G1jf+vgiNepwk3LLCEgIG37lUI4rRbdYhwjWMigo41bujEmhDjW W2M1mqXuwy3dUizGZ7wDU656jR3lqAkt+CgIEsJZQZwAfIe+a1SzHPySbn6kBf/Xsh0I hOew== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references:x-gm-message-state; bh=Fg63Wzi/8NGtRWPzERPuxrmdB3fT2MDAncPoMWD2dWw=; b=IwA033l1Dj2xBk6NvD9lJh6UPuxIcwimylvSPyN0nZMZ44b8dJYK75ZuRcAloKBj+m QKi5KZ752cLS4CEmGAbeNjLBiAz/WZi+pEfRuWha2XKDX8PtT9Hsdbwim+O8wgYp6D4g jkfPrvJ1Rk040suIaGqt0sxd3NgZ4n8L1sGuBIKDYldAXhwjRivKSKWNRfZsAv0gzl3d 6nyZS5u+z2BnKWtSyH7v2/D0OgkHm5F+bRjRgxv9EtOn3eL6eK0VPOiAqRyJBmUg2wbZ OcImzBp2kaiP9MdD/LWCaWkNHv1x3X+sIE5tC04fcDEX6ebDL0VnRxQDGhMxKQ1RdC61 +Xew== X-Received: by 10.236.126.173 with SMTP id b33mr38427786yhi.16.1357776730158; Wed, 09 Jan 2013 16:12:10 -0800 (PST) Received: from wpzn4.hot.corp.google.com (216-239-44-65.google.com [216.239.44.65]) by gmr-mx.google.com with ESMTPS id j11si2972522ank.2.2013.01.09.16.12.10 (version=TLSv1/SSLv3 cipher=AES128-SHA); Wed, 09 Jan 2013 16:12:10 -0800 (PST) Received: from gopher.nyc.corp.google.com (gopher.nyc.corp.google.com [172.26.106.37]) by wpzn4.hot.corp.google.com (Postfix) with ESMTP id 16401820050; Wed, 9 Jan 2013 16:12:10 -0800 (PST) Received: by gopher.nyc.corp.google.com (Postfix, from userid 29878) id BF792C45B9; Wed, 9 Jan 2013 19:12:09 -0500 (EST) From: Willem de Bruijn To: netfilter-devel@vger.kernel.org, pablo@netfilter.org Cc: Willem de Bruijn Subject: [PATCH] extensions: add libxt_bpf extension Date: Wed, 9 Jan 2013 19:12:07 -0500 Message-Id: <1357776727-28547-1-git-send-email-willemb@google.com> X-Mailer: git-send-email 1.7.7.3 In-Reply-To: <1355082884-31927-1-git-send-email-willemb@google.com> References: <1355082884-31927-1-git-send-email-willemb@google.com> X-Gm-Message-State: ALoCoQn1jL1Jd10IR1T7XqQ6i3Vv3/dl6DFWsZt7eCvhegg6oPzXdXWcCzKSVimk95jtOQaSdWj3/RutnJfMzZ++z8KDoGFLdJnAyHX8YtrFvedMV9ibaxGtGL6s49JOJEg6+0S3xdpu97vVlI5/zBijoAX2KiS8dh0E2rNAETHpOgHt22e5sRD9JkIDsddnGLb6f/o1ATA1ldrjsD3tsvF4nmb6sckAiQ== Sender: netfilter-devel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netfilter-devel@vger.kernel.org Changes: - v2 - update to match latest kernel module (fixed size match struct) - add manual page - fix save/restore - fix matching whole program on delete Support filtering using Linux Socket Filters --- extensions/libxt_bpf.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++ extensions/libxt_bpf.man | 17 ++++ 2 files changed, 216 insertions(+), 0 deletions(-) create mode 100644 extensions/libxt_bpf.c create mode 100644 extensions/libxt_bpf.man diff --git a/extensions/libxt_bpf.c b/extensions/libxt_bpf.c new file mode 100644 index 0000000..8ad540f --- /dev/null +++ b/extensions/libxt_bpf.c @@ -0,0 +1,199 @@ +/* + * 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, + O_BCODE_FILE}; + +static void bpf_help(void) +{ + printf( +"bpf match options:\n" +"--bytecode-file : a bpf program as generated by\n" +" `tcpdump -i any -ddd > file\n" +"--bytecode : a bpf program as generated by\n" +" `tcpdump -i any -ddd | 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, +}; + +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: 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(" --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))) ^ + (!!(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)), + .userspacesize = XT_ALIGN(sizeof(struct xt_bpf_info) - sizeof(void *)), + .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 0000000..359684c --- /dev/null +++ b/extensions/libxt_bpf.man @@ -0,0 +1,17 @@ +.TP +Match using Linux Socket Filter. Expects a BPF program in decimal format for a device without link layer. This is the format generated by \fBtcpdump \-ddd \-i $DEV\fP, if $DEV is of type DLT_RAW. +.TP +\fB\-\-bytecode\-file\fP \fIfilename\fP +Pass the program stored in a file. +For example: +.IP +iptables \-A OUTPUT \-m bpf \-\-bytecode-file program.bpf \-j ACCEPT +.TP +\fB\-\-bytecode\fP \fIcode\fP +Pass the program on the command line. In this case all end-lines +must have been converted to commas (for instance using `tr '\\n' ','`). +For example: +.IP +iptables \-A OUTPUT \-m bpf \-\-bytecode + '6,40 0 0 14, 21 0 3 2048,48 0 0 25,21 0 1 20,6 0 0 96,6 0 0 0' + \-j ACCEPT