blob: 788413c85a63bd607ebed58903a840562ed3fcb4 [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/ipv4/netfilter/ipt_u32.c
*
* (C) 2002 by Don Cohen <don-netf@isis.cs3-inc.com>
* Released under the terms of GNU GPL v2
*/
#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <iptables.h>
#include <linux/netfilter_ipv4/ipt_u32.h>
#include <errno.h>
#include <ctype.h>
/* 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, 0, '1' },
{ 0 }
};
/* Initialize the match. */
static void
init(struct ipt_entry_match *m, unsigned int *nfcache)
{
*nfcache |= NFC_UNKNOWN;
}
/* shared printing code */
static void print_u32(struct ipt_u32 *data)
{
unsigned int testind;
for (testind=0; testind < data->ntests; testind++) {
if (testind) printf("&&");
{
unsigned int i;
printf("0x%x", data->tests[testind].location[0].number);
for (i = 1; i < data->tests[testind].nnums; i++) {
switch (data->tests[testind].location[i].nextop) {
case IPT_U32_AND: printf("&"); break;
case IPT_U32_LEFTSH: printf("<<"); break;
case IPT_U32_RIGHTSH: printf(">>"); break;
case IPT_U32_AT: printf("@"); break;
}
printf("0x%x", data->tests[testind].location[i].number);
}
printf("=");
for (i = 0; i < data->tests[testind].nvalues; i++) {
if (i) printf(",");
if (data->tests[testind].value[i].min
== data->tests[testind].value[i].max)
printf("0x%x", data->tests[testind].value[i].min);
else printf("0x%x:0x%x", data->tests[testind].value[i].min,
data->tests[testind].value[i].max);
}
}
}
printf(" ");
}
/* string_to_number is not quite what we need here ... */
u_int32_t parse_number(char **s, int pos)
{
u_int32_t number;
char *end;
errno = 0;
number = strtol(*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 ipt_u32 *data = (struct ipt_u32 *)(*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++; /* skip white space */
if (! *arg) { /* 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 > U32MAXSIZE)
exit_error(PARAMETER_PROBLEM,
"u32: at char %d too many &&'s",
arg-start);
/* debugging
print_u32(data);printf("\n");
exit_error(PARAMETER_PROBLEM, "debugging output done"); */
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) { /* need op before number */
if (*arg == '&') {
data->tests[testind].location[locind].nextop = IPT_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 = IPT_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 = IPT_U32_RIGHTSH;
}
else if (*arg == '@') {
data->tests[testind].location[locind].nextop = IPT_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 > U32MAXSIZE)
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 & 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 > U32MAXSIZE)
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++; /* another place white space could be */
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 > U32MAXSIZE)
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((struct ipt_u32 *)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((struct ipt_u32 *)match->data);
}
struct iptables_match u32
= { NULL,
"u32",
IPTABLES_VERSION,
IPT_ALIGN(sizeof(struct ipt_u32)),
IPT_ALIGN(sizeof(struct ipt_u32)),
&help,
&init,
&parse,
&final_check,
&print,
&save,
opts
};
void
_init(void)
{
register_match(&u32);
}