[libpri-commits] rmudgett: branch 1.4 r1982 - /branches/1.4/

SVN commits to the libpri project libpri-commits at lists.digium.com
Mon Sep 13 11:07:31 CDT 2010


Author: rmudgett
Date: Mon Sep 13 11:07:24 2010
New Revision: 1982

URL: http://svnview.digium.com/svn/libpri?view=rev&rev=1982
Log:
BRI PTMP: Active channels not cleared when the interface goes down.

If the connection to the terminal is lost while there are open channels
on the interface, red alarm is reported, but the open channels are never
cleared.  Additionally, if you manually try to channel request hangup,
Asterisk crashes.

For PTMP, the T309 processing was not searching the call pool on the
master control record.  Additionally, for NT PTMP, the timeout events were
not passed to the upper layer because the events were not put on the
master control record where timer processing expects them.

(closes issue #17865)
Reported by: wimpy
Patches:
      issue17865_v1.4.patch uploaded by rmudgett (license 664)
Tested by: rmudgett, wimpy

Modified:
    branches/1.4/pri.c
    branches/1.4/pri_internal.h
    branches/1.4/pri_q931.h
    branches/1.4/prisched.c
    branches/1.4/q921.c
    branches/1.4/q931.c

Modified: branches/1.4/pri.c
URL: http://svnview.digium.com/svn/libpri/branches/1.4/pri.c?view=diff&rev=1982&r1=1981&r2=1982
==============================================================================
--- branches/1.4/pri.c (original)
+++ branches/1.4/pri.c Mon Sep 13 11:07:24 2010
@@ -1478,7 +1478,7 @@
 			enum PRI_TIMERS_AND_COUNTERS tmr;
 
 			tmr = pri_timer[idx].number;
-			if (0 <= ctrl->timers[tmr] || tmr == PRI_TIMER_T309) {
+			if (0 <= ctrl->timers[tmr]) {
 				used = pri_snprintf(buf, used, buf_size, "  %s: %d\n",
 					pri_timer[idx].name, ctrl->timers[tmr]);
 			}
@@ -1504,8 +1504,11 @@
 
 void pri_enslave(struct pri *master, struct pri *slave)
 {
-	if (master && slave)
+	if (master && slave) {
 		slave->callpool = &master->localpool;
+		slave->nfas = 1;
+		master->nfas = 1;
+	}
 }
 
 struct pri_sr *pri_sr_new(void)

Modified: branches/1.4/pri_internal.h
URL: http://svnview.digium.com/svn/libpri/branches/1.4/pri_internal.h?view=diff&rev=1982&r1=1981&r2=1982
==============================================================================
--- branches/1.4/pri_internal.h (original)
+++ branches/1.4/pri_internal.h Mon Sep 13 11:07:24 2010
@@ -96,6 +96,7 @@
 	int sapi;
 	int tei;
 	int protodisc;
+	unsigned int nfas:1;/* TRUE if this D channel is involved with an NFAS group */
 	unsigned int bri:1;
 	unsigned int acceptinbanddisconnect:1;	/* Should we allow inband progress after DISCONNECT? */
 	unsigned int sendfacility:1;
@@ -886,6 +887,7 @@
 extern pri_event *pri_schedule_run(struct pri *pri);
 
 extern void pri_schedule_del(struct pri *pri, int ev);
+int pri_schedule_check(struct pri *ctrl, int id, void (*function)(void *data), void *data);
 
 extern pri_event *pri_mkerror(struct pri *pri, char *errstr);
 

Modified: branches/1.4/pri_q931.h
URL: http://svnview.digium.com/svn/libpri/branches/1.4/pri_q931.h?view=diff&rev=1982&r1=1981&r2=1982
==============================================================================
--- branches/1.4/pri_q931.h (original)
+++ branches/1.4/pri_q931.h Mon Sep 13 11:07:24 2010
@@ -502,7 +502,8 @@
 
 void q931_destroycall(struct pri *pri, q931_call *c);
 
-extern void q931_dl_indication(struct pri *pri, int event);
+void q931_dl_tei_removal(struct pri *link);
+void q931_dl_indication(struct pri *link, int event);
 
 int q931_send_hold(struct pri *ctrl, struct q931_call *call);
 int q931_send_hold_ack(struct pri *ctrl, struct q931_call *call);

Modified: branches/1.4/prisched.c
URL: http://svnview.digium.com/svn/libpri/branches/1.4/prisched.c?view=diff&rev=1982&r1=1981&r2=1982
==============================================================================
--- branches/1.4/prisched.c (original)
+++ branches/1.4/prisched.c Mon Sep 13 11:07:24 2010
@@ -256,3 +256,32 @@
 			ctrl->sched.num_slots);
 	}
 }
