Change in osmo-ttcn3-hacks[master]: stp: Introduce STP_Tests*.ttcn for testing OsmoSTP

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
Tue Nov 12 12:10:01 UTC 2019


laforge has submitted this change. ( https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/15822 )

Change subject: stp: Introduce STP_Tests*.ttcn for testing OsmoSTP
......................................................................

stp: Introduce STP_Tests*.ttcn for testing OsmoSTP

In the past, we were automatically running [large parts of] the nplab
M3UA and SUA test suites, but those implement only a fraction of the
functionality.  Particularly, they don't cover the non-standard IPA
behavior, and they don't cover RKM (routing key management).

Let's introduce an initial set of STP tests with this patch.  We try
to not duplicate nplab here, and implement bits not covered there.

Change-Id: I628a87385cac0dfe708a0d74a5088fbd5a4790cd
---
M Makefile
A library/M3UA_CodecPort.ttcn
A library/M3UA_CodecPort_CtrlFunct.ttcn
A library/M3UA_CodecPort_CtrlFunctDef.cc
A library/M3UA_Templates.ttcn
M library/SCCP_Templates.ttcn
A stp/STP_Tests.cfg
A stp/STP_Tests.default
A stp/STP_Tests.ttcn
A stp/STP_Tests_Common.ttcn
A stp/STP_Tests_IPA.ttcn
A stp/STP_Tests_M3UA.ttcn
A stp/gen_links.sh
A stp/osmo-stp.cfg
A stp/regen_makefile.sh
15 files changed, 2,048 insertions(+), 1 deletion(-)

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



diff --git a/Makefile b/Makefile
index 99243d6..4ceacf3 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 remsim sccp selftest sgsn sip sysinfo
+SUBDIRS=bsc bsc-nat bts ggsn_tests hlr mgw msc pcu remsim sccp selftest sgsn sip stp sysinfo
 
 NPROC=$(shell nproc 2>/dev/null)
 ifeq ($(NPROC),)
