Commit 1be038c8 authored by Robert Schmidt's avatar Robert Schmidt

Merge remote-tracking branch 'origin/fix-cuup-assert-reconnect' into integration_2025_w06 (!3243)

Reset E1 UE contexts after E1 Setup Response

38.463 sec. 8.2.3 says

> This procedure also re-initialises the E1AP UE-related contexts (if
> any) and erases all related signalling connections in the two nodes
> like a Reset procedure would do.

Hence, delete all contexts after reception of E1 Setup Response. This
minimizes a risk of receiving an E1 bearer setup req for an existing UE,
which currently leads to an assert [we trust the CU-CP does not send the
same UE ID twice, as we only "mirror" the existing UE ID].  After this
MR, this is what happens if these entities fail:

- CU-CP fails: all context lost implicitly, upon reconnection, both DU
  and CU-UP reset all contexts (like "start from zero")
- DU fails: CU-CP triggers NGAP UE context release request immediately
  (radio connection with UE lost), all UE contexts attached to this DU
  cleared (like "start from zero" for affected DU)
- CU-UP fails: initially, nothing (UE has no user plane); upon
  reconnection of CU-UP, CU-CP triggers NGAP UE context release request
  (cause radio connection with UE lost) and sends F1 Reset to DU, all UE
  contexts associated to this DU cleared (like "start from zero")

