src/openvpn/list.c
6fbf66fa
 /*
  *  OpenVPN -- An application to securely tunnel IP networks
  *             over a single TCP/UDP port, with support for SSL/TLS-based
  *             session authentication and key exchange,
  *             packet encryption, packet authentication, and
  *             packet compression.
  *
58716979
  *  Copyright (C) 2002-2017 OpenVPN Technologies, Inc. <sales@openvpn.net>
6fbf66fa
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2
  *  as published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
caa54ac3
  *  You should have received a copy of the GNU General Public License along
  *  with this program; if not, write to the Free Software Foundation, Inc.,
  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
6fbf66fa
  */
 
c110b289
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #elif defined(_MSC_VER)
 #include "config-msvc.h"
 #endif
 
6fbf66fa
 #include "syshead.h"
 
 #if P2MP_SERVER
 
9fc0e963
 #include "integer.h"
6fbf66fa
 #include "list.h"
 #include "misc.h"
 
 #include "memdbg.h"
 
 struct hash *
81d882d5
 hash_init(const int n_buckets,
           const uint32_t iv,
           uint32_t (*hash_function)(const void *key, uint32_t iv),
           bool (*compare_function)(const void *key1, const void *key2))
6fbf66fa
 {
81d882d5
     struct hash *h;
     int i;
 
     ASSERT(n_buckets > 0);
     ALLOC_OBJ_CLEAR(h, struct hash);
     h->n_buckets = (int) adjust_power_of_2(n_buckets);
     h->mask = h->n_buckets - 1;
     h->hash_function = hash_function;
     h->compare_function = compare_function;
     h->iv = iv;
     ALLOC_ARRAY(h->buckets, struct hash_bucket, h->n_buckets);
     for (i = 0; i < h->n_buckets; ++i)
6fbf66fa
     {
81d882d5
         struct hash_bucket *b = &h->buckets[i];
         b->list = NULL;
6fbf66fa
     }
81d882d5
     return h;
6fbf66fa
 }
 
 void
81d882d5
 hash_free(struct hash *hash)
6fbf66fa
 {
81d882d5
     int i;
     for (i = 0; i < hash->n_buckets; ++i)
6fbf66fa
     {
81d882d5
         struct hash_bucket *b = &hash->buckets[i];
         struct hash_element *he = b->list;
 
         while (he)
         {
             struct hash_element *next = he->next;
             free(he);
             he = next;
         }
6fbf66fa
     }
81d882d5
     free(hash->buckets);
     free(hash);
6fbf66fa
 }
 
 struct hash_element *
81d882d5
 hash_lookup_fast(struct hash *hash,
                  struct hash_bucket *bucket,
                  const void *key,
                  uint32_t hv)
6fbf66fa
 {
81d882d5
     struct hash_element *he;
     struct hash_element *prev = NULL;
6fbf66fa
 
81d882d5
     he = bucket->list;
6fbf66fa
 
81d882d5
     while (he)
6fbf66fa
     {
81d882d5
         if (hv == he->hash_value && (*hash->compare_function)(key, he->key))
         {
             /* move to head of list */
             if (prev)
             {
                 prev->next = he->next;
                 he->next = bucket->list;
                 bucket->list = he;
             }
             return he;
         }
         prev = he;
         he = he->next;
6fbf66fa
     }
 
81d882d5
     return NULL;
6fbf66fa
 }
 
 bool
81d882d5
 hash_remove_fast(struct hash *hash,
                  struct hash_bucket *bucket,
                  const void *key,
                  uint32_t hv)
6fbf66fa
 {
81d882d5
     struct hash_element *he;
     struct hash_element *prev = NULL;
6fbf66fa
 
81d882d5
     he = bucket->list;
6fbf66fa
 
81d882d5
     while (he)
6fbf66fa
     {
81d882d5
         if (hv == he->hash_value && (*hash->compare_function)(key, he->key))
         {
             if (prev)
             {
                 prev->next = he->next;
             }
             else
             {
                 bucket->list = he->next;
             }
             free(he);
             --hash->n_elements;
             return true;
         }
         prev = he;
         he = he->next;
6fbf66fa
     }
81d882d5
     return false;
6fbf66fa
 }
 
 bool
81d882d5
 hash_add(struct hash *hash, const void *key, void *value, bool replace)
