ISDN BRI does not recover from line faults
Q.921 was getting stuck in state 2 (Q921_ASSIGN_AWAITING_TEI). For some reason the network was removing the TEI. Libpri then immediately tried to get a new TEI assigned. The network did not reply to the N202(3) attempts to get a new TEI. Libpri then just gave up trying but did not leave the state. Some paths in Q.921 Figure B.3 were not implemented. Q.921 now transitions to the Q921_TEI_UNASSIGNED state when the N202 count is exceeded. Q.921 will wait there until an incoming or outgoing call is attempted. * Fixed initializing the n202_counter. Not initializing the n202_counter would cause the Q921_TEI_IDENTITY_REQUEST to unexpectedly not go out and due to how state transitions were done, Q.921 would get stuck in the Q921_ASSIGN_AWAITING_TEI state. * Fixed start T202 timer fail causing Q.921 to get stuck in the Q921_ASSIGN_AWAITING_TEI state if the network did not respond to the request. * Fixed handling of Q921_TEI_IDENTITY_REMOVE to do the MDL-REMOVE primitive (q921_mdl_remove()) instead of transitioning directly to the Q921_TEI_UNASSIGNED state. Necessary state clean-up was not getting done. * Minor tweaks to q921_mdl_remove(). The worst problem was erroneously generating an error message. * Fixed potential for sending I-frames with an invalid TEI. The I-frame could have been queued when Q.921 did not have an assigned TEI. * Fixed testing of the q931_receive() return value when a UI-frame is received. (closes issue #17570) Reported by: jcovert Patches: issue17570_v1.4.11.3_v3.patch uploaded by rmudgett (license 664) issue17570_v1.4_v3.patch uploaded by rmudgett (license 664) Tested by: jcovert, rmudgett git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@1918 2fbb986a-6c06-0410-b554-c9c1f0a7f128
This commit is contained in:
parent
e12b0913ec
commit
da0b057536
117
q921.c
117
q921.c
@ -196,19 +196,49 @@ static void q921_send_tei(struct pri *ctrl, int message, int ri, int ai, int isc
|
||||
free(f);
|
||||
}
|
||||
|
||||
static void q921_tei_request(void *vpri)
|
||||
static void t202_expire(void *vpri)
|
||||
{
|
||||
struct pri *ctrl = (struct pri *)vpri;
|
||||
ctrl->n202_counter++;
|
||||
if (ctrl->n202_counter > ctrl->timers[PRI_TIMER_N202]) {
|
||||
pri_error(ctrl, "Unable to receive TEI from network!\n");
|
||||
ctrl->n202_counter = 0;
|
||||
|
||||
/* Start the TEI request timer. */
|
||||
pri_schedule_del(ctrl, ctrl->t202_timer);
|
||||
ctrl->t202_timer =
|
||||
pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T202], t202_expire, ctrl);
|
||||
|
||||
++ctrl->n202_counter;
|
||||
if (!ctrl->t202_timer || ctrl->n202_counter > ctrl->timers[PRI_TIMER_N202]) {
|
||||
if (!ctrl->t202_timer) {
|
||||
pri_error(ctrl, "Could not start T202 timer.");
|
||||
} else {
|
||||
pri_schedule_del(ctrl, ctrl->t202_timer);
|
||||
ctrl->t202_timer = 0;
|
||||
}
|
||||
pri_error(ctrl, "Unable to receive TEI from network in state %d(%s)!\n",
|
||||
ctrl->q921_state, q921_state2str(ctrl->q921_state));
|
||||
switch (ctrl->q921_state) {
|
||||
case Q921_ASSIGN_AWAITING_TEI:
|
||||
break;
|
||||
case Q921_ESTABLISH_AWAITING_TEI:
|
||||
q921_discard_iqueue(ctrl);
|
||||
/* DL-RELEASE indication */
|
||||
q931_dl_indication(ctrl, PRI_EVENT_DCHAN_DOWN);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
q921_setstate(ctrl, Q921_TEI_UNASSIGNED);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Send TEI request */
|
||||
ctrl->ri = random() % 65535;
|
||||
q921_send_tei(PRI_MASTER(ctrl), Q921_TEI_IDENTITY_REQUEST, ctrl->ri, Q921_TEI_GROUP, 1);
|
||||
pri_schedule_del(ctrl, ctrl->t202_timer);
|
||||
ctrl->t202_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T202], q921_tei_request, ctrl);
|
||||
}
|
||||
|
||||
static void q921_tei_request(struct pri *ctrl)
|
||||
{
|
||||
ctrl->n202_counter = 0;
|
||||
t202_expire(ctrl);
|
||||
}
|
||||
|
||||
static void q921_send_ua(struct pri *ctrl, int pfbit)
|
||||
@ -397,6 +427,14 @@ static int q921_send_queued_iframes(struct pri *ctrl)
|
||||
if (ctrl->debug & PRI_DEBUG_Q921_DUMP)
|
||||
pri_message(ctrl, "-- Finally transmitting %d, since window opened up (%d)\n", f->h.n_s, ctrl->timers[PRI_TIMER_K]);
|
||||
f->transmitted++;
|
||||
|
||||
/*
|
||||
* Send the frame out on the assigned TEI.
|
||||
* Done now because the frame may have been queued before we
|
||||
* had an assigned TEI.
|
||||
*/
|
||||
f->h.h.tei = ctrl->tei;
|
||||
|
||||
f->h.n_s = ctrl->v_s;
|
||||
f->h.n_r = ctrl->v_r;
|
||||
f->h.ft = 0;
|
||||
@ -619,9 +657,16 @@ int q921_transmit_iframe(struct pri *vpri, int tei, void *buf, int len, int cr)
|
||||
/* We don't care what the tei is, since we only support one sub and one TEI */
|
||||
ctrl = PRI_MASTER(vpri)->subchannel;
|
||||
|
||||
if (ctrl->q921_state == Q921_TEI_UNASSIGNED) {
|
||||
q921_tei_request(ctrl);
|
||||
switch (ctrl->q921_state) {
|
||||
case Q921_TEI_UNASSIGNED:
|
||||
q921_setstate(ctrl, Q921_ESTABLISH_AWAITING_TEI);
|
||||
q921_tei_request(ctrl);
|
||||
break;
|
||||
case Q921_ASSIGN_AWAITING_TEI:
|
||||
q921_setstate(ctrl, Q921_ESTABLISH_AWAITING_TEI);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Should just be PTP modes, which shouldn't have subs */
|
||||
@ -636,9 +681,7 @@ int q921_transmit_iframe(struct pri *vpri, int tei, void *buf, int len, int cr)
|
||||
ctrl->l3initiated = 1;
|
||||
q921_setstate(ctrl, Q921_AWAITING_ESTABLISHMENT);
|
||||
/* For all rest, we've done the work to get us up prior to this and fall through */
|
||||
case Q921_TEI_UNASSIGNED:
|
||||
case Q921_ESTABLISH_AWAITING_TEI:
|
||||
case Q921_ASSIGN_AWAITING_TEI:
|
||||
case Q921_TIMER_RECOVERY:
|
||||
case Q921_AWAITING_ESTABLISHMENT:
|
||||
case Q921_MULTI_FRAME_ESTABLISHED:
|
||||
@ -698,6 +741,8 @@ int q921_transmit_iframe(struct pri *vpri, int tei, void *buf, int len, int cr)
|
||||
pri_error(ctrl, "!! Out of memory for Q.921 transmit\n");
|
||||
return -1;
|
||||
}
|
||||
case Q921_TEI_UNASSIGNED:
|
||||
case Q921_ASSIGN_AWAITING_TEI:
|
||||
case Q921_AWAITING_RELEASE:
|
||||
default:
|
||||
pri_error(ctrl, "Cannot transmit frames in state %d(%s)\n",
|
||||
@ -1049,6 +1094,8 @@ static pri_event *q921_receive_MDL(struct pri *ctrl, q921_u *h, int len)
|
||||
q921_establish_data_link(ctrl);
|
||||
ctrl->l3initiated = 1;
|
||||
q921_setstate(ctrl, Q921_AWAITING_ESTABLISHMENT);
|
||||
ctrl->ev.gen.e = PRI_EVENT_DCHAN_UP;
|
||||
res = &ctrl->ev;
|
||||
break;
|
||||
default:
|
||||
pri_error(ctrl, "Error 3\n");
|
||||
@ -1072,10 +1119,17 @@ static pri_event *q921_receive_MDL(struct pri *ctrl, q921_u *h, int len)
|
||||
if (!BRI_TE_PTMP(ctrl))
|
||||
return NULL;
|
||||
|
||||
if (ctrl->subchannel->q921_state < Q921_TEI_ASSIGNED) {
|
||||
/* We do not have a TEI. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If it's addressed to the group TEI or to our TEI specifically, we respond */
|
||||
if ((tei == Q921_TEI_GROUP) || (tei == ctrl->subchannel->tei)) {
|
||||
q921_setstate(ctrl->subchannel, Q921_TEI_UNASSIGNED);
|
||||
q921_mdl_remove(ctrl->subchannel);
|
||||
q921_start(ctrl->subchannel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return res; /* Do we need to return something??? */
|
||||
}
|
||||
@ -1182,6 +1236,18 @@ static pri_event *q921_disc_rx(struct pri *ctrl, q921_h *h)
|
||||
|
||||
static void q921_mdl_remove(struct pri *ctrl)
|
||||
{
|
||||
int mdl_free_me;
|
||||
|
||||
if (BRI_NT_PTMP(ctrl)) {
|
||||
if (ctrl == PRI_MASTER(ctrl)) {
|
||||
pri_error(ctrl, "Bad bad bad! Cannot MDL-REMOVE master\n");
|
||||
return;
|
||||
}
|
||||
mdl_free_me = 1;
|
||||
} else {
|
||||
mdl_free_me = 0;
|
||||
}
|
||||
|
||||
switch (ctrl->q921_state) {
|
||||
case Q921_TEI_ASSIGNED:
|
||||
/* XXX: deviation! Since we don't have a UI queue, we just discard our I-queue */
|
||||
@ -1192,7 +1258,6 @@ static void q921_mdl_remove(struct pri *ctrl)
|
||||
q921_discard_iqueue(ctrl);
|
||||
/* DL-RELEASE indication */
|
||||
q931_dl_indication(ctrl, PRI_EVENT_DCHAN_DOWN);
|
||||
|
||||
stop_t200(ctrl);
|
||||
q921_setstate(ctrl, Q921_TEI_UNASSIGNED);
|
||||
break;
|
||||
@ -1200,6 +1265,7 @@ static void q921_mdl_remove(struct pri *ctrl)
|
||||
q921_discard_iqueue(ctrl);
|
||||
/* DL-RELEASE confirm */
|
||||
stop_t200(ctrl);
|
||||
q921_setstate(ctrl, Q921_TEI_UNASSIGNED);
|
||||
break;
|
||||
case Q921_MULTI_FRAME_ESTABLISHED:
|
||||
q921_discard_iqueue(ctrl);
|
||||
@ -1215,19 +1281,14 @@ static void q921_mdl_remove(struct pri *ctrl)
|
||||
q931_dl_indication(ctrl, PRI_EVENT_DCHAN_DOWN);
|
||||
stop_t200(ctrl);
|
||||
q921_setstate(ctrl, Q921_TEI_UNASSIGNED);
|
||||
default:
|
||||
pri_error(ctrl, "Cannot handle MDL remove when PRI is in state %d(%s)\n",
|
||||
ctrl->q921_state, q921_state2str(ctrl->q921_state));
|
||||
break;
|
||||
default:
|
||||
pri_error(ctrl, "MDL-REMOVE when in state %d(%s)\n",
|
||||
ctrl->q921_state, q921_state2str(ctrl->q921_state));
|
||||
return;
|
||||
}
|
||||
|
||||
if (BRI_NT_PTMP(ctrl) && ctrl->q921_state == Q921_TEI_UNASSIGNED) {
|
||||
if (ctrl == PRI_MASTER(ctrl)) {
|
||||
pri_error(ctrl, "Bad bad bad! Asked to free master\n");
|
||||
return;
|
||||
}
|
||||
ctrl->mdl_free_me = 1;
|
||||
}
|
||||
ctrl->mdl_free_me = mdl_free_me;
|
||||
}
|
||||
|
||||
static int q921_mdl_handle_network_error(struct pri *ctrl, char error)
|
||||
@ -2020,19 +2081,15 @@ static pri_event *__q921_receive_qualified(struct pri *ctrl, q921_h *h, int len)
|
||||
break;
|
||||
} else if (!h->u.m2) {
|
||||
if ((ctrl->sapi == Q921_SAPI_LAYER2_MANAGEMENT) && (ctrl->tei == Q921_TEI_GROUP)) {
|
||||
|
||||
pri_error(ctrl, "I should never be called\n");
|
||||
q921_receive_MDL(ctrl, (q921_u *)h, len);
|
||||
|
||||
ev = q921_receive_MDL(ctrl, (q921_u *)h, len);
|
||||
} else {
|
||||
int res;
|
||||
|
||||
res = q931_receive(ctrl, ctrl->tei, (q931_h *) h->u.data, len - 3);
|
||||
if (res == -1) {
|
||||
ev = NULL;
|
||||
}
|
||||
if (res & Q931_RES_HAVEEVENT)
|
||||
if (res != -1 && (res & Q931_RES_HAVEEVENT)) {
|
||||
ev = &ctrl->ev;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user