Sapan says vnet_tun is obsolete.
[iptables.git] / extensions / libxt_conntrack.c
1 /*
2  *      libxt_conntrack
3  *      Shared library add-on to iptables for conntrack matching support.
4  *
5  *      GPL (C) 2001  Marc Boucher (marc@mbsi.ca).
6  *      Copyright © CC Computer Consultants GmbH, 2007 - 2008
7  *      Jan Engelhardt <jengelh@computergmbh.de>
8  */
9 #include <sys/socket.h>
10 #include <sys/types.h>
11 #include <ctype.h>
12 #include <getopt.h>
13 #include <netdb.h>
14 #include <stdbool.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <iptables.h>
19 #include <xtables.h>
20 #include <linux/netfilter.h>
21 #include <linux/netfilter/xt_conntrack.h>
22 #include <linux/netfilter/nf_conntrack_common.h>
23 #include <arpa/inet.h>
24
25 /* Function which prints out usage message. */
26 static void conntrack_mt_help(void)
27 {
28         printf(
29 "conntrack match options:\n"
30 "[!] --ctstate {INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED|SNAT|DNAT}[,...]\n"
31 "                               State(s) to match\n"
32 "[!] --ctproto proto            Protocol to match; by number or name, e.g. \"tcp\"\n"
33 "[!] --ctorigsrc address[/mask]\n"
34 "[!] --ctorigdst address[/mask]\n"
35 "[!] --ctreplsrc address[/mask]\n"
36 "[!] --ctrepldst address[/mask]\n"
37 "                               Original/Reply source/destination address\n"
38 "[!] --ctorigsrcport port\n"
39 "[!] --ctorigdstport port\n"
40 "[!] --ctreplsrcport port\n"
41 "[!] --ctrepldstport port\n"
42 "                               TCP/UDP/SCTP orig./reply source/destination port\n"
43 "[!] --ctstatus {NONE|EXPECTED|SEEN_REPLY|ASSURED|CONFIRMED}[,...]\n"
44 "                               Status(es) to match\n"
45 "[!] --ctexpire time[:time]     Match remaining lifetime in seconds against\n"
46 "                               value or range of values (inclusive)\n"
47 "    --ctdir {ORIGINAL|REPLY}   Flow direction of packet\n");
48 }
49
50 static const struct option conntrack_mt_opts_v0[] = {
51         {.name = "ctstate",   .has_arg = true, .val = '1'},
52         {.name = "ctproto",   .has_arg = true, .val = '2'},
53         {.name = "ctorigsrc", .has_arg = true, .val = '3'},
54         {.name = "ctorigdst", .has_arg = true, .val = '4'},
55         {.name = "ctreplsrc", .has_arg = true, .val = '5'},
56         {.name = "ctrepldst", .has_arg = true, .val = '6'},
57         {.name = "ctstatus",  .has_arg = true, .val = '7'},
58         {.name = "ctexpire",  .has_arg = true, .val = '8'},
59         { .name = NULL }
60 };
61
62 static const struct option conntrack_mt_opts[] = {
63         {.name = "ctstate",       .has_arg = true, .val = '1'},
64         {.name = "ctproto",       .has_arg = true, .val = '2'},
65         {.name = "ctorigsrc",     .has_arg = true, .val = '3'},
66         {.name = "ctorigdst",     .has_arg = true, .val = '4'},
67         {.name = "ctreplsrc",     .has_arg = true, .val = '5'},
68         {.name = "ctrepldst",     .has_arg = true, .val = '6'},
69         {.name = "ctstatus",      .has_arg = true, .val = '7'},
70         {.name = "ctexpire",      .has_arg = true, .val = '8'},
71         {.name = "ctorigsrcport", .has_arg = true, .val = 'a'},
72         {.name = "ctorigdstport", .has_arg = true, .val = 'b'},
73         {.name = "ctreplsrcport", .has_arg = true, .val = 'c'},
74         {.name = "ctrepldstport", .has_arg = true, .val = 'd'},
75         {.name = "ctdir",         .has_arg = true, .val = 'e'},
76         {.name = NULL},
77 };
78
79 static int
80 parse_state(const char *state, size_t len, struct xt_conntrack_info *sinfo)
81 {
82         if (strncasecmp(state, "INVALID", len) == 0)
83                 sinfo->statemask |= XT_CONNTRACK_STATE_INVALID;
84         else if (strncasecmp(state, "NEW", len) == 0)
85                 sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_NEW);
86         else if (strncasecmp(state, "ESTABLISHED", len) == 0)
87                 sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
88         else if (strncasecmp(state, "RELATED", len) == 0)
89                 sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
90         else if (strncasecmp(state, "UNTRACKED", len) == 0)
91                 sinfo->statemask |= XT_CONNTRACK_STATE_UNTRACKED;
92         else if (strncasecmp(state, "SNAT", len) == 0)
93                 sinfo->statemask |= XT_CONNTRACK_STATE_SNAT;
94         else if (strncasecmp(state, "DNAT", len) == 0)
95                 sinfo->statemask |= XT_CONNTRACK_STATE_DNAT;
96         else
97                 return 0;
98         return 1;
99 }
100
101 static void
102 parse_states(const char *arg, struct xt_conntrack_info *sinfo)
103 {
104         const char *comma;
105
106         while ((comma = strchr(arg, ',')) != NULL) {
107                 if (comma == arg || !parse_state(arg, comma-arg, sinfo))
108                         exit_error(PARAMETER_PROBLEM, "Bad ctstate `%s'", arg);
109                 arg = comma+1;
110         }
111
112         if (strlen(arg) == 0 || !parse_state(arg, strlen(arg), sinfo))
113                 exit_error(PARAMETER_PROBLEM, "Bad ctstate `%s'", arg);
114 }
115
116 static bool
117 conntrack_ps_state(struct xt_conntrack_mtinfo1 *info, const char *state,
118                    size_t z)
119 {
120         if (strncasecmp(state, "INVALID", z) == 0)
121                 info->state_mask |= XT_CONNTRACK_STATE_INVALID;
122         else if (strncasecmp(state, "NEW", z) == 0)
123                 info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_NEW);
124         else if (strncasecmp(state, "ESTABLISHED", z) == 0)
125                 info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
126         else if (strncasecmp(state, "RELATED", z) == 0)
127                 info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
128         else if (strncasecmp(state, "UNTRACKED", z) == 0)
129                 info->state_mask |= XT_CONNTRACK_STATE_UNTRACKED;
130         else if (strncasecmp(state, "SNAT", z) == 0)
131                 info->state_mask |= XT_CONNTRACK_STATE_SNAT;
132         else if (strncasecmp(state, "DNAT", z) == 0)
133                 info->state_mask |= XT_CONNTRACK_STATE_DNAT;
134         else
135                 return false;
136         return true;
137 }
138
139 static void
140 conntrack_ps_states(struct xt_conntrack_mtinfo1 *info, const char *arg)
141 {
142         const char *comma;
143
144         while ((comma = strchr(arg, ',')) != NULL) {
145                 if (comma == arg || !conntrack_ps_state(info, arg, comma - arg))
146                         exit_error(PARAMETER_PROBLEM,
147                                    "Bad ctstate \"%s\"", arg);
148                 arg = comma + 1;
149         }
150
151         if (strlen(arg) == 0 || !conntrack_ps_state(info, arg, strlen(arg)))
152                 exit_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg);
153 }
154
155 static int
156 parse_status(const char *status, size_t len, struct xt_conntrack_info *sinfo)
157 {
158         if (strncasecmp(status, "NONE", len) == 0)
159                 sinfo->statusmask |= 0;
160         else if (strncasecmp(status, "EXPECTED", len) == 0)
161                 sinfo->statusmask |= IPS_EXPECTED;
162         else if (strncasecmp(status, "SEEN_REPLY", len) == 0)
163                 sinfo->statusmask |= IPS_SEEN_REPLY;
164         else if (strncasecmp(status, "ASSURED", len) == 0)
165                 sinfo->statusmask |= IPS_ASSURED;
166 #ifdef IPS_CONFIRMED
167         else if (strncasecmp(status, "CONFIRMED", len) == 0)
168                 sinfo->statusmask |= IPS_CONFIRMED;
169 #endif
170         else
171                 return 0;
172         return 1;
173 }
174
175 static void
176 parse_statuses(const char *arg, struct xt_conntrack_info *sinfo)
177 {
178         const char *comma;
179
180         while ((comma = strchr(arg, ',')) != NULL) {
181                 if (comma == arg || !parse_status(arg, comma-arg, sinfo))
182                         exit_error(PARAMETER_PROBLEM, "Bad ctstatus `%s'", arg);
183                 arg = comma+1;
184         }
185
186         if (strlen(arg) == 0 || !parse_status(arg, strlen(arg), sinfo))
187                 exit_error(PARAMETER_PROBLEM, "Bad ctstatus `%s'", arg);
188 }
189
190 static bool
191 conntrack_ps_status(struct xt_conntrack_mtinfo1 *info, const char *status,
192                     size_t z)
193 {
194         if (strncasecmp(status, "NONE", z) == 0)
195                 info->status_mask |= 0;
196         else if (strncasecmp(status, "EXPECTED", z) == 0)
197                 info->status_mask |= IPS_EXPECTED;
198         else if (strncasecmp(status, "SEEN_REPLY", z) == 0)
199                 info->status_mask |= IPS_SEEN_REPLY;
200         else if (strncasecmp(status, "ASSURED", z) == 0)
201                 info->status_mask |= IPS_ASSURED;
202         else if (strncasecmp(status, "CONFIRMED", z) == 0)
203                 info->status_mask |= IPS_CONFIRMED;
204         else
205                 return false;
206         return true;
207 }
208
209 static void
210 conntrack_ps_statuses(struct xt_conntrack_mtinfo1 *info, const char *arg)
211 {
212         const char *comma;
213
214         while ((comma = strchr(arg, ',')) != NULL) {
215                 if (comma == arg || !conntrack_ps_status(info, arg, comma - arg))
216                         exit_error(PARAMETER_PROBLEM,
217                                    "Bad ctstatus \"%s\"", arg);
218                 arg = comma + 1;
219         }
220
221         if (strlen(arg) == 0 || !conntrack_ps_status(info, arg, strlen(arg)))
222                 exit_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg);
223 }
224
225 static unsigned long
226 parse_expire(const char *s)
227 {
228         unsigned int len;
229
230         if (string_to_number(s, 0, 0, &len) == -1)
231                 exit_error(PARAMETER_PROBLEM, "expire value invalid: `%s'\n", s);
232         else
233                 return len;
234 }
235
236 /* If a single value is provided, min and max are both set to the value */
237 static void
238 parse_expires(const char *s, struct xt_conntrack_info *sinfo)
239 {
240         char *buffer;
241         char *cp;
242
243         buffer = strdup(s);
244         if ((cp = strchr(buffer, ':')) == NULL)
245                 sinfo->expires_min = sinfo->expires_max =
246                         parse_expire(buffer);
247         else {
248                 *cp = '\0';
249                 cp++;
250
251                 sinfo->expires_min = buffer[0] ? parse_expire(buffer) : 0;
252                 sinfo->expires_max = cp[0]
253                         ? parse_expire(cp)
254                         : (unsigned long)-1;
255         }
256         free(buffer);
257
258         if (sinfo->expires_min > sinfo->expires_max)
259                 exit_error(PARAMETER_PROBLEM,
260                            "expire min. range value `%lu' greater than max. "
261                            "range value `%lu'", sinfo->expires_min, sinfo->expires_max);
262 }
263
264 static void
265 conntrack_ps_expires(struct xt_conntrack_mtinfo1 *info, const char *s)
266 {
267         unsigned int min, max;
268         char *end;
269
270         if (!strtonum(s, &end, &min, 0, ~0))
271                 param_act(P_BAD_VALUE, "conntrack", "--expires", s);
272         max = min;
273         if (*end == ':')
274                 if (!strtonum(s, &end, &max, 0, ~0U))
275                         param_act(P_BAD_VALUE, "conntrack", "--expires", s);
276         if (*end != '\0')
277                 param_act(P_BAD_VALUE, "conntrack", "--expires", s);
278
279         if (min > max)
280                 exit_error(PARAMETER_PROBLEM,
281                            "expire min. range value \"%u\" greater than max. "
282                            "range value \"%u\"", min, max);
283
284         info->expires_min = min;
285         info->expires_max = max;
286 }
287
288 /* Function which parses command options; returns true if it
289    ate an option */
290 static int conntrack_parse(int c, char **argv, int invert, unsigned int *flags,
291                            const void *entry, struct xt_entry_match **match)
292 {
293         struct xt_conntrack_info *sinfo = (void *)(*match)->data;
294         char *protocol = NULL;
295         unsigned int naddrs = 0;
296         struct in_addr *addrs = NULL;
297
298
299         switch (c) {
300         case '1':
301                 check_inverse(optarg, &invert, &optind, 0);
302
303                 parse_states(argv[optind-1], sinfo);
304                 if (invert) {
305                         sinfo->invflags |= XT_CONNTRACK_STATE;
306                 }
307                 sinfo->flags |= XT_CONNTRACK_STATE;
308                 break;
309
310         case '2':
311                 check_inverse(optarg, &invert, &optind, 0);
312
313                 if(invert)
314                         sinfo->invflags |= XT_CONNTRACK_PROTO;
315
316                 /* Canonicalize into lower case */
317                 for (protocol = argv[optind-1]; *protocol; protocol++)
318                         *protocol = tolower(*protocol);
319
320                 protocol = argv[optind-1];
321                 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = parse_protocol(protocol);
322
323                 if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0
324                     && (sinfo->invflags & XT_INV_PROTO))
325                         exit_error(PARAMETER_PROBLEM,
326                                    "rule would never match protocol");
327
328                 sinfo->flags |= XT_CONNTRACK_PROTO;
329                 break;
330
331         case '3':
332                 check_inverse(optarg, &invert, &optind, 0);
333
334                 if (invert)
335                         sinfo->invflags |= XT_CONNTRACK_ORIGSRC;
336
337                 ipparse_hostnetworkmask(argv[optind-1], &addrs,
338                                         &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
339                                         &naddrs);
340                 if(naddrs > 1)
341                         exit_error(PARAMETER_PROBLEM,
342                                 "multiple IP addresses not allowed");
343
344                 if(naddrs == 1) {
345                         sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = addrs[0].s_addr;
346                 }
347
348                 sinfo->flags |= XT_CONNTRACK_ORIGSRC;
349                 break;
350
351         case '4':
352                 check_inverse(optarg, &invert, &optind, 0);
353
354                 if (invert)
355                         sinfo->invflags |= XT_CONNTRACK_ORIGDST;
356
357                 ipparse_hostnetworkmask(argv[optind-1], &addrs,
358                                         &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
359                                         &naddrs);
360                 if(naddrs > 1)
361                         exit_error(PARAMETER_PROBLEM,
362                                 "multiple IP addresses not allowed");
363
364                 if(naddrs == 1) {
365                         sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = addrs[0].s_addr;
366                 }
367
368                 sinfo->flags |= XT_CONNTRACK_ORIGDST;
369                 break;
370
371         case '5':
372                 check_inverse(optarg, &invert, &optind, 0);
373
374                 if (invert)
375                         sinfo->invflags |= XT_CONNTRACK_REPLSRC;
376
377                 ipparse_hostnetworkmask(argv[optind-1], &addrs,
378                                         &sinfo->sipmsk[IP_CT_DIR_REPLY],
379                                         &naddrs);
380                 if(naddrs > 1)
381                         exit_error(PARAMETER_PROBLEM,
382                                 "multiple IP addresses not allowed");
383
384                 if(naddrs == 1) {
385                         sinfo->tuple[IP_CT_DIR_REPLY].src.ip = addrs[0].s_addr;
386                 }
387
388                 sinfo->flags |= XT_CONNTRACK_REPLSRC;
389                 break;
390
391         case '6':
392                 check_inverse(optarg, &invert, &optind, 0);
393
394                 if (invert)
395                         sinfo->invflags |= XT_CONNTRACK_REPLDST;
396
397                 ipparse_hostnetworkmask(argv[optind-1], &addrs,
398                                         &sinfo->dipmsk[IP_CT_DIR_REPLY],
399                                         &naddrs);
400                 if(naddrs > 1)
401                         exit_error(PARAMETER_PROBLEM,
402                                 "multiple IP addresses not allowed");
403
404                 if(naddrs == 1) {
405                         sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = addrs[0].s_addr;
406                 }
407
408                 sinfo->flags |= XT_CONNTRACK_REPLDST;
409                 break;
410
411         case '7':
412                 check_inverse(optarg, &invert, &optind, 0);
413
414                 parse_statuses(argv[optind-1], sinfo);
415                 if (invert) {
416                         sinfo->invflags |= XT_CONNTRACK_STATUS;
417                 }
418                 sinfo->flags |= XT_CONNTRACK_STATUS;
419                 break;
420
421         case '8':
422                 check_inverse(optarg, &invert, &optind, 0);
423
424                 parse_expires(argv[optind-1], sinfo);
425                 if (invert) {
426                         sinfo->invflags |= XT_CONNTRACK_EXPIRES;
427                 }
428                 sinfo->flags |= XT_CONNTRACK_EXPIRES;
429                 break;
430
431         default:
432                 return 0;
433         }
434
435         *flags = sinfo->flags;
436         return 1;
437 }
438
439 static int
440 conntrack_mt_parse(int c, char **argv, int invert, unsigned int *flags,
441                    struct xt_entry_match **match)
442 {
443         struct xt_conntrack_mtinfo1 *info = (void *)(*match)->data;
444         unsigned int port;
445         char *p;
446
447         switch (c) {
448         case '1': /* --ctstate */
449                 conntrack_ps_states(info, optarg);
450                 info->match_flags |= XT_CONNTRACK_STATE;
451                 if (invert)
452                         info->invert_flags |= XT_CONNTRACK_STATE;
453                 break;
454
455         case '2': /* --ctproto */
456                 /* Canonicalize into lower case */
457                 for (p = optarg; *p != '\0'; ++p)
458                         *p = tolower(*p);
459                 info->l4proto = parse_protocol(optarg);
460
461                 if (info->l4proto == 0 && (info->invert_flags & XT_INV_PROTO))
462                         exit_error(PARAMETER_PROBLEM, "conntrack: rule would "
463                                    "never match protocol");
464
465                 info->match_flags |= XT_CONNTRACK_PROTO;
466                 if (invert)
467                         info->invert_flags |= XT_CONNTRACK_PROTO;
468                 break;
469
470         case '7': /* --ctstatus */
471                 conntrack_ps_statuses(info, optarg);
472                 info->match_flags |= XT_CONNTRACK_STATUS;
473                 if (invert)
474                         info->invert_flags |= XT_CONNTRACK_STATUS;
475                 break;
476
477         case '8': /* --ctexpire */
478                 conntrack_ps_expires(info, optarg);
479                 info->match_flags |= XT_CONNTRACK_EXPIRES;
480                 if (invert)
481                         info->invert_flags |= XT_CONNTRACK_EXPIRES;
482                 break;
483
484         case 'a': /* --ctorigsrcport */
485                 if (!strtonum(optarg, NULL, &port, 0, ~(u_int16_t)0))
486                         param_act(P_BAD_VALUE, "conntrack",
487                                   "--ctorigsrcport", optarg);
488                 info->match_flags |= XT_CONNTRACK_ORIGSRC_PORT;
489                 info->origsrc_port = htons(port);
490                 if (invert)
491                         info->invert_flags |= XT_CONNTRACK_ORIGSRC_PORT;
492                 break;
493
494         case 'b': /* --ctorigdstport */
495                 if (!strtonum(optarg, NULL, &port, 0, ~(u_int16_t)0))
496                         param_act(P_BAD_VALUE, "conntrack",
497                                   "--ctorigdstport", optarg);
498                 info->match_flags |= XT_CONNTRACK_ORIGDST_PORT;
499                 info->origdst_port = htons(port);
500                 if (invert)
501                         info->invert_flags |= XT_CONNTRACK_ORIGDST_PORT;
502                 break;
503
504         case 'c': /* --ctreplsrcport */
505                 if (!strtonum(optarg, NULL, &port, 0, ~(u_int16_t)0))
506                         param_act(P_BAD_VALUE, "conntrack",
507                                   "--ctreplsrcport", optarg);
508                 info->match_flags |= XT_CONNTRACK_REPLSRC_PORT;
509                 info->replsrc_port = htons(port);
510                 if (invert)
511                         info->invert_flags |= XT_CONNTRACK_REPLSRC_PORT;
512                 break;
513
514         case 'd': /* --ctrepldstport */
515                 if (!strtonum(optarg, NULL, &port, 0, ~(u_int16_t)0))
516                         param_act(P_BAD_VALUE, "conntrack",
517                                   "--ctrepldstport", optarg);
518                 info->match_flags |= XT_CONNTRACK_REPLDST_PORT;
519                 info->repldst_port = htons(port);
520                 if (invert)
521                         info->invert_flags |= XT_CONNTRACK_REPLDST_PORT;
522                 break;
523
524         case 'e': /* --ctdir */
525                 param_act(P_NO_INVERT, "conntrack", "--ctdir", invert);
526                 if (strcasecmp(optarg, "ORIGINAL") == 0) {
527                         info->match_flags  |= XT_CONNTRACK_DIRECTION;
528                         info->invert_flags &= ~XT_CONNTRACK_DIRECTION;
529                 } else if (strcasecmp(optarg, "REPLY") == 0) {
530                         info->match_flags  |= XT_CONNTRACK_DIRECTION;
531                         info->invert_flags |= XT_CONNTRACK_DIRECTION;
532                 } else {
533                         param_act(P_BAD_VALUE, "conntrack", "--ctdir", optarg);
534                 }
535                 break;
536
537         default:
538                 return false;
539         }
540
541         *flags = info->match_flags;
542         return true;
543 }
544
545 static int
546 conntrack_mt4_parse(int c, char **argv, int invert, unsigned int *flags,
547                     const void *entry, struct xt_entry_match **match)
548 {
549         struct xt_conntrack_mtinfo1 *info = (void *)(*match)->data;
550         struct in_addr *addr = NULL;
551         unsigned int naddrs = 0;
552
553         switch (c) {
554         case '3': /* --ctorigsrc */
555                 ipparse_hostnetworkmask(optarg, &addr, &info->origsrc_mask.in,
556                                         &naddrs);
557                 if (naddrs > 1)
558                         exit_error(PARAMETER_PROBLEM,
559                                    "multiple IP addresses not allowed");
560                 if (naddrs == 1)
561                         memcpy(&info->origsrc_addr.in, addr, sizeof(*addr));
562                 info->match_flags |= XT_CONNTRACK_ORIGSRC;
563                 if (invert)
564                         info->invert_flags |= XT_CONNTRACK_ORIGSRC;
565                 break;
566
567         case '4': /* --ctorigdst */
568                 ipparse_hostnetworkmask(optarg, &addr, &info->origdst_mask.in,
569                                         &naddrs);
570                 if (naddrs > 1)
571                         exit_error(PARAMETER_PROBLEM,
572                                    "multiple IP addresses not allowed");
573                 if (naddrs == 1)
574                         memcpy(&info->origdst_addr.in, addr, sizeof(*addr));
575                 info->match_flags |= XT_CONNTRACK_ORIGDST;
576                 if (invert)
577                         info->invert_flags |= XT_CONNTRACK_ORIGDST;
578                 break;
579
580         case '5': /* --ctreplsrc */
581                 ipparse_hostnetworkmask(optarg, &addr, &info->replsrc_mask.in,
582                                         &naddrs);
583                 if (naddrs > 1)
584                         exit_error(PARAMETER_PROBLEM,
585                                    "multiple IP addresses not allowed");
586                 if (naddrs == 1)
587                         memcpy(&info->replsrc_addr.in, addr, sizeof(*addr));
588                 info->match_flags |= XT_CONNTRACK_REPLSRC;
589                 if (invert)
590                         info->invert_flags |= XT_CONNTRACK_REPLSRC;
591                 break;
592
593         case '6': /* --ctrepldst */
594                 ipparse_hostnetworkmask(optarg, &addr, &info->repldst_mask.in,
595                                         &naddrs);
596                 if (naddrs > 1)
597                         exit_error(PARAMETER_PROBLEM,
598                                    "multiple IP addresses not allowed");
599                 if (naddrs == 1)
600                         memcpy(&info->repldst_addr.in, addr, sizeof(*addr));
601                 info->match_flags |= XT_CONNTRACK_REPLDST;
602                 if (invert)
603                         info->invert_flags |= XT_CONNTRACK_REPLDST;
604                 break;
605
606
607         default:
608                 return conntrack_mt_parse(c, argv, invert, flags, match);
609         }
610
611         *flags = info->match_flags;
612         return true;
613 }
614
615 static int
616 conntrack_mt6_parse(int c, char **argv, int invert, unsigned int *flags,
617                     const void *entry, struct xt_entry_match **match)
618 {
619         struct xt_conntrack_mtinfo1 *info = (void *)(*match)->data;
620         struct in6_addr *addr = NULL;
621         unsigned int naddrs = 0;
622
623         switch (c) {
624         case '3': /* --ctorigsrc */
625                 ip6parse_hostnetworkmask(optarg, &addr,
626                                          &info->origsrc_mask.in6, &naddrs);
627                 if (naddrs > 1)
628                         exit_error(PARAMETER_PROBLEM,
629                                    "multiple IP addresses not allowed");
630                 if (naddrs == 1)
631                         memcpy(&info->origsrc_addr.in6, addr, sizeof(*addr));
632                 info->match_flags |= XT_CONNTRACK_ORIGSRC;
633                 if (invert)
634                         info->invert_flags |= XT_CONNTRACK_ORIGSRC;
635                 break;
636
637         case '4': /* --ctorigdst */
638                 ip6parse_hostnetworkmask(optarg, &addr,
639                                          &info->origdst_mask.in6, &naddrs);
640                 if (naddrs > 1)
641                         exit_error(PARAMETER_PROBLEM,
642                                    "multiple IP addresses not allowed");
643                 if (naddrs == 1)
644                         memcpy(&info->origdst_addr.in, addr, sizeof(*addr));
645                 info->match_flags |= XT_CONNTRACK_ORIGDST;
646                 if (invert)
647                         info->invert_flags |= XT_CONNTRACK_ORIGDST;
648                 break;
649
650         case '5': /* --ctreplsrc */
651                 ip6parse_hostnetworkmask(optarg, &addr,
652                                          &info->replsrc_mask.in6, &naddrs);
653                 if (naddrs > 1)
654                         exit_error(PARAMETER_PROBLEM,
655                                    "multiple IP addresses not allowed");
656                 if (naddrs == 1)
657                         memcpy(&info->replsrc_addr.in, addr, sizeof(*addr));
658                 info->match_flags |= XT_CONNTRACK_REPLSRC;
659                 if (invert)
660                         info->invert_flags |= XT_CONNTRACK_REPLSRC;
661                 break;
662
663         case '6': /* --ctrepldst */
664                 ip6parse_hostnetworkmask(optarg, &addr,
665                                          &info->repldst_mask.in6, &naddrs);
666                 if (naddrs > 1)
667                         exit_error(PARAMETER_PROBLEM,
668                                    "multiple IP addresses not allowed");
669                 if (naddrs == 1)
670                         memcpy(&info->repldst_addr.in, addr, sizeof(*addr));
671                 info->match_flags |= XT_CONNTRACK_REPLDST;
672                 if (invert)
673                         info->invert_flags |= XT_CONNTRACK_REPLDST;
674                 break;
675
676
677         default:
678                 return conntrack_mt_parse(c, argv, invert, flags, match);
679         }
680
681         *flags = info->match_flags;
682         return true;
683 }
684
685 static void conntrack_mt_check(unsigned int flags)
686 {
687         if (flags == 0)
688                 exit_error(PARAMETER_PROBLEM, "conntrack: At least one option "
689                            "is required");
690 }
691
692 static void
693 print_state(unsigned int statemask)
694 {
695         const char *sep = "";
696
697         if (statemask & XT_CONNTRACK_STATE_INVALID) {
698                 printf("%sINVALID", sep);
699                 sep = ",";
700         }
701         if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
702                 printf("%sNEW", sep);
703                 sep = ",";
704         }
705         if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
706                 printf("%sRELATED", sep);
707                 sep = ",";
708         }
709         if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
710                 printf("%sESTABLISHED", sep);
711                 sep = ",";
712         }
713         if (statemask & XT_CONNTRACK_STATE_UNTRACKED) {
714                 printf("%sUNTRACKED", sep);
715                 sep = ",";
716         }
717         if (statemask & XT_CONNTRACK_STATE_SNAT) {
718                 printf("%sSNAT", sep);
719                 sep = ",";
720         }
721         if (statemask & XT_CONNTRACK_STATE_DNAT) {
722                 printf("%sDNAT", sep);
723                 sep = ",";
724         }
725         printf(" ");
726 }
727
728 static void
729 print_status(unsigned int statusmask)
730 {
731         const char *sep = "";
732
733         if (statusmask & IPS_EXPECTED) {
734                 printf("%sEXPECTED", sep);
735                 sep = ",";
736         }
737         if (statusmask & IPS_SEEN_REPLY) {
738                 printf("%sSEEN_REPLY", sep);
739                 sep = ",";
740         }
741         if (statusmask & IPS_ASSURED) {
742                 printf("%sASSURED", sep);
743                 sep = ",";
744         }
745         if (statusmask & IPS_CONFIRMED) {
746                 printf("%sCONFIRMED", sep);
747                 sep = ",";
748         }
749         if (statusmask == 0)
750                 printf("%sNONE", sep);
751         printf(" ");
752 }
753
754 static void
755 conntrack_dump_addr(const union nf_inet_addr *addr,
756                     const union nf_inet_addr *mask,
757                     unsigned int family, bool numeric)
758 {
759         if (family == AF_INET) {
760                 if (!numeric && addr->ip == 0) {
761                         printf("anywhere ");
762                         return;
763                 }
764                 printf("%s ", ipaddr_to_anyname(&addr->in));
765         } else if (family == AF_INET6) {
766                 if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 &&
767                     addr->ip6[2] == 0 && addr->ip6[3] == 0) {
768                         printf("anywhere ");
769                         return;
770                 }
771                 printf("%s ", ip6addr_to_anyname(&addr->in6));
772         }
773 }
774
775 static void
776 print_addr(struct in_addr *addr, struct in_addr *mask, int inv, int numeric)
777 {
778         char buf[BUFSIZ];
779
780         if (inv)
781                 printf("! ");
782
783         if (mask->s_addr == 0L && !numeric)
784                 printf("%s ", "anywhere");
785         else {
786                 if (numeric)
787                         sprintf(buf, "%s", ipaddr_to_numeric(addr));
788                 else
789                         sprintf(buf, "%s", ipaddr_to_anyname(addr));
790                 strcat(buf, ipmask_to_numeric(mask));
791                 printf("%s ", buf);
792         }
793 }
794
795 /* Saves the matchinfo in parsable form to stdout. */
796 static void
797 matchinfo_print(const void *ip, const struct xt_entry_match *match, int numeric, const char *optpfx)
798 {
799         struct xt_conntrack_info *sinfo = (void *)match->data;
800
801         if(sinfo->flags & XT_CONNTRACK_STATE) {
802                 if (sinfo->invflags & XT_CONNTRACK_STATE)
803                         printf("! ");
804                 printf("%sctstate ", optpfx);
805                 print_state(sinfo->statemask);
806         }
807
808         if(sinfo->flags & XT_CONNTRACK_PROTO) {
809                 if (sinfo->invflags & XT_CONNTRACK_PROTO)
810                         printf("! ");
811                 printf("%sctproto ", optpfx);
812                 printf("%u ", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum);
813         }
814
815         if(sinfo->flags & XT_CONNTRACK_ORIGSRC) {
816                 if (sinfo->invflags & XT_CONNTRACK_ORIGSRC)
817                         printf("! ");
818                 printf("%sctorigsrc ", optpfx);
819
820                 print_addr(
821                     (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
822                     &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
823                     false,
824                     numeric);
825         }
826
827         if(sinfo->flags & XT_CONNTRACK_ORIGDST) {
828                 if (sinfo->invflags & XT_CONNTRACK_ORIGDST)
829                         printf("! ");
830                 printf("%sctorigdst ", optpfx);
831
832                 print_addr(
833                     (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
834                     &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
835                     false,
836                     numeric);
837         }
838
839         if(sinfo->flags & XT_CONNTRACK_REPLSRC) {
840                 if (sinfo->invflags & XT_CONNTRACK_REPLSRC)
841                         printf("! ");
842                 printf("%sctreplsrc ", optpfx);
843
844                 print_addr(
845                     (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
846                     &sinfo->sipmsk[IP_CT_DIR_REPLY],
847                     false,
848                     numeric);
849         }
850
851         if(sinfo->flags & XT_CONNTRACK_REPLDST) {
852                 if (sinfo->invflags & XT_CONNTRACK_REPLDST)
853                         printf("! ");
854                 printf("%sctrepldst ", optpfx);
855
856                 print_addr(
857                     (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
858                     &sinfo->dipmsk[IP_CT_DIR_REPLY],
859                     false,
860                     numeric);
861         }
862
863         if(sinfo->flags & XT_CONNTRACK_STATUS) {
864                 if (sinfo->invflags & XT_CONNTRACK_STATUS)
865                         printf("! ");
866                 printf("%sctstatus ", optpfx);
867                 print_status(sinfo->statusmask);
868         }
869
870         if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
871                 if (sinfo->invflags & XT_CONNTRACK_EXPIRES)
872                         printf("! ");
873                 printf("%sctexpire ", optpfx);
874
875                 if (sinfo->expires_max == sinfo->expires_min)
876                         printf("%lu ", sinfo->expires_min);
877                 else
878                         printf("%lu:%lu ", sinfo->expires_min, sinfo->expires_max);
879         }
880 }
881
882 static void
883 conntrack_dump(const struct xt_conntrack_mtinfo1 *info, const char *prefix,
884                unsigned int family, bool numeric)
885 {
886         if (info->match_flags & XT_CONNTRACK_STATE) {
887                 if (info->invert_flags & XT_CONNTRACK_STATE)
888                         printf("! ");
889                 printf("%sctstate ", prefix);
890                 print_state(info->state_mask);
891         }
892
893         if (info->match_flags & XT_CONNTRACK_PROTO) {
894                 if (info->invert_flags & XT_CONNTRACK_PROTO)
895                         printf("! ");
896                 printf("%sctproto %u ", prefix, info->l4proto);
897         }
898
899         if (info->match_flags & XT_CONNTRACK_ORIGSRC) {
900                 if (info->invert_flags & XT_CONNTRACK_PROTO)
901                         printf("! ");
902                 printf("%sctorigsrc ", prefix);
903                 conntrack_dump_addr(&info->origsrc_addr, &info->origsrc_mask,
904                                     family, numeric);
905         }
906
907         if (info->match_flags & XT_CONNTRACK_ORIGDST) {
908                 if (info->invert_flags & XT_CONNTRACK_PROTO)
909                         printf("! ");
910                 printf("%sctorigdst ", prefix);
911                 conntrack_dump_addr(&info->origdst_addr, &info->origdst_mask,
912                                     family, numeric);
913         }
914
915         if (info->match_flags & XT_CONNTRACK_REPLSRC) {
916                 if (info->invert_flags & XT_CONNTRACK_PROTO)
917                         printf("! ");
918                 printf("%sctreplsrc ", prefix);
919                 conntrack_dump_addr(&info->replsrc_addr, &info->replsrc_mask,
920                                     family, numeric);
921         }
922
923         if (info->match_flags & XT_CONNTRACK_REPLDST) {
924                 if (info->invert_flags & XT_CONNTRACK_PROTO)
925                         printf("! ");
926                 printf("%sctrepldst ", prefix);
927                 conntrack_dump_addr(&info->repldst_addr, &info->repldst_mask,
928                                     family, numeric);
929         }
930
931         if (info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) {
932                 if (info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT)
933                         printf("! ");
934                 printf("%sctorigsrcport %u ", prefix,
935                        ntohs(info->origsrc_port));
936         }
937
938         if (info->match_flags & XT_CONNTRACK_ORIGDST_PORT) {
939                 if (info->invert_flags & XT_CONNTRACK_ORIGDST_PORT)
940                         printf("! ");
941                 printf("%sctorigdstport %u ", prefix,
942                        ntohs(info->origdst_port));
943         }
944
945         if (info->match_flags & XT_CONNTRACK_REPLSRC_PORT) {
946                 if (info->invert_flags & XT_CONNTRACK_REPLSRC_PORT)
947                         printf("! ");
948                 printf("%sctreplsrcport %u ", prefix,
949                        ntohs(info->replsrc_port));
950         }
951
952         if (info->match_flags & XT_CONNTRACK_REPLDST_PORT) {
953                 if (info->invert_flags & XT_CONNTRACK_REPLDST_PORT)
954                         printf("! ");
955                 printf("%sctrepldstport %u ", prefix,
956                        ntohs(info->repldst_port));
957         }
958
959         if (info->match_flags & XT_CONNTRACK_STATUS) {
960                 if (info->invert_flags & XT_CONNTRACK_STATUS)
961                         printf("! ");
962                 printf("%sctstatus ", prefix);
963                 print_status(info->status_mask);
964         }
965
966         if (info->match_flags & XT_CONNTRACK_EXPIRES) {
967                 if (info->invert_flags & XT_CONNTRACK_EXPIRES)
968                         printf("! ");
969                 printf("%sctexpire ", prefix);
970
971                 if (info->expires_max == info->expires_min)
972                         printf("%u ", (unsigned int)info->expires_min);
973                 else
974                         printf("%u:%u ", (unsigned int)info->expires_min,
975                                (unsigned int)info->expires_max);
976         }
977 }
978
979 /* Prints out the matchinfo. */
980 static void conntrack_print(const void *ip, const struct xt_entry_match *match,
981                             int numeric)
982 {
983         matchinfo_print(ip, match, numeric, "");
984 }
985
986 static void
987 conntrack_mt_print(const void *ip, const struct xt_entry_match *match,
988                    int numeric)
989 {
990         conntrack_dump((const void *)match->data, "", AF_INET, numeric);
991 }
992
993 static void
994 conntrack_mt6_print(const void *ip, const struct xt_entry_match *match,
995                     int numeric)
996 {
997         conntrack_dump((const void *)match->data, "", AF_INET6, numeric);
998 }
999
1000 /* Saves the matchinfo in parsable form to stdout. */
1001 static void conntrack_save(const void *ip, const struct xt_entry_match *match)
1002 {
1003         matchinfo_print(ip, match, 1, "--");
1004 }
1005
1006 static void conntrack_mt_save(const void *ip,
1007                               const struct xt_entry_match *match)
1008 {
1009         conntrack_dump((const void *)match->data, "--", AF_INET, true);
1010 }
1011
1012 static void conntrack_mt6_save(const void *ip,
1013                                const struct xt_entry_match *match)
1014 {
1015         conntrack_dump((const void *)match->data, "--", AF_INET6, true);
1016 }
1017
1018 static struct xtables_match conntrack_match = {
1019         .version       = XTABLES_VERSION,
1020         .name          = "conntrack",
1021         .revision      = 0,
1022         .family        = AF_INET,
1023         .size          = XT_ALIGN(sizeof(struct xt_conntrack_info)),
1024         .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_info)),
1025         .help          = conntrack_mt_help,
1026         .parse         = conntrack_parse,
1027         .final_check   = conntrack_mt_check,
1028         .print         = conntrack_print,
1029         .save          = conntrack_save,
1030         .extra_opts    = conntrack_mt_opts_v0,
1031 };
1032
1033 static struct xtables_match conntrack_mt_reg = {
1034         .version       = XTABLES_VERSION,
1035         .name          = "conntrack",
1036         .revision      = 1,
1037         .family        = AF_INET,
1038         .size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1039         .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1040         .help          = conntrack_mt_help,
1041         .parse         = conntrack_mt4_parse,
1042         .final_check   = conntrack_mt_check,
1043         .print         = conntrack_mt_print,
1044         .save          = conntrack_mt_save,
1045         .extra_opts    = conntrack_mt_opts,
1046 };
1047
1048 static struct xtables_match conntrack_mt6_reg = {
1049         .version       = XTABLES_VERSION,
1050         .name          = "conntrack",
1051         .revision      = 1,
1052         .family        = AF_INET6,
1053         .size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1054         .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1055         .help          = conntrack_mt_help,
1056         .parse         = conntrack_mt6_parse,
1057         .final_check   = conntrack_mt_check,
1058         .print         = conntrack_mt6_print,
1059         .save          = conntrack_mt6_save,
1060         .extra_opts    = conntrack_mt_opts,
1061 };
1062
1063 void _init(void)
1064 {
1065         xtables_register_match(&conntrack_match);
1066         xtables_register_match(&conntrack_mt_reg);
1067         xtables_register_match(&conntrack_mt6_reg);
1068 }