implement proper handling of most simultaneous IKE_SA rekeying cases
authorMartin Willi <martin@strongswan.org>
Wed, 23 Aug 2006 07:30:43 +0000 (07:30 -0000)
committerMartin Willi <martin@strongswan.org>
Wed, 23 Aug 2006 07:30:43 +0000 (07:30 -0000)
src/charon/queues/jobs/incoming_packet_job.c
src/charon/sa/ike_sa.c
src/charon/sa/ike_sa.h
src/charon/sa/transactions/create_child_sa.c
src/charon/sa/transactions/delete_child_sa.c
src/charon/sa/transactions/ike_sa_init.c
src/charon/sa/transactions/rekey_ike_sa.c
src/charon/sa/transactions/transaction.c

index 69ef141..549d062 100644 (file)
@@ -79,12 +79,14 @@ static void send_notify_response(private_incoming_packet_job_t *this,
        src = request->get_destination(request);
        response->set_source(response, src->clone(src));
        response->set_destination(response, dst->clone(dst));
-       response->set_exchange_type(response, IKE_SA_INIT);
+       response->set_exchange_type(response, request->get_exchange_type(request));
        response->set_request(response, FALSE);
        response->set_message_id(response, 0);
        response->set_ike_sa_id(response, ike_sa_id);
        notify = notify_payload_create_from_protocol_and_type(PROTO_NONE, type);
        response->add_payload(response, (payload_t *)notify);
+       /* generation may fail, as most messages need a crypter/signer.
+        * TODO: Use transforms implementing the "NULL" algorithm */
        if (response->generate(response, NULL, NULL, &packet) != SUCCESS)
        {
                response->destroy(response);
index fba94cc..d129cce 100644 (file)
@@ -64,6 +64,7 @@ mapping_t ike_sa_state_m[] = {
        {IKE_CREATED, "CREATED"},
        {IKE_CONNECTING, "CONNECTING"},
        {IKE_ESTABLISHED, "ESTABLISHED"},
+       {IKE_REKEYING, "REKEYING"},
        {IKE_DELETING, "DELETING"},
        {MAPPING_END, NULL}
 };
@@ -225,6 +226,11 @@ struct private_ike_sa_t {
         * do multi transaction operations.
         */
        transaction_t *transaction_in_next;
+       
+       /**
+        * Transaction which rekeys this IKE_SA, used do detect simultaneus rekeying
+        */
+       rekey_ike_sa_t *rekeying_transaction;
 };
 
 /**
@@ -840,10 +846,12 @@ static status_t initiate(private_ike_sa_t *this,
                        return queue_transaction(this, (transaction_t*)ike_sa_init, TRUE);
                }
                case IKE_DELETING:
+               case IKE_REKEYING:
                {
-                       /* if we are in DELETING, we deny set up of a policy. */
+                       /* if we are in DELETING/REKEYING, we deny set up of a policy. */
                        this->logger->log(this->logger, CONTROL, 
-                                                         "creating CHILD_SA discarded, as IKE_SA is deleting");
+                                                         "creating CHILD_SA discarded, as IKE_SA is in state %s",
+                                                         mapping_find(ike_sa_state_m, this->state));
                        policy->destroy(policy);
                        connection->destroy(connection);
                        return FAILED;
@@ -1084,7 +1092,9 @@ static status_t route(private_ike_sa_t *this, connection_t *connection, policy_t
                        set_name(this, connection->get_name(connection));
                        break;
                case IKE_ESTABLISHED:
-                       /* nothing to do */
+               case IKE_REKEYING:
+                       /* nothing to do. We allow it for rekeying, as it will be
+                        * adopted by the new IKE_SA */
                        break;
                case IKE_DELETING:
                        /* deny */
@@ -1331,7 +1341,7 @@ static void set_other_id(private_ike_sa_t *this, identification_t *other)
 static status_t derive_keys(private_ike_sa_t *this,
                                                        proposal_t *proposal, diffie_hellman_t *dh,
                                                        chunk_t nonce_i, chunk_t nonce_r,
-                                                       bool initiator, prf_t *rekey_prf)
+                                                       bool initiator, prf_t *child_prf, prf_t *old_prf)
 {
        prf_plus_t *prf_plus;
        chunk_t skeyseed, secret, key, nonces, prf_plus_seed;
@@ -1368,7 +1378,7 @@ static status_t derive_keys(private_ike_sa_t *this,
         *
         * if we are rekeying, SKEYSEED built on another way 
         */
-       if (rekey_prf == NULL) /* not rekeying */
+       if (child_prf == NULL) /* not rekeying */
        {       
                /* SKEYSEED = prf(Ni | Nr, g^ir) */
                this->prf->set_key(this->prf, nonces);
@@ -1381,14 +1391,15 @@ static status_t derive_keys(private_ike_sa_t *this,
        }
        else
        {
-               /* SKEYSEED = prf(SK_d (old), [g^ir (new)] | Ni | Nr) */
+               /* SKEYSEED = prf(SK_d (old), [g^ir (new)] | Ni | Nr) 
+                * use OLD SAs PRF functions for both prf_plus and prf */
                secret = chunk_cat("mc", secret, nonces);
-               rekey_prf->allocate_bytes(rekey_prf, secret, &skeyseed);
+               child_prf->allocate_bytes(child_prf, secret, &skeyseed);
                this->logger->log_chunk(this->logger, PRIVATE|LEVEL1, "SKEYSEED", skeyseed);
-               rekey_prf->set_key(rekey_prf, skeyseed); /* abuse of rekeyed SAs child_prf */
+               old_prf->set_key(old_prf, skeyseed);
                chunk_free(&skeyseed);
                chunk_free(&secret);
-               prf_plus = prf_plus_create(rekey_prf, prf_plus_seed);
+               prf_plus = prf_plus_create(old_prf, prf_plus_seed);
        }
        chunk_free(&nonces);
        chunk_free(&prf_plus_seed);
@@ -1421,12 +1432,12 @@ static status_t derive_keys(private_ike_sa_t *this,
        key_size = signer_i->get_key_size(signer_i);
        
        prf_plus->allocate_bytes(prf_plus, key_size, &key);
-       this->logger->log_chunk(this->logger, CONTROL|LEVEL1, "Sk_ai secret", key);
+       this->logger->log_chunk(this->logger, PRIVATE, "Sk_ai secret", key);
        signer_i->set_key(signer_i, key);
        chunk_free(&key);
        
        prf_plus->allocate_bytes(prf_plus, key_size, &key);
-       this->logger->log_chunk(this->logger, CONTROL|LEVEL1, "Sk_ar secret", key);
+       this->logger->log_chunk(this->logger, PRIVATE, "Sk_ar secret", key);
        signer_r->set_key(signer_r, key);
        chunk_free(&key);
        
@@ -1547,7 +1558,7 @@ static child_sa_t* get_child_sa(private_ike_sa_t *this, protocol_id_t protocol,
        {
                iterator->current(iterator, (void**)&current);
                if (current->get_spi(current, inbound) == spi &&
-                       current->get_protocol(current) == protocol)
+                                 current->get_protocol(current) == protocol)
                {
                        found = current;
                }
@@ -1557,6 +1568,14 @@ static child_sa_t* get_child_sa(private_ike_sa_t *this, protocol_id_t protocol,
 }
 
 /**
+ * Implementation of ike_sa_t.create_child_sa_iterator.
+ */
+static iterator_t* create_child_sa_iterator(private_ike_sa_t *this)
+{
+       return this->child_sas->create_iterator(this->child_sas, TRUE);
+}
+
+/**
  * 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)
@@ -1671,6 +1690,22 @@ static status_t rekey(private_ike_sa_t *this)
 }
 
 /**
+ * Implementation of ike_sa_t.get_rekeying_transaction.
+ */
+static rekey_ike_sa_t* get_rekeying_transaction(private_ike_sa_t *this)
+{
+       return this->rekeying_transaction;
+}
+
+/**
+ * Implementation of ike_sa_t.set_rekeying_transaction.
+ */
+static void set_rekeying_transaction(private_ike_sa_t *this, rekey_ike_sa_t *rekey)
+{
+       this->rekeying_transaction = rekey;
+}
+
+/**
  * Implementation of ike_sa_t.adopt_children.
  */
 static void adopt_children(private_ike_sa_t *this, private_ike_sa_t *other)
@@ -1883,10 +1918,11 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
        this->public.get_child_prf = (prf_t *(*) (ike_sa_t *)) get_child_prf;
        this->public.get_prf_auth_i = (prf_t *(*) (ike_sa_t *)) get_prf_auth_i;
        this->public.get_prf_auth_r = (prf_t *(*) (ike_sa_t *)) get_prf_auth_r;
-       this->public.derive_keys = (status_t (*) (ike_sa_t *,proposal_t*,diffie_hellman_t*,chunk_t,chunk_t,bool,prf_t*)) derive_keys;
+       this->public.derive_keys = (status_t (*) (ike_sa_t *,proposal_t*,diffie_hellman_t*,chunk_t,chunk_t,bool,prf_t*,prf_t*)) derive_keys;
        this->public.add_child_sa = (void (*) (ike_sa_t*,child_sa_t*)) add_child_sa;
        this->public.has_child_sa = (bool(*)(ike_sa_t*,u_int32_t)) has_child_sa;
        this->public.get_child_sa = (child_sa_t* (*)(ike_sa_t*,protocol_id_t,u_int32_t,bool)) get_child_sa;
+       this->public.create_child_sa_iterator = (iterator_t* (*)(ike_sa_t*)) create_child_sa_iterator;
        this->public.rekey_child_sa = (status_t(*)(ike_sa_t*,protocol_id_t,u_int32_t)) rekey_child_sa;
        this->public.delete_child_sa = (status_t(*)(ike_sa_t*,protocol_id_t,u_int32_t)) delete_child_sa;
        this->public.destroy_child_sa = (status_t (*)(ike_sa_t*,protocol_id_t,u_int32_t))destroy_child_sa;
@@ -1894,6 +1930,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
        this->public.is_natt_enabled = (bool(*)(ike_sa_t*)) is_natt_enabled;
        this->public.set_lifetimes = (void(*)(ike_sa_t*,u_int32_t,u_int32_t))set_lifetimes;
        this->public.rekey = (status_t(*)(ike_sa_t*))rekey;
+       this->public.get_rekeying_transaction = (void*(*)(ike_sa_t*))get_rekeying_transaction;
+       this->public.set_rekeying_transaction = (void(*)(ike_sa_t*,void*))set_rekeying_transaction;
        this->public.adopt_children = (void(*)(ike_sa_t*,ike_sa_t*))adopt_children;
        
        /* initialize private fields */
@@ -1919,6 +1957,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
        this->transaction_in = NULL;
        this->transaction_in_next = NULL;
        this->transaction_out = NULL;
+       this->rekeying_transaction = NULL;
        this->state = IKE_CREATED;
        this->message_id_out = 0;
        /* set to NOW, as when we rekey an existing IKE_SA no message is exchanged */
index b1b9a8f..39536d5 100644 (file)
@@ -51,30 +51,32 @@ typedef enum ike_sa_state_t ike_sa_state_t;
  * SA is in the state CREATED.
  * @verbatim
                  +----------------+
-                 ¦   SA_CREATED   ¦
+                 Â¦   SA_CREATED   Â¦
                  +----------------+
-                         ¦
-    on initiate()--->    ¦   <----- on IKE_SA_INIT received 
-                         ¦
+                         Â¦
+    on initiate()--->    Â¦   <----- on IKE_SA_INIT received 
+                         V
                  +----------------+
-                 ¦ SA_CONNECTING  ¦
+                 Â¦ SA_CONNECTING  Â¦
                  +----------------+
-                         ¦
-                         ¦   <----- on IKE_AUTH successfully completed
-                         ¦
+                         Â¦
+                         Â¦   <----- on IKE_AUTH successfully completed
+                         V
                  +----------------+
-                 ¦ SA_ESTABLISHED ¦
+                 ¦ SA_ESTABLISHED ¦-------------------------+ <-- on rekeying
+                 +----------------+                         ¦
+                         ¦                                  V
+    on delete()--->      ¦   <----- on IKE_SA        +-------------+
+                         ¦          delete request   ¦ SA_REKEYING ¦
+                         ¦          received         +-------------+
+                         V                                  ¦
+                 +----------------+                         ¦
+                 ¦  SA_DELETING   ¦<------------------------+ <-- after rekeying
                  +----------------+
-                         ¦
-    on delete()--->      ¦   <----- on IKE_SA delete request received
-                         ¦
-                 +----------------+
-                 ¦  SA_DELETING   ¦
-                 +----------------+
-                         ¦
-                         ¦   <----- after delete() acknowledged
-                         ¦
-                        \¦/
+                         ¦
+                         ¦   <----- after delete() acknowledged
+                         ¦
+                        \V/
                          X
                         / \
    @endverbatim
@@ -99,6 +101,11 @@ enum ike_sa_state_t {
        IKE_ESTABLISHED,
        
        /**
+        * IKE_SA rekeying in progress
+        */
+       IKE_REKEYING,
+       
+       /**
         * IKE_SA is in progress of deletion
         */
        IKE_DELETING,
@@ -279,7 +286,7 @@ struct ike_sa_t {
         * @brief Acquire connection setup for a policy.
         *
         * If an installed policy raises an acquire, the kernel calls
-        * this function to establsh the CHILD_SA (and maybe the IKE_SA).
+        * this function to establish the CHILD_SA (and maybe the IKE_SA).
         *
         * @param this                  calling object
         * @param reqid                 reqid of the CHILD_SA the policy belongs to.
@@ -418,12 +425,13 @@ struct ike_sa_t {
         * @param nonce_i               initiators nonce
         * @param nonce_r               responders nonce
         * @param initiator             TRUE if initiator, FALSE otherwise
-        * @param rekey_prf             PRF with SK_d key when rekeying, NULL otherwise
+        * @param child_prf             PRF with SK_d key when rekeying, NULL otherwise
+        * @param old_prf               general purpose PRF of old SA when rekeying
         */
        status_t (*derive_keys)(ike_sa_t *this, proposal_t* proposal,
                                                        diffie_hellman_t *dh,
                                                        chunk_t nonce_i, chunk_t nonce_r,
-                                                       bool initiator, prf_t *rekey_prf);
+                                                       bool initiator, prf_t *child_prf, prf_t *old_prf);
        
        /**
         * @brief Get the multi purpose prf.
@@ -487,6 +495,14 @@ struct ike_sa_t {
                                                                 u_int32_t spi, bool inbound);
        
        /**
+        * @brief Create an iterator over all CHILD_SAs.
+        * 
+        * @param this                  calling object
+        * @return                              iterator
+        */
+       iterator_t* (*create_child_sa_iterator) (ike_sa_t *this);
+       
+       /**
         * @brief Rekey the CHILD SA with the specified reqid.
         *
         * Looks for a CHILD SA owned by this IKE_SA, and start the rekeing.
@@ -556,6 +572,24 @@ struct ike_sa_t {
        status_t (*rekey) (ike_sa_t *this);
 
        /**
+        * @brief Get the transaction which rekeys this IKE_SA.
+        * 
+        * @todo Fix include for rekey_ike_sa.h
+        *
+        * @param this                  calling object
+        * @return                              rekey_ike_sa_t transaction or NULL
+        */
+       void* (*get_rekeying_transaction) (ike_sa_t *this);
+
+       /**
+        * @brief Set the transaction which rekeys this IKE_SA.
+        *
+        * @param this                  calling object
+        * @param rekey                 rekey_ike_sa_t transaction or NULL
+        */
+       void (*set_rekeying_transaction) (ike_sa_t *this, void *rekey);
+
+       /**
         * @brief Move all children from other IKE_SA to this IKE_SA.
         *
         * After rekeying completes, all children are switched over to the
index 7e34be0..aa1d8b3 100644 (file)
@@ -406,7 +406,7 @@ static status_t process_notifys(private_create_child_sa_t *this, notify_payload_
                        if (notify_type < 16383)
                        {
                                this->logger->log(this->logger, AUDIT, 
-                                                                 "received %s notify error (%d), deleting IKE_SA",
+                                                                 "received %s notify error (%d), CHILD_SA creation failed",
                                                                  mapping_find(notify_type_m, notify_type),
                                                                  notify_type);
                                return FAILED;  
@@ -540,6 +540,17 @@ static status_t get_response(private_create_child_sa_t *this, message_t *request
                return FAILED;
        }
        
+       /* we do not allow the creation of new CHILDren/rekeying when IKE_SA is
+        * rekeying */
+       if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING ||
+               this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
+       {
+               build_notify(NO_ADDITIONAL_SAS, CHUNK_INITIALIZER, response, TRUE);
+               this->logger->log(this->logger, AUDIT, 
+                                                 "unable to create new CHILD_SAs, as rekeying in progress");
+               return FAILED;
+       }
+       
        /* Iterate over all payloads. */
        payloads = request->get_payload_iterator(request);
        while (payloads->has_next(payloads))
index f839962..b577e42 100644 (file)
@@ -256,6 +256,17 @@ static status_t get_response(private_delete_child_sa_t *this, message_t *request
                return FAILED;
        }
        
+       /* we can't handle a delete for a CHILD when we are rekeying. There
+        * is no proper solution for this. We send a empty informational response,
+        * as described in ikev2-clarifications draft */
+       if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING ||
+               this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
+       {
+               this->logger->log(this->logger, AUDIT, 
+                                                 "unable to delete CHILD_SA, as rekeying in progress");
+               return FAILED;
+       }
+       
        /* iterate over all payloads */
        payloads = request->get_payload_iterator(request);      
        while (payloads->has_next(payloads))
index b86420c..6dda7eb 100644 (file)
@@ -775,7 +775,7 @@ static status_t get_response(private_ike_sa_init_t *this,
        if (this->ike_sa->derive_keys(this->ike_sa, this->proposal, 
                                                                  this->diffie_hellman, 
                                                                  this->nonce_i, this->nonce_r,
-                                                                 FALSE, NULL) != SUCCESS)
+                                                                 FALSE, NULL, NULL) != SUCCESS)
        {
                notify_payload_t *notify = notify_payload_create();
                notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN);
@@ -1019,7 +1019,7 @@ static status_t conclude(private_ike_sa_init_t *this, message_t *response,
        if (this->ike_sa->derive_keys(this->ike_sa, this->proposal, 
                                                                  this->diffie_hellman, 
                                                                  this->nonce_i, this->nonce_r,
-                                                                 TRUE, NULL) != SUCCESS)
+                                                                 TRUE, NULL, NULL) != SUCCESS)
        {
                this->logger->log(this->logger, AUDIT, 
                                                  "transform objects could not be created from selected proposal, deleting IKE_SA");
index f546540..bc75e6b 100644 (file)
@@ -190,6 +190,15 @@ static status_t get_request(private_rekey_ike_sa_t *this, message_t **result)
                return SUCCESS;
        }
        
+       if (this->ike_sa->get_state(this->ike_sa) != IKE_ESTABLISHED)
+       {
+               this->logger->log(this->logger, ERROR,
+                                                 "tried to rekey in state %s, aborted",
+                                                 mapping_find(ike_sa_state_m,
+                                                                          this->ike_sa->get_state(this->ike_sa)));
+               return FAILED;
+       }
+       
        me = this->ike_sa->get_my_host(this->ike_sa);
        other = this->ike_sa->get_other_host(this->ike_sa);
        
@@ -274,7 +283,7 @@ static status_t get_request(private_rekey_ike_sa_t *this, message_t **result)
                        this->logger->log(this->logger, AUDIT,
                                                          "DH group %s (%d) not supported, aborting",
                                                          mapping_find(diffie_hellman_group_m, dh_group), dh_group);
-                       return DESTROY_ME;
+                       return FAILED;
                }
        }
        
@@ -288,6 +297,10 @@ static status_t get_request(private_rekey_ike_sa_t *this, message_t **result)
        this->message_id = this->ike_sa->get_next_message_id(this->ike_sa);
        request->set_message_id(request, this->message_id);
        
+       /* register us as rekeying to detect multiple rekeying */
+       this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
+       this->ike_sa->set_rekeying_transaction(this->ike_sa, &this->public);
+       
        return SUCCESS;
 }
 
@@ -380,7 +393,8 @@ static status_t switchto_new_sa(private_rekey_ike_sa_t* this, bool initiator)
        if (this->new_sa->derive_keys(this->new_sa, this->proposal,
                                                                  this->diffie_hellman, 
                                                                  this->nonce_i, this->nonce_r, initiator,
-                                                                 this->ike_sa->get_child_prf(this->ike_sa)
+                                                                 this->ike_sa->get_child_prf(this->ike_sa),
+                                                                 this->ike_sa->get_prf(this->ike_sa)
                                                                 ) != SUCCESS)
        {
                return FAILED;
@@ -388,14 +402,9 @@ static status_t switchto_new_sa(private_rekey_ike_sa_t* this, bool initiator)
        
        this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED);
        
-       this->new_sa->adopt_children(this->new_sa, this->ike_sa);
-       
        this->new_sa->set_lifetimes(this->new_sa,
                                                this->connection->get_soft_lifetime(this->connection),
                                                this->connection->get_hard_lifetime(this->connection));
-       
-       charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
-       this->new_sa = NULL;
        return SUCCESS;
 }
 
@@ -433,7 +442,8 @@ static status_t get_response(private_rekey_ike_sa_t *this, message_t *request,
        host_t *me, *other;
        message_t *response;
        status_t status;
-       iterator_t *payloads;
+       iterator_t *payloads, *iterator;
+       child_sa_t *child_sa;
        sa_payload_t *sa_request = NULL;
        nonce_payload_t *nonce_request = NULL;
        ke_payload_t *ke_request = NULL;
@@ -469,6 +479,33 @@ static status_t get_response(private_rekey_ike_sa_t *this, message_t *request,
                return FAILED;
        }
        
+       /* if we already initiate a delete, we do not allow rekeying */
+       if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
+       {
+               build_notify(NO_PROPOSAL_CHOSEN, CHUNK_INITIALIZER, response, TRUE);
+               this->logger->log(this->logger, CONTROL,
+                                                 "unable to rekey, as delete in progress. Sending NO_PROPOSAL_CHOSEN");
+               return FAILED;
+       }
+       
+       /* if we have a CHILD which is "half-open", we do not allow rekeying */
+       iterator = this->ike_sa->create_child_sa_iterator(this->ike_sa);
+       while (iterator->iterate(iterator, (void**)&child_sa))
+       {
+               child_sa_state_t state = child_sa->get_state(child_sa);
+               if (state == CHILD_CREATED ||
+                       state == CHILD_REKEYING ||
+                       state == CHILD_DELETING)
+               {
+                       build_notify(NO_PROPOSAL_CHOSEN, CHUNK_INITIALIZER, response, TRUE);
+                       this->logger->log(this->logger, CONTROL,
+                                                         "unable to rekey, one CHILD_SA is half open. Sending NO_PROPOSAL_CHOSEN");
+                       iterator->destroy(iterator);
+                       return FAILED;
+               }
+       }
+       iterator->destroy(iterator);
+       
        /* apply for notify processing */
        this->next = next;
        
@@ -622,7 +659,49 @@ static status_t get_response(private_rekey_ike_sa_t *this, message_t *request,
                response->add_payload(response, (payload_t*)ke_response);
        }
        
-       return switchto_new_sa(this, FALSE);
+       status = switchto_new_sa(this, FALSE);
+       if (status != SUCCESS)
+       {
+               return status;
+       }
+       
+       /* IKE_SA successfully created. If another transaction is already rekeying
+        * this SA, our lower nonce must be registered for a later nonce compare. */
+       {
+               private_rekey_ike_sa_t *other;
+               
+               other = this->ike_sa->get_rekeying_transaction(this->ike_sa);
+               if (other)
+               {
+                       /* store our lower nonce in the simultaneus transaction, we 
+                        * will later compare it against his nonces when we calls conclude().
+                        * We do not adopt childrens yet, as we don't know if we'll win
+                        * the race...
+                        */
+                       if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr,
+                               min(this->nonce_i.len, this->nonce_r.len)) < 0)
+                       {
+                               other->nonce_s = chunk_clone(this->nonce_i);
+                       }
+                       else
+                       {
+                               other->nonce_s = chunk_clone(this->nonce_r);
+                       }
+                       /* overwrite "other" in IKE_SA, allows "other" to access "this" */
+                       this->ike_sa->set_rekeying_transaction(this->ike_sa, &this->public);
+               }
+               else
+               {
+                       /* if we have no simultaneus transaction, we can safely adopt 
+                        * all children and complete. */
+                       this->new_sa->adopt_children(this->new_sa, this->ike_sa);
+                       charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
+                       this->new_sa = NULL;
+               }
+               this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
+       }
+       
+       return SUCCESS;
 }
 
 /**
@@ -636,6 +715,7 @@ static status_t conclude(private_rekey_ike_sa_t *this, message_t *response,
        sa_payload_t *sa_payload = NULL;
        nonce_payload_t *nonce_payload = NULL;
        ke_payload_t *ke_payload = NULL;
+       private_rekey_ike_sa_t *other_trans;
        status_t status;
        
        /* check message type */
