From 02d09453061f5dea6d94712708bdc35f73c565c0 Mon Sep 17 00:00:00 2001
From: Robert Schmidt <robert.schmidt@eurecom.fr>
Date: Wed, 4 Jul 2018 16:45:32 +0200
Subject: [PATCH] Add NNSF by selected PLMN Identity

---
 openair3/S1AP/s1ap_eNB_nas_procedures.c | 13 ++++-
 openair3/S1AP/s1ap_eNB_nnsf.c           | 70 +++++++++++++++++++++++++
 openair3/S1AP/s1ap_eNB_nnsf.h           |  5 ++
 3 files changed, 86 insertions(+), 2 deletions(-)

diff --git a/openair3/S1AP/s1ap_eNB_nas_procedures.c b/openair3/S1AP/s1ap_eNB_nas_procedures.c
index 91d3da4745..750bcd02fc 100644
--- a/openair3/S1AP/s1ap_eNB_nas_procedures.c
+++ b/openair3/S1AP/s1ap_eNB_nas_procedures.c
@@ -94,10 +94,19 @@ int s1ap_eNB_handle_nas_first_req(
     }
   }
 
+  if (mme_desc_p == NULL) {
+    /* Select MME based on the selected PLMN identity, received through RRC
+     * Connection Setup Complete */
+    mme_desc_p = s1ap_eNB_nnsf_select_mme_by_plmn_id(
+                   instance_p,
+                   s1ap_nas_first_req_p->establishment_cause,
+                   s1ap_nas_first_req_p->selected_plmn_identity);
+  }
+
   if (mme_desc_p == NULL) {
     /*
-     * If no MME corresponds to the GUMMEI or the s-TMSI, selects the MME with the
-     * highest capacity.
+     * If no MME corresponds to the GUMMEI, the s-TMSI, or the selected PLMN
+     * identity, selects the MME with the highest capacity.
      */
     mme_desc_p = s1ap_eNB_nnsf_select_mme(
                    instance_p,
diff --git a/openair3/S1AP/s1ap_eNB_nnsf.c b/openair3/S1AP/s1ap_eNB_nnsf.c
index 0d5519149c..63dc92b5e2 100644
--- a/openair3/S1AP/s1ap_eNB_nnsf.c
+++ b/openair3/S1AP/s1ap_eNB_nnsf.c
@@ -89,6 +89,76 @@ s1ap_eNB_nnsf_select_mme(s1ap_eNB_instance_t       *instance_p,
   return mme_highest_capacity_p;
 }
 
+struct s1ap_eNB_mme_data_s *
+s1ap_eNB_nnsf_select_mme_by_plmn_id(s1ap_eNB_instance_t       *instance_p,
+                                    rrc_establishment_cause_t  cause,
+                                    int                        selected_plmn_identity)
+{
+  struct s1ap_eNB_mme_data_s *mme_data_p = NULL;
+  struct s1ap_eNB_mme_data_s *mme_highest_capacity_p = NULL;
+  uint8_t current_capacity = 0;
+
+  RB_FOREACH(mme_data_p, s1ap_mme_map, &instance_p->s1ap_mme_head) {
+    struct served_gummei_s *gummei_p = NULL;
+    struct plmn_identity_s *served_plmn_p = NULL;
+    if (mme_data_p->state != S1AP_ENB_STATE_CONNECTED) {
+      /* The association between MME and eNB is not ready for the moment,
+       * go to the next known MME.
+       */
+      if (mme_data_p->state == S1AP_ENB_OVERLOAD) {
+        /* MME is overloaded. We have to check the RRC establishment
+         * cause and take decision to the select this MME depending on
+         * the overload state.
+         */
+        if ((cause == RRC_CAUSE_MO_DATA)
+            && (mme_data_p->overload_state == S1AP_OVERLOAD_REJECT_MO_DATA)) {
+          continue;
+        }
+
+        if ((mme_data_p->overload_state == S1AP_OVERLOAD_REJECT_ALL_SIGNALLING)
+            && ((cause == RRC_CAUSE_MO_SIGNALLING) || (cause == RRC_CAUSE_MO_DATA))) {
+          continue;
+        }
+
+        if ((mme_data_p->overload_state == S1AP_OVERLOAD_ONLY_EMERGENCY_AND_MT)
+            && ((cause == RRC_CAUSE_MO_SIGNALLING) || (cause == RRC_CAUSE_MO_DATA)
+                || (cause == RRC_CAUSE_HIGH_PRIO_ACCESS))) {
+          continue;
+        }
+
+        /* At this point, the RRC establishment can be handled by the MME
+         * even if it is in overload state.
+         */
+      } else {
+        /* The MME is not overloaded, association is simply not ready. */
+        continue;
+      }
+    }
+
+    /* Looking for served GUMMEI PLMN Identity selected matching the one provided by the UE */
+    STAILQ_FOREACH(gummei_p, &mme_data_p->served_gummei, next) {
+      STAILQ_FOREACH(served_plmn_p, &gummei_p->served_plmns, next) {
+        if ((served_plmn_p->mcc == instance_p->mcc[selected_plmn_identity]) &&
+            (served_plmn_p->mnc == instance_p->mnc[selected_plmn_identity])) {
+          break;
+        }
+      }
+      /* if found, we can stop the outer loop, too */
+      if (served_plmn_p) break;
+    }
+    /* if we didn't find such a served PLMN, go on with the next MME */
+    if (!served_plmn_p) continue;
+
+    if (current_capacity < mme_data_p->relative_mme_capacity) {
+      /* We find a better MME, keep a reference to it */
+      current_capacity = mme_data_p->relative_mme_capacity;
+      mme_highest_capacity_p = mme_data_p;
+    }
+  }
+
+  return mme_highest_capacity_p;
+}
+
 struct s1ap_eNB_mme_data_s *
 s1ap_eNB_nnsf_select_mme_by_mme_code(s1ap_eNB_instance_t       *instance_p,
                                      rrc_establishment_cause_t  cause,
diff --git a/openair3/S1AP/s1ap_eNB_nnsf.h b/openair3/S1AP/s1ap_eNB_nnsf.h
index 5a3d44416f..706f3ada6f 100644
--- a/openair3/S1AP/s1ap_eNB_nnsf.h
+++ b/openair3/S1AP/s1ap_eNB_nnsf.h
@@ -26,6 +26,11 @@ struct s1ap_eNB_mme_data_s *
 s1ap_eNB_nnsf_select_mme(s1ap_eNB_instance_t       *instance_p,
                          rrc_establishment_cause_t  cause);
 
+struct s1ap_eNB_mme_data_s *
+s1ap_eNB_nnsf_select_mme_by_plmn_id(s1ap_eNB_instance_t       *instance_p,
+                                    rrc_establishment_cause_t  cause,
+                                    int                        selected_plmn_identity);
+
 struct s1ap_eNB_mme_data_s*
 s1ap_eNB_nnsf_select_mme_by_mme_code(s1ap_eNB_instance_t       *instance_p,
                                      rrc_establishment_cause_t  cause,
-- 
GitLab