enb_config.c 22.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
/*******************************************************************************

 Eurecom OpenAirInterface
 Copyright(c) 1999 - 2014 Eurecom

 This program is free software; you can redistribute it and/or modify it
 under the terms and conditions of the GNU General Public License,
 version 2, as published by the Free Software Foundation.

 This program is distributed in the hope it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.

 The full GNU General Public License is included in this distribution in
 the file called "COPYING".

 Contact Information
 Openair Admin: openair_admin@eurecom.fr
 Openair Tech : openair_tech@eurecom.fr
 Forums       : http://forums.eurecom.fr/openairinterface
 Address      : EURECOM, Campus SophiaTech, 450 Route des Chappes
 06410 Biot FRANCE

 *******************************************************************************/

#include <string.h>
#include <libconfig.h>

#include "assertions.h"
#include "enb_config.h"
winckel's avatar
winckel committed
36 37 38 39
#if defined(OAI_EMU)
# include "OCG.h"
# include "OCG_extern.h"
#endif
40 41 42 43 44 45 46 47 48 49
#if defined(ENABLE_ITTI)
# include "intertask_interface.h"
# if defined(ENABLE_USE_MME)
#   include "s1ap_eNB.h"
#   include "sctp_eNB_task.h"
# endif
#endif

#include "LAYER2/MAC/extern.h"

50 51 52 53 54 55 56 57 58 59 60 61 62 63
#define ENB_CONFIG_STRING_ACTIVE_ENBS                   "Active_eNBs"

#define ENB_CONFIG_STRING_ENB_LIST                      "eNBs"
#define ENB_CONFIG_STRING_ENB_ID                        "eNB_ID"
#define ENB_CONFIG_STRING_CELL_TYPE                     "cell_type"
#define ENB_CONFIG_STRING_ENB_NAME                      "eNB_name"

#define ENB_CONFIG_STRING_TRACKING_AREA_CODE            "tracking_area_code"
#define ENB_CONFIG_STRING_MOBILE_COUNTRY_CODE           "mobile_country_code"
#define ENB_CONFIG_STRING_MOBILE_NETWORK_CODE           "mobile_network_code"

#define ENB_CONFIG_STRING_DEFAULT_PAGING_DRX            "default_paging_drx"

#define ENB_CONFIG_STRING_FRAME_TYPE                    "frame_type"
64 65
#define ENB_CONFIG_STRING_TDD_CONFIG                    "tdd_config"
#define ENB_CONFIG_STRING_TDD_CONFIG_S                  "tdd_config_s"
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
#define ENB_CONFIG_STRING_PREFIX_TYPE                   "prefix_type"
#define ENB_CONFIG_STRING_EUTRA_BAND                    "eutra_band"
#define ENB_CONFIG_STRING_DOWNLINK_FREQUENCY            "downlink_frequency"
#define ENB_CONFIG_STRING_UPLINK_FREQUENCY_OFFSET       "uplink_frequency_offset"

#define ENB_CONFIG_STRING_MME_IP_ADDRESS                "mme_ip_address"
#define ENB_CONFIG_STRING_MME_IPV4_ADDRESS              "ipv4"
#define ENB_CONFIG_STRING_MME_IPV6_ADDRESS              "ipv6"
#define ENB_CONFIG_STRING_MME_IP_ADDRESS_ACTIVE         "active"
#define ENB_CONFIG_STRING_MME_IP_ADDRESS_PREFERENCE     "preference"

#define ENB_CONFIG_STRING_NETWORK_INTERFACES_CONFIG     "NETWORK_INTERFACES"
#define ENB_CONFIG_STRING_ENB_INTERFACE_NAME_FOR_S1_MME "ENB_INTERFACE_NAME_FOR_S1_MME"
#define ENB_CONFIG_STRING_ENB_IPV4_ADDRESS_FOR_S1_MME   "ENB_IPV4_ADDRESS_FOR_S1_MME"
#define ENB_CONFIG_STRING_ENB_INTERFACE_NAME_FOR_S1U    "ENB_INTERFACE_NAME_FOR_S1U"
#define ENB_CONFIG_STRING_ENB_IPV4_ADDR_FOR_S1U         "ENB_IPV4_ADDRESS_FOR_S1U"

#define KHz (1000UL)
#define MHz (1000 * KHz)

