/*******************************************************************************
    OpenAirInterface
    Copyright(c) 1999 - 2014 Eurecom

    OpenAirInterface is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.


    OpenAirInterface is distributed in the hope that 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 OpenAirInterface.The full GNU General Public License is
   included in this distribution in the file called "COPYING". If not,
   see <http://www.gnu.org/licenses/>.

  Contact Information
  OpenAirInterface Admin: openair_admin@eurecom.fr
  OpenAirInterface Tech : openair_tech@eurecom.fr
  OpenAirInterface Dev  : openair4g-devel@eurecom.fr

  Address      : Eurecom, Compus SophiaTech 450, route des chappes, 06451 Biot, France.

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

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <libconfig.h>

#include <arpa/inet.h> /* To provide inet_addr */

#include "assertions.h"
#include "mme_config.h"
#include "spgw_config.h"
#include "intertask_interface_conf.h"

mme_config_t mme_config;

static
void mme_config_init(mme_config_t *mme_config_p)
{
    memset(mme_config_p, 0, sizeof(mme_config_t));

    pthread_rwlock_init(&mme_config_p->rw_lock, NULL);

    mme_config_p->verbosity_level            = 0;
    mme_config_p->config_file                = NULL;
    mme_config_p->max_eNBs                   = MAX_NUMBER_OF_ENB;
    mme_config_p->max_ues                    = MAX_NUMBER_OF_UE;

    mme_config_p->emergency_attach_supported     = 0;
    mme_config_p->unauthenticated_imsi_supported = 0;

    /* Timer configuration */
    mme_config_p->gtpv1u_config.port_number = GTPV1_U_PORT_NUMBER;
    mme_config_p->s1ap_config.port_number   = S1AP_PORT_NUMBER;
    /* IP configuration */
    mme_config_p->ipv4.sgw_ip_address_for_S1u_S12_S4_up     = inet_addr(DEFAULT_SGW_IP_ADDRESS_FOR_S1U_S12_S4_UP);

    mme_config_p->ipv4.mme_interface_name_for_S1_MME        = DEFAULT_MME_INTERFACE_NAME_FOR_S1_MME;
    mme_config_p->ipv4.mme_ip_address_for_S1_MME            = inet_addr(DEFAULT_MME_IP_ADDRESS_FOR_S1_MME);

    mme_config_p->ipv4.mme_interface_name_for_S11           = DEFAULT_MME_INTERFACE_NAME_FOR_S11;
    mme_config_p->ipv4.mme_ip_address_for_S11               = inet_addr(DEFAULT_MME_IP_ADDRESS_FOR_S11);

    mme_config_p->ipv4.sgw_ip_address_for_S11               = inet_addr(DEFAULT_SGW_IP_ADDRESS_FOR_S11);

    mme_config_p->s6a_config.conf_file    = S6A_CONF_FILE;

    mme_config_p->itti_config.queue_size  = ITTI_QUEUE_MAX_ELEMENTS;
    mme_config_p->itti_config.log_file    = NULL;

    mme_config_p->sctp_config.in_streams  = SCTP_IN_STREAMS;
    mme_config_p->sctp_config.out_streams = SCTP_OUT_STREAMS;

    mme_config_p->relative_capacity = RELATIVE_CAPACITY;

    mme_config_p->mme_statistic_timer = MME_STATISTIC_TIMER_S;

    mme_config_p->gummei.nb_mme_gid = 1;
    mme_config_p->gummei.mme_gid    = calloc(1, sizeof(*mme_config_p->gummei.mme_gid));
    mme_config_p->gummei.mme_gid[0] = MMEGID;
    mme_config_p->gummei.nb_mmec    = 1;
    mme_config_p->gummei.mmec       = calloc(1, sizeof(*mme_config_p->gummei.mmec));
    mme_config_p->gummei.mmec[0]    = MMEC;

    /* Set the TAI */
    mme_config_p->gummei.nb_plmns   = 1;
    mme_config_p->gummei.plmn_mcc   = calloc(1, sizeof(*mme_config_p->gummei.plmn_mcc));
    mme_config_p->gummei.plmn_mnc   = calloc(1, sizeof(*mme_config_p->gummei.plmn_mnc));
    mme_config_p->gummei.plmn_tac   = calloc(1, sizeof(*mme_config_p->gummei.plmn_tac));

    mme_config_p->gummei.plmn_mcc[0] = PLMN_MCC;
    mme_config_p->gummei.plmn_mnc[0] = PLMN_MNC;
    mme_config_p->gummei.plmn_tac[0] = PLMN_TAC;

    mme_config_p->s1ap_config.outcome_drop_timer_sec = S1AP_OUTCOME_TIMER_DEFAULT;
}

