Blame view

net/netfilter/nft_hash.c 6.28 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
2
3
  /*
   * Copyright (c) 2016 Laura Garcia <nevola@gmail.com>
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
4
5
6
7
8
9
10
11
12
13
14
   */
  
  #include <linux/kernel.h>
  #include <linux/init.h>
  #include <linux/module.h>
  #include <linux/netlink.h>
  #include <linux/netfilter.h>
  #include <linux/netfilter/nf_tables.h>
  #include <net/netfilter/nf_tables.h>
  #include <net/netfilter/nf_tables_core.h>
  #include <linux/jhash.h>
511040eea   Laura Garcia Liebana   netfilter: nft_ha...
15
  struct nft_jhash {
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
16
17
18
  	enum nft_registers      sreg:8;
  	enum nft_registers      dreg:8;
  	u8			len;
79e09ef96   Liping Zhang   netfilter: nft_ha...
19
  	bool			autogen_seed:1;
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
20
21
  	u32			modulus;
  	u32			seed;
70ca767ea   Laura Garcia Liebana   netfilter: nft_ha...
22
  	u32			offset;
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
23
  };
511040eea   Laura Garcia Liebana   netfilter: nft_ha...
24
25
26
  static void nft_jhash_eval(const struct nft_expr *expr,
  			   struct nft_regs *regs,
  			   const struct nft_pktinfo *pkt)
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
27
  {
511040eea   Laura Garcia Liebana   netfilter: nft_ha...
28
  	struct nft_jhash *priv = nft_expr_priv(expr);
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
29
  	const void *data = &regs->data[priv->sreg];
70ca767ea   Laura Garcia Liebana   netfilter: nft_ha...
30
  	u32 h;
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
31

b9ccc07e3   Laura Garcia Liebana   netfilter: nft_ha...
32
33
  	h = reciprocal_scale(jhash(data, priv->len, priv->seed),
  			     priv->modulus);
70ca767ea   Laura Garcia Liebana   netfilter: nft_ha...
34
  	regs->data[priv->dreg] = h + priv->offset;
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
35
  }
3206caded   Laura Garcia Liebana   netfilter: nft_ha...
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
  struct nft_symhash {
  	enum nft_registers      dreg:8;
  	u32			modulus;
  	u32			offset;
  };
  
  static void nft_symhash_eval(const struct nft_expr *expr,
  			     struct nft_regs *regs,
  			     const struct nft_pktinfo *pkt)
  {
  	struct nft_symhash *priv = nft_expr_priv(expr);
  	struct sk_buff *skb = pkt->skb;
  	u32 h;
  
  	h = reciprocal_scale(__skb_get_hash_symmetric(skb), priv->modulus);
  
  	regs->data[priv->dreg] = h + priv->offset;
  }
a5e573364   Wei Yongjun   netfilter: nft_ha...
54
  static const struct nla_policy nft_hash_policy[NFTA_HASH_MAX + 1] = {
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
55
56
57
58
59
  	[NFTA_HASH_SREG]	= { .type = NLA_U32 },
  	[NFTA_HASH_DREG]	= { .type = NLA_U32 },
  	[NFTA_HASH_LEN]		= { .type = NLA_U32 },
  	[NFTA_HASH_MODULUS]	= { .type = NLA_U32 },
  	[NFTA_HASH_SEED]	= { .type = NLA_U32 },
5751e175c   Liping Zhang   netfilter: nft_ha...
60
  	[NFTA_HASH_OFFSET]	= { .type = NLA_U32 },
3206caded   Laura Garcia Liebana   netfilter: nft_ha...
61
  	[NFTA_HASH_TYPE]	= { .type = NLA_U32 },
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
62
  };
