#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <getopt.h>
#include <math.h>
#include <xtables.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/xt_RATEEST.h>
/* hack to pass raw values to final_check */
static struct xt_rateest_target_info *RATEEST_info;
static unsigned int interval;
static unsigned int ewma_log;
static void
RATEEST_help(void)
{
printf(
"RATEEST target options:\n"
" --rateest-name name Rate estimator name\n"
" --rateest-interval sec Rate measurement interval in seconds\n"
" --rateest-ewmalog value Rate measurement averaging time constant\n");
}
enum RATEEST_options {
RATEEST_OPT_NAME,
RATEEST_OPT_INTERVAL,
RATEEST_OPT_EWMALOG,
};
static const struct option RATEEST_opts[] = {
{.name = "rateest-name", .has_arg = true, .val = RATEEST_OPT_NAME},
{.name = "rateest-interval", .has_arg = true, .val = RATEEST_OPT_INTERVAL},
{.name = "rateest-ewmalog", .has_arg = true, .val = RATEEST_OPT_EWMALOG},
XT_GETOPT_TABLEEND,
};
/* Copied from iproute */
#define TIME_UNITS_PER_SEC 1000000
static int
RATEEST_get_time(unsigned int *time, const char *str)
{
double t;
char *p;
t = strtod(str, &p);
if (p == str)
return -1;
if (*p) {
if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec")==0 ||
strcasecmp(p, "secs")==0)
t *= TIME_UNITS_PER_SEC;
else if (strcasecmp(p, "ms") == 0 || strcasecmp(p, "msec")==0 ||
strcasecmp(p, "msecs") == 0)
t *= TIME_UNITS_PER_SEC/1000;
else if (strcasecmp(p, "us") == 0 || strcasecmp(p, "usec")==0 ||
strcasecmp(p, "usecs") == 0)
t *= TIME_UNITS_PER_SEC/1000000;
else
return -1;
}
*time = t;
return 0;
}
static void
RATEEST_print_time(unsigned int time)
{
double tmp = time;
if (tmp >= TIME_UNITS_PER_SEC)
printf(" %.1fs", tmp / TIME_UNITS_PER_SEC);
else if (tmp >= TIME_UNITS_PER_SEC/1000)
printf(" %.1fms", tmp / (TIME_UNITS_PER_SEC / 1000));
else
printf(" %uus", time);
}
static int
RATEEST_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_target **target)
{
struct xt_rateest_target_info *info = (void *)(*target)->data;
RATEEST_info = info;
switch (c) {
case RATEEST_OPT_NAME:
if (*flags & (1 << c))
xtables_error(PARAMETER_PROBLEM,
"RATEEST: can't specify --rateest-name twice");
*flags |= 1 << c;
strncpy(info->name, optarg, sizeof(info->name) - 1);
break;
case RATEEST_OPT_INTERVAL:
if (*flags & (1 << c))
xtables_error(PARAMETER_PROBLEM,
"RATEEST: can't specify --rateest-interval twice");
*flags |= 1 << c;
if (RATEEST_get_time(&interval, optarg) < 0)
xtables_error(PARAMETER_PROBLEM,
"RATEEST: bad interval value `%s'", optarg);
break;
case RATEEST_OPT_EWMALOG:
if (*flags & (1 << c))
xtables_error(PARAMETER_PROBLEM,
"RATEEST: can't specify --rateest-ewmalog twice");
*flags |= 1 << c;
if (RATEEST_get_time(&ewma_log, optarg) < 0)
xtables_error(PARAMETER_PROBLEM,
"RATEEST: bad ewmalog value `%s'", optarg);
break;
}
return 1;
}
static void
RATEEST_final_check(unsigned int flags)
{
struct xt_rateest_target_info *info = RATEEST_info;
if (!(flags & (1 << RATEEST_OPT_NAME)))
xtables_error(PARAMETER_PROBLEM, "RATEEST: no name specified");
if (!(flags & (1 << RATEEST_OPT_INTERVAL)))
xtables_error(PARAMETER_PROBLEM, "RATEEST: no interval specified");
if (!(flags & (1 << RATEEST_OPT_EWMALOG)))
xtables_error(PARAMETER_PROBLEM, "RATEEST: no ewmalog specified");
for (info->interval = 0; info->interval <= 5; info->interval++) {
if (interval <= (1 << info->interval) * (TIME_UNITS_PER_SEC / 4))
break;
}
if (info->interval > 5)
xtables_error(PARAMETER_PROBLEM,
"RATEEST: interval value is too large");
info->interval -= 2;
for (info->ewma_log = 1; info->ewma_log < 32; info->ewma_log++) {
double w = 1.0 - 1.0 / (1 << info->ewma_log);
if (interval / (-log(w)) > ewma_log)
break;
}
info->ewma_log--;
if (info->ewma_log == 0 || info->ewma_log >= 31)
xtables_error(PARAMETER_PROBLEM,
"RATEEST: ewmalog value is out of range");
}
static void
__RATEEST_print(const struct xt_entry_target *target, const char *prefix)
{
const struct xt_rateest_target_info *info = (const void *)target->data;
unsigned int local_interval;
unsigned int local_ewma_log;
local_interval = (TIME_UNITS_PER_SEC << (info->interval + 2)) / 4;
local_ewma_log = local_interval * (1 << (info->ewma_log));
printf(" %sname %s", prefix, info->name);
printf(" %sinterval", prefix);
RATEEST_print_time(local_interval);
printf(" %sewmalog", prefix);
RATEEST_print_time(local_ewma_log);
}
static void
RATEEST_print(const void *ip, const struct xt_entry_target *target,
int numeric)
{
__RATEEST_print(target, "");
}
static void
RATEEST_save(const void *ip, const struct xt_entry_target *target)
{
__RATEEST_print(target, "--rateest-");
}
static struct xtables_target rateest_tg_reg = {
.family = NFPROTO_UNSPEC,
.name = "RATEEST",
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_rateest_target_info)),
.userspacesize = XT_ALIGN(sizeof(struct xt_rateest_target_info)),
.help = RATEEST_help,
.parse = RATEEST_parse,
.final_check = RATEEST_final_check,
.print = RATEEST_print,
.save = RATEEST_save,
.extra_opts = RATEEST_opts,
};
void _init(void)
{
xtables_register_target(&rateest_tg_reg);
}