diff --git a/.clang-format b/.clang-format index a7248821a9..3b0be979ba 100644 --- a/.clang-format +++ b/.clang-format @@ -62,6 +62,7 @@ ForEachMacros: - BOOST_FOREACH - RB_FOREACH - UE_iterator + - FOR_EACH_SEQ_ARR IncludeBlocks: Preserve IncludeCategories: - Regex: '^' diff --git a/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml b/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml index 86b9040b63..3bb99de4ab 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml @@ -36,6 +36,11 @@ 020003 030001 030002 + 050001 + 050002 + 050003 + 020001 + 020002 100001 222222 @@ -139,6 +144,27 @@ 90 + + Custom_Command + Trigger PDU Session Release via gNB telnet + acamas + echo ci pdu_session_release 2 | ncat 192.168.71.140 9090 + + + + Custom_Command + Add PDU Session via UE telnet + acamas + echo ci add_pdu_session 6,7 | ncat 192.168.71.150 8091 + + + + Custom_Command + Trigger PDU Session Release via gNB telnet + acamas + echo ci pdu_session_release 6 | ncat 192.168.71.140 9090 + + Undeploy_Object true diff --git a/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml index 722290aa38..c4a4e2ebea 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml @@ -97,8 +97,9 @@ services: - ALL environment: USE_ADDITIONAL_OPTIONS: --nfapi VNF --log_config.global_log_options level,nocolor,time + --telnetsrv --telnetsrv.listenaddr 192.168.71.140 --telnetsrv.shrmod ci NFAPI_TRACE_LEVEL: info - ASAN_OPTIONS: detect_leaks=0 + ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0 depends_on: - oai-ext-dn networks: @@ -122,7 +123,7 @@ services: environment: USE_ADDITIONAL_OPTIONS: --nfapi PNF --rfsim --log_config.global_log_options level,nocolor,time NFAPI_TRACE_LEVEL: info - ASAN_OPTIONS: detect_leaks=0 + ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0 depends_on: - oai-vnf networks: @@ -147,7 +148,10 @@ services: - NET_ADMIN # for interface bringup - NET_RAW # for ping environment: - USE_ADDITIONAL_OPTIONS: --rfsim -r 25 --numerology 0 --uicc0.imsi 208990100001100 --band 66 -C 2152680000 --CO -400000000 --ssb 48 --rfsimulator.serveraddr 192.168.71.141 --extra-pdu-id 2 --log_config.global_log_options level,nocolor,time + USE_ADDITIONAL_OPTIONS: --rfsim -r 25 --numerology 0 --uicc0.imsi 208990100001100 --band 66 -C 2152680000 --CO -400000000 --ssb 48 + --rfsimulator.serveraddr 192.168.71.141 --extra-pdu-id 2 --log_config.global_log_options level,nocolor,time + --telnetsrv --telnetsrv.shrmod ciUE --telnetsrv.listenaddr 192.168.71.150 --telnetsrv.listenport 8091 + ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0 depends_on: - oai-pnf networks: diff --git a/common/utils/telnetsrv/CMakeLists.txt b/common/utils/telnetsrv/CMakeLists.txt index a249596d97..40380c0418 100644 --- a/common/utils/telnetsrv/CMakeLists.txt +++ b/common/utils/telnetsrv/CMakeLists.txt @@ -62,7 +62,7 @@ add_dependencies(telnetsrv telnetsrv_ci) message(STATUS "Add CI specific telnet functions for nrUE in telnetsrv_ciUE.so") add_library(telnetsrv_ciUE MODULE telnetsrv_ciUE.c) -target_link_libraries(telnetsrv_ciUE PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs UTIL) +target_link_libraries(telnetsrv_ciUE PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs UTIL NR_L2_UE nr_nas) add_dependencies(telnetsrv telnetsrv_ciUE) message(STATUS "Add bearer specific telnet functions in libtelnetsrv_bearer.so") diff --git a/common/utils/telnetsrv/telnetsrv_ci.c b/common/utils/telnetsrv/telnetsrv_ci.c index 291f1f37a2..8b00d529c5 100644 --- a/common/utils/telnetsrv/telnetsrv_ci.c +++ b/common/utils/telnetsrv/telnetsrv_ci.c @@ -39,6 +39,7 @@ #include "openair2/LAYER2/nr_rlc/nr_rlc_entity_am.h" #include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h" #include "openair2/RRC/NR/rrc_gNB_mobility.h" +#include "openair3/NGAP/ngap_gNB_ue_context.h" #define TELNETSERVERCODE #include "telnetsrv.h" @@ -284,6 +285,86 @@ static int get_current_bwp(char *buf, int debug, telnet_printfunc_t prnt) return 0; } +/** @brief Trigger NGAP PDU Session Release for one or more PDU sessions associated with a UE ID/ + * Syntax: trigger_pdu_session_release [ue_id=gNB_ue_ngap_id(int,opt)],pdusession_id(int)[,pdusession_id(int)...] + * - If the gNB_ue_ngap_id is omitted, it is fetched from the only UE present in the RRC layer + * - At least one valid PDU session ID must be provided + * @param[in] buf Comma-separated input string: [ue_id=gNB_ue_ngap_id(int,opt)],PDU1[,PDU2,...] + * @param[in] debug Not used. + * @param[in] prnt Callback for telnet output printing. + * @return 0 on success; negative value on error. */ +static int trigger_ngap_pdu_session_release(char *buf, int debug, telnet_printfunc_t prnt) +{ + if (buf == NULL) { + ERROR_MSG_RET("Missing input. Usage: trigger_pdu_session_release [ue_id=gNB_ue_ngap_id(int,opt)],pdusession_id(int)[,pdusession_id(int)...]\n"); + } + + char *tokens[NGAP_MAX_PDU_SESSION + 1]; + int count = 0; + + for (char *tok = strtok(buf, ","); tok != NULL && count < (int)sizeofArray(tokens); tok = strtok(NULL, ",")) { + tokens[count++] = tok; + } + + if (count < 1) { + ERROR_MSG_RET("Invalid input. Usage: trigger_pdu_session_release [ue_id=gNB_ue_ngap_id(int,opt)],pdusession_id(int)[,pdusession_id(int)...]\n"); + } + + int gNB_ue_ngap_id = -1; + int pdu_start_index = 0; + + if (strncmp(tokens[0], "ue_id=", 6) == 0) { + gNB_ue_ngap_id = atoi(tokens[0] + 6); + pdu_start_index = 1; + } else { + // No UE ID: infer it + if (!RC.nrrrc) + ERROR_MSG_RET("No RRC present\n"); + rrc_gNB_ue_context_t *ue = get_single_rrc_ue(); + if (!ue) + ERROR_MSG_RET("No single UE in RRC present\n"); + gNB_ue_ngap_id = ue->ue_context.rrc_ue_id; + } + + if (pdu_start_index >= count) { + ERROR_MSG_RET("No pdusession_id(int) provided\n"); + } + + ngap_gNB_ue_context_t *ngap = ngap_get_ue_context(gNB_ue_ngap_id); + if (!ngap) { + ERROR_MSG_RET("No NGAP UE context for gNB_ue_ngap_id %d\n", gNB_ue_ngap_id); + } + + MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_PDUSESSION_RELEASE_COMMAND); + ngap_pdusession_release_command_t *msg = &NGAP_PDUSESSION_RELEASE_COMMAND(message_p); + memset(msg, 0, sizeof(*msg)); + + msg->amf_ue_ngap_id = ngap->amf_ue_ngap_id; + msg->gNB_ue_ngap_id = ngap->gNB_ue_ngap_id; + + int nb_sessions = 0; + for (int i = pdu_start_index; i < count; ++i) { + int sid = atoi(tokens[i]); + if (sid < 1 || sid > 255) { + ERROR_MSG_RET("Invalid pdusession_id(int): %s (must be between 1 and 255)\n", tokens[i]); + } + msg->pdusession_release_params[nb_sessions++].pdusession_id = sid; + } + + msg->nb_pdusessions_torelease = nb_sessions; + + if (prnt) { + prnt("Triggering NGAP PDU Session Release for gNB_ue_ngap_id=%d: releasing pdusession_id=%d", gNB_ue_ngap_id); + for (int i = 0; i < nb_sessions; ++i) { + prnt(" %d,", msg->pdusession_release_params[i].pdusession_id); + } + prnt("\n"); + } + + itti_send_msg_to_task(TASK_RRC_GNB, 0, message_p); + return 0; +} + static telnetshell_cmddef_t cicmds[] = { {"get_single_rnti", "", get_single_rnti}, {"force_reestab", "[rnti(hex,opt)]", trigger_reestab}, @@ -294,6 +375,7 @@ static telnetshell_cmddef_t cicmds[] = { {"fetch_du_by_ue_id", "[rrc_ue_id(int,opt)]", fetch_du_by_ue_id}, {"get_current_bwp", "[rnti(hex,opt)]", get_current_bwp}, {"trigger_n2_ho", "[neighbour_pci(uint32_t),ueId(uint32_t)]", rrc_gNB_trigger_n2_ho}, + {"pdu_session_release", "[gNB_ue_ngap_id(int,opt)]", trigger_ngap_pdu_session_release}, {"", "", NULL}, }; diff --git a/common/utils/telnetsrv/telnetsrv_ciUE.c b/common/utils/telnetsrv/telnetsrv_ciUE.c index 88320a6ce0..6f86f1678f 100644 --- a/common/utils/telnetsrv/telnetsrv_ciUE.c +++ b/common/utils/telnetsrv/telnetsrv_ciUE.c @@ -39,6 +39,7 @@ #include "openair2/RRC/NR_UE/rrc_proto.h" #include "openair1/PHY/phy_extern_nr_ue.h" #include "openair1/PHY/defs_nr_common.h" +#include "openair3/NAS/NR_UE/nr_nas_msg.h" #define TELNETSERVERCODE #include "telnetsrv.h" @@ -52,6 +53,14 @@ const char* NR_UE_L2_STATE_STR[] = { #undef UE_STATE }; +static int get_default_ue_id(void) +{ + NR_UE_RRC_INST_t *rrc = get_NR_UE_rrc_inst(0); + if (!rrc) + return -1; + return rrc->ue_id; +} + /** * Get the synchronization state of a UE. * @@ -132,6 +141,40 @@ static int get_dl_toa(char *buf, int debug, telnet_printfunc_t prnt) return 0; } +static int add_pdu_session(char *buf, int debug, telnet_printfunc_t prnt) +{ + int ue_id = -1; + int pdusession_id = -1; + + if (!buf) { + ERROR_MSG_RET("Missing argument: expected PDUSessionID[,UE_ID]\n"); + } + + // Try parsing values in the form: "PDUSessionID[,UE_ID]" + int n = sscanf(buf, "%d,%d", &pdusession_id, &ue_id); + if (n == 1) { + // Only PDUSessionID provided: use default UE ID + ue_id = get_default_ue_id(); + if (ue_id < 0) + ERROR_MSG_RET("No default UE context found\n"); + } else if (n != 2) { + ERROR_MSG_RET("Invalid format: expected PDUSessionID[,UE_ID]\n"); + } + + if (pdusession_id < 0 || pdusession_id > 255) + ERROR_MSG_RET("PDUSessionID must be in range [0,255]\n"); + if (ue_id < 0) + ERROR_MSG_RET("UE_ID must be >= 0\n"); + + nr_ue_nas_t *nas = get_ue_nas_info(ue_id); + if (!nas) + ERROR_MSG_RET("No NAS context found for UE_ID %d\n", ue_id); + + request_pdusession(nas, pdusession_id); + prnt("Triggered PDU session request for UE %d with ID %d\n", ue_id, pdusession_id); + return 0; +} + /* Telnet shell command definitions */ static telnetshell_cmddef_t cicmds[] = { {"sync_state", "[UE_ID(int,opt)]", get_sync_state}, @@ -140,6 +183,7 @@ static telnetshell_cmddef_t cicmds[] = { {"force_crnti_ra", "", force_crnti_ra}, {"deregistration", "", force_deregistration}, {"get_max_dl_toa", "[ant]", get_dl_toa}, + {"add_pdu_session", "[PDUSessionID(int)],[UE_ID(int,opt)]", add_pdu_session}, {"", "", NULL}, }; diff --git a/doc/E1AP/e1ap_procedures.md b/doc/E1AP/e1ap_procedures.md index c46c710333..8857eee26b 100644 --- a/doc/E1AP/e1ap_procedures.md +++ b/doc/E1AP/e1ap_procedures.md @@ -72,3 +72,36 @@ sequenceDiagram u->>c: BEARER CONTEXT MODIFICATION RESPONSE Note over c: e1apCUCP_handle_BEARER_CONTEXT_MODIFICATION_RESPONSE ``` + +## PDU Session Release + +```mermaid +sequenceDiagram + participant AMF + participant CUCP + participant CUUP + participant DU + participant UE + + AMF->>CUCP: NG PDU SESSION RESOURCE RELEASE COMMAND + Note over CUCP: ngap_gNB_handle_pdusession_release_command + CUCP->>CUCP: rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND + Note over CUCP: set status PDU_SESSION_STATUS_TORELEASE + CUCP->>CUUP: E1 Bearer Context Modification Request + Note over CUUP: release_gtpu_tunnel (GTP tunnel, PDCP, SDAP) + CUUP->>CUCP: E1 Bearer Context Modification Response + Note over CUCP: rrc_gNB_send_f1_drb_release_request + CUCP->>DU: F1 UE Context Modification Request + Note over DU: handle_ue_context_drbs_release (release in MAC/RLC) + DU->>CUCP: F1 UE Context Modification Response + Note over CUCP: rrc_CU_process_ue_context_modification_response + Note over CUCP: replace existing CellGroupConfig + Note over CUCP: rrc_gNB_generate_dedicatedRRCReconfiguration + CUCP->>UE: RRCReconfiguration (DRB release list, NAS PDU) + UE->>CUCP: RRCReconfigurationComplete + Note over CUCP: handle_rrcReconfigurationComplete + Note over CUCP: rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE + CUCP->>AMF: NG PDU SESSION RESOURCE RELEASE RESPONSE + Note over CUCP: rm_drbs_by_pdusession (from stored RRC list) + Note over CUCP: rm_pduSession (from stored RRC list) +``` diff --git a/openair2/COMMON/e1ap_messages_types.h b/openair2/COMMON/e1ap_messages_types.h index 788236ca05..6906d54704 100644 --- a/openair2/COMMON/e1ap_messages_types.h +++ b/openair2/COMMON/e1ap_messages_types.h @@ -475,6 +475,14 @@ typedef struct pdu_session_to_mod_s { DRB_nGRAN_to_mod_t DRBnGRanModList[E1AP_MAX_NUM_DRBS]; } pdu_session_to_mod_t; +/** PDU Session Resource To Remove List (3GPP TS 38.463 clause 9.3.3.12) */ +typedef struct { + // PDU Session ID (M) + long sessionId; + // Cause (O) + e1ap_cause_t cause; +} pdu_session_to_remove_t; + /** * Bearer Context Setup Request message, clause 9.2.2.1 of 3GPP TS 38.463 */ @@ -534,6 +542,9 @@ typedef struct e1ap_bearer_mod_req_s { // NG-RAN PDU Session Resource To Modify List (O) int numPDUSessionsMod; pdu_session_to_mod_t pduSessionMod[E1AP_MAX_NUM_PDU_SESSIONS]; + // NG-RAN PDU Session Resource To Remove List (O) + int numPDUSessionsRem; + pdu_session_to_remove_t pduSessionRem[E1AP_MAX_NUM_PDU_SESSIONS]; } e1ap_bearer_mod_req_t; typedef struct e1ap_bearer_release_cmd_s { diff --git a/openair2/E1AP/lib/e1ap_bearer_context_management.c b/openair2/E1AP/lib/e1ap_bearer_context_management.c index 109b0008d3..f2593f103c 100644 --- a/openair2/E1AP/lib/e1ap_bearer_context_management.c +++ b/openair2/E1AP/lib/e1ap_bearer_context_management.c @@ -1631,6 +1631,20 @@ E1AP_E1AP_PDU_t *encode_E1_bearer_context_mod_request(const e1ap_bearer_mod_req_ *ieC3_1 = e1_encode_pdu_session_to_mod_item(i); } } + + // NG-RAN PDU Session Resource To Remove List (O) + if (in->numPDUSessionsRem) { + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationRequestIEs_t, remList); + E1AP_NG_RAN_BearerContextModificationRequest_t *msgNGRAN = encode_ng_ran_to_mod(remList); + msgNGRAN->id = E1AP_ProtocolIE_ID_id_PDU_Session_Resource_To_Remove_List; + msgNGRAN->criticality = E1AP_Criticality_reject; + msgNGRAN->value.present = E1AP_NG_RAN_BearerContextModificationRequest__value_PR_PDU_Session_Resource_To_Remove_List; + E1AP_PDU_Session_Resource_To_Remove_List_t *pdu2Remove = &msgNGRAN->value.choice.PDU_Session_Resource_To_Remove_List; + for (const pdu_session_to_remove_t *i = in->pduSessionRem; i < in->pduSessionRem + in->numPDUSessionsRem; i++) { + asn1cSequenceAdd(pdu2Remove->list, E1AP_PDU_Session_Resource_To_Remove_Item_t, ieC4_1); + ieC4_1->pDU_Session_ID = i->sessionId; + } + } return pdu; } @@ -1854,6 +1868,19 @@ bool decode_E1_bearer_context_mod_request(const E1AP_E1AP_PDU_t *pdu, e1ap_beare CHECK_E1AP_DEC(e1_decode_pdu_session_to_mod_item(out->pduSessionMod + i, modList->list.array[i])); } break; + case E1AP_ProtocolIE_ID_id_PDU_Session_Resource_To_Remove_List: + /* PDU Session Resource To Remove List (see 9.3.3.12 of TS 38.463) */ + _E1_EQ_CHECK_INT(msgNGRAN->value.present, + E1AP_NG_RAN_BearerContextModificationRequest__value_PR_PDU_Session_Resource_To_Remove_List); + E1AP_PDU_Session_Resource_To_Remove_List_t *remList = &msgNGRAN->value.choice.PDU_Session_Resource_To_Remove_List; + out->numPDUSessionsRem = remList->list.count; + // Loop through all PDU sessions to remove + for (int i = 0; i < remList->list.count; i++) { + E1AP_PDU_Session_Resource_To_Remove_Item_t *remItem = remList->list.array[i]; + pdu_session_to_remove_t *pduRem = &out->pduSessionRem[i]; + pduRem->sessionId = remItem->pDU_Session_ID; + } + break; default: PRINT_ERROR("Unknown msgNGRAN->id in E1 Setup Modification Request\n"); return false; @@ -1924,6 +1951,9 @@ e1ap_bearer_mod_req_t cp_bearer_context_mod_request(const e1ap_bearer_mod_req_t cp.pduSession[i] = cp_pdu_session_item(&msg->pduSession[i]); for (int i = 0; i < msg->numPDUSessionsMod; i++) cp.pduSessionMod[i] = cp_pdu_session_to_mod_item(&msg->pduSessionMod[i]); + // Copy PDU sessions to remove (simple struct copy, no deep copy needed) + for (int i = 0; i < msg->numPDUSessionsRem; i++) + cp.pduSessionRem[i] = msg->pduSessionRem[i]; return cp; } @@ -2037,6 +2067,15 @@ bool eq_bearer_context_mod_request(const e1ap_bearer_mod_req_t *a, const e1ap_be if (!eq_pdu_session_to_mod_item(&a->pduSessionMod[i], &b->pduSessionMod[i])) return false; } + // PDU Sessions to Remove + _E1_EQ_CHECK_INT(a->numPDUSessionsRem, b->numPDUSessionsRem); + for (int i = 0; i < a->numPDUSessionsRem; i++) { + const pdu_session_to_remove_t *pduRemA = &a->pduSessionRem[i]; + const pdu_session_to_remove_t *pduRemB = &b->pduSessionRem[i]; + _E1_EQ_CHECK_LONG(pduRemA->sessionId, pduRemB->sessionId); + _E1_EQ_CHECK_INT(pduRemA->cause.type, pduRemB->cause.type); + _E1_EQ_CHECK_INT(pduRemA->cause.value, pduRemB->cause.value); + } return true; } @@ -2114,129 +2153,129 @@ struct E1AP_E1AP_PDU *encode_E1_bearer_context_mod_response(const e1ap_bearer_mo ie2->criticality = E1AP_Criticality_reject; ie2->value.present = E1AP_BearerContextModificationResponseIEs__value_PR_GNB_CU_UP_UE_E1AP_ID; ie2->value.choice.GNB_CU_UP_UE_E1AP_ID = msg->gNB_cu_up_ue_id; - // NG-RAN PDU Session Resource Modified List (O) - asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationResponseIEs_t, ie3); - ie3->id = E1AP_ProtocolIE_ID_id_System_BearerContextModificationResponse; - ie3->criticality = E1AP_Criticality_ignore; - // CHOICE System (O) - ie3->value.present = E1AP_BearerContextModificationResponseIEs__value_PR_System_BearerContextModificationResponse; - E1AP_System_BearerContextModificationResponse_t *sys = &ie3->value.choice.System_BearerContextModificationResponse; - // NG-RAN - E1AP_ProtocolIE_Container_4932P29_t *ngran = calloc_or_fail(1, sizeof(*ngran)); - sys->present = E1AP_System_BearerContextModificationResponse_PR_nG_RAN_BearerContextModificationResponse; - sys->choice.nG_RAN_BearerContextModificationResponse = (struct E1AP_ProtocolIE_Container *)ngran; - asn1cSequenceAdd(ngran->list, E1AP_NG_RAN_BearerContextModificationResponse_t, list); - // PDU Session Resource Modified List (O) - list->id = E1AP_ProtocolIE_ID_id_PDU_Session_Resource_Modified_List; - list->criticality = E1AP_Criticality_reject; - list->value.present = E1AP_NG_RAN_BearerContextModificationResponse__value_PR_PDU_Session_Resource_Modified_List; - E1AP_PDU_Session_Resource_Modified_List_t *pdu_mod_l = &list->value.choice.PDU_Session_Resource_Modified_List; - for (int i = 0; i < msg->numPDUSessionsMod; ++i) { - // PDU Session Resource Modified Item (1..maxnoofPDUSessionResource) - const pdu_session_modif_t *pdu = &msg->pduSessionMod[i]; - asn1cSequenceAdd(pdu_mod_l->list, E1AP_PDU_Session_Resource_Modified_Item_t, iePdu); - // PDU Session ID (M) - iePdu->pDU_Session_ID = pdu->id; - // Security Result (O) - if (pdu->confidentialityProtectionIndication || pdu->integrityProtectionIndication) { - iePdu->securityResult = calloc_or_fail(1, sizeof(*iePdu->securityResult)); - if (pdu->confidentialityProtectionIndication) - iePdu->securityResult->confidentialityProtectionResult = *pdu->confidentialityProtectionIndication; - if (pdu->integrityProtectionIndication) - iePdu->securityResult->integrityProtectionResult = *pdu->integrityProtectionIndication; - } - // NG DL UP Transport Layer Information (O) - if (pdu->ng_DL_UP_TL_info) { - iePdu->nG_DL_UP_TNL_Information = calloc_or_fail(1, sizeof(*iePdu->nG_DL_UP_TNL_Information)); - *iePdu->nG_DL_UP_TNL_Information = e1_encode_up_tnl_info(pdu->ng_DL_UP_TL_info); - } - // DRB Modified List (O) - if (pdu->numDRBModified > 0) { - iePdu->dRB_Modified_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Modified_List_NG_RAN)); - E1AP_DRB_Modified_List_NG_RAN_t *drb_mod_l = iePdu->dRB_Modified_List_NG_RAN; - for (int j = 0; j < pdu->numDRBModified; ++j) { - const DRB_nGRAN_modified_t *drb = &pdu->DRBnGRanModList[j]; - asn1cSequenceAdd(drb_mod_l->list, E1AP_DRB_Modified_Item_NG_RAN_t, drb_mod); - // DRB ID (M) - drb_mod->dRB_ID = drb->id; - // Flow Setup List (O) - if (drb->numQosFlowSetup) - drb_mod->flow_Setup_List = calloc_or_fail(1, sizeof(*drb_mod->flow_Setup_List)); - for (int q = 0; q < drb->numQosFlowSetup; ++q) { - asn1cSequenceAdd(drb_mod->flow_Setup_List->list, E1AP_QoS_Flow_Item_t, qos_mod); - qos_mod->qoS_Flow_Identifier = drb->qosFlows[q].qfi; - } - // Flow Failed List (O) - if (drb->numQosFlowFailed) - drb_mod->flow_Failed_List = calloc_or_fail(1, sizeof(*drb_mod->flow_Failed_List)); - for (int q = 0; q < drb->numQosFlowFailed; ++q) { - asn1cSequenceAdd(drb_mod->flow_Failed_List->list, E1AP_QoS_Flow_Failed_Item_t, fail); - encode_qos_flow_failed_item(fail, &drb->qosFlowsFailed[q]); - } - - // PDCP Status Information (O) - if (drb->pdcp_status) { - asn1cCalloc(drb_mod->pDCP_SN_Status_Information, pdcp); - *pdcp = encode_pdcp_status_info(drb->pdcp_status); - } - - // UL UP Parameters (O) - if (drb->numUpParam) - drb_mod->uL_UP_Transport_Parameters = calloc_or_fail(1, sizeof(*drb_mod->uL_UP_Transport_Parameters)); - for (const up_params_t *k = drb->ul_UP_Params; k < drb->ul_UP_Params + drb->numUpParam; k++) { - asn1cSequenceAdd(drb_mod->uL_UP_Transport_Parameters->list, E1AP_UP_Parameters_Item_t, up); - up->uP_TNL_Information = e1_encode_up_tnl_info(&k->tl_info); - up->cell_Group_ID = k->cell_group_id; + if (msg->numPDUSessionsMod > 0) { + // NG-RAN PDU Session Resource Modified List (O) + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationResponseIEs_t, ie3); + ie3->id = E1AP_ProtocolIE_ID_id_System_BearerContextModificationResponse; + ie3->criticality = E1AP_Criticality_ignore; + // CHOICE System (O) + ie3->value.present = E1AP_BearerContextModificationResponseIEs__value_PR_System_BearerContextModificationResponse; + E1AP_System_BearerContextModificationResponse_t *sys = &ie3->value.choice.System_BearerContextModificationResponse; + // NG-RAN + E1AP_ProtocolIE_Container_4932P29_t *ngran = calloc_or_fail(1, sizeof(*ngran)); + sys->present = E1AP_System_BearerContextModificationResponse_PR_nG_RAN_BearerContextModificationResponse; + sys->choice.nG_RAN_BearerContextModificationResponse = (struct E1AP_ProtocolIE_Container *)ngran; + asn1cSequenceAdd(ngran->list, E1AP_NG_RAN_BearerContextModificationResponse_t, list); + // PDU Session Resource Modified List (O) + list->id = E1AP_ProtocolIE_ID_id_PDU_Session_Resource_Modified_List; + list->criticality = E1AP_Criticality_reject; + list->value.present = E1AP_NG_RAN_BearerContextModificationResponse__value_PR_PDU_Session_Resource_Modified_List; + E1AP_PDU_Session_Resource_Modified_List_t *pdu_mod_l = &list->value.choice.PDU_Session_Resource_Modified_List; + for (int i = 0; i < msg->numPDUSessionsMod; ++i) { + // PDU Session Resource Modified Item (1..maxnoofPDUSessionResource) + const pdu_session_modif_t *pdu = &msg->pduSessionMod[i]; + asn1cSequenceAdd(pdu_mod_l->list, E1AP_PDU_Session_Resource_Modified_Item_t, iePdu); + // PDU Session ID (M) + iePdu->pDU_Session_ID = pdu->id; + // Security Result (O) + if (pdu->confidentialityProtectionIndication || pdu->integrityProtectionIndication) { + iePdu->securityResult = calloc_or_fail(1, sizeof(*iePdu->securityResult)); + if (pdu->confidentialityProtectionIndication) + iePdu->securityResult->confidentialityProtectionResult = *pdu->confidentialityProtectionIndication; + if (pdu->integrityProtectionIndication) + iePdu->securityResult->integrityProtectionResult = *pdu->integrityProtectionIndication; + } + // NG DL UP Transport Layer Information (O) + if (pdu->ng_DL_UP_TL_info) { + iePdu->nG_DL_UP_TNL_Information = calloc_or_fail(1, sizeof(*iePdu->nG_DL_UP_TNL_Information)); + *iePdu->nG_DL_UP_TNL_Information = e1_encode_up_tnl_info(pdu->ng_DL_UP_TL_info); + } + // DRB Modified List (O) + if (pdu->numDRBModified > 0) { + iePdu->dRB_Modified_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Modified_List_NG_RAN)); + E1AP_DRB_Modified_List_NG_RAN_t *drb_mod_l = iePdu->dRB_Modified_List_NG_RAN; + for (int j = 0; j < pdu->numDRBModified; ++j) { + const DRB_nGRAN_modified_t *drb = &pdu->DRBnGRanModList[j]; + asn1cSequenceAdd(drb_mod_l->list, E1AP_DRB_Modified_Item_NG_RAN_t, drb_mod); + // DRB ID (M) + drb_mod->dRB_ID = drb->id; + // Flow Setup List (O) + if (drb->numQosFlowSetup) + drb_mod->flow_Setup_List = calloc_or_fail(1, sizeof(*drb_mod->flow_Setup_List)); + for (int q = 0; q < drb->numQosFlowSetup; ++q) { + asn1cSequenceAdd(drb_mod->flow_Setup_List->list, E1AP_QoS_Flow_Item_t, qos_mod); + qos_mod->qoS_Flow_Identifier = drb->qosFlows[q].qfi; + } + // Flow Failed List (O) + if (drb->numQosFlowFailed) + drb_mod->flow_Failed_List = calloc_or_fail(1, sizeof(*drb_mod->flow_Failed_List)); + for (int q = 0; q < drb->numQosFlowFailed; ++q) { + asn1cSequenceAdd(drb_mod->flow_Failed_List->list, E1AP_QoS_Flow_Failed_Item_t, fail); + encode_qos_flow_failed_item(fail, &drb->qosFlowsFailed[q]); + } + // PDCP Status Information (O) + if (drb->pdcp_status) { + asn1cCalloc(drb_mod->pDCP_SN_Status_Information, pdcp); + *pdcp = encode_pdcp_status_info(drb->pdcp_status); + } + // UL UP Parameters (O) + if (drb->numUpParam) + drb_mod->uL_UP_Transport_Parameters = calloc_or_fail(1, sizeof(*drb_mod->uL_UP_Transport_Parameters)); + for (const up_params_t *k = drb->ul_UP_Params; k < drb->ul_UP_Params + drb->numUpParam; k++) { + asn1cSequenceAdd(drb_mod->uL_UP_Transport_Parameters->list, E1AP_UP_Parameters_Item_t, up); + up->uP_TNL_Information = e1_encode_up_tnl_info(&k->tl_info); + up->cell_Group_ID = k->cell_group_id; + } } } - } - // DRB Failed To Modify List (O) - if (pdu->numDRBFailedToMod > 0) { - iePdu->dRB_Failed_To_Modify_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Failed_To_Modify_List_NG_RAN)); - E1AP_DRB_Failed_To_Modify_List_NG_RAN_t *ngran = iePdu->dRB_Failed_To_Modify_List_NG_RAN; - for (int j = 0; j < pdu->numDRBFailedToMod; ++j) { - const DRB_nGRAN_failed_t *drb = &pdu->DRBnGRanFailedModList[j]; - asn1cSequenceAdd(ngran->list, E1AP_DRB_Failed_To_Modify_Item_NG_RAN_t, fail_to_mod); - encode_drb_failed_item((E1AP_DRB_Failed_Item_NG_RAN_t *)fail_to_mod, drb); - } - } - // DRB Setup List (O) - if (pdu->numDRBSetup > 0) { - iePdu->dRB_Setup_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Setup_List_NG_RAN)); - E1AP_DRB_Setup_List_NG_RAN_t *drb_setup = iePdu->dRB_Setup_List_NG_RAN; - for (int j = 0; j < pdu->numDRBModified; ++j) { - const DRB_nGRAN_setup_t *drb = &pdu->DRBnGRanSetupList[j]; - asn1cSequenceAdd(drb_setup->list, E1AP_DRB_Setup_Item_NG_RAN_t, item); - // DRB ID (M) - item->dRB_ID = drb->id; - // UL UP Parameters (M) - for (const up_params_t *k = drb->UpParamList; k < drb->UpParamList + drb->numUpParam; k++) { - asn1cSequenceAdd(item->uL_UP_Transport_Parameters.list, E1AP_UP_Parameters_Item_t, up); - up->uP_TNL_Information = e1_encode_up_tnl_info(&k->tl_info); - up->cell_Group_ID = drb->UpParamList->cell_group_id; - } - // Flow Setup List (M) - for (int q = 0; q < drb->numQosFlowSetup; ++q) { - asn1cSequenceAdd(item->flow_Setup_List.list, E1AP_QoS_Flow_Item_t, qos); - qos->qoS_Flow_Identifier = drb->qosFlows[q].qfi; - } - // Flow Failed List (O) - if (drb->numQosFlowFailed) - item->flow_Failed_List = calloc_or_fail(1, sizeof(*item->flow_Failed_List)); - for (int q = 0; q < drb->numQosFlowFailed; ++q) { - asn1cSequenceAdd(item->flow_Failed_List->list, E1AP_QoS_Flow_Failed_Item_t, fail); - encode_qos_flow_failed_item(fail, &drb->qosFlowsFailed[q]); + // DRB Failed To Modify List (O) + if (pdu->numDRBFailedToMod > 0) { + iePdu->dRB_Failed_To_Modify_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Failed_To_Modify_List_NG_RAN)); + E1AP_DRB_Failed_To_Modify_List_NG_RAN_t *ngran = iePdu->dRB_Failed_To_Modify_List_NG_RAN; + for (int j = 0; j < pdu->numDRBFailedToMod; ++j) { + const DRB_nGRAN_failed_t *drb = &pdu->DRBnGRanFailedModList[j]; + asn1cSequenceAdd(ngran->list, E1AP_DRB_Failed_To_Modify_Item_NG_RAN_t, fail_to_mod); + encode_drb_failed_item((E1AP_DRB_Failed_Item_NG_RAN_t *)fail_to_mod, drb); } } - } - // DRB Failed List (O) - if (pdu->numDRBFailed) { - iePdu->dRB_Failed_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Failed_List_NG_RAN)); - E1AP_DRB_Failed_List_NG_RAN_t *ngran = iePdu->dRB_Failed_List_NG_RAN; - for (int j = 0; j < pdu->numDRBFailed; ++j) { - const DRB_nGRAN_failed_t *drb = &pdu->DRBnGRanFailedList[j]; - asn1cSequenceAdd(ngran->list, E1AP_DRB_Failed_Item_NG_RAN_t, fail); - encode_drb_failed_item(fail, drb); + // DRB Setup List (O) + if (pdu->numDRBSetup > 0) { + iePdu->dRB_Setup_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Setup_List_NG_RAN)); + E1AP_DRB_Setup_List_NG_RAN_t *drb_setup = iePdu->dRB_Setup_List_NG_RAN; + for (int j = 0; j < pdu->numDRBModified; ++j) { + const DRB_nGRAN_setup_t *drb = &pdu->DRBnGRanSetupList[j]; + asn1cSequenceAdd(drb_setup->list, E1AP_DRB_Setup_Item_NG_RAN_t, item); + // DRB ID (M) + item->dRB_ID = drb->id; + // UL UP Parameters (M) + for (const up_params_t *k = drb->UpParamList; k < drb->UpParamList + drb->numUpParam; k++) { + asn1cSequenceAdd(item->uL_UP_Transport_Parameters.list, E1AP_UP_Parameters_Item_t, up); + up->uP_TNL_Information = e1_encode_up_tnl_info(&k->tl_info); + up->cell_Group_ID = drb->UpParamList->cell_group_id; + } + // Flow Setup List (M) + for (int q = 0; q < drb->numQosFlowSetup; ++q) { + asn1cSequenceAdd(item->flow_Setup_List.list, E1AP_QoS_Flow_Item_t, qos); + qos->qoS_Flow_Identifier = drb->qosFlows[q].qfi; + } + // Flow Failed List (O) + if (drb->numQosFlowFailed) + item->flow_Failed_List = calloc_or_fail(1, sizeof(*item->flow_Failed_List)); + for (int q = 0; q < drb->numQosFlowFailed; ++q) { + asn1cSequenceAdd(item->flow_Failed_List->list, E1AP_QoS_Flow_Failed_Item_t, fail); + encode_qos_flow_failed_item(fail, &drb->qosFlowsFailed[q]); + } + } + } + // DRB Failed List (O) + if (pdu->numDRBFailed) { + iePdu->dRB_Failed_List_NG_RAN = calloc_or_fail(1, sizeof(*iePdu->dRB_Failed_List_NG_RAN)); + E1AP_DRB_Failed_List_NG_RAN_t *ngran = iePdu->dRB_Failed_List_NG_RAN; + for (int j = 0; j < pdu->numDRBFailed; ++j) { + const DRB_nGRAN_failed_t *drb = &pdu->DRBnGRanFailedList[j]; + asn1cSequenceAdd(ngran->list, E1AP_DRB_Failed_Item_NG_RAN_t, fail); + encode_drb_failed_item(fail, drb); + } } } } diff --git a/openair2/E1AP/lib/e1ap_lib_includes.h b/openair2/E1AP/lib/e1ap_lib_includes.h index 028993e6fd..adafbe0ae5 100644 --- a/openair2/E1AP/lib/e1ap_lib_includes.h +++ b/openair2/E1AP/lib/e1ap_lib_includes.h @@ -78,6 +78,7 @@ #include "E1AP_DRB-To-Modify-Item-NG-RAN.h" #include "E1AP_PDU-Session-Resource-To-Setup-Mod-Item.h" #include "E1AP_DRB-To-Setup-Mod-Item-NG-RAN.h" +#include "E1AP_PDU-Session-Resource-To-Remove-Item.h" // E1 Bearer Context Modification Response #include "E1AP_PDU-Session-Resource-Modified-Item.h" #include "E1AP_DRB-Modified-List-NG-RAN.h" diff --git a/openair2/E1AP/tests/e1ap_lib_test.c b/openair2/E1AP/tests/e1ap_lib_test.c index b168ff9e82..50116514fb 100644 --- a/openair2/E1AP/tests/e1ap_lib_test.c +++ b/openair2/E1AP/tests/e1ap_lib_test.c @@ -538,6 +538,14 @@ static void test_bearer_context_modification_request(void) .DRBnGRanList[0] = drb_to_setup, }; + // PDU Session to Remove + pdu_session_to_remove_t pdu_session_to_remove = { + .sessionId = 2, + // Note: cause is optional extension IE, currently encoding/decoding only handles sessionId + .cause.type = 0, + .cause.value = 0, + }; + // Initialize the Bearer Context Modification Request e1ap_bearer_mod_req_t orig = { .gNB_cu_cp_ue_id = 0x1234, @@ -548,6 +556,8 @@ static void test_bearer_context_modification_request(void) .pduSession[0] = pdusession_setup_item, .numPDUSessionsMod = 1, .pduSessionMod[0] = pdusession_mod_item, + .numPDUSessionsRem = 1, + .pduSessionRem[0] = pdu_session_to_remove, }; *orig.bearerContextStatus = BEARER_SUSPEND; *orig.inactivityTimer = 1000; diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c index a612065fc5..cf0f2286bc 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -381,12 +381,9 @@ static int handle_ue_context_drbs_release(NR_UE_info_t *UE, { DevAssert(req_drbs != NULL && cellGroupConfig != NULL); instance_t f1inst = get_f1_gtp_instance(); - cellGroupConfig->rlc_BearerToReleaseList = calloc(1, sizeof(*cellGroupConfig->rlc_BearerToReleaseList)); AssertFatal(cellGroupConfig->rlc_BearerToReleaseList != NULL, "out of memory\n"); - /* Note: the actual GTP tunnels are already removed in the F1AP message - * decoding */ for (int i = 0; i < drbs_len; i++) { const f1ap_drb_to_release_t *drb = &req_drbs[i]; @@ -401,7 +398,7 @@ static int handle_ue_context_drbs_release(NR_UE_info_t *UE, if (idx < cellGroupConfig->rlc_BearerToAddModList->list.count) { nr_mac_remove_lcid(&UE->UE_sched_ctrl, lcid); nr_rlc_release_entity(UE->rnti, lcid); - if (f1inst >= 0) + if (f1inst >= 0) /* Delete F1 tunnel */ newGtpuDeleteOneTunnel(f1inst, UE->rnti, drb->id); asn_sequence_del(&cellGroupConfig->rlc_BearerToAddModList->list, idx, 1); long *plcid = malloc(sizeof(*plcid)); diff --git a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c index 4b95963257..ecdf3dfd72 100644 --- a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c +++ b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c @@ -259,17 +259,58 @@ void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req) get_e1_if()->bearer_setup_response(&resp); } +static void release_gtpu_tunnel(uint32_t ue_id, int pdusession_id) +{ + instance_t n3inst = get_n3_gtp_instance(); + + // Handle N3 tunnel (1 tunnel per PDU session) + if (n3inst >= 0) { + LOG_I(E1AP, "UE %u: Deleting N3 tunnel for PDU session %d\n", ue_id, pdusession_id); + int result = newGtpuDeleteOneTunnel(n3inst, ue_id, pdusession_id); + if (result == GTPNOK) { + LOG_E(E1AP, "UE %u: Failed to delete N3 tunnel for PDU session %d\n", ue_id, pdusession_id); + } + } +} + +static void release_f1_drbs(uint32_t ue_id, int pdusession_id) +{ + instance_t f1inst = get_f1_gtp_instance(); + // Handle F1 tunnels (1 tunnel per DRB, fetch PDCP entities and get DRB IDs) + int drb_ids[MAX_DRBS_PER_UE]; + int n_drbs = nr_pdcp_get_drb_ids_for_pdusession(ue_id, pdusession_id, drb_ids); + if (n_drbs > 0) { + LOG_I(E1AP, "UE %u: Deleting %d F1 tunnels for PDU session %d\n", ue_id, n_drbs, pdusession_id); + // Delete each F1 tunnel using the DRB IDs + for (int i = 0; i < n_drbs; i++) { + int drb_id = drb_ids[i]; + if (f1inst >= 0) { + int result = newGtpuDeleteOneTunnel(f1inst, ue_id, drb_id); + if (result == GTPNOK) { + LOG_E(E1AP, "UE %u: Failed to delete F1 tunnel for DRB %d\n", ue_id, drb_id); + } + } + // Release PDCP entity + nr_pdcp_release_drb(ue_id, drb_id); + } + } else { + LOG_W(E1AP, "UE %u: No DRBs found for PDU session %d\n", ue_id, pdusession_id); + } +} + /** * @brief Fill Bearer Context Modification Response and send to callback */ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req) { - AssertFatal(req->numPDUSessionsMod > 0, "need at least one PDU session to modify\n"); + AssertFatal(req->numPDUSessionsMod > 0 || req->numPDUSessionsRem > 0, + "need at least one PDU session to modify or release (mod=%u, rel=%u)\n", + req->numPDUSessionsMod, + req->numPDUSessionsRem); e1ap_bearer_modif_resp_t modif = { .gNB_cu_cp_ue_id = req->gNB_cu_cp_ue_id, .gNB_cu_up_ue_id = req->gNB_cu_up_ue_id, - .numPDUSessionsMod = req->numPDUSessionsMod, }; instance_t f1inst = get_f1_gtp_instance(); @@ -284,6 +325,7 @@ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req) req->pduSessionMod[i].numDRB2Modify); modif.pduSessionMod[i].id = req->pduSessionMod[i].sessionId; modif.pduSessionMod[i].numDRBModified = req->pduSessionMod[i].numDRB2Modify; + modif.numPDUSessionsMod++; /* DRBs to modify */ for (int j = 0; j < req->pduSessionMod[i].numDRB2Modify; j++) { const DRB_nGRAN_to_mod_t *to_modif = &req->pduSessionMod[i].DRBnGRanModList[j]; @@ -347,6 +389,15 @@ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req) } } + /* PDU Session Resource To Remove List (see 9.3.3.12 of TS 38.463) */ + for (int i = 0; i < req->numPDUSessionsRem; i++) { + modif.pduSessionMod[i].id = req->pduSessionRem[i].sessionId; + modif.numPDUSessionsMod++; + release_gtpu_tunnel(req->gNB_cu_up_ue_id, req->pduSessionRem[i].sessionId); + release_f1_drbs(req->gNB_cu_up_ue_id, req->pduSessionRem[i].sessionId); + nr_sdap_delete_entity(req->gNB_cu_up_ue_id, req->pduSessionRem[i].sessionId); + } + get_e1_if()->bearer_modif_response(&modif); } diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c index 94d44fe8ce..3fd4c4bdd4 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c @@ -653,6 +653,30 @@ void nr_pdcp_add_srbs(eNB_flag_t enb_flag, LOG_W(PDCP, "nr_pdcp_add_srbs() with void list\n"); } +/** @brief Get DRB IDs for a specific PDU session */ +int nr_pdcp_get_drb_ids_for_pdusession(ue_id_t ue_id, long pdusession_id, int *drb_ids) +{ + int num_found = 0; + nr_pdcp_manager_lock(nr_pdcp_ue_manager); + nr_pdcp_ue_t *ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, ue_id); + if (ue) { + for (int i = 0; i < MAX_DRBS_PER_UE; i++) { + nr_pdcp_entity_t *drb = ue->drb[i]; + if (drb && drb->pdusession_id == pdusession_id) { + if (num_found < MAX_DRBS_PER_UE) { + drb_ids[num_found] = drb->rb_id; + num_found++; + } else { + LOG_W(PDCP, "Found more DRBs than MAX_DRBS_PER_UE=%d\n", MAX_DRBS_PER_UE); + break; + } + } + } + } + nr_pdcp_manager_unlock(nr_pdcp_ue_manager); + return num_found; +} + uint64_t get_pdcp_optmask(void) { return pdcp_optmask; diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h index 884ea11f60..75435ed695 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h @@ -66,6 +66,7 @@ void nr_pdcp_reconfigure_srb(ue_id_t ue_id, int srb_id, long t_Reordering); void nr_pdcp_reconfigure_drb(ue_id_t ue_id, int drb_id, NR_PDCP_Config_t *pdcp_config); void nr_pdcp_release_srb(ue_id_t ue_id, int srb_id); void nr_pdcp_release_drb(ue_id_t ue_id, int drb_id); +int nr_pdcp_get_drb_ids_for_pdusession(ue_id_t ue_id, long pdusession_id, int *drb_ids); void add_srb(int is_gnb, ue_id_t UEid, diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_ue_manager.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_ue_manager.c index 07edfb5814..f4dc5c1d5d 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_ue_manager.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_ue_manager.c @@ -178,7 +178,7 @@ void nr_pdcp_ue_add_srb_pdcp_entity(nr_pdcp_ue_t *ue, int srb_id, nr_pdcp_entity /* must be called with lock acquired */ void nr_pdcp_ue_add_drb_pdcp_entity(nr_pdcp_ue_t *ue, int drb_id, nr_pdcp_entity_t *entity) { - if (drb_id < 1 || drb_id > 5) { + if (drb_id < 1 || drb_id > MAX_DRBS_PER_UE) { LOG_E(PDCP, "%s:%d:%s: fatal, bad drb id\n", __FILE__, __LINE__, __FUNCTION__); exit(1); } diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 2136e966ab..585b1e48e2 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -519,7 +519,13 @@ static NR_RRCReconfiguration_IEs_t *build_RRCReconfiguration_IEs(const nr_rrc_re cfg->drb_ToAddModList = params->drb_config_list; cfg->securityConfig = params->security_config; cfg->srb3_ToRelease = NULL; - cfg->drb_ToReleaseList = params->drb_release_list; + if (params->n_drb_rel) { + asn1cCalloc(cfg->drb_ToReleaseList, to_release); + for (int i = 0; i < params->n_drb_rel; i++) { + asn1cSequenceAdd(to_release->list, NR_DRB_Identity_t, DRB_release); + *DRB_release = params->drb_rel[i]; + } + } } /* measConfig */ diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.h b/openair2/RRC/NR/MESSAGES/asn1_msg.h index 48a0ea734b..eb8da91c2a 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.h +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h @@ -64,7 +64,8 @@ typedef struct { uint8_t transaction_id; NR_SRB_ToAddModList_t *srb_config_list; NR_DRB_ToAddModList_t *drb_config_list; - NR_DRB_ToReleaseList_t *drb_release_list; + int *drb_rel; + int n_drb_rel; NR_SecurityConfig_t *security_config; NR_MeasConfig_t *meas_config; byte_array_t dedicated_NAS_msg_list[MAX_DRBS_PER_UE]; diff --git a/openair2/RRC/NR/MESSAGES/tests/test_asn1_msg.cpp b/openair2/RRC/NR/MESSAGES/tests/test_asn1_msg.cpp index 1f92c5aae5..025978218e 100644 --- a/openair2/RRC/NR/MESSAGES/tests/test_asn1_msg.cpp +++ b/openair2/RRC/NR/MESSAGES/tests/test_asn1_msg.cpp @@ -88,7 +88,6 @@ TEST(nr_asn1, paging) void free_RRCReconfiguration_params(nr_rrc_reconfig_param_t params) { ASN_STRUCT_FREE(asn_DEF_NR_MeasConfig, params.meas_config); - ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToReleaseList, params.drb_release_list); ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToAddModList, params.drb_config_list); ASN_STRUCT_FREE(asn_DEF_NR_SRB_ToAddModList, params.srb_config_list); ASN_STRUCT_FREE(asn_DEF_NR_SecurityConfig, params.security_config); diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 27e17e03f0..4d24a771c0 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -97,7 +97,6 @@ typedef enum pdu_session_satus_e { PDU_SESSION_STATUS_TOMODIFY, // ENDC NSA PDU_SESSION_STATUS_FAILED, PDU_SESSION_STATUS_TORELEASE, // to release DRB between eNB and UE - PDU_SESSION_STATUS_RELEASED } pdu_session_status_t; typedef struct pdusession_s { @@ -149,6 +148,12 @@ typedef enum { RRC_UECAPABILITY_ENQUIRY, } rrc_action_t; +/* Small state for delaying NG-triggered actions (setup/release) */ +typedef struct { + int max_delays; + bool ongoing_transaction; +} delayed_action_state_t; + typedef struct nr_redcap_ue_cap { bool support_of_redcap_r17; bool support_of_16drb_redcap_r17; @@ -167,8 +172,6 @@ typedef struct nr_handover_context_s nr_handover_context_t; typedef struct gNB_RRC_UE_s { time_t last_seen; // last time this UE has been accessed - NR_DRB_ToReleaseList_t *DRB_ReleaseList; - NR_SRB_INFO_TABLE_ENTRY Srb[NR_NUM_SRB]; NR_MeasConfig_t *measConfig; nr_handover_context_t *ho_context; @@ -244,8 +247,7 @@ typedef struct gNB_RRC_UE_s { byte_array_t nas_pdu; /* hack, see rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ() for more info */ - int max_delays_pdu_session; - bool ongoing_pdusession_setup_request; + delayed_action_state_t delayed_action; nr_redcap_ue_cap_t *redcap_cap; } gNB_RRC_UE_t; diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h index 777ea9a8d1..a36a0778c6 100644 --- a/openair2/RRC/NR/nr_rrc_proto.h +++ b/openair2/RRC/NR/nr_rrc_proto.h @@ -66,12 +66,6 @@ void *rrc_gnb_task(void *args_p); int nr_rrc_reconfiguration_req(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p, const int dl_bwp_id, const int ul_bwp_id); -void rrc_gNB_generate_dedicatedRRCReconfiguration_release(gNB_RRC_INST *rrc, - gNB_RRC_UE_t *ue_p, - uint8_t xid, - uint32_t nas_length, - uint8_t *nas_buffer); - NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cellid); bool ue_associated_to_cuup(const gNB_RRC_INST *rrc, const gNB_RRC_UE_t *ue); @@ -128,5 +122,6 @@ NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, uint8_t reestablish); NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish, bool do_integrity, bool do_ciphering); void activate_srb(gNB_RRC_UE_t *UE, int srb_id); void e1_notify_pdcp_status(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const ngap_drb_status_t *drb_status); +void init_delayed_action(delayed_action_state_t *delayed_action); #endif diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index dcc17ec2c0..b18e820e91 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -115,6 +115,97 @@ mui_t rrc_gNB_mui = 0; +/* Per-transaction max_delays counter to limit retry attempts */ +#define MAX_DELAYS 100 + +/** @brief clone and re-enqueue an NGAP message after delaying + * delays the ongoing transaction (in msg_p) by setting a timer to wait + * 10ms; upon expiry, delivers to RRC, which sends the message to itself */ +static void delay_transaction(MessageDef *msg_p, int wait_us) +{ + MessagesIds id = ITTI_MSG_ID(msg_p); + AssertFatal(id == NGAP_PDUSESSION_SETUP_REQ || id == NGAP_PDUSESSION_RELEASE_COMMAND, + "delay_transaction(): unsupported message id %d\n", + id); + + MessageDef *new = itti_alloc_new_message(TASK_RRC_GNB, 0, id); + + // Copy only the specific message struct, not the entire union. + // The union (msg_t) contains all message types and is much larger than + // the allocated space (which is sized for the specific message type only). + if (id == NGAP_PDUSESSION_SETUP_REQ) { + NGAP_PDUSESSION_SETUP_REQ(new) = NGAP_PDUSESSION_SETUP_REQ(msg_p); + } else if (id == NGAP_PDUSESSION_RELEASE_COMMAND) { + NGAP_PDUSESSION_RELEASE_COMMAND(new) = NGAP_PDUSESSION_RELEASE_COMMAND(msg_p); + } + + int instance = msg_p->ittiMsgHeader.originInstance; + long timer_id; + timer_setup(0, wait_us, TASK_RRC_GNB, instance, TIMER_ONE_SHOT, new, &timer_id); +} + +static void reset_delayed_action(delayed_action_state_t *delayed_action) +{ + delayed_action->ongoing_transaction = false; + delayed_action->max_delays = 0; +} + +void init_delayed_action(delayed_action_state_t *delayed_action) +{ + delayed_action->ongoing_transaction = true; + delayed_action->max_delays = MAX_DELAYS; +} + +/* \brief checks if any transaction is ongoing for any xid of this UE */ +static bool transaction_ongoing(const gNB_RRC_UE_t *UE) +{ + for (int xid = 0; xid < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++xid) { + if (UE->xids[xid] != RRC_ACTION_NONE) + return true; + } + return false; +} + +/** @brief delay control: returns true if delayed, false if should proceed + * This is a hack. We observed that with some UEs, PDU session requests might + * come in quick succession, faster than the RRC reconfiguration for the PDU + * session requests can be carried out (UE is doing reconfig, and second PDU + * session request arrives). We don't have currently the means to "queue up" + * these transactions, which would probably involve some rework of the RRC. + * To still allow these requests to come in and succeed, we below check and delay transactions + * for 10ms. However, to not accidentally end up in infinite loops, the + * maximum number is capped on a per-UE basis as indicated in variable + * max_delays_pdu_session. See commit 277f8da0 for more details. */ +static bool rrc_delay_transaction(instance_t instance, MessageDef *msg_p) +{ + uint32_t cu_ue_id = 0; + if (ITTI_MSG_ID(msg_p) == NGAP_PDUSESSION_SETUP_REQ) { + cu_ue_id = NGAP_PDUSESSION_SETUP_REQ(msg_p).gNB_ue_ngap_id; + } else if (ITTI_MSG_ID(msg_p) == NGAP_PDUSESSION_RELEASE_COMMAND) { + cu_ue_id = NGAP_PDUSESSION_RELEASE_COMMAND(msg_p).gNB_ue_ngap_id; + } + AssertFatal(cu_ue_id > 0, "cu_ue_id not found in message %s\n", ITTI_MSG_NAME(msg_p)); + + rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[instance], cu_ue_id); + DevAssert(ue_context_p); + + gNB_RRC_UE_t *UE = &ue_context_p->ue_context; + bool delay = UE->delayed_action.ongoing_transaction && UE->delayed_action.max_delays > 0; + /* Check if any PDU session action is ongoing */ + if (delay || transaction_ongoing(UE)) { + int wait_us = 10000; + LOG_I(NR_RRC, + "UE %d: ongoing transaction, delaying incoming transaction by %d us\n", + UE->rrc_ue_id, + wait_us); + delay_transaction(msg_p, wait_us); + UE->delayed_action.max_delays--; + return true; /* delayed */ + } + LOG_D(NR_RRC, "UE %d: no delayed action ongoing, proceeding with incoming transaction\n", UE->rrc_ue_id); + return false; /* not delayed */ +} + typedef struct deliver_ue_ctxt_release_data_t { gNB_RRC_INST *rrc; f1ap_ue_context_rel_cmd_t *release_cmd; @@ -366,6 +457,10 @@ NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish, bool do LOG_D(NR_RRC, "PDU Session %d not found, skip\n", drb->pdusession_id); continue; } + if (pduSession->status > PDU_SESSION_STATUS_TOMODIFY) { + LOG_D(NR_RRC, "PDU Session %d is not to add/mod\n", drb->pdusession_id); + continue; + } pdusession_t *session = &pduSession->param; NR_DRB_ToAddMod_t *drb_ToAddMod = calloc_or_fail(1, sizeof(*drb_ToAddMod)); drb_ToAddMod->drb_Identity = drb->drb_id; @@ -625,10 +720,10 @@ static NR_ReportConfigToAddMod_t *prepare_a3_event_report(const nr_a3_event_t *a void free_RRCReconfiguration_params(nr_rrc_reconfig_param_t params) { - ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToReleaseList, params.drb_release_list); ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToAddModList, params.drb_config_list); ASN_STRUCT_FREE(asn_DEF_NR_SRB_ToAddModList, params.srb_config_list); ASN_STRUCT_FREE(asn_DEF_NR_SecurityConfig, params.security_config); + free(params.drb_rel); for (int i = 0; i < params.num_nas_msg; i++) FREE_AND_ZERO_BYTE_ARRAY(params.dedicated_NAS_msg_list[i]); } @@ -706,19 +801,37 @@ nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC nr_rrc_reconfig_param_t params = {.cell_group_config = UE->masterCellGroup, .transaction_id = xid, .drb_config_list = DRBs, - .drb_release_list = UE->DRB_ReleaseList, .meas_config = UE->measConfig, .srb_config_list = SRBs}; - UE->DRB_ReleaseList = NULL; // pointer transferred to params FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &UE->pduSessions) { pdusession_t *session = &item->param; + // Collect NAS PDUs if (session->nas_pdu.len > 0) { params.dedicated_NAS_msg_list[params.num_nas_msg++] = session->nas_pdu; session->nas_pdu.buf = NULL; session->nas_pdu.len = 0; LOG_D(NR_RRC, "Transfer NAS info with size %ld to RRCReconfiguration params\n", session->nas_pdu.len); } + // Collect DRBs to release for PDU sessions marked for release + if (item->status == PDU_SESSION_STATUS_TORELEASE) { + if (!params.drb_rel) + params.drb_rel = calloc_or_fail(MAX_DRBS_PER_UE, sizeof(int)); + FOR_EACH_SEQ_ARR (drb_t *, drb, &UE->drbs) { + if (drb->pdusession_id == session->pdusession_id) { + if (params.n_drb_rel >= MAX_DRBS_PER_UE) { + LOG_E(NR_RRC, "UE %d: Too many DRBs to release (max %d)\n", UE->rrc_ue_id, MAX_DRBS_PER_UE); + break; + } + params.drb_rel[params.n_drb_rel++] = drb->drb_id; + LOG_D(NR_RRC, + "UE %d: Added DRB %d to release list for PDU session %d\n", + UE->rrc_ue_id, + drb->drb_id, + session->pdusession_id); + } + } + } } if (UE->nas_pdu.len > 0) { @@ -746,21 +859,24 @@ static void rrc_gNB_generate_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_ { /* do not re-establish PDCP for any bearer */ nr_rrc_reconfig_param_t params = get_RRCReconfiguration_params(rrc, ue_p, 0, false); - ue_p->xids[params.transaction_id] = RRC_PDUSESSION_ESTABLISH; + FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &ue_p->pduSessions) { + if (item->status == PDU_SESSION_STATUS_NEW) { + item->status = PDU_SESSION_STATUS_DONE; + continue; + } + // Set xid for all PDU sessions + item->xid = params.transaction_id; + } + + // Set xid for RRC transaction + ue_p->xids[params.transaction_id] = params.n_drb_rel > 0 ? RRC_PDUSESSION_RELEASE : RRC_PDUSESSION_ESTABLISH; + byte_array_t msg = rrc_gNB_encode_RRCReconfiguration(rrc, ue_p, params); if (msg.len <= 0) { LOG_E(NR_RRC, "UE %d: Failed to generate RRCReconfiguration\n", ue_p->rrc_ue_id); return; } - FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &ue_p->pduSessions) { - if (item->param.nas_pdu.buf) - item->xid = params.transaction_id; - if (item->status < PDU_SESSION_STATUS_ESTABLISHED) { - item->status = PDU_SESSION_STATUS_DONE; - } - } - LOG_UE_DL_EVENT(ue_p, "Generate RRCReconfiguration (bytes %ld, xid %d)\n", msg.len, params.transaction_id); const uint32_t msg_id = NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration; nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DL_SCH_LCID_DCCH, msg_id, msg.buf, msg.len); @@ -831,44 +947,27 @@ void rrc_gNB_modify_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t free_byte_array(msg); } -//----------------------------------------------------------------------------- -void rrc_gNB_generate_dedicatedRRCReconfiguration_release(gNB_RRC_INST *rrc, - gNB_RRC_UE_t *ue_p, - uint8_t xid, - uint32_t nas_length, - uint8_t *nas_buffer) -//----------------------------------------------------------------------------- +static void rrc_gNB_send_f1_drb_release_request(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p, int *drb_to_release, int n_drb_to_release) { - nr_rrc_reconfig_param_t params = {.transaction_id = xid}; + f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id); + RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id); + f1ap_ue_context_mod_req_t req = { + .gNB_CU_ue_id = ue_p->rrc_ue_id, + .gNB_DU_ue_id = ue_data.secondary_ue, + .servCellIndex = 0, + }; + req.plmn = malloc_or_fail(sizeof(*req.plmn)); + *req.plmn = rrc->configuration.plmn[0]; + req.nr_cellid = malloc_or_fail(sizeof(*req.nr_cellid)); + *req.nr_cellid = rrc->nr_cellid; + req.drbs_rel = malloc_or_fail(sizeof(*req.drbs_rel)); + req.drbs_rel_len = n_drb_to_release; + memcpy(req.drbs_rel, drb_to_release, n_drb_to_release * sizeof(int)); - NR_DRB_ToReleaseList_t *to_release = CALLOC(sizeof(*to_release), 1); - FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &ue_p->pduSessions) { - if ((item->status != PDU_SESSION_STATUS_TORELEASE) && item->xid != xid) - continue; - FOR_EACH_SEQ_ARR(drb_t *, drb, &ue_p->drbs) { - if (drb->pdusession_id == item->param.pdusession_id) { - asn1cSequenceAdd(to_release->list, NR_DRB_Identity_t, DRB_release); - *DRB_release = drb->drb_id; // DRB ID - } - } - } - params.drb_release_list = to_release; - - if (nas_length > 0) { - params.dedicated_NAS_msg_list[params.num_nas_msg].buf = nas_buffer; - params.dedicated_NAS_msg_list[params.num_nas_msg++].len = nas_length; - } - byte_array_t msg = do_RRCReconfiguration(¶ms); - if (msg.len <= 0) { - LOG_E(NR_RRC, "UE %d: Failed to generate RRCReconfiguration\n", ue_p->rrc_ue_id); - return; - } - LOG_DUMPMSG(NR_RRC, DEBUG_RRC, msg.buf, msg.len, "[MSG] RRC Reconfiguration\n"); - LOG_I(NR_RRC, "UE %d: Generate NR_RRCReconfiguration (bytes %ld)\n", ue_p->rrc_ue_id, msg.len); - const uint32_t msg_id = NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration; - nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DL_SCH_LCID_DCCH, msg_id, msg.buf, msg.len); - free_RRCReconfiguration_params(params); - free_byte_array(msg); + /* send UE Context Modification to DU without attaching any RRC container */ + rrc->mac_rrc.ue_context_modification_request(ue_data.du_assoc_id, &req); + LOG_I(NR_RRC, "UE %d: send F1 UE Context Modification Request with DRB release (%d DRBs)\n", ue_p->rrc_ue_id, req.drbs_rel_len); + free_ue_context_mod_req(&req); } static void fill_security_info(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, security_information_t *secInfo) @@ -1847,10 +1946,8 @@ static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U switch (UE->xids[xid]) { case RRC_PDUSESSION_RELEASE: { - gtpv1u_gnb_delete_tunnel_req_t req = {0}; - gtpv1u_delete_ngu_tunnel(rrc->module_id, &req); - // NGAP_PDUSESSION_RELEASE_RESPONSE rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(rrc, UE, xid); + reset_delayed_action(&UE->delayed_action); } break; case RRC_PDUSESSION_ESTABLISH: if (UE->n_initial_pdu > 0) { @@ -1865,6 +1962,7 @@ static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U LOG_W(NR_RRC, "UE %d: RRC Reconfiguration Complete for PDU session establishment, but no PDU sessions were setup\n", UE->rrc_ue_id); + reset_delayed_action(&UE->delayed_action); break; case RRC_PDUSESSION_MODIFY: rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(rrc, UE, xid); @@ -1881,9 +1979,6 @@ static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U break; } - if (UE->xids[xid] == RRC_PDUSESSION_ESTABLISH) - UE->ongoing_pdusession_setup_request = false; - UE->xids[xid] = RRC_ACTION_NONE; for (int i = 0; i < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++i) { if (UE->xids[i] != RRC_ACTION_NONE) { @@ -2728,15 +2823,19 @@ void rrc_gNB_process_e1_bearer_context_setup_failure(e1ap_bearer_context_setup_f */ void rrc_gNB_process_e1_bearer_context_modif_resp(const e1ap_bearer_modif_resp_t *resp) { + LOG_I(NR_RRC, "Received E1AP Bearer Context Modification Response for UE CU-CP ID %d\n", resp->gNB_cu_cp_ue_id); gNB_RRC_INST *rrc = RC.nrrrc[0]; rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, resp->gNB_cu_cp_ue_id); if (ue_context_p == NULL) { LOG_E(RRC, "no UE with CU-CP UE ID %d found\n", resp->gNB_cu_cp_ue_id); return; } + gNB_RRC_UE_t *ue = &ue_context_p->ue_context; int n_drb_mod = 0; int drb_ids[MAX_DRBS_PER_UE] = {0}; + int drb_to_release[MAX_DRBS_PER_UE] = {0}; + int n_drb_to_release = 0; e1_pdcp_status_info_t pdcp_status[MAX_DRBS_PER_UE] = {0}; for (int i = 0; i < resp->numPDUSessionsMod; ++i) { const pdu_session_modif_t *pdu = &resp->pduSessionMod[i]; @@ -2749,13 +2848,32 @@ void rrc_gNB_process_e1_bearer_context_modif_resp(const e1ap_bearer_modif_resp_t pdcp_status[n_drb_mod++] = *pdu->DRBnGRanModList[j].pdcp_status; } } + // Collect DRBs to release for PDU sessions marked for release + rrc_pdu_session_param_t *pdu_session = find_pduSession(&ue->pduSessions, pdu->id); + if (pdu_session && pdu_session->status == PDU_SESSION_STATUS_TORELEASE) { + FOR_EACH_SEQ_ARR(drb_t *, drb, &ue->drbs) { + if (drb->pdusession_id == pdu->id) { + DevAssert(n_drb_to_release < MAX_DRBS_PER_UE); + drb_to_release[n_drb_to_release++] = drb->drb_id; + } + } + if (n_drb_to_release == 0) { + LOG_E(NR_RRC, "UE %d: no DRBs to release for PDU session %ld\n", ue->rrc_ue_id, pdu->id); + } + } } + if (n_drb_mod) { LOG_I(NR_RRC, "UE %d: received PDU Status Info - send UL RAN Status Transfer\n", resp->gNB_cu_cp_ue_id); - gNB_RRC_UE_t *ue = &ue_context_p->ue_context; if (ue->ho_context && ue->ho_context->source) ue->ho_context->source->ho_status_transfer(rrc, ue, n_drb_mod, drb_ids, pdcp_status); } + + // Send F1 UE Context Modification Request with DRB release + if (n_drb_to_release) { + LOG_I(NR_RRC, "Send F1 UE Context Modification Request with DRB to release\n"); + rrc_gNB_send_f1_drb_release_request(rrc, ue, drb_to_release, n_drb_to_release); + } } /** @brief E1AP Bearer Context Modification Failure processing on CU-CP */ @@ -2852,7 +2970,6 @@ static const char *get_pdusession_status_text(pdu_session_status_t status) case PDU_SESSION_STATUS_TOMODIFY: return "to-modify"; case PDU_SESSION_STATUS_FAILED: return "failed"; case PDU_SESSION_STATUS_TORELEASE: return "to-release"; - case PDU_SESSION_STATUS_RELEASED: return "released"; default: AssertFatal(false, "illegal PDU status code %d\n", status); return "illegal"; } return "illegal"; @@ -2978,7 +3095,8 @@ void *rrc_gnb_task(void *args_p) { break; case NGAP_PDUSESSION_SETUP_REQ: - rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(msg_p, instance); + if (!rrc_delay_transaction(instance, msg_p)) + rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(msg_p, instance); break; case NGAP_PDUSESSION_MODIFY_REQ: @@ -2986,7 +3104,8 @@ void *rrc_gnb_task(void *args_p) { break; case NGAP_PDUSESSION_RELEASE_COMMAND: - rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(msg_p, instance); + if (!rrc_delay_transaction(instance, msg_p)) + rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(&NGAP_PDUSESSION_RELEASE_COMMAND(msg_p), RC.nrrrc[instance]); break; case NGAP_DL_RAN_STATUS_TRANSFER: diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 3b30341371..8ba7b3e950 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -789,29 +789,6 @@ void rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE return; } -/* \brief checks if any transaction is ongoing for any xid of this UE */ -static bool transaction_ongoing(const gNB_RRC_UE_t *UE) -{ - for (int xid = 0; xid < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++xid) { - if (UE->xids[xid] != RRC_ACTION_NONE) - return true; - } - return false; -} - -/* \brief delays the ongoing transaction (in msg_p) by setting a timer to wait - * 10ms; upon expiry, delivers to RRC, which sends the message to itself */ -static void delay_transaction(MessageDef *msg_p, int wait_us) -{ - MessageDef *new = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_PDUSESSION_SETUP_REQ); - ngap_pdusession_setup_req_t *n = &NGAP_PDUSESSION_SETUP_REQ(new); - *n = NGAP_PDUSESSION_SETUP_REQ(msg_p); - - int instance = msg_p->ittiMsgHeader.originInstance; - long timer_id; - timer_setup(0, wait_us, TASK_RRC_GNB, instance, TIMER_ONE_SHOT, new, &timer_id); -} - /** * @brief Fill PDU Session Resource Setup Response with a list of PDU Session Resources Failed to Setup * and send ITTI message to TASK_NGAP @@ -883,25 +860,6 @@ void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t ins UE->amf_ue_ngap_id = msg->amf_ue_ngap_id; - /* This is a hack. We observed that with some UEs, PDU session requests might - * come in quick succession, faster than the RRC reconfiguration for the PDU - * session requests can be carried out (UE is doing reconfig, and second PDU - * session request arrives). We don't have currently the means to "queue up" - * these transactions, which would probably involve some rework of the RRC. - * To still allow these requests to come in and succeed, we below check and delay transactions - * for 10ms. However, to not accidentally end up in infinite loops, the - * maximum number is capped on a per-UE basis as indicated in variable - * max_delays_pdu_session. */ - if (!UE->ongoing_pdusession_setup_request) - UE->max_delays_pdu_session = 100; - - if (UE->max_delays_pdu_session > 0 && (transaction_ongoing(UE) || UE->ongoing_pdusession_setup_request)) { - int wait_us = 10000; - LOG_I(RRC, "UE %d: delay PDU session setup by %d us, pending %d retries\n", UE->rrc_ue_id, wait_us, UE->max_delays_pdu_session); - delay_transaction(msg_p, wait_us); - UE->max_delays_pdu_session--; - return; - } pdusession_t to_setup[NGAP_MAX_PDU_SESSION] = {0}; for (int i = 0; i < msg->nb_pdusessions_tosetup; ++i) @@ -916,7 +874,8 @@ void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t ins send_ngap_pdu_session_setup_resp_fail(instance, msg, cause); rrc_forward_ue_nas_message(rrc, UE); } else { - UE->ongoing_pdusession_setup_request = true; + // Set ongoing_transaction flag to true + init_delayed_action(&UE->delayed_action); } } @@ -1562,68 +1521,75 @@ void rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(gNB_RRC_INST *rrc, gNB_RRC_UE DevAssert(resp->nb_of_pdusessions_released < NGAP_MAX_PDU_SESSION); resp->pdusession_release[resp->nb_of_pdusessions_released++].pdusession_id = pdusession->pdusession_id; } - session->status = PDU_SESSION_STATUS_RELEASED; } } + for (int i = 0; i < resp->nb_of_pdusessions_released; ++i) { + rm_pduSession(&UE->pduSessions, &UE->drbs, resp->pdusession_release[i].pdusession_id); + } + LOG_I(NR_RRC, "NGAP PDUSESSION RELEASE RESPONSE: rrc_ue_id %u release_pdu_sessions %d\n", resp->gNB_ue_ngap_id, resp->nb_of_pdusessions_released); itti_send_msg_to_task (TASK_NGAP, rrc->module_id, msg_p); } -//------------------------------------------------------------------------------ -int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(MessageDef *msg_p, instance_t instance) -//------------------------------------------------------------------------------ +/** @brief Process NG PDU Session Resource Release command (8.2.2 of 3GPP TS 38.413) + * upon reception the NG-RAN node shall execute the release of the requested PDU sessions. + * For each PDU session to be released the NG-RAN node shall release the corresponding + * resources over Uu and over NG, if any. */ +int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(ngap_pdusession_release_command_t *cmd, gNB_RRC_INST *rrc) { - uint32_t gNB_ue_ngap_id; - ngap_pdusession_release_command_t *cmd = &NGAP_PDUSESSION_RELEASE_COMMAND(msg_p); - gNB_ue_ngap_id = cmd->gNB_ue_ngap_id; - gNB_RRC_INST *rrc = RC.nrrrc[instance]; + uint32_t gNB_ue_ngap_id = cmd->gNB_ue_ngap_id; rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, gNB_ue_ngap_id); if (!ue_context_p) { - LOG_E(NR_RRC, "[gNB %ld] not found ue context gNB_ue_ngap_id %u \n", instance, gNB_ue_ngap_id); + LOG_E(NR_RRC, "[gNB %d] UE context not found for gNB_ue_ngap_id %u \n", rrc->module_id, gNB_ue_ngap_id); return -1; } - LOG_I(NR_RRC, "[gNB %ld] gNB_ue_ngap_id %u \n", instance, gNB_ue_ngap_id); gNB_RRC_UE_t *UE = &ue_context_p->ue_context; - LOG_I( - NR_RRC, "PDU Session Release Command: AMF_UE_NGAP_ID %lu rrc_ue_id %u release_pdusessions %d \n", cmd->amf_ue_ngap_id, gNB_ue_ngap_id, cmd->nb_pdusessions_torelease); - bool found = false; - uint8_t xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id); - UE->xids[xid] = RRC_PDUSESSION_RELEASE; + LOG_I(NR_RRC, "NG PDU Session Release command: AMF_UE_NGAP_ID=%lu, rrc_ue_id=%u, nb_pdusessions_torelease=%d \n", + cmd->amf_ue_ngap_id, + gNB_ue_ngap_id, + cmd->nb_pdusessions_torelease); + e1ap_bearer_mod_req_t req = {0}; for (int pdusession = 0; pdusession < cmd->nb_pdusessions_torelease; pdusession++) { rrc_pdu_session_param_t *pduSession = find_pduSession(&UE->pduSessions, cmd->pdusession_release_params[pdusession].pdusession_id); if (!pduSession) { LOG_E(NR_RRC, "Failed to release non-existing PDU Session %d\n", cmd->pdusession_release_params[pdusession].pdusession_id); continue; } - if (pduSession->status == PDU_SESSION_STATUS_FAILED) { - pduSession->xid = xid; + if (pduSession->status == PDU_SESSION_STATUS_TORELEASE) { + LOG_W(NR_RRC, "PDU Session %d already set to be released\n", pduSession->param.pdusession_id); continue; } - if (pduSession->status == PDU_SESSION_STATUS_ESTABLISHED) { - found = true; - LOG_I(NR_RRC, "RELEASE pdusession %d \n", pduSession->param.pdusession_id); - pduSession->status = PDU_SESSION_STATUS_TORELEASE; - pduSession->xid = xid; - } + // Set PDU session to release, regardless of the status + LOG_I(NR_RRC, "Set PDU Session %d to release\n", pduSession->param.pdusession_id); + pdu_session_to_remove_t *release = &req.pduSessionRem[req.numPDUSessionsRem++]; + release->sessionId = pduSession->param.pdusession_id; + release->cause.type = E1AP_CAUSE_RADIO_NETWORK; + release->cause.value = E1AP_RADIO_CAUSE_NORMAL_RELEASE; + pduSession->status = PDU_SESSION_STATUS_TORELEASE; } - if (found) { - // TODO RRCReconfiguration To UE - LOG_I(NR_RRC, "Send RRCReconfiguration To UE \n"); - rrc_gNB_generate_dedicatedRRCReconfiguration_release(rrc, UE, xid, cmd->nas_pdu.len, cmd->nas_pdu.buf); + if (req.numPDUSessionsRem == 0) { + LOG_E(NR_RRC, "Received NG PDU Session Release Command but no PDU Sessions to release\n"); + return -1; + } else if (!ue_associated_to_cuup(rrc, UE)) { + LOG_E(NR_RRC, "UE %d is not associated to CU-UP\n", UE->rrc_ue_id); + // TODO handle, e.g., only trigger F1 release + return 0; } else { - // gtp tunnel delete - LOG_I(NR_RRC, "gtp tunnel delete all tunnels for UE %04x\n", UE->rnti); - gtpv1u_gnb_delete_tunnel_req_t req = {0}; - req.ue_id = UE->rnti; - gtpv1u_delete_ngu_tunnel(rrc->module_id, &req); - // NGAP_PDUSESSION_RELEASE_RESPONSE - rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(rrc, UE, xid); - LOG_I(NR_RRC, "Send PDU Session Release Response \n"); + /* If present, store NAS-PDU in the UE context for later inclusion in RRCReconfiguration */ + if (cmd->nas_pdu.len > 0) { + UE->nas_pdu = copy_byte_array(cmd->nas_pdu); + } + LOG_I(NR_RRC, "Send F1 Bearer Context Modification Request with PDU Session release \n"); + req.gNB_cu_cp_ue_id = UE->rrc_ue_id; + req.gNB_cu_up_ue_id = UE->rrc_ue_id; + sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, UE); + rrc->cucp_cuup.bearer_context_mod(assoc_id, &req); } + init_delayed_action(&UE->delayed_action); return 0; } diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.h b/openair2/RRC/NR/rrc_gNB_NGAP.h index 91c7722c52..c3f692ff5f 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.h +++ b/openair2/RRC/NR/rrc_gNB_NGAP.h @@ -78,7 +78,7 @@ void rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(instance_t instance, uint32_t void rrc_gNB_send_NGAP_UE_CAPABILITIES_IND(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const NR_UECapabilityInformation_t *const ue_cap_info); -int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(MessageDef *msg_p, instance_t instance); +int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(ngap_pdusession_release_command_t *cmd, gNB_RRC_INST *rrc); void rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t xid); diff --git a/openair2/RRC/NR/rrc_gNB_UE_context.c b/openair2/RRC/NR/rrc_gNB_UE_context.c index 9a2c3fb6f6..86838927ab 100644 --- a/openair2/RRC/NR/rrc_gNB_UE_context.c +++ b/openair2/RRC/NR/rrc_gNB_UE_context.c @@ -218,8 +218,6 @@ rrc_gNB_ue_context_t *rrc_gNB_create_ue_context(sctp_assoc_t assoc_id, ue->rrc_ue_id); bool success = cu_add_f1_ue_data(ue->rrc_ue_id, &ue_data); DevAssert(success); - ue->max_delays_pdu_session = 20; /* see rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ() */ - ue->ongoing_pdusession_setup_request = false; // Initialise setup PDU Sessions list seq_arr_init(&ue->pduSessions, sizeof(rrc_pdu_session_param_t)); diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.c b/openair2/RRC/NR/rrc_gNB_radio_bearers.c index 5af84f0399..c636e9e639 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.c +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.c @@ -141,6 +141,62 @@ rrc_pdu_session_param_t *add_pduSession(seq_arr_t *sessions_ptr, const pdusessio return added; } +static bool eq_drb_pdu_session_id(const void *vval, const void *vit) +{ + const int *id = (const int *)vval; + const drb_t *elem = (const drb_t *)vit; + return elem->pdusession_id == *id; +} + +/** @brief Finds the first DRB with the given PDU session ID. + * @return Pointer to matching drb_t or NULL if not found. */ +static drb_t *find_drb_by_pdusession_id(seq_arr_t *seq, int pdusession_id) +{ + DevAssert(seq); + DevAssert(pdusession_id > 0 && pdusession_id <= NGAP_MAX_PDU_SESSION); + elm_arr_t elm = find_if(seq, &pdusession_id, eq_drb_pdu_session_id); + if (elm.found) + return (drb_t *)elm.it; + return NULL; +} + +/** @brief Removes a DRB from the list + * @param drbs The DRB list + * @param drb Pointer to the DRB to remove */ +static void nr_rrc_rm_drb(seq_arr_t *drbs, drb_t *drb) +{ + DevAssert(drbs); + DevAssert(drb); + LOG_I(NR_RRC, "Removing DRB ID %d (PDU Session ID=%d)\n", drb->drb_id, drb->pdusession_id); + seq_arr_erase_deep(drbs, drb, free_drb); +} + +/** @brief Removes a PDU Session from the list by ID + * Also removes all associated DRBs for this PDU session. + * @return true if successfully removed, false if not found */ +bool rm_pduSession(seq_arr_t *sessions, seq_arr_t *drbs, int pdusession_id) +{ + DevAssert(sessions); + DevAssert(drbs); + + rrc_pdu_session_param_t *session = find_pduSession(sessions, pdusession_id); + + if (session) { + LOG_I(NR_RRC, "Removing PDU Session %d from RRC setup list\n", pdusession_id); + // Remove all associated DRBs first + drb_t *drb; + while ((drb = find_drb_by_pdusession_id(drbs, pdusession_id))) { + nr_rrc_rm_drb(drbs, drb); + } + // Then remove the PDU session + seq_arr_erase_deep(sessions, session, free_pdusession); + return true; + } + + LOG_W(NR_RRC, "pdusession_id=%d not found to remove\n", pdusession_id); + return false; +} + /** @brief Add drb_t item in the UE context list for @param pdusession_id */ drb_t *nr_rrc_add_drb(seq_arr_t *drb_ptr, int pdusession_id, nr_pdcp_configuration_t *pdcp) { diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.h b/openair2/RRC/NR/rrc_gNB_radio_bearers.h index f6821e314b..a1b09a5b02 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.h +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.h @@ -39,6 +39,10 @@ rrc_pdu_session_param_t *add_pduSession(seq_arr_t *sessions_ptr, const pdusessio /// @brief get PDU session of UE ue through the DRB drb_id rrc_pdu_session_param_t *find_pduSession_from_drbId(gNB_RRC_UE_t *ue, int drb_id); +/// @brief Remove PDU Session from RRC list +/// Also removes all associated DRBs for this PDU session. +bool rm_pduSession(seq_arr_t *sessions, seq_arr_t *drbs, int pdusession_id); + /// @brief set PDCP configuration in E1 Bearer Context Management message bearer_context_pdcp_config_t set_bearer_context_pdcp_config(const nr_pdcp_configuration_t pdcp, bool um_on_default_drb, diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index 6b7688c19c..24088749a5 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -1655,13 +1655,29 @@ static void nr_rrc_signal_maxrtxindication(int ue_id) itti_send_msg_to_task(TASK_RRC_NRUE, ue_id, msg); } +/** @brief Release all active RLC entities for a UE and set to inactive. + * This is typically used when tearing down all DRBs at UE side, + * such as after a PDU session release or full reconfiguration. + * @param rrc Pointer to NR_UE_RRC_INST_t structure + * @param id Logical Channel ID (must be in range [0, NR_MAX_NUM_LCID-1]) */ +static void nr_rrc_release_rlc_entity(NR_UE_RRC_INST_t *rrc, int id) +{ + DevAssert(rrc); + DevAssert(id >= 0 && id < NR_MAX_NUM_LCID); + if (rrc->active_RLC_entity[id]) { + rrc->active_RLC_entity[id] = false; + nr_rlc_release_entity(rrc->ue_id, id); + LOG_I(RLC, "Released RLC entity: ue_id=%ld, lc_id=%d\n", rrc->ue_id, id); + } +} + static void nr_rrc_manage_rlc_bearers(NR_UE_RRC_INST_t *rrc, const NR_CellGroupConfig_t *cellGroupConfig) { if (cellGroupConfig->rlc_BearerToReleaseList != NULL) { for (int i = 0; i < cellGroupConfig->rlc_BearerToReleaseList->list.count; i++) { NR_LogicalChannelIdentity_t *lcid = cellGroupConfig->rlc_BearerToReleaseList->list.array[i]; AssertFatal(lcid, "LogicalChannelIdentity shouldn't be null here\n"); - nr_rlc_release_entity(rrc->ue_id, *lcid); + nr_rrc_release_rlc_entity(rrc, *lcid); } } @@ -1842,10 +1858,7 @@ static void nr_rrc_rrcsetup_fallback(NR_UE_RRC_INST_t *rrc) } } for (int i = 1; i < NR_MAX_NUM_LCID; i++) { - if (rrc->active_RLC_entity[i]) { - rrc->active_RLC_entity[i] = false; - nr_rlc_release_entity(rrc->ue_id, i); - } + nr_rrc_release_rlc_entity(rrc, i); } nr_sdap_delete_ue_entities(rrc->ue_id); @@ -2947,10 +2960,7 @@ void nr_rrc_going_to_IDLE(NR_UE_RRC_INST_t *rrc, } } for (int i = 0; i < NR_MAX_NUM_LCID; i++) { - if (rrc->active_RLC_entity[i]) { - rrc->active_RLC_entity[i] = false; - nr_rlc_release_entity(rrc->ue_id, i); - } + nr_rrc_release_rlc_entity(rrc, i); } for (int i = 0; i < NB_CNX_UE; i++) { diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index 9056c6e733..dc747e5a60 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -596,14 +596,13 @@ bool nr_sdap_delete_entity(ue_id_t ue_id, int pdusession_id) LOG_E(SDAP, "SDAP entities not established or Invalid range of pdusession_id [0, 256].\n"); return false; } - LOG_D(SDAP, "Deleting SDAP entity for UE %lx and PDU Session id %d\n", ue_id, entityPtr->pdusession_id); if (entityPtr->ue_id == ue_id && entityPtr->pdusession_id == pdusession_id) { sdap_info.sdap_entity_llist = sdap_info.sdap_entity_llist->next_entity; if (entityPtr->pdusession_sock != -1) remove_ip_if(entityPtr); free(entityPtr); - LOG_D(SDAP, "Successfully deleted Entity.\n"); + LOG_D(SDAP, "Successfully deleted SDAP entity for UE %lx and PDU Session id %d\n", ue_id, pdusession_id); return true; } else { while ((entityPtr->ue_id != ue_id || entityPtr->pdusession_id != pdusession_id) && entityPtr->next_entity != NULL @@ -619,7 +618,7 @@ bool nr_sdap_delete_entity(ue_id_t ue_id, int pdusession_id) remove_ip_if(entityPtr); } free(entityPtr); - LOG_D(SDAP, "Successfully deleted Entity.\n"); + LOG_D(SDAP, "Successfully deleted Entity for UE %lx and PDU Session id %d\n", ue_id, pdusession_id); return true; } } diff --git a/openair3/NAS/NR_UE/nr_nas_msg.c b/openair3/NAS/NR_UE/nr_nas_msg.c index d0230e0a03..5a58af6b84 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.c +++ b/openair3/NAS/NR_UE/nr_nas_msg.c @@ -1772,7 +1772,7 @@ static void send_nas_5gmm_ind(instance_t instance, const Guti5GSMobileIdentity_t itti_send_msg_to_task(TASK_RRC_NRUE, instance, msg); } -static void request_pdusession(nr_ue_nas_t *nas, int pdusession_id) +void request_pdusession(nr_ue_nas_t *nas, int pdusession_id) { MessageDef *message_p = itti_alloc_new_message(TASK_NAS_NRUE, nas->UE_id, NAS_PDU_SESSION_REQ); NAS_PDU_SESSION_REQ(message_p).pdusession_id = pdusession_id; diff --git a/openair3/NAS/NR_UE/nr_nas_msg.h b/openair3/NAS/NR_UE/nr_nas_msg.h index 2d62c799ea..7a19997444 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg.h +++ b/openair3/NAS/NR_UE/nr_nas_msg.h @@ -113,5 +113,6 @@ void *nas_nrue_task(void *args_p); void *nas_nrue(void *args_p); void nas_init_nrue(int num_ues); void nr_ue_create_ip_if(const char *ifnameprefix, const char *ipv4, const char *ipv6, int ue_id, int pdu_session_id); +void request_pdusession(nr_ue_nas_t *nas, int pdusession_id); #endif /* __NR_NAS_MSG_SIM_H__*/ diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index c524129098..ed15d48f80 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -292,7 +292,7 @@ static int gtpv1uCreateAndSendMsg(gtpv1u_bearer_t *bearer, 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", + "Peer IP:" IPV4_ADDR " port:%u outgoing TEID:0x%x\n", IPV4_ADDR_FORMAT(to->sin_addr.s_addr), htons(to->sin_port), bearer->teid_outgoing); @@ -332,7 +332,7 @@ static void _gtpv1uSendDirect(instance_t instance, } LOG_D(GTPU, - "[%ld] sending a packet to UE:RAB:teid %lx/%x/%x, len %lu, oldseq %d, oldnum %d\n", + "[%ld] sending a packet to UE:RAB:TEID %lx/%d/0x%x, len %lu, oldseq %d, oldnum %d\n", instance, ue_id, bearer_id, @@ -492,7 +492,7 @@ static void gtpv1uEndTunnel(instance_t instance, gtpv1u_enb_end_marker_req_t *re } LOG_D(GTPU, - "[%ld] sending a end packet packet to UE:RAB:teid %lx/%x/%x\n", + "[%ld] sending a end packet packet to UE:RAB:TEID %lx/%d/0x%x\n", instance, ue_id, bearer_id, @@ -641,7 +641,7 @@ void GtpuUpdateTunnelOutgoingAddressAndTeid(instance_t instance, 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", + "[%ld] UE ID %ld: Update tunnel TEID incoming 0x%x outgoing 0x%x to remote IPv4 %s, IPv6 %s, port %d\n", instance, ue_id, ptr2->second.teid_incoming, @@ -676,7 +676,7 @@ teid_t newGtpuCreateTunnel(instance_t instance, 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); + LOG_W(GTPU, "[%ld] generated a random TEID that exists, re-generating (0x%x)\n", instance, incoming_teid); incoming_teid = gtpv1uNewTeid(); }; @@ -721,7 +721,7 @@ teid_t newGtpuCreateTunnel(instance_t instance, char ip4[INET_ADDRSTRLEN]; char ip6[INET6_ADDRSTRLEN]; LOG_I(GTPU, - "[%ld] UE ID %ld: Create tunnel TEID incoming %x outgoing %x to remote IPv4 %s, IPv6 %s, port %d\n", + "[%ld] UE ID %ld: Create tunnel TEID incoming 0x%x outgoing 0x%x to remote IPv4 %s, IPv6 %s, port %d\n", instance, ue_id, bearer.teid_incoming, @@ -825,7 +825,7 @@ int gtpv1u_create_ngu_tunnel(const instance_t instance, gtpCallbackSDAP callBackSDAP) { LOG_D(GTPU, - "[%ld] Start create tunnels for ue id %lu, num_tunnels %d, sgw_S1u_teid %x\n", + "[%ld] Start create tunnels for ue id %lu, num_tunnels %d, TEID 0x%x\n", instance, create_tunnel_req->ue_id, create_tunnel_req->num_tunnels, @@ -905,17 +905,17 @@ int newGtpuDeleteOneTunnel(instance_t instance, ue_id_t ue_id, int rb_id) } map::iterator rb_it = ue_it->second.bearers.find(rb_id); if (rb_it == ue_it->second.bearers.end()) { - LOG_E(GTPU, "%s() UE %ld has no bearer %d, available\n", __func__, ue_id, rb_id); + LOG_E(GTPU, "%s() UE %ld has no tunnel for bearer %d\n", __func__, ue_id, rb_id); pthread_mutex_unlock(&globGtp.gtp_lock); return !GTPNOK; } - int teid = rb_it->second.teid_incoming; + teid_t teid = rb_it->second.teid_incoming; globGtp.te2ue_mapping.erase(teid); 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); + LOG_I(GTPU, "Deleted tunnel TEID 0x%x for bearer %d of UE %ld, remaining tunnels:\n", teid, rb_id, ue_id); for (auto b : ue_it->second.bearers) - LOG_I(GTPU, "bearer %ld\n", b.first); + LOG_I(GTPU, "Bearer %ld\n", b.first); return !GTPNOK; } @@ -982,45 +982,12 @@ int gtpv1u_delete_all_s1u_tunnel(const instance_t instance, const rnti_t rnti) return newGtpuDeleteAllTunnels(instance, 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); - pthread_mutex_lock(&globGtp.gtp_lock); - getInstRetInt(compatInst(instance)); - getUeRetInt(inst, ue_id); - int nb = 0; - - 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]); - } else { - globGtp.te2ue_mapping.erase(ptr2->second.teid_incoming); - nb++; - } - } - - if (ptrUe->second.bearers.size() == 0) - // no tunnels on this ue id, erase the ue entry - inst->ue2te_mapping.erase(ptrUe); - - pthread_mutex_unlock(&globGtp.gtp_lock); - LOG_I(GTPU, "[%ld] Deleted all tunnels for ue id %lu (%d tunnels deleted)\n", instance, ue_id, nb); - 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"); 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); -} - 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}; @@ -1043,7 +1010,7 @@ static int Gtpv1uHandleEchoReq(int h, uint8_t *msgBuf, uint32_t msgBufLen, const } 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); + LOG_D(GTPU, "[%d] Received a echo request, TEID: 0x%x, seq: %hu\n", h, msgHdr->teid, seq); uint8_t recovery[2] = {14, 0}; gtpv1u_bearer_t bearer = create_bearer(h, addr, ntohl(msgHdr->teid), seq); return gtpv1uCreateAndSendMsg(&bearer, @@ -1061,10 +1028,11 @@ static int Gtpv1uHandleError(int h, uint8_t *msgBuf, uint32_t msgBufLen, const s if (msgBufLen < sizeof(Gtpv1uError)) LOG_E(GTPU, "Received GTP error indication with truncated size %u (mini size: %lu)\n", msgBufLen,sizeof(Gtpv1uError)+4); Gtpv1uError *msg = ( Gtpv1uError *)msgBuf; - LOG_E(GTPU, "Received GTP error indication: \n" - " TEID %u (must be 0 from TS 29.281)\n" - " TV id for TEID %u (must be 16)\n" - " TEID in error %u (should be a TEID we sent)\n" + LOG_E(GTPU, + "Received GTP error indication: \n" + " TEID 0x%x (must be 0 from TS 29.281)\n" + " TV id for TEID 0x%x (must be 16)\n" + " TEID in error 0x%x (should be a TEID we sent)\n" " TV id for GTP addr %u (should be 133)\n" " len for addr of UPF %u (should be IPv4 or IPv6 len)" " (TS 29.281 Sec 7.3.1 Error Handling not implemented)\n", @@ -1103,7 +1071,7 @@ static int Gtpv1uHandleEndMarker(int h, uint8_t *msgBuf, uint32_t msgBufLen, con 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 (0x%x) Dropping!\n", h, msgHdr->teid); pthread_mutex_unlock(&globGtp.gtp_lock); return GTPNOK; } @@ -1132,7 +1100,7 @@ static int Gtpv1uHandleEndMarker(int h, uint8_t *msgBuf, uint32_t msgBufLen, con 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:0x%x\n", h, ntohl(msgHdr->teid)); return !GTPNOK; } @@ -1149,7 +1117,7 @@ static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, const st 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 (0x%x) Dropping!\n", h, ntohl(msgHdr->teid)); pthread_mutex_unlock(&globGtp.gtp_lock); return GTPNOK; } @@ -1282,7 +1250,7 @@ static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, const st 1); } - 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:0x%x\n", h, msgBufLen - offset, ntohl(msgHdr->teid)); return !GTPNOK; } diff --git a/openair3/ocp-gtpu/gtp_itf.h b/openair3/ocp-gtpu/gtp_itf.h index c31f8c935e..9274cd52f2 100644 --- a/openair3/ocp-gtpu/gtp_itf.h +++ b/openair3/ocp-gtpu/gtp_itf.h @@ -100,9 +100,6 @@ typedef struct gtpv1u_gnb_delete_tunnel_req_s gtpv1u_gnb_delete_tunnel_req_t; gtpCallback callBack, gtpCallbackSDAP callBackSDAP); - int gtpv1u_delete_ngu_tunnel( const instance_t instance, - gtpv1u_gnb_delete_tunnel_req_t *req); - int gtpv1u_update_ue_id(const instance_t instanceP, ue_id_t old_ue_id, ue_id_t new_ue_id); // New API @@ -124,7 +121,6 @@ typedef struct gtpv1u_gnb_delete_tunnel_req_s gtpv1u_gnb_delete_tunnel_req_t; int newGtpuDeleteOneTunnel(instance_t instance, ue_id_t ue_id, int rb_id); int newGtpuDeleteAllTunnels(instance_t instance, ue_id_t ue_id); - int newGtpuDeleteTunnels(instance_t instance, ue_id_t ue_id, int nbTunnels, int *pdusession_id); void gtpv1uSendDirect(instance_t instance, ue_id_t ue_id, int bearer_id, uint8_t *buf, size_t len, bool seqNumFlag, bool npduNumFlag); void gtpv1uSendDirectWithNRUSeqNum(instance_t instance,