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 <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
21enum {
22	O_U32 = 0,
23};
24
25static const struct xt_option_entry u32_opts[] = {
26	{.name = "u32", .id = O_U32, .type = XTTYPE_STRING,
27	 .flags = XTOPT_MAND | XTOPT_INVERT},
28	XTOPT_TABLEEND,
29};
30
31static 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
43static 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 ... */
89static uint32_t parse_number(const char **s, int pos)
90{
91	unsigned int number;
92	char *end;
93
94	if (!xtables_strtoui(*s, &end, &number, 0, UINT32_MAX) ||
95	    end == *s)
96		xtables_error(PARAMETER_PROBLEM,
97			"u32: at char %d: not a number or out of range", pos);
98	*s = end;
99	return number;
100}
101
102static void u32_parse(struct xt_option_call *cb)
103{
104	struct xt_u32 *data = cb->data;
105	unsigned int testind = 0, locind = 0, valind = 0;
106	struct xt_u32_test *ct = &data->tests[testind]; /* current test */
107	const char *arg = cb->arg; /* the argument string */
108	const char *start = cb->arg;
109	int state = 0;
110
111	xtables_option_parse(cb);
112	data->invert = cb->invert;
113
114	/*
115	 * states:
116	 * 0 = looking for numbers and operations,
117	 * 1 = looking for ranges
118	 */
119	while (1) {
120		/* read next operand/number or range */
121		while (isspace(*arg))
122			++arg;
123
124		if (*arg == '\0') {
125			/* end of argument found */
126			if (state == 0)
127				xtables_error(PARAMETER_PROBLEM,
128					   "u32: abrupt end of input after location specifier");
129			if (valind == 0)
130				xtables_error(PARAMETER_PROBLEM,
131					   "u32: test ended with no value specified");
132
133			ct->nnums    = locind;
134			ct->nvalues  = valind;
135			data->ntests = ++testind;
136
137			if (testind > XT_U32_MAXSIZE)
138				xtables_error(PARAMETER_PROBLEM,
139				           "u32: at char %u: too many \"&&\"s",
140				           (unsigned int)(arg - start));
141			return;
142		}
143
144		if (state == 0) {
145			/*
146			 * reading location: read a number if nothing read yet,
147			 * otherwise either op number or = to end location spec
148			 */
149			if (*arg == '=') {
150				if (locind == 0) {
151					xtables_error(PARAMETER_PROBLEM,
152					           "u32: at char %u: "
153					           "location spec missing",
154					           (unsigned int)(arg - start));
155				} else {
156					++arg;
157					state = 1;
158				}
159			} else {
160				if (locind != 0) {
161					/* need op before number */
162					if (*arg == '&') {
163						ct->location[locind].nextop = XT_U32_AND;
164					} else if (*arg == '<') {
165						if (*++arg != '<')
166							xtables_error(PARAMETER_PROBLEM,
167								   "u32: at char %u: a second '<' was expected", (unsigned int)(arg - start));
168						ct->location[locind].nextop = XT_U32_LEFTSH;
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_RIGHTSH;
174					} else if (*arg == '@') {
175						ct->location[locind].nextop = XT_U32_AT;
176					} else {
177						xtables_error(PARAMETER_PROBLEM,
178							"u32: at char %u: operator expected", (unsigned int)(arg - start));
179					}
180					++arg;
181				}
182				/* now a number; string_to_number skips white space? */
183				ct->location[locind].number =
184					parse_number(&arg, arg - start);
185				if (++locind > XT_U32_MAXSIZE)
186					xtables_error(PARAMETER_PROBLEM,
187						   "u32: at char %u: too many operators", (unsigned int)(arg - start));
188			}
189		} else {
190			/*
191			 * state 1 - reading values: read a range if nothing
192			 * read yet, otherwise either ,range or && to end
193			 * test spec
194			 */
195			if (*arg == '&') {
196				if (*++arg != '&')
197					xtables_error(PARAMETER_PROBLEM,
198						   "u32: at char %u: a second '&' was expected", (unsigned int)(arg - start));
199				if (valind == 0) {
200					xtables_error(PARAMETER_PROBLEM,
201						   "u32: at char %u: value spec missing", (unsigned int)(arg - start));
202				} else {
203					ct->nnums   = locind;
204					ct->nvalues = valind;
205					ct = &data->tests[++testind];
206					if (testind > XT_U32_MAXSIZE)
207						xtables_error(PARAMETER_PROBLEM,
208							   "u32: at char %u: too many \"&&\"s", (unsigned int)(arg - start));
209					++arg;
210					state  = 0;
211					locind = 0;
212					valind = 0;
213				}
214			} else { /* read value range */
215				if (valind > 0) { /* need , before number */
216					if (*arg != ',')
217						xtables_error(PARAMETER_PROBLEM,
218							   "u32: at char %u: expected \",\" or \"&&\"", (unsigned int)(arg - start));
219					++arg;
220				}
221				ct->value[valind].min =
222					parse_number(&arg, arg - start);
223
224				while (isspace(*arg))
225					++arg;
226
227				if (*arg == ':') {
228					++arg;
229					ct->value[valind].max =
230						parse_number(&arg, arg-start);
231				} else {
232					ct->value[valind].max =
233						ct->value[valind].min;
234				}
235
236				if (++valind > XT_U32_MAXSIZE)
237					xtables_error(PARAMETER_PROBLEM,
238						   "u32: at char %u: too many \",\"s", (unsigned int)(arg - start));
239			}
240		}
241	}
242}
243
244static void u32_print(const void *ip, const struct xt_entry_match *match,
245                      int numeric)
246{
247	const struct xt_u32 *data = (const void *)match->data;
248	printf(" u32");
249	if (data->invert)
250		printf(" !");
251	u32_dump(data);
252}
253
254static void u32_save(const void *ip, const struct xt_entry_match *match)
255{
256	const struct xt_u32 *data = (const void *)match->data;
257	if (data->invert)
258		printf(" !");
259	printf(" --u32");
260	u32_dump(data);
261}
262
263static struct xtables_match u32_match = {
264	.name          = "u32",
265	.family        = NFPROTO_UNSPEC,
266	.version       = XTABLES_VERSION,
267	.size          = XT_ALIGN(sizeof(struct xt_u32)),
268	.userspacesize = XT_ALIGN(sizeof(struct xt_u32)),
269	.help          = u32_help,
270	.print         = u32_print,
271	.save          = u32_save,
272	.x6_parse      = u32_parse,
273	.x6_options    = u32_opts,
274};
275
276void _init(void)
277{
278	xtables_register_match(&u32_match);
279}
280