nft_limit.c 9.5 KB

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