Home | History | Annotate | Download | only in extensions
      1 /*
      2  *	libxt_conntrack
      3  *	Shared library add-on to iptables for conntrack matching support.
      4  *
      5  *	GPL (C) 2001  Marc Boucher (marc (at) mbsi.ca).
      6  *	Copyright  CC Computer Consultants GmbH, 2007 - 2008
      7  *	Jan Engelhardt <jengelh (at) 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 
     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
    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
    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
    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
    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
    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
    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
    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
    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 
    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 
    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 
    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 
    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 
    523 static void conntrack3_mt_parse(struct xt_option_call *cb)
    524 {
    525 	conntrack_mt_parse(cb, 3);
    526 }
    527 
    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
    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
    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
    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
    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
    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
    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
    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 
    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
    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
    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
    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
    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
    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
    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 
    919 static void conntrack_save(const void *ip, const struct xt_entry_match *match)
    920 {
    921 	matchinfo_print(ip, match, 1, "--");
    922 }
    923 
    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 
    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 
    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 
    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
    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
    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 
   1069 void _init(void)
   1070 {
   1071 	xtables_register_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg));
   1072 }
   1073