diff --git a/src/charon/kernel/kernel_interface.c b/src/charon/kernel/kernel_interface.c index 64448e63f..83f542e6c 100644 --- a/src/charon/kernel/kernel_interface.c +++ b/src/charon/kernel/kernel_interface.c @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -67,6 +68,9 @@ #define PRIO_LOW 3000 #define PRIO_HIGH 2000 +/** delay before firing roam jobs (ms) */ +#define ROAM_DELAY 100 + #define BUFFER_SIZE 1024 /** @@ -344,6 +348,11 @@ struct private_kernel_interface_t { * Netlink rt socket to receive address change events */ int socket_rt_events; + + /** + * time of the last roam_job + */ + struct timeval last_roam; }; /** @@ -527,6 +536,31 @@ static void process_expire(private_kernel_interface_t *this, struct nlmsghdr *hd charon->processor->queue_job(charon->processor, job); } +/** + * start a roaming job. We delay it for a second and fire only one job + * for multiple events. Otherwise we would create two many jobs. + */ +static void fire_roam_job(private_kernel_interface_t *this, bool address) +{ + struct timeval now; + + if (gettimeofday(&now, NULL) == 0) + { + if (timercmp(&now, &this->last_roam, >)) + { + now.tv_usec += ROAM_DELAY * 1000; + while (now.tv_usec > 1000000) + { + now.tv_sec++; + now.tv_usec -= 1000000; + } + this->last_roam = now; + charon->scheduler->schedule_job(charon->scheduler, + (job_t*)roam_job_create(address), ROAM_DELAY); + } + } +} + /** * process RTM_NEWLINK/RTM_DELLINK from kernel */ @@ -623,8 +657,7 @@ static void process_link(private_kernel_interface_t *this, /* send an update to all IKE_SAs */ if (update && event) { - charon->processor->queue_job(charon->processor, - (job_t*)roam_job_create(TRUE)); + fire_roam_job(this, TRUE); } } @@ -731,8 +764,7 @@ static void process_addr(private_kernel_interface_t *this, /* send an update to all IKE_SAs */ if (update && event && changed) { - charon->processor->queue_job(charon->processor, - (job_t*)roam_job_create(TRUE)); + fire_roam_job(this, TRUE); } } @@ -828,8 +860,7 @@ static job_requeue_t receive_events(private_kernel_interface_t *this) break; case RTM_NEWROUTE: case RTM_DELROUTE: - charon->processor->queue_job(charon->processor, - (job_t*)roam_job_create(FALSE)); + fire_roam_job(this, FALSE); break; default: break; @@ -2517,6 +2548,7 @@ kernel_interface_t *kernel_interface_create() this->hiter = NULL; this->seq = 200; pthread_mutex_init(&this->mutex,NULL); + timerclear(&this->last_roam); memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c index 4822c840d..d23f08eef 100644 --- a/src/charon/sa/ike_sa.c +++ b/src/charon/sa/ike_sa.c @@ -812,8 +812,6 @@ static status_t generate_message(private_ike_sa_t *this, message_t *message, { this->time.outbound = time(NULL); message->set_ike_sa_id(message, this->ike_sa_id); - message->set_destination(message, this->other_host->clone(this->other_host)); - message->set_source(message, this->my_host->clone(this->my_host)); return message->generate(message, this->crypter_out, this->signer_out, packet); } @@ -859,7 +857,6 @@ static status_t initiate(private_ike_sa_t *this, child_cfg_t *child_cfg) if (this->state == IKE_CREATED) { - if (this->other_host->is_anyaddr(this->other_host)) { child_cfg->destroy(child_cfg); @@ -1802,30 +1799,19 @@ static status_t roam(private_ike_sa_t *this, bool address) other = this->other_host; me = charon->kernel_interface->get_source_addr(charon->kernel_interface, other); - - /* TODO: find a better path using additional addresses of peer */ - - if (!me) - { - /* no route found to host, set to stale, wait for a new route */ - set_condition(this, COND_STALE, TRUE); - return FAILED; - } set_condition(this, COND_STALE, FALSE); - if (me->ip_equals(me, this->my_host) && - other->ip_equals(other, this->other_host)) + if (me) { - DBG2(DBG_IKE, "%H still reached through %H, no update needed", - this->other_host, me); + if (me->ip_equals(me, this->my_host) && + other->ip_equals(other, this->other_host)) + { + DBG2(DBG_IKE, "keeping connection path %H - %H", this->other_host, me); + me->destroy(me); + return SUCCESS; + } me->destroy(me); - return SUCCESS; } - me->set_port(me, this->my_host->get_port(this->my_host)); - other = other->clone(other); - other->set_port(other, this->other_host->get_port(this->other_host)); - set_my_host(this, me); - set_other_host(this, other); /* update addresses with mobike, if supported ... */ if (supports_extension(this, EXT_MOBIKE)) diff --git a/src/charon/sa/task_manager.c b/src/charon/sa/task_manager.c index 55592f437..a44f88b15 100644 --- a/src/charon/sa/task_manager.c +++ b/src/charon/sa/task_manager.c @@ -217,6 +217,9 @@ static status_t retransmit(private_task_manager_t *this, u_int32_t message_id) { u_int32_t timeout; job_t *job; + iterator_t *iterator; + packet_t *packet; + task_t *task; if (this->initiating.retransmitted <= RETRANSMIT_TRIES) { @@ -237,8 +240,22 @@ static status_t retransmit(private_task_manager_t *this, u_int32_t message_id) } this->initiating.retransmitted++; - charon->sender->send(charon->sender, - this->initiating.packet->clone(this->initiating.packet)); + packet = this->initiating.packet->clone(this->initiating.packet); + + /* mobike needs to now when we retransmit, so we call it here */ + iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE); + while (iterator->iterate(iterator, (void*)&task)) + { + if (task->get_type(task) == IKE_MOBIKE) + { + ike_mobike_t *mobike = (ike_mobike_t*)task; + mobike->transmit(mobike, packet); + break; + } + } + iterator->destroy(iterator); + + charon->sender->send(charon->sender, packet); job = (job_t*)retransmit_job_create(this->initiating.mid, this->ike_sa->get_id(this->ike_sa)); charon->scheduler->schedule_job(charon->scheduler, job, timeout); @@ -255,6 +272,7 @@ static status_t build_request(private_task_manager_t *this) iterator_t *iterator; task_t *task; message_t *message; + host_t *me, *other; status_t status; exchange_type_t exchange = 0; @@ -372,8 +390,13 @@ static status_t build_request(private_task_manager_t *this) return SUCCESS; } + me = this->ike_sa->get_my_host(this->ike_sa); + other = this->ike_sa->get_other_host(this->ike_sa); + message = message_create(); message->set_message_id(message, this->initiating.mid); + message->set_source(message, me->clone(me)); + message->set_destination(message, other->clone(other)); message->set_exchange_type(message, exchange); this->initiating.type = exchange; this->initiating.retransmitted = 0; @@ -412,7 +435,7 @@ static status_t build_request(private_task_manager_t *this) * close the SA */ flush(this); return DESTROY_ME; - } + } return retransmit(this, this->initiating.mid); } @@ -523,17 +546,23 @@ static void handle_collisions(private_task_manager_t *this, task_t *task) /** * build a response depending on the "passive" task list */ -static status_t build_response(private_task_manager_t *this, - exchange_type_t exchange) +static status_t build_response(private_task_manager_t *this, message_t *request) { iterator_t *iterator; task_t *task; message_t *message; + host_t *me, *other; bool delete = FALSE; status_t status; + me = request->get_destination(request); + other = request->get_source(request); + message = message_create(); - message->set_exchange_type(message, exchange); + message->set_exchange_type(message, request->get_exchange_type(request)); + /* send response along the path the request came in */ + message->set_source(message, me->clone(me)); + message->set_destination(message, other->clone(other)); message->set_message_id(message, this->responding.mid); message->set_request(message, FALSE); @@ -563,7 +592,7 @@ static status_t build_response(private_task_manager_t *this, iterator->destroy(iterator); /* remove resonder SPI if IKE_SA_INIT failed */ - if (delete && exchange == IKE_SA_INIT) + if (delete && request->get_exchange_type(request) == IKE_SA_INIT) { ike_sa_id_t *id = this->ike_sa->get_id(this->ike_sa); id->set_responder_spi(id, 0); @@ -596,15 +625,12 @@ static status_t process_request(private_task_manager_t *this, { iterator_t *iterator; task_t *task = NULL; - exchange_type_t exchange; payload_t *payload; notify_payload_t *notify; delete_payload_t *delete; - exchange = message->get_exchange_type(message); - /* create tasks depending on request type */ - switch (exchange) + switch (message->get_exchange_type(message)) { case IKE_SA_INIT: { @@ -760,7 +786,7 @@ static status_t process_request(private_task_manager_t *this, } iterator->destroy(iterator); - return build_response(this, exchange); + return build_response(this, message); } /** diff --git a/src/charon/sa/tasks/ike_mobike.c b/src/charon/sa/tasks/ike_mobike.c index 7d98172eb..8d35d966b 100644 --- a/src/charon/sa/tasks/ike_mobike.c +++ b/src/charon/sa/tasks/ike_mobike.c @@ -64,7 +64,12 @@ struct private_ike_mobike_t { /** * use task to update addresses */ - bool roam; + bool update; + + /** + * do routability check + */ + bool check; /** * include address list update @@ -140,7 +145,7 @@ static void process_payloads(private_ike_mobike_t *this, message_t *message) } case UPDATE_SA_ADDRESSES: { - this->roam = TRUE; + this->update = TRUE; break; } case NO_ADDITIONAL_ADDRESSES: @@ -224,6 +229,54 @@ static void update_children(private_ike_mobike_t *this) iterator->destroy(iterator); } +/** + * Implementation of ike_mobike_t.transmit + */ +static void transmit(private_ike_mobike_t *this, packet_t *packet) +{ + host_t *me, *other, *me_old, *other_old; + iterator_t *iterator; + packet_t *copy; + + if (!this->check) + { + return; + } + + me_old = this->ike_sa->get_my_host(this->ike_sa); + other_old = this->ike_sa->get_other_host(this->ike_sa); + + me = charon->kernel_interface->get_source_addr( + charon->kernel_interface, other_old); + if (me) + { + me->set_port(me, me->ip_equals(me, me_old) ? + me_old->get_port(me_old) : IKEV2_NATT_PORT); + packet->set_source(packet, me); + } + + iterator = this->ike_sa->create_additional_address_iterator(this->ike_sa); + while (iterator->iterate(iterator, (void**)&other)) + { + me = charon->kernel_interface->get_source_addr( + charon->kernel_interface, other); + if (me) + { + /* reuse port for an active address, 4500 otherwise */ + me->set_port(me, me->ip_equals(me, me_old) ? + me_old->get_port(me_old) : IKEV2_NATT_PORT); + other = other->clone(other); + other->set_port(other, other->ip_equals(other, other_old) ? + other_old->get_port(other_old) : IKEV2_NATT_PORT); + copy = packet->clone(packet); + copy->set_source(copy, me); + copy->set_destination(copy, other); + charon->sender->send(charon->sender, copy); + } + } + iterator->destroy(iterator); +} + /** * Implementation of task_t.process for initiator */ @@ -237,20 +290,20 @@ static status_t build_i(private_ike_mobike_t *this, message_t *message) } else if (message->get_exchange_type(message) == INFORMATIONAL) { - if (this->roam) + if (this->update) { message->add_notify(message, FALSE, UPDATE_SA_ADDRESSES, chunk_empty); + update_children(this); } if (this->address) { build_address_list(this, message); } - - this->natd = ike_natd_create(this->ike_sa, this->initiator); - this->natd->task.build(&this->natd->task, message); - update_children(this); + if (this->natd) + { + this->natd->task.build(&this->natd->task, message); + } } - return NEED_MORE; } @@ -267,7 +320,7 @@ static status_t process_r(private_ike_mobike_t *this, message_t *message) else if (message->get_exchange_type(message) == INFORMATIONAL) { process_payloads(this, message); - if (this->roam) + if (this->update) { host_t *me, *other; @@ -306,7 +359,7 @@ static status_t build_r(private_ike_mobike_t *this, message_t *message) { this->natd->task.build(&this->natd->task, message); } - if (this->roam) + if (this->update) { update_children(this); } @@ -324,7 +377,6 @@ static status_t process_i(private_ike_mobike_t *this, message_t *message) message->get_payload(message, SECURITY_ASSOCIATION)) { process_payloads(this, message); - return SUCCESS; } else if (message->get_exchange_type(message) == INFORMATIONAL) @@ -341,11 +393,39 @@ static status_t process_i(private_ike_mobike_t *this, message_t *message) { this->natd->task.process(&this->natd->task, message); } - if (this->roam) + if (this->update) { /* update again, as NAT state may have changed */ update_children(this); } + if (this->check) + { + host_t *me_new, *me_old, *other_new, *other_old; + + me_new = message->get_destination(message); + other_new = message->get_source(message); + me_old = this->ike_sa->get_my_host(this->ike_sa); + other_old = this->ike_sa->get_other_host(this->ike_sa); + + if (!me_new->equals(me_new, me_old)) + { + this->update = TRUE; + this->ike_sa->set_my_host(this->ike_sa, me_new->clone(me_new)); + } + if (!other_new->equals(other_new, other_old)) + { + this->update = TRUE; + this->ike_sa->set_other_host(this->ike_sa, other_new->clone(other_new)); + } + if (this->update) + { + /* start the update with the same task */ + this->check = FALSE; + this->address = FALSE; + this->ike_sa->set_pending_updates(this->ike_sa, 1); + return NEED_MORE; + } + } return SUCCESS; } return NEED_MORE; @@ -356,8 +436,9 @@ static status_t process_i(private_ike_mobike_t *this, message_t *message) */ static void roam(private_ike_mobike_t *this, bool address) { - this->roam = TRUE; + this->check = TRUE; this->address = address; + this->natd = ike_natd_create(this->ike_sa, this->initiator); this->ike_sa->set_pending_updates(this->ike_sa, this->ike_sa->get_pending_updates(this->ike_sa) + 1); } @@ -404,6 +485,7 @@ ike_mobike_t *ike_mobike_create(ike_sa_t *ike_sa, bool initiator) private_ike_mobike_t *this = malloc_thing(private_ike_mobike_t); this->public.roam = (void(*)(ike_mobike_t*,bool))roam; + this->public.transmit = (void(*)(ike_mobike_t*,packet_t*))transmit; this->public.task.get_type = (task_type_t(*)(task_t*))get_type; this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; this->public.task.destroy = (void(*)(task_t*))destroy; @@ -421,7 +503,8 @@ ike_mobike_t *ike_mobike_create(ike_sa_t *ike_sa, bool initiator) this->ike_sa = ike_sa; this->initiator = initiator; - this->roam = FALSE; + this->update = FALSE; + this->check = FALSE; this->address = TRUE; this->cookie2 = chunk_empty; this->natd = NULL; diff --git a/src/charon/sa/tasks/ike_mobike.h b/src/charon/sa/tasks/ike_mobike.h index db493c459..cee9da047 100644 --- a/src/charon/sa/tasks/ike_mobike.h +++ b/src/charon/sa/tasks/ike_mobike.h @@ -28,6 +28,7 @@ typedef struct ike_mobike_t ike_mobike_t; #include #include #include +#include /** * @brief Task of type ike_mobike, detects and handles MOBIKE extension. @@ -58,6 +59,18 @@ struct ike_mobike_t { * @param address TRUE to include address list update */ void (*roam)(ike_mobike_t *this, bool address); + + /** + * @brief Transmision hook, called by task manager. + * + * The task manager calls this hook whenever it transmits a packet. It + * allows the mobike task to send the packet on multiple paths to do path + * probing. + * + * @param this calling object + * @param packet the packet to transmit + */ + void (*transmit)(ike_mobike_t *this, packet_t *packet); }; /**