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