module HNBGW_ConnectionHandler { /* HNBGW Connection Handler of HNB_Tests in TTCN-3 * (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 */ 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; import from RTP_Types all; import from RTP_Emulation all; import from HNBLLIF_CodecPort all; import from HNBLLIF_Types all; import from HNBLLIF_Templates all; import from GTP_Emulation all; import from GTPv1U_Templates all; import from GTPv1U_CodecPort all; import from GTPU_Types all; /* this component represents a single Iuh connection at the HNBGW. */ type component HNBGW_ConnHdlr extends Iuh_ConnHdlr, GTP_ConnHdlr, StatsD_ConnHdlr { port TELNETasp_PT HNBVTY; /* HNBLLIF Interface of HNodeB */ port HNBLLIF_CODEC_PT LLSK; var integer g_llsk_conn_id; var RTP_Emulation_CT vc_RTPEM; port RTPEM_CTRL_PT RTPEM_CTRL; port RTPEM_DATA_PT RTPEM_DATA; var GTP_Emulation_CT vc_GTP; 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; } } private function f_HNBGW_ConnHdlr_init_iuh(charstring id) runs on HNBGW_ConnHdlr { var Iuh_Emulation_CT vc_Iuh; vc_Iuh := Iuh_Emulation_CT.create(id & "-HNBGW") alive; 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, id & "-Iuh")); } private function f_HNBGW_ConnHdlr_init_gtp(charstring id) runs on HNBGW_ConnHdlr { id := id & "-GTP"; var GtpEmulationCfg gtp_cfg := { gtpc_bind_ip := g_pars.hnbgw_addr, gtpc_bind_port := GTP1C_PORT, gtpu_bind_ip := g_pars.hnbgw_addr, gtpu_bind_port := GTP1U_PORT, sgsn_role := false }; vc_GTP := GTP_Emulation_CT.create(id) alive; connect(self:GTP[0], vc_GTP:CLIENT); connect(self:GTP_PROC[0], vc_GTP:CLIENT_PROC); vc_GTP.start(GTP_Emulation.main(gtp_cfg)); } /* initialize all parameters */ function f_HNBGW_ConnHdlr_init(charstring id, TestHdlrParams pars) runs on HNBGW_ConnHdlr { g_pars := valueof(pars); f_HNBGW_ConnHdlr_init_iuh(id); f_HNBGW_ConnHdlr_init_gtp(id); f_HNBGW_ConnHdlr_init_vty(); /* Connect to HNB on LLSK and do HELLO ping-pong */ f_start_hnbllif(LLSK, id & "-LLSK", g_pars, g_llsk_conn_id); } function f_start_hnbllif(HNBLLIF_CODEC_PT pt, charstring id, TestHdlrParams pars, out integer hnbllif_conn_id) { timer T := 2.0; var HNBLLIF_send_data sd; var HNBLLIF_Message last_hello_cnf; if (pars.hnbllif_sk_path == "") { hnbllif_conn_id := -1; return; } hnbllif_conn_id := f_hnbllif_connect(pt, pars.hnbllif_sk_path); T.start; pt.send(t_SD_HNBLLIF(hnbllif_conn_id, ts_HNBLLIF_CTL_HELLO_REQ(HNBLL_IF_SAPI_CTL, HNBLLIF_Types.mp_hnbllif_version))); alt { [] as_hnbllif_hello_cnf(pt, hnbllif_conn_id, last_hello_cnf, HNBLL_IF_SAPI_CTL, HNBLLIF_Types.mp_hnbllif_version); [] T.timeout { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for HNBLLIF HELLO.REQ SAPI=CTL"); } } pt.send(t_SD_HNBLLIF(hnbllif_conn_id, ts_HNBLLIF_CTL_HELLO_REQ(HNBLL_IF_SAPI_IUH, pars.hnbllif_sapi_iuh_version))); alt { [] as_hnbllif_hello_cnf(pt, hnbllif_conn_id, last_hello_cnf, HNBLL_IF_SAPI_IUH, pars.hnbllif_sapi_iuh_version); [] T.timeout { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for HNBLLIF HELLO.REQ SAPI=IUH"); } } pt.send(t_SD_HNBLLIF(hnbllif_conn_id, ts_HNBLLIF_CTL_HELLO_REQ(HNBLL_IF_SAPI_AUDIO, pars.hnbllif_sapi_audio_version))); alt { [] as_hnbllif_hello_cnf(pt, hnbllif_conn_id, last_hello_cnf, HNBLL_IF_SAPI_AUDIO, pars.hnbllif_sapi_audio_version); [] T.timeout { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for HNBLLIF HELLO.REQ SAPI=AUDIO"); } } pt.send(t_SD_HNBLLIF(hnbllif_conn_id, ts_HNBLLIF_CTL_HELLO_REQ(HNBLL_IF_SAPI_GTP, pars.hnbllif_sapi_gtp_version))); alt { [] as_hnbllif_hello_cnf(pt, hnbllif_conn_id, last_hello_cnf, HNBLL_IF_SAPI_GTP, pars.hnbllif_sapi_gtp_version); [] T.timeout { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Timeout waiting for HNBLLIF HELLO.REQ SAPI=GTP"); } } } type record TestHdlrParams { charstring hnbllif_sk_path, /* "" means don't connect */ uint16_t hnbllif_sapi_iuh_version, uint16_t hnbllif_sapi_gtp_version, uint16_t hnbllif_sapi_audio_version, charstring hnbgw_addr, charstring hnodeb_addr, integer hnbgw_port, integer hnbgw_rtp_port, uint16_t rnc_id, charstring hNB_Identity_Info, uint16_t mcc, uint16_t mnc, 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 := { hnbllif_sk_path := HNBLL_SOCK_DEFAULT, hnbllif_sapi_iuh_version := 0, hnbllif_sapi_gtp_version := 0, hnbllif_sapi_audio_version := 1, hnbgw_addr := "127.0.0.1", hnodeb_addr := "127.0.0.1", hnbgw_port := 29169, hnbgw_rtp_port := 9000, rnc_id := 23, hNB_Identity_Info := "OsmoHNodeB", mcc := 1, mnc := 1, cell_identity := 1, lac := 2, rac := 3, sac := 4 } template (value) Gtp1uPeer ts_GtpPeerU(charstring ip) := { connId := 1, remName := ip, remPort := GTP1U_PORT } function f_gtpu_send(uint32_t tei, octetstring payload) runs on HNBGW_ConnHdlr { var Gtp1uPeer peer := valueof(ts_GtpPeerU(g_pars.hnodeb_addr)); GTP[0].send(ts_GTP1U_GPDU(peer, omit /*opt_part*/, int2oct(tei, 4), payload)); } /* HNBLLIF socket may at any time receive a new INFO.ind */ altstep as_hnbllif_hello_cnf(HNBLLIF_CODEC_PT pt, integer hnbllif_conn_id, out HNBLLIF_Message last_hello_cnf, template (present) HNBLLIF_Sapi exp_sapi := ?, template (present) uint16_t exp_version := ?) { var HNBLLIF_send_data sd; [] pt.receive(t_SD_HNBLLIF(hnbllif_conn_id, tr_HNBLLIF_CTL_HELLO_CNF(exp_sapi, exp_version))) -> value sd { last_hello_cnf := sd.data; } [] pt.receive(t_SD_HNBLLIF(hnbllif_conn_id, tr_HNBLLIF_CTL_HELLO_CNF(?))) -> value sd { Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Invalid API_VERSION received"); } } function f_llsk_rx(template (present) HNBLLIF_Message exp_tmpl) runs on HNBGW_ConnHdlr return template (present) HNBLLIF_send_data { return t_SD_HNBLLIF(g_llsk_conn_id, exp_tmpl); } function f_llsk_tx(template (value) HNBLLIF_Message tx_msg) runs on HNBGW_ConnHdlr return template (value) HNBLLIF_send_data { return ts_SD_HNBLLIF(g_llsk_conn_id, tx_msg); } function f_enc_mcc_mnc(uint16_t mcc_uint, uint16_t mnc_uint) return OCT3 { var hexstring mnc; var hexstring mcc := int2hex(mcc_uint, 3); if (mnc_uint < 100) { mnc := int2hex(mnc_uint, 2); return hex2oct(mcc[1] & mcc[0] & 'F'H & mcc[2] & mnc[1] & mnc[0]); } else { mnc := int2hex(mnc_uint, 3); return hex2oct(mcc[1] & mcc[0] & mnc[2] & mcc[2] & mnc[1] & mnc[0]); } } function f_handle_hnbap_hnb_register_req() runs on HNBGW_ConnHdlr { HNBAP.receive(tr_HNBAP_HNBRegisterRequest(char2oct(g_pars.hNB_Identity_Info), f_enc_mcc_mnc(g_pars.mcc, g_pars.mnc), 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)); } /* Initialize and start the RTP emulation component for a ConnHdlr */ function f_HNBGW_rtpem_activate(inout octetstring payload) runs on HNBGW_ConnHdlr { /* Initialize, connect and start the emulation component */ var RtpemConfig cfg := c_RtpemDefaultCfg; cfg.iuup_mode := true; cfg.iuup_cfg.active_init := false; cfg.tx_payloads[0].payload_type := 96; cfg.rx_payloads[0].payload_type := 96; vc_RTPEM := RTP_Emulation_CT.create(testcasename() & "-RTPEM") alive; map(vc_RTPEM:RTP, system:RTP); map(vc_RTPEM:RTCP, system:RTCP); connect(vc_RTPEM:CTRL, self:RTPEM_CTRL); connect(vc_RTPEM:DATA, self:RTPEM_DATA); vc_RTPEM.start(RTP_Emulation.f_main()); /* Configure the RTP parameters (TCH/FS). TODO: IuUP */ var integer payload_len := 33; var octetstring hdr := 'D0'O; /* Pad the payload to conform the expected length */ payload := f_pad_oct(hdr & payload, payload_len, '00'O); cfg.tx_payloads[0].fixed_payload := payload; f_rtpem_configure(RTPEM_CTRL, cfg); /* Bind the RTP emulation to the configured address */ f_rtpem_bind(RTPEM_CTRL, g_pars.hnbgw_addr, g_pars.hnbgw_rtp_port); /* Set the given RTP emulation mode */ f_rtpem_mode(RTPEM_CTRL, RTPEM_MODE_RXONLY); } function f_HNBGW_rtpem_connect(HostName remote_host, PortNumber remote_port) runs on HNBGW_ConnHdlr { f_rtpem_connect(RTPEM_CTRL, remote_host, remote_port); /* Set the given RTP emulation mode */ f_rtpem_mode(RTPEM_CTRL, RTPEM_MODE_BIDIR); } }