static int config_parse_file(mme_config_t *mme_config_p)
{
    config_t          cfg;
    config_setting_t *setting_mme                      = NULL;
    config_setting_t *setting                          = NULL;
    config_setting_t *subsetting                       = NULL;
    config_setting_t *sub2setting                      = NULL;

    long int         alongint;
    int              i, num;
    char             *astring                          = NULL;
    char             *address                          = NULL;
    char             *cidr                             = NULL;

    const char*       tac                              = NULL;
    const char*       mcc                              = NULL;
    const char*       mnc                              = NULL;

    char             *sgw_ip_address_for_S1u_S12_S4_up = NULL;
    char             *mme_interface_name_for_S1_MME    = NULL;
    char             *mme_ip_address_for_S1_MME        = NULL;
    char             *mme_interface_name_for_S11       = NULL;
    char             *mme_ip_address_for_S11           = NULL;
    char             *sgw_ip_address_for_S11           = NULL;

    config_init(&cfg);

    if(mme_config_p->config_file != NULL)
    {
        /* Read the file. If there is an error, report it and exit. */
        if(! config_read_file(&cfg, mme_config_p->config_file))
        {
            fprintf(stdout, "ERROR: %s:%d - %s\n", mme_config_p->config_file, config_error_line(&cfg), config_error_text(&cfg));
            config_destroy(&cfg);
            AssertFatal (1 == 0, "Failed to parse MME configuration file %s!\n", mme_config_p->config_file);
        }
    }
    else
    {
        fprintf(stdout, "ERROR No MME configuration file provided!\n");
        config_destroy(&cfg);
        AssertFatal (0, "No MME configuration file provided!\n");
    }

    setting_mme = config_lookup(&cfg, MME_CONFIG_STRING_MME_CONFIG);
    if(setting_mme != NULL) {
        // GENERAL MME SETTINGS
        if(  (config_setting_lookup_string( setting_mme, MME_CONFIG_STRING_REALM, (const char **)&astring) )) {
            mme_config_p->realm = strdup(astring);
            mme_config_p->realm_length = strlen(mme_config_p->realm);
        }
        if(  (config_setting_lookup_int( setting_mme, MME_CONFIG_STRING_MAXENB, &alongint) )) {
            mme_config_p->max_eNBs = (uint32_t)alongint;
        }
        if(  (config_setting_lookup_int( setting_mme, MME_CONFIG_STRING_MAXUE, &alongint) )) {
            mme_config_p->max_ues = (uint32_t)alongint;
        }
        if(  (config_setting_lookup_int( setting_mme, MME_CONFIG_STRING_RELATIVE_CAPACITY, &alongint) )) {
            mme_config_p->relative_capacity = (uint8_t)alongint;
        }
        if(  (config_setting_lookup_int( setting_mme, MME_CONFIG_STRING_STATISTIC_TIMER, &alongint) )) {
            mme_config_p->mme_statistic_timer = (uint32_t)alongint;
        }
        if(  (config_setting_lookup_string( setting_mme, MME_CONFIG_STRING_EMERGENCY_ATTACH_SUPPORTED, (const char **)&astring) )) {
            if (strcasecmp(astring , "yes") == 0)
                mme_config_p->emergency_attach_supported = 1;
            else
                mme_config_p->emergency_attach_supported = 0;
        }
        if(  (config_setting_lookup_string( setting_mme, MME_CONFIG_STRING_UNAUTHENTICATED_IMSI_SUPPORTED, (const char **)&astring) )) {
            if (strcasecmp(astring , "yes") == 0)
                mme_config_p->unauthenticated_imsi_supported = 1;
            else
                mme_config_p->unauthenticated_imsi_supported = 0;
        }
        if(  (config_setting_lookup_string( setting_mme, MME_CONFIG_STRING_ASN1_VERBOSITY, (const char **)&astring) )) {
            if (strcasecmp(astring , MME_CONFIG_STRING_ASN1_VERBOSITY_NONE) == 0)
                mme_config_p->verbosity_level = 0;
            else if (strcasecmp(astring , MME_CONFIG_STRING_ASN1_VERBOSITY_ANNOYING) == 0)
                mme_config_p->verbosity_level = 2;
            else if (strcasecmp(astring , MME_CONFIG_STRING_ASN1_VERBOSITY_INFO) == 0)
                mme_config_p->verbosity_level = 1;
            else
                mme_config_p->verbosity_level = 0;
        }

        // ITTI SETTING
        setting = config_setting_get_member (setting_mme, MME_CONFIG_STRING_INTERTASK_INTERFACE_CONFIG);
        if (setting != NULL) {
            if(  (config_setting_lookup_int( setting, MME_CONFIG_STRING_INTERTASK_INTERFACE_QUEUE_SIZE, &alongint) )) {
                mme_config_p->itti_config.queue_size = (uint32_t)alongint;
            }
        }

        // S6A SETTING
        setting = config_setting_get_member (setting_mme, MME_CONFIG_STRING_S6A_CONFIG);
        if (setting != NULL) {
            if(  (config_setting_lookup_string( setting, MME_CONFIG_STRING_S6A_CONF_FILE_PATH, (const char **)&astring) )) {
                if (astring != NULL)
                    mme_config_p->s6a_config.conf_file = strdup(astring);
            }
        }

        // SCTP SETTING
        setting = config_setting_get_member (setting_mme, MME_CONFIG_STRING_SCTP_CONFIG);
        if (setting != NULL) {
            if(  (config_setting_lookup_int( setting, MME_CONFIG_STRING_SCTP_INSTREAMS, &alongint) )) {
                mme_config_p->sctp_config.in_streams = (uint16_t)alongint;
            }
            if(  (config_setting_lookup_int( setting, MME_CONFIG_STRING_SCTP_OUTSTREAMS, &alongint) )) {
                mme_config_p->sctp_config.out_streams = (uint16_t)alongint;
            }
        }

        // S1AP SETTING
        setting = config_setting_get_member (setting_mme, MME_CONFIG_STRING_S1AP_CONFIG);
        if (setting != NULL) {
            if(  (config_setting_lookup_int( setting, MME_CONFIG_STRING_S1AP_OUTCOME_TIMER, &alongint) )) {
                mme_config_p->s1ap_config.outcome_drop_timer_sec = (uint8_t)alongint;
            }
            if(  (config_setting_lookup_int( setting, MME_CONFIG_STRING_SCTP_OUTSTREAMS, &alongint) )) {
                mme_config_p->sctp_config.out_streams = (uint16_t)alongint;
            }
        }

        // GUMMEI SETTING
        setting = config_setting_get_member (setting_mme, MME_CONFIG_STRING_GUMMEI_CONFIG);
        if (setting != NULL) {
            subsetting = config_setting_get_member (setting, MME_CONFIG_STRING_MME_CODE);
            if (subsetting != NULL) {
                num     = config_setting_length(subsetting);
                if (mme_config_p->gummei.nb_mmec != num) {
                    if (mme_config_p->gummei.mmec != NULL) {
                        free(mme_config_p->gummei.mmec);
                    }
                    mme_config_p->gummei.mmec = calloc(num, sizeof(*mme_config_p->gummei.mmec));
                }
                mme_config_p->gummei.nb_mmec = num;
                for (i = 0; i < num; i++) {
                    mme_config_p->gummei.mmec[i] = config_setting_get_int_elem(subsetting, i);
                }
            }

            subsetting = config_setting_get_member (setting, MME_CONFIG_STRING_MME_GID);
            if (subsetting != NULL) {
                num     = config_setting_length(subsetting);
                if (mme_config_p->gummei.nb_mme_gid != num) {
                    if (mme_config_p->gummei.mme_gid != NULL) {
                        free(mme_config_p->gummei.mme_gid);
                    }
                    mme_config_p->gummei.mme_gid = calloc(num, sizeof(*mme_config_p->gummei.mme_gid));
                }
                mme_config_p->gummei.nb_mme_gid = num;
                for (i = 0; i < num; i++) {
                    mme_config_p->gummei.mme_gid[i] = config_setting_get_int_elem(subsetting, i);
                }
            }

            subsetting = config_setting_get_member (setting, MME_CONFIG_STRING_PLMN);
            if (subsetting != NULL) {
                num     = config_setting_length(subsetting);
                if (mme_config_p->gummei.nb_plmns != num) {
                    if (mme_config_p->gummei.plmn_mcc != NULL)     free(mme_config_p->gummei.plmn_mcc);
                    if (mme_config_p->gummei.plmn_mnc != NULL)     free(mme_config_p->gummei.plmn_mnc);
                    if (mme_config_p->gummei.plmn_mnc_len != NULL) free(mme_config_p->gummei.plmn_mnc_len);
                    if (mme_config_p->gummei.plmn_tac != NULL)     free(mme_config_p->gummei.plmn_tac);

                    mme_config_p->gummei.plmn_mcc     = calloc(num, sizeof(*mme_config_p->gummei.plmn_mcc));
                    mme_config_p->gummei.plmn_mnc     = calloc(num, sizeof(*mme_config_p->gummei.plmn_mnc));
                    mme_config_p->gummei.plmn_mnc_len = calloc(num, sizeof(*mme_config_p->gummei.plmn_mnc_len));
                    mme_config_p->gummei.plmn_tac     = calloc(num, sizeof(*mme_config_p->gummei.plmn_tac));
                }
                mme_config_p->gummei.nb_plmns = num;
                for (i = 0; i < num; i++) {
                    sub2setting =  config_setting_get_elem(subsetting, i);
                    if (sub2setting != NULL) {
                        if(  (config_setting_lookup_string( sub2setting, MME_CONFIG_STRING_MCC, &mcc) )) {
                            mme_config_p->gummei.plmn_mcc[i] = (uint16_t)atoi(mcc);
                        }
                        if(  (config_setting_lookup_string( sub2setting, MME_CONFIG_STRING_MNC, &mnc) )) {
                            mme_config_p->gummei.plmn_mnc[i] = (uint16_t)atoi(mnc);
                            mme_config_p->gummei.plmn_mnc_len[i] = strlen(mnc);
                            AssertFatal((mme_config_p->gummei.plmn_mnc_len[i] == 2) || (mme_config_p->gummei.plmn_mnc_len[i] == 3),
                                "Bad MNC length %u, must be 2 or 3", mme_config_p->gummei.plmn_mnc_len[i]);
                        }
                        if(  (config_setting_lookup_string( sub2setting, MME_CONFIG_STRING_TAC, &tac) )) {
                            mme_config_p->gummei.plmn_tac[i] = (uint16_t)atoi(tac);
                            AssertFatal(mme_config_p->gummei.plmn_tac[i] != 0,
                                "TAC must not be 0");
                        }
                    }
                }
            }
        }

        // NETWORK INTERFACE SETTING
        setting = config_setting_get_member (setting_mme, MME_CONFIG_STRING_NETWORK_INTERFACES_CONFIG);
        if(setting != NULL) {
            if(  (
                       config_setting_lookup_string( setting, MME_CONFIG_STRING_INTERFACE_NAME_FOR_S1_MME,
                               (const char **)&mme_interface_name_for_S1_MME)
                    && config_setting_lookup_string( setting, MME_CONFIG_STRING_IPV4_ADDRESS_FOR_S1_MME,
                            (const char **)&mme_ip_address_for_S1_MME)
                    && config_setting_lookup_string( setting, MME_CONFIG_STRING_INTERFACE_NAME_FOR_S11_MME,
                            (const char **)&mme_interface_name_for_S11)
                    && config_setting_lookup_string( setting, MME_CONFIG_STRING_IPV4_ADDRESS_FOR_S11_MME,
                            (const char **)&mme_ip_address_for_S11)
                  )
              ) {
                mme_config_p->ipv4.mme_interface_name_for_S1_MME = strdup(mme_interface_name_for_S1_MME);
                cidr = strdup(mme_ip_address_for_S1_MME);
                address = strtok(cidr, "/");
                IPV4_STR_ADDR_TO_INT_NWBO ( address, mme_config_p->ipv4.mme_ip_address_for_S1_MME, "BAD IP ADDRESS FORMAT FOR MME S1_MME !\n" )
                free(cidr);

                mme_config_p->ipv4.mme_interface_name_for_S11 = strdup(mme_interface_name_for_S11);
                cidr = strdup(mme_ip_address_for_S11);
                address = strtok(cidr, "/");
                IPV4_STR_ADDR_TO_INT_NWBO ( address, mme_config_p->ipv4.mme_ip_address_for_S11, "BAD IP ADDRESS FORMAT FOR MME S11 !\n" )
                free(cidr);
            }
        }

        // NAS SETTING
        setting = config_setting_get_member (setting_mme, MME_CONFIG_STRING_NAS_CONFIG);
        if (setting != NULL) {
            subsetting = config_setting_get_member (setting, MME_CONFIG_STRING_NAS_SUPPORTED_INTEGRITY_ALGORITHM_LIST);
            if (subsetting != NULL) {
                num     = config_setting_length(subsetting);
                if (num <= 8) {
                    for (i = 0; i < num; i++) {
                        astring = config_setting_get_string_elem(subsetting, i);
                        if (strcmp("EIA0", astring) == 0) mme_config_p->nas_config.prefered_integrity_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EIA0;
                        else if (strcmp("EIA1", astring) == 0) mme_config_p->nas_config.prefered_integrity_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EIA1;
                        else if (strcmp("EIA2", astring) == 0) mme_config_p->nas_config.prefered_integrity_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EIA2;
                        else if (strcmp("EIA3", astring) == 0) mme_config_p->nas_config.prefered_integrity_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EIA0;
                        else if (strcmp("EIA4", astring) == 0) mme_config_p->nas_config.prefered_integrity_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EIA0;
                        else if (strcmp("EIA5", astring) == 0) mme_config_p->nas_config.prefered_integrity_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EIA0;
                        else if (strcmp("EIA6", astring) == 0) mme_config_p->nas_config.prefered_integrity_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EIA0;
                        else if (strcmp("EIA7", astring) == 0) mme_config_p->nas_config.prefered_integrity_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EIA0;
                    }
                    for (i = num; i < 8; i++) {
                        mme_config_p->nas_config.prefered_integrity_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EIA0;
                    }
                }
            }
            subsetting = config_setting_get_member (setting, MME_CONFIG_STRING_NAS_SUPPORTED_CIPHERING_ALGORITHM_LIST);
            if (subsetting != NULL) {
                num     = config_setting_length(subsetting);
                if (num <= 8) {
                    for (i = 0; i < num; i++) {
                        astring = config_setting_get_string_elem(subsetting, i);
                        if (strcmp("EEA0", astring) == 0) mme_config_p->nas_config.prefered_ciphering_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EEA0;
                        else if (strcmp("EEA1", astring) == 0) mme_config_p->nas_config.prefered_ciphering_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EEA1;
                        else if (strcmp("EEA2", astring) == 0) mme_config_p->nas_config.prefered_ciphering_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EEA2;
                        else if (strcmp("EEA3", astring) == 0) mme_config_p->nas_config.prefered_ciphering_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EEA0;
                        else if (strcmp("EEA4", astring) == 0) mme_config_p->nas_config.prefered_ciphering_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EEA0;
                        else if (strcmp("EEA5", astring) == 0) mme_config_p->nas_config.prefered_ciphering_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EEA0;
                        else if (strcmp("EEA6", astring) == 0) mme_config_p->nas_config.prefered_ciphering_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EEA0;
                        else if (strcmp("EEA7", astring) == 0) mme_config_p->nas_config.prefered_ciphering_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EEA0;
                    }
                    for (i = num; i < 8; i++) {
                        mme_config_p->nas_config.prefered_ciphering_algorithm[i] = NAS_CONFIG_SECURITY_ALGORITHMS_EEA0;
                    }
                }
            }

        }
    }

    setting = config_lookup(&cfg, SGW_CONFIG_STRING_SGW_CONFIG);
    if(setting != NULL) {
        subsetting = config_setting_get_member (setting, SGW_CONFIG_STRING_NETWORK_INTERFACES_CONFIG);
        if(subsetting != NULL) {
            if(  (
                    config_setting_lookup_string( subsetting, SGW_CONFIG_STRING_SGW_IPV4_ADDRESS_FOR_S1U_S12_S4_UP,
                            (const char **)&sgw_ip_address_for_S1u_S12_S4_up)
                    && config_setting_lookup_string( subsetting, SGW_CONFIG_STRING_SGW_IPV4_ADDRESS_FOR_S11,
                            (const char **)&sgw_ip_address_for_S11)
                  )
              ) {
                cidr = strdup(sgw_ip_address_for_S1u_S12_S4_up);
                address = strtok(cidr, "/");
                IPV4_STR_ADDR_TO_INT_NWBO ( address, mme_config_p->ipv4.sgw_ip_address_for_S1u_S12_S4_up, "BAD IP ADDRESS FORMAT FOR SGW S1u_S12_S4 !\n" )
                free(cidr);

                cidr = strdup(sgw_ip_address_for_S11);
                address = strtok(cidr, "/");
                IPV4_STR_ADDR_TO_INT_NWBO ( address, mme_config_p->ipv4.sgw_ip_address_for_S11, "BAD IP ADDRESS FORMAT FOR SGW S11 !\n" )
                free(cidr);
            }
        }
    }
    return 0;
}

