changing trunk/trunk to trunk
[iptables.git] / extensions / libxt_u32.c
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 <sys/types.h>
14 #include <ctype.h>
15 #include <errno.h>
16 #include <getopt.h>
17 #include <netdb.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
21
22 #include <xtables.h>
23 #include "../include/linux/netfilter/xt_u32.h"
24
25 static const struct option u32_opts[] = {
26         {"u32", 1, NULL, 'u'},
27         { .name = NULL }
28 };
29
30 static void u32_help(void)
31 {
32         printf(
33                 "u32 match options:\n"
34                 "[!] --u32 tests\n"
35                 "\t\t""tests := location \"=\" value | tests \"&&\" location \"=\" value\n"
36                 "\t\t""value := range | value \",\" range\n"
37                 "\t\t""range := number | number \":\" number\n"
38                 "\t\t""location := number | location operator number\n"
39                 "\t\t""operator := \"&\" | \"<<\" | \">>\" | \"@\"\n");
40 }
41
42 static 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 ... */
87 static 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 */
105 static 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
250 static 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
261 static 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
271 static struct xtables_match u32_match = {
272         .name          = "u32",
273         .family        = AF_UNSPEC,
274         .version       = XTABLES_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
284 void _init(void)
285 {
286         xtables_register_match(&u32_match);
287 }