From: Gabriel Valcazar <gabriel.valcazar@digi.com>
Date: Mon, 5 Mar 2018 10:00:23 +0100
Subject: [PATCH] dhclient: Check if the rebind time has expired when renewing
 the lease

If the system time suffers a big jump, the client will start the renewal
process but it will end prematurely due to the expiry of the lease time. By
adding this check, a time jump can be detected and the active lease can be
updated with the correct expiry dates.

https://jira.digi.com/browse/DEL-5233

Signed-off-by: Gabriel Valcazar <gabriel.valcazar@digi.com>
---
 client/dhclient.c | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/client/dhclient.c b/client/dhclient.c
index dcf3f1a..9074373 100644
--- a/client/dhclient.c
+++ b/client/dhclient.c
@@ -101,6 +101,8 @@ char *mockup_relay = NULL;
 
 char *progname = NULL;
 
+TIME time_of_binding;
+
 void run_stateless(int exit_mode, u_int16_t port);
 
 static isc_result_t write_duid(struct data_string *duid);
@@ -1440,6 +1442,9 @@ void bind_lease (client)
 	client->active = client->new;
 	client->new = NULL;
 
+	/* Save the time at which the binding occurred. */
+	time_of_binding = cur_time;
+
 	/* Set up a timeout to start the renewal process. */
 	tv.tv_sec = client->active->renewal;
 	tv.tv_usec = ((client->active->renewal - cur_tv.tv_sec) > 1) ?
@@ -1472,6 +1477,30 @@ void state_bound (cpp)
 
 	ASSERT_STATE(state, S_BOUND);
 
+	/*
+	 * The T2 lease time for the active lease should never be expired when
+	 * reaching this state. If so, it means there has been a time jump in
+	 * the system's clock. In this case, update the lease with the correct
+	 * times and create a new state_bound timeout.
+	 */
+	if (cur_time > client->active->rebind) {
+		struct timeval tv;
+
+		TIME time_jump = cur_time - time_of_binding;
+		client->active->renewal += time_jump;
+		client->active->rebind += time_jump;
+		client->active->expiry += time_jump;
+
+		rewrite_client_leases();
+
+		/* Set up a timeout to start the renewal process. */
+		tv.tv_sec = client->active->renewal;
+		tv.tv_usec = ((client->active->renewal - cur_tv.tv_sec) > 1) ?
+				random() % 1000000 : cur_tv.tv_usec;
+		add_timeout(&tv, state_bound, client, 0, 0);
+		return;
+	}
+
 	/* T1 has expired. */
 	make_request (client, client -> active);
 	client -> xid = client -> packet.xid;