+
+/*!
+ * \brief Is the scheduled event this callback.
+ *
+ * \param ctrl D channel controller.
+ * \param id Scheduled event id to check.
+ * 0 is a disabled/unscheduled event id.
+ * 1 - MAX_SCHED is a valid event id.
+ * \param function Callback function to call when timeout.
+ * \param data Value to give callback function when timeout.
+ *
+ * \return TRUE if scheduled event has the callback.
+ */
+int pri_schedule_check(struct pri *ctrl, int id, void (*function)(void *data), void *data)
+{
+	/* Scheduling runs on master channels only */
+	ctrl = PRI_MASTER(ctrl);
+
+	if (0 < id && id <= ctrl->sched.num_slots) {
+		if (ctrl->sched.timer[id - 1].callback == function
+			&& ctrl->sched.timer[id - 1].data == data) {
+			return 1;
+		}
+	} else if (id) {
+		pri_error(ctrl, "Asked to check sched id %d??? num_slots=%d\n", id,
+			ctrl->sched.num_slots);
+	}
+	return 0;
+}

Modified: branches/1.4/q921.c
URL: http://svnview.digium.com/svn/libpri/branches/1.4/q921.c?view=diff&rev=1982&r1=1981&r2=1982
==============================================================================
--- branches/1.4/q921.c (original)
+++ branches/1.4/q921.c Mon Sep 13 11:07:24 2010
@@ -1433,6 +1433,8 @@
 		return;
 	}
 
+	q931_dl_tei_removal(ctrl);
+
 	/*
 	 * Negate the TEI value so debug messages will display a
 	 * negated TEI when it is actually unassigned.

Modified: branches/1.4/q931.c
URL: http://svnview.digium.com/svn/libpri/branches/1.4/q931.c?view=diff&rev=1982&r1=1981&r2=1982
==============================================================================
--- branches/1.4/q931.c (original)
+++ branches/1.4/q931.c Mon Sep 13 11:07:24 2010
@@ -4879,10 +4879,11 @@
 {
 	struct q931_call *c = data;
 	struct pri *ctrl = c->pri;
+
 	if (ctrl->debug & PRI_DEBUG_Q931_STATE)
 		pri_message(ctrl, "Timed out looking for connect acknowledge\n");
+	c->retranstimer = 0;
 	q931_disconnect(ctrl, c, PRI_CAUSE_NORMAL_CLEARING);
-	
 }
 
 /* T308 expiry, first time */
