libxt_conntrack.c revision 350661a6eb089f3e54e67e022db9e16ea280499f
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	{ .name = NULL }
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 ", xtables_ipaddr_to_numeric(&addr->in));
846		else
847			printf("%s ", xtables_ipaddr_to_anyname(&addr->in));
848	} else if (family == NFPROTO_IPV6) {
849		if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 &&
850		    addr->ip6[2] == 0 && addr->ip6[3] == 0) {
851			printf("anywhere ");
852			return;
853		}
854		if (numeric)
855			printf("%s ", xtables_ip6addr_to_numeric(&addr->in6));
856		else
857			printf("%s ", xtables_ip6addr_to_anyname(&addr->in6));
858	}
859}
860
861static void
862print_addr(const struct in_addr *addr, const struct in_addr *mask,
863           int inv, int numeric)
864{
865	char buf[BUFSIZ];
866
867	if (inv)
868	       	printf("! ");
869
870	if (mask->s_addr == 0L && !numeric)
871		printf("%s ", "anywhere");
872	else {
873		if (numeric)
874			strcpy(buf, xtables_ipaddr_to_numeric(addr));
875		else
876			strcpy(buf, xtables_ipaddr_to_anyname(addr));
877		strcat(buf, xtables_ipmask_to_numeric(mask));
878		printf("%s ", buf);
879	}
880}
881
882static void
883matchinfo_print(const void *ip, const struct xt_entry_match *match, int numeric, const char *optpfx)
884{
885	const struct xt_conntrack_info *sinfo = (const void *)match->data;
886
887	if(sinfo->flags & XT_CONNTRACK_STATE) {
888        	if (sinfo->invflags & XT_CONNTRACK_STATE)
889                	printf("! ");
890		printf("%sctstate ", optpfx);
891		print_state(sinfo->statemask);
892	}
893
894	if(sinfo->flags & XT_CONNTRACK_PROTO) {
895        	if (sinfo->invflags & XT_CONNTRACK_PROTO)
896                	printf("! ");
897		printf("%sctproto ", optpfx);
898		printf("%u ", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum);
899	}
900
901	if(sinfo->flags & XT_CONNTRACK_ORIGSRC) {
902		if (sinfo->invflags & XT_CONNTRACK_ORIGSRC)
903			printf("! ");
904		printf("%sctorigsrc ", optpfx);
905
906		print_addr(
907		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
908		    &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
909		    false,
910		    numeric);
911	}
912
913	if(sinfo->flags & XT_CONNTRACK_ORIGDST) {
914		if (sinfo->invflags & XT_CONNTRACK_ORIGDST)
915			printf("! ");
916		printf("%sctorigdst ", optpfx);
917
918		print_addr(
919		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
920		    &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
921		    false,
922		    numeric);
923	}
924
925	if(sinfo->flags & XT_CONNTRACK_REPLSRC) {
926		if (sinfo->invflags & XT_CONNTRACK_REPLSRC)
927			printf("! ");
928		printf("%sctreplsrc ", optpfx);
929
930		print_addr(
931		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
932		    &sinfo->sipmsk[IP_CT_DIR_REPLY],
933		    false,
934		    numeric);
935	}
936
937	if(sinfo->flags & XT_CONNTRACK_REPLDST) {
938		if (sinfo->invflags & XT_CONNTRACK_REPLDST)
939			printf("! ");
940		printf("%sctrepldst ", optpfx);
941
942		print_addr(
943		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
944		    &sinfo->dipmsk[IP_CT_DIR_REPLY],
945		    false,
946		    numeric);
947	}
948
949	if(sinfo->flags & XT_CONNTRACK_STATUS) {
950        	if (sinfo->invflags & XT_CONNTRACK_STATUS)
951                	printf("! ");
952		printf("%sctstatus ", optpfx);
953		print_status(sinfo->statusmask);
954	}
955
956	if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
957        	if (sinfo->invflags & XT_CONNTRACK_EXPIRES)
958                	printf("! ");
959		printf("%sctexpire ", optpfx);
960
961        	if (sinfo->expires_max == sinfo->expires_min)
962                	printf("%lu ", sinfo->expires_min);
963        	else
964                	printf("%lu:%lu ", sinfo->expires_min, sinfo->expires_max);
965	}
966
967	if (sinfo->flags & XT_CONNTRACK_DIRECTION) {
968		if (sinfo->invflags & XT_CONNTRACK_DIRECTION)
969			printf("%sctdir REPLY", optpfx);
970		else
971			printf("%sctdir ORIGINAL", optpfx);
972	}
973
974}
975
976static void
977conntrack_dump(const struct xt_conntrack_mtinfo2 *info, const char *prefix,
978               unsigned int family, bool numeric)
979{
980	if (info->match_flags & XT_CONNTRACK_STATE) {
981		if (info->invert_flags & XT_CONNTRACK_STATE)
982			printf("! ");
983		printf("%sctstate ", prefix);
984		print_state(info->state_mask);
985	}
986
987	if (info->match_flags & XT_CONNTRACK_PROTO) {
988		if (info->invert_flags & XT_CONNTRACK_PROTO)
989			printf("! ");
990		printf("%sctproto %u ", prefix, info->l4proto);
991	}
992
993	if (info->match_flags & XT_CONNTRACK_ORIGSRC) {
994		if (info->invert_flags & XT_CONNTRACK_ORIGSRC)
995			printf("! ");
996		printf("%sctorigsrc ", prefix);
997		conntrack_dump_addr(&info->origsrc_addr, &info->origsrc_mask,
998		                    family, numeric);
999	}
1000
1001	if (info->match_flags & XT_CONNTRACK_ORIGDST) {
1002		if (info->invert_flags & XT_CONNTRACK_ORIGDST)
1003			printf("! ");
1004		printf("%sctorigdst ", prefix);
1005		conntrack_dump_addr(&info->origdst_addr, &info->origdst_mask,
1006		                    family, numeric);
1007	}
1008
1009	if (info->match_flags & XT_CONNTRACK_REPLSRC) {
1010		if (info->invert_flags & XT_CONNTRACK_REPLSRC)
1011			printf("! ");
1012		printf("%sctreplsrc ", prefix);
1013		conntrack_dump_addr(&info->replsrc_addr, &info->replsrc_mask,
1014		                    family, numeric);
1015	}
1016
1017	if (info->match_flags & XT_CONNTRACK_REPLDST) {
1018		if (info->invert_flags & XT_CONNTRACK_REPLDST)
1019			printf("! ");
1020		printf("%sctrepldst ", prefix);
1021		conntrack_dump_addr(&info->repldst_addr, &info->repldst_mask,
1022		                    family, numeric);
1023	}
1024
1025	if (info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) {
1026		if (info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT)
1027			printf("! ");
1028		printf("%sctorigsrcport %u ", prefix,
1029		       ntohs(info->origsrc_port));
1030	}
1031
1032	if (info->match_flags & XT_CONNTRACK_ORIGDST_PORT) {
1033		if (info->invert_flags & XT_CONNTRACK_ORIGDST_PORT)
1034			printf("! ");
1035		printf("%sctorigdstport %u ", prefix,
1036		       ntohs(info->origdst_port));
1037	}
1038
1039	if (info->match_flags & XT_CONNTRACK_REPLSRC_PORT) {
1040		if (info->invert_flags & XT_CONNTRACK_REPLSRC_PORT)
1041			printf("! ");
1042		printf("%sctreplsrcport %u ", prefix,
1043		       ntohs(info->replsrc_port));
1044	}
1045
1046	if (info->match_flags & XT_CONNTRACK_REPLDST_PORT) {
1047		if (info->invert_flags & XT_CONNTRACK_REPLDST_PORT)
1048			printf("! ");
1049		printf("%sctrepldstport %u ", prefix,
1050		       ntohs(info->repldst_port));
1051	}
1052
1053	if (info->match_flags & XT_CONNTRACK_STATUS) {
1054		if (info->invert_flags & XT_CONNTRACK_STATUS)
1055			printf("! ");
1056		printf("%sctstatus ", prefix);
1057		print_status(info->status_mask);
1058	}
1059
1060	if (info->match_flags & XT_CONNTRACK_EXPIRES) {
1061		if (info->invert_flags & XT_CONNTRACK_EXPIRES)
1062			printf("! ");
1063		printf("%sctexpire ", prefix);
1064
1065		if (info->expires_max == info->expires_min)
1066			printf("%u ", (unsigned int)info->expires_min);
1067		else
1068			printf("%u:%u ", (unsigned int)info->expires_min,
1069			       (unsigned int)info->expires_max);
1070	}
1071
1072	if (info->match_flags & XT_CONNTRACK_DIRECTION) {
1073		if (info->invert_flags & XT_CONNTRACK_DIRECTION)
1074			printf("%sctdir REPLY", prefix);
1075		else
1076			printf("%sctdir ORIGINAL", prefix);
1077	}
1078}
1079
1080static void conntrack_print(const void *ip, const struct xt_entry_match *match,
1081                            int numeric)
1082{
1083	matchinfo_print(ip, match, numeric, "");
1084}
1085
1086static void
1087conntrack1_mt4_print(const void *ip, const struct xt_entry_match *match,
1088                     int numeric)
1089{
1090	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
1091	struct xt_conntrack_mtinfo2 up;
1092
1093	cinfo_transform(&up, info);
1094	conntrack_dump(&up, "", NFPROTO_IPV4, numeric);
1095}
1096
1097static void
1098conntrack1_mt6_print(const void *ip, const struct xt_entry_match *match,
1099                     int numeric)
1100{
1101	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
1102	struct xt_conntrack_mtinfo2 up;
1103
1104	cinfo_transform(&up, info);
1105	conntrack_dump(&up, "", NFPROTO_IPV6, numeric);
1106}
1107
1108static void
1109conntrack_mt_print(const void *ip, const struct xt_entry_match *match,
1110                   int numeric)
1111{
1112	conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric);
1113}
1114
1115static void
1116conntrack_mt6_print(const void *ip, const struct xt_entry_match *match,
1117                    int numeric)
1118{
1119	conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric);
1120}
1121
1122static void conntrack_save(const void *ip, const struct xt_entry_match *match)
1123{
1124	matchinfo_print(ip, match, 1, "--");
1125}
1126
1127static void conntrack_mt_save(const void *ip,
1128                              const struct xt_entry_match *match)
1129{
1130	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true);
1131}
1132
1133static void conntrack_mt6_save(const void *ip,
1134                               const struct xt_entry_match *match)
1135{
1136	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true);
1137}
1138
1139static void
1140conntrack1_mt4_save(const void *ip, const struct xt_entry_match *match)
1141{
1142	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
1143	struct xt_conntrack_mtinfo2 up;
1144
1145	cinfo_transform(&up, info);
1146	conntrack_dump(&up, "--", NFPROTO_IPV4, true);
1147}
1148
1149static void
1150conntrack1_mt6_save(const void *ip, const struct xt_entry_match *match)
1151{
1152	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
1153	struct xt_conntrack_mtinfo2 up;
1154
1155	cinfo_transform(&up, info);
1156	conntrack_dump(&up, "--", NFPROTO_IPV6, true);
1157}
1158
1159static struct xtables_match conntrack_mt_reg[] = {
1160	{
1161		.version       = XTABLES_VERSION,
1162		.name          = "conntrack",
1163		.revision      = 0,
1164		.family        = NFPROTO_IPV4,
1165		.size          = XT_ALIGN(sizeof(struct xt_conntrack_info)),
1166		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_info)),
1167		.help          = conntrack_mt_help,
1168		.parse         = conntrack_parse,
1169		.final_check   = conntrack_mt_check,
1170		.print         = conntrack_print,
1171		.save          = conntrack_save,
1172		.extra_opts    = conntrack_mt_opts_v0,
1173	},
1174	{
1175		.version       = XTABLES_VERSION,
1176		.name          = "conntrack",
1177		.revision      = 1,
1178		.family        = NFPROTO_IPV4,
1179		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1180		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1181		.help          = conntrack_mt_help,
1182		.parse         = conntrack1_mt4_parse,
1183		.final_check   = conntrack_mt_check,
1184		.print         = conntrack1_mt4_print,
1185		.save          = conntrack1_mt4_save,
1186		.extra_opts    = conntrack_mt_opts,
1187	},
1188	{
1189		.version       = XTABLES_VERSION,
1190		.name          = "conntrack",
1191		.revision      = 1,
1192		.family        = NFPROTO_IPV6,
1193		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1194		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1195		.help          = conntrack_mt_help,
1196		.parse         = conntrack1_mt6_parse,
1197		.final_check   = conntrack_mt_check,
1198		.print         = conntrack1_mt6_print,
1199		.save          = conntrack1_mt6_save,
1200		.extra_opts    = conntrack_mt_opts,
1201	},
1202	{
1203		.version       = XTABLES_VERSION,
1204		.name          = "conntrack",
1205		.revision      = 2,
1206		.family        = NFPROTO_IPV4,
1207		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1208		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1209		.help          = conntrack_mt_help,
1210		.parse         = conntrack2_mt4_parse,
1211		.final_check   = conntrack_mt_check,
1212		.print         = conntrack_mt_print,
1213		.save          = conntrack_mt_save,
1214		.extra_opts    = conntrack_mt_opts,
1215	},
1216	{
1217		.version       = XTABLES_VERSION,
1218		.name          = "conntrack",
1219		.revision      = 2,
1220		.family        = NFPROTO_IPV6,
1221		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1222		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
1223		.help          = conntrack_mt_help,
1224		.parse         = conntrack2_mt6_parse,
1225		.final_check   = conntrack_mt_check,
1226		.print         = conntrack_mt6_print,
1227		.save          = conntrack_mt6_save,
1228		.extra_opts    = conntrack_mt_opts,
1229	},
1230};
1231
1232void _init(void)
1233{
1234	xtables_register_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg));
1235}
1236