diff --git a/library/M3UA_CodecPort.ttcn b/library/M3UA_CodecPort.ttcn
new file mode 100644
index 0000000..94d16d6
--- /dev/null
+++ b/library/M3UA_CodecPort.ttcn
@@ -0,0 +1,84 @@
+module M3UA_CodecPort {
+
+/* Simple M3UA Codec Port, translating between raw SCTP primitives with
+ * octetstring payload towards the IPL4asp provider, and M3UA primitives
+ * which carry the decoded M3UA data types as payload.
+ *
+ * (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_PortType all;
+	import from IPL4asp_Types all;
+	import from M3UA_Types all;
+
+	type record M3UA_RecvFrom {
+		ConnectionId	connId,
+		HostName	remName,
+		PortNumber	remPort,
+		HostName	locName,
+		PortNumber	locPort,
+		PDU_M3UA	msg
+	};
+
+	template M3UA_RecvFrom t_M3UA_RecvFrom(template PDU_M3UA msg) := {
+		connId := ?,
+		remName := ?,
+		remPort := ?,
+		locName := ?,
+		locPort := ?,
+		msg := msg
+	}
+
+	type record M3UA_Send {
+		ConnectionId	connId,
+		integer		stream,
+		PDU_M3UA	msg
+	}
+
+	template M3UA_Send t_M3UA_Send(template ConnectionId connId, template PDU_M3UA msg,
+					template (omit) integer stream := omit) := {
+		connId := connId,
+		stream := stream,
+		msg := msg
+	}
+
+	private function IPL4_to_M3UA_RecvFrom(in ASP_RecvFrom pin, out M3UA_RecvFrom pout) {
+		pout.connId := pin.connId;
+		pout.remName := pin.remName;
+		pout.remPort := pin.remPort;
+		pout.locName := pin.locName;
+		pout.locPort := pin.locPort;
+		pout.msg := dec_PDU_M3UA(pin.msg);
+	} with { extension "prototype(fast)" };
+
+	private function M3UA_to_IPL4_Send(in M3UA_Send pin, out ASP_Send pout) {
+		pout.connId := pin.connId;
+		pout.proto := {
+			sctp := {
+				sinfo_stream := pin.stream,
+				sinfo_ppid := 3,
+				remSocks := omit,
+				assocId := omit
+			}
+		};
+		pout.msg := enc_PDU_M3UA(pin.msg);
+	} with { extension "prototype(fast)" };
+
+	type port M3UA_CODEC_PT message {
+		out	M3UA_Send;
+		in	M3UA_RecvFrom,
+			ASP_ConnId_ReadyToRelease,
+			ASP_Event;
+	} with { extension "user IPL4asp_PT
+		out(M3UA_Send -> ASP_Send:function(M3UA_to_IPL4_Send))
+		in(ASP_RecvFrom -> M3UA_RecvFrom: function(IPL4_to_M3UA_RecvFrom);
+		   ASP_ConnId_ReadyToRelease -> ASP_ConnId_ReadyToRelease: simple;
+		   ASP_Event -> ASP_Event: simple)"
+	}
+}
diff --git a/library/M3UA_CodecPort_CtrlFunct.ttcn b/library/M3UA_CodecPort_CtrlFunct.ttcn
new file mode 100644
index 0000000..fc38e43
--- /dev/null
+++ b/library/M3UA_CodecPort_CtrlFunct.ttcn
@@ -0,0 +1,44 @@
+module M3UA_CodecPort_CtrlFunct {
+
+  import from M3UA_CodecPort all;
+  import from IPL4asp_Types all;
+
+  external function f_IPL4_listen(
+    inout M3UA_CODEC_PT portRef,
+    in HostName locName,
+    in PortNumber locPort,
+    in ProtoTuple proto,
+    in OptionList options := {}
+  ) return Result;
+
+  external function f_IPL4_connect(
+    inout M3UA_CODEC_PT portRef,
+    in HostName remName,
+    in PortNumber remPort,
+    in HostName locName,
+    in PortNumber locPort,
+    in ConnectionId connId,
+    in ProtoTuple proto,
+    in OptionList options := {}
+  ) return Result;
+
+  external function f_IPL4_close(
+    inout M3UA_CODEC_PT portRef,
+    in ConnectionId id,
+    in ProtoTuple proto := { unspecified := {} }
+  ) return Result;
+
+  external function f_IPL4_setUserData(
+    inout M3UA_CODEC_PT portRef,
+    in ConnectionId id,
+    in UserData userData
+  ) return Result;
+
+  external function f_IPL4_getUserData(
+    inout M3UA_CODEC_PT portRef,
+    in ConnectionId id,
+    out UserData userData
+  ) return Result;
+
+}
+
diff --git a/library/M3UA_CodecPort_CtrlFunctDef.cc b/library/M3UA_CodecPort_CtrlFunctDef.cc
new file mode 100644
index 0000000..62533de
--- /dev/null
+++ b/library/M3UA_CodecPort_CtrlFunctDef.cc
@@ -0,0 +1,56 @@
+#include "IPL4asp_PortType.hh"
+#include "M3UA_CodecPort.hh"
+#include "IPL4asp_PT.hh"
+
+namespace M3UA__CodecPort__CtrlFunct {
+
+  IPL4asp__Types::Result f__IPL4__listen(
+    M3UA__CodecPort::M3UA__CODEC__PT& portRef,
+    const IPL4asp__Types::HostName& locName,
+    const IPL4asp__Types::PortNumber& locPort,
+    const IPL4asp__Types::ProtoTuple& proto,
+    const IPL4asp__Types::OptionList& options)
+  {
+    return f__IPL4__PROVIDER__listen(portRef, locName, locPort, proto, options);
+  }
+  
+  IPL4asp__Types::Result f__IPL4__connect(
+    M3UA__CodecPort::M3UA__CODEC__PT& portRef,
+    const IPL4asp__Types::HostName& remName,
+    const IPL4asp__Types::PortNumber& remPort,
+    const IPL4asp__Types::HostName& locName,
+    const IPL4asp__Types::PortNumber& locPort,
+    const IPL4asp__Types::ConnectionId& connId,
+    const IPL4asp__Types::ProtoTuple& proto,
+    const IPL4asp__Types::OptionList& options)
+  {
+    return f__IPL4__PROVIDER__connect(portRef, remName, remPort,
+                                      locName, locPort, connId, proto, options);
+  }
+
+  IPL4asp__Types::Result f__IPL4__close(
+    M3UA__CodecPort::M3UA__CODEC__PT& portRef, 
+    const IPL4asp__Types::ConnectionId& connId, 
+    const IPL4asp__Types::ProtoTuple& proto)
+  {
+      return f__IPL4__PROVIDER__close(portRef, connId, proto);
+  }
+
+  IPL4asp__Types::Result f__IPL4__setUserData(
+    M3UA__CodecPort::M3UA__CODEC__PT& portRef,
+    const IPL4asp__Types::ConnectionId& connId,
+    const IPL4asp__Types::UserData& userData)
+  {
+    return f__IPL4__PROVIDER__setUserData(portRef, connId, userData);
+  }
+  
+  IPL4asp__Types::Result f__IPL4__getUserData(
+    M3UA__CodecPort::M3UA__CODEC__PT& portRef,
+    const IPL4asp__Types::ConnectionId& connId,
+    IPL4asp__Types::UserData& userData)
+  {
+    return f__IPL4__PROVIDER__getUserData(portRef, connId, userData);
+  }
+  
+}
+
diff --git a/library/M3UA_Templates.ttcn b/library/M3UA_Templates.ttcn
new file mode 100644
index 0000000..02b493c
--- /dev/null
+++ b/library/M3UA_Templates.ttcn
@@ -0,0 +1,771 @@
+module M3UA_Templates {
+
+/* M3UA Templates, building on top of M3UA_Types from Ericsson.
+ *
+ * (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 M3UA_Types all;
+import from General_Types all;
+import from Osmocom_Types all;
+
+const OCT1 c_M3UA_VERSION := '01'O;
+
+const OCT2 c_M3UA_ST_T_STATE_CHG := '0001'O;
+const OCT2 c_M3UA_ST_I_RESERVED := '0001'O;
+const OCT2 c_M3UA_ST_I_AS_INACTIVE := '0002'O;
+const OCT2 c_M3UA_ST_I_AS_ACTIVE := '0003'O;
+const OCT2 c_M3UA_ST_I_AS_PENDING := '0004'O;
+
+const OCT2 c_M3UA_ST_T_OTHER := '0002'O;
+const OCT2 c_M3UA_ST_I_INSUFF_RESRC := '0001'O
+const OCT2 c_M3UA_ST_I_ALTERNATE_ASP := '0002'O
+const OCT2 c_M3UA_ST_I_ASP_FAILUREP := '0003'O
+
+private function f_aspid_or_omit(template (omit) OCT4 aspid)
+return template (omit) M3UA_ASP_Identifier {
+	var template (omit) M3UA_ASP_Identifier id;
+	if (istemplatekind(aspid, "omit")) {
+		return omit;
+	} else {
+		id.tag := '0011'O;
+		id.lengthInd := 8;
+		id.aSPIdentifier := aspid;
+		return id;
+	}
+}
+
+function tr_M3UA_asp_id(template OCT4 aspid)
+return template M3UA_ASP_Identifier {
+	var template M3UA_ASP_Identifier id := {
+		tag := '0011'O,
+		lengthInd := 8,
+		aSPIdentifier := aspid
+	};
+	if (istemplatekind(aspid, "omit")) {
+		return omit;
+	} else if (istemplatekind(aspid, "*")) {
+		return *;
+	} else {
+		return id;
+	}
+}
+
+
+/***********************************************************************
+ * ASPSM Class
+ ***********************************************************************/
+
+template (value) PDU_M3UA ts_M3UA_ASPUP(template (omit) OCT4 aspid := omit,
+					template (omit) octetstring infostr := omit) := {
+	m3UA_ASPUP := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0301'O,
+		messageLength := 0, // overwritten
+		messageParameters := {
+			aSP_Identifier := f_aspid_or_omit(aspid),
+			info_String := omit
+		}
+	}
+}
+template (present) PDU_M3UA tr_M3UA_ASPUP(template OCT4 aspid := *,
+					  template octetstring infostr := omit) := {
+	m3UA_ASPUP := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0301'O,
+		messageLength := 0, // overwritten
+		messageParameters := {
+			aSP_Identifier := tr_M3UA_asp_id(aspid),
+			info_String := *
+		}
+	}
+}
+
+template (value) PDU_M3UA ts_M3UA_ASPUP_ACK := {
+	m3UA_ASPUP_Ack := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0304'O,
+		messageLength := 0, // overwritten
+		messageParameters := {
+			info_String := omit
+		}
+	}
+}
+template (present) PDU_M3UA tr_M3UA_ASPUP_ACK := {
+	m3UA_ASPUP_Ack := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0304'O,
+		messageLength := ?,
+		messageParameters := {
+			info_String := *
+		}
+	}
+}
+
+template (value) PDU_M3UA ts_M3UA_ASPDN := {
+	m3UA_ASPDN := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0302'O,
+		messageLength := 0, // overwritten
+		messageParameters := {
+			info_String := omit
+		}
+	}
+}
+template (present) PDU_M3UA tr_M3UA_ASPDN := {
+	m3UA_ASPDN := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0302'O,
+		messageLength := ?,
+		messageParameters := {
+			info_String := *
+		}
+	}
+}
+
+template (value) PDU_M3UA ts_M3UA_ASPDN_ACK := {
+	m3UA_ASPUP_Ack := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0305'O,
+		messageLength := 0, // overwritten
+		messageParameters := {
+			info_String := omit
+		}
+	}
+}
+template (present) PDU_M3UA tr_M3UA_ASPDN_ACK := {
+	m3UA_ASPUP_Ack := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0305'O,
+		messageLength := ?,
+		messageParameters := {
+			info_String := *
+		}
+	}
+}
+
+template (value) M3UA_Heartbeat_Data ts_M3UA_hb_data(template (value) octetstring hb_data) := {
+	tag := '0009'O,
+	lengthInd := 0, // overwritten
+	heartbeat_Data := hb_data
+}
+
+template (present) M3UA_Heartbeat_Data tr_M3UA_hb_data(template (present) octetstring hb_data) := {
+	tag := '0009'O,
+	lengthInd := ?,
+	heartbeat_Data := hb_data
+}
+
+template (value) PDU_M3UA ts_M3UA_BEAT(template (omit) M3UA_Heartbeat_Data hbd := omit) := {
+	m3UA_BEAT := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0303'O,
+		messageLength := 0, // overwritten
+		messageParameters := {
+			heartbeat_Data := hbd
+		}
+	}
+}
+template (present) PDU_M3UA tr_M3UA_BEAT(template M3UA_Heartbeat_Data hbd := *) := {
+	m3UA_BEAT := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0303'O,
+		messageLength := ?,
+		messageParameters := {
+			heartbeat_Data := hbd
+		}
+	}
+}
+
+template (value) PDU_M3UA ts_M3UA_BEAT_ACK(template (omit) M3UA_Heartbeat_Data hb_data)  := {
+	m3UA_BEAT_Ack := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0306'O,
+		messageLength := 0, // overwritten
+		messageParameters := {
+			heartbeat_Data := hb_data
+		}
+	}
+}
+template (present) PDU_M3UA tr_M3UA_BEAT_ACK(template M3UA_Heartbeat_Data hb_data := *) := {
+	m3UA_BEAT_Ack := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0306'O,
+		messageLength := ?,
+		messageParameters := {
+			heartbeat_Data := hb_data
+		}
+	}
+}
+
+
+
+
+/***********************************************************************
+ * ASPTM Class
+ ***********************************************************************/
+
+
+const M3UA_Traffic_Mode_Type c_M3UA_TMT_override := {
+	tag := '000B'O,
+	lengthInd := 8,
+	trafficModeType := int2oct(1, 4)
+}
+
+const M3UA_Traffic_Mode_Type c_M3UA_TMT_loadshare := {
+	tag := '000B'O,
+	lengthInd := 8,
+	trafficModeType := int2oct(2, 4)
+}
+
+const M3UA_Traffic_Mode_Type c_M3UA_TMT_broadcast := {
+	tag := '000B'O,
+	lengthInd := 8,
+	trafficModeType := int2oct(3, 4)
+}
+
+function ts_M3UA_routing_ctx(template (omit) octetstring rctx)
+return template (omit) M3UA_Routing_Context {
+	var template (omit) M3UA_Routing_Context id;
+	if (istemplatekind(rctx, "omit")) {
+		return omit;
+	} else {
+		id.tag := '0006'O;
+		id.lengthInd := 0; // overwritten
+		id.routingContext := rctx;
+		return id;
+	}
+}
+
+function tr_M3UA_routing_ctx(template octetstring rctx)
+return template M3UA_Routing_Context {
+	var template M3UA_Routing_Context id;
+	if (istemplatekind(rctx, "omit")) {
+		return omit;
+	} else if (istemplatekind(rctx, "*")) {
+		return *;
+	} else {
+		id.tag := '0006'O;
+		id.lengthInd := ?;
+		id.routingContext := rctx;
+		return id;
+	}
+}
+
+template (value) PDU_M3UA ts_M3UA_ASPAC(template (omit) M3UA_Traffic_Mode_Type tmt,
+					template (omit) OCT4 rctx) := {
+	m3UA_ASPAC := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0401'O,
+		messageLength := 0, // overwritten
+		messageParameters := {
+			traffic_Mode_Type := tmt,
+			routing_Context := ts_M3UA_routing_ctx(rctx),
+			info_String := omit
+		}
+	}
+}
+template (present) PDU_M3UA tr_M3UA_ASPAC(template M3UA_Traffic_Mode_Type tmt,
+					  template OCT4 rctx) := {
+	m3UA_ASPAC := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0401'O,
+		messageLength := ?,
+		messageParameters := {
+			traffic_Mode_Type := tmt,
+			routing_Context := tr_M3UA_routing_ctx(rctx),
+			info_String := *
+		}
+	}
+}
+
+template (value) PDU_M3UA ts_M3UA_ASPAC_ACK(template (omit) M3UA_Traffic_Mode_Type tmt,
+					template (omit) OCT4 rctx) := {
+	m3UA_ASPAC_Ack := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0403'O,
+		messageLength := 0, // overwritten
+		messageParameters := {
+			traffic_Mode_Type := tmt,
+			routing_Context := ts_M3UA_routing_ctx(rctx),
+			info_String := omit
+		}
+	}
+}
+template (present) PDU_M3UA tr_M3UA_ASPAC_ACK(template M3UA_Traffic_Mode_Type tmt,
+					      template OCT4 rctx) := {
+	m3UA_ASPAC_Ack := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0403'O,
+		messageLength := ?,
+		messageParameters := {
+			traffic_Mode_Type := tmt,
+			routing_Context := tr_M3UA_routing_ctx(rctx),
+			info_String := *
+		}
+	}
+}
+
+template (value) PDU_M3UA ts_M3UA_ASPIA(template (omit) OCT4 rctx) := {
+	m3UA_ASPIA := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0402'O,
+		messageLength := 0, // overwritten
+		messageParameters := {
+			routing_Context := ts_M3UA_routing_ctx(rctx),
+			info_String := omit
+		}
+	}
+}
+template (present) PDU_M3UA tr_M3UA_ASPIA(template OCT4 rctx) := {
+	m3UA_ASPIA := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0402'O,
+		messageLength := ?,
+		messageParameters := {
+			routing_Context := tr_M3UA_routing_ctx(rctx),
+			info_String := *
+		}
+	}
+}
+
+
+template (value) PDU_M3UA ts_M3UA_ASPIA_ACK(template (omit) OCT4 rctx) := {
+	m3UA_ASPIA_Ack := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0404'O,
+		messageLength := 0, // overwritten
+		messageParameters := {
+			routing_Context := ts_M3UA_routing_ctx(rctx),
+			info_String := omit
+		}
+	}
+}
+template (present) PDU_M3UA tr_M3UA_ASPIA_ACK(template OCT4 rctx) := {
+	m3UA_ASPIA_Ack := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0404'O,
+		messageLength := ?,
+		messageParameters := {
+			routing_Context := tr_M3UA_routing_ctx(rctx),
+			info_String := *
+		}
+	}
+}
+
+
+/***********************************************************************
+ * MGMT Class
+ ***********************************************************************/
+
+template (value) M3UA_Error_Code ts_M3UA_err_code(template (value) OCT4 val) := {
+	tag := '000C'O,
+	lengthInd := 8,
+	errorCode := val
+}
+template (present) M3UA_Error_Code tr_M3UA_err_code(template (present) OCT4 val) := {
+	tag := '000C'O,
+	lengthInd := 8,
+	errorCode := val
+}
+
+template (value) M3UA_Status ts_M3UA_status(template (value) OCT2 status_type,
+					    template (value) OCT2 status_info) := {
+	tag := '000D'O,
+	lengthInd := 8,
+	statusType := status_type,
+	statusInfo := status_info
+}
+
+template (present) M3UA_Status tr_M3UA_status(template (present) OCT2 status_type,
+					      template (present) OCT2 status_info) := {
+	tag := '000D'O,
+	lengthInd := 8,
+	statusType := status_type,
+	statusInfo := status_info
+}
+
+
+template (value) PDU_M3UA ts_M3UA_ERR(template (value) OCT4 err_code,
+				      template (omit) OCT4 rctx) := {
+	m3UA_ERR := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0000'O,
+		messageLength := 0, // overwritten
+		messageParameters := {
+			error_Code := ts_M3UA_err_code(err_code),
+			routing_Context := ts_M3UA_routing_ctx(rctx),
+			affected_Point_Codes := omit,
+			network_Appearance := omit,
+			diagnostic_information := omit
+		}
+	}
+}
+template (present) PDU_M3UA tr_M3UA_ERR(template (present) OCT4 err_code,
+				      template OCT4 rctx) := {
+	m3UA_ERR := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0000'O,
+		messageLength := ?,
+		messageParameters := {
+			error_Code := tr_M3UA_err_code(err_code),
+			routing_Context := tr_M3UA_routing_ctx(rctx),
+			affected_Point_Codes := *,
+			network_Appearance := *,
+			diagnostic_information := *
+		}
+	}
+}
+
+
+template (value) PDU_M3UA ts_M3UA_NOTIFY(template (value) OCT2 status_type,
+					 template (value) OCT2 status_info,
+					 template (omit) OCT4 rctx,
+					 template (omit) OCT4 aspid := omit,
+					 template (omit) octetstring infostr := omit) := {
+	m3UA_NOTIFY := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0001'O,
+		messageLength := 0, // overwritten
+		messageParameters := {
+			status := ts_M3UA_status(status_type, status_info),
+			aSP_Identifier := f_aspid_or_omit(aspid),
+			routing_Context := ts_M3UA_routing_ctx(rctx),
+			info_String := omit
+		}
+	}
+}
+template (present) PDU_M3UA tr_M3UA_NOTIFY(template (present) OCT2 status_type,
+					   template (present) OCT2 status_info,
+					   template OCT4 rctx,
+					   template OCT4 aspid := *,
+					   template octetstring infostr := *) := {
+	m3UA_NOTIFY := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0001'O,
+		messageLength := ?,
+		messageParameters := {
+			status := tr_M3UA_status(status_type, status_info),
+			aSP_Identifier := *,
+			routing_Context := tr_M3UA_routing_ctx(rctx),
+			info_String := *
+		}
+	}
+}
+
+/***********************************************************************
+ * Message Transfer Class
+ ***********************************************************************/
+
+template (value) M3UA_Protocol_Data ts_M3UA_protocol_data(template (value) OCT4 opc,
+							  template (value) OCT4 dpc,
+							  template (value) OCT1 si,
+							  template (value) OCT1 ni,
+							  template (value) OCT1 mp,
+							  template (value) OCT1 sls,
+							  template (value) octetstring data) := {
+	tag := '0210'O,
+	lengthInd := 0, // overwritten
+	oPC := opc,
+	dPC := dpc,
+	sI := si,
+	nI := ni,
+	mP := mp,
+	sLS := sls,
+	userProtocolData := data
+}
+template (present) M3UA_Protocol_Data tr_M3UA_protocol_data(template (present) OCT4 opc,
+							  template (present) OCT4 dpc,
+							  template (present) OCT1 si,
+							  template (present) OCT1 ni,
+							  template (present) OCT1 mp,
+							  template (present) OCT1 sls,
+							  template (present) octetstring data) := {
+	tag := '0210'O,
+	lengthInd := ?,
+	oPC := opc,
+	dPC := dpc,
+	sI := si,
+	nI := ni,
+	mP := mp,
+	sLS := sls,
+	userProtocolData := data
+}
+
+
+template (value) PDU_M3UA ts_M3UA_DATA(template (omit) OCT4 rctx,
+				       template (value) M3UA_Protocol_Data data) := {
+	m3UA_DATA := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0101'O,
+		messageLength := 0, // overwritten
+		messageParameters :={
+			network_Appearance := omit,
+			routing_Context := ts_M3UA_routing_ctx(rctx),
+			protocol_Data := data,
+			correlation_ID := omit
+		}
+	}
+}
+template (present) PDU_M3UA tr_M3UA_DATA(template OCT4 rctx,
+				       template (present) M3UA_Protocol_Data data) := {
+	m3UA_DATA := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0101'O,
+		messageLength := ?, // overwritten
+		messageParameters := {
+			network_Appearance := *,
+			routing_Context := tr_M3UA_routing_ctx(rctx),
+			protocol_Data := data,
+			correlation_ID := *
+		}
+	}
+}
+
+/***********************************************************************
+ * Routing Key Management
+ ***********************************************************************/
+
+template (value) M3UA_Local_Routing_Key_Id ts_M3UA_lrkid(template (value) OCT4 id) := {
+	tag := '020a'O,
+	lengthInd := 8,
+	localRkId := id
+}
+
+template (present) M3UA_Local_Routing_Key_Id tr_M3UA_lrkid(template (present) OCT4 id) := {
+	tag := '020a'O,
+	lengthInd := 8,
+	localRkId := id
+}
+
+
+template (value) M3UA_Routing_Key ts_M3UA_rkey(OCT4 id, OCT3 dpc,
+						template (omit) M3UA_Traffic_Mode_Type tmt := omit,
+						template (omit) OCT4 rctx := omit) := {
+	tag := '0207'O,
+	lengthInd := 0, // overwritten
+	routingKey := {
+		local_Routing_Key_Id := ts_M3UA_lrkid(id),
+		routing_Context := ts_M3UA_routing_ctx(rctx),
+		traffic_Mode_Type := tmt,
+		destination_Point_Code := {
+			tag := '020b'O,
+			lengthInd := 8,
+			pointCode := { '00'O, dpc }
+		},
+		network_Appearance := omit,
+		service_Indicators := omit,
+		opc_List := omit
+	}
+}
+template (present) M3UA_Routing_Key tr_M3UA_rkey(template (present) OCT4 id, template (present) OCT3 dpc,
+						template M3UA_Traffic_Mode_Type tmt := *,
+						template OCT4 rctx := *) := {
+	tag := '0207'O,
+	lengthInd := ?,
+	routingKey := {
+		local_Routing_Key_Id := tr_M3UA_lrkid(id),
+		routing_Context := tr_M3UA_routing_ctx(rctx),
+		traffic_Mode_Type := tmt,
+		destination_Point_Code := {
+			tag := '020b'O,
+			lengthInd := 8,
+			pointCode := { '00'O, dpc }
+		},
+		network_Appearance := omit,
+		service_Indicators := omit,
+		opc_List := omit
+	}
+}
+
+
+const OCT4 c_M3UA_REGSTS_SUCCESS := '00000000'O;
+const OCT4 c_M3UA_REGSTS_ERR_UNKNOWN := '00000001'O;
+const OCT4 c_M3UA_REGSTS_ERR_INVAL_DPC := '00000002'O;
+const OCT4 c_M3UA_REGSTS_ERR_INVAL_NA := '00000003'O;
+const OCT4 c_M3UA_REGSTS_ERR_INVAL_RKEY := '00000004'O;
+const OCT4 c_M3UA_REGSTS_ERR_EPERM := '00000005'O;
+// ...
+
+const OCT4 c_m3UA_DEREGSTS_SUCCESS := '00000000'O;
+const OCT4 c_m3UA_DEREGSTS_ERR_UNKNOWN := '00000001'O;
+const OCT4 c_m3UA_DEREGSTS_ERR_INVAL_RCTX := '00000002'O;
+const OCT4 c_m3UA_DEREGSTS_ERR_EPERM := '00000003'O;
+const OCT4 c_m3UA_DEREGSTS_ERR_NOT_REG := '00000004'O;
+const OCT4 c_m3UA_DEREGSTS_ERR_ASP_ACTIVE := '00000005'O;
+
+template (value) M3UA_Registration_Result ts_M3UA_reg_res(template (value) OCT4 id,
+							    template (value) OCT4 status,
+							    template (value) OCT4 rctx) := {
+	tag := '0208'O,
+	lengthInd := 0, // overwritten
+	registrationResult := {
+		local_Routing_Key_Id := ts_M3UA_lrkid(id),
+		registration_Status := {
+			tag := '0212'O,
+			lengthInd := 8,
+			registrationStatus := status
+		},
+		routing_Context := ts_M3UA_routing_ctx(rctx)
+	}
+}
+template (present) M3UA_Registration_Result tr_M3UA_reg_res(template (present) OCT4 id,
+							    template (present) OCT4 status,
+							    template (present) OCT4 rctx) := {
+	tag := '0208'O,
+	lengthInd := ?,
+	registrationResult := {
+		local_Routing_Key_Id := tr_M3UA_lrkid(id),
+		registration_Status := {
+			tag := '0212'O,
+			lengthInd := 8,
+			registrationStatus := status
+		},
+		routing_Context := tr_M3UA_routing_ctx(rctx)
+	}
+}
+
+template (value) M3UA_Deregistration_Result ts_M3UA_dereg_res(template (value) OCT4 rctx,
+							      template (value) OCT4 status) := {
+	tag := '0209'O,
+	lengthInd := 0, // overwritten
+	deregistrationResult := {
+		routing_Context := ts_M3UA_routing_ctx(rctx),
+		deregistration_Status := {
+			tag := '0213'O,
+			lengthInd := 8,
+			deregistrationStatus := status
+		}
+	}
+}
+template (present) M3UA_Deregistration_Result tr_M3UA_dereg_res(template (present) OCT4 rctx,
+								template (present) OCT4 status) := {
+	tag := '0209'O,
+	lengthInd := ?,
+	deregistrationResult := {
+		routing_Context := tr_M3UA_routing_ctx(rctx),
+		deregistration_Status := {
+			tag := '0213'O,
+			lengthInd := 8,
+			deregistrationStatus := status
+		}
+	}
+}
+
+
+template (value) PDU_M3UA ts_M3UA_REG_REQ(template (value) M3UA_Routing_Keys rkeys) := {
+	m3UA_REG_REQ := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0901'O,
+		messageLength := 0, // overwritten
+		messageParameters := rkeys
+	}
+}
+template (present) PDU_M3UA tr_M3UA_REG_REQ(template (present) M3UA_Routing_Keys rkeys) := {
+	m3UA_REG_REQ := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0901'O,
+		messageLength := ?,
+		messageParameters := rkeys
+	}
+}
+
+
+template (value) PDU_M3UA ts_M3UA_REG_RSP(template (value) M3UA_Registration_Results res) := {
+	m3UA_REG_RSP := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0902'O,
+		messageLength := 0, // overwritten
+		messageParameters := res
+	}
+}
+template (present) PDU_M3UA tr_M3UA_REG_RSP(template (present) M3UA_Registration_Results res) := {
+	m3UA_REG_RSP := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0902'O,
+		messageLength := ?,
+		messageParameters := res
+	}
+}
+
+template (value) PDU_M3UA ts_M3UA_DEREG_REQ(template (value) M3UA_Routing_Context rctx) := {
+	m3UA_DEREG_REQ := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0903'O,
+		messageLength := 0, // overwritten
+		messageParameters := {
+			routing_Context := rctx
+		}
+	}
+}
+template (present) PDU_M3UA tr_M3UA_DEREG_REQ(template (present) M3UA_Routing_Context rctx) := {
+	m3UA_DEREG_REQ := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0903'O,
+		messageLength := ?,
+		messageParameters := {
+			routing_Context := rctx
+		}
+	}
+}
+
+template (value) PDU_M3UA ts_M3UA_DEREG_RSP(template (value) M3UA_Deregistration_Results res) := {
+	m3UA_DEREG_RSP := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0904'O,
+		messageLength := 0, // overwritten
+		messageParameters := res
+	}
+}
+template (present) PDU_M3UA tr_M3UA_DEREG_RSP(template (present) M3UA_Deregistration_Results res) := {
+	m3UA_DEREG_RSP := {
+		version := c_M3UA_VERSION,
+		reserved := '00'O,
+		messageClassAndType := '0904'O,
+		messageLength := ?,
+		messageParameters := res
+	}
+}
+
+
+
+}
diff --git a/library/SCCP_Templates.ttcn b/library/SCCP_Templates.ttcn
index a5af073..7c2ffa6 100644
--- a/library/SCCP_Templates.ttcn
+++ b/library/SCCP_Templates.ttcn
@@ -46,4 +46,25 @@
 	}
 }
 