@@ -4890,9 +4891,11 @@
 {
 	struct q931_call *c = data;
 	struct pri *ctrl = c->pri;
+
 	if (ctrl->debug & PRI_DEBUG_Q931_STATE)
 		pri_message(ctrl, "Timed out looking for release complete\n");
 	c->t308_timedout++;
+	c->retranstimer = 0;
 	c->alive = 1;
 
 	/* The call to q931_release will re-schedule T308 */
@@ -4904,6 +4907,8 @@
 {
 	struct q931_call *c = data;
 	struct pri *ctrl = c->pri;
+
+	c->retranstimer = 0;
 	c->alive = 1;
 	if (ctrl->debug & PRI_DEBUG_Q931_STATE)
 		pri_message(ctrl, "Final time-out looking for release complete\n");
@@ -4930,8 +4935,10 @@
 {
 	struct q931_call *c = data;
 	struct pri *ctrl = c->pri;
+
 	if (ctrl->debug & PRI_DEBUG_Q931_STATE)
 		pri_message(ctrl, "Timed out looking for release\n");
+	c->retranstimer = 0;
 	c->alive = 1;
 	q931_release(ctrl, c, PRI_CAUSE_NORMAL_CLEARING);
 }
@@ -5266,6 +5273,9 @@
 
 	c->cc.saved_ie_contents.length = 0;
 	c->cc.saved_ie_flags = 0;
+	if (BRI_NT_PTMP(ctrl)) {
+		c->outboundbroadcast = 1;
+	}
 	if (ctrl->subchannel && !ctrl->bri)
 		res = send_message(ctrl, c, Q931_SETUP, gr303_setup_ies);
 	else if (c->cis_call)
@@ -5279,13 +5289,9 @@
 		UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_INITIATED);
 		c->peercallstate = Q931_CALL_STATE_CALL_PRESENT;
 		c->t303_expirycnt = 0;
-		if (BRI_NT_PTMP(ctrl)) {
-			c->outboundbroadcast = 1;
-		}
 		start_t303(c);
 	}
 	return res;
-	
 }
 
 static int register_ies[] = { Q931_IE_FACILITY, -1 };
@@ -5845,6 +5851,12 @@
 	struct q931_call *c = data;
 	struct pri *ctrl = c->pri;
 
+	/*
+	 * We cannot clear the retranstimer id because we are called by t303_expiry also.
+	 * Fortunately, it doesn't matter because pri_internal_clear() will stop it if
+	 * it was actually running.
+	 */
+	//c->retranstimer = 0;
 	c->performing_fake_clearing = 1;
 	if (pri_internal_clear(c) == Q931_RES_HAVEEVENT)
 		ctrl->schedev = 1;
@@ -5852,6 +5864,7 @@
 
 static void pri_create_fake_clearing(struct q931_call *c, struct pri *master)
 {
+	/* Point to the master so the timeout event can come out. */
 	c->pri = master;
 
 	pri_schedule_del(master, c->retranstimer);
@@ -8279,6 +8292,18 @@
 
 	UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL);
 	c->peercallstate = Q931_CALL_STATE_NULL;
+
+	if (c->master_call->outboundbroadcast
+		&& c == q931_find_winning_call(c)) {
+		/* Pass the hangup cause to the master_call. */
+		c->master_call->cause = c->cause;
+
+		/* Declare this winning subcall to no longer be the winner and destroy it. */
+		c->master_call->pri_winner = -1;
+		q931_destroycall(ctrl, c);
+		return 0;
+	}
+
 	q931_clr_subcommands(ctrl);
 	ctrl->ev.hangup.subcmds = &ctrl->subcmds;
 	ctrl->ev.hangup.channel = q931_encode_channel(c);
