فهرست منبع

tm: Reason header generation for local CANCELs

When cancel-ing branches due to final reply, 2xx or 6xx, add a
Reason header (according to RFC3326).

E.g.: Reason: SIP;cause=200

Internal implementation notes: the branch_bm_t/cancel_bitmap
parameter of many of the reply and cancel functions was replaced
with a bigger structure that contains all the information
associated with the cancel: the cancel branch bitmap, the reason
and reason text (optional) or the  received cancel message (if
any).
Note that the current commit does not implement copying Reason
headers from a received CANCEL, it deals only with CANCEL
generated locally because of a final reply, 2xx or 6xx.
Andrei Pelinescu-Onciul 15 سال پیش
والد
کامیت
70d5b1b4c1
8فایلهای تغییر یافته به همراه221 افزوده شده و 78 حذف شده
  1. 27 20
      modules/tm/t_cancel.c
  2. 5 2
      modules/tm/t_cancel.h
  3. 2 1
      modules/tm/t_fwd.c
  4. 102 11
      modules/tm/t_msgbuilder.c
  5. 4 2
      modules/tm/t_msgbuilder.h
  6. 42 36
      modules/tm/t_reply.c
  7. 35 2
      modules/tm/t_reply.h
  8. 4 4
      modules/tm/timer.c

+ 27 - 20
modules/tm/t_cancel.c

@@ -46,6 +46,7 @@
  *              reflecting its purpose
  *             prepare_to_cancel() takes now an additional skip_branches
  *              bitmap parameter (andrei)
+ * 2010-02-26  cancel reason (rfc3326) basic support (andrei)
  */
 
 #include <stdio.h> /* for FILE* in fifo_uac_cancel */
