linux/net/netfilter/nft_set_hash.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2008-2014 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/log2.h>
  16#include <linux/jhash.h>
  17#include <linux/netlink.h>
  18#include <linux/workqueue.h>
  19#include <linux/rhashtable.h>
  20#include <linux/netfilter.h>
  21#include <linux/netfilter/nf_tables.h>
  22#include <net/netfilter/nf_tables.h>
  23
  24/* We target a hash table size of 4, element hint is 75% of final size */
  25#define NFT_RHASH_ELEMENT_HINT 3
  26
  27struct nft_rhash {
  28        struct rhashtable               ht;
  29        struct delayed_work             gc_work;
  30};
  31
  32struct nft_rhash_elem {
  33        struct rhash_head               node;
  34        struct nft_set_ext              ext;
  35};
  36
  37struct nft_rhash_cmp_arg {
  38        const struct nft_set            *set;
  39        const u32                       *key;
  40        u8                              genmask;
  41};
  42
  43static inline u32 nft_rhash_key(const void *data, u32 len, u32 seed)
  44{
  45        const struct nft_rhash_cmp_arg *arg = data;
  46
  47        return jhash(arg->key, len, seed);
  48}
  49
  50static inline u32 nft_rhash_obj(const void *data, u32 len, u32 seed)
  51{
  52        const struct nft_rhash_elem *he = data;
  53
  54        return jhash(nft_set_ext_key(&he->ext), len, seed);
  55}
  56
  57static inline int nft_rhash_cmp(struct rhashtable_compare_arg *arg,
  58                                const void *ptr)
  59{
  60        const struct nft_rhash_cmp_arg *x = arg->key;
  61        const struct nft_rhash_elem *he = ptr;
  62
  63        if (memcmp(nft_set_ext_key(&he->ext), x->key, x->set->klen))
  64                return 1;
  65        if (nft_set_elem_expired(&he->ext))
  66                return 1;
  67        if (!nft_set_elem_active(&he->ext, x->genmask))
  68                return 1;
  69        return 0;
  70}
  71
  72static const struct rhashtable_params nft_rhash_params = {
  73        .head_offset            = offsetof(struct nft_rhash_elem, node),
  74        .hashfn                 = nft_rhash_key,
  75        .obj_hashfn             = nft_rhash_obj,
  76        .obj_cmpfn              = nft_rhash_cmp,
  77        .automatic_shrinking    = true,
  78};
  79
  80static bool nft_rhash_lookup(const struct net *net, const struct nft_set *set,
  81                             const u32 *key, const struct nft_set_ext **ext)
  82{
  83        struct nft_rhash *priv = nft_set_priv(set);
  84        const struct nft_rhash_elem *he;
  85        struct nft_rhash_cmp_arg arg = {
  86                .genmask = nft_genmask_cur(net),
  87                .set     = set,
  88                .key     = key,
  89        };
  90
  91        he = rhashtable_lookup_fast(&priv->ht, &arg, nft_rhash_params);
  92        if (he != NULL)
  93                *ext = &he->ext;
  94
  95        return !!he;
  96}
  97
  98static void *nft_rhash_get(const struct net *net, const struct nft_set *set,
  99                           const struct nft_set_elem *elem, unsigned int flags)
 100{
 101        struct nft_rhash *priv = nft_set_priv(set);
 102        struct nft_rhash_elem *he;
 103        struct nft_rhash_cmp_arg arg = {
 104                .genmask = nft_genmask_cur(net),
 105                .set     = set,
 106                .key     = elem->key.val.data,
 107        };
 108
 109        he = rhashtable_lookup_fast(&priv->ht, &arg, nft_rhash_params);
 110        if (he != NULL)
 111                return he;
 112
 113        return ERR_PTR(-ENOENT);
 114}
 115
 116static bool nft_rhash_update(struct nft_set *set, const u32 *key,
 117                             void *(*new)(struct nft_set *,
 118                                          const struct nft_expr *,
 119                                          struct nft_regs *regs),
 120                             const struct nft_expr *expr,
 121                             struct nft_regs *regs,
 122                             const struct nft_set_ext **ext)
 123{
 124        struct nft_rhash *priv = nft_set_priv(set);
 125        struct nft_rhash_elem *he, *prev;
 126        struct nft_rhash_cmp_arg arg = {
 127                .genmask = NFT_GENMASK_ANY,
 128                .set     = set,
 129                .key     = key,
 130        };
 131
 132        he = rhashtable_lookup_fast(&priv->ht, &arg, nft_rhash_params);
 133        if (he != NULL)
 134                goto out;
 135
 136        he = new(set, expr, regs);
 137        if (he == NULL)
 138                goto err1;
 139
 140        prev = rhashtable_lookup_get_insert_key(&priv->ht, &arg, &he->node,
 141                                                nft_rhash_params);
 142        if (IS_ERR(prev))
 143                goto err2;
 144
 145        /* Another cpu may race to insert the element with the same key */
 146        if (prev) {
 147                nft_set_elem_destroy(set, he, true);
 148                he = prev;
 149        }
 150
 151out:
 152        *ext = &he->ext;
 153        return true;
 154
 155err2:
 156        nft_set_elem_destroy(set, he, true);
 157err1:
 158        return false;
 159}
 160
 161static int nft_rhash_insert(const struct net *net, const struct nft_set *set,
 162                            const struct nft_set_elem *elem,
 163                            struct nft_set_ext **ext)
 164{
 165        struct nft_rhash *priv = nft_set_priv(set);
 166        struct nft_rhash_elem *he = elem->priv;
 167        struct nft_rhash_cmp_arg arg = {
 168                .genmask = nft_genmask_next(net),
 169                .set     = set,
 170                .key     = elem->key.val.data,
 171        };
 172        struct nft_rhash_elem *prev;
 173
 174        prev = rhashtable_lookup_get_insert_key(&priv->ht, &arg, &he->node,
 175                                                nft_rhash_params);
 176        if (IS_ERR(prev))
 177                return PTR_ERR(prev);
 178        if (prev) {
 179                *ext = &prev->ext;
 180                return -EEXIST;
 181        }
 182        return 0;
 183}
 184
 185static void nft_rhash_activate(const struct net *net, const struct nft_set *set,
 186                               const struct nft_set_elem *elem)
 187{
 188        struct nft_rhash_elem *he = elem->priv;
 189
 190        nft_set_elem_change_active(net, set, &he->ext);
 191        nft_set_elem_clear_busy(&he->ext);
 192}
 193
 194static bool nft_rhash_flush(const struct net *net,
 195                            const struct nft_set *set, void *priv)
 196{
 197        struct nft_rhash_elem *he = priv;
 198
 199        if (!nft_set_elem_mark_busy(&he->ext) ||
 200            !nft_is_active(net, &he->ext)) {
 201                nft_set_elem_change_active(net, set, &he->ext);
 202                return true;
 203        }
 204        return false;
 205}
 206
 207static void *nft_rhash_deactivate(const struct net *net,
 208                                  const struct nft_set *set,
 209                                  const struct nft_set_elem *elem)
 210{
 211        struct nft_rhash *priv = nft_set_priv(set);
 212        struct nft_rhash_elem *he;
 213        struct nft_rhash_cmp_arg arg = {
 214                .genmask = nft_genmask_next(net),
 215                .set     = set,
 216                .key     = elem->key.val.data,
 217        };
 218
 219        rcu_read_lock();
 220        he = rhashtable_lookup_fast(&priv->ht, &arg, nft_rhash_params);
 221        if (he != NULL &&
 222            !nft_rhash_flush(net, set, he))
 223                he = NULL;
 224
 225        rcu_read_unlock();
 226
 227        return he;
 228}
 229
 230static void nft_rhash_remove(const struct net *net,
 231                             const struct nft_set *set,
 232                             const struct nft_set_elem *elem)
 233{
 234        struct nft_rhash *priv = nft_set_priv(set);
 235        struct nft_rhash_elem *he = elem->priv;
 236
 237        rhashtable_remove_fast(&priv->ht, &he->node, nft_rhash_params);
 238}
 239
 240static void nft_rhash_walk(const struct nft_ctx *ctx, struct nft_set *set,
 241                           struct nft_set_iter *iter)
 242{
 243        struct nft_rhash *priv = nft_set_priv(set);
 244        struct nft_rhash_elem *he;
 245        struct rhashtable_iter hti;
 246        struct nft_set_elem elem;
 247        int err;
 248
 249        err = rhashtable_walk_init(&priv->ht, &hti, GFP_ATOMIC);
 250        iter->err = err;
 251        if (err)
 252                return;
 253
 254        rhashtable_walk_start(&hti);
 255
 256        while ((he = rhashtable_walk_next(&hti))) {
 257                if (IS_ERR(he)) {
 258                        err = PTR_ERR(he);
 259                        if (err != -EAGAIN) {
 260                                iter->err = err;
 261                                goto out;
 262                        }
 263
 264                        continue;
 265                }
 266
 267                if (iter->count < iter->skip)
 268                        goto cont;
 269                if (nft_set_elem_expired(&he->ext))
 270                        goto cont;
 271                if (!nft_set_elem_active(&he->ext, iter->genmask))
 272                        goto cont;
 273
 274                elem.priv = he;
 275
 276                iter->err = iter->fn(ctx, set, iter, &elem);
 277                if (iter->err < 0)
 278                        goto out;
 279
 280cont:
 281                iter->count++;
 282        }
 283
 284out:
 285        rhashtable_walk_stop(&hti);
 286        rhashtable_walk_exit(&hti);
 287}
 288
 289static void nft_rhash_gc(struct work_struct *work)
 290{
 291        struct nft_set *set;
 292        struct nft_rhash_elem *he;
 293        struct nft_rhash *priv;
 294        struct nft_set_gc_batch *gcb = NULL;
 295        struct rhashtable_iter hti;
 296        int err;
 297
 298        priv = container_of(work, struct nft_rhash, gc_work.work);
 299        set  = nft_set_container_of(priv);
 300
 301        err = rhashtable_walk_init(&priv->ht, &hti, GFP_KERNEL);
 302        if (err)
 303                goto schedule;
 304
 305        rhashtable_walk_start(&hti);
 306
 307        while ((he = rhashtable_walk_next(&hti))) {
 308                if (IS_ERR(he)) {
 309                        if (PTR_ERR(he) != -EAGAIN)
 310                                goto out;
 311                        continue;
 312                }
 313
 314                if (!nft_set_elem_expired(&he->ext))
 315                        continue;
 316                if (nft_set_elem_mark_busy(&he->ext))
 317                        continue;
 318
 319                gcb = nft_set_gc_batch_check(set, gcb, GFP_ATOMIC);
 320                if (gcb == NULL)
 321                        goto out;
 322                rhashtable_remove_fast(&priv->ht, &he->node, nft_rhash_params);
 323                atomic_dec(&set->nelems);
 324                nft_set_gc_batch_add(gcb, he);
 325        }
 326out:
 327        rhashtable_walk_stop(&hti);
 328        rhashtable_walk_exit(&hti);
 329
 330        nft_set_gc_batch_complete(gcb);
 331schedule:
 332        queue_delayed_work(system_power_efficient_wq, &priv->gc_work,
 333                           nft_set_gc_interval(set));
 334}
 335
 336static unsigned int nft_rhash_privsize(const struct nlattr * const nla[],
 337                                       const struct nft_set_desc *desc)
 338{
 339        return sizeof(struct nft_rhash);
 340}
 341
 342static int nft_rhash_init(const struct nft_set *set,
 343                          const struct nft_set_desc *desc,
 344                          const struct nlattr * const tb[])
 345{
 346        struct nft_rhash *priv = nft_set_priv(set);
 347        struct rhashtable_params params = nft_rhash_params;
 348        int err;
 349
 350        params.nelem_hint = desc->size ?: NFT_RHASH_ELEMENT_HINT;
 351        params.key_len    = set->klen;
 352
 353        err = rhashtable_init(&priv->ht, &params);
 354        if (err < 0)
 355                return err;
 356
 357        INIT_DEFERRABLE_WORK(&priv->gc_work, nft_rhash_gc);
 358        if (set->flags & NFT_SET_TIMEOUT)
 359                queue_delayed_work(system_power_efficient_wq, &priv->gc_work,
 360                                   nft_set_gc_interval(set));
 361        return 0;
 362}
 363
 364static void nft_rhash_elem_destroy(void *ptr, void *arg)
 365{
 366        nft_set_elem_destroy(arg, ptr, true);
 367}
 368
 369static void nft_rhash_destroy(const struct nft_set *set)
 370{
 371        struct nft_rhash *priv = nft_set_priv(set);
 372
 373        cancel_delayed_work_sync(&priv->gc_work);
 374        rhashtable_free_and_destroy(&priv->ht, nft_rhash_elem_destroy,
 375                                    (void *)set);
 376}
 377
 378static u32 nft_hash_buckets(u32 size)
 379{
 380        return roundup_pow_of_two(size * 4 / 3);
 381}
 382
 383static bool nft_rhash_estimate(const struct nft_set_desc *desc, u32 features,
 384                               struct nft_set_estimate *est)
 385{
 386        est->size   = ~0;
 387        est->lookup = NFT_SET_CLASS_O_1;
 388        est->space  = NFT_SET_CLASS_O_N;
 389
 390        return true;
 391}
 392
 393struct nft_hash {
 394        u32                             seed;
 395        u32                             buckets;
 396        struct hlist_head               table[];
 397};
 398
 399struct nft_hash_elem {
 400        struct hlist_node               node;
 401        struct nft_set_ext              ext;
 402};
 403
 404static bool nft_hash_lookup(const struct net *net, const struct nft_set *set,
 405                            const u32 *key, const struct nft_set_ext **ext)
 406{
 407        struct nft_hash *priv = nft_set_priv(set);
 408        u8 genmask = nft_genmask_cur(net);
 409        const struct nft_hash_elem *he;
 410        u32 hash;
 411
 412        hash = jhash(key, set->klen, priv->seed);
 413        hash = reciprocal_scale(hash, priv->buckets);
 414        hlist_for_each_entry_rcu(he, &priv->table[hash], node) {
 415                if (!memcmp(nft_set_ext_key(&he->ext), key, set->klen) &&
 416                    nft_set_elem_active(&he->ext, genmask)) {
 417                        *ext = &he->ext;
 418                        return true;
 419                }
 420        }
 421        return false;
 422}
 423
 424static void *nft_hash_get(const struct net *net, const struct nft_set *set,
 425                          const struct nft_set_elem *elem, unsigned int flags)
 426{
 427        struct nft_hash *priv = nft_set_priv(set);
 428        u8 genmask = nft_genmask_cur(net);
 429        struct nft_hash_elem *he;
 430        u32 hash;
 431
 432        hash = jhash(elem->key.val.data, set->klen, priv->seed);
 433        hash = reciprocal_scale(hash, priv->buckets);
 434        hlist_for_each_entry_rcu(he, &priv->table[hash], node) {
 435                if (!memcmp(nft_set_ext_key(&he->ext), elem->key.val.data, set->klen) &&
 436                    nft_set_elem_active(&he->ext, genmask))
 437                        return he;
 438        }
 439        return ERR_PTR(-ENOENT);
 440}
 441
 442/* nft_hash_select_ops() makes sure key size can be either 2 or 4 bytes . */
 443static inline u32 nft_hash_key(const u32 *key, u32 klen)
 444{
 445        if (klen == 4)
 446                return *key;
 447
 448        return *(u16 *)key;
 449}
 450
 451static bool nft_hash_lookup_fast(const struct net *net,
 452                                 const struct nft_set *set,
 453                                 const u32 *key, const struct nft_set_ext **ext)
 454{
 455        struct nft_hash *priv = nft_set_priv(set);
 456        u8 genmask = nft_genmask_cur(net);
 457        const struct nft_hash_elem *he;
 458        u32 hash, k1, k2;
 459
 460        k1 = nft_hash_key(key, set->klen);
 461        hash = jhash_1word(k1, priv->seed);
 462        hash = reciprocal_scale(hash, priv->buckets);
 463        hlist_for_each_entry_rcu(he, &priv->table[hash], node) {
 464                k2 = nft_hash_key(nft_set_ext_key(&he->ext)->data, set->klen);
 465                if (k1 == k2 &&
 466                    nft_set_elem_active(&he->ext, genmask)) {
 467                        *ext = &he->ext;
 468                        return true;
 469                }
 470        }
 471        return false;
 472}
 473
 474static int nft_hash_insert(const struct net *net, const struct nft_set *set,
 475                           const struct nft_set_elem *elem,
 476                           struct nft_set_ext **ext)
 477{
 478        struct nft_hash_elem *this = elem->priv, *he;
 479        struct nft_hash *priv = nft_set_priv(set);
 480        u8 genmask = nft_genmask_next(net);
 481        u32 hash;
 482
 483        hash = jhash(nft_set_ext_key(&this->ext), set->klen, priv->seed);
 484        hash = reciprocal_scale(hash, priv->buckets);
 485        hlist_for_each_entry(he, &priv->table[hash], node) {
 486                if (!memcmp(nft_set_ext_key(&this->ext),
 487                            nft_set_ext_key(&he->ext), set->klen) &&
 488                    nft_set_elem_active(&he->ext, genmask)) {
 489                        *ext = &he->ext;
 490                        return -EEXIST;
 491                }
 492        }
 493        hlist_add_head_rcu(&this->node, &priv->table[hash]);
 494        return 0;
 495}
 496
 497static void nft_hash_activate(const struct net *net, const struct nft_set *set,
 498                              const struct nft_set_elem *elem)
 499{
 500        struct nft_hash_elem *he = elem->priv;
 501
 502        nft_set_elem_change_active(net, set, &he->ext);
 503}
 504
 505static bool nft_hash_flush(const struct net *net,
 506                           const struct nft_set *set, void *priv)
 507{
 508        struct nft_hash_elem *he = priv;
 509
 510        nft_set_elem_change_active(net, set, &he->ext);
 511        return true;
 512}
 513
 514static void *nft_hash_deactivate(const struct net *net,
 515                                 const struct nft_set *set,
 516                                 const struct nft_set_elem *elem)
 517{
 518        struct nft_hash *priv = nft_set_priv(set);
 519        struct nft_hash_elem *this = elem->priv, *he;
 520        u8 genmask = nft_genmask_next(net);
 521        u32 hash;
 522
 523        hash = jhash(nft_set_ext_key(&this->ext), set->klen, priv->seed);
 524        hash = reciprocal_scale(hash, priv->buckets);
 525        hlist_for_each_entry(he, &priv->table[hash], node) {
 526                if (!memcmp(nft_set_ext_key(&this->ext), &elem->key.val,
 527                            set->klen) &&
 528                    nft_set_elem_active(&he->ext, genmask)) {
 529                        nft_set_elem_change_active(net, set, &he->ext);
 530                        return he;
 531                }
 532        }
 533        return NULL;
 534}
 535
 536static void nft_hash_remove(const struct net *net,
 537                            const struct nft_set *set,
 538                            const struct nft_set_elem *elem)
 539{
 540        struct nft_hash_elem *he = elem->priv;
 541
 542        hlist_del_rcu(&he->node);
 543}
 544
 545static void nft_hash_walk(const struct nft_ctx *ctx, struct nft_set *set,
 546                          struct nft_set_iter *iter)
 547{
 548        struct nft_hash *priv = nft_set_priv(set);
 549        struct nft_hash_elem *he;
 550        struct nft_set_elem elem;
 551        int i;
 552
 553        for (i = 0; i < priv->buckets; i++) {
 554                hlist_for_each_entry_rcu(he, &priv->table[i], node) {
 555                        if (iter->count < iter->skip)
 556                                goto cont;
 557                        if (!nft_set_elem_active(&he->ext, iter->genmask))
 558                                goto cont;
 559
 560                        elem.priv = he;
 561
 562                        iter->err = iter->fn(ctx, set, iter, &elem);
 563                        if (iter->err < 0)
 564                                return;
 565cont:
 566                        iter->count++;
 567                }
 568        }
 569}
 570
 571static unsigned int nft_hash_privsize(const struct nlattr * const nla[],
 572                                      const struct nft_set_desc *desc)
 573{
 574        return sizeof(struct nft_hash) +
 575               nft_hash_buckets(desc->size) * sizeof(struct hlist_head);
 576}
 577
 578static int nft_hash_init(const struct nft_set *set,
 579                         const struct nft_set_desc *desc,
 580                         const struct nlattr * const tb[])
 581{
 582        struct nft_hash *priv = nft_set_priv(set);
 583
 584        priv->buckets = nft_hash_buckets(desc->size);
 585        get_random_bytes(&priv->seed, sizeof(priv->seed));
 586
 587        return 0;
 588}
 589
 590static void nft_hash_destroy(const struct nft_set *set)
 591{
 592        struct nft_hash *priv = nft_set_priv(set);
 593        struct nft_hash_elem *he;
 594        struct hlist_node *next;
 595        int i;
 596
 597        for (i = 0; i < priv->buckets; i++) {
 598                hlist_for_each_entry_safe(he, next, &priv->table[i], node) {
 599                        hlist_del_rcu(&he->node);
 600                        nft_set_elem_destroy(set, he, true);
 601                }
 602        }
 603}
 604
 605static bool nft_hash_estimate(const struct nft_set_desc *desc, u32 features,
 606                              struct nft_set_estimate *est)
 607{
 608        est->size   = sizeof(struct nft_hash) +
 609                      nft_hash_buckets(desc->size) * sizeof(struct hlist_head) +
 610                      desc->size * sizeof(struct nft_hash_elem);
 611        est->lookup = NFT_SET_CLASS_O_1;
 612        est->space  = NFT_SET_CLASS_O_N;
 613
 614        return true;
 615}
 616
 617static struct nft_set_type nft_hash_type;
 618static struct nft_set_ops nft_rhash_ops __read_mostly = {
 619        .type           = &nft_hash_type,
 620        .privsize       = nft_rhash_privsize,
 621        .elemsize       = offsetof(struct nft_rhash_elem, ext),
 622        .estimate       = nft_rhash_estimate,
 623        .init           = nft_rhash_init,
 624        .destroy        = nft_rhash_destroy,
 625        .insert         = nft_rhash_insert,
 626        .activate       = nft_rhash_activate,
 627        .deactivate     = nft_rhash_deactivate,
 628        .flush          = nft_rhash_flush,
 629        .remove         = nft_rhash_remove,
 630        .lookup         = nft_rhash_lookup,
 631        .update         = nft_rhash_update,
 632        .walk           = nft_rhash_walk,
 633        .get            = nft_rhash_get,
 634        .features       = NFT_SET_MAP | NFT_SET_OBJECT | NFT_SET_TIMEOUT,
 635};
 636
 637static struct nft_set_ops nft_hash_ops __read_mostly = {
 638        .type           = &nft_hash_type,
 639        .privsize       = nft_hash_privsize,
 640        .elemsize       = offsetof(struct nft_hash_elem, ext),
 641        .estimate       = nft_hash_estimate,
 642        .init           = nft_hash_init,
 643        .destroy        = nft_hash_destroy,
 644        .insert         = nft_hash_insert,
 645        .activate       = nft_hash_activate,
 646        .deactivate     = nft_hash_deactivate,
 647        .flush          = nft_hash_flush,
 648        .remove         = nft_hash_remove,
 649        .lookup         = nft_hash_lookup,
 650        .walk           = nft_hash_walk,
 651        .get            = nft_hash_get,
 652        .features       = NFT_SET_MAP | NFT_SET_OBJECT,
 653};
 654
 655static struct nft_set_ops nft_hash_fast_ops __read_mostly = {
 656        .type           = &nft_hash_type,
 657        .privsize       = nft_hash_privsize,
 658        .elemsize       = offsetof(struct nft_hash_elem, ext),
 659        .estimate       = nft_hash_estimate,
 660        .init           = nft_hash_init,
 661        .destroy        = nft_hash_destroy,
 662        .insert         = nft_hash_insert,
 663        .activate       = nft_hash_activate,
 664        .deactivate     = nft_hash_deactivate,
 665        .flush          = nft_hash_flush,
 666        .remove         = nft_hash_remove,
 667        .lookup         = nft_hash_lookup_fast,
 668        .walk           = nft_hash_walk,
 669        .get            = nft_hash_get,
 670        .features       = NFT_SET_MAP | NFT_SET_OBJECT,
 671};
 672
 673static const struct nft_set_ops *
 674nft_hash_select_ops(const struct nft_ctx *ctx, const struct nft_set_desc *desc,
 675                    u32 flags)
 676{
 677        if (desc->size && !(flags & (NFT_SET_EVAL | NFT_SET_TIMEOUT))) {
 678                switch (desc->klen) {
 679                case 4:
 680                        return &nft_hash_fast_ops;
 681                default:
 682                        return &nft_hash_ops;
 683                }
 684        }
 685
 686        return &nft_rhash_ops;
 687}
 688
 689static struct nft_set_type nft_hash_type __read_mostly = {
 690        .select_ops     = nft_hash_select_ops,
 691        .owner          = THIS_MODULE,
 692};
 693
 694static int __init nft_hash_module_init(void)
 695{
 696        return nft_register_set(&nft_hash_type);
 697}
 698
 699static void __exit nft_hash_module_exit(void)
 700{
 701        nft_unregister_set(&nft_hash_type);
 702}
 703
 704module_init(nft_hash_module_init);
 705module_exit(nft_hash_module_exit);
 706
 707MODULE_LICENSE("GPL");
 708MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
 709MODULE_ALIAS_NFT_SET();
 710