Annotation of embedaddon/strongswan/src/libcharon/sa/ikev2/connect_manager.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Copyright (C) 2007-2008 Tobias Brunner
        !             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 "connect_manager.h"
        !            17: 
        !            18: #include <math.h>
        !            19: 
        !            20: #include <daemon.h>
        !            21: #include <threading/mutex.h>
        !            22: #include <collections/linked_list.h>
        !            23: #include <crypto/hashers/hasher.h>
        !            24: 
        !            25: #include <processing/jobs/callback_job.h>
        !            26: #include <processing/jobs/initiate_mediation_job.h>
        !            27: #include <encoding/payloads/endpoint_notify.h>
        !            28: 
        !            29: /* base timeout
        !            30:  * the check interval is ME_INTERVAL */
        !            31: #define ME_INTERVAL 25 /* ms */
        !            32: /* retransmission timeout is first ME_INTERVAL for ME_BOOST retransmissions
        !            33:  * then gets reduced to ME_INTERVAL * ME_RETRANS_BASE ^ (sent retransmissions - ME_BOOST). */
        !            34: /* number of initial retransmissions sent in short interval */
        !            35: #define ME_BOOST 2
        !            36: /* base for retransmissions */
        !            37: #define ME_RETRANS_BASE 1.8
        !            38: /* max number of retransmissions */
        !            39: #define ME_MAX_RETRANS 13
        !            40: 
        !            41: /* time to wait before the initiator finishes the connectivity checks after
        !            42:  * the first check has succeeded */
        !            43: #define ME_WAIT_TO_FINISH 1000 /* ms */
        !            44: 
        !            45: typedef struct private_connect_manager_t private_connect_manager_t;
        !            46: 
        !            47: /**
        !            48:  * Additional private members of connect_manager_t.
        !            49:  */
        !            50: struct private_connect_manager_t {
        !            51:        /**
        !            52:         * Public interface of connect_manager_t.
        !            53:         */
        !            54:         connect_manager_t public;
        !            55: 
        !            56:         /**
        !            57:          * Lock for exclusively accessing the manager.
        !            58:          */
        !            59:         mutex_t *mutex;
        !            60: 
        !            61:         /**
        !            62:          * Hasher to generate signatures
        !            63:          */
        !            64:         hasher_t *hasher;
        !            65: 
        !            66:         /**
        !            67:          * Linked list with initiated mediated connections
        !            68:          */
        !            69:         linked_list_t *initiated;
        !            70: 
        !            71:         /**
        !            72:          * Linked list with checklists (hash table with connect ID as key would
        !            73:          * be better).
        !            74:          */
        !            75:         linked_list_t *checklists;
        !            76: };
        !            77: 
        !            78: typedef enum check_state_t check_state_t;
        !            79: 
        !            80: enum check_state_t {
        !            81:        CHECK_NONE,
        !            82:        CHECK_WAITING,
        !            83:        CHECK_IN_PROGRESS,
        !            84:        CHECK_SUCCEEDED,
        !            85:        CHECK_FAILED
        !            86: };
        !            87: 
        !            88: typedef struct endpoint_pair_t endpoint_pair_t;
        !            89: 
        !            90: /**
        !            91:  * An entry in the check list.
        !            92:  */
        !            93: struct endpoint_pair_t {
        !            94:        /** pair id */
        !            95:        uint32_t id;
        !            96: 
        !            97:        /** priority */
        !            98:        uint64_t priority;
        !            99: 
        !           100:        /** local endpoint */
        !           101:        host_t *local;
        !           102: 
        !           103:        /** remote endpoint */
        !           104:        host_t *remote;
        !           105: 
        !           106:        /** state */
        !           107:        check_state_t state;
        !           108: 
        !           109:        /** number of retransmissions */
        !           110:        uint32_t retransmitted;
        !           111: 
        !           112:        /** the generated packet */
        !           113:        packet_t *packet;
        !           114: };
        !           115: 
        !           116: /**
        !           117:  * Destroys an endpoint pair
        !           118:  */
        !           119: static void endpoint_pair_destroy(endpoint_pair_t *this)
        !           120: {
        !           121:        DESTROY_IF(this->local);
        !           122:        DESTROY_IF(this->remote);
        !           123:        DESTROY_IF(this->packet);
        !           124:        free(this);
        !           125: }
        !           126: 
        !           127: /**
        !           128:  * Creates a new entry for the list.
        !           129:  */
        !           130: static endpoint_pair_t *endpoint_pair_create(endpoint_notify_t *initiator,
        !           131:                endpoint_notify_t *responder, bool initiator_is_local)
        !           132: {
        !           133:        endpoint_pair_t *this;
        !           134: 
        !           135:        uint32_t pi = initiator->get_priority(initiator);
        !           136:        uint32_t pr = responder->get_priority(responder);
        !           137: 
        !           138:        INIT(this,
        !           139:                .priority = pow(2, 32) * min(pi, pr) + 2 * max(pi, pr)
        !           140:                                                                                         + (pi > pr ? 1 : 0),
        !           141:                .local = initiator_is_local ? initiator->get_base(initiator)
        !           142:                                                                        : responder->get_base(responder),
        !           143:                .remote = initiator_is_local ? responder->get_host(responder)
        !           144:                                                                         : initiator->get_host(initiator),
        !           145:                .state = CHECK_WAITING,
        !           146:        );
        !           147: 
        !           148:        this->local = this->local->clone(this->local);
        !           149:        this->remote = this->remote->clone(this->remote);
        !           150: 
        !           151:        return this;
        !           152: }
        !           153: 
        !           154: 
        !           155: typedef struct check_list_t check_list_t;
        !           156: 
        !           157: /**
        !           158:  * An entry in the linked list.
        !           159:  */
        !           160: struct check_list_t {
        !           161: 
        !           162:        struct {
        !           163:                /** initiator's id */
        !           164:                identification_t *id;
        !           165: 
        !           166:                /** initiator's key */
        !           167:                chunk_t key;
        !           168: 
        !           169:                /** initiator's endpoints */
        !           170:                linked_list_t *endpoints;
        !           171:        } initiator;
        !           172: 
        !           173:        struct {
        !           174:                /** responder's id */
        !           175:                identification_t *id;
        !           176: 
        !           177:                /** responder's key */
        !           178:                chunk_t key;
        !           179: 
        !           180:                /** responder's endpoints */
        !           181:                linked_list_t *endpoints;
        !           182:        } responder;
        !           183: 
        !           184:        /** connect id */
        !           185:        chunk_t connect_id;
        !           186: 
        !           187:        /** list of endpoint pairs */
        !           188:        linked_list_t *pairs;
        !           189: 
        !           190:        /** pairs queued for triggered checks */
        !           191:        linked_list_t *triggered;
        !           192: 
        !           193:        /** state */
        !           194:        check_state_t state;
        !           195: 
        !           196:        /** TRUE if this is the initiator */
        !           197:        bool is_initiator;
        !           198: 
        !           199:        /** TRUE if the initiator is finishing the checks */
        !           200:        bool is_finishing;
        !           201: 
        !           202:        /** the current sender job */
        !           203:        job_t *sender;
        !           204: 
        !           205: };
        !           206: 
        !           207: /**
        !           208:  * Destroys a checklist
        !           209:  */
        !           210: static void check_list_destroy(check_list_t *this)
        !           211: {
        !           212:        DESTROY_IF(this->initiator.id);
        !           213:        DESTROY_IF(this->responder.id);
        !           214: 
        !           215:        chunk_free(&this->connect_id);
        !           216:        chunk_free(&this->initiator.key);
        !           217:        chunk_free(&this->responder.key);
        !           218: 
        !           219:        DESTROY_OFFSET_IF(this->initiator.endpoints,
        !           220:                                          offsetof(endpoint_notify_t, destroy));
        !           221:        DESTROY_OFFSET_IF(this->responder.endpoints,
        !           222:                                          offsetof(endpoint_notify_t, destroy));
        !           223: 
        !           224:        DESTROY_FUNCTION_IF(this->pairs, (void*)endpoint_pair_destroy);
        !           225:        /* this list contains some of the elements contained in this->pairs */
        !           226:        DESTROY_IF(this->triggered);
        !           227: 
        !           228:        free(this);
        !           229: }
        !           230: 
        !           231: /**
        !           232:  * Creates a new checklist
        !           233:  */
        !           234: static check_list_t *check_list_create(identification_t *initiator,
        !           235:                                                                           identification_t *responder,
        !           236:                                                                           chunk_t connect_id,
        !           237:                                                                           chunk_t initiator_key,
        !           238:                                                                           linked_list_t *initiator_endpoints,
        !           239:                                                                           bool is_initiator)
        !           240: {
        !           241:        check_list_t *this;
        !           242: 
        !           243:        INIT(this,
        !           244:                .connect_id = chunk_clone(connect_id),
        !           245:                .initiator = {
        !           246:                        .id = initiator->clone(initiator),
        !           247:                        .key = chunk_clone(initiator_key),
        !           248:                        .endpoints = initiator_endpoints->clone_offset(initiator_endpoints,
        !           249:                                                                                        offsetof(endpoint_notify_t, clone)),
        !           250:                },
        !           251:                .responder = {
        !           252:                        .id = responder->clone(responder),
        !           253:                },
        !           254:                .pairs = linked_list_create(),
        !           255:                .triggered = linked_list_create(),
        !           256:                .state = CHECK_NONE,
        !           257:                .is_initiator = is_initiator,
        !           258:        );
        !           259: 
        !           260:        return this;
        !           261: }
        !           262: 
        !           263: typedef struct initiated_t initiated_t;
        !           264: 
        !           265: /**
        !           266:  * For an initiator, the data stored about initiated mediation connections
        !           267:  */
        !           268: struct initiated_t {
        !           269:        /** my id */
        !           270:        identification_t *id;
        !           271: 
        !           272:        /** peer id */
        !           273:        identification_t *peer_id;
        !           274: 
        !           275:        /** list of mediated sas */
        !           276:        linked_list_t *mediated;
        !           277: };
        !           278: 
        !           279: /**
        !           280:  * Destroys a queued initiation
        !           281:  */
        !           282: static void initiated_destroy(initiated_t *this)
        !           283: {
        !           284:        DESTROY_IF(this->id);
        !           285:        DESTROY_IF(this->peer_id);
        !           286:        this->mediated->destroy_offset(this->mediated,
        !           287:                                                                   offsetof(ike_sa_id_t, destroy));
        !           288:        free(this);
        !           289: }
        !           290: 
        !           291: /**
        !           292:  * Creates a queued initiation
        !           293:  */
        !           294: static initiated_t *initiated_create(identification_t *id,
        !           295:                                                                         identification_t *peer_id)
        !           296: {
        !           297:        initiated_t *this;
        !           298: 
        !           299:        INIT(this,
        !           300:                .id = id->clone(id),
        !           301:                .peer_id = peer_id->clone(peer_id),
        !           302:                .mediated = linked_list_create(),
        !           303:        );
        !           304: 
        !           305:        return this;
        !           306: }
        !           307: 
        !           308: 
        !           309: typedef struct check_t check_t;
        !           310: 
        !           311: /**
        !           312:  * Data exchanged in a connectivity check
        !           313:  */
        !           314: struct check_t {
        !           315:        /** message id */
        !           316:        uint32_t mid;
        !           317: 
        !           318:        /** source of the connectivity check */
        !           319:        host_t *src;
        !           320: 
        !           321:        /** destination of the connectivity check */
        !           322:        host_t *dst;
        !           323: 
        !           324:        /** connect id */
        !           325:        chunk_t connect_id;
        !           326: 
        !           327:        /** endpoint */
        !           328:        endpoint_notify_t *endpoint;
        !           329: 
        !           330:        /** raw endpoint payload (to verify the signature) */
        !           331:        chunk_t endpoint_raw;
        !           332: 
        !           333:        /** connect auth */
        !           334:        chunk_t auth;
        !           335: };
        !           336: 
        !           337: /**
        !           338:  * Destroys a connectivity check
        !           339:  */
        !           340: static void check_destroy(check_t *this)
        !           341: {
        !           342:        chunk_free(&this->connect_id);
        !           343:        chunk_free(&this->endpoint_raw);
        !           344:        chunk_free(&this->auth);
        !           345:        DESTROY_IF(this->src);
        !           346:        DESTROY_IF(this->dst);
        !           347:        DESTROY_IF(this->endpoint);
        !           348:        free(this);
        !           349: }
        !           350: 
        !           351: /**
        !           352:  * Creates a new connectivity check
        !           353:  */
        !           354: static check_t *check_create()
        !           355: {
        !           356:        check_t *this;
        !           357: 
        !           358:        INIT(this,
        !           359:                .mid = 0,
        !           360:        );
        !           361: 
        !           362:        return this;
        !           363: }
        !           364: 
        !           365: typedef struct callback_data_t callback_data_t;
        !           366: 
        !           367: /**
        !           368:  * Data required by several callback jobs used in this file
        !           369:  */
        !           370: struct callback_data_t {
        !           371:        /** connect manager */
        !           372:        private_connect_manager_t *connect_manager;
        !           373: 
        !           374:        /** connect id */
        !           375:        chunk_t connect_id;
        !           376: 
        !           377:        /** message (pair) id */
        !           378:        uint32_t mid;
        !           379: };
        !           380: 
        !           381: /**
        !           382:  * Destroys a callback data object
        !           383:  */
        !           384: static void callback_data_destroy(callback_data_t *this)
        !           385: {
        !           386:        chunk_free(&this->connect_id);
        !           387:        free(this);
        !           388: }
        !           389: 
        !           390: /**
        !           391:  * Creates a new callback data object
        !           392:  */
        !           393: static callback_data_t *callback_data_create(private_connect_manager_t *connect_manager,
        !           394:                                                                                         chunk_t connect_id)
        !           395: {
        !           396:        callback_data_t *this;
        !           397:        INIT(this,
        !           398:                .connect_manager = connect_manager,
        !           399:                .connect_id = chunk_clone(connect_id),
        !           400:                .mid = 0,
        !           401:        );
        !           402:        return this;
        !           403: }
        !           404: 
        !           405: /**
        !           406:  * Creates a new retransmission data object
        !           407:  */
        !           408: static callback_data_t *retransmit_data_create(private_connect_manager_t *connect_manager,
        !           409:                                                                                           chunk_t connect_id, uint32_t mid)
        !           410: {
        !           411:        callback_data_t *this = callback_data_create(connect_manager, connect_id);
        !           412:        this->mid = mid;
        !           413:        return this;
        !           414: }
        !           415: 
        !           416: typedef struct initiate_data_t initiate_data_t;
        !           417: 
        !           418: /**
        !           419:  * Data required by the initiate mediated
        !           420:  */
        !           421: struct initiate_data_t {
        !           422:        /** checklist */
        !           423:        check_list_t *checklist;
        !           424: 
        !           425:        /** waiting mediated connections */
        !           426:        initiated_t *initiated;
        !           427: };
        !           428: 
        !           429: /**
        !           430:  * Destroys a initiate data object
        !           431:  */
        !           432: static void initiate_data_destroy(initiate_data_t *this)
        !           433: {
        !           434:        check_list_destroy(this->checklist);
        !           435:        initiated_destroy(this->initiated);
        !           436:        free(this);
        !           437: }
        !           438: 
        !           439: /**
        !           440:  * Creates a new initiate data object
        !           441:  */
        !           442: static initiate_data_t *initiate_data_create(check_list_t *checklist,
        !           443:                                                                                         initiated_t *initiated)
        !           444: {
        !           445:        initiate_data_t *this;
        !           446:        INIT(this,
        !           447:                .checklist = checklist,
        !           448:                .initiated = initiated,
        !           449:        );
        !           450:        return this;
        !           451: }
        !           452: 
        !           453: CALLBACK(match_initiated_by_ids, bool,
        !           454:        initiated_t *current, va_list args)
        !           455: {
        !           456:        identification_t *id, *peer_id;
        !           457: 
        !           458:        VA_ARGS_VGET(args, id, peer_id);
        !           459:        return id->equals(id, current->id) && peer_id->equals(peer_id, current->peer_id);
        !           460: }
        !           461: 
        !           462: static bool get_initiated_by_ids(private_connect_manager_t *this,
        !           463:                                                                 identification_t *id,
        !           464:                                                                 identification_t *peer_id,
        !           465:                                                                 initiated_t **initiated)
        !           466: {
        !           467:        return this->initiated->find_first(this->initiated, match_initiated_by_ids,
        !           468:                                                                (void**)initiated, id, peer_id);
        !           469: }
        !           470: 
        !           471: /**
        !           472:  * Removes data about initiated connections
        !           473:  */
        !           474: static void remove_initiated(private_connect_manager_t *this,
        !           475:                                                         initiated_t *initiated)
        !           476: {
        !           477:        enumerator_t *enumerator;
        !           478:        initiated_t *current;
        !           479: 
        !           480:        enumerator = this->initiated->create_enumerator(this->initiated);
        !           481:        while (enumerator->enumerate(enumerator, (void**)&current))
        !           482:        {
        !           483:                if (current == initiated)
        !           484:                {
        !           485:                        this->initiated->remove_at(this->initiated, enumerator);
        !           486:                        break;
        !           487:                }
        !           488:        }
        !           489:        enumerator->destroy(enumerator);
        !           490: }
        !           491: 
        !           492: CALLBACK(match_checklist_by_id, bool,
        !           493:        check_list_t *current, va_list args)
        !           494: {
        !           495:        chunk_t connect_id;
        !           496: 
        !           497:        VA_ARGS_VGET(args, connect_id);
        !           498:        return chunk_equals(connect_id, current->connect_id);
        !           499: }
        !           500: 
        !           501: static bool get_checklist_by_id(private_connect_manager_t *this,
        !           502:                                                                chunk_t connect_id, check_list_t **check_list)
        !           503: {
        !           504:        return this->checklists->find_first(this->checklists, match_checklist_by_id,
        !           505:                                                                                (void**)check_list, connect_id);
        !           506: }
        !           507: 
        !           508: /**
        !           509:  * Removes a checklist
        !           510:  */
        !           511: static void remove_checklist(private_connect_manager_t *this,
        !           512:                                                         check_list_t *checklist)
        !           513: {
        !           514:        enumerator_t *enumerator;
        !           515:        check_list_t *current;
        !           516: 
        !           517:        enumerator = this->checklists->create_enumerator(this->checklists);
        !           518:        while (enumerator->enumerate(enumerator, (void**)&current))
        !           519:        {
        !           520:                if (current == checklist)
        !           521:                {
        !           522:                        this->checklists->remove_at(this->checklists, enumerator);
        !           523:                        break;
        !           524:                }
        !           525:        }
        !           526:        enumerator->destroy(enumerator);
        !           527: }
        !           528: 
        !           529: CALLBACK(match_endpoint_by_host, bool,
        !           530:        endpoint_notify_t *current, va_list args)
        !           531: {
        !           532:        host_t *host;
        !           533: 
        !           534:        VA_ARGS_VGET(args, host);
        !           535:        return host->equals(host, current->get_host(current));
        !           536: }
        !           537: 
        !           538: static bool endpoints_contain(linked_list_t *endpoints, host_t *host,
        !           539:                                                                  endpoint_notify_t **endpoint)
        !           540: {
        !           541:        return endpoints->find_first(endpoints, match_endpoint_by_host,
        !           542:                                                                 (void**)endpoint, host);
        !           543: }
        !           544: 
        !           545: /**
        !           546:  * Inserts an endpoint pair into a list of pairs ordered by priority (high to low)
        !           547:  */
        !           548: static void insert_pair_by_priority(linked_list_t *pairs, endpoint_pair_t *pair)
        !           549: {
        !           550:        enumerator_t *enumerator = pairs->create_enumerator(pairs);
        !           551:        endpoint_pair_t *current;
        !           552:        while (enumerator->enumerate(enumerator, (void**)&current) &&
        !           553:                   current->priority >= pair->priority)
        !           554:        {
        !           555:                continue;
        !           556:        }
        !           557:        pairs->insert_before(pairs, enumerator, pair);
        !           558:        enumerator->destroy(enumerator);
        !           559: }
        !           560: 
        !           561: CALLBACK(match_pair_by_hosts, bool,
        !           562:        endpoint_pair_t *current, va_list args)
        !           563: {
        !           564:        host_t *local, *remote;
        !           565: 
        !           566:        VA_ARGS_VGET(args, local, remote);
        !           567:        return local->equals(local, current->local) &&
        !           568:                   remote->equals(remote, current->remote);
        !           569: }
        !           570: 
        !           571: static bool get_pair_by_hosts(linked_list_t *pairs, host_t *local,
        !           572:                                                          host_t *remote, endpoint_pair_t **pair)
        !           573: {
        !           574:        return pairs->find_first(pairs, match_pair_by_hosts, (void**)pair, local,
        !           575:                                                         remote);
        !           576: }
        !           577: 
        !           578: CALLBACK(match_pair_by_id, bool,
        !           579:        endpoint_pair_t *current, va_list args)
        !           580: {
        !           581:        uint32_t id;
        !           582: 
        !           583:        VA_ARGS_VGET(args, id);
        !           584:        return current->id == id;
        !           585: }
        !           586: 
        !           587: /**
        !           588:  * Searches for a pair with a specific id
        !           589:  */
        !           590: static bool get_pair_by_id(check_list_t *checklist, uint32_t id,
        !           591:                                                   endpoint_pair_t **pair)
        !           592: {
        !           593:        return checklist->pairs->find_first(checklist->pairs, match_pair_by_id,
        !           594:                                                                                (void**)pair, id);
        !           595: }
        !           596: 
        !           597: CALLBACK(match_succeeded_pair, bool,
        !           598:        endpoint_pair_t *current, va_list args)
        !           599: {
        !           600:        return current->state == CHECK_SUCCEEDED;
        !           601: }
        !           602: 
        !           603: /**
        !           604:  * Returns the best pair of state CHECK_SUCCEEDED from a checklist.
        !           605:  */
        !           606: static bool get_best_valid_pair(check_list_t *checklist, endpoint_pair_t **pair)
        !           607: {
        !           608:        return checklist->pairs->find_first(checklist->pairs, match_succeeded_pair,
        !           609:                                                                                (void**)pair);
        !           610: }
        !           611: 
        !           612: CALLBACK(match_waiting_pair, bool,
        !           613:        endpoint_pair_t *current, va_list args)
        !           614: {
        !           615:        return current->state == CHECK_WAITING;
        !           616: }
        !           617: 
        !           618: /**
        !           619:  * Returns and *removes* the first triggered pair in state CHECK_WAITING.
        !           620:  */
        !           621: static status_t get_triggered_pair(check_list_t *checklist,
        !           622:                                                                   endpoint_pair_t **pair)
        !           623: {
        !           624:        enumerator_t *enumerator;
        !           625:        endpoint_pair_t *current;
        !           626:        status_t status = NOT_FOUND;
        !           627: 
        !           628:        enumerator = checklist->triggered->create_enumerator(checklist->triggered);
        !           629:        while (enumerator->enumerate(enumerator, (void**)&current))
        !           630:        {
        !           631:                checklist->triggered->remove_at(checklist->triggered, enumerator);
        !           632: 
        !           633:                if (current->state == CHECK_WAITING)
        !           634:                {
        !           635:                        if (pair)
        !           636:                        {
        !           637:                                *pair = current;
        !           638:                        }
        !           639:                        status = SUCCESS;
        !           640:                        break;
        !           641:                }
        !           642:        }
        !           643:        enumerator->destroy(enumerator);
        !           644: 
        !           645:        return status;
        !           646: }
        !           647: 
        !           648: /**
        !           649:  * Prints all the pairs on a checklist
        !           650:  */
        !           651: static void print_checklist(check_list_t *checklist)
        !           652: {
        !           653:        enumerator_t *enumerator;
        !           654:        endpoint_pair_t *current;
        !           655: 
        !           656:        DBG1(DBG_IKE, "pairs on checklist %#B:", &checklist->connect_id);
        !           657:        enumerator = checklist->pairs->create_enumerator(checklist->pairs);
        !           658:        while (enumerator->enumerate(enumerator, (void**)&current))
        !           659:        {
        !           660:                DBG1(DBG_IKE, " * %#H - %#H (%d)", current->local, current->remote,
        !           661:                         current->priority);
        !           662:        }
        !           663:        enumerator->destroy(enumerator);
        !           664: }
        !           665: 
        !           666: /**
        !           667:  * Prunes identical pairs with lower priority from the list
        !           668:  * Note: this function also numbers the remaining pairs serially
        !           669:  */
        !           670: static void prune_pairs(linked_list_t *pairs)
        !           671: {
        !           672:        enumerator_t *enumerator, *search;
        !           673:        endpoint_pair_t *current, *other;
        !           674:        uint32_t id = 0;
        !           675: 
        !           676:        enumerator = pairs->create_enumerator(pairs);
        !           677:        search = pairs->create_enumerator(pairs);
        !           678:        while (enumerator->enumerate(enumerator, (void**)&current))
        !           679:        {
        !           680:                current->id = ++id;
        !           681: 
        !           682:                while (search->enumerate(search, (void**)&other))
        !           683:                {
        !           684:                        if (current == other)
        !           685:                        {
        !           686:                                continue;
        !           687:                        }
        !           688: 
        !           689:                        if (current->local->equals(current->local, other->local) &&
        !           690:                                current->remote->equals(current->remote, other->remote))
        !           691:                        {
        !           692:                                /* since the list of pairs is sorted by priority in descending
        !           693:                                 * order, and we iterate the list from the beginning, we are
        !           694:                                 * sure that the priority of 'other' is lower than that of
        !           695:                                 * 'current', remove it */
        !           696:                                DBG1(DBG_IKE, "pruning endpoint pair %#H - %#H with priority %d",
        !           697:                                         other->local, other->remote, other->priority);
        !           698:                                pairs->remove_at(pairs, search);
        !           699:                                endpoint_pair_destroy(other);
        !           700:                        }
        !           701:                }
        !           702:                pairs->reset_enumerator(pairs, search);
        !           703:        }
        !           704:        search->destroy(search);
        !           705:        enumerator->destroy(enumerator);
        !           706: }
        !           707: 
        !           708: /**
        !           709:  * Builds a list of endpoint pairs
        !           710:  */
        !           711: static void build_pairs(check_list_t *checklist)
        !           712: {
        !           713:        /* FIXME: limit endpoints and pairs */
        !           714:        enumerator_t *enumerator_i, *enumerator_r;
        !           715:        endpoint_notify_t *initiator, *responder;
        !           716: 
        !           717:        enumerator_i = checklist->initiator.endpoints->create_enumerator(
        !           718:                                                                                checklist->initiator.endpoints);
        !           719:        while (enumerator_i->enumerate(enumerator_i, (void**)&initiator))
        !           720:        {
        !           721:                enumerator_r = checklist->responder.endpoints->create_enumerator(
        !           722:                                                                                checklist->responder.endpoints);
        !           723:                while (enumerator_r->enumerate(enumerator_r, (void**)&responder))
        !           724:                {
        !           725:                        if (initiator->get_family(initiator) != responder->get_family(responder))
        !           726:                        {
        !           727:                                continue;
        !           728:                        }
        !           729: 
        !           730:                        insert_pair_by_priority(checklist->pairs, endpoint_pair_create(
        !           731:                                                        initiator, responder, checklist->is_initiator));
        !           732:                }
        !           733:                enumerator_r->destroy(enumerator_r);
        !           734:        }
        !           735:        enumerator_i->destroy(enumerator_i);
        !           736: 
        !           737:        print_checklist(checklist);
        !           738: 
        !           739:        prune_pairs(checklist->pairs);
        !           740: }
        !           741: 
        !           742: /**
        !           743:  * Processes the payloads of a connectivity check and returns the extracted data
        !           744:  */
        !           745: static status_t process_payloads(message_t *message, check_t *check)
        !           746: {
        !           747:        enumerator_t *enumerator;
        !           748:        payload_t *payload;
        !           749: 
        !           750:        enumerator = message->create_payload_enumerator(message);
        !           751:        while (enumerator->enumerate(enumerator, &payload))
        !           752:        {
        !           753:                if (payload->get_type(payload) != PLV2_NOTIFY)
        !           754:                {
        !           755:                        DBG1(DBG_IKE, "ignoring payload of type '%N' while processing "
        !           756:                                 "connectivity check", payload_type_names,
        !           757:                                 payload->get_type(payload));
        !           758:                        continue;
        !           759:                }
        !           760: 
        !           761:                notify_payload_t *notify = (notify_payload_t*)payload;
        !           762: 
        !           763:                switch (notify->get_notify_type(notify))
        !           764:                {
        !           765:                        case ME_ENDPOINT:
        !           766:                        {
        !           767:                                if (check->endpoint)
        !           768:                                {
        !           769:                                        DBG1(DBG_IKE, "connectivity check contains multiple "
        !           770:                                                 "ME_ENDPOINT notifies");
        !           771:                                        break;
        !           772:                                }
        !           773: 
        !           774:                                endpoint_notify_t *endpoint = endpoint_notify_create_from_payload(notify);
        !           775:                                if (!endpoint)
        !           776:                                {
        !           777:                                        DBG1(DBG_IKE, "received invalid ME_ENDPOINT notify");
        !           778:                                        break;
        !           779:                                }
        !           780:                                check->endpoint = endpoint;
        !           781:                                check->endpoint_raw = chunk_clone(notify->get_notification_data(notify));
        !           782:                                DBG2(DBG_IKE, "received ME_ENDPOINT notify");
        !           783:                                break;
        !           784:                        }
        !           785:                        case ME_CONNECTID:
        !           786:                        {
        !           787:                                if (check->connect_id.ptr)
        !           788:                                {
        !           789:                                        DBG1(DBG_IKE, "connectivity check contains multiple "
        !           790:                                                 "ME_CONNECTID notifies");
        !           791:                                        break;
        !           792:                                }
        !           793:                                check->connect_id = chunk_clone(notify->get_notification_data(notify));
        !           794:                                DBG2(DBG_IKE, "received ME_CONNECTID %#B", &check->connect_id);
        !           795:                                break;
        !           796:                        }
        !           797:                        case ME_CONNECTAUTH:
        !           798:                        {
        !           799:                                if (check->auth.ptr)
        !           800:                                {
        !           801:                                        DBG1(DBG_IKE, "connectivity check contains multiple "
        !           802:                                                 "ME_CONNECTAUTH notifies");
        !           803:                                        break;
        !           804:                                }
        !           805:                                check->auth = chunk_clone(notify->get_notification_data(notify));
        !           806:                                DBG2(DBG_IKE, "received ME_CONNECTAUTH %#B", &check->auth);
        !           807:                                break;
        !           808:                        }
        !           809:                        default:
        !           810:                                break;
        !           811:                }
        !           812:        }
        !           813:        enumerator->destroy(enumerator);
        !           814: 
        !           815:        if (!check->connect_id.ptr || !check->endpoint || !check->auth.ptr)
        !           816:        {
        !           817:                DBG1(DBG_IKE, "at least one required payload was missing from the "
        !           818:                         "connectivity check");
        !           819:                return FAILED;
        !           820:        }
        !           821: 
        !           822:        return SUCCESS;
        !           823: }
        !           824: 
        !           825: /**
        !           826:  * Builds the signature for a connectivity check
        !           827:  */
        !           828: static chunk_t build_signature(private_connect_manager_t *this,
        !           829:                check_list_t *checklist, check_t *check, bool outbound)
        !           830: {
        !           831:        uint32_t mid;
        !           832:        chunk_t mid_chunk, key_chunk, sig_chunk;
        !           833:        chunk_t sig_hash;
        !           834: 
        !           835:        mid = htonl(check->mid);
        !           836:        mid_chunk = chunk_from_thing(mid);
        !           837: 
        !           838:        key_chunk = (checklist->is_initiator && outbound) || (!checklist->is_initiator && !outbound)
        !           839:                                        ? checklist->initiator.key : checklist->responder.key;
        !           840: 
        !           841:        /* signature = SHA1( MID | ME_CONNECTID | ME_ENDPOINT | ME_CONNECTKEY ) */
        !           842:        sig_chunk = chunk_cat("cccc", mid_chunk, check->connect_id,
        !           843:                                                  check->endpoint_raw, key_chunk);
        !           844:        if (!this->hasher->allocate_hash(this->hasher, sig_chunk, &sig_hash))
        !           845:        {
        !           846:                sig_hash = chunk_empty;
        !           847:        }
        !           848:        DBG3(DBG_IKE, "sig_chunk %#B", &sig_chunk);
        !           849:        DBG3(DBG_IKE, "sig_hash %#B", &sig_hash);
        !           850: 
        !           851:        chunk_free(&sig_chunk);
        !           852:        return sig_hash;
        !           853: }
        !           854: 
        !           855: static void queue_retransmission(private_connect_manager_t *this, check_list_t *checklist, endpoint_pair_t *pair);
        !           856: static void schedule_checks(private_connect_manager_t *this, check_list_t *checklist, uint32_t time);
        !           857: static void finish_checks(private_connect_manager_t *this, check_list_t *checklist);
        !           858: 
        !           859: /**
        !           860:  * After one of the initiator's pairs has succeeded we finish the checks without
        !           861:  * waiting for all the timeouts
        !           862:  */
        !           863: static job_requeue_t initiator_finish(callback_data_t *data)
        !           864: {
        !           865:        private_connect_manager_t *this = data->connect_manager;
        !           866: 
        !           867:        this->mutex->lock(this->mutex);
        !           868: 
        !           869:        check_list_t *checklist;
        !           870:        if (!get_checklist_by_id(this, data->connect_id, &checklist))
        !           871:        {
        !           872:                DBG1(DBG_IKE, "checklist with id '%#B' not found, can't finish "
        !           873:                         "connectivity checks", &data->connect_id);
        !           874:                this->mutex->unlock(this->mutex);
        !           875:                return JOB_REQUEUE_NONE;
        !           876:        }
        !           877: 
        !           878:        finish_checks(this, checklist);
        !           879: 
        !           880:        this->mutex->unlock(this->mutex);
        !           881: 
        !           882:        return JOB_REQUEUE_NONE;
        !           883: }
        !           884: 
        !           885: /**
        !           886:  * Updates the state of the whole checklist
        !           887:  */
        !           888: static void update_checklist_state(private_connect_manager_t *this,
        !           889:                                                                   check_list_t *checklist)
        !           890: {
        !           891:        enumerator_t *enumerator;
        !           892:        endpoint_pair_t *current;
        !           893:        bool in_progress = FALSE, succeeded = FALSE;
        !           894: 
        !           895:        enumerator = checklist->pairs->create_enumerator(checklist->pairs);
        !           896:        while (enumerator->enumerate(enumerator, (void**)&current))
        !           897:        {
        !           898:                switch(current->state)
        !           899:                {
        !           900:                        case CHECK_WAITING:
        !           901:                                /* at least one is still waiting -> checklist remains
        !           902:                                 * in waiting state */
        !           903:                                enumerator->destroy(enumerator);
        !           904:                                return;
        !           905:                        case CHECK_IN_PROGRESS:
        !           906:                                in_progress = TRUE;
        !           907:                                break;
        !           908:                        case CHECK_SUCCEEDED:
        !           909:                                succeeded = TRUE;
        !           910:                                break;
        !           911:                        default:
        !           912:                                break;
        !           913:                }
        !           914:        }
        !           915:        enumerator->destroy(enumerator);
        !           916: 
        !           917:        if (checklist->is_initiator && succeeded && !checklist->is_finishing)
        !           918:        {
        !           919:                /* instead of waiting until all checks have finished (i.e. all
        !           920:                 * retransmissions have failed) the initiator finishes the checks
        !           921:                 * right after the first check has succeeded. to allow a probably
        !           922:                 * better pair to succeed, we still wait a certain time */
        !           923:                DBG2(DBG_IKE, "fast finishing checks for checklist '%#B'",
        !           924:                         &checklist->connect_id);
        !           925: 
        !           926:                callback_data_t *data = callback_data_create(this, checklist->connect_id);
        !           927:                lib->scheduler->schedule_job_ms(lib->scheduler,
        !           928:                                (job_t*)callback_job_create((callback_job_cb_t)initiator_finish,
        !           929:                                        data, (callback_job_cleanup_t)callback_data_destroy, NULL),
        !           930:                                ME_WAIT_TO_FINISH);
        !           931:                checklist->is_finishing = TRUE;
        !           932:        }
        !           933: 
        !           934:        if (in_progress)
        !           935:        {
        !           936:                checklist->state = CHECK_IN_PROGRESS;
        !           937:        }
        !           938:        else if (succeeded)
        !           939:        {
        !           940:                checklist->state = CHECK_SUCCEEDED;
        !           941:        }
        !           942:        else
        !           943:        {
        !           944:                checklist->state = CHECK_FAILED;
        !           945:        }
        !           946: }
        !           947: 
        !           948: /**
        !           949:  * This function is triggered for each sent check after a specific timeout
        !           950:  */
        !           951: static job_requeue_t retransmit(callback_data_t *data)
        !           952: {
        !           953:        private_connect_manager_t *this = data->connect_manager;
        !           954: 
        !           955:        this->mutex->lock(this->mutex);
        !           956: 
        !           957:        check_list_t *checklist;
        !           958:        if (!get_checklist_by_id(this, data->connect_id, &checklist))
        !           959:        {
        !           960:                DBG1(DBG_IKE, "checklist with id '%#B' not found, can't retransmit "
        !           961:                         "connectivity check", &data->connect_id);
        !           962:                this->mutex->unlock(this->mutex);
        !           963:                return JOB_REQUEUE_NONE;
        !           964:        }
        !           965: 
        !           966:        endpoint_pair_t *pair;
        !           967:        if (!get_pair_by_id(checklist, data->mid, &pair))
        !           968:        {
        !           969:                DBG1(DBG_IKE, "pair with id '%d' not found, can't retransmit "
        !           970:                         "connectivity check", data->mid);
        !           971:                goto retransmit_end;
        !           972:        }
        !           973: 
        !           974:        if (pair->state != CHECK_IN_PROGRESS)
        !           975:        {
        !           976:                DBG2(DBG_IKE, "pair with id '%d' is in wrong state [%d], don't "
        !           977:                         "retransmit the connectivity check", data->mid, pair->state);
        !           978:                goto retransmit_end;
        !           979:        }
        !           980: 
        !           981:        if (++pair->retransmitted > ME_MAX_RETRANS)
        !           982:        {
        !           983:                DBG2(DBG_IKE, "pair with id '%d' failed after %d retransmissions",
        !           984:                         data->mid, ME_MAX_RETRANS);
        !           985:                pair->state = CHECK_FAILED;
        !           986:                goto retransmit_end;
        !           987:        }
        !           988: 
        !           989:        charon->sender->send(charon->sender, pair->packet->clone(pair->packet));
        !           990: 
        !           991:        queue_retransmission(this, checklist, pair);
        !           992: 
        !           993: retransmit_end:
        !           994:        update_checklist_state(this, checklist);
        !           995: 
        !           996:        switch(checklist->state)
        !           997:        {
        !           998:                case CHECK_SUCCEEDED:
        !           999:                case CHECK_FAILED:
        !          1000:                        finish_checks(this, checklist);
        !          1001:                        break;
        !          1002:                default:
        !          1003:                        break;
        !          1004:        }
        !          1005: 
        !          1006:        this->mutex->unlock(this->mutex);
        !          1007: 
        !          1008:        /* we reschedule it manually */
        !          1009:        return JOB_REQUEUE_NONE;
        !          1010: }
        !          1011: 
        !          1012: /**
        !          1013:  * Queues a retransmission job
        !          1014:  */
        !          1015: static void queue_retransmission(private_connect_manager_t *this, check_list_t *checklist, endpoint_pair_t *pair)
        !          1016: {
        !          1017:        callback_data_t *data;
        !          1018:        job_t *job;
        !          1019: 
        !          1020:        data = retransmit_data_create(this, checklist->connect_id, pair->id);
        !          1021:        job = (job_t*)callback_job_create((callback_job_cb_t)retransmit, data,
        !          1022:                                                (callback_job_cleanup_t)callback_data_destroy, NULL);
        !          1023: 
        !          1024:        uint32_t retransmission = pair->retransmitted + 1;
        !          1025:        uint32_t rto = ME_INTERVAL;
        !          1026:        if (retransmission > ME_BOOST)
        !          1027:        {
        !          1028:                rto = (uint32_t)(ME_INTERVAL * pow(ME_RETRANS_BASE, retransmission - ME_BOOST));
        !          1029:        }
        !          1030:        DBG2(DBG_IKE, "scheduling retransmission %d of pair '%d' in %dms",
        !          1031:                 retransmission, pair->id, rto);
        !          1032: 
        !          1033:        lib->scheduler->schedule_job_ms(lib->scheduler, (job_t*)job, rto);
        !          1034: }
        !          1035: 
        !          1036: /**
        !          1037:  * Sends a check
        !          1038:  */
        !          1039: static void send_check(private_connect_manager_t *this, check_list_t *checklist,
        !          1040:                check_t *check, endpoint_pair_t *pair, bool request)
        !          1041: {
        !          1042:        message_t *message = message_create(IKEV2_MAJOR_VERSION, IKEV2_MINOR_VERSION);
        !          1043:        message->set_message_id(message, check->mid);
        !          1044:        message->set_exchange_type(message, INFORMATIONAL);
        !          1045:        message->set_request(message, request);
        !          1046:        message->set_destination(message, check->dst->clone(check->dst));
        !          1047:        message->set_source(message, check->src->clone(check->src));
        !          1048: 
        !          1049:        ike_sa_id_t *ike_sa_id = ike_sa_id_create(IKEV2_MAJOR_VERSION, 0, 0,
        !          1050:                                                                                          request);
        !          1051:        message->set_ike_sa_id(message, ike_sa_id);
        !          1052:        ike_sa_id->destroy(ike_sa_id);
        !          1053: 
        !          1054:        message->add_notify(message, FALSE, ME_CONNECTID, check->connect_id);
        !          1055:        DBG2(DBG_IKE, "send ME_CONNECTID %#B", &check->connect_id);
        !          1056: 
        !          1057:        notify_payload_t *endpoint = check->endpoint->build_notify(check->endpoint);
        !          1058:        check->endpoint_raw = chunk_clone(endpoint->get_notification_data(endpoint));
        !          1059:        message->add_payload(message, (payload_t*)endpoint);
        !          1060:        DBG2(DBG_IKE, "send ME_ENDPOINT notify");
        !          1061: 
        !          1062:        check->auth = build_signature(this, checklist, check, TRUE);
        !          1063:        message->add_notify(message, FALSE, ME_CONNECTAUTH, check->auth);
        !          1064:        DBG2(DBG_IKE, "send ME_CONNECTAUTH %#B", &check->auth);
        !          1065: 
        !          1066:        packet_t *packet;
        !          1067:        if (message->generate(message, NULL, &packet) == SUCCESS)
        !          1068:        {
        !          1069:                charon->sender->send(charon->sender, packet->clone(packet));
        !          1070: 
        !          1071:                if (request)
        !          1072:                {
        !          1073:                        DESTROY_IF(pair->packet);
        !          1074:                        pair->packet = packet;
        !          1075:                        pair->retransmitted = 0;
        !          1076:                        queue_retransmission(this, checklist, pair);
        !          1077:                }
        !          1078:                else
        !          1079:                {
        !          1080:                        packet->destroy(packet);
        !          1081:                }
        !          1082:        }
        !          1083:        message->destroy(message);
        !          1084: }
        !          1085: 
        !          1086: /**
        !          1087:  * Queues a triggered check
        !          1088:  */
        !          1089: static void queue_triggered_check(private_connect_manager_t *this,
        !          1090:                check_list_t *checklist, endpoint_pair_t *pair)
        !          1091: {
        !          1092:        DBG2(DBG_IKE, "queueing triggered check for pair '%d'", pair->id);
        !          1093:        pair->state = CHECK_WAITING;
        !          1094:        checklist->triggered->insert_last(checklist->triggered, pair);
        !          1095: 
        !          1096:        if (!checklist->sender)
        !          1097:        {
        !          1098:                /* if the sender is not running we restart it */
        !          1099:                schedule_checks(this, checklist, ME_INTERVAL);
        !          1100:        }
        !          1101: }
        !          1102: 
        !          1103: /**
        !          1104:  * This function is triggered for each checklist at a specific interval
        !          1105:  */
        !          1106: static job_requeue_t sender(callback_data_t *data)
        !          1107: {
        !          1108:        private_connect_manager_t *this = data->connect_manager;
        !          1109: 
        !          1110:        this->mutex->lock(this->mutex);
        !          1111: 
        !          1112:        check_list_t *checklist;
        !          1113:        if (!get_checklist_by_id(this, data->connect_id, &checklist))
        !          1114:        {
        !          1115:                DBG1(DBG_IKE, "checklist with id '%#B' not found, can't send "
        !          1116:                         "connectivity check", &data->connect_id);
        !          1117:                this->mutex->unlock(this->mutex);
        !          1118:                return JOB_REQUEUE_NONE;
        !          1119:        }
        !          1120: 
        !          1121:        /* reset the sender */
        !          1122:        checklist->sender = NULL;
        !          1123: 
        !          1124:        endpoint_pair_t *pair;
        !          1125:        if (get_triggered_pair(checklist, &pair) != SUCCESS)
        !          1126:        {
        !          1127:                DBG1(DBG_IKE, "no triggered check queued, sending an ordinary check");
        !          1128: 
        !          1129:                if (!checklist->pairs->find_first(checklist->pairs, match_waiting_pair,
        !          1130:                                                                                  (void**)&pair))
        !          1131:                {
        !          1132:                        this->mutex->unlock(this->mutex);
        !          1133:                        DBG1(DBG_IKE, "no pairs in waiting state, aborting");
        !          1134:                        return JOB_REQUEUE_NONE;
        !          1135:                }
        !          1136:        }
        !          1137:        else
        !          1138:        {
        !          1139:                DBG1(DBG_IKE, "triggered check found");
        !          1140:        }
        !          1141: 
        !          1142:        check_t *check = check_create();
        !          1143:        check->mid = pair->id;
        !          1144:        check->src = pair->local->clone(pair->local);
        !          1145:        check->dst = pair->remote->clone(pair->remote);
        !          1146:        check->connect_id = chunk_clone(checklist->connect_id);
        !          1147:        check->endpoint = endpoint_notify_create_from_host(PEER_REFLEXIVE, NULL,
        !          1148:                                                                                                           NULL);
        !          1149: 
        !          1150:        pair->state = CHECK_IN_PROGRESS;
        !          1151: 
        !          1152:        send_check(this, checklist, check, pair, TRUE);
        !          1153: 
        !          1154:        check_destroy(check);
        !          1155: 
        !          1156:        /* schedule this job again */
        !          1157:        schedule_checks(this, checklist, ME_INTERVAL);
        !          1158: 
        !          1159:        this->mutex->unlock(this->mutex);
        !          1160: 
        !          1161:        /* we reschedule it manually */
        !          1162:        return JOB_REQUEUE_NONE;
        !          1163: }
        !          1164: 
        !          1165: /**
        !          1166:  * Schedules checks for a checklist (time in ms)
        !          1167:  */
        !          1168: static void schedule_checks(private_connect_manager_t *this,
        !          1169:                                                        check_list_t *checklist, uint32_t time)
        !          1170: {
        !          1171:        callback_data_t *data = callback_data_create(this, checklist->connect_id);
        !          1172:        checklist->sender = (job_t*)callback_job_create((callback_job_cb_t)sender,
        !          1173:                                        data, (callback_job_cleanup_t)callback_data_destroy, NULL);
        !          1174:        lib->scheduler->schedule_job_ms(lib->scheduler, checklist->sender, time);
        !          1175: }
        !          1176: 
        !          1177: /**
        !          1178:  * Initiates waiting mediated connections
        !          1179:  */
        !          1180: static job_requeue_t initiate_mediated(initiate_data_t *data)
        !          1181: {
        !          1182:        check_list_t *checklist = data->checklist;
        !          1183:        initiated_t *initiated = data->initiated;
        !          1184: 
        !          1185:        endpoint_pair_t *pair;
        !          1186:        if (get_best_valid_pair(checklist, &pair))
        !          1187:        {
        !          1188:                ike_sa_id_t *waiting_sa;
        !          1189:                enumerator_t *enumerator = initiated->mediated->create_enumerator(
        !          1190:                                                                                                                initiated->mediated);
        !          1191:                while (enumerator->enumerate(enumerator, (void**)&waiting_sa))
        !          1192:                {
        !          1193:                        ike_sa_t *sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, waiting_sa);
        !          1194:                        if (sa->initiate_mediated(sa, pair->local, pair->remote, checklist->connect_id) != SUCCESS)
        !          1195:                        {
        !          1196:                                DBG1(DBG_IKE, "establishing mediated connection failed");
        !          1197:                                charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, sa);
        !          1198:                        }
        !          1199:                        else
        !          1200:                        {
        !          1201:                                charon->ike_sa_manager->checkin(charon->ike_sa_manager, sa);
        !          1202:                        }
        !          1203:                }
        !          1204:                enumerator->destroy(enumerator);
        !          1205:        }
        !          1206:        else
        !          1207:        {
        !          1208:                /* this should (can?) not happen */
        !          1209:        }
        !          1210: 
        !          1211:        return JOB_REQUEUE_NONE;
        !          1212: }
        !          1213: 
        !          1214: /**
        !          1215:  * Finishes checks for a checklist
        !          1216:  */
        !          1217: static void finish_checks(private_connect_manager_t *this, check_list_t *checklist)
        !          1218: {
        !          1219:        if (checklist->is_initiator)
        !          1220:        {
        !          1221:                initiated_t *initiated;
        !          1222:                if (get_initiated_by_ids(this, checklist->initiator.id,
        !          1223:                                                                 checklist->responder.id, &initiated))
        !          1224:                {
        !          1225:                        callback_job_t *job;
        !          1226: 
        !          1227:                        remove_checklist(this, checklist);
        !          1228:                        remove_initiated(this, initiated);
        !          1229: 
        !          1230:                        initiate_data_t *data = initiate_data_create(checklist, initiated);
        !          1231:                        job = callback_job_create((callback_job_cb_t)initiate_mediated,
        !          1232:                                        data, (callback_job_cleanup_t)initiate_data_destroy, NULL);
        !          1233:                        lib->processor->queue_job(lib->processor, (job_t*)job);
        !          1234:                        return;
        !          1235:                }
        !          1236:                else
        !          1237:                {
        !          1238:                        DBG1(DBG_IKE, "there is no mediated connection waiting between '%Y'"
        !          1239:                                 " and '%Y'", checklist->initiator.id, checklist->responder.id);
        !          1240:                }
        !          1241:        }
        !          1242: }
        !          1243: 
        !          1244: /**
        !          1245:  * Process the response to one of our requests
        !          1246:  */
        !          1247: static void process_response(private_connect_manager_t *this, check_t *check,
        !          1248:                check_list_t *checklist)
        !          1249: {
        !          1250:        endpoint_pair_t *pair;
        !          1251:        if (get_pair_by_id(checklist, check->mid, &pair))
        !          1252:        {
        !          1253:                if (pair->local->equals(pair->local, check->dst) &&
        !          1254:                        pair->remote->equals(pair->remote, check->src))
        !          1255:                {
        !          1256:                        DBG1(DBG_IKE, "endpoint pair '%d' is valid: '%#H' - '%#H'",
        !          1257:                                 pair->id, pair->local, pair->remote);
        !          1258:                        pair->state = CHECK_SUCCEEDED;
        !          1259:                }
        !          1260: 
        !          1261:                linked_list_t *local_endpoints = checklist->is_initiator ?
        !          1262:                        checklist->initiator.endpoints : checklist->responder.endpoints;
        !          1263: 
        !          1264:                endpoint_notify_t *local_endpoint;
        !          1265:                if (!endpoints_contain(local_endpoints,
        !          1266:                                                           check->endpoint->get_host(check->endpoint),
        !          1267:                                                           &local_endpoint))
        !          1268:                {
        !          1269:                        local_endpoint = endpoint_notify_create_from_host(PEER_REFLEXIVE,
        !          1270:                                        check->endpoint->get_host(check->endpoint), pair->local);
        !          1271:                        local_endpoint->set_priority(local_endpoint,
        !          1272:                                                                check->endpoint->get_priority(check->endpoint));
        !          1273:                        local_endpoints->insert_last(local_endpoints, local_endpoint);
        !          1274:                }
        !          1275: 
        !          1276:                update_checklist_state(this, checklist);
        !          1277: 
        !          1278:                switch(checklist->state)
        !          1279:                {
        !          1280:                        case CHECK_SUCCEEDED:
        !          1281:                        case CHECK_FAILED:
        !          1282:                                finish_checks(this, checklist);
        !          1283:                                break;
        !          1284:                        default:
        !          1285:                                break;
        !          1286:                }
        !          1287:        }
        !          1288:        else
        !          1289:        {
        !          1290:                DBG1(DBG_IKE, "pair with id '%d' not found", check->mid);
        !          1291:        }
        !          1292: }
        !          1293: 
        !          1294: static void process_request(private_connect_manager_t *this, check_t *check,
        !          1295:                                                        check_list_t *checklist)
        !          1296: {
        !          1297:        linked_list_t *remote_endpoints = checklist->is_initiator ?
        !          1298:                                checklist->responder.endpoints : checklist->initiator.endpoints;
        !          1299: 
        !          1300:        endpoint_notify_t *peer_reflexive, *remote_endpoint;
        !          1301:        peer_reflexive = endpoint_notify_create_from_host(PEER_REFLEXIVE,
        !          1302:                                                                                                          check->src, NULL);
        !          1303:        peer_reflexive->set_priority(peer_reflexive,
        !          1304:                                                        check->endpoint->get_priority(check->endpoint));
        !          1305: 
        !          1306:        if (!endpoints_contain(remote_endpoints, check->src, &remote_endpoint))
        !          1307:        {
        !          1308:                remote_endpoint = peer_reflexive->clone(peer_reflexive);
        !          1309:                remote_endpoints->insert_last(remote_endpoints, remote_endpoint);
        !          1310:        }
        !          1311: 
        !          1312:        endpoint_pair_t *pair;
        !          1313:        if (get_pair_by_hosts(checklist->pairs, check->dst, check->src, &pair))
        !          1314:        {
        !          1315:                switch(pair->state)
        !          1316:                {
        !          1317:                        case CHECK_IN_PROGRESS:
        !          1318:                                /* prevent retransmissions */
        !          1319:                                pair->retransmitted = ME_MAX_RETRANS;
        !          1320:                                /* FIXME: we should wait to the next rto to send the triggered
        !          1321:                                 * check */
        !          1322:                                /* fall-through */
        !          1323:                        case CHECK_WAITING:
        !          1324:                        case CHECK_FAILED:
        !          1325:                                queue_triggered_check(this, checklist, pair);
        !          1326:                                break;
        !          1327:                        case CHECK_SUCCEEDED:
        !          1328:                        default:
        !          1329:                                break;
        !          1330:                }
        !          1331:        }
        !          1332:        else
        !          1333:        {
        !          1334:                endpoint_notify_t *local_endpoint = endpoint_notify_create_from_host(HOST, check->dst, NULL);
        !          1335: 
        !          1336:                endpoint_notify_t *initiator = checklist->is_initiator ? local_endpoint : remote_endpoint;
        !          1337:                endpoint_notify_t *responder = checklist->is_initiator ? remote_endpoint : local_endpoint;
        !          1338: 
        !          1339:                pair = endpoint_pair_create(initiator, responder, checklist->is_initiator);
        !          1340:                pair->id = checklist->pairs->get_count(checklist->pairs) + 1;
        !          1341: 
        !          1342:                insert_pair_by_priority(checklist->pairs, pair);
        !          1343: 
        !          1344:                queue_triggered_check(this, checklist, pair);
        !          1345: 
        !          1346:                local_endpoint->destroy(local_endpoint);
        !          1347:        }
        !          1348: 
        !          1349:        check_t *response = check_create();
        !          1350: 
        !          1351:        response->mid = check->mid;
        !          1352:        response->src = check->dst->clone(check->dst);
        !          1353:        response->dst = check->src->clone(check->src);
        !          1354:        response->connect_id = chunk_clone(check->connect_id);
        !          1355:        response->endpoint = peer_reflexive;
        !          1356: 
        !          1357:        send_check(this, checklist, response, pair, FALSE);
        !          1358: 
        !          1359:        check_destroy(response);
        !          1360: }
        !          1361: 
        !          1362: METHOD(connect_manager_t, process_check, void,
        !          1363:        private_connect_manager_t *this, message_t *message)
        !          1364: {
        !          1365:        if (message->parse_body(message, NULL) != SUCCESS)
        !          1366:        {
        !          1367:                DBG1(DBG_IKE, "%N %s with message ID %d processing failed",
        !          1368:                         exchange_type_names, message->get_exchange_type(message),
        !          1369:                         message->get_request(message) ? "request" : "response",
        !          1370:                         message->get_message_id(message));
        !          1371:                return;
        !          1372:        }
        !          1373: 
        !          1374:        check_t *check = check_create();
        !          1375:        check->mid = message->get_message_id(message);
        !          1376:        check->src = message->get_source(message);
        !          1377:        check->src = check->src->clone(check->src);
        !          1378:        check->dst = message->get_destination(message);
        !          1379:        check->dst = check->dst->clone(check->dst);
        !          1380: 
        !          1381:        if (process_payloads(message, check) != SUCCESS)
        !          1382:        {
        !          1383:                DBG1(DBG_IKE, "invalid connectivity check %s received",
        !          1384:                         message->get_request(message) ? "request" : "response");
        !          1385:                check_destroy(check);
        !          1386:                return;
        !          1387:        }
        !          1388: 
        !          1389:        this->mutex->lock(this->mutex);
        !          1390: 
        !          1391:        check_list_t *checklist;
        !          1392:        if (!get_checklist_by_id(this, check->connect_id, &checklist))
        !          1393:        {
        !          1394:                DBG1(DBG_IKE, "checklist with id '%#B' not found",
        !          1395:                         &check->connect_id);
        !          1396:                check_destroy(check);
        !          1397:                this->mutex->unlock(this->mutex);
        !          1398:                return;
        !          1399:        }
        !          1400: 
        !          1401:        chunk_t sig = build_signature(this, checklist, check, FALSE);
        !          1402:        if (!chunk_equals(sig, check->auth))
        !          1403:        {
        !          1404:                DBG1(DBG_IKE, "connectivity check verification failed");
        !          1405:                check_destroy(check);
        !          1406:                chunk_free(&sig);
        !          1407:                this->mutex->unlock(this->mutex);
        !          1408:                return;
        !          1409:        }
        !          1410:        chunk_free(&sig);
        !          1411: 
        !          1412:        if (message->get_request(message))
        !          1413:        {
        !          1414:                process_request(this, check, checklist);
        !          1415:        }
        !          1416:        else
        !          1417:        {
        !          1418:                process_response(this, check, checklist);
        !          1419:        }
        !          1420: 
        !          1421:        this->mutex->unlock(this->mutex);
        !          1422: 
        !          1423:        check_destroy(check);
        !          1424: }
        !          1425: 
        !          1426: CALLBACK(id_matches, bool,
        !          1427:        ike_sa_id_t *a, va_list args)
        !          1428: {
        !          1429:        ike_sa_id_t *b;
        !          1430: 
        !          1431:        VA_ARGS_VGET(args, b);
        !          1432:        return a->equals(a, b);
        !          1433: }
        !          1434: 
        !          1435: METHOD(connect_manager_t, check_and_register, bool,
        !          1436:        private_connect_manager_t *this, identification_t *id,
        !          1437:        identification_t *peer_id, ike_sa_id_t *mediated_sa)
        !          1438: {
        !          1439:        initiated_t *initiated;
        !          1440:        bool already_there = TRUE;
        !          1441: 
        !          1442:        this->mutex->lock(this->mutex);
        !          1443: 
        !          1444:        if (!get_initiated_by_ids(this, id, peer_id, &initiated))
        !          1445:        {
        !          1446:                DBG2(DBG_IKE, "registered waiting mediated connection with '%Y'",
        !          1447:                         peer_id);
        !          1448:                initiated = initiated_create(id, peer_id);
        !          1449:                this->initiated->insert_last(this->initiated, initiated);
        !          1450:                already_there = FALSE;
        !          1451:        }
        !          1452: 
        !          1453:        if (!initiated->mediated->find_first(initiated->mediated, id_matches,
        !          1454:                                                                                 NULL, mediated_sa))
        !          1455:        {
        !          1456:                initiated->mediated->insert_last(initiated->mediated,
        !          1457:                                                                                 mediated_sa->clone(mediated_sa));
        !          1458:        }
        !          1459: 
        !          1460:        this->mutex->unlock(this->mutex);
        !          1461: 
        !          1462:        return already_there;
        !          1463: }
        !          1464: 
        !          1465: METHOD(connect_manager_t, check_and_initiate, void,
        !          1466:        private_connect_manager_t *this, ike_sa_id_t *mediation_sa,
        !          1467:        identification_t *id, identification_t *peer_id)
        !          1468: {
        !          1469:        initiated_t *initiated;
        !          1470: 
        !          1471:        this->mutex->lock(this->mutex);
        !          1472: 
        !          1473:        if (!get_initiated_by_ids(this, id, peer_id, &initiated))
        !          1474:        {
        !          1475:                DBG2(DBG_IKE, "no waiting mediated connections with '%Y'", peer_id);
        !          1476:                this->mutex->unlock(this->mutex);
        !          1477:                return;
        !          1478:        }
        !          1479: 
        !          1480:        ike_sa_id_t *waiting_sa;
        !          1481:        enumerator_t *enumerator = initiated->mediated->create_enumerator(
        !          1482:                                                                                                                initiated->mediated);
        !          1483:        while (enumerator->enumerate(enumerator, (void**)&waiting_sa))
        !          1484:        {
        !          1485:                job_t *job = (job_t*)reinitiate_mediation_job_create(mediation_sa,
        !          1486:                                                                                                                         waiting_sa);
        !          1487:                lib->processor->queue_job(lib->processor, job);
        !          1488:        }
        !          1489:        enumerator->destroy(enumerator);
        !          1490: 
        !          1491:        this->mutex->unlock(this->mutex);
        !          1492: }
        !          1493: 
        !          1494: METHOD(connect_manager_t, set_initiator_data, status_t,
        !          1495:        private_connect_manager_t *this, identification_t *initiator,
        !          1496:        identification_t *responder, chunk_t connect_id, chunk_t key,
        !          1497:        linked_list_t *endpoints, bool is_initiator)
        !          1498: {
        !          1499:        check_list_t *checklist;
        !          1500: 
        !          1501:        this->mutex->lock(this->mutex);
        !          1502: 
        !          1503:        if (get_checklist_by_id(this, connect_id, NULL))
        !          1504:        {
        !          1505:                DBG1(DBG_IKE, "checklist with id '%#B' already exists, aborting",
        !          1506:                         &connect_id);
        !          1507:                this->mutex->unlock(this->mutex);
        !          1508:                return FAILED;
        !          1509:        }
        !          1510: 
        !          1511:        checklist = check_list_create(initiator, responder, connect_id, key,
        !          1512:                                                                  endpoints, is_initiator);
        !          1513:        this->checklists->insert_last(this->checklists, checklist);
        !          1514: 
        !          1515:        this->mutex->unlock(this->mutex);
        !          1516: 
        !          1517:        return SUCCESS;
        !          1518: }
        !          1519: 
        !          1520: METHOD(connect_manager_t, set_responder_data, status_t,
        !          1521:        private_connect_manager_t *this, chunk_t connect_id, chunk_t key,
        !          1522:        linked_list_t *endpoints)
        !          1523: {
        !          1524:        check_list_t *checklist;
        !          1525: 
        !          1526:        this->mutex->lock(this->mutex);
        !          1527: 
        !          1528:        if (!get_checklist_by_id(this, connect_id, &checklist))
        !          1529:        {
        !          1530:                DBG1(DBG_IKE, "checklist with id '%#B' not found",
        !          1531:                         &connect_id);
        !          1532:                this->mutex->unlock(this->mutex);
        !          1533:                return NOT_FOUND;
        !          1534:        }
        !          1535: 
        !          1536:        checklist->responder.key = chunk_clone(key);
        !          1537:        checklist->responder.endpoints = endpoints->clone_offset(endpoints,
        !          1538:                                                                                        offsetof(endpoint_notify_t, clone));
        !          1539:        checklist->state = CHECK_WAITING;
        !          1540: 
        !          1541:        build_pairs(checklist);
        !          1542: 
        !          1543:        /* send the first check immediately */
        !          1544:        schedule_checks(this, checklist, 0);
        !          1545: 
        !          1546:        this->mutex->unlock(this->mutex);
        !          1547: 
        !          1548:        return SUCCESS;
        !          1549: }
        !          1550: 
        !          1551: METHOD(connect_manager_t, stop_checks, status_t,
        !          1552:        private_connect_manager_t *this, chunk_t connect_id)
        !          1553: {
        !          1554:        check_list_t *checklist;
        !          1555: 
        !          1556:        this->mutex->lock(this->mutex);
        !          1557: 
        !          1558:        if (!get_checklist_by_id(this, connect_id, &checklist))
        !          1559:        {
        !          1560:                DBG1(DBG_IKE, "checklist with id '%#B' not found",
        !          1561:                         &connect_id);
        !          1562:                this->mutex->unlock(this->mutex);
        !          1563:                return NOT_FOUND;
        !          1564:        }
        !          1565: 
        !          1566:        DBG1(DBG_IKE, "removing checklist with id '%#B'", &connect_id);
        !          1567: 
        !          1568:        remove_checklist(this, checklist);
        !          1569:        check_list_destroy(checklist);
        !          1570: 
        !          1571:        this->mutex->unlock(this->mutex);
        !          1572: 
        !          1573:        return SUCCESS;
        !          1574: }
        !          1575: 
        !          1576: METHOD(connect_manager_t, destroy, void,
        !          1577:        private_connect_manager_t *this)
        !          1578: {
        !          1579:        this->mutex->lock(this->mutex);
        !          1580: 
        !          1581:        this->checklists->destroy_function(this->checklists,
        !          1582:                                                                           (void*)check_list_destroy);
        !          1583:        this->initiated->destroy_function(this->initiated,
        !          1584:                                                                         (void*)initiated_destroy);
        !          1585:        DESTROY_IF(this->hasher);
        !          1586: 
        !          1587:        this->mutex->unlock(this->mutex);
        !          1588:        this->mutex->destroy(this->mutex);
        !          1589:        free(this);
        !          1590: }
        !          1591: 
        !          1592: /*
        !          1593:  * Described in header.
        !          1594:  */
        !          1595: connect_manager_t *connect_manager_create()
        !          1596: {
        !          1597:        private_connect_manager_t *this;
        !          1598: 
        !          1599:        INIT(this,
        !          1600:                .public = {
        !          1601:                        .destroy = _destroy,
        !          1602:                        .check_and_register = _check_and_register,
        !          1603:                        .check_and_initiate = _check_and_initiate,
        !          1604:                        .set_initiator_data = _set_initiator_data,
        !          1605:                        .set_responder_data = _set_responder_data,
        !          1606:                        .process_check = _process_check,
        !          1607:                        .stop_checks = _stop_checks,
        !          1608:                },
        !          1609:                .hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1),
        !          1610:                .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
        !          1611:                .checklists = linked_list_create(),
        !          1612:                .initiated = linked_list_create(),
        !          1613:        );
        !          1614: 
        !          1615:        if (this->hasher == NULL)
        !          1616:        {
        !          1617:                DBG1(DBG_IKE, "unable to create connect manager, SHA1 not supported");
        !          1618:                destroy(this);
        !          1619:                return NULL;
        !          1620:        }
        !          1621: 
        !          1622:        return &this->public;
        !          1623: }

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