1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2016 Laura Garcia <nevola@gmail.com> 4 */ 5 6 #include <linux/kernel.h> 7 #include <linux/init.h> 8 #include <linux/module.h> 9 #include <linux/netlink.h> 10 #include <linux/netfilter.h> 11 #include <linux/netfilter/nf_tables.h> 12 #include <linux/static_key.h> 13 #include <net/netfilter/nf_tables.h> 14 #include <net/netfilter/nf_tables_core.h> 15 16 static DEFINE_PER_CPU(struct rnd_state, nft_numgen_prandom_state); 17 18 struct nft_ng_inc { 19 u8 dreg; 20 u32 modulus; 21 atomic_t *counter; 22 u32 offset; 23 }; 24 25 static u32 nft_ng_inc_gen(struct nft_ng_inc *priv) 26 { 27 u32 nval, oval; 28 29 do { 30 oval = atomic_read(priv->counter); 31 nval = (oval + 1 < priv->modulus) ? oval + 1 : 0; 32 } while (atomic_cmpxchg(priv->counter, oval, nval) != oval); 33 34 return nval + priv->offset; 35 } 36 37 static void nft_ng_inc_eval(const struct nft_expr *expr, 38 struct nft_regs *regs, 39 const struct nft_pktinfo *pkt) 40 { 41 struct nft_ng_inc *priv = nft_expr_priv(expr); 42 43 regs->data[priv->dreg] = nft_ng_inc_gen(priv); 44 } 45 46 static const struct nla_policy nft_ng_policy[NFTA_NG_MAX + 1] = { 47 [NFTA_NG_DREG] = { .type = NLA_U32 }, 48 [NFTA_NG_MODULUS] = { .type = NLA_U32 }, 49 [NFTA_NG_TYPE] = { .type = NLA_U32 }, 50 [NFTA_NG_OFFSET] = { .type = NLA_U32 }, 51 }; 52 53 static int nft_ng_inc_init(const struct nft_ctx *ctx, 54 const struct nft_expr *expr, 55 const struct nlattr * const tb[]) 56 { 57 struct nft_ng_inc *priv = nft_expr_priv(expr); 58 int err; 59 60 if (tb[NFTA_NG_OFFSET]) 61 priv->offset = ntohl(nla_get_be32(tb[NFTA_NG_OFFSET])); 62 63 priv->modulus = ntohl(nla_get_be32(tb[NFTA_NG_MODULUS])); 64 if (priv->modulus == 0) 65 return -ERANGE; 66 67 if (priv->offset + priv->modulus - 1 < priv->offset) 68 return -EOVERFLOW; 69 70 priv->counter = kmalloc(sizeof(*priv->counter), GFP_KERNEL); 71 if (!priv->counter) 72 return -ENOMEM; 73 74 atomic_set(priv->counter, priv->modulus - 1); 75 76 err = nft_parse_register_store(ctx, tb[NFTA_NG_DREG], &priv->dreg, 77 NULL, NFT_DATA_VALUE, sizeof(u32)); 78 if (err < 0) 79 goto err; 80 81 return 0; 82 err: 83 kfree(priv->counter); 84 85 return err; 86 } 87 88 static int nft_ng_dump(struct sk_buff *skb, enum nft_registers dreg, 89 u32 modulus, enum nft_ng_types type, u32 offset) 90 { 91 if (nft_dump_register(skb, NFTA_NG_DREG, dreg)) 92 goto nla_put_failure; 93 if (nla_put_be32(skb, NFTA_NG_MODULUS, htonl(modulus))) 94 goto nla_put_failure; 95 if (nla_put_be32(skb, NFTA_NG_TYPE, htonl(type))) 96 goto nla_put_failure; 97 if (nla_put_be32(skb, NFTA_NG_OFFSET, htonl(offset))) 98 goto nla_put_failure; 99 100 return 0; 101 102 nla_put_failure: 103 return -1; 104 } 105 106 static int nft_ng_inc_dump(struct sk_buff *skb, const struct nft_expr *expr) 107 { 108 const struct nft_ng_inc *priv = nft_expr_priv(expr); 109 110 return nft_ng_dump(skb, priv->dreg, priv->modulus, NFT_NG_INCREMENTAL, 111 priv->offset); 112 } 113 114 static void nft_ng_inc_destroy(const struct nft_ctx *ctx, 115 const struct nft_expr *expr) 116 { 117 const struct nft_ng_inc *priv = nft_expr_priv(expr); 118 119 kfree(priv->counter); 120 } 121 122 struct nft_ng_random { 123 u8 dreg; 124 u32 modulus; 125 u32 offset; 126 }; 127 128 static u32 nft_ng_random_gen(struct nft_ng_random *priv) 129 { 130 struct rnd_state *state = this_cpu_ptr(&nft_numgen_prandom_state); 131 132 return reciprocal_scale(prandom_u32_state(state), priv->modulus) + 133 priv->offset; 134 } 135 136 static void nft_ng_random_eval(const struct nft_expr *expr, 137 struct nft_regs *regs, 138 const struct nft_pktinfo *pkt) 139 { 140 struct nft_ng_random *priv = nft_expr_priv(expr); 141 142 regs->data[priv->dreg] = nft_ng_random_gen(priv); 143 } 144 145 static int nft_ng_random_init(const struct nft_ctx *ctx, 146 const struct nft_expr *expr, 147 const struct nlattr * const tb[]) 148 { 149 struct nft_ng_random *priv = nft_expr_priv(expr); 150 151 if (tb[NFTA_NG_OFFSET]) 152 priv->offset = ntohl(nla_get_be32(tb[NFTA_NG_OFFSET])); 153 154 priv->modulus = ntohl(nla_get_be32(tb[NFTA_NG_MODULUS])); 155 if (priv->modulus == 0) 156 return -ERANGE; 157 158 if (priv->offset + priv->modulus - 1 < priv->offset) 159 return -EOVERFLOW; 160 161 prandom_init_once(&nft_numgen_prandom_state); 162 163 return nft_parse_register_store(ctx, tb[NFTA_NG_DREG], &priv->dreg, 164 NULL, NFT_DATA_VALUE, sizeof(u32)); 165 } 166 167 static int nft_ng_random_dump(struct sk_buff *skb, const struct nft_expr *expr) 168 { 169 const struct nft_ng_random *priv = nft_expr_priv(expr); 170 171 return nft_ng_dump(skb, priv->dreg, priv->modulus, NFT_NG_RANDOM, 172 priv->offset); 173 } 174 175 static struct nft_expr_type nft_ng_type; 176 static const struct nft_expr_ops nft_ng_inc_ops = { 177 .type = &nft_ng_type, 178 .size = NFT_EXPR_SIZE(sizeof(struct nft_ng_inc)), 179 .eval = nft_ng_inc_eval, 180 .init = nft_ng_inc_init, 181 .destroy = nft_ng_inc_destroy, 182 .dump = nft_ng_inc_dump, 183 }; 184 185 static const struct nft_expr_ops nft_ng_random_ops = { 186 .type = &nft_ng_type, 187 .size = NFT_EXPR_SIZE(sizeof(struct nft_ng_random)), 188 .eval = nft_ng_random_eval, 189 .init = nft_ng_random_init, 190 .dump = nft_ng_random_dump, 191 }; 192 193 static const struct nft_expr_ops * 194 nft_ng_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) 195 { 196 u32 type; 197 198 if (!tb[NFTA_NG_DREG] || 199 !tb[NFTA_NG_MODULUS] || 200 !tb[NFTA_NG_TYPE]) 201 return ERR_PTR(-EINVAL); 202 203 type = ntohl(nla_get_be32(tb[NFTA_NG_TYPE])); 204 205 switch (type) { 206 case NFT_NG_INCREMENTAL: 207 return &nft_ng_inc_ops; 208 case NFT_NG_RANDOM: 209 return &nft_ng_random_ops; 210 } 211 212 return ERR_PTR(-EINVAL); 213 } 214 215 static struct nft_expr_type nft_ng_type __read_mostly = { 216 .name = "numgen", 217 .select_ops = nft_ng_select_ops, 218 .policy = nft_ng_policy, 219 .maxattr = NFTA_NG_MAX, 220 .owner = THIS_MODULE, 221 }; 222 223 static int __init nft_ng_module_init(void) 224 { 225 return nft_register_expr(&nft_ng_type); 226 } 227 228 static void __exit nft_ng_module_exit(void) 229 { 230 nft_unregister_expr(&nft_ng_type); 231 } 232 233 module_init(nft_ng_module_init); 234 module_exit(nft_ng_module_exit); 235 236 MODULE_LICENSE("GPL"); 237 MODULE_AUTHOR("Laura Garcia <nevola@gmail.com>"); 238 MODULE_ALIAS_NFT_EXPR("numgen"); 239 MODULE_DESCRIPTION("nftables number generator module"); 240