iptables-1.3.2-20050720
[iptables.git] / extensions / libipt_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/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. */
21 static void
22 help(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 */
35 static struct option opts[] = {
36         { "u32", 1, 0, '1' },
37         { 0 }
38 };
39
40 /* shared printing code */
41 static 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 ... */
75 u_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 */
93 static int
94 parse(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. */
224 static void
225 final_check(unsigned int flags)
226 {
227 }
228
229 /* Prints out the matchinfo. */
230 static void
231 print(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. */
240 static 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
246 struct 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
260 void
261 _init(void)
262 {
263         register_match(&u32);
264 }