| 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 <info@sysmocom.de> |
| * Author: Pau Espin Pedrol <pespin@sysmocom.de> |
| * 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; |
| |
| /* General "base class" component definition, of which specific implementations |
| * derive themselves by means of the "extends" feature */ |
| type component Iuh_ConnHdlr { |
| port HNBAP_PT HNBAP; |
| port RUA_PT RUA; |
| }; |
| |
| 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; |
| } |
| |
| private function f_send_IUHEM_Event(template (value) IUHEM_Event evt) runs on Iuh_Emulation_CT { |
| if (HNBAP.checkstate("Connected")) { |
| HNBAP.send(evt); |
| } |
| } |
| |
| 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) { |
| f_send_IUHEM_Event(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, ")"); |
| |
| f_send_IUHEM_Event(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; |
| f_send_IUHEM_Event(IUHEM_Event:{up_down:=IUHEM_EVENT_DOWN}); /* TODO: send asp_evt.connClosed.connId */ |
| if (not emu_is_server()) { |
| self.stop; |
| } |
| } |
| } |
| } |
| } |
| |
| } |