libxt_u32.c revision 23545c2a7a31c68c1e49c7c901b632c2f1c59968
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>
8 * Released under the terms of GNU GPL v2
9 *
10 * Copyright © CC Computer Consultants GmbH, 2007
11 * Contact: <jengelh@computergmbh.de>
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>
23#include "../include/linux/netfilter/xt_u32.h"
24
25static const struct option u32_opts[] = {
26	{"u32", 1, NULL, 'u'},
27	{ .name = NULL }
28};
29
30static void u32_help(void)
31{
32	printf(
33		"u32 v%s options:\n"
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"
39		"\t\t""operator := \"&\" | \"<<\" | \">>\" | \"@\"\n",
40		IPTABLES_VERSION);
41	return;
42}
43
44static void u32_dump(const struct xt_u32 *data)
45{
46	const struct xt_u32_test *ct;
47	unsigned int testind, i;
48
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	printf(" ");
86}
87
88/* string_to_number() is not quite what we need here ... */
89static u_int32_t parse_number(char **s, int pos)
90{
91	u_int32_t number;
92	char *end;
93
94	errno  = 0;
95	number = strtoul(*s, &end, 0);
96	if (end == *s)
97		exit_error(PARAMETER_PROBLEM,
98			   "u32: at char %d: expected number", pos);
99	if (errno != 0)
100		exit_error(PARAMETER_PROBLEM,
101			   "u32: at char %d: error reading number", pos);
102	*s = end;
103	return number;
104}
105
106/* Function which parses command options; returns true if it ate an option */
107static int u32_parse(int c, char **argv, int invert, unsigned int *flags,
108		     const void *entry, struct xt_entry_match **match)
109{
110	struct xt_u32 *data = (void *)(*match)->data;
111	unsigned int testind = 0, locind = 0, valind = 0;
112	struct xt_u32_test *ct = &data->tests[testind]; /* current test */
113	char *arg = argv[optind-1]; /* the argument string */
114	char *start = arg;
115	int state = 0;
116
117	if (c != 'u')
118		return 0;
119
120	data->invert = invert;
121
122	/*
123	 * states:
124	 * 0 = looking for numbers and operations,
125	 * 1 = looking for ranges
126	 */
127	while (1) {
128		/* read next operand/number or range */
129		while (isspace(*arg))
130			++arg;
131
132		if (*arg == '\0') {
133			/* end of argument found */
134			if (state == 0)
135				exit_error(PARAMETER_PROBLEM,
136					   "u32: abrupt end of input after location specifier");
137			if (valind == 0)
138				exit_error(PARAMETER_PROBLEM,
139					   "u32: test ended with no value specified");
140
141			ct->nnums    = locind;
142			ct->nvalues  = valind;
143			data->ntests = ++testind;
144
145			if (testind > XT_U32_MAXSIZE)
146				exit_error(PARAMETER_PROBLEM,
147				           "u32: at char %u: too many \"&&\"s",
148				           (unsigned int)(arg - start));
149			return 1;
150		}
151
152		if (state == 0) {
153			/*
154			 * reading location: read a number if nothing read yet,
155			 * otherwise either op number or = to end location spec
156			 */
157			if (*arg == '=') {
158				if (locind == 0) {
159					exit_error(PARAMETER_PROBLEM,
160					           "u32: at char %u: "
161					           "location spec missing",
162					           (unsigned int)(arg - start));
163				} else {
164					++arg;
165					state = 1;
166				}
167			} else {
168				if (locind != 0) {
169					/* need op before number */
170					if (*arg == '&') {
171						ct->location[locind].nextop = XT_U32_AND;
172					} else if (*arg == '<') {
173						if (*++arg != '<')
174							exit_error(PARAMETER_PROBLEM,
175								   "u32: at char %u: a second '<' was expected", (unsigned int)(arg - start));
176						ct->location[locind].nextop = XT_U32_LEFTSH;
177					} else if (*arg == '>') {
178						if (*++arg != '>')
179							exit_error(PARAMETER_PROBLEM,
180								   "u32: at char %u: a second '>' was expected", (unsigned int)(arg - start));
181						ct->location[locind].nextop = XT_U32_RIGHTSH;
182					} else if (*arg == '@') {
183						ct->location[locind].nextop = XT_U32_AT;
184					} else {
185						exit_error(PARAMETER_PROBLEM,
186							"u32: at char %u: operator expected", (unsigned int)(arg - start));
187					}
188					++arg;
189				}
190				/* now a number; string_to_number skips white space? */
191				ct->location[locind].number =
192					parse_number(&arg, arg - start);
193				if (++locind > XT_U32_MAXSIZE)
194					exit_error(PARAMETER_PROBLEM,
195						   "u32: at char %u: too many operators", (unsigned int)(arg - start));
196			}
197		} else {
198			/*
199			 * state 1 - reading values: read a range if nothing
200			 * read yet, otherwise either ,range or && to end
201			 * test spec
202			 */
203			if (*arg == '&') {
204				if (*++arg != '&')
205					exit_error(PARAMETER_PROBLEM,
206						   "u32: at char %u: a second '&' was expected", (unsigned int)(arg - start));
207				if (valind == 0) {
208					exit_error(PARAMETER_PROBLEM,
209						   "u32: at char %u: value spec missing", (unsigned int)(arg - start));
210				} else {
211					ct->nnums   = locind;
212					ct->nvalues = valind;
213					ct = &data->tests[++testind];
214					if (testind > XT_U32_MAXSIZE)
215						exit_error(PARAMETER_PROBLEM,
216							   "u32: at char %u: too many \"&&\"s", (unsigned int)(arg - start));
217					++arg;
218					state  = 0;
219					locind = 0;
220					valind = 0;
221				}
222			} else { /* read value range */
223				if (valind > 0) { /* need , before number */
224					if (*arg != ',')
225						exit_error(PARAMETER_PROBLEM,
226							   "u32: at char %u: expected \",\" or \"&&\"", (unsigned int)(arg - start));
227					++arg;
228				}
229				ct->value[valind].min =
230					parse_number(&arg, arg - start);
231
232				while (isspace(*arg))
233					++arg;
234
235				if (*arg == ':') {
236					++arg;
237					ct->value[valind].max =
238						parse_number(&arg, arg-start);
239				} else {
240					ct->value[valind].max =
241						ct->value[valind].min;
242				}
243
244				if (++valind > XT_U32_MAXSIZE)
245					exit_error(PARAMETER_PROBLEM,
246						   "u32: at char %u: too many \",\"s", (unsigned int)(arg - start));
247			}
248		}
249	}
250}
251
252static void u32_print(const void *ip, const struct xt_entry_match *match,
253                      int numeric)
254{
255	const struct xt_u32 *data = (const void *)match->data;
256	printf("u32 ");
257	if (data->invert)
258		printf("! ");
259	u32_dump(data);
260	return;
261}
262
263static void u32_save(const void *ip, const struct xt_entry_match *match)
264{
265	const struct xt_u32 *data = (const void *)match->data;
266	if (data->invert)
267		printf("! ");
268	printf("--u32 ");
269	u32_dump(data);
270	return;
271}
272
273static struct xtables_match u32_match = {
274	.name          = "u32",
275	.family        = AF_UNSPEC,
276	.version       = IPTABLES_VERSION,
277	.size          = XT_ALIGN(sizeof(struct xt_u32)),
278	.userspacesize = XT_ALIGN(sizeof(struct xt_u32)),
279	.help          = u32_help,
280	.parse         = u32_parse,
281	.print         = u32_print,
282	.save          = u32_save,
283	.extra_opts    = u32_opts,
284};
285
286void _init(void)
287{
288	xtables_register_match(&u32_match);
289}
290