aboutsummaryrefslogtreecommitdiffstats
path: root/channels/chan_iax2.c
diff options
context:
space:
mode:
authordvossel <dvossel@f38db490-d61c-443f-a65b-d21fe96a405b>2009-10-21 22:00:28 +0000
committerdvossel <dvossel@f38db490-d61c-443f-a65b-d21fe96a405b>2009-10-21 22:00:28 +0000
commit253eced805be1c4f76b0bcac5bf06bb62dc901ee (patch)
tree5b392958b3133118ed63d37ad47bea686da4f3ee /channels/chan_iax2.c
parentb478513f785517f98fd55fc0f4f2d8b242b2680a (diff)
Merged revisions 225307 via svnmerge from
https://origsvn.digium.com/svn/asterisk/trunk ................ r225307 | dvossel | 2009-10-21 16:58:46 -0500 (Wed, 21 Oct 2009) | 20 lines 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/ ........ ................ git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.6.2@225308 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'channels/chan_iax2.c')
-rw-r--r--channels/chan_iax2.c87
1 files changed, 78 insertions, 9 deletions
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index ab28c638d..0a32c0982 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -780,6 +780,15 @@ struct chan_iax2_pvt {
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 */
@@ -1659,10 +1668,56 @@ static int scheduled_destroy(const void *vid)
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]);
iax2_destroy_helper(pvt);
@@ -1682,6 +1737,10 @@ static void pvt_destructor(void *obj)
}
AST_LIST_UNLOCK(&frame_queue);
+ while ((s = AST_LIST_REMOVE_HEAD(&pvt->signaling_queue, next))) {
+ free_signaling_queue_entry(s);
+ }
+
if (pvt->reg) {
pvt->reg->callno = 0;
}
@@ -1738,6 +1797,9 @@ static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, const char *host)
AST_LIST_HEAD_INIT_NOLOCK(&tmp->dpentries);
+ tmp->hold_signaling = 1;
+ AST_LIST_HEAD_INIT_NOLOCK(&tmp->signaling_queue);
+
return tmp;
}
@@ -7068,6 +7130,7 @@ static int __send_command(struct chan_iax2_pvt *i, char type, int command, unsig
int now, int transfer, int final)
{
struct ast_frame f = { 0, };
+ int res = 0;
f.frametype = type;
f.subclass = command;
@@ -7075,6 +7138,10 @@ static int __send_command(struct chan_iax2_pvt *i, char type, int command, unsig
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);
}
@@ -9537,19 +9604,15 @@ static int socket_process(struct iax2_thread *thread)
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))) {
@@ -9825,6 +9888,12 @@ static int socket_process(struct iax2_thread *thread)
}
}
+ /* 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) {
if (f.subclass != iaxs[fr->callno]->voiceformat) {
iaxs[fr->callno]->voiceformat = f.subclass;