Example #1
0
/* 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, call_time;
    long used_secs;
    int adjustment;
    str default_out_of_credit_hdrs = {"Reason: outofcredit\r\n", 21};

    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* ro_session = ((struct ro_session*) ((char *) (tl) - (unsigned long) (&((struct ro_session*) 0)->ro_tl)));
    LM_DBG("offset for ro_tl is [%lu] and ro_session id is [%.*s]\n", (unsigned long) (&((struct ro_session*) 0)->ro_tl), ro_session->ro_session_id.len, ro_session->ro_session_id.s);

    if (!ro_session) {
        LM_ERR("Can't find a session. This is bad");
        return;
    }

    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;
    //	}


    if(ro_session->is_final_allocation) {
        now = get_current_time_micro();
        used_secs = now - ro_session->last_event_timestamp;
        if((ro_session->reserved_secs - used_secs) > 0) {
            update_ro_timer(&ro_session->ro_tl, (ro_session->reserved_secs - used_secs));
            return;
        }
        else {
            ro_session->event_type = no_more_credit;
        }
    }

    switch (ro_session->event_type) {
        case answered:
            now = get_current_time_micro();
            used_secs = rint((now - ro_session->last_event_timestamp) / (float) 1000000);
            call_time = rint((now - ro_session->start_time) / (float) 1000000);

			if ((used_secs + ro_session->billed) < (call_time)) {
				adjustment = call_time - (used_secs + ro_session->billed);
				LM_DBG("Making adjustment for Ro interim timer by adding %d seconds\n", adjustment);
				used_secs += adjustment;
			}
			
            counter_add(ims_charging_cnts_h.billed_secs, used_secs);

            if (ro_session->callid.s != NULL && 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->asserted_identity.len,
                        ro_session->asserted_identity.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
				ro_session->billed += used_secs;
                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) / 1000000);
                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(ro_session, 1, 0);
                    return;
                }
                LM_ERR("Immediately killing call due to unknown error\n");
            }
            break;
        case delayed_delete:
            destroy_ro_session(ro_session);
            return;
        case pending:
            /* call is not answered yet. No point asking more credit. Just wait for dialog to progress somehow */
            return;
        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");
    }

    counter_inc(ims_charging_cnts_h.killed_calls);

    dlgb.lookup_terminate_dlg(ro_session->dlg_h_entry, ro_session->dlg_h_id, &default_out_of_credit_hdrs);
    return;
}
Example #2
0
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");
}
Example #3
0
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;

}
Example #4
0
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);


}
Example #5
0
static void resume_on_initial_ccr(int is_timeout, void *param, AAAMessage *cca, long elapsed_msecs) {
    Ro_CCA_t *ro_cca_data = NULL;
    struct cell *t = NULL;
    struct session_setup_data *ssd = (struct session_setup_data *) param;
    int error_code = RO_RETURN_ERROR;

    if (is_timeout) {
        counter_inc(ims_charging_cnts_h.ccr_timeouts);
        LM_ERR("Transaction timeout - did not get CCA\n");
        error_code = RO_RETURN_ERROR;
        goto error0;
    }

    counter_inc(ims_charging_cnts_h.ccr_replies_received);
    counter_add(ims_charging_cnts_h.ccr_response_time, elapsed_msecs);

    if (!cca) {
        LM_ERR("Error reserving credit for CCA.\n");
        error_code = RO_RETURN_ERROR;
        goto error0;
    }

    if (!ssd) {
        LM_ERR("Session lookup data is NULL.\n");
        error_code = RO_RETURN_ERROR;
        goto error0;
    }

    // we make sure the transaction exists
    if (tmb.t_lookup_ident(&t, ssd->tindex, ssd->tlabel) < 0) {
        LM_ERR("t_continue: transaction not found\n");
        error_code = RO_RETURN_ERROR;
        goto error0;
    }

    // we bring the list of AVPs of the transaction to the current context
    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);

    ro_cca_data = Ro_parse_CCA_avps(cca);

    if (!ro_cca_data) {
        LM_ERR("Could not parse CCA message response.\n");
        error_code = RO_RETURN_ERROR;
	create_cca_result_code(0);
        goto error0;
    }
    create_cca_result_code((int)ro_cca_data->resultcode);
    if (ro_cca_data->resultcode != 2001) {
        LM_ERR("Got bad CCA result code - reservation failed");
        error_code = RO_RETURN_FALSE;
        goto error1;
    }

    LM_DBG("Valid CCA response with time chunk of [%i] and validity [%i]\n",
            ro_cca_data->mscc->granted_service_unit->cc_time,
            ro_cca_data->mscc->validity_time);

    if (ro_cca_data->mscc->granted_service_unit->cc_time <= 0) {
        LM_DBG("got zero GSU.... reservation failed");
        error_code = RO_RETURN_FALSE;
        goto error1;
    }

    ssd->ro_session->last_event_timestamp = get_current_time_micro();
    ssd->ro_session->event_type = pending;
    ssd->ro_session->reserved_secs = ro_cca_data->mscc->granted_service_unit->cc_time;
    ssd->ro_session->valid_for = ro_cca_data->mscc->validity_time;
    ssd->ro_session->is_final_allocation = 0;

    if (ro_cca_data->mscc->final_unit_action && (ro_cca_data->mscc->final_unit_action->action == 0))
        ssd->ro_session->is_final_allocation = 1;

    Ro_free_CCA(ro_cca_data);

    LM_DBG("Freeing CCA message\n");
    cdpb.AAAFreeMessage(&cca);

    link_ro_session(ssd->ro_session, 0); 

    if (ro_db_mode == DB_MODE_REALTIME) {
        ssd->ro_session->flags |= RO_SESSION_FLAG_NEW;
        if (update_ro_dbinfo(ssd->ro_session) != 0) {
            LM_ERR("Failed to update ro_session in database... continuing\n");
        };
    }

    unref_ro_session(ssd->ro_session, 1); /* release our reference */

    create_cca_return_code(RO_RETURN_TRUE);

    if (t)
        tmb.unref_cell(t);

    tmb.t_continue(ssd->tindex, ssd->tlabel, ssd->action);
    shm_free(ssd);

    counter_inc(ims_charging_cnts_h.successful_initial_ccrs);

    return;

error1:
    Ro_free_CCA(ro_cca_data);

error0:
    LM_DBG("Trying to reserve credit on initial INVITE failed on cdp callback\n");
//    counter_add(ims_charging_cnts_h.active_ro_sessions, -1); /*we bumped active on the original initial ccr sent */
    counter_inc(ims_charging_cnts_h.failed_initial_ccrs);      /* drop by one as theoretically this is failed initial ccr */
    create_cca_return_code(error_code);

    if (!is_timeout && cca) {
        cdpb.AAAFreeMessage(&cca);
    }

    if (t)
        tmb.unref_cell(t);

    tmb.t_continue(ssd->tindex, ssd->tlabel, ssd->action);
    shm_free(ssd);
}