From f5c90ead7e37fb618ae2624e41a534921ea0e0d7 Mon Sep 17 00:00:00 2001
From: Robert Schmidt <robert.schmidt@openairinterface.org>
Date: Mon, 21 Aug 2023 17:24:47 +0200
Subject: [PATCH] Free DU at CU on lost connection, store assoc ID

- store the assoc ID of a UE at the RRC
- if F1 receives an SCTP shutdown, it removes the endpoint
- F1 will also send an "F1 Lost Connection" message to the RRC
- it is not yet possible to reconnect a DU after it had already been
  connected at the CU, since the CU F1AP task will try to rebind GTP,
  which fails
---
 openair2/COMMON/f1ap_messages_def.h          |  3 +++
 openair2/COMMON/f1ap_messages_types.h        |  6 +++++
 openair2/F1AP/f1ap_common.c                  | 10 ++++++++
 openair2/F1AP/f1ap_common.h                  |  1 +
 openair2/F1AP/f1ap_cu_interface_management.c |  1 +
 openair2/F1AP/f1ap_cu_task.c                 | 12 +++++++++
 openair2/RRC/NR/nr_rrc_defs.h                |  1 +
 openair2/RRC/NR/rrc_gNB.c                    | 26 +++++++++++++++++---
 8 files changed, 57 insertions(+), 3 deletions(-)