6fbf66fa
 {
81d882d5
     uint32_t hv;
     struct hash_bucket *bucket;
     struct hash_element *he;
     bool ret = false;
6fbf66fa
 
81d882d5
     hv = hash_value(hash, key);
     bucket = &hash->buckets[hv & hash->mask];
6fbf66fa
 
81d882d5
     if ((he = hash_lookup_fast(hash, bucket, key, hv))) /* already exists? */
6fbf66fa
     {
81d882d5
         if (replace)
         {
             he->value = value;
             ret = true;
         }
6fbf66fa
     }
81d882d5
     else
6fbf66fa
     {
81d882d5
         hash_add_fast(hash, bucket, key, hv, value);
         ret = true;
6fbf66fa
     }
 
81d882d5
     return ret;
6fbf66fa
 }
 
 void
81d882d5
 hash_remove_by_value(struct hash *hash, void *value)
6fbf66fa
 {
81d882d5
     struct hash_iterator hi;
     struct hash_element *he;
6fbf66fa
 
81d882d5
     hash_iterator_init(hash, &hi);
     while ((he = hash_iterator_next(&hi)))
6fbf66fa
     {
81d882d5
         if (he->value == value)
         {
             hash_iterator_delete_element(&hi);
         }
6fbf66fa
     }
81d882d5
     hash_iterator_free(&hi);
6fbf66fa
 }
 
 static void
81d882d5
 hash_remove_marked(struct hash *hash, struct hash_bucket *bucket)
6fbf66fa
 {
81d882d5
     struct hash_element *prev = NULL;
     struct hash_element *he = bucket->list;
6fbf66fa
 
81d882d5
     while (he)
6fbf66fa
     {
81d882d5
         if (!he->key) /* marked? */
         {
             struct hash_element *newhe;
             if (prev)
             {
                 newhe = prev->next = he->next;
             }
             else
             {
                 newhe = bucket->list = he->next;
             }
             free(he);
             --hash->n_elements;
             he = newhe;
         }
         else
         {
             prev = he;
             he = he->next;
         }
6fbf66fa
     }
 }
 
 uint32_t
81d882d5
 void_ptr_hash_function(const void *key, uint32_t iv)
6fbf66fa
 {
81d882d5
     return hash_func((const void *)&key, sizeof(key), iv);
6fbf66fa
 }
 
 bool
81d882d5
 void_ptr_compare_function(const void *key1, const void *key2)
6fbf66fa
 {
81d882d5
     return key1 == key2;
6fbf66fa
 }
 
 void
81d882d5
 hash_iterator_init_range(struct hash *hash,
                          struct hash_iterator *hi,
                          int start_bucket,
                          int end_bucket)
6fbf66fa
 {
81d882d5
     if (end_bucket > hash->n_buckets)
     {
         end_bucket = hash->n_buckets;
     }
 
     ASSERT(start_bucket >= 0 && start_bucket <= end_bucket);
 
     hi->hash = hash;
     hi->elem = NULL;
     hi->bucket = NULL;
     hi->last = NULL;
     hi->bucket_marked = false;
     hi->bucket_index_start = start_bucket;
     hi->bucket_index_end = end_bucket;
     hi->bucket_index = hi->bucket_index_start - 1;
6fbf66fa
 }
 
 void
81d882d5
 hash_iterator_init(struct hash *hash,
                    struct hash_iterator *hi)
6fbf66fa
 {
81d882d5
     hash_iterator_init_range(hash, hi, 0, hash->n_buckets);
6fbf66fa
 }
 
 static inline void
81d882d5
 hash_iterator_lock(struct hash_iterator *hi, struct hash_bucket *b)
6fbf66fa
 {
81d882d5
     hi->bucket = b;
     hi->last = NULL;
     hi->bucket_marked = false;
6fbf66fa
 }
 
 static inline void
81d882d5
 hash_iterator_unlock(struct hash_iterator *hi)
6fbf66fa
 {
81d882d5
     if (hi->bucket)
6fbf66fa
     {
81d882d5
         if (hi->bucket_marked)
         {
             hash_remove_marked(hi->hash, hi->bucket);
             hi->bucket_marked = false;
         }
         hi->bucket = NULL;
         hi->last = NULL;
6fbf66fa
     }
 }
 
 static inline void
81d882d5
 hash_iterator_advance(struct hash_iterator *hi)