#define DISPLAY_ARRAY(size, format, args...)                        \
do {                                                                \
    int i;                                                          \
    for (i = 0; i < size; i++) {                                    \
        fprintf(stdout, format, args);                              \
        if ((i != (size - 1)) && ((i + 1) % 10 == 0))               \
        {                                                           \
            fprintf(stdout, "\n        ");                          \
        }                                                           \
    }                                                               \
    if (i > 0)                                                      \
        fprintf(stdout, "\n");                                      \
} while(0)

static void config_display(mme_config_t *mme_config_p)
{
    int j;

    fprintf(stdout, "==== EURECOM %s v%s ====\n", PACKAGE_NAME, PACKAGE_VERSION);
    fprintf(stdout, "Configuration:\n");
    fprintf(stdout, "- File ...............: %s\n", mme_config_p->config_file);
    fprintf(stdout, "- Verbosity level ....: %d\n", mme_config_p->verbosity_level);
    fprintf(stdout, "- Realm ..............: %s\n", mme_config_p->realm);
    fprintf(stdout, "- Max eNBs ...........: %u\n", mme_config_p->max_eNBs);
    fprintf(stdout, "- Max UEs ............: %u\n", mme_config_p->max_ues);
    fprintf(stdout, "- Emergency support ..: %s\n", mme_config_p->emergency_attach_supported == 0 ? "FALSE" : "TRUE");
    fprintf(stdout, "- Unauth IMSI support : %s\n", mme_config_p->unauthenticated_imsi_supported == 0 ? "FALSE" : "TRUE");
    fprintf(stdout, "- Relative capa ......: %u\n\n", mme_config_p->relative_capacity);
    fprintf(stdout, "- Statistics timer ...: %u (seconds)\n\n", mme_config_p->mme_statistic_timer);
    fprintf(stdout, "- S1-U:\n");
    fprintf(stdout, "    port number ......: %d\n", mme_config_p->gtpv1u_config.port_number);
    fprintf(stdout, "- S1-MME:\n");
    fprintf(stdout, "    port number ......: %d\n", mme_config_p->s1ap_config.port_number);
    fprintf(stdout, "- IP:\n");
    //fprintf(stdout, "    s1-u iface .......: %s\n", mme_config_p->ipv4.sgw_interface_name_for_S1u_S12_S4_up);
    //fprintf(stdout, "    s1-u ip ..........: %s/%d\n",
    //        inet_ntoa(*((struct in_addr *)&mme_config_p->ipv4.sgw_ip_address_for_S1u_S12_S4_up)),
    //        mme_config_p->ipv4.sgw_ip_netmask_for_S1u_S12_S4_up);
    //fprintf(stdout, "    sgi iface ........: %s\n", mme_config_p->ipv4.pgw_interface_name_for_SGI);
    //fprintf(stdout, "    sgi ip ...........: %s/%d\n",
    //        inet_ntoa(*((struct in_addr *)&mme_config_p->ipv4.pgw_ip_addr_for_SGI)),
    //        mme_config_p->ipv4.pgw_ip_netmask_for_SGI);
    fprintf(stdout, "    s1-MME iface .....: %s\n", mme_config_p->ipv4.mme_interface_name_for_S1_MME);
    fprintf(stdout, "    s1-MME ip ........: %s\n",
            inet_ntoa(*((struct in_addr *)&mme_config_p->ipv4.mme_ip_address_for_S1_MME)));
    //fprintf(stdout, "    s11 S-GW iface ...: %s\n", mme_config_p->ipv4.sgw_interface_name_for_S11);
    //fprintf(stdout, "    s11 S-GW ip ......: %s/%d\n",
    //        inet_ntoa(*((struct in_addr *)&mme_config_p->ipv4.sgw_ip_address_for_S11)),
    //        mme_config_p->ipv4.sgw_ip_netmask_for_S11);
    fprintf(stdout, "    s11 MME iface ....: %s\n", mme_config_p->ipv4.mme_interface_name_for_S11);
    fprintf(stdout, "    s11 S-GW ip ......: %s\n",
            inet_ntoa(*((struct in_addr *)&mme_config_p->ipv4.mme_ip_address_for_S11)));
    fprintf(stdout, "- ITTI:\n");
    fprintf(stdout, "    queue size .......: %u (bytes)\n", mme_config_p->itti_config.queue_size);
    fprintf(stdout, "    log file .........: %s\n", mme_config_p->itti_config.log_file);
    fprintf(stdout, "- SCTP:\n");
    fprintf(stdout, "    in streams .......: %u\n", mme_config_p->sctp_config.in_streams);
    fprintf(stdout, "    out streams ......: %u\n", mme_config_p->sctp_config.out_streams);
    fprintf(stdout, "- GUMMEI:\n");
    fprintf(stdout, "    mme group ids ....:\n        ");
    DISPLAY_ARRAY(mme_config_p->gummei.nb_mme_gid, "| %u ", mme_config_p->gummei.mme_gid[i]);
    fprintf(stdout, "    mme codes ........:\n        ");
    DISPLAY_ARRAY(mme_config_p->gummei.nb_mmec, "| %u ", mme_config_p->gummei.mmec[i]);
    fprintf(stdout, "    plmns ............: (mcc.mnc:tac)\n");
    for (j= 0; j < mme_config_p->gummei.nb_plmns; j++) {
        if (mme_config_p->gummei.plmn_mnc_len[j] ==2 ) {
            fprintf(stdout, "            %3u.%3u:%u\n",
                mme_config_p->gummei.plmn_mcc[j],
                mme_config_p->gummei.plmn_mnc[j],
                mme_config_p->gummei.plmn_tac[j]);
        } else {
            fprintf(stdout, "            %3u.%03u:%u\n",
                mme_config_p->gummei.plmn_mcc[j],
                mme_config_p->gummei.plmn_mnc[j],
                mme_config_p->gummei.plmn_tac[j]);
        }
    }
    fprintf(stdout, "- S6A:\n");
    fprintf(stdout, "    conf file ........: %s\n", mme_config_p->s6a_config.conf_file);
}

