Annotation of embedaddon/strongswan/src/libcharon/plugins/ha/ha_dispatcher.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Copyright (C) 2008 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_dispatcher.h"
        !            17: 
        !            18: #include <daemon.h>
        !            19: #include <sa/ikev2/keymat_v2.h>
        !            20: #include <sa/ikev1/keymat_v1.h>
        !            21: #include <processing/jobs/callback_job.h>
        !            22: #include <processing/jobs/adopt_children_job.h>
        !            23: 
        !            24: typedef struct private_ha_dispatcher_t private_ha_dispatcher_t;
        !            25: typedef struct ha_diffie_hellman_t ha_diffie_hellman_t;
        !            26: 
        !            27: /**
        !            28:  * Private data of an ha_dispatcher_t object.
        !            29:  */
        !            30: struct private_ha_dispatcher_t {
        !            31: 
        !            32:        /**
        !            33:         * Public ha_dispatcher_t interface.
        !            34:         */
        !            35:        ha_dispatcher_t public;
        !            36: 
        !            37:        /**
        !            38:         * socket to pull messages from
        !            39:         */
        !            40:        ha_socket_t *socket;
        !            41: 
        !            42:        /**
        !            43:         * segments to control
        !            44:         */
        !            45:        ha_segments_t *segments;
        !            46: 
        !            47:        /**
        !            48:         * Cache for resync
        !            49:         */
        !            50:        ha_cache_t *cache;
        !            51: 
        !            52:        /**
        !            53:         * Kernel helper
        !            54:         */
        !            55:        ha_kernel_t *kernel;
        !            56: 
        !            57:        /**
        !            58:         * HA enabled pool
        !            59:         */
        !            60:        ha_attribute_t *attr;
        !            61: };
        !            62: 
        !            63: /**
        !            64:  * DH implementation for HA synced DH values
        !            65:  */
        !            66: struct ha_diffie_hellman_t {
        !            67: 
        !            68:        /**
        !            69:         * Implements diffie_hellman_t
        !            70:         */
        !            71:        diffie_hellman_t dh;
        !            72: 
        !            73:        /**
        !            74:         * Shared secret
        !            75:         */
        !            76:        chunk_t secret;
        !            77: 
        !            78:        /**
        !            79:         * Own public value
        !            80:         */
        !            81:        chunk_t pub;
        !            82: };
        !            83: 
        !            84: METHOD(diffie_hellman_t, dh_get_shared_secret, bool,
        !            85:        ha_diffie_hellman_t *this, chunk_t *secret)
        !            86: {
        !            87:        *secret = chunk_clone(this->secret);
        !            88:        return TRUE;
        !            89: }
        !            90: 
        !            91: METHOD(diffie_hellman_t, dh_get_my_public_value, bool,
        !            92:        ha_diffie_hellman_t *this, chunk_t *value)
        !            93: {
        !            94:        *value = chunk_clone(this->pub);
        !            95:        return TRUE;
        !            96: }
        !            97: 
        !            98: METHOD(diffie_hellman_t, dh_destroy, void,
        !            99:        ha_diffie_hellman_t *this)
        !           100: {
        !           101:        free(this);
        !           102: }
        !           103: 
        !           104: /**
        !           105:  * Create a HA synced DH implementation
        !           106:  */
        !           107: static diffie_hellman_t *ha_diffie_hellman_create(chunk_t secret, chunk_t pub)
        !           108: {
        !           109:        ha_diffie_hellman_t *this;
        !           110: 
        !           111:        INIT(this,
        !           112:                .dh = {
        !           113:                        .get_shared_secret = _dh_get_shared_secret,
        !           114:                        .get_my_public_value = _dh_get_my_public_value,
        !           115:                        .destroy = _dh_destroy,
        !           116:                },
        !           117:                .secret = secret,
        !           118:                .pub = pub,
        !           119:        );
        !           120: 
        !           121:        return &this->dh;
        !           122: }
        !           123: 
        !           124: /**
        !           125:  * Process messages of type IKE_ADD
        !           126:  */
        !           127: static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message)
        !           128: {
        !           129:        ha_message_attribute_t attribute;
        !           130:        ha_message_value_t value;
        !           131:        enumerator_t *enumerator;
        !           132:        ike_sa_t *ike_sa = NULL, *old_sa = NULL;
        !           133:        ike_version_t version = IKEV2;
        !           134:        uint16_t encr = 0, len = 0, integ = 0, prf = 0, old_prf = PRF_UNDEFINED;
        !           135:        uint16_t dh_grp = 0;
        !           136:        chunk_t nonce_i = chunk_empty, nonce_r = chunk_empty;
        !           137:        chunk_t secret = chunk_empty, old_skd = chunk_empty;
        !           138:        chunk_t dh_local = chunk_empty, dh_remote = chunk_empty, psk = chunk_empty;
        !           139:        host_t *other = NULL;
        !           140:        bool ok = FALSE;
        !           141:        auth_method_t method = AUTH_RSA;
        !           142: 
        !           143:        enumerator = message->create_attribute_enumerator(message);
        !           144:        while (enumerator->enumerate(enumerator, &attribute, &value))
        !           145:        {
        !           146:                switch (attribute)
        !           147:                {
        !           148:                        case HA_IKE_ID:
        !           149:                                ike_sa = ike_sa_create(value.ike_sa_id,
        !           150:                                                value.ike_sa_id->is_initiator(value.ike_sa_id), version);
        !           151:                                break;
        !           152:                        case HA_IKE_REKEY_ID:
        !           153:                                old_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
        !           154:                                                                                                                  value.ike_sa_id);
        !           155:                                break;
        !           156:                        case HA_REMOTE_ADDR:
        !           157:                                other = value.host->clone(value.host);
        !           158:                                break;
        !           159:                        case HA_IKE_VERSION:
        !           160:                                version = value.u8;
        !           161:                                break;
        !           162:                        case HA_NONCE_I:
        !           163:                                nonce_i = value.chunk;
        !           164:                                break;
        !           165:                        case HA_NONCE_R:
        !           166:                                nonce_r = value.chunk;
        !           167:                                break;
        !           168:                        case HA_SECRET:
        !           169:                                secret = value.chunk;
        !           170:                                break;
        !           171:                        case HA_LOCAL_DH:
        !           172:                                dh_local = value.chunk;
        !           173:                                break;
        !           174:                        case HA_REMOTE_DH:
        !           175:                                dh_remote = value.chunk;
        !           176:                                break;
        !           177:                        case HA_PSK:
        !           178:                                psk = value.chunk;
        !           179:                                break;
        !           180:                        case HA_OLD_SKD:
        !           181:                                old_skd = value.chunk;
        !           182:                                break;
        !           183:                        case HA_ALG_ENCR:
        !           184:                                encr = value.u16;
        !           185:                                break;
        !           186:                        case HA_ALG_ENCR_LEN:
        !           187:                                len = value.u16;
        !           188:                                break;
        !           189:                        case HA_ALG_INTEG:
        !           190:                                integ = value.u16;
        !           191:                                break;
        !           192:                        case HA_ALG_PRF:
        !           193:                                prf = value.u16;
        !           194:                                break;
        !           195:                        case HA_ALG_OLD_PRF:
        !           196:                                old_prf = value.u16;
        !           197:                                break;
        !           198:                        case HA_ALG_DH:
        !           199:                                dh_grp = value.u16;
        !           200:                                break;
        !           201:                        case HA_AUTH_METHOD:
        !           202:                                method = value.u16;
        !           203:                        default:
        !           204:                                break;
        !           205:                }
        !           206:        }
        !           207:        enumerator->destroy(enumerator);
        !           208: 
        !           209:        if (ike_sa)
        !           210:        {
        !           211:                proposal_t *proposal;
        !           212:                diffie_hellman_t *dh;
        !           213: 
        !           214:                proposal = proposal_create(PROTO_IKE, 0);
        !           215:                if (integ)
        !           216:                {
        !           217:                        proposal->add_algorithm(proposal, INTEGRITY_ALGORITHM, integ, 0);
        !           218:                }
        !           219:                if (encr)
        !           220:                {
        !           221:                        proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr, len);
        !           222:                }
        !           223:                if (prf)
        !           224:                {
        !           225:                        proposal->add_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, prf, 0);
        !           226:                }
        !           227:                if (dh_grp)
        !           228:                {
        !           229:                        proposal->add_algorithm(proposal, DIFFIE_HELLMAN_GROUP, dh_grp, 0);
        !           230:                }
        !           231:                charon->bus->set_sa(charon->bus, ike_sa);
        !           232:                dh = ha_diffie_hellman_create(secret, dh_local);
        !           233:                if (ike_sa->get_version(ike_sa) == IKEV2)
        !           234:                {
        !           235:                        keymat_v2_t *keymat_v2 = (keymat_v2_t*)ike_sa->get_keymat(ike_sa);
        !           236: 
        !           237:                        ok = keymat_v2->derive_ike_keys(keymat_v2, proposal, dh, nonce_i,
        !           238:                                                        nonce_r, ike_sa->get_id(ike_sa), old_prf, old_skd);
        !           239:                }
        !           240:                if (ike_sa->get_version(ike_sa) == IKEV1)
        !           241:                {
        !           242:                        keymat_v1_t *keymat_v1 = (keymat_v1_t*)ike_sa->get_keymat(ike_sa);
        !           243:                        shared_key_t *shared = NULL;
        !           244: 
        !           245:                        if (psk.len)
        !           246:                        {
        !           247:                                method = AUTH_PSK;
        !           248:                                shared = shared_key_create(SHARED_IKE, chunk_clone(psk));
        !           249:                        }
        !           250:                        if (keymat_v1->create_hasher(keymat_v1, proposal))
        !           251:                        {
        !           252:                                ok = keymat_v1->derive_ike_keys(keymat_v1, proposal,
        !           253:                                                                dh, dh_remote, nonce_i, nonce_r,
        !           254:                                                                ike_sa->get_id(ike_sa), method, shared);
        !           255:                        }
        !           256:                        DESTROY_IF(shared);
        !           257:                }
        !           258:                dh->destroy(dh);
        !           259:                if (ok)
        !           260:                {
        !           261:                        if (old_sa)
        !           262:                        {
        !           263:                                ike_sa->inherit_pre(ike_sa, old_sa);
        !           264:                                ike_sa->inherit_post(ike_sa, old_sa);
        !           265:                                charon->ike_sa_manager->checkin_and_destroy(
        !           266:                                                                                                charon->ike_sa_manager, old_sa);
        !           267:                                old_sa = NULL;
        !           268:                        }
        !           269:                        if (other)
        !           270:                        {
        !           271:                                ike_sa->set_other_host(ike_sa, other);
        !           272:                                other = NULL;
        !           273:                        }
        !           274:                        ike_sa->set_state(ike_sa, IKE_CONNECTING);
        !           275:                        ike_sa->set_proposal(ike_sa, proposal);
        !           276:                        this->cache->cache(this->cache, ike_sa, message);
        !           277:                        message = NULL;
        !           278:                        charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
        !           279:                }
        !           280:                else
        !           281:                {
        !           282:                        DBG1(DBG_IKE, "HA keymat derivation failed");
        !           283:                        ike_sa->destroy(ike_sa);
        !           284:                }
        !           285:                charon->bus->set_sa(charon->bus, NULL);
        !           286:                proposal->destroy(proposal);
        !           287:        }
        !           288:        if (old_sa)
        !           289:        {
        !           290:                charon->ike_sa_manager->checkin(charon->ike_sa_manager, old_sa);
        !           291:        }
        !           292:        DESTROY_IF(other);
        !           293:        DESTROY_IF(message);
        !           294: }
        !           295: 
        !           296: /**
        !           297:  * Apply a condition flag to the IKE_SA if it is in set
        !           298:  */
        !           299: static void set_condition(ike_sa_t *ike_sa, ike_condition_t set,
        !           300:                                                  ike_condition_t flag)
        !           301: {
        !           302:        ike_sa->set_condition(ike_sa, flag, flag & set);
        !           303: }
        !           304: 
        !           305: /**
        !           306:  * Apply a extension flag to the IKE_SA if it is in set
        !           307:  */
        !           308: static void set_extension(ike_sa_t *ike_sa, ike_extension_t set,
        !           309:                                                  ike_extension_t flag)
        !           310: {
        !           311:        if (flag & set)
        !           312:        {
        !           313:                ike_sa->enable_extension(ike_sa, flag);
        !           314:        }
        !           315: }
        !           316: 
        !           317: /**
        !           318:  * Process messages of type IKE_UPDATE
        !           319:  */
        !           320: static void process_ike_update(private_ha_dispatcher_t *this,
        !           321:                                                           ha_message_t *message)
        !           322: {
        !           323:        ha_message_attribute_t attribute;
        !           324:        ha_message_value_t value;
        !           325:        enumerator_t *enumerator;
        !           326:        ike_sa_t *ike_sa = NULL;
        !           327:        peer_cfg_t *peer_cfg = NULL;
        !           328:        auth_cfg_t *auth;
        !           329:        bool received_vip = FALSE, first_local_vip = TRUE, first_peer_addr = TRUE;
        !           330: 
        !           331:        enumerator = message->create_attribute_enumerator(message);
        !           332:        while (enumerator->enumerate(enumerator, &attribute, &value))
        !           333:        {
        !           334:                if (attribute != HA_IKE_ID && ike_sa == NULL)
        !           335:                {
        !           336:                        /* must be first attribute */
        !           337:                        break;
        !           338:                }
        !           339:                switch (attribute)
        !           340:                {
        !           341:                        case HA_IKE_ID:
        !           342:                                ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
        !           343:                                                                                                                  value.ike_sa_id);
        !           344:                                break;
        !           345:                        case HA_LOCAL_ID:
        !           346:                                ike_sa->set_my_id(ike_sa, value.id->clone(value.id));
        !           347:                                break;
        !           348:                        case HA_REMOTE_ID:
        !           349:                                ike_sa->set_other_id(ike_sa, value.id->clone(value.id));
        !           350:                                break;
        !           351:                        case HA_REMOTE_EAP_ID:
        !           352:                                auth = auth_cfg_create();
        !           353:                                auth->add(auth, AUTH_RULE_EAP_IDENTITY, value.id->clone(value.id));
        !           354:                                ike_sa->add_auth_cfg(ike_sa, FALSE, auth);
        !           355:                                break;
        !           356:                        case HA_LOCAL_ADDR:
        !           357:                                ike_sa->set_my_host(ike_sa, value.host->clone(value.host));
        !           358:                                break;
        !           359:                        case HA_REMOTE_ADDR:
        !           360:                                ike_sa->set_other_host(ike_sa, value.host->clone(value.host));
        !           361:                                break;
        !           362:                        case HA_LOCAL_VIP:
        !           363:                                if (first_local_vip)
        !           364:                                {
        !           365:                                        ike_sa->clear_virtual_ips(ike_sa, TRUE);
        !           366:                                        first_local_vip = FALSE;
        !           367:                                }
        !           368:                                ike_sa->add_virtual_ip(ike_sa, TRUE, value.host);
        !           369:                                break;
        !           370:                        case HA_REMOTE_VIP:
        !           371:                                if (!received_vip)
        !           372:                                {
        !           373:                                        ike_sa->clear_virtual_ips(ike_sa, FALSE);
        !           374:                                }
        !           375:                                ike_sa->add_virtual_ip(ike_sa, FALSE, value.host);
        !           376:                                received_vip = TRUE;
        !           377:                                break;
        !           378:                        case HA_PEER_ADDR:
        !           379:                                if (first_peer_addr)
        !           380:                                {
        !           381:                                        ike_sa->clear_peer_addresses(ike_sa);
        !           382:                                        first_peer_addr = FALSE;
        !           383:                                }
        !           384:                                ike_sa->add_peer_address(ike_sa, value.host->clone(value.host));
        !           385:                                break;
        !           386:                        case HA_CONFIG_NAME:
        !           387:                                peer_cfg = charon->backends->get_peer_cfg_by_name(
        !           388:                                                                                                charon->backends, value.str);
        !           389:                                if (peer_cfg)
        !           390:                                {
        !           391:                                        ike_sa->set_peer_cfg(ike_sa, peer_cfg);
        !           392:                                        peer_cfg->destroy(peer_cfg);
        !           393:                                }
        !           394:                                else
        !           395:                                {
        !           396:                                        DBG1(DBG_IKE, "HA is missing nodes peer configuration");
        !           397:                                        charon->ike_sa_manager->checkin_and_destroy(
        !           398:                                                                                                charon->ike_sa_manager, ike_sa);
        !           399:                                        ike_sa = NULL;
        !           400:                                }
        !           401:                                break;
        !           402:                        case HA_EXTENSIONS:
        !           403:                                set_extension(ike_sa, value.u32, EXT_NATT);
        !           404:                                set_extension(ike_sa, value.u32, EXT_MOBIKE);
        !           405:                                set_extension(ike_sa, value.u32, EXT_HASH_AND_URL);
        !           406:                                set_extension(ike_sa, value.u32, EXT_MULTIPLE_AUTH);
        !           407:                                set_extension(ike_sa, value.u32, EXT_STRONGSWAN);
        !           408:                                set_extension(ike_sa, value.u32, EXT_EAP_ONLY_AUTHENTICATION);
        !           409:                                set_extension(ike_sa, value.u32, EXT_MS_WINDOWS);
        !           410:                                set_extension(ike_sa, value.u32, EXT_XAUTH);
        !           411:                                set_extension(ike_sa, value.u32, EXT_DPD);
        !           412:                                break;
        !           413:                        case HA_CONDITIONS:
        !           414:                                set_condition(ike_sa, value.u32, COND_NAT_ANY);
        !           415:                                set_condition(ike_sa, value.u32, COND_NAT_HERE);
        !           416:                                set_condition(ike_sa, value.u32, COND_NAT_THERE);
        !           417:                                set_condition(ike_sa, value.u32, COND_NAT_FAKE);
        !           418:                                set_condition(ike_sa, value.u32, COND_EAP_AUTHENTICATED);
        !           419:                                set_condition(ike_sa, value.u32, COND_CERTREQ_SEEN);
        !           420:                                set_condition(ike_sa, value.u32, COND_ORIGINAL_INITIATOR);
        !           421:                                set_condition(ike_sa, value.u32, COND_STALE);
        !           422:                                set_condition(ike_sa, value.u32, COND_INIT_CONTACT_SEEN);
        !           423:                                set_condition(ike_sa, value.u32, COND_XAUTH_AUTHENTICATED);
        !           424:                                break;
        !           425:                        default:
        !           426:                                break;
        !           427:                }
        !           428:        }
        !           429:        enumerator->destroy(enumerator);
        !           430: 
        !           431:        if (ike_sa)
        !           432:        {
        !           433:                if (ike_sa->get_state(ike_sa) == IKE_CONNECTING &&
        !           434:                        ike_sa->get_peer_cfg(ike_sa))
        !           435:                {
        !           436:                        DBG1(DBG_CFG, "installed HA passive IKE_SA '%s' %H[%Y]...%H[%Y]",
        !           437:                                 ike_sa->get_name(ike_sa),
        !           438:                                 ike_sa->get_my_host(ike_sa), ike_sa->get_my_id(ike_sa),
        !           439:                                 ike_sa->get_other_host(ike_sa), ike_sa->get_other_id(ike_sa));
        !           440:                        ike_sa->set_state(ike_sa, IKE_PASSIVE);
        !           441:                }
        !           442:                if (received_vip)
        !           443:                {
        !           444:                        enumerator_t *pools, *vips;
        !           445:                        host_t *vip;
        !           446:                        char *pool;
        !           447: 
        !           448:                        peer_cfg = ike_sa->get_peer_cfg(ike_sa);
        !           449:                        if (peer_cfg)
        !           450:                        {
        !           451:                                pools = peer_cfg->create_pool_enumerator(peer_cfg);
        !           452:                                while (pools->enumerate(pools, &pool))
        !           453:                                {
        !           454:                                        vips = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE);
        !           455:                                        while (vips->enumerate(vips, &vip))
        !           456:                                        {
        !           457:                                                this->attr->reserve(this->attr, pool, vip);
        !           458:                                        }
        !           459:                                        vips->destroy(vips);
        !           460:                                }
        !           461:                                pools->destroy(pools);
        !           462:                        }
        !           463:                }
        !           464: #ifdef USE_IKEV1
        !           465:                if (ike_sa->get_version(ike_sa) == IKEV1)
        !           466:                {
        !           467:                        lib->processor->queue_job(lib->processor, (job_t*)
        !           468:                                                        adopt_children_job_create(ike_sa->get_id(ike_sa)));
        !           469:                }
        !           470: #endif /* USE_IKEV1 */
        !           471:                this->cache->cache(this->cache, ike_sa, message);
        !           472:                charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
        !           473:        }
        !           474:        else
        !           475:        {
        !           476:                DBG1(DBG_CFG, "passive HA IKE_SA to update not found");
        !           477:                message->destroy(message);
        !           478:        }
        !           479: }
        !           480: 
        !           481: /**
        !           482:  * Process messages of type IKE_MID_INITIATOR/RESPONDER
        !           483:  */
        !           484: static void process_ike_mid(private_ha_dispatcher_t *this,
        !           485:                                                           ha_message_t *message, bool initiator)
        !           486: {
        !           487:        ha_message_attribute_t attribute;
        !           488:        ha_message_value_t value;
        !           489:        enumerator_t *enumerator;
        !           490:        ike_sa_t *ike_sa = NULL;
        !           491:        uint32_t mid = 0;
        !           492: 
        !           493:        enumerator = message->create_attribute_enumerator(message);
        !           494:        while (enumerator->enumerate(enumerator, &attribute, &value))
        !           495:        {
        !           496:                switch (attribute)
        !           497:                {
        !           498:                        case HA_IKE_ID:
        !           499:                                ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
        !           500:                                                                                                                  value.ike_sa_id);
        !           501:                                break;
        !           502:                        case HA_MID:
        !           503:                                mid = value.u32;
        !           504:                                break;
        !           505:                        default:
        !           506:                                break;
        !           507:                }
        !           508:        }
        !           509:        enumerator->destroy(enumerator);
        !           510: 
        !           511:        if (ike_sa)
        !           512:        {
        !           513:                if (mid)
        !           514:                {
        !           515:                        ike_sa->set_message_id(ike_sa, initiator, mid);
        !           516:                }
        !           517:                this->cache->cache(this->cache, ike_sa, message);
        !           518:                charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
        !           519:        }
        !           520:        else
        !           521:        {
        !           522:                message->destroy(message);
        !           523:        }
        !           524: }
        !           525: 
        !           526: /**
        !           527:  * Process messages of type IKE_IV
        !           528:  */
        !           529: static void process_ike_iv(private_ha_dispatcher_t *this, ha_message_t *message)
        !           530: {
        !           531:        ha_message_attribute_t attribute;
        !           532:        ha_message_value_t value;
        !           533:        enumerator_t *enumerator;
        !           534:        ike_sa_t *ike_sa = NULL;
        !           535:        chunk_t iv = chunk_empty;
        !           536: 
        !           537:        enumerator = message->create_attribute_enumerator(message);
        !           538:        while (enumerator->enumerate(enumerator, &attribute, &value))
        !           539:        {
        !           540:                switch (attribute)
        !           541:                {
        !           542:                        case HA_IKE_ID:
        !           543:                                ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
        !           544:                                                                                                                  value.ike_sa_id);
        !           545:                                break;
        !           546:                        case HA_IV:
        !           547:                                iv = value.chunk;
        !           548:                                break;
        !           549:                        default:
        !           550:                                break;
        !           551:                }
        !           552:        }
        !           553:        enumerator->destroy(enumerator);
        !           554: 
        !           555:        if (ike_sa)
        !           556:        {
        !           557:                if (ike_sa->get_version(ike_sa) == IKEV1)
        !           558:                {
        !           559:                        if (iv.len)
        !           560:                        {
        !           561:                                keymat_v1_t *keymat;
        !           562: 
        !           563:                                keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa);
        !           564:                                if (keymat->update_iv(keymat, 0, iv))
        !           565:                                {
        !           566:                                        keymat->confirm_iv(keymat, 0);
        !           567:                                }
        !           568:                        }
        !           569:                }
        !           570:                this->cache->cache(this->cache, ike_sa, message);
        !           571:                charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
        !           572:        }
        !           573:        else
        !           574:        {
        !           575:                message->destroy(message);
        !           576:        }
        !           577: }
        !           578: 
        !           579: /**
        !           580:  * Process messages of type IKE_DELETE
        !           581:  */
        !           582: static void process_ike_delete(private_ha_dispatcher_t *this,
        !           583:                                                           ha_message_t *message)
        !           584: {
        !           585:        ha_message_attribute_t attribute;
        !           586:        ha_message_value_t value;
        !           587:        enumerator_t *enumerator;
        !           588:        ike_sa_t *ike_sa = NULL;
        !           589: 
        !           590:        enumerator = message->create_attribute_enumerator(message);
        !           591:        while (enumerator->enumerate(enumerator, &attribute, &value))
        !           592:        {
        !           593:                switch (attribute)
        !           594:                {
        !           595:                        case HA_IKE_ID:
        !           596:                                ike_sa = charon->ike_sa_manager->checkout(
        !           597:                                                                        charon->ike_sa_manager, value.ike_sa_id);
        !           598:                                break;
        !           599:                        default:
        !           600:                                break;
        !           601:                }
        !           602:        }
        !           603:        enumerator->destroy(enumerator);
        !           604:        if (ike_sa)
        !           605:        {
        !           606:                this->cache->cache(this->cache, ike_sa, message);
        !           607:                charon->ike_sa_manager->checkin_and_destroy(
        !           608:                                                charon->ike_sa_manager, ike_sa);
        !           609:        }
        !           610:        else
        !           611:        {
        !           612:                message->destroy(message);
        !           613:        }
        !           614: }
        !           615: 
        !           616: /**
        !           617:  * Lookup a child cfg from the peer cfg by name
        !           618:  */
        !           619: static child_cfg_t* find_child_cfg(ike_sa_t *ike_sa, char *name)
        !           620: {
        !           621:        peer_cfg_t *peer_cfg;
        !           622:        child_cfg_t *current, *found = NULL;
        !           623:        enumerator_t *enumerator;
        !           624: 
        !           625:        peer_cfg = ike_sa->get_peer_cfg(ike_sa);
        !           626:        if (peer_cfg)
        !           627:        {
        !           628:                enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
        !           629:                while (enumerator->enumerate(enumerator, &current))
        !           630:                {
        !           631:                        if (streq(current->get_name(current), name))
        !           632:                        {
        !           633:                                found = current;
        !           634:                                break;
        !           635:                        }
        !           636:                }
        !           637:                enumerator->destroy(enumerator);
        !           638:        }
        !           639:        return found;
        !           640: }
        !           641: 
        !           642: /**
        !           643:  * Process messages of type CHILD_ADD
        !           644:  */
        !           645: static void process_child_add(private_ha_dispatcher_t *this,
        !           646:                                                          ha_message_t *message)
        !           647: {
        !           648:        ha_message_attribute_t attribute;
        !           649:        ha_message_value_t value;
        !           650:        enumerator_t *enumerator;
        !           651:        ike_sa_t *ike_sa = NULL;
        !           652:        char *config_name = "";
        !           653:        child_cfg_t *config = NULL;
        !           654:        child_sa_t *child_sa;
        !           655:        proposal_t *proposal;
        !           656:        bool initiator = FALSE, failed = FALSE, ok = FALSE;
        !           657:        uint32_t inbound_spi = 0, outbound_spi = 0;
        !           658:        uint16_t inbound_cpi = 0, outbound_cpi = 0;
        !           659:        uint8_t mode = MODE_TUNNEL, ipcomp = 0;
        !           660:        uint16_t encr = 0, integ = 0, len = 0, dh_grp = 0;
        !           661:        uint16_t esn = NO_EXT_SEQ_NUMBERS;
        !           662:        u_int seg_i, seg_o;
        !           663:        chunk_t nonce_i = chunk_empty, nonce_r = chunk_empty, secret = chunk_empty;
        !           664:        chunk_t encr_i, integ_i, encr_r, integ_r;
        !           665:        linked_list_t *local_ts, *remote_ts;
        !           666:        diffie_hellman_t *dh = NULL;
        !           667: 
        !           668:        enumerator = message->create_attribute_enumerator(message);
        !           669:        while (enumerator->enumerate(enumerator, &attribute, &value))
        !           670:        {
        !           671:                switch (attribute)
        !           672:                {
        !           673:                        case HA_IKE_ID:
        !           674:                                ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
        !           675:                                                                                                                  value.ike_sa_id);
        !           676:                                break;
        !           677:                        case HA_CONFIG_NAME:
        !           678:                                config_name = value.str;
        !           679:                                break;
        !           680:                        case HA_INITIATOR:
        !           681:                                initiator = value.u8;
        !           682:                                break;
        !           683:                        case HA_INBOUND_SPI:
        !           684:                                inbound_spi = value.u32;
        !           685:                                break;
        !           686:                        case HA_OUTBOUND_SPI:
        !           687:                                outbound_spi = value.u32;
        !           688:                                break;
        !           689:                        case HA_INBOUND_CPI:
        !           690:                                inbound_cpi = value.u32;
        !           691:                                break;
        !           692:                        case HA_OUTBOUND_CPI:
        !           693:                                outbound_cpi = value.u32;
        !           694:                                break;
        !           695:                        case HA_IPSEC_MODE:
        !           696:                                mode = value.u8;
        !           697:                                break;
        !           698:                        case HA_IPCOMP:
        !           699:                                ipcomp = value.u8;
        !           700:                                break;
        !           701:                        case HA_ALG_ENCR:
        !           702:                                encr = value.u16;
        !           703:                                break;
        !           704:                        case HA_ALG_ENCR_LEN:
        !           705:                                len = value.u16;
        !           706:                                break;
        !           707:                        case HA_ALG_INTEG:
        !           708:                                integ = value.u16;
        !           709:                                break;
        !           710:                        case HA_ALG_DH:
        !           711:                                dh_grp = value.u16;
        !           712:                                break;
        !           713:                        case HA_ESN:
        !           714:                                esn = value.u16;
        !           715:                                break;
        !           716:                        case HA_NONCE_I:
        !           717:                                nonce_i = value.chunk;
        !           718:                                break;
        !           719:                        case HA_NONCE_R:
        !           720:                                nonce_r = value.chunk;
        !           721:                                break;
        !           722:                        case HA_SECRET:
        !           723:                                secret = value.chunk;
        !           724:                                break;
        !           725:                        default:
        !           726:                                break;
        !           727:                }
        !           728:        }
        !           729:        enumerator->destroy(enumerator);
        !           730: 
        !           731:        if (!ike_sa)
        !           732:        {
        !           733:                DBG1(DBG_CHD, "IKE_SA for HA CHILD_SA not found");
        !           734:                message->destroy(message);
        !           735:                return;
        !           736:        }
        !           737:        config = find_child_cfg(ike_sa, config_name);
        !           738:        if (!config)
        !           739:        {
        !           740:                DBG1(DBG_CHD, "HA is missing nodes child configuration");
        !           741:                charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
        !           742:                message->destroy(message);
        !           743:                return;
        !           744:        }
        !           745: 
        !           746:        child_sa_create_t data = {
        !           747:                .encap = ike_sa->has_condition(ike_sa, COND_NAT_ANY),
        !           748:        };
        !           749:        child_sa = child_sa_create(ike_sa->get_my_host(ike_sa),
        !           750:                                                           ike_sa->get_other_host(ike_sa), config, &data);
        !           751:        child_sa->set_mode(child_sa, mode);
        !           752:        child_sa->set_protocol(child_sa, PROTO_ESP);
        !           753:        child_sa->set_ipcomp(child_sa, ipcomp);
        !           754: 
        !           755:        proposal = proposal_create(PROTO_ESP, 0);
        !           756:        if (integ)
        !           757:        {
        !           758:                proposal->add_algorithm(proposal, INTEGRITY_ALGORITHM, integ, 0);
        !           759:        }
        !           760:        if (encr)
        !           761:        {
        !           762:                proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr, len);
        !           763:        }
        !           764:        if (dh_grp)
        !           765:        {
        !           766:                proposal->add_algorithm(proposal, DIFFIE_HELLMAN_GROUP, dh_grp, 0);
        !           767:        }
        !           768:        proposal->add_algorithm(proposal, EXTENDED_SEQUENCE_NUMBERS, esn, 0);
        !           769:        if (secret.len)
        !           770:        {
        !           771:                dh = ha_diffie_hellman_create(secret, chunk_empty);
        !           772:        }
        !           773:        if (ike_sa->get_version(ike_sa) == IKEV2)
        !           774:        {
        !           775:                keymat_v2_t *keymat_v2 = (keymat_v2_t*)ike_sa->get_keymat(ike_sa);
        !           776: 
        !           777:                ok = keymat_v2->derive_child_keys(keymat_v2, proposal, dh,
        !           778:                                                nonce_i, nonce_r, &encr_i, &integ_i, &encr_r, &integ_r);
        !           779:        }
        !           780:        if (ike_sa->get_version(ike_sa) == IKEV1)
        !           781:        {
        !           782:                keymat_v1_t *keymat_v1 = (keymat_v1_t*)ike_sa->get_keymat(ike_sa);
        !           783:                uint32_t spi_i, spi_r;
        !           784: 
        !           785:                spi_i = initiator ? inbound_spi : outbound_spi;
        !           786:                spi_r = initiator ? outbound_spi : inbound_spi;
        !           787: 
        !           788:                ok = keymat_v1->derive_child_keys(keymat_v1, proposal, dh, spi_i, spi_r,
        !           789:                                                nonce_i, nonce_r, &encr_i, &integ_i, &encr_r, &integ_r);
        !           790:        }
        !           791:        DESTROY_IF(dh);
        !           792:        if (!ok)
        !           793:        {
        !           794:                DBG1(DBG_CHD, "HA CHILD_SA key derivation failed");
        !           795:                child_sa->destroy(child_sa);
        !           796:                proposal->destroy(proposal);
        !           797:                charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
        !           798:                return;
        !           799:        }
        !           800:        child_sa->set_proposal(child_sa, proposal);
        !           801:        child_sa->set_state(child_sa, CHILD_INSTALLING);
        !           802:        proposal->destroy(proposal);
        !           803: 
        !           804:        /* TODO: Change CHILD_SA API to avoid cloning twice */
        !           805:        local_ts = linked_list_create();
        !           806:        remote_ts = linked_list_create();
        !           807:        enumerator = message->create_attribute_enumerator(message);
        !           808:        while (enumerator->enumerate(enumerator, &attribute, &value))
        !           809:        {
        !           810:                switch (attribute)
        !           811:                {
        !           812:                        case HA_LOCAL_TS:
        !           813:                                local_ts->insert_last(local_ts, value.ts->clone(value.ts));
        !           814:                                break;
        !           815:                        case HA_REMOTE_TS:
        !           816:                                remote_ts->insert_last(remote_ts, value.ts->clone(value.ts));
        !           817:                                break;
        !           818:                        default:
        !           819:                                break;
        !           820:                }
        !           821:        }
        !           822:        enumerator->destroy(enumerator);
        !           823: 
        !           824:        child_sa->set_policies(child_sa, local_ts, remote_ts);
        !           825: 
        !           826:        if (initiator)
        !           827:        {
        !           828:                if (child_sa->install(child_sa, encr_r, integ_r, inbound_spi,
        !           829:                                                          inbound_cpi, initiator, TRUE, TRUE) != SUCCESS ||
        !           830:                        child_sa->install(child_sa, encr_i, integ_i, outbound_spi,
        !           831:                                                          outbound_cpi, initiator, FALSE, TRUE) != SUCCESS)
        !           832:                {
        !           833:                        failed = TRUE;
        !           834:                }
        !           835:        }
        !           836:        else
        !           837:        {
        !           838:                if (child_sa->install(child_sa, encr_i, integ_i, inbound_spi,
        !           839:                                                          inbound_cpi, initiator, TRUE, TRUE) != SUCCESS ||
        !           840:                        child_sa->install(child_sa, encr_r, integ_r, outbound_spi,
        !           841:                                                          outbound_cpi, initiator, FALSE, TRUE) != SUCCESS)
        !           842:                {
        !           843:                        failed = TRUE;
        !           844:                }
        !           845:        }
        !           846:        chunk_clear(&encr_i);
        !           847:        chunk_clear(&integ_i);
        !           848:        chunk_clear(&encr_r);
        !           849:        chunk_clear(&integ_r);
        !           850: 
        !           851:        if (failed)
        !           852:        {
        !           853:                DBG1(DBG_CHD, "HA CHILD_SA installation failed");
        !           854:                child_sa->destroy(child_sa);
        !           855:                local_ts->destroy_offset(local_ts, offsetof(traffic_selector_t, destroy));
        !           856:                remote_ts->destroy_offset(remote_ts, offsetof(traffic_selector_t, destroy));
        !           857:                charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
        !           858:                message->destroy(message);
        !           859:                return;
        !           860:        }
        !           861: 
        !           862:        seg_i = this->kernel->get_segment_spi(this->kernel,
        !           863:                                                                ike_sa->get_my_host(ike_sa), inbound_spi);
        !           864:        seg_o = this->kernel->get_segment_spi(this->kernel,
        !           865:                                                                ike_sa->get_other_host(ike_sa), outbound_spi);
        !           866: 
        !           867:        DBG1(DBG_CFG, "installed HA CHILD_SA %s{%d} %#R === %#R "
        !           868:                "(segment in: %d%s, out: %d%s)", child_sa->get_name(child_sa),
        !           869:                child_sa->get_unique_id(child_sa), local_ts, remote_ts,
        !           870:                seg_i, this->segments->is_active(this->segments, seg_i) ? "*" : "",
        !           871:                seg_o, this->segments->is_active(this->segments, seg_o) ? "*" : "");
        !           872:        child_sa->install_policies(child_sa);
        !           873:        local_ts->destroy_offset(local_ts, offsetof(traffic_selector_t, destroy));
        !           874:        remote_ts->destroy_offset(remote_ts, offsetof(traffic_selector_t, destroy));
        !           875: 
        !           876:        child_sa->set_state(child_sa, CHILD_INSTALLED);
        !           877:        ike_sa->add_child_sa(ike_sa, child_sa);
        !           878:        message->destroy(message);
        !           879:        charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
        !           880: }
        !           881: 
        !           882: /**
        !           883:  * Process messages of type CHILD_DELETE
        !           884:  */
        !           885: static void process_child_delete(private_ha_dispatcher_t *this,
        !           886:                                                                 ha_message_t *message)
        !           887: {
        !           888:        ha_message_attribute_t attribute;
        !           889:        ha_message_value_t value;
        !           890:        enumerator_t *enumerator;
        !           891:        ike_sa_t *ike_sa = NULL;
        !           892:        child_sa_t *child_sa;
        !           893:        uint32_t spi = 0;
        !           894: 
        !           895:        enumerator = message->create_attribute_enumerator(message);
        !           896:        while (enumerator->enumerate(enumerator, &attribute, &value))
        !           897:        {
        !           898:                switch (attribute)
        !           899:                {
        !           900:                        case HA_IKE_ID:
        !           901:                                ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
        !           902:                                                                                                                  value.ike_sa_id);
        !           903:                                break;
        !           904:                        case HA_INBOUND_SPI:
        !           905:                                spi = value.u32;
        !           906:                                break;
        !           907:                        default:
        !           908:                                break;
        !           909:                }
        !           910:        }
        !           911:        enumerator->destroy(enumerator);
        !           912: 
        !           913:        if (ike_sa)
        !           914:        {
        !           915:                child_sa = ike_sa->get_child_sa(ike_sa, PROTO_ESP, spi, TRUE);
        !           916:                if (child_sa)
        !           917:                {
        !           918:                        ike_sa->destroy_child_sa(ike_sa, PROTO_ESP, spi);
        !           919:                }
        !           920:                charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
        !           921:        }
        !           922:        message->destroy(message);
        !           923: }
        !           924: 
        !           925: /**
        !           926:  * Process messages of type SEGMENT_TAKE/DROP
        !           927:  */
        !           928: static void process_segment(private_ha_dispatcher_t *this,
        !           929:                                                        ha_message_t *message, bool take)
        !           930: {
        !           931:        ha_message_attribute_t attribute;
        !           932:        ha_message_value_t value;
        !           933:        enumerator_t *enumerator;
        !           934: 
        !           935:        enumerator = message->create_attribute_enumerator(message);
        !           936:        while (enumerator->enumerate(enumerator, &attribute, &value))
        !           937:        {
        !           938:                switch (attribute)
        !           939:                {
        !           940:                        case HA_SEGMENT:
        !           941:                                if (take)
        !           942:                                {
        !           943:                                        DBG1(DBG_CFG, "remote node takes segment %d", value.u16);
        !           944:                                        this->segments->deactivate(this->segments, value.u16, FALSE);
        !           945:                                }
        !           946:                                else
        !           947:                                {
        !           948:                                        DBG1(DBG_CFG, "remote node drops segment %d", value.u16);
        !           949:                                        this->segments->activate(this->segments, value.u16, FALSE);
        !           950:                                }
        !           951:                                break;
        !           952:                        default:
        !           953:                                break;
        !           954:                }
        !           955:        }
        !           956:        enumerator->destroy(enumerator);
        !           957:        message->destroy(message);
        !           958: }
        !           959: 
        !           960: /**
        !           961:  * Process messages of type STATUS
        !           962:  */
        !           963: static void process_status(private_ha_dispatcher_t *this,
        !           964:                                                   ha_message_t *message)
        !           965: {
        !           966:        ha_message_attribute_t attribute;
        !           967:        ha_message_value_t value;
        !           968:        enumerator_t *enumerator;
        !           969:        segment_mask_t mask = 0;
        !           970: 
        !           971:        enumerator = message->create_attribute_enumerator(message);
        !           972:        while (enumerator->enumerate(enumerator, &attribute, &value))
        !           973:        {
        !           974:                switch (attribute)
        !           975:                {
        !           976:                        case HA_SEGMENT:
        !           977:                                mask |= SEGMENTS_BIT(value.u16);
        !           978:                                break;
        !           979:                        default:
        !           980:                                break;
        !           981:                }
        !           982:        }
        !           983:        enumerator->destroy(enumerator);
        !           984: 
        !           985:        this->segments->handle_status(this->segments, mask);
        !           986:        message->destroy(message);
        !           987: }
        !           988: 
        !           989: /**
        !           990:  * Process messages of type RESYNC
        !           991:  */
        !           992: static void process_resync(private_ha_dispatcher_t *this,
        !           993:                                                   ha_message_t *message)
        !           994: {
        !           995:        ha_message_attribute_t attribute;
        !           996:        ha_message_value_t value;
        !           997:        enumerator_t *enumerator;
        !           998: 
        !           999:        enumerator = message->create_attribute_enumerator(message);
        !          1000:        while (enumerator->enumerate(enumerator, &attribute, &value))
        !          1001:        {
        !          1002:                switch (attribute)
        !          1003:                {
        !          1004:                        case HA_SEGMENT:
        !          1005:                                this->cache->resync(this->cache, value.u16);
        !          1006:                                break;
        !          1007:                        default:
        !          1008:                                break;
        !          1009:                }
        !          1010:        }
        !          1011:        enumerator->destroy(enumerator);
        !          1012:        message->destroy(message);
        !          1013: }
        !          1014: 
        !          1015: /**
        !          1016:  * Dispatcher job function
        !          1017:  */
        !          1018: static job_requeue_t dispatch(private_ha_dispatcher_t *this)
        !          1019: {
        !          1020:        ha_message_t *message;
        !          1021:        ha_message_type_t type;
        !          1022: 
        !          1023:        message = this->socket->pull(this->socket);
        !          1024:        type = message->get_type(message);
        !          1025:        if (type != HA_STATUS)
        !          1026:        {
        !          1027:                DBG2(DBG_CFG, "received HA %N message", ha_message_type_names,
        !          1028:                         message->get_type(message));
        !          1029:        }
        !          1030:        switch (type)
        !          1031:        {
        !          1032:                case HA_IKE_ADD:
        !          1033:                        process_ike_add(this, message);
        !          1034:                        break;
        !          1035:                case HA_IKE_UPDATE:
        !          1036:                        process_ike_update(this, message);
        !          1037:                        break;
        !          1038:                case HA_IKE_MID_INITIATOR:
        !          1039:                        process_ike_mid(this, message, TRUE);
        !          1040:                        break;
        !          1041:                case HA_IKE_MID_RESPONDER:
        !          1042:                        process_ike_mid(this, message, FALSE);
        !          1043:                        break;
        !          1044:                case HA_IKE_IV:
        !          1045:                        process_ike_iv(this, message);
        !          1046:                        break;
        !          1047:                case HA_IKE_DELETE:
        !          1048:                        process_ike_delete(this, message);
        !          1049:                        break;
        !          1050:                case HA_CHILD_ADD:
        !          1051:                        process_child_add(this, message);
        !          1052:                        break;
        !          1053:                case HA_CHILD_DELETE:
        !          1054:                        process_child_delete(this, message);
        !          1055:                        break;
        !          1056:                case HA_SEGMENT_DROP:
        !          1057:                        process_segment(this, message, FALSE);
        !          1058:                        break;
        !          1059:                case HA_SEGMENT_TAKE:
        !          1060:                        process_segment(this, message, TRUE);
        !          1061:                        break;
        !          1062:                case HA_STATUS:
        !          1063:                        process_status(this, message);
        !          1064:                        break;
        !          1065:                case HA_RESYNC:
        !          1066:                        process_resync(this, message);
        !          1067:                        break;
        !          1068:                default:
        !          1069:                        DBG1(DBG_CFG, "received unknown HA message type %d", type);
        !          1070:                        message->destroy(message);
        !          1071:                        break;
        !          1072:        }
        !          1073:        return JOB_REQUEUE_DIRECT;
        !          1074: }
        !          1075: 
        !          1076: METHOD(ha_dispatcher_t, destroy, void,
        !          1077:        private_ha_dispatcher_t *this)
        !          1078: {
        !          1079:        free(this);
        !          1080: }
        !          1081: 
        !          1082: /**
        !          1083:  * See header
        !          1084:  */
        !          1085: ha_dispatcher_t *ha_dispatcher_create(ha_socket_t *socket,
        !          1086:                                                                        ha_segments_t *segments, ha_cache_t *cache,
        !          1087:                                                                        ha_kernel_t *kernel, ha_attribute_t *attr)
        !          1088: {
        !          1089:        private_ha_dispatcher_t *this;
        !          1090: 
        !          1091: 
        !          1092:        INIT(this,
        !          1093:                .public = {
        !          1094:                        .destroy = _destroy,
        !          1095:                },
        !          1096:                .socket = socket,
        !          1097:                .segments = segments,
        !          1098:                .cache = cache,
        !          1099:                .kernel = kernel,
        !          1100:                .attr = attr,
        !          1101:        );
        !          1102:        lib->processor->queue_job(lib->processor,
        !          1103:                (job_t*)callback_job_create_with_prio((callback_job_cb_t)dispatch, this,
        !          1104:                                NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
        !          1105: 
        !          1106:        return &this->public;
        !          1107: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>