From 653c0fd6d5c084b633af9584975c29d592cb939b Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 17 Jun 2025 13:26:35 +0200 Subject: [PATCH] Telnet: add ci command to trigger NGAP PDU Session Release This commit introduces a new telnet server command `pdu_session_release` to manually trigger the NGAP PDU Session Release Command message from the telnet ci shell. This is useful for integration testing of PDU session teardown behavior. Details: - Added `trigger_ngap_pdu_session_release()` to send NGAP_PDUSESSION_RELEASE_COMMAND to the RRC task, targeting the specified `gNB_ue_ngap_id`. - The command currently releases PDU session ID 10 (hardcoded). - Hooked into telnet shell with command: `pdu_session_release [gNB_ue_ngap_id]` Usage example: telnet > pdu_session_release 1 --- common/utils/telnetsrv/telnetsrv_ci.c | 82 +++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) 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}, };