blob: 3a5cdad6de92c1bdf4851c34f2441810323ecf10 [file] [log] [blame]
/* 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 <sys/types.h>
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <netdb.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <xtables.h>
#include "../include/linux/netfilter/xt_u32.h"
static const struct option u32_opts[] = {
{"u32", 1, NULL, 'u'},
{ .name = NULL }
};
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;
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 ... */
static u_int32_t parse_number(char **s, int pos)
{
u_int32_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 != 0)
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 u32_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_match **match)
{
struct xt_u32 *data = (void *)(*match)->data;
unsigned int testind = 0, locind = 0, valind = 0;
struct xt_u32_test *ct = &data->tests[testind]; /* current test */
char *arg = argv[optind-1]; /* the argument string */
char *start = arg;
int state = 0;
if (c != 'u')
return 0;
data->invert = 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)
exit_error(PARAMETER_PROBLEM,
"u32: abrupt end of input after location specifier");
if (valind == 0)
exit_error(PARAMETER_PROBLEM,
"u32: test ended with no value specified");
ct->nnums = locind;
ct->nvalues = valind;
data->ntests = ++testind;
if (testind > XT_U32_MAXSIZE)
exit_error(PARAMETER_PROBLEM,
"u32: at char %u: too many \"&&\"s",
(unsigned int)(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 %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 != '<')
exit_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 != '>')
exit_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 {
exit_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)
exit_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 != '&')
exit_error(PARAMETER_PROBLEM,
"u32: at char %u: a second '&' was expected", (unsigned int)(arg - start));
if (valind == 0) {
exit_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)
exit_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 != ',')
exit_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)
exit_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);
return;
}
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);
return;
}
static struct xtables_match u32_match = {
.name = "u32",
.family = AF_UNSPEC,
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_u32)),
.userspacesize = XT_ALIGN(sizeof(struct xt_u32)),
.help = u32_help,
.parse = u32_parse,
.print = u32_print,
.save = u32_save,
.extra_opts = u32_opts,
};
void _init(void)
{
xtables_register_match(&u32_match);
}