@@ -726,18 +806,72 @@ static status_t conclude(private_rekey_ike_sa_t *this, message_t *response,
                                                        ke_payload->get_key_exchange_data(ke_payload));
        }
        
-       if (switchto_new_sa(this, TRUE) == SUCCESS)
-       {
-               /* new IKE_SA is in use now, delete old */
-               *next = (transaction_t*)delete_ike_sa_create(this->ike_sa);
-               return SUCCESS;
-       }
-       else
+       if (switchto_new_sa(this, TRUE) != SUCCESS)
        {
                /* this should not happen. But if, we destroy both SAs */
                *next = (transaction_t*)delete_ike_sa_create(this->new_sa);
                return DESTROY_ME;
        }
+       
+       /* IKE_SA successfully created. If the other peer initiated rekeying
+        * in the meantime, we detect this by comparing the rekeying_transaction
+        * of the SA. If it changed, we are not alone. Then we must compare the nonces.
+        * If no simultaneous rekeying is going on, we just initiate the delete of
+        * the superseded SA. */
+       other_trans = this->ike_sa->get_rekeying_transaction(this->ike_sa);
+       this->ike_sa->set_rekeying_transaction(this->ike_sa, NULL);
+       
+       if (this->nonce_s.ptr)
+       {       /* simlutaneous rekeying is going on, not so good */
+               chunk_t this_lowest;
+               
+               /* first get our lowest nonce */
+               if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr,
+                       min(this->nonce_i.len, this->nonce_r.len)) < 0)
+               {
+                       this_lowest = this->nonce_i;
+               }
+               else
+               {
+                       this_lowest = this->nonce_r;
+               }
+               /* then compare against other lowest nonce */
+               if (memcmp(this_lowest.ptr, this->nonce_s.ptr,
+                       min(this_lowest.len, this->nonce_s.len)) < 0)
+               {
+                       this->logger->log(this->logger, ERROR,
+                                                               "detected simultaneous IKE_SA rekeying, deleting ours");
+                       this->lost = TRUE;
+               }
+               else
+               {
+                       this->logger->log(this->logger, ERROR,
+                                                               "detected simultaneous IKE_SA rekeying, but ours is preferred");
+               }
+               if (this->lost)
+               {
+                       /* the other has won, he gets our children */
+                       other_trans->new_sa->adopt_children(other_trans->new_sa, this->ike_sa);
+                       /* we have lost simlutaneous rekeying, delete the SA we just have created */
+                       this->new_sa->delete(this->new_sa);
+               }
+               /* other trans' SA is still not checked in, so do it now. It's SA will get
+                * deleted by remote peer. */
+               charon->ike_sa_manager->checkin(charon->ike_sa_manager, other_trans->new_sa);
+               other_trans->new_sa = NULL;
+       }
+       
+       if (!this->lost)
+       {
+               /* we have won. delete old IKE_SA, and migrate all children */
+               *next = (transaction_t*)delete_ike_sa_create(this->ike_sa);
+               this->new_sa->adopt_children(this->new_sa, this->ike_sa);
+       }
+       
+       charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
+       this->new_sa = NULL;
+       
+       return SUCCESS;
 }
 
 /**
index b42b2c8..adc8ce2 100644 (file)
@@ -70,7 +70,7 @@ transaction_t *transaction_create(ike_sa_t *ike_sa, message_t *request)
                }
                case CREATE_CHILD_SA:
                {
-                       if (ike_sa->get_state(ike_sa) != IKE_ESTABLISHED)
+                       if (ike_sa->get_state(ike_sa) < IKE_ESTABLISHED)
                        {
                                break;
                        }
@@ -114,7 +114,7 @@ transaction_t *transaction_create(ike_sa_t *ike_sa, message_t *request)
                }
                case INFORMATIONAL:
                {
-                       if (ike_sa->get_state(ike_sa) == IKE_CREATED)
+                       if (ike_sa->get_state(ike_sa) < IKE_ESTABLISHED)
                        {
                                break;
                        }