xref: /openbmc/linux/net/netfilter/nft_hash.c (revision 965f22bc)
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 
10 #include <linux/kernel.h>
11 #include <linux/init.h>
12 #include <linux/module.h>
13 #include <linux/netlink.h>
14 #include <linux/netfilter.h>
15 #include <linux/netfilter/nf_tables.h>
16 #include <net/netfilter/nf_tables.h>
17 #include <net/netfilter/nf_tables_core.h>
18 #include <linux/jhash.h>
19 
20 struct nft_jhash {
21 	enum nft_registers      sreg:8;
22 	enum nft_registers      dreg:8;
23 	u8			len;
24 	bool			autogen_seed:1;
25 	u32			modulus;
26 	u32			seed;
27 	u32			offset;
28 	struct nft_set		*map;
29 };
30 
31 static void nft_jhash_eval(const struct nft_expr *expr,
32 			   struct nft_regs *regs,
33 			   const struct nft_pktinfo *pkt)
34 {
35 	struct nft_jhash *priv = nft_expr_priv(expr);
36 	const void *data = &regs->data[priv->sreg];
37 	u32 h;
38 
39 	h = reciprocal_scale(jhash(data, priv->len, priv->seed),
40 			     priv->modulus);
41 
42 	regs->data[priv->dreg] = h + priv->offset;
43 }
44 
45 static void nft_jhash_map_eval(const struct nft_expr *expr,
46 			       struct nft_regs *regs,
47 			       const struct nft_pktinfo *pkt)
48 {
49 	struct nft_jhash *priv = nft_expr_priv(expr);
50 	const void *data = &regs->data[priv->sreg];
51 	const struct nft_set *map = priv->map;
52 	const struct nft_set_ext *ext;
53 	u32 result;
54 	bool found;
55 
56 	result = reciprocal_scale(jhash(data, priv->len, priv->seed),
57 					priv->modulus) + priv->offset;
58 
59 	found = map->ops->lookup(nft_net(pkt), map, &result, &ext);
60 	if (!found)
61 		return;
62 
63 	nft_data_copy(&regs->data[priv->dreg],
64 		      nft_set_ext_data(ext), map->dlen);
65 }
66 
67 struct nft_symhash {
68 	enum nft_registers      dreg:8;
69 	u32			modulus;
70 	u32			offset;
71 	struct nft_set		*map;
72 };
73 
74 static void nft_symhash_eval(const struct nft_expr *expr,
75 			     struct nft_regs *regs,
76 			     const struct nft_pktinfo *pkt)
77 {
78 	struct nft_symhash *priv = nft_expr_priv(expr);
79 	struct sk_buff *skb = pkt->skb;
80 	u32 h;
81 
82 	h = reciprocal_scale(__skb_get_hash_symmetric(skb), priv->modulus);
83 
84 	regs->data[priv->dreg] = h + priv->offset;
85 }
86 
87 static void nft_symhash_map_eval(const struct nft_expr *expr,
88 				 struct nft_regs *regs,
89 				 const struct nft_pktinfo *pkt)
90 {
91 	struct nft_symhash *priv = nft_expr_priv(expr);
92 	struct sk_buff *skb = pkt->skb;
93 	const struct nft_set *map = priv->map;
94 	const struct nft_set_ext *ext;
95 	u32 result;
96 	bool found;
97 
98 	result = reciprocal_scale(__skb_get_hash_symmetric(skb),
99 				  priv->modulus) + priv->offset;
100 
101 	found = map->ops->lookup(nft_net(pkt), map, &result, &ext);
102 	if (!found)
103 		return;
104 
105 	nft_data_copy(&regs->data[priv->dreg],
106 		      nft_set_ext_data(ext), map->dlen);
107 }
108 
109 static const struct nla_policy nft_hash_policy[NFTA_HASH_MAX + 1] = {
110 	[NFTA_HASH_SREG]	= { .type = NLA_U32 },
111 	[NFTA_HASH_DREG]	= { .type = NLA_U32 },
112 	[NFTA_HASH_LEN]		= { .type = NLA_U32 },
113 	[NFTA_HASH_MODULUS]	= { .type = NLA_U32 },
114 	[NFTA_HASH_SEED]	= { .type = NLA_U32 },
115 	[NFTA_HASH_OFFSET]	= { .type = NLA_U32 },
116 	[NFTA_HASH_TYPE]	= { .type = NLA_U32 },
117 	[NFTA_HASH_SET_NAME]	= { .type = NLA_STRING,
118 				    .len = NFT_SET_MAXNAMELEN - 1 },
119 	[NFTA_HASH_SET_ID]	= { .type = NLA_U32 },
120 };
121 
122 static int nft_jhash_init(const struct nft_ctx *ctx,
123 			  const struct nft_expr *expr,
124 			  const struct nlattr * const tb[])
125 {
126 	struct nft_jhash *priv = nft_expr_priv(expr);
127 	u32 len;
128 	int err;
129 
130 	if (!tb[NFTA_HASH_SREG] ||
131 	    !tb[NFTA_HASH_DREG] ||
132 	    !tb[NFTA_HASH_LEN]  ||
133 	    !tb[NFTA_HASH_MODULUS])
134 		return -EINVAL;
135 
136 	if (tb[NFTA_HASH_OFFSET])
137 		priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET]));
138 
139 	priv->sreg = nft_parse_register(tb[NFTA_HASH_SREG]);
140 	priv->dreg = nft_parse_register(tb[NFTA_HASH_DREG]);
141 
142 	err = nft_parse_u32_check(tb[NFTA_HASH_LEN], U8_MAX, &len);
143 	if (err < 0)
144 		return err;
145 	if (len == 0)
146 		return -ERANGE;
147 
148 	priv->len = len;
149 
150 	priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS]));
151 	if (priv->modulus < 1)
152 		return -ERANGE;
153 
154 	if (priv->offset + priv->modulus - 1 < priv->offset)
155 		return -EOVERFLOW;
156 
157 	if (tb[NFTA_HASH_SEED]) {
158 		priv->seed = ntohl(nla_get_be32(tb[NFTA_HASH_SEED]));
159 	} else {
160 		priv->autogen_seed = true;
161 		get_random_bytes(&priv->seed, sizeof(priv->seed));
162 	}
163 
164 	return nft_validate_register_load(priv->sreg, len) &&
165 	       nft_validate_register_store(ctx, priv->dreg, NULL,
166 					   NFT_DATA_VALUE, sizeof(u32));
167 }
168 
169 static int nft_jhash_map_init(const struct nft_ctx *ctx,
170 			      const struct nft_expr *expr,
171 			      const struct nlattr * const tb[])
172 {
173 	struct nft_jhash *priv = nft_expr_priv(expr);
174 	u8 genmask = nft_genmask_next(ctx->net);
175 
176 	nft_jhash_init(ctx, expr, tb);
177 	priv->map = nft_set_lookup_global(ctx->net, ctx->table,
178 					  tb[NFTA_HASH_SET_NAME],
179 					  tb[NFTA_HASH_SET_ID], genmask);
180 	return PTR_ERR_OR_ZERO(priv->map);
181 }
182 
183 static int nft_symhash_init(const struct nft_ctx *ctx,
184 			    const struct nft_expr *expr,
185 			    const struct nlattr * const tb[])
186 {
187 	struct nft_symhash *priv = nft_expr_priv(expr);
188 
189 	if (!tb[NFTA_HASH_DREG]    ||
190 	    !tb[NFTA_HASH_MODULUS])
191 		return -EINVAL;
192 
193 	if (tb[NFTA_HASH_OFFSET])
194 		priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET]));
195 
196 	priv->dreg = nft_parse_register(tb[NFTA_HASH_DREG]);
197 
198 	priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS]));
199 	if (priv->modulus <= 1)
200 		return -ERANGE;
201 
202 	if (priv->offset + priv->modulus - 1 < priv->offset)
203 		return -EOVERFLOW;
204 
205 	return nft_validate_register_store(ctx, priv->dreg, NULL,
206 					   NFT_DATA_VALUE, sizeof(u32));
207 }
208 
209 static int nft_symhash_map_init(const struct nft_ctx *ctx,
210 				const struct nft_expr *expr,
211 				const struct nlattr * const tb[])
212 {
213 	struct nft_jhash *priv = nft_expr_priv(expr);
214 	u8 genmask = nft_genmask_next(ctx->net);
215 
216 	nft_symhash_init(ctx, expr, tb);
217 	priv->map = nft_set_lookup_global(ctx->net, ctx->table,
218 					  tb[NFTA_HASH_SET_NAME],
219 					  tb[NFTA_HASH_SET_ID], genmask);
220 	return PTR_ERR_OR_ZERO(priv->map);
221 }
222 
223 static int nft_jhash_dump(struct sk_buff *skb,
224 			  const struct nft_expr *expr)
225 {
226 	const struct nft_jhash *priv = nft_expr_priv(expr);
227 
228 	if (nft_dump_register(skb, NFTA_HASH_SREG, priv->sreg))
229 		goto nla_put_failure;
230 	if (nft_dump_register(skb, NFTA_HASH_DREG, priv->dreg))
231 		goto nla_put_failure;
232 	if (nla_put_be32(skb, NFTA_HASH_LEN, htonl(priv->len)))
233 		goto nla_put_failure;
234 	if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus)))
235 		goto nla_put_failure;
236 	if (!priv->autogen_seed &&
237 	    nla_put_be32(skb, NFTA_HASH_SEED, htonl(priv->seed)))
238 		goto nla_put_failure;
239 	if (priv->offset != 0)
240 		if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset)))
241 			goto nla_put_failure;
242 	if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_JENKINS)))
243 		goto nla_put_failure;
244 	return 0;
245 
246 nla_put_failure:
247 	return -1;
248 }
249 
250 static int nft_jhash_map_dump(struct sk_buff *skb,
251 			       const struct nft_expr *expr)
252 {
253 	const struct nft_jhash *priv = nft_expr_priv(expr);
254 
255 	if (nft_jhash_dump(skb, expr) ||
256 	    nla_put_string(skb, NFTA_HASH_SET_NAME, priv->map->name))
257 		return -1;
258 
259 	return 0;
260 }
261 
262 static int nft_symhash_dump(struct sk_buff *skb,
263 			    const struct nft_expr *expr)
264 {
265 	const struct nft_symhash *priv = nft_expr_priv(expr);
266 
267 	if (nft_dump_register(skb, NFTA_HASH_DREG, priv->dreg))
268 		goto nla_put_failure;
269 	if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus)))
270 		goto nla_put_failure;
271 	if (priv->offset != 0)
272 		if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset)))
273 			goto nla_put_failure;
274 	if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_SYM)))
275 		goto nla_put_failure;
276 	return 0;
277 
278 nla_put_failure:
279 	return -1;
280 }
281 
282 static int nft_symhash_map_dump(struct sk_buff *skb,
283 				const struct nft_expr *expr)
284 {
285 	const struct nft_symhash *priv = nft_expr_priv(expr);
286 
287 	if (nft_symhash_dump(skb, expr) ||
288 	    nla_put_string(skb, NFTA_HASH_SET_NAME, priv->map->name))
289 		return -1;
290 
291 	return 0;
292 }
293 
294 static struct nft_expr_type nft_hash_type;
295 static const struct nft_expr_ops nft_jhash_ops = {
296 	.type		= &nft_hash_type,
297 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_jhash)),
298 	.eval		= nft_jhash_eval,
299 	.init		= nft_jhash_init,
300 	.dump		= nft_jhash_dump,
301 };
302 
303 static const struct nft_expr_ops nft_jhash_map_ops = {
304 	.type		= &nft_hash_type,
305 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_jhash)),
306 	.eval		= nft_jhash_map_eval,
307 	.init		= nft_jhash_map_init,
308 	.dump		= nft_jhash_map_dump,
309 };
310 
311 static const struct nft_expr_ops nft_symhash_ops = {
312 	.type		= &nft_hash_type,
313 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_symhash)),
314 	.eval		= nft_symhash_eval,
315 	.init		= nft_symhash_init,
316 	.dump		= nft_symhash_dump,
317 };
318 
319 static const struct nft_expr_ops nft_symhash_map_ops = {
320 	.type		= &nft_hash_type,
321 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_symhash)),
322 	.eval		= nft_symhash_map_eval,
323 	.init		= nft_symhash_map_init,
324 	.dump		= nft_symhash_map_dump,
325 };
326 
327 static const struct nft_expr_ops *
328 nft_hash_select_ops(const struct nft_ctx *ctx,
329 		    const struct nlattr * const tb[])
330 {
331 	u32 type;
332 
333 	if (!tb[NFTA_HASH_TYPE])
334 		return &nft_jhash_ops;
335 
336 	type = ntohl(nla_get_be32(tb[NFTA_HASH_TYPE]));
337 	switch (type) {
338 	case NFT_HASH_SYM:
339 		if (tb[NFTA_HASH_SET_NAME])
340 			return &nft_symhash_map_ops;
341 		return &nft_symhash_ops;
342 	case NFT_HASH_JENKINS:
343 		if (tb[NFTA_HASH_SET_NAME])
344 			return &nft_jhash_map_ops;
345 		return &nft_jhash_ops;
346 	default:
347 		break;
348 	}
349 	return ERR_PTR(-EOPNOTSUPP);
350 }
351 
352 static struct nft_expr_type nft_hash_type __read_mostly = {
353 	.name		= "hash",
354 	.select_ops	= nft_hash_select_ops,
355 	.policy		= nft_hash_policy,
356 	.maxattr	= NFTA_HASH_MAX,
357 	.owner		= THIS_MODULE,
358 };
359 
360 static int __init nft_hash_module_init(void)
361 {
362 	return nft_register_expr(&nft_hash_type);
363 }
364 
365 static void __exit nft_hash_module_exit(void)
366 {
367 	nft_unregister_expr(&nft_hash_type);
368 }
369 
370 module_init(nft_hash_module_init);
371 module_exit(nft_hash_module_exit);
372 
373 MODULE_LICENSE("GPL");
374 MODULE_AUTHOR("Laura Garcia <nevola@gmail.com>");
375 MODULE_ALIAS_NFT_EXPR("hash");
376