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