src/modules/ims_qos/rx_aar.c
06fb17e5
 /*
  * $Id$
  *
  * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com
  * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com
d99753e4
  *
06fb17e5
  * 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.
d99753e4
  * ported/maintained/improved by
06fb17e5
  * Jason Penton (jason(dot)penton(at)smilecoms.com and
d99753e4
  * Richard Good (richard(dot)good(at)smilecoms.com) as part of an
06fb17e5
  * effort to add full IMS support to Kamailio/SR using a new and
  * improved architecture
d99753e4
  *
06fb17e5
  * NB: Alot of this code was originally part of OpenIMSCore,
d99753e4
  * FhG Fokus.
06fb17e5
  * Copyright (C) 2004-2006 FhG Fokus
d99753e4
  * Thanks for great work! This is an effort to
06fb17e5
  * 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.
  *
d99753e4
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
d99753e4
  *
06fb17e5
  *
  *
  * History:
  * --------
  *  2011-02-02  initial version (jason.penton)
  */
 
cf83221d
 #include "../../core/mem/shm_mem.h"
 #include "../../core/parser/sdp/sdp.h"
318c3cef
 #include "../cdp_avp/cdp_avp_mod.h"
06fb17e5
 
26639561
 #include "../../modules/ims_dialog/dlg_load.h"
06fb17e5
 #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"
 
68fa5058
 #include "ims_qos_mod.h"
06fb17e5
 
324e458a
 #include "../../lib/ims/useful_defs.h"
caf1dbe6
 #include "ims_qos_stats.h"
 
 
06fb17e5
 #define macro_name(_rc)	#_rc
 
 //extern struct tm_binds tmb;
df5521fc
 extern usrloc_api_t ul;
06fb17e5
 
caf1dbe6
 extern struct ims_qos_counters_h ims_qos_cnts_h;
 
0db2ee84
 extern int authorize_video_flow;
 
d99753e4
 extern str af_signaling_ip;
5a85767e
 extern str af_signaling_ip6;
 extern str component_media_type;
 extern str flow_protocol;
ab3d42c4
 
06fb17e5
 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};
 
324e458a
 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;
3d5111e3
     rx_authsessiondata_t* p_session_data = 0;
     AAASession *auth = 0;
 
324e458a
     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");
caf1dbe6
         counter_inc(ims_qos_cnts_h.media_aar_timeouts);
324e458a
         goto error;
     }
     if (!aaa) {
         LM_ERR("Error sending message via CDP\n");
         goto error;
     }
 
caf1dbe6
     counter_inc(ims_qos_cnts_h.media_aars);
     counter_add(ims_qos_cnts_h.media_aar_response_time, elapsed_msecs);
51578d83
     counter_inc(ims_qos_cnts_h.media_aar_replies_received);
