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