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