From 3c343b7902755f6110d89c9a87bf8c08ed30c705 Mon Sep 17 00:00:00 2001
From: Kozlov Dmitry <xeb@mail.ru>
Date: Thu, 19 Jul 2012 22:20:02 +0400
Subject: radius: implemented Idle-Timeout

---
 accel-pppd/include/ap_session.h |  1 +
 accel-pppd/radius/acct.c        | 60 +++++++++++++++++++++++++----------------
 accel-pppd/radius/backup.c      |  8 ++++++
 accel-pppd/radius/radius.c      | 31 +++++++++++++++++++++
 accel-pppd/radius/radius_p.h    |  6 +++++
 5 files changed, 83 insertions(+), 23 deletions(-)

diff --git a/accel-pppd/include/ap_session.h b/accel-pppd/include/ap_session.h
index ac0704b..cc7153c 100644
--- a/accel-pppd/include/ap_session.h
+++ b/accel-pppd/include/ap_session.h
@@ -18,6 +18,7 @@
 #define TERM_NAS_REBOOT 7
 #define TERM_AUTH_ERROR 8
 #define TERM_LOST_CARRIER 9
+#define TERM_IDLE_TIMEOUT 10
 
 #define CTRL_TYPE_PPTP  1
 #define CTRL_TYPE_L2TP  2
diff --git a/accel-pppd/radius/acct.c b/accel-pppd/radius/acct.c
index 87ffd83..ab71c45 100644
--- a/accel-pppd/radius/acct.c
+++ b/accel-pppd/radius/acct.c
@@ -21,6 +21,33 @@
 #define STAT_UPDATE_INTERVAL (10 * 60 * 1000)
 #define INTERIM_SAFE_TIME 10
 
+int rad_read_stats(struct radius_pd_t *rpd, struct rtnl_link_stats *stats)
+{
+	int r;
+
+	if (iplink_get_stats(rpd->ses->ifindex, stats)) {
+		log_ppp_warn("radius: failed to get interface statistics\n");
+		return -1;
+	}
+	
+	stats->rx_packets -= rpd->acct_rx_packets_i;
+	stats->tx_packets -= rpd->acct_tx_packets_i;
+	stats->rx_bytes -= rpd->acct_rx_bytes_i;
+	stats->tx_bytes -= rpd->acct_tx_bytes_i;
+
+	r = stats->rx_bytes != rpd->acct_rx_bytes || stats->tx_bytes < rpd->acct_tx_bytes;
+
+	if (stats->rx_bytes < rpd->acct_rx_bytes)
+		rpd->acct_input_gigawords++;
+	rpd->acct_rx_bytes = stats->rx_packets;
+
+	if (stats->tx_bytes < rpd->acct_tx_bytes)
+		rpd->acct_output_gigawords++;
+	rpd->acct_tx_bytes = stats->tx_bytes;
+
+	return r;
+}
+
 static int req_set_RA(struct rad_req_t *req, const char *secret)
 {
 	MD5_CTX ctx;
@@ -48,31 +75,15 @@ static void req_set_stat(struct rad_req_t *req, struct ap_session *ses)
 	else
 		time(&stop_time);
 
-	if (iplink_get_stats(ses->ifindex, &stats)) {
-		log_ppp_warn("radius: failed to get interface statistics\n");
-		return;
+	if (rad_read_stats(rpd, &stats) > 0) {
+		rad_packet_change_int(req->pack, NULL, "Acct-Input-Octets", stats.rx_bytes);
+		rad_packet_change_int(req->pack, NULL, "Acct-Output-Octets", stats.tx_bytes);
+		rad_packet_change_int(req->pack, NULL, "Acct-Input-Packets", stats.rx_packets);
+		rad_packet_change_int(req->pack, NULL, "Acct-Output-Packets", stats.tx_packets);
+		rad_packet_change_int(req->pack, NULL, "Acct-Input-Gigawords", rpd->acct_input_gigawords);
+		rad_packet_change_int(req->pack, NULL, "Acct-Output-Gigawords", rpd->acct_output_gigawords);
 	}
 
-	stats.rx_packets -= rpd->acct_rx_packets_i;
-	stats.tx_packets -= rpd->acct_tx_packets_i;
-	stats.rx_bytes -= rpd->acct_rx_bytes_i;
-	stats.tx_bytes -= rpd->acct_tx_bytes_i;
-
-	if (stats.rx_bytes < rpd->acct_rx_bytes)
-		req->rpd->acct_input_gigawords++;
-	req->rpd->acct_rx_bytes = stats.rx_packets;
-
-	if (stats.tx_bytes < rpd->acct_tx_bytes)
-		req->rpd->acct_output_gigawords++;
-	req->rpd->acct_tx_bytes = stats.tx_bytes;
-
-	rad_packet_change_int(req->pack, NULL, "Acct-Input-Octets", stats.rx_bytes);
-	rad_packet_change_int(req->pack, NULL, "Acct-Output-Octets", stats.tx_bytes);
-	rad_packet_change_int(req->pack, NULL, "Acct-Input-Packets", stats.rx_packets);
-	rad_packet_change_int(req->pack, NULL, "Acct-Output-Packets", stats.tx_packets);
-	rad_packet_change_int(req->pack, NULL, "Acct-Input-Gigawords", rpd->acct_input_gigawords);
-	rad_packet_change_int(req->pack, NULL, "Acct-Output-Gigawords", rpd->acct_output_gigawords);
-
 	rad_packet_change_int(req->pack, NULL, "Acct-Session-Time", stop_time - ses->start_time);
 }
 
@@ -416,6 +427,9 @@ void rad_acct_stop(struct radius_pd_t *rpd)
 			case TERM_LOST_CARRIER:
 				rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "Lost-Carrier");
 				break;
