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