void resume_ro_session_ontimeout(struct interim_ccr *i_req, int timeout_or_error) { time_t now = get_current_time_micro(); long used_secs; struct ro_session_entry *ro_session_entry = NULL; int call_terminated = 0; if (!i_req) { LM_ERR("This is so wrong: i_req is NULL\n"); return; } ro_session_entry = &(ro_session_table->entries[i_req->ro_session->h_entry]); ro_session_lock(ro_session_table, ro_session_entry); LM_DBG("credit=%d credit_valid_for=%d", i_req->new_credit, i_req->credit_valid_for); used_secs = rint((now - ((timeout_or_error==1 && i_req->ro_session->last_event_timestamp_backup>0)?i_req->ro_session->last_event_timestamp_backup : i_req->ro_session->last_event_timestamp)) / (float) 1000000); /* check to make sure diameter server is giving us sane values */ if (i_req->credit_valid_for !=0 && i_req->new_credit > i_req->credit_valid_for) { LM_WARN("That's weird, Diameter server gave us credit with a lower validity period :D. Setting reserved time to validity period instead \n"); i_req->new_credit = i_req->credit_valid_for; } if (i_req->new_credit > 0) { //now insert the new timer i_req->ro_session->last_event_timestamp = get_current_time_micro(); i_req->ro_session->event_type = answered; i_req->ro_session->valid_for = i_req->credit_valid_for; int ret = 0; if (i_req->is_final_allocation) { LM_DBG("This is a final allocation and call will end in %i seconds\n", i_req->new_credit); i_req->ro_session->event_type = no_more_credit; ret = insert_ro_timer(&i_req->ro_session->ro_tl, i_req->new_credit); } else { int timer_timeout = i_req->new_credit; if (i_req->new_credit > ro_timer_buffer /*TIMEOUTBUFFER*/) { // We haven't finished using our 1st block of units, and we need to set the timer to // (new_credit - ro_timer_buffer[5 secs]) to ensure we get new credit before our previous // reservation is exhausted. This will only be done the first time, because the timer // will always be fired 5 seconds before we run out of time thanks to this operation timer_timeout = i_req->new_credit - ro_timer_buffer; } ret = insert_ro_timer(&i_req->ro_session->ro_tl, timer_timeout); } // update to the new block of units we got i_req->ro_session->reserved_secs = i_req->new_credit; if (ret != 0) { LM_CRIT("unable to insert timer for Ro Session [%.*s]\n", i_req->ro_session->ro_session_id.len, i_req->ro_session->ro_session_id.s); } else { ref_ro_session(i_req->ro_session, 1, 0); } if (ro_db_mode == DB_MODE_REALTIME) { i_req->ro_session->flags |= RO_SESSION_FLAG_CHANGED; if (update_ro_dbinfo_unsafe(i_req->ro_session) != 0) { LM_ERR("Failed to update Ro session in DB... continuing\n"); } } } else { /* just put the timer back in with however many seconds are left (if any!!! in which case we need to kill */ /* also update the event type to no_more_credit to save on processing the next time we get here */ i_req->ro_session->event_type = no_more_credit; if (!timeout_or_error) i_req->ro_session->last_event_timestamp = get_current_time_micro(); int whatsleft = i_req->ro_session->reserved_secs - used_secs; if (whatsleft <= 0) { // TODO we need to handle this situation more precisely. // in case CCR times out, we get a call shutdown but the error message assumes it was due to a lack of credit. // LM_WARN("Immediately killing call due to no more credit *OR* no CCA received (timeout) after reservation request\n"); // // we need to unlock the session or else we might get a deadlock on dlg_terminated() dialog callback. // Do not unref the session because it will be made inside the dlg_terminated() function. // //unref_ro_session_unsafe(i_req->ro_session, 1, ro_session_entry); ro_session_unlock(ro_session_table, ro_session_entry); dlgb.lookup_terminate_dlg(i_req->ro_session->dlg_h_entry, i_req->ro_session->dlg_h_id, NULL); call_terminated = 1; } else { LM_DBG("No more credit for user - letting call run out of money in [%i] seconds", whatsleft); int ret = insert_ro_timer(&i_req->ro_session->ro_tl, whatsleft); if (ret != 0) { LM_CRIT("unable to insert timer for Ro Session [%.*s]\n", i_req->ro_session->ro_session_id.len, i_req->ro_session->ro_session_id.s); } else { ref_ro_session(i_req->ro_session, 1, 0); } } } // // if call was forcefully terminated, the lock was released before dlgb.lookup_terminate_dlg() function call. // if (!call_terminated) { unref_ro_session(i_req->ro_session, 1, 0); //unref from the initial timer that fired this event. ro_session_unlock(ro_session_table, ro_session_entry); } shm_free(i_req); LM_DBG("Exiting async ccr interim nicely"); }
void dlg_terminated(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { //int i; int unref = 0; struct ro_session *ro_session = 0; struct ro_session_entry *ro_session_entry; struct sip_msg *request; LM_DBG("dialog [%p] terminated, lets send stop record\n", dlg); if (!_params) { return; } ro_session = (struct ro_session*)*_params->param; if (!ro_session) { LM_ERR("Ro Session object is NULL...... aborting\n"); return; } request = _params->req; if (!request) { LM_WARN("dlg_terminated has no SIP request associated.\n"); } if (dlg && (dlg->callid.s && dlg->callid.len > 0)) { /* find the session for this call, possibly both for orig and term*/ //for (i=0; i<2; i++) { //TODO: try and get the Ro session specifically for terminating dialog or originating one //currently the way we are doing is a hack..... //if ((ro_session = lookup_ro_session(dlg->h_entry, &dlg->callid, 0, 0))) { ro_session_entry = &(ro_session_table->entries[ro_session->h_entry]); //if the Ro session is not active we don't need to do anything. This prevents //double processing for various dialog_terminated callback events. //If however, the call was never answered, then we can continue as normal if (!ro_session->active && (ro_session->start_time != 0)) { unref_ro_session(ro_session,1); LM_ERR("Ro Session is not active, but may have been answered [%d]\n", (int)ro_session->start_time); return; } ro_session_lock(ro_session_table, ro_session_entry); if (ro_session->active) { // if the call was never activated, there's no timer to remove int ret = remove_ro_timer(&ro_session->ro_tl); if (ret < 0) { LM_CRIT("unable to unlink the timer on ro_session %p [%.*s]\n", ro_session, ro_session->cdp_session_id.len, ro_session->cdp_session_id.s); } else if (ret > 0) { LM_WARN("inconsistent ro timer data on ro_session %p [%.*s]\n", ro_session, ro_session->cdp_session_id.len, ro_session->cdp_session_id.s); } else { unref++; } } LM_DBG("Sending CCR STOP on Ro_Session [%p]\n", ro_session); send_ccr_stop(ro_session); ro_session->active = 0; //ro_session->start_time; unref_ro_session_unsafe(ro_session, 1+unref, ro_session_entry); //lock already acquired //unref_ro_session_unsafe(ro_session, 2+unref, ro_session_entry); //lock already acquired ro_session_unlock(ro_session_table, ro_session_entry); //} //} } }
void dlg_reply(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { struct sip_msg *reply; struct ro_session* session = 0; struct ro_session_entry* ro_session_entry; time_t now = time(0); time_t time_since_last_event; LM_DBG("dlg_reply callback entered\n"); if (!_params) { return; } reply = _params->rpl; if (!reply) { LM_WARN("dlg_reply has no SIP reply associated.\n"); } // if (reply != FAKED_REPLY && reply->REPLY_STATUS == 200) { // //get CC session from callback param // char* cdp_session_id = (char*)*_params->param; // LM_INFO("Call answered\n"); // LM_DBG("Call answered and we have a session id of [%s]\n", cdp_session_id); // // str session_id; // session_id.s = cdp_session_id; // session_id.len = strlen(cdp_session_id); // AAASession* cdp_session = cdpb.AAAGetCCAccSession(session_id); // if (!cdp_session) { // LM_ERR("could not find find CC App CDP session\n"); // return; // } // // cdpb.AAAStartChargingCCAccSession(cdp_session); // cdpb.AAASessionsUnlock(cdp_session->hash); // } if (reply != FAKED_REPLY && reply->REPLY_STATUS == 200) { LM_DBG("Call answered on dlg [%p] - search for Ro Session and initialise timers.\n", dlg); session = (struct ro_session*)*_params->param; if (!session) { LM_ERR("Ro Session object is NULL...... aborting\n"); return; } ro_session_entry = &(ro_session_table->entries[session->h_entry]); ro_session_lock(ro_session_table, ro_session_entry); if (session->active) { LM_CRIT("Why the heck am i receiving a double confirmation of the dialog? Ignoring... "); ro_session_unlock(ro_session_table, ro_session_entry); return; } time_since_last_event = now - session->last_event_timestamp; session->start_time = session->last_event_timestamp = now; session->event_type = answered; session->active = 1; /* check to make sure that the validity of the credit is enough for the bundle */ int ret = 0; if (session->reserved_secs < (session->valid_for - time_since_last_event)) { if (session->reserved_secs > ro_timer_buffer/*TIMEOUTBUFFER*/) { ret = insert_ro_timer(&session->ro_tl, session->reserved_secs - ro_timer_buffer); //subtract 5 seconds so as to get more credit before we run out } else { ret = insert_ro_timer(&session->ro_tl, session->reserved_secs); } } else { if (session->valid_for > ro_timer_buffer) { ret = insert_ro_timer(&session->ro_tl, session->valid_for - ro_timer_buffer); //subtract 5 seconds so as to get more credit before we run out } else { ret = insert_ro_timer(&session->ro_tl, session->valid_for); } } if (ret != 0) { LM_CRIT("unable to insert timer for Ro Session [%.*s]\n", session->ro_session_id.len, session->ro_session_id.s); } else { ref_ro_session_unsafe(session, 1); // lock already acquired } ro_session_unlock(ro_session_table, ro_session_entry); AAASession* cdp_session = cdpb.AAAGetCCAccSession(session->ro_session_id); if (!cdp_session) { LM_ERR("could not find find CC App CDP session for session [%.*s]\n", session->ro_session_id.len, session->ro_session_id.s); // ro_session_unlock(ro_session_table, ro_session_entry); return; } // ro_session_unlock(ro_session_table, ro_session_entry); cdpb.AAAStartChargingCCAccSession(cdp_session); cdpb.AAASessionsUnlock(cdp_session->hash); // unref_ro_session(session, 1); DONT need this anymore because we don't do lookup so no addition to ref counter } }
void dlg_answered(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) { struct sip_msg *reply; struct ro_session* session = 0; struct ro_session_entry* ro_session_entry; time_t now = get_current_time_micro(); time_t time_since_last_event; LM_DBG("dlg_reply callback entered\n"); if (!_params) { return; } session = (struct ro_session*) *_params->param; if (!session) { LM_ERR("Ro Session object is NULL...... aborting\n"); return; } LM_DBG("Call answered on dlg [%p] - search for Ro Session [%p]\n", dlg, session); ro_session_entry = &(ro_session_table->entries[session->h_entry]); ro_session_lock(ro_session_table, ro_session_entry); if (session->active) { LM_CRIT("Why the heck am i receiving a double confirmation of the dialog? Ignoring... "); ro_session_unlock(ro_session_table, ro_session_entry); return; } else if (session->active < 0) { //session has already been terminated - we can't reactivate... LM_WARN("Received an answer after terminating dialog.... ignoring\n"); ro_session_unlock(ro_session_table, ro_session_entry); return; } time_since_last_event = (now - session->last_event_timestamp)/1000000; session->start_time = session->last_event_timestamp = now; session->event_type = answered; session->active = 1; /* check to make sure that the validity of the credit is enough for the bundle */ int ret = 0; LM_DBG("we were granted %d seconds (valud for %d seconds) and it's been %d seconds since we requested\n", (int)session->reserved_secs, (int)session->valid_for, (int)time_since_last_event); if (session->reserved_secs < (session->valid_for - time_since_last_event)) { if (session->reserved_secs > ro_timer_buffer/*TIMEOUTBUFFER*/) { ret = insert_ro_timer(&session->ro_tl, session->reserved_secs - ro_timer_buffer); //subtract 5 seconds so as to get more credit before we run out } else { ret = insert_ro_timer(&session->ro_tl, session->reserved_secs); } } else { if (session->valid_for > ro_timer_buffer) { ret = insert_ro_timer(&session->ro_tl, session->valid_for - ro_timer_buffer); //subtract 5 seconds so as to get more credit before we run out } else { ret = insert_ro_timer(&session->ro_tl, session->valid_for); } } if (ret != 0) { LM_CRIT("unable to insert timer for Ro Session [%.*s]\n", session->ro_session_id.len, session->ro_session_id.s); } else { ref_ro_session_unsafe(session, 1); // lock already acquired } if (ro_db_mode == DB_MODE_REALTIME) { session->flags |= RO_SESSION_FLAG_CHANGED; if (update_ro_dbinfo_unsafe(session) != 0) { LM_ERR("Failed to update ro_session in database... continuing\n"); }; } ro_session_unlock(ro_session_table, ro_session_entry); AAASession* cdp_session = cdpb.AAAGetCCAccSession(session->ro_session_id); if (!cdp_session) { LM_ERR("could not find find CC App CDP session for session [%.*s]\n", session->ro_session_id.len, session->ro_session_id.s); return; } cdpb.AAAStartChargingCCAccSession(cdp_session); cdpb.AAASessionsUnlock(cdp_session->hash); }
/* 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; }
/* this is the function called when a we need to request more funds/credit. We need to try and reserve more credit. * If we cant we need to put a new timer to kill the call at the appropriate time */ void ro_session_ontimeout(struct ro_tl *tl) { time_t now, used_secs, call_time; LM_DBG("We have a fired timer [p=%p] and tl=[%i].\n", tl, tl->timeout); /* find the session id for this timer*/ struct ro_session_entry *ro_session_entry = NULL; struct ro_session* ro_session; ro_session = ((struct ro_session*) ((char *) (tl) - (unsigned long) (&((struct ro_session*) 0)->ro_tl))); if (!ro_session) { LM_ERR("Can't find a session. This is bad"); return; } // LM_ALERT("LOCKING... "); ro_session_entry = &(ro_session_table->entries[ro_session->h_entry]); ro_session_lock(ro_session_table, ro_session_entry); // LM_ALERT("LOCKED!"); LM_DBG("event-type=%d", ro_session->event_type); // if (!ro_session->active) { // LM_ALERT("Looks like this session was terminated while requesting more units"); // goto exit; // return; // } switch (ro_session->event_type) { case answered: now = time(0); used_secs = now - ro_session->last_event_timestamp; call_time = now - ro_session->start_time; update_stat(billed_secs, used_secs); if (ro_session->callid.s != NULL && ro_session->dlg_h_entry > 0 && ro_session->dlg_h_id > 0 && ro_session->ro_session_id.s != NULL) { LM_DBG("Found a session to re-apply for timing [%.*s] and user is [%.*s]\n", ro_session->ro_session_id.len, ro_session->ro_session_id.s, ro_session->from_uri.len, ro_session->from_uri.s); LM_DBG("Call session has been active for %i seconds. The last reserved secs was [%i] and the last event was [%i seconds] ago", (unsigned int) call_time, (unsigned int) ro_session->reserved_secs, (unsigned int) used_secs); LM_DBG("Call session [p=%p]: we will now make a request for another [%i] of credit with a usage of [%i] seconds from the last bundle.\n", ro_session, interim_request_credits/* new reservation request amount */, (unsigned int) used_secs/* charged seconds from previous reservation */); // Apply for more credit. // // The function call will return immediately and we will receive the reply asynchronously via a callback send_ccr_interim(ro_session, (unsigned int) used_secs, interim_request_credits); return; } else { LM_ERR("Hmmm, the session we have either doesn't have all the data or something else has gone wrong.\n"); /* put the timer back so the call will be killed according to previous timeout. */ ro_session->event_type = unknown_error; int ret = insert_ro_timer(&ro_session->ro_tl, ro_session->reserved_secs - used_secs); if (ret != 0) { LM_CRIT("unable to insert timer for Ro Session [%.*s]\n", ro_session->ro_session_id.len, ro_session->ro_session_id.s); } else { ref_ro_session_unsafe(ro_session, 1); } LM_ERR("Immediately killing call due to unknown error\n"); dlgb.lookup_terminate_dlg(ro_session->dlg_h_entry, ro_session->dlg_h_id, NULL ); } break; default: LM_ERR("Diameter call session - event [%d]\n", ro_session->event_type); if (ro_session->event_type == no_more_credit) LM_INFO("Call/session must be ended - no more funds.\n"); else if (ro_session->event_type == unknown_error) LM_ERR("last event caused an error. We will now tear down this session.\n"); dlgb.lookup_terminate_dlg(ro_session->dlg_h_entry, ro_session->dlg_h_id, NULL ); } update_stat(killed_calls, 1); 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; }
void resume_ro_session_ontimeout(struct interim_ccr *i_req) { time_t now = time(0); time_t used_secs; struct ro_session_entry *ro_session_entry = NULL; if (!i_req) { LM_ERR("This is so wrong: i_req is NULL\n"); return; } LM_DBG("credit=%d credit_valid_for=%d", i_req->new_credit, i_req->credit_valid_for); used_secs = now - i_req->ro_session->last_event_timestamp; /* check to make sure diameter server is giving us sane values */ if (i_req->new_credit > i_req->credit_valid_for) { LM_WARN("That's weird, Diameter server gave us credit with a lower validity period :D. Setting reserved time to validity perioud instead \n"); i_req->new_credit = i_req->credit_valid_for; } if (i_req->new_credit > 0) { //now insert the new timer i_req->ro_session->last_event_timestamp = time(0); i_req->ro_session->event_type = answered; i_req->ro_session->valid_for = i_req->credit_valid_for; int ret = 0; if (i_req->is_final_allocation) { LM_DBG("This is a final allocation and call will end in %i seconds\n", i_req->new_credit); i_req->ro_session->event_type = no_more_credit; ret = insert_ro_timer(&i_req->ro_session->ro_tl, i_req->new_credit); } else { int timer_timeout = i_req->new_credit; if (i_req->new_credit > ro_timer_buffer /*TIMEOUTBUFFER*/) { // We haven't finished using our 1st block of units, and we need to set the timer to // (new_credit - ro_timer_buffer[5 secs]) to ensure we get new credit before our previous // reservation is exhausted. This will only be done the first time, because the timer // will always be fired 5 seconds before we run out of time thanks to this operation if ((now - i_req->ro_session->start_time) /* call time */ < i_req->ro_session->reserved_secs) timer_timeout = i_req->new_credit - ro_timer_buffer; else timer_timeout = i_req->new_credit; } ret = insert_ro_timer(&i_req->ro_session->ro_tl, timer_timeout); } // update to the new block of units we got i_req->ro_session->reserved_secs = i_req->new_credit; if (ret != 0) { LM_CRIT("unable to insert timer for Ro Session [%.*s]\n", i_req->ro_session->ro_session_id.len, i_req->ro_session->ro_session_id.s); } else { ref_ro_session_unsafe(i_req->ro_session, 1); } } else { /* just put the timer back in with however many seconds are left (if any!!! in which case we need to kill */ /* also update the event type to no_more_credit to save on processing the next time we get here */ i_req->ro_session->event_type = no_more_credit; int whatsleft = i_req->ro_session->reserved_secs - used_secs; if (whatsleft <= 0) { LM_WARN("Immediately killing call due to no more credit\n"); dlgb.lookup_terminate_dlg(i_req->ro_session->dlg_h_entry, i_req->ro_session->dlg_h_id, NULL ); } else { LM_DBG("No more credit for user - letting call run out of money in [%i] seconds", whatsleft); int ret = insert_ro_timer(&i_req->ro_session->ro_tl, whatsleft); if (ret != 0) { LM_CRIT("unable to insert timer for Ro Session [%.*s]\n", i_req->ro_session->ro_session_id.len, i_req->ro_session->ro_session_id.s); } else { ref_ro_session_unsafe(i_req->ro_session, 1); } } } ro_session_entry = &(ro_session_table->entries[i_req->ro_session->h_entry]); unref_ro_session_unsafe(i_req->ro_session, 1, ro_session_entry);//unref from the initial timer that fired this event. ro_session_unlock(ro_session_table, ro_session_entry); shm_free(i_req); LM_DBG("Exiting async ccr interim nicely"); }
void dlg_terminated(struct dlg_cell *dlg, int type, unsigned int termcode, char* reason, struct dlg_cb_params *_params) { //int i; int unref = 0; struct ro_session *ro_session = 0; struct ro_session_entry *ro_session_entry; struct sip_msg *request; str s_reason; s_reason.s = reason; s_reason.len = strlen(reason); LM_DBG("dialog [%p] terminated on type [%d], lets send stop record\n", dlg, type); if (!_params) { return; } LM_DBG("Direction is %d\n", _params->direction); if (_params->req) { if (_params->req->first_line.u.request.method_value == METHOD_BYE) { if (_params->direction == DLG_DIR_DOWNSTREAM) { LM_DBG("Dialog ended by Caller\n"); } else { LM_DBG("Dialog ended by Callee\n"); } } else { LM_DBG("Request is %.*s\n", _params->req->first_line.u.request.method.len, _params->req->first_line.u.request.method.s); } struct hdr_field* h = get_hdr_by_name(_params->req, "Reason", 6); if(h!=NULL){ LM_DBG("reason header is [%.*s]\n", h->body.len, h->body.s); s_reason = h->body; } } else if (_params->rpl) { LM_DBG("Reply is [%d - %.*s]", _params->rpl->first_line.u.reply.statuscode, _params->rpl->first_line.u.reply.reason.len, _params->rpl->first_line.u.reply.reason.s); } ro_session = (struct ro_session*)*_params->param; if (!ro_session) { LM_ERR("Ro Session object is NULL...... aborting\n"); return; } request = _params->req; if (!request) { LM_WARN("dlg_terminated has no SIP request associated.\n"); } if (dlg && (dlg->callid.s && dlg->callid.len > 0)) { /* find the session for this call, possibly both for orig and term*/ //for (i=0; i<2; i++) { //TODO: try and get the Ro session specifically for terminating dialog or originating one //currently the way we are doing is a hack..... //if ((ro_session = lookup_ro_session(dlg->h_entry, &dlg->callid, 0, 0))) { ro_session_entry = &(ro_session_table->entries[ro_session->h_entry]); //if the Ro session is not active we don't need to do anything. This prevents //double processing for various dialog_terminated callback events. //If however, the call was never answered, then we can continue as normal ro_session_lock(ro_session_table, ro_session_entry); LM_DBG("processing dlg_terminated in Ro and session [%.*s] has active = %d", ro_session->ro_session_id.len, ro_session->ro_session_id.s, ro_session->active); if ((!ro_session->active && (ro_session->start_time != 0)) || (ro_session->ccr_sent == 1)) { unref_ro_session_unsafe(ro_session,1,ro_session_entry); LM_DBG("CCR already sent or Ro Session is not active, but may have been answered [%d]\n", (int)ro_session->start_time); ro_session_unlock(ro_session_table, ro_session_entry); return; } if (ro_session->active) { // if the call was never activated, there's no timer to remove int ret = remove_ro_timer(&ro_session->ro_tl); if (ret < 0) { LM_CRIT("unable to unlink the timer on ro_session %p [%.*s]\n", ro_session, ro_session->ro_session_id.len, ro_session->ro_session_id.s); } else if (ret > 0) { LM_WARN("inconsistent ro timer data on ro_session %p [%.*s]\n", ro_session, ro_session->ro_session_id.len, ro_session->ro_session_id.s); } else { unref++; } } LM_DBG("Sending CCR STOP on Ro_Session [%p]\n", ro_session); send_ccr_stop_with_param(ro_session, termcode, &s_reason); ro_session->active = -1; //deleted.... terminated .... ro_session->ccr_sent = 1; // counter_add(ims_charging_cnts_h.active_ro_sessions, -1); if (ro_db_mode == DB_MODE_REALTIME) { ro_session->flags |= RO_SESSION_FLAG_DELETED; if (update_ro_dbinfo_unsafe(ro_session) != 0) { LM_ERR("Unable to update Ro session in DB...continuing\n"); } } //ro_session->start_time; unref_ro_session_unsafe(ro_session, 1+unref, ro_session_entry); //lock already acquired //unref_ro_session_unsafe(ro_session, 2+unref, ro_session_entry); //lock already acquired ro_session_unlock(ro_session_table, ro_session_entry); //} //} } }