diff --git a/openair2/COMMON/f1ap_messages_def.h b/openair2/COMMON/f1ap_messages_def.h
index 9cb63a0a06b..0689dd52ac3 100644
--- a/openair2/COMMON/f1ap_messages_def.h
+++ b/openair2/COMMON/f1ap_messages_def.h
@@ -32,6 +32,9 @@ MESSAGE_DEF(F1AP_SETUP_RESP         , MESSAGE_PRIORITY_MED, f1ap_setup_resp_t
 MESSAGE_DEF(F1AP_SETUP_FAILURE         , MESSAGE_PRIORITY_MED, f1ap_setup_failure_t          , f1ap_setup_failure)
 MESSAGE_DEF(F1AP_GNB_CU_CONFIGURATION_UPDATE         , MESSAGE_PRIORITY_MED, f1ap_gnb_cu_configuration_update_t          , f1ap_gnb_cu_configuration_update)
 
+/* F1AP -> RRC to inform about lost connection */
+MESSAGE_DEF(F1AP_LOST_CONNECTION, MESSAGE_PRIORITY_MED, f1ap_lost_connection_t, f1ap_lost_connection)
+
 /* MAC -> F1AP messages */
 MESSAGE_DEF(F1AP_INITIAL_UL_RRC_MESSAGE           , MESSAGE_PRIORITY_MED, f1ap_initial_ul_rrc_message_t             , f1ap_initial_ul_rrc_message)
 MESSAGE_DEF(F1AP_UL_RRC_MESSAGE                , MESSAGE_PRIORITY_MED, f1ap_ul_rrc_message_t                , f1ap_ul_rrc_message)
diff --git a/openair2/COMMON/f1ap_messages_types.h b/openair2/COMMON/f1ap_messages_types.h
index b3875c01bff..b0ec1e1e356 100644
--- a/openair2/COMMON/f1ap_messages_types.h
+++ b/openair2/COMMON/f1ap_messages_types.h
@@ -39,6 +39,8 @@
 #define F1AP_GNB_CU_CONFIGURATION_UPDATE_FAILURE(mSGpTR)   (mSGpTR)->ittiMsg.f1ap_gnb_cu_configuration_update_failure
 #define F1AP_SETUP_FAILURE(mSGpTR)                 (mSGpTR)->ittiMsg.f1ap_setup_failure
 
+#define F1AP_LOST_CONNECTION(mSGpTR)   (mSGpTR)->ittiMsg.f1ap_lost_connection
+
 #define F1AP_INITIAL_UL_RRC_MESSAGE(mSGpTR)        (mSGpTR)->ittiMsg.f1ap_initial_ul_rrc_message
 #define F1AP_UL_RRC_MESSAGE(mSGpTR)                (mSGpTR)->ittiMsg.f1ap_ul_rrc_message
 #define F1AP_UE_CONTEXT_SETUP_REQ(mSGpTR)          (mSGpTR)->ittiMsg.f1ap_ue_context_setup_req
@@ -405,4 +407,8 @@ typedef struct f1ap_paging_ind_s {
   uint8_t  paging_drx;
 } f1ap_paging_ind_t;
 
+typedef struct f1ap_lost_connection_t {
+  int dummy;
+} f1ap_lost_connection_t;
+
 #endif /* F1AP_MESSAGES_TYPES_H_ */
diff --git a/openair2/F1AP/f1ap_common.c b/openair2/F1AP/f1ap_common.c
index 91e7d85efed..3491adc48a0 100644
--- a/openair2/F1AP/f1ap_common.c
+++ b/openair2/F1AP/f1ap_common.c
@@ -63,3 +63,13 @@ void createF1inst(instance_t instanceP, f1ap_setup_req_t *req, f1ap_net_config_t
     f1_inst[0]->net_config = *nc;
   pthread_mutex_unlock(&f1_inst_mtx);
 }
+
+void destroyF1inst(instance_t instance)
+{
+  DevAssert(instance == 0);
+  pthread_mutex_lock(&f1_inst_mtx);
+  AssertFatal(f1_inst[0] != NULL, "Attempted to free uninitialized F1 instances\n");
+  free(f1_inst[instance]);
+  f1_inst[instance] = NULL;
+  pthread_mutex_unlock(&f1_inst_mtx);
+}
diff --git a/openair2/F1AP/f1ap_common.h b/openair2/F1AP/f1ap_common.h
index 1329ebdfce0..3e0cbb4c8a4 100644
--- a/openair2/F1AP/f1ap_common.h
+++ b/openair2/F1AP/f1ap_common.h
@@ -418,6 +418,7 @@ uint8_t F1AP_get_next_transaction_identifier(instance_t mod_idP, instance_t cu_m
 f1ap_cudu_inst_t *getCxt(instance_t instanceP);
 
 void createF1inst(instance_t instanceP, f1ap_setup_req_t *req, f1ap_net_config_t *nc);
+void destroyF1inst(instance_t instance);
 
 //lts: C struct type is not homogeneous, so we need macros instead of functions
 #define addnRCGI(nRCGi, servedCelL) \
diff --git a/openair2/F1AP/f1ap_cu_interface_management.c b/openair2/F1AP/f1ap_cu_interface_management.c
index ad3880c71ba..3ee88c33b50 100644
--- a/openair2/F1AP/f1ap_cu_interface_management.c
+++ b/openair2/F1AP/f1ap_cu_interface_management.c
@@ -94,6 +94,7 @@ int CU_handle_F1_SETUP_REQUEST(instance_t instance,
   }
 
   MessageDef *message_p = itti_alloc_new_message(TASK_CU_F1, 0, F1AP_SETUP_REQ);
+  message_p->ittiMsgHeader.originInstance = assoc_id;
   f1ap_setup_req_t *req = &F1AP_SETUP_REQ(message_p);
   /* gNB_DU_id */
   // this function exits if the ie is mandatory
diff --git a/openair2/F1AP/f1ap_cu_task.c b/openair2/F1AP/f1ap_cu_task.c
index 330d4f5422a..2d47398d51b 100644
--- a/openair2/F1AP/f1ap_cu_task.c
+++ b/openair2/F1AP/f1ap_cu_task.c
@@ -71,6 +71,18 @@ static void cu_task_handle_sctp_association_ind(instance_t instance,
 static void cu_task_handle_sctp_association_resp(instance_t instance, sctp_new_association_resp_t *sctp_new_association_resp) {
   DevAssert(sctp_new_association_resp != NULL);
 
+  if (sctp_new_association_resp->sctp_state == SCTP_STATE_SHUTDOWN) {
+    f1ap_cudu_inst_t *f1ap_cu_data = getCxt(instance);
+    AssertFatal(f1ap_cu_data != NULL, "illegal state: SCTP shutdown for non-existing F1AP endpoint\n");
+    LOG_I(F1AP, "Received SCTP shutdown for assoc_id %d, removing endpoint\n", sctp_new_association_resp->assoc_id);
+    destroyF1inst(instance);
+    /* inform RRC that the DU is gone */
+    MessageDef *message_p = itti_alloc_new_message(TASK_CU_F1, 0, F1AP_LOST_CONNECTION);
+    message_p->ittiMsgHeader.originInstance = sctp_new_association_resp->assoc_id;
+    itti_send_msg_to_task(TASK_RRC_GNB, instance, message_p);
+    return;
+  }
+
   if (sctp_new_association_resp->sctp_state != SCTP_STATE_ESTABLISHED) {
     LOG_W(F1AP, "Received unsuccessful result for SCTP association (%u), instance %ld, cnx_id %u\n",
           sctp_new_association_resp->sctp_state,
diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h
index 465f968a4d1..362d2bcee5f 100644
--- a/openair2/RRC/NR/nr_rrc_defs.h
+++ b/openair2/RRC/NR/nr_rrc_defs.h
@@ -369,6 +369,7 @@ typedef struct cucp_cuup_if_s {
 } cucp_cuup_if_t;
 
 typedef struct nr_rrc_du_container_t {
+  int assoc_id;
   f1ap_setup_req_t *setup_req;
   NR_MIB_t *mib;
   NR_SIB1_t *sib1;
diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c
index 505b22ab501..23fcbffe503 100644
--- a/openair2/RRC/NR/rrc_gNB.c
+++ b/openair2/RRC/NR/rrc_gNB.c
@@ -1801,12 +1801,12 @@ static bool rrc_gNB_plmn_matches(const gNB_RRC_INST *rrc, const f1ap_served_cell
     && rrc->nr_cellid == info->nr_cellid;
 }
 
-static void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req)
+static void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, int assoc_id)
 {
   gNB_RRC_INST *rrc = RC.nrrrc[0];
   DevAssert(rrc);
 
-  LOG_I(NR_RRC, "Received F1 Setup Request from gNB_DU %lu (%s)\n", req->gNB_DU_id, req->gNB_DU_name);
+  LOG_I(NR_RRC, "Received F1 Setup Request from gNB_DU %lu (%s) on assoc_id %d\n", req->gNB_DU_id, req->gNB_DU_name, assoc_id);
 
   // check:
   // - it is the first DU
@@ -1876,6 +1876,7 @@ static void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req)
   // we accept the DU
   rrc->du = calloc(1, sizeof(*rrc->du));
   AssertFatal(rrc->du != NULL, "out of memory\n");
+  rrc->du->assoc_id = assoc_id;
 
   /* ITTI will free the setup request message via free(). So the memory
    * "inside" of the message will remain, but the "outside" container no, so
@@ -2423,6 +2424,21 @@ void rrc_gNB_process_e1_bearer_context_setup_resp(e1ap_bearer_setup_resp_t *resp
   prepare_and_send_ue_context_modification_f1(ue_context_p, resp);
 }
 
+static void rrc_CU_process_f1_lost_connection(gNB_RRC_INST *rrc, f1ap_lost_connection_t *lc, int assoc_id)
+{
+  AssertFatal(rrc->du != NULL, "no DU connected, cannot received F1 lost connection\n");
+  AssertFatal(rrc->du->assoc_id == assoc_id,
+              "previously connected DU (%d) does not match DU for which connection has been lost (%d)\n",
+              rrc->du->assoc_id,
+              assoc_id);
+  (void) lc; // unused for the moment
+  ASN_STRUCT_FREE(asn_DEF_NR_MIB, rrc->du->mib);
+  ASN_STRUCT_FREE(asn_DEF_NR_SIB1, rrc->du->sib1);
+  free(rrc->du);
+  rrc->du = NULL;
+  LOG_I(RRC, "dropping DU with assoc_id %d (UE connections remain, if any)\n", assoc_id);
+}
+
 static void print_rrc_meas(FILE *f, const NR_MeasResults_t *measresults)
 {
   DevAssert(measresults->measResultServingMOList.list.count >= 1);
@@ -2621,7 +2637,7 @@ void *rrc_gnb_task(void *args_p) {
       /* Messages from F1AP task */
       case F1AP_SETUP_REQ:
         AssertFatal(!NODE_IS_DU(RC.nrrrc[instance]->node_type), "should not receive F1AP_SETUP_REQUEST in DU!\n");
-        rrc_gNB_process_f1_setup_req(&F1AP_SETUP_REQ(msg_p));
+        rrc_gNB_process_f1_setup_req(&F1AP_SETUP_REQ(msg_p), msg_p->ittiMsgHeader.originInstance);
         break;
 
       case F1AP_UE_CONTEXT_SETUP_RESP:
@@ -2644,6 +2660,10 @@ void *rrc_gnb_task(void *args_p) {
         rrc_CU_process_ue_context_release_complete(msg_p);
         break;
 
+      case F1AP_LOST_CONNECTION:
+        rrc_CU_process_f1_lost_connection(RC.nrrrc[0], &F1AP_LOST_CONNECTION(msg_p), msg_p->ittiMsgHeader.originInstance);
+        break;
+
       /* Messages from X2AP */
       case X2AP_ENDC_SGNB_ADDITION_REQ:
         LOG_I(NR_RRC, "Received ENDC sgNB addition request from X2AP \n");
-- 
GitLab