changeset 406:3a8e91184d4d

Added code to handle RADIUS client deconnection
author Sebastien Decugis <sdecugis@nict.go.jp>
date Wed, 10 Jun 2009 15:33:26 +0900
parents c7ea4e86870a
children 9ea089572588
files extensions/radius_gw/radius_gw.h extensions/radius_gw/rg_common.h extensions/radius_gw/rg_utils.c extensions/radius_gw/rgw_clients.c extensions/radius_gw/rgw_extensions.c extensions/radius_gw/sub_acct.c extensions/radius_gw/sub_debug.c
diffstat 7 files changed, 269 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/extensions/radius_gw/radius_gw.h	Wed Jun 10 15:33:06 2009 +0900
+++ b/extensions/radius_gw/radius_gw.h	Wed Jun 10 15:33:26 2009 +0900
@@ -116,6 +116,8 @@
 void rgw_clients_dispose(struct rgw_client ** ref);
 void rgw_clients_dump(void);
 void rgw_clients_fini(void);
+int rgw_client_session_add(struct rgw_client * cli, sess_id_t *sess, char * dest_realm, char * dest_host, application_id_t appid);
+int rgw_client_session_stop(struct rgw_client * cli, sess_id_t * sess, int32_t reason);
 
 
 /* Management of sub-extensions */
--- a/extensions/radius_gw/rg_common.h	Wed Jun 10 15:33:06 2009 +0900
+++ b/extensions/radius_gw/rg_common.h	Wed Jun 10 15:33:26 2009 +0900
@@ -101,9 +101,9 @@
 int rg_pointers_init(void ** hdl);
 void rg_pointers_fini(void **hdl);
 #define rg_pointers_resolve( ptr, hdl, fct, ret ) {				\
