diff --git a/Makefile b/Makefile index 05670817f..f05ab482b 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,7 @@ SUBDIRS= \ gbproxy \ ggsn_tests \ hlr \ + hnodeb \ mgw \ mme \ msc \ diff --git a/hnodeb/HNBGW_ConnectionHandler.ttcn b/hnodeb/HNBGW_ConnectionHandler.ttcn new file mode 100644 index 000000000..071e3e8a1 --- /dev/null +++ b/hnodeb/HNBGW_ConnectionHandler.ttcn @@ -0,0 +1,112 @@ +module HNBGW_ConnectionHandler { + +/* HNBGW Connection Handler of HNB_Tests in TTCN-3 + * (C) 2021 by sysmocom - s.m.f.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 Native_Functions all; + +import from SDP_Types all; + +import from StatsD_Checker all; + +import from TELNETasp_PortType all; +import from Osmocom_VTY_Functions all; + +import from HNBAP_Templates all; + +import from Iuh_Emulation all; + +/* this component represents a single Iuh connection at the HNBGW. */ +type component HNBGW_ConnHdlr extends StatsD_ConnHdlr { + port TELNETasp_PT HNBVTY; + port HNBAP_PT HNBAP; + port RUA_PT RUA; + var TestHdlrParams g_pars; + + var boolean g_vty_initialized := false; +} + +function f_HNBGW_ConnHdlr_init_vty() runs on HNBGW_ConnHdlr { + if (not g_vty_initialized) { + map(self:HNBVTY, system:HNBVTY); + f_vty_set_prompts(HNBVTY); + f_vty_transceive(HNBVTY, "enable"); + g_vty_initialized := true; + } +} + +/* initialize all parameters */ +function f_HNBGW_ConnHdlr_init(TestHdlrParams pars) runs on HNBGW_ConnHdlr { + var integer i := 0; + var Iuh_Emulation_CT vc_Iuh; + + g_pars := valueof(pars); + vc_Iuh := Iuh_Emulation_CT.create("HNBGW" & int2str(i)); + connect(self:HNBAP, vc_Iuh:HNBAP); + connect(self:RUA, vc_Iuh:RUA); + + var Iuh_conn_parameters iuh_pars; + iuh_pars.remote_ip := g_pars.hnodeb_addr; + iuh_pars.remote_sctp_port := -1; + iuh_pars.local_ip := g_pars.hnbgw_addr; + iuh_pars.local_sctp_port := g_pars.hnbgw_port; + vc_Iuh.start(Iuh_Emulation.main(iuh_pars, "Iuh" & int2str(i))); + + f_HNBGW_ConnHdlr_init_vty(); +} + +type record TestHdlrParams { + charstring hnbgw_addr, + charstring hnodeb_addr, + integer hnbgw_port, + uint16_t rnc_id, + charstring hNB_Identity_Info, + OCT3 plmnid, + uint32_t cell_identity, + uint16_t lac, + uint8_t rac, + uint8_t sac +}; + +/* Note: Do not use valueof() to get a value of this template, use + * f_gen_test_hdlr_pars() instead in order to get a configuration. */ +template (value) TestHdlrParams t_def_TestHdlrPars := { + hnbgw_addr := "127.0.0.1", + hnodeb_addr := "127.0.0.1", + hnbgw_port := 29169, + rnc_id := 23, + hNB_Identity_Info := "OsmoHNodeB", + plmnid := '00F110'O, + cell_identity := 1, + lac := 2, + rac := 3, + sac := 4 +} + + +function f_handle_hnbap_hnb_register_req() +runs on HNBGW_ConnHdlr { + HNBAP.receive(tr_HNBAP_HNBRegisterRequest(char2oct(g_pars.hNB_Identity_Info), + g_pars.plmnid, + int2bit(g_pars.cell_identity, 28), + int2oct(g_pars.lac, 2), + int2oct(g_pars.rac, 1), + int2oct(g_pars.sac, 2) + )); + HNBAP.send(ts_HNBAP_HNBRegisterAccept(g_pars.rnc_id)); +} + + + +} diff --git a/hnodeb/HNB_Tests.cfg b/hnodeb/HNB_Tests.cfg new file mode 100644 index 000000000..38d9cc7c1 --- /dev/null +++ b/hnodeb/HNB_Tests.cfg @@ -0,0 +1,18 @@ +[ORDERED_INCLUDE] +# Common configuration, shared between test suites +"../Common.cfg" +# testsuite specific configuration, not expected to change +"./HNB_Tests.default" + +# Local configuration below + +[LOGGING] + +[TESTPORT_PARAMETERS] + +[MODULE_PARAMETERS] + +[MAIN_CONTROLLER] + +[EXECUTE] +HNB_Tests.control diff --git a/hnodeb/HNB_Tests.default b/hnodeb/HNB_Tests.default new file mode 100644 index 000000000..a06dcc67c --- /dev/null +++ b/hnodeb/HNB_Tests.default @@ -0,0 +1,27 @@ +[LOGGING] +mtc.FileMask := LOG_ALL | TTCN_DEBUG | TTCN_MATCHING | DEBUG_ENCDEC; + +[TESTPORT_PARAMETERS] +*.HNBVTY.CTRL_MODE := "client" +*.HNBVTY.CTRL_HOSTNAME := "127.0.0.1" +*.HNBVTY.CTRL_PORTNUM := "4273" +*.HNBVTY.CTRL_LOGIN_SKIPPED := "yes" +*.HNBVTY.CTRL_DETECT_SERVER_DISCONNECTED := "yes" +*.HNBVTY.CTRL_READMODE := "buffered" +*.HNBVTY.CTRL_CLIENT_CLEANUP_LINEFEED := "yes" +*.HNBVTY.CTRL_DETECT_CONNECTION_ESTABLISHMENT_RESULT := "yes" +*.HNBVTY.PROMPT1 := "OsmoHNodeB> " +*.STATSVTY.CTRL_MODE := "client" +*.STATSVTY.CTRL_HOSTNAME := "127.0.0.1" +*.STATSVTY.CTRL_PORTNUM := "4274" +*.STATSVTY.CTRL_LOGIN_SKIPPED := "yes" +*.STATSVTY.CTRL_DETECT_SERVER_DISCONNECTED := "yes" +*.STATSVTY.CTRL_READMODE := "buffered" +*.STATSVTY.CTRL_CLIENT_CLEANUP_LINEFEED := "yes" +*.STATSVTY.CTRL_DETECT_CONNECTION_ESTABLISHMENT_RESULT := "yes" +*.STATSVTY.PROMPT1 := "OsmoHNodeB> " + +[MODULE_PARAMETERS] +Osmocom_VTY_Functions.mp_prompt_prefix := "OsmoHNodeB"; + +[EXECUTE] diff --git a/hnodeb/HNB_Tests.ttcn b/hnodeb/HNB_Tests.ttcn new file mode 100644 index 000000000..7a668dde0 --- /dev/null +++ b/hnodeb/HNB_Tests.ttcn @@ -0,0 +1,214 @@ +module HNB_Tests { + +/* Integration Tests for OsmoHNodeB + * (C) 2021 by 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 + * + * This test suite tests OsmoHNodB while emulating both multiple UE as + * well as the HNBGW. See README for more details. + * + * There are test cases that run in so-called 'handler mode' and test cases + * that run directly on top of the BSSAP and RSL CodecPorts. The "handler mode" + * tests abstract the multiplexing/demultiplexing of multiple SCCP connections + * and/or RSL channels and are hence suitable for higher-level test cases, while + * the "raw" tests directly on top of the CodecPorts are more suitable for lower- + * level testing. + */ + +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 HNBGW_ConnectionHandler all; +import from Iuh_Emulation all; + +modulepar { + /* IP address at which the HNodeB can be reached */ + charstring mp_hnodeb_ip := "127.0.0.1"; + + /* IP address at which the test binds */ + charstring mp_hnbgw_iuh_ip := "127.0.0.1"; + integer mp_hnbgw_iuh_port := 29169; +} + +type component test_CT extends CTRL_Adapter_CT { + port TELNETasp_PT HNBVTY; + + /* global test case guard timer (actual timeout value is set in f_init()) */ + timer T_guard := 30.0; +} + +/* global altstep for global guard timer; */ +altstep as_Tguard() runs on test_CT { + [] T_guard.timeout { + setverdict(fail, "Timeout of T_guard"); + mtc.stop; + } +} + +friend function f_logp(TELNETasp_PT pt, charstring log_msg) +{ + // log on TTCN3 log output + log(log_msg); + // log in stderr log + f_vty_transceive(pt, "logp lglobal notice TTCN3 f_logp(): " & log_msg); +} + +function f_init_vty(charstring id := "foo") runs on test_CT { + if (HNBVTY.checkstate("Mapped")) { + /* skip initialization if already executed once */ + return; + } + map(self:HNBVTY, system:HNBVTY); + f_vty_set_prompts(HNBVTY); + f_vty_transceive(HNBVTY, "enable"); +} +/* global initialization function */ +function f_init(float guard_timeout := 30.0) runs on test_CT { + var integer bssap_idx; + + T_guard.start(guard_timeout); + activate(as_Tguard()); + + f_init_vty("VirtHNBGW"); + + /* TODO: Wait for Iuh connection to be established */ +} + +friend function f_shutdown_helper() runs on test_CT { + all component.stop; + setverdict(pass); + mtc.stop; +} + +friend function f_gen_test_hdlr_pars() return TestHdlrParams { + + var TestHdlrParams pars := valueof(t_def_TestHdlrPars); + pars.hnodeb_addr := mp_hnodeb_ip; + pars.hnbgw_addr := mp_hnbgw_iuh_ip; + pars.hnbgw_port := mp_hnbgw_iuh_port; + return pars; +} + +type function void_fn(charstring id) runs on HNBGW_ConnHdlr; + +/* helper function to create and connect a HNBGW_ConnHdlr component */ +private function f_connect_handler(inout HNBGW_ConnHdlr vc_conn, integer bssap_idx := 0) runs on test_CT { + /*connect(vc_conn:RAN, g_bssap[bssap_idx].vc_RAN:PROC); + connect(vc_conn:MGCP_PROC, vc_MGCP:MGCP_PROC); + connect(vc_conn:RSL, bts[0].rsl.vc_RSL:CLIENT_PT); + connect(vc_conn:RSL_PROC, bts[0].rsl.vc_RSL:RSL_PROC); + if (isvalue(bts[1])) { + connect(vc_conn:RSL1, bts[1].rsl.vc_RSL:CLIENT_PT); + connect(vc_conn:RSL1_PROC, bts[1].rsl.vc_RSL:RSL_PROC); + } + if (isvalue(bts[2])) { + connect(vc_conn:RSL2, bts[2].rsl.vc_RSL:CLIENT_PT); + connect(vc_conn:RSL2_PROC, bts[2].rsl.vc_RSL:RSL_PROC); + } + connect(vc_conn:BSSAP, g_bssap[bssap_idx].vc_RAN:CLIENT); + if (mp_enable_lcs_tests) { + connect(vc_conn:BSSAP_LE, g_bssap_le.vc_BSSAP_LE:CLIENT); + connect(vc_conn:BSSAP_LE_PROC, g_bssap_le.vc_BSSAP_LE:PROC); + } + connect(vc_conn:MGCP, vc_MGCP:MGCP_CLIENT); + connect(vc_conn:MGCP_MULTI, vc_MGCP:MGCP_CLIENT_MULTI); + connect(vc_conn:STATSD_PROC, vc_STATSD:STATSD_PROC);*/ +} + +function f_start_handler_create(TestHdlrParams pars) +runs on test_CT return HNBGW_ConnHdlr { + var charstring id := testcasename(); + var HNBGW_ConnHdlr vc_conn; + vc_conn := HNBGW_ConnHdlr.create(id); + f_connect_handler(vc_conn); + return vc_conn; +} + +function f_start_handler_run(HNBGW_ConnHdlr vc_conn, void_fn fn, TestHdlrParams pars) +runs on test_CT return HNBGW_ConnHdlr { + var charstring id := testcasename(); + /* Emit a marker to appear in the SUT's own logging output */ + f_logp(HNBVTY, id & "() start"); + vc_conn.start(f_handler_init(fn, id, pars)); + return vc_conn; +} + +function f_start_handler(void_fn fn, template (omit) TestHdlrParams pars_tmpl := omit) +runs on test_CT return HNBGW_ConnHdlr { + var TestHdlrParams pars; + if (isvalue(pars)) { + pars := valueof(pars_tmpl); + } else { + pars := valueof(f_gen_test_hdlr_pars()); + } + return f_start_handler_run(f_start_handler_create(pars), fn, pars); +} + +/* first function inside ConnHdlr component; sets g_pars + starts function */ +private function f_handler_init(void_fn fn, charstring id, TestHdlrParams pars) +runs on HNBGW_ConnHdlr { + f_HNBGW_ConnHdlr_init(pars); + HNBAP.receive(IUHEM_Event:{up_down:=IUHEM_EVENT_UP}); /* Wait for HNB to connect to us */ + fn.apply(id); +} + +private function f_tc_hnb_register_request(charstring id) runs on HNBGW_ConnHdlr { + f_handle_hnbap_hnb_register_req(); + f_sleep(1.0); +} +testcase TC_hnb_register_request_accept() runs on test_CT { + var HNBGW_ConnHdlr vc_conn; + + f_init(); + vc_conn := f_start_handler(refers(f_tc_hnb_register_request)); + vc_conn.done; + f_shutdown_helper(); +} + +private function f_tc_hnb_register_reject(charstring id) runs on HNBGW_ConnHdlr { + HNBAP.receive(tr_HNBAP_HNBRegisterRequest(char2oct(g_pars.hNB_Identity_Info), + g_pars.plmnid, + int2bit(g_pars.cell_identity, 28), + int2oct(g_pars.lac, 2), + int2oct(g_pars.rac, 1), + int2oct(g_pars.sac, 2) + )); + HNBAP.send(ts_HNBAP_HNBRegisterReject(ts_HnbapCause(overload))); + f_sleep(1.0); +} +testcase TC_hnb_register_request_reject() runs on test_CT { + var HNBGW_ConnHdlr vc_conn; + + f_init(); + vc_conn := f_start_handler(refers(f_tc_hnb_register_reject)); + vc_conn.done; + f_shutdown_helper(); +} + +control { + execute( TC_hnb_register_request_accept() ); + execute( TC_hnb_register_request_reject() ); +} + +} diff --git a/hnodeb/README.md b/hnodeb/README.md new file mode 100644 index 000000000..3b3fa9fba --- /dev/null +++ b/hnodeb/README.md @@ -0,0 +1,30 @@ +# HNB_Tests.ttcn + +* external interfaces + * Iuh side (emulates HNBGW-side) + * SCTP/HNBAP + * SCTP/RUA/RANAP + * RTP side: emulates MGW + * GTP-U side: emulates GGSN + * UE side + * VTY + * CTRL + * StatsD + +{% dot hnb_tests.svg +digraph G { + graph [label="HNB_Tests", labelloc=t, fontsize=30]; + rankdir=LR; + { rank=same; ATS; HNB; }; + HNB [label="IUT\nosmo-bsc",shape="box"]; + ATS [label="ATS\nHNB_Tests.ttcn"]; + + HNB <- ATS [label="Uu (or some intermediate lower layer)"]; + HNB -> ATS [label="Iuh"]; + HNB -> ATS [label="RTP"]; + HNB -> ATS [label="GTP-U"]; + HNB <- ATS [label="CTRL"]; + HNB <- ATS [label="VTY"]; + HNB -> ATS [label="StatsD"]; +} +%} diff --git a/hnodeb/README.txt b/hnodeb/README.txt new file mode 100644 index 000000000..1cf81ee0c --- /dev/null +++ b/hnodeb/README.txt @@ -0,0 +1,32 @@ +Integration Tests for OsmoHnodeB +-------------------------------- + +This test suite tests OsmoHNodeB while emulating both multiple UE as +well as the HNBGW. + +The included jenkins.sh script, together with the Dockerfiles from +http://git.osmocom.org/docker-playground/ can be used to run both the +osmo-hnodeb-under-test as well as the extenal entities and the tester. + + +Further Test Ideas +------------------ + +This is a random list of things about things possible to test. +Asterisks '*' are TODO, while 'x' means already implemented. + += exhaustion of resources + += paging + += assignment + += hand-over + += erroneous channel release + += misc + += counters + += VTY based/corresponding tests diff --git a/hnodeb/expected-results.xml b/hnodeb/expected-results.xml new file mode 100644 index 000000000..39cea9feb --- /dev/null +++ b/hnodeb/expected-results.xml @@ -0,0 +1,4 @@ + + + + diff --git a/hnodeb/gen_links.sh b/hnodeb/gen_links.sh new file mode 100755 index 000000000..ba946e8c5 --- /dev/null +++ b/hnodeb/gen_links.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +BASEDIR=../deps + +. ../gen_links.sh.inc + +#DIR=$BASEDIR/titan.TestPorts.UNIX_DOMAIN_SOCKETasp/src +#FILES="UD_PT.cc UD_PT.hh UD_PortType.ttcn UD_Types.ttcn" +#gen_links $DIR $FILES + +DIR=$BASEDIR/titan.Libraries.TCCUsefulFunctions/src +FILES="TCCInterface_Functions.ttcn TCCConversion_Functions.ttcn TCCConversion.cc TCCInterface.cc TCCInterface_ip.h" +FILES+=" TCCEncoding_Functions.ttcn TCCEncoding.cc " # GSM 7-bit coding +gen_links $DIR $FILES + +DIR=$BASEDIR/titan.TestPorts.Common_Components.Socket-API/src +FILES="Socket_API_Definitions.ttcn" +gen_links $DIR $FILES + +# Required by MGCP and IPA +DIR=$BASEDIR/titan.TestPorts.IPL4asp/src +FILES="IPL4asp_Functions.ttcn IPL4asp_PT.cc IPL4asp_PT.hh IPL4asp_PortType.ttcn IPL4asp_Types.ttcn IPL4asp_discovery.cc IPL4asp_protocol_L234.hh" +gen_links $DIR $FILES + +# required by M3UA_Emulation +DIR=$BASEDIR/titan.TestPorts.SCTPasp/src +FILES="SCTPasp_PT.cc SCTPasp_PT.hh SCTPasp_PortType.ttcn SCTPasp_Types.ttcn" +gen_links $DIR $FILES + +DIR=$BASEDIR/titan.ProtocolModules.SDP/src +FILES="SDP_EncDec.cc SDP_Types.ttcn SDP_parse_.tab.c SDP_parse_.tab.h SDP_parse_parser.h SDP_parser.l +SDP_parser.y lex.SDP_parse_.c" +gen_links $DIR $FILES + +DIR=$BASEDIR/titan.ProtocolModules.RTP/src +FILES="RTP_EncDec.cc RTP_Types.ttcn" +gen_links $DIR $FILES + +DIR=$BASEDIR/titan.TestPorts.TELNETasp/src +FILES="TELNETasp_PT.cc TELNETasp_PT.hh TELNETasp_PortType.ttcn" +gen_links $DIR $FILES + +DIR=../library/hnbap +FILES="HNBAP_CommonDataTypes.asn HNBAP_Constants.asn HNBAP_Containers.asn HNBAP_IEs.asn HNBAP_PDU_Contents.asn HNBAP_PDU_Descriptions.asn " +FILES+="HNBAP_EncDec.cc HNBAP_Types.ttcn HNBAP_Templates.ttcn " +gen_links $DIR $FILES + +DIR=../library/rua +FILES="RUA_CommonDataTypes.asn RUA_Constants.asn RUA_Containers.asn RUA_IEs.asn RUA_PDU_Contents.asn RUA_PDU_Descriptions.asn " +FILES+="RUA_EncDec.cc RUA_Types.ttcn RUA_Templates.ttcn " +gen_links $DIR $FILES + +DIR=../library +FILES="Iuh_Types.ttcn Iuh_CodecPort.ttcn Iuh_CodecPort_CtrlFunctDef.cc Iuh_CodecPort_CtrlFunct.ttcn Iuh_Emulation.ttcn DNS_Helpers.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+="StatsD_Types.ttcn StatsD_CodecPort.ttcn StatsD_CodecPort_CtrlFunct.ttcn StatsD_CodecPort_CtrlFunctdef.cc StatsD_Checker.ttcn " + +gen_links $DIR $FILES + +ignore_pp_results diff --git a/hnodeb/osmo-hnodeb.cfg b/hnodeb/osmo-hnodeb.cfg new file mode 100644 index 000000000..3ccd4b412 --- /dev/null +++ b/hnodeb/osmo-hnodeb.cfg @@ -0,0 +1,19 @@ +log stderr + logging filter all 1 + logging color 1 + logging print category-hex 0 + logging print category 1 + logging print extended-timestamp 1 + logging print file basename + logging level set-all debug +line vty + no login +! +hnodeb + cell_identity 1 + location_area_code 2 + routing_area_code 3 + service_area_code 4 + iuh + local-ip 127.0.0.1 + remote-ip 127.0.0.1 diff --git a/hnodeb/regen_makefile.sh b/hnodeb/regen_makefile.sh new file mode 100755 index 000000000..9ebfaf2f0 --- /dev/null +++ b/hnodeb/regen_makefile.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +NAME=HNB_Tests + +FILES=" + *.asn + *.c + *.ttcn + *.ttcnpp + IPA_CodecPort_CtrlFunctDef.cc + IPL4asp_PT.cc + IPL4asp_discovery.cc + IuUP_EncDec.cc + Iuh_CodecPort_CtrlFunctDef.cc + Native_FunctionDefs.cc + RTP_CodecPort_CtrlFunctDef.cc + RTP_EncDec.cc + SCTPasp_PT.cc + SDP_EncDec.cc + StatsD_CodecPort_CtrlFunctdef.cc + TCCConversion.cc + TCCEncoding.cc + TCCInterface.cc + TELNETasp_PT.cc + HNBAP_EncDec.cc + RUA_EncDec.cc +" + +export CPPFLAGS_TTCN3=" + -DIPA_EMULATION_CTRL +" + +../regen-makefile.sh -e $NAME $FILES + +sed -i -e 's/^LINUX_LIBS = -lxml2 -lsctp/LINUX_LIBS = -lxml2 -lsctp -lfftranscode/' Makefile diff --git a/library/Iuh_CodecPort.ttcn b/library/Iuh_CodecPort.ttcn new file mode 100644 index 000000000..569a27fdd --- /dev/null +++ b/library/Iuh_CodecPort.ttcn @@ -0,0 +1,141 @@ +module Iuh_CodecPort { + +/* Simple Iuh Codec Port, translating between raw SCTP primitives with + * octetstring payload towards the IPL4asp provider, and Iuh primitives + * which carry the decoded Iuh data types as payload. + * + * (C) 2021 by sysmocom s.f.m.c. GmbH + * Author: Pau Espin Pedrol + * 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 IPL4asp_PortType all; + import from IPL4asp_Types all; + import from HNBAP_PDU_Descriptions all; + import from HNBAP_Types all; + import from RUA_PDU_Descriptions all; + import from RUA_Types all; + import from Iuh_Types all; + + type record Iuh_RecvFrom { + ConnectionId connId, + HostName remName, + PortNumber remPort, + HostName locName, + PortNumber locPort, + Iuh_PDU msg + }; + + template Iuh_RecvFrom t_Iuh_RecvFrom(template Iuh_PDU msg) := { + connId := ?, + remName := ?, + remPort := ?, + locName := ?, + locPort := ?, + msg := msg + } + + template Iuh_RecvFrom t_Iuh_RecvFrom_HNBAP(template HNBAP_PDU hnbap_msg := ?) := { + connId := ?, + remName := ?, + remPort := ?, + locName := ?, + locPort := ?, + msg := { + hnbap := hnbap_msg + } + } + + template Iuh_RecvFrom t_Iuh_RecvFrom_RUA(template RUA_PDU rua_msg := ?) := { + connId := ?, + remName := ?, + remPort := ?, + locName := ?, + locPort := ?, + msg := { + rua := rua_msg + } + } + + type record Iuh_Send { + ConnectionId connId, + Iuh_PDU msg + }; + + template Iuh_Send t_Iuh_Send_HNBAP(template ConnectionId connId, template HNBAP_PDU hnbap_msg) := { + connId := connId, + msg := { + hnbap := hnbap_msg + } + } + + template Iuh_Send t_Iuh_Send_RUA(template ConnectionId connId, template RUA_PDU rua_msg) := { + connId := connId, + msg := { + rua := rua_msg + } + } + + private function IPL4_to_Iuh_RecvFrom(in ASP_RecvFrom pin, out Iuh_RecvFrom pout) { + pout.connId := pin.connId; + pout.remName := pin.remName; + pout.remPort := pin.remPort; + pout.locName := pin.locName; + pout.locPort := pin.locPort; + select (pin.proto.sctp.sinfo_ppid) { + case (19) { + pout.msg.rua := dec_RUA_PDU(pin.msg); + } + case (20) { + pout.msg.hnbap := dec_HNBAP_PDU(pin.msg); + } + case (0) { + /* FIXME: lower layers report sinfo_ppid=0: */ + pout.msg.hnbap := dec_HNBAP_PDU(pin.msg); + } + case else { + pout.msg.payload := pin.msg; + } + } + } with { extension "prototype(fast)" }; + + private function Iuh_to_IPL4_Send(in Iuh_Send pin, out ASP_Send pout) { + var integer sctp_ppid; + if (ischosen(pin.msg.rua)) { + sctp_ppid := 19; + pout.msg := enc_RUA_PDU(pin.msg.rua); + } else if (ischosen(pin.msg.hnbap)) { + sctp_ppid := 20; + pout.msg := enc_HNBAP_PDU(pin.msg.hnbap); + } else { /*TODO: abort?*/ + sctp_ppid := 0; + pout.msg := pin.msg.payload; + } + pout.connId := pin.connId; + pout.proto := { + sctp := { + sinfo_stream := omit, + sinfo_ppid := sctp_ppid, + remSocks := omit, + assocId := omit + } + }; + } with { extension "prototype(fast)" }; + + type port Iuh_CODEC_PT message { + out Iuh_Send; + in Iuh_RecvFrom, + ASP_ConnId_ReadyToRelease, + ASP_Event; + } with { extension "user IPL4asp_PT + out(Iuh_Send -> ASP_Send:function(Iuh_to_IPL4_Send)) + in(ASP_RecvFrom -> Iuh_RecvFrom: function(IPL4_to_Iuh_RecvFrom); + ASP_ConnId_ReadyToRelease -> ASP_ConnId_ReadyToRelease: simple; + ASP_Event -> ASP_Event: simple)" + } +} diff --git a/library/Iuh_CodecPort_CtrlFunct.ttcn b/library/Iuh_CodecPort_CtrlFunct.ttcn new file mode 100644 index 000000000..c0c7003fe --- /dev/null +++ b/library/Iuh_CodecPort_CtrlFunct.ttcn @@ -0,0 +1,43 @@ +module Iuh_CodecPort_CtrlFunct { + + import from Iuh_CodecPort all; + import from IPL4asp_Types all; + + external function f_IPL4_listen( + inout Iuh_CODEC_PT portRef, + in HostName locName, + in PortNumber locPort, + in ProtoTuple proto, + in OptionList options := {} + ) return Result; + + external function f_IPL4_connect( + inout Iuh_CODEC_PT portRef, + in HostName remName, + in PortNumber remPort, + in HostName locName, + in PortNumber locPort, + in ConnectionId connId, + in ProtoTuple proto, + in OptionList options := {} + ) return Result; + + external function f_IPL4_close( + inout Iuh_CODEC_PT portRef, + in ConnectionId id, + in ProtoTuple proto := { unspecified := {} } + ) return Result; + + external function f_IPL4_setUserData( + inout Iuh_CODEC_PT portRef, + in ConnectionId id, + in UserData userData + ) return Result; + + external function f_IPL4_getUserData( + inout Iuh_CODEC_PT portRef, + in ConnectionId id, + out UserData userData + ) return Result; + +} diff --git a/library/Iuh_CodecPort_CtrlFunctDef.cc b/library/Iuh_CodecPort_CtrlFunctDef.cc new file mode 100644 index 000000000..2f08deca1 --- /dev/null +++ b/library/Iuh_CodecPort_CtrlFunctDef.cc @@ -0,0 +1,55 @@ +#include "IPL4asp_PortType.hh" +#include "Iuh_CodecPort.hh" +#include "IPL4asp_PT.hh" + +namespace Iuh__CodecPort__CtrlFunct { + + IPL4asp__Types::Result f__IPL4__listen( + Iuh__CodecPort::Iuh__CODEC__PT& portRef, + const IPL4asp__Types::HostName& locName, + const IPL4asp__Types::PortNumber& locPort, + const IPL4asp__Types::ProtoTuple& proto, + const IPL4asp__Types::OptionList& options) + { + return f__IPL4__PROVIDER__listen(portRef, locName, locPort, proto, options); + } + + IPL4asp__Types::Result f__IPL4__connect( + Iuh__CodecPort::Iuh__CODEC__PT& portRef, + const IPL4asp__Types::HostName& remName, + const IPL4asp__Types::PortNumber& remPort, + const IPL4asp__Types::HostName& locName, + const IPL4asp__Types::PortNumber& locPort, + const IPL4asp__Types::ConnectionId& connId, + const IPL4asp__Types::ProtoTuple& proto, + const IPL4asp__Types::OptionList& options) + { + return f__IPL4__PROVIDER__connect(portRef, remName, remPort, + locName, locPort, connId, proto, options); + } + + IPL4asp__Types::Result f__IPL4__close( + Iuh__CodecPort::Iuh__CODEC__PT& portRef, + const IPL4asp__Types::ConnectionId& connId, + const IPL4asp__Types::ProtoTuple& proto) + { + return f__IPL4__PROVIDER__close(portRef, connId, proto); + } + + IPL4asp__Types::Result f__IPL4__setUserData( + Iuh__CodecPort::Iuh__CODEC__PT& portRef, + const IPL4asp__Types::ConnectionId& connId, + const IPL4asp__Types::UserData& userData) + { + return f__IPL4__PROVIDER__setUserData(portRef, connId, userData); + } + + IPL4asp__Types::Result f__IPL4__getUserData( + Iuh__CodecPort::Iuh__CODEC__PT& portRef, + const IPL4asp__Types::ConnectionId& connId, + IPL4asp__Types::UserData& userData) + { + return f__IPL4__PROVIDER__getUserData(portRef, connId, userData); + } + +} diff --git a/library/Iuh_Emulation.ttcn b/library/Iuh_Emulation.ttcn new file mode 100644 index 000000000..75a95ba83 --- /dev/null +++ b/library/Iuh_Emulation.ttcn @@ -0,0 +1,212 @@ +module Iuh_Emulation { + +/* Iuh Emulation, runs on top of Iuh_CodecPort. It multiplexes/demultiplexes + * HNBAP and RUA. + * + * The Iuh_Emulation.main() function processes Iuh primitives from the Iuh + * socket via the Iuh_CodecPort, and dispatches them to HNBAP/RUA ports. + * + * (C) 2021 by sysmocom s.f.m.c. GmbH + * Author: Pau Espin Pedrol + * 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 Iuh_CodecPort all; +import from Iuh_CodecPort_CtrlFunct all; +import from HNBAP_Types all; +import from HNBAP_Constants all; +import from HNBAP_PDU_Contents all; +import from HNBAP_PDU_Descriptions all; +import from HNBAP_IEs all; +import from HNBAP_Templates all; +import from RUA_Types all; +import from RUA_Constants all; +import from RUA_PDU_Contents all; +import from RUA_PDU_Descriptions all; +import from RUA_IEs all; +import from RUA_Templates all; +import from Iuh_Types all; + +import from General_Types all; +import from Misc_Helpers all; +import from Osmocom_Types all; +import from IPL4asp_Types all; +import from DNS_Helpers all; + +type enumerated IUHEM_EventUpDown { + IUHEM_EVENT_DOWN, + IUHEM_EVENT_UP +} + +/* an event indicating us whether or not a connection is physically up or down. */ +type union IUHEM_Event { + IUHEM_EventUpDown up_down +} + +type port HNBAP_PT message { + inout HNBAP_PDU, IUHEM_Event; +} with { extension "internal" }; +type port RUA_PT message { + inout RUA_PDU, IUHEM_Event; +} with { extension "internal" }; + +type component Iuh_Emulation_CT { + /* Port facing to the SCTP SUT */ + port Iuh_CODEC_PT Iuh; + /* Port facing to user upper side stack: */ + port HNBAP_PT HNBAP; + port RUA_PT RUA; + + var Iuh_conn_parameters g_pars; + var charstring g_Iuh_id; + var integer g_self_conn_id := -1; + var IPL4asp_Types.ConnectionId g_last_conn_id := -1; /* server only */ +} + +type record Iuh_conn_parameters { + HostName remote_ip, + PortNumber remote_sctp_port, + HostName local_ip, + PortNumber local_sctp_port +} + +function tr_Iuh_RecvFrom_R(template Iuh_PDU msg) +runs on Iuh_Emulation_CT return template Iuh_RecvFrom { + var template Iuh_RecvFrom mrf := { + connId := ?, + remName := ?, + remPort := ?, + locName := ?, + locPort := ?, + msg := msg + } + return mrf; +} + +private template (value) SctpTuple ts_SCTP(template (omit) integer ppid := omit) := { + sinfo_stream := omit, + sinfo_ppid := ppid, + remSocks := omit, + assocId := omit +}; + +private template PortEvent tr_SctpAssocChange := { + sctpEvent := { + sctpAssocChange := ? + } +} +private template PortEvent tr_SctpPeerAddrChange := { + sctpEvent := { + sctpPeerAddrChange := ? + } +} + +private function emu_is_server() runs on Iuh_Emulation_CT return boolean { + return g_pars.remote_sctp_port == -1 +} + +/* Resolve TCP/IP connection identifier depending on server/client mode */ +private function f_iuh_conn_id() runs on Iuh_Emulation_CT +return IPL4asp_Types.ConnectionId { + var IPL4asp_Types.ConnectionId conn_id; + + if (not emu_is_server()) { + conn_id := g_self_conn_id; + } else { + conn_id := g_last_conn_id; + } + + if (conn_id == -1) { /* Just to be sure */ + f_shutdown(__FILE__, __LINE__, fail, "Connection is not established"); + } + + return conn_id; +} + +function main(Iuh_conn_parameters p, charstring id) runs on Iuh_Emulation_CT { + var Result res; + g_pars := p; + g_Iuh_id := id; + + map(self:Iuh, system:Iuh_CODEC_PT); + if (emu_is_server()) { + res := Iuh_CodecPort_CtrlFunct.f_IPL4_listen(Iuh, p.local_ip, p.local_sctp_port, { sctp := valueof(ts_SCTP) }); + } else { + res := Iuh_CodecPort_CtrlFunct.f_IPL4_connect(Iuh, p.remote_ip, p.remote_sctp_port, + p.local_ip, p.local_sctp_port, -1, { sctp := valueof(ts_SCTP) }); + } + if (not ispresent(res.connId)) { + f_shutdown(__FILE__, __LINE__, fail, "Could not connect Iuh socket, check your configuration"); + } + g_self_conn_id := res.connId; + + /* notify user about SCTP establishment */ + if (p.remote_sctp_port != -1) { + HNBAP.send(IUHEM_Event:{up_down:=IUHEM_EVENT_UP}); + } + + while (true) { + var Iuh_RecvFrom mrf; + var HNBAP_PDU hnbap_msg; + var RUA_PDU rua_msg; + var ASP_Event asp_evt; + + alt { + /* HNBAP from client: pass on transparently */ + [] HNBAP.receive(HNBAP_PDU:?) -> value hnbap_msg { + /* Pass message through */ + Iuh.send(t_Iuh_Send_HNBAP(f_iuh_conn_id(), hnbap_msg)); + } + /* RUA from client: pass on transparently */ + [] RUA.receive(RUA_PDU:?) -> value rua_msg { + /* Pass message through */ + Iuh.send(t_Iuh_Send_RUA(f_iuh_conn_id(), rua_msg)); + } + + /* Iuh received from peer (HNBGW or HnodeB) */ + [] Iuh.receive(tr_Iuh_RecvFrom_R(?)) -> value mrf { + if (not match(mrf.connId, f_iuh_conn_id())) { + f_shutdown(__FILE__, __LINE__, fail, log2str("Received message from unexpected conn_id!", mrf)); + } + + if (match(mrf, t_Iuh_RecvFrom_HNBAP(?))) { + HNBAP.send(mrf.msg.hnbap); + } else if (match(mrf, t_Iuh_RecvFrom_RUA(?))) { + RUA.send(mrf.msg.rua); + } else { + /* TODO: special handling, as it contains multiple HNB connection ids */ + f_shutdown(__FILE__, __LINE__, fail, log2str("UNEXPECTED MESSAGE RECEIVED!", mrf)); + } + } + [] Iuh.receive(tr_SctpAssocChange) { } + [] Iuh.receive(tr_SctpPeerAddrChange) { } + + /* server only */ + [] Iuh.receive(ASP_Event:{connOpened:=?}) -> value asp_evt { + if (not emu_is_server()) { + f_shutdown(__FILE__, __LINE__, fail, log2str("Unexpected event receiver in client mode", asp_evt)); + } + g_last_conn_id := asp_evt.connOpened.connId; + log("Established a new Iuh connection (conn_id=", g_last_conn_id, ")"); + + HNBAP.send(IUHEM_Event:{up_down:=IUHEM_EVENT_UP}); /* TODO: send g_last_conn_id */ + } + + [] Iuh.receive(ASP_Event:{connClosed:=?}) -> value asp_evt { + log("Iuh: Closed"); + g_self_conn_id := -1; + HNBAP.send(IUHEM_Event:{up_down:=IUHEM_EVENT_DOWN}); /* TODO: send asp_evt.connClosed.connId */ + if (not emu_is_server()) { + self.stop; + } + } + } + } +} + +} diff --git a/library/Iuh_Types.ttcn b/library/Iuh_Types.ttcn new file mode 100644 index 000000000..17f9c5f25 --- /dev/null +++ b/library/Iuh_Types.ttcn @@ -0,0 +1,36 @@ +/* Osmocom Iuh Interface Types + * (C) 2021 by sysmocom s.f.m.c. GmbH + * Author: Pau Espin Pedrol + * 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 + */ + + /* Iuh Codec Port Types + * + * (C) 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. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +module Iuh_Types { + +import from HNBAP_PDU_Descriptions all; +import from HNBAP_Types all; +import from RUA_PDU_Descriptions all; +import from RUA_Types all; + +type union Iuh_PDU { + HNBAP_PDU hnbap, + RUA_PDU rua, + octetstring payload +} + +}; diff --git a/library/hnbap/HNBAP_Templates.ttcn b/library/hnbap/HNBAP_Templates.ttcn new file mode 100644 index 000000000..44704f872 --- /dev/null +++ b/library/hnbap/HNBAP_Templates.ttcn @@ -0,0 +1,135 @@ +/* HNBAP Templates in TTCN-3 + * (C) 2021 Pau Espin Pedrol + * All rights reserved. + * + * Released under the terms of GNU General Public License, Version 2 or + * (at your option) any later version. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +module HNBAP_Templates { + +import from General_Types all; +import from Osmocom_Types all; + +import from HNBAP_IEs all; +import from HNBAP_CommonDataTypes all; +import from HNBAP_Constants all; +import from HNBAP_Containers all; +import from HNBAP_PDU_Contents all; +import from HNBAP_PDU_Descriptions all; + +/********************************************************************************* + * 3GPP TS 25.469 + *********************************************************************************/ + +template (value) Cause ts_HnbapCause(template (value) CauseRadioNetwork c) := { + radioNetwork := c +} + +/* 9.1.3 HNB REGISTER REQUEST */ +template (present) HNBAP_PDU +tr_HNBAP_HNBRegisterRequest(template (present) octetstring hNB_Identity_Info := ?, + template (present) OCT3 plmnid := ?, + template (present) BIT28 cell_identity := ?, + template (present) OCT2 lac := ?, + template (present) OCT1 rac := ?, + template (present) OCT2 sac := ?) := { + initiatingMessage := { + procedureCode := id_HNBRegister, + criticality := reject, + value_ := { + hNBRegisterRequest := { + protocolIEs := { + { + id := HNBAP_Constants.id_HNB_Identity, + criticality := reject, + value_ := { + hNB_Identity := { + hNB_Identity_Info := hNB_Identity_Info, + iE_Extensions := omit + } + } + }, { + id := 8, + criticality := reject, + value_ := { + hNB_Location_Information := { + macroCoverageInfo := omit, + geographicalCoordinates := omit, + iE_Extensions := omit + } + } + }, { + id := 9, + criticality := reject, + value_ := { pLMNidentity := plmnid } + }, { + id := 11, + criticality := reject, + value_ := { cellIdentity := cell_identity } + }, { + id := 6, + criticality := reject, + value_ := { lAC := lac } + }, { + id := 7, + criticality := reject, + value_ := { rAC := rac } + }, { + id := 10, + criticality := reject, + value_ := { sAC := sac } + } + }, + protocolExtensions := omit + } + } + } +} + + +/* 9.1.4 HNB REGISTER ACCEPT */ +template (value) HNBAP_PDU +ts_HNBAP_HNBRegisterAccept(template (value) uint16_t rnc_id) := { + successfulOutcome := { + procedureCode := id_HNBRegister, + criticality := reject, + value_ := { + hNBRegisterAccept := { + protocolIEs := { + { + id := HNBAP_Constants.id_RNC_ID, + criticality := reject, + value_ := { RNC_ID := rnc_id } + } + }, + protocolExtensions := omit /* TODO: Mux Port Number (optional) 9.2.29 */ + } + } + } +} + +/* 9.1.5 HNB REGISTER REJECT */ +template (value) HNBAP_PDU +ts_HNBAP_HNBRegisterReject(template (value) Cause cause) := { + unsuccessfulOutcome := { + procedureCode := id_HNBRegister, + criticality := reject, + value_ := { + HNBRegisterReject := { + protocolIEs := { + { + id := HNBAP_Constants.id_Cause, + criticality := ignore, + value_ := { Cause := cause } + } + }, + protocolExtensions := omit /* TODO: CriticalityDiagnostics, BackoffTimer */ + } + } + } +} + +} diff --git a/library/rua/RUA_Templates.ttcn b/library/rua/RUA_Templates.ttcn new file mode 100644 index 000000000..b7387bffc --- /dev/null +++ b/library/rua/RUA_Templates.ttcn @@ -0,0 +1,22 @@ +/* RUA Templates in TTCN-3 + * (C) 2021 Pau Espin Pedrol + * All rights reserved. + * + * Released under the terms of GNU General Public License, Version 2 or + * (at your option) any later version. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +module RUA_Templates { + +import from RUA_IEs all; +import from RUA_CommonDataTypes all; +import from RUA_Constants all; +import from RUA_Containers all; +import from RUA_PDU_Contents all; +import from RUA_PDU_Descriptions all; + + +/* TODO: Add RUA Templates here */ +}