/******************************************************************************* 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 . 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 #include #include #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; int hss_associated = 0; 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->ittiMsg.sctpInit.port = S1AP_PORT_NUMBER; message_p->ittiMsg.sctpInit.ppid = S1AP_SCTP_PPID; message_p->ittiMsg.sctpInit.ipv4 = 1; message_p->ittiMsg.sctpInit.ipv6 = 0; message_p->ittiMsg.sctpInit.nb_ipv4_addr = 1; message_p->ittiMsg.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->ittiMsg.sctpInit.nb_ipv6_addr = 0; message_p->ittiMsg.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 ACTIVATE_MESSAGE: { hss_associated = 1; } break; case SCTP_DATA_IND: { /* New message received from SCTP layer. * Decode and handle it. */ s1ap_message message; memset((void *)&message, 0, sizeof(s1ap_message)); /* Invoke S1AP message decoder */ if (s1ap_mme_decode_pdu(&message, SCTP_DATA_IND(received_message_p).buffer, SCTP_DATA_IND(received_message_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(SCTP_DATA_IND(received_message_p).assoc_id, SCTP_DATA_IND(received_message_p).stream, &message); } /* Free received PDU array */ free(SCTP_DATA_IND(received_message_p).buffer); } break; /* SCTP layer notifies S1AP of disconnection of a peer. */ case SCTP_CLOSE_ASSOCIATION: { s1ap_handle_sctp_deconnection(SCTP_CLOSE_ASSOCIATION(received_message_p).assoc_id); } break; case SCTP_NEW_ASSOCIATION: { s1ap_handle_new_association(&received_message_p->ittiMsg.sctp_new_peer); } break; case NAS_DOWNLINK_DATA_REQ: { /* New message received from NAS task. * This corresponds to a S1AP downlink nas transport message. */ s1ap_generate_downlink_nas_transport(NAS_DL_DATA_REQ(received_message_p).UEid, NAS_DL_DATA_REQ(received_message_p).nasMsg.data, NAS_DL_DATA_REQ(received_message_p).nasMsg.length); } break; case S1AP_UE_CONTEXT_RELEASE_COMMAND: { s1ap_handle_ue_context_release_command(&received_message_p->ittiMsg.s1ap_ue_context_release_command); } break; #if defined(DISABLE_USE_NAS) case NAS_ATTACH_ACCEPT: { s1ap_handle_attach_accepted(&received_message_p->ittiMsg.nas_attach_accept); } break; #else // handled by MME_APP know //case NAS_CONNECTION_ESTABLISHMENT_CNF: { // s1ap_handle_conn_est_cnf(&NAS_CONNECTION_ESTABLISHMENT_CNF(received_message_p)); //} break; case MME_APP_CONNECTION_ESTABLISHMENT_CNF: { s1ap_handle_conn_est_cnf(&MME_APP_CONNECTION_ESTABLISHMENT_CNF(received_message_p)); } break; #endif case TIMER_HAS_EXPIRED: { s1ap_handle_timer_expiry(&received_message_p->ittiMsg.timer_has_expired); } break; case TERMINATE_MESSAGE: { itti_exit_task(); } break; default: { S1AP_DEBUG("Unkwnon message ID %d:%s\n", ITTI_MSG_ID(received_message_p), ITTI_MSG_NAME(received_message_p)); } break; } itti_free(ITTI_MSG_ORIGIN_ID(received_message_p), 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(const 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 found 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(const 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 found 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, const 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(const 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 found 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(const 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 found 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(const 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); STAILQ_REMOVE(&eNB_ref->ue_list_head, ue_ref, ue_description_s, ue_entries); eNB_ref->nb_ue_associated--; /* Freeing memory */ free(ue_ref); ue_ref = NULL; } 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); eNB_ref = NULL; nb_eNB_associated--; } #endif