Files
openairinterface5g/openair-cn/S1AP/s1ap_mme.c
T

413 lines
12 KiB
C

/*******************************************************************************
Eurecom OpenAirInterface
Copyright(c) 1999 - 2012 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
*******************************************************************************/
#if HAVE_CONFIG_H
# include "config.h"
#endif
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#if !defined(MME_CLIENT_TEST)
# include "intertask_interface.h"
#endif
#include "assertions.h"
#include "queue.h"
#include "s1ap_mme.h"
#include "s1ap_mme_decoder.h"
#include "s1ap_mme_handlers.h"
#include "s1ap_ies_defs.h"
#include "s1ap_mme_nas_procedures.h"
#include "s1ap_mme_retransmission.h"
#define S1AP_DEBUG_LIST
#ifdef S1AP_DEBUG_LIST
# define eNB_LIST_OUT(x, args...) S1AP_DEBUG("[eNB]%*s"x"\n", 4*indent, "", ##args)
# define UE_LIST_OUT(x, args...) S1AP_DEBUG("[UE] %*s"x"\n", 4*indent, "", ##args)
#else
# define eNB_LIST_OUT(x, args...)
# define UE_LIST_OUT(x, args...)
#endif
#if !defined(MME_CLIENT_TEST)
// static pthread_t s1ap_task_thread;
uint32_t nb_eNB_associated = 0;
STAILQ_HEAD(eNB_list_s, eNB_description_s) eNB_list_head;
static int indent = 0;
void *s1ap_mme_thread(void *args);
static int s1ap_send_init_sctp(void) {
// Create and alloc new message
MessageDef *message_p;
message_p = itti_alloc_new_message(TASK_S1AP, SCTP_INIT_MSG);
message_p->msg.sctpInit.port = S1AP_PORT_NUMBER;
message_p->msg.sctpInit.ppid = S1AP_SCTP_PPID;
message_p->msg.sctpInit.ipv4 = 1;
message_p->msg.sctpInit.ipv6 = 0;
message_p->msg.sctpInit.nb_ipv4_addr = 1;
message_p->msg.sctpInit.ipv4_address[0]
= mme_config.ipv4.mme_ip_address_for_S1_MME;
/* SR WARNING: ipv6 multi-homing fails sometimes for localhost.
* Disable it for now.*/
message_p->msg.sctpInit.nb_ipv6_addr = 0;
message_p->msg.sctpInit.ipv6_address[0] = "0:0:0:0:0:0:0:1";
return itti_send_msg_to_task(TASK_SCTP, INSTANCE_DEFAULT, message_p);
}
void *s1ap_mme_thread(void *args)
{
MessageDef *received_message_p;
itti_mark_task_ready(TASK_S1AP);
while(1) {
/* Trying to fetch a message from the message queue.
* If the queue is empty, this function will block till a
* message is sent to the task.
*/
itti_receive_msg(TASK_S1AP, &received_message_p);
assert(received_message_p != NULL);
switch(received_message_p->header.messageId) {
case S1AP_SCTP_NEW_MESSAGE_IND: {
/* New message received from SCTP layer.
* Decode and handle it.
*/
s1ap_message message;
s1ap_sctp_new_msg_ind_t *s1ap_sctp_new_msg_ind_p;
s1ap_sctp_new_msg_ind_p = &received_message_p->msg.s1ap_sctp_new_msg_ind;
memset((void *)&message, 0, sizeof(s1ap_message));
/* Invoke S1AP message decoder */
if (s1ap_mme_decode_pdu(&message, s1ap_sctp_new_msg_ind_p->buffer, s1ap_sctp_new_msg_ind_p->buf_length) < 0) {
// TODO: Notify eNB of failure with right cause
S1AP_ERROR("Failed to decode new buffer\n");
} else {
s1ap_mme_handle_message(s1ap_sctp_new_msg_ind_p->assoc_id, s1ap_sctp_new_msg_ind_p->stream, &message);
}
// Free received PDU array
free(s1ap_sctp_new_msg_ind_p->buffer);
} break;
/* SCTP layer notifies S1AP of disconnection of a peer. */
case SCTP_CLOSE_ASSOCIATION: {
sctp_close_association_t *sctp_close_association_p;
sctp_close_association_p = &received_message_p->msg.sctp_close_association;
s1ap_handle_sctp_deconnection(sctp_close_association_p->assoc_id);
} break;
case SCTP_NEW_ASSOCIATION: {
s1ap_handle_new_association(&received_message_p->msg.sctp_new_peer);
} break;
case NAS_DOWNLINK_DATA_IND: {
/* New message received from NAS task.
* This corresponds to a S1AP downlink nas transport message.
*/
s1ap_generate_downlink_nas_transport(&received_message_p->msg.nas_dl_data_ind);
} break;
case NAS_ATTACH_ACCEPT: {
s1ap_handle_attach_accepted(&received_message_p->msg.nas_attach_accept);
} break;
case TIMER_HAS_EXPIRED: {
s1ap_handle_timer_expiry(&received_message_p->msg.timer_has_expired);
} break;
default: {
S1AP_DEBUG("Unkwnon message ID %s:%d\n",
itti_get_message_name(received_message_p->header.messageId),
received_message_p->header.messageId);
} break;
}
free(received_message_p);
received_message_p = NULL;
}
return NULL;
}
int s1ap_mme_init(const mme_config_t *mme_config_p) {
S1AP_DEBUG("Initializing S1AP interface\n");
if (get_asn1c_environment_version() < ASN1_MINIMUM_VERSION) {
S1AP_ERROR("ASN1C version %d fount, expecting at least %d\n",
get_asn1c_environment_version(), ASN1_MINIMUM_VERSION);
return -1;
} else {
S1AP_DEBUG("ASN1C version %d\n", get_asn1c_environment_version());
}
#if defined(UPDATE_RELEASE_10)
S1AP_DEBUG("S1AP Release v10.5\n");
#else
# if defined(UPDATE_RELEASE_9)
S1AP_DEBUG("S1AP Release v9.8\n");
# else
S1AP_DEBUG("S1AP Release v8.10\n");
# endif
#endif
STAILQ_INIT(&eNB_list_head);
if (itti_create_task(TASK_S1AP, &s1ap_mme_thread, NULL) < 0) {
S1AP_ERROR("Error while creating S1AP task\n");
return -1;
}
if (s1ap_send_init_sctp() < 0) {
S1AP_ERROR("Error while sendind SCTP_INIT_MSG to SCTP \n");
return -1;
}
S1AP_DEBUG("Initializing S1AP interface: DONE\n");
return 0;
}
void s1ap_dump_eNB_list(void) {
eNB_description_t *eNB_ref;
STAILQ_FOREACH(eNB_ref, &eNB_list_head, eNB_entries)
{
s1ap_dump_eNB(eNB_ref);
}
}
void s1ap_dump_eNB(eNB_description_t *eNB_ref) {
#ifdef S1AP_DEBUG_LIST
ue_description_t *ue_ref;
//Reset indentation
indent = 0;
if (eNB_ref == NULL) {
return;
}
eNB_LIST_OUT("");
eNB_LIST_OUT("eNB name: %s", eNB_ref->eNB_name == NULL ? "not present" : eNB_ref->eNB_name);
eNB_LIST_OUT("eNB ID: %07x", eNB_ref->eNB_id);
eNB_LIST_OUT("SCTP assoc id: %d", eNB_ref->sctp_assoc_id);
eNB_LIST_OUT("SCTP instreams: %d", eNB_ref->instreams);
eNB_LIST_OUT("SCTP outstreams: %d", eNB_ref->outstreams);
eNB_LIST_OUT("UE attache to eNB: %d", eNB_ref->nb_ue_associated);
indent++;
STAILQ_FOREACH(ue_ref, &eNB_ref->ue_list_head, ue_entries)
{
s1ap_dump_ue(ue_ref);
}
indent--;
eNB_LIST_OUT("");
#else
s1ap_dump_ue(NULL);
#endif
}
void s1ap_dump_ue(ue_description_t *ue_ref) {
#ifdef S1AP_DEBUG_LIST
if (ue_ref == NULL) return;
UE_LIST_OUT("eNB UE s1ap id: 0x%06x", ue_ref->eNB_ue_s1ap_id);
UE_LIST_OUT("MME UE s1ap id: 0x%08x", ue_ref->mme_ue_s1ap_id);
UE_LIST_OUT("SCTP stream recv: 0x%04x", ue_ref->sctp_stream_recv);
UE_LIST_OUT("SCTP stream send: 0x%04x", ue_ref->sctp_stream_send);
#else
ue_ref = ue_ref;
#endif
}
eNB_description_t* s1ap_is_eNB_id_in_list(uint32_t eNB_id) {
eNB_description_t *eNB_ref;
STAILQ_FOREACH(eNB_ref, &eNB_list_head, eNB_entries)
{
if (eNB_ref->eNB_id == eNB_id) {
/* We fount a matching reference, return it */
return eNB_ref;
}
}
/* No matching eNB, return NULL */
return eNB_ref;
}
eNB_description_t* s1ap_is_eNB_assoc_id_in_list(uint32_t sctp_assoc_id) {
eNB_description_t *eNB_ref;
STAILQ_FOREACH(eNB_ref, &eNB_list_head, eNB_entries)
{
if (eNB_ref->sctp_assoc_id == sctp_assoc_id) {
/* We fount a matching reference, return it */
return eNB_ref;
}
}
/* No matching eNB or no eNB in list, return NULL */
return NULL;
}
ue_description_t *s1ap_is_ue_eNB_id_in_list(eNB_description_t *eNB_ref,
uint32_t eNB_ue_s1ap_id) {
ue_description_t *ue_ref;
DevAssert(eNB_ref != NULL);
STAILQ_FOREACH(ue_ref, &eNB_ref->ue_list_head, ue_entries)
{
if (ue_ref->eNB_ue_s1ap_id == eNB_ue_s1ap_id) {
return ue_ref;
}
}
return NULL;
}
ue_description_t* s1ap_is_ue_mme_id_in_list(uint32_t mme_ue_s1ap_id) {
ue_description_t *ue_ref;
eNB_description_t *eNB_ref;
STAILQ_FOREACH(eNB_ref, &eNB_list_head, eNB_entries)
{
STAILQ_FOREACH(ue_ref, &eNB_ref->ue_list_head, ue_entries)
{
// We fount a matching reference, return it
if (ue_ref->mme_ue_s1ap_id == mme_ue_s1ap_id) {
return ue_ref;
}
}
}
return NULL;
}
ue_description_t* s1ap_is_teid_in_list(uint32_t teid) {
ue_description_t *ue_ref;
eNB_description_t *eNB_ref;
STAILQ_FOREACH(eNB_ref, &eNB_list_head, eNB_entries)
{
STAILQ_FOREACH(ue_ref, &eNB_ref->ue_list_head, ue_entries)
{
// We fount a matching reference, return it
if (ue_ref->teid == teid) {
return ue_ref;
}
}
}
return NULL;
}
eNB_description_t* s1ap_new_eNB(void) {
eNB_description_t *eNB_ref = NULL;
eNB_ref = calloc(1, sizeof(eNB_description_t));
/* Something bad happened during malloc...
* May be we are running out of memory.
* TODO: Notify eNB with a cause like Hardware Failure.
*/
DevAssert(eNB_ref != NULL);
// Update number of eNB associated
nb_eNB_associated++;
STAILQ_INIT(&eNB_ref->ue_list_head);
eNB_ref->nb_ue_associated = 0;
STAILQ_INSERT_TAIL(&eNB_list_head, eNB_ref, eNB_entries);
return eNB_ref;
}
ue_description_t* s1ap_new_ue(uint32_t sctp_assoc_id) {
eNB_description_t *eNB_ref = NULL;
ue_description_t *ue_ref = NULL;
eNB_ref = s1ap_is_eNB_assoc_id_in_list(sctp_assoc_id);
DevAssert(eNB_ref != NULL);
ue_ref = calloc(1, sizeof(ue_description_t));
/* Something bad happened during malloc...
* May be we are running out of memory.
* TODO: Notify eNB with a cause like Hardware Failure.
*/
DevAssert(ue_ref != NULL);
ue_ref->eNB = eNB_ref;
// Increment number of UE
eNB_ref->nb_ue_associated++;
STAILQ_INSERT_TAIL(&eNB_ref->ue_list_head, ue_ref, ue_entries);
return ue_ref;
}
void s1ap_remove_ue(ue_description_t *ue_ref)
{
eNB_description_t *eNB_ref;
/* NULL reference... */
if (ue_ref == NULL) return;
eNB_ref = ue_ref->eNB;
/* Updating number of UE */
eNB_ref->nb_ue_associated--;
/* Remove any attached timer */
// s1ap_timer_remove_ue(ue_ref->mme_ue_s1ap_id);
/* Freeing memory */
free(ue_ref);
}
void s1ap_remove_eNB(eNB_description_t *eNB_ref)
{
ue_description_t *ue_ref;
if (eNB_ref == NULL) return;
while (!STAILQ_EMPTY(&eNB_ref->ue_list_head))
{
ue_ref = STAILQ_FIRST(&eNB_ref->ue_list_head);
eNB_ref->nb_ue_associated--;
STAILQ_REMOVE_HEAD(&eNB_ref->ue_list_head, ue_entries);
free(ue_ref);
}
STAILQ_REMOVE(&eNB_list_head, eNB_ref, eNB_description_s, eNB_entries);
free(eNB_ref);
nb_eNB_associated--;
}
#endif