xref: /openbmc/linux/net/netfilter/nft_numgen.c (revision 8ede5890)
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