diff options
author | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2008-07-10 12:47:56 +0000 |
---|---|---|
committer | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2008-07-10 12:47:56 +0000 |
commit | eb841c5ef668a48782ef1154fda65cb6048f5885 (patch) | |
tree | 00dd0cb4313bf2291d94ed511fe51f0b4bc7ea7a /src/charon/sa/connect_manager.c | |
parent | 738206039047924ae7e4762a53d121be1ca43000 (diff) | |
download | vyos-strongswan-eb841c5ef668a48782ef1154fda65cb6048f5885.tar.gz vyos-strongswan-eb841c5ef668a48782ef1154fda65cb6048f5885.zip |
- Updated to new upstream.
Diffstat (limited to 'src/charon/sa/connect_manager.c')
-rw-r--r-- | src/charon/sa/connect_manager.c | 607 |
1 files changed, 330 insertions, 277 deletions
diff --git a/src/charon/sa/connect_manager.c b/src/charon/sa/connect_manager.c index 06755fa9c..19ceea666 100644 --- a/src/charon/sa/connect_manager.c +++ b/src/charon/sa/connect_manager.c @@ -1,12 +1,5 @@ -/** - * @file connect_manager.c - * - * @brief Implementation of connect_manager_t. - * - */ - /* - * Copyright (C) 2007 Tobias Brunner + * Copyright (C) 2007-2008 Tobias Brunner * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -18,6 +11,8 @@ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. + * + * $Id: connect_manager.c 3792 2008-04-10 12:51:04Z tobias $ */ #include "connect_manager.h" @@ -34,13 +29,20 @@ #include <encoding/payloads/endpoint_notify.h> /* base timeout - * the sending interval is P2P_INTERVAL * active checklists (N) - * retransmission timeout is P2P_INTERVAL * N * checks in waiting state (NW) */ -#define P2P_INTERVAL 20 /* ms */ -/* min retransmission timeout (RTO is P2P_INTERVAL * N * checks in waiting state) */ -#define P2P_RTO_MIN 100 /* ms */ -/* max number of retransmissions (+ the initial check) */ -#define P2P_MAX_RETRANS 2 + * the check interval is ME_INTERVAL */ +#define ME_INTERVAL 25 /* ms */ +/* retransmission timeout is first ME_INTERVAL for ME_BOOST retransmissions + * then gets reduced to ME_INTERVAL * ME_RETRANS_BASE ^ (sent retransmissions - ME_BOOST). */ +/* number of initial retransmissions sent in short interval */ +#define ME_BOOST 2 +/* base for retransmissions */ +#define ME_RETRANS_BASE 1.8 +/* max number of retransmissions */ +#define ME_MAX_RETRANS 13 + +/* time to wait before the initiator finishes the connectivity checks after + * the first check has succeeded */ +#define ME_WAIT_TO_FINISH 1000 /* ms */ typedef struct private_connect_manager_t private_connect_manager_t; @@ -70,7 +72,7 @@ struct private_connect_manager_t { linked_list_t *initiated; /** - * Linked list with checklists (hash table with session ID as key would be better). + * Linked list with checklists (hash table with connect ID as key would be better). */ linked_list_t *checklists; }; @@ -180,8 +182,8 @@ struct check_list_t { linked_list_t *endpoints; } responder; - /** session id */ - chunk_t session_id; + /** connect id */ + chunk_t connect_id; /** list of endpoint pairs */ linked_list_t *pairs; @@ -195,6 +197,12 @@ struct check_list_t { /** TRUE if this is the initiator */ bool is_initiator; + /** TRUE if the initiator is finishing the checks */ + bool is_finishing; + + /** the current sender job */ + job_t *sender; + }; /** @@ -205,7 +213,7 @@ static void check_list_destroy(check_list_t *this) DESTROY_IF(this->initiator.id); DESTROY_IF(this->responder.id); - chunk_free(&this->session_id); + chunk_free(&this->connect_id); chunk_free(&this->initiator.key); chunk_free(&this->responder.key); @@ -223,12 +231,12 @@ static void check_list_destroy(check_list_t *this) * Creates a new checklist */ static check_list_t *check_list_create(identification_t *initiator, identification_t *responder, - chunk_t session_id, chunk_t initiator_key, linked_list_t *initiator_endpoints, + chunk_t connect_id, chunk_t initiator_key, linked_list_t *initiator_endpoints, bool is_initiator) { check_list_t *this = malloc_thing(check_list_t); - this->session_id = chunk_clone(session_id); + this->connect_id = chunk_clone(connect_id); this->initiator.id = initiator->clone(initiator); this->initiator.key = chunk_clone(initiator_key); @@ -242,43 +250,7 @@ static check_list_t *check_list_create(identification_t *initiator, identificati this->triggered = linked_list_create(); this->state = CHECK_NONE; this->is_initiator = is_initiator; - - return this; -} - - -typedef struct waiting_sa_t waiting_sa_t; - -/** - * For an initiator, the data stored about a waiting mediated sa - */ -struct waiting_sa_t { - /** ike sa id */ - ike_sa_id_t *ike_sa_id; - - /** list of child_cfg_t */ - linked_list_t *childs; -}; - -/** - * Destroys a queued mediated sa - */ -static void waiting_sa_destroy(waiting_sa_t *this) -{ - DESTROY_IF(this->ike_sa_id); - this->childs->destroy_offset(this->childs, offsetof(child_cfg_t, destroy)); - free(this); -} - -/** - * Creates a new mediated sa object - */ -static waiting_sa_t *waiting_sa_create(ike_sa_id_t *ike_sa_id) -{ - waiting_sa_t *this = malloc_thing(waiting_sa_t); - - this->ike_sa_id = ike_sa_id->clone(ike_sa_id); - this->childs = linked_list_create(); + this->is_finishing = FALSE; return this; } @@ -306,7 +278,7 @@ static void initiated_destroy(initiated_t *this) { DESTROY_IF(this->id); DESTROY_IF(this->peer_id); - this->mediated->destroy_function(this->mediated, (void*)waiting_sa_destroy); + this->mediated->destroy_offset(this->mediated, offsetof(ike_sa_id_t, destroy)); free(this); } @@ -340,8 +312,8 @@ struct check_t { /** destination of the connectivity check */ host_t *dst; - /** session id */ - chunk_t session_id; + /** connect id */ + chunk_t connect_id; /** endpoint */ endpoint_notify_t *endpoint; @@ -349,8 +321,8 @@ struct check_t { /** raw endpoint payload (to verify the signature) */ chunk_t endpoint_raw; - /** cookie */ - chunk_t cookie; + /** connect auth */ + chunk_t auth; }; /** @@ -358,9 +330,11 @@ struct check_t { */ static void check_destroy(check_t *this) { - chunk_free(&this->session_id); + chunk_free(&this->connect_id); chunk_free(&this->endpoint_raw); - chunk_free(&this->cookie); + chunk_free(&this->auth); + DESTROY_IF(this->src); + DESTROY_IF(this->dst); DESTROY_IF(this->endpoint); free(this); } @@ -372,9 +346,11 @@ static check_t *check_create() { check_t *this = malloc_thing(check_t); - this->session_id = chunk_empty; - this->cookie = chunk_empty; + this->connect_id = chunk_empty; + this->auth = chunk_empty; this->endpoint_raw = chunk_empty; + this->src = NULL; + this->dst = NULL; this->endpoint = NULL; this->mid = 0; @@ -382,76 +358,52 @@ static check_t *check_create() return this; } -typedef struct sender_data_t sender_data_t; +typedef struct callback_data_t callback_data_t; /** - * Data required by the sender + * Data required by several callback jobs used in this file */ -struct sender_data_t { +struct callback_data_t { /** connect manager */ private_connect_manager_t *connect_manager; - /** session id */ - chunk_t session_id; + /** connect id */ + chunk_t connect_id; + + /** message (pair) id */ + u_int32_t mid; }; /** - * Destroys a sender data object + * Destroys a callback data object */ -static void sender_data_destroy(sender_data_t *this) +static void callback_data_destroy(callback_data_t *this) { - chunk_free(&this->session_id); + chunk_free(&this->connect_id); free(this); } /** - * Creates a new sender data object + * Creates a new callback data object */ -static sender_data_t *sender_data_create(private_connect_manager_t *connect_manager, chunk_t session_id) +static callback_data_t *callback_data_create(private_connect_manager_t *connect_manager, + chunk_t connect_id) { - sender_data_t *this = malloc_thing(sender_data_t); + callback_data_t *this = malloc_thing(callback_data_t); this->connect_manager = connect_manager; - this->session_id = session_id; + this->connect_id = chunk_clone(connect_id); + this->mid = 0; return this; } -typedef struct retransmit_data_t retransmit_data_t; - -/** - * Data required by the retransmission job - */ -struct retransmit_data_t { - /** connect manager */ - private_connect_manager_t *connect_manager; - - /** session id */ - chunk_t session_id; - - /** message (pair) id */ - u_int32_t mid; -}; - -/** - * Destroys a retransmission data object - */ -static void retransmit_data_destroy(retransmit_data_t *this) -{ - chunk_free(&this->session_id); - free(this); -} - /** * Creates a new retransmission data object */ -static retransmit_data_t *retransmit_data_create(private_connect_manager_t *connect_manager, - chunk_t session_id, u_int32_t mid) +static callback_data_t *retransmit_data_create(private_connect_manager_t *connect_manager, + chunk_t connect_id, u_int32_t mid) { - retransmit_data_t *this = malloc_thing(retransmit_data_t); - - this->connect_manager = connect_manager; - this->session_id = session_id; + callback_data_t *this = callback_data_create(connect_manager, connect_id); this->mid = mid; - return this; } @@ -529,34 +481,19 @@ static void remove_initiated(private_connect_manager_t *this, initiated_t *initi } /** - * Finds a waiting sa + * Find the checklist with a specific connect ID */ -static bool match_waiting_sa(waiting_sa_t *current, ike_sa_id_t *ike_sa_id) +static bool match_checklist_by_id(check_list_t *current, chunk_t *connect_id) { - return ike_sa_id->equals(ike_sa_id, current->ike_sa_id); -} - -static status_t get_waiting_sa(initiated_t *initiated, ike_sa_id_t *ike_sa_id, waiting_sa_t **waiting_sa) -{ - return initiated->mediated->find_first(initiated->mediated, - (linked_list_match_t)match_waiting_sa, - (void**)waiting_sa, ike_sa_id); -} - -/** - * Find the checklist with a specific session ID - */ -static bool match_checklist_by_id(check_list_t *current, chunk_t *session_id) -{ - return chunk_equals(*session_id, current->session_id); + return chunk_equals(*connect_id, current->connect_id); } static status_t get_checklist_by_id(private_connect_manager_t *this, - chunk_t session_id, check_list_t **check_list) + chunk_t connect_id, check_list_t **check_list) { return this->checklists->find_first(this->checklists, (linked_list_match_t)match_checklist_by_id, - (void**)check_list, &session_id); + (void**)check_list, &connect_id); } /** @@ -595,51 +532,6 @@ static status_t endpoints_contain(linked_list_t *endpoints, host_t *host, endpoi } /** - * Updates the state of the whole checklist - */ -static void update_checklist_state(check_list_t *checklist) -{ - iterator_t *iterator; - endpoint_pair_t *current; - bool in_progress = FALSE, succeeded = FALSE; - - iterator = checklist->pairs->create_iterator(checklist->pairs, TRUE); - while (iterator->iterate(iterator, (void**)¤t)) - { - switch(current->state) - { - case CHECK_WAITING: - /* at least one is still waiting -> checklist remains - * in waiting state */ - iterator->destroy(iterator); - return; - case CHECK_IN_PROGRESS: - in_progress = TRUE; - break; - case CHECK_SUCCEEDED: - succeeded = TRUE; - break; - default: - break; - } - } - iterator->destroy(iterator); - - if (in_progress) - { - checklist->state = CHECK_IN_PROGRESS; - } - else if (succeeded) - { - checklist->state = CHECK_SUCCEEDED; - } - else - { - checklist->state = CHECK_FAILED; - } -} - -/** * Inserts an endpoint pair into the list of pairs ordered by priority (high to low) */ static void insert_pair_by_priority(linked_list_t *pairs, endpoint_pair_t *pair) @@ -681,14 +573,14 @@ static status_t get_pair_by_hosts(linked_list_t *pairs, host_t *local, host_t *r (void**)pair, local, remote); } -/** - * Searches for a pair with a specific id - */ static bool match_pair_by_id(endpoint_pair_t *current, u_int32_t *id) { return current->id == *id; } +/** + * Searches for a pair with a specific id + */ static status_t get_pair_by_id(check_list_t *checklist, u_int32_t id, endpoint_pair_t **pair) { return checklist->pairs->find_first(checklist->pairs, @@ -696,14 +588,14 @@ static status_t get_pair_by_id(check_list_t *checklist, u_int32_t id, endpoint_p (void**)pair, &id); } -/** - * Returns the best pair of state CHECK_SUCCEEDED from a checklist. - */ static bool match_succeeded_pair(endpoint_pair_t *current) { return current->state == CHECK_SUCCEEDED; } +/** + * Returns the best pair of state CHECK_SUCCEEDED from a checklist. + */ static status_t get_best_valid_pair(check_list_t *checklist, endpoint_pair_t **pair) { return checklist->pairs->find_first(checklist->pairs, @@ -711,14 +603,14 @@ static status_t get_best_valid_pair(check_list_t *checklist, endpoint_pair_t **p (void**)pair); } -/** - * Returns and *removes* the first triggered pair in state CHECK_WAITING. - */ static bool match_waiting_pair(endpoint_pair_t *current) { return current->state == CHECK_WAITING; } +/** + * Returns and *removes* the first triggered pair in state CHECK_WAITING. + */ static status_t get_triggered_pair(check_list_t *checklist, endpoint_pair_t **pair) { iterator_t *iterator; @@ -746,6 +638,24 @@ static status_t get_triggered_pair(check_list_t *checklist, endpoint_pair_t **pa } /** + * Prints all the pairs on a checklist + */ +static void print_checklist(check_list_t *checklist) +{ + iterator_t *iterator; + endpoint_pair_t *current; + + DBG1(DBG_IKE, "pairs on checklist %#B:", &checklist->connect_id); + iterator = checklist->pairs->create_iterator(checklist->pairs, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + DBG1(DBG_IKE, " * %#H - %#H (%d)", current->local, current->remote, + current->priority); + } + iterator->destroy(iterator); +} + +/** * Prunes identical pairs with lower priority from the list * Note: this function also numbers the remaining pairs serially */ @@ -775,7 +685,7 @@ static void prune_pairs(linked_list_t *pairs) * order, and we iterate the list from the beginning, we are * sure that the priority of 'other' is lower than that of * 'current', remove it */ - DBG1(DBG_IKE, "pruning endpoint pair %H - %H with priority %d", + DBG1(DBG_IKE, "pruning endpoint pair %#H - %#H with priority %d", other->local, other->remote, other->priority); search->remove(search); endpoint_pair_destroy(other); @@ -792,6 +702,7 @@ static void prune_pairs(linked_list_t *pairs) */ static void build_pairs(check_list_t *checklist) { + /* FIXME: limit endpoints and pairs */ iterator_t *iterator_i, *iterator_r; endpoint_notify_t *initiator, *responder; @@ -812,6 +723,8 @@ static void build_pairs(check_list_t *checklist) iterator_r->destroy(iterator_r); } iterator_i->destroy(iterator_i); + + print_checklist(checklist); prune_pairs(checklist->pairs); } @@ -838,45 +751,45 @@ static status_t process_payloads(message_t *message, check_t *check) switch (notify->get_notify_type(notify)) { - case P2P_ENDPOINT: + case ME_ENDPOINT: { if (check->endpoint) { - DBG1(DBG_IKE, "connectivity check contains multiple P2P_ENDPOINT notifies"); + DBG1(DBG_IKE, "connectivity check contains multiple ME_ENDPOINT notifies"); break; } endpoint_notify_t *endpoint = endpoint_notify_create_from_payload(notify); if (!endpoint) { - DBG1(DBG_IKE, "received invalid P2P_ENDPOINT notify"); + DBG1(DBG_IKE, "received invalid ME_ENDPOINT notify"); break; } check->endpoint = endpoint; check->endpoint_raw = chunk_clone(notify->get_notification_data(notify)); - DBG2(DBG_IKE, "received P2P_ENDPOINT notify"); + DBG2(DBG_IKE, "received ME_ENDPOINT notify"); break; } - case P2P_SESSIONID: + case ME_CONNECTID: { - if (check->session_id.ptr) + if (check->connect_id.ptr) { - DBG1(DBG_IKE, "connectivity check contains multiple P2P_SESSIONID notifies"); + DBG1(DBG_IKE, "connectivity check contains multiple ME_CONNECTID notifies"); break; } - check->session_id = chunk_clone(notify->get_notification_data(notify)); - DBG3(DBG_IKE, "received p2p_sessionid %B", &check->session_id); + check->connect_id = chunk_clone(notify->get_notification_data(notify)); + DBG2(DBG_IKE, "received ME_CONNECTID %#B", &check->connect_id); break; } - case COOKIE: + case ME_CONNECTAUTH: { - if (check->cookie.ptr) + if (check->auth.ptr) { - DBG1(DBG_IKE, "connectivity check contains multiple COOKIE notifies"); + DBG1(DBG_IKE, "connectivity check contains multiple ME_CONNECTAUTH notifies"); break; } - check->cookie = chunk_clone(notify->get_notification_data(notify)); - DBG3(DBG_IKE, "received cookie %B", &check->cookie); + check->auth = chunk_clone(notify->get_notification_data(notify)); + DBG2(DBG_IKE, "received ME_CONNECTAUTH %#B", &check->auth); break; } default: @@ -885,7 +798,7 @@ static status_t process_payloads(message_t *message, check_t *check) } iterator->destroy(iterator); - if (!check->session_id.ptr || !check->endpoint || !check->cookie.ptr) + if (!check->connect_id.ptr || !check->endpoint || !check->auth.ptr) { DBG1(DBG_IKE, "at least one payload was missing from the connectivity check"); return FAILED; @@ -900,42 +813,129 @@ static status_t process_payloads(message_t *message, check_t *check) static chunk_t build_signature(private_connect_manager_t *this, check_list_t *checklist, check_t *check, bool outbound) { + u_int32_t mid; chunk_t mid_chunk, key_chunk, sig_chunk; chunk_t sig_hash; - mid_chunk = chunk_from_thing(check->mid); + mid = htonl(check->mid); + mid_chunk = chunk_from_thing(mid); key_chunk = (checklist->is_initiator && outbound) || (!checklist->is_initiator && !outbound) ? checklist->initiator.key : checklist->responder.key; - /* signature = SHA1( MID | P2P_SESSIONID | P2P_ENDPOINT | P2P_SESSIONKEY ) */ - sig_chunk = chunk_cat("cccc", mid_chunk, check->session_id, check->endpoint_raw, key_chunk); + /* signature = SHA1( MID | ME_CONNECTID | ME_ENDPOINT | ME_CONNECTKEY ) */ + sig_chunk = chunk_cat("cccc", mid_chunk, check->connect_id, check->endpoint_raw, key_chunk); this->hasher->allocate_hash(this->hasher, sig_chunk, &sig_hash); - DBG3(DBG_IKE, "sig_chunk %B", &sig_chunk); - DBG3(DBG_IKE, "sig_hash %B", &sig_hash); + DBG3(DBG_IKE, "sig_chunk %#B", &sig_chunk); + DBG3(DBG_IKE, "sig_hash %#B", &sig_hash); chunk_free(&sig_chunk); return sig_hash; } -static void queue_retransmission(private_connect_manager_t *this, chunk_t session_id, u_int32_t mid); +static void queue_retransmission(private_connect_manager_t *this, check_list_t *checklist, endpoint_pair_t *pair); static void schedule_checks(private_connect_manager_t *this, check_list_t *checklist, u_int32_t time); static void finish_checks(private_connect_manager_t *this, check_list_t *checklist); /** + * After one of the initiator's pairs has succeeded we finish the checks without + * waiting for all the timeouts + */ +static job_requeue_t initiator_finish(callback_data_t *data) +{ + private_connect_manager_t *this = data->connect_manager; + + pthread_mutex_lock(&(this->mutex)); + + check_list_t *checklist; + if (get_checklist_by_id(this, data->connect_id, &checklist) != SUCCESS) + { + DBG1(DBG_IKE, "checklist with id '%#B' not found, can't finish connectivity checks", + &data->connect_id); + pthread_mutex_unlock(&(this->mutex)); + return JOB_REQUEUE_NONE; + } + + finish_checks(this, checklist); + + pthread_mutex_unlock(&(this->mutex)); + + return JOB_REQUEUE_NONE; +} + +/** + * Updates the state of the whole checklist + */ +static void update_checklist_state(private_connect_manager_t *this, check_list_t *checklist) +{ + iterator_t *iterator; + endpoint_pair_t *current; + bool in_progress = FALSE, succeeded = FALSE; + + iterator = checklist->pairs->create_iterator(checklist->pairs, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + switch(current->state) + { + case CHECK_WAITING: + /* at least one is still waiting -> checklist remains + * in waiting state */ + iterator->destroy(iterator); + return; + case CHECK_IN_PROGRESS: + in_progress = TRUE; + break; + case CHECK_SUCCEEDED: + succeeded = TRUE; + break; + default: + break; + } + } + iterator->destroy(iterator); + + if (checklist->is_initiator && succeeded && !checklist->is_finishing) + { + /* instead of waiting until all checks have finished (i.e. all + * retransmissions have failed) the initiator finishes the checks + * right after the first check has succeeded. to allow a probably + * better pair to succeed, we still wait a certain time */ + DBG2(DBG_IKE, "fast finishing checks for checklist '%#B'", &checklist->connect_id); + + callback_data_t *data = callback_data_create(this, checklist->connect_id); + job_t *job = (job_t*)callback_job_create((callback_job_cb_t)initiator_finish, data, (callback_job_cleanup_t)callback_data_destroy, NULL); + charon->scheduler->schedule_job(charon->scheduler, job, ME_WAIT_TO_FINISH); + checklist->is_finishing = TRUE; + } + + if (in_progress) + { + checklist->state = CHECK_IN_PROGRESS; + } + else if (succeeded) + { + checklist->state = CHECK_SUCCEEDED; + } + else + { + checklist->state = CHECK_FAILED; + } +} + +/** * This function is triggered for each sent check after a specific timeout */ -static job_requeue_t retransmit(retransmit_data_t *data) +static job_requeue_t retransmit(callback_data_t *data) { private_connect_manager_t *this = data->connect_manager; pthread_mutex_lock(&(this->mutex)); check_list_t *checklist; - if (get_checklist_by_id(this, data->session_id, &checklist) != SUCCESS) + if (get_checklist_by_id(this, data->connect_id, &checklist) != SUCCESS) { - DBG1(DBG_IKE, "checklist with id '%B' not found, can't retransmit connectivity check", - &data->session_id); + DBG1(DBG_IKE, "checklist with id '%#B' not found, can't retransmit connectivity check", + &data->connect_id); pthread_mutex_unlock(&(this->mutex)); return JOB_REQUEUE_NONE; } @@ -955,20 +955,20 @@ static job_requeue_t retransmit(retransmit_data_t *data) goto retransmit_end; } - if (++pair->retransmitted >= P2P_MAX_RETRANS) + if (++pair->retransmitted > ME_MAX_RETRANS) { - DBG2(DBG_IKE, "pair with id '%d' failed after %d tries", - data->mid, pair->retransmitted); + DBG2(DBG_IKE, "pair with id '%d' failed after %d retransmissions", + data->mid, ME_MAX_RETRANS); pair->state = CHECK_FAILED; goto retransmit_end; } charon->sender->send(charon->sender, pair->packet->clone(pair->packet)); - queue_retransmission(this, checklist->session_id, pair->id); + queue_retransmission(this, checklist, pair); retransmit_end: - update_checklist_state(checklist); + update_checklist_state(this, checklist); switch(checklist->state) { @@ -989,11 +989,20 @@ retransmit_end: /** * Queues a retransmission job */ -static void queue_retransmission(private_connect_manager_t *this, chunk_t session_id, u_int32_t mid) +static void queue_retransmission(private_connect_manager_t *this, check_list_t *checklist, endpoint_pair_t *pair) { - retransmit_data_t *data = retransmit_data_create(this, chunk_clone(session_id), mid); - job_t *job = (job_t*)callback_job_create((callback_job_cb_t)retransmit, data, (callback_job_cleanup_t)retransmit_data_destroy, NULL); - charon->scheduler->schedule_job(charon->scheduler, (job_t*)job, P2P_RTO_MIN); + callback_data_t *data = retransmit_data_create(this, checklist->connect_id, pair->id); + job_t *job = (job_t*)callback_job_create((callback_job_cb_t)retransmit, data, (callback_job_cleanup_t)callback_data_destroy, NULL); + + u_int32_t retransmission = pair->retransmitted + 1; + u_int32_t rto = ME_INTERVAL; + if (retransmission > ME_BOOST) + { + rto = (u_int32_t)(ME_INTERVAL * pow(ME_RETRANS_BASE, retransmission - ME_BOOST)); + } + DBG2(DBG_IKE, "scheduling retransmission %d of pair '%d' in %dms", retransmission, pair->id, rto); + + charon->scheduler->schedule_job(charon->scheduler, (job_t*)job, rto); } /** @@ -1009,16 +1018,21 @@ static void send_check(private_connect_manager_t *this, check_list_t *checklist, message->set_destination(message, check->dst->clone(check->dst)); message->set_source(message, check->src->clone(check->src)); - message->set_ike_sa_id(message, ike_sa_id_create(0, 0, request)); + ike_sa_id_t *ike_sa_id = ike_sa_id_create(0, 0, request); + message->set_ike_sa_id(message, ike_sa_id); + ike_sa_id->destroy(ike_sa_id); - message->add_notify(message, FALSE, P2P_SESSIONID, check->session_id); + message->add_notify(message, FALSE, ME_CONNECTID, check->connect_id); + DBG2(DBG_IKE, "send ME_CONNECTID %#B", &check->connect_id); notify_payload_t *endpoint = check->endpoint->build_notify(check->endpoint); check->endpoint_raw = chunk_clone(endpoint->get_notification_data(endpoint)); message->add_payload(message, (payload_t*)endpoint); + DBG2(DBG_IKE, "send ME_ENDPOINT notify"); - check->cookie = build_signature(this, checklist, check, TRUE); - message->add_notify(message, FALSE, COOKIE, check->cookie); + check->auth = build_signature(this, checklist, check, TRUE); + message->add_notify(message, FALSE, ME_CONNECTAUTH, check->auth); + DBG2(DBG_IKE, "send ME_CONNECTAUTH %#B", &check->auth); packet_t *packet; if (message->generate(message, NULL, NULL, &packet) == SUCCESS) @@ -1029,42 +1043,55 @@ static void send_check(private_connect_manager_t *this, check_list_t *checklist, { DESTROY_IF(pair->packet); pair->packet = packet; - queue_retransmission(this, checklist->session_id, pair->id); + pair->retransmitted = 0; + queue_retransmission(this, checklist, pair); } else { packet->destroy(packet); } } + message->destroy(message); } /** * Queues a triggered check */ -static void queue_triggered_check(check_list_t *checklist, endpoint_pair_t *pair) +static void queue_triggered_check(private_connect_manager_t *this, + check_list_t *checklist, endpoint_pair_t *pair) { + DBG2(DBG_IKE, "queueing triggered check for pair '%d'", pair->id); pair->state = CHECK_WAITING; checklist->triggered->insert_last(checklist->triggered, pair); + + if (!checklist->sender) + { + /* if the sender is not running we restart it */ + schedule_checks(this, checklist, ME_INTERVAL); + } } /** * This function is triggered for each checklist at a specific interval */ -static job_requeue_t sender(sender_data_t *data) +static job_requeue_t sender(callback_data_t *data) { private_connect_manager_t *this = data->connect_manager; pthread_mutex_lock(&(this->mutex)); - + check_list_t *checklist; - if (get_checklist_by_id(this, data->session_id, &checklist) != SUCCESS) + if (get_checklist_by_id(this, data->connect_id, &checklist) != SUCCESS) { - DBG1(DBG_IKE, "checklist with id '%B' not found, can't send connectivity check", - &data->session_id); + DBG1(DBG_IKE, "checklist with id '%#B' not found, can't send connectivity check", + &data->connect_id); pthread_mutex_unlock(&(this->mutex)); return JOB_REQUEUE_NONE; } + /* reset the sender */ + checklist->sender = NULL; + endpoint_pair_t *pair; if (get_triggered_pair(checklist, &pair) != SUCCESS) { @@ -1087,7 +1114,7 @@ static job_requeue_t sender(sender_data_t *data) check->mid = pair->id; check->src = pair->local->clone(pair->local); check->dst = pair->remote->clone(pair->remote); - check->session_id = chunk_clone(checklist->session_id); + check->connect_id = chunk_clone(checklist->connect_id); check->endpoint = endpoint_notify_create(); pair->state = CHECK_IN_PROGRESS; @@ -1097,8 +1124,7 @@ static job_requeue_t sender(sender_data_t *data) check_destroy(check); /* schedule this job again */ - u_int32_t N = this->checklists->get_count(this->checklists); - schedule_checks(this, checklist, P2P_INTERVAL * N); + schedule_checks(this, checklist, ME_INTERVAL); pthread_mutex_unlock(&(this->mutex)); @@ -1111,10 +1137,9 @@ static job_requeue_t sender(sender_data_t *data) */ static void schedule_checks(private_connect_manager_t *this, check_list_t *checklist, u_int32_t time) { - chunk_t session_id = chunk_clone(checklist->session_id); - sender_data_t *data = sender_data_create(this, session_id); - job_t *job = (job_t*)callback_job_create((callback_job_cb_t)sender, data, (callback_job_cleanup_t)sender_data_destroy, NULL); - charon->scheduler->schedule_job(charon->scheduler, job, time); + callback_data_t *data = callback_data_create(this, checklist->connect_id); + checklist->sender = (job_t*)callback_job_create((callback_job_cb_t)sender, data, (callback_job_cleanup_t)callback_data_destroy, NULL); + charon->scheduler->schedule_job(charon->scheduler, checklist->sender, time); } /** @@ -1128,12 +1153,12 @@ static job_requeue_t initiate_mediated(initiate_data_t *data) endpoint_pair_t *pair; if (get_best_valid_pair(checklist, &pair) == SUCCESS) { - waiting_sa_t *waiting_sa; + ike_sa_id_t *waiting_sa; iterator_t *iterator = initiated->mediated->create_iterator(initiated->mediated, TRUE); while (iterator->iterate(iterator, (void**)&waiting_sa)) { - ike_sa_t *sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, waiting_sa->ike_sa_id); - if (sa->initiate_mediated(sa, pair->local, pair->remote, waiting_sa->childs) != SUCCESS) + ike_sa_t *sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, waiting_sa); + if (sa->initiate_mediated(sa, pair->local, pair->remote, checklist->connect_id) != SUCCESS) { SIG(IKE_UP_FAILED, "establishing the mediated connection failed"); charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, sa); @@ -1175,11 +1200,6 @@ static void finish_checks(private_connect_manager_t *this, check_list_t *checkli "and '%D'", checklist->initiator.id, checklist->responder.id); } } - - /* remove_checklist(this, checklist); - * check_list_destroy(checklist); - * FIXME: we should do this ^^^ after a specific timeout on the - * responder side */ } /** @@ -1212,7 +1232,7 @@ static void process_response(private_connect_manager_t *this, check_t *check, local_endpoints->insert_last(local_endpoints, local_endpoint); } - update_checklist_state(checklist); + update_checklist_state(this, checklist); switch(checklist->state) { @@ -1253,12 +1273,12 @@ static void process_request(private_connect_manager_t *this, check_t *check, { case CHECK_IN_PROGRESS: /* prevent retransmissions */ - pair->retransmitted = P2P_MAX_RETRANS; + pair->retransmitted = ME_MAX_RETRANS; /* FIXME: we should wait to the next rto to send the triggered check * fall-through */ case CHECK_WAITING: case CHECK_FAILED: - queue_triggered_check(checklist, pair); + queue_triggered_check(this, checklist, pair); break; case CHECK_SUCCEEDED: default: @@ -1277,7 +1297,7 @@ static void process_request(private_connect_manager_t *this, check_t *check, insert_pair_by_priority(checklist->pairs, pair); - queue_triggered_check(checklist, pair); + queue_triggered_check(this, checklist, pair); local_endpoint->destroy(local_endpoint); } @@ -1288,7 +1308,7 @@ static void process_request(private_connect_manager_t *this, check_t *check, response->mid = check->mid; response->src = check->dst->clone(check->dst); response->dst = check->src->clone(check->src); - response->session_id = chunk_clone(check->session_id); + response->connect_id = chunk_clone(check->connect_id); response->endpoint = peer_reflexive; send_check(this, checklist, response, pair, FALSE); @@ -1313,7 +1333,9 @@ static void process_check(private_connect_manager_t *this, message_t *message) check_t *check = check_create(); check->mid = message->get_message_id(message); check->src = message->get_source(message); + check->src = check->src->clone(check->src); check->dst = message->get_destination(message); + check->dst = check->dst->clone(check->dst); if (process_payloads(message, check) != SUCCESS) { @@ -1326,17 +1348,17 @@ static void process_check(private_connect_manager_t *this, message_t *message) pthread_mutex_lock(&(this->mutex)); check_list_t *checklist; - if (get_checklist_by_id(this, check->session_id, &checklist) != SUCCESS) + if (get_checklist_by_id(this, check->connect_id, &checklist) != SUCCESS) { - DBG1(DBG_IKE, "checklist with id '%B' not found", - &check->session_id); + DBG1(DBG_IKE, "checklist with id '%#B' not found", + &check->connect_id); check_destroy(check); pthread_mutex_unlock(&(this->mutex)); return; } chunk_t sig = build_signature(this, checklist, check, FALSE); - if (!chunk_equals(sig, check->cookie)) + if (!chunk_equals(sig, check->auth)) { DBG1(DBG_IKE, "connectivity check verification failed"); check_destroy(check); @@ -1365,7 +1387,7 @@ static void process_check(private_connect_manager_t *this, message_t *message) */ static bool check_and_register(private_connect_manager_t *this, identification_t *id, identification_t *peer_id, - ike_sa_id_t *mediated_sa, child_cfg_t *child) + ike_sa_id_t *mediated_sa) { initiated_t *initiated; bool already_there = TRUE; @@ -1380,16 +1402,12 @@ static bool check_and_register(private_connect_manager_t *this, already_there = FALSE; } - waiting_sa_t *waiting_sa; - if (get_waiting_sa(initiated, mediated_sa, &waiting_sa) != SUCCESS) + if (initiated->mediated->find_first(initiated->mediated, + (linked_list_match_t)mediated_sa->equals, NULL, mediated_sa) != SUCCESS) { - waiting_sa = waiting_sa_create(mediated_sa); - initiated->mediated->insert_last(initiated->mediated, waiting_sa); + initiated->mediated->insert_last(initiated->mediated, mediated_sa->clone(mediated_sa)); } - child->get_ref(child); - waiting_sa->childs->insert_last(waiting_sa->childs, child); - pthread_mutex_unlock(&(this->mutex)); return already_there; @@ -1412,14 +1430,14 @@ static void check_and_initiate(private_connect_manager_t *this, ike_sa_id_t *med return; } - waiting_sa_t *waiting_sa; + ike_sa_id_t *waiting_sa; iterator_t *iterator = initiated->mediated->create_iterator(initiated->mediated, TRUE); while (iterator->iterate(iterator, (void**)&waiting_sa)) { - job_t *job = (job_t*)reinitiate_mediation_job_create(mediation_sa, - waiting_sa->ike_sa_id); + job_t *job = (job_t*)reinitiate_mediation_job_create(mediation_sa, waiting_sa); charon->processor->queue_job(charon->processor, job); } + iterator->destroy(iterator); pthread_mutex_unlock(&(this->mutex)); } @@ -1429,21 +1447,21 @@ static void check_and_initiate(private_connect_manager_t *this, ike_sa_id_t *med */ static status_t set_initiator_data(private_connect_manager_t *this, identification_t *initiator, identification_t *responder, - chunk_t session_id, chunk_t key, linked_list_t *endpoints, bool is_initiator) + chunk_t connect_id, chunk_t key, linked_list_t *endpoints, bool is_initiator) { check_list_t *checklist; pthread_mutex_lock(&(this->mutex)); - if (get_checklist_by_id(this, session_id, NULL) == SUCCESS) + if (get_checklist_by_id(this, connect_id, NULL) == SUCCESS) { - DBG1(DBG_IKE, "checklist with id '%B' already exists, aborting", - &session_id); + DBG1(DBG_IKE, "checklist with id '%#B' already exists, aborting", + &connect_id); pthread_mutex_unlock(&(this->mutex)); return FAILED; } - checklist = check_list_create(initiator, responder, session_id, key, endpoints, is_initiator); + checklist = check_list_create(initiator, responder, connect_id, key, endpoints, is_initiator); this->checklists->insert_last(this->checklists, checklist); pthread_mutex_unlock(&(this->mutex)); @@ -1455,16 +1473,16 @@ static status_t set_initiator_data(private_connect_manager_t *this, * Implementation of connect_manager_t.set_responder_data. */ static status_t set_responder_data(private_connect_manager_t *this, - chunk_t session_id, chunk_t key, linked_list_t *endpoints) + chunk_t connect_id, chunk_t key, linked_list_t *endpoints) { check_list_t *checklist; pthread_mutex_lock(&(this->mutex)); - if (get_checklist_by_id(this, session_id, &checklist) != SUCCESS) + if (get_checklist_by_id(this, connect_id, &checklist) != SUCCESS) { - DBG1(DBG_IKE, "checklist with id '%B' not found", - &session_id); + DBG1(DBG_IKE, "checklist with id '%#B' not found", + &connect_id); pthread_mutex_unlock(&(this->mutex)); return NOT_FOUND; } @@ -1484,6 +1502,33 @@ static status_t set_responder_data(private_connect_manager_t *this, } /** + * Implementation of connect_manager_t.stop_checks. + */ +static status_t stop_checks(private_connect_manager_t *this, chunk_t connect_id) +{ + check_list_t *checklist; + + pthread_mutex_lock(&(this->mutex)); + + if (get_checklist_by_id(this, connect_id, &checklist) != SUCCESS) + { + DBG1(DBG_IKE, "checklist with id '%#B' not found", + &connect_id); + pthread_mutex_unlock(&(this->mutex)); + return NOT_FOUND; + } + + DBG1(DBG_IKE, "removing checklist with id '%#B'", &connect_id); + + remove_checklist(this, checklist); + check_list_destroy(checklist); + + pthread_mutex_unlock(&(this->mutex)); + + return SUCCESS; +} + +/** * Implementation of connect_manager_t.destroy. */ static void destroy(private_connect_manager_t *this) @@ -1507,13 +1552,21 @@ connect_manager_t *connect_manager_create() private_connect_manager_t *this = malloc_thing(private_connect_manager_t); this->public.destroy = (void(*)(connect_manager_t*))destroy; - this->public.check_and_register = (bool(*)(connect_manager_t*,identification_t*,identification_t*,ike_sa_id_t*,child_cfg_t*))check_and_register; + this->public.check_and_register = (bool(*)(connect_manager_t*,identification_t*,identification_t*,ike_sa_id_t*))check_and_register; this->public.check_and_initiate = (void(*)(connect_manager_t*,ike_sa_id_t*,identification_t*,identification_t*))check_and_initiate; this->public.set_initiator_data = (status_t(*)(connect_manager_t*,identification_t*,identification_t*,chunk_t,chunk_t,linked_list_t*,bool))set_initiator_data; this->public.set_responder_data = (status_t(*)(connect_manager_t*,chunk_t,chunk_t,linked_list_t*))set_responder_data; this->public.process_check = (void(*)(connect_manager_t*,message_t*))process_check; + this->public.stop_checks = (status_t(*)(connect_manager_t*,chunk_t))stop_checks; + + this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); + if (this->hasher == NULL) + { + DBG1(DBG_IKE, "unable to create connect manager, SHA1 not supported"); + free(this); + return NULL; + } - this->hasher = hasher_create(HASH_SHA1); this->checklists = linked_list_create(); this->initiated = linked_list_create(); |