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