6fbf66fa
 {
81d882d5
     hi->last = hi->elem;
     hi->elem = hi->elem->next;
6fbf66fa
 }
 
 void
81d882d5
 hash_iterator_free(struct hash_iterator *hi)
6fbf66fa
 {
81d882d5
     hash_iterator_unlock(hi);
6fbf66fa
 }
 
 struct hash_element *
81d882d5
 hash_iterator_next(struct hash_iterator *hi)
6fbf66fa
 {
81d882d5
     struct hash_element *ret = NULL;
     if (hi->elem)
6fbf66fa
     {
81d882d5
         ret = hi->elem;
         hash_iterator_advance(hi);
6fbf66fa
     }
81d882d5
     else
6fbf66fa
     {
81d882d5
         while (++hi->bucket_index < hi->bucket_index_end)
         {
             struct hash_bucket *b;
             hash_iterator_unlock(hi);
             b = &hi->hash->buckets[hi->bucket_index];
             if (b->list)
             {
                 hash_iterator_lock(hi, b);
                 hi->elem = b->list;
                 if (hi->elem)
                 {
                     ret = hi->elem;
                     hash_iterator_advance(hi);
                     break;
                 }
             }
         }
6fbf66fa
     }
81d882d5
     return ret;
6fbf66fa
 }
 
 void
81d882d5
 hash_iterator_delete_element(struct hash_iterator *hi)
6fbf66fa
 {
81d882d5
     ASSERT(hi->last);
     hi->last->key = NULL;
     hi->bucket_marked = true;
6fbf66fa
 }
 
 
 #ifdef LIST_TEST
 
 /*
  * Test the hash code by implementing a simple
  * word frequency algorithm.
  */
 
 struct word
 {
81d882d5
     const char *word;
     int n;
6fbf66fa
 };
 
 static uint32_t
81d882d5
 word_hash_function(const void *key, uint32_t iv)
6fbf66fa
 {
81d882d5
     const char *str = (const char *) key;
     const int len = strlen(str);
     return hash_func((const uint8_t *)str, len, iv);
6fbf66fa
 }
 
 static bool
81d882d5
 word_compare_function(const void *key1, const void *key2)
6fbf66fa
 {
81d882d5
     return strcmp((const char *)key1, (const char *)key2) == 0;
6fbf66fa
 }
 
 static void
81d882d5
 print_nhash(struct hash *hash)
6fbf66fa
 {
81d882d5
     struct hash_iterator hi;
     struct hash_element *he;
     int count = 0;
6fbf66fa
 
81d882d5
     hash_iterator_init(hash, &hi, true);
6fbf66fa
 
81d882d5
     while ((he = hash_iterator_next(&hi)))
6fbf66fa
     {
81d882d5
         printf("%d ", (int) he->value);
         ++count;
6fbf66fa
     }
81d882d5
     printf("\n");
6fbf66fa
 
81d882d5
     hash_iterator_free(&hi);
     ASSERT(count == hash_n_elements(hash));
6fbf66fa
 }
 
 static void
81d882d5
 rmhash(struct hash *hash, const char *word)
6fbf66fa
 {
81d882d5
     hash_remove(hash, word);
6fbf66fa
 }
 
 void
81d882d5
 list_test(void)
