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