511040eea   Laura Garcia Liebana   netfilter: nft_ha...
63
64
65
  static int nft_jhash_init(const struct nft_ctx *ctx,
  			  const struct nft_expr *expr,
  			  const struct nlattr * const tb[])
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
66
  {
511040eea   Laura Garcia Liebana   netfilter: nft_ha...
67
  	struct nft_jhash *priv = nft_expr_priv(expr);
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
68
  	u32 len;
abd66e9f3   Laura Garcia Liebana   netfilter: nft_ha...
69
  	int err;
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
70
71
72
73
  
  	if (!tb[NFTA_HASH_SREG] ||
  	    !tb[NFTA_HASH_DREG] ||
  	    !tb[NFTA_HASH_LEN]  ||
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
74
75
  	    !tb[NFTA_HASH_MODULUS])
  		return -EINVAL;
70ca767ea   Laura Garcia Liebana   netfilter: nft_ha...
76
77
  	if (tb[NFTA_HASH_OFFSET])
  		priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET]));
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
78
79
  	priv->sreg = nft_parse_register(tb[NFTA_HASH_SREG]);
  	priv->dreg = nft_parse_register(tb[NFTA_HASH_DREG]);
abd66e9f3   Laura Garcia Liebana   netfilter: nft_ha...
80
81
82
83
  	err = nft_parse_u32_check(tb[NFTA_HASH_LEN], U8_MAX, &len);
  	if (err < 0)
  		return err;
  	if (len == 0)
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
84
85
86
87
88
  		return -ERANGE;
  
  	priv->len = len;
  
  	priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS]));
75e72f054   Laura Garcia Liebana   netfilter: nft_nu...
89
  	if (priv->modulus < 1)
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
90
  		return -ERANGE;
14e2dee09   Laura Garcia Liebana   netfilter: nft_ha...
91
  	if (priv->offset + priv->modulus - 1 < priv->offset)
70ca767ea   Laura Garcia Liebana   netfilter: nft_ha...
92
  		return -EOVERFLOW;
79e09ef96   Liping Zhang   netfilter: nft_ha...
93
  	if (tb[NFTA_HASH_SEED]) {
f86dab3aa   Pablo Neira Ayuso   netfilter: nft_ha...
94
  		priv->seed = ntohl(nla_get_be32(tb[NFTA_HASH_SEED]));
79e09ef96   Liping Zhang   netfilter: nft_ha...
95
96
  	} else {
  		priv->autogen_seed = true;
f86dab3aa   Pablo Neira Ayuso   netfilter: nft_ha...
97
  		get_random_bytes(&priv->seed, sizeof(priv->seed));
79e09ef96   Liping Zhang   netfilter: nft_ha...
98
  	}
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
99
100
101
102
103
  
  	return nft_validate_register_load(priv->sreg, len) &&
  	       nft_validate_register_store(ctx, priv->dreg, NULL,
  					   NFT_DATA_VALUE, sizeof(u32));
  }
3206caded   Laura Garcia Liebana   netfilter: nft_ha...
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
  static int nft_symhash_init(const struct nft_ctx *ctx,
  			    const struct nft_expr *expr,
  			    const struct nlattr * const tb[])
  {
  	struct nft_symhash *priv = nft_expr_priv(expr);
  
  	if (!tb[NFTA_HASH_DREG]    ||
  	    !tb[NFTA_HASH_MODULUS])
  		return -EINVAL;
  
  	if (tb[NFTA_HASH_OFFSET])
  		priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET]));
  
  	priv->dreg = nft_parse_register(tb[NFTA_HASH_DREG]);
  
  	priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS]));
28b1d6ef5   Laura Garcia Liebana   netfilter: nft_ha...
120
  	if (priv->modulus < 1)
3206caded   Laura Garcia Liebana   netfilter: nft_ha...
121
122
123
124
125
126
127
128
  		return -ERANGE;
  
  	if (priv->offset + priv->modulus - 1 < priv->offset)
  		return -EOVERFLOW;
  
  	return nft_validate_register_store(ctx, priv->dreg, NULL,
  					   NFT_DATA_VALUE, sizeof(u32));
  }
511040eea   Laura Garcia Liebana   netfilter: nft_ha...
129
130
  static int nft_jhash_dump(struct sk_buff *skb,
  			  const struct nft_expr *expr)
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
131
  {
511040eea   Laura Garcia Liebana   netfilter: nft_ha...
132
  	const struct nft_jhash *priv = nft_expr_priv(expr);
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
133
134
135
136
137
  
  	if (nft_dump_register(skb, NFTA_HASH_SREG, priv->sreg))
  		goto nla_put_failure;
  	if (nft_dump_register(skb, NFTA_HASH_DREG, priv->dreg))
  		goto nla_put_failure;
7073b16f3   Pablo Neira Ayuso   netfilter: nf_tab...
138
  	if (nla_put_be32(skb, NFTA_HASH_LEN, htonl(priv->len)))
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
139
  		goto nla_put_failure;
7073b16f3   Pablo Neira Ayuso   netfilter: nf_tab...
140
  	if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus)))
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
141
  		goto nla_put_failure;
