From 004227046a683e59f3038bed5f77967131721bb1 Mon Sep 17 00:00:00 2001
From: Jon Sexson <Jon.Sexson@syniverse.com>
Date: Thu, 15 May 2014 12:42:45 +0300
Subject: [PATCH 2/4] Add per-client response window options

Add support for "response_window" and "response_window_usec" in client
sections - options overriding the home server response window for
proxied requests, when set to non-zero. Clipped to the home server's
"zombie_period".

This allows faster home server failover when requests from
higher-priority clients arrive.
---
 raddb/clients.conf    | 12 ++++++++++++
 src/include/radiusd.h |  1 +
 src/main/client.c     | 19 +++++++++++++++++++
 src/main/process.c    | 39 ++++++++++++++++++++++++++++++---------
 4 files changed, 62 insertions(+), 9 deletions(-)

diff --git a/raddb/clients.conf b/raddb/clients.conf
index 81f4501..878276d 100644
--- a/raddb/clients.conf
+++ b/raddb/clients.conf
@@ -174,6 +174,18 @@ client localhost {
 #	virtual_server = home1
 
 	#
+	#  If set to anything but zero, overrides the home server response
+	#  window for proxied requests. Clipped to home server's
+	#  "zombie_period". The default is zero.
+	#
+	#  The time is specified with response_window and
+	#  response_window_usec for second and microsecond parts
+	#  respectively.
+	#
+	response_window = 0
+	response_window_usec = 0
+
+	#
 	#  A pointer to the "home_server_pool" OR a "home_server"
 	#  section that contains the CoA configuration for this
 	#  client.  For an example of a coa home server or pool,
diff --git a/src/include/radiusd.h b/src/include/radiusd.h
index 4aeff7d..ced55a6 100644
--- a/src/include/radiusd.h
+++ b/src/include/radiusd.h
@@ -82,6 +82,7 @@ typedef struct radclient {
 	char			*login;
 	char			*password;
 	char			*server;
+	struct timeval		response_window;
 	int			number;	/* internal use only */
 	CONF_SECTION const 	*cs;
 #ifdef WITH_STATS
diff --git a/src/main/client.c b/src/main/client.c
index feada49..ae8f1aa 100644
--- a/src/main/client.c
+++ b/src/main/client.c
@@ -490,6 +490,8 @@ RADCLIENT *client_find_old(fr_ipaddr_t const *ipaddr)
 static struct in_addr cl_ip4addr;
 static struct in6_addr cl_ip6addr;
 static char *cl_srcipaddr = NULL;
+static int response_window = 0;
+static int response_window_usec = 0;
 #ifdef WITH_TCP
 static char *hs_proto = NULL;
 #endif
@@ -537,6 +539,10 @@ static const CONF_PARSER client_config[] = {
 	  offsetof(RADCLIENT, password), 0, NULL },
 	{ "virtual_server",  PW_TYPE_STRING_PTR,
 	  offsetof(RADCLIENT, server), 0, NULL },
+	{ "response_window", PW_TYPE_INTEGER,
+	  0, &response_window, "0" },
+	{ "response_window_usec", PW_TYPE_INTEGER,
+	  0, &response_window_usec, "0" },
 
 #ifdef WITH_TCP
 	{ "proto",  PW_TYPE_STRING_PTR,
@@ -759,6 +765,19 @@ static RADCLIENT *client_parse(CONF_SECTION *cs, int in_server)
 		break;
 	}
 
+	FR_INTEGER_BOUND_CHECK("response_window", response_window, >=, 0);
+	FR_INTEGER_BOUND_CHECK("response_window", response_window, <=, 60);
+	FR_INTEGER_BOUND_CHECK("response_window", response_window, <=, main_config.max_request_time);
+	FR_INTEGER_BOUND_CHECK("response_window_usec", response_window_usec, >=, 0);
+	FR_INTEGER_BOUND_CHECK("response_window_usec", response_window_usec, <=, 999999);
+	FR_INTEGER_COND_CHECK("response_window_usec", response_window_usec,
+			      response_window < 60 &&
+					response_window < main_config.max_request_time,
+			      0);
+
+	c->response_window.tv_sec = response_window;
+	c->response_window.tv_usec = response_window_usec;
+
 #ifdef WITH_DYNAMIC_CLIENTS
 	if (c->client_server) {
 		c->secret = talloc_typed_strdup(c, "testing123");
diff --git a/src/main/process.c b/src/main/process.c
index e6deafd..91818a9 100644
--- a/src/main/process.c
+++ b/src/main/process.c
@@ -284,6 +284,7 @@ void radius_update_listener(rad_listen_t *this)
 
 static int request_num_counter = 0;
 #ifdef WITH_PROXY
+static void request_response_window(REQUEST *request, struct timeval *response_window);
 static int request_will_proxy(REQUEST *request);
 static int request_proxy(REQUEST *request, int retransmit);
 STATE_MACHINE_DECL(proxy_wait_for_reply);
@@ -419,7 +420,7 @@ static void request_timer(void *ctx)
  */
 STATE_MACHINE_DECL(request_done)
 {
-	struct timeval now, when;
+	struct timeval now, response_window, when;
 #ifdef WITH_PROXY
 	char buffer[128];
 #endif
@@ -595,7 +596,8 @@ STATE_MACHINE_DECL(request_done)
 			when.tv_sec += request->home_server->coa_mrd;
 		} else
 #endif
-		timeradd(&when, &request->home_server->response_window, &when);
+		request_response_window(request, &response_window);
+		timeradd(&when, &response_window, &when);
 
 		/*
 		 *	We haven't received all responses, AND there's still
@@ -1913,6 +1915,24 @@ static int eol_listener(void *ctx, void *data)
  ***********************************************************************/
 
 /*
+ * Determine request response window.
+ */
+static void request_response_window(REQUEST *request, struct timeval *response_window)
+{
+	if (timerisset(&request->client->response_window)) {
+		if (request->client->response_window.tv_sec >=
+			request->home_server->zombie_period) {
+			response_window->tv_sec = request->home_server->zombie_period;
+			response_window->tv_usec = 0;
+		} else {
+			*response_window = request->client->response_window;
+		}
+	} else {
+		*response_window = request->home_server->response_window;
+	}
+}
+
+/*
  *	Called with the proxy mutex held
  */
 static void remove_from_proxy_hash_nl(REQUEST *request, bool yank)
@@ -3031,7 +3051,8 @@ static void home_trigger(home_server_t *home, char const *trigger)
 	exec_trigger(&my_request, home->cs, trigger, false);
 }
 
-static void mark_home_server_zombie(home_server_t *home, struct timeval *now)
+static void mark_home_server_zombie(home_server_t *home,
+					struct timeval *now, struct timeval *response_window)
 {
 	time_t start;
 	char buffer[128];
@@ -3081,7 +3102,7 @@ static void mark_home_server_zombie(home_server_t *home, struct timeval *now)
 	PROXY( "Marking home server %s port %d as zombie (it has not responded in %d.%.6d seconds).",
 	       inet_ntop(home->ipaddr.af, &home->ipaddr.ipaddr,
 			 buffer, sizeof(buffer)),
-	       home->port, (int)home->response_window.tv_sec, (int)home->response_window.tv_usec);
+	       home->port, (int)response_window->tv_sec, (int)response_window->tv_usec);
 
 	ping_home_server(home);
 }
@@ -3161,7 +3182,7 @@ void mark_home_server_dead(home_server_t *home, struct timeval *when)
 
 STATE_MACHINE_DECL(proxy_wait_for_reply)
 {
-	struct timeval now, when;
+	struct timeval now, response_window, when;
 	home_server_t *home = request->home_server;
 	char buffer[128];
 
@@ -3256,6 +3277,7 @@ STATE_MACHINE_DECL(proxy_wait_for_reply)
 		break;
 
 	case FR_ACTION_TIMER:
+		request_response_window(request, &response_window);
 #ifdef WITH_TCP
 		if (!request->proxy_listener ||
 		    (request->proxy_listener->status != RAD_LISTEN_STATUS_KNOWN)) {
@@ -3280,7 +3302,7 @@ STATE_MACHINE_DECL(proxy_wait_for_reply)
 			 *	zombie if it doesn't respond to us.  It may be
 			 *	responding to other (better looking) packets.
 			 */
-			timeradd(&request->proxy->timestamp, &home->response_window, &when);
+			timeradd(&request->proxy->timestamp, &response_window, &when);
 
 			/*
 			 *	Not at the response window.  Set the timer for
@@ -3288,8 +3310,7 @@ STATE_MACHINE_DECL(proxy_wait_for_reply)
 			 */
 			if (timercmp(&when, &now, >)) {
 				RDEBUG("Expecting proxy response no later than %d.%.6d seconds from now",
-					(int)home->response_window.tv_sec,
-					(int)home->response_window.tv_usec);
+					(int)response_window.tv_sec, (int)response_window.tv_usec);
 				STATE_MACHINE_TIMER(FR_ACTION_TIMER);
 				return;
 			}
@@ -3315,7 +3336,7 @@ STATE_MACHINE_DECL(proxy_wait_for_reply)
 		    && (home->proto != IPPROTO_TCP)
 #endif
 			) {
-			mark_home_server_zombie(home, &now);
+			mark_home_server_zombie(home, &now, &response_window);
 		}
 
 		FR_STATS_TYPE_INC(home->stats.total_timeouts);
-- 
1.9.2

