Skip to content

Commit

Permalink
modules/ims_charging: added ability to send vendor-specific charge in…
Browse files Browse the repository at this point in the history
…formation

    - this allows for call dispositions to be stored and reported on in OCS
    - by default this id disabled (modparam - vendor_specific_chargeinfo=0)
  • Loading branch information
jaybeepee committed Jan 20, 2016
1 parent c084e0b commit e010736
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 16 deletions.
45 changes: 35 additions & 10 deletions modules/ims_charging/dialog.c
Expand Up @@ -16,22 +16,26 @@ 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);
}
}

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();
Expand Down Expand Up @@ -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");
Expand All @@ -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);
Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion modules/ims_charging/dialog.h
Expand Up @@ -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);
Expand Down
38 changes: 36 additions & 2 deletions modules/ims_charging/ims_ro.c
Expand Up @@ -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;
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down
7 changes: 6 additions & 1 deletion modules/ims_charging/ims_ro.h
Expand Up @@ -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;
Expand All @@ -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 */
99 changes: 98 additions & 1 deletion modules/ims_charging/mod.c
Expand Up @@ -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;
Expand Down Expand Up @@ -84,17 +87,20 @@ 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);


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

Expand Down Expand Up @@ -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 }
};

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
2 changes: 1 addition & 1 deletion modules/ims_charging/ro_session_hash.c
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions modules/ims_charging/ro_session_hash.h
Expand Up @@ -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 */
Expand Down

0 comments on commit e010736

Please sign in to comment.