void send_ccr_stop_with_param(struct ro_session *ro_session, unsigned int code, str* reason) { AAASession * auth = 0; Ro_CCR_t * ro_ccr_data = 0; AAAMessage * ccr = 0; ims_information_t *ims_info = 0; int32_t acc_record_type; subscription_id_t subscr; time_stamps_t *time_stamps; long used = 0; str user_name = {0, 0}; int ret = 0; time_t stop_time; time_t actual_time_micros; int actual_time_seconds; stop_time = get_current_time_micro(); if (ro_session->start_time == 0) actual_time_micros = 0; else actual_time_micros = stop_time - ro_session->start_time; actual_time_seconds = (actual_time_micros + (1000000 - 1)) / (float) 1000000; if (ro_session->event_type != pending) { used = rint((stop_time - ro_session->last_event_timestamp) / (float) 1000000); LM_DBG("Final used number of seconds for session is %ld\n", used); } LM_DBG("Call started at %ld and ended at %ld and lasted %d seconds and so far we have billed for %ld seconds\n", ro_session->start_time, stop_time, actual_time_seconds, ro_session->billed + used); if (ro_session->billed + used < actual_time_seconds) { LM_DBG("Making adjustment by adding %ld seconds\n", actual_time_seconds - (ro_session->billed + used)); used += actual_time_seconds - (ro_session->billed + used); } counter_add(ims_charging_cnts_h.billed_secs, (int)used); event_type_t *event_type; str sip_method = str_init("dummy"); str sip_event = str_init("dummy"); time_t req_timestamp; event_type = new_event_type(&sip_method, &sip_event, 0); LM_DBG("Sending stop CCR request for (usage) [%i] seconds for user [%.*s] using session id [%.*s] active rating group [%d] active service identifier [%d] incoming_trunk_id [%.*s] outgoing_trunk_id [%.*s] pani [%.*s]\n", (int)used, ro_session->asserted_identity.len, ro_session->asserted_identity.s, ro_session->ro_session_id.len, ro_session->ro_session_id.s, ro_session->rating_group, ro_session->service_identifier, ro_session->incoming_trunk_id.len, ro_session->incoming_trunk_id.s, ro_session->outgoing_trunk_id.len, ro_session->outgoing_trunk_id.s, ro_session->pani.len, ro_session->pani.s); req_timestamp = get_current_time_micro(); if (!(time_stamps = new_time_stamps(&req_timestamp, NULL, NULL, NULL))) goto error0; if (!(ims_info = new_ims_information(event_type, time_stamps, &ro_session->callid, &ro_session->callid, &ro_session->asserted_identity, &ro_session->called_asserted_identity, 0, 0, 0, ro_session->direction, &ro_session->incoming_trunk_id, &ro_session->outgoing_trunk_id, &ro_session->pani))) goto error0; event_type = 0; if (ro_session->direction == RO_ORIG_DIRECTION) { subscr.id = ro_session->asserted_identity; } else if (ro_session->direction == RO_TERM_DIRECTION) { subscr.id = ro_session->called_asserted_identity; } else { LM_CRIT("don't know what to do in unknown mode - should we even get here\n"); goto error0; } //getting subscription id type if (strncasecmp(subscr.id.s, "tel:", 4) == 0) { subscr.type = Subscription_Type_MSISDN; } else { subscr.type = Subscription_Type_IMPU; //default is END_USER_SIP_URI } user_name.s = subscr.id.s; user_name.len = subscr.id.len; acc_record_type = AAA_ACCT_STOP; ro_ccr_data = new_Ro_CCR(acc_record_type, &user_name, ims_info, &subscr); if (!ro_ccr_data) { LM_ERR("send_ccr_stop: no memory left for generic\n"); goto error0; } ims_info = 0; LM_DBG("Created Ro data\n"); auth = cdpb.AAAGetCCAccSession(ro_session->ro_session_id); if (!auth) { LM_DBG("Diameter Auth Session has timed out.... creating a new one.\n"); /* lets try and recreate this session */ auth = cdpb.AAAMakeSession(ro_session->auth_appid, ro_session->auth_session_type, ro_session->ro_session_id); //TODO: would like this session to last longer (see session timeout in cdp if (!auth) goto error1; } if (!(ccr = Ro_new_ccr(auth, ro_ccr_data))) goto error1; LM_DBG("Created new CCR\n"); if (!Ro_add_vendor_specific_appid(ccr, IMS_vendor_id_3GPP, IMS_Ro, 0)) { LM_ERR("Problem adding Vendor specific ID\n"); } ro_session->hop_by_hop += 1; if (!Ro_add_cc_request(ccr, RO_CC_STOP, ro_session->hop_by_hop)) { LM_ERR("Problem adding CC-Request data\n"); } if (!Ro_add_event_timestamp(ccr, time(NULL))) { LM_ERR("Problem adding Event-Timestamp data\n"); } if (!Ro_add_user_equipment_info(ccr, AVP_EPC_User_Equipment_Info_Type_MAC, ro_session->mac)) { LM_ERR("Problem adding User-Equipment data\n"); } if (!Ro_add_subscription_id(ccr, subscr.type, &subscr.id)) { LM_ERR("Problem adding Subscription ID data\n"); } if (!Ro_add_multiple_service_credit_Control_stop(ccr, used, ro_session->rating_group, ro_session->service_identifier)) { LM_ERR("Problem adding Multiple Service Credit Control data\n"); } if (!Ro_add_termination_cause(ccr, TERM_CAUSE_LOGOUT)) { LM_ERR("problem add Termination cause AVP to STOP record.\n"); } if (vendor_specific_chargeinfo) { if (!Ro_add_vendor_specific_termination_cause(ccr, code)) { LM_ERR("problem add Termination cause AVP to STOP record.\n"); } if (!Ro_add_vendor_specific_termination_reason(ccr, reason)) { LM_ERR("problem add Termination cause AVP to STOP record.\n"); } } cdpb.AAASessionsUnlock(auth->hash); if (ro_forced_peer.len > 0) { ret = cdpb.AAASendMessageToPeer(ccr, &ro_forced_peer, resume_on_termination_ccr, NULL); } else { ret = cdpb.AAASendMessage(ccr, resume_on_termination_ccr, NULL); } if (ret != 1) { goto error1; } Ro_free_CCR(ro_ccr_data); counter_inc(ims_charging_cnts_h.final_ccrs); // counter_add(ims_charging_cnts_h.active_ro_sessions, -1); return; error1: LM_ERR("error on Ro STOP record\n"); Ro_free_CCR(ro_ccr_data); if (auth) { cdpb.AAASessionsUnlock(auth->hash); cdpb.AAADropCCAccSession(auth); } error0: return; }
/** * Send a CCR to the OCS based on the SIP message (INVITE ONLY) * @param msg - SIP message * @param direction - orig|term * @param reservation_units - units to try to reserve * @param reservation_units - config route to call when receiving a CCA * @param tindex - transaction index * @param tindex - transaction label * * @returns #CSCF_RETURN_BREAK if OK, #CSCF_RETURN_ERROR on error */ int Ro_Send_CCR(struct sip_msg *msg, struct dlg_cell *dlg, int dir, int reservation_units, str* incoming_trunk_id, str* outgoing_trunk_id, str* pani, cfg_action_t* action, unsigned int tindex, unsigned int tlabel) { str session_id = {0, 0}, called_asserted_identity = {0, 0}, subscription_id = {0, 0}, asserted_identity = {0, 0}; int subscription_id_type = AVP_EPC_Subscription_Id_Type_End_User_SIP_URI; AAASession* cc_acc_session = NULL; Ro_CCR_t * ro_ccr_data = 0; AAAMessage * ccr = 0; struct ro_session *new_session = 0; struct session_setup_data *ssd; int ret = 0; struct hdr_field *h = 0; char *p; int cc_event_number = 0; //According to IOT tests this should start at 0 int cc_event_type = RO_CC_START; int free_called_asserted_identity = 0; sdp_session_cell_t* msg_sdp_session; sdp_stream_cell_t* msg_sdp_stream; int active_service_identifier; int active_rating_group; int sdp_stream_num = 0; LM_DBG("Sending initial CCR request (%c) for reservation_units [%d] incoming_trunk_id [%.*s] outgoing_trunk_id [%.*s]\n", dir==RO_ORIG_DIRECTION?'O':'T', reservation_units, incoming_trunk_id->len, incoming_trunk_id->s, outgoing_trunk_id->len, outgoing_trunk_id->s); ssd = shm_malloc(sizeof (struct session_setup_data)); // lookup structure used to load session info from cdp callback on CCA if (!ssd) { LM_ERR("no more shm mem\n"); goto error; } //getting asserted identity if ((asserted_identity = cscf_get_asserted_identity(msg, 0)).len == 0) { LM_DBG("No P-Asserted-Identity hdr found. Using From hdr for asserted_identity"); asserted_identity = dlg->from_uri; if (asserted_identity.len > 0 && asserted_identity.s) { p=(char*)memchr(asserted_identity.s, ';',asserted_identity.len); if (p) asserted_identity.len = (p-asserted_identity.s); } } //getting called asserted identity if ((called_asserted_identity = cscf_get_public_identity_from_called_party_id(msg, &h)).len == 0) { LM_DBG("No P-Called-Identity hdr found. Using request URI for called_asserted_identity"); called_asserted_identity = cscf_get_public_identity_from_requri(msg); free_called_asserted_identity = 1; } if (dir == RO_ORIG_DIRECTION) { subscription_id.s = asserted_identity.s; subscription_id.len = asserted_identity.len; } else if (dir == RO_TERM_DIRECTION) { subscription_id.s = called_asserted_identity.s; subscription_id.len = called_asserted_identity.len; } else { LM_CRIT("don't know what to do in unknown mode - should we even get here\n"); goto error; } //getting subscription id type if (strncasecmp(subscription_id.s, "tel:", 4) == 0) { subscription_id_type = Subscription_Type_MSISDN; } else { subscription_id_type = Subscription_Type_IMPU; //default is END_USER_SIP_URI } str mac = {0, 0}; if (get_mac_avp_value(msg, &mac) != 0) LM_DBG(RO_MAC_AVP_NAME" was not set. Using default."); //by default we use voice service id and rate group //then we check SDP - if we find video then we use video service id and rate group LM_DBG("Setting default SID to %d and RG to %d for voice", voice_service_identifier, voice_rating_group); active_service_identifier = voice_service_identifier; active_rating_group = voice_rating_group; //check SDP - if there is video then set default to video, if not set it to audio if (parse_sdp(msg) < 0) { LM_ERR("Unable to parse req SDP\n"); goto error; } msg_sdp_session = get_sdp_session(msg, 0); if (!msg_sdp_session) { LM_ERR("Missing SDP session information from rpl\n"); } else { for (;;) { msg_sdp_stream = get_sdp_stream(msg, 0, sdp_stream_num); if (!msg_sdp_stream) { //LM_ERR("Missing SDP stream information\n"); break; } int intportA = atoi(msg_sdp_stream->port.s); if (intportA != 0 && strncasecmp(msg_sdp_stream->media.s, "video", 5) == 0) { LM_DBG("This SDP has a video component and src ports not equal to 0 - so we set default SID to %d and RG to %d for video", video_service_identifier, video_rating_group); active_service_identifier = video_service_identifier; active_rating_group = video_rating_group; break; } sdp_stream_num++; } } free_sdp((sdp_info_t**) (void*) &msg->body); //create a session object without auth and diameter session id - we will add this later. new_session = build_new_ro_session(dir, 0, 0, &session_id, &dlg->callid, &asserted_identity, &called_asserted_identity, &mac, dlg->h_entry, dlg->h_id, reservation_units, 0, active_rating_group, active_service_identifier, incoming_trunk_id, outgoing_trunk_id, pani); if (!new_session) { LM_ERR("Couldn't create new Ro Session - this is BAD!\n"); goto error; } ssd->action = action; ssd->tindex = tindex; ssd->tlabel = tlabel; ssd->ro_session = new_session; if (!sip_create_ro_ccr_data(msg, dir, &ro_ccr_data, &cc_acc_session, asserted_identity, called_asserted_identity, subscription_id, subscription_id_type, incoming_trunk_id, outgoing_trunk_id, pani)) goto error; if (!ro_ccr_data) goto error; if (!cc_acc_session) goto error; if (!(ccr = Ro_new_ccr(cc_acc_session, ro_ccr_data))) goto error; if (!Ro_add_vendor_specific_appid(ccr, IMS_vendor_id_3GPP, IMS_Ro, 0)) { LM_ERR("Problem adding Vendor specific ID\n"); goto error; } if (!Ro_add_cc_request(ccr, cc_event_type, cc_event_number)) { LM_ERR("Problem adding CC-Request data\n"); goto error; } if (!Ro_add_event_timestamp(ccr, time(NULL))) { LM_ERR("Problem adding Event-Timestamp data\n"); goto error; } if (!Ro_add_user_equipment_info(ccr, AVP_EPC_User_Equipment_Info_Type_MAC, mac)) { LM_ERR("Problem adding User-Equipment data\n"); goto error; } if (!Ro_add_subscription_id(ccr, subscription_id_type, &subscription_id)) { LM_ERR("Problem adding Subscription ID data\n"); goto error; } if (!Ro_add_multiple_service_credit_Control(ccr, reservation_units, -1, active_rating_group, active_service_identifier)) { LM_ERR("Problem adding Multiple Service Credit Control data\n"); goto error; } /* before we send, update our session object with CC App session ID and data */ new_session->auth_appid = cc_acc_session->application_id; new_session->auth_session_type = cc_acc_session->type; new_session->ro_session_id.s = (char*) shm_malloc(cc_acc_session->id.len); if (!new_session->ro_session_id.s) { LM_ERR("no more shm mem\n"); goto error; } new_session->ro_session_id.len = cc_acc_session->id.len; memcpy(new_session->ro_session_id.s, cc_acc_session->id.s, cc_acc_session->id.len); LM_DBG("new CC Ro Session ID: [%.*s] stored in shared memory address [%p]\n", cc_acc_session->id.len, cc_acc_session->id.s, new_session); LM_DBG("Sending CCR Diameter message.\n"); // new_session->ccr_sent = 1; //assume we will send successfully cdpb.AAASessionsUnlock(cc_acc_session->hash); if (ro_forced_peer.len > 0) { LM_DBG("Sending message with Peer\n"); ret = cdpb.AAASendMessageToPeer(ccr, &ro_forced_peer, resume_on_initial_ccr, (void *) ssd); } else { LM_DBG("Sending message without Peer and realm is [%.*s]\n", ccr->dest_realm->data.len, ccr->dest_realm->data.s); ret = cdpb.AAASendMessage(ccr, resume_on_initial_ccr, (void *) ssd); } if (ret != 1) { LM_ERR("Failed to send Diameter CCR\n"); // new_session->ccr_sent = 0; goto error; } Ro_free_CCR(ro_ccr_data); LM_DBG("Registering for callbacks on Dialog [%p] and charging session [%p]\n", dlg, new_session); //TODO: if the following fail, we should clean up the Ro session....... if (dlgb.register_dlgcb(dlg, DLGCB_TERMINATED | DLGCB_FAILED | DLGCB_EXPIRED | DLGCB_CONFIRMED, dlg_callback_received, (void*) new_session, NULL) != 0) { LM_CRIT("cannot register callback for dialog confirmation\n"); goto error; } counter_inc(ims_charging_cnts_h.initial_ccrs); counter_inc(ims_charging_cnts_h.active_ro_sessions); if (free_called_asserted_identity) shm_free(called_asserted_identity.s); // shm_malloc in cscf_get_public_identity_from_requri return RO_RETURN_BREAK; error: LM_DBG("Trying to reserve credit on initial INVITE failed.\n"); if (free_called_asserted_identity) shm_free(called_asserted_identity.s); // shm_malloc in cscf_get_public_identity_from_requri Ro_free_CCR(ro_ccr_data); if (cc_acc_session) { cdpb.AAASessionsUnlock(cc_acc_session->hash); cdpb.AAADropSession(cc_acc_session); } if (ssd) shm_free(ssd); return RO_RETURN_ERROR; }
/* must be called with lock on ro_session */ void send_ccr_interim(struct ro_session* ro_session, unsigned int used, unsigned int reserve) { AAASession * auth = 0; AAAMessage * ccr = 0; Ro_CCR_t *ro_ccr_data = 0; ims_information_t *ims_info = 0; int32_t acc_record_type; subscription_id_t subscr; time_stamps_t *time_stamps; struct interim_ccr *i_req = shm_malloc(sizeof (struct interim_ccr)); int ret = 0; event_type_t *event_type; memset(i_req, 0, sizeof (sizeof (struct interim_ccr))); i_req->ro_session = ro_session; str sip_method = str_init("dummy"); str sip_event = str_init("dummy"); str user_name = {0, 0}; time_t req_timestamp; event_type = new_event_type(&sip_method, &sip_event, 0); LM_DBG("Sending interim CCR request for (usage:new) [%i:%i] seconds for user [%.*s] using session id [%.*s] active rating group [%d] active service identifier [%d] incoming_trunk_id [%.*s] outgoing_trunk_id [%.*s]\n", used, reserve, ro_session->asserted_identity.len, ro_session->asserted_identity.s, ro_session->ro_session_id.len, ro_session->ro_session_id.s, ro_session->rating_group, ro_session->service_identifier, ro_session->incoming_trunk_id.len, ro_session->incoming_trunk_id.s, ro_session->outgoing_trunk_id.len, ro_session->outgoing_trunk_id.s); req_timestamp = time(0); if (!(time_stamps = new_time_stamps(&req_timestamp, NULL, NULL, NULL))) goto error; if (!(ims_info = new_ims_information(event_type, time_stamps, &ro_session->callid, &ro_session->callid, &ro_session->asserted_identity, &ro_session->called_asserted_identity, 0, 0, 0, ro_session->direction, &ro_session->incoming_trunk_id, &ro_session->outgoing_trunk_id, &ro_session->pani))) goto error; LM_DBG("Created IMS information\n"); event_type = 0; if (ro_session->direction == RO_ORIG_DIRECTION) { subscr.id = ro_session->asserted_identity; } else if (ro_session->direction == RO_TERM_DIRECTION) { subscr.id = ro_session->called_asserted_identity; } else { LM_CRIT("don't know what to do in unknown mode - should we even get here\n"); goto error; } //getting subscription id type if (strncasecmp(subscr.id.s, "tel:", 4) == 0) { subscr.type = Subscription_Type_MSISDN; // Strip "tel:": subscr.id.s += 4; subscr.id.len -= 4; } else { subscr.type = Subscription_Type_IMPU; //default is END_USER_SIP_URI } user_name.s = subscr.id.s; user_name.len = subscr.id.len; acc_record_type = AAA_ACCT_INTERIM; ro_ccr_data = new_Ro_CCR(acc_record_type, &user_name, ims_info, &subscr); if (!ro_ccr_data) { LM_ERR("no memory left for generic\n"); goto error; } ims_info = NULL; auth = cdpb.AAAGetCCAccSession(ro_session->ro_session_id); if (!auth) { LM_DBG("Diameter Auth Session has timed out.... creating a new one.\n"); /* lets try and recreate this session */ //TODO: make a CC App session auth = cdpb.AAASession(ro_session->auth_appid, ro_session->auth_session_type, ro_session->ro_session_id); //TODO: would like this session to last longer (see session timeout in cdp //BUG("Oh shit, session timed out and I don't know how to create a new one."); auth = cdpb.AAAMakeSession(ro_session->auth_appid, ro_session->auth_session_type, ro_session->ro_session_id); //TODO: would like this session to last longer (see session timeout in cdp if (!auth) goto error; } //don't send INTERIM record if session is not in OPEN state (it could already be waiting for a previous response, etc) if (auth->u.cc_acc.state != ACC_CC_ST_OPEN) { LM_WARN("ignoring interim update on CC session not in correct state, currently in state [%d]\n", auth->u.cc_acc.state); goto error; } if (!(ccr = Ro_new_ccr(auth, ro_ccr_data))) goto error; if (!Ro_add_vendor_specific_appid(ccr, IMS_vendor_id_3GPP, IMS_Ro, 0/*acct id*/)) { LM_ERR("Problem adding Vendor specific ID\n"); } ro_session->hop_by_hop += 1; if (!Ro_add_cc_request(ccr, RO_CC_INTERIM, ro_session->hop_by_hop)) { LM_ERR("Problem adding CC-Request data\n"); } if (!Ro_add_event_timestamp(ccr, time(NULL))) { LM_ERR("Problem adding Event-Timestamp data\n"); } if (!Ro_add_user_equipment_info(ccr, AVP_EPC_User_Equipment_Info_Type_MAC, ro_session->mac)) { LM_ERR("Problem adding User-Equipment data\n"); } if (!Ro_add_subscription_id(ccr, subscr.type, &(subscr.id))) { LM_ERR("Problem adding Subscription ID data\n"); } if (!Ro_add_multiple_service_credit_Control(ccr, interim_request_credits/*INTERIM_CREDIT_REQ_AMOUNT*/, used, ro_session->rating_group, ro_session->service_identifier)) { LM_ERR("Problem adding Multiple Service Credit Control data\n"); } LM_DBG("Sending CCR Diameter message.\n"); cdpb.AAASessionsUnlock(auth->hash); if (ro_forced_peer.len > 0) { ret = cdpb.AAASendMessageToPeer(ccr, &ro_forced_peer, resume_on_interim_ccr, (void *) i_req); } else { ret = cdpb.AAASendMessage(ccr, resume_on_interim_ccr, (void *) i_req); } if (ret != 1) { goto error; } // cdpb.AAASessionsUnlock(auth->hash); Ro_free_CCR(ro_ccr_data); counter_inc(ims_charging_cnts_h.interim_ccrs); return; error: LM_ERR("error trying to reserve interim credit\n"); if (ro_ccr_data) Ro_free_CCR(ro_ccr_data); if (ccr) cdpb.AAAFreeMessage(&ccr); if (auth) { cdpb.AAASessionsUnlock(auth->hash); cdpb.AAADropCCAccSession(auth); } shm_free(i_req); // // since callback function will be never called because of the error, we need to release the lock on the session // to it can be reused later. // struct ro_session_entry *ro_session_entry = &(ro_session_table->entries[ro_session->h_entry]); ro_session_lock(ro_session_table, ro_session_entry); unref_ro_session_unsafe(ro_session, 1, ro_session_entry); //unref from the initial timer that fired this event. ro_session_unlock(ro_session_table, ro_session_entry); return; }
/** * Process a Credit Control Request and return the Answer for it. * @param ccr - the Diameter CCR * @returns the cca Diameter answer */ AAAMessage* Ro_CCA( AAAMessage * ccr) { AAAMessage *cca_msg; int ccr_data; int result; int ccr_avp; int onceoff_credit, persec_credit, perMbyte_credit; int crdtonce, crdtpsec, crdtpMb; char *user_avp; str user_avps; if (!ccr) { LOG(L_ERR,"ERROR:Ro_CCA: No message in the request\n"); return 0; } LOG(L_DBG,"Ro_CCA - Accounting request received from %s\n",ccr->orig_host->data.s); cca_msg = AAACreateResponse(ccr); if (!cca_msg) { LOG(L_ERR,"ERROR:Ro_CCA: No message in the created response\n"); return 0; } if (Ro_get_cc_request_type(ccr, &ccr_data)){//Retrieves accounting event type: START=1, INTERIM=2, STOP=3, EVENT=4 Ro_get_call_record_id(ccr, &ccr_avp); Ro_get_Credit_onceoff(ccr, &crdtonce); Ro_get_Credit_persec(ccr, &crdtpsec); Ro_get_Credit_perMbyte(ccr, &crdtpMb); Ro_get_subscriber(ccr, &user_avp); switch (ccr_data){ case START: msg_handler(user_avp,ccr_data, &result, ccr_avp, &onceoff_credit, &persec_credit, &perMbyte_credit,0,0,0); break; case STOP: msg_handler(user_avp,ccr_data, &result, ccr_avp, &onceoff_credit, &persec_credit, &perMbyte_credit,crdtonce,crdtpsec,crdtpMb); break; case INTERIM: msg_handler(user_avp,ccr_data, &result, ccr_avp, &onceoff_credit, &persec_credit, &perMbyte_credit,crdtonce,crdtpsec,crdtpMb); break; case EVENT: msg_handler(user_avp,ccr_data, &result, ccr_avp, &onceoff_credit, &persec_credit, &perMbyte_credit,0,0,0); break; default: return 0; } } Ro_add_vendor_specific_appid(cca_msg,IMS_vendor_id_3GPP,IMS_Ro,0 /*IMS_Ro*/); if (result == 1) Ro_add_result_code(cca_msg,DIAMETER_SUCCESS); else Ro_add_result_code(cca_msg,0); if (Ro_get_call_record_id(ccr, &ccr_avp)) Ro_add_Call_Record_Id(cca_msg, ccr_avp); else LOG(L_ERR,"Ro_CCA: Call ID AVP not found\n"); if (Ro_get_subscriber(ccr, &user_avp)){ user_avps.s = user_avp; user_avps.len = strlen(user_avps.s); Ro_add_subscriber(cca_msg, user_avps); }else {LOG(L_ERR,"Ro_CCA: Subscriber AVP not found\n");} if (Ro_get_cc_request_type(ccr, &ccr_data)) Ro_add_cc_request_type(ccr, ccr_data); else LOG(L_ERR,"Ro_CCA: Request Type AVP not found\n"); Ro_add_Credit_onceoff(cca_msg, onceoff_credit); Ro_add_Credit_persec(cca_msg, persec_credit); Ro_add_Credit_perMbyte(cca_msg, perMbyte_credit); return cca_msg; }