This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.
Harald Welte gerrit-no-reply at lists.osmocom.orgReview at https://gerrit.osmocom.org/5941 Add MNCC_Emulation layer (similar to BSSMAP_Emulation) This allows us to dispatch the MNCC messages to different TTCN3 components, based on the callref of the MNCC message. Change-Id: I2f10df139397da26aaa7961c595cdce141299af0 --- A library/MNCC_Emulation.ttcn M msc_tests/gen_links.sh M msc_tests/regen_makefile.sh 3 files changed, 371 insertions(+), 2 deletions(-) git pull ssh://gerrit.osmocom.org:29418/osmo-ttcn3-hacks refs/changes/41/5941/1 diff --git a/library/MNCC_Emulation.ttcn b/library/MNCC_Emulation.ttcn new file mode 100644 index 0000000..bcf6c0f --- /dev/null +++ b/library/MNCC_Emulation.ttcn @@ -0,0 +1,346 @@ +module MNCC_Emulation { + +/* MNCC Emulation, runs on top of MNCC_CodecPort. It multiplexes/demultiplexes + * the individual calls, so there can be separate TTCN-3 components handling + * each of the calls + * + * The MNCC_Emulation.main() function processes MNCC primitives from the MNCC + * socket via the MNCC_CodecPort, and dispatches them to the per-connection components. + * + * Outbound MNCC connections are initiated by sending a MNCC_Call_Req primitive + * to the component running the MNCC_Emulation.main() function. + * + * For each new inbound connections, the MnccOps.create_cb() is called. It can create + * or resolve a TTCN-3 component, and returns a component reference to which that inbound + * connection is routed/dispatched. + * + * If a pre-existing component wants to register to handle a future inbound call, it can + * do so by registering an "expect" with the expected destination phone number. This is e.g. useful + * if you are simulating BSC + MNCC, and first trigger a connection from BSC side in a + * component which then subsequently should also handle the MNCC emulation. + * + * Inbound Unit Data messages (such as are dispatched to the MnccOps.unitdata_cb() callback, + * which is registered with an argument to the main() function below. + * + * (C) 2018 by Harald Welte <laforge at gnumonks.org> + * All rights reserved. + * + * Released under the terms of GNU General Public License, Version 2 or + * (at your option) any later version. + */ + + +import from Osmocom_Types all; +import from MNCC_CodecPort all; +import from MNCC_Types all; + +/* General "base class" component definition, of which specific implementations + * derive themselves by means of the "extends" feature */ +type component MNCC_ConnHdlr { + /* port towards MNCC Emulator core / call dispatchar */ + port MNCC_Conn_PT MNCC; +} + +/* Auxiliary primitive that can happen on the port between per-connection client and this dispatcher */ +type enumerated MNCC_Conn_Prim { + /* MNCC tell us that connection was released */ + MNCC_CONN_PRIM_DISC_IND, + /* we tell MNCC to release connection */ + MNCC_CONN_PRIM_DISC_REQ +} + +type record MNCC_Conn_Req { + MNCC_PDU mncc +} + +/* port between individual per-connection components and this dispatcher */ +type port MNCC_Conn_PT message { + inout MNCC_PDU, MNCC_Conn_Prim, MNCC_Conn_Req; +} with { extension "internal" }; + + +/* represents a single MNCC call */ +type record ConnectionData { + /* reference to the instance of the per-connection component */ + MNCC_ConnHdlr comp_ref, + integer mncc_call_id +} + +type component MNCC_Emulation_CT { + /* UNIX DOMAIN socket on the bottom side, using primitives */ + port MNCC_CODEC_PT MNCC; + /* MNCC port to the per-connection clients */ + port MNCC_Conn_PT MNCC_CLIENT; + + /* use 16 as this is also the number of SCCP connections that SCCP_Emulation can handle */ + var ConnectionData MnccCallTable[16]; + + /* pending expected incoming connections */ + var ExpectData MnccExpectTable[8]; + /* procedure based port to register for incoming connections */ + port MNCCEM_PROC_PT MNCC_PROC; + + var integer g_mncc_ud_id; +}; + +private function f_call_id_known(uint32_t mncc_call_id) +runs on MNCC_Emulation_CT return boolean { + var integer i; + for (i := 0; i < sizeof(MnccCallTable); i := i+1) { + if (MnccCallTable[i].mncc_call_id == mncc_call_id){ + return true; + } + } + return false; +} + +private function f_comp_known(MNCC_ConnHdlr client) +runs on MNCC_Emulation_CT return boolean { + var integer i; + for (i := 0; i < sizeof(MnccCallTable); i := i+1) { + if (MnccCallTable[i].comp_ref == client) { + return true; + } + } + return false; +} + +/* resolve component reference by connection ID */ +private function f_comp_by_call_id(uint32_t mncc_call_id) +runs on MNCC_Emulation_CT return MNCC_ConnHdlr { + var integer i; + for (i := 0; i < sizeof(MnccCallTable); i := i+1) { + if (MnccCallTable[i].mncc_call_id == mncc_call_id) { + return MnccCallTable[i].comp_ref; + } + } + log("MNCC Call table not found by MNCC Call ID ", mncc_call_id); + setverdict(fail); + self.stop; +} + +/* resolve connection ID by component reference */ +private function f_call_id_by_comp(MNCC_ConnHdlr client) +runs on MNCC_Emulation_CT return integer { + for (var integer i := 0; i < sizeof(MnccCallTable); i := i+1) { + if (MnccCallTable[i].comp_ref == client) { + return MnccCallTable[i].mncc_call_id; + } + } + log("MNCC Call table not found by component ", client); + setverdict(fail); + self.stop; +} + +private function f_gen_call_id() +runs on MNCC_Emulation_CT return integer { + var uint32_t call_id; + + do { + call_id := float2int(rnd()*4294967296.0); + } while (f_call_id_known(call_id) == true); + + return call_id; +} + +private function f_call_table_init() +runs on MNCC_Emulation_CT { + for (var integer i := 0; i < sizeof(MnccCallTable); i := i+1) { + MnccCallTable[i].comp_ref := null; + MnccCallTable[i].mncc_call_id := -1; + } +} + +private function f_call_table_add(MNCC_ConnHdlr comp_ref, uint32_t mncc_call_id) +runs on MNCC_Emulation_CT { + for (var integer i := 0; i < sizeof(MnccCallTable); i := i+1) { + if (MnccCallTable[i].mncc_call_id == -1) { + MnccCallTable[i].comp_ref := comp_ref; + MnccCallTable[i].mncc_call_id := mncc_call_id; + log("Added conn table entry ", i, comp_ref, mncc_call_id); + return; + } + } + log("MNCC Call table full!"); + setverdict(fail); + self.stop; +} + +private function f_call_table_del(uint32_t mncc_call_id) +runs on MNCC_Emulation_CT { + for (var integer i := 0; i < sizeof(MnccCallTable); i := i+1) { + if (MnccCallTable[i].mncc_call_id == mncc_call_id) { + log("Deleted conn table entry ", i, + MnccCallTable[i].comp_ref, mncc_call_id); + MnccCallTable[i].mncc_call_id := -1; + MnccCallTable[i].comp_ref := null; + return + } + } + log("MNCC Call table attempt to delete non-existant ", mncc_call_id); + setverdict(fail); + self.stop; +} + + +/* call-back type, to be provided by specific implementation; called when new SCCP connection + * arrives */ +type function MnccCreateCallback(MNCC_PDU conn_ind, charstring id) +runs on MNCC_Emulation_CT return MNCC_ConnHdlr; + +type function MnccUnitdataCallback(MNCC_PDU mncp) +runs on MNCC_Emulation_CT return template MNCC_PDU; + +type record MnccOps { + MnccCreateCallback create_cb, + MnccUnitdataCallback unitdata_cb +} + +function main(MnccOps ops, charstring id) runs on MNCC_Emulation_CT { + + f_call_table_init(); + + while (true) { + var MNCC_send_data sd; + var MNCC_Conn_Req creq; + var MNCC_ConnHdlr vc_conn; + var MNCC_PDU mncc; + var MNCC_ConnHdlr vc_hdlr; + var charstring dest_nr; + + alt { + /* MNCC -> Client: UNIT-DATA (connectionless SCCP) from a BSC */ + [] MNCC.receive(t_SD_MNCC_MSGT(g_mncc_ud_id, MNCC_SOCKET_HELLO)) -> value sd { + /* Connectionless Procedures like HELLO */ + var template MNCC_PDU resp; + resp := ops.unitdata_cb.apply(sd.data); + if (isvalue(resp)) { + MNCC.send(t_SD_MNCC(g_mncc_ud_id, resp)); + } + } + + /* MNCC -> Client: Release Indication / confirmation */ + [] MNCC.receive(t_SD_MNCC_MSGT(g_mncc_ud_id, (MNCC_REL_IND, MNCC_REL_CNF))) -> value sd { + var uint32_t call_id := f_mncc_get_call_id(sd.data); + /* forward to respective client */ + vc_conn := f_comp_by_call_id(call_id); + MNCC_CLIENT.send(sd.data) to vc_conn; + /* remove from call table */ + f_call_table_del(call_id); + } + + /* MNCC -> Client: call related messages */ + [] MNCC.receive(t_SD_MNCC_MSGT(g_mncc_ud_id, ?)) -> value sd { + var uint32_t call_id := f_mncc_get_call_id(sd.data); + + if (f_call_id_known(call_id)) { + vc_conn := f_comp_by_call_id(call_id); + MNCC_CLIENT.send(sd.data) to vc_conn; + } else { + /* TODO: Only accept this for SETUP.req? */ + vc_conn := ops.create_cb.apply(sd.data, id) + /* store mapping between client components and SCCP connectionId */ + f_call_table_add(vc_conn, call_id); + /* handle user payload */ + MNCC_CLIENT.send(sd.data) to vc_conn; + } + } + + /* Client -> MNCC Socket: RELEASE.ind or RELEASE.cnf: forward + drop call table entry */ + [] MNCC_CLIENT.receive(MNCC_PDU:{msg_type := (MNCC_REL_IND, MNCC_REL_CNF), u:=?}) -> value mncc sender vc_conn { + var integer call_id := f_call_id_by_comp(vc_conn); + /* forward to MNCC socket */ + MNCC.send(t_SD_MNCC(g_mncc_ud_id, mncc)); + /* remove from call table */ + f_call_table_del(call_id); + } + + /* Client -> MNCC Socket: Normal message */ + [] MNCC_CLIENT.receive(MNCC_PDU:?) -> value mncc sender vc_conn { + /* forward to MNCC socket */ + MNCC.send(t_SD_MNCC(g_mncc_ud_id, mncc)); + } + + + /* Client -> us: procedure call to register expect */ + [] MNCC_PROC.getcall(MNCCEM_register:{?,?}) -> param(dest_nr, vc_hdlr) { + f_create_expect(dest_nr, vc_hdlr); + MNCC_PROC.reply(MNCCEM_register:{dest_nr, vc_hdlr}); + } + + } + } +} + +private function f_mgcp_ep_extract_cic(charstring inp) return integer { + var charstring local_part := regexp(inp, "(*)@*", 0); + return hex2int(str2hex(local_part)); + +} + +/*********************************************************************** + * "Expect" Handling (mapping for expected incoming MNCC calls from IUT) + ***********************************************************************/ + +/* data about an expected future incoming connection */ +type record ExpectData { + /* destination number based on which we can match it */ + charstring dest_number optional, + /* component reference for this connection */ + MNCC_ConnHdlr vc_conn +} + +/* procedure based port to register for incoming calls */ +signature MNCCEM_register(in charstring dest_nr, in MNCC_ConnHdlr hdlr); + +type port MNCCEM_PROC_PT procedure { + inout MNCCEM_register; +} with { extension "internal" }; + +/* CreateCallback that can be used as create_cb and will use the expectation table */ +function ExpectedCreateCallback(MNCC_PDU conn_ind, charstring id) +runs on MNCC_Emulation_CT return MNCC_ConnHdlr { + var MNCC_ConnHdlr ret := null; + var charstring dest_number; + var integer i; + + if (not ischosen(conn_ind.u.signal) or conn_ind.msg_type != MNCC_SETUP_IND) { + setverdict(fail, "MNCC ExpectedCreateCallback needs MNCC_SETUP_IND"); + return ret; + } + dest_number := conn_ind.u.signal.called.number; + + for (i := 0; i < sizeof(MnccExpectTable); i:= i+1) { + if (not ispresent(MnccExpectTable[i].dest_number)) { + continue; + } + if (dest_number == MnccExpectTable[i].dest_number) { + ret := MnccExpectTable[i].vc_conn; + /* release this entry to be used again */ + MnccExpectTable[i].dest_number := omit; + MnccExpectTable[i].vc_conn := null; + log("Found MnccExpect[", i, "] for ", dest_number, " handled at ", ret); + /* return the component reference */ + return ret; + } + } + setverdict(fail, "Couldn't find MnccExpect for incoming call ", dest_number); + return ret; +} + +private function f_create_expect(charstring dest_number, MNCC_ConnHdlr hdlr) +runs on MNCC_Emulation_CT { + var integer i; + for (i := 0; i < sizeof(MnccExpectTable); i := i+1) { + if (not ispresent(MnccExpectTable[i].dest_number)) { + MnccExpectTable[i].dest_number := dest_number; + MnccExpectTable[i].vc_conn := hdlr; + log("Created MnccExpect[", i, "] for ", dest_number, " to be handled at ", hdlr); + return; + } + } + setverdict(fail, "No space left in MnccMnccExpectTable"); +} + + +} diff --git a/msc_tests/gen_links.sh b/msc_tests/gen_links.sh index 6625b11..7703b52 100755 --- a/msc_tests/gen_links.sh +++ b/msc_tests/gen_links.sh @@ -19,6 +19,15 @@ FILES="TCCInterface_Functions.ttcn TCCConversion_Functions.ttcn TCCConversion.cc TCCConversion.hh TCCInterface.cc TCCInterface_ip.h" gen_links $DIR $FILES +DIR=$BASEDIR/titan.TestPorts.Common_Components.Socket-API/src +FILES="Socket_API_Definitions.ttcn" +gen_links $DIR $FILES + +# Required by MGCP and IPA +DIR=$BASEDIR/titan.TestPorts.IPL4asp/src +FILES="IPL4asp_Functions.ttcn IPL4asp_PT.cc IPL4asp_PT.hh IPL4asp_PortType.ttcn IPL4asp_Types.ttcn IPL4asp_discovery.cc IPL4asp_protocol_L234.hh" +gen_links $DIR $FILES + # required by M3UA_Emulation DIR=$BASEDIR/titan.ProtocolModules.M3UA/src FILES="M3UA_Types.ttcn" @@ -52,7 +61,21 @@ FILES="MobileL3_CC_Types.ttcn MobileL3_CommonIE_Types.ttcn MobileL3_GMM_SM_Types.ttcn MobileL3_MM_Types.ttcn MobileL3_RRM_Types.ttcn MobileL3_SMS_Types.ttcn MobileL3_SS_Types.ttcn MobileL3_Types.ttcn" gen_links $DIR $FILES +DIR=$BASEDIR/titan.ProtocolModules.SDP/src +FILES="SDP_EncDec.cc SDP_Types.ttcn SDP_parse_.tab.c SDP_parse_.tab.h SDP_parse_parser.h SDP_parser.l +SDP_parser.y lex.SDP_parse_.c" +gen_links $DIR $FILES + +DIR=$BASEDIR/titan.ProtocolModules.RTP/src +FILES="RTP_EncDec.cc RTP_Types.ttcn" +gen_links $DIR $FILES + DIR=../library -FILES="General_Types.ttcn Osmocom_Types.ttcn MNCC_Types.ttcn MNCC_EncDec.cc MNCC_CodecPort.ttcn mncc.h" +FILES="General_Types.ttcn GSM_Types.ttcn Osmocom_Types.ttcn MNCC_Types.ttcn MNCC_EncDec.cc MNCC_CodecPort.ttcn mncc.h MNCC_Emulation.ttcn " +FILES+="IPA_Types.ttcn IPA_Emulation.ttcn IPA_CodecPort.ttcn IPA_CodecPort_CtrlFunct.ttcn IPA_CodecPort_CtrlFunctDef.cc RSL_Types.ttcn GSUP_Types.ttcn " +FILES+="Osmocom_CTRL_Types.ttcn L3_Templates.ttcn L3_Templates.ttcn " +FILES+="BSSMAP_Emulation.ttcn BSSAP_CodecPort.ttcn BSSMAP_Templates.ttcn MGCP_Types.ttcn MGCP_Templates.ttcn IPA_Emulation.ttcn " +FILES+="RTP_CodecPort.ttcn RTP_CodecPort_CtrlFunctDef.cc " +FILES+="MGCP_CodecPort.ttcn MGCP_CodecPort_CtrlFunctDef.cc " gen_links $DIR $FILES diff --git a/msc_tests/regen_makefile.sh b/msc_tests/regen_makefile.sh index f271c88..ce33695 100755 --- a/msc_tests/regen_makefile.sh +++ b/msc_tests/regen_makefile.sh @@ -1,5 +1,5 @@ #!/bin/sh -FILES="*.ttcn SCCP_EncDec.cc SCTPasp_PT.cc TCCConversion.cc TCCInterface.cc UD_PT.cc MNCC_EncDec.cc" +FILES="*.ttcn SCCP_EncDec.cc SCTPasp_PT.cc TCCConversion.cc TCCInterface.cc UD_PT.cc MNCC_EncDec.cc IPL4asp_PT.cc IPL4asp_discovery.cc SDP_EncDec.cc RTP_EncDec.cc IPA_CodecPort_CtrlFunctDef.cc RTP_CodecPort_CtrlFunctDef.cc MGCP_CodecPort_CtrlFunctDef.cc *.c" ../regen-makefile.sh MSC_Tests.ttcn $FILES -- To view, visit https://gerrit.osmocom.org/5941 To unsubscribe, visit https://gerrit.osmocom.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I2f10df139397da26aaa7961c595cdce141299af0 Gerrit-PatchSet: 1 Gerrit-Project: osmo-ttcn3-hacks Gerrit-Branch: master Gerrit-Owner: Harald Welte <laforge at gnumonks.org>