| /* | |
| * (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); |