Skip to content
Snippets Groups Projects
s1ap_eNB.c 14.5 KiB
Newer Older
Cédric Roux's avatar
Cédric Roux committed
/*******************************************************************************
gauthier's avatar
gauthier committed
    OpenAirInterface
    Copyright(c) 1999 - 2014 Eurecom
Cédric Roux's avatar
Cédric Roux committed

gauthier's avatar
gauthier committed
    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.
Cédric Roux's avatar
Cédric Roux committed


gauthier's avatar
gauthier committed
    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.
Cédric Roux's avatar
Cédric Roux committed

gauthier's avatar
gauthier committed
    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/>.
Cédric Roux's avatar
Cédric Roux committed

  Contact Information
gauthier's avatar
gauthier committed
  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.
Cédric Roux's avatar
Cédric Roux committed

gauthier's avatar
gauthier committed
 *******************************************************************************/
Cédric Roux's avatar
Cédric Roux committed

Cédric Roux's avatar
Cédric Roux committed
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <crypt.h>
Cédric Roux's avatar
Cédric Roux committed

#include "tree.h"
#include "queue.h"

#include "intertask_interface.h"

#include "s1ap_eNB_default_values.h"
Cédric Roux's avatar
Cédric Roux committed

#include "s1ap_common.h"
#include "s1ap_ies_defs.h"

#include "s1ap_eNB_defs.h"
#include "s1ap_eNB.h"
#include "s1ap_eNB_encoder.h"
#include "s1ap_eNB_handlers.h"
#include "s1ap_eNB_nnsf.h"

#include "s1ap_eNB_nas_procedures.h"
#include "s1ap_eNB_management_procedures.h"

Cédric Roux's avatar
Cédric Roux committed
#include "assertions.h"
#include "conversions.h"

#if !defined(OAI_EMU)
s1ap_eNB_config_t s1ap_config;
#endif

static int s1ap_eNB_generate_s1_setup_request(
    s1ap_eNB_instance_t *instance_p, s1ap_eNB_mme_data_t *s1ap_mme_data_p);
Cédric Roux's avatar
Cédric Roux committed

void s1ap_eNB_handle_register_eNB(instance_t instance, s1ap_register_enb_req_t *s1ap_register_eNB);
static
void s1ap_eNB_handle_sctp_association_resp(instance_t instance, sctp_new_association_resp_t *sctp_new_association_resp);
uint32_t s1ap_generate_eNB_id(void)
{
    char    *out;
    char     hostname[50];
    int      ret;
    uint32_t eNB_id;

    /* Retrieve the host name */
    ret = gethostname(hostname, sizeof(hostname));
    DevAssert(ret == 0);

    out = crypt(hostname, "eurecom");
    DevAssert(out != NULL);

    eNB_id = ((out[0] << 24) | (out[1] << 16) | (out[2] << 8) | out[3]);

    return eNB_id;
}

static void s1ap_eNB_register_mme(s1ap_eNB_instance_t *instance_p,
                                  net_ip_address_t    *mme_ip_address,
                                  net_ip_address_t    *local_ip_addr)
