1e90474c37
Convert packet schedulers to use the netlink API. Unfortunately a gradual conversion is not possible without breaking compilation in the middle or adding lots of casts, so this patch converts them all in one step. The patch has been mostly generated automatically with some minor edits to at least allow seperate conversion of classifiers and actions. Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
206 lines
4.8 KiB
C
206 lines
4.8 KiB
C
/*
|
|
* (C) 2007 Patrick McHardy <kaber@trash.net>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
#include <linux/module.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/gen_stats.h>
|
|
#include <linux/jhash.h>
|
|
#include <linux/rtnetlink.h>
|
|
#include <linux/random.h>
|
|
#include <net/gen_stats.h>
|
|
#include <net/netlink.h>
|
|
|
|
#include <linux/netfilter/x_tables.h>
|
|
#include <linux/netfilter/xt_RATEEST.h>
|
|
#include <net/netfilter/xt_rateest.h>
|
|
|
|
static DEFINE_MUTEX(xt_rateest_mutex);
|
|
|
|
#define RATEEST_HSIZE 16
|
|
static struct hlist_head rateest_hash[RATEEST_HSIZE] __read_mostly;
|
|
static unsigned int jhash_rnd __read_mostly;
|
|
|
|
static unsigned int xt_rateest_hash(const char *name)
|
|
{
|
|
return jhash(name, FIELD_SIZEOF(struct xt_rateest, name), jhash_rnd) &
|
|
(RATEEST_HSIZE - 1);
|
|
}
|
|
|
|
static void xt_rateest_hash_insert(struct xt_rateest *est)
|
|
{
|
|
unsigned int h;
|
|
|
|
h = xt_rateest_hash(est->name);
|
|
hlist_add_head(&est->list, &rateest_hash[h]);
|
|
}
|
|
|
|
struct xt_rateest *xt_rateest_lookup(const char *name)
|
|
{
|
|
struct xt_rateest *est;
|
|
struct hlist_node *n;
|
|
unsigned int h;
|
|
|
|
h = xt_rateest_hash(name);
|
|
mutex_lock(&xt_rateest_mutex);
|
|
hlist_for_each_entry(est, n, &rateest_hash[h], list) {
|
|
if (strcmp(est->name, name) == 0) {
|
|
est->refcnt++;
|
|
mutex_unlock(&xt_rateest_mutex);
|
|
return est;
|
|
}
|
|
}
|
|
mutex_unlock(&xt_rateest_mutex);
|
|
return NULL;
|
|
}
|
|
EXPORT_SYMBOL_GPL(xt_rateest_lookup);
|
|
|
|
void xt_rateest_put(struct xt_rateest *est)
|
|
{
|
|
mutex_lock(&xt_rateest_mutex);
|
|
if (--est->refcnt == 0) {
|
|
hlist_del(&est->list);
|
|
gen_kill_estimator(&est->bstats, &est->rstats);
|
|
kfree(est);
|
|
}
|
|
mutex_unlock(&xt_rateest_mutex);
|
|
}
|
|
EXPORT_SYMBOL_GPL(xt_rateest_put);
|
|
|
|
static unsigned int
|
|
xt_rateest_tg(struct sk_buff *skb,
|
|
const struct net_device *in,
|
|
const struct net_device *out,
|
|
unsigned int hooknum,
|
|
const struct xt_target *target,
|
|
const void *targinfo)
|
|
{
|
|
const struct xt_rateest_target_info *info = targinfo;
|
|
struct gnet_stats_basic *stats = &info->est->bstats;
|
|
|
|
spin_lock_bh(&info->est->lock);
|
|
stats->bytes += skb->len;
|
|
stats->packets++;
|
|
spin_unlock_bh(&info->est->lock);
|
|
|
|
return XT_CONTINUE;
|
|
}
|
|
|
|
static bool
|
|
xt_rateest_tg_checkentry(const char *tablename,
|
|
const void *entry,
|
|
const struct xt_target *target,
|
|
void *targinfo,
|
|
unsigned int hook_mask)
|
|
{
|
|
struct xt_rateest_target_info *info = (void *)targinfo;
|
|
struct xt_rateest *est;
|
|
struct {
|
|
struct nlattr opt;
|
|
struct gnet_estimator est;
|
|
} cfg;
|
|
|
|
est = xt_rateest_lookup(info->name);
|
|
if (est) {
|
|
/*
|
|
* If estimator parameters are specified, they must match the
|
|
* existing estimator.
|
|
*/
|
|
if ((!info->interval && !info->ewma_log) ||
|
|
(info->interval != est->params.interval ||
|
|
info->ewma_log != est->params.ewma_log)) {
|
|
xt_rateest_put(est);
|
|
return false;
|
|
}
|
|
info->est = est;
|
|
return true;
|
|
}
|
|
|
|
est = kzalloc(sizeof(*est), GFP_KERNEL);
|
|
if (!est)
|
|
goto err1;
|
|
|
|
strlcpy(est->name, info->name, sizeof(est->name));
|
|
spin_lock_init(&est->lock);
|
|
est->refcnt = 1;
|
|
est->params.interval = info->interval;
|
|
est->params.ewma_log = info->ewma_log;
|
|
|
|
cfg.opt.nla_len = nla_attr_size(sizeof(cfg.est));
|
|
cfg.opt.nla_type = TCA_STATS_RATE_EST;
|
|
cfg.est.interval = info->interval;
|
|
cfg.est.ewma_log = info->ewma_log;
|
|
|
|
if (gen_new_estimator(&est->bstats, &est->rstats, &est->lock,
|
|
&cfg.opt) < 0)
|
|
goto err2;
|
|
|
|
info->est = est;
|
|
xt_rateest_hash_insert(est);
|
|
|
|
return true;
|
|
|
|
err2:
|
|
kfree(est);
|
|
err1:
|
|
return false;
|
|
}
|
|
|
|
static void xt_rateest_tg_destroy(const struct xt_target *target,
|
|
void *targinfo)
|
|
{
|
|
struct xt_rateest_target_info *info = targinfo;
|
|
|
|
xt_rateest_put(info->est);
|
|
}
|
|
|
|
static struct xt_target xt_rateest_target[] __read_mostly = {
|
|
{
|
|
.family = AF_INET,
|
|
.name = "RATEEST",
|
|
.target = xt_rateest_tg,
|
|
.checkentry = xt_rateest_tg_checkentry,
|
|
.destroy = xt_rateest_tg_destroy,
|
|
.targetsize = sizeof(struct xt_rateest_target_info),
|
|
.me = THIS_MODULE,
|
|
},
|
|
{
|
|
.family = AF_INET6,
|
|
.name = "RATEEST",
|
|
.target = xt_rateest_tg,
|
|
.checkentry = xt_rateest_tg_checkentry,
|
|
.destroy = xt_rateest_tg_destroy,
|
|
.targetsize = sizeof(struct xt_rateest_target_info),
|
|
.me = THIS_MODULE,
|
|
},
|
|
};
|
|
|
|
static int __init xt_rateest_tg_init(void)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(rateest_hash); i++)
|
|
INIT_HLIST_HEAD(&rateest_hash[i]);
|
|
|
|
get_random_bytes(&jhash_rnd, sizeof(jhash_rnd));
|
|
return xt_register_targets(xt_rateest_target,
|
|
ARRAY_SIZE(xt_rateest_target));
|
|
}
|
|
|
|
static void __exit xt_rateest_tg_fini(void)
|
|
{
|
|
xt_unregister_targets(xt_rateest_target, ARRAY_SIZE(xt_rateest_target));
|
|
}
|
|
|
|
|
|
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("Xtables: packet rate estimator");
|
|
MODULE_ALIAS("ipt_RATEEST");
|
|
MODULE_ALIAS("ip6t_RATEEST");
|
|
module_init(xt_rateest_tg_init);
|
|
module_exit(xt_rateest_tg_fini);
|