@@ -8320,9 +8345,15 @@
 {
 	struct q931_call *c = data;
 	struct pri *ctrl = c->pri;
+
+	/* Point to the master so the timeout event can come out. */
+	ctrl = PRI_MASTER(ctrl);
+	c->pri = ctrl;
+
 	if (ctrl->debug & PRI_DEBUG_Q931_STATE)
-		pri_message(ctrl, DBGHEAD "Timed out waiting for data link re-establishment\n", DBGINFO);
-
+		pri_message(ctrl, "T309 timed out waiting for data link re-establishment\n");
+
+	c->retranstimer = 0;
 	c->cause = PRI_CAUSE_DESTINATION_OUT_OF_ORDER;
 	if (pri_internal_clear(c) == Q931_RES_HAVEEVENT)
 		ctrl->schedev = 1;
@@ -8333,94 +8364,260 @@
 {
 	struct q931_call *c = data;
 	struct pri *ctrl = c->pri;
+
+	/* Point to the master so the timeout event can come out. */
+	ctrl = PRI_MASTER(ctrl);
+	c->pri = ctrl;
+
 	if (ctrl->debug & PRI_DEBUG_Q931_STATE)
-		pri_message(ctrl, DBGHEAD "Cancel non active call after data link failure\n", DBGINFO);
-
+		pri_message(ctrl, "Cancel call after data link failure\n");
+
+	c->retranstimer = 0;
 	c->cause = PRI_CAUSE_DESTINATION_OUT_OF_ORDER;
 	if (pri_internal_clear(c) == Q931_RES_HAVEEVENT)
 		ctrl->schedev = 1;
 }
 
+/*!
+ * \brief Layer 2 is removing the link's TEI.
+ *
+ * \param link Q.921 link losing it's TEI.
+ *
+ * \note
+ * For NT PTMP, this deviation from the specifications is needed
+ * because we have no way to re-associate any T309 calls on the
+ * removed TEI.
+ *
+ * \return Nothing
+ */
+void q931_dl_tei_removal(struct pri *link)
+{
+	struct q931_call *cur;
+	struct q931_call *call;
+	struct pri *ctrl;
+	int idx;
+
+	/* Find the master - He has the call pool */
+	ctrl = PRI_MASTER(link);
+
+	if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
+		pri_message(ctrl, "DL TEI removal\n");
+	}
+
+	if (!BRI_NT_PTMP(ctrl)) {
+		/* Only NT PTMP has anything to worry about when the TEI is removed. */
+		return;
+	}
+
+	for (cur = *ctrl->callpool; cur; cur = cur->next) {
+		if (!(cur->cr & ~Q931_CALL_REFERENCE_FLAG)) {
+			/* Don't do anything on the global call reference call record. */
+			continue;
+		}
+		if (cur->outboundbroadcast) {
+			/* Does this master call have a subcall on the link that went down? */
+			call = NULL;
+			for (idx = 0; idx < ARRAY_LEN(cur->subcalls); ++idx) {
+				if (cur->subcalls[idx] && cur->subcalls[idx]->pri == link) {
+					/* This subcall is on the link that went down. */
+					call = cur->subcalls[idx];
+					break;
+				}
+			}
+			if (!call) {
+				/* No subcall is on the link that went down. */
+				continue;
+			}
+		} else if (cur->pri != link) {
+			/* This call is not on the link that went down. */
+			continue;
+		} else {
+			call = cur;
+		}
+
+		/*
+		 * NOTE:  We are gambling that no T309 timer's have had a chance
+		 * to expire.  They should not expire since we are either called
+		 * immediately after the q931_dl_indication() or after a timeout
+		 * of 0.
+		 */
+		if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
+			pri_message(ctrl, "Cancel call cref=%d on channel %d in state %d (%s)\n",
+				call->cr, call->channelno, call->ourcallstate,
+				q931_call_state_str(call->ourcallstate));
+		}
+		call->pri = ctrl;/* Point to a safer place until the call is destroyed. */
+		pri_schedule_del(ctrl, call->retranstimer);
+		call->retranstimer = pri_schedule_event(ctrl, 0, pri_dl_down_cancelcall, call);
+	}
+}
+
 /* Receive an indication from Layer 2 */
