1204076Spjd// SPDX-License-Identifier: GPL-2.0-only
2204076Spjd/*
3220272Spjd * Copyright (c) 2016 Laura Garcia <nevola@gmail.com>
4204076Spjd */
5204076Spjd
6204076Spjd#include <linux/kernel.h>
7204076Spjd#include <linux/init.h>
8204076Spjd#include <linux/module.h>
9204076Spjd#include <linux/netlink.h>
10204076Spjd#include <linux/netfilter.h>
11204076Spjd#include <linux/netfilter/nf_tables.h>
12204076Spjd#include <net/netfilter/nf_tables.h>
13204076Spjd#include <net/netfilter/nf_tables_core.h>
14204076Spjd#include <linux/jhash.h>
15204076Spjd
16204076Spjdstruct nft_jhash {
17204076Spjd	u8			sreg;
18204076Spjd	u8			dreg;
19204076Spjd	u8			len;
20204076Spjd	bool			autogen_seed:1;
21204076Spjd	u32			modulus;
22204076Spjd	u32			seed;
23204076Spjd	u32			offset;
24204076Spjd};
25204076Spjd
26204076Spjdstatic void nft_jhash_eval(const struct nft_expr *expr,
27204076Spjd			   struct nft_regs *regs,
28204076Spjd			   const struct nft_pktinfo *pkt)
29204076Spjd{
30204076Spjd	struct nft_jhash *priv = nft_expr_priv(expr);
31204076Spjd	const void *data = &regs->data[priv->sreg];
32204076Spjd	u32 h;
33204076Spjd
34204076Spjd	h = reciprocal_scale(jhash(data, priv->len, priv->seed),
35204076Spjd			     priv->modulus);
36204076Spjd
37204076Spjd	regs->data[priv->dreg] = h + priv->offset;
38220272Spjd}
39220272Spjd
40204076Spjdstruct nft_symhash {
41204076Spjd	u8			dreg;
42220272Spjd	u32			modulus;
43204076Spjd	u32			offset;
44218138Spjd};
45204076Spjd
46204076Spjdstatic void nft_symhash_eval(const struct nft_expr *expr,
47204076Spjd			     struct nft_regs *regs,
48204076Spjd			     const struct nft_pktinfo *pkt)
49211452Spjd{
50204076Spjd	struct nft_symhash *priv = nft_expr_priv(expr);
51204076Spjd	struct sk_buff *skb = pkt->skb;
52220272Spjd	u32 h;
53220272Spjd
54220272Spjd	h = reciprocal_scale(__skb_get_hash_symmetric(skb), priv->modulus);
55220272Spjd
56220272Spjd	regs->data[priv->dreg] = h + priv->offset;
57220272Spjd}
58220272Spjd
59220272Spjdstatic const struct nla_policy nft_hash_policy[NFTA_HASH_MAX + 1] = {
60220272Spjd	[NFTA_HASH_SREG]	= { .type = NLA_U32 },
61220272Spjd	[NFTA_HASH_DREG]	= { .type = NLA_U32 },
62218194Spjd	[NFTA_HASH_LEN]		= NLA_POLICY_MAX(NLA_BE32, 255),
63218194Spjd	[NFTA_HASH_MODULUS]	= { .type = NLA_U32 },
64204076Spjd	[NFTA_HASH_SEED]	= { .type = NLA_U32 },
65218139Spjd	[NFTA_HASH_OFFSET]	= { .type = NLA_U32 },
66218139Spjd	[NFTA_HASH_TYPE]	= { .type = NLA_U32 },
67218139Spjd};
68218139Spjd
69218139Spjdstatic int nft_jhash_init(const struct nft_ctx *ctx,
70218139Spjd			  const struct nft_expr *expr,
71218139Spjd			  const struct nlattr * const tb[])
72218139Spjd{
73218139Spjd	struct nft_jhash *priv = nft_expr_priv(expr);
74218139Spjd	u32 len;
75218139Spjd	int err;
76218139Spjd
77218139Spjd	if (!tb[NFTA_HASH_SREG] ||
78218139Spjd	    !tb[NFTA_HASH_DREG] ||
79218139Spjd	    !tb[NFTA_HASH_LEN]  ||
80218139Spjd	    !tb[NFTA_HASH_MODULUS])
81218139Spjd		return -EINVAL;
82218139Spjd
83218139Spjd	if (tb[NFTA_HASH_OFFSET])
84218148Spjd		priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET]));
85218139Spjd
86218139Spjd	err = nft_parse_u32_check(tb[NFTA_HASH_LEN], U8_MAX, &len);
87218139Spjd	if (err < 0)
88218139Spjd		return err;
89218139Spjd	if (len == 0)
90218139Spjd		return -ERANGE;
91218139Spjd
92218139Spjd	priv->len = len;
93218194Spjd
94218139Spjd	err = nft_parse_register_load(tb[NFTA_HASH_SREG], &priv->sreg, len);
95218194Spjd	if (err < 0)
96218194Spjd		return err;
97220273Spjd
98218194Spjd	priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS]));
99218194Spjd	if (priv->modulus < 1)
100220270Spjd		return -ERANGE;
101220270Spjd
102220270Spjd	if (priv->offset + priv->modulus - 1 < priv->offset)
103220270Spjd		return -EOVERFLOW;
104220270Spjd
105220270Spjd	if (tb[NFTA_HASH_SEED]) {
106220270Spjd		priv->seed = ntohl(nla_get_be32(tb[NFTA_HASH_SEED]));
107220270Spjd	} else {
108220270Spjd		priv->autogen_seed = true;
109220270Spjd		get_random_bytes(&priv->seed, sizeof(priv->seed));
110220270Spjd	}
111218194Spjd
112218194Spjd	return nft_parse_register_store(ctx, tb[NFTA_HASH_DREG], &priv->dreg,
113218194Spjd					NULL, NFT_DATA_VALUE, sizeof(u32));
114218194Spjd}
115218194Spjd
116218194Spjdstatic int nft_symhash_init(const struct nft_ctx *ctx,
117220272Spjd			    const struct nft_expr *expr,
118218194Spjd			    const struct nlattr * const tb[])
119231017Strociny{
120218194Spjd	struct nft_symhash *priv = nft_expr_priv(expr);
121218194Spjd
122220273Spjd	if (!tb[NFTA_HASH_DREG]    ||
123220273Spjd	    !tb[NFTA_HASH_MODULUS])
124220273Spjd		return -EINVAL;
125220273Spjd
126220273Spjd	if (tb[NFTA_HASH_OFFSET])
127220273Spjd		priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET]));
128220273Spjd
129220273Spjd	priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS]));
130220273Spjd	if (priv->modulus < 1)
131220273Spjd		return -ERANGE;
132220273Spjd
133220273Spjd	if (priv->offset + priv->modulus - 1 < priv->offset)
134220273Spjd		return -EOVERFLOW;
135220273Spjd
136220273Spjd	return nft_parse_register_store(ctx, tb[NFTA_HASH_DREG],
137220273Spjd					&priv->dreg, NULL, NFT_DATA_VALUE,
138220273Spjd					sizeof(u32));
139220272Spjd}
140220272Spjd
141220272Spjdstatic int nft_jhash_dump(struct sk_buff *skb,
142220272Spjd			  const struct nft_expr *expr, bool reset)
143220272Spjd{
144220272Spjd	const struct nft_jhash *priv = nft_expr_priv(expr);
145220272Spjd
146220272Spjd	if (nft_dump_register(skb, NFTA_HASH_SREG, priv->sreg))
147218194Spjd		goto nla_put_failure;
148218194Spjd	if (nft_dump_register(skb, NFTA_HASH_DREG, priv->dreg))
149218194Spjd		goto nla_put_failure;
150218194Spjd	if (nla_put_be32(skb, NFTA_HASH_LEN, htonl(priv->len)))
151218194Spjd		goto nla_put_failure;
152220273Spjd	if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus)))
153220273Spjd		goto nla_put_failure;
154220273Spjd	if (!priv->autogen_seed &&
155220273Spjd	    nla_put_be32(skb, NFTA_HASH_SEED, htonl(priv->seed)))
156218194Spjd		goto nla_put_failure;
157218194Spjd	if (priv->offset != 0)
158218194Spjd		if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset)))
159218194Spjd			goto nla_put_failure;
160218194Spjd	if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_JENKINS)))
161218194Spjd		goto nla_put_failure;
162218194Spjd	return 0;
163218194Spjd
164218194Spjdnla_put_failure:
165218139Spjd	return -1;
166218139Spjd}
167218139Spjd
168218139Spjdstatic bool nft_jhash_reduce(struct nft_regs_track *track,
169218139Spjd			     const struct nft_expr *expr)
170218139Spjd{
171218139Spjd	const struct nft_jhash *priv = nft_expr_priv(expr);
172218139Spjd
173218139Spjd	nft_reg_track_cancel(track, priv->dreg, sizeof(u32));
174218139Spjd
175218139Spjd	return false;
176218139Spjd}
177218139Spjd
178218139Spjdstatic int nft_symhash_dump(struct sk_buff *skb,
179218139Spjd			    const struct nft_expr *expr, bool reset)
180218139Spjd{
181218139Spjd	const struct nft_symhash *priv = nft_expr_priv(expr);
182218139Spjd
183218148Spjd	if (nft_dump_register(skb, NFTA_HASH_DREG, priv->dreg))
184240269Strociny		goto nla_put_failure;
185218194Spjd	if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus)))
186218148Spjd		goto nla_put_failure;
187218139Spjd	if (priv->offset != 0)
188218148Spjd		if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset)))
189218139Spjd			goto nla_put_failure;
190218148Spjd	if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_SYM)))
191218139Spjd		goto nla_put_failure;
192218194Spjd	return 0;
193218194Spjd
194218194Spjdnla_put_failure:
195218194Spjd	return -1;
196218194Spjd}
197218194Spjd
198218194Spjdstatic bool nft_symhash_reduce(struct nft_regs_track *track,
199220270Spjd			       const struct nft_expr *expr)
200220270Spjd{
201220270Spjd	struct nft_symhash *priv = nft_expr_priv(expr);
202220270Spjd	struct nft_symhash *symhash;
203220270Spjd
204220270Spjd	if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
205220270Spjd		nft_reg_track_update(track, expr, priv->dreg, sizeof(u32));
206220270Spjd		return false;
207220270Spjd	}
208220270Spjd
209220270Spjd	symhash = nft_expr_priv(track->regs[priv->dreg].selector);
210218194Spjd	if (priv->offset != symhash->offset ||
211218194Spjd	    priv->modulus != symhash->modulus) {
212218194Spjd		nft_reg_track_update(track, expr, priv->dreg, sizeof(u32));
213218194Spjd		return false;
214223143Ssobomax	}
215223143Ssobomax
216220272Spjd	if (!track->regs[priv->dreg].bitwise)
217218194Spjd		return true;
218231017Strociny
219220272Spjd	return false;
220220272Spjd}
221220272Spjd
222220272Spjdstatic struct nft_expr_type nft_hash_type;
223220272Spjdstatic const struct nft_expr_ops nft_jhash_ops = {
224220272Spjd	.type		= &nft_hash_type,
225220272Spjd	.size		= NFT_EXPR_SIZE(sizeof(struct nft_jhash)),
226220272Spjd	.eval		= nft_jhash_eval,
227218194Spjd	.init		= nft_jhash_init,
228220272Spjd	.dump		= nft_jhash_dump,
229218194Spjd	.reduce		= nft_jhash_reduce,
230218194Spjd};
231218194Spjd
232218194Spjdstatic const struct nft_expr_ops nft_symhash_ops = {
233	.type		= &nft_hash_type,
234	.size		= NFT_EXPR_SIZE(sizeof(struct nft_symhash)),
235	.eval		= nft_symhash_eval,
236	.init		= nft_symhash_init,
237	.dump		= nft_symhash_dump,
238	.reduce		= nft_symhash_reduce,
239};
240
241static const struct nft_expr_ops *
242nft_hash_select_ops(const struct nft_ctx *ctx,
243		    const struct nlattr * const tb[])
244{
245	u32 type;
246
247	if (!tb[NFTA_HASH_TYPE])
248		return &nft_jhash_ops;
249
250	type = ntohl(nla_get_be32(tb[NFTA_HASH_TYPE]));
251	switch (type) {
252	case NFT_HASH_SYM:
253		return &nft_symhash_ops;
254	case NFT_HASH_JENKINS:
255		return &nft_jhash_ops;
256	default:
257		break;
258	}
259	return ERR_PTR(-EOPNOTSUPP);
260}
261
262static struct nft_expr_type nft_hash_type __read_mostly = {
263	.name		= "hash",
264	.select_ops	= nft_hash_select_ops,
265	.policy		= nft_hash_policy,
266	.maxattr	= NFTA_HASH_MAX,
267	.owner		= THIS_MODULE,
268};
269
270static int __init nft_hash_module_init(void)
271{
272	return nft_register_expr(&nft_hash_type);
273}
274
275static void __exit nft_hash_module_exit(void)
276{
277	nft_unregister_expr(&nft_hash_type);
278}
279
280module_init(nft_hash_module_init);
281module_exit(nft_hash_module_exit);
282
283MODULE_LICENSE("GPL");
284MODULE_AUTHOR("Laura Garcia <nevola@gmail.com>");
285MODULE_ALIAS_NFT_EXPR("hash");
286MODULE_DESCRIPTION("Netfilter nftables hash module");
287