diff options
author | Martin Willi <martin@revosec.ch> | 2011-08-02 17:00:15 +0200 |
---|---|---|
committer | Martin Willi <martin@revosec.ch> | 2011-08-02 17:00:59 +0200 |
commit | 6e5118e9e3a18f248ec41f86b41a011bf869dafc (patch) | |
tree | 2663a57c603539494eef907ffdf7b42a629aa2ea /src | |
parent | b29ed3715c1cf3a3b14cc10d0a47c5ad6db658d6 (diff) | |
download | strongswan-6e5118e9e3a18f248ec41f86b41a011bf869dafc.tar.bz2 strongswan-6e5118e9e3a18f248ec41f86b41a011bf869dafc.tar.xz |
Check for kernel version and use appropriate jhash version in HA plugin
Diffstat (limited to 'src')
-rw-r--r-- | src/libcharon/plugins/ha/ha_kernel.c | 111 |
1 files changed, 101 insertions, 10 deletions
diff --git a/src/libcharon/plugins/ha/ha_kernel.c b/src/libcharon/plugins/ha/ha_kernel.c index 250c3ef5e..1d619b284 100644 --- a/src/libcharon/plugins/ha/ha_kernel.c +++ b/src/libcharon/plugins/ha/ha_kernel.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Martin Willi + * Copyright (C) 2009-2011 Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -18,7 +18,7 @@ typedef u_int32_t u32; typedef u_int8_t u8; -#include <linux/jhash.h> +#include <sys/utsname.h> #include <string.h> #include <errno.h> #include <unistd.h> @@ -28,6 +28,16 @@ typedef u_int8_t u8; #define CLUSTERIP_DIR "/proc/net/ipt_CLUSTERIP" +/** + * Versions of jhash used in the Linux kernel + */ +typedef enum { + /* old variant, http://burtleburtle.net/bob/c/lookup2.c */ + JHASH_LOOKUP2, + /* new variant, http://burtleburtle.net/bob/c/lookup3.c, since 2.6.37 */ + JHASH_LOOKUP3, +} jhash_version_t; + typedef struct private_ha_kernel_t private_ha_kernel_t; /** @@ -41,17 +51,98 @@ struct private_ha_kernel_t { ha_kernel_t public; /** - * Init value for jhash + * Total number of ClusterIP segments */ - u_int initval; + u_int count; /** - * Total number of ClusterIP segments + * jhash version the kernel uses */ - u_int count; + jhash_version_t version; }; /** + * Get the jhash version based on the uname().release + */ +static jhash_version_t get_jhash_version() +{ + struct utsname utsname; + int a, b, c; + + if (uname(&utsname) == 0) + { + switch (sscanf(utsname.release, "%d.%d.%d", &a, &b, &c)) + { + case 3: + if (a == 2 && b == 6) + { + if (c < 37) + { + DBG1(DBG_CFG, "detected Linux %d.%d.%d, using old " + "jhash", a, b, c); + return JHASH_LOOKUP2; + } + DBG1(DBG_CFG, "detected Linux %d.%d.%d, using new " + "jhash", a, b, c); + return JHASH_LOOKUP3; + } + /* FALL */ + case 2: + DBG1(DBG_CFG, "detected Linux %d.%d, using new jhash", a, b); + return JHASH_LOOKUP3; + default: + break; + } + } + DBG1(DBG_CFG, "detecting Linux version failed, using new jhash"); + return JHASH_LOOKUP3; +} + +/** + * Rotate 32 bit word x by k bits + */ +#define jhash_rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/** + * jhash algorithm of two words, as used in kernel (using 0 as initval) + */ +static u_int32_t jhash(jhash_version_t version, u_int32_t a, u_int32_t b) +{ + u_int32_t c = 0; + + switch (version) + { + case JHASH_LOOKUP2: + a += 0x9e3779b9; + b += 0x9e3779b9; + + 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); + break; + case JHASH_LOOKUP3: + a += 0xdeadbeef; + b += 0xdeadbeef; + + c ^= b; c -= jhash_rot(b, 14); + a ^= c; a -= jhash_rot(c, 11); + b ^= a; b -= jhash_rot(a, 25); + c ^= b; c -= jhash_rot(b, 16); + a ^= c; a -= jhash_rot(c, 4); + b ^= a; b -= jhash_rot(a, 14); + c ^= b; c -= jhash_rot(b, 24); + break; + } + return c; +} + +/** * Segmentate a calculated hash */ static u_int hash2segment(private_ha_kernel_t *this, u_int64_t hash) @@ -78,7 +169,7 @@ METHOD(ha_kernel_t, get_segment, u_int, u_int32_t addr; addr = host2int(host); - hash = jhash_1word(ntohl(addr), this->initval); + hash = jhash(this->version, ntohl(addr), 0); return hash2segment(this, hash); } @@ -90,7 +181,7 @@ METHOD(ha_kernel_t, get_segment_spi, u_int, u_int32_t addr; addr = host2int(host); - hash = jhash_2words(ntohl(addr), ntohl(spi), this->initval); + hash = jhash(this->version, ntohl(addr), ntohl(spi)); return hash2segment(this, hash); } @@ -100,7 +191,7 @@ METHOD(ha_kernel_t, get_segment_int, u_int, { unsigned long hash; - hash = jhash_1word(ntohl(n), this->initval); + hash = jhash(this->version, ntohl(n), 0); return hash2segment(this, hash); } @@ -255,7 +346,7 @@ ha_kernel_t *ha_kernel_create(u_int count) .deactivate = _deactivate, .destroy = _destroy, }, - .initval = 0, + .version = get_jhash_version(), .count = count, ); |