Files
openairinterface5g/openair3/NAS/UE/EMM/SAP/emm_as.c
T
Frédéric Leroy 223247ceb0 UE/EMM: add nas_user_t parameter to emm_sap_send
Surprisingly, it touches every part of openair3, and most of the prototype.
Moreover, a special attention have been put to callback review.
However, there are still places to fix or review indicated by FIXME
2017-01-25 13:08:19 +01:00

1480 lines
56 KiB
C

/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.0 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*****************************************************************************
Source emm_as.c
Version 0.1
Date 2012/10/16
Product NAS stack
Subsystem EPS Mobility Management
Author Frederic Maurel
Description Defines the EMMAS Service Access Point that provides
services to the EPS Mobility Management for NAS message
transfer to/from the Access Stratum sublayer.
*****************************************************************************/
#include "emm_as.h"
#include "emm_recv.h"
#include "emm_send.h"
#include "emmData.h"
#include "commonDef.h"
#include "nas_log.h"
#include "TLVDecoder.h"
#include "as_message.h"
#include "nas_message.h"
#include "emm_cause.h"
#include "LowerLayer.h"
#include <string.h> // memset
#include <stdlib.h> // malloc, free
#if defined(NAS_BUILT_IN_UE)
# include "nas_itti_messaging.h"
#endif
#include "msc.h"
/****************************************************************************/
/**************** E X T E R N A L D E F I N I T I O N S ****************/
/****************************************************************************/
extern int emm_proc_plmn_selection_end(int found, tac_t tac, ci_t ci, AcT_t rat);
extern int emm_proc_status(unsigned int ueid, int emm_cause);
/****************************************************************************/
/******************* L O C A L D E F I N I T I O N S *******************/
/****************************************************************************/
/*
* String representation of EMMAS-SAP primitives
*/
static const char *_emm_as_primitive_str[] = {
"EMMAS_SECURITY_REQ",
"EMMAS_SECURITY_IND",
"EMMAS_SECURITY_RES",
"EMMAS_SECURITY_REJ",
"EMMAS_ESTABLISH_REQ",
"EMMAS_ESTABLISH_CNF",
"EMMAS_ESTABLISH_REJ",
"EMMAS_RELEASE_REQ",
"EMMAS_RELEASE_IND",
"EMMAS_DATA_REQ",
"EMMAS_DATA_IND",
"EMMAS_PAGE_IND",
"EMMAS_STATUS_IND",
"EMMAS_CELL_INFO_REQ",
"EMMAS_CELL_INFO_RES",
"EMMAS_CELL_INFO_IND",
};
/*
* Functions executed to process EMM procedures upon receiving
* data from the network
*/
static int _emm_as_recv(nas_user_t *user, unsigned int ueid, const char *msg, int len,
int *emm_cause);
static int _emm_as_establish_cnf(nas_user_t *user, const emm_as_establish_t *msg, int *emm_cause);
static int _emm_as_establish_rej(nas_user_t *user);
static int _emm_as_release_ind(nas_user_t *user, const emm_as_release_t *msg);
static int _emm_as_page_ind(const emm_as_page_t *msg);
static int _emm_as_cell_info_res(const emm_as_cell_info_t *msg);
static int _emm_as_cell_info_ind(const emm_as_cell_info_t *msg);
static int _emm_as_data_ind(nas_user_t *user, const emm_as_data_t *msg, int *emm_cause);
/*
* Functions executed to send data to the network when requested
* within EMM procedure processing
*/
static EMM_msg *_emm_as_set_header(nas_message_t *msg,
const emm_as_security_data_t *security);
static int
_emm_as_encode(
as_nas_info_t *info,
nas_message_t *msg,
int length,
emm_security_context_t *emm_security_context);
static int _emm_as_encrypt(
as_nas_info_t *info,
const nas_message_security_header_t *header,
const char *buffer,
int length,
emm_security_context_t *emm_security_context);
static int _emm_as_send(const nas_user_t *user, const emm_as_t *msg);
static int _emm_as_security_res(const emm_as_security_t *,
ul_info_transfer_req_t *);
static int _emm_as_establish_req(const emm_as_establish_t *,
nas_establish_req_t *);
static int _emm_as_cell_info_req(const emm_as_cell_info_t *, cell_info_req_t *);
static int _emm_as_data_req(const emm_as_data_t *msg, ul_info_transfer_req_t *);
static int _emm_as_status_ind(const emm_as_status_t *, ul_info_transfer_req_t *);
static int _emm_as_release_req(const emm_as_release_t *, nas_release_req_t *);
/****************************************************************************/
/****************** E X P O R T E D F U N C T I O N S ******************/
/****************************************************************************/
/****************************************************************************
** **
** Name: emm_as_initialize() **
** **
** Description: Initializes the EMMAS Service Access Point **
** **
** Inputs: None **
** Others: None **
** **
** Outputs: None **
** Return: None **
** Others: NONE **
** **
***************************************************************************/
void emm_as_initialize(nas_user_t *user)
{
LOG_FUNC_IN;
/* TODO: Initialize the EMMAS-SAP */
LOG_FUNC_OUT;
}
/****************************************************************************
** **
** Name: emm_as_send() **
** **
** Description: Processes the EMMAS Service Access Point primitive. **
** **
** Inputs: msg: The EMMAS-SAP primitive to process **
** Others: None **
** **
** Outputs: None **
** Return: RETURNok, RETURNerror **
** Others: None **
** **
***************************************************************************/
int emm_as_send(nas_user_t *user, const emm_as_t *msg)
{
LOG_FUNC_IN;
int rc;
int emm_cause = EMM_CAUSE_SUCCESS;
emm_as_primitive_t primitive = msg->primitive;
uint32_t ueid = 0;
LOG_TRACE(INFO, "EMMAS-SAP - Received primitive %s (%d)",
_emm_as_primitive_str[primitive - _EMMAS_START - 1], primitive);
switch (primitive) {
case _EMMAS_DATA_IND:
rc = _emm_as_data_ind(user, &msg->u.data, &emm_cause);
ueid = msg->u.data.ueid;
break;
case _EMMAS_ESTABLISH_CNF:
rc = _emm_as_establish_cnf(user, &msg->u.establish, &emm_cause);
break;
case _EMMAS_ESTABLISH_REJ:
rc = _emm_as_establish_rej(user);
break;
case _EMMAS_RELEASE_IND:
rc = _emm_as_release_ind(user, &msg->u.release);
break;
case _EMMAS_PAGE_IND:
rc = _emm_as_page_ind(&msg->u.page);
break;
case _EMMAS_CELL_INFO_RES:
rc = _emm_as_cell_info_res(&msg->u.cell_info);
break;
case _EMMAS_CELL_INFO_IND:
rc = _emm_as_cell_info_ind(&msg->u.cell_info);
break;
default:
/* Other primitives are forwarded to the Access Stratum */
rc = _emm_as_send(user, msg);
if (rc != RETURNok) {
LOG_TRACE(ERROR, "EMMAS-SAP - "
"Failed to process primitive %s (%d)",
_emm_as_primitive_str[primitive - _EMMAS_START - 1],
primitive);
LOG_FUNC_RETURN (RETURNerror);
}
break;
}
/* Handle decoding errors */
if (emm_cause != EMM_CAUSE_SUCCESS) {
/* Ignore received message that is too short to contain a complete
* message type information element */
if (rc == TLV_DECODE_BUFFER_TOO_SHORT) {
LOG_FUNC_RETURN (RETURNok);
}
/* Ignore received message that contains not supported protocol
* discriminator */
else if (rc == TLV_DECODE_PROTOCOL_NOT_SUPPORTED) {
LOG_FUNC_RETURN (RETURNok);
} else if (rc == TLV_DECODE_WRONG_MESSAGE_TYPE) {
emm_cause = EMM_CAUSE_MESSAGE_TYPE_NOT_IMPLEMENTED;
}
/* EMM message processing failed */
LOG_TRACE(WARNING, "EMMAS-SAP - Received EMM message is not valid "
"(cause=%d)", emm_cause);
/* Return an EMM status message */
rc = emm_proc_status(ueid, emm_cause);
}
if (rc != RETURNok) {
LOG_TRACE(ERROR, "EMMAS-SAP - Failed to process primitive %s (%d)",
_emm_as_primitive_str[primitive - _EMMAS_START - 1],
primitive);
}
LOG_FUNC_RETURN (rc);
}
/****************************************************************************/
/********************* L O C A L F U N C T I O N S *********************/
/****************************************************************************/
/*
* --------------------------------------------------------------------------
* Functions executed to process EMM procedures upon receiving data from the
* network
* --------------------------------------------------------------------------
*/
/****************************************************************************
** **
** Name: _emm_as_recv() **
** **
** Description: Decodes and processes the EPS Mobility Management message **
** received from the Access Stratum **
** **
** Inputs: ueid: UE lower layer identifier **
** msg: The EMM message to process **
** len: The length of the EMM message **
** Others: None **
** **
** Outputs: emm_cause: EMM cause code **
** Return: RETURNok, RETURNerror **
** Others: None **
** **
***************************************************************************/
static int _emm_as_recv(nas_user_t *user, unsigned int ueid, const char *msg, int len,
int *emm_cause)
{
LOG_FUNC_IN;
int decoder_rc;
int rc = RETURNerror;
LOG_TRACE(INFO, "EMMAS-SAP - Received EMM message (length=%d)", len);
nas_message_t nas_msg;
memset(&nas_msg, 0 , sizeof(nas_message_t));
emm_security_context_t *security = NULL; /* Current EPS NAS security context */
security = _emm_data.security;
/* Decode the received message */
decoder_rc = nas_message_decode(msg, &nas_msg, len, security);
if (decoder_rc < 0) {
LOG_TRACE(WARNING, "EMMAS-SAP - Failed to decode NAS message "
"(err=%d)", decoder_rc);
*emm_cause = EMM_CAUSE_PROTOCOL_ERROR;
LOG_FUNC_RETURN (decoder_rc);
}
/* Process NAS message */
EMM_msg *emm_msg = &nas_msg.plain.emm;
switch (emm_msg->header.message_type) {
case EMM_STATUS:
rc = emm_recv_status(ueid, &emm_msg->emm_status, emm_cause);
break;
case IDENTITY_REQUEST:
rc = emm_recv_identity_request(user, &emm_msg->identity_request,
emm_cause);
break;
case AUTHENTICATION_REQUEST:
rc = emm_recv_authentication_request(user,
&emm_msg->authentication_request,
emm_cause);
break;
case AUTHENTICATION_REJECT:
rc = emm_recv_authentication_reject(user,
&emm_msg->authentication_reject,
emm_cause);
break;
case SECURITY_MODE_COMMAND:
rc = emm_recv_security_mode_command(user,
&emm_msg->security_mode_command,
emm_cause);
break;
case DETACH_ACCEPT:
rc = emm_recv_detach_accept(user, &emm_msg->detach_accept, emm_cause);
break;
case TRACKING_AREA_UPDATE_ACCEPT:
case TRACKING_AREA_UPDATE_REJECT:
case SERVICE_REJECT:
case GUTI_REALLOCATION_COMMAND:
case EMM_INFORMATION:
case DOWNLINK_NAS_TRANSPORT:
case CS_SERVICE_NOTIFICATION:
/* TODO */
break;
default:
LOG_TRACE(WARNING, "EMMAS-SAP - EMM message 0x%x is not valid",
emm_msg->header.message_type);
*emm_cause = EMM_CAUSE_MESSAGE_TYPE_NOT_COMPATIBLE;
break;
}
LOG_FUNC_RETURN (rc);
}
/****************************************************************************
** **
** Name: _emm_as_data_ind() **
** **
** Description: Processes the EMMAS-SAP data transfer indication **
** primitive **
** **
** EMMAS-SAP - AS->EMM: DATA_IND - Data transfer procedure **
** **
** Inputs: msg: The EMMAS-SAP primitive to process **
** Others: None **
** **
** Outputs: emm_cause: EMM cause code **
** Return: RETURNok, RETURNerror **
** Others: None **
** **
***************************************************************************/
static int _emm_as_data_ind(nas_user_t *user, const emm_as_data_t *msg, int *emm_cause)
{
LOG_FUNC_IN;
int rc = RETURNerror;
LOG_TRACE(INFO, "EMMAS-SAP - Received AS data transfer indication "
"(ueid=%u, delivered=%s, length=%d)", msg->ueid,
(msg->delivered)? "TRUE" : "FALSE", msg->NASmsg.length);
if (msg->delivered) {
if (msg->NASmsg.length > 0) {
/* Process the received NAS message */
char *plain_msg = (char *)malloc(msg->NASmsg.length);
if (plain_msg) {
nas_message_security_header_t header;
emm_security_context_t *security = NULL; /* Current EPS NAS security context */
memset(&header, 0, sizeof(header));
/* Decrypt the received security protected message */
security = _emm_data.security;
int bytes = nas_message_decrypt((char *)(msg->NASmsg.value),
plain_msg,
&header,
msg->NASmsg.length,
security
);
if (bytes < 0) {
/* Failed to decrypt the message */
*emm_cause = EMM_CAUSE_PROTOCOL_ERROR;
LOG_FUNC_RETURN (bytes);
} else if (header.protocol_discriminator ==
EPS_MOBILITY_MANAGEMENT_MESSAGE) {
/* Process EMM data */
rc = _emm_as_recv(user, msg->ueid, plain_msg, bytes, emm_cause);
} else if (header.protocol_discriminator ==
EPS_SESSION_MANAGEMENT_MESSAGE) {
const OctetString data = {bytes, (uint8_t *)plain_msg};
/* Foward ESM data to EPS session management */
rc = lowerlayer_data_ind(user, msg->ueid, &data);
}
free(plain_msg);
}
} else {
/* Process successfull lower layer transfer indication */
rc = lowerlayer_success(user, msg->ueid);
}
} else {
/* Process lower layer transmission failure of NAS message */
rc = lowerlayer_failure(user, msg->ueid);
}
LOG_FUNC_RETURN (rc);
}
/****************************************************************************
** **
** Name: _emm_as_establish_cnf() **
** **
** Description: Processes the EMMAS-SAP connection establish confirmation **
** primitive **
** **
** EMMAS-SAP - AS->EMM: ESTABLISH_CNF - NAS signalling connection **
** **
** Inputs: msg: The EMMAS-SAP primitive to process **
** Others: None **
** **
** Outputs: emm_cause: EMM cause code **
** Return: RETURNok, RETURNerror **
** Others: None **
** **
***************************************************************************/
static int _emm_as_establish_cnf(nas_user_t *user, const emm_as_establish_t *msg,
int *emm_cause)
{
LOG_FUNC_IN;
int decoder_rc;
int rc = RETURNerror;
LOG_TRACE(INFO, "EMMAS-SAP - Received AS connection establish confirm");
if (msg->NASmsg.length > 0) {
/* The NAS signalling connection is established */
(void) lowerlayer_establish(user);
} else {
/* The initial NAS message has been successfully delivered to
* lower layers */
rc = lowerlayer_success(user, 0);
LOG_FUNC_RETURN (rc);
}
nas_message_t nas_msg;
memset(&nas_msg, 0 , sizeof(nas_message_t));
/* Decode initial NAS message */
decoder_rc = nas_message_decode((char *)(msg->NASmsg.value),
&nas_msg,
msg->NASmsg.length,
_emm_data.security);
if (decoder_rc < 0) {
LOG_TRACE(WARNING, "EMMAS-SAP - Failed to decode initial NAS message"
"(err=%d)", decoder_rc);
*emm_cause = EMM_CAUSE_PROTOCOL_ERROR;
LOG_FUNC_RETURN (decoder_rc);
}
/* Process initial NAS message */
EMM_msg *emm_msg = &nas_msg.plain.emm;
switch (emm_msg->header.message_type) {
case ATTACH_ACCEPT:
rc = emm_recv_attach_accept(user, &emm_msg->attach_accept, emm_cause);
break;
case ATTACH_REJECT:
rc = emm_recv_attach_reject(user, &emm_msg->attach_reject, emm_cause);
break;
case DETACH_ACCEPT:
break;
case TRACKING_AREA_UPDATE_ACCEPT:
break;
default:
LOG_TRACE(WARNING, "EMMAS-SAP - Initial NAS message 0x%x is "
"not valid", emm_msg->header.message_type);
*emm_cause = EMM_CAUSE_MESSAGE_TYPE_NOT_COMPATIBLE;
break;
}
LOG_FUNC_RETURN (rc);
}
/****************************************************************************
** **
** Name: _emm_as_establish_rej() **
** **
** Description: Processes the EMMAS-SAP connection establish reject **
** primitive **
** **
** EMMAS-SAP - AS->EMM: ESTABLISH_REJ - NAS signalling connection **
** **
** Inputs: None **
** Others: None **
** **
** Outputs: None **
** Return: RETURNok, RETURNerror **
** Others: None **
** **
***************************************************************************/
static int _emm_as_establish_rej(nas_user_t *user)
{
LOG_FUNC_IN;
int rc;
LOG_TRACE(INFO, "EMMAS-SAP - Received AS initial NAS message transmission "
"failure");
/* Process lower layer transmission failure of initial NAS message */
rc = lowerlayer_failure(user, 0);
LOG_FUNC_RETURN (rc);
}
/****************************************************************************
** **
** Name: _emm_as_release_ind() **
** **
** Description: Processes the EMMAS-SAP connection release indication **
** primitive **
** **
** EMMAS-SAP - AS->EMM: RELEASE_IND - NAS signalling release procedure **
** **
** Inputs: msg: The EMMAS-SAP primitive to process **
** Others: None **
** **
** Outputs: None **
** Return: RETURNok, RETURNerror **
** Others: None **
** **
***************************************************************************/
static int _emm_as_release_ind(nas_user_t *user, const emm_as_release_t *msg)
{
LOG_FUNC_IN;
int rc = RETURNok;
LOG_TRACE(INFO, "EMMAS-SAP - Received AS connection release indication "
"(cause=%d)", msg->cause);
/* Process NAS signalling connection release indication */
rc = lowerlayer_release(user, msg->cause);
LOG_FUNC_RETURN (rc);
}
/****************************************************************************
** **
** Name: _emm_as_page_ind() **
** **
** Description: Processes the EMMAS-SAP paging data indication primitive **
** **
** EMMAS-SAP - AS->EMM: PAGE_IND - Paging data procedure **
** **
** Inputs: msg: The EMMAS-SAP primitive to process **
** Others: None **
** **
** Outputs: None **
** Return: RETURNok, RETURNerror **
** Others: None **
** **
***************************************************************************/
static int _emm_as_page_ind(const emm_as_page_t *msg)
{
LOG_FUNC_IN;
int rc = RETURNok;
LOG_TRACE(INFO, "EMMAS-SAP - Received AS paging data indication");
/* TODO */
LOG_FUNC_RETURN (rc);
}
/****************************************************************************
** **
** Name: _emm_as_cell_info_res() **
** **
** Description: Processes the EMMAS-SAP cell information response **
** primitive **
** **
** EMMAS-SAP - AS->EMM: CELL_INFO_RES - PLMN and cell selection procedure **
** The NAS received a response to cell selection request pre- **
** viously sent to the Access-Startum. If a suitable cell is **
** found to serve the selected PLMN with associated Radio Ac- **
** cess Technologies, this cell is selected to camp on. **
** **
** Inputs: msg: The EMMAS-SAP primitive to process **
** Others: None **
** **
** Outputs: None **
** Return: RETURNok, RETURNerror **
** Others: None **
** **
***************************************************************************/
static int _emm_as_cell_info_res(const emm_as_cell_info_t *msg)
{
LOG_FUNC_IN;
int rc = RETURNok;
LOG_TRACE(INFO, "EMMAS-SAP - Received AS cell information response");
int AcT = NET_ACCESS_EUTRAN;
if (msg->found == TRUE) {
/* Get the first supported access technology */
while (AcT != NET_ACCESS_UNAVAILABLE) {
if (msg->rat & (1 << AcT)) {
break;
}
AcT -= 1;
}
}
/* Notify EMM that a cell has been found */
rc = emm_proc_plmn_selection_end(msg->found, msg->tac, msg->cellID, AcT);
LOG_FUNC_RETURN (rc);
}
/****************************************************************************
** **
** Name: _emm_as_cell_info_ind() **
** **
** Description: Processes the EMMAS-SAP cell information indication **
** primitive **
** **
** EMMAS-SAP - AS->EMM: CELL_INFO_IND - PLMN and cell selection procedure **
** **
** Inputs: msg: The EMMAS-SAP primitive to process **
** Others: None **
** **
** Outputs: None **
** Return: RETURNok, RETURNerror **
** Others: None **
** **
***************************************************************************/
static int _emm_as_cell_info_ind(const emm_as_cell_info_t *msg)
{
LOG_FUNC_IN;
int rc = RETURNok;
LOG_TRACE(INFO, "EMMAS-SAP - Received AS cell information indication");
/* TODO */
LOG_FUNC_RETURN (rc);
}
/*
* --------------------------------------------------------------------------
* Functions executed to send data to the network when requested within EMM
* procedure processing
* --------------------------------------------------------------------------
*/
/****************************************************************************
** **
** Name: _emm_as_set_header() **
** **
** Description: Setup the security header of the given NAS message **
** **
** Inputs: security: The NAS security data to use **
** Others: None **
** **
** Outputs: msg: The NAS message **
** Return: Pointer to the plain NAS message to be se- **
** curity protected if setting of the securi- **
** ty header succeed; **
** NULL pointer otherwise **
** Others: None **
** **
***************************************************************************/
static EMM_msg *_emm_as_set_header(nas_message_t *msg,
const emm_as_security_data_t *security)
{
LOG_FUNC_IN;
msg->header.protocol_discriminator = EPS_MOBILITY_MANAGEMENT_MESSAGE;
if ( security && (security->ksi != EMM_AS_NO_KEY_AVAILABLE) ) {
/* A valid EPS security context exists */
if (security->is_new) {
/* New EPS security context is taken into use */
if (security->k_int) {
if (security->k_enc) {
/* NAS integrity and cyphering keys are available */
msg->header.security_header_type =
SECURITY_HEADER_TYPE_INTEGRITY_PROTECTED_CYPHERED_NEW;
} else {
/* NAS integrity key only is available */
msg->header.security_header_type =
SECURITY_HEADER_TYPE_INTEGRITY_PROTECTED_NEW;
}
LOG_FUNC_RETURN (&msg->security_protected.plain.emm);
}
} else if (security->k_int) {
if (security->k_enc) {
/* NAS integrity and cyphering keys are available */
msg->header.security_header_type =
SECURITY_HEADER_TYPE_INTEGRITY_PROTECTED_CYPHERED;
} else {
/* NAS integrity key only is available */
msg->header.security_header_type =
SECURITY_HEADER_TYPE_INTEGRITY_PROTECTED;
}
LOG_FUNC_RETURN (&msg->security_protected.plain.emm);
} else {
/* No valid EPS security context exists */
msg->header.security_header_type = SECURITY_HEADER_TYPE_NOT_PROTECTED;
LOG_FUNC_RETURN (&msg->plain.emm);
}
} else {
/* No valid EPS security context exists */
msg->header.security_header_type = SECURITY_HEADER_TYPE_NOT_PROTECTED;
LOG_FUNC_RETURN (&msg->plain.emm);
}
/* A valid EPS security context exists but NAS integrity key
* is not available */
LOG_FUNC_RETURN (NULL);
}
/****************************************************************************
** **
** Name: _emm_as_encode() **
** **
** Description: Encodes NAS message into NAS information container **
** **
** Inputs: msg: The NAS message to encode **
** length: The maximum length of the NAS message **
** Others: None **
** **
** Outputs: info: The NAS information container **
** msg: The NAS message to encode **
** Return: The number of bytes successfully encoded **
** Others: None **
** **
***************************************************************************/
static int
_emm_as_encode(
as_nas_info_t *info,
nas_message_t *msg,
int length,
emm_security_context_t *emm_security_context)
{
LOG_FUNC_IN;
int bytes = 0;
if (msg->header.security_header_type != SECURITY_HEADER_TYPE_NOT_PROTECTED) {
emm_msg_header_t *header = &msg->security_protected.plain.emm.header;
/* Expand size of protected NAS message */
length += NAS_MESSAGE_SECURITY_HEADER_SIZE;
/* Set header of plain NAS message */
header->protocol_discriminator = EPS_MOBILITY_MANAGEMENT_MESSAGE;
header->security_header_type = SECURITY_HEADER_TYPE_NOT_PROTECTED;
}
/* Allocate memory to the NAS information container */
info->data = (Byte_t *)malloc(length * sizeof(Byte_t));
if (info->data != NULL) {
/* Encode the NAS message */
bytes = nas_message_encode(
(char *)(info->data),
msg,
length,
emm_security_context);
if (bytes > 0) {
info->length = bytes;
} else {
free(info->data);
info->length = 0;
info->data = NULL;
}
}
LOG_FUNC_RETURN (bytes);
}
/****************************************************************************
** **
** Name: _emm_as_encrypt() **
** **
** Description: Encryts NAS message into NAS information container **
** **
** Inputs: header: The Security header in used **
** msg: The NAS message to encrypt **
** length: The maximum length of the NAS message **
** Others: None **
** **
** Outputs: info: The NAS information container **
** Return: The number of bytes successfully encrypted **
** Others: None **
** **
***************************************************************************/
static int
_emm_as_encrypt(
as_nas_info_t *info,
const nas_message_security_header_t *header,
const char *msg,
int length,
emm_security_context_t *emm_security_context)
{
LOG_FUNC_IN;
int bytes = 0;
if (header->security_header_type != SECURITY_HEADER_TYPE_NOT_PROTECTED) {
/* Expand size of protected NAS message */
length += NAS_MESSAGE_SECURITY_HEADER_SIZE;
}
/* Allocate memory to the NAS information container */
info->data = (Byte_t *)malloc(length * sizeof(Byte_t));
if (info->data != NULL) {
/* Encrypt the NAS information message */
bytes = nas_message_encrypt(
msg,
(char *)(info->data),
header,
length,
emm_security_context);
if (bytes > 0) {
info->length = bytes;
} else {
free(info->data);
info->length = 0;
info->data = NULL;
}
}
LOG_FUNC_RETURN (bytes);
}
/****************************************************************************
** **
** Name: _emm_as_send() **
** **
** Description: Builds NAS message according to the given EMMAS Service **
** Access Point primitive and sends it to the Access Stratum **
** sublayer **
** **
** Inputs: msg: The EMMAS-SAP primitive to be sent **
** Others: None **
** **
** Outputs: None **
** Return: RETURNok, RETURNerror **
** Others: None **
** **
***************************************************************************/
static int _emm_as_send(const nas_user_t *user, const emm_as_t *msg)
{
LOG_FUNC_IN;
as_message_t as_msg;
memset(&as_msg, 0 , sizeof(as_message_t));
switch (msg->primitive) {
case _EMMAS_DATA_REQ:
as_msg.msgID = _emm_as_data_req(
&msg->u.data,
&as_msg.msg.ul_info_transfer_req);
break;
case _EMMAS_STATUS_IND:
as_msg.msgID = _emm_as_status_ind(
&msg->u.status,
&as_msg.msg.ul_info_transfer_req);
break;
case _EMMAS_RELEASE_REQ:
as_msg.msgID = _emm_as_release_req(
&msg->u.release,
&as_msg.msg.nas_release_req);
break;
case _EMMAS_SECURITY_RES:
as_msg.msgID = _emm_as_security_res(
&msg->u.security,
&as_msg.msg.ul_info_transfer_req);
break;
case _EMMAS_ESTABLISH_REQ:
as_msg.msgID = _emm_as_establish_req(
&msg->u.establish,
&as_msg.msg.nas_establish_req);
break;
case _EMMAS_CELL_INFO_REQ:
as_msg.msgID = _emm_as_cell_info_req(&msg->u.cell_info,
&as_msg.msg.cell_info_req);
/*
* TODO: NAS may provide a list of equivalent PLMNs, if available,
* that AS shall use for cell selection and cell reselection.
*/
break;
default:
as_msg.msgID = 0;
break;
}
/* Send the message to the Access Stratum or S1AP in case of MME */
if (as_msg.msgID > 0) {
# if defined(NAS_BUILT_IN_UE)
LOG_TRACE(DEBUG, "EMMAS-SAP - "
"Sending msg with id 0x%x, primitive %s (%d) to RRC layer for transmission",
as_msg.msgID,
_emm_as_primitive_str[msg->primitive - _EMMAS_START - 1],
msg->primitive);
switch (as_msg.msgID) {
case AS_CELL_INFO_REQ: {
nas_itti_cell_info_req(
as_msg.msg.cell_info_req.plmnID,
as_msg.msg.cell_info_req.rat);
LOG_FUNC_RETURN (RETURNok);
}
break;
case AS_NAS_ESTABLISH_REQ: {
nas_itti_nas_establish_req(
as_msg.msg.nas_establish_req.cause,
as_msg.msg.nas_establish_req.type,
as_msg.msg.nas_establish_req.s_tmsi,
as_msg.msg.nas_establish_req.plmnID,
as_msg.msg.nas_establish_req.initialNasMsg.data,
as_msg.msg.nas_establish_req.initialNasMsg.length);
LOG_FUNC_RETURN (RETURNok);
}
break;
case AS_UL_INFO_TRANSFER_REQ: {
nas_itti_ul_data_req(
as_msg.msg.ul_info_transfer_req.UEid,
as_msg.msg.ul_info_transfer_req.nasMsg.data,
as_msg.msg.ul_info_transfer_req.nasMsg.length);
LOG_FUNC_RETURN (RETURNok);
}
break;
case AS_RAB_ESTABLISH_RSP: {
nas_itti_rab_establish_rsp(
as_msg.msg.rab_establish_rsp.s_tmsi,
as_msg.msg.rab_establish_rsp.rabID,
as_msg.msg.rab_establish_rsp.errCode);
LOG_FUNC_RETURN (RETURNok);
}
break;
default:
break;
}
# else
int bytes = as_message_send(&as_msg);
if (bytes > 0) {
LOG_FUNC_RETURN (RETURNok);
}
# endif
}
LOG_FUNC_RETURN (RETURNerror);
}
/****************************************************************************
** **
** Name: _emm_as_data_req() **
** **
** Description: Processes the EMMAS-SAP data transfer request **
** primitive **
** **
** EMMAS-SAP - EMM->AS: DATA_REQ - Data transfer procedure **
** **
** Inputs: msg: The EMMAS-SAP primitive to process **
** Others: None **
** **
** Outputs: as_msg: The message to send to the AS **
** Return: The identifier of the AS message **
** Others: None **
** **
***************************************************************************/
static int _emm_as_data_req(const emm_as_data_t *msg,
ul_info_transfer_req_t *as_msg)
{
LOG_FUNC_IN;
int size = 0;
int is_encoded = FALSE;
LOG_TRACE(INFO, "EMMAS-SAP - Send AS data transfer request");
nas_message_t nas_msg;
memset(&nas_msg, 0 , sizeof(nas_message_t));
/* Setup the AS message */
if (msg->guti) {
as_msg->s_tmsi.MMEcode = msg->guti->gummei.MMEcode;
as_msg->s_tmsi.m_tmsi = msg->guti->m_tmsi;
} else {
as_msg->UEid = msg->ueid;
}
/* Setup the NAS security header */
EMM_msg *emm_msg = _emm_as_set_header(&nas_msg, &msg->sctx);
/* Setup the NAS information message */
if (emm_msg != NULL) switch (msg->NASinfo) {
case EMM_AS_NAS_DATA_ATTACH:
size = emm_send_attach_complete(msg, &emm_msg->attach_complete);
break;
case EMM_AS_NAS_DATA_DETACH:
size = emm_send_detach_request(msg, &emm_msg->detach_request);
break;
default:
/* Send other NAS messages as already encoded ESM messages */
size = msg->NASmsg.length;
is_encoded = TRUE;
break;
}
if (size > 0) {
int bytes;
emm_security_context_t *emm_security_context = NULL;
emm_security_context = _emm_data.security;
if (emm_security_context) {
nas_msg.header.sequence_number = emm_security_context->ul_count.seq_num;
LOG_TRACE(DEBUG,
"Set nas_msg.header.sequence_number -> %u",
nas_msg.header.sequence_number);
}
if (!is_encoded) {
/* Encode the NAS information message */
bytes = _emm_as_encode(&as_msg->nasMsg,
&nas_msg,
size,
emm_security_context);
} else {
/* Encrypt the NAS information message */
bytes = _emm_as_encrypt(&as_msg->nasMsg,
&nas_msg.header,
(char *)(msg->NASmsg.value),
size,
emm_security_context);
}
if (bytes > 0) {
LOG_FUNC_RETURN (AS_UL_INFO_TRANSFER_REQ);
}
}
LOG_FUNC_RETURN (0);
}
/****************************************************************************
** **
** Name: _emm_as_status_ind() **
** **
** Description: Processes the EMMAS-SAP status indication primitive **
** **
** EMMAS-SAP - EMM->AS: STATUS_IND - EMM status report procedure **
** **
** Inputs: msg: The EMMAS-SAP primitive to process **
** Others: None **
** **
** Outputs: as_msg: The message to send to the AS **
** Return: The identifier of the AS message **
** Others: None **
** **
***************************************************************************/
static int _emm_as_status_ind(const emm_as_status_t *msg,
ul_info_transfer_req_t *as_msg)
{
LOG_FUNC_IN;
int size = 0;
LOG_TRACE(INFO, "EMMAS-SAP - Send AS status indication (cause=%d)",
msg->emm_cause);
nas_message_t nas_msg;
memset(&nas_msg, 0 , sizeof(nas_message_t));
/* Setup the AS message */
if (msg->guti) {
as_msg->s_tmsi.MMEcode = msg->guti->gummei.MMEcode;
as_msg->s_tmsi.m_tmsi = msg->guti->m_tmsi;
} else {
as_msg->UEid = msg->ueid;
}
/* Setup the NAS security header */
EMM_msg *emm_msg = _emm_as_set_header(&nas_msg, &msg->sctx);
/* Setup the NAS information message */
if (emm_msg != NULL) {
size = emm_send_status(msg, &emm_msg->emm_status);
}
if (size > 0) {
emm_security_context_t *emm_security_context = NULL;
emm_security_context = _emm_data.security;
if (emm_security_context) {
nas_msg.header.sequence_number = emm_security_context->ul_count.seq_num;
LOG_TRACE(DEBUG,
"Set nas_msg.header.sequence_number -> %u",
nas_msg.header.sequence_number);
}
/* Encode the NAS information message */
int bytes = _emm_as_encode(
&as_msg->nasMsg,
&nas_msg,
size,
emm_security_context);
if (bytes > 0) {
LOG_FUNC_RETURN (AS_UL_INFO_TRANSFER_REQ);
}
}
LOG_FUNC_RETURN (0);
}
/****************************************************************************
** **
** Name: _emm_as_release_req() **
** **
** Description: Processes the EMMAS-SAP connection release request **
** primitive **
** **
** EMMAS-SAP - EMM->AS: RELEASE_REQ - NAS signalling release procedure **
** **
** Inputs: msg: The EMMAS-SAP primitive to process **
** Others: None **
** **
** Outputs: as_msg: The message to send to the AS **
** Return: The identifier of the AS message **
** Others: None **
** **
***************************************************************************/
static int _emm_as_release_req(const emm_as_release_t *msg,
nas_release_req_t *as_msg)
{
LOG_FUNC_IN;
LOG_TRACE(INFO, "EMMAS-SAP - Send AS release request");
/* Setup the AS message */
if (msg->guti) {
as_msg->s_tmsi.MMEcode = msg->guti->gummei.MMEcode;
as_msg->s_tmsi.m_tmsi = msg->guti->m_tmsi;
} else {
as_msg->UEid = msg->ueid;
}
if (msg->cause == EMM_AS_CAUSE_AUTHENTICATION) {
as_msg->cause = AS_AUTHENTICATION_FAILURE;
} else if (msg->cause == EMM_AS_CAUSE_DETACH) {
as_msg->cause = AS_DETACH;
}
LOG_FUNC_RETURN (AS_NAS_RELEASE_REQ);
}
/****************************************************************************
** **
** Name: _emm_as_security_res() **
** **
** Description: Processes the EMMAS-SAP security response primitive **
** **
** EMMAS-SAP - EMM->AS: SECURITY_RES - Security mode control procedure **
** **
** Inputs: msg: The EMMAS-SAP primitive to process **
** Others: None **
** **
** Outputs: as_msg: The message to send to the AS **
** Return: The identifier of the AS message **
** Others: None **
** **
***************************************************************************/
static int _emm_as_security_res(const emm_as_security_t *msg,
ul_info_transfer_req_t *as_msg)
{
LOG_FUNC_IN;
int size = 0;
LOG_TRACE(INFO, "EMMAS-SAP - Send AS security response");
nas_message_t nas_msg;
memset(&nas_msg, 0 , sizeof(nas_message_t));
/* Setup the AS message */
if (msg->guti) {
as_msg->s_tmsi.MMEcode = msg->guti->gummei.MMEcode;
as_msg->s_tmsi.m_tmsi = msg->guti->m_tmsi;
}
/* Setup the NAS security header */
EMM_msg *emm_msg = _emm_as_set_header(&nas_msg, &msg->sctx);
/* Setup the NAS security message */
if (emm_msg != NULL) switch (msg->msgType) {
case EMM_AS_MSG_TYPE_IDENT:
size = emm_send_identity_response(
msg,
&emm_msg->identity_response);
break;
case EMM_AS_MSG_TYPE_AUTH:
if (msg->emm_cause != EMM_CAUSE_SUCCESS) {
size = emm_send_authentication_failure(
msg,
&emm_msg->authentication_failure);
} else {
size = emm_send_authentication_response(
msg,
&emm_msg->authentication_response);
}
break;
case EMM_AS_MSG_TYPE_SMC:
if (msg->emm_cause != EMM_CAUSE_SUCCESS) {
size = emm_send_security_mode_reject(
msg,
&emm_msg->security_mode_reject);
} else {
size = emm_send_security_mode_complete(
msg,
&emm_msg->security_mode_complete);
}
break;
default:
LOG_TRACE(WARNING, "EMMAS-SAP - Type of NAS security "
"message 0x%.2x is not valid", msg->msgType);
}
if (size > 0) {
/* Encode the NAS security message */
int bytes = _emm_as_encode(&as_msg->nasMsg,
&nas_msg,
size,
_emm_data.security);
if (bytes > 0) {
LOG_FUNC_RETURN (AS_UL_INFO_TRANSFER_REQ);
}
}
LOG_FUNC_RETURN (0);
}
/****************************************************************************
** **
** Name: _emm_as_establish_req() **
** **
** Description: Processes the EMMAS-SAP connection establish request **
** primitive **
** **
** EMMAS-SAP - EMM->AS: ESTABLISH_REQ - NAS signalling connection **
** The NAS requests the AS to establish signalling connection **
** to tranfer initial NAS message to the network. **
** **
** Inputs: msg: The EMMAS-SAP primitive to process **
** Others: None **
** **
** Outputs: as_msg: The message to send to the AS **
** Return: The identifier of the AS message **
** Others: None **
** **
***************************************************************************/
static int _emm_as_establish_req(const emm_as_establish_t *msg,
nas_establish_req_t *as_msg)
{
LOG_FUNC_IN;
int size = 0;
LOG_TRACE(INFO, "EMMAS-SAP - Send AS connection establish request");
nas_message_t nas_msg;
memset(&nas_msg, 0 , sizeof(nas_message_t));
/* Setup the AS message */
as_msg->cause = msg->RRCcause;
as_msg->type = msg->RRCtype;
as_msg->plmnID = *msg->plmnID;
/* Derive the S-TMSI from the GUTI, if valid */
if (msg->UEid.guti) {
as_msg->s_tmsi.MMEcode = msg->UEid.guti->gummei.MMEcode;
as_msg->s_tmsi.m_tmsi = msg->UEid.guti->m_tmsi;
}
/* Setup the NAS security header */
EMM_msg *emm_msg = _emm_as_set_header(&nas_msg, &msg->sctx);
/* Setup the initial NAS information message */
if (emm_msg != NULL) switch (msg->NASinfo) {
case EMM_AS_NAS_INFO_ATTACH:
size = emm_send_attach_request(msg, &emm_msg->attach_request);
break;
case EMM_AS_NAS_INFO_DETACH:
size = emm_send_initial_detach_request(msg,
&emm_msg->detach_request);
break;
case EMM_AS_NAS_INFO_TAU:
size = emm_send_initial_tau_request(msg,
&emm_msg->tracking_area_update_request);
break;
case EMM_AS_NAS_INFO_SR:
size = emm_send_initial_sr_request(msg,
&emm_msg->service_request);
break;
case EMM_AS_NAS_INFO_EXTSR:
size = emm_send_initial_extsr_request(msg,
&emm_msg->extended_service_request);
break;
default:
LOG_TRACE(WARNING, "EMMAS-SAP - Type of initial NAS "
"message 0x%.2x is not valid", msg->NASinfo);
break;
}
if (size > 0) {
/* Encode the initial NAS information message */
int bytes = _emm_as_encode(
&as_msg->initialNasMsg,
&nas_msg,
size,
_emm_data.security);
if (bytes > 0) {
LOG_FUNC_RETURN (AS_NAS_ESTABLISH_REQ);
}
}
LOG_FUNC_RETURN (0);
}
/****************************************************************************
** **
** Name: _emm_as_cell_info_req() **
** **
** Description: Processes the EMMAS-SAP cell information request **
** primitive **
** **
** EMMAS-SAP - EMM->AS: CELL_INFO_REQ - PLMN and cell selection procedure **
** The NAS requests the AS to select a cell belonging to the **
** selected PLMN with associated Radio Access Technologies. **
** **
** Inputs: msg: The EMMAS-SAP primitive to process **
** Others: None **
** **
** Outputs: as_msg: The message to send to the AS **
** Return: The identifier of the AS message **
** Others: None **
** **
***************************************************************************/
static int _emm_as_cell_info_req(const emm_as_cell_info_t *msg,
cell_info_req_t *as_msg)
{
LOG_FUNC_IN;
LOG_TRACE(INFO, "EMMAS-SAP - Send AS cell information request");
as_msg->plmnID = msg->plmnIDs.plmn[0];
as_msg->rat = msg->rat;
LOG_FUNC_RETURN (AS_CELL_INFO_REQ);
}