module IPA_Emulation { /* This module implements the IPA multiplex protocol on top of TCP, using the IPL4asp * test-port as provider. It implements both client and server roles, as well was the CCM * handshake for establishing the identity of the client to the server. * * It already knows certain well-known sub-protocols such as A-bis RSL, MGCP and SCCP and * GSUP. IT hence transcodes messages so the user can work with abstract data types rather * than binary messages. It handles multiple packets inside one TCP segment. * * (C) 2017-2019 by Harald Welte * contributions 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 IPA_Types all; import from IPA_CodecPort all; import from IPA_CodecPort_CtrlFunct all; import from IPL4asp_Types all; import from IPL4asp_PortType all; import from Socket_API_Definitions all; #ifdef IPA_EMULATION_SCCP import from MTP3asp_Types all; import from MTP3asp_PortType all; #endif #ifdef IPA_EMULATION_RSL import from RSL_Types all; #endif #ifdef IPA_EMULATION_OML import from AbisOML_Types all; #endif #ifdef IPA_EMULATION_MGCP import from MGCP_Types all; #endif #ifdef IPA_EMULATION_GSUP import from GSUP_Types all; import from GSUP_Templates all; #endif #ifdef IPA_EMULATION_RSPRO import from RSPRO all; import from RSPRO_Types all; #endif #ifdef IPA_EMULATION_CTRL import from Osmocom_CTRL_Types all; #endif #ifdef IPA_EMULATION_OSMO_PCU import from PCUIF_Types all; #endif modulepar { /* Use Osmocom extended IPA mux header */ boolean mp_ipa_mgcp_uses_osmo_ext := true; } type enumerated IpaMode { IPA_MODE_CLIENT, IPA_MODE_SERVER } type enumerated IpaInitBehavior { IPA_INIT_SEND_IPA_ID_GET, IPA_INIT_SEND_IPA_ID_ACK } type record ASP_IPA_Unitdata { IpaStreamId streamId, IpaExtStreamId streamIdExt optional, octetstring payload } type enumerated ASP_IPA_EventType { ASP_IPA_EVENT_DOWN, ASP_IPA_EVENT_UP, ASP_IPA_EVENT_ID_RESP, ASP_IPA_EVENT_ID_ACK } /* an event indicating us whether or not a connection is physically up or down, * and whether we have received an ID_RESP or ID_ACK */ type record ASP_IPA_Event { ASP_IPA_EventType ev_type, integer conn_id, /* Presence of these fields depends on event type */ IpaCcmIdResp id_resp optional // ASP_IPA_EVENT_ID_RESP } template (value) ASP_IPA_Event ts_ASP_IPA_EV(ASP_IPA_EventType ev_type, integer conn_id, template (omit) IpaCcmIdResp id_resp := omit) := { ev_type := ev_type, conn_id := conn_id, id_resp := id_resp } template ASP_IPA_Event tr_ASP_IPA_EV(template ASP_IPA_EventType ev_type, template integer conn_id := ?, template IpaCcmIdResp id_resp := *) := { ev_type := ev_type, conn_id := conn_id, id_resp := id_resp } template ASP_IPA_Unitdata t_ASP_IPA_UD(IpaStreamId sid, octetstring pl, template IpaExtStreamId esid := omit) := { streamId := sid, streamIdExt := esid, payload := pl } #ifdef IPA_EMULATION_RSL /* like ASP_IPA_Unitdata, but with RSL_Message abstract type instead of octetstring */ type record ASP_RSL_Unitdata { integer conn_id optional, IpaStreamId streamId, RSL_Message rsl }; template (value) ASP_RSL_Unitdata ts_ASP_RSL_UD(template (value) RSL_Message rsl, IpaStreamId sid := IPAC_PROTO_RSL_TRX0, template (omit) integer conn_id := omit) := { conn_id := conn_id, streamId := sid, rsl := rsl } template ASP_RSL_Unitdata tr_ASP_RSL_UD(template (present) RSL_Message rsl, template IpaStreamId sid := IPAC_PROTO_RSL_TRX0, template integer conn_id := *) := { conn_id := conn_id, streamId := sid, rsl := rsl } template IpaStreamId t_IpaSidRSL := ( IPAC_PROTO_RSL_TRX0, IPAC_PROTO_RSL_TRX1, IPAC_PROTO_RSL_TRX2, IPAC_PROTO_RSL_TRX3 ); #endif /* Client port for general IPA messages, not further decoded */ type port IPA_SP_PT message { inout ASP_IPA_Unitdata, ASP_IPA_Event; } with { extension "internal" } #ifdef IPA_EMULATION_MGCP /* Client port for MGCP inside IPA */ type port IPA_MGCP_PT message { inout MgcpCommand, MgcpResponse; } with { extension "internal" } #endif #ifdef IPA_EMULATION_RSL /* Client port for A-bis RSL inside IPA */ type port IPA_RSL_PT message { inout ASP_RSL_Unitdata, ASP_IPA_Event; } with { extension "internal" } #endif #ifdef IPA_EMULATION_OML /* Client port for A-bis OML inside IPA */ type port IPA_OML_PT message { inout OML_PDU, octetstring, ASP_IPA_Event; } with { extension "internal" } #endif #ifdef IPA_EMULATION_CTRL /* Client port for CTRL inside IPA */ type port IPA_CTRL_PT message { inout CtrlMessage, ASP_IPA_Event; } with { extension "internal" } #endif #ifdef IPA_EMULATION_GSUP /* Client port for CTRL inside IPA */ type port IPA_GSUP_PT message { inout GSUP_PDU, ASP_IPA_Event; } with { extension "internal" } #endif #ifdef IPA_EMULATION_RSPRO type port IPA_RSPRO_PT message { inout RsproPDU, ASP_IPA_Event; } with { extension "internal" } #endif #ifdef IPA_EMULATION_OSMO_PCU /* Client port for Osmocom PCU extension inside IPA */ type port IPA_OSMO_PCU_PT message { inout PCUIF_Message, ASP_IPA_Event; } with { extension "internal" } #endif type component IPA_Emulation_CT { /* down-facing port to IPA codec port */ port IPA_CODEC_PT IPA_PORT; /* Down facing port to configure the component */ port IPA_CFG_PT CFG_PORT; #ifdef IPA_EMULATION_SCCP /* up-facing port to SCCP */ port MTP3asp_SP_PT MTP3_SP_PORT; #endif #ifdef IPA_EMULATION_MGCP /* up-facing port for MGCP */ port IPA_MGCP_PT IPA_MGCP_PORT; #endif #ifdef IPA_EMULATION_RSL /* up-facing port for RSL */ port IPA_RSL_PT IPA_RSL_PORT; #endif #ifdef IPA_EMULATION_OML /* up-facing port for OML */ port IPA_OML_PT IPA_OML_PORT; #endif #ifdef IPA_EMULATION_CTRL /* up-facing port for CTRL */ port IPA_CTRL_PT IPA_CTRL_PORT; #endif #ifdef IPA_EMULATION_GSUP /* up-facing port for GSUP */ port IPA_GSUP_PT IPA_GSUP_PORT; #endif #ifdef IPA_EMULATION_RSPRO /* up-facing port for RSPRO */ port IPA_RSPRO_PT IPA_RSPRO_PORT; #endif #ifdef IPA_EMULATION_OSMO_PCU /* up-facing port for RSPRO */ port IPA_OSMO_PCU_PT IPA_OSMO_PCU_PORT; #endif /* up-facing port for other streams */ port IPA_SP_PT IPA_SP_PORT; var boolean g_initialized := false; /* Are we a BSC/MGW (truel) or MSC (false) */ var boolean g_is_bsc_mgw; /* Connection identifier of the client / server itself */ var IPL4asp_Types.ConnectionId g_self_conn_id := -1; /* Connection identifier of the last connected client (server only) */ var IPL4asp_Types.ConnectionId g_last_conn_id := -1; var IpaMode g_mode; var boolean g_ccm_enabled; var IpaInitBehavior g_init_behavior; /* Shall we stop the component once the client disconnects, or keep * running to let next user in ? */ var boolean g_server_stop_on_ipa_ev_down := true; var IPA_CCM_Parameters g_ccm_pars := c_IPA_default_ccm_pars; } type record IPA_CCM_Parameters { charstring ser_nr optional, charstring name optional, charstring location1 optional, charstring location2 optional, charstring equip_version optional, charstring sw_version optional, charstring ip_addr optional, charstring mac_addr optional, charstring unit_id optional, charstring osmo_rand optional } const IPA_CCM_Parameters c_IPA_default_ccm_pars := { ser_nr := "", name := "mahlzeit", location1 := "", location2 := "", equip_version := "", sw_version := "", ip_addr := "", mac_addr := "", unit_id := "0/1/2", osmo_rand := "" }; signature IPA_CFG_disconnect(inout IPL4asp_Types.Result res); type port IPA_CFG_PT procedure { inout IPA_CFG_disconnect; } with { extension "internal" }; function f_ipa_cfg_disconnect(IPA_CFG_PT pt, inout IPL4asp_Types.Result res) { pt.call(IPA_CFG_disconnect:{res}) { [] pt.getreply(IPA_CFG_disconnect:{?}) -> param (res) {}; } } /* Function to use to connect as client to a remote IPA Server */ function f_connect(charstring remote_host, IPL4asp_Types.PortNumber remote_port, charstring local_host, IPL4asp_Types.PortNumber local_port, IPA_CCM_Parameters ccm_pars := c_IPA_default_ccm_pars) runs on IPA_Emulation_CT { var IPL4asp_Types.Result res; res := IPA_CodecPort_CtrlFunct.f_IPL4_connect(IPA_PORT, remote_host, remote_port, local_host, local_port, 0, { tcp:={} }); if (not ispresent(res.connId)) { setverdict(fail, "Could not connect IPA socket from ", local_host, " port ", local_port, " to ", remote_host, " port ", remote_port, "; check your configuration"); mtc.stop; } g_self_conn_id := res.connId; g_ccm_pars := ccm_pars; g_is_bsc_mgw := true; } /* Function to use to bind to a local port as IPA server, accepting remote clients */ function f_bind(charstring local_host, IPL4asp_Types.PortNumber local_port, IPA_CCM_Parameters ccm_pars := c_IPA_default_ccm_pars) runs on IPA_Emulation_CT { var IPL4asp_Types.Result res; res := IPA_CodecPort_CtrlFunct.f_IPL4_listen(IPA_PORT, local_host, local_port, { tcp:={} }); if (not ispresent(res.connId)) { setverdict(fail, "Could not listen IPA socket ", local_host, ":", local_port, ", check your configuration"); mtc.stop; } g_self_conn_id := res.connId; g_ccm_pars := ccm_pars; g_is_bsc_mgw := false; } private function f_close() runs on IPA_Emulation_CT return IPL4asp_Types.Result { var IPL4asp_Types.Result res; select (g_mode) { case (IPA_MODE_CLIENT) { res := IPA_CodecPort_CtrlFunct.f_IPL4_close(IPA_PORT, g_self_conn_id, {tcp := {}}); g_self_conn_id := -1; } case (IPA_MODE_SERVER) { res := IPA_CodecPort_CtrlFunct.f_IPL4_close(IPA_PORT, g_last_conn_id, {tcp := {}}); g_last_conn_id := -1; } case else { setverdict(fail, "Unknown mode"); mtc.stop; } } return res; } #ifdef IPA_EMULATION_SCCP template ASP_MTP3_TRANSFERind ts_MTP3_XFER_ind(integer opc, octetstring data) := { sio := { '10'B, '00'B, '0011'B }, opc := opc, dpc := 0, sls := 0, data := data } #endif private template IpaCcmRespPart t_IdRespPart(IpaCcmIdTag tag, charstring payload) := { len := 0, /* overwritten by codec */ tag := tag, data := char2oct(payload) & '00'O } private function f_send_IPA_EVT(template (value) ASP_IPA_Event evt) runs on IPA_Emulation_CT { if (IPA_SP_PORT.checkstate("Connected")) { IPA_SP_PORT.send(evt); } #ifdef IPA_EMULATION_RSL if (IPA_RSL_PORT.checkstate("Connected")) { IPA_RSL_PORT.send(evt); } #endif #ifdef IPA_EMULATION_OML if (IPA_OML_PORT.checkstate("Connected")) { IPA_OML_PORT.send(evt); } #endif #ifdef IPA_EMULATION_CTRL if (IPA_CTRL_PORT.checkstate("Connected")) { IPA_CTRL_PORT.send(evt); } #endif #ifdef IPA_EMULATION_GSUP if (IPA_GSUP_PORT.checkstate("Connected")) { IPA_GSUP_PORT.send(evt); } #endif #ifdef IPA_EMULATION_RSPRO if (IPA_RSPRO_PORT.checkstate("Connected")) { IPA_RSPRO_PORT.send(evt); } #endif #ifdef IPA_EMULATION_OSMO_PCU if (IPA_OSMO_PCU_PORT.checkstate("Connected")) { IPA_OSMO_PCU_PORT.send(evt); } #endif /* FIXME: to other ports */ } /* build IPA CCM ID RESP response from IPA CCM GET */ private function f_ccm_make_id_resp(PDU_IPA_CCM get) runs on IPA_Emulation_CT return PDU_IPA_CCM { var integer i; var PDU_IPA_CCM resp := { msg_type := IPAC_MSGT_ID_RESP, u := { resp := {} } } /* If no Tags were present in the received IPA message, .u = omit */ if (not ispresent(get.u)) { return resp; } for (i := 0; i < sizeof(get.u.get); i := i + 1) { var IpaCcmIdTag tag := get.u.get[i].tag; var charstring foo; select (tag) { case (IPAC_IDTAG_SERNR) { foo := g_ccm_pars.ser_nr; } case (IPAC_IDTAG_UNITNAME) { foo := g_ccm_pars.name; } case (IPAC_IDTAG_LOCATION1) { foo := g_ccm_pars.location1; } case (IPAC_IDTAG_LOCATION2) { foo := g_ccm_pars.location2; } case (IPAC_IDTAG_EQUIPVERS) { foo := g_ccm_pars.equip_version; } case (IPAC_IDTAG_SWVERSION) { foo := g_ccm_pars.sw_version; } case (IPAC_IDTAG_IPADDR) { foo := g_ccm_pars.ip_addr; } case (IPAC_IDTAG_MACADDR) { foo := g_ccm_pars.mac_addr; } case (IPAC_IDTAG_UNITID) { foo := g_ccm_pars.unit_id; } case (IPAC_IDTAG_OSMO_RAND) { foo := g_ccm_pars.osmo_rand; } case else { foo := "unknown"; } } resp.u.resp[sizeof(resp.u.resp)] := valueof(t_IdRespPart(tag, foo)); } return resp; } /* transmit IPA CCM message */ private function f_ccm_tx(PDU_IPA_CCM ccm, IPL4asp_Types.ConnectionId conn_id) runs on IPA_Emulation_CT { var IPA_Send ipa_tx := valueof(t_IPA_Send(conn_id, IPAC_PROTO_CCM, enc_PDU_IPA_CCM(ccm))); log("CCM Tx:", ccm); IPA_PORT.send(ipa_tx); } template PDU_IPA_CCM ts_IPA_PONG := { msg_type := IPAC_MSGT_PONG, u := omit } template PDU_IPA_CCM ts_IPA_ACK := { msg_type := IPAC_MSGT_ID_ACK, u := omit } template PDU_IPA_CCM ts_IPA_ID_GET := { msg_type := IPAC_MSGT_ID_GET, u := { get := { { 1, IPAC_IDTAG_UNITNAME }, { 1, IPAC_IDTAG_UNITID } } } } template PDU_IPA_CCM tr_IPA_ID_RESP := { msg_type := IPAC_MSGT_ID_RESP, u := { resp := { { ?, IPAC_IDTAG_UNITNAME, ? }, { ?, IPAC_IDTAG_UNITID, ? } } } } /* receive IPA CCM message */ private function f_ccm_rx_client(PDU_IPA_CCM ccm) runs on IPA_Emulation_CT { select (ccm.msg_type) { case (IPAC_MSGT_PING) { f_ccm_tx(valueof(ts_IPA_PONG), g_self_conn_id); } case (IPAC_MSGT_ID_ACK) { f_send_IPA_EVT(ts_ASP_IPA_EV(ASP_IPA_EVENT_ID_ACK, g_self_conn_id)); } case (IPAC_MSGT_ID_GET) { f_ccm_tx(f_ccm_make_id_resp(ccm), g_self_conn_id); } case else { log("Unknown/unsupported IPA CCM message type", ccm); } } } private function f_ccm_rx_server(PDU_IPA_CCM ccm, IPL4asp_Types.ConnectionId conn_id) runs on IPA_Emulation_CT { select (ccm.msg_type) { case (IPAC_MSGT_PING) { f_ccm_tx(valueof(ts_IPA_PONG), conn_id); } case (IPAC_MSGT_ID_ACK) { /* the IPA server should at some point receive an ID_ACK from the client, * in case of RSL/OML from nanoBTS, this is actually the first message after * the TCP connection is established. Other implementations may differ. * We currently ignore it completely - but actually we should make sure that * one ID_ACK is received by the server at some point */ f_send_IPA_EVT(ts_ASP_IPA_EV(ASP_IPA_EVENT_ID_ACK, conn_id)); } case (IPAC_MSGT_ID_RESP) { log("IPA ID RESP: ", ccm.u.resp); /* make sure that the response contains all fields we requested */ if (not match(ccm, tr_IPA_ID_RESP)) { log("IPA identity response ", ccm.u.resp, " mismatch"); return; } /* forward to the upper layers, so they can map conn_id with unit_id */ f_send_IPA_EVT(ts_ASP_IPA_EV(ASP_IPA_EVENT_ID_RESP, conn_id, ccm.u.resp)); /* acknowledge any identity that the client may have sent */ f_ccm_tx(valueof(ts_IPA_ACK), conn_id); } case else { log("Unknown/unsupported IPA CCM message type", ccm); } } } private function f_to_asp(IPA_RecvFrom ipa_rx) return ASP_IPA_Unitdata { var ASP_IPA_Unitdata ret := { streamId := ipa_rx.streamId, streamIdExt := ipa_rx.streamIdExt, payload := ipa_rx.msg } return ret; } private function f_from_asp(IPL4asp_Types.ConnectionId connId, ASP_IPA_Unitdata ipa_tx) return IPA_Send { var IPA_Send ret := valueof(t_IPA_Send(connId, ipa_tx.streamId, ipa_tx.payload, ipa_tx.streamIdExt)); return ret; } #ifdef IPA_EMULATION_RSL private function f_from_rsl(IPL4asp_Types.ConnectionId connId, ASP_RSL_Unitdata rsl_tx) return IPA_Send { var octetstring payload := enc_RSL_Message(rsl_tx.rsl); /* ASP_RSL_Unitdata may (optionally) contain TCP/IP connection ID */ if (rsl_tx.conn_id != omit) { connId := rsl_tx.conn_id; } return valueof(t_IPA_Send(connId, rsl_tx.streamId, payload)); } #endif #ifdef IPA_EMULATION_OML private function f_from_oml(IPL4asp_Types.ConnectionId connId, OML_PDU oml_tx) return IPA_Send { var octetstring payload := enc_OML_PDU(oml_tx); var IPA_Send ret := valueof(t_IPA_Send(connId, IPAC_PROTO_OML, payload)); return ret; } #endif /* main function to use for a client-side IPA implementation */ function main_client(charstring remote_host, IPL4asp_Types.PortNumber remote_port, charstring local_host, IPL4asp_Types.PortNumber local_port, IPA_CCM_Parameters ccm_pars := c_IPA_default_ccm_pars, boolean ccm_enabled := true) runs on IPA_Emulation_CT { g_mode := IPA_MODE_CLIENT; g_ccm_enabled := ccm_enabled; f_connect(remote_host, remote_port, local_host, local_port, ccm_pars); if (g_ccm_enabled) { /* we're a client: Send ID_ACK immediately after connect */ f_ccm_tx(valueof(ts_IPA_ACK), g_self_conn_id); } f_send_IPA_EVT(ts_ASP_IPA_EV(ASP_IPA_EVENT_UP, g_self_conn_id)); ScanEvents(); } /* main function to use for a server-side IPA implementation */ function main_server(charstring local_host, IPL4asp_Types.PortNumber local_port, boolean ccm_enabled := true, IpaInitBehavior init_behavior := IPA_INIT_SEND_IPA_ID_GET, boolean server_stop_on_ipa_ev_down := true) runs on IPA_Emulation_CT { g_mode := IPA_MODE_SERVER; g_ccm_enabled := ccm_enabled; g_init_behavior := init_behavior; g_server_stop_on_ipa_ev_down := server_stop_on_ipa_ev_down; f_bind(local_host, local_port); ScanEvents(); } #ifdef IPA_EMULATION_CTRL private function f_ctrl_to_user(octetstring msg) runs on IPA_Emulation_CT { var charstring msg_ch := oct2char(msg); IPA_CTRL_PORT.send(dec_CtrlMessage(msg_ch)); } #endif #ifdef IPA_EMULATION_GSUP private function f_gsup_to_user(octetstring msg) runs on IPA_Emulation_CT { var GSUP_PDU gsup := dec_GSUP_PDU(msg); f_gsup_postprocess_decoded(gsup); IPA_GSUP_PORT.send(gsup); } #endif #ifdef IPA_EMULATION_RSPRO private function f_rspro_to_user(octetstring msg) runs on IPA_Emulation_CT { var RsproPDU rspro := dec_RsproPDU(msg); IPA_RSPRO_PORT.send(rspro); } #endif #ifdef IPA_EMULATION_OSMO_PCU private function f_osmo_pcu_to_user(octetstring msg) runs on IPA_Emulation_CT { var PCUIF_Message pcuif_msg := dec_PCUIF_Message(msg); IPA_OSMO_PCU_PORT.send(pcuif_msg); } #endif #ifdef IPA_EMULATION_MGCP private function f_mgcp_to_user(octetstring msg) runs on IPA_Emulation_CT { var charstring msg_ch := oct2char(msg); if (g_is_bsc_mgw) { log("============"); log(msg_ch); IPA_MGCP_PORT.send(dec_MgcpCommand(msg_ch)); } else { IPA_MGCP_PORT.send(dec_MgcpResponse(msg_ch)); } } private function f_mgcp_to_ud(octetstring payload) runs on IPA_Emulation_CT return ASP_IPA_Unitdata { if (mp_ipa_mgcp_uses_osmo_ext) { return valueof(t_ASP_IPA_UD(IPAC_PROTO_MGCP_OLD, payload)); } else { return valueof(t_ASP_IPA_UD(IPAC_PROTO_OSMO, payload, IPAC_PROTO_EXT_MGCP)); } } #endif /* Resolve TCP/IP connection identifier depending on g_mode */ private function f_ipa_conn_id() runs on IPA_Emulation_CT return IPL4asp_Types.ConnectionId { var IPL4asp_Types.ConnectionId conn_id; select (g_mode) { case (IPA_MODE_CLIENT) { conn_id := g_self_conn_id; } case (IPA_MODE_SERVER) { conn_id := g_last_conn_id; } case else { setverdict(fail, "Unknown mode"); mtc.stop; } } if (conn_id == -1) { /* Just to be sure */ setverdict(fail, "Connection is not established"); mtc.stop; } return conn_id; } /* main loop function for both client and server. 'thread' of the component */ private function ScanEvents() runs on IPA_Emulation_CT { var IPA_RecvFrom ipa_rx; var ASP_Event asp_evt; var Socket_API_Definitions.PortEvent port_evt; var octetstring payload; var ASP_IPA_Unitdata ipa_ud; var IPL4asp_Types.Result res; #ifdef IPA_EMULATION_CTRL var CtrlMessage ctrl_msg; #endif #ifdef IPA_EMULATION_SCCP var ASP_MTP3_TRANSFERreq mtp_req; #endif #ifdef IPA_EMULATION_MGCP var MgcpCommand mgcp_cmd; var MgcpResponse mgcp_rsp; #endif #ifdef IPA_EMULATION_GSUP var GSUP_PDU gsup_msg; #endif #ifdef IPA_EMULATION_RSL var ASP_RSL_Unitdata rsl; #endif #ifdef IPA_EMULATION_OML var OML_PDU oml; #endif #ifdef IPA_EMULATION_RSPRO var RsproPDU rspro; #endif #ifdef IPA_EMULATION_OSMO_PCU var PCUIF_Message pcu; #endif /* Set function for dissecting the binary */ var f_IPL4_getMsgLen vl_f := refers(f_IPL4_fixedMsgLen); IPA_CodecPort_CtrlFunct.f_IPL4_setGetMsgLen(IPA_PORT, g_self_conn_id, vl_f, {0, 2, 3, 1, 0}); while (true) { alt { /* Received IPA -> up into SCCP stack */ [g_ccm_enabled] IPA_PORT.receive(IPA_RecvFrom:{?,IPAC_PROTO_CCM,omit,?}) -> value ipa_rx { var PDU_IPA_CCM ccm := dec_PDU_IPA_CCM(ipa_rx.msg); log("CCM Rx:", ccm); select (g_mode) { case (IPA_MODE_CLIENT) { f_ccm_rx_client(ccm); } case (IPA_MODE_SERVER) { f_ccm_rx_server(ccm, ipa_rx.connId); } case else { setverdict(fail, "Unknown mode"); mtc.stop; } } } [] IPA_PORT.receive(IPA_RecvFrom:?) -> value ipa_rx { select (ipa_rx.streamId) { #ifdef IPA_EMULATION_SCCP case (IPAC_PROTO_SCCP) { var ASP_MTP3_TRANSFERind mtp; mtp := valueof(ts_MTP3_XFER_ind(0, ipa_rx.msg)); MTP3_SP_PORT.send(mtp); } #endif #ifdef IPA_EMULATION_MGCP case (IPAC_PROTO_MGCP_OLD) { f_mgcp_to_user(ipa_rx.msg); } #endif #ifdef IPA_EMULATION_RSL case (t_IpaSidRSL) { rsl := valueof(ts_ASP_RSL_UD( rsl := dec_RSL_Message(ipa_rx.msg), sid := ipa_rx.streamId, conn_id := ipa_rx.connId )); IPA_RSL_PORT.send(rsl); } #endif #ifdef IPA_EMULATION_OML case (IPAC_PROTO_OML) { oml := dec_OML_PDU(ipa_rx.msg) IPA_OML_PORT.send(oml); } #endif case (IPAC_PROTO_OSMO) { select (ipa_rx.streamIdExt) { #ifdef IPA_EMULATION_MGCP case (IPAC_PROTO_EXT_MGCP) { f_mgcp_to_user(ipa_rx.msg); } #endif #ifdef IPA_EMULATION_CTRL case (IPAC_PROTO_EXT_CTRL) { f_ctrl_to_user(ipa_rx.msg); } #endif #ifdef IPA_EMULATION_GSUP case (IPAC_PROTO_EXT_GSUP) { f_gsup_to_user(ipa_rx.msg); } #endif #ifdef IPA_EMULATION_RSPRO case (IPAC_PROTO_EXT_RSPRO) { f_rspro_to_user(ipa_rx.msg); } #endif #ifdef IPA_EMULATION_OSMO_PCU case (IPAC_PROTO_EXT_OSMO_PCU) { f_osmo_pcu_to_user(ipa_rx.msg); } #endif case else { IPA_SP_PORT.send(f_to_asp(ipa_rx)); } } } case else { IPA_SP_PORT.send(f_to_asp(ipa_rx)); } } } /* server only */ [] IPA_PORT.receive(ASP_Event:{connOpened:=?}) -> value asp_evt { g_last_conn_id := asp_evt.connOpened.connId; log("Established a new IPA connection (conn_id=", g_last_conn_id, ")"); f_send_IPA_EVT(ts_ASP_IPA_EV(ASP_IPA_EVENT_UP, g_last_conn_id)); if (g_mode == IPA_MODE_SERVER and g_ccm_enabled) { select (g_init_behavior) { case (IPA_INIT_SEND_IPA_ID_GET) { f_ccm_tx(valueof(ts_IPA_ID_GET), g_last_conn_id); } case (IPA_INIT_SEND_IPA_ID_ACK) { f_ccm_tx(valueof(ts_IPA_ACK), g_last_conn_id); } } } } [] IPA_PORT.receive(ASP_Event:{connClosed:=?}) -> value asp_evt { log("IPA: Closed"); g_self_conn_id := -1; f_send_IPA_EVT(ts_ASP_IPA_EV(ASP_IPA_EVENT_DOWN, asp_evt.connClosed.connId)); if (g_mode != IPA_MODE_SERVER or g_server_stop_on_ipa_ev_down) { self.stop; } } [] IPA_PORT.receive(Socket_API_Definitions.PortEvent:{result:={errorCode:=ERROR_SOCKET, connId:=?, os_error_code:=?, os_error_text:=?}}) -> value port_evt { log("PortEvent: ERROR_SOCKET: ", port_evt); g_self_conn_id := -1; f_send_IPA_EVT(ts_ASP_IPA_EV(ASP_IPA_EVENT_DOWN, port_evt.result.connId)); self.stop; } #ifdef IPA_EMULATION_SCCP /* Received SCCP -> down into IPA */ [] MTP3_SP_PORT.receive(ASP_MTP3_TRANSFERreq: ?) -> value mtp_req { var IPA_Send ipa_tx := valueof(t_IPA_Send(f_ipa_conn_id(), IPAC_PROTO_SCCP, mtp_req.data)); IPA_PORT.send(ipa_tx); } #endif #ifdef IPA_EMULATION_MGCP /* Received MGCP -> down into IPA */ [] IPA_MGCP_PORT.receive(MgcpCommand:?) -> value mgcp_cmd { payload := char2oct(enc_MgcpCommand(mgcp_cmd)); ipa_ud := f_mgcp_to_ud(payload); IPA_PORT.send(f_from_asp(f_ipa_conn_id(), ipa_ud)); } [] IPA_MGCP_PORT.receive(MgcpResponse:?) -> value mgcp_rsp { payload := char2oct(enc_MgcpResponse(mgcp_rsp)); ipa_ud := f_mgcp_to_ud(payload); IPA_PORT.send(f_from_asp(f_ipa_conn_id(), ipa_ud)); } #endif #ifdef IPA_EMULATION_CTRL [] IPA_CTRL_PORT.receive(CtrlMessage:?) -> value ctrl_msg { payload := char2oct(enc_CtrlMessage(ctrl_msg)); ipa_ud := valueof(t_ASP_IPA_UD(IPAC_PROTO_OSMO, payload, IPAC_PROTO_EXT_CTRL)); IPA_PORT.send(f_from_asp(f_ipa_conn_id(), ipa_ud)); } #endif #ifdef IPA_EMULATION_GSUP [] IPA_GSUP_PORT.receive(GSUP_PDU:?) -> value gsup_msg { f_gsup_preprocess_encoded(gsup_msg); payload := enc_GSUP_PDU(gsup_msg); ipa_ud := valueof(t_ASP_IPA_UD(IPAC_PROTO_OSMO, payload, IPAC_PROTO_EXT_GSUP)); IPA_PORT.send(f_from_asp(f_ipa_conn_id(), ipa_ud)); } #endif #ifdef IPA_EMULATION_RSPRO [] IPA_RSPRO_PORT.receive(RsproPDU:?) -> value rspro { payload := enc_RsproPDU(rspro); ipa_ud := valueof(t_ASP_IPA_UD(IPAC_PROTO_OSMO, payload, IPAC_PROTO_EXT_RSPRO)); IPA_PORT.send(f_from_asp(f_ipa_conn_id(), ipa_ud)); } #endif #ifdef IPA_EMULATION_OSMO_PCU [] IPA_OSMO_PCU_PORT.receive(PCUIF_Message:?) -> value pcu { payload := enc_PCUIF_Message(pcu); ipa_ud := valueof(t_ASP_IPA_UD(IPAC_PROTO_OSMO, payload, IPAC_PROTO_EXT_OSMO_PCU)); IPA_PORT.send(f_from_asp(f_ipa_conn_id(), ipa_ud)); } #endif #ifdef IPA_EMULATION_RSL /* Received RSL -> down into IPA */ [] IPA_RSL_PORT.receive(ASP_RSL_Unitdata:?) -> value rsl { IPA_PORT.send(f_from_rsl(f_ipa_conn_id(), rsl)); } #endif #ifdef IPA_EMULATION_OML /* Received OML -> down into IPA */ [] IPA_OML_PORT.receive(OML_PDU:?) -> value oml { IPA_PORT.send(f_from_oml(f_ipa_conn_id(), oml)); } [] IPA_OML_PORT.receive(octetstring:?) -> value payload { IPA_PORT.send(t_IPA_Send(f_ipa_conn_id(), IPAC_PROTO_OML, payload)); } #endif /* Received MISC (OML/CTRL) -> down into IPA */ [] IPA_SP_PORT.receive(ASP_IPA_Unitdata: ?) -> value ipa_ud { IPA_PORT.send(f_from_asp(f_ipa_conn_id(), ipa_ud)); } /* Received call to configure/operate the component */ [] CFG_PORT.getcall(IPA_CFG_disconnect:{?}) { res := f_close(); CFG_PORT.reply(IPA_CFG_disconnect:{res}); } } } } /*********************************************************************** * IPA Event waiter component. Wait for ASP_IPA_EVENT_ID_ACK ***********************************************************************/ type component IPA_EventWaiter_CT { port IPA_SP_PT IPA_SP_PORT; } function waiter_main(template ASP_IPA_Event wait_for := tr_ASP_IPA_EV(ASP_IPA_EVENT_ID_ACK)) runs on IPA_EventWaiter_CT { alt { [] IPA_SP_PORT.receive(wait_for) { setverdict(pass); } [] IPA_SP_PORT.receive { repeat; } } } }