From 0ef04198b5ba02ced02ec9fe48d04b97e7e101a0 Mon Sep 17 00:00:00 2001
From: winckel <winckel@eurecom.fr>
Date: Wed, 4 Dec 2013 08:32:21 +0000
Subject: [PATCH] Added a mechanism to wait for eNB MME link to be establish
 for all eNB before starting L2L1 task.

git-svn-id: http://svn.eurecom.fr/openair4G/trunk@4577 818b1a75-f10b-46b9-bf7c-635c3b92a50f
---
 openair-cn/S1AP/s1ap_eNB.c            |  78 +++++----
 openair-cn/S1AP/s1ap_eNB_defs.h       |   6 +
 openair-cn/S1AP/s1ap_eNB_handlers.c   |  33 ++++
 openair-cn/S1AP/s1ap_eNB_handlers.h   |   2 +
 openair-cn/SCTP/sctp_eNB_task.c       |   4 +-
 openair2/COMMON/s1ap_messages_def.h   |   7 +-
 openair2/COMMON/s1ap_messages_types.h | 174 ++++++++++---------
 targets/SIMU/USER/oaisim.c            | 231 ++++++++++++++++++--------
 targets/SIMU/USER/oaisim.h            |   2 +-
 9 files changed, 351 insertions(+), 186 deletions(-)

diff --git a/openair-cn/S1AP/s1ap_eNB.c b/openair-cn/S1AP/s1ap_eNB.c
index 2e18f7ccbd6..6a74c6e24db 100644
--- a/openair-cn/S1AP/s1ap_eNB.c
+++ b/openair-cn/S1AP/s1ap_eNB.c
@@ -66,7 +66,7 @@ static int s1ap_eNB_generate_s1_setup_request(
     s1ap_eNB_instance_t *instance_p, s1ap_eNB_mme_data_t *s1ap_mme_data_p);
 
 static
-void s1ap_eNB_handle_register_eNB(instance_t instance, s1ap_register_eNB_t *s1ap_register_eNB);
+void s1ap_eNB_handle_register_eNB(instance_t instance, s1ap_register_enb_req_t *s1ap_register_eNB);
 static
 void s1ap_eNB_handle_sctp_association_resp(instance_t instance, sctp_new_association_resp_t *sctp_new_association_resp);
 
@@ -126,47 +126,56 @@ static void s1ap_eNB_register_mme(s1ap_eNB_instance_t *instance_p,
      * but not yet associated.
      */
     RB_INSERT(s1ap_mme_map, &instance_p->s1ap_mme_head, s1ap_mme_data_p);
+    s1ap_mme_data_p->state = S1AP_ENB_STATE_WAITING;
+    instance_p->s1ap_mme_nb ++;
+    instance_p->s1ap_mme_pending_nb ++;
 
     itti_send_msg_to_task(TASK_SCTP, instance_p->instance, message_p);
 }
 
 static
-void s1ap_eNB_handle_register_eNB(instance_t instance, s1ap_register_eNB_t *s1ap_register_eNB)
+void s1ap_eNB_handle_register_eNB(instance_t instance, s1ap_register_enb_req_t *s1ap_register_eNB)
 {
     s1ap_eNB_instance_t *new_instance;
     uint8_t index;
 
     DevAssert(s1ap_register_eNB != NULL);
 
-    /* Look if the provided instance already exists
-     * If so notify user...
-     */
+    /* Look if the provided instance already exists */
     new_instance = s1ap_eNB_get_instance(instance);
-    DevAssert(new_instance == NULL);
-
-    new_instance = calloc(1, sizeof(s1ap_eNB_instance_t));
-    DevAssert(new_instance != NULL);
-
-    RB_INIT(&new_instance->s1ap_ue_head);
-    RB_INIT(&new_instance->s1ap_mme_head);
-
-    /* Copy usefull parameters */
-    new_instance->instance    = instance;
-    new_instance->eNB_name    = s1ap_register_eNB->eNB_name;
-    new_instance->eNB_id      = s1ap_register_eNB->eNB_id;
-    new_instance->cell_type   = s1ap_register_eNB->cell_type;
-    new_instance->tac         = s1ap_register_eNB->tac;
-    new_instance->mcc         = s1ap_register_eNB->mcc;
-    new_instance->mnc         = s1ap_register_eNB->mnc;
-    new_instance->default_drx = s1ap_register_eNB->default_drx;
-
-    /* Add the new instance to the list of eNB (meaningfull in virtual mode) */
-    s1ap_eNB_insert_new_instance(new_instance);
-
-    S1AP_DEBUG("Registered new eNB[%d] and %s eNB id %u\n",
-               instance,
-               s1ap_register_eNB->cell_type == CELL_MACRO_ENB ? "macro" : "home",
-               s1ap_register_eNB->eNB_id);
+    if (new_instance != NULL) {
+        /* Checks if it is a retry on the same eNB */
+        DevCheck(new_instance->eNB_id == s1ap_register_eNB->eNB_id, new_instance->eNB_id, s1ap_register_eNB->eNB_id, 0);
+        DevCheck(new_instance->cell_type == s1ap_register_eNB->cell_type, new_instance->cell_type, s1ap_register_eNB->cell_type, 0);
+        DevCheck(new_instance->tac == s1ap_register_eNB->tac, new_instance->tac, s1ap_register_eNB->tac, 0);
+        DevCheck(new_instance->mcc == s1ap_register_eNB->mcc, new_instance->mcc, s1ap_register_eNB->mcc, 0);
+        DevCheck(new_instance->mnc == s1ap_register_eNB->mnc, new_instance->mnc, s1ap_register_eNB->mnc, 0);
+        DevCheck(new_instance->default_drx == s1ap_register_eNB->default_drx, new_instance->default_drx, s1ap_register_eNB->default_drx, 0);
+    } else {
+        new_instance = calloc(1, sizeof(s1ap_eNB_instance_t));
+        DevAssert(new_instance != NULL);
+
+        RB_INIT(&new_instance->s1ap_ue_head);
+        RB_INIT(&new_instance->s1ap_mme_head);
+
+        /* Copy usefull parameters */
+        new_instance->instance    = instance;
+        new_instance->eNB_name    = s1ap_register_eNB->eNB_name;
+        new_instance->eNB_id      = s1ap_register_eNB->eNB_id;
+        new_instance->cell_type   = s1ap_register_eNB->cell_type;
+        new_instance->tac         = s1ap_register_eNB->tac;
+        new_instance->mcc         = s1ap_register_eNB->mcc;
+        new_instance->mnc         = s1ap_register_eNB->mnc;
+        new_instance->default_drx = s1ap_register_eNB->default_drx;
+
+        /* Add the new instance to the list of eNB (meaningfull in virtual mode) */
+        s1ap_eNB_insert_new_instance(new_instance);
+
+        S1AP_DEBUG("Registered new eNB[%d] and %s eNB id %u\n",
+                   instance,
+                   s1ap_register_eNB->cell_type == CELL_MACRO_ENB ? "macro" : "home",
+                   s1ap_register_eNB->eNB_id);
+    }
 
     DevCheck(s1ap_register_eNB->nb_mme <= S1AP_MAX_NB_MME_IP_ADDRESS,
              S1AP_MAX_NB_MME_IP_ADDRESS, s1ap_register_eNB->nb_mme, 0);
@@ -198,6 +207,9 @@ void s1ap_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);
+
+        s1ap_handle_s1_setup_message(s1ap_mme_data_p);
+
         return;
     }
 
