-
-
-
-
-
-
-
-
-
-
Here is the output of your OpenAirEmulation :
-
- oaisim.log
-
-
- '.$PROFILING_OUTPUT.'
-
- '.$PCAP_OUTPUT.'
-
-
-
-
-
-';
-
-
-/*
-// THE SECOND SQUARE - DISPLAY FILE RESULTS
- if ($handle = opendir("$RESULTS_PATH_EMULATION_LOGS")) {
- while (false !== ($file = readdir($handle)))
- {
- if ($file != "." && $file != "..")
- {
- $thelist .= '
'.$file.'';
- }
- }
- closedir($handle);
- }
-
-echo '
';
-
-// THE THIRD SQUARE - DISPLAY FILE RESULTS
-echo '
-
HERE DISPLAY PACKET TRACES FILES
-
';
-
-*/
-
-
-
-/* PHIL - J'enlve la SUMO CONFIG
-
- '.nospace($_POST['sumocmd']).'
- '.nospace($_POST['sumoconffile']).'
- '.nospace($_POST['sumostart']).'
- '.nospace($_POST['sumoend']).'
- '.nospace($_POST['sumostep']).'
- '.nospace($_POST['sumohostip']).'
- '.nospace($_POST['sumohostport']).'
-
-
-
-
- '.$CLI_START_ENB.'
- '.$CLI_START_UE.'
-
-
-
'.nospace($_POST['eNB_antennas_rx']).'
-
'.nospace($_POST['eNB_antennas_tx']).'
-
-
'.nospace($_POST['UE_antennas_rx']).'
-
'.nospace($_POST['UE_antennas_tx']).'
-*/
-
-
-
-/*
-// UE MOBILITY TYPE
-//
-if ( $_POST['uemobility'] == 'fixed' ) {
-$UE_MOBILITY =
- '
'.nospace($_POST['uemobility']).'';
- }
-if ( $_POST['uemobility'] == 'random_waypoint' ) {
-$UE_MOBILITY =
- '
'.nospace($_POST['uemobility']).'';
- }
-if ( $_POST['uemobility'] == 'random_walk' ) {
-$UE_MOBILITY =
- '
'.nospace($_POST['uemobility']).'';
- }
-if ( $_POST['uemobility'] == 'grid_walk' ) {
-$UE_MOBILITY =
- '
'.nospace($_POST['uemobility']).'
-
- '.nospace($_POST['horizgrid']).'
- '.nospace($_POST['vertgrid']).'
-
-
'.nospace($_POST['gridtype']).'';
- }
-
-//
-// UEs DISTRIBUTION
-//
-if ( $_POST['uedistr'] == 'random' ) {
-$UE_DISTRIBUTION =
- '
- 1
- ';
- }
-
-if ( $_POST['uedistr'] == 'concentrated' ) {
-$UE_DISTRIBUTION =
- '
- 1
- ';
- }
-
-if ( $_POST['uedistr'] == 'grid_map' ) {
- if ( $_POST['gridmapradio'] == 'random_grid' ) {
-$UE_DISTRIBUTION =
- '
random_grid
-
- '.nospace($_POST['rdgridnbnodes']).'
- ';
- }
- if ( $_POST['gridmapradio'] == 'border_grid' ) {
-$UE_DISTRIBUTION =
- '
border_grid
-
- '.nospace($_POST['bdgridnbnodes']).'
- ';
- }
-}
-
-//
-// eNB INITIAL DISTRIBUTION
-//
-if ( $_POST['eNB_initial_distribution'] == 'grid' ) {
-$eNB_INITIAL_DISTRIBUTION =
- '
grid
-
- '.nospace($_POST['gridx']).'
- '.nospace($_POST['gridy']).'
- ';
- }
-if ( $_POST['eNB_initial_distribution'] == 'hexagonal' ) {
-$eNB_INITIAL_DISTRIBUTION =
- '
hexagonal
-
- '.nospace($_POST['hexanbcells']).'
- '.nospace($_POST['interenbdist']).'
- ';
- }
-if ( $_POST['eNB_initial_distribution'] == 'random' ) {
-$eNB_INITIAL_DISTRIBUTION =
- '
random
-
- '.nospace($_POST['nbenbs']).'
- ';
- }
-
-//
-// TRAFFIC - PACKET SIZE
-//
-if ( $_POST['pktsize'] == 'fixed' ) {
-$PACKET_SIZE =
- '
fixed
-
- '.nospace($_POST['pktsizefixed']).'
- ';
- }
-if ( $_POST['pktsize'] == 'uniform' ) {
-$PACKET_SIZE =
- '
uniform
-
- '.nospace($_POST['pktsizeunimin']).'
- '.nospace($_POST['pktsizeunimax']).'
- ';
- }
-
-//
-// INTER ARRIVAL TIME
-//
-if ( $_POST['interarrivtime'] == 'fixed' ) {
-$INTER_ARRIVAL_TIME =
- '
fixed
-
- '.nospace($_POST['intarrtimefixed']).'
- ';
- }
-if ( $_POST['interarrivtime'] == 'uniform' ) {
-$INTER_ARRIVAL_TIME =
- '
uniform
-
- '.nospace($_POST['intarrtimeunimin']).'
- '.nospace($_POST['intarrtimeunimax']).'
- ';
- }
-if ( $_POST['interarrivtime'] == 'poisson' ) {
-$INTER_ARRIVAL_TIME =
- '
poisson
-
- '.nospace($_POST['intarrtimepoisson']).'
- ';
- }
-
-//
-// RANDOM SEED
-//
-if ( $_POST['randomseed'] == 'random' ) {
-$RANDOM_SEED =
- '
random';
- }
-if ( $_POST['randomseed'] == 'userspecified' ) {
-$RANDOM_SEED =
- '
user_specified
-
- '.nospace($_POST['seedvalue']).'
- ';
- }
-*/
-?>
diff --git a/openair2/UTIL/OSD/toto.php b/openair2/UTIL/OSD/toto.php
deleted file mode 100644
index 870549377b..0000000000
--- a/openair2/UTIL/OSD/toto.php
+++ /dev/null
@@ -1 +0,0 @@
-toto1234
diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c
index 4cfb061cee..dda04f12bb 100644
--- a/openair3/NAS/NR_UE/nr_nas_msg.c
+++ b/openair3/NAS/NR_UE/nr_nas_msg.c
@@ -78,6 +78,12 @@
static nr_ue_nas_t nr_ue_nas[MAX_NUM_NR_UE_INST] = {0};
+nr_ue_nas_t *get_nr_ue_nas_info(uint8_t ue_inst)
+{
+ AssertFatal(ue_inst >= 0 && ue_inst < MAX_NUM_NR_UE_INST, "Invalid UE instance\n");
+ return &nr_ue_nas[ue_inst];
+}
+
#define FOREACH_STATE(TYPE_DEF) \
TYPE_DEF(NAS_SECURITY_NO_SECURITY_CONTEXT, 0) \
TYPE_DEF(NAS_SECURITY_UNPROTECTED, 1) \
@@ -1166,6 +1172,10 @@ static void generateSecurityModeComplete(nr_ue_nas_t *nas, as_nas_info_t *initia
security_header_len
+ mm_msg_encode(plain, (uint8_t *)(initialNasMsg->nas_data + security_header_len), size - security_header_len);
+ if (rr.nas_data) {
+ free(rr.nas_data);
+ }
+
/* ciphering */
uint8_t buf[initialNasMsg->length - 7];
stream_cipher.context = nas->security_container->ciphering_context;
diff --git a/openair3/NAS/NR_UE/nr_nas_msg.h b/openair3/NAS/NR_UE/nr_nas_msg.h
index bbd3a13fe5..7602525313 100644
--- a/openair3/NAS/NR_UE/nr_nas_msg.h
+++ b/openair3/NAS/NR_UE/nr_nas_msg.h
@@ -113,5 +113,6 @@ void *nas_nrue(void *args_p);
void nas_init_nrue(int num_ues);
void nr_ue_create_ip_if(const char *ifnameprefix, const char *ipv4, const char *ipv6, int ue_id, int pdu_session_id);
void request_pdusession(nr_ue_nas_t *nas, const pdu_session_config_t *pdu);
+nr_ue_nas_t *get_nr_ue_nas_info(uint8_t ue_inst);
#endif /* __NR_NAS_MSG_SIM_H__*/
diff --git a/openair3/NAS/UE/UEprocess.c b/openair3/NAS/UE/UEprocess.c
index 2a38c6a461..41ac5d5dde 100644
--- a/openair3/NAS/UE/UEprocess.c
+++ b/openair3/NAS/UE/UEprocess.c
@@ -393,7 +393,6 @@ static void _nas_clean(user_api_id_t *user_api_id, int net_fd)
LOG_TRACE (INFO, "UE-MAIN - Perform EMM and ESM cleanup");
// FIXME nas_network_cleanup depends on nas_user_t
- // Why this program should interfere like that with oaisim ?
//nas_network_cleanup ();
LOG_TRACE (INFO, "UE-MAIN - "
diff --git a/openair3/NGAP/ngap_gNB.c b/openair3/NGAP/ngap_gNB.c
index 995de828bb..da4108df12 100644
--- a/openair3/NGAP/ngap_gNB.c
+++ b/openair3/NGAP/ngap_gNB.c
@@ -66,10 +66,6 @@
#include "sctp_messages_types.h"
#include "tree.h"
-#if defined(TEST_S1C_AMF)
- #include "oaisim_amf_test_s1c.h"
-#endif
-
static int ngap_gNB_generate_ng_setup_request(
ngap_gNB_instance_t *instance_p, ngap_gNB_amf_data_t *ngap_amf_data_p);
@@ -300,13 +296,8 @@ static
void ngap_gNB_handle_sctp_data_ind(sctp_data_ind_t *sctp_data_ind) {
int result;
DevAssert(sctp_data_ind != NULL);
-#if defined(TEST_S1C_AMF)
- amf_test_s1_notify_sctp_data_ind(sctp_data_ind->assoc_id, sctp_data_ind->stream,
- sctp_data_ind->buffer, sctp_data_ind->buffer_length);
-#else
ngap_gNB_handle_message(sctp_data_ind->assoc_id, sctp_data_ind->stream,
sctp_data_ind->buffer, sctp_data_ind->buffer_length);
-#endif
result = itti_free(TASK_UNKNOWN, sctp_data_ind->buffer);
AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
}
diff --git a/openair3/S1AP/s1ap_eNB.c b/openair3/S1AP/s1ap_eNB.c
index 3676ecc88d..e5b2bf5e32 100644
--- a/openair3/S1AP/s1ap_eNB.c
+++ b/openair3/S1AP/s1ap_eNB.c
@@ -59,9 +59,6 @@
#include "assertions.h"
#include "conversions.h"
-#if defined(TEST_S1C_MME)
- #include "oaisim_mme_test_s1c.h"
-#endif
static int s1ap_eNB_generate_s1_setup_request(
s1ap_eNB_instance_t *instance_p, s1ap_eNB_mme_data_t *s1ap_mme_data_p);
@@ -411,15 +408,10 @@ static
void s1ap_eNB_handle_sctp_data_ind(sctp_data_ind_t *sctp_data_ind) {
int result;
DevAssert(sctp_data_ind != NULL);
-#if defined(TEST_S1C_MME)
- mme_test_s1_notify_sctp_data_ind(sctp_data_ind->assoc_id, sctp_data_ind->stream,
- sctp_data_ind->buffer, sctp_data_ind->buffer_length);
-#else
if (s1ap_eNB_handle_message(sctp_data_ind->assoc_id, sctp_data_ind->stream,
sctp_data_ind->buffer, sctp_data_ind->buffer_length) == -1) {
S1AP_ERROR("Failed to handle s1ap eNB message\n");
}
-#endif
result = itti_free(TASK_UNKNOWN, sctp_data_ind->buffer);
AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
}
diff --git a/openair3/valgrind.sh b/openair3/valgrind.sh
deleted file mode 100755
index e353645435..0000000000
--- a/openair3/valgrind.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-valgrind --tool=memcheck --leak-check=full --show-reachable=yes --num-callers=20 --track-origins=yes --track-fds=yes ./objs/OAISIM_MME/oaisim_mme 2>leakage
\ No newline at end of file
diff --git a/radio/fhi_72/oran-config.c b/radio/fhi_72/oran-config.c
index 4478154e6c..512a7841c6 100644
--- a/radio/fhi_72/oran-config.c
+++ b/radio/fhi_72/oran-config.c
@@ -441,17 +441,16 @@ void print_fh_config(const struct xran_fh_config *fh_config)
#endif
}
-static uint64_t get_u64_mask(const paramdef_t *pd)
+static void get_u128_mask(const paramdef_t *pd, uint64_t *u0t63, uint64_t *u64t127)
{
DevAssert(pd != NULL);
AssertFatal(pd->numelt > 0, "no entries for creation of mask\n");
- uint64_t mask = 0;
for (int i = 0; i < pd->numelt; ++i) {
int num = pd->iptr[i];
- AssertFatal(num >= 0 && num < 64, "cannot put element of %d in 64-bit mask\n", num);
- mask |= 1LL << num;
+ AssertFatal(num >= 0 && num < 128, "cannot put element of %d in 128-bit mask\n", num);
+ uint64_t *mask = num < 64 ? u0t63 : u64t127;
+ *mask |= 1LL << (num % 64);
}
- return mask;
}
#if defined F_RELEASE
@@ -520,8 +519,7 @@ static bool set_fh_io_cfg(struct xran_io_cfg *io_cfg, const paramdef_t *fhip, in
these parameters are machine specific */
io_cfg->core = *gpd(fhip, nump, ORAN_CONFIG_IO_CORE)->iptr; // core used for IO; absolute CPU core ID for xran library, it should be an isolated core
io_cfg->system_core = *gpd(fhip, nump, ORAN_CONFIG_SYSTEM_CORE)->iptr; // absolute CPU core ID for DPDK control threads, it should be an isolated core
- io_cfg->pkt_proc_core = get_u64_mask(gpd(fhip, nump, ORAN_CONFIG_WORKER_CORES)); // worker mask 0-63
- io_cfg->pkt_proc_core_64_127 = 0x0; // worker mask 64-127; to be used if machine supports more than 64 cores
+ get_u128_mask(gpd(fhip, nump, ORAN_CONFIG_WORKER_CORES), &io_cfg->pkt_proc_core, &io_cfg->pkt_proc_core_64_127); // worker masks 0-63 and 64-127
io_cfg->pkt_aux_core = 0; // sample app says 0 = "do not start"
io_cfg->timing_core = *gpd(fhip, nump, ORAN_CONFIG_IO_CORE)->iptr; // core used by xran
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 63d036a83c..f401a73a3d 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,2 +1,3 @@
add_subdirectory(nr-cuup)
add_subdirectory(nr-cu-nrppa)
+add_subdirectory(nr-ue-nas-simulator)
diff --git a/tests/nr-ue-nas-simulator/CMakeLists.txt b/tests/nr-ue-nas-simulator/CMakeLists.txt
new file mode 100644
index 0000000000..040d0f79cf
--- /dev/null
+++ b/tests/nr-ue-nas-simulator/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_executable(nr-ue-nas-simulator-test nr-ue-nas-simulator.c
+ ${OPENAIR2_DIR}/GNB_APP/gnb_config_common.c
+ ${OPENAIR2_DIR}/GNB_APP/gnb_config_ng.c)
+
+target_link_libraries(nr-ue-nas-simulator-test PRIVATE nr_nas nr_common lib_uicc usim_lib)
+target_link_libraries(nr-ue-nas-simulator-test PRIVATE
+ CONFIG_LIB ITTI SCTP_CLIENT
+ f1ap ngap SECURITY GTPV1U
+ UTIL softmodem_common)
+target_link_libraries(nr-ue-nas-simulator-test PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs)
+add_dependencies(nr-ue-nas-simulator-test params_libconfig)
diff --git a/tests/nr-ue-nas-simulator/README.md b/tests/nr-ue-nas-simulator/README.md
new file mode 100644
index 0000000000..b1af0e1e21
--- /dev/null
+++ b/tests/nr-ue-nas-simulator/README.md
@@ -0,0 +1,87 @@
+This is a simple standalone tester for 5G UE NAS signaling and NGAP messages. It connects to the AMF to perform NGAP and NAS encoding/decoding and validates the UE Registration and PDU Session Establishment processes without the overhead of a full Radio Access Network (PHY/MAC/RLC).
+
+[[_TOC_]]
+
+# Motivation
+- This tester is a lightweight, deterministic tool to **test the UE NAS layer directly**.
+- Developers can easily inject specific NAS messages depending on the use-case.
+- Developers can validate custom NAS/NGAP messages without the need to implement complete RRC/MAC etc procedures.
+
+# Overview
+From a schematic point of view, the tester and Core Network interaction looks like this:
+```
+ +-------+
+ | AMF |
+ +-------+
+ |
+ NGAP/NAS | (N1/N2)
+ |
+ +-------+
+ |tester | (nr-ue-nas-simulator-test)
+ +-------+
+
+```
+
+The tester is designed to validate the integration of NAS and NGAP messages required for a 5G Standalone (SA) UE to attach to the network. It acts as a simulated gNB and a UE, bridging NAS messages directly into NGAP payloads and connecting to the 5G Core Network's AMF.
+
+The following messages are integrated and tested:
+
+* **NGAP:** NG Setup Request/Response, Initial UE Message, Downlink NAS Transport, Uplink NAS Transport, Initial Context Setup Request/Response, PDU Session Resource Setup Request/Response, UE Context Release Command/Complete.
+
+* **NAS (5GMM/5GSM):** Registration Request/Accept/Complete, Authentication Request/Response, Security Mode Command/Complete, PDU Session Establishment Request/Accept, Deregistration Request/Accept.
+
+* **RRC (Custom):** Forwards the uplink/downlink NAS messages directly between UE and AMF.
+
+A test scenario is fixed to these steps:
+1. The 5G Core Network is deployed via Docker.
+2. The tester connects to the AMF and establishes the NGAP connection (NG Setup).
+3. The tester automatically triggers an Initial UE Registration Request.
+4. Authentication and NAS Security Mode procedures are completed.
+5. The tester requests and successfully establishes an IPv4 PDU Session.
+6. The tester finally initiates deregistration request and exits succesfully.
+
+# Usage
+To run this test, you need to set up the Core Network and build the tester.
+
+**1. Core Network Deployment**
+
+Pull the required Docker images for the OAI 5G Core:
+
+ docker pull oaisoftwarealliance/ims:latest
+ docker pull oaisoftwarealliance/oai-amf:develop
+ docker pull oaisoftwarealliance/oai-nrf:develop
+ docker pull oaisoftwarealliance/oai-smf:develop
+ docker pull oaisoftwarealliance/oai-udr:develop
+ docker pull oaisoftwarealliance/oai-upf:develop
+ docker pull oaisoftwarealliance/oai-udm:develop
+ docker pull oaisoftwarealliance/oai-ausf:develop
+
+Deploy the network using the docker compose file:
+
+ cd doc/tutorial_resources/oai-cn5g
+ docker compose -f docker-compose.yaml up -d
+
+At the end of all experiments, you can also undeploy the network with:
+ docker compose -f docker-compose.yaml down -t 0
+
+**2. Building the Tester**
+
+You can build the tester as follows:
+
+ cd ~/openairinterface5g
+ mkdir build && cd build && cmake .. -GNinja && ninja nr-ue-nas-simulator-test
+
+**3. Running the Tester**
+
+Start the tester with the dedicated configuration file:
+
+ openairinterface5g/build$ LD_LIBRARY_PATH=. ./tests/nr-ue-nas-simulator/nr-ue-nas-simulator-test -O ../tests/nr-ue-nas-simulator/test.conf
+
+# Limitations
+- The tester is limited to a fixed flow: Initial Attach -> PDU Session -> Deregistration.
+- The tester doesnt create a GTP tunnel after PDU session and uses a harcoded teid.
+
+# Future Enhancements
+- A real GTP tunnel can be created to test various data related functionalities like QoS.
+- Simulate the actual UE RRC layer, i.e., forwarding the CellGroupConfig, have the UE apply that (without actually using it).
+- Make it scriptable to test custom message implementations.
diff --git a/tests/nr-ue-nas-simulator/nr-ue-nas-simulator.c b/tests/nr-ue-nas-simulator/nr-ue-nas-simulator.c
new file mode 100644
index 0000000000..7e9f64e856
--- /dev/null
+++ b/tests/nr-ue-nas-simulator/nr-ue-nas-simulator.c
@@ -0,0 +1,410 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ * contact@openairinterface.org
+ */
+
+#include "executables/softmodem-common.h"
+#include "common/utils/ocp_itti/intertask_interface.h"
+#include "common/ran_context.h"
+#include "nfapi/oai_integration/vendor_ext.h"
+#include "openair2/GNB_APP/gnb_config_common.h"
+#include "openair2/GNB_APP/gnb_config_ng.h"
+#include "openair2/GNB_APP/gnb_paramdef.h"
+#include "openair3/SCTP/sctp_default_values.h"
+#include "openair3/NAS/NR_UE/nr_nas_msg.h"
+#include "executables/nr-uesoftmodem.h"
+#include "openair3/ocp-gtpu/gtp_itf.h"
+
+RAN_CONTEXT_t RC;
+THREAD_STRUCT thread_struct;
+uint64_t downlink_frequency[MAX_NUM_CCs][4];
+int64_t uplink_frequency_offset[MAX_NUM_CCs][4];
+int oai_exit = 0;
+int gnb_id = 0;
+uint32_t tac = 1;
+uint16_t mcc = 1;
+uint16_t mnc = 1;
+uint8_t mnc_len = 2;
+char *gnb_ipv4_address_for_NGU = NULL;
+int NB_UE_INST = 1;
+
+void exit_function(const char *file, const char *function, const int line, const char *s, const int assert)
+{
+ LOG_E(GNB_APP, "error at %s:%d:%s: %s\n", file, line, function, s);
+ abort();
+}
+
+nfapi_mode_t nfapi_mod = -1;
+void nfapi_setmode(nfapi_mode_t nfapi_mode)
+{
+ nfapi_mod = nfapi_mode;
+}
+nfapi_mode_t nfapi_getmode(void)
+{
+ return nfapi_mod;
+}
+
+ngran_node_t get_node_type()
+{
+ return ngran_gNB_CUUP;
+}
+
+nrUE_params_t *get_nrUE_params(void)
+{
+ static nrUE_params_t params = {0};
+ params.extra_pdu_id = -1;
+ return ¶ms;
+}
+
+void create_ue_ip_if(void)
+{
+}
+
+void create_ue_eth_if(void)
+{
+}
+
+void set_qfi(void)
+{
+}
+
+int nr_rlc_get_available_tx_space(const rnti_t rntiP, const logical_chan_id_t channel_idP)
+{
+ abort();
+ return 0;
+}
+
+configmodule_interface_t *uniqCfg = NULL;
+
+// Emulate registration request to register UE in the AMF (required for positioning)
+void send_initial_ue_message(instance_t instance)
+{
+ MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_NAS_FIRST_REQ);
+
+ NGAP_NAS_FIRST_REQ(msg_p).gNB_ue_ngap_id = 1; // Simulated UE ID
+
+ NGAP_NAS_FIRST_REQ(msg_p).plmn.mcc = mcc;
+ NGAP_NAS_FIRST_REQ(msg_p).plmn.mnc = mnc;
+ NGAP_NAS_FIRST_REQ(msg_p).plmn.mnc_digit_length = mnc_len;
+
+ NGAP_NAS_FIRST_REQ(msg_p).nr_cell_id = gnb_id;
+
+ NGAP_NAS_FIRST_REQ(msg_p).establishment_cause = NGAP_RRC_CAUSE_MO_SIGNALLING;
+
+ as_nas_info_t initialNasMsg = {0};
+ nr_ue_nas_t *nr_ue_nas = get_ue_nas_info(0);
+ generateRegistrationRequest(&initialNasMsg, nr_ue_nas, false);
+
+ NGAP_NAS_FIRST_REQ(msg_p).nas_pdu.buf = initialNasMsg.nas_data;
+ NGAP_NAS_FIRST_REQ(msg_p).nas_pdu.len = initialNasMsg.length;
+
+ NGAP_NAS_FIRST_REQ(msg_p).ue_identity.presenceMask = 0;
+
+ itti_send_msg_to_task(TASK_NGAP, instance, msg_p);
+}
+
+void send_uplink_nas_to_amf(instance_t instance, as_nas_info_t *nas_msg)
+{
+ if (nas_msg->length > 0 && nas_msg->nas_data != NULL) {
+ MessageDef *msg = itti_alloc_new_message(TASK_RRC_GNB, instance, NGAP_UPLINK_NAS);
+
+ NGAP_UPLINK_NAS(msg).gNB_ue_ngap_id = 1; // Simulated UE ID
+ NGAP_UPLINK_NAS(msg).nas_pdu.buf = nas_msg->nas_data;
+ NGAP_UPLINK_NAS(msg).nas_pdu.len = nas_msg->length;
+
+ NGAP_UPLINK_NAS(msg).plmn.mcc = mcc;
+ NGAP_UPLINK_NAS(msg).plmn.mnc = mnc;
+ NGAP_UPLINK_NAS(msg).plmn.mnc_digit_length = mnc_len;
+
+ NGAP_UPLINK_NAS(msg).nr_cell_id = gnb_id;
+ NGAP_UPLINK_NAS(msg).tac = tac;
+
+ itti_send_msg_to_task(TASK_NGAP, instance, msg);
+ }
+}
+
+void *rrc_ue_task_local(void *args_p)
+{
+ MessageDef *msg_p;
+ instance_t instance;
+ int result;
+
+ itti_mark_task_ready(TASK_RRC_NRUE);
+ LOG_I(NR_RRC, "Entering RRC UE task (TASK_RRC_NRUE)\n");
+
+ while (1) {
+ itti_receive_msg(TASK_RRC_NRUE, &msg_p);
+ instance = ITTI_MSG_DESTINATION_INSTANCE(msg_p);
+
+ switch (ITTI_MSG_ID(msg_p)) {
+ case TERMINATE_MESSAGE:
+ LOG_W(NR_RRC, " *** Exiting RRC UE thread\n");
+ itti_exit_task();
+ break;
+ case NAS_UPLINK_DATA_REQ:
+ LOG_I(NR_RRC, "Received NAS_UPLINK_DATA_REQ from NAS Library (via RRC UE Task)\n");
+ as_nas_info_t nas_msg;
+ nas_msg.nas_data = NAS_UPLINK_DATA_REQ(msg_p).nasMsg.nas_data;
+ nas_msg.length = NAS_UPLINK_DATA_REQ(msg_p).nasMsg.length;
+ send_uplink_nas_to_amf(instance, &nas_msg);
+ break;
+ case NAS_5GMM_IND:
+ LOG_I(NR_RRC, "Received NAS_5GMM_IND (ignored for nr_ue_nas_simulator test)\n");
+ break;
+ case NAS_KENB_REFRESH_REQ:
+ LOG_I(NR_RRC, "Received NAS_KENB_REFRESH_REQ (ignored for nr_ue_nas_simulator test)\n");
+ break;
+ case NAS_DETACH_REQ:
+ LOG_I(NR_RRC, "Received NAS_DETACH_REQ (ignored for nr_ue_nas_simulator test)\n");
+ break;
+ default:
+ LOG_E(NR_RRC, "RRC UE Task received unexpected message %s\n", ITTI_MSG_NAME(msg_p));
+ break;
+ }
+ result = itti_free(ITTI_MSG_ORIGIN_ID(msg_p), msg_p);
+ AssertFatal(result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
+ msg_p = NULL;
+ }
+ return NULL;
+}
+
+void *gNB_app_task(void *args_p)
+{
+ MessageDef *msg_p = NULL;
+ const char *msg_name = NULL;
+ instance_t instance;
+ int result;
+ /* for no gcc warnings */
+ (void)instance;
+
+ itti_mark_task_ready(TASK_GNB_APP);
+ do {
+ // Wait for a message
+ itti_receive_msg(TASK_GNB_APP, &msg_p);
+
+ msg_name = ITTI_MSG_NAME(msg_p);
+ instance = ITTI_MSG_DESTINATION_INSTANCE(msg_p);
+
+ switch (ITTI_MSG_ID(msg_p)) {
+ case TERMINATE_MESSAGE:
+ LOG_W(GNB_APP, " *** Exiting GNB_APP thread\n");
+ itti_exit_task();
+ break;
+
+ case NGAP_REGISTER_GNB_CNF:
+ LOG_I(GNB_APP, "[gNB %ld] Received %s: associated AMF %d\n", instance, msg_name, NGAP_REGISTER_GNB_CNF(msg_p).nb_amf);
+ LOG_I(GNB_APP, "Triggering NAS Registration sequence\n");
+ send_initial_ue_message(instance);
+ break;
+
+ default:
+ LOG_E(GNB_APP, "Received unexpected message %s\n", msg_name);
+ break;
+ }
+ result = itti_free(ITTI_MSG_ORIGIN_ID(msg_p), msg_p);
+ AssertFatal(result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
+ } while (1);
+
+ return NULL;
+}
+
+// Forward NAS payload from the AMF directly to the NAS NRUE Task
+void forward_downlink_nas_to_ue(uint8_t *pdu_buf, uint32_t pdu_len)
+{
+ if (pdu_len > 0 && pdu_buf != NULL) {
+ MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NAS_DOWNLINK_DATA_IND);
+
+ NAS_DOWNLINK_DATA_IND(msg_p).UEid = 0;
+ NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.length = pdu_len;
+ NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.nas_data = malloc_or_fail(pdu_len);
+ memcpy(NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.nas_data, pdu_buf, pdu_len);
+ free(pdu_buf);
+
+ LOG_I(NR_RRC, "Forwarding Downlink NAS message to real NAS Task (Length: %d)\n", pdu_len);
+ itti_send_msg_to_task(TASK_NAS_NRUE, 0, msg_p);
+ }
+}
+
+void *rrc_gnb_task(void *args_p)
+{
+ MessageDef *msg_p;
+ MessageDef *msg_p_resp;
+ instance_t instance;
+ int result;
+
+ itti_mark_task_ready(TASK_RRC_GNB);
+ LOG_I(NR_RRC, "Entering main loop of NR_RRC message task\n");
+
+ while (1) {
+ // Wait for a message
+ itti_receive_msg(TASK_RRC_GNB, &msg_p);
+ const char *msg_name_p = ITTI_MSG_NAME(msg_p);
+ instance = ITTI_MSG_DESTINATION_INSTANCE(msg_p);
+ LOG_D(NR_RRC,
+ "RRC GNB Task Received %s for instance %ld from task %s\n",
+ ITTI_MSG_NAME(msg_p),
+ ITTI_MSG_DESTINATION_INSTANCE(msg_p),
+ ITTI_MSG_ORIGIN_NAME(msg_p));
+ switch (ITTI_MSG_ID(msg_p)) {
+ case TERMINATE_MESSAGE:
+ LOG_W(NR_RRC, " *** Exiting NR_RRC thread\n");
+ itti_exit_task();
+ break;
+ case NGAP_DOWNLINK_NAS:
+ LOG_I(NR_RRC, "Received NGAP_DOWNLINK_NAS\n");
+ forward_downlink_nas_to_ue(NGAP_DOWNLINK_NAS(msg_p).nas_pdu.buf, NGAP_DOWNLINK_NAS(msg_p).nas_pdu.len);
+ break;
+ case NGAP_INITIAL_CONTEXT_SETUP_REQ:
+ LOG_I(NR_RRC, "Received NGAP_INITIAL_CONTEXT_SETUP_REQ\n");
+
+ if (NGAP_INITIAL_CONTEXT_SETUP_REQ(msg_p).nas_pdu.len > 0) {
+ LOG_I(NR_RRC, "Processing NAS PDU in Initial Context Setup\n");
+ forward_downlink_nas_to_ue(NGAP_INITIAL_CONTEXT_SETUP_REQ(msg_p).nas_pdu.buf,
+ NGAP_INITIAL_CONTEXT_SETUP_REQ(msg_p).nas_pdu.len);
+ }
+
+ LOG_I(NR_RRC, "Sending NGAP_INITIAL_CONTEXT_SETUP_RESP\n");
+ msg_p_resp = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_INITIAL_CONTEXT_SETUP_RESP);
+ NGAP_INITIAL_CONTEXT_SETUP_RESP(msg_p_resp).gNB_ue_ngap_id = NGAP_INITIAL_CONTEXT_SETUP_REQ(msg_p).gNB_ue_ngap_id;
+ itti_send_msg_to_task(TASK_NGAP, instance, msg_p_resp);
+ break;
+ case NAS_UPLINK_DATA_REQ:
+ LOG_I(NR_RRC, "Received NAS_UPLINK_DATA_REQ from NAS Library\n");
+ as_nas_info_t nas_msg;
+ nas_msg.nas_data = NAS_UPLINK_DATA_REQ(msg_p).nasMsg.nas_data;
+ nas_msg.length = NAS_UPLINK_DATA_REQ(msg_p).nasMsg.length;
+ send_uplink_nas_to_amf(instance, &nas_msg);
+ break;
+ case NGAP_PDUSESSION_SETUP_REQ:
+ LOG_I(NR_RRC, "Received NGAP_PDUSESSION_SETUP_REQ\n");
+
+ for (int i = 0; i < NGAP_PDUSESSION_SETUP_REQ(msg_p).nb_pdusessions_tosetup; i++) {
+ if (NGAP_PDUSESSION_SETUP_REQ(msg_p).pdusession[i].nas_pdu.len > 0) {
+ forward_downlink_nas_to_ue(NGAP_PDUSESSION_SETUP_REQ(msg_p).pdusession[i].nas_pdu.buf,
+ NGAP_PDUSESSION_SETUP_REQ(msg_p).pdusession[i].nas_pdu.len);
+ }
+ }
+
+ msg_p_resp = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_PDUSESSION_SETUP_RESP);
+ NGAP_PDUSESSION_SETUP_RESP(msg_p_resp).gNB_ue_ngap_id = NGAP_PDUSESSION_SETUP_REQ(msg_p).gNB_ue_ngap_id;
+ NGAP_PDUSESSION_SETUP_RESP(msg_p_resp).nb_of_pdusessions = NGAP_PDUSESSION_SETUP_REQ(msg_p).nb_pdusessions_tosetup;
+
+ for (int i = 0; i < NGAP_PDUSESSION_SETUP_REQ(msg_p).nb_pdusessions_tosetup; i++) {
+ pdusession_setup_t *resp_item = &NGAP_PDUSESSION_SETUP_RESP(msg_p_resp).pdusessions[i];
+ pdusession_resource_item_t *req_item = &NGAP_PDUSESSION_SETUP_REQ(msg_p).pdusession[i];
+
+ resp_item->pdusession_id = req_item->pdusession_id;
+ resp_item->pdu_session_type = req_item->pdusessionTransfer.pdu_session_type;
+ resp_item->n3_outgoing.teid = 0x00000001 + i;
+ struct in_addr addr;
+ inet_pton(AF_INET, gnb_ipv4_address_for_NGU, &addr);
+ resp_item->n3_outgoing.addr.length = 4;
+ memcpy(resp_item->n3_outgoing.addr.buffer, &addr.s_addr, resp_item->n3_outgoing.addr.length);
+ resp_item->nb_of_qos_flow = req_item->pdusessionTransfer.nb_qos;
+ DevAssert(resp_item->nb_of_qos_flow < MAX_QOS_FLOWS);
+ for (int q = 0; q < resp_item->nb_of_qos_flow; q++) {
+ resp_item->associated_qos_flows[q].qfi = req_item->pdusessionTransfer.qos[q].qfi;
+ resp_item->associated_qos_flows[q].qos_flow_mapping_ind = QOSFLOW_MAPPING_INDICATION_DL;
+ }
+ }
+
+ LOG_I(NR_RRC, "Sending NGAP_PDUSESSION_SETUP_RESP\n");
+ itti_send_msg_to_task(TASK_NGAP, instance, msg_p_resp);
+
+ // Initiate UE Deregistration
+ sleep(1);
+ MessageDef *msg = itti_alloc_new_message(TASK_NAS_NRUE, 0, NAS_DEREGISTRATION_REQ);
+ NAS_DEREGISTRATION_REQ(msg).cause = AS_DETACH;
+ itti_send_msg_to_task(TASK_NAS_NRUE, 0, msg);
+ break;
+ case NGAP_UE_CONTEXT_RELEASE_COMMAND:
+ LOG_I(NR_RRC, "Received NGAP_UE_CONTEXT_RELEASE_COMMAND\n");
+
+ msg_p_resp = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_UE_CONTEXT_RELEASE_COMPLETE);
+ NGAP_UE_CONTEXT_RELEASE_COMPLETE(msg_p_resp).gNB_ue_ngap_id = NGAP_UE_CONTEXT_RELEASE_COMMAND(msg_p).gNB_ue_ngap_id;
+
+ LOG_I(NR_RRC, "Sending NGAP_UE_CONTEXT_RELEASE_COMPLETE\n");
+ itti_send_msg_to_task(TASK_NGAP, instance, msg_p_resp);
+ LOG_I(NR_RRC, "Simulator flow finished successfully!\n");
+ exit(EXIT_SUCCESS);
+ break;
+ default:
+ LOG_E(NR_RRC, "[gNB %ld] Received unexpected message %s\n", instance, msg_name_p);
+ break;
+ }
+ result = itti_free(ITTI_MSG_ORIGIN_ID(msg_p), msg_p);
+ AssertFatal(result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
+ msg_p = NULL;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ // load the config file
+ if ((uniqCfg = load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY)) == NULL) {
+ exit_fun("[SOFTMODEM] Error, configuration module init failed\n");
+ }
+ logInit();
+
+ nas_init_nrue(NB_UE_INST);
+
+ set_softmodem_sighandler();
+
+ // Initialize ITTI tasks
+ itti_init(TASK_MAX, tasks_info);
+
+ int rc;
+ rc = itti_create_task(TASK_SCTP, sctp_eNB_task, NULL);
+ AssertFatal(rc >= 0, "Create task for SCTP failed\n");
+
+ rc = itti_create_task(TASK_NGAP, ngap_gNB_task, NULL);
+ AssertFatal(rc >= 0, "Create task for NGAP failed\n");
+
+ rc = itti_create_task(TASK_RRC_GNB, rrc_gnb_task, NULL);
+ AssertFatal(rc >= 0, "Create task for RRC failed\n");
+
+ rc = itti_create_task(TASK_GNB_APP, gNB_app_task, NULL);
+ AssertFatal(rc >= 0, "Create task for gNB_app failed\n");
+
+ rc = itti_create_task(TASK_NAS_NRUE, nas_nrue_task, NULL);
+ AssertFatal(rc >= 0, "Create task for nas_nrue_task failed\n");
+
+ rc = itti_create_task(TASK_RRC_NRUE, rrc_ue_task_local, NULL);
+ AssertFatal(rc >= 0, "Create task for RRC_NRUE failed\n");
+
+ MessageDef *msg_p;
+ msg_p = itti_alloc_new_message(TASK_GNB_APP, 0, NGAP_REGISTER_GNB_REQ);
+ RCconfig_NR_NG(msg_p, 0);
+ gnb_id = NGAP_REGISTER_GNB_REQ(msg_p).gNB_id;
+ tac = NGAP_REGISTER_GNB_REQ(msg_p).tac;
+ mcc = NGAP_REGISTER_GNB_REQ(msg_p).plmn[0].plmn.mcc;
+ mnc = NGAP_REGISTER_GNB_REQ(msg_p).plmn[0].plmn.mnc;
+ mnc_len = NGAP_REGISTER_GNB_REQ(msg_p).plmn[0].plmn.mnc_digit_length;
+ gnb_ipv4_address_for_NGU = NGAP_REGISTER_GNB_REQ(msg_p).gnb_ip_address.ipv4_address;
+
+ LOG_I(GNB_APP, "Sending NGAP_REGISTER_GNB_REQ to TASK_NGAP\n");
+ itti_send_msg_to_task(TASK_NGAP, 0, msg_p);
+
+ printf("TYPE
TO TERMINATE\n");
+ itti_wait_tasks_end(NULL);
+
+ logClean();
+ printf("Bye.\n");
+ return 0;
+}
diff --git a/tests/nr-ue-nas-simulator/test.conf b/tests/nr-ue-nas-simulator/test.conf
new file mode 100644
index 0000000000..ee4c82b90b
--- /dev/null
+++ b/tests/nr-ue-nas-simulator/test.conf
@@ -0,0 +1,59 @@
+Active_gNBs = ( "gNB-OAI");
+# Asn1_verbosity, choice in: none, info, annoying
+Asn1_verbosity = "none";
+
+gNBs =
+(
+ {
+ ////////// Identification parameters:
+ gNB_ID = 0xe00;
+ gNB_name = "gNB-OAI";
+
+ // Tracking area code, 0x0000 and 0xfffe are reserved values
+ tracking_area_code = 1;
+ plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1; }) });
+
+ nr_cellid = 12345678L;
+
+ # ------- SCTP definitions
+ SCTP :
+ {
+ # Number of streams to use in input/output
+ SCTP_INSTREAMS = 2;
+ SCTP_OUTSTREAMS = 2;
+ };
+
+
+ ////////// AMF parameters:
+ amf_ip_address = ({ ipv4 = "192.168.70.132"; });
+
+
+ NETWORK_INTERFACES :
+ {
+ GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24";
+ GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24";
+ GNB_PORT_FOR_S1U = 2152; # Spec 2152
+ };
+
+ }
+);
+
+uicc0 = {
+ imsi = "001010000000001";
+ key = "fec86ba6eb707ed08905757b1bb44b8f";
+ opc = "C42449363BBAD02B66D16BC975D77CC1";
+ pdu_sessions = ({ dnn = "oai"; nssai_sst = 1; });
+}
+
+log_config :
+{
+ global_log_level ="info";
+ hw_log_level ="info";
+ phy_log_level ="info";
+ mac_log_level ="info";
+ rlc_log_level ="info";
+ pdcp_log_level ="info";
+ rrc_log_level ="info";
+ ngap_log_level ="debug";
+ f1ap_log_level ="debug";
+};
diff --git a/tools/plots/README.md b/tools/plots/README.md
index 39f9fab6f1..a04043d097 100644
--- a/tools/plots/README.md
+++ b/tools/plots/README.md
@@ -32,3 +32,51 @@ The plots are saved in three formats: `.png`, `.pdf` and `.svg`.
### Example graphs:
#### BER