324e458a
 
     /* 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);
3d5111e3
         counter_inc(ims_qos_cnts_h.successful_media_aars);
 
         LM_DBG("Auth session ID [%.*s]", aaa->sessionId->data.len, aaa->sessionId->data.s);
2e821613
 
e5c9f006
 	if(!data->aar_update) {
3d5111e3
             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);
bb2fcf8e
 			counter_inc(ims_qos_cnts_h.media_rx_sessions);
3d5111e3
 
             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);
2768f8ce
             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);
39286f99
         } else {
             unref_dlg(data->dlg, 1);
3d5111e3
         }
324e458a
         result = CSCF_RETURN_TRUE;
     } else {
         LM_DBG("Received negative reply from PCRF for AAR Request\n");
3d5111e3
         counter_inc(ims_qos_cnts_h.failed_media_aars);
324e458a
         //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) {
06fb17e5
     struct cell *t = 0;
     pcontact_t* pcontact;
     unsigned int cdp_result;
     struct pcontact_info ci;
     udomain_t* domain_t;
     int finalReply = 0;
2e821613
     AAASession *auth = 0;
     rx_authsessiondata_t* p_session_data = 0;
06fb17e5
     int result = CSCF_RETURN_ERROR;
88a7b00c
     pcontact_info_t contact_info;
06fb17e5
 
     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");
caf1dbe6
         counter_inc(ims_qos_cnts_h.registration_aar_timeouts);
06fb17e5
         goto error;
     }
     if (!aaa) {
         LM_ERR("Error sending message via CDP\n");
         goto error;
     }
 
caf1dbe6
     counter_inc(ims_qos_cnts_h.registration_aars);
     counter_add(ims_qos_cnts_h.registration_aar_response_time, elapsed_msecs);
51578d83
     counter_inc(ims_qos_cnts_h.registration_aar_replies_received);
06fb17e5
 
     /* 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) {
caf1dbe6
         counter_inc(ims_qos_cnts_h.successful_registration_aars);
3d5111e3
         if (is_rereg) {
39ef519b
             LM_DBG("this is a re-registration, therefore we don't need to do anything except know that the subscription was successful\n");
324e458a
             result = CSCF_RETURN_TRUE;
             create_return_code(result);
             goto done;
         }
3d5111e3
         //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);
 
 
06fb17e5
         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");
 
88a7b00c
         ul.lock_udomain(domain_t, &local_data->via_host, local_data->via_port, local_data->via_proto);
998e8c15
 		memset(&contact_info, 0, sizeof(struct pcontact_info));
88a7b00c
         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);
3d5111e3
 
 
88a7b00c
         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;
3d5111e3
         contact_info.reg_state = PCONTACT_ANY;
 
45ebf8bd
         if (ul.get_pcontact(domain_t, &contact_info, &pcontact, 0) != 0) {
88a7b00c
             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);
06fb17e5
             goto error;
         }
 
         //at this point we have the contact
f9c336aa
         /*set the contact state to say we have successfully done ARR for register
          * and that we dont need to do it again
06fb17e5
          * 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");
88a7b00c
             ul.unlock_udomain(domain_t, &local_data->via_host, local_data->via_port, local_data->via_proto);
06fb17e5
             goto error;
         }
324e458a
         memset(&ci, 0, sizeof (struct pcontact_info));
06fb17e5
         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);
88a7b00c
         ul.unlock_udomain(domain_t, &local_data->via_host, local_data->via_port, local_data->via_proto);
06fb17e5
         result = CSCF_RETURN_TRUE;
     } else {
fce92c5e
         LM_DBG("Received negative reply from PCRF for AAR Request\n");
3d5111e3
         counter_inc(ims_qos_cnts_h.failed_registration_aars);
06fb17e5
         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;
 }
 
e5c9f006
 /** 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) {
3d5111e3
 
e5c9f006
     flow_description_t *flow_description;
0db2ee84
     int add_flow = 1;
3d5111e3
 
e5c9f006
     flow_description = p_session_data->first_current_flow_description;
     if(!flow_description) {
3d5111e3
         return -1;
e5c9f006
     }
     while (flow_description) {
3d5111e3
 
0db2ee84
 	if(!authorize_video_flow) {
3d5111e3
             if (strncmp(flow_description->media.s, "video", 5) == 0) {
                 add_flow = 0;
             }
         }
d99753e4
 
0db2ee84
 	if(add_flow) {
3d5111e3
             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,
bb8d1dfa
                     &flow_description->rpl_sdp_raw_stream, flow_description->direction, AVP_EPC_Flow_Usage_No_Information);
3d5111e3
         }
 
         flow_description = flow_description->next;
         add_flow = 1;
e5c9f006
     }
     return 0;
 }
 
 
 
06fb17e5
 /** Helper function for adding media component AVPs for each SDP stream*/
 int add_media_components(AAAMessage* aar, struct sip_msg *req,
e5c9f006
         struct sip_msg *rpl, enum dialog_direction direction, AAASession* auth) {
06fb17e5
     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;
0db2ee84
     int add_flow = 1;
938ac5e7
 	str ttag = {0, 0};
 	str ftag = {0, 0};
 	int request_originated_from_callee = 0;  
97e94d7d
 	str ipA, ipB, portA, portB;
938ac5e7
 	
 	rx_authsessiondata_t* p_session_data = 0;
     p_session_data = (rx_authsessiondata_t*) auth->u.auth.generic_data;
 	
 	if (!req || !rpl) {
552cab38
 			goto error;
06fb17e5
     }
 
     if (parse_sdp(req) < 0) {
         LM_ERR("Unable to parse req SDP\n");
552cab38
         goto error;
06fb17e5
     }
 
     if (parse_sdp(rpl) < 0) {
         LM_ERR("Unable to parse res SDP\n");
552cab38
         goto error;
06fb17e5
     }
 
     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");
d99753e4
 
552cab38
 			goto error;
06fb17e5
         }
 
         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) {
 
3d5111e3
                 //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);
b4e8f3f4
 		if(intportA != 0 && intportB != 0){
0db2ee84
 			if(!authorize_video_flow) {
3d5111e3
                         if (strncmp(req_sdp_stream->media.s, "video", 5) == 0) {
                             add_flow = 0;
                         }
                     }
 
0db2ee84
 			if(add_flow) {
d99753e4
 
938ac5e7
 					
 						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;
97e94d7d
 							portA = rpl_sdp_stream->port;
 							
938ac5e7
 							ipB = req_sdp_session->ip_addr;
97e94d7d
 							portB = req_sdp_stream->port;
938ac5e7
 						} else {
 							ipA = req_sdp_session->ip_addr;
97e94d7d
 							portA = req_sdp_stream->port;
 							
 							
938ac5e7
 							ipB = rpl_sdp_session->ip_addr;
97e94d7d
 							portB = rpl_sdp_stream->port;
938ac5e7
 						}
 						
552cab38
 
 						if (ipA.len <= 0) {
938ac5e7
 								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;
97e94d7d
 									portA = rpl_sdp_stream->port;
938ac5e7
 								} else {
 									ipA = req_sdp_stream->ip_addr;
97e94d7d
 									portA = req_sdp_stream->port;
938ac5e7
 								}
 								
 								
552cab38
 								if (ipA.len <= 0) {
938ac5e7
 										LM_ERR("Requested SDP IP information could not be retrieved\n");
552cab38
 										goto error;
 								}
 						}
d99753e4
 
552cab38
 						if (ipB.len <= 0) {
938ac5e7
 								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;
97e94d7d
 									portB = req_sdp_stream->port;
938ac5e7
 								} else {
 									ipB = rpl_sdp_stream->ip_addr;
97e94d7d
 									portB = rpl_sdp_stream->port;
938ac5e7
 								}
 								
 								
552cab38
 								if (ipB.len <= 0) {
938ac5e7
 										LM_ERR("Request SDP IP information could not be retrieved\n");
552cab38
 										goto error;
 								}
 						}
