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
nft_jhash_eval(const struct nft_expr * expr,struct nft_regs * regs,const struct nft_pktinfo * pkt)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 = ®s->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
nft_jhash_map_eval(const struct nft_expr * expr,struct nft_regs * regs,const struct nft_pktinfo * pkt)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 = ®s->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(®s->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
nft_symhash_eval(const struct nft_expr * expr,struct nft_regs * regs,const struct nft_pktinfo * pkt)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
nft_symhash_map_eval(const struct nft_expr * expr,struct nft_regs * regs,const struct nft_pktinfo * pkt)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(®s->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
nft_jhash_init(const struct nft_ctx * ctx,const struct nft_expr * expr,const struct nlattr * const tb[])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
nft_jhash_map_init(const struct nft_ctx * ctx,const struct nft_expr * expr,const struct nlattr * const tb[])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
nft_symhash_init(const struct nft_ctx * ctx,const struct nft_expr * expr,const struct nlattr * const tb[])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
nft_symhash_map_init(const struct nft_ctx * ctx,const struct nft_expr * expr,const struct nlattr * const tb[])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
nft_jhash_dump(struct sk_buff * skb,const struct nft_expr * expr)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
nft_jhash_map_dump(struct sk_buff * skb,const struct nft_expr * expr)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
nft_symhash_dump(struct sk_buff * skb,const struct nft_expr * expr)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
nft_symhash_map_dump(struct sk_buff * skb,const struct nft_expr * expr)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 *
nft_hash_select_ops(const struct nft_ctx * ctx,const struct nlattr * const tb[])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
nft_hash_module_init(void)360 static int __init nft_hash_module_init(void)
361 {
362 return nft_register_expr(&nft_hash_type);
363 }
364
nft_hash_module_exit(void)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