Linux 4.19-rc7
[linux-2.6/btrfs-unstable.git] / net / netfilter / nft_hash.c
blobc2d237144f747c4e4938ca4807668a400d1018c5
1 /*
2 * Copyright (c) 2016 Laura Garcia <nevola@gmail.com>
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.
8 */
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>
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;
31 static void nft_jhash_eval(const struct nft_expr *expr,
32 struct nft_regs *regs,
33 const struct nft_pktinfo *pkt)
35 struct nft_jhash *priv = nft_expr_priv(expr);
36 const void *data = &regs->data[priv->sreg];
37 u32 h;
39 h = reciprocal_scale(jhash(data, priv->len, priv->seed),
40 priv->modulus);
42 regs->data[priv->dreg] = h + priv->offset;
45 static void nft_jhash_map_eval(const struct nft_expr *expr,
46 struct nft_regs *regs,
47 const struct nft_pktinfo *pkt)
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;
56 result = reciprocal_scale(jhash(data, priv->len, priv->seed),
57 priv->modulus) + priv->offset;
59 found = map->ops->lookup(nft_net(pkt), map, &result, &ext);
60 if (!found)
61 return;
63 nft_data_copy(&regs->data[priv->dreg],
64 nft_set_ext_data(ext), map->dlen);
67 struct nft_symhash {
68 enum nft_registers dreg:8;
69 u32 modulus;
70 u32 offset;
71 struct nft_set *map;
74 static void nft_symhash_eval(const struct nft_expr *expr,
75 struct nft_regs *regs,
76 const struct nft_pktinfo *pkt)
78 struct nft_symhash *priv = nft_expr_priv(expr);
79 struct sk_buff *skb = pkt->skb;
80 u32 h;
82 h = reciprocal_scale(__skb_get_hash_symmetric(skb), priv->modulus);
84 regs->data[priv->dreg] = h + priv->offset;
87 static void nft_symhash_map_eval(const struct nft_expr *expr,
88 struct nft_regs *regs,
89 const struct nft_pktinfo *pkt)
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;
98 result = reciprocal_scale(__skb_get_hash_symmetric(skb),
99 priv->modulus) + priv->offset;
101 found = map->ops->lookup(nft_net(pkt), map, &result, &ext);
102 if (!found)
103 return;
105 nft_data_copy(&regs->data[priv->dreg],
106 nft_set_ext_data(ext), map->dlen);
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 },
122 static int nft_jhash_init(const struct nft_ctx *ctx,
123 const struct nft_expr *expr,
124 const struct nlattr * const tb[])
126 struct nft_jhash *priv = nft_expr_priv(expr);
127 u32 len;
128 int err;
130 if (!tb[NFTA_HASH_SREG] ||
131 !tb[NFTA_HASH_DREG] ||
132 !tb[NFTA_HASH_LEN] ||
133 !tb[NFTA_HASH_MODULUS])
134 return -EINVAL;
136 if (tb[NFTA_HASH_OFFSET])
137 priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET]));
139 priv->sreg = nft_parse_register(tb[NFTA_HASH_SREG]);
140 priv->dreg = nft_parse_register(tb[NFTA_HASH_DREG]);
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;
148 priv->len = len;
150 priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS]));
151 if (priv->modulus < 1)
152 return -ERANGE;
154 if (priv->offset + priv->modulus - 1 < priv->offset)
155 return -EOVERFLOW;
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));
164 return nft_validate_register_load(priv->sreg, len) &&
165 nft_validate_register_store(ctx, priv->dreg, NULL,
166 NFT_DATA_VALUE, sizeof(u32));
169 static int nft_jhash_map_init(const struct nft_ctx *ctx,
170 const struct nft_expr *expr,
171 const struct nlattr * const tb[])
173 struct nft_jhash *priv = nft_expr_priv(expr);
174 u8 genmask = nft_genmask_next(ctx->net);
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);
183 static int nft_symhash_init(const struct nft_ctx *ctx,
184 const struct nft_expr *expr,
185 const struct nlattr * const tb[])
187 struct nft_symhash *priv = nft_expr_priv(expr);
189 if (!tb[NFTA_HASH_DREG] ||
190 !tb[NFTA_HASH_MODULUS])
191 return -EINVAL;
193 if (tb[NFTA_HASH_OFFSET])
194 priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET]));
196 priv->dreg = nft_parse_register(tb[NFTA_HASH_DREG]);
198 priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS]));
199 if (priv->modulus <= 1)
200 return -ERANGE;
202 if (priv->offset + priv->modulus - 1 < priv->offset)
203 return -EOVERFLOW;
205 return nft_validate_register_store(ctx, priv->dreg, NULL,
206 NFT_DATA_VALUE, sizeof(u32));
209 static int nft_symhash_map_init(const struct nft_ctx *ctx,
210 const struct nft_expr *expr,
211 const struct nlattr * const tb[])
213 struct nft_jhash *priv = nft_expr_priv(expr);
214 u8 genmask = nft_genmask_next(ctx->net);
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);
223 static int nft_jhash_dump(struct sk_buff *skb,
224 const struct nft_expr *expr)
226 const struct nft_jhash *priv = nft_expr_priv(expr);
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;
246 nla_put_failure:
247 return -1;
250 static int nft_jhash_map_dump(struct sk_buff *skb,
251 const struct nft_expr *expr)
253 const struct nft_jhash *priv = nft_expr_priv(expr);
255 if (nft_jhash_dump(skb, expr) ||
256 nla_put_string(skb, NFTA_HASH_SET_NAME, priv->map->name))
257 return -1;
259 return 0;
262 static int nft_symhash_dump(struct sk_buff *skb,
263 const struct nft_expr *expr)
265 const struct nft_symhash *priv = nft_expr_priv(expr);
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;
278 nla_put_failure:
279 return -1;
282 static int nft_symhash_map_dump(struct sk_buff *skb,
283 const struct nft_expr *expr)
285 const struct nft_symhash *priv = nft_expr_priv(expr);
287 if (nft_symhash_dump(skb, expr) ||
288 nla_put_string(skb, NFTA_HASH_SET_NAME, priv->map->name))
289 return -1;
291 return 0;
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,
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,
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,
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,
327 static const struct nft_expr_ops *
328 nft_hash_select_ops(const struct nft_ctx *ctx,
329 const struct nlattr * const tb[])
331 u32 type;
333 if (!tb[NFTA_HASH_TYPE])
334 return &nft_jhash_ops;
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;
349 return ERR_PTR(-EOPNOTSUPP);
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,
360 static int __init nft_hash_module_init(void)
362 return nft_register_expr(&nft_hash_type);
365 static void __exit nft_hash_module_exit(void)
367 nft_unregister_expr(&nft_hash_type);
370 module_init(nft_hash_module_init);
371 module_exit(nft_hash_module_exit);
373 MODULE_LICENSE("GPL");
374 MODULE_AUTHOR("Laura Garcia <nevola@gmail.com>");
375 MODULE_ALIAS_NFT_EXPR("hash");