--- embedaddon/strongswan/src/libcharon/sa/ike_sa.c	2020/06/03 09:46:45	1.1.1.1
+++ embedaddon/strongswan/src/libcharon/sa/ike_sa.c	2021/03/17 00:20:09	1.1.1.2
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2019 Tobias Brunner
+ * Copyright (C) 2006-2020 Tobias Brunner
  * Copyright (C) 2006 Daniel Roethlisberger
  * Copyright (C) 2005-2009 Martin Willi
  * Copyright (C) 2005 Jan Hutter
@@ -237,6 +237,12 @@ struct private_ike_sa_t {
 	uint32_t keepalive_interval;
 
 	/**
+	 * Time the NAT keep alive interval may be exceeded before triggering a DPD
+	 * instead of a NAT keep alive
+	 */
+	uint32_t keepalive_dpd_margin;
+
+	/**
 	 * The scheduled keep alive job, if any
 	 */
 	send_keepalive_job_t *keepalive_job;
@@ -655,8 +661,20 @@ METHOD(ike_sa_t, send_keepalive, void,
 
 	diff = now - last_out;
 
-	if (diff >= this->keepalive_interval)
+	if (this->keepalive_dpd_margin &&
+		diff > (this->keepalive_interval + this->keepalive_dpd_margin))
 	{
+		if (!this->task_manager->busy(this->task_manager))
+		{
+			DBG1(DBG_IKE, "sending DPD instead of keep alive %ds after last "
+				 "outbound message", diff);
+			this->task_manager->queue_dpd(this->task_manager);
+			this->task_manager->initiate(this->task_manager);
+		}
+		diff = 0;
+	}
+	else if (diff >= this->keepalive_interval)
+	{
 		packet_t *packet;
 		chunk_t data;
 
@@ -669,6 +687,7 @@ METHOD(ike_sa_t, send_keepalive, void,
 		packet->set_data(packet, data);
 		DBG1(DBG_IKE, "sending keep alive to %#H", this->other_host);
 		charon->sender->send_no_marker(charon->sender, packet);
+		this->stats[STAT_OUTBOUND] = now;
 		diff = 0;
 	}
 	if (!this->keepalive_job)
@@ -934,6 +953,17 @@ METHOD(ike_sa_t, reset, void,
 	{
 		charon->ike_sa_manager->new_initiator_spi(charon->ike_sa_manager,
 												  &this->public);
+
+		/* when starting from scratch, connect to the original peer again e.g.
+		 * if we got redirected but weren't able to connect successfully */
+		if (this->redirected_from)
+		{
+			this->redirected_from->destroy(this->redirected_from);
+			this->redirected_from = NULL;
+			/* we can't restore the original value, if there was any */
+			DESTROY_IF(this->remote_host);
+			this->remote_host = NULL;
+		}
 	}
 	/* the responder ID is reset, as peer may choose another one */
 	if (this->ike_sa_id->is_initiator(this->ike_sa_id))
@@ -1084,9 +1114,10 @@ METHOD(ike_sa_t, float_ports, void,
 }
 
 METHOD(ike_sa_t, update_hosts, void,
-	private_ike_sa_t *this, host_t *me, host_t *other, bool force)
+	private_ike_sa_t *this, host_t *me, host_t *other, update_hosts_flag_t flags)
 {
-	bool update = FALSE;
+	host_t *new_me = NULL, *new_other = NULL;
+	bool silent = FALSE;
 
 	if (me == NULL)
 	{
@@ -1101,43 +1132,53 @@ METHOD(ike_sa_t, update_hosts, void,
 	if (this->my_host->is_anyaddr(this->my_host) ||
 		this->other_host->is_anyaddr(this->other_host))
 	{
-		set_my_host(this, me->clone(me));
-		set_other_host(this, other->clone(other));
-		update = TRUE;
+		new_me = me;
+		new_other = other;
+		silent = TRUE;
 	}
 	else
 	{
-		/* update our address in any case */
-		if (force && !me->equals(me, this->my_host))
+		/* update our address only if forced */
+		if ((flags & UPDATE_HOSTS_FORCE_LOCAL) && !me->equals(me, this->my_host))
 		{
-			charon->bus->ike_update(charon->bus, &this->public, TRUE, me);
-			set_my_host(this, me->clone(me));
-			update = TRUE;
+			new_me = me;
 		}
 
 		if (!other->equals(other, this->other_host) &&
-			(force || has_condition(this, COND_NAT_THERE)))
+			((flags & UPDATE_HOSTS_FORCE_REMOTE) || has_condition(this, COND_NAT_THERE)))
 		{
 			/* only update other's address if we are behind a static NAT,
 			 * which we assume is the case if we are not initiator */
-			if (force ||
+			if ((flags & UPDATE_HOSTS_FORCE_REMOTE) ||
 				(!has_condition(this, COND_NAT_HERE) ||
 				 !has_condition(this, COND_ORIGINAL_INITIATOR)))
 			{
-				charon->bus->ike_update(charon->bus, &this->public, FALSE, other);
-				set_other_host(this, other->clone(other));
-				update = TRUE;
+				new_other = other;
 			}
 		}
 	}
 
-	/* update all associated CHILD_SAs, if required */
-	if (update)
+	if (new_me || new_other || (flags & UPDATE_HOSTS_FORCE_CHILDREN))
 	{
 		enumerator_t *enumerator;
 		child_sa_t *child_sa;
 		linked_list_t *vips;
 
+		if ((new_me || new_other) && !silent)
+		{
+			charon->bus->ike_update(charon->bus, &this->public,
+									new_me ?: this->my_host,
+									new_other ?: this->other_host);
+		}
+		if (new_me)
+		{
+			set_my_host(this, new_me->clone(new_me));
+		}
+		if (new_other)
+		{
+			set_other_host(this, new_other->clone(new_other));
+		}
+
 		vips = linked_list_create_from_enumerator(
 									array_create_enumerator(this->my_vips));
 
@@ -2145,8 +2186,8 @@ METHOD(ike_sa_t, reestablish, status_t,
 		return FAILED;
 	}
 
-	new = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
-											   this->version, TRUE);
+	new = charon->ike_sa_manager->create_new(charon->ike_sa_manager,
+											 this->version, TRUE);
 	if (!new)
 	{
 		return FAILED;
@@ -2229,8 +2270,8 @@ static bool redirect_established(private_ike_sa_t *thi
 	host_t *other;
 	time_t redirect;
 
-	new = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
-											   this->version, TRUE);
+	new = charon->ike_sa_manager->create_new(charon->ike_sa_manager,
+											 this->version, TRUE);
 	if (!new)
 	{
 		return FALSE;
@@ -2287,9 +2328,9 @@ static bool redirect_connecting(private_ike_sa_t *this
 	reset(this, TRUE);
 	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 */
+	DESTROY_IF(this->remote_host);
 	this->remote_host = other;
 	resolve_hosts(this);
 	return TRUE;
@@ -2396,81 +2437,86 @@ METHOD(ike_sa_t, retransmit, status_t,
 	{
 		return INVALID_STATE;
 	}
-	this->stats[STAT_OUTBOUND] = time_monotonic(NULL);
-	if (this->task_manager->retransmit(this->task_manager, message_id) != SUCCESS)
+	switch (this->task_manager->retransmit(this->task_manager, message_id))
 	{
-		/* send a proper signal to brief interested bus listeners */
-		switch (this->state)
+		case SUCCESS:
+			this->stats[STAT_OUTBOUND] = time_monotonic(NULL);
+			return SUCCESS;
+		case INVALID_STATE:
+			return INVALID_STATE;
+		default:
+			break;
+	}
+	/* send a proper signal to brief interested bus listeners */
+	switch (this->state)
+	{
+		case IKE_CONNECTING:
 		{
-			case IKE_CONNECTING:
+			/* retry IKE_SA_INIT/Main Mode if we have multiple keyingtries */
+			uint32_t tries = this->peer_cfg->get_keyingtries(this->peer_cfg);
+			charon->bus->alert(charon->bus, ALERT_PEER_INIT_UNREACHABLE,
+							   this->keyingtry);
+			this->keyingtry++;
+			if (tries == 0 || tries > this->keyingtry)
 			{
-				/* retry IKE_SA_INIT/Main Mode if we have multiple keyingtries */
-				uint32_t tries = this->peer_cfg->get_keyingtries(this->peer_cfg);
-				charon->bus->alert(charon->bus, ALERT_PEER_INIT_UNREACHABLE,
-								   this->keyingtry);
-				this->keyingtry++;
-				if (tries == 0 || tries > this->keyingtry)
-				{
-					DBG1(DBG_IKE, "peer not responding, trying again (%d/%d)",
-						 this->keyingtry + 1, tries);
-					reset(this, TRUE);
-					resolve_hosts(this);
-					return this->task_manager->initiate(this->task_manager);
-				}
-				DBG1(DBG_IKE, "establishing IKE_SA failed, peer not responding");
+				DBG1(DBG_IKE, "peer not responding, trying again (%d/%d)",
+					 this->keyingtry + 1, tries);
+				reset(this, TRUE);
+				resolve_hosts(this);
+				return this->task_manager->initiate(this->task_manager);
+			}
+			DBG1(DBG_IKE, "establishing IKE_SA failed, peer not responding");
 
-				if (this->version == IKEV1 && array_count(this->child_sas))
-				{
-					enumerator_t *enumerator;
-					child_sa_t *child_sa;
+			if (this->version == IKEV1 && array_count(this->child_sas))
+			{
+				enumerator_t *enumerator;
+				child_sa_t *child_sa;
 
-					/* if reauthenticating an IKEv1 SA failed (assumed for an SA
-					 * in this state with CHILD_SAs), try again from scratch */
-					DBG1(DBG_IKE, "reauthentication failed, trying to "
-						 "reestablish IKE_SA");
-					reestablish(this);
-					/* trigger down events for the CHILD_SAs, as no down event
-					 * is triggered below for IKE SAs in this state */
-					enumerator = array_create_enumerator(this->child_sas);
-					while (enumerator->enumerate(enumerator, &child_sa))
+				/* if reauthenticating an IKEv1 SA failed (assumed for an SA
+				 * in this state with CHILD_SAs), try again from scratch */
+				DBG1(DBG_IKE, "reauthentication failed, trying to "
+					 "reestablish IKE_SA");
+				reestablish(this);
+				/* trigger down events for the CHILD_SAs, as no down event
+				 * is triggered below for IKE SAs in this state */
+				enumerator = array_create_enumerator(this->child_sas);
+				while (enumerator->enumerate(enumerator, &child_sa))
+				{
+					if (child_sa->get_state(child_sa) != CHILD_REKEYED &&
+						child_sa->get_state(child_sa) != CHILD_DELETED)
 					{
-						if (child_sa->get_state(child_sa) != CHILD_REKEYED &&
-							child_sa->get_state(child_sa) != CHILD_DELETED)
-						{
-							charon->bus->child_updown(charon->bus, child_sa,
-													  FALSE);
-						}
+						charon->bus->child_updown(charon->bus, child_sa,
+												  FALSE);
 					}
-					enumerator->destroy(enumerator);
 				}
-				break;
+				enumerator->destroy(enumerator);
 			}
-			case IKE_DELETING:
-				DBG1(DBG_IKE, "proper IKE_SA delete failed, peer not responding");
-				if (has_condition(this, COND_REAUTHENTICATING) &&
-					!lib->settings->get_bool(lib->settings,
-										"%s.make_before_break", FALSE, lib->ns))
-				{
-					DBG1(DBG_IKE, "delete during reauthentication failed, "
-						 "trying to reestablish IKE_SA anyway");
-					reestablish(this);
-				}
-				break;
-			case IKE_REKEYING:
-				DBG1(DBG_IKE, "rekeying IKE_SA failed, peer not responding");
-				/* FALL */
-			default:
-				reestablish(this);
-				break;
+			break;
 		}
-		if (this->state != IKE_CONNECTING &&
-			this->state != IKE_REKEYED)
-		{
-			charon->bus->ike_updown(charon->bus, &this->public, FALSE);
-		}
-		return DESTROY_ME;
+		case IKE_DELETING:
+			DBG1(DBG_IKE, "proper IKE_SA delete failed, peer not responding");
+			if (has_condition(this, COND_REAUTHENTICATING) &&
+				!lib->settings->get_bool(lib->settings,
+									"%s.make_before_break", FALSE, lib->ns))
+			{
+				DBG1(DBG_IKE, "delete during reauthentication failed, "
+					 "trying to reestablish IKE_SA anyway");
+				reestablish(this);
+			}
+			break;
+		case IKE_REKEYING:
+			DBG1(DBG_IKE, "rekeying IKE_SA failed, peer not responding");
+			/* FALL */
+		default:
+			reestablish(this);
+			break;
 	}
-	return SUCCESS;
+	if (this->state != IKE_CONNECTING &&
+		this->state != IKE_REKEYED)
+	{
+		charon->bus->ike_updown(charon->bus, &this->public, FALSE);
+	}
+	return DESTROY_ME;
 }
 
 METHOD(ike_sa_t, set_auth_lifetime, status_t,
@@ -2678,6 +2724,14 @@ METHOD(ike_sa_t, roam, status_t,
 			this->task_manager->queue_mobike(this->task_manager, FALSE, TRUE);
 			return this->task_manager->initiate(this->task_manager);
 		}
+		if (lib->settings->get_bool(lib->settings,
+								"%s.check_current_path", FALSE, lib->ns) &&
+			!this->task_manager->busy(this->task_manager))
+		{
+			DBG1(DBG_IKE, "checking if current path still works using DPD");
+			this->task_manager->queue_dpd(this->task_manager);
+			return this->task_manager->initiate(this->task_manager);
+		}
 		return SUCCESS;
 	}
 
@@ -2928,9 +2982,9 @@ METHOD(ike_sa_t, inherit_post, void,
 		time_t reauth, delete, now = time_monotonic(NULL);
 
 		this->stats[STAT_REAUTH] = other->stats[STAT_REAUTH];
-		reauth = this->stats[STAT_REAUTH] - now;
+		reauth = max(0, this->stats[STAT_REAUTH] - now);
 		delete = reauth + this->peer_cfg->get_over_time(this->peer_cfg);
-		this->stats[STAT_DELETE] = this->stats[STAT_REAUTH] + delete;
+		this->stats[STAT_DELETE] = now + delete;
 		DBG1(DBG_IKE, "rescheduling reauthentication in %ds after rekeying, "
 			 "lifetime reduced to %ds", reauth, delete);
 		lib->scheduler->schedule_job(lib->scheduler,
@@ -3171,6 +3225,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool 
 		.unique_id = ref_get(&unique_id),
 		.keepalive_interval = lib->settings->get_time(lib->settings,
 								"%s.keep_alive", KEEPALIVE_INTERVAL, lib->ns),
+		.keepalive_dpd_margin = lib->settings->get_time(lib->settings,
+								"%s.keep_alive_dpd_margin", 0, lib->ns),
 		.retry_initiate_interval = lib->settings->get_time(lib->settings,
 								"%s.retry_initiate_interval", 0, lib->ns),
 		.flush_auth_cfg = lib->settings->get_bool(lib->settings,