libxt_rateest.c revision e37d45ce390c2f5a7f1e64742b9100ecef0def54
1#include <stdbool.h>
2#include <stdio.h>
3#include <string.h>
4#include <stdlib.h>
5#include <stddef.h>
6#include <getopt.h>
7
8#include <xtables.h>
9#include <linux/netfilter/xt_rateest.h>
10
11/* Ugly hack to pass info to final_check function. We should fix the API */
12static struct xt_rateest_match_info *rateest_info;
13
14static void rateest_help(void)
15{
16	printf(
17"rateest match options:\n"
18" --rateest1 name		Rate estimator name\n"
19" --rateest2 name		Rate estimator name\n"
20" --rateest-delta		Compare difference(s) to given rate(s)\n"
21" --rateest-bps1 [bps]		Compare bps\n"
22" --rateest-pps1 [pps]		Compare pps\n"
23" --rateest-bps2 [bps]		Compare bps\n"
24" --rateest-pps2 [pps]		Compare pps\n"
25" [!] --rateest-lt		Match if rate is less than given rate/estimator\n"
26" [!] --rateest-gt		Match if rate is greater than given rate/estimator\n"
27" [!] --rateest-eq		Match if rate is equal to given rate/estimator\n");
28}
29
30enum rateest_options {
31	OPT_RATEEST1,
32	OPT_RATEEST2,
33	OPT_RATEEST_BPS1,
34	OPT_RATEEST_PPS1,
35	OPT_RATEEST_BPS2,
36	OPT_RATEEST_PPS2,
37	OPT_RATEEST_DELTA,
38	OPT_RATEEST_LT,
39	OPT_RATEEST_GT,
40	OPT_RATEEST_EQ,
41};
42
43static const struct option rateest_opts[] = {
44	{.name = "rateest1",      .has_arg = true,  .val = OPT_RATEEST1},
45	{.name = "rateest",       .has_arg = true,  .val = OPT_RATEEST1}, /* alias for absolute mode */
46	{.name = "rateest2",      .has_arg = true,  .val = OPT_RATEEST2},
47	{.name = "rateest-bps1",  .has_arg = false, .val = OPT_RATEEST_BPS1},
48	{.name = "rateest-pps1",  .has_arg = false, .val = OPT_RATEEST_PPS1},
49	{.name = "rateest-bps2",  .has_arg = false, .val = OPT_RATEEST_BPS2},
50	{.name = "rateest-pps2",  .has_arg = false, .val = OPT_RATEEST_PPS2},
51	{.name = "rateest-bps",   .has_arg = false, .val = OPT_RATEEST_BPS2}, /* alias for absolute mode */
52	{.name = "rateest-pps",   .has_arg = false, .val = OPT_RATEEST_PPS2}, /* alias for absolute mode */
53	{.name = "rateest-delta", .has_arg = false, .val = OPT_RATEEST_DELTA},
54	{.name = "rateest-lt",    .has_arg = false, .val = OPT_RATEEST_LT},
55	{.name = "rateest-gt",    .has_arg = false, .val = OPT_RATEEST_GT},
56	{.name = "rateest-eq",    .has_arg = false, .val = OPT_RATEEST_EQ},
57	XT_GETOPT_TABLEEND,
58};
59
60/* Copied from iproute. See http://physics.nist.gov/cuu/Units/binary.html */
61static const struct rate_suffix {
62	const char *name;
63	double scale;
64} suffixes[] = {
65	{ "bit",	1. },
66	{ "Kibit",	1024. },
67	{ "kbit",	1000. },
68	{ "Mibit",	1024.*1024. },
69	{ "mbit",	1000000. },
70	{ "Gibit",	1024.*1024.*1024. },
71	{ "gbit",	1000000000. },
72	{ "Tibit",	1024.*1024.*1024.*1024. },
73	{ "tbit",	1000000000000. },
74	{ "Bps",	8. },
75	{ "KiBps",	8.*1024. },
76	{ "KBps",	8000. },
77	{ "MiBps",	8.*1024*1024. },
78	{ "MBps",	8000000. },
79	{ "GiBps",	8.*1024.*1024.*1024. },
80	{ "GBps",	8000000000. },
81	{ "TiBps",	8.*1024.*1024.*1024.*1024. },
82	{ "TBps",	8000000000000. },
83	{NULL},
84};
85
86static int
87rateest_get_rate(uint32_t *rate, const char *str)
88{
89	char *p;
90	double bps = strtod(str, &p);
91	const struct rate_suffix *s;
92
93	if (p == str)
94		return -1;
95
96	if (*p == '\0') {
97		*rate = bps / 8.;	/* assume bytes/sec */
98		return 0;
99	}
100
101	for (s = suffixes; s->name; ++s) {
102		if (strcasecmp(s->name, p) == 0) {
103			*rate = (bps * s->scale) / 8.;
104			return 0;
105		}
106	}
107
108	return -1;
109}
110
111static int
112rateest_parse(int c, char **argv, int invert, unsigned int *flags,
113	      const void *entry, struct xt_entry_match **match)
114{
115	struct xt_rateest_match_info *info = (void *)(*match)->data;
116	unsigned int val;
117
118	rateest_info = info;
119
120	switch (c) {
121	case OPT_RATEEST1:
122		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
123		if (invert)
124			xtables_error(PARAMETER_PROBLEM,
125				   "rateest: rateest can't be inverted");
126
127		if (*flags & (1 << c))
128			xtables_error(PARAMETER_PROBLEM,
129				   "rateest: can't specify --rateest1 twice");
130		*flags |= 1 << c;
131
132		strncpy(info->name1, optarg, sizeof(info->name1) - 1);
133		break;
134
135	case OPT_RATEEST2:
136		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
137		if (invert)
138			xtables_error(PARAMETER_PROBLEM,
139				   "rateest: rateest can't be inverted");
140
141		if (*flags & (1 << c))
142			xtables_error(PARAMETER_PROBLEM,
143				   "rateest: can't specify --rateest2 twice");
144		*flags |= 1 << c;
145
146		strncpy(info->name2, optarg, sizeof(info->name2) - 1);
147		info->flags |= XT_RATEEST_MATCH_REL;
148		break;
149
150	case OPT_RATEEST_BPS1:
151		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
152		if (invert)
153			xtables_error(PARAMETER_PROBLEM,
154				   "rateest: rateest-bps can't be inverted");
155
156		if (*flags & (1 << c))
157			xtables_error(PARAMETER_PROBLEM,
158				   "rateest: can't specify --rateest-bps1 twice");
159		*flags |= 1 << c;
160
161		info->flags |= XT_RATEEST_MATCH_BPS;
162
163		/* The rate is optional and only required in absolute mode */
164		if (!argv[optind] || *argv[optind] == '-' || *argv[optind] == '!')
165			break;
166
167		if (rateest_get_rate(&info->bps1, argv[optind]) < 0)
168			xtables_error(PARAMETER_PROBLEM,
169				   "rateest: could not parse rate `%s'",
170				   argv[optind]);
171		optind++;
172		break;
173
174	case OPT_RATEEST_PPS1:
175		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
176		if (invert)
177			xtables_error(PARAMETER_PROBLEM,
178				   "rateest: rateest-pps can't be inverted");
179
180		if (*flags & (1 << c))
181			xtables_error(PARAMETER_PROBLEM,
182				   "rateest: can't specify --rateest-pps1 twice");
183		*flags |= 1 << c;
184
185		info->flags |= XT_RATEEST_MATCH_PPS;
186
187		/* The rate is optional and only required in absolute mode */
188		if (!argv[optind] || *argv[optind] == '-' || *argv[optind] == '!')
189			break;
190
191		if (!xtables_strtoui(argv[optind], NULL, &val, 0, UINT32_MAX))
192			xtables_error(PARAMETER_PROBLEM,
193				   "rateest: could not parse pps `%s'",
194				   argv[optind]);
195		info->pps1 = val;
196		optind++;
197		break;
198
199	case OPT_RATEEST_BPS2:
200		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
201		if (invert)
202			xtables_error(PARAMETER_PROBLEM,
203				   "rateest: rateest-bps can't be inverted");
204
205		if (*flags & (1 << c))
206			xtables_error(PARAMETER_PROBLEM,
207				   "rateest: can't specify --rateest-bps2 twice");
208		*flags |= 1 << c;
209
210		info->flags |= XT_RATEEST_MATCH_BPS;
211
212		/* The rate is optional and only required in absolute mode */
213		if (!argv[optind] || *argv[optind] == '-' || *argv[optind] == '!')
214			break;
215
216		if (rateest_get_rate(&info->bps2, argv[optind]) < 0)
217			xtables_error(PARAMETER_PROBLEM,
218				   "rateest: could not parse rate `%s'",
219				   argv[optind]);
220		optind++;
221		break;
222
223	case OPT_RATEEST_PPS2:
224		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
225		if (invert)
226			xtables_error(PARAMETER_PROBLEM,
227				   "rateest: rateest-pps can't be inverted");
228
229		if (*flags & (1 << c))
230			xtables_error(PARAMETER_PROBLEM,
231				   "rateest: can't specify --rateest-pps2 twice");
232		*flags |= 1 << c;
233
234		info->flags |= XT_RATEEST_MATCH_PPS;
235
236		/* The rate is optional and only required in absolute mode */
237		if (!argv[optind] || *argv[optind] == '-' || *argv[optind] == '!')
238			break;
239
240		if (!xtables_strtoui(argv[optind], NULL, &val, 0, UINT32_MAX))
241			xtables_error(PARAMETER_PROBLEM,
242				   "rateest: could not parse pps `%s'",
243				   argv[optind]);
244		info->pps2 = val;
245		optind++;
246		break;
247
248	case OPT_RATEEST_DELTA:
249		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
250		if (invert)
251			xtables_error(PARAMETER_PROBLEM,
252				   "rateest: rateest-delta can't be inverted");
253
254		if (*flags & (1 << c))
255			xtables_error(PARAMETER_PROBLEM,
256				   "rateest: can't specify --rateest-delta twice");
257		*flags |= 1 << c;
258
259		info->flags |= XT_RATEEST_MATCH_DELTA;
260		break;
261
262	case OPT_RATEEST_EQ:
263		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
264
265		if (*flags & (1 << c))
266			xtables_error(PARAMETER_PROBLEM,
267				   "rateest: can't specify lt/gt/eq twice");
268		*flags |= 1 << c;
269
270		info->mode = XT_RATEEST_MATCH_EQ;
271		if (invert)
272			info->flags |= XT_RATEEST_MATCH_INVERT;
273		break;
274
275	case OPT_RATEEST_LT:
276		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
277
278		if (*flags & (1 << c))
279			xtables_error(PARAMETER_PROBLEM,
280				   "rateest: can't specify lt/gt/eq twice");
281		*flags |= 1 << c;
282
283		info->mode = XT_RATEEST_MATCH_LT;
284		if (invert)
285			info->flags |= XT_RATEEST_MATCH_INVERT;
286		break;
287
288	case OPT_RATEEST_GT:
289		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
290
291		if (*flags & (1 << c))
292			xtables_error(PARAMETER_PROBLEM,
293				   "rateest: can't specify lt/gt/eq twice");
294		*flags |= 1 << c;
295
296		info->mode = XT_RATEEST_MATCH_GT;
297		if (invert)
298			info->flags |= XT_RATEEST_MATCH_INVERT;
299		break;
300	}
301
302	return 1;
303}
304
305static void
306rateest_final_check(unsigned int flags)
307{
308	struct xt_rateest_match_info *info = rateest_info;
309
310	if (info == NULL)
311		xtables_error(PARAMETER_PROBLEM, "rateest match: "
312		           "you need to specify some flags");
313	if (!(info->flags & XT_RATEEST_MATCH_REL))
314		info->flags |= XT_RATEEST_MATCH_ABS;
315}
316
317static void
318rateest_print_rate(uint32_t rate, int numeric)
319{
320	double tmp = (double)rate*8;
321
322	if (numeric)
323		printf(" %u", rate);
324	else if (tmp >= 1000.0*1000000.0)
325		printf(" %.0fMbit", tmp/1000000.0);
326	else if (tmp >= 1000.0 * 1000.0)
327		printf(" %.0fKbit", tmp/1000.0);
328	else
329		printf(" %.0fbit", tmp);
330}
331
332static void
333rateest_print_mode(const struct xt_rateest_match_info *info,
334                   const char *prefix)
335{
336	if (info->flags & XT_RATEEST_MATCH_INVERT)
337		printf(" !");
338
339	switch (info->mode) {
340	case XT_RATEEST_MATCH_EQ:
341		printf(" %seq", prefix);
342		break;
343	case XT_RATEEST_MATCH_LT:
344		printf(" %slt", prefix);
345		break;
346	case XT_RATEEST_MATCH_GT:
347		printf(" %sgt", prefix);
348		break;
349	default:
350		exit(1);
351	}
352}
353
354static void
355rateest_print(const void *ip, const struct xt_entry_match *match, int numeric)
356{
357	const struct xt_rateest_match_info *info = (const void *)match->data;
358
359	printf(" rateest match ");
360
361	printf("%s", info->name1);
362	if (info->flags & XT_RATEEST_MATCH_DELTA)
363		printf(" delta");
364
365	if (info->flags & XT_RATEEST_MATCH_BPS) {
366		printf(" bps");
367		if (info->flags & XT_RATEEST_MATCH_DELTA)
368			rateest_print_rate(info->bps1, numeric);
369		if (info->flags & XT_RATEEST_MATCH_ABS) {
370			rateest_print_mode(info, "");
371			rateest_print_rate(info->bps2, numeric);
372		}
373	}
374	if (info->flags & XT_RATEEST_MATCH_PPS) {
375		printf(" pps");
376		if (info->flags & XT_RATEEST_MATCH_DELTA)
377			printf(" %u", info->pps1);
378		if (info->flags & XT_RATEEST_MATCH_ABS) {
379			rateest_print_mode(info, "");
380			printf(" %u", info->pps2);
381		}
382	}
383
384	if (info->flags & XT_RATEEST_MATCH_REL) {
385		rateest_print_mode(info, "");
386
387		printf(" %s", info->name2);
388		if (info->flags & XT_RATEEST_MATCH_DELTA)
389			printf(" delta");
390
391		if (info->flags & XT_RATEEST_MATCH_BPS) {
392			printf(" bps");
393			if (info->flags & XT_RATEEST_MATCH_DELTA)
394				rateest_print_rate(info->bps2, numeric);
395		}
396		if (info->flags & XT_RATEEST_MATCH_PPS) {
397			printf(" pps");
398			if (info->flags & XT_RATEEST_MATCH_DELTA)
399				printf(" %u", info->pps2);
400		}
401	}
402}
403
404static void
405rateest_save(const void *ip, const struct xt_entry_match *match)
406{
407	const struct xt_rateest_match_info *info = (const void *)match->data;
408
409	if (info->flags & XT_RATEEST_MATCH_REL) {
410		printf(" --rateest1 %s", info->name1);
411		if (info->flags & XT_RATEEST_MATCH_BPS)
412			printf(" --rateest-bps");
413		if (info->flags & XT_RATEEST_MATCH_PPS)
414			printf(" --rateest-pps");
415		rateest_print_mode(info, " --rateest-");
416		printf(" --rateest2 %s", info->name2);
417	} else {
418		printf(" --rateest %s", info->name1);
419		if (info->flags & XT_RATEEST_MATCH_BPS) {
420			printf(" --rateest-bps1");
421			rateest_print_rate(info->bps1, 0);
422			printf(" --rateest-bps2");
423			rateest_print_rate(info->bps2, 0);
424			rateest_print_mode(info, "--rateest-");
425		}
426		if (info->flags & XT_RATEEST_MATCH_PPS) {
427			printf(" --rateest-pps");
428			rateest_print_mode(info, "--rateest-");
429			printf(" %u", info->pps2);
430		}
431	}
432}
433
434static struct xtables_match rateest_mt_reg = {
435	.family		= NFPROTO_UNSPEC,
436	.name		= "rateest",
437	.version	= XTABLES_VERSION,
438	.size		= XT_ALIGN(sizeof(struct xt_rateest_match_info)),
439	.userspacesize	= XT_ALIGN(offsetof(struct xt_rateest_match_info, est1)),
440	.help		= rateest_help,
441	.parse		= rateest_parse,
442	.final_check	= rateest_final_check,
443	.print		= rateest_print,
444	.save		= rateest_save,
445	.extra_opts	= rateest_opts,
446};
447
448void _init(void)
449{
450	xtables_register_match(&rateest_mt_reg);
451}
452