Merge remote-tracking branch 'origin/gtp-e1-fix-errors-test' into integration_2025_w44 (!3725)
Handle GTP receiver errors, fix memory leaks, add CU-UP test - Fix use-after-free bugs in GTP by properly stopping the receive thread(s) - Fix a bug from !3519 (merged): QFI 0 is a valid QFI - Fix multiple memory leaks in CU-UP, E1, CU-UP load tester, SCTP - Add a (conservative) functional CU-UP test that verifies that the load tester and CU-UP work. Test only a low throughput, as it will run in parallel with other tests.
This commit is contained in:
@@ -150,7 +150,8 @@ int main(int argc, char **argv)
|
||||
time_manager_start(tick_functions, tick_functions_count, TIME_SOURCE_REALTIME);
|
||||
|
||||
// strdup to put the sring in the core file for post mortem identification
|
||||
LOG_I(HW, "Version: %s\n", strdup(OAI_PACKAGE_VERSION));
|
||||
char *v = strdup(OAI_PACKAGE_VERSION);
|
||||
LOG_I(HW, "Version: %s\n", v);
|
||||
set_softmodem_sighandler();
|
||||
itti_init(TASK_MAX, tasks_info);
|
||||
int rc;
|
||||
@@ -189,6 +190,8 @@ int main(int argc, char **argv)
|
||||
|
||||
time_manager_finish();
|
||||
|
||||
free(v);
|
||||
|
||||
logClean();
|
||||
printf("Bye.\n");
|
||||
return 0;
|
||||
|
||||
@@ -125,8 +125,8 @@ static void e1_task_handle_sctp_data_ind(instance_t instance, sctp_data_ind_t *s
|
||||
int e1ap_send_RESET(bool isCu, sctp_assoc_t assoc_id, E1AP_Reset_t *Reset)
|
||||
{
|
||||
AssertFatal(false, "Not implemented yet\n");
|
||||
E1AP_E1AP_PDU_t pdu = {0};
|
||||
return e1ap_encode_send(isCu, assoc_id, &pdu, 0, __func__);
|
||||
E1AP_E1AP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu));
|
||||
return e1ap_encode_send(isCu, assoc_id, pdu, 0, __func__);
|
||||
}
|
||||
|
||||
int e1ap_send_RESET_ACKNOWLEDGE(instance_t instance, E1AP_Reset_t *Reset)
|
||||
@@ -304,9 +304,9 @@ static void fill_CONFIGURATION_UPDATE(E1AP_E1AP_PDU_t *pdu)
|
||||
|
||||
void e1apCUUP_send_CONFIGURATION_UPDATE(sctp_assoc_t assoc_id)
|
||||
{
|
||||
E1AP_E1AP_PDU_t pdu = {0};
|
||||
fill_CONFIGURATION_UPDATE(&pdu);
|
||||
e1ap_encode_send(UPtype, assoc_id, &pdu, 0, __func__);
|
||||
E1AP_E1AP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu));
|
||||
fill_CONFIGURATION_UPDATE(pdu);
|
||||
e1ap_encode_send(UPtype, assoc_id, pdu, 0, __func__);
|
||||
}
|
||||
|
||||
int e1apCUCP_send_gNB_DU_CONFIGURATION_FAILURE(void)
|
||||
|
||||
@@ -226,7 +226,7 @@ int e1ap_encode_send(E1_t type, sctp_assoc_t assoc_id, E1AP_E1AP_PDU_t *pdu, uin
|
||||
|
||||
void *buffer = NULL;
|
||||
ssize_t encoded = aper_encode_to_new_buffer(&asn_DEF_E1AP_E1AP_PDU, 0, pdu, &buffer);
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_E1AP_E1AP_PDU, pdu);
|
||||
ASN_STRUCT_FREE(asn_DEF_E1AP_E1AP_PDU, pdu);
|
||||
|
||||
if (encoded < 0) {
|
||||
LOG_E(E1AP, "%s: Failed to encode E1AP message\n", func);
|
||||
|
||||
@@ -153,7 +153,7 @@ static void e1_add_bearers(const int ue_id, const NR_DRB_ToAddModList_t *addMod,
|
||||
// get SDAP config
|
||||
sdap_config_t sdap = nr_sdap_get_config(GNB_FLAG_YES, drb->cnAssociation->choice.sdap_Config, drb->drb_Identity);
|
||||
// add SDAP entity
|
||||
nr_sdap_addmod_entity(GNB_FLAG_YES, ue_id, sdap);
|
||||
nr_sdap_addmod_entity(GNB_FLAG_YES, ue_id, &sdap);
|
||||
// add PDCP entity
|
||||
nr_pdcp_add_drb(GNB_FLAG_YES, ue_id, drb->pdcp_Config, &sdap, sp);
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ static void rrc_nsa_add_drb(const int ue_id,
|
||||
sdap.drb_id = drb->drb_Identity;
|
||||
sdap.defaultDRB = true;
|
||||
// add SDAP entity (terminated at gNB, since it's EPC)
|
||||
nr_sdap_addmod_entity(GNB_FLAG_YES, ue_id, sdap);
|
||||
nr_sdap_addmod_entity(GNB_FLAG_YES, ue_id, &sdap);
|
||||
// add PDCP entity
|
||||
nr_pdcp_add_drb(GNB_FLAG_YES, ue_id, drb->pdcp_Config, &sdap, sp);
|
||||
}
|
||||
|
||||
@@ -619,7 +619,7 @@ static void rrc_ue_add_bearer(const int ue_id, const NR_DRB_ToAddMod_t *drb, con
|
||||
sdap = nr_sdap_get_config(GNB_FLAG_NO, drb->cnAssociation->choice.sdap_Config, drb->drb_Identity);
|
||||
}
|
||||
// add SDAP entity
|
||||
nr_sdap_addmod_entity(GNB_FLAG_NO, ue_id, sdap);
|
||||
nr_sdap_addmod_entity(GNB_FLAG_NO, ue_id, &sdap);
|
||||
// add PDCP entity
|
||||
nr_pdcp_add_drb(GNB_FLAG_NO, ue_id, drb->pdcp_Config, &sdap, sp);
|
||||
}
|
||||
|
||||
@@ -210,8 +210,8 @@ static void nr_sdap_rx_entity(nr_sdap_entity_t *entity,
|
||||
{
|
||||
/* The offset of the SDAP header, it might be 0 if has_sdap_rx is not true in the pdcp entity. */
|
||||
int offset=0;
|
||||
int qfi = buf[0] & 0x3F; // QFI is always the first 6 bits in the first octet
|
||||
if (qfi <= 0 || qfi >= SDAP_MAX_QFI) {
|
||||
uint8_t qfi = buf[0] & 0x3F; // QFI is always the first 6 bits in the first octet
|
||||
if (qfi >= SDAP_MAX_QFI) {
|
||||
LOG_E(SDAP, "Invalid QFI %d received in SDAP header\n", qfi);
|
||||
return;
|
||||
}
|
||||
@@ -450,7 +450,7 @@ static void nr_sdap_add_qos_flows_to_drb(nr_sdap_entity_t *entity, const sdap_co
|
||||
for (int i = 0; i < sdap->mappedQFIs2AddCount; i++) {
|
||||
uint8_t qfi = sdap->mappedQFIs2Add[i];
|
||||
LOG_D(SDAP, "Adding QFI to DRB mapping rules: %d mapped QFIs for DRB %d\n", sdap->mappedQFIs2AddCount, sdap->drb_id);
|
||||
if (qfi < SDAP_MAX_QFI && qfi > SDAP_MAP_RULE_EMPTY && sdap->drb_id > 0 && sdap->drb_id <= MAX_DRBS_PER_UE) {
|
||||
if (qfi < SDAP_MAX_QFI && sdap->drb_id > SDAP_MAP_RULE_EMPTY && sdap->drb_id <= MAX_DRBS_PER_UE) {
|
||||
entity->qfi2drb_map_add(entity, qfi, sdap->drb_id, sdap->role);
|
||||
} else {
|
||||
LOG_E(SDAP, "Failed to add qfi2drb mapping: QFI=%d, DRB=%d\n", qfi, sdap->drb_id);
|
||||
@@ -487,13 +487,13 @@ static void nr_sdap_qfi2drb_map_update(nr_sdap_entity_t *entity, const sdap_conf
|
||||
* @param is_gnb, indicates whether it is for gNB or UE
|
||||
* @param ue_id, UE ID
|
||||
* @param sdap, SDAP configuration */
|
||||
static void nr_sdap_add_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t sdap)
|
||||
static void nr_sdap_add_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t *sdap)
|
||||
{
|
||||
nr_sdap_entity_t *sdap_entity = calloc_or_fail(1, sizeof(*sdap_entity));
|
||||
|
||||
// SDAP entity ids
|
||||
sdap_entity->ue_id = ue_id;
|
||||
sdap_entity->pdusession_id = sdap.pdusession_id;
|
||||
sdap_entity->pdusession_id = sdap->pdusession_id;
|
||||
sdap_entity->is_gnb = is_gnb;
|
||||
|
||||
// rx/tx entities
|
||||
@@ -513,13 +513,13 @@ static void nr_sdap_add_entity(const int is_gnb, const ue_id_t ue_id, const sdap
|
||||
sdap_entity->pdusession_sock = -1;
|
||||
|
||||
// set default DRB
|
||||
if (sdap.defaultDRB) {
|
||||
sdap_entity->default_drb = sdap.drb_id;
|
||||
if (sdap->defaultDRB) {
|
||||
sdap_entity->default_drb = sdap->drb_id;
|
||||
LOG_I(SDAP, "Default DRB for the created SDAP entity: DRB %d \n", sdap_entity->default_drb);
|
||||
}
|
||||
|
||||
// Add QoS flows to the DRB (initial configuration)
|
||||
nr_sdap_add_qos_flows_to_drb(sdap_entity, &sdap);
|
||||
nr_sdap_add_qos_flows_to_drb(sdap_entity, sdap);
|
||||
|
||||
// update SDAP entity list pointers
|
||||
sdap_entity->next_entity = sdap_info.sdap_entity_llist;
|
||||
@@ -533,11 +533,11 @@ static void nr_sdap_add_entity(const int is_gnb, const ue_id_t ue_id, const sdap
|
||||
}
|
||||
|
||||
/** @brief Add or modify an SDAP entity if it already exists */
|
||||
void nr_sdap_addmod_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t sdap)
|
||||
void nr_sdap_addmod_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t *sdap)
|
||||
{
|
||||
nr_sdap_entity_t *sdap_entity = nr_sdap_get_entity(ue_id, sdap.pdusession_id);
|
||||
nr_sdap_entity_t *sdap_entity = nr_sdap_get_entity(ue_id, sdap->pdusession_id);
|
||||
if (sdap_entity) {
|
||||
sdap_entity->qfi2drb_map_update(sdap_entity, &sdap);
|
||||
sdap_entity->qfi2drb_map_update(sdap_entity, sdap);
|
||||
} else {
|
||||
nr_sdap_add_entity(is_gnb, ue_id, sdap);
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ sdap_config_t nr_sdap_get_config(const int is_gnb, const NR_SDAP_Config_t *sdap_
|
||||
void nr_sdap_release_drb(ue_id_t ue_id, int drb_id, int pdusession_id);
|
||||
|
||||
/** @brief Add or modify an SDAP entity based on whether it already exists */
|
||||
void nr_sdap_addmod_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t sdap);
|
||||
void nr_sdap_addmod_entity(const int is_gnb, const ue_id_t ue_id, const sdap_config_t *sdap);
|
||||
|
||||
/**
|
||||
* @brief Function to delete a single SDAP Entity based on the ue_id and pdusession_id.
|
||||
|
||||
@@ -477,6 +477,7 @@ static void sctp_handle_new_association_req(const instance_t instance,
|
||||
if (errno != EINPROGRESS) {
|
||||
SCTP_ERROR("Connect failed: %s\n", strerror(errno));
|
||||
sctp_itti_send_association_resp(requestor, instance, -1, req->ulp_cnx_id, SCTP_STATE_UNREACHABLE, 0, 0);
|
||||
freeaddrinfo(serv);
|
||||
close(sd);
|
||||
return;
|
||||
} else {
|
||||
|
||||
@@ -152,6 +152,7 @@ class gtpEndPoint {
|
||||
}
|
||||
};
|
||||
|
||||
static void gtpv1uReceiverCancel(pthread_t t);
|
||||
class gtpEndPoints {
|
||||
public:
|
||||
pthread_mutex_t gtp_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
@@ -168,8 +169,10 @@ class gtpEndPoints {
|
||||
~gtpEndPoints()
|
||||
{
|
||||
// automatically close all sockets on quit
|
||||
for (const auto &p : instances)
|
||||
for (const auto &p : instances) {
|
||||
gtpv1uReceiverCancel(p.second.thrData.t);
|
||||
close(p.first);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1283,7 +1286,7 @@ static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, const st
|
||||
return !GTPNOK;
|
||||
}
|
||||
|
||||
static void gtpv1uReceiveHandleMessage(int h)
|
||||
static bool gtpv1uReceiveHandleMessage(int h)
|
||||
{
|
||||
uint8_t udpData[65536];
|
||||
int udpDataLen;
|
||||
@@ -1293,19 +1296,19 @@ static void gtpv1uReceiveHandleMessage(int h)
|
||||
|
||||
if ((udpDataLen = recvfrom(h, udpData, sizeof(udpData), 0, (struct sockaddr *)&addr, &from_len)) < 0) {
|
||||
LOG_E(GTPU, "[%d] Recvfrom failed (%s)\n", h, strerror(errno));
|
||||
return;
|
||||
return false;
|
||||
} else if (udpDataLen == 0) {
|
||||
LOG_W(GTPU, "[%d] Recvfrom returned 0\n", h);
|
||||
return;
|
||||
return true;
|
||||
} else {
|
||||
if (udpDataLen < (int)sizeof(Gtpv1uMsgHeaderT)) {
|
||||
LOG_W(GTPU, "[%d] received malformed gtp packet \n", h);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
Gtpv1uMsgHeaderT *msg = (Gtpv1uMsgHeaderT *)udpData;
|
||||
if ((int)(ntohs(msg->msgLength) + sizeof(Gtpv1uMsgHeaderT)) != udpDataLen) {
|
||||
LOG_W(GTPU, "[%d] received malformed gtp packet length\n", h);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
LOG_D(GTPU, "[%d] Received GTP data, msg type: %x\n", h, msg->msgType);
|
||||
switch (msg->msgType) {
|
||||
@@ -1337,18 +1340,27 @@ static void gtpv1uReceiveHandleMessage(int h)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void* gtpv1uReceiver(void *thr)
|
||||
{
|
||||
gtpThread_t *gt = (gtpThread_t *)thr;
|
||||
while (true) {
|
||||
gtpv1uReceiveHandleMessage(gt->h);
|
||||
while (gtpv1uReceiveHandleMessage(gt->h)) {
|
||||
}
|
||||
LOG_W(GTPU, "exiting thread\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void gtpv1uReceiverCancel(pthread_t t)
|
||||
{
|
||||
int rc;
|
||||
rc = pthread_cancel(t);
|
||||
DevAssert(rc == 0);
|
||||
rc = pthread_join(t, NULL);
|
||||
DevAssert(rc == 0);
|
||||
}
|
||||
|
||||
#include <openair2/ENB_APP/enb_paramdef.h>
|
||||
|
||||
void *gtpv1uTask(void *args)
|
||||
@@ -1370,6 +1382,8 @@ void *gtpv1uTask(void *args)
|
||||
// DATA TO BE SENT TO UDP
|
||||
|
||||
case TERMINATE_MESSAGE:
|
||||
LOG_W(GTPU, "Exiting GTP instance %ld\n", myInstance);
|
||||
itti_exit_task();
|
||||
break;
|
||||
|
||||
case TIMER_HAS_EXPIRED:
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
add_executable(nr-cuup-load-test nr-cuup-load-test.c)
|
||||
add_dependencies(nr-cuup-load-test params_libconfig)
|
||||
target_link_libraries(nr-cuup-load-test PRIVATE GTPV1U ${T_LIB} utils ITTI e1ap
|
||||
SCTP_CLIENT)
|
||||
target_link_libraries(nr-cuup-load-test PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs) # ITTI
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
# we add also nr-cuup as it is required for the test
|
||||
add_dependencies(tests nr-cuup-load-test nr-cuup)
|
||||
add_test(NAME nr_cuup_functional_test
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/nr-cuup-functional-test.sh ${CMAKE_CURRENT_SOURCE_DIR}/load-test.conf
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
endif()
|
||||
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
# this script takes one argument, which is the path to the CU-UP configuration
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "usage: $0 <cuup-config>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CONFIG=$1
|
||||
|
||||
set -x
|
||||
|
||||
# current dir should be CMAKE_BINARY_DIR
|
||||
./nr-cuup -O ${CONFIG} &
|
||||
CUUP_PID=$!
|
||||
|
||||
timeout 5s ./tests/nr-cuup/nr-cuup-load-test -t 3 -d 10 -u 10
|
||||
RET=$?
|
||||
|
||||
kill ${CUUP_PID}
|
||||
exit ${RET}
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "openair3/SCTP/sctp_eNB_task.h"
|
||||
#include "openair2/E1AP/e1ap.h"
|
||||
#include "openair3/ocp-gtpu/gtp_itf.h"
|
||||
#include "openair2/E1AP/lib/e1ap_interface_management.h"
|
||||
|
||||
configmodule_interface_t *uniqCfg;
|
||||
|
||||
@@ -90,6 +91,7 @@ static void setup_cuup(sctp_assoc_t *assoc_id, e1ap_nssai_t *nssai)
|
||||
DevAssert(req->plmn[0].slice != NULL);
|
||||
*nssai = *req->plmn[0].slice;
|
||||
DevAssert(assoc_id != 0);
|
||||
free_e1ap_cuup_setup_request(req);
|
||||
itti_free(TASK_GNB_APP, itti_req);
|
||||
|
||||
// acknowledge the E1 setup request with a response
|
||||
@@ -664,6 +666,9 @@ int main(int argc, char *argv[])
|
||||
success = false;
|
||||
}
|
||||
|
||||
free(ng_ip);
|
||||
free(f1_ip);
|
||||
|
||||
if (success)
|
||||
LOG_A(GNB_APP, "test succeeded\n");
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user