6fbf66fa
 {
81d882d5
     openvpn_thread_init();
6fbf66fa
 
     {
81d882d5
         struct gc_arena gc = gc_new();
         struct hash *hash = hash_init(10000, get_random(), word_hash_function, word_compare_function);
         struct hash *nhash = hash_init(256, get_random(), word_hash_function, word_compare_function);
 
         printf("hash_init n_buckets=%d mask=0x%08x\n", hash->n_buckets, hash->mask);
 
         /* parse words from stdin */
         while (true)
         {
             char buf[256];
             char wordbuf[256];
             int wbi;
             int bi;
             char c;
 
             if (!fgets(buf, sizeof(buf), stdin))
             {
                 break;
             }
 
             bi = wbi = 0;
             do
             {
                 c = buf[bi++];
                 if (isalnum(c) || c == '_')
                 {
                     ASSERT(wbi < (int) sizeof(wordbuf));
                     wordbuf[wbi++] = c;
                 }
                 else
                 {
                     if (wbi)
                     {
                         struct word *w;
                         ASSERT(wbi < (int) sizeof(wordbuf));
                         wordbuf[wbi++] = '\0';
 
                         /* word is parsed from stdin */
 
                         /* does it already exist in table? */
                         w = (struct word *) hash_lookup(hash, wordbuf);
 
                         if (w)
                         {
                             /* yes, increment count */
                             ++w->n;
                         }
                         else
                         {
                             /* no, make a new object */
                             ALLOC_OBJ_GC(w, struct word, &gc);
                             w->word = string_alloc(wordbuf, &gc);
                             w->n = 1;
                             ASSERT(hash_add(hash, w->word, w, false));
                             ASSERT(hash_add(nhash, w->word, (void *) ((random() & 0x0F) + 1), false));
                         }
                     }
                     wbi = 0;
                 }
             } while (c);
         }
 
 #if 1
         /* remove some words from the table */
         {
             rmhash(hash, "true");
             rmhash(hash, "false");
         }
6fbf66fa
 #endif
 
81d882d5
         /* output contents of hash table */
         {
             int base;
             int inc = 0;
             int count = 0;
 
4cd4899e
             for (base = 0; base < hash_n_buckets(hash); base += inc)
             {
81d882d5
                 struct hash_iterator hi;
                 struct hash_element *he;
                 inc = (get_random() % 3) + 1;
                 hash_iterator_init_range(hash, &hi, true, base, base + inc);
 
                 while ((he = hash_iterator_next(&hi)))
                 {
                     struct word *w = (struct word *) he->value;
                     printf("%6d '%s'\n", w->n, w->word);
                     ++count;
                 }
 
                 hash_iterator_free(&hi);
             }
             ASSERT(count == hash_n_elements(hash));
         }
 
6fbf66fa
 #if 1
81d882d5
         /* test hash_remove_by_value function */
         {
             int i;
             for (i = 1; i <= 16; ++i)
             {
                 printf("[%d] ***********************************\n", i);
                 print_nhash(nhash);
                 hash_remove_by_value(nhash, (void *) i, true);
             }
             printf("FINAL **************************\n");
             print_nhash(nhash);
         }
6fbf66fa
 #endif
 
81d882d5
         hash_free(hash);
         hash_free(nhash);
         gc_free(&gc);
     }
6fbf66fa
 
81d882d5
     openvpn_thread_cleanup();
6fbf66fa
 }
 
81d882d5
 #endif /* ifdef LIST_TEST */
6fbf66fa
 
 /*
81d882d5
  * --------------------------------------------------------------------
  * hash() -- hash a variable-length key into a 32-bit value
  * k     : the key (the unaligned variable-length array of bytes)
  * len   : the length of the key, counting by bytes
  * level : can be any 4-byte value
  * Returns a 32-bit value.  Every bit of the key affects every bit of
  * the return value.  Every 1-bit and 2-bit delta achieves avalanche.
  * About 36+6len instructions.
  *
  * The best hash table sizes are powers of 2.  There is no need to do
  * mod a prime (mod is sooo slow!).  If you need less than 32 bits,
  * use a bitmask.  For example, if you need only 10 bits, do
  * h = (h & hashmask(10));
  * In which case, the hash table should have hashsize(10) elements.
  *
  * If you are hashing n strings (uint8_t **)k, do it like this:
  * for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h);
  *
  * By Bob Jenkins, 1996.  bob_jenkins@burtleburtle.net.  You may use this
  * code any way you wish, private, educational, or commercial.  It's free.
  *
  * See http://burlteburtle.net/bob/hash/evahash.html
  * Use for hash table lookup, or anything where one collision in 2^32 is
  * acceptable.  Do NOT use for cryptographic purposes.
  *
  * --------------------------------------------------------------------
  *
  * mix -- mix 3 32-bit values reversibly.
  * For every delta with one or two bit set, and the deltas of all three
  * high bits or all three low bits, whether the original value of a,b,c
  * is almost all zero or is uniformly distributed,
  * If mix() is run forward or backward, at least 32 bits in a,b,c
  * have at least 1/4 probability of changing.
  * If mix() is run forward, every bit of c will change between 1/3 and
  * 2/3 of the time.  (Well, 22/100 and 78/100 for some 2-bit deltas.)
  * mix() was built out of 36 single-cycle latency instructions in a
  * structure that could supported 2x parallelism, like so:
  *    a -= b;
  *    a -= c; x = (c>>13);
  *    b -= c; a ^= x;
  *    b -= a; x = (a<<8);
  *    c -= a; b ^= x;
  *    c -= b; x = (b>>13);
  *    ...
  * Unfortunately, superscalar Pentiums and Sparcs can't take advantage
  * of that parallelism.  They've also turned some of those single-cycle
  * latency instructions into multi-cycle latency instructions.  Still,
  * this is the fastest good hash I could find.  There were about 2^^68
  * to choose from.  I only looked at a billion or so.
  *
  * James Yonan Notes:
  *
  * This function is faster than it looks, and appears to be
  * appropriate for our usage in OpenVPN which is primarily
  * for hash-table based address lookup (IPv4, IPv6, and Ethernet MAC).
  * NOTE: This function is never used for cryptographic purposes, only
  * to produce evenly-distributed indexes into hash tables.
  *
  * Benchmark results: 11.39 machine cycles per byte on a P2 266Mhz,
  *                   and 12.1 machine cycles per byte on a
  *                   2.2 Ghz P4 when hashing a 6 byte string.
  * --------------------------------------------------------------------
  */
