/* * $Id$ * * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com * * The initial version of this code was written by Dragos Vingarzan * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the * Fruanhofer Institute. It was and still is maintained in a separate * branch of the original SER. We are therefore migrating it to * Kamailio/SR and look forward to maintaining it from here on out. * 2011/2012 Smile Communications, Pty. Ltd. * ported/maintained/improved by * Jason Penton (jason(dot)penton(at)smilecoms.com and * Richard Good (richard(dot)good(at)smilecoms.com) as part of an * effort to add full IMS support to Kamailio/SR using a new and * improved architecture * * NB: Alot of this code was originally part of OpenIMSCore, * FhG Fokus. * Copyright (C) 2004-2006 FhG Fokus * Thanks for great work! This is an effort to * break apart the various CSCF functions into logically separate * components. We hope this will drive wider use. We also feel * that in this way the architecture is more complete and thereby easier * to manage in the Kamailio/SR environment * * This file is part of Kamailio, a free SIP server. * * Kamailio is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * * History: * -------- * 2011-02-02 initial version (jason.penton) */ #include "../../core/mem/shm_mem.h" #include "../../core/parser/sdp/sdp.h" #include "../cdp_avp/cdp_avp_mod.h" #include "../../modules/ims_dialog/dlg_load.h" #include "../../modules/tm/tm_load.h" #include "../ims_usrloc_pcscf/usrloc.h" #include "rx_authdata.h" #include "rx_aar.h" #include "rx_avp.h" #include "../../lib/ims/ims_getters.h" #include "ims_qos_mod.h" #include "../../lib/ims/useful_defs.h" #include "ims_qos_stats.h" #define macro_name(_rc) #_rc //extern struct tm_binds tmb; extern usrloc_api_t ul; extern struct ims_qos_counters_h ims_qos_cnts_h; extern int authorize_video_flow; extern str af_signaling_ip; extern str af_signaling_ip6; extern str component_media_type; extern str flow_protocol; str IMS_Serv_AVP_val = {"IMS Services", 12}; str IMS_Em_Serv_AVP_val = {"Emergency IMS Call", 18}; str IMS_Reg_AVP_val = {"IMS Registration", 16}; static void free_dialog_data(void *data) { str *rx_session_id = (str*) data; if (rx_session_id) { if (rx_session_id->s) { shm_free(rx_session_id->s); rx_session_id->s = 0; } shm_free(rx_session_id); rx_session_id = 0; } } void async_aar_callback(int is_timeout, void *param, AAAMessage *aaa, long elapsed_msecs) { struct cell *t = 0; unsigned int cdp_result; int result = CSCF_RETURN_ERROR; rx_authsessiondata_t* p_session_data = 0; AAASession *auth = 0; LM_DBG("Received AAR callback\n"); saved_transaction_t* data = (saved_transaction_t*) param; LM_DBG("received AAA answer"); if (tmb.t_lookup_ident(&t, data->tindex, data->tlabel) < 0) { LM_ERR("t_continue: transaction not found\n"); goto error; } else { LM_DBG("t_continue: transaction found\n"); } //we have T, lets restore our state (esp. for AVPs) set_avp_list(AVP_TRACK_FROM | AVP_CLASS_URI, &t->uri_avps_from); set_avp_list(AVP_TRACK_TO | AVP_CLASS_URI, &t->uri_avps_to); set_avp_list(AVP_TRACK_FROM | AVP_CLASS_USER, &t->user_avps_from); set_avp_list(AVP_TRACK_TO | AVP_CLASS_USER, &t->user_avps_to); set_avp_list(AVP_TRACK_FROM | AVP_CLASS_DOMAIN, &t->domain_avps_from); set_avp_list(AVP_TRACK_TO | AVP_CLASS_DOMAIN, &t->domain_avps_to); if (is_timeout != 0) { LM_ERR("Error timeout when sending AAR message via CDP\n"); counter_inc(ims_qos_cnts_h.media_aar_timeouts); goto error; } if (!aaa) { LM_ERR("Error sending message via CDP\n"); goto error; } counter_inc(ims_qos_cnts_h.media_aars); counter_add(ims_qos_cnts_h.media_aar_response_time, elapsed_msecs); counter_inc(ims_qos_cnts_h.media_aar_replies_received); /* Process the response to AAR, retrieving result code and associated Rx session ID */ if (rx_process_aaa(aaa, &cdp_result) < 0) { LM_ERR("Failed to process AAA from PCRF\n"); //puri.host.len, puri.host.s); goto error; } if (cdp_result >= 2000 && cdp_result < 3000) { LM_DBG("Success, received code: [%i] from PCRF for AAR request\n", cdp_result); counter_inc(ims_qos_cnts_h.successful_media_aars); LM_DBG("Auth session ID [%.*s]", aaa->sessionId->data.len, aaa->sessionId->data.s); if(!data->aar_update) { LM_DBG("This is an AAA response to an initial AAR"); //need to set Rx auth data to say this session has been successfully opened //This is used elsewhere to prevent acting on termination events when the session has not been opened //getting auth session auth = cdpb.AAAGetAuthSession(aaa->sessionId->data); if (!auth) { LM_DBG("Could not get Auth Session for session id: [%.*s]\n", aaa->sessionId->data.len, aaa->sessionId->data.s); goto error; } //getting session data p_session_data = (rx_authsessiondata_t*) auth->u.auth.generic_data; if (!p_session_data) { LM_DBG("Could not get session data on Auth Session for session id: [%.*s]\n", aaa->sessionId->data.len, aaa->sessionId->data.s); if (auth) cdpb.AAASessionsUnlock(auth->hash); goto error; } p_session_data->session_has_been_opened = 1; counter_inc(ims_qos_cnts_h.active_media_rx_sessions); counter_inc(ims_qos_cnts_h.media_rx_sessions); if (auth) cdpb.AAASessionsUnlock(auth->hash); str * passed_rx_session_id = shm_malloc(sizeof (struct _str)); passed_rx_session_id->s = 0; passed_rx_session_id->len = 0; STR_SHM_DUP(*passed_rx_session_id, aaa->sessionId->data, "cb_passed_rx_session_id"); LM_DBG("passed rx session id [%.*s]", passed_rx_session_id->len, passed_rx_session_id->s); dlgb.register_dlgcb_nodlg( data->dlg, DLGCB_TERMINATED | DLGCB_DESTROY | DLGCB_EXPIRED | DLGCB_RESPONSE_WITHIN | DLGCB_CONFIRMED | DLGCB_FAILED, callback_dialog, (void*) (passed_rx_session_id), free_dialog_data); } else { unref_dlg(data->dlg, 1); } result = CSCF_RETURN_TRUE; } else { LM_DBG("Received negative reply from PCRF for AAR Request\n"); counter_inc(ims_qos_cnts_h.failed_media_aars); //we don't free rx_authdata_p here - it is free-ed when the CDP session expires goto error; // if its not a success then that means i want to reject this call! } //set success response code AVP create_return_code(result); goto done; out_of_memory: error : //set failure response code create_return_code(result); done: if (t) tmb.unref_cell(t); //free memory if (aaa) cdpb.AAAFreeMessage(&aaa); tmb.t_continue(data->tindex, data->tlabel, data->act); free_saved_transaction_global_data(data); } void async_aar_reg_callback(int is_timeout, void *param, AAAMessage *aaa, long elapsed_msecs) { struct cell *t = 0; pcontact_t* pcontact; unsigned int cdp_result; struct pcontact_info ci; udomain_t* domain_t; int finalReply = 0; AAASession *auth = 0; rx_authsessiondata_t* p_session_data = 0; int result = CSCF_RETURN_ERROR; pcontact_info_t contact_info; LM_DBG("Received AAR callback\n"); saved_transaction_local_t* local_data = (saved_transaction_local_t*) param; saved_transaction_t* data = local_data->global_data; domain_t = data->domain; int is_rereg = local_data->is_rereg; //before we do anything else, lets decrement the reference counter on replies lock_get(data->lock); data->answers_not_received--; if (data->answers_not_received <= 0) { finalReply = 1; } if (data->ignore_replies) { //there was obv. a subsequent error AFTER we had sent one/more AAR's - so we can ignore these replies and just free memory free_saved_transaction_data(local_data); if (finalReply) { free_saved_transaction_global_data(data); } return; } lock_release(data->lock); LM_DBG("received answer and we are waiting for [%d] answers so far failures flag is [%d]\n", data->answers_not_received, data->failed); if (tmb.t_lookup_ident(&t, data->tindex, data->tlabel) < 0) { LM_ERR("t_continue: transaction not found\n"); goto error; } //we have T, lets restore our state (esp. for AVPs) set_avp_list(AVP_TRACK_FROM | AVP_CLASS_URI, &t->uri_avps_from); set_avp_list(AVP_TRACK_TO | AVP_CLASS_URI, &t->uri_avps_to); set_avp_list(AVP_TRACK_FROM | AVP_CLASS_USER, &t->user_avps_from); set_avp_list(AVP_TRACK_TO | AVP_CLASS_USER, &t->user_avps_to); set_avp_list(AVP_TRACK_FROM | AVP_CLASS_DOMAIN, &t->domain_avps_from); set_avp_list(AVP_TRACK_TO | AVP_CLASS_DOMAIN, &t->domain_avps_to); if (is_timeout != 0) { LM_ERR("Error timeout when sending AAR message via CDP\n"); counter_inc(ims_qos_cnts_h.registration_aar_timeouts); goto error; } if (!aaa) { LM_ERR("Error sending message via CDP\n"); goto error; } counter_inc(ims_qos_cnts_h.registration_aars); counter_add(ims_qos_cnts_h.registration_aar_response_time, elapsed_msecs); counter_inc(ims_qos_cnts_h.registration_aar_replies_received); /* Process the response to AAR, retrieving result code and associated Rx session ID */ if (rx_process_aaa(aaa, &cdp_result) < 0) { LM_ERR("Failed to process AAA from PCRF\n"); //puri.host.len, puri.host.s); goto error; } if (cdp_result >= 2000 && cdp_result < 3000) { counter_inc(ims_qos_cnts_h.successful_registration_aars); if (is_rereg) { LM_DBG("this is a re-registration, therefore we don't need to do anything except know that the subscription was successful\n"); result = CSCF_RETURN_TRUE; create_return_code(result); goto done; } //need to set Rx auth data to say this session has been successfully opened //This is used elsewhere to prevent acting on termination events when the session has not been opened //getting auth session auth = cdpb.AAAGetAuthSession(aaa->sessionId->data); if (!auth) { LM_DBG("Could not get Auth Session for session id: [%.*s]\n", aaa->sessionId->data.len, aaa->sessionId->data.s); goto error; } //getting session data p_session_data = (rx_authsessiondata_t*) auth->u.auth.generic_data; if (!p_session_data) { LM_DBG("Could not get session data on Auth Session for session id: [%.*s]\n", aaa->sessionId->data.len, aaa->sessionId->data.s); if (auth) cdpb.AAASessionsUnlock(auth->hash); goto error; } p_session_data->session_has_been_opened = 1; counter_inc(ims_qos_cnts_h.active_registration_rx_sessions); if (auth) cdpb.AAASessionsUnlock(auth->hash); LM_DBG("Success, received code: [%i] from PCRF for AAR request (contact: [%.*s]), (auth session id: %.*s)\n", cdp_result, local_data->contact.len, local_data->contact.s, local_data->auth_session_id.len, local_data->auth_session_id.s); LM_DBG("Registering for Usrloc callbacks on DELETE\n"); ul.lock_udomain(domain_t, &local_data->via_host, local_data->via_port, local_data->via_proto); memset(&contact_info, 0, sizeof(struct pcontact_info)); contact_info.received_host = local_data->recv_host; contact_info.received_port = local_data->recv_port; contact_info.received_proto = local_data->recv_proto; contact_info.searchflag = (1 << SEARCH_RECEIVED); contact_info.aor = local_data->contact; contact_info.via_host = local_data->via_host; contact_info.via_port = local_data->via_port; contact_info.via_prot = local_data->via_proto; contact_info.reg_state = PCONTACT_ANY; if (ul.get_pcontact(domain_t, &contact_info, &pcontact, 0) != 0) { LM_ERR("Shouldn't get here, can't find contact....\n"); ul.unlock_udomain(domain_t, &local_data->via_host, local_data->via_port, local_data->via_proto); goto error; } //at this point we have the contact /*set the contact state to say we have successfully done ARR for register * and that we dont need to do it again * for the duration of the registration. * */ if (ul.update_rx_regsession(domain_t, &local_data->auth_session_id, pcontact) != 0) { LM_ERR("unable to update pcontact......\n"); ul.unlock_udomain(domain_t, &local_data->via_host, local_data->via_port, local_data->via_proto); goto error; } memset(&ci, 0, sizeof (struct pcontact_info)); ci.reg_state = PCONTACT_REG_PENDING_AAR; ci.num_service_routes = 0; ci.num_public_ids = 0; LM_DBG("impu: [%.*s] updating status to PCONTACT_REG_PENDING\n", pcontact->aor.len, pcontact->aor.s); ul.update_pcontact(domain_t, &ci, pcontact); //register for callbacks on contact ul.register_ulcb(pcontact, PCSCF_CONTACT_DELETE | PCSCF_CONTACT_EXPIRE, callback_pcscf_contact_cb, NULL); ul.unlock_udomain(domain_t, &local_data->via_host, local_data->via_port, local_data->via_proto); result = CSCF_RETURN_TRUE; } else { LM_DBG("Received negative reply from PCRF for AAR Request\n"); counter_inc(ims_qos_cnts_h.failed_registration_aars); result = CSCF_RETURN_FALSE; goto error; } //set success response code AVP create_return_code(result); goto done; error: //set failure response code create_return_code(result); done: if (t) tmb.unref_cell(t); //free memory if (aaa) cdpb.AAAFreeMessage(&aaa); if (finalReply) { tmb.t_continue(data->tindex, data->tlabel, data->act); free_saved_transaction_global_data(data); } free_saved_transaction_data(local_data); } /* handle an AAA response to an AAR for resource reservation for a successful registration or initiated/updated dialog * @param aaa - the diameter reply * @return - 1 if result code found and processing ok, -1 if error */ int rx_process_aaa(AAAMessage *aaa, unsigned int * rc) { int ret = 1; ret = rx_get_result_code(aaa, rc); if (ret == 0) { LM_DBG("AAA message without result code\n"); return ret; } return ret; } /** Helper function for adding media component AVPs - uses previously stored flow descriptions not SDP from messages*/ int add_media_components_using_current_flow_description(AAAMessage* aar, rx_authsessiondata_t *p_session_data) { flow_description_t *flow_description; int add_flow = 1; flow_description = p_session_data->first_current_flow_description; if(!flow_description) { return -1; } while (flow_description) { if(!authorize_video_flow) { if (strncmp(flow_description->media.s, "video", 5) == 0) { add_flow = 0; } } if(add_flow) { rx_add_media_component_description_avp(aar, flow_description->stream_num, &flow_description->media, &flow_description->req_sdp_ip_addr, &flow_description->req_sdp_port, &flow_description->rpl_sdp_ip_addr, &flow_description->rpl_sdp_port, &flow_description->rpl_sdp_transport, &flow_description->req_sdp_raw_stream, &flow_description->rpl_sdp_raw_stream, flow_description->direction, AVP_EPC_Flow_Usage_No_Information); } flow_description = flow_description->next; add_flow = 1; } return 0; } /** Helper function for adding media component AVPs for each SDP stream*/ int add_media_components(AAAMessage* aar, struct sip_msg *req, struct sip_msg *rpl, enum dialog_direction direction, AAASession* auth) { int sdp_session_num; int sdp_stream_num; sdp_session_cell_t* req_sdp_session, *rpl_sdp_session; sdp_stream_cell_t* req_sdp_stream, *rpl_sdp_stream; int add_flow = 1; str ttag = {0, 0}; str ftag = {0, 0}; int request_originated_from_callee = 0; str ipA, ipB, portA, portB; rx_authsessiondata_t* p_session_data = 0; p_session_data = (rx_authsessiondata_t*) auth->u.auth.generic_data; if (!req || !rpl) { goto error; } if (parse_sdp(req) < 0) { LM_ERR("Unable to parse req SDP\n"); goto error; } if (parse_sdp(rpl) < 0) { LM_ERR("Unable to parse res SDP\n"); goto error; } sdp_session_num = 0; //Loop through req sessions and streams and get corresponding rpl sessions and streams and populate avps for (;;) { //we only cater for one session at the moment: TDOD: extend if (sdp_session_num > 0) { break; } req_sdp_session = get_sdp_session(req, sdp_session_num); rpl_sdp_session = get_sdp_session(rpl, sdp_session_num); if (!req_sdp_session || !rpl_sdp_session) { if (!req_sdp_session) LM_ERR("Missing SDP session information from req\n"); if (!rpl_sdp_session) LM_ERR("Missing SDP session information from rpl\n"); goto error; } sdp_stream_num = 0; for (;;) { req_sdp_stream = get_sdp_stream(req, sdp_session_num, sdp_stream_num); rpl_sdp_stream = get_sdp_stream(rpl, sdp_session_num, sdp_stream_num); if (!req_sdp_stream || !rpl_sdp_stream) { //LM_ERR("Missing SDP stream information\n"); break; } //is this a stream to add to AAR. if (req_sdp_stream->is_rtp) { //check if the src or dst port is 0 and if so then don't add to rx int intportA = atoi(req_sdp_stream->port.s); int intportB = atoi(rpl_sdp_stream->port.s); if(intportA != 0 && intportB != 0){ if(!authorize_video_flow) { if (strncmp(req_sdp_stream->media.s, "video", 5) == 0) { add_flow = 0; } } if(add_flow) { if (cscf_get_to_tag(rpl, &ttag) && cscf_get_from_tag(rpl, &ftag)) { LM_DBG("Original ftag [%.*s] ttag [%.*s]. Current ftag [%.*s] ttag [%.*s]\n", p_session_data->ftag.len, p_session_data->ftag.s, p_session_data->ttag.len, p_session_data->ttag.s, ftag.len, ftag.s, ttag.len, ttag.s); if (!(strncmp(p_session_data->ttag.s, ttag.s, p_session_data->ttag.len) == 0 && strncmp(p_session_data->ftag.s, ftag.s, p_session_data->ftag.len) == 0)) { LM_DBG("ftag and ttag of this response do not match initial response so this request came from callee\n"); request_originated_from_callee = 1; } } else { LM_ERR("Couldn't retrieve ftag so assume this request originated from caller\n"); } if (request_originated_from_callee) { LM_DBG("Request originated from callee so IPs are reversed\n"); ipA = rpl_sdp_session->ip_addr; portA = rpl_sdp_stream->port; ipB = req_sdp_session->ip_addr; portB = req_sdp_stream->port; } else { ipA = req_sdp_session->ip_addr; portA = req_sdp_stream->port; ipB = rpl_sdp_session->ip_addr; portB = rpl_sdp_stream->port; } if (ipA.len <= 0) { LM_DBG("Request SDP connection IP could not be retrieved, so we use SDP 1st stream IP\n"); if (request_originated_from_callee) { LM_DBG("Request originated from callee so IPs are reversed\n"); ipA = rpl_sdp_stream->ip_addr; portA = rpl_sdp_stream->port; } else { ipA = req_sdp_stream->ip_addr; portA = req_sdp_stream->port; } if (ipA.len <= 0) { LM_ERR("Requested SDP IP information could not be retrieved\n"); goto error; } } if (ipB.len <= 0) { LM_DBG("Reply SDP connection IP could not be retrieved, so we use SDP 1st stream IP\n"); if (request_originated_from_callee) { LM_DBG("Request originated from callee so IPs are reversed\n"); ipB = req_sdp_stream->ip_addr; portB = req_sdp_stream->port; } else { ipB = rpl_sdp_stream->ip_addr; portB = rpl_sdp_stream->port; } if (ipB.len <= 0) { LM_ERR("Request SDP IP information could not be retrieved\n"); goto error; } } //add this to auth session data add_flow_description((rx_authsessiondata_t*) auth->u.auth.generic_data, sdp_stream_num + 1, &req_sdp_stream->media, &ipA, &portA, &ipB, &portB, &rpl_sdp_stream->transport, &req_sdp_stream->raw_stream, &rpl_sdp_stream->raw_stream, direction, 0 /*This is a new mcd, we are not setting it as active*/); rx_add_media_component_description_avp(aar, sdp_stream_num + 1, &req_sdp_stream->media, &ipA, &portA, &ipB, &portB, &rpl_sdp_stream->transport, &req_sdp_stream->raw_stream, &rpl_sdp_stream->raw_stream, direction, AVP_EPC_Flow_Usage_No_Information); } add_flow = 1; } } sdp_stream_num++; } sdp_session_num++; } free_sdp((sdp_info_t**) (void*) &req->body); free_sdp((sdp_info_t**) (void*) &rpl->body); return 1; error: return 0; } /** * Sends the Authorization Authentication Request - specifically this is an asynchronous AAR sent if another update adding video has failed so we need to remove video */ int rx_send_aar_update_no_video(AAASession* auth) { AAAMessage* aar = 0; str identifier; int identifier_type; AAA_AVP* avp = 0; char x[4]; int ret = 0; str recv_ip; uint16_t ip_version; //we get ip and identifier for the auth session data rx_authsessiondata_t* p_session_data = 0; p_session_data = (rx_authsessiondata_t*) auth->u.auth.generic_data; identifier = p_session_data->identifier; identifier_type = p_session_data->identifier_type; recv_ip = p_session_data->ip; ip_version = p_session_data->ip_version; aar = cdpb.AAACreateRequest(IMS_Rx, IMS_AAR, Flag_Proxyable, auth); LM_DBG("Sending AAR update to remove a video bearer\n"); show_callsessiondata(p_session_data); if (!aar) goto error; /*Adding AVPs*/ LM_DBG("Adding auth app id AVP...\n"); /* Add Auth-Application-Id AVP */ if (!rx_add_auth_application_id_avp(aar, IMS_Rx)) goto error; if (!rx_add_vendor_specific_application_id_group(aar, IMS_vendor_id_3GPP, IMS_Rx)) goto error; LM_DBG("Adding dest realm if not there already...\n"); /* Add Destination-Realm AVP, if not already there */ avp = cdpb.AAAFindMatchingAVP(aar, aar->avpList.head, AVP_Destination_Realm, 0, AAA_FORWARD_SEARCH); if (!avp) { str realm = rx_dest_realm; if (realm.len && !rx_add_destination_realm_avp(aar, realm)) goto error; } LM_DBG("Adding AF App identifier...\n"); /* Add AF-Application-Identifier AVP */ str af_id = {0, 0}; af_id = IMS_Serv_AVP_val; if (!rx_add_avp(aar, af_id.s, af_id.len, AVP_IMS_AF_Application_Identifier, AAA_AVP_FLAG_MANDATORY, IMS_vendor_id_3GPP, AVP_DUPLICATE_DATA, __FUNCTION__)) goto error; LM_DBG("Adding service info status...\n"); /* Add Service-Info-Status AVP, if prelimiary * by default(when absent): final status is considered*/ set_4bytes(x, AVP_EPC_Service_Info_Status_Preliminary_Service_Information); if (!rx_add_avp(aar, x, 4, AVP_IMS_Service_Info_Status, AAA_AVP_FLAG_MANDATORY, IMS_vendor_id_3GPP, AVP_DUPLICATE_DATA, __FUNCTION__)) goto error; /* Add Auth lifetime AVP */LM_DBG("auth_lifetime %u\n", rx_auth_expiry); //TODO check why this is 0 all the time if (rx_auth_expiry) { set_4bytes(x, rx_auth_expiry); if (!rx_add_avp(aar, x, 4, AVP_Authorization_Lifetime, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__)) goto error; } LM_DBG("Adding subscription id...\n"); rx_add_subscription_id_avp(aar, identifier, identifier_type); LM_DBG("Adding reservation priority...\n"); /* Add Reservation Priority AVP*/ set_4bytes(x, 0); if (!rx_add_avp(aar, x, 4, AVP_ETSI_Reservation_Priority, AAA_AVP_FLAG_VENDOR_SPECIFIC, IMS_vendor_id_ETSI, AVP_DUPLICATE_DATA, __FUNCTION__)) goto error; LM_DBG("Adding media component...\n"); //Note we add this AVP first as it gets the IP address which we need to create the auth session //Could and maybe should have a separate method that retrieves the IP from SDP - TODO /*---------- 2. Create and add Media-Component-Description AVP ----------*/ /* * See 3GPP TS29214 * * <Media-Component-Description> = {Media-Component-Number} * [Media-Sub-Component] * [AF-Application-Identifier] * [Media-Type] * [Max-Requested-Bandwidth-UL] * [Max-Requested-Bandwidth-DL] * [Flow-Status] * [Reservation-Priority] (Not used yet) * [RS-Bandwidth] * [RR-Bandwidth] * *[Codec-Data] */ add_media_components_using_current_flow_description(aar, p_session_data); LM_DBG("Adding framed ip address [%.*s]\n", recv_ip.len, recv_ip.s); /* Add Framed IP address AVP*/ if (!rx_add_framed_ip_avp(&aar->avpList, recv_ip, ip_version)) { LM_ERR("Unable to add framed IP AVP\n"); goto error; } LM_DBG("Unlocking AAA session...\n"); if (auth) cdpb.AAASessionsUnlock(auth->hash); LM_DBG("sending AAR to PCRF\n"); if (rx_forced_peer.len) ret = cdpb.AAASendMessageToPeer(aar, &rx_forced_peer, NULL, NULL); else ret = cdpb.AAASendMessage(aar, NULL, NULL); return ret; error: LM_ERR("unexpected error\n"); if (aar) cdpb.AAAFreeMessage(&aar); if (auth) { cdpb.AAASessionsUnlock(auth->hash); cdpb.AAADropAuthSession(auth); auth = 0; } return ret; } /** * Sends the Authorization Authentication Request. * @param req - SIP request * @param res - SIP response * @param direction - 0/o/orig for originating side, 1/t/term for terminating side * @param rx_auth_data - the returned rx auth data * @returns AAA message or NULL on error */ int rx_send_aar(struct sip_msg *req, struct sip_msg *res, AAASession* auth, char* direction, saved_transaction_t* saved_t_data) { AAAMessage* aar = 0; str identifier; int identifier_type; AAA_AVP* avp = 0; char x[4]; int ret = 0; str ip; uint16_t ip_version; //we get ip and identifier for the auth session data rx_authsessiondata_t* p_session_data = 0; p_session_data = (rx_authsessiondata_t*) auth->u.auth.generic_data; identifier = p_session_data->identifier; identifier_type = p_session_data->identifier_type; ip = p_session_data->ip; ip_version = p_session_data->ip_version; /* find direction for AAR (orig/term) */ //need this to add the media component details enum dialog_direction dlg_direction = get_dialog_direction(direction); if (dlg_direction == DLG_MOBILE_UNKNOWN) { LM_DBG("Asked to send AAR for unknown direction.....Aborting...\n"); goto error; } aar = cdpb.AAACreateRequest(IMS_Rx, IMS_AAR, Flag_Proxyable, auth); LM_DBG("Created aar request...\n"); if (!aar) goto error; /*Adding AVPs*/ LM_DBG("Adding auth app id AVP...\n"); /* Add Auth-Application-Id AVP */ if (!rx_add_auth_application_id_avp(aar, IMS_Rx)) goto error; if (!rx_add_vendor_specific_application_id_group(aar, IMS_vendor_id_3GPP, IMS_Rx)) goto error; LM_DBG("Adding dest realm if not there already...\n"); /* Add Destination-Realm AVP, if not already there */ avp = cdpb.AAAFindMatchingAVP(aar, aar->avpList.head, AVP_Destination_Realm, 0, AAA_FORWARD_SEARCH); if (!avp) { str realm = rx_dest_realm; if (realm.len && !rx_add_destination_realm_avp(aar, realm)) goto error; } LM_DBG("Adding AF App identifier...\n"); /* Add AF-Application-Identifier AVP */ str af_id = {0, 0}; af_id = IMS_Serv_AVP_val; if (!rx_add_avp(aar, af_id.s, af_id.len, AVP_IMS_AF_Application_Identifier, AAA_AVP_FLAG_MANDATORY, IMS_vendor_id_3GPP, AVP_DUPLICATE_DATA, __FUNCTION__)) goto error; LM_DBG("Adding service info status...\n"); /* Add Service-Info-Status AVP, if prelimiary * by default(when absent): final status is considered*/ if (!res) { set_4bytes(x, AVP_EPC_Service_Info_Status_Preliminary_Service_Information); if (!rx_add_avp(aar, x, 4, AVP_IMS_Service_Info_Status, AAA_AVP_FLAG_MANDATORY, IMS_vendor_id_3GPP, AVP_DUPLICATE_DATA, __FUNCTION__)) goto error; } /* Add Auth lifetime AVP */LM_DBG("auth_lifetime %u\n", rx_auth_expiry); //TODO check why this is 0 all the time if (rx_auth_expiry) { set_4bytes(x, rx_auth_expiry); if (!rx_add_avp(aar, x, 4, AVP_Authorization_Lifetime, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__)) goto error; } LM_DBG("Adding subscription id...\n"); rx_add_subscription_id_avp(aar, identifier, identifier_type); LM_DBG("Adding reservation priority...\n"); /* Add Reservation Priority AVP*/ set_4bytes(x, 0); if (!rx_add_avp(aar, x, 4, AVP_ETSI_Reservation_Priority, AAA_AVP_FLAG_VENDOR_SPECIFIC, IMS_vendor_id_ETSI, AVP_DUPLICATE_DATA, __FUNCTION__)) goto error; LM_DBG("Adding media component...\n"); //Note we add this AVP first as it gets the IP address which we need to create the auth session //Could and maybe should have a separate method that retrieves the IP from SDP - TODO /*---------- 2. Create and add Media-Component-Description AVP ----------*/ /* * See 3GPP TS29214 * * <Media-Component-Description> = {Media-Component-Number} * [Media-Sub-Component] * [AF-Application-Identifier] * [Media-Type] * [Max-Requested-Bandwidth-UL] * [Max-Requested-Bandwidth-DL] * [Flow-Status] * [Reservation-Priority] (Not used yet) * [RS-Bandwidth] * [RR-Bandwidth] * *[Codec-Data] */ add_media_components(aar, req, res, dlg_direction, auth); LM_DBG("Adding framed ip address [%.*s]\n", ip.len, ip.s); /* Add Framed IP address AVP*/ if (!rx_add_framed_ip_avp(&aar->avpList, ip, ip_version)) { LM_ERR("Unable to add framed IP AVP\n"); goto error; } /* Add specific action AVP's */ rx_add_specific_action_avp(aar, 1); // CHARGING_CORRELATION_EXCHANGE rx_add_specific_action_avp(aar, 2); // INDICATION_OF_LOSS_OF_BEARER rx_add_specific_action_avp(aar, 3); // INDICATION_RECOVERY_OF_BEARER rx_add_specific_action_avp(aar, 4); // INDICATION_RELEASE_OF_BEARER rx_add_specific_action_avp(aar, 5); // INDICATION_ESTABLISHMENT_OF_BEARER (now void) rx_add_specific_action_avp(aar, 6); // IP-CAN_CHANGE rx_add_specific_action_avp(aar, 12); // ACCESS_NETWORK_INFO_REPORT show_callsessiondata(p_session_data); LM_DBG("Unlocking AAA session...\n"); if (auth) cdpb.AAASessionsUnlock(auth->hash); LM_DBG("sending AAR to PCRF\n"); if (rx_forced_peer.len) ret = cdpb.AAASendMessageToPeer(aar, &rx_forced_peer, (void*) async_aar_callback, (void*) saved_t_data); else ret = cdpb.AAASendMessage(aar, (void*) async_aar_callback, (void*) saved_t_data); return ret; error: LM_ERR("unexpected error\n"); if (aar) cdpb.AAAFreeMessage(&aar); if (auth) { cdpb.AAASessionsUnlock(auth->hash); cdpb.AAADropAuthSession(auth); auth = 0; } return ret; } /** * Sends the Authorization Authentication Request for Register messages * @param req - SIP Register msg * @param rx_auth_data - the returned rx auth data * @param ip - ip address extracted from contact to register * @param ip_version - AF_INET or AF_INET6 * @returns int >0 if sent AAR successfully, otherwise 0 */ int rx_send_aar_register(struct sip_msg *msg, AAASession* auth, saved_transaction_local_t* saved_t_data) { AAAMessage* aar = 0; int ret = 0; AAA_AVP* avp = 0; char x[4]; str identifier; str ip; uint16_t ip_version; str via_host; //we get ip and identifier for the auth session data rx_authsessiondata_t* p_session_data = 0; p_session_data = (rx_authsessiondata_t*) auth->u.auth.generic_data; identifier = p_session_data->identifier; ip = p_session_data->ip; ip_version = p_session_data->ip_version; LM_DBG("Send AAR register\n"); aar = cdpb.AAACreateRequest(IMS_Rx, IMS_AAR, Flag_Proxyable, auth); if (!aar) goto error; /*Add AVPs*/ /* Add Auth-Application-Id AVP */ if (!rx_add_auth_application_id_avp(aar, IMS_Rx)) goto error; if (!rx_add_vendor_specific_application_id_group(aar, IMS_vendor_id_3GPP, IMS_Rx)) goto error; /* Add Destination-Realm AVP, if not already there */ avp = cdpb.AAAFindMatchingAVP(aar, aar->avpList.head, AVP_Destination_Realm, 0, AAA_FORWARD_SEARCH); if (!avp) { str realm = rx_dest_realm; if (realm.len && !rx_add_destination_realm_avp(aar, realm)) goto error; } /* Add Subscription ID AVP*/ identifier = cscf_get_public_identity(msg); int identifier_type = AVP_Subscription_Id_Type_SIP_URI; //we only do IMPU now rx_add_subscription_id_avp(aar, identifier, identifier_type); /* Create flow description for AF-Signaling */ //add this to auth session data str raw_stream; raw_stream.s = 0; raw_stream.len = 0; char c_port_from[10]; str port_from; port_from.len = snprintf(c_port_from, 10, "%u", saved_t_data->via_port); port_from.s = c_port_from; char c_port_to[10]; str port_to; port_to.len = snprintf(c_port_to, 10, "%u", saved_t_data->recv_port); port_to.s = c_port_to; via_host.len = saved_t_data->via_host.len; via_host.s = saved_t_data->via_host.s; if (ip_version == AF_INET6 && via_host.len && via_host.s[0]=='[') { /* skip over [ ] */ if (via_host.s[via_host.len - 1]!=']') { LM_ERR("Invalid IPv6 format %.*s\n", via_host.len, via_host.s); goto error; } via_host.s++; via_host.len -= 2; } //rx_add_media_component_description_avp_register(aar); /* Add media component description avp for register*/ rx_add_media_component_description_avp(aar, 1, &component_media_type, &via_host, &port_from, ip_version == AF_INET ? &af_signaling_ip : &af_signaling_ip6, &port_to, &flow_protocol, &raw_stream, &raw_stream, DLG_MOBILE_REGISTER, AVP_EPC_Flow_Usage_AF_Signaling); /* Add specific action AVP's */ rx_add_specific_action_avp(aar, 1); // CHARGING_CORRELATION_EXCHANGE rx_add_specific_action_avp(aar, 2); // INDICATION_OF_LOSS_OF_BEARER rx_add_specific_action_avp(aar, 3); // INDICATION_RECOVERY_OF_BEARER rx_add_specific_action_avp(aar, 4); // INDICATION_RELEASE_OF_BEARER rx_add_specific_action_avp(aar, 5); // INDICATION_ESTABLISHMENT_OF_BEARER (now void) rx_add_specific_action_avp(aar, 6); // IP-CAN_CHANGE rx_add_specific_action_avp(aar, 12); // ACCESS_NETWORK_INFO_REPORT /* Add Framed IP address AVP*/ if (!rx_add_framed_ip_avp(&aar->avpList, ip, ip_version)) { LM_ERR("Unable to add framed IP AVP\n"); goto error; } /* Add Auth lifetime AVP */LM_DBG("auth_lifetime %u\n", rx_auth_expiry); //TODO check why this is 0 all the time if (rx_auth_expiry) { set_4bytes(x, rx_auth_expiry); if (!rx_add_avp(aar, x, 4, AVP_Authorization_Lifetime, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__)) goto error; } if (auth) cdpb.AAASessionsUnlock(auth->hash); LM_DBG("sending AAR to PCRF\n"); if (rx_forced_peer.len) ret = cdpb.AAASendMessageToPeer(aar, &rx_forced_peer, (void*) async_aar_reg_callback, (void*) saved_t_data); else ret = cdpb.AAASendMessage(aar, (void*) async_aar_reg_callback, (void*) saved_t_data); return ret; error: LM_ERR("unexpected error\n"); if (aar) cdpb.AAAFreeMessage(&aar); if (auth) { cdpb.AAASessionsUnlock(auth->hash); cdpb.AAADropAuthSession(auth); auth = 0; } return ret; } enum dialog_direction get_dialog_direction(char *direction) { if (!direction) { LM_CRIT("Unknown direction NULL"); return DLG_MOBILE_UNKNOWN; } switch (direction[0]) { case 'o': case 'O': case '0': return DLG_MOBILE_ORIGINATING; case 't': case 'T': case '1': return DLG_MOBILE_TERMINATING; default: LM_CRIT("Unknown direction %s", direction); return DLG_MOBILE_UNKNOWN; } } void free_saved_transaction_global_data(saved_transaction_t* data) { if (!data) return; if (data->callid.s && data->callid.len) { shm_free(data->callid.s); data->callid.len = 0; } if (data->ftag.s && data->ftag.len) { shm_free(data->ftag.s); data->ftag.len = 0; } if (data->ttag.s && data->ttag.len) { shm_free(data->ttag.s); data->ttag.len = 0; } if (data->lock) { lock_dealloc(data->lock); lock_destroy(data->lock); } shm_free(data); } void free_saved_transaction_data(saved_transaction_local_t* data) { if (!data) return; shm_free(data); }