summaryrefslogtreecommitdiff
path: root/src/charon/sa/ike_sa.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/charon/sa/ike_sa.c')
-rw-r--r--src/charon/sa/ike_sa.c459
1 files changed, 329 insertions, 130 deletions
diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c
index 0a996329d..9d7a17e89 100644
--- a/src/charon/sa/ike_sa.c
+++ b/src/charon/sa/ike_sa.c
@@ -6,7 +6,8 @@
*/
/*
- * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
+ * Copyright (C) 2006-2007 Tobias Brunner
+ * Copyright (C) 2006 Daniel Roethlisberger
* Copyright (C) 2005-2006 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
@@ -65,6 +66,9 @@
#include <processing/jobs/send_keepalive_job.h>
#include <processing/jobs/rekey_ike_sa_job.h>
+#ifdef P2P
+#include <sa/tasks/ike_p2p.h>
+#endif
#ifndef RESOLV_CONF
#define RESOLV_CONF "/etc/resolv.conf"
@@ -130,6 +134,13 @@ struct private_ike_sa_t {
*/
host_t *other_host;
+#ifdef P2P
+ /**
+ * Server reflexive host
+ */
+ host_t *server_reflexive_host;
+#endif /* P2P */
+
/**
* Identification used for us
*/
@@ -495,6 +506,10 @@ static void set_condition(private_ike_sa_t *this, ike_condition_t condition,
DBG1(DBG_IKE, "remote host is behind NAT");
this->conditions |= COND_NAT_ANY;
break;
+ case COND_NAT_FAKE:
+ DBG1(DBG_IKE, "faking NAT situation to enforce UDP encapsulation");
+ this->conditions |= COND_NAT_ANY;
+ break;
default:
break;
}
@@ -508,10 +523,12 @@ static void set_condition(private_ike_sa_t *this, ike_condition_t condition,
DBG1(DBG_IKE, "new route to %H found", this->other_host);
break;
case COND_NAT_HERE:
+ case COND_NAT_FAKE:
case COND_NAT_THERE:
set_condition(this, COND_NAT_ANY,
has_condition(this, COND_NAT_HERE) ||
- has_condition(this, COND_NAT_THERE));
+ has_condition(this, COND_NAT_THERE) ||
+ has_condition(this, COND_NAT_FAKE));
break;
default:
break;
@@ -581,7 +598,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 state change: %N => %N",
+ DBG1(DBG_IKE, "IKE_SA '%s' state change: %N => %N",
+ get_name(this),
ike_sa_state_names, this->state,
ike_sa_state_names, state);
@@ -663,14 +681,14 @@ static void set_virtual_ip(private_ike_sa_t *this, bool local, host_t *ip)
{
if (local)
{
- DBG1(DBG_IKE, "installing new virtual IP %H", ip);
if (this->my_virtual_ip)
- {
+ {
DBG1(DBG_IKE, "removing old virtual IP %H", this->my_virtual_ip);
charon->kernel_interface->del_ip(charon->kernel_interface,
this->my_virtual_ip);
this->my_virtual_ip->destroy(this->my_virtual_ip);
}
+ DBG1(DBG_IKE, "installing new virtual IP %H", ip);
if (charon->kernel_interface->add_ip(charon->kernel_interface, ip,
this->my_host) == SUCCESS)
{
@@ -812,8 +830,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);
}
@@ -850,102 +866,91 @@ static void send_notify_response(private_ike_sa_t *this, message_t *request,
response->destroy(response);
}
+#ifdef P2P
/**
- * Implementation of ike_sa_t.process_message.
+ * Implementation of ike_sa_t.get_server_reflexive_host.
*/
-static status_t process_message(private_ike_sa_t *this, message_t *message)
+static host_t *get_server_reflexive_host(private_ike_sa_t *this)
{
- status_t status;
- bool is_request;
-
- is_request = message->get_request(message);
+ return this->server_reflexive_host;
+}
+
+/**
+ * Implementation of ike_sa_t.set_server_reflexive_host.
+ */
+static void set_server_reflexive_host(private_ike_sa_t *this, host_t *host)
+{
+ DESTROY_IF(this->server_reflexive_host);
+ this->server_reflexive_host = host;
+}
+
+/**
+ * Implementation of ike_sa_t.respond
+ */
+static status_t respond(private_ike_sa_t *this, identification_t *peer_id,
+ chunk_t session_id)
+{
+ ike_p2p_t *task = ike_p2p_create(&this->public, TRUE);
+ task->respond(task, peer_id, session_id);
+ this->task_manager->queue_task(this->task_manager, (task_t*)task);
+ return this->task_manager->initiate(this->task_manager);
+}
+
+/**
+ * Implementation of ike_sa_t.callback
+ */
+static status_t callback(private_ike_sa_t *this, identification_t *peer_id)
+{
+ ike_p2p_t *task = ike_p2p_create(&this->public, TRUE);
+ task->callback(task, peer_id);
+ this->task_manager->queue_task(this->task_manager, (task_t*)task);
+ return this->task_manager->initiate(this->task_manager);
+}
+
+/**
+ * Implementation of ike_sa_t.relay
+ */
+static status_t relay(private_ike_sa_t *this, identification_t *requester,
+ chunk_t session_id, chunk_t session_key, linked_list_t *endpoints, bool response)
+{
+ ike_p2p_t *task = ike_p2p_create(&this->public, TRUE);
+ task->relay(task, requester, session_id, session_key, endpoints, response);
+ this->task_manager->queue_task(this->task_manager, (task_t*)task);
+ return this->task_manager->initiate(this->task_manager);
+}
+
+/**
+ * Implementation of ike_sa_t.initiate_mediation
+ */
+static status_t initiate_mediation(private_ike_sa_t *this, peer_cfg_t *mediated_cfg)
+{
+ ike_p2p_t *task = ike_p2p_create(&this->public, TRUE);
+ task->connect(task, mediated_cfg->get_peer_id(mediated_cfg));
+ this->task_manager->queue_task(this->task_manager, (task_t*)task);
+ return this->task_manager->initiate(this->task_manager);
+}
+
+/**
+ * Implementation of ike_sa_t.initiate_mediated
+ */
+static status_t initiate_mediated(private_ike_sa_t *this, host_t *me, host_t *other,
+ linked_list_t *childs)
+{
+ this->my_host = me->clone(me);
+ this->other_host = other->clone(other);
- status = message->parse_body(message, this->crypter_in, this->signer_in);
- if (status != SUCCESS)
- {
-
- if (is_request)
- {
- switch (status)
- {
- case NOT_SUPPORTED:
- DBG1(DBG_IKE, "ciritcal unknown payloads found");
- if (is_request)
- {
- send_notify_response(this, message, UNSUPPORTED_CRITICAL_PAYLOAD);
- }
- break;
- case PARSE_ERROR:
- DBG1(DBG_IKE, "message parsing failed");
- if (is_request)
- {
- send_notify_response(this, message, INVALID_SYNTAX);
- }
- break;
- case VERIFY_ERROR:
- DBG1(DBG_IKE, "message verification failed");
- if (is_request)
- {
- send_notify_response(this, message, INVALID_SYNTAX);
- }
- break;
- case FAILED:
- DBG1(DBG_IKE, "integrity check failed");
- /* ignored */
- break;
- case INVALID_STATE:
- DBG1(DBG_IKE, "found encrypted message, but no keys available");
- if (is_request)
- {
- send_notify_response(this, message, INVALID_SYNTAX);
- }
- default:
- break;
- }
- }
- DBG1(DBG_IKE, "%N %s with message ID %d processing failed",
- exchange_type_names, message->get_exchange_type(message),
- message->get_request(message) ? "request" : "response",
- message->get_message_id(message));
- return status;
- }
- else
+ task_t *task;
+ child_cfg_t *child_cfg;
+ iterator_t *iterator = childs->create_iterator(childs, TRUE);
+ while (iterator->iterate(iterator, (void**)&child_cfg))
{
- host_t *me, *other;
-
- me = message->get_destination(message);
- other = message->get_source(message);
-
- /* if this IKE_SA is virgin, we check for a config */
- if (this->ike_cfg == NULL)
- {
- job_t *job;
- this->ike_cfg = charon->backends->get_ike_cfg(charon->backends,
- me, other);
- if (this->ike_cfg == NULL)
- {
- /* no config found for these hosts, destroy */
- DBG1(DBG_IKE, "no IKE config found for %H...%H, sending %N",
- me, other, notify_type_names, NO_PROPOSAL_CHOSEN);
- send_notify_response(this, message, NO_PROPOSAL_CHOSEN);
- return DESTROY_ME;
- }
- /* add a timeout if peer does not establish it completely */
- job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, FALSE);
- charon->scheduler->schedule_job(charon->scheduler, job,
- HALF_OPEN_IKE_SA_TIMEOUT);
- }
-
- /* 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);
- }
- return this->task_manager->process_message(this->task_manager, message);
+ task = (task_t*)child_create_create(&this->public, child_cfg);
+ this->task_manager->queue_task(this->task_manager, task);
}
+ iterator->destroy(iterator);
+ return this->task_manager->initiate(this->task_manager);
}
+#endif /* P2P */
/**
* Implementation of ike_sa_t.initiate.
@@ -956,8 +961,11 @@ 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))
+ if (this->other_host->is_anyaddr(this->other_host)
+#ifdef P2P
+ && !this->peer_cfg->get_mediated_by(this->peer_cfg)
+#endif /* P2P */
+ )
{
child_cfg->destroy(child_cfg);
SIG(IKE_UP_START, "initiating IKE_SA");
@@ -975,13 +983,41 @@ static status_t initiate(private_ike_sa_t *this, child_cfg_t *child_cfg)
this->task_manager->queue_task(this->task_manager, task);
task = (task_t*)ike_config_create(&this->public, TRUE);
this->task_manager->queue_task(this->task_manager, task);
- task = (task_t*)ike_mobike_create(&this->public, TRUE);
+ if (this->peer_cfg->use_mobike(this->peer_cfg))
+ {
+ task = (task_t*)ike_mobike_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, task);
+ }
+#ifdef P2P
+ task = (task_t*)ike_p2p_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, task);
+#endif /* P2P */
+ }
+
+#ifdef P2P
+ if (this->peer_cfg->get_mediated_by(this->peer_cfg))
+ {
+ // mediated connection, initiate mediation process
+ job_t *job = (job_t*)initiate_mediation_job_create(this->ike_sa_id, child_cfg);
+ child_cfg->destroy(child_cfg);
+ charon->processor->queue_job(charon->processor, job);
+ return SUCCESS;
+ }
+ else if (this->peer_cfg->is_mediation(this->peer_cfg))
+ {
+ 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");
+ }
+ }
+ else
+#endif /* P2P */
+ {
+ // normal IKE_SA with CHILD_SA
+ task = (task_t*)child_create_create(&this->public, child_cfg);
+ child_cfg->destroy(child_cfg);
this->task_manager->queue_task(this->task_manager, task);
}
-
- task = (task_t*)child_create_create(&this->public, child_cfg);
- child_cfg->destroy(child_cfg);
- this->task_manager->queue_task(this->task_manager, task);
return this->task_manager->initiate(this->task_manager);
}
@@ -990,7 +1026,7 @@ static status_t initiate(private_ike_sa_t *this, child_cfg_t *child_cfg)
* Implementation of ike_sa_t.acquire.
*/
static status_t acquire(private_ike_sa_t *this, u_int32_t reqid)
-{
+{// FIXME: P2P-NAT-T
child_cfg_t *child_cfg;
iterator_t *iterator;
child_sa_t *current, *child_sa = NULL;
@@ -1037,8 +1073,11 @@ static status_t acquire(private_ike_sa_t *this, u_int32_t reqid)
this->task_manager->queue_task(this->task_manager, task);
task = (task_t*)ike_config_create(&this->public, TRUE);
this->task_manager->queue_task(this->task_manager, task);
- task = (task_t*)ike_mobike_create(&this->public, TRUE);
- this->task_manager->queue_task(this->task_manager, task);
+ if (this->peer_cfg->use_mobike(this->peer_cfg))
+ {
+ task = (task_t*)ike_mobike_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, task);
+ }
}
child_cfg = child_sa->get_config(child_sa);
@@ -1162,12 +1201,156 @@ static status_t unroute(private_ike_sa_t *this, u_int32_t reqid)
}
return SUCCESS;
}
+/**
+ * Implementation of ike_sa_t.process_message.
+ */
+static status_t process_message(private_ike_sa_t *this, message_t *message)
+{
+ status_t status;
+ bool is_request;
+
+ is_request = message->get_request(message);
+
+ status = message->parse_body(message, this->crypter_in, this->signer_in);
+ if (status != SUCCESS)
+ {
+
+ if (is_request)
+ {
+ switch (status)
+ {
+ case NOT_SUPPORTED:
+ DBG1(DBG_IKE, "ciritcal unknown payloads found");
+ if (is_request)
+ {
+ send_notify_response(this, message, UNSUPPORTED_CRITICAL_PAYLOAD);
+ }
+ break;
+ case PARSE_ERROR:
+ DBG1(DBG_IKE, "message parsing failed");
+ if (is_request)
+ {
+ send_notify_response(this, message, INVALID_SYNTAX);
+ }
+ break;
+ case VERIFY_ERROR:
+ DBG1(DBG_IKE, "message verification failed");
+ if (is_request)
+ {
+ send_notify_response(this, message, INVALID_SYNTAX);
+ }
+ break;
+ case FAILED:
+ DBG1(DBG_IKE, "integrity check failed");
+ /* ignored */
+ break;
+ case INVALID_STATE:
+ DBG1(DBG_IKE, "found encrypted message, but no keys available");
+ if (is_request)
+ {
+ send_notify_response(this, message, INVALID_SYNTAX);
+ }
+ default:
+ break;
+ }
+ }
+ DBG1(DBG_IKE, "%N %s with message ID %d processing failed",
+ exchange_type_names, message->get_exchange_type(message),
+ message->get_request(message) ? "request" : "response",
+ message->get_message_id(message));
+ return status;
+ }
+ else
+ {
+ host_t *me, *other;
+ private_ike_sa_t *new;
+ iterator_t *iterator;
+ child_sa_t *child;
+ bool has_routed = FALSE;
+
+ me = message->get_destination(message);
+ other = message->get_source(message);
+
+ /* if this IKE_SA is virgin, we check for a config */
+ if (this->ike_cfg == NULL)
+ {
+ job_t *job;
+ this->ike_cfg = charon->backends->get_ike_cfg(charon->backends,
+ me, other);
+ if (this->ike_cfg == NULL)
+ {
+ /* no config found for these hosts, destroy */
+ DBG1(DBG_IKE, "no IKE config found for %H...%H, sending %N",
+ me, other, notify_type_names, NO_PROPOSAL_CHOSEN);
+ send_notify_response(this, message, NO_PROPOSAL_CHOSEN);
+ return DESTROY_ME;
+ }
+ /* add a timeout if peer does not establish it completely */
+ job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, FALSE);
+ charon->scheduler->schedule_job(charon->scheduler, job,
+ HALF_OPEN_IKE_SA_TIMEOUT);
+ }
+
+ /* 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);
+ }
+ status = this->task_manager->process_message(this->task_manager, message);
+ if (status != DESTROY_ME)
+ {
+ return status;
+ }
+ /* if IKE_SA gets closed for any reasons, reroute routed children */
+ iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+ while (iterator->iterate(iterator, (void**)&child))
+ {
+ if (child->get_state(child) == CHILD_ROUTED)
+ {
+ has_routed = TRUE;
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+ if (!has_routed)
+ {
+ return status;
+ }
+ /* move routed children to a new IKE_SA, apply connection info */
+ new = (private_ike_sa_t*)charon->ike_sa_manager->checkout_new(
+ charon->ike_sa_manager, TRUE);
+ set_peer_cfg(new, this->peer_cfg);
+ new->other_host->destroy(new->other_host);
+ new->other_host = this->other_host->clone(this->other_host);
+ if (!has_condition(this, COND_NAT_THERE))
+ {
+ new->other_host->set_port(new->other_host, IKEV2_UDP_PORT);
+ }
+ if (this->my_virtual_ip)
+ {
+ set_virtual_ip(new, TRUE, this->my_virtual_ip);
+ }
+ iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+ while (iterator->iterate(iterator, (void**)&child))
+ {
+ if (child->get_state(child) == CHILD_ROUTED)
+ {
+ route(new, child->get_config(child));
+ }
+ }
+ iterator->destroy(iterator);
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, &new->public);
+ return status;
+ }
+}
/**
* Implementation of ike_sa_t.retransmit.
*/
static status_t retransmit(private_ike_sa_t *this, u_int32_t message_id)
-{
+{// FIXME: P2P-NAT-T
this->time.outbound = time(NULL);
if (this->task_manager->retransmit(this->task_manager, message_id) != SUCCESS)
{
@@ -1283,9 +1466,12 @@ static status_t retransmit(private_ike_sa_t *this, u_int32_t message_id)
{
task = (task_t*)child_create_create(&new->public, child_cfg);
new->task_manager->queue_task(new->task_manager, task);
+ }
+ if (this->peer_cfg->use_mobike(this->peer_cfg))
+ {
+ task = (task_t*)ike_mobike_create(&new->public, TRUE);
+ new->task_manager->queue_task(new->task_manager, task);
}
- task = (task_t*)ike_mobike_create(&new->public, TRUE);
- new->task_manager->queue_task(new->task_manager, task);
new->task_manager->initiate(new->task_manager);
}
charon->ike_sa_manager->checkin(charon->ike_sa_manager, &new->public);
@@ -1684,9 +1870,12 @@ static status_t delete_(private_ike_sa_t *this)
ike_delete = ike_delete_create(&this->public, TRUE);
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");
+ break;
default:
- DBG1(DBG_IKE, "destroying IKE_SA in state %N without notification",
- ike_sa_state_names, this->state);
+ SIG(IKE_DOWN_SUCCESS, "destroying IKE_SA in state %N "
+ "without notification", ike_sa_state_names, this->state);
break;
}
return DESTROY_ME;
@@ -1743,30 +1932,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))
@@ -1995,6 +2173,15 @@ static void destroy(private_ike_sa_t *this)
offsetof(host_t, destroy));
this->additional_addresses->destroy_offset(this->additional_addresses,
offsetof(host_t, destroy));
+#ifdef P2P
+ if (this->peer_cfg && this->peer_cfg->is_mediation(this->peer_cfg) &&
+ !this->ike_sa_id->is_initiator(this->ike_sa_id))
+ {
+ // mediation server
+ charon->mediation_manager->remove(charon->mediation_manager, this->ike_sa_id);
+ }
+ DESTROY_IF(this->server_reflexive_host);
+#endif /* P2P */
DESTROY_IF(this->my_host);
DESTROY_IF(this->other_host);
@@ -2077,6 +2264,15 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
this->public.set_virtual_ip = (void (*)(ike_sa_t*,bool,host_t*))set_virtual_ip;
this->public.get_virtual_ip = (host_t* (*)(ike_sa_t*,bool))get_virtual_ip;
this->public.add_dns_server = (void (*)(ike_sa_t*,host_t*))add_dns_server;
+#ifdef P2P
+ this->public.get_server_reflexive_host = (host_t* (*)(ike_sa_t*)) get_server_reflexive_host;
+ this->public.set_server_reflexive_host = (void (*)(ike_sa_t*,host_t*)) set_server_reflexive_host;
+ this->public.initiate_mediation = (status_t (*)(ike_sa_t*,peer_cfg_t*)) initiate_mediation;
+ this->public.initiate_mediated = (status_t (*)(ike_sa_t*,host_t*,host_t*,linked_list_t*)) initiate_mediated;
+ this->public.relay = (status_t (*)(ike_sa_t*,identification_t*,chunk_t,chunk_t,linked_list_t*,bool)) relay;
+ this->public.callback = (status_t (*)(ike_sa_t*,identification_t*)) callback;
+ this->public.respond = (status_t (*)(ike_sa_t*,identification_t*,chunk_t)) respond;
+#endif /* P2P */
/* initialize private fields */
this->ike_sa_id = ike_sa_id->clone(ike_sa_id);
@@ -2111,6 +2307,9 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
this->additional_addresses = linked_list_create();
this->pending_updates = 0;
this->keyingtry = 0;
+#ifdef P2P
+ this->server_reflexive_host = NULL;
+#endif /* P2P */
return &this->public;
}