diff options
Diffstat (limited to 'src/charon/sa/ike_sa.c')
-rw-r--r-- | src/charon/sa/ike_sa.c | 213 |
1 files changed, 149 insertions, 64 deletions
diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c index 384226380..7b2608e07 100644 --- a/src/charon/sa/ike_sa.c +++ b/src/charon/sa/ike_sa.c @@ -15,7 +15,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: ike_sa.c 4106 2008-06-25 11:40:50Z martin $ + * $Id: ike_sa.c 4394 2008-10-09 08:25:11Z martin $ */ #include <sys/time.h> @@ -170,6 +170,11 @@ struct private_ike_sa_t { identification_t *other_id; /** + * EAP Identity exchange in EAP-Identity method + */ + identification_t *eap_identity;; + + /** * set of extensions the peer supports */ ike_extension_t extensions; @@ -250,11 +255,21 @@ struct private_ike_sa_t { linked_list_t *additional_addresses; /** + * previously value of received DESTINATION_IP hash + */ + chunk_t nat_detection_dest; + + /** * number pending UPDATE_SA_ADDRESS (MOBIKE) */ u_int32_t pending_updates; /** + * NAT keep alive interval + */ + u_int32_t keepalive_interval; + + /** * Timestamps for this IKE_SA */ struct { @@ -454,7 +469,7 @@ static void send_keepalive(private_ike_sa_t *this) send_keepalive_job_t *job; time_t last_out, now, diff; - if (!(this->conditions & COND_NAT_HERE)) + if (!(this->conditions & COND_NAT_HERE) || this->keepalive_interval == 0) { /* disable keep alives if we are not NATed anymore */ return; } @@ -464,7 +479,7 @@ static void send_keepalive(private_ike_sa_t *this) diff = now - last_out; - if (diff >= KEEPALIVE_INTERVAL) + if (diff >= this->keepalive_interval) { packet_t *packet; chunk_t data; @@ -482,7 +497,7 @@ static void send_keepalive(private_ike_sa_t *this) } job = send_keepalive_job_create(this->ike_sa_id); charon->scheduler->schedule_job(charon->scheduler, (job_t*)job, - (KEEPALIVE_INTERVAL - diff) * 1000); + (this->keepalive_interval - diff) * 1000); } /** @@ -616,8 +631,20 @@ static status_t send_dpd(private_ike_sa_t *this) { /* to long ago, initiate dead peer detection */ task_t *task; + ike_mobike_t *mobike; - task = (task_t*)ike_dpd_create(TRUE); + if (supports_extension(this, EXT_MOBIKE) && + has_condition(this, COND_NAT_HERE)) + { + /* use mobike enabled DPD to detect NAT mapping changes */ + mobike = ike_mobike_create(&this->public, TRUE); + mobike->dpd(mobike); + task = &mobike->task; + } + else + { + task = (task_t*)ike_dpd_create(TRUE); + } diff = 0; DBG1(DBG_IKE, "sending DPD request"); @@ -645,8 +672,8 @@ static ike_sa_state_t get_state(private_ike_sa_t *this) */ static void set_state(private_ike_sa_t *this, ike_sa_state_t state) { - DBG1(DBG_IKE, "IKE_SA '%s' state change: %N => %N", - get_name(this), + DBG2(DBG_IKE, "IKE_SA %s[%d] state change: %N => %N", + get_name(this), this->unique_id, ike_sa_state_names, this->state, ike_sa_state_names, state); @@ -807,7 +834,26 @@ static iterator_t* create_additional_address_iterator(private_ike_sa_t *this) return this->additional_addresses->create_iterator( this->additional_addresses, TRUE); } - + +/** + * Implementation of ike_sa_t.has_mapping_changed + */ +static bool has_mapping_changed(private_ike_sa_t *this, chunk_t hash) +{ + if (this->nat_detection_dest.ptr == NULL) + { + this->nat_detection_dest = chunk_clone(hash); + return FALSE; + } + if (chunk_equals(hash, this->nat_detection_dest)) + { + return FALSE; + } + free(this->nat_detection_dest.ptr); + this->nat_detection_dest = chunk_clone(hash); + return TRUE; +} + /** * Implementation of ike_sa_t.set_pending_updates. */ @@ -831,11 +877,6 @@ static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other) { bool update = FALSE; - if (supports_extension(this, EXT_MOBIKE)) - { /* if peer speaks mobike, address updates are explicit only */ - return; - } - if (me == NULL) { me = this->my_host; @@ -1041,18 +1082,31 @@ static void resolve_hosts(private_ike_sa_t *this) { host_t *host; - host = host_create_from_dns(this->ike_cfg->get_my_addr(this->ike_cfg), 0, - IKEV2_UDP_PORT); + host = host_create_from_dns(this->ike_cfg->get_other_addr(this->ike_cfg), + 0, IKEV2_UDP_PORT); if (host) { - set_my_host(this, host); + set_other_host(this, host); } - host = host_create_from_dns(this->ike_cfg->get_other_addr(this->ike_cfg), + + host = host_create_from_dns(this->ike_cfg->get_my_addr(this->ike_cfg), this->my_host->get_family(this->my_host), IKEV2_UDP_PORT); + + if (host && host->is_anyaddr(host) && + !this->other_host->is_anyaddr(this->other_host)) + { + host->destroy(host); + host = charon->kernel_interface->get_source_addr( + charon->kernel_interface, this->other_host, NULL); + if (host) + { + host->set_port(host, IKEV2_UDP_PORT); + } + } if (host) { - set_other_host(this, host); + set_my_host(this, host); } } @@ -1074,8 +1128,8 @@ static status_t initiate_with_reqid(private_ike_sa_t *this, child_cfg_t *child_c ) { child_cfg->destroy(child_cfg); - SIG(IKE_UP_START, "initiating IKE_SA"); - SIG(IKE_UP_FAILED, "unable to initiate to %%any"); + SIG_IKE(UP_START, "initiating IKE_SA"); + SIG_IKE(UP_FAILED, "unable to initiate to %%any"); return DESTROY_ME; } @@ -1112,7 +1166,7 @@ static status_t initiate_with_reqid(private_ike_sa_t *this, child_cfg_t *child_c /* mediation connection */ if (this->state == IKE_ESTABLISHED) { /* FIXME: we should try to find a better solution to this */ - SIG(CHILD_UP_SUCCESS, "mediation connection is already up and running"); + SIG_CHD(UP_SUCCESS, NULL, "mediation connection is already up and running"); } DESTROY_IF(child_cfg); } @@ -1162,8 +1216,8 @@ static status_t acquire(private_ike_sa_t *this, u_int32_t reqid) if (this->state == IKE_DELETING) { - SIG(CHILD_UP_START, "acquiring CHILD_SA on kernel request"); - SIG(CHILD_UP_FAILED, "acquiring CHILD_SA (reqid %d) failed: " + SIG_CHD(UP_START, NULL, "acquiring CHILD_SA on kernel request"); + SIG_CHD(UP_FAILED, NULL, "acquiring CHILD_SA {reqid %d} failed: " "IKE_SA is deleting", reqid); return FAILED; } @@ -1181,8 +1235,8 @@ static status_t acquire(private_ike_sa_t *this, u_int32_t reqid) iterator->destroy(iterator); if (!child_sa) { - SIG(CHILD_UP_START, "acquiring CHILD_SA on kernel request"); - SIG(CHILD_UP_FAILED, "acquiring CHILD_SA (reqid %d) failed: " + SIG_CHD(UP_START, NULL, "acquiring CHILD_SA on kernel request"); + SIG_CHD(UP_FAILED, NULL, "acquiring CHILD_SA {reqid %d} failed: " "CHILD_SA not found", reqid); return FAILED; } @@ -1204,7 +1258,7 @@ static status_t route(private_ike_sa_t *this, child_cfg_t *child_cfg) host_t *me, *other; status_t status; - SIG(CHILD_ROUTE_START, "routing CHILD_SA"); + SIG_CHD(ROUTE_START, NULL, "routing CHILD_SA"); /* check if not already routed*/ iterator = this->child_sas->create_iterator(this->child_sas, TRUE); @@ -1214,7 +1268,7 @@ static status_t route(private_ike_sa_t *this, child_cfg_t *child_cfg) streq(child_sa->get_name(child_sa), child_cfg->get_name(child_cfg))) { iterator->destroy(iterator); - SIG(CHILD_ROUTE_FAILED, "CHILD_SA with such a config already routed"); + SIG_CHD(ROUTE_FAILED, child_sa, "CHILD_SA with such a config already routed"); return FAILED; } } @@ -1224,7 +1278,7 @@ static status_t route(private_ike_sa_t *this, child_cfg_t *child_cfg) { case IKE_DELETING: case IKE_REKEYING: - SIG(CHILD_ROUTE_FAILED, + SIG_CHD(ROUTE_FAILED, NULL, "unable to route CHILD_SA, as its IKE_SA gets deleted"); return FAILED; case IKE_CREATED: @@ -1253,17 +1307,17 @@ static status_t route(private_ike_sa_t *this, child_cfg_t *child_cfg) my_ts = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, me); other_ts = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL, other); status = child_sa->add_policies(child_sa, my_ts, other_ts, - child_cfg->get_mode(child_cfg)); + child_cfg->get_mode(child_cfg), PROTO_NONE); my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy)); other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy)); if (status == SUCCESS) { this->child_sas->insert_last(this->child_sas, child_sa); - SIG(CHILD_ROUTE_SUCCESS, "CHILD_SA routed"); + SIG_CHD(ROUTE_SUCCESS, child_sa, "CHILD_SA routed"); } else { - SIG(CHILD_ROUTE_FAILED, "routing CHILD_SA failed"); + SIG_CHD(ROUTE_FAILED, child_sa, "routing CHILD_SA failed"); } return status; } @@ -1277,7 +1331,7 @@ static status_t unroute(private_ike_sa_t *this, u_int32_t reqid) child_sa_t *child_sa; bool found = FALSE; - SIG(CHILD_UNROUTE_START, "unrouting CHILD_SA"); + SIG_CHD(UNROUTE_START, NULL, "unrouting CHILD_SA"); /* find CHILD_SA in ROUTED state */ iterator = this->child_sas->create_iterator(this->child_sas, TRUE); @@ -1287,7 +1341,7 @@ static status_t unroute(private_ike_sa_t *this, u_int32_t reqid) child_sa->get_reqid(child_sa) == reqid) { iterator->remove(iterator); - SIG(CHILD_UNROUTE_SUCCESS, "CHILD_SA unrouted"); + SIG_CHD(UNROUTE_SUCCESS, child_sa, "CHILD_SA unrouted"); child_sa->destroy(child_sa); found = TRUE; break; @@ -1297,7 +1351,7 @@ static status_t unroute(private_ike_sa_t *this, u_int32_t reqid) if (!found) { - SIG(CHILD_UNROUTE_FAILED, "CHILD_SA to unroute not found"); + SIG_CHD(UNROUTE_FAILED, NULL, "CHILD_SA to unroute not found"); return FAILED; } /* if we are not established, and we have no more routed childs, remove whole SA */ @@ -1397,13 +1451,15 @@ static status_t process_message(private_ike_sa_t *this, message_t *message) charon->scheduler->schedule_job(charon->scheduler, job, HALF_OPEN_IKE_SA_TIMEOUT); } - + this->time.inbound = time(NULL); /* check if message is trustworthy, and update host information */ if (this->state == IKE_CREATED || this->state == IKE_CONNECTING || message->get_exchange_type(message) != IKE_SA_INIT) { - update_hosts(this, me, other); - this->time.inbound = time(NULL); + if (!supports_extension(this, EXT_MOBIKE)) + { /* with MOBIKE, we do no implicit updates */ + update_hosts(this, me, other); + } } status = this->task_manager->process_message(this->task_manager, message); if (status != DESTROY_ME) @@ -1528,6 +1584,23 @@ static void set_other_id(private_ike_sa_t *this, identification_t *other) } /** + * Implementation of ike_sa_t.get_eap_identity. + */ +static identification_t* get_eap_identity(private_ike_sa_t *this) +{ + return this->eap_identity; +} + +/** + * Implementation of ike_sa_t.set_eap_identity. + */ +static void set_eap_identity(private_ike_sa_t *this, identification_t *id) +{ + DESTROY_IF(this->eap_identity); + this->eap_identity = id; +} + +/** * Implementation of ike_sa_t.derive_keys. */ static status_t derive_keys(private_ike_sa_t *this, @@ -1866,10 +1939,10 @@ static status_t delete_(private_ike_sa_t *this) this->task_manager->queue_task(this->task_manager, &ike_delete->task); return this->task_manager->initiate(this->task_manager); case IKE_CREATED: - SIG(IKE_DOWN_SUCCESS, "deleting unestablished IKE_SA"); + SIG_IKE(DOWN_SUCCESS, "deleting unestablished IKE_SA"); break; default: - SIG(IKE_DOWN_SUCCESS, "destroying IKE_SA in state %N " + SIG_IKE(DOWN_SUCCESS, "destroying IKE_SA in state %N " "without notification", ike_sa_state_names, this->state); break; } @@ -2073,19 +2146,19 @@ static status_t retransmit(private_ike_sa_t *this, u_int32_t message_id) this->keyingtry++; if (tries == 0 || tries > this->keyingtry) { - SIG(IKE_UP_FAILED, "peer not responding, trying again " + SIG_IKE(UP_FAILED, "peer not responding, trying again " "(%d/%d) in background ", this->keyingtry + 1, tries); reset(this); return this->task_manager->initiate(this->task_manager); } - SIG(IKE_UP_FAILED, "establishing IKE_SA failed, peer not responding"); + SIG_IKE(UP_FAILED, "establishing IKE_SA failed, peer not responding"); break; } case IKE_DELETING: - SIG(IKE_DOWN_FAILED, "proper IKE_SA delete failed, peer not responding"); + SIG_IKE(DOWN_FAILED, "proper IKE_SA delete failed, peer not responding"); break; case IKE_REKEYING: - SIG(IKE_REKEY_FAILED, "rekeying IKE_SA failed, peer not responding"); + SIG_IKE(REKEY_FAILED, "rekeying IKE_SA failed, peer not responding"); /* FALL */ default: reestablish(this); @@ -2101,24 +2174,29 @@ static status_t retransmit(private_ike_sa_t *this, u_int32_t message_id) */ static void set_auth_lifetime(private_ike_sa_t *this, u_int32_t lifetime) { - job_t *job; u_int32_t reduction = this->peer_cfg->get_over_time(this->peer_cfg); + u_int32_t reauth_time = time(NULL) + lifetime - reduction; - this->time.reauth = time(NULL) + lifetime - reduction; - job = (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, TRUE); - if (lifetime < reduction) { DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, starting reauthentication", lifetime); - charon->processor->queue_job(charon->processor, job); + charon->processor->queue_job(charon->processor, + (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, TRUE)); } - else + else if (this->time.reauth == 0 || this->time.reauth > reauth_time) { + this->time.reauth = reauth_time; DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, scheduling reauthentication" " in %ds", lifetime, lifetime - reduction); - charon->scheduler->schedule_job(charon->scheduler, job, - (lifetime - reduction) * 1000); + charon->scheduler->schedule_job(charon->scheduler, + (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, TRUE), + (lifetime - reduction) * 1000); + } + else + { + DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, reauthentication already " + "scheduled in %ds", lifetime, this->time.reauth - time(NULL)); } } @@ -2127,7 +2205,7 @@ static void set_auth_lifetime(private_ike_sa_t *this, u_int32_t lifetime) */ static status_t roam(private_ike_sa_t *this, bool address) { - host_t *me, *other; + host_t *src; ike_mobike_t *mobike; switch (this->state) @@ -2151,21 +2229,19 @@ static status_t roam(private_ike_sa_t *this, bool address) return SUCCESS; } - /* get best address pair to use */ - other = this->other_host; - me = charon->kernel_interface->get_source_addr(charon->kernel_interface, - other); - - if (me) + /* keep existing path if possible */ + src = charon->kernel_interface->get_source_addr(charon->kernel_interface, + this->other_host, this->my_host); + if (src) { - if (me->ip_equals(me, this->my_host) && - other->ip_equals(other, this->other_host)) + if (src->ip_equals(src, this->my_host)) { - DBG2(DBG_IKE, "keeping connection path %H - %H", this->other_host, me); - me->destroy(me); + DBG2(DBG_IKE, "keeping connection path %H - %H", + src, this->other_host); + src->destroy(src); return SUCCESS; } - me->destroy(me); + src->destroy(src); } /* update addresses with mobike, if supported ... */ @@ -2177,7 +2253,7 @@ static status_t roam(private_ike_sa_t *this, bool address) this->task_manager->queue_task(this->task_manager, (task_t*)mobike); return this->task_manager->initiate(this->task_manager); } - DBG1(DBG_IKE, "reauthenticating IKE_SA due address change"); + DBG1(DBG_IKE, "reauthenticating IKE_SA due to address change"); /* ... reauth if not */ return reauth(this); } @@ -2453,11 +2529,13 @@ static void destroy(private_ike_sa_t *this) DESTROY_IF(this->server_reflexive_host); chunk_free(&this->connect_id); #endif /* ME */ + free(this->nat_detection_dest.ptr); DESTROY_IF(this->my_host); DESTROY_IF(this->other_host); DESTROY_IF(this->my_id); DESTROY_IF(this->other_id); + DESTROY_IF(this->eap_identity); DESTROY_IF(this->ike_cfg); DESTROY_IF(this->peer_cfg); @@ -2502,6 +2580,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->public.set_my_id = (void (*)(ike_sa_t*,identification_t*)) set_my_id; this->public.get_other_id = (identification_t* (*)(ike_sa_t*)) get_other_id; this->public.set_other_id = (void (*)(ike_sa_t*,identification_t*)) set_other_id; + this->public.get_eap_identity = (identification_t* (*)(ike_sa_t*)) get_eap_identity; + this->public.set_eap_identity = (void (*)(ike_sa_t*,identification_t*)) set_eap_identity; this->public.enable_extension = (void(*)(ike_sa_t*, ike_extension_t extension))enable_extension; this->public.supports_extension = (bool(*)(ike_sa_t*, ike_extension_t extension))supports_extension; this->public.set_condition = (void (*)(ike_sa_t*, ike_condition_t,bool)) set_condition; @@ -2511,6 +2591,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->public.is_ike_initiator = (bool (*)(ike_sa_t*))is_ike_initiator; this->public.create_additional_address_iterator = (iterator_t*(*)(ike_sa_t*))create_additional_address_iterator; this->public.add_additional_address = (void(*)(ike_sa_t*, host_t *host))add_additional_address; + this->public.has_mapping_changed = (bool(*)(ike_sa_t*, chunk_t hash))has_mapping_changed; this->public.retransmit = (status_t (*)(ike_sa_t *, u_int32_t)) retransmit; this->public.delete = (status_t (*)(ike_sa_t*))delete_; this->public.destroy = (void (*)(ike_sa_t*))destroy; @@ -2560,6 +2641,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->other_host = host_create_from_string("0.0.0.0", IKEV2_UDP_PORT); this->my_id = identification_create_from_encoding(ID_ANY, chunk_empty); this->other_id = identification_create_from_encoding(ID_ANY, chunk_empty); + this->eap_identity = NULL; this->extensions = 0; this->conditions = 0; this->selected_proposal = NULL; @@ -2572,6 +2654,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->skp_build = chunk_empty; this->child_prf = NULL; this->state = IKE_CREATED; + this->keepalive_interval = lib->settings->get_time(lib->settings, + "charon.keep_alive", KEEPALIVE_INTERVAL); this->time.inbound = this->time.outbound = time(NULL); this->time.established = 0; this->time.rekey = 0; @@ -2587,6 +2671,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->other_virtual_ip = NULL; this->dns_servers = linked_list_create(); this->additional_addresses = linked_list_create(); + this->nat_detection_dest = chunk_empty; this->pending_updates = 0; this->keyingtry = 0; this->ike_initiator = FALSE; |