1/* Shared library add-on to iptables for conntrack matching support.
2 * GPL (C) 2001  Marc Boucher (marc@mbsi.ca).
3 */
4
5#include <stdio.h>
6#include <netdb.h>
7#include <string.h>
8#include <stdlib.h>
9#include <getopt.h>
10#include <ctype.h>
11#include <iptables.h>
12#include <linux/netfilter/nf_conntrack_common.h>
13/* For 64bit kernel / 32bit userspace */
14#include "../include/linux/netfilter_ipv4/ipt_conntrack.h"
15
16#ifndef IPT_CONNTRACK_STATE_UNTRACKED
17#define IPT_CONNTRACK_STATE_UNTRACKED (1 << (IP_CT_NUMBER + 3))
18#endif
19
20/* Function which prints out usage message. */
21static void
22help(void)
23{
24	printf(
25"conntrack match v%s options:\n"
26" [!] --ctstate [INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED|SNAT|DNAT][,...]\n"
27"				State(s) to match\n"
28" [!] --ctproto	proto		Protocol to match; by number or name, eg. `tcp'\n"
29"     --ctorigsrc  [!] address[/mask]\n"
30"				Original source specification\n"
31"     --ctorigdst  [!] address[/mask]\n"
32"				Original destination specification\n"
33"     --ctreplsrc  [!] address[/mask]\n"
34"				Reply source specification\n"
35"     --ctrepldst  [!] address[/mask]\n"
36"				Reply destination specification\n"
37" [!] --ctstatus [NONE|EXPECTED|SEEN_REPLY|ASSURED|CONFIRMED][,...]\n"
38"				Status(es) to match\n"
39" [!] --ctexpire time[:time]	Match remaining lifetime in seconds against\n"
40"				value or range of values (inclusive)\n"
41"\n", IPTABLES_VERSION);
42}
43
44
45
46static struct option opts[] = {
47	{ "ctstate", 1, 0, '1' },
48	{ "ctproto", 1, 0, '2' },
49	{ "ctorigsrc", 1, 0, '3' },
50	{ "ctorigdst", 1, 0, '4' },
51	{ "ctreplsrc", 1, 0, '5' },
52	{ "ctrepldst", 1, 0, '6' },
53	{ "ctstatus", 1, 0, '7' },
54	{ "ctexpire", 1, 0, '8' },
55	{0}
56};
57
58static int
59parse_state(const char *state, size_t strlen, struct ipt_conntrack_info *sinfo)
60{
61	if (strncasecmp(state, "INVALID", strlen) == 0)
62		sinfo->statemask |= IPT_CONNTRACK_STATE_INVALID;
63	else if (strncasecmp(state, "NEW", strlen) == 0)
64		sinfo->statemask |= IPT_CONNTRACK_STATE_BIT(IP_CT_NEW);
65	else if (strncasecmp(state, "ESTABLISHED", strlen) == 0)
66		sinfo->statemask |= IPT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
67	else if (strncasecmp(state, "RELATED", strlen) == 0)
68		sinfo->statemask |= IPT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
69	else if (strncasecmp(state, "UNTRACKED", strlen) == 0)
70		sinfo->statemask |= IPT_CONNTRACK_STATE_UNTRACKED;
71	else if (strncasecmp(state, "SNAT", strlen) == 0)
72		sinfo->statemask |= IPT_CONNTRACK_STATE_SNAT;
73	else if (strncasecmp(state, "DNAT", strlen) == 0)
74		sinfo->statemask |= IPT_CONNTRACK_STATE_DNAT;
75	else
76		return 0;
77	return 1;
78}
79
80static void
81parse_states(const char *arg, struct ipt_conntrack_info *sinfo)
82{
83	const char *comma;
84
85	while ((comma = strchr(arg, ',')) != NULL) {
86		if (comma == arg || !parse_state(arg, comma-arg, sinfo))
87			exit_error(PARAMETER_PROBLEM, "Bad ctstate `%s'", arg);
88		arg = comma+1;
89	}
90
91	if (strlen(arg) == 0 || !parse_state(arg, strlen(arg), sinfo))
92		exit_error(PARAMETER_PROBLEM, "Bad ctstate `%s'", arg);
93}
94
95static int
96parse_status(const char *status, size_t strlen, struct ipt_conntrack_info *sinfo)
97{
98	if (strncasecmp(status, "NONE", strlen) == 0)
99		sinfo->statusmask |= 0;
100	else if (strncasecmp(status, "EXPECTED", strlen) == 0)
101		sinfo->statusmask |= IPS_EXPECTED;
102	else if (strncasecmp(status, "SEEN_REPLY", strlen) == 0)
103		sinfo->statusmask |= IPS_SEEN_REPLY;
104	else if (strncasecmp(status, "ASSURED", strlen) == 0)
105		sinfo->statusmask |= IPS_ASSURED;
106#ifdef IPS_CONFIRMED
107	else if (strncasecmp(status, "CONFIRMED", strlen) == 0)
108		sinfo->stausmask |= IPS_CONFIRMED;
109#endif
110	else
111		return 0;
112	return 1;
113}
114
115static void
116parse_statuses(const char *arg, struct ipt_conntrack_info *sinfo)
117{
118	const char *comma;
119
120	while ((comma = strchr(arg, ',')) != NULL) {
121		if (comma == arg || !parse_status(arg, comma-arg, sinfo))
122			exit_error(PARAMETER_PROBLEM, "Bad ctstatus `%s'", arg);
123		arg = comma+1;
124	}
125
126	if (strlen(arg) == 0 || !parse_status(arg, strlen(arg), sinfo))
127		exit_error(PARAMETER_PROBLEM, "Bad ctstatus `%s'", arg);
128}
129
130#ifdef KERNEL_64_USERSPACE_32
131static unsigned long long
132parse_expire(const char *s)
133{
134	unsigned long long len;
135
136	if (string_to_number_ll(s, 0, 0, &len) == -1)
137		exit_error(PARAMETER_PROBLEM, "expire value invalid: `%s'\n", s);
138	else
139		return len;
140}
141#else
142static unsigned long
143parse_expire(const char *s)
144{
145	unsigned int len;
146
147	if (string_to_number(s, 0, 0, &len) == -1)
148		exit_error(PARAMETER_PROBLEM, "expire value invalid: `%s'\n", s);
149	else
150		return len;
151}
152#endif
153
154/* If a single value is provided, min and max are both set to the value */
155static void
156parse_expires(const char *s, struct ipt_conntrack_info *sinfo)
157{
158	char *buffer;
159	char *cp;
160
161	buffer = strdup(s);
162	if ((cp = strchr(buffer, ':')) == NULL)
163		sinfo->expires_min = sinfo->expires_max = parse_expire(buffer);
164	else {
165		*cp = '\0';
166		cp++;
167
168		sinfo->expires_min = buffer[0] ? parse_expire(buffer) : 0;
169		sinfo->expires_max = cp[0] ? parse_expire(cp) : -1;
170	}
171	free(buffer);
172
173	if (sinfo->expires_min > sinfo->expires_max)
174		exit_error(PARAMETER_PROBLEM,
175#ifdef KERNEL_64_USERSPACE_32
176		           "expire min. range value `%llu' greater than max. "
177		           "range value `%llu'", sinfo->expires_min, sinfo->expires_max);
178#else
179		           "expire min. range value `%lu' greater than max. "
180		           "range value `%lu'", sinfo->expires_min, sinfo->expires_max);
181#endif
182}
183
184/* Function which parses command options; returns true if it
185   ate an option */
186static int
187parse(int c, char **argv, int invert, unsigned int *flags,
188      const struct ipt_entry *entry,
189      unsigned int *nfcache,
190      struct ipt_entry_match **match)
191{
192	struct ipt_conntrack_info *sinfo = (struct ipt_conntrack_info *)(*match)->data;
193	char *protocol = NULL;
194	unsigned int naddrs = 0;
195	struct in_addr *addrs = NULL;
196
197
198	switch (c) {
199	case '1':
200		check_inverse(optarg, &invert, &optind, 0);
201
202		parse_states(argv[optind-1], sinfo);
203		if (invert) {
204			sinfo->invflags |= IPT_CONNTRACK_STATE;
205		}
206		sinfo->flags |= IPT_CONNTRACK_STATE;
207		break;
208
209	case '2':
210		check_inverse(optarg, &invert, &optind, 0);
211
212		if(invert)
213			sinfo->invflags |= IPT_CONNTRACK_PROTO;
214
215		/* Canonicalize into lower case */
216		for (protocol = argv[optind-1]; *protocol; protocol++)
217			*protocol = tolower(*protocol);
218
219		protocol = argv[optind-1];
220		sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = parse_protocol(protocol);
221
222		if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0
223		    && (sinfo->invflags & IPT_INV_PROTO))
224			exit_error(PARAMETER_PROBLEM,
225				   "rule would never match protocol");
226
227		sinfo->flags |= IPT_CONNTRACK_PROTO;
228		break;
229
230	case '3':
231		check_inverse(optarg, &invert, &optind, 9);
232
233		if (invert)
234			sinfo->invflags |= IPT_CONNTRACK_ORIGSRC;
235
236		parse_hostnetworkmask(argv[optind-1], &addrs,
237					&sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
238					&naddrs);
239		if(naddrs > 1)
240			exit_error(PARAMETER_PROBLEM,
241				"multiple IP addresses not allowed");
242
243		if(naddrs == 1) {
244			sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = addrs[0].s_addr;
245		}
246
247		sinfo->flags |= IPT_CONNTRACK_ORIGSRC;
248		break;
249
250	case '4':
251		check_inverse(optarg, &invert, &optind, 0);
252
253		if (invert)
254			sinfo->invflags |= IPT_CONNTRACK_ORIGDST;
255
256		parse_hostnetworkmask(argv[optind-1], &addrs,
257					&sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
258					&naddrs);
259		if(naddrs > 1)
260			exit_error(PARAMETER_PROBLEM,
261				"multiple IP addresses not allowed");
262
263		if(naddrs == 1) {
264			sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = addrs[0].s_addr;
265		}
266
267		sinfo->flags |= IPT_CONNTRACK_ORIGDST;
268		break;
269
270	case '5':
271		check_inverse(optarg, &invert, &optind, 0);
272
273		if (invert)
274			sinfo->invflags |= IPT_CONNTRACK_REPLSRC;
275
276		parse_hostnetworkmask(argv[optind-1], &addrs,
277					&sinfo->sipmsk[IP_CT_DIR_REPLY],
278					&naddrs);
279		if(naddrs > 1)
280			exit_error(PARAMETER_PROBLEM,
281				"multiple IP addresses not allowed");
282
283		if(naddrs == 1) {
284			sinfo->tuple[IP_CT_DIR_REPLY].src.ip = addrs[0].s_addr;
285		}
286
287		sinfo->flags |= IPT_CONNTRACK_REPLSRC;
288		break;
289
290	case '6':
291		check_inverse(optarg, &invert, &optind, 0);
292
293		if (invert)
294			sinfo->invflags |= IPT_CONNTRACK_REPLDST;
295
296		parse_hostnetworkmask(argv[optind-1], &addrs,
297					&sinfo->dipmsk[IP_CT_DIR_REPLY],
298					&naddrs);
299		if(naddrs > 1)
300			exit_error(PARAMETER_PROBLEM,
301				"multiple IP addresses not allowed");
302
303		if(naddrs == 1) {
304			sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = addrs[0].s_addr;
305		}
306
307		sinfo->flags |= IPT_CONNTRACK_REPLDST;
308		break;
309
310	case '7':
311		check_inverse(optarg, &invert, &optind, 0);
312
313		parse_statuses(argv[optind-1], sinfo);
314		if (invert) {
315			sinfo->invflags |= IPT_CONNTRACK_STATUS;
316		}
317		sinfo->flags |= IPT_CONNTRACK_STATUS;
318		break;
319
320	case '8':
321		check_inverse(optarg, &invert, &optind, 0);
322
323		parse_expires(argv[optind-1], sinfo);
324		if (invert) {
325			sinfo->invflags |= IPT_CONNTRACK_EXPIRES;
326		}
327		sinfo->flags |= IPT_CONNTRACK_EXPIRES;
328		break;
329
330	default:
331		return 0;
332	}
333
334	*flags = sinfo->flags;
335	return 1;
336}
337
338static void
339final_check(unsigned int flags)
340{
341	if (!flags)
342		exit_error(PARAMETER_PROBLEM, "You must specify one or more options");
343}
344
345static void
346print_state(unsigned int statemask)
347{
348	const char *sep = "";
349
350	if (statemask & IPT_CONNTRACK_STATE_INVALID) {
351		printf("%sINVALID", sep);
352		sep = ",";
353	}
354	if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
355		printf("%sNEW", sep);
356		sep = ",";
357	}
358	if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
359		printf("%sRELATED", sep);
360		sep = ",";
361	}
362	if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
363		printf("%sESTABLISHED", sep);
364		sep = ",";
365	}
366	if (statemask & IPT_CONNTRACK_STATE_UNTRACKED) {
367		printf("%sUNTRACKED", sep);
368		sep = ",";
369	}
370	if (statemask & IPT_CONNTRACK_STATE_SNAT) {
371		printf("%sSNAT", sep);
372		sep = ",";
373	}
374	if (statemask & IPT_CONNTRACK_STATE_DNAT) {
375		printf("%sDNAT", sep);
376		sep = ",";
377	}
378	printf(" ");
379}
380
381static void
382print_status(unsigned int statusmask)
383{
384	const char *sep = "";
385
386	if (statusmask & IPS_EXPECTED) {
387		printf("%sEXPECTED", sep);
388		sep = ",";
389	}
390	if (statusmask & IPS_SEEN_REPLY) {
391		printf("%sSEEN_REPLY", sep);
392		sep = ",";
393	}
394	if (statusmask & IPS_ASSURED) {
395		printf("%sASSURED", sep);
396		sep = ",";
397	}
398#ifdef IPS_CONFIRMED
399	if (statusmask & IPS_CONFIRMED) {
400		printf("%sCONFIRMED", sep);
401		sep =",";
402	}
403#endif
404	if (statusmask == 0) {
405		printf("%sNONE", sep);
406		sep = ",";
407	}
408	printf(" ");
409}
410
411static void
412print_addr(struct in_addr *addr, struct in_addr *mask, int inv, int numeric)
413{
414	char buf[BUFSIZ];
415
416        if (inv)
417               	printf("! ");
418
419	if (mask->s_addr == 0L && !numeric)
420		printf("%s ", "anywhere");
421	else {
422		if (numeric)
423			sprintf(buf, "%s", addr_to_dotted(addr));
424		else
425			sprintf(buf, "%s", addr_to_anyname(addr));
426		strcat(buf, mask_to_dotted(mask));
427		printf("%s ", buf);
428	}
429}
430
431/* Saves the matchinfo in parsable form to stdout. */
432static void
433matchinfo_print(const struct ipt_ip *ip, const struct ipt_entry_match *match, int numeric, const char *optpfx)
434{
435	struct ipt_conntrack_info *sinfo = (struct ipt_conntrack_info *)match->data;
436
437	if(sinfo->flags & IPT_CONNTRACK_STATE) {
438		printf("%sctstate ", optpfx);
439        	if (sinfo->invflags & IPT_CONNTRACK_STATE)
440                	printf("! ");
441		print_state(sinfo->statemask);
442	}
443
444	if(sinfo->flags & IPT_CONNTRACK_PROTO) {
445		printf("%sctproto ", optpfx);
446        	if (sinfo->invflags & IPT_CONNTRACK_PROTO)
447                	printf("! ");
448		printf("%u ", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum);
449	}
450
451	if(sinfo->flags & IPT_CONNTRACK_ORIGSRC) {
452		printf("%sctorigsrc ", optpfx);
453
454		print_addr(
455		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
456		    &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
457		    sinfo->invflags & IPT_CONNTRACK_ORIGSRC,
458		    numeric);
459	}
460
461	if(sinfo->flags & IPT_CONNTRACK_ORIGDST) {
462		printf("%sctorigdst ", optpfx);
463
464		print_addr(
465		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
466		    &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
467		    sinfo->invflags & IPT_CONNTRACK_ORIGDST,
468		    numeric);
469	}
470
471	if(sinfo->flags & IPT_CONNTRACK_REPLSRC) {
472		printf("%sctreplsrc ", optpfx);
473
474		print_addr(
475		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
476		    &sinfo->sipmsk[IP_CT_DIR_REPLY],
477		    sinfo->invflags & IPT_CONNTRACK_REPLSRC,
478		    numeric);
479	}
480
481	if(sinfo->flags & IPT_CONNTRACK_REPLDST) {
482		printf("%sctrepldst ", optpfx);
483
484		print_addr(
485		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
486		    &sinfo->dipmsk[IP_CT_DIR_REPLY],
487		    sinfo->invflags & IPT_CONNTRACK_REPLDST,
488		    numeric);
489	}
490
491	if(sinfo->flags & IPT_CONNTRACK_STATUS) {
492		printf("%sctstatus ", optpfx);
493        	if (sinfo->invflags & IPT_CONNTRACK_STATUS)
494                	printf("! ");
495		print_status(sinfo->statusmask);
496	}
497
498	if(sinfo->flags & IPT_CONNTRACK_EXPIRES) {
499		printf("%sctexpire ", optpfx);
500        	if (sinfo->invflags & IPT_CONNTRACK_EXPIRES)
501                	printf("! ");
502
503#ifdef KERNEL_64_USERSPACE_32
504        	if (sinfo->expires_max == sinfo->expires_min)
505                	printf("%llu ", sinfo->expires_min);
506        	else
507                	printf("%llu:%llu ", sinfo->expires_min, sinfo->expires_max);
508#else
509        	if (sinfo->expires_max == sinfo->expires_min)
510                	printf("%lu ", sinfo->expires_min);
511        	else
512                	printf("%lu:%lu ", sinfo->expires_min, sinfo->expires_max);
513#endif
514	}
515}
516
517/* Prints out the matchinfo. */
518static void
519print(const struct ipt_ip *ip,
520      const struct ipt_entry_match *match,
521      int numeric)
522{
523	matchinfo_print(ip, match, numeric, "");
524}
525
526/* Saves the matchinfo in parsable form to stdout. */
527static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
528{
529	matchinfo_print(ip, match, 1, "--");
530}
531
532static struct iptables_match conntrack = {
533	.next 		= NULL,
534	.name		= "conntrack",
535	.version	= IPTABLES_VERSION,
536	.size		= IPT_ALIGN(sizeof(struct ipt_conntrack_info)),
537	.userspacesize	= IPT_ALIGN(sizeof(struct ipt_conntrack_info)),
538	.help		= &help,
539	.parse		= &parse,
540	.final_check	= &final_check,
541	.print		= &print,
542	.save		= &save,
543	.extra_opts	= opts
544};
545
546void _init(void)
547{
548	register_match(&conntrack);
549}
550