+			case TERM_IDLE_TIMEOUT:
+				rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "Idle-Timeout");
+				break;
 		}
 		rad_packet_change_val(rpd->acct_req->pack, NULL, "Acct-Status-Type", "Stop");
 		req_set_stat(rpd->acct_req, rpd->ses);
diff --git a/accel-pppd/radius/backup.c b/accel-pppd/radius/backup.c
index 4c40ec6..a44a760 100644
--- a/accel-pppd/radius/backup.c
+++ b/accel-pppd/radius/backup.c
@@ -21,6 +21,7 @@
 #define RAD_TAG_TERMINATION_ACTION          8
 #define RAD_TAG_ACCT_SERVER_ADDR            9
 #define RAD_TAG_ACCT_SERVER_PORT           10
+#define RAD_TAG_IDLE_TIMEOUT               11
 
 
 #define add_tag(id, data, size) if (!backup_add_tag(m, id, 0, data, size)) return -1;
@@ -30,6 +31,7 @@ static int session_save(struct ap_session *ses, struct backup_mod *m)
 {
 	struct radius_pd_t *rpd = find_pd(ses);
 	uint64_t session_timeout = ses->start_time + rpd->session_timeout.expire_tv.tv_sec;
+	uint32_t idle_timeout = rpd->idle_timeout.period / 1000;
 
 	if (!rpd)
 		return 0;
@@ -42,6 +44,9 @@ static int session_save(struct ap_session *ses, struct backup_mod *m)
 	if (rpd->session_timeout.tpd)
 		add_tag(RAD_TAG_SESSION_TIMEOUT, &session_timeout, 8);
 	
+	if (rpd->idle_timeout.tpd)
+		add_tag(RAD_TAG_IDLE_TIMEOUT, &idle_timeout, 4);
+	
 	if (ses->ipv4 == &rpd->ipv4_addr)
 		add_tag(RAD_TAG_IPV4_ADDR, NULL, 0);
 	
@@ -113,6 +118,9 @@ void radius_restore_session(struct ap_session *ses, struct radius_pd_t *rpd)
 			case RAD_TAG_SESSION_TIMEOUT:
 				rpd->session_timeout.expire_tv.tv_sec = *(uint64_t *)tag->data - ses->start_time;
 				break;
+			case RAD_TAG_IDLE_TIMEOUT:
+				rpd->idle_timeout.period = (*(uint32_t *)tag->data) * 1000;
+				break;
 			case RAD_TAG_IPV4_ADDR:
 				ses->ipv4 = &rpd->ipv4_addr;
 				restore_ipv4_addr(ses);
diff --git a/accel-pppd/radius/radius.c b/accel-pppd/radius/radius.c
index 048f736..f10b156 100644
--- a/accel-pppd/radius/radius.c
+++ b/accel-pppd/radius/radius.c
@@ -4,6 +4,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <arpa/inet.h>
+#include <linux/if_link.h>
 
 #include "mempool.h"
 #include "events.h"
@@ -94,6 +95,9 @@ int rad_proc_attrs(struct rad_req_t *req)
 			case Session_Timeout:
 				req->rpd->session_timeout.expire_tv.tv_sec = attr->val.integer;
 				break;
+			case Idle_Timeout:
+				req->rpd->idle_timeout.period = attr->val.integer * 1000;
+				break;
 			case Class:
 				if (!req->rpd->attr_class)
 					req->rpd->attr_class = _malloc(attr->len);
@@ -227,6 +231,25 @@ static void session_timeout(struct triton_timer_t *t)
 		ap_session_terminate(rpd->ses, TERM_SESSION_TIMEOUT, 0);
 }
 
