Annotation of embedaddon/strongswan/src/libcharon/plugins/ha/ha_kernel.c, revision 1.1.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>