libxt_conntrack.c revision 73866357e4a7a0fdc1b293bf8863fee2bd56da9e
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
25struct 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
44struct 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	uint8_t flags;
54	/* Inverse flags */
55	uint8_t invflags;
56};
57
58static 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
82static 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	XT_GETOPT_TABLEEND,
92};
93
94static 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
111static int
112parse_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
133static void
134parse_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
151static bool
152conntrack_ps_state(struct xt_conntrack_mtinfo3 *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
174static void
175conntrack_ps_states(struct xt_conntrack_mtinfo3 *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
190static int
191parse_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
210static void
211parse_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
225static bool
226conntrack_ps_status(struct xt_conntrack_mtinfo3 *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
244static void
245conntrack_ps_statuses(struct xt_conntrack_mtinfo3 *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
260static unsigned long
261parse_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 */
272static void
273parse_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
299static void
300conntrack_ps_expires(struct xt_conntrack_mtinfo3 *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
323static 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
466	*flags = sinfo->flags;
467	return 1;
468}
469
470static void
471ct_parse_ports(const char *param, const char *str,
472	       u_int16_t *port_low, u_int16_t *port_high)
473{
474	unsigned int port;
475	char *buf, *cp;
476
477	buf = strdup(str);
478	cp = strchr(buf, ':');
479	if (cp != NULL)
480		*cp = '\0';
481
482	if (!xtables_strtoui(buf, NULL, &port, 0, UINT16_MAX))
483		xtables_param_act(XTF_BAD_VALUE, "conntrack", param, buf);
484
485	if (port_high == NULL) {
486		/* revision 0-2 do not support ranges */
487		if (cp != NULL)
488			xtables_error(PARAMETER_PROBLEM, "conntrack: "
489				      "port ranges not supported");
490
491		*port_low = htons(port);
492	} else {
493		*port_low = port;
494
495		if (cp != NULL) {
496			if (!xtables_strtoui(cp + 1, NULL, &port, 0, UINT16_MAX))
497				xtables_param_act(XTF_BAD_VALUE, "conntrack", param, buf);
498
499			*port_high = port;
500			if (*port_low > *port_high)
501				xtables_error(PARAMETER_PROBLEM,
502					      "invalid portrange (min > max)");
503		} else
504			*port_high = port;
505	}
506
507	free(buf);
508}
509
510
511static int
512conntrack_mt_parse(int c, bool invert, unsigned int *flags,
513                   struct xt_conntrack_mtinfo3 *info, bool v3)
514{
515	char *p;
516
517	switch (c) {
518	case '1': /* --ctstate */
519		conntrack_ps_states(info, optarg);
520		info->match_flags |= XT_CONNTRACK_STATE;
521		if (invert)
522			info->invert_flags |= XT_CONNTRACK_STATE;
523		break;
524
525	case '2': /* --ctproto */
526		/* Canonicalize into lower case */
527		for (p = optarg; *p != '\0'; ++p)
528			*p = tolower(*p);
529		info->l4proto = xtables_parse_protocol(optarg);
530
531		if (info->l4proto == 0 && (info->invert_flags & XT_INV_PROTO))
532			xtables_error(PARAMETER_PROBLEM, "conntrack: rule would "
533			           "never match protocol");
534
535		info->match_flags |= XT_CONNTRACK_PROTO;
536		if (invert)
537			info->invert_flags |= XT_CONNTRACK_PROTO;
538		break;
539
540	case '7': /* --ctstatus */
541		conntrack_ps_statuses(info, optarg);
542		info->match_flags |= XT_CONNTRACK_STATUS;
543		if (invert)
544			info->invert_flags |= XT_CONNTRACK_STATUS;
545		break;
546
547	case '8': /* --ctexpire */
548		conntrack_ps_expires(info, optarg);
549		info->match_flags |= XT_CONNTRACK_EXPIRES;
550		if (invert)
551			info->invert_flags |= XT_CONNTRACK_EXPIRES;
552		break;
553
554	case 'a': /* --ctorigsrcport */
555		ct_parse_ports("--ctorigsrcport", optarg,
556			       &info->origsrc_port,
557			       v3 ? &info->origsrc_port_high : NULL);
558
559		info->match_flags |= XT_CONNTRACK_ORIGSRC_PORT;
560		if (invert)
561			info->invert_flags |= XT_CONNTRACK_ORIGSRC_PORT;
562		break;
563
564	case 'b': /* --ctorigdstport */
565		ct_parse_ports("--ctorigdstport", optarg,
566			       &info->origdst_port,
567			       v3 ? &info->origdst_port_high : NULL);
568
569		info->match_flags |= XT_CONNTRACK_ORIGDST_PORT;
570		if (invert)
571			info->invert_flags |= XT_CONNTRACK_ORIGDST_PORT;
572		break;
573
574	case 'c': /* --ctreplsrcport */
575		ct_parse_ports("--ctreplsrcport", optarg,
576			       &info->replsrc_port,
577			       v3 ? &info->replsrc_port_high : NULL);
578
579		info->match_flags |= XT_CONNTRACK_REPLSRC_PORT;
580		if (invert)
581			info->invert_flags |= XT_CONNTRACK_REPLSRC_PORT;
582		break;
583
584	case 'd': /* --ctrepldstport */
585		ct_parse_ports("--ctrepldstport", optarg,
586			       &info->repldst_port,
587			       v3 ? &info->repldst_port_high : NULL);
588
589		info->match_flags |= XT_CONNTRACK_REPLDST_PORT;
590		if (invert)
591			info->invert_flags |= XT_CONNTRACK_REPLDST_PORT;
592		break;
593
594	case 'e': /* --ctdir */
595		xtables_param_act(XTF_NO_INVERT, "conntrack", "--ctdir", invert);
596		if (strcasecmp(optarg, "ORIGINAL") == 0) {
597			info->match_flags  |= XT_CONNTRACK_DIRECTION;
598			info->invert_flags &= ~XT_CONNTRACK_DIRECTION;
599		} else if (strcasecmp(optarg, "REPLY") == 0) {
600			info->match_flags  |= XT_CONNTRACK_DIRECTION;
601			info->invert_flags |= XT_CONNTRACK_DIRECTION;
602		} else {
603			xtables_param_act(XTF_BAD_VALUE, "conntrack", "--ctdir", optarg);
604		}
605		break;
606	}
607
608	*flags = info->match_flags;
609	return true;
610}
611
612static int
613conntrack_mt4_parse(int c, bool invert, unsigned int *flags,
614                    struct xt_conntrack_mtinfo3 *info, bool v3)
615{
616	struct in_addr *addr = NULL;
617	unsigned int naddrs = 0;
618
619	switch (c) {
620	case '3': /* --ctorigsrc */
621		xtables_ipparse_any(optarg, &addr, &info->origsrc_mask.in,
622		                        &naddrs);
623		if (naddrs > 1)
624			xtables_error(PARAMETER_PROBLEM,
625			           "multiple IP addresses not allowed");
626		if (naddrs == 1)
627			memcpy(&info->origsrc_addr.in, addr, sizeof(*addr));
628		info->match_flags |= XT_CONNTRACK_ORIGSRC;
629		if (invert)
630			info->invert_flags |= XT_CONNTRACK_ORIGSRC;
631		break;
632
633	case '4': /* --ctorigdst */
634		xtables_ipparse_any(optarg, &addr, &info->origdst_mask.in,
635		                        &naddrs);
636		if (naddrs > 1)
637			xtables_error(PARAMETER_PROBLEM,
638			           "multiple IP addresses not allowed");
639		if (naddrs == 1)
640			memcpy(&info->origdst_addr.in, addr, sizeof(*addr));
641		info->match_flags |= XT_CONNTRACK_ORIGDST;
642		if (invert)
643			info->invert_flags |= XT_CONNTRACK_ORIGDST;
644		break;
645
646	case '5': /* --ctreplsrc */
647		xtables_ipparse_any(optarg, &addr, &info->replsrc_mask.in,
648		                        &naddrs);
649		if (naddrs > 1)
650			xtables_error(PARAMETER_PROBLEM,
651			           "multiple IP addresses not allowed");
652		if (naddrs == 1)
653			memcpy(&info->replsrc_addr.in, addr, sizeof(*addr));
654		info->match_flags |= XT_CONNTRACK_REPLSRC;
655		if (invert)
656			info->invert_flags |= XT_CONNTRACK_REPLSRC;
657		break;
658
659	case '6': /* --ctrepldst */
660		xtables_ipparse_any(optarg, &addr, &info->repldst_mask.in,
661		                        &naddrs);
662		if (naddrs > 1)
663			xtables_error(PARAMETER_PROBLEM,
664			           "multiple IP addresses not allowed");
665		if (naddrs == 1)
666			memcpy(&info->repldst_addr.in, addr, sizeof(*addr));
667		info->match_flags |= XT_CONNTRACK_REPLDST;
668		if (invert)
669			info->invert_flags |= XT_CONNTRACK_REPLDST;
670		break;
671
672
673	default:
674		return conntrack_mt_parse(c, invert, flags, info, v3);
675	}
676
677	*flags = info->match_flags;
678	return true;
679}
680
681static int
682conntrack_mt6_parse(int c, bool invert, unsigned int *flags,
683                    struct xt_conntrack_mtinfo3 *info, bool v3)
684{
685	struct in6_addr *addr = NULL;
686	unsigned int naddrs = 0;
687
688	switch (c) {
689	case '3': /* --ctorigsrc */
690		xtables_ip6parse_any(optarg, &addr,
691		                         &info->origsrc_mask.in6, &naddrs);
692		if (naddrs > 1)
693			xtables_error(PARAMETER_PROBLEM,
694			           "multiple IP addresses not allowed");
695		if (naddrs == 1)
696			memcpy(&info->origsrc_addr.in6, addr, sizeof(*addr));
697		info->match_flags |= XT_CONNTRACK_ORIGSRC;
698		if (invert)
699			info->invert_flags |= XT_CONNTRACK_ORIGSRC;
700		break;
701
702	case '4': /* --ctorigdst */
703		xtables_ip6parse_any(optarg, &addr,
704		                         &info->origdst_mask.in6, &naddrs);
705		if (naddrs > 1)
706			xtables_error(PARAMETER_PROBLEM,
707			           "multiple IP addresses not allowed");
708		if (naddrs == 1)
709			memcpy(&info->origdst_addr.in, addr, sizeof(*addr));
710		info->match_flags |= XT_CONNTRACK_ORIGDST;
711		if (invert)
712			info->invert_flags |= XT_CONNTRACK_ORIGDST;
713		break;
714
715	case '5': /* --ctreplsrc */
716		xtables_ip6parse_any(optarg, &addr,
717		                         &info->replsrc_mask.in6, &naddrs);
718		if (naddrs > 1)
719			xtables_error(PARAMETER_PROBLEM,
720			           "multiple IP addresses not allowed");
721		if (naddrs == 1)
722			memcpy(&info->replsrc_addr.in, addr, sizeof(*addr));
723		info->match_flags |= XT_CONNTRACK_REPLSRC;
724		if (invert)
725			info->invert_flags |= XT_CONNTRACK_REPLSRC;
726		break;
727
728	case '6': /* --ctrepldst */
729		xtables_ip6parse_any(optarg, &addr,
730		                         &info->repldst_mask.in6, &naddrs);
731		if (naddrs > 1)
732			xtables_error(PARAMETER_PROBLEM,
733			           "multiple IP addresses not allowed");
734		if (naddrs == 1)
735			memcpy(&info->repldst_addr.in, addr, sizeof(*addr));
736		info->match_flags |= XT_CONNTRACK_REPLDST;
737		if (invert)
738			info->invert_flags |= XT_CONNTRACK_REPLDST;
739		break;
740
741
742	default:
743		return conntrack_mt_parse(c, invert, flags, info, v3);
744	}
745
746	*flags = info->match_flags;
747	return true;
748}
749
750#define cinfo_transform(r, l) \
751	do { \
752		memcpy((r), (l), offsetof(typeof(*(l)), state_mask)); \
753		(r)->state_mask  = (l)->state_mask; \
754		(r)->status_mask = (l)->status_mask; \
755	} while (false);
756
757static int
758conntrack1_mt4_parse(int c, char **argv, int invert, unsigned int *flags,
759                     const void *entry, struct xt_entry_match **match)
760{
761	struct xt_conntrack_mtinfo1 *info = (void *)(*match)->data;
762	struct xt_conntrack_mtinfo3 up;
763
764	cinfo_transform(&up, info);
765	if (!conntrack_mt4_parse(c, invert, flags, &up, false))
766		return false;
767	cinfo_transform(info, &up);
768	return true;
769}
770
771static int
772conntrack1_mt6_parse(int c, char **argv, int invert, unsigned int *flags,
773                     const void *entry, struct xt_entry_match **match)
774{
775	struct xt_conntrack_mtinfo1 *info = (void *)(*match)->data;
776	struct xt_conntrack_mtinfo3 up;
777
778	cinfo_transform(&up, info);
779	if (!conntrack_mt6_parse(c, invert, flags, &up, false))
780		return false;
781	cinfo_transform(info, &up);
782	return true;
783}
784
785static int
786conntrack2_mt4_parse(int c, char **argv, int invert, unsigned int *flags,
787                     const void *entry, struct xt_entry_match **match)
788{
789	return conntrack_mt4_parse(c, invert, flags, (void *)(*match)->data, false);
790}
791
792static int
793conntrack2_mt6_parse(int c, char **argv, int invert, unsigned int *flags,
794                     const void *entry, struct xt_entry_match **match)
795{
796	return conntrack_mt6_parse(c, invert, flags, (void *)(*match)->data, false);
797}
798
799static int
800conntrack3_mt4_parse(int c, char **argv, int invert, unsigned int *flags,
801                     const void *entry, struct xt_entry_match **match)
802{
803	return conntrack_mt4_parse(c, invert, flags, (void *)(*match)->data, true);
804}
805
806static int
807conntrack3_mt6_parse(int c, char **argv, int invert, unsigned int *flags,
808                     const void *entry, struct xt_entry_match **match)
809{
810	return conntrack_mt6_parse(c, invert, flags, (void *)(*match)->data, true);
811}
812
813static void conntrack_mt_check(unsigned int flags)
814{
815	if (flags == 0)
816		xtables_error(PARAMETER_PROBLEM, "conntrack: At least one option "
817		           "is required");
818}
819
820static void
821print_state(unsigned int statemask)
822{
823	const char *sep = " ";
824
825	if (statemask & XT_CONNTRACK_STATE_INVALID) {
826		printf("%sINVALID", sep);
827		sep = ",";
828	}
829	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
830		printf("%sNEW", sep);
831		sep = ",";
832	}
833	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
834		printf("%sRELATED", sep);
835		sep = ",";
836	}
837	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
838		printf("%sESTABLISHED", sep);
839		sep = ",";
840	}
841	if (statemask & XT_CONNTRACK_STATE_UNTRACKED) {
842		printf("%sUNTRACKED", sep);
843		sep = ",";
844	}
845	if (statemask & XT_CONNTRACK_STATE_SNAT) {
846		printf("%sSNAT", sep);
847		sep = ",";
848	}
849	if (statemask & XT_CONNTRACK_STATE_DNAT) {
850		printf("%sDNAT", sep);
851		sep = ",";
852	}
853}
854
855static void
856print_status(unsigned int statusmask)
857{
858	const char *sep = " ";
859
860	if (statusmask & IPS_EXPECTED) {
861		printf("%sEXPECTED", sep);
862		sep = ",";
863	}
864	if (statusmask & IPS_SEEN_REPLY) {
865		printf("%sSEEN_REPLY", sep);
866		sep = ",";
867	}
868	if (statusmask & IPS_ASSURED) {
869		printf("%sASSURED", sep);
870		sep = ",";
871	}
872	if (statusmask & IPS_CONFIRMED) {
873		printf("%sCONFIRMED", sep);
874		sep = ",";
875	}
876	if (statusmask == 0)
877		printf("%sNONE", sep);
878}
879
880static void
881conntrack_dump_addr(const union nf_inet_addr *addr,
882                    const union nf_inet_addr *mask,
883                    unsigned int family, bool numeric)
884{
885	if (family == NFPROTO_IPV4) {
886		if (!numeric && addr->ip == 0) {
887			printf(" anywhere");
888			return;
889		}
890		if (numeric)
891			printf(" %s%s",
892			       xtables_ipaddr_to_numeric(&addr->in),
893			       xtables_ipmask_to_numeric(&mask->in));
894		else
895			printf(" %s%s",
896			       xtables_ipaddr_to_anyname(&addr->in),
897			       xtables_ipmask_to_numeric(&mask->in));
898	} else if (family == NFPROTO_IPV6) {
899		if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 &&
900		    addr->ip6[2] == 0 && addr->ip6[3] == 0) {
901			printf(" anywhere");
902			return;
903		}
904		if (numeric)
905			printf(" %s%s",
906			       xtables_ip6addr_to_numeric(&addr->in6),
907			       xtables_ip6mask_to_numeric(&mask->in6));
908		else
909			printf(" %s%s",
910			       xtables_ip6addr_to_anyname(&addr->in6),
911			       xtables_ip6mask_to_numeric(&mask->in6));
912	}
913}
914
915static void
916print_addr(const struct in_addr *addr, const struct in_addr *mask,
917           int inv, int numeric)
918{
919	char buf[BUFSIZ];
920
921	if (inv)
922		printf(" !");
923
924	if (mask->s_addr == 0L && !numeric)
925		printf(" %s", "anywhere");
926	else {
927		if (numeric)
928			strcpy(buf, xtables_ipaddr_to_numeric(addr));
929		else
930			strcpy(buf, xtables_ipaddr_to_anyname(addr));
931		strcat(buf, xtables_ipmask_to_numeric(mask));
932		printf(" %s", buf);
933	}
934}
935
936static void
937matchinfo_print(const void *ip, const struct xt_entry_match *match, int numeric, const char *optpfx)
938{
939	const struct xt_conntrack_info *sinfo = (const void *)match->data;
940
941	if(sinfo->flags & XT_CONNTRACK_STATE) {
942        	if (sinfo->invflags & XT_CONNTRACK_STATE)
943			printf(" !");
944		printf(" %sctstate", optpfx);
945		print_state(sinfo->statemask);
946	}
947
948	if(sinfo->flags & XT_CONNTRACK_PROTO) {
949        	if (sinfo->invflags & XT_CONNTRACK_PROTO)
950			printf(" !");
951		printf(" %sctproto", optpfx);
952		printf(" %u", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum);
953	}
954
955	if(sinfo->flags & XT_CONNTRACK_ORIGSRC) {
956		if (sinfo->invflags & XT_CONNTRACK_ORIGSRC)
957			printf(" !");
958		printf(" %sctorigsrc", optpfx);
959
960		print_addr(
961		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
962		    &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
963		    false,
964		    numeric);
965	}
966
967	if(sinfo->flags & XT_CONNTRACK_ORIGDST) {
968		if (sinfo->invflags & XT_CONNTRACK_ORIGDST)
969			printf(" !");
970		printf(" %sctorigdst", optpfx);
971
972		print_addr(
973		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
974		    &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
975		    false,
976		    numeric);
977	}
978
979	if(sinfo->flags & XT_CONNTRACK_REPLSRC) {
980		if (sinfo->invflags & XT_CONNTRACK_REPLSRC)
981			printf(" !");
982		printf(" %sctreplsrc", optpfx);
983
984		print_addr(
985		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
986		    &sinfo->sipmsk[IP_CT_DIR_REPLY],
987		    false,
988		    numeric);
989	}
990
991	if(sinfo->flags & XT_CONNTRACK_REPLDST) {
992		if (sinfo->invflags & XT_CONNTRACK_REPLDST)
993			printf(" !");
994		printf(" %sctrepldst", optpfx);
995
996		print_addr(
997		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
998		    &sinfo->dipmsk[IP_CT_DIR_REPLY],
999		    false,
1000		    numeric);
1001	}
1002
1003	if(sinfo->flags & XT_CONNTRACK_STATUS) {
1004        	if (sinfo->invflags & XT_CONNTRACK_STATUS)
1005			printf(" !");
1006		printf(" %sctstatus", optpfx);
1007		print_status(sinfo->statusmask);
1008	}
1009
1010	if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
1011        	if (sinfo->invflags & XT_CONNTRACK_EXPIRES)
1012			printf(" !");
1013		printf(" %sctexpire ", optpfx);
1014
1015        	if (sinfo->expires_max == sinfo->expires_min)
1016			printf("%lu", sinfo->expires_min);
1017        	else
1018			printf("%lu:%lu", sinfo->expires_min, sinfo->expires_max);
1019	}
1020
1021	if (sinfo->flags & XT_CONNTRACK_DIRECTION) {
1022		if (sinfo->invflags & XT_CONNTRACK_DIRECTION)
1023			printf(" %sctdir REPLY", optpfx);
1024		else
1025			printf(" %sctdir ORIGINAL", optpfx);
1026	}
1027
1028}
1029
1030static void
1031conntrack_dump_ports(const char *prefix, const char *opt,
1032		     u_int16_t port_low, u_int16_t port_high)
1033{
1034	if (port_high == 0 || port_low == port_high)
1035		printf(" %s%s %u", prefix, opt, port_low);
1036	else
1037		printf(" %s%s %u:%u", prefix, opt, port_low, port_high);
1038}
1039
1040static void
1041conntrack_dump(const struct xt_conntrack_mtinfo3 *info, const char *prefix,
1042               unsigned int family, bool numeric, bool v3)
1043{
1044	if (info->match_flags & XT_CONNTRACK_STATE) {
1045		if (info->invert_flags & XT_CONNTRACK_STATE)
1046			printf(" !");
1047		printf(" %sctstate", prefix);
1048		print_state(info->state_mask);
1049	}
1050
1051	if (info->match_flags & XT_CONNTRACK_PROTO) {
1052		if (info->invert_flags & XT_CONNTRACK_PROTO)
1053			printf(" !");
1054		printf(" %sctproto %u", prefix, info->l4proto);
1055	}
1056
1057	if (info->match_flags & XT_CONNTRACK_ORIGSRC) {
1058		if (info->invert_flags & XT_CONNTRACK_ORIGSRC)
1059			printf(" !");
1060		printf(" %sctorigsrc", prefix);
1061		conntrack_dump_addr(&info->origsrc_addr, &info->origsrc_mask,
1062		                    family, numeric);
1063	}
1064
1065	if (info->match_flags & XT_CONNTRACK_ORIGDST) {
1066		if (info->invert_flags & XT_CONNTRACK_ORIGDST)
1067			printf(" !");
1068		printf(" %sctorigdst", prefix);
1069		conntrack_dump_addr(&info->origdst_addr, &info->origdst_mask,
1070		                    family, numeric);
1071	}
1072
1073	if (info->match_flags & XT_CONNTRACK_REPLSRC) {
1074		if (info->invert_flags & XT_CONNTRACK_REPLSRC)
1075			printf(" !");
1076		printf(" %sctreplsrc", prefix);
1077		conntrack_dump_addr(&info->replsrc_addr, &info->replsrc_mask,
1078		                    family, numeric);
1079	}
1080
1081	if (info->match_flags & XT_CONNTRACK_REPLDST) {
1082		if (info->invert_flags & XT_CONNTRACK_REPLDST)
1083			printf(" !");
1084		printf(" %sctrepldst", prefix);
1085		conntrack_dump_addr(&info->repldst_addr, &info->repldst_mask,
1086		                    family, numeric);
1087	}
1088
1089	if (info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) {
1090		if (info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT)
1091			printf(" !");
1092		conntrack_dump_ports(prefix, "ctorigsrcport",
1093				     v3 ? info->origsrc_port : ntohs(info->origsrc_port),
1094				     v3 ? info->origsrc_port_high : 0);
1095	}
1096
1097	if (info->match_flags & XT_CONNTRACK_ORIGDST_PORT) {
1098		if (info->invert_flags & XT_CONNTRACK_ORIGDST_PORT)
1099			printf(" !");
1100		conntrack_dump_ports(prefix, "ctorigdstport",
1101				     v3 ? info->origdst_port : ntohs(info->origdst_port),
1102				     v3 ? info->origdst_port_high : 0);
1103	}
1104
1105	if (info->match_flags & XT_CONNTRACK_REPLSRC_PORT) {
1106		if (info->invert_flags & XT_CONNTRACK_REPLSRC_PORT)
1107			printf(" !");
1108		conntrack_dump_ports(prefix, "ctreplsrcport",
1109				     v3 ? info->replsrc_port : ntohs(info->replsrc_port),
1110				     v3 ? info->replsrc_port_high : 0);
1111	}
1112
1113	if (info->match_flags & XT_CONNTRACK_REPLDST_PORT) {
1114		if (info->invert_flags & XT_CONNTRACK_REPLDST_PORT)
1115			printf(" !");
1116		conntrack_dump_ports(prefix, "ctrepldstport",
1117				     v3 ? info->repldst_port : ntohs(info->repldst_port),
1118				     v3 ? info->repldst_port_high : 0);
1119	}
1120
1121	if (info->match_flags & XT_CONNTRACK_STATUS) {
1122		if (info->invert_flags & XT_CONNTRACK_STATUS)
1123			printf(" !");
1124		printf(" %sctstatus", prefix);
1125		print_status(info->status_mask);
1126	}
1127
1128	if (info->match_flags & XT_CONNTRACK_EXPIRES) {
1129		if (info->invert_flags & XT_CONNTRACK_EXPIRES)
1130			printf(" !");
1131		printf(" %sctexpire ", prefix);
1132
1133		if (info->expires_max == info->expires_min)
1134			printf("%u", (unsigned int)info->expires_min);
1135		else
1136			printf("%u:%u", (unsigned int)info->expires_min,
1137			       (unsigned int)info->expires_max);
1138	}
1139
1140	if (info->match_flags & XT_CONNTRACK_DIRECTION) {
1141		if (info->invert_flags & XT_CONNTRACK_DIRECTION)
1142			printf(" %sctdir REPLY", prefix);
1143		else
1144			printf(" %sctdir ORIGINAL", prefix);
1145	}
1146}
1147
1148static void conntrack_print(const void *ip, const struct xt_entry_match *match,
1149                            int numeric)
1150{
1151	matchinfo_print(ip, match, numeric, "");
1152}
1153
1154static void
1155conntrack1_mt4_print(const void *ip, const struct xt_entry_match *match,
1156                     int numeric)
1157{
1158	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
1159	struct xt_conntrack_mtinfo3 up;
1160
1161	cinfo_transform(&up, info);
1162	conntrack_dump(&up, "", NFPROTO_IPV4, numeric, false);
1163}
1164
1165static void
1166conntrack1_mt6_print(const void *ip, const struct xt_entry_match *match,
1167                     int numeric)
1168{
1169	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
1170	struct xt_conntrack_mtinfo3 up;
1171
1172	cinfo_transform(&up, info);
1173	conntrack_dump(&up, "", NFPROTO_IPV6, numeric, false);
1174}
1175
1176static void
1177conntrack2_mt_print(const void *ip, const struct xt_entry_match *match,
1178                    int numeric)
1179{
1180	conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric, false);
1181}
1182
1183static void
1184conntrack2_mt6_print(const void *ip, const struct xt_entry_match *match,
1185                     int numeric)
1186{
1187	conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric, false);
1188}
1189
1190static void
1191conntrack3_mt_print(const void *ip, const struct xt_entry_match *match,
1192                    int numeric)
1193{
1194	conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric, true);
1195}
1196
1197static void
1198conntrack3_mt6_print(const void *ip, const struct xt_entry_match *match,
1199                     int numeric)
1200{
1201	conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric, true);
1202}
1203
1204static void conntrack_save(const void *ip, const struct xt_entry_match *match)
1205{
1206	matchinfo_print(ip, match, 1, "--");
1207}
1208
1209static void conntrack3_mt_save(const void *ip,
1210                               const struct xt_entry_match *match)
1211{
1212	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true, true);
1213}
1214
1215static void conntrack3_mt6_save(const void *ip,
1216                                const struct xt_entry_match *match)
1217{
1218	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true, true);
1219}
1220
1221static void conntrack2_mt_save(const void *ip,
1222                               const struct xt_entry_match *match)
1223{
1224	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true, false);
1225}
1226
1227static void conntrack2_mt6_save(const void *ip,
1228                                const struct xt_entry_match *match)
1229{
1230	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true, false);
1231}
1232
1233static void
1234conntrack1_mt4_save(const void *ip, const struct xt_entry_match *match)
1235{
1236	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
1237	struct xt_conntrack_mtinfo3 up;
1238
1239	cinfo_transform(&up, info);
1240	conntrack_dump(&up, "--", NFPROTO_IPV4, true, false);
1241}
1242
1243static void
1244conntrack1_mt6_save(const void *ip, const struct xt_entry_match *match)
1245{
1246	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
1247	struct xt_conntrack_mtinfo3 up;
1248
1249	cinfo_transform(&up, info);
1250	conntrack_dump(&up, "--", NFPROTO_IPV6, true, false);
1251}
1252
1253static struct xtables_match conntrack_mt_reg[] = {
1254	{
1255		.version       = XTABLES_VERSION,
1256		.name          = "conntrack",
1257		.revision      = 0,
1258		.family        = NFPROTO_IPV4,
1259		.size          = XT_ALIGN(sizeof(struct xt_conntrack_info)),
1260		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_info)),
1261		.help          = conntrack_mt_help,
1262		.parse         = conntrack_parse,
1263		.final_check   = conntrack_mt_check,
1264		.print         = conntrack_print,
1265		.save          = conntrack_save,
1266		.extra_opts    = conntrack_mt_opts_v0,
1267	},
1268	{
1269		.version       = XTABLES_VERSION,
1270		.name          = "conntrack",
1271		.revision      = 1,
1272		.family        = NFPROTO_IPV4,
1273		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1274		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1275		.help          = conntrack_mt_help,
1276		.parse         = conntrack1_mt4_parse,
1277		.final_check   = conntrack_mt_check,
1278		.print         = conntrack1_mt4_print,
1279		.save          = conntrack1_mt4_save,
1280		.extra_opts    = conntrack_mt_opts,
1281	},
1282	{
1283		.version       = XTABLES_VERSION,
1284		.name          = "conntrack",
1285		.revision      = 1,
1286		.family        = NFPROTO_IPV6,
1287		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1288		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1289		.help          = conntrack_mt_help,
1290		.parse         = conntrack1_mt6_parse,
1291		.final_check   = conntrack_mt_check,
1292		.print         = conntrack1_mt6_print,
1293		.save          = conntrack1_mt6_save,
1294		.extra_opts    = conntrack_mt_opts,
1295	},
1296	{
1297		.version       = XTABLES_VERSION,
1298		.name          = "conntrack",
1299		.revision      = 2,
1300		.family        = NFPROTO_IPV4,
1301		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1302		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1303		.help          = conntrack_mt_help,
1304		.parse         = conntrack2_mt4_parse,
1305		.final_check   = conntrack_mt_check,
1306		.print         = conntrack2_mt_print,
1307		.save          = conntrack2_mt_save,
1308		.extra_opts    = conntrack_mt_opts,
1309	},
1310	{
1311		.version       = XTABLES_VERSION,
1312		.name          = "conntrack",
1313		.revision      = 2,
1314		.family        = NFPROTO_IPV6,
1315		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1316		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1317		.help          = conntrack_mt_help,
1318		.parse         = conntrack2_mt6_parse,
1319		.final_check   = conntrack_mt_check,
1320		.print         = conntrack2_mt6_print,
1321		.save          = conntrack2_mt6_save,
1322		.extra_opts    = conntrack_mt_opts,
1323	},
1324	{
1325		.version       = XTABLES_VERSION,
1326		.name          = "conntrack",
1327		.revision      = 3,
1328		.family        = NFPROTO_IPV4,
1329		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1330		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1331		.help          = conntrack_mt_help,
1332		.parse         = conntrack3_mt4_parse,
1333		.final_check   = conntrack_mt_check,
1334		.print         = conntrack3_mt_print,
1335		.save          = conntrack3_mt_save,
1336		.extra_opts    = conntrack_mt_opts,
1337	},
1338	{
1339		.version       = XTABLES_VERSION,
1340		.name          = "conntrack",
1341		.revision      = 3,
1342		.family        = NFPROTO_IPV6,
1343		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1344		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1345		.help          = conntrack_mt_help,
1346		.parse         = conntrack3_mt6_parse,
1347		.final_check   = conntrack_mt_check,
1348		.print         = conntrack3_mt6_print,
1349		.save          = conntrack3_mt6_save,
1350		.extra_opts    = conntrack_mt_opts,
1351	},
1352};
1353
1354void _init(void)
1355{
1356	xtables_register_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg));
1357}
1358