From 9abab10b664073c3ad759f02c5ade9d2f24c8260 Mon Sep 17 00:00:00 2001 From: Richard Mudgett Date: Tue, 11 Oct 2011 21:06:55 +0000 Subject: Add protection for SS7 channel allocation and better glare handling. * Added a CLI "ss7 show channels" command that might prove useful for future debugging. * Made the incoming SS7 channel event check and gripe message uniform. * Made sure that the DNID string for an incoming call is always initialized. (issue ASTERISK-17966) Reported by: Kenneth Van Velthoven Patches: jira_asterisk_17966_v1.8_glare.patch (license #5621) patch uploaded by rmudgett ........ Merged revisions 340365 from http://svn.asterisk.org/svn/asterisk/branches/1.8 ........ Merged revisions 340366 from http://svn.asterisk.org/svn/asterisk/branches/10 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@340367 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- channels/chan_dahdi.c | 32 +++++ channels/sig_ss7.c | 382 ++++++++++++++++++++++++++++++++++++-------------- channels/sig_ss7.h | 36 ++++- 3 files changed, 340 insertions(+), 110 deletions(-) (limited to 'channels') diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index 11a7bda8b..3cfdf0c79 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -16248,9 +16248,11 @@ static char *handle_ss7_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_a ast_cli(a->fd, "No SS7 running on linkset %d\n", span); } else { if (!strcasecmp(a->argv[3], "on")) { + linksets[span - 1].ss7.debug = 1; ss7_set_debug(linksets[span-1].ss7.ss7, SIG_SS7_DEBUG); ast_cli(a->fd, "Enabled debugging on linkset %d\n", span); } else { + linksets[span - 1].ss7.debug = 0; ss7_set_debug(linksets[span-1].ss7.ss7, 0); ast_cli(a->fd, "Disabled debugging on linkset %d\n", span); } @@ -16508,6 +16510,35 @@ static char *handle_ss7_show_linkset(struct ast_cli_entry *e, int cmd, struct as } #endif /* defined(HAVE_SS7) */ +#if defined(HAVE_SS7) +static char *handle_ss7_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + int linkset; + + switch (cmd) { + case CLI_INIT: + e->command = "ss7 show channels"; + e->usage = + "Usage: ss7 show channels\n" + " Displays SS7 channel information at a glance.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc != 3) + return CLI_SHOWUSAGE; + + sig_ss7_cli_show_channels_header(a->fd); + for (linkset = 0; linkset < NUM_SPANS; ++linkset) { + if (linksets[linkset].ss7.ss7) { + sig_ss7_cli_show_channels(a->fd, &linksets[linkset].ss7); + } + } + return CLI_SUCCESS; +} +#endif /* defined(HAVE_SS7) */ + #if defined(HAVE_SS7) static char *handle_ss7_version(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { @@ -16536,6 +16567,7 @@ static struct ast_cli_entry dahdi_ss7_cli[] = { AST_CLI_DEFINE(handle_ss7_block_linkset, "Blocks all CICs on a linkset"), AST_CLI_DEFINE(handle_ss7_unblock_linkset, "Unblocks all CICs on a linkset"), AST_CLI_DEFINE(handle_ss7_show_linkset, "Shows the status of a linkset"), + AST_CLI_DEFINE(handle_ss7_show_channels, "Displays SS7 channel information"), AST_CLI_DEFINE(handle_ss7_version, "Displays libss7 version"), }; #endif /* defined(HAVE_SS7) */ diff --git a/channels/sig_ss7.c b/channels/sig_ss7.c index f1bb75a93..616d6002a 100644 --- a/channels/sig_ss7.c +++ b/channels/sig_ss7.c @@ -37,12 +37,36 @@ #include "asterisk/pbx.h" #include "asterisk/causes.h" #include "asterisk/musiconhold.h" +#include "asterisk/cli.h" #include "asterisk/transcap.h" #include "sig_ss7.h" /* ------------------------------------------------------------------- */ +static const char *sig_ss7_call_level2str(enum sig_ss7_call_level level) +{ + switch (level) { + case SIG_SS7_CALL_LEVEL_IDLE: + return "Idle"; + case SIG_SS7_CALL_LEVEL_ALLOCATED: + return "Allocated"; + case SIG_SS7_CALL_LEVEL_CONTINUITY: + return "Continuity"; + case SIG_SS7_CALL_LEVEL_SETUP: + return "Setup"; + case SIG_SS7_CALL_LEVEL_PROCEEDING: + return "Proceeding"; + case SIG_SS7_CALL_LEVEL_ALERTING: + return "Alerting"; + case SIG_SS7_CALL_LEVEL_CONNECT: + return "Connect"; + case SIG_SS7_CALL_LEVEL_GLARE: + return "Glare"; + } + return "Unknown"; +} + #define SIG_SS7_DEADLOCK_AVOIDANCE(p) \ do { \ sig_ss7_unlock_private(p); \ @@ -247,7 +271,7 @@ static void sig_ss7_handle_link_exception(struct sig_ss7_linkset *linkset, int w * \brief Obtain the sig_ss7 owner channel lock if the owner exists. * \since 1.8 * - * \param ss7 sig_ss7 SS7 control structure. + * \param ss7 SS7 linkset control structure. * \param chanpos Channel position in the span. * * \note Assumes the ss7->lock is already obtained. @@ -278,7 +302,7 @@ static void sig_ss7_lock_owner(struct sig_ss7_linkset *ss7, int chanpos) * \brief Queue the given frame onto the owner channel. * \since 1.8 * - * \param ss7 sig_ss7 SS7 control structure. + * \param ss7 SS7 linkset control structure. * \param chanpos Channel position in the span. * \param frame Frame to queue onto the owner channel. * @@ -301,7 +325,7 @@ static void sig_ss7_queue_frame(struct sig_ss7_linkset *ss7, int chanpos, struct * \brief Queue a control frame of the specified subclass onto the owner channel. * \since 1.8 * - * \param ss7 sig_ss7 SS7 control structure. + * \param ss7 SS7 linkset control structure. * \param chanpos Channel position in the span. * \param subclass Control frame subclass to queue onto the owner channel. * @@ -323,6 +347,17 @@ static void sig_ss7_queue_control(struct sig_ss7_linkset *ss7, int chanpos, int sig_ss7_queue_frame(ss7, chanpos, &f); } +/*! + * \internal + * \brief Find the channel position by CIC/DPC. + * + * \param linkset SS7 linkset control structure. + * \param cic Circuit Identification Code + * \param dpc Destination Point Code + * + * \retval chanpos on success. + * \retval -1 on error. + */ static int ss7_find_cic(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc) { int i; @@ -336,6 +371,31 @@ static int ss7_find_cic(struct sig_ss7_linkset *linkset, int cic, unsigned int d return winner; } +/*! + * \internal + * \brief Find the channel position by CIC/DPC and gripe if not found. + * + * \param linkset SS7 linkset control structure. + * \param cic Circuit Identification Code + * \param dpc Destination Point Code + * \param msg_name Message type name that failed. + * + * \retval chanpos on success. + * \retval -1 on error. + */ +static int ss7_find_cic_gripe(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc, const char *msg_name) +{ + int chanpos; + + chanpos = ss7_find_cic(linkset, cic, dpc); + if (chanpos < 0) { + ast_log(LOG_WARNING, "Linkset %d: SS7 %s requested unconfigured CIC/DPC %d/%d.\n", + linkset->span, msg_name, cic, dpc); + return -1; + } + return chanpos; +} + static void ss7_handle_cqm(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc) { unsigned char status[32]; @@ -386,6 +446,7 @@ static inline void ss7_block_cics(struct sig_ss7_linkset *linkset, int startcic, { int i; + /* XXX the use of state here seems questionable about matching up with the linkset channels */ for (i = 0; i < linkset->numchans; i++) { if (linkset->pvts[i] && (linkset->pvts[i]->dpc == dpc && ((linkset->pvts[i]->cic >= startcic) && (linkset->pvts[i]->cic <= endcic)))) { if (state) { @@ -617,8 +678,6 @@ void *ss7_linkset(void *data) struct sig_ss7_chan *p; int chanpos; struct pollfd pollers[SIG_SS7_NUM_DCHANS]; - int cic; - unsigned int dpc; int nextms = 0; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); @@ -685,6 +744,11 @@ void *ss7_linkset(void *data) } while ((e = ss7_check_event(ss7))) { + if (linkset->debug) { + ast_verbose("Linkset %d: Processing event: %s\n", + linkset->span, ss7_event2str(e->e)); + } + switch (e->e) { case SS7_EVENT_UP: if (linkset->state != LINKSET_STATE_UP) { @@ -710,9 +774,8 @@ void *ss7_linkset(void *data) ast_log(LOG_WARNING, "MTP2 link down (SLC %d)\n", e->gen.data); break; case ISUP_EVENT_CPG: - chanpos = ss7_find_cic(linkset, e->cpg.cic, e->cpg.opc); + chanpos = ss7_find_cic_gripe(linkset, e->cpg.cic, e->cpg.opc, "CPG"); if (chanpos < 0) { - ast_log(LOG_WARNING, "CPG on unconfigured CIC %d\n", e->cpg.cic); break; } p = linkset->pvts[chanpos]; @@ -753,17 +816,15 @@ void *ss7_linkset(void *data) break; case ISUP_EVENT_RSC: ast_verbose("Resetting CIC %d\n", e->rsc.cic); - chanpos = ss7_find_cic(linkset, e->rsc.cic, e->rsc.opc); + chanpos = ss7_find_cic_gripe(linkset, e->rsc.cic, e->rsc.opc, "RSC"); if (chanpos < 0) { - ast_log(LOG_WARNING, "RSC on unconfigured CIC %d\n", e->rsc.cic); break; } p = linkset->pvts[chanpos]; sig_ss7_lock_private(p); sig_ss7_set_inservice(p, 1); sig_ss7_set_remotelyblocked(p, 0); - dpc = p->dpc; - isup_set_call_dpc(e->rsc.call, dpc); + isup_set_call_dpc(e->rsc.call, p->dpc); sig_ss7_lock_owner(linkset, chanpos); p->ss7call = NULL; if (p->owner) { @@ -775,9 +836,8 @@ void *ss7_linkset(void *data) break; case ISUP_EVENT_GRS: ast_debug(1, "Got Reset for CICs %d to %d: Acknowledging\n", e->grs.startcic, e->grs.endcic); - chanpos = ss7_find_cic(linkset, e->grs.startcic, e->grs.opc); + chanpos = ss7_find_cic_gripe(linkset, e->grs.startcic, e->grs.opc, "GRS"); if (chanpos < 0) { - ast_log(LOG_WARNING, "GRS on unconfigured CIC %d\n", e->grs.startcic); break; } p = linkset->pvts[chanpos]; @@ -796,29 +856,53 @@ void *ss7_linkset(void *data) break; case ISUP_EVENT_IAM: ast_debug(1, "Got IAM for CIC %d and called number %s, calling number %s\n", e->iam.cic, e->iam.called_party_num, e->iam.calling_party_num); - chanpos = ss7_find_cic(linkset, e->iam.cic, e->iam.opc); + chanpos = ss7_find_cic_gripe(linkset, e->iam.cic, e->iam.opc, "IAM"); if (chanpos < 0) { - ast_log(LOG_WARNING, "IAM on unconfigured CIC %d\n", e->iam.cic); isup_rel(ss7, e->iam.call, -1); break; } p = linkset->pvts[chanpos]; sig_ss7_lock_private(p); - if (p->owner) { - if (p->ss7call == e->iam.call) { - sig_ss7_unlock_private(p); - ast_log(LOG_WARNING, "Duplicate IAM requested on CIC %d\n", e->iam.cic); - break; - } else { - sig_ss7_unlock_private(p); - ast_log(LOG_WARNING, "Ring requested on CIC %d already in use!\n", e->iam.cic); - break; + sig_ss7_lock_owner(linkset, chanpos); + if (p->call_level != SIG_SS7_CALL_LEVEL_IDLE) { + /* + * Detected glare/dual-seizure + * + * Always abort both calls since we can't implement the dual + * seizure procedures due to our channel assignment architecture + * and the fact that we cannot tell libss7 to discard its call + * structure to ignore the incoming IAM. + */ + ast_debug(1, + "Linkset %d: SS7 IAM glare on CIC/DPC %d/%d. Dropping both calls.\n", + linkset->span, e->iam.cic, e->iam.opc); + if (p->call_level == SIG_SS7_CALL_LEVEL_ALLOCATED) { + /* + * We have not sent our IAM yet and we never will at this point. + */ + p->alreadyhungup = 1; + isup_rel(ss7, e->iam.call, -1); } + p->call_level = SIG_SS7_CALL_LEVEL_GLARE; + if (p->owner) { + p->owner->hangupcause = AST_CAUSE_NORMAL_CLEARING; + ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV); + ast_channel_unlock(p->owner); + } + sig_ss7_unlock_private(p); + break; } - - dpc = p->dpc; + /* + * The channel should not have an owner at this point since we + * are in the process of creating an owner for it. + */ + ast_assert(!p->owner); + + /* Mark channel as in use so no outgoing call will steal it. */ + p->call_level = SIG_SS7_CALL_LEVEL_ALLOCATED; p->ss7call = e->iam.call; - isup_set_call_dpc(p->ss7call, dpc); + + isup_set_call_dpc(p->ss7call, p->dpc); if ((p->use_callerid) && (!ast_strlen_zero(e->iam.calling_party_num))) { ss7_apply_plan_to_number(p->cid_num, sizeof(p->cid_num), linkset, e->iam.calling_party_num, e->iam.calling_nai); @@ -830,8 +914,10 @@ void *ss7_linkset(void *data) if (!ast_strlen_zero(e->iam.called_party_num)) { ss7_apply_plan_to_number(p->exten, sizeof(p->exten), linkset, e->iam.called_party_num, e->iam.called_nai); - sig_ss7_set_dnid(p, p->exten); + } else { + p->exten[0] = '\0'; } + sig_ss7_set_dnid(p, p->exten); if (p->immediate) { p->exten[0] = 's'; @@ -874,9 +960,11 @@ void *ss7_linkset(void *data) if (ast_exists_extension(NULL, p->context, p->exten, 1, p->cid_num)) { if (e->iam.cot_check_required) { + p->call_level = SIG_SS7_CALL_LEVEL_CONTINUITY; sig_ss7_loopback(p, 1); - } else + } else { ss7_start_call(p, linkset); + } } else { ast_debug(1, "Call on CIC for unconfigured extension %s\n", p->exten); p->alreadyhungup = 1; @@ -885,9 +973,8 @@ void *ss7_linkset(void *data) sig_ss7_unlock_private(p); break; case ISUP_EVENT_COT: - chanpos = ss7_find_cic(linkset, e->cot.cic, e->cot.opc); + chanpos = ss7_find_cic_gripe(linkset, e->cot.cic, e->cot.opc, "COT"); if (chanpos < 0) { - ast_log(LOG_WARNING, "COT on unconfigured CIC %d\n", e->cot.cic); isup_rel(ss7, e->cot.call, -1); break; } @@ -902,9 +989,8 @@ void *ss7_linkset(void *data) break; case ISUP_EVENT_CCR: ast_debug(1, "Got CCR request on CIC %d\n", e->ccr.cic); - chanpos = ss7_find_cic(linkset, e->ccr.cic, e->ccr.opc); + chanpos = ss7_find_cic_gripe(linkset, e->ccr.cic, e->ccr.opc, "CCR"); if (chanpos < 0) { - ast_log(LOG_WARNING, "CCR on unconfigured CIC %d\n", e->ccr.cic); break; } @@ -918,9 +1004,8 @@ void *ss7_linkset(void *data) break; case ISUP_EVENT_CVT: ast_debug(1, "Got CVT request on CIC %d\n", e->cvt.cic); - chanpos = ss7_find_cic(linkset, e->cvt.cic, e->cvt.opc); + chanpos = ss7_find_cic_gripe(linkset, e->cvt.cic, e->cvt.opc, "CVT"); if (chanpos < 0) { - ast_log(LOG_WARNING, "CVT on unconfigured CIC %d\n", e->cvt.cic); break; } @@ -933,9 +1018,10 @@ void *ss7_linkset(void *data) isup_cvr(linkset->ss7, e->cvt.cic, p->dpc); break; case ISUP_EVENT_REL: - chanpos = ss7_find_cic(linkset, e->rel.cic, e->rel.opc); + chanpos = ss7_find_cic_gripe(linkset, e->rel.cic, e->rel.opc, "REL"); if (chanpos < 0) { - ast_log(LOG_WARNING, "REL on unconfigured CIC %d\n", e->rel.cic); + /* Continue hanging up the call anyway. */ + isup_rlc(ss7, e->rel.call); break; } p = linkset->pvts[chanpos]; @@ -945,8 +1031,6 @@ void *ss7_linkset(void *data) p->owner->hangupcause = e->rel.cause; ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV); ast_channel_unlock(p->owner); - } else { - ast_log(LOG_WARNING, "REL on channel (CIC %d) without owner!\n", p->cic); } /* End the loopback if we have one */ @@ -958,12 +1042,12 @@ void *ss7_linkset(void *data) sig_ss7_unlock_private(p); break; case ISUP_EVENT_ACM: - chanpos = ss7_find_cic(linkset, e->acm.cic, e->acm.opc); + chanpos = ss7_find_cic_gripe(linkset, e->acm.cic, e->acm.opc, "ACM"); if (chanpos < 0) { - ast_log(LOG_WARNING, "ACM on unconfigured CIC %d\n", e->acm.cic); isup_rel(ss7, e->acm.call, -1); break; - } else { + } + { p = linkset->pvts[chanpos]; ast_debug(1, "Queueing frame from SS7_EVENT_ACM on CIC %d\n", p->cic); @@ -994,9 +1078,8 @@ void *ss7_linkset(void *data) } break; case ISUP_EVENT_CGB: - chanpos = ss7_find_cic(linkset, e->cgb.startcic, e->cgb.opc); + chanpos = ss7_find_cic_gripe(linkset, e->cgb.startcic, e->cgb.opc, "CGB"); if (chanpos < 0) { - ast_log(LOG_WARNING, "CGB on unconfigured CIC %d\n", e->cgb.startcic); break; } p = linkset->pvts[chanpos]; @@ -1004,9 +1087,8 @@ void *ss7_linkset(void *data) isup_cgba(linkset->ss7, e->cgb.startcic, e->cgb.endcic, e->cgb.opc, e->cgb.status, e->cgb.type); break; case ISUP_EVENT_CGU: - chanpos = ss7_find_cic(linkset, e->cgu.startcic, e->cgu.opc); + chanpos = ss7_find_cic_gripe(linkset, e->cgu.startcic, e->cgu.opc, "CGU"); if (chanpos < 0) { - ast_log(LOG_WARNING, "CGU on unconfigured CIC %d\n", e->cgu.startcic); break; } p = linkset->pvts[chanpos]; @@ -1014,9 +1096,8 @@ void *ss7_linkset(void *data) isup_cgua(linkset->ss7, e->cgu.startcic, e->cgu.endcic, e->cgu.opc, e->cgu.status, e->cgu.type); break; case ISUP_EVENT_UCIC: - chanpos = ss7_find_cic(linkset, e->ucic.cic, e->ucic.opc); + chanpos = ss7_find_cic_gripe(linkset, e->ucic.cic, e->ucic.opc, "UCIC"); if (chanpos < 0) { - ast_log(LOG_WARNING, "UCIC on unconfigured CIC %d\n", e->ucic.cic); break; } p = linkset->pvts[chanpos]; @@ -1027,9 +1108,8 @@ void *ss7_linkset(void *data) sig_ss7_unlock_private(p);/* doesn't require a SS7 acknowledgement */ break; case ISUP_EVENT_BLO: - chanpos = ss7_find_cic(linkset, e->blo.cic, e->blo.opc); + chanpos = ss7_find_cic_gripe(linkset, e->blo.cic, e->blo.opc, "BLO"); if (chanpos < 0) { - ast_log(LOG_WARNING, "BLO on unconfigured CIC %d\n", e->blo.cic); break; } p = linkset->pvts[chanpos]; @@ -1040,9 +1120,8 @@ void *ss7_linkset(void *data) isup_bla(linkset->ss7, e->blo.cic, p->dpc); break; case ISUP_EVENT_BLA: - chanpos = ss7_find_cic(linkset, e->bla.cic, e->bla.opc); + chanpos = ss7_find_cic_gripe(linkset, e->bla.cic, e->bla.opc, "BLA"); if (chanpos < 0) { - ast_log(LOG_WARNING, "BLA on unconfigured CIC %d\n", e->bla.cic); break; } ast_debug(1, "Blocking CIC %d\n", e->bla.cic); @@ -1052,9 +1131,8 @@ void *ss7_linkset(void *data) sig_ss7_unlock_private(p); break; case ISUP_EVENT_UBL: - chanpos = ss7_find_cic(linkset, e->ubl.cic, e->ubl.opc); + chanpos = ss7_find_cic_gripe(linkset, e->ubl.cic, e->ubl.opc, "UBL"); if (chanpos < 0) { - ast_log(LOG_WARNING, "UBL on unconfigured CIC %d\n", e->ubl.cic); break; } p = linkset->pvts[chanpos]; @@ -1065,9 +1143,8 @@ void *ss7_linkset(void *data) isup_uba(linkset->ss7, e->ubl.cic, p->dpc); break; case ISUP_EVENT_UBA: - chanpos = ss7_find_cic(linkset, e->uba.cic, e->uba.opc); + chanpos = ss7_find_cic_gripe(linkset, e->uba.cic, e->uba.opc, "UBA"); if (chanpos < 0) { - ast_log(LOG_WARNING, "UBA on unconfigured CIC %d\n", e->uba.cic); break; } p = linkset->pvts[chanpos]; @@ -1078,17 +1155,21 @@ void *ss7_linkset(void *data) break; case ISUP_EVENT_CON: case ISUP_EVENT_ANM: - if (e->e == ISUP_EVENT_CON) - cic = e->con.cic; - else - cic = e->anm.cic; - - chanpos = ss7_find_cic(linkset, cic, (e->e == ISUP_EVENT_ANM) ? e->anm.opc : e->con.opc); - if (chanpos < 0) { - ast_log(LOG_WARNING, "ANM/CON on unconfigured CIC %d\n", cic); - isup_rel(ss7, (e->e == ISUP_EVENT_ANM) ? e->anm.call : e->con.call, -1); - break; + if (e->e == ISUP_EVENT_CON) { + chanpos = ss7_find_cic_gripe(linkset, e->con.cic, e->con.opc, "CON"); + if (chanpos < 0) { + isup_rel(ss7, e->con.call, -1); + break; + } } else { + chanpos = ss7_find_cic_gripe(linkset, e->anm.cic, e->anm.opc, "ANM"); + if (chanpos < 0) { + isup_rel(ss7, e->anm.call, -1); + break; + } + } + + { p = linkset->pvts[chanpos]; sig_ss7_lock_private(p); if (p->call_level < SIG_SS7_CALL_LEVEL_CONNECT) { @@ -1106,30 +1187,43 @@ void *ss7_linkset(void *data) } break; case ISUP_EVENT_RLC: - chanpos = ss7_find_cic(linkset, e->rlc.cic, e->rlc.opc); + /* XXX Call ptr should be passed up from libss7! */ + chanpos = ss7_find_cic_gripe(linkset, e->rlc.cic, e->rlc.opc, "RLC"); if (chanpos < 0) { - ast_log(LOG_WARNING, "RLC on unconfigured CIC %d\n", e->rlc.cic); break; - } else { + } + { p = linkset->pvts[chanpos]; sig_ss7_lock_private(p); - if (p->alreadyhungup) + if (p->alreadyhungup) { + if (!p->owner) { + p->call_level = SIG_SS7_CALL_LEVEL_IDLE; + } p->ss7call = NULL; - else - ast_log(LOG_NOTICE, "Received RLC out and we haven't sent REL. Ignoring.\n"); + } sig_ss7_unlock_private(p); } break; case ISUP_EVENT_FAA: - chanpos = ss7_find_cic(linkset, e->faa.cic, e->faa.opc); + /*! + * \todo The handling of the SS7 FAA message is not good and I + * don't know enough to handle it correctly. + */ + chanpos = ss7_find_cic_gripe(linkset, e->faa.cic, e->faa.opc, "FAA"); if (chanpos < 0) { - ast_log(LOG_WARNING, "FAA on unconfigured CIC %d\n", e->faa.cic); + isup_rel(linkset->ss7, e->faa.call, -1); break; - } else { + } + { + /* XXX FAR and FAA used for something dealing with transfers? */ p = linkset->pvts[chanpos]; ast_debug(1, "FAA received on CIC %d\n", e->faa.cic); sig_ss7_lock_private(p); if (p->alreadyhungup){ + if (!p->owner) { + p->call_level = SIG_SS7_CALL_LEVEL_IDLE; + } + /* XXX We seem to be leaking the isup call structure here. */ p->ss7call = NULL; ast_log(LOG_NOTICE, "Received FAA and we haven't sent FAR. Ignoring.\n"); } @@ -1246,6 +1340,24 @@ int sig_ss7_add_sigchan(struct sig_ss7_linkset *linkset, int which, int ss7type, return 0; } +/*! + * \internal + * \brief Determine if a private channel structure is available. + * + * \param pvt Channel to determine if available. + * + * \return TRUE if the channel is available. + */ +static int sig_ss7_is_chan_available(struct sig_ss7_chan *pvt) +{ + if (!pvt->inalarm && !pvt->owner && !pvt->ss7call + && pvt->call_level == SIG_SS7_CALL_LEVEL_IDLE + && !pvt->locallyblocked && !pvt->remotelyblocked) { + return 1; + } + return 0; +} + /*! * \brief Determine if the specified channel is available for an outgoing call. * \since 1.8 @@ -1256,17 +1368,22 @@ int sig_ss7_add_sigchan(struct sig_ss7_linkset *linkset, int which, int ss7type, */ int sig_ss7_available(struct sig_ss7_chan *p) { + int available; + if (!p->ss7) { /* Something is wrong here. A SS7 channel without the ss7 pointer? */ return 0; } - if (!p->inalarm && !p->owner && !p->ss7call - && !p->locallyblocked && !p->remotelyblocked) { - return 1; + /* Only have to deal with the linkset lock. */ + ast_mutex_lock(&p->ss7->lock); + available = sig_ss7_is_chan_available(p); + if (available) { + p->call_level = SIG_SS7_CALL_LEVEL_ALLOCATED; } + ast_mutex_unlock(&p->ss7->lock); - return 0; + return available; } static unsigned char cid_pres2ss7pres(int cid_pres) @@ -1333,6 +1450,12 @@ int sig_ss7_call(struct sig_ss7_chan *p, struct ast_channel *ast, char *rdest) ss7_grab(p, p->ss7); + if (p->call_level != SIG_SS7_CALL_LEVEL_ALLOCATED) { + /* Call collision before sending IAM. Abort call. */ + ss7_rel(p->ss7); + return -1; + } + p->ss7call = isup_new_call(p->ss7->ss7); if (!p->ss7call) { ss7_rel(p->ss7); @@ -1447,14 +1570,14 @@ int sig_ss7_hangup(struct sig_ss7_chan *p, struct ast_channel *ast) p->owner = NULL; sig_ss7_set_dialing(p, 0); - p->call_level = SIG_SS7_CALL_LEVEL_IDLE; p->outgoing = 0; p->progress = 0; p->rlt = 0; p->exten[0] = '\0'; /* Perform low level hangup if no owner left */ + ss7_grab(p, p->ss7); + p->call_level = SIG_SS7_CALL_LEVEL_IDLE; if (p->ss7call) { - ss7_grab(p, p->ss7); if (!p->alreadyhungup) { const char *cause = pbx_builtin_getvar_helper(ast,"SS7_CAUSE"); int icause = ast->hangupcause ? ast->hangupcause : -1; @@ -1466,11 +1589,9 @@ int sig_ss7_hangup(struct sig_ss7_chan *p, struct ast_channel *ast) } isup_rel(p->ss7->ss7, p->ss7call, icause); p->alreadyhungup = 1; - } else { - ast_log(LOG_WARNING, "Trying to hangup twice!\n"); } - ss7_rel(p->ss7); } + ss7_rel(p->ss7); return res; } @@ -1537,17 +1658,19 @@ int sig_ss7_indicate(struct sig_ss7_chan *p, struct ast_channel *chan, int condi res = sig_ss7_play_tone(p, SIG_SS7_TONE_BUSY); break; case AST_CONTROL_RINGING: + ss7_grab(p, p->ss7); if (p->call_level < SIG_SS7_CALL_LEVEL_ALERTING && !p->outgoing) { p->call_level = SIG_SS7_CALL_LEVEL_ALERTING; - if (p->ss7 && p->ss7->ss7) { - ss7_grab(p, p->ss7); - if ((isup_far(p->ss7->ss7, p->ss7call)) != -1) - p->rlt = 1; - if (p->rlt != 1) /* No need to send CPG if call will be RELEASE */ - isup_cpg(p->ss7->ss7, p->ss7call, CPG_EVENT_ALERTING); - ss7_rel(p->ss7); + if ((isup_far(p->ss7->ss7, p->ss7call)) != -1) { + p->rlt = 1; + } + + /* No need to send CPG if call will be RELEASE */ + if (p->rlt != 1) { + isup_cpg(p->ss7->ss7, p->ss7call, CPG_EVENT_ALERTING); } } + ss7_rel(p->ss7); res = sig_ss7_play_tone(p, SIG_SS7_TONE_RINGTONE); @@ -1557,37 +1680,34 @@ int sig_ss7_indicate(struct sig_ss7_chan *p, struct ast_channel *chan, int condi break; case AST_CONTROL_PROCEEDING: ast_debug(1,"Received AST_CONTROL_PROCEEDING on %s\n",chan->name); + ss7_grab(p, p->ss7); /* This IF sends the FAR for an answered ALEG call */ if (chan->_state == AST_STATE_UP && (p->rlt != 1)){ - ss7_grab(p, p->ss7); if ((isup_far(p->ss7->ss7, p->ss7call)) != -1) { p->rlt = 1; } - ss7_rel(p->ss7); } if (p->call_level < SIG_SS7_CALL_LEVEL_PROCEEDING && !p->outgoing) { p->call_level = SIG_SS7_CALL_LEVEL_PROCEEDING; - if (p->ss7 && p->ss7->ss7) { - ss7_grab(p, p->ss7); - isup_acm(p->ss7->ss7, p->ss7call); - ss7_rel(p->ss7); - } + isup_acm(p->ss7->ss7, p->ss7call); } + ss7_rel(p->ss7); /* don't continue in ast_indicate */ res = 0; break; case AST_CONTROL_PROGRESS: ast_debug(1,"Received AST_CONTROL_PROGRESS on %s\n",chan->name); + ss7_grab(p, p->ss7); if (!p->progress && p->call_level < SIG_SS7_CALL_LEVEL_ALERTING && !p->outgoing) { p->progress = 1;/* No need to send inband-information progress again. */ - if (p->ss7 && p->ss7->ss7) { - ss7_grab(p, p->ss7); - isup_cpg(p->ss7->ss7, p->ss7call, CPG_EVENT_INBANDINFO); - ss7_rel(p->ss7); - /* enable echo canceler here on SS7 calls */ - sig_ss7_set_echocanceller(p, 1); - } + isup_cpg(p->ss7->ss7, p->ss7call, CPG_EVENT_INBANDINFO); + ss7_rel(p->ss7); + + /* enable echo canceler here on SS7 calls */ + sig_ss7_set_echocanceller(p, 1); + } else { + ss7_rel(p->ss7); } /* don't continue in ast_indicate */ res = 0; @@ -1639,6 +1759,11 @@ struct ast_channel *sig_ss7_request(struct sig_ss7_chan *p, enum sig_ss7_law law ast = sig_ss7_new_ast_channel(p, AST_STATE_RESERVED, law, transfercapability, p->exten, requestor); if (!ast) { p->outgoing = 0; + + /* Release the allocated channel. Only have to deal with the linkset lock. */ + ast_mutex_lock(&p->ss7->lock); + p->call_level = SIG_SS7_CALL_LEVEL_IDLE; + ast_mutex_unlock(&p->ss7->lock); } return ast; } @@ -1656,6 +1781,51 @@ void sig_ss7_chan_delete(struct sig_ss7_chan *doomed) ast_free(doomed); } +#define SIG_SS7_SC_HEADER "%-4s %4s %-4s %-3s %-3s %-10s %-4s %s\n" +#define SIG_SS7_SC_LINE "%4d %4d %-4s %-3s %-3s %-10s %-4s %s" +void sig_ss7_cli_show_channels_header(int fd) +{ + ast_cli(fd, SIG_SS7_SC_HEADER, "link", "", "Chan", "Lcl", "Rem", "Call", "SS7", "Channel"); + ast_cli(fd, SIG_SS7_SC_HEADER, "set", "Chan", "Idle", "Blk", "Blk", "Level", "Call", "Name"); +} + +void sig_ss7_cli_show_channels(int fd, struct sig_ss7_linkset *linkset) +{ + char line[256]; + int idx; + struct sig_ss7_chan *pvt; + + ast_mutex_lock(&linkset->lock); + for (idx = 0; idx < linkset->numchans; ++idx) { + if (!linkset->pvts[idx]) { + continue; + } + pvt = linkset->pvts[idx]; + sig_ss7_lock_private(pvt); + sig_ss7_lock_owner(linkset, idx); + + snprintf(line, sizeof(line), SIG_SS7_SC_LINE, + linkset->span, + pvt->channel, + sig_ss7_is_chan_available(pvt) ? "Yes" : "No", + pvt->locallyblocked ? "Yes" : "No", + pvt->remotelyblocked ? "Yes" : "No", + sig_ss7_call_level2str(pvt->call_level), + pvt->ss7call ? "Yes" : "No", + pvt->owner ? pvt->owner->name : ""); + + if (pvt->owner) { + ast_channel_unlock(pvt->owner); + } + sig_ss7_unlock_private(pvt); + + ast_mutex_unlock(&linkset->lock); + ast_cli(fd, "%s\n", line); + ast_mutex_lock(&linkset->lock); + } + ast_mutex_unlock(&linkset->lock); +} + /*! * \brief Create a new sig_ss7 private channel structure. * \since 1.8 @@ -1687,7 +1857,7 @@ struct sig_ss7_chan *sig_ss7_chan_new(void *pvt_data, struct sig_ss7_callback *c * \brief Initialize the SS7 linkset control. * \since 1.8 * - * \param ss7 sig_ss7 SS7 control structure. + * \param ss7 SS7 linkset control structure. * * \return Nothing */ diff --git a/channels/sig_ss7.h b/channels/sig_ss7.h index 73839b1b3..14a998010 100644 --- a/channels/sig_ss7.h +++ b/channels/sig_ss7.h @@ -88,14 +88,38 @@ enum sig_ss7_law { enum sig_ss7_call_level { /*! Call does not exist. */ SIG_SS7_CALL_LEVEL_IDLE, - /*! Call is present but has no response yet. (SETUP) */ + /*! + * Call is allocated to the channel. + * We have not sent or responded to IAM yet. + */ + SIG_SS7_CALL_LEVEL_ALLOCATED, + /*! + * Call is performing continuity check after receiving IAM. + * We are waiting for COT to proceed further. + */ + SIG_SS7_CALL_LEVEL_CONTINUITY, + /*! + * Call is present. + * We have not seen a response or sent further call progress to an IAM yet. + */ SIG_SS7_CALL_LEVEL_SETUP, - /*! Call routing is happening. (PROCEEDING) */ + /*! + * Call routing is happening. + * We have sent or received ACM. + */ SIG_SS7_CALL_LEVEL_PROCEEDING, - /*! Called party is being alerted of the call. (ALERTING) */ + /*! + * Called party is being alerted of the call. + * We have sent or received CPG(ALERTING)/ACM(ALERTING). + */ SIG_SS7_CALL_LEVEL_ALERTING, - /*! Call is connected/answered. (CONNECT) */ + /*! + * Call is connected/answered. + * We have sent or received CON/ANM. + */ SIG_SS7_CALL_LEVEL_CONNECT, + /*! Call has collided with incoming call. */ + SIG_SS7_CALL_LEVEL_GLARE, }; struct sig_ss7_linkset; @@ -232,6 +256,7 @@ struct sig_ss7_linkset { int linkstate[SIG_SS7_NUM_DCHANS]; int numchans; int span; /*!< span number put into user output messages */ + int debug; /*!< set to true if to dump SS7 event info */ enum { LINKSET_STATE_DOWN = 0, LINKSET_STATE_UP @@ -267,6 +292,9 @@ void sig_ss7_chan_delete(struct sig_ss7_chan *doomed); struct sig_ss7_chan *sig_ss7_chan_new(void *pvt_data, struct sig_ss7_callback *callback, struct sig_ss7_linkset *ss7); void sig_ss7_init_linkset(struct sig_ss7_linkset *ss7); +void sig_ss7_cli_show_channels_header(int fd); +void sig_ss7_cli_show_channels(int fd, struct sig_ss7_linkset *linkset); + /* ------------------------------------------------------------------- */ -- cgit v1.2.3