diff --git a/doc/README.md b/doc/README.md index 13a9669f76..745fc700a7 100644 --- a/doc/README.md +++ b/doc/README.md @@ -124,6 +124,7 @@ The other SDRs (AW2S, LimeSDR, ...) have no READMEs. - Component tests are under `tests/`. Currently, the following component tests are supported - A simple CU-UP tester, see the corresponding [README.md](../tests/nr-cuup/README.md). - A CU-NRPPA tester, see the corresponding [README.md](../tests/nr-cu-nrppa/README.md). + - A UE NAS tester, see the corresponding [README.md](../tests/nr-ue-nas-simulator/README.md). - [TESTBenches.md](./TESTBenches.md) lists the CI setup and links to pipelines - The CI setup uses a [custom framework](../ci-scripts/README.md) to run end-to-end tests. 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/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"; +};