515 lines
14 KiB
C
515 lines
14 KiB
C
/* wrnad_hash.c - Wind River NAD hash table utility */
|
|
|
|
/*
|
|
* Copyright (c) 2010 Wind River Systems, Inc.
|
|
*
|
|
* The right to copy, distribute or otherwise make use of this software
|
|
* may be licensed only pursuant to the terms of an applicable Wind River
|
|
* license agreement.
|
|
*/
|
|
|
|
/* includes */
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <syslog.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "wrnad.h"
|
|
|
|
#ifdef WRNAD_IDENTIFY_ROUTERS
|
|
|
|
/* defines */
|
|
|
|
#define HASH_ITEMS_BATCH 64
|
|
|
|
/*
|
|
* FIXME: The stdlib.h at ipxinc/include/stdlib.h doesn't define
|
|
* EXIT_FAILURE or EXIT_SUCCESS.
|
|
*/
|
|
#ifndef EXIT_FAILURE
|
|
#define EXIT_FAILURE 1
|
|
#endif
|
|
#ifndef EXIT_SUCCESS
|
|
#define EXIT_SUCCESS 0
|
|
#endif
|
|
|
|
/* typedefs */
|
|
|
|
/* imports */
|
|
|
|
extern unsigned int v4_slots;
|
|
extern unsigned int v6_slots;
|
|
|
|
/* globals */
|
|
|
|
static hash_list_t *free_ip4_items = NULL;
|
|
static hash_list_t *free_ip6_items = NULL;
|
|
|
|
/* locals */
|
|
|
|
/*
|
|
* The hashing code used here is adapted from Bob Jenkins' public-domain
|
|
* lookup3 hash code.
|
|
*
|
|
* http://burtleburtle.net/bob/c/lookup3.c
|
|
*/
|
|
/*
|
|
-------------------------------------------------------------------------------
|
|
lookup3.c, by Bob Jenkins, May 2006, Public Domain.
|
|
|
|
These are functions for producing 32-bit hashes for hash table lookup.
|
|
hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
|
|
are externally useful functions. Routines to test the hash are included
|
|
if SELF_TEST is defined. You can use this free for any purpose. It's in
|
|
the public domain. It has no warranty.
|
|
|
|
You probably want to use hashlittle(). hashlittle() and hashbig()
|
|
hash byte arrays. hashlittle() is is faster than hashbig() on
|
|
little-endian machines. Intel and AMD are little-endian machines.
|
|
On second thought, you probably want hashlittle2(), which is identical to
|
|
hashlittle() except it returns two 32-bit hashes for the price of one.
|
|
You could implement hashbig2() if you wanted but I haven't bothered here.
|
|
|
|
If you want to find a hash of, say, exactly 7 integers, do
|
|
a = i1; b = i2; c = i3;
|
|
mix(a,b,c);
|
|
a += i4; b += i5; c += i6;
|
|
mix(a,b,c);
|
|
a += i7;
|
|
final(a,b,c);
|
|
then use c as the hash value. If you have a variable length array of
|
|
4-byte integers to hash, use hashword(). If you have a byte array (like
|
|
a character string), use hashlittle(). If you have several byte arrays, or
|
|
a mix of things, see the comments above hashlittle().
|
|
|
|
Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
|
|
then mix those integers. This is fast (you can do a lot more thorough
|
|
mixing with 12*3 instructions on 3 integers than you can with 3 instructions
|
|
on 1 byte), but shoehorning those bytes into integers efficiently is messy.
|
|
-------------------------------------------------------------------------------
|
|
*/
|
|
|
|
#define hashsize(n) ((uint32_t)1<<(n))
|
|
#define hashmask(n) (hashsize(n)-1)
|
|
#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
|
|
|
|
/*
|
|
-------------------------------------------------------------------------------
|
|
mix -- mix 3 32-bit values reversibly.
|
|
|
|
This is reversible, so any information in (a,b,c) before mix() is
|
|
still in (a,b,c) after mix().
|
|
|
|
If four pairs of (a,b,c) inputs are run through mix(), or through
|
|
mix() in reverse, there are at least 32 bits of the output that
|
|
are sometimes the same for one pair and different for another pair.
|
|
This was tested for:
|
|
* pairs that differed by one bit, by two bits, in any combination
|
|
of top bits of (a,b,c), or in any combination of bottom bits of
|
|
(a,b,c).
|
|
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
|
|
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
|
|
is commonly produced by subtraction) look like a single 1-bit
|
|
difference.
|
|
* the base values were pseudorandom, all zero but one bit set, or
|
|
all zero plus a counter that starts at zero.
|
|
|
|
Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
|
|
satisfy this are
|
|
4 6 8 16 19 4
|
|
9 15 3 18 27 15
|
|
14 9 3 7 17 3
|
|
Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
|
|
for "differ" defined as + with a one-bit base and a two-bit delta. I
|
|
used http://burtleburtle.net/bob/hash/avalanche.html to choose
|
|
the operations, constants, and arrangements of the variables.
|
|
|
|
This does not achieve avalanche. There are input bits of (a,b,c)
|
|
that fail to affect some output bits of (a,b,c), especially of a. The
|
|
most thoroughly mixed value is c, but it doesn't really even achieve
|
|
avalanche in c.
|
|
|
|
This allows some parallelism. Read-after-writes are good at doubling
|
|
the number of bits affected, so the goal of mixing pulls in the opposite
|
|
direction as the goal of parallelism. I did what I could. Rotates
|
|
seem to cost as much as shifts on every machine I could lay my hands
|
|
on, and rotates are much kinder to the top and bottom bits, so I used
|
|
rotates.
|
|
-------------------------------------------------------------------------------
|
|
*/
|
|
#define mix(a,b,c) \
|
|
{ \
|
|
a -= c; a ^= rot(c, 4); c += b; \
|
|
b -= a; b ^= rot(a, 6); a += c; \
|
|
c -= b; c ^= rot(b, 8); b += a; \
|
|
a -= c; a ^= rot(c,16); c += b; \
|
|
b -= a; b ^= rot(a,19); a += c; \
|
|
c -= b; c ^= rot(b, 4); b += a; \
|
|
}
|
|
|
|
/*
|
|
-------------------------------------------------------------------------------
|
|
final -- final mixing of 3 32-bit values (a,b,c) into c
|
|
|
|
Pairs of (a,b,c) values differing in only a few bits will usually
|
|
produce values of c that look totally different. This was tested for
|
|
* pairs that differed by one bit, by two bits, in any combination
|
|
of top bits of (a,b,c), or in any combination of bottom bits of
|
|
(a,b,c).
|
|
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
|
|
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
|
|
is commonly produced by subtraction) look like a single 1-bit
|
|
difference.
|
|
* the base values were pseudorandom, all zero but one bit set, or
|
|
all zero plus a counter that starts at zero.
|
|
|
|
These constants passed:
|
|
14 11 25 16 4 14 24
|
|
12 14 25 16 4 14 24
|
|
and these came close:
|
|
4 8 15 26 3 22 24
|
|
10 8 15 26 3 22 24
|
|
11 8 15 26 3 22 24
|
|
-------------------------------------------------------------------------------
|
|
*/
|
|
#define final(a,b,c) \
|
|
{ \
|
|
c ^= b; c -= rot(b,14); \
|
|
a ^= c; a -= rot(c,11); \
|
|
b ^= a; b -= rot(a,25); \
|
|
c ^= b; c -= rot(b,16); \
|
|
a ^= c; a -= rot(c,4); \
|
|
b ^= a; b -= rot(a,14); \
|
|
c ^= b; c -= rot(b,24); \
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipcom_lookup3_hashword
|
|
*===========================================================================
|
|
* Context:
|
|
* Description: Hash a key that is an array of 32-bit words
|
|
* Based upon lookup3's hashword() function.
|
|
* Parameters: k : pointer to the hash key, an array of 32-bit words
|
|
* length : the number of 32-bit words in the key
|
|
* initval : an arbitrary value to initialize the hash.
|
|
*
|
|
* Returns: N/A
|
|
*
|
|
*/
|
|
/* DATA */
|
|
|
|
static __inline__ uint32_t
|
|
ipcom_lookup3_hashword(const uint32_t *k, size_t length, uint32_t initval)
|
|
{
|
|
uint32_t a,b,c;
|
|
|
|
/* Set up the internal state */
|
|
a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval;
|
|
|
|
/*--------------------------------------------- handle most of the key */
|
|
while (length > 3)
|
|
{
|
|
a += k[0];
|
|
b += k[1];
|
|
c += k[2];
|
|
mix(a,b,c);
|
|
length -= 3;
|
|
k += 3;
|
|
}
|
|
|
|
/*--------------------------------------- handle the last 3 uint32_t's */
|
|
switch(length) /* all the case statements fall through */
|
|
{
|
|
case 3 : c+=k[2];
|
|
case 2 : b+=k[1];
|
|
case 1 : a+=k[0];
|
|
final(a,b,c);
|
|
case 0: /* case 0: nothing left to add */
|
|
break;
|
|
}
|
|
/*-------------------------------------------------- report the result */
|
|
return c;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* hash_table_init - initialize wrnad monitoring
|
|
*
|
|
* This routine initializes the hash table <table>.
|
|
* The number of slots in the hash table is <n_slots>, which must be a power
|
|
* of two. Each slot heads a linked list of entries, so the table can grow
|
|
* to arbitrary size (although not too efficiently if the lists get long!).
|
|
* The provided <hasher> function is used to calculate the hash index. Its
|
|
* argument is the start of the hash data, while its result is masked with
|
|
* (<n_slots> - 1) to get the hash index.
|
|
*
|
|
* RETURNS: 0 on success, negative error code on failure.
|
|
*
|
|
* ERRNO: N/A
|
|
*/
|
|
|
|
int
|
|
hash_table_init(hash_table_t *table, unsigned int n_slots, hash_func hasher)
|
|
{
|
|
unsigned int ix;
|
|
hash_list_t *item;
|
|
|
|
if (table == NULL || hasher == NULL || n_slots == 0 ||
|
|
(n_slots & (n_slots - 1)) != 0)
|
|
return -EINVAL;
|
|
|
|
table->n_slots = n_slots;
|
|
table->mask = n_slots - 1;
|
|
table->hasher = hasher;
|
|
|
|
table->table = malloc(n_slots * sizeof(hash_list_t));
|
|
if (table->table == NULL)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
item = table->table;
|
|
for (ix = 0; ix < n_slots; ++ix)
|
|
{
|
|
hl_init_list(item);
|
|
++item;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* We could add a compare function to the hash table, but would prefer
|
|
* to be just a bit quicker.
|
|
*/
|
|
|
|
ip4_item_t *
|
|
hash_lookup_ip4addr(hash_table_t *table, uint32_t *paddr, unsigned int *pix)
|
|
{
|
|
unsigned int ix = table->hasher(paddr) & table->mask;
|
|
hash_list_t *list = &table->table[ix];
|
|
hash_list_t *item;
|
|
ip4_item_t *ip4;
|
|
uint32_t addr = *paddr;
|
|
|
|
if (pix)
|
|
*pix = ix;
|
|
|
|
for (item = list->next; item != list; item = item->next)
|
|
{
|
|
ip4 = (ip4_item_t *)item;
|
|
if (ip4->addr == addr)
|
|
return ip4;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
ip6_item_t *
|
|
hash_lookup_ip6addr(hash_table_t *table, uint32_t *paddr, unsigned int *pix)
|
|
{
|
|
unsigned int ix = table->hasher(paddr) & table->mask;
|
|
hash_list_t *list = &table->table[ix];
|
|
hash_list_t *item;
|
|
ip6_item_t *ip6;
|
|
|
|
if (pix)
|
|
*pix = ix;
|
|
|
|
for (item = list->next; item != list; item = item->next)
|
|
{
|
|
ip6 = (ip6_item_t *)item;
|
|
if (ip6->addr[0] == paddr[0] &&
|
|
ip6->addr[1] == paddr[1] &&
|
|
ip6->addr[2] == paddr[2] &&
|
|
ip6->addr[3] == paddr[3])
|
|
return ip6;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void *
|
|
hash_item_alloc(hash_list_t **phead, size_t item_size)
|
|
{
|
|
hash_list_t *item;
|
|
hash_list_t *item0;
|
|
unsigned int ix;
|
|
|
|
if ((item = *phead) != NULL)
|
|
{
|
|
*phead = item->next;
|
|
item->next = NULL; /* safety/paranoia */
|
|
return item;
|
|
}
|
|
item = malloc (item_size * HASH_ITEMS_BATCH);
|
|
if (item == NULL)
|
|
{
|
|
wrnad_log(LOG_ERR, "hash_item_alloc() failed! Exiting.\n");
|
|
wrnad_terminate(EXIT_FAILURE);
|
|
}
|
|
item0 = item;
|
|
for (ix = 0; ix < HASH_ITEMS_BATCH - 1; ++ix)
|
|
{
|
|
item->next = (hash_list_t *)((char *)item + item_size);
|
|
item = item->next;
|
|
}
|
|
item->next = NULL;
|
|
|
|
item = item0;
|
|
*phead = item->next;
|
|
item->next = NULL;
|
|
return item;
|
|
}
|
|
|
|
void
|
|
hash_insert_ip4addr(hash_table_t *table, uint32_t *paddr)
|
|
{
|
|
ip4_item_t *ip4;
|
|
hash_list_t *list;
|
|
unsigned int ix;
|
|
|
|
if ((ip4 = hash_lookup_ip4addr(table, paddr, &ix)) != NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
list = &table->table[ix];
|
|
|
|
/* Terminates on failure */
|
|
ip4 = hash_item_alloc(&free_ip4_items, sizeof(*ip4));
|
|
ip4->addr = *paddr;
|
|
|
|
hl_insert_before(list, &ip4->list);
|
|
}
|
|
|
|
void
|
|
hash_insert_ip6addr(hash_table_t *table, uint32_t *paddr)
|
|
{
|
|
ip6_item_t *ip6;
|
|
hash_list_t *list;
|
|
unsigned int ix;
|
|
|
|
if ((ip6 = hash_lookup_ip6addr(table, paddr, &ix)) != NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
list = &table->table[ix];
|
|
|
|
/* Terminates on failure */
|
|
ip6 = hash_item_alloc(&free_ip6_items, sizeof(*ip6));
|
|
|
|
ip6->addr[0] = paddr[0];
|
|
ip6->addr[1] = paddr[1];
|
|
ip6->addr[2] = paddr[2];
|
|
ip6->addr[3] = paddr[3];
|
|
|
|
hl_insert_before(list, &ip6->list);
|
|
}
|
|
|
|
void
|
|
hash_remove_ip4addr(hash_table_t *table, uint32_t *paddr)
|
|
{
|
|
ip4_item_t *ip4;
|
|
|
|
if ((ip4 = hash_lookup_ip4addr(table, paddr, NULL)) == NULL)
|
|
return;
|
|
hl_delete_item(&ip4->list);
|
|
ip4->list.next = free_ip4_items;
|
|
free_ip4_items = &ip4->list;
|
|
}
|
|
|
|
void
|
|
hash_remove_ip6addr(hash_table_t *table, uint32_t *paddr)
|
|
{
|
|
ip6_item_t *ip6;
|
|
|
|
if ((ip6 = hash_lookup_ip6addr(table, paddr, NULL)) == NULL)
|
|
return;
|
|
hl_delete_item(&ip6->list);
|
|
ip6->list.next = free_ip6_items;
|
|
free_ip6_items = &ip6->list;
|
|
}
|
|
|
|
unsigned int
|
|
v4_hasher(uint32_t *paddr)
|
|
{
|
|
return ipcom_lookup3_hashword(paddr, 1, 0xabadaba2);
|
|
}
|
|
|
|
unsigned int
|
|
v6_hasher(uint32_t *paddr)
|
|
{
|
|
return ipcom_lookup3_hashword(paddr, 4, 0x6197324d);
|
|
}
|
|
|
|
|
|
int
|
|
hash_v4_init(hash_table_t *table)
|
|
{
|
|
return hash_table_init(table, v4_slots, v4_hasher);
|
|
}
|
|
|
|
int
|
|
hash_v6_init(hash_table_t *table)
|
|
{
|
|
return hash_table_init(table, v6_slots, v6_hasher);
|
|
}
|
|
|
|
#if 0
|
|
void
|
|
hash_show(hash_table_t *table, int family)
|
|
{
|
|
unsigned int ix;
|
|
hash_list_t *list;
|
|
hash_list_t *item;
|
|
char buf [64];
|
|
char *name;
|
|
|
|
switch (family)
|
|
{
|
|
case AF_INET:
|
|
name = "IPv4";
|
|
break;
|
|
case AF_INET6:
|
|
name = "IPv6";
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
printf("%s table (%p) mask %u\n", name, table, table->mask);
|
|
for (ix = 0; ix <= table->mask; ++ix)
|
|
{
|
|
list = &table->table[ix];
|
|
if (list->next == list)
|
|
continue;
|
|
printf("%5u: ", ix);
|
|
for (item = list->next; item != list; item = item->next)
|
|
{
|
|
inet_ntop(family, (item + 1), buf, sizeof(buf));
|
|
printf ("%s ", buf);
|
|
}
|
|
printf("\n");
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
void
|
|
port_hashes_show(Wrnad_proxy_interface *proxy)
|
|
{
|
|
printf("%s routers\n", proxy->ifname);
|
|
hash_show(&proxy->v4_routers, AF_INET);
|
|
hash_show(&proxy->v6_routers, AF_INET6);
|
|
}
|
|
#endif
|
|
|
|
#else /* WRNAD_IDENTIFY_ROUTERS */
|
|
|
|
int wrnad_hash_empty_file;
|
|
|
|
#endif /* WRNAD_IDENTIFY_ROUTERS */
|