Change in ...osmo-ttcn3-hacks[master]: Initial TTCN-3 test suite for osmo-remsim

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

laforge gerrit-no-reply at lists.osmocom.org
Fri Sep 13 11:45:45 UTC 2019


laforge has submitted this change and it was merged. ( https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/15509 )

Change subject: Initial TTCN-3 test suite for osmo-remsim
......................................................................

Initial TTCN-3 test suite for osmo-remsim

This adds shared infrastructure and initial test suites for
osmo-remsim-{server,client,bankd}.

Change-Id: I00034d3a991f0f881cfd8ff0bfc4557113daf830
---
M Makefile
M deps/Makefile
A remsim/REMSIM_Tests.cfg
A remsim/REMSIM_Tests.default
A remsim/REMSIM_Tests.ttcn
A remsim/RSPRO.asn
A remsim/RSPRO_EncDec.cc
A remsim/RSPRO_Server.ttcn
A remsim/RSPRO_Types.ttcn
A remsim/RSRES.ttcn
A remsim/RemsimBankd_Tests.ttcn
A remsim/RemsimClient_Tests.ttcn
A remsim/RemsimServer_Tests.ttcn
A remsim/gen_links.sh
A remsim/regen_makefile.sh
15 files changed, 2,252 insertions(+), 1 deletion(-)

Approvals:
  laforge: Looks good to me, approved
  Jenkins Builder: Verified



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 @@
 			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 @@
 			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.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.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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/15509
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-ttcn3-hacks
Gerrit-Branch: master
Gerrit-Change-Id: I00034d3a991f0f881cfd8ff0bfc4557113daf830
Gerrit-Change-Number: 15509
Gerrit-PatchSet: 5
Gerrit-Owner: laforge <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge at gnumonks.org>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20190913/27317e2a/attachment.htm>


More information about the gerrit-log mailing list