• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/nf_conntrack_common.h>
17 
18 struct ip_conntrack_old_tuple {
19 	struct {
20 		__be32 ip;
21 		union {
22 			__u16 all;
23 		} u;
24 	} src;
25 
26 	struct {
27 		__be32 ip;
28 		union {
29 			__u16 all;
30 		} u;
31 
32 		/* The protocol. */
33 		__u16 protonum;
34 	} dst;
35 };
36 
37 struct 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 
51 enum {
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 
conntrack_mt_help(void)67 static 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 */
92 static 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 */
115 static 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 
145 static int
parse_state(const char * state,size_t len,struct xt_conntrack_info * sinfo)146 parse_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 
167 static void
parse_states(const char * arg,struct xt_conntrack_info * sinfo)168 parse_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 
185 static bool
conntrack_ps_state(struct xt_conntrack_mtinfo3 * info,const char * state,size_t z)186 conntrack_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 
208 static void
conntrack_ps_states(struct xt_conntrack_mtinfo3 * info,const char * arg)209 conntrack_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 
224 static int
parse_status(const char * status,size_t len,struct xt_conntrack_info * sinfo)225 parse_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 
244 static void
parse_statuses(const char * arg,struct xt_conntrack_info * sinfo)245 parse_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 
259 static bool
conntrack_ps_status(struct xt_conntrack_mtinfo3 * info,const char * status,size_t z)260 conntrack_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 
278 static void
conntrack_ps_statuses(struct xt_conntrack_mtinfo3 * info,const char * arg)279 conntrack_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 
conntrack_parse(struct xt_option_call * cb)294 static void conntrack_parse(struct xt_option_call *cb)
295 {
296 	struct xt_conntrack_info *sinfo = cb->data;
297 
298 	xtables_option_parse(cb);
299 	switch (cb->entry->id) {
300 	case O_CTSTATE:
301 		parse_states(cb->arg, sinfo);
302 		if (cb->invert)
303 			sinfo->invflags |= XT_CONNTRACK_STATE;
304 		break;
305 	case O_CTPROTO:
306 		if (cb->invert)
307 			sinfo->invflags |= XT_CONNTRACK_PROTO;
308 		sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = cb->val.protocol;
309 
310 		if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0
311 		    && (sinfo->invflags & XT_INV_PROTO))
312 			xtables_error(PARAMETER_PROBLEM,
313 				   "rule would never match protocol");
314 
315 		sinfo->flags |= XT_CONNTRACK_PROTO;
316 		break;
317 	case O_CTORIGSRC:
318 		if (cb->invert)
319 			sinfo->invflags |= XT_CONNTRACK_ORIGSRC;
320 		sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = cb->val.haddr.ip;
321 		sinfo->flags |= XT_CONNTRACK_ORIGSRC;
322 		break;
323 	case O_CTORIGDST:
324 		if (cb->invert)
325 			sinfo->invflags |= XT_CONNTRACK_ORIGDST;
326 		sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = cb->val.haddr.ip;
327 		sinfo->flags |= XT_CONNTRACK_ORIGDST;
328 		break;
329 	case O_CTREPLSRC:
330 		if (cb->invert)
331 			sinfo->invflags |= XT_CONNTRACK_REPLSRC;
332 		sinfo->tuple[IP_CT_DIR_REPLY].src.ip = cb->val.haddr.ip;
333 		sinfo->flags |= XT_CONNTRACK_REPLSRC;
334 		break;
335 	case O_CTREPLDST:
336 		if (cb->invert)
337 			sinfo->invflags |= XT_CONNTRACK_REPLDST;
338 		sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = cb->val.haddr.ip;
339 		sinfo->flags |= XT_CONNTRACK_REPLDST;
340 		break;
341 	case O_CTSTATUS:
342 		parse_statuses(cb->arg, sinfo);
343 		if (cb->invert)
344 			sinfo->invflags |= XT_CONNTRACK_STATUS;
345 		sinfo->flags |= XT_CONNTRACK_STATUS;
346 		break;
347 	case O_CTEXPIRE:
348 		sinfo->expires_min = cb->val.u32_range[0];
349 		sinfo->expires_max = cb->val.u32_range[0];
350 		if (cb->nvals >= 2)
351 			sinfo->expires_max = cb->val.u32_range[1];
352 		if (cb->invert)
353 			sinfo->invflags |= XT_CONNTRACK_EXPIRES;
354 		sinfo->flags |= XT_CONNTRACK_EXPIRES;
355 		break;
356 	}
357 }
358 
conntrack_mt_parse(struct xt_option_call * cb,uint8_t rev)359 static void conntrack_mt_parse(struct xt_option_call *cb, uint8_t rev)
360 {
361 	struct xt_conntrack_mtinfo3 *info = cb->data;
362 
363 	xtables_option_parse(cb);
364 	switch (cb->entry->id) {
365 	case O_CTSTATE:
366 		conntrack_ps_states(info, cb->arg);
367 		info->match_flags |= XT_CONNTRACK_STATE;
368 		if (cb->invert)
369 			info->invert_flags |= XT_CONNTRACK_STATE;
370 		break;
371 	case O_CTPROTO:
372 		info->l4proto = cb->val.protocol;
373 		if (info->l4proto == 0 && (info->invert_flags & XT_INV_PROTO))
374 			xtables_error(PARAMETER_PROBLEM, "conntrack: rule would "
375 			           "never match protocol");
376 
377 		info->match_flags |= XT_CONNTRACK_PROTO;
378 		if (cb->invert)
379 			info->invert_flags |= XT_CONNTRACK_PROTO;
380 		break;
381 	case O_CTORIGSRC:
382 		info->origsrc_addr = cb->val.haddr;
383 		info->origsrc_mask = cb->val.hmask;
384 		info->match_flags |= XT_CONNTRACK_ORIGSRC;
385 		if (cb->invert)
386 			info->invert_flags |= XT_CONNTRACK_ORIGSRC;
387 		break;
388 	case O_CTORIGDST:
389 		info->origdst_addr = cb->val.haddr;
390 		info->origdst_mask = cb->val.hmask;
391 		info->match_flags |= XT_CONNTRACK_ORIGDST;
392 		if (cb->invert)
393 			info->invert_flags |= XT_CONNTRACK_ORIGDST;
394 		break;
395 	case O_CTREPLSRC:
396 		info->replsrc_addr = cb->val.haddr;
397 		info->replsrc_mask = cb->val.hmask;
398 		info->match_flags |= XT_CONNTRACK_REPLSRC;
399 		if (cb->invert)
400 			info->invert_flags |= XT_CONNTRACK_REPLSRC;
401 		break;
402 	case O_CTREPLDST:
403 		info->repldst_addr = cb->val.haddr;
404 		info->repldst_mask = cb->val.hmask;
405 		info->match_flags |= XT_CONNTRACK_REPLDST;
406 		if (cb->invert)
407 			info->invert_flags |= XT_CONNTRACK_REPLDST;
408 		break;
409 	case O_CTSTATUS:
410 		conntrack_ps_statuses(info, cb->arg);
411 		info->match_flags |= XT_CONNTRACK_STATUS;
412 		if (cb->invert)
413 			info->invert_flags |= XT_CONNTRACK_STATUS;
414 		break;
415 	case O_CTEXPIRE:
416 		info->expires_min = cb->val.u32_range[0];
417 		info->expires_max = cb->val.u32_range[0];
418 		if (cb->nvals >= 2)
419 			info->expires_max = cb->val.u32_range[1];
420 		info->match_flags |= XT_CONNTRACK_EXPIRES;
421 		if (cb->invert)
422 			info->invert_flags |= XT_CONNTRACK_EXPIRES;
423 		break;
424 	case O_CTORIGSRCPORT:
425 		info->origsrc_port = cb->val.port_range[0];
426 		info->origsrc_port_high = cb->val.port_range[cb->nvals >= 2];
427 		info->match_flags |= XT_CONNTRACK_ORIGSRC_PORT;
428 		if (cb->invert)
429 			info->invert_flags |= XT_CONNTRACK_ORIGSRC_PORT;
430 		break;
431 	case O_CTORIGDSTPORT:
432 		info->origdst_port = cb->val.port_range[0];
433 		info->origdst_port_high = cb->val.port_range[cb->nvals >= 2];
434 		info->match_flags |= XT_CONNTRACK_ORIGDST_PORT;
435 		if (cb->invert)
436 			info->invert_flags |= XT_CONNTRACK_ORIGDST_PORT;
437 		break;
438 	case O_CTREPLSRCPORT:
439 		info->replsrc_port = cb->val.port_range[0];
440 		info->replsrc_port_high = cb->val.port_range[cb->nvals >= 2];
441 		info->match_flags |= XT_CONNTRACK_REPLSRC_PORT;
442 		if (cb->invert)
443 			info->invert_flags |= XT_CONNTRACK_REPLSRC_PORT;
444 		break;
445 	case O_CTREPLDSTPORT:
446 		info->repldst_port = cb->val.port_range[0];
447 		info->repldst_port_high = cb->val.port_range[cb->nvals >= 2];
448 		info->match_flags |= XT_CONNTRACK_REPLDST_PORT;
449 		if (cb->invert)
450 			info->invert_flags |= XT_CONNTRACK_REPLDST_PORT;
451 		break;
452 	case O_CTDIR:
453 		if (strcasecmp(cb->arg, "ORIGINAL") == 0) {
454 			info->match_flags  |= XT_CONNTRACK_DIRECTION;
455 			info->invert_flags &= ~XT_CONNTRACK_DIRECTION;
456 		} else if (strcasecmp(cb->arg, "REPLY") == 0) {
457 			info->match_flags  |= XT_CONNTRACK_DIRECTION;
458 			info->invert_flags |= XT_CONNTRACK_DIRECTION;
459 		} else {
460 			xtables_param_act(XTF_BAD_VALUE, "conntrack", "--ctdir", cb->arg);
461 		}
462 		break;
463 	}
464 }
465 
466 #define cinfo_transform(r, l) \
467 	do { \
468 		memcpy((r), (l), offsetof(typeof(*(l)), state_mask)); \
469 		(r)->state_mask  = (l)->state_mask; \
470 		(r)->status_mask = (l)->status_mask; \
471 	} while (false);
472 
conntrack1_mt_parse(struct xt_option_call * cb)473 static void conntrack1_mt_parse(struct xt_option_call *cb)
474 {
475 	struct xt_conntrack_mtinfo1 *info = cb->data;
476 	struct xt_conntrack_mtinfo3 up;
477 
478 	memset(&up, 0, sizeof(up));
479 	cinfo_transform(&up, info);
480 	up.origsrc_port_high = up.origsrc_port;
481 	up.origdst_port_high = up.origdst_port;
482 	up.replsrc_port_high = up.replsrc_port;
483 	up.repldst_port_high = up.repldst_port;
484 	cb->data = &up;
485 	conntrack_mt_parse(cb, 3);
486 	if (up.origsrc_port != up.origsrc_port_high ||
487 	    up.origdst_port != up.origdst_port_high ||
488 	    up.replsrc_port != up.replsrc_port_high ||
489 	    up.repldst_port != up.repldst_port_high)
490 		xtables_error(PARAMETER_PROBLEM,
491 			"conntrack rev 1 does not support port ranges");
492 	cinfo_transform(info, &up);
493 	cb->data = info;
494 }
495 
conntrack2_mt_parse(struct xt_option_call * cb)496 static void conntrack2_mt_parse(struct xt_option_call *cb)
497 {
498 #define cinfo2_transform(r, l) \
499 		memcpy((r), (l), offsetof(typeof(*(l)), sizeof(*info));
500 
501 	struct xt_conntrack_mtinfo2 *info = cb->data;
502 	struct xt_conntrack_mtinfo3 up;
503 
504 	memset(&up, 0, sizeof(up));
505 	memcpy(&up, info, sizeof(*info));
506 	up.origsrc_port_high = up.origsrc_port;
507 	up.origdst_port_high = up.origdst_port;
508 	up.replsrc_port_high = up.replsrc_port;
509 	up.repldst_port_high = up.repldst_port;
510 	cb->data = &up;
511 	conntrack_mt_parse(cb, 3);
512 	if (up.origsrc_port != up.origsrc_port_high ||
513 	    up.origdst_port != up.origdst_port_high ||
514 	    up.replsrc_port != up.replsrc_port_high ||
515 	    up.repldst_port != up.repldst_port_high)
516 		xtables_error(PARAMETER_PROBLEM,
517 			"conntrack rev 2 does not support port ranges");
518 	memcpy(info, &up, sizeof(*info));
519 	cb->data = info;
520 #undef cinfo2_transform
521 }
522 
conntrack3_mt_parse(struct xt_option_call * cb)523 static void conntrack3_mt_parse(struct xt_option_call *cb)
524 {
525 	conntrack_mt_parse(cb, 3);
526 }
527 
conntrack_mt_check(struct xt_fcheck_call * cb)528 static void conntrack_mt_check(struct xt_fcheck_call *cb)
529 {
530 	if (cb->xflags == 0)
531 		xtables_error(PARAMETER_PROBLEM, "conntrack: At least one option "
532 		           "is required");
533 }
534 
535 static void
print_state(unsigned int statemask)536 print_state(unsigned int statemask)
537 {
538 	const char *sep = " ";
539 
540 	if (statemask & XT_CONNTRACK_STATE_INVALID) {
541 		printf("%sINVALID", sep);
542 		sep = ",";
543 	}
544 	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
545 		printf("%sNEW", sep);
546 		sep = ",";
547 	}
548 	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
549 		printf("%sRELATED", sep);
550 		sep = ",";
551 	}
552 	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
553 		printf("%sESTABLISHED", sep);
554 		sep = ",";
555 	}
556 	if (statemask & XT_CONNTRACK_STATE_UNTRACKED) {
557 		printf("%sUNTRACKED", sep);
558 		sep = ",";
559 	}
560 	if (statemask & XT_CONNTRACK_STATE_SNAT) {
561 		printf("%sSNAT", sep);
562 		sep = ",";
563 	}
564 	if (statemask & XT_CONNTRACK_STATE_DNAT) {
565 		printf("%sDNAT", sep);
566 		sep = ",";
567 	}
568 }
569 
570 static void
print_status(unsigned int statusmask)571 print_status(unsigned int statusmask)
572 {
573 	const char *sep = " ";
574 
575 	if (statusmask & IPS_EXPECTED) {
576 		printf("%sEXPECTED", sep);
577 		sep = ",";
578 	}
579 	if (statusmask & IPS_SEEN_REPLY) {
580 		printf("%sSEEN_REPLY", sep);
581 		sep = ",";
582 	}
583 	if (statusmask & IPS_ASSURED) {
584 		printf("%sASSURED", sep);
585 		sep = ",";
586 	}
587 	if (statusmask & IPS_CONFIRMED) {
588 		printf("%sCONFIRMED", sep);
589 		sep = ",";
590 	}
591 	if (statusmask == 0)
592 		printf("%sNONE", sep);
593 }
594 
595 static void
conntrack_dump_addr(const union nf_inet_addr * addr,const union nf_inet_addr * mask,unsigned int family,bool numeric)596 conntrack_dump_addr(const union nf_inet_addr *addr,
597                     const union nf_inet_addr *mask,
598                     unsigned int family, bool numeric)
599 {
600 	if (family == NFPROTO_IPV4) {
601 		if (!numeric && addr->ip == 0) {
602 			printf(" anywhere");
603 			return;
604 		}
605 		if (numeric)
606 			printf(" %s%s",
607 			       xtables_ipaddr_to_numeric(&addr->in),
608 			       xtables_ipmask_to_numeric(&mask->in));
609 		else
610 			printf(" %s%s",
611 			       xtables_ipaddr_to_anyname(&addr->in),
612 			       xtables_ipmask_to_numeric(&mask->in));
613 	} else if (family == NFPROTO_IPV6) {
614 		if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 &&
615 		    addr->ip6[2] == 0 && addr->ip6[3] == 0) {
616 			printf(" anywhere");
617 			return;
618 		}
619 		if (numeric)
620 			printf(" %s%s",
621 			       xtables_ip6addr_to_numeric(&addr->in6),
622 			       xtables_ip6mask_to_numeric(&mask->in6));
623 		else
624 			printf(" %s%s",
625 			       xtables_ip6addr_to_anyname(&addr->in6),
626 			       xtables_ip6mask_to_numeric(&mask->in6));
627 	}
628 }
629 
630 static void
print_addr(const struct in_addr * addr,const struct in_addr * mask,int inv,int numeric)631 print_addr(const struct in_addr *addr, const struct in_addr *mask,
632            int inv, int numeric)
633 {
634 	char buf[BUFSIZ];
635 
636 	if (inv)
637 		printf(" !");
638 
639 	if (mask->s_addr == 0L && !numeric)
640 		printf(" %s", "anywhere");
641 	else {
642 		if (numeric)
643 			strcpy(buf, xtables_ipaddr_to_numeric(addr));
644 		else
645 			strcpy(buf, xtables_ipaddr_to_anyname(addr));
646 		strcat(buf, xtables_ipmask_to_numeric(mask));
647 		printf(" %s", buf);
648 	}
649 }
650 
651 static void
matchinfo_print(const void * ip,const struct xt_entry_match * match,int numeric,const char * optpfx)652 matchinfo_print(const void *ip, const struct xt_entry_match *match, int numeric, const char *optpfx)
653 {
654 	const struct xt_conntrack_info *sinfo = (const void *)match->data;
655 
656 	if(sinfo->flags & XT_CONNTRACK_STATE) {
657         	if (sinfo->invflags & XT_CONNTRACK_STATE)
658 			printf(" !");
659 		printf(" %sctstate", optpfx);
660 		print_state(sinfo->statemask);
661 	}
662 
663 	if(sinfo->flags & XT_CONNTRACK_PROTO) {
664         	if (sinfo->invflags & XT_CONNTRACK_PROTO)
665 			printf(" !");
666 		printf(" %sctproto", optpfx);
667 		printf(" %u", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum);
668 	}
669 
670 	if(sinfo->flags & XT_CONNTRACK_ORIGSRC) {
671 		if (sinfo->invflags & XT_CONNTRACK_ORIGSRC)
672 			printf(" !");
673 		printf(" %sctorigsrc", optpfx);
674 
675 		print_addr(
676 		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
677 		    &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
678 		    false,
679 		    numeric);
680 	}
681 
682 	if(sinfo->flags & XT_CONNTRACK_ORIGDST) {
683 		if (sinfo->invflags & XT_CONNTRACK_ORIGDST)
684 			printf(" !");
685 		printf(" %sctorigdst", optpfx);
686 
687 		print_addr(
688 		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
689 		    &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
690 		    false,
691 		    numeric);
692 	}
693 
694 	if(sinfo->flags & XT_CONNTRACK_REPLSRC) {
695 		if (sinfo->invflags & XT_CONNTRACK_REPLSRC)
696 			printf(" !");
697 		printf(" %sctreplsrc", optpfx);
698 
699 		print_addr(
700 		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
701 		    &sinfo->sipmsk[IP_CT_DIR_REPLY],
702 		    false,
703 		    numeric);
704 	}
705 
706 	if(sinfo->flags & XT_CONNTRACK_REPLDST) {
707 		if (sinfo->invflags & XT_CONNTRACK_REPLDST)
708 			printf(" !");
709 		printf(" %sctrepldst", optpfx);
710 
711 		print_addr(
712 		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
713 		    &sinfo->dipmsk[IP_CT_DIR_REPLY],
714 		    false,
715 		    numeric);
716 	}
717 
718 	if(sinfo->flags & XT_CONNTRACK_STATUS) {
719         	if (sinfo->invflags & XT_CONNTRACK_STATUS)
720 			printf(" !");
721 		printf(" %sctstatus", optpfx);
722 		print_status(sinfo->statusmask);
723 	}
724 
725 	if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
726         	if (sinfo->invflags & XT_CONNTRACK_EXPIRES)
727 			printf(" !");
728 		printf(" %sctexpire ", optpfx);
729 
730         	if (sinfo->expires_max == sinfo->expires_min)
731 			printf("%lu", sinfo->expires_min);
732         	else
733 			printf("%lu:%lu", sinfo->expires_min, sinfo->expires_max);
734 	}
735 
736 	if (sinfo->flags & XT_CONNTRACK_DIRECTION) {
737 		if (sinfo->invflags & XT_CONNTRACK_DIRECTION)
738 			printf(" %sctdir REPLY", optpfx);
739 		else
740 			printf(" %sctdir ORIGINAL", optpfx);
741 	}
742 
743 }
744 
745 static void
conntrack_dump_ports(const char * prefix,const char * opt,u_int16_t port_low,u_int16_t port_high)746 conntrack_dump_ports(const char *prefix, const char *opt,
747 		     u_int16_t port_low, u_int16_t port_high)
748 {
749 	if (port_high == 0 || port_low == port_high)
750 		printf(" %s%s %u", prefix, opt, port_low);
751 	else
752 		printf(" %s%s %u:%u", prefix, opt, port_low, port_high);
753 }
754 
755 static void
conntrack_dump(const struct xt_conntrack_mtinfo3 * info,const char * prefix,unsigned int family,bool numeric,bool v3)756 conntrack_dump(const struct xt_conntrack_mtinfo3 *info, const char *prefix,
757                unsigned int family, bool numeric, bool v3)
758 {
759 	if (info->match_flags & XT_CONNTRACK_STATE) {
760 		if (info->invert_flags & XT_CONNTRACK_STATE)
761 			printf(" !");
762 		printf(" %sctstate", prefix);
763 		print_state(info->state_mask);
764 	}
765 
766 	if (info->match_flags & XT_CONNTRACK_PROTO) {
767 		if (info->invert_flags & XT_CONNTRACK_PROTO)
768 			printf(" !");
769 		printf(" %sctproto %u", prefix, info->l4proto);
770 	}
771 
772 	if (info->match_flags & XT_CONNTRACK_ORIGSRC) {
773 		if (info->invert_flags & XT_CONNTRACK_ORIGSRC)
774 			printf(" !");
775 		printf(" %sctorigsrc", prefix);
776 		conntrack_dump_addr(&info->origsrc_addr, &info->origsrc_mask,
777 		                    family, numeric);
778 	}
779 
780 	if (info->match_flags & XT_CONNTRACK_ORIGDST) {
781 		if (info->invert_flags & XT_CONNTRACK_ORIGDST)
782 			printf(" !");
783 		printf(" %sctorigdst", prefix);
784 		conntrack_dump_addr(&info->origdst_addr, &info->origdst_mask,
785 		                    family, numeric);
786 	}
787 
788 	if (info->match_flags & XT_CONNTRACK_REPLSRC) {
789 		if (info->invert_flags & XT_CONNTRACK_REPLSRC)
790 			printf(" !");
791 		printf(" %sctreplsrc", prefix);
792 		conntrack_dump_addr(&info->replsrc_addr, &info->replsrc_mask,
793 		                    family, numeric);
794 	}
795 
796 	if (info->match_flags & XT_CONNTRACK_REPLDST) {
797 		if (info->invert_flags & XT_CONNTRACK_REPLDST)
798 			printf(" !");
799 		printf(" %sctrepldst", prefix);
800 		conntrack_dump_addr(&info->repldst_addr, &info->repldst_mask,
801 		                    family, numeric);
802 	}
803 
804 	if (info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) {
805 		if (info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT)
806 			printf(" !");
807 		conntrack_dump_ports(prefix, "ctorigsrcport",
808 				     v3 ? info->origsrc_port : ntohs(info->origsrc_port),
809 				     v3 ? info->origsrc_port_high : 0);
810 	}
811 
812 	if (info->match_flags & XT_CONNTRACK_ORIGDST_PORT) {
813 		if (info->invert_flags & XT_CONNTRACK_ORIGDST_PORT)
814 			printf(" !");
815 		conntrack_dump_ports(prefix, "ctorigdstport",
816 				     v3 ? info->origdst_port : ntohs(info->origdst_port),
817 				     v3 ? info->origdst_port_high : 0);
818 	}
819 
820 	if (info->match_flags & XT_CONNTRACK_REPLSRC_PORT) {
821 		if (info->invert_flags & XT_CONNTRACK_REPLSRC_PORT)
822 			printf(" !");
823 		conntrack_dump_ports(prefix, "ctreplsrcport",
824 				     v3 ? info->replsrc_port : ntohs(info->replsrc_port),
825 				     v3 ? info->replsrc_port_high : 0);
826 	}
827 
828 	if (info->match_flags & XT_CONNTRACK_REPLDST_PORT) {
829 		if (info->invert_flags & XT_CONNTRACK_REPLDST_PORT)
830 			printf(" !");
831 		conntrack_dump_ports(prefix, "ctrepldstport",
832 				     v3 ? info->repldst_port : ntohs(info->repldst_port),
833 				     v3 ? info->repldst_port_high : 0);
834 	}
835 
836 	if (info->match_flags & XT_CONNTRACK_STATUS) {
837 		if (info->invert_flags & XT_CONNTRACK_STATUS)
838 			printf(" !");
839 		printf(" %sctstatus", prefix);
840 		print_status(info->status_mask);
841 	}
842 
843 	if (info->match_flags & XT_CONNTRACK_EXPIRES) {
844 		if (info->invert_flags & XT_CONNTRACK_EXPIRES)
845 			printf(" !");
846 		printf(" %sctexpire ", prefix);
847 
848 		if (info->expires_max == info->expires_min)
849 			printf("%u", (unsigned int)info->expires_min);
850 		else
851 			printf("%u:%u", (unsigned int)info->expires_min,
852 			       (unsigned int)info->expires_max);
853 	}
854 
855 	if (info->match_flags & XT_CONNTRACK_DIRECTION) {
856 		if (info->invert_flags & XT_CONNTRACK_DIRECTION)
857 			printf(" %sctdir REPLY", prefix);
858 		else
859 			printf(" %sctdir ORIGINAL", prefix);
860 	}
861 }
862 
conntrack_print(const void * ip,const struct xt_entry_match * match,int numeric)863 static void conntrack_print(const void *ip, const struct xt_entry_match *match,
864                             int numeric)
865 {
866 	matchinfo_print(ip, match, numeric, "");
867 }
868 
869 static void
conntrack1_mt4_print(const void * ip,const struct xt_entry_match * match,int numeric)870 conntrack1_mt4_print(const void *ip, const struct xt_entry_match *match,
871                      int numeric)
872 {
873 	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
874 	struct xt_conntrack_mtinfo3 up;
875 
876 	cinfo_transform(&up, info);
877 	conntrack_dump(&up, "", NFPROTO_IPV4, numeric, false);
878 }
879 
880 static void
conntrack1_mt6_print(const void * ip,const struct xt_entry_match * match,int numeric)881 conntrack1_mt6_print(const void *ip, const struct xt_entry_match *match,
882                      int numeric)
883 {
884 	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
885 	struct xt_conntrack_mtinfo3 up;
886 
887 	cinfo_transform(&up, info);
888 	conntrack_dump(&up, "", NFPROTO_IPV6, numeric, false);
889 }
890 
891 static void
conntrack2_mt_print(const void * ip,const struct xt_entry_match * match,int numeric)892 conntrack2_mt_print(const void *ip, const struct xt_entry_match *match,
893                     int numeric)
894 {
895 	conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric, false);
896 }
897 
898 static void
conntrack2_mt6_print(const void * ip,const struct xt_entry_match * match,int numeric)899 conntrack2_mt6_print(const void *ip, const struct xt_entry_match *match,
900                      int numeric)
901 {
902 	conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric, false);
903 }
904 
905 static void
conntrack3_mt_print(const void * ip,const struct xt_entry_match * match,int numeric)906 conntrack3_mt_print(const void *ip, const struct xt_entry_match *match,
907                     int numeric)
908 {
909 	conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric, true);
910 }
911 
912 static void
conntrack3_mt6_print(const void * ip,const struct xt_entry_match * match,int numeric)913 conntrack3_mt6_print(const void *ip, const struct xt_entry_match *match,
914                      int numeric)
915 {
916 	conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric, true);
917 }
918 
conntrack_save(const void * ip,const struct xt_entry_match * match)919 static void conntrack_save(const void *ip, const struct xt_entry_match *match)
920 {
921 	matchinfo_print(ip, match, 1, "--");
922 }
923 
conntrack3_mt_save(const void * ip,const struct xt_entry_match * match)924 static void conntrack3_mt_save(const void *ip,
925                                const struct xt_entry_match *match)
926 {
927 	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true, true);
928 }
929 
conntrack3_mt6_save(const void * ip,const struct xt_entry_match * match)930 static void conntrack3_mt6_save(const void *ip,
931                                 const struct xt_entry_match *match)
932 {
933 	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true, true);
934 }
935 
conntrack2_mt_save(const void * ip,const struct xt_entry_match * match)936 static void conntrack2_mt_save(const void *ip,
937                                const struct xt_entry_match *match)
938 {
939 	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true, false);
940 }
941 
conntrack2_mt6_save(const void * ip,const struct xt_entry_match * match)942 static void conntrack2_mt6_save(const void *ip,
943                                 const struct xt_entry_match *match)
944 {
945 	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true, false);
946 }
947 
948 static void
conntrack1_mt4_save(const void * ip,const struct xt_entry_match * match)949 conntrack1_mt4_save(const void *ip, const struct xt_entry_match *match)
950 {
951 	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
952 	struct xt_conntrack_mtinfo3 up;
953 
954 	cinfo_transform(&up, info);
955 	conntrack_dump(&up, "--", NFPROTO_IPV4, true, false);
956 }
957 
958 static void
conntrack1_mt6_save(const void * ip,const struct xt_entry_match * match)959 conntrack1_mt6_save(const void *ip, const struct xt_entry_match *match)
960 {
961 	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
962 	struct xt_conntrack_mtinfo3 up;
963 
964 	cinfo_transform(&up, info);
965 	conntrack_dump(&up, "--", NFPROTO_IPV6, true, false);
966 }
967 
968 static struct xtables_match conntrack_mt_reg[] = {
969 	{
970 		.version       = XTABLES_VERSION,
971 		.name          = "conntrack",
972 		.revision      = 0,
973 		.family        = NFPROTO_IPV4,
974 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_info)),
975 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_info)),
976 		.help          = conntrack_mt_help,
977 		.x6_parse      = conntrack_parse,
978 		.x6_fcheck     = conntrack_mt_check,
979 		.print         = conntrack_print,
980 		.save          = conntrack_save,
981 		.x6_options    = conntrack_mt_opts_v0,
982 	},
983 	{
984 		.version       = XTABLES_VERSION,
985 		.name          = "conntrack",
986 		.revision      = 1,
987 		.family        = NFPROTO_IPV4,
988 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
989 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
990 		.help          = conntrack_mt_help,
991 		.x6_parse      = conntrack1_mt_parse,
992 		.x6_fcheck     = conntrack_mt_check,
993 		.print         = conntrack1_mt4_print,
994 		.save          = conntrack1_mt4_save,
995 		.x6_options    = conntrack_mt_opts,
996 	},
997 	{
998 		.version       = XTABLES_VERSION,
999 		.name          = "conntrack",
1000 		.revision      = 1,
1001 		.family        = NFPROTO_IPV6,
1002 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1003 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1004 		.help          = conntrack_mt_help,
1005 		.x6_parse      = conntrack1_mt_parse,
1006 		.x6_fcheck     = conntrack_mt_check,
1007 		.print         = conntrack1_mt6_print,
1008 		.save          = conntrack1_mt6_save,
1009 		.x6_options    = conntrack_mt_opts,
1010 	},
1011 	{
1012 		.version       = XTABLES_VERSION,
1013 		.name          = "conntrack",
1014 		.revision      = 2,
1015 		.family        = NFPROTO_IPV4,
1016 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1017 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1018 		.help          = conntrack_mt_help,
1019 		.x6_parse      = conntrack2_mt_parse,
1020 		.x6_fcheck     = conntrack_mt_check,
1021 		.print         = conntrack2_mt_print,
1022 		.save          = conntrack2_mt_save,
1023 		.x6_options    = conntrack_mt_opts,
1024 	},
1025 	{
1026 		.version       = XTABLES_VERSION,
1027 		.name          = "conntrack",
1028 		.revision      = 2,
1029 		.family        = NFPROTO_IPV6,
1030 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1031 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1032 		.help          = conntrack_mt_help,
1033 		.x6_parse      = conntrack2_mt_parse,
1034 		.x6_fcheck     = conntrack_mt_check,
1035 		.print         = conntrack2_mt6_print,
1036 		.save          = conntrack2_mt6_save,
1037 		.x6_options    = conntrack_mt_opts,
1038 	},
1039 	{
1040 		.version       = XTABLES_VERSION,
1041 		.name          = "conntrack",
1042 		.revision      = 3,
1043 		.family        = NFPROTO_IPV4,
1044 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1045 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1046 		.help          = conntrack_mt_help,
1047 		.x6_parse      = conntrack3_mt_parse,
1048 		.x6_fcheck     = conntrack_mt_check,
1049 		.print         = conntrack3_mt_print,
1050 		.save          = conntrack3_mt_save,
1051 		.x6_options    = conntrack_mt_opts,
1052 	},
1053 	{
1054 		.version       = XTABLES_VERSION,
1055 		.name          = "conntrack",
1056 		.revision      = 3,
1057 		.family        = NFPROTO_IPV6,
1058 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1059 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1060 		.help          = conntrack_mt_help,
1061 		.x6_parse      = conntrack3_mt_parse,
1062 		.x6_fcheck     = conntrack_mt_check,
1063 		.print         = conntrack3_mt6_print,
1064 		.save          = conntrack3_mt6_save,
1065 		.x6_options    = conntrack_mt_opts,
1066 	},
1067 };
1068 
_init(void)1069 void _init(void)
1070 {
1071 	xtables_register_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg));
1072 }
1073