fix for f12, gcc4.4
[iptables.git] / extensions / libipt_conntrack.c
1 /* Shared library add-on to iptables for conntrack matching support.
2  * GPL (C) 2001  Marc Boucher (marc@mbsi.ca).
3  */
4
5 #include <stdio.h>
6 #include <netdb.h>
7 #include <string.h>
8 #include <stdlib.h>
9 #include <getopt.h>
10 #include <ctype.h>
11 #include <iptables.h>
12 #include <linux/netfilter/nf_conntrack_common.h>
13 /* For 64bit kernel / 32bit userspace */
14 #include "../include/linux/netfilter_ipv4/ipt_conntrack.h"
15
16 #ifndef IPT_CONNTRACK_STATE_UNTRACKED
17 #define IPT_CONNTRACK_STATE_UNTRACKED (1 << (IP_CT_NUMBER + 3))
18 #endif
19
20 /* Function which prints out usage message. */
21 static void
22 help(void)
23 {
24         printf(
25 "conntrack match v%s options:\n"
26 " [!] --ctstate [INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED|SNAT|DNAT][,...]\n"
27 "                               State(s) to match\n"
28 " [!] --ctproto proto           Protocol to match; by number or name, eg. `tcp'\n"
29 "     --ctorigsrc  [!] address[/mask]\n"
30 "                               Original source specification\n"
31 "     --ctorigdst  [!] address[/mask]\n"
32 "                               Original destination specification\n"
33 "     --ctreplsrc  [!] address[/mask]\n"
34 "                               Reply source specification\n"
35 "     --ctrepldst  [!] address[/mask]\n"
36 "                               Reply destination specification\n"
37 " [!] --ctstatus [NONE|EXPECTED|SEEN_REPLY|ASSURED|CONFIRMED][,...]\n"
38 "                               Status(es) to match\n"
39 " [!] --ctexpire time[:time]    Match remaining lifetime in seconds against\n"
40 "                               value or range of values (inclusive)\n"
41 "\n", IPTABLES_VERSION);
42 }
43
44
45
46 static struct option opts[] = {
47         { "ctstate", 1, 0, '1' },
48         { "ctproto", 1, 0, '2' },
49         { "ctorigsrc", 1, 0, '3' },
50         { "ctorigdst", 1, 0, '4' },
51         { "ctreplsrc", 1, 0, '5' },
52         { "ctrepldst", 1, 0, '6' },
53         { "ctstatus", 1, 0, '7' },
54         { "ctexpire", 1, 0, '8' },
55         {0}
56 };
57
58 static int
59 parse_state(const char *state, size_t strlen, struct ipt_conntrack_info *sinfo)
60 {
61         if (strncasecmp(state, "INVALID", strlen) == 0)
62                 sinfo->statemask |= IPT_CONNTRACK_STATE_INVALID;
63         else if (strncasecmp(state, "NEW", strlen) == 0)
64                 sinfo->statemask |= IPT_CONNTRACK_STATE_BIT(IP_CT_NEW);
65         else if (strncasecmp(state, "ESTABLISHED", strlen) == 0)
66                 sinfo->statemask |= IPT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
67         else if (strncasecmp(state, "RELATED", strlen) == 0)
68                 sinfo->statemask |= IPT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
69         else if (strncasecmp(state, "UNTRACKED", strlen) == 0)
70                 sinfo->statemask |= IPT_CONNTRACK_STATE_UNTRACKED;
71         else if (strncasecmp(state, "SNAT", strlen) == 0)
72                 sinfo->statemask |= IPT_CONNTRACK_STATE_SNAT;
73         else if (strncasecmp(state, "DNAT", strlen) == 0)
74                 sinfo->statemask |= IPT_CONNTRACK_STATE_DNAT;
75         else
76                 return 0;
77         return 1;
78 }
79
80 static void
81 parse_states(const char *arg, struct ipt_conntrack_info *sinfo)
82 {
83         const char *comma;
84
85         while ((comma = strchr(arg, ',')) != NULL) {
86                 if (comma == arg || !parse_state(arg, comma-arg, sinfo))
87                         exit_error(PARAMETER_PROBLEM, "Bad ctstate `%s'", arg);
88                 arg = comma+1;
89         }
90
91         if (strlen(arg) == 0 || !parse_state(arg, strlen(arg), sinfo))
92                 exit_error(PARAMETER_PROBLEM, "Bad ctstate `%s'", arg);
93 }
94
95 static int
96 parse_status(const char *status, size_t strlen, struct ipt_conntrack_info *sinfo)
97 {
98         if (strncasecmp(status, "NONE", strlen) == 0)
99                 sinfo->statusmask |= 0;
100         else if (strncasecmp(status, "EXPECTED", strlen) == 0)
101                 sinfo->statusmask |= IPS_EXPECTED;
102         else if (strncasecmp(status, "SEEN_REPLY", strlen) == 0)
103                 sinfo->statusmask |= IPS_SEEN_REPLY;
104         else if (strncasecmp(status, "ASSURED", strlen) == 0)
105                 sinfo->statusmask |= IPS_ASSURED;
106 #ifdef IPS_CONFIRMED
107         else if (strncasecmp(status, "CONFIRMED", strlen) == 0)
108                 sinfo->stausmask |= IPS_CONFIRMED;
109 #endif
110         else
111                 return 0;
112         return 1;
113 }
114
115 static void
116 parse_statuses(const char *arg, struct ipt_conntrack_info *sinfo)
117 {
118         const char *comma;
119
120         while ((comma = strchr(arg, ',')) != NULL) {
121                 if (comma == arg || !parse_status(arg, comma-arg, sinfo))
122                         exit_error(PARAMETER_PROBLEM, "Bad ctstatus `%s'", arg);
123                 arg = comma+1;
124         }
125
126         if (strlen(arg) == 0 || !parse_status(arg, strlen(arg), sinfo))
127                 exit_error(PARAMETER_PROBLEM, "Bad ctstatus `%s'", arg);
128 }
129
130 #ifdef KERNEL_64_USERSPACE_32
131 static unsigned long long
132 parse_expire(const char *s)
133 {
134         unsigned long long len;
135         
136         if (string_to_number_ll(s, 0, 0, &len) == -1)
137                 exit_error(PARAMETER_PROBLEM, "expire value invalid: `%s'\n", s);
138         else
139                 return len;
140 }
141 #else
142 static unsigned long
143 parse_expire(const char *s)
144 {
145         unsigned int len;
146         
147         if (string_to_number(s, 0, 0, &len) == -1)
148                 exit_error(PARAMETER_PROBLEM, "expire value invalid: `%s'\n", s);
149         else
150                 return len;
151 }
152 #endif
153
154 /* If a single value is provided, min and max are both set to the value */
155 static void
156 parse_expires(const char *s, struct ipt_conntrack_info *sinfo)
157 {
158         char *buffer;
159         char *cp;
160
161         buffer = strdup(s);
162         if ((cp = strchr(buffer, ':')) == NULL)
163                 sinfo->expires_min = sinfo->expires_max = parse_expire(buffer);
164         else {
165                 *cp = '\0';
166                 cp++;
167
168                 sinfo->expires_min = buffer[0] ? parse_expire(buffer) : 0;
169                 sinfo->expires_max = cp[0] ? parse_expire(cp) : -1;
170         }
171         free(buffer);
172         
173         if (sinfo->expires_min > sinfo->expires_max)
174                 exit_error(PARAMETER_PROBLEM,
175 #ifdef KERNEL_64_USERSPACE_32
176                            "expire min. range value `%llu' greater than max. "
177                            "range value `%llu'", sinfo->expires_min, sinfo->expires_max);
178 #else
179                            "expire min. range value `%lu' greater than max. "
180                            "range value `%lu'", sinfo->expires_min, sinfo->expires_max);
181 #endif
182 }
183
184 /* Function which parses command options; returns true if it
185    ate an option */
186 static int
187 parse(int c, char **argv, int invert, unsigned int *flags,
188       const struct ipt_entry *entry,
189       unsigned int *nfcache,
190       struct ipt_entry_match **match)
191 {
192         struct ipt_conntrack_info *sinfo = (struct ipt_conntrack_info *)(*match)->data;
193         char *protocol = NULL;
194         unsigned int naddrs = 0;
195         struct in_addr *addrs = NULL;
196
197
198         switch (c) {
199         case '1':
200                 check_inverse(optarg, &invert, &optind, 0);
201
202                 parse_states(argv[optind-1], sinfo);
203                 if (invert) {
204                         sinfo->invflags |= IPT_CONNTRACK_STATE;
205                 }
206                 sinfo->flags |= IPT_CONNTRACK_STATE;
207                 break;
208
209         case '2':
210                 check_inverse(optarg, &invert, &optind, 0);
211
212                 if(invert)
213                         sinfo->invflags |= IPT_CONNTRACK_PROTO;
214
215                 /* Canonicalize into lower case */
216                 for (protocol = argv[optind-1]; *protocol; protocol++)
217                         *protocol = tolower(*protocol);
218
219                 protocol = argv[optind-1];
220                 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = parse_protocol(protocol);
221
222                 if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0
223                     && (sinfo->invflags & IPT_INV_PROTO))
224                         exit_error(PARAMETER_PROBLEM,
225                                    "rule would never match protocol");
226
227                 sinfo->flags |= IPT_CONNTRACK_PROTO;
228                 break;
229
230         case '3':
231                 check_inverse(optarg, &invert, &optind, 9);
232
233                 if (invert)
234                         sinfo->invflags |= IPT_CONNTRACK_ORIGSRC;
235
236                 parse_hostnetworkmask(argv[optind-1], &addrs,
237                                         &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
238                                         &naddrs);
239                 if(naddrs > 1)
240                         exit_error(PARAMETER_PROBLEM,
241                                 "multiple IP addresses not allowed");
242
243                 if(naddrs == 1) {
244                         sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = addrs[0].s_addr;
245                 }
246
247                 sinfo->flags |= IPT_CONNTRACK_ORIGSRC;
248                 break;
249
250         case '4':
251                 check_inverse(optarg, &invert, &optind, 0);
252
253                 if (invert)
254                         sinfo->invflags |= IPT_CONNTRACK_ORIGDST;
255
256                 parse_hostnetworkmask(argv[optind-1], &addrs,
257                                         &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
258                                         &naddrs);
259                 if(naddrs > 1)
260                         exit_error(PARAMETER_PROBLEM,
261                                 "multiple IP addresses not allowed");
262
263                 if(naddrs == 1) {
264                         sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = addrs[0].s_addr;
265                 }
266
267                 sinfo->flags |= IPT_CONNTRACK_ORIGDST;
268                 break;
269
270         case '5':
271                 check_inverse(optarg, &invert, &optind, 0);
272
273                 if (invert)
274                         sinfo->invflags |= IPT_CONNTRACK_REPLSRC;
275
276                 parse_hostnetworkmask(argv[optind-1], &addrs,
277                                         &sinfo->sipmsk[IP_CT_DIR_REPLY],
278                                         &naddrs);
279                 if(naddrs > 1)
280                         exit_error(PARAMETER_PROBLEM,
281                                 "multiple IP addresses not allowed");
282
283                 if(naddrs == 1) {
284                         sinfo->tuple[IP_CT_DIR_REPLY].src.ip = addrs[0].s_addr;
285                 }
286
287                 sinfo->flags |= IPT_CONNTRACK_REPLSRC;
288                 break;
289
290         case '6':
291                 check_inverse(optarg, &invert, &optind, 0);
292
293                 if (invert)
294                         sinfo->invflags |= IPT_CONNTRACK_REPLDST;
295
296                 parse_hostnetworkmask(argv[optind-1], &addrs,
297                                         &sinfo->dipmsk[IP_CT_DIR_REPLY],
298                                         &naddrs);
299                 if(naddrs > 1)
300                         exit_error(PARAMETER_PROBLEM,
301                                 "multiple IP addresses not allowed");
302
303                 if(naddrs == 1) {
304                         sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = addrs[0].s_addr;
305                 }
306
307                 sinfo->flags |= IPT_CONNTRACK_REPLDST;
308                 break;
309
310         case '7':
311                 check_inverse(optarg, &invert, &optind, 0);
312
313                 parse_statuses(argv[optind-1], sinfo);
314                 if (invert) {
315                         sinfo->invflags |= IPT_CONNTRACK_STATUS;
316                 }
317                 sinfo->flags |= IPT_CONNTRACK_STATUS;
318                 break;
319
320         case '8':
321                 check_inverse(optarg, &invert, &optind, 0);
322
323                 parse_expires(argv[optind-1], sinfo);
324                 if (invert) {
325                         sinfo->invflags |= IPT_CONNTRACK_EXPIRES;
326                 }
327                 sinfo->flags |= IPT_CONNTRACK_EXPIRES;
328                 break;
329
330         default:
331                 return 0;
332         }
333
334         *flags = sinfo->flags;
335         return 1;
336 }
337
338 static void
339 final_check(unsigned int flags)
340 {
341         if (!flags)
342                 exit_error(PARAMETER_PROBLEM, "You must specify one or more options");
343 }
344
345 static void
346 print_state(unsigned int statemask)
347 {
348         const char *sep = "";
349
350         if (statemask & IPT_CONNTRACK_STATE_INVALID) {
351                 printf("%sINVALID", sep);
352                 sep = ",";
353         }
354         if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
355                 printf("%sNEW", sep);
356                 sep = ",";
357         }
358         if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
359                 printf("%sRELATED", sep);
360                 sep = ",";
361         }
362         if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
363                 printf("%sESTABLISHED", sep);
364                 sep = ",";
365         }
366         if (statemask & IPT_CONNTRACK_STATE_UNTRACKED) {
367                 printf("%sUNTRACKED", sep);
368                 sep = ",";
369         }
370         if (statemask & IPT_CONNTRACK_STATE_SNAT) {
371                 printf("%sSNAT", sep);
372                 sep = ",";
373         }
374         if (statemask & IPT_CONNTRACK_STATE_DNAT) {
375                 printf("%sDNAT", sep);
376                 sep = ",";
377         }
378         printf(" ");
379 }
380
381 static void
382 print_status(unsigned int statusmask)
383 {
384         const char *sep = "";
385
386         if (statusmask & IPS_EXPECTED) {
387                 printf("%sEXPECTED", sep);
388                 sep = ",";
389         }
390         if (statusmask & IPS_SEEN_REPLY) {
391                 printf("%sSEEN_REPLY", sep);
392                 sep = ",";
393         }
394         if (statusmask & IPS_ASSURED) {
395                 printf("%sASSURED", sep);
396                 sep = ",";
397         }
398 #ifdef IPS_CONFIRMED
399         if (statusmask & IPS_CONFIRMED) {
400                 printf("%sCONFIRMED", sep);
401                 sep =",";
402         }
403 #endif
404         if (statusmask == 0) {
405                 printf("%sNONE", sep);
406                 sep = ",";
407         }
408         printf(" ");
409 }
410
411 static void
412 print_addr(struct in_addr *addr, struct in_addr *mask, int inv, int numeric)
413 {
414         char buf[BUFSIZ];
415
416         if (inv) 
417                 printf("! ");
418
419         if (mask->s_addr == 0L && !numeric)
420                 printf("%s ", "anywhere");
421         else {
422                 if (numeric)
423                         sprintf(buf, "%s", addr_to_dotted(addr));
424                 else
425                         sprintf(buf, "%s", addr_to_anyname(addr));
426                 strcat(buf, mask_to_dotted(mask));
427                 printf("%s ", buf);
428         }
429 }
430
431 /* Saves the matchinfo in parsable form to stdout. */
432 static void
433 matchinfo_print(const struct ipt_ip *ip, const struct ipt_entry_match *match, int numeric, const char *optpfx)
434 {
435         struct ipt_conntrack_info *sinfo = (struct ipt_conntrack_info *)match->data;
436
437         if(sinfo->flags & IPT_CONNTRACK_STATE) {
438                 printf("%sctstate ", optpfx);
439                 if (sinfo->invflags & IPT_CONNTRACK_STATE)
440                         printf("! ");
441                 print_state(sinfo->statemask);
442         }
443
444         if(sinfo->flags & IPT_CONNTRACK_PROTO) {
445                 printf("%sctproto ", optpfx);
446                 if (sinfo->invflags & IPT_CONNTRACK_PROTO)
447                         printf("! ");
448                 printf("%u ", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum);
449         }
450
451         if(sinfo->flags & IPT_CONNTRACK_ORIGSRC) {
452                 printf("%sctorigsrc ", optpfx);
453
454                 print_addr(
455                     (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
456                     &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
457                     sinfo->invflags & IPT_CONNTRACK_ORIGSRC,
458                     numeric);
459         }
460
461         if(sinfo->flags & IPT_CONNTRACK_ORIGDST) {
462                 printf("%sctorigdst ", optpfx);
463
464                 print_addr(
465                     (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
466                     &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
467                     sinfo->invflags & IPT_CONNTRACK_ORIGDST,
468                     numeric);
469         }
470
471         if(sinfo->flags & IPT_CONNTRACK_REPLSRC) {
472                 printf("%sctreplsrc ", optpfx);
473
474                 print_addr(
475                     (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
476                     &sinfo->sipmsk[IP_CT_DIR_REPLY],
477                     sinfo->invflags & IPT_CONNTRACK_REPLSRC,
478                     numeric);
479         }
480
481         if(sinfo->flags & IPT_CONNTRACK_REPLDST) {
482                 printf("%sctrepldst ", optpfx);
483
484                 print_addr(
485                     (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
486                     &sinfo->dipmsk[IP_CT_DIR_REPLY],
487                     sinfo->invflags & IPT_CONNTRACK_REPLDST,
488                     numeric);
489         }
490
491         if(sinfo->flags & IPT_CONNTRACK_STATUS) {
492                 printf("%sctstatus ", optpfx);
493                 if (sinfo->invflags & IPT_CONNTRACK_STATUS)
494                         printf("! ");
495                 print_status(sinfo->statusmask);
496         }
497
498         if(sinfo->flags & IPT_CONNTRACK_EXPIRES) {
499                 printf("%sctexpire ", optpfx);
500                 if (sinfo->invflags & IPT_CONNTRACK_EXPIRES)
501                         printf("! ");
502
503 #ifdef KERNEL_64_USERSPACE_32
504                 if (sinfo->expires_max == sinfo->expires_min)
505                         printf("%llu ", sinfo->expires_min);
506                 else
507                         printf("%llu:%llu ", sinfo->expires_min, sinfo->expires_max);
508 #else
509                 if (sinfo->expires_max == sinfo->expires_min)
510                         printf("%lu ", sinfo->expires_min);
511                 else
512                         printf("%lu:%lu ", sinfo->expires_min, sinfo->expires_max);
513 #endif
514         }
515 }
516
517 /* Prints out the matchinfo. */
518 static void
519 print(const struct ipt_ip *ip,
520       const struct ipt_entry_match *match,
521       int numeric)
522 {
523         matchinfo_print(ip, match, numeric, "");
524 }
525
526 /* Saves the matchinfo in parsable form to stdout. */
527 static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
528 {
529         matchinfo_print(ip, match, 1, "--");
530 }
531
532 static struct iptables_match conntrack = { 
533         .next           = NULL,
534         .name           = "conntrack",
535         .version        = IPTABLES_VERSION,
536         .size           = IPT_ALIGN(sizeof(struct ipt_conntrack_info)),
537         .userspacesize  = IPT_ALIGN(sizeof(struct ipt_conntrack_info)),
538         .help           = &help,
539         .parse          = &parse,
540         .final_check    = &final_check,
541         .print          = &print,
542         .save           = &save,
543         .extra_opts     = opts
544 };
545
546 void _init(void)
547 {
548         register_match(&conntrack);
549 }