libxt_u32.c revision bbe83862a5e1baf15f7c923352d4afdf59bc70e2
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 <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 match 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}
41
42static 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 ... */
87static 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		xtables_error(PARAMETER_PROBLEM,
96			   "u32: at char %d: expected number", pos);
97	if (errno != 0)
98		xtables_error(PARAMETER_PROBLEM,
99			   "u32: at char %d: error reading number", pos);
100	*s = end;
101	return number;
102}
103
104static 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 = optarg; /* 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				xtables_error(PARAMETER_PROBLEM,
133					   "u32: abrupt end of input after location specifier");
134			if (valind == 0)
135				xtables_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				xtables_error(PARAMETER_PROBLEM,
144				           "u32: at char %u: too many \"&&\"s",
145				           (unsigned int)(arg - start));
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					xtables_error(PARAMETER_PROBLEM,
157					           "u32: at char %u: "
158					           "location spec missing",
159					           (unsigned int)(arg - start));
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							xtables_error(PARAMETER_PROBLEM,
172								   "u32: at char %u: a second '<' was expected", (unsigned int)(arg - start));
173						ct->location[locind].nextop = XT_U32_LEFTSH;
174					} else if (*arg == '>') {
175						if (*++arg != '>')
176							xtables_error(PARAMETER_PROBLEM,
177								   "u32: at char %u: a second '>' was expected", (unsigned int)(arg - start));
178						ct->location[locind].nextop = XT_U32_RIGHTSH;
179					} else if (*arg == '@') {
180						ct->location[locind].nextop = XT_U32_AT;
181					} else {
182						xtables_error(PARAMETER_PROBLEM,
183							"u32: at char %u: operator expected", (unsigned int)(arg - start));
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					xtables_error(PARAMETER_PROBLEM,
192						   "u32: at char %u: too many operators", (unsigned int)(arg - start));
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					xtables_error(PARAMETER_PROBLEM,
203						   "u32: at char %u: a second '&' was expected", (unsigned int)(arg - start));
204				if (valind == 0) {
205					xtables_error(PARAMETER_PROBLEM,
206						   "u32: at char %u: value spec missing", (unsigned int)(arg - start));
207				} else {
208					ct->nnums   = locind;
209					ct->nvalues = valind;
210					ct = &data->tests[++testind];
211					if (testind > XT_U32_MAXSIZE)
212						xtables_error(PARAMETER_PROBLEM,
213							   "u32: at char %u: too many \"&&\"s", (unsigned int)(arg - start));
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						xtables_error(PARAMETER_PROBLEM,
223							   "u32: at char %u: expected \",\" or \"&&\"", (unsigned int)(arg - start));
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					xtables_error(PARAMETER_PROBLEM,
243						   "u32: at char %u: too many \",\"s", (unsigned int)(arg - start));
244			}
245		}
246	}
247}
248
249static 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);
257}
258
259static 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);
266}
267
268static struct xtables_match u32_match = {
269	.name          = "u32",
270	.family        = NFPROTO_UNSPEC,
271	.version       = XTABLES_VERSION,
272	.size          = XT_ALIGN(sizeof(struct xt_u32)),
273	.userspacesize = XT_ALIGN(sizeof(struct xt_u32)),
274	.help          = u32_help,
275	.parse         = u32_parse,
276	.print         = u32_print,
277	.save          = u32_save,
278	.extra_opts    = u32_opts,
279};
280
281void _init(void)
282{
283	xtables_register_match(&u32_match);
284}
285