for the last case, a possible improvement could be to use the F1 Reset
message to only clear affected UE conetxts. I chose not to do this,
because this MR is very big now already, and possible "victim" UEs
that were not at that CU-UP will reconnect as well
parents 9b4e0677 e8661f5a
......@@ -563,24 +563,25 @@ typedef enum F1AP_ResetType_e {
F1AP_RESET_PART_OF_F1_INTERFACE
} f1ap_ResetType_t;
typedef struct f1ap_ue_to_reset_t {
uint32_t *gNB_CU_ue_id;
uint32_t *gNB_DU_ue_id;
} f1ap_ue_to_reset_t;
typedef struct f1ap_reset_t {
uint64_t transaction_id;
f1ap_Cause_t cause;
long cause_value;
f1ap_ResetType_t reset_type;
struct {
uint32_t gNB_CU_ue_id;
uint32_t gNB_DU_ue_id;
} ue_to_reset[F1AP_MAX_NO_OF_INDIVIDUAL_CONNECTIONS_TO_RESET];
int num_ue_to_reset;
f1ap_ue_to_reset_t *ue_to_reset; // array of num_ue_to_reset elements
} f1ap_reset_t;
typedef struct f1ap_reset_ack_t {
uint64_t transaction_id;
struct {
uint32_t gNB_CU_ue_id;
uint32_t gNB_DU_ue_id;
} ue_to_reset[F1AP_MAX_NO_OF_INDIVIDUAL_CONNECTIONS_TO_RESET];
uint16_t criticality_diagnostics;
uint64_t transaction_id;
int num_ue_to_reset;
f1ap_ue_to_reset_t *ue_to_reset; // array of num_ue_to_reset elements
//uint16_t criticality_diagnostics; // not implemented as of now
} f1ap_reset_ack_t;
#endif /* F1AP_MESSAGES_TYPES_H_ */
......@@ -219,6 +219,8 @@ int e1apCUUP_handle_SETUP_RESPONSE(sctp_assoc_t assoc_id, e1ap_upcp_inst_t *inst
return -1;
}
free_e1ap_cuup_setup_response(&out);
e1_reset(); // reset all UE contexts, if any: see 38.463 sec 8.2.3
return 0;
}
......
......@@ -36,11 +36,24 @@
#include "f1ap_cu_interface_management.h"
#include "f1ap_default_values.h"
#include "lib/f1ap_interface_management.h"
#include "lib/f1ap_lib_common.h"
int CU_handle_RESET_ACKNOWLEDGE(instance_t instance, sctp_assoc_t assoc_id, uint32_t stream, F1AP_F1AP_PDU_t *pdu)
{
AssertFatal(1 == 0, "Not implemented yet\n");
DevAssert(pdu != NULL);
f1ap_reset_ack_t ack = {0};
/* Decode */
if (!decode_f1ap_reset_ack(pdu, &ack)) {
LOG_E(F1AP, "cannot decode F1AP Reset acknowledgement\n");
free_f1ap_reset_ack(&ack);
return -1;
}
/* Send to RRC (ITTI) */
MessageDef *message_p = itti_alloc_new_message(TASK_CU_F1, 0, F1AP_RESET_ACK);
message_p->ittiMsgHeader.originInstance = assoc_id;
F1AP_RESET_ACK(message_p) = ack; /* "move" message into ITTI, RRC thread will free it */
itti_send_msg_to_task(TASK_RRC_GNB, GNB_MODULE_ID_TO_INSTANCE(instance), message_p);
return 0;
}
int CU_send_RESET_ACKNOWLEDGE(sctp_assoc_t assoc_id, const f1ap_reset_ack_t *ack)
......@@ -48,6 +61,28 @@ int CU_send_RESET_ACKNOWLEDGE(sctp_assoc_t assoc_id, const f1ap_reset_ack_t *ack
AssertFatal(1 == 0, "Not implemented yet\n");
}
void CU_send_RESET(sctp_assoc_t assoc_id, const f1ap_reset_t *reset)
{
uint8_t *buffer = NULL;
uint32_t len = 0;
/* Encode F1 Reset */
F1AP_F1AP_PDU_t *pdu = encode_f1ap_reset(reset);
if (pdu == NULL) {
LOG_E(F1AP, "failed to create asn.1 message for f1ap reset\n");
return;
}
/* encode ASN.1 PER */
int encoded = f1ap_encode_pdu(pdu, &buffer, &len);
ASN_STRUCT_FREE(asn_DEF_F1AP_F1AP_PDU, pdu);
if (encoded <= 0) {
LOG_E(F1AP, "Failed to encode F1 reset\n");
return;
}
f1ap_itti_send_sctp_data_req(assoc_id, buffer, len);
}
/**
* @brief F1AP Setup Request decoding (9.2.1.4 of 3GPP TS 38.473) and transfer to RRC
*/
......
......@@ -36,6 +36,7 @@
/*
* Reset
*/
void CU_send_RESET(sctp_assoc_t assoc_id, const f1ap_reset_t *reset);
int CU_handle_RESET_ACKNOWLEDGE(instance_t instance, sctp_assoc_t assoc_id, uint32_t stream, F1AP_F1AP_PDU_t *pdu);
int CU_send_RESET_ACKNOWLEDGE(sctp_assoc_t assoc_id, const f1ap_reset_ack_t *ack);
......
......@@ -35,6 +35,7 @@
#include "f1ap_cu_rrc_message_transfer.h"
#include "f1ap_cu_ue_context_management.h"
#include "lib/f1ap_rrc_message_transfer.h"
#include "lib/f1ap_interface_management.h"
#include "f1ap_cu_paging.h"
#include "f1ap_cu_task.h"
#include <openair3/ocp-gtpu/gtp_itf.h>
......@@ -151,6 +152,11 @@ void *F1AP_CU_task(void *arg) {
&received_msg->ittiMsg.sctp_data_ind);
break;
case F1AP_RESET:
CU_send_RESET(assoc_id, &F1AP_RESET(received_msg));
free_f1ap_reset(&F1AP_RESET(received_msg));
break;
case F1AP_RESET_ACK:
CU_send_RESET_ACKNOWLEDGE(assoc_id, &F1AP_RESET_ACK(received_msg));
break;
......
......@@ -35,7 +35,6 @@
#include "f1ap_itti_messaging.h"
#include "f1ap_du_interface_management.h"
#include "lib/f1ap_interface_management.h"
#include "lib/f1ap_lib_common.h"
#include "openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.h"
#include "assertions.h"
......@@ -118,35 +117,21 @@ int DU_handle_RESET(instance_t instance, sctp_assoc_t assoc_id, uint32_t stream,
int DU_send_RESET_ACKNOWLEDGE(sctp_assoc_t assoc_id, const f1ap_reset_ack_t *ack)
{
F1AP_F1AP_PDU_t pdu= {0};
uint8_t *buffer;
uint32_t len;
/* Create */
/* 0. pdu Type */
pdu.present = F1AP_F1AP_PDU_PR_successfulOutcome;
asn1cCalloc(pdu.choice.successfulOutcome, successMsg);
successMsg->procedureCode = F1AP_ProcedureCode_id_Reset;
successMsg->criticality = F1AP_Criticality_reject;
successMsg->value.present = F1AP_SuccessfulOutcome__value_PR_ResetAcknowledge;
F1AP_ResetAcknowledge_t *f1ResetAcknowledge = &successMsg->value.choice.ResetAcknowledge;
/* mandatory */
/* c1. Transaction ID (integer value) */
asn1cSequenceAdd(f1ResetAcknowledge->protocolIEs.list, F1AP_ResetAcknowledgeIEs_t, ieC1);
ieC1->id = F1AP_ProtocolIE_ID_id_TransactionID;
ieC1->criticality = F1AP_Criticality_reject;
ieC1->value.present = F1AP_ResetAcknowledgeIEs__value_PR_TransactionID;
ieC1->value.choice.TransactionID = ack->transaction_id;
/* TODO: (Optional) partialF1Interface, criticality diagnostics */
F1AP_F1AP_PDU_t *pdu = encode_f1ap_reset_ack(ack);
if (!pdu) {
LOG_E(F1AP, "failed to create ASN.1 message for reset acknowledge\n");
return -1;
}
/* encode */
if (f1ap_encode_pdu(&pdu, &buffer, &len) < 0) {
uint8_t *buffer;
uint32_t len;
int encoded = f1ap_encode_pdu(pdu, &buffer, &len);
ASN_STRUCT_FREE(asn_DEF_F1AP_F1AP_PDU, pdu);
if (encoded <= 0) {
LOG_E(F1AP, "Failed to encode F1ResetAcknowledge\n");
return -1;
}
/* send */
ASN_STRUCT_RESET(asn_DEF_F1AP_F1AP_PDU, &pdu);
f1ap_itti_send_sctp_data_req(assoc_id, buffer, len);
return 0;
}
......
......@@ -35,6 +35,7 @@
#include "f1ap_du_ue_context_management.h"
#include "f1ap_du_rrc_message_transfer.h"
#include "lib/f1ap_rrc_message_transfer.h"
#include "lib/f1ap_interface_management.h"
#include "f1ap_du_task.h"
#include <openair3/ocp-gtpu/gtp_itf.h>
......@@ -150,6 +151,7 @@ void *F1AP_DU_task(void *arg) {
case F1AP_RESET_ACK:
DU_send_RESET_ACKNOWLEDGE(assoc_id, &F1AP_RESET_ACK(msg));
free_f1ap_reset_ack(&F1AP_RESET_ACK(msg));
break;
case F1AP_GNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE:
......
......@@ -27,6 +27,20 @@
struct F1AP_F1AP_PDU;
/* F1 Reset */
struct F1AP_F1AP_PDU *encode_f1ap_reset(const f1ap_reset_t *msg);
bool decode_f1ap_reset(const struct F1AP_F1AP_PDU *pdu, f1ap_reset_t *out);
void free_f1ap_reset(f1ap_reset_t *msg);
bool eq_f1ap_reset(const f1ap_reset_t *a, const f1ap_reset_t *b);
f1ap_reset_t cp_f1ap_reset(const f1ap_reset_t *orig);
/* F1 Reset Ack */
struct F1AP_F1AP_PDU *encode_f1ap_reset_ack(const f1ap_reset_ack_t *msg);
bool decode_f1ap_reset_ack(const struct F1AP_F1AP_PDU *pdu, f1ap_reset_ack_t *out);
void free_f1ap_reset_ack(f1ap_reset_ack_t *msg);
bool eq_f1ap_reset_ack(const f1ap_reset_ack_t *a, const f1ap_reset_ack_t *b);
f1ap_reset_ack_t cp_f1ap_reset_ack(const f1ap_reset_ack_t *orig);
struct F1AP_F1AP_PDU *encode_f1ap_setup_request(const f1ap_setup_req_t *msg);
bool decode_f1ap_setup_request(const struct F1AP_F1AP_PDU *pdu, f1ap_setup_req_t *out);
f1ap_setup_req_t cp_f1ap_setup_request(const f1ap_setup_req_t *msg);
......
......@@ -109,3 +109,58 @@ uint8_t *cp_octet_string(const OCTET_STRING_t *os, int *len)
*len = os->size;
return buf;
}
F1AP_Cause_t encode_f1ap_cause(f1ap_Cause_t cause, long cause_value)
{
F1AP_Cause_t f1_cause = {0};
switch (cause) {
case F1AP_CAUSE_RADIO_NETWORK:
f1_cause.present = F1AP_Cause_PR_radioNetwork;
f1_cause.choice.radioNetwork = cause_value;
break;
case F1AP_CAUSE_TRANSPORT:
f1_cause.present = F1AP_Cause_PR_transport;
f1_cause.choice.transport = cause_value;
break;
case F1AP_CAUSE_PROTOCOL:
f1_cause.present = F1AP_Cause_PR_protocol;
f1_cause.choice.protocol = cause_value;
break;
case F1AP_CAUSE_MISC:
f1_cause.present = F1AP_Cause_PR_misc;
f1_cause.choice.misc = cause_value;
break;
case F1AP_CAUSE_NOTHING:
default:
AssertFatal(false, "unknown cause value %d\n", cause);
break;
}
return f1_cause;
}
bool decode_f1ap_cause(F1AP_Cause_t f1_cause, f1ap_Cause_t *cause, long *cause_value)
{
switch (f1_cause.present) {
case F1AP_Cause_PR_radioNetwork:
*cause = F1AP_CAUSE_RADIO_NETWORK;
*cause_value = f1_cause.choice.radioNetwork;
break;
case F1AP_Cause_PR_transport:
*cause = F1AP_CAUSE_TRANSPORT;
*cause_value = f1_cause.choice.transport;
break;
case F1AP_Cause_PR_protocol:
*cause = F1AP_CAUSE_PROTOCOL;
*cause_value = f1_cause.choice.protocol;
break;
case F1AP_Cause_PR_misc:
*cause = F1AP_CAUSE_MISC;
*cause_value = f1_cause.choice.radioNetwork;
break;
case F1AP_Cause_PR_NOTHING:
default:
PRINT_ERROR("received illegal F1AP cause %d\n", f1_cause.present);
return false;
}
return true;
}
......@@ -27,6 +27,9 @@
#include <stdio.h>
#include "openair3/UTILS/conversions.h"
#include "F1AP_Cause.h"
#include "f1ap_messages_types.h"
#ifdef ENABLE_TESTS
#define PRINT_ERROR(...) fprintf(stderr, ##__VA_ARGS__)
#else
......@@ -82,4 +85,7 @@ bool eq_f1ap_tx_bandwidth(const struct f1ap_transmission_bandwidth_t *a, const s
struct OCTET_STRING;
uint8_t *cp_octet_string(const struct OCTET_STRING *os, int *len);
F1AP_Cause_t encode_f1ap_cause(f1ap_Cause_t cause, long cause_value);
bool decode_f1ap_cause(F1AP_Cause_t f1_cause, f1ap_Cause_t *cause, long *cause_value);
#endif /* F1AP_LIB_COMMON_H_ */
......@@ -40,5 +40,6 @@
#include "F1AP_SuccessfulOutcome.h"
#include "F1AP_SibtypetobeupdatedListItem.h"
#include "F1AP_UnsuccessfulOutcome.h"
#include "F1AP_UE-associatedLogicalF1-ConnectionListRes.h"
#endif /* F1AP_LIB_INCLUDES_H_ */
......@@ -383,6 +383,114 @@ static void test_f1ap_setup_failure(void)
AssertFatal(ret, "eq_f1ap_setup_failure(): copied message doesn't match\n");
}
static void _test_f1ap_reset_msg(const f1ap_reset_t *orig)
{
F1AP_F1AP_PDU_t *f1enc = encode_f1ap_reset(orig);
F1AP_F1AP_PDU_t *f1dec = f1ap_encode_decode(f1enc);
f1ap_msg_free(f1enc);
f1ap_reset_t decoded = {0};
bool ret = decode_f1ap_reset(f1dec, &decoded);
AssertFatal(ret, "decode_f1ap_reset(): could not decode message\n");
f1ap_msg_free(f1dec);
ret = eq_f1ap_reset(orig, &decoded);
AssertFatal(ret, "eq_f1ap_reset(): decoded message doesn't match original\n");
free_f1ap_reset(&decoded);
f1ap_reset_t cp = cp_f1ap_reset(orig);
ret = eq_f1ap_reset(orig, &cp);
AssertFatal(ret, "eq_f1ap_reset(): copied message doesn't match original\n");
free_f1ap_reset(&cp);
printf("f1ap_reset successful\n");
}
/**
* @brief Test F1AP Reset message where the entire F1 interface is reset
*/
static void test_f1ap_reset_all(void)
{
f1ap_reset_t orig = {
.transaction_id = 2,
.cause = F1AP_CAUSE_TRANSPORT,
.cause_value = 3, /* no type -> whatever */
.reset_type = F1AP_RESET_ALL,
};
_test_f1ap_reset_msg(&orig);
free_f1ap_reset(&orig);
}
/**
* @brief Test F1AP Reset message where only some UEs are marked to reset
*/
static void test_f1ap_reset_part(void)
{
f1ap_reset_t orig = {
.transaction_id = 3,
.cause = F1AP_CAUSE_MISC,
.cause_value = 3, /* no type -> whatever */
.reset_type = F1AP_RESET_PART_OF_F1_INTERFACE,
.num_ue_to_reset = 4,
};
orig.ue_to_reset = calloc_or_fail(orig.num_ue_to_reset, sizeof(*orig.ue_to_reset));
orig.ue_to_reset[0].gNB_CU_ue_id = malloc_or_fail(sizeof(*orig.ue_to_reset[0].gNB_CU_ue_id));
*orig.ue_to_reset[0].gNB_CU_ue_id = 10;
orig.ue_to_reset[1].gNB_DU_ue_id = malloc_or_fail(sizeof(*orig.ue_to_reset[1].gNB_DU_ue_id));
*orig.ue_to_reset[1].gNB_DU_ue_id = 11;
orig.ue_to_reset[2].gNB_CU_ue_id = malloc_or_fail(sizeof(*orig.ue_to_reset[2].gNB_CU_ue_id));
*orig.ue_to_reset[2].gNB_CU_ue_id = 12;
orig.ue_to_reset[2].gNB_DU_ue_id = malloc_or_fail(sizeof(*orig.ue_to_reset[2].gNB_DU_ue_id));
*orig.ue_to_reset[2].gNB_DU_ue_id = 13;
// orig.ue_to_reset[3] intentionally empty (because it is allowed, both IDs are optional)
_test_f1ap_reset_msg(&orig);
free_f1ap_reset(&orig);
}
/**
* @brief Test F1 reset ack with differently ack'd UEs ("generic ack" is
* special case with no individual UE acked)
*/
static void test_f1ap_reset_ack(void)
{
f1ap_reset_ack_t orig = {
.transaction_id = 4,
.num_ue_to_reset = 4,
};
orig.ue_to_reset = calloc_or_fail(orig.num_ue_to_reset, sizeof(*orig.ue_to_reset));
orig.ue_to_reset[0].gNB_CU_ue_id = malloc_or_fail(sizeof(*orig.ue_to_reset[0].gNB_CU_ue_id));
*orig.ue_to_reset[0].gNB_CU_ue_id = 10;
orig.ue_to_reset[1].gNB_DU_ue_id = malloc_or_fail(sizeof(*orig.ue_to_reset[1].gNB_DU_ue_id));
*orig.ue_to_reset[1].gNB_DU_ue_id = 11;
orig.ue_to_reset[2].gNB_CU_ue_id = malloc_or_fail(sizeof(*orig.ue_to_reset[2].gNB_CU_ue_id));
*orig.ue_to_reset[2].gNB_CU_ue_id = 12;
orig.ue_to_reset[2].gNB_DU_ue_id = malloc_or_fail(sizeof(*orig.ue_to_reset[2].gNB_DU_ue_id));
*orig.ue_to_reset[2].gNB_DU_ue_id = 13;
// orig.ue_to_reset[3] intentionally empty (because it is allowed, both IDs are optional)
F1AP_F1AP_PDU_t *f1enc = encode_f1ap_reset_ack(&orig);
F1AP_F1AP_PDU_t *f1dec = f1ap_encode_decode(f1enc);
f1ap_msg_free(f1enc);
f1ap_reset_ack_t decoded = {0};
bool ret = decode_f1ap_reset_ack(f1dec, &decoded);
AssertFatal(ret, "decode_f1ap_reset_ack(): could not decode message\n");
f1ap_msg_free(f1dec);
ret = eq_f1ap_reset_ack(&orig, &decoded);
AssertFatal(ret, "eq_f1ap_reset_ack(): decoded message doesn't match original\n");
free_f1ap_reset_ack(&decoded);
f1ap_reset_ack_t cp = cp_f1ap_reset_ack(&orig);
ret = eq_f1ap_reset_ack(&orig, &cp);
AssertFatal(ret, "eq_f1ap_reset_ack(): copied message doesn't match original\n");
free_f1ap_reset_ack(&cp);
printf("f1ap_reset_ack successful\n");
free_f1ap_reset_ack(&orig);
}
/**
* @brief Test F1 gNB-DU Configuration Update
*/
......@@ -605,6 +713,9 @@ int main()
test_f1ap_setup_request();
test_f1ap_setup_response();
test_f1ap_setup_failure();
test_f1ap_reset_all();
test_f1ap_reset_part();
test_f1ap_reset_ack();
test_f1ap_du_configuration_update();
test_f1ap_cu_configuration_update();
test_f1ap_cu_configuration_update_acknowledge();
......
......@@ -125,12 +125,9 @@ void f1_reset_cu_initiated(const f1ap_reset_t *reset)
{
LOG_I(MAC, "F1 Reset initiated by CU\n");
f1ap_reset_ack_t ack = {0};
f1ap_reset_ack_t ack = {.transaction_id = reset->transaction_id};
if(reset->reset_type == F1AP_RESET_ALL) {
du_clear_all_ue_states();
ack = (f1ap_reset_ack_t) {
.transaction_id = reset->transaction_id
};
} else {
// reset->reset_type == F1AP_RESET_PART_OF_F1_INTERFACE
AssertFatal(1==0, "Not implemented yet\n");
......
......@@ -34,8 +34,10 @@ static void f1_reset_du_initiated_direct(const f1ap_reset_t *reset)
static void f1_reset_acknowledge_cu_initiated_direct(const f1ap_reset_ack_t *ack)
{
(void) ack;
AssertFatal(false, "%s() not implemented yet\n", __func__);
MessageDef *msg = itti_alloc_new_message(TASK_MAC_GNB, 0, F1AP_RESET_ACK);
msg->ittiMsgHeader.originInstance = -1; // means monolithic
F1AP_RESET_ACK(msg) = cp_f1ap_reset_ack(ack);
itti_send_msg_to_task(TASK_RRC_GNB, 0, msg);
}
static void f1_setup_request_direct(const f1ap_setup_req_t *req)
......
......@@ -61,8 +61,7 @@ static void f1_reset_du_initiated_f1ap(const f1ap_reset_t *reset)
static void f1_reset_acknowledge_cu_initiated_f1ap(const f1ap_reset_ack_t *ack)
{
MessageDef *msg = itti_alloc_new_message(TASK_MAC_GNB, 0, F1AP_RESET_ACK);
f1ap_reset_ack_t *f1ap_msg = &F1AP_RESET_ACK(msg);
*f1ap_msg = *ack;
F1AP_RESET_ACK(msg) = cp_f1ap_reset_ack(ack);
itti_send_msg_to_task(TASK_DU_F1, 0, msg);
}
......
......@@ -300,24 +300,28 @@ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req)
get_e1_if()->bearer_modif_response(&modif);
}
void e1_bearer_release_cmd(const e1ap_bearer_release_cmd_t *cmd)
static void remove_ue_e1(const uint32_t ue_id)
{
bool need_ue_id_mgmt = e1_used();
instance_t n3inst = get_n3_gtp_instance();
instance_t f1inst = get_f1_gtp_instance();
LOG_I(E1AP, "releasing UE %d\n", cmd->gNB_cu_up_ue_id);
newGtpuDeleteAllTunnels(n3inst, cmd->gNB_cu_up_ue_id);
newGtpuDeleteAllTunnels(n3inst, ue_id);
if (f1inst >= 0) // is there F1-U?
newGtpuDeleteAllTunnels(f1inst, cmd->gNB_cu_up_ue_id);
newGtpuDeleteAllTunnels(f1inst, ue_id);
if (need_ue_id_mgmt) {
// see issue #706: in monolithic, gNB will free PDCP of UE
nr_pdcp_remove_UE(cmd->gNB_cu_up_ue_id);
cu_remove_f1_ue_data(cmd->gNB_cu_up_ue_id);
nr_pdcp_remove_UE(ue_id);
cu_remove_f1_ue_data(ue_id);
}
nr_sdap_delete_ue_entities(cmd->gNB_cu_up_ue_id);
nr_sdap_delete_ue_entities(ue_id);
}
void e1_bearer_release_cmd(const e1ap_bearer_release_cmd_t *cmd)
{
LOG_I(E1AP, "releasing UE %d\n", cmd->gNB_cu_up_ue_id);
remove_ue_e1(cmd->gNB_cu_up_ue_id);
e1ap_bearer_release_cplt_t cplt = {
.gNB_cu_cp_ue_id = cmd->gNB_cu_cp_ue_id,
......@@ -326,3 +330,15 @@ void e1_bearer_release_cmd(const e1ap_bearer_release_cmd_t *cmd)
get_e1_if()->bearer_release_complete(&cplt);
}
void e1_reset(void)
{
/* we get the list of all UEs from the PDCP, which maintains a list */
ue_id_t ue_ids[MAX_MOBILES_PER_GNB];
int num = nr_pdcp_get_num_ues(ue_ids, MAX_MOBILES_PER_GNB);
for (uint32_t i = 0; i < num; ++i) {
ue_id_t ue_id = ue_ids[i];
LOG_W(E1AP, "releasing UE %ld\n", ue_id);
remove_ue_e1(ue_id);
}
}
......@@ -31,5 +31,6 @@ struct e1ap_bearer_release_cmd_s;
void e1_bearer_context_setup(const struct e1ap_bearer_setup_req_s *req);
void e1_bearer_context_modif(const struct e1ap_bearer_setup_req_s *req);
void e1_bearer_release_cmd(const struct e1ap_bearer_release_cmd_s *cmd);
void e1_reset(void);
#endif /* CUCP_CUUP_HANDLER_H */
......@@ -30,8 +30,8 @@
static void f1_reset_cu_initiated_direct(sctp_assoc_t assoc_id, const f1ap_reset_t *reset)
{
(void)reset;
AssertFatal(false, "%s() not implemented yet\n", __func__);
AssertFatal(assoc_id == -1, "illegal assoc_id %d\n", assoc_id);
f1_reset_cu_initiated(reset);
}
static void f1_reset_acknowledge_du_initiated_direct(sctp_assoc_t assoc_id, const f1ap_reset_ack_t *ack)
......
......@@ -35,8 +35,11 @@
static void f1_reset_cu_initiated_f1ap(sctp_assoc_t assoc_id, const f1ap_reset_t *reset)
{
(void)reset;
AssertFatal(false, "%s() not implemented yet\n", __func__);
MessageDef *msg = itti_alloc_new_message(TASK_RRC_GNB, 0, F1AP_RESET);
msg->ittiMsgHeader.originInstance = assoc_id;
f1ap_reset_t *f1ap_msg = &F1AP_RESET(msg);
*f1ap_msg = cp_f1ap_reset(reset);
itti_send_msg_to_task(TASK_CU_F1, 0, msg);
}
static void f1_reset_acknowledge_du_initiated_f1ap(sctp_assoc_t assoc_id, const f1ap_reset_ack_t *ack)
......
......@@ -2074,10 +2074,6 @@ void rrc_remove_ue(gNB_RRC_INST *rrc, rrc_gNB_ue_context_t *ue_context_p)
* are in E1, we also need to free the UE in the CU-CP, so call it twice to
* cover all cases */
nr_pdcp_remove_UE(UE->rrc_ue_id);
uint32_t pdu_sessions[256];
for (int i = 0; i < UE->nb_of_pdusessions && i < 256; ++i)
pdu_sessions[i] = UE->pduSession[i].param.pdusession_id;
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(0, UE->rrc_ue_id, UE->nb_of_pdusessions, pdu_sessions);
LOG_I(NR_RRC, "removed UE CU UE ID %u/RNTI %04x \n", UE->rrc_ue_id, UE->rnti);
rrc_delete_ue_data(UE);
rrc_gNB_remove_ue_context(rrc, ue_context_p);
......@@ -2099,6 +2095,9 @@ static void rrc_CU_process_ue_context_release_complete(MessageDef *msg_p)
/* only trigger release if it has been requested by core
* otherwise, it might be CU that requested release on a DU during normal
* operation (i.e, handover) */
uint32_t pdu_sessions[NGAP_MAX_PDU_SESSION];
get_pduSession_array(UE, pdu_sessions);
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(0, UE->rrc_ue_id, UE->nb_of_pdusessions, pdu_sessions);
rrc_remove_ue(RC.nrrrc[0], ue_context_p);
}
}
......@@ -2607,6 +2606,11 @@ void *rrc_gnb_task(void *args_p) {
LOG_E(NR_RRC, "Handling of F1AP_GNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE not implemented\n");
break;
case F1AP_RESET_ACK:
LOG_I(NR_RRC, "received F1AP reset acknowledgement\n");
free_f1ap_reset_ack(&F1AP_RESET_ACK(msg_p));
break;
/* Messages from X2AP */
case X2AP_ENDC_SGNB_ADDITION_REQ:
LOG_I(NR_RRC, "Received ENDC sgNB addition request from X2AP \n");
......
......@@ -1231,6 +1231,9 @@ int rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_COMMAND(MessageDef *msg_p, instance_
/* UE will be freed after UE context release complete */
} else {
// the DU is offline already
uint32_t pdu_sessions[NGAP_MAX_PDU_SESSION];
get_pduSession_array(UE, pdu_sessions);
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(0, UE->rrc_ue_id, UE->nb_of_pdusessions, pdu_sessions);
rrc_remove_ue(rrc, ue_context_p);
}
......
......@@ -24,13 +24,17 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "PHY/defs_common.h"
#include "common/utils/alg/find.h"
#include "common/utils/alg/foreach.h"
#include "RRC/NR/nr_rrc_proto.h"
#include "T.h"
#include "as_message.h"
#include "assertions.h"
#include "common/ran_context.h"
#include "common/utils/T/T.h"
#include "rrc_gNB_UE_context.h"
#include "rrc_gNB_du.h"
#include "e1ap_messages_types.h"
#include "intertask_interface.h"
#include "nr_rrc_defs.h"
......@@ -38,6 +42,7 @@
#include "rrc_messages_types.h"
#include "tree.h"
#include "e1ap_interface_management.h"
#include "rrc_gNB_NGAP.h"
static int cuup_compare(const nr_rrc_cuup_container_t *a, const nr_rrc_cuup_container_t *b)
{
......@@ -168,6 +173,69 @@ static void e1ap_setup_failure(sctp_assoc_t assoc_id, uint64_t transac_id, e1ap_
itti_send_msg_to_task(TASK_CUCP_E1, 0 /*unused by callee*/, msg_p);
}
static bool has_assoc_id(const void *vval, const void *vit)
{
sctp_assoc_t val = *(sctp_assoc_t *)vval;
sctp_assoc_t it = *(sctp_assoc_t *)vit;
return val == it;
}
/* @brief Remove RRC UE contexts of UE without E1 connection.
*
* 38.463 sec 8.2.3. says that "[E1 Setup] procedure also re-initialises the
* E1AP UE-related contexts (if any) and erases all related signalling
* connections in the two nodes like a Reset procedure would do.". If the CU-UP
* reconnects, it's previous UE contexts are here, but not associated. To
* fulfil the above, we simply free all UE contexts that don't have a CU-UP,
* and send a F1 Reset message to the corresponding DU(s), following the fact
* that (38.473 sec 8.2.1.2.1) "a failure at the gNB-CU [...] has resulted in
* the loss of some or all transaction reference information[...] a RESET
* message shall be sent to the gNB-DU.". This might be overarching in that UEs
* that are in the process of connecting or that are on another DU might be
* affected by that reset procedure as well; we suppose they will connect later
* again. */
static void remove_unassociated_e1_connections(gNB_RRC_INST *rrc, sctp_assoc_t e1_assoc_id)
{
seq_arr_t affected_du;
seq_arr_init(&affected_du, sizeof(sctp_assoc_t));
seq_arr_t ue_context_to_remove;
seq_arr_init(&ue_context_to_remove, sizeof(rrc_gNB_ue_context_t *));
rrc_gNB_ue_context_t *ue_context_p = NULL;
RB_FOREACH(ue_context_p, rrc_nr_ue_tree_s, &rrc->rrc_ue_head) {
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
uint32_t ue_id = UE->rrc_ue_id;
if (ue_associated_to_cuup(rrc, UE))
continue;
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_id);
elm_arr_t ret = find_if(&affected_du, &ue_data.du_assoc_id, has_assoc_id);
if (!ret.found)
seq_arr_push_back(&affected_du, &ue_data.du_assoc_id, sizeof(ue_data.du_assoc_id));
/* remove UE context: we store the pointer, so pass pointer to ue_context_p */
seq_arr_push_back(&ue_context_to_remove, &ue_context_p, sizeof(ue_context_p));
LOG_I(NR_RRC, "remove E1-unassociated UE context ID %d\n", ue_id);
}
for (int i = 0; i < seq_arr_size(&ue_context_to_remove); ++i) {
/* we retrieve a pointer (=iterator) to the UE context pointer
* (ue_context_p), so dereference once */
rrc_gNB_ue_context_t *p = *(rrc_gNB_ue_context_t **)seq_arr_at(&ue_context_to_remove, i);
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_RADIO_CONNECTION_WITH_UE_LOST};
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_REQ(0, p, cause);
rrc_remove_ue(rrc, p);
}
seq_arr_free(&ue_context_to_remove, NULL);
for (int i = 0; i < seq_arr_size(&affected_du); ++i) {
void *it = seq_arr_at(&affected_du, i);
sctp_assoc_t du_assoc_id = *(sctp_assoc_t *)it;
LOG_W(NR_RRC, "trigger F1 reset on DU %d due to E1 connection loss\n", du_assoc_id);
trigger_f1_reset(rrc, du_assoc_id);
}
seq_arr_free(&affected_du, NULL);
}
/**
* @brief E1AP Setup Request processing on CU-CP
*/
......@@ -216,6 +284,8 @@ int rrc_gNB_process_e1_setup_req(sctp_assoc_t assoc_id, const e1ap_setup_req_t *
RB_INSERT(rrc_cuup_tree, &rrc->cuups, cuup);
rrc->num_cuups++;
remove_unassociated_e1_connections(rrc, assoc_id);
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, E1AP_SETUP_RESP);
msg_p->ittiMsgHeader.originInstance = assoc_id;
e1ap_setup_resp_t *resp = &E1AP_SETUP_RESP(msg_p);
......@@ -225,6 +295,24 @@ int rrc_gNB_process_e1_setup_req(sctp_assoc_t assoc_id, const e1ap_setup_req_t *
return 0;
}
/* @brief invalidate (remove) all E1 assoc ID from UE contexts belonging to this CU-UP */
static void invalidate_cuup_connections(gNB_RRC_INST *rrc, sctp_assoc_t e1_assoc_id)
{
rrc_gNB_ue_context_t *ue_context_p = NULL;
RB_FOREACH(ue_context_p, rrc_nr_ue_tree_s, &rrc->rrc_ue_head) {
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
uint32_t ue_id = UE->rrc_ue_id;
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_id);
if (ue_data.e1_assoc_id != e1_assoc_id)
continue; /* UE is on another CU-UP */
ue_data.e1_assoc_id = 0;
bool success = cu_update_f1_ue_data(ue_id, &ue_data);
DevAssert(success);
LOG_W(NR_RRC, "CU-UP %d of UE %d is gone\n", e1_assoc_id, ue_id);
}
}
/**
* @brief RRC Processing of the indication of E1 connection loss on CU-CP
*/
......@@ -232,6 +320,7 @@ void rrc_gNB_process_e1_lost_connection(gNB_RRC_INST *rrc, e1ap_lost_connection_
{
LOG_I(NR_RRC, "Received E1 connection loss indication on RRC\n");
AssertFatal(assoc_id != 0, "illegal assoc_id == 0: should be -1 (monolithic) or >0 (split)\n");
invalidate_cuup_connections(rrc, assoc_id);
nr_rrc_cuup_container_t e = {.assoc_id = assoc_id};
nr_rrc_cuup_container_t *cuup = RB_FIND(rrc_cuup_tree, &rrc->cuups, &e);
if (cuup == NULL) {
......@@ -241,6 +330,7 @@ void rrc_gNB_process_e1_lost_connection(gNB_RRC_INST *rrc, e1ap_lost_connection_
if (cuup->setup_req != NULL) {
e1ap_setup_req_t *req = cuup->setup_req;
LOG_I(NR_RRC, "releasing CU-UP %s on assoc_id %d\n", req->gNB_cu_up_name, assoc_id);
free_e1ap_cuup_setup_request(cuup->setup_req);
free(cuup->setup_req);
}
nr_rrc_cuup_container_t *removed = RB_REMOVE(rrc_cuup_tree, &rrc->cuups, cuup);
......
......@@ -644,3 +644,14 @@ nr_rrc_du_container_t *find_target_du(gNB_RRC_INST *rrc, sctp_assoc_t source_ass
}
return target_du;
}
void trigger_f1_reset(gNB_RRC_INST *rrc, sctp_assoc_t du_assoc_id)
{
f1ap_reset_t reset = {
.transaction_id = F1AP_get_next_transaction_identifier(0, 0),
.cause = F1AP_CAUSE_TRANSPORT,
.cause_value = 1, // F1AP_CauseTransport_transport_resource_unavailable
.reset_type = F1AP_RESET_ALL, // DU does not support partial reset yet
};
rrc->mac_rrc.f1_reset(du_assoc_id, &reset);
}
......@@ -60,4 +60,6 @@ struct nr_rrc_du_container_t *find_target_du(struct gNB_RRC_INST_s *rrc, sctp_as
} \
}
void trigger_f1_reset(struct gNB_RRC_INST_s *rrc, sctp_assoc_t du_assoc_id);
#endif /* RRC_GNB_DU_H_ */
......@@ -58,6 +58,12 @@ rrc_pdu_session_param_t *find_pduSession_from_drbId(gNB_RRC_UE_t *ue, int drb_id
return find_pduSession(ue, id, false);
}
void get_pduSession_array(gNB_RRC_UE_t *ue, uint32_t pdu_sessions[NGAP_MAX_PDU_SESSION])
{
for (int i = 0; i < ue->nb_of_pdusessions && i < NGAP_MAX_PDU_SESSION; ++i)
pdu_sessions[i] = ue->pduSession[i].param.pdusession_id;
}
drb_t *get_drb(gNB_RRC_UE_t *ue, uint8_t drb_id)
{
DevAssert(drb_id > 0 && drb_id <= 32);
......
......@@ -73,6 +73,9 @@ rrc_pdu_session_param_t *find_pduSession(gNB_RRC_UE_t *ue, int id, bool create);
/// @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 get the PDU sessions of this UE in a single array
void get_pduSession_array(gNB_RRC_UE_t *ue, uint32_t pdu_sessions[NGAP_MAX_PDU_SESSION]);
/// @brief set PDCP configuration in a bearer context management message
void set_bearer_context_pdcp_config(bearer_context_pdcp_config_t *pdcp_config, drb_t *rrc_drb, bool um_on_default_drb);
......
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