From 6970925422539c965b6a54099a6fb3abaeb9706a Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 7 Apr 2008 15:45:37 +0000 Subject: [PATCH] fast finishing connectivity checks on the initiators side --- src/charon/sa/connect_manager.c | 152 +++++++++++++++++++++----------- 1 file changed, 99 insertions(+), 53 deletions(-) diff --git a/src/charon/sa/connect_manager.c b/src/charon/sa/connect_manager.c index 2d5e68416..2545d0afa 100644 --- a/src/charon/sa/connect_manager.c +++ b/src/charon/sa/connect_manager.c @@ -40,6 +40,10 @@ /* 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; @@ -193,6 +197,9 @@ struct check_list_t { /** TRUE if this is the initiator */ bool is_initiator; + /** TRUE if the initiator is finishing the checks */ + bool is_finishing; + }; /** @@ -240,6 +247,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; + this->is_finishing = FALSE; return this; } @@ -413,7 +421,7 @@ static sender_data_t *sender_data_create(private_connect_manager_t *connect_mana { sender_data_t *this = malloc_thing(sender_data_t); this->connect_manager = connect_manager; - this->connect_id = connect_id; + this->connect_id = chunk_clone(connect_id); return this; } @@ -596,51 +604,6 @@ static status_t endpoints_contain(linked_list_t *endpoints, host_t *host, endpoi (void**)endpoint, host); } -/** - * 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) */ @@ -924,6 +887,91 @@ static void queue_retransmission(private_connect_manager_t *this, check_list_t * 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(sender_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); + + sender_data_t *data = sender_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)sender_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 */ @@ -970,7 +1018,7 @@ static job_requeue_t retransmit(retransmit_data_t *data) queue_retransmission(this, checklist, pair); retransmit_end: - update_checklist_state(checklist); + update_checklist_state(this, checklist); switch(checklist->state) { @@ -1114,8 +1162,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, ME_INTERVAL * N); + schedule_checks(this, checklist, ME_INTERVAL); pthread_mutex_unlock(&(this->mutex)); @@ -1128,8 +1175,7 @@ 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 connect_id = chunk_clone(checklist->connect_id); - sender_data_t *data = sender_data_create(this, connect_id); + sender_data_t *data = sender_data_create(this, checklist->connect_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); } @@ -1225,7 +1271,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) { @@ -1266,7 +1312,7 @@ static void process_request(private_connect_manager_t *this, check_t *check, { case CHECK_IN_PROGRESS: /* prevent retransmissions */ - pair->retransmitted = ME_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: