[svn-commits] rmudgett: mISDNuser/trunk r123 - /mISDNuser/trunk/i4lnet/net_l3.c

SVN commits to the Digium repositories svn-commits at lists.digium.com
Tue Jul 14 16:57:05 CDT 2009


Author: rmudgett
Date: Tue Jul 14 16:57:01 2009
New Revision: 123

URL: http://svn.asterisk.org/svn-view/thirdparty?view=rev&rev=123
Log:
Fixed mISDNuser to handle Q.931 section 5.2.5.3.

This issue is related to Mantis 10457, which I think was never actually
fixed.  If you look closely you'll see that it's the same as described
here.

If there is more than one phone or one which occupies more than one TEI
(e.g.  Gigaset CX475) connected to an NT-PTMP port, the NT does not handle
the DISCONNECT messages from a phone correctly as required in Q.931
section 5.2.5.3.  Instead of releasing the call processes only for the TEI
that answered with DISCONNECT and retaining the cause until all TEIs have
finished their call processes, the L3 of mISDNuser passes it directly to
the call control (chan_misdn), which then hangs up the caller.  The call
processes to any other TEIs are not cleared and so the other phones all
keep ringing.

The case described in Mantis 10475 is with a Gigaset.  Gigasets use
separate TEIs for each handset, and if one handset answers the call, the
other TEIs are actively DISCONNECTed by the Gigaset, which then directly
leads to the hangup described in Mantis 10475.  The wireshark trace there
shows two TEIs, one connecting, the other one disconnecting the call.

Another symptom of this issue is if two (real) phones are connected to the
NT, the call (leg A) is hungup when just one of them rejects or is simply
busy.  The other one keeps ringing.

The this patch adds call clearing during call establishment according to
Q.931 section 5.2.5.3 to mISDNuser, which fixes both symptoms above.

JIRA ABE-1917
Patches:
    disconnect.patch (Modified to fix a potential memory leak and compiler warning.)

Modified:
    mISDNuser/trunk/i4lnet/net_l3.c

Modified: mISDNuser/trunk/i4lnet/net_l3.c
URL: http://svn.asterisk.org/svn-view/thirdparty/mISDNuser/trunk/i4lnet/net_l3.c?view=diff&rev=123&r1=122&r2=123
==============================================================================
--- mISDNuser/trunk/i4lnet/net_l3.c (original)
+++ mISDNuser/trunk/i4lnet/net_l3.c Tue Jul 14 16:57:01 2009
@@ -255,6 +255,23 @@
 		p = p->next;
 	}
 	return(p);
+}
+
+static void
+update_proc_cause(layer3_proc_t *pc, u_char cause)
+{
+	switch(pc->cause) {
+	case CAUSE_USER_BUSY:
+		break;
+	case CAUSE_CALL_REJECTED:
+		if (cause == CAUSE_USER_BUSY) {
+			pc->cause = cause;
+		}
+		break;
+	default:
+		pc->cause = cause;
+		break;
+	}
 }
 
 u_char *
@@ -975,8 +992,32 @@
 	newl3state(pc, 19);
 	test_and_clear_bit(FLG_L3P_TIMER308_1, &pc->Flags);
 	L3AddTimer(&pc->timer1, T308, 0x308);
-	if (mISDN_l3up(pc, umsg))
-		free_msg(umsg);
+
+	/* Q.931 5.2.5.3: this transition must not send a CC_DISCONNECT directly on PTMP link */
+	if (pc->master) {
+		/*
+		 * 2 cases:
+		 * T312 running:
+		 *   - keep T312 (at master) and T310 running (not used)
+		 *   - retain cause (as in l3dss1_release_cmpl_m but at master)
+		 *   - stop T304 (is already stopped)
+		 *   - on T312 timeout CC_DISCONNECT with retained cause
+		 * T312 not running:
+		 *   - retain cause (as in l3dss1_release_cmpl_m but at master)
+		 *   - stop T304 (is already stopped)
+		 *   - then CC_DISCONNECT with retained cause after all other users finished clearing
+		 */
+		l3_debug(pc->l3, "%s: DISC saving cause (%d/%d)", __FUNCTION__, pc->cause, pc->err);
+		if (!cause) {
+			update_proc_cause(pc, pc->err);
+		}
+	}
+	/* only pass to CC if disconnecting the selected proc or master itself */
+	if (!pc->master || pc->master->selces == pc->ces) {
+		if (mISDN_l3up(pc, umsg)) {
+			free_msg(umsg);
+		}
+	}
 }
 
 static void
@@ -1085,8 +1126,12 @@
 		find_and_copy_ie(msg->data, msg->len, IE_SIGNAL, 0, umsg);
 	relc->USER_USER =
 		find_and_copy_ie(msg->data, msg->len, IE_USER_USER, 0, umsg);
-	if (mISDN_l3up(pc, umsg))
-		free_msg(umsg);
+	/* only pass to CC if completing the selected proc or master itself */
+	if (!pc->master || pc->master->selces == pc->ces) {
+		if (mISDN_l3up(pc, umsg)) {
+			free_msg(umsg);
+		}
+	}
 	send_proc(pc, IMSG_END_PROC_M, NULL);
 }
 
@@ -1490,6 +1535,7 @@
 		l3_debug(pc->l3, "cannot create child\n");
 		return(NULL);
 	}
+	l3_debug(pc->l3, "%s: proc %p ces(%x) created", __FUNCTION__, p3i, p3i->ces);
 	p3i->state = pc->state;
 	if (pc->state != -1)
 		newl3state(pc, state);
