root/net/netfilter/nft_limit.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. nft_limit_eval
  2. nft_limit_init
  3. nft_limit_dump
  4. nft_limit_pkts_eval
  5. nft_limit_pkts_init
  6. nft_limit_pkts_dump
  7. nft_limit_bytes_eval
  8. nft_limit_bytes_init
  9. nft_limit_bytes_dump
  10. nft_limit_select_ops
  11. nft_limit_obj_pkts_eval
  12. nft_limit_obj_pkts_init
  13. nft_limit_obj_pkts_dump
  14. nft_limit_obj_bytes_eval
  15. nft_limit_obj_bytes_init
  16. nft_limit_obj_bytes_dump
  17. nft_limit_obj_select_ops
  18. nft_limit_module_init
  19. nft_limit_module_exit

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

/* [<][>][^][v][top][bottom][index][help] */