libxt_conntrack.c revision 5d9678ad3eabc34ac40dfe055d7f6a8e44445a5a
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			exit_error(PARAMETER_PROBLEM, "Bad ctstate `%s'", arg);
107		arg = comma+1;
108	}
109	if (!*arg)
110		exit_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		exit_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			exit_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		exit_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			exit_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		exit_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			exit_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		exit_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 (string_to_number(s, 0, 0, &len) == -1)
232		exit_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		exit_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 (!strtonum(s, &end, &min, 0, ~0))
272		param_act(P_BAD_VALUE, "conntrack", "--expires", s);
273	max = min;
274	if (*end == ':')
275		if (!strtonum(s, &end, &max, 0, ~0U))
276			param_act(P_BAD_VALUE, "conntrack", "--expires", s);
277	if (*end != '\0')
278		param_act(P_BAD_VALUE, "conntrack", "--expires", s);
279
280	if (min > max)
281		exit_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		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		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 = parse_protocol(protocol);
321
322		if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0
323		    && (sinfo->invflags & XT_INV_PROTO))
324			exit_error(PARAMETER_PROBLEM,
325				   "rule would never match protocol");
326
327		sinfo->flags |= XT_CONNTRACK_PROTO;
328		break;
329
330	case '3':
331		check_inverse(optarg, &invert, &optind, 0);
332
333		if (invert)
334			sinfo->invflags |= XT_CONNTRACK_ORIGSRC;
335
336		ipparse_hostnetworkmask(argv[optind-1], &addrs,
337					&sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
338					&naddrs);
339		if(naddrs > 1)
340			exit_error(PARAMETER_PROBLEM,
341				"multiple IP addresses not allowed");
342
343		if(naddrs == 1) {
344			sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = addrs[0].s_addr;
345		}
346
347		sinfo->flags |= XT_CONNTRACK_ORIGSRC;
348		break;
349
350	case '4':
351		check_inverse(optarg, &invert, &optind, 0);
352
353		if (invert)
354			sinfo->invflags |= XT_CONNTRACK_ORIGDST;
355
356		ipparse_hostnetworkmask(argv[optind-1], &addrs,
357					&sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
358					&naddrs);
359		if(naddrs > 1)
360			exit_error(PARAMETER_PROBLEM,
361				"multiple IP addresses not allowed");
362
363		if(naddrs == 1) {
364			sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = addrs[0].s_addr;
365		}
366
367		sinfo->flags |= XT_CONNTRACK_ORIGDST;
368		break;
369
370	case '5':
371		check_inverse(optarg, &invert, &optind, 0);
372
373		if (invert)
374			sinfo->invflags |= XT_CONNTRACK_REPLSRC;
375
376		ipparse_hostnetworkmask(argv[optind-1], &addrs,
377					&sinfo->sipmsk[IP_CT_DIR_REPLY],
378					&naddrs);
379		if(naddrs > 1)
380			exit_error(PARAMETER_PROBLEM,
381				"multiple IP addresses not allowed");
382
383		if(naddrs == 1) {
384			sinfo->tuple[IP_CT_DIR_REPLY].src.ip = addrs[0].s_addr;
385		}
386
387		sinfo->flags |= XT_CONNTRACK_REPLSRC;
388		break;
389
390	case '6':
391		check_inverse(optarg, &invert, &optind, 0);
392
393		if (invert)
394			sinfo->invflags |= XT_CONNTRACK_REPLDST;
395
396		ipparse_hostnetworkmask(argv[optind-1], &addrs,
397					&sinfo->dipmsk[IP_CT_DIR_REPLY],
398					&naddrs);
399		if(naddrs > 1)
400			exit_error(PARAMETER_PROBLEM,
401				"multiple IP addresses not allowed");
402
403		if(naddrs == 1) {
404			sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = addrs[0].s_addr;
405		}
406
407		sinfo->flags |= XT_CONNTRACK_REPLDST;
408		break;
409
410	case '7':
411		check_inverse(optarg, &invert, &optind, 0);
412
413		parse_statuses(argv[optind-1], sinfo);
414		if (invert) {
415			sinfo->invflags |= XT_CONNTRACK_STATUS;
416		}
417		sinfo->flags |= XT_CONNTRACK_STATUS;
418		break;
419
420	case '8':
421		check_inverse(optarg, &invert, &optind, 0);
422
423		parse_expires(argv[optind-1], sinfo);
424		if (invert) {
425			sinfo->invflags |= XT_CONNTRACK_EXPIRES;
426		}
427		sinfo->flags |= XT_CONNTRACK_EXPIRES;
428		break;
429
430	default:
431		return 0;
432	}
433
434	*flags = sinfo->flags;
435	return 1;
436}
437
438static int
439conntrack_mt_parse(int c, char **argv, int invert, unsigned int *flags,
440                   struct xt_entry_match **match)
441{
442	struct xt_conntrack_mtinfo1 *info = (void *)(*match)->data;
443	unsigned int port;
444	char *p;
445
446	switch (c) {
447	case '1': /* --ctstate */
448		conntrack_ps_states(info, optarg);
449		info->match_flags |= XT_CONNTRACK_STATE;
450		if (invert)
451			info->invert_flags |= XT_CONNTRACK_STATE;
452		break;
453
454	case '2': /* --ctproto */
455		/* Canonicalize into lower case */
456		for (p = optarg; *p != '\0'; ++p)
457			*p = tolower(*p);
458		info->l4proto = parse_protocol(optarg);
459
460		if (info->l4proto == 0 && (info->invert_flags & XT_INV_PROTO))
461			exit_error(PARAMETER_PROBLEM, "conntrack: rule would "
462			           "never match protocol");
463
464		info->match_flags |= XT_CONNTRACK_PROTO;
465		if (invert)
466			info->invert_flags |= XT_CONNTRACK_PROTO;
467		break;
468
469	case '7': /* --ctstatus */
470		conntrack_ps_statuses(info, optarg);
471		info->match_flags |= XT_CONNTRACK_STATUS;
472		if (invert)
473			info->invert_flags |= XT_CONNTRACK_STATUS;
474		break;
475
476	case '8': /* --ctexpire */
477		conntrack_ps_expires(info, optarg);
478		info->match_flags |= XT_CONNTRACK_EXPIRES;
479		if (invert)
480			info->invert_flags |= XT_CONNTRACK_EXPIRES;
481		break;
482
483	case 'a': /* --ctorigsrcport */
484		if (!strtonum(optarg, NULL, &port, 0, ~(u_int16_t)0))
485			param_act(P_BAD_VALUE, "conntrack",
486			          "--ctorigsrcport", optarg);
487		info->match_flags |= XT_CONNTRACK_ORIGSRC_PORT;
488		info->origsrc_port = htons(port);
489		if (invert)
490			info->invert_flags |= XT_CONNTRACK_ORIGSRC_PORT;
491		break;
492
493	case 'b': /* --ctorigdstport */
494		if (!strtonum(optarg, NULL, &port, 0, ~(u_int16_t)0))
495			param_act(P_BAD_VALUE, "conntrack",
496			          "--ctorigdstport", optarg);
497		info->match_flags |= XT_CONNTRACK_ORIGDST_PORT;
498		info->origdst_port = htons(port);
499		if (invert)
500			info->invert_flags |= XT_CONNTRACK_ORIGDST_PORT;
501		break;
502
503	case 'c': /* --ctreplsrcport */
504		if (!strtonum(optarg, NULL, &port, 0, ~(u_int16_t)0))
505			param_act(P_BAD_VALUE, "conntrack",
506			          "--ctreplsrcport", optarg);
507		info->match_flags |= XT_CONNTRACK_REPLSRC_PORT;
508		info->replsrc_port = htons(port);
509		if (invert)
510			info->invert_flags |= XT_CONNTRACK_REPLSRC_PORT;
511		break;
512
513	case 'd': /* --ctrepldstport */
514		if (!strtonum(optarg, NULL, &port, 0, ~(u_int16_t)0))
515			param_act(P_BAD_VALUE, "conntrack",
516			          "--ctrepldstport", optarg);
517		info->match_flags |= XT_CONNTRACK_REPLDST_PORT;
518		info->repldst_port = htons(port);
519		if (invert)
520			info->invert_flags |= XT_CONNTRACK_REPLDST_PORT;
521		break;
522
523	case 'e': /* --ctdir */
524		param_act(P_NO_INVERT, "conntrack", "--ctdir", invert);
525		if (strcasecmp(optarg, "ORIGINAL") == 0) {
526			info->match_flags  |= XT_CONNTRACK_DIRECTION;
527			info->invert_flags &= ~XT_CONNTRACK_DIRECTION;
528		} else if (strcasecmp(optarg, "REPLY") == 0) {
529			info->match_flags  |= XT_CONNTRACK_DIRECTION;
530			info->invert_flags |= XT_CONNTRACK_DIRECTION;
531		} else {
532			param_act(P_BAD_VALUE, "conntrack", "--ctdir", optarg);
533		}
534		break;
535
536	default:
537		return false;
538	}
539
540	*flags = info->match_flags;
541	return true;
542}
543
544static int
545conntrack_mt4_parse(int c, char **argv, int invert, unsigned int *flags,
546                    const void *entry, struct xt_entry_match **match)
547{
548	struct xt_conntrack_mtinfo1 *info = (void *)(*match)->data;
549	struct in_addr *addr = NULL;
550	unsigned int naddrs = 0;
551
552	switch (c) {
553	case '3': /* --ctorigsrc */
554		ipparse_hostnetworkmask(optarg, &addr, &info->origsrc_mask.in,
555		                        &naddrs);
556		if (naddrs > 1)
557			exit_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		ipparse_hostnetworkmask(optarg, &addr, &info->origdst_mask.in,
568		                        &naddrs);
569		if (naddrs > 1)
570			exit_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		ipparse_hostnetworkmask(optarg, &addr, &info->replsrc_mask.in,
581		                        &naddrs);
582		if (naddrs > 1)
583			exit_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		ipparse_hostnetworkmask(optarg, &addr, &info->repldst_mask.in,
594		                        &naddrs);
595		if (naddrs > 1)
596			exit_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, argv, invert, flags, match);
608	}
609
610	*flags = info->match_flags;
611	return true;
612}
613
614static int
615conntrack_mt6_parse(int c, char **argv, int invert, unsigned int *flags,
616                    const void *entry, struct xt_entry_match **match)
617{
618	struct xt_conntrack_mtinfo1 *info = (void *)(*match)->data;
619	struct in6_addr *addr = NULL;
620	unsigned int naddrs = 0;
621
622	switch (c) {
623	case '3': /* --ctorigsrc */
624		ip6parse_hostnetworkmask(optarg, &addr,
625		                         &info->origsrc_mask.in6, &naddrs);
626		if (naddrs > 1)
627			exit_error(PARAMETER_PROBLEM,
628			           "multiple IP addresses not allowed");
629		if (naddrs == 1)
630			memcpy(&info->origsrc_addr.in6, addr, sizeof(*addr));
631		info->match_flags |= XT_CONNTRACK_ORIGSRC;
632		if (invert)
633			info->invert_flags |= XT_CONNTRACK_ORIGSRC;
634		break;
635
636	case '4': /* --ctorigdst */
637		ip6parse_hostnetworkmask(optarg, &addr,
638		                         &info->origdst_mask.in6, &naddrs);
639		if (naddrs > 1)
640			exit_error(PARAMETER_PROBLEM,
641			           "multiple IP addresses not allowed");
642		if (naddrs == 1)
643			memcpy(&info->origdst_addr.in, addr, sizeof(*addr));
644		info->match_flags |= XT_CONNTRACK_ORIGDST;
645		if (invert)
646			info->invert_flags |= XT_CONNTRACK_ORIGDST;
647		break;
648
649	case '5': /* --ctreplsrc */
650		ip6parse_hostnetworkmask(optarg, &addr,
651		                         &info->replsrc_mask.in6, &naddrs);
652		if (naddrs > 1)
653			exit_error(PARAMETER_PROBLEM,
654			           "multiple IP addresses not allowed");
655		if (naddrs == 1)
656			memcpy(&info->replsrc_addr.in, addr, sizeof(*addr));
657		info->match_flags |= XT_CONNTRACK_REPLSRC;
658		if (invert)
659			info->invert_flags |= XT_CONNTRACK_REPLSRC;
660		break;
661
662	case '6': /* --ctrepldst */
663		ip6parse_hostnetworkmask(optarg, &addr,
664		                         &info->repldst_mask.in6, &naddrs);
665		if (naddrs > 1)
666			exit_error(PARAMETER_PROBLEM,
667			           "multiple IP addresses not allowed");
668		if (naddrs == 1)
669			memcpy(&info->repldst_addr.in, addr, sizeof(*addr));
670		info->match_flags |= XT_CONNTRACK_REPLDST;
671		if (invert)
672			info->invert_flags |= XT_CONNTRACK_REPLDST;
673		break;
674
675
676	default:
677		return conntrack_mt_parse(c, argv, invert, flags, match);
678	}
679
680	*flags = info->match_flags;
681	return true;
682}
683
684static void conntrack_mt_check(unsigned int flags)
685{
686	if (flags == 0)
687		exit_error(PARAMETER_PROBLEM, "conntrack: At least one option "
688		           "is required");
689}
690
691static void
692print_state(unsigned int statemask)
693{
694	const char *sep = "";
695
696	if (statemask & XT_CONNTRACK_STATE_INVALID) {
697		printf("%sINVALID", sep);
698		sep = ",";
699	}
700	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
701		printf("%sNEW", sep);
702		sep = ",";
703	}
704	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
705		printf("%sRELATED", sep);
706		sep = ",";
707	}
708	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
709		printf("%sESTABLISHED", sep);
710		sep = ",";
711	}
712	if (statemask & XT_CONNTRACK_STATE_UNTRACKED) {
713		printf("%sUNTRACKED", sep);
714		sep = ",";
715	}
716	if (statemask & XT_CONNTRACK_STATE_SNAT) {
717		printf("%sSNAT", sep);
718		sep = ",";
719	}
720	if (statemask & XT_CONNTRACK_STATE_DNAT) {
721		printf("%sDNAT", sep);
722		sep = ",";
723	}
724	printf(" ");
725}
726
727static void
728print_status(unsigned int statusmask)
729{
730	const char *sep = "";
731
732	if (statusmask & IPS_EXPECTED) {
733		printf("%sEXPECTED", sep);
734		sep = ",";
735	}
736	if (statusmask & IPS_SEEN_REPLY) {
737		printf("%sSEEN_REPLY", sep);
738		sep = ",";
739	}
740	if (statusmask & IPS_ASSURED) {
741		printf("%sASSURED", sep);
742		sep = ",";
743	}
744	if (statusmask & IPS_CONFIRMED) {
745		printf("%sCONFIRMED", sep);
746		sep = ",";
747	}
748	if (statusmask == 0)
749		printf("%sNONE", sep);
750	printf(" ");
751}
752
753static void
754conntrack_dump_addr(const union nf_inet_addr *addr,
755                    const union nf_inet_addr *mask,
756                    unsigned int family, bool numeric)
757{
758	if (family == NFPROTO_IPV4) {
759		if (!numeric && addr->ip == 0) {
760			printf("anywhere ");
761			return;
762		}
763		if (numeric)
764			printf("%s ", ipaddr_to_numeric(&addr->in));
765		else
766			printf("%s ", ipaddr_to_anyname(&addr->in));
767	} else if (family == NFPROTO_IPV6) {
768		if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 &&
769		    addr->ip6[2] == 0 && addr->ip6[3] == 0) {
770			printf("anywhere ");
771			return;
772		}
773		if (numeric)
774			printf("%s ", ip6addr_to_numeric(&addr->in6));
775		else
776			printf("%s ", ip6addr_to_anyname(&addr->in6));
777	}
778}
779
780static void
781print_addr(struct in_addr *addr, struct in_addr *mask, int inv, int numeric)
782{
783	char buf[BUFSIZ];
784
785	if (inv)
786	       	printf("! ");
787
788	if (mask->s_addr == 0L && !numeric)
789		printf("%s ", "anywhere");
790	else {
791		if (numeric)
792			sprintf(buf, "%s", ipaddr_to_numeric(addr));
793		else
794			sprintf(buf, "%s", ipaddr_to_anyname(addr));
795		strcat(buf, ipmask_to_numeric(mask));
796		printf("%s ", buf);
797	}
798}
799
800static void
801matchinfo_print(const void *ip, const struct xt_entry_match *match, int numeric, const char *optpfx)
802{
803	struct xt_conntrack_info *sinfo = (void *)match->data;
804
805	if(sinfo->flags & XT_CONNTRACK_STATE) {
806        	if (sinfo->invflags & XT_CONNTRACK_STATE)
807                	printf("! ");
808		printf("%sctstate ", optpfx);
809		print_state(sinfo->statemask);
810	}
811
812	if(sinfo->flags & XT_CONNTRACK_PROTO) {
813        	if (sinfo->invflags & XT_CONNTRACK_PROTO)
814                	printf("! ");
815		printf("%sctproto ", optpfx);
816		printf("%u ", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum);
817	}
818
819	if(sinfo->flags & XT_CONNTRACK_ORIGSRC) {
820		if (sinfo->invflags & XT_CONNTRACK_ORIGSRC)
821			printf("! ");
822		printf("%sctorigsrc ", optpfx);
823
824		print_addr(
825		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
826		    &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
827		    false,
828		    numeric);
829	}
830
831	if(sinfo->flags & XT_CONNTRACK_ORIGDST) {
832		if (sinfo->invflags & XT_CONNTRACK_ORIGDST)
833			printf("! ");
834		printf("%sctorigdst ", optpfx);
835
836		print_addr(
837		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
838		    &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
839		    false,
840		    numeric);
841	}
842
843	if(sinfo->flags & XT_CONNTRACK_REPLSRC) {
844		if (sinfo->invflags & XT_CONNTRACK_REPLSRC)
845			printf("! ");
846		printf("%sctreplsrc ", optpfx);
847
848		print_addr(
849		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
850		    &sinfo->sipmsk[IP_CT_DIR_REPLY],
851		    false,
852		    numeric);
853	}
854
855	if(sinfo->flags & XT_CONNTRACK_REPLDST) {
856		if (sinfo->invflags & XT_CONNTRACK_REPLDST)
857			printf("! ");
858		printf("%sctrepldst ", optpfx);
859
860		print_addr(
861		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
862		    &sinfo->dipmsk[IP_CT_DIR_REPLY],
863		    false,
864		    numeric);
865	}
866
867	if(sinfo->flags & XT_CONNTRACK_STATUS) {
868        	if (sinfo->invflags & XT_CONNTRACK_STATUS)
869                	printf("! ");
870		printf("%sctstatus ", optpfx);
871		print_status(sinfo->statusmask);
872	}
873
874	if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
875        	if (sinfo->invflags & XT_CONNTRACK_EXPIRES)
876                	printf("! ");
877		printf("%sctexpire ", optpfx);
878
879        	if (sinfo->expires_max == sinfo->expires_min)
880                	printf("%lu ", sinfo->expires_min);
881        	else
882                	printf("%lu:%lu ", sinfo->expires_min, sinfo->expires_max);
883	}
884
885	if (sinfo->flags & XT_CONNTRACK_DIRECTION) {
886		if (sinfo->invflags & XT_CONNTRACK_DIRECTION)
887			printf("%sctdir REPLY", optpfx);
888		else
889			printf("%sctdir ORIGINAL", optpfx);
890	}
891
892}
893
894static void
895conntrack_dump(const struct xt_conntrack_mtinfo1 *info, const char *prefix,
896               unsigned int family, bool numeric)
897{
898	if (info->match_flags & XT_CONNTRACK_STATE) {
899		if (info->invert_flags & XT_CONNTRACK_STATE)
900			printf("! ");
901		printf("%sctstate ", prefix);
902		print_state(info->state_mask);
903	}
904
905	if (info->match_flags & XT_CONNTRACK_PROTO) {
906		if (info->invert_flags & XT_CONNTRACK_PROTO)
907			printf("! ");
908		printf("%sctproto %u ", prefix, info->l4proto);
909	}
910
911	if (info->match_flags & XT_CONNTRACK_ORIGSRC) {
912		if (info->invert_flags & XT_CONNTRACK_PROTO)
913			printf("! ");
914		printf("%sctorigsrc ", prefix);
915		conntrack_dump_addr(&info->origsrc_addr, &info->origsrc_mask,
916		                    family, numeric);
917	}
918
919	if (info->match_flags & XT_CONNTRACK_ORIGDST) {
920		if (info->invert_flags & XT_CONNTRACK_PROTO)
921			printf("! ");
922		printf("%sctorigdst ", prefix);
923		conntrack_dump_addr(&info->origdst_addr, &info->origdst_mask,
924		                    family, numeric);
925	}
926
927	if (info->match_flags & XT_CONNTRACK_REPLSRC) {
928		if (info->invert_flags & XT_CONNTRACK_PROTO)
929			printf("! ");
930		printf("%sctreplsrc ", prefix);
931		conntrack_dump_addr(&info->replsrc_addr, &info->replsrc_mask,
932		                    family, numeric);
933	}
934
935	if (info->match_flags & XT_CONNTRACK_REPLDST) {
936		if (info->invert_flags & XT_CONNTRACK_PROTO)
937			printf("! ");
938		printf("%sctrepldst ", prefix);
939		conntrack_dump_addr(&info->repldst_addr, &info->repldst_mask,
940		                    family, numeric);
941	}
942
943	if (info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) {
944		if (info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT)
945			printf("! ");
946		printf("%sctorigsrcport %u ", prefix,
947		       ntohs(info->origsrc_port));
948	}
949
950	if (info->match_flags & XT_CONNTRACK_ORIGDST_PORT) {
951		if (info->invert_flags & XT_CONNTRACK_ORIGDST_PORT)
952			printf("! ");
953		printf("%sctorigdstport %u ", prefix,
954		       ntohs(info->origdst_port));
955	}
956
957	if (info->match_flags & XT_CONNTRACK_REPLSRC_PORT) {
958		if (info->invert_flags & XT_CONNTRACK_REPLSRC_PORT)
959			printf("! ");
960		printf("%sctreplsrcport %u ", prefix,
961		       ntohs(info->replsrc_port));
962	}
963
964	if (info->match_flags & XT_CONNTRACK_REPLDST_PORT) {
965		if (info->invert_flags & XT_CONNTRACK_REPLDST_PORT)
966			printf("! ");
967		printf("%sctrepldstport %u ", prefix,
968		       ntohs(info->repldst_port));
969	}
970
971	if (info->match_flags & XT_CONNTRACK_STATUS) {
972		if (info->invert_flags & XT_CONNTRACK_STATUS)
973			printf("! ");
974		printf("%sctstatus ", prefix);
975		print_status(info->status_mask);
976	}
977
978	if (info->match_flags & XT_CONNTRACK_EXPIRES) {
979		if (info->invert_flags & XT_CONNTRACK_EXPIRES)
980			printf("! ");
981		printf("%sctexpire ", prefix);
982
983		if (info->expires_max == info->expires_min)
984			printf("%u ", (unsigned int)info->expires_min);
985		else
986			printf("%u:%u ", (unsigned int)info->expires_min,
987			       (unsigned int)info->expires_max);
988	}
989
990	if (info->match_flags & XT_CONNTRACK_DIRECTION) {
991		if (info->invert_flags & XT_CONNTRACK_DIRECTION)
992			printf("%sctdir REPLY", prefix);
993		else
994			printf("%sctdir ORIGINAL", prefix);
995	}
996}
997
998static void conntrack_print(const void *ip, const struct xt_entry_match *match,
999                            int numeric)
1000{
1001	matchinfo_print(ip, match, numeric, "");
1002}
1003
1004static void
1005conntrack_mt_print(const void *ip, const struct xt_entry_match *match,
1006                   int numeric)
1007{
1008	conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric);
1009}
1010
1011static void
1012conntrack_mt6_print(const void *ip, const struct xt_entry_match *match,
1013                    int numeric)
1014{
1015	conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric);
1016}
1017
1018static void conntrack_save(const void *ip, const struct xt_entry_match *match)
1019{
1020	matchinfo_print(ip, match, 1, "--");
1021}
1022
1023static void conntrack_mt_save(const void *ip,
1024                              const struct xt_entry_match *match)
1025{
1026	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true);
1027}
1028
1029static void conntrack_mt6_save(const void *ip,
1030                               const struct xt_entry_match *match)
1031{
1032	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true);
1033}
1034
1035static struct xtables_match conntrack_match = {
1036	.version       = XTABLES_VERSION,
1037	.name          = "conntrack",
1038	.revision      = 0,
1039	.family        = NFPROTO_IPV4,
1040	.size          = XT_ALIGN(sizeof(struct xt_conntrack_info)),
1041	.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_info)),
1042	.help          = conntrack_mt_help,
1043	.parse         = conntrack_parse,
1044	.final_check   = conntrack_mt_check,
1045	.print         = conntrack_print,
1046	.save          = conntrack_save,
1047	.extra_opts    = conntrack_mt_opts_v0,
1048};
1049
1050static struct xtables_match conntrack_mt_reg = {
1051	.version       = XTABLES_VERSION,
1052	.name          = "conntrack",
1053	.revision      = 1,
1054	.family        = NFPROTO_IPV4,
1055	.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1056	.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1057	.help          = conntrack_mt_help,
1058	.parse         = conntrack_mt4_parse,
1059	.final_check   = conntrack_mt_check,
1060	.print         = conntrack_mt_print,
1061	.save          = conntrack_mt_save,
1062	.extra_opts    = conntrack_mt_opts,
1063};
1064
1065static struct xtables_match conntrack_mt6_reg = {
1066	.version       = XTABLES_VERSION,
1067	.name          = "conntrack",
1068	.revision      = 1,
1069	.family        = NFPROTO_IPV6,
1070	.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1071	.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
1072	.help          = conntrack_mt_help,
1073	.parse         = conntrack_mt6_parse,
1074	.final_check   = conntrack_mt_check,
1075	.print         = conntrack_mt6_print,
1076	.save          = conntrack_mt6_save,
1077	.extra_opts    = conntrack_mt_opts,
1078};
1079
1080void _init(void)
1081{
1082	xtables_register_match(&conntrack_match);
1083	xtables_register_match(&conntrack_mt_reg);
1084	xtables_register_match(&conntrack_mt6_reg);
1085}
1086