xref: /aosp_15_r20/external/iptables/extensions/libxt_u32.c (revision a71a954618bbadd4a345637e5edcf36eec826889)
1*a71a9546SAutomerger Merge Worker /* Shared library add-on to iptables to add u32 matching,
2*a71a9546SAutomerger Merge Worker  * generalized matching on values found at packet offsets
3*a71a9546SAutomerger Merge Worker  *
4*a71a9546SAutomerger Merge Worker  * Detailed doc is in the kernel module source
5*a71a9546SAutomerger Merge Worker  * net/netfilter/xt_u32.c
6*a71a9546SAutomerger Merge Worker  *
7*a71a9546SAutomerger Merge Worker  * (C) 2002 by Don Cohen <[email protected]>
8*a71a9546SAutomerger Merge Worker  * Released under the terms of GNU GPL v2
9*a71a9546SAutomerger Merge Worker  *
10*a71a9546SAutomerger Merge Worker  * Copyright © CC Computer Consultants GmbH, 2007
11*a71a9546SAutomerger Merge Worker  * Contact: <[email protected]>
12*a71a9546SAutomerger Merge Worker  */
13*a71a9546SAutomerger Merge Worker #include <ctype.h>
14*a71a9546SAutomerger Merge Worker #include <errno.h>
15*a71a9546SAutomerger Merge Worker #include <stdint.h>
16*a71a9546SAutomerger Merge Worker #include <stdlib.h>
17*a71a9546SAutomerger Merge Worker #include <stdio.h>
18*a71a9546SAutomerger Merge Worker #include <xtables.h>
19*a71a9546SAutomerger Merge Worker #include <linux/netfilter/xt_u32.h>
20*a71a9546SAutomerger Merge Worker 
21*a71a9546SAutomerger Merge Worker enum {
22*a71a9546SAutomerger Merge Worker 	O_U32 = 0,
23*a71a9546SAutomerger Merge Worker };
24*a71a9546SAutomerger Merge Worker 
25*a71a9546SAutomerger Merge Worker static const struct xt_option_entry u32_opts[] = {
26*a71a9546SAutomerger Merge Worker 	{.name = "u32", .id = O_U32, .type = XTTYPE_STRING,
27*a71a9546SAutomerger Merge Worker 	 .flags = XTOPT_MAND | XTOPT_INVERT},
28*a71a9546SAutomerger Merge Worker 	XTOPT_TABLEEND,
29*a71a9546SAutomerger Merge Worker };
30*a71a9546SAutomerger Merge Worker 
u32_help(void)31*a71a9546SAutomerger Merge Worker static void u32_help(void)
32*a71a9546SAutomerger Merge Worker {
33*a71a9546SAutomerger Merge Worker 	printf(
34*a71a9546SAutomerger Merge Worker 		"u32 match options:\n"
35*a71a9546SAutomerger Merge Worker 		"[!] --u32 tests\n"
36*a71a9546SAutomerger Merge Worker 		"\t\t""tests := location \"=\" value | tests \"&&\" location \"=\" value\n"
37*a71a9546SAutomerger Merge Worker 		"\t\t""value := range | value \",\" range\n"
38*a71a9546SAutomerger Merge Worker 		"\t\t""range := number | number \":\" number\n"
39*a71a9546SAutomerger Merge Worker 		"\t\t""location := number | location operator number\n"
40*a71a9546SAutomerger Merge Worker 		"\t\t""operator := \"&\" | \"<<\" | \">>\" | \"@\"\n");
41*a71a9546SAutomerger Merge Worker }
42*a71a9546SAutomerger Merge Worker 
u32_dump(const struct xt_u32 * data)43*a71a9546SAutomerger Merge Worker static void u32_dump(const struct xt_u32 *data)
44*a71a9546SAutomerger Merge Worker {
45*a71a9546SAutomerger Merge Worker 	const struct xt_u32_test *ct;
46*a71a9546SAutomerger Merge Worker 	unsigned int testind, i;
47*a71a9546SAutomerger Merge Worker 
48*a71a9546SAutomerger Merge Worker 	printf(" \"");
49*a71a9546SAutomerger Merge Worker 	for (testind = 0; testind < data->ntests; ++testind) {
50*a71a9546SAutomerger Merge Worker 		ct = &data->tests[testind];
51*a71a9546SAutomerger Merge Worker 
52*a71a9546SAutomerger Merge Worker 		if (testind > 0)
53*a71a9546SAutomerger Merge Worker 			printf("&&");
54*a71a9546SAutomerger Merge Worker 
55*a71a9546SAutomerger Merge Worker 		printf("0x%x", ct->location[0].number);
56*a71a9546SAutomerger Merge Worker 		for (i = 1; i < ct->nnums; ++i) {
57*a71a9546SAutomerger Merge Worker 			switch (ct->location[i].nextop) {
58*a71a9546SAutomerger Merge Worker 			case XT_U32_AND:
59*a71a9546SAutomerger Merge Worker 				printf("&");
60*a71a9546SAutomerger Merge Worker 				break;
61*a71a9546SAutomerger Merge Worker 			case XT_U32_LEFTSH:
62*a71a9546SAutomerger Merge Worker 				printf("<<");
63*a71a9546SAutomerger Merge Worker 				break;
64*a71a9546SAutomerger Merge Worker 			case XT_U32_RIGHTSH:
65*a71a9546SAutomerger Merge Worker 				printf(">>");
66*a71a9546SAutomerger Merge Worker 				break;
67*a71a9546SAutomerger Merge Worker 			case XT_U32_AT:
68*a71a9546SAutomerger Merge Worker 				printf("@");
69*a71a9546SAutomerger Merge Worker 				break;
70*a71a9546SAutomerger Merge Worker 			}
71*a71a9546SAutomerger Merge Worker 			printf("0x%x", ct->location[i].number);
72*a71a9546SAutomerger Merge Worker 		}
73*a71a9546SAutomerger Merge Worker 
74*a71a9546SAutomerger Merge Worker 		printf("=");
75*a71a9546SAutomerger Merge Worker 		for (i = 0; i < ct->nvalues; ++i) {
76*a71a9546SAutomerger Merge Worker 			if (i > 0)
77*a71a9546SAutomerger Merge Worker 				printf(",");
78*a71a9546SAutomerger Merge Worker 			if (ct->value[i].min == ct->value[i].max)
79*a71a9546SAutomerger Merge Worker 				printf("0x%x", ct->value[i].min);
80*a71a9546SAutomerger Merge Worker 			else
81*a71a9546SAutomerger Merge Worker 				printf("0x%x:0x%x", ct->value[i].min,
82*a71a9546SAutomerger Merge Worker 				       ct->value[i].max);
83*a71a9546SAutomerger Merge Worker 		}
84*a71a9546SAutomerger Merge Worker 	}
85*a71a9546SAutomerger Merge Worker 	putchar('\"');
86*a71a9546SAutomerger Merge Worker }
87*a71a9546SAutomerger Merge Worker 
88*a71a9546SAutomerger Merge Worker /* string_to_number() is not quite what we need here ... */
parse_number(const char ** s,int pos)89*a71a9546SAutomerger Merge Worker static uint32_t parse_number(const char **s, int pos)
90*a71a9546SAutomerger Merge Worker {
91*a71a9546SAutomerger Merge Worker 	unsigned int number;
92*a71a9546SAutomerger Merge Worker 	char *end;
93*a71a9546SAutomerger Merge Worker 
94*a71a9546SAutomerger Merge Worker 	if (!xtables_strtoui(*s, &end, &number, 0, UINT32_MAX) ||
95*a71a9546SAutomerger Merge Worker 	    end == *s)
96*a71a9546SAutomerger Merge Worker 		xtables_error(PARAMETER_PROBLEM,
97*a71a9546SAutomerger Merge Worker 			"u32: at char %d: not a number or out of range", pos);
98*a71a9546SAutomerger Merge Worker 	*s = end;
99*a71a9546SAutomerger Merge Worker 	return number;
100*a71a9546SAutomerger Merge Worker }
101*a71a9546SAutomerger Merge Worker 
u32_parse(struct xt_option_call * cb)102*a71a9546SAutomerger Merge Worker static void u32_parse(struct xt_option_call *cb)
103*a71a9546SAutomerger Merge Worker {
104*a71a9546SAutomerger Merge Worker 	struct xt_u32 *data = cb->data;
105*a71a9546SAutomerger Merge Worker 	unsigned int testind = 0, locind = 0, valind = 0;
106*a71a9546SAutomerger Merge Worker 	struct xt_u32_test *ct = &data->tests[testind]; /* current test */
107*a71a9546SAutomerger Merge Worker 	const char *arg = cb->arg; /* the argument string */
108*a71a9546SAutomerger Merge Worker 	const char *start = cb->arg;
109*a71a9546SAutomerger Merge Worker 	int state = 0;
110*a71a9546SAutomerger Merge Worker 
111*a71a9546SAutomerger Merge Worker 	xtables_option_parse(cb);
112*a71a9546SAutomerger Merge Worker 	data->invert = cb->invert;
113*a71a9546SAutomerger Merge Worker 
114*a71a9546SAutomerger Merge Worker 	/*
115*a71a9546SAutomerger Merge Worker 	 * states:
116*a71a9546SAutomerger Merge Worker 	 * 0 = looking for numbers and operations,
117*a71a9546SAutomerger Merge Worker 	 * 1 = looking for ranges
118*a71a9546SAutomerger Merge Worker 	 */
119*a71a9546SAutomerger Merge Worker 	while (1) {
120*a71a9546SAutomerger Merge Worker 		/* read next operand/number or range */
121*a71a9546SAutomerger Merge Worker 		while (isspace(*arg))
122*a71a9546SAutomerger Merge Worker 			++arg;
123*a71a9546SAutomerger Merge Worker 
124*a71a9546SAutomerger Merge Worker 		if (*arg == '\0') {
125*a71a9546SAutomerger Merge Worker 			/* end of argument found */
126*a71a9546SAutomerger Merge Worker 			if (state == 0)
127*a71a9546SAutomerger Merge Worker 				xtables_error(PARAMETER_PROBLEM,
128*a71a9546SAutomerger Merge Worker 					   "u32: abrupt end of input after location specifier");
129*a71a9546SAutomerger Merge Worker 			if (valind == 0)
130*a71a9546SAutomerger Merge Worker 				xtables_error(PARAMETER_PROBLEM,
131*a71a9546SAutomerger Merge Worker 					   "u32: test ended with no value specified");
132*a71a9546SAutomerger Merge Worker 
133*a71a9546SAutomerger Merge Worker 			ct->nnums    = locind;
134*a71a9546SAutomerger Merge Worker 			ct->nvalues  = valind;
135*a71a9546SAutomerger Merge Worker 			data->ntests = ++testind;
136*a71a9546SAutomerger Merge Worker 
137*a71a9546SAutomerger Merge Worker 			if (testind > XT_U32_MAXSIZE)
138*a71a9546SAutomerger Merge Worker 				xtables_error(PARAMETER_PROBLEM,
139*a71a9546SAutomerger Merge Worker 				           "u32: at char %u: too many \"&&\"s",
140*a71a9546SAutomerger Merge Worker 				           (unsigned int)(arg - start));
141*a71a9546SAutomerger Merge Worker 			return;
142*a71a9546SAutomerger Merge Worker 		}
143*a71a9546SAutomerger Merge Worker 
144*a71a9546SAutomerger Merge Worker 		if (state == 0) {
145*a71a9546SAutomerger Merge Worker 			/*
146*a71a9546SAutomerger Merge Worker 			 * reading location: read a number if nothing read yet,
147*a71a9546SAutomerger Merge Worker 			 * otherwise either op number or = to end location spec
148*a71a9546SAutomerger Merge Worker 			 */
149*a71a9546SAutomerger Merge Worker 			if (*arg == '=') {
150*a71a9546SAutomerger Merge Worker 				if (locind == 0) {
151*a71a9546SAutomerger Merge Worker 					xtables_error(PARAMETER_PROBLEM,
152*a71a9546SAutomerger Merge Worker 					           "u32: at char %u: "
153*a71a9546SAutomerger Merge Worker 					           "location spec missing",
154*a71a9546SAutomerger Merge Worker 					           (unsigned int)(arg - start));
155*a71a9546SAutomerger Merge Worker 				} else {
156*a71a9546SAutomerger Merge Worker 					++arg;
157*a71a9546SAutomerger Merge Worker 					state = 1;
158*a71a9546SAutomerger Merge Worker 				}
159*a71a9546SAutomerger Merge Worker 			} else {
160*a71a9546SAutomerger Merge Worker 				if (locind != 0) {
161*a71a9546SAutomerger Merge Worker 					/* need op before number */
162*a71a9546SAutomerger Merge Worker 					if (*arg == '&') {
163*a71a9546SAutomerger Merge Worker 						ct->location[locind].nextop = XT_U32_AND;
164*a71a9546SAutomerger Merge Worker 					} else if (*arg == '<') {
165*a71a9546SAutomerger Merge Worker 						if (*++arg != '<')
166*a71a9546SAutomerger Merge Worker 							xtables_error(PARAMETER_PROBLEM,
167*a71a9546SAutomerger Merge Worker 								   "u32: at char %u: a second '<' was expected", (unsigned int)(arg - start));
168*a71a9546SAutomerger Merge Worker 						ct->location[locind].nextop = XT_U32_LEFTSH;
169*a71a9546SAutomerger Merge Worker 					} else if (*arg == '>') {
170*a71a9546SAutomerger Merge Worker 						if (*++arg != '>')
171*a71a9546SAutomerger Merge Worker 							xtables_error(PARAMETER_PROBLEM,
172*a71a9546SAutomerger Merge Worker 								   "u32: at char %u: a second '>' was expected", (unsigned int)(arg - start));
173*a71a9546SAutomerger Merge Worker 						ct->location[locind].nextop = XT_U32_RIGHTSH;
174*a71a9546SAutomerger Merge Worker 					} else if (*arg == '@') {
175*a71a9546SAutomerger Merge Worker 						ct->location[locind].nextop = XT_U32_AT;
176*a71a9546SAutomerger Merge Worker 					} else {
177*a71a9546SAutomerger Merge Worker 						xtables_error(PARAMETER_PROBLEM,
178*a71a9546SAutomerger Merge Worker 							"u32: at char %u: operator expected", (unsigned int)(arg - start));
179*a71a9546SAutomerger Merge Worker 					}
180*a71a9546SAutomerger Merge Worker 					++arg;
181*a71a9546SAutomerger Merge Worker 				}
182*a71a9546SAutomerger Merge Worker 				/* now a number; string_to_number skips white space? */
183*a71a9546SAutomerger Merge Worker 				ct->location[locind].number =
184*a71a9546SAutomerger Merge Worker 					parse_number(&arg, arg - start);
185*a71a9546SAutomerger Merge Worker 				if (++locind > XT_U32_MAXSIZE)
186*a71a9546SAutomerger Merge Worker 					xtables_error(PARAMETER_PROBLEM,
187*a71a9546SAutomerger Merge Worker 						   "u32: at char %u: too many operators", (unsigned int)(arg - start));
188*a71a9546SAutomerger Merge Worker 			}
189*a71a9546SAutomerger Merge Worker 		} else {
190*a71a9546SAutomerger Merge Worker 			/*
191*a71a9546SAutomerger Merge Worker 			 * state 1 - reading values: read a range if nothing
192*a71a9546SAutomerger Merge Worker 			 * read yet, otherwise either ,range or && to end
193*a71a9546SAutomerger Merge Worker 			 * test spec
194*a71a9546SAutomerger Merge Worker 			 */
195*a71a9546SAutomerger Merge Worker 			if (*arg == '&') {
196*a71a9546SAutomerger Merge Worker 				if (*++arg != '&')
197*a71a9546SAutomerger Merge Worker 					xtables_error(PARAMETER_PROBLEM,
198*a71a9546SAutomerger Merge Worker 						   "u32: at char %u: a second '&' was expected", (unsigned int)(arg - start));
199*a71a9546SAutomerger Merge Worker 				if (valind == 0) {
200*a71a9546SAutomerger Merge Worker 					xtables_error(PARAMETER_PROBLEM,
201*a71a9546SAutomerger Merge Worker 						   "u32: at char %u: value spec missing", (unsigned int)(arg - start));
202*a71a9546SAutomerger Merge Worker 				} else {
203*a71a9546SAutomerger Merge Worker 					ct->nnums   = locind;
204*a71a9546SAutomerger Merge Worker 					ct->nvalues = valind;
205*a71a9546SAutomerger Merge Worker 					ct = &data->tests[++testind];
206*a71a9546SAutomerger Merge Worker 					if (testind > XT_U32_MAXSIZE)
207*a71a9546SAutomerger Merge Worker 						xtables_error(PARAMETER_PROBLEM,
208*a71a9546SAutomerger Merge Worker 							   "u32: at char %u: too many \"&&\"s", (unsigned int)(arg - start));
209*a71a9546SAutomerger Merge Worker 					++arg;
210*a71a9546SAutomerger Merge Worker 					state  = 0;
211*a71a9546SAutomerger Merge Worker 					locind = 0;
212*a71a9546SAutomerger Merge Worker 					valind = 0;
213*a71a9546SAutomerger Merge Worker 				}
214*a71a9546SAutomerger Merge Worker 			} else { /* read value range */
215*a71a9546SAutomerger Merge Worker 				if (valind > 0) { /* need , before number */
216*a71a9546SAutomerger Merge Worker 					if (*arg != ',')
217*a71a9546SAutomerger Merge Worker 						xtables_error(PARAMETER_PROBLEM,
218*a71a9546SAutomerger Merge Worker 							   "u32: at char %u: expected \",\" or \"&&\"", (unsigned int)(arg - start));
219*a71a9546SAutomerger Merge Worker 					++arg;
220*a71a9546SAutomerger Merge Worker 				}
221*a71a9546SAutomerger Merge Worker 				ct->value[valind].min =
222*a71a9546SAutomerger Merge Worker 					parse_number(&arg, arg - start);
223*a71a9546SAutomerger Merge Worker 
224*a71a9546SAutomerger Merge Worker 				while (isspace(*arg))
225*a71a9546SAutomerger Merge Worker 					++arg;
226*a71a9546SAutomerger Merge Worker 
227*a71a9546SAutomerger Merge Worker 				if (*arg == ':') {
228*a71a9546SAutomerger Merge Worker 					++arg;
229*a71a9546SAutomerger Merge Worker 					ct->value[valind].max =
230*a71a9546SAutomerger Merge Worker 						parse_number(&arg, arg-start);
231*a71a9546SAutomerger Merge Worker 				} else {
232*a71a9546SAutomerger Merge Worker 					ct->value[valind].max =
233*a71a9546SAutomerger Merge Worker 						ct->value[valind].min;
234*a71a9546SAutomerger Merge Worker 				}
235*a71a9546SAutomerger Merge Worker 
236*a71a9546SAutomerger Merge Worker 				if (++valind > XT_U32_MAXSIZE)
237*a71a9546SAutomerger Merge Worker 					xtables_error(PARAMETER_PROBLEM,
238*a71a9546SAutomerger Merge Worker 						   "u32: at char %u: too many \",\"s", (unsigned int)(arg - start));
239*a71a9546SAutomerger Merge Worker 			}
240*a71a9546SAutomerger Merge Worker 		}
241*a71a9546SAutomerger Merge Worker 	}
242*a71a9546SAutomerger Merge Worker }
243*a71a9546SAutomerger Merge Worker 
u32_print(const void * ip,const struct xt_entry_match * match,int numeric)244*a71a9546SAutomerger Merge Worker static void u32_print(const void *ip, const struct xt_entry_match *match,
245*a71a9546SAutomerger Merge Worker                       int numeric)
246*a71a9546SAutomerger Merge Worker {
247*a71a9546SAutomerger Merge Worker 	const struct xt_u32 *data = (const void *)match->data;
248*a71a9546SAutomerger Merge Worker 	printf(" u32");
249*a71a9546SAutomerger Merge Worker 	if (data->invert)
250*a71a9546SAutomerger Merge Worker 		printf(" !");
251*a71a9546SAutomerger Merge Worker 	u32_dump(data);
252*a71a9546SAutomerger Merge Worker }
253*a71a9546SAutomerger Merge Worker 
u32_save(const void * ip,const struct xt_entry_match * match)254*a71a9546SAutomerger Merge Worker static void u32_save(const void *ip, const struct xt_entry_match *match)
255*a71a9546SAutomerger Merge Worker {
256*a71a9546SAutomerger Merge Worker 	const struct xt_u32 *data = (const void *)match->data;
257*a71a9546SAutomerger Merge Worker 	if (data->invert)
258*a71a9546SAutomerger Merge Worker 		printf(" !");
259*a71a9546SAutomerger Merge Worker 	printf(" --u32");
260*a71a9546SAutomerger Merge Worker 	u32_dump(data);
261*a71a9546SAutomerger Merge Worker }
262*a71a9546SAutomerger Merge Worker 
263*a71a9546SAutomerger Merge Worker static struct xtables_match u32_match = {
264*a71a9546SAutomerger Merge Worker 	.name          = "u32",
265*a71a9546SAutomerger Merge Worker 	.family        = NFPROTO_UNSPEC,
266*a71a9546SAutomerger Merge Worker 	.version       = XTABLES_VERSION,
267*a71a9546SAutomerger Merge Worker 	.size          = XT_ALIGN(sizeof(struct xt_u32)),
268*a71a9546SAutomerger Merge Worker 	.userspacesize = XT_ALIGN(sizeof(struct xt_u32)),
269*a71a9546SAutomerger Merge Worker 	.help          = u32_help,
270*a71a9546SAutomerger Merge Worker 	.print         = u32_print,
271*a71a9546SAutomerger Merge Worker 	.save          = u32_save,
272*a71a9546SAutomerger Merge Worker 	.x6_parse      = u32_parse,
273*a71a9546SAutomerger Merge Worker 	.x6_options    = u32_opts,
274*a71a9546SAutomerger Merge Worker };
275*a71a9546SAutomerger Merge Worker 
_init(void)276*a71a9546SAutomerger Merge Worker void _init(void)
277*a71a9546SAutomerger Merge Worker {
278*a71a9546SAutomerger Merge Worker 	xtables_register_match(&u32_match);
279*a71a9546SAutomerger Merge Worker }
280