+/* construct a SCCP_PAR_Address with PC + SSN and GT */
+template (value) SCCP_PAR_Address ts_SccpAddr_PC_GT(integer pc, octetstring sio,
+						    charstring sccp_srv_type, hexstring gt_addr) := {
+	addressIndicator := {
+		pointCodeIndic := '1'B,
+		ssnIndicator := '0'B,
+		globalTitleIndic := '0001'B, // NAI only
+		routingIndicator := cg_route_on_GT // route on GT
+	},
+	signPointCode := SCCP_SPC_int2bit(pc, sccp_srv_type, sio),
+	subsystemNumber := omit,
+	globalTitle := {
+		gti0001 := {
+			natureOfAddress := '0000011'B,
+			oddeven := '0'B,
+			globalTitleAddress := gt_addr
+		}
+	}
+}
+
+
 }
diff --git a/stp/STP_Tests.cfg b/stp/STP_Tests.cfg
new file mode 100644
index 0000000..3ba2d13
--- /dev/null
+++ b/stp/STP_Tests.cfg
@@ -0,0 +1,18 @@
+[ORDERED_INCLUDE]
+# Common configuration, shared between test suites
+"../Common.cfg"
+# testsuite specific configuration, not expected to change
+"./STP_Tests.default"
+
+# Local configuration below
+
+[LOGGING]
+
+[TESTPORT_PARAMETERS]
+
+[MODULE_PARAMETERS]
+
+[MAIN_CONTROLLER]
+
+[EXECUTE]
+STP_Tests.control
diff --git a/stp/STP_Tests.default b/stp/STP_Tests.default
new file mode 100644
index 0000000..6417e8c
--- /dev/null
+++ b/stp/STP_Tests.default
@@ -0,0 +1,20 @@
+[LOGGING]
+FileMask := LOG_ALL | TTCN_MATCHING;
+
+[TESTPORT_PARAMETERS]
+*.VTY.CTRL_MODE := "client"
+*.VTY.CTRL_HOSTNAME := "127.0.0.1"
+*.VTY.CTRL_PORTNUM := "4239"
+*.VTY.CTRL_LOGIN_SKIPPED := "yes"
+*.VTY.CTRL_DETECT_SERVER_DISCONNECTED := "yes"
+*.VTY.CTRL_READMODE := "buffered"
+*.VTY.CTRL_CLIENT_CLEANUP_LINEFEED := "yes"
+*.VTY.CTRL_DETECT_CONNECTION_ESTABLISHMENT_RESULT := "yes"
+*.VTY.PROMPT1 := "OsmoSTP> "
+
+[MODULE_PARAMETERS]
+Osmocom_VTY_Functions.mp_prompt_prefix := "OsmoSTP";
+
+[MAIN_CONTROLLER]
+
+[EXECUTE]
diff --git a/stp/STP_Tests.ttcn b/stp/STP_Tests.ttcn
new file mode 100644
index 0000000..023c53d
--- /dev/null
+++ b/stp/STP_Tests.ttcn
@@ -0,0 +1,249 @@
+module STP_Tests {
+
+/* Osmocom STP test suite in in TTCN-3
+ * (C) 2019 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 General_Types all;
+import from Osmocom_Types all;
+import from IPL4asp_Types all;
+
+import from Osmocom_VTY_Functions all;
+
+import from M3UA_Types all;
+import from M3UA_Templates all;
+import from M3UA_CodecPort all;
+import from M3UA_CodecPort_CtrlFunct all;
+
+import from M3UA_Emulation all;
+import from MTP3asp_Types all;
+import from MTP3asp_PortType all;
+
+import from SCCP_Types all;
+import from SCCP_Templates all;
+import from SCCPasp_Types all;
+import from SCCP_Emulation all;
+
+import from IPA_Emulation all;
+
+import from STP_Tests_Common all;
+import from STP_Tests_IPA all;
+import from STP_Tests_M3UA all;
+
+
+type component IPA_M3UA_CT extends RAW_M3UA_CT, IPA_CT {
+};
+
+const OCT1 c_M3UA_SI_SCCP := '03'O;
+
+
+/* copy+pasted from SCCP_Emulation.ttcn, where for some reason it is marked as "runs on SCCP_CT"
+ * without depending on anything of that component */
+  function ConvertASPAddressToEncodedAddress_itu( in SCCP_PAR_Address pl_ASPAddress)
+    return SCCP_param_CPartyAddressEnc
+  {
+
+    var SCCP_param_CPartyAddress_itu vl_PDUAddress; //structured fit to encoding
+    var SCCP_param_CPartyAddressEnc vl_PDUAddressEncoded;
+
+    vl_PDUAddress.addressIndicator.pointCodeIndic:=
+      pl_ASPAddress.addressIndicator.pointCodeIndic;
+
+    vl_PDUAddress.addressIndicator.ssnIndicator:=
+      pl_ASPAddress.addressIndicator.ssnIndicator;
+
+    vl_PDUAddress.addressIndicator.globalTitleIndic:=
+     pl_ASPAddress.addressIndicator.globalTitleIndic;
+
+    vl_PDUAddress.addressIndicator.routingIndicator:=
+      pl_ASPAddress.addressIndicator.routingIndicator;
+
+    vl_PDUAddress.addressIndicator.reserved:='0'B;
+    // if (ischosen(pl_ASPAddress.signPointCode) ) not used because it is mandatory field (???)
+
+    //----signPointCode handling
+    if ( ispresent( pl_ASPAddress.signPointCode )) {
+      vl_PDUAddress.signPointCode :=
+        '00'B&pl_ASPAddress.signPointCode;
+    } else {
+      vl_PDUAddress.signPointCode := omit;
+    };
+
+    //----subsystemNumber handling
+    if ( ispresent( pl_ASPAddress.subsystemNumber ) ){
+      vl_PDUAddress.subsystemNumber := pl_ASPAddress.subsystemNumber;
+    } else {
+      vl_PDUAddress.subsystemNumber :=omit;
+    };
+
+    // --- globalTitle handling--
+    if ( ispresent(pl_ASPAddress.globalTitle))
+    {//startif1
+
+      var SCCP_ASPfield_GlobalTitle tmpGT ;
+      tmpGT := pl_ASPAddress.globalTitle;
+
+      if (ischosen(tmpGT.gti0001))
+      {
+        vl_PDUAddress.globalTitle.gti0001.natureOfAddress:=tmpGT.gti0001.natureOfAddress;
+        vl_PDUAddress.globalTitle.gti0001.oddeven:=tmpGT.gti0001.oddeven;
+        vl_PDUAddress.globalTitle.gti0001.globalTitleAddress:=tmpGT.gti0001.globalTitleAddress;
+      }
+      else if (ischosen(tmpGT.gti0010))
+      {
+        vl_PDUAddress.globalTitle.gti0010.translationType:=tmpGT.gti0010.translationType;
+        vl_PDUAddress.globalTitle.gti0010.globalTitleAddress:=tmpGT.gti0010.globalTitleAddress;
+      }
+      else if (ischosen(tmpGT.gti0011))
+      {
+        vl_PDUAddress.globalTitle.gti0011.translationType:=tmpGT.gti0011.translationType;
+        vl_PDUAddress.globalTitle.gti0011.encodingScheme:=tmpGT.gti0011.encodingScheme;
+        vl_PDUAddress.globalTitle.gti0011.numberingPlan:=tmpGT.gti0011.numberingPlan;
+        vl_PDUAddress.globalTitle.gti0011.globalTitleAddress:=tmpGT.gti0011.globalTitleAddress;
+      }
+      else if (ischosen(tmpGT.gti0100))
+      {
+        vl_PDUAddress.globalTitle.gti0100.translationType:=tmpGT.gti0100.translationType;
+        vl_PDUAddress.globalTitle.gti0100.encodingScheme:=tmpGT.gti0100.encodingScheme;
+        vl_PDUAddress.globalTitle.gti0100.numberingPlan:=tmpGT.gti0100.numberingPlan;
+        vl_PDUAddress.globalTitle.gti0100.natureOfAddress:=tmpGT.gti0100.natureOfAddress;
+        vl_PDUAddress.globalTitle.gti0100.reserved:='0'B;
+        vl_PDUAddress.globalTitle.gti0100.globalTitleAddress:=tmpGT.gti0100.globalTitleAddress;
+      }
+    }
+    else
+    {
+        vl_PDUAddress.globalTitle := omit;
+    };
+
+    vl_PDUAddressEncoded.addr:= enc_PDU_SCCP_Address_itu( vl_PDUAddress);
+    vl_PDUAddressEncoded.paramLength:= lengthof(vl_PDUAddressEncoded.addr);
+
+    return vl_PDUAddressEncoded;
+
+  } //ConvertASPAddressToEncodedAddress_itu
+
+template (value) PDU_SCCP ts_SCCP_UDT(SCCP_PAR_Address called, SCCP_PAR_Address calling,
+					template (value) octetstring data,
+					template (value) BIT4 msg_hdl := '0000'B) := {
+	unitdata := {
+		messageType := udt,
+		protClass := {'0000'B, msg_hdl},
+		pointer1 := 0,
+		pointer2 := 0,
+		pointer3 := 0,
+		calledPAddress := ConvertASPAddressToEncodedAddress_itu(called),
+		callingPAddress := ConvertASPAddressToEncodedAddress_itu(calling),
+		data := {
+			paramLength := 0,
+			data := data
+		}
+	}
+}
+
+/* Test routing of SCCP between an M3UA and an IPA ASP */
+testcase TC_m3ua_to_ipa() runs on IPA_M3UA_CT {
+	var OCT4 rctx_sender := int2oct(1023, 4);
+	var OCT4 pc_sender := int2oct(23, 4);
+	var OCT4 pc_receiver := int2oct(5, 4);
+
+	f_init_m3ua();
+	f_init_ipa();
+
+	f_M3UA_asp_up_act(0, omit, omit); // TODO: rctx
+
+	/* send a well-formed, encoded SCCP message via M3UA */
+	var octetstring data := f_rnd_octstring(f_rnd_int(100));
+	var SCCP_PAR_Address called := valueof(ts_SccpAddr_GT('1234'H));
+	var SCCP_PAR_Address calling := valueof(ts_SccpAddr_GT('5678'H));
+	var PDU_SCCP sccp := valueof(ts_SCCP_UDT(called, calling, data));
+	var octetstring sccp_enc := enc_PDU_SCCP(sccp);
+	var template (value) M3UA_Protocol_Data tx_pd;
+	tx_pd := ts_M3UA_protocol_data(pc_sender, pc_receiver, c_M3UA_SI_SCCP, '00'O, '00'O, '00'O, sccp_enc);
+	f_M3UA_send(0, ts_M3UA_DATA(rctx_sender, tx_pd), 1);
+
+	/* expect to receive it via IPA */
+	f_IPA_exp(0, sccp_enc);
+}
+
+/* test routing an SCCP message from IPA ASP to M3UA ASP */
+testcase TC_ipa_to_m3ua() runs on IPA_M3UA_CT {
+	var OCT4 pc_sender := int2oct(5, 4);
+	var OCT4 rctx_receiver := int2oct(1023, 4);
+	var OCT4 pc_receiver := int2oct(23, 4);
+
+	f_init_common();
+	f_vty_config2(VTY, {"cs7 instance 0", "as mahlzeit ipa"},
+		      "point-code override patch-sccp disabled");
+
+	f_init_m3ua();
+	f_init_ipa();
+
+	f_M3UA_asp_up_act(0, omit, omit); // TODO: rctx
+
+	/* send a well-formed, encoded SCCP message via IPA */
+	var octetstring data := f_rnd_octstring(f_rnd_int(100));
+	var SCCP_PAR_Address called := valueof(ts_SccpAddr_GT('1234'H));
+	var SCCP_PAR_Address calling := valueof(ts_SccpAddr_GT('5678'H));
+	var PDU_SCCP sccp := valueof(ts_SCCP_UDT(called, calling, data));
+	var octetstring sccp_enc := enc_PDU_SCCP(sccp);
+	f_IPA_send(0, sccp_enc);
+
+	/* expect to receive it via M3UA */
+	var template (present) M3UA_Protocol_Data rx_pd;
+	rx_pd := tr_M3UA_protocol_data(pc_sender, pc_receiver, c_M3UA_SI_SCCP, '00'O, '00'O, '00'O, sccp_enc);
+	f_M3UA_exp(0, tr_M3UA_DATA(rctx_receiver, rx_pd));
+}
+
+/* test routing an SCCP message from IPA ASP to M3UA ASP while patching PC into SCCP addresses */
+testcase TC_ipa_to_m3ua_patch_sccp() runs on IPA_M3UA_CT {
+	var OCT4 pc_sender := int2oct(5, 4);
+	var OCT4 rctx_receiver := int2oct(1023, 4);
+	var OCT4 pc_receiver := int2oct(23, 4);
+
+	f_init_common();
+	f_vty_config2(VTY, {"cs7 instance 0", "as mahlzeit ipa"},
+			"point-code override patch-sccp both");
+
+	f_init_m3ua();
+	f_init_ipa();
+
+	f_M3UA_asp_up_act(0, omit, omit); // TODO: rctx
+
+	/* send a well-formed, encoded SCCP message via IPA */
+	var octetstring data := f_rnd_octstring(f_rnd_int(100));
+	var SCCP_PAR_Address called := valueof(ts_SccpAddr_GT('1234'H));
+	var SCCP_PAR_Address calling := valueof(ts_SccpAddr_GT('5678'H));
+	var PDU_SCCP sccp := valueof(ts_SCCP_UDT(called, calling, data));
+	f_IPA_send(0, enc_PDU_SCCP(sccp));
+
+	/* patch point codes into addresses */
+	called := valueof(ts_SccpAddr_PC_GT(oct2int(pc_receiver), '83'O, "mtp3_itu", '1234'H));
+	calling := valueof(ts_SccpAddr_PC_GT(oct2int(pc_sender), '83'O, "mtp3_itu", '5678'H));
+	var PDU_SCCP sccp_exp := valueof(ts_SCCP_UDT(called, calling, data));
+
+	/* expect to receive it via M3UA */
+	var template (present) M3UA_Protocol_Data rx_pd;
+	rx_pd := tr_M3UA_protocol_data(pc_sender, pc_receiver, c_M3UA_SI_SCCP, '00'O, '00'O, '00'O,
+					enc_PDU_SCCP(sccp_exp));
+	f_M3UA_exp(0, tr_M3UA_DATA(rctx_receiver, rx_pd));
+}
+
+
+
+control {
+	/* M3UA <-> IPA Tests */
+	execute( TC_m3ua_to_ipa() );
+	execute( TC_ipa_to_m3ua() );
+	execute( TC_ipa_to_m3ua_patch_sccp() );
+}
+
+
+
+}
diff --git a/stp/STP_Tests_Common.ttcn b/stp/STP_Tests_Common.ttcn
new file mode 100644
index 0000000..aee5a88
--- /dev/null
+++ b/stp/STP_Tests_Common.ttcn
@@ -0,0 +1,64 @@
+module STP_Tests_Common {
+
+/* Osmocom STP test suite in in TTCN-3
+ * (C) 2019 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
+ */
+
+friend module STP_Tests;
+friend module STP_Tests_M3UA;
+friend module STP_Tests_IPA;
+
+import from TELNETasp_PortType all;
+import from Osmocom_VTY_Functions all;
+
+import from IPL4asp_Types all;
+
+modulepar {
+	charstring mp_stp_ip := "127.0.0.1";
+	charstring mp_local_ip := "127.0.0.1";
+}
+
+friend template (value) SctpTuple ts_SCTP(template (omit) integer ppid := 3,
+					   template (omit) integer stream := 0) := {
+	sinfo_stream := stream,
+	sinfo_ppid := ppid,
+	remSocks := omit,
+	assocId := omit
+}
+
+type component Test_CT {
+	port TELNETasp_PT VTY;
+	timer g_Tguard := 30.0;
+	var boolean g_test_initialized := false;
+}
+
+private altstep as_gTguard() runs on Test_CT {
+	[] g_Tguard.timeout {
+		setverdict(fail, "Global guard timer timed out");
+		mtc.stop;
+		}
+}
+
+friend function f_init_common() runs on Test_CT {
+	if (g_test_initialized) {
+		return;
+	}
+	g_test_initialized := true;
+
+	map(self:VTY, system:VTY);
+	f_vty_set_prompts(VTY);
+	f_vty_transceive(VTY, "enable");
+
+	activate(as_gTguard());
+	g_Tguard.start;
+}
+
+
+
+}
diff --git a/stp/STP_Tests_IPA.ttcn b/stp/STP_Tests_IPA.ttcn
new file mode 100644
index 0000000..0a36921
--- /dev/null
+++ b/stp/STP_Tests_IPA.ttcn
@@ -0,0 +1,142 @@
+module STP_Tests_IPA {
+
+/* Osmocom STP test suite in in TTCN-3
+ * (C) 2019 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
+ */
+
+friend module STP_Tests;
+
+import from General_Types all;
+import from Osmocom_Types all;
+import from IPL4asp_Types all;
+
+import from TELNETasp_PortType all;
+import from Osmocom_VTY_Functions all;
+
+import from SCCP_Types all;
+import from SCCP_Templates all;
+import from SCCPasp_Types all;
+import from SCCP_Emulation all;
+
+import from IPA_Emulation all;
+
+import from M3UA_Emulation all;
+import from M3UA_CodecPort all;
+import from MTP3asp_Types all;
+import from MTP3asp_PortType all;
+
+import from STP_Tests_Common all;
+
+private const integer NR_IPA := 1;
+
+modulepar {
+	integer mp_stp_ipa_port := 5000;
+	integer mp_local_ipa_port := 20000;
+}
+
+type component IPA_CT extends Test_CT {
+	/* for IPA we use the IPA_Emulation and not directly IPA_CodecPort to avoid
+	 * having to re-invent IPA CCM handling here */
+	port MTP3asp_PT IPA[NR_IPA];
+	var IPA_Emulation_CT vc_IPA[NR_IPA];
+}
+
+friend function f_IPA_send(integer idx, octetstring data) runs on IPA_CT {
+	var MTP3_Field_sio sio := { ni := '00'B, prio := '00'B, si := '0011'B };
+	IPA[idx].send(t_ASP_MTP3_TRANSFERreq(sio, 0, 0, 0, data));
+}
+
+friend function f_IPA_exp(integer idx, template (present) octetstring data) runs on IPA_CT {
+	var M3UA_RecvFrom rx;
+	alt {
+	[] IPA[idx].receive(t_ASP_MTP3_TRANSFERind(?, ?, ?, ?, data)) {
+		setverdict(pass);
+		}
+	[] IPA[idx].receive {
+		setverdict(fail, "Received unexpected data on IPA port while waiting for ", data);
+		mtc.stop;
+		}
+	}
+}
+
+friend function f_init_ipa() runs on IPA_CT {
+	var integer i;
+
+	f_init_common();
+
+	for (i := 0; i < NR_IPA; i:=i+1) {
+		vc_IPA[i] := IPA_Emulation_CT.create("IPA" & int2str(i));
+		map(vc_IPA[i]:IPA_PORT, system:IPA_CODEC_PT);
+		connect(self:IPA[i], vc_IPA[i]:MTP3_SP_PORT);
+		vc_IPA[i].start(IPA_Emulation.main_client(mp_stp_ip, mp_stp_ipa_port, mp_local_ip,
+				mp_local_ipa_port+i));
+	}
+}
+
+
+
+/* "accept-asp-connections pre-configured" and client from unknown source */
+testcase TC_unknown_client_nodynamic() runs on IPA_CT {
+	f_init_common();
+	f_vty_config2(VTY, {"cs7 instance 0", "listen ipa 5000"},
+		      "accept-asp-connections pre-configured");
+	f_init_ipa();
+	f_sleep(1.0);
+	if (IPA[0].checkstate("Connected")) {
+		setverdict(fail, "Expected IPA port to be disconnected");
+	} else {
+		setverdict(pass);
+	}
+	/* switch back to default */
+	f_vty_config2(VTY, {"cs7 instance 0", "listen ipa 5000"},
+		      "accept-asp-connections dynamic-permitted");
+}
+
+/* "accept-asp-connections pre-configured" and client from known source */
+testcase TC_known_client_nodynamic() runs on IPA_CT {
+	f_init_common();
+	f_vty_config2(VTY, {"cs7 instance 0", "listen ipa 5000"},
+		      "accept-asp-connections pre-configured");
+	f_vty_config2(VTY, {"cs7 instance 0"}, "asp ipa-mahlzeit0 20000 5000 ipa");
+	f_vty_config2(VTY, {"cs7 instance 0", "as mahlzeit ipa"}, "asp ipa-mahlzeit0");
+	f_init_ipa();
+	f_sleep(1.0);
+	if (not IPA[0].checkstate("Connected")) {
+		setverdict(fail, "Expected IPA port to be connected");
+	} else {
+		setverdict(pass);
+	}
+	/* switch back to default */
+	f_vty_config2(VTY, {"cs7 instance 0", "listen ipa 5000"},
+		      "accept-asp-connections dynamic-permitted");
+	f_vty_config2(VTY, {"cs7 instance 0"}, "no asp ipa-mahlzeit0");
+}
+
+
+/* "accept-asp-connections dynamic-permitted" and client from unknown source */
+testcase TC_unknown_client_dynamic() runs on IPA_CT {
+	f_init_common();
+	f_init_ipa();
+	f_sleep(1.0);
+	if (not IPA[0].checkstate("Connected")) {
+		setverdict(fail, "Expected IPA port to be connected");
+	} else {
+		setverdict(pass);
+	}
+}
+
+
+control {
+	execute( TC_unknown_client_nodynamic() );
+	execute( TC_known_client_nodynamic() );
+	execute( TC_unknown_client_dynamic() );
+}
+
+
+}
diff --git a/stp/STP_Tests_M3UA.ttcn b/stp/STP_Tests_M3UA.ttcn
new file mode 100644
index 0000000..29e85f9
--- /dev/null
+++ b/stp/STP_Tests_M3UA.ttcn
@@ -0,0 +1,451 @@
+module STP_Tests_M3UA {
+
+/* Osmocom STP test suite in in TTCN-3
+ * (C) 2019 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
+ */
+
+friend module STP_Tests;
+
+import from General_Types all;
+import from Osmocom_Types all;
+import from IPL4asp_Types all;
+
+import from Osmocom_VTY_Functions all;
+
+import from M3UA_Types all;
+import from M3UA_Templates all;
+import from M3UA_CodecPort all;
+import from M3UA_CodecPort_CtrlFunct all;
+
+import from M3UA_Emulation all;
+import from MTP3asp_Types all;
+import from MTP3asp_PortType all;
+
+import from SCCP_Types all;
+import from SCCP_Templates all;
+import from SCCPasp_Types all;
+import from SCCP_Emulation all;
+
+import from STP_Tests_Common all;
+
+modulepar {
+	integer mp_stp_m3ua_port := 2905;
+	integer mp_local_m3ua_port := 9999;
+}
+
+private const integer NR_M3UA := 3;
+
+type component RAW_M3UA_CT extends Test_CT {
+	port M3UA_CODEC_PT M3UA[NR_M3UA];
+	var integer g_m3ua_conn_id[NR_M3UA];
+}
+
+private template PortEvent tr_SctpAssocChange := {
+	sctpEvent := {
+		sctpAssocChange := ?
+	}
+}
+private template PortEvent tr_SctpPeerAddrChange := {
+	sctpEvent := {
+		sctpPeerAddrChange := ?
+	}
+}
+
+private altstep as_m3ua_sctp() runs on RAW_M3UA_CT {
+	[] any from M3UA.receive(tr_SctpAssocChange) { repeat; }
+	[] any from M3UA.receive(tr_SctpPeerAddrChange) { repeat; }
+}
+
+friend function f_M3UA_send(integer idx, template (present) PDU_M3UA msg, template integer stream := 0)
+runs on RAW_M3UA_CT {
+	M3UA[idx].send(t_M3UA_Send(g_m3ua_conn_id[idx], msg, stream));
+}
+
+friend function f_M3UA_exp(integer idx, template (present) PDU_M3UA msg) runs on RAW_M3UA_CT {
+	var M3UA_RecvFrom rx;
+	alt {
+	[] M3UA[idx].receive(t_M3UA_RecvFrom(msg)) {
+		setverdict(pass);
+		}
+	[] M3UA[idx].receive(t_M3UA_RecvFrom(?)) -> value rx {
+		setverdict(fail, "Received unexpected M3UA[", idx, "] ", rx,
+			   "while waiting for ", msg);
+		mtc.stop;
+		}
+	}
+}
+
+friend function f_M3UA_connect(integer i) runs on RAW_M3UA_CT {
+	var Result res;
+	res := M3UA_CodecPort_CtrlFunct.f_IPL4_connect(M3UA[i], mp_stp_ip, mp_stp_m3ua_port,
+						       mp_local_ip, mp_local_m3ua_port+i, -1,
+							{sctp:=valueof(ts_SCTP)});
+	if (not ispresent(res.connId)) {
+		setverdict(fail, "Could not connect M3UA socket, check your configuration");
+	mtc.stop;
+	}
+	g_m3ua_conn_id[i] := res.connId;
+}
+
+friend function f_init_m3ua() runs on RAW_M3UA_CT {
+	var integer i;
+
+	f_init_common();
+
+	activate(as_m3ua_sctp());
+
+	for (i := 0; i < NR_M3UA; i:=i+1) {
+		map(self:M3UA[i], system:M3UA_CODEC_PT);
+		f_M3UA_connect(i);
+	}
+}
+
+/* perform an outbound ASP-UP procedure */
+friend function f_M3UA_asp_up(integer idx, template (omit) OCT4 aspid := omit) runs on RAW_M3UA_CT {
+	f_M3UA_send(idx, ts_M3UA_ASPUP(aspid));
+	f_M3UA_exp(idx, tr_M3UA_ASPUP_ACK);
+}
+
+/* perform an outbound BEAT procedure */
+friend function f_M3UA_beat(integer idx, template (omit) octetstring hbd) runs on RAW_M3UA_CT {
+	if (istemplatekind(hbd, "omit")) {
+		f_M3UA_send(idx, ts_M3UA_BEAT(omit));
+		f_M3UA_exp(idx, tr_M3UA_BEAT_ACK(omit));
+	} else {
+		f_M3UA_send(idx, ts_M3UA_BEAT(ts_M3UA_hb_data(hbd)));
+		f_M3UA_exp(idx, tr_M3UA_BEAT_ACK(tr_M3UA_hb_data(hbd)));
+	}
+}
+
+/* perform an outbound ASP-ACTIVATE procedure */
+friend function f_M3UA_asp_act(integer idx, template (omit) M3UA_Traffic_Mode_Type tmt := omit,
+				template (omit) OCT4 rctx := omit) runs on RAW_M3UA_CT {
+	f_M3UA_send(idx, ts_M3UA_ASPAC(tmt, rctx));
+	f_M3UA_exp(idx, tr_M3UA_ASPAC_ACK(tmt, rctx));
+}
+
+/* perform outbound ASP-UP and ASP-ACT, optionally expect interemittent NOTIFY */
+friend function f_M3UA_asp_up_act(integer idx, template (omit) M3UA_Traffic_Mode_Type tmt := omit,
+				   template (omit) OCT4 rctx := omit,
+				   template (omit) OCT2 ntfy_after_up := c_M3UA_ST_I_AS_INACTIVE,
+				   template (omit) OCT2 ntfy_after_act := c_M3UA_ST_I_AS_ACTIVE)
+runs on RAW_M3UA_CT {
+	f_M3UA_asp_up(idx, omit);
+	if (not istemplatekind(ntfy_after_up, "omit")) {
+		f_M3UA_exp(idx, tr_M3UA_NOTIFY(c_M3UA_ST_T_STATE_CHG, ntfy_after_up, *));
+	}
+	f_M3UA_asp_act(idx, tmt, rctx);
+	if (not istemplatekind(ntfy_after_act, "omit")) {
+		f_M3UA_exp(idx, tr_M3UA_NOTIFY(c_M3UA_ST_T_STATE_CHG, ntfy_after_act, *));
+	}
+}
+
+
+/* Test the ASP-UP procedure */
+testcase TC_connect_asp_up() runs on RAW_M3UA_CT {
+	f_init_m3ua();
+	f_M3UA_asp_up(0);
+	f_M3UA_exp(0, tr_M3UA_NOTIFY(c_M3UA_ST_T_STATE_CHG, c_M3UA_ST_I_AS_INACTIVE, *));
+}
+
+/* Test the heartbeat procedure without optional heartbeat data payload */
+testcase TC_beat() runs on RAW_M3UA_CT {
+	f_init_m3ua();
+	f_M3UA_asp_up(0);
+	f_M3UA_exp(0, tr_M3UA_NOTIFY(c_M3UA_ST_T_STATE_CHG, c_M3UA_ST_I_AS_INACTIVE, *));
+	f_M3UA_beat(0, omit);
+}
+
+/* Test the heartbeat procedure with optional heartbeat data payload */
+testcase TC_beat_payload() runs on RAW_M3UA_CT {
+	f_init_m3ua();
+	f_M3UA_asp_up(0);
+	f_M3UA_exp(0, tr_M3UA_NOTIFY(c_M3UA_ST_T_STATE_CHG, c_M3UA_ST_I_AS_INACTIVE, *));
+	f_M3UA_beat(0, 'a1a2a3a4a5'O);
+}
+
+/* Test the ASP-ACTIVATE procedure (without traffic-mode or routing ctx) */
+testcase TC_asp_act() runs on RAW_M3UA_CT {
+	f_init_m3ua();
+	f_M3UA_asp_up_act(0);
+}
+
+/* Test the ASP-ACTIVATE procedure with traffic-mode override */
+testcase TC_asp_act_override() runs on RAW_M3UA_CT {
+	f_init_m3ua();
+	f_M3UA_asp_up_act(0, c_M3UA_TMT_override, omit);
+}
+
+/* Test the ASP-ACTIVATE procedure with traffic-mode override */
+testcase TC_asp_act_loadshare() runs on RAW_M3UA_CT {
+	f_init_m3ua();
+	f_M3UA_asp_up_act(0, c_M3UA_TMT_loadshare, omit);
+}
+
+/* Test the ASP-ACTIVATE procedure with traffic-mode broadcast */
+testcase TC_asp_act_broadcast() runs on RAW_M3UA_CT {
+	f_init_m3ua();
+	f_M3UA_asp_up_act(0, c_M3UA_TMT_broadcast, omit);
+}
+
+/* Test if traffic is routed from idx_tx/pc_tx to idx_rx/pc_rx */
+private function f_test_traffic(integer idx_tx, template (omit) OCT4 rctx_sender, OCT4 pc_tx,
+				integer idx_rx, template (omit) OCT4 rctx_receiver, OCT4 pc_rx,
+				OCT1 si := '23'O, OCT1 ni := '00'O, OCT1 mp := '00'O, OCT1 sls := '00'O)
+runs on RAW_M3UA_CT {
+	var octetstring data := f_rnd_octstring(f_rnd_int(100));
+	f_M3UA_send(idx_tx, ts_M3UA_DATA(rctx_sender,
+					 ts_M3UA_protocol_data(pc_tx, pc_rx, si, ni, mp, sls, data)), 1);
+	f_M3UA_exp(idx_rx, tr_M3UA_DATA(rctx_receiver,
+					tr_M3UA_protocol_data(pc_tx, pc_rx, si, ni, mp, sls, data)));
+}
+
+
+/* test "traffic-mode override" behavior */
+testcase TC_tmt_override() runs on RAW_M3UA_CT {
+	var OCT4 rctx_sender := int2oct(1023, 4);
+	var OCT4 pc_sender := int2oct(23, 4);
+	var OCT4 rctx_receiver := int2oct(1042, 4);
+	var OCT4 pc_receiver := int2oct(42, 4);
+
+	f_init_m3ua();
+
+	/* bring up the 'sender' side (single ASP in AS) */
+	f_M3UA_asp_up_act(0, omit, omit);
+
+	/* activate the first 'receiver' side ASP */
+	f_M3UA_asp_up_act(1, c_M3UA_TMT_override, rctx_receiver);
+
+	/* verify traffic is routed from sender to [sole] receiver */
+	f_test_traffic(0, rctx_sender, pc_sender, 1, rctx_receiver, pc_receiver);
+
+	/* activate the second 'receiver' side ASP (no NOTIFY as AS state doesn't change) */
+	f_M3UA_asp_up_act(2, c_M3UA_TMT_override, rctx_receiver, omit, omit);
+
+	/* we expect a NOTIFY to the *other* ASP Other/Alternat-ASP-Active */
+	f_M3UA_exp(1, tr_M3UA_NOTIFY(c_M3UA_ST_T_OTHER, c_M3UA_ST_I_ALTERNATE_ASP, *));
+
+	/* verify traffic is routed from sender to new receiver */
+	f_test_traffic(0, rctx_sender, pc_sender, 2, rctx_receiver, pc_receiver);
+
+}
+
+private altstep as_count_rx(integer idx, template (present) PDU_M3UA exp, inout integer counter)
+runs on RAW_M3UA_CT {
+	[] M3UA[idx].receive(t_M3UA_RecvFrom(exp)) {
+		counter := counter + 1;
+		}
+}
+
+/* test "traffic-mode load-share" behavior */
+testcase TC_tmt_loadshare() runs on RAW_M3UA_CT {
+	var OCT4 rctx_sender := int2oct(1023, 4);
+	var OCT4 pc_sender := int2oct(23, 4);
+	var OCT4 rctx_receiver := int2oct(1042, 4);
+	var OCT4 pc_receiver := int2oct(42, 4);
+	var integer i;
+
+	f_init_m3ua();
+
+	/* FIXME: configure the STP via VTY to set traffic-mode */
+
+	/* bring up the 'sender' side (single ASP in AS) */
+	f_M3UA_asp_up_act(0, omit, rctx_sender);
+
+	/* activate the first 'receiver' side ASP */
+	f_M3UA_asp_up(1);
+	f_M3UA_asp_up_act(1, c_M3UA_TMT_loadshare, omit); // TODO: rctx
+
+	/* verify traffic is routed from sender to [sole] receiver */
+	for (i := 0; i < 10; i := i+1) {
+		f_test_traffic(0, rctx_sender, pc_sender, 1, rctx_receiver, pc_receiver);
+	}
+
+	/* activate the second 'receiver' side ASP (no NOTIFY) */
+	f_M3UA_asp_up_act(2, c_M3UA_TMT_loadshare, omit, omit, omit); // TODO: rctx
+
+	/* verify traffic is routed from sender to new receiver */
+	const integer iter_per_asp := 5;
+	var integer num_rx[NR_M3UA] := { 0, 0, 0 };
+	for (i := 0; i < 2*iter_per_asp; i := i+1) {
+		var octetstring data := f_rnd_octstring(f_rnd_int(100));
+		var template (value) M3UA_Protocol_Data tx_pd;
+		var template (present) M3UA_Protocol_Data rx_pd;
+		tx_pd := ts_M3UA_protocol_data(pc_sender, pc_receiver, '23'O, '00'O, '00'O, '00'O, data);
+		rx_pd := tr_M3UA_protocol_data(pc_sender, pc_receiver, '23'O, '00'O, '00'O, '00'O, data);
+		f_M3UA_send(0, ts_M3UA_DATA(rctx_sender, tx_pd), 1);
+		alt {
+		[] as_count_rx(1, tr_M3UA_DATA(rctx_receiver, rx_pd), num_rx[1])
+		[] as_count_rx(2, tr_M3UA_DATA(rctx_receiver, rx_pd), num_rx[2])
+		}
+	}
+	/* FIXME: check for extraneous messages? */
+	for (i := 1; i <= 2; i := i+1) {
+		if (num_rx[i] != iter_per_asp) {
+			setverdict(fail, "Received only ", num_rx[i], " out of expected ", iter_per_asp,
+				   "M3UA DATA messages");
+		}
+	}
+	setverdict(pass);
+}
+
+/* test "traffic-mode broadcast" behavior */
+testcase TC_tmt_broadcast() runs on RAW_M3UA_CT {
+	var OCT4 rctx_sender := int2oct(1023, 4);
+	var OCT4 pc_sender := int2oct(23, 4);
+	var OCT4 rctx_receiver := int2oct(1042, 4);
+	var OCT4 pc_receiver := int2oct(42, 4);
+	var integer i;
+
+	f_init_m3ua();
+
+	/* FIXME: configure the STP via VTY to set traffic-mode */
+
+	/* bring up the 'sender' side (single ASP in AS) */
+	f_M3UA_asp_up_act(0, omit, omit); // TODO: rctx
+
+	/* activate the first 'receiver' side ASP */
+	f_M3UA_asp_up(1);
+	f_M3UA_asp_up_act(1, c_M3UA_TMT_broadcast, omit); // TODO: rctx
+
+	/* verify traffic is routed from sender to [sole] receiver */
+	for (i := 0; i < 10; i := i+1) {
+		f_test_traffic(0, rctx_sender, pc_sender, 1, rctx_receiver, pc_receiver);
+	}
+
+	/* activate the second 'receiver' side ASP */
+	f_M3UA_asp_up_act(2, c_M3UA_TMT_broadcast, omit, omit, omit); // TODO: rctx
+
+	/* verify traffic is routed from sender to new receiver */
+	for (i := 0; i < 10; i := i+1) {
+		var octetstring data := f_rnd_octstring(f_rnd_int(100));
+		var template (value) M3UA_Protocol_Data tx_pd;
+		var template (present) M3UA_Protocol_Data rx_pd;
+		tx_pd := ts_M3UA_protocol_data(pc_sender, pc_receiver, '23'O, '00'O, '00'O, '00'O, data);
+		rx_pd := tr_M3UA_protocol_data(pc_sender, pc_receiver, '23'O, '00'O, '00'O, '00'O, data);
+		f_M3UA_send(0, ts_M3UA_DATA(rctx_sender, tx_pd), 1);
+		/* each message must be received both on 1 and 2 */
+		f_M3UA_exp(1, tr_M3UA_DATA(rctx_receiver, rx_pd));
+		f_M3UA_exp(2, tr_M3UA_DATA(rctx_receiver, rx_pd));
+	}
+	setverdict(pass);
+}
+
+private function f_M3UA_rkm_register(OCT4 id, OCT3 dpc, OCT4 rctx,
+				     template (present) OCT4 exp_status := c_M3UA_REGSTS_SUCCESS)
+runs on RAW_M3UA_CT
+{
+	f_M3UA_send(0, ts_M3UA_REG_REQ({ts_M3UA_rkey(id:=id, dpc:=dpc, rctx:=rctx)}));
+	f_M3UA_exp(0, tr_M3UA_REG_RSP({tr_M3UA_reg_res(id:=id, status:=exp_status, rctx:=rctx)}));
+}
+
+/* Send RKM registration; expect -EPERM as RCTX doesn't match config and dynamic not permitted */
+testcase TC_rkm_reg_static_notpermitted() runs on RAW_M3UA_CT {
+	f_init_m3ua();
+
+	f_M3UA_send(0, ts_M3UA_REG_REQ({ts_M3UA_rkey(id:='00000099'O, dpc:='aabbcc'O)}));
+	f_M3UA_exp(0, tr_M3UA_REG_RSP({tr_M3UA_reg_res(id:='00000099'O, status:=c_M3UA_REGSTS_ERR_EPERM,
+						       rctx:=?)}));
+}
+
+/* Send RKM registration; expect OK as RCTX does match config */
+testcase TC_rkm_reg_static_permitted() runs on RAW_M3UA_CT {
+	var OCT3 dpc := int2oct(23, 3); // must match config
+	var OCT4 rctx := int2oct(1023, 4);  // must match config
+
+	f_init_m3ua();
+
+	f_M3UA_send(0, ts_M3UA_REG_REQ({ts_M3UA_rkey(id:='10000099'O, dpc:=dpc, rctx:=rctx)}));
+	f_M3UA_exp(0, tr_M3UA_REG_RSP({tr_M3UA_reg_res(id:='10000099'O, status:=c_M3UA_REGSTS_SUCCESS,
+						       rctx:=rctx)}));
+}
+
+/* Send RKM registration; expect OK as dynamic not permitted */
+testcase TC_rkm_reg_dynamic_permitted() runs on RAW_M3UA_CT {
+	f_init_common();
+	f_vty_config2(VTY, {"cs7 instance 0"}, "xua rkm routing-key-allocation dynamic-permitted");
+	f_init_m3ua();
+
+	f_M3UA_send(0, ts_M3UA_REG_REQ({ts_M3UA_rkey(id:='20000099'O, dpc:='aabbcc'O)}));
+	f_M3UA_exp(0, tr_M3UA_REG_RSP({tr_M3UA_reg_res(id:='20000099'O, status:=c_M3UA_REGSTS_SUCCESS,
+						       rctx:=?)}));
+
+	f_vty_config2(VTY, {"cs7 instance 0"}, "xua rkm routing-key-allocation static-only");
+}
+
+/* try to de-register a routing key that was never registered -> error */
+testcase TC_rkm_unreg_never_registered() runs on RAW_M3UA_CT {
+	f_init_m3ua();
+	f_M3UA_send(0, ts_M3UA_DEREG_REQ(ts_M3UA_routing_ctx(int2oct(1023,4))));
+	f_M3UA_exp(0, tr_M3UA_DEREG_RSP({tr_M3UA_dereg_res(?,c_m3UA_DEREGSTS_ERR_NOT_REG)}));
+}
+
+/* try to de-register a routing key that is invalid (non-existant) -> error */
+testcase TC_rkm_unreg_invalid() runs on RAW_M3UA_CT {
+	f_init_m3ua();
+	f_M3UA_send(0, ts_M3UA_DEREG_REQ(ts_M3UA_routing_ctx(int2oct(1234,4))));
+	f_M3UA_exp(0, tr_M3UA_DEREG_RSP({tr_M3UA_dereg_res(?,c_m3UA_DEREGSTS_ERR_INVAL_RCTX)}));
+}
+
+/* try to de-register a routing key that was registered -> OK*/
+testcase TC_rkm_unreg_registered() runs on RAW_M3UA_CT {
+	f_init_m3ua();
+	f_M3UA_send(0, ts_M3UA_DEREG_REQ(ts_M3UA_routing_ctx(int2oct(1023,4))));
+	f_M3UA_exp(0, tr_M3UA_DEREG_RSP({tr_M3UA_dereg_res(?,c_m3UA_DEREGSTS_SUCCESS)}));
+}
+
+/* try to de-register a routing key for an active ASP -> ERROR */
+testcase TC_rkm_unreg_active() runs on RAW_M3UA_CT {
+	var OCT3 dpc := int2oct(23, 3); // must match config
+	var OCT4 rctx := int2oct(1023, 4);  // must match config
+
+	f_init_m3ua();
+
+	/* first register the routing key */
+	f_M3UA_rkm_register(id:='30000099'O, dpc:=dpc, rctx:=rctx);
+
+	/* then activate the ASP */
+	f_M3UA_asp_up_act(0);
+
+	/* then try to de-regsiter */
+	f_M3UA_send(0, ts_M3UA_DEREG_REQ(ts_M3UA_routing_ctx(rctx)));
+	f_M3UA_exp(0, tr_M3UA_DEREG_RSP({tr_M3UA_dereg_res(?,c_m3UA_DEREGSTS_ERR_ASP_ACTIVE)}));
+	/* FIXME: we now may have changed the state on the STP side! */
+}
+
+
+control {
+	/* M3UA Tests */
+	execute( TC_connect_asp_up() );
+	execute( TC_beat() );
+	execute( TC_beat_payload() );
+	execute( TC_asp_act() );
+	execute( TC_asp_act_override() );
+	execute( TC_asp_act_loadshare() );
+	execute( TC_asp_act_broadcast() );
+	execute( TC_tmt_override() );
+	execute( TC_tmt_loadshare() );
+	execute( TC_tmt_broadcast() );
+
+	/* M3UA RKM tests */
+	execute( TC_rkm_reg_static_notpermitted() );
+	execute( TC_rkm_reg_static_permitted() );
+	execute( TC_rkm_reg_dynamic_permitted() );
+	execute( TC_rkm_unreg_never_registered() );
+	execute( TC_rkm_unreg_invalid() );
+	execute( TC_rkm_unreg_registered() );
+	execute( TC_rkm_unreg_active() );
+	/* TODO: test RKM with unsupported routing keys: NA, SI, OPC */
+	/* TODO: register/unregister multiple routing contexts in one message; including mixed
+	         success/failure situations */
+}
+
+
+
+}
diff --git a/stp/gen_links.sh b/stp/gen_links.sh
new file mode 100755
index 0000000..aaf3421
--- /dev/null
+++ b/stp/gen_links.sh
@@ -0,0 +1,57 @@
+#!/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"
+FILES+=" TCCEncoding_Functions.ttcn TCCEncoding.cc " # GSM 7-bit coding
+gen_links $DIR $FILES
+
+DIR=$BASEDIR/titan.TestPorts.Common_Components.Socket-API/src
+FILES="Socket_API_Definitions.ttcn"
+gen_links $DIR $FILES
+
+# Required by MGCP and IPA
+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
+
+# required by M3UA_Emulation
+DIR=$BASEDIR/titan.ProtocolModules.M3UA/src
+FILES="M3UA_Types.ttcn"
+gen_links $DIR $FILES
+
+# required by M3UA_Emulation
+DIR=$BASEDIR/titan.TestPorts.SCTPasp/src
+FILES="SCTPasp_PT.cc  SCTPasp_PT.hh  SCTPasp_PortType.ttcn  SCTPasp_Types.ttcn"
+gen_links $DIR $FILES
+
+# required by M3UA Emulation
+DIR=$BASEDIR/titan.TestPorts.MTP3asp/src
+FILES="MTP3asp_PortType.ttcn  MTP3asp_Types.ttcn"
+gen_links $DIR $FILES
+
+# required by SCCP Emulation
+DIR=$BASEDIR/titan.ProtocolEmulations.M3UA/src
+FILES="M3UA_Emulation.ttcn"
+gen_links $DIR $FILES
+
+DIR=$BASEDIR/titan.ProtocolEmulations.SCCP/src
+FILES="SCCP_Emulation.ttcn  SCCP_EncDec.cc  SCCP_Mapping.ttcnpp  SCCP_Types.ttcn  SCCPasp_Types.ttcn"
+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=../library
+FILES="Misc_Helpers.ttcn General_Types.ttcn GSM_Types.ttcn Osmocom_Types.ttcn Osmocom_VTY_Functions.ttcn Native_Functions.ttcn Native_FunctionDefs.cc "
+FILES+="IPA_Types.ttcn IPA_Emulation.ttcnpp IPA_CodecPort.ttcn IPA_CodecPort_CtrlFunct.ttcn IPA_CodecPort_CtrlFunctDef.cc "
+FILES+="Osmocom_CTRL_Types.ttcn Osmocom_CTRL_Functions.ttcn Osmocom_CTRL_Adapter.ttcn "
+FILES+="SCCP_Templates.ttcn "
+FILES+="M3UA_Templates.ttcn M3UA_CodecPort.ttcn M3UA_CodecPort_CtrlFunct.ttcn M3UA_CodecPort_CtrlFunctDef.cc "
+gen_links $DIR $FILES
+
+ignore_pp_results
diff --git a/stp/osmo-stp.cfg b/stp/osmo-stp.cfg
new file mode 100644
index 0000000..d5eccba
--- /dev/null
+++ b/stp/osmo-stp.cfg
@@ -0,0 +1,63 @@
+!
+! OsmoSTP (1.1.0.2-3884) configuration saved from vty
+!!
+!
+log stderr
+ logging filter all 1
+ logging color 1
+ logging print category-hex 1
+ logging print category 1
+ logging timestamp 0
+ logging print file 1
+ logging level lglobal notice
+ logging level llapd notice
+ logging level linp notice
+ logging level lmux notice
+ logging level lmi notice
+ logging level lmib notice
+ logging level lsms notice
+ logging level lctrl notice
+ logging level lgtp notice
+ logging level lstats notice
+ logging level lgsup notice
+ logging level loap notice
+ logging level lss7 debug
+ logging level lsccp debug
+ logging level lsua debug
+ logging level lm3ua debug
+ logging level lmgcp notice
+ logging level ljibuf notice
+ logging level lrspro notice
+!
+stats interval 5
+!
+line vty
+ no login
+!
+cs7 instance 0
+ point-code format 24
+ asp asp-sender 9999 2905 m3ua
+  local-ip 127.0.0.1
+  remote-ip 127.0.0.1
+ asp asp-receiver0 10000 2905 m3ua
+  local-ip 127.0.0.1
+  remote-ip 127.0.0.1
+ asp asp-receiver1 10001 2905 m3ua
+  local-ip 127.0.0.1
+  remote-ip 127.0.0.1
+ as as-sender m3ua
+  asp asp-sender
+  routing-key 1023 23
+ as as-receiver m3ua
+  asp asp-receiver0
+  routing-key 1042 42
+ as mahlzeit ipa
+  routing-key 0 5
+  point-code override dpc 23
+ route-table system
+  update route 23 16777215 linkset as-sender
+  update route 42 16777215 linkset as-receiver
+ listen m3ua 2905
+  accept-asp-connections dynamic-permitted
+ listen ipa 5000
+  accept-asp-connections dynamic-permitted
diff --git a/stp/regen_makefile.sh b/stp/regen_makefile.sh
new file mode 100755
index 0000000..be96829
--- /dev/null
+++ b/stp/regen_makefile.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+FILES="*.ttcn *.ttcnpp SCCP_EncDec.cc SCTPasp_PT.cc TCCConversion.cc  TCCInterface.cc IPL4asp_PT.cc IPL4asp_discovery.cc IPA_CodecPort_CtrlFunctDef.cc TELNETasp_PT.cc Native_FunctionDefs.cc TCCEncoding.cc M3UA_CodecPort_CtrlFunctDef.cc "
+
+export CPPFLAGS_TTCN3="-DIPA_EMULATION_CTRL -DIPA_EMULATION_SCCP -DUSE_MTP3_DISTRIBUTOR"
+
+../regen-makefile.sh STP_Tests.ttcn $FILES

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/15822
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: I628a87385cac0dfe708a0d74a5088fbd5a4790cd
Gerrit-Change-Number: 15822
Gerrit-PatchSet: 3
Gerrit-Owner: laforge <laforge at osmocom.org>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge at osmocom.org>
Gerrit-Reviewer: pespin <pespin at sysmocom.de>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20191112/d0145bdd/attachment.htm>


More information about the gerrit-log mailing list