فهرست منبع

modules/ims_charging: fix charging error in race between interim update and final charge

jaybeepee 9 سال پیش
والد
کامیت
e745e18e52

+ 3 - 4
modules/ims_charging/dialog.c

@@ -72,19 +72,18 @@ void dlg_answered(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
     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);
+    LM_DBG("we were granted %d seconds (valid 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
+            ret = insert_ro_timer(&session->ro_tl, session->reserved_secs - (session->is_final_allocation?0: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
+            ret = insert_ro_timer(&session->ro_tl, session->valid_for - (session->is_final_allocation?0: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);
         }

+ 10 - 19
modules/ims_charging/ims_ro.c

@@ -79,23 +79,6 @@ void credit_control_session_callback(int event, void* session) {
     }
 }
 
-/**
- * Retrieves the SIP request that generated a diameter transaction
- * @param hash - the tm hash value for this request
- * @param label - the tm label value for this request
- * @returns the SIP request
- */
-struct sip_msg * trans_get_request_from_current_reply() {
-    struct cell *t;
-    t = tmb.t_gett();
-    if (!t || t == (void*) - 1) {
-        LM_ERR("trans_get_request_from_current_reply: Reply without transaction\n");
-        return 0;
-    }
-    if (t) return t->uas.request;
-    else return 0;
-}
-
 /**
  * Create and add an AVP to a list of AVPs.
  * @param list - the AVP list to add to
@@ -643,7 +626,9 @@ void send_ccr_interim(struct ro_session* ro_session, unsigned int used, unsigned
     }
 
     LM_DBG("Sending CCR Diameter message.\n");
-
+    ro_session->last_event_timestamp_backup = ro_session->last_event_timestamp;
+    ro_session->last_event_timestamp = get_current_time_micro(); /*this is to make sure that if we get a term request now that we don't double bill for this time we are about to bill for in the interim */
+    
     cdpb.AAASessionsUnlock(auth->hash);
 
     if (ro_forced_peer.len > 0) {
@@ -691,10 +676,12 @@ error:
 static void resume_on_interim_ccr(int is_timeout, void *param, AAAMessage *cca, long elapsed_msecs) {
     struct interim_ccr *i_req = (struct interim_ccr *) param;
     Ro_CCA_t * ro_cca_data = NULL;
+    int error_or_timeout = 0;
 
     if (is_timeout) {
         counter_inc(ims_charging_cnts_h.ccr_timeouts);
         LM_ERR("Transaction timeout - did not get CCA\n");
+        error_or_timeout = 1;
         goto error;
     }
 
@@ -703,11 +690,13 @@ static void resume_on_interim_ccr(int is_timeout, void *param, AAAMessage *cca,
 
     if (!i_req) {
         LM_ERR("This is so wrong: ro session is NULL\n");
+        error_or_timeout = 1;
         goto error;
     }
 
     if (cca == NULL) {
         LM_ERR("Error reserving credit for CCA.\n");
+        error_or_timeout = 1;
         goto error;
     }
 
@@ -715,11 +704,13 @@ static void resume_on_interim_ccr(int is_timeout, void *param, AAAMessage *cca,
 
     if (ro_cca_data == NULL) {
         LM_ERR("Could not parse CCA message response.\n");
+        error_or_timeout = 1;
         goto error;
     }
 
     if (ro_cca_data->resultcode != 2001) {
         LM_ERR("Got bad CCA result code [%d] - reservation failed", ro_cca_data->resultcode);
+        error_or_timeout = 1;
         goto error;
     } else {
         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);
@@ -753,7 +744,7 @@ error:
     }
 
 success:
-    resume_ro_session_ontimeout(i_req);
+    resume_ro_session_ontimeout(i_req, error_or_timeout);
 }
 
 long get_current_time_micro() {

+ 1 - 0
modules/ims_charging/ro_session_hash.h

@@ -65,6 +65,7 @@ struct ro_session {
     unsigned int h_id;
     time_t start_time;
     time_t last_event_timestamp;
+    time_t last_event_timestamp_backup;
     enum ro_session_event_type event_type;
     int auth_appid;
     int auth_session_type;

+ 6 - 4
modules/ims_charging/ro_timer.c

@@ -262,7 +262,7 @@ void ro_timer_routine(unsigned int ticks, void * attr) {
     }
 }
 
-void resume_ro_session_ontimeout(struct interim_ccr *i_req) {
+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;
@@ -277,7 +277,7 @@ void resume_ro_session_ontimeout(struct interim_ccr *i_req) {
     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 - i_req->ro_session->last_event_timestamp) / (float) 1000000);
+    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->new_credit > i_req->credit_valid_for) {
@@ -332,7 +332,9 @@ void resume_ro_session_ontimeout(struct interim_ccr *i_req) {
         /* 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.
@@ -430,8 +432,8 @@ void ro_session_ontimeout(struct ro_tl *tl) {
             counter_add(ims_charging_cnts_h.billed_secs, used_secs);
 
             if (ro_session->callid.s != NULL
-                    && ro_session->dlg_h_entry > 0
-                    && ro_session->dlg_h_id > 0
+                    && ro_session->dlg_h_entry >= 0U
+                    && ro_session->dlg_h_id >= 0U
                     && 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,

+ 1 - 1
modules/ims_charging/ro_timer.h

@@ -87,7 +87,7 @@ void ro_timer_routine(unsigned int ticks, void * attr);
  */
 void ro_session_ontimeout(struct ro_tl *tl);
 
-void resume_ro_session_ontimeout(struct interim_ccr *i_req);
+void resume_ro_session_ontimeout(struct interim_ccr *i_req, int timeour_or_error);
 
 #endif	/* RO_TIMER_H */