• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *	libxt_conntrack
3  *	Shared library add-on to iptables for conntrack matching support.
4  *
5  *	GPL (C) 2001  Marc Boucher (marc@mbsi.ca).
6  *	Copyright © CC Computer Consultants GmbH, 2007 - 2008
7  *	Jan Engelhardt <jengelh@computergmbh.de>
8  */
9 #include <stdbool.h>
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <xtables.h>
15 #include <linux/netfilter/xt_conntrack.h>
16 #include <linux/netfilter/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 		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 
conntrack_mt_parse(struct xt_option_call * cb,uint8_t rev)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 
conntrack1_mt_parse(struct xt_option_call * cb)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 
conntrack2_mt_parse(struct xt_option_call * cb)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 
conntrack3_mt_parse(struct xt_option_call * cb)565 static void conntrack3_mt_parse(struct xt_option_call *cb)
566 {
567 	conntrack_mt_parse(cb, 3);
568 }
569 
conntrack_mt_check(struct xt_fcheck_call * cb)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
print_state(unsigned int statemask)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
print_status(unsigned int statusmask)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
conntrack_dump_addr(const union nf_inet_addr * addr,const union nf_inet_addr * mask,unsigned int family,bool numeric)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
print_addr(const struct in_addr * addr,const struct in_addr * mask,int inv,int numeric)673 print_addr(const struct in_addr *addr, const struct in_addr *mask,
674            int inv, int numeric)
675 {
676 	if (inv)
677 		printf(" !");
678 
679 	if (mask->s_addr == 0L && !numeric)
680 		printf(" anywhere");
681 	else {
682 		if (numeric)
683 			printf(" %s%s",
684 			       xtables_ipaddr_to_numeric(addr),
685 			       xtables_ipmask_to_numeric(mask));
686 		else
687 			printf(" %s%s",
688 			       xtables_ipaddr_to_anyname(addr),
689 			       xtables_ipmask_to_numeric(mask));
690 	}
691 }
692 
693 static void
matchinfo_print(const void * ip,const struct xt_entry_match * match,int numeric,const char * optpfx)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 
779 static void
conntrack_dump_ports(const char * prefix,const char * opt,u_int16_t port_low,u_int16_t port_high)780 conntrack_dump_ports(const char *prefix, const char *opt,
781 		     u_int16_t port_low, u_int16_t port_high)
782 {
783 	if (port_high == 0 || port_low == port_high)
784 		printf(" %s%s %u", prefix, opt, port_low);
785 	else
786 		printf(" %s%s %u:%u", prefix, opt, port_low, port_high);
787 }
788 
789 static void
conntrack_dump(const struct xt_conntrack_mtinfo3 * info,const char * prefix,unsigned int family,bool numeric,bool v3)790 conntrack_dump(const struct xt_conntrack_mtinfo3 *info, const char *prefix,
791                unsigned int family, bool numeric, bool v3)
792 {
793 	if (info->match_flags & XT_CONNTRACK_STATE) {
794 		if (info->invert_flags & XT_CONNTRACK_STATE)
795 			printf(" !");
796 		printf(" %s%s", prefix,
797 			info->match_flags & XT_CONNTRACK_STATE_ALIAS
798 				? "state" : "ctstate");
799 		print_state(info->state_mask);
800 	}
801 
802 	if (info->match_flags & XT_CONNTRACK_PROTO) {
803 		if (info->invert_flags & XT_CONNTRACK_PROTO)
804 			printf(" !");
805 		printf(" %sctproto %u", prefix, info->l4proto);
806 	}
807 
808 	if (info->match_flags & XT_CONNTRACK_ORIGSRC) {
809 		if (info->invert_flags & XT_CONNTRACK_ORIGSRC)
810 			printf(" !");
811 		printf(" %sctorigsrc", prefix);
812 		conntrack_dump_addr(&info->origsrc_addr, &info->origsrc_mask,
813 		                    family, numeric);
814 	}
815 
816 	if (info->match_flags & XT_CONNTRACK_ORIGDST) {
817 		if (info->invert_flags & XT_CONNTRACK_ORIGDST)
818 			printf(" !");
819 		printf(" %sctorigdst", prefix);
820 		conntrack_dump_addr(&info->origdst_addr, &info->origdst_mask,
821 		                    family, numeric);
822 	}
823 
824 	if (info->match_flags & XT_CONNTRACK_REPLSRC) {
825 		if (info->invert_flags & XT_CONNTRACK_REPLSRC)
826 			printf(" !");
827 		printf(" %sctreplsrc", prefix);
828 		conntrack_dump_addr(&info->replsrc_addr, &info->replsrc_mask,
829 		                    family, numeric);
830 	}
831 
832 	if (info->match_flags & XT_CONNTRACK_REPLDST) {
833 		if (info->invert_flags & XT_CONNTRACK_REPLDST)
834 			printf(" !");
835 		printf(" %sctrepldst", prefix);
836 		conntrack_dump_addr(&info->repldst_addr, &info->repldst_mask,
837 		                    family, numeric);
838 	}
839 
840 	if (info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) {
841 		if (info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT)
842 			printf(" !");
843 		conntrack_dump_ports(prefix, "ctorigsrcport",
844 				     v3 ? info->origsrc_port : ntohs(info->origsrc_port),
845 				     v3 ? info->origsrc_port_high : 0);
846 	}
847 
848 	if (info->match_flags & XT_CONNTRACK_ORIGDST_PORT) {
849 		if (info->invert_flags & XT_CONNTRACK_ORIGDST_PORT)
850 			printf(" !");
851 		conntrack_dump_ports(prefix, "ctorigdstport",
852 				     v3 ? info->origdst_port : ntohs(info->origdst_port),
853 				     v3 ? info->origdst_port_high : 0);
854 	}
855 
856 	if (info->match_flags & XT_CONNTRACK_REPLSRC_PORT) {
857 		if (info->invert_flags & XT_CONNTRACK_REPLSRC_PORT)
858 			printf(" !");
859 		conntrack_dump_ports(prefix, "ctreplsrcport",
860 				     v3 ? info->replsrc_port : ntohs(info->replsrc_port),
861 				     v3 ? info->replsrc_port_high : 0);
862 	}
863 
864 	if (info->match_flags & XT_CONNTRACK_REPLDST_PORT) {
865 		if (info->invert_flags & XT_CONNTRACK_REPLDST_PORT)
866 			printf(" !");
867 		conntrack_dump_ports(prefix, "ctrepldstport",
868 				     v3 ? info->repldst_port : ntohs(info->repldst_port),
869 				     v3 ? info->repldst_port_high : 0);
870 	}
871 
872 	if (info->match_flags & XT_CONNTRACK_STATUS) {
873 		if (info->invert_flags & XT_CONNTRACK_STATUS)
874 			printf(" !");
875 		printf(" %sctstatus", prefix);
876 		print_status(info->status_mask);
877 	}
878 
879 	if (info->match_flags & XT_CONNTRACK_EXPIRES) {
880 		if (info->invert_flags & XT_CONNTRACK_EXPIRES)
881 			printf(" !");
882 		printf(" %sctexpire ", prefix);
883 
884 		if (info->expires_max == info->expires_min)
885 			printf("%u", (unsigned int)info->expires_min);
886 		else
887 			printf("%u:%u", (unsigned int)info->expires_min,
888 			       (unsigned int)info->expires_max);
889 	}
890 
891 	if (info->match_flags & XT_CONNTRACK_DIRECTION) {
892 		if (info->invert_flags & XT_CONNTRACK_DIRECTION)
893 			printf(" %sctdir REPLY", prefix);
894 		else
895 			printf(" %sctdir ORIGINAL", prefix);
896 	}
897 }
898 
899 static const char *
conntrack_print_name_alias(const struct xt_entry_match * match)900 conntrack_print_name_alias(const struct xt_entry_match *match)
901 {
902 	struct xt_conntrack_mtinfo1 *info = (void *)match->data;
903 
904 	return info->match_flags & XT_CONNTRACK_STATE_ALIAS
905 		? "state" : "conntrack";
906 }
907 
conntrack_print(const void * ip,const struct xt_entry_match * match,int numeric)908 static void conntrack_print(const void *ip, const struct xt_entry_match *match,
909                             int numeric)
910 {
911 	matchinfo_print(ip, match, numeric, "");
912 }
913 
914 static void
conntrack1_mt4_print(const void * ip,const struct xt_entry_match * match,int numeric)915 conntrack1_mt4_print(const void *ip, const struct xt_entry_match *match,
916                      int numeric)
917 {
918 	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
919 	struct xt_conntrack_mtinfo3 up;
920 
921 	cinfo_transform(&up, info);
922 	conntrack_dump(&up, "", NFPROTO_IPV4, numeric, false);
923 }
924 
925 static void
conntrack1_mt6_print(const void * ip,const struct xt_entry_match * match,int numeric)926 conntrack1_mt6_print(const void *ip, const struct xt_entry_match *match,
927                      int numeric)
928 {
929 	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
930 	struct xt_conntrack_mtinfo3 up;
931 
932 	cinfo_transform(&up, info);
933 	conntrack_dump(&up, "", NFPROTO_IPV6, numeric, false);
934 }
935 
936 static void
conntrack2_mt_print(const void * ip,const struct xt_entry_match * match,int numeric)937 conntrack2_mt_print(const void *ip, const struct xt_entry_match *match,
938                     int numeric)
939 {
940 	conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric, false);
941 }
942 
943 static void
conntrack2_mt6_print(const void * ip,const struct xt_entry_match * match,int numeric)944 conntrack2_mt6_print(const void *ip, const struct xt_entry_match *match,
945                      int numeric)
946 {
947 	conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric, false);
948 }
949 
950 static void
conntrack3_mt_print(const void * ip,const struct xt_entry_match * match,int numeric)951 conntrack3_mt_print(const void *ip, const struct xt_entry_match *match,
952                     int numeric)
953 {
954 	conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric, true);
955 }
956 
957 static void
conntrack3_mt6_print(const void * ip,const struct xt_entry_match * match,int numeric)958 conntrack3_mt6_print(const void *ip, const struct xt_entry_match *match,
959                      int numeric)
960 {
961 	conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric, true);
962 }
963 
conntrack_save(const void * ip,const struct xt_entry_match * match)964 static void conntrack_save(const void *ip, const struct xt_entry_match *match)
965 {
966 	matchinfo_print(ip, match, 1, "--");
967 }
968 
conntrack3_mt_save(const void * ip,const struct xt_entry_match * match)969 static void conntrack3_mt_save(const void *ip,
970                                const struct xt_entry_match *match)
971 {
972 	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true, true);
973 }
974 
conntrack3_mt6_save(const void * ip,const struct xt_entry_match * match)975 static void conntrack3_mt6_save(const void *ip,
976                                 const struct xt_entry_match *match)
977 {
978 	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true, true);
979 }
980 
conntrack2_mt_save(const void * ip,const struct xt_entry_match * match)981 static void conntrack2_mt_save(const void *ip,
982                                const struct xt_entry_match *match)
983 {
984 	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true, false);
985 }
986 
conntrack2_mt6_save(const void * ip,const struct xt_entry_match * match)987 static void conntrack2_mt6_save(const void *ip,
988                                 const struct xt_entry_match *match)
989 {
990 	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true, false);
991 }
992 
993 static void
conntrack1_mt4_save(const void * ip,const struct xt_entry_match * match)994 conntrack1_mt4_save(const void *ip, const struct xt_entry_match *match)
995 {
996 	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
997 	struct xt_conntrack_mtinfo3 up;
998 
999 	cinfo_transform(&up, info);
1000 	conntrack_dump(&up, "--", NFPROTO_IPV4, true, false);
1001 }
1002 
1003 static void
conntrack1_mt6_save(const void * ip,const struct xt_entry_match * match)1004 conntrack1_mt6_save(const void *ip, const struct xt_entry_match *match)
1005 {
1006 	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
1007 	struct xt_conntrack_mtinfo3 up;
1008 
1009 	cinfo_transform(&up, info);
1010 	conntrack_dump(&up, "--", NFPROTO_IPV6, true, false);
1011 }
1012 
1013 static void
state_help(void)1014 state_help(void)
1015 {
1016 	printf(
1017 "state match options:\n"
1018 " [!] --state [INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED][,...]\n"
1019 "				State(s) to match\n");
1020 }
1021 
1022 static const struct xt_option_entry state_opts[] = {
1023 	{.name = "state", .id = O_CTSTATE, .type = XTTYPE_STRING,
1024 	 .flags = XTOPT_MAND | XTOPT_INVERT},
1025 	XTOPT_TABLEEND,
1026 };
1027 
1028 static unsigned int
state_parse_state(const char * state,size_t len)1029 state_parse_state(const char *state, size_t len)
1030 {
1031 	if (strncasecmp(state, "INVALID", len) == 0)
1032 		return XT_CONNTRACK_STATE_INVALID;
1033 	else if (strncasecmp(state, "NEW", len) == 0)
1034 		return XT_CONNTRACK_STATE_BIT(IP_CT_NEW);
1035 	else if (strncasecmp(state, "ESTABLISHED", len) == 0)
1036 		return XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
1037 	else if (strncasecmp(state, "RELATED", len) == 0)
1038 		return XT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
1039 	else if (strncasecmp(state, "UNTRACKED", len) == 0)
1040 		return XT_CONNTRACK_STATE_UNTRACKED;
1041 	return 0;
1042 }
1043 
1044 static unsigned int
state_parse_states(const char * arg)1045 state_parse_states(const char *arg)
1046 {
1047 	const char *comma;
1048 	unsigned int mask = 0, flag;
1049 
1050 	while ((comma = strchr(arg, ',')) != NULL) {
1051 		if (comma == arg)
1052 			goto badstate;
1053 		flag = state_parse_state(arg, comma-arg);
1054 		if (flag == 0)
1055 			goto badstate;
1056 		mask |= flag;
1057 		arg = comma+1;
1058 	}
1059 	if (!*arg)
1060 		xtables_error(PARAMETER_PROBLEM, "\"--state\" requires a list of "
1061 					      "states with no spaces, e.g. "
1062 					      "ESTABLISHED,RELATED");
1063 	if (strlen(arg) == 0)
1064 		goto badstate;
1065 	flag = state_parse_state(arg, strlen(arg));
1066 	if (flag == 0)
1067 		goto badstate;
1068 	mask |= flag;
1069 	return mask;
1070  badstate:
1071 	xtables_error(PARAMETER_PROBLEM, "Bad state \"%s\"", arg);
1072 }
1073 
state_parse(struct xt_option_call * cb)1074 static void state_parse(struct xt_option_call *cb)
1075 {
1076 	struct xt_state_info *sinfo = cb->data;
1077 
1078 	xtables_option_parse(cb);
1079 	sinfo->statemask = state_parse_states(cb->arg);
1080 	if (cb->invert)
1081 		sinfo->statemask = ~sinfo->statemask;
1082 }
1083 
state_ct1_parse(struct xt_option_call * cb)1084 static void state_ct1_parse(struct xt_option_call *cb)
1085 {
1086 	struct xt_conntrack_mtinfo1 *sinfo = cb->data;
1087 
1088 	xtables_option_parse(cb);
1089 	sinfo->match_flags = XT_CONNTRACK_STATE | XT_CONNTRACK_STATE_ALIAS;
1090 	sinfo->state_mask = state_parse_states(cb->arg);
1091 	if (cb->invert)
1092 		sinfo->invert_flags |= XT_CONNTRACK_STATE;
1093 }
1094 
state_ct23_parse(struct xt_option_call * cb)1095 static void state_ct23_parse(struct xt_option_call *cb)
1096 {
1097 	struct xt_conntrack_mtinfo3 *sinfo = cb->data;
1098 
1099 	xtables_option_parse(cb);
1100 	sinfo->match_flags = XT_CONNTRACK_STATE | XT_CONNTRACK_STATE_ALIAS;
1101 	sinfo->state_mask = state_parse_states(cb->arg);
1102 	if (cb->invert)
1103 		sinfo->invert_flags |= XT_CONNTRACK_STATE;
1104 }
1105 
state_print_state(unsigned int statemask)1106 static void state_print_state(unsigned int statemask)
1107 {
1108 	const char *sep = "";
1109 
1110 	if (statemask & XT_CONNTRACK_STATE_INVALID) {
1111 		printf("%sINVALID", sep);
1112 		sep = ",";
1113 	}
1114 	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
1115 		printf("%sNEW", sep);
1116 		sep = ",";
1117 	}
1118 	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
1119 		printf("%sRELATED", sep);
1120 		sep = ",";
1121 	}
1122 	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
1123 		printf("%sESTABLISHED", sep);
1124 		sep = ",";
1125 	}
1126 	if (statemask & XT_CONNTRACK_STATE_UNTRACKED) {
1127 		printf("%sUNTRACKED", sep);
1128 		sep = ",";
1129 	}
1130 }
1131 
1132 static void
state_print(const void * ip,const struct xt_entry_match * match,int numeric)1133 state_print(const void *ip,
1134       const struct xt_entry_match *match,
1135       int numeric)
1136 {
1137 	const struct xt_state_info *sinfo = (const void *)match->data;
1138 
1139 	printf(" state ");
1140 	state_print_state(sinfo->statemask);
1141 }
1142 
state_save(const void * ip,const struct xt_entry_match * match)1143 static void state_save(const void *ip, const struct xt_entry_match *match)
1144 {
1145 	const struct xt_state_info *sinfo = (const void *)match->data;
1146 
1147 	printf(" --state ");
1148 	state_print_state(sinfo->statemask);
1149 }
1150 
state_xlate_print(struct xt_xlate * xl,unsigned int statemask)1151 static void state_xlate_print(struct xt_xlate *xl, unsigned int statemask)
1152 {
1153 	const char *sep = "";
1154 
1155 	if (statemask & XT_CONNTRACK_STATE_INVALID) {
1156 		xt_xlate_add(xl, "%s%s", sep, "invalid");
1157 		sep = ",";
1158 	}
1159 	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
1160 		xt_xlate_add(xl, "%s%s", sep, "new");
1161 		sep = ",";
1162 	}
1163 	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
1164 		xt_xlate_add(xl, "%s%s", sep, "related");
1165 		sep = ",";
1166 	}
1167 	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
1168 		xt_xlate_add(xl, "%s%s", sep, "established");
1169 		sep = ",";
1170 	}
1171 	if (statemask & XT_CONNTRACK_STATE_UNTRACKED) {
1172 		xt_xlate_add(xl, "%s%s", sep, "untracked");
1173 		sep = ",";
1174 	}
1175 }
1176 
state_xlate(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)1177 static int state_xlate(struct xt_xlate *xl,
1178 		       const struct xt_xlate_mt_params *params)
1179 {
1180 	const struct xt_conntrack_mtinfo3 *sinfo =
1181 		(const void *)params->match->data;
1182 
1183 	xt_xlate_add(xl, "ct state %s", sinfo->invert_flags & XT_CONNTRACK_STATE ?
1184 					"!= " : "");
1185 	state_xlate_print(xl, sinfo->state_mask);
1186 	xt_xlate_add(xl, " ");
1187 	return 1;
1188 }
1189 
status_xlate_print(struct xt_xlate * xl,unsigned int statusmask)1190 static void status_xlate_print(struct xt_xlate *xl, unsigned int statusmask)
1191 {
1192 	const char *sep = "";
1193 
1194 	if (statusmask & IPS_EXPECTED) {
1195 		xt_xlate_add(xl, "%s%s", sep, "expected");
1196 		sep = ",";
1197 	}
1198 	if (statusmask & IPS_SEEN_REPLY) {
1199 		xt_xlate_add(xl, "%s%s", sep, "seen-reply");
1200 		sep = ",";
1201 	}
1202 	if (statusmask & IPS_ASSURED) {
1203 		xt_xlate_add(xl, "%s%s", sep, "assured");
1204 		sep = ",";
1205 	}
1206 	if (statusmask & IPS_CONFIRMED) {
1207 		xt_xlate_add(xl, "%s%s", sep, "confirmed");
1208 		sep = ",";
1209 	}
1210 }
1211 
addr_xlate_print(struct xt_xlate * xl,const union nf_inet_addr * addr,const union nf_inet_addr * mask,unsigned int family)1212 static void addr_xlate_print(struct xt_xlate *xl,
1213 			     const union nf_inet_addr *addr,
1214 			     const union nf_inet_addr *mask,
1215 			     unsigned int family)
1216 {
1217 	if (family == NFPROTO_IPV4) {
1218 		xt_xlate_add(xl, "%s%s", xtables_ipaddr_to_numeric(&addr->in),
1219 		     xtables_ipmask_to_numeric(&mask->in));
1220 	} else if (family == NFPROTO_IPV6) {
1221 		xt_xlate_add(xl, "%s%s", xtables_ip6addr_to_numeric(&addr->in6),
1222 		     xtables_ip6mask_to_numeric(&mask->in6));
1223 	}
1224 }
1225 
_conntrack3_mt_xlate(struct xt_xlate * xl,const struct xt_xlate_mt_params * params,int family)1226 static int _conntrack3_mt_xlate(struct xt_xlate *xl,
1227 				const struct xt_xlate_mt_params *params,
1228 				int family)
1229 {
1230 	const struct xt_conntrack_mtinfo3 *sinfo =
1231 		(const void *)params->match->data;
1232 	char *space = "";
1233 
1234 	if (sinfo->match_flags & XT_CONNTRACK_DIRECTION) {
1235 		xt_xlate_add(xl, "ct direction %s",
1236 			     sinfo->invert_flags & XT_CONNTRACK_DIRECTION ?
1237 			     "reply" : "original");
1238 		space = " ";
1239 	}
1240 
1241 	if (sinfo->match_flags & XT_CONNTRACK_PROTO) {
1242 		xt_xlate_add(xl, "%sct %s protocol %s%u", space,
1243 			     sinfo->invert_flags & XT_CONNTRACK_DIRECTION ?
1244 			     "reply" : "original",
1245 			     sinfo->invert_flags & XT_CONNTRACK_PROTO ?
1246 			     "!= " : "",
1247 			     sinfo->l4proto);
1248 		space = " ";
1249 	}
1250 
1251 	if (sinfo->match_flags & XT_CONNTRACK_STATE) {
1252 		if ((sinfo->state_mask & XT_CONNTRACK_STATE_SNAT) ||
1253 		    (sinfo->state_mask & XT_CONNTRACK_STATE_DNAT)) {
1254 			xt_xlate_add(xl, "%sct status %s%s", space,
1255 				     sinfo->invert_flags & XT_CONNTRACK_STATUS ? "!=" : "",
1256 				     sinfo->state_mask & XT_CONNTRACK_STATE_SNAT ? "snat" : "dnat");
1257 			space = " ";
1258 		} else {
1259 			xt_xlate_add(xl, "%sct state %s", space,
1260 				     sinfo->invert_flags & XT_CONNTRACK_STATE ?
1261 				     "!= " : "");
1262 			state_xlate_print(xl, sinfo->state_mask);
1263 			space = " ";
1264 		}
1265 	}
1266 
1267 	if (sinfo->match_flags & XT_CONNTRACK_STATUS) {
1268 		xt_xlate_add(xl, "%sct status %s", space,
1269 			     sinfo->invert_flags & XT_CONNTRACK_STATUS ?
1270 			     "!= " : "");
1271 		status_xlate_print(xl, sinfo->status_mask);
1272 		space = " ";
1273 	}
1274 
1275 	if (sinfo->match_flags & XT_CONNTRACK_EXPIRES) {
1276 		xt_xlate_add(xl, "%sct expiration %s", space,
1277 			     sinfo->invert_flags & XT_CONNTRACK_EXPIRES ?
1278 			     "!= " : "");
1279 		if (sinfo->expires_max == sinfo->expires_min)
1280 			xt_xlate_add(xl, "%u", sinfo->expires_min);
1281 		else
1282 			xt_xlate_add(xl, "%u-%u", sinfo->expires_min,
1283 				     sinfo->expires_max);
1284 		space = " ";
1285 	}
1286 
1287 	if (sinfo->match_flags & XT_CONNTRACK_ORIGSRC) {
1288 		if (&sinfo->origsrc_addr == 0L)
1289 			return 0;
1290 
1291 		xt_xlate_add(xl, "%sct original saddr %s", space,
1292 			     sinfo->invert_flags & XT_CONNTRACK_ORIGSRC ?
1293 			     "!= " : "");
1294 		addr_xlate_print(xl, &sinfo->origsrc_addr,
1295 				 &sinfo->origsrc_mask, family);
1296 		space = " ";
1297 	}
1298 
1299 	if (sinfo->match_flags & XT_CONNTRACK_ORIGDST) {
1300 		if (&sinfo->origdst_addr == 0L)
1301 			return 0;
1302 
1303 		xt_xlate_add(xl, "%sct original daddr %s", space,
1304 			     sinfo->invert_flags & XT_CONNTRACK_ORIGDST ?
1305 			     "!= " : "");
1306 		addr_xlate_print(xl, &sinfo->origdst_addr,
1307 				 &sinfo->origdst_mask, family);
1308 		space = " ";
1309 	}
1310 
1311 	if (sinfo->match_flags & XT_CONNTRACK_REPLSRC) {
1312 		if (&sinfo->replsrc_addr == 0L)
1313 			return 0;
1314 
1315 		xt_xlate_add(xl, "%sct reply saddr %s", space,
1316 			     sinfo->invert_flags & XT_CONNTRACK_REPLSRC ?
1317 			     "!= " : "");
1318 		addr_xlate_print(xl, &sinfo->replsrc_addr,
1319 				 &sinfo->replsrc_mask, family);
1320 		space = " ";
1321 	}
1322 
1323 	if (sinfo->match_flags & XT_CONNTRACK_REPLDST) {
1324 		if (&sinfo->repldst_addr == 0L)
1325 			return 0;
1326 
1327 		xt_xlate_add(xl, "%sct reply daddr %s", space,
1328 			     sinfo->invert_flags & XT_CONNTRACK_REPLDST ?
1329 			     "!= " : "");
1330 		addr_xlate_print(xl, &sinfo->repldst_addr,
1331 				 &sinfo->repldst_mask, family);
1332 		space = " ";
1333 	}
1334 
1335 	if (sinfo->match_flags & XT_CONNTRACK_ORIGSRC_PORT) {
1336 		xt_xlate_add(xl, "%sct original proto-src %s", space,
1337 			     sinfo->invert_flags & XT_CONNTRACK_ORIGSRC_PORT ?
1338 			     "!= " : "");
1339 		if (sinfo->origsrc_port == sinfo->origsrc_port_high)
1340 			xt_xlate_add(xl, "%u", sinfo->origsrc_port);
1341 		else
1342 			xt_xlate_add(xl, "%u-%u", sinfo->origsrc_port,
1343 				     sinfo->origsrc_port_high);
1344 		space = " ";
1345 	}
1346 
1347 	if (sinfo->match_flags & XT_CONNTRACK_ORIGDST_PORT) {
1348 		xt_xlate_add(xl, "%sct original proto-dst %s", space,
1349 			     sinfo->invert_flags & XT_CONNTRACK_ORIGDST_PORT ?
1350 			     "!= " : "");
1351 		if (sinfo->origdst_port == sinfo->origdst_port_high)
1352 			xt_xlate_add(xl, "%u", sinfo->origdst_port);
1353 		else
1354 			xt_xlate_add(xl, "%u-%u", sinfo->origdst_port,
1355 				     sinfo->origdst_port_high);
1356 		space = " ";
1357 	}
1358 
1359 	if (sinfo->match_flags & XT_CONNTRACK_REPLSRC_PORT) {
1360 		xt_xlate_add(xl, "%sct reply proto-src %s", space,
1361 			     sinfo->invert_flags & XT_CONNTRACK_REPLSRC_PORT ?
1362 			     "!= " : "");
1363 		if (sinfo->replsrc_port == sinfo->replsrc_port_high)
1364 			xt_xlate_add(xl, "%u", sinfo->replsrc_port);
1365 		else
1366 			xt_xlate_add(xl, "%u-%u", sinfo->replsrc_port,
1367 				     sinfo->replsrc_port_high);
1368 		space = " ";
1369 	}
1370 
1371 	if (sinfo->match_flags & XT_CONNTRACK_REPLDST_PORT) {
1372 		xt_xlate_add(xl, "%sct reply proto-dst %s", space,
1373 			     sinfo->invert_flags & XT_CONNTRACK_REPLDST_PORT ?
1374 			     "!= " : "");
1375 		if (sinfo->repldst_port == sinfo->repldst_port_high)
1376 			xt_xlate_add(xl, "%u", sinfo->repldst_port);
1377 		else
1378 			xt_xlate_add(xl, "%u-%u", sinfo->repldst_port,
1379 				     sinfo->repldst_port_high);
1380 	}
1381 
1382 	return 1;
1383 }
1384 
conntrack3_mt4_xlate(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)1385 static int conntrack3_mt4_xlate(struct xt_xlate *xl,
1386 				const struct xt_xlate_mt_params *params)
1387 {
1388 	return _conntrack3_mt_xlate(xl, params, NFPROTO_IPV4);
1389 }
1390 
conntrack3_mt6_xlate(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)1391 static int conntrack3_mt6_xlate(struct xt_xlate *xl,
1392 				const struct xt_xlate_mt_params *params)
1393 {
1394 	return _conntrack3_mt_xlate(xl, params, NFPROTO_IPV6);
1395 }
1396 
1397 static struct xtables_match conntrack_mt_reg[] = {
1398 	{
1399 		.version       = XTABLES_VERSION,
1400 		.name          = "conntrack",
1401 		.revision      = 0,
1402 		.family        = NFPROTO_IPV4,
1403 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_info)),
1404 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_info)),
1405 		.help          = conntrack_mt_help,
1406 		.x6_parse      = conntrack_parse,
1407 		.x6_fcheck     = conntrack_mt_check,
1408 		.print         = conntrack_print,
1409 		.save          = conntrack_save,
1410 		.alias	       = conntrack_print_name_alias,
1411 		.x6_options    = conntrack_mt_opts_v0,
1412 	},
1413 	{
1414 		.version       = XTABLES_VERSION,
1415 		.name          = "conntrack",
1416 		.revision      = 1,
1417 		.family        = NFPROTO_IPV4,
1418 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1419 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1420 		.help          = conntrack_mt_help,
1421 		.x6_parse      = conntrack1_mt_parse,
1422 		.x6_fcheck     = conntrack_mt_check,
1423 		.print         = conntrack1_mt4_print,
1424 		.save          = conntrack1_mt4_save,
1425 		.alias	       = conntrack_print_name_alias,
1426 		.x6_options    = conntrack2_mt_opts,
1427 	},
1428 	{
1429 		.version       = XTABLES_VERSION,
1430 		.name          = "conntrack",
1431 		.revision      = 1,
1432 		.family        = NFPROTO_IPV6,
1433 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1434 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1435 		.help          = conntrack_mt_help,
1436 		.x6_parse      = conntrack1_mt_parse,
1437 		.x6_fcheck     = conntrack_mt_check,
1438 		.print         = conntrack1_mt6_print,
1439 		.save          = conntrack1_mt6_save,
1440 		.alias	       = conntrack_print_name_alias,
1441 		.x6_options    = conntrack2_mt_opts,
1442 	},
1443 	{
1444 		.version       = XTABLES_VERSION,
1445 		.name          = "conntrack",
1446 		.revision      = 2,
1447 		.family        = NFPROTO_IPV4,
1448 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1449 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1450 		.help          = conntrack_mt_help,
1451 		.x6_parse      = conntrack2_mt_parse,
1452 		.x6_fcheck     = conntrack_mt_check,
1453 		.print         = conntrack2_mt_print,
1454 		.save          = conntrack2_mt_save,
1455 		.alias	       = conntrack_print_name_alias,
1456 		.x6_options    = conntrack2_mt_opts,
1457 	},
1458 	{
1459 		.version       = XTABLES_VERSION,
1460 		.name          = "conntrack",
1461 		.revision      = 2,
1462 		.family        = NFPROTO_IPV6,
1463 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1464 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1465 		.help          = conntrack_mt_help,
1466 		.x6_parse      = conntrack2_mt_parse,
1467 		.x6_fcheck     = conntrack_mt_check,
1468 		.print         = conntrack2_mt6_print,
1469 		.save          = conntrack2_mt6_save,
1470 		.alias	       = conntrack_print_name_alias,
1471 		.x6_options    = conntrack2_mt_opts,
1472 	},
1473 	{
1474 		.version       = XTABLES_VERSION,
1475 		.name          = "conntrack",
1476 		.revision      = 3,
1477 		.family        = NFPROTO_IPV4,
1478 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1479 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1480 		.help          = conntrack_mt_help,
1481 		.x6_parse      = conntrack3_mt_parse,
1482 		.x6_fcheck     = conntrack_mt_check,
1483 		.print         = conntrack3_mt_print,
1484 		.save          = conntrack3_mt_save,
1485 		.alias	       = conntrack_print_name_alias,
1486 		.x6_options    = conntrack3_mt_opts,
1487 		.xlate	       = conntrack3_mt4_xlate,
1488 	},
1489 	{
1490 		.version       = XTABLES_VERSION,
1491 		.name          = "conntrack",
1492 		.revision      = 3,
1493 		.family        = NFPROTO_IPV6,
1494 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1495 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1496 		.help          = conntrack_mt_help,
1497 		.x6_parse      = conntrack3_mt_parse,
1498 		.x6_fcheck     = conntrack_mt_check,
1499 		.print         = conntrack3_mt6_print,
1500 		.save          = conntrack3_mt6_save,
1501 		.alias	       = conntrack_print_name_alias,
1502 		.x6_options    = conntrack3_mt_opts,
1503 		.xlate	       = conntrack3_mt6_xlate,
1504 	},
1505 	{
1506 		.family        = NFPROTO_UNSPEC,
1507 		.name          = "state",
1508 		.real_name     = "conntrack",
1509 		.revision      = 1,
1510 		.ext_flags     = XTABLES_EXT_ALIAS,
1511 		.version       = XTABLES_VERSION,
1512 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1513 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1514 		.help          = state_help,
1515 		.print         = state_print,
1516 		.save          = state_save,
1517 		.x6_parse      = state_ct1_parse,
1518 		.x6_options    = state_opts,
1519 	},
1520 	{
1521 		.family        = NFPROTO_UNSPEC,
1522 		.name          = "state",
1523 		.real_name     = "conntrack",
1524 		.revision      = 2,
1525 		.ext_flags     = XTABLES_EXT_ALIAS,
1526 		.version       = XTABLES_VERSION,
1527 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1528 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1529 		.help          = state_help,
1530 		.print         = state_print,
1531 		.save          = state_save,
1532 		.x6_parse      = state_ct23_parse,
1533 		.x6_options    = state_opts,
1534 	},
1535 	{
1536 		.family        = NFPROTO_UNSPEC,
1537 		.name          = "state",
1538 		.real_name     = "conntrack",
1539 		.revision      = 3,
1540 		.ext_flags     = XTABLES_EXT_ALIAS,
1541 		.version       = XTABLES_VERSION,
1542 		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1543 		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1544 		.help          = state_help,
1545 		.print         = state_print,
1546 		.save          = state_save,
1547 		.x6_parse      = state_ct23_parse,
1548 		.x6_options    = state_opts,
1549 		.xlate         = state_xlate,
1550 	},
1551 	{
1552 		.family        = NFPROTO_UNSPEC,
1553 		.name          = "state",
1554 		.revision      = 0,
1555 		.version       = XTABLES_VERSION,
1556 		.size          = XT_ALIGN(sizeof(struct xt_state_info)),
1557 		.userspacesize = XT_ALIGN(sizeof(struct xt_state_info)),
1558 		.help          = state_help,
1559 		.print         = state_print,
1560 		.save          = state_save,
1561 		.x6_parse      = state_parse,
1562 		.x6_options    = state_opts,
1563 	},
1564 };
1565 
_init(void)1566 void _init(void)
1567 {
1568 	xtables_register_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg));
1569 }
1570