#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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;
}