git-svn-id: http://svn.eurecom.fr/openair4G/trunk@4414 818b1a75-f10b-46b9-bf7c-635c3b92a50f
414 lines
12 KiB
C
414 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);
|
|
DevAssert(received_message_p != NULL);
|
|
|
|
switch (ITTI_MSG_ID(received_message_p))
|
|
{
|
|
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 %d:%s\n",
|
|
ITTI_MSG_ID(received_message_p), ITTI_MSG_NAME(received_message_p));
|
|
} 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
|