Ver código fonte

- try matching e2e acks in t_lookup() only for transactions which are
interested in them (have the TMCB_E2EACK_IN or TMCB_E2EACK_RETR_IN callbacks
registered). This should improve performance a little in the common case
when no E2EACK callbacks are registered.
- e2e ack fix: when matching e2e acks use the saved totag list => no more
problems with possible wrong transaction matching when a downstream forked
INVITE arrives and more then one branch is replied with 2xx.

Andrei Pelinescu-Onciul 17 anos atrás
pai
commit
7dfb5effd4
1 arquivos alterados com 98 adições e 35 exclusões
  1. 98 35
      modules/tm/t_lookup.c

+ 98 - 35
modules/tm/t_lookup.c

@@ -97,6 +97,8 @@
  * 2007-06-01  support for different retransmissions intervals per transaction;
  *             added maximum inv. and non-inv. transaction life time (andrei)
  * 2007-06-06  switched tm bucket list to a simpler and faster clist;
+ * 2008-02-28  try matching e2e acks in t_lookup() only for transactions
+ *               which have E2EACK callbacks registered (andrei)
  */
 
 #include "defs.h"
@@ -238,6 +240,37 @@ static inline int dlg_matching(struct cell *p_cell, struct sip_msg *ack )
 	return 1;
 }
 
