xref: /aosp_15_r20/external/iptables/extensions/libxt_conntrack.c (revision a71a954618bbadd4a345637e5edcf36eec826889)
1 /*
2  *	libxt_conntrack
3  *	Shared library add-on to iptables for conntrack matching support.
4  *
5  *	GPL (C) 2001  Marc Boucher ([email protected]).
6  *	Copyright © CC Computer Consultants GmbH, 2007 - 2008
7  *	Jan Engelhardt <[email protected]>
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 
conntrack_mt_help(void)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
parse_state(const char * state,size_t len,struct xt_conntrack_info * sinfo)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
parse_states(const char * arg,struct xt_conntrack_info * sinfo)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
conntrack_ps_state(struct xt_conntrack_mtinfo3 * info,const char * state,size_t z)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
conntrack_ps_states(struct xt_conntrack_mtinfo3 * info,const char * arg)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
parse_status(const char * status,size_t len,struct xt_conntrack_info * sinfo)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
parse_statuses(const char * arg,struct xt_conntrack_info * sinfo)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
conntrack_ps_status(struct xt_conntrack_mtinfo3 * info,const char * status,size_t z)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
conntrack_ps_statuses(struct xt_conntrack_mtinfo3 * info,const char * arg)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 
conntrack_parse(struct xt_option_call * cb)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 		if (cb->val.protocol == 0)
350 			xtables_error(PARAMETER_PROBLEM, cb->invert ?
351 				      "condition would always match protocol" :
352 				      "rule would never match protocol");
353 		sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = cb->val.protocol;
354 		if (cb->invert)
355 			sinfo->invflags |= XT_CONNTRACK_PROTO;
356 		sinfo->flags |= XT_CONNTRACK_PROTO;
357 		break;
358 	case O_CTORIGSRC:
359 		if (cb->invert)
360 			sinfo->invflags |= XT_CONNTRACK_ORIGSRC;
361 		sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = cb->val.haddr.ip;
362 		sinfo->flags |= XT_CONNTRACK_ORIGSRC;
363 		break;
364 	case O_CTORIGDST:
365 		if (cb->invert)
366 			sinfo->invflags |= XT_CONNTRACK_ORIGDST;
367 		sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = cb->val.haddr.ip;
368 		sinfo->flags |= XT_CONNTRACK_ORIGDST;
369 		break;
370 	case O_CTREPLSRC:
371 		if (cb->invert)
372 			sinfo->invflags |= XT_CONNTRACK_REPLSRC;
373 		sinfo->tuple[IP_CT_DIR_REPLY].src.ip = cb->val.haddr.ip;
374 		sinfo->flags |= XT_CONNTRACK_REPLSRC;
375 		break;
376 	case O_CTREPLDST:
377 		if (cb->invert)
378 			sinfo->invflags |= XT_CONNTRACK_REPLDST;
379 		sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = cb->val.haddr.ip;
380 		sinfo->flags |= XT_CONNTRACK_REPLDST;
381 		break;
382 	case O_CTSTATUS:
383 		parse_statuses(cb->arg, sinfo);
384 		if (cb->invert)
385 			sinfo->invflags |= XT_CONNTRACK_STATUS;
386 		sinfo->flags |= XT_CONNTRACK_STATUS;
387 		break;
388 	case O_CTEXPIRE:
389 		sinfo->expires_min = cb->val.u32_range[0];
390 		sinfo->expires_max = cb->val.u32_range[0];
391 		if (cb->nvals >= 2)
392 			sinfo->expires_max = cb->val.u32_range[1];
393 		if (cb->invert)
394 			sinfo->invflags |= XT_CONNTRACK_EXPIRES;
395 		sinfo->flags |= XT_CONNTRACK_EXPIRES;
396 		break;
397 	}
398 }
399 
conntrack_mt_parse(struct xt_option_call * cb,uint8_t rev)400 static void conntrack_mt_parse(struct xt_option_call *cb, uint8_t rev)
401 {
402 	struct xt_conntrack_mtinfo3 *info = cb->data;
403 
404 	xtables_option_parse(cb);
405 	switch (cb->entry->id) {
406 	case O_CTSTATE:
407 		conntrack_ps_states(info, cb->arg);
408 		info->match_flags |= XT_CONNTRACK_STATE;
409 		if (cb->invert)
410 			info->invert_flags |= XT_CONNTRACK_STATE;
411 		break;
412 	case O_CTPROTO:
413 		if (cb->val.protocol == 0)
414 			xtables_error(PARAMETER_PROBLEM, cb->invert ?
415 				      "conntrack: condition would always match protocol" :
416 				      "conntrack: rule would never match protocol");
417 		info->l4proto = cb->val.protocol;
418 		info->match_flags |= XT_CONNTRACK_PROTO;
419 		if (cb->invert)
420 			info->invert_flags |= XT_CONNTRACK_PROTO;
421 		break;
422 	case O_CTORIGSRC:
423 		info->origsrc_addr = cb->val.haddr;
424 		info->origsrc_mask = cb->val.hmask;
425 		info->match_flags |= XT_CONNTRACK_ORIGSRC;
426 		if (cb->invert)
427 			info->invert_flags |= XT_CONNTRACK_ORIGSRC;
428 		break;
429 	case O_CTORIGDST:
430 		info->origdst_addr = cb->val.haddr;
431 		info->origdst_mask = cb->val.hmask;
432 		info->match_flags |= XT_CONNTRACK_ORIGDST;
433 		if (cb->invert)
434 			info->invert_flags |= XT_CONNTRACK_ORIGDST;
435 		break;
436 	case O_CTREPLSRC:
437 		info->replsrc_addr = cb->val.haddr;
438 		info->replsrc_mask = cb->val.hmask;
439 		info->match_flags |= XT_CONNTRACK_REPLSRC;
440 		if (cb->invert)
441 			info->invert_flags |= XT_CONNTRACK_REPLSRC;
442 		break;
443 	case O_CTREPLDST:
444 		info->repldst_addr = cb->val.haddr;
445 		info->repldst_mask = cb->val.hmask;
446 		info->match_flags |= XT_CONNTRACK_REPLDST;
447 		if (cb->invert)
448 			info->invert_flags |= XT_CONNTRACK_REPLDST;
449 		break;
450 	case O_CTSTATUS:
451 		conntrack_ps_statuses(info, cb->arg);
452 		info->match_flags |= XT_CONNTRACK_STATUS;
453 		if (cb->invert)
454 			info->invert_flags |= XT_CONNTRACK_STATUS;
455 		break;
456 	case O_CTEXPIRE:
457 		info->expires_min = cb->val.u32_range[0];
458 		info->expires_max = cb->val.u32_range[0];
459 		if (cb->nvals >= 2)
460 			info->expires_max = cb->val.u32_range[1];
461 		info->match_flags |= XT_CONNTRACK_EXPIRES;
462 		if (cb->invert)
463 			info->invert_flags |= XT_CONNTRACK_EXPIRES;
464 		break;
465 	case O_CTORIGSRCPORT:
466 		info->origsrc_port = cb->val.port_range[0];
467 		info->origsrc_port_high = cb->val.port_range[cb->nvals >= 2];
468 		info->match_flags |= XT_CONNTRACK_ORIGSRC_PORT;
469 		if (cb->invert)
470 			info->invert_flags |= XT_CONNTRACK_ORIGSRC_PORT;
471 		break;
472 	case O_CTORIGDSTPORT:
473 		info->origdst_port = cb->val.port_range[0];
474 		info->origdst_port_high = cb->val.port_range[cb->nvals >= 2];
475 		info->match_flags |= XT_CONNTRACK_ORIGDST_PORT;
476 		if (cb->invert)
477 			info->invert_flags |= XT_CONNTRACK_ORIGDST_PORT;
478 		break;
479 	case O_CTREPLSRCPORT:
480 		info->replsrc_port = cb->val.port_range[0];
481 		info->replsrc_port_high = cb->val.port_range[cb->nvals >= 2];
482 		info->match_flags |= XT_CONNTRACK_REPLSRC_PORT;
483 		if (cb->invert)
484 			info->invert_flags |= XT_CONNTRACK_REPLSRC_PORT;
485 		break;
486 	case O_CTREPLDSTPORT:
487 		info->repldst_port = cb->val.port_range[0];
488 		info->repldst_port_high = cb->val.port_range[cb->nvals >= 2];
489 		info->match_flags |= XT_CONNTRACK_REPLDST_PORT;
490 		if (cb->invert)
491 			info->invert_flags |= XT_CONNTRACK_REPLDST_PORT;
492 		break;
493 	case O_CTDIR:
494 		if (strcasecmp(cb->arg, "ORIGINAL") == 0) {
495 			info->match_flags  |= XT_CONNTRACK_DIRECTION;
496 			info->invert_flags &= ~XT_CONNTRACK_DIRECTION;
497 		} else if (strcasecmp(cb->arg, "REPLY") == 0) {
498 			info->match_flags  |= XT_CONNTRACK_DIRECTION;
499 			info->invert_flags |= XT_CONNTRACK_DIRECTION;
500 		} else {
501 			xtables_param_act(XTF_BAD_VALUE, "conntrack", "--ctdir", cb->arg);
502 		}
503 		break;
504 	}
505 }
506 
507 #define cinfo_transform(r, l) \
508 	do { \
509 		memcpy((r), (l), offsetof(typeof(*(l)), state_mask)); \
510 		(r)->state_mask  = (l)->state_mask; \
511 		(r)->status_mask = (l)->status_mask; \
512 	} while (false);
513 
conntrack1_mt_parse(struct xt_option_call * cb)514 static void conntrack1_mt_parse(struct xt_option_call *cb)
515 {
516 	struct xt_conntrack_mtinfo1 *info = cb->data;
517 	struct xt_conntrack_mtinfo3 up;
518 
519 	memset(&up, 0, sizeof(up));
520 	cinfo_transform(&up, info);
521 	up.origsrc_port_high = up.origsrc_port;
522 	up.origdst_port_high = up.origdst_port;
523 	up.replsrc_port_high = up.replsrc_port;
524 	up.repldst_port_high = up.repldst_port;
525 	cb->data = &up;
526 	conntrack_mt_parse(cb, 3);
527 	if (up.origsrc_port != up.origsrc_port_high ||
528 	    up.origdst_port != up.origdst_port_high ||
529 	    up.replsrc_port != up.replsrc_port_high ||
530 	    up.repldst_port != up.repldst_port_high)
531 		xtables_error(PARAMETER_PROBLEM,
532 			"conntrack rev 1 does not support port ranges");
533 	cinfo_transform(info, &up);
534 	cb->data = info;
535 }
536 
conntrack2_mt_parse(struct xt_option_call * cb)537 static void conntrack2_mt_parse(struct xt_option_call *cb)
538 {
539 #define cinfo2_transform(r, l) \
540 		memcpy((r), (l), offsetof(typeof(*(l)), sizeof(*info));
541 
542 	struct xt_conntrack_mtinfo2 *info = cb->data;
543 	struct xt_conntrack_mtinfo3 up;
544 
545 	memset(&up, 0, sizeof(up));
546 	memcpy(&up, info, sizeof(*info));
547 	up.origsrc_port_high = up.origsrc_port;
548 	up.origdst_port_high = up.origdst_port;
549 	up.replsrc_port_high = up.replsrc_port;
550 	up.repldst_port_high = up.repldst_port;
551 	cb->data = &up;
552 	conntrack_mt_parse(cb, 3);
553 	if (up.origsrc_port != up.origsrc_port_high ||
554 	    up.origdst_port != up.origdst_port_high ||
555 	    up.replsrc_port != up.replsrc_port_high ||
556 	    up.repldst_port != up.repldst_port_high)
557 		xtables_error(PARAMETER_PROBLEM,
558 			"conntrack rev 2 does not support port ranges");
559 	memcpy(info, &up, sizeof(*info));
560 	cb->data = info;
561 #undef cinfo2_transform
562 }
563 
conntrack3_mt_parse(struct xt_option_call * cb)564 static void conntrack3_mt_parse(struct xt_option_call *cb)
565 {
566 	conntrack_mt_parse(cb, 3);
567 }
568 
conntrack_mt_check(struct xt_fcheck_call * cb)569 static void conntrack_mt_check(struct xt_fcheck_call *cb)
570 {
571 	if (cb->xflags == 0)
572 		xtables_error(PARAMETER_PROBLEM, "conntrack: At least one option "
573 		           "is required");
574 }
575 
576 static void
print_state(unsigned int statemask)577 print_state(unsigned int statemask)
578 {
579 	const char *sep = " ";
580 
581 	if (statemask & XT_CONNTRACK_STATE_INVALID) {
582 		printf("%sINVALID", sep);
583 		sep = ",";
584 	}
585 	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
586 		printf("%sNEW", sep);
587 		sep = ",";
588 	}
589 	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
590 		printf("%sRELATED", sep);
591 		sep = ",";
592 	}
593 	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
594 		printf("%sESTABLISHED", sep);
595 		sep = ",";
596 	}
597 	if (statemask & XT_CONNTRACK_STATE_UNTRACKED) {
598 		printf("%sUNTRACKED", sep);
599 		sep = ",";
600 	}
601 	if (statemask & XT_CONNTRACK_STATE_SNAT) {
602 		printf("%sSNAT", sep);
603 		sep = ",";
604 	}
605 	if (statemask & XT_CONNTRACK_STATE_DNAT) {
606 		printf("%sDNAT", sep);
607 		sep = ",";
608 	}
609 }
610 
611 static void
print_status(unsigned int statusmask)612 print_status(unsigned int statusmask)
613 {
614 	const char *sep = " ";
615 
616 	if (statusmask & IPS_EXPECTED) {
617 		printf("%sEXPECTED", sep);
618 		sep = ",";
619 	}
620 	if (statusmask & IPS_SEEN_REPLY) {
621 		printf("%sSEEN_REPLY", sep);
622 		sep = ",";
623 	}
624 	if (statusmask & IPS_ASSURED) {
625 		printf("%sASSURED", sep);
626 		sep = ",";
627 	}
628 	if (statusmask & IPS_CONFIRMED) {
629 		printf("%sCONFIRMED", sep);
630 		sep = ",";
631 	}
632 	if (statusmask == 0)
633 		printf("%sNONE", sep);
634 }
635 
636 static void
conntrack_dump_addr(const union nf_inet_addr * addr,const union nf_inet_addr * mask,unsigned int family,bool numeric)637 conntrack_dump_addr(const union nf_inet_addr *addr,
638                     const union nf_inet_addr *mask,
639                     unsigned int family, bool numeric)
640 {
641 	if (family == NFPROTO_IPV4) {
642 		if (!numeric && addr->ip == 0) {
643 			printf(" anywhere");
644 			return;
645 		}
646 		if (numeric)
647 			printf(" %s%s",
648 			       xtables_ipaddr_to_numeric(&addr->in),
649 			       xtables_ipmask_to_numeric(&mask->in));
650 		else
651 			printf(" %s%s",
652 			       xtables_ipaddr_to_anyname(&addr->in),
653 			       xtables_ipmask_to_numeric(&mask->in));
654 	} else if (family == NFPROTO_IPV6) {
655 		if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 &&
656 		    addr->ip6[2] == 0 && addr->ip6[3] == 0) {
657 			printf(" anywhere");
658 			return;
659 		}
660 		if (numeric)
661 			printf(" %s%s",
662 			       xtables_ip6addr_to_numeric(&addr->in6),
663 			       xtables_ip6mask_to_numeric(&mask->in6));
664 		else
665 			printf(" %s%s",
666 			       xtables_ip6addr_to_anyname(&addr->in6),
667 			       xtables_ip6mask_to_numeric(&mask->in6));
668 	}
669 }
670 
671 static void
print_addr(const struct in_addr * addr,const struct in_addr * mask,int inv,int numeric)672 print_addr(const struct in_addr *addr, const struct in_addr *mask,
673            int inv, int numeric)
674 {
675 	if (inv)
676 		printf(" !");
677 
678 	if (mask->s_addr == 0L && !numeric)
679 		printf(" anywhere");
680 	else {
681 		if (numeric)
682 			printf(" %s%s",
683 			       xtables_ipaddr_to_numeric(addr),
684 			       xtables_ipmask_to_numeric(mask));
685 		else
686 			printf(" %s%s",
687 			       xtables_ipaddr_to_anyname(addr),
688 			       xtables_ipmask_to_numeric(mask));
689 	}
690 }
691 
692 static void
matchinfo_print(const void * ip,const struct xt_entry_match * match,int numeric,const char * optpfx)693 matchinfo_print(const void *ip, const struct xt_entry_match *match, int numeric, const char *optpfx)
694 {
695 	const struct xt_conntrack_info *sinfo = (const void *)match->data;
696 
697 	if(sinfo->flags & XT_CONNTRACK_STATE) {
698         	if (sinfo->invflags & XT_CONNTRACK_STATE)
699 			printf(" !");
700 		printf(" %sctstate", optpfx);
701 		print_state(sinfo->statemask);
702 	}
703 
704 	if(sinfo->flags & XT_CONNTRACK_PROTO) {
705         	if (sinfo->invflags & XT_CONNTRACK_PROTO)
706 			printf(" !");
707 		printf(" %sctproto", optpfx);
708 		printf(" %u", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum);
709 	}
710 
711 	if(sinfo->flags & XT_CONNTRACK_ORIGSRC) {
712 		if (sinfo->invflags & XT_CONNTRACK_ORIGSRC)
713 			printf(" !");
714 		printf(" %sctorigsrc", optpfx);
715 
716 		print_addr(
717 		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
718 		    &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
719 		    false,
720 		    numeric);
721 	}
722 
723 	if(sinfo->flags & XT_CONNTRACK_ORIGDST) {
724 		if (sinfo->invflags & XT_CONNTRACK_ORIGDST)
725 			printf(" !");
726 		printf(" %sctorigdst", optpfx);
727 
728 		print_addr(
729 		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
730 		    &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
731 		    false,
732 		    numeric);
733 	}
734 
735 	if(sinfo->flags & XT_CONNTRACK_REPLSRC) {
736 		if (sinfo->invflags & XT_CONNTRACK_REPLSRC)
737 			printf(" !");
738 		printf(" %sctreplsrc", optpfx);
739 
740 		print_addr(
741 		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
742 		    &sinfo->sipmsk[IP_CT_DIR_REPLY],
743 		    false,
744 		    numeric);
745 	}
746 
747 	if(sinfo->flags & XT_CONNTRACK_REPLDST) {
748 		if (sinfo->invflags & XT_CONNTRACK_REPLDST)
749 			printf(" !");
750 		printf(" %sctrepldst", optpfx);
751 
752 		print_addr(
753 		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
754 		    &sinfo->dipmsk[IP_CT_DIR_REPLY],
755 		    false,
756 		    numeric);
757 	}
758 
759 	if(sinfo->flags & XT_CONNTRACK_STATUS) {
760         	if (sinfo->invflags & XT_CONNTRACK_STATUS)
761 			printf(" !");
762 		printf(" %sctstatus", optpfx);
763 		print_status(sinfo->statusmask);
764 	}
765 
766 	if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
767         	if (sinfo->invflags & XT_CONNTRACK_EXPIRES)
768 			printf(" !");
769 		printf(" %sctexpire ", optpfx);
770 
771         	if (sinfo->expires_max == sinfo->expires_min)
772 			printf("%lu", sinfo->expires_min);
773         	else
774 			printf("%lu:%lu", sinfo->expires_min, sinfo->expires_max);
775 	}
776 }
777 
778 static void
conntrack_dump_ports(const char * prefix,const char * opt,uint16_t port_low,uint16_t port_high)779 conntrack_dump_ports(const char *prefix, const char *opt,
780 		     uint16_t port_low, uint16_t port_high)
781 {
782 	if (port_high == 0 || port_low == port_high)
783 		printf(" %s%s %u", prefix, opt, port_low);
784 	else
785 		printf(" %s%s %u:%u", prefix, opt, port_low, port_high);
786 }
787 
788 static void
conntrack_dump(const struct xt_conntrack_mtinfo3 * info,const char * prefix,unsigned int family,bool numeric,bool v3)789 conntrack_dump(const struct xt_conntrack_mtinfo3 *info, const char *prefix,
790                unsigned int family, bool numeric, bool v3)
791 {
792 	if (info->match_flags & XT_CONNTRACK_STATE) {
793 		if (info->invert_flags & XT_CONNTRACK_STATE)
794 			printf(" !");
795 		printf(" %s%s", prefix,
796 			info->match_flags & XT_CONNTRACK_STATE_ALIAS
797 				? "state" : "ctstate");
798 		print_state(info->state_mask);
799 	}
800 
801 	if (info->match_flags & XT_CONNTRACK_PROTO) {
802 		if (info->invert_flags & XT_CONNTRACK_PROTO)
803 			printf(" !");
804 		printf(" %sctproto %u", prefix, info->l4proto);
805 	}
806 
807 	if (info->match_flags & XT_CONNTRACK_ORIGSRC) {
808 		if (info->invert_flags & XT_CONNTRACK_ORIGSRC)
809 			printf(" !");
810 		printf(" %sctorigsrc", prefix);
811 		conntrack_dump_addr(&info->origsrc_addr, &info->origsrc_mask,
812 		                    family, numeric);
813 	}
814 
815 	if (info->match_flags & XT_CONNTRACK_ORIGDST) {
816 		if (info->invert_flags & XT_CONNTRACK_ORIGDST)
817 			printf(" !");
818 		printf(" %sctorigdst", prefix);
819 		conntrack_dump_addr(&info->origdst_addr, &info->origdst_mask,
820 		                    family, numeric);
821 	}
822 
823 	if (info->match_flags & XT_CONNTRACK_REPLSRC) {
824 		if (info->invert_flags & XT_CONNTRACK_REPLSRC)
825 			printf(" !");
826 		printf(" %sctreplsrc", prefix);
827 		conntrack_dump_addr(&info->replsrc_addr, &info->replsrc_mask,
828 		                    family, numeric);
829 	}
830 
831 	if (info->match_flags & XT_CONNTRACK_REPLDST) {
832 		if (info->invert_flags & XT_CONNTRACK_REPLDST)
833 			printf(" !");
834 		printf(" %sctrepldst", prefix);
835 		conntrack_dump_addr(&info->repldst_addr, &info->repldst_mask,
836 		                    family, numeric);
837 	}
838 
839 	if (info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) {
840 		if (info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT)
841 			printf(" !");
842 		conntrack_dump_ports(prefix, "ctorigsrcport",
843 				     v3 ? info->origsrc_port : ntohs(info->origsrc_port),
844 				     v3 ? info->origsrc_port_high : 0);
845 	}
846 
847 	if (info->match_flags & XT_CONNTRACK_ORIGDST_PORT) {
848 		if (info->invert_flags & XT_CONNTRACK_ORIGDST_PORT)
849 			printf(" !");
850 		conntrack_dump_ports(prefix, "ctorigdstport",
851 				     v3 ? info->origdst_port : ntohs(info->origdst_port),
852 				     v3 ? info->origdst_port_high : 0);
853 	}
854 
855 	if (info->match_flags & XT_CONNTRACK_REPLSRC_PORT) {
856 		if (info->invert_flags & XT_CONNTRACK_REPLSRC_PORT)
857 			printf(" !");
858 		conntrack_dump_ports(prefix, "ctreplsrcport",
859 				     v3 ? info->replsrc_port : ntohs(info->replsrc_port),
860 				     v3 ? info->replsrc_port_high : 0);
861 	}
862 
863 	if (info->match_flags & XT_CONNTRACK_REPLDST_PORT) {
864 		if (info->invert_flags & XT_CONNTRACK_REPLDST_PORT)
865 			printf(" !");
866 		conntrack_dump_ports(prefix, "ctrepldstport",
867 				     v3 ? info->repldst_port : ntohs(info->repldst_port),
868 				     v3 ? info->repldst_port_high : 0);
869 	}
870 
871 	if (info->match_flags & XT_CONNTRACK_STATUS) {
872 		if (info->invert_flags & XT_CONNTRACK_STATUS)
873 			printf(" !");
874 		printf(" %sctstatus", prefix);
875 		print_status(info->status_mask);
876 	}
877 
878 	if (info->match_flags & XT_CONNTRACK_EXPIRES) {
879 		if (info->invert_flags & XT_CONNTRACK_EXPIRES)
880 			printf(" !");
881 		printf(" %sctexpire ", prefix);
882 
883 		if (info->expires_max == info->expires_min)
884 			printf("%u", (unsigned int)info->expires_min);
885 		else
886 			printf("%u:%u", (unsigned int)info->expires_min,
887 			       (unsigned int)info->expires_max);
888 	}
889 
890 	if (info->match_flags & XT_CONNTRACK_DIRECTION) {
891 		if (info->invert_flags & XT_CONNTRACK_DIRECTION)
892 			printf(" %sctdir REPLY", prefix);
893 		else
894 			printf(" %sctdir ORIGINAL", prefix);
895 	}
896 }
897 
898 static const char *
conntrack_print_name_alias(const struct xt_entry_match * match)899 conntrack_print_name_alias(const struct xt_entry_match *match)
900 {
901 	struct xt_conntrack_mtinfo1 *info = (void *)match->data;
902 
903 	return info->match_flags & XT_CONNTRACK_STATE_ALIAS
904 		? "state" : "conntrack";
905 }
906 
conntrack_print(const void * ip,const struct xt_entry_match * match,int numeric)907 static void conntrack_print(const void *ip, const struct xt_entry_match *match,
908                             int numeric)
909 {
910 	matchinfo_print(ip, match, numeric, "");
911 }
912 
913 static void
conntrack1_mt4_print(const void * ip,const struct xt_entry_match * match,int numeric)914 conntrack1_mt4_print(const void *ip, const struct xt_entry_match *match,
915                      int numeric)
916 {
917 	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
918 	struct xt_conntrack_mtinfo3 up;
919 
920 	cinfo_transform(&up, info);
921 	conntrack_dump(&up, "", NFPROTO_IPV4, numeric, false);
922 }
923 
924 static void
conntrack1_mt6_print(const void * ip,const struct xt_entry_match * match,int numeric)925 conntrack1_mt6_print(const void *ip, const struct xt_entry_match *match,
926                      int numeric)
927 {
928 	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
929 	struct xt_conntrack_mtinfo3 up;
930 
931 	cinfo_transform(&up, info);
932 	conntrack_dump(&up, "", NFPROTO_IPV6, numeric, false);
933 }
934 
935 static void
conntrack2_mt_print(const void * ip,const struct xt_entry_match * match,int numeric)936 conntrack2_mt_print(const void *ip, const struct xt_entry_match *match,
937                     int numeric)
938 {
939 	conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric, false);
940 }
941 
942 static void
conntrack2_mt6_print(const void * ip,const struct xt_entry_match * match,int numeric)943 conntrack2_mt6_print(const void *ip, const struct xt_entry_match *match,
944                      int numeric)
945 {
946 	conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric, false);
947 }
948 
949 static void
conntrack3_mt_print(const void * ip,const struct xt_entry_match * match,int numeric)950 conntrack3_mt_print(const void *ip, const struct xt_entry_match *match,
951                     int numeric)
952 {
953 	conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric, true);
954 }
955 
956 static void
conntrack3_mt6_print(const void * ip,const struct xt_entry_match * match,int numeric)957 conntrack3_mt6_print(const void *ip, const struct xt_entry_match *match,
958                      int numeric)
959 {
960 	conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric, true);
961 }
962 
conntrack_save(const void * ip,const struct xt_entry_match * match)963 static void conntrack_save(const void *ip, const struct xt_entry_match *match)
964 {
965 	matchinfo_print(ip, match, 1, "--");
966 }
967 
conntrack3_mt_save(const void * ip,const struct xt_entry_match * match)968 static void conntrack3_mt_save(const void *ip,
969                                const struct xt_entry_match *match)
970 {
971 	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true, true);
972 }
973 
conntrack3_mt6_save(const void * ip,const struct xt_entry_match * match)974 static void conntrack3_mt6_save(const void *ip,
975                                 const struct xt_entry_match *match)
976 {
977 	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true, true);
978 }
979 
conntrack2_mt_save(const void * ip,const struct xt_entry_match * match)980 static void conntrack2_mt_save(const void *ip,
981                                const struct xt_entry_match *match)
982 {
983 	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true, false);
984 }
985 
conntrack2_mt6_save(const void * ip,const struct xt_entry_match * match)986 static void conntrack2_mt6_save(const void *ip,
987                                 const struct xt_entry_match *match)
988 {
989 	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true, false);
990 }
991 
992 static void
conntrack1_mt4_save(const void * ip,const struct xt_entry_match * match)993 conntrack1_mt4_save(const void *ip, const struct xt_entry_match *match)
994 {
995 	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
996 	struct xt_conntrack_mtinfo3 up;
997 
998 	cinfo_transform(&up, info);
999 	conntrack_dump(&up, "--", NFPROTO_IPV4, true, false);
1000 }
1001 
1002 static void
conntrack1_mt6_save(const void * ip,const struct xt_entry_match * match)1003 conntrack1_mt6_save(const void *ip, const struct xt_entry_match *match)
1004 {
1005 	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
1006 	struct xt_conntrack_mtinfo3 up;
1007 
1008 	cinfo_transform(&up, info);
1009 	conntrack_dump(&up, "--", NFPROTO_IPV6, true, false);
1010 }
1011 
1012 static void
state_help(void)1013 state_help(void)
1014 {
1015 	printf(
1016 "state match options:\n"
1017 " [!] --state [INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED][,...]\n"
1018 "				State(s) to match\n");
1019 }
1020 
1021 static const struct xt_option_entry state_opts[] = {
1022 	{.name = "state", .id = O_CTSTATE, .type = XTTYPE_STRING,
1023 	 .flags = XTOPT_MAND | XTOPT_INVERT},
1024 	XTOPT_TABLEEND,
1025 };
1026 
1027 static unsigned int
state_parse_state(const char * state,size_t len)1028 state_parse_state(const char *state, size_t len)
1029 {
1030 	if (strncasecmp(state, "INVALID", len) == 0)
1031 		return XT_CONNTRACK_STATE_INVALID;
1032 	else if (strncasecmp(state, "NEW", len) == 0)
1033 		return XT_CONNTRACK_STATE_BIT(IP_CT_NEW);
1034 	else if (strncasecmp(state, "ESTABLISHED", len) == 0)
1035 		return XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
1036 	else if (strncasecmp(state, "RELATED", len) == 0)
1037 		return XT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
1038 	else if (strncasecmp(state, "UNTRACKED", len) == 0)
1039 		return XT_CONNTRACK_STATE_UNTRACKED;
1040 	return 0;
1041 }
1042 
1043 static unsigned int
state_parse_states(const char * arg)1044 state_parse_states(const char *arg)
1045 {
1046 	const char *comma;
1047 	unsigned int mask = 0, flag;
1048 
1049 	while ((comma = strchr(arg, ',')) != NULL) {
1050 		if (comma == arg)
1051 			goto badstate;
1052 		flag = state_parse_state(arg, comma-arg);
1053 		if (flag == 0)
1054 			goto badstate;
1055 		mask |= flag;
1056 		arg = comma+1;
1057 	}
1058 	if (!*arg)
1059 		xtables_error(PARAMETER_PROBLEM, "\"--state\" requires a list of "
1060 					      "states with no spaces, e.g. "
1061 					      "ESTABLISHED,RELATED");
1062 	if (strlen(arg) == 0)
1063 		goto badstate;
1064 	flag = state_parse_state(arg, strlen(arg));
1065 	if (flag == 0)
1066 		goto badstate;
1067 	mask |= flag;
1068 	return mask;
1069  badstate:
1070 	xtables_error(PARAMETER_PROBLEM, "Bad state \"%s\"", arg);
1071 }
1072 
state_parse(struct xt_option_call * cb)1073 static void state_parse(struct xt_option_call *cb)
1074 {
1075 	struct xt_state_info *sinfo = cb->data;
1076 
1077 	xtables_option_parse(cb);
1078 	sinfo->statemask = state_parse_states(cb->arg);
1079 	if (cb->invert)
1080 		sinfo->statemask = ~sinfo->statemask;
1081 }
1082 
state_ct1_parse(struct xt_option_call * cb)1083 static void state_ct1_parse(struct xt_option_call *cb)
1084 {
1085 	struct xt_conntrack_mtinfo1 *sinfo = cb->data;
1086 
1087 	xtables_option_parse(cb);
1088 	sinfo->match_flags = XT_CONNTRACK_STATE | XT_CONNTRACK_STATE_ALIAS;
1089 	sinfo->state_mask = state_parse_states(cb->arg);
1090 	if (cb->invert)
1091 		sinfo->invert_flags |= XT_CONNTRACK_STATE;
1092 }
1093 
state_ct23_parse(struct xt_option_call * cb)1094 static void state_ct23_parse(struct xt_option_call *cb)
1095 {
1096 	struct xt_conntrack_mtinfo3 *sinfo = cb->data;
1097 
1098 	xtables_option_parse(cb);
1099 	sinfo->match_flags = XT_CONNTRACK_STATE | XT_CONNTRACK_STATE_ALIAS;
1100 	sinfo->state_mask = state_parse_states(cb->arg);
1101 	if (cb->invert)
1102 		sinfo->invert_flags |= XT_CONNTRACK_STATE;
1103 }
1104 
state_print_state(unsigned int statemask)1105 static void state_print_state(unsigned int statemask)
1106 {
1107 	const char *sep = "";
1108 
1109 	if (statemask & XT_CONNTRACK_STATE_INVALID) {
1110 		printf("%sINVALID", sep);
1111 		sep = ",";
1112 	}
1113 	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
1114 		printf("%sNEW", sep);
1115 		sep = ",";
1116 	}
1117 	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
1118 		printf("%sRELATED", sep);
1119 		sep = ",";
1120 	}
1121 	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
1122 		printf("%sESTABLISHED", sep);
1123 		sep = ",";
1124 	}
1125 	if (statemask & XT_CONNTRACK_STATE_UNTRACKED) {
1126 		printf("%sUNTRACKED", sep);
1127 		sep = ",";
1128 	}
1129 }
1130 
1131 static void
state_print(const void * ip,const struct xt_entry_match * match,int numeric)1132 state_print(const void *ip,
1133       const struct xt_entry_match *match,
1134       int numeric)
1135 {
1136 	const struct xt_state_info *sinfo = (const void *)match->data;
1137 
1138 	printf(" state ");
1139 	state_print_state(sinfo->statemask);
1140 }
1141 
state_save(const void * ip,const struct xt_entry_match * match)1142 static void state_save(const void *ip, const struct xt_entry_match *match)
1143 {
1144 	const struct xt_state_info *sinfo = (const void *)match->data;
1145 
1146 	printf(" --state ");
1147 	state_print_state(sinfo->statemask);
1148 }
1149 
state_xlate_print(struct xt_xlate * xl,unsigned int statemask,int inverted)1150 static void state_xlate_print(struct xt_xlate *xl, unsigned int statemask, int inverted)
1151 {
1152 	const char *sep = "";
1153 
1154 	if (inverted)
1155 		xt_xlate_add(xl, "! ");
1156 
1157 	if (statemask & XT_CONNTRACK_STATE_INVALID) {
1158 		xt_xlate_add(xl, "%s%s", sep, "invalid");
1159 		sep = ",";
1160 	}
1161 	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
1162 		xt_xlate_add(xl, "%s%s", sep, "new");
1163 		sep = ",";
1164 	}
1165 	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
1166 		xt_xlate_add(xl, "%s%s", sep, "related");
1167 		sep = ",";
1168 	}
1169 	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
1170 		xt_xlate_add(xl, "%s%s", sep, "established");
1171 		sep = ",";
1172 	}
1173 	if (statemask & XT_CONNTRACK_STATE_UNTRACKED) {
1174 		xt_xlate_add(xl, "%s%s", sep, "untracked");
1175 		sep = ",";
1176 	}
1177 }
1178 
state_xlate(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)1179 static int state_xlate(struct xt_xlate *xl,
1180 		       const struct xt_xlate_mt_params *params)
1181 {
1182 	const struct xt_conntrack_mtinfo3 *sinfo =
1183 		(const void *)params->match->data;
1184 
1185 	xt_xlate_add(xl, "ct state ");
1186 	state_xlate_print(xl, sinfo->state_mask,
1187 			  sinfo->invert_flags & XT_CONNTRACK_STATE);
1188 	return 1;
1189 }
1190 
status_xlate_print(struct xt_xlate * xl,unsigned int statusmask,int inverted)1191 static void status_xlate_print(struct xt_xlate *xl, unsigned int statusmask, int inverted)
1192 {
1193 	const char *sep = "";
1194 
1195 	if (inverted)
1196 		xt_xlate_add(xl, "! ");
1197 
1198 	if (statusmask & IPS_EXPECTED) {
1199 		xt_xlate_add(xl, "%s%s", sep, "expected");
1200 		sep = ",";
1201 	}
1202 	if (statusmask & IPS_SEEN_REPLY) {
1203 		xt_xlate_add(xl, "%s%s", sep, "seen-reply");
1204 		sep = ",";
1205 	}
1206 	if (statusmask & IPS_ASSURED) {
1207 		xt_xlate_add(xl, "%s%s", sep, "assured");
1208 		sep = ",";
1209 	}
1210 	if (statusmask & IPS_CONFIRMED) {
1211 		xt_xlate_add(xl, "%s%s", sep, "confirmed");
1212 		sep = ",";
1213 	}
1214 }
1215 
addr_xlate_print(struct xt_xlate * xl,const union nf_inet_addr * addr,const union nf_inet_addr * mask,unsigned int family)1216 static void addr_xlate_print(struct xt_xlate *xl,
1217 			     const union nf_inet_addr *addr,
1218 			     const union nf_inet_addr *mask,
1219 			     unsigned int family)
1220 {
1221 	if (family == NFPROTO_IPV4) {
1222 		xt_xlate_add(xl, "%s%s", xtables_ipaddr_to_numeric(&addr->in),
1223 		     xtables_ipmask_to_numeric(&mask->in));
1224 	} else if (family == NFPROTO_IPV6) {
1225 		xt_xlate_add(xl, "%s%s", xtables_ip6addr_to_numeric(&addr->in6),
1226 		     xtables_ip6mask_to_numeric(&mask->in6));
1227 	}
1228 }
1229 
_conntrack3_mt_xlate(struct xt_xlate * xl,const struct xt_xlate_mt_params * params,int family)1230 static int _conntrack3_mt_xlate(struct xt_xlate *xl,
1231 				const struct xt_xlate_mt_params *params,
1232 				int family)
1233 {
1234 	const struct xt_conntrack_mtinfo3 *sinfo =
1235 		(const void *)params->match->data;
1236 	char *space = "";
1237 
1238 	if (sinfo->match_flags & XT_CONNTRACK_DIRECTION) {
1239 		xt_xlate_add(xl, "ct direction %s",
1240 			     sinfo->invert_flags & XT_CONNTRACK_DIRECTION ?
1241 			     "reply" : "original");
1242 		space = " ";
1243 	}
1244 
1245 	if (sinfo->match_flags & XT_CONNTRACK_PROTO) {
1246 		xt_xlate_add(xl, "%sct %s protocol %s%u", space,
1247 			     sinfo->invert_flags & XT_CONNTRACK_DIRECTION ?
1248 			     "reply" : "original",
1249 			     sinfo->invert_flags & XT_CONNTRACK_PROTO ?
1250 			     "!= " : "",
1251 			     sinfo->l4proto);
1252 		space = " ";
1253 	}
1254 
1255 	if (sinfo->match_flags & XT_CONNTRACK_STATE) {
1256 		if ((sinfo->state_mask & XT_CONNTRACK_STATE_SNAT) ||
1257 		    (sinfo->state_mask & XT_CONNTRACK_STATE_DNAT)) {
1258 			xt_xlate_add(xl, "%sct status %s%s", space,
1259 				     sinfo->invert_flags & XT_CONNTRACK_STATUS ? "!=" : "",
1260 				     sinfo->state_mask & XT_CONNTRACK_STATE_SNAT ? "snat" : "dnat");
1261 			space = " ";
1262 		} else {
1263 			xt_xlate_add(xl, "%sct state ", space);
1264 			state_xlate_print(xl, sinfo->state_mask,
1265 					  sinfo->invert_flags & XT_CONNTRACK_STATE);
1266 			space = " ";
1267 		}
1268 	}
1269 
1270 	if (sinfo->match_flags & XT_CONNTRACK_STATUS) {
1271 		xt_xlate_add(xl, "%sct status ", space);
1272 		status_xlate_print(xl, sinfo->status_mask,
1273 				   sinfo->invert_flags & XT_CONNTRACK_STATUS);
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, "%u", sinfo->expires_min);
1283 		else
1284 			xt_xlate_add(xl, "%u-%u", sinfo->expires_min,
1285 				     sinfo->expires_max);
1286 		space = " ";
1287 	}
1288 
1289 	if (sinfo->match_flags & XT_CONNTRACK_ORIGSRC) {
1290 		xt_xlate_add(xl, "%sct original saddr %s", space,
1291 			     sinfo->invert_flags & XT_CONNTRACK_ORIGSRC ?
1292 			     "!= " : "");
1293 		addr_xlate_print(xl, &sinfo->origsrc_addr,
1294 				 &sinfo->origsrc_mask, family);
1295 		space = " ";
1296 	}
1297 
1298 	if (sinfo->match_flags & XT_CONNTRACK_ORIGDST) {
1299 		xt_xlate_add(xl, "%sct original daddr %s", space,
1300 			     sinfo->invert_flags & XT_CONNTRACK_ORIGDST ?
1301 			     "!= " : "");
1302 		addr_xlate_print(xl, &sinfo->origdst_addr,
1303 				 &sinfo->origdst_mask, family);
1304 		space = " ";
1305 	}
1306 
1307 	if (sinfo->match_flags & XT_CONNTRACK_REPLSRC) {
1308 		xt_xlate_add(xl, "%sct reply saddr %s", space,
1309 			     sinfo->invert_flags & XT_CONNTRACK_REPLSRC ?
1310 			     "!= " : "");
1311 		addr_xlate_print(xl, &sinfo->replsrc_addr,
1312 				 &sinfo->replsrc_mask, family);
1313 		space = " ";
1314 	}
1315 
1316 	if (sinfo->match_flags & XT_CONNTRACK_REPLDST) {
1317 		xt_xlate_add(xl, "%sct reply daddr %s", space,
1318 			     sinfo->invert_flags & XT_CONNTRACK_REPLDST ?
1319 			     "!= " : "");
1320 		addr_xlate_print(xl, &sinfo->repldst_addr,
1321 				 &sinfo->repldst_mask, family);
1322 		space = " ";
1323 	}
1324 
1325 	if (sinfo->match_flags & XT_CONNTRACK_ORIGSRC_PORT) {
1326 		xt_xlate_add(xl, "%sct original proto-src %s", space,
1327 			     sinfo->invert_flags & XT_CONNTRACK_ORIGSRC_PORT ?
1328 			     "!= " : "");
1329 		if (sinfo->origsrc_port == sinfo->origsrc_port_high)
1330 			xt_xlate_add(xl, "%u", sinfo->origsrc_port);
1331 		else
1332 			xt_xlate_add(xl, "%u-%u", sinfo->origsrc_port,
1333 				     sinfo->origsrc_port_high);
1334 		space = " ";
1335 	}
1336 
1337 	if (sinfo->match_flags & XT_CONNTRACK_ORIGDST_PORT) {
1338 		xt_xlate_add(xl, "%sct original proto-dst %s", space,
1339 			     sinfo->invert_flags & XT_CONNTRACK_ORIGDST_PORT ?
1340 			     "!= " : "");
1341 		if (sinfo->origdst_port == sinfo->origdst_port_high)
1342 			xt_xlate_add(xl, "%u", sinfo->origdst_port);
1343 		else
1344 			xt_xlate_add(xl, "%u-%u", sinfo->origdst_port,
1345 				     sinfo->origdst_port_high);
1346 		space = " ";
1347 	}
1348 
1349 	if (sinfo->match_flags & XT_CONNTRACK_REPLSRC_PORT) {
1350 		xt_xlate_add(xl, "%sct reply proto-src %s", space,
1351 			     sinfo->invert_flags & XT_CONNTRACK_REPLSRC_PORT ?
1352 			     "!= " : "");
1353 		if (sinfo->replsrc_port == sinfo->replsrc_port_high)
1354 			xt_xlate_add(xl, "%u", sinfo->replsrc_port);
1355 		else
1356 			xt_xlate_add(xl, "%u-%u", sinfo->replsrc_port,
1357 				     sinfo->replsrc_port_high);
1358 		space = " ";
1359 	}
1360 
1361 	if (sinfo->match_flags & XT_CONNTRACK_REPLDST_PORT) {
1362 		xt_xlate_add(xl, "%sct reply proto-dst %s", space,
1363 			     sinfo->invert_flags & XT_CONNTRACK_REPLDST_PORT ?
1364 			     "!= " : "");
1365 		if (sinfo->repldst_port == sinfo->repldst_port_high)
1366 			xt_xlate_add(xl, "%u", sinfo->repldst_port);
1367 		else
1368 			xt_xlate_add(xl, "%u-%u", sinfo->repldst_port,
1369 				     sinfo->repldst_port_high);
1370 	}
1371 
1372 	return 1;
1373 }
1374 
conntrack3_mt4_xlate(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)1375 static int conntrack3_mt4_xlate(struct xt_xlate *xl,
1376 				const struct xt_xlate_mt_params *params)
1377 {
1378 	return _conntrack3_mt_xlate(xl, params, NFPROTO_IPV4);
1379 }
1380 
conntrack3_mt6_xlate(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)1381 static int conntrack3_mt6_xlate(struct xt_xlate *xl,
1382 				const struct xt_xlate_mt_params *params)
1383 {
1384 	return _conntrack3_mt_xlate(xl, params, NFPROTO_IPV6);
1385 }
1386 
1387 static struct xtables_match conntrack_mt_reg[] = {
1388 	{
1389 		.version       = XTABLES_VERSION,
1390 		.name          = "conntrack",
1391 		.revision      = 0,
1392 		.family        = NFPROTO_IPV4,
1393 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_info)),
1394 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_info)),
1395 		.help          = conntrack_mt_help,
1396 		.x6_parse      = conntrack_parse,
1397 		.x6_fcheck     = conntrack_mt_check,
1398 		.print         = conntrack_print,
1399 		.save          = conntrack_save,
1400 		.alias	       = conntrack_print_name_alias,
1401 		.x6_options    = conntrack_mt_opts_v0,
1402 	},
1403 	{
1404 		.version       = XTABLES_VERSION,
1405 		.name          = "conntrack",
1406 		.revision      = 1,
1407 		.family        = NFPROTO_IPV4,
1408 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1409 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1410 		.help          = conntrack_mt_help,
1411 		.x6_parse      = conntrack1_mt_parse,
1412 		.x6_fcheck     = conntrack_mt_check,
1413 		.print         = conntrack1_mt4_print,
1414 		.save          = conntrack1_mt4_save,
1415 		.alias	       = conntrack_print_name_alias,
1416 		.x6_options    = conntrack2_mt_opts,
1417 	},
1418 	{
1419 		.version       = XTABLES_VERSION,
1420 		.name          = "conntrack",
1421 		.revision      = 1,
1422 		.family        = NFPROTO_IPV6,
1423 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1424 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1425 		.help          = conntrack_mt_help,
1426 		.x6_parse      = conntrack1_mt_parse,
1427 		.x6_fcheck     = conntrack_mt_check,
1428 		.print         = conntrack1_mt6_print,
1429 		.save          = conntrack1_mt6_save,
1430 		.alias	       = conntrack_print_name_alias,
1431 		.x6_options    = conntrack2_mt_opts,
1432 	},
1433 	{
1434 		.version       = XTABLES_VERSION,
1435 		.name          = "conntrack",
1436 		.revision      = 2,
1437 		.family        = NFPROTO_IPV4,
1438 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1439 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1440 		.help          = conntrack_mt_help,
1441 		.x6_parse      = conntrack2_mt_parse,
1442 		.x6_fcheck     = conntrack_mt_check,
1443 		.print         = conntrack2_mt_print,
1444 		.save          = conntrack2_mt_save,
1445 		.alias	       = conntrack_print_name_alias,
1446 		.x6_options    = conntrack2_mt_opts,
1447 	},
1448 	{
1449 		.version       = XTABLES_VERSION,
1450 		.name          = "conntrack",
1451 		.revision      = 2,
1452 		.family        = NFPROTO_IPV6,
1453 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1454 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1455 		.help          = conntrack_mt_help,
1456 		.x6_parse      = conntrack2_mt_parse,
1457 		.x6_fcheck     = conntrack_mt_check,
1458 		.print         = conntrack2_mt6_print,
1459 		.save          = conntrack2_mt6_save,
1460 		.alias	       = conntrack_print_name_alias,
1461 		.x6_options    = conntrack2_mt_opts,
1462 	},
1463 	{
1464 		.version       = XTABLES_VERSION,
1465 		.name          = "conntrack",
1466 		.revision      = 3,
1467 		.family        = NFPROTO_IPV4,
1468 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1469 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1470 		.help          = conntrack_mt_help,
1471 		.x6_parse      = conntrack3_mt_parse,
1472 		.x6_fcheck     = conntrack_mt_check,
1473 		.print         = conntrack3_mt_print,
1474 		.save          = conntrack3_mt_save,
1475 		.alias	       = conntrack_print_name_alias,
1476 		.x6_options    = conntrack3_mt_opts,
1477 		.xlate	       = conntrack3_mt4_xlate,
1478 	},
1479 	{
1480 		.version       = XTABLES_VERSION,
1481 		.name          = "conntrack",
1482 		.revision      = 3,
1483 		.family        = NFPROTO_IPV6,
1484 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1485 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1486 		.help          = conntrack_mt_help,
1487 		.x6_parse      = conntrack3_mt_parse,
1488 		.x6_fcheck     = conntrack_mt_check,
1489 		.print         = conntrack3_mt6_print,
1490 		.save          = conntrack3_mt6_save,
1491 		.alias	       = conntrack_print_name_alias,
1492 		.x6_options    = conntrack3_mt_opts,
1493 		.xlate	       = conntrack3_mt6_xlate,
1494 	},
1495 	{
1496 		.family        = NFPROTO_UNSPEC,
1497 		.name          = "state",
1498 		.real_name     = "conntrack",
1499 		.revision      = 1,
1500 		.ext_flags     = XTABLES_EXT_ALIAS,
1501 		.version       = XTABLES_VERSION,
1502 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1503 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1504 		.help          = state_help,
1505 		.print         = state_print,
1506 		.save          = state_save,
1507 		.x6_parse      = state_ct1_parse,
1508 		.x6_options    = state_opts,
1509 	},
1510 	{
1511 		.family        = NFPROTO_UNSPEC,
1512 		.name          = "state",
1513 		.real_name     = "conntrack",
1514 		.revision      = 2,
1515 		.ext_flags     = XTABLES_EXT_ALIAS,
1516 		.version       = XTABLES_VERSION,
1517 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1518 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1519 		.help          = state_help,
1520 		.print         = state_print,
1521 		.save          = state_save,
1522 		.x6_parse      = state_ct23_parse,
1523 		.x6_options    = state_opts,
1524 	},
1525 	{
1526 		.family        = NFPROTO_UNSPEC,
1527 		.name          = "state",
1528 		.real_name     = "conntrack",
1529 		.revision      = 3,
1530 		.ext_flags     = XTABLES_EXT_ALIAS,
1531 		.version       = XTABLES_VERSION,
1532 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1533 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1534 		.help          = state_help,
1535 		.print         = state_print,
1536 		.save          = state_save,
1537 		.x6_parse      = state_ct23_parse,
1538 		.x6_options    = state_opts,
1539 		.xlate         = state_xlate,
1540 	},
1541 	{
1542 		.family        = NFPROTO_UNSPEC,
1543 		.name          = "state",
1544 		.revision      = 0,
1545 		.version       = XTABLES_VERSION,
1546 		.size          = XT_ALIGN(sizeof(struct xt_state_info)),
1547 		.userspacesize = XT_ALIGN(sizeof(struct xt_state_info)),
1548 		.help          = state_help,
1549 		.print         = state_print,
1550 		.save          = state_save,
1551 		.x6_parse      = state_parse,
1552 		.x6_options    = state_opts,
1553 	},
1554 };
1555 
_init(void)1556 void _init(void)
1557 {
1558 	xtables_register_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg));
1559 }
1560