d99753e4
 
552cab38
 						//add this to auth session data
3d5111e3
                         add_flow_description((rx_authsessiondata_t*) auth->u.auth.generic_data, sdp_stream_num + 1,
552cab38
                                 &req_sdp_stream->media, &ipA,
97e94d7d
                                 &portA, &ipB,
                                 &portB, &rpl_sdp_stream->transport,
3d5111e3
                                 &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,
552cab38
                                 &req_sdp_stream->media, &ipA,
97e94d7d
                                 &portA, &ipB,
                                 &portB, &rpl_sdp_stream->transport,
3d5111e3
                                 &req_sdp_stream->raw_stream,
bb8d1dfa
                                 &rpl_sdp_stream->raw_stream, direction, AVP_EPC_Flow_Usage_No_Information);
3d5111e3
                     }
                     add_flow = 1;
                 }
06fb17e5
             }
             sdp_stream_num++;
         }
         sdp_session_num++;
     }
 
     free_sdp((sdp_info_t**) (void*) &req->body);
     free_sdp((sdp_info_t**) (void*) &rpl->body);
 
552cab38
 	return 1;
d99753e4
 
552cab38
 	error:
d99753e4
 
06fb17e5
     return 0;
 }
 
e5c9f006
 /**
  * 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;
3d5111e3
 
e5c9f006
     str identifier;
     int identifier_type;
 
 
     AAA_AVP* avp = 0;
     char x[4];
     int ret = 0;
 
88a7b00c
     str recv_ip;
e5c9f006
     uint16_t ip_version;
 
d99753e4
     //we get ip and identifier for the auth session data
e5c9f006
     rx_authsessiondata_t* p_session_data = 0;
     p_session_data = (rx_authsessiondata_t*) auth->u.auth.generic_data;
     identifier = p_session_data->identifier;
3dda6086
     identifier_type = p_session_data->identifier_type;
88a7b00c
     recv_ip = p_session_data->ip;
e5c9f006
     ip_version = p_session_data->ip_version;
3d5111e3
 
e5c9f006
     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*/
3d5111e3
 
e5c9f006
     set_4bytes(x,
3d5111e3
             AVP_EPC_Service_Info_Status_Preliminary_Service_Information);
e5c9f006
     if (!rx_add_avp(aar, x, 4, AVP_IMS_Service_Info_Status,
3d5111e3
             AAA_AVP_FLAG_MANDATORY, IMS_vendor_id_3GPP, AVP_DUPLICATE_DATA,
             __FUNCTION__))
         goto error;
e5c9f006
 
     /* 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);
 
88a7b00c
     LM_DBG("Adding framed ip address [%.*s]\n", recv_ip.len, recv_ip.s);
e5c9f006
     /* Add Framed IP address AVP*/
88a7b00c
     if (!rx_add_framed_ip_avp(&aar->avpList, recv_ip, ip_version)) {
e5c9f006
         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;
 }
 
 
 
06fb17e5
 /**
  * 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
  */
 
