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/ipv4/netfilter/ipt_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#include <stdio.h>
11#include <netdb.h>
12#include <string.h>
13#include <stdlib.h>
14#include <getopt.h>
15#include <iptables.h>
16#include <linux/netfilter_ipv4/ipt_u32.h>
17#include <errno.h>
18#include <ctype.h>
19
20/* Function which prints out usage message. */
21static void
22help(void)
23{
24	printf( "u32 v%s options:\n"
25		" --u32 tests\n"
26		" tests := location = value | tests && location = value\n"
27		" value := range | value , range\n"
28		" range := number | number : number\n"
29		" location := number | location operator number\n"
30		" operator := & | << | >> | @\n"
31		,IPTABLES_VERSION);
32}
33
34/* defined in /usr/include/getopt.h maybe in man getopt */
35static struct option opts[] = {
36	{ "u32", 1, 0, '1' },
37	{ 0 }
38};
39
40/* shared printing code */
41static void print_u32(struct ipt_u32 *data)
42{
43	unsigned int testind;
44
45	for (testind=0; testind < data->ntests; testind++) {
46		if (testind) printf("&&");
47		{
48			unsigned int i;
49
50			printf("0x%x", data->tests[testind].location[0].number);
51			for (i = 1; i < data->tests[testind].nnums; i++) {
52				switch (data->tests[testind].location[i].nextop) {
53				case IPT_U32_AND: printf("&"); break;
54				case IPT_U32_LEFTSH: printf("<<"); break;
55				case IPT_U32_RIGHTSH: printf(">>"); break;
56				case IPT_U32_AT: printf("@"); break;
57				}
58				printf("0x%x", data->tests[testind].location[i].number);
59			}
60			printf("=");
61			for (i = 0; i < data->tests[testind].nvalues; i++) {
62				if (i) printf(",");
63				if (data->tests[testind].value[i].min
64				    == data->tests[testind].value[i].max)
65					printf("0x%x", data->tests[testind].value[i].min);
66				else printf("0x%x:0x%x", data->tests[testind].value[i].min,
67					    data->tests[testind].value[i].max);
68			}
69		}
70	}
71	printf(" ");
72}
73
74/* string_to_number is not quite what we need here ... */
75u_int32_t parse_number(char **s, int pos)
76{
77	u_int32_t number;
78	char *end;
79	errno = 0;
80
81	number = strtoul(*s, &end, 0);
82	if (end == *s)
83		exit_error(PARAMETER_PROBLEM,
84			   "u32: at char %d expected number", pos);
85	if (errno)
86		exit_error(PARAMETER_PROBLEM,
87			   "u32: at char %d error reading number", pos);
88	*s = end;
89	return number;
90}
91
92/* Function which parses command options; returns true if it ate an option */
93static int
94parse(int c, char **argv, int invert, unsigned int *flags,
95      const struct ipt_entry *entry,
96      unsigned int *nfcache,
97      struct ipt_entry_match **match)
98{
99	struct ipt_u32 *data = (struct ipt_u32 *)(*match)->data;
100	char *arg = argv[optind-1]; /* the argument string */
101	char *start = arg;
102	int state=0, testind=0, locind=0, valind=0;
103
104	if (c != '1') return 0;
105	/* states: 0 = looking for numbers and operations, 1 = looking for ranges */
106	while (1) { /* read next operand/number or range */
107		while (isspace(*arg))
108			arg++;  /* skip white space */
109		if (! *arg) { /* end of argument found */
110			if (state == 0)
111				exit_error(PARAMETER_PROBLEM,
112					   "u32: input ended in location spec");
113			if (valind == 0)
114				exit_error(PARAMETER_PROBLEM,
115					   "u32: test ended with no value spec");
116			data->tests[testind].nnums = locind;
117			data->tests[testind].nvalues = valind;
118			testind++;
119			data->ntests=testind;
120			if (testind > U32MAXSIZE)
121				exit_error(PARAMETER_PROBLEM,
122					   "u32: at char %d too many &&'s",
123					   arg-start);
124			/* debugging
125			   print_u32(data);printf("\n");
126			   exit_error(PARAMETER_PROBLEM, "debugging output done"); */
127			return 1;
128		}
129		if (state == 0) {
130			/* reading location: read a number if nothing read yet,
131			   otherwise either op number or = to end location spec */
132			if (*arg == '=') {
133				if (locind == 0)
134					exit_error(PARAMETER_PROBLEM,
135						   "u32: at char %d location spec missing", arg-start);
136				else {
137					arg++;
138					state=1;
139				}
140			}
141			else {
142				if (locind) { /* need op before number */
143					if (*arg == '&') {
144						data->tests[testind].location[locind].nextop = IPT_U32_AND;
145					}
146					else if (*arg == '<') {
147						arg++;
148						if (*arg != '<')
149							exit_error(PARAMETER_PROBLEM,
150								   "u32: at char %d a second < expected", arg-start);
151						data->tests[testind].location[locind].nextop = IPT_U32_LEFTSH;
152					}
153					else if (*arg == '>') {
154						arg++;
155						if (*arg != '>')
156							exit_error(PARAMETER_PROBLEM,
157								   "u32: at char %d a second > expected", arg-start);
158						data->tests[testind].location[locind].nextop = IPT_U32_RIGHTSH;
159					}
160					else if (*arg == '@') {
161						data->tests[testind].location[locind].nextop = IPT_U32_AT;
162					}
163					else exit_error(PARAMETER_PROBLEM,
164							"u32: at char %d operator expected", arg-start);
165					arg++;
166				}
167				/* now a number; string_to_number skips white space? */
168				data->tests[testind].location[locind].number =
169					parse_number(&arg, arg-start);
170				locind++;
171				if (locind > U32MAXSIZE)
172					exit_error(PARAMETER_PROBLEM,
173						   "u32: at char %d too many operators", arg-start);
174			}
175		}
176		else {
177			/* state 1 - reading values: read a range if nothing read yet,
178			   otherwise either ,range or && to end test spec */
179			if (*arg == '&') {
180				arg++;
181				if (*arg != '&')
182					exit_error(PARAMETER_PROBLEM,
183						   "u32: at char %d a second & expected", arg-start);
184				if (valind == 0)
185					exit_error(PARAMETER_PROBLEM,
186						   "u32: at char %d value spec missing", arg-start);
187				else {
188					data->tests[testind].nnums = locind;
189					data->tests[testind].nvalues = valind;
190					testind++;
191					if (testind > U32MAXSIZE)
192						exit_error(PARAMETER_PROBLEM,
193							   "u32: at char %d too many &&'s", arg-start);
194					arg++; state=0; locind=0; valind=0;
195				}
196			}
197			else { /* read value range */
198				if (valind) { /* need , before number */
199					if (*arg != ',')
200						exit_error(PARAMETER_PROBLEM,
201							   "u32: at char %d expected , or &&", arg-start);
202					arg++;
203				}
204				data->tests[testind].value[valind].min = parse_number(&arg, arg-start);
205				while (isspace(*arg))
206					arg++;  /* another place white space could be */
207				if (*arg==':') {
208					arg++;
209					data->tests[testind].value[valind].max
210						= parse_number(&arg, arg-start);
211				}
212				else data->tests[testind].value[valind].max
213					     = data->tests[testind].value[valind].min;
214				valind++;
215				if (valind > U32MAXSIZE)
216					exit_error(PARAMETER_PROBLEM,
217						   "u32: at char %d too many ,'s", arg-start);
218			}
219		}
220	}
221}
222
223/* Final check; must specify something. */
224static void
225final_check(unsigned int flags)
226{
227}
228
229/* Prints out the matchinfo. */
230static void
231print(const struct ipt_ip *ip,
232      const struct ipt_entry_match *match,
233      int numeric)
234{
235	printf("u32 ");
236	print_u32((struct ipt_u32 *)match->data);
237}
238
239/* Saves the union ipt_matchinfo in parsable form to stdout. */
240static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
241{
242	printf("--u32 ");
243	print_u32((struct ipt_u32 *)match->data);
244}
245
246struct iptables_match u32 = {
247	.next		= NULL,
248	.name		= "u32",
249	.version	= IPTABLES_VERSION,
250	.size		= IPT_ALIGN(sizeof(struct ipt_u32)),
251	.userspacesize	= IPT_ALIGN(sizeof(struct ipt_u32)),
252	.help		= &help,
253	.parse		= &parse,
254	.final_check	= &final_check,
255	.print		= &print,
256	.save		= &save,
257	.extra_opts	= opts
258};
259
260void
261_init(void)
262{
263	register_match(&u32);
264}
265