From 1729b3dc660aa96cd775fcc6482cb7ea38a28861 Mon Sep 17 00:00:00 2001
From: Konstantinos Alexandris <alexandr@eurecom.fr>
Date: Thu, 6 Sep 2018 15:49:59 +0200
Subject: [PATCH] X2AP: init funcs (register/association) + handler/management
 functions

SCTP: one to many sockets implementation
---
 cmake_targets/CMakeLists.txt                  |  20 +-
 openair2/COMMON/sctp_messages_def.h           |  18 +-
 openair2/COMMON/sctp_messages_types.h         |  16 +-
 openair2/ENB_APP/enb_app.c                    |  61 ++-
 openair2/X2AP/x2ap_common.h                   |   2 +
 openair2/X2AP/x2ap_eNB.c                      | 419 ++++++++++++++++++
 openair2/X2AP/{x2ap.h => x2ap_eNB.h}          |   8 +-
 openair2/X2AP/x2ap_eNB_defs.h                 | 178 ++++++++
 openair2/X2AP/x2ap_eNB_handler.c              |  79 ++++
 openair2/X2AP/x2ap_eNB_handler.h              |  33 ++
 .../X2AP/x2ap_eNB_management_procedures.c     | 236 ++++++++++
 ...2ap.c => x2ap_eNB_management_procedures.h} |  53 +--
 openair3/SCTP/sctp_common.c                   |   6 +
 openair3/SCTP/sctp_eNB_task.c                 | 306 ++++++++++++-
 targets/COMMON/create_tasks.c                 |   6 +
 15 files changed, 1354 insertions(+), 87 deletions(-)
 create mode 100644 openair2/X2AP/x2ap_eNB.c
 rename openair2/X2AP/{x2ap.h => x2ap_eNB.h} (82%)
 create mode 100644 openair2/X2AP/x2ap_eNB_defs.h
 create mode 100644 openair2/X2AP/x2ap_eNB_handler.c
 create mode 100644 openair2/X2AP/x2ap_eNB_handler.h
 create mode 100644 openair2/X2AP/x2ap_eNB_management_procedures.c
 rename openair2/X2AP/{x2ap.c => x2ap_eNB_management_procedures.h} (52%)

diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt
index fdb36a8e1d..da4e3c6fa3 100644
--- a/cmake_targets/CMakeLists.txt
+++ b/cmake_targets/CMakeLists.txt
@@ -474,15 +474,15 @@ add_library(X2AP_LIB
 include_directories ("${X2AP_C_DIR}")
 include_directories ("${X2AP_DIR}")
 
-#add_library(X2AP_ENB
- # ${X2AP_DIR}/x2ap_eNB.c
+add_library(X2AP_ENB
+  ${X2AP_DIR}/x2ap_eNB.c
  # ${X2AP_DIR}/x2ap_eNB_decoder.c
  # ${X2AP_DIR}/x2ap_eNB_encoder.c
- # ${X2AP_DIR}/x2ap_eNB_handler.c
+  ${X2AP_DIR}/x2ap_eNB_handler.c
  # ${X2AP_DIR}/x2ap_eNB_itti_messaging.c
- # ${X2AP_DIR}/x2ap_eNB_management_procedures.c
+  ${X2AP_DIR}/x2ap_eNB_management_procedures.c
  # ${X2AP_DIR}/x2ap_eNB_generate_messages.c
- # )
+ )
 
 # Hardware dependant options
 ###################################
@@ -1947,7 +1947,7 @@ add_executable(lte-softmodem
 
 target_link_libraries (lte-softmodem
   -Wl,--start-group
-  RRC_LIB S1AP_LIB S1AP_ENB X2AP_LIB GTPV1U SECU_CN SECU_OSA UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB SCHED_RU_LIB PHY_COMMON PHY PHY_RU LFDS L2
+  RRC_LIB S1AP_LIB S1AP_ENB X2AP_LIB X2AP_ENB GTPV1U SECU_CN SECU_OSA UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB SCHED_RU_LIB PHY_COMMON PHY PHY_RU LFDS L2
   ${MSC_LIB} ${RAL_LIB} ${NAS_UE_LIB} ${ITTI_LIB} ${FLPT_MSG_LIB} ${ASYNC_IF_LIB} ${FLEXRAN_AGENT_LIB} LFDS7
   NFAPI_COMMON_LIB NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB
   -Wl,--end-group z dl)
@@ -2023,7 +2023,7 @@ add_executable(lte-uesoftmodem
 
 target_link_libraries (lte-uesoftmodem
   -Wl,--start-group
-  RRC_LIB S1AP_LIB S1AP_ENB X2AP_LIB GTPV1U SECU_CN SECU_OSA UTIL HASHTABLE SCTP_CLIENT UDP SCHED_RU_LIB SCHED_UE_LIB PHY_COMMON PHY_UE PHY_RU LFDS L2_UE SIMU
+  RRC_LIB S1AP_LIB S1AP_ENB X2AP_LIB X2AP_ENB GTPV1U SECU_CN SECU_OSA UTIL HASHTABLE SCTP_CLIENT UDP SCHED_RU_LIB SCHED_UE_LIB PHY_COMMON PHY_UE PHY_RU LFDS L2_UE SIMU
   ${MSC_LIB} ${RAL_LIB} ${NAS_UE_LIB} ${ITTI_LIB} ${FLPT_MSG_LIB} ${ASYNC_IF_LIB} LFDS7 ${ATLAS_LIBRARIES}
   NFAPI_COMMON_LIB NFAPI_LIB NFAPI_PNF_LIB NFAPI_USER_LIB
   -Wl,--end-group z dl)
@@ -2140,7 +2140,7 @@ add_executable(test_epc_generate_scenario
   ${OPENAIR3_DIR}/S1AP/s1ap_eNB_defs.h
   )
 target_link_libraries (test_epc_generate_scenario
-  -Wl,--start-group RRC_LIB S1AP_LIB S1AP_ENB X2AP_LIB GTPV1U LIB_NAS_UE SECU_CN UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB PHY LFDS ${ITTI_LIB} ${MSC_LIB} L2 -Wl,--end-group pthread m rt crypt sctp ${LIBXML2_LIBRARIES} ${LIBXSLT_LIBRARIES} ${CRYPTO_LIBRARIES} ${OPENSSL_LIBRARIES} ${NETTLE_LIBRARIES} ${CONFIG_LIBRARIES}
+  -Wl,--start-group RRC_LIB S1AP_LIB S1AP_ENB X2AP_LIB X2AP_ENB GTPV1U LIB_NAS_UE SECU_CN UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB PHY LFDS ${ITTI_LIB} ${MSC_LIB} L2 -Wl,--end-group pthread m rt crypt sctp ${LIBXML2_LIBRARIES} ${LIBXSLT_LIBRARIES} ${CRYPTO_LIBRARIES} ${OPENSSL_LIBRARIES} ${NETTLE_LIBRARIES} ${CONFIG_LIBRARIES}
   )
 
 add_executable(test_epc_play_scenario
@@ -2160,7 +2160,7 @@ add_executable(test_epc_play_scenario
   )
 target_include_directories(test_epc_play_scenario PUBLIC /usr/local/share/asn1c)
 target_link_libraries (test_epc_play_scenario
-  -Wl,--start-group RRC_LIB S1AP_LIB X2AP_LIB GTPV1U LIB_NAS_UE SECU_CN UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB PHY_COMMON PHY PHY_UE LFDS ${ITTI_LIB} ${MSC_LIB} -Wl,--end-group pthread m rt crypt sctp ${LIBXML2_LIBRARIES} ${LIBXSLT_LIBRARIES} ${CRYPTO_LIBRARIES} ${OPENSSL_LIBRARIES} ${NETTLE_LIBRARIES} ${CONFIG_LIBRARIES}
+  -Wl,--start-group RRC_LIB S1AP_LIB X2AP_LIB X2AP_ENB GTPV1U LIB_NAS_UE SECU_CN UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB PHY_COMMON PHY PHY_UE LFDS ${ITTI_LIB} ${MSC_LIB} -Wl,--end-group pthread m rt crypt sctp ${LIBXML2_LIBRARIES} ${LIBXSLT_LIBRARIES} ${CRYPTO_LIBRARIES} ${OPENSSL_LIBRARIES} ${NETTLE_LIBRARIES} ${CONFIG_LIBRARIES}
   )
 
 
@@ -2196,7 +2196,7 @@ if (${T_TRACER})
         dlsim_tm4 dlsim dlsim_tm7 ulsim pbchsim scansim mbmssim
         pdcchsim pucchsim prachsim syncsim ulsim
         #all "add_library" definitions
-        ITTI RRC_LIB S1AP_LIB S1AP_ENB X2AP_LIB
+        ITTI RRC_LIB S1AP_LIB S1AP_ENB X2AP_LIB X2AP_ENB
         oai_exmimodevif oai_usrpdevif oai_bladerfdevif oai_lmssdrdevif
         oai_eth_transpro
         FLPT_MSG ASYNC_IF FLEXRAN_AGENT HASHTABLE MSC UTIL OMG_SUMO SECU_OSA
diff --git a/openair2/COMMON/sctp_messages_def.h b/openair2/COMMON/sctp_messages_def.h
index d4248b7bc3..4975f14d76 100644
--- a/openair2/COMMON/sctp_messages_def.h
+++ b/openair2/COMMON/sctp_messages_def.h
@@ -19,11 +19,13 @@
  *      contact@openairinterface.org
  */
 
-MESSAGE_DEF(SCTP_NEW_ASSOCIATION_REQ , MESSAGE_PRIORITY_MED, sctp_new_association_req_t          , sctp_new_association_req)
-MESSAGE_DEF(SCTP_NEW_ASSOCIATION_RESP, MESSAGE_PRIORITY_MED, sctp_new_association_resp_t         , sctp_new_association_resp)
-MESSAGE_DEF(SCTP_NEW_ASSOCIATION_IND , MESSAGE_PRIORITY_MED, sctp_new_association_ind_t          , sctp_new_association_ind)
-MESSAGE_DEF(SCTP_REGISTER_UPPER_LAYER, MESSAGE_PRIORITY_MED, sctp_listener_register_upper_layer_t, sctp_listener_register_upper_layer)
-MESSAGE_DEF(SCTP_DATA_REQ,             MESSAGE_PRIORITY_MED, sctp_data_req_t                     , sctp_data_req)
-MESSAGE_DEF(SCTP_DATA_IND,             MESSAGE_PRIORITY_MED, sctp_data_ind_t                     , sctp_data_ind)
-MESSAGE_DEF(SCTP_INIT_MSG,             MESSAGE_PRIORITY_MED, sctp_init_t                         , sctp_init)
-MESSAGE_DEF(SCTP_CLOSE_ASSOCIATION,    MESSAGE_PRIORITY_MAX, sctp_close_association_t            , sctp_close_association)
+MESSAGE_DEF(SCTP_NEW_ASSOCIATION_REQ ,      MESSAGE_PRIORITY_MED, sctp_new_association_req_t          , sctp_new_association_req)
+MESSAGE_DEF(SCTP_NEW_ASSOCIATION_REQ_MULTI, MESSAGE_PRIORITY_MED, sctp_new_association_req_t          , sctp_new_association_req_multi)
+MESSAGE_DEF(SCTP_NEW_ASSOCIATION_RESP,      MESSAGE_PRIORITY_MED, sctp_new_association_resp_t         , sctp_new_association_resp)
+MESSAGE_DEF(SCTP_NEW_ASSOCIATION_IND ,      MESSAGE_PRIORITY_MED, sctp_new_association_ind_t          , sctp_new_association_ind)
+MESSAGE_DEF(SCTP_REGISTER_UPPER_LAYER,      MESSAGE_PRIORITY_MED, sctp_listener_register_upper_layer_t, sctp_listener_register_upper_layer)
+MESSAGE_DEF(SCTP_DATA_REQ,                  MESSAGE_PRIORITY_MED, sctp_data_req_t                     , sctp_data_req)
+MESSAGE_DEF(SCTP_DATA_IND,                  MESSAGE_PRIORITY_MED, sctp_data_ind_t                     , sctp_data_ind)
+MESSAGE_DEF(SCTP_INIT_MSG,                  MESSAGE_PRIORITY_MED, sctp_init_t                         , sctp_init)
+MESSAGE_DEF(SCTP_INIT_MSG_MULTI,            MESSAGE_PRIORITY_MED, sctp_init_t                         , sctp_init_multi)
+MESSAGE_DEF(SCTP_CLOSE_ASSOCIATION,         MESSAGE_PRIORITY_MAX, sctp_close_association_t            , sctp_close_association)
diff --git a/openair2/COMMON/sctp_messages_types.h b/openair2/COMMON/sctp_messages_types.h
index 184e951acb..90081c02aa 100644
--- a/openair2/COMMON/sctp_messages_types.h
+++ b/openair2/COMMON/sctp_messages_types.h
@@ -22,13 +22,15 @@
 #ifndef SCTP_MESSAGES_TYPES_H_
 #define SCTP_MESSAGES_TYPES_H_
 
-#define SCTP_NEW_ASSOCIATION_REQ(mSGpTR) (mSGpTR)->ittiMsg.sctp_new_association_req
-#define SCTP_NEW_ASSOCIATION_RESP(mSGpTR)(mSGpTR)->ittiMsg.sctp_new_association_resp
-#define SCTP_NEW_ASSOCIATION_IND(mSGpTR) (mSGpTR)->ittiMsg.sctp_new_association_ind
-#define SCTP_DATA_IND(mSGpTR)            (mSGpTR)->ittiMsg.sctp_data_ind
-#define SCTP_DATA_REQ(mSGpTR)            (mSGpTR)->ittiMsg.sctp_data_req
-#define SCTP_INIT_MSG(mSGpTR)            (mSGpTR)->ittiMsg.sctp_init
-#define SCTP_CLOSE_ASSOCIATION(mSGpTR)   (mSGpTR)->ittiMsg.sctp_close_association
+#define SCTP_NEW_ASSOCIATION_REQ(mSGpTR)       (mSGpTR)->ittiMsg.sctp_new_association_req
+#define SCTP_NEW_ASSOCIATION_REQ_MULTI(mSGpTR) (mSGpTR)->ittiMsg.sctp_new_association_req_multi
+#define SCTP_NEW_ASSOCIATION_RESP(mSGpTR)      (mSGpTR)->ittiMsg.sctp_new_association_resp
+#define SCTP_NEW_ASSOCIATION_IND(mSGpTR)       (mSGpTR)->ittiMsg.sctp_new_association_ind
+#define SCTP_DATA_IND(mSGpTR)                  (mSGpTR)->ittiMsg.sctp_data_ind
+#define SCTP_DATA_REQ(mSGpTR)                  (mSGpTR)->ittiMsg.sctp_data_req
+#define SCTP_INIT_MSG(mSGpTR)                  (mSGpTR)->ittiMsg.sctp_init
+#define SCTP_INIT_MSG_MULTI(mSGpTR)            (mSGpTR)->ittiMsg.sctp_init_multi
+#define SCTP_CLOSE_ASSOCIATION(mSGpTR)         (mSGpTR)->ittiMsg.sctp_close_association
 
 enum sctp_state_e {
   SCTP_STATE_CLOSED,
diff --git a/openair2/ENB_APP/enb_app.c b/openair2/ENB_APP/enb_app.c
index 2857c5e3d4..2791af6c06 100644
--- a/openair2/ENB_APP/enb_app.c
+++ b/openair2/ENB_APP/enb_app.c
@@ -46,7 +46,7 @@
 #   include "gtpv1u_eNB_task.h"
 # endif
 
-//#   include "x2ap_eNB.h"
+#   include "x2ap_eNB.h"
 #   include "x2ap_messages_types.h"
 #   define X2AP_ENB_REGISTER_RETRY_DELAY   10
 
@@ -154,7 +154,7 @@ static uint32_t eNB_app_register_x2(uint32_t enb_id_start, uint32_t enb_id_end)
 
       RCconfig_X2(msg_p, enb_id);
 
-	//itti_send_msg_to_task (TASK_X2AP, ENB_MODULE_ID_TO_INSTANCE(enb_id), msg_p);
+      itti_send_msg_to_task (TASK_X2AP, ENB_MODULE_ID_TO_INSTANCE(enb_id), msg_p);
 
       register_enb_x2_pending++;
     }
@@ -176,8 +176,8 @@ void *eNB_app_task(void *args_p)
   long                            enb_register_retry_timer_id;
 # endif
   uint32_t                        x2_register_enb_pending;
-  //uint32_t                        x2_registered_enb;
-  //long                            x2_enb_register_retry_timer_id;
+  uint32_t                        x2_registered_enb;
+  long                            x2_enb_register_retry_timer_id;
   uint32_t                        enb_id;
   MessageDef                     *msg_p           = NULL;
   instance_t                      instance;
@@ -224,7 +224,7 @@ void *eNB_app_task(void *args_p)
 # endif
 
   /* Try to register each eNB with each other */
- // x2_registered_enb = 0;
+  x2_registered_enb = 0;
   x2_register_enb_pending = eNB_app_register_x2 (enb_id_start, enb_id_end);
 
   do {
@@ -301,8 +301,59 @@ void *eNB_app_task(void *args_p)
         register_enb_pending = eNB_app_register (enb_id_start, enb_id_end);//, enb_properties_p);
       }
 
+      if (TIMER_HAS_EXPIRED (msg_p).timer_id == x2_enb_register_retry_timer_id) {
+        /* Restart the registration process */
+	x2_registered_enb = 0;
+        x2_register_enb_pending = eNB_app_register_x2 (enb_id_start, enb_id_end);
+      }
+
       break;
 # endif
+    case X2AP_DEREGISTERED_ENB_IND:
+      LOG_W(ENB_APP, "[eNB %d] Received %s: associated eNB %d\n", instance, ITTI_MSG_NAME (msg_p),
+            X2AP_DEREGISTERED_ENB_IND(msg_p).nb_x2);
+
+      /* TODO handle recovering of registration */
+      break;
+
+    case X2AP_REGISTER_ENB_CNF:
+      LOG_I(ENB_APP, "[eNB %d] Received %s: associated eNB %d\n", instance, ITTI_MSG_NAME (msg_p),
+            X2AP_REGISTER_ENB_CNF(msg_p).nb_x2);
+
+      DevAssert(x2_register_enb_pending > 0);
+      x2_register_enb_pending--;
+
+      /* Check if at least eNB is registered with one target eNB */
+      if (X2AP_REGISTER_ENB_CNF(msg_p).nb_x2 > 0) {
+        x2_registered_enb++;
+      }
+
+      /* Check if all register eNB requests have been processed */
+      if (x2_register_enb_pending == 0) {
+        if (x2_registered_enb == enb_nb) {
+          /* If all eNB are registered, start RRC HO task */
+
+	}else {
+          uint32_t x2_not_associated = enb_nb - x2_registered_enb;
+
+          LOG_W(ENB_APP, " %d eNB %s not associated with the target\n",
+                x2_not_associated, x2_not_associated > 1 ? "are" : "is");
+	  // timer to retry
+	  /* Restart the eNB registration process in ENB_REGISTER_RETRY_DELAY seconds */
+          if (timer_setup (X2AP_ENB_REGISTER_RETRY_DELAY, 0, TASK_ENB_APP,
+			   INSTANCE_DEFAULT, TIMER_ONE_SHOT, NULL,
+			   &x2_enb_register_retry_timer_id) < 0) {
+            LOG_E(ENB_APP, " Can not start eNB X2AP register: retry timer, use \"sleep\" instead!\n");
+
+            sleep(X2AP_ENB_REGISTER_RETRY_DELAY);
+            /* Restart the registration process */
+            x2_registered_enb = 0;
+            x2_register_enb_pending = eNB_app_register_x2 (enb_id_start, enb_id_end);
+          }
+        }
+      }
+
+      break;
 
     default:
       LOG_E(ENB_APP, "Received unexpected message %s\n", ITTI_MSG_NAME (msg_p));
diff --git a/openair2/X2AP/x2ap_common.h b/openair2/X2AP/x2ap_common.h
index 1a4889f0f0..a2e269dcaa 100644
--- a/openair2/X2AP/x2ap_common.h
+++ b/openair2/X2AP/x2ap_common.h
@@ -62,10 +62,12 @@ extern int asn1_xer_print;
 
 #if defined(ENB_MODE)
 # include "log.h"
+# define X2AP_INFO(x, args...) LOG_I(X2AP, x, ##args)
 # define X2AP_ERROR(x, args...) LOG_E(X2AP, x, ##args)
 # define X2AP_WARN(x, args...)  LOG_W(X2AP, x, ##args)
 # define X2AP_DEBUG(x, args...) LOG_D(X2AP, x, ##args)
 #else
+# define X2AP_INFO(x, args...) do { fprintf(stdout, "[X2AP][I]"x, ##args); } while(0)
 # define X2AP_ERROR(x, args...) do { fprintf(stdout, "[X2AP][E]"x, ##args); } while(0)
 # define X2AP_WARN(x, args...)  do { fprintf(stdout, "[X2AP][W]"x, ##args); } while(0)
 # define X2AP_DEBUG(x, args...) do { fprintf(stdout, "[X2AP][D]"x, ##args); } while(0)
diff --git a/openair2/X2AP/x2ap_eNB.c b/openair2/X2AP/x2ap_eNB.c
new file mode 100644
index 0000000000..016fa1f419
--- /dev/null
+++ b/openair2/X2AP/x2ap_eNB.c
@@ -0,0 +1,419 @@
+/*
+ * 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 <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+
+#include "intertask_interface.h"
+
+#include "x2ap_eNB.h"
+#include "x2ap_eNB_defs.h"
+#include "x2ap_eNB_management_procedures.h"
+#include "x2ap_eNB_handler.h"
+#include "x2ap_common.h"
+
+#include "queue.h"
+#include "assertions.h"
+#include "conversions.h"
+
+struct x2ap_enb_map;
+struct x2ap_eNB_data_s;
+
+RB_PROTOTYPE(x2ap_enb_map, x2ap_eNB_data_s, entry, x2ap_eNB_compare_assoc_id);
+
+//static
+//void x2ap_eNB_handle_sctp_data_ind(instance_t instance,
+  //                                 sctp_data_ind_t *sctp_data_ind);
+static
+void x2ap_eNB_handle_sctp_association_resp(instance_t instance, sctp_new_association_resp_t *sctp_new_association_resp);
+
+static
+void x2ap_eNB_handle_sctp_association_ind(instance_t instance, sctp_new_association_ind_t *sctp_new_association_ind);
+
+static
+void x2ap_eNB_handle_register_eNB(instance_t instance,
+                                  x2ap_register_enb_req_t *x2ap_register_eNB);
+static
+void x2ap_eNB_register_eNB(x2ap_eNB_instance_t *instance_p,
+                           net_ip_address_t    *target_eNB_ip_addr,
+                           net_ip_address_t    *local_ip_addr,
+                           uint16_t             in_streams,
+                           uint16_t             out_streams,
+                           uint32_t             enb_port_for_X2C);
+
+static
+void x2ap_eNB_handle_sctp_association_resp(instance_t instance,
+                                           sctp_new_association_resp_t *sctp_new_association_resp);
+
+/*
+static
+void x2ap_eNB_handle_sctp_data_ind(instance_t instance,
+				   sctp_data_ind_t *sctp_data_ind) {
+
+  int result;
+
+  DevAssert(sctp_data_ind != NULL);
+
+  x2ap_eNB_handle_message(sctp_data_ind->assoc_id, sctp_data_ind->stream,
+                          sctp_data_ind->buffer, sctp_data_ind->buffer_length);
+
+  result = itti_free(TASK_UNKNOWN, sctp_data_ind->buffer);
+  AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
+
+}*/
+
+static
+void x2ap_eNB_handle_sctp_association_resp(instance_t instance, sctp_new_association_resp_t *sctp_new_association_resp)
+{
+  x2ap_eNB_instance_t *instance_p;
+  x2ap_eNB_data_t *x2ap_enb_data_p;
+
+  DevAssert(sctp_new_association_resp != NULL);
+
+printf("x2ap_eNB_handle_sctp_association_resp at 1\n");
+dump_trees();
+
+  instance_p = x2ap_eNB_get_instance(instance);
+  DevAssert(instance_p != NULL);
+
+  /* if the assoc_id is already known, it is certainly because an IND was received
+   * before. In this case, just update streams and return
+   */
+  if (sctp_new_association_resp->assoc_id != -1) {
+    x2ap_enb_data_p = x2ap_get_eNB(instance_p, sctp_new_association_resp->assoc_id,
+                                       sctp_new_association_resp->ulp_cnx_id);
+    if (x2ap_enb_data_p != NULL) {
+      /* some sanity check - to be refined at some point */
+      if (sctp_new_association_resp->sctp_state != SCTP_STATE_ESTABLISHED) {
+        X2AP_ERROR("x2ap_enb_data_p not NULL and sctp state not SCTP_STATE_ESTABLISHED, what to do?\n");
+        abort();
+      }
+      x2ap_enb_data_p->in_streams  = sctp_new_association_resp->in_streams;
+      x2ap_enb_data_p->out_streams = sctp_new_association_resp->out_streams;
+      return;
+    }
+  }
+
+  x2ap_enb_data_p = x2ap_get_eNB(instance_p, -1,
+                                     sctp_new_association_resp->ulp_cnx_id);
+  DevAssert(x2ap_enb_data_p != NULL);
+
+printf("x2ap_eNB_handle_sctp_association_resp at 2\n");
+dump_trees();
+
+  if (sctp_new_association_resp->sctp_state != SCTP_STATE_ESTABLISHED) {
+    X2AP_WARN("Received unsuccessful result for SCTP association (%u), instance %d, cnx_id %u\n",
+              sctp_new_association_resp->sctp_state,
+              instance,
+              sctp_new_association_resp->ulp_cnx_id);
+
+    x2ap_handle_x2_setup_message(x2ap_enb_data_p,
+				 sctp_new_association_resp->sctp_state == SCTP_STATE_SHUTDOWN);
+
+    return;
+  }
+
+printf("x2ap_eNB_handle_sctp_association_resp at 3\n");
+dump_trees();
+
+  /* Update parameters */
+  x2ap_enb_data_p->assoc_id    = sctp_new_association_resp->assoc_id;
+  x2ap_enb_data_p->in_streams  = sctp_new_association_resp->in_streams;
+  x2ap_enb_data_p->out_streams = sctp_new_association_resp->out_streams;
+
+printf("x2ap_eNB_handle_sctp_association_resp at 4\n");
+dump_trees();
+
+  /* Prepare new x2 Setup Request */
+  //x2ap_eNB_generate_x2_setup_request(instance_p, x2ap_enb_data_p);
+}
+
+static
+void x2ap_eNB_handle_sctp_association_ind(instance_t instance, sctp_new_association_ind_t *sctp_new_association_ind)
+{
+  x2ap_eNB_instance_t *instance_p;
+  x2ap_eNB_data_t *x2ap_enb_data_p;
+
+printf("x2ap_eNB_handle_sctp_association_ind at 1 (called for instance %d)\n", instance);
+dump_trees();
+  DevAssert(sctp_new_association_ind != NULL);
+
+  instance_p = x2ap_eNB_get_instance(instance);
+  DevAssert(instance_p != NULL);
+
+  x2ap_enb_data_p = x2ap_get_eNB(instance_p, sctp_new_association_ind->assoc_id, -1);
+  if (x2ap_enb_data_p != NULL) abort();
+//  DevAssert(x2ap_enb_data_p != NULL);
+  if (x2ap_enb_data_p == NULL) {
+    /* Create new eNB descriptor */
+    x2ap_enb_data_p = calloc(1, sizeof(*x2ap_enb_data_p));
+    DevAssert(x2ap_enb_data_p != NULL);
+
+    x2ap_enb_data_p->cnx_id                = x2ap_eNB_fetch_add_global_cnx_id();
+
+    x2ap_enb_data_p->x2ap_eNB_instance = instance_p;
+
+    /* Insert the new descriptor in list of known eNB
+     * but not yet associated.
+     */
+    RB_INSERT(x2ap_enb_map, &instance_p->x2ap_enb_head, x2ap_enb_data_p);
+    x2ap_enb_data_p->state = X2AP_ENB_STATE_CONNECTED;
+    instance_p->x2_target_enb_nb++;
+    if (instance_p->x2_target_enb_pending_nb > 0) {
+      instance_p->x2_target_enb_pending_nb--;
+    }
+  } else {
+    X2AP_WARN("x2ap_enb_data_p already exists\n");
+  }
+
+printf("x2ap_eNB_handle_sctp_association_ind at 2\n");
+dump_trees();
+  /* Update parameters */
+  x2ap_enb_data_p->assoc_id    = sctp_new_association_ind->assoc_id;
+  x2ap_enb_data_p->in_streams  = sctp_new_association_ind->in_streams;
+  x2ap_enb_data_p->out_streams = sctp_new_association_ind->out_streams;
+
+printf("x2ap_eNB_handle_sctp_association_ind at 3\n");
+dump_trees();
+}
+
+int x2ap_eNB_init_sctp (x2ap_eNB_instance_t *instance_p,
+			net_ip_address_t    *local_ip_addr,
+			uint32_t enb_port_for_X2C)
+{
+  // Create and alloc new message
+  MessageDef                             *message;
+  sctp_init_t                            *sctp_init  = NULL;
+
+  DevAssert(instance_p != NULL);
+  DevAssert(local_ip_addr != NULL);
+
+  message = itti_alloc_new_message (TASK_X2AP, SCTP_INIT_MSG_MULTI);
+  sctp_init = &message->ittiMsg.sctp_init_multi;
+
+  sctp_init->port = enb_port_for_X2C;
+  sctp_init->ppid = X2AP_SCTP_PPID;
+  sctp_init->ipv4 = 1;
+  sctp_init->ipv6 = 0;
+  sctp_init->nb_ipv4_addr = 1;
+
+#if 0
+  memcpy(&sctp_init->ipv4_address,
+         local_ip_addr,
+         sizeof(*local_ip_addr));
+#endif
+  sctp_init->ipv4_address[0] = inet_addr(local_ip_addr->ipv4_address);
+  /*
+   * SR WARNING: ipv6 multi-homing fails sometimes for localhost.
+   * * * * Disable it for now.
+   */
+  sctp_init->nb_ipv6_addr = 0;
+  sctp_init->ipv6_address[0] = "0:0:0:0:0:0:0:1";
+
+  return itti_send_msg_to_task (TASK_SCTP, instance_p->instance, message);
+
+}
+
+static void x2ap_eNB_register_eNB(x2ap_eNB_instance_t *instance_p,
+                                  net_ip_address_t    *target_eNB_ip_address,
+                                  net_ip_address_t    *local_ip_addr,
+                                  uint16_t             in_streams,
+                                  uint16_t             out_streams,
+				  uint32_t	       enb_port_for_X2C)
+{
+
+  MessageDef                 *message                   = NULL;
+  sctp_new_association_req_t *sctp_new_association_req  = NULL;
+  x2ap_eNB_data_t            *x2ap_enb_data             = NULL;
+
+  DevAssert(instance_p != NULL);
+  DevAssert(target_eNB_ip_address != NULL);
+
+  message = itti_alloc_new_message(TASK_X2AP, SCTP_NEW_ASSOCIATION_REQ_MULTI);
+
+  sctp_new_association_req = &message->ittiMsg.sctp_new_association_req_multi;
+
+  sctp_new_association_req->port = enb_port_for_X2C;
+  sctp_new_association_req->ppid = X2AP_SCTP_PPID;
+
+  sctp_new_association_req->in_streams  = in_streams;
+  sctp_new_association_req->out_streams = out_streams;
+
+  memcpy(&sctp_new_association_req->remote_address,
+         target_eNB_ip_address,
+         sizeof(*target_eNB_ip_address));
+
+  memcpy(&sctp_new_association_req->local_address,
+         local_ip_addr,
+         sizeof(*local_ip_addr));
+
+  /* Create new eNB descriptor */
+  x2ap_enb_data = calloc(1, sizeof(*x2ap_enb_data));
+  DevAssert(x2ap_enb_data != NULL);
+
+  x2ap_enb_data->cnx_id                = x2ap_eNB_fetch_add_global_cnx_id();
+  sctp_new_association_req->ulp_cnx_id = x2ap_enb_data->cnx_id;
+
+  x2ap_enb_data->assoc_id          = -1;
+  x2ap_enb_data->x2ap_eNB_instance = instance_p;
+
+  /* Insert the new descriptor in list of known eNB
+   * but not yet associated.
+   */
+  RB_INSERT(x2ap_enb_map, &instance_p->x2ap_enb_head, x2ap_enb_data);
+  x2ap_enb_data->state = X2AP_ENB_STATE_WAITING;
+  instance_p->x2_target_enb_nb ++;
+  instance_p->x2_target_enb_pending_nb ++;
+
+  itti_send_msg_to_task(TASK_SCTP, instance_p->instance, message);
+}
+
+static
+void x2ap_eNB_handle_register_eNB(instance_t instance,
+				  x2ap_register_enb_req_t *x2ap_register_eNB)
+{
+
+  x2ap_eNB_instance_t *new_instance;
+  uint8_t index;
+
+  DevAssert(x2ap_register_eNB != NULL);
+
+  /* Look if the provided instance already exists */
+  new_instance = x2ap_eNB_get_instance(instance);
+
+  if (new_instance != NULL) {
+    /* Checks if it is a retry on the same eNB */
+    DevCheck(new_instance->eNB_id == x2ap_register_eNB->eNB_id, new_instance->eNB_id, x2ap_register_eNB->eNB_id, 0);
+    DevCheck(new_instance->cell_type == x2ap_register_eNB->cell_type, new_instance->cell_type, x2ap_register_eNB->cell_type, 0);
+    DevCheck(new_instance->tac == x2ap_register_eNB->tac, new_instance->tac, x2ap_register_eNB->tac, 0);
+    DevCheck(new_instance->mcc == x2ap_register_eNB->mcc, new_instance->mcc, x2ap_register_eNB->mcc, 0);
+    DevCheck(new_instance->mnc == x2ap_register_eNB->mnc, new_instance->mnc, x2ap_register_eNB->mnc, 0);
+
+  }
+  else {
+    new_instance = calloc(1, sizeof(x2ap_eNB_instance_t));
+    DevAssert(new_instance != NULL);
+
+    RB_INIT(&new_instance->x2ap_enb_head);
+
+    /* Copy usefull parameters */
+    new_instance->instance         = instance;
+    new_instance->eNB_name         = x2ap_register_eNB->eNB_name;
+    new_instance->eNB_id           = x2ap_register_eNB->eNB_id;
+    new_instance->cell_type        = x2ap_register_eNB->cell_type;
+    new_instance->tac              = x2ap_register_eNB->tac;
+    new_instance->mcc              = x2ap_register_eNB->mcc;
+    new_instance->mnc              = x2ap_register_eNB->mnc;
+    new_instance->mnc_digit_length = x2ap_register_eNB->mnc_digit_length;
+
+    /* Add the new instance to the list of eNB (meaningfull in virtual mode) */
+    x2ap_eNB_insert_new_instance(new_instance);
+
+    X2AP_INFO("Registered new eNB[%d] and %s eNB id %u\n",
+               instance,
+               x2ap_register_eNB->cell_type == CELL_MACRO_ENB ? "macro" : "home",
+               x2ap_register_eNB->eNB_id);
+    /* initiate the SCTP listener */
+    if (x2ap_eNB_init_sctp(new_instance,&x2ap_register_eNB->enb_x2_ip_address,x2ap_register_eNB->enb_port_for_X2C) <  0 ) {
+          X2AP_ERROR ("Error while sending SCTP_INIT_MSG to SCTP \n");
+          return;
+    }
+      X2AP_INFO("eNB[%d] eNB id %u acting as a listner (server)\n",
+                instance, x2ap_register_eNB->eNB_id);
+  }
+
+  DevCheck(x2ap_register_eNB->nb_x2 <= X2AP_MAX_NB_ENB_IP_ADDRESS,
+           X2AP_MAX_NB_ENB_IP_ADDRESS, x2ap_register_eNB->nb_x2, 0);
+
+  /* Trying to connect to the provided list of eNB ip address */
+
+  for (index = 0; index < x2ap_register_eNB->nb_x2; index++) {
+
+      X2AP_INFO("eNB[%d] eNB id %u acting as an initiator (client)\n",
+		instance, x2ap_register_eNB->eNB_id);
+      x2ap_eNB_register_eNB(new_instance,
+			    &x2ap_register_eNB->target_enb_x2_ip_address[index],
+			    &x2ap_register_eNB->enb_x2_ip_address,
+			    x2ap_register_eNB->sctp_in_streams,
+			    x2ap_register_eNB->sctp_out_streams,
+                            x2ap_register_eNB->enb_port_for_X2C);
+  }
+
+}
+
+void *x2ap_task(void *arg)
+{
+  MessageDef *received_msg = NULL;
+  int         result;
+
+  X2AP_DEBUG("Starting X2AP layer\n");
+
+  x2ap_eNB_prepare_internal_data();
+
+  itti_mark_task_ready(TASK_X2AP);
+
+  while (1) {
+    itti_receive_msg(TASK_X2AP, &received_msg);
+    switch (ITTI_MSG_ID(received_msg)) {
+    case TERMINATE_MESSAGE:
+      X2AP_WARN(" *** Exiting X2AP thread\n");
+      itti_exit_task();
+      break;
+
+    case X2AP_REGISTER_ENB_REQ:
+      x2ap_eNB_handle_register_eNB(ITTI_MESSAGE_GET_INSTANCE(received_msg),
+                                   &X2AP_REGISTER_ENB_REQ(received_msg));
+      break;
+
+    case SCTP_NEW_ASSOCIATION_RESP:
+      x2ap_eNB_handle_sctp_association_resp(ITTI_MESSAGE_GET_INSTANCE(received_msg),
+					    &received_msg->ittiMsg.sctp_new_association_resp);
+      break;
+
+    case SCTP_NEW_ASSOCIATION_IND:
+      x2ap_eNB_handle_sctp_association_ind(ITTI_MESSAGE_GET_INSTANCE(received_msg),
+					   &received_msg->ittiMsg.sctp_new_association_ind);
+      break;
+
+    case SCTP_DATA_IND:
+      //x2ap_eNB_handle_sctp_data_ind(ITTI_MESSAGE_GET_INSTANCE(received_msg),
+	//			    &received_msg->ittiMsg.sctp_data_ind);
+      break;
+
+    default:
+      X2AP_ERROR("Received unhandled message: %d:%s\n",
+                 ITTI_MSG_ID(received_msg), ITTI_MSG_NAME(received_msg));
+      break;
+    }
+
+    result = itti_free (ITTI_MSG_ORIGIN_ID(received_msg), received_msg);
+    AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
+
+    received_msg = NULL;
+  }
+
+  return NULL;
+}
+
+
diff --git a/openair2/X2AP/x2ap.h b/openair2/X2AP/x2ap_eNB.h
similarity index 82%
rename from openair2/X2AP/x2ap.h
rename to openair2/X2AP/x2ap_eNB.h
index 89e8540b97..1a19416122 100644
--- a/openair2/X2AP/x2ap.h
+++ b/openair2/X2AP/x2ap_eNB.h
@@ -30,10 +30,12 @@
 #ifndef X2AP_H_
 #define X2AP_H_
 
-typedef struct x2ap_config_s {
-} x2ap_config_t;
+#define X2AP_SCTP_PPID   (27)    ///< X2AP SCTP Payload Protocol Identifier (PPID)
+#include "x2ap_eNB_defs.h"
 
-extern x2ap_config_t x2ap_config;
+int x2ap_eNB_init_sctp (x2ap_eNB_instance_t *instance_p,
+                        net_ip_address_t    *local_ip_addr,
+                        uint32_t enb_port_for_X2C);
 
 void *x2ap_task(void *arg);
 
diff --git a/openair2/X2AP/x2ap_eNB_defs.h b/openair2/X2AP/x2ap_eNB_defs.h
new file mode 100644
index 0000000000..7869b497c3
--- /dev/null
+++ b/openair2/X2AP/x2ap_eNB_defs.h
@@ -0,0 +1,178 @@
+/*
+ * 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 <stdint.h>
+
+#include "queue.h"
+#include "tree.h"
+
+#include "sctp_eNB_defs.h"
+
+#ifndef X2AP_ENB_DEFS_H_
+#define X2AP_ENB_DEFS_H_
+
+#define X2AP_ENB_NAME_LENGTH_MAX    (150)
+
+typedef enum {
+  /* Disconnected state: initial state for any association. */
+  X2AP_ENB_STATE_DISCONNECTED = 0x0,
+  
+  /* State waiting for x2 Setup response message if the target eNB accepts or
+   * X2 Setup failure if rejects the eNB.
+   */
+  X2AP_ENB_STATE_WAITING     = 0x1,
+  
+  /* The eNB is successfully connected to another eNB. */
+  X2AP_ENB_STATE_CONNECTED   = 0x2,
+  
+  /* X2AP is ready, and the eNB is successfully connected to another eNB. */
+  X2AP_ENB_STATE_READY             = 0x3,
+
+  X2AP_ENB_STATE_OVERLOAD          = 0x4,
+
+  X2AP_ENB_STATE_RESETTING         = 0x5,
+  
+  /* Max number of states available */
+  X2AP_ENB_STATE_MAX,
+} x2ap_eNB_state_t;
+
+
+/* Served PLMN identity element */
+struct plmn_identity_s {
+  uint16_t mcc;
+  uint16_t mnc;
+  uint8_t  mnc_digit_length;
+  STAILQ_ENTRY(plmn_identity_s) next;
+};
+
+/* Served group id element */
+struct served_group_id_s {
+  uint16_t enb_group_id;
+  STAILQ_ENTRY(served_group_id_s) next;
+};
+
+/* Served enn code for a particular eNB */
+struct enb_code_s {
+  uint8_t enb_code;
+  STAILQ_ENTRY(enb_code_s) next;
+};
+
+struct x2ap_eNB_instance_s;
+
+/* This structure describes association of a eNB to another eNB */
+typedef struct x2ap_eNB_data_s {
+  /* eNB descriptors tree, ordered by sctp assoc id */
+  RB_ENTRY(x2ap_eNB_data_s) entry;
+
+  /* This is the optional name provided by the MME */
+  char *eNB_name;
+  
+  /*  target eNB ID */ 
+  uint32_t eNB_id;
+  
+  /* Current eNB load information (if any). */
+  //x2ap_load_state_t overload_state;
+  
+  /* Current eNB->eNB X2AP association state */
+  x2ap_eNB_state_t state;
+
+  /* Next usable stream for UE signalling */
+  int32_t nextstream;
+
+  /* Number of input/ouput streams */
+  uint16_t in_streams;
+  uint16_t out_streams;
+
+  /* Connexion id used between SCTP/X2AP */
+  uint16_t cnx_id;
+
+  /* SCTP association id */
+  int32_t  assoc_id;
+
+  /* Only meaningfull in virtual mode */
+  struct x2ap_eNB_instance_s *x2ap_eNB_instance;
+} x2ap_eNB_data_t;
+
+typedef struct x2ap_eNB_instance_s {
+  /* used in simulation to store multiple eNB instances*/
+  STAILQ_ENTRY(x2ap_eNB_instance_s) x2ap_eNB_entries;
+
+  /* Number of target eNBs requested by eNB (tree size) */
+  uint32_t x2_target_enb_nb;
+  /* Number of target eNBs for which association is pending */
+  uint32_t x2_target_enb_pending_nb;
+  /* Number of target eNB successfully associated to eNB */
+  uint32_t x2_target_enb_associated_nb;
+  /* Tree of X2AP eNB associations ordered by association ID */
+  RB_HEAD(x2ap_enb_map, x2ap_eNB_data_s) x2ap_enb_head;
+
+  /* Tree of UE ordered by eNB_ue_x2ap_id's */
+  //  RB_HEAD(x2ap_ue_map, x2ap_eNB_ue_context_s) x2ap_ue_head;
+
+  /* For virtual mode, mod_id as defined in the rest of the L1/L2 stack */
+  instance_t instance;
+
+  /* Displayable name of eNB */
+  char *eNB_name;
+
+  /* Unique eNB_id to identify the eNB within EPC.
+   * In our case the eNB is a macro eNB so the id will be 20 bits long.
+   * For Home eNB id, this field should be 28 bits long.
+   */
+  uint32_t eNB_id;
+  /* The type of the cell */
+  cell_type_t cell_type;
+
+  /* Tracking area code */
+  uint16_t tac;
+
+  /* Mobile Country Code
+   * Mobile Network Code
+   */
+  uint16_t  mcc;
+  uint16_t  mnc;
+  uint8_t   mnc_digit_length;
+
+ 
+} x2ap_eNB_instance_t;
+
+typedef struct {
+  /* List of served eNBs
+   * Only used for virtual mode
+   */
+  STAILQ_HEAD(x2ap_eNB_instances_head_s, x2ap_eNB_instance_s) x2ap_eNB_instances_head;
+  /* Nb of registered eNBs */
+  uint8_t nb_registered_eNBs;
+
+  /* Generate a unique connexion id used between X2AP and SCTP */
+  uint16_t global_cnx_id;
+} x2ap_eNB_internal_data_t;
+
+int x2ap_eNB_compare_assoc_id(struct x2ap_eNB_data_s *p1, struct x2ap_eNB_data_s *p2);
+
+/* Generate the tree management functions */
+struct x2ap_eNB_map;
+struct x2ap_eNB_data_s;
+RB_PROTOTYPE(x2ap_eNB_map, x2ap_eNB_data_s, entry, x2ap_eNB_compare_assoc_id);
+
+
+#endif /* X2AP_ENB_DEFS_H_ */
+
diff --git a/openair2/X2AP/x2ap_eNB_handler.c b/openair2/X2AP/x2ap_eNB_handler.c
new file mode 100644
index 0000000000..180535baa6
--- /dev/null
+++ b/openair2/X2AP/x2ap_eNB_handler.c
@@ -0,0 +1,79 @@
+/*
+ * 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 <stdint.h>
+
+#include "intertask_interface.h"
+
+#include "asn1_conversions.h"
+
+#include "x2ap_common.h"
+#include "x2ap_eNB_defs.h"
+#include "x2ap_eNB_handler.h"
+
+#include "x2ap_eNB_management_procedures.h"
+
+#include "assertions.h"
+#include "conversions.h"
+
+void x2ap_handle_x2_setup_message(x2ap_eNB_data_t *enb_desc_p, int sctp_shutdown)
+{
+  if (sctp_shutdown) {
+    /* A previously connected eNB has been shutdown */
+
+    /* TODO check if it was used by some eNB and send a message to inform these eNB if there is no more associated eNB */
+    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) {
+        /* Decrease associated eNB number */
+        enb_desc_p->x2ap_eNB_instance-> 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) {
+        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);
+      }
+    }
+  } 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);
+
+    if (enb_desc_p->x2ap_eNB_instance->x2_target_enb_pending_nb > 0) {
+      /* Decrease pending messages number */
+      enb_desc_p->x2ap_eNB_instance->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) {
+      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);
+    }
+  }
+}
diff --git a/openair2/X2AP/x2ap_eNB_handler.h b/openair2/X2AP/x2ap_eNB_handler.h
new file mode 100644
index 0000000000..6d65c25a0a
--- /dev/null
+++ b/openair2/X2AP/x2ap_eNB_handler.h
@@ -0,0 +1,33 @@
+/*
+ * 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_ENB_HANDLERS_H_
+#define X2AP_ENB_HANDLERS_H_
+
+#include "x2ap_eNB_defs.h"
+
+void x2ap_handle_x2_setup_message(x2ap_eNB_data_t *mme_desc_p, int sctp_shutdown);
+
+//int x2ap_eNB_handle_message(uint32_t assoc_id, int32_t stream,
+  //                          const uint8_t * const data, const uint32_t data_length);
+
+#endif /* X2AP_ENB_HANDLERS_H_ */
+
diff --git a/openair2/X2AP/x2ap_eNB_management_procedures.c b/openair2/X2AP/x2ap_eNB_management_procedures.c
new file mode 100644
index 0000000000..c2b128ffd3
--- /dev/null
+++ b/openair2/X2AP/x2ap_eNB_management_procedures.c
@@ -0,0 +1,236 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "intertask_interface.h"
+
+#include "assertions.h"
+#include "conversions.h"
+
+#include "x2ap_common.h"
+#include "x2ap_eNB_defs.h"
+#include "x2ap_eNB.h"
+
+
+#define X2AP_DEBUG_LIST
+#ifdef X2AP_DEBUG_LIST
+#  define X2AP_eNB_LIST_OUT(x, args...) X2AP_DEBUG("[eNB]%*s"x"\n", 4*indent, "", ##args)
+#else
+#  define X2AP_eNB_LIST_OUT(x, args...)
+#endif 
+
+static int                  indent = 0;
+
+
+x2ap_eNB_internal_data_t x2ap_eNB_internal_data;
+
+RB_GENERATE(x2ap_enb_map, x2ap_eNB_data_s, entry, x2ap_eNB_compare_assoc_id);
+
+int x2ap_eNB_compare_assoc_id(
+  struct x2ap_eNB_data_s *p1, struct x2ap_eNB_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 x2ap_eNB_fetch_add_global_cnx_id(void)
+{
+  return ++x2ap_eNB_internal_data.global_cnx_id;
+}
+
+void x2ap_eNB_prepare_internal_data(void)
+{
+  memset(&x2ap_eNB_internal_data, 0, sizeof(x2ap_eNB_internal_data));
+  STAILQ_INIT(&x2ap_eNB_internal_data.x2ap_eNB_instances_head);
+}
+
+void x2ap_eNB_insert_new_instance(x2ap_eNB_instance_t *new_instance_p)
+{
+  DevAssert(new_instance_p != NULL);
+
+  STAILQ_INSERT_TAIL(&x2ap_eNB_internal_data.x2ap_eNB_instances_head,
+                     new_instance_p, x2ap_eNB_entries);
+}
+
+void dump_tree(x2ap_eNB_data_t *t)
+{
+  if (t == NULL) return;
+  printf("-----------------------\n");
+  printf("eNB id %d %s\n", t->eNB_id, t->eNB_name);
+  printf("state %d\n", t->state);
+  printf("nextstream %d\n", t->nextstream);
+  printf("in_streams %d out_streams %d\n", t->in_streams, t->out_streams);
+  printf("cnx_id %d assoc_id %d\n", t->cnx_id, t->assoc_id);
+  dump_tree(t->entry.rbe_left);
+  dump_tree(t->entry.rbe_right);
+}
+
+void dump_trees(void)
+{
+x2ap_eNB_instance_t *zz;
+STAILQ_FOREACH(zz, &x2ap_eNB_internal_data.x2ap_eNB_instances_head,
+               x2ap_eNB_entries) {
+printf("here comes the tree (instance %d):\n---------------------------------------------\n", zz->instance);
+dump_tree(zz->x2ap_enb_head.rbh_root);
+printf("---------------------------------------------\n");
+}
+}
+
+struct x2ap_eNB_data_s *x2ap_get_eNB(x2ap_eNB_instance_t *instance_p,
+				     int32_t assoc_id, 
+				     uint16_t cnx_id)
+{
+  struct x2ap_eNB_data_s  temp;
+  struct x2ap_eNB_data_s *found;
+
+printf("x2ap_get_eNB at 1 (looking for assoc_id %d cnx_id %d)\n", assoc_id, cnx_id);
+dump_trees();
+
+  memset(&temp, 0, sizeof(struct x2ap_eNB_data_s));
+
+  temp.assoc_id = assoc_id;
+  temp.cnx_id   = cnx_id;
+
+  if (instance_p == NULL) {
+    STAILQ_FOREACH(instance_p, &x2ap_eNB_internal_data.x2ap_eNB_instances_head,
+                   x2ap_eNB_entries) {
+      found = RB_FIND(x2ap_enb_map, &instance_p->x2ap_enb_head, &temp);
+
+      if (found != NULL) {
+        return found;
+      }
+    }
+  } else {
+    return RB_FIND(x2ap_enb_map, &instance_p->x2ap_enb_head, &temp);
+  }
+
+  return NULL;
+}
+
+
+x2ap_eNB_instance_t *x2ap_eNB_get_instance(instance_t instance)
+{
+  x2ap_eNB_instance_t *temp = NULL;
+
+  STAILQ_FOREACH(temp, &x2ap_eNB_internal_data.x2ap_eNB_instances_head,
+                 x2ap_eNB_entries) {
+    if (temp->instance == instance) {
+      /* Matching occurence */
+      return temp;
+    }
+  }
+
+  return NULL;
+}
+
+
+/// utility functions 
+
+void x2ap_dump_eNB (x2ap_eNB_data_t  * eNB_ref);
+
+void
+x2ap_dump_eNB_list (void) {
+   x2ap_eNB_instance_t *inst = NULL;
+   struct x2ap_eNB_data_s *found = NULL;
+   struct x2ap_eNB_data_s temp;
+  
+   memset(&temp, 0, sizeof(struct x2ap_eNB_data_s));
+   
+  STAILQ_FOREACH (inst, &x2ap_eNB_internal_data.x2ap_eNB_instances_head,  x2ap_eNB_entries) {
+    found = RB_FIND(x2ap_enb_map, &inst->x2ap_enb_head, &temp);
+    x2ap_dump_eNB (found);
+  }
+}
+
+void x2ap_dump_eNB (x2ap_eNB_data_t  * eNB_ref) {
+
+  if (eNB_ref == NULL) {
+    return;
+  }
+  
+  X2AP_eNB_LIST_OUT ("");
+  X2AP_eNB_LIST_OUT ("eNB name:          %s", eNB_ref->eNB_name == NULL ? "not present" : eNB_ref->eNB_name);
+  X2AP_eNB_LIST_OUT ("eNB STATE:         %07x", eNB_ref->state);
+  X2AP_eNB_LIST_OUT ("eNB ID:            %07x", eNB_ref->eNB_id);
+  indent++; 
+  X2AP_eNB_LIST_OUT ("SCTP cnx id:     %d", eNB_ref->cnx_id);
+  X2AP_eNB_LIST_OUT ("SCTP assoc id:     %d", eNB_ref->assoc_id);
+  X2AP_eNB_LIST_OUT ("SCTP instreams:    %d", eNB_ref->in_streams);
+  X2AP_eNB_LIST_OUT ("SCTP outstreams:   %d", eNB_ref->out_streams);
+  indent--;
+}
+
+
+x2ap_eNB_data_t  * x2ap_is_eNB_id_in_list (const uint32_t eNB_id)
+{
+  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) {
+      if (elm->eNB_id == eNB_id)
+        return elm;
+    }
+  }
+  return NULL;
+}
+
+x2ap_eNB_data_t  * x2ap_is_eNB_assoc_id_in_list (const uint32_t sctp_assoc_id)
+{
+  x2ap_eNB_instance_t    *inst;
+  struct x2ap_eNB_data_s *found;
+  struct x2ap_eNB_data_s temp;
+
+  temp.assoc_id = sctp_assoc_id;
+  temp.cnx_id = -1;
+
+  STAILQ_FOREACH(inst, &x2ap_eNB_internal_data.x2ap_eNB_instances_head, x2ap_eNB_entries) {
+    found = RB_FIND(x2ap_enb_map, &inst->x2ap_enb_head, &temp);
+    if (found != NULL){
+      if (found->assoc_id == sctp_assoc_id) {
+	return found;
+      }
+    }
+  }
+  return NULL;
+}
+
diff --git a/openair2/X2AP/x2ap.c b/openair2/X2AP/x2ap_eNB_management_procedures.h
similarity index 52%
rename from openair2/X2AP/x2ap.c
rename to openair2/X2AP/x2ap_eNB_management_procedures.h
index 8002867e15..b66a6d1035 100644
--- a/openair2/X2AP/x2ap.c
+++ b/openair2/X2AP/x2ap_eNB_management_procedures.h
@@ -19,53 +19,28 @@
  *      contact@openairinterface.org
  */
 
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
+#ifndef X2AP_ENB_MANAGEMENT_PROCEDURES_H_
+#define X2AP_ENB_MANAGEMENT_PROCEDURES_H
 