Cédric Roux's avatar
Cédric Roux committed
{
    MessageDef                 *message_p                   = NULL;
    sctp_new_association_req_t *sctp_new_association_req_p  = NULL;
    s1ap_eNB_mme_data_t        *s1ap_mme_data_p             = NULL;
Cédric Roux's avatar
Cédric Roux committed

    DevAssert(instance_p != NULL);
    DevAssert(mme_ip_address != NULL);
Cédric Roux's avatar
Cédric Roux committed

    message_p = itti_alloc_new_message(TASK_S1AP, SCTP_NEW_ASSOCIATION_REQ);
Cédric Roux's avatar
Cédric Roux committed

    sctp_new_association_req_p = &message_p->ittiMsg.sctp_new_association_req;
Cédric Roux's avatar
Cédric Roux committed

    sctp_new_association_req_p->port = S1AP_PORT_NUMBER;
    sctp_new_association_req_p->ppid = S1AP_SCTP_PPID;
Cédric Roux's avatar
Cédric Roux committed

    memcpy(&sctp_new_association_req_p->remote_address,
            mme_ip_address,
Cédric Roux's avatar
Cédric Roux committed

    memcpy(&sctp_new_association_req_p->local_address,
            local_ip_addr,
gauthier's avatar
 
gauthier committed
            sizeof(*local_ip_addr));

    /* Create new MME descriptor */
    s1ap_mme_data_p = calloc(1, sizeof(*s1ap_mme_data_p));
    DevAssert(s1ap_mme_data_p != NULL);
Cédric Roux's avatar
Cédric Roux committed

    s1ap_mme_data_p->cnx_id                = s1ap_eNB_fetch_add_global_cnx_id();
    sctp_new_association_req_p->ulp_cnx_id = s1ap_mme_data_p->cnx_id;
Cédric Roux's avatar
Cédric Roux committed

    s1ap_mme_data_p->assoc_id          = -1;
    s1ap_mme_data_p->s1ap_eNB_instance = instance_p;
Cédric Roux's avatar
Cédric Roux committed

    STAILQ_INIT(&s1ap_mme_data_p->served_gummei);
Cédric Roux's avatar
Cédric Roux committed

    /* Insert the new descriptor in list of known MME
     * but not yet associated.
     */
    RB_INSERT(s1ap_mme_map, &instance_p->s1ap_mme_head, s1ap_mme_data_p);
    s1ap_mme_data_p->state = S1AP_ENB_STATE_WAITING;
    instance_p->s1ap_mme_nb ++;
    instance_p->s1ap_mme_pending_nb ++;
Cédric Roux's avatar
Cédric Roux committed

    itti_send_msg_to_task(TASK_SCTP, instance_p->instance, message_p);
Cédric Roux's avatar
Cédric Roux committed

void s1ap_eNB_handle_register_eNB(instance_t instance, s1ap_register_enb_req_t *s1ap_register_eNB)
{
    s1ap_eNB_instance_t *new_instance;
    uint8_t index;
Cédric Roux's avatar
Cédric Roux committed

Cédric Roux's avatar
Cédric Roux committed

    /* Look if the provided instance already exists */
    new_instance = s1ap_eNB_get_instance(instance);
    if (new_instance != NULL) {
        /* Checks if it is a retry on the same eNB */
        DevCheck(new_instance->eNB_id == s1ap_register_eNB->eNB_id, new_instance->eNB_id, s1ap_register_eNB->eNB_id, 0);
        DevCheck(new_instance->cell_type == s1ap_register_eNB->cell_type, new_instance->cell_type, s1ap_register_eNB->cell_type, 0);
        DevCheck(new_instance->tac == s1ap_register_eNB->tac, new_instance->tac, s1ap_register_eNB->tac, 0);
        DevCheck(new_instance->mcc == s1ap_register_eNB->mcc, new_instance->mcc, s1ap_register_eNB->mcc, 0);
        DevCheck(new_instance->mnc == s1ap_register_eNB->mnc, new_instance->mnc, s1ap_register_eNB->mnc, 0);
        DevCheck(new_instance->mnc_digit_length == s1ap_register_eNB->mnc_digit_length, new_instance->mnc_digit_length, s1ap_register_eNB->mnc_digit_length, 0);
        DevCheck(new_instance->default_drx == s1ap_register_eNB->default_drx, new_instance->default_drx, s1ap_register_eNB->default_drx, 0);
    } else {
        new_instance = calloc(1, sizeof(s1ap_eNB_instance_t));
        DevAssert(new_instance != NULL);

        RB_INIT(&new_instance->s1ap_ue_head);
        RB_INIT(&new_instance->s1ap_mme_head);

        /* Copy usefull parameters */
        new_instance->instance         = instance;
        new_instance->eNB_name         = s1ap_register_eNB->eNB_name;
        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;
        new_instance->mcc              = s1ap_register_eNB->mcc;
        new_instance->mnc              = s1ap_register_eNB->mnc;
        new_instance->mnc_digit_length = s1ap_register_eNB->mnc_digit_length;
        new_instance->default_drx      = s1ap_register_eNB->default_drx;

        /* Add the new instance to the list of eNB (meaningfull in virtual mode) */
        s1ap_eNB_insert_new_instance(new_instance);

        S1AP_DEBUG("Registered new eNB[%d] and %s eNB id %u\n",
                   instance,
                   s1ap_register_eNB->cell_type == CELL_MACRO_ENB ? "macro" : "home",
                   s1ap_register_eNB->eNB_id);
    }

    DevCheck(s1ap_register_eNB->nb_mme <= S1AP_MAX_NB_MME_IP_ADDRESS,
             S1AP_MAX_NB_MME_IP_ADDRESS, s1ap_register_eNB->nb_mme, 0);

    /* Trying to connect to provided list of MME ip address */
    for (index = 0; index < s1ap_register_eNB->nb_mme; index++) {
        s1ap_eNB_register_mme(new_instance, &s1ap_register_eNB->mme_ip_address[index],
                              &s1ap_register_eNB->enb_ip_address);
    }
}
Cédric Roux's avatar
Cédric Roux committed

static
void s1ap_eNB_handle_sctp_association_resp(instance_t instance, sctp_new_association_resp_t *sctp_new_association_resp)
{
    s1ap_eNB_instance_t *instance_p;
    s1ap_eNB_mme_data_t *s1ap_mme_data_p;

    DevAssert(sctp_new_association_resp != NULL);

    instance_p = s1ap_eNB_get_instance(instance);
    DevAssert(instance_p != NULL);

    s1ap_mme_data_p = s1ap_eNB_get_MME(instance_p, -1,
                                       sctp_new_association_resp->ulp_cnx_id);
    DevAssert(s1ap_mme_data_p != NULL);

    if (sctp_new_association_resp->sctp_state != SCTP_STATE_ESTABLISHED) {
        S1AP_WARN("Received unsuccessful result for SCTP association (%u), instance %d, cnx_id %u\n",
                  instance,
                  sctp_new_association_resp->ulp_cnx_id);
        s1ap_handle_s1_setup_message(s1ap_mme_data_p, sctp_new_association_resp->sctp_state == SCTP_STATE_SHUTDOWN);
Cédric Roux's avatar
Cédric Roux committed
    }

    /* Update parameters */
    s1ap_mme_data_p->assoc_id    = sctp_new_association_resp->assoc_id;
    s1ap_mme_data_p->in_streams  = sctp_new_association_resp->in_streams;
    s1ap_mme_data_p->out_streams = sctp_new_association_resp->out_streams;

    /* Prepare new S1 Setup Request */
    s1ap_eNB_generate_s1_setup_request(instance_p, s1ap_mme_data_p);
}

static
void s1ap_eNB_handle_sctp_data_ind(sctp_data_ind_t *sctp_data_ind)
{
    DevAssert(sctp_data_ind != NULL);

    s1ap_eNB_handle_message(sctp_data_ind->assoc_id, sctp_data_ind->stream,
                            sctp_data_ind->buffer, sctp_data_ind->buffer_length);

    result = itti_free(TASK_UNKNOWN, sctp_data_ind->buffer);
    AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
}

void *s1ap_eNB_task(void *arg)
{
    MessageDef *received_msg = NULL;
    int         result;
    s1ap_eNB_prepare_internal_data();

    itti_mark_task_ready(TASK_S1AP);

    while (1) {
        itti_receive_msg(TASK_S1AP, &received_msg);

        switch (ITTI_MSG_ID(received_msg)) {
            case TERMINATE_MESSAGE:
                itti_exit_task();
                break;
                /* Register a new eNB.
                 * in Virtual mode eNBs will be distinguished using the mod_id/
                 * Each eNB has to send an S1AP_REGISTER_ENB message with its
                 * own parameters.
                 */
                s1ap_eNB_handle_register_eNB(ITTI_MESSAGE_GET_INSTANCE(received_msg),
                s1ap_eNB_handle_sctp_association_resp(ITTI_MESSAGE_GET_INSTANCE(received_msg),
                                                      &received_msg->ittiMsg.sctp_new_association_resp);
                s1ap_eNB_handle_sctp_data_ind(&received_msg->ittiMsg.sctp_data_ind);
            case S1AP_NAS_FIRST_REQ: {
                s1ap_eNB_handle_nas_first_req(ITTI_MESSAGE_GET_INSTANCE(received_msg),
            } break;
            case S1AP_UPLINK_NAS: {
                s1ap_eNB_nas_uplink(ITTI_MESSAGE_GET_INSTANCE(received_msg),
            case S1AP_UE_CAPABILITIES_IND: {
                s1ap_eNB_ue_capabilities(ITTI_MESSAGE_GET_INSTANCE(received_msg),
                                         &S1AP_UE_CAPABILITIES_IND(received_msg));
            } break;
            case S1AP_INITIAL_CONTEXT_SETUP_RESP: {
                s1ap_eNB_initial_ctxt_resp(ITTI_MESSAGE_GET_INSTANCE(received_msg),
                                           &S1AP_INITIAL_CONTEXT_SETUP_RESP(received_msg));
            } break;
            case S1AP_NAS_NON_DELIVERY_IND: {
                s1ap_eNB_nas_non_delivery_ind(ITTI_MESSAGE_GET_INSTANCE(received_msg),
                                              &S1AP_NAS_NON_DELIVERY_IND(received_msg));
            } break;
gauthier's avatar
gauthier committed
            case S1AP_UE_CONTEXT_RELEASE_COMPLETE: {
                s1ap_ue_context_release_complete(ITTI_MESSAGE_GET_INSTANCE(received_msg),
                                              &S1AP_UE_CONTEXT_RELEASE_COMPLETE(received_msg));
            } break;
                S1AP_ERROR("Received unhandled message: %d:%s\n",
                           ITTI_MSG_ID(received_msg), ITTI_MSG_NAME(received_msg));
        result = itti_free (ITTI_MSG_ORIGIN_ID(received_msg), received_msg);
        AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
Cédric Roux's avatar
Cédric Roux committed
}

static int s1ap_eNB_generate_s1_setup_request(
    s1ap_eNB_instance_t *instance_p, s1ap_eNB_mme_data_t *s1ap_mme_data_p)
Cédric Roux's avatar
Cédric Roux committed
{
    s1ap_message               message;

    S1ap_S1SetupRequestIEs_t *s1SetupRequest_p;
    S1ap_PLMNidentity_t       plmnIdentity;
    S1ap_SupportedTAs_Item_t  ta;

    uint8_t  *buffer;
    uint32_t  len;
    int       ret = 0;
Cédric Roux's avatar
Cédric Roux committed

    DevAssert(instance_p != NULL);
    DevAssert(s1ap_mme_data_p != NULL);
Cédric Roux's avatar
Cédric Roux committed

    memset(&message, 0, sizeof(s1ap_message));

    message.direction     = S1AP_PDU_PR_initiatingMessage;
    message.procedureCode = S1ap_ProcedureCode_id_S1Setup;
    message.criticality   = S1ap_Criticality_reject;
Cédric Roux's avatar
Cédric Roux committed

    s1SetupRequest_p = &message.msg.s1ap_S1SetupRequestIEs;
    memset((void *)&plmnIdentity, 0, sizeof(S1ap_PLMNidentity_t));
Cédric Roux's avatar
Cédric Roux committed

    memset((void *)&ta, 0, sizeof(S1ap_SupportedTAs_Item_t));
Cédric Roux's avatar
Cédric Roux committed

    s1ap_mme_data_p->state = S1AP_ENB_STATE_WAITING;
Cédric Roux's avatar
Cédric Roux committed

    s1SetupRequest_p->global_ENB_ID.eNB_ID.present = S1ap_ENB_ID_PR_macroENB_ID;
    MACRO_ENB_ID_TO_BIT_STRING(instance_p->eNB_id,
Cédric Roux's avatar
Cédric Roux committed
                               &s1SetupRequest_p->global_ENB_ID.eNB_ID.choice.macroENB_ID);
    MCC_MNC_TO_PLMNID(instance_p->mcc, instance_p->mnc, instance_p->mnc_digit_length,
Cédric Roux's avatar
Cédric Roux committed
                      &s1SetupRequest_p->global_ENB_ID.pLMNidentity);

    S1AP_INFO("%d -> %02x%02x%02x\n", instance_p->eNB_id, s1SetupRequest_p->global_ENB_ID.eNB_ID.choice.macroENB_ID.buf[0], s1SetupRequest_p->global_ENB_ID.eNB_ID.choice.macroENB_ID.buf[1], s1SetupRequest_p->global_ENB_ID.eNB_ID.choice.macroENB_ID.buf[2]);

    INT16_TO_OCTET_STRING(instance_p->tac, &ta.tAC);
    MCC_MNC_TO_TBCD(instance_p->mcc, instance_p->mnc, instance_p->mnc_digit_length, &plmnIdentity);
Cédric Roux's avatar
Cédric Roux committed

    ASN_SEQUENCE_ADD(&ta.broadcastPLMNs.list, &plmnIdentity);
    ASN_SEQUENCE_ADD(&s1SetupRequest_p->supportedTAs.list, &ta);

    s1SetupRequest_p->defaultPagingDRX = instance_p->default_drx;
Cédric Roux's avatar
Cédric Roux committed

        s1SetupRequest_p->presenceMask |= S1AP_S1SETUPREQUESTIES_ENBNAME_PRESENT;
        OCTET_STRING_fromBuf(&s1SetupRequest_p->eNBname, instance_p->eNB_name,
                             strlen(instance_p->eNB_name));
Cédric Roux's avatar
Cédric Roux committed
    }

    if (s1ap_eNB_encode_pdu(&message, &buffer, &len) < 0) {
        S1AP_ERROR("Failed to encode S1 setup request\n");
        return -1;
    }

    /* Non UE-Associated signalling -> stream = 0 */
    s1ap_eNB_itti_send_sctp_data_req(instance_p->instance, s1ap_mme_data_p->assoc_id, buffer, len, 0);
Cédric Roux's avatar
Cédric Roux committed
    return ret;
}