linux/net/netfilter/xt_RATEEST.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * (C) 2007 Patrick McHardy <kaber@trash.net>
   4 */
   5#include <linux/module.h>
   6#include <linux/skbuff.h>
   7#include <linux/gen_stats.h>
   8#include <linux/jhash.h>
   9#include <linux/rtnetlink.h>
  10#include <linux/random.h>
  11#include <linux/slab.h>
  12#include <net/gen_stats.h>
  13#include <net/netlink.h>
  14#include <net/netns/generic.h>
  15
  16#include <linux/netfilter/x_tables.h>
  17#include <linux/netfilter/xt_RATEEST.h>
  18#include <net/netfilter/xt_rateest.h>
  19
  20#define RATEEST_HSIZE   16
  21
  22struct xt_rateest_net {
  23        struct mutex hash_lock;
  24        struct hlist_head hash[RATEEST_HSIZE];
  25};
  26
  27static unsigned int xt_rateest_id;
  28
  29static unsigned int jhash_rnd __read_mostly;
  30
  31static unsigned int xt_rateest_hash(const char *name)
  32{
  33        return jhash(name, sizeof_field(struct xt_rateest, name), jhash_rnd) &
  34               (RATEEST_HSIZE - 1);
  35}
  36
  37static void xt_rateest_hash_insert(struct xt_rateest_net *xn,
  38                                   struct xt_rateest *est)
  39{
  40        unsigned int h;
  41
  42        h = xt_rateest_hash(est->name);
  43        hlist_add_head(&est->list, &xn->hash[h]);
  44}
  45
  46static struct xt_rateest *__xt_rateest_lookup(struct xt_rateest_net *xn,
  47                                              const char *name)
  48{
  49        struct xt_rateest *est;
  50        unsigned int h;
  51
  52        h = xt_rateest_hash(name);
  53        hlist_for_each_entry(est, &xn->hash[h], list) {
  54                if (strcmp(est->name, name) == 0) {
  55                        est->refcnt++;
  56                        return est;
  57                }
  58        }
  59
  60        return NULL;
  61}
  62
  63struct xt_rateest *xt_rateest_lookup(struct net *net, const char *name)
  64{
  65        struct xt_rateest_net *xn = net_generic(net, xt_rateest_id);
  66        struct xt_rateest *est;
  67
  68        mutex_lock(&xn->hash_lock);
  69        est = __xt_rateest_lookup(xn, name);
  70        mutex_unlock(&xn->hash_lock);
  71        return est;
  72}
  73EXPORT_SYMBOL_GPL(xt_rateest_lookup);
  74
  75void xt_rateest_put(struct net *net, struct xt_rateest *est)
  76{
  77        struct xt_rateest_net *xn = net_generic(net, xt_rateest_id);
  78
  79        mutex_lock(&xn->hash_lock);
  80        if (--est->refcnt == 0) {
  81                hlist_del(&est->list);
  82                gen_kill_estimator(&est->rate_est);
  83                /*
  84                 * gen_estimator est_timer() might access est->lock or bstats,
  85                 * wait a RCU grace period before freeing 'est'
  86                 */
  87                kfree_rcu(est, rcu);
  88        }
  89        mutex_unlock(&xn->hash_lock);
  90}
  91EXPORT_SYMBOL_GPL(xt_rateest_put);
  92
  93static unsigned int
  94xt_rateest_tg(struct sk_buff *skb, const struct xt_action_param *par)
  95{
  96        const struct xt_rateest_target_info *info = par->targinfo;
  97        struct gnet_stats_basic_packed *stats = &info->est->bstats;
  98
  99        spin_lock_bh(&info->est->lock);
 100        stats->bytes += skb->len;
 101        stats->packets++;
 102        spin_unlock_bh(&info->est->lock);
 103
 104        return XT_CONTINUE;
 105}
 106
 107static int xt_rateest_tg_checkentry(const struct xt_tgchk_param *par)
 108{
 109        struct xt_rateest_net *xn = net_generic(par->net, xt_rateest_id);
 110        struct xt_rateest_target_info *info = par->targinfo;
 111        struct xt_rateest *est;
 112        struct {
 113                struct nlattr           opt;
 114                struct gnet_estimator   est;
 115        } cfg;
 116        int ret;
 117
 118        if (strnlen(info->name, sizeof(est->name)) >= sizeof(est->name))
 119                return -ENAMETOOLONG;
 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