324e458a
 int rx_send_aar(struct sip_msg *req, struct sip_msg *res,
         AAASession* auth, char* direction, saved_transaction_t* saved_t_data) {
 
06fb17e5
     AAAMessage* aar = 0;
3d5111e3
 
53245937
     str identifier;
     int identifier_type;
324e458a
 
 
06fb17e5
     AAA_AVP* avp = 0;
     char x[4];
324e458a
     int ret = 0;
06fb17e5
 
     str ip;
     uint16_t ip_version;
 
d99753e4
     //we get ip and identifier for the auth session data
72ddccb2
     rx_authsessiondata_t* p_session_data = 0;
     p_session_data = (rx_authsessiondata_t*) auth->u.auth.generic_data;
     identifier = p_session_data->identifier;
3dda6086
     identifier_type = p_session_data->identifier_type;
72ddccb2
     ip = p_session_data->ip;
e5c9f006
     ip_version = p_session_data->ip_version;
3d5111e3
 
06fb17e5
     /* 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");
1d6b1d87
 
06fb17e5
     rx_add_subscription_id_avp(aar, identifier, identifier_type);
1d6b1d87
 
06fb17e5
 
     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]
      */
 
e5c9f006
     add_media_components(aar, req, res, dlg_direction, auth);
06fb17e5
 
     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;
     }
3d5111e3
 
1aa95fbc
     /* 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
 
e5c9f006
     show_callsessiondata(p_session_data);
3d5111e3
 
06fb17e5
     LM_DBG("Unlocking AAA session...\n");
 
     if (auth)
         cdpb.AAASessionsUnlock(auth->hash);
 
     LM_DBG("sending AAR to PCRF\n");
     if (rx_forced_peer.len)
324e458a
         ret = cdpb.AAASendMessageToPeer(aar, &rx_forced_peer,
             (void*) async_aar_callback, (void*) saved_t_data);
06fb17e5
     else
324e458a
         ret = cdpb.AAASendMessage(aar, (void*) async_aar_callback,
             (void*) saved_t_data);
06fb17e5
 
324e458a
     return ret;
 
06fb17e5
 error:
     LM_ERR("unexpected error\n");
     if (aar)
         cdpb.AAAFreeMessage(&aar);
     if (auth) {
         cdpb.AAASessionsUnlock(auth->hash);
         cdpb.AAADropAuthSession(auth);
         auth = 0;
     }
324e458a
     return ret;
06fb17e5
 }
 
 /**
  * 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
  */
324e458a
 
fce92c5e
 int rx_send_aar_register(struct sip_msg *msg, AAASession* auth, saved_transaction_local_t* saved_t_data) {
06fb17e5
     AAAMessage* aar = 0;
     int ret = 0;
     AAA_AVP* avp = 0;
     char x[4];
53245937
     str identifier;
3d5111e3
 
fce92c5e
     str ip;
     uint16_t ip_version;
5a85767e
     str via_host;
fce92c5e
 
d99753e4
     //we get ip and identifier for the auth session data
fce92c5e
     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;
06fb17e5
 
     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*/
3d5111e3
 
53245937
     identifier = cscf_get_public_identity(msg);
3d5111e3
 
06fb17e5
     int identifier_type = AVP_Subscription_Id_Type_SIP_URI; //we only do IMPU now
     rx_add_subscription_id_avp(aar, identifier, identifier_type);
 
ab3d42c4
     /* Create flow description for AF-Signaling */
     //add this to auth session data
     str raw_stream;
     raw_stream.s = 0;
     raw_stream.len = 0;
 
29beb58a
     char c_port_from[10];
ab3d42c4
     str port_from;
29beb58a
     port_from.len = snprintf(c_port_from, 10, "%u", saved_t_data->via_port);
ab3d42c4
     port_from.s = c_port_from;
 
29beb58a
     char c_port_to[10];
ab3d42c4
     str port_to;
29beb58a
     port_to.len = snprintf(c_port_to, 10, "%u", saved_t_data->recv_port);
ab3d42c4
     port_to.s = c_port_to;
 
5a85767e
     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;
     }
ab3d42c4
 
bb8d1dfa
     //rx_add_media_component_description_avp_register(aar);
06fb17e5
     /* Add media component description avp for register*/
ab3d42c4
     rx_add_media_component_description_avp(aar, 1,
5a85767e
                &component_media_type, &via_host,
                &port_from, ip_version == AF_INET ? &af_signaling_ip : &af_signaling_ip6,
                &port_to, &flow_protocol,
ab3d42c4
                &raw_stream,
bb8d1dfa
                &raw_stream, DLG_MOBILE_REGISTER, AVP_EPC_Flow_Usage_AF_Signaling);
06fb17e5
 
1aa95fbc
     /* 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
 
06fb17e5
     /* Add Framed IP address AVP*/
fce92c5e
     if (!rx_add_framed_ip_avp(&aar->avpList, ip, ip_version)) {
06fb17e5
         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,
324e458a
             (void*) async_aar_reg_callback, (void*) saved_t_data);
06fb17e5
     else
324e458a
         ret = cdpb.AAASendMessage(aar, (void*) async_aar_reg_callback,
06fb17e5
             (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;
324e458a
     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);
     }
06fb17e5
     shm_free(data);
 }
 
 void free_saved_transaction_data(saved_transaction_local_t* data) {
     if (!data)
         return;
     shm_free(data);
 }