@ -25,6 +25,11 @@
*/
*/
# include <unistd.h>
# include <iostream>
# include <iomanip>
# include <fstream>
# include <sstream>
# include "srslte/asn1/liblte_rrc.h"
# include "srslte/asn1/liblte_rrc.h"
# include "upper/nas.h"
# include "upper/nas.h"
# include "srslte/common/bcd_helpers.h"
# include "srslte/common/bcd_helpers.h"
@ -33,9 +38,17 @@ using namespace srslte;
namespace srsue {
namespace srsue {
/*********************************************************************
* NAS
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
nas : : nas ( )
nas : : nas ( )
: state ( EMM_STATE_DEREGISTERED ) , plmn_selection ( PLMN_SELECTED ) , is_guti_set ( false ) , ip_addr ( 0 ) , eps_bearer_id ( 0 ) ,
: state ( EMM_STATE_DEREGISTERED ) , plmn_selection ( PLMN_SELECTED ) , have_guti ( false ) , have_ctxt ( false ) , ip_addr ( 0 ) , eps_bearer_id ( 0 )
count_ul ( 0 ) , count_dl ( 0 ) { }
{
ctxt . rx_count = 0 ;
ctxt . tx_count = 0 ;
}
void nas : : init ( usim_interface_nas * usim_ ,
void nas : : init ( usim_interface_nas * usim_ ,
rrc_interface_nas * rrc_ ,
rrc_interface_nas * rrc_ ,
@ -51,23 +64,34 @@ void nas::init(usim_interface_nas *usim_,
state = EMM_STATE_DEREGISTERED ;
state = EMM_STATE_DEREGISTERED ;
plmn_selection = PLMN_NOT_SELECTED ;
plmn_selection = PLMN_NOT_SELECTED ;
if ( usim - > get_home_plmn_id ( & home_plmn ) ) {
if ( ! usim - > get_home_plmn_id ( & home_plmn ) ) {
nas_log - > error ( " Getting Home PLMN Id from USIM. Defaulting to 001-01 \n " ) ;
nas_log - > error ( " Getting Home PLMN Id from USIM. Defaulting to 001-01 \n " ) ;
home_plmn . mcc = 61441 ; // This is 001
home_plmn . mcc = 61441 ; // This is 001
home_plmn . mnc = 65281 ; // This is 01
home_plmn . mnc = 65281 ; // This is 01
}
}
cfg = cfg_ ;
cfg = cfg_ ;
if ( ( read_ctxt_file ( & ctxt ) ) ) {
usim - > generate_nas_keys ( ctxt . k_asme , k_nas_enc , k_nas_int ,
ctxt . cipher_algo , ctxt . integ_algo ) ;
nas_log - > debug_hex ( k_nas_enc , 32 , " NAS encryption key - k_nas_enc " ) ;
nas_log - > debug_hex ( k_nas_int , 32 , " NAS integrity key - k_nas_int " ) ;
have_guti = true ;
have_ctxt = true ;
}
}
}
void nas : : stop ( ) { }
void nas : : stop ( ) {
write_ctxt_file ( ctxt ) ;
}
emm_state_t nas : : get_state ( ) {
emm_state_t nas : : get_state ( ) {
return state ;
return state ;
}
}
/*******************************************************************************
/*******************************************************************************
UE interface
* UE interface
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void nas : : attach_request ( ) {
void nas : : attach_request ( ) {
nas_log - > info ( " Attach Request \n " ) ;
nas_log - > info ( " Attach Request \n " ) ;
@ -96,8 +120,8 @@ void nas::deattach_request() {
}
}
/*******************************************************************************
/*******************************************************************************
RRC interface
* RRC interface
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void nas : : plmn_found ( LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id , uint16_t tracking_area_code ) {
void nas : : plmn_found ( LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id , uint16_t tracking_area_code ) {
@ -207,24 +231,39 @@ void nas::write_pdu(uint32_t lcid, byte_buffer_t *pdu) {
}
}
uint32_t nas : : get_ul_count ( ) {
uint32_t nas : : get_ul_count ( ) {
return c ount_ul ;
return c txt. tx_c ount;
}
}
bool nas : : get_s_tmsi ( LIBLTE_RRC_S_TMSI_STRUCT * s_tmsi ) {
bool nas : : get_s_tmsi ( LIBLTE_RRC_S_TMSI_STRUCT * s_tmsi ) {
if ( is_guti_set ) {
if ( have_guti ) {
s_tmsi - > mmec = guti. mme_code ;
s_tmsi - > mmec = ctxt. guti. mme_code ;
s_tmsi - > m_tmsi = guti. m_tmsi ;
s_tmsi - > m_tmsi = ctxt. guti. m_tmsi ;
return true ;
return true ;
} else {
} else {
return false ;
return false ;
}
}
}
}
bool nas : : get_k_asme ( uint8_t * k_asme_ , uint32_t n ) {
if ( ! have_ctxt ) {
nas_log - > error ( " K_asme requested before security context established \n " ) ;
return false ;
}
if ( NULL = = k_asme_ | | n < 32 ) {
nas_log - > error ( " Invalid parameters to get_k_asme " ) ;
return false ;
}
memcpy ( k_asme_ , ctxt . k_asme , 32 ) ;
return true ;
}
/*******************************************************************************
/*******************************************************************************
Security
* Security
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void nas : : integrity_generate ( uint8_t * key_128 ,
void nas : : integrity_generate ( uint8_t integ_algo ,
uint8_t * key_128 ,
uint32_t count ,
uint32_t count ,
uint8_t rb_id ,
uint8_t rb_id ,
uint8_t direction ,
uint8_t direction ,
@ -269,10 +308,20 @@ void nas::cipher_decrypt() {
}
}
bool nas : : check_cap_replay ( LIBLTE_MME_UE_SECURITY_CAPABILITIES_STRUCT * caps )
{
for ( uint32_t i = 0 ; i < 8 ; i + + ) {
if ( caps - > eea [ i ] ! = eea_caps [ i ] | | caps - > eia [ i ] ! = eia_caps [ i ] ) {
return false ;
}
}
return true ;
}
/*******************************************************************************
/*******************************************************************************
Parsers
* Parsers
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void nas : : parse_attach_accept ( uint32_t lcid , byte_buffer_t * pdu ) {
void nas : : parse_attach_accept ( uint32_t lcid , byte_buffer_t * pdu ) {
LIBLTE_MME_ATTACH_ACCEPT_MSG_STRUCT attach_accept ;
LIBLTE_MME_ATTACH_ACCEPT_MSG_STRUCT attach_accept ;
@ -288,21 +337,19 @@ void nas::parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu) {
//FIXME: Handle t3412.unit
//FIXME: Handle t3412.unit
//FIXME: Handle tai_list
//FIXME: Handle tai_list
if ( attach_accept . guti_present ) {
if ( attach_accept . guti_present ) {
memcpy ( & guti , & attach_accept . guti . guti , sizeof ( LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT ) ) ;
memcpy ( & ctxt . guti , & attach_accept . guti . guti , sizeof ( LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT ) ) ;
is_guti_set = true ;
have_guti = true ;
// TODO: log message to console
}
if ( attach_accept . lai_present ) {
}
}
if ( attach_accept . lai_present ) { }
if ( attach_accept . ms_id_present ) { }
if ( attach_accept . ms_id_present ) { }
if ( attach_accept . emm_cause_present ) { }
if ( attach_accept . emm_cause_present ) { }
if ( attach_accept . t3402_present ) { }
if ( attach_accept . t3402_present ) { }
if ( attach_accept . t3412_ext_present ) { }
if ( attach_accept . t3423_present ) { }
if ( attach_accept . t3423_present ) { }
if ( attach_accept . equivalent_plmns_present ) { }
if ( attach_accept . equivalent_plmns_present ) { }
if ( attach_accept . emerg_num_list_present ) { }
if ( attach_accept . emerg_num_list_present ) { }
if ( attach_accept . eps_network_feature_support_present ) { }
if ( attach_accept . eps_network_feature_support_present ) { }
if ( attach_accept . additional_update_result_present ) { }
if ( attach_accept . additional_update_result_present ) { }
if ( attach_accept . t3412_ext_present ) { }
liblte_mme_unpack_activate_default_eps_bearer_context_request_msg ( & attach_accept . esm_msg ,
liblte_mme_unpack_activate_default_eps_bearer_context_request_msg ( & attach_accept . esm_msg ,
& act_def_eps_bearer_context_req ) ;
& act_def_eps_bearer_context_req ) ;
@ -313,13 +360,14 @@ void nas::parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu) {
ip_addr | = act_def_eps_bearer_context_req . pdn_addr . addr [ 2 ] < < 8 ;
ip_addr | = act_def_eps_bearer_context_req . pdn_addr . addr [ 2 ] < < 8 ;
ip_addr | = act_def_eps_bearer_context_req . pdn_addr . addr [ 3 ] ;
ip_addr | = act_def_eps_bearer_context_req . pdn_addr . addr [ 3 ] ;
nas_log - > info ( " IP allocated by network %u.%u.%u.%u \n " ,
nas_log - > info ( " Network attach successful. APN: %s, IP: %u.%u.%u.%u \n " ,
act_def_eps_bearer_context_req . apn . apn . c_str ( ) ,
act_def_eps_bearer_context_req . pdn_addr . addr [ 0 ] ,
act_def_eps_bearer_context_req . pdn_addr . addr [ 0 ] ,
act_def_eps_bearer_context_req . pdn_addr . addr [ 1 ] ,
act_def_eps_bearer_context_req . pdn_addr . addr [ 1 ] ,
act_def_eps_bearer_context_req . pdn_addr . addr [ 2 ] ,
act_def_eps_bearer_context_req . pdn_addr . addr [ 2 ] ,
act_def_eps_bearer_context_req . pdn_addr . addr [ 3 ] ) ;
act_def_eps_bearer_context_req . pdn_addr . addr [ 3 ] ) ;
nas_log - > console ( " Network attach successful. IP: %u.%u.%u.%u\n " ,
nas_log - > console ( " Network attach successful. IP: %u.%u.%u.%u\n " ,
act_def_eps_bearer_context_req . pdn_addr . addr [ 0 ] ,
act_def_eps_bearer_context_req . pdn_addr . addr [ 0 ] ,
act_def_eps_bearer_context_req . pdn_addr . addr [ 1 ] ,
act_def_eps_bearer_context_req . pdn_addr . addr [ 1 ] ,
act_def_eps_bearer_context_req . pdn_addr . addr [ 2 ] ,
act_def_eps_bearer_context_req . pdn_addr . addr [ 2 ] ,
@ -358,10 +406,9 @@ void nas::parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu) {
state = EMM_STATE_REGISTERED ;
state = EMM_STATE_REGISTERED ;
current_plmn = selecting_plmn ;
current_plmn = selecting_plmn ;
c ount_dl + + ;
c txt. rx_c ount+ + ;
// Send EPS bearer context accept and attach complete
// Send EPS bearer context accept and attach complete
count_ul + + ;
act_def_eps_bearer_context_accept . eps_bearer_id = eps_bearer_id ;
act_def_eps_bearer_context_accept . eps_bearer_id = eps_bearer_id ;
act_def_eps_bearer_context_accept . proc_transaction_id = transaction_id ;
act_def_eps_bearer_context_accept . proc_transaction_id = transaction_id ;
act_def_eps_bearer_context_accept . protocol_cnfg_opts_present = false ;
act_def_eps_bearer_context_accept . protocol_cnfg_opts_present = false ;
@ -369,10 +416,11 @@ void nas::parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu) {
& attach_complete . esm_msg ) ;
& attach_complete . esm_msg ) ;
liblte_mme_pack_attach_complete_msg ( & attach_complete ,
liblte_mme_pack_attach_complete_msg ( & attach_complete ,
LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED ,
LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED ,
c ount_ul ,
c txt. tx_c ount,
( LIBLTE_BYTE_MSG_STRUCT * ) pdu ) ;
( LIBLTE_BYTE_MSG_STRUCT * ) pdu ) ;
integrity_generate ( & k_nas_int [ 16 ] ,
integrity_generate ( ctxt . integ_algo ,
count_ul ,
& k_nas_int [ 16 ] ,
ctxt . tx_count ,
lcid - 1 ,
lcid - 1 ,
SECURITY_DIRECTION_UPLINK ,
SECURITY_DIRECTION_UPLINK ,
& pdu - > msg [ 5 ] ,
& pdu - > msg [ 5 ] ,
@ -384,6 +432,7 @@ void nas::parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu) {
nas_log - > info ( " Sending Attach Complete \n " ) ;
nas_log - > info ( " Sending Attach Complete \n " ) ;
rrc - > write_sdu ( lcid , pdu ) ;
rrc - > write_sdu ( lcid , pdu ) ;
ctxt . tx_count + + ;
} else {
} else {
nas_log - > info ( " Not handling attach type %u \n " , attach_accept . eps_attach_result ) ;
nas_log - > info ( " Not handling attach type %u \n " , attach_accept . eps_attach_result ) ;
@ -407,7 +456,7 @@ void nas::parse_authentication_request(uint32_t lcid, byte_buffer_t *pdu) {
LIBLTE_MME_AUTHENTICATION_REQUEST_MSG_STRUCT auth_req ;
LIBLTE_MME_AUTHENTICATION_REQUEST_MSG_STRUCT auth_req ;
LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT auth_res ;
LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT auth_res ;
nas_log - > info ( " Received Authentication Request \n " ) ; ;
nas_log - > info ( " Received Authentication Request \n " ) ;
liblte_mme_unpack_authentication_request_msg ( ( LIBLTE_BYTE_MSG_STRUCT * ) pdu , & auth_req ) ;
liblte_mme_unpack_authentication_request_msg ( ( LIBLTE_BYTE_MSG_STRUCT * ) pdu , & auth_req ) ;
// Reuse the pdu for the response message
// Reuse the pdu for the response message
@ -422,7 +471,15 @@ void nas::parse_authentication_request(uint32_t lcid, byte_buffer_t *pdu) {
bool net_valid ;
bool net_valid ;
uint8_t res [ 16 ] ;
uint8_t res [ 16 ] ;
usim - > generate_authentication_response ( auth_req . rand , auth_req . autn , mcc , mnc , & net_valid , res ) ;
usim - > generate_authentication_response ( auth_req . rand , auth_req . autn , mcc , mnc ,
& net_valid , res , ctxt . k_asme ) ;
nas_log - > info ( " Generated k_asme=%s \n " , hex_to_string ( ctxt . k_asme , 32 ) . c_str ( ) ) ;
if ( LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE = = auth_req . nas_ksi . tsc_flag ) {
ctxt . ksi = auth_req . nas_ksi . nas_ksi ;
} else {
nas_log - > error ( " NAS mapped security context not currently supported \n " ) ;
nas_log - > console ( " Warning: NAS mapped security context not currently supported \n " ) ;
}
if ( net_valid ) {
if ( net_valid ) {
nas_log - > info ( " Network authentication successful \n " ) ;
nas_log - > info ( " Network authentication successful \n " ) ;
@ -438,9 +495,6 @@ void nas::parse_authentication_request(uint32_t lcid, byte_buffer_t *pdu) {
nas_log - > console ( " Warning: Network authentication failure \n " ) ;
nas_log - > console ( " Warning: Network authentication failure \n " ) ;
pool - > deallocate ( pdu ) ;
pool - > deallocate ( pdu ) ;
}
}
// Reset DL counter (as per 24.301 5.4.3.2)
count_dl = 0 ;
}
}
void nas : : parse_authentication_reject ( uint32_t lcid , byte_buffer_t * pdu ) {
void nas : : parse_authentication_reject ( uint32_t lcid , byte_buffer_t * pdu ) {
@ -451,108 +505,156 @@ void nas::parse_authentication_reject(uint32_t lcid, byte_buffer_t *pdu) {
}
}
void nas : : parse_identity_request ( uint32_t lcid , byte_buffer_t * pdu ) {
void nas : : parse_identity_request ( uint32_t lcid , byte_buffer_t * pdu ) {
nas_log - > error ( " TODO:parse_identity_request \n " ) ;
LIBLTE_MME_ID_REQUEST_MSG_STRUCT id_req ;
LIBLTE_MME_ID_RESPONSE_MSG_STRUCT id_resp ;
liblte_mme_unpack_identity_request_msg ( ( LIBLTE_BYTE_MSG_STRUCT * ) pdu , & id_req ) ;
nas_log - > info ( " Received Identity Request. ID type: %d \n " , id_req . id_type ) ;
switch ( id_req . id_type ) {
case LIBLTE_MME_MOBILE_ID_TYPE_IMSI :
id_resp . mobile_id . type_of_id = LIBLTE_MME_MOBILE_ID_TYPE_IMSI ;
usim - > get_imsi_vec ( id_resp . mobile_id . imsi , 15 ) ;
break ;
case LIBLTE_MME_MOBILE_ID_TYPE_IMEI :
id_resp . mobile_id . type_of_id = LIBLTE_MME_MOBILE_ID_TYPE_IMEI ;
usim - > get_imei_vec ( id_resp . mobile_id . imei , 15 ) ;
break ;
default :
nas_log - > error ( " Unhandled ID type: %d \n " ) ;
pool - > deallocate ( pdu ) ;
return ;
}
pdu - > reset ( ) ;
liblte_mme_pack_identity_response_msg ( & id_resp , ( LIBLTE_BYTE_MSG_STRUCT * ) pdu ) ;
rrc - > write_sdu ( lcid , pdu ) ;
}
}
void nas : : parse_security_mode_command ( uint32_t lcid , byte_buffer_t * pdu ) {
void nas : : parse_security_mode_command ( uint32_t lcid , byte_buffer_t * pdu )
bool success ;
{
LIBLTE_MME_SECURITY_MODE_COMMAND_MSG_STRUCT sec_mode_cmd ;
LIBLTE_MME_SECURITY_MODE_COMMAND_MSG_STRUCT sec_mode_cmd ;
LIBLTE_MME_SECURITY_MODE_COMPLETE_MSG_STRUCT sec_mode_comp ;
LIBLTE_MME_SECURITY_MODE_COMPLETE_MSG_STRUCT sec_mode_comp ;
LIBLTE_MME_SECURITY_MODE_REJECT_MSG_STRUCT sec_mode_rej ;
nas_log - > info ( " Received Security Mode Command \n " ) ;
liblte_mme_unpack_security_mode_command_msg ( ( LIBLTE_BYTE_MSG_STRUCT * ) pdu , & sec_mode_cmd ) ;
liblte_mme_unpack_security_mode_command_msg ( ( LIBLTE_BYTE_MSG_STRUCT * ) pdu , & sec_mode_cmd ) ;
nas_log - > info ( " Received Security Mode Command ksi: %d, eea: %s, eia: %s \n " ,
sec_mode_cmd . nas_ksi . nas_ksi ,
ciphering_algorithm_id_text [ sec_mode_cmd . selected_nas_sec_algs . type_of_eea ] ,
integrity_algorithm_id_text [ sec_mode_cmd . selected_nas_sec_algs . type_of_eia ] ) ;
if ( sec_mode_cmd . nas_ksi . tsc_flag ! = LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE ) {
nas_log - > error ( " Mapped security context not supported \n " ) ;
pool - > deallocate ( pdu ) ;
return ;
}
ksi = sec_mode_cmd . nas_ksi . nas_ksi ;
if ( have_ctxt ) {
cipher_algo = ( CIPHERING_ALGORITHM_ID_ENUM ) sec_mode_cmd . selected_nas_sec_algs . type_of_eea ;
if ( sec_mode_cmd . nas_ksi . nas_ksi ! = ctxt . ksi ) {
integ_algo = ( INTEGRITY_ALGORITHM_ID_ENUM ) sec_mode_cmd . selected_nas_sec_algs . type_of_eia ;
nas_log - > warning ( " Sending Security Mode Reject due to key set ID mismatch \n " ) ;
// FIXME: Handle nonce_ue, nonce_mme
send_security_mode_reject ( LIBLTE_MME_EMM_CAUSE_SECURITY_MODE_REJECTED_UNSPECIFIED ) ;
// FIXME: Currently only handling ciphering EEA0 (null) and integrity EIA1,EIA2
pool - > deallocate ( pdu ) ;
// FIXME: Use selected_nas_sec_algs to choose correct algos
return ;
}
}
nas_log - > debug ( " Security details: ksi=%d, eea=%s, eia=%s \n " ,
// MME is setting up security context
ksi , ciphering_algorithm_id_text [ cipher_algo ] , integrity_algorithm_id_text [ integ_algo ] ) ;
// TODO: check nonce (not sent by Amari)
if ( CIPHERING_ALGORITHM_ID_EEA0 ! = cipher_algo | |
// Check capabilities replay
( INTEGRITY_ALGORITHM_ID_128_EIA2 ! = integ_algo & &
if ( ! check_cap_replay ( & sec_mode_cmd . ue_security_cap ) ) {
INTEGRITY_ALGORITHM_ID_128_EIA1 ! = integ_algo ) | |
sec_mode_cmd . nas_ksi . tsc_flag ! = LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE ) {
sec_mode_rej . emm_cause = LIBLTE_MME_EMM_CAUSE_UE_SECURITY_CAPABILITIES_MISMATCH ;
nas_log - > warning ( " Sending Security Mode Reject due to security capabilities mismatch \n " ) ;
nas_log - > warning ( " Sending Security Mode Reject due to security capabilities mismatch \n " ) ;
nas_log - > console ( " Unsupported Security Mode Command settings: use ciphering algorithm EEA0 and integrity check EIA1 or EIA2. \n " ) ;
send_security_mode_reject ( LIBLTE_MME_EMM_CAUSE_UE_SECURITY_CAPABILITIES_MISMATCH ) ;
success = false ;
pool - > deallocate ( pdu ) ;
} else {
return ;
// Generate NAS encryption key and integrity protection key
}
usim - > generate_nas_keys ( k_nas_enc , k_nas_int , cipher_algo , integ_algo ) ;
nas_log - > debug_hex ( k_nas_enc , 32 , " NAS encryption key - k_nas_enc " ) ;
nas_log - > debug_hex ( k_nas_int , 32 , " NAS integrity key - k_nas_int " ) ;
// Check incoming MAC
uint8_t * inMAC = & pdu - > msg [ 1 ] ;
uint8_t genMAC [ 4 ] ;
integrity_generate ( & k_nas_int [ 16 ] ,
count_dl ,
lcid - 1 ,
SECURITY_DIRECTION_DOWNLINK ,
& pdu - > msg [ 5 ] ,
pdu - > N_bytes - 5 ,
genMAC ) ;
nas_log - > info_hex ( inMAC , 4 , " Incoming PDU MAC: " ) ;
// Reset counterd (as per 24.301 5.4.3.2)
nas_log - > info_hex ( genMAC , 4 , " Generated PDU MAC: " ) ;
ctxt . rx_count = 0 ;
ctxt . tx_count = 0 ;
bool match = true ;
ctxt . cipher_algo = ( CIPHERING_ALGORITHM_ID_ENUM ) sec_mode_cmd . selected_nas_sec_algs . type_of_eea ;
for ( int i = 0 ; i < 4 ; i + + ) {
ctxt . integ_algo = ( INTEGRITY_ALGORITHM_ID_ENUM ) sec_mode_cmd . selected_nas_sec_algs . type_of_eia ;
if ( inMAC [ i ] ! = genMAC [ i ] ) {
match = false ;
}
}
if ( ! match ) {
sec_mode_rej . emm_cause = LIBLTE_MME_EMM_CAUSE_SECURITY_MODE_REJECTED_UNSPECIFIED ;
nas_log - > warning ( " Sending Security Mode Reject due to integrity check failure \n " ) ;
success = false ;
} else {
if ( sec_mode_cmd . imeisv_req_present & & LIBLTE_MME_IMEISV_REQUESTED = = sec_mode_cmd . imeisv_req ) {
// Check capabilities
sec_mode_comp . imeisv_present = true ;
if ( ! eea_caps [ ctxt . cipher_algo ] | | ! eia_caps [ ctxt . integ_algo ] ) {
sec_mode_comp . imeisv . type_of_id = LIBLTE_MME_MOBILE_ID_TYPE_IMEISV ;
nas_log - > warning ( " Sending Security Mode Reject due to security capabilities mismatch \n " ) ;
usim - > get_imei_vec ( sec_mode_comp . imeisv . imeisv , 15 ) ;
send_security_mode_reject ( LIBLTE_MME_EMM_CAUSE_UE_SECURITY_CAPABILITIES_MISMATCH ) ;
sec_mode_comp . imeisv . imeisv [ 14 ] = 5 ;
pool - > deallocate ( pdu ) ;
sec_mode_comp . imeisv . imeisv [ 15 ] = 3 ;
return ;
} else {
}
sec_mode_comp . imeisv_present = false ;
}
// Reuse pdu for response
// Generate NAS keys
pdu - > reset ( ) ;
usim - > generate_nas_keys ( ctxt . k_asme , k_nas_enc , k_nas_int , ctxt . cipher_algo , ctxt . integ_algo ) ;
liblte_mme_pack_security_mode_complete_msg ( & sec_mode_comp ,
nas_log - > debug_hex ( k_nas_enc , 32 , " NAS encryption key - k_nas_enc " ) ;
LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED ,
nas_log - > debug_hex ( k_nas_int , 32 , " NAS integrity key - k_nas_int " ) ;
count_ul ,
( LIBLTE_BYTE_MSG_STRUCT * ) pdu ) ;
nas_log - > debug ( " Generating integrity check. integ_algo:%d, count_dl:%d, lcid:%d \n " ,
integrity_generate ( & k_nas_int [ 16 ] ,
ctxt . integ_algo , ctxt . rx_count , lcid ) ;
count_ul ,
lcid - 1 ,
// Check incoming MAC
SECURITY_DIRECTION_UPLINK ,
uint8_t * inMAC = & pdu - > msg [ 1 ] ;
& pdu - > msg [ 5 ] ,
uint8_t genMAC [ 4 ] ;
pdu - > N_bytes - 5 ,
integrity_generate ( ctxt . integ_algo ,
& pdu - > msg [ 1 ] ) ;
& k_nas_int [ 16 ] ,
nas_log - > info ( " Sending Security Mode Complete nas_count_ul=%d, RB=%s \n " ,
ctxt . rx_count ,
count_ul ,
lcid - 1 ,
rrc - > get_rb_name ( lcid ) . c_str ( ) ) ;
SECURITY_DIRECTION_DOWNLINK ,
success = true ;
& pdu - > msg [ 5 ] ,
pdu - > N_bytes - 5 ,
genMAC ) ;
nas_log - > info_hex ( inMAC , 4 , " Incoming PDU MAC: " ) ;
nas_log - > info_hex ( genMAC , 4 , " Generated PDU MAC: " ) ;
ctxt . rx_count + + ;
bool match = true ;
for ( int i = 0 ; i < 4 ; i + + ) {
if ( inMAC [ i ] ! = genMAC [ i ] ) {
match = false ;
}
}
}
}
if ( ! match ) {
nas_log - > warning ( " Sending Security Mode Reject due to integrity check failure \n " ) ;
send_security_mode_reject ( LIBLTE_MME_EMM_CAUSE_SECURITY_MODE_REJECTED_UNSPECIFIED ) ;
pool - > deallocate ( pdu ) ;
return ;
}
count_dl + + ;
// Take security context into use
have_ctxt = true ;
if ( ! success ) {
if ( sec_mode_cmd . imeisv_req_present & & LIBLTE_MME_IMEISV_REQUESTED = = sec_mode_cmd . imeisv_req ) {
// Reuse pdu for response
sec_mode_comp . imeisv_present = true ;
pdu - > reset ( ) ;
sec_mode_comp . imeisv . type_of_id = LIBLTE_MME_MOBILE_ID_TYPE_IMEISV ;
liblte_mme_pack_security_mode_reject_msg ( & sec_mode_rej , ( LIBLTE_BYTE_MSG_STRUCT * ) pdu ) ;
usim - > get_imei_vec ( sec_mode_comp . imeisv . imeisv , 15 ) ;
sec_mode_comp . imeisv . imeisv [ 14 ] = 5 ;
sec_mode_comp . imeisv . imeisv [ 15 ] = 3 ;
} else {
sec_mode_comp . imeisv_present = false ;
}
}
rrc - > write_sdu ( lcid , pdu ) ;
// Send response
byte_buffer_t * sdu = pool_allocate ;
liblte_mme_pack_security_mode_complete_msg ( & sec_mode_comp ,
LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED ,
ctxt . tx_count ,
( LIBLTE_BYTE_MSG_STRUCT * ) sdu ) ;
integrity_generate ( ctxt . integ_algo ,
& k_nas_int [ 16 ] ,
ctxt . tx_count ,
lcid - 1 ,
SECURITY_DIRECTION_UPLINK ,
& sdu - > msg [ 5 ] ,
sdu - > N_bytes - 5 ,
& sdu - > msg [ 1 ] ) ;
nas_log - > info ( " Sending Security Mode Complete nas_current_ctxt.tx_count=%d, RB=%s \n " ,
ctxt . tx_count ,
rrc - > get_rb_name ( lcid ) . c_str ( ) ) ;
rrc - > write_sdu ( lcid , sdu ) ;
ctxt . tx_count + + ;
pool - > deallocate ( pdu ) ;
}
}
void nas : : parse_service_reject ( uint32_t lcid , byte_buffer_t * pdu ) {
void nas : : parse_service_reject ( uint32_t lcid , byte_buffer_t * pdu ) {
@ -561,15 +663,20 @@ void nas::parse_service_reject(uint32_t lcid, byte_buffer_t *pdu) {
void nas : : parse_esm_information_request ( uint32_t lcid , byte_buffer_t * pdu ) {
void nas : : parse_esm_information_request ( uint32_t lcid , byte_buffer_t * pdu ) {
nas_log - > error ( " TODO:parse_esm_information_request \n " ) ;
nas_log - > error ( " TODO:parse_esm_information_request \n " ) ;
}
}
void nas : : parse_emm_information ( uint32_t lcid , byte_buffer_t * pdu ) {
void nas : : parse_emm_information ( uint32_t lcid , byte_buffer_t * pdu ) {
nas_log - > error ( " TODO:parse_emm_information \n " ) ;
liblte_mme_unpack_emm_information_msg ( ( LIBLTE_BYTE_MSG_STRUCT * ) pdu , & emm_info ) ;
std : : string str = emm_info_str ( & emm_info ) ;
nas_log - > info ( " Received EMM Information: %s \n " , str . c_str ( ) ) ;
nas_log - > console ( " %s \n " , str . c_str ( ) ) ;
ctxt . rx_count + + ;
}
}
/*******************************************************************************
/*******************************************************************************
Senders
* Senders
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void nas : : send_attach_request ( ) {
void nas : : send_attach_request ( ) {
LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT attach_req ;
LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT attach_req ;
@ -579,30 +686,17 @@ void nas::send_attach_request() {
attach_req . eps_attach_type = LIBLTE_MME_EPS_ATTACH_TYPE_EPS_ATTACH ;
attach_req . eps_attach_type = LIBLTE_MME_EPS_ATTACH_TYPE_EPS_ATTACH ;
for ( i = 0 ; i < 8 ; i + + ) {
for ( i = 0 ; i < 8 ; i + + ) {
attach_req . ue_network_cap . eea [ i ] = false ;
attach_req . ue_network_cap . eea [ i ] = eea_caps [ i ] ;
attach_req . ue_network_cap . eia [ i ] = false ;
attach_req . ue_network_cap . eia [ i ] = eia_caps [ i ] ;
}
}
attach_req . ue_network_cap . eea [ 0 ] = true ; // EEA0 supported
attach_req . ue_network_cap . eia [ 0 ] = true ; // EIA0 supported
attach_req . ue_network_cap . eia [ 1 ] = true ; // EIA1 supported
attach_req . ue_network_cap . eia [ 2 ] = true ; // EIA2 supported
attach_req . ue_network_cap . uea_present = false ; // UMTS encryption algos
attach_req . ue_network_cap . uia_present = false ; // UMTS integrity algos
attach_req . ms_network_cap_present = false ; // A/Gb mode (2G) or Iu mode (3G)
attach_req . eps_mobile_id . type_of_id = LIBLTE_MME_EPS_MOBILE_ID_TYPE_IMSI ;
usim - > get_imsi_vec ( attach_req . eps_mobile_id . imsi , 15 ) ;
// ESM message (PDN connectivity request) for first default bearer
gen_pdn_connectivity_request ( & attach_req . esm_msg ) ;
attach_req . ue_network_cap . uea_present = false ; // UMTS encryption algos
attach_req . ue_network_cap . uia_present = false ; // UMTS integrity algos
attach_req . ms_network_cap_present = false ; // A/Gb mode (2G) or Iu mode (3G)
attach_req . old_p_tmsi_signature_present = false ;
attach_req . old_p_tmsi_signature_present = false ;
attach_req . additional_guti_present = false ;
attach_req . additional_guti_present = false ;
attach_req . last_visited_registered_tai_present = false ;
attach_req . last_visited_registered_tai_present = false ;
attach_req . drx_param_present = false ;
attach_req . drx_param_present = false ;
attach_req . ms_network_cap_present = false ;
attach_req . old_lai_present = false ;
attach_req . old_lai_present = false ;
attach_req . tmsi_status_present = false ;
attach_req . tmsi_status_present = false ;
attach_req . ms_cm2_present = false ;
attach_req . ms_cm2_present = false ;
@ -613,11 +707,47 @@ void nas::send_attach_request() {
attach_req . device_properties_present = false ;
attach_req . device_properties_present = false ;
attach_req . old_guti_type_present = false ;
attach_req . old_guti_type_present = false ;
// Pack the message
// ESM message (PDN connectivity request) for first default bearer
liblte_mme_pack_attach_request_msg ( & attach_req , ( LIBLTE_BYTE_MSG_STRUCT * ) msg ) ;
gen_pdn_connectivity_request ( & attach_req . esm_msg ) ;
// GUTI or IMSI attach
if ( have_guti & & have_ctxt ) {
attach_req . eps_mobile_id . type_of_id = LIBLTE_MME_EPS_MOBILE_ID_TYPE_GUTI ;
memcpy ( & attach_req . eps_mobile_id . guti , & ctxt . guti , sizeof ( LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT ) ) ;
attach_req . old_guti_type = LIBLTE_MME_GUTI_TYPE_NATIVE ;
attach_req . old_guti_type_present = true ;
attach_req . nas_ksi . tsc_flag = LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE ;
attach_req . nas_ksi . nas_ksi = ctxt . ksi ;
nas_log - > info ( " Requesting GUTI attach. "
" m_tmsi: %x, mcc: %x, mnc: %x, mme_group_id: %x, mme_code: %x \n " ,
ctxt . guti . m_tmsi , ctxt . guti . mcc , ctxt . guti . mnc , ctxt . guti . mme_group_id , ctxt . guti . mme_code ) ;
liblte_mme_pack_attach_request_msg ( & attach_req ,
LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY ,
ctxt . tx_count ,
( LIBLTE_BYTE_MSG_STRUCT * ) msg ) ;
// Add MAC
integrity_generate ( ctxt . integ_algo ,
& k_nas_int [ 16 ] ,
ctxt . tx_count ,
cfg . lcid - 1 ,
SECURITY_DIRECTION_UPLINK ,
& msg - > msg [ 5 ] ,
msg - > N_bytes - 5 ,
& msg - > msg [ 1 ] ) ;
} else {
attach_req . eps_mobile_id . type_of_id = LIBLTE_MME_EPS_MOBILE_ID_TYPE_IMSI ;
usim - > get_imsi_vec ( attach_req . eps_mobile_id . imsi , 15 ) ;
nas_log - > info ( " Requesting IMSI attach (IMSI=%s) \n " , usim - > get_imsi_str ( ) . c_str ( ) ) ;
liblte_mme_pack_attach_request_msg ( & attach_req , ( LIBLTE_BYTE_MSG_STRUCT * ) msg ) ;
}
nas_log - > info ( " Sending attach request \n " ) ;
nas_log - > info ( " Sending attach request \n " ) ;
rrc - > write_sdu ( cfg . lcid , msg ) ;
rrc - > write_sdu ( cfg . lcid , msg ) ;
if ( have_ctxt ) {
ctxt . tx_count + + ;
}
}
}
void nas : : gen_pdn_connectivity_request ( LIBLTE_BYTE_MSG_STRUCT * msg ) {
void nas : : gen_pdn_connectivity_request ( LIBLTE_BYTE_MSG_STRUCT * msg ) {
@ -641,27 +771,36 @@ void nas::gen_pdn_connectivity_request(LIBLTE_BYTE_MSG_STRUCT *msg) {
liblte_mme_pack_pdn_connectivity_request_msg ( & pdn_con_req , msg ) ;
liblte_mme_pack_pdn_connectivity_request_msg ( & pdn_con_req , msg ) ;
}
}
void nas : : send_security_mode_reject ( uint8_t cause ) {
byte_buffer_t * msg = pool_allocate ;
LIBLTE_MME_SECURITY_MODE_REJECT_MSG_STRUCT sec_mode_rej ;
sec_mode_rej . emm_cause = cause ;
liblte_mme_pack_security_mode_reject_msg ( & sec_mode_rej , ( LIBLTE_BYTE_MSG_STRUCT * ) msg ) ;
rrc - > write_sdu ( cfg . lcid , msg ) ;
}
void nas : : send_identity_response ( ) { }
void nas : : send_identity_response ( ) { }
void nas : : send_service_request ( ) {
void nas : : send_service_request ( ) {
byte_buffer_t * msg = pool_allocate ;
byte_buffer_t * msg = pool_allocate ;
count_ul + + ;
// Pack the service request message directly
// Pack the service request message directly
msg - > msg [ 0 ] = ( LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST < < 4 ) | ( LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT ) ;
msg - > msg [ 0 ] = ( LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST < < 4 ) | ( LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT ) ;
msg - > N_bytes + + ;
msg - > N_bytes + + ;
msg - > msg [ 1 ] = ( ksi & 0x07 ) < < 5 ;
msg - > msg [ 1 ] = ( ctxt. ksi & 0x07 ) < < 5 ;
msg - > msg [ 1 ] | = c ount_ul & 0x1F ;
msg - > msg [ 1 ] | = c txt. tx_c ount & 0x1F ;
msg - > N_bytes + + ;
msg - > N_bytes + + ;
uint8_t mac [ 4 ] ;
uint8_t mac [ 4 ] ;
integrity_generate ( & k_nas_int [ 16 ] ,
integrity_generate ( ctxt . integ_algo ,
count_ul ,
& k_nas_int [ 16 ] ,
cfg . lcid - 1 ,
ctxt . tx_count ,
SECURITY_DIRECTION_UPLINK ,
cfg . lcid - 1 ,
& msg - > msg [ 0 ] ,
SECURITY_DIRECTION_UPLINK ,
2 ,
& msg - > msg [ 0 ] ,
& mac [ 0 ] ) ;
2 ,
& mac [ 0 ] ) ;
// Set the short MAC
// Set the short MAC
msg - > msg [ 2 ] = mac [ 2 ] ;
msg - > msg [ 2 ] = mac [ 2 ] ;
msg - > N_bytes + + ;
msg - > N_bytes + + ;
@ -669,8 +808,167 @@ void nas::send_service_request() {
msg - > N_bytes + + ;
msg - > N_bytes + + ;
nas_log - > info ( " Sending service request \n " ) ;
nas_log - > info ( " Sending service request \n " ) ;
rrc - > write_sdu ( cfg . lcid , msg ) ;
rrc - > write_sdu ( cfg . lcid , msg ) ;
ctxt . tx_count + + ;
}
}
void nas : : send_esm_information_response ( ) { }
void nas : : send_esm_information_response ( ) { }
/*******************************************************************************
* Security context persistence file
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool nas : : read_ctxt_file ( nas_sec_ctxt * ctxt )
{
std : : ifstream file ;
if ( ! ctxt ) {
return false ;
}
file . open ( " .ctxt " , std : : ios : : in ) ;
if ( file . is_open ( ) ) {
if ( ! readvar ( file , " m_tmsi= " , & ctxt - > guti . m_tmsi ) ) { return false ; }
if ( ! readvar ( file , " mcc= " , & ctxt - > guti . mcc ) ) { return false ; }
if ( ! readvar ( file , " mnc= " , & ctxt - > guti . mnc ) ) { return false ; }
if ( ! readvar ( file , " mme_group_id= " , & ctxt - > guti . mme_group_id ) ) { return false ; }
if ( ! readvar ( file , " mme_code= " , & ctxt - > guti . mme_code ) ) { return false ; }
if ( ! readvar ( file , " tx_count= " , & ctxt - > tx_count ) ) { return false ; }
if ( ! readvar ( file , " rx_count= " , & ctxt - > rx_count ) ) { return false ; }
if ( ! readvar ( file , " int_alg= " , & ctxt - > integ_algo ) ) { return false ; }
if ( ! readvar ( file , " enc_alg= " , & ctxt - > cipher_algo ) ) { return false ; }
if ( ! readvar ( file , " ksi= " , & ctxt - > ksi ) ) { return false ; }
if ( ! readvar ( file , " k_asme= " , ctxt - > k_asme , 32 ) ) { return false ; }
file . close ( ) ;
have_guti = true ;
nas_log - > info ( " Read GUTI from file "
" m_tmsi: %x, mcc: %x, mnc: %x, mme_group_id: %x, mme_code: %x \n " ,
ctxt - > guti . m_tmsi ,
ctxt - > guti . mcc ,
ctxt - > guti . mnc ,
ctxt - > guti . mme_group_id ,
ctxt - > guti . mme_code ) ;
have_ctxt = true ;
nas_log - > info ( " Read security ctxt from file .ctxt. "
" ksi: %x, k_asme: %s, tx_count: %x, rx_count: %x, int_alg: %d, enc_alg: %d \n " ,
ctxt - > ksi ,
hex_to_string ( ctxt - > k_asme , 32 ) . c_str ( ) ,
ctxt - > tx_count ,
ctxt - > rx_count ,
ctxt - > integ_algo ,
ctxt - > cipher_algo ) ;
return true ;
} else {
return false ;
}
}
bool nas : : write_ctxt_file ( nas_sec_ctxt ctxt )
{
if ( ! have_guti | | ! have_ctxt ) {
return false ;
}
std : : ofstream file ;
file . open ( " .ctxt " , std : : ios : : out | std : : ios : : trunc ) ;
if ( file . is_open ( ) ) {
file < < " m_tmsi= " < < ( int ) ctxt . guti . m_tmsi < < std : : endl ;
file < < " mcc= " < < ( int ) ctxt . guti . mcc < < std : : endl ;
file < < " mnc= " < < ( int ) ctxt . guti . mnc < < std : : endl ;
file < < " mme_group_id= " < < ( int ) ctxt . guti . mme_group_id < < std : : endl ;
file < < " mme_code= " < < ( int ) ctxt . guti . mme_code < < std : : endl ;
file < < " tx_count= " < < ( int ) ctxt . tx_count < < std : : endl ;
file < < " rx_count= " < < ( int ) ctxt . rx_count < < std : : endl ;
file < < " int_alg= " < < ( int ) ctxt . integ_algo < < std : : endl ;
file < < " enc_alg= " < < ( int ) ctxt . cipher_algo < < std : : endl ;
file < < " ksi= " < < ( int ) ctxt . ksi < < std : : endl ;
file < < " k_asme= " < < hex_to_string ( ctxt . k_asme , 32 ) < < std : : endl ;
nas_log - > info ( " Saved GUTI to file "
" m_tmsi: %x, mcc: %x, mnc: %x, mme_group_id: %x, mme_code: %x \n " ,
ctxt . guti . m_tmsi ,
ctxt . guti . mcc ,
ctxt . guti . mnc ,
ctxt . guti . mme_group_id ,
ctxt . guti . mme_code ) ;
nas_log - > info ( " Saved security ctxt to file .ctxt. "
" ksi: %x, k_asme: %s, tx_count: %x, rx_count: %x, int_alg: %d, enc_alg: %d \n " ,
ctxt . ksi ,
hex_to_string ( ctxt . k_asme , 32 ) . c_str ( ) ,
ctxt . tx_count ,
ctxt . rx_count ,
ctxt . integ_algo ,
ctxt . cipher_algo ) ;
file . close ( ) ;
return true ;
} else {
return false ;
}
}
/*********************************************************************
* Conversion helpers
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
std : : string nas : : hex_to_string ( uint8_t * hex , int size )
{
std : : stringstream ss ;
ss < < std : : hex < < std : : setfill ( ' 0 ' ) ;
for ( int i = 0 ; i < size ; i + + ) {
ss < < std : : setw ( 2 ) < < static_cast < unsigned > ( hex [ i ] ) ;
}
return ss . str ( ) ;
}
bool nas : : string_to_hex ( std : : string hex_str , uint8_t * hex , uint32_t len )
{
static const char * const lut = " 0123456789abcdef " ;
uint32_t str_len = hex_str . length ( ) ;
if ( str_len & 1 ) {
return false ; // uneven hex_str length
}
if ( str_len > len * 2 ) {
return false ; // not enough space in hex buffer
}
for ( uint32_t i = 0 ; i < str_len ; i + = 2 )
{
char a = hex_str [ i ] ;
const char * p = std : : lower_bound ( lut , lut + 16 , a ) ;
if ( * p ! = a ) {
return false ; // invalid char
}
char b = hex_str [ i + 1 ] ;
const char * q = std : : lower_bound ( lut , lut + 16 , b ) ;
if ( * q ! = b ) {
return false ; // invalid char
}
hex [ i / 2 ] = ( ( p - lut ) < < 4 ) | ( q - lut ) ;
}
return true ;
}
std : : string nas : : emm_info_str ( LIBLTE_MME_EMM_INFORMATION_MSG_STRUCT * info )
{
std : : stringstream ss ;
if ( info - > full_net_name_present ) {
ss < < info - > full_net_name . name ;
}
if ( info - > short_net_name_present ) {
ss < < " ( " < < info - > short_net_name . name < < " ) " ;
}
if ( info - > utc_and_local_time_zone_present ) {
ss < < " " < < ( int ) info - > utc_and_local_time_zone . day ;
ss < < " / " < < ( int ) info - > utc_and_local_time_zone . month ;
ss < < " / " < < ( int ) info - > utc_and_local_time_zone . year ;
ss < < " " < < ( int ) info - > utc_and_local_time_zone . hour ;
ss < < " : " < < ( int ) info - > utc_and_local_time_zone . minute ;
ss < < " : " < < ( int ) info - > utc_and_local_time_zone . second ;
ss < < " TZ: " < < ( int ) info - > utc_and_local_time_zone . tz ;
}
return ss . str ( ) ;
}
} // namespace srsue
} // namespace srsue