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