nft_numgen.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /*
  2. * Copyright (c) 2016 Laura Garcia <nevola@gmail.com>
  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. */
  9. #include <linux/kernel.h>
  10. #include <linux/init.h>
  11. #include <linux/module.h>
  12. #include <linux/netlink.h>
  13. #include <linux/netfilter.h>
  14. #include <linux/netfilter/nf_tables.h>
  15. #include <linux/static_key.h>
  16. #include <net/netfilter/nf_tables.h>
  17. #include <net/netfilter/nf_tables_core.h>
  18. static DEFINE_PER_CPU(struct rnd_state, nft_numgen_prandom_state);
  19. struct nft_ng_inc {
  20. enum nft_registers dreg:8;
  21. u32 modulus;
  22. atomic_t counter;
  23. u32 offset;
  24. };
  25. static void nft_ng_inc_eval(const struct nft_expr *expr,
  26. struct nft_regs *regs,
  27. const struct nft_pktinfo *pkt)
  28. {
  29. struct nft_ng_inc *priv = nft_expr_priv(expr);
  30. u32 nval, oval;
  31. do {
  32. oval = atomic_read(&priv->counter);
  33. nval = (oval + 1 < priv->modulus) ? oval + 1 : 0;
  34. } while (atomic_cmpxchg(&priv->counter, oval, nval) != oval);
  35. regs->data[priv->dreg] = nval + priv->offset;
  36. }
  37. static const struct nla_policy nft_ng_policy[NFTA_NG_MAX + 1] = {
  38. [NFTA_NG_DREG] = { .type = NLA_U32 },
  39. [NFTA_NG_MODULUS] = { .type = NLA_U32 },
  40. [NFTA_NG_TYPE] = { .type = NLA_U32 },
  41. [NFTA_NG_OFFSET] = { .type = NLA_U32 },
  42. };
  43. static int nft_ng_inc_init(const struct nft_ctx *ctx,
  44. const struct nft_expr *expr,
  45. const struct nlattr * const tb[])
  46. {
  47. struct nft_ng_inc *priv = nft_expr_priv(expr);
  48. if (tb[NFTA_NG_OFFSET])
  49. priv->offset = ntohl(nla_get_be32(tb[NFTA_NG_OFFSET]));
  50. priv->modulus = ntohl(nla_get_be32(tb[NFTA_NG_MODULUS]));
  51. if (priv->modulus == 0)
  52. return -ERANGE;
  53. if (priv->offset + priv->modulus - 1 < priv->offset)
  54. return -EOVERFLOW;
  55. priv->dreg = nft_parse_register(tb[NFTA_NG_DREG]);
  56. atomic_set(&priv->counter, 0);
  57. return nft_validate_register_store(ctx, priv->dreg, NULL,
  58. NFT_DATA_VALUE, sizeof(u32));
  59. }
  60. static int nft_ng_dump(struct sk_buff *skb, enum nft_registers dreg,
  61. u32 modulus, enum nft_ng_types type, u32 offset)
  62. {
  63. if (nft_dump_register(skb, NFTA_NG_DREG, dreg))
  64. goto nla_put_failure;
  65. if (nla_put_be32(skb, NFTA_NG_MODULUS, htonl(modulus)))
  66. goto nla_put_failure;
  67. if (nla_put_be32(skb, NFTA_NG_TYPE, htonl(type)))
  68. goto nla_put_failure;
  69. if (nla_put_be32(skb, NFTA_NG_OFFSET, htonl(offset)))
  70. goto nla_put_failure;
  71. return 0;
  72. nla_put_failure:
  73. return -1;
  74. }
  75. static int nft_ng_inc_dump(struct sk_buff *skb, const struct nft_expr *expr)
  76. {
  77. const struct nft_ng_inc *priv = nft_expr_priv(expr);
  78. return nft_ng_dump(skb, priv->dreg, priv->modulus, NFT_NG_INCREMENTAL,
  79. priv->offset);
  80. }
  81. struct nft_ng_random {
  82. enum nft_registers dreg:8;
  83. u32 modulus;
  84. u32 offset;
  85. };
  86. static void nft_ng_random_eval(const struct nft_expr *expr,
  87. struct nft_regs *regs,
  88. const struct nft_pktinfo *pkt)
  89. {
  90. struct nft_ng_random *priv = nft_expr_priv(expr);
  91. struct rnd_state *state = this_cpu_ptr(&nft_numgen_prandom_state);
  92. u32 val;
  93. val = reciprocal_scale(prandom_u32_state(state), priv->modulus);
  94. regs->data[priv->dreg] = val + priv->offset;
  95. }
  96. static int nft_ng_random_init(const struct nft_ctx *ctx,
  97. const struct nft_expr *expr,
  98. const struct nlattr * const tb[])
  99. {
  100. struct nft_ng_random *priv = nft_expr_priv(expr);
  101. if (tb[NFTA_NG_OFFSET])
  102. priv->offset = ntohl(nla_get_be32(tb[NFTA_NG_OFFSET]));
  103. priv->modulus = ntohl(nla_get_be32(tb[NFTA_NG_MODULUS]));
  104. if (priv->modulus == 0)
  105. return -ERANGE;
  106. if (priv->offset + priv->modulus - 1 < priv->offset)
  107. return -EOVERFLOW;
  108. prandom_init_once(&nft_numgen_prandom_state);
  109. priv->dreg = nft_parse_register(tb[NFTA_NG_DREG]);
  110. return nft_validate_register_store(ctx, priv->dreg, NULL,
  111. NFT_DATA_VALUE, sizeof(u32));
  112. }
  113. static int nft_ng_random_dump(struct sk_buff *skb, const struct nft_expr *expr)
  114. {
  115. const struct nft_ng_random *priv = nft_expr_priv(expr);
  116. return nft_ng_dump(skb, priv->dreg, priv->modulus, NFT_NG_RANDOM,
  117. priv->offset);
  118. }
  119. static struct nft_expr_type nft_ng_type;
  120. static const struct nft_expr_ops nft_ng_inc_ops = {
  121. .type = &nft_ng_type,
  122. .size = NFT_EXPR_SIZE(sizeof(struct nft_ng_inc)),
  123. .eval = nft_ng_inc_eval,
  124. .init = nft_ng_inc_init,
  125. .dump = nft_ng_inc_dump,
  126. };
  127. static const struct nft_expr_ops nft_ng_random_ops = {
  128. .type = &nft_ng_type,
  129. .size = NFT_EXPR_SIZE(sizeof(struct nft_ng_random)),
  130. .eval = nft_ng_random_eval,
  131. .init = nft_ng_random_init,
  132. .dump = nft_ng_random_dump,
  133. };
  134. static const struct nft_expr_ops *
  135. nft_ng_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[])
  136. {
  137. u32 type;
  138. if (!tb[NFTA_NG_DREG] ||
  139. !tb[NFTA_NG_MODULUS] ||
  140. !tb[NFTA_NG_TYPE])
  141. return ERR_PTR(-EINVAL);
  142. type = ntohl(nla_get_be32(tb[NFTA_NG_TYPE]));
  143. switch (type) {
  144. case NFT_NG_INCREMENTAL:
  145. return &nft_ng_inc_ops;
  146. case NFT_NG_RANDOM:
  147. return &nft_ng_random_ops;
  148. }
  149. return ERR_PTR(-EINVAL);
  150. }
  151. static struct nft_expr_type nft_ng_type __read_mostly = {
  152. .name = "numgen",
  153. .select_ops = &nft_ng_select_ops,
  154. .policy = nft_ng_policy,
  155. .maxattr = NFTA_NG_MAX,
  156. .owner = THIS_MODULE,
  157. };
  158. static int __init nft_ng_module_init(void)
  159. {
  160. return nft_register_expr(&nft_ng_type);
  161. }
  162. static void __exit nft_ng_module_exit(void)
  163. {
  164. nft_unregister_expr(&nft_ng_type);
  165. }
  166. module_init(nft_ng_module_init);
  167. module_exit(nft_ng_module_exit);
  168. MODULE_LICENSE("GPL");
  169. MODULE_AUTHOR("Laura Garcia <nevola@gmail.com>");
  170. MODULE_ALIAS_NFT_EXPR("numgen");