Home | History | Annotate | Download | only in extensions
      1 /* Shared library add-on to iptables to add u32 matching,
      2  * generalized matching on values found at packet offsets
      3  *
      4  * Detailed doc is in the kernel module source
      5  * net/netfilter/xt_u32.c
      6  *
      7  * (C) 2002 by Don Cohen <don-netf (at) isis.cs3-inc.com>
      8  * Released under the terms of GNU GPL v2
      9  *
     10  * Copyright  CC Computer Consultants GmbH, 2007
     11  * Contact: <jengelh (at) computergmbh.de>
     12  */
     13 #include <ctype.h>
     14 #include <errno.h>
     15 #include <stdint.h>
     16 #include <stdlib.h>
     17 #include <stdio.h>
     18 #include <xtables.h>
     19 #include <linux/netfilter/xt_u32.h>
     20 
     21 enum {
     22 	O_U32 = 0,
     23 };
     24 
     25 static const struct xt_option_entry u32_opts[] = {
     26 	{.name = "u32", .id = O_U32, .type = XTTYPE_STRING,
     27 	 .flags = XTOPT_MAND},
     28 	XTOPT_TABLEEND,
     29 };
     30 
     31 static void u32_help(void)
     32 {
     33 	printf(
     34 		"u32 match options:\n"
     35 		"[!] --u32 tests\n"
     36 		"\t\t""tests := location \"=\" value | tests \"&&\" location \"=\" value\n"
     37 		"\t\t""value := range | value \",\" range\n"
     38 		"\t\t""range := number | number \":\" number\n"
     39 		"\t\t""location := number | location operator number\n"
     40 		"\t\t""operator := \"&\" | \"<<\" | \">>\" | \"@\"\n");
     41 }
     42 
     43 static void u32_dump(const struct xt_u32 *data)
     44 {
     45 	const struct xt_u32_test *ct;
     46 	unsigned int testind, i;
     47 
     48 	printf(" \"");
     49 	for (testind = 0; testind < data->ntests; ++testind) {
     50 		ct = &data->tests[testind];
     51 
     52 		if (testind > 0)
     53 			printf("&&");
     54 
     55 		printf("0x%x", ct->location[0].number);
     56 		for (i = 1; i < ct->nnums; ++i) {
     57 			switch (ct->location[i].nextop) {
     58 			case XT_U32_AND:
     59 				printf("&");
     60 				break;
     61 			case XT_U32_LEFTSH:
     62 				printf("<<");
     63 				break;
     64 			case XT_U32_RIGHTSH:
     65 				printf(">>");
     66 				break;
     67 			case XT_U32_AT:
     68 				printf("@");
     69 				break;
     70 			}
     71 			printf("0x%x", ct->location[i].number);
     72 		}
     73 
     74 		printf("=");
     75 		for (i = 0; i < ct->nvalues; ++i) {
     76 			if (i > 0)
     77 				printf(",");
     78 			if (ct->value[i].min == ct->value[i].max)
     79 				printf("0x%x", ct->value[i].min);
     80 			else
     81 				printf("0x%x:0x%x", ct->value[i].min,
     82 				       ct->value[i].max);
     83 		}
     84 	}
     85 	putchar('\"');
     86 }
     87 
     88 /* string_to_number() is not quite what we need here ... */
     89 static uint32_t parse_number(const char **s, int pos)
     90 {
     91 	uint32_t number;
     92 	char *end;
     93 
     94 	errno  = 0;
     95 	number = strtoul(*s, &end, 0);
     96 	if (end == *s)
     97 		xtables_error(PARAMETER_PROBLEM,
     98 			   "u32: at char %d: expected number", pos);
     99 	if (errno != 0)
    100 		xtables_error(PARAMETER_PROBLEM,
    101 			   "u32: at char %d: error reading number", pos);
    102 	*s = end;
    103 	return number;
    104 }
    105 
    106 static void u32_parse(struct xt_option_call *cb)
    107 {
    108 	struct xt_u32 *data = cb->data;
    109 	unsigned int testind = 0, locind = 0, valind = 0;
    110 	struct xt_u32_test *ct = &data->tests[testind]; /* current test */
    111 	const char *arg = cb->arg; /* the argument string */
    112 	const char *start = cb->arg;
    113 	int state = 0;
    114 
    115 	xtables_option_parse(cb);
    116 	data->invert = cb->invert;
    117 
    118 	/*
    119 	 * states:
    120 	 * 0 = looking for numbers and operations,
    121 	 * 1 = looking for ranges
    122 	 */
    123 	while (1) {
    124 		/* read next operand/number or range */
    125 		while (isspace(*arg))
    126 			++arg;
    127 
    128 		if (*arg == '\0') {
    129 			/* end of argument found */
    130 			if (state == 0)
    131 				xtables_error(PARAMETER_PROBLEM,
    132 					   "u32: abrupt end of input after location specifier");
    133 			if (valind == 0)
    134 				xtables_error(PARAMETER_PROBLEM,
    135 					   "u32: test ended with no value specified");
    136 
    137 			ct->nnums    = locind;
    138 			ct->nvalues  = valind;
    139 			data->ntests = ++testind;
    140 
    141 			if (testind > XT_U32_MAXSIZE)
    142 				xtables_error(PARAMETER_PROBLEM,
    143 				           "u32: at char %u: too many \"&&\"s",
    144 				           (unsigned int)(arg - start));
    145 			return;
    146 		}
    147 
    148 		if (state == 0) {
    149 			/*
    150 			 * reading location: read a number if nothing read yet,
    151 			 * otherwise either op number or = to end location spec
    152 			 */
    153 			if (*arg == '=') {
    154 				if (locind == 0) {
    155 					xtables_error(PARAMETER_PROBLEM,
    156 					           "u32: at char %u: "
    157 					           "location spec missing",
    158 					           (unsigned int)(arg - start));
    159 				} else {
    160 					++arg;
    161 					state = 1;
    162 				}
    163 			} else {
    164 				if (locind != 0) {
    165 					/* need op before number */
    166 					if (*arg == '&') {
    167 						ct->location[locind].nextop = XT_U32_AND;
    168 					} else if (*arg == '<') {
    169 						if (*++arg != '<')
    170 							xtables_error(PARAMETER_PROBLEM,
    171 								   "u32: at char %u: a second '<' was expected", (unsigned int)(arg - start));
    172 						ct->location[locind].nextop = XT_U32_LEFTSH;
    173 					} else if (*arg == '>') {
    174 						if (*++arg != '>')
    175 							xtables_error(PARAMETER_PROBLEM,
    176 								   "u32: at char %u: a second '>' was expected", (unsigned int)(arg - start));
    177 						ct->location[locind].nextop = XT_U32_RIGHTSH;
    178 					} else if (*arg == '@') {
    179 						ct->location[locind].nextop = XT_U32_AT;
    180 					} else {
    181 						xtables_error(PARAMETER_PROBLEM,
    182 							"u32: at char %u: operator expected", (unsigned int)(arg - start));
    183 					}
    184 					++arg;
    185 				}
    186 				/* now a number; string_to_number skips white space? */
    187 				ct->location[locind].number =
    188 					parse_number(&arg, arg - start);
    189 				if (++locind > XT_U32_MAXSIZE)
    190 					xtables_error(PARAMETER_PROBLEM,
    191 						   "u32: at char %u: too many operators", (unsigned int)(arg - start));
    192 			}
    193 		} else {
    194 			/*
    195 			 * state 1 - reading values: read a range if nothing
    196 			 * read yet, otherwise either ,range or && to end
    197 			 * test spec
    198 			 */
    199 			if (*arg == '&') {
    200 				if (*++arg != '&')
    201 					xtables_error(PARAMETER_PROBLEM,
    202 						   "u32: at char %u: a second '&' was expected", (unsigned int)(arg - start));
    203 				if (valind == 0) {
    204 					xtables_error(PARAMETER_PROBLEM,
    205 						   "u32: at char %u: value spec missing", (unsigned int)(arg - start));
    206 				} else {
    207 					ct->nnums   = locind;
    208 					ct->nvalues = valind;
    209 					ct = &data->tests[++testind];
    210 					if (testind > XT_U32_MAXSIZE)
    211 						xtables_error(PARAMETER_PROBLEM,
    212 							   "u32: at char %u: too many \"&&\"s", (unsigned int)(arg - start));
    213 					++arg;
    214 					state  = 0;
    215 					locind = 0;
    216 					valind = 0;
    217 				}
    218 			} else { /* read value range */
    219 				if (valind > 0) { /* need , before number */
    220 					if (*arg != ',')
    221 						xtables_error(PARAMETER_PROBLEM,
    222 							   "u32: at char %u: expected \",\" or \"&&\"", (unsigned int)(arg - start));
    223 					++arg;
    224 				}
    225 				ct->value[valind].min =
    226 					parse_number(&arg, arg - start);
    227 
    228 				while (isspace(*arg))
    229 					++arg;
    230 
    231 				if (*arg == ':') {
    232 					++arg;
    233 					ct->value[valind].max =
    234 						parse_number(&arg, arg-start);
    235 				} else {
    236 					ct->value[valind].max =
    237 						ct->value[valind].min;
    238 				}
    239 
    240 				if (++valind > XT_U32_MAXSIZE)
    241 					xtables_error(PARAMETER_PROBLEM,
    242 						   "u32: at char %u: too many \",\"s", (unsigned int)(arg - start));
    243 			}
    244 		}
    245 	}
    246 }
    247 
    248 static void u32_print(const void *ip, const struct xt_entry_match *match,
    249                       int numeric)
    250 {
    251 	const struct xt_u32 *data = (const void *)match->data;
    252 	printf(" u32");
    253 	if (data->invert)
    254 		printf(" !");
    255 	u32_dump(data);
    256 }
    257 
    258 static void u32_save(const void *ip, const struct xt_entry_match *match)
    259 {
    260 	const struct xt_u32 *data = (const void *)match->data;
    261 	if (data->invert)
    262 		printf(" !");
    263 	printf(" --u32");
    264 	u32_dump(data);
    265 }
    266 
    267 static struct xtables_match u32_match = {
    268 	.name          = "u32",
    269 	.family        = NFPROTO_UNSPEC,
    270 	.version       = XTABLES_VERSION,
    271 	.size          = XT_ALIGN(sizeof(struct xt_u32)),
    272 	.userspacesize = XT_ALIGN(sizeof(struct xt_u32)),
    273 	.help          = u32_help,
    274 	.print         = u32_print,
    275 	.save          = u32_save,
    276 	.x6_parse      = u32_parse,
    277 	.x6_options    = u32_opts,
    278 };
    279 
    280 void _init(void)
    281 {
    282 	xtables_register_match(&u32_match);
    283 }
    284