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