typedef struct eutra_band_s
{
    int16_t             band;
    uint32_t            ul_min;
    uint32_t            ul_max;
    uint32_t            dl_min;
    uint32_t            dl_max;
    lte_frame_type_t    frame_type;
} eutra_band_t;

static const eutra_band_t eutra_bands[] =
{
    { 1, 1920    * MHz, 1980    * MHz, 2110    * MHz, 2170    * MHz, FDD},
    { 2, 1850    * MHz, 1910    * MHz, 1930    * MHz, 1990    * MHz, FDD},
    { 3, 1710    * MHz, 1785    * MHz, 1805    * MHz, 1880    * MHz, FDD},
    { 4, 1710    * MHz, 1755    * MHz, 2110    * MHz, 2155    * MHz, FDD},
    { 5,  824    * MHz,  849    * MHz,  869    * MHz,  894    * MHz, FDD},
    { 6,  830    * MHz,  840    * MHz,  875    * MHz,  885    * MHz, FDD},
    { 7, 2500    * MHz, 2570    * MHz, 2620    * MHz, 2690    * MHz, FDD},
    { 8,  880    * MHz,  915    * MHz,  925    * MHz,  960    * MHz, FDD},
    { 9, 1749900 * KHz, 1784900 * KHz, 1844900 * KHz, 1879900 * KHz, FDD},
    {10, 1710    * MHz, 1770    * MHz, 2110    * MHz, 2170    * MHz, FDD},
    {11, 1427900 * KHz, 1452900 * KHz, 1475900 * KHz, 1500900 * KHz, FDD},
    {12,  698    * MHz,  716    * MHz,  728    * MHz,  746    * MHz, FDD},
    {13,  777    * MHz,  787    * MHz,  746    * MHz,  756    * MHz, FDD},
    {14,  788    * MHz,  798    * MHz,  758    * MHz,  768    * MHz, FDD},

    {17,  704    * MHz,  716    * MHz,  734    * MHz,  746    * MHz, FDD},

    {33, 1900    * MHz, 1920    * MHz, 1900    * MHz, 1920    * MHz, TDD},
    {34, 2010    * MHz, 2025    * MHz, 2010    * MHz, 2025    * MHz, TDD},
    {35, 1850    * MHz, 1910    * MHz, 1850    * MHz, 1910    * MHz, TDD},
    {36, 1930    * MHz, 1990    * MHz, 1930    * MHz, 1990    * MHz, TDD},
    {37, 1910    * MHz, 1930    * MHz, 1910    * MHz, 1930    * MHz, TDD},
    {38, 2570    * MHz, 2620    * MHz, 2570    * MHz, 2630    * MHz, TDD},
    {39, 1880    * MHz, 1920    * MHz, 1880    * MHz, 1920    * MHz, TDD},
    {40, 2300    * MHz, 2400    * MHz, 2300    * MHz, 2400    * MHz, TDD},
};

125
static Enb_properties_array_t enb_properties;
126

127 128 129 130 131 132 133
static int enb_check_band_frequencies(char* lib_config_file_name_pP,
                                      int enb_properties_index,
                                      int16_t band,
                                      uint32_t downlink_frequency,
                                      int32_t uplink_frequency_offset,
                                      lte_frame_type_t frame_type)
{
134
    int errors = 0;
135 136 137 138 139 140 141 142 143 144 145

    if (band > 0)
    {
        int band_index;

        for (band_index = 0; band_index < sizeof (eutra_bands) / sizeof (eutra_bands[0]); band_index++)
        {
            if (band == eutra_bands[band_index].band)
            {
                uint32_t uplink_frequency = downlink_frequency + uplink_frequency_offset;

146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
                AssertError (eutra_bands[band_index].dl_min < downlink_frequency, errors ++,
                             "Failed to parse eNB configuration file %s, enb %d downlink frequency %u too low (%u) for band %d!",
                             lib_config_file_name_pP, enb_properties_index, downlink_frequency, eutra_bands[band_index].dl_min, band);
                AssertError (downlink_frequency < eutra_bands[band_index].dl_max, errors ++,
                             "Failed to parse eNB configuration file %s, enb %d downlink frequency %u too high (%u) for band %d!",
                             lib_config_file_name_pP, enb_properties_index, downlink_frequency, eutra_bands[band_index].dl_max, band);

                AssertError (eutra_bands[band_index].ul_min < uplink_frequency, errors ++,
                             "Failed to parse eNB configuration file %s, enb %d uplink frequency %u too low (%u) for band %d!",
                             lib_config_file_name_pP, enb_properties_index, uplink_frequency, eutra_bands[band_index].ul_min, band);
                AssertError (uplink_frequency < eutra_bands[band_index].ul_max, errors ++,
                             "Failed to parse eNB configuration file %s, enb %d uplink frequency %u too high (%u) for band %d!",
                             lib_config_file_name_pP, enb_properties_index, uplink_frequency, eutra_bands[band_index].ul_max, band);

                AssertError (eutra_bands[band_index].frame_type == frame_type, errors ++,
                             "Failed to parse eNB configuration file %s, enb %d invalid frame type (%d/%d) for band %d!",
                             lib_config_file_name_pP, enb_properties_index, eutra_bands[band_index].frame_type, frame_type, band);
163 164 165
            }
        }
    }
166
    return errors;
167 168
}

