diff --git a/openair-cn/S1AP/s1ap_common.h b/openair-cn/S1AP/s1ap_common.h
index 685455d4a907591be800cb77901cc4b30edac7cc..a8ef931417e64eaddde4dd0c5239a733af4a1d3b 100644
--- a/openair-cn/S1AP/s1ap_common.h
+++ b/openair-cn/S1AP/s1ap_common.h
@@ -421,12 +421,6 @@ struct s1ap_message_s;
 
 /** \brief Function callback prototype.
  **/
-// typedef int (*s1ap_message_decoded_callback)(
-//     eNB_mme_desc_t *eNB_desc_p,
-//     sctp_queue_item_t *packet_p,
-//     struct s1ap_message_s *message_p
-// );
-
 typedef int (*s1ap_message_decoded_callback)(
     uint32_t               assoc_id,
     uint32_t               stream,
diff --git a/openair-cn/S1AP/s1ap_eNB.c b/openair-cn/S1AP/s1ap_eNB.c
index 5e689b964edf3d9f3015dddfb8cc8bc11e5e991b..d0d5c90735da12ab2fdfbdfd716150ee23f88dea 100644
--- a/openair-cn/S1AP/s1ap_eNB.c
+++ b/openair-cn/S1AP/s1ap_eNB.c
@@ -55,69 +55,12 @@
 
 #include "s1ap_eNB_itti_messaging.h"
 
-#include "sctp_primitives_client.h"
-
 #include "assertions.h"
 #include "conversions.h"
 
-s1ap_eNB_internal_data_t s1ap_eNB_internal_data;
-
 static int s1ap_eNB_generate_s1_setup_request(
     s1ap_eNB_instance_t *instance_p, s1ap_eNB_mme_data_t *s1ap_mme_data_p);
 
-RB_GENERATE(s1ap_mme_map, s1ap_eNB_mme_data_s, entry, s1ap_eNB_compare_assoc_id);
-
-inline int s1ap_eNB_compare_assoc_id(
-    struct s1ap_eNB_mme_data_s *p1, struct s1ap_eNB_mme_data_s *p2)
-{
-    if (p1->assoc_id == -1) {
-        if (p1->cnx_id < p2->cnx_id) {
-            return -1;
-        }
-        if (p1->cnx_id > p2->cnx_id) {
-            return 1;
-        }
-    } else {
-        if (p1->assoc_id < p2->assoc_id) {
-            return -1;
-        }
-        if (p1->assoc_id > p2->assoc_id) {
-            return 1;
-        }
-    }
-
-    /* Matching reference */
-    return 0;
-}
-
-inline struct s1ap_eNB_mme_data_s *s1ap_eNB_get_MME(
-    s1ap_eNB_instance_t *instance_p,
-    int32_t assoc_id, uint16_t cnx_id)
-{
-    struct s1ap_eNB_mme_data_s  temp;
-    struct s1ap_eNB_mme_data_s *found;
-
-    memset(&temp, 0, sizeof(struct s1ap_eNB_mme_data_s));
-
-    temp.assoc_id = assoc_id;
-    temp.cnx_id   = cnx_id;
-
-    if (instance_p == NULL) {
-        STAILQ_FOREACH(instance_p, &s1ap_eNB_internal_data.s1ap_eNB_instances_head,
-                       s1ap_eNB_entries)
-        {
-            found = RB_FIND(s1ap_mme_map, &instance_p->s1ap_mme_head, &temp);
-            if (found != NULL) {
-                return found;
-            }
-        }
-    } else {
-        return RB_FIND(s1ap_mme_map, &instance_p->s1ap_mme_head, &temp);
-    }
-
-    return NULL;
-}
-
 uint32_t s1ap_generate_eNB_id(void)
 {
     char *out;
@@ -137,55 +80,6 @@ uint32_t s1ap_generate_eNB_id(void)
     return eNB_id;
 }
 
-// int s1ap_run(eNB_mme_desc_t *eNB_desc_p)
-// {
-//     int ret = 0;
-//     struct s1ap_eNB_mme_data_s *mme_p;
-// 
-//     DevAssert(eNB_desc_p != NULL);
-// 
-//     RB_FOREACH(mme_p, s1ap_mme_map, &eNB_desc_p->s1ap_mme_head) {
-//         struct sctp_queue_item_s *item_p;
-// 
-//         /* Run the SCTP part for each MME */
-//         sctp_run(&mme_p->sctp_data);
-// 
-//         S1AP_DEBUG("Entering s1ap_run for eNB %d: %d packet(s) to handle\n",
-//                    eNB_desc_p->eNB_id, mme_p->sctp_data.queue_length);
-// 
-//         /* Handle every message in the queue */
-//         TAILQ_FOREACH(item_p, &mme_p->sctp_data.sctp_queue, entry) {
-//             /* Handle the message in S1AP */
-//             s1ap_eNB_handle_message(eNB_desc_p, item_p);
-//             /* Remove the packet from the list and update data */
-//             TAILQ_REMOVE(&mme_p->sctp_data.sctp_queue, item_p, entry);
-//             ret += item_p->length;
-//             mme_p->sctp_data.queue_size -= item_p->length;
-//             mme_p->sctp_data.queue_length--;
-//             /* Deallocate memory as the message has been handled */
-//             free(item_p->buffer);
-//             free(item_p);
-//         }
-//     }
-//     return ret;
-// }
-
-static s1ap_eNB_instance_t *s1ap_eNB_get_instance(uint8_t mod_id)
-{
-    s1ap_eNB_instance_t *temp = NULL;
-
-    STAILQ_FOREACH(temp, &s1ap_eNB_internal_data.s1ap_eNB_instances_head,
-                   s1ap_eNB_entries)
-    {
-        if (temp->mod_id == mod_id) {
-            /* Matching occurence */
-            return temp;
-        }
-    }
-
-    return NULL;
-}
-
 static void s1ap_eNB_register_mme(s1ap_eNB_instance_t *instance_p,
                                   net_ip_address_t    *mme_ip_address,
                                   net_ip_address_t    *local_ip_addr)
@@ -211,9 +105,8 @@ static void s1ap_eNB_register_mme(s1ap_eNB_instance_t *instance_p,
     s1ap_mme_data_p = calloc(1, sizeof(*s1ap_mme_data_p));
     DevAssert(s1ap_mme_data_p != NULL);
 
-    s1ap_mme_data_p->cnx_id                = s1ap_eNB_internal_data.global_cnx_id;
+    s1ap_mme_data_p->cnx_id                = s1ap_eNB_fetch_add_global_cnx_id();
     sctp_new_association_req_p->ulp_cnx_id = s1ap_mme_data_p->cnx_id;
-    s1ap_eNB_internal_data.global_cnx_id++;
 
     s1ap_mme_data_p->assoc_id          = -1;
     s1ap_mme_data_p->s1ap_eNB_instance = instance_p;
@@ -257,8 +150,8 @@ void s1ap_eNB_handle_register_eNB(s1ap_register_eNB_t *s1ap_register_eNB)
     new_instance->mnc         = s1ap_register_eNB->mnc;
     new_instance->default_drx = s1ap_register_eNB->default_drx;
 
-    STAILQ_INSERT_TAIL(&s1ap_eNB_internal_data.s1ap_eNB_instances_head,
-                       new_instance, s1ap_eNB_entries);
+    /* 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 with mod_id %u and %s eNB id %u\n",
                s1ap_register_eNB->mod_id,
@@ -322,8 +215,7 @@ void *s1ap_eNB_task(void *arg)
 
     S1AP_DEBUG("Starting S1AP layer\n");
 
-    memset(&s1ap_eNB_internal_data, 0, sizeof(s1ap_eNB_internal_data));
-    STAILQ_INIT(&s1ap_eNB_internal_data.s1ap_eNB_instances_head);
+    s1ap_eNB_prepare_internal_data();
 
     itti_mark_task_ready(TASK_S1AP);
 
@@ -348,6 +240,9 @@ void *s1ap_eNB_task(void *arg)
             case SCTP_DATA_IND: {
                 s1ap_eNB_handle_sctp_data_ind(&received_msg->msg.sctp_data_ind);
             } break;
+            case S1AP_NAS_FIRST_REQ: {
+                s1ap_eNB_handle_nas_first_req(&received_msg->msg.s1ap_nas_first_req);
+            } break;
             default:
                 S1AP_ERROR("Received unhandled message with id %d\n",
                            received_msg->header.messageId);
@@ -418,123 +313,3 @@ static int s1ap_eNB_generate_s1_setup_request(
 
     return ret;
 }
-
-// int s1ap_eNB_generate_initial_UE_message(eNB_mme_desc_t       *eNB_desc_p,
-//                                          s1ap_nas_first_req_t  nas_req_p)
-// {
-//     s1ap_message message;
-//     struct s1ap_eNB_mme_data_s   *mme_desc_p;
-//     struct s1ap_eNB_ue_context_s *ue_desc_p;
-//     InitialUEMessageIEs_t        *initial_ue_message_p;
-// 
-//     uint8_t  *buffer;
-//     uint32_t  length;
-// 
-//     DevAssert(eNB_desc_p != NULL);
-// 
-//     memset(&message, 0, sizeof(s1ap_message));
-// 
-//     message.direction = S1AP_PDU_PR_initiatingMessage;
-//     message.procedureCode = ProcedureCode_id_initialUEMessage;
-// 
-//     initial_ue_message_p = &message.msg.initialUEMessageIEs;
-// 
-//     /* Select the MME corresponding to the provided GUMMEI.
-//      * If no MME corresponds to the GUMMEI, the function selects the MME with the
-//      * highest capacity.
-//      * In case eNB has no MME associated, the eNB should inform RRC and discard
-//      * this request.
-//      */
-//     if (nas_req_p.ue_identity.present == GUMMEI_PROVIDED) {
-//         mme_desc_p = s1ap_eNB_nnsf_select_mme_by_gummei(
-//             eNB_desc_p,
-//             nas_req_p.establishment_cause, nas_req_p.ue_identity.identity.gummei);
-//     } else {
-//         mme_desc_p = s1ap_eNB_nnsf_select_mme_by_mme_code(
-//             eNB_desc_p,
-//             nas_req_p.establishment_cause, nas_req_p.ue_identity.identity.s_tmsi.mme_code);
-//     }
-//     if (mme_desc_p == NULL) {
-//         S1AP_WARN("No MME is associated to the eNB\n");
-//         // TODO: Inform RRC
-//         return -1;
-//     }
-// 
-//     /* The eNB should allocate a unique eNB UE S1AP ID for this UE. The value
-//      * will be used for the duration of the connectivity.
-//      */
-//     if ((ue_desc_p = s1ap_eNB_allocate_new_UE_context()) == NULL) {
-//         return -1;
-//     }
-// 
-//     /* Keep a reference to the selected MME */
-//     ue_desc_p->mme_ref = mme_desc_p;
-//     ue_desc_p->rnti    = nas_req_p.rnti;
-// 
-//     do {
-//         struct s1ap_eNB_ue_context_s *collision_p;
-// 
-//         /* Peek a random value for the eNB_ue_s1ap_id */
-//         ue_desc_p->eNB_ue_s1ap_id = (random() + random()) & 0x00ffffff;
-//         if ((collision_p = RB_INSERT(s1ap_ue_map, &eNB_desc_p->s1ap_ue_head, ue_desc_p))
-//                 == NULL) {
-//             /* Break the loop as the id is not already used by another UE */
-//             break;
-//         }
-//     } while(1);
-// 
-//     initial_ue_message_p->eNB_UE_S1AP_ID = ue_desc_p->eNB_ue_s1ap_id;
-//     /* Prepare the NAS PDU */
-//     initial_ue_message_p->nas_pdu.buf  = nas_req_p.nas_pdu.buffer;
-//     initial_ue_message_p->nas_pdu.size = nas_req_p.nas_pdu.length;
-// 
-//     /* Set the establishment cause according to those provided by RRC */
-//     DevCheck(nas_req_p.establishment_cause <= RRC_CAUSE_MAX,
-//              nas_req_p.establishment_cause, 0, 0);
-//     initial_ue_message_p->rrC_Establishment_Cause = nas_req_p.establishment_cause;
-// 
-//     if (nas_req_p.ue_identity.present == S_TMSI_PROVIDED) {
-//         initial_ue_message_p->presenceMask |= INITIALUEMESSAGEIES_S_TMSI_PRESENT;
-// 
-//         MME_CODE_TO_OCTET_STRING(nas_req_p.ue_identity.identity.s_tmsi.mme_code,
-//                                  &initial_ue_message_p->s_tmsi.mMEC);
-//         M_TMSI_TO_OCTET_STRING(nas_req_p.ue_identity.identity.s_tmsi.m_tmsi,
-//                                &initial_ue_message_p->s_tmsi.m_TMSI);
-//     } else {
-//         initial_ue_message_p->presenceMask |= INITIALUEMESSAGEIES_GUMMEI_ID_PRESENT;
-// 
-//         MCC_MNC_TO_PLMNID(nas_req_p.ue_identity.identity.gummei.mcc,
-//                           nas_req_p.ue_identity.identity.gummei.mnc,
-//                           &initial_ue_message_p->gummei_id.pLMN_Identity);
-//         MME_GID_TO_OCTET_STRING(nas_req_p.ue_identity.identity.gummei.mme_group_id,
-//                                 &initial_ue_message_p->gummei_id.mME_Group_ID);
-//         MME_CODE_TO_OCTET_STRING(nas_req_p.ue_identity.identity.gummei.mme_code,
-//                                  &initial_ue_message_p->gummei_id.mME_Code);
-//     }
-// 
-//     /* Assuming TAI is the TAI from the cell */
-//     INT16_TO_OCTET_STRING(eNB_desc_p->tac, &initial_ue_message_p->tai.tAC);
-//     MCC_MNC_TO_PLMNID(eNB_desc_p->mcc, eNB_desc_p->mnc,
-//                       &initial_ue_message_p->tai.pLMNidentity);
-// 
-//     /* Set the EUTRAN CGI
-//      * The cell identity is defined on 28 bits but as we use macro enb id,
-//      * we have to pad.
-//      */
-//     MACRO_ENB_ID_TO_CELL_IDENTITY(eNB_desc_p->eNB_id,
-//                                   &initial_ue_message_p->eutran_cgi.cell_ID);
-//     MCC_MNC_TO_TBCD(eNB_desc_p->mcc, eNB_desc_p->mnc,
-//                     &initial_ue_message_p->eutran_cgi.pLMNidentity);
-// 
-//     if (s1ap_eNB_encode_pdu(&message, &buffer, &length) < 0) {
-//         /* Failed to encode message */
-//         return -1;
-//     }
-// 
-//     /* Update the current S1AP UE state */
-//     ue_desc_p->ue_state = S1AP_UE_WAITING_CSR;
-// 
-//     /* Send encoded message over sctp */
-//     return sctp_send_msg(&mme_desc_p->sctp_data, S1AP_SCTP_PPID, 1, buffer, length);
-// }
-
diff --git a/openair-cn/S1AP/s1ap_eNB_defs.h b/openair-cn/S1AP/s1ap_eNB_defs.h
index 67c75b641af32af6db09d6ef813734c5e4a7a1b7..7945e7c47c3055f07e1960333668f95cbe4ecd69 100644
--- a/openair-cn/S1AP/s1ap_eNB_defs.h
+++ b/openair-cn/S1AP/s1ap_eNB_defs.h
@@ -82,46 +82,6 @@ typedef enum {
     S1AP_OVERLOAD_MAX,
 } s1ap_overload_state_t;
 
-// typedef enum {
-//     PAGING_DRX_32  = 0x0,
-//     PAGING_DRX_64  = 0x1,
-//     PAGING_DRX_128 = 0x2,
-//     PAGING_DRX_256 = 0x3,
-// } paging_drx_t;
-
-typedef struct {
-    /* Octet string data */
-    uint8_t  *buffer;
-    /* Length of the octet string */
-    uint32_t  length;
-} nas_pdu_t, ue_radio_cap_t;
-
-typedef struct {
-    uint16_t mcc;
-    uint16_t mnc;
-    uint8_t  mme_code;
-    uint16_t mme_group_id;
-} gummei_t;
-
-typedef struct {
-    uint8_t  mme_code;
-    uint32_t m_tmsi;
-} s_tmsi_t;
-
-/* 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
- * 'Mobile Originating'. Defined in TS 36.331.
- */
-typedef enum {
-    RRC_CAUSE_EMERGENCY        = 0x0,
-    RRC_CAUSE_HIGH_PRIO_ACCESS = 0x1,
-    RRC_CAUSE_MT_ACCESS        = 0x2,
-    RRC_CAUSE_MO_SIGNALLING    = 0x3,
-    RRC_CAUSE_MO_DATA          = 0x4,
-    RRC_CAUSE_MAX              = RRC_CAUSE_MO_DATA,
-} rrc_establishment_cause_t;
-
 typedef struct {
     uint8_t qci;
 
@@ -166,16 +126,6 @@ typedef struct {
 //     cause_t cause;
 } e_rab_failed_t;
 
-typedef struct {
-#define S_TMSI_PROVIDED 0x0
-#define GUMMEI_PROVIDED 0x1
-    unsigned     present:1;
-    union {
-        gummei_t gummei;
-        s_tmsi_t s_tmsi;
-    } identity;
-} ue_identity_t;
-
 /* Served PLMN identity element */
 struct plmn_identity_s {
     uint16_t mcc;
diff --git a/openair-cn/S1AP/s1ap_eNB_handlers.c b/openair-cn/S1AP/s1ap_eNB_handlers.c
index 8cbe2fa3863e25271cccdb97d6e5c42939c43752..78a400bd3f2ea6e77b968cedbb0b048432544a52 100644
--- a/openair-cn/S1AP/s1ap_eNB_handlers.c
+++ b/openair-cn/S1AP/s1ap_eNB_handlers.c
@@ -58,6 +58,7 @@ static
 int s1ap_eNB_handle_s1_setup_response(uint32_t               assoc_id,
                                       uint32_t               stream,
                                       struct s1ap_message_s *message_p);
+static
 int s1ap_eNB_handle_s1_setup_failure(uint32_t               assoc_id,
                                      uint32_t               stream,
                                      struct s1ap_message_s *message_p);
@@ -164,6 +165,7 @@ int s1ap_eNB_handle_message(uint32_t assoc_id, int32_t stream,
         (assoc_id, stream, &message);
 }
 
+static
 int s1ap_eNB_handle_s1_setup_failure(uint32_t               assoc_id,
                                      uint32_t               stream,
                                      struct s1ap_message_s *message_p)
@@ -173,14 +175,11 @@ int s1ap_eNB_handle_s1_setup_failure(uint32_t               assoc_id,
         S1AP_WARN("[SCTP %d] Received s1 setup failure on stream != 0 (%d)\n",
                   assoc_id, stream);
     }
-    S1AP_DEBUG("Received s1 setup failure for MME... please check your parameters\n");
+    S1AP_ERROR("Received s1 setup failure for MME... please check your parameters\n");
 
     return 0;
 }
 
-// int s1ap_eNB_handle_s1_setup_response(eNB_mme_desc_t *eNB_desc_p,
-//                                       sctp_queue_item_t *packet_p,
-//                                       struct s1ap_message_s *message_p)
 static
 int s1ap_eNB_handle_s1_setup_response(uint32_t               assoc_id,
                                       uint32_t               stream,
@@ -277,71 +276,62 @@ int s1ap_eNB_handle_s1_setup_response(uint32_t               assoc_id,
      */
     mme_desc_p->state = S1AP_ENB_STATE_CONNECTED;
 
-//     /* We call back our self
-//      * -> generate a dummy initial UE message
-//      */
-//     {
-//         extern int s1ap_eNB_handle_api_req(eNB_mme_desc_t     *eNB_desc_p,
-//                                            s1ap_rrc_api_req_t *api_req_p);
-//         s1ap_rrc_api_req_t api_req;
-//         s1ap_nas_first_req_t *nas_req_p;
-// 
-//         memset(&api_req, 0, sizeof(s1ap_rrc_api_req_t));
-// 
-//         nas_req_p = &api_req.msg.first_nas_req;
-//         api_req.api_req = S1AP_API_NAS_FIRST_REQ;
-// 
-//         nas_req_p->rnti = 0xC03A;
-//         nas_req_p->establishment_cause = RRC_CAUSE_MO_DATA;
-//         nas_req_p->ue_identity.present = GUMMEI_PROVIDED;
-// 
-//         nas_req_p->ue_identity.identity.gummei.mcc = 208;
-//         nas_req_p->ue_identity.identity.gummei.mnc = 34;
-//         nas_req_p->ue_identity.identity.gummei.mme_code = 0;
-//         nas_req_p->ue_identity.identity.gummei.mme_group_id = 0;
-// 
-//         /* NAS Attach request with IMSI */
-//         uint8_t nas_attach_req_imsi[] =
-//         {
-//             0x07, 0x41,
-//             /* EPS Mobile identity = IMSI */
-//             0x71, 0x08, 0x29, 0x80, 0x43, 0x21, 0x43, 0x65, 0x87,
-//             0xF9,
-//             /* End of EPS Mobile Identity */
-//             0x02, 0xE0, 0xE0, 0x00, 0x20, 0x02, 0x03,
-//             0xD0, 0x11, 0x27, 0x1A, 0x80, 0x80, 0x21, 0x10, 0x01, 0x00, 0x00,
-//             0x10, 0x81, 0x06, 0x00, 0x00, 0x00, 0x00, 0x83, 0x06, 0x00, 0x00,
-//             0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x0A, 0x00, 0x52, 0x12, 0xF2,
-//             0x01, 0x27, 0x11,
-//         };
-// 
-//         /* NAS Attach request with GUTI */
-//         uint8_t nas_attach_req_guti[] =
-//         {
-//             0x07, 0x41,
-//             /* EPS Mobile identity = IMSI */
-//             0x71, 0x0B, 0xF6, 0x12, 0xF2, 0x01, 0x80, 0x00, 0x01, 0xE0, 0x00,
-//             0xDA, 0x1F,
-//             /* End of EPS Mobile Identity */
-//             0x02, 0xE0, 0xE0, 0x00, 0x20, 0x02, 0x03,
-//             0xD0, 0x11, 0x27, 0x1A, 0x80, 0x80, 0x21, 0x10, 0x01, 0x00, 0x00,
-//             0x10, 0x81, 0x06, 0x00, 0x00, 0x00, 0x00, 0x83, 0x06, 0x00, 0x00,
-//             0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x0A, 0x00, 0x52, 0x12, 0xF2,
-//             0x01, 0x27, 0x11,
-//         };
-// 
-//         nas_req_p->nas_pdu.buffer = nas_attach_req_guti;
-//         nas_req_p->nas_pdu.length = sizeof(nas_attach_req_guti);
-// 
-//         s1ap_eNB_handle_api_req(eNB_desc_p, &api_req);
-//     }
+    /* We call back our self
+     * -> generate a dummy initial UE message
+     */
+    {
+        s1ap_nas_first_req_t s1ap_nas_first_req;
+
+        memset(&s1ap_nas_first_req, 0, sizeof(s1ap_nas_first_req_t));
+
+        s1ap_nas_first_req.rnti = 0xC03A;
+        s1ap_nas_first_req.establishment_cause = RRC_CAUSE_MO_DATA;
+        s1ap_nas_first_req.ue_identity.present = GUMMEI_PROVIDED;
+
+        s1ap_nas_first_req.ue_identity.identity.gummei.mcc = 208;
+        s1ap_nas_first_req.ue_identity.identity.gummei.mnc = 34;
+        s1ap_nas_first_req.ue_identity.identity.gummei.mme_code = 0;
+        s1ap_nas_first_req.ue_identity.identity.gummei.mme_group_id = 0;
+
+        /* NAS Attach request with IMSI */
+        uint8_t nas_attach_req_imsi[] =
+        {
+            0x07, 0x41,
+            /* EPS Mobile identity = IMSI */
+            0x71, 0x08, 0x29, 0x80, 0x43, 0x21, 0x43, 0x65, 0x87,
+            0xF9,
+            /* End of EPS Mobile Identity */
+            0x02, 0xE0, 0xE0, 0x00, 0x20, 0x02, 0x03,
+            0xD0, 0x11, 0x27, 0x1A, 0x80, 0x80, 0x21, 0x10, 0x01, 0x00, 0x00,
+            0x10, 0x81, 0x06, 0x00, 0x00, 0x00, 0x00, 0x83, 0x06, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x0A, 0x00, 0x52, 0x12, 0xF2,
+            0x01, 0x27, 0x11,
+        };
+
+        /* NAS Attach request with GUTI */
+        uint8_t nas_attach_req_guti[] =
+        {
+            0x07, 0x41,
+            /* EPS Mobile identity = IMSI */
+            0x71, 0x0B, 0xF6, 0x12, 0xF2, 0x01, 0x80, 0x00, 0x01, 0xE0, 0x00,
+            0xDA, 0x1F,
+            /* End of EPS Mobile Identity */
+            0x02, 0xE0, 0xE0, 0x00, 0x20, 0x02, 0x03,
+            0xD0, 0x11, 0x27, 0x1A, 0x80, 0x80, 0x21, 0x10, 0x01, 0x00, 0x00,
+            0x10, 0x81, 0x06, 0x00, 0x00, 0x00, 0x00, 0x83, 0x06, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x0A, 0x00, 0x52, 0x12, 0xF2,
+            0x01, 0x27, 0x11,
+        };
+
+        s1ap_nas_first_req.nas_pdu.buffer = nas_attach_req_guti;
+        s1ap_nas_first_req.nas_pdu.length = sizeof(nas_attach_req_guti);
+
+        s1ap_eNB_handle_nas_first_req(&s1ap_nas_first_req);
+    }
 
     return 0;
 }
 
-// int s1ap_eNB_handle_initial_context_request(eNB_mme_desc_t *eNB_desc_p,
-//         sctp_queue_item_t *packet_p,
-//         struct s1ap_message_s *message_p)
 static
 int s1ap_eNB_handle_initial_context_request(uint32_t               assoc_id,
                                             uint32_t               stream,
@@ -351,30 +341,28 @@ int s1ap_eNB_handle_initial_context_request(uint32_t               assoc_id,
     s1ap_eNB_ue_context_t *ue_desc_p;
 
     InitialContextSetupRequestIEs_t *initialContextSetupRequest_p;
-
-//     DevAssert(eNB_desc_p != NULL);
     DevAssert(message_p != NULL);
 
     initialContextSetupRequest_p = &message_p->msg.initialContextSetupRequestIEs;
 
-    /* Initial context request = UE-related procedure -> stream != 0 */
-    if (stream == 0) {
-        S1AP_ERROR("[SCTP %d] Received UE-related procedure 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 initial context setup request for non "
+                   "existing MME context\n", assoc_id);
+        return -1;
+    }
+    if ((ue_desc_p = s1ap_eNB_get_ue_context(mme_desc_p->s1ap_eNB_instance,
+                     initialContextSetupRequest_p->eNB_UE_S1AP_ID)) == NULL) {
+        S1AP_ERROR("[SCTP %d] Received initial context setup request for non "
+                   "existing UE context\n", assoc_id);
         return -1;
     }
 
-//     if ((mme_desc_p = s1ap_eNB_get_MME(eNB_desc_p, assoc_id)) == NULL) {
-//         S1AP_ERROR("[SCTP %d] Received initial context setup request for non "
-//                    "existing MME context\n", packet_p->assoc_id);
-//         return -1;
-//     }
-//     if ((ue_desc_p = s1ap_eNB_get_ue_context(eNB_desc_p,
-//                      initialContextSetupRequest_p->eNB_UE_S1AP_ID)) == NULL) {
-//         S1AP_ERROR("[SCTP %d] Received initial context setup request for non "
-//                    "existing UE context\n", packet_p->assoc_id);
-//         return -1;
-//     }
+    /* Initial context request = UE-related procedure -> stream != 0 */
+    if (stream != ue_desc_p->stream) {
+        S1AP_ERROR("[SCTP %d] Received UE-related procedure on stream (%d) whereas expecting (%d)\n",
+                   assoc_id, stream, ue_desc_p->stream);
+        return -1;
+    }
 
     ue_desc_p->mme_ue_s1ap_id = initialContextSetupRequest_p->mme_ue_s1ap_id;
 
diff --git a/openair-cn/S1AP/s1ap_eNB_itti_messaging.c b/openair-cn/S1AP/s1ap_eNB_itti_messaging.c
index b9be86165e20f72fc370718b941d31e4f0252a59..04549272e0159040ae5f4322abd795e9cb520d4d 100644
--- a/openair-cn/S1AP/s1ap_eNB_itti_messaging.c
+++ b/openair-cn/S1AP/s1ap_eNB_itti_messaging.c
@@ -19,3 +19,20 @@ void s1ap_eNB_itti_send_sctp_data_req(int32_t assoc_id, uint8_t *buffer,
 
     itti_send_msg_to_task(TASK_SCTP, INSTANCE_DEFAULT, message_p);
 }
+
+void s1ap_eNB_itti_send_nas_downlink_ind(uint8_t mod_id, uint8_t *nas_pdu,
+                                         uint32_t nas_pdu_length)
+{
+    MessageDef          *message_p;
+    s1ap_downlink_nas_t *s1ap_downlink_nas;
+
+    message_p = itti_alloc_new_message(TASK_S1AP, S1AP_DOWNLINK_NAS);
+
+    s1ap_downlink_nas = &message_p->msg.s1ap_downlink_nas;
+
+    s1ap_downlink_nas->mod_id         = mod_id;
+    s1ap_downlink_nas->nas_pdu.buffer = nas_pdu;
+    s1ap_downlink_nas->nas_pdu.length = nas_pdu_length;
+
+    itti_send_msg_to_task(TASK_RRC_ENB, INSTANCE_DEFAULT, message_p);
+}
diff --git a/openair-cn/S1AP/s1ap_eNB_itti_messaging.h b/openair-cn/S1AP/s1ap_eNB_itti_messaging.h
index c111eb50766adb87dae20d29b53a165e87c3e174..dd244ef45fe7770af0311d78ab5177ac18f28487 100644
--- a/openair-cn/S1AP/s1ap_eNB_itti_messaging.h
+++ b/openair-cn/S1AP/s1ap_eNB_itti_messaging.h
@@ -4,4 +4,7 @@
 void s1ap_eNB_itti_send_sctp_data_req(int32_t assoc_id, uint8_t *buffer,
                                       uint32_t buffer_length, uint16_t stream);
 
+void s1ap_eNB_itti_send_nas_downlink_ind(uint8_t mod_id, uint8_t *nas_pdu,
+                                         uint32_t nas_pdu_length);
+
 #endif /* S1AP_ENB_ITTI_MESSAGING_H_ */
diff --git a/openair-cn/S1AP/s1ap_eNB_management_procedures.c b/openair-cn/S1AP/s1ap_eNB_management_procedures.c
index 44e23eed13867a075e9d1607be76d7691e24bccd..058a56fc7b8852b761a952784a2151a580a30e7f 100644
--- a/openair-cn/S1AP/s1ap_eNB_management_procedures.c
+++ b/openair-cn/S1AP/s1ap_eNB_management_procedures.c
@@ -35,17 +35,104 @@
 #include "intertask_interface.h"
 
 #include "assertions.h"
-// #include "conversions.h"
+#include "conversions.h"
 
 #include "s1ap_common.h"
-#include "s1ap_ies_defs.h"
 #include "s1ap_eNB_defs.h"
 #include "s1ap_eNB.h"
 
-#include "s1ap_eNB_encoder.h"
-#include "s1ap_eNB_management_procedures.h"
+s1ap_eNB_internal_data_t s1ap_eNB_internal_data;
 
-#include "sctp_primitives_client.h"
+static int s1ap_eNB_generate_s1_setup_request(
+    s1ap_eNB_instance_t *instance_p, s1ap_eNB_mme_data_t *s1ap_mme_data_p);
+
+RB_GENERATE(s1ap_mme_map, s1ap_eNB_mme_data_s, entry, s1ap_eNB_compare_assoc_id);
+
+inline int s1ap_eNB_compare_assoc_id(
+    struct s1ap_eNB_mme_data_s *p1, struct s1ap_eNB_mme_data_s *p2)
+{
+    if (p1->assoc_id == -1) {
+        if (p1->cnx_id < p2->cnx_id) {
+            return -1;
+        }
+        if (p1->cnx_id > p2->cnx_id) {
+            return 1;
+        }
+    } else {
+        if (p1->assoc_id < p2->assoc_id) {
+            return -1;
+        }
+        if (p1->assoc_id > p2->assoc_id) {
+            return 1;
+        }
+    }
+
+    /* Matching reference */
+    return 0;
+}
+
+uint16_t s1ap_eNB_fetch_add_global_cnx_id(void)
+{
+    return ++s1ap_eNB_internal_data.global_cnx_id;
+}
+
+void s1ap_eNB_prepare_internal_data(void)
+{
+    memset(&s1ap_eNB_internal_data, 0, sizeof(s1ap_eNB_internal_data));
+    STAILQ_INIT(&s1ap_eNB_internal_data.s1ap_eNB_instances_head);
+}
+
+void s1ap_eNB_insert_new_instance(s1ap_eNB_instance_t *new_instance_p)
+{
+    DevAssert(new_instance_p != NULL);
+
+    STAILQ_INSERT_TAIL(&s1ap_eNB_internal_data.s1ap_eNB_instances_head,
+                       new_instance_p, s1ap_eNB_entries);
+}
+
+inline struct s1ap_eNB_mme_data_s *s1ap_eNB_get_MME(
+    s1ap_eNB_instance_t *instance_p,
+    int32_t assoc_id, uint16_t cnx_id)
+{
+    struct s1ap_eNB_mme_data_s  temp;
+    struct s1ap_eNB_mme_data_s *found;
+
+    memset(&temp, 0, sizeof(struct s1ap_eNB_mme_data_s));
+
+    temp.assoc_id = assoc_id;
+    temp.cnx_id   = cnx_id;
+
+    if (instance_p == NULL) {
+        STAILQ_FOREACH(instance_p, &s1ap_eNB_internal_data.s1ap_eNB_instances_head,
+                       s1ap_eNB_entries)
+        {
+            found = RB_FIND(s1ap_mme_map, &instance_p->s1ap_mme_head, &temp);
+            if (found != NULL) {
+                return found;
+            }
+        }
+    } else {
+        return RB_FIND(s1ap_mme_map, &instance_p->s1ap_mme_head, &temp);
+    }
+
+    return NULL;
+}
+
+s1ap_eNB_instance_t *s1ap_eNB_get_instance(uint8_t mod_id)
+{
+    s1ap_eNB_instance_t *temp = NULL;
+
+    STAILQ_FOREACH(temp, &s1ap_eNB_internal_data.s1ap_eNB_instances_head,
+                   s1ap_eNB_entries)
+    {
+        if (temp->mod_id == mod_id) {
+            /* Matching occurence */
+            return temp;
+        }
+    }
+
+    return NULL;
+}
 
 // int s1ap_eNB_ue_capabilities(eNB_mme_desc_t         *eNB_desc_p,
 //                              s1ap_ue_cap_info_ind_t *ue_cap_info_ind_p)
diff --git a/openair-cn/S1AP/s1ap_eNB_management_procedures.h b/openair-cn/S1AP/s1ap_eNB_management_procedures.h
index 93c19257e23fb404e54fdaadfc21ba1e5d4a39d4..7f7115232d39d418724d349a07375cd16b5bebc3 100644
--- a/openair-cn/S1AP/s1ap_eNB_management_procedures.h
+++ b/openair-cn/S1AP/s1ap_eNB_management_procedures.h
@@ -31,7 +31,16 @@
 #ifndef S1AP_ENB_MANAGEMENT_PROCEDURES_H_
 #define S1AP_ENB_MANAGEMENT_PROCEDURES_H_
 
-// int s1ap_eNB_ue_capabilities(eNB_mme_desc_t         *eNB_desc_p,
-//                              s1ap_ue_cap_info_ind_t *ue_cap_info_ind_p);
+inline struct s1ap_eNB_mme_data_s *s1ap_eNB_get_MME(
+    s1ap_eNB_instance_t *instance_p,
+    int32_t assoc_id, uint16_t cnx_id);
+
+void s1ap_eNB_insert_new_instance(s1ap_eNB_instance_t *new_instance_p);
+
+s1ap_eNB_instance_t *s1ap_eNB_get_instance(uint8_t mod_id);
+
+uint16_t s1ap_eNB_fetch_add_global_cnx_id(void);
+
+void s1ap_eNB_prepare_internal_data(void);
 
 #endif /* S1AP_ENB_MANAGEMENT_PROCEDURES_H_ */
diff --git a/openair-cn/S1AP/s1ap_eNB_nas_procedures.c b/openair-cn/S1AP/s1ap_eNB_nas_procedures.c
index 6c5322ef6f204a8d0deab65d09d269e424b773e3..48b1133de38833fd07bcbf2ed801045842d25671 100644
--- a/openair-cn/S1AP/s1ap_eNB_nas_procedures.c
+++ b/openair-cn/S1AP/s1ap_eNB_nas_procedures.c
@@ -40,16 +40,142 @@
 #include "s1ap_common.h"
 #include "s1ap_eNB_defs.h"
 
+#include "s1ap_eNB_itti_messaging.h"
+
 #include "s1ap_ies_defs.h"
 #include "s1ap_eNB_encoder.h"
+#include "s1ap_eNB_nnsf.h"
 #include "s1ap_eNB_ue_context.h"
 #include "s1ap_eNB_nas_procedures.h"
+#include "s1ap_eNB_management_procedures.h"
+
+int s1ap_eNB_handle_nas_first_req(s1ap_nas_first_req_t *s1ap_nas_first_req_p)
+{
+    s1ap_eNB_instance_t          *instance_p;
+    struct s1ap_eNB_mme_data_s   *mme_desc_p;
+    struct s1ap_eNB_ue_context_s *ue_desc_p;
+
+    s1ap_message           message;
+    InitialUEMessageIEs_t *initial_ue_message_p;
+
+    uint8_t  *buffer;
+    uint32_t  length;
+
+    DevAssert(s1ap_nas_first_req_p != NULL);
+
+    /* Retrieve the S1AP eNB instance associated with Mod_id */
+    instance_p = s1ap_eNB_get_instance(s1ap_nas_first_req_p->mod_id);
+    DevAssert(instance_p != NULL);
+
+    memset(&message, 0, sizeof(s1ap_message));
+
+    message.direction = S1AP_PDU_PR_initiatingMessage;
+    message.procedureCode = ProcedureCode_id_initialUEMessage;
+
+    initial_ue_message_p = &message.msg.initialUEMessageIEs;
+
+    /* Select the MME corresponding to the provided GUMMEI.
+     * If no MME corresponds to the GUMMEI, the function selects the MME with the
+     * highest capacity.
+     * In case eNB has no MME associated, the eNB should inform RRC and discard
+     * this request.
+     */
+    if (s1ap_nas_first_req_p->ue_identity.present == GUMMEI_PROVIDED) {
+        mme_desc_p = s1ap_eNB_nnsf_select_mme_by_gummei(
+            instance_p,
+            s1ap_nas_first_req_p->establishment_cause,
+            s1ap_nas_first_req_p->ue_identity.identity.gummei);
+    } else {
+        mme_desc_p = s1ap_eNB_nnsf_select_mme_by_mme_code(
+            instance_p,
+            s1ap_nas_first_req_p->establishment_cause,
+            s1ap_nas_first_req_p->ue_identity.identity.s_tmsi.mme_code);
+    }
+    if (mme_desc_p == NULL) {
+        S1AP_WARN("No MME is associated to the eNB\n");
+        // TODO: Inform RRC
+        return -1;
+    }
+
+    /* The eNB should allocate a unique eNB UE S1AP ID for this UE. The value
+     * will be used for the duration of the connectivity.
+     */
+    ue_desc_p = s1ap_eNB_allocate_new_UE_context();
+    DevAssert(ue_desc_p != NULL);
+
+    /* Keep a reference to the selected MME */
+    ue_desc_p->mme_ref = mme_desc_p;
+    ue_desc_p->rnti    = s1ap_nas_first_req_p->rnti;
+
+    do {
+        struct s1ap_eNB_ue_context_s *collision_p;
+
+        /* Peek a random value for the eNB_ue_s1ap_id */
+        ue_desc_p->eNB_ue_s1ap_id = (random() + random()) & 0x00ffffff;
+        if ((collision_p = RB_INSERT(s1ap_ue_map, &instance_p->s1ap_ue_head, ue_desc_p))
+                == NULL) {
+            /* Break the loop as the id is not already used by another UE */
+            break;
+        }
+    } while(1);
+
+    initial_ue_message_p->eNB_UE_S1AP_ID = ue_desc_p->eNB_ue_s1ap_id;
+    /* Prepare the NAS PDU */
+    initial_ue_message_p->nas_pdu.buf  = s1ap_nas_first_req_p->nas_pdu.buffer;
+    initial_ue_message_p->nas_pdu.size = s1ap_nas_first_req_p->nas_pdu.length;
+
+    /* Set the establishment cause according to those provided by RRC */
+    DevCheck(s1ap_nas_first_req_p->establishment_cause < RRC_CAUSE_LAST,
+             s1ap_nas_first_req_p->establishment_cause, RRC_CAUSE_LAST, 0);
+    initial_ue_message_p->rrC_Establishment_Cause = s1ap_nas_first_req_p->establishment_cause;
+
+    if (s1ap_nas_first_req_p->ue_identity.present == S_TMSI_PROVIDED) {
+        initial_ue_message_p->presenceMask |= INITIALUEMESSAGEIES_S_TMSI_PRESENT;
+
+        MME_CODE_TO_OCTET_STRING(s1ap_nas_first_req_p->ue_identity.identity.s_tmsi.mme_code,
+                                 &initial_ue_message_p->s_tmsi.mMEC);
+        M_TMSI_TO_OCTET_STRING(s1ap_nas_first_req_p->ue_identity.identity.s_tmsi.m_tmsi,
+                               &initial_ue_message_p->s_tmsi.m_TMSI);
+    } else {
+        initial_ue_message_p->presenceMask |= INITIALUEMESSAGEIES_GUMMEI_ID_PRESENT;
+
+        MCC_MNC_TO_PLMNID(s1ap_nas_first_req_p->ue_identity.identity.gummei.mcc,
+                          s1ap_nas_first_req_p->ue_identity.identity.gummei.mnc,
+                          &initial_ue_message_p->gummei_id.pLMN_Identity);
+        MME_GID_TO_OCTET_STRING(s1ap_nas_first_req_p->ue_identity.identity.gummei.mme_group_id,
+                                &initial_ue_message_p->gummei_id.mME_Group_ID);
+        MME_CODE_TO_OCTET_STRING(s1ap_nas_first_req_p->ue_identity.identity.gummei.mme_code,
+                                 &initial_ue_message_p->gummei_id.mME_Code);
+    }
+
+    /* Assuming TAI is the TAI from the cell */
+    INT16_TO_OCTET_STRING(instance_p->tac, &initial_ue_message_p->tai.tAC);
+    MCC_MNC_TO_PLMNID(instance_p->mcc, instance_p->mnc,
+                      &initial_ue_message_p->tai.pLMNidentity);
+
+    /* Set the EUTRAN CGI
+     * The cell identity is defined on 28 bits but as we use macro enb id,
+     * we have to pad.
+     */
+    MACRO_ENB_ID_TO_CELL_IDENTITY(instance_p->eNB_id,
+                                  &initial_ue_message_p->eutran_cgi.cell_ID);
+    MCC_MNC_TO_TBCD(instance_p->mcc, instance_p->mnc,
+                    &initial_ue_message_p->eutran_cgi.pLMNidentity);
+
+    if (s1ap_eNB_encode_pdu(&message, &buffer, &length) < 0) {
+        /* Failed to encode message */
+        return -1;
+    }
 
-#include "sctp_primitives_client.h"
+    /* Update the current S1AP UE state */
+    ue_desc_p->ue_state = S1AP_UE_WAITING_CSR;
+
+    /* Send encoded message over sctp */
+    s1ap_eNB_itti_send_sctp_data_req(mme_desc_p->assoc_id, buffer, length, ue_desc_p->stream);
+
+    return 0;
+}
 
-// int s1ap_eNB_handle_nas_downlink(eNB_mme_desc_t *eNB_desc_p,
-//                                  sctp_queue_item_t *packet_p,
-//                                  struct s1ap_message_s *message_p)
 int s1ap_eNB_handle_nas_downlink(uint32_t               assoc_id,
                                  uint32_t               stream,
                                  struct s1ap_message_s *message_p)
@@ -57,6 +183,7 @@ int s1ap_eNB_handle_nas_downlink(uint32_t               assoc_id,
     DownlinkNASTransportIEs_t *downlink_NAS_transport_p;
     s1ap_eNB_mme_data_t       *mme_desc_p;
     s1ap_eNB_ue_context_t     *ue_desc_p;
+    s1ap_eNB_instance_t       *s1ap_eNB_instance;
 
     DevAssert(message_p != NULL);
 
@@ -69,117 +196,120 @@ int s1ap_eNB_handle_nas_downlink(uint32_t               assoc_id,
         return -1;
     }
 
-//     if ((mme_desc_p = s1ap_eNB_get_MME(eNB_desc_p, assoc_id)) == NULL) {
-//         S1AP_ERROR("[SCTP %d] Received initial context setup request for non "
-//                    "existing MME context\n", assoc_id);
-//         return -1;
-//     }
-//     if ((ue_desc_p = s1ap_eNB_get_ue_context(eNB_desc_p,
-//          downlink_NAS_transport_p->eNB_UE_S1AP_ID)) == NULL)
-//     {
-//         S1AP_ERROR("[SCTP %d] Received initial context setup request for non "
-//                    "existing UE context\n", assoc_id);
-//         return -1;
-//     }
+    if ((mme_desc_p = s1ap_eNB_get_MME(NULL, assoc_id, 0)) == NULL) {
+        S1AP_ERROR("[SCTP %d] Received initial context setup request for non "
+                   "existing MME context\n", assoc_id);
+        return -1;
+    }
+
+    s1ap_eNB_instance = mme_desc_p->s1ap_eNB_instance;
+
+    if ((ue_desc_p = s1ap_eNB_get_ue_context(s1ap_eNB_instance,
+         downlink_NAS_transport_p->eNB_UE_S1AP_ID)) == NULL)
+    {
+        S1AP_ERROR("[SCTP %d] Received initial context setup request for non "
+                   "existing UE context\n", assoc_id);
+        return -1;
+    }
 
     /* Is it the first outcome of the MME for this UE ? If so store the mme
      * UE s1ap id.
      */
-//     if (ue_desc_p->mme_ue_s1ap_id == 0) {
-//         ue_desc_p->mme_ue_s1ap_id = downlink_NAS_transport_p->mme_ue_s1ap_id;
-//     } else {
-//         /* We already have a mme ue s1ap id check the received is the same */
-//         if (ue_desc_p->mme_ue_s1ap_id != downlink_NAS_transport_p->mme_ue_s1ap_id) {
-//             S1AP_ERROR("[SCTP %d] Mismatch is MME UE S1AP ID (0x%08x != 0x%08x)\n",
-//                        downlink_NAS_transport_p->mme_ue_s1ap_id,
-//                        ue_desc_p->mme_ue_s1ap_id,
-//                        packet_p->assoc_id);
-//         }
-//     }
+    if (ue_desc_p->mme_ue_s1ap_id == 0) {
+        ue_desc_p->mme_ue_s1ap_id = downlink_NAS_transport_p->mme_ue_s1ap_id;
+    } else {
+        /* We already have a mme ue s1ap id check the received is the same */
+        if (ue_desc_p->mme_ue_s1ap_id != downlink_NAS_transport_p->mme_ue_s1ap_id) {
+            S1AP_ERROR("[SCTP %d] Mismatch is MME UE S1AP ID (0x%08x != 0x%08x)\n",
+                       downlink_NAS_transport_p->mme_ue_s1ap_id,
+                       ue_desc_p->mme_ue_s1ap_id,
+                       assoc_id);
+        }
+    }
 
-    /* TODO: forward NAS pdu to RRC for transmission */
+    /* Forward the NAS PDU to RRC */
+    s1ap_eNB_itti_send_nas_downlink_ind(s1ap_eNB_instance->mod_id,
+                                        downlink_NAS_transport_p->nas_pdu.buf,
+                                        downlink_NAS_transport_p->nas_pdu.size);
 
     return 0;
 }
 
-// int s1ap_eNB_nas_uplink(eNB_mme_desc_t    *eNB_desc_p,
-//                         s1ap_nas_uplink_t *nas_uplink_p)
-// {
-//     struct s1ap_eNB_ue_context_s *ue_context_p;
-//     UplinkNASTransportIEs_t      *uplink_NAS_transport_p;
-// 
-//     s1ap_message  message;
-// 
-//     uint8_t  *buffer;
-//     uint32_t length;
-//     int      ret = -1;
-// 
-//     DevAssert(nas_uplink_p != NULL);
-//     DevAssert(eNB_desc_p != NULL);
-// 
-//     if ((ue_context_p = s1ap_eNB_get_ue_context(eNB_desc_p, nas_uplink_p->eNB_ue_s1ap_id)) == NULL)
-//     {
-//         /* The context for this eNB ue s1ap id doesn't exist in the map of eNB UEs */
-//         S1AP_WARN("Failed to find ue context associated with eNB ue s1ap id: %u\n",
-//                   nas_uplink_p->eNB_ue_s1ap_id);
-//         return -1;
-//     }
-// 
-//     /* Uplink NAS transport can occur either during an s1ap connected state
-//      * or during initial attach (for example: NAS authentication).
-//      */
-//     if (!(ue_context_p->ue_state == S1AP_UE_CONNECTED ||
-//         ue_context_p->ue_state == S1AP_UE_WAITING_CSR))
-//     {
-//         S1AP_WARN("You are attempting to send NAS data over non-connected "
-//                   "eNB ue s1ap id: %u, current state: %d\n",
-//                   nas_uplink_p->eNB_ue_s1ap_id, ue_context_p->ue_state);
-//         return -1;
-//     }
-// 
-//     /* Prepare the S1AP message to encode */
-//     memset(&message, 0, sizeof(s1ap_message));
-// 
-//     message.direction = S1AP_PDU_PR_initiatingMessage;
-//     message.procedureCode = ProcedureCode_id_uplinkNASTransport;
-// 
-//     uplink_NAS_transport_p = &message.msg.uplinkNASTransportIEs;
-// 
-//     uplink_NAS_transport_p->mme_ue_s1ap_id = ue_context_p->mme_ue_s1ap_id;
-//     uplink_NAS_transport_p->eNB_UE_S1AP_ID = ue_context_p->eNB_ue_s1ap_id;
-// 
-//     uplink_NAS_transport_p->nas_pdu.buf  = nas_uplink_p->nas_pdu.buffer;
-//     uplink_NAS_transport_p->nas_pdu.size = nas_uplink_p->nas_pdu.length;
-// 
-//     MCC_MNC_TO_PLMNID(eNB_desc_p->mcc, eNB_desc_p->mnc,
-//                       &uplink_NAS_transport_p->eutran_cgi.pLMNidentity);
-//     MACRO_ENB_ID_TO_CELL_IDENTITY(eNB_desc_p->eNB_id,
-//                                   &uplink_NAS_transport_p->eutran_cgi.cell_ID);
-// 
-//     /* MCC/MNC should be repeated in TAI and EUTRAN CGI */
-//     MCC_MNC_TO_PLMNID(eNB_desc_p->mcc, eNB_desc_p->mnc,
-//                       &uplink_NAS_transport_p->tai.pLMNidentity);
-//     TAC_TO_ASN1(eNB_desc_p->tac, &uplink_NAS_transport_p->tai.tAC);
-// 
-//     if (s1ap_eNB_encode_pdu(&message, &buffer, &length) < 0) {
-//         S1AP_ERROR("Failed to encode uplink NAS transport\n");
-//         /* Encode procedure has failed... */
-//         return -1;
-//     }
-// 
-//     /* UE associated signalling -> use the allocated stream */
-//     if ((ret = sctp_send_msg(&ue_context_p->mme_ref->sctp_data, S1AP_SCTP_PPID,
-//         ue_context_p->stream, buffer, length)) < 0)
-//     {
-//         S1AP_ERROR("[SCTP %d] Failed to send message over SCTP: %d\n",
-//                    ue_context_p->mme_ref->sctp_data.assoc_id, ret);
-//     }
-// 
-//     free(buffer);
-//     return ret;
-// }
+int s1ap_eNB_nas_uplink(s1ap_uplink_nas_t *s1ap_uplink_nas_p)
+{
+    struct s1ap_eNB_ue_context_s *ue_context_p;
+    UplinkNASTransportIEs_t      *uplink_NAS_transport_p;
+    s1ap_eNB_instance_t          *s1ap_eNB_instance_p;
+
+    s1ap_message  message;
+
+    uint8_t  *buffer;
+    uint32_t length;
+
+    DevAssert(s1ap_uplink_nas_p != NULL);
+
+    /* Retrieve the S1AP eNB instance associated with Mod_id */
+    s1ap_eNB_instance_p = s1ap_eNB_get_instance(s1ap_uplink_nas_p->mod_id);
+    DevAssert(s1ap_eNB_instance_p != NULL);
+
+    if ((ue_context_p = s1ap_eNB_get_ue_context(s1ap_eNB_instance_p, s1ap_uplink_nas_p->eNB_ue_s1ap_id)) == NULL)
+    {
+        /* The context for this eNB ue s1ap id doesn't exist in the map of eNB UEs */
+        S1AP_WARN("Failed to find ue context associated with eNB ue s1ap id: %u\n",
+                  s1ap_uplink_nas_p->eNB_ue_s1ap_id);
+        return -1;
+    }
+
+    /* Uplink NAS transport can occur either during an s1ap connected state
+     * or during initial attach (for example: NAS authentication).
+     */
+    if (!(ue_context_p->ue_state == S1AP_UE_CONNECTED ||
+        ue_context_p->ue_state == S1AP_UE_WAITING_CSR))
+    {
+        S1AP_WARN("You are attempting to send NAS data over non-connected "
+                  "eNB ue s1ap id: %u, current state: %d\n",
+                  s1ap_uplink_nas_p->eNB_ue_s1ap_id, ue_context_p->ue_state);
+        return -1;
+    }
+
+    /* Prepare the S1AP message to encode */
+    memset(&message, 0, sizeof(s1ap_message));
+
+    message.direction = S1AP_PDU_PR_initiatingMessage;
+    message.procedureCode = ProcedureCode_id_uplinkNASTransport;
+
+    uplink_NAS_transport_p = &message.msg.uplinkNASTransportIEs;
+
+    uplink_NAS_transport_p->mme_ue_s1ap_id = ue_context_p->mme_ue_s1ap_id;
+    uplink_NAS_transport_p->eNB_UE_S1AP_ID = ue_context_p->eNB_ue_s1ap_id;
+
+    uplink_NAS_transport_p->nas_pdu.buf  = s1ap_uplink_nas_p->nas_pdu.buffer;
+    uplink_NAS_transport_p->nas_pdu.size = s1ap_uplink_nas_p->nas_pdu.length;
+
+    MCC_MNC_TO_PLMNID(s1ap_eNB_instance_p->mcc, s1ap_eNB_instance_p->mnc,
+                      &uplink_NAS_transport_p->eutran_cgi.pLMNidentity);
+    MACRO_ENB_ID_TO_CELL_IDENTITY(s1ap_eNB_instance_p->eNB_id,
+                                  &uplink_NAS_transport_p->eutran_cgi.cell_ID);
+
+    /* MCC/MNC should be repeated in TAI and EUTRAN CGI */
+    MCC_MNC_TO_PLMNID(s1ap_eNB_instance_p->mcc, s1ap_eNB_instance_p->mnc,
+                      &uplink_NAS_transport_p->tai.pLMNidentity);
+    TAC_TO_ASN1(s1ap_eNB_instance_p->tac, &uplink_NAS_transport_p->tai.tAC);
+
+    if (s1ap_eNB_encode_pdu(&message, &buffer, &length) < 0) {
+        S1AP_ERROR("Failed to encode uplink NAS transport\n");
+        /* Encode procedure has failed... */
+        return -1;
+    }
+
+    /* UE associated signalling -> use the allocated stream */
+    s1ap_eNB_itti_send_sctp_data_req(ue_context_p->mme_ref->assoc_id, buffer,
+                                     length, ue_context_p->stream);
+
+    return 0;
+}
 
-// int s1ap_eNB_initial_ctxt_resp(eNB_mme_desc_t                 *eNB_desc_p,
+// int s1ap_eNB_initial_ctxt_resp(eNB_mme_desc_t                 *s1ap_eNB_instance_p,
 //                                s1ap_initial_ctxt_setup_resp_t *initial_ctxt_resp_p)
 // {
 //     struct s1ap_eNB_ue_context_s *ue_context_p;
@@ -193,9 +323,9 @@ int s1ap_eNB_handle_nas_downlink(uint32_t               assoc_id,
 //     int      i;
 // 
 //     DevAssert(initial_ctxt_resp_p != NULL);
-//     DevAssert(eNB_desc_p != NULL);
+//     DevAssert(s1ap_eNB_instance_p != NULL);
 // 
-//     if ((ue_context_p = s1ap_eNB_get_ue_context(eNB_desc_p,
+//     if ((ue_context_p = s1ap_eNB_get_ue_context(s1ap_eNB_instance_p,
 //         initial_ctxt_resp_p->eNB_ue_s1ap_id)) == NULL)
 //     {
 //         /* The context for this eNB ue s1ap id doesn't exist in the map of eNB UEs */
diff --git a/openair-cn/S1AP/s1ap_eNB_nas_procedures.h b/openair-cn/S1AP/s1ap_eNB_nas_procedures.h
index 50828b8aeda391af2a2e1bc6e6853bceb89c6437..6445f1e00d278cfd58c0f93a83135c9b5b4a01c0 100644
--- a/openair-cn/S1AP/s1ap_eNB_nas_procedures.h
+++ b/openair-cn/S1AP/s1ap_eNB_nas_procedures.h
@@ -35,6 +35,8 @@ int s1ap_eNB_handle_nas_downlink(uint32_t               assoc_id,
                                  uint32_t               stream,
                                  struct s1ap_message_s *message_p);
 
+int s1ap_eNB_handle_nas_first_req(s1ap_nas_first_req_t *s1ap_nas_first_req_p);
+
 // int s1ap_eNB_initial_ctxt_resp(eNB_mme_desc_t                 *eNB_desc_p,
 //                                s1ap_initial_ctxt_setup_resp_t *initial_ctxt_resp_p);
 
diff --git a/openair-cn/S1AP/s1ap_eNB_nnsf.h b/openair-cn/S1AP/s1ap_eNB_nnsf.h
index f24e7be7c43ba64a3d91b8f289bc3356224cc33f..51d3835d933acda9a3f2ebec5dfc39c20855ce9d 100644
--- a/openair-cn/S1AP/s1ap_eNB_nnsf.h
+++ b/openair-cn/S1AP/s1ap_eNB_nnsf.h
@@ -28,11 +28,6 @@
 
 *******************************************************************************/
 
-/** @defgroup _s1ap_impl_ S1AP Layer Reference Implementation
- * @ingroup _ref_implementation_
- * @{
- */
-
 #ifndef S1AP_ENB_NNSF_H_
 #define S1AP_ENB_NNSF_H_
 
diff --git a/openair-cn/S1AP/s1ap_eNB_overload.c b/openair-cn/S1AP/s1ap_eNB_overload.c
index 1e85763ab0ee2a517eec94d2aaedb73dd9240047..0f9ed2406cdcc06f6ae1ab2b9c22cff780eee0cb 100644
--- a/openair-cn/S1AP/s1ap_eNB_overload.c
+++ b/openair-cn/S1AP/s1ap_eNB_overload.c
@@ -54,9 +54,6 @@
 
 #include "assertions.h"
 
-// int s1ap_eNB_handle_overload_start(eNB_mme_desc_t *eNB_desc_p,
-//                                    sctp_queue_item_t *packet_p,
-//                                    struct s1ap_message_s *message_p)
 int s1ap_eNB_handle_overload_start(uint32_t               assoc_id,
                                    uint32_t               stream,
                                    struct s1ap_message_s *message_p)
@@ -68,25 +65,24 @@ int s1ap_eNB_handle_overload_start(uint32_t               assoc_id,
 
     overload_start_p = &message_p->msg.overloadStartIEs;
 
-//     DevCheck(overload_start_p->overloadResponse.present ==
-//              OverloadResponse_PR_overloadAction,
-//              OverloadResponse_PR_overloadAction, 0, 0);
-// 
-//     /* Non UE-associated signalling -> stream 0 */
-//     DevCheck(packet_p->local_stream == 0, packet_p->local_stream,
-//              packet_p->remote_port, packet_p->assoc_id);
-// 
-//     if ((mme_desc_p = s1ap_eNB_get_MME(eNB_desc_p, packet_p->assoc_id)) == NULL) {
-//         /* No MME context associated */
-//         return -1;
-//     }
-// 
-//     /* Mark the MME as overloaded and set the overload state according to
-//      * the value received.
-//      */
-//     mme_desc_p->state = S1AP_ENB_OVERLOAD;
-//     mme_desc_p->overload_state =
-//         overload_start_p->overloadResponse.choice.overloadAction;
+    DevCheck(overload_start_p->overloadResponse.present ==
+             OverloadResponse_PR_overloadAction,
+             OverloadResponse_PR_overloadAction, 0, 0);
+
+    /* Non UE-associated signalling -> stream 0 */
+    DevCheck(stream == 0, stream, 0, 0);
+
+    if ((mme_desc_p = s1ap_eNB_get_MME(NULL, assoc_id, 0)) == NULL) {
+        /* No MME context associated */
+        return -1;
+    }
+
+    /* Mark the MME as overloaded and set the overload state according to
+     * the value received.
+     */
+    mme_desc_p->state = S1AP_ENB_OVERLOAD;
+    mme_desc_p->overload_state =
+        overload_start_p->overloadResponse.choice.overloadAction;
 
     return 0;
 }
@@ -94,9 +90,6 @@ int s1ap_eNB_handle_overload_start(uint32_t               assoc_id,
 int s1ap_eNB_handle_overload_stop(uint32_t               assoc_id,
                                   uint32_t               stream,
                                   struct s1ap_message_s *message_p)
-// int s1ap_eNB_handle_overload_stop(eNB_mme_desc_t *eNB_desc_p,
-//                                   sctp_queue_item_t *packet_p,
-//                                   struct s1ap_message_s *message_p)
 {
     /* We received Overload stop message, meaning that the MME is no more
      * overloaded. This is an empty message, with only message header and no
@@ -105,18 +98,17 @@ int s1ap_eNB_handle_overload_stop(uint32_t               assoc_id,
 
     DevAssert(message_p != NULL);
 
-//     s1ap_eNB_mme_data_t *mme_desc_p;
-// 
-//     /* Non UE-associated signalling -> stream 0 */
-//     DevCheck(packet_p->local_stream == 0, packet_p->local_stream,
-//              packet_p->remote_port, packet_p->assoc_id);
-// 
-//     if ((mme_desc_p = s1ap_eNB_get_MME(eNB_desc_p, packet_p->assoc_id)) == NULL) {
-//         /* No MME context associated */
-//         return -1;
-//     }
-// 
-//     mme_desc_p->state = S1AP_ENB_STATE_CONNECTED;
-//     mme_desc_p->overload_state = S1AP_NO_OVERLOAD;
+    s1ap_eNB_mme_data_t *mme_desc_p;
+
+    /* Non UE-associated signalling -> stream 0 */
+    DevCheck(stream == 0, stream, 0, 0);
+
+    if ((mme_desc_p = s1ap_eNB_get_MME(NULL, assoc_id, 0)) == NULL) {
+        /* No MME context associated */
+        return -1;
+    }
+
+    mme_desc_p->state = S1AP_ENB_STATE_CONNECTED;
+    mme_desc_p->overload_state = S1AP_NO_OVERLOAD;
     return 0;
 }
diff --git a/openair-cn/S1AP/s1ap_eNB_trace.c b/openair-cn/S1AP/s1ap_eNB_trace.c
index 90ff42a294e8be8f3db1d12b0f710ccad76522f3..bd0636ef6a8afa7e7f5d7902c081655d90647246 100644
--- a/openair-cn/S1AP/s1ap_eNB_trace.c
+++ b/openair-cn/S1AP/s1ap_eNB_trace.c
@@ -30,6 +30,8 @@
 
 #include <stdint.h>
 
+#include "assertions.h"
+
 #include "intertask_interface.h"
 
 #include "s1ap_eNB_default_values.h"
@@ -42,95 +44,81 @@
 #include "s1ap_eNB_ue_context.h"
 #include "s1ap_eNB_encoder.h"
 #include "s1ap_eNB_trace.h"
+#include "s1ap_eNB_itti_messaging.h"
 
-#include "sctp_primitives_client.h"
+static
+void s1ap_eNB_generate_trace_failure(struct s1ap_eNB_ue_context_s *ue_desc_p,
+                                     E_UTRAN_Trace_ID_t           *trace_id,
+                                     Cause_t                      *cause_p)
+{
+    s1ap_message message;
+    TraceFailureIndicationIEs_t *trace_failure_p;
+    uint8_t  *buffer;
+    uint32_t  length;
 
-#include "assertions.h"
+    DevAssert(ue_desc_p != NULL);
+    DevAssert(trace_id  != NULL);
+    DevAssert(cause_p   != NULL);
 
-// int s1ap_eNB_generate_trace_failure(sctp_data_t        *sctp_data_p,
-//                                     int32_t             stream,
-//                                     uint32_t            eNB_ue_s1ap_id,
-//                                     uint32_t            mme_ue_s1ap_id,
-//                                     E_UTRAN_Trace_ID_t *trace_id,
-//                                     Cause_t            *cause_p)
-// {
-//     s1ap_message message;
-//     TraceFailureIndicationIEs_t *trace_failure_p;
-//     uint8_t  *buffer;
-//     uint32_t  length;
-//     int       ret;
-// 
-//     DevAssert(sctp_data_p != NULL);
-// 
-//     memset(&message, 0, sizeof(s1ap_message));
-// 
-//     trace_failure_p = &message.msg.traceFailureIndicationIEs;
-// 
-//     trace_failure_p->mme_ue_s1ap_id = mme_ue_s1ap_id;
-//     trace_failure_p->eNB_UE_S1AP_ID = eNB_ue_s1ap_id;
-// 
-//     memcpy(&trace_failure_p->e_UTRAN_Trace_ID, trace_id, sizeof(E_UTRAN_Trace_ID_t));
-//     memcpy(&trace_failure_p->cause, cause_p, sizeof(Cause_t));
-// 
-//     if (s1ap_eNB_encode_pdu(&message, &buffer, &length) < 0) {
-//         return -1;
-//     }
-//     if ((ret = sctp_send_msg(sctp_data_p, S1AP_SCTP_PPID,
-//                              stream, buffer, length)) < 0) {
-//         S1AP_ERROR("Failed to send Trace failure\n");
-//     }
-//     free(buffer);
-//     return ret;
-// }
+    memset(&message, 0, sizeof(s1ap_message));
+
+    trace_failure_p = &message.msg.traceFailureIndicationIEs;
+
+    trace_failure_p->mme_ue_s1ap_id = ue_desc_p->mme_ue_s1ap_id;
+    trace_failure_p->eNB_UE_S1AP_ID = ue_desc_p->eNB_ue_s1ap_id;
+
+    memcpy(&trace_failure_p->e_UTRAN_Trace_ID, trace_id, sizeof(E_UTRAN_Trace_ID_t));
+    memcpy(&trace_failure_p->cause, cause_p, sizeof(Cause_t));
+
+    if (s1ap_eNB_encode_pdu(&message, &buffer, &length) < 0) {
+        return;
+    }
+
+    s1ap_eNB_itti_send_sctp_data_req(ue_desc_p->mme_ref->assoc_id, buffer,
+                                     length, ue_desc_p->stream);
+}
 
 int s1ap_eNB_handle_trace_start(uint32_t               assoc_id,
                                 uint32_t               stream,
                                 struct s1ap_message_s *message_p)
-// int s1ap_eNB_handle_trace_start(eNB_mme_desc_t *eNB_desc_p,
-//                                 sctp_queue_item_t *packet_p,
-//                                 struct s1ap_message_s *message_p)
 {
     TraceStartIEs_t              *trace_start_p;
     struct s1ap_eNB_ue_context_s *ue_desc_p;
+    struct s1ap_eNB_mme_data_s   *mme_ref_p;
 
     DevAssert(message_p != NULL);
 
     trace_start_p = &message_p->msg.traceStartIEs;
 
-//     if ((ue_desc_p = s1ap_eNB_get_ue_context(eNB_desc_p,
-//                      trace_start_p->eNB_UE_S1AP_ID)) == NULL) {
-//         /* Could not find context associated with this eNB_ue_s1ap_id -> generate
-//          * trace failure indication.
-//          */
-//         struct s1ap_eNB_mme_data_s *mme_ref_p;
-//         E_UTRAN_Trace_ID_t trace_id;
-//         Cause_t cause;
-// 
-//         memset(&trace_id, 0, sizeof(E_UTRAN_Trace_ID_t));
-//         memset(&cause, 0, sizeof(Cause_t));
-//         mme_ref_p = s1ap_eNB_get_MME(eNB_desc_p, packet_p->assoc_id);
-// 
-//         cause.present = Cause_PR_radioNetwork;
-//         cause.choice.radioNetwork = CauseRadioNetwork_unknown_pair_ue_s1ap_id;
-// 
-//         return s1ap_eNB_generate_trace_failure(&mme_ref_p->sctp_data,
-//                                                packet_p->local_stream,
-//                                                trace_start_p->eNB_UE_S1AP_ID,
-//                                                trace_start_p->mme_ue_s1ap_id, &trace_id, &cause);
-//     }
+    mme_ref_p = s1ap_eNB_get_MME(NULL, assoc_id, 0);
+    DevAssert(mme_ref_p != NULL);
+
+    if ((ue_desc_p = s1ap_eNB_get_ue_context(mme_ref_p->s1ap_eNB_instance,
+                     trace_start_p->eNB_UE_S1AP_ID)) == NULL) {
+        /* Could not find context associated with this eNB_ue_s1ap_id -> generate
+         * trace failure indication.
+         */
+        E_UTRAN_Trace_ID_t trace_id;
+        Cause_t cause;
+
+        memset(&trace_id, 0, sizeof(E_UTRAN_Trace_ID_t));
+        memset(&cause, 0, sizeof(Cause_t));
+
+        cause.present = Cause_PR_radioNetwork;
+        cause.choice.radioNetwork = CauseRadioNetwork_unknown_pair_ue_s1ap_id;
+
+        s1ap_eNB_generate_trace_failure(ue_desc_p, &trace_id, &cause);
+    }
     return 0;
 }
 
 int s1ap_eNB_handle_deactivate_trace(uint32_t               assoc_id,
                                      uint32_t               stream,
                                      struct s1ap_message_s *message_p)
-// int s1ap_eNB_handle_deactivate_trace(eNB_mme_desc_t *eNB_desc_p,
-//                                      sctp_queue_item_t *packet_p,
-//                                      struct s1ap_message_s *message_p)
 {
-    DeactivateTraceIEs_t *deactivate_trace_p;
-
-    deactivate_trace_p = &message_p->msg.deactivateTraceIEs;
+//     DeactivateTraceIEs_t *deactivate_trace_p;
+// 
+//     deactivate_trace_p = &message_p->msg.deactivateTraceIEs;
 
     return 0;
 }