6fbf66fa
 
 #define mix(a,b,c)               \
81d882d5
     {                                \
         a -= b; a -= c; a ^= (c>>13);  \
         b -= c; b -= a; b ^= (a<<8);   \
         c -= a; c -= b; c ^= (b>>13);  \
         a -= b; a -= c; a ^= (c>>12);  \
         b -= c; b -= a; b ^= (a<<16);  \
         c -= a; c -= b; c ^= (b>>5);   \
         a -= b; a -= c; a ^= (c>>3);   \
         b -= c; b -= a; b ^= (a<<10);  \
         c -= a; c -= b; c ^= (b>>15);  \
     }
6fbf66fa
 
 uint32_t
81d882d5
 hash_func(const uint8_t *k, uint32_t length, uint32_t initval)
6fbf66fa
 {
81d882d5
     uint32_t a, b, c, len;
6fbf66fa
 
81d882d5
     /* Set up the internal state */
     len = length;
     a = b = 0x9e3779b9;      /* the golden ratio; an arbitrary value */
     c = initval;             /* the previous hash value */
6fbf66fa
 
81d882d5
     /*---------------------------------------- handle most of the key */
     while (len >= 12)
6fbf66fa
     {
81d882d5
         a += (k[0] + ((uint32_t) k[1] << 8)
               + ((uint32_t) k[2] << 16)
               + ((uint32_t) k[3] << 24));
         b += (k[4] + ((uint32_t) k[5] << 8)
               + ((uint32_t) k[6] << 16)
               + ((uint32_t) k[7] << 24));
         c += (k[8] + ((uint32_t) k[9] << 8)
               + ((uint32_t) k[10] << 16)
               + ((uint32_t) k[11] << 24));
         mix(a, b, c);
         k += 12;
         len -= 12;
6fbf66fa
     }
 
81d882d5
     /*------------------------------------- handle the last 11 bytes */
     c += length;
     switch (len)            /* all the case statements fall through */
6fbf66fa
     {
81d882d5
         case 11:
             c += ((uint32_t) k[10] << 24);
 
         case 10:
             c += ((uint32_t) k[9] << 16);
 
         case 9:
             c += ((uint32_t) k[8] << 8);
 
         /* the first byte of c is reserved for the length */
         case 8:
             b += ((uint32_t) k[7] << 24);
 
         case 7:
             b += ((uint32_t) k[6] << 16);
 
         case 6:
             b += ((uint32_t) k[5] << 8);
 
         case 5:
             b += k[4];
 
         case 4:
             a += ((uint32_t) k[3] << 24);
 
         case 3:
             a += ((uint32_t) k[2] << 16);
 
         case 2:
             a += ((uint32_t) k[1] << 8);
 
         case 1:
             a += k[0];
             /* case 0: nothing left to add */
6fbf66fa
     }
81d882d5
     mix(a, b, c);
     /*-------------------------------------- report the result */
     return c;
6fbf66fa
 }
 
81d882d5
 #else  /* if P2MP_SERVER */
 static void
4cd4899e
 dummy(void)
 {
81d882d5
 }
6fbf66fa
 #endif /* P2MP_SERVER */