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**)¤t))
! 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**)¤t))
! 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**)¤t) &&
! 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**)¤t))
! 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**)¤t))
! 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**)¤t))
! 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**)¤t))
! 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>