diff -rup strongswan-5.6.0/src/libcharon/plugins/dhcp/dhcp_socket.c ../dev/dhcp/strongswan-5.6.0/src/libcharon/plugins/dhcp/dhcp_socket.c
--- strongswan-5.6.0/src/libcharon/plugins/dhcp/dhcp_socket.c	2017-08-14 02:48:41.000000000 -0400
+++ ../dev/dhcp/strongswan-5.6.0/src/libcharon/plugins/dhcp/dhcp_socket.c	2018-02-11 05:39:32.183525616 -0500
@@ -13,6 +13,28 @@
  * for more details.
  */
 
+/*
+ * Copyright (C) 2017-2018 Two Sigma Investments, LP
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 #include "dhcp_socket.h"
 
 #include <unistd.h>
@@ -105,10 +127,25 @@ struct private_dhcp_socket_t {
 	 */
 	host_t *dst;
 
+ 	/**
+	 * Redundant DHCP server address - for relay to redundant servers
+	 */
+	host_t *extra_dst;
+
+	/**
+	 * DHCP gateway forced address
+ 	 */
+ 	host_t *relay_addr;
+  
 	/**
 	 * Force configured destination address
 	 */
 	bool force_dst;
+
+	/**
+	 * Send DHCP RELEASE on IKE DELETE, or just let lease expire?
+	 */
+	bool release_on_delete;  
 };
 
 /**
@@ -183,8 +220,9 @@ typedef struct __attribute__((packed)) {
  * Prepare a DHCP message for a given transaction
  */
 static int prepare_dhcp(private_dhcp_socket_t *this,
-						dhcp_transaction_t *transaction,
-						dhcp_message_type_t type, dhcp_t *dhcp)
+			dhcp_transaction_t *transaction,
+			dhcp_message_type_t type, dhcp_t *dhcp,
+			uint32_t seconds_elapsed)
 {
 	chunk_t chunk, broadcast = chunk_from_chars(0xFF,0xFF,0xFF,0xFF);
 	identification_t *identity;
@@ -198,6 +236,8 @@ static int prepare_dhcp(private_dhcp_soc
 	dhcp->hw_type = ARPHRD_ETHER;
 	dhcp->hw_addr_len = 6;
 	dhcp->transaction_id = transaction->get_id(transaction);
+	dhcp->number_of_seconds = seconds_elapsed;
+	
 	if (chunk_equals(broadcast, this->dst->get_address(this->dst)))
 	{
 		/* Set broadcast flag to get broadcasted replies, as we actually
@@ -208,11 +248,16 @@ static int prepare_dhcp(private_dhcp_soc
 	else
 	{
 		/* act as relay agent */
-		src = charon->kernel->get_source_addr(charon->kernel, this->dst, NULL);
+		if(this->relay_addr) 
+ 			src = this->relay_addr;	
+ 		else
+ 			src = charon->kernel->get_source_addr(charon->kernel,
+							      this->dst, NULL);
+
 		if (src)
 		{
 			memcpy(&dhcp->gateway_address, src->get_address(src).ptr,
-				   sizeof(dhcp->gateway_address));
+			       sizeof(dhcp->gateway_address));
 			src->destroy(src);
 		}
 	}
@@ -252,8 +297,65 @@ static int prepare_dhcp(private_dhcp_soc
 
 	option = (dhcp_option_t*)&dhcp->options[optlen];
 	option->type = DHCP_CLIENT_ID;
-	option->len = min(chunk.len, 64);
-	memcpy(option->data, chunk.ptr, option->len);
+
+	/*
+	 * If the identity is large, it is likely a DN from a
+	 * certificate.  Within an organization, it is all too
+	 * often the case that the first 64 bytes of the encoded
+	 * DN are the same for many or even _all_ client
+	 * certificates, which sends DHCP servers to a Bad Place.
+	 *
+	 * If this seems likely, we abandon transparency on the
+	 * wire for correct operation, and hash the ID (though
+	 * in a way that preserves more bits than the MAC address
+	 * hash above).  This is also RFC4361-compliant.
+	 */
+	if (chunk.len <= 64) {
+		option->len = chunk.len;
+		memcpy(option->data, chunk.ptr, option->len);
+	} else {
+		uint8_t *datap = (uint8_t *)option->data, *duidp;
+		const uint8_t id_type = 0xff;
+		const uint32_t iaid = htonl(0xca78d065);
+		const uint16_t duid_uuid = htons((uint16_t)4);
+		uint64_t cm_result1, cm_result2;
+		uint8_t cm_key[16];
+
+		memset(cm_key, 0x00, sizeof(cm_key));
+		cm_result1 = chunk_mac(chunk, cm_key);
+		memset(cm_key, 0xff, sizeof(cm_key));
+		cm_result2 = chunk_mac(chunk, cm_key);
+
+		/* Hardware type 255 per RFC4361 section 6.1 */
+		memcpy(datap, &id_type, sizeof(id_type));
+		datap += sizeof(id_type);
+
+		/* "Per-association" IAID per RFC3315 section 10 */
+		memcpy(datap, &iaid, sizeof(iaid));
+		datap += sizeof(iaid);
+
+		/* DUID-UUID (RFC 6355) */
+		duidp = datap;
+		memcpy(datap, &duid_uuid, sizeof(duid_uuid));
+		datap += sizeof(duid_uuid);
+
+		memcpy(datap, &cm_result1, sizeof(cm_result1));
+		datap += sizeof(cm_result1);
+
+		memcpy(datap, &cm_result2, sizeof(cm_result2));
+		datap += sizeof(cm_result2);
+
+		/* RFC 4122 UUID Type 5 */
+		duidp[6] &= 0x0f;
+		duidp[6] |= 0x30;
+
+		/* RFC 4122 UUID */
+		duidp[8] &= 0x3f;
+		duidp[8] |= 0x80;
+
+		option->len = datap - (uint8_t *)option->data;
+	}
+
 	optlen += sizeof(dhcp_option_t) + option->len;
 
 	return optlen;
@@ -263,11 +365,47 @@ static int prepare_dhcp(private_dhcp_soc
  * Send a DHCP message with given options length
  */
 static bool send_dhcp(private_dhcp_socket_t *this,
-					  dhcp_transaction_t *transaction, dhcp_t *dhcp, int optlen)
+		      dhcp_transaction_t *transaction, dhcp_t *dhcp, int optlen)
 {
-	host_t *dst;
+	host_t *dst, *extra_dst;
+	struct sockaddr_in *extra_sin;
 	ssize_t len;
 
+	len = offsetof(dhcp_t, magic_cookie) + ((optlen + 4) / 64 * 64 + 64);
+
+	extra_dst = this->extra_dst;
+	extra_sin = (struct sockaddr_in *)extra_dst->get_sockaddr(extra_dst);
+	if (extra_sin->sin_addr.s_addr != INADDR_ANY) {
+		/*
+		 * We are running against failover DHCP servers.  Per
+		 * draft-ietf-dhc-failover-12 section 3, we should
+		 * send "client broadcasts" to both; only one will
+		 * answer unless there is a timeout.
+		 */
+		dst = transaction->get_server(transaction);
+		if (!dst) {
+			dst = this->dst;
+
+			/* This is a DISCOVER or other broadcast message */
+			DBG1(DBG_CFG, "duplicating DHCP message to %H",
+			     extra_dst);
+
+			if (sendto(this->send, dhcp, len, 0,
+				   extra_dst->get_sockaddr(extra_dst),
+				   *extra_dst->get_sockaddr_len(extra_dst))
+			    != len) {
+				return FALSE;
+			}
+		}
+
+		if (sendto(this->send, dhcp, len, 0, dst->get_sockaddr(dst),
+			   *dst->get_sockaddr_len(dst)) != len)
+		{
+			return FALSE;
+		}
+		return TRUE;
+	}
+
 	dst = transaction->get_server(transaction);
 	if (!dst || this->force_dst)
 	{
@@ -282,13 +420,14 @@ static bool send_dhcp(private_dhcp_socke
  * Send DHCP discover using a given transaction
  */
 static bool discover(private_dhcp_socket_t *this,
-					 dhcp_transaction_t *transaction)
+		     dhcp_transaction_t *transaction,
+		     uint32_t seconds_elapsed)
 {
 	dhcp_option_t *option;
 	dhcp_t dhcp;
 	int optlen;
 
-	optlen = prepare_dhcp(this, transaction, DHCP_DISCOVER, &dhcp);
+	optlen = prepare_dhcp(this, transaction, DHCP_DISCOVER, &dhcp, seconds_elapsed);
 
 	DBG1(DBG_CFG, "sending DHCP DISCOVER to %H", this->dst);
 
@@ -313,7 +452,8 @@ static bool discover(private_dhcp_socket
  * Send DHCP request using a given transaction
  */
 static bool request(private_dhcp_socket_t *this,
-					dhcp_transaction_t *transaction)
+		    dhcp_transaction_t *transaction,
+		    uint32_t seconds_elapsed)
 {
 	dhcp_option_t *option;
 	dhcp_t dhcp;
@@ -321,7 +461,7 @@ static bool request(private_dhcp_socket_
 	chunk_t chunk;
 	int optlen;
 
-	optlen = prepare_dhcp(this, transaction, DHCP_REQUEST, &dhcp);
+	optlen = prepare_dhcp(this, transaction, DHCP_REQUEST, &dhcp, seconds_elapsed);
 
 	offer = transaction->get_address(transaction);
 	server = transaction->get_server(transaction);
@@ -366,8 +506,8 @@ METHOD(dhcp_socket_t, enroll, dhcp_trans
 	private_dhcp_socket_t *this, identification_t *identity)
 {
 	dhcp_transaction_t *transaction;
-	uint32_t id;
-	int try;
+	uint32_t id, seconds_elapsed = 0;
+	int try, tries;
 
 	if (!this->rng->get_bytes(this->rng, sizeof(id), (uint8_t*)&id))
 	{
@@ -379,15 +519,30 @@ METHOD(dhcp_socket_t, enroll, dhcp_trans
 	this->mutex->lock(this->mutex);
 	this->discover->insert_last(this->discover, transaction);
 	try = 1;
-	while (try <= DHCP_TRIES && discover(this, transaction))
-	{
-		if (!this->condvar->timed_wait(this->condvar, this->mutex, 1000 * try) &&
-			this->request->find_first(this->request, NULL, (void**)&transaction))
-		{
+	transaction->set_started(transaction, time(NULL));
+	discover(this, transaction, seconds_elapsed);
+
+	while(1) {
+		time_t now, started;
+
+		if (!this->condvar->timed_wait(this->condvar, this->mutex, 1000) &&
+		    this->request->find_first(this->request, NULL,
+					      (void**)&transaction)) {
 			break;
 		}
-		try++;
+		now = time(NULL);
+		started = transaction->get_started(transaction);
+		seconds_elapsed = now - started;
+		if (seconds_elapsed > DHCP_TRIES) {
+			break;
+		}
+		if (seconds_elapsed > try) {
+			try++;
+			discover(this, transaction, seconds_elapsed);
+		}
 	}
+	tries = try;
+		
 	if (this->discover->remove(this->discover, transaction, NULL))
 	{	/* no OFFER received */
 		this->mutex->unlock(this->mutex);
@@ -396,16 +551,38 @@ METHOD(dhcp_socket_t, enroll, dhcp_trans
 		return NULL;
 	}
 
+	/*
+	 * RFC2131 is ambiguous about whether the "secs" field is reset
+	 * between DISCOVER and REQUEST.  The table in 4.4.1 (page 36) says
+	 * "0 or seconds since DHCP process started", meaning no reset,
+	 * but the text in 3.1.3 says "MUST use the same value in the DHCP
+	 * message header's 'secs' field", meaning reset it back to where
+	 * it started.  We follow what most clients (e.g. dhcpcd) do and
+	 * keep it ticking through the whole transaction, per 4.4.1.
+	 *
+	 * Elsewise, we would reset seconds_elapsed to 0 here.
+	 */
 	try = 1;
-	while (try <= DHCP_TRIES && request(this, transaction))
-	{
-		if (!this->condvar->timed_wait(this->condvar, this->mutex, 1000 * try) &&
-			this->completed->remove(this->completed, transaction, NULL))
-		{
+	request(this, transaction, seconds_elapsed);
+	while(1) {
+		time_t now, started;
+
+		if (!this->condvar->timed_wait(this->condvar, this->mutex, 1000) &&
+		    this->completed->remove(this->completed, transaction, NULL)) {
 			break;
+                }
+		now = time(NULL);
+		started = transaction->get_started(transaction);
+		seconds_elapsed = now - started;
+		if (seconds_elapsed > tries + DHCP_TRIES) {
+                        break;
+		}
+		if (seconds_elapsed > try + tries) {
+			try++;
+			request(this, transaction, seconds_elapsed);
 		}
-		try++;
 	}
+
 	if (this->request->remove(this->request, transaction, NULL))
 	{	/* no ACK received */
 		this->mutex->unlock(this->mutex);
@@ -427,7 +604,17 @@ METHOD(dhcp_socket_t, release, void,
 	chunk_t chunk;
 	int optlen;
 
-	optlen = prepare_dhcp(this, transaction, DHCP_RELEASE, &dhcp);
+	/*
+	 * Because some servers will always grant us a different
+	 * address after a Release, we may prefer to allow the
+	 * server to expire leases (giving the client a good
+	 * chance to receive the same address on next connection)
+	 * rather than generating a Release each time the SA goes
+	 * down.
+	 */
+	if (this->release_on_delete == FALSE) return;
+
+	optlen = prepare_dhcp(this, transaction, DHCP_RELEASE, &dhcp, 0);
 
 	release = transaction->get_address(transaction);
 	server = transaction->get_server(transaction);
@@ -735,9 +922,17 @@ dhcp_socket_t *dhcp_socket_create()
 	this->force_dst = lib->settings->get_str(lib->settings,
 								"%s.plugins.dhcp.force_server_address", FALSE,
 								lib->ns);
+	this->release_on_delete = lib->settings->get_bool(lib->settings,
+								"%s.plugins.dhcp.release_on_delete", TRUE,
+								lib->ns);
+	this->relay_addr = host_create_from_string(lib->settings->get_str(lib->settings, 
+								 "%s.plugins.dhcp.relay_addr", "0.0.0.0",
+								 lib->ns), DHCP_SERVER_PORT);
 	this->dst = host_create_from_string(lib->settings->get_str(lib->settings,
 								"%s.plugins.dhcp.server", "255.255.255.255",
 								lib->ns), DHCP_SERVER_PORT);
+	this->extra_dst = host_create_from_string(lib->settings->get_str(lib->settings, "%s.plugins.dhcp.extra_server",
+								"0.0.0.0", lib->ns), DHCP_SERVER_PORT);
 	iface = lib->settings->get_str(lib->settings, "%s.plugins.dhcp.interface",
 								   NULL, lib->ns);
 	if (!this->dst)
@@ -747,6 +942,38 @@ dhcp_socket_t *dhcp_socket_create()
 		return NULL;
 	}
 
+	if (!this->extra_dst)
+	{
+		DBG1(DBG_CFG, "configured DHCP extra_server address invalid");
+		destroy(this);
+		return NULL;
+	}	
+
+	if (this->relay_addr)
+	{
+		host_t *relay_addr = this->relay_addr;
+		struct sockaddr_in *relay_sockaddr;
+
+		/*
+		 * Relays must receive on port 67 per RFC2131 4.1:
+		 * "If the 'giaddr' field in a DHCP message from a client
+		 *  is non-zero, the server sends any return messages
+		 *  to the 'DHCP server' port on the BOOTP relay agent
+		 *  whose address appears in 'giaddr'."
+		 *
+		 * Our choices (unless we want the local host to respond
+		 * with ICMP unreachables) are to bind _both_ ports
+		 * 67 and 68 when acting as a relay, or to simply send
+		 * from port 67.  It is much simpler to just send
+		 * from 67 and it is recommended by section 4.7.1 of
+		 * draft-ietf-dhc-implementation-02, so we do that.
+		 */
+		relay_sockaddr = (struct sockaddr_in *)relay_addr->get_sockaddr(relay_addr);
+		if (relay_sockaddr->sin_addr.s_addr != INADDR_ANY) {
+			src.sin_port = htons(DHCP_SERVER_PORT);
+		}
+	}
+
 	this->send = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
 	if (this->send == -1)
 	{
diff -rup strongswan-5.6.0/src/libcharon/plugins/dhcp/dhcp_transaction.c ../dev/dhcp/strongswan-5.6.0/src/libcharon/plugins/dhcp/dhcp_transaction.c
--- strongswan-5.6.0/src/libcharon/plugins/dhcp/dhcp_transaction.c	2017-08-14 02:48:41.000000000 -0400
+++ ../dev/dhcp/strongswan-5.6.0/src/libcharon/plugins/dhcp/dhcp_transaction.c	2018-02-07 21:12:58.058905673 -0500
@@ -35,6 +35,11 @@ struct private_dhcp_transaction_t {
 	uint32_t id;
 
 	/**
+	 * First message sent timestamp
+	 */
+	time_t started;
+
+	/**
 	 * Peer identity
 	 */
 	identification_t *identity;
@@ -69,6 +74,18 @@ METHOD(dhcp_transaction_t, get_id, uint3
 	return this->id;
 }
 
+METHOD(dhcp_transaction_t, set_started, void,
+	private_dhcp_transaction_t *this, time_t now)
+{
+	this->started = now;
+}
+
+METHOD(dhcp_transaction_t, get_started, time_t,
+	private_dhcp_transaction_t *this)
+{
+	return this->started;
+}
+
 METHOD(dhcp_transaction_t, get_identity, identification_t*,
 	private_dhcp_transaction_t *this)
 {
@@ -171,6 +188,8 @@ dhcp_transaction_t *dhcp_transaction_cre
 	INIT(this,
 		.public = {
 			.get_id = _get_id,
+			.set_started = _set_started,
+			.get_started = _get_started,
 			.get_identity = _get_identity,
 			.set_address = _set_address,
 			.get_address = _get_address,
@@ -185,6 +204,7 @@ dhcp_transaction_t *dhcp_transaction_cre
 		.attributes = linked_list_create(),
 	);
 
+	this->started = time(NULL);
 	return &this->public;
 }
 
diff -rup strongswan-5.6.0/src/libcharon/plugins/dhcp/dhcp_transaction.h ../dev/dhcp/strongswan-5.6.0/src/libcharon/plugins/dhcp/dhcp_transaction.h
--- strongswan-5.6.0/src/libcharon/plugins/dhcp/dhcp_transaction.h	2016-04-22 16:01:35.000000000 -0400
+++ ../dev/dhcp/strongswan-5.6.0/src/libcharon/plugins/dhcp/dhcp_transaction.h	2018-02-07 21:14:00.371255600 -0500
@@ -24,6 +24,7 @@
 #include <networking/host.h>
 #include <utils/identification.h>
 #include <attributes/attributes.h>
+#include <time.h>
 
 typedef struct dhcp_transaction_t dhcp_transaction_t;
 
@@ -47,6 +48,20 @@ struct dhcp_transaction_t {
 	identification_t* (*get_identity)(dhcp_transaction_t *this);
 
 	/**
+	 * Set the time when this transaction starts.
+	 *
+	 * @param now		transmission time of first packet
+	 */
+	void (*set_started)(dhcp_transaction_t *this, time_t now);
+
+	/**
+	 * Get the time when this transaction started.
+	 *
+	 * @return			Time when transaction started
+	 */
+	time_t (*get_started)(dhcp_transaction_t *this);
+
+	/**
 	 * Set the DHCP address received using this transaction.
 	 *
 	 * @param host		received DHCP address
diff -rup strongswan-5.6.0/src/libcharon/plugins/vici/ruby/Makefile.in ../dev/dhcp/strongswan-5.6.0/src/libcharon/plugins/vici/ruby/Makefile.in
--- strongswan-5.6.0/src/libcharon/plugins/vici/ruby/Makefile.in	2017-08-14 02:51:17.000000000 -0400
+++ ../dev/dhcp/strongswan-5.6.0/src/libcharon/plugins/vici/ruby/Makefile.in	2017-08-28 16:58:02.000000000 -0400
@@ -474,8 +474,8 @@ distclean-generic:
 maintainer-clean-generic:
 	@echo "This command is intended for maintainers to use"
 	@echo "it deletes files that may require special tools to rebuild."
-@RUBY_GEMS_INSTALL_FALSE@install-data-local:
 @RUBY_GEMS_INSTALL_FALSE@uninstall-local:
+@RUBY_GEMS_INSTALL_FALSE@install-data-local:
 clean: clean-am
 
 clean-am: clean-generic clean-libtool clean-local mostlyclean-am
