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