/* * 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 "emm_proc.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 ****************/ /****************************************************************************/ /****************************************************************************/ /******************* 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, 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(nas_user_t *user, 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_data_t *emm_data, const emm_as_security_t *, ul_info_transfer_req_t *); static int _emm_as_establish_req(const emm_data_t *emm_data, 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_data_t *emm_data, const emm_as_data_t *msg, ul_info_transfer_req_t *); static int _emm_as_status_ind(const emm_data_t *emm_data, 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; 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); 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(user, &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(user, 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, 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 = user->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(user->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 = user->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, 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, &data); } free(plain_msg); } } else { /* Process successfull lower layer transfer indication */ rc = lowerlayer_success(user); } } else { /* Process lower layer transmission failure of NAS message */ rc = lowerlayer_failure(user); } 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); 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, user->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); 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(nas_user_t *user, 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(user, 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(user->emm_data, &msg->u.data, &as_msg.msg.ul_info_transfer_req); break; case _EMMAS_STATUS_IND: as_msg.msgID = _emm_as_status_ind(user->emm_data, &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(user->emm_data, &msg->u.security, &as_msg.msg.ul_info_transfer_req); break; case _EMMAS_ESTABLISH_REQ: as_msg.msgID = _emm_as_establish_req(user->emm_data, &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, user->ueid); 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, user->ueid); 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, user->ueid); 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, user->ueid); 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_data_t *emm_data, 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_data_t *emm_data, 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_data_t *emm_data, 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_data_t *emm_data, 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); }