diff --git a/libs/freetdm/docs/ss7-native-bridge.txt b/libs/freetdm/docs/ss7-native-bridge.txt new file mode 100644 index 0000000000..d44b067b0a --- /dev/null +++ b/libs/freetdm/docs/ss7-native-bridge.txt @@ -0,0 +1,43 @@ +SS7 Native Bridge + +Native bridge is enabled on 2 conditions: + +* The SIP header FreeTDM-TransUUID is set in the originating leg and matches a freetdm channel +* The variable freetdm_native_sigbridge is true and the originating leg is also a freetdm channel + +Some coding rules apply to this feature: + +- Each channel is responsible for clearning its own peer_data and event queue + at the end of the call (when moving to DOWN state) + +- Each channel dequeues messages only from its own queue and enqueues messages + in the peer's queue, with the only exception being messages received before + the bridge is stablished (IAM for sure and possible SAM messages) because + if the bridge is not yet stablished the messages must be queued by the channel + in its own queue temporarily until the bridge is ready + +- When the bridge is ready it is the responsibility of the incoming channel to + move the messages that stored temporarily in its own queue to the bridged peer queue + +- During hangup, each channel is responsible for moving itself to DOWN. The procedure + however differs slightly depending on the hangup conditions + + If the user requests hangup (ie, FreeSWITCH) the request will be noted by setting the + FTDM_CHANNEL_USER_HANGUP flag but will not be processed yet because call control is + driven only by the link messages (so no hangup from ESL or command line allowed) + + When REL message comes, the channel receiving it must move to TERMINATING state and: + + - If the user has not hangup yet (FTDM_CHANNEL_USER_HANGUP flag not set) then + notify the user via SIGEVENT_STOP and wait for the user to move to HANGUP + state by calling ftdm_channel_call_hangup() before sending RLC + + - If the user did hangup already (FTDM_CHANNEL_USER_HANGUP flag is set) then + skip user notification and move to HANGUP state directly where the RLC message + will be sent + +- On HANGUP state the RLC is sent and the channel is moved to DOWN, final state + The peer channel will forward the REL message and wait for RLC from the network, when + RLC is received the channel can move straight to DOWN itself because the peer channel + is completing its own shutdown procedure when it received the REL message + diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c index 1c39703182..b6a6f49ca1 100755 --- a/libs/freetdm/mod_freetdm/mod_freetdm.c +++ b/libs/freetdm/mod_freetdm/mod_freetdm.c @@ -1409,7 +1409,7 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi chan_id = 0; } - if (session && globals.sip_headers) { + if (session && globals.sip_headers && !switch_core_session_check_interface (session,freetdm_endpoint_interface) ) { switch_channel_t *channel = switch_core_session_get_channel(session); const char *sipvar; @@ -1474,7 +1474,7 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi sipvar = switch_channel_get_variable(channel, "sip_h_X-FreeTDM-LOC-NADI"); if (sipvar) { ftdm_usrmsg_add_var(&usrmsg, "ss7_loc_nadi", sipvar); - } + } sipvar = switch_channel_get_variable(channel, "sip_h_X-FreeTDM-DNIS-TON"); if (sipvar) { @@ -1726,7 +1726,6 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi ftdm_channel_get_span_id(peer_private->ftdmchan), ftdm_channel_get_id(peer_private->ftdmchan)); switch_core_session_rwunlock(network_peer); } - /* Figure out if there is a native bridge requested through dial plan variable and the originating channel is also freetdm (not going through SIP) */ } else if (session && (var = channel_get_variable(session, var_event, FREETDM_VAR_PREFIX "native_sigbridge")) @@ -2043,6 +2042,7 @@ ftdm_status_t ftdm_channel_from_event(ftdm_sigmsg_t *sigmsg, switch_core_session if (!ftdm_strlen_zero(var_value)) { switch_channel_set_variable_printf(channel, "sip_h_X-FreeTDM-OCN", "%s", var_value); } + var_value = ftdm_sigmsg_get_var(sigmsg, "ss7_ocn_nadi"); if (!ftdm_strlen_zero(var_value)) { switch_channel_set_variable_printf(channel, "sip_h_X-FreeTDM-OCN-NADI", "%s", var_value); diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index e132aaab70..78e65dfe88 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -2203,9 +2203,12 @@ static ftdm_status_t _ftdm_channel_call_hangup_nl(const char *file, const char * { ftdm_status_t status = FTDM_SUCCESS; - if (ftdm_test_flag(chan, FTDM_CHANNEL_NATIVE_SIGBRIDGE)) { + /* In native sigbridge mode we ignore hangup requests from the user and hangup only when the signaling module decides it */ + if (ftdm_test_flag(chan, FTDM_CHANNEL_NATIVE_SIGBRIDGE) && chan->state != FTDM_CHANNEL_STATE_TERMINATING) { + ftdm_log_chan_ex(chan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Ignoring hangup in channel in state %s (native bridge enabled)\n", ftdm_channel_state2str(chan->state)); + ftdm_set_flag(chan, FTDM_CHANNEL_USER_HANGUP); goto done; } diff --git a/libs/freetdm/src/ftdm_state.c b/libs/freetdm/src/ftdm_state.c index bd670d0062..e5aba9d408 100644 --- a/libs/freetdm/src/ftdm_state.c +++ b/libs/freetdm/src/ftdm_state.c @@ -48,7 +48,6 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_complete_state(const char *file, const c ftdm_time_t diff = 0; ftdm_channel_state_t state = fchan->state; - #if 0 /* I could not perform this sanity check without more disruptive changes. Ideally we should check here if the signaling module completing the state executed a state processor (called ftdm_channel_advance_states() which call fchan->span->state_processor(fchan)) for the state. That is just a @@ -59,6 +58,7 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_complete_state(const char *file, const c ftdm_channel_advance_states() would set the state_status to PROCESSING and then the check below for STATUS_NEW would be valid. Currently is not valid because the signaling module may be completing the state at the end of the state_processor callback and therefore the state will still be in STATUS_NEW, and is perfectly valid ... */ + if (fchan->state_status == FTDM_STATE_STATUS_NEW) { ftdm_log_chan_ex(fchan, file, func, line, FTDM_LOG_LEVEL_CRIT, "Asking to complete state change from %s to %s in %llums, but the state is still unprocessed (this might be a bug!)\n", diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cli.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cli.c index 3f1a03f4d8..df872b260c 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cli.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_cli.c @@ -35,7 +35,7 @@ * */ -#if 0 +#if 0 #define SMG_RELAY_DBG #endif @@ -1332,7 +1332,7 @@ static ftdm_status_t handle_show_status(ftdm_stream_handle_t *stream, int span, } #ifdef SMG_RELAY_DBG - stream->write_function(stream," blk_flag=%x | ckt_flag=%x | chan_flag=%x", ss7_info->blk_flags, ss7_info->ckt_flags, ftdmchan->flags); + stream->write_function(stream," | blk_flag=%x | ckt_flag=%x", ss7_info->blk_flags, ss7_info->ckt_flags); #endif stream->write_function(stream, "\n"); } /* if ( hole, sig, voice) */ @@ -1374,36 +1374,26 @@ static ftdm_status_t handle_tx_blo(ftdm_stream_handle_t *stream, int span, int c } if ((ftdmchan->physical_span_id == lspan) && (ftdmchan->physical_chan_id == lchan)) { - /* now that we have the right channel...put a lock on it so no-one else can use it */ ftdm_mutex_lock(ftdmchan->mutex); /* check if there is a pending state change|give it a bit to clear */ if (check_for_state_change(ftdmchan)) { SS7_ERROR("Failed to wait for pending state change on CIC = %d\n", ss7_info->circuit->cic); - /* check if we need to die */ ftdm_assert(0, "State change not completed\n"); - /* unlock the channel again before we exit */ ftdm_mutex_unlock(ftdmchan->mutex); - /* move to the next channel */ continue; } else { - /* throw the ckt block flag */ sngss7_set_ckt_blk_flag(ss7_info, FLAG_CKT_MN_BLOCK_TX); - - /* set the channel to suspended state */ ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_SUSPENDED); } - - /* unlock the channel again before we exit */ + ftdm_mutex_unlock(ftdmchan->mutex); + } - } /* if ( span and chan) */ + } - } /* if ( cic != 0) */ - - /* go the next circuit */ x++; - } /* while (g_ftdm_sngss7_data.cfg.isupCkt[x]id != 0) */ + } handle_show_blocks(stream, span, chan, verbose); @@ -1440,33 +1430,22 @@ static ftdm_status_t handle_tx_ubl(ftdm_stream_handle_t *stream, int span, int c } if ((ftdmchan->physical_span_id == lspan) && (ftdmchan->physical_chan_id == lchan)) { - /* now that we have the right channel...put a lock on it so no-one else can use it */ ftdm_mutex_lock(ftdmchan->mutex); /* check if there is a pending state change|give it a bit to clear */ if (check_for_state_change(ftdmchan)) { SS7_ERROR("Failed to wait for pending state change on CIC = %d\n", ss7_info->circuit->cic); - /* check if we need to die */ ftdm_assert(0, "State change not completed\n"); - /* unlock the channel again before we exit */ ftdm_mutex_unlock(ftdmchan->mutex); - /* move to the next channel */ continue; } else { - /* throw the ckt block flag */ sngss7_set_ckt_blk_flag(ss7_info, FLAG_CKT_MN_UNBLK_TX); - - /* clear the block flag */ sngss7_clear_ckt_blk_flag(ss7_info, FLAG_CKT_MN_BLOCK_TX); - - /* check group blocking */ sngss7_clear_ckt_blk_flag(ss7_info, FLAG_GRP_MN_BLOCK_TX); - /* set the channel to suspended state */ ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_SUSPENDED); } - /* unlock the channel again before we exit */ ftdm_mutex_unlock(ftdmchan->mutex); } @@ -1812,6 +1791,8 @@ static ftdm_status_t handle_tx_cgb(ftdm_stream_handle_t *stream, int span, int c /* throw the grp maint. block flag */ sngss7_set_ckt_blk_flag(sngss7_info, FLAG_GRP_MN_BLOCK_TX); + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_SUSPENDED); + /* bring the sig status down */ sngss7_set_sig_status(sngss7_info, FTDM_SIG_STATE_DOWN); @@ -1945,6 +1926,7 @@ static ftdm_status_t handle_tx_cgu(ftdm_stream_handle_t *stream, int span, int c /* bring the sig status up */ sngss7_set_sig_status(sngss7_info, FTDM_SIG_STATE_UP); + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_SUSPENDED); /* if this is the first channel in the range */ if (!main_chan) { diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_handle.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_handle.c index f48d25ce3c..4e0a20d5f3 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_handle.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_handle.c @@ -131,6 +131,16 @@ ftdm_status_t handle_con_ind(uint32_t suInstId, uint32_t spInstId, uint32_t circ /* KONRAD FIX ME : check in case there is a ckt and grp block */ } + + sngss7_clear_ckt_flag(sngss7_info, FLAG_INR_TX); + sngss7_clear_ckt_flag(sngss7_info, FLAG_INR_SENT); + sngss7_clear_ckt_flag(sngss7_info, FLAG_INR_RX); + sngss7_clear_ckt_flag(sngss7_info, FLAG_INR_RX_DN); + sngss7_clear_ckt_flag(sngss7_info, FLAG_INF_TX); + sngss7_clear_ckt_flag(sngss7_info, FLAG_INF_SENT); + sngss7_clear_ckt_flag(sngss7_info, FLAG_INF_RX); + sngss7_clear_ckt_flag(sngss7_info, FLAG_INF_RX_DN); + sngss7_clear_ckt_flag(sngss7_info, FLAG_FULL_NUMBER); /* check whether the ftdm channel is in a state to accept a call */ switch (ftdmchan->state) { @@ -175,6 +185,12 @@ ftdm_status_t handle_con_ind(uint32_t suInstId, uint32_t spInstId, uint32_t circ /* fill in ANI */ ftdm_set_string(ftdmchan->caller_data.ani.digits, ftdmchan->caller_data.cid_num.digits); } + else { + if (g_ftdm_sngss7_data.cfg.force_inr) { + sngss7_set_ckt_flag(sngss7_info, FLAG_INR_TX); + sngss7_clear_ckt_flag(sngss7_info, FLAG_INR_SENT); + } + } if (siConEvnt->cgPtyNum.scrnInd.pres) { /* fill in the screening indication value */ @@ -186,6 +202,11 @@ ftdm_status_t handle_con_ind(uint32_t suInstId, uint32_t spInstId, uint32_t circ ftdmchan->caller_data.pres = siConEvnt->cgPtyNum.presRest.val; } } else { + if (g_ftdm_sngss7_data.cfg.force_inr) { + sngss7_set_ckt_flag(sngss7_info, FLAG_INR_TX); + sngss7_clear_ckt_flag(sngss7_info, FLAG_INR_SENT); + } + SS7_INFO_CHAN(ftdmchan,"No Calling party (ANI) information in IAM!%s\n", " "); } @@ -436,10 +457,26 @@ ftdm_status_t handle_con_sta(uint32_t suInstId, uint32_t spInstId, uint32_t circ /**************************************************************************/ case (INFORMATION): SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx INF\n", sngss7_info->circuit->cic); + + SS7_DEBUG_CHAN (ftdmchan, "Cancelling T.39 timer %s\n", " "); + /* check if t39 is active */ + if (sngss7_info->t39.hb_timer_id) { + ftdm_sched_cancel_timer (sngss7_info->t39.sched, sngss7_info->t39.hb_timer_id); + SS7_DEBUG_CHAN (ftdmchan, "T.39 timer has been cancelled upon receiving INF message %s\n", " "); + } + + sngss7_set_ckt_flag(sngss7_info, FLAG_INF_RX_DN); + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_IDLE); + break; /**************************************************************************/ case (INFORMATREQ): SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Rx INR\n", sngss7_info->circuit->cic); + + ft_to_sngss7_inf(ftdmchan, siCnStEvnt); + + sngss7_set_ckt_flag(sngss7_info, FLAG_INR_RX); + break; /**************************************************************************/ case (SUBSADDR): @@ -1143,11 +1180,12 @@ ftdm_status_t handle_sta_ind(uint32_t suInstId, uint32_t spInstId, uint32_t circ break; /**************************************************************************/ case SIT_STA_CGBRSP: /* mntc. oriented CGB response */ - /*handle_cgb_req(suInstId, spInstId, circuit, globalFlg, evntType, siStaEvnt);*/ + SS7_INFO(" Rx CGBA \n"); break; /**************************************************************************/ case SIT_STA_CGURSP: /* mntc. oriented CGU response */ /*SS7_WARN(" %s indication not currently supported\n", DECODE_LCC_EVENT(evntType));*/ + SS7_INFO(" Rx CGUA \n"); break; /**************************************************************************/ case SIT_STA_GRSREQ: /* circuit group reset request */ @@ -2472,6 +2510,8 @@ ftdm_status_t handle_cgb_req(uint32_t suInstId, uint32_t spInstId, uint32_t circ /* bring the sig status down */ sngss7_set_sig_status(sngss7_info, FTDM_SIG_STATE_DOWN); + + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_SUSPENDED); /* unlock the channel again before we exit */ ftdm_mutex_unlock(ftdmchan->mutex); @@ -2589,8 +2629,7 @@ ftdm_status_t handle_cgu_req(uint32_t suInstId, uint32_t spInstId, uint32_t circ while( x < loop_range ) { if (g_ftdm_sngss7_data.cfg.isupCkt[x].type != SNG_CKT_VOICE) { loop_range++; - } - else { + } else { if (extract_chan_data(x, &sngss7_info, &ftdmchan)) { SS7_ERROR("Failed to extract channel data for circuit = %d!\n", x); } @@ -2627,7 +2666,8 @@ ftdm_status_t handle_cgu_req(uint32_t suInstId, uint32_t spInstId, uint32_t circ if (sngss7_channel_status_clear(sngss7_info)) { sngss7_set_sig_status(sngss7_info, FTDM_SIG_STATE_UP); } - + + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_SUSPENDED); ftdm_mutex_unlock(ftdmchan->mutex); /* update the bit and byte counter*/ diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_in.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_in.c index b13c98b1e6..92931c4b8d 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_in.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_in.c @@ -81,6 +81,7 @@ void sngss7_con_ind(uint32_t suInstId, uint32_t spInstId, uint32_t circuit, SiCo /* initalize the sngss7_event */ sngss7_event = ftdm_malloc(sizeof(*sngss7_event)); + if (sngss7_event == NULL) { SS7_ERROR("Failed to allocate memory for sngss7_event!\n"); SS7_FUNC_TRACE_EXIT(__FUNCTION__); diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c index fe129c0f73..1098126890 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c @@ -340,10 +340,9 @@ static void handle_hw_alarm(ftdm_event_t *e) /* MONITIOR THREADS ***********************************************************/ static void *ftdm_sangoma_ss7_run(ftdm_thread_t * me, void *obj) { - ftdm_interrupt_t *ftdm_sangoma_ss7_int[3]; + ftdm_interrupt_t *ftdm_sangoma_ss7_int[2]; ftdm_span_t *ftdmspan = (ftdm_span_t *) obj; ftdm_channel_t *ftdmchan = NULL; - ftdm_channel_t *peerchan = NULL; ftdm_event_t *event = NULL; sngss7_event_data_t *sngss7_event = NULL; sngss7_span_data_t *sngss7_span = (sngss7_span_data_t *)ftdmspan->signal_data; @@ -368,12 +367,6 @@ static void *ftdm_sangoma_ss7_run(ftdm_thread_t * me, void *obj) goto ftdm_sangoma_ss7_run_exit; } - /* get an interrupt queue for this span for peer channel events */ - if (ftdm_queue_get_interrupt (sngss7_span->peer_chans, &ftdm_sangoma_ss7_int[2]) != FTDM_SUCCESS) { - SS7_CRITICAL ("Failed to get a ftdm_interrupt for span = %d for peer channel events queue!\n", ftdmspan->span_id); - goto ftdm_sangoma_ss7_run_exit; - } - while (ftdm_running () && !(ftdm_test_flag (ftdmspan, FTDM_SPAN_STOP_THREAD))) { int x = 0; if (b_alarm_test) { @@ -410,42 +403,26 @@ static void *ftdm_sangoma_ss7_run(ftdm_thread_t * me, void *obj) /* clean out all pending channel state changes */ while ((ftdmchan = ftdm_queue_dequeue (ftdmspan->pendingchans))) { + sngss7_chan_data_t *chan_info = ftdmchan->call_data; /*first lock the channel */ ftdm_mutex_lock(ftdmchan->mutex); /* process state changes for this channel until they are all done */ ftdm_channel_advance_states(ftdmchan); + + if (chan_info->peer_data) { + /* clean out all pending stack events in the peer channel */ + while ((sngss7_event = ftdm_queue_dequeue(chan_info->event_queue))) { + ftdm_sangoma_ss7_process_peer_stack_event(ftdmchan, sngss7_event); + ftdm_safe_free(sngss7_event); + } + } /* unlock the channel */ ftdm_mutex_unlock (ftdmchan->mutex); } - /* clean out all peer pending channel events */ - while ((peerchan = ftdm_queue_dequeue (sngss7_span->peer_chans))) { - /* note that the channels being dequeued here may not belong to this span - they may belong to just about any other span that one of our channels - happens to be bridged to */ - sngss7_chan_data_t *peer_info = peerchan->call_data; - sngss7_chan_data_t *chan_info = peer_info->peer_data; - ftdmchan = chan_info->ftdmchan; - - /* - if there is any state changes at all, those will be done in the opposite channel - to peerchan (where the original event was received), therefore we must lock ftdmchan, - but do not need to lock peerchan as we only read its event queue, which is already - locked when dequeueing */ - ftdm_channel_lock(ftdmchan); - - /* clean out all pending stack events in the peer channel */ - while ((sngss7_event = ftdm_queue_dequeue(peer_info->event_queue))) { - ftdm_sangoma_ss7_process_peer_stack_event(ftdmchan, sngss7_event); - ftdm_safe_free(sngss7_event); - } - - ftdm_channel_unlock(ftdmchan); - } - /* clean out all pending stack events */ while ((sngss7_event = ftdm_queue_dequeue(sngss7_span->event_queue))) { ftdm_sangoma_ss7_process_stack_event(sngss7_event); @@ -561,16 +538,16 @@ static void ftdm_sangoma_ss7_process_stack_event (sngss7_event_data_t *sngss7_ev ftdm_channel_advance_states(ftdmchan); if (sngss7_event->event_id == SNGSS7_CON_IND_EVENT) { - /* this is the first event in a call, flush the event queue */ - sngss7_flush_queue(sngss7_info->event_queue); - /* clear the peer if any */ - sngss7_info->peer_data = NULL; clone_event++; } - /* if the call has already started and the event is not a release confirmation, clone the event */ - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_CALL_STARTED) && - sngss7_event->event_id != SNGSS7_REL_CFM_EVENT) { + /* If the call has already started (we only bridge events related to calls) + * and the event is not a release confirmation, then clone the event. + * We do not clone release cfm events because that is the only event (final event) that is not + * bridged to the other leg, the first Spirou customer we had explicitly requested to send + * release confirm as soon as the release is received and therefore not wait for the other leg + * to send release confirm (hence, not need to clone and enqueue in the other leg) */ + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_CALL_STARTED) && sngss7_event->event_id != SNGSS7_REL_CFM_EVENT) { clone_event++; } @@ -597,11 +574,38 @@ static void ftdm_sangoma_ss7_process_stack_event (sngss7_event_data_t *sngss7_ev event_clone = ftdm_calloc(1, sizeof(*sngss7_event)); if (event_clone) { memcpy(event_clone, sngss7_event, sizeof(*sngss7_event)); - ftdm_queue_enqueue(sngss7_info->event_queue, event_clone); + /* if we have already a peer channel then enqueue the event in their queue */ if (sngss7_info->peer_data) { - sngss7_span_data_t *sngss7_peer_span = (sngss7_span_data_t *)sngss7_info->peer_data->ftdmchan->span->signal_data; + ftdm_span_t *peer_span = sngss7_info->peer_data->ftdmchan->span; + if (sngss7_info->peer_event_transfer_cnt) { + sngss7_event_data_t *peer_event = NULL; + int qi = 0; + /* looks like for the first time we found our peer, transfer any messages we enqueued */ + for (qi = 0; qi < sngss7_info->peer_event_transfer_cnt; qi++) { + peer_event = ftdm_queue_dequeue(sngss7_info->event_queue); + if (peer_event) { + ftdm_queue_enqueue(sngss7_info->peer_data->event_queue, peer_event); + } else { + /* This should never happen! */ + SS7_CRIT_CHAN(ftdmchan,"[CIC:%d]What!? someone stole my messages!\n", sngss7_info->circuit->cic); + } + } + SS7_DEBUG_CHAN(ftdmchan,"[CIC:%d]Transferred %d messages into my peer's queue\n", + sngss7_info->circuit->cic, sngss7_info->peer_event_transfer_cnt); + sngss7_info->peer_event_transfer_cnt = 0; + } /* we already have a peer attached, wake him up */ - ftdm_queue_enqueue(sngss7_peer_span->peer_chans, sngss7_info->ftdmchan); + ftdm_queue_enqueue(sngss7_info->peer_data->event_queue, event_clone); + ftdm_queue_enqueue(peer_span->pendingchans, sngss7_info->peer_data->ftdmchan); + } else { + /* we don't have a peer yet, save the event on our own queue for later + * only the first event in this queue is directly consumed by our peer (IAM), subsequent events + * must be transferred by us to their queue as soon as we find our peer */ + ftdm_queue_enqueue(sngss7_info->event_queue, event_clone); + if (sngss7_event->event_id != SNGSS7_CON_IND_EVENT) { + /* This could be an SAM, save it for transfer once we know who our peer is (if we ever find that) */ + sngss7_info->peer_event_transfer_cnt++; + } } } } @@ -614,25 +618,7 @@ static void ftdm_sangoma_ss7_process_stack_event (sngss7_event_data_t *sngss7_ev ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); break; case SNGSS7_REL_CFM_EVENT: - { - if (sngss7_info->peer_data) { - ftdm_channel_t *peer_chan = sngss7_info->peer_data->ftdmchan; - ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN); - if (peer_chan) { - /* we need to unlock our chan or we risk deadlock */ - ftdm_channel_advance_states(ftdmchan); - ftdm_channel_unlock(ftdmchan); - - ftdm_channel_lock(peer_chan); - if (peer_chan->state != FTDM_CHANNEL_STATE_DOWN) { - ftdm_set_state(peer_chan, FTDM_CHANNEL_STATE_DOWN); - } - ftdm_channel_unlock(peer_chan); - - ftdm_channel_lock(ftdmchan); - } - } - } + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN); break; default: break; @@ -1011,7 +997,11 @@ static ftdm_status_t ftdm_sangoma_ss7_native_bridge_state_change(ftdm_channel_t ftdm_channel_t *close_chan = ftdmchan; sngss7_clear_ckt_flag(sngss7_info, FLAG_SUS_RECVD); sngss7_clear_ckt_flag(sngss7_info, FLAG_T6_CANCELED); + sngss7_clear_ckt_flag (sngss7_info, FLAG_SENT_ACM); + sngss7_clear_ckt_flag (sngss7_info, FLAG_SENT_CPG); + sngss7_flush_queue(sngss7_info->event_queue); + sngss7_info->peer_data = NULL; ftdm_channel_close (&close_chan); } break; @@ -1026,9 +1016,22 @@ static ftdm_status_t ftdm_sangoma_ss7_native_bridge_state_change(ftdm_channel_t case FTDM_CHANNEL_STATE_TERMINATING: { - ft_to_sngss7_rlc(ftdmchan); + /* Release confirm is sent immediately, since Spirou customer asked us not to wait for the second call leg + * to come back with a release confirm ... */ /* when receiving REL we move to TERMINATING and notify the user that the bridge is ending */ - sngss7_send_signal(sngss7_info, FTDM_SIGEVENT_STOP); + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_USER_HANGUP)) { + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } else { + /* Notify the user and wait for their ack before sending RLC */ + sngss7_send_signal(sngss7_info, FTDM_SIGEVENT_STOP); + } + } + break; + + case FTDM_CHANNEL_STATE_HANGUP: + { + ft_to_sngss7_rlc(ftdmchan); + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN); } break; @@ -1053,11 +1056,11 @@ ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t *ftdmchan) sngss7_info->blk_flags); if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NATIVE_SIGBRIDGE)) { - /* DIALING is the only state we process normally when doing an outgoing call that is natively bridged */ + /* DIALING is the only state we process normally when doing an outgoing call that is natively bridged, + * all other states are run by a different state machine (and the freetdm core does not do any checking) */ if (ftdmchan->state != FTDM_CHANNEL_STATE_DIALING) { return ftdm_sangoma_ss7_native_bridge_state_change(ftdmchan); } - sngss7_info->peer_data = NULL; } /*check what state we are supposed to be in */ @@ -1080,23 +1083,91 @@ ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t *ftdmchan) } /* check if the end of pulsing (ST) character has arrived or the right number of digits */ - if (ftdmchan->caller_data.dnis.digits[i-1] == 'F') { + if (ftdmchan->caller_data.dnis.digits[i-1] == 'F' + || sngss7_test_ckt_flag(sngss7_info, FLAG_FULL_NUMBER) ) + { SS7_DEBUG_CHAN(ftdmchan, "Received the end of pulsing character %s\n", ""); - /* remove the ST */ - ftdmchan->caller_data.dnis.digits[i-1] = '\0'; - - /*now go to the RING state */ - state_flag = 0; - ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING); + if (!sngss7_test_ckt_flag(sngss7_info, FLAG_FULL_NUMBER)) { + /* remove the ST */ + ftdmchan->caller_data.dnis.digits[i-1] = '\0'; + sngss7_set_ckt_flag(sngss7_info, FLAG_FULL_NUMBER); + } + if (sngss7_test_ckt_flag(sngss7_info, FLAG_INR_TX)) { + if (!sngss7_test_ckt_flag(sngss7_info, FLAG_INR_SENT) ) { + ft_to_sngss7_inr(ftdmchan); + sngss7_set_ckt_flag(sngss7_info, FLAG_INR_SENT); + + SS7_DEBUG_CHAN (ftdmchan, "Scheduling T.39 timer %s \n", " "); + + /* start ISUP t39 */ + if (ftdm_sched_timer (sngss7_info->t39.sched, + "t39", + sngss7_info->t39.beat, + sngss7_info->t39.callback, + &sngss7_info->t39, + &sngss7_info->t39.hb_timer_id)) + { + + SS7_ERROR ("Unable to schedule timer T39, hanging up call!\n"); + + ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_TEMPORARY_FAILURE; + sngss7_set_ckt_flag (sngss7_info, FLAG_LOCAL_REL); + + /* end the call */ + state_flag = 0; + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_CANCEL); + } + }else { + state_flag = 0; + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING); + } + } else { + state_flag = 0; + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING); + } } else if (i >= sngss7_info->circuit->min_digits) { SS7_DEBUG_CHAN(ftdmchan, "Received %d digits (min digits = %d)\n", i, sngss7_info->circuit->min_digits); - /*now go to the RING state */ - state_flag = 0; - ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING); - + if (sngss7_test_ckt_flag(sngss7_info, FLAG_INR_TX)) { + if (!sngss7_test_ckt_flag(sngss7_info, FLAG_INR_SENT) ) { + ft_to_sngss7_inr(ftdmchan); + sngss7_set_ckt_flag(sngss7_info, FLAG_INR_SENT); + + SS7_DEBUG_CHAN (ftdmchan, "Scheduling T.39 timer %s\n", " " ); + + /* start ISUP t39 */ + if (ftdm_sched_timer (sngss7_info->t39.sched, + "t39", + sngss7_info->t39.beat, + sngss7_info->t39.callback, + &sngss7_info->t39, + &sngss7_info->t39.hb_timer_id)) + { + + SS7_ERROR ("Unable to schedule timer T39, hanging up call!\n"); + + ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_TEMPORARY_FAILURE; + sngss7_set_ckt_flag (sngss7_info, FLAG_LOCAL_REL); + + /* end the call */ + state_flag = 0; + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_CANCEL); + } + + state_flag = 0; + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_IDLE); + }else { + if (sngss7_test_ckt_flag(sngss7_info, FLAG_INF_RX_DN) ) { + state_flag = 0; + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING); + } + } + } else { + state_flag = 0; + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING); + } } else { /* if we are coming from idle state then we have already been here once before */ if (ftdmchan->last_state != FTDM_CHANNEL_STATE_IDLE) { @@ -1152,6 +1223,15 @@ ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t *ftdmchan) /**************************************************************************/ case FTDM_CHANNEL_STATE_RING: /*incoming call request */ + sngss7_clear_ckt_flag(sngss7_info, FLAG_INR_TX); + sngss7_clear_ckt_flag(sngss7_info, FLAG_INR_SENT); + sngss7_clear_ckt_flag(sngss7_info, FLAG_INR_RX); + sngss7_clear_ckt_flag(sngss7_info, FLAG_INR_RX_DN); + sngss7_clear_ckt_flag(sngss7_info, FLAG_INF_TX); + sngss7_clear_ckt_flag(sngss7_info, FLAG_INF_SENT); + sngss7_clear_ckt_flag(sngss7_info, FLAG_INF_RX); + sngss7_clear_ckt_flag(sngss7_info, FLAG_INF_RX_DN); + if (ftdmchan->last_state == FTDM_CHANNEL_STATE_SUSPENDED) { SS7_DEBUG("re-entering state from processing block/unblock request ... do nothing\n"); break; @@ -1162,6 +1242,11 @@ ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t *ftdmchan) ftdm_sched_cancel_timer (sngss7_info->t35.sched, sngss7_info->t35.hb_timer_id); } + /* cancel t39 timer */ + if (sngss7_info->t39.hb_timer_id) { + ftdm_sched_cancel_timer (sngss7_info->t39.sched, sngss7_info->t39.hb_timer_id); + } + SS7_DEBUG_CHAN(ftdmchan, "Sending incoming call from %s to %s to FTDM core\n", ftdmchan->caller_data.ani.digits, ftdmchan->caller_data.dnis.digits); @@ -1512,19 +1597,6 @@ ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t *ftdmchan) if (ftdm_test_flag (ftdmchan, FTDM_CHANNEL_OPEN)) { ftdm_channel_t *close_chan = ftdmchan; - - /* detach native bridging if needed (only the outbound leg is responsible for that) - Inbound leg was responsible of flushing its queue of events, but peer attach/detach - is left as an outbound leg responsibility - */ - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { - sngss7_chan_data_t *peer_info = sngss7_info->peer_data; - sngss7_info->peer_data = NULL; - if (peer_info) { - peer_info->peer_data = NULL; - } - } - /* close the channel */ SS7_DEBUG_CHAN(ftdmchan,"FTDM Channel Close %s\n", ""); sngss7_flush_queue(sngss7_info->event_queue); @@ -2404,12 +2476,6 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_sangoma_ss7_span_config) return FTDM_FAIL; } - /* create an peer channel queue for this span */ - if ((ftdm_queue_create(&(ss7_span_info)->peer_chans, SNGSS7_PEER_CHANS_QUEUE_SIZE)) != FTDM_SUCCESS) { - SS7_CRITICAL("Unable to create peer chans queue!\n"); - return FTDM_FAIL; - } - /*setup the span structure with the info so far */ g_ftdm_sngss7_data.sig_cb = sig_cb; span->start = ftdm_sangoma_ss7_start; diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h index 63d4ded203..4ccf4efe0e 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h @@ -395,6 +395,7 @@ typedef struct sng_isup_ckt { uint16_t t16; uint16_t t17; uint32_t t35; + uint32_t t39; uint16_t tval; } sng_isup_ckt_t; @@ -466,6 +467,7 @@ typedef struct sng_ss7_cfg { sng_nsap_t nsap[MAX_NSAPS+1]; sng_isap_t isap[MAX_ISAPS+1]; sng_glare_resolution glareResolution; + uint32_t force_inr; } sng_ss7_cfg_t; typedef struct ftdm_sngss7_data { @@ -517,12 +519,14 @@ typedef struct sngss7_chan_data { sngss7_glare_data_t glare; sngss7_timer_data_t t35; sngss7_timer_data_t t10; + sngss7_timer_data_t t39; sngss7_group_data_t rx_grs; sngss7_group_data_t rx_gra; sngss7_group_data_t tx_grs; sngss7_group_data_t ucic; ftdm_queue_t *event_queue; - struct sngss7_chan_data *peer_data; + struct sngss7_chan_data *peer_data; + uint8_t peer_event_transfer_cnt; } sngss7_chan_data_t; #define SNGSS7_RX_GRS_PENDING (1 << 0) @@ -536,7 +540,6 @@ typedef struct sngss7_span_data { sngss7_group_data_t rx_cgu; sngss7_group_data_t tx_cgu; ftdm_queue_t *event_queue; - ftdm_queue_t *peer_chans; } sngss7_span_data_t; typedef struct sngss7_event_data @@ -584,6 +587,15 @@ typedef enum { FLAG_SENT_CPG = (1 << 17), FLAG_SUS_RECVD = (1 << 18), FLAG_T6_CANCELED = (1 << 19), + FLAG_INR_TX = (1 << 20), + FLAG_INR_SENT = (1 << 21), + FLAG_INR_RX = (1 << 22), + FLAG_INR_RX_DN = (1 << 23), + FLAG_INF_TX = (1 << 24), + FLAG_INF_SENT = (1 << 25), + FLAG_INF_RX = (1 << 26), + FLAG_INF_RX_DN = (1 << 27), + FLAG_FULL_NUMBER = (1 << 28), FLAG_RELAY_DOWN = (1 << 30), FLAG_CKT_RECONFIG = (1 << 31) } sng_ckt_flag_t; @@ -606,6 +618,14 @@ typedef enum { "INF_RESUME", \ "INF_PAUSED", \ "TX_ACM_SENT" \ + "TX_INR" \ + "INR_SENT" \ + "RX_INR" \ + "RX_INR_DN" \ + "TX_INF" \ + "INF SENT" \ + "RX_INF" \ + "RX_INF_DN" \ "RELAY_DOWN", \ "CKT_RECONFIG" FTDM_STR2ENUM_P(ftmod_ss7_ckt_state2flag, ftmod_ss7_ckt_flag2str, sng_ckt_flag_t) @@ -820,6 +840,9 @@ void ft_to_sngss7_cgb(ftdm_channel_t * ftdmchan); void ft_to_sngss7_cgu(ftdm_channel_t * ftdmchan); void ft_to_sngss7_itx (ftdm_channel_t * ftdmchan); void ft_to_sngss7_txa (ftdm_channel_t * ftdmchan); +void ft_to_sngss7_inr(ftdm_channel_t * ftdmchan); +void ft_to_sngss7_inf(ftdm_channel_t *ftdmchan, SiCnStEvnt *inr); + /* in ftmod_sangoma_ss7_in.c */ @@ -949,6 +972,7 @@ ftdm_status_t sngss7_add_raw_data(sngss7_chan_data_t *sngss7_info, uint8_t* data /* in ftmod_sangoma_ss7_timers.c */ void handle_isup_t35(void *userdata); void handle_isup_t10(void *userdata); +void handle_isup_t39(void *userdata); /******************************************************************************/ @@ -970,7 +994,7 @@ if (ftdmchan->state == new_state) { \ #define SS7_INFO_CHAN(fchan, msg, args...) ftdm_log_chan(fchan, FTDM_LOG_INFO, msg , ##args) #define SS7_WARN_CHAN(fchan, msg, args...) ftdm_log_chan(fchan, FTDM_LOG_WARNING, msg , ##args) #define SS7_ERROR_CHAN(fchan, msg, args...) ftdm_log_chan(fchan, FTDM_LOG_ERROR, msg , ##args) -#define SS7_CTRIT_CHAN(fchan, msg, args...) ftdm_log_chan(fchan, FTDM_LOG_CRIT, msg , ##args) +#define SS7_CRIT_CHAN(fchan, msg, args...) ftdm_log_chan(fchan, FTDM_LOG_CRIT, msg , ##args) #ifdef SS7_CODE_DEVEL #define SS7_DEVEL_DEBUG(a,...) ftdm_log(FTDM_LOG_DEBUG,a,##__VA_ARGS__ ); diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_out.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_out.c index 5f0a0f0960..9ba11ada58 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_out.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_out.c @@ -48,6 +48,7 @@ void ft_to_sngss7_iam (ftdm_channel_t * ftdmchan) SiConEvnt iam; ftdm_bool_t native_going_up = FTDM_FALSE; sngss7_chan_data_t *sngss7_info = ftdmchan->call_data;; + sngss7_event_data_t *event_clone = NULL; SS7_FUNC_TRACE_ENTER (__FUNCTION__); @@ -75,28 +76,25 @@ void ft_to_sngss7_iam (ftdm_channel_t * ftdmchan) SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Starting native bridge with peer CIC %d\n", sngss7_info->circuit->cic, peer_info->circuit->cic); + /* retrieve only first message from the others guys queue (must be IAM) */ + event_clone = ftdm_queue_dequeue(peer_info->event_queue); + /* make each one of us aware of the native bridge */ peer_info->peer_data = sngss7_info; sngss7_info->peer_data = peer_info; - /* flush our own queue */ - sngss7_flush_queue(sngss7_info->event_queue); - /* Go to up until release comes, note that state processing is done different and much simpler when there is a peer, We can't go to UP state right away yet though, so do not set the state to UP here, wait until the end of this function - because moving from one state to another causes the ftdmchan->usrmsg structure to be wiped + because moving from one state to another causes the ftdmchan->usrmsg structure to be wiped and we still need those variables for further IAM processing */ native_going_up = FTDM_TRUE; } } } - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NATIVE_SIGBRIDGE) && sngss7_info->peer_data) { - sngss7_span_data_t *span_data = ftdmchan->span->signal_data; - sngss7_event_data_t *event_clone = ftdm_queue_dequeue(sngss7_info->peer_data->event_queue); - /* Retrieve IAM from our peer */ + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NATIVE_SIGBRIDGE)) { if (!event_clone) { - SS7_ERROR_CHAN(ftdmchan, "No event clone in peer queue!%s\n", ""); + SS7_ERROR_CHAN(ftdmchan, "No IAM event clone in peer queue!%s\n", ""); } else if (event_clone->event_id != SNGSS7_CON_IND_EVENT) { /* first message in the queue should ALWAYS be an IAM */ SS7_ERROR_CHAN(ftdmchan, "Invalid initial peer message type '%d'\n", event_clone->event_id); @@ -141,9 +139,6 @@ void ft_to_sngss7_iam (ftdm_channel_t * ftdmchan) copy_ocn_to_sngss7(ftdmchan, &iam.origCdNum); } } - /* since this is the first time we dequeue an event from the peer, make sure our main thread process any other events, - this will trigger the interrupt in our span peer_chans queue which will wake up our main thread if it is sleeping */ - ftdm_queue_enqueue(span_data->peer_chans, sngss7_info->peer_data->ftdmchan); } else if (sngss7_info->circuit->transparent_iam && sngss7_retrieve_iam(ftdmchan, &iam) == FTDM_SUCCESS) { SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Tx IAM (Transparent)\n", sngss7_info->circuit->cic); @@ -234,12 +229,107 @@ void ft_to_sngss7_iam (ftdm_channel_t * ftdmchan) the user sending FTDM_SIGEVENT_UP which can cause the application to misbehave (ie, no audio) */ ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_UP); ftdm_channel_advance_states(ftdmchan); - } + } + + ftdm_safe_free(event_clone); SS7_FUNC_TRACE_EXIT (__FUNCTION__); return; } +void ft_to_sngss7_inf(ftdm_channel_t *ftdmchan, SiCnStEvnt *inr) +{ + SiCnStEvnt evnt; + sngss7_chan_data_t *sngss7_info = ftdmchan->call_data; + + memset (&evnt, 0x0, sizeof (evnt)); + + evnt.infoInd.eh.pres = PRSNT_NODEF; + evnt.infoInd.cgPtyAddrRespInd.pres = PRSNT_NODEF; + evnt.infoInd.cgPtyCatRespInd.pres = PRSNT_NODEF; + + evnt.infoInd.chrgInfoRespInd.pres = PRSNT_NODEF; + evnt.infoInd.chrgInfoRespInd.val = 0; + evnt.infoInd.solInfoInd.pres = PRSNT_NODEF; + evnt.infoInd.solInfoInd.val = 0; + evnt.infoInd.holdProvInd.pres = PRSNT_NODEF; + evnt.infoInd.holdProvInd.val = 0; + evnt.infoInd.spare.pres = PRSNT_NODEF; + evnt.infoInd.spare.val = 0; + + if (inr->infoReqInd.eh.pres == PRSNT_NODEF) { + if ((inr->infoReqInd.holdingInd.pres == PRSNT_NODEF) && (inr->infoReqInd.holdingInd.val == HOLD_REQ)) { + SS7_DEBUG_CHAN(ftdmchan,"[CIC:%d]Received INR requesting holding information. Holding is not supported in INF.\n", sngss7_info->circuit->cic); + } + if ((inr->infoReqInd.chrgInfoReqInd.pres == PRSNT_NODEF) && (inr->infoReqInd.chrgInfoReqInd.val == CHRGINFO_REQ)) { + SS7_DEBUG_CHAN(ftdmchan,"[CIC:%d]Received INR requesting charging information. Charging is not supported in INF.\n", sngss7_info->circuit->cic); + } + if ((inr->infoReqInd.malCaIdReqInd.pres == PRSNT_NODEF) && (inr->infoReqInd.malCaIdReqInd.val == CHRGINFO_REQ)) { + SS7_DEBUG_CHAN(ftdmchan,"[CIC:%d]Received INR requesting malicious call id. Malicious call id is not supported in INF.\n", sngss7_info->circuit->cic); + } + + if ((inr->infoReqInd.cgPtyAdReqInd.pres == PRSNT_NODEF) && (inr->infoReqInd.cgPtyAdReqInd.val == CGPRTYADDREQ_REQ)) { + evnt.infoInd.cgPtyAddrRespInd.val=CGPRTYADDRESP_INCL; + copy_cgPtyNum_to_sngss7 (ftdmchan, &evnt.cgPtyNum); + } else { + evnt.infoInd.cgPtyAddrRespInd.val=CGPRTYADDRESP_NOTINCL; + } + + if ((inr->infoReqInd.cgPtyCatReqInd.pres == PRSNT_NODEF) && (inr->infoReqInd.cgPtyCatReqInd.val == CGPRTYCATREQ_REQ)) { + evnt.infoInd.cgPtyCatRespInd.val = CGPRTYCATRESP_INCL; + copy_cgPtyCat_to_sngss7 (ftdmchan, &evnt.cgPtyCat); + } else { + evnt.infoInd.cgPtyCatRespInd.val = CGPRTYCATRESP_NOTINCL; + } + } + else { + SS7_DEBUG_CHAN(ftdmchan,"[CIC:%d]Received INR with no information request. Sending back default INF.\n", sngss7_info->circuit->cic); + } + + sng_cc_inf(1, + sngss7_info->suInstId, + sngss7_info->spInstId, + sngss7_info->circuit->id, + &evnt, + INFORMATION); + + SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Tx INF\n", sngss7_info->circuit->cic); + +} + +void ft_to_sngss7_inr(ftdm_channel_t *ftdmchan) +{ + SiCnStEvnt evnt; + sngss7_chan_data_t *sngss7_info = ftdmchan->call_data; + + memset (&evnt, 0x0, sizeof (evnt)); + + evnt.infoReqInd.eh.pres = PRSNT_NODEF; + evnt.infoReqInd.cgPtyAdReqInd.pres = PRSNT_NODEF; + evnt.infoReqInd.cgPtyAdReqInd.val=CGPRTYADDREQ_REQ; + + evnt.infoReqInd.holdingInd.pres = PRSNT_NODEF; + evnt.infoReqInd.holdingInd.val = HOLD_REQ; + + evnt.infoReqInd.cgPtyCatReqInd.pres = PRSNT_NODEF; + evnt.infoReqInd.cgPtyCatReqInd.val = CGPRTYCATREQ_REQ; + + evnt.infoReqInd.chrgInfoReqInd.pres = PRSNT_NODEF; + evnt.infoReqInd.chrgInfoReqInd.val = CHRGINFO_REQ; + + evnt.infoReqInd.malCaIdReqInd.pres = PRSNT_NODEF; + evnt.infoReqInd.malCaIdReqInd.val = MLBG_INFOREQ; + + sng_cc_inr(1, + sngss7_info->suInstId, + sngss7_info->spInstId, + sngss7_info->circuit->id, + &evnt, + INFORMATREQ); + + SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Tx INR\n", sngss7_info->circuit->cic); +} + void ft_to_sngss7_acm (ftdm_channel_t * ftdmchan) { SS7_FUNC_TRACE_ENTER (__FUNCTION__); diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_support.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_support.c index 7426b033b8..96359ba595 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_support.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_support.c @@ -186,7 +186,7 @@ ftdm_status_t copy_cgPtyNum_to_sngss7(ftdm_channel_t *ftdmchan, SiCgPtyNum *cgPt ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Found user supplied Calling NADI value \"%s\"\n", clg_nadi); cgPtyNum->natAddrInd.val = atoi(clg_nadi); } - ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Calling Party Number Presentation Ind %d\n", cgPtyNum->presRest.val); + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Calling Party Number NADI value %d\n", cgPtyNum->natAddrInd.val); return copy_tknStr_to_sngss7(caller_data->cid_num.digits, &cgPtyNum->addrSig, &cgPtyNum->oddEven); } @@ -257,7 +257,7 @@ ftdm_status_t copy_locPtyNum_to_sngss7(ftdm_channel_t *ftdmchan, SiCgPtyNum *loc locPtyNum->natAddrInd.val = g_ftdm_sngss7_data.cfg.isupCkt[sngss7_info->circuit->id].loc_nadi; locPtyNum->scrnInd.pres = pres_val; - val = ftdm_usrmsg_get_var(ftdmchan->usrmsg, "ss7_loc_screen_ind"); + val = ftdm_usrmsg_get_var(ftdmchan->usrmsg, "ss7_loc_screen_ind"); if (!ftdm_strlen_zero(val)) { locPtyNum->scrnInd.val = atoi(val); ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Found user supplied Location Screening Ind %d\n", locPtyNum->scrnInd.val); diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_timers.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_timers.c index 6138ea34b0..8cac996213 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_timers.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_timers.c @@ -49,7 +49,7 @@ /******************************************************************************/ /* PROTOTYPES *****************************************************************/ -void handle_isup_t35(void *userdata); + /******************************************************************************/ /* FUNCTIONS ******************************************************************/ @@ -76,10 +76,13 @@ void handle_isup_t35(void *userdata) /* end the call */ ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_CANCEL); - /* kill t10 if active */ + /* kill t10 t39 if active */ if (sngss7_info->t10.hb_timer_id) { ftdm_sched_cancel_timer (sngss7_info->t10.sched, sngss7_info->t10.hb_timer_id); } + if (sngss7_info->t39.hb_timer_id) { + ftdm_sched_cancel_timer (sngss7_info->t39.sched, sngss7_info->t39.hb_timer_id); + } /*unlock*/ ftdm_channel_unlock(ftdmchan); @@ -108,7 +111,43 @@ void handle_isup_t10(void *userdata) SS7_FUNC_TRACE_EXIT(__FUNCTION__); } - + +void handle_isup_t39(void *userdata) +{ + SS7_FUNC_TRACE_ENTER(__FUNCTION__); + + sngss7_timer_data_t *timer = userdata; + sngss7_chan_data_t *sngss7_info = timer->sngss7_info; + ftdm_channel_t *ftdmchan = sngss7_info->ftdmchan; + + /* now that we have the right channel...put a lock on it so no-one else can use it */ + ftdm_channel_lock(ftdmchan); + + /* Q.764 2.2.5 Address incomplete (T35 expiry action is hangup with cause 28 according to Table A.1/Q.764) */ + SS7_ERROR("[Call-Control] Timer 39 expired on CIC = %d\n", sngss7_info->circuit->cic); + + /* set the flag to indicate this hangup is started from the local side */ + sngss7_set_ckt_flag(sngss7_info, FLAG_LOCAL_REL); + + /* hang up on timer expiry */ + ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_INVALID_NUMBER_FORMAT; + + /* end the call */ + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_CANCEL); + + /* kill t10 t35 if active */ + if (sngss7_info->t10.hb_timer_id) { + ftdm_sched_cancel_timer (sngss7_info->t10.sched, sngss7_info->t10.hb_timer_id); + } + if (sngss7_info->t35.hb_timer_id) { + ftdm_sched_cancel_timer (sngss7_info->t35.sched, sngss7_info->t35.hb_timer_id); + } + + /*unlock*/ + ftdm_channel_unlock(ftdmchan); + + SS7_FUNC_TRACE_EXIT(__FUNCTION__); +} /******************************************************************************/ /* For Emacs: * Local Variables: diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_xml.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_xml.c index 724fee25b9..f64356dd1f 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_xml.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_xml.c @@ -146,6 +146,7 @@ typedef struct sng_ccSpan uint32_t t16; uint32_t t17; uint32_t t35; + uint32_t t39; uint32_t tval; } sng_ccSpan_t; @@ -487,6 +488,7 @@ static int ftmod_ss7_parse_sng_gen(ftdm_conf_node_t *sng_gen) /* Set the transparent_iam_max_size to default value */ g_ftdm_sngss7_data.cfg.transparent_iam_max_size=800; + g_ftdm_sngss7_data.cfg.force_inr = 0; /* extract all the information from the parameters */ for (i = 0; i < num_parms; i++) { @@ -508,6 +510,14 @@ static int ftmod_ss7_parse_sng_gen(ftdm_conf_node_t *sng_gen) ftmod_ss7_set_glare_resolution (parm->val); SS7_DEBUG("Found glare resolution configuration = %d %s\n", g_ftdm_sngss7_data.cfg.glareResolution, parm->val ); } + else if (!strcasecmp(parm->var, "force-inr")) { + if (ftdm_true(parm->val)) { + g_ftdm_sngss7_data.cfg.force_inr = 1; + } else { + g_ftdm_sngss7_data.cfg.force_inr = 0; + } + SS7_DEBUG("Found INR force configuration = %s\n", parm->val ); + } else { SS7_ERROR("Found an invalid parameter \"%s\"!\n", parm->val); return FTDM_FAIL; @@ -2004,7 +2014,6 @@ static int ftmod_ss7_parse_cc_span(ftdm_conf_node_t *cc_span) flag_loc_nadi = 1; sng_ccSpan.loc_nadi = atoi(parm->val); SS7_DEBUG("Found default LOC_NADI parm->value = %d\n", sng_ccSpan.loc_nadi); - /**********************************************************************/ } else if (!strcasecmp(parm->var, "lpa_on_cot")) { /**********************************************************************/ @@ -2062,6 +2071,11 @@ static int ftmod_ss7_parse_cc_span(ftdm_conf_node_t *cc_span) sng_ccSpan.t35 = atoi(parm->val); SS7_DEBUG("Found isup t35 = %d\n",sng_ccSpan.t35); /**********************************************************************/ + } else if (!strcasecmp(parm->var, "isup.t39")) { + /**********************************************************************/ + sng_ccSpan.t39 = atoi(parm->val); + SS7_DEBUG("Found isup t39 = %d\n",sng_ccSpan.t39); + /**********************************************************************/ } else if (!strcasecmp(parm->var, "isup.tval")) { /**********************************************************************/ sng_ccSpan.tval = atoi(parm->val); @@ -3044,6 +3058,12 @@ static int ftmod_ss7_fill_in_ccSpan(sng_ccSpan_t *ccSpan) } else { g_ftdm_sngss7_data.cfg.isupCkt[x].t35 = ccSpan->t35; } + if (ccSpan->t39 == 0) { + g_ftdm_sngss7_data.cfg.isupCkt[x].t39 = 120; + } else { + g_ftdm_sngss7_data.cfg.isupCkt[x].t39 = ccSpan->t39; + } + if (ccSpan->tval == 0) { g_ftdm_sngss7_data.cfg.isupCkt[x].tval = 10; } else { @@ -3148,6 +3168,13 @@ static int ftmod_ss7_fill_in_circuits(sng_span_t *sngSpan) ss7_info->t10.callback = handle_isup_t10; ss7_info->t10.sngss7_info = ss7_info; + /* prepare the timer structures */ + ss7_info->t39.sched = ((sngss7_span_data_t *)(ftdmspan->signal_data))->sched; + ss7_info->t39.counter = 1; + ss7_info->t39.beat = (isupCkt->t39) * 100; /* beat is in ms, t39 is in 100ms */ + ss7_info->t39.callback = handle_isup_t39; + ss7_info->t39.sngss7_info = ss7_info; + /**************************************************************************/ } /* for (i == 1; i < ftdmspan->chan_count; i++) */