static void usage(void)
{
    fprintf(stdout, "==== EURECOM %s v%s ====\n", PACKAGE_NAME, PACKAGE_VERSION);
    fprintf(stdout, "Please report any bug to: %s\n\n", PACKAGE_BUGREPORT);
    fprintf(stdout, "Usage: oaisim_mme [options]\n\n");
    fprintf(stdout, "Available options:\n");
    fprintf(stdout, "-h      Print this help and return\n");
    fprintf(stdout, "-i<interface name>\n");
    fprintf(stdout, "        Set the network card to use for IPv4 forwarding\n");
    fprintf(stdout, "-c<path>\n");
    fprintf(stdout, "        Set the configuration file for mme\n");
    fprintf(stdout, "        See template in UTILS/CONF\n");
    fprintf(stdout, "-K<file>\n");
    fprintf(stdout, "        Output intertask messages to provided file\n");
    fprintf(stdout, "-V      Print %s version and return\n", PACKAGE_NAME);
    fprintf(stdout, "-v[1-2] Debug level:\n");
    fprintf(stdout, "            1 -> ASN1 XER printf on and ASN1 debug off\n");
    fprintf(stdout, "            2 -> ASN1 XER printf on and ASN1 debug on\n");
}

extern void
nwGtpv1uDisplayBanner(void);