169
const Enb_properties_array_t *enb_config_init(char* lib_config_file_name_pP) {
170 171 172 173 174
  config_t          cfg;
  config_setting_t *setting;
  config_setting_t *setting_mme_addresses;
  config_setting_t *setting_mme_address;
  config_setting_t *setting_enb;
175 176 177 178 179 180
  int               num_enb_properties = 0;
  int               enb_properties_index = 0;
  int               num_enbs;
  int               num_mme_address;
  int               i;
  int               j;
181
  int               parse_errors = 0;
182 183 184 185 186 187 188
  long int          enb_id;
  const char*       cell_type;
  long int          tac;
  const char*       enb_name;
  long int          mcc;
  long int          mnc;
  const char*       default_drx;
189
  const char*       frame_type;
190 191
  long int          tdd_config;
  long int          tdd_config_s;
192
  const char*       prefix_type;
193 194 195
  long int          eutra_band;
  double            downlink_frequency;
  double            uplink_frequency_offset;
196 197 198 199
  char*             ipv4;
  char*             ipv6;
  char*             active;
  char*             preference;
200
  const char*       active_enb[MAX_ENB];
201

202
  memset((char*) (enb_properties.properties), 0 , MAX_ENB * sizeof(Enb_properties_t *));
203
  memset((char*)active_enb,     0 , MAX_ENB * sizeof(char*));
204 205 206

  config_init(&cfg);

winckel's avatar
winckel committed
207 208 209 210 211 212
  if(lib_config_file_name_pP != NULL)
  {
      /* Read the file. If there is an error, report it and exit. */
      if(! config_read_file(&cfg, lib_config_file_name_pP))
      {
          config_destroy(&cfg);
213
          AssertFatal (0, "Failed to parse eNB configuration file %s!\n", lib_config_file_name_pP);
winckel's avatar
winckel committed
214 215 216
      }
  }
  else
217 218
  {
      config_destroy(&cfg);
winckel's avatar
winckel committed
219
      AssertFatal (0, "No eNB configuration file provided!\n");
220 221 222 223 224 225 226 227 228 229 230 231 232 233
  }

  // Get list of active eNBs, (only these will be configured)
  setting = config_lookup(&cfg, ENB_CONFIG_STRING_ACTIVE_ENBS);
  if(setting != NULL)
  {
      num_enbs = config_setting_length(setting);
      for (i = 0; i < num_enbs; i++) {
          setting_enb   = config_setting_get_elem(setting, i);
          active_enb[i] = config_setting_get_string (setting_enb);
          AssertFatal (active_enb[i] != NULL,
                       "Failed to parse config file %s, %uth attribute %s \n",
                       lib_config_file_name_pP, i, ENB_CONFIG_STRING_ACTIVE_ENBS);
          active_enb[i] = strdup(active_enb[i]);
234
          num_enb_properties += 1;
235 236 237 238 239 240 241 242
      }
  }

  /* Output a list of all eNBs. */
  setting = config_lookup(&cfg, ENB_CONFIG_STRING_ENB_LIST);
  if(setting != NULL)
  {
      enb_properties_index = 0;
243
      parse_errors      = 0;
244 245 246
      num_enbs = config_setting_length(setting);
      for (i = 0; i < num_enbs; i++) {
          setting_enb = config_setting_get_elem(setting, i);
247 248

          if(! config_setting_lookup_int(setting_enb, ENB_CONFIG_STRING_ENB_ID, &enb_id)) {
249 250
              /* Calculate a default eNB ID */
# if defined(ENABLE_USE_MME)
251 252 253 254
              uint32_t hash;

              hash = s1ap_generate_eNB_id ();
              enb_id = i + (hash & 0xFFFF8);
255 256 257
# else
              enb_id = i;
# endif
258 259 260
          }

          if(  !(       config_setting_lookup_string(setting_enb, ENB_CONFIG_STRING_CELL_TYPE,           &cell_type)
261 262 263 264 265 266 267
                     && config_setting_lookup_string(setting_enb, ENB_CONFIG_STRING_ENB_NAME,            &enb_name)
                     && config_setting_lookup_int   (setting_enb, ENB_CONFIG_STRING_TRACKING_AREA_CODE,  &tac)
                     && config_setting_lookup_int   (setting_enb, ENB_CONFIG_STRING_MOBILE_COUNTRY_CODE, &mcc)
                     && config_setting_lookup_int   (setting_enb, ENB_CONFIG_STRING_MOBILE_NETWORK_CODE, &mnc)
                     && config_setting_lookup_string(setting_enb, ENB_CONFIG_STRING_DEFAULT_PAGING_DRX,  &default_drx)
                )
            ) {
268
              AssertError (0, parse_errors ++,
winckel's avatar
winckel committed
269
                      "Failed to parse eNB configuration file %s, %u th enb\n",
270 271 272
                      lib_config_file_name_pP, i);
          }
          // search if in active list
273
          for (j=0; j < num_enb_properties; j++) {
274
              if (strcmp(active_enb[j], enb_name) == 0) {
275
                  enb_properties.properties[enb_properties_index] = calloc(1, sizeof(Enb_properties_t));
276

277
                  enb_properties.properties[enb_properties_index]->eNB_id   = enb_id;
278
                  if (strcmp(cell_type, "CELL_MACRO_ENB") == 0) {
279
                      enb_properties.properties[enb_properties_index]->cell_type = CELL_MACRO_ENB;
280
                  } else  if (strcmp(cell_type, "CELL_HOME_ENB") == 0) {
281
                      enb_properties.properties[enb_properties_index]->cell_type = CELL_HOME_ENB;
282
                  } else {
283
                      AssertError (0, parse_errors ++,
284 285
                              "Failed to parse eNB configuration file %s, enb %d unknown value \"%s\" for cell_type choice: CELL_MACRO_ENB or CELL_HOME_ENB !\n",
                              lib_config_file_name_pP, i, cell_type);
286
                  }
287 288 289 290
                  enb_properties.properties[enb_properties_index]->eNB_name = strdup(enb_name);
                  enb_properties.properties[enb_properties_index]->tac      = (uint16_t)tac;
                  enb_properties.properties[enb_properties_index]->mcc      = (uint16_t)mcc;
                  enb_properties.properties[enb_properties_index]->mnc      = (uint16_t)mnc;
291 292

                  if (strcmp(default_drx, "PAGING_DRX_32") == 0) {
293
                      enb_properties.properties[enb_properties_index]->default_drx = PAGING_DRX_32;
294
                  } else  if (strcmp(default_drx, "PAGING_DRX_64") == 0) {
295
                      enb_properties.properties[enb_properties_index]->default_drx = PAGING_DRX_64;
296
                  } else  if (strcmp(default_drx, "PAGING_DRX_128") == 0) {
297
                      enb_properties.properties[enb_properties_index]->default_drx = PAGING_DRX_128;
298
                  } else  if (strcmp(default_drx, "PAGING_DRX_256") == 0) {
299
                      enb_properties.properties[enb_properties_index]->default_drx = PAGING_DRX_256;
300
                  } else {
301
                      AssertError (0, parse_errors ++,
302 303 304 305 306 307 308
                              "Failed to parse eNB configuration file %s, enb %d unknown value \"%s\" for default_drx choice: PAGING_DRX_32..PAGING_DRX_256 !\n",
                              lib_config_file_name_pP, i, default_drx);
                  }

                  // Parse optional physical parameters
                  if(config_setting_lookup_string(setting_enb, ENB_CONFIG_STRING_FRAME_TYPE, &frame_type)) {
                      if (strcmp(frame_type, "FDD") == 0) {
309
                          enb_properties.properties[enb_properties_index]->frame_type = FDD;
310
                      } else  if (strcmp(frame_type, "TDD") == 0) {
311
                          enb_properties.properties[enb_properties_index]->frame_type = TDD;
312
                      } else {
313
                          AssertError (0, parse_errors ++,
314 315 316 317
                                  "Failed to parse eNB configuration file %s, enb %d unknown value \"%s\" for frame_type choice: FDD or TDD !\n",
                                  lib_config_file_name_pP, i, frame_type);
                      }
                  } else {
318
                      enb_properties.properties[enb_properties_index]->frame_type = FDD; // Default frame type
319 320
                  }

321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
                  if(config_setting_lookup_int(setting_enb, ENB_CONFIG_STRING_TDD_CONFIG, &tdd_config)) {
                      enb_properties.properties[enb_properties_index]->tdd_config = tdd_config;
                      AssertError (tdd_config <= TDD_Config__subframeAssignment_sa6, parse_errors ++,
                              "Failed to parse eNB configuration file %s, enb %d illegal tdd_config %ld (should be 0-%d)!",
                              lib_config_file_name_pP, i, tdd_config, TDD_Config__subframeAssignment_sa6);
                  } else {
                      enb_properties.properties[enb_properties_index]->tdd_config = 3; // Default TDD sub-frame configuration
                  }

                  if(config_setting_lookup_int(setting_enb, ENB_CONFIG_STRING_TDD_CONFIG_S, &tdd_config_s)) {
                      enb_properties.properties[enb_properties_index]->tdd_config_s = tdd_config_s;
                      AssertError (tdd_config_s <= TDD_Config__specialSubframePatterns_ssp8, parse_errors ++,
                              "Failed to parse eNB configuration file %s, enb %d illegal tdd_config_s %ld (should be 0-%d)!",
                              lib_config_file_name_pP, i, tdd_config_s, TDD_Config__specialSubframePatterns_ssp8);
                  } else {
                      enb_properties.properties[enb_properties_index]->tdd_config_s = 0; // Default TDD S-sub-frame configuration
                  }

339 340
                  if(config_setting_lookup_string(setting_enb, ENB_CONFIG_STRING_PREFIX_TYPE, &prefix_type)) {
                      if (strcmp(prefix_type, "NORMAL") == 0) {
341
                          enb_properties.properties[enb_properties_index]->prefix_type = NORMAL;
342
                      } else  if (strcmp(prefix_type, "EXTENDED") == 0) {
343
                          enb_properties.properties[enb_properties_index]->prefix_type = EXTENDED;
344
                      } else {
345
                          AssertError (0, parse_errors ++,
346 347 348 349
                                  "Failed to parse eNB configuration file %s, enb %d unknown value \"%s\" for prefix_type choice: NORMAL or EXTENDED !\n",
                                  lib_config_file_name_pP, i, prefix_type);
                      }
                  } else {
350
                      enb_properties.properties[enb_properties_index]->prefix_type = NORMAL; // Default prefix type
351 352
                  }

353 354 355 356 357 358 359
                  if(config_setting_lookup_int(setting_enb, ENB_CONFIG_STRING_EUTRA_BAND, &eutra_band)) {
                      enb_properties.properties[enb_properties_index]->eutra_band = eutra_band;
                  } else {
                      enb_properties.properties[enb_properties_index]->eutra_band = 7; // Default band
                  }

                  if(config_setting_lookup_float(setting_enb, ENB_CONFIG_STRING_DOWNLINK_FREQUENCY, &downlink_frequency)) {
360
                      enb_properties.properties[enb_properties_index]->downlink_frequency = downlink_frequency;
361
                  } else {
362
                      enb_properties.properties[enb_properties_index]->downlink_frequency = 2680000000UL; // Default downlink frequency
363 364
                  }

365
                  if(config_setting_lookup_float(setting_enb, ENB_CONFIG_STRING_UPLINK_FREQUENCY_OFFSET, &uplink_frequency_offset)) {
366
                      enb_properties.properties[enb_properties_index]->uplink_frequency_offset = uplink_frequency_offset;
367
                  } else {
368 369 370 371 372 373
                      // Default uplink frequency offset
                      if (enb_properties.properties[enb_properties_index]->frame_type == FDD) {
                          enb_properties.properties[enb_properties_index]->uplink_frequency_offset = -120000000;
                      } else {
                          enb_properties.properties[enb_properties_index]->uplink_frequency_offset = 0;
                      }
374 375
                  }

376 377 378 379 380 381
                  parse_errors += enb_check_band_frequencies(lib_config_file_name_pP,
                                                             enb_properties_index,
                                                             enb_properties.properties[enb_properties_index]->eutra_band,
                                                             enb_properties.properties[enb_properties_index]->downlink_frequency,
                                                             enb_properties.properties[enb_properties_index]->uplink_frequency_offset,
                                                             enb_properties.properties[enb_properties_index]->frame_type);
382

383 384
                  setting_mme_addresses = config_setting_get_member (setting_enb, ENB_CONFIG_STRING_MME_IP_ADDRESS);
                  num_mme_address     = config_setting_length(setting_mme_addresses);
385
                  enb_properties.properties[enb_properties_index]->nb_mme = 0;
386 387 388 389 390 391 392 393 394
                  for (j = 0; j < num_mme_address; j++) {
                      setting_mme_address = config_setting_get_elem(setting_mme_addresses, j);
                      if(  !(
                              config_setting_lookup_string(setting_mme_address, ENB_CONFIG_STRING_MME_IPV4_ADDRESS, (const char **)&ipv4)
                              && config_setting_lookup_string(setting_mme_address, ENB_CONFIG_STRING_MME_IPV6_ADDRESS, (const char **)&ipv6)
                              && config_setting_lookup_string(setting_mme_address, ENB_CONFIG_STRING_MME_IP_ADDRESS_ACTIVE, (const char **)&active)
                              && config_setting_lookup_string(setting_mme_address, ENB_CONFIG_STRING_MME_IP_ADDRESS_PREFERENCE, (const char **)&preference)
                            )
                        ) {
395
                          AssertError (0, parse_errors ++,
396
                                  "Failed to parse eNB configuration file %s, %u th enb %u th mme address !\n",
397 398
                                  lib_config_file_name_pP, i, j);
                      }
399
                      enb_properties.properties[enb_properties_index]->nb_mme += 1;
400

401 402
                      enb_properties.properties[enb_properties_index]->mme_ip_address[j].ipv4_address = strdup(ipv4);
                      enb_properties.properties[enb_properties_index]->mme_ip_address[j].ipv6_address = strdup(ipv6);
403
                      if (strcmp(active, "yes") == 0) {
404
                          enb_properties.properties[enb_properties_index]->mme_ip_address[j].active = 1;
winckel's avatar
winckel committed
405 406 407
#if defined(ENABLE_USE_MME)
                          EPC_MODE_ENABLED = 1;
#endif
408 409 410
                      } // else { (calloc)

                      if (strcmp(preference, "ipv4") == 0) {
411
                          enb_properties.properties[enb_properties_index]->mme_ip_address[j].ipv4 = 1;
412
                      } else if (strcmp(preference, "ipv6") == 0) {
413
                          enb_properties.properties[enb_properties_index]->mme_ip_address[j].ipv6 = 1;
414
                      } else if (strcmp(preference, "no") == 0) {
415 416
                          enb_properties.properties[enb_properties_index]->mme_ip_address[j].ipv4 = 1;
                          enb_properties.properties[enb_properties_index]->mme_ip_address[j].ipv6 = 1;
417 418 419 420 421 422 423 424
                      }
                  }
                  enb_properties_index += 1;
                  break;
              }
          }
      }
  }
425 426
  enb_properties.number = num_enb_properties;

427
  AssertError (enb_properties_index == num_enb_properties, parse_errors ++,
428
          "Failed to parse eNB configuration file %s, mismatch between %u active eNBs and %u corresponding defined eNBs !\n",
429
          lib_config_file_name_pP, num_enb_properties, enb_properties_index);
430

431
  AssertFatal (parse_errors == 0,
432
               "Failed to parse eNB configuration file %s, found %d error%s !\n",
433
               lib_config_file_name_pP, parse_errors, parse_errors > 1 ? "s" : "");
434 435 436 437 438 439

  return &enb_properties;
}

const Enb_properties_array_t *enb_config_get(void) {
    return &enb_properties;
440
}