mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-13 04:30:28 +00:00
Merge remote-tracking branch 'origin/gtp-unit-test' into integration_2026_w08 (!3918)
Add a simple GTP unit test Add a simple unit test for GTP that as of now tests the main functionality, i.e., for a single UE, set up a bearer between two endpoints and checking that traffic sending works. This is a start to extend it further, e.g., with API changes, extensions to support new functionality such as IPv6, and refactorings. A number of supporting cleanup has been done as well.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
add_subdirectory(LPP)
|
||||
add_subdirectory(ocp-gtpu)
|
||||
add_subdirectory(M3AP)
|
||||
add_subdirectory(S1AP)
|
||||
add_subdirectory(NAS)
|
||||
|
||||
8
openair3/ocp-gtpu/CMakeLists.txt
Normal file
8
openair3/ocp-gtpu/CMakeLists.txt
Normal file
@@ -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()
|
||||
@@ -15,15 +15,15 @@ extern "C" {
|
||||
#include <openair3/UTILS/conversions.h>
|
||||
#include "common/utils/LOG/log.h"
|
||||
#include <common/utils/ocp_itti/intertask_interface.h>
|
||||
#include <openair2/COMMON/gtpv1_u_messages_types.h>
|
||||
#include <openair3/ocp-gtpu/gtp_itf.h>
|
||||
#include <openair2/LAYER2/PDCP_v10.1.0/pdcp.h>
|
||||
#include <openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h>
|
||||
#include <openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h>
|
||||
#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,
|
||||
|
||||
@@ -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
|
||||
|
||||
6
openair3/ocp-gtpu/tests/CMakeLists.txt
Normal file
6
openair3/ocp-gtpu/tests/CMakeLists.txt
Normal file
@@ -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)
|
||||
236
openair3/ocp-gtpu/tests/test_gtp.cpp
Normal file
236
openair3/ocp-gtpu/tests/test_gtp.cpp
Normal file
@@ -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 <stdlib.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
Reference in New Issue
Block a user