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, FIELD_SIZEOF(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        net_get_random_once(&jhash_rnd, sizeof(jhash_rnd));
 119
 120        mutex_lock(&xn->hash_lock);
 121        est = __xt_rateest_lookup(xn, info->name);
 122        if (est) {
 123                mutex_unlock(&xn->hash_lock);
 124                /*
 125                 * If estimator parameters are specified, they must match the
 126                 * existing estimator.
 127                 */
 128                if ((!info->interval && !info->ewma_log) ||
 129                    (info->interval != est->params.interval ||
 130                     info->ewma_log != est->params.ewma_log)) {
 131                        xt_rateest_put(par->net, est);
 132                        return -EINVAL;
 133                }
 134                info->est = est;
 135                return 0;
 136        }
 137
 138        ret = -ENOMEM;
 139        est = kzalloc(sizeof(*est), GFP_KERNEL);
 140        if (!est)
 141                goto err1;
 142
 143        strlcpy(est->name, info->name, sizeof(est->name));
 144        spin_lock_init(&est->lock);
 145        est->refcnt             = 1;
 146        est->params.interval    = info->interval;
 147        est->params.ewma_log    = info->ewma_log;
 148
 149        cfg.opt.nla_len         = nla_attr_size(sizeof(cfg.est));
 150        cfg.opt.nla_type        = TCA_STATS_RATE_EST;
 151        cfg.est.interval        = info->interval;
 152        cfg.est.ewma_log        = info->ewma_log;
 153
 154        ret = gen_new_estimator(&est->bstats, NULL, &est->rate_est,
 155                                &est->lock, NULL, &cfg.opt);
 156        if (ret < 0)
 157                goto err2;
 158
 159        info->est = est;
 160        xt_rateest_hash_insert(xn, est);
 161        mutex_unlock(&xn->hash_lock);
 162        return 0;
 163
 164err2:
 165        kfree(est);
 166err1:
 167        mutex_unlock(&xn->hash_lock);
 168        return ret;
 169}
 170
 171static void xt_rateest_tg_destroy(const struct xt_tgdtor_param *par)
 172{
 173        struct xt_rateest_target_info *info = par->targinfo;
 174
 175        xt_rateest_put(par->net, info->est);
 176}
 177
 178static struct xt_target xt_rateest_tg_reg __read_mostly = {
 179        .name       = "RATEEST",
 180        .revision   = 0,
 181        .family     = NFPROTO_UNSPEC,
 182        .target     = xt_rateest_tg,
 183        .checkentry = xt_rateest_tg_checkentry,
 184        .destroy    = xt_rateest_tg_destroy,
 185        .targetsize = sizeof(struct xt_rateest_target_info),
 186        .usersize   = offsetof(struct xt_rateest_target_info, est),
 187        .me         = THIS_MODULE,
 188};
 189
 190static __net_init int xt_rateest_net_init(struct net *net)
 191{
 192        struct xt_rateest_net *xn = net_generic(net, xt_rateest_id);
 193        int i;
 194
 195        mutex_init(&xn->hash_lock);
 196        for (i = 0; i < ARRAY_SIZE(xn->hash); i++)
 197                INIT_HLIST_HEAD(&xn->hash[i]);
 198        return 0;
 199}
 200
 201static struct pernet_operations xt_rateest_net_ops = {
 202        .init = xt_rateest_net_init,
 203        .id   = &xt_rateest_id,
 204        .size = sizeof(struct xt_rateest_net),
 205};
 206
 207static int __init xt_rateest_tg_init(void)
 208{
 209        int err = register_pernet_subsys(&xt_rateest_net_ops);
 210
 211        if (err)
 212                return err;
 213        return xt_register_target(&xt_rateest_tg_reg);
 214}
 215
 216static void __exit xt_rateest_tg_fini(void)
 217{
 218        xt_unregister_target(&xt_rateest_tg_reg);
 219        unregister_pernet_subsys(&xt_rateest_net_ops);
 220}
 221
 222
 223MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
 224MODULE_LICENSE("GPL");
 225MODULE_DESCRIPTION("Xtables: packet rate estimator");
 226MODULE_ALIAS("ipt_RATEEST");
 227MODULE_ALIAS("ip6t_RATEEST");
 228module_init(xt_rateest_tg_init);
 229module_exit(xt_rateest_tg_fini);
 230