aboutsummaryrefslogtreecommitdiffstats
path: root/net/netfilter
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2015-07-31 08:10:22 -0400
committerPablo Neira Ayuso <pablo@netfilter.org>2015-08-07 05:49:49 -0400
commitdba27ec1bc382014aa2ba46e96f421b5f6536dae (patch)
tree315ad68755c1dc85658effc89e56bc7235b1234e /net/netfilter
parent09e4e42a00b99e94cfce27e63b06daca0c26e841 (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.c42
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 @@
20static DEFINE_SPINLOCK(limit_lock); 20static DEFINE_SPINLOCK(limit_lock);
21 21
22struct nft_limit { 22struct 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
29static void nft_limit_pkts_eval(const struct nft_expr *expr, 30static 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
73static int nft_limit_dump(struct sk_buff *skb, const struct nft_expr *expr) 83static 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