mirror of
https://github.com/OpenVPN/openvpn.git
synced 2025-05-08 21:25:53 +08:00

git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@5599 e7ae566f-a301-0410-adde-c780ea21d3b5
660 lines
15 KiB
C
660 lines
15 KiB
C
/*
|
|
* 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.
|
|
*
|
|
* Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program (see the file COPYING included with this
|
|
* distribution); if not, write to the Free Software Foundation, Inc.,
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "syshead.h"
|
|
|
|
#if P2MP_SERVER
|
|
|
|
#include "list.h"
|
|
#include "misc.h"
|
|
|
|
#include "memdbg.h"
|
|
|
|
struct hash *
|
|
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))
|
|
{
|
|
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)
|
|
{
|
|
struct hash_bucket *b = &h->buckets[i];
|
|
b->list = NULL;
|
|
mutex_init (&b->mutex);
|
|
}
|
|
return h;
|
|
}
|
|
|
|
void
|
|
hash_free (struct hash *hash)
|
|
{
|
|
int i;
|
|
for (i = 0; i < hash->n_buckets; ++i)
|
|
{
|
|
struct hash_bucket *b = &hash->buckets[i];
|
|
struct hash_element *he = b->list;
|
|
|
|
mutex_destroy (&b->mutex);
|
|
while (he)
|
|
{
|
|
struct hash_element *next = he->next;
|
|
free (he);
|
|
he = next;
|
|
}
|
|
}
|
|
free (hash->buckets);
|
|
free (hash);
|
|
}
|
|
|
|
struct hash_element *
|
|
hash_lookup_fast (struct hash *hash,
|
|
struct hash_bucket *bucket,
|
|
const void *key,
|
|
uint32_t hv)
|
|
{
|
|
struct hash_element *he;
|
|
struct hash_element *prev = NULL;
|
|
|
|
he = bucket->list;
|
|
|
|
while (he)
|
|
{
|
|
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;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
bool
|
|
hash_remove_fast (struct hash *hash,
|
|
struct hash_bucket *bucket,
|
|
const void *key,
|
|
uint32_t hv)
|
|
{
|
|
struct hash_element *he;
|
|
struct hash_element *prev = NULL;
|
|
|
|
he = bucket->list;
|
|
|
|
while (he)
|
|
{
|
|
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;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
hash_add (struct hash *hash, const void *key, void *value, bool replace)
|
|
{
|
|
uint32_t hv;
|
|
struct hash_bucket *bucket;
|
|
struct hash_element *he;
|
|
bool ret = false;
|
|
|
|
hv = hash_value (hash, key);
|
|
bucket = &hash->buckets[hv & hash->mask];
|
|
mutex_lock (&bucket->mutex);
|
|
|
|
if ((he = hash_lookup_fast (hash, bucket, key, hv))) /* already exists? */
|
|
{
|
|
if (replace)
|
|
{
|
|
he->value = value;
|
|
ret = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hash_add_fast (hash, bucket, key, hv, value);
|
|
ret = true;
|
|
}
|
|
|
|
mutex_unlock (&bucket->mutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
hash_remove_by_value (struct hash *hash, void *value, bool autolock)
|
|
{
|
|
struct hash_iterator hi;
|
|
struct hash_element *he;
|
|
|
|
hash_iterator_init (hash, &hi, autolock);
|
|
while ((he = hash_iterator_next (&hi)))
|
|
{
|
|
if (he->value == value)
|
|
hash_iterator_delete_element (&hi);
|
|
}
|
|
hash_iterator_free (&hi);
|
|
}
|
|
|
|
static void
|
|
hash_remove_marked (struct hash *hash, struct hash_bucket *bucket)
|
|
{
|
|
struct hash_element *prev = NULL;
|
|
struct hash_element *he = bucket->list;
|
|
|
|
while (he)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t
|
|
void_ptr_hash_function (const void *key, uint32_t iv)
|
|
{
|
|
return hash_func ((const void *)&key, sizeof (key), iv);
|
|
}
|
|
|
|
bool
|
|
void_ptr_compare_function (const void *key1, const void *key2)
|
|
{
|
|
return key1 == key2;
|
|
}
|
|
|
|
void
|
|
hash_iterator_init_range (struct hash *hash,
|
|
struct hash_iterator *hi,
|
|
bool autolock,
|
|
int start_bucket,
|
|
int end_bucket)
|
|
{
|
|
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->autolock = autolock;
|
|
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;
|
|
}
|
|
|
|
void
|
|
hash_iterator_init (struct hash *hash,
|
|
struct hash_iterator *hi,
|
|
bool autolock)
|
|
{
|
|
hash_iterator_init_range (hash, hi, autolock, 0, hash->n_buckets);
|
|
}
|
|
|
|
static inline void
|
|
hash_iterator_lock (struct hash_iterator *hi, struct hash_bucket *b)
|
|
{
|
|
if (hi->autolock)
|
|
{
|
|
mutex_lock (&b->mutex);
|
|
}
|
|
hi->bucket = b;
|
|
hi->last = NULL;
|
|
hi->bucket_marked = false;
|
|
}
|
|
|
|
static inline void
|
|
hash_iterator_unlock (struct hash_iterator *hi)
|
|
{
|
|
if (hi->bucket)
|
|
{
|
|
if (hi->bucket_marked)
|
|
{
|
|
hash_remove_marked (hi->hash, hi->bucket);
|
|
hi->bucket_marked = false;
|
|
}
|
|
if (hi->autolock)
|
|
{
|
|
mutex_unlock (&hi->bucket->mutex);
|
|
}
|
|
hi->bucket = NULL;
|
|
hi->last = NULL;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
hash_iterator_advance (struct hash_iterator *hi)
|
|
{
|
|
hi->last = hi->elem;
|
|
hi->elem = hi->elem->next;
|
|
}
|
|
|
|
void
|
|
hash_iterator_free (struct hash_iterator *hi)
|
|
{
|
|
hash_iterator_unlock (hi);
|
|
}
|
|
|
|
struct hash_element *
|
|
hash_iterator_next (struct hash_iterator *hi)
|
|
{
|
|
struct hash_element *ret = NULL;
|
|
if (hi->elem)
|
|
{
|
|
ret = hi->elem;
|
|
hash_iterator_advance (hi);
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
hash_iterator_delete_element (struct hash_iterator *hi)
|
|
{
|
|
ASSERT (hi->last);
|
|
hi->last->key = NULL;
|
|
hi->bucket_marked = true;
|
|
}
|
|
|
|
|
|
#ifdef LIST_TEST
|
|
|
|
/*
|
|
* Test the hash code by implementing a simple
|
|
* word frequency algorithm.
|
|
*/
|
|
|
|
struct word
|
|
{
|
|
const char *word;
|
|
int n;
|
|
};
|
|
|
|
static uint32_t
|
|
word_hash_function (const void *key, uint32_t iv)
|
|
{
|
|
const char *str = (const char *) key;
|
|
const int len = strlen (str);
|
|
return hash_func ((const uint8_t *)str, len, iv);
|
|
}
|
|
|
|
static bool
|
|
word_compare_function (const void *key1, const void *key2)
|
|
{
|
|
return strcmp ((const char *)key1, (const char *)key2) == 0;
|
|
}
|
|
|
|
static void
|
|
print_nhash (struct hash *hash)
|
|
{
|
|
struct hash_iterator hi;
|
|
struct hash_element *he;
|
|
int count = 0;
|
|
|
|
hash_iterator_init (hash, &hi, true);
|
|
|
|
while ((he = hash_iterator_next (&hi)))
|
|
{
|
|
printf ("%d ", (int) he->value);
|
|
++count;
|
|
}
|
|
printf ("\n");
|
|
|
|
hash_iterator_free (&hi);
|
|
ASSERT (count == hash_n_elements (hash));
|
|
}
|
|
|
|
static void
|
|
rmhash (struct hash *hash, const char *word)
|
|
{
|
|
hash_remove (hash, word);
|
|
}
|
|
|
|
void
|
|
list_test (void)
|
|
{
|
|
openvpn_thread_init ();
|
|
|
|
{
|
|
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");
|
|
}
|
|
#endif
|
|
|
|
/* output contents of hash table */
|
|
{
|
|
int base;
|
|
int inc = 0;
|
|
int count = 0;
|
|
|
|
for (base = 0; base < hash_n_buckets (hash); base += inc) {
|
|
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));
|
|
}
|
|
|
|
#if 1
|
|
/* 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);
|
|
}
|
|
#endif
|
|
|
|
hash_free (hash);
|
|
hash_free (nhash);
|
|
gc_free (&gc);
|
|
}
|
|
|
|
openvpn_thread_cleanup ();
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
--------------------------------------------------------------------
|
|
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.
|
|
--------------------------------------------------------------------
|
|
*/
|
|
|
|
#define mix(a,b,c) \
|
|
{ \
|
|
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); \
|
|
}
|
|
|
|
uint32_t
|
|
hash_func (const uint8_t *k, uint32_t length, uint32_t initval)
|
|
{
|
|
uint32_t a, b, c, len;
|
|
|
|
/* Set up the internal state */
|
|
len = length;
|
|
a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
|
|
c = initval; /* the previous hash value */
|
|
|
|
/*---------------------------------------- handle most of the key */
|
|
while (len >= 12)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/*------------------------------------- handle the last 11 bytes */
|
|
c += length;
|
|
switch (len) /* all the case statements fall through */
|
|
{
|
|
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 */
|
|
}
|
|
mix (a, b, c);
|
|
/*-------------------------------------- report the result */
|
|
return c;
|
|
}
|
|
|
|
#else
|
|
static void dummy(void) {}
|
|
#endif /* P2MP_SERVER */
|