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