]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - net/netfilter/nft_limit.c
netfilter: nft_limit: convert to token-based limiting at nanosecond granularity
[mirror_ubuntu-bionic-kernel.git] / net / netfilter / nft_limit.c
CommitLineData
96518518 1/*
ef1f7df9 2 * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
96518518
PM
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 {
dba27ec1 23 u64 last;
96518518 24 u64 tokens;
dba27ec1 25 u64 tokens_max;
96518518 26 u64 rate;
dba27ec1 27 u64 nsecs;
96518518
PM
28};
29
09e4e42a
PNA
30static void nft_limit_pkts_eval(const struct nft_expr *expr,
31 struct nft_regs *regs,
32 const struct nft_pktinfo *pkt)
96518518
PM
33{
34 struct nft_limit *priv = nft_expr_priv(expr);
dba27ec1
PNA
35 u64 now, tokens, cost = div_u64(priv->nsecs, priv->rate);
36 s64 delta;
96518518
PM
37
38 spin_lock_bh(&limit_lock);
dba27ec1
PNA
39 now = ktime_get_ns();
40 tokens = priv->tokens + now - priv->last;
41 if (tokens > priv->tokens_max)
42 tokens = priv->tokens_max;
43
44 priv->last = now;
45 delta = tokens - cost;
46 if (delta >= 0) {
47 priv->tokens = delta;
96518518
PM
48 spin_unlock_bh(&limit_lock);
49 return;
50 }
dba27ec1 51 priv->tokens = tokens;
96518518
PM
52 spin_unlock_bh(&limit_lock);
53
a55e22e9 54 regs->verdict.code = NFT_BREAK;
96518518
PM
55}
56
57static const struct nla_policy nft_limit_policy[NFTA_LIMIT_MAX + 1] = {
58 [NFTA_LIMIT_RATE] = { .type = NLA_U64 },
59 [NFTA_LIMIT_UNIT] = { .type = NLA_U64 },
60};
61
62static int nft_limit_init(const struct nft_ctx *ctx,
63 const struct nft_expr *expr,
64 const struct nlattr * const tb[])
65{
66 struct nft_limit *priv = nft_expr_priv(expr);
dba27ec1 67 u64 unit;
96518518
PM
68
69 if (tb[NFTA_LIMIT_RATE] == NULL ||
70 tb[NFTA_LIMIT_UNIT] == NULL)
71 return -EINVAL;
72
dba27ec1
PNA
73 priv->rate = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_RATE]));
74 unit = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_UNIT]));
75 priv->nsecs = unit * NSEC_PER_SEC;
76 if (priv->rate == 0 || priv->nsecs < unit)
77 return -EOVERFLOW;
78 priv->tokens = priv->tokens_max = priv->nsecs;
79 priv->last = ktime_get_ns();
96518518
PM
80 return 0;
81}
82
83static int nft_limit_dump(struct sk_buff *skb, const struct nft_expr *expr)
84{
85 const struct nft_limit *priv = nft_expr_priv(expr);
dba27ec1 86 u64 secs = div_u64(priv->nsecs, NSEC_PER_SEC);
96518518 87
dba27ec1
PNA
88 if (nla_put_be64(skb, NFTA_LIMIT_RATE, cpu_to_be64(priv->rate)) ||
89 nla_put_be64(skb, NFTA_LIMIT_UNIT, cpu_to_be64(secs)))
96518518
PM
90 goto nla_put_failure;
91 return 0;
92
93nla_put_failure:
94 return -1;
95}
96
ef1f7df9 97static struct nft_expr_type nft_limit_type;
09e4e42a 98static const struct nft_expr_ops nft_limit_pkts_ops = {
ef1f7df9 99 .type = &nft_limit_type,
96518518 100 .size = NFT_EXPR_SIZE(sizeof(struct nft_limit)),
09e4e42a 101 .eval = nft_limit_pkts_eval,
96518518
PM
102 .init = nft_limit_init,
103 .dump = nft_limit_dump,
ef1f7df9
PM
104};
105
106static struct nft_expr_type nft_limit_type __read_mostly = {
107 .name = "limit",
09e4e42a 108 .ops = &nft_limit_pkts_ops,
96518518
PM
109 .policy = nft_limit_policy,
110 .maxattr = NFTA_LIMIT_MAX,
151d799a 111 .flags = NFT_EXPR_STATEFUL,
ef1f7df9 112 .owner = THIS_MODULE,
96518518
PM
113};
114
115static int __init nft_limit_module_init(void)
116{
ef1f7df9 117 return nft_register_expr(&nft_limit_type);
96518518
PM
118}
119
120static void __exit nft_limit_module_exit(void)
121{
ef1f7df9 122 nft_unregister_expr(&nft_limit_type);
96518518
PM
123}
124
125module_init(nft_limit_module_init);
126module_exit(nft_limit_module_exit);
127
128MODULE_LICENSE("GPL");
129MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
130MODULE_ALIAS_NFT_EXPR("limit");