libxt_conntrack.c revision 34d9ce1b80618eebcf63e933cf4a15cc5482c0d2
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 /* for v1-v2 */
114/* We exploit the fact that v1-v2 share the same 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 /* for v1-v3 */
146/* We exploit the fact that v1-v3 share the same layout */
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		if (cb->invert)
339			sinfo->invflags |= XT_CONNTRACK_PROTO;
340		sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = cb->val.protocol;
341
342		if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0
343		    && (sinfo->invflags & XT_INV_PROTO))
344			xtables_error(PARAMETER_PROBLEM,
345				   "rule would never match protocol");
346
347		sinfo->flags |= XT_CONNTRACK_PROTO;
348		break;
349	case O_CTORIGSRC:
350		if (cb->invert)
351			sinfo->invflags |= XT_CONNTRACK_ORIGSRC;
352		sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = cb->val.haddr.ip;
353		sinfo->flags |= XT_CONNTRACK_ORIGSRC;
354		break;
355	case O_CTORIGDST:
356		if (cb->invert)
357			sinfo->invflags |= XT_CONNTRACK_ORIGDST;
358		sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = cb->val.haddr.ip;
359		sinfo->flags |= XT_CONNTRACK_ORIGDST;
360		break;
361	case O_CTREPLSRC:
362		if (cb->invert)
363			sinfo->invflags |= XT_CONNTRACK_REPLSRC;
364		sinfo->tuple[IP_CT_DIR_REPLY].src.ip = cb->val.haddr.ip;
365		sinfo->flags |= XT_CONNTRACK_REPLSRC;
366		break;
367	case O_CTREPLDST:
368		if (cb->invert)
369			sinfo->invflags |= XT_CONNTRACK_REPLDST;
370		sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = cb->val.haddr.ip;
371		sinfo->flags |= XT_CONNTRACK_REPLDST;
372		break;
373	case O_CTSTATUS:
374		parse_statuses(cb->arg, sinfo);
375		if (cb->invert)
376			sinfo->invflags |= XT_CONNTRACK_STATUS;
377		sinfo->flags |= XT_CONNTRACK_STATUS;
378		break;
379	case O_CTEXPIRE:
380		sinfo->expires_min = cb->val.u32_range[0];
381		sinfo->expires_max = cb->val.u32_range[0];
382		if (cb->nvals >= 2)
383			sinfo->expires_max = cb->val.u32_range[1];
384		if (cb->invert)
385			sinfo->invflags |= XT_CONNTRACK_EXPIRES;
386		sinfo->flags |= XT_CONNTRACK_EXPIRES;
387		break;
388	}
389}
390
391static void conntrack_mt_parse(struct xt_option_call *cb, uint8_t rev)
392{
393	struct xt_conntrack_mtinfo3 *info = cb->data;
394
395	xtables_option_parse(cb);
396	switch (cb->entry->id) {
397	case O_CTSTATE:
398		conntrack_ps_states(info, cb->arg);
399		info->match_flags |= XT_CONNTRACK_STATE;
400		if (cb->invert)
401			info->invert_flags |= XT_CONNTRACK_STATE;
402		break;
403	case O_CTPROTO:
404		info->l4proto = cb->val.protocol;
405		if (info->l4proto == 0 && (info->invert_flags & XT_INV_PROTO))
406			xtables_error(PARAMETER_PROBLEM, "conntrack: rule would "
407			           "never match protocol");
408
409		info->match_flags |= XT_CONNTRACK_PROTO;
410		if (cb->invert)
411			info->invert_flags |= XT_CONNTRACK_PROTO;
412		break;
413	case O_CTORIGSRC:
414		info->origsrc_addr = cb->val.haddr;
415		info->origsrc_mask = cb->val.hmask;
416		info->match_flags |= XT_CONNTRACK_ORIGSRC;
417		if (cb->invert)
418			info->invert_flags |= XT_CONNTRACK_ORIGSRC;
419		break;
420	case O_CTORIGDST:
421		info->origdst_addr = cb->val.haddr;
422		info->origdst_mask = cb->val.hmask;
423		info->match_flags |= XT_CONNTRACK_ORIGDST;
424		if (cb->invert)
425			info->invert_flags |= XT_CONNTRACK_ORIGDST;
426		break;
427	case O_CTREPLSRC:
428		info->replsrc_addr = cb->val.haddr;
429		info->replsrc_mask = cb->val.hmask;
430		info->match_flags |= XT_CONNTRACK_REPLSRC;
431		if (cb->invert)
432			info->invert_flags |= XT_CONNTRACK_REPLSRC;
433		break;
434	case O_CTREPLDST:
435		info->repldst_addr = cb->val.haddr;
436		info->repldst_mask = cb->val.hmask;
437		info->match_flags |= XT_CONNTRACK_REPLDST;
438		if (cb->invert)
439			info->invert_flags |= XT_CONNTRACK_REPLDST;
440		break;
441	case O_CTSTATUS:
442		conntrack_ps_statuses(info, cb->arg);
443		info->match_flags |= XT_CONNTRACK_STATUS;
444		if (cb->invert)
445			info->invert_flags |= XT_CONNTRACK_STATUS;
446		break;
447	case O_CTEXPIRE:
448		info->expires_min = cb->val.u32_range[0];
449		info->expires_max = cb->val.u32_range[0];
450		if (cb->nvals >= 2)
451			info->expires_max = cb->val.u32_range[1];
452		info->match_flags |= XT_CONNTRACK_EXPIRES;
453		if (cb->invert)
454			info->invert_flags |= XT_CONNTRACK_EXPIRES;
455		break;
456	case O_CTORIGSRCPORT:
457		info->origsrc_port = cb->val.port_range[0];
458		info->origsrc_port_high = cb->val.port_range[cb->nvals >= 2];
459		info->match_flags |= XT_CONNTRACK_ORIGSRC_PORT;
460		if (cb->invert)
461			info->invert_flags |= XT_CONNTRACK_ORIGSRC_PORT;
462		break;
463	case O_CTORIGDSTPORT:
464		info->origdst_port = cb->val.port_range[0];
465		info->origdst_port_high = cb->val.port_range[cb->nvals >= 2];
466		info->match_flags |= XT_CONNTRACK_ORIGDST_PORT;
467		if (cb->invert)
468			info->invert_flags |= XT_CONNTRACK_ORIGDST_PORT;
469		break;
470	case O_CTREPLSRCPORT:
471		info->replsrc_port = cb->val.port_range[0];
472		info->replsrc_port_high = cb->val.port_range[cb->nvals >= 2];
473		info->match_flags |= XT_CONNTRACK_REPLSRC_PORT;
474		if (cb->invert)
475			info->invert_flags |= XT_CONNTRACK_REPLSRC_PORT;
476		break;
477	case O_CTREPLDSTPORT:
478		info->repldst_port = cb->val.port_range[0];
479		info->repldst_port_high = cb->val.port_range[cb->nvals >= 2];
480		info->match_flags |= XT_CONNTRACK_REPLDST_PORT;
481		if (cb->invert)
482			info->invert_flags |= XT_CONNTRACK_REPLDST_PORT;
483		break;
484	case O_CTDIR:
485		if (strcasecmp(cb->arg, "ORIGINAL") == 0) {
486			info->match_flags  |= XT_CONNTRACK_DIRECTION;
487			info->invert_flags &= ~XT_CONNTRACK_DIRECTION;
488		} else if (strcasecmp(cb->arg, "REPLY") == 0) {
489			info->match_flags  |= XT_CONNTRACK_DIRECTION;
490			info->invert_flags |= XT_CONNTRACK_DIRECTION;
491		} else {
492			xtables_param_act(XTF_BAD_VALUE, "conntrack", "--ctdir", cb->arg);
493		}
494		break;
495	}
496}
497
498#define cinfo_transform(r, l) \
499	do { \
500		memcpy((r), (l), offsetof(typeof(*(l)), state_mask)); \
501		(r)->state_mask  = (l)->state_mask; \
502		(r)->status_mask = (l)->status_mask; \
503	} while (false);
504
505static void conntrack1_mt_parse(struct xt_option_call *cb)
506{
507	struct xt_conntrack_mtinfo1 *info = cb->data;
508	struct xt_conntrack_mtinfo3 up;
509
510	memset(&up, 0, sizeof(up));
511	cinfo_transform(&up, info);
512	up.origsrc_port_high = up.origsrc_port;
513	up.origdst_port_high = up.origdst_port;
514	up.replsrc_port_high = up.replsrc_port;
515	up.repldst_port_high = up.repldst_port;
516	cb->data = &up;
517	conntrack_mt_parse(cb, 3);
518	if (up.origsrc_port != up.origsrc_port_high ||
519	    up.origdst_port != up.origdst_port_high ||
520	    up.replsrc_port != up.replsrc_port_high ||
521	    up.repldst_port != up.repldst_port_high)
522		xtables_error(PARAMETER_PROBLEM,
523			"conntrack rev 1 does not support port ranges");
524	cinfo_transform(info, &up);
525	cb->data = info;
526}
527
528static void conntrack2_mt_parse(struct xt_option_call *cb)
529{
530#define cinfo2_transform(r, l) \
531		memcpy((r), (l), offsetof(typeof(*(l)), sizeof(*info));
532
533	struct xt_conntrack_mtinfo2 *info = cb->data;
534	struct xt_conntrack_mtinfo3 up;
535
536	memset(&up, 0, sizeof(up));
537	memcpy(&up, info, sizeof(*info));
538	up.origsrc_port_high = up.origsrc_port;
539	up.origdst_port_high = up.origdst_port;
540	up.replsrc_port_high = up.replsrc_port;
541	up.repldst_port_high = up.repldst_port;
542	cb->data = &up;
543	conntrack_mt_parse(cb, 3);
544	if (up.origsrc_port != up.origsrc_port_high ||
545	    up.origdst_port != up.origdst_port_high ||
546	    up.replsrc_port != up.replsrc_port_high ||
547	    up.repldst_port != up.repldst_port_high)
548		xtables_error(PARAMETER_PROBLEM,
549			"conntrack rev 2 does not support port ranges");
550	memcpy(info, &up, sizeof(*info));
551	cb->data = info;
552#undef cinfo2_transform
553}
554
555static void conntrack3_mt_parse(struct xt_option_call *cb)
556{
557	conntrack_mt_parse(cb, 3);
558}
559
560static void conntrack_mt_check(struct xt_fcheck_call *cb)
561{
562	if (cb->xflags == 0)
563		xtables_error(PARAMETER_PROBLEM, "conntrack: At least one option "
564		           "is required");
565}
566
567static void
568print_state(unsigned int statemask)
569{
570	const char *sep = " ";
571
572	if (statemask & XT_CONNTRACK_STATE_INVALID) {
573		printf("%sINVALID", sep);
574		sep = ",";
575	}
576	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
577		printf("%sNEW", sep);
578		sep = ",";
579	}
580	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
581		printf("%sRELATED", sep);
582		sep = ",";
583	}
584	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
585		printf("%sESTABLISHED", sep);
586		sep = ",";
587	}
588	if (statemask & XT_CONNTRACK_STATE_UNTRACKED) {
589		printf("%sUNTRACKED", sep);
590		sep = ",";
591	}
592	if (statemask & XT_CONNTRACK_STATE_SNAT) {
593		printf("%sSNAT", sep);
594		sep = ",";
595	}
596	if (statemask & XT_CONNTRACK_STATE_DNAT) {
597		printf("%sDNAT", sep);
598		sep = ",";
599	}
600}
601
602static void
603print_status(unsigned int statusmask)
604{
605	const char *sep = " ";
606
607	if (statusmask & IPS_EXPECTED) {
608		printf("%sEXPECTED", sep);
609		sep = ",";
610	}
611	if (statusmask & IPS_SEEN_REPLY) {
612		printf("%sSEEN_REPLY", sep);
613		sep = ",";
614	}
615	if (statusmask & IPS_ASSURED) {
616		printf("%sASSURED", sep);
617		sep = ",";
618	}
619	if (statusmask & IPS_CONFIRMED) {
620		printf("%sCONFIRMED", sep);
621		sep = ",";
622	}
623	if (statusmask == 0)
624		printf("%sNONE", sep);
625}
626
627static void
628conntrack_dump_addr(const union nf_inet_addr *addr,
629                    const union nf_inet_addr *mask,
630                    unsigned int family, bool numeric)
631{
632	if (family == NFPROTO_IPV4) {
633		if (!numeric && addr->ip == 0) {
634			printf(" anywhere");
635			return;
636		}
637		if (numeric)
638			printf(" %s%s",
639			       xtables_ipaddr_to_numeric(&addr->in),
640			       xtables_ipmask_to_numeric(&mask->in));
641		else
642			printf(" %s%s",
643			       xtables_ipaddr_to_anyname(&addr->in),
644			       xtables_ipmask_to_numeric(&mask->in));
645	} else if (family == NFPROTO_IPV6) {
646		if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 &&
647		    addr->ip6[2] == 0 && addr->ip6[3] == 0) {
648			printf(" anywhere");
649			return;
650		}
651		if (numeric)
652			printf(" %s%s",
653			       xtables_ip6addr_to_numeric(&addr->in6),
654			       xtables_ip6mask_to_numeric(&mask->in6));
655		else
656			printf(" %s%s",
657			       xtables_ip6addr_to_anyname(&addr->in6),
658			       xtables_ip6mask_to_numeric(&mask->in6));
659	}
660}
661
662static void
663print_addr(const struct in_addr *addr, const struct in_addr *mask,
664           int inv, int numeric)
665{
666	char buf[BUFSIZ];
667
668	if (inv)
669		printf(" !");
670
671	if (mask->s_addr == 0L && !numeric)
672		printf(" %s", "anywhere");
673	else {
674		if (numeric)
675			strcpy(buf, xtables_ipaddr_to_numeric(addr));
676		else
677			strcpy(buf, xtables_ipaddr_to_anyname(addr));
678		strcat(buf, xtables_ipmask_to_numeric(mask));
679		printf(" %s", buf);
680	}
681}
682
683static void
684matchinfo_print(const void *ip, const struct xt_entry_match *match, int numeric, const char *optpfx)
685{
686	const struct xt_conntrack_info *sinfo = (const void *)match->data;
687
688	if(sinfo->flags & XT_CONNTRACK_STATE) {
689        	if (sinfo->invflags & XT_CONNTRACK_STATE)
690			printf(" !");
691		printf(" %sctstate", optpfx);
692		print_state(sinfo->statemask);
693	}
694
695	if(sinfo->flags & XT_CONNTRACK_PROTO) {
696        	if (sinfo->invflags & XT_CONNTRACK_PROTO)
697			printf(" !");
698		printf(" %sctproto", optpfx);
699		printf(" %u", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum);
700	}
701
702	if(sinfo->flags & XT_CONNTRACK_ORIGSRC) {
703		if (sinfo->invflags & XT_CONNTRACK_ORIGSRC)
704			printf(" !");
705		printf(" %sctorigsrc", optpfx);
706
707		print_addr(
708		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
709		    &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
710		    false,
711		    numeric);
712	}
713
714	if(sinfo->flags & XT_CONNTRACK_ORIGDST) {
715		if (sinfo->invflags & XT_CONNTRACK_ORIGDST)
716			printf(" !");
717		printf(" %sctorigdst", optpfx);
718
719		print_addr(
720		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
721		    &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
722		    false,
723		    numeric);
724	}
725
726	if(sinfo->flags & XT_CONNTRACK_REPLSRC) {
727		if (sinfo->invflags & XT_CONNTRACK_REPLSRC)
728			printf(" !");
729		printf(" %sctreplsrc", optpfx);
730
731		print_addr(
732		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
733		    &sinfo->sipmsk[IP_CT_DIR_REPLY],
734		    false,
735		    numeric);
736	}
737
738	if(sinfo->flags & XT_CONNTRACK_REPLDST) {
739		if (sinfo->invflags & XT_CONNTRACK_REPLDST)
740			printf(" !");
741		printf(" %sctrepldst", optpfx);
742
743		print_addr(
744		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
745		    &sinfo->dipmsk[IP_CT_DIR_REPLY],
746		    false,
747		    numeric);
748	}
749
750	if(sinfo->flags & XT_CONNTRACK_STATUS) {
751        	if (sinfo->invflags & XT_CONNTRACK_STATUS)
752			printf(" !");
753		printf(" %sctstatus", optpfx);
754		print_status(sinfo->statusmask);
755	}
756
757	if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
758        	if (sinfo->invflags & XT_CONNTRACK_EXPIRES)
759			printf(" !");
760		printf(" %sctexpire ", optpfx);
761
762        	if (sinfo->expires_max == sinfo->expires_min)
763			printf("%lu", sinfo->expires_min);
764        	else
765			printf("%lu:%lu", sinfo->expires_min, sinfo->expires_max);
766	}
767
768	if (sinfo->flags & XT_CONNTRACK_DIRECTION) {
769		if (sinfo->invflags & XT_CONNTRACK_DIRECTION)
770			printf(" %sctdir REPLY", optpfx);
771		else
772			printf(" %sctdir ORIGINAL", optpfx);
773	}
774
775}
776
777static void
778conntrack_dump_ports(const char *prefix, const char *opt,
779		     u_int16_t port_low, u_int16_t port_high)
780{
781	if (port_high == 0 || port_low == port_high)
782		printf(" %s%s %u", prefix, opt, port_low);
783	else
784		printf(" %s%s %u:%u", prefix, opt, port_low, port_high);
785}
786
787static void
788conntrack_dump(const struct xt_conntrack_mtinfo3 *info, const char *prefix,
789               unsigned int family, bool numeric, bool v3)
790{
791	if (info->match_flags & XT_CONNTRACK_STATE) {
792		if (info->invert_flags & XT_CONNTRACK_STATE)
793			printf(" !");
794		printf(" %sctstate", prefix);
795		print_state(info->state_mask);
796	}
797
798	if (info->match_flags & XT_CONNTRACK_PROTO) {
799		if (info->invert_flags & XT_CONNTRACK_PROTO)
800			printf(" !");
801		printf(" %sctproto %u", prefix, info->l4proto);
802	}
803
804	if (info->match_flags & XT_CONNTRACK_ORIGSRC) {
805		if (info->invert_flags & XT_CONNTRACK_ORIGSRC)
806			printf(" !");
807		printf(" %sctorigsrc", prefix);
808		conntrack_dump_addr(&info->origsrc_addr, &info->origsrc_mask,
809		                    family, numeric);
810	}
811
812	if (info->match_flags & XT_CONNTRACK_ORIGDST) {
813		if (info->invert_flags & XT_CONNTRACK_ORIGDST)
814			printf(" !");
815		printf(" %sctorigdst", prefix);
816		conntrack_dump_addr(&info->origdst_addr, &info->origdst_mask,
817		                    family, numeric);
818	}
819
820	if (info->match_flags & XT_CONNTRACK_REPLSRC) {
821		if (info->invert_flags & XT_CONNTRACK_REPLSRC)
822			printf(" !");
823		printf(" %sctreplsrc", prefix);
824		conntrack_dump_addr(&info->replsrc_addr, &info->replsrc_mask,
825		                    family, numeric);
826	}
827
828	if (info->match_flags & XT_CONNTRACK_REPLDST) {
829		if (info->invert_flags & XT_CONNTRACK_REPLDST)
830			printf(" !");
831		printf(" %sctrepldst", prefix);
832		conntrack_dump_addr(&info->repldst_addr, &info->repldst_mask,
833		                    family, numeric);
834	}
835
836	if (info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) {
837		if (info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT)
838			printf(" !");
839		conntrack_dump_ports(prefix, "ctorigsrcport",
840				     v3 ? info->origsrc_port : ntohs(info->origsrc_port),
841				     v3 ? info->origsrc_port_high : 0);
842	}
843
844	if (info->match_flags & XT_CONNTRACK_ORIGDST_PORT) {
845		if (info->invert_flags & XT_CONNTRACK_ORIGDST_PORT)
846			printf(" !");
847		conntrack_dump_ports(prefix, "ctorigdstport",
848				     v3 ? info->origdst_port : ntohs(info->origdst_port),
849				     v3 ? info->origdst_port_high : 0);
850	}
851
852	if (info->match_flags & XT_CONNTRACK_REPLSRC_PORT) {
853		if (info->invert_flags & XT_CONNTRACK_REPLSRC_PORT)
854			printf(" !");
855		conntrack_dump_ports(prefix, "ctreplsrcport",
856				     v3 ? info->replsrc_port : ntohs(info->replsrc_port),
857				     v3 ? info->replsrc_port_high : 0);
858	}
859
860	if (info->match_flags & XT_CONNTRACK_REPLDST_PORT) {
861		if (info->invert_flags & XT_CONNTRACK_REPLDST_PORT)
862			printf(" !");
863		conntrack_dump_ports(prefix, "ctrepldstport",
864				     v3 ? info->repldst_port : ntohs(info->repldst_port),
865				     v3 ? info->repldst_port_high : 0);
866	}
867
868	if (info->match_flags & XT_CONNTRACK_STATUS) {
869		if (info->invert_flags & XT_CONNTRACK_STATUS)
870			printf(" !");
871		printf(" %sctstatus", prefix);
872		print_status(info->status_mask);
873	}
874
875	if (info->match_flags & XT_CONNTRACK_EXPIRES) {
876		if (info->invert_flags & XT_CONNTRACK_EXPIRES)
877			printf(" !");
878		printf(" %sctexpire ", prefix);
879
880		if (info->expires_max == info->expires_min)
881			printf("%u", (unsigned int)info->expires_min);
882		else
883			printf("%u:%u", (unsigned int)info->expires_min,
884			       (unsigned int)info->expires_max);
885	}
886
887	if (info->match_flags & XT_CONNTRACK_DIRECTION) {
888		if (info->invert_flags & XT_CONNTRACK_DIRECTION)
889			printf(" %sctdir REPLY", prefix);
890		else
891			printf(" %sctdir ORIGINAL", prefix);
892	}
893}
894
895static void conntrack_print(const void *ip, const struct xt_entry_match *match,
896                            int numeric)
897{
898	matchinfo_print(ip, match, numeric, "");
899}
900
901static void
902conntrack1_mt4_print(const void *ip, const struct xt_entry_match *match,
903                     int numeric)
904{
905	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
906	struct xt_conntrack_mtinfo3 up;
907
908	cinfo_transform(&up, info);
909	conntrack_dump(&up, "", NFPROTO_IPV4, numeric, false);
910}
911
912static void
913conntrack1_mt6_print(const void *ip, const struct xt_entry_match *match,
914                     int numeric)
915{
916	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
917	struct xt_conntrack_mtinfo3 up;
918
919	cinfo_transform(&up, info);
920	conntrack_dump(&up, "", NFPROTO_IPV6, numeric, false);
921}
922
923static void
924conntrack2_mt_print(const void *ip, const struct xt_entry_match *match,
925                    int numeric)
926{
927	conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric, false);
928}
929
930static void
931conntrack2_mt6_print(const void *ip, const struct xt_entry_match *match,
932                     int numeric)
933{
934	conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric, false);
935}
936
937static void
938conntrack3_mt_print(const void *ip, const struct xt_entry_match *match,
939                    int numeric)
940{
941	conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric, true);
942}
943
944static void
945conntrack3_mt6_print(const void *ip, const struct xt_entry_match *match,
946                     int numeric)
947{
948	conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric, true);
949}
950
951static void conntrack_save(const void *ip, const struct xt_entry_match *match)
952{
953	matchinfo_print(ip, match, 1, "--");
954}
955
956static void conntrack3_mt_save(const void *ip,
957                               const struct xt_entry_match *match)
958{
959	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true, true);
960}
961
962static void conntrack3_mt6_save(const void *ip,
963                                const struct xt_entry_match *match)
964{
965	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true, true);
966}
967
968static void conntrack2_mt_save(const void *ip,
969                               const struct xt_entry_match *match)
970{
971	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true, false);
972}
973
974static void conntrack2_mt6_save(const void *ip,
975                                const struct xt_entry_match *match)
976{
977	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true, false);
978}
979
980static void
981conntrack1_mt4_save(const void *ip, const struct xt_entry_match *match)
982{
983	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
984	struct xt_conntrack_mtinfo3 up;
985
986	cinfo_transform(&up, info);
987	conntrack_dump(&up, "--", NFPROTO_IPV4, true, false);
988}
989
990static void
991conntrack1_mt6_save(const void *ip, const struct xt_entry_match *match)
992{
993	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
994	struct xt_conntrack_mtinfo3 up;
995
996	cinfo_transform(&up, info);
997	conntrack_dump(&up, "--", NFPROTO_IPV6, true, false);
998}
999
1000static struct xtables_match conntrack_mt_reg[] = {
1001	{
1002		.version       = XTABLES_VERSION,
1003		.name          = "conntrack",
1004		.revision      = 0,
1005		.family        = NFPROTO_IPV4,
1006		.size          = XT_ALIGN(sizeof(struct xt_conntrack_info)),
1007		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_info)),
1008		.help          = conntrack_mt_help,
1009		.x6_parse      = conntrack_parse,
1010		.x6_fcheck     = conntrack_mt_check,
1011		.print         = conntrack_print,
1012		.save          = conntrack_save,
1013		.x6_options    = conntrack_mt_opts_v0,
1014	},
1015	{
1016		.version       = XTABLES_VERSION,
1017		.name          = "conntrack",
1018		.revision      = 1,
1019		.family        = NFPROTO_IPV4,
1020		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1021		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1022		.help          = conntrack_mt_help,
1023		.x6_parse      = conntrack1_mt_parse,
1024		.x6_fcheck     = conntrack_mt_check,
1025		.print         = conntrack1_mt4_print,
1026		.save          = conntrack1_mt4_save,
1027		.x6_options    = conntrack2_mt_opts,
1028	},
1029	{
1030		.version       = XTABLES_VERSION,
1031		.name          = "conntrack",
1032		.revision      = 1,
1033		.family        = NFPROTO_IPV6,
1034		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1035		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1036		.help          = conntrack_mt_help,
1037		.x6_parse      = conntrack1_mt_parse,
1038		.x6_fcheck     = conntrack_mt_check,
1039		.print         = conntrack1_mt6_print,
1040		.save          = conntrack1_mt6_save,
1041		.x6_options    = conntrack2_mt_opts,
1042	},
1043	{
1044		.version       = XTABLES_VERSION,
1045		.name          = "conntrack",
1046		.revision      = 2,
1047		.family        = NFPROTO_IPV4,
1048		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1049		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1050		.help          = conntrack_mt_help,
1051		.x6_parse      = conntrack2_mt_parse,
1052		.x6_fcheck     = conntrack_mt_check,
1053		.print         = conntrack2_mt_print,
1054		.save          = conntrack2_mt_save,
1055		.x6_options    = conntrack2_mt_opts,
1056	},
1057	{
1058		.version       = XTABLES_VERSION,
1059		.name          = "conntrack",
1060		.revision      = 2,
1061		.family        = NFPROTO_IPV6,
1062		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1063		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1064		.help          = conntrack_mt_help,
1065		.x6_parse      = conntrack2_mt_parse,
1066		.x6_fcheck     = conntrack_mt_check,
1067		.print         = conntrack2_mt6_print,
1068		.save          = conntrack2_mt6_save,
1069		.x6_options    = conntrack2_mt_opts,
1070	},
1071	{
1072		.version       = XTABLES_VERSION,
1073		.name          = "conntrack",
1074		.revision      = 3,
1075		.family        = NFPROTO_IPV4,
1076		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1077		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1078		.help          = conntrack_mt_help,
1079		.x6_parse      = conntrack3_mt_parse,
1080		.x6_fcheck     = conntrack_mt_check,
1081		.print         = conntrack3_mt_print,
1082		.save          = conntrack3_mt_save,
1083		.x6_options    = conntrack3_mt_opts,
1084	},
1085	{
1086		.version       = XTABLES_VERSION,
1087		.name          = "conntrack",
1088		.revision      = 3,
1089		.family        = NFPROTO_IPV6,
1090		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1091		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
1092		.help          = conntrack_mt_help,
1093		.x6_parse      = conntrack3_mt_parse,
1094		.x6_fcheck     = conntrack_mt_check,
1095		.print         = conntrack3_mt6_print,
1096		.save          = conntrack3_mt6_save,
1097		.x6_options    = conntrack3_mt_opts,
1098	},
1099};
1100
1101void _init(void)
1102{
1103	xtables_register_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg));
1104}
1105