@@ -238,14 +250,14 @@ void *s1ap_eNB_task(void *arg)
             case TERMINATE_MESSAGE:
                 itti_exit_task();
                 break;
-            case S1AP_REGISTER_ENB: {
+            case S1AP_REGISTER_ENB_REQ: {
                 /* Register a new eNB.
                  * in Virtual mode eNBs will be distinguished using the mod_id/
                  * Each eNB has to send an S1AP_REGISTER_ENB message with its
                  * own parameters.
                  */
                 s1ap_eNB_handle_register_eNB(ITTI_MESSAGE_GET_INSTANCE(received_msg),
-                                             &received_msg->ittiMsg.s1ap_register_eNB);
+                                             &S1AP_REGISTER_ENB_REQ(received_msg));
             } break;
             case SCTP_NEW_ASSOCIATION_RESP: {
                 s1ap_eNB_handle_sctp_association_resp(ITTI_MESSAGE_GET_INSTANCE(received_msg),
@@ -318,6 +330,8 @@ static int s1ap_eNB_generate_s1_setup_request(
     MCC_MNC_TO_PLMNID(instance_p->mcc, instance_p->mnc,
                       &s1SetupRequest_p->global_ENB_ID.pLMNidentity);
 
+    S1AP_INFO("%d -> %02x%02x%02x\n", instance_p->eNB_id, s1SetupRequest_p->global_ENB_ID.eNB_ID.choice.macroENB_ID.buf[0], s1SetupRequest_p->global_ENB_ID.eNB_ID.choice.macroENB_ID.buf[1], s1SetupRequest_p->global_ENB_ID.eNB_ID.choice.macroENB_ID.buf[2]);
+
     INT16_TO_OCTET_STRING(instance_p->tac, &ta.tAC);
     MCC_MNC_TO_TBCD(instance_p->mcc, instance_p->mnc, &plmnIdentity);
 
diff --git a/openair-cn/S1AP/s1ap_eNB_defs.h b/openair-cn/S1AP/s1ap_eNB_defs.h
index 8c2f2530b8f..1a1609d0ceb 100644
--- a/openair-cn/S1AP/s1ap_eNB_defs.h
+++ b/openair-cn/S1AP/s1ap_eNB_defs.h
@@ -172,6 +172,12 @@ typedef struct s1ap_eNB_instance_s {
      */
     STAILQ_ENTRY(s1ap_eNB_instance_s) s1ap_eNB_entries;
 
+    /* Number of MME requested by eNB (tree size) */
+    uint32_t s1ap_mme_nb;
+    /* Number of MME for which association is pending */
+    uint32_t s1ap_mme_pending_nb;
+    /* Number of MME successfully associated to eNB */
+    uint32_t s1ap_mme_associated_nb;
     /* Tree of S1AP MME associations ordered by association ID */
     RB_HEAD(s1ap_mme_map, s1ap_eNB_mme_data_s) s1ap_mme_head;
 
diff --git a/openair-cn/S1AP/s1ap_eNB_handlers.c b/openair-cn/S1AP/s1ap_eNB_handlers.c
index 52bbbf807ba..c24cb96cc52 100644
--- a/openair-cn/S1AP/s1ap_eNB_handlers.c
+++ b/openair-cn/S1AP/s1ap_eNB_handlers.c
@@ -55,6 +55,7 @@
 
 #include "s1ap_eNB_default_values.h"
 
+#include "assertions.h"
 #include "conversions.h"
 
 static
@@ -132,6 +133,24 @@ static const char *direction2String[] = {
     "UnSuccessfull outcome", /* successfull outcome */
 };
 
+void s1ap_handle_s1_setup_message(s1ap_eNB_mme_data_t *mme_desc_p) {
+    /* Check that at least one setup message is pending */
+    DevCheck(mme_desc_p->s1ap_eNB_instance->s1ap_mme_pending_nb > 0, mme_desc_p->s1ap_eNB_instance->instance,
+             mme_desc_p->s1ap_eNB_instance->s1ap_mme_pending_nb, 0);
+    /* Decrease pending messages number */
+    mme_desc_p->s1ap_eNB_instance->s1ap_mme_pending_nb --;
+
+    /* If there are no more pending messages, inform eNB app */
+    if (mme_desc_p->s1ap_eNB_instance->s1ap_mme_pending_nb == 0)
+    {
+      MessageDef                 *message_p;
+
+      message_p = itti_alloc_new_message(TASK_S1AP, S1AP_REGISTER_ENB_CNF);
+      S1AP_REGISTER_ENB_CNF(message_p).nb_mme = mme_desc_p->s1ap_eNB_instance->s1ap_mme_associated_nb;
+      itti_send_msg_to_task(TASK_ENB_APP, mme_desc_p->s1ap_eNB_instance->instance, message_p);
+    }
+}
+
 int s1ap_eNB_handle_message(uint32_t assoc_id, int32_t stream,
                             const uint8_t * const data, const uint32_t data_length)
 {
@@ -173,13 +192,25 @@ int s1ap_eNB_handle_s1_setup_failure(uint32_t               assoc_id,
                                      uint32_t               stream,
                                      struct s1ap_message_s *message_p)
 {
+    s1ap_eNB_mme_data_t       *mme_desc_p;
+
     /* S1 Setup Failure == Non UE-related procedure -> stream 0 */
     if (stream != 0) {
         S1AP_WARN("[SCTP %d] Received s1 setup failure on stream != 0 (%d)\n",
                   assoc_id, stream);
     }
+
+    if ((mme_desc_p = s1ap_eNB_get_MME(NULL, assoc_id, 0)) == NULL) {
+        S1AP_ERROR("[SCTP %d] Received S1 setup response for non existing "
+                   "MME context\n", assoc_id);
+        return -1;
+    }
+
     S1AP_ERROR("Received s1 setup failure for MME... please check your parameters\n");
 
+    mme_desc_p->state = S1AP_ENB_STATE_WAITING;
+    s1ap_handle_s1_setup_message(mme_desc_p);
+
     return 0;
 }
 
@@ -278,6 +309,8 @@ int s1ap_eNB_handle_s1_setup_response(uint32_t               assoc_id,
      * Mark the association as UP to enable UE contexts creation.
      */
     mme_desc_p->state = S1AP_ENB_STATE_CONNECTED;
+    mme_desc_p->s1ap_eNB_instance->s1ap_mme_associated_nb ++;
+    s1ap_handle_s1_setup_message(mme_desc_p);
 
 #if 0
     /* We call back our self
diff --git a/openair-cn/S1AP/s1ap_eNB_handlers.h b/openair-cn/S1AP/s1ap_eNB_handlers.h
index 0ae1cb2fcb1..c708517b73a 100644
--- a/openair-cn/S1AP/s1ap_eNB_handlers.h
+++ b/openair-cn/S1AP/s1ap_eNB_handlers.h
@@ -31,6 +31,8 @@
 #ifndef S1AP_ENB_HANDLERS_H_
 #define S1AP_ENB_HANDLERS_H_
 
+void s1ap_handle_s1_setup_message(s1ap_eNB_mme_data_t *mme_desc_p);
+
 int s1ap_eNB_handle_message(uint32_t assoc_id, int32_t stream,
                             const uint8_t * const data, const uint32_t data_length);
 
diff --git a/openair-cn/SCTP/sctp_eNB_task.c b/openair-cn/SCTP/sctp_eNB_task.c
index f7427a9dfa6..cc1d8dd950d 100644
--- a/openair-cn/SCTP/sctp_eNB_task.c
+++ b/openair-cn/SCTP/sctp_eNB_task.c
@@ -237,7 +237,7 @@ void sctp_handle_new_association_req(
                 close(sd);
                 return;
             } else {
-                SCTP_DEBUG("connectx in progress...\n");
+                SCTP_DEBUG("connectx %d in progress...\n", assoc_id);
             }
         }
     } else {
@@ -270,6 +270,7 @@ void sctp_handle_new_association_req(
     sctp_cnx->cnx_id   = sctp_new_association_req_p->ulp_cnx_id;
     sctp_cnx->ppid     = sctp_new_association_req_p->ppid;
     sctp_cnx->instance = instance;
+    sctp_cnx->assoc_id = assoc_id;
 
     /* Insert new element at end of list */
     STAILQ_INSERT_TAIL(&sctp_cnx_list, sctp_cnx, entries);
@@ -412,6 +413,7 @@ inline void sctp_eNB_read_from_socket(struct sctp_cnx_list_elm_s *sctp_cnx)
                 sctp_cnx->cnx_id, SCTP_STATE_UNREACHABLE, 0, 0);
 
             close(sctp_cnx->sd);
+            STAILQ_REMOVE(&sctp_cnx_list, sctp_cnx, sctp_cnx_list_elm_s, entries);
             sctp_nb_cnx--;
             free(sctp_cnx);
         } else {
diff --git a/openair2/COMMON/s1ap_messages_def.h b/openair2/COMMON/s1ap_messages_def.h
index 019dc21066e..3e41dae2a99 100644
--- a/openair2/COMMON/s1ap_messages_def.h
+++ b/openair2/COMMON/s1ap_messages_def.h
@@ -1,9 +1,12 @@
 /* eNB application layer -> S1AP messages */
-MESSAGE_DEF(S1AP_REGISTER_ENB              , MESSAGE_PRIORITY_MED, s1ap_register_eNB_t              , s1ap_register_eNB )
+MESSAGE_DEF(S1AP_REGISTER_ENB_REQ          , MESSAGE_PRIORITY_MED, s1ap_register_enb_req_t          , s1ap_register_enb_req)
+
+/* S1AP -> eNB application layer messages */
+MESSAGE_DEF(S1AP_REGISTER_ENB_CNF          , MESSAGE_PRIORITY_MED, s1ap_register_enb_cnf_t          , s1ap_register_enb_cnf)
 
 /* RRC -> S1AP messages */
 MESSAGE_DEF(S1AP_NAS_FIRST_REQ             , MESSAGE_PRIORITY_MED, s1ap_nas_first_req_t             , s1ap_nas_first_req)
-MESSAGE_DEF(S1AP_UPLINK_NAS                , MESSAGE_PRIORITY_MED, s1ap_uplink_nas_t                , s1ap_uplink_nas   )
+MESSAGE_DEF(S1AP_UPLINK_NAS                , MESSAGE_PRIORITY_MED, s1ap_uplink_nas_t                , s1ap_uplink_nas)
 MESSAGE_DEF(S1AP_UE_CAPABILITIES_IND       , MESSAGE_PRIORITY_MED, s1ap_ue_cap_info_ind_t           , s1ap_ue_cap_info_ind)
 MESSAGE_DEF(S1AP_INITIAL_CONTEXT_SETUP_RESP, MESSAGE_PRIORITY_MED, s1ap_initial_context_setup_resp_t, s1ap_initial_context_setup_resp)
 MESSAGE_DEF(S1AP_INITIAL_CONTEXT_SETUP_FAIL, MESSAGE_PRIORITY_MED, s1ap_initial_context_setup_fail_t, s1ap_initial_context_setup_fail)
diff --git a/openair2/COMMON/s1ap_messages_types.h b/openair2/COMMON/s1ap_messages_types.h
index 223b69a7bbf..898f5989eff 100644
--- a/openair2/COMMON/s1ap_messages_types.h
+++ b/openair2/COMMON/s1ap_messages_types.h
@@ -4,6 +4,10 @@
 //-------------------------------------------------------------------------------------------//
 // Defines to access message fields.
 
+#define S1AP_REGISTER_ENB_REQ(mSGpTR)           (mSGpTR)->ittiMsg.s1ap_register_enb_req
+
+#define S1AP_REGISTER_ENB_CNF(mSGpTR)           (mSGpTR)->ittiMsg.s1ap_register_enb_cnf
+
 #define S1AP_NAS_FIRST_REQ(mSGpTR)              (mSGpTR)->ittiMsg.s1ap_nas_first_req
 #define S1AP_UPLINK_NAS(mSGpTR)                 (mSGpTR)->ittiMsg.s1ap_uplink_nas
 #define S1AP_UE_CAPABILITIES_IND(mSGpTR)        (mSGpTR)->ittiMsg.s1ap_ue_cap_info_ind
@@ -22,6 +26,24 @@
 #define S1AP_UE_CONTEXT_RELEASE_REQ(mSGpTR)     (mSGpTR)->ittiMsg.s1ap_ue_release_req
 
 //-------------------------------------------------------------------------------------------//
+/* Maximum number of e-rabs to be setup/deleted in a single message.
+ * Even if only one bearer will be modified by message.
+ */
+#define S1AP_MAX_E_RAB  11
+
+/* Length of the transport layer address string
+ * 160 bits / 8 bits by char.
+ */
+#define S1AP_TRANSPORT_LAYER_ADDRESS_SIZE (160 / 8)
+
+#define S1AP_MAX_NB_MME_IP_ADDRESS 10
+#define S1AP_IMSI_LENGTH           16
+
+/* Security key length used within eNB
+ * Even if only 16 bytes will be effectively used,
+ * the key length is 32 bytes (256 bits)
+ */
+#define SECURITY_KEY_LENGTH 32
 
 typedef enum cell_type_e {
     CELL_MACRO_ENB,
@@ -63,7 +85,7 @@ typedef struct net_ip_address_s {
 
 typedef uint64_t bitrate_t;
 
-typedef struct {
+typedef struct ambr_s {
     bitrate_t br_ul;
     bitrate_t br_dl;
 } ambr_t;
@@ -75,19 +97,19 @@ typedef enum priority_level_s {
     PRIORITY_LEVEL_NO_PRIORITY = 15
 } priority_level_t;
 
-typedef enum {
+typedef enum pre_emp_capability_e {
     PRE_EMPTION_CAPABILITY_ENABLED  = 0,
     PRE_EMPTION_CAPABILITY_DISABLED = 1,
     PRE_EMPTION_CAPABILITY_MAX,
 } pre_emp_capability_t;
 
-typedef enum {
+typedef enum pre_emp_vulnerability_e {
     PRE_EMPTION_VULNERABILITY_ENABLED  = 0,
     PRE_EMPTION_VULNERABILITY_DISABLED = 1,
     PRE_EMPTION_VULNERABILITY_MAX,
 } pre_emp_vulnerability_t;
 
-typedef struct {
+typedef struct allocation_retention_priority_s {
     priority_level_t        priority_level;
     pre_emp_capability_t    pre_emp_capability;
     pre_emp_vulnerability_t pre_emp_vulnerability;
@@ -98,25 +120,6 @@ typedef struct security_capabilities_s {
     uint16_t integrity_algorithms;
 } security_capabilities_t;
 
-/* Maximum number of e-rabs to be setup/deleted in a single message.
- * Even if only one bearer will be modified by message.
- */
-#define S1AP_MAX_E_RAB  11
-
-/* Length of the transport layer address string
- * 160 bits / 8 bits by char.
- */
-#define S1AP_TRANSPORT_LAYER_ADDRESS_SIZE (160 / 8)
-
-#define S1AP_MAX_NB_MME_IP_ADDRESS 10
-#define S1AP_IMSI_LENGTH           16
-
-/* Security key length used within eNB
- * Even if only 16 bytes will be effectively used,
- * the key length is 32 bytes (256 bits)
- */
-#define SECURITY_KEY_LENGTH 32
-
 /* Provides the establishment cause for the RRC connection request as provided
  * by the upper layers. W.r.t. the cause value names: highPriorityAccess
  * concerns AC11..AC15, ‘mt’ stands for ‘Mobile Terminating’ and ‘mo’ for
@@ -226,7 +229,20 @@ typedef struct e_rab_failed_s {
     //     cause_t cause;
 } e_rab_failed_t;
 
-typedef struct s1ap_register_eNB_s {
+typedef enum s1ap_ue_ctxt_modification_present_s {
+    S1AP_UE_CONTEXT_MODIFICATION_SECURITY_KEY = (1 << 0),
+    S1AP_UE_CONTEXT_MODIFICATION_UE_AMBR      = (1 << 1),
+    S1AP_UE_CONTEXT_MODIFICATION_UE_SECU_CAP  = (1 << 2),
+} s1ap_ue_ctxt_modification_present_t;
+
+typedef enum s1ap_paging_ind_present_s {
+    S1AP_PAGING_IND_PAGING_DRX      = (1 << 0),
+    S1AP_PAGING_IND_PAGING_PRIORITY = (1 << 1),
+} s1ap_paging_ind_present_t;
+
+//-------------------------------------------------------------------------------------------//
+// eNB application layer -> S1AP messages
+typedef struct s1ap_register_enb_req_s {
     /* Unique eNB_id to identify the eNB within EPC.
      * For macro eNB ids this field should be 20 bits long.
      * For home eNB ids this field should be 28 bits long.
@@ -260,7 +276,18 @@ typedef struct s1ap_register_eNB_s {
     uint8_t          nb_mme;
     /* List of MME to connect to */
     net_ip_address_t mme_ip_address[S1AP_MAX_NB_MME_IP_ADDRESS];
-} s1ap_register_eNB_t;
+} s1ap_register_enb_req_t;
+
+//-------------------------------------------------------------------------------------------//
+// S1AP -> eNB application layer messages
+typedef struct s1ap_register_enb_cnf_s {
+    /* Nb of MME connected */
+    uint8_t          nb_mme;
+
+} s1ap_register_enb_cnf_t;
+
+//-------------------------------------------------------------------------------------------//
+// RRC -> S1AP messages
 
 /* The NAS First Req is the first message exchanged between RRC and S1AP
  * for an UE.
@@ -291,38 +318,10 @@ typedef struct s1ap_uplink_nas_s {
     nas_pdu_t nas_pdu;
 } s1ap_uplink_nas_t;
 
-typedef struct s1ap_downlink_nas_s {
-    /* UE id for initial connection to S1AP */
-    uint16_t ue_initial_id;
-
-    /* Unique UE identifier within an eNB */
-    unsigned eNB_ue_s1ap_id:24;
-
-    /* NAS pdu */
-    nas_pdu_t nas_pdu;
-} s1ap_downlink_nas_t;
-
-typedef struct s1ap_initial_context_setup_req_s {
-    /* UE id for initial connection to S1AP */
-    uint16_t ue_initial_id;
-
-    /* eNB ue s1ap id as initialized by S1AP layer */
-    unsigned eNB_ue_s1ap_id:24;
-
-    /* UE aggregate maximum bitrate */
-    ambr_t ue_ambr;
-
-    /* Security algorithms */
-    security_capabilities_t security_capabilities;
-
-    /* Security key */
-    uint8_t security_key[SECURITY_KEY_LENGTH];
-
-    /* Number of e_rab to be setup in the list */
-    uint8_t  nb_of_e_rabs;
-    /* list of e_rab to be setup by RRC layers */
-    e_rab_t  e_rab_param[S1AP_MAX_E_RAB];
-} s1ap_initial_context_setup_req_t;
+typedef struct s1ap_ue_cap_info_ind_s {
+    unsigned  eNB_ue_s1ap_id:24;
+    ue_radio_cap_t ue_radio_cap;
+} s1ap_ue_cap_info_ind_t;
 
 typedef struct s1ap_initial_context_setup_resp_s {
     unsigned  eNB_ue_s1ap_id:24;
@@ -344,21 +343,11 @@ typedef struct s1ap_initial_context_setup_fail_s {
     /* TODO add cause */
 } s1ap_initial_context_setup_fail_t, s1ap_ue_ctxt_modification_fail_t;
 
-typedef struct s1ap_ue_cap_info_ind_s {
-    unsigned  eNB_ue_s1ap_id:24;
-    ue_radio_cap_t ue_radio_cap;
-} s1ap_ue_cap_info_ind_t;
-
-typedef struct s1ap_ue_release_req_s {
+typedef struct s1ap_nas_non_delivery_ind_s {
     unsigned  eNB_ue_s1ap_id:24;
+    nas_pdu_t nas_pdu;
     /* TODO: add cause */
-} s1ap_ue_release_req_t, s1ap_ue_release_resp_t;
-
-typedef enum s1ap_ue_ctxt_modification_present_s {
-    S1AP_UE_CONTEXT_MODIFICATION_SECURITY_KEY = (1 << 0),
-    S1AP_UE_CONTEXT_MODIFICATION_UE_AMBR      = (1 << 1),
-    S1AP_UE_CONTEXT_MODIFICATION_UE_SECU_CAP  = (1 << 2),
-} s1ap_ue_ctxt_modification_present_t;
+} s1ap_nas_non_delivery_ind_t;
 
 typedef struct s1ap_ue_ctxt_modification_req_s {
     unsigned  eNB_ue_s1ap_id:24;
@@ -382,10 +371,40 @@ typedef struct s1ap_ue_ctxt_modification_resp_s {
     unsigned  eNB_ue_s1ap_id:24;
 } s1ap_ue_ctxt_modification_resp_t;
 
-typedef enum s1ap_paging_ind_present_s {
-    S1AP_PAGING_IND_PAGING_DRX      = (1 << 0),
-    S1AP_PAGING_IND_PAGING_PRIORITY = (1 << 1),
-} s1ap_paging_ind_present_t;
+//-------------------------------------------------------------------------------------------//
+// S1AP -> RRC messages
+typedef struct s1ap_downlink_nas_s {
+    /* UE id for initial connection to S1AP */
+    uint16_t ue_initial_id;
+
+    /* Unique UE identifier within an eNB */
+    unsigned eNB_ue_s1ap_id:24;
+
+    /* NAS pdu */
+    nas_pdu_t nas_pdu;
+} s1ap_downlink_nas_t;
+
+typedef struct s1ap_initial_context_setup_req_s {
+    /* UE id for initial connection to S1AP */
+    uint16_t ue_initial_id;
+
+    /* eNB ue s1ap id as initialized by S1AP layer */
+    unsigned eNB_ue_s1ap_id:24;
+
+    /* UE aggregate maximum bitrate */
+    ambr_t ue_ambr;
+
+    /* Security algorithms */
+    security_capabilities_t security_capabilities;
+
+    /* Security key */
+    uint8_t security_key[SECURITY_KEY_LENGTH];
+
+    /* Number of e_rab to be setup in the list */
+    uint8_t  nb_of_e_rabs;
+    /* list of e_rab to be setup by RRC layers */
+    e_rab_t  e_rab_param[S1AP_MAX_E_RAB];
+} s1ap_initial_context_setup_req_t;
 
 typedef struct s1ap_paging_ind_s {
     /* UE identity index value.
@@ -405,10 +424,11 @@ typedef struct s1ap_paging_ind_s {
     paging_priority_t paging_priority;
 } s1ap_paging_ind_t;
 
-typedef struct s1ap_nas_non_delivery_ind_s {
+//-------------------------------------------------------------------------------------------//
+// S1AP <-> RRC messages
+typedef struct s1ap_ue_release_req_s {
     unsigned  eNB_ue_s1ap_id:24;
-    nas_pdu_t nas_pdu;
     /* TODO: add cause */
-} s1ap_nas_non_delivery_ind_t;
+} s1ap_ue_release_req_t, s1ap_ue_release_resp_t;
 
 #endif /* S1AP_MESSAGES_TYPES_H_ */
diff --git a/targets/SIMU/USER/oaisim.c b/targets/SIMU/USER/oaisim.c
index a2b57c5e6f8..07cb090b13a 100644
--- a/targets/SIMU/USER/oaisim.c
+++ b/targets/SIMU/USER/oaisim.c
@@ -69,6 +69,7 @@ char smbv_ip[16];
 
 #include "UTIL/LOG/vcd_signal_dumper.h"
 #include "UTIL/OTG/otg_kpi.h"
+#include "assertions.h"
 
 #if defined(ENABLE_ITTI)
 # include "intertask_interface_init.h"
@@ -372,9 +373,8 @@ static s32 UE_id = 0, eNB_id = 0;
 static s32 RN_id=0;
 #endif
 
-int itti_create_task_successful(void){
-
 #if defined(ENABLE_ITTI)
+int itti_create_task_successful(void){
 # if defined(ENABLE_USE_MME)
   if (itti_create_task(TASK_SCTP, sctp_eNB_task, NULL) < 0) {
       LOG_E(EMU, "Create task failed");
@@ -388,103 +388,180 @@ int itti_create_task_successful(void){
   }
 # endif
 
-  if (itti_create_task(TASK_ENB_APP, eNB_app_task, NULL) < 0) {
+  if (itti_create_task(TASK_L2L1, l2l1_task, NULL) < 0) {
     LOG_E(EMU, "Create task failed");
-    LOG_D(EMU, "Initializing eNB APP task interface: FAILED\n");
+    LOG_D(EMU, "Initializing L2L1 task interface: FAILED\n");
     return -1;
   }
 
-  if (itti_create_task(TASK_L2L1, l2l1_task, NULL) < 0) {
+  /* Last task to create, others task must be ready before its start */
+  if (itti_create_task(TASK_ENB_APP, eNB_app_task, NULL) < 0) {
     LOG_E(EMU, "Create task failed");
-    LOG_D(EMU, "Initializing L2L1 task interface: FAILED\n");
+    LOG_D(EMU, "Initializing eNB APP task interface: FAILED\n");
     return -1;
   }
-
-#endif  
   return 1;
 }
+#endif
 
+#if defined(ENABLE_ITTI)
 /*
- * later, the enb task will be moved from here
+ * later, the eNB task will be moved from here
  */
-void *eNB_app_task(void *args_p) {
-#if defined(ENABLE_ITTI)
-  MessageDef *message_p;
 
 # if defined(ENABLE_USE_MME)
-    /* Trying to register each eNB */
+
+#define ENB_REGISTER_RETRY_DELAY 10
+
+uint32_t eNB_app_register()
+{
+  MessageDef *msg_p;
+  uint32_t register_enb_pending = 0;
 
   for (eNB_id = oai_emulation.info.first_enb_local;
-       (eNB_id < (oai_emulation.info.first_enb_local + oai_emulation.info.nb_enb_local)) && (oai_emulation.info.cli_start_enb[eNB_id] == 1);
-       eNB_id++) {
-      char *mme_address_v4;
-      
-      if (EPC_MODE_ENABLED){
-	  mme_address_v4 = EPC_MODE_MME_ADDRESS;
-      }else {
-	mme_address_v4 = "192.168.12.87";
-      }
-      char *mme_address_v6 = "2001:660:5502:12:30da:829a:2343:b6cf";
-      s1ap_register_eNB_t *s1ap_register_eNB;
-      uint32_t hash;
-      
-      //note:  there is an implicit relationship between the data struct and the message name
-      message_p = itti_alloc_new_message(TASK_ENB_APP, S1AP_REGISTER_ENB);
-      
-      s1ap_register_eNB = &message_p->ittiMsg.s1ap_register_eNB;
-      
-      hash = s1ap_generate_eNB_id();
-
-      /* Some default/random parameters */
-      s1ap_register_eNB->eNB_id      = eNB_id + (hash & 0xFFFF8);
-      s1ap_register_eNB->cell_type   = CELL_MACRO_ENB;
-      s1ap_register_eNB->tac         = 0;
-      s1ap_register_eNB->mcc         = 208;
-      s1ap_register_eNB->mnc         = 34;
-      s1ap_register_eNB->default_drx = PAGING_DRX_256;
-      s1ap_register_eNB->nb_mme      = 1;
-      s1ap_register_eNB->mme_ip_address[0].ipv4 = 1;
-      s1ap_register_eNB->mme_ip_address[0].ipv6 = 0;
-      memcpy(s1ap_register_eNB->mme_ip_address[0].ipv4_address, mme_address_v4,
-	     strlen(mme_address_v4));
-      memcpy(s1ap_register_eNB->mme_ip_address[0].ipv6_address, mme_address_v6,
-	     strlen(mme_address_v6));
-      
-      itti_send_msg_to_task(TASK_S1AP, eNB_id, message_p);
+     (eNB_id < (oai_emulation.info.first_enb_local + oai_emulation.info.nb_enb_local)) && (oai_emulation.info.cli_start_enb[eNB_id] == 1);
+     eNB_id++) {
+    char *mme_address_v4;
+
+    if (EPC_MODE_ENABLED){
+      mme_address_v4 = EPC_MODE_MME_ADDRESS;
+    } else {
+      mme_address_v4 = "192.168.12.87";
     }
+    char *mme_address_v6 = "2001:660:5502:12:30da:829a:2343:b6cf";
+    s1ap_register_enb_req_t *s1ap_register_eNB;
+    uint32_t hash;
+
+    //note:  there is an implicit relationship between the data struct and the message name
+    msg_p = itti_alloc_new_message(TASK_ENB_APP, S1AP_REGISTER_ENB_REQ);
+
+    s1ap_register_eNB = &S1AP_REGISTER_ENB_REQ(msg_p);
+
+    hash = s1ap_generate_eNB_id();
+
+    /* Some default/random parameters */
+    s1ap_register_eNB->eNB_id      = eNB_id + (hash & 0xFFFF8);
+    s1ap_register_eNB->cell_type   = CELL_MACRO_ENB;
+    s1ap_register_eNB->tac         = 0;
+    s1ap_register_eNB->mcc         = 208;
+    s1ap_register_eNB->mnc         = 34;
+    s1ap_register_eNB->default_drx = PAGING_DRX_256;
+
+    s1ap_register_eNB->nb_mme      = 1;
+    s1ap_register_eNB->mme_ip_address[0].ipv4 = 1;
+    s1ap_register_eNB->mme_ip_address[0].ipv6 = 0;
+    memcpy(s1ap_register_eNB->mme_ip_address[0].ipv4_address, mme_address_v4, strlen(mme_address_v4));
+    memcpy(s1ap_register_eNB->mme_ip_address[0].ipv6_address, mme_address_v6, strlen(mme_address_v6));
+
+#   if defined ENB_APP_ENB_REGISTER_2_MME
+    s1ap_register_eNB->nb_mme      = 2;
+    s1ap_register_eNB->mme_ip_address[1].ipv4 = 1;
+    s1ap_register_eNB->mme_ip_address[1].ipv6 = 0;
+    mme_address_v4 = "192.168.12.88";
+    memcpy(s1ap_register_eNB->mme_ip_address[1].ipv4_address, mme_address_v4, strlen(mme_address_v4));
+    memcpy(s1ap_register_eNB->mme_ip_address[1].ipv6_address, mme_address_v6, strlen(mme_address_v6));
+#   endif
+
+    itti_send_msg_to_task(TASK_S1AP, eNB_id, msg_p);
+
+    register_enb_pending ++;
+  }
+
+  return register_enb_pending;
+}
 # endif
+#endif
 
-    itti_mark_task_ready (TASK_ENB_APP); // at the end of init for the current task 
+void *eNB_app_task(void *args_p) {
+#if defined(ENABLE_ITTI)
+# if defined(ENABLE_USE_MME)
+  static uint32_t register_enb_pending;
+  static uint32_t registered_enb;
+  static long enb_register_retry_timer_id;
+# endif
+  MessageDef *msg_p;
+  const char *msg_name;
+  instance_t instance;
+  itti_mark_task_ready (TASK_ENB_APP);
+
+# if defined(ENABLE_USE_MME)
+  /* Try to register each eNB */
+  registered_enb = 0;
+  register_enb_pending = eNB_app_register();
+# else
+  msg_p = itti_alloc_new_message(TASK_ENB_APP, INITIALIZE_MESSAGE);
+  itti_send_msg_to_task(TASK_L2L1, INSTANCE_DEFAULT, msg_p);
+# endif
 
   do {
-    // Checks if a message has been sent to L2L1 task
-    itti_receive_msg (TASK_ENB_APP, &message_p);
+    // Wait for a message
+    itti_receive_msg (TASK_ENB_APP, &msg_p);
     
-    if (message_p != NULL) {
-      switch (ITTI_MSG_ID(message_p)) {
+    msg_name = ITTI_MSG_NAME (msg_p);
+    instance = ITTI_MSG_INSTANCE (msg_p);
+
+    switch (ITTI_MSG_ID(msg_p)) {
       case TERMINATE_MESSAGE:
-	itti_exit_task ();
-	break;
-	
+        itti_exit_task ();
+        break;
+
       case MESSAGE_TEST:
-	LOG_D(EMU, "Received %s\n", ITTI_MSG_NAME(message_p));
-	break;
-
-	/*      case MME_REGISTERED:
-	LOG_D(EMU, "Received %s\n", ITTI_MSG_NAME(message_p));
-	itti_mark_task_ready (TASK_L2L1);
-	break;
-	*/
+        LOG_I(EMU, "Received %s\n", ITTI_MSG_NAME(msg_p));
+        break;
+
+# if defined(ENABLE_USE_MME)
+      case S1AP_REGISTER_ENB_CNF:
+        LOG_I(EMU, "[eNB %d] Received %s: associated MME %d\n", instance, msg_name, S1AP_REGISTER_ENB_CNF(msg_p).nb_mme);
+
+        DevAssert(register_enb_pending > 0);
+        register_enb_pending--;
+
+        /* Check if at least eNB is registered with one MME */
+        if (S1AP_REGISTER_ENB_CNF(msg_p).nb_mme > 0) {
+          registered_enb ++;
+        }
+
+        /* Check if all register eNB requests have been processed */
+        if (register_enb_pending == 0) {
+          if (registered_enb == oai_emulation.info.nb_enb_local) {
+            /* If all eNB are registered, start L2L1 task */
+            MessageDef *msg_init_p;
+
+            msg_init_p = itti_alloc_new_message (TASK_ENB_APP, INITIALIZE_MESSAGE);
+            itti_send_msg_to_task (TASK_L2L1, INSTANCE_DEFAULT, msg_init_p);
+          }
+          else {
+            uint32_t not_associated = oai_emulation.info.nb_enb_local - registered_enb;
+
+            LOG_W(EMU, " %d eNB %s not associated with a MME, retrying registration in %d seconds ...\n",
+                  not_associated, not_associated > 1 ? "are" : "is", ENB_REGISTER_RETRY_DELAY);
+
+            /* Restart the eNB registration process in ENB_REGISTER_RETRY_DELAY seconds */
+            if (timer_setup (ENB_REGISTER_RETRY_DELAY, 0, TASK_ENB_APP, INSTANCE_DEFAULT, TIMER_ONE_SHOT, NULL, &enb_register_retry_timer_id) < 0) {
+              LOG_E(EMU, " Can not start eNB register retry timer!\n");
+            }
+          }
+        }
+      break;
+
+      case TIMER_HAS_EXPIRED:
+        LOG_I(EMU, " Received %s: timer_id %d\n", msg_name, TIMER_HAS_EXPIRED(msg_p).timer_id);
+
+        if (TIMER_HAS_EXPIRED(msg_p).timer_id == enb_register_retry_timer_id) {
+          /* Restart the registration process */
+          registered_enb = 0;
+          register_enb_pending = eNB_app_register();
+        }
+        break;
+# endif
+
       default:
-	LOG_E(EMU, "Received unexpected message %s\n", ITTI_MSG_NAME(message_p));
-	break;
-      }
-      
-      free (message_p);
+        LOG_E(EMU, "Received unexpected message %s\n", msg_name);
+        break;
     }
-  } while(1);
 
-  itti_terminate_tasks(TASK_ENB_APP);
+    free (msg_p);
+  } while(1);
 #endif
 
   return NULL;
@@ -503,10 +580,18 @@ void *l2l1_task(void *args_p) {
   char fname[64], vname[64];
 
 #if defined(ENABLE_ITTI)
-  MessageDef *message_p;
+  MessageDef *message_p = NULL;
 
   itti_mark_task_ready (TASK_L2L1);
 
+  /* Wait for the initialize message */
+  do {
+    if (message_p != NULL) {
+      free (message_p);
+    }
+    itti_receive_msg (TASK_L2L1, &message_p);
+  } while (ITTI_MSG_ID(message_p) != INITIALIZE_MESSAGE);
+  free (message_p);
 #endif
 
   for (frame = 0; frame < oai_emulation.info.n_frames; frame++) {
diff --git a/targets/SIMU/USER/oaisim.h b/targets/SIMU/USER/oaisim.h
index 2fe5cf8d909..71b56dc8b43 100644
--- a/targets/SIMU/USER/oaisim.h
+++ b/targets/SIMU/USER/oaisim.h
@@ -42,9 +42,9 @@ void calc_path_loss(node_desc_t* node_tx, node_desc_t* node_rx, channel_desc_t *
 
 void do_OFDM_mod(mod_sym_t **txdataF, s32 **txdata, uint32_t frame, u16 next_slot, LTE_DL_FRAME_PARMS *frame_parms);
 
+void *eNB_app_task(void *args_p);
 #ifdef ENABLE_ITTI
 int itti_create_task_successful(void);
-void *eNB_app_task(void *args_p);
 void *l2l1_task(void *args_p); 
 #endif 
 #ifdef OPENAIR2
-- 
GitLab