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