@@ -1559,16 +1605,7 @@
 		if ((p = l3dss1_get_cause(pc, msg, NULL))) {
 			dprint(DBGM_L3, pc->l3->nst->cardnr,"%s cause (%d/%d)\n", __FUNCTION__,
 				pc->cause, pc->err);
-			switch(pc->cause) {
-				case CAUSE_USER_BUSY:
-					break;
-				case CAUSE_CALL_REJECTED:
-					if (pc->err == CAUSE_USER_BUSY)
-						pc->cause = pc->err;
-					break;
-				default:
-					pc->cause = pc->err;
-			}
+			update_proc_cause(pc, pc->err);
 		}
 		test_and_set_bit(FLG_L3P_GOTRELCOMP, &pc->Flags);
 	}
@@ -2302,9 +2339,29 @@
 	if (pc->state == 22 || pc->state == 25 || pc->state == 9 || pc->state == 7) {
 		StopAllL3Timer(pc);
 		if (!pc->child) {
-			if_link(pc->l3->nst->manager, (ifunc_t)pc->l3->nst->l3_manager,
-				CC_TIMEOUT | INDICATION,pc->ces |
-				(pc->callref << 16), sizeof(int), &t, 0);
+			/* Q.931 5.2.5.3: pass the retained cause (if any) with CC_RELEASE_COMPLETE */
+			if (pc->cause) {
+				msg_t *msg;
+				RELEASE_COMPLETE_t *relc;
+
+				msg = prep_l3data_msg(CC_RELEASE_COMPLETE | INDICATION,
+					pc->ces | (pc->callref << 16),
+					sizeof(RELEASE_COMPLETE_t), 3, NULL);
+				if (msg) {
+					relc = (RELEASE_COMPLETE_t *)(msg->data + mISDNUSER_HEAD_SIZE);
+					relc->CAUSE = msg_put(msg, 3);
+					relc->CAUSE[0] = 2;
+					relc->CAUSE[1] = 0x80;
+					relc->CAUSE[2] = pc->cause | 0x80;
+					if (mISDN_l3up(pc, msg)) {
+						free_msg(msg);
+					}
+				}
+			} else {
+				if_link(pc->l3->nst->manager, (ifunc_t)pc->l3->nst->l3_manager,
+					CC_TIMEOUT | INDICATION,pc->ces |
+					(pc->callref << 16), sizeof(int), &t, 0);
+			}
 			send_proc(pc, IMSG_END_PROC_M, NULL);
 		}
 	}
@@ -2683,11 +2740,32 @@
 		case IMSG_END_PROC_M:
 			RemoveAllL3Timer(proc);
 			if (!proc->master && !arg) {
-				if_link(proc->l3->nst->manager,
-					(ifunc_t)proc->l3->nst->l3_manager,
-					CC_RELEASE_CR | INDICATION,
-					proc->ces | (proc->callref << 16),
-					sizeof(int), &proc->err, 0);
+				/* Q.931 5.2.5.3: pass the retained cause (if any) with CC_RELEASE_COMPLETE */
+				l3_debug(proc->l3, "%s: cause(%d)", __FUNCTION__, proc->cause);
+				if (proc->cause) {
+					msg_t *msg;
+					RELEASE_COMPLETE_t *relc;
+
+					msg = prep_l3data_msg(CC_RELEASE_COMPLETE | INDICATION,
+						proc->ces | (proc->callref << 16),
+						sizeof(RELEASE_COMPLETE_t), 3, NULL);
+					if (msg) {
+						relc = (RELEASE_COMPLETE_t *)(msg->data + mISDNUSER_HEAD_SIZE);
+						relc->CAUSE = msg_put(msg, 3);
+						relc->CAUSE[0] = 2;
+						relc->CAUSE[1] = 0x80;
+						relc->CAUSE[2] = proc->cause | 0x80;
+						if (mISDN_l3up(proc, msg)) {
+							free_msg(msg);
+						}
+					}
+				} else {
+					if_link(proc->l3->nst->manager,
+						(ifunc_t)proc->l3->nst->l3_manager,
+						CC_RELEASE_CR | INDICATION,
+						proc->ces | (proc->callref << 16),
+						sizeof(int), &proc->err, 0);
+				}
 			}
 			while (proc->child)
 				send_proc(proc->child, IMSG_END_PROC, NULL);
@@ -2698,6 +2776,11 @@
 			if (proc->l3 && (proc == proc->l3->proc) )
 				proc->l3->proc = proc->next;
 			if (proc->master) {
+				if (proc->cause && !proc->master->selces) {
+					/* Q.931 5.2.5.3: update master with the retained cause */
+					l3_debug(proc->l3, "%s: cause(%d) -> master", __FUNCTION__, proc->cause);
+					update_proc_cause(proc->master, proc->cause);
+				}
 				if (proc->master->child == proc)
 					proc->master->child = proc->next;
 				if (op == IMSG_END_PROC_M)
@@ -2798,6 +2881,8 @@
 				CC_NEW_CR | INDICATION, proc->ces |
 				(proc->callref << 16), sizeof(int), &i, 0);
 			proc->ces = proc->selces;
+			/* forget all causes collected so far */
+			proc->cause = 0;
 			send_proc(selp, IMSG_END_PROC, NULL);
 			break;
 		case IMSG_RELEASE_CHILDS:
@@ -3104,6 +3189,7 @@
 
 	if (!l3p || !l3p->l3 || !l3p->l3->nst)
 		return(-EINVAL);
+	dprint(DBGM_L3, l3p->l3->nst->cardnr, "%s: pr(%x)\n", __FUNCTION__, ((mISDNuser_head_t *)msg->data)->prim);
 	if (l3p->l3->nst->l3_manager)
 		err = l3p->l3->nst->l3_manager(l3p->l3->nst->manager, msg);
 	if (err)




More information about the svn-commits mailing list