Selaa lähdekoodia

dialg(k): possibility to send keep alives for dialogs

- keepalives are OPTIONS requests
- if keepalive request get 408 ot 481, dialog is timed out after 10 secs
Daniel-Constantin Mierla 13 vuotta sitten
vanhempi
commit
7d013ceb1e

+ 74 - 0
modules_k/dialog/dialog.c

@@ -70,6 +70,7 @@
 #include "../../lib/kcore/kstats_wrapper.h"
 #include "../../mem/mem.h"
 #include "../../lib/kmi/mi.h"
+#include "../../timer_proc.h"
 #include "../../lvalue.h"
 #include "../../parser/parse_to.h"
 #include "../../modules/tm/tm_load.h"
@@ -132,6 +133,10 @@ pv_spec_t timeout_avp;
 
 int dlg_db_mode_param = DB_MODE_NONE;
 
+str dlg_xavp_cfg = {0};
+int dlg_ka_timer = 0;
+int dlg_ka_interval = 0;
+
 /* db stuff */
 static str db_url = str_init(DEFAULT_DB_URL);
 static unsigned int db_update_period = DB_DEFAULT_UPDATE_PERIOD;
@@ -139,6 +144,8 @@ static unsigned int db_update_period = DB_DEFAULT_UPDATE_PERIOD;
 static int pv_get_dlg_count( struct sip_msg *msg, pv_param_t *param,
 		pv_value_t *res);
 
+void dlg_ka_timer_exec(unsigned int ticks, void* param);
+
 /* commands wrappers and fixups */
 static int fixup_profile(void** param, int param_no);
 static int fixup_get_profile2(void** param, int param_no);
@@ -151,6 +158,7 @@ static int w_get_profile_size3(struct sip_msg*, char*, char*, char*);
 static int w_dlg_isflagset(struct sip_msg *msg, char *flag, str *s2);
 static int w_dlg_resetflag(struct sip_msg *msg, char *flag, str *s2);
 static int w_dlg_setflag(struct sip_msg *msg, char *flag, char *s2);
+static int w_dlg_set_property(struct sip_msg *msg, char *prop, char *s2);
 static int w_dlg_manage(struct sip_msg*, char*, char*);
 static int w_dlg_bye(struct sip_msg*, char*, char*);
 static int w_dlg_refer(struct sip_msg*, char*, char*);
@@ -201,6 +209,8 @@ static cmd_export_t cmds[]={
 			0, ANY_ROUTE },
 	{"dlg_set_timeout", (cmd_function)w_dlg_set_timeout,  3,fixup_igp_all,
 			0, ANY_ROUTE },
+	{"dlg_set_property", (cmd_function)w_dlg_set_property,1,fixup_spve_null,
+			0, ANY_ROUTE },
 	{"load_dlg",  (cmd_function)load_dlg,   0, 0, 0, 0},
 	{0,0,0,0,0,0}
 };
@@ -254,6 +264,9 @@ static param_export_t mod_params[]={
 	{ "initial_cbs_inscript",  INT_PARAM, &initial_cbs_inscript     },
 	{ "send_bye",              INT_PARAM, &dlg_send_bye             },
 	{ "wait_ack",              INT_PARAM, &dlg_wait_ack             },
+	{ "xavp_cfg",              STR_PARAM, &dlg_xavp_cfg.s           },
+	{ "ka_timer",              INT_PARAM, &dlg_ka_timer             },
+	{ "ka_interval",           INT_PARAM, &dlg_ka_interval          },
 	{ 0,0,0 }
 };
 
@@ -478,6 +491,9 @@ static int mod_init(void)
 	vars_key_column.len = strlen(vars_key_column.s);
 	vars_value_column.len = strlen(vars_value_column.s);
 
+	if(dlg_xavp_cfg.s!=NULL)
+		dlg_xavp_cfg.len = strlen(dlg_xavp_cfg.s);
+
 	/* param checkings */
 	if (dlg_flag==-1) {
 		LM_ERR("no dlg flag set!!\n");
@@ -664,6 +680,8 @@ static int mod_init(void)
 	}
 
 	destroy_dlg_callbacks( DLGCB_LOADED );
