Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1763343AbXFBVw1 (ORCPT ); Sat, 2 Jun 2007 17:52:27 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1758669AbXFBVwT (ORCPT ); Sat, 2 Jun 2007 17:52:19 -0400 Received: from mailer.gwdg.de ([134.76.10.26]:41066 "EHLO mailer.gwdg.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1757767AbXFBVwS (ORCPT ); Sat, 2 Jun 2007 17:52:18 -0400 Date: Sat, 2 Jun 2007 23:51:20 +0200 (MEST) From: Jan Engelhardt To: Netfilter Mailing List , Netfilter Developer Mailing List , Linux Kernel Mailing List Subject: [PATCH 2/2] xt_u32 (iptables) - match arbitrary bits and bytes of a packet In-Reply-To: Message-ID: References: MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII X-Spam-Report: Content analysis: 0.0 points, 6.0 required _SUMMARY_ Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 9179 Lines: 329 Signed-off-by: Jan Engelhardt --- extensions/.u32-test | 2 extensions/libipt_u32.c | 290 ++++++++++++++++++++++++++++++++++++++++++++++ extensions/libipt_u32.man | 8 + 3 files changed, 300 insertions(+) Index: iptables/extensions/.u32-test =================================================================== --- /dev/null +++ iptables/extensions/.u32-test @@ -0,0 +1,2 @@ +#!/bin/sh +[ -f "$KERNEL_DIR/include/linux/netfilter/xt_u32.h" ] && echo u32 Index: iptables/extensions/libipt_u32.c =================================================================== --- /dev/null +++ iptables/extensions/libipt_u32.c @@ -0,0 +1,290 @@ +/* Shared library add-on to iptables to add u32 matching, + * generalized matching on values found at packet offsets + * + * Detailed doc is in the kernel module source + * net/netfilter/xt_u32.c + * + * (C) 2002 by Don Cohen + * Released under the terms of GNU GPL v2 + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Function which prints out usage message. */ +static void help(void) +{ + printf( "u32 v%s options:\n" + " --u32 tests\n" + " tests := location = value | tests && location = value\n" + " value := range | value , range\n" + " range := number | number : number\n" + " location := number | location operator number\n" + " operator := & | << | >> | @\n", + IPTABLES_VERSION); +} + +/* defined in /usr/include/getopt.h maybe in man getopt */ +static struct option opts[] = { + {"u32", 1, NULL, '1'}, + {NULL}, +}; + +/* shared printing code */ +static void print_u32(const struct xt_u32 *data) +{ + const struct xt_u32_test *ct; + unsigned int testind, i; + + for (testind = 0; testind < data->ntests; ++testind) { + ct = &data->tests[testind]; + + if (testind > 0) + printf("&&"); + + printf("0x%x", ct->location[0].number); + for (i = 1; i < ct->nnums; ++i) { + switch (ct->location[i].nextop) { + case XT_U32_AND: + printf("&"); + break; + case XT_U32_LEFTSH: + printf("<<"); + break; + case XT_U32_RIGHTSH: + printf(">>"); + break; + case XT_U32_AT: + printf("@"); + break; + } + printf("0x%x", ct->location[i].number); + } + + printf("="); + for (i = 0; i < ct->nvalues; ++i) { + if (i > 0) + printf(","); + if (ct->value[i].min == ct->value[i].max) + printf("0x%x", ct->value[i].min); + else + printf("0x%x:0x%x", ct->value[i].min, + ct->value[i].max); + } + } + printf(" "); +} + +/* string_to_number is not quite what we need here ... */ +uint32_t parse_number(char **s, int pos) +{ + uint32_t number; + char *end; + errno = 0; + + number = strtoul(*s, &end, 0); + if (end == *s) + exit_error(PARAMETER_PROBLEM, + "u32: at char %d: expected number", pos); + if (errno) + exit_error(PARAMETER_PROBLEM, + "u32: at char %d: error reading number", pos); + *s = end; + return number; +} + +/* Function which parses command options; returns true if it ate an option */ +static int parse(int c, char **argv, int invert, unsigned int *flags, + const struct ipt_entry *entry, unsigned int *nfcache, + struct ipt_entry_match **match) +{ + struct xt_u32 *data = (void *)(*match)->data; + char *arg = argv[optind-1]; /* the argument string */ + char *start = arg; + int state = 0, testind = 0, locind = 0, valind = 0; + + if (c != '1') + return 0; + + /* + * states: + * 0 = looking for numbers and operations, + * 1 = looking for ranges + */ + while (1) { + /* read next operand/number or range */ + while (isspace(*arg)) + ++arg; + + if (*arg == '\0') { + /* end of argument found */ + if (state == 0) + exit_error(PARAMETER_PROBLEM, + "u32: input ended in location spec"); + if (valind == 0) + exit_error(PARAMETER_PROBLEM, + "u32: test ended with no value spec"); + + data->tests[testind].nnums = locind; + data->tests[testind].nvalues = valind; + testind++; + data->ntests = testind; + + if (testind > XT_U32_MAXSIZE) + exit_error(PARAMETER_PROBLEM, + "u32: at char %d: too many \"&&\"s", + arg - start); + return 1; + } + + if (state == 0) { + /* + * reading location: read a number if nothing read yet, + * otherwise either op number or = to end location spec + */ + if (*arg == '=') { + if (locind == 0) { + exit_error(PARAMETER_PROBLEM, + "u32: at char %d: " + "location spec missing", + arg - start); + } else { + arg++; + state = 1; + } + } else { + if (locind != 0) { + /* need op before number */ + if (*arg == '&') { + data->tests[testind].location[locind].nextop = XT_U32_AND; + } else if (*arg == '<') { + arg++; + if (*arg != '<') + exit_error(PARAMETER_PROBLEM, + "u32: at char %d: a second < expected", arg - start); + data->tests[testind].location[locind].nextop = XT_U32_LEFTSH; + } else if (*arg == '>') { + arg++; + if (*arg != '>') + exit_error(PARAMETER_PROBLEM, + "u32: at char %d: a second > expected", arg - start); + data->tests[testind].location[locind].nextop = XT_U32_RIGHTSH; + } else if (*arg == '@') { + data->tests[testind].location[locind].nextop = XT_U32_AT; + } else { + exit_error(PARAMETER_PROBLEM, + "u32: at char %d: operator expected", arg - start); + } + ++arg; + } + /* now a number; string_to_number skips white space? */ + data->tests[testind].location[locind].number = + parse_number(&arg, arg - start); + locind++; + if (locind > XT_U32_MAXSIZE) + exit_error(PARAMETER_PROBLEM, + "u32: at char %d: too many operators", arg - start); + } + } else { + /* + * state 1 - reading values: read a range if nothing + * read yet, otherwise either ,range or && to end + * test spec + */ + if (*arg == '&') { + arg++; + if (*arg != '&') + exit_error(PARAMETER_PROBLEM, + "u32: at char %d: a second & was expected", arg - start); + if (valind == 0) { + exit_error(PARAMETER_PROBLEM, + "u32: at char %d: value spec missing", arg - start); + } else { + data->tests[testind].nnums = locind; + data->tests[testind].nvalues = valind; + ++testind; + if (testind > XT_U32_MAXSIZE) + exit_error(PARAMETER_PROBLEM, + "u32: at char %d: too many \"&&\"s", arg - start); + ++arg; + state = 0; + locind = 0; + valind = 0; + } + } + else { /* read value range */ + if (valind) { /* need , before number */ + if (*arg != ',') + exit_error(PARAMETER_PROBLEM, + "u32: at char %d: expected , or &&", arg - start); + arg++; + } + data->tests[testind].value[valind].min = parse_number(&arg, arg - start); + + while (isspace(*arg)) + ++arg; + + if (*arg==':') { + arg++; + data->tests[testind].value[valind].max = + parse_number(&arg, arg-start); + } else { + data->tests[testind].value[valind].max = + data->tests[testind].value[valind].min; + } + + valind++; + if (valind > XT_U32_MAXSIZE) + exit_error(PARAMETER_PROBLEM, + "u32: at char %d: too many ,'s", arg-start); + } + } + } +} + +/* Final check; must specify something. */ +static void final_check(unsigned int flags) +{ +} + +/* Prints out the matchinfo. */ +static void print(const struct ipt_ip *ip, const struct ipt_entry_match *match, + int numeric) +{ + printf("u32 "); + print_u32((const void *)match->data); +} + +/* Saves the union ipt_matchinfo in parsable form to stdout. */ +static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match) +{ + printf("--u32 "); + print_u32((const void *)match->data); +} + +struct iptables_match u32 = { + .next = NULL, + .name = "u32", + .version = IPTABLES_VERSION, + .size = IPT_ALIGN(sizeof(struct xt_u32)), + .userspacesize = IPT_ALIGN(sizeof(struct xt_u32)), + .help = &help, + .parse = &parse, + .final_check = &final_check, + .print = &print, + .save = &save, + .extra_opts = opts, +}; + +static __attribute__((constructor)) void libipt_u32_init(void) +{ + register_match(&u32); +} Index: iptables/extensions/libipt_u32.man =================================================================== --- /dev/null +++ iptables/extensions/libipt_u32.man @@ -0,0 +1,8 @@ +U32 allows you to extract quantities of up to 4 bytes from a packet, +AND them with specified masks, shift them by specified amounts and +test whether the results are in any of a set of specified ranges. +The specification of what to extract is general enough to skip over +headers with lengths stored in the packet, as in IP or TCP header +lengths. + +Details and examples are in the kernel module source. - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/