0001
0002
0003
0004
0005
0006 #include <linux/kernel.h>
0007 #include <linux/init.h>
0008 #include <linux/module.h>
0009 #include <linux/netlink.h>
0010 #include <linux/netfilter.h>
0011 #include <linux/netfilter/nf_tables.h>
0012 #include <linux/random.h>
0013 #include <linux/static_key.h>
0014 #include <net/netfilter/nf_tables.h>
0015 #include <net/netfilter/nf_tables_core.h>
0016
0017 struct nft_ng_inc {
0018 u8 dreg;
0019 u32 modulus;
0020 atomic_t *counter;
0021 u32 offset;
0022 };
0023
0024 static u32 nft_ng_inc_gen(struct nft_ng_inc *priv)
0025 {
0026 u32 nval, oval;
0027
0028 do {
0029 oval = atomic_read(priv->counter);
0030 nval = (oval + 1 < priv->modulus) ? oval + 1 : 0;
0031 } while (atomic_cmpxchg(priv->counter, oval, nval) != oval);
0032
0033 return nval + priv->offset;
0034 }
0035
0036 static void nft_ng_inc_eval(const struct nft_expr *expr,
0037 struct nft_regs *regs,
0038 const struct nft_pktinfo *pkt)
0039 {
0040 struct nft_ng_inc *priv = nft_expr_priv(expr);
0041
0042 regs->data[priv->dreg] = nft_ng_inc_gen(priv);
0043 }
0044
0045 static const struct nla_policy nft_ng_policy[NFTA_NG_MAX + 1] = {
0046 [NFTA_NG_DREG] = { .type = NLA_U32 },
0047 [NFTA_NG_MODULUS] = { .type = NLA_U32 },
0048 [NFTA_NG_TYPE] = { .type = NLA_U32 },
0049 [NFTA_NG_OFFSET] = { .type = NLA_U32 },
0050 };
0051
0052 static int nft_ng_inc_init(const struct nft_ctx *ctx,
0053 const struct nft_expr *expr,
0054 const struct nlattr * const tb[])
0055 {
0056 struct nft_ng_inc *priv = nft_expr_priv(expr);
0057 int err;
0058
0059 if (tb[NFTA_NG_OFFSET])
0060 priv->offset = ntohl(nla_get_be32(tb[NFTA_NG_OFFSET]));
0061
0062 priv->modulus = ntohl(nla_get_be32(tb[NFTA_NG_MODULUS]));
0063 if (priv->modulus == 0)
0064 return -ERANGE;
0065
0066 if (priv->offset + priv->modulus - 1 < priv->offset)
0067 return -EOVERFLOW;
0068
0069 priv->counter = kmalloc(sizeof(*priv->counter), GFP_KERNEL);
0070 if (!priv->counter)
0071 return -ENOMEM;
0072
0073 atomic_set(priv->counter, priv->modulus - 1);
0074
0075 err = nft_parse_register_store(ctx, tb[NFTA_NG_DREG], &priv->dreg,
0076 NULL, NFT_DATA_VALUE, sizeof(u32));
0077 if (err < 0)
0078 goto err;
0079
0080 return 0;
0081 err:
0082 kfree(priv->counter);
0083
0084 return err;
0085 }
0086
0087 static bool nft_ng_inc_reduce(struct nft_regs_track *track,
0088 const struct nft_expr *expr)
0089 {
0090 const struct nft_ng_inc *priv = nft_expr_priv(expr);
0091
0092 nft_reg_track_cancel(track, priv->dreg, NFT_REG32_SIZE);
0093
0094 return false;
0095 }
0096
0097 static int nft_ng_dump(struct sk_buff *skb, enum nft_registers dreg,
0098 u32 modulus, enum nft_ng_types type, u32 offset)
0099 {
0100 if (nft_dump_register(skb, NFTA_NG_DREG, dreg))
0101 goto nla_put_failure;
0102 if (nla_put_be32(skb, NFTA_NG_MODULUS, htonl(modulus)))
0103 goto nla_put_failure;
0104 if (nla_put_be32(skb, NFTA_NG_TYPE, htonl(type)))
0105 goto nla_put_failure;
0106 if (nla_put_be32(skb, NFTA_NG_OFFSET, htonl(offset)))
0107 goto nla_put_failure;
0108
0109 return 0;
0110
0111 nla_put_failure:
0112 return -1;
0113 }
0114
0115 static int nft_ng_inc_dump(struct sk_buff *skb, const struct nft_expr *expr)
0116 {
0117 const struct nft_ng_inc *priv = nft_expr_priv(expr);
0118
0119 return nft_ng_dump(skb, priv->dreg, priv->modulus, NFT_NG_INCREMENTAL,
0120 priv->offset);
0121 }
0122
0123 static void nft_ng_inc_destroy(const struct nft_ctx *ctx,
0124 const struct nft_expr *expr)
0125 {
0126 const struct nft_ng_inc *priv = nft_expr_priv(expr);
0127
0128 kfree(priv->counter);
0129 }
0130
0131 struct nft_ng_random {
0132 u8 dreg;
0133 u32 modulus;
0134 u32 offset;
0135 };
0136
0137 static u32 nft_ng_random_gen(const struct nft_ng_random *priv)
0138 {
0139 return reciprocal_scale(get_random_u32(), priv->modulus) + priv->offset;
0140 }
0141
0142 static void nft_ng_random_eval(const struct nft_expr *expr,
0143 struct nft_regs *regs,
0144 const struct nft_pktinfo *pkt)
0145 {
0146 struct nft_ng_random *priv = nft_expr_priv(expr);
0147
0148 regs->data[priv->dreg] = nft_ng_random_gen(priv);
0149 }
0150
0151 static int nft_ng_random_init(const struct nft_ctx *ctx,
0152 const struct nft_expr *expr,
0153 const struct nlattr * const tb[])
0154 {
0155 struct nft_ng_random *priv = nft_expr_priv(expr);
0156
0157 if (tb[NFTA_NG_OFFSET])
0158 priv->offset = ntohl(nla_get_be32(tb[NFTA_NG_OFFSET]));
0159
0160 priv->modulus = ntohl(nla_get_be32(tb[NFTA_NG_MODULUS]));
0161 if (priv->modulus == 0)
0162 return -ERANGE;
0163
0164 if (priv->offset + priv->modulus - 1 < priv->offset)
0165 return -EOVERFLOW;
0166
0167 return nft_parse_register_store(ctx, tb[NFTA_NG_DREG], &priv->dreg,
0168 NULL, NFT_DATA_VALUE, sizeof(u32));
0169 }
0170
0171 static int nft_ng_random_dump(struct sk_buff *skb, const struct nft_expr *expr)
0172 {
0173 const struct nft_ng_random *priv = nft_expr_priv(expr);
0174
0175 return nft_ng_dump(skb, priv->dreg, priv->modulus, NFT_NG_RANDOM,
0176 priv->offset);
0177 }
0178
0179 static bool nft_ng_random_reduce(struct nft_regs_track *track,
0180 const struct nft_expr *expr)
0181 {
0182 const struct nft_ng_random *priv = nft_expr_priv(expr);
0183
0184 nft_reg_track_cancel(track, priv->dreg, NFT_REG32_SIZE);
0185
0186 return false;
0187 }
0188
0189 static struct nft_expr_type nft_ng_type;
0190 static const struct nft_expr_ops nft_ng_inc_ops = {
0191 .type = &nft_ng_type,
0192 .size = NFT_EXPR_SIZE(sizeof(struct nft_ng_inc)),
0193 .eval = nft_ng_inc_eval,
0194 .init = nft_ng_inc_init,
0195 .destroy = nft_ng_inc_destroy,
0196 .dump = nft_ng_inc_dump,
0197 .reduce = nft_ng_inc_reduce,
0198 };
0199
0200 static const struct nft_expr_ops nft_ng_random_ops = {
0201 .type = &nft_ng_type,
0202 .size = NFT_EXPR_SIZE(sizeof(struct nft_ng_random)),
0203 .eval = nft_ng_random_eval,
0204 .init = nft_ng_random_init,
0205 .dump = nft_ng_random_dump,
0206 .reduce = nft_ng_random_reduce,
0207 };
0208
0209 static const struct nft_expr_ops *
0210 nft_ng_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[])
0211 {
0212 u32 type;
0213
0214 if (!tb[NFTA_NG_DREG] ||
0215 !tb[NFTA_NG_MODULUS] ||
0216 !tb[NFTA_NG_TYPE])
0217 return ERR_PTR(-EINVAL);
0218
0219 type = ntohl(nla_get_be32(tb[NFTA_NG_TYPE]));
0220
0221 switch (type) {
0222 case NFT_NG_INCREMENTAL:
0223 return &nft_ng_inc_ops;
0224 case NFT_NG_RANDOM:
0225 return &nft_ng_random_ops;
0226 }
0227
0228 return ERR_PTR(-EINVAL);
0229 }
0230
0231 static struct nft_expr_type nft_ng_type __read_mostly = {
0232 .name = "numgen",
0233 .select_ops = nft_ng_select_ops,
0234 .policy = nft_ng_policy,
0235 .maxattr = NFTA_NG_MAX,
0236 .owner = THIS_MODULE,
0237 };
0238
0239 static int __init nft_ng_module_init(void)
0240 {
0241 return nft_register_expr(&nft_ng_type);
0242 }
0243
0244 static void __exit nft_ng_module_exit(void)
0245 {
0246 nft_unregister_expr(&nft_ng_type);
0247 }
0248
0249 module_init(nft_ng_module_init);
0250 module_exit(nft_ng_module_exit);
0251
0252 MODULE_LICENSE("GPL");
0253 MODULE_AUTHOR("Laura Garcia <nevola@gmail.com>");
0254 MODULE_ALIAS_NFT_EXPR("numgen");
0255 MODULE_DESCRIPTION("nftables number generator module");