+static void idle_timeout(struct triton_timer_t *t)
+{
+	struct radius_pd_t *rpd = container_of(t, typeof(*rpd), idle_timeout);
+	struct rtnl_link_stats stats;
+	
+	if (rpd->ses->stop_time)
+		return;
+
+	rad_read_stats(rpd, &stats);
+
+	if (stats.rx_packets == rpd->acct_rx_packets && stats.tx_packets == rpd->acct_tx_packets) {
+		log_ppp_msg("radius: session timed out\n");
+		ap_session_terminate(rpd->ses, TERM_IDLE_TIMEOUT, 0);
+	} else {
+		rpd->acct_rx_packets = stats.rx_packets;
+		rpd->acct_tx_packets = stats.tx_packets;
+	}
+}
+
 static void ses_starting(struct ap_session *ses)
 {
 	struct radius_pd_t *rpd = mempool_alloc(rpd_pool);
@@ -271,6 +294,11 @@ static void ses_acct_start(struct ap_session *ses)
 		rpd->session_timeout.expire = session_timeout;
 		triton_timer_add(ses->ctrl->ctx, &rpd->session_timeout, 0);
 	}
+	
+	if (rpd->idle_timeout.period) {
+		rpd->idle_timeout.expire = idle_timeout;
+		triton_timer_add(ses->ctrl->ctx, &rpd->idle_timeout, 0);
+	}
 }
 static void ses_finishing(struct ap_session *ses)
 {
@@ -303,6 +331,9 @@ static void ses_finished(struct ap_session *ses)
 
 	if (rpd->session_timeout.tpd)
 		triton_timer_del(&rpd->session_timeout);
+	
+	if (rpd->idle_timeout.tpd)
+		triton_timer_del(&rpd->idle_timeout);
 
 	if (rpd->attr_class)
 		_free(rpd->attr_class);
diff --git a/accel-pppd/radius/radius_p.h b/accel-pppd/radius/radius_p.h
index 383be7d..8a4d27a 100644
--- a/accel-pppd/radius/radius_p.h
+++ b/accel-pppd/radius/radius_p.h
@@ -27,6 +27,8 @@ struct radius_pd_t
 
 	uint32_t acct_rx_bytes;
 	uint32_t acct_tx_bytes;
+	uint32_t acct_rx_packets;
+	uint32_t acct_tx_packets;
 	uint32_t acct_input_gigawords;
 	uint32_t acct_output_gigawords;
 	uint32_t acct_rx_packets_i;
@@ -35,6 +37,7 @@ struct radius_pd_t
 	uint32_t acct_tx_bytes_i;
 
 	struct triton_timer_t session_timeout;
+	struct triton_timer_t idle_timeout;
 
 	struct rad_packet_t *dm_coa_req;
 	struct sockaddr_in dm_coa_addr;
@@ -187,6 +190,9 @@ void rad_server_reply(struct rad_server_t *);
 			
 void radius_restore_session(struct ap_session *ses, struct radius_pd_t *rpd);
 
+struct rtnl_link_stats;
+int rad_read_stats(struct radius_pd_t *rpd, struct rtnl_link_stats *stats);
+
 struct stat_accm_t;
 struct stat_accm_t *stat_accm_create(unsigned int time);
 void stat_accm_free(struct stat_accm_t *);
-- 
cgit v1.2.3