libxt_conntrack.c revision 1829ed482efbc8b390cc760d012b3a4450494e1a
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(struct in_addr *addr, struct in_addr *mask, int inv, int numeric)
783{
784	char buf[BUFSIZ];
785
786	if (inv)
787	       	printf("! ");
788
789	if (mask->s_addr == 0L && !numeric)
790		printf("%s ", "anywhere");
791	else {
792		if (numeric)
793			strcpy(buf, xtables_ipaddr_to_numeric(addr));
794		else
795			strcpy(buf, xtables_ipaddr_to_anyname(addr));
796		strcat(buf, xtables_ipmask_to_numeric(mask));
797		printf("%s ", buf);
798	}
799}
800
801static void
802matchinfo_print(const void *ip, const struct xt_entry_match *match, int numeric, const char *optpfx)
803{
804	struct xt_conntrack_info *sinfo = (void *)match->data;
805
806	if(sinfo->flags & XT_CONNTRACK_STATE) {
807        	if (sinfo->invflags & XT_CONNTRACK_STATE)
808                	printf("! ");
809		printf("%sctstate ", optpfx);
810		print_state(sinfo->statemask);
811	}
812
813	if(sinfo->flags & XT_CONNTRACK_PROTO) {
814        	if (sinfo->invflags & XT_CONNTRACK_PROTO)
815                	printf("! ");
816		printf("%sctproto ", optpfx);
817		printf("%u ", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum);
818	}
819
820	if(sinfo->flags & XT_CONNTRACK_ORIGSRC) {
821		if (sinfo->invflags & XT_CONNTRACK_ORIGSRC)
822			printf("! ");
823		printf("%sctorigsrc ", optpfx);
824
825		print_addr(
826		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
827		    &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
828		    false,
829		    numeric);
830	}
831
832	if(sinfo->flags & XT_CONNTRACK_ORIGDST) {
833		if (sinfo->invflags & XT_CONNTRACK_ORIGDST)
834			printf("! ");
835		printf("%sctorigdst ", optpfx);
836
837		print_addr(
838		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
839		    &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
840		    false,
841		    numeric);
842	}
843
844	if(sinfo->flags & XT_CONNTRACK_REPLSRC) {
845		if (sinfo->invflags & XT_CONNTRACK_REPLSRC)
846			printf("! ");
847		printf("%sctreplsrc ", optpfx);
848
849		print_addr(
850		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
851		    &sinfo->sipmsk[IP_CT_DIR_REPLY],
852		    false,
853		    numeric);
854	}
855
856	if(sinfo->flags & XT_CONNTRACK_REPLDST) {
857		if (sinfo->invflags & XT_CONNTRACK_REPLDST)
858			printf("! ");
859		printf("%sctrepldst ", optpfx);
860
861		print_addr(
862		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
863		    &sinfo->dipmsk[IP_CT_DIR_REPLY],
864		    false,
865		    numeric);
866	}
867
868	if(sinfo->flags & XT_CONNTRACK_STATUS) {
869        	if (sinfo->invflags & XT_CONNTRACK_STATUS)
870                	printf("! ");
871		printf("%sctstatus ", optpfx);
872		print_status(sinfo->statusmask);
873	}
874
875	if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
876        	if (sinfo->invflags & XT_CONNTRACK_EXPIRES)
877                	printf("! ");
878		printf("%sctexpire ", optpfx);
879
880        	if (sinfo->expires_max == sinfo->expires_min)
881                	printf("%lu ", sinfo->expires_min);
882        	else
883                	printf("%lu:%lu ", sinfo->expires_min, sinfo->expires_max);
884	}
885
886	if (sinfo->flags & XT_CONNTRACK_DIRECTION) {
887		if (sinfo->invflags & XT_CONNTRACK_DIRECTION)
888			printf("%sctdir REPLY", optpfx);
889		else
890			printf("%sctdir ORIGINAL", optpfx);
891	}
892
893}
894
895static void
896conntrack_dump(const struct xt_conntrack_mtinfo1 *info, const char *prefix,
897               unsigned int family, bool numeric)
898{
899	if (info->match_flags & XT_CONNTRACK_STATE) {
900		if (info->invert_flags & XT_CONNTRACK_STATE)
901			printf("! ");
902		printf("%sctstate ", prefix);
903		print_state(info->state_mask);
904	}
905
906	if (info->match_flags & XT_CONNTRACK_PROTO) {
907		if (info->invert_flags & XT_CONNTRACK_PROTO)
908			printf("! ");
909		printf("%sctproto %u ", prefix, info->l4proto);
910	}
911
912	if (info->match_flags & XT_CONNTRACK_ORIGSRC) {
913		if (info->invert_flags & XT_CONNTRACK_PROTO)
914			printf("! ");
915		printf("%sctorigsrc ", prefix);
916		conntrack_dump_addr(&info->origsrc_addr, &info->origsrc_mask,
917		                    family, numeric);
918	}
919
920	if (info->match_flags & XT_CONNTRACK_ORIGDST) {
921		if (info->invert_flags & XT_CONNTRACK_PROTO)
922			printf("! ");
923		printf("%sctorigdst ", prefix);
924		conntrack_dump_addr(&info->origdst_addr, &info->origdst_mask,
925		                    family, numeric);
926	}
927
928	if (info->match_flags & XT_CONNTRACK_REPLSRC) {
929		if (info->invert_flags & XT_CONNTRACK_PROTO)
930			printf("! ");
931		printf("%sctreplsrc ", prefix);
932		conntrack_dump_addr(&info->replsrc_addr, &info->replsrc_mask,
933		                    family, numeric);
934	}
935
936	if (info->match_flags & XT_CONNTRACK_REPLDST) {
937		if (info->invert_flags & XT_CONNTRACK_PROTO)
938			printf("! ");
939		printf("%sctrepldst ", prefix);
940		conntrack_dump_addr(&info->repldst_addr, &info->repldst_mask,
941		                    family, numeric);
942	}
943
944	if (info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) {
945		if (info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT)
946			printf("! ");
947		printf("%sctorigsrcport %u ", prefix,
948		       ntohs(info->origsrc_port));
949	}
950
951	if (info->match_flags & XT_CONNTRACK_ORIGDST_PORT) {
952		if (info->invert_flags & XT_CONNTRACK_ORIGDST_PORT)
953			printf("! ");
954		printf("%sctorigdstport %u ", prefix,
955		       ntohs(info->origdst_port));
956	}
957
958	if (info->match_flags & XT_CONNTRACK_REPLSRC_PORT) {
959		if (info->invert_flags & XT_CONNTRACK_REPLSRC_PORT)
960			printf("! ");
961		printf("%sctreplsrcport %u ", prefix,
962		       ntohs(info->replsrc_port));
963	}
964
965	if (info->match_flags & XT_CONNTRACK_REPLDST_PORT) {
966		if (info->invert_flags & XT_CONNTRACK_REPLDST_PORT)
967			printf("! ");
968		printf("%sctrepldstport %u ", prefix,
969		       ntohs(info->repldst_port));
970	}
971
972	if (info->match_flags & XT_CONNTRACK_STATUS) {
973		if (info->invert_flags & XT_CONNTRACK_STATUS)
974			printf("! ");
975		printf("%sctstatus ", prefix);
976		print_status(info->status_mask);
977	}
978
979	if (info->match_flags & XT_CONNTRACK_EXPIRES) {
980		if (info->invert_flags & XT_CONNTRACK_EXPIRES)
981			printf("! ");
982		printf("%sctexpire ", prefix);
983
984		if (info->expires_max == info->expires_min)
985			printf("%u ", (unsigned int)info->expires_min);
986		else
987			printf("%u:%u ", (unsigned int)info->expires_min,
988			       (unsigned int)info->expires_max);
989	}
990
991	if (info->match_flags & XT_CONNTRACK_DIRECTION) {
992		if (info->invert_flags & XT_CONNTRACK_DIRECTION)
993			printf("%sctdir REPLY", prefix);
994		else
995			printf("%sctdir ORIGINAL", prefix);
996	}
997}
998
999static void conntrack_print(const void *ip, const struct xt_entry_match *match,
1000                            int numeric)
1001{
1002	matchinfo_print(ip, match, numeric, "");
1003}
1004
1005static void
1006conntrack_mt_print(const void *ip, const struct xt_entry_match *match,
1007                   int numeric)
1008{
1009	conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric);
1010}
1011
1012static void
1013conntrack_mt6_print(const void *ip, const struct xt_entry_match *match,
1014                    int numeric)
1015{
1016	conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric);
1017}
1018
1019static void conntrack_save(const void *ip, const struct xt_entry_match *match)
1020{
1021	matchinfo_print(ip, match, 1, "--");
1022}
1023
1024static void conntrack_mt_save(const void *ip,
1025                              const struct xt_entry_match *match)
1026{
1027	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true);
1028}
1029
1030static void conntrack_mt6_save(const void *ip,
1031                               const struct xt_entry_match *match)
1032{
1033	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true);
1034}
1035
1036static struct xtables_match conntrack_match = {
1037	.version       = XTABLES_VERSION,
1038	.name          = "conntrack",
1039	.revision      = 0,
1040	.family        = NFPROTO_IPV4,
1041	.size          = XT_ALIGN(sizeof(struct xt_conntrack_info)),
1042	.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_info)),
1043	.help          = conntrack_mt_help,
1044	.parse         = conntrack_parse,
1045	.final_check   = conntrack_mt_check,
1046	.print         = conntrack_print,
1047	.save          = conntrack_save,
1048	.extra_opts    = conntrack_mt_opts_v0,
1049};
1050
1051static struct xtables_match conntrack_mt_reg = {
1052	.version       = XTABLES_VERSION,
1053	.name          = "conntrack",
1054	.revision      = 1,
1055	.family        = NFPROTO_IPV4,
1056	.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1057	.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1058	.help          = conntrack_mt_help,
1059	.parse         = conntrack_mt4_parse,
1060	.final_check   = conntrack_mt_check,
1061	.print         = conntrack_mt_print,
1062	.save          = conntrack_mt_save,
1063	.extra_opts    = conntrack_mt_opts,
1064};
1065
1066static struct xtables_match conntrack_mt6_reg = {
1067	.version       = XTABLES_VERSION,
1068	.name          = "conntrack",
1069	.revision      = 1,
1070	.family        = NFPROTO_IPV6,
1071	.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1072	.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1073	.help          = conntrack_mt_help,
1074	.parse         = conntrack_mt6_parse,
1075	.final_check   = conntrack_mt_check,
1076	.print         = conntrack_mt6_print,
1077	.save          = conntrack_mt6_save,
1078	.extra_opts    = conntrack_mt_opts,
1079};
1080
1081void _init(void)
1082{
1083	xtables_register_match(&conntrack_match);
1084	xtables_register_match(&conntrack_mt_reg);
1085	xtables_register_match(&conntrack_mt6_reg);
1086}
1087