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