From fffb7babaf13ee1bdf10ff4c142163ddd4c58b5b Mon Sep 17 00:00:00 2001 From: Richard Mudgett Date: Thu, 28 Jun 2012 00:16:33 +0000 Subject: [PATCH] Implement T316 to allow RESTART messages to be automatically retransmitted. Q.931 defines the T316 timer to retransmit RESTART messages if a RESTART ACKNOWLEDGE message is not received before the timer expires. Q.931 defaults the time of T316 to 2 minutes with the default number of consecutive RESTART failures as two. * To support legacy behavior, the T316 timer is disabled by default. It is also disabled because the user cannot configure it to disabled if it is enabled. * The N316 count is created to allow the number of RESTART attempts to be configurable. Note you will need to recompile Asterisk to be able to configure N316. (issue ASTERISK-19608) (issue AST-815) (closes issue PRI-133) Reported by: Mike Boylan Tested by: rmudgett git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@2288 2fbb986a-6c06-0410-b554-c9c1f0a7f128 --- libpri.h | 3 +- pri.c | 8 +++- pri_internal.h | 10 +++++ q931.c | 114 ++++++++++++++++++++++++++++++++++++++++++------- 4 files changed, 117 insertions(+), 18 deletions(-) diff --git a/libpri.h b/libpri.h index 1faba8b..b1f8a3d 100644 --- a/libpri.h +++ b/libpri.h @@ -2179,7 +2179,7 @@ enum PRI_TIMERS_AND_COUNTERS { PRI_TIMER_T310, /*!< Maximum time between receiving a CALL_PROCEEDING and receiving a ALERT/CONNECT/DISCONNECT/PROGRESS */ PRI_TIMER_T313, /*!< Wait for CONNECT acknowledge, CPE side only */ PRI_TIMER_T314, - PRI_TIMER_T316, /*!< Maximum time between transmitting a RESTART and receiving a RESTART ACK */ + PRI_TIMER_T316, /*!< Time to wait for a RESTART ACK before retransmitting RESTART. (Timer enabled if greater than zero.) */ PRI_TIMER_T317, PRI_TIMER_T318, PRI_TIMER_T319, @@ -2221,6 +2221,7 @@ enum PRI_TIMERS_AND_COUNTERS { PRI_TIMER_QSIG_CC_T4, /*!< Path reservation supervision timeout. */ PRI_TIMER_T312, /*!< Supervise broadcast SETUP message call reference retention. */ + PRI_TIMER_N316, /*!< Number of times to transmit RESTART before giving up if T316 enabled. */ /* Must be last in the enum list */ PRI_MAX_TIMERS diff --git a/pri.c b/pri.c index 162dca0..340e239 100644 --- a/pri.c +++ b/pri.c @@ -81,6 +81,7 @@ static const struct pri_timer_table pri_timer[] = { { "T313", PRI_TIMER_T313, PRI_ALL_SWITCHES }, { "T314", PRI_TIMER_T314, PRI_ALL_SWITCHES }, { "T316", PRI_TIMER_T316, PRI_ALL_SWITCHES }, + { "N316", PRI_TIMER_N316, PRI_ALL_SWITCHES }, { "T317", PRI_TIMER_T317, PRI_ALL_SWITCHES }, { "T318", PRI_TIMER_T318, PRI_ALL_SWITCHES }, { "T319", PRI_TIMER_T319, PRI_ALL_SWITCHES }, @@ -184,6 +185,10 @@ static void pri_default_timers(struct pri *ctrl, int switchtype) ctrl->timers[PRI_TIMER_T309] = 6 * 1000; /* Time to wait before clearing calls in case of D-channel transient event. Q.931 specifies 6-90 seconds */ ctrl->timers[PRI_TIMER_T312] = (4 + 2) * 1000;/* Supervise broadcast SETUP message call reference retention. T303 + 2 seconds */ ctrl->timers[PRI_TIMER_T313] = 4 * 1000; /* Wait for CONNECT acknowledge, CPE side only */ +#if 0 /* Default disable the T316 timer otherwise the user cannot disable it. */ + ctrl->timers[PRI_TIMER_T316] = 2 * 60 * 1000; /* RESTART retransmit timer */ +#endif + ctrl->timers[PRI_TIMER_N316] = 2; /* Send RESTART this many times before giving up. */ ctrl->timers[PRI_TIMER_TM20] = 2500; /* Max time awaiting XID response - Q.921 Appendix IV */ ctrl->timers[PRI_TIMER_NM20] = 3; /* Number of XID retransmits - Q.921 Appendix IV */ @@ -1795,7 +1800,8 @@ char *pri_dump_info_str(struct pri *ctrl) enum PRI_TIMERS_AND_COUNTERS tmr; tmr = pri_timer[idx].number; - if (0 <= ctrl->timers[tmr]) { + if (0 <= ctrl->timers[tmr] + || tmr == PRI_TIMER_T316) { used = pri_snprintf(buf, used, buf_size, " %s: %d\n", pri_timer[idx].name, ctrl->timers[tmr]); } diff --git a/pri_internal.h b/pri_internal.h index c9a89ff..8ac0bac 100644 --- a/pri_internal.h +++ b/pri_internal.h @@ -652,6 +652,7 @@ struct q931_call { unsigned int slotmap_size:1;/* TRUE if the slotmap is E1 (32 bits). */ + /*! Control the RESTART reception to the upper layer. */ struct { /*! Timer ID of RESTART notification events to upper layer. */ int timer; @@ -662,6 +663,15 @@ struct q931_call { /*! Channel ID list */ char chan_no[32]; } restart; + /*! Control the RESTART retransmissions. */ + struct { + /*! T316 RESTART retransmit timer. */ + int t316_timer; + /*! Number of times remaining that RESTART can be transmitted. */ + int remain; + /*! Encoded RESTART channel id. */ + int channel; + } restart_tx; }; enum CC_STATES { diff --git a/q931.c b/q931.c index d636267..8bd2351 100644 --- a/q931.c +++ b/q931.c @@ -4422,6 +4422,7 @@ static void cleanup_and_free_call(struct q931_call *cur) ctrl = cur->pri; pri_schedule_del(ctrl, cur->restart.timer); + pri_schedule_del(ctrl, cur->restart_tx.t316_timer); pri_schedule_del(ctrl, cur->retranstimer); pri_schedule_del(ctrl, cur->hold_timer); pri_schedule_del(ctrl, cur->fake_clearing_timer); @@ -5913,6 +5914,81 @@ int q931_release(struct pri *ctrl, q931_call *c, int cause) static int restart_ies[] = { Q931_CHANNEL_IDENT, Q931_RESTART_INDICATOR, -1 }; +static void t316_expire(void *vcall); + +/*! + * \internal + * \brief Send the RESTART message to the peer. + * + * \param call Q.931 call leg + * \param channel Encoded channel id to use. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int q931_send_restart(struct q931_call *call) +{ + struct pri *ctrl = call->pri; + int channel = call->restart_tx.channel; + + /* Start timer T316 if enabled. */ + if (0 < ctrl->timers[PRI_TIMER_T316]) { + call->restart_tx.t316_timer = + pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T316], t316_expire, call); + --call->restart_tx.remain; + } + + call->ri = 0; + call->ds1no = (channel >> 8) & 0xFF; + call->ds1explicit = (channel >> 16) & 0x1; + call->channelno = channel & 0xFF; + call->chanflags &= ~FLAG_PREFERRED; + call->chanflags |= FLAG_EXCLUSIVE; + UPDATE_OURCALLSTATE(ctrl, call, Q931_CALL_STATE_RESTART); + call->peercallstate = Q931_CALL_STATE_RESTART_REQUEST; + return send_message(ctrl, call, Q931_RESTART, restart_ies); +} + +/*! + * \internal + * \brief T316 expired. + * + * \param vcall Q.931 call leg + * + * \return Nothing + */ +static void t316_expire(void *vcall) +{ + struct q931_call *call = vcall; + + call->restart_tx.t316_timer = 0; + + if (call->restart_tx.remain) { + /* Retransmit the RESTART */ + q931_send_restart(call); + } else { + int channel = call->restart_tx.channel; + + pri_message(call->pri, + "!! Peer failed to ack our RESTART request for ds1/channel:%d/%d.\n", + (channel >> 8) & 0xFF, channel & 0xFF); + } +} + +/*! + * \internal + * \brief Stop timer T316. + * + * \param call Q.931 call leg + * + * \return Nothing + */ +static void stop_t316(struct q931_call *call) +{ + pri_schedule_del(call->pri, call->restart_tx.t316_timer); + call->restart_tx.t316_timer = 0; +} + /*! * \brief Send the RESTART message to the peer. * @@ -5930,9 +6006,6 @@ static int restart_ies[] = { Q931_CHANNEL_IDENT, Q931_RESTART_INDICATOR, -1 }; * there might not be anything connected. The broadcast could * be handled in a similar manner to the broadcast SETUP. * - * \todo Need to implement T316 to protect against missing - * RESTART_ACKNOWLEDGE and STATUS messages. - * * \todo NT PTMP mode should implement some protection from * receiving a RESTART on channels in use by another TEI. * @@ -5941,22 +6014,21 @@ static int restart_ies[] = { Q931_CHANNEL_IDENT, Q931_RESTART_INDICATOR, -1 }; */ int q931_restart(struct pri *ctrl, int channel) { - struct q931_call *c; + struct q931_call *call; - c = q931_getcall(&ctrl->link, 0 | Q931_CALL_REFERENCE_FLAG); - if (!c) + if (!channel) { return -1; - if (!channel) + } + call = q931_getcall(&ctrl->link, 0 | Q931_CALL_REFERENCE_FLAG); + if (!call) { return -1; - c->ri = 0; - c->ds1no = (channel & 0xff00) >> 8; - c->ds1explicit = (channel & 0x10000) >> 16; - c->channelno = channel & 0xff; - c->chanflags &= ~FLAG_PREFERRED; - c->chanflags |= FLAG_EXCLUSIVE; - UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_RESTART); - c->peercallstate = Q931_CALL_STATE_RESTART_REQUEST; - return send_message(ctrl, c, Q931_RESTART, restart_ies); + } + + stop_t316(call); + call->restart_tx.remain = (0 < ctrl->timers[PRI_TIMER_N316]) + ? ctrl->timers[PRI_TIMER_N316] : 1; + call->restart_tx.channel = channel; + return q931_send_restart(call); } static int disconnect_ies[] = { Q931_CAUSE, Q931_IE_FACILITY, Q931_IE_USER_USER, -1 }; @@ -9007,6 +9079,16 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct c->peercallstate = Q931_CALL_STATE_NULL; ctrl->ev.e = PRI_EVENT_RESTART_ACK; ctrl->ev.restartack.channel = q931_encode_channel(c); + if (c->restart_tx.t316_timer + /* + * Since the DS1 value can vary, only check the channel number. + * We're only supposed to have one RESTART request outstanding + * at a time anyway. + */ + && (c->restart_tx.channel & 0xFF) == (ctrl->ev.restartack.channel & 0xFF)) { + /* This is the RESTART ACKNOWLEDGE we are expecting. */ + stop_t316(c); + } return Q931_RES_HAVEEVENT; case Q931_INFORMATION: /* XXX We're handling only INFORMATION messages that contain