# HG changeset patch # User Sebastien Decugis # Date 1271827384 -32400 # Node ID bce8e5b7bf780e5bfe52d4c20de7e43facd37e16 # Parent 1faed3f64af64bffc786956718ab7db8aa0cf861 Added code to send an STR after a STOP accounting record in RADIUS diff -r 1faed3f64af6 -r bce8e5b7bf78 extensions/app_radgw/rgw_common.h --- a/extensions/app_radgw/rgw_common.h Wed Apr 21 10:39:19 2010 +0900 +++ b/extensions/app_radgw/rgw_common.h Wed Apr 21 14:23:04 2010 +0900 @@ -93,6 +93,9 @@ #define RGW_PLG_TYPE_AUTH 1 #define RGW_PLG_TYPE_ACCT 2 +/* Class attribute prefix to store the Auth Application Id (required to send STR) */ +#define CLASS_AAI_PREFIX "fD/rgwx/aai:" + /* Attributes missing from radius.h (not used in EAP) */ enum { RADIUS_ATTR_CHAP_PASSWORD = 3, RADIUS_ATTR_SERVICE_TYPE = 6, diff -r 1faed3f64af6 -r bce8e5b7bf78 extensions/app_radgw/rgwx_acct.c --- a/extensions/app_radgw/rgwx_acct.c Wed Apr 21 10:39:19 2010 +0900 +++ b/extensions/app_radgw/rgwx_acct.c Wed Apr 21 14:23:04 2010 +0900 @@ -81,6 +81,8 @@ struct dict_object * CHAP_Ident; /* CHAP-Ident */ struct dict_object * CHAP_Response; /* CHAP-Response */ struct dict_object * Connect_Info; /* Connect-Info */ + struct dict_object * Destination_Host; /* Destination-Host */ + struct dict_object * Destination_Realm; /* Destination-Realm */ struct dict_object * EAP_Payload; /* EAP-Payload */ struct dict_object * Error_Message; /* Error-Message */ struct dict_object * Error_Reporting_Host; /* Error-Reporting-Host */ @@ -129,6 +131,7 @@ struct dict_object * Session_Id; /* Session-Id */ struct dict_object * Session_Timeout; /* Session-Timeout */ struct dict_object * State; /* State */ + struct dict_object * Termination_Cause; /* Termination-Cause */ struct dict_object * Tunneling; /* Tunneling */ struct dict_object * Tunnel_Type; /* Tunnel-Type */ struct dict_object * Tunnel_Assignment_Id; /* Tunnel-Assignment-Id */ @@ -140,6 +143,8 @@ struct dict_object * Tunnel_Client_Auth_Id; /* Tunnel-Client-Auth-Id */ struct dict_object * Tunnel_Server_Auth_Id; /* Tunnel-Server-Auth-Id */ struct dict_object * User_Name; /* User-Name */ + + struct dict_object * Session_Termination_Request;/* STR */ } dict; /* cache of the dictionary objects we use */ struct session_handler * sess_hdl; /* We store RADIUS request authenticator information in the session */ char * confstr; @@ -147,9 +152,10 @@ /* The state we store in the session */ struct sess_state { - uint8_t req_auth[16]; /* The request authenticator */ - int send_str; /* If not 0, we must send a STR when the ACA is received. */ - uint32_t term_cause; /* If not 0, the Termination-Cause to put in the STR. */ + uint8_t req_auth[16]; /* The request authenticator */ + application_id_t auth_appl; /* Auth-Application-Id used for this session, if available (stored in a Class attribute) */ + int send_str; /* If not 0, we must send a STR when the ACA is received. */ + uint32_t term_cause; /* If not 0, the Termination-Cause to put in the STR. */ }; /* Initialize the plugin */ @@ -192,6 +198,8 @@ CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Calling-Station-Id", &new->dict.Calling_Station_Id, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Class", &new->dict.Class, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Connect-Info", &new->dict.Connect_Info, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Host", &new->dict.Destination_Host, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Realm", &new->dict.Destination_Realm, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "EAP-Payload", &new->dict.EAP_Payload, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Message", &new->dict.Error_Message, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Reporting-Host", &new->dict.Error_Reporting_Host, ENOENT) ); @@ -240,6 +248,7 @@ CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &new->dict.Session_Id, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Timeout", &new->dict.Session_Timeout, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "State", &new->dict.State, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Termination-Cause", &new->dict.Termination_Cause, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunneling", &new->dict.Tunneling, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Assignment-Id", &new->dict.Tunnel_Assignment_Id, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Type", &new->dict.Tunnel_Type, ENOENT) ); @@ -252,6 +261,8 @@ CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Server-Auth-Id", &new->dict.Tunnel_Server_Auth_Id, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "User-Name", &new->dict.User_Name, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Session-Termination-Request", &new->dict.Session_Termination_Request, ENOENT) ); + /* This plugin provides the following Diameter authentication applications support: */ CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Diameter Base Accounting", &app, ENOENT) ); CHECK_FCT( fd_disp_app_support ( app, NULL, 0, 1 ) ); @@ -275,6 +286,7 @@ int idx; int send_str=0; uint32_t str_cause=0; + application_id_t auth_appl=0; int got_id = 0; uint32_t status_type; uint32_t termination_action = 0; @@ -556,6 +568,23 @@ case RADIUS_ATTR_CLASS: CONV2DIAM_STR( Class ); + /* In addition, save the data in the session if it is "our" CLASS_AAI_PREFIX Class attribute */ + { + char buf[32]; + char * attr_val, *auth_val; + attr_val = (char *)(attr + 1); + auth_val = attr_val + strlen(CLASS_AAI_PREFIX); + if ( (attr->length > sizeof(struct radius_attr_hdr) + strlen(CLASS_AAI_PREFIX) ) + && (attr->length < sizeof(struct radius_attr_hdr) + strlen(CLASS_AAI_PREFIX) + sizeof(buf)) + && ! strncmp(attr_val, CLASS_AAI_PREFIX, strlen(CLASS_AAI_PREFIX))) { + + memset(buf, 0, sizeof(buf)); + memcpy(buf, auth_val, attr->length - sizeof(struct radius_attr_hdr) - strlen(CLASS_AAI_PREFIX)); + if (sscanf(buf, "%u", &auth_appl) == 1) { + TRACE_DEBUG(ANNOYING, "Found Class attribute with '%s' prefix (attr #%d), AAI:%u.", CLASS_AAI_PREFIX, idx, auth_appl); + } + } + } break; case RADIUS_ATTR_VENDOR_SPECIFIC: @@ -1042,6 +1071,7 @@ CHECK_MALLOC( st = malloc(sizeof(struct sess_state)) ); memset(st, 0, sizeof(struct sess_state)); memcpy(&st->req_auth, &rad_req->hdr->authenticator[0], 16); + st->auth_appl = auth_appl; st->send_str = send_str; st->term_cause = str_cause; CHECK_FCT( fd_sess_state_store( cs->sess_hdl, session, &st ) ); @@ -1050,6 +1080,36 @@ return 0; } +/* Callback when an STA is received after having sent an STR. */ +static void handle_sta(void * data, struct msg ** answer) +{ + struct rgwp_config * cs = data; + struct avp *avp; + struct avp_hdr *ahdr; + + CHECK_PARAMS_DO( data && answer && *answer, goto out ); + + /* Check the Diameter error code */ + CHECK_FCT_DO( fd_msg_search_avp (*answer, cs->dict.Result_Code, &avp), goto out ); + CHECK_PARAMS_DO( avp, goto out ); + CHECK_FCT_DO( fd_msg_avp_hdr ( avp, &ahdr ), goto out ); + if (ahdr->avp_value->u32 != ER_DIAMETER_SUCCESS) + goto out; + + /* OK, discard the message without complaining */ + fd_msg_free(*answer); + *answer = NULL; + +out: + if (answer && *answer) { + TRACE_DEBUG(INFO, "Received the following problematic STA message, discarding..."); + fd_msg_dump_walk( INFO, *answer ); + fd_msg_free(*answer); + *answer = NULL; + } + return; +} + static int acct_diam_ans( struct rgwp_config * cs, struct session * session, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli ) { struct sess_state * st = NULL; @@ -1117,10 +1177,64 @@ /* If it was a response to a STOP record, we must send an STR for this session */ if (st->send_str) { - TODO("Send STR, including sid, [Dest-Host=oh,] Dest-Realm=or, Term-Cause=st->term_cause... Register to receive the answer."); + struct msg * str = NULL; + char * fqdn; + char * realm; + union avp_value avp_val; + + /* Create a new STR message */ + CHECK_FCT( fd_msg_new ( cs->dict.Session_Termination_Request, MSGFL_ALLOC_ETEID, &str ) ); + + /* Add the Session-Id AVP as first AVP */ + CHECK_FCT( fd_msg_avp_new ( cs->dict.Session_Id, 0, &avp ) ); + CHECK_FCT( fd_msg_avp_setvalue ( avp, sid->avp_value ) ); + CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_FIRST_CHILD, avp) ); + + /* Add the Destination-Realm as next AVP */ + CHECK_FCT( fd_msg_avp_new ( cs->dict.Destination_Realm, 0, &avp ) ); + CHECK_FCT( fd_msg_avp_setvalue ( avp, or->avp_value ) ); + CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) ); + + /* Add the Destination-Host as next AVP */ + CHECK_FCT( fd_msg_avp_new ( cs->dict.Destination_Host, 0, &avp ) ); + CHECK_FCT( fd_msg_avp_setvalue ( avp, oh->avp_value ) ); + CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) ); + + /* Get information on the NAS */ + CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &realm) ); + + /* Add the Origin-Host as next AVP */ + CHECK_FCT( fd_msg_avp_new ( cs->dict.Origin_Host, 0, &avp ) ); + memset(&avp_val, 0, sizeof(avp_val)); + avp_val.os.data = (unsigned char *)fqdn; + avp_val.os.len = strlen(fqdn); + CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); + CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) ); + + /* Add the Origin-Realm as next AVP */ + CHECK_FCT( fd_msg_avp_new ( cs->dict.Origin_Realm, 0, &avp ) ); + memset(&avp_val, 0, sizeof(avp_val)); + avp_val.os.data = (unsigned char *)realm; + avp_val.os.len = strlen(realm); + CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); + CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) ); + + /* Auth-Application-Id -- if we did not get it from our Class attribute, we just set "0" */ + CHECK_FCT( fd_msg_avp_new ( cs->dict.Auth_Application_Id, 0, &avp ) ); + avp_val.u32 = st->auth_appl; + CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); + CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) ); + + /* Termination-Cause */ + CHECK_FCT( fd_msg_avp_new ( cs->dict.Termination_Cause, 0, &avp ) ); + avp_val.u32 = st->term_cause; + CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) ); + CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) ); + + /* Send this message */ + CHECK_FCT( fd_msg_send ( &str, handle_sta, cs ) ); } - /* No attributes should be found in Accounting-Response packets except Proxy-State and possibly Vendor- diff -r 1faed3f64af6 -r bce8e5b7bf78 extensions/app_radgw/rgwx_auth.c --- a/extensions/app_radgw/rgwx_auth.c Wed Apr 21 10:39:19 2010 +0900 +++ b/extensions/app_radgw/rgwx_auth.c Wed Apr 21 14:23:04 2010 +0900 @@ -914,6 +914,7 @@ static int auth_diam_ans( struct rgwp_config * cs, struct session * session, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli ) { + struct msg_hdr * hdr; struct avp *avp, *next, *avp_x, *avp_y, *asid, *aoh; struct avp_hdr *ahdr, *sid, *oh; char buf[254]; /* to store some attributes values (with final '\0') */ @@ -1048,11 +1049,20 @@ } /* The RFC text says that this should always be the case, but it seems odd... */ if ((*rad_fw)->hdr->code == RADIUS_CODE_ACCESS_ACCEPT) { + /* Add the Session-Id */ if (sizeof(buf) < snprintf(buf, sizeof(buf), "Diameter/%.*s", sid->avp_value->os.len, sid->avp_value->os.data)) { TRACE_DEBUG(INFO, "Data truncated in Class attribute: %s", buf); } CONV2RAD_STR(RADIUS_ATTR_CLASS, buf, strlen(buf), 0); + + /* Add the auth-application-id required for STR */ + CHECK_FCT( fd_msg_hdr( *diam_ans, &hdr ) ); + if (sizeof(buf) < snprintf(buf, sizeof(buf), CLASS_AAI_PREFIX "%u", + hdr->msg_appl)) { + TRACE_DEBUG(INFO, "Data truncated in Class attribute: %s", buf); + } + CONV2RAD_STR(RADIUS_ATTR_CLASS, buf, strlen(buf), 0); } /* Unlink the Origin-Realm now; the others are unlinked at the end of this function */