netfilter: fix ptr_ret.cocci warnings
[linux-2.6-block.git] / net / netfilter / nft_hash.c
CommitLineData
cb1b69b0
LGL
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
511040ee 20struct nft_jhash {
cb1b69b0
LGL
21 enum nft_registers sreg:8;
22 enum nft_registers dreg:8;
23 u8 len;
79e09ef9 24 bool autogen_seed:1;
cb1b69b0
LGL
25 u32 modulus;
26 u32 seed;
70ca767e 27 u32 offset;
b9ccc07e 28 struct nft_set *map;
cb1b69b0
LGL
29};
30
511040ee
LGL
31static void nft_jhash_eval(const struct nft_expr *expr,
32 struct nft_regs *regs,
33 const struct nft_pktinfo *pkt)
cb1b69b0 34{
511040ee 35 struct nft_jhash *priv = nft_expr_priv(expr);
cb1b69b0 36 const void *data = &regs->data[priv->sreg];
70ca767e 37 u32 h;
cb1b69b0 38
b9ccc07e
LGL
39 h = reciprocal_scale(jhash(data, priv->len, priv->seed),
40 priv->modulus);
41
70ca767e 42 regs->data[priv->dreg] = h + priv->offset;
cb1b69b0
LGL
43}
44
b9ccc07e
LGL
45static 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
3206cade
LGL
67struct nft_symhash {
68 enum nft_registers dreg:8;
69 u32 modulus;
70 u32 offset;
b9ccc07e 71 struct nft_set *map;
3206cade
LGL
72};
73
74static 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
b9ccc07e
LGL
87static 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
a5e57336 109static const struct nla_policy nft_hash_policy[NFTA_HASH_MAX + 1] = {
cb1b69b0
LGL
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 },
5751e175 115 [NFTA_HASH_OFFSET] = { .type = NLA_U32 },
3206cade 116 [NFTA_HASH_TYPE] = { .type = NLA_U32 },
b9ccc07e
LGL
117 [NFTA_HASH_SET_NAME] = { .type = NLA_STRING,
118 .len = NFT_SET_MAXNAMELEN - 1 },
119 [NFTA_HASH_SET_ID] = { .type = NLA_U32 },
cb1b69b0
LGL
120};
121
511040ee
LGL
122static int nft_jhash_init(const struct nft_ctx *ctx,
123 const struct nft_expr *expr,
124 const struct nlattr * const tb[])
cb1b69b0 125{
511040ee 126 struct nft_jhash *priv = nft_expr_priv(expr);
cb1b69b0 127 u32 len;
abd66e9f 128 int err;
cb1b69b0
LGL
129
130 if (!tb[NFTA_HASH_SREG] ||
131 !tb[NFTA_HASH_DREG] ||
132 !tb[NFTA_HASH_LEN] ||
cb1b69b0
LGL
133 !tb[NFTA_HASH_MODULUS])
134 return -EINVAL;
135
70ca767e
LGL
136 if (tb[NFTA_HASH_OFFSET])
137 priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET]));
138
cb1b69b0
LGL
139 priv->sreg = nft_parse_register(tb[NFTA_HASH_SREG]);
140 priv->dreg = nft_parse_register(tb[NFTA_HASH_DREG]);
141
abd66e9f
LGL
142 err = nft_parse_u32_check(tb[NFTA_HASH_LEN], U8_MAX, &len);
143 if (err < 0)
144 return err;
145 if (len == 0)
cb1b69b0
LGL
146 return -ERANGE;
147
148 priv->len = len;
149
150 priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS]));
75e72f05 151 if (priv->modulus < 1)
cb1b69b0
LGL
152 return -ERANGE;
153
14e2dee0 154 if (priv->offset + priv->modulus - 1 < priv->offset)
70ca767e
LGL
155 return -EOVERFLOW;
156
79e09ef9 157 if (tb[NFTA_HASH_SEED]) {
f86dab3a 158 priv->seed = ntohl(nla_get_be32(tb[NFTA_HASH_SEED]));
79e09ef9
LZ
159 } else {
160 priv->autogen_seed = true;
f86dab3a 161 get_random_bytes(&priv->seed, sizeof(priv->seed));
79e09ef9 162 }
cb1b69b0
LGL
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
b9ccc07e
LGL
169static 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);
7849958b 180 return PTR_ERR_OR_ZERO(priv->map);
b9ccc07e
LGL
181}
182
3206cade
LGL
183static 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
b9ccc07e
LGL
209static 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);
7849958b 220 return PTR_ERR_OR_ZERO(priv->map);
b9ccc07e
LGL
221}
222
511040ee
LGL
223static int nft_jhash_dump(struct sk_buff *skb,
224 const struct nft_expr *expr)
cb1b69b0 225{
511040ee 226 const struct nft_jhash *priv = nft_expr_priv(expr);
cb1b69b0
LGL
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;
7073b16f 232 if (nla_put_be32(skb, NFTA_HASH_LEN, htonl(priv->len)))
cb1b69b0 233 goto nla_put_failure;
7073b16f 234 if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus)))
cb1b69b0 235 goto nla_put_failure;
79e09ef9
LZ
236 if (!priv->autogen_seed &&
237 nla_put_be32(skb, NFTA_HASH_SEED, htonl(priv->seed)))
cb1b69b0 238 goto nla_put_failure;
70ca767e
LGL
239 if (priv->offset != 0)
240 if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset)))
241 goto nla_put_failure;
3206cade
LGL
242 if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_JENKINS)))
243 goto nla_put_failure;
244 return 0;
245
246nla_put_failure:
247 return -1;
248}
249
b9ccc07e
LGL
250static 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
3206cade
LGL
262static 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;
cb1b69b0
LGL
276 return 0;
277
278nla_put_failure:
279 return -1;
280}
281
b9ccc07e
LGL
282static 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
cb1b69b0 294static struct nft_expr_type nft_hash_type;
511040ee 295static const struct nft_expr_ops nft_jhash_ops = {
cb1b69b0 296 .type = &nft_hash_type,
511040ee
LGL
297 .size = NFT_EXPR_SIZE(sizeof(struct nft_jhash)),
298 .eval = nft_jhash_eval,
299 .init = nft_jhash_init,
300 .dump = nft_jhash_dump,
cb1b69b0
LGL
301};
302
b9ccc07e
LGL
303static 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
3206cade
LGL
311static 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
b9ccc07e
LGL
319static 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
3206cade
LGL
327static const struct nft_expr_ops *
328nft_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:
b9ccc07e
LGL
339 if (tb[NFTA_HASH_SET_NAME])
340 return &nft_symhash_map_ops;
3206cade
LGL
341 return &nft_symhash_ops;
342 case NFT_HASH_JENKINS:
b9ccc07e
LGL
343 if (tb[NFTA_HASH_SET_NAME])
344 return &nft_jhash_map_ops;
3206cade
LGL
345 return &nft_jhash_ops;
346 default:
347 break;
348 }
349 return ERR_PTR(-EOPNOTSUPP);
350}
351
cb1b69b0
LGL
352static struct nft_expr_type nft_hash_type __read_mostly = {
353 .name = "hash",
d4ef3835 354 .select_ops = nft_hash_select_ops,
cb1b69b0
LGL
355 .policy = nft_hash_policy,
356 .maxattr = NFTA_HASH_MAX,
357 .owner = THIS_MODULE,
358};
359
360static int __init nft_hash_module_init(void)
361{
362 return nft_register_expr(&nft_hash_type);
363}
364
365static void __exit nft_hash_module_exit(void)
366{
367 nft_unregister_expr(&nft_hash_type);
368}
369
370module_init(nft_hash_module_init);
371module_exit(nft_hash_module_exit);
372
373MODULE_LICENSE("GPL");
374MODULE_AUTHOR("Laura Garcia <nevola@gmail.com>");
375MODULE_ALIAS_NFT_EXPR("hash");