diff options
author | Harald Welte <laforge@gnumonks.org> | 2018-02-20 15:49:30 +0100 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2018-02-20 16:17:17 +0100 |
commit | df277258f1d5fbf8ce80c2748c4fb0783359790f (patch) | |
tree | ffcde8d196de3de575c8e4a5ccc9d59a17cab587 /library/IPA_Emulation.ttcnpp | |
parent | 9abd1289ec612e7c2f32b1110da3901a3135c8c0 (diff) |
IPA_Emulation: Make dependencies to RSL/MGCP/SCCP/GSUP conditional
Let's use the preprocessor to avoid IPA_Emulation pulling *all*
dependencies into each and any of our projects. The code readability
suffers a bit from the many #ifdefs, but compilation speed increases
if we don't have to pull in all those (recursive) dependencies.
After all, a BTS test case will never need SCCP, GSUP or MGCP.
Change-Id: Ic0231adbd2171214de133d26b3fbf36130ee8aa0
Diffstat (limited to 'library/IPA_Emulation.ttcnpp')
-rw-r--r-- | library/IPA_Emulation.ttcnpp | 587 |
1 files changed, 587 insertions, 0 deletions
diff --git a/library/IPA_Emulation.ttcnpp b/library/IPA_Emulation.ttcnpp new file mode 100644 index 00000000..3508091b --- /dev/null +++ b/library/IPA_Emulation.ttcnpp @@ -0,0 +1,587 @@ +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 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 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_MGCP +import from MGCP_Types all; +#endif + +#ifdef IPA_EMULATION_GSUP +import from GSUP_Types all; +#endif + +import from Osmocom_CTRL_Types all; + +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 record ASP_IPA_Unitdata { + IpaStreamId streamId, + IpaExtStreamId streamIdExt optional, + octetstring payload +} + +type enumerated ASP_IPA_EventUpDown { + ASP_IPA_EVENT_DOWN, + ASP_IPA_EVENT_UP, + 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_ACK */ +type union ASP_IPA_Event { + ASP_IPA_EventUpDown up_down +} + +template ASP_IPA_Event t_ASP_IPA_EVT_UD(ASP_IPA_EventUpDown ud) := { + up_down := ud +} + +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 { + IpaStreamId streamId, + RSL_Message rsl +}; + +template ASP_RSL_Unitdata ts_ASP_RSL_UD(IpaStreamId sid, template RSL_Message rsl) := { + streamId := sid, + rsl := valueof(rsl) +} + +template ASP_RSL_Unitdata tr_ASP_RSL_UD(IpaStreamId sid, template RSL_Message rsl) := { + 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 + +/* Client port for CTRL inside IPA */ +type port IPA_CTRL_PT message { + inout CtrlMessage, ASP_IPA_Event; +} with { extension "internal" } + +#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 + + +type component IPA_Emulation_CT { + /* down-facing port to IPA codec port */ + port IPA_CODEC_PT IPA_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 + /* up-facing port for CTRL */ + port IPA_CTRL_PT IPA_CTRL_PORT; +#ifdef IPA_EMULATION_GSUP + /* up-facing port for GSUP */ + port IPA_GSUP_PT IPA_GSUP_PORT; +#endif + + /* up-facing port for other streams */ + port IPA_SP_PT IPA_SP_PORT; + + var boolean g_initialized := false; + var IPL4asp_Types.ConnectionId g_ipa_conn_id := -1; + /* Are we a BSC/MGW (truel) or MSC (false) */ + var boolean g_is_bsc_mgw; + + var IpaMode g_mode; + 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 := "" +}; + +/* 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:={} }); + g_ipa_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:={} }); + g_ipa_conn_id := res.connId; + g_ccm_pars := ccm_pars; + g_is_bsc_mgw := false; +} + +#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 := payload +} + +private function f_send_IPA_EVT(template ASP_IPA_Event evt) runs on IPA_Emulation_CT { +#ifdef IPA_EMULATION_RSL + if (IPA_RSL_PORT.checkstate("Connected")) { + IPA_RSL_PORT.send(evt); + } +#endif + if (IPA_CTRL_PORT.checkstate("Connected")) { + IPA_CTRL_PORT.send(evt); + } +#ifdef IPA_EMULATION_GSUP + if (IPA_GSUP_PORT.checkstate("Connected")) { + IPA_GSUP_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 := {} + } + } + + 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_UNIT) { + 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) runs on IPA_Emulation_CT { + var IPA_Send ipa_tx := valueof(t_IPA_Send(g_ipa_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 } + } + } +} + +/* receive IPA CCM message */ +private function f_ccm_rx(PDU_IPA_CCM ccm) runs on IPA_Emulation_CT { + select (ccm.msg_type) { + case (IPAC_MSGT_PING) { + f_ccm_tx(valueof(ts_IPA_PONG)); + } + case (IPAC_MSGT_ID_ACK) { + f_ccm_tx(valueof(ts_IPA_ACK)); + } + case (IPAC_MSGT_ID_GET) { + f_ccm_tx(f_ccm_make_id_resp(ccm)); + } + 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); + var IPA_Send ret := valueof(t_IPA_Send(connId, rsl_tx.streamId, 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) runs on IPA_Emulation_CT { + g_mode := IPA_MODE_CLIENT; + f_connect(remote_host, remote_port, local_host, local_port, ccm_pars); + f_send_IPA_EVT(t_ASP_IPA_EVT_UD(ASP_IPA_EVENT_UP)); + ScanEvents(); +} + +/* main function to use for a server-side IPA implementation */ +function main_server(charstring local_host, IPL4asp_Types.PortNumber local_port) runs on IPA_Emulation_CT { + g_mode := IPA_MODE_SERVER; + f_bind(local_host, local_port); + ScanEvents(); +} + +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)); +} + +#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_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 + +/* 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 CtrlMessage ctrl_msg; + var ASP_IPA_Unitdata ipa_ud; +#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 + + /* 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_ipa_conn_id, vl_f, {0, 2, 3, 1, 0}); + + while (true) { + alt { + /* Received IPA -> up into SCCP stack */ + [] IPA_PORT.receive(IPA_RecvFrom: ?) -> value ipa_rx { + select (ipa_rx.streamId) { + case (IPAC_PROTO_CCM) { + var PDU_IPA_CCM ccm := dec_PDU_IPA_CCM(ipa_rx.msg); + log("CCM Rx:", ccm); + f_ccm_rx(ccm); + } +#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 := { + streamId := ipa_rx.streamId, + rsl := dec_RSL_Message(ipa_rx.msg) + }; + IPA_RSL_PORT.send(rsl); + } +#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 + case (IPAC_PROTO_EXT_CTRL) { + f_ctrl_to_user(ipa_rx.msg); + } +#ifdef IPA_EMULATION_GSUP + case (IPAC_PROTO_EXT_GSUP) { + f_gsup_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 { + log("IPA: Connected"); + g_ipa_conn_id := asp_evt.connOpened.connId; + f_send_IPA_EVT(t_ASP_IPA_EVT_UD(ASP_IPA_EVENT_UP)); + if (g_mode == IPA_MODE_SERVER) { + f_ccm_tx(valueof(ts_IPA_ID_GET)); + } + } + + [] IPA_PORT.receive(ASP_Event:{connClosed:=?}) -> value asp_evt { + log("IPA: Closed"); + g_ipa_conn_id := -1; + f_send_IPA_EVT(t_ASP_IPA_EVT_UD(ASP_IPA_EVENT_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_ipa_conn_id := -1; + f_send_IPA_EVT(t_ASP_IPA_EVT_UD(ASP_IPA_EVENT_DOWN)); + 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(g_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(g_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(g_ipa_conn_id, ipa_ud)); + } +#endif + + [] 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(g_ipa_conn_id, ipa_ud)); + } + +#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(g_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(g_ipa_conn_id, rsl)); + } +#endif + + /* Received MISC (OML/CTRL) -> down into IPA */ + [] IPA_SP_PORT.receive(ASP_IPA_Unitdata: ?) -> value ipa_ud { + IPA_PORT.send(f_from_asp(g_ipa_conn_id, ipa_ud)); + } + + + } + } +} + +} |