netfilter: nf_tables: add transaction helper functions
[deliverable/linux.git] / net / netfilter / nft_hash.c
CommitLineData
96518518 1/*
ce6eb0d7 2 * Copyright (c) 2008-2014 Patrick McHardy <kaber@trash.net>
96518518
PM
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 * Development of this code funded by Astaro AG (http://www.astaro.com/)
9 */
10
11#include <linux/kernel.h>
12#include <linux/init.h>
13#include <linux/module.h>
14#include <linux/list.h>
c50b960c 15#include <linux/log2.h>
96518518
PM
16#include <linux/jhash.h>
17#include <linux/netlink.h>
cfe4a9dd 18#include <linux/rhashtable.h>
96518518
PM
19#include <linux/netfilter.h>
20#include <linux/netfilter/nf_tables.h>
21#include <net/netfilter/nf_tables.h>
22
cfe4a9dd
TG
23/* We target a hash table size of 4, element hint is 75% of final size */
24#define NFT_HASH_ELEMENT_HINT 3
96518518 25
745f5450
PM
26struct nft_hash {
27 struct rhashtable ht;
28};
29
96518518 30struct nft_hash_elem {
cfe4a9dd 31 struct rhash_head node;
fe2811eb 32 struct nft_set_ext ext;
96518518
PM
33};
34
bfd6e327
PM
35struct nft_hash_cmp_arg {
36 const struct nft_set *set;
37 const struct nft_data *key;
38};
39
fa377321
HX
40static const struct rhashtable_params nft_hash_params;
41
bfd6e327
PM
42static inline u32 nft_hash_key(const void *data, u32 len, u32 seed)
43{
44 const struct nft_hash_cmp_arg *arg = data;
45
46 return jhash(arg->key, len, seed);
47}
48
49static inline u32 nft_hash_obj(const void *data, u32 len, u32 seed)
50{
51 const struct nft_hash_elem *he = data;
52
fe2811eb 53 return jhash(nft_set_ext_key(&he->ext), len, seed);
bfd6e327
PM
54}
55
56static inline int nft_hash_cmp(struct rhashtable_compare_arg *arg,
57 const void *ptr)
58{
59 const struct nft_hash_cmp_arg *x = arg->key;
60 const struct nft_hash_elem *he = ptr;
61
fe2811eb 62 if (nft_data_cmp(nft_set_ext_key(&he->ext), x->key, x->set->klen))
bfd6e327
PM
63 return 1;
64 return 0;
65}
66
20a69341
PM
67static bool nft_hash_lookup(const struct nft_set *set,
68 const struct nft_data *key,
b2832dd6 69 const struct nft_set_ext **ext)
96518518 70{
745f5450 71 struct nft_hash *priv = nft_set_priv(set);
20a69341 72 const struct nft_hash_elem *he;
bfd6e327
PM
73 struct nft_hash_cmp_arg arg = {
74 .set = set,
75 .key = key,
76 };
ce6eb0d7 77
bfd6e327 78 he = rhashtable_lookup_fast(&priv->ht, &arg, nft_hash_params);
b2832dd6
PM
79 if (he != NULL)
80 *ext = &he->ext;
ce6eb0d7 81
cfe4a9dd 82 return !!he;
96518518
PM
83}
84
20a69341
PM
85static int nft_hash_insert(const struct nft_set *set,
86 const struct nft_set_elem *elem)
96518518 87{
745f5450 88 struct nft_hash *priv = nft_set_priv(set);
fe2811eb 89 struct nft_hash_elem *he = elem->priv;
bfd6e327
PM
90 struct nft_hash_cmp_arg arg = {
91 .set = set,
92 .key = &elem->key,
93 };
ce6eb0d7 94
fe2811eb
PM
95 return rhashtable_lookup_insert_key(&priv->ht, &arg, &he->node,
96 nft_hash_params);
96518518
PM
97}
98
20a69341
PM
99static void nft_hash_remove(const struct nft_set *set,
100 const struct nft_set_elem *elem)
96518518 101{
745f5450 102 struct nft_hash *priv = nft_set_priv(set);
ce6eb0d7 103
745f5450 104 rhashtable_remove_fast(&priv->ht, elem->cookie, nft_hash_params);
20a69341 105}
96518518 106
20a69341
PM
107static int nft_hash_get(const struct nft_set *set, struct nft_set_elem *elem)
108{
745f5450 109 struct nft_hash *priv = nft_set_priv(set);
fa377321 110 struct nft_hash_elem *he;
bfd6e327
PM
111 struct nft_hash_cmp_arg arg = {
112 .set = set,
113 .key = &elem->key,
114 };
fa377321 115
bfd6e327 116 he = rhashtable_lookup_fast(&priv->ht, &arg, nft_hash_params);
fa377321
HX
117 if (!he)
118 return -ENOENT;
8d24c0b4 119
fe2811eb 120 elem->priv = he;
8d24c0b4 121
fa377321 122 return 0;
96518518
PM
123}
124
20a69341
PM
125static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set,
126 struct nft_set_iter *iter)
96518518 127{
745f5450 128 struct nft_hash *priv = nft_set_priv(set);
fe2811eb 129 struct nft_hash_elem *he;
9a776628 130 struct rhashtable_iter hti;
20a69341 131 struct nft_set_elem elem;
9a776628 132 int err;
96518518 133
745f5450 134 err = rhashtable_walk_init(&priv->ht, &hti);
9a776628
HX
135 iter->err = err;
136 if (err)
137 return;
88d6ed15 138
9a776628
HX
139 err = rhashtable_walk_start(&hti);
140 if (err && err != -EAGAIN) {
141 iter->err = err;
142 goto out;
143 }
144
145 while ((he = rhashtable_walk_next(&hti))) {
146 if (IS_ERR(he)) {
147 err = PTR_ERR(he);
148 if (err != -EAGAIN) {
149 iter->err = err;
150 goto out;
151 }
d8bdff59
HX
152
153 continue;
9a776628
HX
154 }
155
156 if (iter->count < iter->skip)
157 goto cont;
20a69341 158
fe2811eb 159 elem.priv = he;
9a776628
HX
160
161 iter->err = iter->fn(ctx, set, iter, &elem);
162 if (iter->err < 0)
163 goto out;
20a69341 164
20a69341 165cont:
9a776628 166 iter->count++;
96518518 167 }
9a776628
HX
168
169out:
170 rhashtable_walk_stop(&hti);
171 rhashtable_walk_exit(&hti);
96518518
PM
172}
173
20a69341
PM
174static unsigned int nft_hash_privsize(const struct nlattr * const nla[])
175{
745f5450 176 return sizeof(struct nft_hash);
cfe4a9dd
TG
177}
178
fa377321 179static const struct rhashtable_params nft_hash_params = {
45d84751 180 .head_offset = offsetof(struct nft_hash_elem, node),
bfd6e327
PM
181 .hashfn = nft_hash_key,
182 .obj_hashfn = nft_hash_obj,
183 .obj_cmpfn = nft_hash_cmp,
45d84751 184 .automatic_shrinking = true,
fa377321
HX
185};
186
20a69341 187static int nft_hash_init(const struct nft_set *set,
c50b960c 188 const struct nft_set_desc *desc,
96518518
PM
189 const struct nlattr * const tb[])
190{
745f5450 191 struct nft_hash *priv = nft_set_priv(set);
fa377321
HX
192 struct rhashtable_params params = nft_hash_params;
193
194 params.nelem_hint = desc->size ?: NFT_HASH_ELEMENT_HINT;
45d84751 195 params.key_len = set->klen;
96518518 196
745f5450 197 return rhashtable_init(&priv->ht, &params);
96518518
PM
198}
199
61edafbb 200static void nft_hash_elem_destroy(void *ptr, void *arg)
96518518 201{
61edafbb 202 nft_set_elem_destroy((const struct nft_set *)arg, ptr);
6b6f302c 203}
97defe1e 204
6b6f302c
TG
205static void nft_hash_destroy(const struct nft_set *set)
206{
745f5450
PM
207 struct nft_hash *priv = nft_set_priv(set);
208
61edafbb
PM
209 rhashtable_free_and_destroy(&priv->ht, nft_hash_elem_destroy,
210 (void *)set);
96518518
PM
211}
212
c50b960c
PM
213static bool nft_hash_estimate(const struct nft_set_desc *desc, u32 features,
214 struct nft_set_estimate *est)
215{
216 unsigned int esize;
217
218 esize = sizeof(struct nft_hash_elem);
c50b960c 219 if (desc->size) {
745f5450 220 est->size = sizeof(struct nft_hash) +
cfe4a9dd 221 roundup_pow_of_two(desc->size * 4 / 3) *
c50b960c
PM
222 sizeof(struct nft_hash_elem *) +
223 desc->size * esize;
224 } else {
225 /* Resizing happens when the load drops below 30% or goes
226 * above 75%. The average of 52.5% load (approximated by 50%)
227 * is used for the size estimation of the hash buckets,
228 * meaning we calculate two buckets per element.
229 */
230 est->size = esize + 2 * sizeof(struct nft_hash_elem *);
231 }
232
233 est->class = NFT_SET_CLASS_O_1;
234
235 return true;
236}
237
20a69341
PM
238static struct nft_set_ops nft_hash_ops __read_mostly = {
239 .privsize = nft_hash_privsize,
fe2811eb 240 .elemsize = offsetof(struct nft_hash_elem, ext),
c50b960c 241 .estimate = nft_hash_estimate,
96518518
PM
242 .init = nft_hash_init,
243 .destroy = nft_hash_destroy,
20a69341
PM
244 .get = nft_hash_get,
245 .insert = nft_hash_insert,
246 .remove = nft_hash_remove,
247 .lookup = nft_hash_lookup,
248 .walk = nft_hash_walk,
249 .features = NFT_SET_MAP,
250 .owner = THIS_MODULE,
96518518
PM
251};
252
253static int __init nft_hash_module_init(void)
254{
20a69341 255 return nft_register_set(&nft_hash_ops);
96518518
PM
256}
257
258static void __exit nft_hash_module_exit(void)
259{
20a69341 260 nft_unregister_set(&nft_hash_ops);
96518518
PM
261}
262
263module_init(nft_hash_module_init);
264module_exit(nft_hash_module_exit);
265
266MODULE_LICENSE("GPL");
267MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
20a69341 268MODULE_ALIAS_NFT_SET();
This page took 0.10192 seconds and 5 git commands to generate.