linux/net/netfilter/nft_limit.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2008-2009 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 * Development of this code funded by Astaro AG (http://www.astaro.com/)
   9 */
  10
  11#include <linux/kernel.h>
  12#include <linux/init.h>
  13#include <linux/module.h>
  14#include <linux/spinlock.h>
  15#include <linux/netlink.h>
  16#include <linux/netfilter.h>
  17#include <linux/netfilter/nf_tables.h>
  18#include <net/netfilter/nf_tables.h>
  19
  20static DEFINE_SPINLOCK(limit_lock);
  21
  22struct nft_limit {
  23        u64             tokens;
  24        u64             rate;
  25        u64             unit;
  26        unsigned long   stamp;
  27};
  28
  29static void nft_limit_eval(const struct nft_expr *expr,
  30                           struct nft_data data[NFT_REG_MAX + 1],
  31                           const struct nft_pktinfo *pkt)
  32{
  33        struct nft_limit *priv = nft_expr_priv(expr);
  34
  35        spin_lock_bh(&limit_lock);
  36        if (time_after_eq(jiffies, priv->stamp)) {
  37                priv->tokens = priv->rate;
  38                priv->stamp = jiffies + priv->unit * HZ;
  39        }
  40
  41        if (priv->tokens >= 1) {
  42                priv->tokens--;
  43                spin_unlock_bh(&limit_lock);
  44                return;
  45        }
  46        spin_unlock_bh(&limit_lock);
  47
  48        data[NFT_REG_VERDICT].verdict = NFT_BREAK;
  49}
  50
  51static const struct nla_policy nft_limit_policy[NFTA_LIMIT_MAX + 1] = {
  52        [NFTA_LIMIT_RATE]       = { .type = NLA_U64 },
  53        [NFTA_LIMIT_UNIT]       = { .type = NLA_U64 },
  54};
  55
  56static int nft_limit_init(const struct nft_ctx *ctx,
  57                          const struct nft_expr *expr,
  58                          const struct nlattr * const tb[])
  59{
  60        struct nft_limit *priv = nft_expr_priv(expr);
  61
  62        if (tb[NFTA_LIMIT_RATE] == NULL ||
  63            tb[NFTA_LIMIT_UNIT] == NULL)
  64                return -EINVAL;
  65
  66        priv->rate   = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_RATE]));
  67        priv->unit   = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_UNIT]));
  68        priv->stamp  = jiffies + priv->unit * HZ;
  69        priv->tokens = priv->rate;
  70        return 0;
  71}
  72
  73static int nft_limit_dump(struct sk_buff *skb, const struct nft_expr *expr)
  74{
  75        const struct nft_limit *priv = nft_expr_priv(expr);
  76
  77        if (nla_put_be64(skb, NFTA_LIMIT_RATE, cpu_to_be64(priv->rate)))
  78                goto nla_put_failure;
  79        if (nla_put_be64(skb, NFTA_LIMIT_UNIT, cpu_to_be64(priv->unit)))
  80                goto nla_put_failure;
  81        return 0;
  82
  83nla_put_failure:
  84        return -1;
  85}
  86
  87static struct nft_expr_type nft_limit_type;
  88static const struct nft_expr_ops nft_limit_ops = {
  89        .type           = &nft_limit_type,
  90        .size           = NFT_EXPR_SIZE(sizeof(struct nft_limit)),
  91        .eval           = nft_limit_eval,
  92        .init           = nft_limit_init,
  93        .dump           = nft_limit_dump,
  94};
  95
  96static struct nft_expr_type nft_limit_type __read_mostly = {
  97        .name           = "limit",
  98        .ops            = &nft_limit_ops,
  99        .policy         = nft_limit_policy,
 100        .maxattr        = NFTA_LIMIT_MAX,
 101        .owner          = THIS_MODULE,
 102};
 103
 104static int __init nft_limit_module_init(void)
 105{
 106        return nft_register_expr(&nft_limit_type);
 107}
 108
 109static void __exit nft_limit_module_exit(void)
 110{
 111        nft_unregister_expr(&nft_limit_type);
 112}
 113
 114module_init(nft_limit_module_init);
 115module_exit(nft_limit_module_exit);
 116
 117MODULE_LICENSE("GPL");
 118MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
 119MODULE_ALIAS_NFT_EXPR("limit");
 120