From 2fce7887f480614491c9e983555c67921ea29d81 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 15 Apr 2019 11:48:05 +0200 Subject: RAN_Emulation: Modularize protocol support The RAN_Emulation currently unconditionally provides BSSAP and MGCP support. Let's re-structure the code so that support for those protocols is now possible to enable/disable at compile time. This patch is in preparation of introducing RANAP support in RAN_Emulation. Change-Id: Id53ba3ff05f9946230e0e4a759245de14a0f9fbd Related: OS#2856 --- bsc-nat/BSC_MS_ConnectionHandler.ttcn | 1 + bsc-nat/MSC_ConnectionHandler.ttcn | 1 + bsc-nat/gen_links.sh | 2 +- bsc-nat/regen_makefile.sh | 2 +- bsc/MSC_ConnectionHandler.ttcn | 1 + bsc/gen_links.sh | 2 +- bsc/regen_makefile.sh | 2 +- library/RAN_Adapter.ttcn | 161 ------ library/RAN_Adapter.ttcnpp | 165 +++++++ library/RAN_Emulation.ttcn | 836 ------------------------------- library/RAN_Emulation.ttcnpp | 903 ++++++++++++++++++++++++++++++++++ msc/BSC_ConnectionHandler.ttcn | 1 + msc/gen_links.sh | 2 +- msc/regen_makefile.sh | 2 +- 14 files changed, 1078 insertions(+), 1003 deletions(-) delete mode 100644 library/RAN_Adapter.ttcn create mode 100644 library/RAN_Adapter.ttcnpp delete mode 100644 library/RAN_Emulation.ttcn create mode 100644 library/RAN_Emulation.ttcnpp diff --git a/bsc-nat/BSC_MS_ConnectionHandler.ttcn b/bsc-nat/BSC_MS_ConnectionHandler.ttcn index 63d0451c..e52b678f 100644 --- a/bsc-nat/BSC_MS_ConnectionHandler.ttcn +++ b/bsc-nat/BSC_MS_ConnectionHandler.ttcn @@ -53,6 +53,7 @@ const RanOps BSC_MS_RanOps := { unitdata_cb := refers(UnitdataCallback), decode_dtap := false, role_ms := true, + protocol := RAN_PROTOCOL_BSSAP, sccp_addr_local := omit, sccp_addr_peer := omit } diff --git a/bsc-nat/MSC_ConnectionHandler.ttcn b/bsc-nat/MSC_ConnectionHandler.ttcn index 383b67b1..8635a296 100644 --- a/bsc-nat/MSC_ConnectionHandler.ttcn +++ b/bsc-nat/MSC_ConnectionHandler.ttcn @@ -59,6 +59,7 @@ const RanOps MSC_RanOps := { unitdata_cb := refers(UnitdataCallback), decode_dtap := false, role_ms := false, + protocol := RAN_PROTOCOL_BSSAP, sccp_addr_local := omit, sccp_addr_peer := omit } diff --git a/bsc-nat/gen_links.sh b/bsc-nat/gen_links.sh index e54eec48..16e32b78 100755 --- a/bsc-nat/gen_links.sh +++ b/bsc-nat/gen_links.sh @@ -47,7 +47,7 @@ FILES="RTP_EncDec.cc RTP_Types.ttcn" gen_links $DIR $FILES DIR=../library -FILES="Misc_Helpers.ttcn General_Types.ttcn Osmocom_Types.ttcn GSM_Types.ttcn IPA_Types.ttcn IPA_CodecPort.ttcn IPA_CodecPort_CtrlFunct.ttcn IPA_CodecPort_CtrlFunctDef.cc IPA_Emulation.ttcnpp L3_Templates.ttcn BSSMAP_Templates.ttcn RAN_Emulation.ttcn MGCP_Types.ttcn MGCP_Templates.ttcn MGCP_CodecPort.ttcn MGCP_CodecPort_CtrlFunct.ttcn MGCP_CodecPort_CtrlFunctDef.cc Osmocom_CTRL_Types.ttcn Osmocom_CTRL_Functions.ttcn BSSAP_CodecPort.ttcn" +FILES="Misc_Helpers.ttcn General_Types.ttcn Osmocom_Types.ttcn GSM_Types.ttcn IPA_Types.ttcn IPA_CodecPort.ttcn IPA_CodecPort_CtrlFunct.ttcn IPA_CodecPort_CtrlFunctDef.cc IPA_Emulation.ttcnpp L3_Templates.ttcn BSSMAP_Templates.ttcn RAN_Emulation.ttcnpp MGCP_Types.ttcn MGCP_Templates.ttcn MGCP_CodecPort.ttcn MGCP_CodecPort_CtrlFunct.ttcn MGCP_CodecPort_CtrlFunctDef.cc Osmocom_CTRL_Types.ttcn Osmocom_CTRL_Functions.ttcn BSSAP_CodecPort.ttcn" gen_links $DIR $FILES ignore_pp_results diff --git a/bsc-nat/regen_makefile.sh b/bsc-nat/regen_makefile.sh index c5fe64c4..f49df7e2 100755 --- a/bsc-nat/regen_makefile.sh +++ b/bsc-nat/regen_makefile.sh @@ -4,6 +4,6 @@ MAIN=BSCNAT_Tests.ttcn FILES="*.ttcn *.ttcnpp SCCP_EncDec.cc IPA_CodecPort_CtrlFunctDef.cc IPL4asp_PT.cc IPL4asp_discovery.cc TCCConversion.cc TCCInterface.cc RTP_EncDec.cc SDP_EncDec.cc *.c MGCP_CodecPort_CtrlFunctDef.cc" -export CPPFLAGS_TTCN3="-DIPA_EMULATION_SCCP -DIPA_EMULATION_MGCP -DUSE_MTP3_DISTRIBUTOR" +export CPPFLAGS_TTCN3="-DIPA_EMULATION_SCCP -DIPA_EMULATION_MGCP -DRAN_EMULATION_BSSAP -DRAN_EMULATION_MGCP -DUSE_MTP3_DISTRIBUTOR" ../regen-makefile.sh $MAIN $FILES diff --git a/bsc/MSC_ConnectionHandler.ttcn b/bsc/MSC_ConnectionHandler.ttcn index 36e554df..520cc3ea 100644 --- a/bsc/MSC_ConnectionHandler.ttcn +++ b/bsc/MSC_ConnectionHandler.ttcn @@ -376,6 +376,7 @@ const RanOps MSC_RanOps := { unitdata_cb := refers(UnitdataCallback), decode_dtap := false, role_ms := false, + protocol := RAN_PROTOCOL_BSSAP, sccp_addr_local := omit, sccp_addr_peer := omit } diff --git a/bsc/gen_links.sh b/bsc/gen_links.sh index d8393c32..a4f09f4d 100755 --- a/bsc/gen_links.sh +++ b/bsc/gen_links.sh @@ -67,7 +67,7 @@ FILES="TELNETasp_PT.cc TELNETasp_PT.hh TELNETasp_PortType.ttcn" gen_links $DIR $FILES DIR=../library -FILES="Misc_Helpers.ttcn General_Types.ttcn Osmocom_Types.ttcn GSM_Types.ttcn Osmocom_VTY_Functions.ttcn Native_Functions.ttcn Native_FunctionDefs.cc IPA_Types.ttcn IPA_CodecPort.ttcn IPA_CodecPort_CtrlFunct.ttcn IPA_CodecPort_CtrlFunctDef.cc IPA_Emulation.ttcnpp L3_Templates.ttcn BSSMAP_Templates.ttcn RAN_Emulation.ttcn RLCMAC_CSN1_Types.ttcn GSM_RR_Types.ttcn RSL_Types.ttcn RSL_Emulation.ttcn MGCP_Emulation.ttcn MGCP_Types.ttcn MGCP_Templates.ttcn MGCP_CodecPort.ttcn MGCP_CodecPort_CtrlFunct.ttcn MGCP_CodecPort_CtrlFunctDef.cc BSSAP_CodecPort.ttcn RAN_Adapter.ttcn Osmocom_CTRL_Types.ttcn Osmocom_CTRL_Functions.ttcn Osmocom_CTRL_Adapter.ttcn RTP_CodecPort.ttcn RTP_CodecPort_CtrlFunct.ttcn RTP_CodecPort_CtrlFunctDef.cc RTP_Emulation.ttcn IuUP_Types.ttcn IuUP_EncDec.cc IuUP_Emulation.ttcn SCCP_Templates.ttcn IPA_Testing.ttcn" +FILES="Misc_Helpers.ttcn General_Types.ttcn Osmocom_Types.ttcn GSM_Types.ttcn Osmocom_VTY_Functions.ttcn Native_Functions.ttcn Native_FunctionDefs.cc IPA_Types.ttcn IPA_CodecPort.ttcn IPA_CodecPort_CtrlFunct.ttcn IPA_CodecPort_CtrlFunctDef.cc IPA_Emulation.ttcnpp L3_Templates.ttcn BSSMAP_Templates.ttcn RAN_Emulation.ttcnpp RLCMAC_CSN1_Types.ttcn GSM_RR_Types.ttcn RSL_Types.ttcn RSL_Emulation.ttcn MGCP_Emulation.ttcn MGCP_Types.ttcn MGCP_Templates.ttcn MGCP_CodecPort.ttcn MGCP_CodecPort_CtrlFunct.ttcn MGCP_CodecPort_CtrlFunctDef.cc BSSAP_CodecPort.ttcn RAN_Adapter.ttcnpp Osmocom_CTRL_Types.ttcn Osmocom_CTRL_Functions.ttcn Osmocom_CTRL_Adapter.ttcn RTP_CodecPort.ttcn RTP_CodecPort_CtrlFunct.ttcn RTP_CodecPort_CtrlFunctDef.cc RTP_Emulation.ttcn IuUP_Types.ttcn IuUP_EncDec.cc IuUP_Emulation.ttcn SCCP_Templates.ttcn IPA_Testing.ttcn" gen_links $DIR $FILES ignore_pp_results diff --git a/bsc/regen_makefile.sh b/bsc/regen_makefile.sh index 08629d20..06fa8121 100755 --- a/bsc/regen_makefile.sh +++ b/bsc/regen_makefile.sh @@ -4,6 +4,6 @@ MAIN=BSC_Tests.ttcn FILES="*.ttcn *.ttcnpp IPA_CodecPort_CtrlFunctDef.cc IPL4asp_PT.cc IPL4asp_discovery.cc TCCConversion.cc TCCInterface.cc SCTPasp_PT.cc RTP_EncDec.cc SDP_EncDec.cc RTP_CodecPort_CtrlFunctDef.cc MGCP_CodecPort_CtrlFunctDef.cc IuUP_EncDec.cc Native_FunctionDefs.cc TELNETasp_PT.cc *.c" -export CPPFLAGS_TTCN3="-DIPA_EMULATION_RSL -DIPA_EMULATION_MGCP -DIPA_EMULATION_SCCP -DUSE_MTP3_DISTRIBUTOR" +export CPPFLAGS_TTCN3="-DIPA_EMULATION_RSL -DIPA_EMULATION_MGCP -DIPA_EMULATION_SCCP -DRAN_EMULATION_BSSAP -DRAN_EMULATION_MGCP -DUSE_MTP3_DISTRIBUTOR" ../regen-makefile.sh $MAIN $FILES diff --git a/library/RAN_Adapter.ttcn b/library/RAN_Adapter.ttcn deleted file mode 100644 index 294f747b..00000000 --- a/library/RAN_Adapter.ttcn +++ /dev/null @@ -1,161 +0,0 @@ -module RAN_Adapter { - -/* This module implements a 'dumb' RAN adapter. It creates the M3UA and SCCP components and stacks a - * BSSAP/RANAP codec port on top. As a result, it provides the ability to transceive SCCP-User-SAP primitives - * with deoded BSSAP/RANAP payload. Use this if you want to have full control about what you transmit or - * receive, without any automatisms in place. Allows you to refuse connections or other abnormal behavior. */ - -import from General_Types all; -import from Osmocom_Types all; - -import from M3UA_Emulation all; -import from MTP3asp_Types all; -import from MTP3asp_PortType all; - -import from IPA_Emulation all; - -import from SCCP_Types all; -import from SCCPasp_Types all; -import from SCCP_Emulation all; -import from SCCP_Templates all; - -import from SCTPasp_Types all; -import from SCTPasp_PortType all; - -import from BSSMAP_Templates all; -import from RAN_Emulation all; - -type record RAN_Adapter { - /* component references */ - M3UA_CT vc_M3UA, /* only in 3GPP AoIP */ - IPA_Emulation_CT vc_IPA, /* only in SCCPlite */ - IPA_EventWaiter_CT vc_WAIT, /* only in SCCPlite */ - SCCP_CT vc_SCCP, - - MSC_SCCP_MTP3_parameters sccp_pars, - SCCP_PAR_Address sccp_addr_own, - SCCP_PAR_Address sccp_addr_peer, - - /* handler mode */ - RAN_Emulation_CT vc_RAN -} - -type enumerated RAN_Transport { - BSSAP_TRANSPORT_AoIP, /* 3GPP AoIP: SCCP over M3UA over SCTP */ - BSSAP_TRANSPORT_SCCPlite_SERVER, /* SCCPlite: SCCP over IPA over TCP */ - BSSAP_TRANSPORT_SCCPlite_CLIENT /* SCCPlite: SCCP over IPA over TCP */ -}; - -type record RAN_Configuration { - RAN_Transport transport, - charstring sccp_service_type, - SCTP_Association_Address sctp_addr, - integer own_pc, - integer own_ssn, - integer peer_pc, - integer peer_ssn, - octetstring sio, - integer rctx -}; - -private function init_pars(inout RAN_Adapter ba, in RAN_Configuration cfg) { - ba.sccp_pars := { - sio := { - ni := substr(oct2bit(cfg.sio),0,2), - prio := substr(oct2bit(cfg.sio),2,2), - si := substr(oct2bit(cfg.sio),4,4) - }, - opc := cfg.own_pc, - dpc := cfg.peer_pc, - sls := 0, - sccp_serviceType := cfg.sccp_service_type, - ssn := cfg.own_ssn - }; - ba.sccp_addr_own := valueof(ts_SccpAddr_PC_SSN(cfg.own_pc, cfg.own_ssn, cfg.sio, cfg.sccp_service_type)); - ba.sccp_addr_peer := valueof(ts_SccpAddr_PC_SSN(cfg.peer_pc, cfg.peer_ssn, cfg.sio, cfg.sccp_service_type)); -} - - -function f_bssap_init(inout RAN_Adapter ba, in RAN_Configuration cfg, charstring id, - template RanOps ops) { - init_pars(ba, cfg); - ops.sccp_addr_local := ba.sccp_addr_own; - ops.sccp_addr_peer := ba.sccp_addr_peer; - - /* create components */ - ba.vc_SCCP := SCCP_CT.create(id & "-SCCP"); - if (isvalue(ops)) { - ba.vc_RAN := RAN_Emulation_CT.create(id & "-RAN"); - } - select (cfg.transport) { - case (BSSAP_TRANSPORT_AoIP) { - ba.vc_M3UA := M3UA_CT.create(id & "-M3UA"); - map(ba.vc_M3UA:SCTP_PORT, system:sctp); - /* connect MTP3 service provider (M3UA) to lower side of SCCP */ - connect(ba.vc_M3UA:MTP3_SP_PORT, ba.vc_SCCP:MTP3_SCCP_PORT); - ba.vc_M3UA.start(f_M3UA_Emulation(cfg.sctp_addr, cfg.rctx)); - } - case (BSSAP_TRANSPORT_SCCPlite_SERVER) { - ba.vc_IPA := IPA_Emulation_CT.create(id & "-IPA"); - map(ba.vc_IPA:IPA_PORT, system:IPA_CODEC_PT); - /* connect MTP3 service provider (IPA) to lower side of SCCP */ - connect(ba.vc_IPA:MTP3_SP_PORT, ba.vc_SCCP:MTP3_SCCP_PORT); - /* connect waiter to general IPA port (for ASP_IPA_Event) */ - ba.vc_WAIT := IPA_EventWaiter_CT.create(id & "-IPA-WAIT"); - connect(ba.vc_IPA:IPA_SP_PORT, ba.vc_WAIT:IPA_SP_PORT); - ba.vc_WAIT.start(IPA_Emulation.waiter_main()); - ba.vc_IPA.start(IPA_Emulation.main_server(cfg.sctp_addr.local_ip_addr, - cfg.sctp_addr.local_sctp_port, - true, IPA_INIT_SEND_IPA_ID_ACK)); - /* wait until we received an IPA CCM ID_ACK */ - ba.vc_WAIT.done; - disconnect(ba.vc_IPA:IPA_SP_PORT, ba.vc_WAIT:IPA_SP_PORT); - } - case (BSSAP_TRANSPORT_SCCPlite_CLIENT) { - ba.vc_IPA := IPA_Emulation_CT.create(id & "-IPA"); - map(ba.vc_IPA:IPA_PORT, system:IPA_CODEC_PT); - /* connect MTP3 service provider (IPA) to lower side of SCCP */ - connect(ba.vc_IPA:MTP3_SP_PORT, ba.vc_SCCP:MTP3_SCCP_PORT); - /* connect waiter to general IPA port (for ASP_IPA_Event) */ - ba.vc_WAIT := IPA_EventWaiter_CT.create(id & "-IPA-WAIT"); - connect(ba.vc_IPA:IPA_SP_PORT, ba.vc_WAIT:IPA_SP_PORT); - ba.vc_WAIT.start(IPA_Emulation.waiter_main()); - ba.vc_IPA.start(IPA_Emulation.main_client(cfg.sctp_addr.remote_ip_addr, - cfg.sctp_addr.remote_sctp_port, - cfg.sctp_addr.local_ip_addr, - cfg.sctp_addr.local_sctp_port)); - /* wait until we received an IPA CCM ID_ACK */ - ba.vc_WAIT.done; - disconnect(ba.vc_IPA:IPA_SP_PORT, ba.vc_WAIT:IPA_SP_PORT); - } - case else { - setverdict(fail, "Unsuppored RAN_Transport"); - mtc.stop; - } - } - - if (isvalue(ops)) { - timer T := 5.0; - T.start; - //T.timeout; - log("Connecting BSSMAP Emulation to SCCP_SP_PORT and starting emulation"); - /* connect BSSNAP component to upper side of SCCP */ - connect(ba.vc_RAN:BSSAP, ba.vc_SCCP:SCCP_SP_PORT); - if (cfg.transport == BSSAP_TRANSPORT_SCCPlite_SERVER or - cfg.transport == BSSAP_TRANSPORT_SCCPlite_CLIENT) { - /* connect IPA MGCP port with BSSMAP MGCP port */ - connect(ba.vc_IPA:IPA_MGCP_PORT, ba.vc_RAN:MGCP); - } - /* start the BSSMAP emulation */ - ba.vc_RAN.start(RAN_Emulation.main(valueof(ops), "")); - } - - -} - -function f_bssap_start(inout RAN_Adapter ba) { - ba.vc_SCCP.start(SCCPStart(ba.sccp_pars)); -} - - -} diff --git a/library/RAN_Adapter.ttcnpp b/library/RAN_Adapter.ttcnpp new file mode 100644 index 00000000..43b4988a --- /dev/null +++ b/library/RAN_Adapter.ttcnpp @@ -0,0 +1,165 @@ +module RAN_Adapter { + +/* This module implements a 'dumb' RAN adapter. It creates the M3UA and SCCP components and stacks a + * BSSAP/RANAP codec port on top. As a result, it provides the ability to transceive SCCP-User-SAP primitives + * with deoded BSSAP/RANAP payload. Use this if you want to have full control about what you transmit or + * receive, without any automatisms in place. Allows you to refuse connections or other abnormal behavior. */ + +import from General_Types all; +import from Osmocom_Types all; + +import from M3UA_Emulation all; +import from MTP3asp_Types all; +import from MTP3asp_PortType all; + +import from IPA_Emulation all; + +import from SCCP_Types all; +import from SCCPasp_Types all; +import from SCCP_Emulation all; +import from SCCP_Templates all; + +import from SCTPasp_Types all; +import from SCTPasp_PortType all; + +import from BSSMAP_Templates all; +import from RAN_Emulation all; + +type record RAN_Adapter { + /* component references */ + M3UA_CT vc_M3UA, /* only in 3GPP AoIP */ + IPA_Emulation_CT vc_IPA, /* only in SCCPlite */ + IPA_EventWaiter_CT vc_WAIT, /* only in SCCPlite */ + SCCP_CT vc_SCCP, + + MSC_SCCP_MTP3_parameters sccp_pars, + SCCP_PAR_Address sccp_addr_own, + SCCP_PAR_Address sccp_addr_peer, + + /* handler mode */ + RAN_Emulation_CT vc_RAN +} + +type enumerated RAN_Transport { + BSSAP_TRANSPORT_AoIP, /* 3GPP AoIP: SCCP over M3UA over SCTP */ + BSSAP_TRANSPORT_SCCPlite_SERVER, /* SCCPlite: SCCP over IPA over TCP */ + BSSAP_TRANSPORT_SCCPlite_CLIENT /* SCCPlite: SCCP over IPA over TCP */ +}; + +type record RAN_Configuration { + RAN_Transport transport, + charstring sccp_service_type, + SCTP_Association_Address sctp_addr, + integer own_pc, + integer own_ssn, + integer peer_pc, + integer peer_ssn, + octetstring sio, + integer rctx +}; + +private function init_pars(inout RAN_Adapter ba, in RAN_Configuration cfg) { + ba.sccp_pars := { + sio := { + ni := substr(oct2bit(cfg.sio),0,2), + prio := substr(oct2bit(cfg.sio),2,2), + si := substr(oct2bit(cfg.sio),4,4) + }, + opc := cfg.own_pc, + dpc := cfg.peer_pc, + sls := 0, + sccp_serviceType := cfg.sccp_service_type, + ssn := cfg.own_ssn + }; + ba.sccp_addr_own := valueof(ts_SccpAddr_PC_SSN(cfg.own_pc, cfg.own_ssn, cfg.sio, cfg.sccp_service_type)); + ba.sccp_addr_peer := valueof(ts_SccpAddr_PC_SSN(cfg.peer_pc, cfg.peer_ssn, cfg.sio, cfg.sccp_service_type)); +} + + +function f_bssap_init(inout RAN_Adapter ba, in RAN_Configuration cfg, charstring id, + template RanOps ops) { + init_pars(ba, cfg); + ops.sccp_addr_local := ba.sccp_addr_own; + ops.sccp_addr_peer := ba.sccp_addr_peer; + + /* create components */ + ba.vc_SCCP := SCCP_CT.create(id & "-SCCP"); + if (isvalue(ops)) { + ba.vc_RAN := RAN_Emulation_CT.create(id & "-RAN"); + } + select (cfg.transport) { +#ifdef RAN_EMULATION_BSSAP + case (BSSAP_TRANSPORT_AoIP) { + ba.vc_M3UA := M3UA_CT.create(id & "-M3UA"); + map(ba.vc_M3UA:SCTP_PORT, system:sctp); + /* connect MTP3 service provider (M3UA) to lower side of SCCP */ + connect(ba.vc_M3UA:MTP3_SP_PORT, ba.vc_SCCP:MTP3_SCCP_PORT); + ba.vc_M3UA.start(f_M3UA_Emulation(cfg.sctp_addr, cfg.rctx)); + } + case (BSSAP_TRANSPORT_SCCPlite_SERVER) { + ba.vc_IPA := IPA_Emulation_CT.create(id & "-IPA"); + map(ba.vc_IPA:IPA_PORT, system:IPA_CODEC_PT); + /* connect MTP3 service provider (IPA) to lower side of SCCP */ + connect(ba.vc_IPA:MTP3_SP_PORT, ba.vc_SCCP:MTP3_SCCP_PORT); + /* connect waiter to general IPA port (for ASP_IPA_Event) */ + ba.vc_WAIT := IPA_EventWaiter_CT.create(id & "-IPA-WAIT"); + connect(ba.vc_IPA:IPA_SP_PORT, ba.vc_WAIT:IPA_SP_PORT); + ba.vc_WAIT.start(IPA_Emulation.waiter_main()); + ba.vc_IPA.start(IPA_Emulation.main_server(cfg.sctp_addr.local_ip_addr, + cfg.sctp_addr.local_sctp_port, + true, IPA_INIT_SEND_IPA_ID_ACK)); + /* wait until we received an IPA CCM ID_ACK */ + ba.vc_WAIT.done; + disconnect(ba.vc_IPA:IPA_SP_PORT, ba.vc_WAIT:IPA_SP_PORT); + } + case (BSSAP_TRANSPORT_SCCPlite_CLIENT) { + ba.vc_IPA := IPA_Emulation_CT.create(id & "-IPA"); + map(ba.vc_IPA:IPA_PORT, system:IPA_CODEC_PT); + /* connect MTP3 service provider (IPA) to lower side of SCCP */ + connect(ba.vc_IPA:MTP3_SP_PORT, ba.vc_SCCP:MTP3_SCCP_PORT); + /* connect waiter to general IPA port (for ASP_IPA_Event) */ + ba.vc_WAIT := IPA_EventWaiter_CT.create(id & "-IPA-WAIT"); + connect(ba.vc_IPA:IPA_SP_PORT, ba.vc_WAIT:IPA_SP_PORT); + ba.vc_WAIT.start(IPA_Emulation.waiter_main()); + ba.vc_IPA.start(IPA_Emulation.main_client(cfg.sctp_addr.remote_ip_addr, + cfg.sctp_addr.remote_sctp_port, + cfg.sctp_addr.local_ip_addr, + cfg.sctp_addr.local_sctp_port)); + /* wait until we received an IPA CCM ID_ACK */ + ba.vc_WAIT.done; + disconnect(ba.vc_IPA:IPA_SP_PORT, ba.vc_WAIT:IPA_SP_PORT); + } +#endif + case else { + setverdict(fail, "Unsuppored RAN_Transport"); + mtc.stop; + } + } + + if (isvalue(ops)) { + timer T := 5.0; + T.start; + //T.timeout; + log("Connecting BSSMAP Emulation to SCCP_SP_PORT and starting emulation"); +#if RAN_EMULATION_BSSAP + /* connect BSSNAP component to upper side of SCCP */ + connect(ba.vc_RAN:BSSAP, ba.vc_SCCP:SCCP_SP_PORT); +#endif + if (cfg.transport == BSSAP_TRANSPORT_SCCPlite_SERVER or + cfg.transport == BSSAP_TRANSPORT_SCCPlite_CLIENT) { + /* connect IPA MGCP port with BSSMAP MGCP port */ + connect(ba.vc_IPA:IPA_MGCP_PORT, ba.vc_RAN:MGCP); + } + /* start the BSSMAP emulation */ + ba.vc_RAN.start(RAN_Emulation.main(valueof(ops), "")); + } + + +} + +function f_bssap_start(inout RAN_Adapter ba) { + ba.vc_SCCP.start(SCCPStart(ba.sccp_pars)); +} + + +} diff --git a/library/RAN_Emulation.ttcn b/library/RAN_Emulation.ttcn deleted file mode 100644 index 72e2733f..00000000 --- a/library/RAN_Emulation.ttcn +++ /dev/null @@ -1,836 +0,0 @@ -module RAN_Emulation { - -/* RAN Emulation, runs on top of BSSAP_CodecPort. It multiplexes/demultiplexes - * the individual connections, so there can be separate TTCN-3 components handling - * each of the connections. - * - * The RAN_Emulation.main() function processes SCCP primitives from the SCCP - * stack via the BSSAP_CodecPort, and dispatches them to the per-connection components. - * - * Outbound BSSAP/SCCP connections are initiated by sending a BSSAP_Conn_Req primitive - * to the component running the RAN_Emulation.main() function. - * - * For each new inbound connections, the RanOps.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 connection, it can - * do so by registering an "expect" with the expected Layer 3 (DTAP) payload. This is e.g. useful - * if you are simulating BTS + MSC, and first trigger a connection from BTS/RSL side in a - * component which then subsequently should also handle the MSC emulation. - * - * Inbound Unit Data messages (such as are dispatched to the RanOps.unitdata_cb() callback, - * which is registered with an argument to the main() function below. - * - * (C) 2017-2018 by Harald Welte - * All rights reserved. - * - * Released under the terms of GNU General Public License, Version 2 or - * (at your option) any later version. - */ - - -import from General_Types all; -import from Osmocom_Types all; -import from SCCP_Emulation all; -import from SCCPasp_Types all; -import from BSSAP_Types all; -import from BSSAP_CodecPort all; -import from BSSMAP_Templates all; -import from MGCP_Types all; -import from MGCP_Templates all; -import from IPA_Emulation all; -import from MobileL3_Types all; - -/* General "base class" component definition, of which specific implementations - * derive themselves by means of the "extends" feature */ -type component RAN_ConnHdlr { - /* port towards MSC Emulator core / SCCP connection dispatchar */ - port RAN_Conn_PT BSSAP; - /* procedure based port to register for incoming connections */ - port RAN_PROC_PT BSSAP_PROC; -} - -/* Auxiliary primitive that can happen on the port between per-connection client and this dispatcher */ -type enumerated RAN_Conn_Prim { - /* SCCP tell us that connection was released */ - MSC_CONN_PRIM_DISC_IND, - /* we tell SCCP to release connection */ - MSC_CONN_PRIM_DISC_REQ, - /* Connection confirmed indication */ - MSC_CONN_PRIM_CONF_IND -} - -type record BSSAP_Conn_Req { - SCCP_PAR_Address addr_peer, - SCCP_PAR_Address addr_own, - PDU_BSSAP bssap -} - -/* similar to PDU_BSSAP with DTAP, but DTAP is already decoded! */ -type record PDU_DTAP_MO { - OCT1 dlci optional, - boolean skip_seq_patching optional, - PDU_ML3_MS_NW dtap -} - -/* similar to PDU_BSSAP with DTAP, but DTAP is already decoded! */ -type record PDU_DTAP_MT { - OCT1 dlci optional, - PDU_ML3_NW_MS dtap -} - -template PDU_DTAP_MT ts_PDU_DTAP_MT(template PDU_ML3_NW_MS dtap, template OCT1 dlci := omit) := { - dlci := dlci, - dtap := dtap -} - -template PDU_DTAP_MO ts_PDU_DTAP_MO(template PDU_ML3_MS_NW dtap, template OCT1 dlci := '00'O, boolean skip_seq_patching := false) := { - dlci := dlci, - skip_seq_patching := skip_seq_patching, - dtap := dtap -} - -template PDU_DTAP_MT tr_PDU_DTAP_MT(template PDU_ML3_NW_MS dtap, template OCT1 dlci := *) := { - dlci := dlci, - dtap := dtap -} - -template PDU_DTAP_MO tr_PDU_DTAP_MO(template PDU_ML3_MS_NW dtap, template OCT1 dlci := *) := { - dlci := dlci, - skip_seq_patching := ?, - dtap := dtap -} - -template BSSAP_Conn_Req ts_BSSAP_Conn_Req(SCCP_PAR_Address peer, SCCP_PAR_Address own, PDU_BSSAP bssap) := { - addr_peer := peer, - addr_own := own, - bssap := bssap -}; - - -/* port between individual per-connection components and this dispatcher */ -type port RAN_Conn_PT message { - /* BSSAP or direct DTAP messages from/to clients */ - inout PDU_BSSAP, PDU_DTAP_MO, PDU_DTAP_MT, - /* misc indications / requests between SCCP and client */ - RAN_Conn_Prim, - /* Client requests us to create SCCP Connection */ - BSSAP_Conn_Req, - /* MGCP, only used for IPA SCCPlite (MGCP in IPA mux) */ - MgcpCommand, MgcpResponse; -} with { extension "internal" }; - - -/* represents a single BSSAP connection over SCCP */ -type record ConnectionData { - /* reference to the instance of the per-connection component */ - RAN_ConnHdlr comp_ref, - integer sccp_conn_id, - /* most recent MGCP transaction ID (Used on MSC side) */ - MgcpTransId mgcp_trans_id optional, - /* CIC that has been used for voice of this channel (BSC side) */ - integer cic optional, - /* array of N(SD) values for MO DTAP messages, indexed by discriminator */ - uint2_t n_sd[4] -} - -type record ImsiMapping { - RAN_ConnHdlr comp_ref, - hexstring imsi optional, - OCT4 tmsi -} - -type component RAN_Emulation_CT { - /* SCCP port on the bottom side, using ASP primitives */ - port BSSAP_CODEC_PT BSSAP; - /* BSSAP port to the per-connection clients */ - port RAN_Conn_PT CLIENT; - /* MGCP port */ - port IPA_MGCP_PT MGCP; - - /* use 16 as this is also the number of SCCP connections that SCCP_Emulation can handle */ - var ConnectionData ConnectionTable[16]; - - /* pending expected incoming connections */ - var ExpectData ExpectTable[8]; - - /* tables for mapping inbound unitdata (like paging) */ - var ImsiMapping ImsiTable[16]; - - /* procedure based port to register for incoming connections */ - port RAN_PROC_PT PROC; - - var charstring g_ran_id; - var integer g_next_e1_ts := 1; - var RanOps g_ran_ops; -}; - -private function f_conn_id_known(integer sccp_conn_id) -runs on RAN_Emulation_CT return boolean { - var integer i; - for (i := 0; i < sizeof(ConnectionTable); i := i+1) { - if (ConnectionTable[i].sccp_conn_id == sccp_conn_id){ - return true; - } - } - return false; -} - -private function f_comp_known(RAN_ConnHdlr client) -runs on RAN_Emulation_CT return boolean { - var integer i; - for (i := 0; i < sizeof(ConnectionTable); i := i+1) { - if (ConnectionTable[i].comp_ref == client) { - return true; - } - } - return false; -} - -private function f_cic_known(integer cic) -runs on RAN_Emulation_CT return boolean { - var integer i; - for (i := 0; i < sizeof(ConnectionTable); i := i+1) { - if (ConnectionTable[i].cic == cic) { - return true; - } - } - return false; -} - -/* resolve component reference by connection ID */ -private function f_comp_by_conn_id(integer sccp_conn_id) -runs on RAN_Emulation_CT return RAN_ConnHdlr { - var integer i; - for (i := 0; i < sizeof(ConnectionTable); i := i+1) { - if (ConnectionTable[i].sccp_conn_id == sccp_conn_id) { - return ConnectionTable[i].comp_ref; - } - } - setverdict(fail, "RAN Connection table not found by SCCP Connection ID ", sccp_conn_id); - mtc.stop; -} - -/* resolve component reference by CIC */ -private function f_comp_by_mgcp_tid(MgcpTransId tid) -runs on RAN_Emulation_CT return RAN_ConnHdlr { - var integer i; - for (i := 0; i < sizeof(ConnectionTable); i := i+1) { - if (ConnectionTable[i].mgcp_trans_id == tid) { - return ConnectionTable[i].comp_ref; - } - } - setverdict(fail, "RAN Connection table not found by MGCP Transaction ID ", tid); - mtc.stop; -} - -private function f_comp_store_mgcp_tid(RAN_ConnHdlr client, MgcpTransId tid) -runs on RAN_Emulation_CT { - var integer i; - for (i := 0; i < sizeof(ConnectionTable); i := i+1) { - if (ConnectionTable[i].comp_ref == client) { - ConnectionTable[i].mgcp_trans_id := tid; - return; - } - } - setverdict(fail, "RAN Connection table not found by component ", client); - mtc.stop; -} - -private function f_comp_by_cic(integer cic) -runs on RAN_Emulation_CT return RAN_ConnHdlr { - var integer i; - for (i := 0; i < sizeof(ConnectionTable); i := i+1) { - if (ConnectionTable[i].cic == cic) { - return ConnectionTable[i].comp_ref; - } - } - setverdict(fail, "RAN Connection table not found by CIC ", cic); - mtc.stop; -} - -private function f_comp_store_cic(RAN_ConnHdlr client, integer cic) -runs on RAN_Emulation_CT { - var integer i; - for (i := 0; i < sizeof(ConnectionTable); i := i+1) { - if (ConnectionTable[i].comp_ref == client) { - ConnectionTable[i].cic := cic; - return; - } - } - setverdict(fail, "RAN Connection table not found by component ", client); - mtc.stop; -} - -/* resolve connection ID by component reference */ -private function f_conn_id_by_comp(RAN_ConnHdlr client) -runs on RAN_Emulation_CT return integer { - for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) { - if (ConnectionTable[i].comp_ref == client) { - return ConnectionTable[i].sccp_conn_id; - } - } - setverdict(fail, "RAN Connection table not found by component ", client); - mtc.stop; -} - -/* resolve ConnectionTable index component reference */ -private function f_idx_by_comp(RAN_ConnHdlr client) -runs on RAN_Emulation_CT return integer { - for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) { - if (ConnectionTable[i].comp_ref == client) { - return i; - } - } - setverdict(fail, "RAN Connection table not found by component ", client); - mtc.stop; -} - -private function f_gen_conn_id() -runs on RAN_Emulation_CT return integer { - var integer conn_id; - - do { - conn_id := float2int(rnd()*SCCP_Emulation.tsp_max_ConnectionId); - } while (f_conn_id_known(conn_id) == true); - - return conn_id; -} - -private function f_conn_table_init() -runs on RAN_Emulation_CT { - for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) { - ConnectionTable[i].comp_ref := null; - ConnectionTable[i].sccp_conn_id := -1; - ConnectionTable[i].mgcp_trans_id := omit; - ConnectionTable[i].cic := omit; - ConnectionTable[i].n_sd := { 0, 0, 0, 0 }; - } - for (var integer i := 0; i < sizeof(ImsiTable); i := i+1) { - ImsiTable[i].comp_ref := null; - ImsiTable[i].imsi := omit; - ImsiTable[i].tmsi := 'FFFFFFFF'O; - } -} - -private function f_conn_table_add(RAN_ConnHdlr comp_ref, integer sccp_conn_id) -runs on RAN_Emulation_CT { - for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) { - if (ConnectionTable[i].sccp_conn_id == -1) { - ConnectionTable[i].comp_ref := comp_ref; - ConnectionTable[i].sccp_conn_id := sccp_conn_id; - ConnectionTable[i].n_sd := { 0, 0, 0, 0 }; - log("Added conn table entry ", i, comp_ref, sccp_conn_id); - return; - } - } - testcase.stop("RAN Connection table full!"); -} - -private function f_conn_table_del(integer sccp_conn_id) -runs on RAN_Emulation_CT { - for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) { - if (ConnectionTable[i].sccp_conn_id == sccp_conn_id) { - log("Deleted conn table entry ", i, - ConnectionTable[i].comp_ref, sccp_conn_id); - ConnectionTable[i].sccp_conn_id := -1; - ConnectionTable[i].comp_ref := null; - return - } - } - setverdict(fail, "RAN Connection table attempt to delete non-existant ", sccp_conn_id); - mtc.stop; -} - -private function f_imsi_table_find(hexstring imsi, template OCT4 tmsi) -runs on RAN_Emulation_CT return RAN_ConnHdlr { - for (var integer i := 0; i < sizeof(ImsiTable); i := i+1) { - if (ImsiTable[i].imsi == imsi or - isvalue(tmsi) and match(ImsiTable[i].tmsi, tmsi)) { - return ImsiTable[i].comp_ref; - } - } - return null; -} - -/* handle (optional) userData portion of various primitives and dispatch it to the client */ -private function f_handle_userData(RAN_ConnHdlr client, PDU_BSSAP bssap) -runs on RAN_Emulation_CT { - /* decode + send decoded BSSAP to client */ - - if (ischosen(bssap.pdu.bssmap)) { - /* BSC Side: If this is an assignment command, store CIC */ - if (ischosen(bssap.pdu.bssmap.assignmentRequest) and - ispresent(bssap.pdu.bssmap.assignmentRequest.circuitIdentityCode)) { - var BSSMAP_IE_CircuitIdentityCode cic_ie := - bssap.pdu.bssmap.assignmentRequest.circuitIdentityCode; - var integer cic := (oct2int(cic_ie.cicHigh) * 256) + oct2int(cic_ie.cicLow); - f_comp_store_cic(client, cic); - } - } - - if (ischosen(bssap.pdu.dtap) and g_ran_ops.decode_dtap) { - if (g_ran_ops.role_ms) { - /* we are the MS, so any message to us must be MT */ - var PDU_DTAP_MT mt := { - dlci := bssap.dlci, - dtap := dec_PDU_ML3_NW_MS(bssap.pdu.dtap) - }; - CLIENT.send(mt) to client; - } else { - /* we are the Network, so any message to us must be MO */ - var PDU_DTAP_MO mo := { - dlci := bssap.dlci, - dtap := dec_PDU_ML3_MS_NW(bssap.pdu.dtap) - }; - CLIENT.send(mo) to client; - } - } else { - CLIENT.send(bssap) to client; - } -} - -/* call-back type, to be provided by specific implementation; called when new SCCP connection - * arrives */ -type function BssmapCreateCallback(BSSAP_N_CONNECT_ind conn_ind, charstring id) -runs on RAN_Emulation_CT return RAN_ConnHdlr; - -type function BssmapUnitdataCallback(PDU_BSSAP bssap) -runs on RAN_Emulation_CT return template PDU_BSSAP; - -/* handle common Unitdata such as Paging */ -private function CommonBssmapUnitdataCallback(PDU_BSSAP bssap) -runs on RAN_Emulation_CT return template PDU_BSSAP { - if (match(bssap, tr_BSSMAP_Paging)) { - var RAN_ConnHdlr client := null; - client := f_imsi_table_find(bssap.pdu.bssmap.paging.iMSI.digits, - bssap.pdu.bssmap.paging.tMSI.tmsiOctets); - if (client != null) { - log("CommonBssmapUnitdataCallback: IMSI/TMSI found in table, dispatching to ", - client); - CLIENT.send(bssap) to client; - return omit; - } - log("CommonBssmapUnitdataCallback: IMSI/TMSI not found in table"); - } else { - log("CommonBssmapUnitdataCallback: Not a paging message"); - } - /* ELSE: handle in user callback */ - return g_ran_ops.unitdata_cb.apply(bssap); -} - -type record RanOps { - BssmapCreateCallback create_cb, - BssmapUnitdataCallback unitdata_cb, - boolean decode_dtap, - boolean role_ms, - /* needed for performing BSSMAP RESET */ - SCCP_PAR_Address sccp_addr_local optional, - SCCP_PAR_Address sccp_addr_peer optional -} - -template BIT4 t_ML3_DISC_CC_MM_SS := ('0011'B, '0101'B, '1011'B); - -/* patch N(SD) into enc_l3, according to 24.007 11.2.3.2 */ -function f_ML3_patch_seq(inout ConnectionData cd, in PDU_ML3_MS_NW dtap, inout octetstring enc_l3) { - var uint2_t seq_nr; - if (ischosen(dtap.msgs.cc) or ischosen(dtap.msgs.mm) or ischosen(dtap.msgs.ss)) { - seq_nr := cd.n_sd[0]; - cd.n_sd[0] := (cd.n_sd[0] + 1) mod 4; - } else if (ischosen(dtap.msgs.gcc)) { - seq_nr := cd.n_sd[1]; - cd.n_sd[1] := (cd.n_sd[1] + 1) mod 2; - } else if (ischosen(dtap.msgs.bcc)) { - seq_nr := cd.n_sd[2]; - cd.n_sd[2] := (cd.n_sd[2] + 1) mod 2; - } else if (ischosen(dtap.msgs.loc)) { - seq_nr := cd.n_sd[3]; - cd.n_sd[3] := (cd.n_sd[3] + 1) mod 2; - } else { - /* no sequence number to patch */ - return; - } - log("patching N(SD)=", seq_nr, " into dtap ", enc_l3); - enc_l3[1] := (enc_l3[1] and4b '3f'O) or4b bit2oct(int2bit(seq_nr, 8) << 6); - log("patched enc_l3: ", enc_l3); -} - -private function f_bssap_l3_is_rr(PDU_BSSAP bssap) return boolean { - var template octetstring l3 := f_bssap_extract_l3(bssap); - if (not isvalue(l3)) { - return false; - } - var octetstring l3v := valueof(l3); - if (lengthof(l3v) < 1) { - return false; - } - /* lower 4 bits of first octet are protocol discriminator */ - if ((oct2bit(l3v[0]) and4b '00001111'B) == '00000110'B) { - return true; - } - return false; -} - -private altstep as_reset_ack() runs on RAN_Emulation_CT { - var BSSAP_N_UNITDATA_ind ud_ind; - [] BSSAP.receive(tr_BSSAP_UNITDATA_ind(?, ?, tr_BSSMAP_Reset)) -> value ud_ind { - log("Respoding to inbound RESET with RESET-ACK"); - BSSAP.send(ts_BSSAP_UNITDATA_req(ud_ind.callingAddress, ud_ind.calledAddress, - ts_BSSMAP_ResetAck)); - repeat; - } -} - - -private function f_bssap_wait_for_reset() runs on RAN_Emulation_CT { - var BSSAP_N_UNITDATA_ind ud_ind; - timer T := 20.0; - - T.start; - alt { - [] BSSAP.receive(tr_BSSAP_UNITDATA_ind(?, ?, tr_BSSMAP_Reset)) -> value ud_ind { - BSSAP.send(ts_BSSAP_UNITDATA_req(ud_ind.callingAddress, ud_ind.calledAddress, - ts_BSSMAP_ResetAck)); - } - [] as_reset_ack(); - [] BSSAP.receive { - repeat; - } - [] T.timeout { - setverdict(fail, "Timeout waiting for BSSAP RESET"); - mtc.stop; - } - } -} - -function f_bssap_reset(SCCP_PAR_Address peer, SCCP_PAR_Address own) runs on RAN_Emulation_CT { - timer T := 5.0; - - BSSAP.send(ts_BSSAP_UNITDATA_req(peer, own, ts_BSSMAP_Reset(0))); - T.start; - alt { - [] BSSAP.receive(tr_BSSAP_UNITDATA_ind(own, peer, tr_BSSMAP_ResetAck)) { - log("Received RESET-ACK in response to RESET, we're ready to go!"); - } - [] as_reset_ack(); - [] BSSAP.receive { repeat }; - [] T.timeout { - setverdict(fail, "Timeout waiting for RESET-ACK after sending RESET"); - mtc.stop; - } - } -} - -function main(RanOps ops, charstring id) runs on RAN_Emulation_CT { - - g_ran_id := id; - g_ran_ops := ops; - f_conn_table_init(); - f_expect_table_init(); - - if (isvalue(ops.sccp_addr_peer) and isvalue(ops.sccp_addr_local)) { - f_sleep(1.0); /* HACK to wait for M3UA/ASP to be ACTIVE */ - f_bssap_reset(ops.sccp_addr_peer, ops.sccp_addr_local); - } - - while (true) { - var BSSAP_N_UNITDATA_ind ud_ind; - var BSSAP_N_CONNECT_ind conn_ind; - var BSSAP_N_CONNECT_cfm conn_cfm; - var BSSAP_N_DATA_ind data_ind; - var BSSAP_N_DISCONNECT_ind disc_ind; - var BSSAP_Conn_Req creq; - var RAN_ConnHdlr vc_conn; - var PDU_BSSAP bssap; - var PDU_DTAP_MO dtap_mo; - var PDU_DTAP_MT dtap_mt; - var MgcpCommand mgcp_req; - var MgcpResponse mgcp_resp; - var RAN_ConnHdlr vc_hdlr; - var octetstring l3_info; - var hexstring imsi; - var OCT4 tmsi; - - alt { - /* SCCP -> Client: UNIT-DATA (connectionless SCCP) from a BSC */ - [] BSSAP.receive(BSSAP_N_UNITDATA_ind:?) -> value ud_ind { - /* Connectionless Procedures like RESET */ - var template PDU_BSSAP resp; - resp := CommonBssmapUnitdataCallback(ud_ind.userData); - if (isvalue(resp)) { - BSSAP.send(ts_BSSAP_UNITDATA_req(ud_ind.callingAddress, - ud_ind.calledAddress, resp)); - } - } - - /* SCCP -> Client: new connection from BSC */ - [] BSSAP.receive(BSSAP_N_CONNECT_ind:?) -> value conn_ind { - vc_conn := ops.create_cb.apply(conn_ind, id); - /* store mapping between client components and SCCP connectionId */ - f_conn_table_add(vc_conn, conn_ind.connectionId); - /* handle user payload */ - f_handle_userData(vc_conn, conn_ind.userData); - /* confirm connection establishment */ - BSSAP.send(ts_BSSAP_CONNECT_res(conn_ind.connectionId, omit)); - } - - /* SCCP -> Client: connection-oriented data in existing connection */ - [] BSSAP.receive(BSSAP_N_DATA_ind:?) -> value data_ind { - vc_conn := f_comp_by_conn_id(data_ind.connectionId); - if (ispresent(data_ind.userData)) { - f_handle_userData(vc_conn, data_ind.userData); - } - } - - /* SCCP -> Client: disconnect of an existing connection */ - [] BSSAP.receive(BSSAP_N_DISCONNECT_ind:?) -> value disc_ind { - vc_conn := f_comp_by_conn_id(disc_ind.connectionId); - if (ispresent(disc_ind.userData)) { - f_handle_userData(vc_conn, disc_ind.userData); - } - /* notify client about termination */ - var RAN_Conn_Prim prim := MSC_CONN_PRIM_DISC_IND; - CLIENT.send(prim) to vc_conn; - f_conn_table_del(disc_ind.connectionId); - /* TOOD: return confirm to other side? */ - } - - /* SCCP -> Client: connection confirm for outbound connection */ - [] BSSAP.receive(BSSAP_N_CONNECT_cfm:?) -> value conn_cfm { - vc_conn := f_comp_by_conn_id(conn_cfm.connectionId); - var RAN_Conn_Prim prim := MSC_CONN_PRIM_CONF_IND; - CLIENT.send(prim) to vc_conn; - /* handle user payload */ - if (ispresent(conn_cfm.userData)) { - f_handle_userData(vc_conn, conn_cfm.userData); - } - } - - /* Disconnect request client -> SCCP */ - [] CLIENT.receive(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_REQ) -> sender vc_conn { - var integer conn_id := f_conn_id_by_comp(vc_conn); - BSSAP.send(ts_BSSAP_DISC_req(conn_id, 0)); - f_conn_table_del(conn_id); - } - - /* BSSAP from client -> SCCP */ - [] CLIENT.receive(BSSAP_Conn_Req:?) -> value creq sender vc_conn { - var integer conn_id; - /* send to dispatcher */ - - if (f_comp_known(vc_conn) == false) { - /* unknown client, create new connection */ - conn_id := f_gen_conn_id(); - - /* store mapping between client components and SCCP connectionId */ - f_conn_table_add(vc_conn, conn_id); - - BSSAP.send(ts_BSSAP_CONNECT_req(creq.addr_peer, creq.addr_own, conn_id, - creq.bssap)); - } else { - /* known client, send via existing connection */ - conn_id := f_conn_id_by_comp(vc_conn); - BSSAP.send(ts_BSSAP_DATA_req(conn_id, creq.bssap)); - } - - /* InitialL3 contains RR (PAG RESP) or MM (CM SRV REQ), we must increment - * counter only on MM/CC/SS, but not on RR */ - if (g_ran_ops.role_ms and not f_bssap_l3_is_rr(creq.bssap)) { - /* we have just sent the first MM message, increment the counter */ - var integer idx := f_idx_by_comp(vc_conn); - ConnectionTable[idx].n_sd[0] := 1; - log("patch: N(SD) for ConnIdx ", idx, " set to 1"); - } - - } - - [] CLIENT.receive(PDU_BSSAP:?) -> value bssap sender vc_conn { - var integer conn_id := f_conn_id_by_comp(vc_conn); - /* send it to dispatcher */ - BSSAP.send(ts_BSSAP_DATA_req(conn_id, bssap)); - } - - [g_ran_ops.role_ms] CLIENT.receive(PDU_DTAP_MO:?) -> value dtap_mo sender vc_conn { - var integer idx := f_idx_by_comp(vc_conn); - /* convert from decoded DTAP to encoded DTAP */ - var octetstring l3_enc := enc_PDU_ML3_MS_NW(dtap_mo.dtap); - /* patch correct L3 send sequence number N(SD) into l3_enc */ - if (dtap_mo.skip_seq_patching == false) { - f_ML3_patch_seq(ConnectionTable[idx], dtap_mo.dtap, l3_enc); - } - bssap := valueof(ts_BSSAP_DTAP(l3_enc, dtap_mo.dlci)); - BSSAP.send(ts_BSSAP_DATA_req(ConnectionTable[idx].sccp_conn_id, bssap)); - } - - [not g_ran_ops.role_ms] CLIENT.receive(PDU_DTAP_MT:?) -> value dtap_mt sender vc_conn { - var integer conn_id := f_conn_id_by_comp(vc_conn); - /* convert from decoded DTAP to encoded DTAP */ - var octetstring l3_enc := enc_PDU_ML3_NW_MS(dtap_mt.dtap); - bssap := valueof(ts_BSSAP_DTAP(l3_enc, dtap_mo.dlci)); - BSSAP.send(ts_BSSAP_DATA_req(conn_id, bssap)); - } - - - /* Handling of MGCP in IPA SCCPLite case. This predates 3GPP AoIP - * and uses a MGCP session in parallel to BSSAP. BSSAP uses CIC - * as usual, and MGCP uses "CIC@mgw" endpoint naming, where CIC - * is printed as hex string, e.g. a@mgw for CIC 10 */ - - /* CLIENT -> MGCP */ - [] CLIENT.receive(MgcpCommand:?) -> value mgcp_req sender vc_conn { - /* MGCP request from Handler (we're MSC) */ - /* store the transaction ID we've seen */ - f_comp_store_mgcp_tid(vc_conn, mgcp_req.line.trans_id); - /* simply forward any MGCP from the client to the port */ - MGCP.send(mgcp_req); - } - [] CLIENT.receive(MgcpResponse:?) -> value mgcp_resp sender vc_conn { - /* MGCP response from Handler (we're BSC/MGW) */ - /* simply forward any MGCP from the client to the port */ - MGCP.send(mgcp_resp); - } - - /* MGCP -> CLIENT */ - [] MGCP.receive(MgcpCommand:?) -> value mgcp_req { - /* MGCP request from network side (we're BSC/MGW) */ - /* Extract CIC from local part of endpoint name */ - var integer cic := f_mgcp_ep_extract_cic(mgcp_req.line.ep); - if (match(mgcp_req, tr_RSIP) and f_cic_known(cic) == false) { - /* ignore RSIP for unknown CIC */ - } else { - /* Resolve the vc_conn by the CIC */ - vc_conn := f_comp_by_cic(cic); - CLIENT.send(mgcp_req) to vc_conn; - } - } - [] MGCP.receive(MgcpResponse:?) -> value mgcp_resp { - /* MGCP response from network side (we're MSC) */ - /* Resolve the vc_conn by the transaction ID */ - vc_conn := f_comp_by_mgcp_tid(mgcp_resp.line.trans_id); - CLIENT.send(mgcp_resp) to vc_conn; - } - - - [] PROC.getcall(RAN_register:{?,?}) -> param(l3_info, vc_hdlr) { - f_create_expect(l3_info, vc_hdlr); - PROC.reply(RAN_register:{l3_info, vc_hdlr}) to vc_hdlr; - } - - [] PROC.getcall(RAN_register_imsi:{?,?,?}) -> param(imsi, tmsi, vc_hdlr) { - f_create_imsi(imsi, tmsi, vc_hdlr); - PROC.reply(RAN_register_imsi:{imsi, tmsi, vc_hdlr}) to 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 SCCP connections) - ***********************************************************************/ - -/* data about an expected future incoming connection */ -type record ExpectData { - /* L3 payload based on which we can match it */ - octetstring l3_payload optional, - /* component reference for this connection */ - RAN_ConnHdlr vc_conn -} - -/* procedure based port to register for incoming connections */ -signature RAN_register(in octetstring l3, in RAN_ConnHdlr hdlr); - -/* procedure based port to register for incoming IMSI/TMSI */ -signature RAN_register_imsi(in hexstring imsi, in OCT4 tmsi, in RAN_ConnHdlr hdlr); - -type port RAN_PROC_PT procedure { - inout RAN_register, RAN_register_imsi; -} with { extension "internal" }; - -/* CreateCallback that can be used as create_cb and will use the expectation table */ -function ExpectedCreateCallback(BSSAP_N_CONNECT_ind conn_ind, charstring id) -runs on RAN_Emulation_CT return RAN_ConnHdlr { - var RAN_ConnHdlr ret := null; - var octetstring l3_info; - var integer i; - - if (not ischosen(conn_ind.userData.pdu.bssmap.completeLayer3Information)) { - setverdict(fail, "N-CONNECT.ind with L3 != COMPLETE L3"); - mtc.stop; - return ret; - } - l3_info := conn_ind.userData.pdu.bssmap.completeLayer3Information.layer3Information.layer3info; - - for (i := 0; i < sizeof(ExpectTable); i:= i+1) { - if (not ispresent(ExpectTable[i].l3_payload)) { - continue; - } - if (l3_info == ExpectTable[i].l3_payload) { - ret := ExpectTable[i].vc_conn; - /* release this entry to be used again */ - ExpectTable[i].l3_payload := omit; - ExpectTable[i].vc_conn := null; - log("Found Expect[", i, "] for ", l3_info, " handled at ", ret); - /* return the component reference */ - return ret; - } - } - setverdict(fail, "Couldn't find Expect for incoming connection ", conn_ind); - mtc.stop; - return ret; -} - -private function f_create_expect(octetstring l3, RAN_ConnHdlr hdlr) -runs on RAN_Emulation_CT { - var integer i; - for (i := 0; i < sizeof(ExpectTable); i := i+1) { - if (not ispresent(ExpectTable[i].l3_payload)) { - ExpectTable[i].l3_payload := l3; - ExpectTable[i].vc_conn := hdlr; - log("Created Expect[", i, "] for ", l3, " to be handled at ", hdlr); - return; - } - } - testcase.stop("No space left in ExpectTable"); -} - -private function f_create_imsi(hexstring imsi, OCT4 tmsi, RAN_ConnHdlr hdlr) -runs on RAN_Emulation_CT { - for (var integer i := 0; i < sizeof(ImsiTable); i := i+1) { - if (not ispresent(ImsiTable[i].imsi)) { - ImsiTable[i].imsi := imsi; - ImsiTable[i].tmsi := tmsi; - ImsiTable[i].comp_ref := hdlr; - log("Created IMSI[", i, "] for ", imsi, tmsi, " to be handled at ", hdlr); - return; - } - } - testcase.stop("No space left in ImsiTable"); -} - - -private function f_expect_table_init() -runs on RAN_Emulation_CT { - for (var integer i := 0; i < sizeof(ExpectTable); i := i+1) { - ExpectTable[i].l3_payload := omit; - } -} - -/* helper function for clients to register their IMSI/TMSI */ -function f_ran_register_imsi(hexstring imsi, OCT4 tmsi) -runs on RAN_ConnHdlr { - BSSAP_PROC.call(RAN_register_imsi:{imsi, tmsi, self}) { - [] BSSAP_PROC.getreply(RAN_register_imsi:{?,?,?}) {}; - } -} - - -} diff --git a/library/RAN_Emulation.ttcnpp b/library/RAN_Emulation.ttcnpp new file mode 100644 index 00000000..e0911331 --- /dev/null +++ b/library/RAN_Emulation.ttcnpp @@ -0,0 +1,903 @@ +module RAN_Emulation { + +/* RAN Emulation, runs on top of BSSAP_CodecPort. It multiplexes/demultiplexes + * the individual connections, so there can be separate TTCN-3 components handling + * each of the connections. + * + * The RAN_Emulation.main() function processes SCCP primitives from the SCCP + * stack via the BSSAP_CodecPort, and dispatches them to the per-connection components. + * + * Outbound BSSAP/SCCP connections are initiated by sending a BSSAP_Conn_Req primitive + * to the component running the RAN_Emulation.main() function. + * + * For each new inbound connections, the RanOps.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 connection, it can + * do so by registering an "expect" with the expected Layer 3 (DTAP) payload. This is e.g. useful + * if you are simulating BTS + MSC, and first trigger a connection from BTS/RSL side in a + * component which then subsequently should also handle the MSC emulation. + * + * Inbound Unit Data messages (such as are dispatched to the RanOps.unitdata_cb() callback, + * which is registered with an argument to the main() function below. + * + * (C) 2017-2019 by Harald Welte + * All rights reserved. + * + * Released under the terms of GNU General Public License, Version 2 or + * (at your option) any later version. + */ + + +import from General_Types all; +import from Osmocom_Types all; +import from SCCP_Emulation all; +import from SCCPasp_Types all; +import from IPA_Emulation all; +import from MobileL3_Types all; + +#ifdef RAN_EMULATION_BSSAP +import from BSSAP_Types all; +import from BSSAP_CodecPort all; +import from BSSMAP_Templates all; +#endif + +#ifdef RAN_EMULATION_MGCP +import from MGCP_Types all; +import from MGCP_Templates all; +#endif + +/* General "base class" component definition, of which specific implementations + * derive themselves by means of the "extends" feature */ +type component RAN_ConnHdlr { + /* port towards MSC Emulator core / SCCP connection dispatchar */ + port RAN_Conn_PT BSSAP; + /* procedure based port to register for incoming connections */ + port RAN_PROC_PT BSSAP_PROC; +} + +/* Auxiliary primitive that can happen on the port between per-connection client and this dispatcher */ +type enumerated RAN_Conn_Prim { + /* SCCP tell us that connection was released */ + MSC_CONN_PRIM_DISC_IND, + /* we tell SCCP to release connection */ + MSC_CONN_PRIM_DISC_REQ, + /* Connection confirmed indication */ + MSC_CONN_PRIM_CONF_IND +} + +/* similar to PDU_BSSAP with DTAP, but DTAP is already decoded! */ +type record PDU_DTAP_MO { + OCT1 dlci optional, + boolean skip_seq_patching optional, + PDU_ML3_MS_NW dtap +} + +/* similar to PDU_BSSAP with DTAP, but DTAP is already decoded! */ +type record PDU_DTAP_MT { + OCT1 dlci optional, + PDU_ML3_NW_MS dtap +} + +template PDU_DTAP_MT ts_PDU_DTAP_MT(template PDU_ML3_NW_MS dtap, template OCT1 dlci := omit) := { + dlci := dlci, + dtap := dtap +} + +template PDU_DTAP_MO ts_PDU_DTAP_MO(template PDU_ML3_MS_NW dtap, template OCT1 dlci := '00'O, boolean skip_seq_patching := false) := { + dlci := dlci, + skip_seq_patching := skip_seq_patching, + dtap := dtap +} + +template PDU_DTAP_MT tr_PDU_DTAP_MT(template PDU_ML3_NW_MS dtap, template OCT1 dlci := *) := { + dlci := dlci, + dtap := dtap +} + +template PDU_DTAP_MO tr_PDU_DTAP_MO(template PDU_ML3_MS_NW dtap, template OCT1 dlci := *) := { + dlci := dlci, + skip_seq_patching := ?, + dtap := dtap +} + +/* port between individual per-connection components and this dispatcher */ +type port RAN_Conn_PT message { + inout +#ifdef RAN_EMULATION_BSSAP + PDU_BSSAP, + /* Client requests us to create SCCP Connection */ + BSSAP_Conn_Req, +#endif +#ifdef RAN_EMULATION_MGCP + /* MGCP, only used for IPA SCCPlite (MGCP in IPA mux) */ + MgcpCommand, MgcpResponse, +#endif + /* direct DTAP messages from/to clients */ + PDU_DTAP_MO, PDU_DTAP_MT, + /* misc indications / requests between SCCP and client */ + RAN_Conn_Prim; +} with { extension "internal" }; + + +/* represents a single BSSAP connection over SCCP */ +type record ConnectionData { + /* reference to the instance of the per-connection component */ + RAN_ConnHdlr comp_ref, + integer sccp_conn_id, +#ifdef RAN_EMULATION_MGCP + /* most recent MGCP transaction ID (Used on MSC side) */ + MgcpTransId mgcp_trans_id optional, +#endif + /* CIC that has been used for voice of this channel (BSC side) */ + integer cic optional, + /* array of N(SD) values for MO DTAP messages, indexed by discriminator */ + uint2_t n_sd[4] +} + +type record ImsiMapping { + RAN_ConnHdlr comp_ref, + hexstring imsi optional, + OCT4 tmsi +} + +type component RAN_Emulation_CT { + /* SCCP ports on the bottom side, using ASP primitives */ +#ifdef RAN_EMULATION_BSSAP + port BSSAP_CODEC_PT BSSAP; +#endif + /* BSSAP port to the per-connection clients */ + port RAN_Conn_PT CLIENT; +#ifdef RAN_EMULATION_MGCP + /* MGCP port */ + port IPA_MGCP_PT MGCP; +#endif + + /* use 16 as this is also the number of SCCP connections that SCCP_Emulation can handle */ + var ConnectionData ConnectionTable[16]; + + /* pending expected incoming connections */ + var ExpectData ExpectTable[8]; + + /* tables for mapping inbound unitdata (like paging) */ + var ImsiMapping ImsiTable[16]; + + /* procedure based port to register for incoming connections */ + port RAN_PROC_PT PROC; + + var charstring g_ran_id; + var integer g_next_e1_ts := 1; + var RanOps g_ran_ops; +}; + +private function f_conn_id_known(integer sccp_conn_id) +runs on RAN_Emulation_CT return boolean { + var integer i; + for (i := 0; i < sizeof(ConnectionTable); i := i+1) { + if (ConnectionTable[i].sccp_conn_id == sccp_conn_id){ + return true; + } + } + return false; +} + +private function f_comp_known(RAN_ConnHdlr client) +runs on RAN_Emulation_CT return boolean { + var integer i; + for (i := 0; i < sizeof(ConnectionTable); i := i+1) { + if (ConnectionTable[i].comp_ref == client) { + return true; + } + } + return false; +} + +private function f_cic_known(integer cic) +runs on RAN_Emulation_CT return boolean { + var integer i; + for (i := 0; i < sizeof(ConnectionTable); i := i+1) { + if (ConnectionTable[i].cic == cic) { + return true; + } + } + return false; +} + +/* resolve component reference by connection ID */ +private function f_comp_by_conn_id(integer sccp_conn_id) +runs on RAN_Emulation_CT return RAN_ConnHdlr { + var integer i; + for (i := 0; i < sizeof(ConnectionTable); i := i+1) { + if (ConnectionTable[i].sccp_conn_id == sccp_conn_id) { + return ConnectionTable[i].comp_ref; + } + } + setverdict(fail, "RAN Connection table not found by SCCP Connection ID ", sccp_conn_id); + mtc.stop; +} + + +#ifdef RAN_EMULATION_MGCP +/* resolve component reference by CIC */ +private function f_comp_by_mgcp_tid(MgcpTransId tid) +runs on RAN_Emulation_CT return RAN_ConnHdlr { + var integer i; + for (i := 0; i < sizeof(ConnectionTable); i := i+1) { + if (ConnectionTable[i].mgcp_trans_id == tid) { + return ConnectionTable[i].comp_ref; + } + } + setverdict(fail, "RAN Connection table not found by MGCP Transaction ID ", tid); + mtc.stop; +} + +private function f_comp_store_mgcp_tid(RAN_ConnHdlr client, MgcpTransId tid) +runs on RAN_Emulation_CT { + var integer i; + for (i := 0; i < sizeof(ConnectionTable); i := i+1) { + if (ConnectionTable[i].comp_ref == client) { + ConnectionTable[i].mgcp_trans_id := tid; + return; + } + } + setverdict(fail, "RAN Connection table not found by component ", client); + mtc.stop; +} +#endif + +private function f_comp_by_cic(integer cic) +runs on RAN_Emulation_CT return RAN_ConnHdlr { + var integer i; + for (i := 0; i < sizeof(ConnectionTable); i := i+1) { + if (ConnectionTable[i].cic == cic) { + return ConnectionTable[i].comp_ref; + } + } + setverdict(fail, "RAN Connection table not found by CIC ", cic); + mtc.stop; +} + +private function f_comp_store_cic(RAN_ConnHdlr client, integer cic) +runs on RAN_Emulation_CT { + var integer i; + for (i := 0; i < sizeof(ConnectionTable); i := i+1) { + if (ConnectionTable[i].comp_ref == client) { + ConnectionTable[i].cic := cic; + return; + } + } + setverdict(fail, "RAN Connection table not found by component ", client); + mtc.stop; +} + +/* resolve connection ID by component reference */ +private function f_conn_id_by_comp(RAN_ConnHdlr client) +runs on RAN_Emulation_CT return integer { + for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) { + if (ConnectionTable[i].comp_ref == client) { + return ConnectionTable[i].sccp_conn_id; + } + } + setverdict(fail, "RAN Connection table not found by component ", client); + mtc.stop; +} + +/* resolve ConnectionTable index component reference */ +private function f_idx_by_comp(RAN_ConnHdlr client) +runs on RAN_Emulation_CT return integer { + for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) { + if (ConnectionTable[i].comp_ref == client) { + return i; + } + } + setverdict(fail, "RAN Connection table not found by component ", client); + mtc.stop; +} + +private function f_gen_conn_id() +runs on RAN_Emulation_CT return integer { + var integer conn_id; + + do { + conn_id := float2int(rnd()*SCCP_Emulation.tsp_max_ConnectionId); + } while (f_conn_id_known(conn_id) == true); + + return conn_id; +} + +private function f_conn_table_init() +runs on RAN_Emulation_CT { + for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) { + ConnectionTable[i].comp_ref := null; + ConnectionTable[i].sccp_conn_id := -1; +#ifdef RAN_EMULATION_MGCP + ConnectionTable[i].mgcp_trans_id := omit; +#endif + ConnectionTable[i].cic := omit; + ConnectionTable[i].n_sd := { 0, 0, 0, 0 }; + } + for (var integer i := 0; i < sizeof(ImsiTable); i := i+1) { + ImsiTable[i].comp_ref := null; + ImsiTable[i].imsi := omit; + ImsiTable[i].tmsi := 'FFFFFFFF'O; + } +} + +private function f_conn_table_add(RAN_ConnHdlr comp_ref, integer sccp_conn_id) +runs on RAN_Emulation_CT { + for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) { + if (ConnectionTable[i].sccp_conn_id == -1) { + ConnectionTable[i].comp_ref := comp_ref; + ConnectionTable[i].sccp_conn_id := sccp_conn_id; + ConnectionTable[i].n_sd := { 0, 0, 0, 0 }; + log("Added conn table entry ", i, comp_ref, sccp_conn_id); + return; + } + } + testcase.stop("RAN Connection table full!"); +} + +private function f_conn_table_del(integer sccp_conn_id) +runs on RAN_Emulation_CT { + for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) { + if (ConnectionTable[i].sccp_conn_id == sccp_conn_id) { + log("Deleted conn table entry ", i, + ConnectionTable[i].comp_ref, sccp_conn_id); + ConnectionTable[i].sccp_conn_id := -1; + ConnectionTable[i].comp_ref := null; + return + } + } + setverdict(fail, "RAN Connection table attempt to delete non-existant ", sccp_conn_id); + mtc.stop; +} + +private function f_imsi_table_find(hexstring imsi, template OCT4 tmsi) +runs on RAN_Emulation_CT return RAN_ConnHdlr { + for (var integer i := 0; i < sizeof(ImsiTable); i := i+1) { + if (ImsiTable[i].imsi == imsi or + isvalue(tmsi) and match(ImsiTable[i].tmsi, tmsi)) { + return ImsiTable[i].comp_ref; + } + } + return null; +} + +#ifdef RAN_EMULATION_BSSAP +type record BSSAP_Conn_Req { + SCCP_PAR_Address addr_peer, + SCCP_PAR_Address addr_own, + PDU_BSSAP bssap +} +template BSSAP_Conn_Req ts_BSSAP_Conn_Req(SCCP_PAR_Address peer, SCCP_PAR_Address own, PDU_BSSAP bssap) := { + addr_peer := peer, + addr_own := own, + bssap := bssap +}; + +/* handle (optional) userData portion of various primitives and dispatch it to the client */ +private function f_handle_userData(RAN_ConnHdlr client, PDU_BSSAP bssap) +runs on RAN_Emulation_CT { + /* decode + send decoded BSSAP to client */ + + if (ischosen(bssap.pdu.bssmap)) { + /* BSC Side: If this is an assignment command, store CIC */ + if (ischosen(bssap.pdu.bssmap.assignmentRequest) and + ispresent(bssap.pdu.bssmap.assignmentRequest.circuitIdentityCode)) { + var BSSMAP_IE_CircuitIdentityCode cic_ie := + bssap.pdu.bssmap.assignmentRequest.circuitIdentityCode; + var integer cic := (oct2int(cic_ie.cicHigh) * 256) + oct2int(cic_ie.cicLow); + f_comp_store_cic(client, cic); + } + } + + if (ischosen(bssap.pdu.dtap) and g_ran_ops.decode_dtap) { + if (g_ran_ops.role_ms) { + /* we are the MS, so any message to us must be MT */ + var PDU_DTAP_MT mt := { + dlci := bssap.dlci, + dtap := dec_PDU_ML3_NW_MS(bssap.pdu.dtap) + }; + CLIENT.send(mt) to client; + } else { + /* we are the Network, so any message to us must be MO */ + var PDU_DTAP_MO mo := { + dlci := bssap.dlci, + dtap := dec_PDU_ML3_MS_NW(bssap.pdu.dtap) + }; + CLIENT.send(mo) to client; + } + } else { + CLIENT.send(bssap) to client; + } +} + +/* call-back type, to be provided by specific implementation; called when new SCCP connection + * arrives */ +type function BssmapCreateCallback(BSSAP_N_CONNECT_ind conn_ind, charstring id) +runs on RAN_Emulation_CT return RAN_ConnHdlr; + +type function BssmapUnitdataCallback(PDU_BSSAP bssap) +runs on RAN_Emulation_CT return template PDU_BSSAP; + +/* handle common Unitdata such as Paging */ +private function CommonBssmapUnitdataCallback(PDU_BSSAP bssap) +runs on RAN_Emulation_CT return template PDU_BSSAP { + if (match(bssap, tr_BSSMAP_Paging)) { + var RAN_ConnHdlr client := null; + client := f_imsi_table_find(bssap.pdu.bssmap.paging.iMSI.digits, + bssap.pdu.bssmap.paging.tMSI.tmsiOctets); + if (client != null) { + log("CommonBssmapUnitdataCallback: IMSI/TMSI found in table, dispatching to ", + client); + CLIENT.send(bssap) to client; + return omit; + } + log("CommonBssmapUnitdataCallback: IMSI/TMSI not found in table"); + } else { + log("CommonBssmapUnitdataCallback: Not a paging message"); + } + /* ELSE: handle in user callback */ + return g_ran_ops.unitdata_cb.apply(bssap); +} + +private function f_bssap_wait_for_reset() runs on RAN_Emulation_CT { + var BSSAP_N_UNITDATA_ind ud_ind; + timer T := 20.0; + + T.start; + alt { + [] BSSAP.receive(tr_BSSAP_UNITDATA_ind(?, ?, tr_BSSMAP_Reset)) -> value ud_ind { + BSSAP.send(ts_BSSAP_UNITDATA_req(ud_ind.callingAddress, ud_ind.calledAddress, + ts_BSSMAP_ResetAck)); + } + [] as_reset_ack(); + [] BSSAP.receive { + repeat; + } + [] T.timeout { + setverdict(fail, "Timeout waiting for BSSAP RESET"); + mtc.stop; + } + } +} + +function f_bssap_reset(SCCP_PAR_Address peer, SCCP_PAR_Address own) runs on RAN_Emulation_CT { + timer T := 5.0; + + BSSAP.send(ts_BSSAP_UNITDATA_req(peer, own, ts_BSSMAP_Reset(0))); + T.start; + alt { + [] BSSAP.receive(tr_BSSAP_UNITDATA_ind(own, peer, tr_BSSMAP_ResetAck)) { + log("Received RESET-ACK in response to RESET, we're ready to go!"); + } + [] as_reset_ack(); + [] BSSAP.receive { repeat }; + [] T.timeout { + setverdict(fail, "Timeout waiting for RESET-ACK after sending RESET"); + mtc.stop; + } + } +} + +private function f_bssap_l3_is_rr(PDU_BSSAP bssap) return boolean { + var template octetstring l3 := f_bssap_extract_l3(bssap); + return f_L3_is_rr(l3); +} +#endif + + + +type enumerated RanProtocol { + RAN_PROTOCOL_BSSAP +} + +type record RanOps { +#ifdef RAN_EMULATION_BSSAP + BssmapCreateCallback create_cb optional, + BssmapUnitdataCallback unitdata_cb optional, +#endif + boolean decode_dtap, + boolean role_ms, + RanProtocol protocol, + /* needed for performing BSSMAP RESET */ + SCCP_PAR_Address sccp_addr_local optional, + SCCP_PAR_Address sccp_addr_peer optional +} + +template BIT4 t_ML3_DISC_CC_MM_SS := ('0011'B, '0101'B, '1011'B); + +private function f_L3_is_rr(template octetstring l3) return boolean { + if (not isvalue(l3)) { + return false; + } + var octetstring l3v := valueof(l3); + if (lengthof(l3v) < 1) { + return false; + } + /* lower 4 bits of first octet are protocol discriminator */ + if ((oct2bit(l3v[0]) and4b '00001111'B) == '00000110'B) { + return true; + } + return false; +} + +/* patch N(SD) into enc_l3, according to 24.007 11.2.3.2 */ +function f_ML3_patch_seq(inout ConnectionData cd, in PDU_ML3_MS_NW dtap, inout octetstring enc_l3) { + var uint2_t seq_nr; + if (ischosen(dtap.msgs.cc) or ischosen(dtap.msgs.mm) or ischosen(dtap.msgs.ss)) { + seq_nr := cd.n_sd[0]; + cd.n_sd[0] := (cd.n_sd[0] + 1) mod 4; + } else if (ischosen(dtap.msgs.gcc)) { + seq_nr := cd.n_sd[1]; + cd.n_sd[1] := (cd.n_sd[1] + 1) mod 2; + } else if (ischosen(dtap.msgs.bcc)) { + seq_nr := cd.n_sd[2]; + cd.n_sd[2] := (cd.n_sd[2] + 1) mod 2; + } else if (ischosen(dtap.msgs.loc)) { + seq_nr := cd.n_sd[3]; + cd.n_sd[3] := (cd.n_sd[3] + 1) mod 2; + } else { + /* no sequence number to patch */ + return; + } + log("patching N(SD)=", seq_nr, " into dtap ", enc_l3); + enc_l3[1] := (enc_l3[1] and4b '3f'O) or4b bit2oct(int2bit(seq_nr, 8) << 6); + log("patched enc_l3: ", enc_l3); +} + +private altstep as_reset_ack() runs on RAN_Emulation_CT { +#ifdef RAN_EMULATION_BSSAP + var BSSAP_N_UNITDATA_ind ud_ind; +#endif +#ifdef RAN_EMULATION_BSSAP + [] BSSAP.receive(tr_BSSAP_UNITDATA_ind(?, ?, tr_BSSMAP_Reset)) -> value ud_ind { + log("Respoding to inbound RESET with RESET-ACK"); + BSSAP.send(ts_BSSAP_UNITDATA_req(ud_ind.callingAddress, ud_ind.calledAddress, + ts_BSSMAP_ResetAck)); + repeat; + } +#endif +} + + +private altstep as_main_bssap() runs on RAN_Emulation_CT { +#ifdef RAN_EMULATION_BSSAP + var BSSAP_N_UNITDATA_ind ud_ind; + var BSSAP_N_CONNECT_ind conn_ind; + var BSSAP_N_CONNECT_cfm conn_cfm; + var BSSAP_N_DATA_ind data_ind; + var BSSAP_N_DISCONNECT_ind disc_ind; + var BSSAP_Conn_Req creq; + var PDU_BSSAP bssap; + var RAN_ConnHdlr vc_conn; + + /* SCCP -> Client: UNIT-DATA (connectionless SCCP) from a BSC */ + [] BSSAP.receive(BSSAP_N_UNITDATA_ind:?) -> value ud_ind { + /* Connectionless Procedures like RESET */ + var template PDU_BSSAP resp; + resp := CommonBssmapUnitdataCallback(ud_ind.userData); + if (isvalue(resp)) { + BSSAP.send(ts_BSSAP_UNITDATA_req(ud_ind.callingAddress, + ud_ind.calledAddress, resp)); + } + } + /* SCCP -> Client: new connection from BSC */ + [] BSSAP.receive(BSSAP_N_CONNECT_ind:?) -> value conn_ind { + vc_conn := g_ran_ops.create_cb.apply(conn_ind, g_ran_id); + /* store mapping between client components and SCCP connectionId */ + f_conn_table_add(vc_conn, conn_ind.connectionId); + /* handle user payload */ + f_handle_userData(vc_conn, conn_ind.userData); + /* confirm connection establishment */ + BSSAP.send(ts_BSSAP_CONNECT_res(conn_ind.connectionId, omit)); + } + /* SCCP -> Client: connection-oriented data in existing connection */ + [] BSSAP.receive(BSSAP_N_DATA_ind:?) -> value data_ind { + vc_conn := f_comp_by_conn_id(data_ind.connectionId); + if (ispresent(data_ind.userData)) { + f_handle_userData(vc_conn, data_ind.userData); + } + } + /* SCCP -> Client: disconnect of an existing connection */ + [] BSSAP.receive(BSSAP_N_DISCONNECT_ind:?) -> value disc_ind { + vc_conn := f_comp_by_conn_id(disc_ind.connectionId); + if (ispresent(disc_ind.userData)) { + f_handle_userData(vc_conn, disc_ind.userData); + } + /* notify client about termination */ + var RAN_Conn_Prim prim := MSC_CONN_PRIM_DISC_IND; + CLIENT.send(prim) to vc_conn; + f_conn_table_del(disc_ind.connectionId); + /* TOOD: return confirm to other side? */ + } + /* SCCP -> Client: connection confirm for outbound connection */ + [] BSSAP.receive(BSSAP_N_CONNECT_cfm:?) -> value conn_cfm { + vc_conn := f_comp_by_conn_id(conn_cfm.connectionId); + var RAN_Conn_Prim prim := MSC_CONN_PRIM_CONF_IND; + CLIENT.send(prim) to vc_conn; + /* handle user payload */ + if (ispresent(conn_cfm.userData)) { + f_handle_userData(vc_conn, conn_cfm.userData); + } + } + [] CLIENT.receive(PDU_BSSAP:?) -> value bssap sender vc_conn { + var integer conn_id := f_conn_id_by_comp(vc_conn); + /* send it to dispatcher */ + BSSAP.send(ts_BSSAP_DATA_req(conn_id, bssap)); + } + + /* Disconnect request client -> SCCP */ + [] CLIENT.receive(RAN_Conn_Prim:MSC_CONN_PRIM_DISC_REQ) -> sender vc_conn { + var integer conn_id := f_conn_id_by_comp(vc_conn); + BSSAP.send(ts_BSSAP_DISC_req(conn_id, 0)); + f_conn_table_del(conn_id); + } + + /* BSSAP from client -> SCCP */ + [] CLIENT.receive(BSSAP_Conn_Req:?) -> value creq sender vc_conn { + var integer conn_id; + /* send to dispatcher */ + + if (f_comp_known(vc_conn) == false) { + /* unknown client, create new connection */ + conn_id := f_gen_conn_id(); + + /* store mapping between client components and SCCP connectionId */ + f_conn_table_add(vc_conn, conn_id); + + BSSAP.send(ts_BSSAP_CONNECT_req(creq.addr_peer, creq.addr_own, conn_id, + creq.bssap)); + } else { + /* known client, send via existing connection */ + conn_id := f_conn_id_by_comp(vc_conn); + BSSAP.send(ts_BSSAP_DATA_req(conn_id, creq.bssap)); + } + + /* InitialL3 contains RR (PAG RESP) or MM (CM SRV REQ), we must increment + * counter only on MM/CC/SS, but not on RR */ + if (g_ran_ops.role_ms and not f_bssap_l3_is_rr(creq.bssap)) { + /* we have just sent the first MM message, increment the counter */ + var integer idx := f_idx_by_comp(vc_conn); + ConnectionTable[idx].n_sd[0] := 1; + log("patch: N(SD) for ConnIdx ", idx, " set to 1"); + } + + } +#else + [false] CLIENT.receive(false) {} +#endif +} + +private altstep as_main_mgcp() runs on RAN_Emulation_CT { +#ifdef RAN_EMULATION_MGCP + var MgcpCommand mgcp_req; + var MgcpResponse mgcp_resp; + var RAN_ConnHdlr vc_conn; + + /* Handling of MGCP in IPA SCCPLite case. This predates 3GPP AoIP + * and uses a MGCP session in parallel to BSSAP. BSSAP uses CIC + * as usual, and MGCP uses "CIC@mgw" endpoint naming, where CIC + * is printed as hex string, e.g. a@mgw for CIC 10 */ + + /* CLIENT -> MGCP */ + [] CLIENT.receive(MgcpCommand:?) -> value mgcp_req sender vc_conn { + /* MGCP request from Handler (we're MSC) */ + /* store the transaction ID we've seen */ + f_comp_store_mgcp_tid(vc_conn, mgcp_req.line.trans_id); + /* simply forward any MGCP from the client to the port */ + MGCP.send(mgcp_req); + } + [] CLIENT.receive(MgcpResponse:?) -> value mgcp_resp sender vc_conn { + /* MGCP response from Handler (we're BSC/MGW) */ + /* simply forward any MGCP from the client to the port */ + MGCP.send(mgcp_resp); + } + + /* MGCP -> CLIENT */ + [] MGCP.receive(MgcpCommand:?) -> value mgcp_req { + /* MGCP request from network side (we're BSC/MGW) */ + /* Extract CIC from local part of endpoint name */ + var integer cic := f_mgcp_ep_extract_cic(mgcp_req.line.ep); + if (match(mgcp_req, tr_RSIP) and f_cic_known(cic) == false) { + /* ignore RSIP for unknown CIC */ + } else { + /* Resolve the vc_conn by the CIC */ + vc_conn := f_comp_by_cic(cic); + CLIENT.send(mgcp_req) to vc_conn; + } + } + [] MGCP.receive(MgcpResponse:?) -> value mgcp_resp { + /* MGCP response from network side (we're MSC) */ + /* Resolve the vc_conn by the transaction ID */ + vc_conn := f_comp_by_mgcp_tid(mgcp_resp.line.trans_id); + CLIENT.send(mgcp_resp) to vc_conn; + } +#else + [false] CLIENT.receive {} +#endif +} + +/* send a raw (encoded) L3 message over given SCCP connection */ +private function f_xmit_raw_l3(integer sccp_conn_id, OCT1 dlci, octetstring l3_enc) runs on RAN_Emulation_CT +{ + select (g_ran_ops.protocol) { +#ifdef RAN_EMULATION_BSSAP + case (RAN_PROTOCOL_BSSAP) { + var PDU_BSSAP bssap; + bssap := valueof(ts_BSSAP_DTAP(l3_enc, dlci)); + BSSAP.send(ts_BSSAP_DATA_req(sccp_conn_id, bssap)); + } +#endif + } +} + +function main(RanOps ops, charstring id) runs on RAN_Emulation_CT { + + g_ran_id := id; + g_ran_ops := ops; + f_conn_table_init(); + f_expect_table_init(); + + if (isvalue(ops.sccp_addr_peer) and isvalue(ops.sccp_addr_local)) { + f_sleep(1.0); /* HACK to wait for M3UA/ASP to be ACTIVE */ + f_bssap_reset(ops.sccp_addr_peer, ops.sccp_addr_local); + } + + while (true) { + var RAN_ConnHdlr vc_conn; + var PDU_DTAP_MO dtap_mo; + var PDU_DTAP_MT dtap_mt; + var RAN_ConnHdlr vc_hdlr; + var octetstring l3_info; + var hexstring imsi; + var OCT4 tmsi; + + alt { + [g_ran_ops.protocol == RAN_PROTOCOL_BSSAP] as_main_bssap(); + + [g_ran_ops.role_ms] CLIENT.receive(PDU_DTAP_MO:?) -> value dtap_mo sender vc_conn { + var integer idx := f_idx_by_comp(vc_conn); + /* convert from decoded DTAP to encoded DTAP */ + var octetstring l3_enc := enc_PDU_ML3_MS_NW(dtap_mo.dtap); + /* patch correct L3 send sequence number N(SD) into l3_enc */ + if (dtap_mo.skip_seq_patching == false) { + f_ML3_patch_seq(ConnectionTable[idx], dtap_mo.dtap, l3_enc); + } + f_xmit_raw_l3(ConnectionTable[idx].sccp_conn_id, dtap_mo.dlci, l3_enc); + } + + [not g_ran_ops.role_ms] CLIENT.receive(PDU_DTAP_MT:?) -> value dtap_mt sender vc_conn { + var integer idx := f_idx_by_comp(vc_conn); + /* convert from decoded DTAP to encoded DTAP */ + var octetstring l3_enc := enc_PDU_ML3_NW_MS(dtap_mt.dtap); + f_xmit_raw_l3(ConnectionTable[idx].sccp_conn_id, dtap_mt.dlci, l3_enc); + } + + [] as_main_mgcp(); + + [] PROC.getcall(RAN_register:{?,?}) -> param(l3_info, vc_hdlr) { + f_create_expect(l3_info, vc_hdlr); + PROC.reply(RAN_register:{l3_info, vc_hdlr}) to vc_hdlr; + } + + [] PROC.getcall(RAN_register_imsi:{?,?,?}) -> param(imsi, tmsi, vc_hdlr) { + f_create_imsi(imsi, tmsi, vc_hdlr); + PROC.reply(RAN_register_imsi:{imsi, tmsi, vc_hdlr}) to vc_hdlr; + } + + + } + } +} + +#ifdef RAN_EMULATION_MGCP +private function f_mgcp_ep_extract_cic(charstring inp) return integer { + var charstring local_part := regexp(inp, "(*)@*", 0); + return hex2int(str2hex(local_part)); + +} +#endif + +/*********************************************************************** + * "Expect" Handling (mapping for expected incoming SCCP connections) + ***********************************************************************/ + +/* data about an expected future incoming connection */ +type record ExpectData { + /* L3 payload based on which we can match it */ + octetstring l3_payload optional, + /* component reference for this connection */ + RAN_ConnHdlr vc_conn +} + +/* procedure based port to register for incoming connections */ +signature RAN_register(in octetstring l3, in RAN_ConnHdlr hdlr); + +/* procedure based port to register for incoming IMSI/TMSI */ +signature RAN_register_imsi(in hexstring imsi, in OCT4 tmsi, in RAN_ConnHdlr hdlr); + +type port RAN_PROC_PT procedure { + inout RAN_register, RAN_register_imsi; +} with { extension "internal" }; + +/* CreateCallback that can be used as create_cb and will use the expectation table */ +function ExpectedCreateCallback(BSSAP_N_CONNECT_ind conn_ind, charstring id) +runs on RAN_Emulation_CT return RAN_ConnHdlr { + var RAN_ConnHdlr ret := null; + var octetstring l3_info; + var integer i; + + if (not ischosen(conn_ind.userData.pdu.bssmap.completeLayer3Information)) { + setverdict(fail, "N-CONNECT.ind with L3 != COMPLETE L3"); + mtc.stop; + return ret; + } + l3_info := conn_ind.userData.pdu.bssmap.completeLayer3Information.layer3Information.layer3info; + + for (i := 0; i < sizeof(ExpectTable); i:= i+1) { + if (not ispresent(ExpectTable[i].l3_payload)) { + continue; + } + if (l3_info == ExpectTable[i].l3_payload) { + ret := ExpectTable[i].vc_conn; + /* release this entry to be used again */ + ExpectTable[i].l3_payload := omit; + ExpectTable[i].vc_conn := null; + log("Found Expect[", i, "] for ", l3_info, " handled at ", ret); + /* return the component reference */ + return ret; + } + } + setverdict(fail, "Couldn't find Expect for incoming connection ", conn_ind); + mtc.stop; + return ret; +} + +private function f_create_expect(octetstring l3, RAN_ConnHdlr hdlr) +runs on RAN_Emulation_CT { + var integer i; + for (i := 0; i < sizeof(ExpectTable); i := i+1) { + if (not ispresent(ExpectTable[i].l3_payload)) { + ExpectTable[i].l3_payload := l3; + ExpectTable[i].vc_conn := hdlr; + log("Created Expect[", i, "] for ", l3, " to be handled at ", hdlr); + return; + } + } + testcase.stop("No space left in ExpectTable"); +} + +private function f_create_imsi(hexstring imsi, OCT4 tmsi, RAN_ConnHdlr hdlr) +runs on RAN_Emulation_CT { + for (var integer i := 0; i < sizeof(ImsiTable); i := i+1) { + if (not ispresent(ImsiTable[i].imsi)) { + ImsiTable[i].imsi := imsi; + ImsiTable[i].tmsi := tmsi; + ImsiTable[i].comp_ref := hdlr; + log("Created IMSI[", i, "] for ", imsi, tmsi, " to be handled at ", hdlr); + return; + } + } + testcase.stop("No space left in ImsiTable"); +} + + +private function f_expect_table_init() +runs on RAN_Emulation_CT { + for (var integer i := 0; i < sizeof(ExpectTable); i := i+1) { + ExpectTable[i].l3_payload := omit; + } +} + +/* helper function for clients to register their IMSI/TMSI */ +function f_ran_register_imsi(hexstring imsi, OCT4 tmsi) +runs on RAN_ConnHdlr { + BSSAP_PROC.call(RAN_register_imsi:{imsi, tmsi, self}) { + [] BSSAP_PROC.getreply(RAN_register_imsi:{?,?,?}) {}; + } +} + + +} diff --git a/msc/BSC_ConnectionHandler.ttcn b/msc/BSC_ConnectionHandler.ttcn index e408f829..11baf2ab 100644 --- a/msc/BSC_ConnectionHandler.ttcn +++ b/msc/BSC_ConnectionHandler.ttcn @@ -180,6 +180,7 @@ const RanOps BSC_RanOps := { unitdata_cb := refers(BscUnitdataCallback), decode_dtap := true, role_ms := true, + protocol := RAN_PROTOCOL_BSSAP, sccp_addr_local := omit, sccp_addr_peer := omit } diff --git a/msc/gen_links.sh b/msc/gen_links.sh index a29118a8..e4e142ba 100755 --- a/msc/gen_links.sh +++ b/msc/gen_links.sh @@ -89,7 +89,7 @@ DIR=../library FILES="Misc_Helpers.ttcn General_Types.ttcn GSM_Types.ttcn Osmocom_Types.ttcn MNCC_Types.ttcn MNCC_EncDec.cc MNCC_CodecPort.ttcn mncc.h MNCC_Emulation.ttcn Osmocom_VTY_Functions.ttcn Native_Functions.ttcn Native_FunctionDefs.cc " FILES+="IPA_Types.ttcn IPA_Emulation.ttcnpp IPA_CodecPort.ttcn IPA_CodecPort_CtrlFunct.ttcn IPA_CodecPort_CtrlFunctDef.cc GSUP_Types.ttcn GSUP_Emulation.ttcn " FILES+="Osmocom_CTRL_Types.ttcn Osmocom_CTRL_Functions.ttcn Osmocom_CTRL_Adapter.ttcn L3_Templates.ttcn L3_Common.ttcn " -FILES+="RAN_Emulation.ttcn BSSAP_CodecPort.ttcn BSSMAP_Templates.ttcn RAN_Adapter.ttcn MGCP_Types.ttcn MGCP_Templates.ttcn MGCP_CodecPort_CtrlFunct.ttcn MGCP_Emulation.ttcn " +FILES+="RAN_Emulation.ttcnpp BSSAP_CodecPort.ttcn BSSMAP_Templates.ttcn RAN_Adapter.ttcnpp MGCP_Types.ttcn MGCP_Templates.ttcn MGCP_CodecPort_CtrlFunct.ttcn MGCP_Emulation.ttcn " FILES+="RTP_CodecPort.ttcn RTP_CodecPort_CtrlFunctDef.cc " FILES+="MGCP_CodecPort.ttcn MGCP_CodecPort_CtrlFunctDef.cc " FILES+="SMPP_CodecPort.ttcn SMPP_CodecPort_CtrlFunct.ttcn SMPP_CodecPort_CtrlFunctDef.cc SMPP_Emulation.ttcn SMPP_Templates.ttcn " diff --git a/msc/regen_makefile.sh b/msc/regen_makefile.sh index 5645fdde..091faf82 100755 --- a/msc/regen_makefile.sh +++ b/msc/regen_makefile.sh @@ -2,6 +2,6 @@ FILES="*.ttcn *.ttcnpp 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 TELNETasp_PT.cc Native_FunctionDefs.cc SMPP_EncDec.cc SMPP_CodecPort_CtrlFunctDef.cc MAP_EncDec.cc SS_EncDec.cc TCCEncoding.cc SGsAP_CodecPort_CtrlFunctDef.cc *.c *.asn" -export CPPFLAGS_TTCN3="-DIPA_EMULATION_MGCP -DIPA_EMULATION_GSUP -DIPA_EMULATION_SCCP -DUSE_MTP3_DISTRIBUTOR" +export CPPFLAGS_TTCN3="-DIPA_EMULATION_MGCP -DIPA_EMULATION_GSUP -DIPA_EMULATION_SCCP -DRAN_EMULATION_BSSAP -DRAN_EMULATION_MGCP -DUSE_MTP3_DISTRIBUTOR" ../regen-makefile.sh MSC_Tests.ttcn $FILES -- cgit v1.2.3