diff options
author | Pablo Neira Ayuso <pablo@netfilter.org> | 2015-07-31 08:10:22 -0400 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2015-08-07 05:49:49 -0400 |
commit | dba27ec1bc382014aa2ba46e96f421b5f6536dae (patch) | |
tree | 315ad68755c1dc85658effc89e56bc7235b1234e /net/netfilter | |
parent | 09e4e42a00b99e94cfce27e63b06daca0c26e841 (diff) |
netfilter: nft_limit: convert to token-based limiting at nanosecond granularity
Rework the limit expression to use a token-based limiting approach that refills
the bucket gradually. The tokens are calculated at nanosecond granularity
instead jiffies to improve precision.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net/netfilter')
-rw-r--r-- | net/netfilter/nft_limit.c | 42 |
1 files changed, 26 insertions, 16 deletions
diff --git a/net/netfilter/nft_limit.c b/net/netfilter/nft_limit.c index d0788e172b4c..c79703e5baad 100644 --- a/net/netfilter/nft_limit.c +++ b/net/netfilter/nft_limit.c | |||
@@ -20,10 +20,11 @@ | |||
20 | static DEFINE_SPINLOCK(limit_lock); | 20 | static DEFINE_SPINLOCK(limit_lock); |
21 | 21 | ||
22 | struct nft_limit { | 22 | struct nft_limit { |
23 | u64 last; | ||
23 | u64 tokens; | 24 | u64 tokens; |
25 | u64 tokens_max; | ||
24 | u64 rate; | 26 | u64 rate; |
25 | u64 unit; | 27 | u64 nsecs; |
26 | unsigned long stamp; | ||
27 | }; | 28 | }; |
28 | 29 | ||
29 | static void nft_limit_pkts_eval(const struct nft_expr *expr, | 30 | static void nft_limit_pkts_eval(const struct nft_expr *expr, |
@@ -31,18 +32,23 @@ static void nft_limit_pkts_eval(const struct nft_expr *expr, | |||
31 | const struct nft_pktinfo *pkt) | 32 | const struct nft_pktinfo *pkt) |
32 | { | 33 | { |
33 | struct nft_limit *priv = nft_expr_priv(expr); | 34 | struct nft_limit *priv = nft_expr_priv(expr); |
35 | u64 now, tokens, cost = div_u64(priv->nsecs, priv->rate); | ||
36 | s64 delta; | ||
34 | 37 | ||
35 | spin_lock_bh(&limit_lock); | 38 | spin_lock_bh(&limit_lock); |
36 | if (time_after_eq(jiffies, priv->stamp)) { | 39 | now = ktime_get_ns(); |
37 | priv->tokens = priv->rate; | 40 | tokens = priv->tokens + now - priv->last; |
38 | priv->stamp = jiffies + priv->unit * HZ; | 41 | if (tokens > priv->tokens_max) |
39 | } | 42 | tokens = priv->tokens_max; |
40 | 43 | ||
41 | if (priv->tokens >= 1) { | 44 | priv->last = now; |
42 | priv->tokens--; | 45 | delta = tokens - cost; |
46 | if (delta >= 0) { | ||
47 | priv->tokens = delta; | ||
43 | spin_unlock_bh(&limit_lock); | 48 | spin_unlock_bh(&limit_lock); |
44 | return; | 49 | return; |
45 | } | 50 | } |
51 | priv->tokens = tokens; | ||
46 | spin_unlock_bh(&limit_lock); | 52 | spin_unlock_bh(&limit_lock); |
47 | 53 | ||
48 | regs->verdict.code = NFT_BREAK; | 54 | regs->verdict.code = NFT_BREAK; |
@@ -58,25 +64,29 @@ static int nft_limit_init(const struct nft_ctx *ctx, | |||
58 | const struct nlattr * const tb[]) | 64 | const struct nlattr * const tb[]) |
59 | { | 65 | { |
60 | struct nft_limit *priv = nft_expr_priv(expr); | 66 | struct nft_limit *priv = nft_expr_priv(expr); |
67 | u64 unit; | ||
61 | 68 | ||
62 | if (tb[NFTA_LIMIT_RATE] == NULL || | 69 | if (tb[NFTA_LIMIT_RATE] == NULL || |
63 | tb[NFTA_LIMIT_UNIT] == NULL) | 70 | tb[NFTA_LIMIT_UNIT] == NULL) |
64 | return -EINVAL; | 71 | return -EINVAL; |
65 | 72 | ||
66 | priv->rate = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_RATE])); | 73 | 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])); | 74 | unit = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_UNIT])); |
68 | priv->stamp = jiffies + priv->unit * HZ; | 75 | priv->nsecs = unit * NSEC_PER_SEC; |
69 | priv->tokens = priv->rate; | 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(); | ||
70 | return 0; | 80 | return 0; |
71 | } | 81 | } |
72 | 82 | ||
73 | static int nft_limit_dump(struct sk_buff *skb, const struct nft_expr *expr) | 83 | static int nft_limit_dump(struct sk_buff *skb, const struct nft_expr *expr) |
74 | { | 84 | { |
75 | const struct nft_limit *priv = nft_expr_priv(expr); | 85 | const struct nft_limit *priv = nft_expr_priv(expr); |
86 | u64 secs = div_u64(priv->nsecs, NSEC_PER_SEC); | ||
76 | 87 | ||
77 | if (nla_put_be64(skb, NFTA_LIMIT_RATE, cpu_to_be64(priv->rate))) | 88 | if (nla_put_be64(skb, NFTA_LIMIT_RATE, cpu_to_be64(priv->rate)) || |
78 | goto nla_put_failure; | 89 | nla_put_be64(skb, NFTA_LIMIT_UNIT, cpu_to_be64(secs))) |
79 | if (nla_put_be64(skb, NFTA_LIMIT_UNIT, cpu_to_be64(priv->unit))) | ||
80 | goto nla_put_failure; | 90 | goto nla_put_failure; |
81 | return 0; | 91 | return 0; |
82 | 92 | ||