+void x2ap_eNB_prepare_internal_data(void);
 
-#include "intertask_interface.h"
+void dump_trees(void);
 
-#include "x2ap.h"
+void x2ap_eNB_insert_new_instance(x2ap_eNB_instance_t *new_instance_p);
 
-#include "assertions.h"
-#include "conversions.h"
+x2ap_eNB_instance_t *x2ap_eNB_get_instance(uint8_t mod_id);
 
+uint16_t x2ap_eNB_fetch_add_global_cnx_id(void);
 
-void *x2ap_task(void *arg)
-{
-  MessageDef *received_msg = NULL;
-  int         result;
+void x2ap_eNB_prepare_internal_data(void);
 
-  X2AP_DEBUG("Starting X2AP layer\n");
+x2ap_eNB_data_t* x2ap_is_eNB_id_in_list(uint32_t eNB_id);
 
-  x2ap_prepare_internal_data();
+x2ap_eNB_data_t* x2ap_is_eNB_assoc_id_in_list(uint32_t sctp_assoc_id);
 
-  itti_mark_task_ready(TASK_X2AP);
-
-  while (1) {
-    itti_receive_msg(TASK_X2AP, &received_msg);
-
-    switch (ITTI_MSG_ID(received_msg)) {
-    case TERMINATE_MESSAGE:
-      X2AP_WARN(" *** Exiting X2AP thread\n");
-      itti_exit_task();
-      break;
-
-    default:
-      X2AP_ERROR("Received unhandled message: %d:%s\n",
-                 ITTI_MSG_ID(received_msg), ITTI_MSG_NAME(received_msg));
-      break;
-    }
-
-    result = itti_free (ITTI_MSG_ORIGIN_ID(received_msg), received_msg);
-    AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
-
-    received_msg = NULL;
-  }
-
-  return NULL;
-}
+struct x2ap_eNB_data_s *x2ap_get_eNB(x2ap_eNB_instance_t *instance_p,
+                                     int32_t assoc_id,
+                                     uint16_t cnx_id);
 
