1/* Shared library add-on to iptables to add byte tracking support. */
2#include <stdio.h>
3#include <netdb.h>
4#include <string.h>
5#include <stdlib.h>
6#include <getopt.h>
7#include <iptables.h>
8#include <linux/netfilter/nf_conntrack_common.h>
9#include <linux/netfilter_ipv4/ipt_connbytes.h>
10
11/* Function which prints out usage message. */
12static void
13help(void)
14{
15	printf(
16"connbytes v%s options:\n"
17" [!] --connbytes from:[to]\n"
18"     --connbytes-dir [original, reply, both]\n"
19"     --connbytes-mode [packets, bytes, avgpkt]\n"
20"\n", IPTABLES_VERSION);
21}
22
23static struct option opts[] = {
24	{ "connbytes", 1, 0, '1' },
25	{ "connbytes-dir", 1, 0, '2' },
26	{ "connbytes-mode", 1, 0, '3' },
27	{0}
28};
29
30static void
31parse_range(const char *arg, struct ipt_connbytes_info *si)
32{
33	char *colon,*p;
34
35	si->count.from = strtoul(arg,&colon,10);
36	if (*colon != ':')
37		exit_error(PARAMETER_PROBLEM, "Bad range `%s'", arg);
38	si->count.to = strtoul(colon+1,&p,10);
39	if (p == colon+1) {
40		/* second number omited */
41		si->count.to = 0xffffffff;
42	}
43	if (si->count.from > si->count.to)
44		exit_error(PARAMETER_PROBLEM, "%llu should be less than %llu",
45			   si->count.from, si->count.to);
46}
47
48/* Function which parses command options; returns true if it
49   ate an option */
50static int
51parse(int c, char **argv, int invert, unsigned int *flags,
52      const struct ipt_entry *entry,
53      unsigned int *nfcache,
54      struct ipt_entry_match **match)
55{
56	struct ipt_connbytes_info *sinfo = (struct ipt_connbytes_info *)(*match)->data;
57	unsigned long i;
58
59	switch (c) {
60	case '1':
61		if (check_inverse(optarg, &invert, &optind, 0))
62			optind++;
63
64		parse_range(argv[optind-1], sinfo);
65		if (invert) {
66			i = sinfo->count.from;
67			sinfo->count.from = sinfo->count.to;
68			sinfo->count.to = i;
69		}
70		*flags |= 1;
71		break;
72	case '2':
73		if (!strcmp(optarg, "original"))
74			sinfo->direction = IPT_CONNBYTES_DIR_ORIGINAL;
75		else if (!strcmp(optarg, "reply"))
76			sinfo->direction = IPT_CONNBYTES_DIR_REPLY;
77		else if (!strcmp(optarg, "both"))
78			sinfo->direction = IPT_CONNBYTES_DIR_BOTH;
79		else
80			exit_error(PARAMETER_PROBLEM,
81				   "Unknown --connbytes-dir `%s'", optarg);
82
83		*flags |= 2;
84		break;
85	case '3':
86		if (!strcmp(optarg, "packets"))
87			sinfo->what = IPT_CONNBYTES_PKTS;
88		else if (!strcmp(optarg, "bytes"))
89			sinfo->what = IPT_CONNBYTES_BYTES;
90		else if (!strcmp(optarg, "avgpkt"))
91			sinfo->what = IPT_CONNBYTES_AVGPKT;
92		else
93			exit_error(PARAMETER_PROBLEM,
94				   "Unknown --connbytes-mode `%s'", optarg);
95		*flags |= 4;
96		break;
97	default:
98		return 0;
99	}
100
101	return 1;
102}
103
104static void final_check(unsigned int flags)
105{
106	if (flags != 7)
107		exit_error(PARAMETER_PROBLEM, "You must specify `--connbytes'"
108			   "`--connbytes-dir' and `--connbytes-mode'");
109}
110
111static void print_mode(struct ipt_connbytes_info *sinfo)
112{
113	switch (sinfo->what) {
114		case IPT_CONNBYTES_PKTS:
115			fputs("packets ", stdout);
116			break;
117		case IPT_CONNBYTES_BYTES:
118			fputs("bytes ", stdout);
119			break;
120		case IPT_CONNBYTES_AVGPKT:
121			fputs("avgpkt ", stdout);
122			break;
123		default:
124			fputs("unknown ", stdout);
125			break;
126	}
127}
128
129static void print_direction(struct ipt_connbytes_info *sinfo)
130{
131	switch (sinfo->direction) {
132		case IPT_CONNBYTES_DIR_ORIGINAL:
133			fputs("original ", stdout);
134			break;
135		case IPT_CONNBYTES_DIR_REPLY:
136			fputs("reply ", stdout);
137			break;
138		case IPT_CONNBYTES_DIR_BOTH:
139			fputs("both ", stdout);
140			break;
141		default:
142			fputs("unknown ", stdout);
143			break;
144	}
145}
146
147/* Prints out the matchinfo. */
148static void
149print(const struct ipt_ip *ip,
150      const struct ipt_entry_match *match,
151      int numeric)
152{
153	struct ipt_connbytes_info *sinfo = (struct ipt_connbytes_info *)match->data;
154
155	if (sinfo->count.from > sinfo->count.to)
156		printf("connbytes ! %llu:%llu ", sinfo->count.to,
157			sinfo->count.from);
158	else
159		printf("connbytes %llu:%llu ",sinfo->count.from,
160			sinfo->count.to);
161
162	fputs("connbytes mode ", stdout);
163	print_mode(sinfo);
164
165	fputs("connbytes direction ", stdout);
166	print_direction(sinfo);
167}
168
169/* Saves the matchinfo in parsable form to stdout. */
170static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
171{
172	struct ipt_connbytes_info *sinfo = (struct ipt_connbytes_info *)match->data;
173
174	if (sinfo->count.from > sinfo->count.to)
175		printf("! --connbytes %llu:%llu ", sinfo->count.to,
176			sinfo->count.from);
177	else
178		printf("--connbytes %llu:%llu ", sinfo->count.from,
179			sinfo->count.to);
180
181	fputs("--connbytes-mode ", stdout);
182	print_mode(sinfo);
183
184	fputs("--connbytes-dir ", stdout);
185	print_direction(sinfo);
186}
187
188static struct iptables_match state = {
189	.next 		= NULL,
190	.name 		= "connbytes",
191	.version 	= IPTABLES_VERSION,
192	.size 		= IPT_ALIGN(sizeof(struct ipt_connbytes_info)),
193	.userspacesize	= IPT_ALIGN(sizeof(struct ipt_connbytes_info)),
194	.help		= &help,
195	.parse		= &parse,
196	.final_check	= &final_check,
197	.print		= &print,
198	.save 		= &save,
199	.extra_opts	= opts
200};
201
202void _init(void)
203{
204	register_match(&state);
205}
206