[asterisk-commits] dvossel: trunk r225307 - in /trunk: ./ channels/chan_iax2.c

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Oct 21 16:58:49 CDT 2009


Author: dvossel
Date: Wed Oct 21 16:58:46 2009
New Revision: 225307

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=225307
Log:
Merged revisions 225243 via svnmerge from 
https://origsvn.digium.com/svn/asterisk/branches/1.4

........
  r225243 | dvossel | 2009-10-21 15:58:08 -0500 (Wed, 21 Oct 2009) | 13 lines
  
  IAX2: VNAK loop caused by signaling frames with no destination call number
  
  It is possible for the PBX thread to queue up signaling frames before
  a destination call number is received.  This can result in signaling
  frames being sent out with no destination call number. Since recent
  versions of Asterisk require accurate destination callnumbers for all
  Full Frames, this can cause a VNAK loop to occur.  To resolve this
  no signaling frames are sent until a destination callnumber is received,
  and destination call numbers are now only required for iax_pvt matching
  when the frame is an ACK.
  
  Review: https://reviewboard.asterisk.org/r/413/
........

Modified:
    trunk/   (props changed)
    trunk/channels/chan_iax2.c

Propchange: trunk/
------------------------------------------------------------------------------
Binary property 'branch-1.4-merged' - no diff available.

Modified: trunk/channels/chan_iax2.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/chan_iax2.c?view=diff&rev=225307&r1=225306&r2=225307
==============================================================================
--- trunk/channels/chan_iax2.c (original)
+++ trunk/channels/chan_iax2.c Wed Oct 21 16:58:46 2009
@@ -820,6 +820,15 @@
 	int frames_received;
 	/*! num bytes used for calltoken ie, even an empty ie should contain 2 */
 	unsigned char calltoken_ie_len;
+	/*! hold all signaling frames from the pbx thread until we have a destination callno */
+	char hold_signaling;
+	/*! frame queue for signaling frames from pbx thread waiting for destination callno */
+	AST_LIST_HEAD_NOLOCK(signaling_queue, signaling_queue_entry) signaling_queue;
+};
+
+struct signaling_queue_entry {
+	struct ast_frame f;
+	AST_LIST_ENTRY(signaling_queue_entry) next;
 };
 
 /*! table of available call numbers */
@@ -1703,10 +1712,56 @@
 	return 0;
 }
 
+static void free_signaling_queue_entry(struct signaling_queue_entry *s)
+{
+	ast_free(s->f.data.ptr);
+	ast_free(s);
+}
+
+/*! \brief This function must be called once we are sure the other side has
+ *  given us a call number.  All signaling is held here until that point. */
+static void send_signaling(struct chan_iax2_pvt *pvt)
+{
+	struct signaling_queue_entry *s = NULL;
+
+	while ((s = AST_LIST_REMOVE_HEAD(&pvt->signaling_queue, next))) {
+		iax2_send(pvt, &s->f, 0, -1, 0, 0, 0);
+		free_signaling_queue_entry(s);
+	}
+	pvt->hold_signaling = 0;
+}
+
+/*! \brief All frames other than that of type AST_FRAME_IAX must be held until
+ *  we have received a destination call number. */
+static int queue_signalling(struct chan_iax2_pvt *pvt, struct ast_frame *f)
+{
+	struct signaling_queue_entry *new;
+
+	if (f->frametype == AST_FRAME_IAX || !pvt->hold_signaling) {
+		return 1; /* do not queue this frame */
+	} else if (!(new = ast_calloc(1, sizeof(struct signaling_queue_entry)))) {
+		return -1;  /* out of memory */
+	}
+
+	memcpy(&new->f, f, sizeof(new->f)); /* copy ast_frame into our queue entry */
+
+	if (new->f.datalen) { /* if there is data in this frame copy it over as well */
+		if (!(new->f.data.ptr = ast_calloc(1, new->f.datalen))) {
+			free_signaling_queue_entry(new);
+			return -1;
+		}
+		memcpy(new->f.data.ptr, f->data.ptr, sizeof(*new->f.data.ptr));
+	}
+	AST_LIST_INSERT_TAIL(&pvt->signaling_queue, new, next);
+
+	return 0;
+}
+
 static void pvt_destructor(void *obj)
 {
 	struct chan_iax2_pvt *pvt = obj;
 	struct iax_frame *cur = NULL;
+	struct signaling_queue_entry *s = NULL;
 
 	ast_mutex_lock(&iaxsl[pvt->callno]);
 
@@ -1724,6 +1779,10 @@
 	}
 
 	ast_mutex_unlock(&iaxsl[pvt->callno]);
