浏览代码

modules/ims_charging: added ability to send vendor-specific charge information
- this allows for call dispositions to be stored and reported on in OCS
- by default this id disabled (modparam - vendor_specific_chargeinfo=0)

jaybeepee 9 年之前
父节点
当前提交
e010736858

+ 35 - 10
modules/ims_charging/dialog.c

@@ -16,14 +16,19 @@ extern struct ims_charging_counters_h ims_charging_cnts_h;
 
 void dlg_callback_received(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) {
     LM_DBG("Received dialog callback event [%d]\n", type);
+    unsigned int termcode = 0;
     switch (type) {
         case DLGCB_CONFIRMED:
             dlg_answered(dlg, type, _params);
             break;
         case DLGCB_TERMINATED:
+            dlg_terminated(dlg, type, termcode, "normal call clearing", _params);
+            break;
         case DLGCB_FAILED:
+            dlg_terminated(dlg, type, termcode, "call failed", _params);
+            break;
         case DLGCB_EXPIRED:
-            dlg_terminated(dlg, type, _params);
+            dlg_terminated(dlg, type, termcode, "dialog timeout", _params);
             break;
         default:
             LM_WARN("Received unknown dialog callback [%d]\n", type);
@@ -31,7 +36,6 @@ void dlg_callback_received(struct dlg_cell *dlg, int type, struct dlg_cb_params
 }
 
 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();
@@ -116,19 +120,38 @@ void dlg_answered(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
 
 }
 
-void dlg_terminated(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) {
+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, lets send stop record\n", dlg);
+	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);
+		}
+	} 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");
@@ -155,14 +178,14 @@ void dlg_terminated(struct dlg_cell *dlg, int type, struct dlg_cb_params *_param
 				//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);
-				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);
+                                
+                                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_ERR("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);
@@ -178,8 +201,10 @@ void dlg_terminated(struct dlg_cell *dlg, int type, struct dlg_cb_params *_param
 				}
 
 				LM_DBG("Sending CCR STOP on Ro_Session [%p]\n", ro_session);
-				send_ccr_stop(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;

+ 1 - 1
modules/ims_charging/dialog.h

@@ -9,7 +9,7 @@
 extern int ro_timer_buffer;
 
 void dlg_callback_received(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params);
-void dlg_terminated(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params);
+void dlg_terminated(struct dlg_cell *dlg, int type, unsigned int termcode, char* reason, struct dlg_cb_params *_params);
 void dlg_answered(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params);
 void add_dlg_data_to_contact(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params);
 void remove_dlg_data_from_contact(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params);

+ 36 - 2
modules/ims_charging/ims_ro.c

@@ -42,6 +42,8 @@ extern cdp_avp_bind_t *cdp_avp;
 extern str ro_forced_peer;
 extern int ro_db_mode;
 extern struct ims_charging_counters_h ims_charging_cnts_h;
+extern int vendor_specific_id;
+extern int vendor_specific_chargeinfo;
 
 struct session_setup_data {
     struct ro_session *ro_session;
@@ -180,6 +182,21 @@ inline int Ro_add_termination_cause(AAAMessage *msg, unsigned int term_code) {
     return Ro_add_avp(msg, s.s, s.len, AVP_Termination_Cause, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);
 }
 
+inline int Ro_add_vendor_specific_termination_cause(AAAMessage *msg, unsigned int term_code) {
+    char x[4];
+    str s = {x, 4};
+    uint32_t code = htonl(term_code);
+    memcpy(x, &code, sizeof (uint32_t));
+
+    return Ro_add_avp(msg, s.s, s.len, VS_TERMCODE, AAA_AVP_FLAG_VENDOR_SPECIFIC, 10, AVP_DUPLICATE_DATA, __FUNCTION__);
+}
+
+inline int Ro_add_vendor_specific_termination_reason(AAAMessage *msg, str* reason) {
+    return Ro_add_avp(msg, reason->s, reason->len, VS_TERMREASON, AAA_AVP_FLAG_VENDOR_SPECIFIC, 10, AVP_DUPLICATE_DATA, __FUNCTION__);
+}
+
+
+
 /* called only when building stop record AVPS */
 inline int Ro_add_multiple_service_credit_Control_stop(AAAMessage *msg, int used_unit, int active_rating_group, int active_service_identifier) {
     char x[4];
@@ -745,7 +762,7 @@ long get_current_time_micro() {
     return tv.tv_sec*1000000 + tv.tv_usec;
 }
 
-void send_ccr_stop(struct ro_session *ro_session) {
+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;
@@ -890,6 +907,16 @@ void send_ccr_stop(struct ro_session *ro_session) {
         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) {
@@ -956,6 +983,11 @@ static void resume_on_termination_ccr(int is_timeout, void *param, AAAMessage *c
     }
 
     counter_inc(ims_charging_cnts_h.successful_final_ccrs);
+    Ro_free_CCA(ro_cca_data);
+    if (!is_timeout && cca) {
+        cdpb.AAAFreeMessage(&cca);
+    }
+    return;
 
 error:
     counter_inc(ims_charging_cnts_h.failed_final_ccrs);      
@@ -1162,6 +1194,7 @@ int Ro_Send_CCR(struct sip_msg *msg, struct dlg_cell *dlg, int dir, int reservat
     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) {
@@ -1174,6 +1207,7 @@ int Ro_Send_CCR(struct sip_msg *msg, struct dlg_cell *dlg, int dir, int reservat
 
     if (ret != 1) {
         LM_ERR("Failed to send Diameter CCR\n");
+//        new_session->ccr_sent = 0;
         goto error;
     }
 
@@ -1290,7 +1324,7 @@ static void resume_on_initial_ccr(int is_timeout, void *param, AAAMessage *cca,
     LM_DBG("Freeing CCA message\n");
     cdpb.AAAFreeMessage(&cca);
 
-    link_ro_session(ssd->ro_session, 1); /* create extra ref for the fact that dialog has a handle in the callbacks */
+    link_ro_session(ssd->ro_session, 0); 
 
     if (ro_db_mode == DB_MODE_REALTIME) {
         ssd->ro_session->flags |= RO_SESSION_FLAG_NEW;

+ 6 - 1
modules/ims_charging/ims_ro.h

@@ -6,6 +6,11 @@
 #include "../dialog_ng/dlg_hash.h"
 #include "ro_session_hash.h"
 
+typedef enum {
+    VS_TERMCODE = 3,
+    VS_TERMREASON = 2
+} vs_term_avp;
+
 struct interim_ccr {
 	struct ro_session* ro_session;
 	int new_credit;
@@ -19,7 +24,7 @@ int Ro_Send_CCR(struct sip_msg *msg, struct dlg_cell *dlg, int dir, int reservat
 	    str *incoming_trunk_id, str *outgoing_trunk_id, str *enb_cell_id, cfg_action_t* action, unsigned int tindex, unsigned int tlabel);
 long get_current_time_micro();
 void send_ccr_interim(struct ro_session* ro_session, unsigned int used, unsigned int reserve);
-void send_ccr_stop(struct ro_session *ro_session);
+void send_ccr_stop_with_param(struct ro_session *ro_session, unsigned int code, str* reason);
 int get_direction_as_int(str* direction);
 
 #endif /* CLIENT_RF_IMS_RO_H */

+ 98 - 1
modules/ims_charging/mod.c

@@ -33,6 +33,9 @@ char* ro_service_context_id_ext_s = "ext";
 char* ro_service_context_id_mnc_s = "01";
 char* ro_service_context_id_mcc_s = "001";
 char* ro_service_context_id_release_s = "8";
+int	termination_code = 0;
+int vendor_specific_id = 10;
+int vendor_specific_chargeinfo = 0;
 static int ro_session_hash_size = 4096;
 int ro_timer_buffer = 5;
 int interim_request_credits = 30;
@@ -84,6 +87,7 @@ static int mod_child_init(int);
 static void mod_destroy(void);
 
 static int w_ro_ccr(struct sip_msg *msg, char* route_name, char* direction, int reservation_units, char* incoming_trunk_id, char* outgoing_trunk_id);
+static int w_ro_ccr_stop(struct sip_msg *msg, char* direction, char* _code, char* _reason);
 //void ro_session_ontimeout(struct ro_tl *tl);
 
 
@@ -91,10 +95,12 @@ int create_response_avp_string(char* name, str* val);
 static int w_ro_set_session_id_avp(struct sip_msg *msg, char *str1, char *str2);
 
 static int ro_fixup(void **param, int param_no);
+static int ro_fixup_stop(void **param, int param_no);
 
 static cmd_export_t cmds[] = {
 		{ "Ro_CCR", 	(cmd_function) w_ro_ccr, 5, ro_fixup, 0, REQUEST_ROUTE },
-                { "Ro_set_session_id_avp", 	(cmd_function) w_ro_set_session_id_avp, 0, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE },
+		{ "Ro_CCR_Stop",(cmd_function) w_ro_ccr_stop, 3, ro_fixup_stop, 0, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE},
+        { "Ro_set_session_id_avp", 	(cmd_function) w_ro_set_session_id_avp, 0, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE },
 		{ 0, 0, 0, 0, 0, 0 }
 };
 
@@ -127,6 +133,8 @@ static param_export_t params[] = {
 		{ "db_mode",			INT_PARAM,			&ro_db_mode_param		},
 		{ "db_url",			PARAM_STRING,			&db_url 			},
 		{ "db_update_period",		INT_PARAM,			&db_update_period		},
+		{ "vendor_specific_chargeinfo",		INT_PARAM,	&vendor_specific_chargeinfo		}, /* VSI for extra charing info in Ro */
+		{ "vendor_specific_id",		INT_PARAM,			&vendor_specific_id		}, /* VSI for extra charing info in Ro */
 		{ 0, 0, 0 }
 };
 
@@ -355,11 +363,93 @@ static int w_ro_set_session_id_avp(struct sip_msg *msg, char *str1, char *str2)
     //set avp response with session id
     res = create_response_avp_string("ro_session_id", &ro_session->ro_session_id);
     dlgb.release_dlg(dlg);
+    unref_ro_session(ro_session, 1);
     return res;
 }
 
+static int w_ro_ccr_stop(struct sip_msg *msg, char* c_direction, char* _code, char* _reason) {
+    struct ro_session* ro_session;
+    struct ro_session_entry *ro_session_entry;
+    unsigned int h_entry;
+    str s_code, s_reason;
+    unsigned int code;
+    int dir = 0; /*any side*/
+
+    LM_DBG("Inside Ro_CCR_Stop with direction [%s]\n", c_direction);
+    if (strlen(c_direction) == 4) {
+        if (c_direction[0] == 'O' || c_direction[0] == 'o') {
+            dir = RO_ORIG_DIRECTION;
+        } else {
+            dir = RO_TERM_DIRECTION;
+        }
+    } else {
+        LM_ERR("Unknown direction [%s] to terminate\n", c_direction);
+        return RO_RETURN_FALSE;
+    }
+    struct dlg_cell* dlg = dlgb.get_dlg(msg);
+    if (!dlg) {
+        LM_ERR("Unable to find dialog to send CCR STOP record\n");
+        return RO_RETURN_ERROR;
+    }
 
+    if (get_str_fparam(&s_code, msg, (fparam_t*) _code) < 0) {
+        LM_ERR("failed to get code\n");
+        return RO_RETURN_ERROR;
+    }
+    LM_DBG("Code is [%.*s]\n", s_code.len, s_code.s);
+    if (get_str_fparam(&s_reason, msg, (fparam_t*) _reason) < 0) {
+        LM_ERR("failed to get reason\n");
+        return RO_RETURN_ERROR;
+    }
 
+    if (str2int(&s_code, &code) != 0) {
+        LM_ERR("Bad response code: [%.*s]\n", s_code.len, s_code.s);
+        return RO_RETURN_FALSE;
+    }
+
+//    switch (code) {
+//        case 486:
+//            termcode = VS_TERMCODE_BUSYHERE;
+//            break;
+//        case 487:
+//            termcode = VS_TERMCODE_CANCELLED;
+//            break;
+//        case 480:
+//        case 408:
+//            /* subscriber not available */
+//            termcode = VS_TERMCODE_NOTFOUND;
+//            break;
+//    }
+
+    LM_DBG("Sending Stop record with code [%d] and reason [%.*s]\n", code, s_reason.len, s_reason.s);
+
+    LM_DBG("Found DLG [%d : %d]\n", dlg->h_id, dlg->h_entry);
+
+    ro_session = lookup_ro_session(dlg->h_entry, &dlg->callid, dir, 0);
+    if (ro_session == NULL) {
+        LM_DBG("no ro_session - ignoring\n");
+        return RO_RETURN_TRUE;
+    }
+    h_entry = ro_session->h_entry;
+    ro_session_entry = &(ro_session_table->entries[h_entry]);
+
+    ro_session_lock(ro_session_table, ro_session_entry);
+    
+    if (ro_session->ccr_sent == 1) {
+        LM_DBG("Ro CCR already sent for session [%.*s]\n", ro_session->ro_session_id.len, ro_session->ro_session_id.s);
+        goto done;
+    }
+    send_ccr_stop_with_param(ro_session, code, &s_reason);
+    //TODO = check the CCR was sent successfully.
+    LM_DBG("Setting Ro session [%.*s] ccr_sent to 1\n", ro_session->ro_session_id.len, ro_session->ro_session_id.s);
+    ro_session->ccr_sent = 1;
+    ro_session->active = -1;
+//    counter_add(ims_charging_cnts_h.active_ro_sessions, -1);
+done:
+    unref_ro_session_unsafe(ro_session, 1, ro_session_entry);
+    ro_session_unlock(ro_session_table, ro_session_entry);
+    return RO_RETURN_TRUE;
+}
 
 static int w_ro_ccr(struct sip_msg *msg, char* c_route_name, char* c_direction, int reservation_units, char* c_incoming_trunk_id, char* c_outgoing_trunk_id) {
 	/* PSEUDOCODE/NOTES
@@ -544,3 +634,10 @@ static int ro_fixup(void **param, int param_no) {
 	
 	return 0;
 }
+
+static int ro_fixup_stop(void **param, int param_no) {
+	if (param_no == 2 || param_no == 3) {
+		return fixup_var_pve_12(param, param_no);
+	}
+	return 0;
+}

+ 1 - 1
modules/ims_charging/ro_session_hash.c

@@ -227,7 +227,7 @@ struct ro_session* build_new_ro_session(int direction, int auth_appid, int auth_
 
     new_ro_session->h_entry = dlg_h_entry; /* we will use the same entry ID as the dlg - saves us using our own hash function */
     new_ro_session->h_id = 0;
-    new_ro_session->ref = 0;
+    new_ro_session->ref = 1;
     
     new_ro_session->rating_group = active_rating_group;
     new_ro_session->service_identifier = active_service_identifier;

+ 1 - 0
modules/ims_charging/ro_session_hash.h

@@ -76,6 +76,7 @@ struct ro_session {
     int service_identifier;
     unsigned int is_final_allocation;
     long billed;
+    unsigned int ccr_sent;
 };
 
 /*! entries in the main ro_session table */