[asterisk-commits] dvossel: branch 1.4 r225243 - /branches/1.4/channels/chan_iax2.c

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


Author: dvossel
Date: Wed Oct 21 15:58:08 2009
New Revision: 225243

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=225243
Log:
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:
    branches/1.4/channels/chan_iax2.c

Modified: branches/1.4/channels/chan_iax2.c
URL: http://svnview.digium.com/svn/asterisk/branches/1.4/channels/chan_iax2.c?view=diff&rev=225243&r1=225242&r2=225243
==============================================================================
--- branches/1.4/channels/chan_iax2.c (original)
+++ branches/1.4/channels/chan_iax2.c Wed Oct 21 15:58:08 2009
@@ -656,6 +656,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 */
@@ -1510,10 +1519,56 @@
 	return 0;
 }
 
+static void free_signaling_queue_entry(struct signaling_queue_entry *s)
+{
+	ast_free(s->f.data);
+	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 = ast_calloc(1, new->f.datalen))) {
+			free_signaling_queue_entry(new);
+			return -1;
+		}
+		memcpy(new->f.data, f->data, sizeof(*new->f.data));
+	}
+	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;
 
 	iax2_destroy_helper(pvt);
 	sched_delay_remove(&pvt->addr, pvt->callno_entry);
@@ -1530,6 +1585,10 @@
 		}
 	}
 	AST_LIST_UNLOCK(&iaxq.queue);
+
+	while ((s = AST_LIST_REMOVE_HEAD(&pvt->signaling_queue, next))) {
+		free_signaling_queue_entry(s);
+	}
 
 	if (pvt->reg) {
 		pvt->reg->callno = 0;
@@ -1586,6 +1645,9 @@
 	jbconf.resync_threshold = resyncthreshold;
 	jbconf.max_contig_interp = maxjitterinterps;
 	jb_setconf(tmp->jb,&jbconf);
+
+	tmp->hold_signaling = 1;
+	AST_LIST_HEAD_INIT_NOLOCK(&tmp->signaling_queue);
 
 	return tmp;
 }
@@ -6240,13 +6302,16 @@
 		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 = (void *) data;
 
+	if ((res = queue_signalling(i, &f)) <= 0) {
+		return res;
+	}
 	return iax2_send(i, &f, ts, seqno, now, transfer, final);
 }
 
@@ -8512,19 +8577,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))) {
@@ -8744,6 +8805,12 @@
 					return 1;
 				}
 			}
+		}
+
+		/* 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