+#endif /* X2AP_ENB_MANAGEMENT_PROCEDURES_H_ */
 
diff --git a/openair3/SCTP/sctp_common.c b/openair3/SCTP/sctp_common.c
index c467290b36..55a2943924 100644
--- a/openair3/SCTP/sctp_common.c
+++ b/openair3/SCTP/sctp_common.c
@@ -86,6 +86,12 @@ int sctp_get_sockinfo(int sock, uint16_t *instream, uint16_t *outstream,
   memset(&status, 0, sizeof(struct sctp_status));
   i = sizeof(struct sctp_status);
 
+  /* if sock refers to a multi SCTP endpoint, *assoc_id gives us
+   * the association ID that we want
+   */
+  if (assoc_id != NULL)
+    status.sstat_assoc_id = *assoc_id;
+
   if (getsockopt(sock, IPPROTO_SCTP, SCTP_STATUS, &status, &i) < 0) {
     SCTP_ERROR("Getsockopt SCTP_STATUS failed: %s\n", strerror(errno));
     return -1;
diff --git a/openair3/SCTP/sctp_eNB_task.c b/openair3/SCTP/sctp_eNB_task.c
index 168eb58b37..998ea9589c 100644
--- a/openair3/SCTP/sctp_eNB_task.c
+++ b/openair3/SCTP/sctp_eNB_task.c
@@ -61,6 +61,7 @@
 enum sctp_connection_type_e {
   SCTP_TYPE_CLIENT,
   SCTP_TYPE_SERVER,
+  SCTP_TYPE_MULTI_SERVER,
   SCTP_TYPE_MAX
 };
 
@@ -91,7 +92,6 @@ typedef struct sctp_cnx_list_elm_s {
 static STAILQ_HEAD(sctp_cnx_list_head, sctp_cnx_list_elm_s) sctp_cnx_list;
 static uint16_t sctp_nb_cnx = 0;
 
-
 //------------------------------------------------------------------------------
 struct sctp_cnx_list_elm_s *sctp_get_cnx(int32_t assoc_id, int sd)
 {
@@ -112,6 +112,238 @@ struct sctp_cnx_list_elm_s *sctp_get_cnx(int32_t assoc_id, int sd)
   return NULL;
 }
 
+//------------------------------------------------------------------------------
+static inline
+void
+sctp_eNB_accept_associations_multi(
+  struct sctp_cnx_list_elm_s *sctp_cnx)
+{
+  int                    ns;
+  int                    n;
+  int                    flags = 0;
+  socklen_t              from_len;
+  struct sctp_sndrcvinfo sinfo;
+
+  struct sockaddr_in addr;
+  uint8_t buffer[SCTP_RECV_BUFFER_SIZE];
+
+  DevAssert(sctp_cnx != NULL);
+
+  memset((void *)&addr, 0, sizeof(struct sockaddr_in));
+  from_len = (socklen_t)sizeof(struct sockaddr_in);
+  memset((void *)&sinfo, 0, sizeof(struct sctp_sndrcvinfo));
+
+  n = sctp_recvmsg(sctp_cnx->sd, (void *)buffer, SCTP_RECV_BUFFER_SIZE,
+                   (struct sockaddr *)&addr, &from_len,
+                   &sinfo, &flags);
+
+  if (n < 0) {
+    if (errno == ENOTCONN) {
+      SCTP_DEBUG("Received not connected for sd %d\n", sctp_cnx->sd);
+      close(sctp_cnx->sd);
+    } else {
+      SCTP_DEBUG("An error occured during read\n");
+      SCTP_ERROR("sctp_recvmsg (fd %d, len %d ): %s:%d\n", sctp_cnx->sd, n, strerror(errno), errno);
+    }
+    return;
+  }
+
+  if (flags & MSG_NOTIFICATION) {
+    union sctp_notification *snp;
+    snp = (union sctp_notification *)buffer;
+
+    SCTP_DEBUG("Received notification for sd %d, type %u\n",
+               sctp_cnx->sd, snp->sn_header.sn_type);
+
+    /* Association has changed. */
+    if (SCTP_ASSOC_CHANGE == snp->sn_header.sn_type) {
+      struct sctp_assoc_change *sctp_assoc_changed;
+      sctp_assoc_changed = &snp->sn_assoc_change;
+
+      SCTP_DEBUG("Client association changed: %d\n", sctp_assoc_changed->sac_state);
+
+      /* New physical association requested by a peer */
+      switch (sctp_assoc_changed->sac_state) {
+      case SCTP_COMM_UP: {
+        SCTP_DEBUG("Comm up notified for sd %d, assigned assoc_id %d\n",
+                   sctp_cnx->sd, sctp_assoc_changed->sac_assoc_id);
+          struct sctp_cnx_list_elm_s *new_cnx;
+
+          new_cnx = calloc(1, sizeof(*sctp_cnx));
+
+	  DevAssert(new_cnx != NULL);
+
+	  new_cnx->connection_type = SCTP_TYPE_CLIENT;
+
+          ns = sctp_peeloff(sctp_cnx->sd, sctp_assoc_changed->sac_assoc_id);
+
+	  new_cnx->sd         = ns;
+	  new_cnx->task_id    = sctp_cnx->task_id;
+          new_cnx->cnx_id     = 0;
+	  new_cnx->ppid       = sctp_cnx->ppid;
+	  new_cnx->instance   = sctp_cnx->instance;
+	  new_cnx->local_port = sctp_cnx->local_port;
+          new_cnx->assoc_id   = sctp_assoc_changed->sac_assoc_id;
+
+          if (sctp_get_sockinfo(ns, &new_cnx->in_streams, &new_cnx->out_streams,
+                                &new_cnx->assoc_id) != 0) {
+            SCTP_ERROR("sctp_get_sockinfo failed\n");
+            close(ns);
+            free(new_cnx);
+            return;
+          }
+
+	  /* Insert new element at end of list */
+	  STAILQ_INSERT_TAIL(&sctp_cnx_list, new_cnx, entries);
+          sctp_nb_cnx++;
+
+	  /* Add the socket to list of fd monitored by ITTI */
+	  itti_subscribe_event_fd(TASK_SCTP, ns);
+
+	  sctp_itti_send_association_ind(new_cnx->task_id, new_cnx->instance,
+                                          new_cnx->assoc_id, new_cnx->local_port,
+                                          new_cnx->out_streams, new_cnx->in_streams);
+      }
+      break;
+
+      default:
+        break;
+      }
+    }
+  } else {
+       SCTP_DEBUG("No notification from SCTP\n");
+  }
+}
+
+//------------------------------------------------------------------------------
+void
+sctp_handle_new_association_req_multi(
+  const instance_t instance,
+  const task_id_t requestor,
+  const sctp_new_association_req_t * const sctp_new_association_req_p,
+  int   sd)
+{
+  int                           ns;
+
+  int32_t                       assoc_id = 0;
+
+  struct sctp_cnx_list_elm_s   *sctp_cnx = NULL;
+  enum sctp_connection_type_e   connection_type = SCTP_TYPE_CLIENT;
+
+  /* Prepare a new SCTP association as requested by upper layer and try to connect
+   * to remote host.
+   */
+  DevAssert(sctp_new_association_req_p != NULL);
+
+  /* Create new socket with IPv6 affinity */
+//#warning "SCTP may Force IPv4 only, here"
+
+  /* Mark the socket as non-blocking */
+  //if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0) {
+    //SCTP_ERROR("fcntl F_SETFL O_NONBLOCK failed: %s\n",
+      //         strerror(errno));
+    //close(sd);
+    //return;
+  //}
+
+  /* SOCK_STREAM socket type requires an explicit connect to the remote host
+   * address and port.
+   * Only use IPv4 for the first connection attempt
+   */
+  if ((sctp_new_association_req_p->remote_address.ipv6 != 0) ||
+      (sctp_new_association_req_p->remote_address.ipv4 != 0)) {
+    uint8_t address_index = 0;
+    uint8_t used_address  = sctp_new_association_req_p->remote_address.ipv6 +
+                            sctp_new_association_req_p->remote_address.ipv4;
+    struct sockaddr_in addr[used_address];
+
+    memset(addr, 0, used_address * sizeof(struct sockaddr_in));
+
+    if (sctp_new_association_req_p->remote_address.ipv6 == 1) {
+      if (inet_pton(AF_INET6, sctp_new_association_req_p->remote_address.ipv6_address,
+                    &addr[address_index].sin_addr.s_addr) != 1) {
+        SCTP_ERROR("Failed to convert ipv6 address %*s to network type\n",
+                   (int)strlen(sctp_new_association_req_p->remote_address.ipv6_address),
+                   sctp_new_association_req_p->remote_address.ipv6_address);
+        close(sd);
+        return;
+      }
+
+      SCTP_DEBUG("Converted ipv6 address %*s to network type\n",
+                 (int)strlen(sctp_new_association_req_p->remote_address.ipv6_address),
+                 sctp_new_association_req_p->remote_address.ipv6_address);
+
+      addr[address_index].sin_family = AF_INET6;
+      addr[address_index].sin_port   = htons(sctp_new_association_req_p->port);
+      address_index++;
+    }
+
+    if (sctp_new_association_req_p->remote_address.ipv4 == 1) {
+      if (inet_pton(AF_INET, sctp_new_association_req_p->remote_address.ipv4_address,
+                    &addr[address_index].sin_addr.s_addr) != 1) {
+        SCTP_ERROR("Failed to convert ipv4 address %*s to network type\n",
+                   (int)strlen(sctp_new_association_req_p->remote_address.ipv4_address),
+                   sctp_new_association_req_p->remote_address.ipv4_address);
+        close(sd);
+        return;
+      }
+
+      SCTP_DEBUG("Converted ipv4 address %*s to network type\n",
+                 (int)strlen(sctp_new_association_req_p->remote_address.ipv4_address),
+                 sctp_new_association_req_p->remote_address.ipv4_address);
+
+      addr[address_index].sin_family = AF_INET;
+      addr[address_index].sin_port   = htons(sctp_new_association_req_p->port);
+      address_index++;
+    }
+
+    /* Connect to remote host and port */
+    if (sctp_connectx(sd, (struct sockaddr *)addr, 1, &assoc_id) < 0) {
+      /* sctp_connectx on non-blocking socket return EINPROGRESS */
+      if (errno != EINPROGRESS) {
+        SCTP_ERROR("Connect failed: %s\n", strerror(errno));
+        sctp_itti_send_association_resp(
+          requestor, instance, -1, sctp_new_association_req_p->ulp_cnx_id,
+          SCTP_STATE_UNREACHABLE, 0, 0);
+        /* Add the socket to list of fd monitored by ITTI */
+        //itti_unsubscribe_event_fd(TASK_SCTP, sd);
+        close(sd);
+        return;
+      } else {
+        SCTP_DEBUG("connectx assoc_id  %d in progress..., used %d addresses\n",
+                   assoc_id, used_address);
+      }
+    } else {
+      SCTP_DEBUG("sctp_connectx SUCCESS, used %d addresses assoc_id %d\n",
+                 used_address,
+                 assoc_id);
+    }
+  }
+
+  ns = sctp_peeloff(sd,assoc_id);
+
+  sctp_cnx = calloc(1, sizeof(*sctp_cnx));
+
+  sctp_cnx->connection_type = connection_type;
+
+  sctp_cnx->sd       = ns;
+  sctp_cnx->task_id  = requestor;
+  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;
+
+  /* Add the socket to list of fd monitored by ITTI */
+  itti_subscribe_event_fd(TASK_SCTP, ns);
+
+  /* Insert new element at end of list */
+  STAILQ_INSERT_TAIL(&sctp_cnx_list, sctp_cnx, entries);
+  sctp_nb_cnx++;
+
+  SCTP_DEBUG("Inserted new descriptor for sd %d in list, nb elements %u, assoc_id %d\n",
+             ns, sctp_nb_cnx, assoc_id);
+}
+
 //------------------------------------------------------------------------------
 void
 sctp_handle_new_association_req(
@@ -441,7 +673,8 @@ static int sctp_close_association(
 static int sctp_create_new_listener(
   const instance_t instance,
   const task_id_t  requestor,
-  sctp_init_t     *init_p)
+  sctp_init_t     *init_p,
+  int server_type)
 {
   struct sctp_event_subscribe   event;
   struct sockaddr              *addr      = NULL;
@@ -501,9 +734,17 @@ static int sctp_create_new_listener(
     }
   }
 
-  if ((sd = socket(AF_INET6, SOCK_STREAM, IPPROTO_SCTP)) < 0) {
-    SCTP_ERROR("socket: %s:%d\n", strerror(errno), errno);
-    return -1;
+  if (server_type) {
+    if ((sd = socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) < 0) {
+      SCTP_ERROR("socket: %s:%d\n", strerror(errno), errno);
+      return -1;
+    }
+  }
+  else {
+    if ((sd = socket(AF_INET6, SOCK_STREAM, IPPROTO_SCTP)) < 0) {
+      SCTP_ERROR("socket: %s:%d\n", strerror(errno), errno);
+      return -1;
+    }
   }
 
   memset((void *)&event, 1, sizeof(struct sctp_event_subscribe));
@@ -516,7 +757,13 @@ static int sctp_create_new_listener(
 
   sctp_cnx = calloc(1, sizeof(*sctp_cnx));
 
-  sctp_cnx->connection_type = SCTP_TYPE_SERVER;
+  if (server_type) {
+    sctp_cnx->connection_type = SCTP_TYPE_MULTI_SERVER;
+  }
+  else {
+    sctp_cnx->connection_type = SCTP_TYPE_SERVER;
+  }
+
   sctp_cnx->sd              = sd;
   sctp_cnx->local_port      = init_p->port;
   sctp_cnx->in_streams      = 32;
@@ -729,6 +976,7 @@ sctp_eNB_read_from_socket(
       break;
 
       default:
+        SCTP_WARN("unhandled: SCTP_ASSOC_CHANGE to %d\n", sctp_assoc_changed->sac_state);
         break;
       }
     }
@@ -777,7 +1025,11 @@ sctp_eNB_flush_sockets(
 
     if (sctp_cnx->connection_type == SCTP_TYPE_CLIENT) {
       sctp_eNB_read_from_socket(sctp_cnx);
-    } else {
+    }
+    else if (sctp_cnx->connection_type == SCTP_TYPE_MULTI_SERVER) {
+      sctp_eNB_accept_associations_multi(sctp_cnx);
+    }
+    else {
       sctp_eNB_accept_associations(sctp_cnx);
     }
   }
@@ -791,6 +1043,7 @@ void *sctp_eNB_task(void *arg)
   struct epoll_event *events;
   MessageDef         *received_msg = NULL;
   int                 result;
+  int                 multi_sd = -1;
 
   SCTP_DEBUG("Starting SCTP layer\n");
 
@@ -812,13 +1065,43 @@ void *sctp_eNB_task(void *arg)
         if (sctp_create_new_listener(
               ITTI_MESSAGE_GET_INSTANCE(received_msg),
               ITTI_MSG_ORIGIN_ID(received_msg),
-              &received_msg->ittiMsg.sctp_init) < 0) {
+              &received_msg->ittiMsg.sctp_init,0) < 0) {
           /* SCTP socket creation or bind failed... */
           SCTP_ERROR("Failed to create new SCTP listener\n");
         }
       }
       break;
 
+      case SCTP_INIT_MSG_MULTI: {
+        SCTP_DEBUG("Received SCTP_INIT_MSG_MULTI\n");
+
+        multi_sd = sctp_create_new_listener(
+              ITTI_MESSAGE_GET_INSTANCE(received_msg),
+              ITTI_MSG_ORIGIN_ID(received_msg),
+              &received_msg->ittiMsg.sctp_init_multi,1);
+        /* We received a new connection request */
+         if (multi_sd < 0) {
+          /* SCTP socket creation or bind failed... */
+          SCTP_ERROR("Failed to create new SCTP listener\n");
+        }
+      }
+      break;
+
+      case SCTP_NEW_ASSOCIATION_REQ: {
+        sctp_handle_new_association_req(ITTI_MESSAGE_GET_INSTANCE(received_msg),
+                                        ITTI_MSG_ORIGIN_ID(received_msg),
+                                        &received_msg->ittiMsg.sctp_new_association_req);
+      }
+      break;
+
+      case SCTP_NEW_ASSOCIATION_REQ_MULTI: {
+        sctp_handle_new_association_req_multi(ITTI_MESSAGE_GET_INSTANCE(received_msg),
+                                        ITTI_MSG_ORIGIN_ID(received_msg),
+                                        &received_msg->ittiMsg.sctp_new_association_req_multi,
+                                        multi_sd);
+      }
+      break;
+
       case SCTP_CLOSE_ASSOCIATION:
         sctp_close_association(ITTI_MESSAGE_GET_INSTANCE(received_msg),
                                ITTI_MSG_ORIGIN_ID(received_msg),
@@ -830,13 +1113,6 @@ void *sctp_eNB_task(void *arg)
         itti_exit_task();
         break;
 
-      case SCTP_NEW_ASSOCIATION_REQ: {
-        sctp_handle_new_association_req(ITTI_MESSAGE_GET_INSTANCE(received_msg),
-                                        ITTI_MSG_ORIGIN_ID(received_msg),
-                                        &received_msg->ittiMsg.sctp_new_association_req);
-      }
-      break;
-
       case SCTP_DATA_REQ: {
         sctp_send_data(ITTI_MESSAGE_GET_INSTANCE(received_msg),
                        ITTI_MSG_ORIGIN_ID(received_msg),
diff --git a/targets/COMMON/create_tasks.c b/targets/COMMON/create_tasks.c
index 391e448deb..1870575b85 100644
--- a/targets/COMMON/create_tasks.c
+++ b/targets/COMMON/create_tasks.c
@@ -27,6 +27,7 @@
 # ifdef OPENAIR2
 #   if defined(ENABLE_USE_MME)
 #     include "sctp_eNB_task.h"
+#     include "x2ap_eNB.h"
 #     include "s1ap_eNB.h"
 #     include "nas_ue_task.h"
 #     include "udp_eNB_task.h"
@@ -62,6 +63,11 @@ int create_tasks(uint32_t enb_nb)
 
 #   if defined(ENABLE_USE_MME)
       if (enb_nb > 0) {
+        if (itti_create_task (TASK_X2AP, x2ap_task, NULL) < 0) {
+          LOG_E(X2AP, "Create task for X2AP failed\n");
+          return -1;
+        }
+
         if (itti_create_task (TASK_SCTP, sctp_eNB_task, NULL) < 0) {
           LOG_E(SCTP, "Create task for SCTP failed\n");
           return -1;
-- 
GitLab