+
+
+/* returns 2 if one of the save totags matches the totag in the current
+ * message (which should be an ACK) and 0 if not */
+static inline int totag_e2e_ack_matching(struct cell* p_cell, 
+												struct sip_msg *ack)
+{
+	struct totag_elem *i;
+	str *tag;
+	
+	tag=&get_to(ack)->tag_value;
+	/* no locking needed for reading/searching, see update_totag_set() */
+	for (i=p_cell->fwded_totags; i; i=i->next){
+		membar_depends(); /* make sure we don't see some old i content
+							(needed on CPUs like Alpha) */
+		if (i->tag.len==tag->len && memcmp(i->tag.s, tag->s, tag->len)==0) {
+			return 2;
+		}
+	}
+	return 0;
+}
+
+
+
+/* returns: 0 - no match
+ *          1 - full match to a local transaction
+ *          2 - full match to a proxied transaction
+ *          3 - partial match to a proxied transaction (totag not checked) =>
+ *          care must be taken not to falsely match an ACK for a negative
+ *          reply to a "fork" of the transaction
+ */
 static inline int ack_matching(struct cell *p_cell, struct sip_msg *p_msg) 
 {
 	/* partial dialog matching -- no to-tag, only from-tag, 
@@ -249,8 +282,15 @@ static inline int ack_matching(struct cell *p_cell, struct sip_msg *p_msg)
 	 * done now -- we ignore to-tags; the ACK simply belongs to
 	 * this UAS part of dialog, whatever to-tag it gained
 	 */
-	if (p_cell->relayed_reply_branch!=-2) {
-		return 2; /* e2e proxied ACK */
+	if (likely(p_cell->relayed_reply_branch!=-2)) {
+		if (likely(has_tran_tmcbs(p_cell, 
+							TMCB_E2EACK_IN|TMCB_E2EACK_RETR_IN)))
+			return totag_e2e_ack_matching(p_cell, p_msg); /* 2 or 0 */
+		else
+			LOG(L_WARN, "WARNING: ack_matching() attempted on"
+					" a transaction with no E2EACK callbacks => the results"
+					" are not completely reliable when forking is involved\n");
+		return 3; /* e2e proxied ACK partial match */
 	}
 	/* it's a local dialog -- we wish to verify to-tags too */
 	if (dlg_matching(p_cell, p_msg)) {
@@ -302,12 +342,12 @@ static int matching_3261( struct sip_msg *p_msg, struct cell **trans,
 			enum request_method skip_method, int* cancel)
 {
 	struct cell *p_cell;
+	struct cell *e2e_ack_trans;
 	struct sip_msg  *t_msg;
 	struct via_body *via1;
 	int is_ack;
 	int dlg_parsed;
 	int ret = 0;
-	struct cell *e2e_ack_trans;
 	struct entry* hash_bucket;
 
 	*cancel=0;
@@ -344,35 +384,51 @@ static int matching_3261( struct sip_msg *p_msg, struct cell **trans,
 		   ACK belongs, only the first one will match.
 		*/
 
-		/* dialog matching needs to be applied for ACK/200s */
-		if (unlikely(is_ack && p_cell->uas.status<300 && e2e_ack_trans==0)) {
-			/* make sure we have parsed all things we need for dialog
-			 * matching */
-			if (!dlg_parsed) {
-				dlg_parsed=1;
-				if (unlikley(!parse_dlg(p_msg))) {
-					LOG(L_ERR, "ERROR: matching_3261: dlg parsing failed\n");
-					return 0;
+		/* dialog matching needs to be applied for ACK/200s but only if
+		 * this is a local transaction or its a proxied transaction interested
+		 *  in e2e ACKs (has E2EACK* callbacks installed) */
+		if (unlikely(is_ack && p_cell->uas.status<300)) {
+			if (unlikely(has_tran_tmcbs(p_cell, 
+							TMCB_E2EACK_IN|TMCB_E2EACK_RETR_IN) ||
+							(p_cell->relayed_reply_branch==-2)  )) {
+				/* make sure we have parsed all things we need for dialog
+				 * matching */
+				if (!dlg_parsed) {
+					dlg_parsed=1;
+					if (unlikely(!parse_dlg(p_msg))) {
+						LOG(L_INFO, "ERROR: matching_3261: dlg parsing "
+								"failed\n");
+						return 0;
+					}
 				}
-			}
-			ret=ack_matching(p_cell /* t w/invite */, p_msg /* ack */);
-			if (unlikely(ret>0)) {
-				/* if ret==1 => fully matching e2e ack for local trans 
-				 * if ret==2 => partial matching e2e ack for proxied
-				 * transaction. However this could also be an ack for
-				 * a negative reply for a tranaction with the same callid,
-				 * cseq & fromtag (e.g. am invite forked prior to reaching the
-				 * proxy), since for e2e acks no totag matching is done
-				 */
-				if (unlikely(ret==1)) goto found;
-				e2e_ack_trans=p_cell;
-				/* no break, we want to continue looking in case we can find
-				 * a match for a negative reply */
+				ret=ack_matching(p_cell /* t w/invite */, p_msg /* ack */);
+				if (unlikely(ret>0)) {
+					/* if ret==1 => fully matching e2e ack for local trans 
+					 * if ret==2 => matching e2e ack for proxied  transaction.
+					 *  which is interested in it (E2EACK* callbacks)
+					 * if ret==3 => partial match => we should at least
+					 *  make sure the ACK is not for a negative reply
+					 *  (FIXME: ret==3 should never happen, it's a bug catch
+					 *    case)*/
+					if (unlikely(ret==1)) goto found;
+					if (unlikely(ret==3)){
+						if (e2e_ack_trans==0)
+							e2e_ack_trans=p_cell;
+						continue; /* maybe we get a better 
+													   match for a neg. 
+													   replied trans. */
+					}
+					e2e_ack_trans=p_cell;
+					goto e2eack_found;
+				}
+				/* this ACK is neither local "negative" one, nor a proxied
+				* end-2-end one, nor an end-2-end one for a UAS transaction
+				* -- we failed to match */
 				continue;
 			}
-			/* this ACK is neither local "negative" one, nor a proxied
-			 * end-2-end one, nor an end-2-end one for a UAS transaction
-			 * -- we failed to match */
+			/* not interested, it's an ack and a 2xx replied
+			  transaction but the transaction is not local and
+			  the transaction is not interested in e2eacks (no e2e callbacks)*/
 			continue;
 		}
 		/* now real tid matching occurs  for negative ACKs and any 
@@ -401,6 +457,7 @@ found:
 	 *  w/o totag => for a pre-forked invite it might match the wrong
 	 *  transaction) */
 	if (e2e_ack_trans) {
+e2eack_found:
 		*trans=e2e_ack_trans;
 		return 2;
 	}
@@ -413,7 +470,7 @@ found:
  *      negative - transaction wasn't found
  *			(-2 = possibly e2e ACK matched )
  *      positive - transaction found
- * It also sets *cancel if a there is already a cancel transaction
+ * It also sets *cancel if there is already a cancel transaction
  */
 
 int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked,
@@ -493,7 +550,7 @@ int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked,
 
 		if (!t_msg) continue; /* skip UAC transactions */
 
-		if (!isACK) {	
+		if (likely(!isACK)) {	
 			/* for non-ACKs we want same method matching, we 
 			 * make an exception for pre-exisiting CANCELs because we
 			 * want to set *cancel */
@@ -551,10 +608,16 @@ int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked,
 				get_to(t_msg)->uri.len)!=0) continue;
 
 			/* it is e2e ACK/200 */
-			if (p_cell->uas.status<300 && e2e_ack_trans==0) {
+			if (p_cell->uas.status<300) {
 				/* all criteria for proxied ACK are ok */
-				if (p_cell->relayed_reply_branch!=-2) {
-					e2e_ack_trans=p_cell;
+				if (likely(p_cell->relayed_reply_branch!=-2)) {
+					if (unlikely(has_tran_tmcbs(p_cell, 
+									TMCB_E2EACK_IN|TMCB_E2EACK_RETR_IN))){
+						if (likely(totag_e2e_ack_matching(p_cell, p_msg)==2))
+							goto e2e_ack;
+						else if (e2e_ack_trans==0)
+							e2e_ack_trans=p_cell;
+					}
 					continue;
 				}
 				/* it's a local UAS transaction */
@@ -1201,7 +1264,7 @@ static inline int new_t(struct sip_msg *p_msg)
 	<0	on error
 
 	+1	if a request did not match a transaction
-		- it that was an ack, the calling function
+		- if that was an ack, the calling function
 		  shall forward statelessly
 		- otherwise it means, a new transaction was
 		  introduced and the calling function