int config_parse_opt_line(int argc, char *argv[], mme_config_t *mme_config_p)
{
    int c;
    mme_config_init(mme_config_p);
    /* Parsing command line */
    while ((c = getopt (argc, argv, "c:hi:K:v:V")) != -1) {
        switch (c) {
            case 'c': {
                /* Store the given configuration file. If no file is given,
                 * then the default values will be used.
                 */
                int config_file_len = 0;
                config_file_len = strlen(optarg);
                mme_config_p->config_file = malloc(sizeof(char) * (config_file_len + 1));
                memcpy(mme_config_p->config_file, optarg, config_file_len);
                mme_config_p->config_file[config_file_len] = '\0';
            } break;
            /*case 'i': {
                int interface_len = 0;

                // Copying provided interface name to use for ipv4 forwarding
                interface_len = strlen(optarg);
                mme_config_p->ipv4.sgw_interface_name_for_S1u_S12_S4_up = calloc(interface_len + 1, sizeof(char));
                memcpy(mme_config_p->ipv4.sgw_interface_name_for_S1u_S12_S4_up, optarg, interface_len);
                mme_config_p->ipv4.sgw_interface_name_for_S1u_S12_S4_up[interface_len] = '\0';
            } break;*/
            case 'v': {
                mme_config_p->verbosity_level = atoi(optarg);
            } break;
            case 'V': {
                fprintf(stdout, "==== EURECOM %s v%s ====\n"
                        "Please report any bug to: %s\n", PACKAGE_NAME, PACKAGE_VERSION,
                        PACKAGE_BUGREPORT);
                exit(0);
                nwGtpv1uDisplayBanner();
            } break;
            case 'K':
                mme_config_p->itti_config.log_file = strdup(optarg);
                break;
            case 'h': /* Fall through */
            default:
                usage();
                exit(0);
        }
    }
    /* Parse the configuration file using libconfig */
    if (config_parse_file(mme_config_p) != 0) {
        return -1;
    }
    /* Display yhe configuration */
    config_display(mme_config_p);
    return 0;
}
