Commit 1948858c authored by Robert Schmidt's avatar Robert Schmidt

Merge remote-tracking branch 'origin/rrc-pdu-session-release' into integration_2025_w46 (!3684)

PDU Session Release

This MR implements end-to-end PDU Session Release functionality
according to 3GPP TS 38.413 and TS 38.331 specifications.

Reason for Change Previous implementation had incomplete and wrongly
places tunnel cleanup during PDU session release. UP cleanup shall be
done at CUUP, RRCReconfiguration shall be sent after retrieval of
updated CellGroupConfig from the DU. This implies F1 and E1 exchanges
during PDU session release procedure.

Major Changes:

1. RRC PDU Session Release: DRBs and PDU session teardown in NGAP PDU
   Session Release Response callback
  - Helper Functions: Introduced rm_pduSession,
    find_drb_by_pdusession_id and rm_drbs() helpers to locate and clean
    up DRBs linked to a specific PDU session
  - Targeted Cleanup: RRC PDU Session and per-session DRB removal in
    rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE()
  - Status Cleanup: Removed unused PDU_SESSION_STATUS_RELEASED status
2. RRC PDU Session Release: Refactor
   rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND
  - Any PDU session in the pdusession_release_params list that is
    present in the RRC list can be set to release, regardless of its
    status
  - Function Signature Update: Updated function signature and other
    minor changes
  - Response Handling: rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE is
    sent upon completion of RRCReconfiguration
3. RRC PDU Session Release: add missing procedures E1AP Integration:
  - Populates E1AP Bearer Modification for each to be released session
  - PDCP Enhancement: Added nr_pdcp_release_drbs() to remove all DRBs
    linked to a given PDU session in CUUP
  - GTP-U Cleanup: GTP tunnel teardown handled in CUUP
    e1_bearer_context_modif() using correct F1 or N3 GTP instance
4. NR PDCP/GTP:
  - Enhanced Tunnel Deletion Logic: Separate handling for N3 tunnels (1
    per PDU session) and F1-U tunnels (1 per DRB)
  - New PDCP API Function: Added nr_pdcp_get_drb_ids_for_pdusession()
    function to retrieve all DRB IDs associated with a specific PDU
    session
5. RRC PDU Session Release: F1AP-based UE Context Modification:
  - Replaced direct RRC message transfer with F1AP UE Context
    Modification Request for proper retrieval from DU
  - Cell Group Configuration: handling of updated CellGroupConfig from
    DU ensures DRB release at CU
6. Docs and testing:
  - Added PDU session add/remove test cases to 25 PRB RFSIM pipeline
  - Documentation: Added PDU Session Release sequence diagram
  - Telnet Commands: Added ci command to trigger NGAP PDU Session
    Release and ciUE command to trigger establishment of a new PDU
    Session
7. Additional fixes:
  - UE RLC Management: ensure release of RLC entities
  - Unify NG delay control before calling the RRC NGAP handler, for both
    Setup and Release
8. Code Cleanup: Cleaned up unused GTP teardown functions.

Closes #867
parents b57495b3 1fea6108
......@@ -62,6 +62,7 @@ ForEachMacros:
- BOOST_FOREACH
- RB_FOREACH
- UE_iterator
- FOR_EACH_SEQ_ARR
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^<ext/.*\.h>'
......
......@@ -36,6 +36,11 @@
020003
030001
030002
050001
050002
050003
020001
020002
100001
222222
</TestCaseRequestedList>
......@@ -139,6 +144,27 @@
<iperf_bitrate_threshold>90</iperf_bitrate_threshold>
</testCase>
<testCase id="050001">
<class>Custom_Command</class>
<desc>Trigger PDU Session Release via gNB telnet</desc>
<node>acamas</node>
<command>echo ci pdu_session_release 2 | ncat 192.168.71.140 9090</command>
</testCase>
<testCase id="050002">
<class>Custom_Command</class>
<desc>Add PDU Session via UE telnet</desc>
<node>acamas</node>
<command>echo ci add_pdu_session 6,7 | ncat 192.168.71.150 8091</command>
</testCase>
<testCase id="050003">
<class>Custom_Command</class>
<desc>Trigger PDU Session Release via gNB telnet</desc>
<node>acamas</node>
<command>echo ci pdu_session_release 6 | ncat 192.168.71.140 9090</command>
</testCase>
<testCase id="100001">
<class>Undeploy_Object</class>
<always_exec>true</always_exec>
......
......@@ -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:
......
......@@ -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")
......
......@@ -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},
};
......
......@@ -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},
};
......
......@@ -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)
```
......@@ -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 {
......
......@@ -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"
......
......@@ -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;
......
......@@ -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));
......
......@@ -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);
}
......
......@@ -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;
......
......@@ -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,
......
......@@ -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);
}
......
......@@ -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 */
......
......@@ -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];
......
......@@ -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);
......
......@@ -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;
......
......@@ -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
This diff is collapsed.
......@@ -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;
}
......
......@@ -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);
......
......@@ -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));
......
......@@ -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)
{
......
......@@ -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,
......
......@@ -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++) {
......
......@@ -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;
}
}
......
......@@ -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;
......
......@@ -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__*/
......@@ -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<ue_id_t, gtpv1u_bearer_t>::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;
}
......
......@@ -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,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment