}
/**
+ * handle exchange collisions
+ */
+static void handle_collisions(private_task_manager_t *this, task_t *task)
+{
+ iterator_t *iterator;
+ task_t *active;
+ task_type_t type;
+
+ type = task->get_type(task);
+
+ /* check if we have initiated rekeying ourself */
+ if (this->initiating.type != CREATE_CHILD_SA ||
+ (type != IKE_REKEY && type != CHILD_REKEY && type != CHILD_DELETE))
+ {
+ task->destroy(task);
+ 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)
+ {
+ //ike_rekey_t *rekey = (ike_rekey_t*)active;
+ //rekey->collide(rekey, (ike_rekey_t*)task);
+ break;
+ }
+ continue;
+ case CHILD_REKEY:
+ /* TODO: check if it is the SAME child we are talking about! */
+ if (type == CHILD_REKEY || type == CHILD_DELETE)
+ {
+ child_rekey_t *rekey = (child_rekey_t*)active;
+ rekey->collide(rekey, task);
+ break;
+ }
+ continue;
+ default:
+ continue;
+ }
+ break;
+ }
+ iterator->destroy(iterator);
+}
+
+/**
* build a response depending on the "passive" task list
*/
static status_t build_response(private_task_manager_t *this,
case SUCCESS:
/* task completed, remove it */
iterator->remove(iterator);
- task->destroy(task);
- break;
+ handle_collisions(this, task);
case NEED_MORE:
/* processed, but task needs another exchange */
break;
* CHILD_SA which gets established
*/
child_sa_t *child_sa;
+
+ /**
+ * successfully established the CHILD?
+ */
+ bool established;
};
/**
/* add to IKE_SA, and remove from task */
this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
- this->child_sa = NULL;
+ this->established = TRUE;
return SUCCESS;
}
{
iterator_t *iterator;
payload_t *payload;
- status_t status;
switch (message->get_exchange_type(message))
{
process_payloads(this, message);
- status = select_and_install(this);
-
- SIG(CHILD_UP_SUCCESS, "established CHILD_SA successfully");
-
+ if (select_and_install(this) == SUCCESS)
+ {
+ SIG(CHILD_UP_SUCCESS, "established CHILD_SA successfully");
+ }
return SUCCESS;
}
}
/**
+ * Implementation of child_create_t.get_child
+ */
+static child_sa_t* get_child(private_child_create_t *this)
+{
+ if (this->established)
+ {
+ return this->child_sa;
+ }
+ return NULL;
+}
+
+/**
* Implementation of task_t.migrate
*/
static void migrate(private_child_create_t *this, ike_sa_t *ike_sa)
this->child_sa = NULL;
this->mode = MODE_TUNNEL;
this->reqid = 0;
+ this->established = FALSE;
}
/**
{
this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
}
- DESTROY_IF(this->child_sa);
+ if (!this->established)
+ {
+ DESTROY_IF(this->child_sa);
+ }
DESTROY_IF(this->proposal);
if (this->proposals)
{
{
private_child_create_t *this = malloc_thing(private_child_create_t);
+ this->public.get_child = (child_sa_t*(*)(child_create_t*))get_child;
this->public.use_reqid = (void(*)(child_create_t*,u_int32_t))use_reqid;
this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
this->child_sa = NULL;
this->mode = MODE_TUNNEL;
this->reqid = 0;
+ this->established = FALSE;
return &this->public;
}
child_sa_t *child_sa;
/**
- * redundandt CHILD_SA created simultaneously
+ * colliding task, may be delete or rekey
*/
- child_sa_t *simultaneous;
+ task_t *collision;
/**
* the lowest nonce compared so far
*/
chunk_t nonce;
-
- /**
- * TRUE if we have the lower nonce
- */
- bool winner;
};
/**
- * get the nonce from a message, return TRUE if it was lower than this->nonce
+ * get the nonce from a message, IF it is lower than a previous one
*/
-static bool get_nonce(private_child_rekey_t *this, message_t *message)
+static void get_nonce(private_child_rekey_t *this, message_t *message)
{
nonce_payload_t *payload;
chunk_t nonce;
payload = (nonce_payload_t*)message->get_payload(message, NONCE);
- if (payload == NULL)
- {
- return FALSE;
- }
- nonce = payload->get_nonce(payload);
+ if (payload)
+ {
+ nonce = payload->get_nonce(payload);
- if (this->nonce.ptr && memcmp(nonce.ptr, this->nonce.ptr,
- min(nonce.len, this->nonce.len)) > 0)
- {
- chunk_free(&nonce);
- return FALSE;
+ if (this->nonce.ptr && memcmp(nonce.ptr, this->nonce.ptr,
+ min(nonce.len, this->nonce.len)) > 0)
+ {
+ chunk_free(&nonce);
+ }
+ else
+ {
+ chunk_free(&this->nonce);
+ this->nonce = nonce;
+ }
}
-
- chunk_free(&this->nonce);
- this->nonce = nonce;
- return TRUE;
}
/**
if (this->child_sa == NULL ||
this->child_sa->get_state(this->child_sa) == CHILD_DELETING)
{
+ DBG1(DBG_IKE, "unable to rekey, CHILD_SA not found");
message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
return SUCCESS;
}
}
get_nonce(this, message);
-
- if (this->child_sa->get_state(this->child_sa) == CHILD_REKEYING)
- {
- /* TODO: handle simultaneous rekeying */
- }
this->child_sa->set_state(this->child_sa, CHILD_REKEYING);
{
protocol_id_t protocol;
u_int32_t spi;
+ child_sa_t *to_delete;
this->child_create->task.process(&this->child_create->task, message);
if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL)
{
- /* establishing new child failed, fallback */
- this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
- /* TODO: rescedule rekeying */
+ /* establishing new child failed, reuse old. but not when we
+ * recieved a delete in the meantime */
+ if (!(this->collision &&
+ this->collision->get_type(this->collision) == CHILD_DELETE))
+ {
+ this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
+ /* TODO: rescedule rekeying */
+ }
return SUCCESS;
}
- /* TODO: delete the redundant CHILD_SA, instead of the rekeyed */
- spi = this->child_sa->get_spi(this->child_sa, TRUE);
- protocol = this->child_sa->get_protocol(this->child_sa);
+ get_nonce(this, message);
+
+ to_delete = this->child_sa;
+
+ /* check for rekey collisions */
+ if (this->collision &&
+ this->collision->get_type(this->collision) == CHILD_REKEY)
+ {
+ private_child_rekey_t *other = (private_child_rekey_t*)this->collision;
+
+ /* if we have the lower nonce, delete rekeyed SA. If not, delete
+ * the redundant. */
+ if (memcmp(this->nonce.ptr, other->nonce.ptr,
+ min(this->nonce.len, other->nonce.len)) < 0)
+ {
+ DBG1(DBG_IKE, "CHILD_SA rekey collision won, deleting rekeyed child");
+ }
+ else
+ {
+ DBG1(DBG_IKE, "CHILD_SA rekey collision lost, deleting redundant child");
+ to_delete = this->child_create->get_child(this->child_create);
+ if (to_delete == NULL)
+ {
+ /* ooops, should not happen, fallback */
+ to_delete = this->child_sa;
+ }
+ }
+ }
+
+ 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;
}
/**
+ * Implementation of child_rekey_t.collide
+ */
+static void collide(private_child_rekey_t *this, task_t *other)
+{
+ DESTROY_IF(this->collision);
+ this->collision = other;
+}
+
+/**
* Implementation of task_t.migrate
*/
static void migrate(private_child_rekey_t *this, ike_sa_t *ike_sa)
chunk_free(&this->nonce);
this->ike_sa = ike_sa;
- this->winner = TRUE;
- this->simultaneous = NULL;
+ this->collision = NULL;
}
/**
{
this->child_create->task.destroy(&this->child_create->task);
chunk_free(&this->nonce);
+ DESTROY_IF(this->collision);
free(this);
}
private_child_rekey_t *this = malloc_thing(private_child_rekey_t);
policy_t *policy;
+ 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;
this->ike_sa = ike_sa;
this->child_sa = child_sa;
this->nonce = chunk_empty;
- this->winner = TRUE;
- this->simultaneous = NULL;
+ this->collision = NULL;
return &this->public;
}