#include <processing/jobs/rekey_ike_sa_job.h>
#include <processing/jobs/retry_initiate_job.h>
#include <sa/ikev2/tasks/ike_auth_lifetime.h>
+#include <sa/ikev2/tasks/ike_reauth_complete.h>
#ifdef ME
#include <sa/ikev2/tasks/ike_me.h>
return found;
}
+/**
+ * Reestablish CHILD_SAs and migrate queued tasks.
+ *
+ * If force is true all SAs are restarted, otherwise their close/dpd_action
+ * is followed.
+ */
+static status_t reestablish_children(private_ike_sa_t *this, ike_sa_t *new,
+ bool force)
+{
+ enumerator_t *enumerator;
+ child_sa_t *child_sa;
+ child_cfg_t *child_cfg;
+ action_t action;
+ status_t status = FAILED;
+
+ /* handle existing CHILD_SAs */
+ enumerator = create_child_sa_enumerator(this);
+ while (enumerator->enumerate(enumerator, (void**)&child_sa))
+ {
+ if (force)
+ {
+ switch (child_sa->get_state(child_sa))
+ {
+ case CHILD_ROUTED:
+ { /* move routed child directly */
+ remove_child_sa(this, enumerator);
+ new->add_child_sa(new, child_sa);
+ action = ACTION_NONE;
+ break;
+ }
+ default:
+ { /* initiate/queue all other CHILD_SAs */
+ action = ACTION_RESTART;
+ break;
+ }
+ }
+ }
+ else
+ { /* only restart CHILD_SAs that are configured accordingly */
+ if (this->state == IKE_DELETING)
+ {
+ action = child_sa->get_close_action(child_sa);
+ }
+ else
+ {
+ action = child_sa->get_dpd_action(child_sa);
+ }
+ }
+ switch (action)
+ {
+ case ACTION_RESTART:
+ child_cfg = child_sa->get_config(child_sa);
+ DBG1(DBG_IKE, "restarting CHILD_SA %s",
+ child_cfg->get_name(child_cfg));
+ child_cfg->get_ref(child_cfg);
+ status = new->initiate(new, child_cfg,
+ child_sa->get_reqid(child_sa), NULL, NULL);
+ break;
+ default:
+ continue;
+ }
+ if (status == DESTROY_ME)
+ {
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ /* adopt any active or queued CHILD-creating tasks */
+ if (status != DESTROY_ME)
+ {
+ task_manager_t *other_tasks = ((private_ike_sa_t*)new)->task_manager;
+ other_tasks->adopt_child_tasks(other_tasks, this->task_manager);
+ if (new->get_state(new) == IKE_CREATED)
+ {
+ status = new->initiate(new, NULL, 0, NULL, NULL);
+ }
+ }
+ return status;
+}
+
METHOD(ike_sa_t, reestablish, status_t,
private_ike_sa_t *this)
{
action_t action;
enumerator_t *enumerator;
child_sa_t *child_sa;
- child_cfg_t *child_cfg;
bool restart = FALSE;
status_t status = FAILED;
else
#endif /* ME */
{
- /* handle existing CHILD_SAs */
- enumerator = create_child_sa_enumerator(this);
- while (enumerator->enumerate(enumerator, (void**)&child_sa))
- {
- if (has_condition(this, COND_REAUTHENTICATING))
- {
- switch (child_sa->get_state(child_sa))
- {
- case CHILD_ROUTED:
- { /* move routed child directly */
- remove_child_sa(this, enumerator);
- new->add_child_sa(new, child_sa);
- action = ACTION_NONE;
- break;
- }
- default:
- { /* initiate/queue all other CHILD_SAs */
- action = ACTION_RESTART;
- break;
- }
- }
- }
- else
- { /* only restart CHILD_SAs that are configured accordingly */
- if (this->state == IKE_DELETING)
- {
- action = child_sa->get_close_action(child_sa);
- }
- else
- {
- action = child_sa->get_dpd_action(child_sa);
- }
- }
- switch (action)
- {
- case ACTION_RESTART:
- child_cfg = child_sa->get_config(child_sa);
- DBG1(DBG_IKE, "restarting CHILD_SA %s",
- child_cfg->get_name(child_cfg));
- child_cfg->get_ref(child_cfg);
- status = new->initiate(new, child_cfg,
- child_sa->get_reqid(child_sa), NULL, NULL);
- break;
- default:
- continue;
- }
- if (status == DESTROY_ME)
- {
- break;
- }
- }
- enumerator->destroy(enumerator);
- /* adopt any active or queued CHILD-creating tasks */
- if (status != DESTROY_ME)
- {
- task_manager_t *other_tasks = ((private_ike_sa_t*)new)->task_manager;
- other_tasks->adopt_child_tasks(other_tasks, this->task_manager);
- if (new->get_state(new) == IKE_CREATED)
- {
- status = new->initiate(new, NULL, 0, NULL, NULL);
- }
- }
+ status = reestablish_children(this, new,
+ has_condition(this, COND_REAUTHENTICATING));
}
if (status == DESTROY_ME)
return status;
}
-METHOD(ike_sa_t, handle_redirect, bool,
- private_ike_sa_t *this, identification_t *gateway)
+/**
+ * Resolve the given gateway ID
+ */
+static host_t *resolve_gateway_id(identification_t *gateway)
{
char gw[BUF_LEN];
- host_t *other, *from;
+ host_t *addr;
- DBG1(DBG_IKE, "redirected to %Y", gateway);
- if (!this->follow_redirects)
+ snprintf(gw, sizeof(gw), "%Y", gateway);
+ gw[sizeof(gw)-1] = '\0';
+ addr = host_create_from_dns(gw, AF_UNSPEC, IKEV2_UDP_PORT);
+ if (!addr)
+ {
+ DBG1(DBG_IKE, "unable to resolve gateway ID '%Y', redirect failed",
+ gateway);
+ }
+ return addr;
+}
+
+/**
+ * Redirect the current SA to the given target host
+ */
+static bool redirect_established(private_ike_sa_t *this, identification_t *to)
+{
+ private_ike_sa_t *new_priv;
+ ike_sa_t *new;
+ host_t *other;
+
+ new = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
+ this->version, TRUE);
+ if (!new)
{
- DBG1(DBG_IKE, "server sent REDIRECT even though we disabled it");
return FALSE;
}
+ new_priv = (private_ike_sa_t*)new;
+ new->set_peer_cfg(new, this->peer_cfg);
+ new_priv->redirected_from = this->other_host->clone(this->other_host);
+ charon->bus->ike_reestablish_pre(charon->bus, &this->public, new);
+ other = resolve_gateway_id(to);
+ if (other)
+ {
+ set_my_host(new_priv, this->my_host->clone(this->my_host));
+ /* this allows us to force the remote address while we still properly
+ * resolve the local address */
+ new_priv->remote_host = other;
+ resolve_hosts(new_priv);
+ if (reestablish_children(this, new, TRUE) != DESTROY_ME)
+ {
+#ifdef USE_IKEV2
+ new->queue_task(new, (task_t*)ike_reauth_complete_create(new,
+ this->ike_sa_id));
+#endif
+ charon->bus->ike_reestablish_post(charon->bus, &this->public, new,
+ TRUE);
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, new);
+ charon->bus->set_sa(charon->bus, &this->public);
+ return TRUE;
+ }
+ }
+ charon->bus->ike_reestablish_post(charon->bus, &this->public, new,
+ FALSE);
+ charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, new);
+ charon->bus->set_sa(charon->bus, &this->public);
+ return FALSE;
+}
- snprintf(gw, sizeof(gw), "%Y", gateway);
- gw[sizeof(gw)-1] = '\0';
- other = host_create_from_dns(gw, AF_UNSPEC, IKEV2_UDP_PORT);
+/**
+ * Redirect the current connecting SA to the given target host
+ */
+static bool redirect_connecting(private_ike_sa_t *this, identification_t *to)
+{
+ host_t *other;
+
+ other = resolve_gateway_id(to);
if (!other)
{
- DBG1(DBG_IKE, "unable to resolve gateway ID '%Y', redirect failed",
- gateway);
return FALSE;
}
- from = this->other_host->clone(this->other_host);
+ reset(this);
+ DESTROY_IF(this->redirected_from);
+ this->redirected_from = this->other_host->clone(this->other_host);
+ DESTROY_IF(this->remote_host);
+ /* this allows us to force the remote address while we still properly
+ * resolve the local address */
+ this->remote_host = other;
+ resolve_hosts(this);
+ return TRUE;
+}
+
+METHOD(ike_sa_t, handle_redirect, bool,
+ private_ike_sa_t *this, identification_t *gateway)
+{
+ DBG1(DBG_IKE, "redirected to %Y", gateway);
+ if (!this->follow_redirects)
+ {
+ DBG1(DBG_IKE, "server sent REDIRECT even though we disabled it");
+ return FALSE;
+ }
+
switch (this->state)
{
case IKE_CONNECTING:
- reset(this);
- set_other_host(this, other);
- DESTROY_IF(this->redirected_from);
- this->redirected_from = from;
- return TRUE;
+ return redirect_connecting(this, gateway);
+ case IKE_ESTABLISHED:
+ return redirect_established(this, gateway);
default:
DBG1(DBG_IKE, "unable to handle redirect for IKE_SA in state %N",
ike_sa_state_names, this->state);
- other->destroy(other);
- from->destroy(from);
return FALSE;
}
}