1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2016 Laura Garcia <nevola@gmail.com>
4  */
5 
6 #include <linux/kernel.h>
7 #include <linux/init.h>
8 #include <linux/module.h>
9 #include <linux/netlink.h>
10 #include <linux/netfilter.h>
11 #include <linux/netfilter/nf_tables.h>
12 #include <net/netfilter/nf_tables.h>
13 #include <net/netfilter/nf_tables_core.h>
14 #include <linux/jhash.h>
15 
16 struct nft_jhash {
17 	u8			sreg;
18 	u8			dreg;
19 	u8			len;
20 	bool			autogen_seed:1;
21 	u32			modulus;
22 	u32			seed;
23 	u32			offset;
24 };
25 
nft_jhash_eval(const struct nft_expr * expr,struct nft_regs * regs,const struct nft_pktinfo * pkt)26 static void nft_jhash_eval(const struct nft_expr *expr,
27 			   struct nft_regs *regs,
28 			   const struct nft_pktinfo *pkt)
29 {
30 	struct nft_jhash *priv = nft_expr_priv(expr);
31 	const void *data = &regs->data[priv->sreg];
32 	u32 h;
33 
34 	h = reciprocal_scale(jhash(data, priv->len, priv->seed),
35 			     priv->modulus);
36 
37 	regs->data[priv->dreg] = h + priv->offset;
38 }
39 
40 struct nft_symhash {
41 	u8			dreg;
42 	u32			modulus;
43 	u32			offset;
44 };
45 
nft_symhash_eval(const struct nft_expr * expr,struct nft_regs * regs,const struct nft_pktinfo * pkt)46 static void nft_symhash_eval(const struct nft_expr *expr,
47 			     struct nft_regs *regs,
48 			     const struct nft_pktinfo *pkt)
49 {
50 	struct nft_symhash *priv = nft_expr_priv(expr);
51 	struct sk_buff *skb = pkt->skb;
52 	u32 h;
53 
54 	h = reciprocal_scale(__skb_get_hash_symmetric_net(nft_net(pkt), skb),
55 			     priv->modulus);
56 
57 	regs->data[priv->dreg] = h + priv->offset;
58 }
59 
60 static const struct nla_policy nft_hash_policy[NFTA_HASH_MAX + 1] = {
61 	[NFTA_HASH_SREG]	= { .type = NLA_U32 },
62 	[NFTA_HASH_DREG]	= { .type = NLA_U32 },
63 	[NFTA_HASH_LEN]		= NLA_POLICY_MAX(NLA_BE32, 255),
64 	[NFTA_HASH_MODULUS]	= { .type = NLA_U32 },
65 	[NFTA_HASH_SEED]	= { .type = NLA_U32 },
66 	[NFTA_HASH_OFFSET]	= { .type = NLA_U32 },
67 	[NFTA_HASH_TYPE]	= { .type = NLA_U32 },
68 };
69 
nft_jhash_init(const struct nft_ctx * ctx,const struct nft_expr * expr,const struct nlattr * const tb[])70 static int nft_jhash_init(const struct nft_ctx *ctx,
71 			  const struct nft_expr *expr,
72 			  const struct nlattr * const tb[])
73 {
74 	struct nft_jhash *priv = nft_expr_priv(expr);
75 	u32 len;
76 	int err;
77 
78 	if (!tb[NFTA_HASH_SREG] ||
79 	    !tb[NFTA_HASH_DREG] ||
80 	    !tb[NFTA_HASH_LEN]  ||
81 	    !tb[NFTA_HASH_MODULUS])
82 		return -EINVAL;
83 
84 	if (tb[NFTA_HASH_OFFSET])
85 		priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET]));
86 
87 	err = nft_parse_u32_check(tb[NFTA_HASH_LEN], U8_MAX, &len);
88 	if (err < 0)
89 		return err;
90 	if (len == 0)
91 		return -ERANGE;
92 
93 	priv->len = len;
94 
95 	err = nft_parse_register_load(ctx, tb[NFTA_HASH_SREG], &priv->sreg, len);
96 	if (err < 0)
97 		return err;
98 
99 	priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS]));
100 	if (priv->modulus < 1)
101 		return -ERANGE;
102 
103 	if (priv->offset + priv->modulus - 1 < priv->offset)
104 		return -EOVERFLOW;
105 
106 	if (tb[NFTA_HASH_SEED]) {
107 		priv->seed = ntohl(nla_get_be32(tb[NFTA_HASH_SEED]));
108 	} else {
109 		priv->autogen_seed = true;
110 		get_random_bytes(&priv->seed, sizeof(priv->seed));
111 	}
112 
113 	return nft_parse_register_store(ctx, tb[NFTA_HASH_DREG], &priv->dreg,
114 					NULL, NFT_DATA_VALUE, sizeof(u32));
115 }
116 
nft_symhash_init(const struct nft_ctx * ctx,const struct nft_expr * expr,const struct nlattr * const tb[])117 static int nft_symhash_init(const struct nft_ctx *ctx,
118 			    const struct nft_expr *expr,
119 			    const struct nlattr * const tb[])
120 {
121 	struct nft_symhash *priv = nft_expr_priv(expr);
122 
123 	if (!tb[NFTA_HASH_DREG]    ||
124 	    !tb[NFTA_HASH_MODULUS])
125 		return -EINVAL;
126 
127 	if (tb[NFTA_HASH_OFFSET])
128 		priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET]));
129 
130 	priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS]));
131 	if (priv->modulus < 1)
132 		return -ERANGE;
133 
134 	if (priv->offset + priv->modulus - 1 < priv->offset)
135 		return -EOVERFLOW;
136 
137 	return nft_parse_register_store(ctx, tb[NFTA_HASH_DREG],
138 					&priv->dreg, NULL, NFT_DATA_VALUE,
139 					sizeof(u32));
140 }
141 
nft_jhash_dump(struct sk_buff * skb,const struct nft_expr * expr,bool reset)142 static int nft_jhash_dump(struct sk_buff *skb,
143 			  const struct nft_expr *expr, bool reset)
144 {
145 	const struct nft_jhash *priv = nft_expr_priv(expr);
146 
147 	if (nft_dump_register(skb, NFTA_HASH_SREG, priv->sreg))
148 		goto nla_put_failure;
149 	if (nft_dump_register(skb, NFTA_HASH_DREG, priv->dreg))
150 		goto nla_put_failure;
151 	if (nla_put_be32(skb, NFTA_HASH_LEN, htonl(priv->len)))
152 		goto nla_put_failure;
153 	if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus)))
154 		goto nla_put_failure;
155 	if (!priv->autogen_seed &&
156 	    nla_put_be32(skb, NFTA_HASH_SEED, htonl(priv->seed)))
157 		goto nla_put_failure;
158 	if (priv->offset != 0)
159 		if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset)))
160 			goto nla_put_failure;
161 	if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_JENKINS)))
162 		goto nla_put_failure;
163 	return 0;
164 
165 nla_put_failure:
166 	return -1;
167 }
168 
nft_jhash_reduce(struct nft_regs_track * track,const struct nft_expr * expr)169 static bool nft_jhash_reduce(struct nft_regs_track *track,
170 			     const struct nft_expr *expr)
171 {
172 	const struct nft_jhash *priv = nft_expr_priv(expr);
173 
174 	nft_reg_track_cancel(track, priv->dreg, sizeof(u32));
175 
176 	return false;
177 }
178 
nft_symhash_dump(struct sk_buff * skb,const struct nft_expr * expr,bool reset)179 static int nft_symhash_dump(struct sk_buff *skb,
180 			    const struct nft_expr *expr, bool reset)
181 {
182 	const struct nft_symhash *priv = nft_expr_priv(expr);
183 
184 	if (nft_dump_register(skb, NFTA_HASH_DREG, priv->dreg))
185 		goto nla_put_failure;
186 	if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus)))
187 		goto nla_put_failure;
188 	if (priv->offset != 0)
189 		if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset)))
190 			goto nla_put_failure;
191 	if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_SYM)))
192 		goto nla_put_failure;
193 	return 0;
194 
195 nla_put_failure:
196 	return -1;
197 }
198 
nft_symhash_reduce(struct nft_regs_track * track,const struct nft_expr * expr)199 static bool nft_symhash_reduce(struct nft_regs_track *track,
200 			       const struct nft_expr *expr)
201 {
202 	struct nft_symhash *priv = nft_expr_priv(expr);
203 	struct nft_symhash *symhash;
204 
205 	if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
206 		nft_reg_track_update(track, expr, priv->dreg, sizeof(u32));
207 		return false;
208 	}
209 
210 	symhash = nft_expr_priv(track->regs[priv->dreg].selector);
211 	if (priv->offset != symhash->offset ||
212 	    priv->modulus != symhash->modulus) {
213 		nft_reg_track_update(track, expr, priv->dreg, sizeof(u32));
214 		return false;
215 	}
216 
217 	if (!track->regs[priv->dreg].bitwise)
218 		return true;
219 
220 	return false;
221 }
222 
223 static struct nft_expr_type nft_hash_type;
224 static const struct nft_expr_ops nft_jhash_ops = {
225 	.type		= &nft_hash_type,
226 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_jhash)),
227 	.eval		= nft_jhash_eval,
228 	.init		= nft_jhash_init,
229 	.dump		= nft_jhash_dump,
230 	.reduce		= nft_jhash_reduce,
231 };
232 
233 static const struct nft_expr_ops nft_symhash_ops = {
234 	.type		= &nft_hash_type,
235 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_symhash)),
236 	.eval		= nft_symhash_eval,
237 	.init		= nft_symhash_init,
238 	.dump		= nft_symhash_dump,
239 	.reduce		= nft_symhash_reduce,
240 };
241 
242 static const struct nft_expr_ops *
nft_hash_select_ops(const struct nft_ctx * ctx,const struct nlattr * const tb[])243 nft_hash_select_ops(const struct nft_ctx *ctx,
244 		    const struct nlattr * const tb[])
245 {
246 	u32 type;
247 
248 	if (!tb[NFTA_HASH_TYPE])
249 		return &nft_jhash_ops;
250 
251 	type = ntohl(nla_get_be32(tb[NFTA_HASH_TYPE]));
252 	switch (type) {
253 	case NFT_HASH_SYM:
254 		return &nft_symhash_ops;
255 	case NFT_HASH_JENKINS:
256 		return &nft_jhash_ops;
257 	default:
258 		break;
259 	}
260 	return ERR_PTR(-EOPNOTSUPP);
261 }
262 
263 static struct nft_expr_type nft_hash_type __read_mostly = {
264 	.name		= "hash",
265 	.select_ops	= nft_hash_select_ops,
266 	.policy		= nft_hash_policy,
267 	.maxattr	= NFTA_HASH_MAX,
268 	.owner		= THIS_MODULE,
269 };
270 
nft_hash_module_init(void)271 static int __init nft_hash_module_init(void)
272 {
273 	return nft_register_expr(&nft_hash_type);
274 }
275 
nft_hash_module_exit(void)276 static void __exit nft_hash_module_exit(void)
277 {
278 	nft_unregister_expr(&nft_hash_type);
279 }
280 
281 module_init(nft_hash_module_init);
282 module_exit(nft_hash_module_exit);
283 
284 MODULE_LICENSE("GPL");
285 MODULE_AUTHOR("Laura Garcia <nevola@gmail.com>");
286 MODULE_ALIAS_NFT_EXPR("hash");
287 MODULE_DESCRIPTION("Netfilter nftables hash module");
288