51c1639884
The hashing function that tinc uses is currently broken as it only looks at the first 4 bytes of data. This leads to interesting bugs, like the node UDP address cache being subtly broken because two addresses with the same protocol and port (but not the same IP address) will override each other. This is because the first four bytes of sockaddr_in contains the IP protocol and port, while the IP address itself is contained in the four remaining bytes that are never used when the hash is computed.
107 lines
3.1 KiB
C
107 lines
3.1 KiB
C
/*
|
|
hash.c -- hash table management
|
|
Copyright (C) 2012 Guus Sliepen <guus@tinc-vpn.org>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
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; if not, write to the Free Software Foundation, Inc.,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include "system.h"
|
|
|
|
#include "hash.h"
|
|
#include "xalloc.h"
|
|
|
|
/* Generic hash function */
|
|
|
|
static uint32_t hash_function(const void *p, size_t len) {
|
|
const uint8_t *q = p;
|
|
uint32_t hash = 0;
|
|
while(true) {
|
|
for(int i = len > 4 ? 4 : len; --i;)
|
|
hash += q[len - i] << (8 * i);
|
|
hash *= 0x9e370001UL; // Golden ratio prime.
|
|
if(len <= 4)
|
|
break;
|
|
len -= 4;
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
/* Map 32 bits int onto 0..n-1, without throwing away too many bits if n is 2^8 or 2^16 */
|
|
|
|
static uint32_t modulo(uint32_t hash, size_t n) {
|
|
if(n == 0x100)
|
|
return (hash >> 24) ^ ((hash >> 16) & 0xff) ^ ((hash >> 8) & 0xff) ^ (hash & 0xff);
|
|
else if(n == 0x10000)
|
|
return (hash >> 16) ^ (hash & 0xffff);
|
|
else
|
|
return hash % n;
|
|
}
|
|
|
|
/* (De)allocation */
|
|
|
|
hash_t *hash_alloc(size_t n, size_t size) {
|
|
hash_t *hash = xzalloc(sizeof *hash);
|
|
hash->n = n;
|
|
hash->size = size;
|
|
hash->keys = xzalloc(hash->n * hash->size);
|
|
hash->values = xzalloc(hash->n * sizeof *hash->values);
|
|
return hash;
|
|
}
|
|
|
|
void hash_free(hash_t *hash) {
|
|
free(hash->keys);
|
|
free(hash->values);
|
|
free(hash);
|
|
}
|
|
|
|
/* Searching and inserting */
|
|
|
|
void hash_insert(hash_t *hash, const void *key, const void *value) {
|
|
uint32_t i = modulo(hash_function(key, hash->size), hash->n);
|
|
memcpy(hash->keys + i * hash->size, key, hash->size);
|
|
hash->values[i] = value;
|
|
}
|
|
|
|
void *hash_search(const hash_t *hash, const void *key) {
|
|
uint32_t i = modulo(hash_function(key, hash->size), hash->n);
|
|
if(hash->values[i] && !memcmp(key, hash->keys + i * hash->size, hash->size)) {
|
|
return (void *)hash->values[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void *hash_search_or_insert(hash_t *hash, const void *key, const void *value) {
|
|
uint32_t i = modulo(hash_function(key, hash->size), hash->n);
|
|
if(hash->values[i] && !memcmp(key, hash->keys + i * hash->size, hash->size))
|
|
return (void *)hash->values[i];
|
|
memcpy(hash->keys + i * hash->size, key, hash->size);
|
|
hash->values[i] = value;
|
|
return NULL;
|
|
}
|
|
|
|
/* Utility functions */
|
|
|
|
void hash_clear(hash_t *hash) {
|
|
memset(hash->values, 0, hash->n * sizeof *hash->values);
|
|
}
|
|
|
|
void hash_resize(hash_t *hash, size_t n) {
|
|
hash->keys = xrealloc(hash->keys, n * hash->size);
|
|
hash->values = xrealloc(hash->values, n * sizeof *hash->values);
|
|
if(n > hash->n) {
|
|
memset(hash->keys + hash->n * hash->size, 0, (n - hash->n) * hash->size);
|
|
memset(hash->values + hash->n, 0, (n - hash->n) * sizeof *hash->values);
|
|
}
|
|
}
|