From 1013e7626d75e69c376c58bb5b30a64465e77644 Mon Sep 17 00:00:00 2001 From: Richard Mudgett Date: Tue, 1 Mar 2011 00:50:04 +0000 Subject: [PATCH] Implement the mandatory T312 timer for NT PTMP broadcast SETUP calls. * Fixed stopping T303 too early on a NT PTMP broadcast SETUP call if a subcall just receives a RELEASE_COMPLETE(busy). * Fixed a valgrind reported invalid read/write when hanging up a NT PTMP broadcast SETUP call. JIRA LIBPRI-32 JIRA SWP-2548 git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@2238 2fbb986a-6c06-0410-b554-c9c1f0a7f128 --- libpri.h | 2 + pri.c | 2 + pri_internal.h | 5 +- pri_q931.h | 2 + q931.c | 605 ++++++++++++++++++++++++++++++++----------------- 5 files changed, 408 insertions(+), 208 deletions(-) diff --git a/libpri.h b/libpri.h index 3361ff6..3ee78b8 100644 --- a/libpri.h +++ b/libpri.h @@ -2153,6 +2153,8 @@ enum PRI_TIMERS_AND_COUNTERS { PRI_TIMER_QSIG_CC_T3, /*!< Max time to wait for user A to respond to user B availability. */ PRI_TIMER_QSIG_CC_T4, /*!< Path reservation supervision timeout. */ + PRI_TIMER_T312, /*!< Supervise broadcast SETUP message call reference retention. */ + /* Must be last in the enum list */ PRI_MAX_TIMERS }; diff --git a/pri.c b/pri.c index f866088..f137da7 100644 --- a/pri.c +++ b/pri.c @@ -77,6 +77,7 @@ static const struct pri_timer_table pri_timer[] = { { "T308", PRI_TIMER_T308, PRI_ALL_SWITCHES }, { "T309", PRI_TIMER_T309, PRI_ALL_SWITCHES }, { "T310", PRI_TIMER_T310, PRI_ALL_SWITCHES }, + { "T312", PRI_TIMER_T312, PRI_ALL_SWITCHES }, { "T313", PRI_TIMER_T313, PRI_ALL_SWITCHES }, { "T314", PRI_TIMER_T314, PRI_ALL_SWITCHES }, { "T316", PRI_TIMER_T316, PRI_ALL_SWITCHES }, @@ -181,6 +182,7 @@ static void pri_default_timers(struct pri *ctrl, int switchtype) ctrl->timers[PRI_TIMER_T305] = 30 * 1000; /* Wait for DISCONNECT acknowledge */ ctrl->timers[PRI_TIMER_T308] = 4 * 1000; /* Wait for RELEASE acknowledge */ 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 */ ctrl->timers[PRI_TIMER_TM20] = 2500; /* Max time awaiting XID response - Q.921 Appendix IV */ diff --git a/pri_internal.h b/pri_internal.h index b4b61b2..615c000 100644 --- a/pri_internal.h +++ b/pri_internal.h @@ -578,11 +578,14 @@ struct q931_call { 0,2-7 - Reserved for future use */ int t303_timer; int t303_expirycnt; + int t312_timer; + int fake_clearing_timer; int hangupinitiated; /*! \brief TRUE if we broadcast this call's SETUP message. */ int outboundbroadcast; - int performing_fake_clearing; + /*! TRUE if the master call is processing a hangup. Don't destroy it now. */ + int master_hanging_up; /*! * \brief Master call controlling this call. * \note Always valid. Master and normal calls point to self. diff --git a/pri_q931.h b/pri_q931.h index 7704407..3a14b41 100644 --- a/pri_q931.h +++ b/pri_q931.h @@ -443,6 +443,8 @@ enum Q931_RANKED_CALL_STATE { Q931_RANKED_CALL_STATE_CONNECT, /*! Call is in some non-call establishment state (likely disconnecting). */ Q931_RANKED_CALL_STATE_OTHER, + /*! Master call is aborting. */ + Q931_RANKED_CALL_STATE_ABORT, }; /* EuroISDN */ diff --git a/q931.c b/q931.c index cb0cbba..062d618 100644 --- a/q931.c +++ b/q931.c @@ -4184,10 +4184,23 @@ struct q931_call *q931_new_call(struct pri *ctrl) static void stop_t303(struct q931_call *call); +static void stop_t312(struct q931_call *call) +{ + /* T312 should only be running on the master call */ + pri_schedule_del(call->pri, call->t312_timer); + call->t312_timer = 0; +} + static void cleanup_and_free_call(struct q931_call *cur) { + struct pri *ctrl; + + ctrl = cur->pri; + pri_schedule_del(ctrl, cur->retranstimer); + pri_schedule_del(ctrl, cur->hold_timer); + pri_schedule_del(ctrl, cur->fake_clearing_timer); stop_t303(cur); - pri_schedule_del(cur->pri, cur->retranstimer); + stop_t312(cur); pri_call_apdu_queue_cleanup(cur); if (cur->cc.record) { /* Unlink CC associations. */ @@ -4215,7 +4228,118 @@ int q931_get_subcall_count(struct q931_call *master) return count; } -static void pri_create_fake_clearing(struct q931_call *c, struct pri *master); +static int pri_internal_clear(struct q931_call *call); + +/*! + * \brief Fake RELEASE for NT-PTMP initiated SETUPs w/o response + * + * \param param call Call is not a subcall call record. + */ +static void pri_fake_clearing(struct q931_call *call) +{ + struct pri *ctrl; + + ctrl = call->pri; + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "Fake clearing. cref:%d\n", call->cr); + } + + /* + * This does not need to be running since this is what we are + * doing right now anyway. + */ + pri_schedule_del(ctrl, call->fake_clearing_timer); + call->fake_clearing_timer = 0; + + if (call->cause == -1) { + /* Ensure that there is a resonable cause code. */ + call->cause = PRI_CAUSE_NO_USER_RESPONSE; + } + if (pri_internal_clear(call) == Q931_RES_HAVEEVENT) { + ctrl->schedev = 1; + } +} + +static void pri_fake_clearing_expiry(void *data) +{ + struct q931_call *master = data; + + master->fake_clearing_timer = 0; + pri_fake_clearing(master); +} + +static void pri_create_fake_clearing(struct pri *ctrl, struct q931_call *master) +{ + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "Fake clearing requested. cref:%d\n", master->cr); + } + pri_schedule_del(ctrl, master->fake_clearing_timer); + master->fake_clearing_timer = pri_schedule_event(ctrl, 0, pri_fake_clearing_expiry, + master); +} + +static void t312_expiry(void *data) +{ + struct q931_call *master = data; + struct pri *ctrl; + + ctrl = master->pri; + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "T312 timed out. cref:%d\n", master->cr); + } + + master->t312_timer = 0; + if (!q931_get_subcall_count(master)) { + /* No subcalls remain. */ + switch (master->ourcallstate) { + case Q931_CALL_STATE_CALL_ABORT: + /* We can destroy the master. */ + q931_destroycall(ctrl, master); + break; + default: + /* Let the upper layer know about the lack of call prospects. */ + UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_CALL_ABORT); + pri_fake_clearing(master); + break; + } + } +} + +/*! \param master Master call record for PTMP NT call. */ +static void start_t312(struct q931_call *master) +{ + struct pri *ctrl; + + ctrl = master->pri; + pri_schedule_del(ctrl, master->t312_timer); + master->t312_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T312], + t312_expiry, master); +} + +/*! + * \internal + * \brief Helper function to destroy a subcall. + * + * \param master Q.931 master call of subcall to destroy. + * \param idx Master subcall index to destroy. + * + * \return Nothing + */ +static void q931_destroy_subcall(struct q931_call *master, int idx) +{ + struct pri *ctrl = master->pri; + + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "Destroying subcall %p of call %p at %d\n", + master->subcalls[idx], master, idx); + } + cleanup_and_free_call(master->subcalls[idx]); + if (master->pri_winner == idx) { + /* This was the winning subcall. */ + master->pri_winner = -1; + } + master->subcalls[idx] = NULL; +} void q931_destroycall(struct pri *ctrl, q931_call *c) { @@ -4224,7 +4348,6 @@ void q931_destroycall(struct pri *ctrl, q931_call *c) struct q931_call *slave; int i; int slavesleft; - int slaveidx; if (q931_is_dummy_call(c)) { /* Cannot destroy the dummy call. */ @@ -4241,93 +4364,68 @@ void q931_destroycall(struct pri *ctrl, q931_call *c) cur = *ctrl->callpool; while (cur) { if (cur == c) { - slaveidx = -1; if (slave) { /* Destroying a slave. */ for (i = 0; i < ARRAY_LEN(cur->subcalls); ++i) { if (cur->subcalls[i] == slave) { - if (ctrl->debug & PRI_DEBUG_Q931_STATE) { - pri_message(ctrl, "Destroying subcall %p of call %p at %d\n", - slave, cur, i); - } - cleanup_and_free_call(slave); - cur->subcalls[i] = NULL; - slaveidx = i; + q931_destroy_subcall(cur, i); break; } } - } - slavesleft = 0; - for (i = 0; i < ARRAY_LEN(cur->subcalls); ++i) { - if (cur->subcalls[i]) { - if (ctrl->debug & PRI_DEBUG_Q931_STATE) { - pri_message(ctrl, "Subcall still present at %d\n", i); + /* How many slaves are left? */ + slavesleft = 0; + for (i = 0; i < ARRAY_LEN(cur->subcalls); ++i) { + if (cur->subcalls[i]) { + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "Subcall still present at %d\n", i); + } + ++slavesleft; + } + } + + if (slavesleft || cur->t312_timer || cur->master_hanging_up) { + return; + } + + /* No slaves left. */ + switch (cur->ourcallstate) { + case Q931_CALL_STATE_CALL_ABORT: + break; + default: + /* Let the upper layer know about the call clearing. */ + UPDATE_OURCALLSTATE(ctrl, cur, Q931_CALL_STATE_CALL_ABORT); + pri_create_fake_clearing(ctrl, cur); + return; + } + + /* We can try to destroy the master now. */ + } else { + /* Destroy any slaves that may be present as well. */ + slavesleft = 0; + for (i = 0; i < ARRAY_LEN(cur->subcalls); ++i) { + if (cur->subcalls[i]) { + ++slavesleft; + q931_destroy_subcall(cur, i); } - slavesleft++; } } - /* We have 3 different phases to deal with: - * 1.) Sent outbound call, but no response (no subcalls present) - * 2.) Sent outbound call, with responses (subcalls present) - * 3.) Outbound call connected, indicated by pri_winner > -1 - * - * If chan_dahdi hangs up in phase: - * 1.) T303 will be present, and we will fake clear in this case - * 2.) pri_winner will be < 0 and subcalls will be present. - * 3.) pri_winner will be > -1 and we will free the master when the winner dies. - * - * If remote ends hang up in phase: - * 1.) Impossible, defined by phase. - * 2.) When last end hangs up, we should cause a fake clearing. - * 3.) Pass events to winner up and be freed when winner is freed - * - * Exceptional conditions in phase: - * 1.) None. - * 2.) None. - * 3.) We hang up a call so quickly that it hangs up before other competing lines finish hangup sequence - * Subcalls present still even though we have hung up the winner. - * - * So, we could say: - * If, when the library user hangs up the master call, and there are more than one subcall up, we fake clear - * regardless of whether or not we drop down to one subcall left in the clearing process. - * - * If there is only one call up, we mirror what it does. - * - * OR - * - * Phase 2. them clearing: - * For handling of Phase 2 (indicated by not running and pri_winner not present): - * We create a fake hangup sequence after all the subcalls have been destroyed and after - * - * "" us clearing: - * For we need to start the fake clearing, but it needs to be half of a fake clearing, not a full one (since we already had a hangup). - * - * For handling of Phase 3 plus exceptions: - * - * If pri_winner exists, we mirror him in terms of events (which provides our hangup sequence), and when we have the complete - * hangup sequence completed (destroy called on master call), if there still exist non winner subcalls at this time, we declare the master - * call as dead and free it when the last subcall clears. - */ - - if (slave && !slavesleft /* i.e., The last slave was just destroyed */ - && (cur->pri_winner < 0 || slaveidx != cur->pri_winner)) { - pri_create_fake_clearing(cur, ctrl); + if (cur->fake_clearing_timer) { + /* + * Must wait for the fake clearing to complete before destroying + * the master call record. + */ return; } - if (slavesleft) { - return; + /* This is likely not good. */ + pri_error(ctrl, + "Destroyed %d subcalls unconditionally with the master. cref:%d\n", + slavesleft, cur->cr); } /* Master call or normal call destruction. */ - if ((cur->pri_winner > -1) && cur->outboundbroadcast) { - if (ctrl->debug & PRI_DEBUG_Q931_STATE) { - pri_message(ctrl, - "Since we already had a winner, we should just be able to kill the call anyways\n"); - } - } if (prev) prev->next = cur->next; else @@ -4339,7 +4437,6 @@ void q931_destroycall(struct pri *ctrl, q931_call *c) q931_call_state_str(cur->ourcallstate), q931_call_state_str(cur->peercallstate), q931_hold_state_str(cur->hold_state)); - pri_schedule_del(ctrl, cur->hold_timer); cleanup_and_free_call(cur); return; } @@ -5689,36 +5786,45 @@ static int cis_setup_ies[] = { static void stop_t303(struct q931_call *call) { /* T303 should only be running on the master call */ - pri_schedule_del(call->master_call->pri, call->master_call->t303_timer); - call->master_call->t303_timer = 0; + pri_schedule_del(call->pri, call->t303_timer); + call->t303_timer = 0; } static void t303_expiry(void *data); +/*! \param call Call is not a subcall call record. */ static void start_t303(struct q931_call *call) { - if (call->t303_timer) { - pri_error(call->pri, "Should not have T303 set when starting again. Stopping first\n"); - stop_t303(call); - } + struct pri *ctrl; - //pri_error(call->pri, "T303 should be %d\n", call->pri->timers[PRI_TIMER_T303]); - call->t303_timer = pri_schedule_event(call->pri, call->pri->timers[PRI_TIMER_T303], t303_expiry, call); + ctrl = call->pri; + pri_schedule_del(ctrl, call->t303_timer); + call->t303_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T303], t303_expiry, + call); } -static void pri_fake_clearing(void *data); - static void t303_expiry(void *data) { - struct q931_call *c = data; + struct q931_call *c = data;/* Call is not a subcall call record. */ struct pri *ctrl = c->pri; int res; + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "T303 timed out. cref:%d\n", c->cr); + } + c->t303_expirycnt++; c->t303_timer = 0; if (c->cause != -1) { /* We got a DISCONNECT, RELEASE, or RELEASE_COMPLETE and no other responses. */ + if (c->outboundbroadcast) { + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_ABORT); + } else { + /* This should never happen. T303 should not be running in this case. */ + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); + c->peercallstate = Q931_CALL_STATE_NULL; + } pri_fake_clearing(c); } else if (c->t303_expirycnt < 2) { /*! @@ -5745,8 +5851,22 @@ static void t303_expiry(void *data) pri_error(ctrl, "Error resending setup message!\n"); } start_t303(c); + if (c->outboundbroadcast) { + start_t312(c); + } } else { + /* + * We never got any response for a normal call or an + * establishment response from any TEI for a master/subcall + * call. + */ c->cause = PRI_CAUSE_NO_USER_RESPONSE; + if (c->outboundbroadcast) { + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_ABORT); + } else { + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); + c->peercallstate = Q931_CALL_STATE_NULL; + } pri_fake_clearing(c); } } @@ -5859,6 +5979,9 @@ int q931_setup(struct pri *ctrl, q931_call *c, struct pri_sr *req) c->peercallstate = Q931_CALL_STATE_CALL_PRESENT; c->t303_expirycnt = 0; start_t303(c); + if (c->outboundbroadcast) { + start_t312(c); + } } return res; } @@ -6400,37 +6523,10 @@ int q931_send_retrieve_rej(struct pri *ctrl, struct q931_call *call, int cause) return q931_send_retrieve_rej_msg(ctrl, winner, cause); } -static int pri_internal_clear(struct q931_call *call); - -/* Fake RELEASE for NT-PTMP initiated SETUPs w/o response */ -static void pri_fake_clearing(void *data) -{ - 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; - } -} - -static void pri_create_fake_clearing(struct q931_call *c, struct pri *master) -{ - pri_schedule_del(master, c->retranstimer); - c->retranstimer = pri_schedule_event(master, 0, pri_fake_clearing, c); -} - static int __q931_hangup(struct pri *ctrl, q931_call *c, int cause) { int disconnect = 1; int release_compl = 0; - int t303_was_running = c->master_call->t303_timer; if (!ctrl || !c) { return -1; @@ -6517,17 +6613,6 @@ static int __q931_hangup(struct pri *ctrl, q931_call *c, int cause) q931_release_complete(ctrl,c,cause); break; case Q931_CALL_STATE_CALL_INITIATED: - if (c->outboundbroadcast && c->master_call == c && t303_was_running) { - //c->fakeclearing = 1; - //c->alive = 0; - /* We need to fake a received clearing sequence in this case... */ - if (ctrl->debug & PRI_DEBUG_Q931_STATE) { - pri_message(ctrl, "Faking clearing\n"); - } - pri_create_fake_clearing(c, ctrl); - /* This means that we never got a response from a TEI */ - return 0; - } /* we sent SETUP */ case Q931_CALL_STATE_OVERLAP_SENDING: /* received SETUP_ACKNOWLEDGE */ @@ -6580,6 +6665,9 @@ static int __q931_hangup(struct pri *ctrl, q931_call *c, int cause) /* sent DISCONNECT */ q931_release(ctrl,c,cause); break; + case Q931_CALL_STATE_CALL_ABORT: + /* Don't do anything, waiting for T312 to expire. */ + break; case Q931_CALL_STATE_DISCONNECT_INDICATION: /* received DISCONNECT */ if (c->peercallstate == Q931_CALL_STATE_DISCONNECT_REQUEST) { @@ -6615,7 +6703,7 @@ static int __q931_hangup(struct pri *ctrl, q931_call *c, int cause) return 0; } -static void initiate_hangup_if_needed(struct pri *ctrl, struct q931_call *subcall, int cause); +static void initiate_hangup_if_needed(struct q931_call *master, int idx, int cause); int q931_hangup(struct pri *ctrl, q931_call *call, int cause) { @@ -6623,42 +6711,72 @@ int q931_hangup(struct pri *ctrl, q931_call *call, int cause) if (call->master_call->outboundbroadcast) { if (call->master_call == call) { - int slaves = 0; + int slaves; - /* Master is called with hangup - initiate hangup with slaves */ + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, DBGHEAD "Hangup master cref:%d\n", DBGINFO, call->cr); + } + + UPDATE_OURCALLSTATE(ctrl, call, Q931_CALL_STATE_CALL_ABORT); + if (call->pri_winner < 0 && call->alive) { + /* + * Fake clearing if we have no winner to get rid of the upper + * layer. + */ + pri_create_fake_clearing(ctrl, call); + } else if (call->fake_clearing_timer) { + /* + * No need for fake clearing to be running anymore. + * Will this actually happen? + */ + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "Fake clearing request cancelled. cref:%d\n", + call->cr); + } + pri_schedule_del(ctrl, call->fake_clearing_timer); + call->fake_clearing_timer = 0; + } + + /* Initiate hangup of slaves */ + call->master_hanging_up = 1; for (i = 0; i < ARRAY_LEN(call->subcalls); ++i) { if (call->subcalls[i]) { - slaves++; - if (i == call->master_call->pri_winner) { - __q931_hangup(call->subcalls[i]->pri, call->subcalls[i], cause); - } else { - initiate_hangup_if_needed(ctrl, call->subcalls[i], cause); - } if (ctrl->debug & PRI_DEBUG_Q931_STATE) { - pri_message(ctrl, "%s: Hanging up %d, winner %d\n", __FUNCTION__, - i, call->master_call->pri_winner); + pri_message(ctrl, DBGHEAD "Hanging up %d, winner:%d subcall:%p\n", + DBGINFO, i, call->pri_winner, call->subcalls[i]); + } + if (i == call->pri_winner) { + q931_hangup(ctrl, call->subcalls[i], cause); + } else { + initiate_hangup_if_needed(call, i, cause); } } } + call->master_hanging_up = 0; - call->hangupinitiated = 1; - - if ((!slaves && (call->master_call->pri_winner < 0)) || (call->performing_fake_clearing)) { - __q931_hangup(ctrl, call, cause); - } + slaves = q931_get_subcall_count(call); if (ctrl->debug & PRI_DEBUG_Q931_STATE) { - pri_message(ctrl, "%s: Slaves %d\n", __FUNCTION__, slaves); + pri_message(ctrl, DBGHEAD "Remaining slaves %d\n", DBGINFO, slaves); + } + + stop_t303(call); + if (!call->t312_timer && !slaves) { + /* + * T312 has expired and no slaves are left so we can safely + * destroy the master. + */ + q931_destroycall(ctrl, call); } return 0; } else { if (ctrl->debug & PRI_DEBUG_Q931_STATE) { - pri_message(ctrl, "%s: Slave hangup\n", __FUNCTION__); + pri_message(ctrl, DBGHEAD "Hangup slave cref:%d\n", DBGINFO, call->cr); } return __q931_hangup(ctrl, call, cause); } } else { if (ctrl->debug & PRI_DEBUG_Q931_STATE) { - pri_message(ctrl, "%s: other hangup\n", __FUNCTION__); + pri_message(ctrl, DBGHEAD "Hangup other cref:%d\n", DBGINFO, call->cr); } return __q931_hangup(ctrl, call, cause); } @@ -6868,28 +6986,19 @@ static struct q931_call *q931_get_subcall_winner(struct q931_call *master) } } -static void initiate_hangup_if_needed(struct pri *ctrl, struct q931_call *subcall, int cause) +static void initiate_hangup_if_needed(struct q931_call *master, int idx, int cause) { + struct pri *ctrl; + struct q931_call *subcall; + + ctrl = master->pri; + subcall = master->subcalls[idx]; + if (!subcall->hangupinitiated) { q931_hangup(ctrl, subcall, cause); - subcall->alive = 0; - } else { - switch (subcall->ourcallstate) { - case Q931_CALL_STATE_NULL: - switch (subcall->peercallstate) { - case Q931_CALL_STATE_NULL: - /* - * Complete the hangup of the dead subcall. Noone else will at - * this point. - */ - q931_hangup(ctrl, subcall, cause); - break; - default: - break; - } - break; - default: - break; + if (master->subcalls[idx] == subcall) { + /* The subcall was not destroyed. */ + subcall->alive = 0; } } } @@ -6914,8 +7023,7 @@ static void q931_set_subcall_winner(struct q931_call *subcall) /* Start tear down of calls that were not chosen */ for (i = 0; i < ARRAY_LEN(master->subcalls); ++i) { if (master->subcalls[i] && master->subcalls[i] != subcall) { - initiate_hangup_if_needed(master->pri, master->subcalls[i], - PRI_CAUSE_NONSELECTED_USER_CLEARING); + initiate_hangup_if_needed(master, i, PRI_CAUSE_NONSELECTED_USER_CLEARING); } } } @@ -6962,9 +7070,17 @@ static struct q931_call *q931_get_subcall(struct q921_link *link, struct q931_ca cur->subcalls[i] = NULL; } cur->t303_timer = 0;/* T303 should only be on on the master call */ + cur->t312_timer = 0;/* T312 should only be on on the master call */ + cur->fake_clearing_timer = 0;/* Fake clearing should only be on on the master call */ cur->hold_timer = 0; cur->retranstimer = 0; + /* + * Mark this subcall as a newcall until it is determined if the + * subcall can compete for the call. + */ + cur->newcall = 1; + /* Assume we sent a SETUP and this is the first response to it from this peer. */ cur->ourcallstate = Q931_CALL_STATE_CALL_INITIATED; cur->peercallstate = Q931_CALL_STATE_CALL_PRESENT; @@ -7065,12 +7181,7 @@ int q931_receive(struct q921_link *link, q931_h *h, int len) case Q931_PROTOCOL_DISCRIMINATOR: if (prepare_to_handle_q931_message(ctrl, mh, c)) { /* Discard message. We don't know how to handle it. */ - if (!c->master_call->outboundbroadcast && c->newcall) { - /* - * Destroy new non-subcalls immediately. Let the normal - * disconnect/destruction of subcalls happen when there is a - * winner. - */ + if (c->newcall) { pri_destroycall(ctrl, c); } return 0; @@ -7317,6 +7428,9 @@ static enum Q931_RANKED_CALL_STATE q931_rank_state(enum Q931_CALL_STATE state) case Q931_CALL_STATE_CALL_INDEPENDENT_SERVICE: rank = Q931_RANKED_CALL_STATE_CONNECT; break; + case Q931_CALL_STATE_CALL_ABORT: + rank = Q931_RANKED_CALL_STATE_ABORT; + break; default: rank = Q931_RANKED_CALL_STATE_OTHER; break; @@ -7446,7 +7560,6 @@ static void nt_ptmp_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, st struct q931_call *winner = q931_get_subcall_winner(master); enum Q931_RANKED_CALL_STATE master_rank; enum Q931_RANKED_CALL_STATE subcall_rank; - enum Q931_CALL_STATE newstate; /* For broadcast calls, we default to not allowing events to keep events received to a minimum * and to allow post processing, since that is where hangup and subcall state handling and other processing is done */ @@ -7454,6 +7567,10 @@ static void nt_ptmp_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, st *allow_posthandle = 1; master_rank = q931_rank_state(master->ourcallstate); + if (master_rank < Q931_RANKED_CALL_STATE_CONNECT) { + /* This subcall can compete for the call. */ + subcall->newcall = 0; + } switch (mh->msg) { case Q931_SETUP_ACKNOWLEDGE: @@ -7496,22 +7613,53 @@ static void nt_ptmp_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, st } break; case Q931_DISCONNECT: - newstate = Q931_CALL_STATE_DISCONNECT_INDICATION; - goto process_hangup; case Q931_RELEASE: case Q931_RELEASE_COMPLETE: - newstate = Q931_CALL_STATE_NULL; -process_hangup: if (!winner) { - /* If there's not a winner, we just take the cause and pass it up to the - * master_call */ - master->cause = subcall->cause; + int master_priority; + int slave_priority; + + /* Pass up the cause on a priority basis. */ + switch (master->cause) { + case PRI_CAUSE_USER_BUSY: + master_priority = 2; + break; + case PRI_CAUSE_CALL_REJECTED: + master_priority = 1; + break; + default: + master_priority = 0; + break; + case -1: + /* First time priority. */ + master_priority = -2; + break; + } + switch (subcall->cause) { + case PRI_CAUSE_USER_BUSY: + slave_priority = 2; + break; + case PRI_CAUSE_CALL_REJECTED: + slave_priority = 1; + break; + default: + slave_priority = 0; + break; + case PRI_CAUSE_INCOMPATIBLE_DESTINATION: + /* Cause explicitly ignored */ + slave_priority = -1; + break; + } + if (master_priority < slave_priority) { + /* Pass up the cause to the master. */ + master->cause = subcall->cause; + } } else { /* There *is* a winner */ if (subcall == winner) { /* .. and we're it: */ *allow_event = 1; - UPDATE_OURCALLSTATE(ctrl, master, newstate); + UPDATE_OURCALLSTATE(ctrl, master, Q931_CALL_STATE_CALL_ABORT); } } break; @@ -7958,6 +8106,34 @@ static struct q931_call *q931_find_held_call(struct pri *ctrl, struct q931_call return match; } +/*! + * \internal + * \brief Determine RELEASE_COMPLETE cause code for newcall rejection. + * + * \param call Q.931 call leg. + * + * \return Cause code for RELEASE_COMPLETE. + */ +static int newcall_rel_comp_cause(struct q931_call *call) +{ + struct q931_call *master; + int cause; + + cause = PRI_CAUSE_INVALID_CALL_REFERENCE; + master = call->master_call; + if (master != call && master->t312_timer) { + switch (master->ourcallstate) { + case Q931_CALL_STATE_CALL_ABORT: + cause = PRI_CAUSE_RECOVERY_ON_TIMER_EXPIRE; + break; + default: + break; + } + } + + return cause; +} + /*! * \internal * \brief Process the decoded information in the Q.931 message. @@ -8077,9 +8253,9 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct return Q931_RES_HAVEEVENT; case Q931_ALERTING: q931_display_subcmd(ctrl, c); - stop_t303(c); + stop_t303(c->master_call); if (c->newcall) { - q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); + q931_release_complete(ctrl, c, newcall_rel_comp_cause(c)); break; } UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_CALL_DELIVERED); @@ -8113,9 +8289,9 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct return Q931_RES_HAVEEVENT; case Q931_CONNECT: q931_display_subcmd(ctrl, c); - stop_t303(c); + stop_t303(c->master_call); if (c->newcall) { - q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); + q931_release_complete(ctrl, c, newcall_rel_comp_cause(c)); break; } switch (c->ourcallstate) { @@ -8170,7 +8346,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct case Q931_FACILITY: q931_display_subcmd(ctrl, c); if (c->newcall) { - q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); + q931_release_complete(ctrl, c, newcall_rel_comp_cause(c)); break; } switch (c->incoming_ct_state) { @@ -8201,10 +8377,10 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct /* Fall through */ case Q931_CALL_PROCEEDING: q931_display_subcmd(ctrl, c); - stop_t303(c); + stop_t303(c->master_call); ctrl->ev.proceeding.subcmds = &ctrl->subcmds; if (c->newcall) { - q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); + q931_release_complete(ctrl, c, newcall_rel_comp_cause(c)); break; } if ((c->ourcallstate != Q931_CALL_STATE_CALL_INITIATED) && @@ -8235,7 +8411,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct case Q931_CONNECT_ACKNOWLEDGE: q931_display_subcmd(ctrl, c); if (c->newcall) { - q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); + q931_release_complete(ctrl, c, newcall_rel_comp_cause(c)); break; } switch (c->ourcallstate) { @@ -8307,6 +8483,12 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); c->peercallstate = Q931_CALL_STATE_NULL; + if (c->outboundbroadcast && (c != q931_get_subcall_winner(c->master_call))) { + /* Complete clearing the disconnecting non-winning subcall. */ + pri_hangup(ctrl, c, -1); + return 0; + } + /* Free resources */ if (c->alive) { ctrl->ev.e = PRI_EVENT_HANGUP; @@ -8342,6 +8524,12 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct pri_cc_event(ctrl, c, c->cc.record, CC_EVENT_SIGNALING_GONE); } + if (c->outboundbroadcast && (c != q931_get_subcall_winner(c->master_call))) { + /* Complete clearing the disconnecting non-winning subcall. */ + pri_hangup(ctrl, c, -1); + return 0; + } + /* Free resources */ if (c->alive) { ctrl->ev.e = PRI_EVENT_HANGUP; @@ -8374,7 +8562,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); if (c->newcall) { - q931_release_complete(ctrl, c, PRI_CAUSE_INVALID_CALL_REFERENCE); + q931_release_complete(ctrl, c, newcall_rel_comp_cause(c)); break; } @@ -8395,7 +8583,9 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct } if (c->outboundbroadcast && (c != q931_get_subcall_winner(c->master_call))) { - return pri_hangup(ctrl, c, -1); + /* Complete clearing the disconnecting non-winning subcall. */ + pri_hangup(ctrl, c, -1); + return 0; } return Q931_RES_HAVEEVENT; case Q931_DISCONNECT: @@ -8406,7 +8596,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct c->cause = PRI_CAUSE_MANDATORY_IE_MISSING; } if (c->newcall) { - q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); + q931_release_complete(ctrl, c, newcall_rel_comp_cause(c)); break; } @@ -8476,6 +8666,12 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct libpri_copy_string(ctrl->ev.hangup.useruserinfo, c->useruserinfo, sizeof(ctrl->ev.hangup.useruserinfo)); c->useruserinfo[0] = '\0'; + if (c->outboundbroadcast && (c != q931_get_subcall_winner(c->master_call))) { + /* Complete clearing the disconnecting non-winning subcall. */ + pri_hangup(ctrl, c, -1); + return 0; + } + if (c->alive) { switch (c->cause) { case PRI_CAUSE_USER_BUSY: @@ -8511,7 +8707,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct XXX */ q931_display_subcmd(ctrl, c); if (c->newcall) { - q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); + q931_release_complete(ctrl, c, newcall_rel_comp_cause(c)); break; } if (c->ourcallstate != Q931_CALL_STATE_OVERLAP_RECEIVING) { @@ -8541,15 +8737,15 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct case Q931_STATUS_ENQUIRY: q931_display_clear(c); if (c->newcall) { - q931_release_complete(ctrl, c, PRI_CAUSE_INVALID_CALL_REFERENCE); + q931_release_complete(ctrl, c, newcall_rel_comp_cause(c)); } else q931_status(ctrl,c, PRI_CAUSE_RESPONSE_TO_STATUS_ENQUIRY); break; case Q931_SETUP_ACKNOWLEDGE: q931_display_subcmd(ctrl, c); - stop_t303(c); + stop_t303(c->master_call); if (c->newcall) { - q931_release_complete(ctrl,c,PRI_CAUSE_INVALID_CALL_REFERENCE); + q931_release_complete(ctrl, c, newcall_rel_comp_cause(c)); break; } UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_OVERLAP_SENDING); @@ -8921,12 +9117,7 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct msg2str(mh->msg), mh->msg); q931_display_clear(c); q931_status(ctrl,c, PRI_CAUSE_MESSAGE_TYPE_NONEXIST); - if (!c->master_call->outboundbroadcast && c->newcall) { - /* - * Destroy new non-subcalls immediately. Let the normal - * disconnect/destruction of subcalls happen when there is a - * winner. - */ + if (c->newcall) { pri_destroycall(ctrl, c); } return -1; @@ -8949,18 +9140,10 @@ static int pri_internal_clear(struct q931_call *c) c->sugcallstate = Q931_CALL_STATE_NOT_SET; c->aoc_units = -1; - 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); @@ -9021,6 +9204,8 @@ static void pri_dl_down_timeout(void *data) c->retranstimer = 0; c->cause = PRI_CAUSE_DESTINATION_OUT_OF_ORDER; + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); + c->peercallstate = Q931_CALL_STATE_NULL; if (pri_internal_clear(c) == Q931_RES_HAVEEVENT) { ctrl->schedev = 1; } @@ -9038,6 +9223,8 @@ static void pri_dl_down_cancelcall(void *data) c->retranstimer = 0; c->cause = PRI_CAUSE_DESTINATION_OUT_OF_ORDER; + UPDATE_OURCALLSTATE(ctrl, c, Q931_CALL_STATE_NULL); + c->peercallstate = Q931_CALL_STATE_NULL; if (pri_internal_clear(c) == Q931_RES_HAVEEVENT) { ctrl->schedev = 1; } @@ -9090,6 +9277,7 @@ static const char *q931_dl_event2str(enum Q931_DL_EVENT event) void q931_dl_event(struct q921_link *link, enum Q931_DL_EVENT event) { struct q931_call *cur; + struct q931_call *cur_next; struct q931_call *call; struct pri *ctrl; int idx; @@ -9168,7 +9356,10 @@ void q931_dl_event(struct q921_link *link, enum Q931_DL_EVENT event) break; case Q931_DL_EVENT_DL_RELEASE_IND: case Q931_DL_EVENT_DL_RELEASE_CONFIRM: - for (cur = *ctrl->callpool; cur; cur = cur->next) { + for (cur = *ctrl->callpool; cur; cur = cur_next) { + /* The master call could get destroyed if the last subcall dies. */ + cur_next = cur->next; + if (!(cur->cr & ~Q931_CALL_REFERENCE_FLAG)) { /* Don't do anything on the global call reference call record. */ continue;