aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMartin Willi <martin@revosec.ch>2011-08-02 17:00:15 +0200
committerMartin Willi <martin@revosec.ch>2011-08-02 17:00:59 +0200
commit6e5118e9e3a18f248ec41f86b41a011bf869dafc (patch)
tree2663a57c603539494eef907ffdf7b42a629aa2ea /src
parentb29ed3715c1cf3a3b14cc10d0a47c5ad6db658d6 (diff)
downloadstrongswan-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.c111
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,
);