libxt_conntrack.c revision 10dbcd0bfb5a62a71a706d11134f83b0539f4dd3
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/*
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *	libxt_conntrack
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *	Shared library add-on to iptables for conntrack matching support.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *	GPL (C) 2001  Marc Boucher (marc@mbsi.ca).
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *	Copyright © CC Computer Consultants GmbH, 2007 - 2008
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *	Jan Engelhardt <jengelh@computergmbh.de>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdbool.h>
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdint.h>
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <stdio.h>
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdlib.h>
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string.h>
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <xtables.h>
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <linux/netfilter/xt_conntrack.h>
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <linux/netfilter/nf_conntrack_common.h>
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)struct ip_conntrack_old_tuple {
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct {
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		__be32 ip;
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		union {
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			__u16 all;
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		} u;
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	} src;
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct {
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)		__be32 ip;
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		union {
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			__u16 all;
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		} u;
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		/* The protocol. */
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		__u16 protonum;
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	} dst;
35};
36
37struct xt_conntrack_info {
38	unsigned int statemask, statusmask;
39
40	struct ip_conntrack_old_tuple tuple[IP_CT_DIR_MAX];
41	struct in_addr sipmsk[IP_CT_DIR_MAX], dipmsk[IP_CT_DIR_MAX];
42
43	unsigned long expires_min, expires_max;
44
45	/* Flags word */
46	uint8_t flags;
47	/* Inverse flags */
48	uint8_t invflags;
49};
50
51enum {
52	O_CTSTATE = 0,
53	O_CTPROTO,
54	O_CTORIGSRC,
55	O_CTORIGDST,
56	O_CTREPLSRC,
57	O_CTREPLDST,
58	O_CTORIGSRCPORT,
59	O_CTORIGDSTPORT,
60	O_CTREPLSRCPORT,
61	O_CTREPLDSTPORT,
62	O_CTSTATUS,
63	O_CTEXPIRE,
64	O_CTDIR,
65};
66
67static void conntrack_mt_help(void)
68{
69	printf(
70"conntrack match options:\n"
71"[!] --ctstate {INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED|SNAT|DNAT}[,...]\n"
72"                               State(s) to match\n"
73"[!] --ctproto proto            Protocol to match; by number or name, e.g. \"tcp\"\n"
74"[!] --ctorigsrc address[/mask]\n"
75"[!] --ctorigdst address[/mask]\n"
76"[!] --ctreplsrc address[/mask]\n"
77"[!] --ctrepldst address[/mask]\n"
78"                               Original/Reply source/destination address\n"
79"[!] --ctorigsrcport port\n"
80"[!] --ctorigdstport port\n"
81"[!] --ctreplsrcport port\n"
82"[!] --ctrepldstport port\n"
83"                               TCP/UDP/SCTP orig./reply source/destination port\n"
84"[!] --ctstatus {NONE|EXPECTED|SEEN_REPLY|ASSURED|CONFIRMED}[,...]\n"
85"                               Status(es) to match\n"
86"[!] --ctexpire time[:time]     Match remaining lifetime in seconds against\n"
87"                               value or range of values (inclusive)\n"
88"    --ctdir {ORIGINAL|REPLY}   Flow direction of packet\n");
89}
90
91#define s struct xt_conntrack_info /* for v0 */
92static const struct xt_option_entry conntrack_mt_opts_v0[] = {
93	{.name = "ctstate", .id = O_CTSTATE, .type = XTTYPE_STRING,
94	 .flags = XTOPT_INVERT},
95	{.name = "ctproto", .id = O_CTPROTO, .type = XTTYPE_PROTOCOL,
96	 .flags = XTOPT_INVERT},
97	{.name = "ctorigsrc", .id = O_CTORIGSRC, .type = XTTYPE_HOST,
98	 .flags = XTOPT_INVERT},
99	{.name = "ctorigdst", .id = O_CTORIGDST, .type = XTTYPE_HOST,
100	 .flags = XTOPT_INVERT},
101	{.name = "ctreplsrc", .id = O_CTREPLSRC, .type = XTTYPE_HOST,
102	 .flags = XTOPT_INVERT},
103	{.name = "ctrepldst", .id = O_CTREPLDST, .type = XTTYPE_HOST,
104	 .flags = XTOPT_INVERT},
105	{.name = "ctstatus", .id = O_CTSTATUS, .type = XTTYPE_STRING,
106	 .flags = XTOPT_INVERT},
107	{.name = "ctexpire", .id = O_CTEXPIRE, .type = XTTYPE_UINT32RC,
108	 .flags = XTOPT_INVERT},
109	XTOPT_TABLEEND,
110};
111#undef s
112
113#define s struct xt_conntrack_mtinfo3 /* for v1-v3 */
114/* We exploit the fact that v1-v3 share the same layout */
115static const struct xt_option_entry conntrack_mt_opts[] = {
116	{.name = "ctstate", .id = O_CTSTATE, .type = XTTYPE_STRING,
117	 .flags = XTOPT_INVERT},
118	{.name = "ctproto", .id = O_CTPROTO, .type = XTTYPE_PROTOCOL,
119	 .flags = XTOPT_INVERT},
120	{.name = "ctorigsrc", .id = O_CTORIGSRC, .type = XTTYPE_HOSTMASK,
121	 .flags = XTOPT_INVERT},
122	{.name = "ctorigdst", .id = O_CTORIGDST, .type = XTTYPE_HOSTMASK,
123	 .flags = XTOPT_INVERT},
124	{.name = "ctreplsrc", .id = O_CTREPLSRC, .type = XTTYPE_HOSTMASK,
125	 .flags = XTOPT_INVERT},
126	{.name = "ctrepldst", .id = O_CTREPLDST, .type = XTTYPE_HOSTMASK,
127	 .flags = XTOPT_INVERT},
128	{.name = "ctstatus", .id = O_CTSTATUS, .type = XTTYPE_STRING,
129	 .flags = XTOPT_INVERT},
130	{.name = "ctexpire", .id = O_CTEXPIRE, .type = XTTYPE_UINT32RC,
131	 .flags = XTOPT_INVERT},
132	{.name = "ctorigsrcport", .id = O_CTORIGSRCPORT, .type = XTTYPE_PORTRC,
133	 .flags = XTOPT_INVERT},
134	{.name = "ctorigdstport", .id = O_CTORIGDSTPORT, .type = XTTYPE_PORTRC,
135	 .flags = XTOPT_INVERT},
136	{.name = "ctreplsrcport", .id = O_CTREPLSRCPORT, .type = XTTYPE_PORTRC,
137	 .flags = XTOPT_INVERT},
138	{.name = "ctrepldstport", .id = O_CTREPLDSTPORT, .type = XTTYPE_PORTRC,
139	 .flags = XTOPT_INVERT},
140	{.name = "ctdir", .id = O_CTDIR, .type = XTTYPE_STRING},
141	XTOPT_TABLEEND,
142};
143#undef s
144
145static int
146parse_state(const char *state, size_t len, struct xt_conntrack_info *sinfo)
147{
148	if (strncasecmp(state, "INVALID", len) == 0)
149		sinfo->statemask |= XT_CONNTRACK_STATE_INVALID;
150	else if (strncasecmp(state, "NEW", len) == 0)
151		sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_NEW);
152	else if (strncasecmp(state, "ESTABLISHED", len) == 0)
153		sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
154	else if (strncasecmp(state, "RELATED", len) == 0)
155		sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
156	else if (strncasecmp(state, "UNTRACKED", len) == 0)
157		sinfo->statemask |= XT_CONNTRACK_STATE_UNTRACKED;
158	else if (strncasecmp(state, "SNAT", len) == 0)
159		sinfo->statemask |= XT_CONNTRACK_STATE_SNAT;
160	else if (strncasecmp(state, "DNAT", len) == 0)
161		sinfo->statemask |= XT_CONNTRACK_STATE_DNAT;
162	else
163		return 0;
164	return 1;
165}
166
167static void
168parse_states(const char *arg, struct xt_conntrack_info *sinfo)
169{
170	const char *comma;
171
172	while ((comma = strchr(arg, ',')) != NULL) {
173		if (comma == arg || !parse_state(arg, comma-arg, sinfo))
174			xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg);
175		arg = comma+1;
176	}
177	if (!*arg)
178		xtables_error(PARAMETER_PROBLEM, "\"--ctstate\" requires a list of "
179					      "states with no spaces, e.g. "
180					      "ESTABLISHED,RELATED");
181	if (strlen(arg) == 0 || !parse_state(arg, strlen(arg), sinfo))
182		xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg);
183}
184
185static bool
186conntrack_ps_state(struct xt_conntrack_mtinfo3 *info, const char *state,
187                   size_t z)
188{
189	if (strncasecmp(state, "INVALID", z) == 0)
190		info->state_mask |= XT_CONNTRACK_STATE_INVALID;
191	else if (strncasecmp(state, "NEW", z) == 0)
192		info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_NEW);
193	else if (strncasecmp(state, "ESTABLISHED", z) == 0)
194		info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
195	else if (strncasecmp(state, "RELATED", z) == 0)
196		info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
197	else if (strncasecmp(state, "UNTRACKED", z) == 0)
198		info->state_mask |= XT_CONNTRACK_STATE_UNTRACKED;
199	else if (strncasecmp(state, "SNAT", z) == 0)
200		info->state_mask |= XT_CONNTRACK_STATE_SNAT;
201	else if (strncasecmp(state, "DNAT", z) == 0)
202		info->state_mask |= XT_CONNTRACK_STATE_DNAT;
203	else
204		return false;
205	return true;
206}
207
208static void
209conntrack_ps_states(struct xt_conntrack_mtinfo3 *info, const char *arg)
210{
211	const char *comma;
212
213	while ((comma = strchr(arg, ',')) != NULL) {
214		if (comma == arg || !conntrack_ps_state(info, arg, comma - arg))
215			xtables_error(PARAMETER_PROBLEM,
216			           "Bad ctstate \"%s\"", arg);
217		arg = comma + 1;
218	}
219
220	if (strlen(arg) == 0 || !conntrack_ps_state(info, arg, strlen(arg)))
221		xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg);
222}
223
224static int
225parse_status(const char *status, size_t len, struct xt_conntrack_info *sinfo)
226{
227	if (strncasecmp(status, "NONE", len) == 0)
228		sinfo->statusmask |= 0;
229	else if (strncasecmp(status, "EXPECTED", len) == 0)
230		sinfo->statusmask |= IPS_EXPECTED;
231	else if (strncasecmp(status, "SEEN_REPLY", len) == 0)
232		sinfo->statusmask |= IPS_SEEN_REPLY;
233	else if (strncasecmp(status, "ASSURED", len) == 0)
234		sinfo->statusmask |= IPS_ASSURED;
235#ifdef IPS_CONFIRMED
236	else if (strncasecmp(status, "CONFIRMED", len) == 0)
237		sinfo->statusmask |= IPS_CONFIRMED;
238#endif
239	else
240		return 0;
241	return 1;
242}
243
244static void
245parse_statuses(const char *arg, struct xt_conntrack_info *sinfo)
246{
247	const char *comma;
248
249	while ((comma = strchr(arg, ',')) != NULL) {
250		if (comma == arg || !parse_status(arg, comma-arg, sinfo))
251			xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg);
252		arg = comma+1;
253	}
254
255	if (strlen(arg) == 0 || !parse_status(arg, strlen(arg), sinfo))
256		xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg);
257}
258
259static bool
260conntrack_ps_status(struct xt_conntrack_mtinfo3 *info, const char *status,
261                    size_t z)
262{
263	if (strncasecmp(status, "NONE", z) == 0)
264		info->status_mask |= 0;
265	else if (strncasecmp(status, "EXPECTED", z) == 0)
266		info->status_mask |= IPS_EXPECTED;
267	else if (strncasecmp(status, "SEEN_REPLY", z) == 0)
268		info->status_mask |= IPS_SEEN_REPLY;
269	else if (strncasecmp(status, "ASSURED", z) == 0)
270		info->status_mask |= IPS_ASSURED;
271	else if (strncasecmp(status, "CONFIRMED", z) == 0)
272		info->status_mask |= IPS_CONFIRMED;
273	else
274		return false;
275	return true;
276}
277
278static void
279conntrack_ps_statuses(struct xt_conntrack_mtinfo3 *info, const char *arg)
280{
281	const char *comma;
282
283	while ((comma = strchr(arg, ',')) != NULL) {
284		if (comma == arg || !conntrack_ps_status(info, arg, comma - arg))
285			xtables_error(PARAMETER_PROBLEM,
286			           "Bad ctstatus \"%s\"", arg);
287		arg = comma + 1;
288	}
289
290	if (strlen(arg) == 0 || !conntrack_ps_status(info, arg, strlen(arg)))
291		xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg);
292}
293
294static unsigned long
295parse_expire(const char *s)
296{
297	unsigned int len;
298
299	if (!xtables_strtoui(s, NULL, &len, 0, UINT32_MAX))
300		xtables_error(PARAMETER_PROBLEM, "expire value invalid: \"%s\"\n", s);
301	else
302		return len;
303}
304
305/* If a single value is provided, min and max are both set to the value */
306static void
307parse_expires(const char *s, struct xt_conntrack_info *sinfo)
308{
309	char *buffer;
310	char *cp;
311
312	buffer = strdup(s);
313	if ((cp = strchr(buffer, ':')) == NULL)
314		sinfo->expires_min = sinfo->expires_max =
315			parse_expire(buffer);
316	else {
317		*cp = '\0';
318		cp++;
319
320		sinfo->expires_min = buffer[0] ? parse_expire(buffer) : 0;
321		sinfo->expires_max = cp[0]
322			? parse_expire(cp)
323			: (unsigned long)-1;
324	}
325	free(buffer);
326
327	if (sinfo->expires_min > sinfo->expires_max)
328		xtables_error(PARAMETER_PROBLEM,
329		           "expire min. range value `%lu' greater than max. "
330		           "range value `%lu'", sinfo->expires_min, sinfo->expires_max);
331}
332
333static void
334conntrack_ps_expires(struct xt_conntrack_mtinfo3 *info, const char *s)
335{
336	unsigned int min, max;
337	char *end;
338
339	if (!xtables_strtoui(s, &end, &min, 0, UINT32_MAX))
340		xtables_param_act(XTF_BAD_VALUE, "conntrack", "--expires", s);
341	max = min;
342	if (*end == ':')
343		if (!xtables_strtoui(end + 1, &end, &max, 0, UINT32_MAX))
344			xtables_param_act(XTF_BAD_VALUE, "conntrack", "--expires", s);
345	if (*end != '\0')
346		xtables_param_act(XTF_BAD_VALUE, "conntrack", "--expires", s);
347
348	if (min > max)
349		xtables_error(PARAMETER_PROBLEM,
350		           "expire min. range value \"%u\" greater than max. "
351		           "range value \"%u\"", min, max);
352
353	info->expires_min = min;
354	info->expires_max = max;
355}
356
357static void conntrack_parse(struct xt_option_call *cb)
358{
359	struct xt_conntrack_info *sinfo = cb->data;
360
361	xtables_option_parse(cb);
362	switch (cb->entry->id) {
363	case O_CTSTATE:
364		parse_states(cb->arg, sinfo);
365		if (cb->invert)
366			sinfo->invflags |= XT_CONNTRACK_STATE;
367		break;
368	case O_CTPROTO:
369		if (cb->invert)
370			sinfo->invflags |= XT_CONNTRACK_PROTO;
371		sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = cb->val.protocol;
372
373		if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0
374		    && (sinfo->invflags & XT_INV_PROTO))
375			xtables_error(PARAMETER_PROBLEM,
376				   "rule would never match protocol");
377
378		sinfo->flags |= XT_CONNTRACK_PROTO;
379		break;
380	case O_CTORIGSRC:
381		if (cb->invert)
382			sinfo->invflags |= XT_CONNTRACK_ORIGSRC;
383		sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = cb->val.haddr.ip;
384		sinfo->flags |= XT_CONNTRACK_ORIGSRC;
385		break;
386	case O_CTORIGDST:
387		if (cb->invert)
388			sinfo->invflags |= XT_CONNTRACK_ORIGDST;
389		sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = cb->val.haddr.ip;
390		sinfo->flags |= XT_CONNTRACK_ORIGDST;
391		break;
392	case O_CTREPLSRC:
393		if (cb->invert)
394			sinfo->invflags |= XT_CONNTRACK_REPLSRC;
395		sinfo->tuple[IP_CT_DIR_REPLY].src.ip = cb->val.haddr.ip;
396		sinfo->flags |= XT_CONNTRACK_REPLSRC;
397		break;
398	case O_CTREPLDST:
399		if (cb->invert)
400			sinfo->invflags |= XT_CONNTRACK_REPLDST;
401		sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = cb->val.haddr.ip;
402		sinfo->flags |= XT_CONNTRACK_REPLDST;
403		break;
404	case O_CTSTATUS:
405		parse_statuses(cb->arg, sinfo);
406		if (cb->invert)
407			sinfo->invflags |= XT_CONNTRACK_STATUS;
408		sinfo->flags |= XT_CONNTRACK_STATUS;
409		break;
410	case O_CTEXPIRE:
411		parse_expires(cb->arg, sinfo);
412		if (cb->invert)
413			sinfo->invflags |= XT_CONNTRACK_EXPIRES;
414		sinfo->flags |= XT_CONNTRACK_EXPIRES;
415		break;
416	}
417}
418
419static void conntrack_mt_parse(struct xt_option_call *cb, uint8_t rev)
420{
421	struct xt_conntrack_mtinfo3 *info = cb->data;
422
423	xtables_option_parse(cb);
424	switch (cb->entry->id) {
425	case O_CTSTATE:
426		conntrack_ps_states(info, cb->arg);
427		info->match_flags |= XT_CONNTRACK_STATE;
428		if (cb->invert)
429			info->invert_flags |= XT_CONNTRACK_STATE;
430		break;
431	case O_CTPROTO:
432		info->l4proto = cb->val.protocol;
433		if (info->l4proto == 0 && (info->invert_flags & XT_INV_PROTO))
434			xtables_error(PARAMETER_PROBLEM, "conntrack: rule would "
435			           "never match protocol");
436
437		info->match_flags |= XT_CONNTRACK_PROTO;
438		if (cb->invert)
439			info->invert_flags |= XT_CONNTRACK_PROTO;
440		break;
441	case O_CTORIGSRC:
442		info->origsrc_addr = cb->val.haddr;
443		info->origsrc_mask = cb->val.hmask;
444		info->match_flags |= XT_CONNTRACK_ORIGSRC;
445		if (cb->invert)
446			info->invert_flags |= XT_CONNTRACK_ORIGSRC;
447		break;
448	case O_CTORIGDST:
449		info->origdst_addr = cb->val.haddr;
450		info->origdst_mask = cb->val.hmask;
451		info->match_flags |= XT_CONNTRACK_ORIGDST;
452		if (cb->invert)
453			info->invert_flags |= XT_CONNTRACK_ORIGDST;
454		break;
455	case O_CTREPLSRC:
456		info->replsrc_addr = cb->val.haddr;
457		info->replsrc_mask = cb->val.hmask;
458		info->match_flags |= XT_CONNTRACK_REPLSRC;
459		if (cb->invert)
460			info->invert_flags |= XT_CONNTRACK_REPLSRC;
461		break;
462	case O_CTREPLDST:
463		info->repldst_addr = cb->val.haddr;
464		info->repldst_mask = cb->val.hmask;
465		info->match_flags |= XT_CONNTRACK_REPLDST;
466		if (cb->invert)
467			info->invert_flags |= XT_CONNTRACK_REPLDST;
468		break;
469	case O_CTSTATUS:
470		conntrack_ps_statuses(info, cb->arg);
471		info->match_flags |= XT_CONNTRACK_STATUS;
472		if (cb->invert)
473			info->invert_flags |= XT_CONNTRACK_STATUS;
474		break;
475	case O_CTEXPIRE:
476		conntrack_ps_expires(info, cb->arg);
477		info->match_flags |= XT_CONNTRACK_EXPIRES;
478		if (cb->invert)
479			info->invert_flags |= XT_CONNTRACK_EXPIRES;
480		break;
481	case O_CTORIGSRCPORT:
482		info->origsrc_port = cb->val.port_range[0];
483		info->origsrc_port = (cb->nvals == 2) ? cb->val.port_range[1] :
484		                     cb->val.port_range[0];
485		info->match_flags |= XT_CONNTRACK_ORIGSRC_PORT;
486		if (cb->invert)
487			info->invert_flags |= XT_CONNTRACK_ORIGSRC_PORT;
488		break;
489	case O_CTORIGDSTPORT:
490		info->origdst_port = cb->val.port_range[0];
491		info->origdst_port = (cb->nvals == 2) ? cb->val.port_range[1] :
492		                     cb->val.port_range[0];
493		info->match_flags |= XT_CONNTRACK_ORIGDST_PORT;
494		if (cb->invert)
495			info->invert_flags |= XT_CONNTRACK_ORIGDST_PORT;
496		break;
497	case O_CTREPLSRCPORT:
498		info->replsrc_port = cb->val.port_range[0];
499		info->replsrc_port = (cb->nvals == 2) ? cb->val.port_range[1] :
500		                     cb->val.port_range[0];
501		info->match_flags |= XT_CONNTRACK_REPLSRC_PORT;
502		if (cb->invert)
503			info->invert_flags |= XT_CONNTRACK_REPLSRC_PORT;
504		break;
505	case O_CTREPLDSTPORT:
506		info->repldst_port = cb->val.port_range[0];
507		info->repldst_port = (cb->nvals == 2) ? cb->val.port_range[1] :
508		                     cb->val.port_range[0];
509		info->match_flags |= XT_CONNTRACK_REPLDST_PORT;
510		if (cb->invert)
511			info->invert_flags |= XT_CONNTRACK_REPLDST_PORT;
512		break;
513	case O_CTDIR:
514		if (strcasecmp(cb->arg, "ORIGINAL") == 0) {
515			info->match_flags  |= XT_CONNTRACK_DIRECTION;
516			info->invert_flags &= ~XT_CONNTRACK_DIRECTION;
517		} else if (strcasecmp(cb->arg, "REPLY") == 0) {
518			info->match_flags  |= XT_CONNTRACK_DIRECTION;
519			info->invert_flags |= XT_CONNTRACK_DIRECTION;
520		} else {
521			xtables_param_act(XTF_BAD_VALUE, "conntrack", "--ctdir", cb->arg);
522		}
523		break;
524	}
525}
526
527#define cinfo_transform(r, l) \
528	do { \
529		memcpy((r), (l), offsetof(typeof(*(l)), state_mask)); \
530		(r)->state_mask  = (l)->state_mask; \
531		(r)->status_mask = (l)->status_mask; \
532	} while (false);
533
534static void conntrack1_mt_parse(struct xt_option_call *cb)
535{
536	struct xt_conntrack_mtinfo1 *info = cb->data;
537	struct xt_conntrack_mtinfo3 up;
538
539	memset(&up, 0, sizeof(up));
540	cinfo_transform(&up, info);
541	cb->data = &up;
542	conntrack_mt_parse(cb, 3);
543	if (up.origsrc_port != up.origsrc_port_high ||
544	    up.origdst_port != up.origdst_port_high ||
545	    up.replsrc_port != up.replsrc_port_high ||
546	    up.repldst_port != up.repldst_port_high)
547		xtables_error(PARAMETER_PROBLEM,
548			"conntrack rev 1 does not support port ranges");
549	cinfo_transform(info, &up);
550	cb->data = info;
551}
552
553static void conntrack2_mt_parse(struct xt_option_call *cb)
554{
555#define cinfo2_transform(r, l) \
556		memcpy((r), (l), offsetof(typeof(*(l)), sizeof(*info));
557
558	struct xt_conntrack_mtinfo2 *info = cb->data;
559	struct xt_conntrack_mtinfo3 up;
560
561	memset(&up, 0, sizeof(up));
562	memcpy(&up, info, sizeof(*info));
563	cb->data = &up;
564	conntrack_mt_parse(cb, 3);
565	if (up.origsrc_port != up.origsrc_port_high ||
566	    up.origdst_port != up.origdst_port_high ||
567	    up.replsrc_port != up.replsrc_port_high ||
568	    up.repldst_port != up.repldst_port_high)
569		xtables_error(PARAMETER_PROBLEM,
570			"conntrack rev 2 does not support port ranges");
571	memcpy(info, &up, sizeof(*info));
572	cb->data = info;
573#undef cinfo2_transform
574}
575
576static void conntrack3_mt_parse(struct xt_option_call *cb)
577{
578	conntrack_mt_parse(cb, 3);
579}
580
581static void conntrack_mt_check(struct xt_fcheck_call *cb)
582{
583	if (cb->xflags == 0)
584		xtables_error(PARAMETER_PROBLEM, "conntrack: At least one option "
585		           "is required");
586}
587
588static void
589print_state(unsigned int statemask)
590{
591	const char *sep = " ";
592
593	if (statemask & XT_CONNTRACK_STATE_INVALID) {
594		printf("%sINVALID", sep);
595		sep = ",";
596	}
597	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
598		printf("%sNEW", sep);
599		sep = ",";
600	}
601	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
602		printf("%sRELATED", sep);
603		sep = ",";
604	}
605	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
606		printf("%sESTABLISHED", sep);
607		sep = ",";
608	}
609	if (statemask & XT_CONNTRACK_STATE_UNTRACKED) {
610		printf("%sUNTRACKED", sep);
611		sep = ",";
612	}
613	if (statemask & XT_CONNTRACK_STATE_SNAT) {
614		printf("%sSNAT", sep);
615		sep = ",";
616	}
617	if (statemask & XT_CONNTRACK_STATE_DNAT) {
618		printf("%sDNAT", sep);
619		sep = ",";
620	}
621}
622
623static void
624print_status(unsigned int statusmask)
625{
626	const char *sep = " ";
627
628	if (statusmask & IPS_EXPECTED) {
629		printf("%sEXPECTED", sep);
630		sep = ",";
631	}
632	if (statusmask & IPS_SEEN_REPLY) {
633		printf("%sSEEN_REPLY", sep);
634		sep = ",";
635	}
636	if (statusmask & IPS_ASSURED) {
637		printf("%sASSURED", sep);
638		sep = ",";
639	}
640	if (statusmask & IPS_CONFIRMED) {
641		printf("%sCONFIRMED", sep);
642		sep = ",";
643	}
644	if (statusmask == 0)
645		printf("%sNONE", sep);
646}
647
648static void
649conntrack_dump_addr(const union nf_inet_addr *addr,
650                    const union nf_inet_addr *mask,
651                    unsigned int family, bool numeric)
652{
653	if (family == NFPROTO_IPV4) {
654		if (!numeric && addr->ip == 0) {
655			printf(" anywhere");
656			return;
657		}
658		if (numeric)
659			printf(" %s%s",
660			       xtables_ipaddr_to_numeric(&addr->in),
661			       xtables_ipmask_to_numeric(&mask->in));
662		else
663			printf(" %s%s",
664			       xtables_ipaddr_to_anyname(&addr->in),
665			       xtables_ipmask_to_numeric(&mask->in));
666	} else if (family == NFPROTO_IPV6) {
667		if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 &&
668		    addr->ip6[2] == 0 && addr->ip6[3] == 0) {
669			printf(" anywhere");
670			return;
671		}
672		if (numeric)
673			printf(" %s%s",
674			       xtables_ip6addr_to_numeric(&addr->in6),
675			       xtables_ip6mask_to_numeric(&mask->in6));
676		else
677			printf(" %s%s",
678			       xtables_ip6addr_to_anyname(&addr->in6),
679			       xtables_ip6mask_to_numeric(&mask->in6));
680	}
681}
682
683static void
684print_addr(const struct in_addr *addr, const struct in_addr *mask,
685           int inv, int numeric)
686{
687	char buf[BUFSIZ];
688
689	if (inv)
690		printf(" !");
691
692	if (mask->s_addr == 0L && !numeric)
693		printf(" %s", "anywhere");
694	else {
695		if (numeric)
696			strcpy(buf, xtables_ipaddr_to_numeric(addr));
697		else
698			strcpy(buf, xtables_ipaddr_to_anyname(addr));
699		strcat(buf, xtables_ipmask_to_numeric(mask));
700		printf(" %s", buf);
701	}
702}
703
704static void
705matchinfo_print(const void *ip, const struct xt_entry_match *match, int numeric, const char *optpfx)
706{
707	const struct xt_conntrack_info *sinfo = (const void *)match->data;
708
709	if(sinfo->flags & XT_CONNTRACK_STATE) {
710        	if (sinfo->invflags & XT_CONNTRACK_STATE)
711			printf(" !");
712		printf(" %sctstate", optpfx);
713		print_state(sinfo->statemask);
714	}
715
716	if(sinfo->flags & XT_CONNTRACK_PROTO) {
717        	if (sinfo->invflags & XT_CONNTRACK_PROTO)
718			printf(" !");
719		printf(" %sctproto", optpfx);
720		printf(" %u", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum);
721	}
722
723	if(sinfo->flags & XT_CONNTRACK_ORIGSRC) {
724		if (sinfo->invflags & XT_CONNTRACK_ORIGSRC)
725			printf(" !");
726		printf(" %sctorigsrc", optpfx);
727
728		print_addr(
729		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
730		    &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
731		    false,
732		    numeric);
733	}
734
735	if(sinfo->flags & XT_CONNTRACK_ORIGDST) {
736		if (sinfo->invflags & XT_CONNTRACK_ORIGDST)
737			printf(" !");
738		printf(" %sctorigdst", optpfx);
739
740		print_addr(
741		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
742		    &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
743		    false,
744		    numeric);
745	}
746
747	if(sinfo->flags & XT_CONNTRACK_REPLSRC) {
748		if (sinfo->invflags & XT_CONNTRACK_REPLSRC)
749			printf(" !");
750		printf(" %sctreplsrc", optpfx);
751
752		print_addr(
753		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
754		    &sinfo->sipmsk[IP_CT_DIR_REPLY],
755		    false,
756		    numeric);
757	}
758
759	if(sinfo->flags & XT_CONNTRACK_REPLDST) {
760		if (sinfo->invflags & XT_CONNTRACK_REPLDST)
761			printf(" !");
762		printf(" %sctrepldst", optpfx);
763
764		print_addr(
765		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
766		    &sinfo->dipmsk[IP_CT_DIR_REPLY],
767		    false,
768		    numeric);
769	}
770
771	if(sinfo->flags & XT_CONNTRACK_STATUS) {
772        	if (sinfo->invflags & XT_CONNTRACK_STATUS)
773			printf(" !");
774		printf(" %sctstatus", optpfx);
775		print_status(sinfo->statusmask);
776	}
777
778	if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
779        	if (sinfo->invflags & XT_CONNTRACK_EXPIRES)
780			printf(" !");
781		printf(" %sctexpire ", optpfx);
782
783        	if (sinfo->expires_max == sinfo->expires_min)
784			printf("%lu", sinfo->expires_min);
785        	else
786			printf("%lu:%lu", sinfo->expires_min, sinfo->expires_max);
787	}
788
789	if (sinfo->flags & XT_CONNTRACK_DIRECTION) {
790		if (sinfo->invflags & XT_CONNTRACK_DIRECTION)
791			printf(" %sctdir REPLY", optpfx);
792		else
793			printf(" %sctdir ORIGINAL", optpfx);
794	}
795
796}
797
798static void
799conntrack_dump_ports(const char *prefix, const char *opt,
800		     u_int16_t port_low, u_int16_t port_high)
801{
802	if (port_high == 0 || port_low == port_high)
803		printf(" %s%s %u", prefix, opt, port_low);
804	else
805		printf(" %s%s %u:%u", prefix, opt, port_low, port_high);
806}
807
808static void
809conntrack_dump(const struct xt_conntrack_mtinfo3 *info, const char *prefix,
810               unsigned int family, bool numeric, bool v3)
811{
812	if (info->match_flags & XT_CONNTRACK_STATE) {
813		if (info->invert_flags & XT_CONNTRACK_STATE)
814			printf(" !");
815		printf(" %sctstate", prefix);
816		print_state(info->state_mask);
817	}
818
819	if (info->match_flags & XT_CONNTRACK_PROTO) {
820		if (info->invert_flags & XT_CONNTRACK_PROTO)
821			printf(" !");
822		printf(" %sctproto %u", prefix, info->l4proto);
823	}
824
825	if (info->match_flags & XT_CONNTRACK_ORIGSRC) {
826		if (info->invert_flags & XT_CONNTRACK_ORIGSRC)
827			printf(" !");
828		printf(" %sctorigsrc", prefix);
829		conntrack_dump_addr(&info->origsrc_addr, &info->origsrc_mask,
830		                    family, numeric);
831	}
832
833	if (info->match_flags & XT_CONNTRACK_ORIGDST) {
834		if (info->invert_flags & XT_CONNTRACK_ORIGDST)
835			printf(" !");
836		printf(" %sctorigdst", prefix);
837		conntrack_dump_addr(&info->origdst_addr, &info->origdst_mask,
838		                    family, numeric);
839	}
840
841	if (info->match_flags & XT_CONNTRACK_REPLSRC) {
842		if (info->invert_flags & XT_CONNTRACK_REPLSRC)
843			printf(" !");
844		printf(" %sctreplsrc", prefix);
845		conntrack_dump_addr(&info->replsrc_addr, &info->replsrc_mask,
846		                    family, numeric);
847	}
848
849	if (info->match_flags & XT_CONNTRACK_REPLDST) {
850		if (info->invert_flags & XT_CONNTRACK_REPLDST)
851			printf(" !");
852		printf(" %sctrepldst", prefix);
853		conntrack_dump_addr(&info->repldst_addr, &info->repldst_mask,
854		                    family, numeric);
855	}
856
857	if (info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) {
858		if (info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT)
859			printf(" !");
860		conntrack_dump_ports(prefix, "ctorigsrcport",
861				     v3 ? info->origsrc_port : ntohs(info->origsrc_port),
862				     v3 ? info->origsrc_port_high : 0);
863	}
864
865	if (info->match_flags & XT_CONNTRACK_ORIGDST_PORT) {
866		if (info->invert_flags & XT_CONNTRACK_ORIGDST_PORT)
867			printf(" !");
868		conntrack_dump_ports(prefix, "ctorigdstport",
869				     v3 ? info->origdst_port : ntohs(info->origdst_port),
870				     v3 ? info->origdst_port_high : 0);
871	}
872
873	if (info->match_flags & XT_CONNTRACK_REPLSRC_PORT) {
874		if (info->invert_flags & XT_CONNTRACK_REPLSRC_PORT)
875			printf(" !");
876		conntrack_dump_ports(prefix, "ctreplsrcport",
877				     v3 ? info->replsrc_port : ntohs(info->replsrc_port),
878				     v3 ? info->replsrc_port_high : 0);
879	}
880
881	if (info->match_flags & XT_CONNTRACK_REPLDST_PORT) {
882		if (info->invert_flags & XT_CONNTRACK_REPLDST_PORT)
883			printf(" !");
884		conntrack_dump_ports(prefix, "ctrepldstport",
885				     v3 ? info->repldst_port : ntohs(info->repldst_port),
886				     v3 ? info->repldst_port_high : 0);
887	}
888
889	if (info->match_flags & XT_CONNTRACK_STATUS) {
890		if (info->invert_flags & XT_CONNTRACK_STATUS)
891			printf(" !");
892		printf(" %sctstatus", prefix);
893		print_status(info->status_mask);
894	}
895
896	if (info->match_flags & XT_CONNTRACK_EXPIRES) {
897		if (info->invert_flags & XT_CONNTRACK_EXPIRES)
898			printf(" !");
899		printf(" %sctexpire ", prefix);
900
901		if (info->expires_max == info->expires_min)
902			printf("%u", (unsigned int)info->expires_min);
903		else
904			printf("%u:%u", (unsigned int)info->expires_min,
905			       (unsigned int)info->expires_max);
906	}
907
908	if (info->match_flags & XT_CONNTRACK_DIRECTION) {
909		if (info->invert_flags & XT_CONNTRACK_DIRECTION)
910			printf(" %sctdir REPLY", prefix);
911		else
912			printf(" %sctdir ORIGINAL", prefix);
913	}
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 struct xtables_match conntrack_mt_reg[] = {
1022	{
1023		.version       = XTABLES_VERSION,
1024		.name          = "conntrack",
1025		.revision      = 0,
1026		.family        = NFPROTO_IPV4,
1027		.size          = XT_ALIGN(sizeof(struct xt_conntrack_info)),
1028		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_info)),
1029		.help          = conntrack_mt_help,
1030		.x6_parse      = conntrack_parse,
1031		.x6_fcheck     = conntrack_mt_check,
1032		.print         = conntrack_print,
1033		.save          = conntrack_save,
1034		.x6_options    = conntrack_mt_opts_v0,
1035	},
1036	{
1037		.version       = XTABLES_VERSION,
1038		.name          = "conntrack",
1039		.revision      = 1,
1040		.family        = NFPROTO_IPV4,
1041		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1042		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1043		.help          = conntrack_mt_help,
1044		.x6_parse      = conntrack1_mt_parse,
1045		.x6_fcheck     = conntrack_mt_check,
1046		.print         = conntrack1_mt4_print,
1047		.save          = conntrack1_mt4_save,
1048		.x6_options    = conntrack_mt_opts,
1049	},
1050	{
1051		.version       = XTABLES_VERSION,
1052		.name          = "conntrack",
1053		.revision      = 1,
1054		.family        = NFPROTO_IPV6,
1055		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1056		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1057		.help          = conntrack_mt_help,
1058		.x6_parse      = conntrack1_mt_parse,
1059		.x6_fcheck     = conntrack_mt_check,
1060		.print         = conntrack1_mt6_print,
1061		.save          = conntrack1_mt6_save,
1062		.x6_options    = conntrack_mt_opts,
1063	},
1064	{
1065		.version       = XTABLES_VERSION,
1066		.name          = "conntrack",
1067		.revision      = 2,
1068		.family        = NFPROTO_IPV4,
1069		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1070		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1071		.help          = conntrack_mt_help,
1072		.x6_parse      = conntrack2_mt_parse,
1073		.x6_fcheck     = conntrack_mt_check,
1074		.print         = conntrack2_mt_print,
1075		.save          = conntrack2_mt_save,
1076		.x6_options    = conntrack_mt_opts,
1077	},
1078	{
1079		.version       = XTABLES_VERSION,
1080		.name          = "conntrack",
1081		.revision      = 2,
1082		.family        = NFPROTO_IPV6,
1083		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1084		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1085		.help          = conntrack_mt_help,
1086		.x6_parse      = conntrack2_mt_parse,
1087		.x6_fcheck     = conntrack_mt_check,
1088		.print         = conntrack2_mt6_print,
1089		.save          = conntrack2_mt6_save,
1090		.x6_options    = conntrack_mt_opts,
1091	},
1092	{
1093		.version       = XTABLES_VERSION,
1094		.name          = "conntrack",
1095		.revision      = 3,
1096		.family        = NFPROTO_IPV4,
1097		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1098		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1099		.help          = conntrack_mt_help,
1100		.x6_parse      = conntrack3_mt_parse,
1101		.x6_fcheck     = conntrack_mt_check,
1102		.print         = conntrack3_mt_print,
1103		.save          = conntrack3_mt_save,
1104		.x6_options    = conntrack_mt_opts,
1105	},
1106	{
1107		.version       = XTABLES_VERSION,
1108		.name          = "conntrack",
1109		.revision      = 3,
1110		.family        = NFPROTO_IPV6,
1111		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1112		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1113		.help          = conntrack_mt_help,
1114		.x6_parse      = conntrack3_mt_parse,
1115		.x6_fcheck     = conntrack_mt_check,
1116		.print         = conntrack3_mt6_print,
1117		.save          = conntrack3_mt6_save,
1118		.x6_options    = conntrack_mt_opts,
1119	},
1120};
1121
1122void _init(void)
1123{
1124	xtables_register_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg));
1125}
1126