From 52a52eefb9eafa51dcc3d9af020974304897921c Mon Sep 17 00:00:00 2001
From: Konstantinos Alexandris <alexandr@eurecom.fr>
Date: Fri, 8 Feb 2019 17:25:24 +0100
Subject: [PATCH] X2AP UE Context Release (generate/handle message), Release
 eNB resources

---
 openair2/COMMON/x2ap_messages_def.h        |  3 ++
 openair2/COMMON/x2ap_messages_types.h      |  9 ++++
 openair2/RRC/LTE/rrc_defs.h                |  3 +-
 openair2/RRC/LTE/rrc_eNB.c                 | 63 +++++++++++++++++++++-
 openair2/RRC/LTE/rrc_eNB_S1AP.c            | 55 ++++++++++++++++++-
 openair2/RRC/LTE/rrc_eNB_S1AP.h            |  4 ++
 openair2/X2AP/x2ap_eNB.c                   | 27 ++++++++++
 openair2/X2AP/x2ap_eNB_decoder.c           |  4 ++
 openair2/X2AP/x2ap_eNB_generate_messages.c | 12 ++---
 openair2/X2AP/x2ap_eNB_generate_messages.h |  3 +-
 openair2/X2AP/x2ap_eNB_handler.c           | 52 +++++++++++++++++-
 11 files changed, 223 insertions(+), 12 deletions(-)

diff --git a/openair2/COMMON/x2ap_messages_def.h b/openair2/COMMON/x2ap_messages_def.h
index d5555c1839..65fa9c801d 100644
--- a/openair2/COMMON/x2ap_messages_def.h
+++ b/openair2/COMMON/x2ap_messages_def.h
@@ -40,3 +40,6 @@ MESSAGE_DEF(X2AP_DEREGISTERED_ENB_IND           , MESSAGE_PRIORITY_MED, x2ap_der
 /* handover messages X2AP <-> RRC */
 MESSAGE_DEF(X2AP_HANDOVER_REQ                   , MESSAGE_PRIORITY_MED, x2ap_handover_req_t              , x2ap_handover_req)
 MESSAGE_DEF(X2AP_HANDOVER_REQ_ACK               , MESSAGE_PRIORITY_MED, x2ap_handover_req_ack_t          , x2ap_handover_req_ack)
+
+/* handover messages X2AP <-> S1AP */
+MESSAGE_DEF(X2AP_UE_CONTEXT_RELEASE             , MESSAGE_PRIORITY_MED, x2ap_ue_context_release_t        , x2ap_ue_context_release)
diff --git a/openair2/COMMON/x2ap_messages_types.h b/openair2/COMMON/x2ap_messages_types.h
index 7cdd0ff0ff..d43057dbdf 100644
--- a/openair2/COMMON/x2ap_messages_types.h
+++ b/openair2/COMMON/x2ap_messages_types.h
@@ -33,11 +33,20 @@
 #define X2AP_HANDOVER_REQ_ACK(mSGpTR)           (mSGpTR)->ittiMsg.x2ap_handover_req_ack
 #define X2AP_REGISTER_ENB_CNF(mSGpTR)           (mSGpTR)->ittiMsg.x2ap_register_enb_cnf
 #define X2AP_DEREGISTERED_ENB_IND(mSGpTR)       (mSGpTR)->ittiMsg.x2ap_deregistered_enb_ind
+#define X2AP_UE_CONTEXT_RELEASE(mSGpTR)         (mSGpTR)->ittiMsg.x2ap_ue_context_release
 
 
 #define X2AP_MAX_NB_ENB_IP_ADDRESS 2
 
 // eNB application layer -> X2AP messages
+
+/* X2AP UE CONTEXT RELEASE */
+typedef struct x2ap_ue_context_release_s {
+  int old_eNB_ue_x2ap_id;
+  int new_eNB_ue_x2ap_id;
+  int target_mod_id;
+} x2ap_ue_context_release_t;
+
 typedef struct x2ap_register_enb_req_s {
   /* Unique eNB_id to identify the eNB within EPC.
    * For macro eNB ids this field should be 20 bits long.
diff --git a/openair2/RRC/LTE/rrc_defs.h b/openair2/RRC/LTE/rrc_defs.h
index 38fc7b7262..d5faf89bc7 100644
--- a/openair2/RRC/LTE/rrc_defs.h
+++ b/openair2/RRC/LTE/rrc_defs.h
@@ -343,7 +343,8 @@ typedef enum HO_STATE_e {
   HO_COMPLETE, // initiated by the target eNB
   HO_REQUEST,
   HO_ACK,
-  HO_CONFIGURED
+  HO_CONFIGURED,
+  HO_RELEASE
 } HO_STATE_t;
 
 typedef enum SL_TRIGGER_e {
diff --git a/openair2/RRC/LTE/rrc_eNB.c b/openair2/RRC/LTE/rrc_eNB.c
index 4754abfe53..d5631ef155 100644
--- a/openair2/RRC/LTE/rrc_eNB.c
+++ b/openair2/RRC/LTE/rrc_eNB.c
@@ -4223,6 +4223,42 @@ void rrc_eNB_process_handoverCommand(
   ue_context->ue_context.handover_info->size = size;
 }
 
+void rrc_eNB_handover_ue_context_release(
+protocol_ctxt_t *const ctxt_pP,
+struct rrc_eNB_ue_context_s *ue_context_p) {
+  int e_rab = 0;
+  //MessageDef *msg_release_p = NULL;
+  MessageDef *msg_delete_tunnels_p = NULL;
+  uint32_t eNB_ue_s1ap_id = ue_context_p->ue_context.eNB_ue_s1ap_id;
+
+  //msg_release_p = itti_alloc_new_message(TASK_RRC_ENB, S1AP_UE_CONTEXT_RELEASE);
+  //itti_send_msg_to_task(TASK_S1AP, ctxt_pP->module_id, msg_release_p);
+  s1ap_ue_context_release(ctxt_pP->instance, ue_context_p->ue_context.eNB_ue_s1ap_id);
+
+  //MSC_LOG_TX_MESSAGE(MSC_RRC_ENB, MSC_GTPU_ENB, NULL,0, "0 GTPV1U_ENB_DELETE_TUNNEL_REQ rnti %x ", eNB_ue_s1ap_id);
+  msg_delete_tunnels_p = itti_alloc_new_message(TASK_RRC_ENB, GTPV1U_ENB_DELETE_TUNNEL_REQ);
+  memset(&GTPV1U_ENB_DELETE_TUNNEL_REQ(msg_delete_tunnels_p), 0, sizeof(GTPV1U_ENB_DELETE_TUNNEL_REQ(msg_delete_tunnels_p)));
+
+  GTPV1U_ENB_DELETE_TUNNEL_REQ(msg_delete_tunnels_p).rnti = ue_context_p->ue_context.rnti;
+
+  for (e_rab = 0; e_rab < ue_context_p->ue_context.nb_of_e_rabs; e_rab++) {
+    GTPV1U_ENB_DELETE_TUNNEL_REQ(msg_delete_tunnels_p).eps_bearer_id[GTPV1U_ENB_DELETE_TUNNEL_REQ(msg_delete_tunnels_p).num_erab++] =
+      ue_context_p->ue_context.enb_gtp_ebi[e_rab];
+    // erase data
+    ue_context_p->ue_context.enb_gtp_teid[e_rab] = 0;
+    memset(&ue_context_p->ue_context.enb_gtp_addrs[e_rab], 0, sizeof(ue_context_p->ue_context.enb_gtp_addrs[e_rab]));
+    ue_context_p->ue_context.enb_gtp_ebi[e_rab] = 0;
+  }
+
+  itti_send_msg_to_task(TASK_GTPV1_U, ctxt_pP->module_id, msg_delete_tunnels_p);
+  struct rrc_ue_s1ap_ids_s *rrc_ue_s1ap_ids = NULL;
+  rrc_ue_s1ap_ids = rrc_eNB_S1AP_get_ue_ids(RC.rrc[ctxt_pP->module_id], 0, eNB_ue_s1ap_id);
+
+  if (rrc_ue_s1ap_ids != NULL) {
+    rrc_eNB_S1AP_remove_ue_ids(RC.rrc[ctxt_pP->module_id], rrc_ue_s1ap_ids);
+  }
+}
+
 #if 0
 //-----------------------------------------------------------------------------
 void
@@ -7990,6 +8026,14 @@ void rrc_subframe_process(protocol_ctxt_t *const ctxt_pP, const int CC_id)
       }
     }
 
+    if (ue_context_p->ue_context.handover_info != NULL) {
+      if (ue_context_p->ue_context.handover_info->state == HO_RELEASE) {
+        ue_to_be_removed = ue_context_p;
+        rrc_eNB_handover_ue_context_release(ctxt_pP, ue_context_p);
+        break; //break RB_FOREACH (why to break ?)
+      }
+    }
+
     pthread_mutex_lock(&rrc_release_freelist);
 
     if (rrc_release_info.num_UEs > 0) {
@@ -8161,6 +8205,9 @@ void *rrc_enb_process_itti_msg(void *notUsed) {
   SRB_INFO                           *srb_info_p;
   int                                 CC_id;
   protocol_ctxt_t                     ctxt;
+
+//  memset(&ctxt, 0, sizeof(ctxt));
+
   // Wait for a message
   itti_receive_msg(TASK_RRC_ENB, &msg_p);
   msg_name_p = ITTI_MSG_NAME(msg_p);
@@ -8278,7 +8325,8 @@ void *rrc_enb_process_itti_msg(void *notUsed) {
       //LOG_I(RRC, "[eNB %d] Received message %s, not processed because procedure not synched\n",
       //instance, msg_name_p);
       if (rrc_eNB_get_ue_context(RC.rrc[instance], GTPV1U_ENB_DELETE_TUNNEL_RESP(msg_p).rnti)
-          && rrc_eNB_get_ue_context(RC.rrc[instance], GTPV1U_ENB_DELETE_TUNNEL_RESP(msg_p).rnti)->ue_context.ue_release_timer_rrc > 0) {
+          && rrc_eNB_get_ue_context(RC.rrc[instance], GTPV1U_ENB_DELETE_TUNNEL_RESP(msg_p).rnti)->ue_context.ue_release_timer_rrc > 0
+          && rrc_eNB_get_ue_context(RC.rrc[instance], GTPV1U_ENB_DELETE_TUNNEL_RESP(msg_p).rnti)->ue_context.handover_info->state != HO_RELEASE) {
         rrc_eNB_get_ue_context(RC.rrc[instance], GTPV1U_ENB_DELETE_TUNNEL_RESP(msg_p).rnti)->ue_context.ue_release_timer_rrc =
           rrc_eNB_get_ue_context(RC.rrc[instance], GTPV1U_ENB_DELETE_TUNNEL_RESP(msg_p).rnti)->ue_context.ue_release_timer_thres_rrc;
       }
@@ -8310,6 +8358,19 @@ void *rrc_enb_process_itti_msg(void *notUsed) {
       break;
     }
 
+   case X2AP_UE_CONTEXT_RELEASE: {
+      struct rrc_eNB_ue_context_s        *ue_context_p = NULL;
+      ue_context_p = rrc_eNB_get_ue_context(RC.rrc[instance], ctxt.rnti);
+      LOG_I(RRC, "[eNB %d] source eNB receives the X2 UE CONTEXT RELEASE %s at frame %d subframe %d \n", instance, msg_name_p,
+            ctxt.frame,ctxt.subframe);
+      DevAssert(ue_context_p != NULL);
+
+      if (ue_context_p->ue_context.handover_info->state != HO_COMPLETE) abort();
+
+      ue_context_p->ue_context.handover_info->state = HO_RELEASE;
+      break;
+    }
+
     /* Messages from eNB app */
     case RRC_CONFIGURATION_REQ:
       LOG_I(RRC, "[eNB %d] Received %s : %p\n", instance, msg_name_p,&RRC_CONFIGURATION_REQ(msg_p));
diff --git a/openair2/RRC/LTE/rrc_eNB_S1AP.c b/openair2/RRC/LTE/rrc_eNB_S1AP.c
index d80c45d699..ce5bdc7d54 100644
--- a/openair2/RRC/LTE/rrc_eNB_S1AP.c
+++ b/openair2/RRC/LTE/rrc_eNB_S1AP.c
@@ -2087,9 +2087,60 @@ int rrc_eNB_process_S1AP_PATH_SWITCH_REQ_ACK (MessageDef *msg_p, const char *msg
     /* Security key */
     ue_context_p->ue_context.next_hop_chain_count=S1AP_PATH_SWITCH_REQ_ACK (msg_p).next_hop_chain_count;
     memcpy ( ue_context_p->ue_context.next_security_key,
-             S1AP_PATH_SWITCH_REQ_ACK (msg_p).next_security_key,
-             SECURITY_KEY_LENGTH);
+	     S1AP_PATH_SWITCH_REQ_ACK (msg_p).next_security_key,
+	     SECURITY_KEY_LENGTH);
+
+    rrc_eNB_send_X2AP_UE_CONTEXT_RELEASE(&ctxt, ue_context_p);
+
     return (0);
   }
 }
 
+int rrc_eNB_send_X2AP_UE_CONTEXT_RELEASE(const protocol_ctxt_t* const ctxt_pP, rrc_eNB_ue_context_t* const ue_context_pP) {
+
+  MessageDef      *msg_p         = NULL;
+
+  msg_p = itti_alloc_new_message (TASK_RRC_ENB, X2AP_UE_CONTEXT_RELEASE);
+
+  X2AP_UE_CONTEXT_RELEASE (msg_p).old_eNB_ue_x2ap_id = 0;
+  X2AP_UE_CONTEXT_RELEASE (msg_p).new_eNB_ue_x2ap_id = 0;
+  X2AP_UE_CONTEXT_RELEASE (msg_p).target_mod_id = ue_context_pP->ue_context.handover_info->modid_t;
+  itti_send_msg_to_task (TASK_X2AP, ctxt_pP->instance, msg_p);
+  return (0);
+}
+
+int s1ap_ue_context_release(instance_t instance, const uint32_t eNB_ue_s1ap_id){
+  s1ap_eNB_instance_t *s1ap_eNB_instance_p = NULL;
+  struct s1ap_eNB_ue_context_s *ue_context_p = NULL;
+
+  s1ap_eNB_instance_p = s1ap_eNB_get_instance(instance);
+  DevAssert(s1ap_eNB_instance_p != NULL);
+
+  if ((ue_context_p = s1ap_eNB_get_ue_context(s1ap_eNB_instance_p,
+                      eNB_ue_s1ap_id)) == NULL) {
+    /* The context for this eNB ue s1ap id doesn't exist in the map of eNB UEs */
+    LOG_W(RRC,"Failed to find ue context associated with eNB ue s1ap id: %u\n",
+              eNB_ue_s1ap_id);
+    return -1;
+  }
+
+  // release UE context
+  struct s1ap_eNB_ue_context_s *ue_context2_p = NULL;
+
+  if ((ue_context2_p = RB_REMOVE(s1ap_ue_map, &s1ap_eNB_instance_p->s1ap_ue_head, ue_context_p))
+      != NULL) {
+    LOG_W(RRC,"Removed UE context eNB_ue_s1ap_id %u\n",
+              ue_context2_p->eNB_ue_s1ap_id);
+    s1ap_eNB_free_ue_context(ue_context2_p);
+  } else {
+    LOG_W(RRC,"Removing UE context eNB_ue_s1ap_id %u: did not find context\n",
+              ue_context_p->eNB_ue_s1ap_id);
+  }
+  /*RB_FOREACH(ue_context_p, s1ap_ue_map, &s1ap_eNB_instance_p->s1ap_ue_head) {
+    S1AP_WARN("in s1ap_ue_map: UE context eNB_ue_s1ap_id %u mme_ue_s1ap_id %u state %u\n",
+        ue_context_p->eNB_ue_s1ap_id, ue_context_p->mme_ue_s1ap_id,
+        ue_context_p->ue_state);
+  }*/
+
+  return 0;
+}
diff --git a/openair2/RRC/LTE/rrc_eNB_S1AP.h b/openair2/RRC/LTE/rrc_eNB_S1AP.h
index 33a0caabc8..da63fc68a3 100644
--- a/openair2/RRC/LTE/rrc_eNB_S1AP.h
+++ b/openair2/RRC/LTE/rrc_eNB_S1AP.h
@@ -270,4 +270,8 @@ int rrc_eNB_send_PATH_SWITCH_REQ(const protocol_ctxt_t *const ctxt_pP,
                                  rrc_eNB_ue_context_t          *const ue_context_pP);
 int rrc_eNB_process_S1AP_PATH_SWITCH_REQ_ACK (MessageDef *msg_p, const char *msg_name, instance_t instance);
 
