| /* Simple / General implementation of an External USSD Entity using OsmoHLR's GSUP Protocol |
| * |
| * The idea is that a test case can simply start an instance of this component in parallel to its |
| * normal test components. HLR_EUSE_CT will then connect via GSUP to the HLR as the specified EUSE |
| * name. Any USSD related GSUP message received will be passed to a user-provided call-back |
| * function, which will return whatever PDU to send in response back to the HLR. |
| */ |
| |
| |
| /* (C) 2018 Harald Welte <laforge@gnumonks.org> |
| * All rights reserved. |
| * |
| * Released under the terms of GNU General Public License, Version 2 or |
| * (at your option) any later version. |
| * |
| * SPDX-License-Identifier: GPL-2.0-or-later |
| */ |
| |
| module HLR_EUSE { |
| |
| import from GSUP_Types all; |
| import from IPA_Emulation all; |
| |
| import from General_Types all; |
| import from Osmocom_Types all; |
| import from SS_Types all; |
| import from SS_Templates all; |
| |
| /* emulating an external USSD Entity towards OsmoHLR */ |
| type component HLR_EUSE_CT { |
| /* Component reference + config of underlying IPA emulation */ |
| var IPA_Emulation_CT vc_IPA_EUSE; |
| var IPA_CCM_Parameters ccm_pars; |
| /* port towards the underlying IPA emulation */ |
| port IPA_GSUP_PT EUSE; |
| } |
| |
| private function f_init(charstring hlr_ip, uint16_t hlr_gsup_port, charstring name) runs on HLR_EUSE_CT { |
| var charstring id := "EUSE-" & name; |
| ccm_pars := c_IPA_default_ccm_pars; |
| ccm_pars.name := "Osmocom TTCN-3 EUSE Simulator"; |
| ccm_pars.ser_nr := id; |
| |
| vc_IPA_EUSE := IPA_Emulation_CT.create("IPA-" & id); |
| map(vc_IPA_EUSE:IPA_PORT, system:IPA_CODEC_PT); |
| connect(vc_IPA_EUSE:IPA_GSUP_PORT, self:EUSE); |
| vc_IPA_EUSE.start(IPA_Emulation.main_client(hlr_ip, hlr_gsup_port, "", 0, ccm_pars)); |
| |
| timer T := 10.0; |
| T.start; |
| alt { |
| [] EUSE.receive(tr_ASP_IPA_EV(ASP_IPA_EVENT_UP)) { repeat; } |
| [] EUSE.receive(tr_ASP_IPA_EV(ASP_IPA_EVENT_ID_ACK)) { } |
| [] T.timeout { |
| setverdict(fail, "EUSE: Timeout waiting for GSUP IPA Link to come up"); |
| self.stop; |
| } |
| } |
| } |
| |
| type function f_euse_cb(GSUP_PDU rx_pdu) return GSUP_PDU; |
| |
| function f_ss_echo_continue(GSUP_PDU rx_pdu) return GSUP_PDU { |
| var GSUP_SessionState ss_next_state; |
| var GSUP_IeValue ss_ie, state_ie; |
| var SS_FacilityInformation dec_fac, rsp_fac; |
| var octetstring ss_rsp; |
| |
| f_gsup_find_ie(rx_pdu, OSMO_GSUP_SESSION_STATE_IE, state_ie); |
| var GSUP_SessionState ss_state := state_ie.session_state; |
| |
| f_gsup_find_ie(rx_pdu, OSMO_GSUP_SS_INFO_IE, ss_ie); |
| dec_fac := dec_SS_FacilityInformation(ss_ie.ss_info); |
| log("dec_fac: ", dec_fac); |
| rsp_fac := valueof(ts_SS_USSD_FACILITY_RETURN_RESULT(dec_fac[0].invoke.invokeId.present_, |
| SS_OP_CODE_PROCESS_USS_REQ, |
| dec_fac[0].invoke.argument.uSSD_Arg.ussd_DataCodingScheme, |
| dec_fac[0].invoke.argument.uSSD_Arg.ussd_String)); |
| ss_rsp := enc_SS_FacilityInformation(rsp_fac); |
| select (ss_state) { |
| case (OSMO_GSUP_SESSION_STATE_BEGIN) { ss_next_state := OSMO_GSUP_SESSION_STATE_CONTINUE; } |
| case (OSMO_GSUP_SESSION_STATE_CONTINUE) { ss_next_state := OSMO_GSUP_SESSION_STATE_END; } |
| } |
| return valueof(ts_GSUP_PROC_SS_RES(rx_pdu.ies[0].val.imsi, rx_pdu.ies[1].val.session_id, |
| ss_next_state, ss_rsp)); |
| } |
| |
| function f_ss_echo(GSUP_PDU rx_pdu) return GSUP_PDU { |
| var GSUP_IeValue ss_ie; |
| var SS_FacilityInformation dec_fac, rsp_fac; |
| var octetstring ss_rsp; |
| |
| f_gsup_find_ie(rx_pdu, OSMO_GSUP_SS_INFO_IE, ss_ie); |
| dec_fac := dec_SS_FacilityInformation(ss_ie.ss_info); |
| log("dec_fac: ", dec_fac); |
| rsp_fac := valueof(ts_SS_USSD_FACILITY_RETURN_RESULT(dec_fac[0].invoke.invokeId.present_, |
| SS_OP_CODE_PROCESS_USS_REQ, |
| dec_fac[0].invoke.argument.uSSD_Arg.ussd_DataCodingScheme, |
| dec_fac[0].invoke.argument.uSSD_Arg.ussd_String)); |
| ss_rsp := enc_SS_FacilityInformation(rsp_fac); |
| return valueof(ts_GSUP_PROC_SS_RES(rx_pdu.ies[0].val.imsi, rx_pdu.ies[1].val.session_id, |
| OSMO_GSUP_SESSION_STATE_END, ss_rsp)); |
| } |
| |
| /* main function for handling mobile-originated USSD via GSUP */ |
| function f_main_mo(charstring hlr_ip, uint16_t hlr_gsup_port, charstring name, f_euse_cb cb_fn) |
| runs on HLR_EUSE_CT { |
| var GSUP_PDU rx_pdu, tx_pdu; |
| |
| f_init(hlr_ip, hlr_gsup_port, name); |
| |
| while (true) { |
| alt { |
| [] EUSE.receive(tr_GSUP_PROC_SS_REQ(?, ?, ?)) -> value rx_pdu { |
| EUSE.send(cb_fn.apply(rx_pdu)); |
| } |
| |
| |
| [] EUSE.receive { |
| setverdict(fail, "EUSE: Unexpected Rx from HLR"); |
| self.stop; |
| } |
| } |
| } |
| } |
| |
| |
| } |