OAI openair3-NAS-UE-EMM代码解读
1.下述函数作用:初始化EMM函数计时器
_emm_timers_initialize(emm_timers_t *emm_timers)
2.emm会话建立过程初始化,将attempt_count归零
void _emm_attach_initialize(emm_attach_data_t *emm_attach_data) {
emm_attach_data->attempt_count = 0;
}
3.emm会话断开连接初始化过程
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;
}
4.初始化EMM内部数据,内部数据定义包括且不局限于USIM,IMEI,EPS,PLMN等
输入:用户回调数据;IMEI信息读取自终端非易失性存储器;
输出:无;
return:无;
Others: ? ?user->emm_data->
void emm_main_initialize(nas_user_t *user, emm_indication_callback_t cb, const char *imei)
下述代码通过IMSI(国际移动用户识别码)获取归属PLMN(公共陆地移动网)
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;
下述代码获取运营商控制的PLMN选择器的列表,总体来说,这段代码是用于数据处理的,它从用户USIM数据中提取并复制有效的运营商PLMN选择器到EMM数据结构中,确保这些数据在EMM上下文中可用。
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;
}
}
下述代码获取EPS位置信息
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;
?下述代码是从用户的数据中提取NAS(Non-Access Stratum)配置参数,并将其复制到EMM(EPS移动管理)数据结构中。
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];
下述代码创建了NAS安全上下文并将其分配给了用户,在创建并初始化安全上下文后,检查内存是否分配成功,并处书画安全上下文情况内容
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;
}
下述代码为ASME(安全管理实体)安全密钥生成
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);
}
下述代码为上下行链路计数,对于每个参数,它首先检查长度是否合适,然后使用memcpy
函数将值从一个结构复制到另一个结构中。
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);
}
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);
}
下述代码为密码算法实现
user->emm_data->security->capability.eps_encryption =
((user->usim_data.securityctx.algorithmID.value[0] >> 4) & 0xf);
下述代码为身份保护算法实现
user->emm_data->security->capability.eps_integrity =
(user->usim_data.securityctx.algorithmID.value[0] & 0xf);
5.emm_main_cleanup()?执行EPS移动性管理清理程序,无输入,无输出,无return
emm_data->usim_is_valid //更新USIM应用程序数据
/*
*将EMM数据存储到UE的非易失性存储器中
*注册PLMN
*等效PLMN列表
*/
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);
}
6.emm_main_get_imsi()获取国际移动用户身份识别号码,无输入,return IMSI指针;
这个函数主要用于从 EMM 数据中提取 IMSI 信息,并返回该信息的地址。这通常用于处理与移动通信相关的操作,例如身份验证、位置更新等。
const imsi_t *emm_main_get_imsi(emm_data_t *emm_data)
{
LOG_FUNC_IN;
LOG_FUNC_RETURN (&emm_data->nvdata.imsi);
}
7.?emm_main_get_msisdn() ,从USIM中获取移动用户拨号号码,无输入,return指向用户拨号号码的指针;
const msisdn_t *emm_main_get_msisdn(nas_user_t *user)
{
LOG_FUNC_IN;
LOG_FUNC_RETURN (&user->usim_data.msisdn.number);
}
8.emm_main_set_plmn_selection_mode() ,将网络选择操作模式设置为给定模式,并更新手动选择的网络选择数据;
输入:指定的网络选择操作模式
格式:PLMN标识符的表示格式
plmn:所选plmn的标识符
rat:选定的无线电接入技术
输出:无
return:RETURNok, RETURNerror?
//这段代码定义了一个函数 emm_main_set_plmn_selection_mode,该函数用于设置用户设备的 PLMN(公共陆地移动网络)选择模式。
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) {
/*获取可用PLMN列表中PLMN的索引*/
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 {
/*更新手动选择的网络选择数据*/
emm_data->plmn_index = index;
emm_data->plmn_rat = rat;
}
} else {
/*
*获取UE已经自动尝试的最后一个PLMN的索引
*打开时注册到;等效PLMN列表不应
*应用于自动网络选择模式下的用户重选。
*/
index = IdleMode_get_hplmn_index(emm_plmn_list);
}
LOG_FUNC_RETURN (index);
}
函数的主要逻辑如下:
- 记录函数开始执行的日志信息(
LOG_FUNC_IN
)。- 获取?
user
?结构中的?emm_data
?和?emm_plmn_list
?成员的指针。- 记录 PLMN 选择模式的日志信息(
LOG_TRACE
)。- 将 PLMN 选择模式设置为函数参数中的?
mode
。- 如果 PLMN 选择模式不是自动模式,则执行以下操作:
- 获取 PLMN 在可用 PLMN 列表中的索引。
- 如果索引小于 0,表示 PLMN 不可用,记录警告日志信息。
- 否则,更新手动选择的网络选择数据,将索引和RAT设置到 EMM数据中。
- 如果 PLMN 选择模式是自动模式,则执行以下操作:
- 获取用户设备在开启时尝试自动注册的最后一个 PLMN 的索引。
- 记录函数返回的索引值(
LOG_FUNC_RETURN
)。
9.?emm_main_get_plmn_selection_mode()?获取网络选择操作模式的当前值,return:网络选择模式的值;
int emm_main_get_plmn_selection_mode(emm_data_t *emm_data)
{
LOG_FUNC_IN;
LOG_FUNC_RETURN (emm_data->plmn_mode);
}
10.emm_main_get_plmn_list()?获取可用PLMN的列表,输出:plist:指向可用PLMN列表的指针;
return:列表的大小(以字节为单位)
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);
}
11.emm_main_get_selected_plmn()?获取当前所选PLMN的标识:
输入:格式:PLMN标识符的字符串表示的请求格式;
输出:plmn:以请求格式编码的选定plmn标识符;
Return:指向所选PLMN的字符串表示形式的指针;
//总体而言,这个函数的目的是获取已选择的 PLMN 的标识符,并将其复制到提供的 plmn 结构中。如果未选择任何 PLMN 或标识符无效,则返回 NULL。
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;
/*
* 获取可用PLMN列表中所选PLMN的标识符
*/
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);
}
函数的参数包括:
emm_plmn_list_t *emm_plmn_list
:指向 PLMN 列表结构的指针。emm_data_t *emm_data
:指向 EMM 数据的指针。network_plmn_t *plmn
:指向包含 PLMN 信息的结构的指针,用于返回 PLMN 的标识符。int format
:PLMN ID 的格式。
文件路径openair3/NAS/UE/EMM/emm_main.c
目前更新到714行,未完待续...?