daniel has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/27110 )
Change subject: WIP: more work towards HNBGW_Tests ......................................................................
WIP: more work towards HNBGW_Tests
Change-Id: I99ddd30fd2a27f2025928006db79388625e26b01 --- M hnbgw/HNBGW_Tests.ttcn D hnbgw/MSC_ConnHdlr.ttcn M hnbgw/gen_links.sh M hnbgw/osmo-hnbgw.cfg M hnbgw/osmo-stp.cfg M hnbgw/regen_makefile.sh A library/RUA_Emulation.ttcn 7 files changed, 790 insertions(+), 181 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-ttcn3-hacks refs/changes/10/27110/1
diff --git a/hnbgw/HNBGW_Tests.ttcn b/hnbgw/HNBGW_Tests.ttcn index 750c927..59cda1a 100644 --- a/hnbgw/HNBGW_Tests.ttcn +++ b/hnbgw/HNBGW_Tests.ttcn @@ -15,6 +15,7 @@
import from Misc_Helpers all; import from General_Types all; +import from GSM_Types all; import from Osmocom_Types all; import from IPL4asp_Types all;
@@ -31,23 +32,24 @@ import from TELNETasp_PortType all;
import from HNBAP_Templates all; +import from HNBAP_PDU_Descriptions all; + import from RUA_IEs all; import from RUA_Templates all; -import from HNBAP_PDU_Descriptions all; +import from RUA_Emulation all;
import from Iuh_Emulation all;
import from RANAP_Types all; +import from RANAP_PDU_Descriptions all; +import from RANAP_PDU_Contents all; +import from RANAP_IEs all; +import from RANAP_Templates all; + import from RAN_Adapter all;
-import from BSSAP_Types all; import from RAN_Adapter all; import from RAN_Emulation all; -import from BSSAP_CodecPort all; -import from IPA_Emulation all; -import from IPA_CodecPort all; -import from IPA_Types all; -
import from MGCP_Emulation all; import from MGCP_Types all; @@ -55,8 +57,6 @@ import from MGCP_CodecPort all; import from SDP_Types all;
-import from MSC_ConnHdlr all; - modulepar { /* IP address at which the HNodeB can be reached */ charstring mp_hnodeb_ip := "127.0.0.1"; @@ -83,16 +83,68 @@ RAN_Configuration mp_sgsn_cfg := { transport := RANAP_TRANSPORT_IuCS, sccp_service_type := "mtp3_itu", - sctp_addr := { 23905, "127.0.0.1", 2905, "127.0.0.1" }, - own_pc := 185, /* 0.23.1 first MSC emulation */ + sctp_addr := { 23906, "127.0.0.1", 2905, "127.0.0.1" }, + own_pc := 185, /* 0.23.1 first SGSN emulation */ own_ssn := 142, peer_pc := 189, /* 0.23.5 osmo-hnbgw */ peer_ssn := 142, sio := '83'O, - rctx := 1 + rctx := 2 }; }
+function MSC_UnitdataCallback(RANAP_PDU ranap) runs on RAN_Emulation_CT return template RANAP_PDU { + // FIXME + return ts_RANAP_Reset(ts_RanapCause_om_intervention, cs_domain); +} + +const RanOps MSC_RanOps := { + ranap_create_cb := refers(RAN_Emulation.RanapExpectedCreateCallback), + ranap_unitdata_cb := refers(MSC_UnitdataCallback), + ps_domain := false, + decode_dtap := false, + role_ms := false, + protocol := RAN_PROTOCOL_RANAP, + transport := RANAP_TRANSPORT_IuCS, + use_osmux := false, + sccp_addr_local := omit, + sccp_addr_peer := omit +} + +type record TestHdlrParams { + integer hnb_idx, + hexstring imsi, + boolean ps_domain, + HnbConfig hnb optional +}; + +/* We extend: + * RUA_ConnHdlr (for the Iuh side, emulating the HNB) + * RAN_ConnHdlr (for the Iu side, emulating the MSC) + * MGCP_ConnHdlr (for the MGCP side, emulating the MGW) + */ +type component ConnHdlr extends RAN_ConnHdlr, MGCP_ConnHdlr, RUA_ConnHdlr { + var integer g_sccp_conn_id; + + var TestHdlrParams g_pars; + timer g_Tguard; +} + + +const MGCPOps MSC_MGCPOps := { + create_cb := refers(MGCP_Emulation.ExpectedCreateCallback), + unitdata_cb := refers(MGCP_Emulation.DummyUnitdataCallback) +} + +function f_create_ranap_exp(octetstring l3_enc) runs on ConnHdlr { + BSSAP_PROC.call(RAN_register:{l3_enc, self}) { + [] BSSAP_PROC.getreply(RAN_register:{?, ?}) {}; + } +} + + +const integer NUM_HNB := 1; + const hexstring ranap_cm_service_req := '001340400000060003400100000f40060000f11028b6003a40080000f110ffffffff0010400e0d052411035758a605f44e9d4aef004f400300001c0056400500f1100017'H; const hexstring ranap_auth_req := '00144032000002001040262505120217dc146aeac56cb5ff6d5fb51f47f19220108ca5a6d0c8110000b9e9272498872764003b400100'H; const hexstring ranap_auth_resp := '001440140000010010400d0c0554ccbdd0302104002f3ae4'H; @@ -100,20 +152,27 @@ const hexstring ranap_rab_ass_req := '0000005900000100364052000001003500487824cd80102fa7201a2c0000f44c080a028000514000272028140067400000222814003c40000000503d02000227c03500010a0901a200000000000000000000000000401f4a0000400100'H; const hexstring iu_release_compl := '20010003000000'H;
+type record HnbConfig { + LocationAreaIdentification lai, + integer sac +} + type component test_CT extends CTRL_Adapter_CT { var boolean g_initialized := false;
- var MGCP_Emulation_CT vc_MGCP; - var Iuh_Emulation_CT vc_Iuh; - + /********************* Iu side */ var RAN_Adapter g_msc; var RAN_Adapter g_sgsn; /* SGSN IuPS missing */
- port TELNETasp_PT HNBGWVTY; - port HNBAP_PT HNBAP; - port RUA_PT RUA; + /********************* Iuh side */ + var HnbConfig g_hnb_cfg[NUM_HNB]; + var Iuh_Emulation_CT vc_Iuh[NUM_HNB]; + var RUA_Emulation_CT vc_RUA[NUM_HNB]; + port HNBAP_PT HNBAP[NUM_HNB];
+ var MGCP_Emulation_CT vc_MGCP; + port TELNETasp_PT HNBGWVTY; /* global test case guard timer (actual timeout value is set in f_init()) */ timer T_guard := 30.0; } @@ -163,25 +222,49 @@ vc_MGCP.start(MGCP_Emulation.main(ops, pars, id)); }
-/* global initialization function */ -function f_init(charstring id := "HBGW", float guard_timeout := 30.0) runs on test_CT { +function f_init_hnodeb(charstring id, integer hnb_idx, RuaOps rua_ops) runs on test_CT { + id := id & "-Iuh" & int2str(hnb_idx);
- T_guard.start(guard_timeout); - activate(as_Tguard()); - + /* Iuh lower layer (RUA/HNBAP demux) */ var Iuh_conn_parameters iuh_pars; iuh_pars.remote_ip := mp_hnbgw_ip; iuh_pars.remote_sctp_port := mp_hnbgw_iuh_port; iuh_pars.local_ip := mp_hnodeb_ip; - iuh_pars.local_sctp_port := mp_hnodeb_port; - vc_Iuh := Iuh_Emulation_CT.create(id & "-Iuh"); - connect(self:HNBAP, vc_Iuh:HNBAP); - connect(self:RUA, vc_Iuh:RUA); + iuh_pars.local_sctp_port := mp_hnodeb_port + hnb_idx; + vc_Iuh[hnb_idx] := Iuh_Emulation_CT.create(id); + connect(self:HNBAP[hnb_idx], vc_Iuh[hnb_idx]:HNBAP);
- vc_Iuh.start(Iuh_Emulation.main(iuh_pars, id & "-Iuh")); - f_init_mgcp(id); + vc_RUA[hnb_idx] := RUA_Emulation_CT.create(id & "-RUA"); + connect(vc_RUA[hnb_idx]:RUA, vc_Iuh[hnb_idx]:RUA);
- /* MSC */ + /* Start Iuh side components */ + vc_Iuh[hnb_idx].start(Iuh_Emulation.main(iuh_pars, id)); + vc_RUA[hnb_idx].start(RUA_Emulation.main(rua_ops, id & "-RUA")); +} + +/* global initialization function */ +function f_init(charstring id := "HBGW", float guard_timeout := 30.0) runs on test_CT { + + g_hnb_cfg[0] := { + lai := { + mcc_mnc := '00101'H, + lac := 2342 + }, + sac := 55 + } + T_guard.start(guard_timeout); + activate(as_Tguard()); + + /* RUA/RANAP emulation on top of lower-layer Iuh */ + var RuaOps rua_ops := { + create_cb := refers(IuhRanapCreateCallback), + unitdata_cb := refers(IuhRanapUnitdataCallback) + }; + for (var integer i := 0; i < NUM_HNB; i := i+1) { + f_init_hnodeb(testcasename(), i, rua_ops); + } + + /* MSC emulation */ var RanOps ranops := { ranap_create_cb := refers(RAN_Emulation.RanapExpectedCreateCallback), ranap_unitdata_cb := omit, @@ -197,9 +280,12 @@ f_ran_adapter_init(g_msc, mp_msc_cfg, "HNBGW_Test", ranops); f_ran_adapter_start(g_msc);
- /* SGSN*/ + /* SGSN emulation */ + ranops.ps_domain := true; + f_ran_adapter_init(g_sgsn, mp_sgsn_cfg, "HNBGW_Test", ranops); + f_ran_adapter_start(g_sgsn);
- + f_init_mgcp(id); f_init_vty("VirtHNBGW"); }
@@ -209,43 +295,242 @@ mtc.stop; }
-/*function f_start_handler(void_fn fn, template (omit) TestHdlrParams pars := omit) -runs on test_CT return MSC_ConnHdlr { - return f_start_handler_run(f_start_handler_create(pars), fn, pars); -}*/ +/* helper function to start all of the simulated hNodeBs */ +function f_start_hnbs() runs on test_CT { + for (var integer i:= 0; i < NUM_HNB; i := i+1) { + f_hnbap_register(i); + } +} + +/*********************************************************************** + * code running in test_CT, preparing start of per-UE ConnHdlr + ***********************************************************************/ + +/* inbound RUA connection establishment on Iuh side */ +function IuhRanapCreateCallback(ContextId context_id, RUA_IEs.CN_DomainIndicator domain, charstring id) +runs on RUA_Emulation_CT return RUA_ConnHdlr { + log("CreateCallback"); + return null; +} + +/* inbound RUA connectionless data on Iuh side */ +function IuhRanapUnitdataCallback(RANAP_PDU ranap) +runs on RUA_Emulation_CT return template RANAP_PDU { + log("UnitdataCallback"); + return omit; +} + +private function f_start_handler_create(TestHdlrParams pars) runs on test_CT return ConnHdlr { + var ConnHdlr vc_conn; + var charstring id := testcasename() & int2str(pars.hnb_idx); + + vc_conn := ConnHdlr.create(id); + + /* Iuh RUA part */ + connect(vc_conn:RUA, vc_RUA[pars.hnb_idx]:CLIENT); + + if (pars.ps_domain) { + /* SGSN side */ + connect(vc_conn:BSSAP, g_sgsn.vc_RAN:CLIENT); + connect(vc_conn:BSSAP_PROC, g_sgsn.vc_RAN:PROC); + } else { + /* MSC side */ + connect(vc_conn:BSSAP, g_msc.vc_RAN:CLIENT); + connect(vc_conn:BSSAP_PROC, g_msc.vc_RAN:PROC); + } + + return vc_conn; +} + +private function f_start_handler_run(ConnHdlr vc_conn, void_fn fn, TestHdlrParams pars) runs on test_CT { + var charstring id := testcasename(); // & int2str(pars.ran_idx); + /* We cannot use vc_conn.start(f_init_handler(fn, id, pars)); as we cannot have + * a stand-alone 'derefers()' call, see https://www.eclipse.org/forums/index.php/t/1091364/ */ + pars.hnb := g_hnb_cfg[pars.hnb_idx]; + vc_conn.start(derefers(fn)(id, pars)); +} + +function f_start_handler_with_pars(void_fn fn, template (value) TestHdlrParams pars) +runs on test_CT return ConnHdlr { + var ConnHdlr vc_conn; + vc_conn := f_start_handler_create(valueof(pars)); + f_start_handler_run(vc_conn, fn, valueof(pars)); + return vc_conn; +} + +/*********************************************************************** + * code running inside per-UE ConnHdlr + ***********************************************************************/ + +type function void_fn(charstring id, TestHdlrParams pars) runs on ConnHdlr; + +function f_init_handler(TestHdlrParams pars, float t_guard := 20.0) runs on ConnHdlr { + /* make parameters available via component variable */ + g_pars := pars; + /* start guard timer and activate it as default */ + g_Tguard.start(t_guard); + activate(as_Tguard_ConnHdlr()); + + /* TODO: CTRL? */ + /* TODO: VTY? */ +} + +/* global altstep for global guard timer; */ +private altstep as_Tguard_ConnHdlr() runs on ConnHdlr { + [] g_Tguard.timeout { + setverdict(fail, "Timeout of T_guard"); + mtc.stop; + } +} + +/* send RANAP on Iuh and expect it to show up on Iu */ +function f_iuh2iu(template (present) RANAP_PDU tx, template RANAP_PDU exp_rx := omit) +runs on ConnHdlr return RANAP_PDU { + var RANAP_PDU rx; + timer T := 5.0; + + if (istemplatekind(exp_rx, "omit")) { + exp_rx := tx; + } + + RUA.send(tx); + T.start; + + alt { + [] BSSAP.receive(exp_rx) -> value rx { + setverdict(pass); + } + [] T.timeout { + setverdict(fail, "Timeout waiting for Iu ", exp_rx); + } + } + return rx; +} + +/* send RANAP on Iu and expect it to show up on Iuh */ +function f_iu2iuh(template (present) RANAP_PDU tx, template RANAP_PDU exp_rx := omit) +runs on ConnHdlr return RANAP_PDU { + var RANAP_PDU rx; + timer T := 5.0; + + if (istemplatekind(exp_rx, "omit")) { + exp_rx := tx; + } + + BSSAP.send(tx); + T.start; + + alt { + [] RUA.receive(exp_rx) -> value rx { + setverdict(pass); + } + [] T.timeout { + setverdict(fail, "Timeout waiting for Iuh ", exp_rx); + } + } + return rx; +} + +/* send RANAP on Iuh and expect it to show up on Iu */ +function f_iuh2iu_connect(template (present) RANAP_PDU tx, template RANAP_PDU exp_rx := omit) +runs on ConnHdlr return RANAP_PDU { + var RANAP_PDU rx; + timer T := 5.0; + + if (istemplatekind(exp_rx, "omit")) { + exp_rx := tx; + } + + /* create an expect on the Iu side for the random NAS portion */ + var template (omit) octetstring nas := f_ranap_extract_l3(valueof(tx)); + f_ran_register_exp(valueof(nas)); + + /* send it via Iuh (creating a RUA connection) */ + RUA.send(RUA_Conn_Req:{g_pars.ps_domain, tx}); + + /* expect to receive it on the Iu side */ + T.start; + alt { + [] BSSAP.receive(exp_rx) -> value rx { + setverdict(pass); + } + [] T.timeout { + setverdict(fail, "Timeout waiting for Iu ", exp_rx); + } + } + return rx; +} + +/* build a RANAP InitialUE based on the TestHdlrParams */ +friend function f_build_initial_ue(TestHdlrParams pars) return RANAP_PDU { + var LAI lai := { + pLMNidentity := hex2oct(pars.hnb.lai.mcc_mnc), + lAC := int2oct(pars.hnb.lai.lac, 2), + iE_Extensions := omit + }; + var SAI sai := { + pLMNidentity := lai.pLMNidentity, + lAC := lai.lAC, + sAC := int2oct(pars.hnb.sac, 2), + iE_Extensions := omit + } + var octetstring nas := f_rnd_octstring(10); + var IuSignallingConnectionIdentifier sigc_id := int2bit(f_rnd_int(1000), 24); + var GlobalRNC_ID grnc_id := { + pLMNidentity := lai.pLMNidentity, + rNC_ID := 2342 + } + + if (pars.ps_domain) { + var RAC rac := '00'O; + return valueof(ts_RANAP_initialUE_PS(lai, rac, sai, nas, sigc_id, grnc_id)); + } else { + return valueof(ts_RANAP_initialUE_CS(lai, sai, nas, sigc_id, grnc_id)); + } +}
-/*private function f_tc_hnb_register_request(charstring id) runs on HNBGW_ConnHdlr { - f_handle_hnbap_hnb_register_req(); - f_sleep(1.0); -}*/ +/*********************************************************************** + * HNBAP Testing + ***********************************************************************/
-testcase TC_hnb_register() runs on test_CT { - //var HNBGW_ConnHdlr vc_conn; - f_init(); - HNBAP.send(tr_HNBAP_HNBRegisterRequest(char2oct("TTCN3 HNodeB"), + +function f_hnbap_register(integer hnb_idx := 0) runs on test_CT +{ + HNBAP[hnb_idx].send(tr_HNBAP_HNBRegisterRequest(char2oct("TTCN3 HNodeB"), '00F110'O, - int2bit(1, 28), + int2bit(1 + hnb_idx, 28), int2oct(2, 2), int2oct(3, 1), int2oct(4, 2)));
alt { - [] HNBAP.receive(tr_HNBAP_HNBRegisterAccept(?)) { + [] HNBAP[hnb_idx].receive(tr_HNBAP_HNBRegisterAccept(?)) { setverdict(pass); } - [] HNBAP.receive(IUHEM_Event:?) { + [] HNBAP[hnb_idx].receive(IUHEM_Event:?) { repeat; } } +} + +testcase TC_hnb_register() runs on test_CT { + //var HNBGW_ConnHdlr vc_conn; + f_init(); + f_hnbap_register(0);
f_sleep(10.0); f_shutdown_helper(); }
+/*********************************************************************** + * RUA / RANAP Testing + ***********************************************************************/ + testcase TC_RAB_Assignment() runs on test_CT { //var HNBGW_ConnHdlr vc_conn; f_init(); +/* f_sleep(3.0); HNBAP.send(tr_HNBAP_HNBRegisterRequest(char2oct("TTCN3 HNodeB"), '00F110'O, @@ -263,16 +548,90 @@ } }
- RUA.send(tr_RUA_Connect(cs_domain, int2bit(23, 24), normal_call, hex2oct(ranap_cm_service_req))); + //RUA.send(tr_RUA_Connect(cs_domain, int2bit(23, 24), normal_call, hex2oct(ranap_cm_service_req))); //RANAP.receive(); - +*/ f_sleep(2.0); f_shutdown_helper(); }
+private template (value) TestHdlrParams +t_pars(integer imsi_suffix, boolean ps_domain := false,integer hnb_idx := 0) := { + hnb_idx := hnb_idx, + imsi := f_gen_imsi(imsi_suffix), + ps_domain := ps_domain, + hnb := omit /* filled in later */ +} + +/* Create an Iuh connection; send InitialUE; expect it to appear on new SCCP conenction */ +friend function f_tc_initial_ue(charstring id, TestHdlrParams pars) runs on ConnHdlr { + f_init_handler(pars); + var RANAP_PDU tx := f_build_initial_ue(g_pars); + f_iuh2iu_connect(tx); +} +testcase TC_ranap_cs_initial_ue() runs on test_CT { + var ConnHdlr vc_conn; + + f_init(); + f_start_hnbs(); + + vc_conn := f_start_handler_with_pars(refers(f_tc_initial_ue), t_pars(1)); + vc_conn.done; +} +testcase TC_ranap_ps_initial_ue() runs on test_CT { + var ConnHdlr vc_conn; + + f_init(); + f_start_hnbs(); + + vc_conn := f_start_handler_with_pars(refers(f_tc_initial_ue), t_pars(2, true)); + vc_conn.done; +} + + +/* Create an Iuh connection; send InitialUE; transceive data both directions */ +friend function f_tc_ranap_bidir(charstring id, TestHdlrParams pars) runs on ConnHdlr { + f_init_handler(pars); + + /* HNB -> MSC: InitialUE */ + f_iuh2iu_connect(f_build_initial_ue(g_pars)); + + /* MSC <- HNB: DirectTransfer */ + f_iu2iuh(ts_RANAP_DirectTransfer(f_rnd_octstring(10))); + /* MSC -> HNB: DirectTransfer */ + f_iuh2iu(ts_RANAP_DirectTransfer(f_rnd_octstring(10))); + + /* HNB <- MSC: CommonID */ + f_iu2iuh(ts_RANAP_CommonId(hex2oct(pars.imsi))); +} +testcase TC_ranap_cs_bidir() runs on test_CT { + var ConnHdlr vc_conn; + + f_init(); + f_start_hnbs(); + + vc_conn := f_start_handler_with_pars(refers(f_tc_ranap_bidir), t_pars(3)); + vc_conn.done; +} +testcase TC_ranap_ps_bidir() runs on test_CT { + var ConnHdlr vc_conn; + + f_init(); + f_start_hnbs(); + + vc_conn := f_start_handler_with_pars(refers(f_tc_ranap_bidir), t_pars(4, true)); + vc_conn.done; +} + + + + control { execute(TC_hnb_register()); - execute(TC_RAB_Assignment()); + execute(TC_ranap_cs_initial_ue()); + execute(TC_ranap_ps_initial_ue()); + execute(TC_ranap_cs_bidir()); + execute(TC_ranap_ps_bidir()); }
} diff --git a/hnbgw/MSC_ConnHdlr.ttcn b/hnbgw/MSC_ConnHdlr.ttcn deleted file mode 100644 index 290ee82..0000000 --- a/hnbgw/MSC_ConnHdlr.ttcn +++ /dev/null @@ -1,125 +0,0 @@ -module MSC_ConnHdlr { - -/* MSC connection handler for hnbgs tests in TTCN-3 - * (C) sysmocom - s.f.m.c. GmbH - * All rights reserved. - * - * Released under the terms of GNU General Public License, Version 2 or - * (at your option) any later version. - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -import from Misc_Helpers all; -import from General_Types all; -import from Osmocom_Types all; -import from IPL4asp_Types all; - -import from Osmocom_CTRL_Functions all; -import from Osmocom_CTRL_Types all; -import from Osmocom_CTRL_Adapter all; - -import from StatsD_Types all; -import from StatsD_CodecPort all; -import from StatsD_CodecPort_CtrlFunct all; -import from StatsD_Checker all; - -import from Osmocom_VTY_Functions all; -import from TELNETasp_PortType all; - -import from HNBAP_Templates all; -import from RUA_IEs all; -import from RUA_Templates all; -import from HNBAP_PDU_Descriptions all; - -import from Iuh_Emulation all; - -import from RANAP_Types all; -import from RANAP_PDU_Contents all; -import from RANAP_PDU_Descriptions all; -import from RANAP_Templates all; -import from RAN_Adapter all; - -import from BSSAP_Types all; -import from RAN_Adapter all; -import from RAN_Emulation all; -import from BSSAP_CodecPort all; -import from IPA_Emulation all; -import from IPA_CodecPort all; -import from IPA_Types all; - -import from MGCP_Emulation all; -import from MGCP_Types all; -import from MGCP_Templates all; -import from MGCP_CodecPort all; -import from SDP_Types all; - -import from SCCPasp_Types all; -import from BSSAP_Types all; - -import from MobileL3_Types all; -import from MobileL3_CommonIE_Types all; -import from MobileL3_RRM_Types all; -//import from L3_Templates all; - -import from TELNETasp_PortType all; -import from Osmocom_VTY_Functions all; -import from TCCConversion_Functions all; - -type record TestHdlrParams { - hexstring imsi, - integer media_nr -}; - -function f_get_test_hdlr_pars() return TestHdlrParams { - var TestHdlrParams pars := { - imsi := f_rnd_imsi('00101'H), - media_nr := 1 - }; - - return pars; -} - -type component MSC_ConnHdlr extends RAN_ConnHdlr, MGCP_ConnHdlr { /* RUA_ConnHdlr? Iuh_Emulation */ - var integer g_sccp_conn_id; - - port RAN_PROC_PT RAN; - port IPA_MGCP_PT MGCP_CLIENT; - var TestHdlrParams g_pars; - -} - -function f_MscConnHdlr_init(integer i, HostName bts, HostName mgw) runs on MSC_ConnHdlr { -} - -function UnitdataCallback(RANAP_PDU ranap) runs on RAN_Emulation_CT return template RANAP_PDU { - // FIXME - return ts_RANAP_Reset(ts_RanapCause_om_intervention, cs_domain); -} - -const RanOps MSC_RanOps := { - ranap_create_cb := refers(RAN_Emulation.RanapExpectedCreateCallback), - ranap_unitdata_cb := refers(UnitdataCallback), - ps_domain := false, - decode_dtap := false, - role_ms := false, - protocol := RAN_PROTOCOL_RANAP, - transport := BSSAP_TRANSPORT_AoIP, - use_osmux := false, - sccp_addr_local := omit, - sccp_addr_peer := omit -} - -const MGCPOps MSC_MGCPOps := { - create_cb := refers(MGCP_Emulation.ExpectedCreateCallback), - unitdata_cb := refers(MGCP_Emulation.DummyUnitdataCallback) -} - -function f_create_ranap_exp(octetstring l3_enc) runs on MSC_ConnHdlr { - RAN.call(RAN_register:{l3_enc, self}) { - [] RAN.getreply(RAN_register:{?, ?}) {}; - } -} - - -} \ No newline at end of file diff --git a/hnbgw/gen_links.sh b/hnbgw/gen_links.sh index 197815c..5224980 100755 --- a/hnbgw/gen_links.sh +++ b/hnbgw/gen_links.sh @@ -95,8 +95,9 @@ FILES="Iuh_Types.ttcn Iuh_CodecPort.ttcn Iuh_CodecPort_CtrlFunctDef.cc Iuh_CodecPort_CtrlFunct.ttcn Iuh_Emulation.ttcn DNS_Helpers.ttcn " FILES+="MGCP_Emulation.ttcn MGCP_Types.ttcn MGCP_Templates.ttcn MGCP_CodecPort.ttcn MGCP_CodecPort_CtrlFunct.ttcn MGCP_CodecPort_CtrlFunctDef.cc " FILES+="RAN_Adapter.ttcnpp RAN_Emulation.ttcnpp BSSAP_CodecPort.ttcn SCCP_Templates.ttcn " -FILES+="Misc_Helpers.ttcn General_Types.ttcn Osmocom_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 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 " +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 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 " FILES+="StatsD_Types.ttcn StatsD_CodecPort.ttcn StatsD_CodecPort_CtrlFunct.ttcn StatsD_CodecPort_CtrlFunctdef.cc StatsD_Checker.ttcn " +FILES+="RUA_Emulation.ttcn "
gen_links $DIR $FILES
diff --git a/hnbgw/osmo-hnbgw.cfg b/hnbgw/osmo-hnbgw.cfg index dab3e39..4a87bd8 100644 --- a/hnbgw/osmo-hnbgw.cfg +++ b/hnbgw/osmo-hnbgw.cfg @@ -49,7 +49,7 @@ point-code 0.23.4 sccp-address sgsn routing-indicator PC - point-code 0.1.1 + point-code 0.23.1 hnbgw log-prefix hnb-id iuh diff --git a/hnbgw/osmo-stp.cfg b/hnbgw/osmo-stp.cfg index 7aa8735..85d60e9 100644 --- a/hnbgw/osmo-stp.cfg +++ b/hnbgw/osmo-stp.cfg @@ -37,8 +37,16 @@ as virt-msc0 m3ua asp virt-msc0-0 routing-key 1 0.23.4 + asp virt-sgsn0-0 23906 2905 m3ua + local-ip 127.0.0.1 + remote-ip 127.0.0.1 + as virt-sgsn0 m3ua + asp virt-sgsn0-0 + routing-key 2 0.23.1 +
route-table system update route 0.23.4 7.255.7 linkset virt-msc0 + update route 0.23.1 7.255.7 linkset virt-sgsn0 listen m3ua 2905 accept-asp-connections dynamic-permitted diff --git a/hnbgw/regen_makefile.sh b/hnbgw/regen_makefile.sh index 1584335..47f7ee8 100755 --- a/hnbgw/regen_makefile.sh +++ b/hnbgw/regen_makefile.sh @@ -31,11 +31,7 @@
export CPPFLAGS_TTCN3=" -DIPA_EMULATION_CTRL - -DIPA_EMULATION_MGCP - -DIPA_EMULATION_SCCP -DRAN_EMULATION_RANAP - -DRAN_EMULATION_CTRL - -DRAN_EMULATION_MGCP -DUSE_MTP3_DISTRIBUTOR "
diff --git a/library/RUA_Emulation.ttcn b/library/RUA_Emulation.ttcn new file mode 100644 index 0000000..23b4a7a --- /dev/null +++ b/library/RUA_Emulation.ttcn @@ -0,0 +1,370 @@ +module RUA_Emulation { + +/* RUA_Emulation runs on top of Iuh_Emulation. It multiplexes/demultiplexes + * the individuao connections, so there can be separate TTCN-3 components + * handling each of the connections (one connection per UE). + * + * The RUA_Emulation.main() function processes RUA messages from the Iuh stack + * via the RUA_PT, and dispatches them to the per-connection components. + * + * Outbound RUA connections are initiated by sending a FIXME primitive to the + * RUA_Emulation component. + * + * For each new inbound connection, the RuaOps.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 future inbound connection, + * it can do so by registering an "expect" with the expected RANAP payload. + + * FIXME + + * (C) 2022 by Harald Welte laforge@gnumonks.org + * All rights reserved. + * + * Released under the terms of GNU General Public License, Version 2 or + * (at your option) any later version. + */ + +import from General_Types all; +import from Osmocom_Types all; + +import from Iuh_Emulation all; + +import from RUA_Templates all; +//import from RUA_Constants all; +import from RUA_PDU_Descriptions all; +import from RUA_IEs all; + +import from RANAP_PDU_Descriptions all; +//import from RANAP_Constants all; +import from RANAP_IEs all; +import from RANAP_Types all; +import from RANAP_Templates all; + +modulepar { + integer mp_max_context_id := hex2int('FFFFFF'H); +} + + +/* General "base class" component definition, of which specific implementations + * derive themselves by means of the "extends" feature */ +type component RUA_ConnHdlr { + port RUA_Conn_PT RUA; +} + + +/* port between individual per-connection components and this dispatcher */ +type port RUA_Conn_PT message { + inout RANAP_PDU, + RUA_Conn_Req; +} with { extension "internal" }; + +type record RUA_Conn_Req { + boolean ps_domain, + RANAP_PDU ranap +}; + +type bitstring ContextId length(24); // with { variant "FIELDLENGTH(24)" }; + +/* represents a single RANAP connection over RUA */ +type record ConnectionData { + RUA_ConnHdlr comp_ref, + RUA_IEs.CN_DomainIndicator domain, + integer context_id +} + +type component RUA_Emulation_CT { + /* port to the bottom side (Iuh) */ + port RUA_PT RUA; + + /* ports to the upper side (per-connection components) */ + port RUA_Conn_PT CLIENT; + + /* 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 RUA_PROC_PT PROC; + + var charstring g_rua_id; + var RuaOps g_rua_ops; +} + +type function RanapCreateCallback(ContextId context_id, RUA_IEs.CN_DomainIndicator domain, charstring id) +runs on RUA_Emulation_CT return RUA_ConnHdlr; + +type function RanapUnitdataCallback(RANAP_PDU ranap) +runs on RUA_Emulation_CT return template RANAP_PDU; + +type record RuaOps { + RanapCreateCallback create_cb optional, + RanapUnitdataCallback unitdata_cb optional + //boolean deode_dtap + //boolean role_ms +}; + +private function f_context_id_known(ContextId context_id) +runs on RUA_Emulation_CT return boolean { + var integer i; + for (i := 0; i < sizeof(ConnectionTable); i := i+1) { + if (ConnectionTable[i].context_id == bit2int(context_id)){ + return true; + } + } + return false; +} + +private function f_comp_known(RUA_ConnHdlr client) +runs on RUA_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; +} + +/* resolve connection ID by component reference */ +private function f_context_id_by_comp(RUA_ConnHdlr client) +runs on RUA_Emulation_CT return ContextId { + for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) { + if (ConnectionTable[i].comp_ref == client) { + return int2bit(ConnectionTable[i].context_id, 24); + } + } + setverdict(fail, "RAN Connection table not found by component ", client); + mtc.stop; +} + +/* resolve ConnectionTable index component reference */ +private function f_idx_by_comp(RUA_ConnHdlr client) +runs on RUA_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_context_id() +runs on RUA_Emulation_CT return ContextId { + var ContextId context_id; + + do { + context_id := int2bit(float2int(rnd()*int2float(mp_max_context_id)), 24); + } while (f_context_id_known(context_id) == true); + + return context_id; +} + +private function f_conn_table_init() +runs on RUA_Emulation_CT { + for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) { + ConnectionTable[i].comp_ref := null; + ConnectionTable[i].context_id := -1; + } +/* + 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(RUA_ConnHdlr comp_ref, RUA_IEs.CN_DomainIndicator domain, ContextId context_id) +runs on RUA_Emulation_CT { + var integer int_context_id := bit2int(context_id); + for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) { + if (ConnectionTable[i].context_id == -1) { + ConnectionTable[i].comp_ref := comp_ref; + ConnectionTable[i].domain := domain; + ConnectionTable[i].context_id := int_context_id; + log("Added conn table entry ", i, comp_ref, int_context_id); + return; + } + } + testcase.stop("RUA Connection table full!"); +} + +private function f_conn_table_del(ContextId context_id) +runs on RUA_Emulation_CT { + var integer int_context_id := bit2int(context_id); + for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) { + if (ConnectionTable[i].context_id == int_context_id) { + log("Deleted conn table entry ", i, + ConnectionTable[i].comp_ref, int_context_id); + ConnectionTable[i].context_id := -1; + return + } + } + setverdict(fail, "RUA Connection table attempt to delete non-existant ", int_context_id); + mtc.stop; +} + + +/* resolve component reference by connection ID */ +private function f_comp_by_context_id(ContextId context_id) +runs on RUA_Emulation_CT return RUA_ConnHdlr { + var integer int_context_id := bit2int(context_id); + for (var integer i := 0; i < sizeof(ConnectionTable); i := i+1) { + if (ConnectionTable[i].context_id == int_context_id) { + return ConnectionTable[i].comp_ref; + } + } + setverdict(fail, "RUA Connection table not found by RUA Context ID ", int_context_id); + mtc.stop; +} + +private function CommonRanapUnitdataCallback(RANAP_PDU ranap) +runs on RUA_Emulation_CT return template RANAP_PDU { + /* TODO: paging */ + return g_rua_ops.unitdata_cb.apply(ranap); +} + +private function f_handle_userData_RANAP(RUA_ConnHdlr client, RANAP_PDU ranap) +runs on RUA_Emulation_CT { + /* TODO: L3 decoding, if requested */ + CLIENT.send(ranap) to client; +} + + +private altstep as_reset_ack() runs on RUA_Emulation_CT { + var RUA_PDU rua_clt; + [] RUA.receive(tr_RUA_ConnectionlessTransfer(decmatch tr_RANAP_Reset)) -> value rua_clt { + var RANAP_PDU rx := dec_RANAP_PDU(rua_clt.initiatingMessage.value_.connectionlessTransfer.protocolIEs[0].value_.rANAP_Message); + var RANAP_IEs.CN_DomainIndicator dom; + dom := rx.initiatingMessage.value_.Reset.protocolIEs[1].value_.cN_DomainIndicator; + RUA.send(ts_RUA_ConnectionlessTransfer(enc_RANAP_PDU(valueof(ts_RANAP_ResetAck(dom))))); + } +} + +private altstep as_main_rua() runs on RUA_Emulation_CT { + var RANAP_PDU ranap; + var RUA_PDU rua; + var octetstring ranap_enc; + var ContextId context_id; + var RUA_IEs.CN_DomainIndicator domain_ind; + var RUA_ConnHdlr vc_conn; + var RUA_Conn_Req creq; + + /* RUA -> Client: UNIT-DATA (connectionless RUA) from CN */ + [] RUA.receive(tr_RUA_ConnectionlessTransfer) -> value rua { + ranap := dec_RANAP_PDU(rua.initiatingMessage.value_.connectionlessTransfer.protocolIEs[0].value_.rANAP_Message); + var template RANAP_PDU resp; + resp := CommonRanapUnitdataCallback(ranap); + if (isvalue(resp)) { + RUA.send(ts_RUA_ConnectionlessTransfer(enc_RANAP_PDU(valueof(resp)))); + } + } + + /* RUA -> Client: new connection from CN */ + [] RUA.receive(tr_RUA_Connect) -> value rua { + domain_ind := rua.initiatingMessage.value_.connect_.protocolIEs[0].value_.cN_DomainIndicator; + context_id := rua.initiatingMessage.value_.connect_.protocolIEs[1].value_.context_ID; + ranap_enc := rua.initiatingMessage.value_.connect_.protocolIEs[3].value_.rANAP_Message; + ranap := dec_RANAP_PDU(ranap_enc); + vc_conn := g_rua_ops.create_cb.apply(context_id, domain_ind, g_rua_id); + /* store mapping between client components and RUA contextId */ + f_conn_table_add(vc_conn, domain_ind, context_id); + /* handle user payload */ + f_handle_userData_RANAP(vc_conn, ranap); + } + + /* RUA -> Client: connection-oriented data in existing connection */ + [] RUA.receive(tr_RUA_DirectTransfer) -> value rua { + context_id := rua.initiatingMessage.value_.directTransfer.protocolIEs[1].value_.context_ID; + vc_conn := f_comp_by_context_id(context_id); + ranap_enc := rua.initiatingMessage.value_.directTransfer.protocolIEs[2].value_.rANAP_Message; + f_handle_userData_RANAP(vc_conn, dec_RANAP_PDU(ranap_enc)); + } + + /* RUA -> Client: disconnect of an existing connection */ + [] RUA.receive(tr_RUA_Disconnect) -> value rua { + } + + /* RANAP from client through an existing RANAP connection */ + [] CLIENT.receive(RANAP_PDU:?) -> value ranap sender vc_conn { + var integer idx := f_idx_by_comp(vc_conn); + context_id := int2bit(ConnectionTable[idx].context_id, 24); + domain_ind := ConnectionTable[idx].domain; + RUA.send(ts_RUA_DirectTransfer(domain_ind, context_id, enc_RANAP_PDU(ranap))); + } + + /* RANAP from client, for a new RANAP connection */ + [] CLIENT.receive(RUA_Conn_Req:?) -> value creq sender vc_conn { + var octetstring enc_ranap := enc_RANAP_PDU(creq.ranap); + + if (f_comp_known(vc_conn) == false) { + /* unknown client, create new connection */ + context_id := f_gen_context_id(); + if (creq.ps_domain) { + domain_ind := ps_domain; + } else { + domain_ind := cs_domain; + } + + f_conn_table_add(vc_conn, domain_ind, context_id); + RUA.send(ts_RUA_Connect(domain_ind, context_id, normal_call, enc_ranap)); + } else { + /* known client, send via existing component */ + context_id := f_context_id_by_comp(vc_conn); + RUA.send(ts_RUA_DirectTransfer(domain_ind, context_id, enc_ranap)); + } + } + +} + + + +function f_ranap_reset(RANAP_IEs.CN_DomainIndicator dom) runs on RUA_Emulation_CT { + timer T := 5.0; + + var RANAP_PDU tx := valueof(ts_RANAP_Reset(ts_RanapCause_om_intervention,dom)); + RUA.send(ts_RUA_ConnectionlessTransfer(enc_RANAP_PDU(tx))); + T.start; + alt { + [] RUA.receive(tr_RUA_ConnectionlessTransfer(decmatch tr_RANAP_ResetAck)) { + log("RUA-RANAP: Received RESET-ACK in resposne to RESET, we're reay to go!"); + } + [] as_reset_ack(); + [] RUA.receive { repeat; } + [] T.timeout { + setverdict(fail, "RUA-RANAP: Timeout waiting for RESET-ACK after sending RESET"); + mtc.stop; + } + } +} + +function main(RuaOps ops, charstring id) runs on RUA_Emulation_CT { + g_rua_id := id; + g_rua_ops := ops; + f_conn_table_init(); + //f_expect_table_init(); + + while (true) { + alt { + [] as_main_rua(); + + /* + [] PROC.getcall(RUA_Register:{?,?}) -> param(l3_info, vc_hdlr) { + f_create_expect(l3_info, vc_hdlr); + PROC.reply(RUA_register:{l3_info, vc_hdlr}) to vc_hdlr; + } + */ + } + } +} + + +}