@@ -102,13 +103,14 @@ void prepare_to_cancel(struct cell *t, branch_bm_t *cancel_bm,
 
 /* cancel branches scheduled for deletion
  * params: t          - transaction
- *          cancel_bm - bitmap with the branches that are supposed to be 
- *                       canceled 
+ *          cancel_data - structure filled with the cancel bitmap (bitmap with
+ *                       the branches that are supposed to be canceled) and
+ *                       the cancel reason.
  *          flags     - how_to_cancel flags, see cancel_branch()
  * returns: bitmap with the still active branches (on fr timer)
- * WARNING: always fill cancel_bm using prepare_to_cancel(), supplying values
- *          in any other way is a bug*/
-int cancel_uacs( struct cell *t, branch_bm_t cancel_bm, int flags)
+ * WARNING: always fill cancel_data->cancel_bitmap using prepare_to_cancel(),
+ *          supplying values in any other way is a bug*/
+int cancel_uacs( struct cell *t, struct cancel_info* cancel_data, int flags)
 {
 	int i;
 	int ret;
@@ -117,10 +119,11 @@ int cancel_uacs( struct cell *t, branch_bm_t cancel_bm, int flags)
 	ret=0;
 	/* cancel pending client transactions, if any */
 	for( i=0 ; i<t->nr_of_outgoings ; i++ ) 
-		if (cancel_bm & (1<<i)){
+		if (cancel_data->cancel_bitmap & (1<<i)){
 			r=cancel_branch(
 				t,
 				i,
+				&cancel_data->reason,
 				flags | ((t->uac[i].request.buffer==NULL)?
 					F_CANCEL_B_FAKE_REPLY:0) /* blind UAC? */
 			);
@@ -131,7 +134,7 @@ int cancel_uacs( struct cell *t, branch_bm_t cancel_bm, int flags)
 
 int cancel_all_uacs(struct cell *trans, int how)
 {
-	branch_bm_t cancel_bm;
+	struct cancel_info cancel_data;
 	int i,j;
 
 #ifdef EXTRA_DEBUG
@@ -139,10 +142,10 @@ int cancel_all_uacs(struct cell *trans, int how)
 #endif
 	DBG("Canceling T@%p [%u:%u]\n", trans, trans->hash_index, trans->label);
 	
-	cancel_bm=0;
-	prepare_to_cancel(trans, &cancel_bm, 0);
+	init_cancel_info(&cancel_data);
+	prepare_to_cancel(trans, &cancel_data.cancel_bitmap, 0);
 	 /* tell tm to cancel the call */
-	i=cancel_uacs(trans, cancel_bm, how);
+	i=cancel_uacs(trans, &cancel_data, how);
 	
 	if (how & F_CANCEL_UNREF)
 #ifndef TM_DEL_UNREF
@@ -172,6 +175,7 @@ int cancel_all_uacs(struct cell *trans, int how)
  *
  * params:  t - transaction
  *          branch - branch number to be canceled
+ *          reason - cancel reason structure
  *          flags - howto cancel: 
  *                   F_CANCEL_B_KILL - will completely stop the 
  *                     branch (stops the timers), use with care
@@ -202,13 +206,14 @@ int cancel_all_uacs(struct cell *trans, int how)
  *          - checking for buffer==0 under REPLY_LOCK is no enough, an 
  *           atomic_cmpxhcg or atomic_get_and_set _must_ be used.
  */
-int cancel_branch( struct cell *t, int branch, int flags )
+int cancel_branch( struct cell *t, int branch, struct cancel_reason* reason,
+					int flags )
 {
 	char *cancel;
 	unsigned int len;
 	struct retr_buf *crb, *irb;
 	int ret;
-	branch_bm_t tmp_bm;
+	struct cancel_info tmp_cd;
 	void* pcbuf;
 
 	crb=&t->uac[branch].local_cancel;
@@ -236,7 +241,7 @@ int cancel_branch( struct cell *t, int branch, int flags )
 			atomic_set_long(pcbuf, 0);
 			if (flags & F_CANCEL_B_FAKE_REPLY){
 				LOCK_REPLIES(t);
-				if (relay_reply(t, FAKED_REPLY, branch, 487, &tmp_bm, 1) == 
+				if (relay_reply(t, FAKED_REPLY, branch, 487, &tmp_cd, 1) == 
 										RPS_ERROR){
 					return -1;
 				}
@@ -257,7 +262,7 @@ int cancel_branch( struct cell *t, int branch, int flags )
 				if (flags & F_CANCEL_B_FAKE_REPLY){
 					stop_rb_timers( irb ); /* stop even the fr timer */
 					LOCK_REPLIES(t);
-					if (relay_reply(t, FAKED_REPLY, branch, 487, &tmp_bm, 1)== 
+					if (relay_reply(t, FAKED_REPLY, branch, 487, &tmp_cd, 1)== 
 											RPS_ERROR){
 						return -1;
 					}
@@ -272,10 +277,12 @@ int cancel_branch( struct cell *t, int branch, int flags )
 
 	if (cfg_get(tm, tm_cfg, reparse_invite)) {
 		/* build the CANCEL from the INVITE which was sent out */
-		cancel = build_local_reparse(t, branch, &len, CANCEL, CANCEL_LEN, &t->to);
+		cancel = build_local_reparse(t, branch, &len, CANCEL, CANCEL_LEN,
+									 &t->to, reason);
 	} else {
 		/* build the CANCEL from the reveived INVITE */
-		cancel = build_local(t, branch, &len, CANCEL, CANCEL_LEN, &t->to);
+		cancel = build_local(t, branch, &len, CANCEL, CANCEL_LEN, &t->to,
+							 reason);
 	}
 	if (!cancel) {
 		LOG(L_ERR, "ERROR: attempt to build a CANCEL failed\n");
@@ -339,7 +346,7 @@ void rpc_cancel(rpc_t* rpc, void* c)
 {
 	struct cell *trans;
 	static char cseq[128], callid[128];
-	branch_bm_t cancel_bm;
+	struct cancel_info cancel_data;
 	int i,j;
 
 	str cseq_s;   /* cseq */
@@ -347,7 +354,7 @@ void rpc_cancel(rpc_t* rpc, void* c)
 
 	cseq_s.s=cseq;
 	callid_s.s=callid;
-	cancel_bm=0;
+	init_cancel_info(&cancel_data);
 
 	if (rpc->scan(c, "SS", &callid_s, &cseq_s) < 2) {
 		rpc->fault(c, 400, "Callid and CSeq expected as parameters");
@@ -360,10 +367,10 @@ void rpc_cancel(rpc_t* rpc, void* c)
 		return;
 	}
 	/*  find the branches that need cancel-ing */
-	prepare_to_cancel(trans, &cancel_bm, 0);
+	prepare_to_cancel(trans, &cancel_data.cancel_bitmap, 0);
 	 /* tell tm to cancel the call */
 	DBG("Now calling cancel_uacs\n");
-	i=cancel_uacs(trans, cancel_bm, 0); /* don't fake 487s, 
+	i=cancel_uacs(trans, &cancel_data, 0); /* don't fake 487s, 
 										 just wait for timeout */
 	
 	/* t_lookup_callid REF`d the transaction for us, we must UNREF here! */

+ 5 - 2
modules/tm/t_cancel.h

@@ -35,6 +35,7 @@
  *  2009-07-14  should_cancel_branch() renamed to prepare_cancel_branch() to
  *               better reflect its purpose
  *              which_cancel() renamed to prepare_to_cancel() (andrei)
+ * 2010-02-26  cancel reason (rfc3326) basic support (andrei)
  */
 
 
@@ -46,6 +47,7 @@
 #include "../../atomic_ops.h"
 #include "defs.h"
 #include "h_table.h"
+#include "t_reply.h"
 
 
 /* a buffer is empty but cannot be used by anyone else;
@@ -79,9 +81,10 @@
 
 
 void prepare_to_cancel(struct cell *t, branch_bm_t *cancel_bm, branch_bm_t s);
-int cancel_uacs( struct cell *t, branch_bm_t cancel_bm, int flags );
+int cancel_uacs( struct cell *t, struct cancel_info* cancel_data, int flags );
 int cancel_all_uacs(struct cell *trans, int how);
-int cancel_branch( struct cell *t, int branch, int flags );
+int cancel_branch( struct cell *t, int branch, struct cancel_reason* reason,
+					int flags );
 
 typedef int(*cancel_uacs_f)( struct cell *t, branch_bm_t cancel_bm,
 								int flags );

+ 2 - 1
modules/tm/t_fwd.c

@@ -918,7 +918,7 @@ int e2e_cancel_branch( struct sip_msg *cancel_msg, struct cell *t_cancel,
 			"thus lumps are not applied to the message!\n");
 		}
 		shbuf=build_local_reparse( t_invite, branch, &len, CANCEL,
-									CANCEL_LEN, &t_invite->to);
+									CANCEL_LEN, &t_invite->to, 0);
 		if (unlikely(!shbuf)) {
 			LOG(L_ERR, "e2e_cancel_branch: printing e2e cancel failed\n");
 			ret=ser_error=E_OUT_OF_MEM;
@@ -1011,6 +1011,7 @@ void e2e_cancel( struct sip_msg *cancel_msg,
 			ret=cancel_branch(
 				t_invite,
 				i,
+				0,
 				cfg_get(tm,tm_cfg, cancel_b_flags)
 					| ((t_invite->uac[i].request.buffer==NULL)?
 						F_CANCEL_B_FAKE_REPLY:0) /* blind UAC? */

+ 102 - 11
modules/tm/t_msgbuilder.c

@@ -46,6 +46,7 @@
  *               resolving nexthop twice (andrei)
  * 2007-05-28: build_local_reparse() is introdued: it uses the outgoing
  *             INVITE as a source to construct a CANCEL or ACK (Miklos)
+ * 2010-02-26  cancel reason (rfc3326) basic support (andrei)
  */
 
 #include "defs.h"
@@ -72,6 +73,13 @@
 #include "../../cfg_core.h" /* cfg_get(core, core_cfg, use_dns_failover) */
 #endif
 
+
+/* reason building blocks (see rfc3326) */
+#define REASON_PREFIX "Reason: SIP;cause="
+#define REASON_PREFIX_LEN (sizeof(REASON_PREFIX)-1)
+#define REASON_TEXT ";text="
+#define REASON_TEXT_LEN (sizeof(REASON_TEXT)-1)
+
 /* convenience macros */
 #define memapp(_d,_s,_len) \
 	do{\
@@ -84,7 +92,8 @@
    customers of this function are local ACK and local CANCEL
  */
 char *build_local(struct cell *Trans,unsigned int branch,
-	unsigned int *len, char *method, int method_len, str *to)
+	unsigned int *len, char *method, int method_len, str *to,
+	struct cancel_reason* reason)
 {
 	char                *cancel_buf, *p, *via;
 	unsigned int         via_len;
@@ -94,6 +103,7 @@ char *build_local(struct cell *Trans,unsigned int branch,
 	str branch_str;
 	str via_id;
 	struct hostport hp;
+	int reason_len, code_len;
 
 	/* init */
 	via_id.s=0;
@@ -157,7 +167,24 @@ char *build_local(struct cell *Trans,unsigned int branch,
 		*len += user_agent_hdr.len + CRLF_LEN;
 	}
 	/* Content Length, EoM */
-	*len+=CONTENT_LENGTH_LEN+1 + CRLF_LEN + CRLF_LEN;
+	*len+=CONTENT_LENGTH_LEN+1 + CRLF_LEN;
+	reason_len = 0;
+	/* compute reason size */
+	if (reason && reason->cause != CANCEL_REAS_UNKNOWN){
+		if (likely(reason->cause > 0)){
+			/* Reason: SIP;cause=<reason->cause>[;text=<reason->u.text.s>] */
+			reason_len = REASON_PREFIX_LEN + USHORT2SBUF_MAX_LEN +
+				(reason->u.text.s?
+					REASON_TEXT_LEN + 1 + reason->u.text.len + 1 : 0) +
+				CRLF_LEN;
+		} else if (reason->cause == CANCEL_REAS_RCVD_CANCEL &&
+					reason->u.e2e_cancel) {
+			/* FIXME: TODO */
+		} else if (unlikely(reason->cause != -1))
+			BUG("unhandled reason cause %d\n", reason->cause);
+	}
+	*len+= reason_len;
+	*len+= CRLF_LEN; /* end of msg. */
 
 	cancel_buf=shm_malloc( *len+1 );
 	if (!cancel_buf)
@@ -197,9 +224,29 @@ char *build_local(struct cell *Trans,unsigned int branch,
 		append_str(p, user_agent_hdr.s, user_agent_hdr.len );
 		append_str(p, CRLF, CRLF_LEN );
 	}
-	/* Content Length, EoM */
-	append_str(p, CONTENT_LENGTH "0" CRLF CRLF ,
-		CONTENT_LENGTH_LEN+1 + CRLF_LEN + CRLF_LEN);
+	/* Content Length */
+	append_str(p, CONTENT_LENGTH "0" CRLF, CONTENT_LENGTH_LEN + 1 + CRLF_LEN);
+	/* add reason if needed */
+	if (reason_len) {
+		if (likely(reason->cause > 0)) {
+			append_str(p, REASON_PREFIX, REASON_PREFIX_LEN);
+			code_len=ushort2sbuf(reason->cause, p,
+									*len-(int)(p-cancel_buf));
+			if (unlikely(code_len==0))
+				BUG("not enough space to write reason code");
+			p+=code_len;
+			if (reason->u.text.s){
+				append_str(p, REASON_TEXT, REASON_TEXT_LEN);
+				*p='"'; p++;
+				append_str(p, reason->u.text.s, reason->u.text.len);
+				*p='"'; p++;
+			}
+			append_str(p, CRLF, CRLF_LEN);
+		} else if (reason->cause == CANCEL_REAS_RCVD_CANCEL) {
+			/* FIXME: handle cancel */
+		}
+	}
+	append_str(p, CRLF, CRLF_LEN); /* msg. end */
 	*p=0;
 
 	pkg_free(via);
@@ -217,7 +264,8 @@ error:
  * Can not be used to build other type of requests!
  */
 char *build_local_reparse(struct cell *Trans,unsigned int branch,
-	unsigned int *len, char *method, int method_len, str *to)
+	unsigned int *len, char *method, int method_len, str *to,
+	struct cancel_reason *reason)
 {
 	char	*invite_buf, *invite_buf_end;
 	char	*cancel_buf;
@@ -225,6 +273,7 @@ char *build_local_reparse(struct cell *Trans,unsigned int branch,
 	short	invite_len;
 	enum _hdr_types_t	hf_type;
 	int	first_via, to_len;
+	int cancel_buf_len, reason_len, code_len;
 
 	invite_buf = Trans->uac[branch].request.buffer;
 	invite_len = Trans->uac[branch].request.buffer_len;
@@ -234,9 +283,27 @@ char *build_local_reparse(struct cell *Trans,unsigned int branch,
 		goto error;
 	}
 	if ((*invite_buf != 'I') && (*invite_buf != 'i')) {
-		LOG(L_ERR, "ERROR: build_local_reparse: trying to call build_local_reparse() for a non-INVITE request?\n");
+		LOG(L_ERR, "ERROR: trying to call build_local_reparse()"
+					" for a non-INVITE request?\n");
 		goto error;
 	}
+	
+	reason_len = 0;
+	/* compute reason size */
+	if (reason && reason->cause != CANCEL_REAS_UNKNOWN){
+		if (likely(reason->cause > 0)){
+			/* Reason: SIP;cause=<reason->cause>[;text=<reason->u.text.s>] */
+			reason_len = REASON_PREFIX_LEN + USHORT2SBUF_MAX_LEN +
+				(reason->u.text.s?
+					REASON_TEXT_LEN + 1 + reason->u.text.len + 1 : 0) +
+				CRLF_LEN;
+		} else if (reason->cause == CANCEL_REAS_RCVD_CANCEL &&
+					reason->u.e2e_cancel) {
+			/* FIXME: TODO */
+		} else if (unlikely(reason->cause != -1))
+			BUG("unhandled reason cause %d\n", reason->cause);
+	}
+
 	invite_buf_end = invite_buf + invite_len;
 	s = invite_buf;
 
@@ -245,10 +312,11 @@ char *build_local_reparse(struct cell *Trans,unsigned int branch,
 	I just extend it with the length of new To HF to be sure.
 	Ugly, but we avoid lots of checks and memory allocations this way */
 	to_len = to ? to->len : 0;
-	cancel_buf = shm_malloc(sizeof(char)*(invite_len + to_len));
+	cancel_buf_len = invite_len + to_len + reason_len;
+	cancel_buf = shm_malloc(sizeof(char)*cancel_buf_len);
 	if (!cancel_buf)
 	{
-		LOG(L_ERR, "ERROR: build_local_reparse: cannot allocate shared memory\n");
+		LOG(L_ERR, "ERROR: cannot allocate shared memory\n");
 		goto error;
 	}
 	d = cancel_buf;
@@ -281,7 +349,8 @@ char *build_local_reparse(struct cell *Trans,unsigned int branch,
 			case HDR_CSEQ_T:
 				/* find the method name and replace it */
 				while ((s < invite_buf_end)
-					&& ((*s == ':') || (*s == ' ') || (*s == '\t') || ((*s >= '0') && (*s <= '9')))
+					&& ((*s == ':') || (*s == ' ') || (*s == '\t') ||
+						((*s >= '0') && (*s <= '9')))
 					) s++;
 				append_str(d, s1, s - s1);
 				append_str(d, method, method_len);
@@ -300,7 +369,8 @@ char *build_local_reparse(struct cell *Trans,unsigned int branch,
 
 			case HDR_TO_T:
 				if (to_len == 0) {
-					/* there is no To tag required, just copy paste the header */
+					/* there is no To tag required, just copy paste
+					   the header */
 					s = lw_next_line(s, invite_buf_end);
 					append_str(d, s1, s - s1);
 				} else {
@@ -336,6 +406,27 @@ char *build_local_reparse(struct cell *Trans,unsigned int branch,
 
 			case HDR_EOH_T:
 				/* end of SIP message found */
+				/* add reason if needed */
+				if (reason_len) {
+					if (likely(reason->cause > 0)) {
+						append_str(d, REASON_PREFIX, REASON_PREFIX_LEN);
+						code_len=ushort2sbuf(reason->cause, d,
+										cancel_buf_len-(int)(d-cancel_buf));
+						if (unlikely(code_len==0))
+							BUG("not enough space to write reason code");
+						d+=code_len;
+						if (reason->u.text.s){
+							append_str(d, REASON_TEXT, REASON_TEXT_LEN);
+							*d='"'; d++;
+							append_str(d, reason->u.text.s,
+											reason->u.text.len);
+							*d='"'; d++;
+						}
+						append_str(d, CRLF, CRLF_LEN);
+					} else if (reason->cause == CANCEL_REAS_RCVD_CANCEL) {
+						/* FIXME: handle cancel */
+					}
+				}
 				append_str(d, CRLF, CRLF_LEN);
 				*len = d - cancel_buf;
 				/* LOG(L_DBG, "DBG: build_local: %.*s\n", *len, cancel_buf); */

+ 4 - 2
modules/tm/t_msgbuilder.h

@@ -54,10 +54,12 @@
 
 
 char *build_local(struct cell *Trans, unsigned int branch,
-	unsigned int *len, char *method, int method_len, str *to);
+	unsigned int *len, char *method, int method_len, str *to,
+	struct cancel_reason* reason);
 
 char *build_local_reparse(struct cell *Trans, unsigned int branch,
-	unsigned int *len, char *method, int method_len, str *to);
+	unsigned int *len, char *method, int method_len, str *to,
+	struct cancel_reason* reason);
 
 char *build_uac_request(  str msg_type, str dst, str from,
 	str fromtag, int cseq, str callid, str headers, 

+ 42 - 36
modules/tm/t_reply.c

@@ -96,6 +96,7 @@
  *             (andrei)
  * 2010-02-26  added experimental support for final reply dropping, not
  *             enabled by default (performance hit) (andrei)
+ * 2010-02-26  cancel reason (rfc3326) basic support (andrei)
  *
  */
 
@@ -391,11 +392,11 @@ static char *build_ack(struct sip_msg* rpl,struct cell *trans,int branch,
 	if (cfg_get(tm, tm_cfg, reparse_invite)) {
 		/* build the ACK from the INVITE which was sent out */
 		return build_local_reparse( trans, branch, ret_len,
-					ACK, ACK_LEN, &to );
+					ACK, ACK_LEN, &to, 0 );
 	} else {
 		/* build the ACK from the reveived INVITE */
 		return build_local( trans, branch, ret_len,
-					ACK, ACK_LEN, &to );
+					ACK, ACK_LEN, &to, 0 );
 	}
 }
 
@@ -536,23 +537,23 @@ static int _reply_light( struct cell *trans, char* buf, unsigned int len,
 {
 	struct retr_buf *rb;
 	unsigned int buf_len;
-	branch_bm_t cancel_bitmap;
+	struct cancel_info cancel_data;
 #ifdef TMCB_ONSEND
 	struct tmcb_params onsend_params;
 #endif
 
+	init_cancel_info(&cancel_data);
 	if (!buf)
 	{
 		DBG("DEBUG: _reply_light: response building failed\n");
 		/* determine if there are some branches to be canceled */
 		if ( is_invite(trans) ) {
-			prepare_to_cancel(trans, &cancel_bitmap, 0);
+			prepare_to_cancel(trans, &cancel_data.cancel_bitmap, 0);
 		}
 		/* and clean-up, including cancellations, if needed */
 		goto error;
 	}
 
-	cancel_bitmap=0;
 	if (lock) LOCK_REPLIES( trans );
 	if (trans->uas.status>=200) {
 		LOG( L_ERR, "ERROR: _reply_light: can't generate %d reply"
@@ -592,8 +593,9 @@ static int _reply_light( struct cell *trans, char* buf, unsigned int len,
 									0, FAKED_REPLY, code);
 		cleanup_uac_timers( trans );
 		if (is_invite(trans)){
-			prepare_to_cancel(trans, &cancel_bitmap, 0);
-			cancel_uacs( trans, cancel_bitmap, F_CANCEL_B_KILL );
+			prepare_to_cancel(trans, &cancel_data.cancel_bitmap, 0);
+			cancel_data.reason.cause=code;
+			cancel_uacs( trans, &cancel_data, F_CANCEL_B_KILL );
 		}
 		start_final_repl_retr(  trans );
 	}
@@ -642,15 +644,15 @@ static int _reply_light( struct cell *trans, char* buf, unsigned int len,
 	return 1;
 
 error3:
-	prepare_to_cancel(trans, &cancel_bitmap, 0);
+	prepare_to_cancel(trans, &cancel_data.cancel_bitmap, 0);
 error2:
 	if (lock) UNLOCK_REPLIES( trans );
 	pkg_free ( buf );
 error:
 	/* do UAC cleanup */
 	cleanup_uac_timers( trans );
-	if ( is_invite(trans) && cancel_bitmap )
-		cancel_uacs( trans, cancel_bitmap, F_CANCEL_B_KILL);
+	if ( is_invite(trans) && cancel_data.cancel_bitmap )
+		cancel_uacs( trans, &cancel_data, F_CANCEL_B_KILL);
 	/* we did not succeed -- put the transaction on wait */
 	put_on_wait(trans);
 	return -1;
@@ -1063,7 +1065,7 @@ static unsigned char drop_replies;
  */
 static enum rps t_should_relay_response( struct cell *Trans , int new_code,
 	int branch , int *should_store, int *should_relay,
-	branch_bm_t *cancel_bitmap, struct sip_msg *reply )
+	struct cancel_info *cancel_data, struct sip_msg *reply )
 {
 	int branch_cnt;
 	int picked_code;
@@ -1136,8 +1138,9 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
 				if (!(Trans->flags & (T_6xx | T_DISABLE_6xx))){
 					/* cancel only the first time we get a 6xx and only
 					  if the 6xx handling is not disabled */
-					prepare_to_cancel(Trans, cancel_bitmap, 0);
+					prepare_to_cancel(Trans, &cancel_data->cancel_bitmap, 0);
 					Trans->flags|=T_6xx;
+					cancel_data->reason.cause=new_code;
 				}
 			}
 			return RPS_STORE;
@@ -1291,7 +1294,8 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
 		Trans->uac[branch].last_received=new_code;
 		*should_relay= new_code==100? -1 : branch;
 		if (new_code>=200 ) {
-			prepare_to_cancel( Trans, cancel_bitmap, 0);
+			prepare_to_cancel( Trans, &cancel_data->cancel_bitmap, 0);
+			cancel_data->reason.cause=new_code;
 			return RPS_COMPLETED;
 		} else return RPS_PROVISIONAL;
 	}
@@ -1534,7 +1538,8 @@ skip:
    wait timer will be started (put_on_wait(t)).
 */
 enum rps relay_reply( struct cell *t, struct sip_msg *p_msg, int branch,
-	unsigned int msg_status, branch_bm_t *cancel_bitmap, int do_put_on_wait )
+	unsigned int msg_status, struct cancel_info *cancel_data,
+	int do_put_on_wait )
 {
 	int relay;
 	int save_clone;
@@ -1567,7 +1572,7 @@ enum rps relay_reply( struct cell *t, struct sip_msg *p_msg, int branch,
 
 	/* *** store and relay message as needed *** */
 	reply_status = t_should_relay_response(t, msg_status, branch,
-		&save_clone, &relay, cancel_bitmap, p_msg );
+		&save_clone, &relay, cancel_data, p_msg );
 	DBG("DEBUG: relay_reply: branch=%d, save=%d, relay=%d\n",
 		branch, save_clone, relay );
 
@@ -1778,7 +1783,8 @@ error02:
 	}
 error01:
 	t_reply_unsafe( t, t->uas.request, 500, "Reply processing error" );
-	*cancel_bitmap=0; /* t_reply_unsafe already canceled everything needed */
+	cancel_data->cancel_bitmap=0; /* t_reply_unsafe already canceled
+									 everything needed */
 	UNLOCK_REPLIES(t);
 	/* if (is_invite(t)) cancel_uacs( t, *cancel_bitmap, 0); 
 	 *  -- not needed, t_reply_unsafe took care of this */
@@ -1795,7 +1801,7 @@ error01:
    it is entered locked with REPLY_LOCK and it returns unlocked!
 */
 enum rps local_reply( struct cell *t, struct sip_msg *p_msg, int branch,
-	unsigned int msg_status, branch_bm_t *cancel_bitmap)
+	unsigned int msg_status, struct cancel_info *cancel_data)
 {
 	/* how to deal with replies for local transaction */
 	int local_store, local_winner;
@@ -1803,17 +1809,16 @@ enum rps local_reply( struct cell *t, struct sip_msg *p_msg, int branch,
 	struct sip_msg *winning_msg;
 	int winning_code;
 	int totag_retr;
-	/* branch_bm_t cancel_bitmap; */
 
 	/* keep warning 'var might be used un-inited' silent */
 	winning_msg=0;
 	winning_code=0;
 	totag_retr=0;
 
-	*cancel_bitmap=0;
+	cancel_data->cancel_bitmap=0;
 
 	reply_status=t_should_relay_response( t, msg_status, branch,
-		&local_store, &local_winner, cancel_bitmap, p_msg );
+		&local_store, &local_winner, cancel_data, p_msg );
 	DBG("DEBUG: local_reply: branch=%d, save=%d, winner=%d\n",
 		branch, local_store, local_winner );
 	if (local_store) {
@@ -1862,13 +1867,14 @@ enum rps local_reply( struct cell *t, struct sip_msg *p_msg, int branch,
 	return reply_status;
 
 error:
-	prepare_to_cancel(t, cancel_bitmap, 0);
+	prepare_to_cancel(t, &cancel_data->cancel_bitmap, 0);
 	UNLOCK_REPLIES(t);
 	cleanup_uac_timers(t);
 	if (p_msg && p_msg!=FAKED_REPLY && get_cseq(p_msg)->method.len==INVITE_LEN
-		&& memcmp( get_cseq(p_msg)->method.s, INVITE, INVITE_LEN)==0)
-		cancel_uacs( t, *cancel_bitmap, F_CANCEL_B_KILL);
-	*cancel_bitmap=0; /* we've already took care of everything */
+		&& memcmp( get_cseq(p_msg)->method.s, INVITE, INVITE_LEN)==0){
+		cancel_uacs( t, cancel_data, F_CANCEL_B_KILL);
+	}
+	cancel_data->cancel_bitmap=0; /* we've already took care of everything */
 	put_on_wait(t);
 	return RPS_ERROR;
 }
@@ -1893,7 +1899,7 @@ int reply_received( struct sip_msg  *p_msg )
 	/* has the transaction completed now and we need to clean-up? */
 	int reply_status;
 	int onreply_route;
-	branch_bm_t cancel_bitmap;
+	struct cancel_info cancel_data;
 	struct ua_client *uac;
 	struct cell *t;
 	struct dest_info  lack_dst;
@@ -1928,7 +1934,7 @@ int reply_received( struct sip_msg  *p_msg )
 	if (unlikely(branch==T_BR_UNDEFINED))
 		BUG("invalid branch, please report to [email protected]\n");
 	tm_ctx_set_branch_index(branch);
-	cancel_bitmap=0;
+	init_cancel_info(&cancel_data);
 	msg_status=p_msg->REPLY_STATUS;
 	replies_locked=0;
 
@@ -2035,7 +2041,7 @@ int reply_received( struct sip_msg  *p_msg )
 				 * if BUSY or set just exit, a cancel will be (or was) sent 
 				 * shortly on this branch */
 				DBG("tm: reply_received: branch CANCEL created\n");
-				cancel_branch(t, branch, F_CANCEL_B_FORCE_C);
+				cancel_branch(t, branch, 0, F_CANCEL_B_FORCE_C);
 			}
 			goto done; /* nothing to do */
 		}
@@ -2179,24 +2185,24 @@ int reply_received( struct sip_msg  *p_msg )
 		replies_locked=1;
 	}
 	if ( is_local(t) ) {
-		reply_status=local_reply( t, p_msg, branch, msg_status, &cancel_bitmap );
+		reply_status=local_reply( t, p_msg, branch, msg_status, &cancel_data );
 		if (reply_status == RPS_COMPLETED) {
 			     /* no more UAC FR/RETR (if I received a 2xx, there may
 			      * be still pending branches ...
 			      */
 			cleanup_uac_timers( t );
-			if (is_invite(t)) cancel_uacs(t, cancel_bitmap, F_CANCEL_B_KILL);
+			if (is_invite(t)) cancel_uacs(t, &cancel_data, F_CANCEL_B_KILL);
 			/* There is no need to call set_final_timer because we know
 			 * that the transaction is local */
 			put_on_wait(t);
-		}else if (cancel_bitmap){
+		}else if (unlikely(cancel_data.cancel_bitmap)){
 			/* cancel everything, even non-INVITEs (e.g in case of 6xx), use
 			 * cancel_b_method for canceling unreplied branches */
-			cancel_uacs(t, cancel_bitmap, cfg_get(tm,tm_cfg, cancel_b_flags));
+			cancel_uacs(t, &cancel_data, cfg_get(tm,tm_cfg, cancel_b_flags));
 		}
 	} else {
 		reply_status=relay_reply( t, p_msg, branch, msg_status,
-									&cancel_bitmap, 1 );
+									&cancel_data, 1 );
 		if (reply_status == RPS_COMPLETED) {
 			     /* no more UAC FR/RETR (if I received a 2xx, there may
 				be still pending branches ...
@@ -2204,16 +2210,16 @@ int reply_received( struct sip_msg  *p_msg )
 			cleanup_uac_timers( t );
 			/* 2xx is a special case: we can have a COMPLETED request
 			 * with branches still open => we have to cancel them */
-			if (is_invite(t) && cancel_bitmap) 
-				cancel_uacs( t, cancel_bitmap,  F_CANCEL_B_KILL);
+			if (is_invite(t) && cancel_data.cancel_bitmap) 
+				cancel_uacs( t, &cancel_data,  F_CANCEL_B_KILL);
 			/* FR for negative INVITES, WAIT anything else */
 			/* Call to set_final_timer is embedded in relay_reply to avoid
 			 * race conditions when reply is sent out and an ACK to stop
 			 * retransmissions comes before retransmission timer is set.*/
-		}else if (cancel_bitmap){
+		}else if (unlikely(cancel_data.cancel_bitmap)){
 			/* cancel everything, even non-INVITEs (e.g in case of 6xx), use
 			 * cancel_b_method for canceling unreplied branches */
-			cancel_uacs(t, cancel_bitmap, cfg_get(tm,tm_cfg, cancel_b_flags));
+			cancel_uacs(t, &cancel_data, cfg_get(tm,tm_cfg, cancel_b_flags));
 		}
 	}
 	uac->request.flags|=F_RB_REPLIED;

+ 35 - 2
modules/tm/t_reply.h

@@ -64,6 +64,38 @@ int unmatched_totag(struct cell *t, struct sip_msg *ack);
 /* branch bitmap type */
 typedef unsigned int branch_bm_t;
 
+
+#define CANCEL_REAS_UNKNOWN 0
+#define CANCEL_REAS_RCVD_CANCEL -1
+#define CANCEL_REAS_FINAL_REPLY(x) (x)
+
+/** cancel reason structure.*/
+struct cancel_reason {
+	short cause; /**< 0 = unknown, -1 =  cancel, > 0 final reply code. */
+	union{
+		str text; /**< reason text if reason is final reply .*/
+		struct sip_msg* e2e_cancel; /**< cancel msg if reason is cancel. */
+	}u;
+};
+
+struct cancel_info {
+	branch_bm_t cancel_bitmap; /**< cancel branch bitmap */
+	struct cancel_reason reason;
+};
+
+
+#define init_cancel_reason(cr) \
+	do {\
+		(cr)->cause=0; \
+		(cr)->u.e2e_cancel=0; \
+	} while(0)
+
+#define init_cancel_info(ci) \
+	do {\
+		(ci)->cancel_bitmap=0; \
+		init_cancel_reason(&(ci)->reason); \
+	}while (0);
+
 /* reply export types */
 typedef int (*treply_f)(struct sip_msg * , unsigned int , char * );
 typedef int (*treply_wb_f)( struct cell* trans,
@@ -121,10 +153,11 @@ int t_reply_unsafe( struct cell *t, struct sip_msg * , unsigned int , char * );
 
 
 enum rps relay_reply( struct cell *t, struct sip_msg *p_msg, int branch, 
-	unsigned int msg_status, branch_bm_t *cancel_bitmap, int do_put_on_wait );
+	unsigned int msg_status, struct cancel_info *cancel_data,
+	int do_put_on_wait );
 
 enum rps local_reply( struct cell *t, struct sip_msg *p_msg, int branch,
-    unsigned int msg_status, branch_bm_t *cancel_bitmap );
+    unsigned int msg_status, struct cancel_info *cancel_data );
 
 void set_final_timer( /* struct s_table *h_table,*/ struct cell *t );
 

+ 4 - 4
modules/tm/timer.c

@@ -322,7 +322,7 @@ inline static ticks_t  delete_cell( struct cell *p_cell, int unlock )
  * it assumes the REPLY_LOCK is already held and returns unlocked */
 static void fake_reply(struct cell *t, int branch, int code )
 {
-	branch_bm_t cancel_bitmap;
+	struct cancel_info cancel_data;
 	short do_cancel_branch;
 	enum rps reply_status;
 
@@ -331,15 +331,15 @@ static void fake_reply(struct cell *t, int branch, int code )
 	t->uac[branch].request.flags|=F_RB_CANCELED;
 	if ( is_local(t) ) {
 		reply_status=local_reply( t, FAKED_REPLY, branch, 
-					  code, &cancel_bitmap );
+					  code, &cancel_data );
 	} else {
 		/* rely reply, but don't put on wait, we still need t
 		 * to send the cancels */
 		reply_status=relay_reply( t, FAKED_REPLY, branch, code,
-					  &cancel_bitmap, 0 );
+					  &cancel_data, 0 );
 	}
 	/* now when out-of-lock do the cancel I/O */
-	if (do_cancel_branch) cancel_branch(t, branch, 0);
+	if (do_cancel_branch) cancel_branch(t, branch, &cancel_data.reason, 0);
 	/* it's cleaned up on error; if no error occurred and transaction
 	   completed regularly, I have to clean-up myself
 	*/