Return to ha_kernel.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / libcharon / plugins / ha |
1.1 misho 1: /* 2: * Copyright (C) 2009-2011 Martin Willi 3: * HSR Hochschule fuer Technik Rapperswil 4: * 5: * This program is free software; you can redistribute it and/or modify it 6: * under the terms of the GNU General Public License as published by the 7: * Free Software Foundation; either version 2 of the License, or (at your 8: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. 9: * 10: * This program is distributed in the hope that it will be useful, but 11: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 12: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13: * for more details. 14: */ 15: 16: #include "ha_kernel.h" 17: 18: typedef uint32_t u32; 19: typedef uint8_t u8; 20: 21: #include <sys/utsname.h> 22: #include <string.h> 23: #include <errno.h> 24: #include <unistd.h> 25: #include <sys/types.h> 26: #include <sys/stat.h> 27: #include <fcntl.h> 28: 29: #define CLUSTERIP_DIR "/proc/net/ipt_CLUSTERIP" 30: 31: /** 32: * Versions of jhash used in the Linux kernel 33: */ 34: typedef enum { 35: /* old variant, http://burtleburtle.net/bob/c/lookup2.c */ 36: JHASH_LOOKUP2, 37: /* new variant, http://burtleburtle.net/bob/c/lookup3.c, since 2.6.37 */ 38: JHASH_LOOKUP3, 39: /* variant with different init values, since 4.1 */ 40: JHASH_LOOKUP3_1, 41: } jhash_version_t; 42: 43: typedef struct private_ha_kernel_t private_ha_kernel_t; 44: 45: /** 46: * Private data of an ha_kernel_t object. 47: */ 48: struct private_ha_kernel_t { 49: 50: /** 51: * Public ha_kernel_t interface. 52: */ 53: ha_kernel_t public; 54: 55: /** 56: * Total number of ClusterIP segments 57: */ 58: u_int count; 59: 60: /** 61: * jhash version the kernel uses 62: */ 63: jhash_version_t version; 64: }; 65: 66: /** 67: * Get the jhash version based on the uname().release 68: */ 69: static jhash_version_t get_jhash_version() 70: { 71: struct utsname utsname; 72: int a, b, c; 73: 74: if (uname(&utsname) == 0) 75: { 76: switch (sscanf(utsname.release, "%d.%d.%d", &a, &b, &c)) 77: { 78: case 3: 79: if (a == 2 && b == 6) 80: { 81: if (c < 37) 82: { 83: DBG1(DBG_CFG, "detected Linux %d.%d.%d, using old " 84: "jhash", a, b, c); 85: return JHASH_LOOKUP2; 86: } 87: DBG1(DBG_CFG, "detected Linux %d.%d.%d, using new " 88: "jhash", a, b, c); 89: return JHASH_LOOKUP3; 90: } 91: /* FALL */ 92: case 2: 93: if (a < 4 || (a == 4 && b == 0)) 94: { 95: DBG1(DBG_CFG, "detected Linux %d.%d, using new jhash", 96: a, b); 97: return JHASH_LOOKUP3; 98: } 99: DBG1(DBG_CFG, "detected Linux %d.%d, using new jhash with " 100: "updated init values", a, b); 101: return JHASH_LOOKUP3_1; 102: default: 103: break; 104: } 105: } 106: DBG1(DBG_CFG, "detecting Linux version failed, using new jhash"); 107: return JHASH_LOOKUP3; 108: } 109: 110: /** 111: * Rotate 32 bit word x by k bits 112: */ 113: #define jhash_rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) 114: 115: /** 116: * jhash algorithm of two words, as used in kernel (using 0 as initval) 117: */ 118: static uint32_t jhash(jhash_version_t version, uint32_t a, uint32_t b) 119: { 120: uint32_t c = 0; 121: 122: switch (version) 123: { 124: case JHASH_LOOKUP2: 125: a += 0x9e3779b9; 126: b += 0x9e3779b9; 127: 128: a -= b; a -= c; a ^= (c >> 13); 129: b -= c; b -= a; b ^= (a << 8); 130: c -= a; c -= b; c ^= (b >> 13); 131: a -= b; a -= c; a ^= (c >> 12); 132: b -= c; b -= a; b ^= (a << 16); 133: c -= a; c -= b; c ^= (b >> 5); 134: a -= b; a -= c; a ^= (c >> 3); 135: b -= c; b -= a; b ^= (a << 10); 136: c -= a; c -= b; c ^= (b >> 15); 137: break; 138: case JHASH_LOOKUP3_1: 139: /* changed with 4.1: # of 32-bit words shifted by 2 and c is 140: * initialized. we only use the two word variant with SPIs, so it's 141: * unlikely that b is 0 in that case */ 142: c += ((b ? 2 : 1) << 2) + 0xdeadbeef; 143: a += ((b ? 2 : 1) << 2); 144: b += ((b ? 2 : 1) << 2); 145: /* FALL */ 146: case JHASH_LOOKUP3: 147: a += 0xdeadbeef; 148: b += 0xdeadbeef; 149: 150: c ^= b; c -= jhash_rot(b, 14); 151: a ^= c; a -= jhash_rot(c, 11); 152: b ^= a; b -= jhash_rot(a, 25); 153: c ^= b; c -= jhash_rot(b, 16); 154: a ^= c; a -= jhash_rot(c, 4); 155: b ^= a; b -= jhash_rot(a, 14); 156: c ^= b; c -= jhash_rot(b, 24); 157: break; 158: } 159: return c; 160: } 161: 162: /** 163: * Segment a calculated hash 164: */ 165: static u_int hash2segment(private_ha_kernel_t *this, uint64_t hash) 166: { 167: return ((hash * this->count) >> 32) + 1; 168: } 169: 170: /** 171: * Get a host as an integer for hashing 172: */ 173: static uint32_t host2int(host_t *host) 174: { 175: if (host->get_family(host) == AF_INET) 176: { 177: return *(uint32_t*)host->get_address(host).ptr; 178: } 179: return 0; 180: } 181: 182: METHOD(ha_kernel_t, get_segment, u_int, 183: private_ha_kernel_t *this, host_t *host) 184: { 185: unsigned long hash; 186: uint32_t addr; 187: 188: addr = host2int(host); 189: hash = jhash(this->version, ntohl(addr), 0); 190: 191: return hash2segment(this, hash); 192: } 193: 194: METHOD(ha_kernel_t, get_segment_spi, u_int, 195: private_ha_kernel_t *this, host_t *host, uint32_t spi) 196: { 197: unsigned long hash; 198: uint32_t addr; 199: 200: addr = host2int(host); 201: hash = jhash(this->version, ntohl(addr), ntohl(spi)); 202: 203: return hash2segment(this, hash); 204: } 205: 206: METHOD(ha_kernel_t, get_segment_int, u_int, 207: private_ha_kernel_t *this, int n) 208: { 209: unsigned long hash; 210: 211: hash = jhash(this->version, ntohl(n), 0); 212: 213: return hash2segment(this, hash); 214: } 215: 216: /** 217: * Activate/Deactivate a segment for a given clusterip file 218: */ 219: static void enable_disable(private_ha_kernel_t *this, u_int segment, 220: char *file, bool enable) 221: { 222: char cmd[8]; 223: int fd; 224: 225: snprintf(cmd, sizeof(cmd), "%c%d\n", enable ? '+' : '-', segment); 226: 227: fd = open(file, O_WRONLY); 228: if (fd == -1) 229: { 230: DBG1(DBG_CFG, "opening CLUSTERIP file '%s' failed: %s", 231: file, strerror(errno)); 232: return; 233: } 234: if (write(fd, cmd, strlen(cmd)) == -1) 235: { 236: DBG1(DBG_CFG, "writing to CLUSTERIP file '%s' failed: %s", 237: file, strerror(errno)); 238: } 239: close(fd); 240: } 241: 242: /** 243: * Get the currently active segments in the kernel for a clusterip file 244: */ 245: static segment_mask_t get_active(private_ha_kernel_t *this, char *file) 246: { 247: char buf[256]; 248: segment_mask_t mask = 0; 249: ssize_t len; 250: int fd; 251: 252: fd = open(file, O_RDONLY); 253: if (fd == -1) 254: { 255: DBG1(DBG_CFG, "opening CLUSTERIP file '%s' failed: %s", 256: file, strerror(errno)); 257: return 0; 258: } 259: len = read(fd, buf, sizeof(buf)-1); 260: close(fd); 261: if (len == -1) 262: { 263: DBG1(DBG_CFG, "reading from CLUSTERIP file '%s' failed: %s", 264: file, strerror(errno)); 265: } 266: else 267: { 268: enumerator_t *enumerator; 269: u_int segment; 270: char *token; 271: 272: buf[len] = '\0'; 273: enumerator = enumerator_create_token(buf, ",", " "); 274: while (enumerator->enumerate(enumerator, &token)) 275: { 276: segment = atoi(token); 277: if (segment) 278: { 279: mask |= SEGMENTS_BIT(segment); 280: } 281: } 282: enumerator->destroy(enumerator); 283: } 284: return mask; 285: } 286: 287: METHOD(ha_kernel_t, activate, void, 288: private_ha_kernel_t *this, u_int segment) 289: { 290: enumerator_t *enumerator; 291: char *file; 292: 293: enumerator = enumerator_create_directory(CLUSTERIP_DIR); 294: if (enumerator) 295: { 296: while (enumerator->enumerate(enumerator, NULL, &file, NULL)) 297: { 298: enable_disable(this, segment, file, TRUE); 299: } 300: enumerator->destroy(enumerator); 301: } 302: } 303: 304: METHOD(ha_kernel_t, deactivate, void, 305: private_ha_kernel_t *this, u_int segment) 306: { 307: enumerator_t *enumerator; 308: char *file; 309: 310: enumerator = enumerator_create_directory(CLUSTERIP_DIR); 311: if (enumerator) 312: { 313: while (enumerator->enumerate(enumerator, NULL, &file, NULL)) 314: { 315: enable_disable(this, segment, file, FALSE); 316: } 317: enumerator->destroy(enumerator); 318: } 319: } 320: 321: /** 322: * Disable all not-yet disabled segments on all clusterip addresses 323: */ 324: static void disable_all(private_ha_kernel_t *this) 325: { 326: enumerator_t *enumerator; 327: segment_mask_t active; 328: char *file; 329: int i; 330: 331: enumerator = enumerator_create_directory(CLUSTERIP_DIR); 332: if (enumerator) 333: { 334: while (enumerator->enumerate(enumerator, NULL, &file, NULL)) 335: { 336: if (chown(file, lib->caps->get_uid(lib->caps), 337: lib->caps->get_gid(lib->caps)) != 0) 338: { 339: DBG1(DBG_CFG, "changing ClusterIP permissions failed: %s", 340: strerror(errno)); 341: } 342: active = get_active(this, file); 343: for (i = 1; i <= this->count; i++) 344: { 345: if (active & SEGMENTS_BIT(i)) 346: { 347: enable_disable(this, i, file, FALSE); 348: } 349: } 350: } 351: enumerator->destroy(enumerator); 352: } 353: } 354: 355: METHOD(ha_kernel_t, destroy, void, 356: private_ha_kernel_t *this) 357: { 358: free(this); 359: } 360: 361: /** 362: * See header 363: */ 364: ha_kernel_t *ha_kernel_create(u_int count) 365: { 366: private_ha_kernel_t *this; 367: 368: INIT(this, 369: .public = { 370: .get_segment = _get_segment, 371: .get_segment_spi = _get_segment_spi, 372: .get_segment_int = _get_segment_int, 373: .activate = _activate, 374: .deactivate = _deactivate, 375: .destroy = _destroy, 376: }, 377: .version = get_jhash_version(), 378: .count = count, 379: ); 380: 381: disable_all(this); 382: 383: return &this->public; 384: } 385: