diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt
index 6c176a75c24a1c1945d84791e377d12aed305b59..a1b4955b21212ae2dad2c10b37ba0d88556f25b7 100644
--- a/cmake_targets/CMakeLists.txt
+++ b/cmake_targets/CMakeLists.txt
@@ -469,6 +469,7 @@ add_library(X2AP_ENB
   ${X2AP_DIR}/x2ap_eNB_itti_messaging.c
   ${X2AP_DIR}/x2ap_eNB_management_procedures.c
   ${X2AP_DIR}/x2ap_eNB_generate_messages.c
+  ${X2AP_DIR}/x2ap_ids.c
  )
 add_dependencies(X2AP_ENB rrc_flag x2_flag)
 
diff --git a/openair2/COMMON/rrc_messages_def.h b/openair2/COMMON/rrc_messages_def.h
index 08ea93f427add5aa6dbd032e54180848cb3e85da..40005459666864367edcbc5eee52917950d48d27 100644
--- a/openair2/COMMON/rrc_messages_def.h
+++ b/openair2/COMMON/rrc_messages_def.h
@@ -72,3 +72,6 @@ MESSAGE_DEF(NAS_CONN_ESTABLI_CNF,       MESSAGE_PRIORITY_MED,       NasConnEstab
 MESSAGE_DEF(NAS_CONN_RELEASE_IND,       MESSAGE_PRIORITY_MED,       NasConnReleaseInd,          nas_conn_release_ind)
 MESSAGE_DEF(NAS_UPLINK_DATA_CNF,        MESSAGE_PRIORITY_MED,       NasUlDataCnf,               nas_ul_data_cnf)
 MESSAGE_DEF(NAS_DOWNLINK_DATA_IND,      MESSAGE_PRIORITY_MED,       NasDlDataInd,               nas_dl_data_ind)
+
+// eNB: realtime -> RRC messages
+MESSAGE_DEF(RRC_SUBFRAME_PROCESS,       MESSAGE_PRIORITY_MED,       RrcSubframeProcess,         rrc_subframe_process)
diff --git a/openair2/COMMON/rrc_messages_types.h b/openair2/COMMON/rrc_messages_types.h
index f6e2216c1ba848233d85dd7b5206dc1f83f40d02..3880604e61f730203c7f665c5c6cd9da23f05e33 100644
--- a/openair2/COMMON/rrc_messages_types.h
+++ b/openair2/COMMON/rrc_messages_types.h
@@ -81,6 +81,8 @@
 #define NAS_UPLINK_DATA_CNF(mSGpTR)     (mSGpTR)->ittiMsg.nas_ul_data_cnf
 #define NAS_DOWNLINK_DATA_IND(mSGpTR)   (mSGpTR)->ittiMsg.nas_dl_data_ind
 
+#define RRC_SUBFRAME_PROCESS(mSGpTR)    (mSGpTR)->ittiMsg.rrc_subframe_process
+
 //-------------------------------------------------------------------------------------------//
 typedef struct RrcStateInd_s {
   Rrc_State_t     state;
@@ -399,4 +401,10 @@ typedef nas_release_ind_t       NasConnReleaseInd;
 typedef ul_info_transfer_cnf_t  NasUlDataCnf;
 typedef dl_info_transfer_ind_t  NasDlDataInd;
 
+// eNB: realtime -> RRC messages
+typedef struct rrc_subframe_process_s {
+  protocol_ctxt_t ctxt;
+  int             CC_id;
+} RrcSubframeProcess;
+
 #endif /* RRC_MESSAGES_TYPES_H_ */
diff --git a/openair2/COMMON/x2ap_messages_def.h b/openair2/COMMON/x2ap_messages_def.h
index d5555c183989b4573501e005620fba7b57302787..65fa9c801dee5279383d0cbeefe57406fc682fb4 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 7cdd0ff0ff37a605e59604a0186e951ae6d47463..38429b712edc64caf6557a7bc98a710e308586da 100644
--- a/openair2/COMMON/x2ap_messages_types.h
+++ b/openair2/COMMON/x2ap_messages_types.h
@@ -33,11 +33,21 @@
 #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 {
+  /* used for X2AP->RRC in source and RRC->X2AP in target */
+  int rnti;
+
+  int source_assoc_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.
@@ -128,12 +138,12 @@ typedef struct x2ap_lastvisitedcell_info_s {
   uint64_t time_UE_StayedInCell;
 }x2ap_lastvisitedcell_info_t;
 
-//used for src
 typedef struct x2ap_handover_req_s {
-  int source_rnti;                       /* TODO: to be fixed/remove */
-  int source_x2id;                       /* TODO: to be fixed/remove */
+  /* used for RRC->X2AP in source eNB */
+  int rnti;
 
-  int old_eNB_ue_x2ap_id;
+  /* used for X2AP->RRC in target eNB */
+  int x2_id;
 
   LTE_PhysCellId_t target_physCellId;
 
@@ -167,15 +177,17 @@ typedef struct x2ap_handover_req_s {
   uint8_t rrc_buffer[1024 /* arbitrary, big enough */];
   int rrc_buffer_size;
 
-  /* TODO: this parameter has to be removed */
-  int target_mod_id;
+  int target_assoc_id;
 } x2ap_handover_req_t;
 
 typedef struct x2ap_handover_req_ack_s {
-  int source_rnti;                       /* TODO: to be fixed/remove */
-  int source_x2id;                       /* TODO: to be fixed/remove */
-  /* TODO: this parameter has to be removed */
-  int target_mod_id;
+  /* used for RRC->X2AP in target and X2AP->RRC in source */
+  int rnti;
+
+  /* used for RRC->X2AP in target */
+  int x2_id_target;
+
+  int source_assoc_id;
 
   uint8_t nb_e_rabs_tobesetup;
 
diff --git a/openair2/ENB_APP/enb_config.c b/openair2/ENB_APP/enb_config.c
index 478f14f07c7a239174b54df7852768a5d21c90c6..d4439ab349defe28e370bc8b85b92c36224c1160 100644
--- a/openair2/ENB_APP/enb_config.c
+++ b/openair2/ENB_APP/enb_config.c
@@ -1923,162 +1923,164 @@ int RCconfig_X2(MessageDef *msg_p, uint32_t i) {
 
     if (ENBParamList.numelt > 0) {
       for (k = 0; k < ENBParamList.numelt; k++) {
-        if (ENBParamList.paramarray[k][ENB_ENB_ID_IDX].uptr == NULL) {
-          // Calculate a default eNB ID
+	if (ENBParamList.paramarray[k][ENB_ENB_ID_IDX].uptr == NULL) {
+	  // Calculate a default eNB ID
           if (EPC_MODE_ENABLED) {
-            uint32_t hash;
-            hash = s1ap_generate_eNB_id ();
-            enb_id = k + (hash & 0xFFFF8);
+	    uint32_t hash;
+	    hash = s1ap_generate_eNB_id ();
+	    enb_id = k + (hash & 0xFFFF8);
           } else {
-            enb_id = k;
+	    enb_id = k;
           }
-        } else {
-          enb_id = *(ENBParamList.paramarray[k][ENB_ENB_ID_IDX].uptr);
-        }
-
-        // search if in active list
-        for (j = 0; j < ENBSParams[ENB_ACTIVE_ENBS_IDX].numelt; j++) {
-          if (strcmp(ENBSParams[ENB_ACTIVE_ENBS_IDX].strlistptr[j], *(ENBParamList.paramarray[k][ENB_ENB_NAME_IDX].strptr)) == 0) {
-            paramdef_t PLMNParams[] = PLMNPARAMS_DESC;
-            paramlist_def_t PLMNParamList = {ENB_CONFIG_STRING_PLMN_LIST, NULL, 0};
-            /* map parameter checking array instances to parameter definition array instances */
-            checkedparam_t config_check_PLMNParams [] = PLMNPARAMS_CHECK;
-
-            for (int I = 0; I < sizeof(PLMNParams) / sizeof(paramdef_t); ++I)
-              PLMNParams[I].chkPptr = &(config_check_PLMNParams[I]);
-
-            paramdef_t X2Params[]  = X2PARAMS_DESC;
-            paramlist_def_t X2ParamList = {ENB_CONFIG_STRING_TARGET_ENB_X2_IP_ADDRESS,NULL,0};
-            paramdef_t SCTPParams[]  = SCTPPARAMS_DESC;
-            paramdef_t NETParams[]  =  NETPARAMS_DESC;
-            /* TODO: fix the size - if set lower we have a crash (MAX_OPTNAME_SIZE was 64 when this code was written) */
-            /* this is most probably a problem with the config module */
-            char aprefix[MAX_OPTNAME_SIZE*80 + 8];
-            sprintf(aprefix,"%s.[%i]",ENB_CONFIG_STRING_ENB_LIST,k);
-            /* Some default/random parameters */
-            X2AP_REGISTER_ENB_REQ (msg_p).eNB_id = enb_id;
-
-            if (strcmp(*(ENBParamList.paramarray[k][ENB_CELL_TYPE_IDX].strptr), "CELL_MACRO_ENB") == 0) {
-              X2AP_REGISTER_ENB_REQ (msg_p).cell_type = CELL_MACRO_ENB;
-            } else  if (strcmp(*(ENBParamList.paramarray[k][ENB_CELL_TYPE_IDX].strptr), "CELL_HOME_ENB") == 0) {
-              X2AP_REGISTER_ENB_REQ (msg_p).cell_type = CELL_HOME_ENB;
-            } else {
-              AssertFatal (0,
-                           "Failed to parse eNB configuration file %s, enb %d unknown value \"%s\" for cell_type choice: CELL_MACRO_ENB or CELL_HOME_ENB !\n",
-                           RC.config_file_name, i, *(ENBParamList.paramarray[k][ENB_CELL_TYPE_IDX].strptr));
-            }
-
-            X2AP_REGISTER_ENB_REQ (msg_p).eNB_name         = strdup(*(ENBParamList.paramarray[k][ENB_ENB_NAME_IDX].strptr));
-            X2AP_REGISTER_ENB_REQ (msg_p).tac              = *ENBParamList.paramarray[k][ENB_TRACKING_AREA_CODE_IDX].uptr;
-            config_getlist(&PLMNParamList, PLMNParams, sizeof(PLMNParams)/sizeof(paramdef_t), aprefix);
-
-            if (PLMNParamList.numelt < 1 || PLMNParamList.numelt > 6)
-              AssertFatal(0, "The number of PLMN IDs must be in [1,6], but is %d\n",
-                          PLMNParamList.numelt);
-
-            if (PLMNParamList.numelt > 1)
-              LOG_W(X2AP, "X2AP currently handles only one PLMN, ignoring the others!\n");
-
-            X2AP_REGISTER_ENB_REQ (msg_p).mcc = *PLMNParamList.paramarray[0][ENB_MOBILE_COUNTRY_CODE_IDX].uptr;
-            X2AP_REGISTER_ENB_REQ (msg_p).mnc = *PLMNParamList.paramarray[0][ENB_MOBILE_NETWORK_CODE_IDX].uptr;
-            X2AP_REGISTER_ENB_REQ (msg_p).mnc_digit_length = *PLMNParamList.paramarray[0][ENB_MNC_DIGIT_LENGTH].u8ptr;
-            AssertFatal(X2AP_REGISTER_ENB_REQ(msg_p).mnc_digit_length == 3
-                        || X2AP_REGISTER_ENB_REQ(msg_p).mnc < 100,
-                        "MNC %d cannot be encoded in two digits as requested (change mnc_digit_length to 3)\n",
-                        X2AP_REGISTER_ENB_REQ(msg_p).mnc);
-            /* CC params */
-            config_getlist(&CCsParamList, NULL, 0, aprefix);
-            X2AP_REGISTER_ENB_REQ (msg_p).num_cc = CCsParamList.numelt;
-
-            if (CCsParamList.numelt > 0) {
-              //char ccspath[MAX_OPTNAME_SIZE*2 + 16];
-              for (J = 0; J < CCsParamList.numelt ; J++) {
-                sprintf(aprefix, "%s.[%i].%s.[%i]", ENB_CONFIG_STRING_ENB_LIST, k, ENB_CONFIG_STRING_COMPONENT_CARRIERS, J);
-                config_get(CCsParams, sizeof(CCsParams)/sizeof(paramdef_t), aprefix);
-                X2AP_REGISTER_ENB_REQ (msg_p).eutra_band[J] = ccparams_lte.eutra_band;
-                X2AP_REGISTER_ENB_REQ (msg_p).downlink_frequency[J] = (uint32_t) ccparams_lte.downlink_frequency;
-                X2AP_REGISTER_ENB_REQ (msg_p).uplink_frequency_offset[J] = (unsigned int) ccparams_lte.uplink_frequency_offset;
-                X2AP_REGISTER_ENB_REQ (msg_p).Nid_cell[J]= ccparams_lte.Nid_cell;
-
-                if (ccparams_lte.Nid_cell>503) {
-                  AssertFatal (0,
-                               "Failed to parse eNB configuration file %s, enb %d unknown value \"%d\" for Nid_cell choice: 0...503 !\n",
-                               RC.config_file_name, k, ccparams_lte.Nid_cell);
-                }
-
-                X2AP_REGISTER_ENB_REQ (msg_p).N_RB_DL[J]= ccparams_lte.N_RB_DL;
-
-                if ((ccparams_lte.N_RB_DL!=6) && (ccparams_lte.N_RB_DL!=15) && (ccparams_lte.N_RB_DL!=25) && (ccparams_lte.N_RB_DL!=50) && (ccparams_lte.N_RB_DL!=75) && (ccparams_lte.N_RB_DL!=100)) {
-                  AssertFatal (0,
-                               "Failed to parse eNB configuration file %s, enb %d unknown value \"%d\" for N_RB_DL choice: 6,15,25,50,75,100 !\n",
-                               RC.config_file_name, k, ccparams_lte.N_RB_DL);
-                }
-
-                if (strcmp(ccparams_lte.frame_type, "FDD") == 0) {
-                  X2AP_REGISTER_ENB_REQ (msg_p).frame_type[J] = FDD;
-                } else  if (strcmp(ccparams_lte.frame_type, "TDD") == 0) {
-                  X2AP_REGISTER_ENB_REQ (msg_p).frame_type[J] = TDD;
-                } else {
-                  AssertFatal (0,
-                               "Failed to parse eNB configuration file %s, enb %d unknown value \"%s\" for frame_type choice: FDD or TDD !\n",
-                               RC.config_file_name, k, ccparams_lte.frame_type);
-                }
-
-                X2AP_REGISTER_ENB_REQ (msg_p).fdd_earfcn_DL[J] = to_earfcn_DL(ccparams_lte.eutra_band, ccparams_lte.downlink_frequency, ccparams_lte.N_RB_DL);
-                X2AP_REGISTER_ENB_REQ (msg_p).fdd_earfcn_UL[J] = to_earfcn_UL(ccparams_lte.eutra_band, ccparams_lte.downlink_frequency + ccparams_lte.uplink_frequency_offset, ccparams_lte.N_RB_DL);
-              }
-            }
-
-            sprintf(aprefix,"%s.[%i]",ENB_CONFIG_STRING_ENB_LIST,k);
-            config_getlist( &X2ParamList,X2Params,sizeof(X2Params)/sizeof(paramdef_t),aprefix);
-            AssertFatal(X2ParamList.numelt <= X2AP_MAX_NB_ENB_IP_ADDRESS,
-                        "value of X2ParamList.numelt %d must be lower than X2AP_MAX_NB_ENB_IP_ADDRESS %d value: reconsider to increase X2AP_MAX_NB_ENB_IP_ADDRESS\n",
-                        X2ParamList.numelt,X2AP_MAX_NB_ENB_IP_ADDRESS);
-            X2AP_REGISTER_ENB_REQ (msg_p).nb_x2 = 0;
-
-            for (l = 0; l < X2ParamList.numelt; l++) {
-              X2AP_REGISTER_ENB_REQ (msg_p).nb_x2 += 1;
-              strcpy(X2AP_REGISTER_ENB_REQ (msg_p).target_enb_x2_ip_address[l].ipv4_address,*(X2ParamList.paramarray[l][ENB_X2_IPV4_ADDRESS_IDX].strptr));
-              strcpy(X2AP_REGISTER_ENB_REQ (msg_p).target_enb_x2_ip_address[l].ipv6_address,*(X2ParamList.paramarray[l][ENB_X2_IPV6_ADDRESS_IDX].strptr));
-
-              if (strcmp(*(X2ParamList.paramarray[l][ENB_X2_IP_ADDRESS_PREFERENCE_IDX].strptr), "ipv4") == 0) {
-                X2AP_REGISTER_ENB_REQ (msg_p).target_enb_x2_ip_address[l].ipv4 = 1;
-              } else if (strcmp(*(X2ParamList.paramarray[l][ENB_X2_IP_ADDRESS_PREFERENCE_IDX].strptr), "ipv6") == 0) {
-                X2AP_REGISTER_ENB_REQ (msg_p).target_enb_x2_ip_address[l].ipv6 = 1;
-              } else if (strcmp(*(X2ParamList.paramarray[l][ENB_X2_IP_ADDRESS_PREFERENCE_IDX].strptr), "no") == 0) {
-                X2AP_REGISTER_ENB_REQ (msg_p).target_enb_x2_ip_address[l].ipv4 = 1;
-                X2AP_REGISTER_ENB_REQ (msg_p).target_enb_x2_ip_address[l].ipv6 = 1;
-              }
-            }
-
-            // SCTP SETTING
-            X2AP_REGISTER_ENB_REQ (msg_p).sctp_out_streams = SCTP_OUT_STREAMS;
-            X2AP_REGISTER_ENB_REQ (msg_p).sctp_in_streams  = SCTP_IN_STREAMS;
+	} else {
+	  enb_id = *(ENBParamList.paramarray[k][ENB_ENB_ID_IDX].uptr);
+	}
+	
+	// search if in active list
+	for (j = 0; j < ENBSParams[ENB_ACTIVE_ENBS_IDX].numelt; j++) {
+	  if (strcmp(ENBSParams[ENB_ACTIVE_ENBS_IDX].strlistptr[j], *(ENBParamList.paramarray[k][ENB_ENB_NAME_IDX].strptr)) == 0) {
+	    paramdef_t PLMNParams[] = PLMNPARAMS_DESC;
+	    paramlist_def_t PLMNParamList = {ENB_CONFIG_STRING_PLMN_LIST, NULL, 0};
+	    /* map parameter checking array instances to parameter definition array instances */
+	    checkedparam_t config_check_PLMNParams [] = PLMNPARAMS_CHECK;
+	    
+	    for (int I = 0; I < sizeof(PLMNParams) / sizeof(paramdef_t); ++I)
+	      PLMNParams[I].chkPptr = &(config_check_PLMNParams[I]);
+	    
+	    paramdef_t X2Params[]  = X2PARAMS_DESC;
+	    paramlist_def_t X2ParamList = {ENB_CONFIG_STRING_TARGET_ENB_X2_IP_ADDRESS,NULL,0};
+	    paramdef_t SCTPParams[]  = SCTPPARAMS_DESC;
+	    paramdef_t NETParams[]  =  NETPARAMS_DESC;
+	    /* TODO: fix the size - if set lower we have a crash (MAX_OPTNAME_SIZE was 64 when this code was written) */
+	    /* this is most probably a problem with the config module */
+	    char aprefix[MAX_OPTNAME_SIZE*80 + 8];
+	    sprintf(aprefix,"%s.[%i]",ENB_CONFIG_STRING_ENB_LIST,k);
+	    /* Some default/random parameters */
+	    X2AP_REGISTER_ENB_REQ (msg_p).eNB_id = enb_id;
+	    
+	    if (strcmp(*(ENBParamList.paramarray[k][ENB_CELL_TYPE_IDX].strptr), "CELL_MACRO_ENB") == 0) {
+	      X2AP_REGISTER_ENB_REQ (msg_p).cell_type = CELL_MACRO_ENB;
+	    } else  if (strcmp(*(ENBParamList.paramarray[k][ENB_CELL_TYPE_IDX].strptr), "CELL_HOME_ENB") == 0) {
+	      X2AP_REGISTER_ENB_REQ (msg_p).cell_type = CELL_HOME_ENB;
+	    } else {
+	      AssertFatal (0,
+			   "Failed to parse eNB configuration file %s, enb %d unknown value \"%s\" for cell_type choice: CELL_MACRO_ENB or CELL_HOME_ENB !\n",
+			   RC.config_file_name, i, *(ENBParamList.paramarray[k][ENB_CELL_TYPE_IDX].strptr));
+	    }
+	    
+	    X2AP_REGISTER_ENB_REQ (msg_p).eNB_name         = strdup(*(ENBParamList.paramarray[k][ENB_ENB_NAME_IDX].strptr));
+	    X2AP_REGISTER_ENB_REQ (msg_p).tac              = *ENBParamList.paramarray[k][ENB_TRACKING_AREA_CODE_IDX].uptr;
+	    config_getlist(&PLMNParamList, PLMNParams, sizeof(PLMNParams)/sizeof(paramdef_t), aprefix);
+	    
+	    if (PLMNParamList.numelt < 1 || PLMNParamList.numelt > 6)
+	      AssertFatal(0, "The number of PLMN IDs must be in [1,6], but is %d\n",
+			  PLMNParamList.numelt);
+	    
+	    if (PLMNParamList.numelt > 1)
+	      LOG_W(X2AP, "X2AP currently handles only one PLMN, ignoring the others!\n");
+	    
+	    X2AP_REGISTER_ENB_REQ (msg_p).mcc = *PLMNParamList.paramarray[0][ENB_MOBILE_COUNTRY_CODE_IDX].uptr;
+	    X2AP_REGISTER_ENB_REQ (msg_p).mnc = *PLMNParamList.paramarray[0][ENB_MOBILE_NETWORK_CODE_IDX].uptr;
+	    X2AP_REGISTER_ENB_REQ (msg_p).mnc_digit_length = *PLMNParamList.paramarray[0][ENB_MNC_DIGIT_LENGTH].u8ptr;
+	    AssertFatal(X2AP_REGISTER_ENB_REQ(msg_p).mnc_digit_length == 3
+			|| X2AP_REGISTER_ENB_REQ(msg_p).mnc < 100,
+			"MNC %d cannot be encoded in two digits as requested (change mnc_digit_length to 3)\n",
+			X2AP_REGISTER_ENB_REQ(msg_p).mnc);
+	    /* CC params */
+	    config_getlist(&CCsParamList, NULL, 0, aprefix);
+	    X2AP_REGISTER_ENB_REQ (msg_p).num_cc = CCsParamList.numelt;
+	    
+	    if (CCsParamList.numelt > 0) {
+	      //char ccspath[MAX_OPTNAME_SIZE*2 + 16];
+	      for (J = 0; J < CCsParamList.numelt ; J++) {
+		sprintf(aprefix, "%s.[%i].%s.[%i]", ENB_CONFIG_STRING_ENB_LIST, k, ENB_CONFIG_STRING_COMPONENT_CARRIERS, J);
+		config_get(CCsParams, sizeof(CCsParams)/sizeof(paramdef_t), aprefix);
+		X2AP_REGISTER_ENB_REQ (msg_p).eutra_band[J] = ccparams_lte.eutra_band;
+		X2AP_REGISTER_ENB_REQ (msg_p).downlink_frequency[J] = (uint32_t) ccparams_lte.downlink_frequency;
+		X2AP_REGISTER_ENB_REQ (msg_p).uplink_frequency_offset[J] = (unsigned int) ccparams_lte.uplink_frequency_offset;
+		X2AP_REGISTER_ENB_REQ (msg_p).Nid_cell[J]= ccparams_lte.Nid_cell;
+		
+		if (ccparams_lte.Nid_cell>503) {
+		  AssertFatal (0,
+			       "Failed to parse eNB configuration file %s, enb %d unknown value \"%d\" for Nid_cell choice: 0...503 !\n",
+			       RC.config_file_name, k, ccparams_lte.Nid_cell);
+		}
+		
+		X2AP_REGISTER_ENB_REQ (msg_p).N_RB_DL[J]= ccparams_lte.N_RB_DL;
+		
+		if ((ccparams_lte.N_RB_DL!=6) && (ccparams_lte.N_RB_DL!=15) && (ccparams_lte.N_RB_DL!=25) && (ccparams_lte.N_RB_DL!=50) && (ccparams_lte.N_RB_DL!=75) && (ccparams_lte.N_RB_DL!=100)) {
+		  AssertFatal (0,
+			       "Failed to parse eNB configuration file %s, enb %d unknown value \"%d\" for N_RB_DL choice: 6,15,25,50,75,100 !\n",
+			       RC.config_file_name, k, ccparams_lte.N_RB_DL);
+		}
+		
+		if (strcmp(ccparams_lte.frame_type, "FDD") == 0) {
+		  X2AP_REGISTER_ENB_REQ (msg_p).frame_type[J] = FDD;
+		} else  if (strcmp(ccparams_lte.frame_type, "TDD") == 0) {
+		  X2AP_REGISTER_ENB_REQ (msg_p).frame_type[J] = TDD;
+		} else {
+		  AssertFatal (0,
+			       "Failed to parse eNB configuration file %s, enb %d unknown value \"%s\" for frame_type choice: FDD or TDD !\n",
+			       RC.config_file_name, k, ccparams_lte.frame_type);
+		}
+		
+		X2AP_REGISTER_ENB_REQ (msg_p).fdd_earfcn_DL[J] = to_earfcn_DL(ccparams_lte.eutra_band, ccparams_lte.downlink_frequency, ccparams_lte.N_RB_DL);
+		X2AP_REGISTER_ENB_REQ (msg_p).fdd_earfcn_UL[J] = to_earfcn_UL(ccparams_lte.eutra_band, ccparams_lte.downlink_frequency + ccparams_lte.uplink_frequency_offset, ccparams_lte.N_RB_DL);
+	      }
+	    }
+	    
+	    sprintf(aprefix,"%s.[%i]",ENB_CONFIG_STRING_ENB_LIST,k);
+	    config_getlist( &X2ParamList,X2Params,sizeof(X2Params)/sizeof(paramdef_t),aprefix);
+	    AssertFatal(X2ParamList.numelt <= X2AP_MAX_NB_ENB_IP_ADDRESS,
+			"value of X2ParamList.numelt %d must be lower than X2AP_MAX_NB_ENB_IP_ADDRESS %d value: reconsider to increase X2AP_MAX_NB_ENB_IP_ADDRESS\n",
+			X2ParamList.numelt,X2AP_MAX_NB_ENB_IP_ADDRESS);
+	    X2AP_REGISTER_ENB_REQ (msg_p).nb_x2 = 0;
+	    
+	    for (l = 0; l < X2ParamList.numelt; l++) {
+	      X2AP_REGISTER_ENB_REQ (msg_p).nb_x2 += 1;
+	      strcpy(X2AP_REGISTER_ENB_REQ (msg_p).target_enb_x2_ip_address[l].ipv4_address,*(X2ParamList.paramarray[l][ENB_X2_IPV4_ADDRESS_IDX].strptr));
+	      strcpy(X2AP_REGISTER_ENB_REQ (msg_p).target_enb_x2_ip_address[l].ipv6_address,*(X2ParamList.paramarray[l][ENB_X2_IPV6_ADDRESS_IDX].strptr));
+	      
+	      if (strcmp(*(X2ParamList.paramarray[l][ENB_X2_IP_ADDRESS_PREFERENCE_IDX].strptr), "ipv4") == 0) {
+		X2AP_REGISTER_ENB_REQ (msg_p).target_enb_x2_ip_address[l].ipv4 = 1;
+		X2AP_REGISTER_ENB_REQ (msg_p).target_enb_x2_ip_address[l].ipv6 = 0;
+	      } else if (strcmp(*(X2ParamList.paramarray[l][ENB_X2_IP_ADDRESS_PREFERENCE_IDX].strptr), "ipv6") == 0) {
+		X2AP_REGISTER_ENB_REQ (msg_p).target_enb_x2_ip_address[l].ipv4 = 0;
+		X2AP_REGISTER_ENB_REQ (msg_p).target_enb_x2_ip_address[l].ipv6 = 1;
+	      } else if (strcmp(*(X2ParamList.paramarray[l][ENB_X2_IP_ADDRESS_PREFERENCE_IDX].strptr), "no") == 0) {
+		X2AP_REGISTER_ENB_REQ (msg_p).target_enb_x2_ip_address[l].ipv4 = 1;
+		X2AP_REGISTER_ENB_REQ (msg_p).target_enb_x2_ip_address[l].ipv6 = 1;
+	      }
+	    }
+	    
+	    // SCTP SETTING
+	    X2AP_REGISTER_ENB_REQ (msg_p).sctp_out_streams = SCTP_OUT_STREAMS;
+	    X2AP_REGISTER_ENB_REQ (msg_p).sctp_in_streams  = SCTP_IN_STREAMS;
 
             if (EPC_MODE_ENABLED) {
-              sprintf(aprefix,"%s.[%i].%s",ENB_CONFIG_STRING_ENB_LIST,k,ENB_CONFIG_STRING_SCTP_CONFIG);
-              config_get( SCTPParams,sizeof(SCTPParams)/sizeof(paramdef_t),aprefix);
-              X2AP_REGISTER_ENB_REQ (msg_p).sctp_in_streams = (uint16_t)*(SCTPParams[ENB_SCTP_INSTREAMS_IDX].uptr);
-              X2AP_REGISTER_ENB_REQ (msg_p).sctp_out_streams = (uint16_t)*(SCTPParams[ENB_SCTP_OUTSTREAMS_IDX].uptr);
-            }
-
-            sprintf(aprefix,"%s.[%i].%s",ENB_CONFIG_STRING_ENB_LIST,k,ENB_CONFIG_STRING_NETWORK_INTERFACES_CONFIG);
-            // NETWORK_INTERFACES
-            config_get( NETParams,sizeof(NETParams)/sizeof(paramdef_t),aprefix);
-            X2AP_REGISTER_ENB_REQ (msg_p).enb_port_for_X2C = (uint32_t)*(NETParams[ENB_PORT_FOR_X2C_IDX].uptr);
-
-            if ((NETParams[ENB_IPV4_ADDR_FOR_X2C_IDX].strptr == NULL) || (X2AP_REGISTER_ENB_REQ (msg_p).enb_port_for_X2C == 0)) {
-              LOG_E(RRC,"Add eNB IPv4 address and/or port for X2C in the CONF file!\n");
-              exit(1);
+	      sprintf(aprefix,"%s.[%i].%s",ENB_CONFIG_STRING_ENB_LIST,k,ENB_CONFIG_STRING_SCTP_CONFIG);
+	      config_get( SCTPParams,sizeof(SCTPParams)/sizeof(paramdef_t),aprefix);
+	      X2AP_REGISTER_ENB_REQ (msg_p).sctp_in_streams = (uint16_t)*(SCTPParams[ENB_SCTP_INSTREAMS_IDX].uptr);
+	      X2AP_REGISTER_ENB_REQ (msg_p).sctp_out_streams = (uint16_t)*(SCTPParams[ENB_SCTP_OUTSTREAMS_IDX].uptr);
             }
 
-            cidr = *(NETParams[ENB_IPV4_ADDR_FOR_X2C_IDX].strptr);
-            address = strtok(cidr, "/");
-            X2AP_REGISTER_ENB_REQ (msg_p).enb_x2_ip_address.ipv6 = 0;
-            X2AP_REGISTER_ENB_REQ (msg_p).enb_x2_ip_address.ipv4 = 1;
-            strcpy(X2AP_REGISTER_ENB_REQ (msg_p).enb_x2_ip_address.ipv4_address, address);
-          }
-        }
+	    sprintf(aprefix,"%s.[%i].%s",ENB_CONFIG_STRING_ENB_LIST,k,ENB_CONFIG_STRING_NETWORK_INTERFACES_CONFIG);
+	    // NETWORK_INTERFACES
+	    config_get( NETParams,sizeof(NETParams)/sizeof(paramdef_t),aprefix);
+	    X2AP_REGISTER_ENB_REQ (msg_p).enb_port_for_X2C = (uint32_t)*(NETParams[ENB_PORT_FOR_X2C_IDX].uptr);
+	    
+	    if ((NETParams[ENB_IPV4_ADDR_FOR_X2C_IDX].strptr == NULL) || (X2AP_REGISTER_ENB_REQ (msg_p).enb_port_for_X2C == 0)) {
+	      LOG_E(RRC,"Add eNB IPv4 address and/or port for X2C in the CONF file!\n");
+	      exit(1);
+	    }
+	    
+	    cidr = *(NETParams[ENB_IPV4_ADDR_FOR_X2C_IDX].strptr);
+	    address = strtok(cidr, "/");
+	    X2AP_REGISTER_ENB_REQ (msg_p).enb_x2_ip_address.ipv6 = 0;
+	    X2AP_REGISTER_ENB_REQ (msg_p).enb_x2_ip_address.ipv4 = 1;
+	    strcpy(X2AP_REGISTER_ENB_REQ (msg_p).enb_x2_ip_address.ipv4_address, address);
+	  }
+	}
       }
     }
   }
diff --git a/openair2/LAYER2/MAC/config.c b/openair2/LAYER2/MAC/config.c
index 1a334a36b9a3c32434c55b0800279119832cfd61..312edea2c17b751f84d266bb3392c9475886ffe3 100644
--- a/openair2/LAYER2/MAC/config.c
+++ b/openair2/LAYER2/MAC/config.c
@@ -883,7 +883,7 @@ int rrc_mac_config_req_eNB(module_id_t Mod_idP,
                              rntiP, -1
 #if (LTE_RRC_VERSION >= MAKE_VERSION(14, 0, 0))
                              ,
-                             -1
+                             0
 #endif
                              )) == -1)
      {
diff --git a/openair2/RRC/LTE/rrc_defs.h b/openair2/RRC/LTE/rrc_defs.h
index 38fc7b7262ced788cd273a94fa97c96acea93abd..22a9332a58c5842e81ac89ce1d1e71c6a99b786d 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 {
@@ -450,12 +451,14 @@ typedef struct HANDOVER_INFO_s {
   HO_STATE_t state; //current state of handover
   uint32_t modid_s; //module_idP of serving cell
   uint32_t modid_t; //module_idP of target cell
+  int assoc_id;
   uint8_t ueid_s; //UE index in serving cell
   uint8_t ueid_t; //UE index in target cell
   LTE_AS_Config_t as_config; /* these two parameters are taken from 36.331 section 10.2.2: HandoverPreparationInformation-r8-IEs */
   LTE_AS_Context_t as_context; /* They are mandatory for HO */
   uint8_t buf[RRC_BUF_SIZE];  /* ASN.1 encoded handoverCommandMessage */
   int size;   /* size of above message in bytes */
+  int x2_id;   /* X2AP UE ID in the target eNB */
 } HANDOVER_INFO;
 
 #define RRC_HEADER_SIZE_MAX 64
diff --git a/openair2/RRC/LTE/rrc_eNB.c b/openair2/RRC/LTE/rrc_eNB.c
index 3503956b108746292fbb28eacea58b1fe0c821d4..95048d763ea079ab28fd02129d8d697c4e8d28a6 100644
--- a/openair2/RRC/LTE/rrc_eNB.c
+++ b/openair2/RRC/LTE/rrc_eNB.c
@@ -105,6 +105,9 @@ extern uint16_t                     two_tier_hexagonal_cellIds[7];
 mui_t                               rrc_eNB_mui = 0;
 
 extern uint32_t to_earfcn_DL(int eutra_bandP, uint32_t dl_CarrierFreq, uint32_t bw);
+extern int rrc_eNB_process_security(const protocol_ctxt_t *const ctxt_pP, rrc_eNB_ue_context_t *const ue_context_pP, security_capabilities_t *security_capabilities_pP);
+extern void process_eNB_security_key (const protocol_ctxt_t *const ctxt_pP, rrc_eNB_ue_context_t *const ue_context_pP, uint8_t *security_key_pP);
+extern int derive_keNB_star(const uint8_t *kenb_32, const uint16_t pci, const uint32_t earfcn_dl, const bool is_rel8_only, uint8_t * kenb_star);
 
 void
 openair_rrc_on(
@@ -778,7 +781,8 @@ rrc_eNB_free_mem_UE_context(
       ue_context_pP->ue_context.measGapConfig = NULL;
     }*/
   if (ue_context_pP->ue_context.handover_info) {
-    ASN_STRUCT_FREE(asn_DEF_LTE_Handover, ue_context_pP->ue_context.handover_info);
+    /* TODO: be sure free is enough here (check memory leaks) */
+    free(ue_context_pP->ue_context.handover_info);
     ue_context_pP->ue_context.handover_info = NULL;
   }
 
@@ -846,7 +850,9 @@ rrc_eNB_free_UE(
       LOG_I(RRC, "[eNB %d] S1AP_UE_CONTEXT_RELEASE_REQ sent for RNTI %x, cause 21, radio connection with ue lost\n",
             enb_mod_idP,
             rnti);
+#if defined(ENABLE_USE_MME)
       rrc_eNB_send_S1AP_UE_CONTEXT_RELEASE_REQ(enb_mod_idP, ue_context_pP, S1AP_CAUSE_RADIO_NETWORK, 21);
+#endif
       // send cause 21: radio connection with ue lost
       /* From 3GPP 36300v10 p129 : 19.2.2.2.2 S1 UE Context Release Request (eNB triggered)
         * If the E-UTRAN internal reason is a radio link failure detected in the eNB, the eNB shall wait a sufficient time before
@@ -862,7 +868,9 @@ rrc_eNB_free_UE(
       LOG_I(RRC, "[eNB %d] S1AP_UE_CONTEXT_RELEASE_REQ sent for RNTI %x, cause 20, user inactivity\n",
             enb_mod_idP,
             rnti);
+#if defined(ENABLE_USE_MME)
       rrc_eNB_send_S1AP_UE_CONTEXT_RELEASE_REQ(enb_mod_idP, ue_context_pP, S1AP_CAUSE_RADIO_NETWORK, 20);
+#endif
       // send cause 20: user inactivity
       return;
     }
@@ -1035,7 +1043,9 @@ rrc_eNB_process_RRCConnectionSetupComplete(
 
   if (EPC_MODE_ENABLED == 1) {
     // Forward message to S1AP layer
+#if defined(ENABLE_USE_MME)
     rrc_eNB_send_S1AP_NAS_FIRST_REQ(ctxt_pP, ue_context_pP, rrcConnectionSetupComplete);
+#endif
   } else {
     // RRC loop back (no S1AP), send SecurityModeCommand to UE
     rrc_eNB_generate_SecurityModeCommand(ctxt_pP, ue_context_pP);
@@ -3800,6 +3810,9 @@ rrc_eNB_process_MeasurementReport(
   int neighboring_cells=-1;
   int ncell_index = 0;
   long ncell_max = -150;
+  uint32_t earfcn_dl;
+  uint8_t KeNB_star[32] = { 0 };
+
   T(T_ENB_RRC_MEASUREMENT_REPORT, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
     T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
 
@@ -3917,8 +3930,7 @@ rrc_eNB_process_MeasurementReport(
       ue_context_pP,
       X2AP_HANDOVER_REQ(msg).rrc_buffer,
       &X2AP_HANDOVER_REQ(msg).rrc_buffer_size);
-    X2AP_HANDOVER_REQ(msg).source_rnti = ctxt_pP->rnti;
-    X2AP_HANDOVER_REQ(msg).old_eNB_ue_x2ap_id = 0;
+    X2AP_HANDOVER_REQ(msg).rnti = ctxt_pP->rnti;
     X2AP_HANDOVER_REQ(msg).target_physCellId = measResults2->measResultNeighCells->choice.
         measResultListEUTRA.list.array[ncell_index]->physCellId;
     X2AP_HANDOVER_REQ(msg).ue_gummei.mcc = ue_context_pP->ue_context.ue_gummei.mcc;
@@ -3929,9 +3941,11 @@ rrc_eNB_process_MeasurementReport(
     // Don't know how to get this ID?
     X2AP_HANDOVER_REQ(msg).mme_ue_s1ap_id = ue_context_pP->ue_context.mme_ue_s1ap_id;
     X2AP_HANDOVER_REQ(msg).security_capabilities = ue_context_pP->ue_context.security_capabilities;
-    memcpy (X2AP_HANDOVER_REQ(msg).kenb,
-            ue_context_pP->ue_context.kenb,
-            32);
+    // compute keNB*
+    earfcn_dl = (uint32_t)to_earfcn_DL(RC.rrc[ctxt_pP->module_id]->carrier[0].eutra_band, RC.rrc[ctxt_pP->module_id]->carrier[0].dl_CarrierFreq,
+    RC.rrc[ctxt_pP->module_id]->carrier[0].N_RB_DL);
+    derive_keNB_star(ue_context_pP->ue_context.kenb, X2AP_HANDOVER_REQ(msg).target_physCellId, earfcn_dl, true, KeNB_star);
+    memcpy(X2AP_HANDOVER_REQ(msg).kenb, KeNB_star, 32);
     X2AP_HANDOVER_REQ(msg).kenb_ncc = ue_context_pP->ue_context.kenb_ncc;
     //X2AP_HANDOVER_REQ(msg).ue_ambr=ue_context_pP->ue_context.ue_ambr;
     X2AP_HANDOVER_REQ(msg).nb_e_rabs_tobesetup = ue_context_pP->ue_context.setup_e_rabs;
@@ -3973,114 +3987,15 @@ rrc_eNB_generate_HandoverPreparationInformation(
   *_size = ho_size;
 }
 
-#if 0
-//-----------------------------------------------------------------------------
-void
-rrc_eNB_generate_HandoverPreparationInformation(
-  const protocol_ctxt_t *const ctxt_pP,
-  rrc_eNB_ue_context_t *const ue_context_pP,
-  LTE_PhysCellId_t            targetPhyId
-)
-//-----------------------------------------------------------------------------
-{
-  struct rrc_eNB_ue_context_s        *ue_context_target_p = NULL;
-  //uint8_t                             UE_id_target        = -1;
-  uint8_t                             mod_id_target = get_adjacent_cell_mod_id(targetPhyId);
-  HANDOVER_INFO                      *handoverInfo = CALLOC(1, sizeof(*handoverInfo));
-  /*
-     uint8_t buffer[100];
-     uint8_t size;
-     struct LTE_PhysicalConfigDedicated  **physicalConfigDedicated = &RC.rrc[enb_mod_idP]->physicalConfigDedicated[ue_mod_idP];
-     RadioResourceConfigDedicated_t *radioResourceConfigDedicated = CALLOC(1,sizeof(RadioResourceConfigDedicated_t));
-   */
-  T(T_ENB_RRC_HANDOVER_PREPARATION_INFORMATION, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
-    T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
-  handoverInfo->as_config.antennaInfoCommon.antennaPortsCount = 0;    //Not used 0- but check value
-  handoverInfo->as_config.sourceDl_CarrierFreq = 36090;   //Verify!
-  memcpy((void *)&handoverInfo->as_config.sourceMasterInformationBlock,
-         (void *)&RC.rrc[ctxt_pP->module_id]->carrier[0] /* CROUX TBC */.mib, sizeof(LTE_MasterInformationBlock_t));
-  memcpy((void *)&handoverInfo->as_config.sourceMeasConfig,
-         (void *)ue_context_pP->ue_context.measConfig, sizeof(LTE_MeasConfig_t));
-  // FIXME handoverInfo not used...
-  free( handoverInfo );
-  handoverInfo = 0;
-  //to be configured
-  memset((void *)&ue_context_pP->ue_context.handover_info->as_config.sourceSecurityAlgorithmConfig,
-         0, sizeof(LTE_SecurityAlgorithmConfig_t));
-  memcpy((void *)&ue_context_pP->ue_context.handover_info->as_config.sourceSystemInformationBlockType1,
-         (void *)&RC.rrc[ctxt_pP->module_id]->carrier[0] /* CROUX TBC */.SIB1, sizeof(LTE_SystemInformationBlockType1_t));
-  memcpy((void *)&ue_context_pP->ue_context.handover_info->as_config.sourceSystemInformationBlockType2,
-         (void *)&RC.rrc[ctxt_pP->module_id]->carrier[0] /* CROUX TBC */.SIB23, sizeof(LTE_SystemInformationBlockType2_t));
-  ue_context_pP->ue_context.handover_info->as_context.reestablishmentInfo =
-    CALLOC(1, sizeof(LTE_ReestablishmentInfo_t));
-  ue_context_pP->ue_context.handover_info->as_context.reestablishmentInfo->sourcePhysCellId =
-    RC.rrc[ctxt_pP->module_id]->carrier[0] /* CROUX TBC */.physCellId;
-  ue_context_pP->ue_context.handover_info->as_context.reestablishmentInfo->targetCellShortMAC_I.buf = NULL;  // Check values later
-  ue_context_pP->ue_context.handover_info->as_context.reestablishmentInfo->targetCellShortMAC_I.size = 0;
-  ue_context_pP->ue_context.handover_info->as_context.reestablishmentInfo->targetCellShortMAC_I.bits_unused = 0;
-  ue_context_pP->ue_context.handover_info->as_context.reestablishmentInfo->additionalReestabInfoList = NULL;
-  ue_context_pP->ue_context.handover_info->ho_prepare = 0xFF;    //0xF0;
-  ue_context_pP->ue_context.handover_info->ho_complete = 0;
-
-  if (mod_id_target != 0xFF) {
-    //UE_id_target = rrc_find_free_ue_index(modid_target);
-    ue_context_target_p =
-      rrc_eNB_get_ue_context(
-        RC.rrc[mod_id_target],
-        ue_context_pP->ue_context.rnti);
-
-    /*UE_id_target = rrc_eNB_get_next_free_UE_index(
-                    mod_id_target,
-                    RC.rrc[ctxt_pP->module_id]->Info.UE_list[ue_mod_idP]);  //this should return a new index*/
-
-    if (ue_context_target_p == NULL) { // if not already in target cell
-      ue_context_target_p = rrc_eNB_allocate_new_UE_context(RC.rrc[ctxt_pP->module_id]);
-      ue_context_target_p->ue_id_rnti      = ue_context_pP->ue_context.rnti;             // LG: should not be the same
-      ue_context_target_p->ue_context.rnti = ue_context_target_p->ue_id_rnti; // idem
-      LOG_I(RRC,
-            "[eNB %d] Frame %d : Emulate sending HandoverPreparationInformation msg from eNB source %d to eNB target %ld: source UE_id %x target UE_id %x source_modId: %d target_modId: %d\n",
-            ctxt_pP->module_id,
-            ctxt_pP->frame,
-            RC.rrc[ctxt_pP->module_id]->carrier[0] /* CROUX TBC */.physCellId,
-            targetPhyId,
-            ue_context_pP->ue_context.rnti,
-            ue_context_target_p->ue_id_rnti,
-            ctxt_pP->module_id,
-            mod_id_target);
-      ue_context_target_p->ue_context.handover_info =
-        CALLOC(1, sizeof(*(ue_context_target_p->ue_context.handover_info)));
-      memcpy((void *)&ue_context_target_p->ue_context.handover_info->as_context,
-             (void *)&ue_context_pP->ue_context.handover_info->as_context,
-             sizeof(LTE_AS_Context_t));
-      memcpy((void *)&ue_context_target_p->ue_context.handover_info->as_config,
-             (void *)&ue_context_pP->ue_context.handover_info->as_config,
-             sizeof(LTE_AS_Config_t));
-      ue_context_target_p->ue_context.handover_info->ho_prepare = 0x00;// 0xFF;
-      ue_context_target_p->ue_context.handover_info->ho_complete = 0;
-      ue_context_pP->ue_context.handover_info->modid_t = mod_id_target;
-      ue_context_pP->ue_context.handover_info->ueid_s  = ue_context_pP->ue_context.rnti;
-      ue_context_pP->ue_context.handover_info->modid_s = ctxt_pP->module_id;
-      ue_context_target_p->ue_context.handover_info->modid_t = mod_id_target;
-      ue_context_target_p->ue_context.handover_info->modid_s = ctxt_pP->module_id;
-      ue_context_target_p->ue_context.handover_info->ueid_t  = ue_context_target_p->ue_context.rnti;
-    } else {
-      LOG_E(RRC, "\nError in obtaining free UE id in target eNB %ld for handover \n", targetPhyId);
-    }
-  } else {
-    LOG_E(RRC, "\nError in obtaining Module ID of target eNB for handover \n");
-  }
-}
-#endif
-
 void rrc_eNB_process_handoverPreparationInformation(int mod_id, x2ap_handover_req_t *m) {
   struct rrc_eNB_ue_context_s        *ue_context_target_p = NULL;
   /* TODO: get proper UE rnti */
   int rnti = taus() & 0xffff;
   int i;
   //global_rnti = rnti;
-  //HandoverPreparationInformation_t *ho = NULL;
-  //HandoverPreparationInformation_r8_IEs_t *ho_info;
-  //asn_dec_rval_t                      dec_rval;
+  LTE_HandoverPreparationInformation_t *ho = NULL;
+  LTE_HandoverPreparationInformation_r8_IEs_t *ho_info;
+  asn_dec_rval_t                      dec_rval;
   ue_context_target_p = rrc_eNB_get_ue_context(RC.rrc[mod_id], rnti);
 
   if (ue_context_target_p != NULL) {
@@ -4100,30 +4015,30 @@ void rrc_eNB_process_handoverPreparationInformation(int mod_id, x2ap_handover_re
   RB_INSERT(rrc_ue_tree_s, &RC.rrc[mod_id]->rrc_ue_head, ue_context_target_p);
   LOG_D(RRC, "eNB %d: Created new UE context uid %u\n", mod_id, ue_context_target_p->local_uid);
   ue_context_target_p->ue_context.handover_info = CALLOC(1, sizeof(*(ue_context_target_p->ue_context.handover_info)));
-  //ue_context_target_p->ue_context.handover_info->source_x2id = m->source_x2id;
   ue_context_target_p->ue_context.Status = RRC_HO_EXECUTION;
   ue_context_target_p->ue_context.handover_info->state = HO_ACK;
-  /* TODO: remove this hack */
-  //ue_context_target_p->ue_context.handover_info->modid_t = mod_id;
-  ue_context_target_p->ue_context.handover_info->modid_t = m->target_mod_id;
-  //ue_context_target_p->ue_context.handover_info->modid_s = 1-mod_id;
-  //ue_context_target_p->ue_context.handover_info->ueid_s  = m->source_rnti;
+  ue_context_target_p->ue_context.handover_info->x2_id = m->x2_id;
+  ue_context_target_p->ue_context.handover_info->assoc_id = m->target_assoc_id;
   memset (ue_context_target_p->ue_context.nh, 0, 32);
   ue_context_target_p->ue_context.nh_ncc = -1;
   memcpy (ue_context_target_p->ue_context.kenb, m->kenb, 32);
   ue_context_target_p->ue_context.kenb_ncc = m->kenb_ncc;
   ue_context_target_p->ue_context.security_capabilities.encryption_algorithms = m->security_capabilities.encryption_algorithms;
   ue_context_target_p->ue_context.security_capabilities.integrity_algorithms = m->security_capabilities.integrity_algorithms;
-  /*
+
   dec_rval = uper_decode(NULL,
-                         &asn_DEF_HandoverPreparationInformation,
+                         &asn_DEF_LTE_HandoverPreparationInformation,
                          (void **)&ho,
                          m->rrc_buffer,
                          m->rrc_buffer_size, 0, 0);
 
+ if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) {
+     xer_fprint(stdout, &asn_DEF_LTE_HandoverPreparationInformation, ho);
+ }
+
   if (dec_rval.code != RC_OK ||
-      ho->criticalExtensions.present != HandoverPreparationInformation__criticalExtensions_PR_c1 ||
-      ho->criticalExtensions.choice.c1.present != HandoverPreparationInformation__criticalExtensions__c1_PR_handoverPreparationInformation_r8) {
+      ho->criticalExtensions.present != LTE_HandoverPreparationInformation__criticalExtensions_PR_c1 ||
+      ho->criticalExtensions.choice.c1.present != LTE_HandoverPreparationInformation__criticalExtensions__c1_PR_handoverPreparationInformation_r8) {
     LOG_E(RRC, "could not decode Handover Preparation\n");
     abort();
   }
@@ -4132,13 +4047,13 @@ void rrc_eNB_process_handoverPreparationInformation(int mod_id, x2ap_handover_re
 
   if (ue_context_target_p->ue_context.UE_Capability) {
     LOG_I(RRC, "freeing old UE capabilities for UE %x\n", rnti);
-    ASN_STRUCT_FREE(asn_DEF_UE_EUTRA_Capability,
+    ASN_STRUCT_FREE(asn_DEF_LTE_UE_EUTRA_Capability,
                     ue_context_target_p->ue_context.UE_Capability);
     ue_context_target_p->ue_context.UE_Capability = 0;
   }
 
   dec_rval = uper_decode(NULL,
-                         &asn_DEF_UE_EUTRA_Capability,
+                         &asn_DEF_LTE_UE_EUTRA_Capability,
                          (void **)&ue_context_target_p->ue_context.UE_Capability,
                          ho_info->ue_RadioAccessCapabilityInfo.list.array[0]->ueCapabilityRAT_Container.buf,
                          ho_info->ue_RadioAccessCapabilityInfo.list.array[0]->ueCapabilityRAT_Container.size, 0, 0);
@@ -4146,16 +4061,16 @@ void rrc_eNB_process_handoverPreparationInformation(int mod_id, x2ap_handover_re
   ue_context_target_p->ue_context.UE_Capability_size = ho_info->ue_RadioAccessCapabilityInfo.list.array[0]->ueCapabilityRAT_Container.size;
 
   if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) {
-     xer_fprint(stdout, &asn_DEF_UE_EUTRA_Capability, ue_context_target_p->ue_context.UE_Capability);
+     xer_fprint(stdout, &asn_DEF_LTE_UE_EUTRA_Capability, ue_context_target_p->ue_context.UE_Capability);
   }
 
   if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
       LOG_E(RRC, "Failed to decode UE capabilities (%zu bytes)\n", dec_rval.consumed);
-      ASN_STRUCT_FREE(asn_DEF_UE_EUTRA_Capability,
+      ASN_STRUCT_FREE(asn_DEF_LTE_UE_EUTRA_Capability,
                       ue_context_target_p->ue_context.UE_Capability);
       ue_context_target_p->ue_context.UE_Capability = 0;
   }
-  */
+
   ue_context_target_p->ue_context.nb_of_e_rabs = m->nb_e_rabs_tobesetup;
   ue_context_target_p->ue_context.setup_e_rabs = m->nb_e_rabs_tobesetup;
   ue_context_target_p->ue_context.mme_ue_s1ap_id = m->mme_ue_s1ap_id;
@@ -4211,27 +4126,43 @@ void rrc_eNB_process_handoverCommand(
   ue_context->ue_context.handover_info->size = size;
 }
 
-#if 0
-//-----------------------------------------------------------------------------
-void
-rrc_eNB_process_handoverPreparationInformation(
-  const protocol_ctxt_t *const ctxt_pP,
-  rrc_eNB_ue_context_t           *const ue_context_pP
-)
-//-----------------------------------------------------------------------------
-{
-  T(T_ENB_RRC_HANDOVER_PREPARATION_INFORMATION, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
-    T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
-  LOG_I(RRC,
-        "[eNB %d] Frame %d : Logical Channel UL-DCCH, processing RRCHandoverPreparationInformation, sending LTE_RRCConnectionReconfiguration to UE %d \n",
-        ctxt_pP->module_id, ctxt_pP->frame, ue_context_pP->ue_context.rnti);
-  rrc_eNB_generate_RRCConnectionReconfiguration_handover(
-    ctxt_pP,
-    ue_context_pP,
-    NULL,
-    0);
+#if defined(ENABLE_USE_MME)
+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);
+  }
 }
-#endif
+#endif /* defined(ENABLE_USE_MME) */
 
 void
 check_handovers(
@@ -4239,7 +4170,6 @@ check_handovers(
 )
 //-----------------------------------------------------------------------------
 {
-  int                                 result;
   struct rrc_eNB_ue_context_s        *ue_context_p;
   RB_FOREACH(ue_context_p, rrc_ue_tree_s, &(RC.rrc[ctxt_pP->module_id]->rrc_ue_head)) {
     ctxt_pP->rnti  = ue_context_p->ue_id_rnti;
@@ -4256,19 +4186,15 @@ check_handovers(
         LOG_I(RRC,
               "[eNB %d] Frame %d : Logical Channel UL-DCCH, processing RRCHandoverPreparationInformation, sending RRCConnectionReconfiguration to UE %d \n",
               ctxt_pP->module_id, ctxt_pP->frame, ue_context_p->ue_context.rnti);
-        result = pdcp_data_req(ctxt_pP,
-                               SRB_FLAG_YES,
-                               DCCH,
-                               rrc_eNB_mui++,
-                               SDU_CONFIRM_NO,
-                               ue_context_p->ue_context.handover_info->size,
-                               ue_context_p->ue_context.handover_info->buf,
-                               PDCP_TRANSMISSION_MODE_CONTROL
-#if (LTE_RRC_VERSION >= MAKE_VERSION(14, 0, 0))
-                               ,NULL, NULL
-#endif
-                              );
-        AssertFatal(result == TRUE, "PDCP data request failed!\n");
+        rrc_data_req(
+          ctxt_pP,
+          DCCH,
+          rrc_eNB_mui++,
+          SDU_CONFIRM_NO,
+          ue_context_p->ue_context.handover_info->size,
+          ue_context_p->ue_context.handover_info->buf,
+          PDCP_TRANSMISSION_MODE_CONTROL);
+
         ue_context_p->ue_context.handover_info->state = HO_COMPLETE;
         LOG_I(RRC, "RRC Sends RRCConnectionReconfiguration to UE %d  at frame %d and subframe %d \n", ue_context_p->ue_context.rnti, ctxt_pP->frame,ctxt_pP->subframe);
       }
@@ -4282,10 +4208,10 @@ check_handovers(
         rrc_eNB_generate_HO_RRCConnectionReconfiguration(ctxt_pP, ue_context_p, X2AP_HANDOVER_REQ_ACK(msg).rrc_buffer,
             &X2AP_HANDOVER_REQ_ACK(msg).rrc_buffer_size);
         rrc_eNB_configure_rbs_handover(ue_context_p,ctxt_pP);
-        /* TODO: remove this hack */
-        //X2AP_HANDOVER_REQ_ACK(msg).target_mod_id = 1 - ctxt_pP->module_id;
-        X2AP_HANDOVER_REQ_ACK(msg).target_mod_id = ue_context_p->ue_context.handover_info->modid_t;
-        //X2AP_HANDOVER_REQ_ACK(msg).source_x2id = ue_context_p->ue_context.handover_info->source_x2id;
+
+        X2AP_HANDOVER_REQ_ACK(msg).rnti = ue_context_p->ue_context.rnti;
+        X2AP_HANDOVER_REQ_ACK(msg).x2_id_target = ue_context_p->ue_context.handover_info->x2_id;
+        X2AP_HANDOVER_REQ_ACK(msg).source_assoc_id = ue_context_p->ue_context.handover_info->assoc_id;
         /* Call admission control not implemented yet */
         X2AP_HANDOVER_REQ_ACK(msg).nb_e_rabs_tobesetup = ue_context_p->ue_context.setup_e_rabs;
 
@@ -4300,70 +4226,6 @@ check_handovers(
   }
 }
 
-#if 0
-//-----------------------------------------------------------------------------
-void
-check_handovers(
-  protocol_ctxt_t *const ctxt_pP
-)
-//-----------------------------------------------------------------------------
-{
-  int                                 result;
-  struct rrc_eNB_ue_context_s        *ue_context_p;
-  RB_FOREACH(ue_context_p, rrc_ue_tree_s, &RC.rrc[ctxt_pP->module_id]->rrc_ue_head) {
-    ctxt_pP->rnti  = ue_context_p->ue_id_rnti;
-
-    if (ue_context_p->ue_context.handover_info != NULL) {
-      if (ue_context_p->ue_context.handover_info->ho_prepare == 0xFF) {
-        LOG_D(RRC,
-              "[eNB %d] Frame %d: Incoming handover detected for new UE_idx %d (source eNB %d->target eNB %d) \n",
-              ctxt_pP->module_id,
-              ctxt_pP->frame,
-              ctxt_pP->rnti,
-              ctxt_pP->module_id,
-              ue_context_p->ue_context.handover_info->modid_t);
-        // source eNB generates LTE_RRCConnectionreconfiguration to prepare the HO
-        rrc_eNB_process_handoverPreparationInformation(
-          ctxt_pP,
-          ue_context_p);
-        ue_context_p->ue_context.handover_info->ho_prepare = 0xF1;
-      }
-
-      if (ue_context_p->ue_context.handover_info->ho_complete == 0xF1) {
-        LOG_D(RRC,
-              "[eNB %d] Frame %d: handover Command received for new UE_id  %x current eNB %d target eNB: %d \n",
-              ctxt_pP->module_id,
-              ctxt_pP->frame,
-              ctxt_pP->rnti,
-              ctxt_pP->module_id,
-              ue_context_p->ue_context.handover_info->modid_t);
-        //rrc_eNB_process_handoverPreparationInformation(enb_mod_idP,frameP,i);
-        result = pdcp_data_req(ctxt_pP,
-                               SRB_FLAG_YES,
-                               DCCH,
-                               rrc_eNB_mui++,
-                               SDU_CONFIRM_NO,
-                               ue_context_p->ue_context.handover_info->size,
-                               ue_context_p->ue_context.handover_info->buf,
-                               PDCP_TRANSMISSION_MODE_CONTROL
-#if (LTE_RRC_VERSION >= MAKE_VERSION(14, 0, 0))
-                               ,NULL, NULL
-#endif
-                              );
-
-        //AssertFatal(result == TRUE, "PDCP data request failed!\n");
-        if(result != TRUE) {
-          LOG_I(RRC, "PDCP data request failed!\n");
-          return;
-        }
-
-        ue_context_p->ue_context.handover_info->ho_complete = 0xF2;
-      }
-    }
-  }
-}
-#endif
-
 void
 rrc_eNB_generate_HO_RRCConnectionReconfiguration(const protocol_ctxt_t *const ctxt_pP,
     rrc_eNB_ue_context_t  *const ue_context_pP,
@@ -5306,6 +5168,21 @@ rrc_eNB_configure_rbs_handover(struct rrc_eNB_ue_context_s *ue_context_p, protoc
                           , 0, 0
 #endif
                          );
+#if defined(ENABLE_USE_MME)
+  rrc_eNB_process_security (
+    ctxt_pP,
+    ue_context_p,
+    &ue_context_p->ue_context.security_capabilities);
+  process_eNB_security_key (
+    ctxt_pP,
+    ue_context_p,
+    ue_context_p->ue_context.kenb);
+  rrc_pdcp_config_security(
+    ctxt_pP,
+    ue_context_p,
+    FALSE);
+#endif
+
   // Add a new user (called during the HO procedure)
   LOG_I(RRC, "rrc_eNB_target_add_ue_handover module_id %d rnti %d\n", ctxt_pP->module_id, ctxt_pP->rnti);
   // Configure MAC for the target
@@ -7709,9 +7586,11 @@ rrc_eNB_decode_dcch(
 
         if (EPC_MODE_ENABLED) {
           if (EPC_MODE_ENABLED == 1) {
+#if defined(ENABLE_USE_MME)
             rrc_eNB_send_S1AP_UE_CAPABILITIES_IND(ctxt_pP,
                                                   ue_context_p,
                                                   ul_dcch_msg);
+#endif
           }
         } else {
           ue_context_p->ue_context.nb_of_e_rabs = 1;
@@ -7759,9 +7638,11 @@ rrc_eNB_decode_dcch(
           sdu_sizeP);
 
         if (EPC_MODE_ENABLED == 1) {
+#if defined(ENABLE_USE_MME)
           rrc_eNB_send_S1AP_UPLINK_NAS(ctxt_pP,
                                        ue_context_p,
                                        ul_dcch_msg);
+#endif
         }
 
         break;
@@ -7898,100 +7779,350 @@ void rrc_enb_init(void) {
 }
 
 //-----------------------------------------------------------------------------
-void *rrc_enb_process_itti_msg(void *notUsed) {
-  MessageDef                         *msg_p;
-  const char                         *msg_name_p;
-  instance_t                          instance;
-  int                                 result;
-  SRB_INFO                           *srb_info_p;
-  int                                 CC_id;
-  protocol_ctxt_t                     ctxt;
-  // Wait for a message
-  itti_receive_msg(TASK_RRC_ENB, &msg_p);
-  msg_name_p = ITTI_MSG_NAME(msg_p);
-  instance = ITTI_MSG_INSTANCE(msg_p);
-  LOG_I(RRC,"Received message %s\n",msg_name_p);
-
-  switch (ITTI_MSG_ID(msg_p)) {
-    case TERMINATE_MESSAGE:
-      LOG_W(RRC, " *** Exiting RRC thread\n");
-      itti_exit_task();
-      break;
+void rrc_subframe_process(protocol_ctxt_t *const ctxt_pP, const int CC_id)
+{
+  int32_t current_timestamp_ms = 0;
+  int32_t ref_timestamp_ms = 0;
+  struct timeval ts;
+  struct rrc_eNB_ue_context_s *ue_context_p = NULL;
+  struct rrc_eNB_ue_context_s *ue_to_be_removed = NULL;
+#ifdef LOCALIZATION
+  double estimated_distance = 0;
+  protocol_ctxt_t ctxt;
+#endif
+  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_RRC_RX_TX, VCD_FUNCTION_IN);
+  check_handovers(ctxt_pP); // counter, get the value and aggregate
+  // check for UL failure or for UE to be released
+  RB_FOREACH(ue_context_p, rrc_ue_tree_s, &(RC.rrc[ctxt_pP->module_id]->rrc_ue_head)) {
+    ctxt_pP->rnti = ue_context_p->ue_id_rnti;
 
-    case MESSAGE_TEST:
-      LOG_I(RRC, "[eNB %d] Received %s\n", instance, msg_name_p);
-      break;
+    if ((ctxt_pP->frame == 0) && (ctxt_pP->subframe == 0)) {
+      if (ue_context_p->ue_context.Initialue_identity_s_TMSI.presence == TRUE) {
+        LOG_I(RRC, "UE rnti %x: S-TMSI %x failure timer %d/8\n",
+              ue_context_p->ue_context.rnti,
+              ue_context_p->ue_context.Initialue_identity_s_TMSI.m_tmsi,
+              ue_context_p->ue_context.ul_failure_timer);
+      } else {
+        LOG_I(RRC, "UE rnti %x failure timer %d/8\n",
+              ue_context_p->ue_context.rnti,
+              ue_context_p->ue_context.ul_failure_timer);
+      }
+    }
 
-    /* Messages from MAC */
-    case RRC_MAC_CCCH_DATA_IND:
-      PROTOCOL_CTXT_SET_BY_INSTANCE(&ctxt,
-                                    instance,
-                                    ENB_FLAG_YES,
-                                    RRC_MAC_CCCH_DATA_IND(msg_p).rnti,
-                                    msg_p->ittiMsgHeader.lte_time.frame,
-                                    msg_p->ittiMsgHeader.lte_time.slot);
-      LOG_I(RRC, PROTOCOL_RRC_CTXT_UE_FMT" Received %s\n",
-            PROTOCOL_RRC_CTXT_UE_ARGS(&ctxt),
-            msg_name_p);
-      CC_id = RRC_MAC_CCCH_DATA_IND(msg_p).CC_id;
-      srb_info_p = &RC.rrc[instance]->carrier[CC_id].Srb0;
-      LOG_I(RRC,"Decoding CCCH : inst %d, CC_id %d, ctxt %p, sib_info_p->Rx_buffer.payload_size %d\n",
-            instance,CC_id,&ctxt, RRC_MAC_CCCH_DATA_IND(msg_p).sdu_size);
+    if (ue_context_p->ue_context.ul_failure_timer > 0) {
+      ue_context_p->ue_context.ul_failure_timer++;
 
-      if (RRC_MAC_CCCH_DATA_IND(msg_p).sdu_size >= RRC_BUFFER_SIZE_MAX) {
-        LOG_I(RRC, "CCCH message has size %d > %d\n",RRC_MAC_CCCH_DATA_IND(msg_p).sdu_size,RRC_BUFFER_SIZE_MAX);
-        break;
+      if (ue_context_p->ue_context.ul_failure_timer >= 20000) {
+        // remove UE after 20 seconds after MAC (or else) has indicated UL failure
+        LOG_I(RRC, "Removing UE %x instance, because of uplink failure timer timeout\n",
+              ue_context_p->ue_context.rnti);
+        ue_to_be_removed = ue_context_p;
+        break; // break RB_FOREACH
       }
+    }
 
-      memcpy(srb_info_p->Rx_buffer.Payload,
-             RRC_MAC_CCCH_DATA_IND(msg_p).sdu,
-             RRC_MAC_CCCH_DATA_IND(msg_p).sdu_size);
-      srb_info_p->Rx_buffer.payload_size = RRC_MAC_CCCH_DATA_IND(msg_p).sdu_size;
-      rrc_eNB_decode_ccch(&ctxt, srb_info_p, CC_id);
-      break;
+    if (ue_context_p->ue_context.ue_release_timer_s1 > 0) {
+      ue_context_p->ue_context.ue_release_timer_s1++;
 
-    /* Messages from PDCP */
-    case RRC_DCCH_DATA_IND:
-      PROTOCOL_CTXT_SET_BY_INSTANCE(&ctxt,
-                                    instance,
-                                    ENB_FLAG_YES,
-                                    RRC_DCCH_DATA_IND(msg_p).rnti,
-                                    msg_p->ittiMsgHeader.lte_time.frame,
-                                    msg_p->ittiMsgHeader.lte_time.slot);
-      LOG_I(RRC, PROTOCOL_RRC_CTXT_UE_FMT" Received on DCCH %d %s\n",
-            PROTOCOL_RRC_CTXT_UE_ARGS(&ctxt),
-            RRC_DCCH_DATA_IND(msg_p).dcch_index,
-            msg_name_p);
-      rrc_eNB_decode_dcch(&ctxt,
-                          RRC_DCCH_DATA_IND(msg_p).dcch_index,
-                          RRC_DCCH_DATA_IND(msg_p).sdu_p,
-                          RRC_DCCH_DATA_IND(msg_p).sdu_size);
-      // Message buffer has been processed, free it now.
-      result = itti_free(ITTI_MSG_ORIGIN_ID(msg_p), RRC_DCCH_DATA_IND(msg_p).sdu_p);
+      if (ue_context_p->ue_context.ue_release_timer_s1 >= ue_context_p->ue_context.ue_release_timer_thres_s1) {
+        LOG_I(RRC, "Removing UE %x instance, because of UE_CONTEXT_RELEASE_COMMAND not received after %d ms from sending request\n",
+              ue_context_p->ue_context.rnti,
+              ue_context_p->ue_context.ue_release_timer_thres_s1);
 
-      if (result != EXIT_SUCCESS) {
-        LOG_I(RRC, "Failed to free memory (%d)!\n",result);
-        break;
-      }
+        if (EPC_MODE_ENABLED)
+          rrc_eNB_generate_RRCConnectionRelease(ctxt_pP, ue_context_p);
+        else
+          ue_to_be_removed = ue_context_p;
 
-      break;
+        ue_context_p->ue_context.ue_release_timer_s1 = 0;
+        break; // break RB_FOREACH
+      } // end if timer_s1 timeout
+    } // end if timer_s1 > 0 (S1 UE_CONTEXT_RELEASE_REQ ongoing)
 
-    /* Messages from S1AP */
-    case S1AP_DOWNLINK_NAS:
-      rrc_eNB_process_S1AP_DOWNLINK_NAS(msg_p, msg_name_p, instance, &rrc_eNB_mui);
-      break;
+    if (ue_context_p->ue_context.ue_release_timer_rrc > 0) {
+      ue_context_p->ue_context.ue_release_timer_rrc++;
 
-    case S1AP_INITIAL_CONTEXT_SETUP_REQ:
-      rrc_eNB_process_S1AP_INITIAL_CONTEXT_SETUP_REQ(msg_p, msg_name_p, instance);
-      break;
+      if (ue_context_p->ue_context.ue_release_timer_rrc >= ue_context_p->ue_context.ue_release_timer_thres_rrc) {
+        LOG_I(RRC, "Removing UE %x instance after UE_CONTEXT_RELEASE_Complete (ue_release_timer_rrc timeout)\n",
+              ue_context_p->ue_context.rnti);
+        ue_context_p->ue_context.ue_release_timer_rrc = 0;
+        ue_to_be_removed = ue_context_p;
+        break; // break RB_FOREACH
+      }
+    }
 
-    case S1AP_UE_CTXT_MODIFICATION_REQ:
-      rrc_eNB_process_S1AP_UE_CTXT_MODIFICATION_REQ(msg_p, msg_name_p, instance);
-      break;
+#if defined(ENABLE_USE_MME)
+    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 ?)
+      }
+    }
+#endif
 
-    case S1AP_PAGING_IND:
-      LOG_D(RRC, "[eNB %d] Received Paging message from S1AP: %s\n", instance, msg_name_p);
-      rrc_eNB_process_PAGING_IND(msg_p, msg_name_p, instance);
+    pthread_mutex_lock(&rrc_release_freelist);
+
+    if (rrc_release_info.num_UEs > 0) {
+      uint16_t release_total = 0;
+
+      for (uint16_t release_num = 0; release_num < NUMBER_OF_UE_MAX; release_num++) {
+        if (rrc_release_info.RRC_release_ctrl[release_num].flag > 0) {
+          release_total++;
+        }
+
+        if ((rrc_release_info.RRC_release_ctrl[release_num].flag > 2) &&
+            (rrc_release_info.RRC_release_ctrl[release_num].rnti == ue_context_p->ue_context.rnti)) {
+          ue_context_p->ue_context.ue_release_timer_rrc = 1;
+          ue_context_p->ue_context.ue_release_timer_thres_rrc = 100;
+
+          if (EPC_MODE_ENABLED) {
+            int e_rab = 0;
+            MessageDef *msg_complete_p = NULL;
+            MessageDef *msg_delete_tunnels_p = NULL;
+            uint32_t eNB_ue_s1ap_id = ue_context_p->ue_context.eNB_ue_s1ap_id;
+
+            if (rrc_release_info.RRC_release_ctrl[release_num].flag == 4) { // if timer_s1 == 0
+              MSC_LOG_TX_MESSAGE(MSC_RRC_ENB, MSC_S1AP_ENB, NULL, 0,
+                                 "0 S1AP_UE_CONTEXT_RELEASE_COMPLETE eNB_ue_s1ap_id 0x%06"PRIX32" ",
+                                 eNB_ue_s1ap_id);
+              msg_complete_p = itti_alloc_new_message(TASK_RRC_ENB, S1AP_UE_CONTEXT_RELEASE_COMPLETE);
+              S1AP_UE_CONTEXT_RELEASE_COMPLETE(msg_complete_p).eNB_ue_s1ap_id = eNB_ue_s1ap_id;
+              itti_send_msg_to_task(TASK_S1AP, ctxt_pP->module_id, msg_complete_p);
+            }
+
+            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)));
+            // do not wait response
+            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);
+            }
+          } /* EPC_MODE_ENABLED */
+
+          rrc_release_info.RRC_release_ctrl[release_num].flag = 0;
+          rrc_release_info.num_UEs--;
+          break; // break for (release_num)
+        } // end if ((rrc_release_info.RRC_release_ctrl[release_num].flag > 2) && ...
+
+        if (release_total >= rrc_release_info.num_UEs) {
+          break; // break for (release_num)
+        }
+      } // end for (release_num)
+    } // end if (rrc_release_info.num_UEs > 0)
+
+    pthread_mutex_unlock(&rrc_release_freelist);
+
+    if ((ue_context_p->ue_context.ue_rrc_inactivity_timer > 0) && (RC.rrc[ctxt_pP->module_id]->configuration.rrc_inactivity_timer_thres > 0)) {
+      ue_context_p->ue_context.ue_rrc_inactivity_timer++; // (un)comment this line to (de)activate the RRC inactivity timer
+
+      if (ue_context_p->ue_context.ue_rrc_inactivity_timer >= RC.rrc[ctxt_pP->module_id]->configuration.rrc_inactivity_timer_thres) {
+        LOG_I(RRC, "Removing UE %x instance because of rrc_inactivity_timer timeout\n",
+              ue_context_p->ue_context.rnti);
+        ue_to_be_removed = ue_context_p;
+        break; // break RB_FOREACH
+      }
+    }
+
+    if (ue_context_p->ue_context.ue_reestablishment_timer > 0) {
+      ue_context_p->ue_context.ue_reestablishment_timer++;
+
+      if (ue_context_p->ue_context.ue_reestablishment_timer >= ue_context_p->ue_context.ue_reestablishment_timer_thres) {
+        LOG_I(RRC, "Removing UE %x instance because of reestablishment_timer timeout\n",
+              ue_context_p->ue_context.rnti);
+        ue_context_p->ue_context.ul_failure_timer = 20000; // lead to send S1 UE_CONTEXT_RELEASE_REQ
+        ue_to_be_removed = ue_context_p;
+        ue_context_p->ue_context.ue_reestablishment_timer = 0;
+        break; // break RB_FOREACH
+      }
+    }
+
+    if (ue_context_p->ue_context.ue_release_timer > 0) {
+      ue_context_p->ue_context.ue_release_timer++;
+
+      if (ue_context_p->ue_context.ue_release_timer >= ue_context_p->ue_context.ue_release_timer_thres) {
+        LOG_I(RRC, "Removing UE %x instance because of RRC Connection Setup timer timeout\n",
+              ue_context_p->ue_context.rnti);
+        /*
+        * TODO: Naming problem here: ue_release_timer seems to have been used when RRC Connection Release was sent.
+        * It is no more the case.
+        * The timer should be renamed.
+        */
+        ue_to_be_removed = ue_context_p;
+        ue_context_p->ue_context.ue_release_timer = 0;
+        break; // break RB_FOREACH
+      }
+    }
+  } // end RB_FOREACH
+
+  if (ue_to_be_removed) {
+    if ((ue_to_be_removed->ue_context.ul_failure_timer >= 20000) ||
+        ((ue_to_be_removed->ue_context.ue_rrc_inactivity_timer >= RC.rrc[ctxt_pP->module_id]->configuration.rrc_inactivity_timer_thres) &&
+         (RC.rrc[ctxt_pP->module_id]->configuration.rrc_inactivity_timer_thres > 0))) {
+      ue_to_be_removed->ue_context.ue_release_timer_s1 = 1;
+      ue_to_be_removed->ue_context.ue_release_timer_thres_s1 = 100;
+      ue_to_be_removed->ue_context.ue_release_timer = 0;
+      ue_to_be_removed->ue_context.ue_reestablishment_timer = 0;
+    }
+
+    rrc_eNB_free_UE(ctxt_pP->module_id, ue_to_be_removed);
+
+    if (ue_to_be_removed->ue_context.ul_failure_timer >= 20000) {
+      ue_to_be_removed->ue_context.ul_failure_timer = 0;
+    }
+
+    if ((ue_to_be_removed->ue_context.ue_rrc_inactivity_timer >= RC.rrc[ctxt_pP->module_id]->configuration.rrc_inactivity_timer_thres) &&
+        (RC.rrc[ctxt_pP->module_id]->configuration.rrc_inactivity_timer_thres > 0)) {
+      ue_to_be_removed->ue_context.ue_rrc_inactivity_timer = 0; //reset timer after S1 command UE context release request is sent
+    }
+  }
+
+#ifdef RRC_LOCALIZATION
+  /* for the localization, only primary CC_id might be relevant*/
+  gettimeofday(&ts, NULL);
+  current_timestamp_ms = ts.tv_sec * 1000 + ts.tv_usec / 1000;
+  ref_timestamp_ms = RC.rrc[ctxt_pP->module_id]->reference_timestamp_ms;
+  RB_FOREACH(ue_context_p, rrc_ue_tree_s, &(RC.rrc[ctxt_pP->module_id]->rrc_ue_head)) {
+    ctxt = *ctxt_pP;
+    ctxt.rnti = ue_context_p->ue_context.rnti;
+    estimated_distance = rrc_get_estimated_ue_distance(&ctxt, CC_id, RC.rrc[ctxt_pP->module_id]->loc_type);
+
+    if ((current_timestamp_ms - ref_timestamp_ms > RC.rrc[ctxt_pP->module_id]->aggregation_period_ms) &&
+        estimated_distance != -1) {
+      LOG_D(LOCALIZE, "RRC [UE/id %d -> eNB/id %d] timestamp %d frame %d estimated r = %f\n",
+            ctxt.rnti,
+            ctxt_pP->module_id,
+            current_timestamp_ms,
+            ctxt_pP->frame,
+            estimated_distance);
+      LOG_D(LOCALIZE, "RRC status %d\n",
+            ue_context_p->ue_context.Status);
+      push_front(&RC.rrc[ctxt_pP->module_id]->loc_list, estimated_distance);
+      RC.rrc[ctxt_pP->module_id]->reference_timestamp_ms = current_timestamp_ms;
+    } // end if
+  } // end RB_FOREACH
+#endif
+  (void)ts; /* remove gcc warning "unused variable" */
+  (void)ref_timestamp_ms; /* remove gcc warning "unused variable" */
+  (void)current_timestamp_ms; /* remove gcc warning "unused variable" */
+  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_RRC_RX_TX, VCD_FUNCTION_OUT);
+}
+
+//-----------------------------------------------------------------------------
+void *rrc_enb_process_itti_msg(void *notUsed) {
+  MessageDef                         *msg_p;
+  const char                         *msg_name_p;
+  instance_t                          instance;
+  int                                 result;
+  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);
+  instance = ITTI_MSG_INSTANCE(msg_p);
+  /* RRC_SUBFRAME_PROCESS is sent every subframe, do not log it */
+  if (ITTI_MSG_ID(msg_p) != RRC_SUBFRAME_PROCESS)
+    LOG_I(RRC,"Received message %s\n",msg_name_p);
+
+  switch (ITTI_MSG_ID(msg_p)) {
+    case TERMINATE_MESSAGE:
+      LOG_W(RRC, " *** Exiting RRC thread\n");
+      itti_exit_task();
+      break;
+
+    case MESSAGE_TEST:
+      LOG_I(RRC, "[eNB %d] Received %s\n", instance, msg_name_p);
+      break;
+
+    /* Messages from MAC */
+    case RRC_MAC_CCCH_DATA_IND:
+      PROTOCOL_CTXT_SET_BY_INSTANCE(&ctxt,
+                                    instance,
+                                    ENB_FLAG_YES,
+                                    RRC_MAC_CCCH_DATA_IND(msg_p).rnti,
+                                    msg_p->ittiMsgHeader.lte_time.frame,
+                                    msg_p->ittiMsgHeader.lte_time.slot);
+      LOG_I(RRC, PROTOCOL_RRC_CTXT_UE_FMT" Received %s\n",
+            PROTOCOL_RRC_CTXT_UE_ARGS(&ctxt),
+            msg_name_p);
+      CC_id = RRC_MAC_CCCH_DATA_IND(msg_p).CC_id;
+      srb_info_p = &RC.rrc[instance]->carrier[CC_id].Srb0;
+      LOG_I(RRC,"Decoding CCCH : inst %d, CC_id %d, ctxt %p, sib_info_p->Rx_buffer.payload_size %d\n",
+            instance,CC_id,&ctxt, RRC_MAC_CCCH_DATA_IND(msg_p).sdu_size);
+
+      if (RRC_MAC_CCCH_DATA_IND(msg_p).sdu_size >= RRC_BUFFER_SIZE_MAX) {
+        LOG_I(RRC, "CCCH message has size %d > %d\n",RRC_MAC_CCCH_DATA_IND(msg_p).sdu_size,RRC_BUFFER_SIZE_MAX);
+        break;
+      }
+
+      memcpy(srb_info_p->Rx_buffer.Payload,
+             RRC_MAC_CCCH_DATA_IND(msg_p).sdu,
+             RRC_MAC_CCCH_DATA_IND(msg_p).sdu_size);
+      srb_info_p->Rx_buffer.payload_size = RRC_MAC_CCCH_DATA_IND(msg_p).sdu_size;
+      rrc_eNB_decode_ccch(&ctxt, srb_info_p, CC_id);
+      break;
+
+    /* Messages from PDCP */
+    case RRC_DCCH_DATA_IND:
+      PROTOCOL_CTXT_SET_BY_INSTANCE(&ctxt,
+                                    instance,
+                                    ENB_FLAG_YES,
+                                    RRC_DCCH_DATA_IND(msg_p).rnti,
+                                    msg_p->ittiMsgHeader.lte_time.frame,
+                                    msg_p->ittiMsgHeader.lte_time.slot);
+      LOG_I(RRC, PROTOCOL_RRC_CTXT_UE_FMT" Received on DCCH %d %s\n",
+            PROTOCOL_RRC_CTXT_UE_ARGS(&ctxt),
+            RRC_DCCH_DATA_IND(msg_p).dcch_index,
+            msg_name_p);
+      rrc_eNB_decode_dcch(&ctxt,
+                          RRC_DCCH_DATA_IND(msg_p).dcch_index,
+                          RRC_DCCH_DATA_IND(msg_p).sdu_p,
+                          RRC_DCCH_DATA_IND(msg_p).sdu_size);
+      // Message buffer has been processed, free it now.
+      result = itti_free(ITTI_MSG_ORIGIN_ID(msg_p), RRC_DCCH_DATA_IND(msg_p).sdu_p);
+
+      if (result != EXIT_SUCCESS) {
+        LOG_I(RRC, "Failed to free memory (%d)!\n",result);
+        break;
+      }
+
+      break;
+
+    /* Messages from S1AP */
+    case S1AP_DOWNLINK_NAS:
+      rrc_eNB_process_S1AP_DOWNLINK_NAS(msg_p, msg_name_p, instance, &rrc_eNB_mui);
+      break;
+
+    case S1AP_INITIAL_CONTEXT_SETUP_REQ:
+      rrc_eNB_process_S1AP_INITIAL_CONTEXT_SETUP_REQ(msg_p, msg_name_p, instance);
+      break;
+
+    case S1AP_UE_CTXT_MODIFICATION_REQ:
+      rrc_eNB_process_S1AP_UE_CTXT_MODIFICATION_REQ(msg_p, msg_name_p, instance);
+      break;
+
+    case S1AP_PAGING_IND:
+      LOG_D(RRC, "[eNB %d] Received Paging message from S1AP: %s\n", instance, msg_name_p);
+      rrc_eNB_process_PAGING_IND(msg_p, msg_name_p, instance);
       break;
 
     case S1AP_E_RAB_SETUP_REQ:
@@ -8015,18 +8146,17 @@ void *rrc_enb_process_itti_msg(void *notUsed) {
       rrc_eNB_process_S1AP_UE_CONTEXT_RELEASE_COMMAND(msg_p, msg_name_p, instance);
       break;
 
-    case GTPV1U_ENB_DELETE_TUNNEL_RESP:
+    case GTPV1U_ENB_DELETE_TUNNEL_RESP: {
+      rrc_eNB_ue_context_t *ue = rrc_eNB_get_ue_context(RC.rrc[instance], GTPV1U_ENB_DELETE_TUNNEL_RESP(msg_p).rnti);
 
-      /* Nothing to do. Apparently everything is done in S1AP processing */
-      //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 =
-          rrc_eNB_get_ue_context(RC.rrc[instance], GTPV1U_ENB_DELETE_TUNNEL_RESP(msg_p).rnti)->ue_context.ue_release_timer_thres_rrc;
+      if (ue != NULL
+          && ue->ue_context.ue_release_timer_rrc > 0
+          && (ue->ue_context.handover_info == NULL || ue->ue_context.handover_info->state != HO_RELEASE)) {
+        ue->ue_context.ue_release_timer_rrc = ue->ue_context.ue_release_timer_thres_rrc;
       }
 
       break;
+    }
 
     case S1AP_PATH_SWITCH_REQ_ACK:
       LOG_I(RRC, "[eNB %d] received path switch ack %s\n", instance, msg_name_p);
@@ -8034,16 +8164,20 @@ void *rrc_enb_process_itti_msg(void *notUsed) {
       break;
 
     case X2AP_HANDOVER_REQ:
-      LOG_I(RRC, "[eNB %d] target eNB Receives X2 HO Req %s at frame %d subframe %d\n", instance, msg_name_p,
-            ctxt.frame, ctxt.subframe);
+      LOG_I(RRC, "[eNB %d] target eNB Receives X2 HO Req %s\n", instance, msg_name_p);
       rrc_eNB_process_handoverPreparationInformation(instance, &X2AP_HANDOVER_REQ(msg_p));
       break;
 
     case X2AP_HANDOVER_REQ_ACK: {
       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 HO ACK %s at frame %d subframe %d \n", instance, msg_name_p,
-            ctxt.frame,ctxt.subframe);
+      ue_context_p = rrc_eNB_get_ue_context(RC.rrc[instance], X2AP_HANDOVER_REQ_ACK(msg_p).rnti);
+      if (ue_context_p == NULL) {
+        /* is it possible? */
+        LOG_E(RRC, "could not find UE (rnti %x) while processing X2AP_HANDOVER_REQ_ACK\n",
+              X2AP_HANDOVER_REQ_ACK(msg_p).rnti);
+        exit(1);
+      }
+      LOG_I(RRC, "[eNB %d] source eNB receives the X2 HO ACK %s\n", instance, msg_name_p);
       DevAssert(ue_context_p != NULL);
 
       if (ue_context_p->ue_context.handover_info->state != HO_REQUEST) abort();
@@ -8053,12 +8187,28 @@ 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], X2AP_UE_CONTEXT_RELEASE(msg_p).rnti);
+      LOG_I(RRC, "[eNB %d] source eNB receives the X2 UE CONTEXT RELEASE %s\n", instance, msg_name_p);
+      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));
       openair_rrc_eNB_configuration(ENB_INSTANCE_TO_MODULE_ID(instance), &RRC_CONFIGURATION_REQ(msg_p));
       break;
 
+    case RRC_SUBFRAME_PROCESS:
+      rrc_subframe_process(&RRC_SUBFRAME_PROCESS(msg_p).ctxt, RRC_SUBFRAME_PROCESS(msg_p).CC_id);
+      break;
+
     default:
       LOG_E(RRC, "[eNB %d] Received unexpected message %s\n", instance, msg_name_p);
       break;
@@ -8434,236 +8584,11 @@ rrc_rx_tx(
 )
 //-----------------------------------------------------------------------------
 {
-  int32_t current_timestamp_ms = 0;
-  int32_t ref_timestamp_ms = 0;
-  struct timeval ts;
-  struct rrc_eNB_ue_context_s *ue_context_p = NULL;
-  struct rrc_eNB_ue_context_s *ue_to_be_removed = NULL;
-#ifdef LOCALIZATION
-  double estimated_distance = 0;
-  protocol_ctxt_t ctxt;
-#endif
-  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_RRC_RX_TX, VCD_FUNCTION_IN);
-  check_handovers(ctxt_pP); // counter, get the value and aggregate
-  // check for UL failure or for UE to be released
-  RB_FOREACH(ue_context_p, rrc_ue_tree_s, &(RC.rrc[ctxt_pP->module_id]->rrc_ue_head)) {
-    ctxt_pP->rnti = ue_context_p->ue_id_rnti;
-
-    if ((ctxt_pP->frame == 0) && (ctxt_pP->subframe == 0)) {
-      if (ue_context_p->ue_context.Initialue_identity_s_TMSI.presence == TRUE) {
-        LOG_I(RRC, "UE rnti %x: S-TMSI %x failure timer %d/8\n",
-              ue_context_p->ue_context.rnti,
-              ue_context_p->ue_context.Initialue_identity_s_TMSI.m_tmsi,
-              ue_context_p->ue_context.ul_failure_timer);
-      } else {
-        LOG_I(RRC, "UE rnti %x failure timer %d/8\n",
-              ue_context_p->ue_context.rnti,
-              ue_context_p->ue_context.ul_failure_timer);
-      }
-    }
-
-    if (ue_context_p->ue_context.ul_failure_timer > 0) {
-      ue_context_p->ue_context.ul_failure_timer++;
-
-      if (ue_context_p->ue_context.ul_failure_timer >= 20000) {
-        // remove UE after 20 seconds after MAC (or else) has indicated UL failure
-        LOG_I(RRC, "Removing UE %x instance, because of uplink failure timer timeout\n",
-              ue_context_p->ue_context.rnti);
-        ue_to_be_removed = ue_context_p;
-        break; // break RB_FOREACH
-      }
-    }
-
-    if (ue_context_p->ue_context.ue_release_timer_s1 > 0) {
-      ue_context_p->ue_context.ue_release_timer_s1++;
-
-      if (ue_context_p->ue_context.ue_release_timer_s1 >= ue_context_p->ue_context.ue_release_timer_thres_s1) {
-        LOG_I(RRC, "Removing UE %x instance, because of UE_CONTEXT_RELEASE_COMMAND not received after %d ms from sending request\n",
-              ue_context_p->ue_context.rnti,
-              ue_context_p->ue_context.ue_release_timer_thres_s1);
-
-        if (EPC_MODE_ENABLED)
-          rrc_eNB_generate_RRCConnectionRelease(ctxt_pP, ue_context_p);
-        else
-          ue_to_be_removed = ue_context_p;
-
-        ue_context_p->ue_context.ue_release_timer_s1 = 0;
-        break; // break RB_FOREACH
-      } // end if timer_s1 timeout
-    } // end if timer_s1 > 0 (S1 UE_CONTEXT_RELEASE_REQ ongoing)
-
-    if (ue_context_p->ue_context.ue_release_timer_rrc > 0) {
-      ue_context_p->ue_context.ue_release_timer_rrc++;
-
-      if (ue_context_p->ue_context.ue_release_timer_rrc >= ue_context_p->ue_context.ue_release_timer_thres_rrc) {
-        LOG_I(RRC, "Removing UE %x instance after UE_CONTEXT_RELEASE_Complete (ue_release_timer_rrc timeout)\n",
-              ue_context_p->ue_context.rnti);
-        ue_context_p->ue_context.ue_release_timer_rrc = 0;
-        ue_to_be_removed = ue_context_p;
-        break; // break RB_FOREACH
-      }
-    }
-
-    pthread_mutex_lock(&rrc_release_freelist);
-
-    if (rrc_release_info.num_UEs > 0) {
-      uint16_t release_total = 0;
-
-      for (uint16_t release_num = 0; release_num < NUMBER_OF_UE_MAX; release_num++) {
-        if (rrc_release_info.RRC_release_ctrl[release_num].flag > 0) {
-          release_total++;
-        }
-
-        if ((rrc_release_info.RRC_release_ctrl[release_num].flag > 2) &&
-            (rrc_release_info.RRC_release_ctrl[release_num].rnti == ue_context_p->ue_context.rnti)) {
-          ue_context_p->ue_context.ue_release_timer_rrc = 1;
-          ue_context_p->ue_context.ue_release_timer_thres_rrc = 100;
-
-          if (EPC_MODE_ENABLED) {
-            int e_rab = 0;
-            MessageDef *msg_complete_p = NULL;
-            MessageDef *msg_delete_tunnels_p = NULL;
-            uint32_t eNB_ue_s1ap_id = ue_context_p->ue_context.eNB_ue_s1ap_id;
-
-            if (rrc_release_info.RRC_release_ctrl[release_num].flag == 4) { // if timer_s1 == 0
-              MSC_LOG_TX_MESSAGE(MSC_RRC_ENB, MSC_S1AP_ENB, NULL, 0,
-                                 "0 S1AP_UE_CONTEXT_RELEASE_COMPLETE eNB_ue_s1ap_id 0x%06"PRIX32" ",
-                                 eNB_ue_s1ap_id);
-              msg_complete_p = itti_alloc_new_message(TASK_RRC_ENB, S1AP_UE_CONTEXT_RELEASE_COMPLETE);
-              S1AP_UE_CONTEXT_RELEASE_COMPLETE(msg_complete_p).eNB_ue_s1ap_id = eNB_ue_s1ap_id;
-              itti_send_msg_to_task(TASK_S1AP, ctxt_pP->module_id, msg_complete_p);
-            }
-
-            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)));
-            // do not wait response
-            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);
-            }
-          } /* EPC_MODE_ENABLED */
-
-          rrc_release_info.RRC_release_ctrl[release_num].flag = 0;
-          rrc_release_info.num_UEs--;
-          break; // break for (release_num)
-        } // end if ((rrc_release_info.RRC_release_ctrl[release_num].flag > 2) && ...
-
-        if (release_total >= rrc_release_info.num_UEs) {
-          break; // break for (release_num)
-        }
-      } // end for (release_num)
-    } // end if (rrc_release_info.num_UEs > 0)
-
-    pthread_mutex_unlock(&rrc_release_freelist);
-
-    if ((ue_context_p->ue_context.ue_rrc_inactivity_timer > 0) && (RC.rrc[ctxt_pP->module_id]->configuration.rrc_inactivity_timer_thres > 0)) {
-      ue_context_p->ue_context.ue_rrc_inactivity_timer++; // (un)comment this line to (de)activate the RRC inactivity timer
-
-      if (ue_context_p->ue_context.ue_rrc_inactivity_timer >= RC.rrc[ctxt_pP->module_id]->configuration.rrc_inactivity_timer_thres) {
-        LOG_I(RRC, "Removing UE %x instance because of rrc_inactivity_timer timeout\n",
-              ue_context_p->ue_context.rnti);
-        ue_to_be_removed = ue_context_p;
-        break; // break RB_FOREACH
-      }
-    }
-
-    if (ue_context_p->ue_context.ue_reestablishment_timer > 0) {
-      ue_context_p->ue_context.ue_reestablishment_timer++;
-
-      if (ue_context_p->ue_context.ue_reestablishment_timer >= ue_context_p->ue_context.ue_reestablishment_timer_thres) {
-        LOG_I(RRC, "Removing UE %x instance because of reestablishment_timer timeout\n",
-              ue_context_p->ue_context.rnti);
-        ue_context_p->ue_context.ul_failure_timer = 20000; // lead to send S1 UE_CONTEXT_RELEASE_REQ
-        ue_to_be_removed = ue_context_p;
-        ue_context_p->ue_context.ue_reestablishment_timer = 0;
-        break; // break RB_FOREACH
-      }
-    }
-
-    if (ue_context_p->ue_context.ue_release_timer > 0) {
-      ue_context_p->ue_context.ue_release_timer++;
-
-      if (ue_context_p->ue_context.ue_release_timer >= ue_context_p->ue_context.ue_release_timer_thres) {
-        LOG_I(RRC, "Removing UE %x instance because of RRC Connection Setup timer timeout\n",
-              ue_context_p->ue_context.rnti);
-        /*
-        * TODO: Naming problem here: ue_release_timer seems to have been used when RRC Connection Release was sent.
-        * It is no more the case.
-        * The timer should be renamed.
-        */
-        ue_to_be_removed = ue_context_p;
-        ue_context_p->ue_context.ue_release_timer = 0;
-        break; // break RB_FOREACH
-      }
-    }
-  } // end RB_FOREACH
-
-  if (ue_to_be_removed) {
-    if ((ue_to_be_removed->ue_context.ul_failure_timer >= 20000) ||
-        ((ue_to_be_removed->ue_context.ue_rrc_inactivity_timer >= RC.rrc[ctxt_pP->module_id]->configuration.rrc_inactivity_timer_thres) &&
-         (RC.rrc[ctxt_pP->module_id]->configuration.rrc_inactivity_timer_thres > 0))) {
-      ue_to_be_removed->ue_context.ue_release_timer_s1 = 1;
-      ue_to_be_removed->ue_context.ue_release_timer_thres_s1 = 100;
-      ue_to_be_removed->ue_context.ue_release_timer = 0;
-      ue_to_be_removed->ue_context.ue_reestablishment_timer = 0;
-    }
-
-    rrc_eNB_free_UE(ctxt_pP->module_id, ue_to_be_removed);
-
-    if (ue_to_be_removed->ue_context.ul_failure_timer >= 20000) {
-      ue_to_be_removed->ue_context.ul_failure_timer = 0;
-    }
-
-    if ((ue_to_be_removed->ue_context.ue_rrc_inactivity_timer >= RC.rrc[ctxt_pP->module_id]->configuration.rrc_inactivity_timer_thres) &&
-        (RC.rrc[ctxt_pP->module_id]->configuration.rrc_inactivity_timer_thres > 0)) {
-      ue_to_be_removed->ue_context.ue_rrc_inactivity_timer = 0; //reset timer after S1 command UE context release request is sent
-    }
-  }
-
-#ifdef RRC_LOCALIZATION
-  /* for the localization, only primary CC_id might be relevant*/
-  gettimeofday(&ts, NULL);
-  current_timestamp_ms = ts.tv_sec * 1000 + ts.tv_usec / 1000;
-  ref_timestamp_ms = RC.rrc[ctxt_pP->module_id]->reference_timestamp_ms;
-  RB_FOREACH(ue_context_p, rrc_ue_tree_s, &(RC.rrc[ctxt_pP->module_id]->rrc_ue_head)) {
-    ctxt = *ctxt_pP;
-    ctxt.rnti = ue_context_p->ue_context.rnti;
-    estimated_distance = rrc_get_estimated_ue_distance(&ctxt, CC_id, RC.rrc[ctxt_pP->module_id]->loc_type);
-
-    if ((current_timestamp_ms - ref_timestamp_ms > RC.rrc[ctxt_pP->module_id]->aggregation_period_ms) &&
-        estimated_distance != -1) {
-      LOG_D(LOCALIZE, "RRC [UE/id %d -> eNB/id %d] timestamp %d frame %d estimated r = %f\n",
-            ctxt.rnti,
-            ctxt_pP->module_id,
-            current_timestamp_ms,
-            ctxt_pP->frame,
-            estimated_distance);
-      LOG_D(LOCALIZE, "RRC status %d\n",
-            ue_context_p->ue_context.Status);
-      push_front(&RC.rrc[ctxt_pP->module_id]->loc_list, estimated_distance);
-      RC.rrc[ctxt_pP->module_id]->reference_timestamp_ms = current_timestamp_ms;
-    } // end if
-  } // end RB_FOREACH
-#endif
-  (void)ts; /* remove gcc warning "unused variable" */
-  (void)ref_timestamp_ms; /* remove gcc warning "unused variable" */
-  (void)current_timestamp_ms; /* remove gcc warning "unused variable" */
-  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_RRC_RX_TX, VCD_FUNCTION_OUT);
+  MessageDef *message_p;
+  message_p = itti_alloc_new_message(TASK_RRC_ENB, RRC_SUBFRAME_PROCESS);
+  RRC_SUBFRAME_PROCESS(message_p).ctxt  = *ctxt_pP;
+  RRC_SUBFRAME_PROCESS(message_p).CC_id = CC_id;
+  itti_send_msg_to_task(TASK_RRC_ENB, ctxt_pP->module_id, message_p);
   return RRC_OK;
 }
 
diff --git a/openair2/RRC/LTE/rrc_eNB_S1AP.c b/openair2/RRC/LTE/rrc_eNB_S1AP.c
index e07a4bd5d2c43cb85427975113ffad0e1659ff41..82818fcd1ed8c3ec226ba0d718d8fa02dfdc61e3 100644
--- a/openair2/RRC/LTE/rrc_eNB_S1AP.c
+++ b/openair2/RRC/LTE/rrc_eNB_S1AP.c
@@ -426,7 +426,7 @@ static e_LTE_SecurityAlgorithmConfig__integrityProtAlgorithm rrc_eNB_select_inte
  *\param security_capabilities The security capabilities received from S1AP.
  *\return TRUE if at least one algorithm has been changed else FALSE.
  */
-static int
+int
 rrc_eNB_process_security(
   const protocol_ctxt_t *const ctxt_pP,
   rrc_eNB_ue_context_t          *const ue_context_pP,
@@ -477,7 +477,7 @@ rrc_eNB_process_security(
  *\param security_key_pP The security key received from S1AP.
  */
 //------------------------------------------------------------------------------
-static void process_eNB_security_key (
+void process_eNB_security_key (
   const protocol_ctxt_t *const ctxt_pP,
   rrc_eNB_ue_context_t          *const ue_context_pP,
   uint8_t               *security_key_pP
@@ -1958,7 +1958,7 @@ int rrc_eNB_send_PATH_SWITCH_REQ(const protocol_ctxt_t *const ctxt_pP,
     S1AP_PATH_SWITCH_REQ (msg_p).e_rabs_tobeswitched[e_rab].eNB_addr = create_tunnel_resp.enb_addr;
     LOG_I (RRC,"enb_gtp_addr (msg index %d, e_rab index %d, status %d): nb_of_e_rabs %d,  e_rab_id %d, teid: %u, addr: %d.%d.%d.%d \n ",
            e_rabs_done,  e_rab, ue_context_pP->ue_context.e_rab[inde_list[e_rab]].status,
-           ue_context_pP->ue_context.nb_of_e_rabs,
+           S1AP_PATH_SWITCH_REQ (msg_p).nb_of_e_rabs,
            S1AP_PATH_SWITCH_REQ (msg_p).e_rabs_tobeswitched[e_rab].e_rab_id,
            S1AP_PATH_SWITCH_REQ (msg_p).e_rabs_tobeswitched[e_rab].gtp_teid,
            S1AP_PATH_SWITCH_REQ (msg_p).e_rabs_tobeswitched[e_rab].eNB_addr.buffer[0],
@@ -1970,7 +1970,7 @@ int rrc_eNB_send_PATH_SWITCH_REQ(const protocol_ctxt_t *const ctxt_pP,
   // NN: add conditions for e_rabs_failed
   if (e_rabs_done > 0) {
     LOG_I(RRC,"S1AP_PATH_SWITCH_REQ: sending the message: nb_of_erabstobeswitched %d, total e_rabs %d, index %d\n",
-          ue_context_pP->ue_context.nb_of_e_rabs, ue_context_pP->ue_context.setup_e_rabs, e_rab);
+          S1AP_PATH_SWITCH_REQ (msg_p).nb_of_e_rabs, ue_context_pP->ue_context.setup_e_rabs, e_rab);
     MSC_LOG_TX_MESSAGE(
       MSC_RRC_ENB,
       MSC_S1AP_ENB,
@@ -2017,46 +2017,64 @@ int rrc_eNB_process_S1AP_PATH_SWITCH_REQ_ACK (MessageDef *msg_p, const char *msg
     ue_context_p->ue_context.mme_ue_s1ap_id = S1AP_PATH_SWITCH_REQ_ACK (msg_p).mme_ue_s1ap_id;
     /* Save e RAB information for later */
     {
+      ue_context_p->ue_context.nb_release_of_e_rabs = S1AP_PATH_SWITCH_REQ_ACK (msg_p).nb_e_rabs_tobereleased;
+
       for (i = 0;
-           i < ue_context_p->ue_context.setup_e_rabs; // go over total number of e_rabs received through x2_ho_req msg
-           i++) {
-        // assume that we are releasing all the DRBs
-        ue_context_p->ue_context.e_rab[i].status =  E_RAB_STATUS_TORELEASE;
+	   i < ue_context_p->ue_context.setup_e_rabs; // go over total number of e_rabs received through x2_ho_req msg
+	   i++) {
+	// assume that we are releasing all the DRBs
+	ue_context_p->ue_context.e_rab[i].status = E_RAB_STATUS_REESTABLISHED;
+        if (ue_context_p->ue_context.nb_release_of_e_rabs==0) {
+          LOG_I(RRC,"Bearer re-established with ID: %d\n", ue_context_p->ue_context.e_rab[i].param.e_rab_id);
+        }
       }
 
       //memset(&create_tunnel_req, 0 , sizeof(create_tunnel_req));
-      //uint8_t nb_e_rabs_tobeswitched = S1AP_PATH_SWITCH_REQ_ACK (msg_p).nb_e_rabs_tobeswitched;
+      uint8_t nb_e_rabs_tobeswitched = S1AP_PATH_SWITCH_REQ_ACK (msg_p).nb_e_rabs_tobeswitched;
 
       // keep the previous bearer
       // the index for the rec
-      for (i = 0;
-           i < 1;//nb_e_rabs_tobeswitched; // go over total number of e_rabs received through x2_ho_req msg
-           i++) {
-        LOG_I(RRC,"Bearer re-established with ID: %d\n", ue_context_p->ue_context.e_rab[i].param.e_rab_id);
-        /* Harmonize with enb_gtp_teid, enb_gtp_addrs, and enb_gtp_rbi vars in the top level structure */
-        ue_context_p->ue_context.e_rab[i].status =  E_RAB_STATUS_REESTABLISHED;
-        //ue_context_p->ue_context.e_rab[i].param.e_rab_id = S1AP_PATH_SWITCH_REQ_ACK (msg_p).e_rabs_tobeswitched[i].e_rab_id;
-        //ue_context_p->ue_context.e_rab[i].param.sgw_addr= S1AP_PATH_SWITCH_REQ_ACK (msg_p).e_rabs_tobeswitched[i].sgw_addr;
-        //ue_context_p->ue_context.e_rab[i].param.gtp_teid = S1AP_PATH_SWITCH_REQ_ACK (msg_p).e_rabs_tobeswitched[i].gtp_teid;
-        /* Tunnel must have been already created in X2_HO_REQ procedure */
+      if (nb_e_rabs_tobeswitched>0) {
+        int e_rab_switch_index=0;
+        for (i = 0;
+	     i < ue_context_p->ue_context.setup_e_rabs; // go over total number of e_rabs received through x2_ho_req msg
+	     i++) {
+	  /* Harmonize with enb_gtp_teid, enb_gtp_addrs, and enb_gtp_rbi vars in the top level structure */
+          if (ue_context_p->ue_context.e_rab[i].param.e_rab_id == S1AP_PATH_SWITCH_REQ_ACK (msg_p).e_rabs_tobeswitched[e_rab_switch_index].e_rab_id) {
+	    ue_context_p->ue_context.e_rab[i].param.e_rab_id = S1AP_PATH_SWITCH_REQ_ACK (msg_p).e_rabs_tobeswitched[e_rab_switch_index].e_rab_id;
+	    ue_context_p->ue_context.e_rab[i].param.sgw_addr= S1AP_PATH_SWITCH_REQ_ACK (msg_p).e_rabs_tobeswitched[e_rab_switch_index].sgw_addr;
+	    ue_context_p->ue_context.e_rab[i].param.gtp_teid = S1AP_PATH_SWITCH_REQ_ACK (msg_p).e_rabs_tobeswitched[e_rab_switch_index].gtp_teid;
+            e_rab_switch_index++;
+          }
+        }
       }
-
-      ue_context_p->ue_context.setup_e_rabs=i;
-      ue_context_p->ue_context.nb_of_e_rabs=i;
     }
     ue_context_p->ue_context.ue_ambr=S1AP_PATH_SWITCH_REQ_ACK (msg_p).ue_ambr;
-    ue_context_p->ue_context.nb_release_of_e_rabs = S1AP_PATH_SWITCH_REQ_ACK (msg_p).nb_e_rabs_tobereleased;
-    memset(&delete_tunnel_req, 0, sizeof(delete_tunnel_req));
-
-    for (i = 0;
-         i < ue_context_p->ue_context.nb_release_of_e_rabs;
-         i++) {
-      LOG_I(RRC,"Bearer released with ID: %d\n", ue_context_p->ue_context.e_rab[i].param.e_rab_id);
-      ue_context_p->ue_context.e_rabs_tobereleased[i]=S1AP_PATH_SWITCH_REQ_ACK (msg_p).e_rabs_tobereleased[i].e_rab_id;
-      delete_tunnel_req.eps_bearer_id[i]       = S1AP_PATH_SWITCH_REQ_ACK (msg_p).e_rabs_tobereleased[i].e_rab_id;
-    }
+
+    ue_context_p->ue_context.setup_e_rabs = ue_context_p->ue_context.setup_e_rabs - ue_context_p->ue_context.nb_release_of_e_rabs;
+    ue_context_p->ue_context.nb_of_e_rabs = ue_context_p->ue_context.nb_of_e_rabs - ue_context_p->ue_context.nb_release_of_e_rabs;
+
+    memset(&delete_tunnel_req, 0 , sizeof(delete_tunnel_req));
 
     if (ue_context_p->ue_context.nb_release_of_e_rabs>0) {
+      int e_rab_release_index=0;
+      for (i = 0;
+	   i < ue_context_p->ue_context.setup_e_rabs;
+	   i++) {
+        if (ue_context_p->ue_context.e_rab[i].param.e_rab_id == S1AP_PATH_SWITCH_REQ_ACK (msg_p).e_rabs_tobereleased[e_rab_release_index].e_rab_id) {
+          LOG_I(RRC,"Bearer released with ID: %d\n", ue_context_p->ue_context.e_rab[i].param.e_rab_id);
+          ue_context_p->ue_context.e_rab[i].status =  E_RAB_STATUS_TORELEASE;
+          ue_context_p->ue_context.e_rabs_tobereleased[e_rab_release_index]=S1AP_PATH_SWITCH_REQ_ACK (msg_p).e_rabs_tobereleased[e_rab_release_index].e_rab_id;
+          delete_tunnel_req.eps_bearer_id[e_rab_release_index] = S1AP_PATH_SWITCH_REQ_ACK (msg_p).e_rabs_tobereleased[e_rab_release_index].e_rab_id;
+          e_rab_release_index++;
+        }
+        else {
+          LOG_I(RRC,"Bearer re-established with ID: %d\n", ue_context_p->ue_context.e_rab[i].param.e_rab_id);
+        }
+      }
+    }
+
+    if (ue_context_p->ue_context.nb_release_of_e_rabs>0){
       delete_tunnel_req.rnti= ue_context_p->ue_context.rnti;
       delete_tunnel_req.num_erab= ue_context_p->ue_context.nb_release_of_e_rabs;
       /* this could also be done through ITTI message */
@@ -2069,9 +2087,59 @@ 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).rnti = ue_context_pP->ue_context.rnti;
+  X2AP_UE_CONTEXT_RELEASE (msg_p).source_assoc_id = ue_context_pP->ue_context.handover_info->assoc_id;
+  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 33a0caabc8923fd533d101e6f2645c815436abda..da63fc68a36ab17ec304bc6300015a1e852831b3 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 db29f32d7ba839fb8f5d98627b815feb902c9a8e..c05495a0c414c79bb5ee1bcf20da02065c9682f8 100644
--- a/openair2/X2AP/x2ap_eNB.c
+++ b/openair2/X2AP/x2ap_eNB.c
@@ -40,6 +40,7 @@
 #include "x2ap_eNB_handler.h"
 #include "x2ap_eNB_generate_messages.h"
 #include "x2ap_common.h"
+#include "x2ap_ids.h"
 
 #include "queue.h"
 #include "assertions.h"
@@ -79,6 +80,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_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;
@@ -130,7 +136,7 @@ void x2ap_eNB_handle_sctp_association_resp(instance_t instance, sctp_new_associa
               sctp_new_association_resp->sctp_state,
               instance,
               sctp_new_association_resp->ulp_cnx_id);
-    x2ap_handle_x2_setup_message(x2ap_enb_data_p,
+    x2ap_handle_x2_setup_message(instance_p, x2ap_enb_data_p,
                                  sctp_new_association_resp->sctp_state == SCTP_STATE_SHUTDOWN);
     return;
   }
@@ -294,6 +300,8 @@ void x2ap_eNB_handle_register_eNB(instance_t instance,
     new_instance->mnc_digit_length = x2ap_register_eNB->mnc_digit_length;
     new_instance->num_cc           = x2ap_register_eNB->num_cc;
 
+    x2ap_id_manager_init(&new_instance->id_manager);
+
     for (int i = 0; i< x2ap_register_eNB->num_cc; i++) {
       new_instance->eutra_band[i]              = x2ap_register_eNB->eutra_band[i];
       new_instance->downlink_frequency[i]      = x2ap_register_eNB->downlink_frequency[i];
@@ -371,30 +379,31 @@ static
 void x2ap_eNB_handle_handover_req(instance_t instance,
                                   x2ap_handover_req_t *x2ap_handover_req)
 {
-  /* TODO: remove this hack (the goal is to find the correct
-   * eNodeB structure for the target) - we need a proper way for RRC
-   * and X2AP to identify eNodeBs
-   * RRC knows about mod_id and X2AP knows about eNB_id (eNB_ID in
-   * the configuration file)
-   * as far as I understand.. CROUX
-   */
   x2ap_eNB_instance_t *instance_p;
   x2ap_eNB_data_t     *target;
+  x2ap_id_manager     *id_manager;
+  int                 ue_id;
 
-  int target_enb_id = x2ap_handover_req->target_physCellId;
+  int target_pci = x2ap_handover_req->target_physCellId;
 
-  instance_p = x2ap_eNB_pci_get_instance(target_enb_id);
+  instance_p = x2ap_eNB_get_instance(instance);
   DevAssert(instance_p != NULL);
 
-  //instance_p = x2ap_eNB_get_instance(instance);
-  //DevAssert(instance_p != NULL);
-
-  target = x2ap_is_eNB_id_in_list(instance_p->eNB_id);
+  target = x2ap_is_eNB_pci_in_list(target_pci);
   DevAssert(target != NULL);
 
-  /* store rnti at index 0 */
-  //x2id_to_source_rnti[0] = x2ap_handover_req->source_rnti;
-  x2ap_eNB_generate_x2_handover_request(target, x2ap_handover_req);
+  /* allocate x2ap ID */
+  id_manager = &instance_p->id_manager;
+  ue_id = x2ap_allocate_new_id(id_manager);
+  if (ue_id == -1) {
+    X2AP_ERROR("could not allocate a new X2AP UE ID\n");
+    /* TODO: cancel handover: send (to be defined) message to RRC */
+    exit(1);
+  }
+  /* id_source is ue_id, id_target is unknown yet */
+  x2ap_set_ids(id_manager, ue_id, x2ap_handover_req->rnti, ue_id, -1);
+
+  x2ap_eNB_generate_x2_handover_request(instance_p, target, x2ap_handover_req, ue_id);
 }
 
 static
@@ -410,17 +419,49 @@ void x2ap_eNB_handle_handover_req_ack(instance_t instance,
    */
   x2ap_eNB_instance_t *instance_p;
   x2ap_eNB_data_t     *target;
-  int target_enb_id = x2ap_handover_req_ack->target_mod_id;
+  int source_assoc_id = x2ap_handover_req_ack->source_assoc_id;
+  int                 ue_id;
+  int                 id_source;
+  int                 id_target;
+
+  instance_p = x2ap_eNB_get_instance(instance);
+  DevAssert(instance_p != NULL);
+
+  target = x2ap_get_eNB(NULL, source_assoc_id, 0);
+  DevAssert(target != NULL);
 
+  /* rnti is a new information, save it */
+  ue_id     = x2ap_handover_req_ack->x2_id_target;
+  id_source = x2ap_id_get_id_source(&instance_p->id_manager, ue_id);
+  id_target = ue_id;
+  x2ap_set_ids(&instance_p->id_manager, ue_id, x2ap_handover_req_ack->rnti, id_source, id_target);
+
+  x2ap_eNB_generate_x2_handover_request_ack(instance_p, target, x2ap_handover_req_ack);
+}
+
+static
+void x2ap_eNB_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 source_assoc_id = x2ap_ue_context_release->source_assoc_id;
+  int ue_id;
   instance_p = x2ap_eNB_get_instance(instance);
   DevAssert(instance_p != NULL);
 
-  target = x2ap_is_eNB_id_in_list(target_enb_id);
+  target = x2ap_get_eNB(NULL, source_assoc_id, 0);
   DevAssert(target != NULL);
 
-  x2ap_eNB_generate_x2_handover_request_ack(target, x2ap_handover_req_ack);
-  //x2ap_eNB_generate_x2_handover_req_ack(instance_p, target, x2ap_handover_req_ack->source_x2id,
-          //x2ap_handover_req_ack->rrc_buffer, x2ap_handover_req_ack->rrc_buffer_size);
+  x2ap_eNB_generate_x2_ue_context_release(instance_p, target, x2ap_ue_context_release);
+
+  /* free the X2AP UE ID */
+  ue_id = x2ap_find_id_from_rnti(&instance_p->id_manager, x2ap_ue_context_release->rnti);
+  if (ue_id == -1) {
+    X2AP_ERROR("could not find UE %x\n", x2ap_ue_context_release->rnti);
+    exit(1);
+  }
+  x2ap_release_id(&instance_p->id_manager, ue_id);
 }
 
 void *x2ap_task(void *arg) {
@@ -454,6 +495,11 @@ void *x2ap_task(void *arg) {
                                          &X2AP_HANDOVER_REQ_ACK(received_msg));
         break;
 
+      case X2AP_UE_CONTEXT_RELEASE:
+        x2ap_eNB_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 553cf8e559c1d38b3969e33a2e21384c6401a326..a5b19fe2ccac109bd10256de1be489cb494ca75f 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_defs.h b/openair2/X2AP/x2ap_eNB_defs.h
index a5558c42a5307d7f55c64be8eca6cf788e37b3c8..3356a101b4e632927cc72d9397f850d1a75885ca 100644
--- a/openair2/X2AP/x2ap_eNB_defs.h
+++ b/openair2/X2AP/x2ap_eNB_defs.h
@@ -33,6 +33,8 @@
 
 #include "sctp_eNB_defs.h"
 
+#include "x2ap_ids.h"
+
 #ifndef X2AP_ENB_DEFS_H_
 #define X2AP_ENB_DEFS_H_
 
@@ -114,6 +116,10 @@ typedef struct x2ap_eNB_data_s {
   /* SCTP association id */
   int32_t  assoc_id;
 
+  /* Nid cells */
+  uint32_t                Nid_cell[MAX_NUM_CCs];
+  int                     num_cc;
+
   /* Only meaningfull in virtual mode */
   struct x2ap_eNB_instance_s *x2ap_eNB_instance;
 } x2ap_eNB_data_t;
@@ -163,7 +169,6 @@ typedef struct x2ap_eNB_instance_s {
   uint32_t                downlink_frequency[MAX_NUM_CCs];
   int32_t                 uplink_frequency_offset[MAX_NUM_CCs];
   uint32_t                Nid_cell[MAX_NUM_CCs];
-  uint32_t                Nid_target_cell[MAX_NUM_CCs];
   int16_t                 N_RB_DL[MAX_NUM_CCs];
   lte_frame_type_t        frame_type[MAX_NUM_CCs];
   uint32_t                fdd_earfcn_DL[MAX_NUM_CCs];
@@ -177,6 +182,8 @@ typedef struct x2ap_eNB_instance_s {
   uint16_t         sctp_out_streams;
   uint32_t         enb_port_for_X2C;
   int              multi_sd;
+
+  x2ap_id_manager  id_manager;
 } x2ap_eNB_instance_t;
 
 typedef struct {
diff --git a/openair2/X2AP/x2ap_eNB_generate_messages.c b/openair2/X2AP/x2ap_eNB_generate_messages.c
index 76d6053b59a1fb3a242a8730e3dface21a4eecda..c13469d5d4bfc209c523c6b063620a4e6d5c8067 100644
--- a/openair2/X2AP/x2ap_eNB_generate_messages.c
+++ b/openair2/X2AP/x2ap_eNB_generate_messages.c
@@ -35,6 +35,7 @@
 #include "x2ap_eNB_generate_messages.h"
 #include "x2ap_eNB_encoder.h"
 #include "x2ap_eNB_decoder.h"
+#include "x2ap_ids.h"
 
 #include "x2ap_eNB_itti_messaging.h"
 
@@ -180,7 +181,7 @@ int x2ap_eNB_generate_x2_setup_request(
   return ret;
 }
 
-int x2ap_eNB_generate_x2_setup_response(x2ap_eNB_data_t *x2ap_eNB_data_p)
+int x2ap_eNB_generate_x2_setup_response(x2ap_eNB_instance_t *instance_p, x2ap_eNB_data_t *x2ap_eNB_data_p)
 {
   X2AP_X2AP_PDU_t                     pdu;
   X2AP_X2SetupResponse_t              *out;
@@ -189,18 +190,12 @@ int x2ap_eNB_generate_x2_setup_response(x2ap_eNB_data_t *x2ap_eNB_data_p)
   ServedCells__Member                 *servedCellMember;
   X2AP_GU_Group_ID_t                  *gu;
 
-  x2ap_eNB_instance_t                 *instance_p;
-
   uint8_t  *buffer;
   uint32_t  len;
   int       ret = 0;
 
-  DevAssert(x2ap_eNB_data_p != NULL);
-
-  /* get the eNB instance */
-  instance_p = x2ap_eNB_data_p->x2ap_eNB_instance;
-
   DevAssert(instance_p != NULL);
+  DevAssert(x2ap_eNB_data_p != NULL);
 
   /* Prepare the X2AP message to encode */
   memset(&pdu, 0, sizeof(pdu));
@@ -414,8 +409,8 @@ int x2ap_eNB_set_cause (X2AP_Cause_t * cause_p,
   return 0;
 }
 
-int x2ap_eNB_generate_x2_handover_request (x2ap_eNB_data_t *x2ap_eNB_data_p,
-                                           x2ap_handover_req_t *x2ap_handover_req)
+int x2ap_eNB_generate_x2_handover_request (x2ap_eNB_instance_t *instance_p, x2ap_eNB_data_t *x2ap_eNB_data_p,
+                                           x2ap_handover_req_t *x2ap_handover_req, int ue_id)
 {
 
   X2AP_X2AP_PDU_t                     pdu;
@@ -425,19 +420,12 @@ int x2ap_eNB_generate_x2_handover_request (x2ap_eNB_data_t *x2ap_eNB_data_p,
   X2AP_E_RABs_ToBeSetup_Item_t        *e_RABs_ToBeSetup_Item;
   X2AP_LastVisitedCell_Item_t         *lastVisitedCell_Item;
 
-  x2ap_eNB_instance_t                 *instance_p;
-
   uint8_t  *buffer;
   uint32_t  len;
   int       ret = 0;
 
-  DevAssert(x2ap_eNB_data_p != NULL);
-
-  /* get the eNB instance */
-  instance_p = x2ap_eNB_data_p->x2ap_eNB_instance;
-
   DevAssert(instance_p != NULL);
-
+  DevAssert(x2ap_eNB_data_p != NULL);
 
   /* Prepare the X2AP handover message to encode */
   memset(&pdu, 0, sizeof(pdu));
@@ -452,7 +440,7 @@ int x2ap_eNB_generate_x2_handover_request (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_HandoverRequest_IEs__value_PR_UE_X2AP_ID;
-  ie->value.choice.UE_X2AP_ID = x2ap_handover_req->old_eNB_ue_x2ap_id;
+  ie->value.choice.UE_X2AP_ID = x2ap_id_get_id_source(&instance_p->id_manager, ue_id);
   ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
 
   /* mandatory */
@@ -471,7 +459,7 @@ int x2ap_eNB_generate_x2_handover_request (x2ap_eNB_data_t *x2ap_eNB_data_p,
   ie->value.present = X2AP_HandoverRequest_IEs__value_PR_ECGI;
   MCC_MNC_TO_PLMNID(instance_p->mcc, instance_p->mnc, instance_p->mnc_digit_length,
                        &ie->value.choice.ECGI.pLMN_Identity);
-  MACRO_ENB_ID_TO_CELL_IDENTITY(instance_p->eNB_id, 0, &ie->value.choice.ECGI.eUTRANcellIdentifier);
+  MACRO_ENB_ID_TO_CELL_IDENTITY(x2ap_eNB_data_p->eNB_id, 0, &ie->value.choice.ECGI.eUTRANcellIdentifier);
   ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
 
   /* mandatory */
@@ -576,7 +564,7 @@ int x2ap_eNB_generate_x2_handover_request (x2ap_eNB_data_t *x2ap_eNB_data_p,
   return ret;
 }
 
-int x2ap_eNB_generate_x2_handover_request_ack (x2ap_eNB_data_t *x2ap_eNB_data_p,
+int x2ap_eNB_generate_x2_handover_request_ack (x2ap_eNB_instance_t *instance_p, x2ap_eNB_data_t *x2ap_eNB_data_p,
                                                x2ap_handover_req_ack_t *x2ap_handover_req_ack)
 {
 
@@ -585,19 +573,20 @@ int x2ap_eNB_generate_x2_handover_request_ack (x2ap_eNB_data_t *x2ap_eNB_data_p,
   X2AP_HandoverRequestAcknowledge_IEs_t  *ie;
   X2AP_E_RABs_Admitted_ItemIEs_t         *e_RABS_Admitted_ItemIEs;
   X2AP_E_RABs_Admitted_Item_t            *e_RABs_Admitted_Item;
-
-  x2ap_eNB_instance_t                 *instance_p;
+  int                                    ue_id;
+  int                                    id_source;
+  int                                    id_target;
 
   uint8_t  *buffer;
   uint32_t  len;
   int       ret = 0;
 
+  DevAssert(instance_p != NULL);
   DevAssert(x2ap_eNB_data_p != NULL);
 
-  /* get the eNB instance */
-  instance_p = x2ap_eNB_data_p->x2ap_eNB_instance;
-
-  DevAssert(instance_p != NULL);
+  ue_id     = x2ap_handover_req_ack->x2_id_target;
+  id_source = x2ap_id_get_id_source(&instance_p->id_manager, ue_id);
+  id_target = ue_id;
 
   /* Prepare the X2AP handover message to encode */
   memset(&pdu, 0, sizeof(pdu));
@@ -612,15 +601,15 @@ int x2ap_eNB_generate_x2_handover_request_ack (x2ap_eNB_data_t *x2ap_eNB_data_p,
   ie->id = X2AP_ProtocolIE_ID_id_Old_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.choice.UE_X2AP_ID = id_source;
   ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
 
   /* mandatory */
   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 = id_target;
   ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
 
   /* mandatory */
@@ -668,25 +657,30 @@ 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_instance_t *instance_p, x2ap_eNB_data_t *x2ap_eNB_data_p, x2ap_ue_context_release_t *x2ap_ue_context_release)
 {
 
   X2AP_X2AP_PDU_t                pdu;
   X2AP_UEContextRelease_t        *out;
   X2AP_UEContextRelease_IEs_t    *ie;
-
-  x2ap_eNB_instance_t               *instance_p;
+  int                            ue_id;
+  int                            id_source;
+  int                            id_target;
 
   uint8_t  *buffer;
   uint32_t  len;
   int       ret = 0;
 
+  DevAssert(instance_p != NULL);
   DevAssert(x2ap_eNB_data_p != NULL);
 
-  /* get the eNB instance */
-  instance_p = x2ap_eNB_data_p->x2ap_eNB_instance;
-
-  DevAssert(instance_p != NULL);
+  ue_id = x2ap_find_id_from_rnti(&instance_p->id_manager, x2ap_ue_context_release->rnti);
+  if (ue_id == -1) {
+    X2AP_ERROR("could not find UE %x\n", x2ap_ue_context_release->rnti);
+    exit(1);
+  }
+  id_source = x2ap_id_get_id_source(&instance_p->id_manager, ue_id);
+  id_target = ue_id;
 
   /* Prepare the X2AP ue context relase message to encode */
   memset(&pdu, 0, sizeof(pdu));
@@ -701,15 +695,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 = id_source;
   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 = id_target;
   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 9028d8cd054e9d78388e7cf17967d6b00bdbee7f..059d213277599a465876f3ebfb4c6b5fba242ce2 100644
--- a/openair2/X2AP/x2ap_eNB_generate_messages.h
+++ b/openair2/X2AP/x2ap_eNB_generate_messages.h
@@ -35,7 +35,7 @@
 int x2ap_eNB_generate_x2_setup_request(x2ap_eNB_instance_t *instance_p,
 				       x2ap_eNB_data_t *x2ap_eNB_data_p);
 
-int x2ap_eNB_generate_x2_setup_response(x2ap_eNB_data_t *x2ap_eNB_data_p);
+int x2ap_eNB_generate_x2_setup_response(x2ap_eNB_instance_t *instance_p, x2ap_eNB_data_t *x2ap_eNB_data_p);
 
 int x2ap_eNB_generate_x2_setup_failure(instance_t instance,
                                        uint32_t assoc_id,
@@ -47,12 +47,13 @@ int x2ap_eNB_set_cause (X2AP_Cause_t * cause_p,
                         X2AP_Cause_PR cause_type,
                         long cause_value);
 
-int x2ap_eNB_generate_x2_handover_request (x2ap_eNB_data_t *x2ap_eNB_data_p,
-                                           x2ap_handover_req_t *x2ap_handover_req);
+int x2ap_eNB_generate_x2_handover_request (x2ap_eNB_instance_t *instance_p, x2ap_eNB_data_t *x2ap_eNB_data_p,
+                                           x2ap_handover_req_t *x2ap_handover_req, int ue_id);
 
-int x2ap_eNB_generate_x2_handover_request_ack (x2ap_eNB_data_t *x2ap_eNB_data_p,
+int x2ap_eNB_generate_x2_handover_request_ack (x2ap_eNB_instance_t *instance_p, 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_instance_t *instance_p, 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 52b14f1d236510804626c9ee889383f19f306e78..9da92787416a1a24f5453ad9b07541b2737c3670 100644
--- a/openair2/X2AP/x2ap_eNB_handler.c
+++ b/openair2/X2AP/x2ap_eNB_handler.c
@@ -36,6 +36,7 @@
 #include "x2ap_eNB_defs.h"
 #include "x2ap_eNB_handler.h"
 #include "x2ap_eNB_decoder.h"
+#include "x2ap_ids.h"
 
 #include "x2ap_eNB_management_procedures.h"
 #include "x2ap_eNB_generate_messages.h"
@@ -71,6 +72,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 +85,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 */
@@ -112,7 +119,7 @@ static char *x2ap_direction_String[] = {
 return(x2ap_direction_String[x2ap_dir]);
 }
 
-void x2ap_handle_x2_setup_message(x2ap_eNB_data_t *enb_desc_p, int sctp_shutdown)
+void x2ap_handle_x2_setup_message(x2ap_eNB_instance_t *instance_p, x2ap_eNB_data_t *enb_desc_p, int sctp_shutdown)
 {
   if (sctp_shutdown) {
     /* A previously connected eNB has been shutdown */
@@ -121,38 +128,38 @@ void x2ap_handle_x2_setup_message(x2ap_eNB_data_t *enb_desc_p, int sctp_shutdown
     if (enb_desc_p->state == X2AP_ENB_STATE_CONNECTED) {
       enb_desc_p->state = X2AP_ENB_STATE_DISCONNECTED;
 
-      if (enb_desc_p->x2ap_eNB_instance-> x2_target_enb_associated_nb > 0) {
+      if (instance_p-> x2_target_enb_associated_nb > 0) {
         /* Decrease associated eNB number */
-        enb_desc_p->x2ap_eNB_instance-> x2_target_enb_associated_nb --;
+        instance_p-> x2_target_enb_associated_nb --;
       }
 
       /* If there are no more associated eNB, inform eNB app */
-      if (enb_desc_p->x2ap_eNB_instance->x2_target_enb_associated_nb == 0) {
+      if (instance_p->x2_target_enb_associated_nb == 0) {
         MessageDef                 *message_p;
 
         message_p = itti_alloc_new_message(TASK_X2AP, X2AP_DEREGISTERED_ENB_IND);
         X2AP_DEREGISTERED_ENB_IND(message_p).nb_x2 = 0;
-        itti_send_msg_to_task(TASK_ENB_APP, enb_desc_p->x2ap_eNB_instance->instance, message_p);
+        itti_send_msg_to_task(TASK_ENB_APP, instance_p->instance, message_p);
       }
     }
   } else {
     /* Check that at least one setup message is pending */
-    DevCheck(enb_desc_p->x2ap_eNB_instance->x2_target_enb_pending_nb > 0,
-             enb_desc_p->x2ap_eNB_instance->instance,
-             enb_desc_p->x2ap_eNB_instance->x2_target_enb_pending_nb, 0);
+    DevCheck(instance_p->x2_target_enb_pending_nb > 0,
+             instance_p->instance,
+             instance_p->x2_target_enb_pending_nb, 0);
 
-    if (enb_desc_p->x2ap_eNB_instance->x2_target_enb_pending_nb > 0) {
+    if (instance_p->x2_target_enb_pending_nb > 0) {
       /* Decrease pending messages number */
-      enb_desc_p->x2ap_eNB_instance->x2_target_enb_pending_nb --;
+      instance_p->x2_target_enb_pending_nb --;
     }
 
     /* If there are no more pending messages, inform eNB app */
-    if (enb_desc_p->x2ap_eNB_instance->x2_target_enb_pending_nb == 0) {
+    if (instance_p->x2_target_enb_pending_nb == 0) {
       MessageDef                 *message_p;
 
       message_p = itti_alloc_new_message(TASK_X2AP, X2AP_REGISTER_ENB_CNF);
-      X2AP_REGISTER_ENB_CNF(message_p).nb_x2 = enb_desc_p->x2ap_eNB_instance->x2_target_enb_associated_nb;
-      itti_send_msg_to_task(TASK_ENB_APP, enb_desc_p->x2ap_eNB_instance->instance, message_p);
+      X2AP_REGISTER_ENB_CNF(message_p).nb_x2 = instance_p->x2_target_enb_associated_nb;
+      itti_send_msg_to_task(TASK_ENB_APP, instance_p->instance, message_p);
     }
   }
 }
@@ -276,6 +283,7 @@ x2ap_eNB_handle_x2_setup_request(instance_t instance,
   X2AP_X2SetupRequest_IEs_t          *ie;
   ServedCells__Member                *servedCellMember;
 
+  x2ap_eNB_instance_t                *instance_p;
   x2ap_eNB_data_t                    *x2ap_eNB_data;
   uint32_t                           eNB_id = 0;
 
@@ -358,8 +366,7 @@ x2ap_eNB_handle_x2_setup_request(instance_t instance,
        */
       X2AP_ERROR("Rejecting x2 setup request as eNB id %d is already associated to an active sctp association" "Previous known: %d, new one: %d\n", eNB_id, x2ap_eNB_data->assoc_id, assoc_id);
 
-      DevAssert(x2ap_eNB_data->x2ap_eNB_instance != NULL);
-      x2ap_eNB_generate_x2_setup_failure (x2ap_eNB_data->x2ap_eNB_instance->instance,
+      x2ap_eNB_generate_x2_setup_failure (instance,
                                           assoc_id,
                                           X2AP_Cause_PR_protocol,
                                           X2AP_CauseProtocol_unspecified,
@@ -374,19 +381,22 @@ x2ap_eNB_handle_x2_setup_request(instance_t instance,
   /* Set proper pci */
   X2AP_FIND_PROTOCOLIE_BY_ID(X2AP_X2SetupRequest_IEs_t, ie, x2SetupRequest,
                              X2AP_ProtocolIE_ID_id_ServedCells, true);
-
   if (ie == NULL ) {
     X2AP_ERROR("%s %d: ie is a NULL pointer \n",__FILE__,__LINE__);
     return -1;
-  } else if (ie->value.choice.ServedCells.list.count > 0) {
-    x2ap_eNB_data->x2ap_eNB_instance->num_cc = ie->value.choice.ServedCells.list.count;
+  }
+  if (ie->value.choice.ServedCells.list.count > 0) {
+    x2ap_eNB_data->num_cc = ie->value.choice.ServedCells.list.count;
     for (int i=0; i<ie->value.choice.ServedCells.list.count;i++) {
       servedCellMember = (ServedCells__Member *)ie->value.choice.ServedCells.list.array[i];
-      x2ap_eNB_data->x2ap_eNB_instance->Nid_target_cell[i] = servedCellMember->servedCellInfo.pCI;
+      x2ap_eNB_data->Nid_cell[i] = servedCellMember->servedCellInfo.pCI;
     }
   }
 
-  return x2ap_eNB_generate_x2_setup_response(x2ap_eNB_data);
+  instance_p = x2ap_eNB_get_instance(instance);
+  DevAssert(instance_p != NULL);
+
+  return x2ap_eNB_generate_x2_setup_response(instance_p, x2ap_eNB_data);
 }
 
 static
@@ -400,6 +410,7 @@ int x2ap_eNB_handle_x2_setup_response(instance_t instance,
   X2AP_X2SetupResponse_IEs_t          *ie;
   ServedCells__Member                 *servedCellMember;
 
+  x2ap_eNB_instance_t                 *instance_p;
   x2ap_eNB_data_t                     *x2ap_eNB_data;
   uint32_t                            eNB_id = 0;
 
@@ -478,15 +489,16 @@ int x2ap_eNB_handle_x2_setup_response(instance_t instance,
   /* Set proper pci */
   X2AP_FIND_PROTOCOLIE_BY_ID(X2AP_X2SetupResponse_IEs_t, ie, x2SetupResponse,
                              X2AP_ProtocolIE_ID_id_ServedCells, true);
-
   if (ie == NULL ) {
     X2AP_ERROR("%s %d: ie is a NULL pointer \n",__FILE__,__LINE__);
     return -1;
-  } else if (ie->value.choice.ServedCells.list.count > 0) {
-    x2ap_eNB_data->x2ap_eNB_instance->num_cc = ie->value.choice.ServedCells.list.count;
+  }
+
+  if (ie->value.choice.ServedCells.list.count > 0) {
+    x2ap_eNB_data->num_cc = ie->value.choice.ServedCells.list.count;
     for (int i=0; i<ie->value.choice.ServedCells.list.count;i++) {
       servedCellMember = (ServedCells__Member *)ie->value.choice.ServedCells.list.array[i];
-      x2ap_eNB_data->x2ap_eNB_instance->Nid_target_cell[i] = servedCellMember->servedCellInfo.pCI;
+      x2ap_eNB_data->Nid_cell[i] = servedCellMember->servedCellInfo.pCI;
     }
   }
 
@@ -496,8 +508,12 @@ int x2ap_eNB_handle_x2_setup_response(instance_t instance,
    * Mark the association as connected.
    */
   x2ap_eNB_data->state = X2AP_ENB_STATE_READY;
-  x2ap_eNB_data->x2ap_eNB_instance->x2_target_enb_associated_nb ++;
-  x2ap_handle_x2_setup_message(x2ap_eNB_data, 0);
+
+  instance_p = x2ap_eNB_get_instance(instance);
+  DevAssert(instance_p != NULL);
+
+  instance_p->x2_target_enb_associated_nb ++;
+  x2ap_handle_x2_setup_message(instance_p, x2ap_eNB_data, 0);
 
   return 0;
 }
@@ -512,6 +528,7 @@ int x2ap_eNB_handle_x2_setup_failure(instance_t instance,
   X2AP_X2SetupFailure_t              *x2SetupFailure;
   X2AP_X2SetupFailure_IEs_t          *ie;
 
+  x2ap_eNB_instance_t                *instance_p;
   x2ap_eNB_data_t                    *x2ap_eNB_data;
 
   DevAssert(pdu != NULL);
@@ -552,7 +569,11 @@ int x2ap_eNB_handle_x2_setup_failure(instance_t instance,
   }
 
   x2ap_eNB_data->state = X2AP_ENB_STATE_WAITING;
-  x2ap_handle_x2_setup_message(x2ap_eNB_data, 0);
+
+  instance_p = x2ap_eNB_get_instance(instance);
+  DevAssert(instance_p != NULL);
+
+  x2ap_handle_x2_setup_message(instance_p, x2ap_eNB_data, 0);
 
   return 0;
 }
@@ -570,8 +591,10 @@ int x2ap_eNB_handle_handover_preparation (instance_t instance,
   X2AP_E_RABs_ToBeSetup_ItemIEs_t    *e_RABS_ToBeSetup_ItemIEs;
   X2AP_E_RABs_ToBeSetup_Item_t       *e_RABs_ToBeSetup_Item;
 
+  x2ap_eNB_instance_t                *instance_p;
   x2ap_eNB_data_t                    *x2ap_eNB_data;
   MessageDef                         *msg;
+  int                                ue_id;
 
   DevAssert (pdu != NULL);
   x2HandoverRequest = &pdu->choice.initiatingMessage.value.choice.HandoverRequest;
@@ -587,19 +610,30 @@ int x2ap_eNB_handle_handover_preparation (instance_t instance,
   x2ap_eNB_data = x2ap_get_eNB(NULL, assoc_id, 0);
   DevAssert(x2ap_eNB_data != NULL);
 
+  instance_p = x2ap_eNB_get_instance(instance);
+  DevAssert(instance_p != NULL);
+
   msg = itti_alloc_new_message(TASK_X2AP, X2AP_HANDOVER_REQ);
 
   X2AP_FIND_PROTOCOLIE_BY_ID(X2AP_HandoverRequest_IEs_t, ie, x2HandoverRequest,
                              X2AP_ProtocolIE_ID_id_Old_eNB_UE_X2AP_ID, true);
-  //X2AP_HANDOVER_REQ(msg).source_rnti = ctxt_pP->rnti;
-  //X2AP_HANDOVER_REQ(m).source_x2id = x2HandoverRequest->old_eNB_UE_X2AP_ID;
   if (ie == NULL ) {
     X2AP_ERROR("%s %d: ie is a NULL pointer \n",__FILE__,__LINE__);
     return -1;
-  } else {
-    X2AP_HANDOVER_REQ(msg).old_eNB_ue_x2ap_id = ie->value.choice.UE_X2AP_ID;
   }
 
+  /* allocate a new X2AP UE ID */
+  ue_id = x2ap_allocate_new_id(&instance_p->id_manager);
+  if (ue_id == -1) {
+    X2AP_ERROR("could not allocate a new X2AP UE ID\n");
+    /* TODO: cancel handover: send HO preparation failure to source eNB */
+    exit(1);
+  }
+  /* rnti is unknown yet, must not be set to -1, 0 is fine */
+  x2ap_set_ids(&instance_p->id_manager, ue_id, 0, ie->value.choice.UE_X2AP_ID, ue_id);
+
+  X2AP_HANDOVER_REQ(msg).x2_id = ue_id;
+
   //X2AP_HANDOVER_REQ(msg).target_physCellId = measResults2->measResultNeighCells->choice.
                                                //measResultListEUTRA.list.array[ncell_index]->physCellId;
   X2AP_FIND_PROTOCOLIE_BY_ID(X2AP_HandoverRequest_IEs_t, ie, x2HandoverRequest,
@@ -619,7 +653,11 @@ int x2ap_eNB_handle_handover_preparation (instance_t instance,
   }
 
   X2AP_HANDOVER_REQ(msg).mme_ue_s1ap_id = ie->value.choice.UE_ContextInformation.mME_UE_S1AP_ID;
-  X2AP_HANDOVER_REQ(msg).target_mod_id = x2ap_eNB_data->x2ap_eNB_instance->eNB_id;
+
+  /* TODO: properly store Target Cell ID */
+
+  X2AP_HANDOVER_REQ(msg).target_assoc_id = assoc_id;
+
   X2AP_HANDOVER_REQ(msg).security_capabilities.encryption_algorithms =
     BIT_STRING_to_uint16(&ie->value.choice.UE_ContextInformation.uESecurityCapabilities.encryptionAlgorithms);
   X2AP_HANDOVER_REQ(msg).security_capabilities.integrity_algorithms =
@@ -671,10 +709,10 @@ int x2ap_eNB_handle_handover_preparation (instance_t instance,
   if (c->size > 1024 /* TODO: this is the size of rrc_buffer in struct x2ap_handover_req_ack_s*/)
     { printf("%s:%d: fatal: buffer too big\n", __FILE__, __LINE__); abort(); }
 
-  memcpy(X2AP_HANDOVER_REQ_ACK(msg).rrc_buffer, c->buf, c->size);
-  X2AP_HANDOVER_REQ_ACK(msg).rrc_buffer_size = c->size;
+  memcpy(X2AP_HANDOVER_REQ(msg).rrc_buffer, c->buf, c->size);
+  X2AP_HANDOVER_REQ(msg).rrc_buffer_size = c->size;
 
-  itti_send_msg_to_task(TASK_RRC_ENB, x2ap_eNB_data->x2ap_eNB_instance->instance, msg);
+  itti_send_msg_to_task(TASK_RRC_ENB, instance_p->instance, msg);
 
   return 0;
 }
@@ -688,8 +726,13 @@ int x2ap_eNB_handle_handover_response (instance_t instance,
   X2AP_HandoverRequestAcknowledge_t             *x2HandoverRequestAck;
   X2AP_HandoverRequestAcknowledge_IEs_t         *ie;
 
+  x2ap_eNB_instance_t                           *instance_p;
   x2ap_eNB_data_t                               *x2ap_eNB_data;
   MessageDef                                    *msg;
+  int                                           ue_id;
+  int                                           id_source;
+  int                                           id_target;
+  int                                           rnti;
 
   DevAssert (pdu != NULL);
   x2HandoverRequestAck = &pdu->choice.successfulOutcome.value.choice.HandoverRequestAcknowledge;
@@ -705,12 +748,44 @@ int x2ap_eNB_handle_handover_response (instance_t instance,
   x2ap_eNB_data = x2ap_get_eNB(NULL, assoc_id, 0);
   DevAssert(x2ap_eNB_data != NULL);
 
+  instance_p = x2ap_eNB_get_instance(instance);
+  DevAssert(instance_p != NULL);
 
   msg = itti_alloc_new_message(TASK_X2AP, X2AP_HANDOVER_REQ_ACK);
-  /* TODO: fill the message */
-  //extern int x2id_to_source_rnti[1];
-  //X2AP_HANDOVER_REQ_ACK(m).source_x2id = x2HandoverRequestAck->old_eNB_UE_X2AP_ID;
-  //X2AP_HANDOVER_REQ_ACK(m).source_rnti = x2id_to_source_rnti[x2HandoverRequestAck->old_eNB_UE_X2AP_ID];
+
+  X2AP_FIND_PROTOCOLIE_BY_ID(X2AP_HandoverRequestAcknowledge_IEs_t, ie, x2HandoverRequestAck,
+                             X2AP_ProtocolIE_ID_id_Old_eNB_UE_X2AP_ID, true);
+
+  if (ie == NULL ) {
+    X2AP_ERROR("%s %d: ie is a NULL pointer \n",__FILE__,__LINE__);
+    return -1;
+  }
+
+  id_source = ie->value.choice.UE_X2AP_ID;
+
+  X2AP_FIND_PROTOCOLIE_BY_ID(X2AP_HandoverRequestAcknowledge_IEs_t, ie, x2HandoverRequestAck,
+                             X2AP_ProtocolIE_ID_id_New_eNB_UE_X2AP_ID, true);
+
+  if (ie == NULL ) {
+    X2AP_ERROR("%s %d: ie is a NULL pointer \n",__FILE__,__LINE__);
+    return -1;
+  }
+
+  id_target = ie->value.choice.UE_X2AP_ID_1;
+
+  ue_id = id_source;
+
+  if (id_source != x2ap_id_get_id_source(&instance_p->id_manager, ue_id)) {
+    X2AP_ERROR("incorrect X2AP IDs for UE (old ID %d new ID %d)\n", id_source, id_target);
+    exit(1);
+  }
+
+  rnti = x2ap_id_get_rnti(&instance_p->id_manager, ue_id);
+
+  /* id_target is a new information, store it */
+  x2ap_set_ids(&instance_p->id_manager, ue_id, rnti, id_source, id_target);
+
+  X2AP_HANDOVER_REQ_ACK(msg).rnti = rnti;
 
   X2AP_FIND_PROTOCOLIE_BY_ID(X2AP_HandoverRequestAcknowledge_IEs_t, ie, x2HandoverRequestAck,
                              X2AP_ProtocolIE_ID_id_TargeteNBtoSource_eNBTransparentContainer, true);
@@ -723,6 +798,81 @@ int x2ap_eNB_handle_handover_response (instance_t instance,
   memcpy(X2AP_HANDOVER_REQ_ACK(msg).rrc_buffer, c->buf, c->size);
   X2AP_HANDOVER_REQ_ACK(msg).rrc_buffer_size = c->size;
 
-  itti_send_msg_to_task(TASK_RRC_ENB, x2ap_eNB_data->x2ap_eNB_instance->instance, msg);
+  itti_send_msg_to_task(TASK_RRC_ENB, instance_p->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_instance_t                 *instance_p;
+  x2ap_eNB_data_t                     *x2ap_eNB_data;
+  MessageDef                          *msg;
+  int                                 ue_id;
+  int                                 id_source;
+  int                                 id_target;
+
+  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);
+
+  instance_p = x2ap_eNB_get_instance(instance);
+  DevAssert(instance_p != 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);
+
+  if (ie == NULL ) {
+    X2AP_ERROR("%s %d: ie is a NULL pointer \n",__FILE__,__LINE__);
+    return -1;
+  }
+
+  id_source = 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);
+
+  if (ie == NULL ) {
+    X2AP_ERROR("%s %d: ie is a NULL pointer \n",__FILE__,__LINE__);
+    return -1;
+  }
+
+  id_target = ie->value.choice.UE_X2AP_ID_1;
+
+  ue_id = id_source;
+
+  if (id_target != x2ap_id_get_id_target(&instance_p->id_manager, ue_id)) {
+    X2AP_ERROR("UE context release: bad id_target for UE %x (id_source %d) expected %d got %d\n",
+               x2ap_id_get_rnti(&instance_p->id_manager, ue_id),
+               id_source,
+               x2ap_id_get_id_target(&instance_p->id_manager, ue_id),
+               id_target);
+  }
+
+  X2AP_UE_CONTEXT_RELEASE(msg).rnti = x2ap_id_get_rnti(&instance_p->id_manager, ue_id);
+
+  itti_send_msg_to_task(TASK_RRC_ENB, instance_p->instance, msg);
+
+  x2ap_release_id(&instance_p->id_manager, ue_id);
+
   return 0;
 }
diff --git a/openair2/X2AP/x2ap_eNB_handler.h b/openair2/X2AP/x2ap_eNB_handler.h
index e4dac14e8e637078880ac10dd25b442a81cce51f..402212f62d78e453777d4bfa40d5fda7dbd0a741 100644
--- a/openair2/X2AP/x2ap_eNB_handler.h
+++ b/openair2/X2AP/x2ap_eNB_handler.h
@@ -31,7 +31,7 @@
 
 #include "x2ap_eNB_defs.h"
 
-void x2ap_handle_x2_setup_message(x2ap_eNB_data_t *eNB_desc_p, int sctp_shutdown);
+void x2ap_handle_x2_setup_message(x2ap_eNB_instance_t *instance_p, x2ap_eNB_data_t *eNB_desc_p, int sctp_shutdown);
 
 int x2ap_eNB_handle_message(instance_t instance, uint32_t assoc_id, int32_t stream,
                             const uint8_t * const data, const uint32_t data_length);
diff --git a/openair2/X2AP/x2ap_eNB_management_procedures.c b/openair2/X2AP/x2ap_eNB_management_procedures.c
index 670581406b9e2190ac0233bd9b5b797fc66ef521..137bad40add81872cb8641270131f0f0f97cb345 100644
--- a/openair2/X2AP/x2ap_eNB_management_procedures.c
+++ b/openair2/X2AP/x2ap_eNB_management_procedures.c
@@ -169,23 +169,6 @@ x2ap_eNB_instance_t *x2ap_eNB_get_instance(instance_t instance)
   return NULL;
 }
 
-x2ap_eNB_instance_t *x2ap_eNB_pci_get_instance(uint32_t pci)
-{
-  x2ap_eNB_instance_t *temp = NULL;
-
-  STAILQ_FOREACH(temp, &x2ap_eNB_internal_data.x2ap_eNB_instances_head,
-                 x2ap_eNB_entries) {
-    for (int i=0; i<temp->num_cc;i++) {
-      if (temp->Nid_target_cell[i] == pci) {
-        /* Matching occurence */
-        return temp;
-      }
-    }
-  }
-
-  return NULL;
-}
-
 /// utility functions 
 
 void x2ap_dump_eNB (x2ap_eNB_data_t  * eNB_ref);
@@ -222,6 +205,22 @@ void x2ap_dump_eNB (x2ap_eNB_data_t  * eNB_ref) {
   indent--;
 }
 
+x2ap_eNB_data_t  * x2ap_is_eNB_pci_in_list (const uint32_t pci)
+{
+  x2ap_eNB_instance_t    *inst;
+  struct x2ap_eNB_data_s *elm;
+
+  STAILQ_FOREACH(inst, &x2ap_eNB_internal_data.x2ap_eNB_instances_head, x2ap_eNB_entries) {
+    RB_FOREACH(elm, x2ap_enb_map, &inst->x2ap_enb_head) {
+      for (int i = 0; i<elm->num_cc; i++) {
+        if (elm->Nid_cell[i] == pci) {
+          return elm;
+        }
+      }
+    }
+  }
+  return NULL;
+}
 
 x2ap_eNB_data_t  * x2ap_is_eNB_id_in_list (const uint32_t eNB_id)
 {
diff --git a/openair2/X2AP/x2ap_eNB_management_procedures.h b/openair2/X2AP/x2ap_eNB_management_procedures.h
index 80234159515f401f439b47ed0695b9223963cc69..bcad728aaaf328fd7b15965c229c6392eabb83b5 100644
--- a/openair2/X2AP/x2ap_eNB_management_procedures.h
+++ b/openair2/X2AP/x2ap_eNB_management_procedures.h
@@ -37,8 +37,6 @@ void x2ap_eNB_insert_new_instance(x2ap_eNB_instance_t *new_instance_p);
 
 x2ap_eNB_instance_t *x2ap_eNB_get_instance(uint8_t mod_id);
 
-x2ap_eNB_instance_t *x2ap_eNB_pci_get_instance(uint32_t pci);
-
 uint16_t x2ap_eNB_fetch_add_global_cnx_id(void);
 
 void x2ap_eNB_prepare_internal_data(void);
@@ -47,6 +45,8 @@ x2ap_eNB_data_t* x2ap_is_eNB_id_in_list(uint32_t eNB_id);
 
 x2ap_eNB_data_t* x2ap_is_eNB_assoc_id_in_list(uint32_t sctp_assoc_id);
 
+x2ap_eNB_data_t* x2ap_is_eNB_pci_in_list (const uint32_t pci);
+
 struct x2ap_eNB_data_s *x2ap_get_eNB(x2ap_eNB_instance_t *instance_p,
                                      int32_t assoc_id,
                                      uint16_t cnx_id);
diff --git a/openair2/X2AP/x2ap_ids.c b/openair2/X2AP/x2ap_ids.c
new file mode 100644
index 0000000000000000000000000000000000000000..b5607821fe5d97ef6c026000236c738740134963
--- /dev/null
+++ b/openair2/X2AP/x2ap_ids.c
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+#include "x2ap_ids.h"
+
+#include <string.h>
+
+void x2ap_id_manager_init(x2ap_id_manager *m)
+{
+  int i;
+  memset(m, 0, sizeof(x2ap_id_manager));
+  for (i = 0; i < X2AP_MAX_IDS; i++)
+    m->ids[i].rnti = -1;
+}
+
+int x2ap_allocate_new_id(x2ap_id_manager *m)
+{
+  int i;
+  for (i = 0; i < X2AP_MAX_IDS; i++)
+    if (m->ids[i].rnti == -1) {
+      m->ids[i].rnti = 0;
+      m->ids[i].id_source = -1;
+      m->ids[i].id_target = -1;
+      return i;
+    }
+  return -1;
+}
+
+void x2ap_release_id(x2ap_id_manager *m, int id)
+{
+  m->ids[id].rnti = -1;
+}
+
+int x2ap_find_id(x2ap_id_manager *m, int id_source, int id_target)
+{
+  int i;
+  for (i = 0; i < X2AP_MAX_IDS; i++)
+    if (m->ids[i].rnti != -1 &&
+        m->ids[i].id_source == id_source &&
+        m->ids[i].id_target == id_target)
+      return i;
+  return -1;
+}
+
+int x2ap_find_id_from_rnti(x2ap_id_manager *m, int rnti)
+{
+  int i;
+  for (i = 0; i < X2AP_MAX_IDS; i++)
+    if (m->ids[i].rnti == rnti)
+      return i;
+  return -1;
+}
+
+void x2ap_set_ids(x2ap_id_manager *m, int ue_id, int rnti, int id_source, int id_target)
+{
+  m->ids[ue_id].rnti      = rnti;
+  m->ids[ue_id].id_source = id_source;
+  m->ids[ue_id].id_target = id_target;
+}
+
+int x2ap_id_get_id_source(x2ap_id_manager *m, int ue_id)
+{
+  return m->ids[ue_id].id_source;
+}
+
+int x2ap_id_get_id_target(x2ap_id_manager *m, int ue_id)
+{
+  return m->ids[ue_id].id_target;
+}
+
+int x2ap_id_get_rnti(x2ap_id_manager *m, int ue_id)
+{
+  return m->ids[ue_id].rnti;
+}
diff --git a/openair2/X2AP/x2ap_ids.h b/openair2/X2AP/x2ap_ids.h
new file mode 100644
index 0000000000000000000000000000000000000000..2232308f73dd944cd578e327467edcd017d67e6d
--- /dev/null
+++ b/openair2/X2AP/x2ap_ids.h
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+#ifndef X2AP_IDS_H_
+#define X2AP_IDS_H_
+
+#define X2AP_MAX_IDS	16
+
+typedef struct {
+  int rnti;             /* -1 when free */
+  int id_source;
+  int id_target;
+} x2ap_id;
+
+typedef struct {
+  x2ap_id ids[X2AP_MAX_IDS];
+} x2ap_id_manager;
+
+void x2ap_id_manager_init(x2ap_id_manager *m);
+int x2ap_allocate_new_id(x2ap_id_manager *m);
+void x2ap_release_id(x2ap_id_manager *m, int id);
+int x2ap_find_id(x2ap_id_manager *, int id_source, int id_target);
+int x2ap_find_id_from_rnti(x2ap_id_manager *, int rnti);
+void x2ap_set_ids(x2ap_id_manager *m, int ue_id, int rnti, int id_source, int id_target);
+int x2ap_id_get_id_source(x2ap_id_manager *m, int ue_id);
+int x2ap_id_get_id_target(x2ap_id_manager *m, int ue_id);
+int x2ap_id_get_rnti(x2ap_id_manager *m, int ue_id);
+
+#endif /* X2AP_IDS_H_ */
diff --git a/openair3/SCTP/sctp_eNB_itti_messaging.c b/openair3/SCTP/sctp_eNB_itti_messaging.c
index d2064c74c08671b215bbca98525ca9b58be91d47..9c24da73706a2331d00280371d94d6437ae1fafb 100644
--- a/openair3/SCTP/sctp_eNB_itti_messaging.c
+++ b/openair3/SCTP/sctp_eNB_itti_messaging.c
@@ -38,7 +38,7 @@ int sctp_itti_send_init_msg_multi_cnf(task_id_t task_id, instance_t instance, in
   return itti_send_msg_to_task(task_id, instance, message_p);
 }
 
-int sctp_itti_send_new_message_ind(task_id_t task_id, uint32_t assoc_id, uint8_t *buffer,
+int sctp_itti_send_new_message_ind(task_id_t task_id, instance_t instance, uint32_t assoc_id, uint8_t *buffer,
                                    uint32_t buffer_length, uint16_t stream)
 {
   MessageDef      *message_p;
@@ -57,7 +57,7 @@ int sctp_itti_send_new_message_ind(task_id_t task_id, uint32_t assoc_id, uint8_t
   sctp_data_ind_p->buffer_length = buffer_length;
   sctp_data_ind_p->assoc_id      = assoc_id;
 
-  return itti_send_msg_to_task(task_id, INSTANCE_DEFAULT, message_p);
+  return itti_send_msg_to_task(task_id, instance, message_p);
 }
 
 int sctp_itti_send_association_resp(task_id_t task_id, instance_t instance,
diff --git a/openair3/SCTP/sctp_eNB_itti_messaging.h b/openair3/SCTP/sctp_eNB_itti_messaging.h
index 49d2056a3cc254975c18fe469554d50d07bd8379..b4f1a0ee692ce858ca7ca464cc08a4c8429645f8 100644
--- a/openair3/SCTP/sctp_eNB_itti_messaging.h
+++ b/openair3/SCTP/sctp_eNB_itti_messaging.h
@@ -24,7 +24,7 @@
 
 int sctp_itti_send_init_msg_multi_cnf(task_id_t task_id, instance_t instance, int multi_sd);
 
-int sctp_itti_send_new_message_ind(task_id_t task_id, uint32_t assoc_id, uint8_t *buffer,
+int sctp_itti_send_new_message_ind(task_id_t task_id, instance_t instance, uint32_t assoc_id, uint8_t *buffer,
                                    uint32_t buffer_length, uint16_t stream);
 
 int sctp_itti_send_association_resp(task_id_t task_id, instance_t instance,
diff --git a/openair3/SCTP/sctp_eNB_task.c b/openair3/SCTP/sctp_eNB_task.c
index b0c18c36923b1e9a65e3c548258d8de1de024cb7..68fa34f810d98aa2c1bf2dbf4b9a42432d106ff9 100644
--- a/openair3/SCTP/sctp_eNB_task.c
+++ b/openair3/SCTP/sctp_eNB_task.c
@@ -324,7 +324,12 @@ sctp_handle_new_association_req_multi(
         }
     }
 
-    ns = sctp_peeloff(sd,assoc_id);
+    ns = sctp_peeloff(sd, assoc_id);
+    if (ns == -1) {
+      perror("sctp_peeloff");
+      printf("sctp_peeloff: sd=%d assoc_id=%d\n", sd, assoc_id);
+      exit(1);
+    }
 
     sctp_cnx = calloc(1, sizeof(*sctp_cnx));
 
@@ -1003,7 +1008,7 @@ sctp_eNB_read_from_socket(
                    sinfo.sinfo_assoc_id, sctp_cnx->sd, n, ntohs(addr.sin_port),
                    sinfo.sinfo_stream, ntohl(sinfo.sinfo_ppid));
 
-        sctp_itti_send_new_message_ind(sctp_cnx->task_id,
+        sctp_itti_send_new_message_ind(sctp_cnx->task_id, sctp_cnx->instance,
                                        sinfo.sinfo_assoc_id,
                                        buffer, n, sinfo.sinfo_stream);
     }