-	ptr = dlsym(hdl, #fct);							\
+	ptr = dlsym(hdl, fct);							\
 	if (!ptr) {								\
-		TRACE_DEBUG(INFO, "Error in dlsym(" #fct "): %s", dlerror());	\
+		TRACE_DEBUG(INFO, "Error in dlsym(" fct "): %s", dlerror());	\
 		return ret;							\
 	}									\
 }
--- a/extensions/radius_gw/rg_utils.c	Wed Jun 10 15:33:06 2009 +0900
+++ b/extensions/radius_gw/rg_utils.c	Wed Jun 10 15:33:26 2009 +0900
@@ -130,7 +130,7 @@
 int rg_pointers_init(void ** hdl)
 {
 	CHECK_PARAMS(hdl);
-	*hdl = dlopen(0, RTLD_LAZY);
+	*hdl = dlopen(0, RTLD_LAZY | RTLD_GLOBAL);
 	if (!*hdl) {
 		TRACE_DEBUG(INFO, "Error in dlopen(0): %s", dlerror());
 		return ENOTSUP;
--- a/extensions/radius_gw/rgw_clients.c	Wed Jun 10 15:33:06 2009 +0900
+++ b/extensions/radius_gw/rgw_clients.c	Wed Jun 10 15:33:26 2009 +0900
@@ -82,8 +82,22 @@
 		uint8_t			id;
 		struct radius_msg * 	ans; /* to be able to resend the lost answer */
 	} last[2];
+	
+	/* User sessions attached to this client (ordered by session-id) */
+	struct rg_list		sessions; /* list of struct rgw_client_session */
 };
 
+struct rgw_client_session {
+	struct rg_list chain;
+	char * sid;
+	sess_id_t *session;
+	char * dest_host;
+	char * dest_realm;
+	application_id_t appid;
+};
+
+static dict_object_t * str_dict = NULL;
+
 
 /* create a new rgw_client. the arguments are moved into the structure. */
 static int client_create(struct rgw_client ** res, struct sockaddr ** ip_port, unsigned char ** key, size_t keylen )
@@ -103,6 +117,7 @@
 	CHECK_MALLOC( tmp = malloc(sizeof (struct rgw_client)) );
 	memset(tmp, 0, sizeof(struct rgw_client));
 	rg_list_init(&tmp->chain);
+	rg_list_init(&tmp->sessions);
 	
 	/* Copy the fqdn */
 	CHECK_MALLOC( tmp->fqdn = strdup(buf) );
@@ -135,9 +150,13 @@
 	
 	if (client->refcount <= 0) {
 		int idx;
-		/* to be sure */
+		/* to be sure: the refcount should be 0 only when client_fini is called */
 		ASSERT( rg_list_is_empty(&client->chain) );
 		
+		/* All sessions should have been deleted also */
+		ASSERT( rg_list_is_empty(&client->sessions) );
+		
+		
 		/* Free the data */
 		for (idx = 0; idx < client->aliases_nb; idx++)
 			free(client->aliases[idx]);
@@ -504,6 +523,7 @@
 {
 	rg_list_init(&cli_ip);
 	rg_list_init(&cli_ip6);
+	CHECK_FCT( dict_search ( DICT_COMMAND, CMD_BY_NAME, "Session-Termination-Request", &str_dict, ENOENT) );
 	return 0;
 }
 
@@ -692,3 +712,229 @@
 	return 0;
 }
 
+/* New user session attached to the client. Server info is needed */
+int rgw_client_session_add(struct rgw_client * cli, sess_id_t *sess, char * dest_realm, char * dest_host, application_id_t appid)
+{
+	struct rgw_client_session * new;
+	struct rg_list * li;
+	
+	TRACE_ENTRY("%p %p %p %p %d", cli, sess, dest_realm, dest_host, appid);
+	CHECK_PARAMS(cli && sess && dest_realm);
+	
+	CHECK_MALLOC(new = malloc(sizeof(struct rgw_client_session)));
+	memset(new, 0, sizeof(*new));
+	rg_list_init(&new->chain);
+	
+	/* Increment the session refcount */
+	CHECK_FCT( sess_link(sess) );
+	
+	/* Get the session name */
+	CHECK_FCT( sess_getsid(sess, &new->sid) );
+	new->session = sess;
+	
+	/* Copy other information */
+	CHECK_MALLOC( new->dest_realm = strdup(dest_realm) );
+	if (dest_host) {
+		CHECK_MALLOC( new->dest_host = strdup(dest_host) );
+	}
+	new->appid = appid;
+	
+	/* Now insert in the client's list */
+	for (li = cli->sessions.next; li != &cli->sessions; li = li->next) {
+		struct rgw_client_session *sli = (struct rgw_client_session *)li;
+		int cmp = strcmp(new->sid, sli->sid);
+		if (cmp <= 0)
+			break;
+	}
+	rg_list_insert_before(li, &new->chain);
+	
+	return 0;
+}
+
+static void sta_cb(void * data, msg_t ** answer)
+{
+	TRACE_ENTRY("%p %p");
+	
+	/* We should destroy all data associated to this session here */
+	TRACE_DEBUG(INFO, "Received STA, should destroy the session data, not implemented yet...");
+	
+}
+
+static int send_str(sess_id_t * sess, int32_t reason, char * orig_host, char * orig_realm, char * dest_host, char * dest_realm, application_id_t  appid)
+{
+	char * sid;
+	char * reason_str = "unknown";
+	msg_t * str;
+	dict_type_enum_request_t get_reason_name =
+	 	{
+		 .type_name = "Enumerated(Termination-Cause)",
+		 .search.enum_name=NULL,
+		 .search.enum_value.i32 = reason
+	 	};
+	dict_object_t * reason_enum;
+	
+	TRACE_ENTRY("%p %u %s %s %s %s %d", sess, reason, orig_host, orig_realm, dest_host, dest_realm, appid);
+	CHECK_PARAMS(sess && orig_host && orig_realm && dest_realm);
+	
+	CHECK_FCT( sess_getsid(sess, &sid) );
+	
+	CHECK_FCT( dict_search ( DICT_TYPE_ENUM, ENUM_BY_STRUCT, &get_reason_name, &reason_enum, 0) );
+	if (reason_enum) {
+		dict_type_enum_data_t reason_data;
+		CHECK_FCT( dict_getval ( reason_enum, &reason_data ) );
+		reason_str = reason_data.enum_name;
+	}
+	TRACE_DEBUG(INFO, "Terminating session '%s', cause %s (%d).", sid, reason_str, reason);
+	
+	/* Now ,create the STR message */
+	CHECK_FCT( msg_new(str_dict, MSGFL_ALLOW_ETEID, &str) );
+	
+	/* Add the Session-Id */
+	{
+		dict_object_t * avp_dict;
+		avp_value_t value;
+		msg_avp_t * avp;
+		CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Session-Id", &avp_dict, ENOENT));
+		CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );
+		value.os.data = sid;
+		value.os.len = strlen(sid);
+		CHECK_FCT( msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( msg_avp_add ( str, MSG_BRW_FIRST_CHILD, avp) );
+	}
+	/* Add the Origin-Host */
+	{
+		dict_object_t * avp_dict;
+		avp_value_t value;
+		msg_avp_t * avp;
+		CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Origin-Host", &avp_dict, ENOENT));
+		CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );
+		value.os.data = orig_host;
+		value.os.len = strlen(orig_host);
+		CHECK_FCT( msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) );
+	}
+	/* Add the Origin-Realm */
+	{
+		dict_object_t * avp_dict;
+		avp_value_t value;
+		msg_avp_t * avp;
+		CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Origin-Realm", &avp_dict, ENOENT));
+		CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );
+		value.os.data = orig_realm;
+		value.os.len = strlen(orig_realm);
+		CHECK_FCT( msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) );
+	}
+	/* Add the Destination-Realm */
+	{
+		dict_object_t * avp_dict;
+		avp_value_t value;
+		msg_avp_t * avp;
+		CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Destination-Realm", &avp_dict, ENOENT));
+		CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );
+		value.os.data = dest_realm;
+		value.os.len = strlen(dest_realm);
+		CHECK_FCT( msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) );
+	}
+	/* Add the Destination-Host */
+	if (dest_host) {
+		dict_object_t * avp_dict;
+		avp_value_t value;
+		msg_avp_t * avp;
+		CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Destination-Host", &avp_dict, ENOENT));
+		CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );
+		value.os.data = dest_host;
+		value.os.len = strlen(dest_host);
+		CHECK_FCT( msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) );
+	}
+	/* Add the Auth-Application-Id */
+	{
+		dict_object_t * avp_dict;
+		avp_value_t value;
+		msg_avp_t * avp;
+		CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Auth-Application-Id", &avp_dict, ENOENT));
+		CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );
+		value.i32 = appid;
+		CHECK_FCT( msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) );
+	}
+	/* also in the header of the message */
+	{
+		msg_data_t *str_data;
+		CHECK_FCT( msg_data ( str, &str_data ) );
+		str_data->msg_appl = appid;
+	}
+	/* Add the Termination-Cause */
+	{
+		dict_object_t * avp_dict;
+		avp_value_t value;
+		msg_avp_t * avp;
+		CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Termination-Cause", &avp_dict, ENOENT));
+		CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );
+		value.i32 = reason;
+		CHECK_FCT( msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) );
+	}
+	/* Add a Route-Record for our host */
+	{
+		dict_object_t * avp_dict;
+		avp_value_t value;
+		msg_avp_t * avp;
+		CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Route-Record", &avp_dict, ENOENT));
+		CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );
+		value.os.data = g_pconf->diameter_identity;
+		value.os.len = strlen(g_pconf->diameter_identity);
+		CHECK_FCT( msg_avp_setvalue ( avp, &value ) );
+		CHECK_FCT( msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) );
+	}
+	
+	/* Send this message */
+	CHECK_FCT( msg_send( &str, sta_cb, NULL ) );
+	
+	return 0;
+}
+
+static int sli_str_free(struct rgw_client_session *sli, struct rgw_client * cli, int32_t reason)
+{
+	CHECK_FCT( send_str(sli->session, reason, cli->fqdn, cli->realm, sli->dest_host, sli->dest_realm, sli->appid) );
+	rg_list_unlink(&sli->chain);
+	CHECK_FCT( sess_unlink ( &sli->session ) );
+	free(sli->dest_host);
+	free(sli->dest_realm);
+	free(sli);
+	return 0;
+}
+
+/* if sess is NULL, all sessions are stopped on this client */
+int rgw_client_session_stop(struct rgw_client * cli, sess_id_t * sess, int32_t reason)
+{
+	struct rg_list * li;
+	struct rgw_client_session *sli;
+	
+	TRACE_ENTRY("%p %p", cli, sess);
+	CHECK_PARAMS(cli);
+	
+	if (sess != NULL) {
+		/* Search this session in the list */
+		for (li = cli->sessions.next; li != &cli->sessions; li = li->next) {
+			sli = (struct rgw_client_session *)li;
+			if (sli->session == sess)
+				break;
+			sli = NULL;
+		}
+		/* Found it */
+		if (sli != NULL) {
+			CHECK_FCT( sli_str_free(sli, cli, reason) );
+		} else {
+			TRACE_DEBUG(FULL, "Session %p not found in this RADIUS client's list, ignore", sess);
+		}
+	} else {
+		for (li = cli->sessions.next; li != &cli->sessions; li = li->next) {
+			sli = (struct rgw_client_session *)li;
+			CHECK_FCT( sli_str_free(sli, cli, reason) );
+		}
+	}
+	return 0;
+}
--- a/extensions/radius_gw/rgw_extensions.c	Wed Jun 10 15:33:06 2009 +0900
+++ b/extensions/radius_gw/rgw_extensions.c	Wed Jun 10 15:33:26 2009 +0900
@@ -196,7 +196,7 @@
 	
 	/* Try and load the extension */
 	TRACE_DEBUG(FULL, "Loading subextension: %s", extfile);
