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 | }; |
22 | |
23 | struct nft_limit_priv { |
24 | struct nft_limit *limit; |
25 | u64 tokens_max; |
26 | u64 rate; |
27 | u64 nsecs; |
28 | u32 burst; |
29 | bool invert; |
30 | }; |
31 | |
32 | static inline bool nft_limit_eval(struct nft_limit_priv *priv, u64 cost) |
33 | { |
34 | u64 now, tokens; |
35 | s64 delta; |
36 | |
37 | spin_lock_bh(lock: &priv->limit->lock); |
38 | now = ktime_get_ns(); |
39 | tokens = priv->limit->tokens + now - priv->limit->last; |
40 | if (tokens > priv->tokens_max) |
41 | tokens = priv->tokens_max; |
42 | |
43 | priv->limit->last = now; |
44 | delta = tokens - cost; |
45 | if (delta >= 0) { |
46 | priv->limit->tokens = delta; |
47 | spin_unlock_bh(lock: &priv->limit->lock); |
48 | return priv->invert; |
49 | } |
50 | priv->limit->tokens = tokens; |
51 | spin_unlock_bh(lock: &priv->limit->lock); |
52 | return !priv->invert; |
53 | } |
54 | |
55 | /* Use same default as in iptables. */ |
56 | #define NFT_LIMIT_PKT_BURST_DEFAULT 5 |
57 | |
58 | static int nft_limit_init(struct nft_limit_priv *priv, |
59 | const struct nlattr * const tb[], bool pkts) |
60 | { |
61 | u64 unit, tokens, rate_with_burst; |
62 | bool invert = false; |
63 | |
64 | if (tb[NFTA_LIMIT_RATE] == NULL || |
65 | tb[NFTA_LIMIT_UNIT] == NULL) |
66 | return -EINVAL; |
67 | |
68 | priv->rate = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_RATE])); |
69 | if (priv->rate == 0) |
70 | return -EINVAL; |
71 | |
72 | unit = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_UNIT])); |
73 | if (check_mul_overflow(unit, NSEC_PER_SEC, &priv->nsecs)) |
74 | return -EOVERFLOW; |
75 | |
76 | if (tb[NFTA_LIMIT_BURST]) |
77 | priv->burst = ntohl(nla_get_be32(tb[NFTA_LIMIT_BURST])); |
78 | |
79 | if (pkts && priv->burst == 0) |
80 | priv->burst = NFT_LIMIT_PKT_BURST_DEFAULT; |
81 | |
82 | if (check_add_overflow(priv->rate, priv->burst, &rate_with_burst)) |
83 | return -EOVERFLOW; |
84 | |
85 | if (pkts) { |
86 | u64 tmp = div64_u64(dividend: priv->nsecs, divisor: priv->rate); |
87 | |
88 | if (check_mul_overflow(tmp, priv->burst, &tokens)) |
89 | return -EOVERFLOW; |
90 | } else { |
91 | u64 tmp; |
92 | |
93 | /* The token bucket size limits the number of tokens can be |
94 | * accumulated. tokens_max specifies the bucket size. |
95 | * tokens_max = unit * (rate + burst) / rate. |
96 | */ |
97 | if (check_mul_overflow(priv->nsecs, rate_with_burst, &tmp)) |
98 | return -EOVERFLOW; |
99 | |
100 | tokens = div64_u64(dividend: tmp, divisor: priv->rate); |
101 | } |
102 | |
103 | if (tb[NFTA_LIMIT_FLAGS]) { |
104 | u32 flags = ntohl(nla_get_be32(tb[NFTA_LIMIT_FLAGS])); |
105 | |
106 | if (flags & ~NFT_LIMIT_F_INV) |
107 | return -EOPNOTSUPP; |
108 | |
109 | if (flags & NFT_LIMIT_F_INV) |
110 | invert = true; |
111 | } |
112 | |
113 | priv->limit = kmalloc(size: sizeof(*priv->limit), GFP_KERNEL_ACCOUNT); |
114 | if (!priv->limit) |
115 | return -ENOMEM; |
116 | |
117 | priv->limit->tokens = tokens; |
118 | priv->tokens_max = priv->limit->tokens; |
119 | priv->invert = invert; |
120 | priv->limit->last = ktime_get_ns(); |
121 | spin_lock_init(&priv->limit->lock); |
122 | |
123 | return 0; |
124 | } |
125 | |
126 | static int nft_limit_dump(struct sk_buff *skb, const struct nft_limit_priv *priv, |
127 | enum nft_limit_type type) |
128 | { |
129 | u32 flags = priv->invert ? NFT_LIMIT_F_INV : 0; |
130 | u64 secs = div_u64(dividend: priv->nsecs, NSEC_PER_SEC); |
131 | |
132 | if (nla_put_be64(skb, attrtype: NFTA_LIMIT_RATE, cpu_to_be64(priv->rate), |
133 | padattr: NFTA_LIMIT_PAD) || |
134 | nla_put_be64(skb, attrtype: NFTA_LIMIT_UNIT, cpu_to_be64(secs), |
135 | padattr: NFTA_LIMIT_PAD) || |
136 | nla_put_be32(skb, attrtype: NFTA_LIMIT_BURST, htonl(priv->burst)) || |
137 | nla_put_be32(skb, attrtype: NFTA_LIMIT_TYPE, htonl(type)) || |
138 | nla_put_be32(skb, attrtype: NFTA_LIMIT_FLAGS, htonl(flags))) |
139 | goto nla_put_failure; |
140 | return 0; |
141 | |
142 | nla_put_failure: |
143 | return -1; |
144 | } |
145 | |
146 | static void nft_limit_destroy(const struct nft_ctx *ctx, |
147 | const struct nft_limit_priv *priv) |
148 | { |
149 | kfree(objp: priv->limit); |
150 | } |
151 | |
152 | static int nft_limit_clone(struct nft_limit_priv *priv_dst, |
153 | const struct nft_limit_priv *priv_src) |
154 | { |
155 | priv_dst->tokens_max = priv_src->tokens_max; |
156 | priv_dst->rate = priv_src->rate; |
157 | priv_dst->nsecs = priv_src->nsecs; |
158 | priv_dst->burst = priv_src->burst; |
159 | priv_dst->invert = priv_src->invert; |
160 | |
161 | priv_dst->limit = kmalloc(size: sizeof(*priv_dst->limit), GFP_ATOMIC); |
162 | if (!priv_dst->limit) |
163 | return -ENOMEM; |
164 | |
165 | spin_lock_init(&priv_dst->limit->lock); |
166 | priv_dst->limit->tokens = priv_src->tokens_max; |
167 | priv_dst->limit->last = ktime_get_ns(); |
168 | |
169 | return 0; |
170 | } |
171 | |
172 | struct nft_limit_priv_pkts { |
173 | struct nft_limit_priv limit; |
174 | u64 cost; |
175 | }; |
176 | |
177 | static void nft_limit_pkts_eval(const struct nft_expr *expr, |
178 | struct nft_regs *regs, |
179 | const struct nft_pktinfo *pkt) |
180 | { |
181 | struct nft_limit_priv_pkts *priv = nft_expr_priv(expr); |
182 | |
183 | if (nft_limit_eval(priv: &priv->limit, cost: priv->cost)) |
184 | regs->verdict.code = NFT_BREAK; |
185 | } |
186 | |
187 | static const struct nla_policy nft_limit_policy[NFTA_LIMIT_MAX + 1] = { |
188 | [NFTA_LIMIT_RATE] = { .type = NLA_U64 }, |
189 | [NFTA_LIMIT_UNIT] = { .type = NLA_U64 }, |
190 | [NFTA_LIMIT_BURST] = { .type = NLA_U32 }, |
191 | [NFTA_LIMIT_TYPE] = { .type = NLA_U32 }, |
192 | [NFTA_LIMIT_FLAGS] = { .type = NLA_U32 }, |
193 | }; |
194 | |
195 | static int nft_limit_pkts_init(const struct nft_ctx *ctx, |
196 | const struct nft_expr *expr, |
197 | const struct nlattr * const tb[]) |
198 | { |
199 | struct nft_limit_priv_pkts *priv = nft_expr_priv(expr); |
200 | int err; |
201 | |
202 | err = nft_limit_init(priv: &priv->limit, tb, pkts: true); |
203 | if (err < 0) |
204 | return err; |
205 | |
206 | priv->cost = div64_u64(dividend: priv->limit.nsecs, divisor: priv->limit.rate); |
207 | return 0; |
208 | } |
209 | |
210 | static int nft_limit_pkts_dump(struct sk_buff *skb, |
211 | const struct nft_expr *expr, bool reset) |
212 | { |
213 | const struct nft_limit_priv_pkts *priv = nft_expr_priv(expr); |
214 | |
215 | return nft_limit_dump(skb, priv: &priv->limit, type: NFT_LIMIT_PKTS); |
216 | } |
217 | |
218 | static void nft_limit_pkts_destroy(const struct nft_ctx *ctx, |
219 | const struct nft_expr *expr) |
220 | { |
221 | const struct nft_limit_priv_pkts *priv = nft_expr_priv(expr); |
222 | |
223 | nft_limit_destroy(ctx, priv: &priv->limit); |
224 | } |
225 | |
226 | static int nft_limit_pkts_clone(struct nft_expr *dst, const struct nft_expr *src) |
227 | { |
228 | struct nft_limit_priv_pkts *priv_dst = nft_expr_priv(expr: dst); |
229 | struct nft_limit_priv_pkts *priv_src = nft_expr_priv(expr: src); |
230 | |
231 | priv_dst->cost = priv_src->cost; |
232 | |
233 | return nft_limit_clone(priv_dst: &priv_dst->limit, priv_src: &priv_src->limit); |
234 | } |
235 | |
236 | static struct nft_expr_type nft_limit_type; |
237 | static const struct nft_expr_ops nft_limit_pkts_ops = { |
238 | .type = &nft_limit_type, |
239 | .size = NFT_EXPR_SIZE(sizeof(struct nft_limit_priv_pkts)), |
240 | .eval = nft_limit_pkts_eval, |
241 | .init = nft_limit_pkts_init, |
242 | .destroy = nft_limit_pkts_destroy, |
243 | .clone = nft_limit_pkts_clone, |
244 | .dump = nft_limit_pkts_dump, |
245 | .reduce = NFT_REDUCE_READONLY, |
246 | }; |
247 | |
248 | static void nft_limit_bytes_eval(const struct nft_expr *expr, |
249 | struct nft_regs *regs, |
250 | const struct nft_pktinfo *pkt) |
251 | { |
252 | struct nft_limit_priv *priv = nft_expr_priv(expr); |
253 | u64 cost = div64_u64(dividend: priv->nsecs * pkt->skb->len, divisor: priv->rate); |
254 | |
255 | if (nft_limit_eval(priv, cost)) |
256 | regs->verdict.code = NFT_BREAK; |
257 | } |
258 | |
259 | static int nft_limit_bytes_init(const struct nft_ctx *ctx, |
260 | const struct nft_expr *expr, |
261 | const struct nlattr * const tb[]) |
262 | { |
263 | struct nft_limit_priv *priv = nft_expr_priv(expr); |
264 | |
265 | return nft_limit_init(priv, tb, pkts: false); |
266 | } |
267 | |
268 | static int nft_limit_bytes_dump(struct sk_buff *skb, |
269 | const struct nft_expr *expr, bool reset) |
270 | { |
271 | const struct nft_limit_priv *priv = nft_expr_priv(expr); |
272 | |
273 | return nft_limit_dump(skb, priv, type: NFT_LIMIT_PKT_BYTES); |
274 | } |
275 | |
276 | static void nft_limit_bytes_destroy(const struct nft_ctx *ctx, |
277 | const struct nft_expr *expr) |
278 | { |
279 | const struct nft_limit_priv *priv = nft_expr_priv(expr); |
280 | |
281 | nft_limit_destroy(ctx, priv); |
282 | } |
283 | |
284 | static int nft_limit_bytes_clone(struct nft_expr *dst, const struct nft_expr *src) |
285 | { |
286 | struct nft_limit_priv *priv_dst = nft_expr_priv(expr: dst); |
287 | struct nft_limit_priv *priv_src = nft_expr_priv(expr: src); |
288 | |
289 | return nft_limit_clone(priv_dst, priv_src); |
290 | } |
291 | |
292 | static const struct nft_expr_ops nft_limit_bytes_ops = { |
293 | .type = &nft_limit_type, |
294 | .size = NFT_EXPR_SIZE(sizeof(struct nft_limit_priv)), |
295 | .eval = nft_limit_bytes_eval, |
296 | .init = nft_limit_bytes_init, |
297 | .dump = nft_limit_bytes_dump, |
298 | .clone = nft_limit_bytes_clone, |
299 | .destroy = nft_limit_bytes_destroy, |
300 | .reduce = NFT_REDUCE_READONLY, |
301 | }; |
302 | |
303 | static const struct nft_expr_ops * |
304 | nft_limit_select_ops(const struct nft_ctx *ctx, |
305 | const struct nlattr * const tb[]) |
306 | { |
307 | if (tb[NFTA_LIMIT_TYPE] == NULL) |
308 | return &nft_limit_pkts_ops; |
309 | |
310 | switch (ntohl(nla_get_be32(tb[NFTA_LIMIT_TYPE]))) { |
311 | case NFT_LIMIT_PKTS: |
312 | return &nft_limit_pkts_ops; |
313 | case NFT_LIMIT_PKT_BYTES: |
314 | return &nft_limit_bytes_ops; |
315 | } |
316 | return ERR_PTR(error: -EOPNOTSUPP); |
317 | } |
318 | |
319 | static struct nft_expr_type nft_limit_type __read_mostly = { |
320 | .name = "limit" , |
321 | .select_ops = nft_limit_select_ops, |
322 | .policy = nft_limit_policy, |
323 | .maxattr = NFTA_LIMIT_MAX, |
324 | .flags = NFT_EXPR_STATEFUL, |
325 | .owner = THIS_MODULE, |
326 | }; |
327 | |
328 | static void nft_limit_obj_pkts_eval(struct nft_object *obj, |
329 | struct nft_regs *regs, |
330 | const struct nft_pktinfo *pkt) |
331 | { |
332 | struct nft_limit_priv_pkts *priv = nft_obj_data(obj); |
333 | |
334 | if (nft_limit_eval(priv: &priv->limit, cost: priv->cost)) |
335 | regs->verdict.code = NFT_BREAK; |
336 | } |
337 | |
338 | static int nft_limit_obj_pkts_init(const struct nft_ctx *ctx, |
339 | const struct nlattr * const tb[], |
340 | struct nft_object *obj) |
341 | { |
342 | struct nft_limit_priv_pkts *priv = nft_obj_data(obj); |
343 | int err; |
344 | |
345 | err = nft_limit_init(priv: &priv->limit, tb, pkts: true); |
346 | if (err < 0) |
347 | return err; |
348 | |
349 | priv->cost = div64_u64(dividend: priv->limit.nsecs, divisor: priv->limit.rate); |
350 | return 0; |
351 | } |
352 | |
353 | static int nft_limit_obj_pkts_dump(struct sk_buff *skb, |
354 | struct nft_object *obj, |
355 | bool reset) |
356 | { |
357 | const struct nft_limit_priv_pkts *priv = nft_obj_data(obj); |
358 | |
359 | return nft_limit_dump(skb, priv: &priv->limit, type: NFT_LIMIT_PKTS); |
360 | } |
361 | |
362 | static void nft_limit_obj_pkts_destroy(const struct nft_ctx *ctx, |
363 | struct nft_object *obj) |
364 | { |
365 | struct nft_limit_priv_pkts *priv = nft_obj_data(obj); |
366 | |
367 | nft_limit_destroy(ctx, priv: &priv->limit); |
368 | } |
369 | |
370 | static struct nft_object_type nft_limit_obj_type; |
371 | static const struct nft_object_ops nft_limit_obj_pkts_ops = { |
372 | .type = &nft_limit_obj_type, |
373 | .size = NFT_EXPR_SIZE(sizeof(struct nft_limit_priv_pkts)), |
374 | .init = nft_limit_obj_pkts_init, |
375 | .destroy = nft_limit_obj_pkts_destroy, |
376 | .eval = nft_limit_obj_pkts_eval, |
377 | .dump = nft_limit_obj_pkts_dump, |
378 | }; |
379 | |
380 | static void nft_limit_obj_bytes_eval(struct nft_object *obj, |
381 | struct nft_regs *regs, |
382 | const struct nft_pktinfo *pkt) |
383 | { |
384 | struct nft_limit_priv *priv = nft_obj_data(obj); |
385 | u64 cost = div64_u64(dividend: priv->nsecs * pkt->skb->len, divisor: priv->rate); |
386 | |
387 | if (nft_limit_eval(priv, cost)) |
388 | regs->verdict.code = NFT_BREAK; |
389 | } |
390 | |
391 | static int nft_limit_obj_bytes_init(const struct nft_ctx *ctx, |
392 | const struct nlattr * const tb[], |
393 | struct nft_object *obj) |
394 | { |
395 | struct nft_limit_priv *priv = nft_obj_data(obj); |
396 | |
397 | return nft_limit_init(priv, tb, pkts: false); |
398 | } |
399 | |
400 | static int nft_limit_obj_bytes_dump(struct sk_buff *skb, |
401 | struct nft_object *obj, |
402 | bool reset) |
403 | { |
404 | const struct nft_limit_priv *priv = nft_obj_data(obj); |
405 | |
406 | return nft_limit_dump(skb, priv, type: NFT_LIMIT_PKT_BYTES); |
407 | } |
408 | |
409 | static void nft_limit_obj_bytes_destroy(const struct nft_ctx *ctx, |
410 | struct nft_object *obj) |
411 | { |
412 | struct nft_limit_priv *priv = nft_obj_data(obj); |
413 | |
414 | nft_limit_destroy(ctx, priv); |
415 | } |
416 | |
417 | static struct nft_object_type nft_limit_obj_type; |
418 | static const struct nft_object_ops nft_limit_obj_bytes_ops = { |
419 | .type = &nft_limit_obj_type, |
420 | .size = sizeof(struct nft_limit_priv), |
421 | .init = nft_limit_obj_bytes_init, |
422 | .destroy = nft_limit_obj_bytes_destroy, |
423 | .eval = nft_limit_obj_bytes_eval, |
424 | .dump = nft_limit_obj_bytes_dump, |
425 | }; |
426 | |
427 | static const struct nft_object_ops * |
428 | nft_limit_obj_select_ops(const struct nft_ctx *ctx, |
429 | const struct nlattr * const tb[]) |
430 | { |
431 | if (!tb[NFTA_LIMIT_TYPE]) |
432 | return &nft_limit_obj_pkts_ops; |
433 | |
434 | switch (ntohl(nla_get_be32(tb[NFTA_LIMIT_TYPE]))) { |
435 | case NFT_LIMIT_PKTS: |
436 | return &nft_limit_obj_pkts_ops; |
437 | case NFT_LIMIT_PKT_BYTES: |
438 | return &nft_limit_obj_bytes_ops; |
439 | } |
440 | return ERR_PTR(error: -EOPNOTSUPP); |
441 | } |
442 | |
443 | static struct nft_object_type nft_limit_obj_type __read_mostly = { |
444 | .select_ops = nft_limit_obj_select_ops, |
445 | .type = NFT_OBJECT_LIMIT, |
446 | .maxattr = NFTA_LIMIT_MAX, |
447 | .policy = nft_limit_policy, |
448 | .owner = THIS_MODULE, |
449 | }; |
450 | |
451 | static int __init nft_limit_module_init(void) |
452 | { |
453 | int err; |
454 | |
455 | err = nft_register_obj(obj_type: &nft_limit_obj_type); |
456 | if (err < 0) |
457 | return err; |
458 | |
459 | err = nft_register_expr(&nft_limit_type); |
460 | if (err < 0) |
461 | goto err1; |
462 | |
463 | return 0; |
464 | err1: |
465 | nft_unregister_obj(obj_type: &nft_limit_obj_type); |
466 | return err; |
467 | } |
468 | |
469 | static void __exit nft_limit_module_exit(void) |
470 | { |
471 | nft_unregister_expr(&nft_limit_type); |
472 | nft_unregister_obj(obj_type: &nft_limit_obj_type); |
473 | } |
474 | |
475 | module_init(nft_limit_module_init); |
476 | module_exit(nft_limit_module_exit); |
477 | |
478 | MODULE_LICENSE("GPL" ); |
479 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>" ); |
480 | MODULE_ALIAS_NFT_EXPR("limit" ); |
481 | MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_LIMIT); |
482 | MODULE_DESCRIPTION("nftables limit expression support" ); |
483 | |