diff --git a/CHANGES b/CHANGES index 031533fd2b1e17730268f811f6ecef9db3425888..c3774fa6ad0cd5dbe6ad0dadc3187175fc978844 100644 --- a/CHANGES +++ b/CHANGES @@ -20,6 +20,18 @@ AMI res_manager_presence_state.so. If the high frequency of these events is problematic for you, do not load these modules. +chan_dahdi +------------------ + * SS7 support now requires libss7 v2.0 or later. + + * Added SS7 support for connected line and redirecting. + + * Most SS7 CLI commands are reworked as well as new SS7 commands added. + See online CLI help. + + * Added several SS7 config option parameters described in + chan_dahdi.conf.sample. + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 12.3.0 to Asterisk 12.4.0 ------------ ------------------------------------------------------------------------------ diff --git a/UPGRADE.txt b/UPGRADE.txt index 9d3f462227449b3508397466a32e7f73c93a02b1..82dad8c2e17790a90d84f7b4ead13154fcfce504 100644 --- a/UPGRADE.txt +++ b/UPGRADE.txt @@ -117,6 +117,9 @@ CDRs: handler subroutine). In general, this is not the preferred default: this causes extra CDRs to be generated for a channel in many common dialplans. +chan_dahdi: + - SS7 support now requires libss7 v2.0 or later. + chan_sip: - Made set SIPREFERREDBYHDR as inheritable for better chan_pjsip interoperability. diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index 71e5831e031126b920bdbe51b1b4325f89399379..adc025ae0672bdcec5b6124dca5609d3f0151ce1 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -81,7 +81,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #if defined(HAVE_SS7) #include "sig_ss7.h" -#if defined(LIBSS7_ABI_COMPATIBILITY) +#if !defined(LIBSS7_ABI_COMPATIBILITY) +#error "Upgrade your libss7" +#elif LIBSS7_ABI_COMPATIBILITY != 2 #error "Your installed libss7 is not compatible" #endif #endif /* defined(HAVE_SS7) */ @@ -611,6 +613,7 @@ struct dahdi_ss7 { static struct dahdi_ss7 linksets[NUM_SPANS]; static int cur_ss7type = -1; +static int cur_slc = -1; static int cur_linkset = -1; static int cur_pointcode = -1; static int cur_cicbeginswith = -1; @@ -758,8 +761,8 @@ const char * const subnames[] = { MEMBER(dahdi_pvt, mwimonitoractive, AST_DATA_BOOLEAN) \ MEMBER(dahdi_pvt, mwisendactive, AST_DATA_BOOLEAN) \ MEMBER(dahdi_pvt, inservice, AST_DATA_BOOLEAN) \ - MEMBER(dahdi_pvt, locallyblocked, AST_DATA_BOOLEAN) \ - MEMBER(dahdi_pvt, remotelyblocked, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, locallyblocked, AST_DATA_UNSIGNED_INTEGER) \ + MEMBER(dahdi_pvt, remotelyblocked, AST_DATA_UNSIGNED_INTEGER) \ MEMBER(dahdi_pvt, manages_span_alarms, AST_DATA_BOOLEAN) \ MEMBER(dahdi_pvt, use_smdi, AST_DATA_BOOLEAN) \ MEMBER(dahdi_pvt, context, AST_DATA_STRING) \ @@ -873,7 +876,8 @@ static struct dahdi_chan_conf dahdi_chan_conf_default(void) .internationalprefix = "", .nationalprefix = "", .subscriberprefix = "", - .unknownprefix = "" + .unknownprefix = "", + .networkroutedprefix = "" }, #endif /* defined(HAVE_SS7) */ #ifdef HAVE_OPENR2 @@ -3019,6 +3023,34 @@ static void my_ss7_set_loopback(void *pvt, int enable) } #endif /* defined(HAVE_SS7) */ +#if defined(HAVE_SS7) +/*! + * \internal + * \brief Find the linkset to which SS7 belongs. + * \since 11.0 + * + * \param ss7 structure to match on. + * + * \retval linkset if found. + * \retval NULL if not found. + */ +static struct sig_ss7_linkset *my_ss7_find_linkset(struct ss7 *ss7) +{ + int idx; + + if (!ss7) { + return NULL; + } + + for (idx = 0; idx < NUM_SPANS; ++idx) { + if (linksets[idx].ss7.ss7 == ss7) { + return &linksets[idx].ss7; + } + } + return NULL; +} +#endif /* defined(HAVE_SS7) */ + #if defined(HAVE_SS7) /*! * \internal @@ -3128,6 +3160,7 @@ struct sig_ss7_callback sig_ss7_callbacks = .set_callerid = my_set_callerid, .set_dnid = my_set_dnid, .open_media = my_pri_ss7_open_media, + .find_linkset = my_ss7_find_linkset, }; #endif /* defined(HAVE_SS7) */ @@ -10688,9 +10721,10 @@ static int mwi_send_process_buffer(struct dahdi_pvt * pvt, int num_read) break; case MWI_SEND_SPILL: /* We read some number of bytes. Write an equal amount of data */ - if(0 < num_read) { - if (num_read > pvt->cidlen - pvt->cidpos) + if (0 < num_read) { + if (num_read > pvt->cidlen - pvt->cidpos) { num_read = pvt->cidlen - pvt->cidpos; + } res = write(pvt->subs[SUB_REAL].dfd, pvt->cidspill + pvt->cidpos, num_read); if (res > 0) { pvt->cidpos += res; @@ -10741,7 +10775,7 @@ static int mwi_send_process_event(struct dahdi_pvt * pvt, int event) if (MWI_SEND_DONE != pvt->mwisend_data.mwisend_current) { switch (event) { case DAHDI_EVENT_RINGEROFF: - if(pvt->mwisend_data.mwisend_current == MWI_SEND_SA_WAIT) { + if (pvt->mwisend_data.mwisend_current == MWI_SEND_SA_WAIT) { handled = 1; if (dahdi_set_hook(pvt->subs[SUB_REAL].dfd, DAHDI_RINGOFF) ) { @@ -11946,6 +11980,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, ast_copy_string(ss7->ss7.nationalprefix, conf->ss7.ss7.nationalprefix, sizeof(ss7->ss7.nationalprefix)); ast_copy_string(ss7->ss7.subscriberprefix, conf->ss7.ss7.subscriberprefix, sizeof(ss7->ss7.subscriberprefix)); ast_copy_string(ss7->ss7.unknownprefix, conf->ss7.ss7.unknownprefix, sizeof(ss7->ss7.unknownprefix)); + ast_copy_string(ss7->ss7.networkroutedprefix, conf->ss7.ss7.networkroutedprefix, sizeof(ss7->ss7.networkroutedprefix)); ss7->ss7.called_nai = conf->ss7.ss7.called_nai; ss7->ss7.calling_nai = conf->ss7.ss7.calling_nai; @@ -12452,12 +12487,12 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, tmp->named_pickupgroups = ast_ref_namedgroups(conf->chan.named_pickupgroups); if (conf->chan.vars) { struct ast_variable *v, *tmpvar; - for (v = conf->chan.vars ; v ; v = v->next) { - if ((tmpvar = ast_variable_new(v->name, v->value, v->file))) { - tmpvar->next = tmp->vars; - tmp->vars = tmpvar; - } - } + for (v = conf->chan.vars ; v ; v = v->next) { + if ((tmpvar = ast_variable_new(v->name, v->value, v->file))) { + tmpvar->next = tmp->vars; + tmp->vars = tmpvar; + } + } } tmp->hwrxgain_enabled = conf->chan.hwrxgain_enabled; tmp->hwtxgain_enabled = conf->chan.hwtxgain_enabled; @@ -12566,6 +12601,9 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, #if defined(HAVE_SS7) case SIG_SS7: tmp->inservice = 0; + if (tmp->ss7->flags & LINKSET_FLAG_INITIALHWBLO) { + tmp->remotelyblocked |= SS7_BLOCKED_HARDWARE; + } break; #endif /* defined(HAVE_SS7) */ default: @@ -12600,6 +12638,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, case SIG_SS7: if (ss7_chan) { ss7_chan->inalarm = tmp->inalarm; + ss7_chan->inservice = tmp->inservice; ss7_chan->stripmsd = tmp->stripmsd; ss7_chan->hidecallerid = tmp->hidecallerid; @@ -14891,6 +14930,9 @@ static int dahdi_restart(void) } ss7_set_error(dahdi_ss7_error); ss7_set_message(dahdi_ss7_message); + ss7_set_hangup(sig_ss7_cb_hangup); + ss7_set_notinservice(sig_ss7_cb_notinservice); + ss7_set_call_null(sig_ss7_cb_call_null); #endif /* defined(HAVE_SS7) */ if (setup_dahdi(2) != 0) { @@ -14943,9 +14985,8 @@ static char *dahdi_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cl ast_group_t targetnum = 0; int filtertype = 0; struct dahdi_pvt *tmp = NULL; - char tmps[20] = ""; - char statestr[20] = ""; - char blockstr[20] = ""; + char tmps[20]; + char blockstr[20]; switch (cmd) { case CLI_INIT: @@ -14961,8 +15002,9 @@ static char *dahdi_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cl /* syntax: dahdi show channels [ group <group> | context <context> ] */ - if (!((a->argc == 3) || (a->argc == 5))) + if (!((a->argc == 3) || (a->argc == 5))) { return CLI_SHOWUSAGE; + } if (a->argc == 5) { if (!strcasecmp(a->argv[3], "group")) { @@ -14977,7 +15019,7 @@ static char *dahdi_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cl } } - ast_cli(a->fd, FORMAT2, "Chan", "Extension", "Context", "Language", "MOH Interpret", "Blocked", "State", "Description"); + ast_cli(a->fd, FORMAT2, "Chan", "Extension", "Context", "Language", "MOH Interpret", "Blocked", "In Service", "Description"); ast_mutex_lock(&iflock); for (tmp = iflist; tmp; tmp = tmp->next) { if (filtertype) { @@ -14998,24 +15040,15 @@ static char *dahdi_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cl } if (tmp->channel > 0) { snprintf(tmps, sizeof(tmps), "%d", tmp->channel); - } else + } else { ast_copy_string(tmps, "pseudo", sizeof(tmps)); + } - if (tmp->locallyblocked) - blockstr[0] = 'L'; - else - blockstr[0] = ' '; - - if (tmp->remotelyblocked) - blockstr[1] = 'R'; - else - blockstr[1] = ' '; - + blockstr[0] = tmp->locallyblocked ? 'L' : ' '; + blockstr[1] = tmp->remotelyblocked ? 'R' : ' '; blockstr[2] = '\0'; - snprintf(statestr, sizeof(statestr), "%s", "In Service"); - - ast_cli(a->fd, FORMAT, tmps, tmp->exten, tmp->context, tmp->language, tmp->mohinterpret, blockstr, statestr, tmp->description); + ast_cli(a->fd, FORMAT, tmps, tmp->exten, tmp->context, tmp->language, tmp->mohinterpret, blockstr, tmp->inservice ? "Yes" : "No", tmp->description); } ast_mutex_unlock(&iflock); return CLI_SUCCESS; @@ -16023,7 +16056,7 @@ static int linkset_addsigchan(int sigchan) (params.sigtype == DAHDI_SIG_MTP2) ? SS7_TRANSPORT_DAHDIMTP2 : SS7_TRANSPORT_DAHDIDCHAN, - si.alarms, cur_networkindicator, cur_pointcode, cur_adjpointcode); + si.alarms, cur_networkindicator, cur_pointcode, cur_adjpointcode, cur_slc); if (res) { dahdi_close_ss7_fd(link, curfd); return -1; @@ -16049,8 +16082,11 @@ static char *handle_ss7_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_a case CLI_GENERATE: return NULL; } - if (a->argc < 6) + + if (a->argc < 6) { return CLI_SHOWUSAGE; + } + span = atoi(a->argv[5]); if ((span < 1) || (span > NUM_SPANS)) { ast_cli(a->fd, "Invalid linkset %s. Should be a number from %d to %d\n", a->argv[5], 1, NUM_SPANS); @@ -16075,24 +16111,35 @@ static char *handle_ss7_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_a #endif /* defined(HAVE_SS7) */ #if defined(HAVE_SS7) -static char *handle_ss7_block_cic(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +static char *handle_ss7_cic_blocking(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { int linkset, cic; - int blocked = -1, i; + int blocked, i; + int do_block = 0; + unsigned int dpc; + switch (cmd) { case CLI_INIT: - e->command = "ss7 block cic"; + e->command = "ss7 {block|unblock} cic"; e->usage = - "Usage: ss7 block cic <linkset> <CIC>\n" - " Sends a remote blocking request for the given CIC on the specified linkset\n"; + "Usage: ss7 {block|unblock} cic <linkset> <dpc> <CIC>\n" + " Sends a remote {blocking|unblocking} request for the given CIC on the specified linkset\n"; return NULL; case CLI_GENERATE: return NULL; } - if (a->argc == 5) + + if (a->argc == 6) { linkset = atoi(a->argv[3]); - else + } else { return CLI_SHOWUSAGE; + } + + if (!strcasecmp(a->argv[1], "block")) { + do_block = 1; + } else if (strcasecmp(a->argv[1], "unblock")) { + return CLI_SHOWUSAGE; + } if ((linkset < 1) || (linkset > NUM_SPANS)) { ast_cli(a->fd, "Invalid linkset %s. Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS); @@ -16104,63 +16151,157 @@ static char *handle_ss7_block_cic(struct ast_cli_entry *e, int cmd, struct ast_c return CLI_SUCCESS; } - cic = atoi(a->argv[4]); - + cic = atoi(a->argv[5]); if (cic < 1) { ast_cli(a->fd, "Invalid CIC specified!\n"); return CLI_SUCCESS; } + dpc = atoi(a->argv[4]); + if (dpc < 1) { + ast_cli(a->fd, "Invalid DPC specified!\n"); + return CLI_SUCCESS; + } + for (i = 0; i < linksets[linkset-1].ss7.numchans; i++) { - if (linksets[linkset-1].ss7.pvts[i]->cic == cic) { + if (linksets[linkset-1].ss7.pvts[i] && linksets[linkset-1].ss7.pvts[i]->cic == cic && linksets[linkset-1].ss7.pvts[i]->dpc == dpc) { blocked = linksets[linkset-1].ss7.pvts[i]->locallyblocked; - if (!blocked) { - ast_mutex_lock(&linksets[linkset-1].ss7.lock); - isup_blo(linksets[linkset-1].ss7.ss7, cic, linksets[linkset-1].ss7.pvts[i]->dpc); - ast_mutex_unlock(&linksets[linkset-1].ss7.lock); + if (!do_block ^ !(blocked & SS7_BLOCKED_MAINTENANCE)) { + if (sig_ss7_cic_blocking(&linksets[linkset-1].ss7, do_block, i) < 0) { + ast_cli(a->fd, "Unable to allocate new ss7call\n"); + } else { + ast_cli(a->fd, "Sent %sblocking request for linkset %d on CIC %d DPC %d\n", (do_block) ? "" : "un", linkset, cic, dpc); + } + } else if (!do_block && blocked) { + ast_cli(a->fd, "CIC %d is hardware locally blocked!\n", cic); + } else { + ast_cli(a->fd, "CIC %d %s locally blocked\n", cic, do_block ? "already" : "is not"); } + return CLI_SUCCESS; } } - if (blocked < 0) { - ast_cli(a->fd, "Invalid CIC specified!\n"); + ast_cli(a->fd, "Invalid CIC specified!\n"); + return CLI_SUCCESS; +} +#endif /* defined(HAVE_SS7) */ + +#if defined(HAVE_SS7) +static char *handle_ss7_linkset_mng(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + int linkset, i; + enum { + DO_BLOCK, + DO_UNBLOCK, + DO_RESET, + } do_what; + + switch (cmd) { + case CLI_INIT: + e->command = "ss7 {reset|block|unblock} linkset"; + e->usage = + "Usage: ss7 {reset|block|unblock} linkset <linkset number>\n" + " Sends a remote {reset|blocking|unblocking} request for all CICs on the given linkset\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc == 4) { + linkset = atoi(a->argv[3]); + } else { + return CLI_SHOWUSAGE; + } + + if (!strcasecmp(a->argv[1], "block")) { + do_what = DO_BLOCK; + } else if (!strcasecmp(a->argv[1], "unblock")) { + do_what = DO_UNBLOCK; + } else if (!strcasecmp(a->argv[1], "reset")) { + do_what = DO_RESET; + } else { + return CLI_SHOWUSAGE; + } + + if ((linkset < 1) || (linkset > NUM_SPANS)) { + ast_cli(a->fd, "Invalid linkset %s. Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS); return CLI_SUCCESS; } - if (!blocked) - ast_cli(a->fd, "Sent blocking request for linkset %d on CIC %d\n", linkset, cic); - else - ast_cli(a->fd, "CIC %d already locally blocked\n", cic); + if (!linksets[linkset - 1].ss7.ss7) { + ast_cli(a->fd, "No SS7 running on linkset %d\n", linkset); + return CLI_SUCCESS; + } - /* Break poll on the linkset so it sends our messages */ - pthread_kill(linksets[linkset-1].ss7.master, SIGURG); + for (i = 0; i < linksets[linkset - 1].ss7.numchans; i++) { + /* XXX Should be done with GRS/CGB/CGU instead - see ss7_reset_linkset() */ + if (linksets[linkset - 1].ss7.pvts[i]) { + switch (do_what) { + case DO_BLOCK: + case DO_UNBLOCK: + if (sig_ss7_cic_blocking(&linksets[linkset - 1].ss7, do_what == DO_BLOCK, i)) { + ast_cli(a->fd, "Sent remote %s request on CIC %d\n", + (do_what == DO_BLOCK) ? "blocking" : "unblocking", + linksets[linkset - 1].ss7.pvts[i]->cic); + } + break; + case DO_RESET: + if (sig_ss7_reset_cic(&linksets[linkset - 1].ss7, + linksets[linkset - 1].ss7.pvts[i]->cic, + linksets[linkset - 1].ss7.pvts[i]->dpc)) { + ast_cli(a->fd, "Sent reset request on CIC %d\n", + linksets[linkset - 1].ss7.pvts[i]->cic); + } + break; + } + } + } return CLI_SUCCESS; } #endif /* defined(HAVE_SS7) */ #if defined(HAVE_SS7) -static char *handle_ss7_block_linkset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +static char *handle_ss7_group_blocking(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - int linkset; - int i; + int linkset, cic, range, chanpos; + int i, dpc, orient = 0; + int do_block = 0; + unsigned char state[255]; + switch (cmd) { case CLI_INIT: - e->command = "ss7 block linkset"; + e->command = "ss7 {block|unblock} group"; e->usage = - "Usage: ss7 block linkset <linkset number>\n" - " Sends a remote blocking request for all CICs on the given linkset\n"; + "Usage: ss7 {block|unblock} group <linkset> <dpc> <1st. CIC> <range> [H]\n" + " Sends a remote {blocking|unblocking} request for CIC range on the specified linkset\n"; return NULL; case CLI_GENERATE: return NULL; } - if (a->argc == 4) + + if (a->argc == 7 || a->argc == 8) { linkset = atoi(a->argv[3]); - else + } else { return CLI_SHOWUSAGE; + } + + if (!strcasecmp(a->argv[1], "block")) { + do_block = 1; + } else if (strcasecmp(a->argv[1], "unblock")) { + return CLI_SHOWUSAGE; + } + + if (a->argc == 8) { + if (!strcasecmp(a->argv[7], "H")) { + orient = 1; + } else { + return CLI_SHOWUSAGE; + } + } if ((linkset < 1) || (linkset > NUM_SPANS)) { - ast_cli(a->fd, "Invalid linkset %s. Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS); + ast_cli(a->fd, "Invalid linkset %s. Should be a number %d to %d\n", a->argv[4], 1, NUM_SPANS); return CLI_SUCCESS; } @@ -16169,43 +16310,81 @@ static char *handle_ss7_block_linkset(struct ast_cli_entry *e, int cmd, struct a return CLI_SUCCESS; } - for (i = 0; i < linksets[linkset-1].ss7.numchans; i++) { - ast_cli(a->fd, "Sending remote blocking request on CIC %d\n", linksets[linkset-1].ss7.pvts[i]->cic); - ast_mutex_lock(&linksets[linkset-1].ss7.lock); - isup_blo(linksets[linkset-1].ss7.ss7, linksets[linkset-1].ss7.pvts[i]->cic, linksets[linkset-1].ss7.pvts[i]->dpc); + cic = atoi(a->argv[5]); + if (cic < 1) { + ast_cli(a->fd, "Invalid CIC specified!\n"); + return CLI_SUCCESS; + } + + range = atoi(a->argv[6]); + /* ITU-T Q.763 3.43 - range 0 is reserved, which makes a range of 2 CICs a minimum group */ + if (range < 1 || range > (linksets[linkset - 1].ss7.type == SS7_ANSI ? 24 : 31)) { + ast_cli(a->fd, "Invalid range specified!\n"); + return CLI_SUCCESS; + } + + dpc = atoi(a->argv[4]); + if (dpc < 1) { + ast_cli(a->fd, "Invalid DPC specified!\n"); + return CLI_SUCCESS; + } + + ast_mutex_lock(&linksets[linkset-1].ss7.lock); + if (!sig_ss7_find_cic_range(&linksets[linkset-1].ss7, cic, cic + range, dpc)) { ast_mutex_unlock(&linksets[linkset-1].ss7.lock); + ast_cli(a->fd, "Invalid CIC/RANGE\n"); + return CLI_SHOWUSAGE; } - /* Break poll on the linkset so it sends our messages */ - pthread_kill(linksets[linkset-1].ss7.master, SIGURG); + memset(state, 0, sizeof(state)); + for (i = 0; i <= range; ++i) { + state[i] = 1; + } + /* We are guaranteed to find chanpos because of sig_ss7_find_cic_range() includes it. */ + chanpos = sig_ss7_find_cic(&linksets[linkset-1].ss7, cic, dpc); + if (sig_ss7_group_blocking(&linksets[linkset-1].ss7, do_block, chanpos, cic + range, state, orient)) { + ast_cli(a->fd, "Unable allocate new ss7call\n"); + } else { + ast_cli(a->fd, "Sending remote%s %sblocking request linkset %d on CIC %d range %d\n", + orient ? " hardware" : "", do_block ? "" : "un", linkset, cic, range); + } + + ast_mutex_unlock(&linksets[linkset-1].ss7.lock); + + /* Break poll on the linkset so it sends our messages */ + if (linksets[linkset-1].ss7.master != AST_PTHREADT_NULL) { + pthread_kill(linksets[linkset-1].ss7.master, SIGURG); + } return CLI_SUCCESS; } #endif /* defined(HAVE_SS7) */ #if defined(HAVE_SS7) -static char *handle_ss7_unblock_cic(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +static char *handle_ss7_group_reset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - int linkset, cic; - int i, blocked = -1; + int linkset, cic, range; + unsigned int dpc; + switch (cmd) { case CLI_INIT: - e->command = "ss7 unblock cic"; + e->command = "ss7 reset group"; e->usage = - "Usage: ss7 unblock cic <linkset> <CIC>\n" - " Sends a remote unblocking request for the given CIC on the specified linkset\n"; + "Usage: ss7 reset group <linkset> <dpc> <1st CIC> <range>\n" + " Send a GRS for the given CIC range on the specified linkset\n"; return NULL; case CLI_GENERATE: return NULL; } - if (a->argc == 5) + if (a->argc == 7) { linkset = atoi(a->argv[3]); - else + } else { return CLI_SHOWUSAGE; + } if ((linkset < 1) || (linkset > NUM_SPANS)) { - ast_cli(a->fd, "Invalid linkset %s. Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS); + ast_cli(a->fd, "Invalid linkset %s. Should be a number %d to %d\n", a->argv[4], 1, NUM_SPANS); return CLI_SUCCESS; } @@ -16214,54 +16393,69 @@ static char *handle_ss7_unblock_cic(struct ast_cli_entry *e, int cmd, struct ast return CLI_SUCCESS; } - cic = atoi(a->argv[4]); + cic = atoi(a->argv[5]); if (cic < 1) { ast_cli(a->fd, "Invalid CIC specified!\n"); return CLI_SUCCESS; } - for (i = 0; i < linksets[linkset-1].ss7.numchans; i++) { - if (linksets[linkset-1].ss7.pvts[i]->cic == cic) { - blocked = linksets[linkset-1].ss7.pvts[i]->locallyblocked; - if (blocked) { - ast_mutex_lock(&linksets[linkset-1].ss7.lock); - isup_ubl(linksets[linkset-1].ss7.ss7, cic, linksets[linkset-1].ss7.pvts[i]->dpc); - ast_mutex_unlock(&linksets[linkset-1].ss7.lock); - } - } + range = atoi(a->argv[6]); + if (range < 1 || range > (linksets[linkset - 1].ss7.type == SS7_ANSI ? 24 : 31)) { + ast_cli(a->fd, "Invalid range specified!\n"); + return CLI_SUCCESS; } - if (blocked > 0) - ast_cli(a->fd, "Sent unblocking request for linkset %d on CIC %d\n", linkset, cic); + dpc = atoi(a->argv[4]); + if (dpc < 1) { + ast_cli(a->fd, "Invalid DPC specified!\n"); + return CLI_SUCCESS; + } - /* Break poll on the linkset so it sends our messages */ - pthread_kill(linksets[linkset-1].ss7.master, SIGURG); + ast_mutex_lock(&linksets[linkset-1].ss7.lock); + if (!sig_ss7_find_cic_range(&linksets[linkset-1].ss7, cic, cic + range, dpc)) { + ast_mutex_unlock(&linksets[linkset-1].ss7.lock); + ast_cli(a->fd, "Invalid CIC/RANGE\n"); + return CLI_SHOWUSAGE; + } + if (sig_ss7_reset_group(&linksets[linkset-1].ss7, cic, dpc, range)) { + ast_cli(a->fd, "Unable to allocate new ss7call\n"); + } else { + ast_cli(a->fd, "GRS sent ... \n"); + } + + ast_mutex_unlock(&linksets[linkset-1].ss7.lock); + + /* Break poll on the linkset so it sends our messages */ + if (linksets[linkset-1].ss7.master != AST_PTHREADT_NULL) { + pthread_kill(linksets[linkset-1].ss7.master, SIGURG); + } return CLI_SUCCESS; } #endif /* defined(HAVE_SS7) */ #if defined(HAVE_SS7) -static char *handle_ss7_unblock_linkset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +static char *handle_ss7_show_calls(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { int linkset; - int i; + switch (cmd) { case CLI_INIT: - e->command = "ss7 unblock linkset"; + e->command = "ss7 show calls"; e->usage = - "Usage: ss7 unblock linkset <linkset number>\n" - " Sends a remote unblocking request for all CICs on the specified linkset\n"; + "Usage: ss7 show calls <linkset>\n" + " Show SS7 calls on the specified linkset\n"; return NULL; case CLI_GENERATE: return NULL; } - if (a->argc == 4) + if (a->argc == 4) { linkset = atoi(a->argv[3]); - else + } else { return CLI_SHOWUSAGE; + } if ((linkset < 1) || (linkset > NUM_SPANS)) { ast_cli(a->fd, "Invalid linkset %s. Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS); @@ -16273,15 +16467,164 @@ static char *handle_ss7_unblock_linkset(struct ast_cli_entry *e, int cmd, struct return CLI_SUCCESS; } - for (i = 0; i < linksets[linkset-1].ss7.numchans; i++) { - ast_cli(a->fd, "Sending remote unblock request on CIC %d\n", linksets[linkset-1].ss7.pvts[i]->cic); - ast_mutex_lock(&linksets[linkset-1].ss7.lock); - isup_ubl(linksets[linkset-1].ss7.ss7, linksets[linkset-1].ss7.pvts[i]->cic, linksets[linkset-1].ss7.pvts[i]->dpc); - ast_mutex_unlock(&linksets[linkset-1].ss7.lock); + ast_mutex_lock(&linksets[linkset-1].ss7.lock); + isup_show_calls(linksets[linkset-1].ss7.ss7, &ast_cli, a->fd); + ast_mutex_unlock(&linksets[linkset-1].ss7.lock); + + return CLI_SUCCESS; +} +#endif /* defined(HAVE_SS7) */ + +#if defined(HAVE_SS7) +static char *handle_ss7_reset_cic(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + int linkset, cic, res; + unsigned int dpc; + + switch (cmd) { + case CLI_INIT: + e->command = "ss7 reset cic"; + e->usage = + "Usage: ss7 reset cic <linkset> <dpc> <CIC>\n" + " Send a RSC for the given CIC on the specified linkset\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc == 6) { + linkset = atoi(a->argv[3]); + } else { + return CLI_SHOWUSAGE; + } + + if ((linkset < 1) || (linkset > NUM_SPANS)) { + ast_cli(a->fd, "Invalid linkset %s. Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS); + return CLI_SUCCESS; + } + + if (!linksets[linkset-1].ss7.ss7) { + ast_cli(a->fd, "No SS7 running on linkset %d\n", linkset); + return CLI_SUCCESS; + } + + cic = atoi(a->argv[5]); + + if (cic < 1) { + ast_cli(a->fd, "Invalid CIC specified!\n"); + return CLI_SUCCESS; + } + + dpc = atoi(a->argv[4]); + if (dpc < 1) { + ast_cli(a->fd, "Invalid DPC specified!\n"); + return CLI_SUCCESS; + } + + res = sig_ss7_reset_cic(&linksets[linkset-1].ss7, cic, dpc); + + ast_cli(a->fd, "%s RSC for linkset %d on CIC %d DPC %d\n", res ? "Sent" : "Failed", linkset, cic, dpc); + + return CLI_SUCCESS; +} +#endif /* defined(HAVE_SS7) */ + +#if defined(HAVE_SS7) +static char *handle_ss7_net_mng(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + int linkset; + unsigned int slc; + unsigned int arg = 0; + const char *res; + + switch (cmd) { + case CLI_INIT: + e->command = "ss7 mtp3"; + e->usage = + "Usage: ss7 mtp3 <linkset> <slc> coo|coa|cbd|cba|eco|eca|tfp|tfa|lin|lun|lia|lua|lid|lfu <arg>\n" + " Send a NET MNG message\n" + " WARNING!!! WARNING!!! We are not a STP, just for testing/development purposes\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc < 5) { + return CLI_SHOWUSAGE; } + linkset = atoi(a->argv[2]); + if ((linkset < 1) || (linkset > NUM_SPANS)) { + ast_cli(a->fd, "Invalid linkset %s. Should be a number %d to %d\n", a->argv[2], 1, NUM_SPANS); + return CLI_SUCCESS; + } + if (!linksets[linkset-1].ss7.ss7) { + ast_cli(a->fd, "No SS7 running on linkset %d\n", linkset); + return CLI_SUCCESS; + } + + slc = atoi(a->argv[3]); + + if (a->argc == 6) { + arg = atoi(a->argv[5]); + } + + ast_mutex_lock(&linksets[linkset-1].ss7.lock); + res = mtp3_net_mng(linksets[linkset-1].ss7.ss7, slc, a->argv[4], arg); + ast_mutex_unlock(&linksets[linkset-1].ss7.lock); + /* Break poll on the linkset so it sends our messages */ - pthread_kill(linksets[linkset-1].ss7.master, SIGURG); + if (linksets[linkset-1].ss7.master != AST_PTHREADT_NULL) { + pthread_kill(linksets[linkset-1].ss7.master, SIGURG); + } + + ast_cli(a->fd, "%s", res); + + return CLI_SUCCESS; +} +#endif /* defined(HAVE_SS7) */ + +#if defined(HAVE_SS7) +static char *handle_ss7_mtp3_restart(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + int linkset; + unsigned int slc = 0; + + switch (cmd) { + case CLI_INIT: + e->command = "ss7 restart mtp3"; + e->usage = + "Usage: ss7 restart mtp3 <linkset> <slc>\n" + " Restart link\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc < 5) { + return CLI_SHOWUSAGE; + } + + linkset = atoi(a->argv[3]); + if ((linkset < 1) || (linkset > NUM_SPANS)) { + ast_cli(a->fd, "Invalid linkset %s. Should be a number %d to %d\n", a->argv[2], 1, NUM_SPANS); + return CLI_SUCCESS; + } + if (!linksets[linkset-1].ss7.ss7) { + ast_cli(a->fd, "No SS7 running on linkset %d\n", linkset); + return CLI_SUCCESS; + } + + slc = atoi(a->argv[4]); + + ast_mutex_lock(&linksets[linkset-1].ss7.lock); + mtp3_init_restart(linksets[linkset-1].ss7.ss7, slc); + ast_mutex_unlock(&linksets[linkset-1].ss7.lock); + + /* Break poll on the linkset so it sends our messages */ + if (linksets[linkset-1].ss7.master != AST_PTHREADT_NULL) { + pthread_kill(linksets[linkset-1].ss7.master, SIGURG); + } return CLI_SUCCESS; } @@ -16303,8 +16646,10 @@ static char *handle_ss7_show_linkset(struct ast_cli_entry *e, int cmd, struct as return NULL; } - if (a->argc < 4) + if (a->argc < 4) { return CLI_SHOWUSAGE; + } + linkset = atoi(a->argv[3]); if ((linkset < 1) || (linkset > NUM_SPANS)) { ast_cli(a->fd, "Invalid linkset %s. Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS); @@ -16316,7 +16661,16 @@ static char *handle_ss7_show_linkset(struct ast_cli_entry *e, int cmd, struct as return CLI_SUCCESS; } + ast_cli(a->fd, "SS7 flags: 0x%x\n", ss7->flags); ast_cli(a->fd, "SS7 linkset %d status: %s\n", linkset, (ss7->state == LINKSET_STATE_UP) ? "Up" : "Down"); + ast_cli(a->fd, "SS7 calling nai: %i\n", ss7->calling_nai); + ast_cli(a->fd, "SS7 called nai: %i\n", ss7->called_nai); + ast_cli(a->fd, "SS7 nationalprefix: %s\n", ss7->nationalprefix); + ast_cli(a->fd, "SS7 internationalprefix: %s\n", ss7->internationalprefix); + ast_cli(a->fd, "SS7 unknownprefix: %s\n", ss7->unknownprefix); + ast_cli(a->fd, "SS7 networkroutedprefix: %s\n", ss7->networkroutedprefix); + ast_cli(a->fd, "SS7 subscriberprefix: %s\n", ss7->subscriberprefix); + ss7_show_linkset(ss7->ss7, &ast_cli, a->fd); return CLI_SUCCESS; } @@ -16338,8 +16692,9 @@ static char *handle_ss7_show_channels(struct ast_cli_entry *e, int cmd, struct a return NULL; } - if (a->argc != 3) + if (a->argc != 3) { return CLI_SHOWUSAGE; + } sig_ss7_cli_show_channels_header(a->fd); for (linkset = 0; linkset < NUM_SPANS; ++linkset) { @@ -16351,6 +16706,110 @@ static char *handle_ss7_show_channels(struct ast_cli_entry *e, int cmd, struct a } #endif /* defined(HAVE_SS7) */ +#if defined(HAVE_SS7) +static char *handle_ss7_show_cics(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ +#define FORMAT "%5s %5s %6s %12s %-12s\n" +#define FORMAT2 "%5i %5i %6i %12s %-12s\n" + int i, linkset, dpc = 0; + struct sig_ss7_linkset *ss7; + char *state; + char blocking[12]; + + switch (cmd) { + case CLI_INIT: + e->command = "ss7 show cics"; + e->usage = + "Usage: ss7 show cics <linkset> [dpc]\n" + " Shows the cics of an SS7 linkset.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc < 4 || a->argc > 5) { + return CLI_SHOWUSAGE; + } + + linkset = atoi(a->argv[3]); + + if ((linkset < 1) || (linkset > NUM_SPANS)) { + ast_cli(a->fd, "Invalid linkset %s. Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS); + return CLI_SUCCESS; + } + + if (!linksets[linkset-1].ss7.ss7) { + ast_cli(a->fd, "No SS7 running on linkset %d\n", linkset); + return CLI_SUCCESS; + } + ss7 = &linksets[linkset-1].ss7; + + if (a->argc == 5) { + dpc = atoi(a->argv[4]); + if (dpc < 1) { + ast_cli(a->fd, "Invalid DPC specified!\n"); + return CLI_SUCCESS; + } + } + + ast_cli(a->fd, FORMAT, "CIC", "DPC", "DAHDI", "STATE", "BLOCKING"); + + for (i = 0; i < ss7->numchans; i++) { + if (!dpc || (ss7->pvts[i] && ss7->pvts[i]->dpc == dpc)) { + struct dahdi_pvt *p = ss7->pvts[i]->chan_pvt; + + if (ss7->pvts[i]->owner) { + state = "Used"; + } else if (ss7->pvts[i]->ss7call) { + state = "Pending"; + } else if (!p->inservice) { + state = "NotInServ"; + } else { + state = "Idle"; + } + + if (p->locallyblocked) { + strcpy(blocking, "L:"); + if (p->locallyblocked & SS7_BLOCKED_MAINTENANCE) { + strcat(blocking, "M"); + } else { + strcat(blocking, " "); + } + + if (p->locallyblocked & SS7_BLOCKED_HARDWARE) { + strcat(blocking, "H"); + } else { + strcat(blocking, " "); + } + } else { + strcpy(blocking, " "); + } + + if (p->remotelyblocked) { + strcat(blocking, " R:"); + if (p->remotelyblocked & SS7_BLOCKED_MAINTENANCE) { + strcat(blocking, "M"); + } else { + strcat(blocking, " "); + } + + if (p->remotelyblocked & SS7_BLOCKED_HARDWARE) { + strcat(blocking, "H"); + } else { + strcat(blocking, " "); + } + } + + ast_cli(a->fd, FORMAT2, ss7->pvts[i]->cic, ss7->pvts[i]->dpc, ss7->pvts[i]->channel, state, blocking); + } + } + + return CLI_SUCCESS; +#undef FORMAT +#undef FORMAT2 +} +#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) { @@ -16374,12 +16833,17 @@ static char *handle_ss7_version(struct ast_cli_entry *e, int cmd, struct ast_cli #if defined(HAVE_SS7) static struct ast_cli_entry dahdi_ss7_cli[] = { AST_CLI_DEFINE(handle_ss7_debug, "Enables SS7 debugging on a linkset"), - AST_CLI_DEFINE(handle_ss7_block_cic, "Blocks the given CIC"), - AST_CLI_DEFINE(handle_ss7_unblock_cic, "Unblocks the given CIC"), - 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_cic_blocking, "Blocks/Unblocks the given CIC"), + AST_CLI_DEFINE(handle_ss7_linkset_mng, "Resets/Blocks/Unblocks all CICs on a linkset"), + AST_CLI_DEFINE(handle_ss7_group_blocking, "Blocks/Unblocks the given CIC range"), + AST_CLI_DEFINE(handle_ss7_reset_cic, "Resets the given CIC"), + AST_CLI_DEFINE(handle_ss7_group_reset, "Resets the given CIC range"), + AST_CLI_DEFINE(handle_ss7_mtp3_restart, "Restart a link"), + AST_CLI_DEFINE(handle_ss7_net_mng, "Send an NET MNG message"), 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_show_calls, "Show ss7 calls"), + AST_CLI_DEFINE(handle_ss7_show_cics, "Show cics on a linkset"), AST_CLI_DEFINE(handle_ss7_version, "Displays libss7 version"), }; #endif /* defined(HAVE_SS7) */ @@ -16581,6 +17045,10 @@ static int __unload_module(void) for (j = 0; j < SIG_SS7_NUM_DCHANS; j++) { dahdi_close_ss7_fd(&(linksets[i]), j); } + if (linksets[i].ss7.ss7) { + ss7_destroy(linksets[i].ss7.ss7); + linksets[i].ss7.ss7 = NULL; + } } #endif /* defined(HAVE_SS7) */ ast_cond_destroy(&ss_thread_complete); @@ -16856,7 +17324,7 @@ static void parse_busy_pattern(struct ast_variable *v, struct ast_dsp_busy_patte for (; ;) { /* Scans the string for the next value in the pattern. If none, it checks to see if any have been entered so far. */ - if(!sscanf(v->value, "%30d", &norval) && count_pattern == 0) { + if (!sscanf(v->value, "%30d", &norval) && count_pattern == 0) { ast_log(LOG_ERROR, "busypattern= expects either busypattern=tonelength,quietlength or busypattern=t1length, q1length, t2length, q2length at line %d.\n", v->lineno); break; } @@ -17220,23 +17688,23 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct #else /* Default is fsk, to turn it off you must specify nofsk */ memset(&confp->chan.mwisend_setting, 0, sizeof(confp->chan.mwisend_setting)); - if (strcasestr(v->value, "nofsk")) { /* NoFSK */ + if (strcasestr(v->value, "nofsk")) { /* NoFSK */ confp->chan.mwisend_fsk = 0; } else { /* Default FSK */ confp->chan.mwisend_fsk = 1; } - if (strcasestr(v->value, "rpas")) { /* Ring Pulse Alert Signal, normally followed by FSK */ + if (strcasestr(v->value, "rpas")) { /* Ring Pulse Alert Signal, normally followed by FSK */ confp->chan.mwisend_rpas = 1; } else { confp->chan.mwisend_rpas = 0; } - if (strcasestr(v->value, "lrev")) { /* Line Reversal */ + if (strcasestr(v->value, "lrev")) { /* Line Reversal */ confp->chan.mwisend_setting.vmwi_type |= DAHDI_VMWI_LREV; } - if (strcasestr(v->value, "hvdc")) { /* HV 90VDC */ + if (strcasestr(v->value, "hvdc")) { /* HV 90VDC */ confp->chan.mwisend_setting.vmwi_type |= DAHDI_VMWI_HVDC; } - if ( (strcasestr(v->value, "neon")) || (strcasestr(v->value, "hvac")) ){ /* 90V DC pulses */ + if ( (strcasestr(v->value, "neon")) || (strcasestr(v->value, "hvac")) ) { /* 90V DC pulses */ confp->chan.mwisend_setting.vmwi_type |= DAHDI_VMWI_HVAC; } #endif @@ -17744,8 +18212,11 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct cur_ss7type = SS7_ITU; } else if (!strcasecmp(v->value, "ansi")) { cur_ss7type = SS7_ANSI; - } else + } else { ast_log(LOG_WARNING, "'%s' is an unknown ss7 switch type at line %d.!\n", v->value, v->lineno); + } + } else if (!strcasecmp(v->name, "slc")) { + cur_slc = atoi(v->value); } else if (!strcasecmp(v->name, "linkset")) { cur_linkset = atoi(v->value); } else if (!strcasecmp(v->name, "pointcode")) { @@ -17757,16 +18228,17 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct } else if (!strcasecmp(v->name, "cicbeginswith")) { cur_cicbeginswith = atoi(v->value); } else if (!strcasecmp(v->name, "networkindicator")) { - if (!strcasecmp(v->value, "national")) + if (!strcasecmp(v->value, "national")) { cur_networkindicator = SS7_NI_NAT; - else if (!strcasecmp(v->value, "national_spare")) + } else if (!strcasecmp(v->value, "national_spare")) { cur_networkindicator = SS7_NI_NAT_SPARE; - else if (!strcasecmp(v->value, "international")) + } else if (!strcasecmp(v->value, "international")) { cur_networkindicator = SS7_NI_INT; - else if (!strcasecmp(v->value, "international_spare")) + } else if (!strcasecmp(v->value, "international_spare")) { cur_networkindicator = SS7_NI_INT_SPARE; - else + } else { cur_networkindicator = -1; + } } else if (!strcasecmp(v->name, "ss7_internationalprefix")) { ast_copy_string(confp->ss7.ss7.internationalprefix, v->value, sizeof(confp->ss7.ss7.internationalprefix)); } else if (!strcasecmp(v->name, "ss7_nationalprefix")) { @@ -17775,6 +18247,8 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct ast_copy_string(confp->ss7.ss7.subscriberprefix, v->value, sizeof(confp->ss7.ss7.subscriberprefix)); } else if (!strcasecmp(v->name, "ss7_unknownprefix")) { ast_copy_string(confp->ss7.ss7.unknownprefix, v->value, sizeof(confp->ss7.ss7.unknownprefix)); + } else if (!strcasecmp(v->name, "ss7_networkroutedprefix")) { + ast_copy_string(confp->ss7.ss7.networkroutedprefix, v->value, sizeof(confp->ss7.ss7.networkroutedprefix)); } else if (!strcasecmp(v->name, "ss7_called_nai")) { if (!strcasecmp(v->value, "national")) { confp->ss7.ss7.called_nai = SS7_NAI_NATIONAL; @@ -17807,9 +18281,9 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct int sigchan, res; sigchan = atoi(v->value); res = linkset_addsigchan(sigchan); - if (res < 0) + if (res < 0) { return -1; - + } } else if (!strcasecmp(v->name, "ss7_explicitacm")) { struct dahdi_ss7 *link; link = ss7_resolve_linkset(cur_linkset); @@ -17817,8 +18291,148 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct ast_log(LOG_ERROR, "Invalid linkset number. Must be between 1 and %d\n", NUM_SPANS + 1); return -1; } - if (ast_true(v->value)) + if (ast_true(v->value)) { link->ss7.flags |= LINKSET_FLAG_EXPLICITACM; + } else { + link->ss7.flags &= ~LINKSET_FLAG_EXPLICITACM; + } + } else if (!strcasecmp(v->name, "ss7_autoacm")) { + struct dahdi_ss7 *link; + link = ss7_resolve_linkset(cur_linkset); + if (!link) { + ast_log(LOG_ERROR, "Invalid linkset number. Must be between 1 and %d\n", NUM_SPANS + 1); + return -1; + } + if (ast_true(v->value)) { + link->ss7.flags |= LINKSET_FLAG_AUTOACM; + } else { + link->ss7.flags &= ~LINKSET_FLAG_AUTOACM; + } + } else if (!strcasecmp(v->name, "ss7_initialhwblo")) { + struct dahdi_ss7 *link; + link = ss7_resolve_linkset(cur_linkset); + if (!link) { + ast_log(LOG_ERROR, "Invalid linkset number. Must be between 1 and %d\n", NUM_SPANS + 1); + return -1; + } + if (ast_true(v->value)) { + link->ss7.flags |= LINKSET_FLAG_INITIALHWBLO; + } else { + link->ss7.flags &= ~LINKSET_FLAG_INITIALHWBLO; + } + } else if (!strcasecmp(v->name, "ss7_use_echocontrol")) { + struct dahdi_ss7 *link; + link = ss7_resolve_linkset(cur_linkset); + if (!link) { + ast_log(LOG_ERROR, "Invalid linkset number. Must be between 1 and %d\n", NUM_SPANS + 1); + return -1; + } + if (ast_true(v->value)) { + link->ss7.flags |= LINKSET_FLAG_USEECHOCONTROL; + } else { + link->ss7.flags &= ~LINKSET_FLAG_USEECHOCONTROL; + } + } else if (!strcasecmp(v->name, "ss7_default_echocontrol")) { + struct dahdi_ss7 *link; + link = ss7_resolve_linkset(cur_linkset); + if (!link) { + ast_log(LOG_ERROR, "Invalid linkset number. Must be between 1 and %d\n", NUM_SPANS + 1); + return -1; + } + if (ast_true(v->value)) { + link->ss7.flags |= LINKSET_FLAG_DEFAULTECHOCONTROL; + } else { + link->ss7.flags &= ~LINKSET_FLAG_DEFAULTECHOCONTROL; + } + } else if (!strncasecmp(v->name, "isup_timer.", 11)) { + struct dahdi_ss7 *link; + link = ss7_resolve_linkset(cur_linkset); + if (!link) { + ast_log(LOG_ERROR, "Invalid linkset number. Must be between 1 and %d\n", NUM_SPANS + 1); + return -1; + } + if (!link->ss7.ss7) { + ast_log(LOG_ERROR, "Please specify isup timers after sigchan!\n"); + } else if (!ss7_set_isup_timer(link->ss7.ss7, strstr(v->name, ".") + 1, atoi(v->value))) { + ast_log(LOG_ERROR, "Invalid isup timer %s\n", v->name); + } + } else if (!strncasecmp(v->name, "mtp3_timer.", 11)) { + struct dahdi_ss7 *link; + link = ss7_resolve_linkset(cur_linkset); + if (!link) { + ast_log(LOG_ERROR, "Invalid linkset number. Must be between 1 and %d\n", NUM_SPANS + 1); + return -1; + } + if (!link->ss7.ss7) { + ast_log(LOG_ERROR, "Please specify mtp3 timers after sigchan!\n"); + } else if (!ss7_set_mtp3_timer(link->ss7.ss7, strstr(v->name, ".") + 1, atoi(v->value))) { + ast_log(LOG_ERROR, "Invalid mtp3 timer %s\n", v->name); + } + } else if (!strcasecmp(v->name, "inr_if_no_calling")) { + struct dahdi_ss7 *link; + link = ss7_resolve_linkset(cur_linkset); + if (!link) { + ast_log(LOG_ERROR, "Invalid linkset number. Must be between 1 and %d\n", NUM_SPANS + 1); + return -1; + } + if (!link->ss7.ss7) { + ast_log(LOG_ERROR, "Please specify inr_if_no_calling after sigchan!\n"); + } else if (ast_true(v->value)) { + ss7_set_flags(link->ss7.ss7, SS7_INR_IF_NO_CALLING); + } else { + ss7_clear_flags(link->ss7.ss7, SS7_INR_IF_NO_CALLING); + } + } else if (!strcasecmp(v->name, "non_isdn_access")) { + struct dahdi_ss7 *link; + link = ss7_resolve_linkset(cur_linkset); + if (!link) { + ast_log(LOG_ERROR, "Invalid linkset number. Must be between 1 and %d\n", NUM_SPANS + 1); + return -1; + } + if (!link->ss7.ss7) { + ast_log(LOG_ERROR, "Please specify non_isdn_access after sigchan!\n"); + } else if (ast_true(v->value)) { + ss7_clear_flags(link->ss7.ss7, SS7_ISDN_ACCESS_INDICATOR); + } else { + ss7_set_flags(link->ss7.ss7, SS7_ISDN_ACCESS_INDICATOR); + } + } else if (!strcasecmp(v->name, "sls_shift")) { + struct dahdi_ss7 *link; + int sls_shift = atoi(v->value); + + if (sls_shift < 0 || sls_shift > 7) { + ast_log(LOG_ERROR, "Invalid sls_shift value. Must be between 0 and 7\n"); + return -1; + } + + link = ss7_resolve_linkset(cur_linkset); + if (!link) { + ast_log(LOG_ERROR, "Invalid linkset number. Must be between 1 and %d\n", NUM_SPANS + 1); + return -1; + } + if (!link->ss7.ss7) { + ast_log(LOG_ERROR, "Please specify sls_shift after sigchan!\n"); + } else { + ss7_set_sls_shift(link->ss7.ss7, sls_shift); + } + } else if (!strcasecmp(v->name, "cause_location")) { + struct dahdi_ss7 *link; + int cause_location = atoi(v->value); + + if (cause_location < 0 || cause_location > 15) { + ast_log(LOG_ERROR, "Invalid cause_location value. Must be between 0 and 15\n"); + return -1; + } + link = ss7_resolve_linkset(cur_linkset); + if (!link) { + ast_log(LOG_ERROR, "Invalid linkset number. Must be between 1 and %d\n", NUM_SPANS + 1); + return -1; + } + if (!link->ss7.ss7) { + ast_log(LOG_ERROR, "Please specify cause_location after sigchan!\n"); + } else { + ss7_set_cause_location(link->ss7.ss7, cause_location); + } #endif /* defined(HAVE_SS7) */ #ifdef HAVE_OPENR2 } else if (!strcasecmp(v->name, "mfcr2_advanced_protocol_file")) { @@ -17881,12 +18495,12 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct confp->mfcr2.call_files = ast_true(v->value) ? 1 : 0; } else if (!strcasecmp(v->name, "mfcr2_max_ani")) { confp->mfcr2.max_ani = atoi(v->value); - if (confp->mfcr2.max_ani >= AST_MAX_EXTENSION){ + if (confp->mfcr2.max_ani >= AST_MAX_EXTENSION) { confp->mfcr2.max_ani = AST_MAX_EXTENSION - 1; } } else if (!strcasecmp(v->name, "mfcr2_max_dnis")) { confp->mfcr2.max_dnis = atoi(v->value); - if (confp->mfcr2.max_dnis >= AST_MAX_EXTENSION){ + if (confp->mfcr2.max_dnis >= AST_MAX_EXTENSION) { confp->mfcr2.max_dnis = AST_MAX_EXTENSION - 1; } } else if (!strcasecmp(v->name, "mfcr2_category")) { @@ -18677,6 +19291,9 @@ static int load_module(void) } ss7_set_error(dahdi_ss7_error); ss7_set_message(dahdi_ss7_message); + ss7_set_hangup(sig_ss7_cb_hangup); + ss7_set_notinservice(sig_ss7_cb_notinservice); + ss7_set_call_null(sig_ss7_cb_call_null); #endif /* defined(HAVE_SS7) */ res = setup_dahdi(0); /* Make sure we can register our DAHDI channel type */ diff --git a/channels/chan_dahdi.h b/channels/chan_dahdi.h index d302bd7f7102e00f8cbcc1ec57a7c13c556b09ca..e4a689a1abb538bb7e9ea2ac93d08fd62cda82fe 100644 --- a/channels/chan_dahdi.h +++ b/channels/chan_dahdi.h @@ -389,19 +389,27 @@ struct dahdi_pvt { unsigned int mwisendactive:1; /*! * \brief TRUE if channel is out of reset and ready - * \note Set but not used. + * \note Used by SS7. Otherwise set but not used. */ unsigned int inservice:1; /*! - * \brief TRUE if the channel is locally blocked. + * \brief Bitmask for the channel being locally blocked. * \note Applies to SS7 and MFCR2 channels. + * \note For MFCR2 only the first bit is used - TRUE if blocked + * \note For SS7 two bits are used + * \note Bit 0 - TRUE if maintenance blocked + * \note Bit 1 - TRUE if hardware blocked */ - unsigned int locallyblocked:1; + unsigned int locallyblocked:2; /*! - * \brief TRUE if the channel is remotely blocked. + * \brief Bitmask for the channel being remotely blocked. 1 maintenance, 2 blocked in hardware. * \note Applies to SS7 and MFCR2 channels. + * \note For MFCR2 only the first bit is used - TRUE if blocked + * \note For SS7 two bits are used + * \note Bit 0 - TRUE if maintenance blocked + * \note Bit 1 - TRUE if hardware blocked */ - unsigned int remotelyblocked:1; + unsigned int remotelyblocked:2; /*! * \brief TRUE if the channel alarms will be managed also as Span ones * \note Applies to all channels diff --git a/channels/sig_ss7.c b/channels/sig_ss7.c index 2e391891213ae27367067b134837b48ff5251839..a7df9e9e08b59a14495eb9c714d09266853385ac 100644 --- a/channels/sig_ss7.c +++ b/channels/sig_ss7.c @@ -41,11 +41,14 @@ #include "asterisk/causes.h" #include "asterisk/musiconhold.h" #include "asterisk/cli.h" +#include "asterisk/callerid.h" #include "asterisk/transcap.h" #include "asterisk/stasis_channels.h" #include "sig_ss7.h" -#if defined(LIBSS7_ABI_COMPATIBILITY) +#if !defined(LIBSS7_ABI_COMPATIBILITY) +#error "Upgrade your libss7" +#elif LIBSS7_ABI_COMPATIBILITY != 2 #error "Your installed libss7 is not compatible" #endif @@ -68,8 +71,6 @@ static const char *sig_ss7_call_level2str(enum sig_ss7_call_level level) return "Alerting"; case SIG_SS7_CALL_LEVEL_CONNECT: return "Connect"; - case SIG_SS7_CALL_LEVEL_GLARE: - return "Glare"; } return "Unknown"; } @@ -132,24 +133,35 @@ static void sig_ss7_set_outgoing(struct sig_ss7_chan *p, int is_outgoing) static void sig_ss7_set_inservice(struct sig_ss7_chan *p, int is_inservice) { + p->inservice = is_inservice; if (sig_ss7_callbacks.set_inservice) { sig_ss7_callbacks.set_inservice(p->chan_pvt, is_inservice); } } -static void sig_ss7_set_locallyblocked(struct sig_ss7_chan *p, int is_blocked) +static void sig_ss7_set_locallyblocked(struct sig_ss7_chan *p, int is_blocked, int type) { - p->locallyblocked = is_blocked; + if (is_blocked) { + p->locallyblocked |= type; + } else { + p->locallyblocked &= ~type; + } + if (sig_ss7_callbacks.set_locallyblocked) { - sig_ss7_callbacks.set_locallyblocked(p->chan_pvt, is_blocked); + sig_ss7_callbacks.set_locallyblocked(p->chan_pvt, p->locallyblocked); } } -static void sig_ss7_set_remotelyblocked(struct sig_ss7_chan *p, int is_blocked) +static void sig_ss7_set_remotelyblocked(struct sig_ss7_chan *p, int is_blocked, int type) { - p->remotelyblocked = is_blocked; + if (is_blocked) { + p->remotelyblocked |= type; + } else { + p->remotelyblocked &= ~type; + } + if (sig_ss7_callbacks.set_remotelyblocked) { - sig_ss7_callbacks.set_remotelyblocked(p->chan_pvt, is_blocked); + sig_ss7_callbacks.set_remotelyblocked(p->chan_pvt, p->remotelyblocked); } } @@ -277,7 +289,13 @@ static struct ast_channel *sig_ss7_new_ast_channel(struct sig_ss7_chan *p, int s if (!p->owner) { p->owner = ast; } - p->alreadyhungup = 0; + + if (p->outgoing) { + p->do_hangup = SS7_HANGUP_FREE_CALL; + } else { + p->do_hangup = SS7_HANGUP_SEND_REL; + } + ast_channel_transfercapability_set(ast, transfercapability); pbx_builtin_setvar_helper(ast, "TRANSFERCAPABILITY", ast_transfercapability2str(transfercapability)); @@ -295,6 +313,14 @@ static void sig_ss7_handle_link_exception(struct sig_ss7_linkset *linkset, int w } } +static struct sig_ss7_linkset *sig_ss7_find_linkset(struct ss7 *ss7) +{ + if (sig_ss7_callbacks.find_linkset) { + return sig_ss7_callbacks.find_linkset(ss7); + } + return NULL; +} + /*! * \internal * \brief Determine if a private channel structure is available. @@ -305,7 +331,7 @@ static void sig_ss7_handle_link_exception(struct sig_ss7_linkset *linkset, int w */ static int sig_ss7_is_chan_available(struct sig_ss7_chan *pvt) { - if (!pvt->inalarm && !pvt->owner && !pvt->ss7call + if (pvt->inservice && !pvt->inalarm && !pvt->owner && !pvt->ss7call && pvt->call_level == SIG_SS7_CALL_LEVEL_IDLE && !pvt->locallyblocked && !pvt->remotelyblocked) { return 1; @@ -398,7 +424,7 @@ static void sig_ss7_queue_control(struct sig_ss7_linkset *ss7, int chanpos, int /*! * \internal * \brief Queue a PVT_CAUSE_CODE frame onto the owner channel. - * \since 11 + * \since 11.0 * * \param owner Owner channel of the pvt. * \param cause String describing the cause to be placed into the frame. @@ -425,7 +451,6 @@ static void ss7_queue_pvt_cause_data(struct ast_channel *owner, const char *caus /*! - * \internal * \brief Find the channel position by CIC/DPC. * * \param linkset SS7 linkset control structure. @@ -435,7 +460,7 @@ static void ss7_queue_pvt_cause_data(struct ast_channel *owner, const char *caus * \retval chanpos on success. * \retval -1 on error. */ -static int ss7_find_cic(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc) +int sig_ss7_find_cic(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc) { int i; int winner = -1; @@ -464,45 +489,206 @@ static int ss7_find_cic_gripe(struct sig_ss7_linkset *linkset, int cic, unsigned { int chanpos; - chanpos = ss7_find_cic(linkset, cic, dpc); + chanpos = sig_ss7_find_cic(linkset, cic, dpc); if (chanpos < 0) { - ast_log(LOG_WARNING, "Linkset %d: SS7 %s requested unconfigured CIC/DPC %d/%d.\n", + ast_log(LOG_WARNING, "Linkset %d: SS7 %s requested on 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) +static struct sig_ss7_chan *ss7_find_pvt(struct ss7 *ss7, int cic, unsigned int dpc) +{ + int chanpos; + struct sig_ss7_linkset *winner; + + winner = sig_ss7_find_linkset(ss7); + if (winner && (chanpos = sig_ss7_find_cic(winner, cic, dpc)) > -1) { + return winner->pvts[chanpos]; + } + return NULL; +} + +int sig_ss7_cb_hangup(struct ss7 *ss7, int cic, unsigned int dpc, int cause, int do_hangup) +{ + struct sig_ss7_chan *p; + int res; + + if (!(p = ss7_find_pvt(ss7, cic, dpc))) { + return SS7_CIC_NOT_EXISTS; + } + + sig_ss7_lock_private(p); + if (p->owner) { + ast_channel_hangupcause_set(p->owner, cause); + ast_channel_softhangup_internal_flag_add(p->owner, AST_SOFTHANGUP_DEV); + p->do_hangup = do_hangup; + res = SS7_CIC_USED; + } else { + res = SS7_CIC_IDLE; + } + sig_ss7_unlock_private(p); + + return res; +} + +void sig_ss7_cb_call_null(struct ss7 *ss7, struct isup_call *call, int lock) +{ + int i; + struct sig_ss7_linkset *winner; + + winner = sig_ss7_find_linkset(ss7); + if (!winner) { + return; + } + for (i = 0; i < winner->numchans; i++) { + if (winner->pvts[i] && (winner->pvts[i]->ss7call == call)) { + if (lock) { + sig_ss7_lock_private(winner->pvts[i]); + } + winner->pvts[i]->ss7call = NULL; + if (winner->pvts[i]->owner) { + ast_channel_hangupcause_set(winner->pvts[i]->owner, AST_CAUSE_NORMAL_TEMPORARY_FAILURE); + ast_channel_softhangup_internal_flag_add(winner->pvts[i]->owner, AST_SOFTHANGUP_DEV); + } + if (lock) { + sig_ss7_unlock_private(winner->pvts[i]); + } + ast_log(LOG_WARNING, "libss7 asked set ss7 call to NULL on CIC %d DPC %d\n", winner->pvts[i]->cic, winner->pvts[i]->dpc); + } + } +} + +void sig_ss7_cb_notinservice(struct ss7 *ss7, int cic, unsigned int dpc) +{ + struct sig_ss7_chan *p; + + if (!(p = ss7_find_pvt(ss7, cic, dpc))) { + return; + } + + sig_ss7_lock_private(p); + sig_ss7_set_inservice(p, 0); + sig_ss7_unlock_private(p); +} + +/*! + * \internal + * \brief Check if CICs in a range belong to the linkset for a given DPC. + * \since 11.0 + * + * \param linkset SS7 linkset control structure. + * \param startcic Circuit Identification Code to start from + * \param endcic Circuit Identification Code to search up-to + * \param dpc Destination Point Code + * \param state Array containing the status of the search + * + * \retval Nothing. + */ +static void ss7_check_range(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc, unsigned char *state) +{ + int cic; + + for (cic = startcic; cic <= endcic; cic++) { + if (state[cic - startcic] && sig_ss7_find_cic(linkset, cic, dpc) == -1) { + state[cic - startcic] = 0; + } + } +} + +static int ss7_match_range(struct sig_ss7_chan *pvt, int startcic, int endcic, unsigned int dpc) +{ + if (pvt && pvt->dpc == dpc && pvt->cic >= startcic && pvt->cic <= endcic) { + return 1; + } + + return 0; +} + +/*! + * \internal + * \brief Check if a range is defined for the given DPC. + * \since 11.0 + * + * \param linkset SS7 linkset control structure. + * \param startcic Start CIC of the range to clear. + * \param endcic End CIC of the range to clear. + * \param dpc Destination Point Code. + * + * \note Assumes the linkset->lock is already obtained. + * + * \return TRUE if all CICs in the range are present + */ +int sig_ss7_find_cic_range(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc) +{ + int i, found = 0; + + for (i = 0; i < linkset->numchans; i++) { + if (ss7_match_range(linkset->pvts[i], startcic, endcic, dpc)) { + found++; + } + } + + if (found == endcic - startcic + 1) { + return 1; + } + + return 0; +} + +static void ss7_handle_cqm(struct sig_ss7_linkset *linkset, ss7_event *e) { unsigned char status[32]; struct sig_ss7_chan *p = NULL; - int i, offset; + int i; + int offset; + int chanpos; + memset(status, 0, sizeof(status)); 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 (ss7_match_range(linkset->pvts[i], e->cqm.startcic, e->cqm.endcic, e->cqm.opc)) { p = linkset->pvts[i]; - offset = p->cic - startcic; + sig_ss7_lock_private(p); + offset = p->cic - e->cqm.startcic; status[offset] = 0; - if (p->locallyblocked) + if (p->locallyblocked) { status[offset] |= (1 << 0) | (1 << 4); - if (p->remotelyblocked) + } + if (p->remotelyblocked) { status[offset] |= (1 << 1) | (1 << 5); + } if (p->ss7call) { - if (p->outgoing) + if (p->outgoing) { status[offset] |= (1 << 3); - else + } else { status[offset] |= (1 << 2); - } else + } + } else { status[offset] |= 0x3 << 2; + } + sig_ss7_unlock_private(p); } } - if (p) - isup_cqr(linkset->ss7, startcic, endcic, dpc, status); - else + if (p) { + isup_cqr(linkset->ss7, e->cqm.startcic, e->cqm.endcic, e->cqm.opc, status); + } else { ast_log(LOG_WARNING, "Could not find any equipped circuits within CQM CICs\n"); + } + chanpos = sig_ss7_find_cic(linkset, e->cqm.startcic, e->cqm.opc); + if (chanpos < 0) { + isup_free_call(linkset->ss7, e->cqm.call); + return; + } + p = linkset->pvts[chanpos]; + sig_ss7_lock_private(p); + p->ss7call = e->cqm.call; + if (!p->owner) { + p->ss7call = isup_free_call_if_clear(linkset->ss7, e->cqm.call); + } + sig_ss7_unlock_private(p); } static inline void ss7_hangup_cics(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc) @@ -510,7 +696,7 @@ static inline void ss7_hangup_cics(struct sig_ss7_linkset *linkset, int startcic int i; 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 (ss7_match_range(linkset->pvts[i], startcic, endcic, dpc)) { sig_ss7_lock_private(linkset->pvts[i]); sig_ss7_lock_owner(linkset, i); if (linkset->pvts[i]->owner) { @@ -522,67 +708,274 @@ static inline void ss7_hangup_cics(struct sig_ss7_linkset *linkset, int startcic } } -static inline void ss7_block_cics(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc, unsigned char state[], int block) +/*! + * \param linkset SS7 linkset control structure. + * \param startcic Start CIC of the range to clear. + * \param endcic End CIC of the range to clear. + * \param dpc Destination Point Code. + * \param state Affected CICs from the operation. NULL for all CICs in the range. + * \param block Operation to perform. TRUE to block. + * \param remotely Direction of the blocking. TRUE to block/unblock remotely. + * \param type Blocking type - hardware or maintenance. + * + * \note Assumes the linkset->lock is already obtained. + * \note Must be called without sig_ss7_lock_private() obtained. + * + * \return Nothing. + */ +static inline void ss7_block_cics(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc, unsigned char state[], int block, int remotely, int type) { 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 (ss7_match_range(linkset->pvts[i], startcic, endcic, dpc)) { + sig_ss7_lock_private(linkset->pvts[i]); if (state) { - if (state[i]) - sig_ss7_set_remotelyblocked(linkset->pvts[i], block); - } else - sig_ss7_set_remotelyblocked(linkset->pvts[i], block); + if (state[linkset->pvts[i]->cic - startcic]) { + + if (remotely) { + sig_ss7_set_remotelyblocked(linkset->pvts[i], block, type); + } else { + sig_ss7_set_locallyblocked(linkset->pvts[i], block, type); + } + + sig_ss7_lock_owner(linkset, i); + if (linkset->pvts[i]->owner) { + if (ast_channel_state(linkset->pvts[i]->owner) == AST_STATE_DIALING + && linkset->pvts[i]->call_level < SIG_SS7_CALL_LEVEL_PROCEEDING) { + ast_channel_hangupcause_set(linkset->pvts[i]->owner, SS7_CAUSE_TRY_AGAIN); + } + ast_channel_unlock(linkset->pvts[i]->owner); + } + } + } else { + if (remotely) { + sig_ss7_set_remotelyblocked(linkset->pvts[i], block, type); + } else { + sig_ss7_set_locallyblocked(linkset->pvts[i], block, type); + } + } + sig_ss7_unlock_private(linkset->pvts[i]); } } } +/*! + * \param linkset SS7 linkset control structure. + * \param startcic Start CIC of the range to set in service. + * \param endcic End CIC of the range to set in service. + * \param dpc Destination Point Code. + * + * \note Must be called without sig_ss7_lock_private() obtained. + * + * \return Nothing. + */ static void ss7_inservice(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc) { int i; 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 (ss7_match_range(linkset->pvts[i], startcic, endcic, dpc)) { + sig_ss7_lock_private(linkset->pvts[i]); sig_ss7_set_inservice(linkset->pvts[i], 1); + sig_ss7_unlock_private(linkset->pvts[i]); + } + } +} + +static int ss7_find_alloc_call(struct sig_ss7_chan *p) +{ + if (!p) { + return 0; + } + + if (!p->ss7call) { + p->ss7call = isup_new_call(p->ss7->ss7, p->cic, p->dpc, 0); + if (!p->ss7call) { + return 0; + } } + return 1; } +/* + * XXX This routine is not tolerant of holes in the pvts[] array. + * XXX This routine assumes the cic's in the pvts[] array are sorted. + * + * Probably the easiest way to deal with the invalid assumptions + * is to have a local pvts[] array and sort it by dpc and cic. + * Then the existing algorithm could work. + */ static void ss7_reset_linkset(struct sig_ss7_linkset *linkset) { - int i, startcic = -1, endcic, dpc; + int i, startcic, endcic, dpc; + struct sig_ss7_chan *p; - if (linkset->numchans <= 0) + if (linkset->numchans <= 0) { return; + } startcic = linkset->pvts[0]->cic; + p = linkset->pvts[0]; /* DB: CIC's DPC fix */ dpc = linkset->pvts[0]->dpc; for (i = 0; i < linkset->numchans; i++) { - if (linkset->pvts[i+1] && linkset->pvts[i+1]->dpc == dpc && ((linkset->pvts[i+1]->cic - linkset->pvts[i]->cic) == 1) && (linkset->pvts[i]->cic - startcic < 31)) { + if (linkset->pvts[i+1] + && linkset->pvts[i+1]->dpc == dpc + && linkset->pvts[i+1]->cic - linkset->pvts[i]->cic == 1 + && linkset->pvts[i]->cic - startcic < (linkset->type == SS7_ANSI ? 24 : 31)) { continue; } else { endcic = linkset->pvts[i]->cic; - ast_verbose("Resetting CICs %d to %d\n", startcic, endcic); - isup_grs(linkset->ss7, startcic, endcic, dpc); + ast_verb(1, "Resetting CICs %d to %d\n", startcic, endcic); + + sig_ss7_lock_private(p); + if (!ss7_find_alloc_call(p)) { + ast_log(LOG_ERROR, "Unable to allocate new ss7call\n"); + } else if (!(endcic - startcic)) { /* GRS range can not be 0 - use RSC instead */ + isup_rsc(linkset->ss7, p->ss7call); + } else { + isup_grs(linkset->ss7, p->ss7call, endcic); + } + sig_ss7_unlock_private(p); /* DB: CIC's DPC fix */ if (linkset->pvts[i+1]) { startcic = linkset->pvts[i+1]->cic; dpc = linkset->pvts[i+1]->dpc; + p = linkset->pvts[i+1]; + } + } + } +} + +/*! + * \internal + * \brief Complete the RSC procedure started earlier + * \since 11.0 + * + * \param p Signaling private structure pointer. + * + * \note Assumes the ss7->lock is already obtained. + * \note Assumes sig_ss7_lock_private(p) is already obtained. + * + * \return Nothing. + */ +static void ss7_do_rsc(struct sig_ss7_chan *p) +{ + if (!p || !p->ss7call) { + return; + } + + isup_rsc(p->ss7->ss7, p->ss7call); + + if (p->locallyblocked & SS7_BLOCKED_MAINTENANCE) { + isup_blo(p->ss7->ss7, p->ss7call); + } else { + sig_ss7_set_locallyblocked(p, 0, SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE); + } +} + +/*! + * \internal + * \brief Start RSC procedure on a specific link + * \since 11.0 + * + * \param ss7 SS7 linkset control structure. + * \param which Channel position in the span. + * + * \note Assumes the ss7->lock is already obtained. + * \note Assumes the sig_ss7_lock_private(ss7->pvts[chanpos]) is already obtained. + * + * \return TRUE on success + */ +static int ss7_start_rsc(struct sig_ss7_linkset *linkset, int which) +{ + if (!linkset->pvts[which]) { + return 0; + } + + if (!ss7_find_alloc_call(linkset->pvts[which])) { + return 0; + } + + sig_ss7_set_remotelyblocked(linkset->pvts[which], 0, SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE); + sig_ss7_set_inservice(linkset->pvts[which], 0); + sig_ss7_loopback(linkset->pvts[which], 0); + + sig_ss7_lock_owner(linkset, which); + if (linkset->pvts[which]->owner) { + ast_channel_hangupcause_set(linkset->pvts[which]->owner, AST_CAUSE_NORMAL_CLEARING); + ast_softhangup_nolock(linkset->pvts[which]->owner, AST_SOFTHANGUP_DEV); + ast_channel_unlock(linkset->pvts[which]->owner); + linkset->pvts[which]->do_hangup = SS7_HANGUP_SEND_RSC; + } else { + ss7_do_rsc(linkset->pvts[which]); + } + + return 1; +} + +/*! + * \internal + * \brief Determine if a private channel structure is available. + * \since 11.0 + * + * \param linkset SS7 linkset control structure. + * \param startcic Start CIC of the range to clear. + * \param endcic End CIC of the range to clear. + * \param dpc Destination Point Code. + * \param do_hangup What we have to do to clear the call. + * + * \note Assumes the linkset->lock is already obtained. + * \note Must be called without sig_ss7_lock_private() obtained. + * + * \return Nothing. + */ +static void ss7_clear_channels(struct sig_ss7_linkset *linkset, int startcic, int endcic, int dpc, int do_hangup) +{ + int i; + + for (i = 0; i < linkset->numchans; i++) { + if (ss7_match_range(linkset->pvts[i], startcic, endcic, dpc)) { + sig_ss7_lock_private(linkset->pvts[i]); + sig_ss7_set_inservice(linkset->pvts[i], 0); + sig_ss7_lock_owner(linkset, i); + if (linkset->pvts[i]->owner) { + ast_channel_hangupcause_set(linkset->pvts[i]->owner, + AST_CAUSE_NORMAL_CLEARING); + ast_softhangup_nolock(linkset->pvts[i]->owner, AST_SOFTHANGUP_DEV); + ast_channel_unlock(linkset->pvts[i]->owner); + linkset->pvts[i]->do_hangup = (linkset->pvts[i]->cic != startcic) ? + do_hangup : SS7_HANGUP_DO_NOTHING; + } else if (linkset->pvts[i] && linkset->pvts[i]->cic != startcic) { + isup_free_call(linkset->pvts[i]->ss7->ss7, linkset->pvts[i]->ss7call); + linkset->pvts[i]->ss7call = NULL; } + sig_ss7_unlock_private(linkset->pvts[i]); } } } -/* This function is assumed to be called with the private channel lock and linkset lock held */ +/*! + * \internal + * + * \param p Signaling private structure pointer. + * \param linkset SS7 linkset control structure. + * + * \note Assumes the linkset->lock is already obtained. + * \note Assumes the sig_ss7_lock_private(ss7->pvts[chanpos]) is already obtained. + * + * \return Nothing. + */ static void ss7_start_call(struct sig_ss7_chan *p, struct sig_ss7_linkset *linkset) { struct ss7 *ss7 = linkset->ss7; int law; struct ast_channel *c; char tmp[256]; + char *strp; struct ast_callid *callid = NULL; int callid_created = ast_callid_threadstorage_auto(&callid); @@ -600,6 +993,8 @@ static void ss7_start_call(struct sig_ss7_chan *p, struct sig_ss7_linkset *links law = SIG_SS7_ULAW; } + isup_set_echocontrol(p->ss7call, (linkset->flags & LINKSET_FLAG_DEFAULTECHOCONTROL) ? 1 : 0); + /* * Release the SS7 lock while we create the channel so other * threads can send messages. We must also release the private @@ -614,7 +1009,6 @@ static void ss7_start_call(struct sig_ss7_chan *p, struct sig_ss7_linkset *links sig_ss7_lock_private(p); isup_rel(linkset->ss7, p->ss7call, AST_CAUSE_SWITCH_CONGESTION); p->call_level = SIG_SS7_CALL_LEVEL_IDLE; - p->alreadyhungup = 1; ast_callid_threadstorage_auto_clean(callid, callid_created); return; } @@ -623,8 +1017,6 @@ static void ss7_start_call(struct sig_ss7_chan *p, struct sig_ss7_linkset *links ast_channel_lock(c); sig_ss7_lock_private(p); - sig_ss7_set_echocanceller(p, 1); - ast_channel_stage_snapshot(c); /* @@ -658,11 +1050,6 @@ static void ss7_start_call(struct sig_ss7_chan *p, struct sig_ss7_linkset *links /* Clear this after we set it */ p->gen_dig_number[0] = 0; } - if (!ast_strlen_zero(p->orig_called_num)) { - pbx_builtin_setvar_helper(c, "SS7_ORIG_CALLED_NUM", p->orig_called_num); - /* Clear this after we set it */ - p->orig_called_num[0] = 0; - } snprintf(tmp, sizeof(tmp), "%d", p->gen_dig_type); pbx_builtin_setvar_helper(c, "SS7_GENERIC_DIGTYPE", tmp); @@ -695,28 +1082,187 @@ static void ss7_start_call(struct sig_ss7_chan *p, struct sig_ss7_linkset *links /* Clear this after we set it */ p->calling_party_cat = 0; - if (!ast_strlen_zero(p->redirecting_num)) { - pbx_builtin_setvar_helper(c, "SS7_REDIRECTING_NUMBER", p->redirecting_num); - /* Clear this after we set it */ - p->redirecting_num[0] = 0; - } - if (!ast_strlen_zero(p->generic_name)) { - pbx_builtin_setvar_helper(c, "SS7_GENERIC_NAME", p->generic_name); + if (p->redirect_counter) { + struct ast_party_redirecting redirecting; + + switch (p->redirect_info_ind) { + case 0: + strp = "NO_REDIRECTION"; + break; + case 1: + strp = "CALL_REROUTED_PRES_ALLOWED"; + break; + case 2: + strp = "CALL_REROUTED_INFO_RESTRICTED"; + break; + case 3: + strp = "CALL_DIVERTED_PRES_ALLOWED"; + break; + case 4: + strp = "CALL_DIVERTED_INFO_RESTRICTED"; + break; + case 5: + strp = "CALL_REROUTED_PRES_RESTRICTED"; + break; + case 6: + strp = "CALL_DIVERTED_PRES_RESTRICTED"; + break; + case 7: + strp = "SPARE"; + break; + default: + strp = "NO_REDIRECTION"; + break; + } + pbx_builtin_setvar_helper(c, "SS7_REDIRECT_INFO_IND", strp); /* Clear this after we set it */ - p->generic_name[0] = 0; - } + p->redirect_info_ind = 0; - ast_channel_stage_snapshot_done(c); + ast_party_redirecting_init(&redirecting); - sig_ss7_unlock_private(p); - ast_channel_unlock(c); + if (p->redirect_info_counter) { + redirecting.count = p->redirect_info_counter; + if (p->redirect_info_counter != p->redirect_counter) { + if (p->redirect_info_counter < p->redirect_counter) { + redirecting.count = p->redirect_counter; + } + ast_log(LOG_WARNING, "Redirect counters differ: %u while info says %u - using %u\n", + p->redirect_counter, p->redirect_info_counter, redirecting.count); + } + /* Clear this after we set it */ + p->redirect_info_counter = 0; + p->redirect_counter = 0; + } - if (ast_pbx_start(c)) { - ast_log(LOG_WARNING, "Unable to start PBX on %s (CIC %d)\n", ast_channel_name(c), p->cic); - ast_hangup(c); - } else { - ast_verb(3, "Accepting call to '%s' on CIC %d\n", p->exten, p->cic); - } + if (p->redirect_counter) { + redirecting.count = p->redirect_counter; + /* Clear this after we set it */ + p->redirect_counter = 0; + } + + switch (p->redirect_info_orig_reas) { + case SS7_REDIRECTING_REASON_UNKNOWN: + redirecting.orig_reason.code = AST_REDIRECTING_REASON_UNKNOWN; + break; + case SS7_REDIRECTING_REASON_USER_BUSY: + redirecting.orig_reason.code = AST_REDIRECTING_REASON_USER_BUSY; + break; + case SS7_REDIRECTING_REASON_NO_ANSWER: + redirecting.orig_reason.code = AST_REDIRECTING_REASON_NO_ANSWER; + break; + case SS7_REDIRECTING_REASON_UNCONDITIONAL: + redirecting.orig_reason.code = AST_REDIRECTING_REASON_UNCONDITIONAL; + break; + default: + redirecting.orig_reason.code = AST_REDIRECTING_REASON_UNKNOWN; + break; + } + + switch (p->redirect_info_reas) { + case SS7_REDIRECTING_REASON_UNKNOWN: + redirecting.reason.code = AST_REDIRECTING_REASON_UNKNOWN; + break; + case SS7_REDIRECTING_REASON_USER_BUSY: + redirecting.reason.code = AST_REDIRECTING_REASON_USER_BUSY; + if (!p->redirect_info_orig_reas && redirecting.count == 1) { + redirecting.orig_reason.code = AST_REDIRECTING_REASON_USER_BUSY; + } + break; + case SS7_REDIRECTING_REASON_NO_ANSWER: + redirecting.reason.code = AST_REDIRECTING_REASON_NO_ANSWER; + if (!p->redirect_info_orig_reas && redirecting.count == 1) { + redirecting.orig_reason.code = AST_REDIRECTING_REASON_NO_ANSWER; + } + break; + case SS7_REDIRECTING_REASON_UNCONDITIONAL: + redirecting.reason.code = AST_REDIRECTING_REASON_UNCONDITIONAL; + if (!p->redirect_info_orig_reas && redirecting.count == 1) { + redirecting.orig_reason.code = AST_REDIRECTING_REASON_UNCONDITIONAL; + } + break; + case SS7_REDIRECTING_REASON_DEFLECTION_DURING_ALERTING: + case SS7_REDIRECTING_REASON_DEFLECTION_IMMEDIATE_RESPONSE: + redirecting.reason.code = AST_REDIRECTING_REASON_DEFLECTION; + break; + case SS7_REDIRECTING_REASON_UNAVAILABLE: + redirecting.reason.code = AST_REDIRECTING_REASON_UNAVAILABLE; + break; + default: + redirecting.reason.code = AST_REDIRECTING_REASON_UNKNOWN; + break; + } + /* Clear this after we set it */ + p->redirect_info_orig_reas = 0; + p->redirect_info_reas = 0; + + if (!ast_strlen_zero(p->redirecting_num)) { + redirecting.from.number.str = ast_strdup(p->redirecting_num); + redirecting.from.number.presentation = p->redirecting_presentation; + redirecting.from.number.valid = 1; + /* Clear this after we set it */ + p->redirecting_num[0] = 0; + } + + if (!ast_strlen_zero(p->generic_name)) { + redirecting.from.name.str = ast_strdup(p->generic_name); + redirecting.from.name.presentation = p->redirecting_presentation; + redirecting.from.name.valid = 1; + /* Clear this after we set it */ + p->generic_name[0] = 0; + } + + if (!ast_strlen_zero(p->orig_called_num)) { + redirecting.orig.number.str = ast_strdup(p->orig_called_num); + redirecting.orig.number.presentation = p->orig_called_presentation; + redirecting.orig.number.valid = 1; + /* Clear this after we set it */ + p->orig_called_num[0] = 0; + } else if (redirecting.count == 1) { + ast_party_id_copy(&redirecting.orig, &redirecting.from); + } + + ast_channel_update_redirecting(c, &redirecting, NULL); + ast_party_redirecting_free(&redirecting); + } + + if (p->cug_indicator != ISUP_CUG_NON) { + sprintf(tmp, "%d", p->cug_interlock_code); + pbx_builtin_setvar_helper(c, "SS7_CUG_INTERLOCK_CODE", tmp); + + switch (p->cug_indicator) { + case ISUP_CUG_NON: + strp = "NON_CUG"; + break; + case ISUP_CUG_OUTGOING_ALLOWED: + strp = "OUTGOING_ALLOWED"; + break; + case ISUP_CUG_OUTGOING_NOT_ALLOWED: + strp = "OUTGOING_NOT_ALLOWED"; + break; + default: + strp = "SPARE"; + break; + } + pbx_builtin_setvar_helper(c, "SS7_CUG_INDICATOR", strp); + + if (!ast_strlen_zero(p->cug_interlock_ni)) { + pbx_builtin_setvar_helper(c, "SS7_CUG_INTERLOCK_NI", p->cug_interlock_ni); + } + + p->cug_indicator = ISUP_CUG_NON; + } + + ast_channel_stage_snapshot_done(c); + + sig_ss7_unlock_private(p); + ast_channel_unlock(c); + + if (ast_pbx_start(c)) { + ast_log(LOG_WARNING, "Unable to start PBX on %s (CIC %d)\n", ast_channel_name(c), p->cic); + ast_hangup(c); + } else { + ast_verb(3, "Accepting call to '%s' on CIC %d\n", p->exten, p->cic); + } /* Must return with linkset and private lock. */ ast_mutex_lock(&linkset->lock); @@ -745,6 +1291,9 @@ static void ss7_apply_plan_to_number(char *buf, size_t size, const struct sig_ss case SS7_NAI_UNKNOWN: snprintf(buf, size, "%s%s", ss7->unknownprefix, number); break; + case SS7_NAI_NETWORKROUTED: + snprintf(buf, size, "%s%s", ss7->networkroutedprefix, number); + break; default: snprintf(buf, size, "%s", number); break; @@ -786,6 +1335,50 @@ static struct ast_callid *func_ss7_linkset_callid(struct sig_ss7_linkset *linkse return callid; } +/*! + * \internal + * \brief Proceed with the call based on the extension matching status + * is matching in the dialplan. + * \since 11.0 + * + * \param linkset ss7 span control structure. + * \param p Signaling private structure pointer. + * \param e Event causing the match. + * + * \note Assumes the linkset->lock is already obtained. + * \note Assumes the sig_ss7_lock_private(ss7->pvts[chanpos]) is already obtained. + * + * \return Nothing. + */ +static void ss7_match_extension(struct sig_ss7_linkset *linkset, struct sig_ss7_chan *p, ss7_event *e) +{ + ast_verb(3, "SS7 exten: %s complete: %d\n", p->exten, p->called_complete); + + if (!p->called_complete + && linkset->type == SS7_ITU /* ANSI does not support overlap dialing. */ + && ast_matchmore_extension(NULL, p->context, p->exten, 1, p->cid_num) + && !isup_start_digittimeout(linkset->ss7, p->ss7call)) { + /* Wait for more digits. */ + return; + } + if (ast_exists_extension(NULL, p->context, p->exten, 1, p->cid_num)) { + /* DNID is complete */ + p->called_complete = 1; + sig_ss7_set_dnid(p, p->exten); + + /* If COT successful start call! */ + if ((e->e == ISUP_EVENT_IAM) + ? !(e->iam.cot_check_required || e->iam.cot_performed_on_previous_cic) + : (!(e->sam.cot_check_required || e->sam.cot_performed_on_previous_cic) || e->sam.cot_check_passed)) { + ss7_start_call(p, linkset); + } + return; + } + + ast_debug(1, "Call on CIC for unconfigured extension %s\n", p->exten); + isup_rel(linkset->ss7, (e->e == ISUP_EVENT_IAM) ? e->iam.call : e->sam.call, AST_CAUSE_UNALLOCATED); +} + /* This is a thread per linkset that handles all received events from libss7. */ void *ss7_linkset(void *data) { @@ -796,6 +1389,7 @@ void *ss7_linkset(void *data) ss7_event *e = NULL; struct sig_ss7_chan *p; struct pollfd pollers[SIG_SS7_NUM_DCHANS]; + unsigned char mb_state[255]; int nextms; #define SS7_MAX_POLL 60000 /* Maximum poll time in ms. */ @@ -843,16 +1437,13 @@ void *ss7_linkset(void *data) pthread_testcancel(); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + ast_mutex_lock(&linkset->lock); if ((res < 0) && (errno != EINTR)) { ast_log(LOG_ERROR, "poll(%s)\n", strerror(errno)); } else if (!res) { - ast_mutex_lock(&linkset->lock); ss7_schedule_run(ss7); - ast_mutex_unlock(&linkset->lock); - continue; } - ast_mutex_lock(&linkset->lock); for (i = 0; i < linkset->numsigchans; i++) { if (pollers[i].revents & POLLPRI) { sig_ss7_handle_link_exception(linkset, i); @@ -881,23 +1472,26 @@ void *ss7_linkset(void *data) switch (e->e) { case SS7_EVENT_UP: if (linkset->state != LINKSET_STATE_UP) { - ast_verbose("--- SS7 Up ---\n"); + ast_verb(1, "--- SS7 Up ---\n"); ss7_reset_linkset(linkset); } linkset->state = LINKSET_STATE_UP; break; case SS7_EVENT_DOWN: - ast_verbose("--- SS7 Down ---\n"); + ast_verb(1, "--- SS7 Down ---\n"); linkset->state = LINKSET_STATE_DOWN; for (i = 0; i < linkset->numchans; i++) { p = linkset->pvts[i]; if (p) { - sig_ss7_set_alarm(p, 1); + sig_ss7_set_inservice(p, 0); + if (linkset->flags & LINKSET_FLAG_INITIALHWBLO) { + sig_ss7_set_remotelyblocked(p, 1, SS7_BLOCKED_HARDWARE); + } } } break; case MTP2_LINK_UP: - ast_verbose("MTP2 link up (SLC %d)\n", e->gen.data); + ast_verb(1, "MTP2 link up (SLC %d)\n", e->gen.data); break; case MTP2_LINK_DOWN: ast_log(LOG_WARNING, "MTP2 link down (SLC %d)\n", e->gen.data); @@ -905,6 +1499,7 @@ void *ss7_linkset(void *data) case ISUP_EVENT_CPG: chanpos = ss7_find_cic_gripe(linkset, e->cpg.cic, e->cpg.opc, "CPG"); if (chanpos < 0) { + isup_free_call(ss7, e->cpg.call); break; } p = linkset->pvts[chanpos]; @@ -919,6 +1514,21 @@ void *ss7_linkset(void *data) sig_ss7_lock_owner(linkset, chanpos); if (p->owner) { ast_setstate(p->owner, AST_STATE_RINGING); + if (!ast_strlen_zero(e->cpg.connected_num)) { + struct ast_party_connected_line ast_connected; + char connected_num[AST_MAX_EXTENSION]; + + ast_party_connected_line_init(&ast_connected); + ast_connected.id.number.presentation = + ss7_pres_scr2cid_pres(e->cpg.connected_presentation_ind, + e->cpg.connected_screening_ind); + ss7_apply_plan_to_number(connected_num, sizeof(connected_num), + linkset, e->cpg.connected_num, e->cpg.connected_nai); + ast_connected.id.number.str = ast_strdup(connected_num); + ast_connected.id.number.valid = 1; + ast_channel_queue_connected_line_update(p->owner, &ast_connected, NULL); + ast_party_connected_line_free(&ast_connected); + } ast_channel_unlock(p->owner); } sig_ss7_queue_control(linkset, chanpos, AST_CONTROL_RINGING); @@ -941,95 +1551,201 @@ void *ss7_linkset(void *data) sig_ss7_unlock_private(p); break; case ISUP_EVENT_RSC: - ast_verbose("Resetting CIC %d\n", e->rsc.cic); + ast_verb(1, "Resetting CIC %d\n", e->rsc.cic); chanpos = ss7_find_cic_gripe(linkset, e->rsc.cic, e->rsc.opc, "RSC"); if (chanpos < 0) { + isup_free_call(ss7, e->rsc.call); break; } p = linkset->pvts[chanpos]; sig_ss7_lock_private(p); + p->ss7call = e->rsc.call; callid = func_ss7_linkset_callid(linkset, chanpos); sig_ss7_set_inservice(p, 1); - sig_ss7_set_remotelyblocked(p, 0); + sig_ss7_set_remotelyblocked(p, 0, SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE); + + if (p->locallyblocked & SS7_BLOCKED_MAINTENANCE) { + isup_blo(ss7, e->rsc.call); + } else if (p->locallyblocked & SS7_BLOCKED_HARDWARE) { + sig_ss7_set_locallyblocked(p, 0, SS7_BLOCKED_HARDWARE); + } + isup_set_call_dpc(e->rsc.call, p->dpc); sig_ss7_lock_owner(linkset, chanpos); - p->ss7call = NULL; if (p->owner) { + p->do_hangup = SS7_HANGUP_SEND_RLC; + if (!(e->rsc.got_sent_msg & ISUP_SENT_IAM)) { + /* Q.784 6.2.3 */ + ast_channel_hangupcause_set(p->owner, AST_CAUSE_NORMAL_CLEARING); + } else { + ast_channel_hangupcause_set(p->owner, SS7_CAUSE_TRY_AGAIN); + } + ss7_queue_pvt_cause_data(p->owner, "SS7 ISUP_EVENT_RSC", AST_CAUSE_INTERWORKING); + ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV); ast_channel_unlock(p->owner); + } else { + isup_rlc(ss7, e->rsc.call); + p->ss7call = isup_free_call_if_clear(ss7, e->rsc.call); } + /* End the loopback if we have one */ + sig_ss7_loopback(p, 0); + sig_ss7_unlock_private(p); - isup_rlc(ss7, e->rsc.call); 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_gripe(linkset, e->grs.startcic, e->grs.opc, "GRS"); - if (chanpos < 0) { + if (!sig_ss7_find_cic_range(linkset, e->grs.startcic, e->grs.endcic, + e->grs.opc)) { + ast_log(LOG_WARNING, "GRS on unconfigured range CIC %d - %d PC %d\n", + e->grs.startcic, e->grs.endcic, e->grs.opc); + chanpos = sig_ss7_find_cic(linkset, e->grs.startcic, e->grs.opc); + if (chanpos < 0) { + isup_free_call(ss7, e->grs.call); + break; + } + p = linkset->pvts[chanpos]; + sig_ss7_lock_private(p); + p->ss7call = isup_free_call_if_clear(ss7, e->grs.call); + sig_ss7_unlock_private(p); break; } - p = linkset->pvts[chanpos]; - isup_gra(ss7, e->grs.startcic, e->grs.endcic, e->grs.opc); - ss7_block_cics(linkset, e->grs.startcic, e->grs.endcic, e->grs.opc, NULL, 0); - ss7_hangup_cics(linkset, e->grs.startcic, e->grs.endcic, e->grs.opc); + + /* Leave startcic last to collect all cics mb_state */ + for (i = e->grs.endcic - e->grs.startcic; 0 <= i; --i) { + /* + * We are guaranteed to find chanpos because + * sig_ss7_find_cic_range() includes it. + */ + chanpos = sig_ss7_find_cic(linkset, e->grs.startcic + i, e->grs.opc); + p = linkset->pvts[chanpos]; + sig_ss7_lock_private(p); + + if (p->locallyblocked & SS7_BLOCKED_MAINTENANCE) { + mb_state[i] = 1; + } else { + mb_state[i] = 0; + sig_ss7_set_locallyblocked(p, 0, SS7_BLOCKED_HARDWARE); + } + + sig_ss7_set_remotelyblocked(p, 0, SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE); + + if (!i) { + p->ss7call = e->grs.call; + isup_gra(ss7, p->ss7call, e->grs.endcic, mb_state); + } + + sig_ss7_lock_owner(linkset, chanpos); + if (p->owner) { + ast_channel_softhangup_internal_flag_add(p->owner, AST_SOFTHANGUP_DEV); + if (ast_channel_state(p->owner) == AST_STATE_DIALING + && linkset->pvts[i]->call_level < SIG_SS7_CALL_LEVEL_PROCEEDING) { + ast_channel_hangupcause_set(p->owner, SS7_CAUSE_TRY_AGAIN); + } else { + ast_channel_hangupcause_set(p->owner, AST_CAUSE_NORMAL_CLEARING); + } + p->do_hangup = SS7_HANGUP_FREE_CALL; + ast_channel_unlock(p->owner); + } else if (!i) { + p->ss7call = isup_free_call_if_clear(ss7, p->ss7call); + } else if (p->ss7call) { + /* clear any other session */ + isup_free_call(ss7, p->ss7call); + p->ss7call = NULL; + } + sig_ss7_set_inservice(p, 1); + sig_ss7_unlock_private(p); + } break; case ISUP_EVENT_CQM: - ast_debug(1, "Got Circuit group query message from CICs %d to %d\n", e->cqm.startcic, e->cqm.endcic); - ss7_handle_cqm(linkset, e->cqm.startcic, e->cqm.endcic, e->cqm.opc); + ast_debug(1, "Got Circuit group query message from CICs %d to %d\n", + e->cqm.startcic, e->cqm.endcic); + ss7_handle_cqm(linkset, e); break; case ISUP_EVENT_GRA: - ast_verbose("Got reset acknowledgement from CIC %d to %d.\n", e->gra.startcic, e->gra.endcic); + if (!sig_ss7_find_cic_range(linkset, e->gra.startcic, + e->gra.endcic, e->gra.opc)) { /* Never will be true */ + ast_log(LOG_WARNING, "GRA on unconfigured range CIC %d - %d PC %d\n", + e->gra.startcic, e->gra.endcic, e->gra.opc); + isup_free_call(ss7, e->gra.call); + break; + } + ast_verb(1, "Got reset acknowledgement from CIC %d to %d DPC: %d\n", + e->gra.startcic, e->gra.endcic, e->gra.opc); + ss7_block_cics(linkset, e->gra.startcic, e->gra.endcic, e->gra.opc, + e->gra.status, 1, 1, SS7_BLOCKED_MAINTENANCE); ss7_inservice(linkset, e->gra.startcic, e->gra.endcic, e->gra.opc); - ss7_block_cics(linkset, e->gra.startcic, e->gra.endcic, e->gra.opc, e->gra.status, 1); + + chanpos = sig_ss7_find_cic(linkset, e->gra.startcic, e->gra.opc); + if (chanpos < 0) { + isup_free_call(ss7, e->gra.call); + break; + } + + p = linkset->pvts[chanpos]; + sig_ss7_lock_private(p); + + /* we may send a CBD with GRS! */ + p->ss7call = isup_free_call_if_clear(ss7, e->gra.call); + sig_ss7_unlock_private(p); 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_gripe(linkset, e->iam.cic, e->iam.opc, "IAM"); + case ISUP_EVENT_SAM: + chanpos = ss7_find_cic_gripe(linkset, e->sam.cic, e->sam.opc, "SAM"); if (chanpos < 0) { - isup_rel(ss7, e->iam.call, -1); + isup_free_call(ss7, e->sam.call); break; } p = linkset->pvts[chanpos]; sig_ss7_lock_private(p); 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, AST_CAUSE_NORMAL_CIRCUIT_CONGESTION); - } - p->call_level = SIG_SS7_CALL_LEVEL_GLARE; - if (p->owner) { - ss7_queue_pvt_cause_data(p->owner, "SS7 ISUP_EVENT_IAM (glare)", AST_CAUSE_NORMAL_CIRCUIT_CONGESTION); - ast_channel_hangupcause_set(p->owner, AST_CAUSE_NORMAL_CIRCUIT_CONGESTION); - ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV); - ast_channel_unlock(p->owner); - } + if (p->owner) { + ast_log(LOG_WARNING, "SAM on CIC %d PC %d already have call\n", e->sam.cic, e->sam.opc); + ast_channel_unlock(p->owner); sig_ss7_unlock_private(p); break; } + p->called_complete = 0; + if (!ast_strlen_zero(e->sam.called_party_num)) { + char *st; + strncat(p->exten, e->sam.called_party_num, sizeof(p->exten) - strlen(p->exten) - 1); + st = strchr(p->exten, '#'); + if (st) { + *st = '\0'; + p->called_complete = 1; + } + ss7_match_extension(linkset, p, e); + } + sig_ss7_unlock_private(p); + 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_gripe(linkset, e->iam.cic, e->iam.opc, "IAM"); + if (chanpos < 0) { + isup_free_call(ss7, e->iam.call); + break; + } + p = linkset->pvts[chanpos]; + sig_ss7_lock_private(p); /* - * The channel should not have an owner at this point since we + * The channel should be idle and not have an owner at this point since we * are in the process of creating an owner for it. */ - ast_assert(!p->owner); + ast_assert(!p->owner && p->call_level == SIG_SS7_CALL_LEVEL_IDLE); + + if (p->remotelyblocked) { + ast_log(LOG_NOTICE, "Got IAM on remotely blocked CIC %d DPC %d remove blocking\n", e->iam.cic, e->iam.opc); + sig_ss7_set_remotelyblocked(p, 0, SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE); + sig_ss7_set_inservice(p, 1); + } if (!sig_ss7_is_chan_available(p)) { /* Circuit is likely blocked or in alarm. */ isup_rel(ss7, e->iam.call, AST_CAUSE_NORMAL_CIRCUIT_CONGESTION); + if (p->locallyblocked) { + isup_clear_callflags(ss7, e->iam.call, ISUP_GOT_IAM); + p->ss7call = isup_free_call_if_clear(ss7, e->iam.call); + ast_log(LOG_WARNING, "Got IAM on locally blocked CIC %d DPC %d, ignore\n", e->iam.cic, e->iam.opc); + } sig_ss7_unlock_private(p); break; } @@ -1043,18 +1759,14 @@ void *ss7_linkset(void *data) 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); p->callingpres = ss7_pres_scr2cid_pres(e->iam.presentation_ind, e->iam.screening_ind); - } else - p->cid_num[0] = 0; - - /* Set DNID */ - 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); } else { - p->exten[0] = '\0'; + p->cid_num[0] = 0; + if (e->iam.presentation_ind) { + p->callingpres = ss7_pres_scr2cid_pres(e->iam.presentation_ind, e->iam.screening_ind); + } } - sig_ss7_set_dnid(p, p->exten); + p->called_complete = 0; if (p->immediate) { p->exten[0] = 's'; p->exten[1] = '\0'; @@ -1064,16 +1776,18 @@ void *ss7_linkset(void *data) st = strchr(p->exten, '#'); if (st) { *st = '\0'; + p->called_complete = 1; } } else { p->exten[0] = '\0'; } p->cid_ani[0] = '\0'; - if ((p->use_callerid) && (!ast_strlen_zero(e->iam.generic_name))) + if ((p->use_callerid) && (!ast_strlen_zero(e->iam.generic_name))) { ast_copy_string(p->cid_name, e->iam.generic_name, sizeof(p->cid_name)); - else + } else { p->cid_name[0] = '\0'; + } p->cid_ani2 = e->iam.oli_ani2; p->cid_ton = 0; @@ -1087,81 +1801,162 @@ void *ss7_linkset(void *data) p->gen_dig_type = e->iam.gen_dig_type; p->gen_dig_scheme = e->iam.gen_dig_scheme; ast_copy_string(p->jip_number, e->iam.jip_number, sizeof(p->jip_number)); - ast_copy_string(p->orig_called_num, e->iam.orig_called_num, sizeof(p->orig_called_num)); - ast_copy_string(p->redirecting_num, e->iam.redirecting_num, sizeof(p->redirecting_num)); + if (!ast_strlen_zero(e->iam.orig_called_num)) { + ss7_apply_plan_to_number(p->orig_called_num, sizeof(p->orig_called_num), linkset, e->iam.orig_called_num, e->iam.orig_called_nai); + p->orig_called_presentation = ss7_pres_scr2cid_pres(e->iam.orig_called_pres_ind, e->iam.orig_called_screening_ind); + } + if (!ast_strlen_zero(e->iam.redirecting_num)) { + ss7_apply_plan_to_number(p->redirecting_num, sizeof(p->redirecting_num), linkset, e->iam.redirecting_num, e->iam.redirecting_num_nai); + p->redirecting_presentation = ss7_pres_scr2cid_pres(e->iam.redirecting_num_presentation_ind, e->iam.redirecting_num_screening_ind); + } ast_copy_string(p->generic_name, e->iam.generic_name, sizeof(p->generic_name)); p->calling_party_cat = e->iam.calling_party_cat; + p->redirect_counter = e->iam.redirect_counter; + p->redirect_info = e->iam.redirect_info; + p->redirect_info_ind = e->iam.redirect_info_ind; + p->redirect_info_orig_reas = e->iam.redirect_info_orig_reas; + p->redirect_info_counter = e->iam.redirect_info_counter; + if (p->redirect_info_counter && !p->redirect_counter) { + p->redirect_counter = p->redirect_info_counter; + } + p->redirect_info_reas = e->iam.redirect_info_reas; + p->cug_indicator = e->iam.cug_indicator; + p->cug_interlock_code = e->iam.cug_interlock_code; + ast_copy_string(p->cug_interlock_ni, e->iam.cug_interlock_ni, sizeof(p->cug_interlock_ni)); + + if (e->iam.cot_check_required) { + sig_ss7_loopback(p, 1); + } + p->echocontrol_ind = e->iam.echocontrol_ind; sig_ss7_set_caller_id(p); + ss7_match_extension(linkset, p, e); + sig_ss7_unlock_private(p); - 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; + if (e->iam.cot_performed_on_previous_cic) { + chanpos = sig_ss7_find_cic(linkset, (e->iam.cic - 1), e->iam.opc); + if (chanpos < 0) { + /* some stupid switch do this */ + ast_verb(1, "COT request on previous nonexistent CIC %d in IAM PC %d\n", (e->iam.cic - 1), e->iam.opc); + break; + } + ast_verb(1, "COT request on previous CIC %d in IAM PC %d\n", (e->iam.cic - 1), e->iam.opc); + p = linkset->pvts[chanpos]; + sig_ss7_lock_private(p); + if (sig_ss7_is_chan_available(p)) { + sig_ss7_set_inservice(p, 0); /* to prevent to use this circuit */ sig_ss7_loopback(p, 1); - } else { + } /* If already have a call don't loop */ + sig_ss7_unlock_private(p); + } + break; + case ISUP_EVENT_DIGITTIMEOUT: + chanpos = ss7_find_cic_gripe(linkset, e->digittimeout.cic, e->digittimeout.opc, "DIGITTIMEOUT"); + if (chanpos < 0) { + isup_free_call(ss7, e->digittimeout.call); + break; + } + p = linkset->pvts[chanpos]; + sig_ss7_lock_private(p); + ast_debug(1, "Digittimeout on CIC: %d PC: %d\n", e->digittimeout.cic, e->digittimeout.opc); + if (ast_exists_extension(NULL, p->context, p->exten, 1, p->cid_num)) { + /* DNID is complete */ + p->called_complete = 1; + sig_ss7_set_dnid(p, p->exten); + + /* If COT successful start call! */ + if (!(e->digittimeout.cot_check_required || e->digittimeout.cot_performed_on_previous_cic) || e->digittimeout.cot_check_passed) { ss7_start_call(p, linkset); } } else { ast_debug(1, "Call on CIC for unconfigured extension %s\n", p->exten); - p->alreadyhungup = 1; - isup_rel(ss7, e->iam.call, AST_CAUSE_UNALLOCATED); + isup_rel(linkset->ss7, e->digittimeout.call, AST_CAUSE_UNALLOCATED); } sig_ss7_unlock_private(p); break; case ISUP_EVENT_COT: + if (e->cot.cot_performed_on_previous_cic) { + chanpos = sig_ss7_find_cic(linkset, (e->cot.cic - 1), e->cot.opc); + /* some stupid switches do this!!! */ + if (-1 < chanpos) { + p = linkset->pvts[chanpos]; + sig_ss7_lock_private(p); + sig_ss7_set_inservice(p, 1); + sig_ss7_loopback(p, 0); + sig_ss7_unlock_private(p);; + ast_verb(1, "Loop turned off on CIC: %d PC: %d\n", (e->cot.cic - 1), e->cot.opc); + } + } + chanpos = ss7_find_cic_gripe(linkset, e->cot.cic, e->cot.opc, "COT"); if (chanpos < 0) { - isup_rel(ss7, e->cot.call, -1); + isup_free_call(ss7, e->cot.call); break; } p = linkset->pvts[chanpos]; sig_ss7_lock_private(p); + p->ss7call = e->cot.call; + if (p->loopedback) { sig_ss7_loopback(p, 0); + ast_verb(1, "Loop turned off on CIC: %d PC: %d\n", e->cot.cic, e->cot.opc); + } + + /* Don't start call if we didn't get IAM or COT failed! */ + if ((e->cot.got_sent_msg & ISUP_GOT_IAM) && e->cot.passed && p->called_complete) { ss7_start_call(p, linkset); } + + p->ss7call = isup_free_call_if_clear(ss7, p->ss7call); sig_ss7_unlock_private(p); break; case ISUP_EVENT_CCR: ast_debug(1, "Got CCR request on CIC %d\n", e->ccr.cic); chanpos = ss7_find_cic_gripe(linkset, e->ccr.cic, e->ccr.opc, "CCR"); if (chanpos < 0) { + isup_free_call(ss7, e->ccr.call); break; } p = linkset->pvts[chanpos]; sig_ss7_lock_private(p); + p->ss7call = e->ccr.call; sig_ss7_loopback(p, 1); + if (linkset->type == SS7_ANSI) { + isup_lpa(linkset->ss7, e->ccr.cic, p->dpc); + } sig_ss7_unlock_private(p); - - isup_lpa(linkset->ss7, e->ccr.cic, p->dpc); break; case ISUP_EVENT_CVT: ast_debug(1, "Got CVT request on CIC %d\n", e->cvt.cic); chanpos = ss7_find_cic_gripe(linkset, e->cvt.cic, e->cvt.opc, "CVT"); if (chanpos < 0) { + isup_free_call(ss7, e->cvt.call); break; } p = linkset->pvts[chanpos]; sig_ss7_lock_private(p); + p->ss7call = e->cvt.call; sig_ss7_loopback(p, 1); - sig_ss7_unlock_private(p); - + if (!p->owner) { + p->ss7call = isup_free_call_if_clear(ss7, e->cvt.call); + } isup_cvr(linkset->ss7, e->cvt.cic, p->dpc); + sig_ss7_unlock_private(p); break; case ISUP_EVENT_REL: chanpos = ss7_find_cic_gripe(linkset, e->rel.cic, e->rel.opc, "REL"); if (chanpos < 0) { - /* Continue hanging up the call anyway. */ - isup_rlc(ss7, e->rel.call); + isup_free_call(ss7, e->rel.call); break; } p = linkset->pvts[chanpos]; sig_ss7_lock_private(p); + p->ss7call = e->rel.call; callid = func_ss7_linkset_callid(linkset, chanpos); sig_ss7_lock_owner(linkset, chanpos); if (p->owner) { @@ -1170,128 +1965,193 @@ void *ss7_linkset(void *data) ast_channel_hangupcause_set(p->owner, e->rel.cause); ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV); + p->do_hangup = SS7_HANGUP_SEND_RLC; ast_channel_unlock(p->owner); + } else { + ast_verb(1, "REL on CIC %d DPC %d without owner!\n", p->cic, p->dpc); + isup_rlc(ss7, p->ss7call); + p->ss7call = isup_free_call_if_clear(ss7, p->ss7call); } /* End the loopback if we have one */ sig_ss7_loopback(p, 0); - isup_rlc(ss7, e->rel.call); - p->ss7call = NULL; - + /* the rel is not complete here!!! */ sig_ss7_unlock_private(p); break; case ISUP_EVENT_ACM: chanpos = ss7_find_cic_gripe(linkset, e->acm.cic, e->acm.opc, "ACM"); if (chanpos < 0) { - isup_rel(ss7, e->acm.call, -1); + isup_free_call(ss7, e->acm.call); break; } - { - p = linkset->pvts[chanpos]; - ast_debug(1, "Queueing frame from SS7_EVENT_ACM on CIC %d\n", p->cic); + p = linkset->pvts[chanpos]; - if (e->acm.call_ref_ident > 0) { - p->rlt = 1; /* Setting it but not using it here*/ - } + ast_debug(1, "Queueing frame from SS7_EVENT_ACM on CIC %d\n", p->cic); - sig_ss7_lock_private(p); - callid = func_ss7_linkset_callid(linkset, chanpos); - sig_ss7_queue_control(linkset, chanpos, AST_CONTROL_PROCEEDING); - if (p->call_level < SIG_SS7_CALL_LEVEL_PROCEEDING) { - p->call_level = SIG_SS7_CALL_LEVEL_PROCEEDING; + if (e->acm.call_ref_ident > 0) { + p->rlt = 1; /* Setting it but not using it here*/ + } + + sig_ss7_lock_private(p); + p->ss7call = e->acm.call; + callid = func_ss7_linkset_callid(linkset, chanpos); + sig_ss7_queue_control(linkset, chanpos, AST_CONTROL_PROCEEDING); + if (p->call_level < SIG_SS7_CALL_LEVEL_PROCEEDING) { + p->call_level = SIG_SS7_CALL_LEVEL_PROCEEDING; + } + sig_ss7_set_dialing(p, 0); + /* Send alerting if subscriber is free */ + if (e->acm.called_party_status_ind == 1) { + if (p->call_level < SIG_SS7_CALL_LEVEL_ALERTING) { + p->call_level = SIG_SS7_CALL_LEVEL_ALERTING; } - sig_ss7_set_dialing(p, 0); - /* Send alerting if subscriber is free */ - if (e->acm.called_party_status_ind == 1) { - if (p->call_level < SIG_SS7_CALL_LEVEL_ALERTING) { - p->call_level = SIG_SS7_CALL_LEVEL_ALERTING; - } - sig_ss7_lock_owner(linkset, chanpos); - if (p->owner) { - ast_setstate(p->owner, AST_STATE_RINGING); - ast_channel_unlock(p->owner); - } - sig_ss7_queue_control(linkset, chanpos, AST_CONTROL_RINGING); + sig_ss7_lock_owner(linkset, chanpos); + if (p->owner) { + ast_setstate(p->owner, AST_STATE_RINGING); + ast_channel_unlock(p->owner); } - sig_ss7_unlock_private(p); + sig_ss7_queue_control(linkset, chanpos, AST_CONTROL_RINGING); } + p->echocontrol_ind = e->acm.echocontrol_ind; + sig_ss7_unlock_private(p); break; case ISUP_EVENT_CGB: chanpos = ss7_find_cic_gripe(linkset, e->cgb.startcic, e->cgb.opc, "CGB"); if (chanpos < 0) { + isup_free_call(ss7, e->cgb.call); break; } p = linkset->pvts[chanpos]; - ss7_block_cics(linkset, e->cgb.startcic, e->cgb.endcic, e->cgb.opc, e->cgb.status, 1); - isup_cgba(linkset->ss7, e->cgb.startcic, e->cgb.endcic, e->cgb.opc, e->cgb.status, e->cgb.type); + ss7_check_range(linkset, e->cgb.startcic, e->cgb.endcic, + e->cgb.opc, e->cgb.status); + ss7_block_cics(linkset, e->cgb.startcic, e->cgb.endcic, + e->cgb.opc, e->cgb.status, 1, 1, + (e->cgb.type) ? SS7_BLOCKED_HARDWARE : SS7_BLOCKED_MAINTENANCE); + + sig_ss7_lock_private(p); + p->ss7call = e->cgb.call; + + isup_cgba(linkset->ss7, p->ss7call, e->cgb.endcic, e->cgb.status); + if (!p->owner) { + p->ss7call = isup_free_call_if_clear(ss7, e->cgb.call); + } + sig_ss7_unlock_private(p); break; case ISUP_EVENT_CGU: chanpos = ss7_find_cic_gripe(linkset, e->cgu.startcic, e->cgu.opc, "CGU"); if (chanpos < 0) { + isup_free_call(ss7, e->cgu.call); break; } p = linkset->pvts[chanpos]; - ss7_block_cics(linkset, e->cgu.startcic, e->cgu.endcic, e->cgu.opc, e->cgu.status, 0); - isup_cgua(linkset->ss7, e->cgu.startcic, e->cgu.endcic, e->cgu.opc, e->cgu.status, e->cgu.type); + ss7_check_range(linkset, e->cgu.startcic, e->cgu.endcic, + e->cgu.opc, e->cgu.status); + ss7_block_cics(linkset, e->cgu.startcic, e->cgu.endcic, + e->cgu.opc, e->cgu.status, 0, 1, + e->cgu.type ? SS7_BLOCKED_HARDWARE : SS7_BLOCKED_MAINTENANCE); + + sig_ss7_lock_private(p); + p->ss7call = e->cgu.call; + + isup_cgua(linkset->ss7, p->ss7call, e->cgu.endcic, e->cgu.status); + if (!p->owner) { + p->ss7call = isup_free_call_if_clear(ss7, e->cgu.call); + } + sig_ss7_unlock_private(p); break; case ISUP_EVENT_UCIC: chanpos = ss7_find_cic_gripe(linkset, e->ucic.cic, e->ucic.opc, "UCIC"); if (chanpos < 0) { + isup_free_call(ss7, e->ucic.call); break; } p = linkset->pvts[chanpos]; ast_debug(1, "Unequiped Circuit Id Code on CIC %d\n", e->ucic.cic); sig_ss7_lock_private(p); - sig_ss7_set_remotelyblocked(p, 1); + sig_ss7_lock_owner(linkset, chanpos); + if (p->owner) { + ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV); + ast_channel_unlock(p->owner); + } + sig_ss7_set_remotelyblocked(p, 1, SS7_BLOCKED_MAINTENANCE); sig_ss7_set_inservice(p, 0); + p->ss7call = NULL; + isup_free_call(ss7, e->ucic.call); sig_ss7_unlock_private(p);/* doesn't require a SS7 acknowledgement */ break; case ISUP_EVENT_BLO: chanpos = ss7_find_cic_gripe(linkset, e->blo.cic, e->blo.opc, "BLO"); if (chanpos < 0) { + isup_free_call(ss7, e->blo.call); break; } p = linkset->pvts[chanpos]; ast_debug(1, "Blocking CIC %d\n", e->blo.cic); sig_ss7_lock_private(p); - sig_ss7_set_remotelyblocked(p, 1); + p->ss7call = e->blo.call; + sig_ss7_set_remotelyblocked(p, 1, SS7_BLOCKED_MAINTENANCE); + isup_bla(linkset->ss7, e->blo.call); + sig_ss7_lock_owner(linkset, chanpos); + if (!p->owner) { + p->ss7call = isup_free_call_if_clear(ss7, e->blo.call); + } else { + if (e->blo.got_sent_msg & ISUP_SENT_IAM) { + /* Q.784 6.2.2 */ + ast_channel_hangupcause_set(p->owner, SS7_CAUSE_TRY_AGAIN); + } + ast_channel_unlock(p->owner); + } sig_ss7_unlock_private(p); - isup_bla(linkset->ss7, e->blo.cic, p->dpc); break; case ISUP_EVENT_BLA: chanpos = ss7_find_cic_gripe(linkset, e->bla.cic, e->bla.opc, "BLA"); if (chanpos < 0) { + isup_free_call(ss7, e->bla.call); break; } - ast_debug(1, "Blocking CIC %d\n", e->bla.cic); + ast_debug(1, "Locally blocking CIC %d\n", e->bla.cic); p = linkset->pvts[chanpos]; sig_ss7_lock_private(p); - sig_ss7_set_locallyblocked(p, 1); + p->ss7call = e->bla.call; + sig_ss7_set_locallyblocked(p, 1, SS7_BLOCKED_MAINTENANCE); + if (!p->owner) { + p->ss7call = isup_free_call_if_clear(ss7, p->ss7call); + } sig_ss7_unlock_private(p); break; case ISUP_EVENT_UBL: chanpos = ss7_find_cic_gripe(linkset, e->ubl.cic, e->ubl.opc, "UBL"); if (chanpos < 0) { + isup_free_call(ss7, e->ubl.call); break; } p = linkset->pvts[chanpos]; - ast_debug(1, "Unblocking CIC %d\n", e->ubl.cic); + ast_debug(1, "Remotely unblocking CIC %d PC %d\n", e->ubl.cic, e->ubl.opc); sig_ss7_lock_private(p); - sig_ss7_set_remotelyblocked(p, 0); + p->ss7call = e->ubl.call; + sig_ss7_set_remotelyblocked(p, 0, SS7_BLOCKED_MAINTENANCE); + isup_uba(linkset->ss7, e->ubl.call); + if (!p->owner) { + p->ss7call = isup_free_call_if_clear(ss7, p->ss7call); + } sig_ss7_unlock_private(p); - isup_uba(linkset->ss7, e->ubl.cic, p->dpc); break; case ISUP_EVENT_UBA: chanpos = ss7_find_cic_gripe(linkset, e->uba.cic, e->uba.opc, "UBA"); if (chanpos < 0) { + isup_free_call(ss7, e->uba.call); break; } p = linkset->pvts[chanpos]; - ast_debug(1, "Unblocking CIC %d\n", e->uba.cic); + ast_debug(1, "Locally unblocking CIC %d PC %d\n", e->uba.cic, e->uba.opc); sig_ss7_lock_private(p); - sig_ss7_set_locallyblocked(p, 0); + p->ss7call = e->uba.call; + sig_ss7_set_locallyblocked(p, 0, SS7_BLOCKED_MAINTENANCE); + if (!p->owner) { + p->ss7call = isup_free_call_if_clear(ss7, p->ss7call); + } sig_ss7_unlock_private(p); break; case ISUP_EVENT_CON: @@ -1299,49 +2159,92 @@ void *ss7_linkset(void *data) 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); + isup_free_call(ss7, e->con.call); 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); + isup_free_call(ss7, e->anm.call); break; } } - { - p = linkset->pvts[chanpos]; - sig_ss7_lock_private(p); - callid = func_ss7_linkset_callid(linkset, chanpos); - if (p->call_level < SIG_SS7_CALL_LEVEL_CONNECT) { - p->call_level = SIG_SS7_CALL_LEVEL_CONNECT; - } - sig_ss7_queue_control(linkset, chanpos, AST_CONTROL_ANSWER); - sig_ss7_set_dialing(p, 0); - sig_ss7_open_media(p); - sig_ss7_set_echocanceller(p, 1); - sig_ss7_unlock_private(p); - } - break; + p = linkset->pvts[chanpos]; + sig_ss7_lock_private(p); + p->ss7call = (e->e == ISUP_EVENT_ANM) ? e->anm.call : e->con.call; + callid = func_ss7_linkset_callid(linkset, chanpos); + if (p->call_level < SIG_SS7_CALL_LEVEL_CONNECT) { + p->call_level = SIG_SS7_CALL_LEVEL_CONNECT; + } + + if (!ast_strlen_zero((e->e == ISUP_EVENT_ANM) + ? e->anm.connected_num : e->con.connected_num)) { + sig_ss7_lock_owner(linkset, chanpos); + if (p->owner) { + struct ast_party_connected_line ast_connected; + char connected_num[AST_MAX_EXTENSION]; + + ast_party_connected_line_init(&ast_connected); + if (e->e == ISUP_EVENT_ANM) { + ast_connected.id.number.presentation = ss7_pres_scr2cid_pres( + e->anm.connected_presentation_ind, + e->anm.connected_screening_ind); + ss7_apply_plan_to_number(connected_num, sizeof(connected_num), + linkset, e->anm.connected_num, e->anm.connected_nai); + ast_connected.id.number.str = ast_strdup(connected_num); + } else { + ast_connected.id.number.presentation = ss7_pres_scr2cid_pres( + e->con.connected_presentation_ind, + e->con.connected_screening_ind); + ss7_apply_plan_to_number(connected_num, sizeof(connected_num), + linkset, e->con.connected_num, e->con.connected_nai); + ast_connected.id.number.str = ast_strdup(connected_num); + } + ast_connected.id.number.valid = 1; + ast_connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; + ast_channel_queue_connected_line_update(p->owner, &ast_connected, NULL); + ast_party_connected_line_free(&ast_connected); + ast_channel_unlock(p->owner); + } + } + + sig_ss7_queue_control(linkset, chanpos, AST_CONTROL_ANSWER); + sig_ss7_set_dialing(p, 0); + sig_ss7_open_media(p); + if (((e->e == ISUP_EVENT_ANM) ? !e->anm.echocontrol_ind : + !e->con.echocontrol_ind) || !(linkset->flags & LINKSET_FLAG_USEECHOCONTROL)) { + sig_ss7_set_echocanceller(p, 1); + } + sig_ss7_unlock_private(p); + break; case ISUP_EVENT_RLC: - /* 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) { + isup_free_call(ss7, e->rlc.call); break; } - { - p = linkset->pvts[chanpos]; - sig_ss7_lock_private(p); - callid = func_ss7_linkset_callid(linkset, chanpos); - if (p->alreadyhungup) { - if (!p->owner) { - p->call_level = SIG_SS7_CALL_LEVEL_IDLE; - } - p->ss7call = NULL; + + p = linkset->pvts[chanpos]; + sig_ss7_lock_private(p); + p->ss7call = e->rlc.call; + callid = func_ss7_linkset_callid(linkset, chanpos); + if (e->rlc.got_sent_msg & (ISUP_SENT_RSC | ISUP_SENT_REL)) { + sig_ss7_loopback(p, 0); + if (e->rlc.got_sent_msg & ISUP_SENT_RSC) { + sig_ss7_set_inservice(p, 1); } - sig_ss7_unlock_private(p); } + sig_ss7_lock_owner(linkset, chanpos); + if (!p->owner) { + p->ss7call = isup_free_call_if_clear(ss7, e->rlc.call); + p->call_level = SIG_SS7_CALL_LEVEL_IDLE; + } else { + p->do_hangup = SS7_HANGUP_DO_NOTHING; + ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV); + ast_channel_unlock(p->owner); + } + sig_ss7_unlock_private(p); break; case ISUP_EVENT_FAA: /*! @@ -1350,25 +2253,87 @@ void *ss7_linkset(void *data) */ chanpos = ss7_find_cic_gripe(linkset, e->faa.cic, e->faa.opc, "FAA"); if (chanpos < 0) { - isup_rel(linkset->ss7, e->faa.call, -1); + isup_free_call(ss7, e->faa.call); break; } - { - /* 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); - callid = func_ss7_linkset_callid(linkset, chanpos); - 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"); - } - sig_ss7_unlock_private(p); + + /* XXX FAR and FAA used for something dealing with transfers? */ + p = linkset->pvts[chanpos]; + sig_ss7_lock_private(p); + callid = func_ss7_linkset_callid(linkset, chanpos); + ast_debug(1, "FAA received on CIC %d\n", e->faa.cic); + p->ss7call = isup_free_call_if_clear(ss7, e->faa.call); + sig_ss7_unlock_private(p); + break; + case ISUP_EVENT_CGBA: + chanpos = ss7_find_cic_gripe(linkset, e->cgba.startcic, e->cgba.opc, "CGBA"); + if (chanpos < 0) { /* Never will be true */ + isup_free_call(ss7, e->cgba.call); + break; + } + + ss7_block_cics(linkset, e->cgba.startcic, e->cgba.endcic, + e->cgba.opc, e->cgba.status, 1, 0, + e->cgba.type ? SS7_BLOCKED_HARDWARE : SS7_BLOCKED_MAINTENANCE); + + p = linkset->pvts[chanpos]; + sig_ss7_lock_private(p); + p->ss7call = e->cgba.call; + + if (!p->owner) { + p->ss7call = isup_free_call_if_clear(ss7, p->ss7call); + } + sig_ss7_unlock_private(p); + break; + case ISUP_EVENT_CGUA: + chanpos = ss7_find_cic_gripe(linkset, e->cgua.startcic, e->cgua.opc, "CGUA"); + if (chanpos < 0) { /* Never will be true */ + isup_free_call(ss7, e->cgua.call); + break; + } + + ss7_block_cics(linkset, e->cgua.startcic, e->cgua.endcic, + e->cgua.opc, e->cgua.status, 0, 0, + e->cgba.type ? SS7_BLOCKED_HARDWARE : SS7_BLOCKED_MAINTENANCE); + + p = linkset->pvts[chanpos]; + sig_ss7_lock_private(p); + p->ss7call = e->cgua.call; + + if (!p->owner) { + p->ss7call = isup_free_call_if_clear(ss7, p->ss7call); + } + sig_ss7_unlock_private(p); + break; + case ISUP_EVENT_SUS: + chanpos = ss7_find_cic_gripe(linkset, e->sus.cic, e->sus.opc, "SUS"); + if (chanpos < 0) { + isup_free_call(ss7, e->sus.call); + break; + } + + p = linkset->pvts[chanpos]; + sig_ss7_lock_private(p); + p->ss7call = e->sus.call; + if (!p->owner) { + p->ss7call = isup_free_call_if_clear(ss7, p->ss7call); + } + sig_ss7_unlock_private(p); + break; + case ISUP_EVENT_RES: + chanpos = ss7_find_cic_gripe(linkset, e->res.cic, e->res.opc, "RES"); + if (chanpos < 0) { + isup_free_call(ss7, e->res.call); + break; + } + + p = linkset->pvts[chanpos]; + sig_ss7_lock_private(p); + p->ss7call = e->res.call; + if (!p->owner) { + p->ss7call = isup_free_call_if_clear(ss7, p->ss7call); } + sig_ss7_unlock_private(p); break; default: ast_debug(1, "Unknown event %s\n", ss7_event2str(e->e)); @@ -1387,9 +2352,15 @@ void *ss7_linkset(void *data) return 0; } -static inline void ss7_rel(struct sig_ss7_linkset *ss7) +static void ss7_rel(struct sig_ss7_linkset *ss7) { + /* Release the lock first */ ast_mutex_unlock(&ss7->lock); + + /* Then break the poll to send our messages */ + if (ss7->master != AST_PTHREADT_NULL) { + pthread_kill(ss7->master, SIGURG); + } } static void ss7_grab(struct sig_ss7_chan *pvt, struct sig_ss7_linkset *ss7) @@ -1399,10 +2370,177 @@ static void ss7_grab(struct sig_ss7_chan *pvt, struct sig_ss7_linkset *ss7) /* Avoid deadlock */ sig_ss7_deadlock_avoidance_private(pvt); } - /* Then break the poll */ - if (ss7->master != AST_PTHREADT_NULL) { - pthread_kill(ss7->master, SIGURG); +} + +/*! + * \brief Reset a specific CIC. + * \since 11.0 + * + * \param linkset linkset control structure. + * \param cic Circuit Identification Code + * \param dpc Destination Point Code + * + * \return TRUE on success + */ +int sig_ss7_reset_cic(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc) +{ + int i; + + ast_mutex_lock(&linkset->lock); + for (i = 0; i < linkset->numchans; i++) { + if (linkset->pvts[i] && linkset->pvts[i]->cic == cic && linkset->pvts[i]->dpc == dpc) { + int res; + + sig_ss7_lock_private(linkset->pvts[i]); + sig_ss7_set_locallyblocked(linkset->pvts[i], 0, SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE); + res = ss7_start_rsc(linkset, i); + sig_ss7_unlock_private(linkset->pvts[i]); + ss7_rel(linkset); /* Also breaks the poll to send our messages */ + return res; + } + } + ss7_rel(linkset); + + return 0; +} + +/*! + * \brief Block or Unblock a specific CIC. + * \since 11.0 + * + * \param linkset linkset control structure. + * \param do_block Action to perform. Block if TRUE. + * \param which On which CIC to perform the operation. + * + * \return 0 on success + */ +int sig_ss7_cic_blocking(struct sig_ss7_linkset *linkset, int do_block, int which) +{ + ast_mutex_lock(&linkset->lock); + sig_ss7_lock_private(linkset->pvts[which]); + if (!ss7_find_alloc_call(linkset->pvts[which])) { + sig_ss7_unlock_private(linkset->pvts[which]); + ss7_rel(linkset); + return -1; + } + + if (do_block) { + isup_blo(linkset->ss7, linkset->pvts[which]->ss7call); + } else { + isup_ubl(linkset->ss7, linkset->pvts[which]->ss7call); + } + + sig_ss7_unlock_private(linkset->pvts[which]); + ss7_rel(linkset); /* Also breaks the poll to send our messages */ + + return 0; +} + +/*! + * \brief Block or Unblock a range of CICs. + * \since 11.0 + * + * \param linkset linkset control structure. + * \param do_block Action to perform. Block if TRUE. + * \param chanpos Channel position to start from. + * \param endcic Circuit Identification Code of the end of the range. + * \param state Array of CIC blocking status. + * \param type Type of the blocking - maintenance or hardware + * + * \note Assumes the linkset->lock is already obtained. + * + * \return 0 on success + */ +int sig_ss7_group_blocking(struct sig_ss7_linkset *linkset, int do_block, int chanpos, int endcic, unsigned char state[], int type) +{ + sig_ss7_lock_private(linkset->pvts[chanpos]); + if (!ss7_find_alloc_call(linkset->pvts[chanpos])) { + sig_ss7_unlock_private(linkset->pvts[chanpos]); + return -1; + } + + if (do_block) { + isup_cgb(linkset->ss7, linkset->pvts[chanpos]->ss7call, endcic, state, type); + } else { + isup_cgu(linkset->ss7, linkset->pvts[chanpos]->ss7call, endcic, state, type); + } + + sig_ss7_unlock_private(linkset->pvts[chanpos]); + return 0; +} + +/*! + * \brief Reset a group of CICs. + * \since 11.0 + * + * \param linkset linkset control structure. + * \param cic Circuit Identification Code + * \param dpc Destination Point Code + * \param range Range of the CICs to reset + * + * \note Assumes the linkset->lock is already obtained. + * + * \return 0 on success + */ +int sig_ss7_reset_group(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc, int range) +{ + int i; + + for (i = 0; i < linkset->numchans; i++) { + if (linkset->pvts[i] && linkset->pvts[i]->cic == cic && linkset->pvts[i]->dpc == dpc) { + ss7_clear_channels(linkset, cic, cic + range, dpc, SS7_HANGUP_FREE_CALL); + ss7_block_cics(linkset, cic, cic + range, dpc, NULL, 0, 1, + SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE); + ss7_block_cics(linkset, cic, cic + range, dpc, NULL, 0, 0, + SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE); + + sig_ss7_lock_private(linkset->pvts[i]); + if (!ss7_find_alloc_call(linkset->pvts[i])) { + sig_ss7_unlock_private(linkset->pvts[i]); + return -1; + } + isup_grs(linkset->ss7, linkset->pvts[i]->ss7call, linkset->pvts[i]->cic + range); + sig_ss7_unlock_private(linkset->pvts[i]); + break; + } } + return 0; +} + +void sig_ss7_free_isup_call(struct sig_ss7_linkset *linkset, int channel) +{ + sig_ss7_lock_private(linkset->pvts[channel]); + if (linkset->pvts[channel]->ss7call) { + isup_free_call(linkset->ss7, linkset->pvts[channel]->ss7call); + linkset->pvts[channel]->ss7call = NULL; + } + sig_ss7_unlock_private(linkset->pvts[channel]); +} + +static int ss7_parse_prefix(struct sig_ss7_chan *p, const char *number, char *nai) +{ + int strip = 0; + + if (strncmp(number, p->ss7->internationalprefix, strlen(p->ss7->internationalprefix)) == 0) { + strip = strlen(p->ss7->internationalprefix); + *nai = SS7_NAI_INTERNATIONAL; + } else if (strncmp(number, p->ss7->nationalprefix, strlen(p->ss7->nationalprefix)) == 0) { + strip = strlen(p->ss7->nationalprefix); + *nai = SS7_NAI_NATIONAL; + } else if (strncmp(number, p->ss7->networkroutedprefix, strlen(p->ss7->networkroutedprefix)) == 0) { + strip = strlen(p->ss7->networkroutedprefix); + *nai = SS7_NAI_NETWORKROUTED; + } else if (strncmp(number, p->ss7->unknownprefix, strlen(p->ss7->unknownprefix)) == 0) { + strip = strlen(p->ss7->unknownprefix); + *nai = SS7_NAI_UNKNOWN; + } else if (strncmp(number, p->ss7->subscriberprefix, strlen(p->ss7->subscriberprefix)) == 0) { + strip = strlen(p->ss7->subscriberprefix); + *nai = SS7_NAI_SUBSCRIBER; + } else { + *nai = SS7_NAI_SUBSCRIBER; + } + + return strip; } /*! @@ -1453,7 +2591,7 @@ void sig_ss7_link_noalarm(struct sig_ss7_linkset *linkset, int which) * \retval 0 on success. * \retval -1 on error. */ -int sig_ss7_add_sigchan(struct sig_ss7_linkset *linkset, int which, int ss7type, int transport, int inalarm, int networkindicator, int pointcode, int adjpointcode) +int sig_ss7_add_sigchan(struct sig_ss7_linkset *linkset, int which, int ss7type, int transport, int inalarm, int networkindicator, int pointcode, int adjpointcode, int cur_slc) { if (!linkset->ss7) { linkset->type = ss7type; @@ -1467,7 +2605,7 @@ int sig_ss7_add_sigchan(struct sig_ss7_linkset *linkset, int which, int ss7type, ss7_set_network_ind(linkset->ss7, networkindicator); ss7_set_pc(linkset->ss7, pointcode); - if (ss7_add_link(linkset->ss7, transport, linkset->fds[which])) { + if (ss7_add_link(linkset->ss7, transport, linkset->fds[which], cur_slc, adjpointcode)) { ast_log(LOG_WARNING, "Could not add SS7 link!\n"); } @@ -1479,8 +2617,6 @@ int sig_ss7_add_sigchan(struct sig_ss7_linkset *linkset, int which, int ss7type, ss7_link_noalarm(linkset->ss7, linkset->fds[which]); } - ss7_set_adjpc(linkset->ss7, linkset->fds[which], adjpointcode); - return 0; } @@ -1505,7 +2641,13 @@ int sig_ss7_available(struct sig_ss7_chan *p) ast_mutex_lock(&p->ss7->lock); available = sig_ss7_is_chan_available(p); if (available) { - p->call_level = SIG_SS7_CALL_LEVEL_ALLOCATED; + p->ss7call = isup_new_call(p->ss7->ss7, p->cic, p->dpc, 1); + if (!p->ss7call) { + ast_log(LOG_ERROR, "Unable to allocate new SS7 call!\n"); + available = 0; + } else { + p->call_level = SIG_SS7_CALL_LEVEL_ALLOCATED; + } } ast_mutex_unlock(&p->ss7->lock); @@ -1522,6 +2664,159 @@ static unsigned char cid_pres2ss7screen(int cid_pres) return cid_pres & 0x03; } +static void ss7_connected_line_update(struct sig_ss7_chan *p, struct ast_party_connected_line *connected) +{ + int connected_strip = 0; + char connected_nai; + unsigned char connected_pres; + unsigned char connected_screen; + const char *connected_num; + + if (!connected->id.number.valid) { + return; + } + + connected_num = S_OR(connected->id.number.str, ""); + if (p->ss7->called_nai == SS7_NAI_DYNAMIC) { + connected_strip = ss7_parse_prefix(p, connected_num, &connected_nai); + } else { + connected_nai = p->ss7->called_nai; + } + + connected_pres = cid_pres2ss7pres(connected->id.number.presentation); + connected_screen = cid_pres2ss7screen(connected->id.number.presentation); + + isup_set_connected(p->ss7call, connected_num + connected_strip, connected_nai, connected_pres, connected_screen); +} + +static unsigned char ss7_redirect_reason(struct sig_ss7_chan *p, struct ast_party_redirecting *redirecting, int orig) +{ + int reason = (orig) ? redirecting->orig_reason.code : redirecting->reason.code; + + switch (reason) { + case AST_REDIRECTING_REASON_USER_BUSY: + return SS7_REDIRECTING_REASON_USER_BUSY; + case AST_REDIRECTING_REASON_NO_ANSWER: + return SS7_REDIRECTING_REASON_NO_ANSWER; + case AST_REDIRECTING_REASON_UNCONDITIONAL: + return SS7_REDIRECTING_REASON_UNCONDITIONAL; + } + + if (orig || reason == AST_REDIRECTING_REASON_UNKNOWN) { + return SS7_REDIRECTING_REASON_UNKNOWN; + } + + if (reason == AST_REDIRECTING_REASON_UNAVAILABLE) { + return SS7_REDIRECTING_REASON_UNAVAILABLE; + } + + if (reason == AST_REDIRECTING_REASON_DEFLECTION) { + if (p->call_level > SIG_SS7_CALL_LEVEL_PROCEEDING) { + return SS7_REDIRECTING_REASON_DEFLECTION_DURING_ALERTING; + } + return SS7_REDIRECTING_REASON_DEFLECTION_IMMEDIATE_RESPONSE; + } + + return SS7_REDIRECTING_REASON_UNKNOWN; +} + +static unsigned char ss7_redirect_info_ind(struct ast_channel *ast) +{ + const char *redirect_info_ind; + struct ast_party_redirecting *redirecting = ast_channel_redirecting(ast); + + redirect_info_ind = pbx_builtin_getvar_helper(ast, "SS7_REDIRECT_INFO_IND"); + if (!ast_strlen_zero(redirect_info_ind)) { + if (!strcasecmp(redirect_info_ind, "CALL_REROUTED_PRES_ALLOWED")) { + return SS7_INDICATION_REROUTED_PRES_ALLOWED; + } + if (!strcasecmp(redirect_info_ind, "CALL_REROUTED_INFO_RESTRICTED")) { + return SS7_INDICATION_REROUTED_INFO_RESTRICTED; + } + if (!strcasecmp(redirect_info_ind, "CALL_DIVERTED_PRES_ALLOWED")) { + return SS7_INDICATION_DIVERTED_PRES_ALLOWED; + } + if (!strcasecmp(redirect_info_ind, "CALL_DIVERTED_INFO_RESTRICTED")) { + return SS7_INDICATION_DIVERTED_INFO_RESTRICTED; + } + if (!strcasecmp(redirect_info_ind, "CALL_REROUTED_PRES_RESTRICTED")) { + return SS7_INDICATION_REROUTED_PRES_RESTRICTED; + } + if (!strcasecmp(redirect_info_ind, "CALL_DIVERTED_PRES_RESTRICTED")) { + return SS7_INDICATION_DIVERTED_PRES_RESTRICTED; + } + if (!strcasecmp(redirect_info_ind, "SPARE")) { + return SS7_INDICATION_SPARE; + } + return SS7_INDICATION_NO_REDIRECTION; + } + + if (redirecting->reason.code == AST_REDIRECTING_REASON_DEFLECTION) { + if ((redirecting->to.number.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) { + if ((redirecting->orig.number.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) { + return SS7_INDICATION_DIVERTED_PRES_ALLOWED; + } + return SS7_INDICATION_DIVERTED_PRES_RESTRICTED; + } + return SS7_INDICATION_DIVERTED_INFO_RESTRICTED; + } + + if ((redirecting->to.number.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) { + if ((redirecting->orig.number.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) { + return SS7_INDICATION_REROUTED_PRES_ALLOWED; + } + return SS7_INDICATION_REROUTED_PRES_RESTRICTED; + } + return SS7_INDICATION_REROUTED_INFO_RESTRICTED; +} + +static void ss7_redirecting_update(struct sig_ss7_chan *p, struct ast_channel *ast) +{ + int num_nai_strip = 0; + struct ast_party_redirecting *redirecting = ast_channel_redirecting(ast); + + if (!redirecting->count) { + return; + } + + isup_set_redirect_counter(p->ss7call, redirecting->count); + + if (redirecting->orig.number.valid) { + char ss7_orig_called_nai = p->ss7->called_nai; + const char *ss7_orig_called_num = S_OR(redirecting->orig.number.str, ""); + + if (ss7_orig_called_nai == SS7_NAI_DYNAMIC) { + num_nai_strip = ss7_parse_prefix(p, ss7_orig_called_num, &ss7_orig_called_nai); + } else { + num_nai_strip = 0; + } + isup_set_orig_called_num(p->ss7call, ss7_orig_called_num + num_nai_strip, + ss7_orig_called_nai, + cid_pres2ss7pres(redirecting->orig.number.presentation), + cid_pres2ss7screen(redirecting->orig.number.presentation)); + } + + if (redirecting->from.number.valid) { + char ss7_redirecting_num_nai = p->ss7->calling_nai; + const char *redirecting_number = S_OR(redirecting->from.number.str, ""); + + if (ss7_redirecting_num_nai == SS7_NAI_DYNAMIC) { + num_nai_strip = ss7_parse_prefix(p, redirecting_number, &ss7_redirecting_num_nai); + } else { + num_nai_strip = 0; + } + + isup_set_redirecting_number(p->ss7call, redirecting_number + num_nai_strip, + ss7_redirecting_num_nai, + cid_pres2ss7pres(redirecting->from.number.presentation), + cid_pres2ss7screen(redirecting->from.number.presentation)); + } + + isup_set_redirection_info(p->ss7call, ss7_redirect_info_ind(ast), + ss7_redirect_reason(p, ast_channel_redirecting(ast), 1), + redirecting->count, ss7_redirect_reason(p, ast_channel_redirecting(ast), 0)); +} + /*! * \brief Dial out using the specified SS7 channel. * \since 1.8 @@ -1539,6 +2834,13 @@ int sig_ss7_call(struct sig_ss7_chan *p, struct ast_channel *ast, const char *rd int called_nai_strip; char ss7_calling_nai; int calling_nai_strip; + const char *col_req = NULL; + const char *ss7_cug_indicator_str; + const char *ss7_cug_interlock_ni; + const char *ss7_cug_interlock_code; + const char *ss7_interworking_indicator; + const char *ss7_forward_indicator_pmbits; + unsigned char ss7_cug_indicator; const char *charge_str = NULL; const char *gen_address = NULL; const char *gen_digits = NULL; @@ -1551,6 +2853,7 @@ int sig_ss7_call(struct sig_ss7_chan *p, struct ast_channel *ast, const char *rd const char *call_ref_id = NULL; const char *call_ref_pc = NULL; const char *send_far = NULL; + const char *tmr = NULL; char *c; char *l; char dest[256]; @@ -1582,47 +2885,27 @@ int sig_ss7_call(struct sig_ss7_chan *p, struct ast_channel *ast, const char *rd return -1; } - p->ss7call = isup_new_call(p->ss7->ss7); - if (!p->ss7call) { - ss7_rel(p->ss7); - ast_log(LOG_ERROR, "Unable to allocate new SS7 call!\n"); - return -1; - } - called_nai_strip = 0; ss7_called_nai = p->ss7->called_nai; if (ss7_called_nai == SS7_NAI_DYNAMIC) { /* compute dynamically */ - if (strncmp(c + p->stripmsd, p->ss7->internationalprefix, strlen(p->ss7->internationalprefix)) == 0) { - called_nai_strip = strlen(p->ss7->internationalprefix); - ss7_called_nai = SS7_NAI_INTERNATIONAL; - } else if (strncmp(c + p->stripmsd, p->ss7->nationalprefix, strlen(p->ss7->nationalprefix)) == 0) { - called_nai_strip = strlen(p->ss7->nationalprefix); - ss7_called_nai = SS7_NAI_NATIONAL; - } else { - ss7_called_nai = SS7_NAI_SUBSCRIBER; - } + called_nai_strip = ss7_parse_prefix(p, c + p->stripmsd, &ss7_called_nai); } isup_set_called(p->ss7call, c + p->stripmsd + called_nai_strip, ss7_called_nai, p->ss7->ss7); calling_nai_strip = 0; ss7_calling_nai = p->ss7->calling_nai; if ((l != NULL) && (ss7_calling_nai == SS7_NAI_DYNAMIC)) { /* compute dynamically */ - if (strncmp(l, p->ss7->internationalprefix, strlen(p->ss7->internationalprefix)) == 0) { - calling_nai_strip = strlen(p->ss7->internationalprefix); - ss7_calling_nai = SS7_NAI_INTERNATIONAL; - } else if (strncmp(l, p->ss7->nationalprefix, strlen(p->ss7->nationalprefix)) == 0) { - calling_nai_strip = strlen(p->ss7->nationalprefix); - ss7_calling_nai = SS7_NAI_NATIONAL; - } else { - ss7_calling_nai = SS7_NAI_SUBSCRIBER; - } + calling_nai_strip = ss7_parse_prefix(p, l, &ss7_calling_nai); } + isup_set_calling(p->ss7call, l ? (l + calling_nai_strip) : NULL, ss7_calling_nai, - p->use_callingpres ? cid_pres2ss7pres(ast_channel_connected(ast)->id.number.presentation) : (l ? SS7_PRESENTATION_ALLOWED : SS7_PRESENTATION_RESTRICTED), + p->use_callingpres ? cid_pres2ss7pres(ast_channel_connected(ast)->id.number.presentation) + : (l ? SS7_PRESENTATION_ALLOWED + : (ast_channel_connected(ast)->id.number.presentation == AST_PRES_UNAVAILABLE + ? SS7_PRESENTATION_ADDR_NOT_AVAILABLE : SS7_PRESENTATION_RESTRICTED)), p->use_callingpres ? cid_pres2ss7screen(ast_channel_connected(ast)->id.number.presentation) : SS7_SCREENING_USER_PROVIDED); isup_set_oli(p->ss7call, ast_channel_connected(ast)->ani2); - isup_init_call(p->ss7->ss7, p->ss7call, p->cic, p->dpc); /* Set the charge number if it is set */ charge_str = pbx_builtin_getvar_helper(ast, "SS7_CHARGE_NUMBER"); @@ -1664,10 +2947,66 @@ int sig_ss7_call(struct sig_ss7_chan *p, struct ast_channel *ast, const char *rd } send_far = pbx_builtin_getvar_helper(ast, "SS7_SEND_FAR"); - if ((send_far) && ((strncmp("NO", send_far, strlen(send_far))) != 0 )) - (isup_far(p->ss7->ss7, p->ss7call)); + if (send_far && strncmp("NO", send_far, strlen(send_far)) != 0) { + isup_far(p->ss7->ss7, p->ss7call); + } + + tmr = pbx_builtin_getvar_helper(ast, "SS7_TMR_NUM"); + if (tmr) { + isup_set_tmr(p->ss7call, atoi(tmr)); + } else if ((tmr = pbx_builtin_getvar_helper(ast, "SS7_TMR")) && tmr[0] != '\0') { + if (!strcasecmp(tmr, "SPEECH")) { + isup_set_tmr(p->ss7call, SS7_TMR_SPEECH); + } else if (!strcasecmp(tmr, "SPARE")) { + isup_set_tmr(p->ss7call, SS7_TMR_SPARE); + } else if (!strcasecmp(tmr, "3K1_AUDIO")) { + isup_set_tmr(p->ss7call, SS7_TMR_3K1_AUDIO); + } else if (!strcasecmp(tmr, "64K_UNRESTRICTED")) { + isup_set_tmr(p->ss7call, SS7_TMR_64K_UNRESTRICTED); + } else { + isup_set_tmr(p->ss7call, SS7_TMR_N64K_OR_SPARE); + } + } + + col_req = pbx_builtin_getvar_helper(ast, "SS7_COL_REQUEST"); + if (ast_true(col_req)) { + isup_set_col_req(p->ss7call); + } + + ss7_cug_indicator_str = pbx_builtin_getvar_helper(ast, "SS7_CUG_INDICATOR"); + if (!ast_strlen_zero(ss7_cug_indicator_str)) { + if (!strcasecmp(ss7_cug_indicator_str, "OUTGOING_ALLOWED")) { + ss7_cug_indicator = ISUP_CUG_OUTGOING_ALLOWED; + } else if (!strcasecmp(ss7_cug_indicator_str, "OUTGOING_NOT_ALLOWED")) { + ss7_cug_indicator = ISUP_CUG_OUTGOING_NOT_ALLOWED; + } else { + ss7_cug_indicator = ISUP_CUG_NON; + } + + if (ss7_cug_indicator != ISUP_CUG_NON) { + ss7_cug_interlock_code = pbx_builtin_getvar_helper(ast, "SS7_CUG_INTERLOCK_CODE"); + ss7_cug_interlock_ni = pbx_builtin_getvar_helper(ast, "SS7_CUG_INTERLOCK_NI"); + if (ss7_cug_interlock_code && ss7_cug_interlock_ni && strlen(ss7_cug_interlock_ni) == 4) { + isup_set_cug(p->ss7call, ss7_cug_indicator, ss7_cug_interlock_ni, atoi(ss7_cug_interlock_code)); + } + } + } + + ss7_redirecting_update(p, ast); + + isup_set_echocontrol(p->ss7call, (p->ss7->flags & LINKSET_FLAG_DEFAULTECHOCONTROL) ? 1 : 0); + ss7_interworking_indicator = pbx_builtin_getvar_helper(ast, "SS7_INTERWORKING_INDICATOR"); + if (ss7_interworking_indicator) { + isup_set_interworking_indicator(p->ss7call, ast_true(ss7_interworking_indicator)); + } + + ss7_forward_indicator_pmbits = pbx_builtin_getvar_helper(ast, "SS7_FORWARD_INDICATOR_PMBITS"); + if (ss7_forward_indicator_pmbits) { + isup_set_forward_indicator_pmbits(p->ss7call, atoi(ss7_forward_indicator_pmbits)); + } p->call_level = SIG_SS7_CALL_LEVEL_SETUP; + p->do_hangup = SS7_HANGUP_SEND_REL; isup_iam(p->ss7->ss7, p->ss7call); sig_ss7_set_dialing(p, 1); ast_setstate(ast, AST_STATE_DIALING); @@ -1687,8 +3026,6 @@ int sig_ss7_call(struct sig_ss7_chan *p, struct ast_channel *ast, const char *rd */ int sig_ss7_hangup(struct sig_ss7_chan *p, struct ast_channel *ast) { - int res = 0; - if (!ast_channel_tech_pvt(ast)) { ast_log(LOG_WARNING, "Asked to hangup channel not connected\n"); return 0; @@ -1704,22 +3041,51 @@ int sig_ss7_hangup(struct sig_ss7_chan *p, struct ast_channel *ast) ss7_grab(p, p->ss7); p->call_level = SIG_SS7_CALL_LEVEL_IDLE; if (p->ss7call) { - if (!p->alreadyhungup) { - const char *cause = pbx_builtin_getvar_helper(ast,"SS7_CAUSE"); - int icause = ast_channel_hangupcause(ast) ? ast_channel_hangupcause(ast) : -1; - - if (cause) { - if (atoi(cause)) { - icause = atoi(cause); + switch (p->do_hangup) { + case SS7_HANGUP_SEND_REL: + { + const char *cause = pbx_builtin_getvar_helper(ast,"SS7_CAUSE"); + int icause = ast_channel_hangupcause(ast) ? ast_channel_hangupcause(ast) : -1; + + if (cause) { + if (atoi(cause)) { + icause = atoi(cause); + } + } + if (icause > 255) { + icause = 16; } + + isup_rel(p->ss7->ss7, p->ss7call, icause); + p->do_hangup = SS7_HANGUP_DO_NOTHING; } - isup_rel(p->ss7->ss7, p->ss7call, icause); - p->alreadyhungup = 1; + break; + case SS7_HANGUP_SEND_RSC: + ss7_do_rsc(p); + p->do_hangup = SS7_HANGUP_DO_NOTHING; + break; + case SS7_HANGUP_SEND_RLC: + isup_rlc(p->ss7->ss7, p->ss7call); + p->do_hangup = SS7_HANGUP_DO_NOTHING; + p->ss7call = isup_free_call_if_clear(p->ss7->ss7, p->ss7call); + break; + case SS7_HANGUP_FREE_CALL: + p->do_hangup = SS7_HANGUP_DO_NOTHING; + isup_free_call(p->ss7->ss7, p->ss7call); + p->ss7call = NULL; + break; + case SS7_HANGUP_REEVENT_IAM: + isup_event_iam(p->ss7->ss7, p->ss7call, p->dpc); + p->do_hangup = SS7_HANGUP_SEND_REL; + break; + case SS7_HANGUP_DO_NOTHING: + p->ss7call = isup_free_call_if_clear(p->ss7->ss7, p->ss7call); + break; } } ss7_rel(p->ss7); - return res; + return 0; } /*! @@ -1738,10 +3104,14 @@ int sig_ss7_answer(struct sig_ss7_chan *p, struct ast_channel *ast) ss7_grab(p, p->ss7); if (p->call_level < SIG_SS7_CALL_LEVEL_CONNECT) { + if (p->call_level < SIG_SS7_CALL_LEVEL_PROCEEDING && (p->ss7->flags & LINKSET_FLAG_AUTOACM)) { + isup_acm(p->ss7->ss7, p->ss7call); + } p->call_level = SIG_SS7_CALL_LEVEL_CONNECT; } - sig_ss7_open_media(p); + res = isup_anm(p->ss7->ss7, p->ss7call); + sig_ss7_open_media(p); ss7_rel(p->ss7); return res; } @@ -1764,7 +3134,7 @@ void sig_ss7_fixup(struct ast_channel *oldchan, struct ast_channel *newchan, str } /*! - * \brief SS7 answer channel. + * \brief SS7 indication. * \since 1.8 * * \param p Signaling private structure pointer. @@ -1793,15 +3163,20 @@ int sig_ss7_indicate(struct sig_ss7_chan *p, struct ast_channel *chan, int condi 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 ((isup_far(p->ss7->ss7, p->ss7call)) != -1) { p->rlt = 1; } + if (p->call_level < SIG_SS7_CALL_LEVEL_PROCEEDING && (p->ss7->flags & LINKSET_FLAG_AUTOACM)) { + isup_acm(p->ss7->ss7, p->ss7call); + } + /* No need to send CPG if call will be RELEASE */ if (p->rlt != 1) { isup_cpg(p->ss7->ss7, p->ss7call, CPG_EVENT_ALERTING); } + + p->call_level = SIG_SS7_CALL_LEVEL_ALERTING; } ss7_rel(p->ss7); @@ -1833,15 +3208,15 @@ int sig_ss7_indicate(struct sig_ss7_chan *p, struct ast_channel *chan, int condi ast_debug(1,"Received AST_CONTROL_PROGRESS on %s\n",ast_channel_name(chan)); 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. */ + p->progress = 1; /* No need to send inband-information progress again. */ 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); + if (!p->echocontrol_ind || !(p->ss7->flags & LINKSET_FLAG_USEECHOCONTROL)) { + sig_ss7_set_echocanceller(p, 1); + } } + ss7_rel(p->ss7); /* don't continue in ast_indicate */ res = 0; break; @@ -1873,6 +3248,14 @@ int sig_ss7_indicate(struct sig_ss7_chan *p, struct ast_channel *chan, int condi case AST_CONTROL_SRCUPDATE: res = 0; break; + case AST_CONTROL_CONNECTED_LINE: + ss7_connected_line_update(p, ast_channel_connected(chan)); + res = 0; + break; + case AST_CONTROL_REDIRECTING: + ss7_redirecting_update(p, chan); + res = 0; + break; case -1: res = sig_ss7_play_tone(p, -1); break; @@ -1914,6 +3297,7 @@ struct ast_channel *sig_ss7_request(struct sig_ss7_chan *p, enum sig_ss7_law law /* 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; + isup_free_call(p->ss7->ss7, p->ss7call); ast_mutex_unlock(&p->ss7->lock); } return ast; diff --git a/channels/sig_ss7.h b/channels/sig_ss7.h index 56d393c81efd4b0d1651fd4d246363685b86a367..e2bc8e4361d25c1f8a0ed81cca2b4a5fece669e0 100644 --- a/channels/sig_ss7.h +++ b/channels/sig_ss7.h @@ -66,6 +66,13 @@ extern "C" { #define SS7_NAI_DYNAMIC -1 #define LINKSET_FLAG_EXPLICITACM (1 << 0) +#define LINKSET_FLAG_INITIALHWBLO (1 << 1) +#define LINKSET_FLAG_USEECHOCONTROL (1 << 2) +#define LINKSET_FLAG_DEFAULTECHOCONTROL (1 << 3) +#define LINKSET_FLAG_AUTOACM (1 << 4) + +#define SS7_BLOCKED_MAINTENANCE (1 << 0) +#define SS7_BLOCKED_HARDWARE (1 << 1) enum sig_ss7_tone { @@ -84,6 +91,27 @@ enum sig_ss7_law { SIG_SS7_ALAW }; +enum sig_ss7_redirect_idication { + SS7_INDICATION_NO_REDIRECTION = 0, + SS7_INDICATION_REROUTED_PRES_ALLOWED, + SS7_INDICATION_REROUTED_INFO_RESTRICTED, + SS7_INDICATION_DIVERTED_PRES_ALLOWED, + SS7_INDICATION_DIVERTED_INFO_RESTRICTED, + SS7_INDICATION_REROUTED_PRES_RESTRICTED, + SS7_INDICATION_DIVERTED_PRES_RESTRICTED, + SS7_INDICATION_SPARE +}; + +enum sig_ss7_redirect_reason { + SS7_REDIRECTING_REASON_UNKNOWN = 0, + SS7_REDIRECTING_REASON_USER_BUSY, + SS7_REDIRECTING_REASON_NO_ANSWER, + SS7_REDIRECTING_REASON_UNCONDITIONAL, + SS7_REDIRECTING_REASON_DEFLECTION_DURING_ALERTING, + SS7_REDIRECTING_REASON_DEFLECTION_IMMEDIATE_RESPONSE, + SS7_REDIRECTING_REASON_UNAVAILABLE +}; + /*! Call establishment life cycle level for simple comparisons. */ enum sig_ss7_call_level { /*! Call does not exist. */ @@ -118,8 +146,6 @@ enum sig_ss7_call_level { * 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; @@ -153,6 +179,8 @@ struct sig_ss7_callback { void (* const queue_control)(void *pvt, int subclass); void (* const open_media)(void *pvt); + + struct sig_ss7_linkset *(* const find_linkset)(struct ss7 *ss7); }; /*! Global sig_ss7 callbacks to the upper layer. */ @@ -192,10 +220,19 @@ struct sig_ss7_chan { unsigned int use_callingpres:1; unsigned int immediate:1; /*!< Answer before getting digits? */ - /*! \brief TRUE if the channel is locally blocked. Set by user and link. */ - unsigned int locallyblocked:1; - /*! \brief TRUE if the channel is remotely blocked. Set by user and link. */ - unsigned int remotelyblocked:1; + /*! + * \brief Bitmask for the channel being locally blocked. + * \note 1 maintenance blocked, 2 blocked in hardware. + * \note Set by user and link. + */ + unsigned int locallyblocked:2; + + /*! + * \brief Bitmask for the channel being remotely blocked. + * \note 1 maintenance blocked, 2 blocked in hardware. + * \note Set by user and link. + */ + unsigned int remotelyblocked:2; char context[AST_MAX_CONTEXT]; char mohinterpret[MAX_MUSICCLASS]; @@ -215,7 +252,15 @@ struct sig_ss7_chan { char gen_add_number[50]; char gen_dig_number[50]; char orig_called_num[50]; + int orig_called_presentation; char redirecting_num[50]; + int redirecting_presentation; + unsigned char redirect_counter; + unsigned char redirect_info; + unsigned char redirect_info_ind; + unsigned char redirect_info_orig_reas; + unsigned char redirect_info_counter; + unsigned char redirect_info_reas; char generic_name[50]; unsigned char gen_add_num_plan; unsigned char gen_add_nai; @@ -233,22 +278,41 @@ struct sig_ss7_chan { unsigned int call_ref_ident; unsigned int call_ref_pc; unsigned char calling_party_cat; + unsigned int do_hangup; /* What we have to do to clear the call */ + unsigned int echocontrol_ind; /* * Channel status bits. */ - /*! TRUE if channel is associated with a link that is down. */ + /*! \brief TRUE if channel is associated with a link that is down. */ unsigned int inalarm:1; - /*! TRUE if this channel is being used for an outgoing call. */ + /*! \brief TRUE if channel is in service. */ + unsigned int inservice:1; + /*! \brief TRUE if this channel is being used for an outgoing call. */ unsigned int outgoing:1; + /*! \brief TRUE if the channel has completed collecting digits. */ + unsigned int called_complete:1; /*! \brief TRUE if the call has seen inband-information progress through the network. */ unsigned int progress:1; - /*! \brief TRUE if the call has already gone/hungup */ - unsigned int alreadyhungup:1; /*! \brief XXX BOOLEAN Purpose??? */ unsigned int rlt:1; - /*! TRUE if this channel is in loopback. */ + /*! \brief TRUE if this channel is in loopback. */ unsigned int loopedback:1; + + /* + * Closed User Group fields Q.735.1 + */ + /*! \brief Network Identify Code as per Q.763 3.15.a */ + char cug_interlock_ni[5]; + /*! \brief Binari Code to uniquely identify a CUG inside the network. */ + unsigned short cug_interlock_code; + /*! + * \brief Indication of the call being a CUG call and its permissions. + * \note 0 or 1 - non-CUG call + * \note 2 - CUG call, outgoing access alowed + * \note 3 - CUG call, outgoing access not alowed + */ + unsigned char cug_indicator; }; struct sig_ss7_linkset { @@ -276,6 +340,7 @@ struct sig_ss7_linkset { char nationalprefix[10]; /*!< area access code ('0' for european dialplans) */ char subscriberprefix[20]; /*!< area access code + area code ('0'+area code for european dialplans) */ char unknownprefix[20]; /*!< for unknown dialplans */ + char networkroutedprefix[20]; }; void sig_ss7_set_alarm(struct sig_ss7_chan *p, int in_alarm); @@ -284,12 +349,19 @@ void *ss7_linkset(void *data); void sig_ss7_link_alarm(struct sig_ss7_linkset *linkset, int which); void sig_ss7_link_noalarm(struct sig_ss7_linkset *linkset, int which); -int sig_ss7_add_sigchan(struct sig_ss7_linkset *linkset, int which, int ss7type, int transport, int inalarm, int networkindicator, int pointcode, int adjpointcode); +int sig_ss7_add_sigchan(struct sig_ss7_linkset *linkset, int which, int ss7type, int transport, int inalarm, int networkindicator, int pointcode, int adjpointcode, int cur_slc); + +int sig_ss7_reset_cic(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc); +int sig_ss7_reset_group(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc, int range); +int sig_ss7_cic_blocking(struct sig_ss7_linkset *linkset, int do_block, int cic); +int sig_ss7_group_blocking(struct sig_ss7_linkset *linkset, int do_block, int startcic, int endcic, unsigned char state[], int type); int sig_ss7_available(struct sig_ss7_chan *p); int sig_ss7_call(struct sig_ss7_chan *p, struct ast_channel *ast, const char *rdest); int sig_ss7_hangup(struct sig_ss7_chan *p, struct ast_channel *ast); int sig_ss7_answer(struct sig_ss7_chan *p, struct ast_channel *ast); +int sig_ss7_find_cic(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc); +int sig_ss7_find_cic_range(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc); void sig_ss7_fixup(struct ast_channel *oldchan, struct ast_channel *newchan, struct sig_ss7_chan *pchan); int sig_ss7_indicate(struct sig_ss7_chan *p, struct ast_channel *chan, int condition, const void *data, size_t datalen); struct ast_channel *sig_ss7_request(struct sig_ss7_chan *p, enum sig_ss7_law law, @@ -298,10 +370,14 @@ struct ast_channel *sig_ss7_request(struct sig_ss7_chan *p, enum sig_ss7_law law void sig_ss7_chan_delete(struct sig_ss7_chan *doomed); struct sig_ss7_chan *sig_ss7_chan_new(void *pvt_data, struct sig_ss7_linkset *ss7); void sig_ss7_init_linkset(struct sig_ss7_linkset *ss7); +void sig_ss7_free_isup_call(struct sig_ss7_linkset *linkset, int channel); void sig_ss7_cli_show_channels_header(int fd); void sig_ss7_cli_show_channels(int fd, struct sig_ss7_linkset *linkset); +int sig_ss7_cb_hangup(struct ss7 *ss7, int cic, unsigned int dpc, int cause, int do_hangup); +void sig_ss7_cb_call_null(struct ss7 *ss7, struct isup_call *c, int lock); +void sig_ss7_cb_notinservice(struct ss7 *ss7, int cic, unsigned int dpc); /* ------------------------------------------------------------------- */ diff --git a/configs/chan_dahdi.conf.sample b/configs/chan_dahdi.conf.sample index 83701ef0013856fc3537bf4ff9addd167809845f..2c0488aec8bbd7232304bf827a39da714fe6702b 100644 --- a/configs/chan_dahdi.conf.sample +++ b/configs/chan_dahdi.conf.sample @@ -1371,12 +1371,35 @@ pickupgroup=1 ; This option is used to disable automatic sending of ACM when the call is started ; in the dialplan. If you do use this option, you will need to use the Proceeding() -; application in the dialplan to send ACM. -;ss7_explictacm=yes +; application in the dialplan to send ACM or enable ss7_autoacm below. +;ss7_explicitacm=yes + +; Use this option to automatically send ACM when the call rings or is answered and +; has not seen proceeding yet. If you use this option, you should disable ss7_explicitacm. +; You may still use Proceeding() to explicitly send an ACM from the dialplan. +;ss7_autoacm=yes + +; Create the linkset with all CICs in hardware remotely blocked state. +;ss7_initialhwblo=yes + +; This option is whether or not to trust the remote echo control indication. This means +; that in cases where echo control is reported by the remote end, we will trust them and +; not enable echo cancellation on the call. +;ss7_use_echocontrol=yes + +; This option is to set what our echo control indication is to the other end. Set to +; yes to indicate that we are using echo cancellation or no if we are not. +;ss7_default_echocontrol=yes ; All settings apply to linkset 1 ;linkset = 1 +; Set the Signaling Link Code (SLC) for each sigchan. +; If you manually set any you need to manually set all. +; Should be defined before sigchan. +; The default SLC starts with zero and increases for each defined sigchan. +;slc= + ; Point code of the linkset. For ITU, this is the decimal number ; format of the point code. For ANSI, this can either be in decimal ; number format or in the xxx-xxx-xxx format @@ -1410,6 +1433,30 @@ pickupgroup=1 ; Channels to associate with CICs on this linkset ;channel = 25-47 ; + +; Set this option if you wish to send an Information Request Message (INR) request +; if no calling party number is specified. This will attempt to tell the other end +; to send it anyways. Should be defined after sigchan. +;inr_if_no_calling=yes + +; Set this to set whether or not the originating access is (non) ISDN in the forward and +; backward call indicators. Should be defined after sigchan +;non_isdn_access=yes + +; This sets the number of binary places to shift the CIC when doing load balancing between +; sigchans on a linkset. Should be defined after sigchan. Default 0 +;sls_shift = 0 + +; Send custom cause_location value +; Should be defined after sigchan. Default 1 (private local) +;cause_location=1 + +; SS7 timers (ISUP and MTP3) should be explicitly defined for each linkset to be used. +; For a full list of supported timers and their default values (applicable for both ITU +; and ANSI) see ss7.timers +; Should be defined after sigchan +;#include ss7.timers + ; For more information on setting up SS7, see the README file in libss7 or ; https://wiki.asterisk.org/wiki/display/AST/Signaling+System+Number+7 ; ----------------- SS7 Options ---------------------------------------- diff --git a/configs/ss7.timers.sample b/configs/ss7.timers.sample new file mode 100644 index 0000000000000000000000000000000000000000..9cf9bd1ab479aabd718aa2f604c67d0dff6635d5 --- /dev/null +++ b/configs/ss7.timers.sample @@ -0,0 +1,65 @@ +;;;;; ITU-T Q.707 timers + +;mtp3_timer.q707_t1 = 4000 +;mtp3_timer.q707_t2 = 30000 + +;;;;; MTP3 timers as specified in ITU-T Q.704 or ANSI T1.111-2001 + +mtp3_timer.t1 = 500 +mtp3_timer.t2 = 700 +mtp3_timer.t3 = 500 +mtp3_timer.t4 = 500 +mtp3_timer.t5 = 500 +mtp3_timer.t6 = 500 +mtp3_timer.t7 = 1000 + +mtp3_timer.t10 = 60000 + +mtp3_timer.t12 = 800 +mtp3_timer.t13 = 800 +mtp3_timer.t14 = 2000 + +; enable for ITU only. Timers after T17 are defined differently for ANSI +;mtp3_timer.t19 = 67000 +;mtp3_timer.t21 = 63000 +; +;mtp3_timer.t22 = 300000 +;mtp3_timer.t23 = 300000 + + +;;;;; ISUP timers as specified in ITU-T Q.764 or ANSI T1.113-2000 + +isup_timer.t1 = 15000 +;isup_timer.t2 = 180000 ; ITU only + +;isup_timer.t5 = 300000 ; for ITU +;isup_timer.t5 = 60000 ; for ANSI +isup_timer.t6 = 30000 +isup_timer.t7 = 20000 +isup_timer.t8 = 10000 + +;isup_timer.t10 = 4000 ; ITU only + +isup_timer.t12 = 15000 +;isup_timer.t13 = 300000 ; for ITU +;isup_timer.t13 = 60000 ; for ANSI +isup_timer.t14 = 15000 +;isup_timer.t15 = 300000 ; for ITU +;isup_timer.t15 = 60000 ; for ANSI +isup_timer.t16 = 15000 +;isup_timer.t17 = 300000 ; for ITU +;isup_timer.t17 = 60000 ; for ANSI +isup_timer.t18 = 15000 +;isup_timer.t19 = 300000 ; for ITU +;isup_timer.t19 = 60000 ; for ANSI +isup_timer.t20 = 15000 +;isup_timer.t21 = 300000 ; for ITU +;isup_timer.t21 = 60000 ; for ANSI +isup_timer.t22 = 15000 +;isup_timer.t23 = 300000 ; for ITU +;isup_timer.t23 = 60000 ; for ANSI + +isup_timer.t27 = 240000 + +isup_timer.t33 = 12000 +;isup_timer.t35 = 15000 ; ITU only diff --git a/configure b/configure index a028170258908ed8af7164a0c6da1c2e431c6c5c..59d48c6c1daea647539be32b9a922ff226fc9616 100755 --- a/configure +++ b/configure @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.ac Revision: 412977 . +# From configure.ac Revision: 413772 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.68 for asterisk trunk. # @@ -26664,7 +26664,7 @@ fi fi -# Check for libss7 v1.0 branch compatible version. +# Check for libss7 v2.0 branch compatible version. if test "x${PBX_SS7}" != "x1" -a "${USE_SS7}" != "no"; then pbxlibdir="" @@ -26676,7 +26676,7 @@ if test "x${PBX_SS7}" != "x1" -a "${USE_SS7}" != "no"; then pbxlibdir="-L${SS7_DIR}" fi fi - pbxfuncname="ss7_set_adjpc" + pbxfuncname="ss7_set_isup_timer" if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers AST_SS7_FOUND=yes else diff --git a/configure.ac b/configure.ac index da9d2d7519072d1b9a52749f4eccb80b5a301538..95b7dd02bfe047c1b3903043c196351269676af9 100644 --- a/configure.ac +++ b/configure.ac @@ -2096,8 +2096,8 @@ if test "x${PBX_SPANDSP}" = "x1" ; then AST_EXT_LIB_CHECK([SPANDSP], [spandsp], [t38_terminal_init], [spandsp.h], [-ltiff]) fi -# Check for libss7 v1.0 branch compatible version. -AST_EXT_LIB_CHECK([SS7], [ss7], [ss7_set_adjpc], [libss7.h]) +# Check for libss7 v2.0 branch compatible version. +AST_EXT_LIB_CHECK([SS7], [ss7], [ss7_set_isup_timer], [libss7.h]) AST_EXT_LIB_CHECK([OPENR2], [openr2], [openr2_chan_new], [openr2.h])