/* PSEUDOCODE/NOTES * 1. What mode are we in - terminating or originating * 2. check request type - IEC - Immediate Event Charging * ECUR - Event Charging with Unit Reservation * SCUR - Session Charging with Unit Reservation * 3. probably only do SCUR in this module for now - can see event based charging in another component instead (AS for SMS for example, etc) * 4. Check a dialog exists for call, if not we fail * 5. make sure we dont already have an Ro Session for this dialog * 6. create new Ro Session * 7. register for DLG callback passing new Ro session as parameter - (if dlg torn down we know which Ro session it is associated with) * * */ int ret = RO_RETURN_TRUE; int dir = 0; str identity = {0, 0}, contact = {0, 0}; struct hdr_field *h=0; cfg_action_t* cfg_action; tm_cell_t *t; unsigned int tindex = 0, tlabel = 0; struct impu_data *impu_data; udomain_t* domain_t = (udomain_t*) _d; char *p; struct dlg_cell* dlg; unsigned int len; struct ro_session *ro_session = 0; int free_contact = 0; LM_DBG("Ro CCR initiated: direction:%.*s, charge_type:%.*s, unit_type:%.*s, reservation_units:%i, route_name:%.*s", direction->len, direction->s, charge_type->len, charge_type->s, unit_type->len, unit_type->s, reservation_units, route_name->len, route_name->s); if (msg->first_line.type != SIP_REQUEST) { LM_ERR("Ro_CCR() called from SIP reply."); return RO_RETURN_ERROR;; } //make sure we can get the dialog! if not, we can't continue dlg = dlgb.get_dlg(msg); if (!dlg) { LM_ERR("Unable to find dialog and cannot do Ro charging without it\n"); return RO_RETURN_ERROR; } dir = get_direction_as_int(direction); if (dir == RO_ORIG_DIRECTION) { //get caller IMPU from asserted identity if ((identity = cscf_get_asserted_identity(msg, 0)).len == 0) { LM_DBG("No P-Asserted-Identity hdr found. Using From hdr for asserted_identity"); identity = dlg->from_uri; } //get caller contact from contact header - if not present then skip this if ((contact = cscf_get_contact(msg)).len == 0) { LM_WARN("Can not get contact from message - will not get callbacks if this IMPU is removed to terminate call"); goto send_ccr; } } else if (dir == RO_TERM_DIRECTION){ //get callee IMPU from called part id - if not present then skip this if ((identity = cscf_get_public_identity_from_called_party_id(msg, &h)).len == 0) { LM_WARN("No P-Called-Identity hdr found - will not get callbacks if this IMPU is removed to terminate call"); goto send_ccr; } //get callee contact from request URI contact = cscf_get_contact_from_requri(msg); free_contact = 1; } else { LM_CRIT("don't know what to do in unknown mode - should we even get here\n"); ret = RO_RETURN_ERROR; goto done; } LM_DBG("IMPU data to pass to usrloc: contact <%.*s> identity <%.*s>\n", contact.len, contact.s, identity.len, identity.s); //create impu_data_parcel len = identity.len + contact.len + sizeof (struct impu_data); impu_data = (struct impu_data*) shm_malloc(len); if (!impu_data) { LM_ERR("Unable to allocate memory for impu_data, trying to send CCR\n"); ret = RO_RETURN_ERROR; goto done; } memset(impu_data, 0, len); p = (char*) (impu_data + 1); impu_data->identity.s = p; impu_data->identity.len = identity.len; memcpy(p, identity.s, identity.len); p += identity.len; impu_data->contact.s = p; impu_data->contact.len = contact.len; memcpy(p, contact.s, contact.len); p += contact.len; impu_data->d = domain_t; if (p != (((char*) impu_data) + len)) { LM_ERR("buffer overflow creating impu data, trying to send CCR\n"); shm_free(impu_data); ret = RO_RETURN_ERROR; goto done; } //reg for callbacks on confirmed and terminated if (dlgb.register_dlgcb(dlg, /* DLGCB_RESPONSE_FWDED */ DLGCB_CONFIRMED, add_dlg_data_to_contact, (void*)impu_data ,NULL ) != 0) { LM_CRIT("cannot register callback for dialog confirmation\n"); ret = RO_RETURN_ERROR; goto done; } if (dlgb.register_dlgcb(dlg, DLGCB_TERMINATED | DLGCB_FAILED | DLGCB_EXPIRED /*| DLGCB_DESTROY */, remove_dlg_data_from_contact, (void*)impu_data, NULL ) != 0) { LM_CRIT("cannot register callback for dialog termination\n"); ret = RO_RETURN_ERROR; goto done; } send_ccr: //check if we need to send_ccr - //we get the ro_session based on dlg->h_id and dlg->h_entry and direction 0 (so get any ro_session) //if it already exists then we go to done if (single_ro_session_per_dialog && (ro_session = lookup_ro_session(dlg->h_entry, &dlg->callid, 0, 0))) { LM_DBG("single_ro_session_per_dialog = 1 and ro_session already exists for this dialog -so we don't need to send another one\n"); unref_ro_session(ro_session,1);//for the lookup ro session ref goto done; } LM_DBG("Looking for route block [%.*s]\n", route_name->len, route_name->s); int ri = route_get(&main_rt, route_name->s); if (ri < 0) { LM_ERR("unable to find route block [%.*s]\n", route_name->len, route_name->s); ret = RO_RETURN_ERROR; goto done; } cfg_action = main_rt.rlist[ri]; if (!cfg_action) { LM_ERR("empty action lists in route block [%.*s]\n", route_name->len, route_name->s); ret = RO_RETURN_ERROR; goto done; } //before we send lets suspend the transaction t = tmb.t_gett(); if (t == NULL || t == T_UNDEFINED) { if (tmb.t_newtran(msg) < 0) { LM_ERR("cannot create the transaction for CCR async\n"); ret = RO_RETURN_ERROR; goto done; } t = tmb.t_gett(); if (t == NULL || t == T_UNDEFINED) { LM_ERR("cannot lookup the transaction\n"); ret = RO_RETURN_ERROR; goto done; } } LM_DBG("Suspending SIP TM transaction\n"); if (tmb.t_suspend(msg, &tindex, &tlabel) < 0) { LM_ERR("failed to suspend the TM processing\n"); ret = RO_RETURN_ERROR; goto done; } ret = Ro_Send_CCR(msg, dlg, dir, charge_type, unit_type, reservation_units, cfg_action, tindex, tlabel); if(ret < 0){ LM_ERR("Failed to send CCR\n"); tmb.t_cancel_suspend(tindex, tlabel); } done: if(free_contact) shm_free(contact.s);// shm_malloc in cscf_get_public_identity_from_requri return ret; } ///* fixups */ static int domain_fixup(void** param) { udomain_t* d; if (ul.register_udomain((char*)*param, &d) < 0) { LM_ERR("failed to register domain\n"); return E_UNSPEC; } *param = (void*)d; return 0; }
/*! \brief * Convert char* parameter to udomain_t* pointer */ static int domain_fixup(void** param, int param_no) { udomain_t* d; if (param_no == 2) { if (isc_ulb.register_udomain((char*) *param, &d) < 0) { LM_ERR("failed to register domain\n"); return E_UNSPEC; } *param = (void*) d; } return 0; }
/* * Convert char* parameter to udomain_t* pointer */ static int domain_fixup(void** param, int param_no) { udomain_t* d; if (param_no == 1) { if (ul.register_udomain((char*)*param, &d) < 0) { LOG(L_ERR, "domain_fixup(): Error while registering domain\n"); return E_UNSPEC; } *param = (void*)d; } return 0; }
void remove_dlg_data_from_contact(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { struct impu_data *impu_data; impurecord_t* implicit_impurecord = 0; struct ucontact* ucontact; str callid = {0, 0}; str path = {0, 0}; udomain_t* domain_t; LM_DBG("dialog [%p] terminated, lets remove dlg data from contact\n", dlg); if (ul.register_udomain(domain, &domain_t) < 0) { LM_ERR("Unable to register usrloc domain....aborting\n"); return; } if(_params && _params->param){ impu_data = (struct impu_data*)*_params->param; if (!impu_data) { LM_ERR("IMPU data object is NULL...... aborting\n"); return; } LM_DBG("IMPU data is present, contact: <%.*s> identity <%.*s>", impu_data->contact.len, impu_data->contact.s, impu_data->identity.len, impu_data->identity.s); LM_DBG("IMPU data domain <%.*s>", domain_t->name->len, domain_t->name->s); ul.lock_udomain(domain_t, &impu_data->identity); if (ul.get_impurecord(domain_t, &impu_data->identity, &implicit_impurecord) != 0) { LM_DBG("usrloc does not have imprecord for implicity IMPU, ignore\n"); }else { if (ul.get_ucontact(&impu_data->contact, &callid, &path, 0/*cseq*/, &ucontact) != 0) { //contact does not exist LM_DBG("This contact: <%.*s> is not in usrloc, ignore - NOTE: You need S-CSCF usrloc set to match_mode CONTACT_PORT_IP_ONLY\n", impu_data->contact.len, impu_data->contact.s); } else {//contact exists so add dialog data to it ul.remove_dialog_data_from_contact(ucontact, dlg->h_entry, dlg->h_id); ul.release_ucontact(ucontact); } } ul.unlock_udomain(domain_t, &impu_data->identity); free_impu_data(impu_data); } //we referenced the dialog when we registered for callbacks on it... dlgb.release_dlg(dlg); }
static int fixup_aar_register(void** param, int param_no) { // udomain_t* d; // aar_param_t *ap; // // if (param_no != 1) // return 0; // ap = (aar_param_t*) pkg_malloc(sizeof (aar_param_t)); // if (ap == NULL) { // LM_ERR("no more pkg\n"); // return -1; // } // memset(ap, 0, sizeof (aar_param_t)); // ap->paction = get_action_from_param(param, param_no); // // if (ul.register_udomain((char*) *param, &d) < 0) { // LM_ERR("failed to register domain\n"); // return E_UNSPEC; // } // ap->domain = d; // // *param = (void*) ap; // return 0; if (strlen((char*) *param) <= 0) { LM_ERR("empty parameter %d not allowed\n", param_no); return -1; } if (param_no == 1) { //route name - static or dynamic string (config vars) if (fixup_spve_null(param, param_no) < 0) return -1; return 0; } else if (param_no == 2) { udomain_t* d; if (ul.register_udomain((char*) *param, &d) < 0) { LM_ERR("Error doing fixup on assign save"); return -1; } *param = (void*) d; } return 0; }
/* * Convert the char* parameters */ static int assign_save_fixup3_async(void** param, int param_no) { if (strlen((char*) *param) <= 0) { LM_ERR("empty parameter %d not allowed\n", param_no); return -1; } if (param_no == 1) { //route name - static or dynamic string (config vars) if (fixup_spve_null(param, param_no) < 0) return -1; return 0; } else if (param_no == 2) { udomain_t* d; if (ul.register_udomain((char*) *param, &d) < 0) { LM_ERR("Error doing fixup on assign save"); return -1; } *param = (void*) d; } return 0; }
AAAMessage* cxdx_process_rtr(AAAMessage *rtr) { LM_DBG("Processing RTR"); AAAMessage *rta_msg; AAA_AVP* avp; str public_id; impurecord_t* r; int res = 0; udomain_t* udomain; impu_contact_t *impucontact; rta_msg = cdpb.AAACreateResponse(rtr);//session ID? if (!rta_msg) return 0; avp = cxdx_get_next_public_identity(rtr,0,AVP_IMS_Public_Identity,IMS_vendor_id_3GPP,__FUNCTION__); if(avp==0){ LM_WARN("RTR received with only IMPI (username AVP) - currently S-CSCF does not support this kind of RTR\n"); return 0; //TODO add support for receiving RTR with IMPI //get all impus related to this impu //get all contacts related to each impu //set the contact expire for each contact to now }else{ public_id=avp->data; LM_DBG("RTR received with IMPU [%.*s] in public identity AVP - this is supported\n", public_id.len, public_id.s); //TODO this should be a configurable module param if (ul.register_udomain(domain, &udomain) < 0) { LM_ERR("Unable to register usrloc domain....aborting\n"); return 0; } ul.lock_udomain(udomain, &public_id); res = ul.get_impurecord(udomain, &public_id, &r); if (res != 0) { LM_WARN("Strange, '%.*s' Not found in usrloc\n", public_id.len, public_id.s); ul.unlock_udomain(udomain, &public_id); //no point in continuing return 0; } impucontact = r->linked_contacts.head; while (impucontact) { LM_DBG("Deleting contact with AOR [%.*s]\n", impucontact->contact->aor.len, impucontact->contact->aor.s); ul.lock_contact_slot_i(impucontact->contact->sl); impucontact->contact->state = CONTACT_DELETE_PENDING; if (r->shead) { //send NOTIFY to all subscribers of this IMPU. notify_subscribers(r, 0, 0); } impucontact->contact->state = CONTACT_DELETED; ul.unlock_contact_slot_i(impucontact->contact->sl); impucontact = impucontact->next; } ul.unlock_udomain(udomain, &public_id); while(cdpb.AAAGetNextAVP(avp) && (avp=cxdx_get_next_public_identity(rtr,cdpb.AAAGetNextAVP(avp),AVP_IMS_Public_Identity,IMS_vendor_id_3GPP,__FUNCTION__))!=0){ public_id=avp->data; LM_DBG("RTR also has public id [%.*s]\n", public_id.len, public_id.s); ul.lock_udomain(udomain, &public_id); res = ul.get_impurecord(udomain, &public_id, &r); if (res != 0) { LM_WARN("Strange, '%.*s' Not found in usrloc\n", public_id.len, public_id.s); ul.unlock_udomain(udomain, &public_id); //no point in continuing return 0; } impucontact = r->linked_contacts.head; while (impucontact) { LM_DBG("Deleting contact with AOR [%.*s]\n", impucontact->contact->aor.len, impucontact->contact->aor.s); ul.lock_contact_slot_i(impucontact->contact->sl); impucontact->contact->state = CONTACT_DELETE_PENDING; if (r->shead) { //send NOTIFY to all subscribers of this IMPU. notify_subscribers(r, 0, 0); } impucontact->contact->state = CONTACT_DELETED; ul.unlock_contact_slot_i(impucontact->contact->sl); impucontact = impucontact->next; } ul.unlock_udomain(udomain, &public_id); } } cxdx_add_vendor_specific_appid(rta_msg,IMS_vendor_id_3GPP,IMS_Cx,0 /*IMS_Cx*/); cxdx_add_auth_session_state(rta_msg,1); /* send an RTA back to the HSS */ cxdx_add_result_code(rta_msg,DIAMETER_SUCCESS); return rta_msg; }
/*main event process function*/ void cdp_cb_event_process() { cdp_cb_event_t *ev; udomain_t* domain; pcontact_t* pcontact; str release_reason = {"QoS released", 12}; /* TODO: This could be a module parameter */ struct pcontact_info ci; memset(&ci, 0, sizeof (struct pcontact_info)); for (;;) { ev = pop_cdp_cb_event(); if (cdp_event_latency) { //track delays unsigned int diff = time(NULL) - ev->registered; if (diff > cdp_event_threshold) { switch (cdp_event_latency_loglevel) { case 0: LM_ERR("Took to long to pickup CDP callback event [%d] > [%d]\n", diff, cdp_event_threshold); break; case 1: LM_WARN("Took to long to pickup CDP callback event [%d] > [%d]\n", diff, cdp_event_threshold); break; case 2: LM_INFO("Took to long to pickup CDP callback event [%d] > [%d]\n", diff, cdp_event_threshold); break; case 3: LM_DBG("Took to long to pickup CDP callback event [%d] > [%d]\n", diff, cdp_event_threshold); break; default: LM_DBG("Unknown log level....printing as debug\n"); LM_DBG("Took to long to pickup CDP callback event [%d] > [%d]\n", diff, cdp_event_threshold); break; } } } LM_DBG("processing event [%d]\n", ev->event); rx_authsessiondata_t *p_session_data = ev->session_data; str *rx_session_id = &ev->rx_session_id; switch (ev->event) { case AUTH_EV_SESSION_TIMEOUT: case AUTH_EV_SESSION_GRACE_TIMEOUT: case AUTH_EV_RECV_ASR: LM_DBG("Received notification of ASR from transport plane or CDP timeout for CDP session with Rx session ID: [%.*s] and associated contact [%.*s]" " and domain [%.*s]\n", rx_session_id->len, rx_session_id->s, p_session_data->registration_aor.len, p_session_data->registration_aor.s, p_session_data->domain.len, p_session_data->domain.s); if (p_session_data->subscribed_to_signaling_path_status) { LM_DBG("This is a subscription to signalling bearer session"); //nothing to do here - just wait for AUTH_EV_SERVICE_TERMINATED event } else { LM_DBG("This is a media bearer session session"); //this is a media bearer session that was terminated from the transport plane - we need to terminate the associated dialog //so we set p_session_data->must_terminate_dialog to 1 and when we receive AUTH_EV_SERVICE_TERMINATED event we will terminate the dialog p_session_data->must_terminate_dialog = 1; } break; case AUTH_EV_SERVICE_TERMINATED: LM_DBG("Received notification of CDP TERMINATE of CDP session with Rx session ID: [%.*s] and associated contact [%.*s]" " and domain [%.*s]\n", rx_session_id->len, rx_session_id->s, p_session_data->registration_aor.len, p_session_data->registration_aor.s, p_session_data->domain.len, p_session_data->domain.s); if (p_session_data->subscribed_to_signaling_path_status) { LM_DBG("This is a subscription to signalling bearer session"); //instead of removing the contact from usrloc_pcscf we just change the state of the contact to TERMINATE_PENDING_NOTIFY //pcscf_registrar sees this, sends a SIP PUBLISH and on SIP NOTIFY the contact is deleted if (ul.register_udomain(p_session_data->domain.s, &domain) < 0) { LM_DBG("Unable to register usrloc domain....aborting\n"); return; } ul.lock_udomain(domain, &p_session_data->registration_aor); if (ul.get_pcontact(domain, &p_session_data->registration_aor, &pcontact) != 0) { LM_DBG("no contact found for terminated Rx reg session..... ignoring\n"); } else { LM_DBG("Updating contact [%.*s] after Rx reg session terminated, setting state to PCONTACT_DEREG_PENDING_PUBLISH\n", pcontact->aor.len, pcontact->aor.s); ci.reg_state = PCONTACT_DEREG_PENDING_PUBLISH; ci.num_service_routes = 0; ul.update_pcontact(domain, &ci, pcontact); } ul.unlock_udomain(domain, &p_session_data->registration_aor); } else { LM_DBG("This is a media bearer session session"); //we only terminate the dialog if this was triggered from the transport plane or timeout - i.e. if must_terminate_dialog is set //if this was triggered from the signalling plane (i.e. someone hanging up) then we don'y need to terminate the dialog if (p_session_data->must_terminate_dialog) { LM_DBG("Terminating dialog with callid, ftag, ttag: [%.*s], [%.*s], [%.*s]\n", p_session_data->callid.len, p_session_data->callid.s, p_session_data->ftag.len, p_session_data->ftag.s, p_session_data->ttag.len, p_session_data->ttag.s); dlgb.terminate_dlg(&p_session_data->callid, &p_session_data->ftag, &p_session_data->ttag, NULL, &release_reason); } } //free callback data if (p_session_data) { shm_free(p_session_data); p_session_data = 0; } break; default: break; } free_cdp_cb_event(ev); } }