libxt_RATEEST.c revision 42979363f3958b4436c6d2503753c182c58e55ea
1#include <stdio.h>
2#include <string.h>
3#include <stdlib.h>
4#include <stddef.h>
5#include <getopt.h>
6#include <math.h>
7
8#include <xtables.h>
9#include <linux/netfilter/x_tables.h>
10#include <linux/netfilter/xt_RATEEST.h>
11
12/* hack to pass raw values to final_check */
13static struct xt_rateest_target_info *RATEEST_info;
14static unsigned int interval;
15static unsigned int ewma_log;
16
17static void
18RATEEST_help(void)
19{
20	printf(
21"RATEEST target options:\n"
22"  --rateest-name name		Rate estimator name\n"
23"  --rateest-interval sec	Rate measurement interval in seconds\n"
24"  --rateest-ewmalog value	Rate measurement averaging time constant\n");
25}
26
27enum RATEEST_options {
28	RATEEST_OPT_NAME,
29	RATEEST_OPT_INTERVAL,
30	RATEEST_OPT_EWMALOG,
31};
32
33static const struct option RATEEST_opts[] = {
34	{ "rateest-name",	1, NULL, RATEEST_OPT_NAME },
35	{ "rateest-interval",	1, NULL, RATEEST_OPT_INTERVAL },
36	{ "rateest-ewmalog",	1, NULL, RATEEST_OPT_EWMALOG },
37	{ .name = NULL },
38};
39
40/* Copied from iproute */
41#define TIME_UNITS_PER_SEC	1000000
42
43static int
44RATEEST_get_time(unsigned int *time, const char *str)
45{
46	double t;
47	char *p;
48
49	t = strtod(str, &p);
50	if (p == str)
51		return -1;
52
53	if (*p) {
54		if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec")==0 ||
55		    strcasecmp(p, "secs")==0)
56			t *= TIME_UNITS_PER_SEC;
57		else if (strcasecmp(p, "ms") == 0 || strcasecmp(p, "msec")==0 ||
58			 strcasecmp(p, "msecs") == 0)
59			t *= TIME_UNITS_PER_SEC/1000;
60		else if (strcasecmp(p, "us") == 0 || strcasecmp(p, "usec")==0 ||
61			 strcasecmp(p, "usecs") == 0)
62			t *= TIME_UNITS_PER_SEC/1000000;
63		else
64			return -1;
65	}
66
67	*time = t;
68	return 0;
69}
70
71static void
72RATEEST_print_time(unsigned int time)
73{
74	double tmp = time;
75
76	if (tmp >= TIME_UNITS_PER_SEC)
77		printf("%.1fs ", tmp/TIME_UNITS_PER_SEC);
78	else if (tmp >= TIME_UNITS_PER_SEC/1000)
79		printf("%.1fms ", tmp/(TIME_UNITS_PER_SEC/1000));
80	else
81		printf("%uus ", time);
82}
83
84static void
85RATEEST_init(struct xt_entry_target *target)
86{
87	interval = 0;
88	ewma_log = 0;
89}
90
91static int
92RATEEST_parse(int c, char **argv, int invert, unsigned int *flags,
93	      const void *entry, struct xt_entry_target **target)
94{
95	struct xt_rateest_target_info *info = (void *)(*target)->data;
96
97	RATEEST_info = info;
98
99	switch (c) {
100	case RATEEST_OPT_NAME:
101		if (*flags & (1 << c))
102			xtables_error(PARAMETER_PROBLEM,
103				   "RATEEST: can't specify --rateest-name twice");
104		*flags |= 1 << c;
105
106		strncpy(info->name, optarg, sizeof(info->name) - 1);
107		break;
108
109	case RATEEST_OPT_INTERVAL:
110		if (*flags & (1 << c))
111			xtables_error(PARAMETER_PROBLEM,
112				   "RATEEST: can't specify --rateest-interval twice");
113		*flags |= 1 << c;
114
115		if (RATEEST_get_time(&interval, optarg) < 0)
116			xtables_error(PARAMETER_PROBLEM,
117				   "RATEEST: bad interval value `%s'", optarg);
118
119		break;
120
121	case RATEEST_OPT_EWMALOG:
122		if (*flags & (1 << c))
123			xtables_error(PARAMETER_PROBLEM,
124				   "RATEEST: can't specify --rateest-ewmalog twice");
125		*flags |= 1 << c;
126
127		if (RATEEST_get_time(&ewma_log, optarg) < 0)
128			xtables_error(PARAMETER_PROBLEM,
129				   "RATEEST: bad ewmalog value `%s'", optarg);
130
131		break;
132
133	default:
134		return 0;
135	}
136
137	return 1;
138}
139
140static void
141RATEEST_final_check(unsigned int flags)
142{
143	struct xt_rateest_target_info *info = RATEEST_info;
144
145	if (!(flags & (1 << RATEEST_OPT_NAME)))
146		xtables_error(PARAMETER_PROBLEM, "RATEEST: no name specified");
147	if (!(flags & (1 << RATEEST_OPT_INTERVAL)))
148		xtables_error(PARAMETER_PROBLEM, "RATEEST: no interval specified");
149	if (!(flags & (1 << RATEEST_OPT_EWMALOG)))
150		xtables_error(PARAMETER_PROBLEM, "RATEEST: no ewmalog specified");
151
152	for (info->interval = 0; info->interval <= 5; info->interval++) {
153		if (interval <= (1 << info->interval) * (TIME_UNITS_PER_SEC / 4))
154			break;
155	}
156
157	if (info->interval > 5)
158		xtables_error(PARAMETER_PROBLEM,
159			   "RATEEST: interval value is too large");
160	info->interval -= 2;
161
162	for (info->ewma_log = 1; info->ewma_log < 32; info->ewma_log++) {
163		double w = 1.0 - 1.0 / (1 << info->ewma_log);
164		if (interval / (-log(w)) > ewma_log)
165			break;
166	}
167	info->ewma_log--;
168
169	if (info->ewma_log == 0 || info->ewma_log >= 31)
170		xtables_error(PARAMETER_PROBLEM,
171			   "RATEEST: ewmalog value is out of range");
172}
173
174static void
175__RATEEST_print(const struct xt_entry_target *target, const char *prefix)
176{
177	const struct xt_rateest_target_info *info = (const void *)target->data;
178	unsigned int local_interval;
179	unsigned int local_ewma_log;
180
181	local_interval = (TIME_UNITS_PER_SEC << (info->interval + 2)) / 4;
182	local_ewma_log = local_interval * (1 << (info->ewma_log));
183
184	printf("%sname %s ", prefix, info->name);
185	printf("%sinterval ", prefix);
186	RATEEST_print_time(local_interval);
187	printf("%sewmalog ", prefix);
188	RATEEST_print_time(local_ewma_log);
189}
190
191static void
192RATEEST_print(const void *ip, const struct xt_entry_target *target,
193	      int numeric)
194{
195	__RATEEST_print(target, "");
196}
197
198static void
199RATEEST_save(const void *ip, const struct xt_entry_target *target)
200{
201	__RATEEST_print(target, "--rateest-");
202}
203
204static struct xtables_target rateest_tg_reg = {
205	.family		= NFPROTO_UNSPEC,
206	.name		= "RATEEST",
207	.version	= XTABLES_VERSION,
208	.size		= XT_ALIGN(sizeof(struct xt_rateest_target_info)),
209	.userspacesize	= XT_ALIGN(sizeof(struct xt_rateest_target_info)),
210	.help		= RATEEST_help,
211	.init		= RATEEST_init,
212	.parse		= RATEEST_parse,
213	.final_check	= RATEEST_final_check,
214	.print		= RATEEST_print,
215	.save		= RATEEST_save,
216	.extra_opts	= RATEEST_opts,
217};
218
219void _init(void)
220{
221	xtables_register_target(&rateest_tg_reg);
222}
223