No need for GTPURH kernel module (work done in GTPUAH, will change module name later). still classification with iptables mark, conntrack. Tested with EPC local to eNB, test with remote EPC after this commit. Added srb1 parameters in enb config file, Correction of mscgen traces git-svn-id: http://svn.eurecom.fr/openair4G/trunk@7413 818b1a75-f10b-46b9-bf7c-635c3b92a50f
1194 lines
45 KiB
C
1194 lines
45 KiB
C
/*******************************************************************************
|
|
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.
|
|
|
|
*******************************************************************************/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
|
|
#include "mme_config.h"
|
|
#include "assertions.h"
|
|
#include "conversions.h"
|
|
|
|
#include "s1ap_common.h"
|
|
#include "s1ap_ies_defs.h"
|
|
#include "s1ap_mme_encoder.h"
|
|
#include "s1ap_mme_handlers.h"
|
|
#include "s1ap_mme_nas_procedures.h"
|
|
|
|
#include "s1ap_mme_itti_messaging.h"
|
|
|
|
#include "s1ap_mme.h"
|
|
#include "s1ap_mme_ta.h"
|
|
#include "msc.h"
|
|
|
|
static int s1ap_generate_s1_setup_response(eNB_description_t *eNB_association);
|
|
static int s1ap_mme_generate_ue_context_release_command(
|
|
ue_description_t *ue_ref);
|
|
|
|
//Forward declaration
|
|
struct s1ap_message_s;
|
|
|
|
/* Handlers matrix. Only mme related procedures present here.
|
|
*/
|
|
s1ap_message_decoded_callback messages_callback[][3] = {
|
|
{ 0, 0, 0 }, /* HandoverPreparation */
|
|
{ 0, 0, 0 }, /* HandoverResourceAllocation */
|
|
{ 0, 0, 0 }, /* HandoverNotification */
|
|
{ s1ap_mme_handle_path_switch_request, 0, 0 }, /* PathSwitchRequest */
|
|
{ 0, 0, 0 }, /* HandoverCancel */
|
|
{ 0, 0, 0 }, /* E_RABSetup */
|
|
{ 0, 0, 0 }, /* E_RABModify */
|
|
{ 0, 0, 0 }, /* E_RABRelease */
|
|
{ 0, 0, 0 }, /* E_RABReleaseIndication */
|
|
{
|
|
0, s1ap_mme_handle_initial_context_setup_response,
|
|
s1ap_mme_handle_initial_context_setup_failure
|
|
}, /* InitialContextSetup */
|
|
{ 0, 0, 0 }, /* Paging */
|
|
{ 0, 0, 0 }, /* downlinkNASTransport */
|
|
{ s1ap_mme_handle_initial_ue_message, 0, 0 }, /* initialUEMessage */
|
|
{ s1ap_mme_handle_uplink_nas_transport, 0, 0 }, /* uplinkNASTransport */
|
|
{ 0, 0, 0 }, /* Reset */
|
|
{ 0, 0, 0 }, /* ErrorIndication */
|
|
{ s1ap_mme_handle_nas_non_delivery, 0, 0 }, /* NASNonDeliveryIndication */
|
|
{ s1ap_mme_handle_s1_setup_request, 0, 0 }, /* S1Setup */
|
|
{ s1ap_mme_handle_ue_context_release_request, 0, 0 }, /* UEContextReleaseRequest */
|
|
{ 0, 0, 0 }, /* DownlinkS1cdma2000tunneling */
|
|
{ 0, 0, 0 }, /* UplinkS1cdma2000tunneling */
|
|
{ 0, 0, 0 }, /* UEContextModification */
|
|
{ s1ap_mme_handle_ue_cap_indication, 0, 0 }, /* UECapabilityInfoIndication*/
|
|
{
|
|
s1ap_mme_handle_ue_context_release_request,
|
|
s1ap_mme_handle_ue_context_release_complete, 0
|
|
}, /* UEContextRelease*/
|
|
{ 0, 0, 0 }, /* eNBStatusTransfer*/
|
|
{ 0, 0, 0 }, /* MMEStatusTransfer*/
|
|
{ 0, 0, 0 }, /* DeactivateTrace*/
|
|
{ 0, 0, 0 }, /* TraceStart*/
|
|
{ 0, 0, 0 }, /* TraceFailureIndication*/
|
|
{ 0, 0, 0 }, /* ENBConfigurationUpdate*/
|
|
{ 0, 0, 0 }, /* MMEConfigurationUpdate*/
|
|
{ 0, 0, 0 }, /* LocationReportingControl*/
|
|
{ 0, 0, 0 }, /* LocationReportingFailureIndication*/
|
|
{ 0, 0, 0 }, /* LocationReport*/
|
|
{ 0, 0, 0 }, /* OverloadStart*/
|
|
{ 0, 0, 0 }, /* OverloadStop*/
|
|
{ 0, 0, 0 }, /* WriteReplaceWarning*/
|
|
{ 0, 0, 0 }, /* eNBDirectInformationTransfer*/
|
|
{ 0, 0, 0 }, /* MMEDirectInformationTransfer*/
|
|
{ 0, 0, 0 }, /* PrivateMessage*/
|
|
{ 0, 0, 0 }, /* eNBConfigurationTransfer*/
|
|
{ 0, 0, 0 }, /* MMEConfigurationTransfer*/
|
|
{ 0, 0, 0 }, /* CellTrafficTrace */
|
|
#if defined(UPDATE_RELEASE_9)
|
|
{ 0, 0, 0 }, /* Kill */
|
|
{ 0, 0, 0 }, /* DownlinkUEAssociatedLPPaTransport */
|
|
{ 0, 0, 0 }, /* UplinkUEAssociatedLPPaTransport */
|
|
{ 0, 0, 0 }, /* DownlinkNonUEAssociatedLPPaTransport */
|
|
{ 0, 0, 0 }, /* UplinkNonUEAssociatedLPPaTransport */
|
|
#endif
|
|
};
|
|
|
|
const char *s1ap_direction2String[] = {
|
|
"", /* Nothing */
|
|
"Originating message", /* originating message */
|
|
"Successfull outcome", /* successfull outcome */
|
|
"UnSuccessfull outcome", /* successfull outcome */
|
|
};
|
|
|
|
//------------------------------------------------------------------------------
|
|
int s1ap_mme_handle_message(uint32_t assoc_id, uint32_t stream,
|
|
struct s1ap_message_s *message)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
/* Checking procedure Code and direction of message */
|
|
if ((message->procedureCode > (sizeof(messages_callback) / (3 * sizeof(
|
|
s1ap_message_decoded_callback)))) ||
|
|
(message->direction > S1AP_PDU_PR_unsuccessfulOutcome)) {
|
|
|
|
S1AP_DEBUG("[SCTP %d] Either procedureCode %d or direction %d exceed expected\n",
|
|
assoc_id, (int)message->procedureCode, (int)message->direction);
|
|
return -1;
|
|
}
|
|
|
|
/* No handler present.
|
|
* This can mean not implemented or no procedure for eNB (wrong message).
|
|
*/
|
|
if (messages_callback[message->procedureCode][message->direction-1] == NULL) {
|
|
S1AP_DEBUG("[SCTP %d] No handler for procedureCode %d in %s\n", assoc_id,
|
|
(int)message->procedureCode,
|
|
s1ap_direction2String[(int)message->direction]);
|
|
return -2;
|
|
}
|
|
|
|
/* Calling the right handler */
|
|
return (*messages_callback[message->procedureCode][message->direction-1])(
|
|
assoc_id, stream, message);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int s1ap_mme_set_cause(S1ap_Cause_t *cause_p, S1ap_Cause_PR cause_type, long cause_value)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
DevAssert(cause_p != NULL);
|
|
|
|
cause_p->present = cause_type;
|
|
|
|
switch(cause_type) {
|
|
case S1ap_Cause_PR_radioNetwork:
|
|
cause_p->choice.misc = cause_value;
|
|
break;
|
|
|
|
case S1ap_Cause_PR_transport:
|
|
cause_p->choice.transport = cause_value;
|
|
break;
|
|
|
|
case S1ap_Cause_PR_nas:
|
|
cause_p->choice.nas = cause_value;
|
|
break;
|
|
|
|
case S1ap_Cause_PR_protocol:
|
|
cause_p->choice.protocol = cause_value;
|
|
break;
|
|
|
|
case S1ap_Cause_PR_misc:
|
|
cause_p->choice.misc = cause_value;
|
|
break;
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int s1ap_mme_generate_s1_setup_failure(
|
|
uint32_t assoc_id, S1ap_Cause_PR cause_type, long cause_value,
|
|
long time_to_wait)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
uint8_t *buffer_p;
|
|
uint32_t length;
|
|
s1ap_message message;
|
|
S1ap_S1SetupFailureIEs_t *s1_setup_failure_p;
|
|
|
|
memset(&message, 0, sizeof(s1ap_message));
|
|
|
|
s1_setup_failure_p = &message.msg.s1ap_S1SetupFailureIEs;
|
|
|
|
message.procedureCode = S1ap_ProcedureCode_id_S1Setup;
|
|
message.direction = S1AP_PDU_PR_unsuccessfulOutcome;
|
|
|
|
s1ap_mme_set_cause(&s1_setup_failure_p->cause, cause_type, cause_value);
|
|
|
|
/* Include the optional field time to wait only if the value is > -1 */
|
|
if (time_to_wait > -1) {
|
|
s1_setup_failure_p->presenceMask |= S1AP_S1SETUPFAILUREIES_TIMETOWAIT_PRESENT;
|
|
s1_setup_failure_p->timeToWait = time_to_wait;
|
|
}
|
|
|
|
if (s1ap_mme_encode_pdu(&message, &buffer_p, &length) < 0) {
|
|
S1AP_ERROR("Failed to encode s1 setup failure\n");
|
|
return -1;
|
|
}
|
|
MSC_LOG_TX_MESSAGE(
|
|
MSC_S1AP_MME,
|
|
MSC_S1AP_ENB,
|
|
NULL,0,
|
|
"0 S1Setup/unsuccessfulOutcome assoc_id %u cause %u value %u",
|
|
assoc_id, cause_type, cause_value);
|
|
return s1ap_mme_itti_send_sctp_request(buffer_p, length, assoc_id, 0);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//************************** Management procedures ***************************//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//------------------------------------------------------------------------------
|
|
int s1ap_mme_handle_s1_setup_request(uint32_t assoc_id, uint32_t stream,
|
|
struct s1ap_message_s *message)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
if (hss_associated) {
|
|
S1ap_S1SetupRequestIEs_t *s1SetupRequest_p;
|
|
eNB_description_t *eNB_association;
|
|
uint32_t eNB_id = 0;
|
|
char *eNB_name = NULL;
|
|
int ta_ret;
|
|
uint16_t max_enb_connected;
|
|
|
|
DevAssert(message != NULL);
|
|
|
|
s1SetupRequest_p = &message->msg.s1ap_S1SetupRequestIEs;
|
|
|
|
/* We received a new valid S1 Setup Request on a stream != 0.
|
|
* This should not happen -> reject eNB s1 setup request.
|
|
*/
|
|
MSC_LOG_RX_MESSAGE(
|
|
MSC_S1AP_MME,
|
|
MSC_S1AP_ENB,
|
|
NULL,0,
|
|
"0 S1Setup/%s assoc_id %u stream %u",
|
|
s1ap_direction2String[message->direction],
|
|
assoc_id, stream);
|
|
|
|
if (stream != 0) {
|
|
S1AP_ERROR("Received new s1 setup request on stream != 0\n");
|
|
/* Send a s1 setup failure with protocol cause unspecified */
|
|
return s1ap_mme_generate_s1_setup_failure(assoc_id, S1ap_Cause_PR_protocol,
|
|
S1ap_CauseProtocol_unspecified, -1);
|
|
}
|
|
|
|
S1AP_DEBUG("New s1 setup request incoming from ");
|
|
|
|
if (s1SetupRequest_p->presenceMask & S1AP_S1SETUPREQUESTIES_ENBNAME_PRESENT) {
|
|
S1AP_DEBUG("%*s ", s1SetupRequest_p->eNBname.size, s1SetupRequest_p->eNBname.buf);
|
|
eNB_name = (char *)s1SetupRequest_p->eNBname.buf;
|
|
}
|
|
|
|
if (s1SetupRequest_p->global_ENB_ID.eNB_ID.present == S1ap_ENB_ID_PR_homeENB_ID) {
|
|
// Home eNB ID = 28 bits
|
|
uint8_t *eNB_id_buf =
|
|
s1SetupRequest_p->global_ENB_ID.eNB_ID.choice.homeENB_ID.buf;
|
|
|
|
if (s1SetupRequest_p->global_ENB_ID.eNB_ID.choice.macroENB_ID.size != 28) {
|
|
//TODO: handle case were size != 28 -> notify ? reject ?
|
|
}
|
|
|
|
eNB_id = (eNB_id_buf[0] << 20) + (eNB_id_buf[1] << 12) +
|
|
(eNB_id_buf[2] << 4) + ((eNB_id_buf[3] & 0xf0) >> 4);
|
|
S1AP_DEBUG("home eNB id: %07x\n", eNB_id);
|
|
} else {
|
|
// Macro eNB = 20 bits
|
|
uint8_t *eNB_id_buf =
|
|
s1SetupRequest_p->global_ENB_ID.eNB_ID.choice.macroENB_ID.buf;
|
|
|
|
if (s1SetupRequest_p->global_ENB_ID.eNB_ID.choice.macroENB_ID.size != 20) {
|
|
//TODO: handle case were size != 20 -> notify ? reject ?
|
|
}
|
|
|
|
eNB_id = (eNB_id_buf[0] << 12) + (eNB_id_buf[1] << 4) + ((
|
|
eNB_id_buf[2] & 0xf0) >> 4);
|
|
S1AP_DEBUG("macro eNB id: %05x\n", eNB_id);
|
|
}
|
|
|
|
config_read_lock(&mme_config);
|
|
max_enb_connected = mme_config.max_eNBs;
|
|
config_unlock(&mme_config);
|
|
|
|
if (nb_eNB_associated == max_enb_connected) {
|
|
S1AP_ERROR("There is too much eNB connected to MME, rejecting the association\n");
|
|
S1AP_DEBUG("Connected = %d, maximum allowed = %d\n", nb_eNB_associated,
|
|
max_enb_connected);
|
|
|
|
/* Send an overload cause... */
|
|
return s1ap_mme_generate_s1_setup_failure(assoc_id, S1ap_Cause_PR_misc,
|
|
S1ap_CauseMisc_control_processing_overload,
|
|
S1ap_TimeToWait_v20s);
|
|
}
|
|
|
|
/* If none of the provided PLMNs/TAC match the one configured in MME,
|
|
* the s1 setup should be rejected with a cause set to Unknown PLMN.
|
|
*/
|
|
ta_ret = s1ap_mme_compare_ta_lists(&s1SetupRequest_p->supportedTAs);
|
|
|
|
/* eNB and MME have no common PLMN */
|
|
if (ta_ret != TA_LIST_RET_OK) {
|
|
S1AP_ERROR("No Common PLMN with eNB, generate_s1_setup_failure\n");
|
|
return s1ap_mme_generate_s1_setup_failure(assoc_id, S1ap_Cause_PR_misc,
|
|
S1ap_CauseMisc_unknown_PLMN,
|
|
S1ap_TimeToWait_v20s);
|
|
}
|
|
|
|
S1AP_DEBUG("Adding eNB to the list of served eNBs\n");
|
|
|
|
if ((eNB_association = s1ap_is_eNB_id_in_list(eNB_id)) == NULL) {
|
|
/* eNB has not been fount in list of associated eNB,
|
|
* Add it to the tail of list and initialize data */
|
|
if ((eNB_association = s1ap_is_eNB_assoc_id_in_list(assoc_id)) == NULL) {
|
|
/* ?? */
|
|
return -1;
|
|
} else {
|
|
eNB_association->s1_state = S1AP_RESETING;
|
|
eNB_association->eNB_id = eNB_id;
|
|
eNB_association->default_paging_drx = s1SetupRequest_p->defaultPagingDRX;
|
|
|
|
if (eNB_name != NULL) {
|
|
memcpy(eNB_association->eNB_name, s1SetupRequest_p->eNBname.buf,
|
|
s1SetupRequest_p->eNBname.size);
|
|
eNB_association->eNB_name[s1SetupRequest_p->eNBname.size] = '\0';
|
|
}
|
|
}
|
|
} else {
|
|
eNB_association->s1_state = S1AP_RESETING;
|
|
|
|
/* eNB has been fount in list, consider the s1 setup request as a reset connection,
|
|
* reseting any previous UE state if sctp association is != than the previous one */
|
|
if (eNB_association->sctp_assoc_id != assoc_id) {
|
|
S1ap_S1SetupFailureIEs_t s1SetupFailure;
|
|
|
|
memset(&s1SetupFailure, 0, sizeof(s1SetupFailure));
|
|
|
|
/* Send an overload cause... */
|
|
s1SetupFailure.cause.present = S1ap_Cause_PR_misc; //TODO: send the right cause
|
|
s1SetupFailure.cause.choice.misc = S1ap_CauseMisc_control_processing_overload;
|
|
S1AP_ERROR("Rejeting s1 setup request as eNB id %d is already associated to an active sctp association"
|
|
"Previous known: %d, new one: %d\n",
|
|
eNB_id, eNB_association->sctp_assoc_id, assoc_id);
|
|
// s1ap_mme_encode_s1setupfailure(&s1SetupFailure,
|
|
// receivedMessage->msg.s1ap_sctp_new_msg_ind.assocId);
|
|
return -1;
|
|
}
|
|
|
|
/* TODO: call the reset procedure */
|
|
}
|
|
|
|
s1ap_dump_eNB(eNB_association);
|
|
return s1ap_generate_s1_setup_response(eNB_association);
|
|
} else {
|
|
/* Can not process the request, MME is not connected to HSS */
|
|
S1AP_ERROR("Rejecting s1 setup request Can not process the request, MME is not connected to HSS\n");
|
|
return s1ap_mme_generate_s1_setup_failure(assoc_id, S1ap_Cause_PR_misc,
|
|
S1ap_CauseMisc_unspecified, -1);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static
|
|
int s1ap_generate_s1_setup_response(eNB_description_t *eNB_association)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
int i;
|
|
int enc_rval = 0;
|
|
S1ap_S1SetupResponseIEs_t *s1_setup_response_p;
|
|
S1ap_ServedGUMMEIsItem_t servedGUMMEI;
|
|
s1ap_message message;
|
|
uint8_t *buffer;
|
|
uint32_t length;
|
|
|
|
DevAssert(eNB_association != NULL);
|
|
|
|
// Generating response
|
|
memset(&message, 0, sizeof(s1ap_message));
|
|
memset(&servedGUMMEI, 0, sizeof(servedGUMMEI));
|
|
|
|
s1_setup_response_p = &message.msg.s1ap_S1SetupResponseIEs;
|
|
|
|
config_read_lock(&mme_config);
|
|
|
|
s1_setup_response_p->relativeMMECapacity = mme_config.relative_capacity;
|
|
|
|
/* Use the gummei parameters provided by configuration */
|
|
for (i = 0; i < mme_config.gummei.nb_plmns; i++) {
|
|
S1ap_PLMNidentity_t *plmn;
|
|
|
|
/* FIXME: free object from list once encoded */
|
|
plmn = calloc(1, sizeof(*plmn));
|
|
MCC_MNC_TO_PLMNID(
|
|
mme_config.gummei.plmn_mcc[i],
|
|
mme_config.gummei.plmn_mnc[i],
|
|
mme_config.gummei.plmn_mnc_len[i],
|
|
plmn);
|
|
ASN_SEQUENCE_ADD(&servedGUMMEI.servedPLMNs.list, plmn);
|
|
}
|
|
|
|
for (i = 0; i < mme_config.gummei.nb_mme_gid; i++) {
|
|
S1ap_MME_Group_ID_t *mme_gid;
|
|
|
|
/* FIXME: free object from list once encoded */
|
|
mme_gid = calloc(1, sizeof(*mme_gid));
|
|
INT16_TO_OCTET_STRING(mme_config.gummei.mme_gid[i], mme_gid);
|
|
ASN_SEQUENCE_ADD(&servedGUMMEI.servedGroupIDs.list, mme_gid);
|
|
}
|
|
|
|
for (i = 0; i < mme_config.gummei.nb_mmec; i++) {
|
|
S1ap_MME_Code_t *mmec;
|
|
|
|
/* FIXME: free object from list once encoded */
|
|
mmec = calloc(1, sizeof(*mmec));
|
|
INT8_TO_OCTET_STRING(mme_config.gummei.mmec[i], mmec);
|
|
ASN_SEQUENCE_ADD(&servedGUMMEI.servedMMECs.list, mmec);
|
|
}
|
|
|
|
config_unlock(&mme_config);
|
|
|
|
/* The MME is only serving E-UTRAN RAT, so the list contains only one element */
|
|
ASN_SEQUENCE_ADD(&s1_setup_response_p->servedGUMMEIs, &servedGUMMEI);
|
|
|
|
message.procedureCode = S1ap_ProcedureCode_id_S1Setup;
|
|
message.direction = S1AP_PDU_PR_successfulOutcome;
|
|
|
|
enc_rval = s1ap_mme_encode_pdu(&message, &buffer, &length);
|
|
|
|
/* Failed to encode s1 setup response... */
|
|
if (enc_rval < 0) {
|
|
S1AP_DEBUG("Removed eNB %d\n", eNB_association->sctp_assoc_id);
|
|
s1ap_remove_eNB(eNB_association);
|
|
} else {
|
|
/* Consider the response as sent. S1AP is ready to accept UE contexts */
|
|
eNB_association->s1_state = S1AP_READY;
|
|
}
|
|
|
|
MSC_LOG_TX_MESSAGE(
|
|
MSC_S1AP_MME,
|
|
MSC_S1AP_ENB,
|
|
NULL,0,
|
|
"0 S1Setup/successfulOutcome assoc_id %u",
|
|
eNB_association->sctp_assoc_id);
|
|
/* Non-UE signalling -> stream 0 */
|
|
return s1ap_mme_itti_send_sctp_request(buffer, length, eNB_association->sctp_assoc_id,
|
|
0);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int s1ap_mme_handle_ue_cap_indication(uint32_t assoc_id, uint32_t stream,
|
|
struct s1ap_message_s *message)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
ue_description_t *ue_ref;
|
|
S1ap_UECapabilityInfoIndicationIEs_t *ue_cap_p;
|
|
|
|
DevAssert(message != NULL);
|
|
|
|
ue_cap_p = &message->msg.s1ap_UECapabilityInfoIndicationIEs;
|
|
|
|
MSC_LOG_RX_MESSAGE(
|
|
MSC_S1AP_MME,
|
|
MSC_S1AP_ENB,
|
|
NULL,0,
|
|
"0 UECapabilityInfoIndication/%s eNB_ue_s1ap_id "S1AP_UE_ID_FMT" mme_ue_s1ap_id "S1AP_UE_ID_FMT" ",
|
|
s1ap_direction2String[message->direction],
|
|
ue_cap_p->eNB_UE_S1AP_ID,
|
|
ue_cap_p->mme_ue_s1ap_id);
|
|
|
|
if ((ue_ref = s1ap_is_ue_mme_id_in_list(ue_cap_p->mme_ue_s1ap_id)) == NULL) {
|
|
S1AP_DEBUG("No UE is attached to this mme UE s1ap id: "S1AP_UE_ID_FMT"\n",
|
|
(uint32_t)ue_cap_p->mme_ue_s1ap_id);
|
|
return -1;
|
|
}
|
|
|
|
if (ue_ref->eNB_ue_s1ap_id != ue_cap_p->eNB_UE_S1AP_ID) {
|
|
S1AP_DEBUG("Mismatch in eNB UE S1AP ID, known: "S1AP_UE_ID_FMT", received: "S1AP_UE_ID_FMT"\n",
|
|
ue_ref->eNB_ue_s1ap_id, (uint32_t)ue_cap_p->eNB_UE_S1AP_ID);
|
|
return -1;
|
|
}
|
|
|
|
/* Just display a warning when message received over wrong stream */
|
|
if (ue_ref->sctp_stream_recv != stream) {
|
|
S1AP_ERROR("Received ue capability indication for "
|
|
"(MME UE S1AP ID/eNB UE S1AP ID) ("S1AP_UE_ID_FMT"/"S1AP_UE_ID_FMT") over wrong stream "
|
|
"expecting %u, received on %u\n",
|
|
(uint32_t)ue_cap_p->mme_ue_s1ap_id, ue_ref->eNB_ue_s1ap_id,
|
|
ue_ref->sctp_stream_recv, stream);
|
|
}
|
|
|
|
/* Forward the ue capabilities to MME application layer */
|
|
{
|
|
MessageDef *message_p;
|
|
s1ap_ue_cap_ind_t *ue_cap_ind_p;
|
|
|
|
message_p = itti_alloc_new_message(TASK_S1AP, S1AP_UE_CAPABILITIES_IND);
|
|
|
|
DevAssert(message_p != NULL);
|
|
|
|
ue_cap_ind_p = &message_p->ittiMsg.s1ap_ue_cap_ind;
|
|
ue_cap_ind_p->eNB_ue_s1ap_id = ue_ref->eNB_ue_s1ap_id;
|
|
ue_cap_ind_p->mme_ue_s1ap_id = ue_ref->mme_ue_s1ap_id;
|
|
|
|
DevCheck(ue_cap_p->ueRadioCapability.size < 100,
|
|
100, ue_cap_p->ueRadioCapability.size, 0);
|
|
|
|
memcpy(ue_cap_ind_p->radio_capabilities, ue_cap_p->ueRadioCapability.buf,
|
|
ue_cap_p->ueRadioCapability.size);
|
|
|
|
ue_cap_ind_p->radio_capabilities_length = ue_cap_p->ueRadioCapability.size;
|
|
|
|
MSC_LOG_TX_MESSAGE(
|
|
MSC_S1AP_MME,
|
|
MSC_MMEAPP_MME,
|
|
NULL,0,
|
|
"0 S1AP_UE_CAPABILITIES_IND eNB_ue_s1ap_id "S1AP_UE_ID_FMT" mme_ue_s1ap_id "S1AP_UE_ID_FMT" len %u",
|
|
ue_cap_ind_p->eNB_ue_s1ap_id,
|
|
ue_cap_ind_p->mme_ue_s1ap_id,
|
|
ue_cap_ind_p->radio_capabilities_length);
|
|
|
|
return itti_send_msg_to_task(TASK_MME_APP, INSTANCE_DEFAULT, message_p);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//******************* Context Management procedures **************************//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//------------------------------------------------------------------------------
|
|
int s1ap_mme_handle_initial_context_setup_response(
|
|
uint32_t assoc_id,
|
|
uint32_t stream,
|
|
struct s1ap_message_s *message)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
|
|
S1ap_InitialContextSetupResponseIEs_t *initialContextSetupResponseIEs_p = NULL;
|
|
S1ap_E_RABSetupItemCtxtSURes_t *eRABSetupItemCtxtSURes_p = NULL;
|
|
ue_description_t *ue_ref = NULL;
|
|
MessageDef *message_p = NULL;
|
|
|
|
initialContextSetupResponseIEs_p = &message->msg.s1ap_InitialContextSetupResponseIEs;
|
|
|
|
MSC_LOG_RX_MESSAGE(
|
|
MSC_S1AP_MME,
|
|
MSC_S1AP_ENB,
|
|
NULL,0,
|
|
"0 InitialContextSetup/%s eNB_ue_s1ap_id "S1AP_UE_ID_FMT" mme_ue_s1ap_id "S1AP_UE_ID_FMT" len %u",
|
|
s1ap_direction2String[message->direction],
|
|
initialContextSetupResponseIEs_p->eNB_UE_S1AP_ID,
|
|
initialContextSetupResponseIEs_p->mme_ue_s1ap_id);
|
|
|
|
if ((ue_ref = s1ap_is_ue_mme_id_in_list(
|
|
(uint32_t)initialContextSetupResponseIEs_p->mme_ue_s1ap_id)) == NULL) {
|
|
S1AP_DEBUG("No UE is attached to this mme UE s1ap id: "S1AP_UE_ID_FMT" %u(10)\n",
|
|
(uint32_t)initialContextSetupResponseIEs_p->mme_ue_s1ap_id,
|
|
(uint32_t)initialContextSetupResponseIEs_p->mme_ue_s1ap_id);
|
|
return -1;
|
|
}
|
|
|
|
if (ue_ref->eNB_ue_s1ap_id !=
|
|
initialContextSetupResponseIEs_p->eNB_UE_S1AP_ID) {
|
|
S1AP_DEBUG("Mismatch in eNB UE S1AP ID, known: "S1AP_UE_ID_FMT" %u(10), received: 0x%06x %u(10)\n",
|
|
ue_ref->eNB_ue_s1ap_id,
|
|
ue_ref->eNB_ue_s1ap_id,
|
|
(uint32_t)initialContextSetupResponseIEs_p->eNB_UE_S1AP_ID,
|
|
(uint32_t)initialContextSetupResponseIEs_p->eNB_UE_S1AP_ID);
|
|
return -1;
|
|
}
|
|
|
|
if (initialContextSetupResponseIEs_p->e_RABSetupListCtxtSURes.s1ap_E_RABSetupItemCtxtSURes.count
|
|
!= 1) {
|
|
S1AP_DEBUG("E-RAB creation has failed\n");
|
|
return -1;
|
|
}
|
|
|
|
ue_ref->s1_ue_state = S1AP_UE_CONNECTED;
|
|
|
|
message_p = itti_alloc_new_message(TASK_S1AP, MME_APP_INITIAL_CONTEXT_SETUP_RSP);
|
|
|
|
AssertFatal(message_p != NULL,"itti_alloc_new_message Failed");
|
|
memset((void*)&message_p->ittiMsg.mme_app_initial_context_setup_rsp,
|
|
0,
|
|
sizeof(mme_app_initial_context_setup_rsp_t));
|
|
|
|
/* Bad, very bad cast... */
|
|
eRABSetupItemCtxtSURes_p = (S1ap_E_RABSetupItemCtxtSURes_t *)
|
|
initialContextSetupResponseIEs_p->e_RABSetupListCtxtSURes.s1ap_E_RABSetupItemCtxtSURes.array[0];
|
|
|
|
|
|
MME_APP_INITIAL_CONTEXT_SETUP_RSP(message_p).mme_ue_s1ap_id = ue_ref->mme_ue_s1ap_id;
|
|
MME_APP_INITIAL_CONTEXT_SETUP_RSP(message_p).eps_bearer_id = eRABSetupItemCtxtSURes_p->e_RAB_ID ;
|
|
MME_APP_INITIAL_CONTEXT_SETUP_RSP(message_p).bearer_s1u_enb_fteid.ipv4 = 1; // TO DO
|
|
MME_APP_INITIAL_CONTEXT_SETUP_RSP(message_p).bearer_s1u_enb_fteid.ipv6 = 0; // TO DO
|
|
MME_APP_INITIAL_CONTEXT_SETUP_RSP(message_p).bearer_s1u_enb_fteid.interface_type = S1_U_ENODEB_GTP_U;
|
|
MME_APP_INITIAL_CONTEXT_SETUP_RSP(message_p).bearer_s1u_enb_fteid.teid = htonl(*((uint32_t *)eRABSetupItemCtxtSURes_p->gTP_TEID.buf));
|
|
memcpy(&MME_APP_INITIAL_CONTEXT_SETUP_RSP(message_p).bearer_s1u_enb_fteid.ipv4_address,
|
|
eRABSetupItemCtxtSURes_p->transportLayerAddress.buf,
|
|
4);
|
|
|
|
|
|
MSC_LOG_TX_MESSAGE(
|
|
MSC_S1AP_MME,
|
|
MSC_MMEAPP_MME,
|
|
NULL,0,
|
|
"0 MME_APP_INITIAL_CONTEXT_SETUP_RSP mme_ue_s1ap_id "S1AP_UE_ID_FMT" ebi %u s1u enb teid %u",
|
|
MME_APP_INITIAL_CONTEXT_SETUP_RSP(message_p).mme_ue_s1ap_id,
|
|
MME_APP_INITIAL_CONTEXT_SETUP_RSP(message_p).eps_bearer_id,
|
|
MME_APP_INITIAL_CONTEXT_SETUP_RSP(message_p).bearer_s1u_enb_fteid.teid);
|
|
|
|
return itti_send_msg_to_task(TASK_MME_APP, INSTANCE_DEFAULT, message_p);
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
int s1ap_mme_handle_ue_context_release_request(uint32_t assoc_id,
|
|
uint32_t stream, struct s1ap_message_s *message)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
S1ap_UEContextReleaseRequestIEs_t *ueContextReleaseRequest_p = NULL;
|
|
ue_description_t *ue_ref = NULL;
|
|
MessageDef *message_p = NULL;
|
|
|
|
ueContextReleaseRequest_p = &message->msg.s1ap_UEContextReleaseRequestIEs;
|
|
|
|
MSC_LOG_RX_MESSAGE(
|
|
MSC_S1AP_MME,
|
|
MSC_S1AP_ENB,
|
|
NULL,0,
|
|
"0 UEContextReleaseRequest/%s eNB_ue_s1ap_id "S1AP_UE_ID_FMT" mme_ue_s1ap_id "S1AP_UE_ID_FMT" len %u",
|
|
s1ap_direction2String[message->direction],
|
|
ueContextReleaseRequest_p->eNB_UE_S1AP_ID,
|
|
ueContextReleaseRequest_p->mme_ue_s1ap_id);
|
|
|
|
/* The UE context release procedure is initiated if the cause is != than user inactivity.
|
|
* TS36.413 #8.3.2.2.
|
|
*/
|
|
if (ueContextReleaseRequest_p->cause.present == S1ap_Cause_PR_radioNetwork) {
|
|
if (ueContextReleaseRequest_p->cause.choice.radioNetwork ==
|
|
S1ap_CauseRadioNetwork_user_inactivity) {
|
|
S1AP_DEBUG("UE_CONTEXT_RELEASE_REQUEST ignored, cause user inactivity\n");
|
|
MSC_LOG_EVENT(
|
|
MSC_S1AP_MME,
|
|
"0 UE_CONTEXT_RELEASE_REQUEST ignored, cause user inactivity",
|
|
ueContextReleaseRequest_p->mme_ue_s1ap_id);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if ((ue_ref = s1ap_is_ue_mme_id_in_list(
|
|
ueContextReleaseRequest_p->mme_ue_s1ap_id)) == NULL) {
|
|
/* MME doesn't know the MME UE S1AP ID provided.
|
|
* TODO
|
|
*/
|
|
S1AP_DEBUG("UE_CONTEXT_RELEASE_REQUEST ignored cause could not get context with mme_ue_s1ap_id "S1AP_UE_ID_FMT" %u(10)\n",
|
|
ueContextReleaseRequest_p->mme_ue_s1ap_id,
|
|
ueContextReleaseRequest_p->mme_ue_s1ap_id);
|
|
MSC_LOG_EVENT(
|
|
MSC_S1AP_MME,
|
|
"0 UE_CONTEXT_RELEASE_REQUEST ignored, no context mme_ue_s1ap_id "S1AP_UE_ID_FMT" ",
|
|
ueContextReleaseRequest_p->mme_ue_s1ap_id);
|
|
return -1;
|
|
} else {
|
|
if (ue_ref->eNB_ue_s1ap_id == (ueContextReleaseRequest_p->eNB_UE_S1AP_ID &
|
|
0x00ffffff)) {
|
|
/* Both eNB UE S1AP ID and MME UE S1AP ID match.
|
|
* -> Send a UE context Release Command to eNB.
|
|
* TODO
|
|
*/
|
|
//s1ap_mme_generate_ue_context_release_command(ue_ref);
|
|
// UE context will be removed when receiving UE_CONTEXT_RELEASE_COMPLETE
|
|
|
|
message_p = itti_alloc_new_message(TASK_S1AP, S1AP_UE_CONTEXT_RELEASE_REQ);
|
|
|
|
AssertFatal(message_p != NULL,"itti_alloc_new_message Failed");
|
|
memset((void*)&message_p->ittiMsg.s1ap_ue_context_release_req,
|
|
0,
|
|
sizeof(s1ap_ue_context_release_req_t));
|
|
|
|
S1AP_UE_CONTEXT_RELEASE_REQ(message_p).mme_ue_s1ap_id = ue_ref->mme_ue_s1ap_id;
|
|
|
|
MSC_LOG_TX_MESSAGE(
|
|
MSC_S1AP_MME,
|
|
MSC_MMEAPP_MME,
|
|
NULL,0,
|
|
"0 S1AP_UE_CONTEXT_RELEASE_REQ mme_ue_s1ap_id "S1AP_UE_ID_FMT" ",
|
|
S1AP_UE_CONTEXT_RELEASE_REQ(message_p).mme_ue_s1ap_id);
|
|
|
|
return itti_send_msg_to_task(TASK_MME_APP, INSTANCE_DEFAULT, message_p);
|
|
|
|
} else {
|
|
// TODO
|
|
S1AP_DEBUG("UE_CONTEXT_RELEASE_REQUEST ignored, cause mismatch eNB_ue_s1ap_id: ctxt "S1AP_UE_ID_FMT" != request "S1AP_UE_ID_FMT" ",
|
|
ue_ref->eNB_ue_s1ap_id, ueContextReleaseRequest_p->eNB_UE_S1AP_ID);
|
|
MSC_LOG_EVENT(
|
|
MSC_S1AP_MME,
|
|
"0 UE_CONTEXT_RELEASE_REQUEST ignored, cause mismatch eNB_ue_s1ap_id: ctxt "S1AP_UE_ID_FMT" != request "S1AP_UE_ID_FMT" ",
|
|
ue_ref->eNB_ue_s1ap_id, ueContextReleaseRequest_p->eNB_UE_S1AP_ID);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static int s1ap_mme_generate_ue_context_release_command(
|
|
ue_description_t *ue_ref)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
uint8_t *buffer;
|
|
uint32_t length;
|
|
|
|
s1ap_message message;
|
|
S1ap_UEContextReleaseCommandIEs_t *ueContextReleaseCommandIEs_p;
|
|
|
|
if (ue_ref == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
memset(&message, 0, sizeof(s1ap_message));
|
|
|
|
message.procedureCode = S1ap_ProcedureCode_id_UEContextRelease;
|
|
message.direction = S1AP_PDU_PR_initiatingMessage;
|
|
|
|
ueContextReleaseCommandIEs_p = &message.msg.s1ap_UEContextReleaseCommandIEs;
|
|
|
|
/* Fill in ID pair */
|
|
ueContextReleaseCommandIEs_p->uE_S1AP_IDs.present = S1ap_UE_S1AP_IDs_PR_uE_S1AP_ID_pair;
|
|
ueContextReleaseCommandIEs_p->uE_S1AP_IDs.choice.uE_S1AP_ID_pair.mME_UE_S1AP_ID =
|
|
ue_ref->mme_ue_s1ap_id;
|
|
ueContextReleaseCommandIEs_p->uE_S1AP_IDs.choice.uE_S1AP_ID_pair.eNB_UE_S1AP_ID =
|
|
ue_ref->eNB_ue_s1ap_id;
|
|
ueContextReleaseCommandIEs_p->uE_S1AP_IDs.choice.uE_S1AP_ID_pair.iE_Extensions =
|
|
NULL;
|
|
|
|
ueContextReleaseCommandIEs_p->cause.present = S1ap_Cause_PR_radioNetwork;
|
|
ueContextReleaseCommandIEs_p->cause.choice.radioNetwork =
|
|
S1ap_CauseRadioNetwork_release_due_to_eutran_generated_reason;
|
|
|
|
if (s1ap_mme_encode_pdu(&message, &buffer, &length) < 0) {
|
|
MSC_LOG_EVENT(
|
|
MSC_S1AP_MME,
|
|
"0 UEContextRelease/initiatingMessage eNB_ue_s1ap_id "S1AP_UE_ID_FMT" mme_ue_s1ap_id "S1AP_UE_ID_FMT" encoding failed",
|
|
ue_ref->eNB_ue_s1ap_id,
|
|
ue_ref->mme_ue_s1ap_id);
|
|
return -1;
|
|
}
|
|
MSC_LOG_TX_MESSAGE(
|
|
MSC_S1AP_MME,
|
|
MSC_S1AP_ENB,
|
|
NULL,0,
|
|
"0 UEContextRelease/initiatingMessage eNB_ue_s1ap_id "S1AP_UE_ID_FMT" mme_ue_s1ap_id "S1AP_UE_ID_FMT"",
|
|
ue_ref->eNB_ue_s1ap_id,ue_ref->mme_ue_s1ap_id);
|
|
|
|
return s1ap_mme_itti_send_sctp_request(buffer, length, ue_ref->eNB->sctp_assoc_id,
|
|
ue_ref->sctp_stream_send);
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
int s1ap_handle_ue_context_release_command(
|
|
const s1ap_ue_context_release_command_t * const ue_context_release_command_pP)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
ue_description_t *ue_ref_p = NULL;
|
|
|
|
if ((ue_ref_p = s1ap_is_ue_mme_id_in_list(
|
|
ue_context_release_command_pP->mme_ue_s1ap_id)) == NULL) {
|
|
/* MME doesn't know the MME UE S1AP ID provided.
|
|
* TODO
|
|
*/
|
|
S1AP_DEBUG("UE_CONTEXT_RELEASE_COMMAND ignored cause could not get context with mme_ue_s1ap_id "S1AP_UE_ID_FMT" %u(10)\n",
|
|
ue_context_release_command_pP->mme_ue_s1ap_id,
|
|
ue_context_release_command_pP->mme_ue_s1ap_id);
|
|
MSC_LOG_EVENT(
|
|
MSC_S1AP_MME,
|
|
"0 UE_CONTEXT_RELEASE_COMMAND ignored, no context mme_ue_s1ap_id",
|
|
ue_context_release_command_pP->mme_ue_s1ap_id);
|
|
return -1;
|
|
} else {
|
|
return s1ap_mme_generate_ue_context_release_command(ue_ref_p);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int s1ap_mme_handle_ue_context_release_complete(uint32_t assoc_id,
|
|
uint32_t stream, struct s1ap_message_s *message)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
S1ap_UEContextReleaseCompleteIEs_t *ueContextReleaseComplete_p;
|
|
ue_description_t *ue_ref = NULL;
|
|
|
|
ueContextReleaseComplete_p = &message->msg.s1ap_UEContextReleaseCompleteIEs;
|
|
MSC_LOG_RX_MESSAGE(
|
|
MSC_S1AP_MME,
|
|
MSC_S1AP_ENB,
|
|
NULL,0,
|
|
"0 UEContextRelease/%s eNB_ue_s1ap_id "S1AP_UE_ID_FMT" mme_ue_s1ap_id "S1AP_UE_ID_FMT" len %u",
|
|
s1ap_direction2String[message->direction],
|
|
ueContextReleaseComplete_p->eNB_UE_S1AP_ID,
|
|
ueContextReleaseComplete_p->mme_ue_s1ap_id);
|
|
|
|
if ((ue_ref = s1ap_is_ue_mme_id_in_list(
|
|
ueContextReleaseComplete_p->mme_ue_s1ap_id)) == NULL) {
|
|
/* MME doesn't know the MME UE S1AP ID provided.
|
|
* TODO
|
|
*/
|
|
MSC_LOG_EVENT(
|
|
MSC_S1AP_MME,
|
|
"0 UEContextReleaseComplete ignored, no context mme_ue_s1ap_id "S1AP_UE_ID_FMT" ",
|
|
ueContextReleaseComplete_p->mme_ue_s1ap_id);
|
|
return -1;
|
|
}
|
|
|
|
/* eNB has sent a release complete message. We can safely remove UE context.
|
|
* TODO: inform NAS and remove e-RABS.
|
|
*/
|
|
s1ap_remove_ue(ue_ref);
|
|
S1AP_DEBUG("Removed UE "S1AP_UE_ID_FMT"\n",
|
|
(uint32_t)ueContextReleaseComplete_p->mme_ue_s1ap_id);
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int s1ap_mme_handle_initial_context_setup_failure(uint32_t assoc_id,
|
|
uint32_t stream, struct s1ap_message_s *message)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
S1ap_InitialContextSetupFailureIEs_t *initialContextSetupFailureIEs_p;
|
|
|
|
ue_description_t *ue_ref = NULL;
|
|
|
|
initialContextSetupFailureIEs_p = &message->msg.s1ap_InitialContextSetupFailureIEs;
|
|
MSC_LOG_RX_MESSAGE(
|
|
MSC_S1AP_MME,
|
|
MSC_S1AP_ENB,
|
|
NULL,0,
|
|
"0 InitialContextSetup/%s eNB_ue_s1ap_id "S1AP_UE_ID_FMT" mme_ue_s1ap_id "S1AP_UE_ID_FMT" len %u",
|
|
s1ap_direction2String[message->direction],
|
|
initialContextSetupFailureIEs_p->eNB_UE_S1AP_ID,
|
|
initialContextSetupFailureIEs_p->mme_ue_s1ap_id);
|
|
|
|
if ((ue_ref = s1ap_is_ue_mme_id_in_list(
|
|
initialContextSetupFailureIEs_p->mme_ue_s1ap_id)) == NULL) {
|
|
/* MME doesn't know the MME UE S1AP ID provided. */
|
|
MSC_LOG_EVENT(
|
|
MSC_S1AP_MME,
|
|
"0 InitialContextSetupFailure ignored, no context mme_ue_s1ap_id "S1AP_UE_ID_FMT" ",
|
|
initialContextSetupFailureIEs_p->mme_ue_s1ap_id);
|
|
return -1;
|
|
}
|
|
|
|
if (ue_ref->eNB_ue_s1ap_id != (initialContextSetupFailureIEs_p->eNB_UE_S1AP_ID
|
|
& 0x00ffffff)) {
|
|
return -1;
|
|
}
|
|
|
|
s1ap_remove_ue(ue_ref);
|
|
MSC_LOG_EVENT(
|
|
MSC_S1AP_MME,
|
|
"0 Removed UE mme_ue_s1ap_id "S1AP_UE_ID_FMT" ",
|
|
initialContextSetupFailureIEs_p->mme_ue_s1ap_id);
|
|
S1AP_DEBUG("Removed UE "S1AP_UE_ID_FMT"\n",
|
|
(uint32_t)initialContextSetupFailureIEs_p->mme_ue_s1ap_id);
|
|
return 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//************************ Handover signalling *******************************//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//------------------------------------------------------------------------------
|
|
int s1ap_mme_handle_path_switch_request(uint32_t assoc_id, uint32_t stream,
|
|
struct s1ap_message_s *message)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
S1ap_PathSwitchRequestIEs_t *pathSwitchRequest_p;
|
|
ue_description_t *ue_ref;
|
|
uint32_t eNB_ue_s1ap_id;
|
|
|
|
pathSwitchRequest_p = &message->msg.s1ap_PathSwitchRequestIEs;
|
|
|
|
// eNB UE S1AP ID is limited to 24 bits
|
|
eNB_ue_s1ap_id = (uint32_t)(pathSwitchRequest_p->eNB_UE_S1AP_ID & 0x00ffffff);
|
|
|
|
S1AP_DEBUG("Path Switch Request message received from eNB UE S1AP ID: "S1AP_UE_ID_FMT"\n",
|
|
(int)eNB_ue_s1ap_id);
|
|
|
|
if ((ue_ref = s1ap_is_ue_mme_id_in_list(
|
|
pathSwitchRequest_p->sourceMME_UE_S1AP_ID)) == NULL) {
|
|
/* The MME UE S1AP ID provided by eNB doesn't point to any valid UE.
|
|
* MME replies with a PATH SWITCH REQUEST FAILURE message and start operation
|
|
* as described in TS 36.413 [11].
|
|
* TODO
|
|
*/
|
|
} else {
|
|
if (ue_ref->eNB_ue_s1ap_id != eNB_ue_s1ap_id) {
|
|
/* Received unique UE eNB ID mismatch with the one known in MME.
|
|
* Handle this case as defined upper.
|
|
* TODO
|
|
*/
|
|
return -1;
|
|
}
|
|
|
|
//TODO: Switch the eRABs provided
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int s1ap_handle_sctp_deconnection(uint32_t assoc_id)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
int current_ue_index = 0;
|
|
int handled_ues = 0;
|
|
int i;
|
|
|
|
MessageDef *message_p = NULL;
|
|
|
|
ue_description_t *ue_ref = NULL;
|
|
eNB_description_t *eNB_association;
|
|
|
|
/* Checking that the assoc id has a valid eNB attached to. */
|
|
eNB_association = s1ap_is_eNB_assoc_id_in_list(assoc_id);
|
|
|
|
if (eNB_association == NULL) {
|
|
S1AP_ERROR("No eNB attached to this assoc_id: %d\n",
|
|
assoc_id);
|
|
return -1;
|
|
}
|
|
MSC_LOG_EVENT(
|
|
MSC_S1AP_MME,
|
|
"0 Event SCTP_CLOSE_ASSOCIATION assoc_id: %d",
|
|
assoc_id);
|
|
|
|
STAILQ_FOREACH(ue_ref, &eNB_association->ue_list_head, ue_entries) {
|
|
/* Ask for a release of each UE context associated to the eNB */
|
|
if (current_ue_index == 0) {
|
|
message_p = itti_alloc_new_message(TASK_S1AP, S1AP_ENB_DEREGISTERED_IND);
|
|
}
|
|
|
|
S1AP_ENB_DEREGISTERED_IND(message_p).mme_ue_s1ap_id[current_ue_index] = ue_ref->mme_ue_s1ap_id;
|
|
|
|
if (current_ue_index == 0 && handled_ues > 0) {
|
|
S1AP_ENB_DEREGISTERED_IND(message_p).nb_ue_to_deregister = S1AP_ITTI_UE_PER_DEREGISTER_MESSAGE;
|
|
itti_send_msg_to_task(TASK_NAS_MME, INSTANCE_DEFAULT, message_p);
|
|
}
|
|
|
|
handled_ues++;
|
|
current_ue_index = handled_ues % S1AP_ITTI_UE_PER_DEREGISTER_MESSAGE;
|
|
}
|
|
|
|
if ((handled_ues % S1AP_ITTI_UE_PER_DEREGISTER_MESSAGE) != 0) {
|
|
S1AP_ENB_DEREGISTERED_IND(message_p).nb_ue_to_deregister = current_ue_index;
|
|
|
|
for (i = current_ue_index; i < S1AP_ITTI_UE_PER_DEREGISTER_MESSAGE; i++) {
|
|
S1AP_ENB_DEREGISTERED_IND(message_p).mme_ue_s1ap_id[current_ue_index] = 0;
|
|
}
|
|
|
|
MSC_LOG_TX_MESSAGE(
|
|
MSC_S1AP_MME,
|
|
MSC_NAS_MME,
|
|
NULL,0,
|
|
"0 S1AP_ENB_DEREGISTERED_IND num ue to deregister %u",
|
|
S1AP_ENB_DEREGISTERED_IND(message_p).nb_ue_to_deregister);
|
|
|
|
itti_send_msg_to_task(TASK_NAS_MME, INSTANCE_DEFAULT, message_p);
|
|
}
|
|
|
|
s1ap_remove_eNB(eNB_association);
|
|
s1ap_dump_eNB_list();
|
|
|
|
S1AP_DEBUG("Removed eNB attached to assoc_id: %d\n",
|
|
assoc_id);
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int s1ap_handle_new_association(sctp_new_peer_t *sctp_new_peer_p)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
eNB_description_t *eNB_association;
|
|
|
|
DevAssert(sctp_new_peer_p != NULL);
|
|
|
|
/* Checking that the assoc id has a valid eNB attached to. */
|
|
if ((eNB_association = s1ap_is_eNB_assoc_id_in_list(sctp_new_peer_p->assoc_id)) == NULL) {
|
|
S1AP_DEBUG("Create eNB context for assoc_id: %d\n",
|
|
sctp_new_peer_p->assoc_id);
|
|
/* Create new context */
|
|
eNB_association = s1ap_new_eNB();
|
|
|
|
if (eNB_association == NULL) {
|
|
/* We failed to allocate memory */
|
|
/* TODO: send reject there */
|
|
S1AP_ERROR("Failed to allocate eNB context for assoc_id: %d\n",
|
|
sctp_new_peer_p->assoc_id);
|
|
}
|
|
} else {
|
|
S1AP_DEBUG("eNB context already exists for assoc_id: %d, update it\n",
|
|
sctp_new_peer_p->assoc_id);
|
|
}
|
|
|
|
eNB_association->sctp_assoc_id = sctp_new_peer_p->assoc_id;
|
|
|
|
/* Fill in in and out number of streams available on SCTP connection. */
|
|
eNB_association->instreams = sctp_new_peer_p->instreams;
|
|
eNB_association->outstreams = sctp_new_peer_p->outstreams;
|
|
|
|
/* initialize the next sctp stream to 1 as 0 is reserved for non
|
|
* ue associated signalling.
|
|
*/
|
|
eNB_association->next_sctp_stream = 1;
|
|
MSC_LOG_EVENT(
|
|
MSC_S1AP_MME,
|
|
"0 Event SCTP_NEW_ASSOCIATION assoc_id: %d",
|
|
eNB_association->sctp_assoc_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//************************* E-RAB management *********************************//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// NOT CALLED
|
|
int s1ap_handle_create_session_response(SgwCreateSessionResponse
|
|
*session_response_p)
|
|
{
|
|
/* We received create session response from S-GW on S11 interface abstraction.
|
|
* At least one bearer has been established. We can now send s1ap initial context setup request
|
|
* message to eNB.
|
|
*/
|
|
char supportedAlgorithms[] = { 0x02, 0xa0 };
|
|
char securityKey[] = { 0xfd, 0x23, 0xad, 0x22, 0xd0, 0x21, 0x02, 0x90, 0x19, 0xed,
|
|
0xcf, 0xc9, 0x78, 0x44, 0xba, 0xbb, 0x34, 0x6e, 0xff, 0x89,
|
|
0x1c, 0x3a, 0x56, 0xf0, 0x81, 0x34, 0xdd, 0xee, 0x19, 0x55,
|
|
0xf2, 0x1f
|
|
};
|
|
|
|
ue_description_t *ue_ref = NULL;
|
|
s1ap_message message;
|
|
uint8_t *buffer_p;
|
|
uint32_t length;
|
|
|
|
S1ap_InitialContextSetupRequestIEs_t *initialContextSetupRequest_p;
|
|
S1ap_E_RABToBeSetupItemCtxtSUReq_t e_RABToBeSetup;
|
|
|
|
AssertFatal(0, "Not called anymore");
|
|
DevAssert(session_response_p != NULL);
|
|
|
|
DevCheck(session_response_p->bearer_context_created.cause == REQUEST_ACCEPTED,
|
|
REQUEST_ACCEPTED, session_response_p->bearer_context_created.cause, 0);
|
|
|
|
if ((session_response_p->bearer_context_created.s1u_sgw_fteid.ipv4 == 0) &&
|
|
(session_response_p->bearer_context_created.s1u_sgw_fteid.ipv6 == 0)) {
|
|
|
|
S1AP_ERROR("No IP address provided for transport layer address\n"
|
|
" -->Sending Intial context setup failure\n");
|
|
return -1;
|
|
}
|
|
|
|
if ((ue_ref = s1ap_is_teid_in_list(session_response_p->s11_sgw_teid.teid)) == NULL) {
|
|
S1AP_DEBUG("[%d] is not attached to any UE context\n",
|
|
session_response_p->s11_sgw_teid.teid);
|
|
return -1;
|
|
}
|
|
|
|
memset(&message, 0, sizeof(s1ap_message));
|
|
memset(&e_RABToBeSetup, 0, sizeof(S1ap_E_RABToBeSetupItemCtxtSUReq_t));
|
|
|
|
message.procedureCode = S1ap_ProcedureCode_id_InitialContextSetup;
|
|
message.direction = S1AP_PDU_PR_initiatingMessage;
|
|
|
|
initialContextSetupRequest_p = &message.msg.s1ap_InitialContextSetupRequestIEs;
|
|
|
|
initialContextSetupRequest_p->mme_ue_s1ap_id = ue_ref->mme_ue_s1ap_id;
|
|
initialContextSetupRequest_p->eNB_UE_S1AP_ID = ue_ref->eNB_ue_s1ap_id;
|
|
|
|
/* uEaggregateMaximumBitrateDL and uEaggregateMaximumBitrateUL expressed in term of bits/sec */
|
|
asn_int642INTEGER(
|
|
&initialContextSetupRequest_p->uEaggregateMaximumBitrate.uEaggregateMaximumBitRateDL,
|
|
100000000UL);
|
|
asn_int642INTEGER(
|
|
&initialContextSetupRequest_p->uEaggregateMaximumBitrate.uEaggregateMaximumBitRateUL,
|
|
50000000UL);
|
|
|
|
//initialContextSetupRequest_p->uEaggregateMaximumBitrate.uEaggregateMaximumBitRateDL = 100000000UL;
|
|
//initialContextSetupRequest_p->uEaggregateMaximumBitrate.uEaggregateMaximumBitRateUL = 50000000UL;
|
|
|
|
e_RABToBeSetup.e_RAB_ID =
|
|
session_response_p->bearer_context_created.eps_bearer_id; /* ??? */
|
|
e_RABToBeSetup.e_RABlevelQoSParameters.qCI = 0; // ??
|
|
e_RABToBeSetup.e_RABlevelQoSParameters.allocationRetentionPriority.priorityLevel
|
|
= 15; //No priority
|
|
e_RABToBeSetup.e_RABlevelQoSParameters.allocationRetentionPriority.pre_emptionCapability
|
|
= S1ap_Pre_emptionCapability_shall_not_trigger_pre_emption;
|
|
e_RABToBeSetup.e_RABlevelQoSParameters.allocationRetentionPriority.pre_emptionVulnerability
|
|
= S1ap_Pre_emptionVulnerability_not_pre_emptable;
|
|
|
|
// e_RABToBeSetup.gTP_TEID.buf = calloc(4, sizeof(uint8_t));
|
|
// INT32_TO_BUFFER(session_response_p->bearer_context_created.s1u_sgw_fteid.teid,
|
|
// e_RABToBeSetup.gTP_TEID.buf);
|
|
// e_RABToBeSetup.gTP_TEID.size = 4;
|
|
DevCheck(session_response_p->bearer_context_created.s1u_sgw_fteid.teid != 0,
|
|
session_response_p->bearer_context_created.s1u_sgw_fteid.teid, 0, 0);
|
|
INT32_TO_OCTET_STRING(
|
|
session_response_p->bearer_context_created.s1u_sgw_fteid.teid,
|
|
&e_RABToBeSetup.gTP_TEID);
|
|
|
|
if ((session_response_p->bearer_context_created.s1u_sgw_fteid.ipv4 == 1) &&
|
|
(session_response_p->bearer_context_created.s1u_sgw_fteid.ipv6 == 0)) {
|
|
/* Only IPv4 supported */
|
|
INT32_TO_BIT_STRING(
|
|
session_response_p->bearer_context_created.s1u_sgw_fteid.ipv4_address,
|
|
&e_RABToBeSetup.transportLayerAddress);
|
|
} else if ((session_response_p->bearer_context_created.s1u_sgw_fteid.ipv4 == 0) &&
|
|
(session_response_p->bearer_context_created.s1u_sgw_fteid.ipv6 == 1)) {
|
|
/* Only IPv6 supported */
|
|
e_RABToBeSetup.transportLayerAddress.buf = calloc(16, sizeof(uint8_t));
|
|
memcpy(e_RABToBeSetup.transportLayerAddress.buf,
|
|
(void *)&session_response_p->bearer_context_created.s1u_sgw_fteid.ipv6_address,
|
|
16);
|
|
e_RABToBeSetup.transportLayerAddress.size = 16;
|
|
e_RABToBeSetup.transportLayerAddress.bits_unused = 0;
|
|
} else if ((session_response_p->bearer_context_created.s1u_sgw_fteid.ipv4 == 1) &&
|
|
(session_response_p->bearer_context_created.s1u_sgw_fteid.ipv6 == 1)) {
|
|
/* Both IPv4 and IPv6 supported */
|
|
e_RABToBeSetup.transportLayerAddress.buf = calloc(20, sizeof(uint8_t));
|
|
e_RABToBeSetup.transportLayerAddress.size = 20;
|
|
e_RABToBeSetup.transportLayerAddress.bits_unused = 0;
|
|
memcpy(e_RABToBeSetup.transportLayerAddress.buf,
|
|
&session_response_p->bearer_context_created.s1u_sgw_fteid.ipv4_address, 4);
|
|
memcpy(e_RABToBeSetup.transportLayerAddress.buf + 4,
|
|
session_response_p->bearer_context_created.s1u_sgw_fteid.ipv6_address, 16);
|
|
}
|
|
|
|
ASN_SEQUENCE_ADD(&initialContextSetupRequest_p->e_RABToBeSetupListCtxtSUReq,
|
|
&e_RABToBeSetup);
|
|
|
|
initialContextSetupRequest_p->ueSecurityCapabilities.encryptionAlgorithms.buf =
|
|
(uint8_t *)supportedAlgorithms;
|
|
initialContextSetupRequest_p->ueSecurityCapabilities.encryptionAlgorithms.size = 2;
|
|
initialContextSetupRequest_p->ueSecurityCapabilities.encryptionAlgorithms.bits_unused
|
|
= 0;
|
|
|
|
initialContextSetupRequest_p->ueSecurityCapabilities.integrityProtectionAlgorithms.buf
|
|
= (uint8_t *)supportedAlgorithms;
|
|
initialContextSetupRequest_p->ueSecurityCapabilities.integrityProtectionAlgorithms.size
|
|
= 2;
|
|
initialContextSetupRequest_p->ueSecurityCapabilities.integrityProtectionAlgorithms.bits_unused
|
|
= 0;
|
|
|
|
initialContextSetupRequest_p->securityKey.buf = (uint8_t *)
|
|
securityKey; /* 256 bits length */
|
|
initialContextSetupRequest_p->securityKey.size = 32;
|
|
initialContextSetupRequest_p->securityKey.bits_unused = 0;
|
|
|
|
if (s1ap_mme_encode_pdu(&message, &buffer_p, &length) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
return s1ap_mme_itti_send_sctp_request(buffer_p, length, ue_ref->eNB->sctp_assoc_id,
|
|
ue_ref->sctp_stream_send);
|
|
|
|
// return s1ap_mme_encode_initial_context_setup_request(&initialContextSetupRequest,
|
|
// ue_ref);
|
|
}
|