/* 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 <don-netf@isis.cs3-inc.com> * Released under the terms of GNU GPL v2 * * Copyright © CC Computer Consultants GmbH, 2007 * Contact: <jengelh@computergmbh.de> */ #include <ctype.h> #include <errno.h> #include <stdint.h> #include <stdlib.h> #include <stdio.h> #include <xtables.h> #include <linux/netfilter/xt_u32.h> enum { O_U32 = 0, }; static const struct xt_option_entry u32_opts[] = { {.name = "u32", .id = O_U32, .type = XTTYPE_STRING, .flags = XTOPT_MAND}, XTOPT_TABLEEND, }; static void u32_help(void) { printf( "u32 match options:\n" "[!] --u32 tests\n" "\t\t""tests := location \"=\" value | tests \"&&\" location \"=\" value\n" "\t\t""value := range | value \",\" range\n" "\t\t""range := number | number \":\" number\n" "\t\t""location := number | location operator number\n" "\t\t""operator := \"&\" | \"<<\" | \">>\" | \"@\"\n"); } static void u32_dump(const struct xt_u32 *data) { const struct xt_u32_test *ct; unsigned int testind, i; printf(" \""); 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); } } putchar('\"'); } /* string_to_number() is not quite what we need here ... */ static uint32_t parse_number(const char **s, int pos) { uint32_t number; char *end; errno = 0; number = strtoul(*s, &end, 0); if (end == *s) xtables_error(PARAMETER_PROBLEM, "u32: at char %d: expected number", pos); if (errno != 0) xtables_error(PARAMETER_PROBLEM, "u32: at char %d: error reading number", pos); *s = end; return number; } static void u32_parse(struct xt_option_call *cb) { struct xt_u32 *data = cb->data; unsigned int testind = 0, locind = 0, valind = 0; struct xt_u32_test *ct = &data->tests[testind]; /* current test */ const char *arg = cb->arg; /* the argument string */ const char *start = cb->arg; int state = 0; xtables_option_parse(cb); data->invert = cb->invert; /* * 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) xtables_error(PARAMETER_PROBLEM, "u32: abrupt end of input after location specifier"); if (valind == 0) xtables_error(PARAMETER_PROBLEM, "u32: test ended with no value specified"); ct->nnums = locind; ct->nvalues = valind; data->ntests = ++testind; if (testind > XT_U32_MAXSIZE) xtables_error(PARAMETER_PROBLEM, "u32: at char %u: too many \"&&\"s", (unsigned int)(arg - start)); return; } 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) { xtables_error(PARAMETER_PROBLEM, "u32: at char %u: " "location spec missing", (unsigned int)(arg - start)); } else { ++arg; state = 1; } } else { if (locind != 0) { /* need op before number */ if (*arg == '&') { ct->location[locind].nextop = XT_U32_AND; } else if (*arg == '<') { if (*++arg != '<') xtables_error(PARAMETER_PROBLEM, "u32: at char %u: a second '<' was expected", (unsigned int)(arg - start)); ct->location[locind].nextop = XT_U32_LEFTSH; } else if (*arg == '>') { if (*++arg != '>') xtables_error(PARAMETER_PROBLEM, "u32: at char %u: a second '>' was expected", (unsigned int)(arg - start)); ct->location[locind].nextop = XT_U32_RIGHTSH; } else if (*arg == '@') { ct->location[locind].nextop = XT_U32_AT; } else { xtables_error(PARAMETER_PROBLEM, "u32: at char %u: operator expected", (unsigned int)(arg - start)); } ++arg; } /* now a number; string_to_number skips white space? */ ct->location[locind].number = parse_number(&arg, arg - start); if (++locind > XT_U32_MAXSIZE) xtables_error(PARAMETER_PROBLEM, "u32: at char %u: too many operators", (unsigned int)(arg - start)); } } else { /* * state 1 - reading values: read a range if nothing * read yet, otherwise either ,range or && to end * test spec */ if (*arg == '&') { if (*++arg != '&') xtables_error(PARAMETER_PROBLEM, "u32: at char %u: a second '&' was expected", (unsigned int)(arg - start)); if (valind == 0) { xtables_error(PARAMETER_PROBLEM, "u32: at char %u: value spec missing", (unsigned int)(arg - start)); } else { ct->nnums = locind; ct->nvalues = valind; ct = &data->tests[++testind]; if (testind > XT_U32_MAXSIZE) xtables_error(PARAMETER_PROBLEM, "u32: at char %u: too many \"&&\"s", (unsigned int)(arg - start)); ++arg; state = 0; locind = 0; valind = 0; } } else { /* read value range */ if (valind > 0) { /* need , before number */ if (*arg != ',') xtables_error(PARAMETER_PROBLEM, "u32: at char %u: expected \",\" or \"&&\"", (unsigned int)(arg - start)); ++arg; } ct->value[valind].min = parse_number(&arg, arg - start); while (isspace(*arg)) ++arg; if (*arg == ':') { ++arg; ct->value[valind].max = parse_number(&arg, arg-start); } else { ct->value[valind].max = ct->value[valind].min; } if (++valind > XT_U32_MAXSIZE) xtables_error(PARAMETER_PROBLEM, "u32: at char %u: too many \",\"s", (unsigned int)(arg - start)); } } } } static void u32_print(const void *ip, const struct xt_entry_match *match, int numeric) { const struct xt_u32 *data = (const void *)match->data; printf(" u32"); if (data->invert) printf(" !"); u32_dump(data); } static void u32_save(const void *ip, const struct xt_entry_match *match) { const struct xt_u32 *data = (const void *)match->data; if (data->invert) printf(" !"); printf(" --u32"); u32_dump(data); } static struct xtables_match u32_match = { .name = "u32", .family = NFPROTO_UNSPEC, .version = XTABLES_VERSION, .size = XT_ALIGN(sizeof(struct xt_u32)), .userspacesize = XT_ALIGN(sizeof(struct xt_u32)), .help = u32_help, .print = u32_print, .save = u32_save, .x6_parse = u32_parse, .x6_options = u32_opts, }; void _init(void) { xtables_register_match(&u32_match); }