-	new->dlo = dlopen(extfile, RTLD_LAZY | RTLD_LOCAL);
+	new->dlo = dlopen(extfile, RTLD_LAZY | RTLD_GLOBAL);
 	if (new->dlo == NULL) {
 		/* An error occured */
 		log_error("Loading of subextension '%s' failed:\n %s\n", extfile, dlerror());
--- a/extensions/radius_gw/sub_acct.c	Wed Jun 10 15:33:06 2009 +0900
+++ b/extensions/radius_gw/sub_acct.c	Wed Jun 10 15:33:26 2009 +0900
@@ -50,6 +50,8 @@
 
 struct rga_conf_state {
 	char * conffile;
+	void * dl_handle;
+	int (*rgw_client_session_stop)(void * cli, sess_id_t * sess, int32_t reason);
 };
 
 static struct rga_conf_state * acct_conf_parse(char * conffile)
@@ -68,6 +70,10 @@
 	} else {
 		TRACE_DEBUG(INFO, "Sub extension Accounting (RFC2866) initialized with default configuration");
 	}
+	
+	CHECK_FCT_DO( rg_pointers_init(&cs->dl_handle), return NULL );
+	rg_pointers_resolve(cs->rgw_client_session_stop, cs->dl_handle, "rgw_client_session_stop", NULL);
+	
 	return cs;
 }
 
