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
  20struct nft_limit {
  21        spinlock_t      lock;
  22        u64             last;
  23        u64             tokens;
  24        u64             tokens_max;
  25        u64             rate;
  26        u64             nsecs;
  27        u32             burst;
  28        bool            invert;
  29};
  30
  31static inline bool nft_limit_eval(struct nft_limit *limit, u64 cost)
  32{
  33        u64 now, tokens;
  34        s64 delta;
  35
  36        spin_lock_bh(&limit->lock);
  37        now = ktime_get_ns();
  38        tokens = limit->tokens + now - limit->last;
  39        if (tokens > limit->tokens_max)
  40                tokens = limit->tokens_max;
  41
  42        limit->last = now;
  43        delta = tokens - cost;
  44        if (delta >= 0) {
  45                limit->tokens = delta;
  46                spin_unlock_bh(&limit->lock);
  47                return limit->invert;
  48        }
  49        limit->tokens = tokens;
  50        spin_unlock_bh(&limit->lock);
  51        return !limit->invert;
  52}
  53
  54/* Use same default as in iptables. */
  55#define NFT_LIMIT_PKT_BURST_DEFAULT     5
  56
  57static int nft_limit_init(struct nft_limit *limit,
  58                          const struct nlattr * const tb[], bool pkts)
  59{
  60        u64 unit, tokens;
  61
  62        if (tb[NFTA_LIMIT_RATE] == NULL ||
  63            tb[NFTA_LIMIT_UNIT] == NULL)
  64                return -EINVAL;
  65
  66        limit->rate = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_RATE]));
  67        unit = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_UNIT]));
  68        limit->nsecs = unit * NSEC_PER_SEC;
  69        if (limit->rate == 0 || limit->nsecs < unit)
  70                return -EOVERFLOW;
  71
  72        if (tb[NFTA_LIMIT_BURST])
  73                limit->burst = ntohl(nla_get_be32(tb[NFTA_LIMIT_BURST]));
  74
  75        if (pkts && limit->burst == 0)
  76                limit->burst = NFT_LIMIT_PKT_BURST_DEFAULT;
  77
  78        if (limit->rate + limit->burst < limit->rate)
  79                return -EOVERFLOW;
  80
  81        if (pkts) {
  82                tokens = div_u64(limit->nsecs, limit->rate) * limit->burst;
  83        } else {
  84                /* The token bucket size limits the number of tokens can be
  85                 * accumulated. tokens_max specifies the bucket size.
  86                 * tokens_max = unit * (rate + burst) / rate.
  87                 */
  88                tokens = div_u64(limit->nsecs * (limit->rate + limit->burst),
  89                                 limit->rate);
  90        }
  91
  92        limit->tokens = tokens;
  93        limit->tokens_max = limit->tokens;
  94
  95        if (tb[NFTA_LIMIT_FLAGS]) {
  96                u32 flags = ntohl(nla_get_be32(tb[NFTA_LIMIT_FLAGS]));
  97
  98                if (flags & NFT_LIMIT_F_INV)
  99                        limit->invert = true;
 100        }
 101        limit->last = ktime_get_ns();
 102        spin_lock_init(&limit->lock);
 103
 104        return 0;
 105}
 106
 107static int nft_limit_dump(struct sk_buff *skb, const struct nft_limit *limit,
 108                          enum nft_limit_type type)
 109{
 110        u32 flags = limit->invert ? NFT_LIMIT_F_INV : 0;
 111        u64 secs = div_u64(limit->nsecs, NSEC_PER_SEC);
 112
 113        if (nla_put_be64(skb, NFTA_LIMIT_RATE, cpu_to_be64(limit->rate),
 114                         NFTA_LIMIT_PAD) ||
 115            nla_put_be64(skb, NFTA_LIMIT_UNIT, cpu_to_be64(secs),
 116                         NFTA_LIMIT_PAD) ||
 117            nla_put_be32(skb, NFTA_LIMIT_BURST, htonl(limit->burst)) ||
 118            nla_put_be32(skb, NFTA_LIMIT_TYPE, htonl(type)) ||
 119            nla_put_be32(skb, NFTA_LIMIT_FLAGS, htonl(flags)))
 120                goto nla_put_failure;
 121        return 0;
 122
 123nla_put_failure:
 124        return -1;
 125}
 126
 127struct nft_limit_pkts {
 128        struct nft_limit        limit;
 129        u64                     cost;
 130};
 131
 132static void nft_limit_pkts_eval(const struct nft_expr *expr,
 133                                struct nft_regs *regs,
 134                                const struct nft_pktinfo *pkt)
 135{
 136        struct nft_limit_pkts *priv = nft_expr_priv(expr);
 137
 138        if (nft_limit_eval(&priv->limit, priv->cost))
 139                regs->verdict.code = NFT_BREAK;
 140}
 141
 142static const struct nla_policy nft_limit_policy[NFTA_LIMIT_MAX + 1] = {
 143        [NFTA_LIMIT_RATE]       = { .type = NLA_U64 },
 144        [NFTA_LIMIT_UNIT]       = { .type = NLA_U64 },
 145        [NFTA_LIMIT_BURST]      = { .type = NLA_U32 },
 146        [NFTA_LIMIT_TYPE]       = { .type = NLA_U32 },
 147        [NFTA_LIMIT_FLAGS]      = { .type = NLA_U32 },
 148};
 149
 150static int nft_limit_pkts_init(const struct nft_ctx *ctx,
 151                               const struct nft_expr *expr,
 152                               const struct nlattr * const tb[])
 153{
 154        struct nft_limit_pkts *priv = nft_expr_priv(expr);
 155        int err;
 156
 157        err = nft_limit_init(&priv->limit, tb, true);
 158        if (err < 0)
 159                return err;
 160
 161        priv->cost = div64_u64(priv->limit.nsecs, priv->limit.rate);
 162        return 0;
 163}
 164
 165static int nft_limit_pkts_dump(struct sk_buff *skb, const struct nft_expr *expr)
 166{
 167        const struct nft_limit_pkts *priv = nft_expr_priv(expr);
 168
 169        return nft_limit_dump(skb, &priv->limit, NFT_LIMIT_PKTS);
 170}
 171
 172static struct nft_expr_type nft_limit_type;
 173static const struct nft_expr_ops nft_limit_pkts_ops = {
 174        .type           = &nft_limit_type,
 175        .size           = NFT_EXPR_SIZE(sizeof(struct nft_limit_pkts)),
 176        .eval           = nft_limit_pkts_eval,
 177        .init           = nft_limit_pkts_init,
 178        .dump           = nft_limit_pkts_dump,
 179};
 180
 181static void nft_limit_bytes_eval(const struct nft_expr *expr,
 182                                 struct nft_regs *regs,
 183                                 const struct nft_pktinfo *pkt)
 184{
 185        struct nft_limit *priv = nft_expr_priv(expr);
 186        u64 cost = div64_u64(priv->nsecs * pkt->skb->len, priv->rate);
 187
 188        if (nft_limit_eval(priv, cost))
 189                regs->verdict.code = NFT_BREAK;
 190}
 191
 192static int nft_limit_bytes_init(const struct nft_ctx *ctx,
 193                                const struct nft_expr *expr,
 194                                const struct nlattr * const tb[])
 195{
 196        struct nft_limit *priv = nft_expr_priv(expr);
 197
 198        return nft_limit_init(priv, tb, false);
 199}
 200
 201static int nft_limit_bytes_dump(struct sk_buff *skb,
 202                                const struct nft_expr *expr)
 203{
 204        const struct nft_limit *priv = nft_expr_priv(expr);
 205
 206        return nft_limit_dump(skb, priv, NFT_LIMIT_PKT_BYTES);
 207}
 208
 209static const struct nft_expr_ops nft_limit_bytes_ops = {
 210        .type           = &nft_limit_type,
 211        .size           = NFT_EXPR_SIZE(sizeof(struct nft_limit)),
 212        .eval           = nft_limit_bytes_eval,
 213        .init           = nft_limit_bytes_init,
 214        .dump           = nft_limit_bytes_dump,
 215};
 216
 217static const struct nft_expr_ops *
 218nft_limit_select_ops(const struct nft_ctx *ctx,
 219                     const struct nlattr * const tb[])
 220{
 221        if (tb[NFTA_LIMIT_TYPE] == NULL)
 222                return &nft_limit_pkts_ops;
 223
 224        switch (ntohl(nla_get_be32(tb[NFTA_LIMIT_TYPE]))) {
 225        case NFT_LIMIT_PKTS:
 226                return &nft_limit_pkts_ops;
 227        case NFT_LIMIT_PKT_BYTES:
 228                return &nft_limit_bytes_ops;
 229        }
 230        return ERR_PTR(-EOPNOTSUPP);
 231}
 232
 233static struct nft_expr_type nft_limit_type __read_mostly = {
 234        .name           = "limit",
 235        .select_ops     = nft_limit_select_ops,
 236        .policy         = nft_limit_policy,
 237        .maxattr        = NFTA_LIMIT_MAX,
 238        .flags          = NFT_EXPR_STATEFUL,
 239        .owner          = THIS_MODULE,
 240};
 241
 242static void nft_limit_obj_pkts_eval(struct nft_object *obj,
 243                                    struct nft_regs *regs,
 244                                    const struct nft_pktinfo *pkt)
 245{
 246        struct nft_limit_pkts *priv = nft_obj_data(obj);
 247
 248        if (nft_limit_eval(&priv->limit, priv->cost))
 249                regs->verdict.code = NFT_BREAK;
 250}
 251
 252static int nft_limit_obj_pkts_init(const struct nft_ctx *ctx,
 253                                   const struct nlattr * const tb[],
 254                                   struct nft_object *obj)
 255{
 256        struct nft_limit_pkts *priv = nft_obj_data(obj);
 257        int err;
 258
 259        err = nft_limit_init(&priv->limit, tb, true);
 260        if (err < 0)
 261                return err;
 262
 263        priv->cost = div64_u64(priv->limit.nsecs, priv->limit.rate);
 264        return 0;
 265}
 266
 267static int nft_limit_obj_pkts_dump(struct sk_buff *skb,
 268                                   struct nft_object *obj,
 269                                   bool reset)
 270{
 271        const struct nft_limit_pkts *priv = nft_obj_data(obj);
 272
 273        return nft_limit_dump(skb, &priv->limit, NFT_LIMIT_PKTS);
 274}
 275
 276static struct nft_object_type nft_limit_obj_type;
 277static const struct nft_object_ops nft_limit_obj_pkts_ops = {
 278        .type           = &nft_limit_obj_type,
 279        .size           = NFT_EXPR_SIZE(sizeof(struct nft_limit_pkts)),
 280        .init           = nft_limit_obj_pkts_init,
 281        .eval           = nft_limit_obj_pkts_eval,
 282        .dump           = nft_limit_obj_pkts_dump,
 283};
 284
 285static void nft_limit_obj_bytes_eval(struct nft_object *obj,
 286                                     struct nft_regs *regs,
 287                                     const struct nft_pktinfo *pkt)
 288{
 289        struct nft_limit *priv = nft_obj_data(obj);
 290        u64 cost = div64_u64(priv->nsecs * pkt->skb->len, priv->rate);
 291
 292        if (nft_limit_eval(priv, cost))
 293                regs->verdict.code = NFT_BREAK;
 294}
 295
 296static int nft_limit_obj_bytes_init(const struct nft_ctx *ctx,
 297                                    const struct nlattr * const tb[],
 298                                    struct nft_object *obj)
 299{
 300        struct nft_limit *priv = nft_obj_data(obj);
 301
 302        return nft_limit_init(priv, tb, false);
 303}
 304
 305static int nft_limit_obj_bytes_dump(struct sk_buff *skb,
 306                                    struct nft_object *obj,
 307                                    bool reset)
 308{
 309        const struct nft_limit *priv = nft_obj_data(obj);
 310
 311        return nft_limit_dump(skb, priv, NFT_LIMIT_PKT_BYTES);
 312}
 313
 314static struct nft_object_type nft_limit_obj_type;
 315static const struct nft_object_ops nft_limit_obj_bytes_ops = {
 316        .type           = &nft_limit_obj_type,
 317        .size           = sizeof(struct nft_limit),
 318        .init           = nft_limit_obj_bytes_init,
 319        .eval           = nft_limit_obj_bytes_eval,
 320        .dump           = nft_limit_obj_bytes_dump,
 321};
 322
 323static const struct nft_object_ops *
 324nft_limit_obj_select_ops(const struct nft_ctx *ctx,
 325                         const struct nlattr * const tb[])
 326{
 327        if (!tb[NFTA_LIMIT_TYPE])
 328                return &nft_limit_obj_pkts_ops;
 329
 330        switch (ntohl(nla_get_be32(tb[NFTA_LIMIT_TYPE]))) {
 331        case NFT_LIMIT_PKTS:
 332                return &nft_limit_obj_pkts_ops;
 333        case NFT_LIMIT_PKT_BYTES:
 334                return &nft_limit_obj_bytes_ops;
 335        }
 336        return ERR_PTR(-EOPNOTSUPP);
 337}
 338
 339static struct nft_object_type nft_limit_obj_type __read_mostly = {
 340        .select_ops     = nft_limit_obj_select_ops,
 341        .type           = NFT_OBJECT_LIMIT,
 342        .maxattr        = NFTA_LIMIT_MAX,
 343        .policy         = nft_limit_policy,
 344        .owner          = THIS_MODULE,
 345};
 346
 347static int __init nft_limit_module_init(void)
 348{
 349        int err;
 350
 351        err = nft_register_obj(&nft_limit_obj_type);
 352        if (err < 0)
 353                return err;
 354
 355        err = nft_register_expr(&nft_limit_type);
 356        if (err < 0)
 357                goto err1;
 358
 359        return 0;
 360err1:
 361        nft_unregister_obj(&nft_limit_obj_type);
 362        return err;
 363}
 364
 365static void __exit nft_limit_module_exit(void)
 366{
 367        nft_unregister_expr(&nft_limit_type);
 368        nft_unregister_obj(&nft_limit_obj_type);
 369}
 370
 371module_init(nft_limit_module_init);
 372module_exit(nft_limit_module_exit);
 373
 374MODULE_LICENSE("GPL");
 375MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
 376MODULE_ALIAS_NFT_EXPR("limit");
 377MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_LIMIT);
 378