]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - net/netfilter/nft_rbtree.c
netfilter: nf_tables: consolide set element destruction
[mirror_ubuntu-bionic-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
20a69341
PM
75static int __nft_rbtree_insert(const struct nft_set *set,
76 struct nft_rbtree_elem *new)
77{
78 struct nft_rbtree *priv = nft_set_priv(set);
79 struct nft_rbtree_elem *rbe;
80 struct rb_node *parent, **p;
81 int d;
82
83 parent = NULL;
84 p = &priv->root.rb_node;
85 while (*p != NULL) {
86 parent = *p;
87 rbe = rb_entry(parent, struct nft_rbtree_elem, node);
fe2811eb
PM
88 d = nft_data_cmp(nft_set_ext_key(&rbe->ext),
89 nft_set_ext_key(&new->ext),
90 set->klen);
20a69341
PM
91 if (d < 0)
92 p = &parent->rb_left;
93 else if (d > 0)
94 p = &parent->rb_right;
95 else
96 return -EEXIST;
97 }
98 rb_link_node(&new->node, parent, p);
99 rb_insert_color(&new->node, &priv->root);
100 return 0;
101}
102
103static int nft_rbtree_insert(const struct nft_set *set,
104 const struct nft_set_elem *elem)
105{
fe2811eb 106 struct nft_rbtree_elem *rbe = elem->priv;
20a69341
PM
107 int err;
108
7632667d 109 spin_lock_bh(&nft_rbtree_lock);
20a69341 110 err = __nft_rbtree_insert(set, rbe);
7632667d 111 spin_unlock_bh(&nft_rbtree_lock);
fe2811eb 112
20a69341
PM
113 return err;
114}
115
116static void nft_rbtree_remove(const struct nft_set *set,
117 const struct nft_set_elem *elem)
118{
119 struct nft_rbtree *priv = nft_set_priv(set);
120 struct nft_rbtree_elem *rbe = elem->cookie;
121
7632667d 122 spin_lock_bh(&nft_rbtree_lock);
20a69341 123 rb_erase(&rbe->node, &priv->root);
7632667d 124 spin_unlock_bh(&nft_rbtree_lock);
20a69341
PM
125}
126
127static int nft_rbtree_get(const struct nft_set *set, struct nft_set_elem *elem)
128{
129 const struct nft_rbtree *priv = nft_set_priv(set);
130 const struct rb_node *parent = priv->root.rb_node;
131 struct nft_rbtree_elem *rbe;
132 int d;
133
134 while (parent != NULL) {
135 rbe = rb_entry(parent, struct nft_rbtree_elem, node);
136
fe2811eb
PM
137 d = nft_data_cmp(nft_set_ext_key(&rbe->ext), &elem->key,
138 set->klen);
20a69341
PM
139 if (d < 0)
140 parent = parent->rb_left;
141 else if (d > 0)
142 parent = parent->rb_right;
143 else {
144 elem->cookie = rbe;
fe2811eb 145 elem->priv = rbe;
20a69341
PM
146 return 0;
147 }
148 }
149 return -ENOENT;
150}
151
152static void nft_rbtree_walk(const struct nft_ctx *ctx,
153 const struct nft_set *set,
154 struct nft_set_iter *iter)
155{
156 const struct nft_rbtree *priv = nft_set_priv(set);
fe2811eb 157 struct nft_rbtree_elem *rbe;
20a69341
PM
158 struct nft_set_elem elem;
159 struct rb_node *node;
160
7632667d 161 spin_lock_bh(&nft_rbtree_lock);
20a69341
PM
162 for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) {
163 if (iter->count < iter->skip)
164 goto cont;
165
166 rbe = rb_entry(node, struct nft_rbtree_elem, node);
fe2811eb 167 elem.priv = rbe;
20a69341
PM
168
169 iter->err = iter->fn(ctx, set, iter, &elem);
7632667d
PNA
170 if (iter->err < 0) {
171 spin_unlock_bh(&nft_rbtree_lock);
20a69341 172 return;
7632667d 173 }
20a69341
PM
174cont:
175 iter->count++;
176 }
7632667d 177 spin_unlock_bh(&nft_rbtree_lock);
20a69341
PM
178}
179
180static unsigned int nft_rbtree_privsize(const struct nlattr * const nla[])
181{
182 return sizeof(struct nft_rbtree);
183}
184
185static int nft_rbtree_init(const struct nft_set *set,
c50b960c 186 const struct nft_set_desc *desc,
20a69341
PM
187 const struct nlattr * const nla[])
188{
189 struct nft_rbtree *priv = nft_set_priv(set);
190
191 priv->root = RB_ROOT;
192 return 0;
193}
194
195static void nft_rbtree_destroy(const struct nft_set *set)
196{
197 struct nft_rbtree *priv = nft_set_priv(set);
198 struct nft_rbtree_elem *rbe;
199 struct rb_node *node;
200
201 while ((node = priv->root.rb_node) != NULL) {
202 rb_erase(node, &priv->root);
203 rbe = rb_entry(node, struct nft_rbtree_elem, node);
61edafbb 204 nft_set_elem_destroy(set, rbe);
20a69341
PM
205 }
206}
207
c50b960c
PM
208static bool nft_rbtree_estimate(const struct nft_set_desc *desc, u32 features,
209 struct nft_set_estimate *est)
210{
211 unsigned int nsize;
212
213 nsize = sizeof(struct nft_rbtree_elem);
c50b960c
PM
214 if (desc->size)
215 est->size = sizeof(struct nft_rbtree) + desc->size * nsize;
216 else
217 est->size = nsize;
218
219 est->class = NFT_SET_CLASS_O_LOG_N;
220
221 return true;
222}
223
20a69341
PM
224static struct nft_set_ops nft_rbtree_ops __read_mostly = {
225 .privsize = nft_rbtree_privsize,
fe2811eb 226 .elemsize = offsetof(struct nft_rbtree_elem, ext),
c50b960c 227 .estimate = nft_rbtree_estimate,
20a69341
PM
228 .init = nft_rbtree_init,
229 .destroy = nft_rbtree_destroy,
230 .insert = nft_rbtree_insert,
231 .remove = nft_rbtree_remove,
232 .get = nft_rbtree_get,
233 .lookup = nft_rbtree_lookup,
234 .walk = nft_rbtree_walk,
235 .features = NFT_SET_INTERVAL | NFT_SET_MAP,
236 .owner = THIS_MODULE,
237};
238
239static int __init nft_rbtree_module_init(void)
240{
241 return nft_register_set(&nft_rbtree_ops);
242}
243
244static void __exit nft_rbtree_module_exit(void)
245{
246 nft_unregister_set(&nft_rbtree_ops);
247}
248
249module_init(nft_rbtree_module_init);
250module_exit(nft_rbtree_module_exit);
251
252MODULE_LICENSE("GPL");
253MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
254MODULE_ALIAS_NFT_SET();