Annotation of embedaddon/strongswan/src/libcharon/plugins/ha/ha_kernel.c, revision 1.1
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:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>