diff options
Diffstat (limited to 'src/libcharon/plugins/ha/ha_kernel.c')
-rw-r--r-- | src/libcharon/plugins/ha/ha_kernel.c | 157 |
1 files changed, 129 insertions, 28 deletions
diff --git a/src/libcharon/plugins/ha/ha_kernel.c b/src/libcharon/plugins/ha/ha_kernel.c index 56bdbf454..2377a2630 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); } @@ -123,7 +214,7 @@ static void enable_disable(private_ha_kernel_t *this, u_int segment, file, strerror(errno)); return; } - if (write(fd, cmd, strlen(cmd) == -1)) + if (write(fd, cmd, strlen(cmd)) == -1) { DBG1(DBG_CFG, "writing to CLUSTERIP file '%s' failed: %s", file, strerror(errno)); @@ -149,6 +240,7 @@ static segment_mask_t get_active(private_ha_kernel_t *this, char *file) return 0; } len = read(fd, buf, sizeof(buf)-1); + close(fd); if (len == -1) { DBG1(DBG_CFG, "reading from CLUSTERIP file '%s' failed: %s", @@ -182,11 +274,14 @@ METHOD(ha_kernel_t, activate, void, char *file; enumerator = enumerator_create_directory(CLUSTERIP_DIR); - while (enumerator->enumerate(enumerator, NULL, &file, NULL)) + if (enumerator) { - enable_disable(this, segment, file, TRUE); + while (enumerator->enumerate(enumerator, NULL, &file, NULL)) + { + enable_disable(this, segment, file, TRUE); + } + enumerator->destroy(enumerator); } - enumerator->destroy(enumerator); } METHOD(ha_kernel_t, deactivate, void, @@ -196,11 +291,14 @@ METHOD(ha_kernel_t, deactivate, void, char *file; enumerator = enumerator_create_directory(CLUSTERIP_DIR); - while (enumerator->enumerate(enumerator, NULL, &file, NULL)) + if (enumerator) { - enable_disable(this, segment, file, FALSE); + while (enumerator->enumerate(enumerator, NULL, &file, NULL)) + { + enable_disable(this, segment, file, FALSE); + } + enumerator->destroy(enumerator); } - enumerator->destroy(enumerator); } /** @@ -214,23 +312,26 @@ static void disable_all(private_ha_kernel_t *this) int i; enumerator = enumerator_create_directory(CLUSTERIP_DIR); - while (enumerator->enumerate(enumerator, NULL, &file, NULL)) + if (enumerator) { - if (chown(file, charon->uid, charon->gid) != 0) + while (enumerator->enumerate(enumerator, NULL, &file, NULL)) { - DBG1(DBG_CFG, "changing ClusterIP permissions failed: %s", - strerror(errno)); - } - active = get_active(this, file); - for (i = 1; i <= this->count; i++) - { - if (active & SEGMENTS_BIT(i)) + if (chown(file, charon->uid, charon->gid) != 0) { - enable_disable(this, i, file, FALSE); + DBG1(DBG_CFG, "changing ClusterIP permissions failed: %s", + strerror(errno)); + } + active = get_active(this, file); + for (i = 1; i <= this->count; i++) + { + if (active & SEGMENTS_BIT(i)) + { + enable_disable(this, i, file, FALSE); + } } } + enumerator->destroy(enumerator); } - enumerator->destroy(enumerator); } METHOD(ha_kernel_t, destroy, void, @@ -255,7 +356,7 @@ ha_kernel_t *ha_kernel_create(u_int count) .deactivate = _deactivate, .destroy = _destroy, }, - .initval = 0, + .version = get_jhash_version(), .count = count, ); |