linux/net/netfilter/xt_RATEEST.c
<<
>>
Prefs
   1/*
   2 * (C) 2007 Patrick McHardy <kaber@trash.net>
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 */
   8#include <linux/module.h>
   9#include <linux/skbuff.h>
  10#include <linux/gen_stats.h>
  11#include <linux/jhash.h>
  12#include <linux/rtnetlink.h>
  13#include <linux/random.h>
  14#include <linux/slab.h>
  15#include <net/gen_stats.h>
  16#include <net/netlink.h>
  17#include <net/netns/generic.h>
  18
  19#include <linux/netfilter/x_tables.h>
  20#include <linux/netfilter/xt_RATEEST.h>
  21#include <net/netfilter/xt_rateest.h>
  22
  23#define RATEEST_HSIZE   16
  24
  25struct xt_rateest_net {
  26        struct mutex hash_lock;
  27        struct hlist_head hash[RATEEST_HSIZE];
  28};
  29
  30static unsigned int xt_rateest_id;
  31
  32static unsigned int jhash_rnd __read_mostly;
  33
  34static unsigned int xt_rateest_hash(const char *name)
  35{
  36        return jhash(name, FIELD_SIZEOF(struct xt_rateest, name), jhash_rnd) &
  37               (RATEEST_HSIZE - 1);
  38}
  39
  40static void xt_rateest_hash_insert(struct xt_rateest_net *xn,
  41                                   struct xt_rateest *est)
  42{
  43        unsigned int h;
  44
  45        h = xt_rateest_hash(est->name);
  46        hlist_add_head(&est->list, &xn->hash[h]);
  47}
  48
  49static struct xt_rateest *__xt_rateest_lookup(struct xt_rateest_net *xn,
  50                                              const char *name)
  51{
  52        struct xt_rateest *est;
  53        unsigned int h;
  54
  55        h = xt_rateest_hash(name);
  56        hlist_for_each_entry(est, &xn->hash[h], list) {
  57                if (strcmp(est->name, name) == 0) {
  58                        est->refcnt++;
  59                        return est;
  60                }
  61        }
  62
  63        return NULL;
  64}
  65
  66struct xt_rateest *xt_rateest_lookup(struct net *net, const char *name)
  67{
  68        struct xt_rateest_net *xn = net_generic(net, xt_rateest_id);
  69        struct xt_rateest *est;
  70
  71        mutex_lock(&xn->hash_lock);
  72        est = __xt_rateest_lookup(xn, name);
  73        mutex_unlock(&xn->hash_lock);
  74        return est;
  75}
  76EXPORT_SYMBOL_GPL(xt_rateest_lookup);
  77
  78void xt_rateest_put(struct net *net, struct xt_rateest *est)
  79{
  80        struct xt_rateest_net *xn = net_generic(net, xt_rateest_id);
  81
  82        mutex_lock(&xn->hash_lock);
  83        if (--est->refcnt == 0) {
  84                hlist_del(&est->list);
  85                gen_kill_estimator(&est->rate_est);
  86                /*
  87                 * gen_estimator est_timer() might access est->lock or bstats,
  88                 * wait a RCU grace period before freeing 'est'
  89                 */
  90                kfree_rcu(est, rcu);
  91        }
  92        mutex_unlock(&xn->hash_lock);
  93}
  94EXPORT_SYMBOL_GPL(xt_rateest_put);
  95
  96static unsigned int
  97xt_rateest_tg(struct sk_buff *skb, const struct xt_action_param *par)
  98{
  99        const struct xt_rateest_target_info *info = par->targinfo;
 100        struct gnet_stats_basic_packed *stats = &info->est->bstats;
 101
 102        spin_lock_bh(&info->est->lock);
 103        stats->bytes += skb->len;
 104        stats->packets++;
 105        spin_unlock_bh(&info->est->lock);
 106
 107        return XT_CONTINUE;
 108}
 109
 110static int xt_rateest_tg_checkentry(const struct xt_tgchk_param *par)
 111{
 112        struct xt_rateest_net *xn = net_generic(par->net, xt_rateest_id);
 113        struct xt_rateest_target_info *info = par->targinfo;
 114        struct xt_rateest *est;
 115        struct {
 116                struct nlattr           opt;
 117                struct gnet_estimator   est;
 118        } cfg;
 119        int ret;
 120
 121        net_get_random_once(&jhash_rnd, sizeof(jhash_rnd));
 122
 123        mutex_lock(&xn->hash_lock);
 124        est = __xt_rateest_lookup(xn, info->name);
 125        if (est) {
 126                mutex_unlock(&xn->hash_lock);
 127                /*
 128                 * If estimator parameters are specified, they must match the
 129                 * existing estimator.
 130                 */
 131                if ((!info->interval && !info->ewma_log) ||
 132                    (info->interval != est->params.interval ||
 133                     info->ewma_log != est->params.ewma_log)) {
 134                        xt_rateest_put(par->net, est);
 135                        return -EINVAL;
 136                }
 137                info->est = est;
 138                return 0;
 139        }
 140
 141        ret = -ENOMEM;
 142        est = kzalloc(sizeof(*est), GFP_KERNEL);
 143        if (!est)
 144                goto err1;
 145
 146        strlcpy(est->name, info->name, sizeof(est->name));
 147        spin_lock_init(&est->lock);
 148        est->refcnt             = 1;
 149        est->params.interval    = info->interval;
 150        est->params.ewma_log    = info->ewma_log;
 151
 152        cfg.opt.nla_len         = nla_attr_size(sizeof(cfg.est));
 153        cfg.opt.nla_type        = TCA_STATS_RATE_EST;
 154        cfg.est.interval        = info->interval;
 155        cfg.est.ewma_log        = info->ewma_log;
 156
 157        ret = gen_new_estimator(&est->bstats, NULL, &est->rate_est,
 158                                &est->lock, NULL, &cfg.opt);
 159        if (ret < 0)
 160                goto err2;
 161
 162        info->est = est;
 163        xt_rateest_hash_insert(xn, est);
 164        mutex_unlock(&xn->hash_lock);
 165        return 0;
 166
 167err2:
 168        kfree(est);
 169err1:
 170        mutex_unlock(&xn->hash_lock);
 171        return ret;
 172}
 173
 174static void xt_rateest_tg_destroy(const struct xt_tgdtor_param *par)
 175{
 176        struct xt_rateest_target_info *info = par->targinfo;
 177
 178        xt_rateest_put(par->net, info->est);
 179}
 180
 181static struct xt_target xt_rateest_tg_reg __read_mostly = {
 182        .name       = "RATEEST",
 183        .revision   = 0,
 184        .family     = NFPROTO_UNSPEC,
 185        .target     = xt_rateest_tg,
 186        .checkentry = xt_rateest_tg_checkentry,
 187        .destroy    = xt_rateest_tg_destroy,
 188        .targetsize = sizeof(struct xt_rateest_target_info),
 189        .usersize   = offsetof(struct xt_rateest_target_info, est),
 190        .me         = THIS_MODULE,
 191};
 192
 193static __net_init int xt_rateest_net_init(struct net *net)
 194{
 195        struct xt_rateest_net *xn = net_generic(net, xt_rateest_id);
 196        int i;
 197
 198        mutex_init(&xn->hash_lock);
 199        for (i = 0; i < ARRAY_SIZE(xn->hash); i++)
 200                INIT_HLIST_HEAD(&xn->hash[i]);
 201        return 0;
 202}
 203
 204static struct pernet_operations xt_rateest_net_ops = {
 205        .init = xt_rateest_net_init,
 206        .id   = &xt_rateest_id,
 207        .size = sizeof(struct xt_rateest_net),
 208};
 209
 210static int __init xt_rateest_tg_init(void)
 211{
 212        int err = register_pernet_subsys(&xt_rateest_net_ops);
 213
 214        if (err)
 215                return err;
 216        return xt_register_target(&xt_rateest_tg_reg);
 217}
 218
 219static void __exit xt_rateest_tg_fini(void)
 220{
 221        xt_unregister_target(&xt_rateest_tg_reg);
 222        unregister_pernet_subsys(&xt_rateest_net_ops);
 223}
 224
 225
 226MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
 227MODULE_LICENSE("GPL");
 228MODULE_DESCRIPTION("Xtables: packet rate estimator");
 229MODULE_ALIAS("ipt_RATEEST");
 230MODULE_ALIAS("ip6t_RATEEST");
 231module_init(xt_rateest_tg_init);
 232module_exit(xt_rateest_tg_fini);
 233