+int rrc_eNB_send_X2AP_UE_CONTEXT_RELEASE(const protocol_ctxt_t* const ctxt_pP, rrc_eNB_ue_context_t* const ue_context_pP);
+
+int s1ap_ue_context_release(instance_t instance, const uint32_t eNB_ue_s1ap_id);
+
 #endif /* RRC_ENB_S1AP_H_ */
diff --git a/openair2/X2AP/x2ap_eNB.c b/openair2/X2AP/x2ap_eNB.c
index db29f32d7b..eb79401153 100644
--- a/openair2/X2AP/x2ap_eNB.c
+++ b/openair2/X2AP/x2ap_eNB.c
@@ -79,6 +79,11 @@ static
 void x2ap_eNB_handle_handover_req_ack(instance_t instance,
                                       x2ap_handover_req_ack_t *x2ap_handover_req_ack);
 
+static
+void x2ap_eNB_handle_ue_context_release(instance_t instance,
+                                        x2ap_ue_context_release_t *x2ap_ue_context_release);
+
+
 static
 void x2ap_eNB_handle_sctp_data_ind(instance_t instance, sctp_data_ind_t *sctp_data_ind) {
   int result;
@@ -423,6 +428,23 @@ void x2ap_eNB_handle_handover_req_ack(instance_t instance,
           //x2ap_handover_req_ack->rrc_buffer, x2ap_handover_req_ack->rrc_buffer_size);
 }
 
+static
+void x2ap_eNB_handle_ue_context_release(instance_t instance,
+                                        x2ap_ue_context_release_t *x2ap_ue_context_release)
+{
+  x2ap_eNB_instance_t *instance_p;
+  x2ap_eNB_data_t     *target;
+  int target_enb_id = x2ap_ue_context_release->target_mod_id;
+
+  instance_p = x2ap_eNB_get_instance(instance);
+  DevAssert(instance_p != NULL);
+
+  target = x2ap_is_eNB_id_in_list(target_enb_id);
+  DevAssert(target != NULL);
+
+  x2ap_eNB_generate_x2_ue_context_release(target, x2ap_ue_context_release);
+}
+
 void *x2ap_task(void *arg) {
   MessageDef *received_msg = NULL;
   int         result;
@@ -454,6 +476,11 @@ void *x2ap_task(void *arg) {
                                          &X2AP_HANDOVER_REQ_ACK(received_msg));
         break;
 
+      case X2AP_UE_CONTEXT_RELEASE:
+        x2ap_eNB_handle_ue_context_release(ITTI_MESSAGE_GET_INSTANCE(received_msg),
+                                                &X2AP_UE_CONTEXT_RELEASE(received_msg));
+        break;
+
       case SCTP_INIT_MSG_MULTI_CNF:
         x2ap_eNB_handle_sctp_init_msg_multi_cnf(ITTI_MESSAGE_GET_INSTANCE(received_msg),
                                                 &received_msg->ittiMsg.sctp_init_msg_multi_cnf);
diff --git a/openair2/X2AP/x2ap_eNB_decoder.c b/openair2/X2AP/x2ap_eNB_decoder.c
index 553cf8e559..a5b19fe2cc 100644
--- a/openair2/X2AP/x2ap_eNB_decoder.c
+++ b/openair2/X2AP/x2ap_eNB_decoder.c
@@ -48,6 +48,10 @@ static int x2ap_eNB_decode_initiating_message(X2AP_X2AP_PDU_t *pdu)
       //asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_X2AP_X2AP_PDU, pdu);
       X2AP_INFO("x2ap_eNB_decode_initiating_message!\n");
       break;
+    case X2AP_ProcedureCode_id_uEContextRelease:
+      //asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_X2AP_X2AP_PDU, pdu);
+      X2AP_INFO("x2ap_eNB_decode_initiating_message!\n");
+      break;
 
     default:
       X2AP_ERROR("Unknown procedure ID (%d) for initiating message\n",
diff --git a/openair2/X2AP/x2ap_eNB_generate_messages.c b/openair2/X2AP/x2ap_eNB_generate_messages.c
index 76d6053b59..65dc3ae9d8 100644
--- a/openair2/X2AP/x2ap_eNB_generate_messages.c
+++ b/openair2/X2AP/x2ap_eNB_generate_messages.c
@@ -619,8 +619,8 @@ int x2ap_eNB_generate_x2_handover_request_ack (x2ap_eNB_data_t *x2ap_eNB_data_p,
   ie = (X2AP_HandoverRequestAcknowledge_IEs_t *)calloc(1, sizeof(X2AP_HandoverRequestAcknowledge_IEs_t));
   ie->id = X2AP_ProtocolIE_ID_id_New_eNB_UE_X2AP_ID;
   ie->criticality = X2AP_Criticality_ignore;
-  ie->value.present = X2AP_HandoverRequestAcknowledge_IEs__value_PR_UE_X2AP_ID;
-  ie->value.choice.UE_X2AP_ID = 0;
+  ie->value.present = X2AP_HandoverRequestAcknowledge_IEs__value_PR_UE_X2AP_ID_1;
+  ie->value.choice.UE_X2AP_ID_1 = 0;
   ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
 
   /* mandatory */
@@ -668,7 +668,7 @@ int x2ap_eNB_generate_x2_handover_request_ack (x2ap_eNB_data_t *x2ap_eNB_data_p,
   return ret;
 }
 
-int x2ap_eNB_generate_x2_ue_context_release (x2ap_eNB_data_t *x2ap_eNB_data_p)
+int x2ap_eNB_generate_x2_ue_context_release (x2ap_eNB_data_t *x2ap_eNB_data_p, x2ap_ue_context_release_t *x2ap_ue_context_release)
 {
 
   X2AP_X2AP_PDU_t                pdu;
@@ -701,15 +701,15 @@ int x2ap_eNB_generate_x2_ue_context_release (x2ap_eNB_data_t *x2ap_eNB_data_p)
   ie->id = X2AP_ProtocolIE_ID_id_Old_eNB_UE_X2AP_ID;
   ie->criticality = X2AP_Criticality_reject;
   ie->value.present = X2AP_UEContextRelease_IEs__value_PR_UE_X2AP_ID;
-  ie->value.choice.UE_X2AP_ID = 0;
+  ie->value.choice.UE_X2AP_ID = x2ap_ue_context_release->old_eNB_ue_x2ap_id;
   ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
 
   /* mandatory */
   ie = (X2AP_UEContextRelease_IEs_t *)calloc(1, sizeof(X2AP_UEContextRelease_IEs_t));
   ie->id = X2AP_ProtocolIE_ID_id_New_eNB_UE_X2AP_ID;
   ie->criticality = X2AP_Criticality_reject;
-  ie->value.present = X2AP_UEContextRelease_IEs__value_PR_UE_X2AP_ID;
-  ie->value.choice.UE_X2AP_ID = 0;
+  ie->value.present = X2AP_UEContextRelease_IEs__value_PR_UE_X2AP_ID_1;
+  ie->value.choice.UE_X2AP_ID_1 = x2ap_ue_context_release->new_eNB_ue_x2ap_id;
   ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
 
   if (x2ap_eNB_encode_pdu(&pdu, &buffer, &len) < 0) {
diff --git a/openair2/X2AP/x2ap_eNB_generate_messages.h b/openair2/X2AP/x2ap_eNB_generate_messages.h
index 9028d8cd05..df599c60ab 100644
--- a/openair2/X2AP/x2ap_eNB_generate_messages.h
+++ b/openair2/X2AP/x2ap_eNB_generate_messages.h
@@ -53,6 +53,7 @@ int x2ap_eNB_generate_x2_handover_request (x2ap_eNB_data_t *x2ap_eNB_data_p,
 int x2ap_eNB_generate_x2_handover_request_ack (x2ap_eNB_data_t *x2ap_eNB_data_p,
                                                x2ap_handover_req_ack_t *x2ap_handover_req_ack);
 
-int x2ap_eNB_generate_x2_ue_context_release (x2ap_eNB_data_t *x2ap_eNB_data_p);
+int x2ap_eNB_generate_x2_ue_context_release (x2ap_eNB_data_t *x2ap_eNB_data_p,
+                                             x2ap_ue_context_release_t *x2ap_ue_context_release);
 #endif /*  X2AP_ENB_GENERATE_MESSAGES_H_ */
 
diff --git a/openair2/X2AP/x2ap_eNB_handler.c b/openair2/X2AP/x2ap_eNB_handler.c
index b3b83a7e69..c751239095 100644
--- a/openair2/X2AP/x2ap_eNB_handler.c
+++ b/openair2/X2AP/x2ap_eNB_handler.c
@@ -71,6 +71,12 @@ int x2ap_eNB_handle_handover_response (instance_t instance,
                                       uint32_t stream,
                                       X2AP_X2AP_PDU_t *pdu);
 
+static
+int x2ap_eNB_handle_ue_context_release (instance_t instance,
+                                        uint32_t assoc_id,
+                                        uint32_t stream,
+                                        X2AP_X2AP_PDU_t *pdu);
+
 /* Handlers matrix. Only eNB related procedure present here */
 x2ap_message_decoded_callback x2ap_messages_callback[][3] = {
   { x2ap_eNB_handle_handover_preparation, x2ap_eNB_handle_handover_response, 0 }, /* handoverPreparation */
@@ -78,7 +84,7 @@ x2ap_message_decoded_callback x2ap_messages_callback[][3] = {
   { 0, 0, 0 }, /* loadIndication */
   { 0, 0, 0 }, /* errorIndication */
   { 0, 0, 0 }, /* snStatusTransfer */
-  { 0, 0, 0 }, /* uEContextRelease */
+  { x2ap_eNB_handle_ue_context_release, 0, 0 }, /* uEContextRelease */
   { x2ap_eNB_handle_x2_setup_request, x2ap_eNB_handle_x2_setup_response, x2ap_eNB_handle_x2_setup_failure }, /* x2Setup */
   { 0, 0, 0 }, /* reset */
   { 0, 0, 0 }, /* eNBConfigurationUpdate */
@@ -726,3 +732,47 @@ int x2ap_eNB_handle_handover_response (instance_t instance,
   itti_send_msg_to_task(TASK_RRC_ENB, x2ap_eNB_data->x2ap_eNB_instance->instance, msg);
   return 0;
 }
+
+
+static
+int x2ap_eNB_handle_ue_context_release (instance_t instance,
+                                        uint32_t assoc_id,
+                                        uint32_t stream,
+                                        X2AP_X2AP_PDU_t *pdu)
+{
+  X2AP_UEContextRelease_t             *x2UEContextRelease;
+  X2AP_UEContextRelease_IEs_t         *ie;
+
+  x2ap_eNB_data_t                     *x2ap_eNB_data;
+  MessageDef                          *msg;
+
+  DevAssert (pdu != NULL);
+  x2UEContextRelease = &pdu->choice.initiatingMessage.value.choice.UEContextRelease;
+
+  if (stream == 0) {
+    X2AP_ERROR ("Received new x2 ue context release on stream == 0\n");
+    /* TODO: send a x2 failure response */
+    return 0;
+  }
+
+  X2AP_DEBUG ("Received a new X2 ue context release\n");
+
+  x2ap_eNB_data = x2ap_get_eNB(NULL, assoc_id, 0);
+  DevAssert(x2ap_eNB_data != NULL);
+
+  msg = itti_alloc_new_message(TASK_X2AP, X2AP_UE_CONTEXT_RELEASE);
+
+  X2AP_FIND_PROTOCOLIE_BY_ID(X2AP_UEContextRelease_IEs_t, ie, x2UEContextRelease,
+                             X2AP_ProtocolIE_ID_id_Old_eNB_UE_X2AP_ID, true);
+
+  X2AP_UE_CONTEXT_RELEASE(msg).old_eNB_ue_x2ap_id = ie->value.choice.UE_X2AP_ID;
+
+  X2AP_FIND_PROTOCOLIE_BY_ID(X2AP_UEContextRelease_IEs_t, ie, x2UEContextRelease,
+                             X2AP_ProtocolIE_ID_id_New_eNB_UE_X2AP_ID, true);
+
+  X2AP_UE_CONTEXT_RELEASE(msg).new_eNB_ue_x2ap_id = ie->value.choice.UE_X2AP_ID;
+
+  itti_send_msg_to_task(TASK_RRC_ENB, x2ap_eNB_data->x2ap_eNB_instance->instance, msg);
+
+  return 0;
+}
-- 
GitLab