# HG changeset patch # User Sebastien Decugis # Date 1244615606 -32400 # Node ID 3a8e91184d4dd22b2893dafc093bba0c99e66a6e # Parent c7ea4e86870ac17bfdbfac0a5fd42cb497a71831 Added code to handle RADIUS client deconnection diff -r c7ea4e86870a -r 3a8e91184d4d extensions/radius_gw/radius_gw.h --- 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 */ diff -r c7ea4e86870a -r 3a8e91184d4d extensions/radius_gw/rg_common.h --- 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; \ } \ } diff -r c7ea4e86870a -r 3a8e91184d4d extensions/radius_gw/rg_utils.c --- 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; diff -r c7ea4e86870a -r 3a8e91184d4d extensions/radius_gw/rgw_clients.c --- 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; +} diff -r c7ea4e86870a -r 3a8e91184d4d extensions/radius_gw/rgw_extensions.c --- 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()); diff -r c7ea4e86870a -r 3a8e91184d4d extensions/radius_gw/sub_acct.c --- 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; } diff -r c7ea4e86870a -r 3a8e91184d4d extensions/radius_gw/sub_debug.c --- 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);