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

Change subject: library: Add S1AP CodecPort/Emulation
......................................................................

library: Add S1AP CodecPort/Emulation

Change-Id: I9bfba3ab2a3830e590b203c44c03b9c9383fff99
---
A library/S1AP_CodecPort.ttcn
A library/S1AP_CodecPort_CtrlFunct.ttcn
A library/S1AP_CodecPort_CtrlFunctDef.cc
A library/S1AP_Emulation.ttcn
M mme/gen_links.sh
M mme/regen_makefile.sh
6 files changed, 871 insertions(+), 1 deletion(-)

Approvals:
  laforge: Looks good to me, approved
  pespin: Looks good to me, but someone else must approve
  Jenkins Builder: Verified



diff --git a/library/S1AP_CodecPort.ttcn b/library/S1AP_CodecPort.ttcn
new file mode 100644
index 0000000..59cef18
--- /dev/null
+++ b/library/S1AP_CodecPort.ttcn
@@ -0,0 +1,82 @@
+module S1AP_CodecPort {
+
+/* Simple S1AP Codec Port, translating between raw SCTP primitives with
+ * octetstring payload towards the IPL4asp provider, and S1AP primitives
+ * which carry the decoded S1AP data types as payload.
+ *
+ * (C) 2019 by Harald Welte <lafo...@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 S1AP_PDU_Descriptions all;
+       import from S1AP_Types all;
+
+       type record S1AP_RecvFrom {
+               ConnectionId    connId,
+               HostName        remName,
+               PortNumber      remPort,
+               HostName        locName,
+               PortNumber      locPort,
+               S1AP_PDU        msg
+       };
+
+       template S1AP_RecvFrom t_S1AP_RecvFrom(template S1AP_PDU msg) := {
+               connId := ?,
+               remName := ?,
+               remPort := ?,
+               locName := ?,
+               locPort := ?,
+               msg := msg
+       }
+
+       type record S1AP_Send {
+               ConnectionId    connId,
+               S1AP_PDU        msg
+       }
+
+       template S1AP_Send t_S1AP_Send(template ConnectionId connId, template 
S1AP_PDU msg) := {
+               connId := connId,
+               msg := msg
+       }
+
+       private function IPL4_to_S1AP_RecvFrom(in ASP_RecvFrom pin, out 
S1AP_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_S1AP_PDU(pin.msg);
+       } with { extension "prototype(fast)" };
+
+       private function S1AP_to_IPL4_Send(in S1AP_Send pin, out ASP_Send pout) 
{
+               pout.connId := pin.connId;
+               pout.proto := {
+                       sctp := {
+                               sinfo_stream := omit,
+                               sinfo_ppid := 18,
+                               remSocks := omit,
+                               assocId := omit
+                       }
+               };
+               pout.msg := enc_S1AP_PDU(pin.msg);
+       } with { extension "prototype(fast)" };
+
+       type port S1AP_CODEC_PT message {
+               out     S1AP_Send;
+               in      S1AP_RecvFrom,
+                       ASP_ConnId_ReadyToRelease,
+                       ASP_Event;
+       } with { extension "user IPL4asp_PT
+               out(S1AP_Send -> ASP_Send:function(S1AP_to_IPL4_Send))
+               in(ASP_RecvFrom -> S1AP_RecvFrom: 
function(IPL4_to_S1AP_RecvFrom);
+                  ASP_ConnId_ReadyToRelease -> ASP_ConnId_ReadyToRelease: 
simple;
+                  ASP_Event -> ASP_Event: simple)"
+       }
+}
diff --git a/library/S1AP_CodecPort_CtrlFunct.ttcn 
b/library/S1AP_CodecPort_CtrlFunct.ttcn
new file mode 100644
index 0000000..0399199
--- /dev/null
+++ b/library/S1AP_CodecPort_CtrlFunct.ttcn
@@ -0,0 +1,44 @@
+module S1AP_CodecPort_CtrlFunct {
+
+  import from S1AP_CodecPort all;
+  import from IPL4asp_Types all;
+
+  external function f_IPL4_listen(
+    inout S1AP_CODEC_PT portRef,
+    in HostName locName,
+    in PortNumber locPort,
+    in ProtoTuple proto,
+    in OptionList options := {}
+  ) return Result;
+
+  external function f_IPL4_connect(
+    inout S1AP_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 S1AP_CODEC_PT portRef,
+    in ConnectionId id,
+    in ProtoTuple proto := { unspecified := {} }
+  ) return Result;
+
+  external function f_IPL4_setUserData(
+    inout S1AP_CODEC_PT portRef,
+    in ConnectionId id,
+    in UserData userData
+  ) return Result;
+
+  external function f_IPL4_getUserData(
+    inout S1AP_CODEC_PT portRef,
+    in ConnectionId id,
+    out UserData userData
+  ) return Result;
+
+}
+
diff --git a/library/S1AP_CodecPort_CtrlFunctDef.cc 
b/library/S1AP_CodecPort_CtrlFunctDef.cc
new file mode 100644
index 0000000..dc73046
--- /dev/null
+++ b/library/S1AP_CodecPort_CtrlFunctDef.cc
@@ -0,0 +1,56 @@
+#include "IPL4asp_PortType.hh"
+#include "S1AP_CodecPort.hh"
+#include "IPL4asp_PT.hh"
+
+namespace S1AP__CodecPort__CtrlFunct {
+
+  IPL4asp__Types::Result f__IPL4__listen(
+    S1AP__CodecPort::S1AP__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(
+    S1AP__CodecPort::S1AP__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(
+    S1AP__CodecPort::S1AP__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(
+    S1AP__CodecPort::S1AP__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(
+    S1AP__CodecPort::S1AP__CODEC__PT& portRef,
+    const IPL4asp__Types::ConnectionId& connId,
+    IPL4asp__Types::UserData& userData)
+  {
+    return f__IPL4__PROVIDER__getUserData(portRef, connId, userData);
+  }
+
+}
+
diff --git a/library/S1AP_Emulation.ttcn b/library/S1AP_Emulation.ttcn
new file mode 100644
index 0000000..d09b499
--- /dev/null
+++ b/library/S1AP_Emulation.ttcn
@@ -0,0 +1,681 @@
+module S1AP_Emulation {
+
+/* S1AP Emulation, runs on top of S1AP_CodecPort.  It multiplexes/demultiplexes
+ * the individual IMSIs/subscribers, so there can be separate TTCN-3 
components handling
+ * each of them.
+ *
+ * The S1AP_Emulation.main() function processes S1AP primitives from the S1AP
+ * socket via the S1AP_CodecPort, and dispatches them to the per-IMSI 
components.
+ *
+ * For each new IMSI, the S1apOps.create_cb() is called.  It can create
+ * or resolve a TTCN-3 component, and returns a component reference to which 
that IMSI
+ * is routed/dispatched.
+ *
+ * If a pre-existing component wants to register to handle a future inbound 
IMSI, it can
+ * do so by registering an "expect" with the expected IMSI.
+ *
+ * Inbound non-UE related S1AP messages (such as RESET, SETUP, OVERLOAD) are 
dispatched to
+ * the S1apOps.unitdata_cb() callback, which is registered with an argument to 
the
+ * main() function below.
+ *
+ * (C) 2019 by Harald Welte <lafo...@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 S1AP_CodecPort all;
+import from S1AP_CodecPort_CtrlFunct all;
+import from S1AP_Types all;
+import from S1AP_Constants all;
+import from S1AP_PDU_Contents all;
+import from S1AP_PDU_Descriptions all;
+import from S1AP_IEs all;
+import from S1AP_Templates all;
+
+import from NAS_EPS_Types all;
+import from NAS_Templates all;
+
+import from LTE_CryptoFunctions all;
+
+import from General_Types all;
+import from Osmocom_Types all;
+import from IPL4asp_Types all;
+import from DNS_Helpers all;
+
+
+type component S1AP_ConnHdlr {
+       port S1AP_Conn_PT S1AP;
+       /* procedure based port to register for incoming connections */
+       port S1APEM_PROC_PT S1AP_PROC;
+}
+
+/* port between individual per-connection components and this dispatcher */
+type port S1AP_Conn_PT message {
+       inout S1AP_PDU, PDU_NAS_EPS, S1APEM_Config;
+} with { extension "internal" };
+
+type record NAS_Keys {
+       octetstring k_nas_int,
+       octetstring k_nas_enc
+};
+type union S1APEM_Config {
+       NAS_Keys set_nas_keys
+};
+
+type enumerated S1APEM_EventUpDown {
+       S1APEM_EVENT_DOWN,
+       S1APEM_EVENT_UP
+}
+
+/* an event indicating us whether or not a connection is physically up or down,
+ * and whether we have received an ID_ACK */
+type union S1APEM_Event {
+       S1APEM_EventUpDown      up_down
+}
+
+/* global test port e.g. for non-imsi/conn specific messages */
+type port S1AP_PT message {
+       inout S1AP_PDU, S1APEM_Event;
+} with { extension "internal" };
+
+
+/* represents a single S1AP Association */
+type record AssociationData {
+       S1AP_ConnHdlr   comp_ref,                       /* component handling 
this UE connection */
+       uint24_t        enb_ue_s1ap_id optional,        /* eNB side S1AP ID */
+       uint32_t        mme_ue_s1ap_id optional,        /* MME side S1AP ID */
+       EUTRAN_CGI      cgi optional,
+       TAI             tai optional,
+       NAS_UE_State    nus
+
+       //hexstring     imsi optional
+};
+
+type component S1AP_Emulation_CT {
+       /* Port facing to the UDP SUT */
+       port S1AP_CODEC_PT S1AP;
+       /* All S1AP_ConnHdlr S1AP ports connect here
+        * S1AP_Emulation_CT.main needs to figure out what messages
+        * to send where with CLIENT.send() to vc_conn */
+       port S1AP_Conn_PT S1AP_CLIENT;
+       /* currently tracked connections */
+       var AssociationData S1apAssociationTable[16];
+       /* pending expected CRCX */
+       var ExpectData S1apExpectTable[8];
+       /* procedure based port to register for incoming connections */
+       port S1APEM_PROC_PT S1AP_PROC;
+       /* test port for unit data messages */
+       port S1AP_PT S1AP_UNIT;
+
+       var S1AP_conn_parameters g_pars;
+       var charstring g_s1ap_id;
+       var integer g_s1ap_conn_id := -1;
+}
+
+type function S1APCreateCallback(S1AP_PDU msg, template (omit) MME_UE_S1AP_ID 
mme_id,
+                                template (omit) ENB_UE_S1AP_ID enb_id, 
charstring id)
+runs on S1AP_Emulation_CT return S1AP_ConnHdlr;
+
+type function S1APUnitdataCallback(S1AP_PDU msg)
+runs on S1AP_Emulation_CT return template S1AP_PDU;
+
+type record S1APOps {
+       S1APCreateCallback create_cb,
+       S1APUnitdataCallback unitdata_cb
+}
+
+type record S1AP_conn_parameters {
+       HostName remote_ip,
+       PortNumber remote_sctp_port,
+       HostName local_ip,
+       PortNumber local_sctp_port,
+       NAS_Role role
+}
+
+function tr_S1AP_RecvFrom_R(template S1AP_PDU msg)
+runs on S1AP_Emulation_CT return template S1AP_RecvFrom {
+       var template S1AP_RecvFrom mrf := {
+               connId := g_s1ap_conn_id,
+               remName := ?,
+               remPort := ?,
+               locName := ?,
+               locPort := ?,
+               msg := msg
+       }
+       return mrf;
+}
+
+private function f_s1ap_ids_known(template (omit) MME_UE_S1AP_ID mme_id,
+                                 template (omit) ENB_UE_S1AP_ID enb_id)
+runs on S1AP_Emulation_CT return boolean {
+       var integer i;
+       log("f_s1ap_ids_known(",mme_id,", ",enb_id,")");
+       for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
+               log("tbl[",i,"]: mme=", S1apAssociationTable[i].mme_ue_s1ap_id,
+                   ", enb=", S1apAssociationTable[i].enb_ue_s1ap_id);
+               /* skip empty records */
+               if (S1apAssociationTable[i].mme_ue_s1ap_id == omit and
+                   S1apAssociationTable[i].enb_ue_s1ap_id == omit) {
+                       log("skipping empty ", i);
+                       continue;
+               }
+               if (S1apAssociationTable[i].mme_ue_s1ap_id == omit) {
+                       log("entry ", i, " has no MME ID yet (enb=", 
S1apAssociationTable[i].enb_ue_s1ap_id);
+                       /* Table doesn't yet know the MME side ID, let's 
look-up only
+                        * based on the eNB side ID */
+                       if (match(S1apAssociationTable[i].enb_ue_s1ap_id, 
enb_id)) {
+                               /* update table with MME side ID */
+                               S1apAssociationTable[i].mme_ue_s1ap_id := 
valueof(mme_id);
+                               return true;
+                       }
+               } else if (match(S1apAssociationTable[i].enb_ue_s1ap_id, 
enb_id) and
+                       match(S1apAssociationTable[i].mme_ue_s1ap_id, mme_id)) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+private function f_comp_known(S1AP_ConnHdlr client)
+runs on S1AP_Emulation_CT return boolean {
+       var integer i;
+       for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
+               if (S1apAssociationTable[i].comp_ref == client) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+private function f_assoc_id_by_s1ap_ids(template (omit) MME_UE_S1AP_ID mme_id,
+                                   template (omit) ENB_UE_S1AP_ID enb_id)
+runs on S1AP_Emulation_CT return integer {
+       var integer i;
+       for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
+               if (match(S1apAssociationTable[i].enb_ue_s1ap_id, enb_id)) {
+                       if (istemplatekind(mme_id, "omit")) {
+                               return i;
+                       } else {
+                               if 
(match(S1apAssociationTable[i].mme_ue_s1ap_id, mme_id)) {
+                                       return i;
+                               }
+                       }
+               }
+       }
+       setverdict(fail, "S1AP Association Table not found by ENB-ID=", enb_id, 
" MME-ID=", mme_id);
+       mtc.stop;
+}
+
+private function f_assoc_id_by_comp(S1AP_ConnHdlr client)
+runs on S1AP_Emulation_CT return integer {
+       var integer i;
+       for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
+               if (S1apAssociationTable[i].comp_ref == client) {
+                       return i;
+               }
+       }
+       setverdict(fail, "S1AP Association Table not found by component ", 
client);
+       mtc.stop;
+}
+
+private function f_assoc_by_comp(S1AP_ConnHdlr client)
+runs on S1AP_Emulation_CT return AssociationData {
+       var integer i := f_assoc_id_by_comp(client);
+       return S1apAssociationTable[i];
+}
+
+private function f_s1ap_id_table_add(S1AP_ConnHdlr comp_ref,
+                                    template (omit) MME_UE_S1AP_ID mme_id, 
ENB_UE_S1AP_ID enb_id)
+runs on S1AP_Emulation_CT return integer {
+       var integer i;
+       for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
+               if (not isvalue(S1apAssociationTable[i].enb_ue_s1ap_id)) {
+                       S1apAssociationTable[i].enb_ue_s1ap_id := enb_id;
+                       if (istemplatekind(mme_id, "omit")) {
+                               S1apAssociationTable[i].mme_ue_s1ap_id := omit;
+                       } else {
+                               S1apAssociationTable[i].mme_ue_s1ap_id := 
valueof(mme_id);
+                       }
+                       S1apAssociationTable[i].comp_ref := comp_ref;
+                       return i;
+               }
+       }
+       testcase.stop("S1AP Association Table full!");
+       return -1;
+}
+
+private function f_s1ap_id_table_del(S1AP_ConnHdlr comp_ref, ENB_UE_S1AP_ID 
enb_id)
+runs on S1AP_Emulation_CT {
+       var integer i;
+       for (i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
+               if (S1apAssociationTable[i].comp_ref == comp_ref and
+                   S1apAssociationTable[i].mme_ue_s1ap_id == enb_id) {
+                       S1apAssociationTable[i].enb_ue_s1ap_id := omit;
+                       S1apAssociationTable[i].mme_ue_s1ap_id := omit;
+                       S1apAssociationTable[i].comp_ref := null;
+                       return;
+               }
+       }
+       setverdict(fail, "S1AP Association Table: Couldn't find to-be-deleted 
entry!");
+       mtc.stop;
+}
+
+
+private function f_s1ap_id_table_init()
+runs on S1AP_Emulation_CT {
+       for (var integer i := 0; i < sizeof(S1apAssociationTable); i := i+1) {
+               S1apAssociationTable[i].mme_ue_s1ap_id := omit;
+               S1apAssociationTable[i].enb_ue_s1ap_id := omit;
+               S1apAssociationTable[i].cgi := omit;
+               S1apAssociationTable[i].tai := omit;
+               S1apAssociationTable[i].nus := 
valueof(t_NAS_UE_State(g_pars.role));
+       }
+}
+
+private template (value) SctpTuple ts_SCTP(template (omit) integer ppid := 18) 
:= {
+       sinfo_stream := omit,
+       sinfo_ppid := ppid,
+       remSocks := omit,
+       assocId := omit
+};
+
+private template PortEvent tr_SctpAssocChange := {
+       sctpEvent := {
+               sctpAssocChange := ?
+       }
+}
+private template PortEvent tr_SctpPeerAddrChange := {
+       sctpEvent := {
+               sctpPeerAddrChange := ?
+       }
+}
+
+private function f_s1ap_xceive(template (value) S1AP_PDU tx,
+                               template S1AP_PDU rx_t := ?)
+runs on S1AP_Emulation_CT  return S1AP_PDU {
+       timer T := 10.0;
+       var S1AP_RecvFrom mrf;
+
+       S1AP.send(t_S1AP_Send(g_s1ap_conn_id, tx));
+       alt {
+       [] S1AP.receive(tr_S1AP_RecvFrom_R(rx_t)) -> value mrf { }
+       [] S1AP.receive(tr_SctpAssocChange) { repeat; }
+       [] S1AP.receive(tr_SctpPeerAddrChange)  { repeat; }
+       [] T.timeout {
+               setverdict(fail, "Timeout waiting for ", rx_t);
+               mtc.stop;
+               }
+       }
+       return mrf.msg;
+}
+
+/*
+private function f_nas_try_decaps(PDU_NAS_EPS nas) return PDU_NAS_EPS
+{
+       var PDU_NAS_EPS_SecurityProtectedNASMessage secp_nas;
+       if (not match(nas, tr_NAS_EMM_SecurityProtected)) {
+               return nas;
+       }
+       secp_nas := 
nas.ePS_messages.ePS_MobilityManagement.pDU_NAS_EPS_SecurityProtectedNASMessage;
+       select (secp_nas.securityHeaderType) {
+       case ('0011'B) {
+               var octetstring knas_int := '530ce32318f26264eab26bc116870b86'O;
+               var octetstring data_with_seq := 
int2oct(secp_nas.sequenceNumber, 1) & secp_nas.nAS_Message;
+               var OCT4 exp_mac := f_snow_3g_f9(knas_int, 
secp_nas.sequenceNumber, 0,
+                                                is_downlink:=true, 
data:=data_with_seq);
+               if (exp_mac != secp_nas.messageAuthenticationCode) {
+                       setverdict(fail, "Received NAS MAC ", 
secp_nas.messageAuthenticationCode,
+                                  " doesn't match expected MAC ", exp_mac, ": 
", nas);
+                       mtc.stop;
+               }
+               return dec_PDU_NAS_EPS(secp_nas.nAS_Message);
+               }
+       case else  {
+               setverdict(fail, "Implement SecHdrType for ", secp_nas);
+               mtc.stop;
+               }
+       }
+}
+*/
+
+function main(S1APOps ops, S1AP_conn_parameters p, charstring id) runs on 
S1AP_Emulation_CT {
+       var Result res;
+       g_pars := p;
+       g_s1ap_id := id;
+       f_s1ap_id_table_init();
+       f_expect_table_init();
+
+       map(self:S1AP, system:S1AP_CODEC_PT);
+       if (p.remote_sctp_port == -1) {
+               res := S1AP_CodecPort_CtrlFunct.f_IPL4_listen(S1AP, p.local_ip, 
p.local_sctp_port, { sctp := valueof(ts_SCTP) });
+       } else {
+               res := S1AP_CodecPort_CtrlFunct.f_IPL4_connect(S1AP, 
p.remote_ip, p.remote_sctp_port,
+                                                               p.local_ip, 
p.local_sctp_port, -1, { sctp := valueof(ts_SCTP) });
+       }
+       if (not ispresent(res.connId)) {
+               setverdict(fail, "Could not connect S1AP socket, check your 
configuration");
+               mtc.stop;
+       }
+       g_s1ap_conn_id := res.connId;
+
+       /* notify user about SCTP establishment */
+       if (p.remote_sctp_port != -1) {
+               S1AP_UNIT.send(S1APEM_Event:{up_down:=S1APEM_EVENT_UP})
+       }
+
+       while (true) {
+               var S1AP_ConnHdlr vc_conn;
+               var PDU_NAS_EPS nas;
+               var hexstring imsi;
+               var S1AP_RecvFrom mrf;
+               var S1AP_PDU msg;
+               var S1APEM_Config s1cfg;
+               var charstring vlr_name, mme_name;
+               var integer ai;
+
+               alt {
+               /* Configuration primitive from client */
+               [] S1AP_CLIENT.receive(S1APEM_Config:{set_nas_keys:=?}) -> 
value s1cfg sender vc_conn {
+                       var integer assoc_id := f_assoc_id_by_comp(vc_conn);
+                       S1apAssociationTable[assoc_id].nus.k_nas_int := 
s1cfg.set_nas_keys.k_nas_int;
+                       S1apAssociationTable[assoc_id].nus.k_nas_enc := 
s1cfg.set_nas_keys.k_nas_enc;
+                       }
+               /* S1AP from client: InitialUE */
+               [] S1AP_CLIENT.receive(tr_S1AP_InitialUE) -> value msg sender 
vc_conn {
+                       /* create a table entry about this connection */
+                       ai := f_s1ap_id_table_add(vc_conn, omit, 
valueof(f_S1AP_get_ENB_UE_S1AP_ID(msg)));
+                       /* Store CGI + TAI so we can use it for generating 
UlNasTransport from NAS */
+                       S1apAssociationTable[ai].tai := 
msg.initiatingMessage.value_.InitialUEMessage.protocolIEs[2].value_.TAI;
+                       S1apAssociationTable[ai].cgi := 
msg.initiatingMessage.value_.InitialUEMessage.protocolIEs[3].value_.EUTRAN_CGI;
+                       /* Pass message through */
+                       S1AP.send(t_S1AP_Send(g_s1ap_conn_id, msg));
+                       }
+               /* NAS from client: Wrap in S1AP Uplink NAS Transport */
+               [] S1AP_CLIENT.receive(PDU_NAS_EPS:?) -> value nas sender 
vc_conn {
+                       var integer assoc_id := f_assoc_id_by_comp(vc_conn);
+                       var AssociationData ad := 
S1apAssociationTable[assoc_id];
+                       nas := f_nas_encaps(S1apAssociationTable[assoc_id].nus, 
nas, new_ctx := false);
+                       var octetstring nas_enc := enc_PDU_NAS_EPS(nas);
+                       S1AP.send(t_S1AP_Send(g_s1ap_conn_id,
+                                             
ts_S1AP_UlNasTransport(ad.mme_ue_s1ap_id,
+                                                                    
ad.enb_ue_s1ap_id,
+                                                                    nas_enc, 
ad.cgi, ad.tai)));
+                       }
+               /* S1AP from client: pass on transparently */
+               [] S1AP_CLIENT.receive(S1AP_PDU:?) -> value msg sender vc_conn {
+                       /* Pass message through */
+                       /* FIXME: validate S1AP_IDs ? */
+                       S1AP.send(t_S1AP_Send(g_s1ap_conn_id, msg));
+                       }
+
+               /* non-UE related S1AP: pass through unmodified/unverified */
+               [] S1AP_UNIT.receive(S1AP_PDU:?) -> value msg sender vc_conn {
+                       /* Pass message through */
+                       S1AP.send(t_S1AP_Send(g_s1ap_conn_id, msg));
+                       }
+
+               /* S1AP received from peer (MME) */
+               [] S1AP.receive(tr_S1AP_RecvFrom_R(?)) -> value mrf {
+                       if (match(mrf.msg, tr_S1AP_nonUErelated)) {
+                               /* non-UE-related S1AP message */
+                               var template S1AP_PDU resp := 
ops.unitdata_cb.apply(mrf.msg);
+                               if (isvalue(resp)) {
+                                       S1AP.send(t_S1AP_Send(g_s1ap_conn_id, 
valueof(resp)));
+                               }
+                       } else if (match(mrf.msg, tr_S1AP_UeContextReleaseCmd)) 
{
+                               /* TODO: special handling, as it contains 
multiple eNB or MME IDs */
+                               setverdict(fail, "implement UeContextReleaseCmd 
handling");
+                               mtc.stop;
+                       } else {
+                               /* Ue-related S1AP message */
+                               /* obtain MME + ENB UE S1AP ID */
+                               var template (omit) MME_UE_S1AP_ID mme_ue_id := 
f_S1AP_get_MME_UE_S1AP_ID(mrf.msg);
+                               var template (omit) ENB_UE_S1AP_ID enb_ue_id := 
f_S1AP_get_ENB_UE_S1AP_ID(mrf.msg);
+                               /* check if those IDs are known in our table */
+                               if (f_s1ap_ids_known(mme_ue_id, enb_ue_id)) {
+                                       /* if yes, dispatch to the ConnHdlr for 
this Ue-Connection */
+                                       var template (omit) octetstring nas_enc;
+                                       var integer assoc_id  := 
f_assoc_id_by_s1ap_ids(mme_ue_id, enb_ue_id);
+                                       vc_conn := 
S1apAssociationTable[assoc_id].comp_ref;
+                                       nas_enc := f_S1AP_get_NAS_PDU(mrf.msg);
+                                       if (isvalue(nas_enc)) {
+                                               nas := 
dec_PDU_NAS_EPS(valueof(nas_enc));
+                                               if (match(nas, 
tr_NAS_EMM_SecurityProtected)) {
+                                                       nas := 
f_nas_try_decaps(S1apAssociationTable[assoc_id].nus, nas);
+                                               }
+                                               /* send decoded NAS */
+                                               S1AP_CLIENT.send(nas) to 
vc_conn;
+                                       } else {
+                                               /* send raw S1AP */
+                                               S1AP_CLIENT.send(mrf.msg) to 
vc_conn;
+                                       }
+                               } else {
+                                       /* if not, call create_cb so it can 
create new ConnHdlr */
+                                       vc_conn := ops.create_cb.apply(mrf.msg, 
mme_ue_id, enb_ue_id, id);
+                                       f_s1ap_id_table_add(vc_conn, mme_ue_id, 
valueof(enb_ue_id));
+                                       S1AP_CLIENT.send(mrf.msg) to vc_conn;
+                               }
+                       }
+                       }
+               [] S1AP.receive(tr_SctpAssocChange) { }
+               [] S1AP.receive(tr_SctpPeerAddrChange)  { }
+               [] S1AP_PROC.getcall(S1APEM_register:{?,?}) -> param(imsi, 
vc_conn) {
+                       f_create_expect(imsi, vc_conn);
+                       S1AP_PROC.reply(S1APEM_register:{imsi, vc_conn}) to 
vc_conn;
+                       }
+               }
+
+       }
+}
+
+/* "Expect" Handling */
+
+type record ExpectData {
+       hexstring imsi optional,
+       S1AP_ConnHdlr vc_conn
+}
+
+signature S1APEM_register(in hexstring imsi, in S1AP_ConnHdlr hdlr);
+
+type port S1APEM_PROC_PT procedure {
+       inout S1APEM_register;
+} with { extension "internal" };
+
+/* Function that can be used as create_cb and will usse the expect table */
+function ExpectedCreateCallback(S1AP_PDU msg, hexstring imsi, charstring id)
+runs on S1AP_Emulation_CT return S1AP_ConnHdlr {
+       var S1AP_ConnHdlr ret := null;
+       var integer i;
+
+       for (i := 0; i < sizeof(S1apExpectTable); i := i+1) {
+               if (not ispresent(S1apExpectTable[i].imsi)) {
+                       continue;
+               }
+               if (imsi == S1apExpectTable[i].imsi) {
+                       ret := S1apExpectTable[i].vc_conn;
+                       /* Release this entry */
+                       S1apExpectTable[i].imsi := omit;
+                       S1apExpectTable[i].vc_conn := null;
+                       log("Found Expect[", i, "] for ", msg, " handled at ", 
ret);
+                       return ret;
+               }
+       }
+       setverdict(fail, "Couldn't find Expect for ", msg);
+       mtc.stop;
+}
+
+private function f_create_expect(hexstring imsi, S1AP_ConnHdlr hdlr)
+runs on S1AP_Emulation_CT {
+       var integer i;
+
+       /* Check an entry like this is not already presnt */
+       for (i := 0; i < sizeof(S1apExpectTable); i := i+1) {
+               if (imsi == S1apExpectTable[i].imsi) {
+                       setverdict(fail, "IMSI already present", imsi);
+                       mtc.stop;
+               }
+       }
+       for (i := 0; i < sizeof(S1apExpectTable); i := i+1) {
+               if (not ispresent(S1apExpectTable[i].imsi)) {
+                       S1apExpectTable[i].imsi := imsi;
+                       S1apExpectTable[i].vc_conn := hdlr;
+                       log("Created Expect[", i, "] for ", imsi, " to be 
handled at ", hdlr);
+                       return;
+               }
+       }
+       testcase.stop("No space left in S1apExpectTable")
+}
+
+/* client/conn_hdlr side function to use procedure port to create expect in 
emulation */
+function f_create_s1ap_expect(hexstring imsi) runs on S1AP_ConnHdlr {
+       S1AP_PROC.call(S1APEM_register:{imsi, self}) {
+               [] S1AP_PROC.getreply(S1APEM_register:{?,?}) {};
+       }
+}
+
+
+private function f_expect_table_init()
+runs on S1AP_Emulation_CT {
+       var integer i;
+       for (i := 0; i < sizeof(S1apExpectTable); i := i + 1) {
+               S1apExpectTable[i].imsi := omit;
+       }
+}
+
+function DummyUnitdataCallback(S1AP_PDU msg)
+runs on S1AP_Emulation_CT return template S1AP_PDU {
+       log("Ignoring S1AP ", msg);
+       return omit;
+}
+
+
+function f_S1AP_get_ENB_UE_S1AP_ID(S1AP_PDU s1ap) return template (omit) 
ENB_UE_S1AP_ID
+{
+       if (ischosen(s1ap.initiatingMessage)) {
+               var InitiatingMessage im := s1ap.initiatingMessage;
+               select (s1ap) {
+               case (tr_S1AP_InitialUE) {
+                       return 
im.value_.InitialUEMessage.protocolIEs[0].value_.ENB_UE_S1AP_ID;
+                       }
+               case (tr_S1AP_DlNasTransport) {
+                       return 
im.value_.DownlinkNASTransport.protocolIEs[1].value_.ENB_UE_S1AP_ID;
+                       }
+               case (tr_S1AP_UlNasTransport) {
+                       return 
im.value_.UplinkNASTransport.protocolIEs[1].value_.ENB_UE_S1AP_ID;
+                       }
+               case (tr_S1AP_IntialCtxSetupReq) {
+                       return 
im.value_.initialContextSetupRequest.protocolIEs[1].value_.ENB_UE_S1AP_ID;
+                       }
+               case (tr_S1AP_UeContextReleaseReq) {
+                       return 
im.value_.UEContextReleaseRequest.protocolIEs[1].value_.ENB_UE_S1AP_ID;
+                       }
+               /* UeContextReleaseCmd needs special handling; it can contain 
any number of MME/UE IDs */
+               case (tr_S1AP_ConnEstInd) {
+                       return 
im.value_.ConnectionEstablishmentIndication.protocolIEs[1].value_.ENB_UE_S1AP_ID;
+                       }
+               /* TODO */
+               }
+       } else if (ischosen(s1ap.successfulOutcome)) {
+               var SuccessfulOutcome so := s1ap.successfulOutcome;
+               select (s1ap) {
+               case (tr_S1AP_InitialCtxSetupResp) {
+                       return 
so.value_.initialContextSetupResponse.protocolIEs[1].value_.ENB_UE_S1AP_ID;
+                       }
+               case (tr_S1AP_UeContextReleaseCompl) {
+                       return 
so.value_.UEContextReleaseComplete.protocolIEs[1].value_.ENB_UE_S1AP_ID;
+                       }
+               /* TODO */
+               }
+       } else if (ischosen(s1ap.unsuccessfulOutcome)) {
+               var UnsuccessfulOutcome uo := s1ap.unsuccessfulOutcome;
+               select (s1ap) {
+               case (tr_S1AP_InitialCtxSetupFail) {
+                       return 
uo.value_.initialContextSetupFailure.protocolIEs[1].value_.ENB_UE_S1AP_ID;
+                       }
+               /* TODO */
+               }
+       }
+       return omit;
+}
+
+function f_S1AP_get_MME_UE_S1AP_ID(S1AP_PDU s1ap) return template (omit) 
MME_UE_S1AP_ID
+{
+       if (ischosen(s1ap.initiatingMessage)) {
+               var InitiatingMessage im := s1ap.initiatingMessage;
+               select (s1ap) {
+               case (tr_S1AP_DlNasTransport) {
+                       return 
im.value_.DownlinkNASTransport.protocolIEs[0].value_.MME_UE_S1AP_ID;
+                       }
+               case (tr_S1AP_UlNasTransport) {
+                       return 
im.value_.UplinkNASTransport.protocolIEs[0].value_.MME_UE_S1AP_ID;
+                       }
+               case (tr_S1AP_IntialCtxSetupReq) {
+                       return 
im.value_.initialContextSetupRequest.protocolIEs[0].value_.MME_UE_S1AP_ID;
+                       }
+               case (tr_S1AP_UeContextReleaseReq) {
+                       return 
im.value_.UEContextReleaseRequest.protocolIEs[0].value_.MME_UE_S1AP_ID;
+                       }
+               /* UeContextReleaseCmd needs special handling; it can contain 
any number of MME/UE IDs */
+               case (tr_S1AP_ConnEstInd) {
+                       return 
im.value_.ConnectionEstablishmentIndication.protocolIEs[0].value_.MME_UE_S1AP_ID;
+                       }
+               /* TODO */
+               }
+       } else if (ischosen(s1ap.successfulOutcome)) {
+               var SuccessfulOutcome so := s1ap.successfulOutcome;
+               select (s1ap) {
+               case (tr_S1AP_InitialCtxSetupResp) {
+                       return 
so.value_.initialContextSetupResponse.protocolIEs[0].value_.MME_UE_S1AP_ID;
+                       }
+               case (tr_S1AP_UeContextReleaseCompl) {
+                       return 
so.value_.UEContextReleaseComplete.protocolIEs[0].value_.MME_UE_S1AP_ID;
+                       }
+               /* TODO */
+               }
+       } else if (ischosen(s1ap.unsuccessfulOutcome)) {
+               var UnsuccessfulOutcome uo := s1ap.unsuccessfulOutcome;
+               select (s1ap) {
+               case (tr_S1AP_InitialCtxSetupFail) {
+                       return 
uo.value_.initialContextSetupFailure.protocolIEs[0].value_.MME_UE_S1AP_ID;
+                       }
+               /* TODO */
+               }
+       }
+       return omit;
+}
+
+function f_S1AP_get_NAS_PDU(S1AP_PDU s1ap) return template (omit) NAS_PDU
+{
+       var integer i;
+
+       if (ischosen(s1ap.initiatingMessage)) {
+               var InitiatingMessage im := s1ap.initiatingMessage;
+               select (s1ap) {
+               case (tr_S1AP_DlNasTransport) {
+                       var DownlinkNASTransport msg := 
im.value_.DownlinkNASTransport;
+                       for (i := 0; i < lengthof(msg.protocolIEs); i := i+1) {
+                               if (msg.protocolIEs[i].id == id_NAS_PDU) {
+                                       return 
msg.protocolIEs[i].value_.NAS_PDU;
+                               }
+                       }
+                       }
+               case (tr_S1AP_UlNasTransport) {
+                       var UplinkNASTransport msg := 
im.value_.UplinkNASTransport;
+                       for (i := 0; i < lengthof(msg.protocolIEs); i := i+1) {
+                               if (msg.protocolIEs[i].id == id_NAS_PDU) {
+                                       return 
msg.protocolIEs[i].value_.NAS_PDU;
+                               }
+                       }
+                       }
+               }
+       }
+       return omit;
+}
+
+
+
+}
diff --git a/mme/gen_links.sh b/mme/gen_links.sh
index 2a98c70..1f2d85b 100755
--- a/mme/gen_links.sh
+++ b/mme/gen_links.sh
@@ -31,10 +31,17 @@
 FILES="SGsAP_Types.ttcn"
 gen_links $DIR $FILES

+DIR=../library/s1ap
+FILES="S1AP_CommonDataTypes.asn S1AP_Constants.asn S1AP_Containers.asn 
S1AP_IEs.asn S1AP_PDU_Contents.asn
+S1AP_PDU_Descriptions.asn "
+FILES+="S1AP_EncDec.cc  S1AP_Types.ttcn "
+gen_links $DIR $FILES
+
 DIR=../library
 FILES="Misc_Helpers.ttcn General_Types.ttcn GSM_Types.ttcn Osmocom_Types.ttcn 
Native_Functions.ttcn Native_FunctionDefs.cc "
 FILES+="SGsAP_Templates.ttcn SGsAP_CodecPort.ttcn 
SGsAP_CodecPort_CtrlFunct.ttcn SGsAP_CodecPort_CtrlFunctDef.cc 
SGsAP_Emulation.ttcn DNS_Helpers.ttcn "
 FILES+="L3_Templates.ttcn "
+FILES+="S1AP_CodecPort.ttcn "
 gen_links $DIR $FILES

 ignore_pp_results
diff --git a/mme/regen_makefile.sh b/mme/regen_makefile.sh
index 4d6d7c6..ee73658 100755
--- a/mme/regen_makefile.sh
+++ b/mme/regen_makefile.sh
@@ -1,6 +1,6 @@
 #!/bin/sh

-FILES="*.ttcn IPL4asp_PT.cc  IPL4asp_discovery.cc  Native_FunctionDefs.cc 
SGsAP_CodecPort_CtrlFunctDef.cc  TCCConversion.cc  TCCEncoding.cc  
TCCInterface.cc  TELNETasp_PT.cc "
+FILES="*.ttcn *.asn IPL4asp_PT.cc  IPL4asp_discovery.cc  
Native_FunctionDefs.cc SGsAP_CodecPort_CtrlFunctDef.cc  TCCConversion.cc  
TCCEncoding.cc  TCCInterface.cc  TELNETasp_PT.cc S1AP_EncDec.cc "

 export CPPFLAGS_TTCN3=""


--
To view, visit https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/15196
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: I9bfba3ab2a3830e590b203c44c03b9c9383fff99
Gerrit-Change-Number: 15196
Gerrit-PatchSet: 3
Gerrit-Owner: laforge <lafo...@gnumonks.org>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <lafo...@gnumonks.org>
Gerrit-Reviewer: pespin <pes...@sysmocom.de>
Gerrit-MessageType: merged

Reply via email to