]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - net/netfilter/nft_rbtree.c
netfilter: nf_tables: convert hash and rbtree to set extensions
[mirror_ubuntu-hirsute-kernel.git] / net / netfilter / nft_rbtree.c
CommitLineData
20a69341
PM
1/*
2 * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
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>
15#include <linux/rbtree.h>
16#include <linux/netlink.h>
17#include <linux/netfilter.h>
18#include <linux/netfilter/nf_tables.h>
19#include <net/netfilter/nf_tables.h>
20
7632667d
PNA
21static DEFINE_SPINLOCK(nft_rbtree_lock);
22
20a69341
PM
23struct nft_rbtree {
24 struct rb_root root;
25};
26
27struct nft_rbtree_elem {
28 struct rb_node node;
fe2811eb 29 struct nft_set_ext ext;
20a69341
PM
30};
31
32static bool nft_rbtree_lookup(const struct nft_set *set,
33 const struct nft_data *key,
34 struct nft_data *data)
35{
36 const struct nft_rbtree *priv = nft_set_priv(set);
37 const struct nft_rbtree_elem *rbe, *interval = NULL;
16c45eda 38 const struct rb_node *parent;
20a69341
PM
39 int d;
40
7632667d 41 spin_lock_bh(&nft_rbtree_lock);
16c45eda 42 parent = priv->root.rb_node;
20a69341
PM
43 while (parent != NULL) {
44 rbe = rb_entry(parent, struct nft_rbtree_elem, node);
45
fe2811eb 46 d = nft_data_cmp(nft_set_ext_key(&rbe->ext), key, set->klen);
20a69341
PM
47 if (d < 0) {
48 parent = parent->rb_left;
49 interval = rbe;
50 } else if (d > 0)
51 parent = parent->rb_right;
52 else {
53found:
fe2811eb
PM
54 if (nft_set_ext_exists(&rbe->ext, NFT_SET_EXT_FLAGS) &&
55 *nft_set_ext_flags(&rbe->ext) &
56 NFT_SET_ELEM_INTERVAL_END)
20a69341
PM
57 goto out;
58 if (set->flags & NFT_SET_MAP)
fe2811eb 59 nft_data_copy(data, nft_set_ext_data(&rbe->ext));
7632667d
PNA
60
61 spin_unlock_bh(&nft_rbtree_lock);
20a69341
PM
62 return true;
63 }
64 }
65
66 if (set->flags & NFT_SET_INTERVAL && interval != NULL) {
67 rbe = interval;
68 goto found;
69 }
70out:
7632667d 71 spin_unlock_bh(&nft_rbtree_lock);
20a69341
PM
72 return false;
73}
74
75static void nft_rbtree_elem_destroy(const struct nft_set *set,
76 struct nft_rbtree_elem *rbe)
77{
fe2811eb 78 nft_data_uninit(nft_set_ext_key(&rbe->ext), NFT_DATA_VALUE);
2fb91ddb 79 if (set->flags & NFT_SET_MAP &&
fe2811eb
PM
80 nft_set_ext_exists(&rbe->ext, NFT_SET_EXT_DATA))
81 nft_data_uninit(nft_set_ext_data(&rbe->ext), set->dtype);
2fb91ddb 82
20a69341
PM
83 kfree(rbe);
84}
85
86static int __nft_rbtree_insert(const struct nft_set *set,
87 struct nft_rbtree_elem *new)
88{
89 struct nft_rbtree *priv = nft_set_priv(set);
90 struct nft_rbtree_elem *rbe;
91 struct rb_node *parent, **p;
92 int d;
93
94 parent = NULL;
95 p = &priv->root.rb_node;
96 while (*p != NULL) {
97 parent = *p;
98 rbe = rb_entry(parent, struct nft_rbtree_elem, node);
fe2811eb
PM
99 d = nft_data_cmp(nft_set_ext_key(&rbe->ext),
100 nft_set_ext_key(&new->ext),
101 set->klen);
20a69341
PM
102 if (d < 0)
103 p = &parent->rb_left;
104 else if (d > 0)
105 p = &parent->rb_right;
106 else
107 return -EEXIST;
108 }
109 rb_link_node(&new->node, parent, p);
110 rb_insert_color(&new->node, &priv->root);
111 return 0;
112}
113
114static int nft_rbtree_insert(const struct nft_set *set,
115 const struct nft_set_elem *elem)
116{
fe2811eb 117 struct nft_rbtree_elem *rbe = elem->priv;
20a69341
PM
118 int err;
119
7632667d 120 spin_lock_bh(&nft_rbtree_lock);
20a69341 121 err = __nft_rbtree_insert(set, rbe);
7632667d 122 spin_unlock_bh(&nft_rbtree_lock);
fe2811eb 123
20a69341
PM
124 return err;
125}
126
127static void nft_rbtree_remove(const struct nft_set *set,
128 const struct nft_set_elem *elem)
129{
130 struct nft_rbtree *priv = nft_set_priv(set);
131 struct nft_rbtree_elem *rbe = elem->cookie;
132
7632667d 133 spin_lock_bh(&nft_rbtree_lock);
20a69341 134 rb_erase(&rbe->node, &priv->root);
7632667d 135 spin_unlock_bh(&nft_rbtree_lock);
20a69341
PM
136 kfree(rbe);
137}
138
139static int nft_rbtree_get(const struct nft_set *set, struct nft_set_elem *elem)
140{
141 const struct nft_rbtree *priv = nft_set_priv(set);
142 const struct rb_node *parent = priv->root.rb_node;
143 struct nft_rbtree_elem *rbe;
144 int d;
145
146 while (parent != NULL) {
147 rbe = rb_entry(parent, struct nft_rbtree_elem, node);
148
fe2811eb
PM
149 d = nft_data_cmp(nft_set_ext_key(&rbe->ext), &elem->key,
150 set->klen);
20a69341
PM
151 if (d < 0)
152 parent = parent->rb_left;
153 else if (d > 0)
154 parent = parent->rb_right;
155 else {
156 elem->cookie = rbe;
fe2811eb 157 elem->priv = rbe;
20a69341
PM
158 return 0;
159 }
160 }
161 return -ENOENT;
162}
163
164static void nft_rbtree_walk(const struct nft_ctx *ctx,
165 const struct nft_set *set,
166 struct nft_set_iter *iter)
167{
168 const struct nft_rbtree *priv = nft_set_priv(set);
fe2811eb 169 struct nft_rbtree_elem *rbe;
20a69341
PM
170 struct nft_set_elem elem;
171 struct rb_node *node;
172
7632667d 173 spin_lock_bh(&nft_rbtree_lock);
20a69341
PM
174 for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) {
175 if (iter->count < iter->skip)
176 goto cont;
177
178 rbe = rb_entry(node, struct nft_rbtree_elem, node);
fe2811eb 179 elem.priv = rbe;
20a69341
PM
180
181 iter->err = iter->fn(ctx, set, iter, &elem);
7632667d
PNA
182 if (iter->err < 0) {
183 spin_unlock_bh(&nft_rbtree_lock);
20a69341 184 return;
7632667d 185 }
20a69341
PM
186cont:
187 iter->count++;
188 }
7632667d 189 spin_unlock_bh(&nft_rbtree_lock);
20a69341
PM
190}
191
192static unsigned int nft_rbtree_privsize(const struct nlattr * const nla[])
193{
194 return sizeof(struct nft_rbtree);
195}
196
197static int nft_rbtree_init(const struct nft_set *set,
c50b960c 198 const struct nft_set_desc *desc,
20a69341
PM
199 const struct nlattr * const nla[])
200{
201 struct nft_rbtree *priv = nft_set_priv(set);
202
203 priv->root = RB_ROOT;
204 return 0;
205}
206
207static void nft_rbtree_destroy(const struct nft_set *set)
208{
209 struct nft_rbtree *priv = nft_set_priv(set);
210 struct nft_rbtree_elem *rbe;
211 struct rb_node *node;
212
213 while ((node = priv->root.rb_node) != NULL) {
214 rb_erase(node, &priv->root);
215 rbe = rb_entry(node, struct nft_rbtree_elem, node);
216 nft_rbtree_elem_destroy(set, rbe);
217 }
218}
219
c50b960c
PM
220static bool nft_rbtree_estimate(const struct nft_set_desc *desc, u32 features,
221 struct nft_set_estimate *est)
222{
223 unsigned int nsize;
224
225 nsize = sizeof(struct nft_rbtree_elem);
c50b960c
PM
226 if (desc->size)
227 est->size = sizeof(struct nft_rbtree) + desc->size * nsize;
228 else
229 est->size = nsize;
230
231 est->class = NFT_SET_CLASS_O_LOG_N;
232
233 return true;
234}
235
20a69341
PM
236static struct nft_set_ops nft_rbtree_ops __read_mostly = {
237 .privsize = nft_rbtree_privsize,
fe2811eb 238 .elemsize = offsetof(struct nft_rbtree_elem, ext),
c50b960c 239 .estimate = nft_rbtree_estimate,
20a69341
PM
240 .init = nft_rbtree_init,
241 .destroy = nft_rbtree_destroy,
242 .insert = nft_rbtree_insert,
243 .remove = nft_rbtree_remove,
244 .get = nft_rbtree_get,
245 .lookup = nft_rbtree_lookup,
246 .walk = nft_rbtree_walk,
247 .features = NFT_SET_INTERVAL | NFT_SET_MAP,
248 .owner = THIS_MODULE,
249};
250
251static int __init nft_rbtree_module_init(void)
252{
253 return nft_register_set(&nft_rbtree_ops);
254}
255
256static void __exit nft_rbtree_module_exit(void)
257{
258 nft_unregister_set(&nft_rbtree_ops);
259}
260
261module_init(nft_rbtree_module_init);
262module_exit(nft_rbtree_module_exit);
263
264MODULE_LICENSE("GPL");
265MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
266MODULE_ALIAS_NFT_SET();