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>