libxt_conntrack.c revision 6944f2c8190f1c4319aeac748470c71b0ba45025
1/*
2 *	libxt_conntrack
3 *	Shared library add-on to iptables for conntrack matching support.
4 *
5 *	GPL (C) 2001  Marc Boucher (marc@mbsi.ca).
6 *	Copyright © CC Computer Consultants GmbH, 2007 - 2008
7 *	Jan Engelhardt <jengelh@computergmbh.de>
8 */
9#include <stdbool.h>
10#include <stdint.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <xtables.h>
15#include <linux/netfilter/xt_conntrack.h>
16#include <linux/netfilter/nf_conntrack_common.h>
17
18struct ip_conntrack_old_tuple {
19	struct {
20		__be32 ip;
21		union {
22			__u16 all;
23		} u;
24	} src;
25
26	struct {
27		__be32 ip;
28		union {
29			__u16 all;
30		} u;
31
32		/* The protocol. */
33		__u16 protonum;
34	} dst;
35};
36
37struct xt_conntrack_info {
38	unsigned int statemask, statusmask;
39
40	struct ip_conntrack_old_tuple tuple[IP_CT_DIR_MAX];
41	struct in_addr sipmsk[IP_CT_DIR_MAX], dipmsk[IP_CT_DIR_MAX];
42
43	unsigned long expires_min, expires_max;
44
45	/* Flags word */
46	uint8_t flags;
47	/* Inverse flags */
48	uint8_t invflags;
49};
50
51enum {
52	O_CTSTATE = 0,
53	O_CTPROTO,
54	O_CTORIGSRC,
55	O_CTORIGDST,
56	O_CTREPLSRC,
57	O_CTREPLDST,
58	O_CTORIGSRCPORT,
59	O_CTORIGDSTPORT,
60	O_CTREPLSRCPORT,
61	O_CTREPLDSTPORT,
62	O_CTSTATUS,
63	O_CTEXPIRE,
64	O_CTDIR,
65};
66
67static void conntrack_mt_help(void)
68{
69	printf(
70"conntrack match options:\n"
71"[!] --ctstate {INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED|SNAT|DNAT}[,...]\n"
72"                               State(s) to match\n"
73"[!] --ctproto proto            Protocol to match; by number or name, e.g. \"tcp\"\n"
74"[!] --ctorigsrc address[/mask]\n"
75"[!] --ctorigdst address[/mask]\n"
76"[!] --ctreplsrc address[/mask]\n"
77"[!] --ctrepldst address[/mask]\n"
78"                               Original/Reply source/destination address\n"
79"[!] --ctorigsrcport port\n"
80"[!] --ctorigdstport port\n"
81"[!] --ctreplsrcport port\n"
82"[!] --ctrepldstport port\n"
83"                               TCP/UDP/SCTP orig./reply source/destination port\n"
84"[!] --ctstatus {NONE|EXPECTED|SEEN_REPLY|ASSURED|CONFIRMED}[,...]\n"
85"                               Status(es) to match\n"
86"[!] --ctexpire time[:time]     Match remaining lifetime in seconds against\n"
87"                               value or range of values (inclusive)\n"
88"    --ctdir {ORIGINAL|REPLY}   Flow direction of packet\n");
89}
90
91#define s struct xt_conntrack_info /* for v0 */
92static const struct xt_option_entry conntrack_mt_opts_v0[] = {
93	{.name = "ctstate", .id = O_CTSTATE, .type = XTTYPE_STRING,
94	 .flags = XTOPT_INVERT},
95	{.name = "ctproto", .id = O_CTPROTO, .type = XTTYPE_PROTOCOL,
96	 .flags = XTOPT_INVERT},
97	{.name = "ctorigsrc", .id = O_CTORIGSRC, .type = XTTYPE_HOST,
98	 .flags = XTOPT_INVERT},
99	{.name = "ctorigdst", .id = O_CTORIGDST, .type = XTTYPE_HOST,
100	 .flags = XTOPT_INVERT},
101	{.name = "ctreplsrc", .id = O_CTREPLSRC, .type = XTTYPE_HOST,
102	 .flags = XTOPT_INVERT},
103	{.name = "ctrepldst", .id = O_CTREPLDST, .type = XTTYPE_HOST,
104	 .flags = XTOPT_INVERT},
105	{.name = "ctstatus", .id = O_CTSTATUS, .type = XTTYPE_STRING,
106	 .flags = XTOPT_INVERT},
107	{.name = "ctexpire", .id = O_CTEXPIRE, .type = XTTYPE_UINT32RC,
108	 .flags = XTOPT_INVERT},
109	XTOPT_TABLEEND,
110};
111#undef s
112
113#define s struct xt_conntrack_mtinfo3 /* for v1-v3 */
114/* We exploit the fact that v1-v3 share the same layout */
115static const struct xt_option_entry conntrack_mt_opts[] = {
116	{.name = "ctstate", .id = O_CTSTATE, .type = XTTYPE_STRING,
117	 .flags = XTOPT_INVERT},
118	{.name = "ctproto", .id = O_CTPROTO, .type = XTTYPE_PROTOCOL,
119	 .flags = XTOPT_INVERT},
120	{.name = "ctorigsrc", .id = O_CTORIGSRC, .type = XTTYPE_HOSTMASK,
121	 .flags = XTOPT_INVERT},
122	{.name = "ctorigdst", .id = O_CTORIGDST, .type = XTTYPE_HOSTMASK,
123	 .flags = XTOPT_INVERT},
124	{.name = "ctreplsrc", .id = O_CTREPLSRC, .type = XTTYPE_HOSTMASK,
125	 .flags = XTOPT_INVERT},
126	{.name = "ctrepldst", .id = O_CTREPLDST, .type = XTTYPE_HOSTMASK,
127	 .flags = XTOPT_INVERT},
128	{.name = "ctstatus", .id = O_CTSTATUS, .type = XTTYPE_STRING,
129	 .flags = XTOPT_INVERT},
130	{.name = "ctexpire", .id = O_CTEXPIRE, .type = XTTYPE_UINT32RC,
131	 .flags = XTOPT_INVERT},
132	{.name = "ctorigsrcport", .id = O_CTORIGSRCPORT, .type = XTTYPE_PORTRC,
133	 .flags = XTOPT_INVERT},
134	{.name = "ctorigdstport", .id = O_CTORIGDSTPORT, .type = XTTYPE_PORTRC,
135	 .flags = XTOPT_INVERT},
136	{.name = "ctreplsrcport", .id = O_CTREPLSRCPORT, .type = XTTYPE_PORTRC,
137	 .flags = XTOPT_INVERT},
138	{.name = "ctrepldstport", .id = O_CTREPLDSTPORT, .type = XTTYPE_PORTRC,
139	 .flags = XTOPT_INVERT},
140	{.name = "ctdir", .id = O_CTDIR, .type = XTTYPE_STRING},
141	XTOPT_TABLEEND,
142};
143#undef s
144
145static int
146parse_state(const char *state, size_t len, struct xt_conntrack_info *sinfo)
147{
148	if (strncasecmp(state, "INVALID", len) == 0)
149		sinfo->statemask |= XT_CONNTRACK_STATE_INVALID;
150	else if (strncasecmp(state, "NEW", len) == 0)
151		sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_NEW);
152	else if (strncasecmp(state, "ESTABLISHED", len) == 0)
153		sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
154	else if (strncasecmp(state, "RELATED", len) == 0)
155		sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
156	else if (strncasecmp(state, "UNTRACKED", len) == 0)
157		sinfo->statemask |= XT_CONNTRACK_STATE_UNTRACKED;
158	else if (strncasecmp(state, "SNAT", len) == 0)
159		sinfo->statemask |= XT_CONNTRACK_STATE_SNAT;
160	else if (strncasecmp(state, "DNAT", len) == 0)
161		sinfo->statemask |= XT_CONNTRACK_STATE_DNAT;
162	else
163		return 0;
164	return 1;
165}
166
167static void
168parse_states(const char *arg, struct xt_conntrack_info *sinfo)
169{
170	const char *comma;
171
172	while ((comma = strchr(arg, ',')) != NULL) {
173		if (comma == arg || !parse_state(arg, comma-arg, sinfo))
174			xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg);
175		arg = comma+1;
176	}
177	if (!*arg)
178		xtables_error(PARAMETER_PROBLEM, "\"--ctstate\" requires a list of "
179					      "states with no spaces, e.g. "
180					      "ESTABLISHED,RELATED");
181	if (strlen(arg) == 0 || !parse_state(arg, strlen(arg), sinfo))
182		xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg);
183}
184
185static bool
186conntrack_ps_state(struct xt_conntrack_mtinfo3 *info, const char *state,
187                   size_t z)
188{
189	if (strncasecmp(state, "INVALID", z) == 0)
190		info->state_mask |= XT_CONNTRACK_STATE_INVALID;
191	else if (strncasecmp(state, "NEW", z) == 0)
192		info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_NEW);
193	else if (strncasecmp(state, "ESTABLISHED", z) == 0)
194		info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
195	else if (strncasecmp(state, "RELATED", z) == 0)
196		info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
197	else if (strncasecmp(state, "UNTRACKED", z) == 0)
198		info->state_mask |= XT_CONNTRACK_STATE_UNTRACKED;
199	else if (strncasecmp(state, "SNAT", z) == 0)
200		info->state_mask |= XT_CONNTRACK_STATE_SNAT;
201	else if (strncasecmp(state, "DNAT", z) == 0)
202		info->state_mask |= XT_CONNTRACK_STATE_DNAT;
203	else
204		return false;
205	return true;
206}
207
208static void
209conntrack_ps_states(struct xt_conntrack_mtinfo3 *info, const char *arg)
210{
211	const char *comma;
212
213	while ((comma = strchr(arg, ',')) != NULL) {
214		if (comma == arg || !conntrack_ps_state(info, arg, comma - arg))
215			xtables_error(PARAMETER_PROBLEM,
216			           "Bad ctstate \"%s\"", arg);
217		arg = comma + 1;
218	}
219
220	if (strlen(arg) == 0 || !conntrack_ps_state(info, arg, strlen(arg)))
221		xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg);
222}
223
224static int
225parse_status(const char *status, size_t len, struct xt_conntrack_info *sinfo)
226{
227	if (strncasecmp(status, "NONE", len) == 0)
228		sinfo->statusmask |= 0;
229	else if (strncasecmp(status, "EXPECTED", len) == 0)
230		sinfo->statusmask |= IPS_EXPECTED;
231	else if (strncasecmp(status, "SEEN_REPLY", len) == 0)
232		sinfo->statusmask |= IPS_SEEN_REPLY;
233	else if (strncasecmp(status, "ASSURED", len) == 0)
234		sinfo->statusmask |= IPS_ASSURED;
235#ifdef IPS_CONFIRMED
236	else if (strncasecmp(status, "CONFIRMED", len) == 0)
237		sinfo->statusmask |= IPS_CONFIRMED;
238#endif
239	else
240		return 0;
241	return 1;
242}
243
244static void
245parse_statuses(const char *arg, struct xt_conntrack_info *sinfo)
246{
247	const char *comma;
248
249	while ((comma = strchr(arg, ',')) != NULL) {
250		if (comma == arg || !parse_status(arg, comma-arg, sinfo))
251			xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg);
252		arg = comma+1;
253	}
254
255	if (strlen(arg) == 0 || !parse_status(arg, strlen(arg), sinfo))
256		xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg);
257}
258
259static bool
260conntrack_ps_status(struct xt_conntrack_mtinfo3 *info, const char *status,
261                    size_t z)
262{
263	if (strncasecmp(status, "NONE", z) == 0)
264		info->status_mask |= 0;
265	else if (strncasecmp(status, "EXPECTED", z) == 0)
266		info->status_mask |= IPS_EXPECTED;
267	else if (strncasecmp(status, "SEEN_REPLY", z) == 0)
268		info->status_mask |= IPS_SEEN_REPLY;
269	else if (strncasecmp(status, "ASSURED", z) == 0)
270		info->status_mask |= IPS_ASSURED;
271	else if (strncasecmp(status, "CONFIRMED", z) == 0)
272		info->status_mask |= IPS_CONFIRMED;
273	else
274		return false;
275	return true;
276}
277
278static void
279conntrack_ps_statuses(struct xt_conntrack_mtinfo3 *info, const char *arg)
280{
281	const char *comma;
282
283	while ((comma = strchr(arg, ',')) != NULL) {
284		if (comma == arg || !conntrack_ps_status(info, arg, comma - arg))
285			xtables_error(PARAMETER_PROBLEM,
286			           "Bad ctstatus \"%s\"", arg);
287		arg = comma + 1;
288	}
289
290	if (strlen(arg) == 0 || !conntrack_ps_status(info, arg, strlen(arg)))
291		xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg);
292}
293
294static void conntrack_parse(struct xt_option_call *cb)
295{
296	struct xt_conntrack_info *sinfo = cb->data;
297
298	xtables_option_parse(cb);
299	switch (cb->entry->id) {
300	case O_CTSTATE:
301		parse_states(cb->arg, sinfo);
302		if (cb->invert)
303			sinfo->invflags |= XT_CONNTRACK_STATE;
304		break;
305	case O_CTPROTO:
306		if (cb->invert)
307			sinfo->invflags |= XT_CONNTRACK_PROTO;
308		sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = cb->val.protocol;
309
310		if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0
311		    && (sinfo->invflags & XT_INV_PROTO))
312			xtables_error(PARAMETER_PROBLEM,
313				   "rule would never match protocol");
314
315		sinfo->flags |= XT_CONNTRACK_PROTO;
316		break;
317	case O_CTORIGSRC:
318		if (cb->invert)
319			sinfo->invflags |= XT_CONNTRACK_ORIGSRC;
320		sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = cb->val.haddr.ip;
321		sinfo->flags |= XT_CONNTRACK_ORIGSRC;
322		break;
323	case O_CTORIGDST:
324		if (cb->invert)
325			sinfo->invflags |= XT_CONNTRACK_ORIGDST;
326		sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = cb->val.haddr.ip;
327		sinfo->flags |= XT_CONNTRACK_ORIGDST;
328		break;
329	case O_CTREPLSRC:
330		if (cb->invert)
331			sinfo->invflags |= XT_CONNTRACK_REPLSRC;
332		sinfo->tuple[IP_CT_DIR_REPLY].src.ip = cb->val.haddr.ip;
333		sinfo->flags |= XT_CONNTRACK_REPLSRC;
334		break;
335	case O_CTREPLDST:
336		if (cb->invert)
337			sinfo->invflags |= XT_CONNTRACK_REPLDST;
338		sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = cb->val.haddr.ip;
339		sinfo->flags |= XT_CONNTRACK_REPLDST;
340		break;
341	case O_CTSTATUS:
342		parse_statuses(cb->arg, sinfo);
343		if (cb->invert)
344			sinfo->invflags |= XT_CONNTRACK_STATUS;
345		sinfo->flags |= XT_CONNTRACK_STATUS;
346		break;
347	case O_CTEXPIRE:
348		sinfo->expires_min = cb->val.u32_range[0];
349		sinfo->expires_max = cb->val.u32_range[0];
350		if (cb->nvals >= 2)
351			sinfo->expires_max = cb->val.u32_range[1];
352		if (cb->invert)
353			sinfo->invflags |= XT_CONNTRACK_EXPIRES;
354		sinfo->flags |= XT_CONNTRACK_EXPIRES;
355		break;
356	}
357}
358
359static void conntrack_mt_parse(struct xt_option_call *cb, uint8_t rev)
360{
361	struct xt_conntrack_mtinfo3 *info = cb->data;
362
363	xtables_option_parse(cb);
364	switch (cb->entry->id) {
365	case O_CTSTATE:
366		conntrack_ps_states(info, cb->arg);
367		info->match_flags |= XT_CONNTRACK_STATE;
368		if (cb->invert)
369			info->invert_flags |= XT_CONNTRACK_STATE;
370		break;
371	case O_CTPROTO:
372		info->l4proto = cb->val.protocol;
373		if (info->l4proto == 0 && (info->invert_flags & XT_INV_PROTO))
374			xtables_error(PARAMETER_PROBLEM, "conntrack: rule would "
375			           "never match protocol");
376
377		info->match_flags |= XT_CONNTRACK_PROTO;
378		if (cb->invert)
379			info->invert_flags |= XT_CONNTRACK_PROTO;
380		break;
381	case O_CTORIGSRC:
382		info->origsrc_addr = cb->val.haddr;
383		info->origsrc_mask = cb->val.hmask;
384		info->match_flags |= XT_CONNTRACK_ORIGSRC;
385		if (cb->invert)
386			info->invert_flags |= XT_CONNTRACK_ORIGSRC;
387		break;
388	case O_CTORIGDST:
389		info->origdst_addr = cb->val.haddr;
390		info->origdst_mask = cb->val.hmask;
391		info->match_flags |= XT_CONNTRACK_ORIGDST;
392		if (cb->invert)
393			info->invert_flags |= XT_CONNTRACK_ORIGDST;
394		break;
395	case O_CTREPLSRC:
396		info->replsrc_addr = cb->val.haddr;
397		info->replsrc_mask = cb->val.hmask;
398		info->match_flags |= XT_CONNTRACK_REPLSRC;
399		if (cb->invert)
400			info->invert_flags |= XT_CONNTRACK_REPLSRC;
401		break;
402	case O_CTREPLDST:
403		info->repldst_addr = cb->val.haddr;
404		info->repldst_mask = cb->val.hmask;
405		info->match_flags |= XT_CONNTRACK_REPLDST;
406		if (cb->invert)
407			info->invert_flags |= XT_CONNTRACK_REPLDST;
408		break;
409	case O_CTSTATUS:
410		conntrack_ps_statuses(info, cb->arg);
411		info->match_flags |= XT_CONNTRACK_STATUS;
412		if (cb->invert)
413			info->invert_flags |= XT_CONNTRACK_STATUS;
414		break;
415	case O_CTEXPIRE:
416		info->expires_min = cb->val.u32_range[0];
417		info->expires_max = cb->val.u32_range[0];
418		if (cb->nvals >= 2)
419			info->expires_max = cb->val.u32_range[1];
420		info->match_flags |= XT_CONNTRACK_EXPIRES;
421		if (cb->invert)
422			info->invert_flags |= XT_CONNTRACK_EXPIRES;
423		break;
424	case O_CTORIGSRCPORT:
425		info->origsrc_port = cb->val.port_range[0];
426		info->origsrc_port_high = cb->val.port_range[cb->nvals >= 2];
427		info->match_flags |= XT_CONNTRACK_ORIGSRC_PORT;
428		if (cb->invert)
429			info->invert_flags |= XT_CONNTRACK_ORIGSRC_PORT;
430		break;
431	case O_CTORIGDSTPORT:
432		info->origdst_port = cb->val.port_range[0];
433		info->origdst_port_high = cb->val.port_range[cb->nvals >= 2];
434		info->match_flags |= XT_CONNTRACK_ORIGDST_PORT;
435		if (cb->invert)
436			info->invert_flags |= XT_CONNTRACK_ORIGDST_PORT;
437		break;
438	case O_CTREPLSRCPORT:
439		info->replsrc_port = cb->val.port_range[0];
440		info->replsrc_port_high = cb->val.port_range[cb->nvals >= 2];
441		info->match_flags |= XT_CONNTRACK_REPLSRC_PORT;
442		if (cb->invert)
443			info->invert_flags |= XT_CONNTRACK_REPLSRC_PORT;
444		break;
445	case O_CTREPLDSTPORT:
446		info->repldst_port = cb->val.port_range[0];
447		info->repldst_port_high = cb->val.port_range[cb->nvals >= 2];
448		info->match_flags |= XT_CONNTRACK_REPLDST_PORT;
449		if (cb->invert)
450			info->invert_flags |= XT_CONNTRACK_REPLDST_PORT;
451		break;
452	case O_CTDIR:
453		if (strcasecmp(cb->arg, "ORIGINAL") == 0) {
454			info->match_flags  |= XT_CONNTRACK_DIRECTION;
455			info->invert_flags &= ~XT_CONNTRACK_DIRECTION;
456		} else if (strcasecmp(cb->arg, "REPLY") == 0) {
457			info->match_flags  |= XT_CONNTRACK_DIRECTION;
458			info->invert_flags |= XT_CONNTRACK_DIRECTION;
459		} else {
460			xtables_param_act(XTF_BAD_VALUE, "conntrack", "--ctdir", cb->arg);
461		}
462		break;
463	}
464}
465
466#define cinfo_transform(r, l) \
467	do { \
468		memcpy((r), (l), offsetof(typeof(*(l)), state_mask)); \
469		(r)->state_mask  = (l)->state_mask; \
470		(r)->status_mask = (l)->status_mask; \
471	} while (false);
472
473static void conntrack1_mt_parse(struct xt_option_call *cb)
474{
475	struct xt_conntrack_mtinfo1 *info = cb->data;
476	struct xt_conntrack_mtinfo3 up;
477
478	memset(&up, 0, sizeof(up));
479	cinfo_transform(&up, info);
480	up.origsrc_port_high = up.origsrc_port;
481	up.origdst_port_high = up.origdst_port;
482	up.replsrc_port_high = up.replsrc_port;
483	up.repldst_port_high = up.repldst_port;
484	cb->data = &up;
485	conntrack_mt_parse(cb, 3);
486	if (up.origsrc_port != up.origsrc_port_high ||
487	    up.origdst_port != up.origdst_port_high ||
488	    up.replsrc_port != up.replsrc_port_high ||
489	    up.repldst_port != up.repldst_port_high)
490		xtables_error(PARAMETER_PROBLEM,
491			"conntrack rev 1 does not support port ranges");
492	cinfo_transform(info, &up);
493	cb->data = info;
494}
495
496static void conntrack2_mt_parse(struct xt_option_call *cb)
497{
498#define cinfo2_transform(r, l) \
499		memcpy((r), (l), offsetof(typeof(*(l)), sizeof(*info));
500
501	struct xt_conntrack_mtinfo2 *info = cb->data;
502	struct xt_conntrack_mtinfo3 up;
503
504	memset(&up, 0, sizeof(up));
505	memcpy(&up, info, sizeof(*info));
506	up.origsrc_port_high = up.origsrc_port;
507	up.origdst_port_high = up.origdst_port;
508	up.replsrc_port_high = up.replsrc_port;
509	up.repldst_port_high = up.repldst_port;
510	cb->data = &up;
511	conntrack_mt_parse(cb, 3);
512	if (up.origsrc_port != up.origsrc_port_high ||
513	    up.origdst_port != up.origdst_port_high ||
514	    up.replsrc_port != up.replsrc_port_high ||
515	    up.repldst_port != up.repldst_port_high)
516		xtables_error(PARAMETER_PROBLEM,
517			"conntrack rev 2 does not support port ranges");
518	memcpy(info, &up, sizeof(*info));
519	cb->data = info;
520#undef cinfo2_transform
521}
522
523static void conntrack3_mt_parse(struct xt_option_call *cb)
524{
525	conntrack_mt_parse(cb, 3);
526}
527
528static void conntrack_mt_check(struct xt_fcheck_call *cb)
529{
530	if (cb->xflags == 0)
531		xtables_error(PARAMETER_PROBLEM, "conntrack: At least one option "
532		           "is required");
533}
534
535static void
536print_state(unsigned int statemask)
537{
538	const char *sep = " ";
539
540	if (statemask & XT_CONNTRACK_STATE_INVALID) {
541		printf("%sINVALID", sep);
542		sep = ",";
543	}
544	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
545		printf("%sNEW", sep);
546		sep = ",";
547	}
548	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
549		printf("%sRELATED", sep);
550		sep = ",";
551	}
552	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
553		printf("%sESTABLISHED", sep);
554		sep = ",";
555	}
556	if (statemask & XT_CONNTRACK_STATE_UNTRACKED) {
557		printf("%sUNTRACKED", sep);
558		sep = ",";
559	}
560	if (statemask & XT_CONNTRACK_STATE_SNAT) {
561		printf("%sSNAT", sep);
562		sep = ",";
563	}
564	if (statemask & XT_CONNTRACK_STATE_DNAT) {
565		printf("%sDNAT", sep);
566		sep = ",";
567	}
568}
569
570static void
571print_status(unsigned int statusmask)
572{
573	const char *sep = " ";
574
575	if (statusmask & IPS_EXPECTED) {
576		printf("%sEXPECTED", sep);
577		sep = ",";
578	}
579	if (statusmask & IPS_SEEN_REPLY) {
580		printf("%sSEEN_REPLY", sep);
581		sep = ",";
582	}
583	if (statusmask & IPS_ASSURED) {
584		printf("%sASSURED", sep);
585		sep = ",";
586	}
587	if (statusmask & IPS_CONFIRMED) {
588		printf("%sCONFIRMED", sep);
589		sep = ",";
590	}
591	if (statusmask == 0)
592		printf("%sNONE", sep);
593}
594
595static void
596conntrack_dump_addr(const union nf_inet_addr *addr,
597                    const union nf_inet_addr *mask,
598                    unsigned int family, bool numeric)
599{
600	if (family == NFPROTO_IPV4) {
601		if (!numeric && addr->ip == 0) {
602			printf(" anywhere");
603			return;
604		}
605		if (numeric)
606			printf(" %s%s",
607			       xtables_ipaddr_to_numeric(&addr->in),
608			       xtables_ipmask_to_numeric(&mask->in));
609		else
610			printf(" %s%s",
611			       xtables_ipaddr_to_anyname(&addr->in),
612			       xtables_ipmask_to_numeric(&mask->in));
613	} else if (family == NFPROTO_IPV6) {
614		if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 &&
615		    addr->ip6[2] == 0 && addr->ip6[3] == 0) {
616			printf(" anywhere");
617			return;
618		}
619		if (numeric)
620			printf(" %s%s",
621			       xtables_ip6addr_to_numeric(&addr->in6),
622			       xtables_ip6mask_to_numeric(&mask->in6));
623		else
624			printf(" %s%s",
625			       xtables_ip6addr_to_anyname(&addr->in6),
626			       xtables_ip6mask_to_numeric(&mask->in6));
627	}
628}
629
630static void
631print_addr(const struct in_addr *addr, const struct in_addr *mask,
632           int inv, int numeric)
633{
634	char buf[BUFSIZ];
635
636	if (inv)
637		printf(" !");
638
639	if (mask->s_addr == 0L && !numeric)
640		printf(" %s", "anywhere");
641	else {
642		if (numeric)
643			strcpy(buf, xtables_ipaddr_to_numeric(addr));
644		else
645			strcpy(buf, xtables_ipaddr_to_anyname(addr));
646		strcat(buf, xtables_ipmask_to_numeric(mask));
647		printf(" %s", buf);
648	}
649}
650
651static void
652matchinfo_print(const void *ip, const struct xt_entry_match *match, int numeric, const char *optpfx)
653{
654	const struct xt_conntrack_info *sinfo = (const void *)match->data;
655
656	if(sinfo->flags & XT_CONNTRACK_STATE) {
657        	if (sinfo->invflags & XT_CONNTRACK_STATE)
658			printf(" !");
659		printf(" %sctstate", optpfx);
660		print_state(sinfo->statemask);
661	}
662
663	if(sinfo->flags & XT_CONNTRACK_PROTO) {
664        	if (sinfo->invflags & XT_CONNTRACK_PROTO)
665			printf(" !");
666		printf(" %sctproto", optpfx);
667		printf(" %u", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum);
668	}
669
670	if(sinfo->flags & XT_CONNTRACK_ORIGSRC) {
671		if (sinfo->invflags & XT_CONNTRACK_ORIGSRC)
672			printf(" !");
673		printf(" %sctorigsrc", optpfx);
674
675		print_addr(
676		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
677		    &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
678		    false,
679		    numeric);
680	}
681
682	if(sinfo->flags & XT_CONNTRACK_ORIGDST) {
683		if (sinfo->invflags & XT_CONNTRACK_ORIGDST)
684			printf(" !");
685		printf(" %sctorigdst", optpfx);
686
687		print_addr(
688		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
689		    &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
690		    false,
691		    numeric);
692	}
693
694	if(sinfo->flags & XT_CONNTRACK_REPLSRC) {
695		if (sinfo->invflags & XT_CONNTRACK_REPLSRC)
696			printf(" !");
697		printf(" %sctreplsrc", optpfx);
698
699		print_addr(
700		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
701		    &sinfo->sipmsk[IP_CT_DIR_REPLY],
702		    false,
703		    numeric);
704	}
705
706	if(sinfo->flags & XT_CONNTRACK_REPLDST) {
707		if (sinfo->invflags & XT_CONNTRACK_REPLDST)
708			printf(" !");
709		printf(" %sctrepldst", optpfx);
710
711		print_addr(
712		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
713		    &sinfo->dipmsk[IP_CT_DIR_REPLY],
714		    false,
715		    numeric);
716	}
717
718	if(sinfo->flags & XT_CONNTRACK_STATUS) {
719        	if (sinfo->invflags & XT_CONNTRACK_STATUS)
720			printf(" !");
721		printf(" %sctstatus", optpfx);
722		print_status(sinfo->statusmask);
723	}
724
725	if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
726        	if (sinfo->invflags & XT_CONNTRACK_EXPIRES)
727			printf(" !");
728		printf(" %sctexpire ", optpfx);
729
730        	if (sinfo->expires_max == sinfo->expires_min)
731			printf("%lu", sinfo->expires_min);
732        	else
733			printf("%lu:%lu", sinfo->expires_min, sinfo->expires_max);
734	}
735
736	if (sinfo->flags & XT_CONNTRACK_DIRECTION) {
737		if (sinfo->invflags & XT_CONNTRACK_DIRECTION)
738			printf(" %sctdir REPLY", optpfx);
739		else
740			printf(" %sctdir ORIGINAL", optpfx);
741	}
742
743}
744
745static void
746conntrack_dump_ports(const char *prefix, const char *opt,
747		     u_int16_t port_low, u_int16_t port_high)
748{
749	if (port_high == 0 || port_low == port_high)
750		printf(" %s%s %u", prefix, opt, port_low);
751	else
752		printf(" %s%s %u:%u", prefix, opt, port_low, port_high);
753}
754
755static void
756conntrack_dump(const struct xt_conntrack_mtinfo3 *info, const char *prefix,
757               unsigned int family, bool numeric, bool v3)
758{
759	if (info->match_flags & XT_CONNTRACK_STATE) {
760		if (info->invert_flags & XT_CONNTRACK_STATE)
761			printf(" !");
762		printf(" %sctstate", prefix);
763		print_state(info->state_mask);
764	}
765
766	if (info->match_flags & XT_CONNTRACK_PROTO) {
767		if (info->invert_flags & XT_CONNTRACK_PROTO)
768			printf(" !");
769		printf(" %sctproto %u", prefix, info->l4proto);
770	}
771
772	if (info->match_flags & XT_CONNTRACK_ORIGSRC) {
773		if (info->invert_flags & XT_CONNTRACK_ORIGSRC)
774			printf(" !");
775		printf(" %sctorigsrc", prefix);
776		conntrack_dump_addr(&info->origsrc_addr, &info->origsrc_mask,
777		                    family, numeric);
778	}
779
780	if (info->match_flags & XT_CONNTRACK_ORIGDST) {
781		if (info->invert_flags & XT_CONNTRACK_ORIGDST)
782			printf(" !");
783		printf(" %sctorigdst", prefix);
784		conntrack_dump_addr(&info->origdst_addr, &info->origdst_mask,
785		                    family, numeric);
786	}
787
788	if (info->match_flags & XT_CONNTRACK_REPLSRC) {
789		if (info->invert_flags & XT_CONNTRACK_REPLSRC)
790			printf(" !");
791		printf(" %sctreplsrc", prefix);
792		conntrack_dump_addr(&info->replsrc_addr, &info->replsrc_mask,
793		                    family, numeric);
794	}
795
796	if (info->match_flags & XT_CONNTRACK_REPLDST) {
797		if (info->invert_flags & XT_CONNTRACK_REPLDST)
798			printf(" !");
799		printf(" %sctrepldst", prefix);
800		conntrack_dump_addr(&info->repldst_addr, &info->repldst_mask,
801		                    family, numeric);
802	}
803
804	if (info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) {
805		if (info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT)
806			printf(" !");
807		conntrack_dump_ports(prefix, "ctorigsrcport",
808				     v3 ? info->origsrc_port : ntohs(info->origsrc_port),
809				     v3 ? info->origsrc_port_high : 0);
810	}
811
812	if (info->match_flags & XT_CONNTRACK_ORIGDST_PORT) {
813		if (info->invert_flags & XT_CONNTRACK_ORIGDST_PORT)
814			printf(" !");
815		conntrack_dump_ports(prefix, "ctorigdstport",
816				     v3 ? info->origdst_port : ntohs(info->origdst_port),
817				     v3 ? info->origdst_port_high : 0);
818	}
819
820	if (info->match_flags & XT_CONNTRACK_REPLSRC_PORT) {
821		if (info->invert_flags & XT_CONNTRACK_REPLSRC_PORT)
822			printf(" !");
823		conntrack_dump_ports(prefix, "ctreplsrcport",
824				     v3 ? info->replsrc_port : ntohs(info->replsrc_port),
825				     v3 ? info->replsrc_port_high : 0);
826	}
827
828	if (info->match_flags & XT_CONNTRACK_REPLDST_PORT) {
829		if (info->invert_flags & XT_CONNTRACK_REPLDST_PORT)
830			printf(" !");
831		conntrack_dump_ports(prefix, "ctrepldstport",
832				     v3 ? info->repldst_port : ntohs(info->repldst_port),
833				     v3 ? info->repldst_port_high : 0);
834	}
835
836	if (info->match_flags & XT_CONNTRACK_STATUS) {
837		if (info->invert_flags & XT_CONNTRACK_STATUS)
838			printf(" !");
839		printf(" %sctstatus", prefix);
840		print_status(info->status_mask);
841	}
842
843	if (info->match_flags & XT_CONNTRACK_EXPIRES) {
844		if (info->invert_flags & XT_CONNTRACK_EXPIRES)
845			printf(" !");
846		printf(" %sctexpire ", prefix);
847
848		if (info->expires_max == info->expires_min)
849			printf("%u", (unsigned int)info->expires_min);
850		else
851			printf("%u:%u", (unsigned int)info->expires_min,
852			       (unsigned int)info->expires_max);
853	}
854
855	if (info->match_flags & XT_CONNTRACK_DIRECTION) {
856		if (info->invert_flags & XT_CONNTRACK_DIRECTION)
857			printf(" %sctdir REPLY", prefix);
858		else
859			printf(" %sctdir ORIGINAL", prefix);
860	}
861}
862
863static void conntrack_print(const void *ip, const struct xt_entry_match *match,
864                            int numeric)
865{
866	matchinfo_print(ip, match, numeric, "");
867}
868
869static void
870conntrack1_mt4_print(const void *ip, const struct xt_entry_match *match,
871                     int numeric)
872{
873	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
874	struct xt_conntrack_mtinfo3 up;
875
876	cinfo_transform(&up, info);
877	conntrack_dump(&up, "", NFPROTO_IPV4, numeric, false);
878}
879
880static void
881conntrack1_mt6_print(const void *ip, const struct xt_entry_match *match,
882                     int numeric)
883{
884	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
885	struct xt_conntrack_mtinfo3 up;
886
887	cinfo_transform(&up, info);
888	conntrack_dump(&up, "", NFPROTO_IPV6, numeric, false);
889}
890
891static void
892conntrack2_mt_print(const void *ip, const struct xt_entry_match *match,
893                    int numeric)
894{
895	conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric, false);
896}
897
898static void
899conntrack2_mt6_print(const void *ip, const struct xt_entry_match *match,
900                     int numeric)
901{
902	conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric, false);
903}
904
905static void
906conntrack3_mt_print(const void *ip, const struct xt_entry_match *match,
907                    int numeric)
908{
909	conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric, true);
910}
911
912static void
913conntrack3_mt6_print(const void *ip, const struct xt_entry_match *match,
914                     int numeric)
915{
916	conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric, true);
917}
918
919static void conntrack_save(const void *ip, const struct xt_entry_match *match)
920{
921	matchinfo_print(ip, match, 1, "--");
922}
923
924static void conntrack3_mt_save(const void *ip,
925                               const struct xt_entry_match *match)
926{
927	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true, true);
928}
929
930static void conntrack3_mt6_save(const void *ip,
931                                const struct xt_entry_match *match)
932{
933	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true, true);
934}
935
936static void conntrack2_mt_save(const void *ip,
937                               const struct xt_entry_match *match)
938{
939	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true, false);
940}
941
942static void conntrack2_mt6_save(const void *ip,
943                                const struct xt_entry_match *match)
944{
945	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true, false);
946}
947
948static void
949conntrack1_mt4_save(const void *ip, const struct xt_entry_match *match)
950{
951	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
952	struct xt_conntrack_mtinfo3 up;
953
954	cinfo_transform(&up, info);
955	conntrack_dump(&up, "--", NFPROTO_IPV4, true, false);
956}
957
958static void
959conntrack1_mt6_save(const void *ip, const struct xt_entry_match *match)
960{
961	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
962	struct xt_conntrack_mtinfo3 up;
963
964	cinfo_transform(&up, info);
965	conntrack_dump(&up, "--", NFPROTO_IPV6, true, false);
966}
967
968static struct xtables_match conntrack_mt_reg[] = {
969	{
970		.version       = XTABLES_VERSION,
971		.name          = "conntrack",
972		.revision      = 0,
973		.family        = NFPROTO_IPV4,
974		.size          = XT_ALIGN(sizeof(struct xt_conntrack_info)),
975		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_info)),
976		.help          = conntrack_mt_help,
977		.x6_parse      = conntrack_parse,
978		.x6_fcheck     = conntrack_mt_check,
979		.print         = conntrack_print,
980		.save          = conntrack_save,
981		.x6_options    = conntrack_mt_opts_v0,
982	},
983	{
984		.version       = XTABLES_VERSION,
985		.name          = "conntrack",
986		.revision      = 1,
987		.family        = NFPROTO_IPV4,
988		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
989		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
990		.help          = conntrack_mt_help,
991		.x6_parse      = conntrack1_mt_parse,
992		.x6_fcheck     = conntrack_mt_check,
993		.print         = conntrack1_mt4_print,
994		.save          = conntrack1_mt4_save,
995		.x6_options    = conntrack_mt_opts,
996	},
997	{
998		.version       = XTABLES_VERSION,
999		.name          = "conntrack",
1000		.revision      = 1,
1001		.family        = NFPROTO_IPV6,
1002		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1003		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1004		.help          = conntrack_mt_help,
1005		.x6_parse      = conntrack1_mt_parse,
1006		.x6_fcheck     = conntrack_mt_check,
1007		.print         = conntrack1_mt6_print,
1008		.save          = conntrack1_mt6_save,
1009		.x6_options    = conntrack_mt_opts,
1010	},
1011	{
1012		.version       = XTABLES_VERSION,
1013		.name          = "conntrack",
1014		.revision      = 2,
1015		.family        = NFPROTO_IPV4,
1016		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1017		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1018		.help          = conntrack_mt_help,
1019		.x6_parse      = conntrack2_mt_parse,
1020		.x6_fcheck     = conntrack_mt_check,
1021		.print         = conntrack2_mt_print,
1022		.save          = conntrack2_mt_save,
1023		.x6_options    = conntrack_mt_opts,
1024	},
1025	{
1026		.version       = XTABLES_VERSION,
1027		.name          = "conntrack",
1028		.revision      = 2,
1029		.family        = NFPROTO_IPV6,
1030		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1031		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1032		.help          = conntrack_mt_help,
1033		.x6_parse      = conntrack2_mt_parse,
1034		.x6_fcheck     = conntrack_mt_check,
1035		.print         = conntrack2_mt6_print,
1036		.save          = conntrack2_mt6_save,
1037		.x6_options    = conntrack_mt_opts,
1038	},
1039	{
1040		.version       = XTABLES_VERSION,
1041		.name          = "conntrack",
1042		.revision      = 3,
1043		.family        = NFPROTO_IPV4,
1044		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1045		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1046		.help          = conntrack_mt_help,
1047		.x6_parse      = conntrack3_mt_parse,
1048		.x6_fcheck     = conntrack_mt_check,
1049		.print         = conntrack3_mt_print,
1050		.save          = conntrack3_mt_save,
1051		.x6_options    = conntrack_mt_opts,
1052	},
1053	{
1054		.version       = XTABLES_VERSION,
1055		.name          = "conntrack",
1056		.revision      = 3,
1057		.family        = NFPROTO_IPV6,
1058		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1059		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1060		.help          = conntrack_mt_help,
1061		.x6_parse      = conntrack3_mt_parse,
1062		.x6_fcheck     = conntrack_mt_check,
1063		.print         = conntrack3_mt6_print,
1064		.save          = conntrack3_mt6_save,
1065		.x6_options    = conntrack_mt_opts,
1066	},
1067};
1068
1069void _init(void)
1070{
1071	xtables_register_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg));
1072}
1073