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 int
86RATEEST_parse(int c, char **argv, int invert, unsigned int *flags,
87	      const void *entry, struct xt_entry_target **target)
88{
89	struct xt_rateest_target_info *info = (void *)(*target)->data;
90
91	RATEEST_info = info;
92
93	switch (c) {
94	case RATEEST_OPT_NAME:
95		if (*flags & (1 << c))
96			xtables_error(PARAMETER_PROBLEM,
97				   "RATEEST: can't specify --rateest-name twice");
98		*flags |= 1 << c;
99
100		strncpy(info->name, optarg, sizeof(info->name) - 1);
101		break;
102
103	case RATEEST_OPT_INTERVAL:
104		if (*flags & (1 << c))
105			xtables_error(PARAMETER_PROBLEM,
106				   "RATEEST: can't specify --rateest-interval twice");
107		*flags |= 1 << c;
108
109		if (RATEEST_get_time(&interval, optarg) < 0)
110			xtables_error(PARAMETER_PROBLEM,
111				   "RATEEST: bad interval value `%s'", optarg);
112
113		break;
114
115	case RATEEST_OPT_EWMALOG:
116		if (*flags & (1 << c))
117			xtables_error(PARAMETER_PROBLEM,
118				   "RATEEST: can't specify --rateest-ewmalog twice");
119		*flags |= 1 << c;
120
121		if (RATEEST_get_time(&ewma_log, optarg) < 0)
122			xtables_error(PARAMETER_PROBLEM,
123				   "RATEEST: bad ewmalog value `%s'", optarg);
124
125		break;
126	}
127
128	return 1;
129}
130
131static void
132RATEEST_final_check(unsigned int flags)
133{
134	struct xt_rateest_target_info *info = RATEEST_info;
135
136	if (!(flags & (1 << RATEEST_OPT_NAME)))
137		xtables_error(PARAMETER_PROBLEM, "RATEEST: no name specified");
138	if (!(flags & (1 << RATEEST_OPT_INTERVAL)))
139		xtables_error(PARAMETER_PROBLEM, "RATEEST: no interval specified");
140	if (!(flags & (1 << RATEEST_OPT_EWMALOG)))
141		xtables_error(PARAMETER_PROBLEM, "RATEEST: no ewmalog specified");
142
143	for (info->interval = 0; info->interval <= 5; info->interval++) {
144		if (interval <= (1 << info->interval) * (TIME_UNITS_PER_SEC / 4))
145			break;
146	}
147
148	if (info->interval > 5)
149		xtables_error(PARAMETER_PROBLEM,
150			   "RATEEST: interval value is too large");
151	info->interval -= 2;
152
153	for (info->ewma_log = 1; info->ewma_log < 32; info->ewma_log++) {
154		double w = 1.0 - 1.0 / (1 << info->ewma_log);
155		if (interval / (-log(w)) > ewma_log)
156			break;
157	}
158	info->ewma_log--;
159
160	if (info->ewma_log == 0 || info->ewma_log >= 31)
161		xtables_error(PARAMETER_PROBLEM,
162			   "RATEEST: ewmalog value is out of range");
163}
164
165static void
166__RATEEST_print(const struct xt_entry_target *target, const char *prefix)
167{
168	const struct xt_rateest_target_info *info = (const void *)target->data;
169	unsigned int local_interval;
170	unsigned int local_ewma_log;
171
172	local_interval = (TIME_UNITS_PER_SEC << (info->interval + 2)) / 4;
173	local_ewma_log = local_interval * (1 << (info->ewma_log));
174
175	printf(" %sname %s", prefix, info->name);
176	printf(" %sinterval", prefix);
177	RATEEST_print_time(local_interval);
178	printf(" %sewmalog", prefix);
179	RATEEST_print_time(local_ewma_log);
180}
181
182static void
183RATEEST_print(const void *ip, const struct xt_entry_target *target,
184	      int numeric)
185{
186	__RATEEST_print(target, "");
187}
188
189static void
190RATEEST_save(const void *ip, const struct xt_entry_target *target)
191{
192	__RATEEST_print(target, "--rateest-");
193}
194
195static struct xtables_target rateest_tg_reg = {
196	.family		= NFPROTO_UNSPEC,
197	.name		= "RATEEST",
198	.version	= XTABLES_VERSION,
199	.size		= XT_ALIGN(sizeof(struct xt_rateest_target_info)),
200	.userspacesize	= XT_ALIGN(sizeof(struct xt_rateest_target_info)),
201	.help		= RATEEST_help,
202	.parse		= RATEEST_parse,
203	.final_check	= RATEEST_final_check,
204	.print		= RATEEST_print,
205	.save		= RATEEST_save,
206	.extra_opts	= RATEEST_opts,
207};
208
209void _init(void)
210{
211	xtables_register_target(&rateest_tg_reg);
212}
213