+
+	while ((s = AST_LIST_REMOVE_HEAD(&pvt->signaling_queue, next))) {
+		free_signaling_queue_entry(s);
+	}
 
 	if (pvt->reg) {
 		pvt->reg->callno = 0;
@@ -1780,6 +1839,9 @@
 	jb_setconf(tmp->jb,&jbconf);
 
 	AST_LIST_HEAD_INIT_NOLOCK(&tmp->dpentries);
+
+	tmp->hold_signaling = 1;
+	AST_LIST_HEAD_INIT_NOLOCK(&tmp->signaling_queue);
 
 	return tmp;
 }
@@ -7148,12 +7210,17 @@
 		int now, int transfer, int final)
 {
 	struct ast_frame f = { 0, };
+	int res = 0;
 
 	f.frametype = type;
 	f.subclass = command;
 	f.datalen = datalen;
 	f.src = __FUNCTION__;
 	f.data.ptr = (void *) data;
+
+	if ((res = queue_signalling(i, &f)) <= 0) {
+		return res;
+	}
 
 	return iax2_send(i, &f, ts, seqno, now, transfer, final);
 }
@@ -9634,19 +9701,15 @@
 		int check_dcallno = 0;
 
 		/*
-		 * We enforce accurate destination call numbers for all full frames except
-		 * LAGRQ and PING commands.  This is because older versions of Asterisk
-		 * schedule these commands to get sent very quickly, and they will sometimes
-		 * be sent before they receive the first frame from the other side.  When
-		 * that happens, it doesn't contain the destination call number.  However,
-		 * not checking it for these frames is safe.
-		 * 
+		 * We enforce accurate destination call numbers for ACKs.  This forces the other
+		 * end to know the destination call number before call setup can complete.
+		 *
 		 * Discussed in the following thread:
 		 *    http://lists.digium.com/pipermail/asterisk-dev/2008-May/033217.html 
 		 */
 
-		if (ntohs(mh->callno) & IAX_FLAG_FULL) {
-			check_dcallno = f.frametype == AST_FRAME_IAX ? (f.subclass != IAX_COMMAND_PING && f.subclass != IAX_COMMAND_LAGRQ) : 1;
+		if ((ntohs(mh->callno) & IAX_FLAG_FULL) && ((f.frametype == AST_FRAME_IAX) && (f.subclass == IAX_COMMAND_ACK))) {
+			check_dcallno = 1;
 		}
 
 		if (!(fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, new, fd, check_dcallno))) {
@@ -9920,6 +9983,12 @@
 			if (ies.vars) {
 				ast_debug(1, "I have IAX variables, but they were not processed\n");
 			}
+		}
+
+		/* once we receive our first IAX Full Frame that is not CallToken related, send all
+		 * queued signaling frames that were being held. */
+		if ((f.frametype == AST_FRAME_IAX) && (f.subclass != IAX_COMMAND_CALLTOKEN) && iaxs[fr->callno]->hold_signaling) {
+			send_signaling(iaxs[fr->callno]);
 		}
 
 		if (f.frametype == AST_FRAME_VOICE) {




More information about the asterisk-commits mailing list