79e09ef96   Liping Zhang   netfilter: nft_ha...
142
143
  	if (!priv->autogen_seed &&
  	    nla_put_be32(skb, NFTA_HASH_SEED, htonl(priv->seed)))
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
144
  		goto nla_put_failure;
70ca767ea   Laura Garcia Liebana   netfilter: nft_ha...
145
146
147
  	if (priv->offset != 0)
  		if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset)))
  			goto nla_put_failure;
3206caded   Laura Garcia Liebana   netfilter: nft_ha...
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
  	if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_JENKINS)))
  		goto nla_put_failure;
  	return 0;
  
  nla_put_failure:
  	return -1;
  }
  
  static int nft_symhash_dump(struct sk_buff *skb,
  			    const struct nft_expr *expr)
  {
  	const struct nft_symhash *priv = nft_expr_priv(expr);
  
  	if (nft_dump_register(skb, NFTA_HASH_DREG, priv->dreg))
  		goto nla_put_failure;
  	if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus)))
  		goto nla_put_failure;
  	if (priv->offset != 0)
  		if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset)))
  			goto nla_put_failure;
  	if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_SYM)))
  		goto nla_put_failure;
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
170
171
172
173
174
175
176
  	return 0;
  
  nla_put_failure:
  	return -1;
  }
  
  static struct nft_expr_type nft_hash_type;
511040eea   Laura Garcia Liebana   netfilter: nft_ha...
177
  static const struct nft_expr_ops nft_jhash_ops = {
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
178
  	.type		= &nft_hash_type,
511040eea   Laura Garcia Liebana   netfilter: nft_ha...
179
180
181
182
  	.size		= NFT_EXPR_SIZE(sizeof(struct nft_jhash)),
  	.eval		= nft_jhash_eval,
  	.init		= nft_jhash_init,
  	.dump		= nft_jhash_dump,
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
183
  };
3206caded   Laura Garcia Liebana   netfilter: nft_ha...
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
  static const struct nft_expr_ops nft_symhash_ops = {
  	.type		= &nft_hash_type,
  	.size		= NFT_EXPR_SIZE(sizeof(struct nft_symhash)),
  	.eval		= nft_symhash_eval,
  	.init		= nft_symhash_init,
  	.dump		= nft_symhash_dump,
  };
  
  static const struct nft_expr_ops *
  nft_hash_select_ops(const struct nft_ctx *ctx,
  		    const struct nlattr * const tb[])
  {
  	u32 type;
  
  	if (!tb[NFTA_HASH_TYPE])
  		return &nft_jhash_ops;
  
  	type = ntohl(nla_get_be32(tb[NFTA_HASH_TYPE]));
  	switch (type) {
  	case NFT_HASH_SYM:
  		return &nft_symhash_ops;
  	case NFT_HASH_JENKINS:
  		return &nft_jhash_ops;
  	default:
  		break;
  	}
  	return ERR_PTR(-EOPNOTSUPP);
  }
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
212
213
  static struct nft_expr_type nft_hash_type __read_mostly = {
  	.name		= "hash",
d4ef38354   Arushi Singhal   netfilter: Remove...
214
  	.select_ops	= nft_hash_select_ops,
cb1b69b0b   Laura Garcia Liebana   netfilter: nf_tab...
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
  	.policy		= nft_hash_policy,
  	.maxattr	= NFTA_HASH_MAX,
  	.owner		= THIS_MODULE,
  };
  
  static int __init nft_hash_module_init(void)
  {
  	return nft_register_expr(&nft_hash_type);
  }
  
  static void __exit nft_hash_module_exit(void)
  {
  	nft_unregister_expr(&nft_hash_type);
  }
  
  module_init(nft_hash_module_init);
  module_exit(nft_hash_module_exit);
  
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Laura Garcia <nevola@gmail.com>");
  MODULE_ALIAS_NFT_EXPR("hash");
4cacc3951   Rob Gill   netfilter: Add MO...
236
  MODULE_DESCRIPTION("Netfilter nftables hash module");