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