pespin has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/40366?usp=email )


Change subject: library: Introduce NGAP Emulation
......................................................................

library: Introduce NGAP Emulation

Only initial features are working, like sending NG Setup Request +
Response.

Change-Id: I5aea8be12c54cf907e71bffe6456efb5e60eb203
---
A library/NGAP_CodecPort.ttcn
A library/NGAP_CodecPort_CtrlFunct.ttcn
A library/NGAP_CodecPort_CtrlFunctDef.cc
A library/NGAP_Emulation.ttcn
A library/NGAP_Functions.ttcn
5 files changed, 890 insertions(+), 0 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-ttcn3-hacks 
refs/changes/66/40366/1

diff --git a/library/NGAP_CodecPort.ttcn b/library/NGAP_CodecPort.ttcn
new file mode 100644
index 0000000..95868ac
--- /dev/null
+++ b/library/NGAP_CodecPort.ttcn
@@ -0,0 +1,83 @@
+module NGAP_CodecPort {
+
+/* Simple NGAP Codec Port, translating between raw SCTP primitives with
+ * octetstring payload towards the IPL4asp provider, and NGAP primitives
+ * which carry the decoded NGAP data types as payload.
+ *
+ * (C) 2019 by Harald Welte <lafo...@gnumonks.org>
+ * (C) 2025 by sysmocom - s.f.m.c. GmbH <i...@sysmocom.de>
+ * 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 NGAP_PDU_Descriptions all;
+       import from NGAP_Types all;
+
+       type record NGAP_RecvFrom {
+               ConnectionId    connId,
+               HostName        remName,
+               PortNumber      remPort,
+               HostName        locName,
+               PortNumber      locPort,
+               NGAP_PDU        msg
+       };
+
+       template NGAP_RecvFrom t_NGAP_RecvFrom(template NGAP_PDU msg) := {
+               connId := ?,
+               remName := ?,
+               remPort := ?,
+               locName := ?,
+               locPort := ?,
+               msg := msg
+       }
+
+       type record NGAP_Send {
+               ConnectionId    connId,
+               NGAP_PDU        msg
+       }
+
+       template NGAP_Send t_NGAP_Send(template ConnectionId connId, template 
NGAP_PDU msg) := {
+               connId := connId,
+               msg := msg
+       }
+
+       private function IPL4_to_NGAP_RecvFrom(in ASP_RecvFrom pin, out 
NGAP_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_NGAP_PDU(pin.msg);
+       } with { extension "prototype(fast)" };
+
+       private function NGAP_to_IPL4_Send(in NGAP_Send pin, out ASP_Send pout) 
{
+               pout.connId := pin.connId;
+               pout.proto := {
+                       sctp := {
+                               sinfo_stream := omit,
+                               sinfo_ppid := 60,
+                               remSocks := omit,
+                               assocId := omit
+                       }
+               };
+               pout.msg := enc_NGAP_PDU(pin.msg);
+       } with { extension "prototype(fast)" };
+
+       type port NGAP_CODEC_PT message {
+               out     NGAP_Send;
+               in      NGAP_RecvFrom,
+                       ASP_ConnId_ReadyToRelease,
+                       ASP_Event;
+       } with { extension "user IPL4asp_PT
+               out(NGAP_Send -> ASP_Send:function(NGAP_to_IPL4_Send))
+               in(ASP_RecvFrom -> NGAP_RecvFrom: 
function(IPL4_to_NGAP_RecvFrom);
+                  ASP_ConnId_ReadyToRelease -> ASP_ConnId_ReadyToRelease: 
simple;
+                  ASP_Event -> ASP_Event: simple)"
+       }
+}
diff --git a/library/NGAP_CodecPort_CtrlFunct.ttcn 
b/library/NGAP_CodecPort_CtrlFunct.ttcn
new file mode 100644
index 0000000..d2392d1
--- /dev/null
+++ b/library/NGAP_CodecPort_CtrlFunct.ttcn
@@ -0,0 +1,44 @@
+module NGAP_CodecPort_CtrlFunct {
+
+  import from NGAP_CodecPort all;
+  import from IPL4asp_Types all;
+
+  external function f_IPL4_listen(
+    inout NGAP_CODEC_PT portRef,
+    in HostName locName,
+    in PortNumber locPort,
+    in ProtoTuple proto,
+    in OptionList options := {}
+  ) return Result;
+
+  external function f_IPL4_connect(
+    inout NGAP_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 NGAP_CODEC_PT portRef,
+    in ConnectionId id,
+    in ProtoTuple proto := { unspecified := {} }
+  ) return Result;
+
+  external function f_IPL4_setUserData(
+    inout NGAP_CODEC_PT portRef,
+    in ConnectionId id,
+    in UserData userData
+  ) return Result;
+
+  external function f_IPL4_getUserData(
+    inout NGAP_CODEC_PT portRef,
+    in ConnectionId id,
+    out UserData userData
+  ) return Result;
+
+}
+
diff --git a/library/NGAP_CodecPort_CtrlFunctDef.cc 
b/library/NGAP_CodecPort_CtrlFunctDef.cc
new file mode 100644
index 0000000..480c002
--- /dev/null
+++ b/library/NGAP_CodecPort_CtrlFunctDef.cc
@@ -0,0 +1,56 @@
+#include "IPL4asp_PortType.hh"
+#include "NGAP_CodecPort.hh"
+#include "IPL4asp_PT.hh"
+
+namespace NGAP__CodecPort__CtrlFunct {
+
+  IPL4asp__Types::Result f__IPL4__listen(
+    NGAP__CodecPort::NGAP__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(
+    NGAP__CodecPort::NGAP__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(
+    NGAP__CodecPort::NGAP__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(
+    NGAP__CodecPort::NGAP__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(
+    NGAP__CodecPort::NGAP__CODEC__PT& portRef,
+    const IPL4asp__Types::ConnectionId& connId,
+    IPL4asp__Types::UserData& userData)
+  {
+    return f__IPL4__PROVIDER__getUserData(portRef, connId, userData);
+  }
+
+}
+
diff --git a/library/NGAP_Emulation.ttcn b/library/NGAP_Emulation.ttcn
new file mode 100644
index 0000000..d599b4a
--- /dev/null
+++ b/library/NGAP_Emulation.ttcn
@@ -0,0 +1,615 @@
+module NGAP_Emulation {
+
+/* NGAP Emulation, runs on top of NGAP_CodecPort. It multiplexes/demultiplexes
+ * the individual subscribers by their UE association (AMF_UE_NGAP_ID/
+ * RAN_UE_NGAP_ID identifiers), so there can be separate TTCN-3 components
+ * handling each of them.
+ *
+ * The NGAP_Emulation.main() function processes NGAP primitives from the NGAP
+ * socket via the NGAP_CodecPort, and dispatches them to the per-subscriber
+ * components.
+ *
+ * For each new subscriber, the NGapOps.create_cb() is called. It can create
+ * or resolve a TTCN-3 component, and returns a component reference to which
+ * that subscriber traffic is routed/dispatched.
+ *
+ * If a pre-existing component wants to register to handle a future inbound UE
+ * association, it can do so by registering an "expect" with the expected
+ * AMF_UE_NGAP_ID/RAN_UE_NGAP_ID identifiers. It is also possible to register
+ * an expect for a specific procedureCode, in case the expected message is non
+ * UE related (unit-data).
+ *
+ * Inbound non-UE related NGAP messages (such as RESET, SETUP, OVERLOAD) are
+ * dispatched to the NGapOps.unitdata_cb() callback, which is registered with
+ * an argument to the main() function below.
+ *
+ * (C) 2019 by Harald Welte <lafo...@gnumonks.org>
+ * (C) 2025 by sysmocom - s.f.m.c. GmbH <i...@sysmocom.de>
+ * 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 NGAP_CodecPort all;
+import from NGAP_CodecPort_CtrlFunct all;
+import from NGAP_Types all;
+import from NGAP_Constants all;
+import from NGAP_PDU_Contents all;
+import from NGAP_PDU_Descriptions all;
+import from NGAP_IEs all;
+import from NGAP_Templates all;
+import from NGAP_Functions all;
+import from SCTP_Templates all;
+
+import from General_Types all;
+import from Osmocom_Types all;
+import from IPL4asp_Types all;
+import from DNS_Helpers all;
+
+
+type component NGAP_ConnHdlr {
+       port NGAP_Conn_PT NGAP;
+       /* procedure based port to register for incoming connections */
+       port NGAPEM_PROC_PT NGAP_PROC;
+}
+
+/* port between individual per-connection components and this dispatcher */
+type port NGAP_Conn_PT message {
+       inout NGAP_PDU;
+} with { extension "internal" };
+
+
+type enumerated NGAPEM_EventUpDown {
+       NGAPEM_EVENT_DOWN,
+       NGAPEM_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 NGAPEM_Event {
+       NGAPEM_EventUpDown      up_down
+}
+
+/* global test port e.g. for non-imsi/conn specific messages */
+type port NGAP_PT message {
+       inout NGAP_PDU, NGAPEM_Event;
+} with { extension "internal" };
+
+/* "Expect" Handling */
+
+type record ExpectData {
+       AMF_UE_NGAP_ID amf_id optional,
+       RAN_UE_NGAP_ID ran_id optional,
+       NGAP_ConnHdlr vc_conn
+}
+
+/* represents a single NGAP PDU that we expect. When a matching PDU is seen, 
it is forwarded to the registered
+ * component */
+type record ExpectDataProc {
+       integer procedureCode optional,
+       NGAP_ConnHdlr vc_conn                           /* component handling 
this UE connection */
+};
+
+signature NGAPEM_register(in AMF_UE_NGAP_ID amf_id, in RAN_UE_NGAP_ID ran_id, 
in NGAP_ConnHdlr hdlr);
+signature NGAPEM_register_proc(in integer procedureCode, in NGAP_ConnHdlr 
hdlr);
+//signature NGAPEM_derive_nas_token(in octetstring kasme, in NGAP_ConnHdlr 
hdlr, out OCT32 nas_token);
+
+type port NGAPEM_PROC_PT procedure {
+       inout NGAPEM_register;
+       inout NGAPEM_register_proc;
+       //inout NGAPEM_derive_nas_token;
+} with { extension "internal" };
+
+/* Function that can be used as create_cb and will use the expect table */
+function ExpectedCreateCallback(NGAP_PDU msg,
+                               template (omit) AMF_UE_NGAP_ID amf_id,
+                               template (omit) RAN_UE_NGAP_ID ran_id,
+                               charstring id)
+runs on NGAP_Emulation_CT return NGAP_ConnHdlr {
+       var NGAP_ConnHdlr ret := null;
+       var integer i;
+
+       for (i := 0; i < sizeof(NGapExpectTable); i := i+1) {
+               if (not ispresent(NGapExpectTable[i].amf_id) and not 
ispresent(NGapExpectTable[i].ran_id)) {
+                       continue;
+               }
+
+               if (valueof(amf_id) == NGapExpectTable[i].amf_id and 
valueof(ran_id) == NGapExpectTable[i].ran_id) {
+                       ret := NGapExpectTable[i].vc_conn;
+                       /* Release this entry */
+                       NGapExpectTable[i].amf_id := omit;
+                       NGapExpectTable[i].ran_id := omit;
+                       NGapExpectTable[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;
+}
+
+/* represents a single NGAP Association */
+type record AssociationData {
+       NGAP_ConnHdlr   comp_ref,                       /* component handling 
this UE connection */
+       uint32_t        ran_ue_ngap_id optional,        /* eNB side NGAP ID */
+       uint40_t        amf_ue_ngap_id optional//,      /* MME side NGAP ID */
+       //EUTRAN_CGI    cgi optional,
+       //TAI           tai optional,
+       //NAS_UE_State  nus
+};
+
+type component NGAP_Emulation_CT {
+       /* Port facing to the UDP SUT */
+       port NGAP_CODEC_PT NGAP;
+       /* All NGAP_ConnHdlr NGAP ports connect here
+        * NGAP_Emulation_CT.main needs to figure out what messages
+        * to send where with CLIENT.send() to vc_conn */
+       port NGAP_Conn_PT NGAP_CLIENT;
+       /* currently tracked connections */
+       var AssociationData NGapAssociationTable[16];
+       /* pending expected NGAP Association (UE oriented) */
+       var ExpectData NGapExpectTable[8];
+       /* pending expected NGAP PDU */
+       var ExpectDataProc NGapExpectTableProc[8];
+       /* procedure based port to register for incoming connections */
+       port NGAPEM_PROC_PT NGAP_PROC;
+       /* test port for unit data messages */
+       port NGAP_PT NGAP_UNIT;
+
+       var NGAP_conn_parameters g_pars;
+       var charstring g_ngap_id;
+       var integer g_ngap_conn_id := -1;
+}
+
+type function NGAPCreateCallback(NGAP_PDU msg,
+                               template (omit) AMF_UE_NGAP_ID amf_id,
+                               template (omit) RAN_UE_NGAP_ID ran_id,
+                               charstring id)
+runs on NGAP_Emulation_CT return NGAP_ConnHdlr;
+
+type function NGAPUnitdataCallback(NGAP_PDU msg)
+runs on NGAP_Emulation_CT return template NGAP_PDU;
+
+type record NGAPOps {
+       NGAPCreateCallback create_cb,
+       NGAPUnitdataCallback unitdata_cb
+}
+
+type record NGAP_conn_parameters {
+       HostName remote_ip,
+       IPL4asp_Types.PortNumber remote_sctp_port,
+       HostName local_ip,
+       IPL4asp_Types.PortNumber local_sctp_port//,
+       //NAS_Role role
+}
+
+function tr_NGAP_RecvFrom_R(template NGAP_PDU msg)
+runs on NGAP_Emulation_CT return template NGAP_Recv>From {
+       var template NGAP_RecvFrom mrf := {
+               connId := g_ngap_conn_id,
+               remName := ?,
+               remPort := ?,
+               locName := ?,
+               locPort := ?,
+               msg := msg
+       }
+       return mrf;
+}
+
+private function f_ngap_ids_known(template (omit) AMF_UE_NGAP_ID amf_id,
+                                 template (omit) RAN_UE_NGAP_ID ran_id)
+runs on NGAP_Emulation_CT return boolean {
+       var integer i;
+       log("f_ngap_ids_known(",amf_id,", ",ran_id,")");
+       for (i := 0; i < sizeof(NGapAssociationTable); i := i+1) {
+               log("tbl[",i,"]: mme=", NGapAssociationTable[i].amf_ue_ngap_id,
+                   ", enb=", NGapAssociationTable[i].ran_ue_ngap_id);
+               /* skip empty records */
+               if (NGapAssociationTable[i].amf_ue_ngap_id == omit and
+                   NGapAssociationTable[i].ran_ue_ngap_id == omit) {
+                       log("skipping empty ", i);
+                       continue;
+               }
+               if (NGapAssociationTable[i].amf_ue_ngap_id == omit) {
+                       log("entry ", i, " has no MME ID yet (enb=", 
NGapAssociationTable[i].ran_ue_ngap_id);
+                       /* Table doesn't yet know the MME side ID, let's 
look-up only
+                        * based on the eNB side ID */
+                       if (match(NGapAssociationTable[i].ran_ue_ngap_id, 
ran_id)) {
+                               /* update table with MME side ID */
+                               NGapAssociationTable[i].amf_ue_ngap_id := 
valueof(amf_id);
+                               return true;
+                       }
+               } else if (match(NGapAssociationTable[i].ran_ue_ngap_id, 
ran_id) and
+                       match(NGapAssociationTable[i].amf_ue_ngap_id, amf_id)) {
+                       return true;
+               }
+       }
+       return false;
+}
+private function f_comp_known(NGAP_ConnHdlr client)
+runs on NGAP_Emulation_CT return boolean {
+       var integer i;
+       for (i := 0; i < sizeof(NGapAssociationTable); i := i+1) {
+               if (NGapAssociationTable[i].comp_ref == client) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+private function f_assoc_id_by_ngap_ids(template (omit) AMF_UE_NGAP_ID amf_id,
+                                   template (omit) RAN_UE_NGAP_ID ran_id)
+runs on NGAP_Emulation_CT return integer {
+       var integer i;
+       for (i := 0; i < sizeof(NGapAssociationTable); i := i+1) {
+               if (istemplatekind(ran_id, "omit") or
+                   match(NGapAssociationTable[i].ran_ue_ngap_id, ran_id)) {
+                       if (istemplatekind(amf_id, "omit")) {
+                               return i;
+                       } else {
+                               if 
(match(NGapAssociationTable[i].amf_ue_ngap_id, amf_id)) {
+                                       return i;
+                               }
+                       }
+               }
+       }
+       setverdict(fail, "NGAP Association Table not found by ENB-ID=", ran_id, 
" MME-ID=", amf_id);
+       mtc.stop;
+}
+
+private function f_assoc_id_by_comp(NGAP_ConnHdlr client)
+runs on NGAP_Emulation_CT return integer {
+       var integer i;
+       for (i := 0; i < sizeof(NGapAssociationTable); i := i+1) {
+               if (NGapAssociationTable[i].comp_ref == client) {
+                       return i;
+               }
+       }
+       setverdict(fail, "NGAP Association Table not found by component ", 
client);
+       mtc.stop;
+}
+
+private function f_assoc_by_comp(NGAP_ConnHdlr client)
+runs on NGAP_Emulation_CT return AssociationData {
+       var integer i := f_assoc_id_by_comp(client);
+       return NGapAssociationTable[i];
+}
+
+private function f_ngap_id_table_add(NGAP_ConnHdlr comp_ref,
+                                    template (omit) AMF_UE_NGAP_ID amf_id, 
RAN_UE_NGAP_ID ran_id)
+runs on NGAP_Emulation_CT return integer {
+       var integer i;
+       for (i := 0; i < sizeof(NGapAssociationTable); i := i+1) {
+               if (not isvalue(NGapAssociationTable[i].ran_ue_ngap_id)) {
+                       NGapAssociationTable[i].ran_ue_ngap_id := ran_id;
+                       if (istemplatekind(amf_id, "omit")) {
+                               NGapAssociationTable[i].amf_ue_ngap_id := omit;
+                       } else {
+                               NGapAssociationTable[i].amf_ue_ngap_id := 
valueof(amf_id);
+                       }
+                       NGapAssociationTable[i].comp_ref := comp_ref;
+                       return i;
+               }
+       }
+       testcase.stop("NGAP Association Table full!");
+       return -1;
+}
+
+private function f_ngap_id_table_del(NGAP_ConnHdlr comp_ref, RAN_UE_NGAP_ID 
ran_id)
+runs on NGAP_Emulation_CT {
+       var integer i;
+       for (i := 0; i < sizeof(NGapAssociationTable); i := i+1) {
+               if (NGapAssociationTable[i].comp_ref == comp_ref and
+                   NGapAssociationTable[i].ran_ue_ngap_id == ran_id) {
+                       NGapAssociationTable[i].ran_ue_ngap_id := omit;
+                       NGapAssociationTable[i].amf_ue_ngap_id := omit;
+                       NGapAssociationTable[i].comp_ref := null;
+                       return;
+               }
+       }
+       setverdict(fail, "NGAP Association Table: Couldn't find to-be-deleted 
entry!");
+       mtc.stop;
+}
+
+
+private function f_ngap_id_table_init()
+runs on NGAP_Emulation_CT {
+       for (var integer i := 0; i < sizeof(NGapAssociationTable); i := i+1) {
+               NGapAssociationTable[i].amf_ue_ngap_id := omit;
+               NGapAssociationTable[i].ran_ue_ngap_id := omit;
+               //NGapAssociationTable[i].cgi := omit;
+               //NGapAssociationTable[i].tai := omit;
+               //NGapAssociationTable[i].nus := 
valueof(t_NAS_UE_State(g_pars.role));
+               NGapAssociationTable[i].comp_ref := null;
+       }
+}
+
+private function f_ngap_xceive(template (value) NGAP_PDU tx,
+                               template NGAP_PDU rx_t := ?)
+runs on NGAP_Emulation_CT  return NGAP_PDU {
+       timer T := 10.0;
+       var NGAP_RecvFrom mrf;
+
+       NGAP.send(t_NGAP_Send(g_ngap_conn_id, tx));
+       alt {
+       [] NGAP.receive(tr_NGAP_RecvFrom_R(rx_t)) -> value mrf { }
+       [] NGAP.receive(tr_SctpAssocChange) { repeat; }
+       [] NGAP.receive(tr_SctpPeerAddrChange)  { repeat; }
+       [] T.timeout {
+               setverdict(fail, "Timeout waiting for ", rx_t);
+               mtc.stop;
+               }
+       }
+       return mrf.msg;
+}
+
+//function handle_NGAP_UeContextReleaseCmd(template (present) NGAP_PDU 
rel_cmd) runs on NGAP_Emulation_CT {
+//     if 
(ispresent(rel_cmd.initiatingMessage.value_.uEContextReleaseCommand.protocolIEs[0].value_.uE_NGAP_IDs.uE_NGAP_ID_pair))
 {
+//             var template AMF_UE_NGAP_ID mme_ue_id;
+//             var template RAN_UE_NGAP_ID enb_ue_id;
+//             var integer assoc_id;
+//             var NGAP_ConnHdlr vc_conn
+//
+//             mme_ue_id := 
rel_cmd.initiatingMessage.value_.uEContextReleaseCommand.protocolIEs[0].value_.uE_NGAP_IDs.uE_NGAP_ID_pair.mME_UE_NGAP_ID;
+//             enb_ue_id := 
rel_cmd.initiatingMessage.value_.uEContextReleaseCommand.protocolIEs[0].value_.uE_NGAP_IDs.uE_NGAP_ID_pair.eNB_UE_NGAP_ID;
+//
+//             assoc_id := f_assoc_id_by_ngap_ids(mme_ue_id, enb_ue_id);
+//             vc_conn := NGapAssociationTable[assoc_id].comp_ref;
+//
+//             f_ngap_id_table_del(vc_conn, valueof(enb_ue_id));
+//     } else {
+//             /* TODO: The UE CONTEXT RELEASE COMMAND (see also: 3GPP TS 
36.413, section 9.1.4.6), may identify the
+//              * context by either an uE_NGAP_ID_pair (AMF_UE_NGAP_ID and 
RAN_UE_NGAP_ID) or an AMF_UE_NGAP_ID alone.
+//              * The latter case is not implemented here yet. */
+//             setverdict(fail, "complete implementation of 
UeContextReleaseCmd handling");
+//             mtc.stop;
+//     }
+//}
+
+private function SendToNGapExpectTableProc(NGAP_PDU msg) runs on 
NGAP_Emulation_CT {
+       var integer procedureCode;
+       var NGAP_ConnHdlr vc_conn;
+
+       if (ispresent(msg.initiatingMessage.procedureCode)) {
+               procedureCode := msg.initiatingMessage.procedureCode;
+       } else if (ispresent(msg.unsuccessfulOutcome.procedureCode)) {
+               procedureCode := msg.unsuccessfulOutcome.procedureCode;
+       } else if (ispresent(msg.successfulOutcome.procedureCode)) {
+               procedureCode := msg.successfulOutcome.procedureCode;
+       } else {
+               return;
+       }
+
+       for (var integer i := 0; i < sizeof(NGapExpectTableProc); i := i+1) {
+               if (NGapExpectTableProc[i].procedureCode == procedureCode) {
+                       vc_conn := NGapExpectTableProc[i].vc_conn;
+                       if (vc_conn != null) {
+                               NGAP_CLIENT.send(msg) to vc_conn;
+                       }
+               }
+       }
+
+       return;
+}
+
+function main(NGAPOps ops, NGAP_conn_parameters p, charstring id) runs on 
NGAP_Emulation_CT {
+       var Result res;
+       g_pars := p;
+       g_ngap_id := id;
+       f_ngap_id_table_init();
+       f_expect_table_init();
+
+       map(self:NGAP, system:NGAP_CODEC_PT);
+       if (p.remote_sctp_port == -1) {
+               res := NGAP_CodecPort_CtrlFunct.f_IPL4_listen(NGAP, p.local_ip, 
p.local_sctp_port,
+                                                             { sctp := 
valueof(ts_SctpTuple(18)) });
+       } else {
+               res := NGAP_CodecPort_CtrlFunct.f_IPL4_connect(NGAP, 
p.remote_ip, p.remote_sctp_port,
+                                                              p.local_ip, 
p.local_sctp_port, -1,
+                                                              { sctp := 
valueof(ts_SctpTuple(18)) });
+       }
+       if (not ispresent(res.connId)) {
+               setverdict(fail, "Could not connect NGAP socket, check your 
configuration");
+               mtc.stop;
+       }
+       g_ngap_conn_id := res.connId;
+
+       /* notify user about SCTP establishment */
+       if (p.remote_sctp_port != -1) {
+               NGAP_UNIT.send(NGAPEM_Event:{up_down:=NGAPEM_EVENT_UP})
+       }
+
+       while (true) {
+               var NGAP_ConnHdlr vc_conn;
+               //var PDU_NAS_EPS nas;
+               var AMF_UE_NGAP_ID amf_id;
+               var RAN_UE_NGAP_ID ran_id;
+               var integer procedureCode;
+               var NGAP_RecvFrom mrf;
+               var NGAP_PDU msg;
+               var charstring vlr_name, mme_name;
+               var integer ai;
+               var octetstring kasme;
+
+               alt {
+               /* NGAP from client: InitialUE */
+               [] NGAP_CLIENT.receive(mw_ngap_initMsg(mw_n2_initialUeMessage)) 
-> value msg sender vc_conn {
+                       /* create a table entry about this connection */
+                       ai := f_ngap_id_table_add(vc_conn, omit, 
valueof(f_NGAP_get_RAN_UE_NGAP_ID(msg)));
+                       /* Store CGI + TAI so we can use it for generating 
UlNasTransport from NAS */
+//                     NGapAssociationTable[ai].tai := 
msg.initiatingMessage.value_.InitialUEMessage.protocolIEs[2].value_.TAI;
+//                     NGapAssociationTable[ai].cgi := 
msg.initiatingMessage.value_.InitialUEMessage.protocolIEs[3].value_.EUTRAN_CGI;
+//                     /* Pass message through */
+                       NGAP.send(t_NGAP_Send(g_ngap_conn_id, msg));
+                       }
+               /* NGAP from client: pass on transparently */
+               [] NGAP_CLIENT.receive(NGAP_PDU:?) -> value msg sender vc_conn {
+                       /* Pass message through */
+                       /* FIXME: validate NGAP_IDs ? */
+                       NGAP.send(t_NGAP_Send(g_ngap_conn_id, msg));
+                       }
+
+               /* non-UE related NGAP: pass through unmodified/unverified */
+               [] NGAP_UNIT.receive(NGAP_PDU:?) -> value msg sender vc_conn {
+                       /* Pass message through */
+                       NGAP.send(t_NGAP_Send(g_ngap_conn_id, msg));
+                       }
+
+               /* NGAP received from peer (MME) */
+               [] NGAP.receive(tr_NGAP_RecvFrom_R(?)) -> value mrf {
+//                     if (match(mrf.msg, tr_NGAP_nonUErelated)) {
+                               /* non-UE-related NGAP message */
+                               SendToNGapExpectTableProc(mrf.msg);
+                               var template NGAP_PDU resp := 
ops.unitdata_cb.apply(mrf.msg);
+                               if (isvalue(resp)) {
+                                       NGAP.send(t_NGAP_Send(g_ngap_conn_id, 
valueof(resp)));
+                               }
+//                     } else {
+//                             /* Ue-related NGAP message */
+//                             /* obtain MME + ENB UE NGAP ID */
+//                             var template (omit) AMF_UE_NGAP_ID mme_ue_id := 
f_NGAP_get_AMF_UE_NGAP_ID(mrf.msg);
+//                             var template (omit) RAN_UE_NGAP_ID enb_ue_id := 
f_NGAP_get_RAN_UE_NGAP_ID(mrf.msg);
+//                             /* check if those IDs are known in our table */
+//                             if (f_ngap_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_ngap_ids(mme_ue_id, enb_ue_id);
+//                                     vc_conn := 
NGapAssociationTable[assoc_id].comp_ref;
+//                                     nas_enc := f_NGAP_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(NGapAssociationTable[assoc_id].nus, nas);
+//                                             }
+//                                             /* DL/UlNasTransport are not 
interesting, don't send them */
+//                                             if (not match(mrf.msg, 
(tr_NGAP_DlNasTransport, tr_NGAP_UlNasTransport))) {
+//                                                     /* send raw NGAP */
+//                                                     
NGAP_CLIENT.send(mrf.msg) to vc_conn;
+//                                             }
+//                                             /* send decoded NAS */
+//                                             NGAP_CLIENT.send(nas) to 
vc_conn;
+//                                     } else {
+//                                             /* send raw NGAP */
+//                                             NGAP_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_ngap_id_table_add(vc_conn, mme_ue_id, 
valueof(enb_ue_id));
+//                                     NGAP_CLIENT.send(mrf.msg) to vc_conn;
+//                             }
+//                             if (match(mrf.msg, 
tr_NGAP_UeContextReleaseCmd)) {
+//                                     
handle_NGAP_UeContextReleaseCmd(mrf.msg);
+//                             }
+//                     }
+                       }
+               [] NGAP.receive(tr_SctpAssocChange) { }
+               [] NGAP.receive(tr_SctpPeerAddrChange)  { }
+               [] NGAP_PROC.getcall(NGAPEM_register:{?,?,?}) -> param(amf_id, 
ran_id, vc_conn) {
+                       f_create_expect(amf_id, ran_id, vc_conn);
+                       NGAP_PROC.reply(NGAPEM_register:{amf_id, ran_id, 
vc_conn}) to vc_conn;
+                       }
+               [] NGAP_PROC.getcall(NGAPEM_register_proc:{?,?}) -> 
param(procedureCode, vc_conn) {
+                       f_create_expect_proc(procedureCode, vc_conn);
+                       NGAP_PROC.reply(NGAPEM_register_proc:{procedureCode, 
vc_conn}) to vc_conn;
+                       }
+//             [] NGAP_PROC.getcall(NGAPEM_derive_nas_token:{?, ?, -}) -> 
param(kasme, vc_conn) {
+//                     var integer assoc_id := f_assoc_id_by_comp(vc_conn);
+//                     var OCT32 nas_token := f_kdf_nas_token(kasme, 
NGapAssociationTable[assoc_id].nus.tx_count)
+//                     NGapAssociationTable[assoc_id].nus.tx_count := 
NGapAssociationTable[assoc_id].nus.tx_count + 1;
+//                     NGAP_PROC.reply(NGAPEM_derive_nas_token:{kasme, 
vc_conn, nas_token}) to vc_conn;
+//                     }
+               }
+       }
+}
+
+
+private function f_create_expect(template (omit) AMF_UE_NGAP_ID amf_id,
+                                template (omit) RAN_UE_NGAP_ID ran_id,
+                                NGAP_ConnHdlr hdlr)
+runs on NGAP_Emulation_CT {
+       var integer i;
+
+       /* Check an entry like this is not already presnt */
+       for (i := 0; i < sizeof(NGapExpectTable); i := i+1) {
+               if (not ispresent(NGapExpectTable[i].amf_id) and not 
ispresent(NGapExpectTable[i].ran_id)) {
+                       continue;
+               }
+               if (valueof(amf_id) == NGapExpectTable[i].amf_id and 
valueof(ran_id) == NGapExpectTable[i].ran_id) {
+                       setverdict(fail, "UE AMF id / UE RAN id pair already 
present", amf_id, ran_id);
+                       mtc.stop;
+               }
+       }
+       for (i := 0; i < sizeof(NGapExpectTable); i := i+1) {
+               if (not ispresent(NGapExpectTable[i].amf_id) and not 
ispresent(NGapExpectTable[i].ran_id)) {
+                       NGapExpectTable[i].amf_id := valueof(amf_id);
+                       NGapExpectTable[i].ran_id := valueof(ran_id);
+                       NGapExpectTable[i].vc_conn := hdlr;
+                       log("Created Expect[", i, "] for UE AMF id:", amf_id, 
", UE RAN id:", ran_id, " to be handled at ", hdlr);
+                       return;
+               }
+       }
+       testcase.stop("No space left in NGapExpectTable")
+}
+
+/* client/conn_hdlr side function to use procedure port to create expect in 
emulation */
+function f_create_s1ap_expect(template (omit) AMF_UE_NGAP_ID amf_id,
+                             template (omit) RAN_UE_NGAP_ID ran_id) runs on 
NGAP_ConnHdlr {
+       NGAP_PROC.call(NGAPEM_register:{amf_id, ran_id, self}) {
+               [] NGAP_PROC.getreply(NGAPEM_register:{?,?,?}) {};
+       }
+}
+
+private function f_create_expect_proc(integer procedureCode,NGAP_ConnHdlr 
hdlr) runs on NGAP_Emulation_CT {
+       var integer i;
+
+       /* Check an entry like this is not already presnt */
+       for (i := 0; i < sizeof(NGapExpectTableProc); i := i+1) {
+               if (NGapExpectTableProc[i].vc_conn == null) {
+                       continue;
+               }
+               if (NGapExpectTableProc[i].procedureCode == procedureCode) {
+                       setverdict(fail, "procedureCode ", procedureCode, " 
already present");
+                       mtc.stop;
+               }
+       }
+       for (i := 0; i < sizeof(NGapExpectTableProc); i := i+1) {
+               if (NGapExpectTableProc[i].vc_conn == null) {
+                       NGapExpectTableProc[i].procedureCode := procedureCode;
+                       NGapExpectTableProc[i].vc_conn := hdlr;
+                       log("Created Expect[", i, "] for PDU:", procedureCode, 
" to be handled at ", hdlr);
+                       return;
+               }
+       }
+       testcase.stop("No space left in NGapExpectTableProc")
+}
+
+/* client/conn_hdlr side function to use procedure port to create expect (PDU) 
in emulation */
+function f_create_ngap_expect_proc(integer procedureCode, NGAP_ConnHdlr hdlr) 
runs on NGAP_ConnHdlr
+{
+       NGAP_PROC.call(NGAPEM_register_proc:{procedureCode, self}) {
+               [] NGAP_PROC.getreply(NGAPEM_register_proc:{?,?}) {};
+       }
+
+       log(procedureCode);
+}
+
+private function f_expect_table_init()
+runs on NGAP_Emulation_CT {
+       var integer i;
+       for (i := 0; i < sizeof(NGapExpectTable); i := i + 1) {
+               NGapExpectTable[i].amf_id := omit;
+               NGapExpectTable[i].ran_id := omit;
+               NGapExpectTable[i].vc_conn := null;
+       }
+
+       for (i := 0; i < sizeof(NGapExpectTableProc); i := i + 1) {
+               NGapExpectTableProc[i].procedureCode := omit;
+               NGapExpectTableProc[i].vc_conn := null;
+       }
+}
+
+}
diff --git a/library/NGAP_Functions.ttcn b/library/NGAP_Functions.ttcn
new file mode 100644
index 0000000..a35ccf3
--- /dev/null
+++ b/library/NGAP_Functions.ttcn
@@ -0,0 +1,92 @@
+module NGAP_Functions {
+
+/* (C) 2019 by Harald Welte <lafo...@gnumonks.org>
+ * (C) 2025 by sysmocom - s.f.m.c. GmbH <i...@sysmocom.de>
+ * 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 NGAP_IEs all;
+import from NGAP_Types all;
+import from NGAP_Constants all;
+import from NGAP_PDU_Contents all;
+import from NGAP_PDU_Descriptions all;
+import from NGAP_Templates all;
+
+function f_NGAP_get_AMF_UE_NGAP_ID(NGAP_PDU ngap) return template (omit) 
AMF_UE_NGAP_ID
+{
+       if (ischosen(ngap.initiatingMessage)) {
+               var InitiatingMessage im := ngap.initiatingMessage;
+               select (ngap) {
+               case (?) {
+                       return omit;
+                       /* TODO */
+                       /* return 
im.value_.InitialUEMessage.protocolIEs[0].value_.AMF_UE_NGAP_ID; */
+                       }
+               /* TODO */
+               }
+       } else if (ischosen(ngap.successfulOutcome)) {
+               var SuccessfulOutcome so := ngap.successfulOutcome;
+               select (ngap) {
+               case (?) {
+                       return omit;
+                       /* TODO */
+                       /* return 
im.value_.SuccessfulOutcome.protocolIEs[0].value_.AMF_UE_NGAP_ID; */
+                       }
+               /* TODO */
+               }
+       } else if (ischosen(ngap.unsuccessfulOutcome)) {
+               var UnsuccessfulOutcome uo := ngap.unsuccessfulOutcome;
+               select (ngap) {
+               case (?) {
+                       return omit;
+                       /* TODO */
+                       /* return 
im.value_.UnsuccessfulOutcome.protocolIEs[0].value_.AMF_UE_NGAP_ID; */
+                       }
+               /* TODO */
+               }
+       }
+       return omit;
+}
+
+function f_NGAP_get_RAN_UE_NGAP_ID(NGAP_PDU ngap) return template (omit) 
RAN_UE_NGAP_ID
+{
+       if (ischosen(ngap.initiatingMessage)) {
+               var InitiatingMessage im := ngap.initiatingMessage;
+               select (ngap) {
+               case (?) {
+                       return omit;
+                       /* TODO */
+                       /* return 
im.value_.InitialUEMessage.protocolIEs[0].value_.RAN_UE_NGAP_ID; */
+                       }
+               /* TODO */
+               }
+       } else if (ischosen(ngap.successfulOutcome)) {
+               var SuccessfulOutcome so := ngap.successfulOutcome;
+               select (ngap) {
+               case (?) {
+                       return omit;
+                       /* TODO */
+                       /* return 
im.value_.SuccessfulOutcome.protocolIEs[0].value_.RAN_UE_NGAP_ID; */
+                       }
+               /* TODO */
+               }
+       } else if (ischosen(ngap.unsuccessfulOutcome)) {
+               var UnsuccessfulOutcome uo := ngap.unsuccessfulOutcome;
+               select (ngap) {
+               case (?) {
+                       return omit;
+                       /* TODO */
+                       /* return 
im.value_.UnsuccessfulOutcome.protocolIEs[0].value_.RAN_UE_NGAP_ID; */
+                       }
+               /* TODO */
+               }
+       }
+       return omit;
+}
+
+}

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

Gerrit-MessageType: newchange
Gerrit-Project: osmo-ttcn3-hacks
Gerrit-Branch: master
Gerrit-Change-Id: I5aea8be12c54cf907e71bffe6456efb5e60eb203
Gerrit-Change-Number: 40366
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pes...@sysmocom.de>

Reply via email to