diff --git a/CMakeLists.txt b/CMakeLists.txt index 98dba37442..8484d4bf78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -648,7 +648,6 @@ include_directories("${OPENAIR3_DIR}/S1AP") include_directories("${OPENAIR2_DIR}/X2AP") include_directories("${OPENAIR2_DIR}/M2AP") include_directories("${OPENAIR2_DIR}/F1AP") -include_directories("${OPENAIR3_DIR}/ocp-gtpu") include_directories("${OPENAIR3_DIR}/M3AP") include_directories("${OPENAIR3_DIR}/MME_APP") include_directories("${OPENAIR_DIR}/radio/COMMON") @@ -1149,7 +1148,7 @@ set(L2_SRC ${RRC_DIR}/rrc_eNB_S1AP.c ${RRC_DIR}/rrc_eNB_M2AP.c ${RRC_DIR}/rrc_eNB_UE_context.c - ${NR_RRC_DIR}/rrc_gNB_UE_context.c + ${RRC_DIR}/rrc_eNB_GTPV1U.c ${RRC_DIR}/rrc_common.c ${RRC_DIR}/L2_interface.c ${RRC_DIR}/L2_interface_common.c @@ -1396,15 +1395,6 @@ include_directories("${OPENAIR1_DIR}/SCHED_NR_UE") # CN libs ########################## -add_library (GTPV1U - ${RRC_DIR}/rrc_eNB_GTPV1U.c - ${OPENAIR3_DIR}/ocp-gtpu/gtp_itf.cpp - ${OPENAIR3_DIR}/ocp-gtpu/gtpu_extensions.c - ) -target_link_libraries(GTPV1U PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) -target_link_libraries(GTPV1U PRIVATE SIMU ds) -include_directories(${OPENAIR3_DIR}/ocp-gtp) - set (MME_APP_SRC ${OPENAIR3_DIR}/MME_APP/mme_app.c ${OPENAIR3_DIR}/MME_APP/mme_config.c diff --git a/openair2/E1AP/CMakeLists.txt b/openair2/E1AP/CMakeLists.txt index eb3bd0f546..39b768ffc3 100644 --- a/openair2/E1AP/CMakeLists.txt +++ b/openair2/E1AP/CMakeLists.txt @@ -4,7 +4,7 @@ add_subdirectory(lib) add_library(e1ap e1ap.c e1ap_common.c) target_link_libraries(e1ap PUBLIC asn1_e1ap e1ap_lib - PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs asn1_f1ap UTIL e1_if) + PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs asn1_f1ap UTIL e1_if GTPV1U) target_include_directories(e1ap PUBLIC ${CMAKE_CURRENT_DIR}) if(ENABLE_TESTS) diff --git a/openair2/RRC/LTE/rrc_eNB_GTPV1U.c b/openair2/RRC/LTE/rrc_eNB_GTPV1U.c index 7010c7c831..4088e2b0c6 100644 --- a/openair2/RRC/LTE/rrc_eNB_GTPV1U.c +++ b/openair2/RRC/LTE/rrc_eNB_GTPV1U.c @@ -82,168 +82,6 @@ rrc_eNB_process_GTPV1U_CREATE_TUNNEL_RESP( } } -//------------------------------------------------------------------------------ -bool gtpv_data_req(const protocol_ctxt_t* const ctxt_pP, - const rb_id_t rb_idP, - const mui_t muiP, - const confirm_t confirmP, - const sdu_size_t sdu_sizeP, - uint8_t* const buffer_pP, - const pdcp_transmission_mode_t modeP, - uint32_t task_id) -//------------------------------------------------------------------------------ -{ - if(sdu_sizeP == 0) - { - LOG_I(GTPU,"gtpv_data_req sdu_sizeP == 0"); - return false; - } - LOG_D(GTPU, "gtpv_data_req ue rnti %lx sdu_sizeP %d rb id %ld", ctxt_pP->rntiMaybeUEid, sdu_sizeP, rb_idP); - MessageDef *message_p; - // Uses a new buffer to avoid issue with PDCP buffer content that could be changed by PDCP (asynchronous message handling). - uint8_t *message_buffer; - - if(task_id == TASK_DATA_FORWARDING){ - - LOG_I(GTPU,"gtpv_data_req task_id = TASK_DATA_FORWARDING\n"); - - message_buffer = itti_malloc (TASK_GTPV1_U, TASK_DATA_FORWARDING, sdu_sizeP); - - memcpy (message_buffer, buffer_pP, sdu_sizeP); - - message_p = itti_alloc_new_message (TASK_GTPV1_U, 0, GTPV1U_ENB_DATA_FORWARDING_IND); - GTPV1U_ENB_DATA_FORWARDING_IND (message_p).frame = ctxt_pP->frame; - GTPV1U_ENB_DATA_FORWARDING_IND (message_p).enb_flag = ctxt_pP->enb_flag; - GTPV1U_ENB_DATA_FORWARDING_IND (message_p).rb_id = rb_idP; - GTPV1U_ENB_DATA_FORWARDING_IND (message_p).muip = muiP; - GTPV1U_ENB_DATA_FORWARDING_IND (message_p).confirmp = confirmP; - GTPV1U_ENB_DATA_FORWARDING_IND (message_p).sdu_size = sdu_sizeP; - GTPV1U_ENB_DATA_FORWARDING_IND (message_p).sdu_p = message_buffer; - GTPV1U_ENB_DATA_FORWARDING_IND (message_p).mode = modeP; - GTPV1U_ENB_DATA_FORWARDING_IND (message_p).module_id = ctxt_pP->module_id; - GTPV1U_ENB_DATA_FORWARDING_IND(message_p).rnti = ctxt_pP->rntiMaybeUEid; - GTPV1U_ENB_DATA_FORWARDING_IND (message_p).eNB_index = ctxt_pP->eNB_index; - - itti_send_msg_to_task (TASK_DATA_FORWARDING, ctxt_pP->instance, message_p); - return true; // TODO should be changed to a CNF message later, currently RRC lite does not used the returned value anyway. - } else if (task_id == TASK_END_MARKER){ - - LOG_I(GTPU,"gtpv_data_req task_id = TASK_END_MARKER\n"); - - message_buffer = itti_malloc (TASK_GTPV1_U, TASK_END_MARKER, sdu_sizeP); - - memcpy (message_buffer, buffer_pP, sdu_sizeP); - - message_p = itti_alloc_new_message (TASK_GTPV1_U, 0, GTPV1U_ENB_END_MARKER_IND); - GTPV1U_ENB_END_MARKER_IND (message_p).frame = ctxt_pP->frame; - GTPV1U_ENB_END_MARKER_IND (message_p).enb_flag = ctxt_pP->enb_flag; - GTPV1U_ENB_END_MARKER_IND (message_p).rb_id = rb_idP; - GTPV1U_ENB_END_MARKER_IND (message_p).muip = muiP; - GTPV1U_ENB_END_MARKER_IND (message_p).confirmp = confirmP; - GTPV1U_ENB_END_MARKER_IND (message_p).sdu_size = sdu_sizeP; - GTPV1U_ENB_END_MARKER_IND (message_p).sdu_p = message_buffer; - GTPV1U_ENB_END_MARKER_IND (message_p).mode = modeP; - GTPV1U_ENB_END_MARKER_IND (message_p).module_id = ctxt_pP->module_id; - GTPV1U_ENB_END_MARKER_IND(message_p).rnti = ctxt_pP->rntiMaybeUEid; - GTPV1U_ENB_END_MARKER_IND (message_p).eNB_index = ctxt_pP->eNB_index; - - itti_send_msg_to_task (TASK_END_MARKER, ctxt_pP->instance, message_p); - return true; // TODO should be changed to a CNF message later, currently RRC lite does not used the returned value anyway. - } - LOG_E(RRC, "Impossible state\n"); - return false; -} - -bool gtpv_data_req_new(protocol_ctxt_t *ctxt, - const srb_flag_t srb_flagP, - const rb_id_t rb_idP, - const mui_t muiP, - const confirm_t confirmP, - const sdu_size_t sdu_buffer_sizeP, - unsigned char *const sdu_buffer_pP, - const pdcp_transmission_mode_t modeP, - const uint32_t *sourceL2Id, - const uint32_t *destinationL2Id) { - int task; - - if (sdu_buffer_sizeP==0) - task=TASK_END_MARKER; - else - task=TASK_DATA_FORWARDING; - - struct rrc_eNB_ue_context_s *ue_context_p = rrc_eNB_get_ue_context(RC.rrc[ctxt->module_id], ctxt->rntiMaybeUEid); - if(ue_context_p == NULL || ue_context_p->ue_context.handover_info == NULL || - ue_context_p->ue_context.StatusRrc != RRC_HO_EXECUTION) { - LOG_E(RRC,"incoming GTP-U for X2 in non HO context\n"); - return false; - } - - /* in the source enb, UE in RRC_HO_EXECUTION mode */ - // We have 2*2=4 cases (data or end marker) * (from slave, from EPC) - if (ue_context_p->ue_context.handover_info->state == HO_COMPLETE) { - LOG_I(GTPU, "source receive END MARKER\n"); - /* set handover state */ - //ue_context_p->ue_context.handover_info->state = HO_END_MARKER; - MessageDef *msg; - // Configure end marker - msg = itti_alloc_new_message(TASK_GTPV1_U, 0, GTPV1U_ENB_END_MARKER_REQ); - GTPV1U_ENB_END_MARKER_REQ(msg).buffer = itti_malloc(TASK_GTPV1_U, TASK_GTPV1_U, GTPU_HEADER_OVERHEAD_MAX + sdu_buffer_sizeP); - memcpy(>PV1U_ENB_END_MARKER_REQ(msg).buffer[GTPU_HEADER_OVERHEAD_MAX], sdu_buffer_pP, sdu_buffer_sizeP); - GTPV1U_ENB_END_MARKER_REQ(msg).length = sdu_buffer_sizeP; - GTPV1U_ENB_END_MARKER_REQ(msg).rnti = ctxt->rntiMaybeUEid; - GTPV1U_ENB_END_MARKER_REQ(msg).rab_id = rb_idP; - GTPV1U_ENB_END_MARKER_REQ(msg).offset = GTPU_HEADER_OVERHEAD_MAX; - LOG_I(GTPU, "Send End Marker to GTPV1-U at frame %d and subframe %d \n", ctxt->frame,ctxt->subframe); - itti_send_msg_to_task(TASK_GTPV1_U, ENB_MODULE_ID_TO_INSTANCE(ctxt->module_id), msg); - return 0; - } - - /* target enb */ - // We have 2*2=4 cases (data or end marker) * (from source, from EPC) - if (ue_context_p->ue_context.StatusRrc == RRC_RECONFIGURED) { - // It should come from remote eNB - // case end marker by EPC is not possible ? - if (task==TASK_END_MARKER) { - LOG_I(GTPU, "target end receive END MARKER\n"); - // We close the HO, nothing to send to remote ? - ue_context_p->ue_context.handover_info->state = HO_END_MARKER; - gtpv1u_enb_delete_tunnel_req_t delete_tunnel_req; - memset(&delete_tunnel_req, 0, sizeof(delete_tunnel_req)); - delete_tunnel_req.rnti = ctxt->rntiMaybeUEid; - gtpv1u_delete_x2u_tunnel(ctxt->module_id, &delete_tunnel_req); - return true; - } else { - /* data packet */ - LOG_I(GTPU, "Received a message data forwarding length %d\n", sdu_buffer_sizeP); - // Is it from remote ENB - // We have to push it to the UE ? - if(ue_context_p->ue_context.handover_info->state != HO_COMPLETE) { - int result = pdcp_data_req( - ctxt, - srb_flagP, - rb_idP, - muiP, // mui - confirmP, // confirm - sdu_buffer_sizeP, - sdu_buffer_pP, - modeP, - sourceL2Id, - destinationL2Id - ); - ue_context_p->ue_context.handover_info->forwarding_state = FORWARDING_NO_EMPTY; - return result; - } else { /* It is from from epc message */ - /* in the source enb, UE in RRC_HO_EXECUTION mode */ - // ????? - return true; - } - } - } - LOG_E(RRC,"This function should return before the end\n"); - return false; -} - - void rrc_eNB_send_GTPV1U_ENB_DELETE_TUNNEL_REQ( module_id_t enb_mod_idP, rrc_eNB_ue_context_t* ue_context_pP diff --git a/openair2/RRC/LTE/rrc_eNB_GTPV1U.h b/openair2/RRC/LTE/rrc_eNB_GTPV1U.h index 7d1ff07e10..f23f575e9b 100644 --- a/openair2/RRC/LTE/rrc_eNB_GTPV1U.h +++ b/openair2/RRC/LTE/rrc_eNB_GTPV1U.h @@ -54,13 +54,4 @@ void rrc_eNB_send_GTPV1U_ENB_DELETE_TUNNEL_REQ( rrc_eNB_ue_context_t* ue_context_pP ); -bool gtpv_data_req(const protocol_ctxt_t* const ctxt_pP, - const rb_id_t rb_idP, - const mui_t muiP, - const confirm_t confirmP, - const sdu_size_t sdu_sizeP, - uint8_t* const buffer_pP, - const pdcp_transmission_mode_t modeP, - uint32_t task_id); - #endif /* RRC_ENB_GTPV1U_H_ */ diff --git a/openair3/CMakeLists.txt b/openair3/CMakeLists.txt index 55f9fe91d6..706d343f56 100644 --- a/openair3/CMakeLists.txt +++ b/openair3/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(LPP) +add_subdirectory(ocp-gtpu) add_subdirectory(M3AP) add_subdirectory(S1AP) add_subdirectory(NAS) diff --git a/openair3/ocp-gtpu/CMakeLists.txt b/openair3/ocp-gtpu/CMakeLists.txt new file mode 100644 index 0000000000..37e583aa5d --- /dev/null +++ b/openair3/ocp-gtpu/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library (GTPV1U gtp_itf.cpp gtpu_extensions.c) +target_link_libraries(GTPV1U PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs) # for ITTI +target_link_libraries(GTPV1U PRIVATE SIMU ds) +target_include_directories(GTPV1U PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + +if(ENABLE_TESTS) + add_subdirectory(tests) +endif() diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index 6650439e81..2327d24c2f 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -15,15 +15,15 @@ extern "C" { #include #include "common/utils/LOG/log.h" #include -#include -#include -#include -#include -#include -#include "openair2/SDAP/nr_sdap/nr_sdap.h" -#include "openair3/ocp-gtpu/gtpu_extensions.h" #include "sim.h" +// TODO these dependencies should not exist and be removed +#include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h" +#include "openair2/LAYER2/RLC/rlc.h" + +#include "gtp_itf.h" +#include "gtpu_extensions.h" + #pragma pack(1) typedef struct Gtpv1uMsgHeader { @@ -613,6 +613,20 @@ instance_t gtpv1Init(openAddr_t context) return id; } +/* \brief remove the GTP instance from the list of instances. Does not make an + * attempt to free corresponding TEIDs, as we have many and will simply not + * reuse it later. */ +int gtpv1Term(instance_t instance) +{ + pthread_mutex_lock(&globGtp.gtp_lock); + getInstRetInt(compatInst(instance)); + gtpv1uReceiverCancel(inst->thrData.t); + close(instance); + globGtp.instances.erase(instance); + pthread_mutex_unlock(&globGtp.gtp_lock); + return 0; +} + void GtpuUpdateTunnelOutgoingAddressAndTeid(instance_t instance, ue_id_t ue_id, ebi_t bearer_id, diff --git a/openair3/ocp-gtpu/gtp_itf.h b/openair3/ocp-gtpu/gtp_itf.h index 9274cd52f2..0ecddd6f19 100644 --- a/openair3/ocp-gtpu/gtp_itf.h +++ b/openair3/ocp-gtpu/gtp_itf.h @@ -130,50 +130,9 @@ typedef struct gtpv1u_gnb_delete_tunnel_req_s gtpv1u_gnb_delete_tunnel_req_t; size_t len); instance_t gtpv1Init(openAddr_t context); + int gtpv1Term(instance_t inst); void *gtpv1uTask(void *args); -#define GTPV1U_BEARER_OFFSET 3 -#define GTPV1U_MAX_BEARERS_ID (32 - GTPV1U_BEARER_OFFSET) /* max_val_LTE_DRB_Identity */ - typedef enum { - BEARER_DOWN = 0, - BEARER_IN_CONFIG, - BEARER_UP, - BEARER_DL_HANDOVER, - BEARER_UL_HANDOVER, - BEARER_MAX, - } bearer_state_t; - - typedef struct fixMe_gtpv1u_bearer_s { - /* TEID used in dl and ul */ - teid_t teid_eNB; ///< eNB TEID - uintptr_t teid_eNB_stack_session; ///< eNB TEID - teid_t teid_sgw; ///< Remote TEID - in_addr_t sgw_ip_addr; - struct in6_addr sgw_ip6_addr; - teid_t teid_teNB; - in_addr_t tenb_ip_addr; ///< target eNB ipv4 - struct in6_addr tenb_ip6_addr; ///< target eNB ipv6 - tcp_udp_port_t port; - //NwGtpv1uStackSessionHandleT stack_session; - bearer_state_t state; - } fixMe_gtpv1u_bearer_t; - - typedef struct gtpv1u_ue_data_s { - /* UE identifier for oaisim stack */ - rnti_t ue_id; - - /* Unique identifier used between PDCP and GTP-U to distinguish UEs */ - uint32_t instance_id; - int num_bearers; - /* Bearer related data. - * Note that the first LCID available for data is 3 and we fixed the maximum - * number of e-rab per UE to be (32 [id range]), max RB is 11. The real rb id will 3 + rab_id (3..32). - */ - fixMe_gtpv1u_bearer_t bearers[GTPV1U_MAX_BEARERS_ID]; - - //RB_ENTRY(gtpv1u_ue_data_s) gtpv1u_ue_node; - } gtpv1u_ue_data_t; - #ifdef __cplusplus } #endif diff --git a/openair3/ocp-gtpu/tests/CMakeLists.txt b/openair3/ocp-gtpu/tests/CMakeLists.txt new file mode 100644 index 0000000000..2b6b4d6189 --- /dev/null +++ b/openair3/ocp-gtpu/tests/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(test_gtp test_gtp.cpp) +target_link_libraries(test_gtp PRIVATE GTPV1U GTest::gtest minimal_lib) +# the following dependencies should ideally not exist, but GTP requires them +target_link_libraries(test_gtp PRIVATE ITTI asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs) +add_dependencies(tests test_gtp) +add_test(NAME test_gtp COMMAND ./test_gtp) diff --git a/openair3/ocp-gtpu/tests/test_gtp.cpp b/openair3/ocp-gtpu/tests/test_gtp.cpp new file mode 100644 index 0000000000..cd307df7e9 --- /dev/null +++ b/openair3/ocp-gtpu/tests/test_gtp.cpp @@ -0,0 +1,236 @@ +/* + * 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 +#include + +#include + +#include "common/platform_types.h" +#include "common/utils/LOG/log.h" +#include "common/config/config_userapi.h" + +#include "gtp_itf.h" + +extern "C" { +configmodule_interface_t *uniqCfg; + +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(); +} + +int nr_rlc_get_available_tx_space(const rnti_t rntiP, const logical_chan_id_t channel_idP) +{ + abort(); + return 0; +} +void *get_softmodem_params(void) +{ + abort(); + return NULL; +}; +} + +/* @brief initialize GTP interface for IP:port */ +static instance_t init_gtp(const char *ip, uint16_t port) +{ + openAddr_t iface = {0}; + DevAssert(strlen(ip) <= sizeof(iface.originHost)); + strcpy(iface.originHost, ip); + snprintf(iface.originService, sizeof(iface.originService), "%d", port); + snprintf(iface.destinationService, sizeof(iface.destinationService), "%d", port); + return gtpv1Init(iface); +} + +static in_addr_t get_addr(int ai_family, const char *ip) +{ + AssertFatal(ai_family == AF_INET, "only IPv4 supported\n"); + in_addr_t addr = {0}; + inet_pton(ai_family, ip, &addr); + return addr; +} + +static transport_layer_addr_t get_tl_addr(int ai_family, const char *ip) +{ + in_addr_t addr = get_addr(ai_family, ip); + transport_layer_addr_t tl_addr = {.length = 32}; + memcpy(tl_addr.buffer, &addr, 4); + return tl_addr; +} + +static void run_basic_test(uint32_t ue_id, + long pdu_id, + long qfi, + int num_send, + int *rcv_count, + gtpCallback callBack, + gtpCallbackSDAP callBackSDAP) +{ + /* set up two instances on different IPs */ + const char *ip1 = "127.0.0.1"; + const char *ip2 = "127.0.0.2"; + uint16_t port = 4567; + instance_t ep1 = init_gtp(ip1, port); + EXPECT_GE(ep1, 1); + instance_t ep2 = init_gtp(ip2, port); + EXPECT_GE(ep2, 1); + EXPECT_NE(ep1, ep2); // different IPs => different interfaces + + /* Create receiving end on ep1. Since we don't know the ep2's TEID, we also + * don't provide an address yet, hence "null_addr". Install the callback + * specific to this test. */ + transport_layer_addr_t null_addr = {.length = 32}; + teid_t t1 = newGtpuCreateTunnel(ep1, ue_id, pdu_id, pdu_id, -1, qfi, null_addr, callBack, callBackSDAP); + + /* Create the sending end on ep2. We have ep1's address/TEID, so create the + * remote endpoint. Don't provide a callback, as this is supposed to be + * unidirectional. */ + transport_layer_addr_t tl_addr1 = get_tl_addr(AF_INET, ip1); + teid_t t2 = newGtpuCreateTunnel(ep2, ue_id, pdu_id, pdu_id, t1, qfi, tl_addr1, NULL, NULL); + + EXPECT_NE(t1, t2); // cannot be the same TEIDs + + /* With the TEID at ep2, update ep1 endpoint. */ + in_addr_t addr2 = get_addr(AF_INET, ip2); + GtpuUpdateTunnelOutgoingAddressAndTeid(ep1, ue_id, pdu_id, addr2, t2); + + /* Send num_send messages, with each message three bytes with a specfic + * payload structure (each byte increasing within the buffer and across + * message) checked by the callback recv_basic_conn_qfi(). */ + uint8_t buf[3]; + uint8_t payload_counter = 0; + for (int i = 0; i < num_send; ++i) { + for (size_t p = 0; p < sizeof(buf); p++) + buf[p] = payload_counter++; + gtpv1uSendDirect(ep2, ue_id, pdu_id, buf, sizeof(buf), false, false); + } + + usleep(100 * 1000); // wait 100ms to give time to receive packets + + /* check that three messages have been received. The callback checked that + * the messages had the expected format. */ + EXPECT_EQ(*rcv_count, num_send); + + int ret; + ret = newGtpuDeleteAllTunnels(ep1, ue_id); + EXPECT_EQ(ret, 0); + ret = newGtpuDeleteAllTunnels(ep2, ue_id); + EXPECT_EQ(ret, 0); + + ret = gtpv1Term(ep1); + EXPECT_EQ(ret, 0); + ret = gtpv1Term(ep2); + EXPECT_EQ(ret, 0); +} + +static int recv_count_qfi = 0; +static bool recv_basic_conn_qfi(protocol_ctxt_t *ctxt, + const ue_id_t ue_id, + const srb_flag_t flag, + const mui_t mui, + const confirm_t confirm, + const sdu_size_t size, + unsigned char *const buf, + const pdcp_transmission_mode_t modeP, + const uint32_t *sourceL2Id, + const uint32_t *destinationL2Id, + const uint8_t qfi, + const bool rqi, + const int pdusession_id) +{ + EXPECT_EQ(ue_id, 1); // as defined in basic_conn_qfi + EXPECT_EQ(qfi, 1); + EXPECT_EQ(size, 3); + for (int i = 0; i < size; ++i) + EXPECT_EQ(buf[i], recv_count_qfi * size + i); + recv_count_qfi++; + LOG_I(GTPU, "received message %d with size 3, content %d.%d.%d\n", recv_count_qfi, buf[0], buf[1], buf[2]); + return true; +} + +/* Test unidirectional GTP message forwarding for a single UE with QFI. */ +TEST(gtp, basic_conn_qfi) +{ + /* we consider only a single UE on a specfic bearer (ID=3) */ + uint32_t ue_id = 1; + long pdu_id = 3; + long qfi = 1; + int num_send = 3; + run_basic_test(ue_id, pdu_id, qfi, num_send, &recv_count_qfi, NULL, recv_basic_conn_qfi); +} + +static int recv_count = 0; +static bool recv_basic_conn(protocol_ctxt_t *ctxt, + const srb_flag_t srb_flagP, + const rb_id_t rb_idP, + const mui_t muiP, + const confirm_t confirmP, + const sdu_size_t size, + unsigned char *const buf, + const pdcp_transmission_mode_t modeP, + const uint32_t *sourceL2Id, + const uint32_t *destinationL2Id) +{ + EXPECT_EQ(ctxt->rntiMaybeUEid, 12); // as defined in basic_conn + EXPECT_EQ(size, 3); + for (int i = 0; i < size; ++i) + EXPECT_EQ(buf[i], recv_count * size + i); + recv_count++; + LOG_I(GTPU, "received message %d with size 3, content %d.%d.%d\n", recv_count, buf[0], buf[1], buf[2]); + return true; +} + +/* Test unidirectional GTP message forwarding for a single UE (without QFI). */ +TEST(gtp, basic_conn) +{ + /* we consider only a single UE on a specfic bearer (ID=3) */ + uint32_t ue_id = 12; + long pdu_id = 5; + long noqfi = -1; + int num_send = 12; + run_basic_test(ue_id, pdu_id, noqfi, num_send, &recv_count, recv_basic_conn, NULL); +} + +/* ideas for tests: + * - share IP addresses among two instances (e.g., F1-U/NG-U on same IP/port) + * - support of IPv6 + * - test extension headers + * + * ideas for cleanup: + * - simplify callback, harmonize "normal" and "SDAP" callbacks + * - consistently use either transport_layer_addr_t or in_addr + */ + +int main(int argc, char **argv) +{ + //if ((uniqCfg = load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY)) == NULL) { + // exit_fun("[SOFTMODEM] Error, configuration module init failed\n"); + //} + // TODO logInit() without config? + logInit(); + g_log->log_component[GTPU].level = OAILOG_DEBUG; + + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +}