summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2019-03-04 18:31:11 +0100
committerHarald Welte <laforge@gnumonks.org>2019-09-13 12:36:40 +0200
commit94d03173cef10b96efed56e0178f0419cdf06a4b (patch)
treeef65faca111a510179eb3916f71985e877fde953
parentd8f28e6d19563fc629e2e242e7c4e3cdb7515dcb (diff)
Initial TTCN-3 test suite for osmo-remsimlaforge/rspro
This adds shared infrastructure and initial test suites for osmo-remsim-{server,client,bankd}. Change-Id: I00034d3a991f0f881cfd8ff0bfc4557113daf830
-rw-r--r--Makefile2
-rw-r--r--deps/Makefile6
-rw-r--r--remsim/REMSIM_Tests.cfg10
-rw-r--r--remsim/REMSIM_Tests.default3
-rw-r--r--remsim/REMSIM_Tests.ttcn247
-rw-r--r--remsim/RSPRO.asn349
-rw-r--r--remsim/RSPRO_EncDec.cc26
-rw-r--r--remsim/RSPRO_Server.ttcn174
-rw-r--r--remsim/RSPRO_Types.ttcn375
-rw-r--r--remsim/RSRES.ttcn110
-rw-r--r--remsim/RemsimBankd_Tests.ttcn298
-rw-r--r--remsim/RemsimClient_Tests.ttcn118
-rw-r--r--remsim/RemsimServer_Tests.ttcn485
-rwxr-xr-xremsim/gen_links.sh43
-rwxr-xr-xremsim/regen_makefile.sh7
15 files changed, 2252 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 2c8e951..99243d6 100644
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-SUBDIRS=bsc bsc-nat bts ggsn_tests hlr mgw msc pcu sccp selftest sgsn sip sysinfo
+SUBDIRS=bsc bsc-nat bts ggsn_tests hlr mgw msc pcu remsim sccp selftest sgsn sip sysinfo
NPROC=$(shell nproc 2>/dev/null)
ifeq ($(NPROC),)
diff --git a/deps/Makefile b/deps/Makefile
index ed265c0..ec31e59 100644
--- a/deps/Makefile
+++ b/deps/Makefile
@@ -22,6 +22,7 @@ ECLIPSEGIT_REPOS= titan.Libraries.TCCUsefulFunctions \
titan.ProtocolModules.ICMP \
titan.ProtocolModules.ICMPv6 \
titan.ProtocolModules.IP \
+ titan.ProtocolModules.JSON_v07_2006 \
titan.ProtocolModules.L2TP \
titan.ProtocolModules.M3UA \
titan.ProtocolModules.SMPP \
@@ -30,6 +31,8 @@ ECLIPSEGIT_REPOS= titan.Libraries.TCCUsefulFunctions \
titan.ProtocolModules.RTP \
titan.ProtocolModules.DIAMETER_ProtocolModule_Generator \
titan.TestPorts.Common_Components.Socket-API \
+ titan.TestPorts.Common_Components.Abstract_Socket \
+ titan.TestPorts.HTTPmsg \
titan.TestPorts.IPL4asp \
titan.TestPorts.LANL2asp \
titan.TestPorts.PCAPasp \
@@ -84,6 +87,7 @@ titan.ProtocolModules.ICMP_commit= R.2.A
titan.ProtocolModules.ICMPv6_commit= R.2.A
titan.ProtocolModules.IP_commit= R.10.B-1-g99d0ec9
titan.ProtocolModules.ISUP_Q.762_commit= R.8.A
+titan.ProtocolModules.JSON_v07_2006_commit= R.1.A
titan.ProtocolModules.L2TP_commit= R.2.A
titan.ProtocolModules.LLC_v7.1.0_commit= 2a3c09fbf7bae22f802aa88689800f38a1f3732d
titan.ProtocolModules.MAP_commit= R.2.A-1-g79c6a3d
@@ -104,7 +108,9 @@ titan.ProtocolModules.SUA_commit= R.5.A
titan.ProtocolModules.TCP_commit= R.3.A
titan.ProtocolModules.UDP_commit= R.4.A
titan.TestPorts.Common_Components.Socket-API_commit= R.6.A
+titan.TestPorts.Common_Components.Abstract_Socket_commit= R.9.B
titan.TestPorts.GPIO_commit= R.3.A
+titan.TestPorts.HTTPmsg_commit= R.9.B
titan.TestPorts.IPL4asp_commit= R.29.A
titan.TestPorts.LANL2asp_commit= R.8.B
titan.TestPorts.MTP3asp_commit= 1cecdad6f3641a5f19b3833703bff6e5005eff11
diff --git a/remsim/REMSIM_Tests.cfg b/remsim/REMSIM_Tests.cfg
new file mode 100644
index 0000000..b2dd9b2
--- /dev/null
+++ b/remsim/REMSIM_Tests.cfg
@@ -0,0 +1,10 @@
+[ORDERED_INCLUDE]
+# Common configuration, shared between test suites
+"../Common.cfg"
+# testsuite specific configuration, not expected to change
+"./REMSIM_Tests.default"
+
+[CONTROL]
+RemsimServer_Tests.control
+#RemsimBankd_Tests.control
+#RemsimClient_Tests.control
diff --git a/remsim/REMSIM_Tests.default b/remsim/REMSIM_Tests.default
new file mode 100644
index 0000000..7a41555
--- /dev/null
+++ b/remsim/REMSIM_Tests.default
@@ -0,0 +1,3 @@
+[TESTPORT_PARAMETERS]
+system.HTTP.http_debugging := "yes"
+system.HTTP.use_notification_ASPs := "no"
diff --git a/remsim/REMSIM_Tests.ttcn b/remsim/REMSIM_Tests.ttcn
new file mode 100644
index 0000000..ee2d450
--- /dev/null
+++ b/remsim/REMSIM_Tests.ttcn
@@ -0,0 +1,247 @@
+module REMSIM_Tests {
+
+/* Implementation of RSPRO Client in TTCN-3.
+ * (C) 2019 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.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+import from IPL4asp_Types all;
+import from RSPRO all;
+import from RSPRO_Types all;
+import from IPA_Types all;
+import from IPA_Emulation all;
+
+
+modulepar {
+ charstring mp_bankd_ip := "127.0.0.1";
+ integer mp_bankd_port := 9999;
+
+ charstring mp_server_ip := "127.0.0.1";
+ integer mp_server_port := 9998;
+
+ integer mp_rsres_port := 9997;
+}
+
+const integer NUM_CLIENT := 3;
+
+type record RSPRO_Client {
+ IPA_Emulation_CT vc_IPA,
+ IPA_CCM_Parameters ccm_pars,
+ charstring id,
+ ComponentIdentity rspro_id,
+
+ ClientSlot rspro_client_slot optional,
+ BankId rspro_bank_id optional,
+ SlotNumber rspro_bank_nslots optional
+};
+
+type component rspro_client_CT {
+ var RSPRO_Client rspro[NUM_CLIENT];
+ port IPA_RSPRO_PT RSPRO[NUM_CLIENT];
+};
+
+private altstep as_ignore_id_ack(integer i := 0) runs on rspro_client_CT {
+ [] RSPRO[i].receive(ASP_IPA_Event:{up_down := ASP_IPA_EVENT_ID_ACK}) { repeat; }
+}
+
+function f_rspro_init(inout RSPRO_Client clnt, charstring dst_host, integer dst_port,
+ ComponentIdentity rspro_id, integer i)
+runs on rspro_client_CT
+{
+ timer T := 4.0;
+
+ clnt.id := "RSPRO" & int2str(i);
+ clnt.vc_IPA := IPA_Emulation_CT.create(clnt.id);
+ clnt.ccm_pars := c_IPA_default_ccm_pars;
+ clnt.ccm_pars.name := "Osmocom TTCN-3 RSPRO client simulator";
+ clnt.rspro_id := rspro_id;
+
+ /* leave it up to the caller to set those */
+ clnt.rspro_client_slot := omit;
+ clnt.rspro_bank_id := omit;
+ clnt.rspro_bank_nslots := omit;
+
+ map(clnt.vc_IPA:IPA_PORT, system:IPA_CODEC_PT);
+ connect(clnt.vc_IPA:IPA_RSPRO_PORT, self:RSPRO[i]);
+
+ clnt.vc_IPA.start(IPA_Emulation.main_client(dst_host, dst_port, "", 10000+i, clnt.ccm_pars));
+
+ T.start;
+ alt {
+ [] RSPRO[i].receive(ASP_IPA_Event:{up_down := ASP_IPA_EVENT_UP}) { }
+ [] T.timeout {
+ setverdict(fail, "Timeout waiting for ASP_IPA_EVENT_UP");
+ mtc.stop;
+ }
+ }
+ T.start;
+ alt {
+ [] RSPRO[i].receive(ASP_IPA_Event:{up_down := ASP_IPA_EVENT_ID_ACK}) { }
+ [] T.timeout {
+ setverdict(fail, "Timeout waiting for ASP_IPA_EVENT_ID_ACK");
+ mtc.stop;
+ }
+ }
+
+
+ activate(as_ignore_id_ack(i));
+}
+
+function f_rspro_fini(inout RSPRO_Client clnt, integer i)
+runs on rspro_client_CT {
+ clnt.vc_IPA.stop;
+ disconnect(clnt.vc_IPA:IPA_RSPRO_PORT, self:RSPRO[i]);
+ unmap(clnt.vc_IPA:IPA_PORT, system:IPA_CODEC_PT);
+}
+
+
+function f_rspro_exp(template RsproPDU exp, integer i := 0)
+runs on rspro_client_CT return RsproPDU
+{
+ var RsproPDU pdu;
+
+ timer T := 10.0;
+ T.start;
+ alt {
+ [] RSPRO[i].receive(exp) -> value pdu {
+ setverdict(pass);
+ }
+ [] RSPRO[i].receive(RsproPDU:?) -> value pdu {
+ setverdict(fail, "Received unexpected RPSRO", pdu);
+ mtc.stop;
+ }
+ [] RSPRO[i].receive {
+ setverdict(fail, "Received unexpected != RPSRO");
+ mtc.stop;
+ }
+ [] T.timeout {
+ setverdict(fail, "Timeout waiting for ", exp);
+ mtc.stop;
+ }
+ }
+ return pdu;
+}
+
+function f_rspro_exp_disconnect(integer i := 0)
+runs on rspro_client_CT {
+ timer T := 10.0;
+ T.start;
+ alt {
+ [] RSPRO[i].receive(ASP_IPA_Event:{up_down := ASP_IPA_EVENT_DOWN}) {
+ setverdict(pass);
+ }
+ [] T.timeout {
+ setverdict(fail, "Timeout expecting disconnect");
+ mtc.stop;
+ }
+ }
+}
+
+
+function f_rspro_connect_client(integer i, template ResultCode exp_res := ok) runs on rspro_client_CT
+{
+ select (rspro[i].rspro_id.type_) {
+ case (remsimClient) {
+ RSPRO[i].send(ts_RSPRO_ConnectClientReq(rspro[i].rspro_id, rspro[i].rspro_client_slot));
+ f_rspro_exp(tr_RSPRO_ConnectClientRes(?, exp_res), i);
+ }
+ case (remsimBankd) {
+ var template IpAddress ip := ts_IPv4(mp_bankd_ip);
+ RSPRO[i].send(ts_RSPRO_ConnectBankReq(rspro[i].rspro_id, rspro[i].rspro_bank_id,
+ rspro[i].rspro_bank_nslots,
+ ts_IpPort(ip, mp_bankd_port)));
+ f_rspro_exp(tr_RSPRO_ConnectBankRes(?, exp_res), i);
+ }
+ case else {
+ setverdict(fail, "Unsupported type ", rspro[i].rspro_id.type_);
+ mtc.stop;
+ }
+ }
+}
+
+function f_rspro_connect_clients() runs on rspro_client_CT
+{
+ var integer i;
+
+ for (i := 0; i < NUM_CLIENT; i := i+1) {
+ select (rspro[i].rspro_id.type_) {
+ case (remsimClient) {
+ RSPRO[i].send(ts_RSPRO_ConnectClientReq(rspro[i].rspro_id,
+ rspro[i].rspro_client_slot));
+ }
+ case (remsimBankd) {
+ var template IpAddress ip := ts_IPv4(mp_bankd_ip);
+ RSPRO[i].send(ts_RSPRO_ConnectBankReq(rspro[i].rspro_id, rspro[i].rspro_bank_id,
+ rspro[i].rspro_bank_nslots,
+ ts_IpPort(ip, mp_bankd_port)));
+ }
+ }
+ }
+ for (i := 0; i < NUM_CLIENT; i := i+1) {
+ select (rspro[i].rspro_id.type_) {
+ case (remsimClient) {
+ f_rspro_exp(tr_RSPRO_ConnectClientRes(?, ResultCode:ok), i);
+ }
+ case (remsimBankd) {
+ f_rspro_exp(tr_RSPRO_ConnectBankRes(?, ResultCode:ok), i);
+ }
+ }
+ }
+}
+
+/* transceive a TPDU from modem to card (and back) */
+function f_rspro_xceive_mdm2card(integer idx, BankSlot bs, template (value) octetstring data,
+ template (value) TpduFlags flags) runs on rspro_client_CT return octetstring {
+ var RsproPDU rx;
+ RSPRO[idx].send(ts_RSPRO_TpduModemToCard(rspro[idx].rspro_client_slot, bs, flags, data));
+ rx := f_rspro_exp(tr_RSPRO_TpduCardToModem(bs, rspro[idx].rspro_client_slot, ?, ?));
+ return rx.msg.tpduCardToModem.data;
+}
+
+/* handle an incoming CreateMapping + ACK it */
+altstep as_rspro_create_mapping(integer i, template ClientSlot cslot := ?, template BankSlot bslot := ?,
+ template ResultCode res := ok)
+runs on rspro_client_CT {
+ var RsproPDU rx;
+ [] RSPRO[i].receive(tr_RSPRO_CreateMappingReq(cslot, bslot)) -> value rx {
+ RSPRO[i].send(ts_RSPRO_CreateMappingRes(res));
+ }
+}
+
+/* handle an incoming RemoveMapping + ACK it */
+altstep as_rspro_remove_mapping(integer i, template ClientSlot cslot := ?, template BankSlot bslot := ?,
+ template ResultCode res := ok)
+runs on rspro_client_CT {
+ var RsproPDU rx;
+ [] RSPRO[i].receive(tr_RSPRO_RemoveMappingReq(cslot, bslot)) -> value rx {
+ RSPRO[i].send(ts_RSPRO_RemoveMappingRes(res));
+ }
+}
+
+altstep as_rspro_cfg_client_id(integer i, template ClientSlot cslot := ?,
+ template (value) ResultCode res := ok)
+runs on rspro_client_CT {
+ var RsproPDU rx;
+ [] RSPRO[i].receive(tr_RSPRO_ConfigClientIdReq(cslot)) -> value rx {
+ RSPRO[i].send(ts_RSPRO_ConfigClientIdRes(res));
+ }
+}
+
+altstep as_rspro_cfg_client_bank(integer i, template BankSlot bslot := ?,
+ template IpPort ip_port := ?,
+ template (value) ResultCode res := ok)
+runs on rspro_client_CT {
+ var RsproPDU rx;
+ [] RSPRO[i].receive(tr_RSPRO_ConfigClientBankReq(bslot, ip_port)) -> value rx {
+ RSPRO[i].send(ts_RSPRO_ConfigClientBankRes(res));
+ }
+}
+
+
+
+}
diff --git a/remsim/RSPRO.asn b/remsim/RSPRO.asn
new file mode 100644
index 0000000..aaafb96
--- /dev/null
+++ b/remsim/RSPRO.asn
@@ -0,0 +1,349 @@
+----------------------------------------------------------------------
+-- RSPRO - Remote SIM Protocol, part of Osmocom Remote SIM Suite
+-- (C) 2018 by Harald Welte <laforge@gnumonks.org>
+-- All Rights Reserved
+--
+-- SPDX-License-Identifier: GPL-2.0+
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License along
+-- with this program; if not, write to the Free Software Foundation, Inc.,
+-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+--
+----------------------------------------------------------------------
+
+RSPRO {} DEFINITIONS
+
+IMPLICIT TAGS
+
+::=
+
+BEGIN
+
+EXPORTS
+ RsproPDU
+;
+
+----------------------------------------------------------------------
+-- Elementary Data Types
+----------------------------------------------------------------------
+
+-- Some random ID the requestor can chose and which the client echos back in a response.
+-- This allows multiple outstanding commands in flight and matching of responses to requests.
+OperationTag ::= INTEGER(0..2147483647)
+
+-- Unique identifier of a given SIM bank
+BankId ::= INTEGER(0..1023)
+
+-- Unique identifier of a given client (modem)
+ClientId ::= INTEGER(0..1023)
+
+ComponentType ::= ENUMERATED {
+ -- client: Modems / Phones
+ remsimClient (0),
+ -- server: Coordination
+ remsimServer (1),
+ -- bank daemon: SIM cards
+ remsimBankd (2)
+}
+ComponentName ::= IA5String (SIZE (1..32))
+ComponentIdentity ::= SEQUENCE {
+ type ComponentType,
+ name ComponentName,
+ software [0] ComponentName,
+ swVersion [1] ComponentName,
+ hwManufacturer [2] ComponentName OPTIONAL,
+ hwModel [3] ComponentName OPTIONAL,
+ hwSerialNr [4] ComponentName OPTIONAL,
+ hwVersion [5] ComponentName OPTIONAL,
+ fwVersion [6] ComponentName OPTIONAL,
+ ...
+}
+
+-- IP address / port details
+Ipv4Address ::= OCTET STRING (SIZE (4))
+Ipv6Address ::= OCTET STRING (SIZE (16))
+IpAddress ::= CHOICE {
+ ipv4 [0] Ipv4Address,
+ ipv6 [1] Ipv6Address
+}
+PortNumber ::= INTEGER (0..65535)
+IpPort ::= SEQUENCE {
+ ip IpAddress,
+ port PortNumber
+}
+
+-- Result of a given operation
+ResultCode ::= ENUMERATED {
+ ok (0),
+ -- client / bank / slot ID not accepted
+ illegalClientId (1),
+ illegalBankId (2),
+ illegalSlotId (3),
+ unsupportedProtocolVersion (4),
+ unknownSlotmap (5),
+
+ -- no card is present in given slot
+ cardNotPresent (100),
+ -- card is present but unresponsive in given slot
+ cardUnresponsive (101),
+ -- unrecoverable transmission errors detected
+ cardTransmissionError (102),
+ ...
+}
+
+ErrorCode ::= ENUMERATED {
+ -- Bankd or Server has received connection form unknown client (no mapping)
+ unknownClientConnected (1),
+ -- unexpected disconnect (typically bankd reports client disconnect)
+ unexpectedDisconnect (2),
+ unexpectedProtocolVersion (3),
+ ...
+}
+
+ErrorString ::= IA5String (SIZE (1..255))
+
+ErrorSeverity ::= ENUMERATED {
+ minor (1),
+ major (2),
+ fatal (3),
+ ...
+}
+
+-- Slot number within a SIM bank or a client.
+SlotNumber ::= INTEGER(0..1023)
+
+-- Slot identity on client (modem) side
+ClientSlot ::= SEQUENCE {
+ clientId ClientId,
+ slotNr SlotNumber,
+ ...
+}
+
+-- Slot identity on SIM bank side
+BankSlot ::= SEQUENCE {
+ bankId BankId,
+ slotNr SlotNumber,
+ ...
+}
+
+ATR ::= OCTET STRING (SIZE (1..55))
+
+-- flags related to a TPDU in either of the two directions
+TpduFlags ::= SEQUENCE {
+ -- indicates a TPDU header is present in this message
+ tpduHeaderPresent BOOLEAN,
+ -- indicates last part of transmission in this direction
+ finalPart BOOLEAN,
+ -- indicates a PB is present and we should continue with TX
+ procByteContinueTx BOOLEAN,
+ -- indicates a PB is present and we should continue with RX
+ procByteContinueRx BOOLEAN,
+ ...
+}
+
+--- physical state of a given slot
+SlotPhysStatus ::= SEQUENCE {
+ -- is RST activated by the modem?
+ resetActive [0] BOOLEAN,
+ -- is VCC applied by the modem?
+ vccPresent [1] BOOLEAN OPTIONAL,
+ -- is CLK applied by the modem?
+ clkActive [2] BOOLEAN OPTIONAL, -- not all hardware supports this
+ -- is card presence signalled to the modem?
+ cardPresent [3] BOOLEAN OPTIONAL,
+ ...
+}
+
+----------------------------------------------------------------------
+-- Messages
+----------------------------------------------------------------------
+
+
+-- BANKD->SERVER: SIM Bank connects to central server
+ConnectBankReq ::= SEQUENCE {
+ -- identity of the bank that is connecting to the server
+ identity ComponentIdentity,
+ -- bank number, pre-configured on bank side
+ bankId BankId,
+ numberOfSlots SlotNumber,
+ -- IP/Port to which this bankd has bound and is listening for clients
+ bound IpPort OPTIONAL,
+ ...
+}
+ConnectBankRes ::= SEQUENCE {
+ -- identity of the server to which the bank is connecting
+ identity ComponentIdentity,
+ result ResultCode,
+ ...
+}
+
+-- CLIENT->SERVER or CLIENT->BANKD
+ConnectClientReq ::= SEQUENCE {
+ -- identity of the client that is connecting to the server/bankd
+ identity ComponentIdentity,
+ clientSlot ClientSlot OPTIONAL, -- mandatory for CL->BANKD; CL->SERVER: old identity, if any
+ ...
+}
+ConnectClientRes ::= SEQUENCE {
+ -- identity of the bankd/server to which the client is connecting
+ identity ComponentIdentity,
+ result ResultCode,
+ ...
+}
+
+-- SERVER->BANKD: create a mapping between a given Bank:Slot <-> Client:Slot
+CreateMappingReq ::= SEQUENCE {
+ client ClientSlot,
+ bank BankSlot,
+ ...
+}
+CreateMappingRes ::= SEQUENCE {
+ result ResultCode,
+ ...
+}
+
+-- SERVER->BANKD: remove a mapping between a given Bank:Slot <-> Client:Slot
+RemoveMappingReq ::= SEQUENCE {
+ client ClientSlot,
+ bank BankSlot,
+ ...
+}
+RemoveMappingRes ::= SEQUENCE {
+ result ResultCode,
+ ...
+}
+
+-- SERVER->CLIENT: set Client ID
+ConfigClientIdReq ::= SEQUENCE {
+ -- server-allocated assignment of a client ID
+ clientSlot ClientSlot,
+ ...
+}
+ConfigClientIdRes ::= SEQUENCE {
+ result ResultCode,
+ ...
+}
+
+-- SERVER->CLIENT: set BankId/Slot and IP/Port
+ConfigClientBankReq ::= SEQUENCE {
+ -- server-allocated assignment of a client ID
+ bankSlot BankSlot,
+ -- bank to which the client shall connect
+ bankd IpPort,
+ ...
+}
+ConfigClientBankRes ::= SEQUENCE {
+ result ResultCode,
+ ...
+}
+
+
+-- BANKD->CLIENT: configure the ATR which the card emulator (client) shall send to the modem
+SetAtrReq ::= SEQUENCE {
+ slot ClientSlot,
+ atr ATR,
+ ...
+}
+SetAtrRes ::= SEQUENCE {
+ result ResultCode,
+ ...
+}
+
+-- CLIENT->BANKD: TPDU in Modem -> Card direction
+TpduModemToCard ::= SEQUENCE {
+ -- we include fully-qualified bank and client slots for easier debugging
+ fromClientSlot ClientSlot,
+ toBankSlot BankSlot,
+ flags TpduFlags,
+ data OCTET STRING,
+ ...
+}
+
+-- BANKD->CLIENT: TPDU in Card -> Modem direction
+TpduCardToModem ::= SEQUENCE {
+ -- we include fully-qualified bank and client slots for easier debugging
+ fromBankSlot BankSlot,
+ toClientSlot ClientSlot,
+ flags TpduFlags,
+ data OCTET STRING,
+ ...
+}
+
+-- CLIENT->BANKD: indciation about the current status of a client (modem side)
+ClientSlotStatusInd ::= SEQUENCE {
+ fromClientSlot ClientSlot,
+ toBankSlot BankSlot,
+ slotPhysStatus SlotPhysStatus,
+ ...
+}
+
+-- BANKD->CLIENT: indciation about the current status of a bank (modem side)
+BankSlotStatusInd ::= SEQUENCE {
+ fromBankSlot BankSlot,
+ toClientSlot ClientSlot,
+ slotPhysStatus SlotPhysStatus,
+ ...
+}
+
+-- *->SERVER: indication about some kind of error
+ErrorInd ::= SEQUENCE {
+ -- whoever is detecting + sending us the error
+ sender ComponentType,
+ severity ErrorSeverity,
+ code ErrorCode,
+ -- any bank-side slot that's affected
+ bankSlot [0] BankSlot OPTIONAL,
+ -- any client-side slot that's affected
+ clientSlot [1] ClientSlot OPTIONAL,
+ -- any additional textual information
+ errorString [2] ErrorString OPTIONAL,
+ ...
+}
+
+
+----------------------------------------------------------------------
+-- PDU
+----------------------------------------------------------------------
+
+RsproPDUchoice ::= CHOICE {
+ -- configuration + management
+ connectBankReq [0] ConnectBankReq,
+ connectBankRes [1] ConnectBankRes,
+ connectClientReq [2] ConnectClientReq,
+ connectClientRes [3] ConnectClientRes,
+ createMappingReq [4] CreateMappingReq,
+ createMappingRes [5] CreateMappingRes,
+ removeMappingReq [6] RemoveMappingReq,
+ removeMappingRes [7] RemoveMappingRes,
+ configClientIdReq [8] ConfigClientIdReq,
+ configClientIdRes [9] ConfigClientIdRes,
+ configClientBankReq [17] ConfigClientBankReq,
+ configClientBankRes [18] ConfigClientBankRes,
+ errorInd [16] ErrorInd,
+ -- APDUs etc.
+ setAtrReq [10] SetAtrReq,
+ setAtrRes [11] SetAtrRes,
+ tpduModemToCard [12] TpduModemToCard,
+ tpduCardToModem [13] TpduCardToModem,
+ clientSlotStatusInd [14] ClientSlotStatusInd,
+ bankSlotStatusInd [15] BankSlotStatusInd,
+ ...
+}
+
+RsproPDU ::= SEQUENCE {
+ version [0] INTEGER(0..32),
+ tag [1] OperationTag,
+ msg [2] RsproPDUchoice
+}
+
+END
diff --git a/remsim/RSPRO_EncDec.cc b/remsim/RSPRO_EncDec.cc
new file mode 100644
index 0000000..a1f898e
--- /dev/null
+++ b/remsim/RSPRO_EncDec.cc
@@ -0,0 +1,26 @@
+#include "RSPRO.hh"
+
+namespace RSPRO__Types {
+
+using namespace RSPRO;
+
+TTCN_Module RSPRO__EncDec("RSPRO_EncDec", __DATE__, __TIME__);
+
+OCTETSTRING enc__RsproPDU(const RsproPDU& pdu) {
+ TTCN_Buffer buf;
+
+ buf.clear();
+ pdu.encode(RsproPDU_descr_, buf, TTCN_EncDec::CT_BER, BER_ENCODE_DER);
+ return OCTETSTRING(buf.get_len(), buf.get_data());
+}
+
+RsproPDU dec__RsproPDU(const OCTETSTRING &stream) {
+ TTCN_Buffer buf;
+ RsproPDU pdu;
+ buf.put_os(stream);
+
+ pdu.decode(RsproPDU_descr_, buf, TTCN_EncDec::CT_BER, BER_ACCEPT_ALL);
+ return pdu;
+}
+
+}
diff --git a/remsim/RSPRO_Server.ttcn b/remsim/RSPRO_Server.ttcn
new file mode 100644
index 0000000..977e7c6
--- /dev/null
+++ b/remsim/RSPRO_Server.ttcn
@@ -0,0 +1,174 @@
+module RSPRO_Server {
+
+/* Utility functions implementing an RSRPO server.
+ * (C) 2019 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.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+
+import from IPL4asp_Types all;
+import from RSPRO all;
+import from RSPRO_Types all;
+import from IPA_Types all;
+import from IPA_Emulation all;
+
+
+type record RSPRO_Server {
+ IPA_Emulation_CT vc_IPA,
+ IPA_CCM_Parameters ccm_pars,
+ charstring id,
+ ComponentIdentity rspro_id//,
+
+ //ClientSlot rspro_client_slot optional,
+ //BankId rspro_bank_id optional,
+ //SlotNumber rspro_bank_nslots optional
+};
+
+const integer NUM_SERVER := 2;
+
+type component rspro_server_CT {
+ var RSPRO_Server g_rspro_srv[NUM_SERVER];
+ port IPA_RSPRO_PT RSPRO_SRV[NUM_SERVER];
+};
+
+
+altstep as_ignore_id_ack(integer i) runs on rspro_server_CT {
+ [] RSPRO_SRV[i].receive(ASP_IPA_Event:{up_down := ASP_IPA_EVENT_ID_ACK}) { repeat; }
+}
+
+
+function f_rspro_srv_exp_connect(integer i)
+runs on rspro_server_CT
+{
+ timer T := 20.0;
+ T.start;
+ alt {
+ [] RSPRO_SRV[i].receive(ASP_IPA_Event:{up_down := ASP_IPA_EVENT_UP}) { }
+ [] T.timeout {
+ setverdict(fail, "Timeout waiting for ASP_IPA_EVENT_UP");
+ mtc.stop;
+ }
+ }
+}
+
+function f_rspro_srv_init(integer i, charstring bind_host, integer bind_port,
+ ComponentIdentity rspro_id, boolean exp_connect := true)
+runs on rspro_server_CT
+{
+ g_rspro_srv[i].id := "RSPRO_SRV" & int2str(i);
+ g_rspro_srv[i].vc_IPA := IPA_Emulation_CT.create(g_rspro_srv[i].id);
+ g_rspro_srv[i].ccm_pars := c_IPA_default_ccm_pars;
+ g_rspro_srv[i].ccm_pars.name := "Osmocom TTCN-3 RSPRO server simulator";
+ g_rspro_srv[i].rspro_id := rspro_id;
+
+ map(g_rspro_srv[i].vc_IPA:IPA_PORT, system:IPA_CODEC_PT);
+ connect(g_rspro_srv[i].vc_IPA:IPA_RSPRO_PORT, self:RSPRO_SRV[i]);
+
+ g_rspro_srv[i].vc_IPA.start(IPA_Emulation.main_server(bind_host, bind_port));
+
+ activate(as_ignore_id_ack(i));
+
+ if (exp_connect) {
+ f_rspro_srv_exp_connect(i);
+ }
+}
+
+function f_rspro_srv_fini(integer i)
+runs on rspro_server_CT
+{
+ g_rspro_srv[i].vc_IPA.stop;
+ disconnect(g_rspro_srv[i].vc_IPA:IPA_RSPRO_PORT, self:RSPRO_SRV[i]);
+ unmap(g_rspro_srv[i].vc_IPA:IPA_PORT, system:IPA_CODEC_PT);
+}
+
+
+function f_rspro_srv_restart(integer i, charstring bind_host, integer bind_port)
+runs on rspro_server_CT
+{
+ g_rspro_srv[i].vc_IPA.stop;
+ g_rspro_srv[i].vc_IPA.start(IPA_Emulation.main_server(bind_host, bind_port));
+}
+
+
+function f_rspro_srv_exp(template RsproPDU exp, integer i := 0, float tout := 10.0)
+runs on rspro_server_CT return RsproPDU
+{
+ var RsproPDU pdu;
+
+ timer T := tout;
+ T.start;
+ alt {
+ [] RSPRO_SRV[i].receive(exp) -> value pdu {
+ setverdict(pass);
+ }
+ [] RSPRO_SRV[i].receive(RsproPDU:?) -> value pdu {
+ setverdict(fail, "Received unexpected RPSRO", pdu, " instead of ", exp);
+ mtc.stop;
+ }
+ [] as_ignore_id_ack(i) { repeat; }
+ [] RSPRO_SRV[i].receive {
+ setverdict(fail, "Received unexpected != RPSRO");
+ mtc.stop;
+ }
+ [] T.timeout {
+ setverdict(fail, "Timeout waiting for ", exp);
+ mtc.stop;
+ }
+ }
+ return pdu;
+}
+
+function f_rspro_srv_create_slotmap(ClientSlot cslot, BankSlot bslot,
+ template ResultCode exp_res := ok, integer i := 0)
+runs on rspro_server_CT
+{
+ RSPRO_SRV[i].send(ts_RSPRO_CreateMappingReq(cslot, bslot));
+ f_rspro_srv_exp(tr_RSPRO_CreateMappingRes(exp_res), i);
+}
+
+function f_rspro_srv_remove_slotmap(ClientSlot cslot, BankSlot bslot,
+ template ResultCode exp_res := ok, integer i := 0)
+runs on rspro_server_CT
+{
+ RSPRO_SRV[i].send(ts_RSPRO_RemoveMappingReq(cslot, bslot));
+ f_rspro_srv_exp(tr_RSPRO_RemoveMappingRes(exp_res), i);
+}
+
+function f_rspro_config_client_bank(template (value) BankSlot bank_slot,
+ template (value) IpPort bank_iport,
+ template ResultCode exp_res := ok, integer i := 0)
+runs on rspro_server_CT {
+ RSPRO_SRV[i].send(ts_RSPRO_ConfigClientBankReq(bank_slot, bank_iport));
+ f_rspro_srv_exp(tr_RSPRO_ConfigClientBankRes(exp_res));
+}
+
+
+altstep as_connectBankReq(template ComponentIdentity comp_id := tr_CompId(remsimBankd, ?,
+ "remsim-bankd", ?),
+ template BankId bid := ?,
+ template SlotNumber nslots := ?,
+ ResultCode res := ok, integer i := 0)
+runs on rspro_server_CT {
+ [] RSPRO_SRV[i].receive(tr_RSPRO_ConnectBankReq(comp_id, bid, nslots)) {
+ RSPRO_SRV[i].send(ts_RSPRO_ConnectBankRes(g_rspro_srv[i].rspro_id, res));
+ }
+}
+
+altstep as_connectClientReq(template ComponentIdentity comp_id := tr_CompId(remsimClient, ?,
+ "remsim-client", ?),
+ template ClientSlot cslot := *,
+ ResultCode res := ok, integer i := 0)
+runs on rspro_server_CT {
+ [] RSPRO_SRV[i].receive(tr_RSPRO_ConnectClientReq(comp_id, cslot)) {
+ RSPRO_SRV[i].send(ts_RSPRO_ConnectClientRes(g_rspro_srv[i].rspro_id, res));
+ }
+}
+
+
+
+}
diff --git a/remsim/RSPRO_Types.ttcn b/remsim/RSPRO_Types.ttcn
new file mode 100644
index 0000000..1eb84bd
--- /dev/null
+++ b/remsim/RSPRO_Types.ttcn
@@ -0,0 +1,375 @@
+module RSPRO_Types {
+
+/* Templates and utility functions for the RSPRO protocol.
+ * (C) 2019 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.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+import from RSPRO all;
+import from Native_Functions all;
+
+template ResultCode tr_Status_ok_or_nocard := (ok, cardNotPresent);
+
+external function dec_RsproPDU(in octetstring stream) return RsproPDU;
+external function enc_RsproPDU(in RsproPDU pdu) return octetstring;
+
+type integer RsproVersion (0..32);
+
+template (value) BankSlot ts_BankSlot(template (value) BankId bid,
+ template (value) SlotNumber slot) := {
+ bankId := bid,
+ slotNr := slot
+}
+
+template (value) ClientSlot ts_ClientSlot(template (value) ClientId cid,
+ template (value) SlotNumber slot) := {
+ clientId := cid,
+ slotNr := slot
+}
+
+template (value) ComponentIdentity ts_CompId(template (value) ComponentType ctype,
+ template (value) ComponentName name,
+ template (value) ComponentName sw := "TTCN3",
+ template (value) ComponentName sw_version := "0.1") := {
+ type_ := ctype,
+ name := name,
+ software := sw,
+ swVersion := sw_version,
+ hwManufacturer := "sysmocom",
+ hwModel := omit,
+ hwSerialNr := omit,
+ hwVersion := omit,
+ fwVersion := omit
+}
+template ComponentIdentity tr_CompId(template ComponentType ctype,
+ template ComponentName name,
+ template ComponentName sw := "TTCN3",
+ template ComponentName sw_version := "0.1") := {
+ type_ := ctype,
+ name := name,
+ software := sw,
+ swVersion := sw_version,
+ hwManufacturer := *,
+ hwModel := *,
+ hwSerialNr := *,
+ hwVersion := *,
+ fwVersion := *
+}
+
+template (value) IpPort ts_IpPort(template (value) IpAddress ip,
+ template (value) PortNumber port_nr) := {
+ ip := ip,
+ port_ := port_nr
+}
+template IpPort tr_IpPort(template IpAddress ip, template PortNumber port_nr) := {
+ ip := ip,
+ port_ := port_nr
+}
+
+template (value) IpAddress ts_IPv4(charstring ip_str) := {
+ ipv4 := f_inet_addr(ip_str)
+}
+
+
+
+
+template (value) RsproPDU ts_RSPRO(template (value) RsproPDUchoice msg,
+ template (value) RsproVersion version := 2,
+ template (value) OperationTag tag := 0) := {
+ version := version,
+ tag := tag,
+ msg := msg
+}
+
+template RsproPDU tr_RSPRO(template RsproPDUchoice msg,
+ template RsproVersion version := 2,
+ template OperationTag tag := ?) := {
+ version := version,
+ tag := tag,
+ msg := msg
+}
+
+
+template (value) RsproPDU ts_RSPRO_ConnectClientReq(template (value) ComponentIdentity id,
+ template (omit) ClientSlot clslot) :=
+ ts_RSPRO(RsproPDUchoice:{
+ connectClientReq := {
+ identity := id,
+ clientSlot := clslot
+ }
+ });
+template RsproPDU tr_RSPRO_ConnectClientReq(template ComponentIdentity id,
+ template ClientSlot clslot) :=
+ tr_RSPRO(RsproPDUchoice:{
+ connectClientReq := {
+ identity := id,
+ clientSlot := clslot
+ }
+ });
+
+
+template (value) RsproPDU ts_RSPRO_ConnectClientRes(template (value) ComponentIdentity id,
+ template (value) ResultCode res) :=
+ ts_RSPRO(RsproPDUchoice:{
+ connectClientRes := {
+ identity := id,
+ result := res
+ }
+ });
+template RsproPDU tr_RSPRO_ConnectClientRes(template ComponentIdentity id := ?,
+ template ResultCode res := ?) :=
+ tr_RSPRO(RsproPDUchoice:{
+ connectClientRes := {
+ identity := id,
+ result := res
+ }
+ });
+
+template (value) RsproPDU ts_RSPRO_ConnectBankReq(template (value) ComponentIdentity id,
+ template (value) BankId bid,
+ template (value) SlotNumber nslots,
+ template (omit) IpPort bound) :=
+ ts_RSPRO(RsproPDUchoice:{
+ connectBankReq := {
+ identity := id,
+ bankId := bid,
+ numberOfSlots := nslots,
+ bound := bound
+ }
+ });
+template RsproPDU tr_RSPRO_ConnectBankReq(template ComponentIdentity id,
+ template BankId bid,
+ template SlotNumber nslots,
+ template IpPort bound := *) :=
+ tr_RSPRO(RsproPDUchoice:{
+ connectBankReq := {
+ identity := id,
+ bankId := bid,
+ numberOfSlots := nslots,
+ bound := *
+ }
+ });
+
+
+template (value) RsproPDU ts_RSPRO_ConnectBankRes(template (value) ComponentIdentity id,
+ template (value) ResultCode res) :=
+ ts_RSPRO(RsproPDUchoice:{
+ connectBankRes := {
+ identity := id,
+ result := res
+ }
+ });
+template RsproPDU tr_RSPRO_ConnectBankRes(template ComponentIdentity id := ?,
+ template ResultCode res := ?) :=
+ tr_RSPRO(RsproPDUchoice:{
+ connectBankRes := {
+ identity := id,
+ result := res
+ }
+ });
+
+
+template (value) RsproPDU ts_RSPRO_SetAtrReq(template (value) ClientSlot clslot,
+ template (value) ATR atr) :=
+ ts_RSPRO(RsproPDUchoice:{
+ setAtrReq := {
+ slot := clslot,
+ atr := atr
+ }
+ });
+template RsproPDU tr_RSPRO_SetAtrReq(template ClientSlot clslot := ?,
+ template ATR atr := ?) :=
+ tr_RSPRO(RsproPDUchoice:{
+ setAtrReq := {
+ slot := clslot,
+ atr := atr
+ }
+ });
+
+template (value) RsproPDU ts_RSPRO_SetAtrRes(template (value) ResultCode res) :=
+ ts_RSPRO(RsproPDUchoice:{
+ setAtrRes := {
+ result := res
+ }
+ });
+template RsproPDU tr_RSPRO_SetAtrRes(template ResultCode res := ?) :=
+ tr_RSPRO(RsproPDUchoice:{
+ setAtrRes := {
+ result := res
+ }
+ });
+
+template (value) RsproPDU ts_RSPRO_TpduModemToCard(template (value) ClientSlot from_clslot,
+ template (value) BankSlot to_bslot,
+ template (value) TpduFlags flags,
+ template (value) octetstring data) :=
+ ts_RSPRO(RsproPDUchoice:{
+ tpduModemToCard := {
+ fromClientSlot := from_clslot,
+ toBankSlot := to_bslot,
+ flags := flags,
+ data := data
+ }
+ });
+template RsproPDU tr_RSPRO_TpduModemToCard(template ClientSlot from_clslot := ?,
+ template BankSlot to_bslot := ?,
+ template TpduFlags flags := ?,
+ template octetstring data := ?) :=
+ tr_RSPRO(RsproPDUchoice:{
+ tpduModemToCard := {
+ fromClientSlot := from_clslot,
+ toBankSlot := to_bslot,
+ flags := flags,
+ data := data
+ }
+ });
+
+template (value) RsproPDU ts_RSPRO_TpduCardToModem(template (value) BankSlot from_bslot,
+ template (value) ClientSlot to_clslot,
+ template (value) TpduFlags flags,
+ template (value) octetstring data) :=
+ ts_RSPRO(RsproPDUchoice:{
+ tpduCardToModem := {
+ fromBankSlot := from_bslot,
+ toClientSlot := to_clslot,
+ flags := flags,
+ data := data
+ }
+ });
+template RsproPDU tr_RSPRO_TpduCardToModem(template BankSlot from_bslot := ?,
+ template ClientSlot to_clslot := ?,
+ template TpduFlags flags := ?,
+ template octetstring data := ?) :=
+ tr_RSPRO(RsproPDUchoice:{
+ tpduCardToModem := {
+ fromBankSlot := from_bslot,
+ toClientSlot := to_clslot,
+ flags := flags,
+ data := data
+ }
+ });
+
+template (value) RsproPDU ts_RSPRO_CreateMappingReq(template (value) ClientSlot cslot,
+ template (value) BankSlot bslot) :=
+ ts_RSPRO(RsproPDUchoice:{
+ createMappingReq := {
+ client := cslot,
+ bank := bslot
+ }
+ });
+template RsproPDU tr_RSPRO_CreateMappingReq(template ClientSlot cslot,
+ template BankSlot bslot) :=
+ tr_RSPRO(RsproPDUchoice:{
+ createMappingReq := {
+ client := cslot,
+ bank := bslot
+ }
+ });
+
+template (value) RsproPDU ts_RSPRO_CreateMappingRes(template (value) ResultCode res) :=
+ ts_RSPRO(RsproPDUchoice:{
+ createMappingRes := {
+ result := res
+ }
+ });
+template RsproPDU tr_RSPRO_CreateMappingRes(template ResultCode res) :=
+ tr_RSPRO(RsproPDUchoice:{
+ createMappingRes := {
+ result := res
+ }
+ });
+
+template (value) RsproPDU ts_RSPRO_RemoveMappingReq(template (value) ClientSlot cslot,
+ template (value) BankSlot bslot) :=
+ ts_RSPRO(RsproPDUchoice:{
+ removeMappingReq := {
+ client := cslot,
+ bank := bslot
+ }
+ });
+template RsproPDU tr_RSPRO_RemoveMappingReq(template ClientSlot cslot,
+ template BankSlot bslot) :=
+ tr_RSPRO(RsproPDUchoice:{
+ removeMappingReq := {
+ client := cslot,
+ bank := bslot
+ }
+ });
+
+template (value) RsproPDU ts_RSPRO_RemoveMappingRes(template (value) ResultCode res) :=
+ ts_RSPRO(RsproPDUchoice:{
+ removeMappingRes := {
+ result := res
+ }
+ });
+template RsproPDU tr_RSPRO_RemoveMappingRes(template ResultCode res) :=
+ tr_RSPRO(RsproPDUchoice:{
+ removeMappingRes := {
+ result := res
+ }
+ });
+
+
+template (value) RsproPDU ts_RSPRO_ConfigClientIdReq(template (value) ClientSlot cslot) :=
+ ts_RSPRO(RsproPDUchoice: {
+ configClientIdReq := {
+ clientSlot := cslot
+ }
+ });
+template RsproPDU tr_RSPRO_ConfigClientIdReq(template ClientSlot cslot) :=
+ tr_RSPRO(RsproPDUchoice: {
+ configClientIdReq := {
+ clientSlot := cslot
+ }
+ });
+
+template (value) RsproPDU ts_RSPRO_ConfigClientIdRes(template (value) ResultCode res) :=
+ ts_RSPRO(RsproPDUchoice:{
+ configClientIdRes := {
+ result := res
+ }
+ });
+template RsproPDU tr_RSPRO_ConfigClientIdRes(template ResultCode res) :=
+ tr_RSPRO(RsproPDUchoice:{
+ configClientIdRes := {
+ result := res
+ }
+ });
+
+template (value) RsproPDU ts_RSPRO_ConfigClientBankReq(template (value) BankSlot bslot,
+ template (value) IpPort ip_port) :=
+ ts_RSPRO(RsproPDUchoice: {
+ configClientBankReq := {
+ bankSlot := bslot,
+ bankd := ip_port
+ }
+ });
+template RsproPDU tr_RSPRO_ConfigClientBankReq(template BankSlot bslot,
+ template IpPort ip_port) :=
+ tr_RSPRO(RsproPDUchoice: {
+ configClientBankReq := {
+ bankSlot := bslot,
+ bankd := ip_port
+ }
+ });
+
+template (value) RsproPDU ts_RSPRO_ConfigClientBankRes(template (value) ResultCode res) :=
+ ts_RSPRO(RsproPDUchoice:{
+ configClientBankRes := {
+ result := res
+ }
+ });
+template RsproPDU tr_RSPRO_ConfigClientBankRes(template ResultCode res) :=
+ tr_RSPRO(RsproPDUchoice:{
+ configClientBankRes := {
+ result := res
+ }
+ });
+
+
+}
diff --git a/remsim/RSRES.ttcn b/remsim/RSRES.ttcn
new file mode 100644
index 0000000..a988b66
--- /dev/null
+++ b/remsim/RSRES.ttcn
@@ -0,0 +1,110 @@
+module RSRES {
+
+/* Implementation of the REmote Sim RESt (RSRES) JSON data types in TTCN-3.
+ * (C) 2019 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.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+import from RSPRO all;
+
+/* resembles "enum remsim_server_client_fsm_state" */
+type enumerated ConnectionState {
+ INIT,
+ ESTABLISHED,
+ CONNECTED_CLIENT,
+ CONNECTED_BANKD
+};
+
+type record JsBank {
+ charstring peer,
+ ConnectionState state,
+ ComponentIdentity component_id,
+ BankId bankId,
+ SlotNumber numberOfSlots
+};
+type record of JsBank JsBanks;
+
+template JsBank tr_JsBank(template ConnectionState state,
+ template ComponentIdentity comp_id,
+ template BankId bank_id,
+ template SlotNumber num_of_slots) := {
+ peer := ?,
+ state := state,
+ component_id := comp_id,
+ bankId := bank_id,
+ numberOfSlots := num_of_slots
+}
+
+type record JsClient {
+ charstring peer,
+ ConnectionState state,
+ ComponentIdentity component_id
+};
+type record of JsClient JsClients;
+
+template JsClient tr_JsClient(template ConnectionState state,
+ template ComponentIdentity comp_id) := {
+ peer := ?,
+ state := state,
+ component_id := comp_id
+}
+
+/* resembles "enum slot_mapping_state" */
+type enumerated SlotmapState {
+ NEW,
+ UNACKNOWLEDGED,
+ ACTIVE,
+ DELETE_REQ,
+ DELETING
+};
+
+type record JsSlotmap {
+ BankSlot bank,
+ ClientSlot client,
+ SlotmapState state optional
+};
+type record of JsSlotmap JsSlotmaps;
+
+template (value) JsSlotmap ts_JsSlotmap(template (value) BankSlot bslot,
+ template (value) ClientSlot cslot,
+ template (omit) SlotmapState state := omit) := {
+ bank := bslot,
+ client := cslot,
+ state := state
+}
+template JsSlotmap tr_JsSlotmap(template BankSlot bslot,
+ template ClientSlot cslot,
+ template SlotmapState state := ?) := {
+ bank := bslot,
+ client := cslot,
+ state := state
+}
+
+
+
+/* root JSON type expressing what remsim-server can return */
+type record JsRoot {
+ JsClients clients optional,
+ JsBanks banks optional,
+ JsSlotmaps slotmaps optional
+};
+
+external function f_enc_JsRoot(in JsRoot inp) return octetstring
+ with { extension "prototype(convert) encode(JSON)" }
+external function f_dec_JsRoot(in octetstring inp) return JsRoot
+ with { extension "prototype(convert) decode(JSON)" }
+
+external function f_enc_JsSlotmap(in JsSlotmap inp) return octetstring
+ with { extension "prototype(convert) encode(JSON)" }
+external function f_dec_JsSlotmap(in octetstring inp) return JsSlotmap
+ with { extension "prototype(convert) decode(JSON)" }
+
+
+
+} with { encode "JSON" }
diff --git a/remsim/RemsimBankd_Tests.ttcn b/remsim/RemsimBankd_Tests.ttcn
new file mode 100644
index 0000000..bbdea6d
--- /dev/null
+++ b/remsim/RemsimBankd_Tests.ttcn
@@ -0,0 +1,298 @@
+module RemsimBankd_Tests {
+
+/* Integration Tests for osmo-remsim-bankd
+ * (C) 2019 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.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This test suite tests osmo-remsim-bankd by attaching to the external interfaces
+ * such as RSPRO for simulated clients + server.
+ */
+
+import from Osmocom_Types all;
+import from IPA_Emulation all;
+import from Misc_Helpers all;
+
+import from RSPRO all;
+import from RSRES all;
+import from RSPRO_Types all;
+import from RSPRO_Server all;
+import from REMSIM_Tests all;
+
+modulepar {
+ integer mp_bank_id := 1;
+ integer mp_num_slots := 8;
+}
+
+/* We implement a RSPRO server to simulate the remsim-server and a
+ RSPRO client to simulate a remsim-client connecting to bankd */
+type component bankd_test_CT extends rspro_server_CT, rspro_client_CT {
+}
+
+private function f_init(boolean start_client := false) runs on bankd_test_CT {
+ var ComponentIdentity srv_comp_id := valueof(ts_CompId(remsimServer, "ttcn-server"));
+
+ f_rspro_srv_init(0, mp_server_ip, mp_server_port, srv_comp_id);
+
+ if (start_client) {
+ f_init_client(0);
+ }
+}
+
+private function f_init_client(integer i := 0) runs on rspro_client_CT {
+ var ComponentIdentity clnt_comp_id := valueof(ts_CompId(remsimClient, "ttcn-client"));
+ f_rspro_init(rspro[0], mp_bankd_ip, mp_bankd_port, clnt_comp_id, 0);
+ rspro[0].rspro_client_slot := { clientId := 23+i, slotNr := 0 };
+}
+
+
+
+/* Test if the bankd disconnects the TCP/IPA session if we don't respond to connectBankReq */
+testcase TC_connectBankReq_timeout() runs on bankd_test_CT {
+ timer T := 20.0;
+ f_init();
+
+ f_rspro_srv_exp(tr_RSPRO_ConnectBankReq(?, ?, ?));
+ T.start;
+ alt {
+ [] RSPRO_SRV[0].receive(ASP_IPA_Event:{up_down := ASP_IPA_EVENT_DOWN}) {
+ setverdict(pass);
+ }
+ [] RSPRO_SRV[0].receive { repeat; }
+ [] T.timeout {
+ setverdict(fail, "Timeout waiting for disconnect");
+ }
+ }
+}
+
+/* accept an inbound connection from bankd to simulated server */
+testcase TC_connectBankReq() runs on bankd_test_CT {
+ f_init();
+
+ as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots);
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
+}
+
+/* attempt to create a mapping */
+testcase TC_createMapping() runs on bankd_test_CT {
+ f_init();
+ as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots);
+ var BankSlot bs := { bankId := mp_bank_id, slotNr := 0 };
+ var ClientSlot cs := { clientId := 23, slotNr := 42 };
+ f_rspro_srv_create_slotmap(cs, bs);
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
+}
+
+/* attempt to create a mapping for a slot that already has a mapping */
+testcase TC_createMapping_busySlot() runs on bankd_test_CT {
+ f_init();
+ as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots);
+ var BankSlot bs := { bankId := mp_bank_id, slotNr := 0 };
+ var ClientSlot cs := { clientId := 23, slotNr := 42 };
+ f_rspro_srv_create_slotmap(cs, bs);
+ f_rspro_srv_create_slotmap(cs, bs, exp_res := illegalSlotId);
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
+}
+
+/* attempt to create a mapping for an out-of-range slot number */
+testcase TC_createMapping_invalidSlot() runs on bankd_test_CT {
+ f_init();
+ as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots);
+ var BankSlot bs := { bankId := mp_bank_id, slotNr := 200 };
+ var ClientSlot cs := { clientId := 23, slotNr := 42 };
+ f_rspro_srv_create_slotmap(cs, bs, exp_res := illegalSlotId);
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
+}
+
+/* attempt to create a mapping for an invalid bankID */
+testcase TC_createMapping_invalidBank() runs on bankd_test_CT {
+ f_init();
+ as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots);
+ var BankSlot bs := { bankId := 200, slotNr := 0 };
+ var ClientSlot cs := { clientId := 23, slotNr := 42 };
+ f_rspro_srv_create_slotmap(cs, bs, exp_res := illegalBankId);
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
+}
+
+/* attempt to remove a non-existant mapping */
+testcase TC_removeMapping_unknownMap() runs on bankd_test_CT {
+ f_init();
+ as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots);
+ var BankSlot bs := { bankId := mp_bank_id, slotNr := 0 };
+ var ClientSlot cs := { clientId := 23, slotNr := 42 };
+ f_rspro_srv_remove_slotmap(cs, bs, exp_res := unknownSlotmap);
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
+}
+
+/* add and then remove a mapping, expect both to be successful */
+testcase TC_removeMapping() runs on bankd_test_CT {
+ f_init();
+ as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots);
+ var BankSlot bs := { bankId := mp_bank_id, slotNr := 0 };
+ var ClientSlot cs := { clientId := 23, slotNr := 42 };
+ f_rspro_srv_create_slotmap(cs, bs);
+ f_rspro_srv_remove_slotmap(cs, bs);
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
+}
+
+/* connect from client to bankd without specifying a clientId */
+testcase TC_clientConnect_missingSlot() runs on bankd_test_CT {
+ f_init_client(0);
+ RSPRO[0].send(ts_RSPRO_ConnectClientReq(rspro[0].rspro_id, omit));
+ f_rspro_exp(tr_RSPRO_ConnectClientRes(?, ResultCode:illegalClientId), 0);
+ f_rspro_exp_disconnect(0);
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
+}
+
+/* connect from client to bankd using a clientId for which bankd has no map */
+testcase TC_clientConnect_unknown() runs on bankd_test_CT {
+ f_init_client(0);
+ f_rspro_connect_client(0, tr_Status_ok_or_nocard);
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
+}
+
+/* connect from client to bankd using a clientSlot for which bankd has no map */
+
+
+/* first connect client, then later add matching mapping from server */
+testcase TC_clientConnect_createMapping() runs on bankd_test_CT {
+ f_init_client(0);
+ f_rspro_connect_client(0, tr_Status_ok_or_nocard);
+
+ f_init();
+ as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots);
+
+ var BankSlot bs := { bankId := mp_bank_id, slotNr := 0 };
+ f_rspro_srv_create_slotmap(rspro[0].rspro_client_slot, bs);
+ f_sleep(10.0);
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
+}
+
+
+/* first add mapping, then connect matching client */
+testcase TC_createMapping_clientConnect() runs on bankd_test_CT {
+ /* FIXME: this would only be done in f_init_client(), but we need it before */
+ rspro[0].rspro_client_slot := { clientId := 23+0, slotNr := 0 };
+
+ f_init();
+ as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots);
+
+ var BankSlot bs := { bankId := mp_bank_id, slotNr := 0 };
+ f_rspro_srv_create_slotmap(rspro[0].rspro_client_slot, bs);
+
+ f_sleep(1.0);
+
+ f_init_client(0);
+ f_rspro_connect_client(0, tr_Status_ok_or_nocard);
+ /* FIXME: how to determine that bank correctly mapped us */
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
+}
+
+
+
+/* add mapping, connect matching client, disconnect + reconnect */
+testcase TC_createMapping_clientReconnect() runs on bankd_test_CT {
+ /* FIXME: this would only be done in f_init_client(), but we need it before */
+ rspro[0].rspro_client_slot := { clientId := 23+0, slotNr := 0 };
+
+ f_init();
+ as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots);
+
+ var BankSlot bs := { bankId := mp_bank_id, slotNr := 0 };
+ f_rspro_srv_create_slotmap(rspro[0].rspro_client_slot, bs);
+
+ f_sleep(1.0);
+
+ f_init_client(0);
+ f_rspro_connect_client(0, tr_Status_ok_or_nocard);
+ /* TODO: works only with empty slot, as setAtrReq isn't handled */
+ /* FIXME: how to determine that bank correctly mapped us */
+ f_sleep(5.0);
+ f_rspro_fini(rspro[0], 0);
+
+ f_init_client(0);
+ f_rspro_connect_client(0, tr_Status_ok_or_nocard);
+ /* FIXME: how to determine that bank correctly mapped us */
+ f_sleep(5.0);
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
+}
+
+
+
+/* remove mapping while client is connected */
+testcase TC_removeMapping_connected() runs on bankd_test_CT {
+ f_init_client(0);
+ f_rspro_connect_client(0, tr_Status_ok_or_nocard);
+ /* TODO: works only with empty slot, as setAtrReq isn't handled */
+
+ f_init();
+ as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots);
+
+ var BankSlot bs := { bankId := mp_bank_id, slotNr := 0 };
+ f_rspro_srv_create_slotmap(rspro[0].rspro_client_slot, bs);
+ /* FIXME: how to determine that bank correctly mapped us */
+ f_sleep(5.0);
+ f_rspro_srv_remove_slotmap(rspro[0].rspro_client_slot, bs);
+ f_rspro_exp_disconnect(0);
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
+}
+
+/* first add mapping, then connect matching client and exchange some TPDUs */
+testcase TC_createMapping_exchangeTPDU() runs on bankd_test_CT {
+ /* FIXME: this would only be done in f_init_client(), but we need it before */
+ rspro[0].rspro_client_slot := { clientId := 23+0, slotNr := 0 };
+
+ f_init();
+ as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots);
+
+ var BankSlot bs := { bankId := mp_bank_id, slotNr := 0 };
+ f_rspro_srv_create_slotmap(rspro[0].rspro_client_slot, bs);
+
+ f_sleep(1.0);
+
+ f_init_client(0);
+ f_rspro_connect_client(0, ok);
+ /* FIXME: how to determine that bank correctly mapped us */
+ f_rspro_exp(tr_RSPRO_SetAtrReq(rspro[0].rspro_client_slot, ?));
+
+ var TpduFlags f := {tpduHeaderPresent:=true, finalPart:=true, procByteContinueTx:=false,
+ procByteContinueRx:=false};
+ for (var integer i := 0; i < 10; i:=i+1) {
+ f_rspro_xceive_mdm2card(0, bs, 'A0A40000023F00'O, f);
+ }
+ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass);
+}
+
+
+
+control {
+ execute( TC_connectBankReq_timeout() );
+ execute( TC_connectBankReq() );
+
+ execute( TC_createMapping() );
+ execute( TC_createMapping_busySlot() );
+ execute( TC_createMapping_invalidSlot() );
+ execute( TC_createMapping_invalidBank() );
+
+ execute( TC_removeMapping_unknownMap() );
+ execute( TC_removeMapping() );
+
+ execute( TC_clientConnect_missingSlot() );
+ execute( TC_clientConnect_unknown() );
+ execute( TC_clientConnect_createMapping() );
+ execute( TC_createMapping_clientConnect() );
+ execute( TC_createMapping_clientReconnect() );
+ execute( TC_removeMapping_connected() );
+
+ execute( TC_createMapping_exchangeTPDU() );
+}
+
+
+
+
+
+}
diff --git a/remsim/RemsimClient_Tests.ttcn b/remsim/RemsimClient_Tests.ttcn
new file mode 100644
index 0000000..d5520ab
--- /dev/null
+++ b/remsim/RemsimClient_Tests.ttcn
@@ -0,0 +1,118 @@
+module RemsimClient_Tests {
+
+/* Integration Tests for osmo-remsim-client
+ * (C) 2019 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.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This test suite tests osmo-remsim-client by attaching to the external interfaces.
+ */
+
+import from Osmocom_Types all;
+import from IPA_Emulation all;
+
+import from RSPRO all;
+import from RSPRO_Types all;
+import from RSPRO_Server all;
+import from REMSIM_Tests all;
+
+type component client_test_CT extends rspro_server_CT {
+ var ComponentIdentity g_srv_comp_id, g_bankd_comp_id;
+};
+
+private function f_init() runs on client_test_CT {
+ g_srv_comp_id := valueof(ts_CompId(remsimServer, "ttcn-server"));
+ g_bankd_comp_id := valueof(ts_CompId(remsimBankd, "ttcn-bankd"));
+
+ f_rspro_srv_init(0, mp_server_ip, mp_server_port, g_srv_comp_id);
+ f_rspro_srv_init(1, mp_bankd_ip, mp_bankd_port, g_bankd_comp_id, exp_connect := false);
+}
+
+
+/* ConnectClientReq from client to remsim-server */
+testcase TC_srv_connectClient() runs on client_test_CT {
+ f_init();
+ as_connectClientReq();
+ setverdict(pass);
+ f_sleep(1.0);
+}
+
+/* ConnectClientReq from client to remsim-server */
+testcase TC_srv_connectClient_reject() runs on client_test_CT {
+ f_init();
+ as_connectClientReq(res := illegalClientId);
+ /* expect disconnect by client */
+ RSPRO_SRV[0].receive(t_ASP_IPA_EVT_UD(ASP_IPA_EVENT_DOWN));
+ setverdict(pass);
+ f_sleep(1.0);
+}
+
+/* ConnectClientReq from client to remsim-server */
+testcase TC_srv_connectClient_configClientBank() runs on client_test_CT {
+ var BankSlot bslot := { 1, 0 };
+ f_init();
+ as_connectClientReq();
+ f_rspro_config_client_bank(bslot, ts_IpPort(ts_IPv4(mp_bankd_ip), mp_bankd_port));
+ f_rspro_srv_exp_connect(1);
+ as_connectClientReq(i := 1);
+ setverdict(pass);
+ f_sleep(1.0);
+}
+
+/* Test if client re-connects to server after connection is lost */
+testcase TC_srv_reconnect() runs on client_test_CT {
+ var BankSlot bslot := { 1, 0 };
+ f_init();
+ as_connectClientReq();
+
+ /* disconnect the client from server and expect re-establish + re-connect */
+ f_rspro_srv_fini(0);
+ f_rspro_srv_init(0, mp_server_ip, mp_server_port, g_srv_comp_id, exp_connect := true);
+ as_connectClientReq(i := 0);
+
+ setverdict(pass);
+ f_sleep(1.0);
+}
+
+/* Test if client re-connects to bank after connection is lost */
+testcase TC_bank_reconnect() runs on client_test_CT {
+ var BankSlot bslot := { 1, 0 };
+ f_init();
+ as_connectClientReq();
+ f_rspro_config_client_bank(bslot, ts_IpPort(ts_IPv4(mp_bankd_ip), mp_bankd_port));
+ f_rspro_srv_exp_connect(1);
+ as_connectClientReq(i := 1);
+
+ /* disconnect the client from bankd and expect re-establish + re-connect */
+ f_rspro_srv_fini(1);
+ f_rspro_srv_init(1, mp_bankd_ip, mp_bankd_port, g_bankd_comp_id, exp_connect := true);
+ as_connectClientReq(i := 1);
+
+ setverdict(pass);
+ f_sleep(1.0);
+}
+
+/* TODO:
+ * send a configClientBankIpReq and change the bank of an active client
+ * send a configClientBankSlotReq and chagne the bank slot of an active client
+ * test keepalive mechanism: do we get IPA PING?
+ * test keepalive mechanism: do we see disconnect+reconnect if we don't respond to IPA PING?
+ * test actual APDU transfers
+ * test messages in invalid state, e.g. APDUs before we're connected to a bank
+ * test messages on server connection which are only permitted on bankd connection
+ */
+
+control {
+ execute( TC_srv_connectClient() );
+ execute( TC_srv_connectClient_reject() );
+ execute( TC_srv_connectClient_configClientBank() );
+ execute( TC_srv_reconnect() );
+ execute( TC_bank_reconnect() );
+}
+
+
+}
diff --git a/remsim/RemsimServer_Tests.ttcn b/remsim/RemsimServer_Tests.ttcn
new file mode 100644
index 0000000..f0589ef
--- /dev/null
+++ b/remsim/RemsimServer_Tests.ttcn
@@ -0,0 +1,485 @@
+module RemsimServer_Tests {
+
+/* Integration Tests for osmo-remsim-server
+ * (C) 2019 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.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This test suite tests osmo-remsim-server by attaching to the external interfaces
+ * such as RSPRO for simulated clients + bankds and RSRES (REST backend interface).
+ */
+
+import from Osmocom_Types all;
+
+import from RSPRO all;
+import from RSRES all;
+import from RSPRO_Types all;
+import from REMSIM_Tests all;
+
+import from IPA_Emulation all;
+
+import from HTTPmsg_Types all;
+import from HTTPmsg_PortType all;
+import from JSON_Types all;
+
+type component http_CT {
+ port HTTPmsg_PT HTTP;
+ var charstring g_http_host;
+ var integer g_http_port;
+};
+
+function f_http_init(charstring host, integer http_port) runs on http_CT {
+ map(self:HTTP, system:HTTP);
+ g_http_host := host;
+ g_http_port := http_port;
+}
+
+template (value) Connect ts_HTTP_Connect(template (value) charstring hostname,
+ template (value) integer http_port := 80,
+ template (value) boolean use_ssl := false) := {
+ hostname := hostname,
+ portnumber := http_port,
+ use_ssl := use_ssl
+}
+template (value) Close ts_HTTP_Close := { client_id := omit };
+
+template (value) HeaderLines ts_HTTP_Header(charstring body) := {
+ { header_name := "Content-Type", header_value := "application/json" },
+ { header_name := "Content-Length", header_value := int2str(lengthof(body)) }
+}
+
+template (value) HTTPMessage ts_HTTP_Req(charstring url,
+ charstring method := "GET",
+ charstring body := "",
+ integer v_maj := 1, integer v_min := 1) := {
+ request := {
+ client_id := omit,
+ method := method,
+ uri := url,
+ version_major := v_maj,
+ version_minor := v_min,
+ header := ts_HTTP_Header(body),
+ body := body
+ }
+}
+
+template HTTPMessage tr_HTTP_Resp(template integer sts := ?) := {
+ response := {
+ client_id := ?,
+ version_major := ?,
+ version_minor := ?,
+ statuscode := sts,
+ statustext := ?,
+ header := ?,
+ body := ?
+ }
+};
+
+template HTTPMessage tr_HTTP_Resp2xx := tr_HTTP_Resp((200..299));
+
+/* run a HTTP request and return the response */
+function f_http_transact(charstring url, charstring method := "GET",
+ charstring body := "", template HTTPMessage exp := tr_HTTP_Resp2xx)
+runs on http_CT return HTTPMessage {
+ var HTTPMessage resp;
+ timer T := 2.0;
+
+ HTTP.send(ts_HTTP_Connect(g_http_host, g_http_port));
+ //HTTP.receive(Connect_result:?);
+ HTTP.send(ts_HTTP_Req(url, method, body));
+ T.start;
+ alt {
+ [] HTTP.receive(exp) -> value resp {
+ setverdict(pass);
+ }
+ [] HTTP.receive(tr_HTTP_Resp) -> value resp {
+ setverdict(fail, "Unexpected HTTP response ", resp);
+ }
+ [] T.timeout {
+ setverdict(fail, "Timeout waiting for HTTP response");
+ self.stop;
+ }
+ }
+ HTTP.send(ts_HTTP_Close);
+ return resp;
+}
+
+/* run a HTTP GET on specified URL expecting json in RSRES format as response */
+function f_rsres_get(charstring url, template integer exp_sts := 200)
+runs on http_CT return JsRoot {
+ var HTTPMessage http_resp;
+ http_resp := f_http_transact(url, exp := tr_HTTP_Resp(exp_sts));
+ return f_dec_JsRoot(char2oct(http_resp.response.body));
+}
+
+/* run a HTTP PUT to add a new slotmap to the remsim-server */
+function f_rsres_post_slotmap(JsSlotmap slotmap, template integer exp_sts := 201)
+runs on http_CT return HTTPResponse {
+ var charstring body := oct2char(f_enc_JsSlotmap(slotmap));
+ var HTTPMessage http_resp;
+ http_resp := f_http_transact(url := "/api/backend/v1/slotmaps", method := "POST",
+ body := body, exp := tr_HTTP_Resp(exp_sts))
+ return http_resp.response;
+}
+
+/* run a HTTP PUT to add a new slotmap to the remsim-server */
+function f_rsres_post_reset(template integer exp_sts := 200)
+runs on http_CT return HTTPResponse {
+ var HTTPMessage http_resp;
+ http_resp := f_http_transact(url := "/api/backend/v1/global-reset", method := "POST",
+ body := "", exp := tr_HTTP_Resp(exp_sts))
+ return http_resp.response;
+}
+
+
+/* run a HTTP DELETE to remove a slotmap from te remsim-server */
+function f_rsres_delete_slotmap(BankSlot bs, template integer exp_sts := 200)
+runs on http_CT return HTTPResponse {
+ var HTTPMessage http_resp;
+ var integer slotmap_id := bs.bankId * 65536 + bs.slotNr;
+ http_resp := f_http_transact(url := "/api/backend/v1/slotmaps/" & int2str(slotmap_id),
+ method := "DELETE", exp := tr_HTTP_Resp(exp_sts));
+ return http_resp.response;
+}
+
+
+function f_rsres_init() runs on http_CT {
+ f_http_init(mp_server_ip, mp_rsres_port);
+ f_rsres_post_reset();
+}
+
+type component test_CT extends rspro_client_CT, http_CT {
+};
+
+
+testcase TC_connect_and_nothing() runs on rspro_client_CT {
+ var ComponentIdentity rspro_id := valueof(ts_CompId(remsimClient, "foobar"));
+ timer T := 20.0;
+
+ f_rspro_init(rspro[0], mp_server_ip, mp_server_port, rspro_id, 0);
+ T.start;
+ /* expect that we're disconnected if we never send a ConnectClientReq */
+ alt {
+ [] RSPRO[0].receive(t_ASP_IPA_EVT_UD(ASP_IPA_EVENT_ID_ACK)) { repeat; }
+ [] RSPRO[0].receive(t_ASP_IPA_EVT_UD(ASP_IPA_EVENT_DOWN)) {
+ setverdict(pass);
+ }
+ [] T.timeout {
+ setverdict(fail, "Timeout waiting for disconnect");
+ }
+ }
+}
+
+testcase TC_connect_client() runs on test_CT {
+ var ComponentIdentity rspro_id := valueof(ts_CompId(remsimClient, "foobar"));
+ var JsRoot js;
+
+ f_rsres_init();
+ f_rspro_init(rspro[0], mp_server_ip, mp_server_port, rspro_id, 0);
+ rspro[0].rspro_client_slot := valueof(ts_ClientSlot(3,4));
+
+ js := f_rsres_get("/api/backend/v1/clients");
+ if (not match(js.clients, JsClients:{})) {
+ setverdict(fail, "Initial state not empty");
+ mtc.stop;
+ }
+ f_rspro_connect_client(0);
+ js := f_rsres_get("/api/backend/v1/clients");
+ if (not match(js.clients[0], tr_JsClient(CONNECTED_CLIENT, rspro[0].rspro_id))) {
+ setverdict(fail, "Non-matching JSON response");
+ mtc.stop;
+ }
+ //as_rspro_cfg_client_id(0, cslot);
+ setverdict(pass);
+}
+
+testcase TC_connect_bank() runs on test_CT {
+ var ComponentIdentity rspro_id := valueof(ts_CompId(remsimBankd, "foobar"));
+ var JsRoot js;
+
+ f_rsres_init();
+ f_rspro_init(rspro[0], mp_server_ip, mp_server_port, rspro_id, 0);
+ rspro[0].rspro_bank_id := 1;
+ rspro[0].rspro_bank_nslots := 8;
+
+ js := f_rsres_get("/api/backend/v1/banks");
+ if (not match(js.banks, JsBanks:{})) {
+ setverdict(fail, "Initial state not empty");
+ mtc.stop;
+ }
+ f_rspro_connect_client(0);
+ js := f_rsres_get("/api/backend/v1/banks");
+ if (not match(js.banks[0], tr_JsBank(CONNECTED_BANKD, rspro[0].rspro_id, rspro[0].rspro_bank_id,
+ rspro[0].rspro_bank_nslots))) {
+ setverdict(fail, "Non-matching JSON response");
+ mtc.stop;
+ }
+ setverdict(pass);
+}
+
+function f_ensure_slotmaps(template JsSlotmaps maps)
+runs on http_CT {
+ var JsRoot js;
+
+ /* check that it is actually added */
+ js := f_rsres_get("/api/backend/v1/slotmaps");
+ if (match(js.slotmaps, maps)) {
+ setverdict(pass);
+ } else {
+ setverdict(fail, "Unexpected slotmaps: ", js);
+ }
+}
+
+/* verify that exactly only one slotmap exists (the specified one) */
+function f_ensure_slotmap_exists_only(template ClientSlot cslot, template BankSlot bslot,
+ template SlotmapState state := ?)
+runs on http_CT {
+ f_ensure_slotmaps({ tr_JsSlotmap(bslot, cslot, state) } );
+}
+
+/* verify that exactly only one slotmap exists (possibly among others) */
+function f_ensure_slotmap_exists(template ClientSlot cslot, template BankSlot bslot,
+ template SlotmapState state := ?)
+runs on http_CT {
+ f_ensure_slotmaps({ *, tr_JsSlotmap(bslot, cslot, state), * } );
+}
+
+
+/* test adding a single slotmap */
+testcase TC_slotmap_add() runs on test_CT {
+ f_rsres_init();
+
+ var JsSlotmap sm := valueof(ts_JsSlotmap(ts_BankSlot(1,2), ts_ClientSlot(3,4)));
+ var HTTPResponse res := f_rsres_post_slotmap(sm);
+
+ /* check that it is actually added */
+ f_ensure_slotmap_exists_only(sm.client, sm.bank, NEW);
+}
+
+/* test adding a slotmap and then connecting a client + bankd */
+testcase TC_slotmap_add_conn_cl_b() runs on test_CT {
+ /* Simulate one client */
+ var ComponentIdentity rspro_id := valueof(ts_CompId(remsimClient, testcasename()));
+ f_rspro_init(rspro[0], mp_server_ip, mp_server_port, rspro_id, 0);
+ rspro[0].rspro_client_slot := valueof(ts_ClientSlot(3,4));
+
+ /* Simulate one bankd */
+ var BankSlot bslot := valueof(ts_BankSlot(1,2));
+ var ComponentIdentity rspro_bank_id := valueof(ts_CompId(remsimBankd, testcasename()));
+ f_rspro_init(rspro[1], mp_server_ip, mp_server_port, rspro_bank_id, 1);
+ rspro[1].rspro_bank_id := bslot.bankId;
+ rspro[1].rspro_bank_nslots := 8
+
+ f_rsres_init();
+ var JsSlotmap sm := valueof(ts_JsSlotmap(bslot, rspro[0].rspro_client_slot));
+ var HTTPResponse res;
+
+ /* 1) Create a new slotmap via HTTP */
+ res := f_rsres_post_slotmap(sm);
+
+ /* 2) verify that the slotmap exists and is NEW */
+ f_ensure_slotmap_exists_only(sm.client, sm.bank, NEW);
+
+ /* 3) connect a client for that slotmap */
+ f_rspro_connect_client(0);
+
+ /* 4) connect a bankd for that slotmap */
+ f_rspro_connect_client(1);
+
+ /* 5) verify that the slotmap exists and is UNACKNOWLEDGED */
+ f_ensure_slotmap_exists_only(sm.client, sm.bank, UNACKNOWLEDGED);
+
+ /* 6) expect bankd to receive that mapping */
+ as_rspro_create_mapping(1, sm.client, sm.bank);
+
+ /* 7) verify that the slotmap exists and is ACTIVE */
+ f_ensure_slotmap_exists_only(sm.client, sm.bank, ACTIVE);
+
+ /* 8) expect the client to be configured with bankd side settings */
+ as_rspro_cfg_client_bank(0, bslot, ?);
+}
+
+/* test connecting a client and later adding a slotmap for it */
+testcase TC_conn_cl_b_slotmap_add() runs on test_CT {
+ /* Simulate one client */
+ var ComponentIdentity rspro_id := valueof(ts_CompId(remsimClient, testcasename()));
+ f_rspro_init(rspro[0], mp_server_ip, mp_server_port, rspro_id, 0);
+ rspro[0].rspro_client_slot := valueof(ts_ClientSlot(3,4));
+
+ /* Simulate one bankd */
+ var BankSlot bslot := valueof(ts_BankSlot(1,2));
+ var ComponentIdentity rspro_bank_id := valueof(ts_CompId(remsimBankd, testcasename()));
+ f_rspro_init(rspro[1], mp_server_ip, mp_server_port, rspro_bank_id, 1);
+ rspro[1].rspro_bank_id := bslot.bankId;
+ rspro[1].rspro_bank_nslots := 8
+
+ f_rsres_init();
+ var JsSlotmap sm := valueof(ts_JsSlotmap(bslot, rspro[0].rspro_client_slot));
+ var HTTPResponse res;
+
+ /* 1) connect a client for that slotmap */
+ f_rspro_connect_client(0);
+
+ /* 2) Create a new slotmap via HTTP */
+ res := f_rsres_post_slotmap(sm);
+
+ /* 3) verify that the slotmap exists and is NEW */
+ f_ensure_slotmap_exists_only(sm.client, sm.bank, NEW);
+
+ /* 4) connect a bankd for that slotmap */
+ f_rspro_connect_client(1);
+
+ /* 5) verify that the slotmap exists and is UNACKNOWLEDGED */
+ f_ensure_slotmap_exists_only(sm.client, sm.bank, UNACKNOWLEDGED);
+
+ /* 6) expect bankd to receive that mapping */
+ as_rspro_create_mapping(1, sm.client, sm.bank);
+
+ /* 7) verify that the slotmap exists and is ACTIVE */
+ f_ensure_slotmap_exists_only(sm.client, sm.bank, ACTIVE);
+
+ /* 8) expect the client to be configured with bankd IP/port */
+ as_rspro_cfg_client_bank(0, bslot, ?);
+}
+
+/* simple delete of a 'NEW' slotmap */
+testcase TC_slotmap_del_new() runs on test_CT {
+ f_rsres_init();
+
+ var JsSlotmap sm := valueof(ts_JsSlotmap(ts_BankSlot(1,2), ts_ClientSlot(3,4)));
+ var HTTPResponse res := f_rsres_post_slotmap(sm);
+ log(res);
+ res := f_rsres_delete_slotmap(sm.bank);
+ log(res);
+}
+
+/* simple delete of a 'UNACKNOWLEDGED' slotmap */
+testcase TC_slotmap_del_unack() runs on test_CT {
+ var ComponentIdentity rspro_id := valueof(ts_CompId(remsimBankd, testcasename()));
+ f_rspro_init(rspro[0], mp_server_ip, mp_server_port, rspro_id, 0);
+ rspro[0].rspro_bank_id := 1;
+ rspro[0].rspro_bank_nslots := 8;
+
+ f_rsres_init();
+ var JsSlotmap sm := valueof(ts_JsSlotmap(ts_BankSlot(1,2), ts_ClientSlot(3,4)));
+ var HTTPResponse res;
+
+ /* Create a new slotmap via HTTP */
+ res := f_rsres_post_slotmap(sm);
+
+ /* verify that the slotmap exists and is NEW */
+ f_ensure_slotmap_exists_only(sm.client, sm.bank, NEW);
+
+ /* connect a bankd for that slotmap */
+ f_rspro_connect_client(0);
+
+ /* expect the slotmap to be pushed to bank but don't ACK it */
+ f_rspro_exp(tr_RSPRO_CreateMappingReq(sm.client, sm.bank));
+
+ /* verify that the slotmap exists and is UNACKNOWLEDGED */
+ f_ensure_slotmap_exists_only(sm.client, sm.bank, UNACKNOWLEDGED);
+
+ /* delete the slotmap via REST */
+ res := f_rsres_delete_slotmap(sm.bank);
+
+ /* verify the slotmap is gone */
+ f_ensure_slotmaps({});
+}
+
+/* simple delete of a 'ACTIVE' slotmap */
+testcase TC_slotmap_del_active() runs on test_CT {
+ var ComponentIdentity rspro_id := valueof(ts_CompId(remsimBankd, testcasename()));
+ f_rspro_init(rspro[0], mp_server_ip, mp_server_port, rspro_id, 0);
+ rspro[0].rspro_bank_id := 1;
+ rspro[0].rspro_bank_nslots := 8;
+
+ f_rsres_init();
+ var JsSlotmap sm := valueof(ts_JsSlotmap(ts_BankSlot(1,2), ts_ClientSlot(3,4)));
+ var HTTPResponse res;
+
+ /* Create a new slotmap via HTTP */
+ res := f_rsres_post_slotmap(sm);
+
+ /* verify that the slotmap exists and is NEW */
+ f_ensure_slotmap_exists_only(sm.client, sm.bank, NEW);
+
+ /* connect a bankd for that slotmap */
+ f_rspro_connect_client(0);
+
+ /* expect the slotmap to be pushed to bank and ACK it */
+ as_rspro_create_mapping(0, sm.client, sm.bank);
+
+ /* verify that the slotmap exists and is ACTIVE */
+ f_ensure_slotmap_exists_only(sm.client, sm.bank, ACTIVE);
+
+ f_sleep(1.0);
+
+ /* delete the slotmap via REST */
+ res := f_rsres_delete_slotmap(sm.bank);
+
+ /* verify the slotmap is gone from REST interface immediately */
+ f_ensure_slotmaps({});
+
+ /* verify the slotmap is removed from bankd */
+ as_rspro_remove_mapping(0, sm.client, sm.bank);
+}
+
+
+/* Add a slotmap to a currently active bank */
+testcase TC_slotmap_add_active_bank() runs on test_CT {
+ var ComponentIdentity rspro_id := valueof(ts_CompId(remsimBankd, testcasename()));
+ f_rspro_init(rspro[0], mp_server_ip, mp_server_port, rspro_id, 0);
+ rspro[0].rspro_bank_id := 1;
+ rspro[0].rspro_bank_nslots := 8;
+
+ f_rsres_init();
+ var JsSlotmap sm := valueof(ts_JsSlotmap(ts_BankSlot(1,2), ts_ClientSlot(3,4)));
+ var HTTPResponse res;
+
+ /* connect a bankd for that slotmap */
+ f_rspro_connect_client(0);
+
+ /* Create a new slotmap via HTTP */
+ res := f_rsres_post_slotmap(sm);
+
+ /* expect the slotmap to be pushed to bank and ACK it */
+ as_rspro_create_mapping(0, sm.client, sm.bank);
+
+ /* verify that the slotmap exists and is ACTIVE */
+ f_ensure_slotmap_exists_only(sm.client, sm.bank, ACTIVE);
+}
+
+
+
+
+/* TODO
+ * - add slotmap, then connect matching client (see if slotmap is sent; check slotmap state)
+ * - connect client w/slotmap; delete slotmap via REST (see if it is deleted)
+ * - don't acknowledge delete from client, disconnect client (see if slotmap is deleted)
+ * - delete non-existing slotmap via REST
+ * - create slotmap with integers out of range via REST
+
+ * - connect from unknown client (name not known, no clientId provisioned?
+ * - add client name/ID mappings from REST API?
+ */
+
+
+control {
+ execute( TC_connect_and_nothing() );
+ execute( TC_connect_client() );
+ execute( TC_connect_bank() );
+ execute( TC_slotmap_add() );
+ execute( TC_slotmap_add_conn_cl_b() );
+ execute( TC_conn_cl_b_slotmap_add() );
+ execute( TC_slotmap_del_new() );
+ execute( TC_slotmap_del_unack() );
+ execute( TC_slotmap_del_active() );
+ execute( TC_slotmap_add_active_bank() );
+}
+
+
+}
diff --git a/remsim/gen_links.sh b/remsim/gen_links.sh
new file mode 100755
index 0000000..7c234ec
--- /dev/null
+++ b/remsim/gen_links.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+BASEDIR=../deps
+
+. ../gen_links.sh.inc
+
+DIR=$BASEDIR/titan.Libraries.TCCUsefulFunctions/src
+FILES="TCCInterface_Functions.ttcn TCCConversion_Functions.ttcn TCCConversion.cc TCCInterface.cc TCCInterface_ip.h"
+gen_links $DIR $FILES
+
+DIR=$BASEDIR/titan.TestPorts.Common_Components.Socket-API/src
+FILES="Socket_API_Definitions.ttcn"
+gen_links $DIR $FILES
+
+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
+
+DIR=$BASEDIR/titan.TestPorts.TELNETasp/src
+FILES="TELNETasp_PT.cc TELNETasp_PT.hh TELNETasp_PortType.ttcn"
+gen_links $DIR $FILES
+
+DIR=$BASEDIR/titan.TestPorts.Common_Components.Abstract_Socket/src
+FILES="Abstract_Socket.cc Abstract_Socket.hh "
+gen_links $DIR $FILES
+
+DIR=$BASEDIR/titan.TestPorts.HTTPmsg/src
+FILES="HTTPmsg_MessageLen.ttcn HTTPmsg_MessageLen_Function.cc HTTPmsg_PT.cc HTTPmsg_PT.hh HTTPmsg_PortType.ttcn HTTPmsg_Types.ttcn "
+gen_links $DIR $FILES
+
+DIR=$BASEDIR/titan.ProtocolModules.JSON_v07_2006/src
+FILES="JSON_EncDec.cc JSON_Types.ttcn "
+gen_links $DIR $FILES
+
+
+DIR=../library
+FILES="Misc_Helpers.ttcn General_Types.ttcn Osmocom_VTY_Functions.ttcn Osmocom_Types.ttcn "
+FILES+="IPA_Types.ttcn IPA_CodecPort.ttcn IPA_CodecPort_CtrlFunct.ttcn IPA_CodecPort_CtrlFunctDef.cc IPA_Emulation.ttcnpp IPA_CodecPort.ttcn " #RSL_Types.ttcn RSL_Emulation.ttcn "
+FILES+="Osmocom_CTRL_Types.ttcn Osmocom_CTRL_Functions.ttcn Osmocom_CTRL_Adapter.ttcn "
+FILES+="Native_Functions.ttcn Native_FunctionDefs.cc "
+gen_links $DIR $FILES
+
+ignore_pp_results
diff --git a/remsim/regen_makefile.sh b/remsim/regen_makefile.sh
new file mode 100755
index 0000000..e398360
--- /dev/null
+++ b/remsim/regen_makefile.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+FILES="*.ttcn *.ttcnpp *.asn IPA_CodecPort_CtrlFunctDef.cc IPL4asp_PT.cc IPL4asp_discovery.cc TCCConversion.cc TCCInterface.cc TELNETasp_PT.cc Native_FunctionDefs.cc RSPRO_EncDec.cc Abstract_Socket.cc HTTPmsg_PT.cc HTTPmsg_MessageLen_Function.cc JSON_EncDec.cc"
+
+export CPPFLAGS_TTCN3="-DIPA_EMULATION_RSPRO -DIPA_EMULATION_CTRL"
+
+../regen-makefile.sh REMSIM_Tests.ttcn $FILES