1089 lines
49 KiB
C
1089 lines
49 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.1 (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_main.c
|
|
|
|
Version 0.1
|
|
|
|
Date 2012/10/10
|
|
|
|
Product NAS stack
|
|
|
|
Subsystem EPS Mobility Management
|
|
|
|
Author Frederic Maurel
|
|
|
|
Description Defines the EPS Mobility Management procedure call manager,
|
|
the main entry point for elementary EMM processing.
|
|
|
|
*****************************************************************************/
|
|
|
|
#include "emm_main.h"
|
|
#include "nas_log.h"
|
|
#include "utils.h"
|
|
#include "emmData.h"
|
|
#include "MobileIdentity.h"
|
|
#include "emm_proc_defs.h"
|
|
|
|
#include "memory.h"
|
|
#include "usim_api.h"
|
|
#include "IdleMode.h"
|
|
|
|
#include <string.h> // memset, memcpy, strlen
|
|
#include <stdio.h> // sprintf
|
|
#include <stdlib.h> // malloc, free
|
|
|
|
/****************************************************************************/
|
|
/**************** E X T E R N A L D E F I N I T I O N S ****************/
|
|
/****************************************************************************/
|
|
extern uint8_t usim_test;
|
|
|
|
/****************************************************************************/
|
|
/******************* L O C A L D E F I N I T I O N S *******************/
|
|
/****************************************************************************/
|
|
|
|
static int _emm_main_get_imei(imei_t *imei, const char *imei_str);
|
|
|
|
static int _emm_main_imsi_cmp(imsi_t *imsi1, imsi_t *imsi2);
|
|
|
|
static const char *_emm_main_get_plmn(emm_plmn_list_t *emm_plmn_list, const plmn_t *plmn, int index,
|
|
int format, size_t *size);
|
|
|
|
static int _emm_main_get_plmn_index(emm_plmn_list_t *emm_plmn_list, const char *plmn, int format);
|
|
|
|
/*
|
|
* Callback executed whenever a change in the network has to be notified
|
|
* to the user application
|
|
*/
|
|
static emm_indication_callback_t _emm_main_user_callback;
|
|
static int _emm_main_callback(user_api_id_t *user_api_id, emm_data_t *emm_data, int size);
|
|
|
|
/****************************************************************************/
|
|
/****************** E X P O R T E D F U N C T I O N S ******************/
|
|
/****************************************************************************/
|
|
|
|
/*
|
|
* Initialize EMM timers
|
|
*/
|
|
void _emm_timers_initialize(emm_timers_t *emm_timers) {
|
|
emm_timers->T3410.id = NAS_TIMER_INACTIVE_ID;
|
|
emm_timers->T3410.sec = T3410_DEFAULT_VALUE;
|
|
emm_timers->T3411.id = NAS_TIMER_INACTIVE_ID;
|
|
emm_timers->T3411.sec = T3411_DEFAULT_VALUE;
|
|
emm_timers->T3402.id = NAS_TIMER_INACTIVE_ID;
|
|
emm_timers->T3402.sec = T3402_DEFAULT_VALUE;
|
|
emm_timers->T3416.id = NAS_TIMER_INACTIVE_ID;
|
|
emm_timers->T3416.sec = T3416_DEFAULT_VALUE;
|
|
emm_timers->T3417.id = NAS_TIMER_INACTIVE_ID;
|
|
emm_timers->T3417.sec = T3417_DEFAULT_VALUE;
|
|
emm_timers->T3418.id = NAS_TIMER_INACTIVE_ID;
|
|
emm_timers->T3418.sec = T3418_DEFAULT_VALUE;
|
|
emm_timers->T3420.id = NAS_TIMER_INACTIVE_ID;
|
|
emm_timers->T3420.sec = T3420_DEFAULT_VALUE;
|
|
emm_timers->T3421.id = NAS_TIMER_INACTIVE_ID;
|
|
emm_timers->T3421.sec = T3421_DEFAULT_VALUE;
|
|
emm_timers->T3423.id = NAS_TIMER_INACTIVE_ID;
|
|
emm_timers->T3423.sec = T3423_DEFAULT_VALUE;
|
|
emm_timers->T3430.id = NAS_TIMER_INACTIVE_ID;
|
|
emm_timers->T3430.sec = T3430_DEFAULT_VALUE;
|
|
}
|
|
|
|
void _emm_attach_initialize(emm_attach_data_t *emm_attach_data) {
|
|
emm_attach_data->attempt_count = 0;
|
|
}
|
|
|
|
void _emm_detach_initialize(emm_detach_data_t *emm_detach) {
|
|
emm_detach->count = 0;
|
|
emm_detach->switch_off = FALSE;
|
|
emm_detach->type = EMM_DETACH_TYPE_RESERVED;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** **
|
|
** Name: emm_main_initialize() **
|
|
** **
|
|
** Description: Initializes EMM internal data **
|
|
** **
|
|
** Inputs: cb: The user notification callback **
|
|
** imei: The IMEI read from the UE's non-volatile **
|
|
** memory **
|
|
** **
|
|
** Outputs: None **
|
|
** Return: None **
|
|
** Others: user->emm_data-> **
|
|
** **
|
|
***************************************************************************/
|
|
void emm_main_initialize(nas_user_t *user, emm_indication_callback_t cb, const char *imei)
|
|
{
|
|
LOG_FUNC_IN;
|
|
user->emm_data = calloc_or_fail(sizeof(emm_data_t));
|
|
/* USIM validity indicator */
|
|
user->emm_data->usim_is_valid = FALSE;
|
|
/* The IMEI read from the UE's non-volatile memory */
|
|
user->emm_data->imei = (imei_t *)malloc(sizeof(imei_t));
|
|
user->emm_data->imei->length = _emm_main_get_imei(user->emm_data->imei, imei);
|
|
/* The IMSI, valid only if USIM is present */
|
|
user->emm_data->imsi = NULL;
|
|
/* EPS location information */
|
|
user->emm_data->guti = NULL;
|
|
user->emm_data->tai = NULL;
|
|
user->emm_data->ltai.n_tais = 0;
|
|
/* EPS Connection Management status */
|
|
user->emm_data->ecm_status = ECM_IDLE;
|
|
/* Network selection mode of operation */
|
|
user->emm_data->plmn_mode = EMM_DATA_PLMN_AUTO;
|
|
/* Index of the PLMN manually selected by the user */
|
|
user->emm_data->plmn_index = -1;
|
|
/* Selected Radio Access Technology */
|
|
user->emm_data->plmn_rat = NET_ACCESS_UNAVAILABLE;
|
|
/* Selected PLMN */
|
|
memset(&user->emm_data->splmn, 0xFF, sizeof(plmn_t));
|
|
user->emm_data->is_rplmn = FALSE;
|
|
user->emm_data->is_eplmn = FALSE;
|
|
/* Radio Access Technology of the serving cell */
|
|
user->emm_data->rat = NET_ACCESS_UNAVAILABLE;
|
|
/* Network registration status */
|
|
user->emm_data->stat = NET_REG_STATE_OFF;
|
|
user->emm_data->is_attached = FALSE;
|
|
user->emm_data->is_emergency = FALSE;
|
|
/* Location/Tracking area code */
|
|
user->emm_data->tac = 0; // two byte in hexadecimal format
|
|
/* Identifier of the serving cell */
|
|
user->emm_data->ci = 0; // four byte in hexadecimal format
|
|
/* List of operators present in the network */
|
|
memset(user->emm_data->plist.buffer, 0, EMM_DATA_BUFFER_SIZE + 1);
|
|
/* Home PLMN */
|
|
memset(&user->emm_data->hplmn, 0xFF, sizeof(plmn_t));
|
|
/* List of Forbidden PLMNs */
|
|
user->emm_data->fplmn.n_plmns = 0;
|
|
/* List of Forbidden PLMNs for GPRS service */
|
|
user->emm_data->fplmn_gprs.n_plmns = 0;
|
|
/* List of Equivalent HPLMNs */
|
|
user->emm_data->ehplmn.n_plmns = 0;
|
|
/* List of user controlled PLMNs */
|
|
user->emm_data->plmn.n_plmns = 0;
|
|
/* List of operator controlled PLMNs */
|
|
user->emm_data->oplmn.n_plmns = 0;
|
|
/* List of operator network name records */
|
|
user->emm_data->n_opnns = 0;
|
|
/* List of Forbidden Tracking Areas */
|
|
user->emm_data->ftai.n_tais = 0;
|
|
/* List of Forbidden Tracking Areas for roaming */
|
|
user->emm_data->ftai_roaming.n_tais = 0;
|
|
|
|
/*
|
|
* Get USIM application data
|
|
*/
|
|
if ( usim_api_read(user->usim_data_store, &user->usim_data) != RETURNok ) {
|
|
/* The USIM application may not be present or not valid */
|
|
LOG_TRACE(WARNING, "EMM-MAIN - Failed to read USIM application data");
|
|
} else {
|
|
int i;
|
|
|
|
/* The USIM application is present and valid */
|
|
LOG_TRACE(INFO, "EMM-MAIN - USIM application data successfully read");
|
|
user->emm_data->usim_is_valid = TRUE;
|
|
|
|
/* print keys (for debugging) */
|
|
{
|
|
char usim_api_k[256];
|
|
char opc[256];
|
|
sprintf(usim_api_k,
|
|
"%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x "
|
|
"%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x",
|
|
user->usim_data.keys.usim_api_k[0],
|
|
user->usim_data.keys.usim_api_k[1],
|
|
user->usim_data.keys.usim_api_k[2],
|
|
user->usim_data.keys.usim_api_k[3],
|
|
user->usim_data.keys.usim_api_k[4],
|
|
user->usim_data.keys.usim_api_k[5],
|
|
user->usim_data.keys.usim_api_k[6],
|
|
user->usim_data.keys.usim_api_k[7],
|
|
user->usim_data.keys.usim_api_k[8],
|
|
user->usim_data.keys.usim_api_k[9],
|
|
user->usim_data.keys.usim_api_k[10],
|
|
user->usim_data.keys.usim_api_k[11],
|
|
user->usim_data.keys.usim_api_k[12],
|
|
user->usim_data.keys.usim_api_k[13],
|
|
user->usim_data.keys.usim_api_k[14],
|
|
user->usim_data.keys.usim_api_k[15]);
|
|
sprintf(opc,
|
|
"%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x "
|
|
"%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x",
|
|
user->usim_data.keys.opc[0],
|
|
user->usim_data.keys.opc[1],
|
|
user->usim_data.keys.opc[2],
|
|
user->usim_data.keys.opc[3],
|
|
user->usim_data.keys.opc[4],
|
|
user->usim_data.keys.opc[5],
|
|
user->usim_data.keys.opc[6],
|
|
user->usim_data.keys.opc[7],
|
|
user->usim_data.keys.opc[8],
|
|
user->usim_data.keys.opc[9],
|
|
user->usim_data.keys.opc[10],
|
|
user->usim_data.keys.opc[11],
|
|
user->usim_data.keys.opc[12],
|
|
user->usim_data.keys.opc[13],
|
|
user->usim_data.keys.opc[14],
|
|
user->usim_data.keys.opc[15]);
|
|
LOG_TRACE(INFO, "EMM-MAIN - usim_api_k: %s", usim_api_k);
|
|
LOG_TRACE(INFO, "EMM-MAIN - opc: %s", opc);
|
|
}
|
|
|
|
/* Get the Home PLMN derived from the IMSI */
|
|
user->emm_data->hplmn.MCCdigit1 = user->usim_data.imsi.u.num.digit1;
|
|
user->emm_data->hplmn.MCCdigit2 = user->usim_data.imsi.u.num.digit2;
|
|
user->emm_data->hplmn.MCCdigit3 = user->usim_data.imsi.u.num.digit3;
|
|
user->emm_data->hplmn.MNCdigit1 = user->usim_data.imsi.u.num.digit4;
|
|
user->emm_data->hplmn.MNCdigit2 = user->usim_data.imsi.u.num.digit5;
|
|
user->emm_data->hplmn.MNCdigit3 = user->usim_data.imsi.u.num.digit6;
|
|
|
|
/* Get the list of forbidden PLMNs */
|
|
for (i=0; (i < EMM_DATA_FPLMN_MAX) && (i < USIM_FPLMN_MAX); i++) {
|
|
if ( PLMN_IS_VALID(user->usim_data.fplmn[i]) ) {
|
|
user->emm_data->fplmn.plmn[i] = user->usim_data.fplmn[i];
|
|
user->emm_data->fplmn.n_plmns += 1;
|
|
}
|
|
}
|
|
|
|
/* Get the list of Equivalent HPLMNs */
|
|
for (i=0; (i < EMM_DATA_EHPLMN_MAX) && (i < USIM_EHPLMN_MAX); i++) {
|
|
if ( PLMN_IS_VALID(user->usim_data.ehplmn[i]) ) {
|
|
user->emm_data->ehplmn.plmn[i] = user->usim_data.ehplmn[i];
|
|
user->emm_data->ehplmn.n_plmns += 1;
|
|
}
|
|
}
|
|
|
|
/* Get the list of User controlled PLMN Selector */
|
|
for (i=0; (i < EMM_DATA_PLMN_MAX) && (i < USIM_PLMN_MAX); i++) {
|
|
if ( PLMN_IS_VALID(user->usim_data.plmn[i].plmn) ) {
|
|
user->emm_data->plmn.plmn[i] = user->usim_data.plmn[i].plmn;
|
|
user->emm_data->userAcT[i] = user->usim_data.plmn[i].AcT;
|
|
user->emm_data->plmn.n_plmns += 1;
|
|
}
|
|
}
|
|
|
|
/* Get the list of Operator controlled PLMN Selector */
|
|
for (i=0; (i < EMM_DATA_OPLMN_MAX) && (i < USIM_OPLMN_MAX); i++) {
|
|
if ( PLMN_IS_VALID(user->usim_data.oplmn[i].plmn) ) {
|
|
user->emm_data->oplmn.plmn[i] = user->usim_data.oplmn[i].plmn;
|
|
user->emm_data->operAcT[i] = user->usim_data.oplmn[i].AcT;
|
|
user->emm_data->oplmn.n_plmns += 1;
|
|
}
|
|
}
|
|
|
|
/* Get the list of Operator network name records */
|
|
for (i=0; (i < EMM_DATA_OPNN_MAX) && (i < USIM_OPL_MAX); i++) {
|
|
if ( PLMN_IS_VALID(user->usim_data.opl[i].plmn) ) {
|
|
int pnn_id = user->usim_data.opl[i].record_id;
|
|
user->emm_data->opnn[i].plmn = &user->usim_data.opl[i].plmn;
|
|
user->emm_data->opnn[i].fullname = (char *)user->usim_data.pnn[pnn_id].fullname.value;
|
|
user->emm_data->opnn[i].shortname = (char *)user->usim_data.pnn[pnn_id].shortname.value;
|
|
user->emm_data->n_opnns += 1;
|
|
}
|
|
}
|
|
|
|
/* TODO: Get the Higher Priority PLMN search period parameter */
|
|
|
|
/* Get the EPS location information */
|
|
if (PLMN_IS_VALID(user->usim_data.epsloci.guti.gummei.plmn)) {
|
|
user->emm_data->guti = &user->usim_data.epsloci.guti;
|
|
}
|
|
|
|
if (TAI_IS_VALID(user->usim_data.epsloci.tai)) {
|
|
user->emm_data->tai = &user->usim_data.epsloci.tai;
|
|
}
|
|
|
|
user->emm_data->status = user->usim_data.epsloci.status;
|
|
|
|
/* Get NAS configuration parameters */
|
|
user->emm_data->NAS_SignallingPriority =
|
|
user->usim_data.nasconfig.NAS_SignallingPriority.value[0];
|
|
user->emm_data->NMO_I_Behaviour = user->usim_data.nasconfig.NMO_I_Behaviour.value[0];
|
|
user->emm_data->AttachWithImsi = user->usim_data.nasconfig.AttachWithImsi.value[0];
|
|
user->emm_data->MinimumPeriodicSearchTimer =
|
|
user->usim_data.nasconfig.MinimumPeriodicSearchTimer.value[0];
|
|
user->emm_data->ExtendedAccessBarring =
|
|
user->usim_data.nasconfig.ExtendedAccessBarring.value[0];
|
|
user->emm_data->Timer_T3245_Behaviour =
|
|
user->usim_data.nasconfig.Timer_T3245_Behaviour.value[0];
|
|
|
|
/*
|
|
* Get EPS NAS security context
|
|
*/
|
|
/* Create NAS security context */
|
|
user->emm_data->security =
|
|
(emm_security_context_t *)malloc(sizeof(emm_security_context_t));
|
|
|
|
if (user->emm_data->security != NULL) {
|
|
memset(user->emm_data->security, 0, sizeof(emm_security_context_t));
|
|
|
|
/* Type of security context */
|
|
if (user->usim_data.securityctx.KSIasme.value[0] !=
|
|
USIM_KSI_NOT_AVAILABLE) {
|
|
user->emm_data->security->type = EMM_KSI_NATIVE;
|
|
} else {
|
|
user->emm_data->security->type = EMM_KSI_NOT_AVAILABLE;
|
|
}
|
|
|
|
/* EPS key set identifier */
|
|
user->emm_data->security->eksi = user->usim_data.securityctx.KSIasme.value[0];
|
|
/* ASME security key */
|
|
user->emm_data->security->kasme.length =
|
|
user->usim_data.securityctx.Kasme.length;
|
|
user->emm_data->security->kasme.value =
|
|
(uint8_t *)malloc(user->emm_data->security->kasme.length);
|
|
|
|
if (user->emm_data->security->kasme.value) {
|
|
memcpy(user->emm_data->security->kasme.value,
|
|
user->usim_data.securityctx.Kasme.value,
|
|
user->emm_data->security->kasme.length);
|
|
}
|
|
|
|
/* Downlink count parameter */
|
|
if (user->usim_data.securityctx.dlNAScount.length <= sizeof(uint32_t)) {
|
|
memcpy(&user->emm_data->security->dl_count,
|
|
user->usim_data.securityctx.dlNAScount.value,
|
|
user->usim_data.securityctx.dlNAScount.length);
|
|
}
|
|
|
|
/* Uplink count parameter */
|
|
if (user->usim_data.securityctx.ulNAScount.length <= sizeof(uint32_t)) {
|
|
memcpy(&user->emm_data->security->ul_count,
|
|
user->usim_data.securityctx.ulNAScount.value,
|
|
user->usim_data.securityctx.ulNAScount.length);
|
|
}
|
|
|
|
/* Ciphering algorithm */
|
|
user->emm_data->security->capability.eps_encryption =
|
|
((user->usim_data.securityctx.algorithmID.value[0] >> 4) & 0xf);
|
|
/* Identity protection algorithm */
|
|
user->emm_data->security->capability.eps_integrity =
|
|
(user->usim_data.securityctx.algorithmID.value[0] & 0xf);
|
|
/* NAS integrity and cyphering keys are not available */
|
|
} else {
|
|
LOG_TRACE(WARNING,
|
|
"EMM-PROC - Failed to create security context");
|
|
}
|
|
|
|
/*
|
|
* Get EMM data from the UE's non-volatile memory
|
|
*/
|
|
memset(&user->emm_data->nvdata.rplmn, 0xFF, sizeof(plmn_t));
|
|
user->emm_data->nvdata.eplmn.n_plmns = 0;
|
|
|
|
/* Get EMM data stored in the non-volatile memory device */
|
|
int rc = memory_read(user->emm_nvdata_store, &user->emm_data->nvdata, sizeof(emm_nvdata_t));
|
|
|
|
if (rc != RETURNok) {
|
|
LOG_TRACE(ERROR, "EMM-MAIN - Failed to read %s", user->emm_nvdata_store);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* Check the IMSI */
|
|
LOG_TRACE(INFO, "EMM-MAIN - EMM data successfully read");
|
|
user->emm_data->imsi = &user->usim_data.imsi;
|
|
int imsi_ok = _emm_main_imsi_cmp(&user->emm_data->nvdata.imsi,
|
|
&user->usim_data.imsi);
|
|
|
|
if (!imsi_ok) {
|
|
LOG_TRACE(WARNING, "EMM-MAIN - IMSI checking failed nvram: "
|
|
"%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x, "
|
|
"usim: %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x",
|
|
user->emm_data->nvdata.imsi.u.value[0],
|
|
user->emm_data->nvdata.imsi.u.value[1],
|
|
user->emm_data->nvdata.imsi.u.value[2],
|
|
user->emm_data->nvdata.imsi.u.value[3],
|
|
user->emm_data->nvdata.imsi.u.value[4],
|
|
user->emm_data->nvdata.imsi.u.value[5],
|
|
user->emm_data->nvdata.imsi.u.value[6],
|
|
user->emm_data->nvdata.imsi.u.value[7],
|
|
user->usim_data.imsi.u.value[0],
|
|
user->usim_data.imsi.u.value[1],
|
|
user->usim_data.imsi.u.value[2],
|
|
user->usim_data.imsi.u.value[3],
|
|
user->usim_data.imsi.u.value[4],
|
|
user->usim_data.imsi.u.value[5],
|
|
user->usim_data.imsi.u.value[6],
|
|
user->usim_data.imsi.u.value[7]);
|
|
memset(&user->emm_data->nvdata.rplmn, 0xFF, sizeof(plmn_t));
|
|
user->emm_data->nvdata.eplmn.n_plmns = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize EMM timers
|
|
*/
|
|
user->emm_data->emm_timers = calloc_or_fail(sizeof(emm_timers_t));
|
|
_emm_timers_initialize(user->emm_data->emm_timers);
|
|
|
|
/*
|
|
* Initialize Internal data used for detach procedure
|
|
*/
|
|
user->emm_data->emm_detach_data = calloc_or_fail(sizeof(emm_detach_data_t));
|
|
_emm_detach_initialize(user->emm_data->emm_detach_data);
|
|
|
|
/*
|
|
* Initialize Internal data used for attach procedure
|
|
*/
|
|
user->emm_data->emm_attach_data = calloc_or_fail(sizeof(emm_attach_data_t));
|
|
_emm_attach_initialize(user->emm_data->emm_attach_data);
|
|
|
|
/*
|
|
* Initialize the user notification callback
|
|
*/
|
|
_emm_main_user_callback = *cb;
|
|
|
|
/*
|
|
* Initialize EMM internal data used for UE in idle mode
|
|
*/
|
|
IdleMode_initialize(user, &_emm_main_callback);
|
|
|
|
LOG_FUNC_OUT;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
** **
|
|
** Name: emm_main_cleanup() **
|
|
** **
|
|
** Description: Performs the EPS Mobility Management clean up procedure **
|
|
** **
|
|
** Inputs: None **
|
|
** Others: None **
|
|
** **
|
|
** Outputs: None **
|
|
** Return: None **
|
|
** Others: None **
|
|
** **
|
|
***************************************************************************/
|
|
void emm_main_cleanup(nas_user_t *user)
|
|
{
|
|
LOG_FUNC_IN;
|
|
|
|
emm_data_t *emm_data = user->emm_data;
|
|
|
|
if (emm_data->usim_is_valid) {
|
|
/*
|
|
* TODO: Update USIM application data
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* Store EMM data into the UE's non-volatile memory
|
|
* - Registered PLMN
|
|
* - List of equivalent PLMNs
|
|
*/
|
|
int rc = memory_write(user->emm_nvdata_store, &emm_data->nvdata, sizeof(emm_nvdata_t));
|
|
|
|
if (rc != RETURNok) {
|
|
LOG_TRACE(ERROR, "EMM-MAIN - Failed to write %s", user->emm_nvdata_store);
|
|
}
|
|
|
|
/* Release dynamically allocated memory */
|
|
if (emm_data->imei) {
|
|
free(emm_data->imei);
|
|
emm_data->imei = NULL;
|
|
}
|
|
|
|
if (emm_data->security) {
|
|
emm_security_context_t *security = emm_data->security;
|
|
|
|
if (security->kasme.value) {
|
|
free(security->kasme.value);
|
|
security->kasme.value = NULL;
|
|
security->kasme.length = 0;
|
|
}
|
|
|
|
if (security->knas_enc.value) {
|
|
free(security->knas_enc.value);
|
|
security->knas_enc.value = NULL;
|
|
security->knas_enc.length = 0;
|
|
}
|
|
|
|
if (security->knas_int.value) {
|
|
free(security->knas_int.value);
|
|
security->knas_int.value = NULL;
|
|
security->knas_int.length = 0;
|
|
}
|
|
|
|
free(emm_data->security);
|
|
emm_data->security = NULL;
|
|
}
|
|
LOG_FUNC_OUT;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** **
|
|
** Name: emm_main_get_imsi() **
|
|
** **
|
|
** Description: Get the International Mobile Subscriber Identity number **
|
|
** **
|
|
** Inputs: None **
|
|
** Others: user->emm_data-> **
|
|
** **
|
|
** Outputs: None **
|
|
** Return: Pointer to the IMSI **
|
|
** Others: None **
|
|
** **
|
|
***************************************************************************/
|
|
const imsi_t *emm_main_get_imsi(emm_data_t *emm_data)
|
|
{
|
|
LOG_FUNC_IN;
|
|
LOG_FUNC_RETURN (&emm_data->nvdata.imsi);
|
|
}
|
|
|
|
/****************************************************************************
|
|
** **
|
|
** Name: emm_main_get_msisdn() **
|
|
** **
|
|
** Description: Get the Mobile Subscriber Dialing Number from the USIM **
|
|
** **
|
|
** Inputs: None **
|
|
** **
|
|
** Outputs: None **
|
|
** Return: Pointer to the subscriber dialing number **
|
|
** Others: None **
|
|
** **
|
|
***************************************************************************/
|
|
const msisdn_t *emm_main_get_msisdn(nas_user_t *user)
|
|
{
|
|
LOG_FUNC_IN;
|
|
LOG_FUNC_RETURN (&user->usim_data.msisdn.number);
|
|
}
|
|
|
|
/****************************************************************************
|
|
** **
|
|
** Name: emm_main_set_plmn_selection_mode() **
|
|
** **
|
|
** Description: Set the network selection mode of operation to the given **
|
|
** mode and update the manually selected network selection **
|
|
** data **
|
|
** **
|
|
** Inputs: mode: The specified network selection mode of **
|
|
** operation **
|
|
** format: The representation format of the PLMN **
|
|
** identifier **
|
|
** plmn: Identifier of the selected PLMN **
|
|
** rat: The selected Radio Access Techonology **
|
|
** Others: None **
|
|
** **
|
|
** Outputs: None **
|
|
** Return: RETURNok, RETURNerror **
|
|
** Others: user->emm_data-> **
|
|
** **
|
|
***************************************************************************/
|
|
int emm_main_set_plmn_selection_mode(nas_user_t *user, int mode, int format,
|
|
const network_plmn_t *plmn, int rat)
|
|
{
|
|
LOG_FUNC_IN;
|
|
|
|
int index;
|
|
emm_data_t *emm_data = user->emm_data;
|
|
emm_plmn_list_t *emm_plmn_list = user->emm_plmn_list;
|
|
|
|
LOG_TRACE(INFO, "EMM-MAIN - PLMN selection: mode=%d, format=%d, plmn=%s, "
|
|
"rat=%d", mode, format, (const char *)&plmn->id, rat);
|
|
|
|
emm_data->plmn_mode = mode;
|
|
|
|
if (mode != EMM_DATA_PLMN_AUTO) {
|
|
/* Get the index of the PLMN in the list of available PLMNs */
|
|
index = _emm_main_get_plmn_index(emm_plmn_list, (const char *)&plmn->id, format);
|
|
|
|
if (index < 0) {
|
|
LOG_TRACE(WARNING, "EMM-MAIN - PLMN %s not available",
|
|
(const char *)&plmn->id);
|
|
} else {
|
|
/* Update the manually selected network selection data */
|
|
emm_data->plmn_index = index;
|
|
emm_data->plmn_rat = rat;
|
|
}
|
|
} else {
|
|
/*
|
|
* Get the index of the last PLMN the UE already tried to automatically
|
|
* register to when switched on; the equivalent PLMNs list shall not be
|
|
* applied to the user reselection in Automatic Network Selection Mode.
|
|
*/
|
|
index = IdleMode_get_hplmn_index(emm_plmn_list);
|
|
}
|
|
|
|
LOG_FUNC_RETURN (index);
|
|
}
|
|
|
|
/****************************************************************************
|
|
** **
|
|
** Name: emm_main_get_plmn_selection_mode() **
|
|
** **
|
|
** Description: Get the current value of the network selection mode of **
|
|
** operation **
|
|
** **
|
|
** Inputs: None **
|
|
** Others: user->emm_data-> **
|
|
** **
|
|
** Outputs: None **
|
|
** Return: The value of the network selection mode **
|
|
** Others: None **
|
|
** **
|
|
***************************************************************************/
|
|
int emm_main_get_plmn_selection_mode(emm_data_t *emm_data)
|
|
{
|
|
LOG_FUNC_IN;
|
|
LOG_FUNC_RETURN (emm_data->plmn_mode);
|
|
}
|
|
|
|
/****************************************************************************
|
|
** **
|
|
** Name: emm_main_get_plmn_list() **
|
|
** **
|
|
** Description: Get the list of available PLMNs **
|
|
** **
|
|
** Inputs: None **
|
|
** Others: user->emm_data-> **
|
|
** **
|
|
** Outputs: plist: Pointer to the list of available PLMNs **
|
|
** Return: The size of the list in bytes **
|
|
** Others: None **
|
|
** **
|
|
***************************************************************************/
|
|
int emm_main_get_plmn_list(emm_plmn_list_t *emm_plmn_list, emm_data_t *emm_data, const char **plist)
|
|
{
|
|
LOG_FUNC_IN;
|
|
|
|
int size = IdleMode_update_plmn_list(emm_plmn_list, emm_data, 0);
|
|
*plist = emm_data->plist.buffer;
|
|
|
|
LOG_FUNC_RETURN (size);
|
|
}
|
|
|
|
/****************************************************************************
|
|
** **
|
|
** Name: emm_main_get_selected_plmn() **
|
|
** **
|
|
** Description: Get the identifier of the currently selected PLMN **
|
|
** **
|
|
** Inputs: format: The requested format of the string repre- **
|
|
** sentation of the PLMN identifier **
|
|
** **
|
|
** Outputs: plmn: The selected PLMN identifier coded in the **
|
|
** requested format **
|
|
** Return: A pointer to the string representation of **
|
|
** the selected PLMN **
|
|
** Others: None **
|
|
** **
|
|
***************************************************************************/
|
|
const char *emm_main_get_selected_plmn(emm_plmn_list_t *emm_plmn_list, emm_data_t *emm_data, network_plmn_t *plmn, int format)
|
|
{
|
|
LOG_FUNC_IN;
|
|
|
|
size_t size = 0;
|
|
/*
|
|
* Get the identifier of the selected PLMN in the list of available PLMNs
|
|
*/
|
|
int index = IdleMode_get_splmn_index(emm_plmn_list);
|
|
|
|
if ( !(index < 0) ) {
|
|
const char *name = _emm_main_get_plmn(emm_plmn_list, &emm_data->splmn, index,
|
|
format, &size);
|
|
|
|
if (size > 0) {
|
|
LOG_FUNC_RETURN ((char *) memcpy(&plmn->id, name, size));
|
|
}
|
|
}
|
|
|
|
LOG_FUNC_RETURN (NULL);
|
|
}
|
|
|
|
/****************************************************************************
|
|
** **
|
|
** Name: emm_main_get_registered_plmn() **
|
|
** **
|
|
** Description: Get the identifier of the currently registered PLMN **
|
|
** **
|
|
** Inputs: format: The requested format of the string repre- **
|
|
** sentation of the PLMN identifier **
|
|
** **
|
|
** Outputs: plmn: The registered PLMN identifier coded in **
|
|
** the requested format **
|
|
** Return: A pointer to the string representation of **
|
|
** the registered PLMN **
|
|
** Others: None **
|
|
** **
|
|
***************************************************************************/
|
|
const char *emm_main_get_registered_plmn(emm_plmn_list_t *emm_plmn_list, emm_data_t *emm_data, network_plmn_t *plmn, int format)
|
|
{
|
|
LOG_FUNC_IN;
|
|
|
|
size_t size = 0;
|
|
|
|
/*
|
|
* Get the identifier of the registered PLMN in the list of available PLMNs
|
|
*/
|
|
int index = IdleMode_get_rplmn_index(emm_plmn_list);
|
|
|
|
if ( !(index < 0) ) {
|
|
const char *name = _emm_main_get_plmn(emm_plmn_list, &emm_data->nvdata.rplmn,
|
|
index, format, &size);
|
|
|
|
if (size > 0) {
|
|
LOG_FUNC_RETURN ((char *) memcpy(&plmn->id, name, size));
|
|
}
|
|
}
|
|
|
|
LOG_FUNC_RETURN (NULL);
|
|
}
|
|
|
|
/****************************************************************************
|
|
** **
|
|
** Name: emm_main_get_plmn_status() **
|
|
** **
|
|
** Description: Get the value of the network registration status which **
|
|
** shows whether the network has currently indicated the **
|
|
** registration of the UE **
|
|
** **
|
|
** Inputs: None **
|
|
** **
|
|
** Outputs: None **
|
|
** Return: The current network registration status **
|
|
** Others: None **
|
|
** **
|
|
***************************************************************************/
|
|
Stat_t emm_main_get_plmn_status(emm_data_t *emm_data)
|
|
{
|
|
LOG_FUNC_IN;
|
|
LOG_FUNC_RETURN (emm_data->stat);
|
|
}
|
|
|
|
/****************************************************************************
|
|
** **
|
|
** Name: emm_main_get_plmn_tac() **
|
|
** **
|
|
** Description: Get the code of the Tracking area the registered PLMN **
|
|
** belongs to **
|
|
** **
|
|
** Inputs: None **
|
|
** **
|
|
** Outputs: None **
|
|
** Return: The Location/Tracking area code **
|
|
** Others: None **
|
|
** **
|
|
***************************************************************************/
|
|
tac_t emm_main_get_plmn_tac(emm_data_t *emm_data)
|
|
{
|
|
LOG_FUNC_IN;
|
|
LOG_FUNC_RETURN (emm_data->tac);
|
|
}
|
|
|
|
/****************************************************************************
|
|
** **
|
|
** Name: emm_main_get_plmn_ci() **
|
|
** **
|
|
** Description: Get the identifier of the serving cell **
|
|
** **
|
|
** Inputs: None **
|
|
** **
|
|
** Outputs: None **
|
|
** Return: The serving cell identifier **
|
|
** Others: None **
|
|
** **
|
|
***************************************************************************/
|
|
ci_t emm_main_get_plmn_ci(emm_data_t *emm_data)
|
|
{
|
|
LOG_FUNC_IN;
|
|
LOG_FUNC_RETURN (emm_data->ci);
|
|
}
|
|
|
|
/****************************************************************************
|
|
** **
|
|
** Name: emm_main_get_plmn_rat() **
|
|
** **
|
|
** Description: Get the value of the Radio Access Technology of the ser- **
|
|
** ving cell **
|
|
** **
|
|
** Inputs: None **
|
|
** **
|
|
** Outputs: None **
|
|
** Return: The value of the Radio Access Technology **
|
|
** of the serving cell **
|
|
** Others: None **
|
|
** **
|
|
***************************************************************************/
|
|
AcT_t emm_main_get_plmn_rat(emm_data_t *emm_data)
|
|
{
|
|
LOG_FUNC_IN;
|
|
LOG_FUNC_RETURN (emm_data->rat);
|
|
}
|
|
|
|
/****************************************************************************
|
|
** **
|
|
** Name: emm_main_is_attached() **
|
|
** **
|
|
** Description: Indicates whether the UE is currently attached to the **
|
|
** network for EPS services or emergency service only **
|
|
** **
|
|
** Inputs: None **
|
|
** Others: user->emm_data-> **
|
|
** **
|
|
** Outputs: None **
|
|
** Return: TRUE if the UE is currently attached to **
|
|
** the network; FALSE otherwise. **
|
|
** Others: None **
|
|
** **
|
|
***************************************************************************/
|
|
int emm_main_is_attached(emm_data_t *emm_data)
|
|
{
|
|
LOG_FUNC_IN;
|
|
LOG_FUNC_RETURN (emm_data->is_attached);
|
|
}
|
|
|
|
/****************************************************************************
|
|
** **
|
|
** Name: emm_main_get_plmn_rat() **
|
|
** **
|
|
** Description: Indicates whether the UE is currently attached to the **
|
|
** network for emergency bearer services **
|
|
** **
|
|
** Inputs: None **
|
|
** Others: user->emm_data-> **
|
|
** **
|
|
** Outputs: None **
|
|
** Return: TRUE if the UE is currently attached or is **
|
|
** attempting to attach to the network for **
|
|
** emergency bearer services; FALSE otherwise **
|
|
** Others: None **
|
|
** **
|
|
***************************************************************************/
|
|
int emm_main_is_emergency(emm_data_t *emm_data)
|
|
{
|
|
LOG_FUNC_IN;
|
|
LOG_FUNC_RETURN (emm_data->is_attached && emm_data->is_emergency);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/********************* L O C A L F U N C T I O N S *********************/
|
|
/****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
** **
|
|
** Name: _emm_main_callback() **
|
|
** **
|
|
** Description: Forwards the network indication to the upper control la- **
|
|
** yer (user API) to notify that network registration and/or **
|
|
** location information has changed. **
|
|
** **
|
|
** Inputs: size: Size in byte of the list of operators **
|
|
** present in the network. The list has to be **
|
|
** displayed to the user application when **
|
|
** size > 0. **
|
|
** Others: user->emm_data-> **
|
|
** **
|
|
** Outputs: None **
|
|
** Return: RETURNok, RETURNerror **
|
|
** Others: None **
|
|
** **
|
|
***************************************************************************/
|
|
static int _emm_main_callback(user_api_id_t *user_api_id, emm_data_t *emm_data, int size)
|
|
{
|
|
LOG_FUNC_IN;
|
|
|
|
/* Forward the notification to the user API */
|
|
int rc = (*_emm_main_user_callback)(user_api_id, emm_data->stat, emm_data->tac,
|
|
emm_data->ci, emm_data->rat,
|
|
emm_data->plist.buffer, size);
|
|
|
|
LOG_FUNC_RETURN (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
** **
|
|
** Name: _emm_main_get_imei() **
|
|
** **
|
|
** Description: Returns the International Mobile Equipment Identity con- **
|
|
** tained in the given string representation **
|
|
** **
|
|
** Inputs: imei: The string representation of the IMEI **
|
|
** Others: None **
|
|
** **
|
|
** Outputs: imei: The IMEI of the UE **
|
|
** Return: The number of digits in the IMEI **
|
|
** Others: None **
|
|
** **
|
|
***************************************************************************/
|
|
static int _emm_main_get_imei(imei_t *imei, const char *imei_str)
|
|
{
|
|
int len = strlen(imei_str);
|
|
|
|
if (len % 2) {
|
|
imei->u.num.parity = IMEI_ODD_PARITY;
|
|
} else {
|
|
imei->u.num.parity = EVEN_PARITY;
|
|
}
|
|
|
|
imei->u.num.digit1 = imei_str[0] - '0';
|
|
imei->u.num.digit2 = imei_str[1] - '0';
|
|
imei->u.num.digit3 = imei_str[2] - '0';
|
|
imei->u.num.digit4 = imei_str[3] - '0';
|
|
imei->u.num.digit5 = imei_str[4] - '0';
|
|
imei->u.num.digit6 = imei_str[5] - '0';
|
|
imei->u.num.digit7 = imei_str[6] - '0';
|
|
imei->u.num.digit8 = imei_str[7] - '0';
|
|
imei->u.num.digit9 = imei_str[8] - '0';
|
|
imei->u.num.digit10 = imei_str[9] - '0';
|
|
imei->u.num.digit11 = imei_str[10] - '0';
|
|
imei->u.num.digit12 = imei_str[11] - '0';
|
|
imei->u.num.digit13 = imei_str[12] - '0';
|
|
imei->u.num.digit14 = imei_str[13] - '0';
|
|
imei->u.num.digit15 = imei_str[14] - '0';
|
|
return (len);
|
|
}
|
|
|
|
/****************************************************************************
|
|
** **
|
|
** Name: _emm_main_imsi_cmp() **
|
|
** **
|
|
** Description: Compares two International Mobile Subscriber Identifiers **
|
|
** **
|
|
** Inputs: imsi1: The first IMSI **
|
|
** imsi2: The second IMSI to compare to **
|
|
** Others: None **
|
|
** **
|
|
** Outputs: None **
|
|
** Return: TRUE if the first IMSI is found to match **
|
|
** the second; FALSE otherwise. **
|
|
** Others: None **
|
|
** **
|
|
***************************************************************************/
|
|
static int _emm_main_imsi_cmp(imsi_t *imsi1, imsi_t *imsi2)
|
|
{
|
|
int i;
|
|
|
|
if (imsi1->length != imsi2->length) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < imsi1->length; i++) {
|
|
if (imsi1->u.value[i] != imsi2->u.value[i]) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** **
|
|
** Name: _emm_main_get_plmn() **
|
|
** **
|
|
** Description: Get the identifier of the PLMN at the given index in the **
|
|
** list of available PLMNs. **
|
|
** **
|
|
** Inputs: plmn: The PLMN to search for **
|
|
** index: The index of the PLMN in the list of PLMNs **
|
|
** format: The requested representation format of the **
|
|
** PLMN identifier **
|
|
** Others: None **
|
|
** **
|
|
** Outputs: size: The size in bytes of the PLMN identifier **
|
|
** coded in the requested format **
|
|
** Return: A pointer to the identifier of the PLMN **
|
|
** Others: None **
|
|
** **
|
|
***************************************************************************/
|
|
static const char *_emm_main_get_plmn(emm_plmn_list_t *emm_plmn_list, const plmn_t *plmn, int index,
|
|
int format, size_t *size)
|
|
{
|
|
if ( PLMN_IS_VALID(*plmn) ) {
|
|
switch (format) {
|
|
case NET_FORMAT_LONG:
|
|
/* Get the long alpha-numeric representation of the PLMN */
|
|
return IdleMode_get_plmn_fullname(emm_plmn_list, plmn, index, size);
|
|
break;
|
|
|
|
case NET_FORMAT_SHORT:
|
|
/* Get the short alpha-numeric representation of the PLMN */
|
|
return IdleMode_get_plmn_shortname(emm_plmn_list, plmn, index, size);
|
|
break;
|
|
|
|
case NET_FORMAT_NUM:
|
|
/* Get the numeric representation of the PLMN */
|
|
return IdleMode_get_plmn_id(emm_plmn_list, plmn, index, size);
|
|
break;
|
|
|
|
default:
|
|
LOG_TRACE(WARNING, "EMM-MAIN - Format is not valid (%d)",
|
|
format);
|
|
*size = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
/****************************************************************************
|
|
** **
|
|
** Name: _emm_main_get_plmn_index() **
|
|
** **
|
|
** Description: Get the index of the given PLMN in the ordered list of **
|
|
** available PLMNs **
|
|
** **
|
|
** Inputs: plmn: Identifier of the PLMN **
|
|
** format: The representation format of the PLMN **
|
|
** identifier **
|
|
** Others: None **
|
|
** **
|
|
** Outputs: None **
|
|
** Return: The index of the selected PLMN in the list **
|
|
** of available PLMNs; -1 if the PLMN is not **
|
|
** found **
|
|
** Others: None **
|
|
** **
|
|
***************************************************************************/
|
|
static int _emm_main_get_plmn_index(emm_plmn_list_t *emm_plmn_list, const char *plmn, int format)
|
|
{
|
|
int index = -1;
|
|
|
|
switch (format) {
|
|
case NET_FORMAT_LONG:
|
|
/* Get the index of the long alpha-numeric PLMN identifier */
|
|
index = IdleMode_get_plmn_fullname_index(emm_plmn_list, plmn);
|
|
break;
|
|
|
|
case NET_FORMAT_SHORT:
|
|
/* Get the index of the short alpha-numeric PLMN identifier */
|
|
index = IdleMode_get_plmn_shortname_index(emm_plmn_list, plmn);
|
|
break;
|
|
|
|
case NET_FORMAT_NUM:
|
|
/* Get the index of the numeric PLMN identifier */
|
|
index = IdleMode_get_plmn_id_index(emm_plmn_list, plmn);
|
|
break;
|
|
|
|
default:
|
|
LOG_TRACE(WARNING, "EMM-MAIN - Format is not valid (%d)", format);
|
|
break;
|
|
}
|
|
|
|
return (index);
|
|
}
|
|
|