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