diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt
index f5392acf5d2e18cdca1d88d8b401f84a4b2bf730..315510519c7dec53f109dfaa47e8349aada94a2c 100644
--- a/cmake_targets/CMakeLists.txt
+++ b/cmake_targets/CMakeLists.txt
@@ -923,6 +923,7 @@ include_directories("${OPENAIR2_DIR}/ENB_APP/CONTROL_MODULES/PHY")
 include_directories("${OPENAIR2_DIR}/ENB_APP/CONTROL_MODULES/MAC")
 include_directories("${OPENAIR2_DIR}/ENB_APP/CONTROL_MODULES/RRC")
 include_directories("${OPENAIR2_DIR}/ENB_APP/CONTROL_MODULES/PDCP")
+include_directories("${OPENAIR2_DIR}/ENB_APP/CONTROL_MODULES/S1AP")
 include_directories("${OPENAIR2_DIR}/UTIL/OSA")
 include_directories("${OPENAIR2_DIR}/UTIL/LFDS/liblfds6.1.1/liblfds611/inc")
 include_directories("${OPENAIR2_DIR}/UTIL/LFDS/liblfds7.0.0/liblfds700/inc")
@@ -1016,6 +1017,7 @@ add_library(FLEXRAN_AGENT
   ${OPENAIR2_DIR}/ENB_APP/CONTROL_MODULES/RRC/flexran_agent_rrc.c
   ${OPENAIR2_DIR}/ENB_APP/CONTROL_MODULES/RRC/flexran_agent_rrc_internal.c
   ${OPENAIR2_DIR}/ENB_APP/CONTROL_MODULES/PDCP/flexran_agent_pdcp.c
+  ${OPENAIR2_DIR}/ENB_APP/CONTROL_MODULES/S1AP/flexran_agent_s1ap.c
   ${OPENAIR2_DIR}/ENB_APP/flexran_agent.c
   ${OPENAIR2_DIR}/ENB_APP/flexran_agent_task_manager.c
   ${OPENAIR2_DIR}/ENB_APP/flexran_agent_net_comm.c
diff --git a/openair2/ENB_APP/CONTROL_MODULES/S1AP/flexran_agent_s1ap.c b/openair2/ENB_APP/CONTROL_MODULES/S1AP/flexran_agent_s1ap.c
new file mode 100644
index 0000000000000000000000000000000000000000..04a47406e1b9d6e7f58acd9ae9da94a4d3dbff63
--- /dev/null
+++ b/openair2/ENB_APP/CONTROL_MODULES/S1AP/flexran_agent_s1ap.c
@@ -0,0 +1,161 @@
+/*
+ * 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
+ */
+
+/*! \file flexran_agent_s1ap.c
+ * \brief FlexRAN agent Control Module S1AP
+ * \author Navid Nikaein
+ * \date 2019
+ * \version 0.1
+ */
+
+#include "flexran_agent_s1ap.h"
+
+
+/*Array containing the Agent-S1AP interfaces*/
+AGENT_S1AP_xface *agent_s1ap_xface[NUM_MAX_ENB];
+
+void flexran_agent_fill_s1ap_cell_config(mid_t mod_id,
+                                         Protocol__FlexS1apConfig **s1ap_config) {
+  *s1ap_config = malloc(sizeof(Protocol__FlexS1apConfig));
+  if (!*s1ap_config) return;
+  protocol__flex_s1ap_config__init(*s1ap_config);
+  LOG_D(FLEXRAN_AGENT, "flexran_agent_fill_s1ap_cell_config %d\n", mod_id);
+
+  // S1AP status
+  (*s1ap_config)->has_pending = 1;
+  (*s1ap_config)->pending = flexran_get_s1ap_mme_pending(mod_id);
+
+  (*s1ap_config)->has_connected = 1;
+  (*s1ap_config)->connected = flexran_get_s1ap_mme_connected(mod_id);
+
+  (*s1ap_config)->enb_s1_ip = flexran_get_s1ap_enb_s1_ip(mod_id);
+  if (!(*s1ap_config)->enb_s1_ip)
+    (*s1ap_config)->enb_s1_ip = "";
+
+  (*s1ap_config)->enb_name = flexran_get_s1ap_enb_name(mod_id);
+
+  (*s1ap_config)->mme = NULL;
+  (*s1ap_config)->n_mme = flexran_get_s1ap_nb_mme(mod_id);
+  if ((*s1ap_config)->n_mme > 0) {
+    Protocol__FlexS1apMme **mme_conf = calloc((*s1ap_config)->n_mme,
+                                              sizeof(Protocol__FlexS1apMme *));
+    AssertFatal(mme_conf, "%s(): MME malloc failed\n", __func__);
+    for(int i = 0; i < (*s1ap_config)->n_mme; i++){
+      mme_conf[i] = malloc(sizeof(Protocol__FlexS1apMme));
+      AssertFatal(mme_conf[i], "%s(): MME malloc failed\n", __func__);
+      protocol__flex_s1ap_mme__init(mme_conf[i]);
+      if (flexran_get_s1ap_mme_conf(mod_id, i, mme_conf[i]) < 0) {
+        LOG_E(FLEXRAN_AGENT,
+              "error in flexran_get_s1ap_mme_conf(): cannot retrieve MME state\n");
+        (*s1ap_config)->n_mme = 0;
+        break;
+      }
+    }
+    (*s1ap_config)->mme = mme_conf;
+  }
+}
+
+int flexran_agent_s1ap_stats_reply(mid_t mod_id,
+                                   Protocol__FlexUeStatsReport **ue_report,
+                                   int n_ue,
+                                   uint32_t ue_flags) {
+  if (n_ue <= 0)
+    return 0;
+  if (!(ue_flags & PROTOCOL__FLEX_UE_STATS_TYPE__FLUST_S1AP_STATS))
+    return 0;
+
+  for (int i = 0; i < n_ue; i++) {
+    const rnti_t rnti = ue_report[i]->rnti;
+    Protocol__FlexS1apUe *ue = malloc(sizeof(Protocol__FlexS1apUe));
+    AssertFatal(ue, "%s(): MME malloc failed\n", __func__);
+    protocol__flex_s1ap_ue__init(ue);
+    if (flexran_get_s1ap_ue(mod_id, rnti, ue) < 0) {
+      LOG_E(FLEXRAN_AGENT, "error in %s(): cannot retrieve UE conf\n", __func__);
+      break;
+    }
+    ue_report[i]->s1ap_stats = ue;
+    ue_report[i]->flags |= PROTOCOL__FLEX_UE_STATS_TYPE__FLUST_S1AP_STATS;
+  }
+  return 0;
+}
+
+void flexran_agent_free_s1ap_cell_config(Protocol__FlexS1apConfig **s1ap) {
+  for (int i = 0; i < (*s1ap)->n_mme; i++) {
+    /* following structures allocated in the RAN API */
+    for(int j = 0; j < (*s1ap)->mme[i]->n_served_gummeis; j++) {
+      free((*s1ap)->mme[i]->served_gummeis[j]->plmn);
+      free((*s1ap)->mme[i]->served_gummeis[j]);
+    }
+    free((*s1ap)->mme[i]->served_gummeis);
+    for (int j = 0; j < (*s1ap)->mme[i]->n_requested_plmns; j++)
+      free((*s1ap)->mme[i]->requested_plmns[j]);
+    free((*s1ap)->mme[i]->requested_plmns);
+    free((*s1ap)->mme[i]);
+  }
+  free((*s1ap)->mme);
+  free(*s1ap);
+  *s1ap = NULL;
+}
+
+void flexran_agent_s1ap_destroy_stats_reply(Protocol__FlexStatsReply *reply) {
+  for (int i = 0; i < reply->n_ue_report; ++i) {
+    if (!reply->ue_report[i]->s1ap_stats)
+      continue;
+    free(reply->ue_report[i]->s1ap_stats->selected_plmn);
+    free(reply->ue_report[i]->s1ap_stats);
+    reply->ue_report[i]->s1ap_stats = NULL;
+  }
+}
+
+int flexran_agent_register_s1ap_xface(mid_t mod_id) {
+  if (agent_s1ap_xface[mod_id]) {
+    LOG_E(FLEXRAN_AGENT, "S1AP agent CM for eNB %d is already registered\n", mod_id);
+    return -1;
+  }
+
+  AGENT_S1AP_xface *xface = malloc(sizeof(AGENT_S1AP_xface));
+  if (!xface) {
+    LOG_E(FLEXRAN_AGENT, "could not allocate memory for S1AP agent xface %d\n", mod_id);
+    return -1;
+  }
+
+  // not implemented yet
+  xface->flexran_s1ap_notify_release_request=NULL;
+
+  agent_s1ap_xface[mod_id] = xface;
+
+  return 0;
+}
+
+int flexran_agent_unregister_s1ap_xface(mid_t mod_id) {
+  if (!agent_s1ap_xface[mod_id]) {
+    LOG_E(FLEXRAN_AGENT, "S1AP agent for eNB %d is not registered\n", mod_id);
+    return -1;
+  }
+  agent_s1ap_xface[mod_id]->flexran_s1ap_notify_release_request=NULL;
+  free(agent_s1ap_xface[mod_id]);
+  agent_s1ap_xface[mod_id] = NULL;
+  return 0;
+}
+
+AGENT_S1AP_xface *flexran_agent_get_s1ap_xface(mid_t mod_id) {
+  return agent_s1ap_xface[mod_id];
+}
diff --git a/openair2/ENB_APP/CONTROL_MODULES/S1AP/flexran_agent_s1ap.h b/openair2/ENB_APP/CONTROL_MODULES/S1AP/flexran_agent_s1ap.h
new file mode 100644
index 0000000000000000000000000000000000000000..1cca6ac9c15e0a26d59b2b832fdd361d31c65de8
--- /dev/null
+++ b/openair2/ENB_APP/CONTROL_MODULES/S1AP/flexran_agent_s1ap.h
@@ -0,0 +1,69 @@
+/*
+ * 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
+ */
+
+/*! \file flexran_agent_s1ap.h
+ * \brief FlexRAN agent S1AP Control Module
+ * \author navid nikaein
+ * \date 2017
+ * \version 0.1
+ */
+
+#ifndef FLEXRAN_AGENT_S1AP_H_
+#define FLEXRAN_AGENT_S1AP_H_
+
+#include "header.pb-c.h"
+#include "flexran.pb-c.h"
+#include "stats_messages.pb-c.h"
+#include "stats_common.pb-c.h"
+
+
+#include "flexran_agent_common.h"
+#include "flexran_agent_defs.h"
+#include "flexran_agent_s1ap_defs.h"
+#include "flexran_agent_ran_api.h"
+
+/***************************************
+ * FlexRAN agent - technology S1AP API *
+ ***************************************/
+
+/* Send to the controller all the S1AP configs */
+void flexran_agent_fill_s1ap_cell_config(mid_t mod_id,
+                                         Protocol__FlexS1apConfig **s1ap_config);
+
+/* Free allocated S1AP cell configs */
+void flexran_agent_free_s1ap_cell_config(Protocol__FlexS1apConfig **s1ap);
+
+/* Fill the stats message for S1AP */
+int flexran_agent_s1ap_stats_reply(mid_t mod_id,
+                                   Protocol__FlexUeStatsReport **ue_report,
+                                   int n_ue,
+                                   uint32_t ue_flags);
+
+/* Free allocated S1AP stats message */
+void flexran_agent_s1ap_destroy_stats_reply(Protocol__FlexStatsReply *reply);
+
+/* Register technology specific interface callbacks */
+int flexran_agent_register_s1ap_xface(mid_t mod_id);
+
+/* Unregister technology specific callbacks */
+int flexran_agent_unregister_s1ap_xface(mid_t mod_id);
+
+#endif
diff --git a/openair2/ENB_APP/CONTROL_MODULES/S1AP/flexran_agent_s1ap_defs.h b/openair2/ENB_APP/CONTROL_MODULES/S1AP/flexran_agent_s1ap_defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..bbbc75160d8b44015d1ecb6520906af703c82721
--- /dev/null
+++ b/openair2/ENB_APP/CONTROL_MODULES/S1AP/flexran_agent_s1ap_defs.h
@@ -0,0 +1,32 @@
+/*
+ * 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 __FLEXRAN_AGENT_S1AP_PRIMITIVES_H__
+#define __FLEXRAN_AGENT_S1AP_PRIMITIVES_H__
+
+#include "flexran_agent_defs.h"
+
+/* FLEXRAN AGENT-S1AP Interface */
+typedef struct {
+  // S1AP  statistics
+  void (*flexran_s1ap_notify_release_request)(mid_t mod_id);
+} AGENT_S1AP_xface;
+
+#endif
diff --git a/openair2/ENB_APP/MESSAGES/V2/config_common.proto b/openair2/ENB_APP/MESSAGES/V2/config_common.proto
index c87e589d89bc4defab2788e8632e32975cd6bdba..733d883c92c0ed5af68e848051e6d2783ff937d6 100644
--- a/openair2/ENB_APP/MESSAGES/V2/config_common.proto
+++ b/openair2/ENB_APP/MESSAGES/V2/config_common.proto
@@ -355,13 +355,6 @@ message flex_s1ap_mme {
         optional uint32 rel_capacity = 6;                 // Relative MME capacity, TS23.401
 }
 
-message flex_s1ap_ue {
-        optional string mme_s1_ip = 1;                  // IP of MME to which UE is connected
-        optional uint32 enb_ue_s1ap_id = 2;             // S1AP ID on eNodeB side for UE
-        optional uint32 mme_ue_s1ap_id = 3;             // S1AP ID on MME side for UE
-        optional flex_plmn selected_plmn = 4;           // UE-selected PLMN in RRC Conn Setup Cplt
-}
-
 enum flex_mme_state {
         FLMMES_DISCONNECTED = 0;
         FLMMES_WAITING = 1;
diff --git a/openair2/ENB_APP/MESSAGES/V2/config_messages.proto b/openair2/ENB_APP/MESSAGES/V2/config_messages.proto
index 4d8c628c4f44902168756111d15374ef1672d1c2..324153613d396307e3a37bce643bdcb60220e19e 100644
--- a/openair2/ENB_APP/MESSAGES/V2/config_messages.proto
+++ b/openair2/ENB_APP/MESSAGES/V2/config_messages.proto
@@ -100,7 +100,6 @@ message flex_ue_config {
 	optional uint32 ul_slice_id = 32;
   // Configuration about RRC measurements
   optional flex_measurement_info info = 33;
-  optional uint32 enb_ue_s1ap_id = 34;            // S1AP ID on eNodeB side
 }
 
 message flex_lc_ue_config {
@@ -109,10 +108,9 @@ message flex_lc_ue_config {
 }
 
 message flex_s1ap_config {
-        optional uint32 pending = 1;              // number of pending (to be connected) MMEs
-        optional uint32 connected = 2;            // number of connected MMEs
-        optional string enb_s1_ip = 3;            // S1-MME IP of eNodeB
-        optional string enb_name = 4;             // S1-MME name of eNodeB
-        repeated flex_s1ap_mme mme = 5;
-        repeated flex_s1ap_ue ue = 6;
+  optional uint32 pending = 1;              // number of pending (to be connected) MMEs
+  optional uint32 connected = 2;            // number of connected MMEs
+  optional string enb_s1_ip = 3;            // S1-MME IP of eNodeB
+  optional string enb_name = 4;             // S1-MME name of eNodeB
+  repeated flex_s1ap_mme mme = 5;
 }
diff --git a/openair2/ENB_APP/MESSAGES/V2/flexran.proto b/openair2/ENB_APP/MESSAGES/V2/flexran.proto
index 24a219a564833a29a5f1976e264d9e9188f2c79b..e2ced1dac71089d19b97780dfc93f285bb4b7ea8 100644
--- a/openair2/ENB_APP/MESSAGES/V2/flexran.proto
+++ b/openair2/ENB_APP/MESSAGES/V2/flexran.proto
@@ -75,6 +75,7 @@ enum flex_bs_capability {
     PDCP  = 5;
     SDAP  = 6;
     RRC   = 7;
+    S1AP  = 8;
 }
 
 enum flex_bs_split {
diff --git a/openair2/ENB_APP/MESSAGES/V2/stats_common.proto b/openair2/ENB_APP/MESSAGES/V2/stats_common.proto
index 20f0f60a9f00a74340a9f38d0049c2bcd0d701ea..62bd06d70706191b9bdd88a6a13ade6516afcc20 100644
--- a/openair2/ENB_APP/MESSAGES/V2/stats_common.proto
+++ b/openair2/ENB_APP/MESSAGES/V2/stats_common.proto
@@ -310,3 +310,15 @@ message flex_gtp_stats {
     optional uint32 teid_sgw = 4;
     optional string addr_sgw = 5;
 }
+
+//
+// S1AP stats
+//
+
+message flex_s1ap_ue {
+        optional string mme_s1_ip = 1;                  // IP of MME to which UE is connected
+        optional uint32 enb_ue_s1ap_id = 2;             // S1AP ID on eNodeB side for UE
+        optional uint32 mme_ue_s1ap_id = 3;             // S1AP ID on MME side for UE
+        optional flex_plmn selected_plmn = 4;           // UE-selected PLMN in RRC Conn Setup Cplt
+}
+
diff --git a/openair2/ENB_APP/MESSAGES/V2/stats_messages.proto b/openair2/ENB_APP/MESSAGES/V2/stats_messages.proto
index 7cceb04a01cf61f020ca55f2b1b7de09d9e6a5dd..6f67648d829fe991f43f68d7937a872aae65e0f7 100644
--- a/openair2/ENB_APP/MESSAGES/V2/stats_messages.proto
+++ b/openair2/ENB_APP/MESSAGES/V2/stats_messages.proto
@@ -51,6 +51,7 @@ message flex_ue_stats_report {
         optional flex_pdcp_stats pdcp_stats = 11;
         optional flex_mac_stats mac_stats = 12;
     repeated flex_gtp_stats gtp_stats = 13;
+    optional flex_s1ap_ue s1ap_stats = 14;
 }
 
 //
@@ -91,6 +92,7 @@ enum flex_ue_stats_type {
 
      FLUST_PDCP_STATS = 1024;     
      FLUST_GTP_STATS = 2048;
+     FLUST_S1AP_STATS = 4096;
      FLUST_RRC_MEASUREMENTS = 65536;
      // To be extended with more types of stats
 
diff --git a/openair2/ENB_APP/flexran_agent.c b/openair2/ENB_APP/flexran_agent.c
index a8ee9b98e857449addeda13358704975c0a3d264..519d2bef4bfa17e2ff9328addc0ce6089be3ec42 100644
--- a/openair2/ENB_APP/flexran_agent.c
+++ b/openair2/ENB_APP/flexran_agent.c
@@ -177,8 +177,8 @@ int flexran_agent_start(mid_t mod_id)
   /* Register and initialize the control modules depending on capabilities.
    * After registering, calling flexran_agent_get_*_xface() tells whether a
    * control module is operational */
-  uint16_t caps = flexran_get_capabilities_mask(mod_id);
-  LOG_I(FLEXRAN_AGENT, "Agent handles BS ID %ld, capabilities=0x%x => handling%s%s%s%s%s%s%s%s\n",
+  uint32_t caps = flexran_get_capabilities_mask(mod_id);
+  LOG_I(FLEXRAN_AGENT, "Agent handles BS ID %ld, capabilities=0x%x => handling%s%s%s%s%s%s%s%s%s\n",
         flexran_get_bs_id(mod_id), caps,
         FLEXRAN_CAP_LOPHY(caps) ? " LOPHY" : "",
         FLEXRAN_CAP_HIPHY(caps) ? " HIPHY" : "",
@@ -187,7 +187,8 @@ int flexran_agent_start(mid_t mod_id)
         FLEXRAN_CAP_RLC(caps)   ? " RLC"   : "",
         FLEXRAN_CAP_PDCP(caps)  ? " PDCP"  : "",
         FLEXRAN_CAP_SDAP(caps)  ? " SDAP"  : "",
-        FLEXRAN_CAP_RRC(caps)   ? " RRC"   : "");
+        FLEXRAN_CAP_RRC(caps)   ? " RRC"   : "",
+        FLEXRAN_CAP_S1AP(caps)  ? " S1AP"  : "");
 
   if (FLEXRAN_CAP_LOPHY(caps) || FLEXRAN_CAP_HIPHY(caps)) {
     flexran_agent_register_phy_xface(mod_id);
@@ -210,6 +211,11 @@ int flexran_agent_start(mid_t mod_id)
     LOG_I(FLEXRAN_AGENT, "registered PDCP interface/CM for eNB %d\n", mod_id);
   }
 
+  if (FLEXRAN_CAP_S1AP(caps)) {
+    flexran_agent_register_s1ap_xface(mod_id);
+    LOG_I(FLEXRAN_AGENT, "registered S1AP interface/CM for eNB %d\n", mod_id);
+  }
+
   /* 
    * initilize a timer 
    */ 
diff --git a/openair2/ENB_APP/flexran_agent.h b/openair2/ENB_APP/flexran_agent.h
index e50a2be4f65cf6a0e0f29541cb0333a645cc5064..42295fc111ca5efd72670cd7b9fd1b8a8ee1bb54 100644
--- a/openair2/ENB_APP/flexran_agent.h
+++ b/openair2/ENB_APP/flexran_agent.h
@@ -40,6 +40,7 @@
 #include "flexran_agent_mac.h"
 #include "flexran_agent_rrc.h"
 #include "flexran_agent_pdcp.h"
+#include "flexran_agent_s1ap.h"
 #include "common/utils/LOG/log.h"
 #include "assertions.h"
 
diff --git a/openair2/ENB_APP/flexran_agent_common.c b/openair2/ENB_APP/flexran_agent_common.c
index fe65315fe9c1e4c49b5753a5f4e9bf7568bfdade..ef428b7be52e716b31a75fc43936183b73378c83 100644
--- a/openair2/ENB_APP/flexran_agent_common.c
+++ b/openair2/ENB_APP/flexran_agent_common.c
@@ -21,7 +21,7 @@
 
 /*! \file flexran_agent_common.c
  * \brief common primitives for all agents
- * \author Xenofon Foukas, Mohamed Kassem and Navid Nikaein, shahab SHARIAT BAGHERI
+ * \author Xenofon Foukas, Mohamed Kassem and Navid Nikaein
  * \date 2017
  * \version 0.1
  */
@@ -38,6 +38,7 @@
 #include "flexran_agent_phy.h"
 #include "flexran_agent_mac.h"
 #include "flexran_agent_rrc.h"
+#include "flexran_agent_s1ap.h"
 //#include "PHY/extern.h"
 #include "common/utils/LOG/log.h"
 #include "flexran_agent_mac_internal.h"
@@ -339,6 +340,9 @@ int flexran_agent_destroy_enb_config_reply(Protocol__FlexranMessage *msg) {
 
     free(reply->cell_config[i]);
   }
+  
+  if (reply->s1ap)
+    flexran_agent_free_s1ap_cell_config(&reply->s1ap);
 
   free(reply->cell_config);
   free(reply);
@@ -757,9 +761,12 @@ int flexran_agent_enb_config_reply(mid_t mod_id, const void *params, Protocol__F
       cell_conf[i]->carrier_index = i;
       cell_conf[i]->has_carrier_index = 1;
     }
-
+    
     enb_config_reply_msg->cell_config=cell_conf;
   }
+  
+  if (flexran_agent_get_s1ap_xface(mod_id))
+    flexran_agent_fill_s1ap_cell_config(mod_id, &enb_config_reply_msg->s1ap);
 
   *msg = malloc(sizeof(Protocol__FlexranMessage));
 
diff --git a/openair2/ENB_APP/flexran_agent_defs.h b/openair2/ENB_APP/flexran_agent_defs.h
index 20aac5f56e42c5da4e59c7f9cecf3bafb3448c63..bc1976abd95993435c5a98f88c6646a82b17e221 100644
--- a/openair2/ENB_APP/flexran_agent_defs.h
+++ b/openair2/ENB_APP/flexran_agent_defs.h
@@ -116,6 +116,7 @@ typedef int32_t  err_code_t;
 #define FLEXRAN_CAP_PDCP(cApS)  (((cApS) & (1 << PROTOCOL__FLEX_BS_CAPABILITY__PDCP))  > 0)
 #define FLEXRAN_CAP_SDAP(cApS)  (((cApS) & (1 << PROTOCOL__FLEX_BS_CAPABILITY__SDAP))  > 0)
 #define FLEXRAN_CAP_RRC(cApS)   (((cApS) & (1 << PROTOCOL__FLEX_BS_CAPABILITY__RRC))   > 0)
+#define FLEXRAN_CAP_S1AP(cApS)  (((cApS) & (1 << PROTOCOL__FLEX_BS_CAPABILITY__S1AP))  > 0)
 
 typedef enum {
   ENB_NORMAL_OPERATION = 0x0,
diff --git a/openair2/ENB_APP/flexran_agent_extern.h b/openair2/ENB_APP/flexran_agent_extern.h
index e1dec8506ce6c257429d773981c3edd2bbb9d91d..119bc3d44c6b32967efb6e50f64495b5d72960af 100644
--- a/openair2/ENB_APP/flexran_agent_extern.h
+++ b/openair2/ENB_APP/flexran_agent_extern.h
@@ -35,6 +35,7 @@
 #include "flexran_agent_mac_defs.h"
 #include "flexran_agent_rrc_defs.h"
 #include "flexran_agent_pdcp_defs.h"
+#include "flexran_agent_s1ap_defs.h"
 
 /* Control module interface for the communication of the PHY control module with the agent */
 AGENT_PHY_xface *flexran_agent_get_phy_xface(mid_t mod_id);
@@ -48,6 +49,9 @@ AGENT_RRC_xface *flexran_agent_get_rrc_xface(mid_t mod_id);
 /* Control module interface for the communication of the RRC Control Module with the agent */
 AGENT_PDCP_xface *flexran_agent_get_pdcp_xface(mid_t mod_id);
 
+/* Control module interface for the communication of the S1AP Control Module with the agent */
+AGENT_S1AP_xface *flexran_agent_get_s1ap_xface(mid_t mod_id);
+
 /* Requried to know which UEs had a harq updated over some subframe */
 extern int harq_pid_updated[NUM_MAX_UE][8];
 extern int harq_pid_round[NUM_MAX_UE][8];
diff --git a/openair2/ENB_APP/flexran_agent_handler.c b/openair2/ENB_APP/flexran_agent_handler.c
index 7c251e9172d7c89dfdf014b8c2b12b179a8a3eec..d991078cf74c19430703bb8f97a07f25586cb592 100644
--- a/openair2/ENB_APP/flexran_agent_handler.c
+++ b/openair2/ENB_APP/flexran_agent_handler.c
@@ -31,6 +31,7 @@
 #include "flexran_agent_mac.h"
 #include "flexran_agent_rrc.h"
 #include "flexran_agent_pdcp.h"
+#include "flexran_agent_s1ap.h"
 #include "flexran_agent_timer.h"
 #include "flexran_agent_ran_api.h"
 #include "common/utils/LOG/log.h"
@@ -324,6 +325,14 @@ int flexran_agent_stats_reply(mid_t enb_id,
     goto error;
   }
 
+  /* S1AP statistics, depends on RRC to find S1AP ID */
+  if (flexran_agent_get_rrc_xface(enb_id)
+      && flexran_agent_get_s1ap_xface(enb_id)
+      && flexran_agent_s1ap_stats_reply(enb_id, ue_report, n_ue, ue_flags) < 0) {
+    err_code = PROTOCOL__FLEXRAN_ERR__MSG_BUILD;
+    goto error;
+  }
+
   if (flexran_create_header(xid, PROTOCOL__FLEX_TYPE__FLPT_STATS_REPLY, &header) != 0) {
     goto error;
   }
@@ -523,6 +532,8 @@ int flexran_agent_destroy_stats_reply(Protocol__FlexranMessage *msg)
   flexran_agent_mac_destroy_stats_reply(msg->stats_reply_msg);
   flexran_agent_rrc_destroy_stats_reply(msg->stats_reply_msg);
   flexran_agent_pdcp_destroy_stats_reply(msg->stats_reply_msg);
+  flexran_agent_rrc_gtp_destroy_stats_reply(msg->stats_reply_msg);
+  flexran_agent_s1ap_destroy_stats_reply(msg->stats_reply_msg);
   for (int i = 0; i < msg->stats_reply_msg->n_cell_report; ++i)
     free(msg->stats_reply_msg->cell_report[i]);
   for (int i = 0; i < msg->stats_reply_msg->n_ue_report; ++i)
diff --git a/openair2/ENB_APP/flexran_agent_ran_api.c b/openair2/ENB_APP/flexran_agent_ran_api.c
index f479198895306b966bb7f547ea41e55713093109..a66081d99f236bdb378a5a887e9ca8835998e3aa 100644
--- a/openair2/ENB_APP/flexran_agent_ran_api.c
+++ b/openair2/ENB_APP/flexran_agent_ran_api.c
@@ -28,6 +28,8 @@
 
 #include <dlfcn.h>
 #include "flexran_agent_ran_api.h"
+#include "s1ap_eNB_ue_context.h"
+#include "s1ap_eNB_management_procedures.h"
 
 static inline int phy_is_present(mid_t mod_id, uint8_t cc_id) {
   return RC.eNB && RC.eNB[mod_id] && RC.eNB[mod_id][cc_id];
@@ -3002,6 +3004,14 @@ int flexran_agent_rrc_gtp_get_teid_sgw(mid_t mod_id, rnti_t rnti, int index) {
   return ue_context_p->ue_context.e_rab[index].param.gtp_teid;
 }
 
+uint32_t flexran_get_rrc_enb_ue_s1ap_id(mid_t mod_id, rnti_t rnti)
+{
+  if (!rrc_is_present(mod_id)) return 0;
+  struct rrc_eNB_ue_context_s* ue_context_p = rrc_eNB_get_ue_context(RC.rrc[mod_id], rnti);
+  if (!ue_context_p) return -1;
+  return ue_context_p->ue_context.eNB_ue_s1ap_id;
+}
+
 /**************************** SLICING ****************************/
 int flexran_get_ue_dl_slice_id(mid_t mod_id, mid_t ue_id) {
   if (!mac_is_present(mod_id)) return -1;
@@ -3470,6 +3480,195 @@ int flexran_set_ul_slice_scheduler(mid_t mod_id, int slice_idx, char *name) {
   return RC.mac[mod_id]->slice_info.ul[slice_idx].sched_cb != NULL;
 }
 
+/************************** S1AP **************************/
+int flexran_get_s1ap_mme_pending(mid_t mod_id){
+  if (!rrc_is_present(mod_id)) return -1;
+  s1ap_eNB_instance_t *s1ap = s1ap_eNB_get_instance(mod_id);
+  if (!s1ap) return -1;
+  return s1ap->s1ap_mme_pending_nb;
+}
+
+int flexran_get_s1ap_mme_connected(mid_t mod_id){
+  if (!rrc_is_present(mod_id)) return -1;
+  s1ap_eNB_instance_t *s1ap = s1ap_eNB_get_instance(mod_id);
+  if (!s1ap) return -1;
+  return s1ap->s1ap_mme_associated_nb;
+}
+
+char* flexran_get_s1ap_enb_s1_ip(mid_t mod_id){
+  if (!rrc_is_present(mod_id)) return NULL;
+  s1ap_eNB_instance_t *s1ap = s1ap_eNB_get_instance(mod_id);
+  if (!s1ap) return NULL;
+  if (s1ap->eNB_s1_ip.ipv4)
+    return &s1ap->eNB_s1_ip.ipv4_address[0];
+  if (s1ap->eNB_s1_ip.ipv6)
+    return &s1ap->eNB_s1_ip.ipv6_address[0];
+  return NULL;
+}
+
+char* flexran_get_s1ap_enb_name(mid_t mod_id){
+  if (!rrc_is_present(mod_id)) return NULL;
+  s1ap_eNB_instance_t *s1ap = s1ap_eNB_get_instance(mod_id);
+  if (!s1ap) return NULL;
+  return s1ap->eNB_name;
+}
+
+int flexran_get_s1ap_nb_mme(mid_t mod_id) {
+  s1ap_eNB_instance_t *s1ap = s1ap_eNB_get_instance(mod_id);
+  if (!s1ap) return 0;
+  struct s1ap_eNB_mme_data_s *mme = NULL;
+  int count = 0;
+  RB_FOREACH(mme, s1ap_mme_map, &s1ap->s1ap_mme_head) {
+    count++;
+  }
+  return count;
+}
+
+int flexran_get_s1ap_nb_ue(mid_t mod_id) {
+  s1ap_eNB_instance_t *s1ap = s1ap_eNB_get_instance(mod_id);
+  if (!s1ap) return 0;
+  struct s1ap_eNB_ue_context_s *ue = NULL;
+  int count = 0;
+  RB_FOREACH(ue, s1ap_ue_map, &s1ap->s1ap_ue_head) {
+    count++;
+  }
+  return count;
+}
+
+int flexran_get_s1ap_mme_conf(mid_t mod_id, mid_t mme_index, Protocol__FlexS1apMme * mme_conf){
+  if (!rrc_is_present(mod_id)) return -1;
+  s1ap_eNB_instance_t *s1ap = s1ap_eNB_get_instance(mod_id);
+  if (!s1ap) return -1;
+
+  struct served_gummei_s   *gummei_p = NULL;
+  struct plmn_identity_s   *served_plmn_p = NULL;
+  struct served_group_id_s *group_id_p = NULL;
+  struct mme_code_s        *mme_code_p = NULL;
+  int i = 0;
+  Protocol__FlexGummei  **served_gummeis;
+  Protocol__FlexPlmn    **requested_plmns;
+
+  struct s1ap_eNB_mme_data_s *mme = NULL;
+
+  RB_FOREACH(mme, s1ap_mme_map, &s1ap->s1ap_mme_head){
+    if (mme_index == 0) break;
+    mme_index--;
+  }
+  if (mme_index > 0) return -1;
+
+  if (mme->mme_s1_ip.ipv4) {
+    mme_conf->s1_ip = (char*) &mme->mme_s1_ip.ipv4_address[0];
+  } else if (mme->mme_s1_ip.ipv6) {
+    mme_conf->s1_ip = (char*) &mme->mme_s1_ip.ipv6_address[0];
+  }
+  mme_conf->name = mme->mme_name;
+  mme_conf->has_state = 1;
+  mme_conf->state = mme->state;
+
+  mme_conf->n_served_gummeis = 0;
+  STAILQ_FOREACH(gummei_p, &mme->served_gummei, next) {
+    mme_conf->n_served_gummeis++;
+  }
+  if (mme_conf->n_served_gummeis > 0) {
+    served_gummeis = calloc(mme_conf->n_served_gummeis, sizeof(Protocol__FlexGummei*));
+    if(served_gummeis == NULL) return -1;
+
+    STAILQ_FOREACH(gummei_p, &mme->served_gummei, next) {
+      served_plmn_p = STAILQ_FIRST(&gummei_p->served_plmns);
+      group_id_p = STAILQ_FIRST(&gummei_p->served_group_ids);
+      mme_code_p = STAILQ_FIRST(&gummei_p->mme_codes);
+
+      served_gummeis[i] = malloc(sizeof(Protocol__FlexGummei));
+      if (!served_gummeis[i]) return -1;
+      protocol__flex_gummei__init(served_gummeis[i]);
+      served_gummeis[i]->plmn = malloc(sizeof(Protocol__FlexPlmn));
+      if (!served_gummeis[i]->plmn) return -1;
+      protocol__flex_plmn__init(served_gummeis[i]->plmn);
+
+      if (served_plmn_p) {
+        served_gummeis[i]->plmn->has_mcc = 1;
+        served_gummeis[i]->plmn->mcc = served_plmn_p->mcc;
+        served_gummeis[i]->plmn->has_mnc = 1;
+        served_gummeis[i]->plmn->mnc = served_plmn_p->mnc;
+        served_gummeis[i]->plmn->has_mnc_length = 1;
+        served_gummeis[i]->plmn->mnc_length = served_plmn_p-> mnc_digit_length;
+        STAILQ_NEXT(served_plmn_p, next);
+      }
+      if (group_id_p) {
+        served_gummeis[i]->has_mme_group_id = 1;
+        served_gummeis[i]->mme_group_id = group_id_p->mme_group_id;
+        STAILQ_NEXT(group_id_p, next);
+      }
+      if (mme_code_p){
+        served_gummeis[i]->has_mme_code = 1;
+        served_gummeis[i]->mme_code = mme_code_p->mme_code;
+        STAILQ_NEXT(mme_code_p, next);
+      }
+      i++;
+    }
+
+    mme_conf->served_gummeis = served_gummeis;
+  }
+
+  // requested PLMNS
+  mme_conf->n_requested_plmns = mme->broadcast_plmn_num;
+  if (mme_conf->n_requested_plmns > 0){
+    requested_plmns = calloc(mme_conf->n_requested_plmns, sizeof(Protocol__FlexPlmn*));
+    if(requested_plmns == NULL) return -1;
+    for(int i = 0; i < mme_conf->n_requested_plmns; i++) {
+      requested_plmns[i] = malloc(sizeof(Protocol__FlexPlmn));
+      if (!requested_plmns[i]) return -1;
+      protocol__flex_plmn__init(requested_plmns[i]);
+      requested_plmns[i]->mcc = s1ap->mcc[mme->broadcast_plmn_index[i]];
+      requested_plmns[i]->has_mcc = 1;
+      requested_plmns[i]->mnc = s1ap->mnc[mme->broadcast_plmn_index[i]];
+      requested_plmns[i]->has_mnc = 1;
+      requested_plmns[i]->mnc_length = s1ap->mnc_digit_length[mme->broadcast_plmn_index[i]];
+      requested_plmns[i]->has_mnc_length = 1;
+    }
+    mme_conf->requested_plmns = requested_plmns;
+  }
+
+  mme_conf->has_rel_capacity = 1;
+  mme_conf->rel_capacity = mme->relative_mme_capacity;
+  return 0;
+}
+
+int flexran_get_s1ap_ue(mid_t mod_id, rnti_t rnti, Protocol__FlexS1apUe * ue_conf){
+  if (!rrc_is_present(mod_id)) return -1;
+  s1ap_eNB_instance_t *s1ap = s1ap_eNB_get_instance(mod_id);
+  if (!s1ap) return -1;
+
+  uint32_t enb_ue_s1ap_id = flexran_get_rrc_enb_ue_s1ap_id(mod_id, rnti);
+  struct s1ap_eNB_ue_context_s *ue = NULL;
+  RB_FOREACH(ue, s1ap_ue_map, &s1ap->s1ap_ue_head){
+    if (ue->eNB_ue_s1ap_id == enb_ue_s1ap_id) break;
+  }
+  if (ue == NULL) return -1;
+
+  if (ue->mme_ref->mme_s1_ip.ipv4)
+    ue_conf->mme_s1_ip = (char*) &ue->mme_ref->mme_s1_ip.ipv4_address[0];
+  else if (ue->mme_ref->mme_s1_ip.ipv6)
+    ue_conf->mme_s1_ip = (char*) &ue->mme_ref->mme_s1_ip.ipv6_address[0];
+
+  ue_conf->has_enb_ue_s1ap_id = 1;
+  ue_conf->enb_ue_s1ap_id = ue->eNB_ue_s1ap_id;
+  ue_conf->has_mme_ue_s1ap_id = 1;
+  ue_conf->mme_ue_s1ap_id = ue->mme_ue_s1ap_id;
+
+  ue_conf->selected_plmn = malloc(sizeof(Protocol__FlexPlmn));
+  if (!ue_conf->selected_plmn) return -1;
+  protocol__flex_plmn__init(ue_conf->selected_plmn);
+
+  ue_conf->selected_plmn->has_mcc = 1;
+  ue_conf->selected_plmn->mcc = s1ap->mcc[ue->selected_plmn_identity];
+  ue_conf->selected_plmn->has_mnc = 1;
+  ue_conf->selected_plmn->mnc = s1ap->mnc[ue->selected_plmn_identity];
+  ue_conf->selected_plmn->has_mnc_length = 1;
+  ue_conf->selected_plmn->mnc_length = s1ap->mnc_digit_length[ue->selected_plmn_identity];
+  return 0;
+}
+
 /**************************** General BS info  ****************************/
 uint64_t flexran_get_bs_id(mid_t mod_id) {
   if (!rrc_is_present(mod_id)) return 0;
@@ -3488,13 +3687,14 @@ size_t flexran_get_capabilities(mid_t mod_id, Protocol__FlexBsCapability **caps)
     case ngran_eNB_CU:
     case ngran_ng_eNB_CU:
     case ngran_gNB_CU:
-      n_caps = 3;
+      n_caps = 4;
       *caps = calloc(n_caps, sizeof(Protocol__FlexBsCapability));
       AssertFatal(*caps, "could not allocate %zu bytes for Protocol__FlexBsCapability array\n",
                   n_caps * sizeof(Protocol__FlexBsCapability));
       (*caps)[0] = PROTOCOL__FLEX_BS_CAPABILITY__PDCP;
       (*caps)[1] = PROTOCOL__FLEX_BS_CAPABILITY__SDAP;
       (*caps)[2] = PROTOCOL__FLEX_BS_CAPABILITY__RRC;
+      (*caps)[3] = PROTOCOL__FLEX_BS_CAPABILITY__S1AP;
       break;
     case ngran_eNB_DU:
     case ngran_gNB_DU:
@@ -3511,7 +3711,7 @@ size_t flexran_get_capabilities(mid_t mod_id, Protocol__FlexBsCapability **caps)
     case ngran_eNB:
     case ngran_ng_eNB:
     case ngran_gNB:
-      n_caps = 8;
+      n_caps = 9;
       *caps = calloc(n_caps, sizeof(Protocol__FlexBsCapability));
       AssertFatal(*caps, "could not allocate %zu bytes for Protocol__FlexBsCapability array\n",
                   n_caps * sizeof(Protocol__FlexBsCapability));
@@ -3523,26 +3723,27 @@ size_t flexran_get_capabilities(mid_t mod_id, Protocol__FlexBsCapability **caps)
       (*caps)[5] = PROTOCOL__FLEX_BS_CAPABILITY__PDCP;
       (*caps)[6] = PROTOCOL__FLEX_BS_CAPABILITY__SDAP;
       (*caps)[7] = PROTOCOL__FLEX_BS_CAPABILITY__RRC;
+      (*caps)[8] = PROTOCOL__FLEX_BS_CAPABILITY__S1AP;
       break;
     case ngran_eNB_MBMS_STA:
+      AssertFatal(0, "MBMS STA not supported by FlexRAN!\n");
      break;
   }
 
   return n_caps;
 }
 
-uint16_t flexran_get_capabilities_mask(mid_t mod_id) {
+uint32_t flexran_get_capabilities_mask(mid_t mod_id) {
   if (!rrc_is_present(mod_id)) return 0;
-
-  uint16_t mask = 0;
-
+  uint32_t mask = 0;
   switch (RC.rrc[mod_id]->node_type) {
     case ngran_eNB_CU:
     case ngran_ng_eNB_CU:
     case ngran_gNB_CU:
       mask = (1 << PROTOCOL__FLEX_BS_CAPABILITY__PDCP)
            | (1 << PROTOCOL__FLEX_BS_CAPABILITY__SDAP)
-           | (1 << PROTOCOL__FLEX_BS_CAPABILITY__RRC);
+           | (1 << PROTOCOL__FLEX_BS_CAPABILITY__RRC)
+           | (1 << PROTOCOL__FLEX_BS_CAPABILITY__S1AP);
       break;
     case ngran_eNB_DU:
     case ngran_gNB_DU:
@@ -3562,9 +3763,11 @@ uint16_t flexran_get_capabilities_mask(mid_t mod_id) {
            | (1 << PROTOCOL__FLEX_BS_CAPABILITY__RLC)
            | (1 << PROTOCOL__FLEX_BS_CAPABILITY__PDCP)
            | (1 << PROTOCOL__FLEX_BS_CAPABILITY__SDAP)
-           | (1 << PROTOCOL__FLEX_BS_CAPABILITY__RRC);
+           | (1 << PROTOCOL__FLEX_BS_CAPABILITY__RRC)
+           | (1 << PROTOCOL__FLEX_BS_CAPABILITY__S1AP);
       break;
     case ngran_eNB_MBMS_STA:
+      AssertFatal(0, "MBMS STA not supported by FlexRAN!\n");
      break;
   }
 
diff --git a/openair2/ENB_APP/flexran_agent_ran_api.h b/openair2/ENB_APP/flexran_agent_ran_api.h
index 6aef1c406b3a4f1c3abf28a198dbd239dc34ba6e..ce328838529508ba3e65153e83171ab2d0a7c978 100644
--- a/openair2/ENB_APP/flexran_agent_ran_api.h
+++ b/openair2/ENB_APP/flexran_agent_ran_api.h
@@ -652,6 +652,9 @@ int flexran_agent_rrc_gtp_get_teid_enb(mid_t mod_id, rnti_t rnti, int index);
 /* Get the TEID at the SGW for UE */
 int flexran_agent_rrc_gtp_get_teid_sgw(mid_t mod_id, rnti_t rnti, int index);
 
+/* gets the UEs S1AP ID at eNodeB, stored in RRC */
+uint32_t flexran_get_rrc_enb_ue_s1ap_id(mid_t mod_id, rnti_t rnti);
+
 /************************** Slice configuration **************************/
 
 /* Get the DL slice ID for a UE */
@@ -804,6 +807,31 @@ char *flexran_get_ul_slice_scheduler(mid_t mod_id, int slice_idx);
 /* Set the scheduler name for a slice in UL */
 int flexran_set_ul_slice_scheduler(mid_t mod_id, int slice_idx, char *name);
 
+/************************** S1AP **************************/
+/* Get the number of MMEs to be connected */
+int flexran_get_s1ap_mme_pending(mid_t mod_id);
+
+/* Get the number of connected MMEs */
+int flexran_get_s1ap_mme_connected(mid_t mod_id);
+
+/* Get the eNB S1AP IP address */
+char* flexran_get_s1ap_enb_s1_ip(mid_t mod_id);
+
+/* Get the name of the eNB */
+char* flexran_get_s1ap_enb_name(mid_t mod_id);
+
+/* Get the number of connected MMEs to this eNB */
+int flexran_get_s1ap_nb_mme(mid_t mod_id);
+
+/* Get the number of connected UEs to this eNB */
+int flexran_get_s1ap_nb_ue(mid_t mod_id);
+
+/* Get the S1AP MME conf */
+int flexran_get_s1ap_mme_conf(mid_t mod_id, mid_t mme_index, Protocol__FlexS1apMme * mme_conf);
+
+/* Get the S1AP UE conf */
+int flexran_get_s1ap_ue(mid_t mod_id, rnti_t rnti, Protocol__FlexS1apUe * ue_conf);
+
 /********************* general information *****************/
 /* get an ID for this BS (or part of a BS) */
 uint64_t flexran_get_bs_id(mid_t mod_id);
@@ -815,7 +843,7 @@ size_t flexran_get_capabilities(mid_t mod_id, Protocol__FlexBsCapability **caps)
 
 /* get the capabilities supported by the underlying network function as a bit
  * mask. */
-uint16_t flexran_get_capabilities_mask(mid_t mod_id);
+uint32_t flexran_get_capabilities_mask(mid_t mod_id);
 
 /* get the splits used by the underlying network function,
  * return the number and stores list of this length in splits. If there are
diff --git a/openair3/S1AP/s1ap_eNB.c b/openair3/S1AP/s1ap_eNB.c
index 03e6273d1b4dbdce4d1bace082b9a221c3e83cd5..3592cf80a14ef7d2e52f87b3b3112cf4558b38a2 100644
--- a/openair3/S1AP/s1ap_eNB.c
+++ b/openair3/S1AP/s1ap_eNB.c
@@ -124,7 +124,9 @@ static void s1ap_eNB_register_mme(s1ap_eNB_instance_t *instance_p,
     sctp_new_association_req_p->ulp_cnx_id = s1ap_mme_data_p->cnx_id;
     s1ap_mme_data_p->assoc_id          = -1;
     s1ap_mme_data_p->broadcast_plmn_num = broadcast_plmn_num;
-
+    memcpy(&s1ap_mme_data_p->mme_s1_ip,
+    	   mme_ip_address,
+    	   sizeof(*mme_ip_address));
     for (int i = 0; i < broadcast_plmn_num; ++i)
       s1ap_mme_data_p->broadcast_plmn_index[i] = broadcast_plmn_index[i];
 
@@ -193,6 +195,10 @@ void s1ap_eNB_handle_register_eNB(instance_t instance, s1ap_register_enb_req_t *
     new_instance->eNB_id           = s1ap_register_eNB->eNB_id;
     new_instance->cell_type        = s1ap_register_eNB->cell_type;
     new_instance->tac              = s1ap_register_eNB->tac;
+    
+    memcpy(&new_instance->eNB_s1_ip,
+	   &s1ap_register_eNB->enb_ip_address,
+	   sizeof(s1ap_register_eNB->enb_ip_address));
 
     for (int i = 0; i < s1ap_register_eNB->num_plmn; i++) {
       new_instance->mcc[i]              = s1ap_register_eNB->mcc[i];
diff --git a/openair3/S1AP/s1ap_eNB_defs.h b/openair3/S1AP/s1ap_eNB_defs.h
index 5bffa0df0cb4c123bef19d44b978cfca7738c8fe..d4bc6cfc0fa13144243670de0e15acb634cd9b58 100644
--- a/openair3/S1AP/s1ap_eNB_defs.h
+++ b/openair3/S1AP/s1ap_eNB_defs.h
@@ -124,6 +124,9 @@ typedef struct s1ap_eNB_mme_data_s {
   /* This is the optional name provided by the MME */
   char *mme_name;
 
+  /* MME S1AP IP address */
+  net_ip_address_t mme_s1_ip;
+
   /* List of served GUMMEI per MME. There is one GUMMEI per RAT with a max
    * number of 8 RATs but in our case only one is used. The LTE related pool
    * configuration is included on the first place in the list.
@@ -200,6 +203,9 @@ typedef struct s1ap_eNB_instance_s {
   /* Tracking area code */
   uint16_t tac;
 
+  /* eNB S1AP IP address */
+  net_ip_address_t eNB_s1_ip;
+  
   /* Mobile Country Code
    * Mobile Network Code
    */
diff --git a/openair3/S1AP/s1ap_eNB_nas_procedures.c b/openair3/S1AP/s1ap_eNB_nas_procedures.c
index b87bd7144a73e6de6a9f175a43c342c12d4e9ecf..d271ddf6bad6c884daacc3874f30bf9e03b02229 100644
--- a/openair3/S1AP/s1ap_eNB_nas_procedures.c
+++ b/openair3/S1AP/s1ap_eNB_nas_procedures.c
@@ -1468,7 +1468,7 @@ int s1ap_eNB_path_switch_req(instance_t instance,
       break;
     }
   } while(1);
-
+  
   ue_context_p->mme_ue_s1ap_id = path_switch_req_p->mme_ue_s1ap_id;
 
   /* Prepare the S1AP message to encode */