-void q931_dl_indication(struct pri *ctrl, int event)
+void q931_dl_indication(struct pri *link, int event)
 {
 	struct q931_call *cur;
-	struct q931_call *winner;
-
-	/* Just return if T309 is not enabled. */
-	if (!ctrl || ctrl->timers[PRI_TIMER_T309] < 0)
+	struct q931_call *call;
+	struct pri *ctrl;
+	int idx;
+
+	if (!link) {
 		return;
+	}
+
+	/* Find the master - He has the call pool */
+	ctrl = PRI_MASTER(link);
+
+	if (BRI_TE_PTMP(ctrl)) {
+		/* The link is always the master */
+		link = ctrl;
+	}
 
 	switch (event) {
 	case PRI_EVENT_DCHAN_DOWN:
 		if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
-			pri_message(ctrl, DBGHEAD "link is DOWN\n", DBGINFO);
+			pri_message(ctrl, "DL-RELEASE indication (link is DOWN)\n");
 		}
 		for (cur = *ctrl->callpool; cur; cur = cur->next) {
 			if (!(cur->cr & ~Q931_CALL_REFERENCE_FLAG)) {
 				/* Don't do anything on the global call reference call record. */
 				continue;
-			} else if (cur->ourcallstate == Q931_CALL_STATE_ACTIVE) {
-				/* For a call in Active state, activate T309 only if there is no timer already running. */
-				if (!cur->retranstimer) {
+			}
+			if (cur->outboundbroadcast) {
+				/* Does this master call have a subcall on the link that went down? */
+				call = NULL;
+				for (idx = 0; idx < ARRAY_LEN(cur->subcalls); ++idx) {
+					if (cur->subcalls[idx] && cur->subcalls[idx]->pri == link) {
+						/* This subcall is on the link that went down. */
+						call = cur->subcalls[idx];
+						break;
+					}
+				}
+				if (!call) {
+					/* No subcall is on the link that went down. */
+					continue;
+				}
+			} else if (cur->pri != link) {
+				/* This call is not on the link that went down. */
+				continue;
+			} else {
+				call = cur;
+			}
+			switch (call->ourcallstate) {
+			case Q931_CALL_STATE_ACTIVE:
+				/* NOTE: Only a winning subcall can get to the active state. */
+				if (ctrl->nfas) {
+					/*
+					 * The upper layer should handle T309 for NFAS since the calls
+					 * could be maintained by a backup D channel if the B channel
+					 * for the call did not go into alarm.
+					 */
+					break;
+				}
+				/*
+				 * For a call in Active state, activate T309 only if there is
+				 * no timer already running.
+				 *
+				 * NOTE: cur != call when we have a winning subcall.
+				 */
+				if (!cur->retranstimer || !call->retranstimer) {
 					if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
-						pri_message(ctrl,
-							DBGHEAD "activate T309 for call %d on channel %d\n", DBGINFO,
-							cur->cr, cur->channelno);
+						pri_message(ctrl, "Start T309 for call cref=%d on channel %d\n",
+							call->cr, call->channelno);
 					}
-					cur->retranstimer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T309], pri_dl_down_timeout, cur);
+					call->retranstimer = pri_schedule_event(ctrl,
+						ctrl->timers[PRI_TIMER_T309], pri_dl_down_timeout, call);
 				}
-			} else if (cur->ourcallstate != Q931_CALL_STATE_NULL) {
-				/* For a call that is not in Active state, schedule internal clearing of the call 'ASAP' (delay 0). */
+				break;
+			case Q931_CALL_STATE_NULL:
+				break;
+			default:
+				/*
+				 * For a call that is not in Active state, schedule internal
+				 * clearing of the call 'ASAP' (delay 0).
+				 *
+				 * NOTE: We are killing NFAS calls that are not connected yet
+				 * because there are likely messages in flight when this link
+				 * went down that could leave the call in an unknown/stuck state.
+				 */
 				if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
 					pri_message(ctrl,
-						DBGHEAD "cancel call %d on channel %d in state %d (%s)\n", DBGINFO,
-						cur->cr, cur->channelno, cur->ourcallstate,
-						q931_call_state_str(cur->ourcallstate));
+						"Cancel call cref=%d on channel %d in state %d (%s)\n",
+						call->cr, call->channelno, call->ourcallstate,
+						q931_call_state_str(call->ourcallstate));
 				}
-				pri_schedule_del(ctrl, cur->retranstimer);
-				cur->retranstimer = pri_schedule_event(ctrl, 0, pri_dl_down_cancelcall, cur);
+				if (cur->outboundbroadcast) {
+					/* Simply destroy non-winning subcalls. */
+					q931_destroycall(ctrl, call);
+					continue;
+				}
+				pri_schedule_del(ctrl, call->retranstimer);
+				call->retranstimer = pri_schedule_event(ctrl, 0, pri_dl_down_cancelcall,
+					call);
+				break;
 			}
 		}
 		break;
 	case PRI_EVENT_DCHAN_UP:
 		if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
