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