+	if(dlg_ka_timer>0 && dlg_ka_interval>0)
+		register_sync_timers(1);
 
 	return 0;
 }
@@ -673,6 +691,15 @@ static int child_init(int rank)
 {
 	dlg_db_mode = dlg_db_mode_param;
 
+	if(rank==PROC_MAIN && dlg_ka_timer>0 && dlg_ka_interval>0)
+	{
+		if(fork_sync_timer(PROC_TIMER, "Dialog KA Timer", 1 /*socks flag*/,
+				dlg_ka_timer_exec, NULL, dlg_ka_interval /*sec*/)<0) {
+			LM_ERR("failed to start ka timer routine as process\n");
+			return -1; /* error */
+		}
+	}
+
 	if (rank==1) {
 		if_update_stat(dlg_enable_stats, active_dlgs, active_dlgs_cnt);
 		if_update_stat(dlg_enable_stats, early_dlgs, early_dlgs_cnt);
@@ -1106,6 +1133,53 @@ static int w_dlg_set_timeout(struct sip_msg *msg, char *pto, char *phe, char *ph
 	return 1;
 }
 
+static int w_dlg_set_property(struct sip_msg *msg, char *prop, char *s2)
+{
+	dlg_ctx_t *dctx;
+	dlg_cell_t *d;
+	str val;
+
+	if(fixup_get_svalue(msg, (gparam_t*)prop, &val)!=0)
+	{
+		LM_ERR("no property value\n");
+		return -1;
+	}
+	if(val.len<=0)
+	{
+		LM_ERR("empty property value\n");
+		return -1;
+	}
+	if ( (dctx=dlg_get_dlg_ctx())==NULL )
+		return -1;
+
+	if(val.len==6 && strncmp(val.s, "ka-src", 6)==0) {
+		dctx->iflags |= DLG_IFLAG_KA_SRC;
+		d = dlg_get_by_iuid(&dctx->iuid);
+		if(d!=NULL) {
+			d->iflags |= DLG_IFLAG_KA_SRC;
+			dlg_release(d);
+		}
+	} else if(val.len==6 && strncmp(val.s, "ka-dst", 6)==0) {
+		dctx->iflags |= DLG_IFLAG_KA_DST;
+		d = dlg_get_by_iuid(&dctx->iuid);
+		if(d!=NULL) {
+			d->iflags |= DLG_IFLAG_KA_DST;
+			dlg_release(d);
+		}
+	} else {
+		LM_ERR("unknown property value [%.*s]\n", val.len, val.s);
+		return -1;
+	}
+
+	return 1;
+}
+
+
+void dlg_ka_timer_exec(unsigned int ticks, void* param)
+{
+	dlg_ka_run(ticks);
+}
+
 static int fixup_dlg_bye(void** param, int param_no)
 {
 	char *val;

+ 3 - 0
modules_k/dialog/dlg_handlers.c

@@ -476,6 +476,9 @@ static void dlg_onreply(struct cell* t, int type, struct tmcb_params *param)
 		goto done;
 	}
 
+	if (new_state==DLG_STATE_CONFIRMED && old_state!=DLG_STATE_CONFIRMED)
+		dlg_ka_add(dlg);
+
 	if (new_state==DLG_STATE_CONFIRMED_NA &&
 	old_state!=DLG_STATE_CONFIRMED_NA && old_state!=DLG_STATE_CONFIRMED ) {
 		LM_DBG("dialog %p confirmed (ACK pending)\n",dlg);

+ 122 - 0
modules_k/dialog/dlg_hash.c

@@ -66,10 +66,14 @@
 #define MAX_LDG_LOCKS  2048
 #define MIN_LDG_LOCKS  2
 
+extern int dlg_ka_interval;
 
 /*! global dialog table */
 struct dlg_table *d_table = 0;
 
+dlg_ka_t **dlg_ka_list_head = NULL;
+dlg_ka_t **dlg_ka_list_tail = NULL;
+gen_lock_t *dlg_ka_list_lock = NULL;
 
 /*!
  * \brief Reference a dialog without locking
@@ -118,6 +122,98 @@ struct dlg_table *d_table = 0;
 		}\
 	}while(0)
 
+/**
+ * add item to keep-alive list
+ *
+ */
+int dlg_ka_add(dlg_cell_t *dlg)
+{
+	dlg_ka_t *dka;
+
+	if(dlg_ka_interval<=0)
+		return 0;
+	if(!(dlg->iflags & (DLG_IFLAG_KA_SRC | DLG_IFLAG_KA_SRC)))
+		return 0;
+
+	dka = (dlg_ka_t*)shm_malloc(sizeof(dlg_ka_t));
+	if(dka==NULL) {
+		LM_ERR("no more shm mem\n");
+		return -1;
+	}
+	memset(dka, 0, sizeof(dlg_ka_t));
+	dka->katime = get_ticks() + dlg_ka_interval;
+	dka->iuid.h_entry = dlg->h_entry;
+	dka->iuid.h_id = dlg->h_id;
+	dka->iflags = dlg->iflags;
+
+	lock_get(dlg_ka_list_lock);
+	if(*dlg_ka_list_tail!=NULL)
+		(*dlg_ka_list_tail)->next = dka;
+	if(*dlg_ka_list_head==NULL)
+		*dlg_ka_list_tail = dka;
+	*dlg_ka_list_tail = dka;
+	lock_release(dlg_ka_list_lock);
+	return 0;
+}
+
+/**
+ * run keep-alive list
+ *
+ */
+int dlg_ka_run(ticks_t ti)
+{
+	dlg_ka_t *dka;
+	dlg_cell_t *dlg;
+
+	if(dlg_ka_interval<=0)
+		return 0;
+
+	while(1) {
+		/* get head item */
+		lock_get(dlg_ka_list_lock);
+		if(*dlg_ka_list_head==NULL) {
+			lock_release(dlg_ka_list_lock);
+			return 0;
+		}
+		dka = *dlg_ka_list_head;
+		if(dka->katime>ti) {
+			lock_release(dlg_ka_list_lock);
+			return 0;
+		}
+		if(*dlg_ka_list_head == *dlg_ka_list_tail) {
+			*dlg_ka_list_head = NULL;
+			*dlg_ka_list_head = NULL;
+		}
+		*dlg_ka_list_head = dka->next;
+		lock_release(dlg_ka_list_lock);
+
+		/* send keep-alive for dka */
+		dlg = dlg_get_by_iuid(&dka->iuid);
+		if(dlg==NULL) {
+			shm_free(dka);
+			dka = NULL;
+		} else {
+			if(dka->iflags & DLG_IFLAG_KA_SRC)
+				dlg_send_ka(dlg, DLG_CALLER_LEG, 0);
+			if(dka->iflags & DLG_IFLAG_KA_DST)
+				dlg_send_ka(dlg, DLG_CALLEE_LEG, 0);
+			dlg_release(dlg);
+		}
+		/* append to tail */
+		if(dka!=NULL)
+		{
+			lock_get(dlg_ka_list_lock);
+			if(*dlg_ka_list_tail!=NULL)
+				(*dlg_ka_list_tail)->next = dka;
+			if(*dlg_ka_list_head==NULL)
+				*dlg_ka_list_tail = dka;
+			*dlg_ka_list_tail = dka;
+			lock_release(dlg_ka_list_lock);
+		}
+	}
+
+	return 0;
+}
 
 /*!
  * \brief Initialize the global dialog table
@@ -129,6 +225,25 @@ int init_dlg_table(unsigned int size)
 	unsigned int n;
 	unsigned int i;
 
+	dlg_ka_list_head = (dlg_ka_t **)shm_malloc(sizeof(dlg_ka_t *));
+	if(dlg_ka_list_head==NULL) {
+		LM_ERR("no more shm mem (h)\n");
+		goto error0;
+	}
+	dlg_ka_list_tail = (dlg_ka_t **)shm_malloc(sizeof(dlg_ka_t *));
+	if(dlg_ka_list_tail==NULL) {
+		LM_ERR("no more shm mem (t)\n");
+		goto error0;
+	}
+	*dlg_ka_list_head = NULL;
+	*dlg_ka_list_tail = NULL;
+	dlg_ka_list_lock = (gen_lock_t*)shm_malloc(sizeof(gen_lock_t));
+	if(dlg_ka_list_lock==NULL) {
+		LM_ERR("no more shm mem (l)\n");
+		goto error0;
+	}
+	lock_init(dlg_ka_list_lock);
+
 	d_table = (struct dlg_table*)shm_malloc
 		( sizeof(struct dlg_table) + size*sizeof(struct dlg_entry));
 	if (d_table==0) {
@@ -169,7 +284,14 @@ int init_dlg_table(unsigned int size)
 	return 0;
 error1:
 	shm_free( d_table );
+	d_table = NULL;
 error0:
+	if(dlg_ka_list_head!=NULL)
+		shm_free(dlg_ka_list_head);
+	if(dlg_ka_list_tail!=NULL)
+		shm_free(dlg_ka_list_tail);
+	dlg_ka_list_head = NULL;
+	dlg_ka_list_tail = NULL;
 	return -1;
 }
 

+ 14 - 0
modules_k/dialog/dlg_hash.h

@@ -45,6 +45,7 @@
 
 #include "../../locking.h"
 #include "../../lib/kmi/mi.h"
+#include "../../timer.h"
 #include "dlg_timer.h"
 #include "dlg_cb.h"
 
@@ -82,6 +83,8 @@
 
 /* internal flags stored in db */
 #define DLG_IFLAG_TIMEOUTBYE        (1<<0) /*!< send bye on time-out */
+#define DLG_IFLAG_KA_SRC            (1<<1) /*!< send keep alive to src */
+#define DLG_IFLAG_KA_DST            (1<<2) /*!< send keep alive to dst */
 
 #define DLG_CALLER_LEG         0 /*!< attribute that belongs to a caller leg */
 #define DLG_CALLEE_LEG         1 /*!< attribute that belongs to a callee leg */
@@ -154,6 +157,13 @@ typedef struct dlg_table
 } dlg_table_t;
 
 
+typedef struct dlg_ka {
+	dlg_iuid_t iuid;
+	ticks_t katime;
+	unsigned iflags;
+	struct dlg_ka *next;
+} dlg_ka_t;
+
 /*! global dialog table */
 extern dlg_table_t *d_table;
 
@@ -513,6 +523,10 @@ static inline int match_downstream_dialog(dlg_cell_t *dlg, str *callid, str *fta
 void dlg_run_event_route(dlg_cell_t *dlg, sip_msg_t *msg, int ostate, int nstate);
 
 
+int dlg_ka_add(dlg_cell_t *dlg);
+
+int dlg_ka_run(ticks_t ti);
+
 /*!
  * \brief Output a dialog via the MI interface
  * \param rpl MI node that should be filled

+ 104 - 1
modules_k/dialog/dlg_req_within.c

@@ -127,7 +127,7 @@ error:
 
 
 
-/*callback function to handle responses to the BYE request */
+/* callback function to handle responses to the BYE request */
 void bye_reply_cb(struct cell* t, int type, struct tmcb_params* ps){
 
 	struct dlg_cell* dlg;
@@ -201,6 +201,45 @@ void bye_reply_cb(struct cell* t, int type, struct tmcb_params* ps){
 }
 
 
+/* callback function to handle responses to the keep-alive request */
+void dlg_ka_cb(struct cell* t, int type, struct tmcb_params* ps){
+
+	dlg_cell_t* dlg;
+	dlg_iuid_t *iuid = NULL;
+
+	if(ps->param == NULL || *ps->param == NULL) {
+		LM_ERR("invalid parameter\n");
+		return;
+	}
+
+	if(ps->code < 200) {
+		LM_DBG("receiving a provisional reply\n");
+		return;
+	}
+
+	LM_DBG("receiving a final reply %d\n",ps->code);
+
+	iuid = (dlg_iuid_t*)(*ps->param);
+	dlg = dlg_get_by_iuid(iuid);
+	if(dlg==0) {
+		dlg_iuid_sfree(iuid);
+		return;
+	}
+
+	if(ps->code==408 || ps->code==481) {
+		if(update_dlg_timer(&dlg->tl, 10)<0) {
+			LM_ERR("failed to update dialog lifetime\n");
+			goto done;
+		}
+		dlg->lifetime = 10;
+		dlg->dflags |= DLG_FLAG_CHANGED;
+	}
+
+done:
+	dlg_unref(dlg, 1);
+	dlg_iuid_sfree(iuid);
+}
+
 
 static inline int build_extra_hdr(struct dlg_cell * cell, str *extra_hdrs,
 		str *str_hdr)
@@ -291,6 +330,70 @@ err:
 }
 
 
+/* send keep-alive
+ * dlg - pointer to a struct dlg_cell
+ * dir - direction: the request will be sent to:
+ * 		DLG_CALLER_LEG (0): caller
+ * 		DLG_CALLEE_LEG (1): callee
+ */
+int dlg_send_ka(dlg_cell_t *dlg, int dir, str *hdrs)
+{
+	uac_req_t uac_r;
+	dlg_t* di;
+	str met = {"OPTIONS", 7};
+	int result;
+	dlg_iuid_t *iuid = NULL;
+
+	/* do not send KA request for non-confirmed dialogs (not supported) */
+	if (dlg->state != DLG_STATE_CONFIRMED) {
+		LM_DBG("skipping non-confirmed dialogs\n");
+		return 0;
+	}
+
+	/* build tm dlg by direction */
+	if ((di = build_dlg_t(dlg, dir)) == 0){
+		LM_ERR("failed to create dlg_t\n");
+		goto err;
+	}
+
+	/* tm increases cseq value, decrease it no to make it invalid
+	 * - dialog is ended on timeout (408) or C/L does not exist (481) */
+	if(di->loc_seq.value>1)
+		di->loc_seq.value -= 2;
+	else
+		di->loc_seq.value -= 1;
+
+	LM_DBG("sending BYE to %s\n", (dir==DLG_CALLER_LEG)?"caller":"callee");
+
+	iuid = dlg_get_iuid_shm_clone(dlg);
+	if(iuid==NULL)
+	{
+		LM_ERR("failed to create dialog unique id clone\n");
+		goto err;
+	}
+
+	memset(&uac_r,'\0', sizeof(uac_req_t));
+	set_uac_req(&uac_r, &met, hdrs, NULL, di, TMCB_LOCAL_COMPLETED,
+				dlg_ka_cb, (void*)iuid);
+	result = d_tmb.t_request_within(&uac_r);
+
+	if(result < 0){
+		LM_ERR("failed to send the BYE request\n");
+		goto err;
+	}
+
+	free_tm_dlg(di);
+
+	LM_DBG("keep-alive sent to %s\n", (dir==0)?"caller":"callee");
+	return 0;
+
+err:
+	if(di)
+		free_tm_dlg(di);
+	return -1;
+}
+
+
 
 /*parameters from MI: h_entry, h_id of the requested dialog*/
 struct mi_root * mi_terminate_dlg(struct mi_root *cmd_tree, void *param ){

+ 1 - 0
modules_k/dialog/dlg_req_within.h

@@ -52,5 +52,6 @@ dlg_t* build_dlg_t(struct dlg_cell * cell, int dir);
 int free_tm_dlg(dlg_t *td);
 int dlg_bye(struct dlg_cell *dlg, str *hdrs, int side);
 int dlg_bye_all(struct dlg_cell *dlg, str *hdrs);
+int dlg_send_ka(dlg_cell_t *dlg, int dir, str *hdrs);
 
 #endif

+ 1 - 0
modules_k/dialog/dlg_var.h

@@ -32,6 +32,7 @@
 typedef struct _dlg_ctx {
 	int on;
 	unsigned int flags;
+	unsigned int iflags;
 	int to_route;
 	char to_route_name[DLG_TOROUTE_SIZE];
 	int to_bye;