linux/net/netfilter/nft_set_rbtree.c
<<
>>
Prefs
   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
  21static DEFINE_SPINLOCK(nft_rbtree_lock);
  22
  23struct nft_rbtree {
  24        struct rb_root          root;
  25};
  26
  27struct nft_rbtree_elem {
  28        struct rb_node          node;
  29        struct nft_set_ext      ext;
  30};
  31
  32static bool nft_rbtree_interval_end(const struct nft_rbtree_elem *rbe)
  33{
  34        return nft_set_ext_exists(&rbe->ext, NFT_SET_EXT_FLAGS) &&
  35               (*nft_set_ext_flags(&rbe->ext) & NFT_SET_ELEM_INTERVAL_END);
  36}
  37
  38static bool nft_rbtree_equal(const struct nft_set *set, const void *this,
  39                             const struct nft_rbtree_elem *interval)
  40{
  41        return memcmp(this, nft_set_ext_key(&interval->ext), set->klen) == 0;
  42}
  43
  44static bool nft_rbtree_lookup(const struct net *net, const struct nft_set *set,
  45                              const u32 *key, const struct nft_set_ext **ext)
  46{
  47        const struct nft_rbtree *priv = nft_set_priv(set);
  48        const struct nft_rbtree_elem *rbe, *interval = NULL;
  49        u8 genmask = nft_genmask_cur(net);
  50        const struct rb_node *parent;
  51        const void *this;
  52        int d;
  53
  54        spin_lock_bh(&nft_rbtree_lock);
  55        parent = priv->root.rb_node;
  56        while (parent != NULL) {
  57                rbe = rb_entry(parent, struct nft_rbtree_elem, node);
  58
  59                this = nft_set_ext_key(&rbe->ext);
  60                d = memcmp(this, key, set->klen);
  61                if (d < 0) {
  62                        parent = parent->rb_left;
  63                        if (interval &&
  64                            nft_rbtree_equal(set, this, interval) &&
  65                            nft_rbtree_interval_end(this) &&
  66                            !nft_rbtree_interval_end(interval))
  67                                continue;
  68                        interval = rbe;
  69                } else if (d > 0)
  70                        parent = parent->rb_right;
  71                else {
  72                        if (!nft_set_elem_active(&rbe->ext, genmask)) {
  73                                parent = parent->rb_left;
  74                                continue;
  75                        }
  76                        if (nft_rbtree_interval_end(rbe))
  77                                goto out;
  78                        spin_unlock_bh(&nft_rbtree_lock);
  79
  80                        *ext = &rbe->ext;
  81                        return true;
  82                }
  83        }
  84
  85        if (set->flags & NFT_SET_INTERVAL && interval != NULL &&
  86            nft_set_elem_active(&interval->ext, genmask) &&
  87            !nft_rbtree_interval_end(interval)) {
  88                spin_unlock_bh(&nft_rbtree_lock);
  89                *ext = &interval->ext;
  90                return true;
  91        }
  92out:
  93        spin_unlock_bh(&nft_rbtree_lock);
  94        return false;
  95}
  96
  97static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
  98                               struct nft_rbtree_elem *new,
  99                               struct nft_set_ext **ext)
 100{
 101        struct nft_rbtree *priv = nft_set_priv(set);
 102        u8 genmask = nft_genmask_next(net);
 103        struct nft_rbtree_elem *rbe;
 104        struct rb_node *parent, **p;
 105        int d;
 106
 107        parent = NULL;
 108        p = &priv->root.rb_node;
 109        while (*p != NULL) {
 110                parent = *p;
 111                rbe = rb_entry(parent, struct nft_rbtree_elem, node);
 112                d = memcmp(nft_set_ext_key(&rbe->ext),
 113                           nft_set_ext_key(&new->ext),
 114                           set->klen);
 115                if (d < 0)
 116                        p = &parent->rb_left;
 117                else if (d > 0)
 118                        p = &parent->rb_right;
 119                else {
 120                        if (nft_set_elem_active(&rbe->ext, genmask)) {
 121                                if (nft_rbtree_interval_end(rbe) &&
 122                                    !nft_rbtree_interval_end(new))
 123                                        p = &parent->rb_left;
 124                                else if (!nft_rbtree_interval_end(rbe) &&
 125                                         nft_rbtree_interval_end(new))
 126                                        p = &parent->rb_right;
 127                                else {
 128                                        *ext = &rbe->ext;
 129                                        return -EEXIST;
 130                                }
 131                        }
 132                }
 133        }
 134        rb_link_node(&new->node, parent, p);
 135        rb_insert_color(&new->node, &priv->root);
 136        return 0;
 137}
 138
 139static int nft_rbtree_insert(const struct net *net, const struct nft_set *set,
 140                             const struct nft_set_elem *elem,
 141                             struct nft_set_ext **ext)
 142{
 143        struct nft_rbtree_elem *rbe = elem->priv;
 144        int err;
 145
 146        spin_lock_bh(&nft_rbtree_lock);
 147        err = __nft_rbtree_insert(net, set, rbe, ext);
 148        spin_unlock_bh(&nft_rbtree_lock);
 149
 150        return err;
 151}
 152
 153static void nft_rbtree_remove(const struct net *net,
 154                              const struct nft_set *set,
 155                              const struct nft_set_elem *elem)
 156{
 157        struct nft_rbtree *priv = nft_set_priv(set);
 158        struct nft_rbtree_elem *rbe = elem->priv;
 159
 160        spin_lock_bh(&nft_rbtree_lock);
 161        rb_erase(&rbe->node, &priv->root);
 162        spin_unlock_bh(&nft_rbtree_lock);
 163}
 164
 165static void nft_rbtree_activate(const struct net *net,
 166                                const struct nft_set *set,
 167                                const struct nft_set_elem *elem)
 168{
 169        struct nft_rbtree_elem *rbe = elem->priv;
 170
 171        nft_set_elem_change_active(net, set, &rbe->ext);
 172}
 173
 174static bool nft_rbtree_flush(const struct net *net,
 175                             const struct nft_set *set, void *priv)
 176{
 177        struct nft_rbtree_elem *rbe = priv;
 178
 179        nft_set_elem_change_active(net, set, &rbe->ext);
 180        return true;
 181}
 182
 183static void *nft_rbtree_deactivate(const struct net *net,
 184                                   const struct nft_set *set,
 185                                   const struct nft_set_elem *elem)
 186{
 187        const struct nft_rbtree *priv = nft_set_priv(set);
 188        const struct rb_node *parent = priv->root.rb_node;
 189        struct nft_rbtree_elem *rbe, *this = elem->priv;
 190        u8 genmask = nft_genmask_next(net);
 191        int d;
 192
 193        while (parent != NULL) {
 194                rbe = rb_entry(parent, struct nft_rbtree_elem, node);
 195
 196                d = memcmp(nft_set_ext_key(&rbe->ext), &elem->key.val,
 197                                           set->klen);
 198                if (d < 0)
 199                        parent = parent->rb_left;
 200                else if (d > 0)
 201                        parent = parent->rb_right;
 202                else {
 203                        if (!nft_set_elem_active(&rbe->ext, genmask)) {
 204                                parent = parent->rb_left;
 205                                continue;
 206                        }
 207                        if (nft_rbtree_interval_end(rbe) &&
 208                            !nft_rbtree_interval_end(this)) {
 209                                parent = parent->rb_left;
 210                                continue;
 211                        } else if (!nft_rbtree_interval_end(rbe) &&
 212                                   nft_rbtree_interval_end(this)) {
 213                                parent = parent->rb_right;
 214                                continue;
 215                        }
 216                        nft_rbtree_flush(net, set, rbe);
 217                        return rbe;
 218                }
 219        }
 220        return NULL;
 221}
 222
 223static void nft_rbtree_walk(const struct nft_ctx *ctx,
 224                            struct nft_set *set,
 225                            struct nft_set_iter *iter)
 226{
 227        const struct nft_rbtree *priv = nft_set_priv(set);
 228        struct nft_rbtree_elem *rbe;
 229        struct nft_set_elem elem;
 230        struct rb_node *node;
 231
 232        spin_lock_bh(&nft_rbtree_lock);
 233        for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) {
 234                rbe = rb_entry(node, struct nft_rbtree_elem, node);
 235
 236                if (iter->count < iter->skip)
 237                        goto cont;
 238                if (!nft_set_elem_active(&rbe->ext, iter->genmask))
 239                        goto cont;
 240
 241                elem.priv = rbe;
 242
 243                iter->err = iter->fn(ctx, set, iter, &elem);
 244                if (iter->err < 0) {
 245                        spin_unlock_bh(&nft_rbtree_lock);
 246                        return;
 247                }
 248cont:
 249                iter->count++;
 250        }
 251        spin_unlock_bh(&nft_rbtree_lock);
 252}
 253
 254static unsigned int nft_rbtree_privsize(const struct nlattr * const nla[])
 255{
 256        return sizeof(struct nft_rbtree);
 257}
 258
 259static int nft_rbtree_init(const struct nft_set *set,
 260                           const struct nft_set_desc *desc,
 261                           const struct nlattr * const nla[])
 262{
 263        struct nft_rbtree *priv = nft_set_priv(set);
 264
 265        priv->root = RB_ROOT;
 266        return 0;
 267}
 268
 269static void nft_rbtree_destroy(const struct nft_set *set)
 270{
 271        struct nft_rbtree *priv = nft_set_priv(set);
 272        struct nft_rbtree_elem *rbe;
 273        struct rb_node *node;
 274
 275        while ((node = priv->root.rb_node) != NULL) {
 276                rb_erase(node, &priv->root);
 277                rbe = rb_entry(node, struct nft_rbtree_elem, node);
 278                nft_set_elem_destroy(set, rbe, true);
 279        }
 280}
 281
 282static bool nft_rbtree_estimate(const struct nft_set_desc *desc, u32 features,
 283                                struct nft_set_estimate *est)
 284{
 285        unsigned int nsize;
 286
 287        nsize = sizeof(struct nft_rbtree_elem);
 288        if (desc->size)
 289                est->size = sizeof(struct nft_rbtree) + desc->size * nsize;
 290        else
 291                est->size = nsize;
 292
 293        est->lookup = NFT_SET_CLASS_O_LOG_N;
 294        est->space  = NFT_SET_CLASS_O_N;
 295
 296        return true;
 297}
 298
 299static struct nft_set_ops nft_rbtree_ops __read_mostly = {
 300        .privsize       = nft_rbtree_privsize,
 301        .elemsize       = offsetof(struct nft_rbtree_elem, ext),
 302        .estimate       = nft_rbtree_estimate,
 303        .init           = nft_rbtree_init,
 304        .destroy        = nft_rbtree_destroy,
 305        .insert         = nft_rbtree_insert,
 306        .remove         = nft_rbtree_remove,
 307        .deactivate     = nft_rbtree_deactivate,
 308        .flush          = nft_rbtree_flush,
 309        .activate       = nft_rbtree_activate,
 310        .lookup         = nft_rbtree_lookup,
 311        .walk           = nft_rbtree_walk,
 312        .features       = NFT_SET_INTERVAL | NFT_SET_MAP | NFT_SET_OBJECT,
 313        .owner          = THIS_MODULE,
 314};
 315
 316static int __init nft_rbtree_module_init(void)
 317{
 318        return nft_register_set(&nft_rbtree_ops);
 319}
 320
 321static void __exit nft_rbtree_module_exit(void)
 322{
 323        nft_unregister_set(&nft_rbtree_ops);
 324}
 325
 326module_init(nft_rbtree_module_init);
 327module_exit(nft_rbtree_module_exit);
 328
 329MODULE_LICENSE("GPL");
 330MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
 331MODULE_ALIAS_NFT_SET();
 332