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