Send INITIAL_CONTACT for the first IKE_SA if it has a unique policy
authorMartin Willi <martin@revosec.ch>
Wed, 5 Jan 2011 14:58:38 +0000 (15:58 +0100)
committerMartin Willi <martin@revosec.ch>
Wed, 5 Jan 2011 15:46:08 +0000 (16:46 +0100)
src/libcharon/sa/ike_sa_manager.c
src/libcharon/sa/ike_sa_manager.h
src/libcharon/sa/tasks/ike_auth.c

index c8fbbde..ec2d82d 100644 (file)
@@ -1320,9 +1320,8 @@ METHOD(ike_sa_manager_t, checkin, void,
                segment = put_entry(this, entry);
        }
 
-       /* apply identities for duplicate test (only as responder) */
-       if (!entry->ike_sa_id->is_initiator(entry->ike_sa_id) &&
-               ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
+       /* apply identities for duplicate test */
+       if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
                entry->my_id == NULL && entry->other_id == NULL)
        {
                entry->my_id = my_id->clone(my_id);
@@ -1377,8 +1376,7 @@ METHOD(ike_sa_manager_t, checkin_and_destroy, void,
                {
                        remove_half_open(this, entry);
                }
-               if (!entry->ike_sa_id->is_initiator(entry->ike_sa_id) &&
-                       entry->my_id && entry->other_id)
+               if (entry->my_id && entry->other_id)
                {
                        remove_connected_peers(this, entry);
                }
@@ -1502,6 +1500,34 @@ METHOD(ike_sa_manager_t, check_uniqueness, bool,
        return cancel;
 }
 
+METHOD(ike_sa_manager_t, has_contact, bool,
+       private_ike_sa_manager_t *this, identification_t *me,
+       identification_t *other, int family)
+{
+       linked_list_t *list;
+       u_int row, segment;
+       rwlock_t *lock;
+       bool found = FALSE;
+
+       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);
+       list = this->connected_peers_table[row];
+       if (list)
+       {
+               if (list->find_first(list, (linked_list_match_t)connected_peers_match,
+                                                        NULL, me, other, family) == SUCCESS)
+               {
+                       found = TRUE;
+               }
+       }
+       lock->unlock(lock);
+
+       return found;
+}
+
 METHOD(ike_sa_manager_t, get_half_open_count, int,
        private_ike_sa_manager_t *this, host_t *ip)
 {
@@ -1608,8 +1634,7 @@ METHOD(ike_sa_manager_t, flush, void,
                {
                        remove_half_open(this, entry);
                }
-               if (!entry->ike_sa_id->is_initiator(entry->ike_sa_id) &&
-                       entry->my_id && entry->other_id)
+               if (entry->my_id && entry->other_id)
                {
                        remove_connected_peers(this, entry);
                }
@@ -1686,6 +1711,7 @@ ike_sa_manager_t *ike_sa_manager_create()
                        .checkout_by_id = _checkout_by_id,
                        .checkout_by_name = _checkout_by_name,
                        .check_uniqueness = _check_uniqueness,
+                       .has_contact = _has_contact,
                        .create_enumerator = _create_enumerator,
                        .checkin = _checkin,
                        .checkin_and_destroy = _checkin_and_destroy,
index 115e8d3..a9dbca6 100644 (file)
@@ -112,6 +112,17 @@ struct ike_sa_manager_t {
        bool (*check_uniqueness)(ike_sa_manager_t *this, ike_sa_t *ike_sa);
 
        /**
+        * Check if we already have a connected IKE_SA between two identities.
+        *
+        * @param me                            own identity
+        * @param other                         remote identity
+        * @param family                        address family to include in uniqueness check
+        * @return                                      TRUE if we have a connected IKE_SA
+        */
+       bool (*has_contact)(ike_sa_manager_t *this, identification_t *me,
+                                               identification_t *other, int family);
+
+       /**
         * Check out an IKE_SA a unique ID.
         *
         * Every IKE_SA and every CHILD_SA is uniquely identified by an ID.
index 48174e2..6b024cc 100644 (file)
@@ -390,7 +390,7 @@ static status_t build_i(private_ike_auth_t *this, message_t *message)
        /* check if an authenticator is in progress */
        if (this->my_auth == NULL)
        {
-               identification_t *id;
+               identification_t *idi, *idr = NULL;
                id_payload_t *id_payload;
 
                /* clean up authentication config from a previous round */
@@ -401,29 +401,42 @@ static status_t build_i(private_ike_auth_t *this, message_t *message)
                cfg = get_auth_cfg(this, FALSE);
                if (cfg)
                {
-                       id = cfg->get(cfg, AUTH_RULE_IDENTITY);
-                       if (id && !id->contains_wildcards(id))
+                       idr = cfg->get(cfg, AUTH_RULE_IDENTITY);
+                       if (idr && !idr->contains_wildcards(idr))
                        {
-                               this->ike_sa->set_other_id(this->ike_sa, id->clone(id));
+                               this->ike_sa->set_other_id(this->ike_sa, idr->clone(idr));
                                id_payload = id_payload_create_from_identification(
-                                                                                                                       ID_RESPONDER, id);
+                                                                                                                       ID_RESPONDER, idr);
                                message->add_payload(message, (payload_t*)id_payload);
                        }
                }
                /* add IDi */
                cfg = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
                cfg->merge(cfg, get_auth_cfg(this, TRUE), TRUE);
-               id = cfg->get(cfg, AUTH_RULE_IDENTITY);
-               if (!id)
+               idi = cfg->get(cfg, AUTH_RULE_IDENTITY);
+               if (!idi)
                {
                        DBG1(DBG_CFG, "configuration misses IDi");
                        return FAILED;
                }
-               this->ike_sa->set_my_id(this->ike_sa, id->clone(id));
-               id_payload = id_payload_create_from_identification(ID_INITIATOR, id);
+               this->ike_sa->set_my_id(this->ike_sa, idi->clone(idi));
+               id_payload = id_payload_create_from_identification(ID_INITIATOR, idi);
                get_reserved_id_bytes(this, id_payload);
                message->add_payload(message, (payload_t*)id_payload);
 
+               if (idr && message->get_message_id(message) == 1 &&
+                       this->peer_cfg->get_unique_policy(this->peer_cfg) != UNIQUE_NO)
+               {
+                       host_t *host;
+
+                       host = this->ike_sa->get_other_host(this->ike_sa);
+                       if (!charon->ike_sa_manager->has_contact(charon->ike_sa_manager,
+                                                                                       idi, idr, host->get_family(host)))
+                       {
+                               message->add_notify(message, FALSE, INITIAL_CONTACT, chunk_empty);
+                       }
+               }
+
                /* build authentication data */
                this->my_auth = authenticator_create_builder(this->ike_sa, cfg,
                                                        this->other_nonce, this->my_nonce,