+
+
+## `plot_power_control.sh`
+
+This plots the relevant graphs for power control at the gNB, exported through T
+and recorded via the [`csv` tracer](../../common/utils/T/DOC/T/csv.md) and
+plotted with `gnuplot`.
+
+Run first the `csv` tracer in one terminal, selecting the desired quantities, and
+write them out to a file:
+
+ ./common/utils/T/tracer/csv -d ../common/utils/T/T_messages.txt -s $'\t' -t time GNB_MAC_PUSCH_POWER_CONTROL time snrx10 phr tpc tb_size txpower_calc rbSize mcs > /tmp/pusch.csv
+
+in a second terminal, run the gNB with desired configuration, and pass the
+`--T_stdout 2` option to connect to the `csv` tracer:
+
+ sudo ./nr-softmodem -O --T_stdout 2
+
+Note that you could also run both in one window, connecting both commands with
+a `&`. In that case, it's important to run the `nr-softmodem` at second
+position, which will receive signals and stop the `csv` tracer when stopping
+the main process.
+
+The script might also be used to print PUCCH SNR. This can be achieved by only
+recording the relevant fields with the `csv` tracer for trace
+`GNB_MAC_PUCCH_POWER_CONTROL
+
+ ./common/utils/T/tracer/record -d ../common/utils/T/T_messages.txt -OFF -on GNB_MAC_PUSCH_POWER_CONTROL -on GNB_MAC_PUCCH_POWER_CONTROL -o /tmp/record.raw
+
+Use the `replay` tracer and `csv` to separate into PUSCH and PUCCH traces.
+
+After recovering the `pusch.csv` file, you could plot it with a script.
+
+ ./plot-power-control.gp.sh /tmp/pusch.csv
+
+The script prints four graphs:
+
+1. prints instantaneous SNR, and "deltaMCS" (the power used for the
+ transmission if in deltaMCS mode that accounts for MCS). It also prints an
+ average SNR, but this has to be manually post-processed from the instanteous
+ SNR values (`snrx10` above)
+2. TPC commands and PHR of the UE
+3. Resource blocks used for all transmissions, and the MCS
+4. transport block size of the transmission, to estimate the amount of traffic.
+
+The script should either auto-scale graphs or have sensible defaults. It prints
+on screen. There is code to print to a file; uncomment the relevant lines to
+achieve this.
diff --git a/tools/plots/plot-power-control.gp.sh b/tools/plots/plot-power-control.gp.sh
new file mode 100755
index 0000000000..33329ad212
--- /dev/null
+++ b/tools/plots/plot-power-control.gp.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+function die() { echo "$@"; exit 1; }
+
+[[ $# == 1 ]] || die "usage: $0 "
+
+FILE=${1}
+
+#OUTPUT=/tmp/results.png
+#echo "dumping to $OUTPUT"
+gnuplot << EOF
+
+#set terminal pngcairo size 2000,2000 enhanced font 'Verdana,10'
+#set output "$OUTPUT"
+
+set format x '%H:%M:%S'
+set timefmt '%H:%M:%S'
+set datafile separator "\t"
+set xdata time
+#set xrange ['08:43:17':'08:43:18']
+set grid
+
+set multiplot layout 4,1
+
+set yrange [0:300]
+set ylabel "dB"
+plot "$FILE" using 1:2 w l title "SNRx10 (inst)", "$FILE" using 1:9 w l title "SNRx10 (smooth)", "$FILE" using 1:6 w l title "txpower-calc"
+
+set yrange [5:50]
+set ylabel "PHR (dB)"
+set y2range [0:3]
+set y2label "TPC"
+set boxwidth 0.1
+plot "$FILE" using 1:4 w points pt 7 axes x1y2 title "TPC", "$FILE" using 1:3 w l title "PHR"
+
+set yrange [0:60]
+set ylabel "RB"
+set y2range [0:27]
+set y2label "MCS"
+plot "$FILE" using 1:7 w l title "RB", "$FILE" using 1:8 w l axes x1y2 title "MCS"
+
+set yrange [0:1000]
+set ylabel "TB size (B)"
+plot "$FILE" using 1:5 w l
+
+unset multiplot
+
+pause mouse close
+EOF