-			pri_message(ctrl, DBGHEAD "link is UP\n", DBGINFO);
+			pri_message(ctrl, "DL-ESTABLISH indication (link is UP)\n");
 		}
 		for (cur = *ctrl->callpool; cur; cur = cur->next) {
 			if (!(cur->cr & ~Q931_CALL_REFERENCE_FLAG)) {
 				/* Don't do anything on the global call reference call record. */
 				continue;
-			} else if (cur->ourcallstate == Q931_CALL_STATE_ACTIVE && cur->retranstimer) {
-				if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
-					pri_message(ctrl,
-						DBGHEAD "cancel T309 for call %d on channel %d\n", DBGINFO,
-						cur->cr, cur->channelno);
+			}
+			if (cur->outboundbroadcast) {
+				/* Does this master call have a subcall on the link that came up? */
+				call = NULL;
+				for (idx = 0; idx < ARRAY_LEN(cur->subcalls); ++idx) {
+					if (cur->subcalls[idx] && cur->subcalls[idx]->pri == link) {
+						/* This subcall is on the link that came up. */
+						call = cur->subcalls[idx];
+						break;
+					}
 				}
-				pri_schedule_del(ctrl, cur->retranstimer);
-				cur->retranstimer = 0;
-				winner = q931_find_winning_call(cur);
-				if (winner) {
-					q931_status(ctrl, winner, PRI_CAUSE_NORMAL_UNSPECIFIED);
+				if (!call) {
+					/* No subcall is on the link that came up. */
+					continue;
 				}
-			} else if (cur->ourcallstate != Q931_CALL_STATE_NULL &&
-				cur->ourcallstate != Q931_CALL_STATE_DISCONNECT_REQUEST &&
-				cur->ourcallstate != Q931_CALL_STATE_DISCONNECT_INDICATION &&
-				cur->ourcallstate != Q931_CALL_STATE_RELEASE_REQUEST) {
+			} else if (cur->pri != link) {
+				/* This call is not on the link that came up. */
+				continue;
+			} else {
+				call = cur;
+			}
+			switch (call->ourcallstate) {
+			case Q931_CALL_STATE_ACTIVE:
+				/* NOTE: Only a winning subcall can get to the active state. */
+				if (pri_schedule_check(ctrl, call->retranstimer, pri_dl_down_timeout, call)) {
+					if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
+						pri_message(ctrl, "Stop T309 for call cref=%d on channel %d\n",
+							call->cr, call->channelno);
+					}
+					pri_schedule_del(ctrl, call->retranstimer);
+					call->retranstimer = 0;
+				}
+				q931_status(ctrl, call, PRI_CAUSE_NORMAL_UNSPECIFIED);
+				break;
+			case Q931_CALL_STATE_NULL:
+			case Q931_CALL_STATE_DISCONNECT_REQUEST:
+			case Q931_CALL_STATE_DISCONNECT_INDICATION:
+			case Q931_CALL_STATE_RELEASE_REQUEST:
+				break;
+			default:
 				/*
 				 * The STATUS message sent here is not required by Q.931,
 				 * but it may help anyway.
 				 * This looks like a new call started while the link was down.
 				 */
-				winner = q931_find_winning_call(cur);
-				if (winner) {
-					q931_status(ctrl, winner, PRI_CAUSE_NORMAL_UNSPECIFIED);
-				}
+				q931_status(ctrl, call, PRI_CAUSE_NORMAL_UNSPECIFIED);
+				break;
 			}
 		}
 		break;
 	default:
 		pri_message(ctrl, DBGHEAD "unexpected event %d.\n", DBGINFO, event);
+		break;
 	}
 }
 




More information about the libpri-commits mailing list