# include <stdio.h>
# include <stdint.h>
# include <execinfo.h>
# include <stdbool.h>
# include <stdlib.h>
# include <string.h>
# include <stdarg.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <unistd.h>
# include <time.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# define MIN_SIZE 8
# define MAX_SIZE ( 1 << 30 )
# define CAPACITY ( size) ( size / 3 * 2 )
# define RESIZE_FACTOR 3
# define INIT ( this, . . . ) ( { ( this) = malloc ( sizeof ( * ( this) ) ) ; \
* ( this) = ( typeof ( * ( this) ) ) { __VA_ARGS__ } ; ( this) ; } )
# define TRUE true
# define FALSE false
# define METHOD ( iface, name, ret, this, . . . ) \
static ret name ( union { iface * _public; this; } \
__attribute__ ( ( transparent_union) ) , ## __VA_ARGS__) ; \
static typeof ( name) * _## name = ( typeof ( name) * ) name; \
static ret name ( this, ## __VA_ARGS__)
# define max ( x, y) ( { \
typeof ( x) _x = ( x) ; \
typeof ( y) _y = ( y) ; \
_x > _y ? _x : _y; } )
# define VA_ARGS_NUM ( . . . ) _VA_ARGS_NUM ( 0 , ## __VA_ARGS__, 10 , 9 , 8 , 7 , 6 , 5 , 4 , 3 , 2 , 1 , 0 )
# define _VA_ARGS_NUM ( _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, NUM, . . . ) NUM
# define VA_ARGS_DISPATCH ( func, . . . ) _VA_ARGS_DISPATCH ( func, VA_ARGS_NUM ( __VA_ARGS__) )
# define _VA_ARGS_DISPATCH ( func, num) __VA_ARGS_DISPATCH ( func, num)
# define __VA_ARGS_DISPATCH ( func, num) func ## num
# define VA_ARGS_GET ( last, . . . ) ( { \
va_list _va_args_get_ap; \
va_start ( _va_args_get_ap, last) ; \
_VA_ARGS_GET_ASGN ( __VA_ARGS__) \
va_end ( _va_args_get_ap) ; \
} )
# define VA_ARGS_VGET ( list, . . . ) ( { \
va_list _va_args_get_ap; \
va_copy ( _va_args_get_ap, list) ; \
_VA_ARGS_GET_ASGN ( __VA_ARGS__) \
va_end ( _va_args_get_ap) ; \
} )
# define _VA_ARGS_GET_ASGN ( . . . ) VA_ARGS_DISPATCH ( _VA_ARGS_GET_ASGN, __VA_ARGS__) ( __VA_ARGS__)
# define _VA_ARGS_GET_ASGN1 ( v1) __VA_ARGS_GET_ASGN ( v1)
# define _VA_ARGS_GET_ASGN2 ( v1, v2) __VA_ARGS_GET_ASGN ( v1) __VA_ARGS_GET_ASGN ( v2)
# define _VA_ARGS_GET_ASGN3 ( v1, v2, v3) __VA_ARGS_GET_ASGN ( v1) __VA_ARGS_GET_ASGN ( v2) \
__VA_ARGS_GET_ASGN ( v3)
# define _VA_ARGS_GET_ASGN4 ( v1, v2, v3, v4) __VA_ARGS_GET_ASGN ( v1) __VA_ARGS_GET_ASGN ( v2) \
__VA_ARGS_GET_ASGN ( v3) __VA_ARGS_GET_ASGN ( v4)
# define _VA_ARGS_GET_ASGN5 ( v1, v2, v3, v4, v5) __VA_ARGS_GET_ASGN ( v1) __VA_ARGS_GET_ASGN ( v2) \
__VA_ARGS_GET_ASGN ( v3) __VA_ARGS_GET_ASGN ( v4) __VA_ARGS_GET_ASGN ( v5)
# define __VA_ARGS_GET_ASGN ( v) v = va_arg ( _va_args_get_ap, typeof ( v) ) ;
typedef struct hashtable_t hashtable_t ;
typedef struct hashlist_t hashlist_t ;
typedef struct enumerator_t enumerator_t ;
struct enumerator_t {
bool ( * enumerate) ( enumerator_t * this, . . . ) ;
bool ( * venumerate) ( enumerator_t * this, va_list args) ;
void ( * destroy) ( enumerator_t * this) ;
} ;
bool enumerator_enumerate_default ( enumerator_t * enumerator, . . . )
{
va_list args;
bool result;
if ( ! enumerator-> venumerate)
{
return FALSE;
}
va_start ( args, enumerator) ;
result = enumerator-> venumerate ( enumerator, args) ;
va_end ( args) ;
return result;
}
METHOD ( enumerator_t , enumerate_empty, bool,
enumerator_t * enumerator, va_list args)
{
return FALSE;
}
typedef struct backtrace_t backtrace_t ;
struct backtrace_t {
void ( * log) ( backtrace_t * this, FILE * file, bool detailed) ;
bool ( * contains_function) ( backtrace_t * this, char * function[ ] , int count) ;
bool ( * equals) ( backtrace_t * this, backtrace_t * other) ;
backtrace_t * ( * clone) ( backtrace_t * this) ;
enumerator_t * ( * create_frame_enumerator) ( backtrace_t * this) ;
void ( * destroy) ( backtrace_t * this) ;
} ;
typedef struct private_backtrace_t private_backtrace_t ;
struct private_backtrace_t {
backtrace_t public;
int frame_count;
void * frames[ ] ;
} ;
# define BUILD_ASSERT ( x) ( sizeof ( char [ ( x) ? 0 : - 1 ] ) )
# define BUILD_ASSERT_ARRAY ( a) \
BUILD_ASSERT ( ! __builtin_types_compatible_p ( typeof ( a) , typeof ( & ( a) [ 0 ] ) ) )
# define countof ( array) ( sizeof ( array) / sizeof ( ( array) [ 0 ] ) \
+ BUILD_ASSERT_ARRAY ( array) )
static backtrace_t get_methods ( ) ;
static void println ( FILE * file, char * format, . . . )
{
char buf[ 512 ] ;
va_list args;
va_start ( args, format) ;
if ( file)
{
vfprintf ( file, format, args) ;
fputs ( "\n" , file) ;
}
else
{
vsnprintf ( buf, sizeof ( buf) , format, args) ;
}
va_end ( args) ;
}
METHOD ( backtrace_t , log_, void ,
private_backtrace_t * this, FILE * file, bool detailed)
{
size_t i;
char * * strings = NULL ;
println ( file, " dumping %d stack frame addresses:" , this-> frame_count) ;
for ( i = 0 ; i < this-> frame_count; i++ )
{
if ( ! strings)
{
strings = backtrace_symbols ( this-> frames, this-> frame_count) ;
}
if ( strings)
{
println ( file, " %s" , strings[ i] ) ;
}
else
{
println ( file, " %p" , this-> frames[ i] ) ;
}
}
free ( strings) ;
}
METHOD ( backtrace_t , contains_function, bool,
private_backtrace_t * this, char * function[ ] , int count)
{
return FALSE;
}
METHOD ( backtrace_t , equals, bool,
private_backtrace_t * this, backtrace_t * other_public)
{
private_backtrace_t * other = ( private_backtrace_t * ) other_public;
int i;
if ( this == other)
{
return TRUE;
}
if ( this-> frame_count != other-> frame_count)
{
return FALSE;
}
for ( i = 0 ; i < this-> frame_count; i++ )
{
if ( this-> frames[ i] != other-> frames[ i] )
{
return FALSE;
}
}
return TRUE;
}
typedef struct {
enumerator_t public;
private_backtrace_t * bt;
int i;
} frame_enumerator_t ;
METHOD ( enumerator_t , frame_enumerate, bool,
frame_enumerator_t * this, va_list args)
{
void * * addr;
VA_ARGS_VGET ( args, addr) ;
if ( this-> i < this-> bt-> frame_count)
{
* addr = this-> bt-> frames[ this-> i++ ] ;
return TRUE;
}
return FALSE;
}
METHOD ( backtrace_t , create_frame_enumerator, enumerator_t * ,
private_backtrace_t * this)
{
frame_enumerator_t * enumerator;
INIT ( enumerator,
. public = {
. enumerate = enumerator_enumerate_default,
. venumerate = _frame_enumerate,
. destroy = ( void * ) free,
} ,
. bt = this,
) ;
return & enumerator-> public;
}
METHOD ( backtrace_t , destroy, void ,
private_backtrace_t * this)
{
free ( this) ;
}
METHOD ( backtrace_t , clone_, backtrace_t * ,
private_backtrace_t * this)
{
private_backtrace_t * clone;
clone = malloc ( sizeof ( private_backtrace_t ) +
this-> frame_count * sizeof ( void * ) ) ;
memcpy ( clone-> frames, this-> frames, this-> frame_count * sizeof ( void * ) ) ;
clone-> frame_count = this-> frame_count;
clone-> public = get_methods ( ) ;
return & clone-> public;
}
static backtrace_t get_methods ( )
{
return ( backtrace_t ) {
. log = _log_,
. contains_function = _contains_function,
. equals = _equals,
. clone = _clone_,
. create_frame_enumerator = _create_frame_enumerator,
. destroy = _destroy,
} ;
}
backtrace_t * backtrace_create ( int skip)
{
private_backtrace_t * this;
void * frames[ 50 ] ;
int frame_count = 0 ;
frame_count = backtrace ( frames, countof ( frames) ) ;
frame_count = max ( frame_count - skip, 0 ) ;
this = malloc ( sizeof ( private_backtrace_t ) + frame_count * sizeof ( void * ) ) ;
memcpy ( this-> frames, frames + skip, frame_count * sizeof ( void * ) ) ;
this-> frame_count = frame_count;
this-> public = get_methods ( ) ;
return & this-> public;
}
void backtrace_dump ( char * label, FILE * file, bool detailed)
{
backtrace_t * backtrace;
backtrace = backtrace_create ( 2 ) ;
if ( label)
{
println ( file, "Debug backtrace: %s" , label) ;
}
backtrace-> log ( backtrace, file, detailed) ;
backtrace-> destroy ( backtrace) ;
}
typedef struct hashtable_profile_t hashtable_profile_t ;
struct hashtable_profile_t {
struct {
size_t count;
size_t probes;
size_t longest;
} success, failure;
struct {
size_t count;
size_t size;
} max;
backtrace_t * backtrace;
} ;
static inline void profiler_cleanup ( hashtable_profile_t * profile, u_int count,
u_int size)
{
if ( profile-> success. count || profile-> failure. count)
{
fprintf ( stderr , "%zu elements [max. %zu], %zu buckets [%zu], %zu "
"successful / %zu failed lookups, %.4f [%zu] / %.4f "
"[%zu] avg. probes in table created at:" ,
count, profile-> max. count, size, profile-> max. size,
profile-> success. count, profile-> failure. count,
( double ) profile-> success. probes/ profile-> success. count,
profile-> success. longest,
( double ) profile-> failure. probes/ profile-> failure. count,
profile-> failure. longest) ;
profile-> backtrace-> log ( profile-> backtrace, stderr , TRUE) ;
}
profile-> backtrace-> destroy ( profile-> backtrace) ;
}
static inline void profiler_init ( hashtable_profile_t * profile, int skip)
{
profile-> backtrace = backtrace_create ( skip) ;
}
# define lookup_start ( ) \
u_int _lookup_probes = 0 ;
# define lookup_probing ( ) \
_lookup_probes++ ;
# define _lookup_done ( profile, result) \
( profile) -> result. count++ ; \
( profile) -> result. probes += _lookup_probes; \
( profile) -> result. longest = max ( ( profile) -> result. longest, _lookup_probes) ;
# define lookup_success ( profile) _lookup_done ( profile, success) ;
# define lookup_failure ( profile) _lookup_done ( profile, failure) ;
static inline void profile_size ( hashtable_profile_t * profile, u_int size)
{
profile-> max. size = max ( profile-> max. size, size) ;
}
static inline void profile_count ( hashtable_profile_t * profile, u_int count)
{
profile-> max. count = max ( profile-> max. count, count) ;
}
struct hashtable_t {
enumerator_t * ( * create_enumerator) ( hashtable_t * this) ;
void * ( * put) ( hashtable_t * this, const void * key, void * value) ;
void * ( * get) ( hashtable_t * this, const void * key) ;
void * ( * remove) ( hashtable_t * this, const void * key) ;
void ( * remove_at) ( hashtable_t * this, enumerator_t * enumerator) ;
uint32_t ( * get_count) ( hashtable_t * this) ;
void ( * destroy) ( hashtable_t * this) ;
void ( * destroy_function) ( hashtable_t * this,
void ( * ) ( void * val, const void * key) ) ;
} ;
typedef struct pair_t pair_t ;
struct pair_t {
const void * key;
void * value;
uint32_t hash;
} ;
typedef uint32_t ( * hashtable_hash_t ) ( const void * key) ;
typedef bool ( * hashtable_equals_t ) ( const void * key, const void * other_key) ;
typedef struct private_hashtable_t private_hashtable_t ;
struct private_hashtable_t {
hashtable_t public;
uint32_t count;
uint32_t size;
uint32_t mask;
pair_t * items;
uint32_t capacity;
uint32_t items_count;
void * table;
hashtable_hash_t hash;
hashtable_equals_t equals;
hashtable_profile_t profile;
} ;
# define min ( x, y) ( { \
typeof ( x) _x = ( x) ; \
typeof ( y) _y = ( y) ; \
_x < _y ? _x : _y; } )
u_int hashtable_get_nearest_powerof2 ( u_int n)
{
u_int i;
-- n;
for ( i = 1 ; i < sizeof ( u_int) * 8 ; i <<= 1 )
{
n |= n >> i;
}
return ++ n;
}
static void init_hashtable ( private_hashtable_t * this, u_int size)
{
u_int index_size = sizeof ( u_int) ;
this-> size = max ( MIN_SIZE, min ( size, MAX_SIZE) ) ;
this-> size = hashtable_get_nearest_powerof2 ( this-> size) ;
this-> mask = this-> size - 1 ;
profile_size ( & this-> profile, this-> size) ;
this-> capacity = CAPACITY ( this-> size) ;
this-> items = calloc ( this-> capacity, sizeof ( pair_t ) ) ;
this-> items_count = 0 ;
if ( this-> capacity <= 0xff )
{
index_size = sizeof ( uint8_t ) ;
}
else if ( this-> capacity <= 0xffff )
{
index_size = sizeof ( uint16_t ) ;
}
this-> table = calloc ( this-> size, index_size) ;
}
static inline u_int get_next ( private_hashtable_t * this, u_int row, u_int * p)
{
* p += 1 ;
return ( row + * p) & this-> mask;
}
static inline u_int get_index ( private_hashtable_t * this, u_int row)
{
if ( this-> capacity <= 0xff )
{
return ( ( uint8_t * ) this-> table) [ row] ;
}
else if ( this-> capacity <= 0xffff )
{
return ( ( uint16_t * ) this-> table) [ row] ;
}
return ( ( u_int* ) this-> table) [ row] ;
}
static inline void set_index ( private_hashtable_t * this, u_int row, u_int index)
{
if ( this-> capacity <= 0xff )
{
( ( uint8_t * ) this-> table) [ row] = index;
}
else if ( this-> capacity <= 0xffff )
{
( ( uint16_t * ) this-> table) [ row] = index;
}
else
{
( ( u_int* ) this-> table) [ row] = index;
}
}
static inline u_int insert_item ( private_hashtable_t * this, u_int row)
{
u_int index = this-> items_count++ ;
set_index ( this, row, index + 1 ) ;
return index;
}
static bool rehash ( private_hashtable_t * this, u_int size)
{
pair_t * old_items, * pair;
u_int old_count, i, p, row, index;
if ( size > MAX_SIZE)
{
return FALSE;
}
old_items = this-> items;
old_count = this-> items_count;
free ( this-> table) ;
init_hashtable ( this, size) ;
if ( this-> count)
{
for ( i = 0 ; i < old_count; i++ )
{
pair = & old_items[ i] ;
if ( pair-> key)
{
row = pair-> hash & this-> mask;
index = get_index ( this, row) ;
for ( p = 0 ; index; )
{
row = get_next ( this, row, & p) ;
index = get_index ( this, row) ;
}
index = insert_item ( this, row) ;
this-> items[ index] = * pair;
}
}
}
free ( old_items) ;
return TRUE;
}
static inline pair_t * find_key ( private_hashtable_t * this, const void * key,
u_int * out_hash, u_int * out_row)
{
pair_t * pair;
u_int hash, row, p = 0 , removed = 0 , index;
bool found_removed = FALSE;
if ( ! this-> count && ! out_hash && ! out_row)
{
return NULL ;
}
lookup_start ( ) ;
hash = this-> hash ( key) ;
row = hash & this-> mask;
index = get_index ( this, row) ;
while ( index)
{
lookup_probing ( ) ;
pair = & this-> items[ index- 1 ] ;
if ( ! pair-> key)
{
if ( ! found_removed && out_row)
{
removed = row;
found_removed = TRUE;
}
}
else if ( pair-> hash == hash && this-> equals ( key, pair-> key) )
{
lookup_success ( & this-> profile) ;
return pair;
}
row = get_next ( this, row, & p) ;
index = get_index ( this, row) ;
}
if ( out_hash)
{
* out_hash = hash;
}
if ( out_row)
{
* out_row = found_removed ? removed : row;
}
lookup_failure ( & this-> profile) ;
return NULL ;
}
METHOD ( hashtable_t , put, void * ,
private_hashtable_t * this, const void * key, void * value)
{
void * old_value = NULL ;
pair_t * pair;
u_int index, hash = 0 , row = 0 ;
if ( this-> items_count >= this-> capacity &&
! rehash ( this, this-> count * RESIZE_FACTOR) )
{
return NULL ;
}
pair = find_key ( this, key, & hash, & row) ;
if ( pair)
{
old_value = pair-> value;
pair-> value = value;
pair-> key = key;
return old_value;
}
index = insert_item ( this, row) ;
this-> items[ index] = ( pair_t ) {
. hash = hash,
. key = key,
. value = value,
} ;
this-> count++ ;
profile_count ( & this-> profile, this-> count) ;
return value;
}
METHOD ( hashtable_t , get, void * ,
private_hashtable_t * this, const void * key)
{
pair_t * pair = find_key ( this, key, NULL , NULL ) ;
return pair ? pair-> value : NULL ;
}
static void * remove_internal ( private_hashtable_t * this, pair_t * pair)
{
void * value = NULL ;
if ( pair)
{
value = pair-> value;
pair-> key = NULL ;
this-> count-- ;
}
return value;
}
METHOD ( hashtable_t , remove_, void * ,
private_hashtable_t * this, const void * key)
{
pair_t * pair = find_key ( this, key, NULL , NULL ) ;
return remove_internal ( this, pair) ;
}
typedef struct private_enumerator_t private_enumerator_t ;
struct private_enumerator_t {
enumerator_t enumerator;
private_hashtable_t * table;
u_int index;
} ;
METHOD ( enumerator_t , enumerate, bool,
private_enumerator_t * this, va_list args)
{
const void * * key;
void * * value;
pair_t * pair;
VA_ARGS_VGET ( args, key, value) ;
while ( this-> index < this-> table-> items_count)
{
pair = & this-> table-> items[ this-> index++ ] ;
if ( pair-> key)
{
if ( key)
{
* key = pair-> key;
}
if ( value)
{
* value = pair-> value;
}
return TRUE;
}
}
return FALSE;
}
METHOD ( hashtable_t , create_enumerator, enumerator_t * ,
private_hashtable_t * this)
{
private_enumerator_t * enumerator;
INIT ( enumerator,
. enumerator = {
. enumerate = enumerator_enumerate_default,
. venumerate = _enumerate,
. destroy = ( void * ) free,
} ,
. table = this,
) ;
return & enumerator-> enumerator;
}
METHOD ( hashtable_t , remove_at, void ,
private_hashtable_t * this, private_enumerator_t * enumerator)
{
if ( enumerator-> table == this && enumerator-> index)
{
printf ( "remove index: %d\n" , enumerator-> index) ;
u_int index = enumerator-> index - 1 ;
remove_internal ( this, & this-> items[ index] ) ;
}
}
METHOD ( hashtable_t , get_count, u_int,
private_hashtable_t * this)
{
return this-> count;
}
static void destroy_internal ( private_hashtable_t * this,
void ( * fn) ( void * , const void * ) )
{
pair_t * pair;
u_int i;
profiler_cleanup ( & this-> profile, this-> count, this-> size) ;
if ( fn)
{
for ( i = 0 ; i < this-> items_count; i++ )
{
pair = & this-> items[ i] ;
if ( pair-> key)
{
fn ( pair-> value, pair-> key) ;
}
}
}
free ( this-> items) ;
free ( this-> table) ;
free ( this) ;
}
METHOD ( hashtable_t , destroy_, void ,
private_hashtable_t * this)
{
destroy_internal ( this, NULL ) ;
}
METHOD ( hashtable_t , destroy_function, void ,
private_hashtable_t * this, void ( * fn) ( void * , const void * ) )
{
destroy_internal ( this, fn) ;
}
hashtable_t * hashtable_create ( hashtable_hash_t hash, hashtable_equals_t equals,
u_int size)
{
private_hashtable_t * this;
INIT ( this,
. public = {
. put = _put,
. get = _get,
. remove = _remove_,
. remove_at = ( void * ) _remove_at,
. get_count = _get_count,
. create_enumerator = _create_enumerator,
. destroy = _destroy_,
. destroy_function = _destroy_function,
} ,
. hash = hash,
. equals = equals,
) ;
init_hashtable ( this, size) ;
profiler_init ( & this-> profile, 2 ) ;
return & this-> public;
}
static u_char hash_key[ 16 ] = { } ;
void chunk_hash_seed ( )
{
static bool seeded = FALSE;
ssize_t len;
size_t done = 0 ;
int fd;
if ( seeded)
{
return ;
}
fd = open ( "/dev/urandom" , O_RDONLY) ;
if ( fd >= 0 )
{
while ( done < sizeof ( hash_key) )
{
len = read ( fd, hash_key + done, sizeof ( hash_key) - done) ;
if ( len < 0 )
{
break ;
}
done += len;
}
close ( fd) ;
}
if ( done < sizeof ( hash_key) )
{
srandom ( time ( NULL ) + getpid ( ) ) ;
for ( ; done < sizeof ( hash_key) ; done++ )
{
hash_key[ done] = ( u_char) random ( ) ;
}
}
seeded = TRUE;
}
typedef struct chunk_t chunk_t ;
struct chunk_t {
u_char * ptr;
size_t len;
} ;
static inline uint64_t sipget ( u_char * in)
{
uint64_t v = 0 ;
int i;
for ( i = 0 ; i < 64 ; i += 8 , ++ in)
{
v |= ( ( uint64_t ) * in) << i;
}
return v;
}
static inline uint64_t siprotate ( uint64_t v, int shift)
{
return ( v << shift) | ( v >> ( 64 - shift) ) ;
}
static inline void sipround ( uint64_t * v0, uint64_t * v1, uint64_t * v2,
uint64_t * v3)
{
* v0 += * v1;
* v1 = siprotate ( * v1, 13 ) ;
* v1 ^= * v0;
* v0 = siprotate ( * v0, 32 ) ;
* v2 += * v3;
* v3 = siprotate ( * v3, 16 ) ;
* v3 ^= * v2;
* v2 += * v1;
* v1 = siprotate ( * v1, 17 ) ;
* v1 ^= * v2;
* v2 = siprotate ( * v2, 32 ) ;
* v0 += * v3;
* v3 = siprotate ( * v3, 21 ) ;
* v3 ^= * v0;
}
static inline void sipcompress ( uint64_t * v0, uint64_t * v1, uint64_t * v2,
uint64_t * v3, uint64_t m)
{
* v3 ^= m;
sipround ( v0, v1, v2, v3) ;
sipround ( v0, v1, v2, v3) ;
* v0 ^= m;
}
static inline uint64_t siplast ( size_t len, u_char * pos)
{
uint64_t b;
int rem = len & 7 ;
b = ( ( uint64_t ) len) << 56 ;
switch ( rem)
{
case 7 :
b |= ( ( uint64_t ) pos[ 6 ] ) << 48 ;
case 6 :
b |= ( ( uint64_t ) pos[ 5 ] ) << 40 ;
case 5 :
b |= ( ( uint64_t ) pos[ 4 ] ) << 32 ;
case 4 :
b |= ( ( uint64_t ) pos[ 3 ] ) << 24 ;
case 3 :
b |= ( ( uint64_t ) pos[ 2 ] ) << 16 ;
case 2 :
b |= ( ( uint64_t ) pos[ 1 ] ) << 8 ;
case 1 :
b |= ( ( uint64_t ) pos[ 0 ] ) ;
break ;
case 0 :
break ;
}
return b;
}
static uint64_t chunk_mac_inc ( chunk_t chunk, u_char * key, uint64_t m)
{
uint64_t v0, v1, v2, v3, k0, k1;
size_t len = chunk. len;
u_char * pos = chunk. ptr, * end;
end = chunk. ptr + len - ( len % 8 ) ;
k0 = sipget ( key) ;
k1 = sipget ( key + 8 ) ;
v0 = k0 ^ 0x736f6d6570736575ULL ;
v1 = k1 ^ 0x646f72616e646f6dULL ;
v2 = k0 ^ 0x6c7967656e657261ULL ;
v3 = k1 ^ 0x7465646279746573ULL ;
if ( m)
{
sipcompress ( & v0, & v1, & v2, & v3, m) ;
}
for ( ; pos != end; pos += 8 )
{
m = sipget ( pos) ;
sipcompress ( & v0, & v1, & v2, & v3, m) ;
}
sipcompress ( & v0, & v1, & v2, & v3, siplast ( len, pos) ) ;
v2 ^= 0xff ;
sipround ( & v0, & v1, & v2, & v3) ;
sipround ( & v0, & v1, & v2, & v3) ;
sipround ( & v0, & v1, & v2, & v3) ;
sipround ( & v0, & v1, & v2, & v3) ;
return v0 ^ v1 ^ v2 ^ v3;
}
uint32_t chunk_hash_inc ( chunk_t chunk, uint32_t hash)
{
return chunk_mac_inc ( chunk, hash_key, ( ( uint64_t ) hash) << 32 | hash) ;
}
static inline chunk_t chunk_create ( u_char * ptr, size_t len)
{
chunk_t chunk = { ptr, len} ;
return chunk;
}
uint64_t chunk_mac ( chunk_t chunk, u_char * key)
{
return chunk_mac_inc ( chunk, key, 0 ) ;
}
# define chunk_from_thing ( thing) chunk_create ( ( u_char* ) & ( thing) , sizeof ( thing) )
uint32_t chunk_hash ( chunk_t chunk)
{
return chunk_mac ( chunk, hash_key) ;
}
enum protocol_id_t {
PROTO_NONE = 0 ,
PROTO_IKE = 1 ,
PROTO_AH = 2 ,
PROTO_ESP = 3 ,
PROTO_IPCOMP = 4 ,
} ;
typedef enum protocol_id_t protocol_id_t ;
typedef struct private_host_t private_host_t ;
struct private_host_t {
union {
struct sockaddr address;
struct sockaddr_storage address_max;
struct sockaddr_in address4;
struct sockaddr_in6 address6;
} ;
socklen_t socklen;
} ;
typedef struct {
uint32_t unique_id;
uint32_t spi_in;
uint32_t spi_out;
private_host_t host_in;
private_host_t host_out;
protocol_id_t proto;
} child_entry_t ;
# define IPV6_LEN 6
# define IPV4_LEN 4
chunk_t chunk_empty = { NULL , 0 } ;
chunk_t get_address ( private_host_t * this) {
chunk_t address = chunk_empty;
switch ( this-> address. sa_family)
{
case AF_INET:
{
address. ptr = ( char * ) & ( this-> address4. sin_addr. s_addr) ;
address. len = IPV4_LEN;
return address;
}
case AF_INET6:
{
address. ptr = ( char * ) & ( this-> address6. sin6_addr. s6_addr) ;
address. len = IPV6_LEN;
return address;
}
default :
{
return address;
}
}
}
static u_int hash_in ( child_entry_t * entry)
{
return chunk_hash_inc ( chunk_from_thing ( entry-> spi_in) ,
chunk_hash_inc ( get_address ( & entry-> host_in) ,
chunk_hash ( chunk_from_thing ( entry-> proto) ) ) ) ;
}
static inline bool memeq ( const void * x, const void * y, size_t len)
{
return memcmp ( x, y, len) == 0 ;
}
bool is_anyaddr ( private_host_t * this) {
static const uint8_t zeroes[ IPV6_LEN] ;
switch ( this-> address. sa_family)
{
case AF_INET:
{
return memeq ( zeroes, & ( this-> address4. sin_addr. s_addr) , IPV4_LEN) ;
}
case AF_INET6:
{
return memeq ( zeroes, & ( this-> address6. sin6_addr. s6_addr) , IPV6_LEN) ;
}
default :
{
return FALSE;
}
}
}
static bool ip_equals ( private_host_t * this, private_host_t * other)
{
if ( this-> address. sa_family != other-> address. sa_family)
{
return ( is_anyaddr ( this) && is_anyaddr ( other) ) ;
}
switch ( this-> address. sa_family)
{
case AF_INET:
{
return memeq ( & this-> address4. sin_addr, & other-> address4. sin_addr,
sizeof ( this-> address4. sin_addr) ) ;
}
case AF_INET6:
{
return memeq ( & this-> address6. sin6_addr, & other-> address6. sin6_addr,
sizeof ( this-> address6. sin6_addr) ) ;
}
default :
break ;
}
return FALSE;
}
static bool equals_in ( child_entry_t * a, child_entry_t * b)
{
return a-> spi_in == b-> spi_in &&
a-> proto == b-> proto &&
ip_equals ( & a-> host_in, & b-> host_in) ;
}
hashtable_t * child_sa_manager_create ( )
{
hashtable_t * in;
in = hashtable_create ( ( hashtable_hash_t ) hash_in,
( hashtable_equals_t ) equals_in, 8 ) ;
return in;
}
int child_sa_put ( hashtable_t * in) {
static uint32_t unique_id = 100 ;
static uint32_t spi_in = 1 ;
static uint32_t spi_out = 2 ;
child_entry_t * entry;
INIT ( entry,
. unique_id = unique_id++ ,
. proto = PROTO_ESP,
. spi_in = spi_in++ ,
. spi_out = spi_out++ ,
) ;
entry-> host_in. address4. sin_family = AF_INET;
inet_pton ( AF_INET, "192.168.1.250" , & entry-> host_in. address4. sin_addr) ;
entry-> host_out. address4. sin_family = AF_INET;
inet_pton ( AF_INET, "192.168.1.200" , & entry-> host_out. address4. sin_addr) ;
child_entry_t * val;
char buf[ 128 ] ;
val = in-> put ( in, entry, entry) ;
if ( val) {
printf ( "unique_id: %d, spi_in: %d, spi_out: %d " ,
val-> unique_id, val-> spi_in, val-> spi_out
) ;
printf ( "host_in:%s " ,
inet_ntop ( AF_INET, & entry-> host_in. address4. sin_addr, buf, sizeof ( buf) )
) ;
printf ( "host_out:%s\n" ,
inet_ntop ( AF_INET, & entry-> host_out. address4. sin_addr, buf, sizeof ( buf) )
) ;
} else {
printf ( "not find sa\n" ) ;
}
printf ( "count :%d\n" , in-> get_count ( in) ) ;
return 0 ;
}
child_entry_t * child_sa_get ( hashtable_t * in, child_entry_t * entry) {
char buf[ 128 ] ;
child_entry_t * val = in-> get ( in, entry) ;
if ( val) {
printf ( "get unique_id: %d, spi_in: %d, spi_out: %d " ,
val-> unique_id, val-> spi_in, val-> spi_out
) ;
printf ( "host_in:%s " ,
inet_ntop ( AF_INET, & val-> host_in. address4. sin_addr, buf, sizeof ( buf) )
) ;
printf ( "host_out:%s\n" ,
inet_ntop ( AF_INET, & val-> host_out. address4. sin_addr, buf, sizeof ( buf) )
) ;
} else {
printf ( "not find sa\n" ) ;
}
printf ( "count :%d\n" , in-> get_count ( in) ) ;
return val;
}
child_entry_t * child_sa_remove ( hashtable_t * in, child_entry_t * entry) {
char buf[ 128 ] ;
child_entry_t * val = in-> remove ( in, entry) ;
if ( val) {
printf ( "remove unique_id: %d, spi_in: %d, spi_out: %d " ,
val-> unique_id, val-> spi_in, val-> spi_out
) ;
printf ( "host_in:%s " ,
inet_ntop ( AF_INET, & val-> host_in. address4. sin_addr, buf, sizeof ( buf) )
) ;
printf ( "host_out:%s\n" ,
inet_ntop ( AF_INET, & val-> host_out. address4. sin_addr, buf, sizeof ( buf) )
) ;
} else {
printf ( "not find sa\n" ) ;
}
printf ( "count :%d\n" , in-> get_count ( in) ) ;
return val;
}
void child_sa_destroy ( void * val, const void * key) {
if ( val == key) {
free ( key) ;
val = NULL ;
} else {
free ( val) ;
val = NULL ;
free ( key) ;
val = NULL ;
}
}
int main ( )
{
hashtable_t * in;
in = child_sa_manager_create ( ) ;
printf ( "count :%d\n" , in-> get_count ( in) ) ;
for ( int i = 0 ; i < 100 ; i++ ) {
child_sa_put ( in) ;
}
child_entry_t entry = {
. proto = PROTO_ESP,
. spi_in = 5 ,
} ;
entry. host_in. address4. sin_family = AF_INET;
inet_pton ( AF_INET, "192.168.1.250" , & entry. host_in. address4. sin_addr) ;
child_sa_get ( in, & entry) ;
child_sa_remove ( in, & entry) ;
child_sa_get ( in, & entry) ;
enumerator_t * enumerator;
child_entry_t * key, * val;
char buf[ 128 ] ;
enumerator = in-> create_enumerator ( in) ;
while ( enumerator-> enumerate ( enumerator, & key, & val) ) {
if ( val) {
printf ( "enumerate unique_id: %d, spi_in: %d, spi_out: %d " ,
val-> unique_id, val-> spi_in, val-> spi_out
) ;
printf ( "host_in:%s " ,
inet_ntop ( AF_INET, & val-> host_in. address4. sin_addr, buf, sizeof ( buf) )
) ;
printf ( "host_out:%s\n" ,
inet_ntop ( AF_INET, & val-> host_out. address4. sin_addr, buf, sizeof ( buf) )
) ;
}
if ( ( ( private_enumerator_t * ) enumerator) -> index == 10 ) {
child_sa_get ( in, key) ;
in-> remove_at ( in, enumerator) ;
child_sa_get ( in, key) ;
}
}
enumerator-> destroy ( enumerator) ;
in-> destroy_function ( in, child_sa_destroy) ;
return 0 ;
}