diff --git a/CMakeLists.txt b/CMakeLists.txt index d5790159d4..667ad2a8b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,8 @@ if(NOT DEFINED ENV{CPM_SOURCE_CACHE}) endif() include("cmake_targets/CPM.cmake") +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_targets/tools/MODULES" "${CMAKE_MODULE_PATH}") + ############################## ### CCache: reduce compilation time ############################## @@ -1407,12 +1409,14 @@ add_library(MME_APP ${MME_APP_SRC}) target_link_libraries(MME_APP PRIVATE m2ap m3ap) target_link_libraries(MME_APP PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) +find_package(sctp REQUIRED) set(SCTP_SRC ${OPENAIR3_DIR}/SCTP/sctp_common.c ${OPENAIR3_DIR}/SCTP/sctp_eNB_task.c ${OPENAIR3_DIR}/SCTP/sctp_eNB_itti_messaging.c ) add_library(SCTP_CLIENT ${SCTP_SRC}) +target_link_libraries(SCTP_CLIENT PRIVATE sctp::sctp) target_link_libraries(SCTP_CLIENT PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) set(NAS_SRC ${OPENAIR3_DIR}/NAS/) @@ -1711,7 +1715,6 @@ include_directories("${NFAPI_DIR}/pnf_sim/inc") add_library(oai_iqplayer MODULE ${OPENAIR_DIR}/radio/iqplayer/iqplayer_lib.c ) -set(CMAKE_MODULE_PATH "${OPENAIR_DIR}/cmake_targets/tools/MODULES" "${CMAKE_MODULE_PATH}") ################################# # add executables for operation @@ -1758,7 +1761,7 @@ target_link_libraries(lte-softmodem PRIVATE ${NAS_UE_LIB} ITTI SIMU shlib_loader -Wl,--end-group z dl) -target_link_libraries(lte-softmodem PRIVATE pthread m CONFIG_LIB rt sctp) +target_link_libraries(lte-softmodem PRIVATE pthread m CONFIG_LIB rt) target_link_libraries(lte-softmodem PRIVATE ${T_LIB}) target_link_libraries(lte-softmodem PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) target_link_libraries(lte-softmodem PRIVATE ${blas_LIBRARIES} ${cblas_LIBRARIES} ${lapacke_LIBRARIES} ${lapack_LIBRARIES}) @@ -1814,7 +1817,7 @@ target_link_libraries(lte-uesoftmodem PRIVATE ${NAS_UE_LIB} ITTI shlib_loader -Wl,--end-group z dl) -target_link_libraries(lte-uesoftmodem PRIVATE pthread m CONFIG_LIB rt sctp) +target_link_libraries(lte-uesoftmodem PRIVATE pthread m CONFIG_LIB rt) target_link_libraries(lte-uesoftmodem PRIVATE ${T_LIB}) target_link_libraries(lte-uesoftmodem PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) target_link_libraries(lte-uesoftmodem PRIVATE ${blas_LIBRARIES} ${cblas_LIBRARIES} ${lapacke_LIBRARIES} ${lapack_LIBRARIES}) @@ -1851,7 +1854,7 @@ target_link_libraries(nr-softmodem PRIVATE time_management -Wl,--end-group z dl) -target_link_libraries(nr-softmodem PRIVATE pthread m CONFIG_LIB rt sctp) +target_link_libraries(nr-softmodem PRIVATE pthread m CONFIG_LIB rt) target_link_libraries(nr-softmodem PRIVATE ${T_LIB}) target_link_libraries(nr-softmodem PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) target_link_libraries(nr-softmodem PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) @@ -1888,7 +1891,7 @@ target_link_libraries(nr-cuup PRIVATE CONFIG_LIB ITTI SCTP_CLIENT GTPV1U e1ap f1ap time_management - z sctp dl pthread shlib_loader ${T_LIB}) + z dl pthread shlib_loader ${T_LIB}) target_link_libraries(nr-cuup PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs) if(E2_AGENT) target_link_libraries(nr-cuup PRIVATE e2_agent e2_agent_arg e2_ran_func_cuup) @@ -2182,3 +2185,4 @@ add_subdirectory(openair1) add_subdirectory(openair2) add_subdirectory(openair3) add_subdirectory(radio) +add_subdirectory(tests) diff --git a/ci-scripts/conf_files/gnb-cu.sa.band78.106prb.conf b/ci-scripts/conf_files/gnb-cu.sa.band78.106prb.conf index 311c8551fd..051ebd04fc 100644 --- a/ci-scripts/conf_files/gnb-cu.sa.band78.106prb.conf +++ b/ci-scripts/conf_files/gnb-cu.sa.band78.106prb.conf @@ -23,9 +23,7 @@ gNBs = local_s_address = "192.168.71.150"; remote_s_address = "192.168.71.171"; - local_s_portc = 501; local_s_portd = 2153; - remote_s_portc = 500; remote_s_portd = 2153; # ------- SCTP definitions diff --git a/ci-scripts/conf_files/gnb-cu.sa.f1.conf b/ci-scripts/conf_files/gnb-cu.sa.f1.conf index 84e5dbadb0..280281997e 100644 --- a/ci-scripts/conf_files/gnb-cu.sa.f1.conf +++ b/ci-scripts/conf_files/gnb-cu.sa.f1.conf @@ -23,9 +23,7 @@ gNBs = local_s_address = "127.0.0.4"; remote_s_address = "127.0.0.5"; - local_s_portc = 501; local_s_portd = 2153; - remote_s_portc = 500; remote_s_portd = 2153; # ------- SCTP definitions diff --git a/ci-scripts/conf_files/gnb-cucp.sa.f1.conf b/ci-scripts/conf_files/gnb-cucp.sa.f1.conf index 28bb7cfc33..214b5dd519 100644 --- a/ci-scripts/conf_files/gnb-cucp.sa.f1.conf +++ b/ci-scripts/conf_files/gnb-cucp.sa.f1.conf @@ -23,9 +23,7 @@ gNBs = local_s_address = "192.168.71.150"; remote_s_address = "0.0.0.0"; # multiple DUs - local_s_portc = 501; local_s_portd = 2152; - remote_s_portc = 500; remote_s_portd = 2152; # ------- SCTP definitions diff --git a/ci-scripts/conf_files/gnb-cucp.sa.f1.quectel.conf b/ci-scripts/conf_files/gnb-cucp.sa.f1.quectel.conf index 8cebc8640a..6a76eb7689 100644 --- a/ci-scripts/conf_files/gnb-cucp.sa.f1.quectel.conf +++ b/ci-scripts/conf_files/gnb-cucp.sa.f1.quectel.conf @@ -23,9 +23,7 @@ gNBs = local_s_address = "127.0.0.4"; remote_s_address = "127.0.0.5"; - local_s_portc = 501; local_s_portd = 2153; - remote_s_portc = 500; remote_s_portd = 2153; # ------- SCTP definitions diff --git a/ci-scripts/conf_files/gnb-cuup.sa.f1.conf b/ci-scripts/conf_files/gnb-cuup.sa.f1.conf index 5dd6c77a9c..e87ac862b1 100644 --- a/ci-scripts/conf_files/gnb-cuup.sa.f1.conf +++ b/ci-scripts/conf_files/gnb-cuup.sa.f1.conf @@ -22,9 +22,7 @@ gNBs = local_s_address = "192.168.72.161"; remote_s_address = "192.168.72.171"; - local_s_portc = 501; local_s_portd = 2153; - remote_s_portc = 500; remote_s_portd = 2153; # ------- SCTP definitions diff --git a/ci-scripts/conf_files/gnb-cuup.sa.f1.quectel.conf b/ci-scripts/conf_files/gnb-cuup.sa.f1.quectel.conf index b9f65cdf53..dbe510ea3f 100644 --- a/ci-scripts/conf_files/gnb-cuup.sa.f1.quectel.conf +++ b/ci-scripts/conf_files/gnb-cuup.sa.f1.quectel.conf @@ -22,9 +22,7 @@ gNBs = local_s_address = "127.0.0.6"; remote_s_address = "127.0.0.5"; - local_s_portc = 501; local_s_portd = 2153; - remote_s_portc = 500; remote_s_portd = 2153; # ------- SCTP definitions diff --git a/ci-scripts/conf_files/gnb-du.sa.band1.52prb.usrpb210.conf b/ci-scripts/conf_files/gnb-du.sa.band1.52prb.usrpb210.conf index 2af3185ffc..50bed1fb5d 100644 --- a/ci-scripts/conf_files/gnb-du.sa.band1.52prb.usrpb210.conf +++ b/ci-scripts/conf_files/gnb-du.sa.band1.52prb.usrpb210.conf @@ -163,9 +163,7 @@ MACRLCs = ( tr_n_preference = "f1"; local_n_address = "127.0.0.5"; remote_n_address = "127.0.0.4"; - local_n_portc = 500; local_n_portd = 2153; - remote_n_portc = 501; remote_n_portd = 2153; pusch_TargetSNRx10 = 200; pucch_TargetSNRx10 = 200; diff --git a/ci-scripts/conf_files/gnb-du.sa.band78.106prb.rfsim.conf b/ci-scripts/conf_files/gnb-du.sa.band78.106prb.rfsim.conf index fce5e6dc6e..328efe1b90 100644 --- a/ci-scripts/conf_files/gnb-du.sa.band78.106prb.rfsim.conf +++ b/ci-scripts/conf_files/gnb-du.sa.band78.106prb.rfsim.conf @@ -168,9 +168,7 @@ MACRLCs = ( tr_n_preference = "f1"; local_n_address = "192.168.71.171"; remote_n_address = "192.168.71.150"; - local_n_portc = 500; local_n_portd = 2153; - remote_n_portc = 501; remote_n_portd = 2153; pusch_TargetSNRx10 = 200; pucch_TargetSNRx10 = 200; diff --git a/ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf b/ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf index 66e721ef84..86ba733344 100644 --- a/ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf +++ b/ci-scripts/conf_files/gnb-du.sa.band78.106prb.usrpb200.conf @@ -204,9 +204,7 @@ MACRLCs = ( tr_n_preference = "f1"; local_n_address = "127.0.0.5"; remote_n_address = "127.0.0.4"; - local_n_portc = 500; local_n_portd = 2153; - remote_n_portc = 501; remote_n_portd = 2153; pusch_TargetSNRx10 = 200; pucch_TargetSNRx10 = 200; diff --git a/cmake_targets/tools/MODULES/FindSCTP.cmake b/cmake_targets/tools/MODULES/FindSCTP.cmake deleted file mode 100755 index de8e878a99..0000000000 --- a/cmake_targets/tools/MODULES/FindSCTP.cmake +++ /dev/null @@ -1,46 +0,0 @@ -# - Try to find SCTP library and headers -# Once done, this will define -# -# SCTP_FOUND - system has SCTP -# SCTP_INCLUDE_DIR - the SCTP include directories -# SCTP_LIBRARIES - link these to use SCTP - -if (SCTP_INCLUDE_DIR AND SCTP_LIBRARIES) - set(SCTP_FIND_QUIETLY TRUE) -endif (SCTP_INCLUDE_DIR AND SCTP_LIBRARIES) - -# Include dir -find_path(SCTP_INCLUDE_DIR - NAMES netinet/sctp.h -) - -# Library -find_library(SCTP_LIBRARY - NAMES sctp -) - -# Set the include dir variables and the libraries and let libfind_process do the rest. -# NOTE: Singular variables for this library, plural for libraries this this lib depends on. -#set(SCTP_PROCESS_INCLUDES SCTP_INCLUDE_DIR) -#set(SCTP_PROCESS_LIBS SCTP_LIBRARY) -#libfind_process(SCTP) - - -# handle the QUIETLY and REQUIRED arguments and set SCTP_FOUND to TRUE if -# all listed variables are TRUE -INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(SCTP DEFAULT_MSG SCTP_LIBRARY SCTP_INCLUDE_DIR) - -# If we successfully found the sctp library then add the library to the -# SCTP_LIBRARIES cmake variable otherwise set SCTP_LIBRARIES to nothing. -IF(SCTP_FOUND) - SET( SCTP_LIBRARIES ${SCTP_LIBRARY} ) -ELSE(SCTP_FOUND) - SET( SCTP_LIBRARIES ) -ENDIF(SCTP_FOUND) - - -# Lastly make it so that the SCTP_LIBRARY and SCTP_INCLUDE_DIR variables -# only show up under the advanced options in the gui cmake applications. -MARK_AS_ADVANCED( SCTP_LIBRARY SCTP_INCLUDE_DIR ) - diff --git a/cmake_targets/tools/MODULES/Findsctp.cmake b/cmake_targets/tools/MODULES/Findsctp.cmake new file mode 100644 index 0000000000..44e3ee6a2b --- /dev/null +++ b/cmake_targets/tools/MODULES/Findsctp.cmake @@ -0,0 +1,64 @@ +# - Try to find SCTP library and headers +# +# Imported Targets +# ^^^^^^^^^^^^^^^^ +# +# This module provides the following imported targets, if found: +# +# ``sctp::sctp`` +# The SCTP library +# +# Result Variables +# ^^^^^^^^^^^^^^^^ +# +# this will define +# +# sctp_FOUND - system has SCTP +# sctp_INCLUDE_DIRS - the SCTP include directories +# sctp_LIBRARIES - link these to use SCTP + +# Include dir +find_path(sctp_INCLUDE_DIR + NAMES netinet/sctp.h +) + +# Library +find_library(sctp_LIBRARY + NAMES sctp +) + +# Set the include dir variables and the libraries and let libfind_process do the rest. +# NOTE: Singular variables for this library, plural for libraries this this lib depends on. +#set(sctp_PROCESS_INCLUDES sctp_INCLUDE_DIR) +#set(sctp_PROCESS_LIBS sctp_LIBRARY) +#libfind_process(sctp) + + +# handle the QUIETLY and REQUIRED arguments and set sctp_FOUND to TRUE if +# all listed variables are TRUE +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(sctp + FOUND_VAR sctp_FOUND + REQUIRED_VARS + sctp_LIBRARY + sctp_INCLUDE_DIR +) + +# If we successfully found the sctp library then add the library to the +# sctp_LIBRARIES cmake variable otherwise set sctp_LIBRARIES to nothing. +if(sctp_FOUND) + set(sctp_LIBRARIES ${sctp_LIBRARY}) + set(sctp_INCLUDE_DIRS ${sctp_INCLUDE_DIR}) +endif() + +if(sctp_FOUND AND NOT TARGET sctp::sctp) + add_library(sctp::sctp UNKNOWN IMPORTED) + set_target_properties(sctp::sctp PROPERTIES + IMPORTED_LOCATION "${sctp_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${sctp_INCLUDE_DIRS}" + ) +endif() + +# Lastly make it so that the SCTP_LIBRARY and SCTP_INCLUDE_DIR variables +# only show up under the advanced options in the gui cmake applications. +mark_as_advanced(SCTP_LIBRARY SCTP_INCLUDE_DIR) diff --git a/common/ran_context.h b/common/ran_context.h index e009d0987f..91baa78d84 100644 --- a/common/ran_context.h +++ b/common/ran_context.h @@ -46,7 +46,6 @@ struct gNB_RRC_INST_s; struct eNB_MAC_INST_s; struct eNB_MAC_INST_NB_IoT_s; struct gNB_MAC_INST_s; -struct gtpv1u_data_s; struct RU_t_s; typedef struct { @@ -100,8 +99,6 @@ typedef struct { struct eNB_MAC_INST_NB_IoT_s **nb_iot_mac; /// NR MAC context variables struct gNB_MAC_INST_s **nrmac; - /// GTPu descriptor - struct gtpv1u_data_s *gtpv1u_data_g; /// RU descriptors. These describe what each radio unit is supposed to do and contain the necessary functions for fronthaul interfaces struct RU_t_s **ru; /// Mask to indicate fronthaul setup status of RU (hard-limit to 64 RUs) diff --git a/doc/F1AP/F1-design.md b/doc/F1AP/F1-design.md index b96f29c149..34c48a640c 100644 --- a/doc/F1AP/F1-design.md +++ b/doc/F1AP/F1-design.md @@ -221,9 +221,7 @@ In the CU file: ignored, but we recommend to put `0.0.0.0` ("any") - Ports should match the ones in the DU config, but for simplicity and standards-conformity, simply set all to 2152: - - `local_s_portc` in CU should match `remote_n_portc` in DU - `local_s_portd` in CU should match `remote_n_portd` in DU - - `remote_s_portc` in CU should match `local_n_portc` in DU - `remote_s_portd` in CU should match `local_n_portd` in DU In the DU file: diff --git a/doc/README.md b/doc/README.md index 756294fb5d..27c2688673 100644 --- a/doc/README.md +++ b/doc/README.md @@ -130,6 +130,8 @@ The other SDRs (AW2S, LimeSDR, ...) have no READMEs. # Testing - [UnitTests.md](./UnitTests.md) explains the unit testing setup +- Component tests are under `tests/`. Currently, there is a simple CU-UP + tester, see the corresponding [README.md](../tests/nr-cuup/README.md). - [TESTBenches.md](./TESTBenches.md) lists the CI setup and links to pipelines # Developer tools diff --git a/openair2/COMMON/gtpv1_u_messages_def.h b/openair2/COMMON/gtpv1_u_messages_def.h index 19001b883e..6fad3df938 100644 --- a/openair2/COMMON/gtpv1_u_messages_def.h +++ b/openair2/COMMON/gtpv1_u_messages_def.h @@ -25,4 +25,3 @@ MESSAGE_DEF(GTPV1U_ENB_DATA_FORWARDING_IND, MESSAGE_PRIORITY_MED, gtpv1u_enb_dat MESSAGE_DEF(GTPV1U_ENB_END_MARKER_REQ, MESSAGE_PRIORITY_MED, gtpv1u_enb_end_marker_req_t, Gtpv1uEndMarkerReq) MESSAGE_DEF(GTPV1U_ENB_END_MARKER_IND, MESSAGE_PRIORITY_MED, gtpv1u_enb_end_marker_ind_t, Gtpv1uEndMarkerInd) MESSAGE_DEF(GTPV1U_REQ, MESSAGE_PRIORITY_MED, Gtpv1uReq, gtpv1uReq) -MESSAGE_DEF(GTPV1U_DU_BUFFER_REPORT_REQ, MESSAGE_PRIORITY_MED, gtpv1u_DU_buffer_report_req_t, NRGtpv1uBufferReportReq) diff --git a/openair2/COMMON/gtpv1_u_messages_types.h b/openair2/COMMON/gtpv1_u_messages_types.h index 30471c9c4f..05ca7d7a74 100644 --- a/openair2/COMMON/gtpv1_u_messages_types.h +++ b/openair2/COMMON/gtpv1_u_messages_types.h @@ -37,8 +37,6 @@ #define GTPV1U_REQ(mSGpTR) (mSGpTR)->ittiMsg.gtpv1uReq -#define GTPV1U_DU_BUFFER_REPORT_REQ(mSGpTR) (mSGpTR)->ittiMsg.NRGtpv1uBufferReportReq - #define GTPV1U_ALL_TUNNELS_TEID (teid_t)0xFFFFFFFF typedef struct gtpv1u_enb_create_x2u_tunnel_req_s { @@ -188,11 +186,4 @@ typedef struct gtpv1u_gnb_delete_tunnel_resp_s { teid_t gnb_NGu_teid; ///< local NGU Tunnel Endpoint Identifier to be deleted } gtpv1u_gnb_delete_tunnel_resp_t; - -typedef struct gtpv1u_DU_buffer_report_req_s { - uint32_t buffer_availability; - ue_id_t ue_id; - int pdusession_id; -} gtpv1u_DU_buffer_report_req_t; - #endif /* GTPV1_U_MESSAGES_TYPES_H_ */ diff --git a/openair2/RRC/LTE/rrc_eNB.c b/openair2/RRC/LTE/rrc_eNB.c index 8d0fd2f8ed..90b2d3b86f 100644 --- a/openair2/RRC/LTE/rrc_eNB.c +++ b/openair2/RRC/LTE/rrc_eNB.c @@ -7721,9 +7721,6 @@ void *rrc_enb_process_itti_msg(void *notUsed) { case X2AP_HANDOVER_REQ_ACK: { struct rrc_eNB_ue_context_s *ue_context_p = NULL; - x2ap_handover_req_ack_t *x2ap_handover_req_ack = NULL; - hashtable_rc_t hash_rc = HASH_TABLE_KEY_NOT_EXISTS; - gtpv1u_ue_data_t *gtpv1u_ue_data_p = NULL; ue_context_p = rrc_eNB_get_ue_context(RC.rrc[instance], X2AP_HANDOVER_REQ_ACK(msg_p).rnti); if (ue_context_p == NULL) { @@ -7738,40 +7735,6 @@ void *rrc_enb_process_itti_msg(void *notUsed) { if (ue_context_p->ue_context.handover_info->state != HO_REQUEST) abort(); - hash_rc = hashtable_get(RC.gtpv1u_data_g->ue_mapping, ue_context_p->ue_context.rnti, (void **)>pv1u_ue_data_p); - - /* set target enb gtp teid */ - if (hash_rc == HASH_TABLE_KEY_NOT_EXISTS) { - LOG_E(RRC, "X2AP_HANDOVER_REQ_ACK func(), hashtable_get failed: while getting ue rnti %x in hashtable ue_mapping\n", ue_context_p->ue_context.rnti); - } else { - uint8_t nb_e_rabs_tobesetup = 0; - ebi_t eps_bearer_id = 0; - int ip_offset = 0; - in_addr_t in_addr; - x2ap_handover_req_ack = &X2AP_HANDOVER_REQ_ACK(msg_p); - nb_e_rabs_tobesetup = x2ap_handover_req_ack->nb_e_rabs_tobesetup; - ue_context_p->ue_context.nb_x2u_e_rabs = nb_e_rabs_tobesetup; - - for (int i = 0; i < nb_e_rabs_tobesetup; i++) { - ip_offset = 0; - eps_bearer_id = x2ap_handover_req_ack->e_rabs_tobesetup[i].e_rab_id; - ue_context_p->ue_context.enb_gtp_x2u_ebi[i] = eps_bearer_id; - ue_context_p->ue_context.enb_gtp_x2u_teid[i] = x2ap_handover_req_ack->e_rabs_tobesetup[i].gtp_teid; - gtpv1u_ue_data_p->bearers[eps_bearer_id - GTPV1U_BEARER_OFFSET].teid_teNB = x2ap_handover_req_ack->e_rabs_tobesetup[i].gtp_teid; - - if ((x2ap_handover_req_ack->e_rabs_tobesetup[i].eNB_addr.length == 4) || (x2ap_handover_req_ack->e_rabs_tobesetup[i].eNB_addr.length == 20)) { - in_addr = *((in_addr_t *)x2ap_handover_req_ack->e_rabs_tobesetup[i].eNB_addr.buffer); - ip_offset = 4; - gtpv1u_ue_data_p->bearers[eps_bearer_id - GTPV1U_BEARER_OFFSET].tenb_ip_addr = in_addr; - ue_context_p->ue_context.enb_gtp_x2u_addrs[i] = x2ap_handover_req_ack->e_rabs_tobesetup[i].eNB_addr; - } - - if ((x2ap_handover_req_ack->e_rabs_tobesetup[i].eNB_addr.length == 16) || (x2ap_handover_req_ack->e_rabs_tobesetup[i].eNB_addr.length == 20)) { - memcpy(gtpv1u_ue_data_p->bearers[eps_bearer_id - GTPV1U_BEARER_OFFSET].tenb_ip6_addr.s6_addr, &x2ap_handover_req_ack->e_rabs_tobesetup[i].eNB_addr.buffer[ip_offset], 16); - } - } - } - rrc_eNB_process_handoverCommand(instance, ue_context_p, &X2AP_HANDOVER_REQ_ACK(msg_p)); ue_context_p->ue_context.handover_info->state = HO_PREPARE; break; diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index 58be5ed638..fec0f035eb 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -11,6 +11,7 @@ extern "C" { #include #include "common/platform_types.h" +#include "common/utils/system.h" #include #include "common/utils/LOG/log.h" #include @@ -25,91 +26,89 @@ extern "C" { #pragma pack(1) typedef struct Gtpv1uMsgHeader { - uint8_t PN:1; - uint8_t S:1; - uint8_t E:1; - uint8_t spare:1; - uint8_t PT:1; - uint8_t version:3; + uint8_t PN: 1; + uint8_t S: 1; + uint8_t E: 1; + uint8_t spare: 1; + uint8_t PT: 1; + uint8_t version: 3; uint8_t msgType; uint16_t msgLength; teid_t teid; } __attribute__((packed)) Gtpv1uMsgHeaderT; -//TS 38.425, Figure 5.5.2.2-1 +// TS 38.425, Figure 5.5.2.2-1 typedef struct DlDataDeliveryStatus_flags { - uint8_t LPR:1; //Lost packet report - uint8_t FFI:1; //Final Frame Ind - uint8_t deliveredPdcpSn:1; //Highest Delivered NR PDCP SN Ind - uint8_t transmittedPdcpSn:1; //Highest Transmitted NR PDCP SN Ind - uint8_t pduType:4; //PDU type - uint8_t CR:1; //Cause Report - uint8_t deliveredReTxPdcpSn:1; //Delivered retransmitted NR PDCP SN Ind - uint8_t reTxPdcpSn:1; //Retransmitted NR PDCP SN Ind - uint8_t DRI:1; //Data Rate Indication - uint8_t deliveredPdcpSnRange:1; //Delivered NR PDCP SN Range Ind - uint8_t spare:3; - uint32_t drbBufferSize; //Desired buffer size for the data radio bearer + uint8_t LPR: 1; // Lost packet report + uint8_t FFI: 1; // Final Frame Ind + uint8_t deliveredPdcpSn: 1; // Highest Delivered NR PDCP SN Ind + uint8_t transmittedPdcpSn: 1; // Highest Transmitted NR PDCP SN Ind + uint8_t pduType: 4; // PDU type + uint8_t CR: 1; // Cause Report + uint8_t deliveredReTxPdcpSn: 1; // Delivered retransmitted NR PDCP SN Ind + uint8_t reTxPdcpSn: 1; // Retransmitted NR PDCP SN Ind + uint8_t DRI: 1; // Data Rate Indication + uint8_t deliveredPdcpSnRange: 1; // Delivered NR PDCP SN Range Ind + uint8_t spare: 3; + uint32_t drbBufferSize; // Desired buffer size for the data radio bearer } __attribute__((packed)) DlDataDeliveryStatus_flagsT; typedef struct Gtpv1uMsgHeaderOptFields { uint8_t seqNum1Oct; uint8_t seqNum2Oct; uint8_t NPDUNum; - uint8_t NextExtHeaderType; + uint8_t NextExtHeaderType; } __attribute__((packed)) Gtpv1uMsgHeaderOptFieldsT; #define DL_PDU_SESSION_INFORMATION 0 #define UL_PDU_SESSION_INFORMATION 1 - typedef struct PDUSessionContainer { - uint8_t spare:4; - uint8_t PDU_type:4; - uint8_t QFI:6; - uint8_t Reflective_QoS_activation:1; - uint8_t Paging_Policy_Indicator:1; +typedef struct PDUSessionContainer { + uint8_t spare: 4; + uint8_t PDU_type: 4; + uint8_t QFI: 6; + uint8_t Reflective_QoS_activation: 1; + uint8_t Paging_Policy_Indicator: 1; } __attribute__((packed)) PDUSessionContainerT; typedef struct Gtpv1uExtHeader { uint8_t ExtHeaderLen; PDUSessionContainerT pdusession_cntr; uint8_t NextExtHeaderType; -}__attribute__((packed)) Gtpv1uExtHeaderT; +} __attribute__((packed)) Gtpv1uExtHeaderT; #pragma pack() // TS 29.281, fig 5.2.1-3 -#define PDU_SESSION_CONTAINER (0x85) -#define NR_RAN_CONTAINER (0x84) +#define PDU_SESSION_CONTAINER (0x85) +#define NR_RAN_CONTAINER (0x84) // TS 29.281, 5.2.1 -#define EXT_HDR_LNTH_OCTET_UNITS (4) -#define NO_MORE_EXT_HDRS (0) +#define EXT_HDR_LNTH_OCTET_UNITS (4) +#define NO_MORE_EXT_HDRS (0) // TS 29.060, table 7.1 defines the possible message types // here are all the possible messages (3GPP R16) -#define GTP_ECHO_REQ (1) -#define GTP_ECHO_RSP (2) -#define GTP_ERROR_INDICATION (26) -#define GTP_SUPPORTED_EXTENSION_HEADER_INDICATION (31) -#define GTP_END_MARKER (254) -#define GTP_GPDU (255) +#define GTP_ECHO_REQ (1) +#define GTP_ECHO_RSP (2) +#define GTP_ERROR_INDICATION (26) +#define GTP_SUPPORTED_EXTENSION_HEADER_INDICATION (31) +#define GTP_END_MARKER (254) +#define GTP_GPDU (255) +// GTP bearer context: for sending data typedef struct gtpv1u_bearer_s { - /* TEID used in dl and ul */ - teid_t teid_incoming; ///< eNB TEID - teid_t teid_outgoing; ///< Remote TEID - in_addr_t outgoing_ip_addr; - struct in6_addr outgoing_ip6_addr; - tcp_udp_port_t outgoing_port; - uint16_t seqNum; - uint8_t npduNum; + int sock_fd; + struct sockaddr_storage ip; + teid_t teid_incoming; + teid_t teid_outgoing; + uint16_t seqNum; + uint8_t npduNum; int outgoing_qfi; } gtpv1u_bearer_t; typedef struct { map bearers; - teid_t outgoing_teid; } teidData_t; typedef struct { @@ -121,34 +120,43 @@ typedef struct { int pdusession_id; } ueidData_t; +typedef struct { + int h; + pthread_t t; +} gtpThread_t; + class gtpEndPoint { public: openAddr_t addr; uint8_t foundAddr[20]; int foundAddrLen; int ipVersion; + gtpThread_t thrData; map ue2te_mapping; // we use the same port number for source and destination address // this allow using non standard gtp port number (different from 2152) // and so, for example tu run 4G and 5G cores on one system - tcp_udp_port_t get_dstport() { + tcp_udp_port_t get_dstport() + { return (tcp_udp_port_t)atol(addr.destinationService); } }; class gtpEndPoints { public: - pthread_mutex_t gtp_lock=PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_t gtp_lock = PTHREAD_MUTEX_INITIALIZER; // the instance id will be the Linux socket handler, as this is uniq map instances; map te2ue_mapping; - gtpEndPoints() { + gtpEndPoints() + { unsigned int seed; fill_random(&seed, sizeof(seed)); srandom(seed); } - ~gtpEndPoints() { + ~gtpEndPoints() + { // automatically close all sockets on quit for (const auto &p : instances) close(p.first); @@ -158,7 +166,8 @@ class gtpEndPoints { static gtpEndPoints globGtp; // note TEid 0 is reserved for specific usage: echo req/resp, error and supported extensions -static teid_t gtpv1uNewTeid(void) { +static teid_t gtpv1uNewTeid(void) +{ #ifdef GTPV1U_LINEAR_TEID_ALLOCATION g_gtpv1u_teid = g_gtpv1u_teid + 1; return g_gtpv1u_teid; @@ -167,84 +176,78 @@ static teid_t gtpv1uNewTeid(void) { #endif } -instance_t legacyInstanceMapping=0; -#define compatInst(a) ((a)==0 || (a)==INSTANCE_DEFAULT ? legacyInstanceMapping:a) +instance_t legacyInstanceMapping = 0; +#define compatInst(a) ((a) == 0 || (a) == INSTANCE_DEFAULT ? legacyInstanceMapping : a) -#define getInstRetVoid(insT) \ - auto instChk=globGtp.instances.find(compatInst(insT)); \ - if (instChk == globGtp.instances.end()) { \ - LOG_E(GTPU,"try to get a gtp-u not existing output\n"); \ - pthread_mutex_unlock(&globGtp.gtp_lock); \ - return; \ - } \ - gtpEndPoint * inst=&instChk->second; - -#define getInstRetInt(insT) \ - auto instChk=globGtp.instances.find(compatInst(insT)); \ - if (instChk == globGtp.instances.end()) { \ - LOG_E(GTPU,"try to get a gtp-u not existing output\n"); \ - pthread_mutex_unlock(&globGtp.gtp_lock); \ - return GTPNOK; \ - } \ - gtpEndPoint * inst=&instChk->second; +#define getInstRetVoid(insT) \ + auto instChk = globGtp.instances.find(compatInst(insT)); \ + if (instChk == globGtp.instances.end()) { \ + LOG_E(GTPU, "try to get a gtp-u not existing output\n"); \ + pthread_mutex_unlock(&globGtp.gtp_lock); \ + return; \ + } \ + gtpEndPoint *inst = &instChk->second; +#define getInstRetInt(insT) \ + auto instChk = globGtp.instances.find(compatInst(insT)); \ + if (instChk == globGtp.instances.end()) { \ + LOG_E(GTPU, "try to get a gtp-u not existing output\n"); \ + pthread_mutex_unlock(&globGtp.gtp_lock); \ + return GTPNOK; \ + } \ + gtpEndPoint *inst = &instChk->second; -#define getUeRetVoid(insT, Ue) \ - auto ptrUe=insT->ue2te_mapping.find(Ue); \ - \ - if ( ptrUe==insT->ue2te_mapping.end() ) { \ +#define getUeRetVoid(insT, Ue) \ + auto ptrUe = insT->ue2te_mapping.find(Ue); \ + \ + if (ptrUe == insT->ue2te_mapping.end()) { \ LOG_E(GTPU, "[%ld] %s failed: while getting ue id %ld in hashtable ue_mapping\n", instance, __func__, ue_id); \ - pthread_mutex_unlock(&globGtp.gtp_lock); \ - return; \ + pthread_mutex_unlock(&globGtp.gtp_lock); \ + return; \ } - -#define getUeRetInt(insT, Ue) \ - auto ptrUe=insT->ue2te_mapping.find(Ue); \ - \ - if ( ptrUe==insT->ue2te_mapping.end() ) { \ + +#define getUeRetInt(insT, Ue) \ + auto ptrUe = insT->ue2te_mapping.find(Ue); \ + \ + if (ptrUe == insT->ue2te_mapping.end()) { \ LOG_E(GTPU, "[%ld] %s failed: while getting ue id %ld in hashtable ue_mapping\n", instance, __func__, ue_id); \ - pthread_mutex_unlock(&globGtp.gtp_lock); \ - return GTPNOK; \ + pthread_mutex_unlock(&globGtp.gtp_lock); \ + return GTPNOK; \ } - + #define HDR_MAX 256 // 256 is supposed to be larger than any gtp header -static int gtpv1uCreateAndSendMsg(int h, - uint32_t peerIp, - uint16_t peerPort, +static int gtpv1uCreateAndSendMsg(gtpv1u_bearer_t *bearer, int msgType, - teid_t teid, uint8_t *Msg, int msgLen, bool seqNumFlag, bool npduNumFlag, - int seqNum, - int npduNum, int extHdrType, uint8_t *extensionHeader_buffer, - uint8_t extensionHeader_length) { - LOG_D(GTPU, "Peer IP:%u peer port:%u outgoing teid:%u \n", peerIp, peerPort, teid); + uint8_t extensionHeader_length) +{ DevAssert(msgLen + HDR_MAX < 65536); // maximum size of UDP packet - uint8_t buffer[msgLen+HDR_MAX]; - uint8_t *curPtr=buffer; - Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *)buffer ; + uint8_t buffer[msgLen + HDR_MAX]; + uint8_t *curPtr = buffer; + Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *)buffer; // N should be 0 for us (it was used only in 2G and 3G) - msgHdr->PN=npduNumFlag; - msgHdr->S=seqNumFlag; + msgHdr->PN = npduNumFlag; + msgHdr->S = seqNumFlag; msgHdr->E = extHdrType != NO_MORE_EXT_HDRS; - msgHdr->spare=0; - //PT=0 is for GTP' TS 32.295 (charging) - msgHdr->PT=1; - msgHdr->version=1; - msgHdr->msgType=msgType; - msgHdr->teid=htonl(teid); + msgHdr->spare = 0; + // PT=0 is for GTP' TS 32.295 (charging) + msgHdr->PT = 1; + msgHdr->version = 1; + msgHdr->msgType = msgType; + msgHdr->teid = htonl(bearer->teid_outgoing); - curPtr+=sizeof(Gtpv1uMsgHeaderT); + curPtr += sizeof(Gtpv1uMsgHeaderT); if (seqNumFlag || (extHdrType != NO_MORE_EXT_HDRS) || npduNumFlag) { - *(uint16_t *)curPtr = seqNumFlag ? seqNum : 0x0000; - curPtr+=sizeof(uint16_t); - *(uint8_t *)curPtr = npduNumFlag ? npduNum : 0x00; + *(uint16_t *)curPtr = seqNumFlag ? bearer->seqNum : 0x0000; + curPtr += sizeof(uint16_t); + *(uint8_t *)curPtr = npduNumFlag ? bearer->npduNum : 0x00; curPtr++; *(uint8_t *)curPtr = extHdrType; curPtr++; @@ -255,7 +258,10 @@ static int gtpv1uCreateAndSendMsg(int h, if (extensionHeader_length > 0) { memcpy(curPtr, extensionHeader_buffer, extensionHeader_length); curPtr += extensionHeader_length; - LOG_D(GTPU, "Extension Header for DDD added. The length is: %d, extension header type is: %x \n", extensionHeader_length, *((uint8_t *)(buffer + 11))); + LOG_D(GTPU, + "Extension Header for DDD added. The length is: %d, extension header type is: %x \n", + extensionHeader_length, + *((uint8_t *)(buffer + 11))); extHdrType = extensionHeader_buffer[extensionHeader_length - 1]; LOG_D(GTPU, "Next extension header type is: %x \n", *((uint8_t *)(buffer + 11))); } else { @@ -263,28 +269,34 @@ static int gtpv1uCreateAndSendMsg(int h, } } - if (Msg!= NULL){ + if (Msg != NULL) { memcpy(curPtr, Msg, msgLen); - curPtr+=msgLen; + curPtr += msgLen; } - msgHdr->msgLength = htons(curPtr-(buffer+sizeof(Gtpv1uMsgHeaderT))); - AssertFatal(curPtr-(buffer+msgLen) < HDR_MAX, "fixed max size of all headers too short"); - // Fix me: add IPv6 support, using flag ipVersion - struct sockaddr_in to= {0}; - to.sin_family = AF_INET; - to.sin_port = htons(peerPort); - to.sin_addr.s_addr = peerIp ; - LOG_D(GTPU,"sending packet size: %ld to %s\n",curPtr-buffer, inet_ntoa(to.sin_addr) ); - int ret; + msgHdr->msgLength = htons(curPtr - (buffer + sizeof(Gtpv1uMsgHeaderT))); + AssertFatal(curPtr - (buffer + msgLen) < HDR_MAX, "fixed max size of all headers too short"); - if ((ret=sendto(h, (void *)buffer, curPtr-buffer, 0,(struct sockaddr *)&to, sizeof(to) )) != curPtr-buffer ) { - LOG_E(GTPU, "[SD %d] Failed to send data to " IPV4_ADDR " on port %d, buffer size %lu, ret: %d, errno: %d\n", - h, IPV4_ADDR_FORMAT(peerIp), peerPort, curPtr-buffer, ret, errno); + // Fix me: add IPv6 support + DevAssert(bearer->ip.ss_family == AF_INET); + struct sockaddr_in *to = (struct sockaddr_in *)&bearer->ip; + LOG_D(GTPU, + "Peer IP:" IPV4_ADDR " port:%u outgoing teid:%x\n", + IPV4_ADDR_FORMAT(to->sin_addr.s_addr), + htons(to->sin_port), + bearer->teid_outgoing); + int ret = sendto(bearer->sock_fd, buffer, curPtr - buffer, 0, (struct sockaddr *)to, sizeof(*to)); + if (ret != curPtr - buffer) { + LOG_E(GTPU, + "[SD %d] Failed to send data buffer size %lu, ret: %d, errno: %d\n", + bearer->sock_fd, + curPtr - buffer, + ret, + errno); return GTPNOK; } - return !GTPNOK; + return !GTPNOK; } void gtpv1uSendDirect(instance_t instance, @@ -324,203 +336,129 @@ void gtpv1uSendDirect(instance_t instance, ptr2->second.npduNum++; // copy to release the mutex - gtpv1u_bearer_t tmp = ptr2->second; + gtpv1u_bearer_t bearer = ptr2->second; pthread_mutex_unlock(&globGtp.gtp_lock); - if (tmp.outgoing_qfi != -1) { - Gtpv1uExtHeaderT ext = {0}; + Gtpv1uExtHeaderT *opt_ext = NULL; + Gtpv1uExtHeaderT ext = {0}; + if (bearer.outgoing_qfi != -1) { + opt_ext = &ext; ext.ExtHeaderLen = 1; // in quad bytes EXT_HDR_LNTH_OCTET_UNITS ext.pdusession_cntr.spare = 0; ext.pdusession_cntr.PDU_type = UL_PDU_SESSION_INFORMATION; - ext.pdusession_cntr.QFI = tmp.outgoing_qfi; + ext.pdusession_cntr.QFI = bearer.outgoing_qfi; ext.pdusession_cntr.Reflective_QoS_activation = false; ext.pdusession_cntr.Paging_Policy_Indicator = false; ext.NextExtHeaderType = NO_MORE_EXT_HDRS; - gtpv1uCreateAndSendMsg(compatInst(instance), - tmp.outgoing_ip_addr, - tmp.outgoing_port, - GTP_GPDU, - tmp.teid_outgoing, - buf, - len, - seqNumFlag, - npduNumFlag, - tmp.seqNum, - tmp.npduNum, - PDU_SESSION_CONTAINER, - (uint8_t *)&ext, - sizeof(ext)); - } else { - gtpv1uCreateAndSendMsg(compatInst(instance), - tmp.outgoing_ip_addr, - tmp.outgoing_port, - GTP_GPDU, - tmp.teid_outgoing, - buf, - len, - seqNumFlag, - npduNumFlag, - tmp.seqNum, - tmp.npduNum, - NO_MORE_EXT_HDRS, - NULL, - 0); - } -} - -static void fillDlDeliveryStatusReport(extensionHeader_t *extensionHeader, uint32_t RLC_buffer_availability, uint32_t NR_PDCP_PDU_SN){ - - extensionHeader->buffer[0] = (1+sizeof(DlDataDeliveryStatus_flagsT)+(NR_PDCP_PDU_SN>0?3:0)+(NR_PDCP_PDU_SN>0?1:0)+1)/4; - DlDataDeliveryStatus_flagsT DlDataDeliveryStatus; - DlDataDeliveryStatus.deliveredPdcpSn = 0; - DlDataDeliveryStatus.transmittedPdcpSn= NR_PDCP_PDU_SN>0?1:0; - DlDataDeliveryStatus.pduType = 1; - DlDataDeliveryStatus.drbBufferSize = htonl(RLC_buffer_availability); - memcpy(extensionHeader->buffer+1, &DlDataDeliveryStatus, sizeof(DlDataDeliveryStatus_flagsT)); - uint8_t offset = sizeof(DlDataDeliveryStatus_flagsT)+1; - - if(NR_PDCP_PDU_SN>0){ - extensionHeader->buffer[offset] = (NR_PDCP_PDU_SN >> 16) & 0xff; - extensionHeader->buffer[offset+1] = (NR_PDCP_PDU_SN >> 8) & 0xff; - extensionHeader->buffer[offset+2] = NR_PDCP_PDU_SN & 0xff; - LOG_D(GTPU, "Octets reporting NR_PDCP_PDU_SN, extensionHeader->buffer[offset]: %u, extensionHeader->buffer[offset+1]:%u, extensionHeader->buffer[offset+2]:%u \n", extensionHeader->buffer[offset], extensionHeader->buffer[offset+1],extensionHeader->buffer[offset+2]); - extensionHeader->buffer[offset+3] = 0x00; //Padding octet - offset = offset+3; - } - extensionHeader->buffer[offset] = 0x00; //No more extension headers - /*Total size of DDD_status PDU = size of mandatory part + - * 3 octets for highest transmitted/delivered PDCP SN + - * 1 octet for padding + 1 octet for next extension header type, - * according to TS 38.425: Fig. 5.5.2.2-1 and section 5.5.3.24*/ - extensionHeader->length = 1+sizeof(DlDataDeliveryStatus_flagsT)+ - (NR_PDCP_PDU_SN>0?3:0)+ - (NR_PDCP_PDU_SN>0?1:0)+1; -} - -static void gtpv1uSendDlDeliveryStatus(instance_t instance, gtpv1u_DU_buffer_report_req_t *req){ - ue_id_t ue_id=req->ue_id; - int bearer_id=req->pdusession_id; - pthread_mutex_lock(&globGtp.gtp_lock); - getInstRetVoid(compatInst(instance)); - getUeRetVoid(inst, ue_id); - - auto ptr2=ptrUe->second.bearers.find(bearer_id); - - if ( ptr2 == ptrUe->second.bearers.end() ) { - LOG_D(GTPU,"GTP-U instance: %ld sending a packet to a non existant UE ID:RAB: %lu/%x\n", instance, ue_id, bearer_id); - pthread_mutex_unlock(&globGtp.gtp_lock); - return; } - extensionHeader_t *extensionHeader; - extensionHeader = (extensionHeader_t *) calloc(1, sizeof(extensionHeader_t)); - fillDlDeliveryStatusReport(extensionHeader, req->buffer_availability,0); - - LOG_I(GTPU,"[%ld] GTP-U sending DL Data Delivery status to UE ID:RAB:teid %lu/%x/%x, oldseq %d, oldnum %d\n", - instance, ue_id, bearer_id,ptr2->second.teid_outgoing, ptr2->second.seqNum,ptr2->second.npduNum ); - // copy to release the mutex - gtpv1u_bearer_t tmp=ptr2->second; - pthread_mutex_unlock(&globGtp.gtp_lock); - gtpv1uCreateAndSendMsg( - compatInst(instance), tmp.outgoing_ip_addr, tmp.outgoing_port, GTP_GPDU, tmp.teid_outgoing, NULL, 0, false, false, 0, 0, NR_RAN_CONTAINER, extensionHeader->buffer, extensionHeader->length); + DevAssert(compatInst(instance) == bearer.sock_fd); + gtpv1uCreateAndSendMsg(&bearer, + GTP_GPDU, + buf, + len, + seqNumFlag, + npduNumFlag, + opt_ext ? PDU_SESSION_CONTAINER : NO_MORE_EXT_HDRS, + (uint8_t *)opt_ext, + sizeof(*opt_ext)); } static void gtpv1uEndTunnel(instance_t instance, gtpv1u_enb_end_marker_req_t *req) { - ue_id_t ue_id=req->rnti; - int bearer_id=req->rab_id; + ue_id_t ue_id = req->rnti; + int bearer_id = req->rab_id; pthread_mutex_lock(&globGtp.gtp_lock); getInstRetVoid(compatInst(instance)); getUeRetVoid(inst, ue_id); - auto ptr2=ptrUe->second.bearers.find(bearer_id); + auto ptr2 = ptrUe->second.bearers.find(bearer_id); - if ( ptr2 == ptrUe->second.bearers.end() ) { - LOG_E(GTPU,"[%ld] GTP-U sending a packet to a non existant UE:RAB: %lx/%x\n", instance, ue_id, bearer_id); + if (ptr2 == ptrUe->second.bearers.end()) { + LOG_E(GTPU, "[%ld] GTP-U sending a packet to a non existant UE:RAB: %lx/%x\n", instance, ue_id, bearer_id); pthread_mutex_unlock(&globGtp.gtp_lock); return; } - LOG_D(GTPU,"[%ld] sending a end packet packet to UE:RAB:teid %lx/%x/%x\n", - instance, ue_id, bearer_id,ptr2->second.teid_outgoing); - gtpv1u_bearer_t tmp=ptr2->second; + LOG_D(GTPU, + "[%ld] sending a end packet packet to UE:RAB:teid %lx/%x/%x\n", + instance, + ue_id, + bearer_id, + ptr2->second.teid_outgoing); + gtpv1u_bearer_t tmp = ptr2->second; pthread_mutex_unlock(&globGtp.gtp_lock); - Gtpv1uMsgHeaderT msgHdr; + Gtpv1uMsgHeaderT msgHdr; // N should be 0 for us (it was used only in 2G and 3G) - msgHdr.PN=0; - msgHdr.S=0; - msgHdr.E=0; - msgHdr.spare=0; - //PT=0 is for GTP' TS 32.295 (charging) - msgHdr.PT=1; - msgHdr.version=1; - msgHdr.msgType=GTP_END_MARKER; - msgHdr.msgLength=htons(0); - msgHdr.teid=htonl(tmp.teid_outgoing); - // Fix me: add IPv6 support, using flag ipVersion - static struct sockaddr_in to= {0}; - to.sin_family = AF_INET; - to.sin_port = htons(tmp.outgoing_port); - to.sin_addr.s_addr = tmp.outgoing_ip_addr; - char ip4[INET_ADDRSTRLEN]; - //char ip6[INET6_ADDRSTRLEN]; - LOG_D(GTPU,"[%ld] sending end packet to %s\n", instance, inet_ntoa(to.sin_addr) ); + msgHdr.PN = 0; + msgHdr.S = 0; + msgHdr.E = 0; + msgHdr.spare = 0; + // PT=0 is for GTP' TS 32.295 (charging) + msgHdr.PT = 1; + msgHdr.version = 1; + msgHdr.msgType = GTP_END_MARKER; + msgHdr.msgLength = htons(0); + msgHdr.teid = htonl(tmp.teid_outgoing); - if (sendto(compatInst(instance), (void *)&msgHdr, sizeof(msgHdr), 0,(struct sockaddr *)&to, sizeof(to) ) != sizeof(msgHdr)) { - LOG_E(GTPU, - "[%ld] Failed to send data to %s on port %d, buffer size %lu\n", - compatInst(instance), inet_ntop(AF_INET, &tmp.outgoing_ip_addr, ip4, INET_ADDRSTRLEN), tmp.outgoing_port, sizeof(msgHdr)); + // Fix me: add IPv6 support + DevAssert(instance == tmp.sock_fd); + DevAssert(tmp.ip.ss_family == AF_INET); + struct sockaddr_in *to = (struct sockaddr_in *)&tmp.ip; + LOG_D(GTPU, "[%ld] sending end packet to " IPV4_ADDR " port %d\n", instance, IPV4_ADDR_FORMAT(to->sin_addr.s_addr), htons(to->sin_port)); + + ssize_t ret = sendto(tmp.sock_fd, &msgHdr, sizeof(msgHdr), 0, (struct sockaddr *)to, sizeof(*to)); + if (ret != sizeof(msgHdr)) { + LOG_E(GTPU, "[%d] Failed to send data with buffer size %lu: ret %ld errno %d\n", tmp.sock_fd, sizeof(msgHdr), ret, errno); } } -static int udpServerSocket(openAddr_s addr) { +static int udpServerSocket(openAddr_s addr) +{ LOG_I(GTPU, "Initializing UDP for local address %s with port %s\n", addr.originHost, addr.originService); int status; - struct addrinfo hints= {0}, *servinfo, *p; + struct addrinfo hints = {0}, *servinfo, *p; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_PASSIVE; if ((status = getaddrinfo(addr.originHost, addr.originService, &hints, &servinfo)) != 0) { - LOG_E(GTPU,"getaddrinfo error: %s\n", gai_strerror(status)); + LOG_E(GTPU, "getaddrinfo error: %s\n", gai_strerror(status)); return -1; } - int sockfd=-1; + int sockfd = -1; // loop through all the results and bind to the first we can - for(p = servinfo; p != NULL; p = p->ai_next) { - if ((sockfd = socket(p->ai_family, p->ai_socktype, - p->ai_protocol)) == -1) { - LOG_W(GTPU,"socket: %s\n", strerror(errno)); + for (p = servinfo; p != NULL; p = p->ai_next) { + if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { + LOG_W(GTPU, "socket: %s\n", strerror(errno)); continue; } if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { close(sockfd); - LOG_W(GTPU,"bind: %s\n", strerror(errno)); + LOG_W(GTPU, "bind: %s\n", strerror(errno)); continue; } else { // We create the gtp instance on the socket - globGtp.instances[sockfd].addr=addr; + globGtp.instances[sockfd].addr = addr; if (p->ai_family == AF_INET) { - struct sockaddr_in *ipv4=(struct sockaddr_in *)p->ai_addr; - memcpy(globGtp.instances[sockfd].foundAddr, - &ipv4->sin_addr.s_addr, sizeof(ipv4->sin_addr.s_addr)); - globGtp.instances[sockfd].foundAddrLen=sizeof(ipv4->sin_addr.s_addr); - globGtp.instances[sockfd].ipVersion=4; + struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; + memcpy(globGtp.instances[sockfd].foundAddr, &ipv4->sin_addr.s_addr, sizeof(ipv4->sin_addr.s_addr)); + globGtp.instances[sockfd].foundAddrLen = sizeof(ipv4->sin_addr.s_addr); + globGtp.instances[sockfd].ipVersion = 4; break; } else if (p->ai_family == AF_INET6) { - LOG_W(GTPU,"Local address is IP v6\n"); - struct sockaddr_in6 *ipv6=(struct sockaddr_in6 *)p->ai_addr; - memcpy(globGtp.instances[sockfd].foundAddr, - &ipv6->sin6_addr.s6_addr, sizeof(ipv6->sin6_addr.s6_addr)); - globGtp.instances[sockfd].foundAddrLen=sizeof(ipv6->sin6_addr.s6_addr); - globGtp.instances[sockfd].ipVersion=6; + LOG_W(GTPU, "Local address is IP v6\n"); + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; + memcpy(globGtp.instances[sockfd].foundAddr, &ipv6->sin6_addr.s6_addr, sizeof(ipv6->sin6_addr.s6_addr)); + globGtp.instances[sockfd].foundAddrLen = sizeof(ipv6->sin6_addr.s6_addr); + globGtp.instances[sockfd].ipVersion = 6; } else - AssertFatal(false,"Local address is not IPv4 or IPv6"); + AssertFatal(false, "Local address is not IPv4 or IPv6"); } break; // if we get here, we must have connected successfully @@ -530,46 +468,78 @@ static int udpServerSocket(openAddr_s addr) { if (p == NULL) { // looped off the end of the list with no successful bind - LOG_E(GTPU,"failed to bind socket: %s %s \n", addr.originHost, addr.originService); + LOG_E(GTPU, "failed to bind socket: %s %s \n", addr.originHost, addr.originService); return -1; } - int sendbuff = 1000*1000*10; - AssertFatal(0==setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sendbuff, sizeof(sendbuff)),""); - LOG_D(GTPU,"[%d] Created listener for paquets to: %s:%s, send buffer size: %d\n", sockfd, addr.originHost, addr.originService,sendbuff); + int sendbuff = 1000 * 1000 * 10; + AssertFatal(0 == setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sendbuff, sizeof(sendbuff)), ""); + LOG_D(GTPU, + "[%d] Created listener for paquets to: %s:%s, send buffer size: %d\n", + sockfd, + addr.originHost, + addr.originService, + sendbuff); return sockfd; } -instance_t gtpv1Init(openAddr_t context) { +static void* gtpv1uReceiver(void *thr); +instance_t gtpv1Init(openAddr_t context) +{ pthread_mutex_lock(&globGtp.gtp_lock); - int id=udpServerSocket(context); + int id = udpServerSocket(context); - if (id>=0) { - itti_subscribe_event_fd(TASK_GTPV1_U, id); + if (id >= 0) { + LOG_I(GTPU, "Created gtpu instance id: %d\n", id); + getInstRetInt(compatInst(id)); + inst->thrData.h = id; + char name[32]; + snprintf(name, sizeof(name), "GTPrx_%d", id); + threadCreate(&inst->thrData.t, gtpv1uReceiver, &inst->thrData, name, -1, OAI_PRIORITY_RT); } else - LOG_E(GTPU,"can't create GTP-U instance\n"); + LOG_E(GTPU, "can't create GTP-U instance\n"); pthread_mutex_unlock(&globGtp.gtp_lock); - LOG_I(GTPU, "Created gtpu instance id: %d\n", id); return id; } -void GtpuUpdateTunnelOutgoingAddressAndTeid(instance_t instance, ue_id_t ue_id, ebi_t bearer_id, in_addr_t newOutgoingAddr, teid_t newOutgoingTeid) { +void GtpuUpdateTunnelOutgoingAddressAndTeid(instance_t instance, + ue_id_t ue_id, + ebi_t bearer_id, + in_addr_t newOutgoingAddr, + teid_t newOutgoingTeid) +{ pthread_mutex_lock(&globGtp.gtp_lock); getInstRetVoid(compatInst(instance)); getUeRetVoid(inst, ue_id); - auto ptr2=ptrUe->second.bearers.find(bearer_id); + auto ptr2 = ptrUe->second.bearers.find(bearer_id); - if ( ptr2 == ptrUe->second.bearers.end() ) { - LOG_E(GTPU,"[%ld] Update tunnel for a existing ue id %lu, but wrong bearer_id %u\n", instance, ue_id, bearer_id); + if (ptr2 == ptrUe->second.bearers.end()) { + LOG_E(GTPU, "[%ld] Update tunnel for a existing ue id %lu, but wrong bearer_id %u\n", instance, ue_id, bearer_id); pthread_mutex_unlock(&globGtp.gtp_lock); return; } - ptr2->second.outgoing_ip_addr = newOutgoingAddr; + struct sockaddr_in *sockaddr = (struct sockaddr_in *)&ptr2->second.ip; + sockaddr->sin_family = AF_INET; + memcpy(&sockaddr->sin_addr, &newOutgoingAddr, sizeof(newOutgoingAddr)); + AssertFatal(ptr2->second.ip.ss_family == AF_INET, "only IPv4 is supported\n"); ptr2->second.teid_outgoing = newOutgoingTeid; - LOG_I(GTPU, "[%ld] Tunnel Outgoing TEID updated to %x and address to %x\n", instance, ptr2->second.teid_outgoing, ptr2->second.outgoing_ip_addr); + char ip4[INET_ADDRSTRLEN]; + char ip6[INET6_ADDRSTRLEN]; + struct sockaddr_in *sa4 = (struct sockaddr_in *)sockaddr; + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sockaddr; + LOG_I(GTPU, + "[%ld] UE ID %ld: Update tunnel TEID incoming %x outgoing %x to remote IPv4 %s, IPv6 %s, port %d\n", + instance, + ue_id, + ptr2->second.teid_incoming, + ptr2->second.teid_outgoing, + inet_ntop(AF_INET, &sa4->sin_addr, ip4, INET_ADDRSTRLEN), + inet_ntop(AF_INET6, &sa6->sin6_addr, ip6, INET6_ADDRSTRLEN), + ntohs(sa4->sin_port)); + pthread_mutex_unlock(&globGtp.gtp_lock); return; } @@ -581,23 +551,23 @@ teid_t newGtpuCreateTunnel(instance_t instance, teid_t outgoing_teid, int outgoing_qfi, transport_layer_addr_t remoteAddr, - int port, gtpCallback callBack, - gtpCallbackSDAP callBackSDAP) { + gtpCallbackSDAP callBackSDAP) +{ pthread_mutex_lock(&globGtp.gtp_lock); getInstRetInt(compatInst(instance)); - auto it=inst->ue2te_mapping.find(ue_id); + auto it = inst->ue2te_mapping.find(ue_id); - if ( it != inst->ue2te_mapping.end() && it->second.bearers.find(outgoing_bearer_id) != it->second.bearers.end()) { - LOG_W(GTPU,"[%ld] Create a config for a already existing GTP tunnel (ue id %lu)\n", instance, ue_id); + if (it != inst->ue2te_mapping.end() && it->second.bearers.find(outgoing_bearer_id) != it->second.bearers.end()) { + LOG_W(GTPU, "[%ld] Create a config for a already existing GTP tunnel (ue id %lu)\n", instance, ue_id); inst->ue2te_mapping.erase(it); } - teid_t incoming_teid=gtpv1uNewTeid(); + teid_t incoming_teid = gtpv1uNewTeid(); while (globGtp.te2ue_mapping.find(incoming_teid) != globGtp.te2ue_mapping.end()) { LOG_W(GTPU, "[%ld] generated a random Teid that exists, re-generating (%x)\n", instance, incoming_teid); - incoming_teid=gtpv1uNewTeid(); + incoming_teid = gtpv1uNewTeid(); }; globGtp.te2ue_mapping[incoming_teid].ue_id = ue_id; @@ -607,71 +577,74 @@ teid_t newGtpuCreateTunnel(instance_t instance, globGtp.te2ue_mapping[incoming_teid].callBackSDAP = callBackSDAP; globGtp.te2ue_mapping[incoming_teid].pdusession_id = (uint8_t)outgoing_bearer_id; - gtpv1u_bearer_t *tmp=&inst->ue2te_mapping[ue_id].bearers[outgoing_bearer_id]; + gtpv1u_bearer_t bearer = { + .sock_fd = (int) compatInst(instance), // avoid warning on narrowing conversion: instance is long, sock_fd is int + .teid_incoming = incoming_teid, + .teid_outgoing = outgoing_teid, + .outgoing_qfi = outgoing_qfi, + }; int addrs_length_in_bytes = remoteAddr.length / 8; - + struct sockaddr_in *sa4 = (struct sockaddr_in *)&bearer.ip; + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&bearer.ip; switch (addrs_length_in_bytes) { case 4: - memcpy(&tmp->outgoing_ip_addr,remoteAddr.buffer,4); + memcpy(&sa4->sin_addr, remoteAddr.buffer, 4); + sa4->sin_family = AF_INET; + sa4->sin_port = htons(inst->get_dstport()); break; case 16: - memcpy(tmp->outgoing_ip6_addr.s6_addr,remoteAddr.buffer, - 16); + AssertFatal(false, "IPv6 not supported\n"); break; case 20: - memcpy(&tmp->outgoing_ip_addr,remoteAddr.buffer,4); - memcpy(tmp->outgoing_ip6_addr.s6_addr, - remoteAddr.buffer+4, - 16); + AssertFatal(false, "dual-IPv4/v6 not supported\n"); + break; default: AssertFatal(false, "SGW Address size impossible"); } - tmp->teid_incoming = incoming_teid; - tmp->outgoing_port=port; - tmp->teid_outgoing= outgoing_teid; - tmp->outgoing_qfi=outgoing_qfi; + inst->ue2te_mapping[ue_id].bearers[outgoing_bearer_id] = bearer; pthread_mutex_unlock(&globGtp.gtp_lock); char ip4[INET_ADDRSTRLEN]; char ip6[INET6_ADDRSTRLEN]; LOG_I(GTPU, - "[%ld] Created tunnel for UE ID %lu, teid for incoming: %x, teid for outgoing %x to remote IPv4: %s, IPv6 %s\n", + "[%ld] UE ID %ld: Create tunnel TEID incoming %x outgoing %x to remote IPv4 %s, IPv6 %s, port %d\n", instance, ue_id, - tmp->teid_incoming, - tmp->teid_outgoing, - inet_ntop(AF_INET, (void *)&tmp->outgoing_ip_addr, ip4, INET_ADDRSTRLEN), - inet_ntop(AF_INET6, (void *)&tmp->outgoing_ip6_addr.s6_addr, ip6, INET6_ADDRSTRLEN)); + bearer.teid_incoming, + bearer.teid_outgoing, + inet_ntop(AF_INET, &sa4->sin_addr, ip4, INET_ADDRSTRLEN), + inet_ntop(AF_INET6, &sa6->sin6_addr, ip6, INET6_ADDRSTRLEN), + ntohs(sa4->sin_port)); return incoming_teid; } int gtpv1u_create_s1u_tunnel(instance_t instance, - const gtpv1u_enb_create_tunnel_req_t *create_tunnel_req, + const gtpv1u_enb_create_tunnel_req_t *create_tunnel_req, gtpv1u_enb_create_tunnel_resp_t *create_tunnel_resp, gtpCallback callBack) { - LOG_D(GTPU, "[%ld] Start create tunnels for UE ID %u, num_tunnels %d, sgw_S1u_teid %x\n", + LOG_D(GTPU, + "[%ld] Start create tunnels for UE ID %u, num_tunnels %d, sgw_S1u_teid %x\n", instance, create_tunnel_req->rnti, create_tunnel_req->num_tunnels, create_tunnel_req->sgw_S1u_teid[0]); pthread_mutex_lock(&globGtp.gtp_lock); getInstRetInt(compatInst(instance)); - - tcp_udp_port_t dstport=inst->get_dstport(); + uint8_t addr[inst->foundAddrLen]; memcpy(addr, inst->foundAddr, inst->foundAddrLen); pthread_mutex_unlock(&globGtp.gtp_lock); - + for (int i = 0; i < create_tunnel_req->num_tunnels; i++) { AssertFatal(create_tunnel_req->eps_bearer_id[i] > 4, "From legacy code not clear, seems impossible (bearer=%d)\n", create_tunnel_req->eps_bearer_id[i]); - int incoming_rb_id=create_tunnel_req->eps_bearer_id[i]-4; + int incoming_rb_id = create_tunnel_req->eps_bearer_id[i] - 4; teid_t teid = newGtpuCreateTunnel(compatInst(instance), create_tunnel_req->rnti, incoming_rb_id, @@ -679,27 +652,26 @@ int gtpv1u_create_s1u_tunnel(instance_t instance, create_tunnel_req->sgw_S1u_teid[i], -1, // no pdu session in 4G create_tunnel_req->sgw_addr[i], - dstport, callBack, NULL); - create_tunnel_resp->status=0; - create_tunnel_resp->rnti=create_tunnel_req->rnti; - create_tunnel_resp->num_tunnels=create_tunnel_req->num_tunnels; - create_tunnel_resp->enb_S1u_teid[i]=teid; + create_tunnel_resp->status = 0; + create_tunnel_resp->rnti = create_tunnel_req->rnti; + create_tunnel_resp->num_tunnels = create_tunnel_req->num_tunnels; + create_tunnel_resp->enb_S1u_teid[i] = teid; create_tunnel_resp->eps_bearer_id[i] = create_tunnel_req->eps_bearer_id[i]; - memcpy(create_tunnel_resp->enb_addr.buffer,addr,sizeof(addr)); - create_tunnel_resp->enb_addr.length= sizeof(addr); + memcpy(create_tunnel_resp->enb_addr.buffer, addr, sizeof(addr)); + create_tunnel_resp->enb_addr.length = sizeof(addr); } return !GTPNOK; } -int gtpv1u_update_s1u_tunnel( - const instance_t instance, - const gtpv1u_enb_create_tunnel_req_t *const create_tunnel_req, - const rnti_t prior_rnti -) { - LOG_D(GTPU, "[%ld] Start update tunnels for old RNTI %x, new RNTI %x, num_tunnels %d, sgw_S1u_teid %x, eps_bearer_id %x\n", +int gtpv1u_update_s1u_tunnel(const instance_t instance, + const gtpv1u_enb_create_tunnel_req_t *const create_tunnel_req, + const rnti_t prior_rnti) +{ + LOG_D(GTPU, + "[%ld] Start update tunnels for old RNTI %x, new RNTI %x, num_tunnels %d, sgw_S1u_teid %x, eps_bearer_id %x\n", instance, prior_rnti, create_tunnel_req->rnti, @@ -709,22 +681,28 @@ int gtpv1u_update_s1u_tunnel( pthread_mutex_lock(&globGtp.gtp_lock); getInstRetInt(compatInst(instance)); - if ( inst->ue2te_mapping.find(create_tunnel_req->rnti) == inst->ue2te_mapping.end() ) { - LOG_E(GTPU,"[%ld] Update not already existing tunnel (new rnti %x, old rnti %x)\n", - instance, create_tunnel_req->rnti, prior_rnti); + if (inst->ue2te_mapping.find(create_tunnel_req->rnti) == inst->ue2te_mapping.end()) { + LOG_E(GTPU, + "[%ld] Update not already existing tunnel (new rnti %x, old rnti %x)\n", + instance, + create_tunnel_req->rnti, + prior_rnti); } - auto it=inst->ue2te_mapping.find(prior_rnti); + auto it = inst->ue2te_mapping.find(prior_rnti); - if ( it != inst->ue2te_mapping.end() ) { + if (it != inst->ue2te_mapping.end()) { pthread_mutex_unlock(&globGtp.gtp_lock); - AssertFatal(false, "logic bug: update of non-existing tunnel (new ue id %u, old ue id %u)\n", create_tunnel_req->rnti, prior_rnti); + AssertFatal(false, + "logic bug: update of non-existing tunnel (new ue id %u, old ue id %u)\n", + create_tunnel_req->rnti, + prior_rnti); /* we don't know if we need 4G or 5G PDCP and can therefore not create a * new tunnel */ return 0; } - inst->ue2te_mapping[create_tunnel_req->rnti]=it->second; + inst->ue2te_mapping[create_tunnel_req->rnti] = it->second; inst->ue2te_mapping.erase(it); pthread_mutex_unlock(&globGtp.gtp_lock); return 0; @@ -736,7 +714,8 @@ int gtpv1u_create_ngu_tunnel(const instance_t instance, gtpCallback callBack, gtpCallbackSDAP callBackSDAP) { - LOG_D(GTPU, "[%ld] Start create tunnels for ue id %lu, num_tunnels %d, sgw_S1u_teid %x\n", + LOG_D(GTPU, + "[%ld] Start create tunnels for ue id %lu, num_tunnels %d, sgw_S1u_teid %x\n", instance, create_tunnel_req->ue_id, create_tunnel_req->num_tunnels, @@ -744,7 +723,6 @@ int gtpv1u_create_ngu_tunnel(const instance_t instance, pthread_mutex_lock(&globGtp.gtp_lock); getInstRetInt(compatInst(instance)); - tcp_udp_port_t dstport = inst->get_dstport(); uint8_t addr[inst->foundAddrLen]; memcpy(addr, inst->foundAddr, inst->foundAddrLen); pthread_mutex_unlock(&globGtp.gtp_lock); @@ -756,15 +734,14 @@ int gtpv1u_create_ngu_tunnel(const instance_t instance, create_tunnel_req->outgoing_teid[i], create_tunnel_req->outgoing_qfi[i], create_tunnel_req->dst_addr[i], - dstport, callBack, callBackSDAP); - create_tunnel_resp->status=0; - create_tunnel_resp->ue_id=create_tunnel_req->ue_id; - create_tunnel_resp->num_tunnels=create_tunnel_req->num_tunnels; - create_tunnel_resp->gnb_NGu_teid[i]=teid; - memcpy(create_tunnel_resp->gnb_addr.buffer,addr,sizeof(addr)); - create_tunnel_resp->gnb_addr.length= sizeof(addr); + create_tunnel_resp->status = 0; + create_tunnel_resp->ue_id = create_tunnel_req->ue_id; + create_tunnel_resp->num_tunnels = create_tunnel_req->num_tunnels; + create_tunnel_resp->gnb_NGu_teid[i] = teid; + memcpy(create_tunnel_resp->gnb_addr.buffer, addr, sizeof(addr)); + create_tunnel_resp->gnb_addr.length = sizeof(addr); create_tunnel_resp->pdusession_id[i] = create_tunnel_req->pdusession_id[i]; } @@ -799,11 +776,11 @@ int gtpv1u_update_ue_id(const instance_t instanceP, ue_id_t old_ue_id, ue_id_t n return !GTPNOK; } -int gtpv1u_create_x2u_tunnel( - const instance_t instanceP, - const gtpv1u_enb_create_x2u_tunnel_req_t *const create_tunnel_req_pP, - gtpv1u_enb_create_x2u_tunnel_resp_t *const create_tunnel_resp_pP) { - AssertFatal( false, "to be developped\n"); +int gtpv1u_create_x2u_tunnel(const instance_t instanceP, + const gtpv1u_enb_create_x2u_tunnel_req_t *const create_tunnel_req_pP, + gtpv1u_enb_create_x2u_tunnel_resp_t *const create_tunnel_resp_pP) +{ + AssertFatal(false, "to be developped\n"); } int newGtpuDeleteOneTunnel(instance_t instance, ue_id_t ue_id, int rb_id) @@ -827,35 +804,33 @@ int newGtpuDeleteOneTunnel(instance_t instance, ue_id_t ue_id, int rb_id) ue_it->second.bearers.erase(rb_id); pthread_mutex_unlock(&globGtp.gtp_lock); LOG_I(GTPU, "Deleted tunnel TEID %d (RB %d) for ue id %ld, remaining bearers:\n", teid, rb_id, ue_id); - for (auto b: ue_it->second.bearers) + for (auto b : ue_it->second.bearers) LOG_I(GTPU, "bearer %ld\n", b.first); return !GTPNOK; } -int newGtpuDeleteAllTunnels(instance_t instance, ue_id_t ue_id) { - LOG_D(GTPU, "[%ld] Start delete tunnels for ue id %lu\n", - instance, ue_id); +int newGtpuDeleteAllTunnels(instance_t instance, ue_id_t ue_id) +{ + LOG_D(GTPU, "[%ld] Start delete tunnels for ue id %lu\n", instance, ue_id); pthread_mutex_lock(&globGtp.gtp_lock); getInstRetInt(compatInst(instance)); getUeRetInt(inst, ue_id); - int nb=0; + int nb = 0; - for (auto j=ptrUe->second.bearers.begin(); - j!=ptrUe->second.bearers.end(); - ++j) { + for (auto j = ptrUe->second.bearers.begin(); j != ptrUe->second.bearers.end(); ++j) { globGtp.te2ue_mapping.erase(j->second.teid_incoming); nb++; } inst->ue2te_mapping.erase(ptrUe); pthread_mutex_unlock(&globGtp.gtp_lock); - LOG_I(GTPU, "[%ld] Deleted all tunnels for ue id %ld (%d tunnels deleted)\n", instance, ue_id, nb); + LOG_I(GTPU, "[%ld] UE ID %ld: Delete all tunnels (%d tunnels)\n", instance, ue_id, nb); return !GTPNOK; } -int gtpv1u_delete_s1u_tunnel( const instance_t instance, - const gtpv1u_enb_delete_tunnel_req_t *const req_pP) { +int gtpv1u_delete_s1u_tunnel(const instance_t instance, const gtpv1u_enb_delete_tunnel_req_t *const req_pP) +{ LOG_D(GTPU, "[%ld] Start delete tunnels for RNTI %x\n", instance, req_pP->rnti); pthread_mutex_lock(&globGtp.gtp_lock); auto inst = &globGtp.instances[compatInst(instance)]; @@ -899,25 +874,24 @@ int gtpv1u_delete_all_s1u_tunnel(const instance_t instance, const rnti_t rnti) int newGtpuDeleteTunnels(instance_t instance, ue_id_t ue_id, int nbTunnels, int *pdusession_id) { - LOG_D(GTPU, "[%ld] Start delete tunnels for ue id %lu\n", - instance, ue_id); + LOG_D(GTPU, "[%ld] Start delete tunnels for ue id %lu\n", instance, ue_id); pthread_mutex_lock(&globGtp.gtp_lock); getInstRetInt(compatInst(instance)); getUeRetInt(inst, ue_id); - int nb=0; + int nb = 0; - for (int i=0; isecond.bearers.find(pdusession_id[i]); + for (int i = 0; i < nbTunnels; i++) { + auto ptr2 = ptrUe->second.bearers.find(pdusession_id[i]); - if ( ptr2 == ptrUe->second.bearers.end() ) { - LOG_E(GTPU,"[%ld] GTP-U instance: delete of not existing tunnel UE ID:RAB: %ld/%x\n", instance, ue_id, pdusession_id[i]); + if (ptr2 == ptrUe->second.bearers.end()) { + LOG_E(GTPU, "[%ld] GTP-U instance: delete of not existing tunnel UE ID:RAB: %ld/%x\n", instance, ue_id, pdusession_id[i]); } else { globGtp.te2ue_mapping.erase(ptr2->second.teid_incoming); nb++; } } - if (ptrUe->second.bearers.size() == 0 ) + if (ptrUe->second.bearers.size() == 0) // no tunnels on this ue id, erase the ue entry inst->ue2te_mapping.erase(ptrUe); @@ -926,56 +900,63 @@ int newGtpuDeleteTunnels(instance_t instance, ue_id_t ue_id, int nbTunnels, int return !GTPNOK; } -int gtpv1u_delete_x2u_tunnel( const instance_t instanceP, - const gtpv1u_enb_delete_tunnel_req_t *const req_pP) { - LOG_E(GTPU,"x2 tunnel not implemented\n"); +int gtpv1u_delete_x2u_tunnel(const instance_t instanceP, const gtpv1u_enb_delete_tunnel_req_t *const req_pP) +{ + LOG_E(GTPU, "x2 tunnel not implemented\n"); return 0; } -int gtpv1u_delete_ngu_tunnel( const instance_t instance, - gtpv1u_gnb_delete_tunnel_req_t *req) { - return newGtpuDeleteTunnels(instance, req->ue_id, req->num_pdusession, req->pdusession_id); +int gtpv1u_delete_ngu_tunnel(const instance_t instance, gtpv1u_gnb_delete_tunnel_req_t *req) +{ + return newGtpuDeleteTunnels(instance, req->ue_id, req->num_pdusession, req->pdusession_id); } -static int Gtpv1uHandleEchoReq(int h, - uint8_t *msgBuf, - uint32_t msgBufLen, - uint16_t peerPort, - uint32_t peerIp) { - Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *) msgBuf; +static gtpv1u_bearer_t create_bearer(int socket, const struct sockaddr_in *addr, uint32_t teid, uint16_t seq) +{ + gtpv1u_bearer_t bearer = {.sock_fd = socket, .teid_outgoing = teid, .seqNum = seq}; + memcpy(&bearer.ip, addr, sizeof(*addr)); + return bearer; +} - if ( msgHdr->version != 1 || msgHdr->PT != 1 ) { +static int Gtpv1uHandleEchoReq(int h, uint8_t *msgBuf, uint32_t msgBufLen, const struct sockaddr_in *addr) +{ + Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *)msgBuf; + + if (msgHdr->version != 1 || msgHdr->PT != 1) { LOG_E(GTPU, "[%d] Received a packet that is not GTP header\n", h); return GTPNOK; } - if ( msgHdr->S != 1 ) { + if (msgHdr->S != 1) { LOG_E(GTPU, "[%d] Received a echo request packet with no sequence number \n", h); return GTPNOK; } - uint16_t seq=ntohs(*(uint16_t *)(msgHdr+1)); + uint16_t seq = ntohs(*(uint16_t *)(msgHdr + 1)); LOG_D(GTPU, "[%d] Received a echo request, TEID: %d, seq: %hu\n", h, msgHdr->teid, seq); - uint8_t recovery[2]= {14,0}; - return gtpv1uCreateAndSendMsg(h, peerIp, peerPort, GTP_ECHO_RSP, ntohl(msgHdr->teid), recovery, sizeof recovery, true, false, seq, 0, NO_MORE_EXT_HDRS, NULL, 0); + uint8_t recovery[2] = {14, 0}; + gtpv1u_bearer_t bearer = create_bearer(h, addr, ntohl(msgHdr->teid), seq); + return gtpv1uCreateAndSendMsg(&bearer, + GTP_ECHO_RSP, + recovery, + sizeof recovery, + true, + false, + NO_MORE_EXT_HDRS, + NULL, + 0); } -static int Gtpv1uHandleError(int h, - uint8_t *msgBuf, - uint32_t msgBufLen, - uint16_t peerPort, - uint32_t peerIp) { +static int Gtpv1uHandleError(int h, uint8_t *msgBuf, uint32_t msgBufLen, const struct sockaddr_in *addr) +{ LOG_E(GTPU, "Received GTP error indication (error handling is missing/not implemented)\n"); int rc = GTPNOK; return rc; } -static int Gtpv1uHandleSupportedExt(int h, - uint8_t *msgBuf, - uint32_t msgBufLen, - uint16_t peerPort, - uint32_t peerIp) { - LOG_E(GTPU,"Supported extensions to be dev\n"); +static int Gtpv1uHandleSupportedExt(int h, uint8_t *msgBuf, uint32_t msgBufLen, const sockaddr_in *addr) +{ + LOG_E(GTPU, "Supported extensions to be dev\n"); int rc = GTPNOK; return rc; } @@ -983,14 +964,11 @@ static int Gtpv1uHandleSupportedExt(int h, // When end marker arrives, we notify the client with buffer size = 0 // The client will likely call "delete tunnel" // nevertheless we don't take the initiative -static int Gtpv1uHandleEndMarker(int h, - uint8_t *msgBuf, - uint32_t msgBufLen, - uint16_t peerPort, - uint32_t peerIp) { - Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *) msgBuf; +static int Gtpv1uHandleEndMarker(int h, uint8_t *msgBuf, uint32_t msgBufLen, const sockaddr_in *addr) +{ + Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *)msgBuf; - if ( msgHdr->version != 1 || msgHdr->PT != 1 ) { + if (msgHdr->version != 1 || msgHdr->PT != 1) { LOG_E(GTPU, "[%d] Received a packet that is not GTP header\n", h); return GTPNOK; } @@ -1002,7 +980,7 @@ static int Gtpv1uHandleEndMarker(int h, auto tunnel = globGtp.te2ue_mapping.find(ntohl(msgHdr->teid)); if (tunnel == globGtp.te2ue_mapping.end()) { - LOG_E(GTPU,"[%d] Received a incoming packet on unknown teid (%x) Dropping!\n", h, msgHdr->teid); + LOG_E(GTPU, "[%d] Received a incoming packet on unknown teid (%x) Dropping!\n", h, msgHdr->teid); pthread_mutex_unlock(&globGtp.gtp_lock); return GTPNOK; } @@ -1019,56 +997,44 @@ static int Gtpv1uHandleEndMarker(int h, ctxt.subframe = 0; ctxt.eNB_index = 0; ctxt.brOption = 0; - const srb_flag_t srb_flag=SRB_FLAG_NO; - const rb_id_t rb_id=tunnel->second.incoming_rb_id; - const mui_t mui=RLC_MUI_UNDEFINED; - const confirm_t confirm=RLC_SDU_CONFIRM_NO; - const pdcp_transmission_mode_t mode=PDCP_TRANSMISSION_MODE_DATA; - const uint32_t sourceL2Id=0; - const uint32_t destinationL2Id=0; + const srb_flag_t srb_flag = SRB_FLAG_NO; + const rb_id_t rb_id = tunnel->second.incoming_rb_id; + const mui_t mui = RLC_MUI_UNDEFINED; + const confirm_t confirm = RLC_SDU_CONFIRM_NO; + const pdcp_transmission_mode_t mode = PDCP_TRANSMISSION_MODE_DATA; + const uint32_t sourceL2Id = 0; + const uint32_t destinationL2Id = 0; pthread_mutex_unlock(&globGtp.gtp_lock); - if ( !tunnel->second.callBack(&ctxt, - srb_flag, - rb_id, - mui, - confirm, - 0, - NULL, - mode, - &sourceL2Id, - &destinationL2Id) ) - LOG_E(GTPU,"[%d] down layer refused incoming packet\n", h); + if (!tunnel->second.callBack(&ctxt, srb_flag, rb_id, mui, confirm, 0, NULL, mode, &sourceL2Id, &destinationL2Id)) + LOG_E(GTPU, "[%d] down layer refused incoming packet\n", h); - LOG_D(GTPU,"[%d] Received END marker packet for: teid:%x\n", h, ntohl(msgHdr->teid)); + LOG_D(GTPU, "[%d] Received END marker packet for: teid:%x\n", h, ntohl(msgHdr->teid)); return !GTPNOK; } -static int Gtpv1uHandleGpdu(int h, - uint8_t *msgBuf, - uint32_t msgBufLen, - uint16_t peerPort, - uint32_t peerIp) { - Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *) msgBuf; +static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, const struct sockaddr_in *addr) +{ + Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *)msgBuf; - if ( msgHdr->version != 1 || msgHdr->PT != 1 ) { + if (msgHdr->version != 1 || msgHdr->PT != 1) { LOG_E(GTPU, "[%d] Received a packet that is not GTP header\n", h); return GTPNOK; } pthread_mutex_lock(&globGtp.gtp_lock); - // the socket Linux file handler is the instance id - getInstRetInt(h); auto tunnel = globGtp.te2ue_mapping.find(ntohl(msgHdr->teid)); if (tunnel == globGtp.te2ue_mapping.end()) { - LOG_E(GTPU,"[%d] Received a incoming packet on unknown teid (%x) Dropping!\n", h, ntohl(msgHdr->teid)); + LOG_E(GTPU, "[%d] Received a incoming packet on unknown teid (%x) Dropping!\n", h, ntohl(msgHdr->teid)); pthread_mutex_unlock(&globGtp.gtp_lock); return GTPNOK; } + ueidData_t uedata = tunnel->second; + pthread_mutex_unlock(&globGtp.gtp_lock); /* see TS 29.281 5.1 */ - //Minimum length of GTP-U header if non of the optional fields are present + // Minimum length of GTP-U header if non of the optional fields are present unsigned int offset = sizeof(Gtpv1uMsgHeaderT); int8_t qfi = -1; @@ -1076,7 +1042,7 @@ static int Gtpv1uHandleGpdu(int h, uint32_t NR_PDCP_PDU_SN = 0; /* if E, S, or PN is set then there are 4 more bytes of header */ - if( msgHdr->E || msgHdr->S ||msgHdr->PN) + if (msgHdr->E || msgHdr->S || msgHdr->PN) offset += 4; if (msgHdr->E) { @@ -1087,53 +1053,53 @@ static int Gtpv1uHandleGpdu(int h, extension_header_length = msgBuf[offset]; switch (next_extension_header_type) { case PDU_SESSION_CONTAINER: { - if (offset + sizeof(PDUSessionContainerT) > msgBufLen ) { - LOG_E(GTPU, "gtp-u received header is malformed, ignore gtp packet\n"); - return GTPNOK; - } + if (offset + sizeof(PDUSessionContainerT) > msgBufLen) { + LOG_E(GTPU, "gtp-u received header is malformed, ignore gtp packet\n"); + return GTPNOK; + } PDUSessionContainerT *pdusession_cntr = (PDUSessionContainerT *)(msgBuf + offset + 1); qfi = pdusession_cntr->QFI; rqi = pdusession_cntr->Reflective_QoS_activation; break; } case NR_RAN_CONTAINER: { - if (offset + 1 > msgBufLen ) { - LOG_E(GTPU, "gtp-u received header is malformed, ignore gtp packet\n"); - return GTPNOK; - } - uint8_t PDU_type = (msgBuf[offset+1]>>4) & 0x0f; - if (PDU_type == 0){ //DL USER Data Format - int additional_offset = 6; //Additional offset capturing the first non-mandatory octet (TS 38.425, Figure 5.5.2.1-1) - if(msgBuf[offset+1]>>2 & 0x1){ //DL Discard Blocks flag is present - LOG_I(GTPU, "DL User Data: DL Discard Blocks handling not enabled\n"); - additional_offset = additional_offset + 9; //For the moment ignore + if (offset + 1 > msgBufLen) { + LOG_E(GTPU, "gtp-u received header is malformed, ignore gtp packet\n"); + return GTPNOK; + } + uint8_t PDU_type = (msgBuf[offset + 1] >> 4) & 0x0f; + if (PDU_type == 0) { // DL USER Data Format + int additional_offset = 6; // Additional offset capturing the first non-mandatory octet (TS 38.425, Figure 5.5.2.1-1) + if (msgBuf[offset + 1] >> 2 & 0x1) { // DL Discard Blocks flag is present + LOG_I(GTPU, "DL User Data: DL Discard Blocks handling not enabled\n"); + additional_offset = additional_offset + 9; // For the moment ignore } - if(msgBuf[offset+1]>>1 & 0x1){ //DL Flush flag is present + if (msgBuf[offset + 1] >> 1 & 0x1) { // DL Flush flag is present LOG_I(GTPU, "DL User Data: DL Flush handling not enabled\n"); - additional_offset = additional_offset + 3; //For the moment ignore + additional_offset = additional_offset + 3; // For the moment ignore } - if((msgBuf[offset+2]>>3)& 0x1){ //"Report delivered" enabled (TS 38.425, 5.4) + if ((msgBuf[offset + 2] >> 3) & 0x1) { //"Report delivered" enabled (TS 38.425, 5.4) /*Store the NR PDCP PDU SN for which a delivery status report shall be generated once the *PDU gets forwarded to the lower layers*/ - //NR_PDCP_PDU_SN = msgBuf[offset+6] << 16 | msgBuf[offset+7] << 8 | msgBuf[offset+8]; - NR_PDCP_PDU_SN = msgBuf[offset+additional_offset] << 16 | msgBuf[offset+additional_offset+1] << 8 | msgBuf[offset+additional_offset+2]; - LOG_D(GTPU, " NR_PDCP_PDU_SN: %u \n", NR_PDCP_PDU_SN); + // NR_PDCP_PDU_SN = msgBuf[offset+6] << 16 | msgBuf[offset+7] << 8 | msgBuf[offset+8]; + NR_PDCP_PDU_SN = msgBuf[offset + additional_offset] << 16 | msgBuf[offset + additional_offset + 1] << 8 + | msgBuf[offset + additional_offset + 2]; + LOG_D(GTPU, " NR_PDCP_PDU_SN: %u \n", NR_PDCP_PDU_SN); } - } - else{ + } else { LOG_W(GTPU, "NR-RAN container type: %d not supported \n", PDU_type); } break; } default: LOG_W(GTPU, "unhandled extension 0x%2.2x, skipping\n", next_extension_header_type); - break; + break; } offset += extension_header_length * EXT_HDR_LNTH_OCTET_UNITS; - if (offset > msgBufLen ) { - LOG_E(GTPU, "gtp-u received header is malformed, ignore gtp packet\n"); - return GTPNOK; + if (offset > msgBufLen) { + LOG_E(GTPU, "gtp-u received header is malformed, ignore gtp packet\n"); + return GTPNOK; } next_extension_header_type = msgBuf[offset - 1]; } @@ -1142,60 +1108,42 @@ static int Gtpv1uHandleGpdu(int h, // This context is not good for gtp // frame, ... has no meaning // manyother attributes may come from create tunnel - protocol_ctxt_t ctxt; - ctxt.module_id = 0; - ctxt.enb_flag = 1; - ctxt.instance = inst->addr.originInstance; - ctxt.rntiMaybeUEid = tunnel->second.ue_id; - ctxt.frame = 0; - ctxt.subframe = 0; - ctxt.eNB_index = 0; - ctxt.brOption = 0; - const srb_flag_t srb_flag=SRB_FLAG_NO; - const rb_id_t rb_id=tunnel->second.incoming_rb_id; - const mui_t mui=RLC_MUI_UNDEFINED; - const confirm_t confirm=RLC_SDU_CONFIRM_NO; + protocol_ctxt_t ctxt = { .enb_flag = 1, .rntiMaybeUEid = uedata.ue_id, }; + const srb_flag_t srb_flag = SRB_FLAG_NO; + const rb_id_t rb_id = uedata.incoming_rb_id; + const mui_t mui = RLC_MUI_UNDEFINED; + const confirm_t confirm = RLC_SDU_CONFIRM_NO; const sdu_size_t sdu_buffer_size = msgBufLen - offset; - unsigned char *const sdu_buffer=msgBuf+offset; - const pdcp_transmission_mode_t mode=PDCP_TRANSMISSION_MODE_DATA; - const uint32_t sourceL2Id=0; - const uint32_t destinationL2Id=0; - pthread_mutex_unlock(&globGtp.gtp_lock); + unsigned char *const sdu_buffer = msgBuf + offset; + const pdcp_transmission_mode_t mode = PDCP_TRANSMISSION_MODE_DATA; + const uint32_t sourceL2Id = 0; + const uint32_t destinationL2Id = 0; if (sdu_buffer_size > 0) { - if (qfi != -1 && tunnel->second.callBackSDAP) { - if ( !tunnel->second.callBackSDAP(&ctxt, - tunnel->second.ue_id, - srb_flag, - rb_id, - mui, - confirm, - sdu_buffer_size, - sdu_buffer, - mode, - &sourceL2Id, - &destinationL2Id, - qfi, - rqi, - tunnel->second.pdusession_id) ) - LOG_E(GTPU,"[%d] down layer refused incoming packet\n", h); + if (qfi != -1 && uedata.callBackSDAP) { + if (!uedata.callBackSDAP(&ctxt, + uedata.ue_id, + srb_flag, + rb_id, + mui, + confirm, + sdu_buffer_size, + sdu_buffer, + mode, + &sourceL2Id, + &destinationL2Id, + qfi, + rqi, + uedata.pdusession_id)) + LOG_E(GTPU, "[%d] down layer refused incoming packet\n", h); } else { - if ( !tunnel->second.callBack(&ctxt, - srb_flag, - rb_id, - mui, - confirm, - sdu_buffer_size, - sdu_buffer, - mode, - &sourceL2Id, - &destinationL2Id) ) - LOG_E(GTPU,"[%d] down layer refused incoming packet\n", h); + if (!uedata.callBack(&ctxt, srb_flag, rb_id, mui, confirm, sdu_buffer_size, sdu_buffer, mode, &sourceL2Id, &destinationL2Id)) + LOG_E(GTPU, "[%d] down layer refused incoming packet\n", h); } } - if(NR_PDCP_PDU_SN > 0 && NR_PDCP_PDU_SN %5 ==0){ - LOG_D (GTPU, "Create and send DL DATA Delivery status for the previously received PDU, NR_PDCP_PDU_SN: %u \n", NR_PDCP_PDU_SN); + if (NR_PDCP_PDU_SN > 0 && NR_PDCP_PDU_SN % 5 == 0) { + LOG_D(GTPU, "Create and send DL DATA Delivery status for the previously received PDU, NR_PDCP_PDU_SN: %u \n", NR_PDCP_PDU_SN); int rlc_tx_buffer_space = nr_rlc_get_available_tx_space(ctxt.rntiMaybeUEid, rb_id + 3); LOG_D(GTPU, "Available buffer size in RLC for Tx: %d \n", rlc_tx_buffer_space); /*Total size of DDD_status PDU = 1 octet to report extension header length @@ -1203,87 +1151,96 @@ static int Gtpv1uHandleGpdu(int h, * 1 octet for padding + 1 octet for next extension header type, * according to TS 38.425: Fig. 5.5.2.2-1 and section 5.5.3.24*/ extensionHeader_t *extensionHeader; - extensionHeader = (extensionHeader_t *) calloc(1, sizeof(extensionHeader_t)) ; - extensionHeader->buffer[0] = (1+sizeof(DlDataDeliveryStatus_flagsT)+3+1+1)/4; + extensionHeader = (extensionHeader_t *)calloc(1, sizeof(extensionHeader_t)); + extensionHeader->buffer[0] = (1 + sizeof(DlDataDeliveryStatus_flagsT) + 3 + 1 + 1) / 4; DlDataDeliveryStatus_flagsT DlDataDeliveryStatus; DlDataDeliveryStatus.deliveredPdcpSn = 0; - DlDataDeliveryStatus.transmittedPdcpSn= 1; + DlDataDeliveryStatus.transmittedPdcpSn = 1; DlDataDeliveryStatus.pduType = 1; - DlDataDeliveryStatus.drbBufferSize = htonl(rlc_tx_buffer_space); //htonl(10000000); //hardcoded for now but normally we should extract it from RLC - memcpy(extensionHeader->buffer+1, &DlDataDeliveryStatus, sizeof(DlDataDeliveryStatus_flagsT)); - uint8_t offset = sizeof(DlDataDeliveryStatus_flagsT)+1; + DlDataDeliveryStatus.drbBufferSize = + htonl(rlc_tx_buffer_space); // htonl(10000000); //hardcoded for now but normally we should extract it from RLC + memcpy(extensionHeader->buffer + 1, &DlDataDeliveryStatus, sizeof(DlDataDeliveryStatus_flagsT)); + uint8_t offset = sizeof(DlDataDeliveryStatus_flagsT) + 1; - extensionHeader->buffer[offset] = (NR_PDCP_PDU_SN >> 16) & 0xff; - extensionHeader->buffer[offset+1] = (NR_PDCP_PDU_SN >> 8) & 0xff; - extensionHeader->buffer[offset+2] = NR_PDCP_PDU_SN & 0xff; - LOG_D(GTPU, "Octets reporting NR_PDCP_PDU_SN, extensionHeader-> %u:%u:%u \n", + extensionHeader->buffer[offset] = (NR_PDCP_PDU_SN >> 16) & 0xff; + extensionHeader->buffer[offset + 1] = (NR_PDCP_PDU_SN >> 8) & 0xff; + extensionHeader->buffer[offset + 2] = NR_PDCP_PDU_SN & 0xff; + LOG_D(GTPU, + "Octets reporting NR_PDCP_PDU_SN, extensionHeader-> %u:%u:%u \n", extensionHeader->buffer[offset], - extensionHeader->buffer[offset+1], - extensionHeader->buffer[offset+2]); - extensionHeader->buffer[offset+3] = 0x00; //Padding octet - extensionHeader->buffer[offset+4] = 0x00; //No more extension headers + extensionHeader->buffer[offset + 1], + extensionHeader->buffer[offset + 2]); + extensionHeader->buffer[offset + 3] = 0x00; // Padding octet + extensionHeader->buffer[offset + 4] = 0x00; // No more extension headers /*Total size of DDD_status PDU = size of mandatory part + * 3 octets for highest transmitted/delivered PDCP SN + * 1 octet for padding + 1 octet for next extension header type, * according to TS 38.425: Fig. 5.5.2.2-1 and section 5.5.3.24*/ - extensionHeader->length = 1+sizeof(DlDataDeliveryStatus_flagsT)+3+1+1; - gtpv1uCreateAndSendMsg( - h, peerIp, peerPort, GTP_GPDU, globGtp.te2ue_mapping[ntohl(msgHdr->teid)].outgoing_teid, NULL, 0, false, false, 0, 0, NR_RAN_CONTAINER, extensionHeader->buffer, extensionHeader->length); + extensionHeader->length = 1 + sizeof(DlDataDeliveryStatus_flagsT) + 3 + 1 + 1; + uint32_t teid = globGtp.te2ue_mapping[ntohl(msgHdr->teid)].outgoing_teid; + gtpv1u_bearer_t bearer = create_bearer(h, addr, teid, 0); + gtpv1uCreateAndSendMsg(&bearer, + GTP_GPDU, + NULL, + 0, + false, + false, + NR_RAN_CONTAINER, + extensionHeader->buffer, + extensionHeader->length); } - LOG_D(GTPU,"[%d] Received a %d bytes packet for: teid:%x\n", h, - msgBufLen-offset, - ntohl(msgHdr->teid)); + LOG_D(GTPU, "[%d] Received a %d bytes packet for: teid:%x\n", h, msgBufLen - offset, ntohl(msgHdr->teid)); return !GTPNOK; } -void gtpv1uReceiver(int h) { - uint8_t udpData[65536]; - int udpDataLen; - socklen_t from_len; +static void gtpv1uReceiveHandleMessage(int h) +{ + uint8_t udpData[65536]; + int udpDataLen; + socklen_t from_len; struct sockaddr_in addr; from_len = (socklen_t)sizeof(struct sockaddr_in); - if ((udpDataLen = recvfrom(h, udpData, sizeof(udpData), 0, - (struct sockaddr *)&addr, &from_len)) < 0) { + 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; } else if (udpDataLen == 0) { LOG_W(GTPU, "[%d] Recvfrom returned 0\n", h); return; } else { - if ( udpDataLen < (int)sizeof(Gtpv1uMsgHeaderT) ) { + if (udpDataLen < (int)sizeof(Gtpv1uMsgHeaderT)) { LOG_W(GTPU, "[%d] received malformed gtp packet \n", h); return; } - Gtpv1uMsgHeaderT* msg=(Gtpv1uMsgHeaderT*) udpData; - if ( (int)(ntohs(msg->msgLength) + sizeof(Gtpv1uMsgHeaderT)) != udpDataLen ) { + Gtpv1uMsgHeaderT *msg = (Gtpv1uMsgHeaderT *)udpData; + if ((int)(ntohs(msg->msgLength) + sizeof(Gtpv1uMsgHeaderT)) != udpDataLen) { LOG_W(GTPU, "[%d] received malformed gtp packet length\n", h); return; } LOG_D(GTPU, "[%d] Received GTP data, msg type: %x\n", h, msg->msgType); - switch(msg->msgType) { + switch (msg->msgType) { case GTP_ECHO_RSP: break; case GTP_ECHO_REQ: - Gtpv1uHandleEchoReq( h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr); + Gtpv1uHandleEchoReq(h, udpData, udpDataLen, &addr); break; case GTP_ERROR_INDICATION: - Gtpv1uHandleError( h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr); + Gtpv1uHandleError(h, udpData, udpDataLen, &addr); break; case GTP_SUPPORTED_EXTENSION_HEADER_INDICATION: - Gtpv1uHandleSupportedExt( h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr); + Gtpv1uHandleSupportedExt(h, udpData, udpDataLen, &addr); break; case GTP_END_MARKER: - Gtpv1uHandleEndMarker( h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr); + Gtpv1uHandleEndMarker(h, udpData, udpDataLen, &addr); break; case GTP_GPDU: - Gtpv1uHandleGpdu( h, udpData, udpDataLen, htons(addr.sin_port), addr.sin_addr.s_addr); + Gtpv1uHandleGpdu(h, udpData, udpDataLen, &addr); break; default: @@ -1293,10 +1250,21 @@ void gtpv1uReceiver(int h) { } } +static void* gtpv1uReceiver(void *thr) +{ + gtpThread_t *gt = (gtpThread_t *)thr; + while (true) { + gtpv1uReceiveHandleMessage(gt->h); + } + LOG_W(GTPU, "exiting thread\n"); + return NULL; +} + #include -void *gtpv1uTask(void *args) { - while(1) { +void *gtpv1uTask(void *args) +{ + while (1) { /* Trying to fetch a message from the message queue. If the queue is empty, this function will block till a message is sent to the task. @@ -1304,19 +1272,14 @@ void *gtpv1uTask(void *args) { MessageDef *message_p = NULL; itti_receive_msg(TASK_GTPV1_U, &message_p); - if (message_p != NULL ) { - openAddr_t addr= {{0}}; + if (message_p != NULL) { + openAddr_t addr = {{0}}; const instance_t myInstance = ITTI_MSG_DESTINATION_INSTANCE(message_p); const int msgType = ITTI_MSG_ID(message_p); LOG_D(GTPU, "GTP-U received %s for instance %ld\n", messages_info[msgType].name, myInstance); switch (msgType) { // DATA TO BE SENT TO UDP - case GTPV1U_DU_BUFFER_REPORT_REQ:{ - gtpv1uSendDlDeliveryStatus(compatInst(myInstance), >PV1U_DU_BUFFER_REPORT_REQ(message_p)); - } - break; - case TERMINATE_MESSAGE: break; @@ -1340,8 +1303,8 @@ void *gtpv1uTask(void *args) { // to be dev: should be removed, to use API strcpy(addr.originHost, GTPV1U_REQ(message_p).localAddrStr); strcpy(addr.originService, GTPV1U_REQ(message_p).localPortStr); - strcpy(addr.destinationService,addr.originService); - AssertFatal((legacyInstanceMapping=gtpv1Init(addr))!=0,"Instance 0 reserved for legacy\n"); + strcpy(addr.destinationService, addr.originService); + AssertFatal((legacyInstanceMapping = gtpv1Init(addr)) != 0, "Instance 0 reserved for legacy\n"); break; default: @@ -1350,15 +1313,8 @@ void *gtpv1uTask(void *args) { break; } - AssertFatal(EXIT_SUCCESS==itti_free(TASK_GTPV1_U, message_p), "Failed to free memory!\n"); + AssertFatal(EXIT_SUCCESS == itti_free(TASK_GTPV1_U, message_p), "Failed to free memory!\n"); } - - struct epoll_event events[20]; - int nb_events = itti_get_events(TASK_GTPV1_U, events, 20); - - for (int i = 0; i < nb_events; i++) - if ((events[i].events&EPOLLIN)) - gtpv1uReceiver(events[i].data.fd); } return NULL; diff --git a/openair3/ocp-gtpu/gtp_itf.h b/openair3/ocp-gtpu/gtp_itf.h index 7a8c4da627..d341acb311 100644 --- a/openair3/ocp-gtpu/gtp_itf.h +++ b/openair3/ocp-gtpu/gtp_itf.h @@ -1,16 +1,40 @@ #ifndef __GTPUNEW_ITF_H__ #define __GTPUNEW_ITF_H__ +#include +#include +#include +#include + #define GTPNOK -1 # define GTPU_HEADER_OVERHEAD_MAX 64 -#include "openair2/COMMON/gtpv1_u_messages_types.h" -#include "common/utils/hashtable/hashtable.h" +#include "common/platform_types.h" #ifdef __cplusplus extern "C" { #endif +/* forward declaration */ +struct protocol_ctxt_s; +typedef struct protocol_ctxt_s protocol_ctxt_t; +struct gtpv1u_enb_create_tunnel_req_s; +typedef struct gtpv1u_enb_create_tunnel_req_s gtpv1u_enb_create_tunnel_req_t; +struct gtpv1u_enb_create_tunnel_resp_s; +typedef struct gtpv1u_enb_create_tunnel_resp_s gtpv1u_enb_create_tunnel_resp_t; +struct gtpv1u_enb_delete_tunnel_req_s; +typedef struct gtpv1u_enb_delete_tunnel_req_s gtpv1u_enb_delete_tunnel_req_t; +struct gtpv1u_enb_create_x2u_tunnel_req_s; +typedef struct gtpv1u_enb_create_x2u_tunnel_req_s gtpv1u_enb_create_x2u_tunnel_req_t; +struct gtpv1u_enb_create_x2u_tunnel_resp_s; +typedef struct gtpv1u_enb_create_x2u_tunnel_resp_s gtpv1u_enb_create_x2u_tunnel_resp_t; +struct gtpv1u_gnb_create_tunnel_req_s; +typedef struct gtpv1u_gnb_create_tunnel_req_s gtpv1u_gnb_create_tunnel_req_t; +struct gtpv1u_gnb_create_tunnel_resp_s; +typedef struct gtpv1u_gnb_create_tunnel_resp_s gtpv1u_gnb_create_tunnel_resp_t; +struct gtpv1u_gnb_delete_tunnel_req_s; +typedef struct gtpv1u_gnb_delete_tunnel_req_s gtpv1u_gnb_delete_tunnel_req_t; + typedef bool (*gtpCallback)(protocol_ctxt_t *ctxt_pP, const srb_flag_t srb_flagP, const rb_id_t rb_idP, @@ -52,7 +76,6 @@ extern "C" { // the init function create a gtp instance and return the gtp instance id // the parameter originInstance will be sent back in each message from gtp to the creator - void gtpv1uReceiver(int h); void gtpv1uProcessTimeout(int handle,void *arg); int gtpv1u_create_s1u_tunnel(const instance_t instance, const gtpv1u_enb_create_tunnel_req_t *create_tunnel_req, @@ -91,7 +114,6 @@ extern "C" { teid_t teid, int outgoing_qfi, transport_layer_addr_t remoteAddr, - int port, gtpCallback callBack, gtpCallbackSDAP callBackSDAP); @@ -110,13 +132,8 @@ extern "C" { instance_t gtpv1Init(openAddr_t context); void *gtpv1uTask(void *args); - // Legacy to fix - typedef struct gtpv1u_data_s { - /* RB tree of UEs */ - hash_table_t *ue_mapping; - } gtpv1u_data_t; #define GTPV1U_BEARER_OFFSET 3 -#define GTPV1U_MAX_BEARERS_ID (max_val_LTE_DRB_Identity - GTPV1U_BEARER_OFFSET) +#define GTPV1U_MAX_BEARERS_ID (32 - GTPV1U_BEARER_OFFSET) /* max_val_LTE_DRB_Identity */ typedef enum { BEARER_DOWN = 0, BEARER_IN_CONFIG, diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/cu.lte.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/cu.lte.conf deleted file mode 100644 index e486be3605..0000000000 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/cu.lte.conf +++ /dev/null @@ -1,196 +0,0 @@ -Active_eNBs = ( "eNB-CU-Eurecom-LTEBox"); -# Asn1_verbosity, choice in: none, info, annoying -Asn1_verbosity = "none"; - -eNBs = ( - { - ////////// Identification parameters: - eNB_ID = 0xe00; - - cell_type = "CELL_MACRO_ENB"; - - eNB_name = "eNB-CU-Eurecom-LTEBox"; - - // Tracking area code, 0x0000 and 0xfffe are reserved values - tracking_area_code = 1; - plmn_list = ( { mcc = 208; mnc = 95; mnc_length = 2; } ) - - nr_cellid = 12345678L - - tr_s_preference = "f1" - - remote_s_address = "192.168.12.4"; - local_s_address = "192.168.12.45"; - local_s_portc = 501; - remote_s_portc = 500; - local_s_portd = 601; - remote_s_portd = 600; - - ////////// Physical parameters: - - component_carriers = ( - { - node_function = "3GPP_eNodeB"; - node_timing = "synch_to_ext_device"; - node_synch_ref = 0; - frame_type = "FDD"; - tdd_config = 3; - tdd_config_s = 0; - prefix_type = "NORMAL"; - eutra_band = 7; - downlink_frequency = 2665000000L; - uplink_frequency_offset = -120000000; - Nid_cell = 0; - N_RB_DL = 50; - pbch_repetition = "FALSE"; - prach_root = 0; - prach_config_index = 0; - prach_high_speed = "DISABLE"; - prach_zero_correlation = 1; - prach_freq_offset = 2; - pucch_delta_shift = 1; - pucch_nRB_CQI = 0; - pucch_nCS_AN = 0; - pucch_n1_AN = 0; - pdsch_referenceSignalPower = -27; - pdsch_p_b = 0; - pusch_n_SB = 1; - pusch_enable64QAM = "DISABLE"; - pusch_hoppingMode = "interSubFrame"; - pusch_hoppingOffset = 0; - pusch_groupHoppingEnable = "ENABLE"; - pusch_groupAssignment = 0; - pusch_sequenceHoppingEnabled = "DISABLE"; - pusch_nDMRS1 = 1; - phich_duration = "NORMAL"; - phich_resource = "ONESIXTH"; - srs_enable = "DISABLE"; - /* - srs_BandwidthConfig =; - srs_SubframeConfig =; - srs_ackNackST =; - srs_MaxUpPts =; - */ - - pusch_p0_Nominal = -96; - pusch_alpha = "AL1"; - pucch_p0_Nominal = -104; - msg3_delta_Preamble = 6; - pucch_deltaF_Format1 = "deltaF2"; - pucch_deltaF_Format1b = "deltaF3"; - pucch_deltaF_Format2 = "deltaF0"; - pucch_deltaF_Format2a = "deltaF0"; - pucch_deltaF_Format2b = "deltaF0"; - - rach_numberOfRA_Preambles = 64; - rach_preamblesGroupAConfig = "DISABLE"; - /* - rach_sizeOfRA_PreamblesGroupA = ; - rach_messageSizeGroupA = ; - rach_messagePowerOffsetGroupB = ; - */ - rach_powerRampingStep = 4; - rach_preambleInitialReceivedTargetPower = -108; - rach_preambleTransMax = 10; - rach_raResponseWindowSize = 10; - rach_macContentionResolutionTimer = 48; - rach_maxHARQ_Msg3Tx = 4; - - pcch_default_PagingCycle = 128; - pcch_nB = "oneT"; - bcch_modificationPeriodCoeff= 2; - ue_TimersAndConstants_t300 = 1000; - ue_TimersAndConstants_t301 = 1000; - ue_TimersAndConstants_t310 = 1000; - ue_TimersAndConstants_t311 = 10000; - ue_TimersAndConstants_n310 = 20; - ue_TimersAndConstants_n311 = 1; - ue_TransmissionMode = 1; - - //Parameters for SIB18 - rxPool_sc_CP_Len = "normal"; - rxPool_sc_Period = "sf40"; - rxPool_data_CP_Len = "normal"; - rxPool_ResourceConfig_prb_Num = 20; - rxPool_ResourceConfig_prb_Start = 5; - rxPool_ResourceConfig_prb_End = 44; - rxPool_ResourceConfig_offsetIndicator_present = "prSmall"; - rxPool_ResourceConfig_offsetIndicator_choice = 0; - rxPool_ResourceConfig_subframeBitmap_present = "prBs40"; - rxPool_ResourceConfig_subframeBitmap_choice_bs_buf = "00000000000000000000"; - rxPool_ResourceConfig_subframeBitmap_choice_bs_size = 5; - rxPool_ResourceConfig_subframeBitmap_choice_bs_bits_unused = 0; - /* - rxPool_dataHoppingConfig_hoppingParameter = 0; - rxPool_dataHoppingConfig_numSubbands = "ns1"; - rxPool_dataHoppingConfig_rbOffset = 0; - rxPool_commTxResourceUC-ReqAllowed = "TRUE"; - */ - - // Parameters for SIB19 - discRxPool_cp_Len = "normal" - discRxPool_discPeriod = "rf32" - discRxPool_numRetx = 1; - discRxPool_numRepetition = 2; - discRxPool_ResourceConfig_prb_Num = 5; - discRxPool_ResourceConfig_prb_Start = 3; - discRxPool_ResourceConfig_prb_End = 21; - discRxPool_ResourceConfig_offsetIndicator_present = "prSmall"; - discRxPool_ResourceConfig_offsetIndicator_choice = 0; - discRxPool_ResourceConfig_subframeBitmap_present = "prBs40"; - discRxPool_ResourceConfig_subframeBitmap_choice_bs_buf = "f0ffffffff"; - discRxPool_ResourceConfig_subframeBitmap_choice_bs_size = 5; - discRxPool_ResourceConfig_subframeBitmap_choice_bs_bits_unused = 0; - } - ); - - - srb1_parameters : - { - # timer_poll_retransmit = (ms) [5, 10, 15, 20,... 250, 300, 350, ... 500] - timer_poll_retransmit = 80; - - # timer_reordering = (ms) [0,5, ... 100, 110, 120, ... ,200] - timer_reordering = 35; - - # timer_reordering = (ms) [0,5, ... 250, 300, 350, ... ,500] - timer_status_prohibit = 0; - - # poll_pdu = [4, 8, 16, 32 , 64, 128, 256, infinity(>10000)] - poll_pdu = 4; - - # poll_byte = (kB) [25,50,75,100,125,250,375,500,750,1000,1250,1500,2000,3000,infinity(>10000)] - poll_byte = 99999; - - # max_retx_threshold = [1, 2, 3, 4 , 6, 8, 16, 32] - max_retx_threshold = 4; - } - - # ------- SCTP definitions - SCTP : - { - # Number of streams to use in input/output - SCTP_INSTREAMS = 2; - SCTP_OUTSTREAMS = 2; - }; - - - ////////// MME parameters: - mme_ip_address = ({ ipv4 = "127.0.1.10"; }); - - NETWORK_INTERFACES : { - ENB_IPV4_ADDRESS_FOR_S1_MME = "127.0.1.30/24"; - ENB_IPV4_ADDRESS_FOR_S1U = "127.0.1.30/24"; - ENB_PORT_FOR_S1U = 2152; # Spec 2152 - ENB_IPV4_ADDRESS_FOR_X2C = "127.0.1.30/24"; - ENB_PORT_FOR_X2C = 36422; # Spec 36422 - }; - } -); - -log_config = { - global_log_level = "info"; - pdcp_log_level = "info"; - rrc_log_level = "info"; - gtp_log_level = "info"; -}; diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/du.lte.band7.10MHz.if4p5.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/du.lte.band7.10MHz.if4p5.conf deleted file mode 100644 index f66bc1b29e..0000000000 --- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/du.lte.band7.10MHz.if4p5.conf +++ /dev/null @@ -1,102 +0,0 @@ -Active_eNBs = ( "eNB-Eurecom-DU"); -# Asn1_verbosity, choice in: none, info, annoying -Asn1_verbosity = "none"; - -eNBs = -( - { - ////////// Identification parameters: - eNB_CU_ID = 0xe00; - - eNB_name = "eNB-Eurecom-DU"; - - // Tracking area code, 0x0000 and 0xfffe are reserved values - tracking_area_code = 1; - plmn_list = ( { mcc = 208; mnc = 95; mnc_length = 2; } ) - - nr_cellid = 12345678L - - ////////// Physical parameters: - - component_carriers = ( - { - node_function = "3GPP_eNODEB"; - node_timing = "synch_to_ext_device"; - node_synch_ref = 0; - frame_type = "FDD"; - tdd_config = 3; - tdd_config_s = 0; - prefix_type = "NORMAL"; - eutra_band = 7; - downlink_frequency = 2665000000L; - uplink_frequency_offset = -120000000; - Nid_cell = 0; - N_RB_DL = 50; - Nid_cell_mbsfn = 0; - nb_antenna_ports = 1; - nb_antennas_tx = 1; - nb_antennas_rx = 1; - tx_gain = 90; - rx_gain = 125; - - pucch_deltaF_Format1 = "deltaF2"; - pucch_deltaF_Format1b = "deltaF3"; - pucch_deltaF_Format2 = "deltaF0"; - pucch_deltaF_Format2a = "deltaF0"; - pucch_deltaF_Format2b = "deltaF0"; - } - ); - - - # ------- SCTP definitions - SCTP : - { - # Number of streams to use in input/output - SCTP_INSTREAMS = 2; - SCTP_OUTSTREAMS = 2; - }; - } -); - -MACRLCs = ( - { - num_cc = 1; - tr_s_preference = "local_L1"; - tr_n_preference = "f1"; - remote_n_address = "127.0.0.4"; - local_n_address = "127.0.0.3"; - local_n_portc = 500; - remote_n_portc = 501; - local_n_portd = 600; - remote_n_portd = 601; - } -); - -L1s = ( - { - num_cc = 1; - tr_n_preference = "local_mac"; - } -); - -RUs = ( - { - local_rf = "yes"; - nb_tx = 1; - nb_rx = 1; - att_tx = 10; - att_rx = 10; - bands = [7]; - max_pdschReferenceSignalPower = -27; - max_rxgain = 125; - eNB_instances = [0]; - } -); - -log_config = { - global_log_level = "info"; - hw_log_level = "info"; - phy_log_level = "info"; - mac_log_level = "info"; - rlc_log_level = "info"; -}; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf index 1666ea4a70..3261f716c7 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf @@ -22,9 +22,7 @@ gNBs = local_s_address = "127.0.0.4"; remote_s_address = "127.0.0.3"; - local_s_portc = 501; local_s_portd = 2152; - remote_s_portc = 500; remote_s_portd = 2152; # ------- SCTP definitions diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf index 02b1e89b88..ef180cab63 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf @@ -168,9 +168,7 @@ MACRLCs = ( tr_n_preference = "f1"; local_n_address = "127.0.0.3"; remote_n_address = "127.0.0.4"; - local_n_portc = 500; local_n_portd = 2152; - remote_n_portc = 501; remote_n_portd = 2152; } diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf index f57725cc9b..102c23018e 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-cu.sa.f1.conf @@ -17,12 +17,9 @@ gNBs = tr_s_preference = "f1"; - local_s_if_name = "lo"; local_s_address = "127.0.0.3"; remote_s_address = "0.0.0.0"; # multiple DUs - local_s_portc = 501; local_s_portd = 2152; - remote_s_portc = 500; remote_s_portd = 2152; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci0.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci0.conf index c2e28725bd..f2ac964033 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci0.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci0.conf @@ -163,12 +163,9 @@ MACRLCs = ({ num_cc = 1; tr_s_preference = "local_L1"; tr_n_preference = "f1"; - local_n_if_name = "lo"; local_n_address = "127.0.0.4"; remote_n_address = "127.0.0.3"; - local_n_portc = 500; local_n_portd = 2152; - remote_n_portc = 501; remote_n_portd = 2152; pusch_FailureThres = 1000; }); diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci1.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci1.conf index c5c84c9919..96acca0d10 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci1.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band66.25prb.rfsim.pci1.conf @@ -163,12 +163,9 @@ MACRLCs = ({ num_cc = 1; tr_s_preference = "local_L1"; tr_n_preference = "f1"; - local_n_if_name = "lo"; local_n_address = "127.0.0.5"; remote_n_address = "127.0.0.3"; - local_n_portc = 500; local_n_portd = 2152; - remote_n_portc = 501; remote_n_portd = 2152; pusch_FailureThres = 1000; }); diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.4x4-benetel650.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.4x4-benetel650.conf index a267d2e864..5c6506beb3 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.4x4-benetel650.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.4x4-benetel650.conf @@ -172,12 +172,9 @@ MACRLCs = ( num_cc = 1; tr_s_preference = "local_L1"; tr_n_preference = "f1"; - local_n_if_name = "enp193s0f0"; local_n_address = "172.21.16.51"; remote_n_address = "172.21.6.90"; - local_n_portc = 500; local_n_portd = 2153; - remote_n_portc = 501; remote_n_portd = 2153; pusch_TargetSNRx10 = 170; pucch_TargetSNRx10 = 230; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650-mplane.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650-mplane.conf index 28f9baf6f8..522266a70e 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650-mplane.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650-mplane.conf @@ -172,12 +172,9 @@ MACRLCs = ( num_cc = 1; tr_s_preference = "local_L1"; tr_n_preference = "f1"; - local_n_if_name = "enp193s0f0"; local_n_address = "172.21.16.51"; remote_n_address = "172.21.6.90"; - local_n_portc = 500; local_n_portd = 2153; - remote_n_portc = 501; remote_n_portd = 2153; pusch_TargetSNRx10 = 120; pucch_TargetSNRx10 = 200; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650.conf index 18f885945f..90692c7b8e 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650.conf @@ -172,12 +172,9 @@ MACRLCs = ( num_cc = 1; tr_s_preference = "local_L1"; tr_n_preference = "f1"; - local_n_if_name = "enp193s0f0"; local_n_address = "172.21.16.51"; remote_n_address = "172.21.6.90"; - local_n_portc = 500; local_n_portd = 2153; - remote_n_portc = 501; remote_n_portd = 2153; pusch_TargetSNRx10 = 120; pucch_TargetSNRx10 = 200; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf index 3221813bef..fa456a03cf 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf @@ -160,12 +160,9 @@ MACRLCs = ({ num_cc = 1; tr_s_preference = "local_L1"; tr_n_preference = "f1"; - local_n_if_name = "lo"; local_n_address = "127.0.0.4"; remote_n_address = "127.0.0.3"; - local_n_portc = 500; local_n_portd = 2152; - remote_n_portc = 501; remote_n_portd = 2152; pusch_FailureThres = 1000; }); diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf index c7d8cb3269..350ce578d3 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci1.conf @@ -160,12 +160,9 @@ MACRLCs = ({ num_cc = 1; tr_s_preference = "local_L1"; tr_n_preference = "f1"; - local_n_if_name = "lo"; local_n_address = "127.0.0.5"; remote_n_address = "127.0.0.3"; - local_n_portc = 500; local_n_portd = 2152; - remote_n_portc = 501; remote_n_portd = 2152; pusch_FailureThres = 1000; }); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000000..cefa52d456 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(nr-cuup) diff --git a/tests/nr-cuup/CMakeLists.txt b/tests/nr-cuup/CMakeLists.txt new file mode 100644 index 0000000000..02becb2496 --- /dev/null +++ b/tests/nr-cuup/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(nr-cuup-load-test nr-cuup-load-test.c) +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 diff --git a/tests/nr-cuup/README.md b/tests/nr-cuup/README.md new file mode 100644 index 0000000000..3095e5f600 --- /dev/null +++ b/tests/nr-cuup/README.md @@ -0,0 +1,78 @@ +This is a simple tester for the CU-UP. It configures the CU-UP via E1, and can +stream data via GTP in uplink and downlink directions. + +[[_TOC_]] + +# Overview + +From a schematic point of view, the tester/CU-UP interaction looks like this: + +``` + +-------+ ++------>| CU-UP |<------+ +| +-------+ | +| | | +| F1-U | E1 | NG-U +| (data) | (control) | (data) +| | | +| +-------+ | ++------>|tester |<------+ + +-------+ +``` + +The tester is for performance testing of a CU-UP, and behaves like an +integrated DU/CU-CP/UPF from the CU-UP's point of view. + +The tester and CU-UP have an E1 connection through which control information is +exchanged, e.g., configuration of UEs/bearers and associated information. In +other words, towards the CU-UP, the tester appears like a CU-CP. + +Further, for each UE/bearer, CU-UP is configured with GTP IP information and +the corresponding TEID. The CU-UP will forward downlink data arriving on NG-U +on the F1-U interface, and uplink data arriving on F1-U on the NG-U interface. +In other words, on the F1-U interface, the tester appears like a DU to the +CU-UP, and on the NG-U interface, the tester appears like a UPF to the CU-UP. + +A test scenario is fixed to these steps: + +1. The tester sets up a number of UEs via the E1 interface (it is possible to + see the message exchange in Wireshark). +2. The tester streams data in downlink/uplink (from the CU-UP's point of view) + and measures how much data is lost. +3. The tester releases all the UEs via the E1 interface, and disconnects from + the CU-UP. + +Note: The tester uses the same GTP module as the CU-UP. Thus, data might not +only be lost at the GTP interface of the CU-UP, but also at the tester. + +# Usage + +You can build the CU-UP and the tester like so: + + cd ~/openairinterface5g + mkdir build && cd build && cmake .. -GNinja && ninja nr-cuup nr-cuup-load-test params_libconfig + ./tests/nr-cuup/nr-cuup-load-test + ./nr-cuup -O ../tests/nr-cuup/load-test.conf + +This builds both tester and CU-UP in the directory `build/`, then starts the +load tester with default values + +- 1 UE +- 10s of Test +- 60 Mbps of traffic in both downlik/uplink with a packet size of 1400 bytes + +To see the available options, run + + ./tests/nr-cuup/nr-cuup-load-test -h + +The configuration file [`load-test.conf`](./load-test.conf) matches the default +tester configuration (F1-U GTP traffic over non-standard port 2153, NG-U GTP +over 2152, tests on localhost). The configuration file includes this +non-standard port 2153 as the GTP module, as of now, cannot bind on the same +interface and port for both F1-U and NG-U due to internal limitations. + +# Limitations + +- The tester does not yet create/remove UEs during a traffic test. +- It might be possible to integrate with an external GTP traffic generator, + but we did not test this. diff --git a/tests/nr-cuup/load-test.conf b/tests/nr-cuup/load-test.conf new file mode 100644 index 0000000000..9829640a02 --- /dev/null +++ b/tests/nr-cuup/load-test.conf @@ -0,0 +1,58 @@ +Active_gNBs = ( "gNB-OAI"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + gNB_CU_UP_ID = 0xe00; + +# cell_type = "CELL_MACRO_GNB"; + + gNB_name = "gNB-OAI"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ mcc = 222; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1, sd = 0xffffff }) }); + + + tr_s_preference = "f1"; + + local_s_address = "127.0.0.6"; + remote_s_address = "127.0.0.1"; + local_s_portc = 501; + local_s_portd = 2153; + remote_s_portc = 500; + remote_s_portd = 2153; + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + E1_INTERFACE = + ( + { + type = "up"; + ipv4_cucp = "127.0.0.1"; + ipv4_cuup = "127.0.0.6"; + } + ) + + NETWORK_INTERFACES : + { + GNB_IPV4_ADDRESS_FOR_NG_AMF = "172.21.16.109"; + GNB_IPV4_ADDRESS_FOR_NGU = "127.0.0.7"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + } +); + +log_config: { + global_log_level = "info"; +}; diff --git a/tests/nr-cuup/nr-cuup-load-test.c b/tests/nr-cuup/nr-cuup-load-test.c new file mode 100644 index 0000000000..0c417ca5aa --- /dev/null +++ b/tests/nr-cuup/nr-cuup-load-test.c @@ -0,0 +1,673 @@ +/* + * 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/utils/LOG/log.h" +#include "common/utils/ocp_itti/intertask_interface.h" // this pulls in all of OAI+4G+5G +#include "openair3/SCTP/sctp_eNB_task.h" +#include "openair2/E1AP/e1ap.h" +#include "openair3/ocp-gtpu/gtp_itf.h" + +configmodule_interface_t *uniqCfg; + +#define DL 0 +#define UL 1 + +int nr_rlc_get_available_tx_space(const rnti_t rntiP, const logical_chan_id_t channel_idP) { abort(); return 0; } /* in GTP */ +softmodem_params_t *get_softmodem_params(void) { static softmodem_params_t p = {0}; return &p; }; /* in ITTI */ +void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req) { abort(); } /* CU-UP */ +void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req) { abort(); } /* CU-UP */ +void e1_bearer_release_cmd(const e1ap_bearer_release_cmd_t *cmd) { abort(); } /* CU-UP */ +void e1_reset(void) { abort(); } /* CU-UP */ +instance_t *N3GTPUInst = NULL; /* CU-UP */ +instance_t CUuniqInstance=0; /* CU-UP */ + +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(); +} + +/* @brief Dummy function to pass a fptr to ITTI for staring RRC queue, without + * actually handling any messages. Functions called by main() will send/receive + * ITTI messages to E1, as if it was RRC. */ +static void *cuup_tester_rrc(void *) +{ + itti_mark_task_ready(TASK_RRC_GNB); + LOG_I(GNB_APP, "created RRC queue\n"); + return NULL; +} + +/* @brief Initialize E1 interface (as if it was CU-CP) */ +static void init_e1() +{ + net_ip_address_t ip = {.ipv4 = 1, .ipv4_address = "127.0.0.1" }; + e1ap_net_config_t conf = { .CUCP_e1_ip_address = ip }; + MessageDef *msg = itti_alloc_new_message(TASK_GNB_APP, 0, E1AP_REGISTER_REQ); + e1ap_net_config_t *e1ap_nc = &E1AP_REGISTER_REQ(msg).net_config; + *e1ap_nc = conf; + itti_send_msg_to_task(TASK_CUCP_E1, 0, msg); +} + +/* @brief Setup CU-UP connection and retrieve parameters for further + * connection. + * + * Waits for E1 Setup Request, sends response, and returns SCTP association ID + * and NSSAI used by this CU-UP. + */ +static void setup_cuup(sctp_assoc_t *assoc_id, e1ap_nssai_t *nssai) +{ + /* Wait for E1AP setup request, store CU-UP info */ + MessageDef *itti_req = NULL; + itti_receive_msg(TASK_RRC_GNB, &itti_req); + MessagesIds id = ITTI_MSG_ID(itti_req); + AssertFatal(id == E1AP_SETUP_REQ, "expected E1AP setup request, received %s instead\n", itti_get_message_name(id)); + *assoc_id = itti_req->ittiMsgHeader.originInstance; + e1ap_setup_req_t *req = &E1AP_SETUP_REQ(itti_req); + long t_id = req->transac_id; + DevAssert(req->supported_plmns = 1); + DevAssert(req->plmn[0].slice != NULL); + *nssai = *req->plmn[0].slice; + DevAssert(assoc_id != 0); + itti_free(TASK_GNB_APP, itti_req); + + // acknowledge the E1 setup request with a response + MessageDef *itti_resp = itti_alloc_new_message(TASK_GNB_APP, 0, E1AP_SETUP_RESP); + itti_resp->ittiMsgHeader.originInstance = *assoc_id; + e1ap_setup_resp_t *resp = &E1AP_SETUP_RESP(itti_resp); + resp->transac_id = t_id; + itti_send_msg_to_task(TASK_CUCP_E1, 0, itti_resp); +} + +/* @brief get E1AP bearer setup request based on parameters */ +static e1ap_bearer_setup_req_t get_breq(uint32_t ue_id, long pdu_id, long drb_id, const e1ap_nssai_t *nssai, const UP_TL_information_t *tnl) +{ + e1ap_bearer_setup_req_t bearer_req = { + .gNB_cu_cp_ue_id = ue_id, + .secInfo.cipheringAlgorithm = 0, /* no ciphering/NEA0 */ + .secInfo.integrityProtectionAlgorithm = 0, /* no integrity/NIA0 */ + /* encryptionKey not needed */ + /* integrityProtectionKey not needed */ + .numPDUSessions = 1, + }; + pdu_session_to_setup_t *pdu = &bearer_req.pduSession[0]; + pdu->sessionId = pdu_id; + pdu->nssai = *nssai; + pdu->securityIndication.integrityProtectionIndication = SECURITY_NOT_NEEDED; + pdu->securityIndication.confidentialityProtectionIndication = SECURITY_NOT_NEEDED; + pdu->UP_TL_information = *tnl; + pdu->numDRB2Setup = 1; + DRB_nGRAN_to_setup_t *drb = &pdu->DRBnGRanList[0]; + drb->id = drb_id; + drb->numQosFlow2Setup = 1; + drb->numCellGroups = 1; + + return bearer_req; +} + +/* @brief check for packet loss by verifing the sequence number. Can handle + * packet losses and reordering. + * @param payload the sequence number of the current packet + * @param expected the expected sequence number + * @param [inout] pointer to number of lost packets, which will be updated by + * the difference between payload and expected. + * @return the next expected packet number. */ +static uint32_t estimate_packetloss(const uint32_t payload, uint32_t expected, uint32_t *lost) +{ + if (payload == expected) { + /* all good */ + return expected + 1; + } else if (payload > expected) { + /* there was packet loss, assume all packets in between have been lost */ + uint32_t diff = payload - expected; + *lost += diff; + LOG_W(GNB_APP, "packet loss: expected %d received %d diff %d (total loss %d)\n", expected, payload, diff, *lost); + return payload + 1; + } else { /* payload < expected */ + /* reordered: we assumed current packet to be lost, so reduce the + * loss for this packet as it arrived in the end. */ + *lost -= 1; + LOG_W(GNB_APP, "packet reordering occurred: expected %d received %d\n", expected, payload); + return expected; /* we still wait for the next packet */ + } +} + +static struct ue_stat { + uint32_t count; + uint32_t lost; + uint64_t received; +} ue_stat[10][2] = {0}; +/* @brief ulReceived callback for F1-U + * + * Checks for packet loss by verifying that the first 4 bytes are corresponding + * to a continuous 4-byte (word) enumeration. */ +static bool recv_ng(protocol_ctxt_t *ctxt, + const ue_id_t ue_id, + const srb_flag_t flag, + const rb_id_t rb, + 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) +{ + uint32_t *payload = (uint32_t *) buf; + DevAssert(size % 4 == 0); + struct ue_stat *s = &ue_stat[ue_id][UL]; + s->count = estimate_packetloss(*payload, s->count, &s->lost); + s->received += size; + + return true; +} + +/* @brief Set up the NG-U (north-bound) GTP tunnel, and inform CU-UP via E1 */ +static up_params_t setup_cuup_ue_ng(sctp_assoc_t assoc_id, const e1ap_nssai_t *nssai, uint32_t ue_id, instance_t gtp_inst, const char *ip) +{ + long pdu_id = 1; + long drb_id = pdu_id + 3; + in_addr_t addr_lo; + inet_pton(AF_INET, ip, &addr_lo); + + /* create the local tunnel (i.e., as if we were UPF) */ + transport_layer_addr_t null_addr = {.length = 32}; + teid_t teid_lo = newGtpuCreateTunnel(gtp_inst, ue_id, pdu_id, pdu_id, -1, -1, null_addr, NULL, recv_ng); + UP_TL_information_t tnl = {.teId = teid_lo}; + memcpy(&tnl.tlAddress, &addr_lo, 4); + + /* Create a new bearer */ + MessageDef *itti_breq = itti_alloc_new_message(TASK_GNB_APP, 0, E1AP_BEARER_CONTEXT_SETUP_REQ); + itti_breq->ittiMsgHeader.originInstance = assoc_id; + e1ap_bearer_setup_req_t *breq = &E1AP_BEARER_CONTEXT_SETUP_REQ(itti_breq); + *breq = get_breq(ue_id, pdu_id, drb_id, nssai, &tnl); + itti_send_msg_to_task(TASK_CUCP_E1, 0, itti_breq); + + /* Wait for the ack */ + MessageDef *itti_bresp; + itti_receive_msg(TASK_RRC_GNB, &itti_bresp); + MessagesIds id = ITTI_MSG_ID(itti_bresp); + AssertFatal(id == E1AP_BEARER_CONTEXT_SETUP_RESP, "expected E1AP bearer context setup response, received %s instead\n", itti_get_message_name(id)); + DevAssert(assoc_id == itti_bresp->ittiMsgHeader.originInstance); + e1ap_bearer_setup_resp_t *bresp = &E1AP_BEARER_CONTEXT_SETUP_RESP(itti_bresp); + DevAssert(bresp->gNB_cu_cp_ue_id == ue_id); + DevAssert(bresp->gNB_cu_up_ue_id == ue_id); + DevAssert(bresp->numPDUSessions == 1); + teid_t teid_rm = bresp->pduSession[0].tl_info.teId; + in_addr_t addr_rm; + memcpy(&addr_rm, &bresp->pduSession[0].tl_info.tlAddress, 4); + DevAssert(bresp->pduSession[0].numDRBSetup == 1); + DRB_nGRAN_setup_t *drb = &bresp->pduSession[0].DRBnGRanList[0]; + DevAssert(drb->id == drb_id); + DevAssert(drb->numUpParam == 1); + up_params_t f1_up = drb->UpParamList[0]; + itti_free(TASK_GNB_APP, itti_bresp); + + /* print diagnostics */ + char ip_lo[32] = {0}; + inet_ntop(AF_INET, &addr_lo, ip_lo, sizeof(ip_lo)); + char ip_rm[32] = {0}; + inet_ntop(AF_INET, &addr_rm, ip_rm, sizeof(ip_rm)); + GtpuUpdateTunnelOutgoingAddressAndTeid(gtp_inst, ue_id, pdu_id, addr_rm, teid_rm); + LOG_I(GNB_APP, "CU-UP created NG-U, local %s/%x remote %s/%x\n", ip_lo, teid_lo, ip_rm, teid_rm); + + inet_ntop(AF_INET, &f1_up.tl_info.tlAddress, ip_rm, sizeof(ip_rm)); + LOG_I(GNB_APP, "CU-UP created F1-U, remove %s/%x\n", ip_rm, f1_up.tl_info.teId); + return f1_up; +} + +/* @brief get E1AP bearer modification request based on parameters */ +static e1ap_bearer_mod_req_t get_bmod(uint32_t ue_id, long pdu_id, up_params_t up) +{ + e1ap_bearer_mod_req_t bearer_mod = { + .gNB_cu_cp_ue_id = ue_id, + .gNB_cu_up_ue_id = ue_id, + .numPDUSessionsMod = 1, + }; + pdu_session_to_mod_t *pdum = &bearer_mod.pduSessionMod[0]; + pdum->sessionId = pdu_id; + pdum->numDRB2Modify = 1; + DRB_nGRAN_to_mod_t *drb = &pdum->DRBnGRanModList[0]; + drb->id = 4; + drb->numDlUpParam = 1; + drb->DlUpParamList[0] = up; + + return bearer_mod; +} + +/* @brief Received callback for F1-U + * + * Checks for packet loss by verifying that the first 4 bytes are corresponding + * to a continuous 4-byte (word) enumeration. */ +static bool recv_f1(protocol_ctxt_t *ctxt, + const srb_flag_t flag, + const rb_id_t rb, + 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) +{ + int skip_bytes = 2; + uint32_t *payload = (uint32_t *) &buf[skip_bytes]; + DevAssert((size - skip_bytes) % 4 == 0); + int ue_id = ctxt->rntiMaybeUEid; + struct ue_stat *s = &ue_stat[ue_id][DL]; + s->count = estimate_packetloss(*payload, s->count, &s->lost); + s->received += size - skip_bytes; + + return true; +} + +/* @brief Set up the F1-U (south-bound) GTP tunnel, and inform CU-UP via E1 */ +static void setup_cuup_ue_f1(sctp_assoc_t assoc_id, uint32_t ue_id, instance_t gtp_inst, const char *ip, up_params_t rm) +{ + int pdu_id = 1; + int drb_id = pdu_id + 3; + + /* create tunnel (i.e., as if we were DU) */ + transport_layer_addr_t addr = {.length = 32}; + memcpy(addr.buffer, &rm.tl_info.tlAddress, 4); + teid_t teid_lo = newGtpuCreateTunnel(gtp_inst, ue_id, drb_id, drb_id, rm.tl_info.teId, -1, addr, recv_f1, NULL); + + up_params_t lo = {.tl_info.teId = teid_lo, .cell_group_id = rm.cell_group_id}; + inet_pton(AF_INET, ip, &lo.tl_info.tlAddress); + + /* modify existing tunnel via E1 to pass in F1-U TEID */ + MessageDef *itti_bmod = itti_alloc_new_message(TASK_GNB_APP, 0, E1AP_BEARER_CONTEXT_MODIFICATION_REQ); + itti_bmod->ittiMsgHeader.originInstance = assoc_id; + e1ap_bearer_mod_req_t *bmod = &E1AP_BEARER_CONTEXT_MODIFICATION_REQ(itti_bmod); + *bmod = get_bmod(ue_id, pdu_id, lo); + itti_send_msg_to_task(TASK_CUCP_E1, 0, itti_bmod); + + /* Wait for the ack */ + MessageDef *itti_bmodr; + itti_receive_msg(TASK_RRC_GNB, &itti_bmodr); + MessagesIds id = ITTI_MSG_ID(itti_bmodr); + AssertFatal(id == E1AP_BEARER_CONTEXT_MODIFICATION_RESP, "expected E1AP bearer context modification response, received %s instead\n", itti_get_message_name(id)); + //DevAssert(assoc_id == itti_bmodr->ittiMsgHeader.originInstance); + itti_free(TASK_GNB_APP, itti_bmodr); + + /* print diagnostics */ + char ip_lo[32] = {0}; + inet_ntop(AF_INET, &lo.tl_info.tlAddress, ip_lo, sizeof(ip_lo)); + char ip_rm[32] = {0}; + inet_ntop(AF_INET, &rm.tl_info.tlAddress, ip_rm, sizeof(ip_rm)); + LOG_I(GNB_APP, "CU-UP created F1-U, local %s/%x remote %s/%x\n", ip_lo, lo.tl_info.teId, ip_rm, rm.tl_info.teId); +} + +/* @brief release the UE over E1, and delete corresponding GTP tunnels */ +static void e1_release_ue(sctp_assoc_t assoc_id, uint32_t ue_id, instance_t f1_inst, instance_t ng_inst) +{ + MessageDef *itti_cmd = itti_alloc_new_message(TASK_GNB_APP, 0, E1AP_BEARER_CONTEXT_RELEASE_CMD); + itti_cmd->ittiMsgHeader.originInstance = assoc_id; + e1ap_bearer_release_cmd_t *cmd = &E1AP_BEARER_CONTEXT_RELEASE_CMD(itti_cmd); + cmd->gNB_cu_cp_ue_id = cmd->gNB_cu_up_ue_id = ue_id; + cmd->cause.type = E1AP_CAUSE_RADIO_NETWORK; + cmd->cause.value = E1AP_RADIO_CAUSE_NORMAL_RELEASE; + itti_send_msg_to_task(TASK_CUCP_E1, 0, itti_cmd); + + /* Wait for the ack */ + MessageDef *itti_cplt; + itti_receive_msg(TASK_RRC_GNB, &itti_cplt); + MessagesIds id = ITTI_MSG_ID(itti_cplt); + AssertFatal(id == E1AP_BEARER_CONTEXT_RELEASE_CPLT, "expected E1AP bearer context release complete, received %s instead\n", itti_get_message_name(id)); + itti_free(TASK_GNB_APP, itti_cplt); + + newGtpuDeleteAllTunnels(f1_inst, ue_id); + newGtpuDeleteAllTunnels(ng_inst, ue_id); +} + +/* @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); +} + +/* @brief calculate time difference of two struct timespec */ +static float time_diff(struct timespec s, struct timespec e) +{ + //printf("start %ld.%ld end %ld.%ld\n", s.tv_sec, s.tv_nsec, e.tv_sec, e.tv_nsec); + float dur = e.tv_sec - s.tv_sec + (e.tv_nsec - s.tv_nsec) / 1000000000.f; + //printf("duration %f s %f us\n", dur, dur * 1000000.f); + return dur * 1000000.f; +} + +enum pdcp_len {PDCP_SN_LEN_12, PDCP_SN_LEN_18}; +/* @brief generate hardcoded PDCP header in case of UL transmission */ +static void write_pdcp_header(uint32_t sn, enum pdcp_len len, uint8_t *buf) +{ + DevAssert(len == PDCP_SN_LEN_12); + /* see nr_pdcp_entity_process_sdu() or 38.323 sec 6.2.2.2 */ + buf[0] = 0x80 | ((sn >> 8) & 0xf); + buf[1] = sn & 0xff; + //printf("%02x %02x\n", buf[0], buf[1]); +} + +/* @brief the configuration for a thread to send GTP traffic */ +struct thr_data { + /// pthread ID to join this thread + pthread_t t; + + /* in */ + /// GTP instance to send on + instance_t inst; + /// UE ID to be used + ue_id_t ue_id; + /// flag to decide if PDCP/SDAP header is to be prepended + bool is_ul; + /// length of data packet + unsigned int data_len; + /// target throughput to reach + uint64_t throughput; + /// duration of the data traffic to last + unsigned int duration; + + /* out */ + /// exact measured transmission time + float total_time; + /// exact measured data that has been sent + uint64_t total_data; +}; +/* @brief Main traffic generation/transmission function. + * + * The function calculates the amount of packets to be sent during each TTI + * (one millisecond) (as a float, to send the exact amount of packets). It then + * enters a loop, in which (1) it sends the packets of the current TTI, possibly + * including additional packets if they could not be sent in the last TTI, (2) + * measure the time since the last TTI boundary, and sleep, if necessary, to + * adjust to the exact throughput (3) update for additional packets to send if + * the last TTI took too long. The last point ensure that we send exactly an + * exact amount of data, possibly taking longer than specified. + * + * @param v pointer to generation function of type `struct thr_data`. + * @return always NULL */ +static void *sender_thread(void *v) +{ + struct thr_data *d = v; + + size_t oh = d->is_ul ? 3 : 0; + size_t packet_len = d->data_len + oh; + uint8_t buf[packet_len]; + memset(buf, 0, packet_len); + uint32_t *payload = (uint32_t *)(buf + oh); + uint64_t to_send = (float)d->throughput / 8.0 * d->duration; + const float pps_1ms = (float) d->throughput / 8.0 / d->data_len / 1000; + float pps_1ms_nextwin = pps_1ms; + int bearer = d->is_ul ? 4 : 1; + + + //printf("sending %ld Mbps for %ld s => data %ld, %.3f pps\n", (float) d->throughput / 1000000.0, d->duration, to_send, pps_1ms); + + int i_1ms = 0; + struct timespec start; + clock_gettime(CLOCK_MONOTONIC, &start); + size_t packet_count = 0; + while (true) { + int pps_now = pps_1ms_nextwin; + pps_1ms_nextwin = pps_1ms + pps_1ms_nextwin - (float) pps_now; + //printf("this window send %d packets next window %.3f (pps_1ms %.3f)\n", pps_now, pps_1ms_nextwin, pps_1ms); + for (uint32_t i = 0; i < pps_now; ++i) { + if (d->is_ul) + write_pdcp_header(packet_count, PDCP_SN_LEN_12, buf); + *payload = packet_count++; + gtpv1uSendDirect(d->inst, d->ue_id, bearer, (uint8_t *)buf, packet_len, false, false); + d->total_data += d->data_len; + } + + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + float diff_us = time_diff(start, t); + int us_passed = diff_us - i_1ms*1000; + int iter_advance = us_passed / 1000 + 1; + int wait_usec = 1000 - us_passed; + //printf("diff %.3fus i_1ms %d us_passed %d iter %d wait %d (%.3f %%)\n", diff_us, i_1ms, us_passed, iter_advance, wait_usec, 100.0 * d->total_data / to_send); + DevAssert(iter_advance > 0); + if (wait_usec > 0) + usleep(wait_usec); + + if (iter_advance > 1) /* we have to send more in the next window */ + pps_1ms_nextwin += (iter_advance - 1) * pps_1ms_nextwin; + i_1ms += iter_advance; + if (d->total_data >= to_send) + break; + } + struct timespec end; + clock_gettime(CLOCK_MONOTONIC, &end); + d->total_time = time_diff(start, end) / 1000000.f; + return NULL; +} + +/* @brief print statistics of a UE (amount of data, achieved rate, if data was + * lost). */ +static bool print_stats(const char *pref, int ue_id, uint64_t total_data, float duration, const struct ue_stat *s) +{ + float tthr = 8.0 * total_data / duration / 1000000.f; + float rthr = 8.0 * s->received / duration / 1000000.f; + LOG_I(GNB_APP, "%d %s sent %ld in %.3f s received %ld bytes => rates [Mbps] transmit %.3f receive %.3f\n", + ue_id, + pref, + total_data, + duration, + s->received, + tthr, + rthr); + if (total_data == s->received) { + DevAssert(s->lost == 0); + LOG_A(GNB_APP, "%d %s received all data sent\n", ue_id, pref); + return true; + } else { + uint64_t m = total_data - s->received; + LOG_W(GNB_APP, "%d %s warning: missing %ld bytes (%.3f %%, %d packets)\n", ue_id, pref, m, 100.f * m / total_data, s->lost); + return false; + } +} + +/* @brief start a thread if specified through template, and in direction of + * given GTP bearer. + * @param template specifies if thread is to be started (throughput and + * duration are positive). If so, the template data is stored inside the + * thread. + * @param thr place to store thread data + * @param b the GTP bearer to use */ +static struct thr_data *start_thread(const struct thr_data *template, struct thr_data *thr, instance_t inst, ue_id_t ue_id) +{ + if (template->throughput == 0 || template->duration == 0) + return NULL; + + *thr = *template; + thr->inst = inst; + thr->ue_id = ue_id; + pthread_create(&thr->t, NULL, sender_thread, thr); + return thr; +} + +/* @brief print progress of a thread specified in thr_data, and prepend DL/UL + * based on dir. data access to thread data is not private, but we only read, + * so don't care. */ +static bool print_progress(const int dir, const struct thr_data *obs) +{ + if (!obs) + return false; + + uint64_t target = (uint64_t) obs->throughput / 8 * obs->duration; + float progress = (float)obs->total_data / target; + printf(" %s sent %3.f %%", dir == DL ? "DL" : "UL", 100.0 * progress); + return progress < 0.99; +} + +int main(int argc, char *argv[]) +{ + if ((uniqCfg = load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY)) == NULL) { + exit_fun("[SOFTMODEM] Error, configuration module init failed\n"); + } + logInit(); + itti_init(TASK_MAX, tasks_info); + + unsigned int n_ue; + unsigned int duration_s; + unsigned int packet_size; + uint64_t target_rate_dl; + uint64_t target_rate_ul; + char *ng_ip = malloc_or_fail(100); + uint16_t ng_port; + char *f1_ip = malloc_or_fail(100); + uint16_t f1_port; + + paramdef_t param_def[] = { + {"n", "number of UEs\n", 0, .uptr = &n_ue, .defuintval = 1, TYPE_UINT}, + {"t", "duration of test (s)\n", 0, .uptr = &duration_s, .defuintval = 10, TYPE_UINT}, + {"p", "packet size\n", 0, .uptr = &packet_size, .defuintval = 1400, TYPE_UINT}, + {"d", "DL target rate (Mbps, per UE)\n", 0, .u64ptr = &target_rate_dl, .defint64val = 60, TYPE_UINT64}, + {"u", "UL target rate (Mbps, per UE)\n", 0, .u64ptr = &target_rate_ul, .defint64val = 60, TYPE_UINT64}, + {"ng-ip", "Tester NG-U IP\n", 0, .strptr = &ng_ip, .defstrval = "127.0.0.1", TYPE_STRING, 100}, + {"ng-port", "Tester NG-U port\n", 0, .u16ptr = &ng_port, .defuintval = 2152, TYPE_UINT16}, + {"f1-ip", "Tester F1-U IP\n", 0, .strptr = &f1_ip, .defstrval = "127.0.0.1", TYPE_STRING, 100}, + {"f1-port", "Tester F1-U port\n", 0, .u16ptr = &f1_port, .defuintval = 2153, TYPE_UINT16}, + }; + config_get(uniqCfg, param_def, sizeofArray(param_def), NULL); + // on command line, target rate is in Mbps, convert to bps + target_rate_dl *= 1000000LL; + target_rate_ul *= 1000000LL; + LOG_I(GNB_APP, + "CU-UP load tester: running test for\n" + "- Number of UEs %d\n" + "- Duration %d seconds\n" + "- Packet size %d\n" + "- Target DL rate %.3f Mbps (per UE)\n" + "- Target UL rate %.3f Mbps (per UE)\n" + "- Tester endpoint for CU-UP NG-U: %s:%d\n" + "- Tester endpoint for CU-UP F1-U: %s:%d\n", + n_ue, duration_s, packet_size, + (float) target_rate_dl / 1e6, + (float) target_rate_ul / 1e6, + ng_ip, ng_port, f1_ip, f1_port); + + struct thr_data dl_template = {.is_ul = false, .data_len = packet_size, .throughput = target_rate_dl, .duration = duration_s}; + struct thr_data ul_template = {.is_ul = true, .data_len = packet_size, .throughput = target_rate_ul, .duration = duration_s}; + + /* init GTP for NG-U comm with CU-UP */ + instance_t ng_inst = init_gtp(ng_ip, ng_port); + AssertFatal(ng_inst > 0, "could not initialize GTP interface on %s:%d towards CU-UP NG-U\n", ng_ip, ng_port); + + /* init GTP for F1-U comm with CU-UP */ + instance_t f1_inst = init_gtp(f1_ip, f1_port); + AssertFatal(f1_inst > 0, "could not initialize GTP interface on %s:%d towards CU-UP F1-U\n", f1_ip, f1_port); + + /* Start relevant services: SCTP, CUCP_E1: E1; GTP not actively used; RRC to + * register the queue so we can receive what E1 sends us back */ + 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_CUCP_E1, E1AP_CUCP_task, NULL); + AssertFatal(rc >= 0, "Create task for CUUP E1 failed\n"); + rc = itti_create_task(TASK_GTPV1_U, gtpv1uTask, NULL); + AssertFatal(rc >= 0, "Create task for GTPV1U failed\n"); + rc = itti_create_task(TASK_RRC_GNB, cuup_tester_rrc, NULL); + AssertFatal(rc >= 0, "Create task for RRC failed\n"); + init_e1(); + + /* Set up CU-UP connection: waits for CU-UP-initiated SCTP+E1 setup request */ + sctp_assoc_t assoc_id; + e1ap_nssai_t nssai; + setup_cuup(&assoc_id, &nssai); + + /* Set up UE contexts in CU-UP. the below two calls correspond to + * - setup_cuup_ue_ng(): E1 bearer context setup <=> completely sets up NG-U + * in CU-UP, gets TEID for F1-U UL + * - setup_cuup_ue_f1(): E1 bearer context modification <=> send TEID for + * F1-U DL (as if we had talked to the DU in the middle) */ + for (int ue = 0; ue < n_ue; ++ue) { + up_params_t f1_up = setup_cuup_ue_ng(assoc_id, &nssai, ue, ng_inst, ng_ip); + setup_cuup_ue_f1(assoc_id, ue, f1_inst, f1_ip, f1_up); + } + + usleep(1000); + + /* for each UE, start DL&UL data streams in dedicated thread, if configured */ + struct thr_data thrd[n_ue][2]; + memset(thrd, 0, sizeof(thrd)); + struct thr_data *obs_dl = NULL, *obs_ul = NULL; + for (int ue = 0; ue < n_ue; ++ue) { + obs_dl = start_thread(&dl_template, &thrd[ue][DL], ng_inst, ue); + obs_ul = start_thread(&ul_template, &thrd[ue][UL], f1_inst, ue); + } + /* show progress of one thread (all take the same time) */ + while (obs_dl || obs_ul) { + bool dlrunning = print_progress(DL, obs_dl); + bool ulrunning = print_progress(UL, obs_ul); + printf("\r"); + fflush(stdout); + if (!dlrunning && !ulrunning) + break; + usleep(100000); + } + /* join threads, if applicable */ + for (int ue = 0; ue < n_ue; ++ue) { + struct thr_data *dl = &thrd[ue][DL]; + if (dl->t) + pthread_join(dl->t, NULL); + struct thr_data *ul = &thrd[ue][UL]; + if (ul->t) + pthread_join(ul->t, NULL); + } + + /* wait a bit, to ensure all data is received after having sent it above */ + sleep(1); + + for (int ue = 0; ue < n_ue; ++ue) { + e1_release_ue(assoc_id, ue, f1_inst, ng_inst); + } + + /* show stats of all threads */ + bool success = true; + for (int ue = 0; ue < n_ue; ++ue) { + struct thr_data *thrddl = &thrd[ue][DL]; + struct ue_stat *statdl = &ue_stat[ue][DL]; + if (thrddl->t) + if (!print_stats("DL", ue, thrddl->total_data, thrddl->total_time, statdl)) + success = false; + struct thr_data *thrdul = &thrd[ue][UL]; + struct ue_stat *statul = &ue_stat[ue][UL]; + if (thrdul->t) + if (!print_stats("UL", ue, thrdul->total_data, thrdul->total_time, statul)) + success = false; + } + + if (success) + LOG_A(GNB_APP, "test succeeded\n"); + else + LOG_W(GNB_APP, "TEST FAILED\n"); + return success ? 0 : 1; +}