@@ -75,6 +81,7 @@
 {
 	TRACE_ENTRY("%p", cs);
 	CHECK_PARAMS_DO( cs, );
+	rg_pointers_fini(&cs->dl_handle);
 	free(cs);
 	return;
 }
@@ -113,6 +120,14 @@
 		return -3;
 	}
 	
+	/* Handle Accounting-Off case: terminate all registered sessions with this RADIUS client */
+	if (status_type == RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF) {
+		TRACE_DEBUG(FULL, "Received Accounting-Off Acct-Status-Type attribute, replying directly and terminating all sessions.");
+		CHECK_MALLOC( *rad_ans = radius_msg_new(RADIUS_CODE_ACCOUNTING_RESPONSE, rad_req->hdr->identifier) );
+		CHECK_FCT( (*cs->rgw_client_session_stop)(cli, NULL, 4 /* DIAMETER_ADMINISTRATIVE */) );
+		return -3;
+	}
+	
 	return ENOTSUP;
 }
 
--- a/extensions/radius_gw/sub_debug.c	Wed Jun 10 15:33:06 2009 +0900
+++ b/extensions/radius_gw/sub_debug.c	Wed Jun 10 15:33:26 2009 +0900
@@ -66,7 +66,7 @@
 	
 	/* Resolve the msg_dump_walk function from main waaad image */
 	CHECK_FCT_DO( rg_pointers_init(&cs->waaad_handle), return NULL );
-	rg_pointers_resolve(cs->waaad_msg_dump_walk, cs->waaad_handle, msg_dump_walk, NULL);
+	rg_pointers_resolve(cs->waaad_msg_dump_walk, cs->waaad_handle, "msg_dump_walk", NULL);
 	
 	TRACE_DEBUG(INFO, "Sub extension Debug initialized with id: '%s'", cs->conffile);
 	
"Welcome to our mercurial repository"