summaryrefslogtreecommitdiff
path: root/src/charon/sa
diff options
context:
space:
mode:
Diffstat (limited to 'src/charon/sa')
-rw-r--r--src/charon/sa/authenticators/eap/eap_method.c6
-rw-r--r--src/charon/sa/authenticators/eap/eap_method.h3
-rw-r--r--src/charon/sa/authenticators/eap_authenticator.c3
-rw-r--r--src/charon/sa/child_sa.c411
-rw-r--r--src/charon/sa/child_sa.h205
-rw-r--r--src/charon/sa/ike_sa.c96
-rw-r--r--src/charon/sa/ike_sa.h22
-rw-r--r--src/charon/sa/ike_sa_manager.c1365
-rw-r--r--src/charon/sa/ike_sa_manager.h64
-rw-r--r--src/charon/sa/keymat.c55
-rw-r--r--src/charon/sa/keymat.h15
-rw-r--r--src/charon/sa/task_manager.c292
-rw-r--r--src/charon/sa/task_manager.h14
-rw-r--r--src/charon/sa/tasks/child_create.c242
-rw-r--r--src/charon/sa/tasks/child_delete.c36
-rw-r--r--src/charon/sa/tasks/child_delete.h8
-rw-r--r--src/charon/sa/tasks/child_rekey.c112
-rw-r--r--src/charon/sa/tasks/child_rekey.h8
-rw-r--r--src/charon/sa/tasks/ike_auth.c76
-rw-r--r--src/charon/sa/tasks/ike_config.c106
-rw-r--r--src/charon/sa/tasks/ike_init.c69
-rw-r--r--src/charon/sa/tasks/ike_mobike.c21
-rw-r--r--src/charon/sa/tasks/ike_rekey.c48
23 files changed, 2109 insertions, 1168 deletions
diff --git a/src/charon/sa/authenticators/eap/eap_method.c b/src/charon/sa/authenticators/eap/eap_method.c
index 11b12fb49..6babab212 100644
--- a/src/charon/sa/authenticators/eap/eap_method.c
+++ b/src/charon/sa/authenticators/eap/eap_method.c
@@ -12,7 +12,7 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
- * $Id: eap_method.c 4269 2008-08-21 12:10:07Z martin $
+ * $Id: eap_method.c 4882 2009-02-18 19:57:15Z tobias $
*/
#include "eap_method.h"
@@ -28,7 +28,9 @@ ENUM_NEXT(eap_type_names, EAP_SIM, EAP_SIM, EAP_GTC,
"EAP_SIM");
ENUM_NEXT(eap_type_names, EAP_AKA, EAP_AKA, EAP_SIM,
"EAP_AKA");
-ENUM_NEXT(eap_type_names, EAP_EXPANDED, EAP_EXPERIMENTAL, EAP_AKA,
+ENUM_NEXT(eap_type_names, EAP_MSCHAPV2, EAP_MSCHAPV2, EAP_AKA,
+ "EAP_MSCHAPV2");
+ENUM_NEXT(eap_type_names, EAP_EXPANDED, EAP_EXPERIMENTAL, EAP_MSCHAPV2,
"EAP_EXPANDED",
"EAP_EXPERIMENTAL");
ENUM_END(eap_type_names, EAP_EXPERIMENTAL);
diff --git a/src/charon/sa/authenticators/eap/eap_method.h b/src/charon/sa/authenticators/eap/eap_method.h
index 663117931..1fd7bd24b 100644
--- a/src/charon/sa/authenticators/eap/eap_method.h
+++ b/src/charon/sa/authenticators/eap/eap_method.h
@@ -12,7 +12,7 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
- * $Id: eap_method.h 4276 2008-08-22 10:44:51Z martin $
+ * $Id: eap_method.h 4882 2009-02-18 19:57:15Z tobias $
*/
/**
@@ -56,6 +56,7 @@ enum eap_type_t {
EAP_GTC = 6,
EAP_SIM = 18,
EAP_AKA = 23,
+ EAP_MSCHAPV2 = 26,
EAP_EXPANDED = 254,
EAP_EXPERIMENTAL = 255,
};
diff --git a/src/charon/sa/authenticators/eap_authenticator.c b/src/charon/sa/authenticators/eap_authenticator.c
index 5c22f3df2..0c0abcf2e 100644
--- a/src/charon/sa/authenticators/eap_authenticator.c
+++ b/src/charon/sa/authenticators/eap_authenticator.c
@@ -12,7 +12,7 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
- * $Id: eap_authenticator.c 4495 2008-10-28 16:07:06Z martin $
+ * $Id: eap_authenticator.c 4754 2008-12-04 10:09:21Z martin $
*/
#include <string.h>
@@ -360,6 +360,7 @@ static status_t process_eap_identity(private_eap_authenticator_t *this,
}
/* restart EAP exchange, but with real method */
this->method->destroy(this->method);
+ this->method = NULL;
this->do_eap_identity = FALSE;
return initiate(this, this->type, this->vendor, out);
}
diff --git a/src/charon/sa/child_sa.c b/src/charon/sa/child_sa.c
index d7a63d5e8..022b9149a 100644
--- a/src/charon/sa/child_sa.c
+++ b/src/charon/sa/child_sa.c
@@ -15,7 +15,7 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
- * $Id: child_sa.c 4665 2008-11-17 00:01:34Z andreas $
+ * $Id: child_sa.c 4677 2008-11-19 15:31:27Z martin $
*/
#define _GNU_SOURCE
@@ -90,16 +90,6 @@ struct private_child_sa_t {
linked_list_t *other_ts;
/**
- * Allocated SPI for a ESP proposal candidates
- */
- u_int32_t alloc_esp_spi;
-
- /**
- * Allocated SPI for a AH proposal candidates
- */
- u_int32_t alloc_ah_spi;
-
- /**
* Protocol used to protect this SA, ESP|AH
*/
protocol_id_t protocol;
@@ -135,11 +125,6 @@ struct private_child_sa_t {
ipcomp_transform_t ipcomp;
/**
- * TRUE if we allocated (or tried to allocate) a CPI
- */
- bool cpi_allocated;
-
- /**
* mode this SA uses, tunnel/transport
*/
ipsec_mode_t mode;
@@ -170,7 +155,32 @@ static u_int32_t get_reqid(private_child_sa_t *this)
{
return this->reqid;
}
-
+
+/**
+ * Implements child_sa_t.get_config
+ */
+static child_cfg_t* get_config(private_child_sa_t *this)
+{
+ return this->config;
+}
+
+/**
+ * Implements child_sa_t.set_state
+ */
+static void set_state(private_child_sa_t *this, child_sa_state_t state)
+{
+ charon->bus->child_state_change(charon->bus, &this->public, state);
+ this->state = state;
+}
+
+/**
+ * Implements child_sa_t.get_state
+ */
+static child_sa_state_t get_state(private_child_sa_t *this)
+{
+ return this->state;
+}
+
/**
* Implements child_sa_t.get_spi
*/
@@ -196,6 +206,14 @@ protocol_id_t get_protocol(private_child_sa_t *this)
}
/**
+ * Implementation of child_sa_t.set_protocol
+ */
+static void set_protocol(private_child_sa_t *this, protocol_id_t protocol)
+{
+ this->protocol = protocol;
+}
+
+/**
* Implementation of child_sa_t.get_mode
*/
static ipsec_mode_t get_mode(private_child_sa_t *this)
@@ -204,6 +222,14 @@ static ipsec_mode_t get_mode(private_child_sa_t *this)
}
/**
+ * Implementation of child_sa_t.set_mode
+ */
+static void set_mode(private_child_sa_t *this, ipsec_mode_t mode)
+{
+ this->mode = mode;
+}
+
+/**
* Implementation of child_sa_t.has_encap
*/
static bool has_encap(private_child_sa_t *this)
@@ -220,19 +246,35 @@ static ipcomp_transform_t get_ipcomp(private_child_sa_t *this)
}
/**
- * Implements child_sa_t.get_state
+ * Implementation of child_sa_t.set_ipcomp.
*/
-static child_sa_state_t get_state(private_child_sa_t *this)
+static void set_ipcomp(private_child_sa_t *this, ipcomp_transform_t ipcomp)
{
- return this->state;
+ this->ipcomp = ipcomp;
}
/**
- * Implements child_sa_t.get_config
+ * Implementation of child_sa_t.get_proposal
*/
-static child_cfg_t* get_config(private_child_sa_t *this)
+static proposal_t* get_proposal(private_child_sa_t *this)
{
- return this->config;
+ return this->proposal;
+}
+
+/**
+ * Implementation of child_sa_t.set_proposal
+ */
+static void set_proposal(private_child_sa_t *this, proposal_t *proposal)
+{
+ this->proposal = proposal->clone(proposal);
+}
+
+/**
+ * Implementation of child_sa_t.get_traffic_selectors.
+ */
+static linked_list_t *get_traffic_selectors(private_child_sa_t *this, bool local)
+{
+ return local ? this->my_ts : this->other_ts;
}
typedef struct policy_enumerator_t policy_enumerator_t;
@@ -366,143 +408,100 @@ static u_int32_t get_lifetime(private_child_sa_t *this, bool hard)
}
/**
- * Implements child_sa_t.set_state
- */
-static void set_state(private_child_sa_t *this, child_sa_state_t state)
-{
- charon->bus->child_state_change(charon->bus, &this->public, state);
- this->state = state;
-}
-
-/**
- * Allocate SPI for a single proposal
+ * Implementation of child_sa_t.alloc_spi
*/
-static status_t alloc_proposal(private_child_sa_t *this, proposal_t *proposal)
+static u_int32_t alloc_spi(private_child_sa_t *this, protocol_id_t protocol)
{
- protocol_id_t protocol = proposal->get_protocol(proposal);
-
- if (protocol == PROTO_AH)
+ switch (protocol)
{
- /* get a new spi for AH, if not already done */
- if (this->alloc_ah_spi == 0)
- {
- if (charon->kernel_interface->get_spi(
- charon->kernel_interface,
- this->other_addr, this->my_addr,
- PROTO_AH, this->reqid,
- &this->alloc_ah_spi) != SUCCESS)
+ case PROTO_AH:
+ if (charon->kernel_interface->get_spi(charon->kernel_interface,
+ this->other_addr, this->my_addr, PROTO_AH,
+ this->reqid, &this->my_spi) == SUCCESS)
{
- return FAILED;
+ return this->my_spi;
}
- }
- proposal->set_spi(proposal, this->alloc_ah_spi);
- }
- if (protocol == PROTO_ESP)
- {
- /* get a new spi for ESP, if not already done */
- if (this->alloc_esp_spi == 0)
- {
- if (charon->kernel_interface->get_spi(
- charon->kernel_interface,
- this->other_addr, this->my_addr,
- PROTO_ESP, this->reqid,
- &this->alloc_esp_spi) != SUCCESS)
+ break;
+ case PROTO_ESP:
+ if (charon->kernel_interface->get_spi(charon->kernel_interface,
+ this->other_addr, this->my_addr, PROTO_ESP,
+ this->reqid, &this->my_spi) == SUCCESS)
{
- return FAILED;
+ return this->my_spi;
}
- }
- proposal->set_spi(proposal, this->alloc_esp_spi);
+ break;
+ default:
+ break;
}
- return SUCCESS;
+ return 0;
}
/**
- * Implements child_sa_t.alloc
+ * Implementation of child_sa_t.alloc_cpi
*/
-static status_t alloc(private_child_sa_t *this, linked_list_t *proposals)
+static u_int16_t alloc_cpi(private_child_sa_t *this)
{
- iterator_t *iterator;
- proposal_t *proposal;
-
- /* iterator through proposals to update spis */
- iterator = proposals->create_iterator(proposals, TRUE);
- while(iterator->iterate(iterator, (void**)&proposal))
+ if (charon->kernel_interface->get_cpi(charon->kernel_interface,
+ this->other_addr, this->my_addr, this->reqid,
+ &this->my_cpi) == SUCCESS)
{
- if (alloc_proposal(this, proposal) != SUCCESS)
- {
- iterator->destroy(iterator);
- return FAILED;
- }
+ return this->my_cpi;
}
- iterator->destroy(iterator);
- return SUCCESS;
+ return 0;
}
/**
- * Install an SA for one direction
+ * Implementation of child_sa_t.install
*/
-static status_t install(private_child_sa_t *this, proposal_t *proposal,
- ipsec_mode_t mode, chunk_t integ, chunk_t encr, bool in)
+static status_t install(private_child_sa_t *this, chunk_t encr, chunk_t integ,
+ u_int32_t spi, u_int16_t cpi, bool inbound)
{
u_int16_t enc_alg = ENCR_UNDEFINED, int_alg = AUTH_UNDEFINED, size;
- u_int32_t spi, soft, hard, now;
+ u_int32_t soft, hard, now;
host_t *src, *dst;
status_t status;
+ bool update = FALSE;
/* now we have to decide which spi to use. Use self allocated, if "in",
* or the one in the proposal, if not "in" (others). Additionally,
* source and dest host switch depending on the role */
- if (in)
+ if (inbound)
{
- /* if we have allocated SPIs for AH and ESP, we must delete the unused
- * one. */
- if (this->protocol == PROTO_ESP)
- {
- this->my_spi = this->alloc_esp_spi;
- if (this->alloc_ah_spi)
- {
- charon->kernel_interface->del_sa(charon->kernel_interface,
- this->my_addr, this->alloc_ah_spi, 0, PROTO_AH);
- }
- }
- else
- {
- this->my_spi = this->alloc_ah_spi;
- if (this->alloc_esp_spi)
- {
- charon->kernel_interface->del_sa(charon->kernel_interface,
- this->my_addr, this->alloc_esp_spi, 0, PROTO_ESP);
- }
- }
- spi = this->my_spi;
dst = this->my_addr;
src = this->other_addr;
+ if (this->my_spi == spi)
+ { /* alloc_spi has been called, do an SA update */
+ update = TRUE;
+ }
+ this->my_spi = spi;
+ this->my_cpi = cpi;
}
else
{
- this->other_spi = proposal->get_spi(proposal);
- spi = this->other_spi;
src = this->my_addr;
dst = this->other_addr;
+ this->other_spi = spi;
+ this->other_cpi = cpi;
}
- DBG2(DBG_CHD, "adding %s %N SA", in ? "inbound" : "outbound",
+ DBG2(DBG_CHD, "adding %s %N SA", inbound ? "inbound" : "outbound",
protocol_id_names, this->protocol);
/* send SA down to the kernel */
DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), src, dst);
- proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &enc_alg, &size);
- proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &int_alg, &size);
+ this->proposal->get_algorithm(this->proposal, ENCRYPTION_ALGORITHM,
+ &enc_alg, &size);
+ this->proposal->get_algorithm(this->proposal, INTEGRITY_ALGORITHM,
+ &int_alg, &size);
soft = this->config->get_lifetime(this->config, TRUE);
hard = this->config->get_lifetime(this->config, FALSE);
-
+
status = charon->kernel_interface->add_sa(charon->kernel_interface,
src, dst, spi, this->protocol, this->reqid,
- in ? soft : 0, hard, enc_alg, encr, int_alg, integ,
- mode, this->ipcomp, in ? this->my_cpi : this->other_cpi,
- this->encap, in);
+ inbound ? soft : 0, hard, enc_alg, encr, int_alg, integ,
+ this->mode, this->ipcomp, cpi, this->encap, update);
now = time(NULL);
this->rekey_time = now + soft;
@@ -511,83 +510,16 @@ static status_t install(private_child_sa_t *this, proposal_t *proposal,
}
/**
- * Implementation of child_sa_t.add
- */
-static status_t add(private_child_sa_t *this,
- proposal_t *proposal, ipsec_mode_t mode,
- chunk_t integ_in, chunk_t integ_out,
- chunk_t encr_in, chunk_t encr_out)
-{
- this->proposal = proposal->clone(proposal);
- this->protocol = proposal->get_protocol(proposal);
-
- /* get SPIs for inbound SAs, write to proposal */
- if (alloc_proposal(this, proposal) != SUCCESS)
- {
- return FAILED;
- }
- /* install inbound SAs using allocated SPI */
- if (install(this, proposal, mode, integ_in, encr_in, TRUE) != SUCCESS)
- {
- return FAILED;
- }
- /* install outbound SAs using received SPI*/
- if (install(this, this->proposal, mode, integ_out, encr_out, FALSE) != SUCCESS)
- {
- return FAILED;
- }
- return SUCCESS;
-}
-
-/**
- * Implementation of child_sa_t.update
- */
-static status_t update(private_child_sa_t *this,
- proposal_t *proposal, ipsec_mode_t mode,
- chunk_t integ_in, chunk_t integ_out,
- chunk_t encr_in, chunk_t encr_out)
-{
- this->proposal = proposal->clone(proposal);
- this->protocol = proposal->get_protocol(proposal);
-
- /* install outbound SAs */
- if (install(this, proposal, mode, integ_out, encr_out, FALSE) != SUCCESS)
- {
- return FAILED;
- }
- /* install inbound SAs */
- if (install(this, proposal, mode, integ_in, encr_in, TRUE) != SUCCESS)
- {
- return FAILED;
- }
- return SUCCESS;
-}
-
-/**
- * Implementation of child_sa_t.get_proposal
- */
-static proposal_t* get_proposal(private_child_sa_t *this)
-{
- return this->proposal;
-}
-
-/**
* Implementation of child_sa_t.add_policies
*/
static status_t add_policies(private_child_sa_t *this,
- linked_list_t *my_ts_list, linked_list_t *other_ts_list,
- ipsec_mode_t mode, protocol_id_t proto)
+ linked_list_t *my_ts_list, linked_list_t *other_ts_list)
{
enumerator_t *enumerator;
traffic_selector_t *my_ts, *other_ts;
status_t status = SUCCESS;
bool routed = (this->state == CHILD_CREATED);
- if (this->protocol == PROTO_NONE)
- { /* update if not set yet */
- this->protocol = proto;
- }
-
/* apply traffic selectors */
enumerator = my_ts_list->create_enumerator(my_ts_list);
while (enumerator->enumerate(enumerator, &my_ts))
@@ -611,19 +543,19 @@ static status_t add_policies(private_child_sa_t *this,
/* install 3 policies: out, in and forward */
status |= charon->kernel_interface->add_policy(charon->kernel_interface,
this->my_addr, this->other_addr, my_ts, other_ts, POLICY_OUT,
- this->other_spi, this->protocol, this->reqid, mode, this->ipcomp,
- this->other_cpi, routed);
+ this->other_spi, this->protocol, this->reqid, this->mode,
+ this->ipcomp, this->other_cpi, routed);
status |= charon->kernel_interface->add_policy(charon->kernel_interface,
this->other_addr, this->my_addr, other_ts, my_ts, POLICY_IN,
- this->my_spi, this->protocol, this->reqid, mode, this->ipcomp,
- this->my_cpi, routed);
- if (mode != MODE_TRANSPORT)
+ this->my_spi, this->protocol, this->reqid, this->mode,
+ this->ipcomp, this->my_cpi, routed);
+ if (this->mode != MODE_TRANSPORT)
{
status |= charon->kernel_interface->add_policy(charon->kernel_interface,
this->other_addr, this->my_addr, other_ts, my_ts, POLICY_FWD,
- this->my_spi, this->protocol, this->reqid, mode, this->ipcomp,
- this->my_cpi, routed);
+ this->my_spi, this->protocol, this->reqid, this->mode,
+ this->ipcomp, this->my_cpi, routed);
}
if (status != SUCCESS)
@@ -634,32 +566,18 @@ static status_t add_policies(private_child_sa_t *this,
enumerator->destroy(enumerator);
}
- if (status == SUCCESS)
- {
- /* switch to routed state if no SAD entry set up */
- if (this->state == CHILD_CREATED)
- {
- set_state(this, CHILD_ROUTED);
- }
- /* needed to update hosts */
- this->mode = mode;
+ if (status == SUCCESS && this->state == CHILD_CREATED)
+ { /* switch to routed state if no SAD entry set up */
+ set_state(this, CHILD_ROUTED);
}
return status;
}
/**
- * Implementation of child_sa_t.get_traffic_selectors.
+ * Implementation of child_sa_t.update.
*/
-static linked_list_t *get_traffic_selectors(private_child_sa_t *this, bool local)
-{
- return local ? this->my_ts : this->other_ts;
-}
-
-/**
- * Implementation of child_sa_t.update_hosts.
- */
-static status_t update_hosts(private_child_sa_t *this,
- host_t *me, host_t *other, host_t *vip, bool encap)
+static status_t update(private_child_sa_t *this, host_t *me, host_t *other,
+ host_t *vip, bool encap)
{
child_sa_state_t old;
bool transport_proxy_mode;
@@ -792,30 +710,6 @@ static status_t update_hosts(private_child_sa_t *this,
}
/**
- * Implementation of child_sa_t.activate_ipcomp.
- */
-static void activate_ipcomp(private_child_sa_t *this, ipcomp_transform_t ipcomp,
- u_int16_t other_cpi)
-{
- this->ipcomp = ipcomp;
- this->other_cpi = other_cpi;
-}
-
-/**
- * Implementation of child_sa_t.allocate_cpi.
- */
-static u_int16_t allocate_cpi(private_child_sa_t *this)
-{
- if (!this->cpi_allocated)
- {
- charon->kernel_interface->get_cpi(charon->kernel_interface,
- this->other_addr, this->my_addr, this->reqid, &this->my_cpi);
- this->cpi_allocated = TRUE;
- }
- return this->my_cpi;
-}
-
-/**
* Implementation of child_sa_t.destroy.
*/
static void destroy(private_child_sa_t *this)
@@ -833,16 +727,6 @@ static void destroy(private_child_sa_t *this)
this->my_addr, this->my_spi, this->protocol,
this->my_cpi);
}
- if (this->alloc_esp_spi && this->alloc_esp_spi != this->my_spi)
- {
- charon->kernel_interface->del_sa(charon->kernel_interface,
- this->my_addr, this->alloc_esp_spi, PROTO_ESP, 0);
- }
- if (this->alloc_ah_spi && this->alloc_ah_spi != this->my_spi)
- {
- charon->kernel_interface->del_sa(charon->kernel_interface,
- this->my_addr, this->alloc_ah_spi, PROTO_AH, 0);
- }
if (this->other_spi)
{
charon->kernel_interface->del_sa(charon->kernel_interface,
@@ -890,40 +774,39 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
/* public functions */
this->public.get_name = (char*(*)(child_sa_t*))get_name;
this->public.get_reqid = (u_int32_t(*)(child_sa_t*))get_reqid;
+ this->public.get_config = (child_cfg_t*(*)(child_sa_t*))get_config;
+ this->public.get_state = (child_sa_state_t(*)(child_sa_t*))get_state;
+ this->public.set_state = (void(*)(child_sa_t*,child_sa_state_t))set_state;
this->public.get_spi = (u_int32_t(*)(child_sa_t*, bool))get_spi;
this->public.get_cpi = (u_int16_t(*)(child_sa_t*, bool))get_cpi;
this->public.get_protocol = (protocol_id_t(*)(child_sa_t*))get_protocol;
+ this->public.set_protocol = (void(*)(child_sa_t*, protocol_id_t protocol))set_protocol;
this->public.get_mode = (ipsec_mode_t(*)(child_sa_t*))get_mode;
- this->public.get_ipcomp = (ipcomp_transform_t(*)(child_sa_t*))get_ipcomp;
- this->public.has_encap = (bool(*)(child_sa_t*))has_encap;
+ this->public.set_mode = (void(*)(child_sa_t*, ipsec_mode_t mode))set_mode;
+ this->public.get_proposal = (proposal_t*(*)(child_sa_t*))get_proposal;
+ this->public.set_proposal = (void(*)(child_sa_t*, proposal_t *proposal))set_proposal;
this->public.get_lifetime = (u_int32_t(*)(child_sa_t*, bool))get_lifetime;
this->public.get_usetime = (u_int32_t(*)(child_sa_t*, bool))get_usetime;
- this->public.alloc = (status_t(*)(child_sa_t*,linked_list_t*))alloc;
- this->public.add = (status_t(*)(child_sa_t*,proposal_t*,ipsec_mode_t,chunk_t,chunk_t,chunk_t,chunk_t))add;
- this->public.update = (status_t(*)(child_sa_t*,proposal_t*,ipsec_mode_t,chunk_t,chunk_t,chunk_t,chunk_t))update;
- this->public.get_proposal = (proposal_t*(*)(child_sa_t*))get_proposal;
- this->public.update_hosts = (status_t (*)(child_sa_t*,host_t*,host_t*,host_t*,bool))update_hosts;
- this->public.add_policies = (status_t (*)(child_sa_t*, linked_list_t*,linked_list_t*,ipsec_mode_t,protocol_id_t))add_policies;
+ this->public.has_encap = (bool(*)(child_sa_t*))has_encap;
+ this->public.get_ipcomp = (ipcomp_transform_t(*)(child_sa_t*))get_ipcomp;
+ this->public.set_ipcomp = (void(*)(child_sa_t*,ipcomp_transform_t))set_ipcomp;
+ this->public.alloc_spi = (u_int32_t(*)(child_sa_t*, protocol_id_t protocol))alloc_spi;
+ this->public.alloc_cpi = (u_int16_t(*)(child_sa_t*))alloc_cpi;
+ this->public.install = (status_t(*)(child_sa_t*, chunk_t encr, chunk_t integ, u_int32_t spi, u_int16_t cpi, bool inbound))install;
+ this->public.update = (status_t (*)(child_sa_t*,host_t*,host_t*,host_t*,bool))update;
+ this->public.add_policies = (status_t (*)(child_sa_t*, linked_list_t*,linked_list_t*))add_policies;
this->public.get_traffic_selectors = (linked_list_t*(*)(child_sa_t*,bool))get_traffic_selectors;
this->public.create_policy_enumerator = (enumerator_t*(*)(child_sa_t*))create_policy_enumerator;
- this->public.set_state = (void(*)(child_sa_t*,child_sa_state_t))set_state;
- this->public.get_state = (child_sa_state_t(*)(child_sa_t*))get_state;
- this->public.get_config = (child_cfg_t*(*)(child_sa_t*))get_config;
- this->public.activate_ipcomp = (void(*)(child_sa_t*,ipcomp_transform_t,u_int16_t))activate_ipcomp;
- this->public.allocate_cpi = (u_int16_t(*)(child_sa_t*))allocate_cpi;
this->public.destroy = (void(*)(child_sa_t*))destroy;
-
+
/* private data */
this->my_addr = me->clone(me);
this->other_addr = other->clone(other);
this->my_spi = 0;
- this->my_cpi = 0;
this->other_spi = 0;
+ this->my_cpi = 0;
this->other_cpi = 0;
- this->alloc_ah_spi = 0;
- this->alloc_esp_spi = 0;
this->encap = encap;
- this->cpi_allocated = FALSE;
this->ipcomp = IPCOMP_NONE;
this->state = CHILD_CREATED;
/* reuse old reqid if we are rekeying an existing CHILD_SA */
@@ -935,7 +818,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
this->proposal = NULL;
this->config = config;
config->get_ref(config);
-
+
/* MIPv6 proxy transport mode sets SA endpoints to TS hosts */
if (config->get_mode(config) == MODE_TRANSPORT &&
config->use_proxy_mode(config))
@@ -947,9 +830,9 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
enumerator_t *enumerator;
linked_list_t *my_ts_list, *other_ts_list;
traffic_selector_t *my_ts, *other_ts;
-
+
this->mode = MODE_TRANSPORT;
-
+
my_ts_list = config->get_traffic_selectors(config, TRUE, NULL, me);
enumerator = my_ts_list->create_enumerator(my_ts_list);
if (enumerator->enumerate(enumerator, &my_ts))
@@ -970,7 +853,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
}
enumerator->destroy(enumerator);
my_ts_list->destroy_offset(my_ts_list, offsetof(traffic_selector_t, destroy));
-
+
other_ts_list = config->get_traffic_selectors(config, FALSE, NULL, other);
enumerator = other_ts_list->create_enumerator(other_ts_list);
if (enumerator->enumerate(enumerator, &other_ts))
diff --git a/src/charon/sa/child_sa.h b/src/charon/sa/child_sa.h
index 7109de5cd..277fd0a79 100644
--- a/src/charon/sa/child_sa.h
+++ b/src/charon/sa/child_sa.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2006-2008 Tobias Brunner
- * Copyright (C) 2006-2007 Martin Willi
+ * Copyright (C) 2006-2008 Martin Willi
* Copyright (C) 2006 Daniel Roethlisberger
* Hochschule fuer Technik Rapperswil
*
@@ -14,7 +14,7 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
- * $Id: child_sa.h 4618 2008-11-11 09:22:00Z tobias $
+ * $Id: child_sa.h 4677 2008-11-19 15:31:27Z martin $
*/
/**
@@ -93,12 +93,13 @@ extern enum_name_t *child_sa_state_names;
* SAs and the policies have the same reqid.
*
* The procedure for child sa setup is as follows:
- * - A gets SPIs for a proposal via child_sa_t.alloc
- * - A send the updated proposal to B
+ * - A gets SPIs for a all protocols in its proposals via child_sa_t.alloc
+ * - A send the proposals with the allocated SPIs to B
* - B selects a suitable proposal
- * - B calls child_sa_t.add to add and update the selected proposal
- * - B sends the updated proposal to A
- * - A calls child_sa_t.update to update the already allocated SPIs with the chosen proposal
+ * - B allocates an SPI for the selected protocol
+ * - B calls child_sa_t.install for both, the allocated and received SPI
+ * - B sends the proposal with the allocated SPI to A
+ * - A calls child_sa_t.install for both, the allocated and recevied SPI
*
* Once SAs are set up, policies can be added using add_policies.
*/
@@ -122,6 +123,27 @@ struct child_sa_t {
u_int32_t (*get_reqid)(child_sa_t *this);
/**
+ * Get the config used to set up this child sa.
+ *
+ * @return child_cfg
+ */
+ child_cfg_t* (*get_config) (child_sa_t *this);
+
+ /**
+ * Get the state of the CHILD_SA.
+ *
+ * @return CHILD_SA state
+ */
+ child_sa_state_t (*get_state) (child_sa_t *this);
+
+ /**
+ * Set the state of the CHILD_SA.
+ *
+ * @param state state to set on CHILD_SA
+ */
+ void (*set_state) (child_sa_t *this, child_sa_state_t state);
+
+ /**
* Get the SPI of this CHILD_SA.
*
* Set the boolean parameter inbound to TRUE to
@@ -153,6 +175,13 @@ struct child_sa_t {
protocol_id_t (*get_protocol) (child_sa_t *this);
/**
+ * Set the negotiated protocol to use for this CHILD_SA.
+ *
+ * @param protocol AH | ESP
+ */
+ void (*set_protocol)(child_sa_t *this, protocol_id_t protocol);
+
+ /**
* Get the IPsec mode of this CHILD_SA.
*
* @return TUNNEL | TRANSPORT | BEET
@@ -160,6 +189,13 @@ struct child_sa_t {
ipsec_mode_t (*get_mode)(child_sa_t *this);
/**
+ * Set the negotiated IPsec mode to use.
+ *
+ * @param mode TUNNEL | TRANPORT | BEET
+ */
+ void (*set_mode)(child_sa_t *this, ipsec_mode_t mode);
+
+ /**
* Get the used IPComp algorithm.
*
* @return IPComp compression algorithm.
@@ -167,6 +203,27 @@ struct child_sa_t {
ipcomp_transform_t (*get_ipcomp)(child_sa_t *this);
/**
+ * Set the IPComp algorithm to use.
+ *
+ * @param ipcomp the IPComp transform to use
+ */
+ void (*set_ipcomp)(child_sa_t *this, ipcomp_transform_t ipcomp);
+
+ /**
+ * Get the selected proposal.
+ *
+ * @return selected proposal
+ */
+ proposal_t* (*get_proposal)(child_sa_t *this);
+
+ /**
+ * Set the negotiated proposal.
+ *
+ * @param proposal selected proposal
+ */
+ void (*set_proposal)(child_sa_t *this, proposal_t *proposal);
+
+ /**
* Check if this CHILD_SA uses UDP encapsulation.
*
* @return TRUE if SA encapsulates ESP packets
@@ -190,69 +247,48 @@ struct child_sa_t {
u_int32_t (*get_usetime)(child_sa_t *this, bool inbound);
/**
- * Allocate SPIs for given proposals.
- *
- * Since the kernel manages SPIs for us, we need
- * to allocate them. If a proposal contains more
- * than one protocol, for each protocol an SPI is
- * allocated. SPIs are stored internally and written
- * back to the proposal.
+ * Get the traffic selectors list added for one side.
*
- * @param proposals list of proposals for which SPIs are allocated
- */
- status_t (*alloc)(child_sa_t *this, linked_list_t* proposals);
+ * @param local TRUE for own traffic selectors, FALSE for remote
+ * @return list of traffic selectors
+ */
+ linked_list_t* (*get_traffic_selectors) (child_sa_t *this, bool local);
/**
- * Install the kernel SAs for a proposal, without previous SPI allocation.
+ * Create an enumerator over installed policies.
*
- * @param proposal proposal for which SPIs are allocated
- * @param mode mode for the CHILD_SA
- * @param integ_in integrity key for inbound traffic
- * @param integ_out integrity key for outbound traffic
- * @param encr_in encryption key for inbound traffic
- * @param enc_out encryption key for outbound traffic
- * @return SUCCESS or FAILED
+ * @return enumerator over pairs of traffic selectors.
*/
- status_t (*add)(child_sa_t *this, proposal_t *proposal, ipsec_mode_t mode,
- chunk_t integ_in, chunk_t integ_out,
- chunk_t encr_in, chunk_t encr_out);
+ enumerator_t* (*create_policy_enumerator)(child_sa_t *this);
+
/**
- * Install the kernel SAs for a proposal, after SPIs have been allocated.
- *
- * Updates an SA, for which SPIs are already allocated via alloc().
+ * Allocate an SPI to include in a proposal.
*
- * @param proposal proposal for which SPIs are allocated
- * @param mode mode for the CHILD_SA
- * @param integ_in integrity key for inbound traffic
- * @param integ_out integrity key for outbound traffic
- * @param encr_in encryption key for inbound traffic
- * @param enc_out encryption key for outbound traffic
- * @return SUCCESS or FAILED
+ * @param protocol protocol to allocate SPI for (ESP|AH)
+ * @param spi SPI output pointer
+ * @return SPI, 0 on failure
*/
- status_t (*update)(child_sa_t *this, proposal_t *proposal, ipsec_mode_t mode,
- chunk_t integ_in, chunk_t integ_out,
- chunk_t encr_in, chunk_t encr_out);
+ u_int32_t (*alloc_spi)(child_sa_t *this, protocol_id_t protocol);
+
/**
- * Get the selected proposal passed to add()/update().
+ * Allocate a CPI to use for IPComp.
*
- * @return selected proposal
+ * @return CPI, 0 on failure
*/
- proposal_t* (*get_proposal)(child_sa_t *this);
+ u_int16_t (*alloc_cpi)(child_sa_t *this);
/**
- * Update the hosts in the kernel SAs and policies.
+ * Install an IPsec SA for one direction.
*
- * The CHILD must be INSTALLED to do this update.
- *
- * @param me the new local host
- * @param other the new remote host
- * @param vip virtual IP, if any
- * @param TRUE to use UDP encapsulation for NAT traversal
+ * @param encr encryption key, if any
+ * @param integ integrity key
+ * @param spi SPI to use, allocated for inbound
+ * @param cpi CPI to use, allocated for outbound
+ * @param inbound TRUE to install an inbound SA, FALSE for outbound
* @return SUCCESS or FAILED
*/
- status_t (*update_hosts)(child_sa_t *this, host_t *me, host_t *other,
- host_t *vip, bool encap);
-
+ status_t (*install)(child_sa_t *this, chunk_t encr, chunk_t integ,
+ u_int32_t spi, u_int16_t cpi, bool inbound);
/**
* Install the policies using some traffic selectors.
*
@@ -261,64 +297,21 @@ struct child_sa_t {
*
* @param my_ts traffic selectors for local site
* @param other_ts traffic selectors for remote site
- * @param mode mode for the SA: tunnel/transport
- * @param proto protocol for policy, ESP/AH
* @return SUCCESS or FAILED
*/
status_t (*add_policies)(child_sa_t *this, linked_list_t *my_ts_list,
- linked_list_t *other_ts_list, ipsec_mode_t mode,
- protocol_id_t proto);
-
- /**
- * Get the traffic selectors of added policies of local host.
- *
- * @param local TRUE for own traffic selectors, FALSE for remote
- * @return list of traffic selectors
- */
- linked_list_t* (*get_traffic_selectors) (child_sa_t *this, bool local);
-
+ linked_list_t *other_ts_list);
/**
- * Create an enumerator over installed policies.
+ * Update hosts and ecapulation mode in the kernel SAs and policies.
*
- * @return enumerator over pairs of traffic selectors.
- */
- enumerator_t* (*create_policy_enumerator)(child_sa_t *this);
-
- /**
- * Get the state of the CHILD_SA.
- */
- child_sa_state_t (*get_state) (child_sa_t *this);
-
- /**
- * Set the state of the CHILD_SA.
- *
- * @param state state to set on CHILD_SA
- */
- void (*set_state) (child_sa_t *this, child_sa_state_t state);
-
- /**
- * Get the config used to set up this child sa.
- *
- * @return child_cfg
- */
- child_cfg_t* (*get_config) (child_sa_t *this);
-
- /**
- * Activate IPComp by setting the transform ID and CPI values.
- *
- * @param ipcomp the IPComp transform to use
- * @param other_cpi other Compression Parameter Index
- */
- void (*activate_ipcomp) (child_sa_t *this, ipcomp_transform_t ipcomp,
- u_int16_t other_cpi);
-
- /**
- * Returns the Compression Parameter Index (CPI) allocated from the kernel.
- *
- * @return allocated CPI
+ * @param me the new local host
+ * @param other the new remote host
+ * @param vip virtual IP, if any
+ * @param TRUE to use UDP encapsulation for NAT traversal
+ * @return SUCCESS or FAILED
*/
- u_int16_t (*allocate_cpi) (child_sa_t *this);
-
+ status_t (*update)(child_sa_t *this, host_t *me, host_t *other,
+ host_t *vip, bool encap);
/**
* Destroys a child_sa.
*/
diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c
index d9bb01c60..82dd479ca 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 4652 2008-11-14 08:38:53Z martin $
+ * $Id: ike_sa.c 4808 2008-12-16 15:48:36Z martin $
*/
#include <sys/time.h>
@@ -66,6 +66,7 @@ ENUM(ike_sa_state_names, IKE_CREATED, IKE_DESTROYING,
"CREATED",
"CONNECTING",
"ESTABLISHED",
+ "PASSIVE",
"REKEYING",
"DELETING",
"DESTROYING",
@@ -410,6 +411,21 @@ static void set_proposal(private_ike_sa_t *this, proposal_t *proposal)
}
/**
+ * Implementation of ike_sa_t.set_message_id
+ */
+static void set_message_id(private_ike_sa_t *this, bool initiate, u_int32_t mid)
+{
+ if (initiate)
+ {
+ this->task_manager->reset(this->task_manager, mid, UINT_MAX);
+ }
+ else
+ {
+ this->task_manager->reset(this->task_manager, UINT_MAX, mid);
+ }
+}
+
+/**
* Implementation of ike_sa_t.send_keepalive
*/
static void send_keepalive(private_ike_sa_t *this)
@@ -621,7 +637,8 @@ static void set_state(private_ike_sa_t *this, ike_sa_state_t state)
{
case IKE_ESTABLISHED:
{
- if (this->state == IKE_CONNECTING)
+ if (this->state == IKE_CONNECTING ||
+ this->state == IKE_PASSIVE)
{
job_t *job;
u_int32_t t;
@@ -708,7 +725,7 @@ static void reset(private_ike_sa_t *this)
set_state(this, IKE_CREATED);
- this->task_manager->reset(this->task_manager);
+ this->task_manager->reset(this->task_manager, 0, 0);
}
/**
@@ -874,7 +891,7 @@ static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other)
iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
while (iterator->iterate(iterator, (void**)&child_sa))
{
- if (child_sa->update_hosts(child_sa, this->my_host,
+ if (child_sa->update(child_sa, this->my_host,
this->other_host, this->my_virtual_ip,
has_condition(this, COND_NAT_ANY)) == NOT_SUPPORTED)
{
@@ -1199,11 +1216,17 @@ static status_t acquire(private_ike_sa_t *this, u_int32_t reqid)
iterator_t *iterator;
child_sa_t *current, *child_sa = NULL;
- if (this->state == IKE_DELETING)
+ switch (this->state)
{
- DBG1(DBG_IKE, "acquiring CHILD_SA {reqid %d} failed: "
- "IKE_SA is deleting", reqid);
- return FAILED;
+ case IKE_DELETING:
+ DBG1(DBG_IKE, "acquiring CHILD_SA {reqid %d} failed: "
+ "IKE_SA is deleting", reqid);
+ return FAILED;
+ case IKE_PASSIVE:
+ /* do not process acquires if passive */
+ return FAILED;
+ default:
+ break;
}
/* find CHILD_SA */
@@ -1265,6 +1288,7 @@ static status_t route(private_ike_sa_t *this, child_cfg_t *child_cfg)
case IKE_CREATED:
case IKE_CONNECTING:
case IKE_ESTABLISHED:
+ case IKE_PASSIVE:
default:
break;
}
@@ -1288,8 +1312,8 @@ 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), PROTO_NONE);
+ child_sa->set_mode(child_sa, child_cfg->get_mode(child_cfg));
+ status = child_sa->add_policies(child_sa, my_ts, other_ts);
my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy));
other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));
@@ -1353,6 +1377,11 @@ static status_t process_message(private_ike_sa_t *this, message_t *message)
status_t status;
bool is_request;
+ if (this->state == IKE_PASSIVE)
+ { /* do not handle messages in passive state */
+ return FAILED;
+ }
+
is_request = message->get_request(message);
status = message->parse_body(message,
@@ -1366,7 +1395,7 @@ static status_t process_message(private_ike_sa_t *this, message_t *message)
switch (status)
{
case NOT_SUPPORTED:
- DBG1(DBG_IKE, "ciritcal unknown payloads found");
+ DBG1(DBG_IKE, "critical unknown payloads found");
if (is_request)
{
send_notify_response(this, message, UNSUPPORTED_CRITICAL_PAYLOAD);
@@ -1449,6 +1478,14 @@ static status_t process_message(private_ike_sa_t *this, message_t *message)
status = this->task_manager->process_message(this->task_manager, message);
if (status != DESTROY_ME)
{
+ if (message->get_exchange_type(message) == IKE_AUTH &&
+ this->state == IKE_ESTABLISHED)
+ {
+ /* purge auth items if SA is up, as they contain certs
+ * and other memory wasting elements */
+ this->my_auth->purge(this->my_auth);
+ this->other_auth->purge(this->other_auth);
+ }
return status;
}
/* if IKE_SA gets closed for any reasons, reroute routed children */
@@ -1594,37 +1631,27 @@ static iterator_t* create_child_sa_iterator(private_ike_sa_t *this)
/**
* Implementation of ike_sa_t.rekey_child_sa.
*/
-static status_t rekey_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi)
+static status_t rekey_child_sa(private_ike_sa_t *this, protocol_id_t protocol,
+ u_int32_t spi)
{
- child_sa_t *child_sa;
child_rekey_t *child_rekey;
- child_sa = get_child_sa(this, protocol, spi, TRUE);
- if (child_sa)
- {
- child_rekey = child_rekey_create(&this->public, child_sa);
- this->task_manager->queue_task(this->task_manager, &child_rekey->task);
- return this->task_manager->initiate(this->task_manager);
- }
- return FAILED;
+ child_rekey = child_rekey_create(&this->public, protocol, spi);
+ this->task_manager->queue_task(this->task_manager, &child_rekey->task);
+ return this->task_manager->initiate(this->task_manager);
}
/**
* Implementation of ike_sa_t.delete_child_sa.
*/
-static status_t delete_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi)
+static status_t delete_child_sa(private_ike_sa_t *this, protocol_id_t protocol,
+ u_int32_t spi)
{
- child_sa_t *child_sa;
child_delete_t *child_delete;
- child_sa = get_child_sa(this, protocol, spi, TRUE);
- if (child_sa)
- {
- child_delete = child_delete_create(&this->public, child_sa);
- this->task_manager->queue_task(this->task_manager, &child_delete->task);
- return this->task_manager->initiate(this->task_manager);
- }
- return FAILED;
+ child_delete = child_delete_create(&this->public, protocol, spi);
+ this->task_manager->queue_task(this->task_manager, &child_delete->task);
+ return this->task_manager->initiate(this->task_manager);
}
/**
@@ -1670,6 +1697,8 @@ static status_t delete_(private_ike_sa_t *this)
case IKE_CREATED:
DBG1(DBG_IKE, "deleting unestablished IKE_SA");
break;
+ case IKE_PASSIVE:
+ break;
default:
DBG1(DBG_IKE, "destroying IKE_SA in state %N "
"without notification", ike_sa_state_names, this->state);
@@ -1943,6 +1972,7 @@ static status_t roam(private_ike_sa_t *this, bool address)
{
case IKE_CREATED:
case IKE_DELETING:
+ case IKE_PASSIVE:
return SUCCESS;
default:
break;
@@ -2239,7 +2269,7 @@ static void destroy(private_ike_sa_t *this)
{
charon->attributes->release_address(charon->attributes,
this->peer_cfg->get_pool(this->peer_cfg),
- this->other_virtual_ip);
+ this->other_virtual_ip, this->other_id);
}
this->other_virtual_ip->destroy(this->other_virtual_ip);
}
@@ -2308,6 +2338,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
this->public.set_my_host = (void (*)(ike_sa_t*,host_t*)) set_my_host;
this->public.get_other_host = (host_t* (*)(ike_sa_t*)) get_other_host;
this->public.set_other_host = (void (*)(ike_sa_t*,host_t*)) set_other_host;
+ this->public.set_message_id = (void(*)(ike_sa_t*, bool inbound, u_int32_t mid))set_message_id;
this->public.update_hosts = (void(*)(ike_sa_t*, host_t *me, host_t *other))update_hosts;
this->public.get_my_id = (identification_t* (*)(ike_sa_t*)) get_my_id;
this->public.set_my_id = (void (*)(ike_sa_t*,identification_t*)) set_my_id;
@@ -2365,6 +2396,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
this->ike_sa_id = ike_sa_id->clone(ike_sa_id);
this->child_sas = linked_list_create();
this->my_host = host_create_any(AF_INET);
+ this->my_host->set_port(this->my_host, IKEV2_UDP_PORT);
this->other_host = host_create_any(AF_INET);
this->my_id = identification_create_from_encoding(ID_ANY, chunk_empty);
this->other_id = identification_create_from_encoding(ID_ANY, chunk_empty);
diff --git a/src/charon/sa/ike_sa.h b/src/charon/sa/ike_sa.h
index 5aa458704..d3976527b 100644
--- a/src/charon/sa/ike_sa.h
+++ b/src/charon/sa/ike_sa.h
@@ -15,7 +15,7 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
- * $Id: ike_sa.h 4640 2008-11-12 16:07:17Z martin $
+ * $Id: ike_sa.h 4810 2008-12-16 17:21:28Z tobias $
*/
/**
@@ -200,6 +200,11 @@ enum ike_sa_state_t {
IKE_ESTABLISHED,
/**
+ * IKE_SA is managed externally and does not process messages
+ */
+ IKE_PASSIVE,
+
+ /**
* IKE_SA rekeying in progress
*/
IKE_REKEYING,
@@ -414,6 +419,17 @@ struct ike_sa_t {
void (*set_proposal)(ike_sa_t *this, proposal_t *proposal);
/**
+ * Set the message id of the IKE_SA.
+ *
+ * The IKE_SA stores two message IDs, one for initiating exchanges (send)
+ * and one to respond to exchanges (expect).
+ *
+ * @param initiate TRUE to set message ID for initiating
+ * @param mid message id to set
+ */
+ void (*set_message_id)(ike_sa_t *this, bool initiate, u_int32_t mid);
+
+ /**
* Add an additional address for the peer.
*
* In MOBIKE, a peer may transmit additional addresses where it is
@@ -641,9 +657,9 @@ struct ike_sa_t {
*
* @return
* - SUCCESS if deletion is initialized
- * - INVALID_STATE, if the IKE_SA is not in
+ * - DESTROY_ME, if the IKE_SA is not in
* an established state and can not be
- * delete (but destroyed).
+ * deleted (but destroyed).
*/
status_t (*delete) (ike_sa_t *this);
diff --git a/src/charon/sa/ike_sa_manager.c b/src/charon/sa/ike_sa_manager.c
index a760409c0..447fa2dd5 100644
--- a/src/charon/sa/ike_sa_manager.c
+++ b/src/charon/sa/ike_sa_manager.c
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2005-2006 Martin Willi
+ * Copyright (C) 2008 Tobias Brunner
+ * Copyright (C) 2005-2008 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
*
@@ -13,7 +14,7 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
- * $Id: ike_sa_manager.c 4579 2008-11-05 11:29:56Z martin $
+ * $Id: ike_sa_manager.c 4811 2008-12-17 09:00:22Z martin $
*/
#include <string.h>
@@ -27,6 +28,15 @@
#include <utils/linked_list.h>
#include <crypto/hashers/hasher.h>
+/* the default size of the hash table (MUST be a power of 2) */
+#define DEFAULT_HASHTABLE_SIZE 1
+
+/* the maximum size of the hash table (MUST be a power of 2) */
+#define MAX_HASHTABLE_SIZE (1 << 30)
+
+/* the default number of segments (MUST be a power of 2) */
+#define DEFAULT_SEGMENT_COUNT 1
+
typedef struct entry_t entry_t;
/**
@@ -60,7 +70,7 @@ struct entry_t {
bool driveout_waiting_threads;
/**
- * Identifiaction of an IKE_SA (SPIs).
+ * Identification of an IKE_SA (SPIs).
*/
ike_sa_id_t *ike_sa_id;
@@ -80,6 +90,11 @@ struct entry_t {
host_t *other;
/**
+ * As responder: Is this SA half-open?
+ */
+ bool half_open;
+
+ /**
* own identity, required for duplicate checking
*/
identification_t *my_id;
@@ -115,7 +130,7 @@ static status_t entry_destroy(entry_t *this)
/**
* Creates a new entry for the ike_sa_t list.
*/
-static entry_t *entry_create(ike_sa_id_t *ike_sa_id)
+static entry_t *entry_create()
{
entry_t *this = malloc_thing(entry_t);
@@ -129,18 +144,151 @@ static entry_t *entry_create(ike_sa_id_t *ike_sa_id)
this->message_id = -1;
this->init_hash = chunk_empty;
this->other = NULL;
+ this->half_open = FALSE;
this->my_id = NULL;
this->other_id = NULL;
+ this->ike_sa_id = NULL;
+ this->ike_sa = NULL;
- /* ike_sa_id is always cloned */
- this->ike_sa_id = ike_sa_id->clone(ike_sa_id);
+ return this;
+}
- /* create new ike_sa */
- this->ike_sa = ike_sa_create(ike_sa_id);
+/**
+ * Function that matches entry_t objects by initiator SPI and the hash of the
+ * IKE_SA_INIT message.
+ */
+static bool entry_match_by_hash(entry_t *entry, ike_sa_id_t *id, chunk_t *hash)
+{
+ return id->get_responder_spi(id) == 0 &&
+ id->is_initiator(id) == entry->ike_sa_id->is_initiator(entry->ike_sa_id) &&
+ id->get_initiator_spi(id) == entry->ike_sa_id->get_initiator_spi(entry->ike_sa_id) &&
+ chunk_equals(*hash, entry->init_hash);
+}
- return this;
+/**
+ * Function that matches entry_t objects by ike_sa_id_t.
+ */
+static bool entry_match_by_id(entry_t *entry, ike_sa_id_t *id)
+{
+ if (id->equals(id, entry->ike_sa_id))
+ {
+ return TRUE;
+ }
+ if ((id->get_responder_spi(id) == 0 ||
+ entry->ike_sa_id->get_responder_spi(entry->ike_sa_id) == 0) &&
+ id->is_initiator(id) == entry->ike_sa_id->is_initiator(entry->ike_sa_id) &&
+ id->get_initiator_spi(id) == entry->ike_sa_id->get_initiator_spi(entry->ike_sa_id))
+ {
+ /* this is TRUE for IKE_SAs that we initiated but have not yet received a response */
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Function that matches entry_t objects by ike_sa_t pointers.
+ */
+static bool entry_match_by_sa(entry_t *entry, ike_sa_t *ike_sa)
+{
+ return entry->ike_sa == ike_sa;
+}
+
+/**
+ * Hash function for ike_sa_id_t objects.
+ */
+static u_int ike_sa_id_hash(ike_sa_id_t *ike_sa_id)
+{
+ /* we always use initiator spi as key */
+ return ike_sa_id->get_initiator_spi(ike_sa_id);
}
+typedef struct half_open_t half_open_t;
+
+/**
+ * Struct to manage half-open IKE_SAs per peer.
+ */
+struct half_open_t {
+ /** chunk of remote host address */
+ chunk_t other;
+
+ /** the number of half-open IKE_SAs with that host */
+ u_int count;
+};
+
+/**
+ * Destroys a half_open_t object.
+ */
+static void half_open_destroy(half_open_t *this)
+{
+ chunk_free(&this->other);
+ free(this);
+}
+
+/**
+ * Function that matches half_open_t objects by the given IP address chunk.
+ */
+static bool half_open_match(half_open_t *half_open, chunk_t *addr)
+{
+ return chunk_equals(*addr, half_open->other);
+}
+
+typedef struct connected_peers_t connected_peers_t;
+
+struct connected_peers_t {
+ /** own identity */
+ identification_t *my_id;
+
+ /** remote identity */
+ identification_t *other_id;
+
+ /** list of ike_sa_id_t objects of IKE_SAs between the two identities */
+ linked_list_t *sas;
+};
+
+static void connected_peers_destroy(connected_peers_t *this)
+{
+ this->my_id->destroy(this->my_id);
+ this->other_id->destroy(this->other_id);
+ this->sas->destroy(this->sas);
+ free(this);
+}
+
+/**
+ * Function that matches connected_peers_t objects by the given ids.
+ */
+static bool connected_peers_match(connected_peers_t *connected_peers,
+ identification_t *my_id, identification_t *other_id)
+{
+ return my_id->equals(my_id, connected_peers->my_id) &&
+ other_id->equals(other_id, connected_peers->other_id);
+}
+
+typedef struct segment_t segment_t;
+
+/**
+ * Struct to manage segments of the hash table.
+ */
+struct segment_t {
+ /** mutex to access a segment exclusively */
+ mutex_t *mutex;
+
+ /** the number of entries in this segment */
+ u_int count;
+};
+
+typedef struct shareable_segment_t shareable_segment_t;
+
+/**
+ * Struct to manage segments of the "half-open" and "connected peers" hash tables.
+ */
+struct shareable_segment_t {
+ /** rwlock to access a segment non-/exclusively */
+ rwlock_t *lock;
+
+ /** the number of entries in this segment - in case of the "half-open table"
+ * it's the sum of all half_open_t.count in a segment. */
+ u_int count;
+};
typedef struct private_ike_sa_manager_t private_ike_sa_manager_t;
@@ -154,14 +302,54 @@ struct private_ike_sa_manager_t {
ike_sa_manager_t public;
/**
- * Lock for exclusivly accessing the manager.
+ * Hash table with entries for the ike_sa_t objects.
*/
- mutex_t *mutex;
-
+ linked_list_t **ike_sa_table;
+
+ /**
+ * The size of the hash table.
+ */
+ u_int table_size;
+
+ /**
+ * Mask to map the hashes to table rows.
+ */
+ u_int table_mask;
+
+ /**
+ * Segments of the hash table.
+ */
+ segment_t *segments;
+
+ /**
+ * The number of segments.
+ */
+ u_int segment_count;
+
+ /**
+ * Mask to map a table row to a segment.
+ */
+ u_int segment_mask;
+
+ /**
+ * Hash table with half_open_t objects.
+ */
+ linked_list_t **half_open_table;
+
/**
- * Linked list with entries for the ike_sa_t objects.
+ * Segments of the "half-open" hash table.
*/
- linked_list_t *ike_sa_list;
+ shareable_segment_t *half_open_segments;
+
+ /**
+ * Hash table with connected_peers_t objects.
+ */
+ linked_list_t **connected_peers_table;
+
+ /**
+ * Segments of the "connected peers" hash table.
+ */
+ shareable_segment_t *connected_peers_segments;
/**
* RNG to get random SPIs for our side
@@ -180,126 +368,304 @@ struct private_ike_sa_manager_t {
};
/**
- * Implementation of private_ike_sa_manager_t.get_entry_by_id.
+ * Acquire a lock to access the segment of the table row with the given index.
+ * It also works with the segment index directly.
*/
-static status_t get_entry_by_id(private_ike_sa_manager_t *this,
- ike_sa_id_t *ike_sa_id, entry_t **entry)
+static void lock_single_segment(private_ike_sa_manager_t *this, u_int index)
{
- enumerator_t *enumerator;
- entry_t *current;
- status_t status;
+ mutex_t *lock = this->segments[index & this->segment_mask].mutex;
- /* create enumerator over list of ike_sa's */
- enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list);
+ lock->lock(lock);
+}
- /* default status */
- status = NOT_FOUND;
+/**
+ * Release the lock required to access the segment of the table row with the given index.
+ * It also works with the segment index directly.
+ */
+static void unlock_single_segment(private_ike_sa_manager_t *this, u_int index)
+{
+ mutex_t *lock = this->segments[index & this->segment_mask].mutex;
- while (enumerator->enumerate(enumerator, &current))
+ lock->unlock(lock);
+}
+
+/**
+ * Lock all segments
+ */
+static void lock_all_segments(private_ike_sa_manager_t *this)
+{
+ u_int i;
+
+ for (i = 0; i < this->segment_count; ++i)
{
- if (current->ike_sa_id->equals(current->ike_sa_id, ike_sa_id))
- {
- DBG2(DBG_MGR, "found entry by both SPIs");
- *entry = current;
- status = SUCCESS;
- break;
- }
- if (ike_sa_id->get_responder_spi(ike_sa_id) == 0 ||
- current->ike_sa_id->get_responder_spi(current->ike_sa_id) == 0)
- {
- /* seems to be a half ready ike_sa */
- if ((current->ike_sa_id->get_initiator_spi(current->ike_sa_id) ==
- ike_sa_id->get_initiator_spi(ike_sa_id)) &&
- (current->ike_sa_id->is_initiator(ike_sa_id) ==
- ike_sa_id->is_initiator(current->ike_sa_id)))
- {
- DBG2(DBG_MGR, "found entry by initiator SPI");
- *entry = current;
- status = SUCCESS;
- break;
- }
- }
+ this->segments[i].mutex->lock(this->segments[i].mutex);
}
-
- enumerator->destroy(enumerator);
- return status;
}
/**
- * Implementation of private_ike_sa_manager_t.get_entry_by_sa.
+ * Unlock all segments
*/
-static status_t get_entry_by_sa(private_ike_sa_manager_t *this,
- ike_sa_t *ike_sa, entry_t **entry)
+static void unlock_all_segments(private_ike_sa_manager_t *this)
{
- enumerator_t *enumerator;
- entry_t *current;
- status_t status;
+ u_int i;
+
+ for (i = 0; i < this->segment_count; ++i)
+ {
+ this->segments[i].mutex->unlock(this->segments[i].mutex);
+ }
+}
+
+typedef struct private_enumerator_t private_enumerator_t;
+
+/**
+ * hash table enumerator implementation
+ */
+struct private_enumerator_t {
+
+ /**
+ * implements enumerator interface
+ */
+ enumerator_t enumerator;
+
+ /**
+ * associated ike_sa_manager_t
+ */
+ private_ike_sa_manager_t *manager;
+
+ /**
+ * current segment index
+ */
+ u_int segment;
- enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list);
+ /**
+ * currently enumerating entry
+ */
+ entry_t *entry;
- /* default status */
- status = NOT_FOUND;
+ /**
+ * current table row index
+ */
+ u_int row;
- while (enumerator->enumerate(enumerator, &current))
+ /**
+ * enumerator for the current table row
+ */
+ enumerator_t *current;
+};
+
+/**
+ * Implementation of private_enumerator_t.enumerator.enumerate.
+ */
+static bool enumerate(private_enumerator_t *this, entry_t **entry, u_int *segment)
+{
+ if (this->entry)
+ {
+ this->entry->condvar->signal(this->entry->condvar);
+ this->entry = NULL;
+ }
+ while (this->segment < this->manager->segment_count)
{
- /* only pointers are compared */
- if (current->ike_sa == ike_sa)
+ while (this->row < this->manager->table_size)
{
- DBG2(DBG_MGR, "found entry by pointer");
- *entry = current;
- status = SUCCESS;
- break;
+ if (this->current)
+ {
+ entry_t *item;
+
+ if (this->current->enumerate(this->current, &item))
+ {
+ *entry = this->entry = item;
+ *segment = this->segment;
+ return TRUE;
+ }
+ this->current->destroy(this->current);
+ this->current = NULL;
+ unlock_single_segment(this->manager, this->segment);
+ }
+ else
+ {
+ linked_list_t *list;
+
+ lock_single_segment(this->manager, this->segment);
+ if ((list = this->manager->ike_sa_table[this->row]) != NULL &&
+ list->get_count(list))
+ {
+ this->current = list->create_enumerator(list);
+ continue;
+ }
+ unlock_single_segment(this->manager, this->segment);
+ }
+ this->row += this->manager->segment_count;
}
+ this->segment++;
+ this->row = this->segment;
}
- enumerator->destroy(enumerator);
+ return FALSE;
+}
+
+/**
+ * Implementation of private_enumerator_t.enumerator.destroy.
+ */
+static void enumerator_destroy(private_enumerator_t *this)
+{
+ if (this->entry)
+ {
+ this->entry->condvar->signal(this->entry->condvar);
+ }
+ if (this->current)
+ {
+ this->current->destroy(this->current);
+ unlock_single_segment(this->manager, this->segment);
+ }
+ free(this);
+}
+
+/**
+ * Creates an enumerator to enumerate the entries in the hash table.
+ */
+static enumerator_t* create_table_enumerator(private_ike_sa_manager_t *this)
+{
+ private_enumerator_t *enumerator = malloc_thing(private_enumerator_t);
+
+ enumerator->enumerator.enumerate = (void*)enumerate;
+ enumerator->enumerator.destroy = (void*)enumerator_destroy;
+ enumerator->manager = this;
+ enumerator->segment = 0;
+ enumerator->entry = NULL;
+ enumerator->row = 0;
+ enumerator->current = NULL;
- return status;
+ return &enumerator->enumerator;
}
/**
- * Implementation of private_ike_sa_manager_s.delete_entry.
+ * Put an entry into the hash table.
+ * Note: The caller has to unlock the returned segment.
*/
-static status_t delete_entry(private_ike_sa_manager_t *this, entry_t *entry)
+static u_int put_entry(private_ike_sa_manager_t *this, entry_t *entry)
{
- enumerator_t *enumerator;
- entry_t *current;
- status_t status;
+ linked_list_t *list;
+ u_int row = ike_sa_id_hash(entry->ike_sa_id) & this->table_mask;
+ u_int segment = row & this->segment_mask;
- enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list);
+ lock_single_segment(this, segment);
+ if ((list = this->ike_sa_table[row]) == NULL)
+ {
+ list = this->ike_sa_table[row] = linked_list_create();
+ }
+ list->insert_last(list, entry);
+ this->segments[segment].count++;
+ return segment;
+}
- status = NOT_FOUND;
+/**
+ * Remove an entry from the hash table.
+ * Note: The caller MUST have a lock on the segment of this entry.
+ */
+static void remove_entry(private_ike_sa_manager_t *this, entry_t *entry)
+{
+ linked_list_t *list;
+ u_int row = ike_sa_id_hash(entry->ike_sa_id) & this->table_mask;
+ u_int segment = row & this->segment_mask;
- while (enumerator->enumerate(enumerator, &current))
+ if ((list = this->ike_sa_table[row]) != NULL)
{
- if (current == entry)
+ entry_t *current;
+
+ enumerator_t *enumerator = list->create_enumerator(list);
+ while (enumerator->enumerate(enumerator, &current))
{
- /* mark it, so now new threads can get this entry */
- entry->driveout_new_threads = TRUE;
- /* wait until all workers have done their work */
- while (entry->waiting_threads)
+ if (current == entry)
{
- /* wake up all */
- entry->condvar->broadcast(entry->condvar);
- /* they will wake us again when their work is done */
- entry->condvar->wait(entry->condvar, this->mutex);
+ list->remove_at(list, enumerator);
+ this->segments[segment].count--;
+ break;
}
-
- DBG2(DBG_MGR, "found entry by pointer, deleting it");
- this->ike_sa_list->remove_at(this->ike_sa_list, enumerator);
- entry_destroy(entry);
- status = SUCCESS;
- break;
}
+ enumerator->destroy(enumerator);
}
- enumerator->destroy(enumerator);
- return status;
+}
+
+/**
+ * Remove the entry at the current enumerator position.
+ */
+static void remove_entry_at(private_enumerator_t *this)
+{
+ this->entry = NULL;
+ if (this->current)
+ {
+ linked_list_t *list = this->manager->ike_sa_table[this->row];
+ list->remove_at(list, this->current);
+ this->manager->segments[this->segment].count--;
+ }
+}
+
+/**
+ * Find an entry using the provided match function to compare the entries for
+ * equality.
+ */
+static status_t get_entry_by_match_function(private_ike_sa_manager_t *this,
+ ike_sa_id_t *ike_sa_id, entry_t **entry, u_int *segment,
+ linked_list_match_t match, void *p1, void *p2)
+{
+ entry_t *current;
+ linked_list_t *list;
+ u_int row = ike_sa_id_hash(ike_sa_id) & this->table_mask;
+ u_int seg = row & this->segment_mask;
+
+ lock_single_segment(this, seg);
+ if ((list = this->ike_sa_table[row]) != NULL)
+ {
+ if (list->find_first(list, match, (void**)&current, p1, p2) == SUCCESS)
+ {
+ *entry = current;
+ *segment = seg;
+ /* the locked segment has to be unlocked by the caller */
+ return SUCCESS;
+ }
+ }
+ unlock_single_segment(this, seg);
+ return NOT_FOUND;
+}
+
+/**
+ * Find an entry by ike_sa_id_t.
+ * Note: On SUCCESS, the caller has to unlock the segment.
+ */
+static status_t get_entry_by_id(private_ike_sa_manager_t *this,
+ ike_sa_id_t *ike_sa_id, entry_t **entry, u_int *segment)
+{
+ return get_entry_by_match_function(this, ike_sa_id, entry, segment,
+ (linked_list_match_t)entry_match_by_id, ike_sa_id, NULL);
+}
+
+/**
+ * Find an entry by initiator SPI and IKE_SA_INIT hash.
+ * Note: On SUCCESS, the caller has to unlock the segment.
+ */
+static status_t get_entry_by_hash(private_ike_sa_manager_t *this,
+ ike_sa_id_t *ike_sa_id, chunk_t hash, entry_t **entry, u_int *segment)
+{
+ return get_entry_by_match_function(this, ike_sa_id, entry, segment,
+ (linked_list_match_t)entry_match_by_hash, ike_sa_id, &hash);
+}
+
+/**
+ * Find an entry by IKE_SA pointer.
+ * Note: On SUCCESS, the caller has to unlock the segment.
+ */
+static status_t get_entry_by_sa(private_ike_sa_manager_t *this,
+ ike_sa_id_t *ike_sa_id, ike_sa_t *ike_sa, entry_t **entry, u_int *segment)
+{
+ return get_entry_by_match_function(this, ike_sa_id, entry, segment,
+ (linked_list_match_t)entry_match_by_sa, ike_sa, NULL);
}
/**
* Wait until no other thread is using an IKE_SA, return FALSE if entry not
- * acquireable
+ * acquirable.
*/
-static bool wait_for_entry(private_ike_sa_manager_t *this, entry_t *entry)
+static bool wait_for_entry(private_ike_sa_manager_t *this, entry_t *entry,
+ u_int segment)
{
if (entry->driveout_new_threads)
{
@@ -311,7 +677,7 @@ static bool wait_for_entry(private_ike_sa_manager_t *this, entry_t *entry)
/* so wait until we can get it for us.
* we register us as waiting. */
entry->waiting_threads++;
- entry->condvar->wait(entry->condvar, this->mutex);
+ entry->condvar->wait(entry->condvar, this->segments[segment].mutex);
entry->waiting_threads--;
}
/* hm, a deletion request forbids us to get this SA, get next one */
@@ -325,6 +691,176 @@ static bool wait_for_entry(private_ike_sa_manager_t *this, entry_t *entry)
}
/**
+ * Put a half-open SA into the hash table.
+ */
+static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry)
+{
+ half_open_t *half_open = NULL;
+ linked_list_t *list;
+ chunk_t addr = entry->other->get_address(entry->other);
+ u_int row = chunk_hash(addr) & this->table_mask;
+ u_int segment = row & this->segment_mask;
+
+ rwlock_t *lock = this->half_open_segments[segment].lock;
+ lock->write_lock(lock);
+ if ((list = this->half_open_table[row]) == NULL)
+ {
+ list = this->half_open_table[row] = linked_list_create();
+ }
+ else
+ {
+ half_open_t *current;
+ if (list->find_first(list, (linked_list_match_t)half_open_match,
+ (void**)&current, &addr) == SUCCESS)
+ {
+ half_open = current;
+ half_open->count++;
+ this->half_open_segments[segment].count++;
+ }
+ }
+
+ if (!half_open)
+ {
+ half_open = malloc_thing(half_open_t);
+ half_open->other = chunk_clone(addr);
+ half_open->count = 1;
+ list->insert_last(list, half_open);
+ this->half_open_segments[segment].count++;
+ }
+ lock->unlock(lock);
+}
+
+/**
+ * Remove a half-open SA from the hash table.
+ */
+static void remove_half_open(private_ike_sa_manager_t *this, entry_t *entry)
+{
+ linked_list_t *list;
+ chunk_t addr = entry->other->get_address(entry->other);
+ u_int row = chunk_hash(addr) & this->table_mask;
+ u_int segment = row & this->segment_mask;
+
+ rwlock_t *lock = this->half_open_segments[segment].lock;
+ lock->write_lock(lock);
+ if ((list = this->half_open_table[row]) != NULL)
+ {
+ half_open_t *current;
+ enumerator_t *enumerator = list->create_enumerator(list);
+ while (enumerator->enumerate(enumerator, &current))
+ {
+ if (half_open_match(current, &addr))
+ {
+ if (--current->count == 0)
+ {
+ list->remove_at(list, enumerator);
+ half_open_destroy(current);
+ }
+ this->half_open_segments[segment].count--;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ lock->unlock(lock);
+}
+
+/**
+ * Put an SA between two peers into the hash table.
+ */
+static void put_connected_peers(private_ike_sa_manager_t *this, entry_t *entry)
+{
+ linked_list_t *list;
+ connected_peers_t *connected_peers = NULL;
+ chunk_t my_id = entry->my_id->get_encoding(entry->my_id),
+ other_id = entry->other_id->get_encoding(entry->other_id);
+ u_int row = chunk_hash_inc(other_id, chunk_hash(my_id)) & this->table_mask;
+ u_int segment = row & this->segment_mask;
+
+ rwlock_t *lock = this->connected_peers_segments[segment].lock;
+ lock->write_lock(lock);
+ if ((list = this->connected_peers_table[row]) == NULL)
+ {
+ list = this->connected_peers_table[row] = linked_list_create();
+ }
+ else
+ {
+ connected_peers_t *current;
+ if (list->find_first(list, (linked_list_match_t)connected_peers_match,
+ (void**)&current, entry->my_id, entry->other_id) == SUCCESS)
+ {
+ connected_peers = current;
+ if (connected_peers->sas->find_first(connected_peers->sas,
+ (linked_list_match_t)entry->ike_sa_id->equals,
+ NULL, entry->ike_sa_id) == SUCCESS)
+ {
+ lock->unlock(lock);
+ return;
+ }
+ }
+ }
+
+ if (!connected_peers)
+ {
+ connected_peers = malloc_thing(connected_peers_t);
+ connected_peers->my_id = entry->my_id->clone(entry->my_id);
+ connected_peers->other_id = entry->other_id->clone(entry->other_id);
+ connected_peers->sas = linked_list_create();
+ list->insert_last(list, connected_peers);
+ }
+ connected_peers->sas->insert_last(connected_peers->sas,
+ entry->ike_sa_id->clone(entry->ike_sa_id));
+ this->connected_peers_segments[segment].count++;
+ lock->unlock(lock);
+}
+
+/**
+ * Remove an SA between two peers from the hash table.
+ */
+static void remove_connected_peers(private_ike_sa_manager_t *this, entry_t *entry)
+{
+ linked_list_t *list;
+ chunk_t my_id = entry->my_id->get_encoding(entry->my_id),
+ other_id = entry->other_id->get_encoding(entry->other_id);
+ u_int row = chunk_hash_inc(other_id, chunk_hash(my_id)) & this->table_mask;
+ u_int segment = row & this->segment_mask;
+
+ rwlock_t *lock = this->connected_peers_segments[segment].lock;
+ lock->write_lock(lock);
+ if ((list = this->connected_peers_table[row]) != NULL)
+ {
+ connected_peers_t *current;
+ enumerator_t *enumerator = list->create_enumerator(list);
+ while (enumerator->enumerate(enumerator, &current))
+ {
+ if (connected_peers_match(current, entry->my_id, entry->other_id))
+ {
+ ike_sa_id_t *ike_sa_id;
+ enumerator_t *inner = current->sas->create_enumerator(current->sas);
+ while (inner->enumerate(inner, &ike_sa_id))
+ {
+ if (ike_sa_id->equals(ike_sa_id, entry->ike_sa_id))
+ {
+ current->sas->remove_at(current->sas, inner);
+ ike_sa_id->destroy(ike_sa_id);
+ this->connected_peers_segments[segment].count--;
+ break;
+ }
+ }
+ inner->destroy(inner);
+ if (current->sas->get_count(current->sas) == 0)
+ {
+ list->remove_at(list, enumerator);
+ connected_peers_destroy(current);
+ }
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ lock->unlock(lock);
+}
+
+/**
* Implementation of private_ike_sa_manager_t.get_next_spi.
*/
static u_int64_t get_next_spi(private_ike_sa_manager_t *this)
@@ -342,21 +878,20 @@ static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id
{
ike_sa_t *ike_sa = NULL;
entry_t *entry;
+ u_int segment;
- DBG2(DBG_MGR, "checkout IKE_SA, %d IKE_SAs in manager",
- this->ike_sa_list->get_count(this->ike_sa_list));
+ DBG2(DBG_MGR, "checkout IKE_SA");
- this->mutex->lock(this->mutex);
- if (get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS)
+ if (get_entry_by_id(this, ike_sa_id, &entry, &segment) == SUCCESS)
{
- if (wait_for_entry(this, entry))
+ if (wait_for_entry(this, entry, segment))
{
DBG2(DBG_MGR, "IKE_SA successfully checked out");
entry->checked_out = TRUE;
ike_sa = entry->ike_sa;
}
+ unlock_single_segment(this, segment);
}
- this->mutex->unlock(this->mutex);
charon->bus->set_sa(charon->bus, ike_sa);
return ike_sa;
}
@@ -367,24 +902,24 @@ static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id
static ike_sa_t *checkout_new(private_ike_sa_manager_t* this, bool initiator)
{
entry_t *entry;
- ike_sa_id_t *id;
+ u_int segment;
+ entry = entry_create();
if (initiator)
{
- id = ike_sa_id_create(get_next_spi(this), 0, TRUE);
+ entry->ike_sa_id = ike_sa_id_create(get_next_spi(this), 0, TRUE);
}
else
{
- id = ike_sa_id_create(0, get_next_spi(this), FALSE);
+ entry->ike_sa_id = ike_sa_id_create(0, get_next_spi(this), FALSE);
}
- entry = entry_create(id);
- id->destroy(id);
- this->mutex->lock(this->mutex);
- this->ike_sa_list->insert_last(this->ike_sa_list, entry);
+ entry->ike_sa = ike_sa_create(entry->ike_sa_id);
+
+ segment = put_entry(this, entry);
entry->checked_out = TRUE;
- this->mutex->unlock(this->mutex);
- DBG2(DBG_MGR, "created IKE_SA, %d IKE_SAs in manager",
- this->ike_sa_list->get_count(this->ike_sa_list));
+ unlock_single_segment(this, segment);
+
+ DBG2(DBG_MGR, "created IKE_SA");
return entry->ike_sa;
}
@@ -394,53 +929,45 @@ static ike_sa_t *checkout_new(private_ike_sa_manager_t* this, bool initiator)
static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this,
message_t *message)
{
+ u_int segment;
entry_t *entry;
ike_sa_t *ike_sa = NULL;
ike_sa_id_t *id = message->get_ike_sa_id(message);
+
id = id->clone(id);
id->switch_initiator(id);
- DBG2(DBG_MGR, "checkout IKE_SA by message, %d IKE_SAs in manager",
- this->ike_sa_list->get_count(this->ike_sa_list));
+ DBG2(DBG_MGR, "checkout IKE_SA by message");
if (message->get_request(message) &&
message->get_exchange_type(message) == IKE_SA_INIT)
{
/* IKE_SA_INIT request. Check for an IKE_SA with such a message hash. */
- enumerator_t *enumerator;
chunk_t data, hash;
-
+
data = message->get_packet_data(message);
this->hasher->allocate_hash(this->hasher, data, &hash);
chunk_free(&data);
- this->mutex->lock(this->mutex);
- enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list);
- while (enumerator->enumerate(enumerator, &entry))
+ if (get_entry_by_hash(this, id, hash, &entry, &segment) == SUCCESS)
{
- if (chunk_equals(hash, entry->init_hash))
+ if (entry->message_id == 0)
{
- if (entry->message_id == 0)
- {
- enumerator->destroy(enumerator);
- this->mutex->unlock(this->mutex);
- chunk_free(&hash);
- id->destroy(id);
- DBG1(DBG_MGR, "ignoring IKE_SA_INIT, already processing");
- return NULL;
- }
- else if (wait_for_entry(this, entry))
- {
- DBG2(DBG_MGR, "IKE_SA checked out by hash");
- entry->checked_out = TRUE;
- entry->message_id = message->get_message_id(message);
- ike_sa = entry->ike_sa;
- }
- break;
+ unlock_single_segment(this, segment);
+ chunk_free(&hash);
+ id->destroy(id);
+ DBG1(DBG_MGR, "ignoring IKE_SA_INIT, already processing");
+ return NULL;
+ }
+ else if (wait_for_entry(this, entry, segment))
+ {
+ DBG2(DBG_MGR, "IKE_SA checked out by hash");
+ entry->checked_out = TRUE;
+ entry->message_id = message->get_message_id(message);
+ ike_sa = entry->ike_sa;
}
+ unlock_single_segment(this, segment);
}
- enumerator->destroy(enumerator);
- this->mutex->unlock(this->mutex);
if (ike_sa == NULL)
{
@@ -449,15 +976,19 @@ static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this,
{
/* no IKE_SA found, create a new one */
id->set_responder_spi(id, get_next_spi(this));
- entry = entry_create(id);
+ entry = entry_create();
+ entry->ike_sa = ike_sa_create(id);
+ entry->ike_sa_id = id->clone(id);
- this->mutex->lock(this->mutex);
- this->ike_sa_list->insert_last(this->ike_sa_list, entry);
+ segment = put_entry(this, entry);
entry->checked_out = TRUE;
- entry->message_id = message->get_message_id(message);
- this->mutex->unlock(this->mutex);
+ unlock_single_segment(this, segment);
+
+ entry->message_id = message->get_message_id(message);
entry->init_hash = hash;
ike_sa = entry->ike_sa;
+
+ DBG2(DBG_MGR, "created IKE_SA");
}
else
{
@@ -474,8 +1005,7 @@ static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this,
return ike_sa;
}
- this->mutex->lock(this->mutex);
- if (get_entry_by_id(this, id, &entry) == SUCCESS)
+ if (get_entry_by_id(this, id, &entry, &segment) == SUCCESS)
{
/* only check out if we are not processing this request */
if (message->get_request(message) &&
@@ -484,7 +1014,7 @@ static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this,
DBG1(DBG_MGR, "ignoring request with ID %d, already processing",
entry->message_id);
}
- else if (wait_for_entry(this, entry))
+ else if (wait_for_entry(this, entry, segment))
{
ike_sa_id_t *ike_id = entry->ike_sa->get_id(entry->ike_sa);
DBG2(DBG_MGR, "IKE_SA successfully checked out");
@@ -496,8 +1026,8 @@ static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this,
}
ike_sa = entry->ike_sa;
}
+ unlock_single_segment(this, segment);
}
- this->mutex->unlock(this->mutex);
id->destroy(id);
charon->bus->set_sa(charon->bus, ike_sa);
return ike_sa;
@@ -515,6 +1045,14 @@ static ike_sa_t* checkout_by_config(private_ike_sa_manager_t *this,
identification_t *my_id, *other_id;
host_t *my_host, *other_host;
ike_cfg_t *ike_cfg;
+ u_int segment;
+
+ if (!this->reuse_ikesa)
+ { /* IKE_SA reuse disable by config */
+ ike_sa = checkout_new(this, TRUE);
+ charon->bus->set_sa(charon->bus, ike_sa);
+ return ike_sa;
+ }
ike_cfg = peer_cfg->get_ike_cfg(peer_cfg);
my_id = peer_cfg->get_my_id(peer_cfg);
@@ -522,24 +1060,22 @@ static ike_sa_t* checkout_by_config(private_ike_sa_manager_t *this,
my_host = host_create_from_dns(ike_cfg->get_my_addr(ike_cfg), 0, 0);
other_host = host_create_from_dns(ike_cfg->get_other_addr(ike_cfg), 0, 0);
- this->mutex->lock(this->mutex);
-
- if (my_host && other_host && this->reuse_ikesa)
+ if (my_host && other_host)
{
- enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list);
- while (enumerator->enumerate(enumerator, &entry))
+ enumerator = create_table_enumerator(this);
+ while (enumerator->enumerate(enumerator, &entry, &segment))
{
identification_t *found_my_id, *found_other_id;
host_t *found_my_host, *found_other_host;
- if (!wait_for_entry(this, entry))
+ if (!wait_for_entry(this, entry, segment))
{
continue;
}
if (entry->ike_sa->get_state(entry->ike_sa) == IKE_DELETING)
{
- /* skip IKE_SA which are not useable */
+ /* skip IKE_SAs which are not usable */
continue;
}
@@ -584,28 +1120,9 @@ static ike_sa_t* checkout_by_config(private_ike_sa_manager_t *this,
DESTROY_IF(other_host);
if (!ike_sa)
- {
- u_int64_t initiator_spi;
- entry_t *new_entry;
- ike_sa_id_t *new_ike_sa_id;
-
- initiator_spi = get_next_spi(this);
- new_ike_sa_id = ike_sa_id_create(0, 0, TRUE);
- new_ike_sa_id->set_initiator_spi(new_ike_sa_id, initiator_spi);
-
- /* create entry */
- new_entry = entry_create(new_ike_sa_id);
- DBG2(DBG_MGR, "created IKE_SA");
- new_ike_sa_id->destroy(new_ike_sa_id);
-
- this->ike_sa_list->insert_last(this->ike_sa_list, new_entry);
-
- /* check ike_sa out */
- DBG2(DBG_MGR, "new IKE_SA created for IDs [%D]...[%D]", my_id, other_id);
- new_entry->checked_out = TRUE;
- ike_sa = new_entry->ike_sa;
+ { /* no IKE_SA using such a config, hand out a new */
+ ike_sa = checkout_new(this, TRUE);
}
- this->mutex->unlock(this->mutex);
charon->bus->set_sa(charon->bus, ike_sa);
return ike_sa;
}
@@ -621,13 +1138,12 @@ static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, u_int32_t id,
entry_t *entry;
ike_sa_t *ike_sa = NULL;
child_sa_t *child_sa;
+ u_int segment;
- this->mutex->lock(this->mutex);
-
- enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list);
- while (enumerator->enumerate(enumerator, &entry))
+ enumerator = create_table_enumerator(this);
+ while (enumerator->enumerate(enumerator, &entry, &segment))
{
- if (wait_for_entry(this, entry))
+ if (wait_for_entry(this, entry, segment))
{
/* look for a child with such a reqid ... */
if (child)
@@ -659,7 +1175,6 @@ static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, u_int32_t id,
}
}
enumerator->destroy(enumerator);
- this->mutex->unlock(this->mutex);
charon->bus->set_sa(charon->bus, ike_sa);
return ike_sa;
@@ -676,13 +1191,12 @@ static ike_sa_t* checkout_by_name(private_ike_sa_manager_t *this, char *name,
entry_t *entry;
ike_sa_t *ike_sa = NULL;
child_sa_t *child_sa;
+ u_int segment;
- this->mutex->lock(this->mutex);
-
- enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list);
- while (enumerator->enumerate(enumerator, &entry))
+ enumerator = create_table_enumerator(this);
+ while (enumerator->enumerate(enumerator, &entry, &segment))
{
- if (wait_for_entry(this, entry))
+ if (wait_for_entry(this, entry, segment))
{
/* look for a child with such a policy name ... */
if (child)
@@ -714,68 +1228,18 @@ static ike_sa_t* checkout_by_name(private_ike_sa_manager_t *this, char *name,
}
}
enumerator->destroy(enumerator);
- this->mutex->unlock(this->mutex);
charon->bus->set_sa(charon->bus, ike_sa);
return ike_sa;
}
-
-/**
- * Implementation of ike_sa_manager_t.checkout_duplicate.
- */
-static ike_sa_t* checkout_duplicate(private_ike_sa_manager_t *this,
- ike_sa_t *ike_sa)
-{
- enumerator_t *enumerator;
- entry_t *entry;
- ike_sa_t *duplicate = NULL;
- identification_t *me, *other;
-
- me = ike_sa->get_my_id(ike_sa);
- other = ike_sa->get_other_id(ike_sa);
-
- this->mutex->lock(this->mutex);
- enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list);
- while (enumerator->enumerate(enumerator, &entry))
- {
- if (entry->ike_sa == ike_sa)
- { /* self is not a duplicate */
- continue;
- }
- if (entry->my_id && me->equals(me, entry->my_id) &&
- entry->other_id && other->equals(other, entry->other_id))
- {
- /* we are sure that the other entry is not calling
- * checkout_duplicate here, as the identities in entry would not
- * have been set yet. Otherwise we would risk a deadlock. */
- if (wait_for_entry(this, entry))
- {
- duplicate = entry->ike_sa;
- entry->checked_out = TRUE;
- break;
- }
- }
- }
- enumerator->destroy(enumerator);
- this->mutex->unlock(this->mutex);
- return duplicate;
-}
-
-/**
- * enumerator cleanup function
- */
-static void enumerator_unlock(private_ike_sa_manager_t *this)
-{
- this->mutex->unlock(this->mutex);
-}
/**
* enumerator filter function
*/
static bool enumerator_filter(private_ike_sa_manager_t *this,
- entry_t **in, ike_sa_t **out)
+ entry_t **in, ike_sa_t **out, u_int *segment)
{
- if (wait_for_entry(this, *in))
+ if (wait_for_entry(this, *in, *segment))
{
*out = (*in)->ike_sa;
return TRUE;
@@ -784,126 +1248,266 @@ static bool enumerator_filter(private_ike_sa_manager_t *this,
}
/**
- * Implementation of ike_sa_manager_t.create_iterator.
+ * Implementation of ike_sa_manager_t.create_enumerator.
*/
static enumerator_t *create_enumerator(private_ike_sa_manager_t* this)
{
- this->mutex->lock(this->mutex);
return enumerator_create_filter(
- this->ike_sa_list->create_enumerator(this->ike_sa_list),
- (void*)enumerator_filter, this, (void*)enumerator_unlock);
+ create_table_enumerator(this),
+ (void*)enumerator_filter, this, NULL);
}
/**
* Implementation of ike_sa_manager_t.checkin.
*/
-static status_t checkin(private_ike_sa_manager_t *this, ike_sa_t *ike_sa)
+static void checkin(private_ike_sa_manager_t *this, ike_sa_t *ike_sa)
{
/* to check the SA back in, we look for the pointer of the ike_sa
* in all entries.
- * We can't search by SPI's since the MAY have changed (e.g. on reception
- * of a IKE_SA_INIT response). Updating of the SPI MAY be necessary...
+ * The lookup is done by initiator SPI, so even if the SPI has changed (e.g.
+ * on reception of a IKE_SA_INIT response) the lookup will work but
+ * updating of the SPI MAY be necessary...
*/
- status_t retval;
entry_t *entry;
ike_sa_id_t *ike_sa_id;
host_t *other;
identification_t *my_id, *other_id;
+ u_int segment;
ike_sa_id = ike_sa->get_id(ike_sa);
+ my_id = ike_sa->get_my_id(ike_sa);
+ other_id = ike_sa->get_other_id(ike_sa);
+ other = ike_sa->get_other_host(ike_sa);
DBG2(DBG_MGR, "checkin IKE_SA");
- this->mutex->lock(this->mutex);
-
/* look for the entry */
- if (get_entry_by_sa(this, ike_sa, &entry) == SUCCESS)
+ if (get_entry_by_sa(this, ike_sa_id, ike_sa, &entry, &segment) == SUCCESS)
{
/* ike_sa_id must be updated */
entry->ike_sa_id->replace_values(entry->ike_sa_id, ike_sa->get_id(ike_sa));
/* signal waiting threads */
entry->checked_out = FALSE;
entry->message_id = -1;
- /* apply remote address for DoS detection */
- other = ike_sa->get_other_host(ike_sa);
- if (!entry->other || !other->equals(other, entry->other))
+ /* check if this SA is half-open */
+ if (entry->half_open && ike_sa->get_state(ike_sa) != IKE_CONNECTING)
{
- DESTROY_IF(entry->other);
- entry->other = other->clone(other);
+ /* not half open anymore */
+ entry->half_open = FALSE;
+ remove_half_open(this, entry);
}
- /* apply identities for diplicate test */
- my_id = ike_sa->get_my_id(ike_sa);
- other_id = ike_sa->get_other_id(ike_sa);
- if (!entry->my_id ||
- entry->my_id->get_type(entry->my_id) == ID_ANY)
+ else if (entry->half_open && !other->ip_equals(other, entry->other))
{
- DESTROY_IF(entry->my_id);
- entry->my_id = my_id->clone(my_id);
+ /* the other host's IP has changed, we must update the hash table */
+ remove_half_open(this, entry);
+ DESTROY_IF(entry->other);
+ entry->other = other->clone(other);
+ put_half_open(this, entry);
}
- if (!entry->other_id ||
- entry->other_id->get_type(entry->other_id) == ID_ANY)
+ else if (!entry->half_open &&
+ !entry->ike_sa_id->is_initiator(entry->ike_sa_id) &&
+ ike_sa->get_state(ike_sa) == IKE_CONNECTING)
{
- DESTROY_IF(entry->other_id);
- entry->other_id = other_id->clone(other_id);
+ /* this is a new half-open SA */
+ entry->half_open = TRUE;
+ entry->other = other->clone(other);
+ put_half_open(this, entry);
}
DBG2(DBG_MGR, "check-in of IKE_SA successful.");
entry->condvar->signal(entry->condvar);
- retval = SUCCESS;
}
else
{
- DBG2(DBG_MGR, "tried to check in nonexisting IKE_SA");
- /* this SA is no more, this REALLY should not happen */
- retval = NOT_FOUND;
+ entry = entry_create();
+ entry->ike_sa_id = ike_sa_id->clone(ike_sa_id);
+ entry->ike_sa = ike_sa;
+ segment = put_entry(this, entry);
+ }
+
+ /* apply identities for duplicate test (only as responder) */
+ if (!entry->ike_sa_id->is_initiator(entry->ike_sa_id) &&
+ (!entry->my_id || !entry->other_id))
+ {
+ if (!entry->my_id && my_id->get_type(my_id) != ID_ANY)
+ {
+ entry->my_id = my_id->clone(my_id);
+ }
+ if (!entry->other_id && other_id->get_type(other_id) != ID_ANY)
+ {
+ entry->other_id = other_id->clone(other_id);
+ }
+ if (entry->my_id && entry->other_id)
+ {
+ put_connected_peers(this, entry);
+ }
}
- DBG2(DBG_MGR, "%d IKE_SAs in manager now",
- this->ike_sa_list->get_count(this->ike_sa_list));
- this->mutex->unlock(this->mutex);
+ unlock_single_segment(this, segment);
charon->bus->set_sa(charon->bus, NULL);
- return retval;
}
-
/**
* Implementation of ike_sa_manager_t.checkin_and_destroy.
*/
-static status_t checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ike_sa)
+static void checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ike_sa)
{
- /* deletion is a bit complex, we must garant that no thread is waiting for
+ /* deletion is a bit complex, we must ensure that no thread is waiting for
* this SA.
- * We take this SA from the list, and start signaling while threads
+ * We take this SA from the table, and start signaling while threads
* are in the condvar.
*/
entry_t *entry;
- status_t retval;
ike_sa_id_t *ike_sa_id;
+ u_int segment;
ike_sa_id = ike_sa->get_id(ike_sa);
+
DBG2(DBG_MGR, "checkin and destroy IKE_SA");
-
- this->mutex->lock(this->mutex);
-
- if (get_entry_by_sa(this, ike_sa, &entry) == SUCCESS)
+
+ if (get_entry_by_sa(this, ike_sa_id, ike_sa, &entry, &segment) == SUCCESS)
{
/* drive out waiting threads, as we are in hurry */
entry->driveout_waiting_threads = TRUE;
+ /* mark it, so no new threads can get this entry */
+ entry->driveout_new_threads = TRUE;
+ /* wait until all workers have done their work */
+ while (entry->waiting_threads)
+ {
+ /* wake up all */
+ entry->condvar->broadcast(entry->condvar);
+ /* they will wake us again when their work is done */
+ entry->condvar->wait(entry->condvar, this->segments[segment].mutex);
+ }
+ remove_entry(this, entry);
+ unlock_single_segment(this, segment);
+
+ if (entry->half_open)
+ {
+ remove_half_open(this, entry);
+ }
+ if (!entry->ike_sa_id->is_initiator(entry->ike_sa_id) &&
+ entry->my_id && entry->other_id)
+ {
+ remove_connected_peers(this, entry);
+ }
- delete_entry(this, entry);
+ entry_destroy(entry);
DBG2(DBG_MGR, "check-in and destroy of IKE_SA successful");
- retval = SUCCESS;
}
else
{
- DBG2(DBG_MGR, "tried to check-in and delete nonexisting IKE_SA");
- retval = NOT_FOUND;
+ DBG1(DBG_MGR, "tried to check-in and delete nonexisting IKE_SA");
+ ike_sa->destroy(ike_sa);
}
charon->bus->set_sa(charon->bus, NULL);
+}
+
+
+/**
+ * Implementation of ike_sa_manager_t.check_uniqueness.
+ */
+static bool check_uniqueness(private_ike_sa_manager_t *this, ike_sa_t *ike_sa)
+{
+ bool cancel = FALSE;
+ peer_cfg_t *peer_cfg;
+ unique_policy_t policy;
+ linked_list_t *list, *duplicate_ids = NULL;
+ enumerator_t *enumerator;
+ ike_sa_id_t *duplicate_id = NULL;
+ identification_t *me, *other;
+ u_int row, segment;
+ rwlock_t *lock;
+
+ peer_cfg = ike_sa->get_peer_cfg(ike_sa);
+ policy = peer_cfg->get_unique_policy(peer_cfg);
+ if (policy == UNIQUE_NO)
+ {
+ return FALSE;
+ }
+
+ me = ike_sa->get_my_id(ike_sa);
+ other = ike_sa->get_other_id(ike_sa);
+
+ row = chunk_hash_inc(other->get_encoding(other),
+ chunk_hash(me->get_encoding(me))) & this->table_mask;
+ segment = row & this->segment_mask;
+
+ lock = this->connected_peers_segments[segment & this->segment_mask].lock;
+ lock->read_lock(lock);
+ if ((list = this->connected_peers_table[row]) != NULL)
+ {
+ connected_peers_t *current;
+
+ if (list->find_first(list, (linked_list_match_t)connected_peers_match,
+ (void**)&current, me, other) == SUCCESS)
+ {
+ /* clone the list, so we can release the lock */
+ duplicate_ids = current->sas->clone_offset(current->sas,
+ offsetof(ike_sa_id_t, clone));
+ }
+ }
+ lock->unlock(lock);
+
+ if (!duplicate_ids)
+ {
+ return FALSE;
+ }
- this->mutex->unlock(this->mutex);
- return retval;
+ enumerator = duplicate_ids->create_enumerator(duplicate_ids);
+ while (enumerator->enumerate(enumerator, &duplicate_id))
+ {
+ status_t status = SUCCESS;
+ ike_sa_t *duplicate;
+
+ duplicate = checkout(this, duplicate_id);
+ if (!duplicate)
+ {
+ continue;
+ }
+ peer_cfg = duplicate->get_peer_cfg(duplicate);
+ if (peer_cfg && peer_cfg->equals(peer_cfg, ike_sa->get_peer_cfg(ike_sa)))
+ {
+ switch (duplicate->get_state(duplicate))
+ {
+ case IKE_ESTABLISHED:
+ case IKE_REKEYING:
+ switch (policy)
+ {
+ case UNIQUE_REPLACE:
+ DBG1(DBG_IKE, "deleting duplicate IKE_SA due"
+ " uniqueness policy");
+ status = duplicate->delete(duplicate);
+ break;
+ case UNIQUE_KEEP:
+ cancel = TRUE;
+ /* we keep the first IKE_SA and delete all
+ * other duplicates that might exist */
+ policy = UNIQUE_REPLACE;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (status == DESTROY_ME)
+ {
+ checkin_and_destroy(this, duplicate);
+ }
+ else
+ {
+ checkin(this, duplicate);
+ }
+ }
+ enumerator->destroy(enumerator);
+ duplicate_ids->destroy_offset(duplicate_ids, offsetof(ike_sa_id_t, destroy));
+ /* reset thread's current IKE_SA after checkin */
+ charon->bus->set_sa(charon->bus, ike_sa);
+ return cancel;
}
/**
@@ -911,35 +1515,43 @@ static status_t checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ik
*/
static int get_half_open_count(private_ike_sa_manager_t *this, host_t *ip)
{
- enumerator_t *enumerator;
- entry_t *entry;
int count = 0;
- this->mutex->lock(this->mutex);
- enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list);
- while (enumerator->enumerate(enumerator, &entry))
+ if (ip)
{
- /* we check if we have a responder CONNECTING IKE_SA without checkout */
- if (!entry->ike_sa_id->is_initiator(entry->ike_sa_id) &&
- entry->ike_sa->get_state(entry->ike_sa) == IKE_CONNECTING)
+ linked_list_t *list;
+ chunk_t addr = ip->get_address(ip);
+ u_int row = chunk_hash(addr) & this->table_mask;
+ u_int segment = row & this->segment_mask;
+
+ rwlock_t *lock = this->half_open_segments[segment & this->segment_mask].lock;
+ lock->read_lock(lock);
+ if ((list = this->half_open_table[row]) != NULL)
{
- /* if we have a host, count only matching IKE_SAs */
- if (ip)
- {
- if (entry->other && ip->ip_equals(ip, entry->other))
- {
- count++;
- }
- }
- else
+ half_open_t *current;
+
+ if (list->find_first(list, (linked_list_match_t)half_open_match,
+ (void**)&current, &addr) == SUCCESS)
{
- count++;
+ count = current->count;
}
}
+ lock->unlock(lock);
+ }
+ else
+ {
+ u_int segment;
+
+ for (segment = 0; segment < this->segment_count; ++segment)
+ {
+ rwlock_t *lock;
+ lock = this->half_open_segments[segment & this->segment_mask].lock;
+ lock->read_lock(lock);
+ count += this->half_open_segments[segment].count;
+ lock->unlock(lock);
+ }
}
- enumerator->destroy(enumerator);
- this->mutex->unlock(this->mutex);
return count;
}
@@ -951,13 +1563,14 @@ static void flush(private_ike_sa_manager_t *this)
/* destroy all list entries */
enumerator_t *enumerator;
entry_t *entry;
+ u_int segment;
- this->mutex->lock(this->mutex);
+ lock_all_segments(this);
DBG2(DBG_MGR, "going to destroy IKE_SA manager and all managed IKE_SA's");
/* Step 1: drive out all waiting threads */
DBG2(DBG_MGR, "set driveout flags for all stored IKE_SA's");
- enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list);
- while (enumerator->enumerate(enumerator, &entry))
+ enumerator = create_table_enumerator(this);
+ while (enumerator->enumerate(enumerator, &entry, &segment))
{
/* do not accept new threads, drive out waiting threads */
entry->driveout_new_threads = TRUE;
@@ -966,22 +1579,22 @@ static void flush(private_ike_sa_manager_t *this)
enumerator->destroy(enumerator);
DBG2(DBG_MGR, "wait for all threads to leave IKE_SA's");
/* Step 2: wait until all are gone */
- enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list);
- while (enumerator->enumerate(enumerator, &entry))
+ enumerator = create_table_enumerator(this);
+ while (enumerator->enumerate(enumerator, &entry, &segment))
{
- while (entry->waiting_threads)
+ while (entry->waiting_threads || entry->checked_out)
{
/* wake up all */
entry->condvar->broadcast(entry->condvar);
/* go sleeping until they are gone */
- entry->condvar->wait(entry->condvar, this->mutex);
+ entry->condvar->wait(entry->condvar, this->segments[segment].mutex);
}
}
enumerator->destroy(enumerator);
DBG2(DBG_MGR, "delete all IKE_SA's");
/* Step 3: initiate deletion of all IKE_SAs */
- enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list);
- while (enumerator->enumerate(enumerator, &entry))
+ enumerator = create_table_enumerator(this);
+ while (enumerator->enumerate(enumerator, &entry, &segment))
{
charon->bus->set_sa(charon->bus, entry->ike_sa);
entry->ike_sa->delete(entry->ike_sa);
@@ -990,14 +1603,25 @@ static void flush(private_ike_sa_manager_t *this)
DBG2(DBG_MGR, "destroy all entries");
/* Step 4: destroy all entries */
- while (this->ike_sa_list->remove_last(this->ike_sa_list,
- (void**)&entry) == SUCCESS)
+ enumerator = create_table_enumerator(this);
+ while (enumerator->enumerate(enumerator, &entry, &segment))
{
charon->bus->set_sa(charon->bus, entry->ike_sa);
+ if (entry->half_open)
+ {
+ remove_half_open(this, entry);
+ }
+ if (!entry->ike_sa_id->is_initiator(entry->ike_sa_id) &&
+ entry->my_id && entry->other_id)
+ {
+ remove_connected_peers(this, entry);
+ }
+ remove_entry_at((private_enumerator_t*)enumerator);
entry_destroy(entry);
}
+ enumerator->destroy(enumerator);
charon->bus->set_sa(charon->bus, NULL);
- this->mutex->unlock(this->mutex);
+ unlock_all_segments(this);
}
/**
@@ -1005,18 +1629,67 @@ static void flush(private_ike_sa_manager_t *this)
*/
static void destroy(private_ike_sa_manager_t *this)
{
- this->ike_sa_list->destroy(this->ike_sa_list);
+ u_int i;
+
+ for (i = 0; i < this->table_size; ++i)
+ {
+ linked_list_t *list;
+
+ if ((list = this->ike_sa_table[i]) != NULL)
+ {
+ list->destroy(list);
+ }
+ if ((list = this->half_open_table[i]) != NULL)
+ {
+ list->destroy(list);
+ }
+ if ((list = this->connected_peers_table[i]) != NULL)
+ {
+ list->destroy(list);
+ }
+ }
+ free(this->ike_sa_table);
+ free(this->half_open_table);
+ free(this->connected_peers_table);
+ for (i = 0; i < this->segment_count; ++i)
+ {
+ this->segments[i].mutex->destroy(this->segments[i].mutex);
+ this->half_open_segments[i].lock->destroy(this->half_open_segments[i].lock);
+ this->connected_peers_segments[i].lock->destroy(this->connected_peers_segments[i].lock);
+ }
+ free(this->segments);
+ free(this->half_open_segments);
+ free(this->connected_peers_segments);
+
this->rng->destroy(this->rng);
this->hasher->destroy(this->hasher);
- this->mutex->destroy(this->mutex);
free(this);
}
+/**
+ * This function returns the next-highest power of two for the given number.
+ * The algorithm works by setting all bits on the right-hand side of the most
+ * significant 1 to 1 and then increments the whole number so it rolls over
+ * to the nearest power of two. Note: returns 0 for n == 0
+ */
+static u_int get_nearest_powerof2(u_int n)
+{
+ u_int i;
+
+ --n;
+ for (i = 1; i < sizeof(u_int) * 8; i <<= 1)
+ {
+ n |= n >> i;
+ }
+ return ++n;
+}
+
/*
* Described in header.
*/
ike_sa_manager_t *ike_sa_manager_create()
{
+ u_int i;
private_ike_sa_manager_t *this = malloc_thing(private_ike_sa_manager_t);
/* assign public functions */
@@ -1028,10 +1701,10 @@ ike_sa_manager_t *ike_sa_manager_create()
this->public.checkout_by_config = (ike_sa_t*(*)(ike_sa_manager_t*,peer_cfg_t*))checkout_by_config;
this->public.checkout_by_id = (ike_sa_t*(*)(ike_sa_manager_t*,u_int32_t,bool))checkout_by_id;
this->public.checkout_by_name = (ike_sa_t*(*)(ike_sa_manager_t*,char*,bool))checkout_by_name;
- this->public.checkout_duplicate = (ike_sa_t*(*)(ike_sa_manager_t*, ike_sa_t *ike_sa))checkout_duplicate;
+ this->public.check_uniqueness = (bool(*)(ike_sa_manager_t*, ike_sa_t *ike_sa))check_uniqueness;
this->public.create_enumerator = (enumerator_t*(*)(ike_sa_manager_t*))create_enumerator;
- this->public.checkin = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin;
- this->public.checkin_and_destroy = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin_and_destroy;
+ this->public.checkin = (void(*)(ike_sa_manager_t*,ike_sa_t*))checkin;
+ this->public.checkin_and_destroy = (void(*)(ike_sa_manager_t*,ike_sa_t*))checkin_and_destroy;
this->public.get_half_open_count = (int(*)(ike_sa_manager_t*,host_t*))get_half_open_count;
/* initialize private variables */
@@ -1050,10 +1723,44 @@ ike_sa_manager_t *ike_sa_manager_create()
free(this);
return NULL;
}
- this->ike_sa_list = linked_list_create();
- this->mutex = mutex_create(MUTEX_DEFAULT);
+ this->table_size = get_nearest_powerof2(lib->settings->get_int(lib->settings,
+ "charon.ikesa_table_size", DEFAULT_HASHTABLE_SIZE));
+ this->table_size = max(1, min(this->table_size, MAX_HASHTABLE_SIZE));
+ this->table_mask = this->table_size - 1;
+
+ this->segment_count = get_nearest_powerof2(lib->settings->get_int(lib->settings,
+ "charon.ikesa_table_segments", DEFAULT_SEGMENT_COUNT));
+ this->segment_count = max(1, min(this->segment_count, this->table_size));
+ this->segment_mask = this->segment_count - 1;
+
+ this->ike_sa_table = calloc(this->table_size, sizeof(linked_list_t*));
+
+ this->segments = (segment_t*)calloc(this->segment_count, sizeof(segment_t));
+ for (i = 0; i < this->segment_count; ++i)
+ {
+ this->segments[i].mutex = mutex_create(MUTEX_RECURSIVE);
+ this->segments[i].count = 0;
+ }
+
+ /* we use the same table parameters for the table to track half-open SAs */
+ this->half_open_table = calloc(this->table_size, sizeof(linked_list_t*));
+ this->half_open_segments = calloc(this->segment_count, sizeof(shareable_segment_t));
+ for (i = 0; i < this->segment_count; ++i)
+ {
+ this->half_open_segments[i].lock = rwlock_create(RWLOCK_DEFAULT);
+ this->half_open_segments[i].count = 0;
+ }
+
+ /* also for the hash table used for duplicate tests */
+ this->connected_peers_table = calloc(this->table_size, sizeof(linked_list_t*));
+ this->connected_peers_segments = calloc(this->segment_count, sizeof(shareable_segment_t));
+ for (i = 0; i < this->segment_count; ++i)
+ {
+ this->connected_peers_segments[i].lock = rwlock_create(RWLOCK_DEFAULT);
+ this->connected_peers_segments[i].count = 0;
+ }
+
this->reuse_ikesa = lib->settings->get_bool(lib->settings,
"charon.reuse_ikesa", TRUE);
return &this->public;
}
-
diff --git a/src/charon/sa/ike_sa_manager.h b/src/charon/sa/ike_sa_manager.h
index 3f0752cc8..6b6d5a32d 100644
--- a/src/charon/sa/ike_sa_manager.h
+++ b/src/charon/sa/ike_sa_manager.h
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2005-2006 Martin Willi
+ * Copyright (C) 2008 Tobias Brunner
+ * Copyright (C) 2005-2008 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
*
@@ -13,7 +14,7 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
- * $Id: ike_sa_manager.h 4624 2008-11-11 13:11:44Z tobias $
+ * $Id: ike_sa_manager.h 4811 2008-12-17 09:00:22Z martin $
*/
/**
@@ -32,16 +33,11 @@ typedef struct ike_sa_manager_t ike_sa_manager_t;
#include <config/peer_cfg.h>
/**
- * The IKE_SA-Manager is responsible for managing all initiated and responded IKE_SA's.
+ * Manages and synchronizes access to all IKE_SAs.
*
- * To avoid access from multiple threads, IKE_SAs must be checked out from
- * the manager, and checked in after usage.
- * The manager also handles deletion of SAs.
- *
- * @todo checking of double-checkouts from the same threads would be nice.
- * This could be done by comparing thread-ids via pthread_self()...
- *
- * @todo Managing of ike_sa_t objects in a hash table instead of linked list.
+ * To synchronize access to thread-unsave IKE_SAs, they are checked out for
+ * use and checked in afterwards. A checked out SA is exclusively accessible
+ * by the owning thread.
*/
struct ike_sa_manager_t {
@@ -57,7 +53,7 @@ struct ike_sa_manager_t {
/**
* Create and check out a new IKE_SA.
- *
+ *
* @param initiator TRUE for initiator, FALSE otherwise
* @returns created and checked out IKE_SA
*/
@@ -103,12 +99,19 @@ struct ike_sa_manager_t {
peer_cfg_t *peer_cfg);
/**
- * Check out a duplicate if ike_sa to do uniqueness tests.
- *
- * @param ike_sa ike_sa to get a duplicate from
- * @return checked out duplicate
+ * Check for duplicates of the given IKE_SA.
+ *
+ * Measures are taken according to the uniqueness policy of the IKE_SA.
+ * The return value indicates whether duplicates have been found and if
+ * further measures should be taken (e.g. cancelling an IKE_AUTH exchange).
+ * check_uniqueness() must be called before the IKE_SA is complete,
+ * deadlocks occur otherwise.
+ *
+ * @param ike_sa ike_sa to check
+ * @return TRUE, if the given IKE_SA has duplicates and
+ * should be deleted
*/
- ike_sa_t* (*checkout_duplicate)(ike_sa_manager_t *this, ike_sa_t *ike_sa);
+ bool (*check_uniqueness)(ike_sa_manager_t *this, ike_sa_t *ike_sa);
/**
* Check out an IKE_SA a unique ID.
@@ -130,8 +133,8 @@ struct ike_sa_manager_t {
/**
* Check out an IKE_SA by the policy/connection name.
*
- * Check out the IKE_SA by the connections name or by a CHILD_SAs policy
- * name.
+ * Check out the IKE_SA by the configuration name, either from the IKE- or
+ * one of its CHILD_SAs.
*
* @param name name of the connection/policy
* @param child TRUE to use policy name, FALSE to use conn name
@@ -145,8 +148,8 @@ struct ike_sa_manager_t {
/**
* Create an enumerator over all stored IKE_SAs.
*
- * The avoid synchronization issues, the enumerator locks access
- * to the manager exclusively, until it gets destroyed.
+ * While enumerating an IKE_SA, it is temporarily checked out and
+ * automatically checked in after the current enumeration step.
*
* @return enumerator over all IKE_SAs.
*/
@@ -154,17 +157,13 @@ struct ike_sa_manager_t {
/**
* Checkin the SA after usage.
- *
- * @warning the SA pointer MUST NOT be used after checkin!
- * The SA must be checked out again!
- *
+ *
+ * If the IKE_SA is not registered in the manager, a new entry is created.
+ *
* @param ike_sa_id the SA identifier, will be updated
* @param ike_sa checked out SA
- * @returns
- * - SUCCESS if checked in
- * - NOT_FOUND when not found (shouldn't happen!)
*/
- status_t (*checkin) (ike_sa_manager_t* this, ike_sa_t *ike_sa);
+ void (*checkin) (ike_sa_manager_t* this, ike_sa_t *ike_sa);
/**
* Destroy a checked out SA.
@@ -177,11 +176,8 @@ struct ike_sa_manager_t {
* risk that another thread can get the SA.
*
* @param ike_sa SA to delete
- * @returns
- * - SUCCESS if found
- * - NOT_FOUND when no such SA is available
*/
- status_t (*checkin_and_destroy) (ike_sa_manager_t* this, ike_sa_t *ike_sa);
+ void (*checkin_and_destroy) (ike_sa_manager_t* this, ike_sa_t *ike_sa);
/**
* Get the number of IKE_SAs which are in the connecting state.
@@ -214,7 +210,7 @@ struct ike_sa_manager_t {
};
/**
- * Create a manager.
+ * Create the IKE_SA manager.
*
* @returns ike_sa_manager_t object, NULL if initialization fails
*/
diff --git a/src/charon/sa/keymat.c b/src/charon/sa/keymat.c
index c65bfc3b7..b2e646c93 100644
--- a/src/charon/sa/keymat.c
+++ b/src/charon/sa/keymat.c
@@ -63,6 +63,11 @@ struct private_keymat_t {
prf_t *prf;
/**
+ * Negotiated PRF algorithm
+ */
+ pseudo_random_function_t prf_alg;
+
+ /**
* Key to derive key material from for CHILD_SAs, rekeying
*/
chunk_t skd;
@@ -145,7 +150,8 @@ static diffie_hellman_t* create_dh(private_keymat_t *this,
static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal,
diffie_hellman_t *dh, chunk_t nonce_i,
chunk_t nonce_r, ike_sa_id_t *id,
- private_keymat_t *rekey)
+ pseudo_random_function_t rekey_function,
+ chunk_t rekey_skd)
{
chunk_t skeyseed, key, secret, full_nonce, fixed_nonce, prf_plus_seed;
chunk_t spi_i, spi_r;
@@ -153,6 +159,7 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal,
signer_t *signer_i, *signer_r;
prf_plus_t *prf_plus;
u_int16_t alg, key_size;
+ prf_t *rekey_prf = NULL;
spi_i = chunk_alloca(sizeof(u_int64_t));
spi_r = chunk_alloca(sizeof(u_int64_t));
@@ -169,6 +176,7 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal,
transform_type_names, PSEUDO_RANDOM_FUNCTION);
return FALSE;
}
+ this->prf_alg = alg;
this->prf = lib->crypto->create_prf(lib->crypto, alg);
if (this->prf == NULL)
{
@@ -205,7 +213,7 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal,
*
* if we are rekeying, SKEYSEED is built on another way
*/
- if (rekey == NULL) /* not rekeying */
+ if (rekey_function == PRF_UNDEFINED) /* not rekeying */
{
/* SKEYSEED = prf(Ni | Nr, g^ir) */
this->prf->set_key(this->prf, fixed_nonce);
@@ -217,11 +225,21 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal,
{
/* SKEYSEED = prf(SK_d (old), [g^ir (new)] | Ni | Nr)
* use OLD SAs PRF functions for both prf_plus and prf */
+ rekey_prf = lib->crypto->create_prf(lib->crypto, rekey_function);
+ if (!rekey_prf)
+ {
+ DBG1(DBG_IKE, "PRF of old SA %N not supported!",
+ pseudo_random_function_names, rekey_function);
+ chunk_free(&full_nonce);
+ chunk_free(&fixed_nonce);
+ chunk_clear(&prf_plus_seed);
+ return FALSE;
+ }
secret = chunk_cat("mc", secret, full_nonce);
- rekey->prf->set_key(rekey->prf, rekey->skd);
- rekey->prf->allocate_bytes(rekey->prf, secret, &skeyseed);
- rekey->prf->set_key(rekey->prf, skeyseed);
- prf_plus = prf_plus_create(rekey->prf, prf_plus_seed);
+ rekey_prf->set_key(rekey_prf, rekey_skd);
+ rekey_prf->allocate_bytes(rekey_prf, secret, &skeyseed);
+ rekey_prf->set_key(rekey_prf, skeyseed);
+ prf_plus = prf_plus_create(rekey_prf, prf_plus_seed);
}
DBG4(DBG_IKE, "SKEYSEED %B", &skeyseed);
@@ -243,6 +261,8 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal,
{
DBG1(DBG_IKE, "no %N selected",
transform_type_names, INTEGRITY_ALGORITHM);
+ prf_plus->destroy(prf_plus);
+ DESTROY_IF(rekey_prf);
return FALSE;
}
signer_i = lib->crypto->create_signer(lib->crypto, alg);
@@ -253,6 +273,7 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal,
transform_type_names, INTEGRITY_ALGORITHM,
integrity_algorithm_names ,alg);
prf_plus->destroy(prf_plus);
+ DESTROY_IF(rekey_prf);
return FALSE;
}
key_size = signer_i->get_key_size(signer_i);
@@ -284,6 +305,7 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal,
DBG1(DBG_IKE, "no %N selected",
transform_type_names, ENCRYPTION_ALGORITHM);
prf_plus->destroy(prf_plus);
+ DESTROY_IF(rekey_prf);
return FALSE;
}
crypter_i = lib->crypto->create_crypter(lib->crypto, alg, key_size / 8);
@@ -294,6 +316,7 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal,
transform_type_names, ENCRYPTION_ALGORITHM,
encryption_algorithm_names, alg, key_size);
prf_plus->destroy(prf_plus);
+ DESTROY_IF(rekey_prf);
return FALSE;
}
key_size = crypter_i->get_key_size(crypter_i);
@@ -344,6 +367,7 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal,
/* all done, prf_plus not needed anymore */
prf_plus->destroy(prf_plus);
+ DESTROY_IF(rekey_prf);
return TRUE;
}
@@ -382,9 +406,9 @@ static bool derive_child_keys(private_keymat_t *this,
{
enc_size = lookup_keylen(keylen_enc, enc_alg);
}
- if (!enc_size)
+ if (enc_alg != ENCR_NULL && !enc_size)
{
- DBG1(DBG_CHD, "no keylenth defined for %N",
+ DBG1(DBG_CHD, "no keylength defined for %N",
encryption_algorithm_names, enc_alg);
return FALSE;
}
@@ -421,7 +445,7 @@ static bool derive_child_keys(private_keymat_t *this,
}
if (!int_size)
{
- DBG1(DBG_CHD, "no keylenth defined for %N",
+ DBG1(DBG_CHD, "no keylength defined for %N",
integrity_algorithm_names, int_alg);
return FALSE;
}
@@ -443,6 +467,15 @@ static bool derive_child_keys(private_keymat_t *this,
}
/**
+ * Implementation of keymat_t.get_skd
+ */
+static pseudo_random_function_t get_skd(private_keymat_t *this, chunk_t *skd)
+{
+ *skd = this->skd;
+ return this->prf_alg;
+}
+
+/**
* Implementation of keymat_t.get_signer
*/
static signer_t* get_signer(private_keymat_t *this, bool in)
@@ -544,8 +577,9 @@ keymat_t *keymat_create(bool initiator)
private_keymat_t *this = malloc_thing(private_keymat_t);
this->public.create_dh = (diffie_hellman_t*(*)(keymat_t*, diffie_hellman_group_t group))create_dh;
- this->public.derive_ike_keys = (bool(*)(keymat_t*, proposal_t *proposal, diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id, keymat_t *rekey))derive_ike_keys;
+ this->public.derive_ike_keys = (bool(*)(keymat_t*, proposal_t *proposal, diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id, pseudo_random_function_t,chunk_t))derive_ike_keys;
this->public.derive_child_keys = (bool(*)(keymat_t*, proposal_t *proposal, diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r, chunk_t *encr_i, chunk_t *integ_i, chunk_t *encr_r, chunk_t *integ_r))derive_child_keys;
+ this->public.get_skd = (pseudo_random_function_t(*)(keymat_t*, chunk_t *skd))get_skd;
this->public.get_signer = (signer_t*(*)(keymat_t*, bool in))get_signer;
this->public.get_crypter = (crypter_t*(*)(keymat_t*, bool in))get_crypter;
this->public.get_auth_octets = (chunk_t(*)(keymat_t *, bool verify, chunk_t ike_sa_init, chunk_t nonce, identification_t *id))get_auth_octets;
@@ -559,6 +593,7 @@ keymat_t *keymat_create(bool initiator)
this->crypter_in = NULL;
this->crypter_out = NULL;
this->prf = NULL;
+ this->prf_alg = PRF_UNDEFINED;
this->skd = chunk_empty;
this->skp_verify = chunk_empty;
this->skp_build = chunk_empty;
diff --git a/src/charon/sa/keymat.h b/src/charon/sa/keymat.h
index 3ca25da9e..0d6d08f51 100644
--- a/src/charon/sa/keymat.h
+++ b/src/charon/sa/keymat.h
@@ -61,12 +61,15 @@ struct keymat_t {
* @param nonce_i initiators nonce value
* @param nonce_r responders nonce value
* @param id IKE_SA identifier
- * @param rekey keymat of old SA if we are rekeying
+ * @param rekey_prf PRF of old SA if rekeying, PRF_UNDEFINED otherwise
+ * @param rekey_sdk SKd of old SA if rekeying
* @return TRUE on success
*/
bool (*derive_ike_keys)(keymat_t *this, proposal_t *proposal,
diffie_hellman_t *dh, chunk_t nonce_i,
- chunk_t nonce_r, ike_sa_id_t *id, keymat_t *rekey);
+ chunk_t nonce_r, ike_sa_id_t *id,
+ pseudo_random_function_t rekey_function,
+ chunk_t rekey_skd);
/**
* Derive keys for a CHILD_SA.
*
@@ -91,6 +94,14 @@ struct keymat_t {
chunk_t *encr_i, chunk_t *integ_i,
chunk_t *encr_r, chunk_t *integ_r);
/**
+ * Get SKd to pass to derive_ikey_keys() during rekeying.
+ *
+ * @param skd chunk to write SKd to (internal data)
+ * @return PRF function to derive keymat
+ */
+ pseudo_random_function_t (*get_skd)(keymat_t *this, chunk_t *skd);
+
+ /**
* Get a signer to sign/verify IKE messages.
*
* @param in TRUE for inbound (verify), FALSE for outbound (sign)
diff --git a/src/charon/sa/task_manager.c b/src/charon/sa/task_manager.c
index 0630647c9..e5c5fe178 100644
--- a/src/charon/sa/task_manager.c
+++ b/src/charon/sa/task_manager.c
@@ -13,7 +13,7 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
- * $Id: task_manager.c 4484 2008-10-27 11:13:33Z martin $
+ * $Id: task_manager.c 4857 2009-02-09 10:45:51Z martin $
*/
#include "task_manager.h"
@@ -48,12 +48,12 @@ typedef struct exchange_t exchange_t;
* An exchange in the air, used do detect and handle retransmission
*/
struct exchange_t {
-
+
/**
* Message ID used for this transaction
*/
u_int32_t mid;
-
+
/**
* generated packet for retransmission
*/
@@ -66,17 +66,17 @@ typedef struct private_task_manager_t private_task_manager_t;
* private data of the task manager
*/
struct private_task_manager_t {
-
+
/**
* public functions
*/
task_manager_t public;
-
+
/**
* associated IKE_SA we are serving
*/
ike_sa_t *ike_sa;
-
+
/**
* Exchange we are currently handling as responder
*/
@@ -85,14 +85,14 @@ struct private_task_manager_t {
* Message ID of the exchange
*/
u_int32_t mid;
-
+
/**
* packet for retransmission
*/
packet_t *packet;
} responding;
-
+
/**
* Exchange we are currently handling as initiator
*/
@@ -118,17 +118,17 @@ struct private_task_manager_t {
exchange_type_t type;
} initiating;
-
+
/**
* List of queued tasks not yet in action
*/
linked_list_t *queued_tasks;
-
+
/**
* List of active tasks, initiated by ourselve
*/
linked_list_t *active_tasks;
-
+
/**
* List of tasks initiated by peer
*/
@@ -417,43 +417,48 @@ static status_t build_request(private_task_manager_t *this)
message->set_exchange_type(message, exchange);
this->initiating.type = exchange;
this->initiating.retransmitted = 0;
-
+
iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE);
while (iterator->iterate(iterator, (void*)&task))
{
- switch (task->build(task, message))
- {
- case SUCCESS:
- /* task completed, remove it */
- iterator->remove(iterator);
- task->destroy(task);
- break;
- case NEED_MORE:
- /* processed, but task needs another exchange */
- break;
- case FAILED:
- default:
- /* critical failure, destroy IKE_SA */
- iterator->destroy(iterator);
+ switch (task->build(task, message))
+ {
+ case SUCCESS:
+ /* task completed, remove it */
+ iterator->remove(iterator);
+ task->destroy(task);
+ break;
+ case NEED_MORE:
+ /* processed, but task needs another exchange */
+ break;
+ case FAILED:
+ default:
+ /* critical failure, destroy IKE_SA */
+ iterator->destroy(iterator);
message->destroy(message);
flush(this);
- return DESTROY_ME;
- }
+ return DESTROY_ME;
+ }
}
iterator->destroy(iterator);
-
- DESTROY_IF(this->initiating.packet);
+
+ /* update exchange type if a task changed it */
+ this->initiating.type = message->get_exchange_type(message);
+
status = this->ike_sa->generate_message(this->ike_sa, message,
&this->initiating.packet);
- message->destroy(message);
if (status != SUCCESS)
{
- /* message generation failed. There is nothing more to do than to
+ /* message generation failed. There is nothing more to do than to
* close the SA */
+ message->destroy(message);
flush(this);
- return DESTROY_ME;
+ return DESTROY_ME;
}
+ charon->bus->message(charon->bus, message, FALSE);
+ message->destroy(message);
+
return retransmit(this, this->initiating.mid);
}
@@ -473,32 +478,34 @@ static status_t process_response(private_task_manager_t *this,
exchange_type_names, this->initiating.type);
return DESTROY_ME;
}
-
+
/* catch if we get resetted while processing */
this->reset = FALSE;
iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE);
while (iterator->iterate(iterator, (void*)&task))
{
- switch (task->process(task, message))
- {
- case SUCCESS:
- /* task completed, remove it */
- iterator->remove(iterator);
- task->destroy(task);
- break;
- case NEED_MORE:
- /* processed, but task needs another exchange */
- break;
- case FAILED:
- default:
- /* critical failure, destroy IKE_SA */
- iterator->destroy(iterator);
- return DESTROY_ME;
- }
- if (this->reset)
- { /* start all over again if we were reset */
- this->reset = FALSE;
- iterator->destroy(iterator);
+ switch (task->process(task, message))
+ {
+ case SUCCESS:
+ /* task completed, remove it */
+ iterator->remove(iterator);
+ task->destroy(task);
+ break;
+ case NEED_MORE:
+ /* processed, but task needs another exchange */
+ break;
+ case FAILED:
+ default:
+ /* critical failure, destroy IKE_SA */
+ iterator->remove(iterator);
+ iterator->destroy(iterator);
+ task->destroy(task);
+ return DESTROY_ME;
+ }
+ if (this->reset)
+ { /* start all over again if we were reset */
+ this->reset = FALSE;
+ iterator->destroy(iterator);
return build_request(this);
}
}
@@ -506,7 +513,9 @@ static status_t process_response(private_task_manager_t *this,
this->initiating.mid++;
this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
-
+ this->initiating.packet->destroy(this->initiating.packet);
+ this->initiating.packet = NULL;
+
return build_request(this);
}
@@ -525,34 +534,34 @@ static void handle_collisions(private_task_manager_t *this, task_t *task)
if (type == IKE_REKEY || type == CHILD_REKEY ||
type == CHILD_DELETE || type == IKE_DELETE || type == IKE_REAUTH)
{
- /* find an exchange collision, and notify these tasks */
- iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE);
- while (iterator->iterate(iterator, (void**)&active))
- {
- switch (active->get_type(active))
- {
- case IKE_REKEY:
- if (type == IKE_REKEY || type == IKE_DELETE ||
- type == IKE_REAUTH)
- {
- ike_rekey_t *rekey = (ike_rekey_t*)active;
- rekey->collide(rekey, task);
- break;
- }
- continue;
- case CHILD_REKEY:
- if (type == CHILD_REKEY || type == CHILD_DELETE)
- {
- child_rekey_t *rekey = (child_rekey_t*)active;
- rekey->collide(rekey, task);
- break;
- }
- continue;
- default:
- continue;
- }
- iterator->destroy(iterator);
- return;
+ /* find an exchange collision, and notify these tasks */
+ iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE);
+ while (iterator->iterate(iterator, (void**)&active))
+ {
+ switch (active->get_type(active))
+ {
+ case IKE_REKEY:
+ if (type == IKE_REKEY || type == IKE_DELETE ||
+ type == IKE_REAUTH)
+ {
+ ike_rekey_t *rekey = (ike_rekey_t*)active;
+ rekey->collide(rekey, task);
+ break;
+ }
+ continue;
+ case CHILD_REKEY:
+ if (type == CHILD_REKEY || type == CHILD_DELETE)
+ {
+ child_rekey_t *rekey = (child_rekey_t*)active;
+ rekey->collide(rekey, task);
+ break;
+ }
+ continue;
+ default:
+ continue;
+ }
+ iterator->destroy(iterator);
+ return;
}
iterator->destroy(iterator);
}
@@ -571,10 +580,10 @@ static status_t build_response(private_task_manager_t *this, message_t *request)
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, request->get_exchange_type(request));
/* send response along the path the request came in */
@@ -582,29 +591,29 @@ static status_t build_response(private_task_manager_t *this, message_t *request)
message->set_destination(message, other->clone(other));
message->set_message_id(message, this->responding.mid);
message->set_request(message, FALSE);
-
+
iterator = this->passive_tasks->create_iterator(this->passive_tasks, TRUE);
while (iterator->iterate(iterator, (void*)&task))
{
- switch (task->build(task, message))
- {
- case SUCCESS:
- /* task completed, remove it */
- iterator->remove(iterator);
+ switch (task->build(task, message))
+ {
+ case SUCCESS:
+ /* task completed, remove it */
+ iterator->remove(iterator);
handle_collisions(this, task);
- case NEED_MORE:
- /* processed, but task needs another exchange */
- break;
- case FAILED:
- default:
- /* destroy IKE_SA, but SEND response first */
- delete = TRUE;
- break;
- }
- if (delete)
- {
- break;
- }
+ case NEED_MORE:
+ /* processed, but task needs another exchange */
+ break;
+ case FAILED:
+ default:
+ /* destroy IKE_SA, but SEND response first */
+ delete = TRUE;
+ break;
+ }
+ if (delete)
+ {
+ break;
+ }
}
iterator->destroy(iterator);
@@ -614,7 +623,7 @@ static status_t build_response(private_task_manager_t *this, message_t *request)
ike_sa_id_t *id = this->ike_sa->get_id(this->ike_sa);
id->set_responder_spi(id, 0);
}
-
+
/* message complete, send it */
DESTROY_IF(this->responding.packet);
status = this->ike_sa->generate_message(this->ike_sa, message,
@@ -623,7 +632,7 @@ static status_t build_response(private_task_manager_t *this, message_t *request)
message->destroy(message);
if (status != SUCCESS)
{
- return DESTROY_ME;
+ return DESTROY_ME;
}
charon->sender->send(charon->sender,
@@ -646,7 +655,7 @@ static status_t process_request(private_task_manager_t *this,
payload_t *payload;
notify_payload_t *notify;
delete_payload_t *delete;
-
+
/* create tasks depending on request type */
switch (message->get_exchange_type(message))
{
@@ -713,7 +722,8 @@ static status_t process_request(private_task_manager_t *this,
{
if (notify_found)
{
- task = (task_t*)child_rekey_create(this->ike_sa, NULL);
+ task = (task_t*)child_rekey_create(this->ike_sa,
+ PROTO_NONE, 0);
}
else
{
@@ -770,7 +780,8 @@ static status_t process_request(private_task_manager_t *this,
}
else
{
- task = (task_t*)child_delete_create(this->ike_sa, NULL);
+ task = (task_t*)child_delete_create(this->ike_sa,
+ PROTO_NONE, 0);
}
break;
}
@@ -801,30 +812,32 @@ static status_t process_request(private_task_manager_t *this,
default:
break;
}
-
+
/* let the tasks process the message */
iterator = this->passive_tasks->create_iterator(this->passive_tasks, TRUE);
while (iterator->iterate(iterator, (void*)&task))
{
- switch (task->process(task, message))
- {
- case SUCCESS:
- /* task completed, remove it */
- iterator->remove(iterator);
- task->destroy(task);
- break;
- case NEED_MORE:
- /* processed, but task needs at least another call to build() */
- break;
- case FAILED:
- default:
- /* critical failure, destroy IKE_SA */
- iterator->destroy(iterator);
- return DESTROY_ME;
- }
+ switch (task->process(task, message))
+ {
+ case SUCCESS:
+ /* task completed, remove it */
+ iterator->remove(iterator);
+ task->destroy(task);
+ break;
+ case NEED_MORE:
+ /* processed, but task needs at least another call to build() */
+ break;
+ case FAILED:
+ default:
+ /* critical failure, destroy IKE_SA */
+ iterator->remove(iterator);
+ iterator->destroy(iterator);
+ task->destroy(task);
+ return DESTROY_ME;
+ }
}
iterator->destroy(iterator);
-
+
return build_response(this, message);
}
@@ -834,7 +847,7 @@ static status_t process_request(private_task_manager_t *this,
static status_t process_message(private_task_manager_t *this, message_t *msg)
{
u_int32_t mid = msg->get_message_id(msg);
-
+
if (msg->get_request(msg))
{
if (mid == this->responding.mid)
@@ -919,7 +932,7 @@ static void queue_task(private_task_manager_t *this, task_t *task)
static void adopt_tasks(private_task_manager_t *this, private_task_manager_t *other)
{
task_t *task;
-
+
/* move queued tasks from other to this */
while (other->queued_tasks->remove_last(other->queued_tasks,
(void**)&task) == SUCCESS)
@@ -950,7 +963,8 @@ static bool busy(private_task_manager_t *this)
/**
* Implementation of task_manager_t.reset
*/
-static void reset(private_task_manager_t *this)
+static void reset(private_task_manager_t *this,
+ u_int32_t initiate, u_int32_t respond)
{
task_t *task;
@@ -959,8 +973,14 @@ static void reset(private_task_manager_t *this)
DESTROY_IF(this->initiating.packet);
this->responding.packet = NULL;
this->initiating.packet = NULL;
- this->responding.mid = 0;
- this->initiating.mid = 0;
+ if (initiate != UINT_MAX)
+ {
+ this->initiating.mid = initiate;
+ }
+ if (respond != UINT_MAX)
+ {
+ this->responding.mid = respond;
+ }
this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
/* reset active tasks */
@@ -996,16 +1016,16 @@ static void destroy(private_task_manager_t *this)
task_manager_t *task_manager_create(ike_sa_t *ike_sa)
{
private_task_manager_t *this = malloc_thing(private_task_manager_t);
-
+
this->public.process_message = (status_t(*)(task_manager_t*,message_t*))process_message;
this->public.queue_task = (void(*)(task_manager_t*,task_t*))queue_task;
this->public.initiate = (status_t(*)(task_manager_t*))build_request;
this->public.retransmit = (status_t(*)(task_manager_t*,u_int32_t))retransmit;
- this->public.reset = (void(*)(task_manager_t*))reset;
+ this->public.reset = (void(*)(task_manager_t*,u_int32_t,u_int32_t))reset;
this->public.adopt_tasks = (void(*)(task_manager_t*,task_manager_t*))adopt_tasks;
this->public.busy = (bool(*)(task_manager_t*))busy;
this->public.destroy = (void(*)(task_manager_t*))destroy;
-
+
this->ike_sa = ike_sa;
this->responding.packet = NULL;
this->initiating.packet = NULL;
@@ -1016,6 +1036,6 @@ task_manager_t *task_manager_create(ike_sa_t *ike_sa)
this->active_tasks = linked_list_create();
this->passive_tasks = linked_list_create();
this->reset = FALSE;
-
+
return &this->public;
}
diff --git a/src/charon/sa/task_manager.h b/src/charon/sa/task_manager.h
index 6243ac888..2aa6018fd 100644
--- a/src/charon/sa/task_manager.h
+++ b/src/charon/sa/task_manager.h
@@ -12,7 +12,7 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
- * $Id: task_manager.h 3589 2008-03-13 14:14:44Z martin $
+ * $Id: task_manager.h 4689 2008-11-24 12:46:06Z martin $
*/
/**
@@ -25,6 +25,8 @@
typedef struct task_manager_t task_manager_t;
+#include <limits.h>
+
#include <library.h>
#include <encoding/message.h>
#include <sa/ike_sa.h>
@@ -125,7 +127,7 @@ struct task_manager_t {
* - SUCCESS if retransmission sent
*/
status_t (*retransmit) (task_manager_t *this, u_int32_t message_id);
-
+
/**
* Migrate all tasks from other to this.
*
@@ -143,10 +145,12 @@ struct task_manager_t {
* reset to zero (INVALID_KE_PAYLOAD, COOKIES, ...). The reset() method
* resets the message IDs and resets all active tasks using the migrate()
* method.
- *
- * @param other manager which gives away its tasks
+ * Use a value of UINT_MAX to keep the current message ID.
+ *
+ * @param initiate message ID to initiate exchanges (send)
+ * @param respond message ID to respond to exchanges (expect)
*/
- void (*reset) (task_manager_t *this);
+ void (*reset) (task_manager_t *this, u_int32_t initiate, u_int32_t respond);
/**
* Check if we are currently waiting for a reply.
diff --git a/src/charon/sa/tasks/child_create.c b/src/charon/sa/tasks/child_create.c
index 767ceef55..f6043979f 100644
--- a/src/charon/sa/tasks/child_create.c
+++ b/src/charon/sa/tasks/child_create.c
@@ -14,7 +14,7 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
- * $Id: child_create.c 4618 2008-11-11 09:22:00Z tobias $
+ * $Id: child_create.c 4860 2009-02-11 13:09:52Z martin $
*/
#include "child_create.h"
@@ -117,7 +117,22 @@ struct private_child_create_t {
ipcomp_transform_t ipcomp_received;
/**
- * Other Compression Parameter Index (CPI)
+ * Own allocated SPI
+ */
+ u_int32_t my_spi;
+
+ /**
+ * SPI received in proposal
+ */
+ u_int32_t other_spi;
+
+ /**
+ * Own allocated Compression Parameter Index (CPI)
+ */
+ u_int16_t my_cpi;
+
+ /**
+ * Other Compression Parameter Index (CPI), received via IPCOMP_SUPPORTED
*/
u_int16_t other_cpi;
@@ -189,6 +204,36 @@ static bool ts_list_is_host(linked_list_t *list, host_t *host)
}
/**
+ * Allocate SPIs and update proposals
+ */
+static bool allocate_spi(private_child_create_t *this)
+{
+ enumerator_t *enumerator;
+ proposal_t *proposal;
+
+ /* TODO: allocate additional SPI for AH if we have such proposals */
+ this->my_spi = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP);
+ if (this->my_spi)
+ {
+ if (this->initiator)
+ {
+ enumerator = this->proposals->create_enumerator(this->proposals);
+ while (enumerator->enumerate(enumerator, &proposal))
+ {
+ proposal->set_spi(proposal, this->my_spi);
+ }
+ enumerator->destroy(enumerator);
+ }
+ else
+ {
+ this->proposal->set_spi(this->proposal, this->my_spi);
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
* Install a CHILD_SA for usage, return value:
* - FAILED: no acceptable proposal
* - INVALID_ARG: diffie hellman group inacceptable
@@ -197,7 +242,9 @@ static bool ts_list_is_host(linked_list_t *list, host_t *host)
static status_t select_and_install(private_child_create_t *this, bool no_dh)
{
status_t status;
- chunk_t nonce_i, nonce_r, encr_i, integ_i, encr_r, integ_r;
+ chunk_t nonce_i, nonce_r;
+ chunk_t encr_i = chunk_empty, encr_r = chunk_empty;
+ chunk_t integ_i = chunk_empty, integ_r = chunk_empty;
linked_list_t *my_ts, *other_ts;
host_t *me, *other, *other_vip, *my_vip;
@@ -216,7 +263,7 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh)
other = this->ike_sa->get_other_host(this->ike_sa);
my_vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE);
other_vip = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE);
-
+
this->proposal = this->config->select_proposal(this->config, this->proposals,
no_dh);
if (this->proposal == NULL)
@@ -224,6 +271,14 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh)
DBG1(DBG_IKE, "no acceptable proposal found");
return FAILED;
}
+ this->other_spi = this->proposal->get_spi(this->proposal);
+
+ if (!this->initiator && !allocate_spi(this))
+ { /* responder has no SPI allocated yet */
+ DBG1(DBG_IKE, "allocating SPI failed");
+ return FAILED;
+ }
+ this->child_sa->set_proposal(this->child_sa, this->proposal);
if (!this->proposal->has_dh_group(this->proposal, this->dh_group))
{
@@ -328,26 +383,33 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh)
}
this->child_sa->set_state(this->child_sa, CHILD_INSTALLING);
+ this->child_sa->set_ipcomp(this->child_sa, this->ipcomp);
+ this->child_sa->set_mode(this->child_sa, this->mode);
+ this->child_sa->set_protocol(this->child_sa,
+ this->proposal->get_protocol(this->proposal));
- if (this->ipcomp != IPCOMP_NONE)
+ if (this->my_cpi == 0 || this->other_cpi == 0 || this->ipcomp == IPCOMP_NONE)
{
- this->child_sa->activate_ipcomp(this->child_sa, this->ipcomp,
- this->other_cpi);
+ this->my_cpi = this->other_cpi = 0;
+ this->ipcomp = IPCOMP_NONE;
}
-
status = FAILED;
if (this->keymat->derive_child_keys(this->keymat, this->proposal,
this->dh, nonce_i, nonce_r, &encr_i, &integ_i, &encr_r, &integ_r))
{
if (this->initiator)
{
- status = this->child_sa->update(this->child_sa, this->proposal,
- this->mode, integ_r, integ_i, encr_r, encr_i);
+ status = this->child_sa->install(this->child_sa, encr_r, integ_r,
+ this->my_spi, this->my_cpi, TRUE);
+ status = this->child_sa->install(this->child_sa, encr_i, integ_i,
+ this->other_spi, this->other_cpi, FALSE);
}
else
{
- status = this->child_sa->add(this->child_sa, this->proposal,
- this->mode, integ_i, integ_r, encr_i, encr_r);
+ status = this->child_sa->install(this->child_sa, encr_i, integ_i,
+ this->my_spi, this->my_cpi, TRUE);
+ status = this->child_sa->install(this->child_sa, encr_r, integ_r,
+ this->other_spi, this->other_cpi, FALSE);
}
}
chunk_clear(&integ_i);
@@ -361,8 +423,7 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh)
return FAILED;
}
- status = this->child_sa->add_policies(this->child_sa, my_ts, other_ts,
- this->mode, this->proposal->get_protocol(this->proposal));
+ status = this->child_sa->add_policies(this->child_sa, my_ts, other_ts);
if (status != SUCCESS)
{
DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel");
@@ -436,33 +497,71 @@ static void build_payloads(private_child_create_t *this, message_t *message)
}
/**
- * Adds an IPCOMP_SUPPORTED notify to the message, if possible
+ * Adds an IPCOMP_SUPPORTED notify to the message, allocating a CPI
*/
-static void build_ipcomp_supported_notify(private_child_create_t *this,
- message_t *message)
+static void add_ipcomp_notify(private_child_create_t *this,
+ message_t *message, u_int8_t ipcomp)
{
- u_int16_t cpi;
- u_int8_t tid;
-
if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY))
{
DBG1(DBG_IKE, "IPComp is not supported if either peer is natted, "
"IPComp disabled");
- this->ipcomp = IPCOMP_NONE;
return;
}
- cpi = this->child_sa->allocate_cpi(this->child_sa);
- tid = this->ipcomp;
- if (cpi)
+ this->my_cpi = this->child_sa->alloc_cpi(this->child_sa);
+ if (this->my_cpi)
{
- message->add_notify(message, FALSE, IPCOMP_SUPPORTED,
- chunk_cata("cc", chunk_from_thing(cpi), chunk_from_thing(tid)));
+ this->ipcomp = ipcomp;
+ message->add_notify(message, FALSE, IPCOMP_SUPPORTED,
+ chunk_cata("cc", chunk_from_thing(this->my_cpi),
+ chunk_from_thing(ipcomp)));
}
else
{
DBG1(DBG_IKE, "unable to allocate a CPI from kernel, IPComp disabled");
- this->ipcomp = IPCOMP_NONE;
+ }
+}
+
+/**
+ * handle a received notify payload
+ */
+static void handle_notify(private_child_create_t *this, notify_payload_t *notify)
+{
+ switch (notify->get_notify_type(notify))
+ {
+ case USE_TRANSPORT_MODE:
+ this->mode = MODE_TRANSPORT;
+ break;
+ case USE_BEET_MODE:
+ this->mode = MODE_BEET;
+ break;
+ case IPCOMP_SUPPORTED:
+ {
+ ipcomp_transform_t ipcomp;
+ u_int16_t cpi;
+ chunk_t data;
+
+ data = notify->get_notification_data(notify);
+ cpi = *(u_int16_t*)data.ptr;
+ ipcomp = (ipcomp_transform_t)(*(data.ptr + 2));
+ switch (ipcomp)
+ {
+ case IPCOMP_DEFLATE:
+ this->other_cpi = cpi;
+ this->ipcomp_received = ipcomp;
+ break;
+ case IPCOMP_LZS:
+ case IPCOMP_LZJH:
+ default:
+ DBG1(DBG_IKE, "received IPCOMP_SUPPORTED notify with a "
+ "transform ID we don't support %N",
+ ipcomp_transform_names, ipcomp);
+ break;
+ }
+ }
+ default:
+ break;
}
}
@@ -476,7 +575,6 @@ static void process_payloads(private_child_create_t *this, message_t *message)
sa_payload_t *sa_payload;
ke_payload_t *ke_payload;
ts_payload_t *ts_payload;
- notify_payload_t *notify_payload;
/* defaults to TUNNEL mode */
this->mode = MODE_TUNNEL;
@@ -512,37 +610,7 @@ static void process_payloads(private_child_create_t *this, message_t *message)
this->tsr = ts_payload->get_traffic_selectors(ts_payload);
break;
case NOTIFY:
- notify_payload = (notify_payload_t*)payload;
- switch (notify_payload ->get_notify_type(notify_payload ))
- {
- case USE_TRANSPORT_MODE:
- this->mode = MODE_TRANSPORT;
- break;
- case USE_BEET_MODE:
- this->mode = MODE_BEET;
- break;
- case IPCOMP_SUPPORTED:
- {
- chunk_t data = notify_payload->get_notification_data(notify_payload);
- u_int16_t cpi = *(u_int16_t*)data.ptr;
- ipcomp_transform_t ipcomp = (ipcomp_transform_t)(*(data.ptr + 2));
- switch(ipcomp)
- {
- case IPCOMP_DEFLATE:
- this->other_cpi = cpi;
- this->ipcomp_received = ipcomp;
- break;
- case IPCOMP_LZS:
- case IPCOMP_LZJH:
- default:
- DBG1(DBG_IKE, "received IPCOMP_SUPPORTED notify with a transform"
- " ID we don't support %N", ipcomp_transform_names, ipcomp);
- break;
- }
- }
- default:
- break;
- }
+ handle_notify(this, (notify_payload_t*)payload);
break;
default:
break;
@@ -557,9 +625,8 @@ static void process_payloads(private_child_create_t *this, message_t *message)
static status_t build_i(private_child_create_t *this, message_t *message)
{
host_t *me, *other, *vip;
- bool propose_all = FALSE;
peer_cfg_t *peer_cfg;
-
+
switch (message->get_exchange_type(message))
{
case IKE_SA_INIT:
@@ -610,23 +677,18 @@ static status_t build_i(private_child_create_t *this, message_t *message)
}
/* check if we want a virtual IP, but don't have one */
- if (!this->reqid)
+ peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+ vip = peer_cfg->get_virtual_ip(peer_cfg);
+ if (!this->reqid && vip)
{
- peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
- vip = peer_cfg->get_virtual_ip(peer_cfg);
- if (vip)
- {
- propose_all = TRUE;
- }
- }
-
- if (propose_all)
- { /* propose a 0.0.0.0/0 subnet when we use virtual ip */
+ /* propose a 0.0.0.0/0 or ::/0 subnet when we use virtual ip */
+ vip = host_create_any(vip->get_family(vip));
this->tsi = this->config->get_traffic_selectors(this->config, TRUE,
- NULL, NULL);
+ NULL, vip);
+ vip->destroy(vip);
}
else
- { /* but shorten a 0.0.0.0/0 subnet for host2host/we already have a vip */
+ { /* but narrow it for host2host / if we already have a vip */
this->tsi = this->config->get_traffic_selectors(this->config, TRUE,
NULL, me);
}
@@ -641,7 +703,7 @@ static status_t build_i(private_child_create_t *this, message_t *message)
this->ike_sa->get_other_host(this->ike_sa), this->config, this->reqid,
this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY));
- if (this->child_sa->alloc(this->child_sa, this->proposals) != SUCCESS)
+ if (!allocate_spi(this))
{
DBG1(DBG_IKE, "unable to allocate SPIs from kernel");
return FAILED;
@@ -652,10 +714,10 @@ static status_t build_i(private_child_create_t *this, message_t *message)
this->dh = this->keymat->create_dh(this->keymat, this->dh_group);
}
- if (this->config->use_ipcomp(this->config)) {
+ if (this->config->use_ipcomp(this->config))
+ {
/* IPCOMP_DEFLATE is the only transform we support at the moment */
- this->ipcomp = IPCOMP_DEFLATE;
- build_ipcomp_supported_notify(this, message);
+ add_ipcomp_notify(this, message, IPCOMP_DEFLATE);
}
build_payloads(this, message);
@@ -821,16 +883,17 @@ static status_t build_r(private_child_create_t *this, message_t *message)
this->ike_sa->get_other_host(this->ike_sa), this->config, this->reqid,
this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY));
- if (this->config->use_ipcomp(this->config) &&
- this->ipcomp_received != IPCOMP_NONE)
+ if (this->ipcomp_received != IPCOMP_NONE)
{
- this->ipcomp = this->ipcomp_received;
- build_ipcomp_supported_notify(this, message);
- }
- else if (this->ipcomp_received != IPCOMP_NONE)
- {
- DBG1(DBG_IKE, "received %N notify but IPComp is disabled, ignoring",
- notify_type_names, IPCOMP_SUPPORTED);
+ if (this->config->use_ipcomp(this->config))
+ {
+ add_ipcomp_notify(this, message, this->ipcomp_received);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received %N notify but IPComp is disabled, ignoring",
+ notify_type_names, IPCOMP_SUPPORTED);
+ }
}
switch (select_and_install(this, no_dh))
@@ -1052,6 +1115,8 @@ static void migrate(private_child_create_t *this, ike_sa_t *ike_sa)
}
this->ike_sa = ike_sa;
+ this->keymat = ike_sa->get_keymat(ike_sa);
+ this->proposal = NULL;
this->proposals = NULL;
this->tsi = NULL;
this->tsr = NULL;
@@ -1137,6 +1202,9 @@ child_create_t *child_create_create(ike_sa_t *ike_sa, child_cfg_t *config)
this->mode = MODE_TUNNEL;
this->ipcomp = IPCOMP_NONE;
this->ipcomp_received = IPCOMP_NONE;
+ this->my_spi = 0;
+ this->other_spi = 0;
+ this->my_cpi = 0;
this->other_cpi = 0;
this->reqid = 0;
this->established = FALSE;
diff --git a/src/charon/sa/tasks/child_delete.c b/src/charon/sa/tasks/child_delete.c
index cab1d63f0..0fd4a056b 100644
--- a/src/charon/sa/tasks/child_delete.c
+++ b/src/charon/sa/tasks/child_delete.c
@@ -12,7 +12,7 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
- * $Id: child_delete.c 4434 2008-10-14 08:52:13Z martin $
+ * $Id: child_delete.c 4730 2008-12-01 18:38:28Z martin $
*/
#include "child_delete.h"
@@ -44,9 +44,19 @@ struct private_child_delete_t {
bool initiator;
/**
- * wheter to enforce delete action policy
- */
- bool check_delete_action;
+ * Protocol of CHILD_SA to delete
+ */
+ protocol_id_t protocol;
+
+ /**
+ * Inbound SPI of CHILD_SA to delete
+ */
+ u_int32_t spi;
+
+ /**
+ * wheter to enforce delete action policy
+ */
+ bool check_delete_action;
/**
* CHILD_SAs which get deleted
@@ -238,6 +248,16 @@ static void log_children(private_child_delete_t *this)
*/
static status_t build_i(private_child_delete_t *this, message_t *message)
{
+ child_sa_t *child_sa;
+
+ child_sa = this->ike_sa->get_child_sa(this->ike_sa, this->protocol,
+ this->spi, TRUE);
+ if (!child_sa)
+ { /* child does not exist anymore */
+ return SUCCESS;
+ }
+ this->child_sas->insert_last(this->child_sas, child_sa);
+
log_children(this);
build_payloads(this, message);
return NEED_MORE;
@@ -323,7 +343,8 @@ static void destroy(private_child_delete_t *this)
/*
* Described in header.
*/
-child_delete_t *child_delete_create(ike_sa_t *ike_sa, child_sa_t *child_sa)
+child_delete_t *child_delete_create(ike_sa_t *ike_sa, protocol_id_t protocol,
+ u_int32_t spi)
{
private_child_delete_t *this = malloc_thing(private_child_delete_t);
@@ -335,13 +356,14 @@ child_delete_t *child_delete_create(ike_sa_t *ike_sa, child_sa_t *child_sa)
this->ike_sa = ike_sa;
this->check_delete_action = FALSE;
this->child_sas = linked_list_create();
+ this->protocol = protocol;
+ this->spi = spi;
- if (child_sa != NULL)
+ if (protocol != PROTO_NONE)
{
this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
this->initiator = TRUE;
- this->child_sas->insert_last(this->child_sas, child_sa);
}
else
{
diff --git a/src/charon/sa/tasks/child_delete.h b/src/charon/sa/tasks/child_delete.h
index c304ea9d8..c5ebec338 100644
--- a/src/charon/sa/tasks/child_delete.h
+++ b/src/charon/sa/tasks/child_delete.h
@@ -12,7 +12,7 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
- * $Id: child_delete.h 3589 2008-03-13 14:14:44Z martin $
+ * $Id: child_delete.h 4730 2008-12-01 18:38:28Z martin $
*/
/**
@@ -52,9 +52,11 @@ struct child_delete_t {
* Create a new child_delete task.
*
* @param ike_sa IKE_SA this task works for
- * @param child_sa CHILD_SA to delete, or NULL as responder
+ * @param protocol protocol of CHILD_SA to delete, PROTO_NONE as responder
+ * @param spi inbound SPI of CHILD_SA to delete
* @return child_delete task to handle by the task_manager
*/
-child_delete_t *child_delete_create(ike_sa_t *ike_sa, child_sa_t *child_sa);
+child_delete_t *child_delete_create(ike_sa_t *ike_sa, protocol_id_t protocol,
+ u_int32_t spi);
#endif /* CHILD_DELETE_H_ @} */
diff --git a/src/charon/sa/tasks/child_rekey.c b/src/charon/sa/tasks/child_rekey.c
index e50ad33be..0d8cf2db7 100644
--- a/src/charon/sa/tasks/child_rekey.c
+++ b/src/charon/sa/tasks/child_rekey.c
@@ -13,7 +13,7 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
- * $Id: child_rekey.c 4659 2008-11-14 14:05:47Z martin $
+ * $Id: child_rekey.c 4730 2008-12-01 18:38:28Z martin $
*/
#include "child_rekey.h"
@@ -49,11 +49,26 @@ struct private_child_rekey_t {
bool initiator;
/**
+ * Protocol of CHILD_SA to rekey
+ */
+ protocol_id_t protocol;
+
+ /**
+ * Inbound SPI of CHILD_SA to rekey
+ */
+ u_int32_t spi;
+
+ /**
* the CHILD_CREATE task which is reused to simplify rekeying
*/
child_create_t *child_create;
/**
+ * the CHILD_DELETE task to delete rekeyed CHILD_SA
+ */
+ child_delete_t *child_delete;
+
+ /**
* CHILD_SA which gets rekeyed
*/
child_sa_t *child_sa;
@@ -65,6 +80,25 @@ struct private_child_rekey_t {
};
/**
+ * Implementation of task_t.build for initiator, after rekeying
+ */
+static status_t build_i_delete(private_child_rekey_t *this, message_t *message)
+{
+ /* update exchange type to INFORMATIONAL for the delete */
+ message->set_exchange_type(message, INFORMATIONAL);
+
+ return this->child_delete->task.build(&this->child_delete->task, message);
+}
+
+/**
+ * Implementation of task_t.process for initiator, after rekeying
+ */
+static status_t process_i_delete(private_child_rekey_t *this, message_t *message)
+{
+ return this->child_delete->task.process(&this->child_delete->task, message);
+}
+
+/**
* find a child using the REKEY_SA notify
*/
static void find_child(private_child_rekey_t *this, message_t *message)
@@ -104,25 +138,33 @@ static void find_child(private_child_rekey_t *this, message_t *message)
* Implementation of task_t.build for initiator
*/
static status_t build_i(private_child_rekey_t *this, message_t *message)
-{
+{
notify_payload_t *notify;
- protocol_id_t protocol;
- u_int32_t spi, reqid;
+ u_int32_t reqid;
+ child_cfg_t *config;
+
+ this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, this->protocol,
+ this->spi, TRUE);
+ if (!this->child_sa)
+ { /* CHILD_SA is gone, unable to rekey */
+ return SUCCESS;
+ }
+ config = this->child_sa->get_config(this->child_sa);
/* we just need the rekey notify ... */
- protocol = this->child_sa->get_protocol(this->child_sa);
- spi = this->child_sa->get_spi(this->child_sa, TRUE);
- notify = notify_payload_create_from_protocol_and_type(protocol, REKEY_SA);
- notify->set_spi(notify, spi);
+ notify = notify_payload_create_from_protocol_and_type(this->protocol,
+ REKEY_SA);
+ notify->set_spi(notify, this->spi);
message->add_payload(message, (payload_t*)notify);
-
+
/* ... our CHILD_CREATE task does the hard work for us. */
reqid = this->child_sa->get_reqid(this->child_sa);
+ this->child_create = child_create_create(this->ike_sa, config);
this->child_create->use_reqid(this->child_create, reqid);
this->child_create->task.build(&this->child_create->task, message);
this->child_sa->set_state(this->child_sa, CHILD_REKEYING);
-
+
return NEED_MORE;
}
@@ -133,7 +175,7 @@ static status_t process_r(private_child_rekey_t *this, message_t *message)
{
/* let the CHILD_CREATE task process the message */
this->child_create->task.process(&this->child_create->task, message);
-
+
find_child(this, message);
return NEED_MORE;
@@ -265,11 +307,13 @@ static status_t process_i(private_child_rekey_t *this, message_t *message)
spi = to_delete->get_spi(to_delete, TRUE);
protocol = to_delete->get_protocol(to_delete);
- if (this->ike_sa->delete_child_sa(this->ike_sa, protocol, spi) != SUCCESS)
- {
- return FAILED;
- }
- return SUCCESS;
+
+ /* rekeying done, delete the obsolete CHILD_SA using a subtask */
+ this->child_delete = child_delete_create(this->ike_sa, protocol, spi);
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i_delete;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i_delete;
+
+ return NEED_MORE;
}
/**
@@ -319,9 +363,16 @@ static void collide(private_child_rekey_t *this, task_t *other)
*/
static void migrate(private_child_rekey_t *this, ike_sa_t *ike_sa)
{
- this->child_create->task.migrate(&this->child_create->task, ike_sa);
+ if (this->child_create)
+ {
+ this->child_create->task.migrate(&this->child_create->task, ike_sa);
+ }
+ if (this->child_delete)
+ {
+ this->child_delete->task.migrate(&this->child_delete->task, ike_sa);
+ }
DESTROY_IF(this->collision);
-
+
this->ike_sa = ike_sa;
this->collision = NULL;
}
@@ -331,7 +382,14 @@ static void migrate(private_child_rekey_t *this, ike_sa_t *ike_sa)
*/
static void destroy(private_child_rekey_t *this)
{
- this->child_create->task.destroy(&this->child_create->task);
+ if (this->child_create)
+ {
+ this->child_create->task.destroy(&this->child_create->task);
+ }
+ if (this->child_delete)
+ {
+ this->child_delete->task.destroy(&this->child_delete->task);
+ }
DESTROY_IF(this->collision);
free(this);
}
@@ -339,22 +397,21 @@ static void destroy(private_child_rekey_t *this)
/*
* Described in header.
*/
-child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa)
+child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, protocol_id_t protocol,
+ u_int32_t spi)
{
- child_cfg_t *config;
private_child_rekey_t *this = malloc_thing(private_child_rekey_t);
-
+
this->public.collide = (void (*)(child_rekey_t*,task_t*))collide;
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;
- if (child_sa != NULL)
+ if (protocol != PROTO_NONE)
{
this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
this->initiator = TRUE;
- config = child_sa->get_config(child_sa);
- this->child_create = child_create_create(ike_sa, config);
+ this->child_create = NULL;
}
else
{
@@ -365,8 +422,11 @@ child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa)
}
this->ike_sa = ike_sa;
- this->child_sa = child_sa;
+ this->child_sa = NULL;
+ this->protocol = protocol;
+ this->spi = spi;
this->collision = NULL;
+ this->child_delete = NULL;
return &this->public;
}
diff --git a/src/charon/sa/tasks/child_rekey.h b/src/charon/sa/tasks/child_rekey.h
index b386ef3c6..37b61a9ef 100644
--- a/src/charon/sa/tasks/child_rekey.h
+++ b/src/charon/sa/tasks/child_rekey.h
@@ -12,7 +12,7 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
- * $Id: child_rekey.h 3589 2008-03-13 14:14:44Z martin $
+ * $Id: child_rekey.h 4730 2008-12-01 18:38:28Z martin $
*/
/**
@@ -56,9 +56,11 @@ struct child_rekey_t {
* Create a new CHILD_REKEY task.
*
* @param ike_sa IKE_SA this task works for
- * @param child_sa child_sa to rekey, NULL if responder
+ * @param protocol protocol of CHILD_SA to rekey, PROTO_NONE as responder
+ * @param spi inbound SPI of CHILD_SA to rekey
* @return child_rekey task to handle by the task_manager
*/
-child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa);
+child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, protocol_id_t protocol,
+ u_int32_t spi);
#endif /* CHILD_REKEY_H_ @} */
diff --git a/src/charon/sa/tasks/ike_auth.c b/src/charon/sa/tasks/ike_auth.c
index 5c3f33cbd..93b145755 100644
--- a/src/charon/sa/tasks/ike_auth.c
+++ b/src/charon/sa/tasks/ike_auth.c
@@ -13,7 +13,7 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details
*
- * $Id: ike_auth.c 4463 2008-10-20 11:38:16Z martin $
+ * $Id: ike_auth.c 4858 2009-02-10 17:21:44Z martin $
*/
#include "ike_auth.h"
@@ -88,70 +88,6 @@ struct private_ike_auth_t {
};
/**
- * check uniqueness and delete duplicates
- */
-static bool check_uniqueness(private_ike_auth_t *this)
-{
- ike_sa_t *duplicate;
- unique_policy_t policy;
- status_t status = SUCCESS;
- peer_cfg_t *peer_cfg;
- bool cancel = FALSE;
-
- peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
- policy = peer_cfg->get_unique_policy(peer_cfg);
- if (policy == UNIQUE_NO)
- {
- return FALSE;
- }
- duplicate = charon->ike_sa_manager->checkout_duplicate(
- charon->ike_sa_manager, this->ike_sa);
- if (duplicate)
- {
- peer_cfg = duplicate->get_peer_cfg(duplicate);
- if (peer_cfg &&
- peer_cfg->equals(peer_cfg, this->ike_sa->get_peer_cfg(this->ike_sa)))
- {
- switch (duplicate->get_state(duplicate))
- {
- case IKE_ESTABLISHED:
- case IKE_REKEYING:
- switch (policy)
- {
- case UNIQUE_REPLACE:
- DBG1(DBG_IKE, "deleting duplicate IKE_SA due "
- "uniqueness policy");
- status = duplicate->delete(duplicate);
- break;
- case UNIQUE_KEEP:
- DBG1(DBG_IKE, "cancelling IKE_SA setup due "
- "uniqueness policy");
- cancel = TRUE;
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
- }
- if (status == DESTROY_ME)
- {
- charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
- duplicate);
- }
- else
- {
- charon->ike_sa_manager->checkin(charon->ike_sa_manager, duplicate);
- }
- }
- /* set threads active IKE_SA after checkin */
- charon->bus->set_sa(charon->bus, this->ike_sa);
- return cancel;
-}
-
-/**
* get the authentication class of a config
*/
auth_class_t get_auth_class(peer_cfg_t *config)
@@ -400,6 +336,12 @@ static status_t build_auth_eap(private_ike_auth_t *this, message_t *message)
authenticator_t *auth;
auth_payload_t *auth_payload;
+ if (!this->initiator && !this->peer_authenticated)
+ {
+ message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty);
+ return FAILED;
+ }
+
auth = (authenticator_t*)this->eap_auth;
if (auth->build(auth, this->my_packet->get_data(this->my_packet),
this->other_nonce, &auth_payload) != SUCCESS)
@@ -681,8 +623,10 @@ static status_t build_r(private_ike_auth_t *this, message_t *message)
return FAILED;
}
- if (check_uniqueness(this))
+ if (charon->ike_sa_manager->check_uniqueness(charon->ike_sa_manager,
+ this->ike_sa))
{
+ DBG1(DBG_IKE, "cancelling IKE_SA setup due uniqueness policy");
message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty);
return FAILED;
}
diff --git a/src/charon/sa/tasks/ike_config.c b/src/charon/sa/tasks/ike_config.c
index e89f381d3..b890e93ba 100644
--- a/src/charon/sa/tasks/ike_config.c
+++ b/src/charon/sa/tasks/ike_config.c
@@ -13,7 +13,7 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
- * $Id: ike_config.c 4129 2008-07-01 06:36:52Z martin $
+ * $Id: ike_config.c 4867 2009-02-13 11:57:50Z andreas $
*/
#include "ike_config.h"
@@ -21,6 +21,9 @@
#include <daemon.h>
#include <encoding/payloads/cp_payload.h>
+#define DNS_SERVER_MAX 2
+#define NBNS_SERVER_MAX 2
+
typedef struct private_ike_config_t private_ike_config_t;
/**
@@ -52,6 +55,11 @@ struct private_ike_config_t {
* list of DNS servers
*/
linked_list_t *dns;
+
+ /**
+ * list of WINS servers
+ */
+ linked_list_t *nbns;
};
/**
@@ -121,7 +129,10 @@ static void build_payloads(private_ike_config_t *this, message_t *message,
else
{
host_t *ip;
- iterator_t *iterator = this->dns->create_iterator(this->dns, TRUE);
+ iterator_t *iterator;
+
+ /* Add internal DNS servers */
+ iterator = this->dns->create_iterator(this->dns, TRUE);
while (iterator->iterate(iterator, (void**)&ip))
{
ca = configuration_attribute_create();
@@ -138,6 +149,25 @@ static void build_payloads(private_ike_config_t *this, message_t *message,
cp->add_configuration_attribute(cp, ca);
}
iterator->destroy(iterator);
+
+ /* Add internal WINS servers */
+ iterator = this->nbns->create_iterator(this->nbns, TRUE);
+ while (iterator->iterate(iterator, (void**)&ip))
+ {
+ ca = configuration_attribute_create();
+ if (ip->get_family(ip) == AF_INET)
+ {
+ ca->set_type(ca, INTERNAL_IP4_NBNS);
+ }
+ else
+ {
+ ca->set_type(ca, INTERNAL_IP6_NBNS);
+ }
+ chunk = ip->get_address(ip);
+ ca->set_value(ca, chunk);
+ cp->add_configuration_attribute(cp, ca);
+ }
+ iterator->destroy(iterator);
}
message->add_payload(message, (payload_t*)cp);
}
@@ -201,7 +231,22 @@ static void process_attribute(private_ike_config_t *this,
}
case INTERNAL_IP4_NBNS:
case INTERNAL_IP6_NBNS:
- /* TODO */
+ {
+ addr = ca->get_value(ca);
+ if (addr.len == 0)
+ {
+ ip = host_create_any(family);
+ }
+ else
+ {
+ ip = host_create_from_chunk(family, addr, 0);
+ }
+ if (ip)
+ {
+ this->nbns->insert_last(this->nbns, ip);
+ }
+ break;
+ }
default:
DBG1(DBG_IKE, "ignoring %N config attribute",
configuration_attribute_type_names,
@@ -351,7 +396,7 @@ static status_t process_i(private_ike_config_t *this, message_t *message)
process_payloads(this, message);
if (this->virtual_ip == NULL)
- { /* force a configured virtual IP, even server didn't return one */
+ { /* force a configured virtual IP, even if server didn't return one */
config = this->ike_sa->get_peer_cfg(this->ike_sa);
this->virtual_ip = config->get_virtual_ip(config);
if (this->virtual_ip)
@@ -406,6 +451,7 @@ static void destroy(private_ike_config_t *this)
{
DESTROY_IF(this->virtual_ip);
this->dns->destroy_offset(this->dns, offsetof(host_t, destroy));
+ this->nbns->destroy_offset(this->nbns, offsetof(host_t, destroy));
free(this);
}
@@ -420,6 +466,12 @@ ike_config_t *ike_config_create(ike_sa_t *ike_sa, bool initiator)
this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
this->public.task.destroy = (void(*)(task_t*))destroy;
+ this->initiator = initiator;
+ this->ike_sa = ike_sa;
+ this->virtual_ip = NULL;
+ this->dns = linked_list_create();
+ this->nbns = linked_list_create();
+
if (initiator)
{
this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
@@ -427,13 +479,49 @@ ike_config_t *ike_config_create(ike_sa_t *ike_sa, bool initiator)
}
else
{
+ int i;
+
+ /* assign DNS servers */
+ for (i = 1; i <= DNS_SERVER_MAX; i++)
+ {
+ char dns_key[16], *dns_str;
+
+ snprintf(dns_key, sizeof(dns_key), "charon.dns%d", i);
+ dns_str = lib->settings->get_str(lib->settings, dns_key, NULL);
+ if (dns_str)
+ {
+ host_t *dns = host_create_from_string(dns_str, 0);
+
+ if (dns)
+ {
+ DBG2(DBG_CFG, "assigning DNS server %H to peer", dns);
+ this->dns->insert_last(this->dns, dns);
+ }
+ }
+ }
+
+ /* assign WINS servers */
+ for (i = 1; i <= NBNS_SERVER_MAX; i++)
+ {
+ char nbns_key[16], *nbns_str;
+
+ snprintf(nbns_key, sizeof(nbns_key), "charon.nbns%d", i);
+ nbns_str = lib->settings->get_str(lib->settings, nbns_key, NULL);
+ if (nbns_str)
+ {
+ host_t *nbns = host_create_from_string(nbns_str, 0);
+
+ if (nbns)
+ {
+ DBG2(DBG_CFG, "assigning NBNS server %H to peer", nbns);
+ this->nbns->insert_last(this->nbns, nbns);
+ }
+ }
+ }
+
this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
}
- this->initiator = initiator;
- this->ike_sa = ike_sa;
- this->virtual_ip = NULL;
- this->dns = linked_list_create();
-
+
return &this->public;
}
diff --git a/src/charon/sa/tasks/ike_init.c b/src/charon/sa/tasks/ike_init.c
index bd2cd39bb..139107480 100644
--- a/src/charon/sa/tasks/ike_init.c
+++ b/src/charon/sa/tasks/ike_init.c
@@ -14,7 +14,7 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
- * $Id: ike_init.c 4531 2008-10-30 12:58:54Z martin $
+ * $Id: ike_init.c 4717 2008-11-28 09:51:44Z martin $
*/
#include "ike_init.h"
@@ -370,13 +370,46 @@ static status_t process_r(private_ike_init_t *this, message_t *message)
}
/**
- * Implementation of task_t.build for responder
+ * Derive the keymat for the IKE_SA
*/
-static status_t build_r(private_ike_init_t *this, message_t *message)
+static bool derive_keys(private_ike_init_t *this,
+ chunk_t nonce_i, chunk_t nonce_r)
{
- keymat_t *old_keymat = NULL;
+ keymat_t *old_keymat;
+ pseudo_random_function_t prf_alg = PRF_UNDEFINED;
+ chunk_t skd = chunk_empty;
ike_sa_id_t *id;
+ id = this->ike_sa->get_id(this->ike_sa);
+ if (this->old_sa)
+ {
+ /* rekeying: Include old SKd, use old PRF, apply SPI */
+ old_keymat = this->old_sa->get_keymat(this->old_sa);
+ prf_alg = old_keymat->get_skd(old_keymat, &skd);
+ if (this->initiator)
+ {
+ id->set_responder_spi(id, this->proposal->get_spi(this->proposal));
+ }
+ else
+ {
+ id->set_initiator_spi(id, this->proposal->get_spi(this->proposal));
+ }
+ }
+ if (!this->keymat->derive_ike_keys(this->keymat, this->proposal, this->dh,
+ nonce_i, nonce_r, id, prf_alg, skd))
+ {
+ return FALSE;
+ }
+ charon->bus->ike_keys(charon->bus, this->ike_sa, this->dh,
+ nonce_i, nonce_r, this->old_sa);
+ return TRUE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_init_t *this, message_t *message)
+{
/* check if we have everything we need */
if (this->proposal == NULL ||
this->other_nonce.len == 0 || this->my_nonce.len == 0)
@@ -410,23 +443,12 @@ static status_t build_r(private_ike_init_t *this, message_t *message)
return FAILED;
}
- id = this->ike_sa->get_id(this->ike_sa);
- if (this->old_sa)
- { /* rekeying: Apply SPI, include keymat from old SA in key derivation */
- id->set_initiator_spi(id, this->proposal->get_spi(this->proposal));
- old_keymat = this->old_sa->get_keymat(this->old_sa);
- }
- if (!this->keymat->derive_ike_keys(this->keymat, this->proposal, this->dh,
- this->other_nonce, this->my_nonce, id, old_keymat))
+ if (!derive_keys(this, this->other_nonce, this->my_nonce))
{
DBG1(DBG_IKE, "key derivation failed");
message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
return FAILED;
}
-
- charon->bus->ike_keys(charon->bus, this->ike_sa, this->dh,
- this->other_nonce, this->my_nonce, this->old_sa);
-
build_payloads(this, message);
return SUCCESS;
}
@@ -436,8 +458,6 @@ static status_t build_r(private_ike_init_t *this, message_t *message)
*/
static status_t process_i(private_ike_init_t *this, message_t *message)
{
- keymat_t *old_keymat = NULL;
- ike_sa_id_t *id;
iterator_t *iterator;
payload_t *payload;
@@ -521,22 +541,11 @@ static status_t process_i(private_ike_init_t *this, message_t *message)
return FAILED;
}
- id = this->ike_sa->get_id(this->ike_sa);
- if (this->old_sa)
- { /* rekeying: Apply SPI, include keymat from old SA in key derivation */
- id->set_responder_spi(id, this->proposal->get_spi(this->proposal));
- old_keymat = this->old_sa->get_keymat(this->old_sa);
- }
- if (!this->keymat->derive_ike_keys(this->keymat, this->proposal, this->dh,
- this->my_nonce, this->other_nonce, id, old_keymat))
+ if (!derive_keys(this, this->my_nonce, this->other_nonce))
{
DBG1(DBG_IKE, "key derivation failed");
return FAILED;
}
-
- charon->bus->ike_keys(charon->bus, this->ike_sa, this->dh,
- this->my_nonce, this->other_nonce, this->old_sa);
-
return SUCCESS;
}
diff --git a/src/charon/sa/tasks/ike_mobike.c b/src/charon/sa/tasks/ike_mobike.c
index a791d1892..b5e065081 100644
--- a/src/charon/sa/tasks/ike_mobike.c
+++ b/src/charon/sa/tasks/ike_mobike.c
@@ -12,7 +12,7 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
- * $Id: ike_mobike.c 4618 2008-11-11 09:22:00Z tobias $
+ * $Id: ike_mobike.c 4816 2008-12-19 14:34:40Z martin $
*/
#include "ike_mobike.h"
@@ -24,6 +24,7 @@
#include <encoding/payloads/notify_payload.h>
#define COOKIE2_SIZE 16
+#define MAX_ADDITIONAL_ADDRS 8
typedef struct private_ike_mobike_t private_ike_mobike_t;
@@ -191,8 +192,8 @@ static void build_address_list(private_ike_mobike_t *this, message_t *message)
enumerator_t *enumerator;
host_t *host, *me;
notify_type_t type;
- bool additional = FALSE;
-
+ int added = 0;
+
me = this->ike_sa->get_my_host(this->ike_sa);
enumerator = charon->kernel_interface->create_address_enumerator(
charon->kernel_interface, FALSE, FALSE);
@@ -214,9 +215,13 @@ static void build_address_list(private_ike_mobike_t *this, message_t *message)
continue;
}
message->add_notify(message, FALSE, type, host->get_address(host));
- additional = TRUE;
+ if (++added >= MAX_ADDITIONAL_ADDRS)
+ { /* limit number of notifys, some implementations do not like too
+ * many of them (f.e. strongSwan ;-) */
+ break;
+ }
}
- if (!additional)
+ if (!added)
{
message->add_notify(message, FALSE, NO_ADDITIONAL_ADDRESSES, chunk_empty);
}
@@ -251,7 +256,7 @@ static void update_children(private_ike_mobike_t *this)
iterator = this->ike_sa->create_child_sa_iterator(this->ike_sa);
while (iterator->iterate(iterator, (void**)&child_sa))
{
- if (child_sa->update_hosts(child_sa,
+ if (child_sa->update(child_sa,
this->ike_sa->get_my_host(this->ike_sa),
this->ike_sa->get_other_host(this->ike_sa),
this->ike_sa->get_virtual_ip(this->ike_sa, TRUE),
@@ -516,6 +521,10 @@ static status_t process_i(private_ike_mobike_t *this, message_t *message)
/* start the update with the same task */
this->check = FALSE;
this->address = FALSE;
+ if (this->natd)
+ {
+ this->natd->task.destroy(&this->natd->task);
+ }
this->natd = ike_natd_create(this->ike_sa, this->initiator);
this->ike_sa->set_pending_updates(this->ike_sa, 1);
return NEED_MORE;
diff --git a/src/charon/sa/tasks/ike_rekey.c b/src/charon/sa/tasks/ike_rekey.c
index 28d63cca7..e61d161bc 100644
--- a/src/charon/sa/tasks/ike_rekey.c
+++ b/src/charon/sa/tasks/ike_rekey.c
@@ -13,7 +13,7 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
- * $Id: ike_rekey.c 4659 2008-11-14 14:05:47Z martin $
+ * $Id: ike_rekey.c 4730 2008-12-01 18:38:28Z martin $
*/
#include "ike_rekey.h"
@@ -21,6 +21,7 @@
#include <daemon.h>
#include <encoding/payloads/notify_payload.h>
#include <sa/tasks/ike_init.h>
+#include <sa/tasks/ike_delete.h>
#include <processing/jobs/delete_ike_sa_job.h>
#include <processing/jobs/rekey_ike_sa_job.h>
@@ -58,12 +59,36 @@ struct private_ike_rekey_t {
ike_init_t *ike_init;
/**
+ * IKE_DELETE task to delete the old IKE_SA after rekeying was successful
+ */
+ ike_delete_t *ike_delete;
+
+ /**
* colliding task detected by the task manager
*/
task_t *collision;
};
/**
+ * Implementation of task_t.build for initiator, after rekeying
+ */
+static status_t build_i_delete(private_ike_rekey_t *this, message_t *message)
+{
+ /* update exchange type to INFORMATIONAL for the delete */
+ message->set_exchange_type(message, INFORMATIONAL);
+
+ return this->ike_delete->task.build(&this->ike_delete->task, message);
+}
+
+/**
+ * Implementation of task_t.process for initiator, after rekeying
+ */
+static status_t process_i_delete(private_ike_rekey_t *this, message_t *message)
+{
+ return this->ike_delete->task.process(&this->ike_delete->task, message);
+}
+
+/**
* Implementation of task_t.build for initiator
*/
static status_t build_i(private_ike_rekey_t *this, message_t *message)
@@ -168,7 +193,6 @@ static status_t build_r(private_ike_rekey_t *this, message_t *message)
*/
static status_t process_i(private_ike_rekey_t *this, message_t *message)
{
- job_t *job;
ike_sa_id_t *to_delete;
iterator_t *iterator;
payload_t *payload;
@@ -271,10 +295,12 @@ static status_t process_i(private_ike_rekey_t *this, message_t *message)
charon->bus->set_sa(charon->bus, this->ike_sa);
}
- job = (job_t*)delete_ike_sa_job_create(to_delete, TRUE);
- charon->processor->queue_job(charon->processor, job);
+ /* rekeying successful, delete the IKE_SA using a subtask */
+ this->ike_delete = ike_delete_create(this->ike_sa, TRUE);
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i_delete;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i_delete;
- return SUCCESS;
+ return NEED_MORE;
}
/**
@@ -300,6 +326,10 @@ static void migrate(private_ike_rekey_t *this, ike_sa_t *ike_sa)
{
this->ike_init->task.destroy(&this->ike_init->task);
}
+ if (this->ike_delete)
+ {
+ this->ike_delete->task.destroy(&this->ike_delete->task);
+ }
if (this->new_sa)
{
charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
@@ -308,11 +338,12 @@ static void migrate(private_ike_rekey_t *this, ike_sa_t *ike_sa)
charon->bus->set_sa(charon->bus, this->ike_sa);
}
DESTROY_IF(this->collision);
-
+
this->collision = NULL;
this->ike_sa = ike_sa;
this->new_sa = NULL;
this->ike_init = NULL;
+ this->ike_delete = NULL;
}
/**
@@ -339,6 +370,10 @@ static void destroy(private_ike_rekey_t *this)
{
this->ike_init->task.destroy(&this->ike_init->task);
}
+ if (this->ike_delete)
+ {
+ this->ike_delete->task.destroy(&this->ike_delete->task);
+ }
DESTROY_IF(this->collision);
free(this);
}
@@ -368,6 +403,7 @@ ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator)
this->ike_sa = ike_sa;
this->new_sa = NULL;
this->ike_init = NULL;
+ this->ike_delete = NULL;
this->initiator = initiator;
this->collision = NULL;