diff --git a/src/amf/amf-sm.c b/src/amf/amf-sm.c index de5757d58..43d32eee5 100644 --- a/src/amf/amf-sm.c +++ b/src/amf/amf-sm.c @@ -891,19 +891,19 @@ void amf_state_operational(ogs_fsm_t *s, amf_event_t *e) ogs_pkbuf_free(pkbuf); break; } + + ogs_assert(CM_IDLE(amf_ue)); } else { /* Here, if the AMF_UE Context is found, * the integrity check is not performed - * For example, REGISTRATION_REQUEST, - * TRACKING_AREA_UPDATE_REQUEST message + * For example, REGISTRATION_REQUEST, SERVICE_REQUEST message * * Now, We will check the MAC in the NAS message*/ ogs_nas_security_header_type_t h; h.type = e->nas.type; if (h.integrity_protected) { /* Decryption was performed in NGAP handler. - * So, we disabled 'ciphered' - * not to decrypt NAS message */ + * So, we disabled 'ciphered' not to decrypt NAS message */ h.ciphered = 0; if (nas_5gs_security_decode(amf_ue, h, pkbuf) != OGS_OK) { ogs_error("[%s] nas_security_decode() failed", @@ -912,42 +912,45 @@ void amf_state_operational(ogs_fsm_t *s, amf_event_t *e) break; } } - } - /* - * TS23.502 - * 4.2.3.2 UE Triggered Service Request - * - * 4. [Conditional] - * AMF to SMF: Nsmf_PDUSession_UpdateSMContext Request - * - * The AMF may receive a Service Request to establish another - * NAS signalling connection via a NG-RAN while it has maintained - * an old NAS signalling connection for UE still via NG-RAN. - * In this case, AMF shall trigger the AN release procedure toward - * the old NG-RAN to release the old NAS signalling connection - * as defined in clause 4.2.6 with following logic: */ + /* + * TS23.502 + * 4.2.3.2 UE Triggered Service Request + * + * 4. [Conditional] + * AMF to SMF: Nsmf_PDUSession_UpdateSMContext Request + * + * The AMF may receive a Service Request to establish another + * NAS signalling connection via a NG-RAN while it has + * maintained an old NAS signalling connection for UE still + * via NG-RAN. In this case, AMF shall trigger the AN release + * procedure toward the old NG-RAN to release the old NAS + * signalling connection as defined in clause 4.2.6 + * with following logic: + */ - /* If NAS(amf_ue_t) has already been associated with - * older NG(ran_ue_t) context */ - if (CM_CONNECTED(amf_ue)) { - /* Previous NG(ran_ue_t) context the holding timer(30secs) - * is started. - * Newly associated NG(ran_ue_t) context holding timer - * is stopped. */ - ogs_debug("[%s] Start NG Holding Timer", amf_ue->suci); - ogs_debug("[%s] RAN_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%lld]", - amf_ue->suci, amf_ue->ran_ue->ran_ue_ngap_id, - (long long)amf_ue->ran_ue->amf_ue_ngap_id); - - /* De-associate NG with NAS/EMM */ - ran_ue_deassociate(amf_ue->ran_ue); - - r = ngap_send_ran_ue_context_release_command(amf_ue->ran_ue, - NGAP_Cause_PR_nas, NGAP_CauseNas_normal_release, - NGAP_UE_CTX_REL_NG_CONTEXT_REMOVE, 0); - ogs_expect(r == OGS_OK); - ogs_assert(r != OGS_ERROR); + /* If NAS(amf_ue_t) has already been associated with + * older NG(ran_ue_t) context */ + if (CM_CONNECTED(amf_ue)) { + /* + * Issue #2786 + * + * In cases where the UE sends an Integrity Un-Protected Registration + * Request or Service Request, there is an issue of sending + * a UEContextReleaseCommand for the OLD RAN Context. + * + * For example, if the UE switchs off and power-on after + * the first connection, the 5G Core sends a UEContextReleaseCommand. + * + * However, since there is no RAN context for this on the gNB, + * the gNB does not send a UEContextReleaseComplete, + * so the deletion of the RAN Context does not function properly. + * + * To solve this problem, the 5G Core has been modified to implicitly + * delete the RAN Context instead of sending a UEContextReleaseCommand. + */ + HOLDING_NG_CONTEXT(amf_ue); + } } amf_ue_associate_ran_ue(amf_ue, ran_ue); diff --git a/src/amf/context.c b/src/amf/context.c index 7ce9310d7..0b736c027 100644 --- a/src/amf/context.c +++ b/src/amf/context.c @@ -1973,8 +1973,8 @@ void amf_ue_set_suci(amf_ue_t *amf_ue, ogs_warn("[%s] OLD UE Context Release", suci); if (CM_CONNECTED(old_amf_ue)) { /* Implcit NG release */ - ogs_info("[%s] Implicit NG release", suci); - ogs_info("[%s] RAN_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%lld]", + ogs_warn("[%s] Implicit NG release", suci); + ogs_warn("[%s] RAN_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%lld]", old_amf_ue->suci, old_amf_ue->ran_ue->ran_ue_ngap_id, (long long)old_amf_ue->ran_ue->amf_ue_ngap_id); ran_ue_remove(old_amf_ue->ran_ue); diff --git a/src/amf/context.h b/src/amf/context.h index 11a25b94c..2e4020d25 100644 --- a/src/amf/context.h +++ b/src/amf/context.h @@ -107,7 +107,7 @@ typedef struct amf_context_s { ogs_hash_t *gnb_addr_hash; /* hash table for GNB Address */ ogs_hash_t *gnb_id_hash; /* hash table for GNB-ID */ - ogs_hash_t *guti_ue_hash; /* hash table (GUTI : AMF_UE) */ + ogs_hash_t *guti_ue_hash; /* hash table (GUTI : AMF_UE) */ ogs_hash_t *suci_hash; /* hash table (SUCI) */ ogs_hash_t *supi_hash; /* hash table (SUPI) */ @@ -382,6 +382,45 @@ struct amf_ue_s { /* NG UE context */ ran_ue_t *ran_ue; +#define HOLDING_NG_CONTEXT(__aMF) \ + do { \ + ran_ue_deassociate((__aMF)->ran_ue); \ + \ + (__aMF)->ran_ue_holding = ran_ue_cycle((__aMF)->ran_ue); \ + if ((__aMF)->ran_ue_holding) { \ + ogs_warn("[%s] Holding NG Context", (__aMF)->suci); \ + ogs_warn("[%s] RAN_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%lld]", \ + (__aMF)->suci, (__aMF)->ran_ue_holding->ran_ue_ngap_id, \ + (long long)(__aMF)->ran_ue_holding->amf_ue_ngap_id); \ + \ + (__aMF)->ran_ue_holding->ue_ctx_rel_action = \ + NGAP_UE_CTX_REL_NG_CONTEXT_REMOVE; \ + ogs_timer_start((__aMF)->ran_ue_holding->t_ng_holding, \ + amf_timer_cfg(AMF_TIMER_NG_HOLDING)->duration); \ + } else \ + ogs_error("[%s] NG Context has already been removed", \ + (__aMF)->suci); \ + } while(0) +#define CLEAR_NG_CONTEXT(__aMF) \ + do { \ + if (ran_ue_cycle((__aMF)->ran_ue_holding)) { \ + int r; \ + ogs_warn("[%s] Clear NG Context", (__aMF)->suci); \ + ogs_warn("[%s] RAN_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%lld]", \ + (__aMF)->suci, (__aMF)->ran_ue_holding->ran_ue_ngap_id, \ + (long long)(__aMF)->ran_ue_holding->amf_ue_ngap_id); \ + \ + r = ngap_send_ran_ue_context_release_command( \ + (__aMF)->ran_ue_holding, \ + NGAP_Cause_PR_nas, NGAP_CauseNas_normal_release, \ + NGAP_UE_CTX_REL_NG_CONTEXT_REMOVE, 0); \ + ogs_expect(r == OGS_OK); \ + ogs_assert(r != OGS_ERROR); \ + } \ + (__aMF)->ran_ue_holding = NULL; \ + } while(0) + ran_ue_t *ran_ue_holding; + #define CLEAR_AMF_UE_ALL_TIMERS(__aMF) \ do { \ CLEAR_AMF_UE_TIMER((__aMF)->t3513); \ diff --git a/src/amf/gmm-sm.c b/src/amf/gmm-sm.c index 7dc59ec73..0a7d4aaba 100644 --- a/src/amf/gmm-sm.c +++ b/src/amf/gmm-sm.c @@ -1204,6 +1204,13 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e, if (h.integrity_protected && SECURITY_CONTEXT_IS_VALID(amf_ue)) { + /* + * If the OLD RAN_UE is being maintained in AMF-UE Context, + * it deletes the NG Context after exchanging + * the UEContextReleaseCommand/Complete with the gNB + */ + CLEAR_NG_CONTEXT(amf_ue); + gmm_cause = gmm_handle_registration_update( amf_ue, &nas_message->gmm.registration_request); if (gmm_cause != OGS_5GMM_CAUSE_REQUEST_ACCEPTED) { @@ -1325,6 +1332,13 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e, break; } + /* + * If the OLD RAN_UE is being maintained in AMF-UE Context, + * it deletes the NG Context after exchanging + * the UEContextReleaseCommand/Complete with the gNB + */ + CLEAR_NG_CONTEXT(amf_ue); + gmm_cause = gmm_handle_service_update( amf_ue, &nas_message->gmm.service_request); if (gmm_cause != OGS_5GMM_CAUSE_REQUEST_ACCEPTED) { @@ -1391,6 +1405,19 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e, case OGS_NAS_5GS_DEREGISTRATION_REQUEST_FROM_UE: ogs_info("[%s] Deregistration request", amf_ue->supi); + if (!h.integrity_protected || !SECURITY_CONTEXT_IS_VALID(amf_ue)) { + ogs_error("No Security Context"); + OGS_FSM_TRAN(s, gmm_state_exception); + break; + } + + /* + * If the OLD RAN_UE is being maintained in AMF-UE Context, + * it deletes the NG Context after exchanging + * the UEContextReleaseCommand/Complete with the gNB + */ + CLEAR_NG_CONTEXT(amf_ue); + gmm_handle_deregistration_request( amf_ue, &nas_message->gmm.deregistration_request_from_ue); OGS_FSM_TRAN(s, &gmm_state_de_registered); @@ -1831,6 +1858,13 @@ void gmm_state_security_mode(ogs_fsm_t *s, amf_event_t *e) break; } + /* + * If the OLD RAN_UE is being maintained in AMF-UE Context, + * it deletes the NG Context after exchanging + * the UEContextReleaseCommand/Complete with the gNB + */ + CLEAR_NG_CONTEXT(amf_ue); + CLEAR_AMF_UE_TIMER(amf_ue->t3560); gmm_cause = gmm_handle_security_mode_complete( @@ -2429,6 +2463,13 @@ void gmm_state_exception(ogs_fsm_t *s, amf_event_t *e) if (h.integrity_protected && SECURITY_CONTEXT_IS_VALID(amf_ue)) { + /* + * If the OLD RAN_UE is being maintained in AMF-UE Context, + * it deletes the NG Context after exchanging + * the UEContextReleaseCommand/Complete with the gNB + */ + CLEAR_NG_CONTEXT(amf_ue); + gmm_cause = gmm_handle_registration_update( amf_ue, &nas_message->gmm.registration_request); if (gmm_cause != OGS_5GMM_CAUSE_REQUEST_ACCEPTED) { diff --git a/src/amf/ngap-handler.c b/src/amf/ngap-handler.c index e415e82c9..e662616fe 100644 --- a/src/amf/ngap-handler.c +++ b/src/amf/ngap-handler.c @@ -486,24 +486,24 @@ void ngap_handle_initial_ue_message(amf_gnb_t *gnb, ogs_ngap_message_t *message) /* If NAS(amf_ue_t) has already been associated with * older NG(ran_ue_t) context */ if (CM_CONNECTED(amf_ue)) { - /* Previous NG(ran_ue_t) context the holding timer(30secs) - * is started. - * Newly associated NG(ran_ue_t) context holding timer - * is stopped. */ - ogs_debug("[%s] Start NG Holding Timer", amf_ue->suci); - ogs_debug("[%s] RAN_UE_NGAP_ID[%d] AMF_UE_NGAP_ID[%lld]", - amf_ue->suci, amf_ue->ran_ue->ran_ue_ngap_id, - (long long)amf_ue->ran_ue->amf_ue_ngap_id); - - /* De-associate NG with NAS/EMM */ - ran_ue_deassociate(amf_ue->ran_ue); - - r = ngap_send_ran_ue_context_release_command( - amf_ue->ran_ue, - NGAP_Cause_PR_nas, NGAP_CauseNas_normal_release, - NGAP_UE_CTX_REL_NG_CONTEXT_REMOVE, 0); - ogs_expect(r == OGS_OK); - ogs_assert(r != OGS_ERROR); + /* + * Issue #2786 + * + * In cases where the UE sends an Integrity Un-Protected Registration + * Request or Service Request, there is an issue of sending + * a UEContextReleaseCommand for the OLD RAN Context. + * + * For example, if the UE switchs off and power-on after + * the first connection, the 5G Core sends a UEContextReleaseCommand. + * + * However, since there is no RAN context for this on the gNB, + * the gNB does not send a UEContextReleaseComplete, + * so the deletion of the RAN Context does not function properly. + * + * To solve this problem, the 5G Core has been modified to implicitly + * delete the RAN Context instead of sending a UEContextReleaseCommand. + */ + HOLDING_NG_CONTEXT(amf_ue); } amf_ue_associate_ran_ue(amf_ue, ran_ue); diff --git a/src/mme/emm-sm.c b/src/mme/emm-sm.c index a53fe6c6f..cd6d41246 100644 --- a/src/mme/emm-sm.c +++ b/src/mme/emm-sm.c @@ -370,6 +370,13 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, break; } + /* + * If the OLD ENB_UE is being maintained in MME-UE Context, + * it deletes the S1 Context after exchanging + * the UEContextReleaseCommand/Complete with the eNB + */ + CLEAR_S1_CONTEXT(mme_ue); + r = s1ap_send_initial_context_setup_request(mme_ue); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); @@ -427,6 +434,13 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, } if (h.integrity_protected && SECURITY_CONTEXT_IS_VALID(mme_ue)) { + /* + * If the OLD ENB_UE is being maintained in MME-UE Context, + * it deletes the S1 Context after exchanging + * the UEContextReleaseCommand/Complete with the eNB + */ + CLEAR_S1_CONTEXT(mme_ue); + mme_gtp_send_delete_all_sessions(mme_ue, OGS_GTP_DELETE_HANDLE_PDN_CONNECTIVITY_REQUEST); @@ -510,6 +524,13 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, break; } + /* + * If the OLD ENB_UE is being maintained in MME-UE Context, + * it deletes the S1 Context after exchanging + * the UEContextReleaseCommand/Complete with the eNB + */ + CLEAR_S1_CONTEXT(mme_ue); + /* * * 1. InitialUEMessage + Tracking area update request @@ -669,6 +690,13 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, break; } + /* + * If the OLD ENB_UE is being maintained in MME-UE Context, + * it deletes the S1 Context after exchanging + * the UEContextReleaseCommand/Complete with the eNB + */ + CLEAR_S1_CONTEXT(mme_ue); + if (e->s1ap_code == S1AP_ProcedureCode_id_initialUEMessage) { ogs_debug(" Initial UE Message"); @@ -795,6 +823,13 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, break; } + /* + * If the OLD ENB_UE is being maintained in MME-UE Context, + * it deletes the S1 Context after exchanging + * the UEContextReleaseCommand/Complete with the eNB + */ + CLEAR_S1_CONTEXT(mme_ue); + if (MME_P_TMSI_IS_AVAILABLE(mme_ue)) { ogs_assert(OGS_OK == sgsap_send_detach_indication(mme_ue)); } else { @@ -1010,6 +1045,13 @@ void emm_state_authentication(ogs_fsm_t *s, mme_event_t *e) break; } + /* + * If the OLD ENB_UE is being maintained in MME-UE Context, + * it deletes the S1 Context after exchanging + * the UEContextReleaseCommand/Complete with the eNB + */ + CLEAR_S1_CONTEXT(mme_ue); + if (MME_P_TMSI_IS_AVAILABLE(mme_ue)) { ogs_assert(OGS_OK == sgsap_send_detach_indication(mme_ue)); } else { @@ -1127,6 +1169,13 @@ void emm_state_security_mode(ogs_fsm_t *s, mme_event_t *e) break; } + /* + * If the OLD ENB_UE is being maintained in MME-UE Context, + * it deletes the S1 Context after exchanging + * the UEContextReleaseCommand/Complete with the eNB + */ + CLEAR_S1_CONTEXT(mme_ue); + emm_handle_security_mode_complete( mme_ue, &message->emm.security_mode_complete); @@ -1210,6 +1259,13 @@ void emm_state_security_mode(ogs_fsm_t *s, mme_event_t *e) break; } + /* + * If the OLD ENB_UE is being maintained in MME-UE Context, + * it deletes the S1 Context after exchanging + * the UEContextReleaseCommand/Complete with the eNB + */ + CLEAR_S1_CONTEXT(mme_ue); + if (MME_P_TMSI_IS_AVAILABLE(mme_ue)) { ogs_assert(OGS_OK == sgsap_send_detach_indication(mme_ue)); } else { @@ -1324,6 +1380,13 @@ void emm_state_initial_context_setup(ogs_fsm_t *s, mme_event_t *e) break; } + /* + * If the OLD ENB_UE is being maintained in MME-UE Context, + * it deletes the S1 Context after exchanging + * the UEContextReleaseCommand/Complete with the eNB + */ + CLEAR_S1_CONTEXT(mme_ue); + CLEAR_MME_UE_TIMER(mme_ue->t3450); rv = emm_handle_attach_complete( @@ -1376,6 +1439,13 @@ void emm_state_initial_context_setup(ogs_fsm_t *s, mme_event_t *e) break; } + /* + * If the OLD ENB_UE is being maintained in MME-UE Context, + * it deletes the S1 Context after exchanging + * the UEContextReleaseCommand/Complete with the eNB + */ + CLEAR_S1_CONTEXT(mme_ue); + CLEAR_MME_UE_TIMER(mme_ue->t3450); /* Confirm GUTI */ @@ -1441,6 +1511,13 @@ void emm_state_initial_context_setup(ogs_fsm_t *s, mme_event_t *e) break; } + /* + * If the OLD ENB_UE is being maintained in MME-UE Context, + * it deletes the S1 Context after exchanging + * the UEContextReleaseCommand/Complete with the eNB + */ + CLEAR_S1_CONTEXT(mme_ue); + if (MME_P_TMSI_IS_AVAILABLE(mme_ue)) { ogs_assert(OGS_OK == sgsap_send_detach_indication(mme_ue)); } else { @@ -1560,6 +1637,13 @@ void emm_state_exception(ogs_fsm_t *s, mme_event_t *e) } if (h.integrity_protected && SECURITY_CONTEXT_IS_VALID(mme_ue)) { + /* + * If the OLD ENB_UE is being maintained in MME-UE Context, + * it deletes the S1 Context after exchanging + * the UEContextReleaseCommand/Complete with the eNB + */ + CLEAR_S1_CONTEXT(mme_ue); + mme_gtp_send_delete_all_sessions(mme_ue, OGS_GTP_DELETE_HANDLE_PDN_CONNECTIVITY_REQUEST); diff --git a/src/mme/mme-context.c b/src/mme/mme-context.c index 480dbc505..281e9f9ad 100644 --- a/src/mme/mme-context.c +++ b/src/mme/mme-context.c @@ -3671,8 +3671,8 @@ int mme_ue_set_imsi(mme_ue_t *mme_ue, char *imsi_bcd) ogs_warn("[%s] OLD UE Context Release", mme_ue->imsi_bcd); if (ECM_CONNECTED(old_mme_ue)) { /* Implcit S1 release */ - ogs_info("[%s] Implicit S1 release", mme_ue->imsi_bcd); - ogs_info("[%s] ENB_UE_S1AP_ID[%d] MME_UE_S1AP_ID[%d]", + ogs_warn("[%s] Implicit S1 release", mme_ue->imsi_bcd); + ogs_warn("[%s] ENB_UE_S1AP_ID[%d] MME_UE_S1AP_ID[%d]", old_mme_ue->imsi_bcd, old_mme_ue->enb_ue->enb_ue_s1ap_id, old_mme_ue->enb_ue->mme_ue_s1ap_id); diff --git a/src/mme/mme-context.h b/src/mme/mme-context.h index 3b00e5932..a528b1ca9 100644 --- a/src/mme/mme-context.h +++ b/src/mme/mme-context.h @@ -531,11 +531,50 @@ struct mme_ue_s { (((__mME)->enb_ue == NULL) || (enb_ue_cycle((__mME)->enb_ue) == NULL))) enb_ue_t *enb_ue; /* S1 UE context */ +#define HOLDING_S1_CONTEXT(__mME) \ + do { \ + enb_ue_deassociate((__mME)->enb_ue); \ + \ + (__mME)->enb_ue_holding = enb_ue_cycle((__mME)->enb_ue); \ + if ((__mME)->enb_ue_holding) { \ + ogs_warn("[%s] Holding S1 Context", (__mME)->imsi_bcd); \ + ogs_warn("[%s] ENB_UE_S1AP_ID[%d] MME_UE_S1AP_ID[%d]", \ + (__mME)->imsi_bcd, (__mME)->enb_ue_holding->enb_ue_s1ap_id, \ + (__mME)->enb_ue_holding->mme_ue_s1ap_id); \ + \ + (__mME)->enb_ue_holding->ue_ctx_rel_action = \ + S1AP_UE_CTX_REL_S1_CONTEXT_REMOVE; \ + ogs_timer_start((__mME)->enb_ue_holding->t_s1_holding, \ + mme_timer_cfg(MME_TIMER_S1_HOLDING)->duration); \ + } else \ + ogs_error("[%s] S1 Context has already been removed", \ + (__mME)->imsi_bcd); \ + } while(0) +#define CLEAR_S1_CONTEXT(__mME) \ + do { \ + if (enb_ue_cycle((__mME)->enb_ue_holding)) { \ + int r; \ + ogs_warn("[%s] Clear S1 Context", (__mME)->imsi_bcd); \ + ogs_warn("[%s] ENB_UE_S1AP_ID[%d] MME_UE_S1AP_ID[%d]", \ + (__mME)->imsi_bcd, (__mME)->enb_ue_holding->enb_ue_s1ap_id, \ + (__mME)->enb_ue_holding->mme_ue_s1ap_id); \ + \ + r = s1ap_send_ue_context_release_command( \ + (__mME)->enb_ue_holding, \ + S1AP_Cause_PR_nas, S1AP_CauseNas_normal_release, \ + S1AP_UE_CTX_REL_S1_CONTEXT_REMOVE, 0); \ + ogs_expect(r == OGS_OK); \ + ogs_assert(r != OGS_ERROR); \ + } \ + (__mME)->enb_ue_holding = NULL; \ + } while(0) + enb_ue_t *enb_ue_holding; + struct { #define MME_CLEAR_PAGING_INFO(__mME) \ do { \ ogs_assert(__mME); \ - ogs_debug("[%s] Clear Paging Info", mme_ue->imsi_bcd); \ + ogs_debug("[%s] Clear Paging Info", (__mME)->imsi_bcd); \ (__mME)->paging.type = 0; \ } while(0) diff --git a/src/mme/mme-sm.c b/src/mme/mme-sm.c index 87554cba4..1a437a5d5 100644 --- a/src/mme/mme-sm.c +++ b/src/mme/mme-sm.c @@ -265,6 +265,8 @@ void mme_state_operational(ogs_fsm_t *s, mme_event_t *e) ogs_pkbuf_free(pkbuf); return; } + + ogs_assert(ECM_IDLE(mme_ue)); } else { /* Here, if the MME_UE Context is found, * the integrity check is not performed @@ -285,29 +287,31 @@ void mme_state_operational(ogs_fsm_t *s, mme_event_t *e) return; } } + + /* If NAS(mme_ue_t) has already been associated with + * older S1(enb_ue_t) context */ + if (ECM_CONNECTED(mme_ue)) { + /* + * Issue #2786 + * + * In cases where the UE sends an Integrity Un-Protected Attach + * Request or Service Request, there is an issue of sending + * a UEContextReleaseCommand for the OLD ENB Context. + * + * For example, if the UE switchs off and power-on after + * the first connection, the EPC sends a UEContextReleaseCommand. + * + * However, since there is no ENB context for this on the eNB, + * the eNB does not send a UEContextReleaseComplete, + * so the deletion of the ENB Context does not function properly. + * + * To solve this problem, the EPC has been modified to implicitly + * delete the ENB Context instead of sending a UEContextReleaseCommand. + */ + HOLDING_S1_CONTEXT(mme_ue); + } } - /* If NAS(mme_ue_t) has already been associated with - * older S1(enb_ue_t) context */ - if (ECM_CONNECTED(mme_ue)) { - /* Previous S1(enb_ue_t) context the holding timer(30secs) - * is started. - * Newly associated S1(enb_ue_t) context holding timer - * is stopped. */ - ogs_debug("Start S1 Holding Timer"); - ogs_debug(" ENB_UE_S1AP_ID[%d] MME_UE_S1AP_ID[%d]", - mme_ue->enb_ue->enb_ue_s1ap_id, - mme_ue->enb_ue->mme_ue_s1ap_id); - - /* De-associate S1 with NAS/EMM */ - enb_ue_deassociate(mme_ue->enb_ue); - - r = s1ap_send_ue_context_release_command(mme_ue->enb_ue, - S1AP_Cause_PR_nas, S1AP_CauseNas_normal_release, - S1AP_UE_CTX_REL_S1_CONTEXT_REMOVE, 0); - ogs_expect(r == OGS_OK); - ogs_assert(r != OGS_ERROR); - } enb_ue_associate_mme_ue(enb_ue, mme_ue); ogs_debug("Mobile Reachable timer stopped for IMSI[%s]", mme_ue->imsi_bcd); diff --git a/src/mme/s1ap-handler.c b/src/mme/s1ap-handler.c index b9e1edaee..024a0a74c 100644 --- a/src/mme/s1ap-handler.c +++ b/src/mme/s1ap-handler.c @@ -496,23 +496,24 @@ void s1ap_handle_initial_ue_message(mme_enb_t *enb, ogs_s1ap_message_t *message) /* If NAS(mme_ue_t) has already been associated with * older S1(enb_ue_t) context */ if (ECM_CONNECTED(mme_ue)) { - /* Previous S1(enb_ue_t) context the holding timer(30secs) - * is started. - * Newly associated S1(enb_ue_t) context holding timer - * is stopped. */ - ogs_debug("Start S1 Holding Timer"); - ogs_debug(" ENB_UE_S1AP_ID[%d] MME_UE_S1AP_ID[%d]", - mme_ue->enb_ue->enb_ue_s1ap_id, - mme_ue->enb_ue->mme_ue_s1ap_id); - - /* De-associate S1 with NAS/EMM */ - enb_ue_deassociate(mme_ue->enb_ue); - - r = s1ap_send_ue_context_release_command(mme_ue->enb_ue, - S1AP_Cause_PR_nas, S1AP_CauseNas_normal_release, - S1AP_UE_CTX_REL_S1_CONTEXT_REMOVE, 0); - ogs_expect(r == OGS_OK); - ogs_assert(r != OGS_ERROR); + /* + * Issue #2786 + * + * In cases where the UE sends an Integrity Un-Protected Attach + * Request or Service Request, there is an issue of sending + * a UEContextReleaseCommand for the OLD ENB Context. + * + * For example, if the UE switchs off and power-on after + * the first connection, the EPC sends a UEContextReleaseCommand. + * + * However, since there is no ENB context for this on the eNB, + * the eNB does not send a UEContextReleaseComplete, + * so the deletion of the ENB Context does not function properly. + * + * To solve this problem, the EPC has been modified to implicitly + * delete the ENB Context instead of sending a UEContextReleaseCommand. + */ + HOLDING_S1_CONTEXT(mme_ue); } enb_ue_associate_mme_ue(enb_ue, mme_ue); ogs_debug("Mobile Reachable timer stopped for IMSI[%s]", diff --git a/tests/attach/auth-test.c b/tests/attach/auth-test.c index 370e4a1b3..04b4225e2 100644 --- a/tests/attach/auth-test.c +++ b/tests/attach/auth-test.c @@ -288,6 +288,7 @@ static void test1_func(abts_case *tc, void *data) rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); +#if SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_UNPROTECTED /* Receive OLD UE Context Release Command */ enb_ue_s1ap_id = test_ue->enb_ue_s1ap_id; @@ -302,6 +303,7 @@ static void test1_func(abts_case *tc, void *data) ABTS_INT_EQUAL(tc, OGS_OK, rv); test_ue->enb_ue_s1ap_id = enb_ue_s1ap_id; +#endif /* Receive Authentication request */ recvbuf = testenb_s1ap_read(s1ap); @@ -361,6 +363,7 @@ static void test1_func(abts_case *tc, void *data) rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); +#if SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_UNPROTECTED /* Receive OLD UE Context Release Command */ enb_ue_s1ap_id = test_ue->enb_ue_s1ap_id; @@ -375,6 +378,7 @@ static void test1_func(abts_case *tc, void *data) ABTS_INT_EQUAL(tc, OGS_OK, rv); test_ue->enb_ue_s1ap_id = enb_ue_s1ap_id; +#endif /* Receive Authentication request */ recvbuf = testenb_s1ap_read(s1ap); diff --git a/tests/attach/guti-test.c b/tests/attach/guti-test.c index a66bea3db..b8662b1b6 100644 --- a/tests/attach/guti-test.c +++ b/tests/attach/guti-test.c @@ -638,6 +638,7 @@ static void test2_func(abts_case *tc, void *data) rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); +#if SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_UNPROTECTED /* Receive OLD UE Context Release Command */ enb_ue_s1ap_id = test_ue->enb_ue_s1ap_id; @@ -652,6 +653,7 @@ static void test2_func(abts_case *tc, void *data) ABTS_INT_EQUAL(tc, OGS_OK, rv); test_ue->enb_ue_s1ap_id = enb_ue_s1ap_id; +#endif /* Receive Authentication request */ recvbuf = testenb_s1ap_read(s1ap); @@ -680,6 +682,23 @@ static void test2_func(abts_case *tc, void *data) rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); +#if SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_PROTECTED + /* Receive OLD UE Context Release Command */ + enb_ue_s1ap_id = test_ue->enb_ue_s1ap_id; + + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send OLD UE Context Release Complete */ + sendbuf = test_s1ap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + test_ue->enb_ue_s1ap_id = enb_ue_s1ap_id; +#endif + /* Receive ESM Information Request */ recvbuf = testenb_s1ap_read(s1ap); ABTS_PTR_NOTNULL(tc, recvbuf); @@ -1047,6 +1066,7 @@ static void test3_func(abts_case *tc, void *data) rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); +#if SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_UNPROTECTED /* Receive OLD UE Context Release Command */ enb_ue_s1ap_id = test_ue->enb_ue_s1ap_id; @@ -1061,6 +1081,7 @@ static void test3_func(abts_case *tc, void *data) ABTS_INT_EQUAL(tc, OGS_OK, rv); test_ue->enb_ue_s1ap_id = enb_ue_s1ap_id; +#endif /* Receive Authentication Request */ recvbuf = testenb_s1ap_read(s1ap); @@ -1089,6 +1110,23 @@ static void test3_func(abts_case *tc, void *data) rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); +#if SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_PROTECTED + /* Receive OLD UE Context Release Command */ + enb_ue_s1ap_id = test_ue->enb_ue_s1ap_id; + + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send OLD UE Context Release Complete */ + sendbuf = test_s1ap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + test_ue->enb_ue_s1ap_id = enb_ue_s1ap_id; +#endif + /* Receive InitialContextSetupResponse + TAU Accept */ recvbuf = testenb_s1ap_read(s1ap); ABTS_PTR_NOTNULL(tc, recvbuf); @@ -1369,6 +1407,7 @@ static void test4_func(abts_case *tc, void *data) rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); +#if SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_UNPROTECTED /* Receive OLD UE Context Release Command */ enb_ue_s1ap_id = test_ue->enb_ue_s1ap_id; @@ -1383,6 +1422,7 @@ static void test4_func(abts_case *tc, void *data) ABTS_INT_EQUAL(tc, OGS_OK, rv); test_ue->enb_ue_s1ap_id = enb_ue_s1ap_id; +#endif /* Receive Authentication Request */ recvbuf = testenb_s1ap_read(s1ap); @@ -1411,6 +1451,23 @@ static void test4_func(abts_case *tc, void *data) rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); +#if SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_PROTECTED + /* Receive OLD UE Context Release Command */ + enb_ue_s1ap_id = test_ue->enb_ue_s1ap_id; + + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send OLD UE Context Release Complete */ + sendbuf = test_s1ap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + test_ue->enb_ue_s1ap_id = enb_ue_s1ap_id; +#endif + /* Receive Initial Context Setup Request + * Attach Accept + * Activate Default Bearer Context Request */ diff --git a/tests/attach/ue-context-test.c b/tests/attach/ue-context-test.c index cfe45f4d0..5826b0685 100644 --- a/tests/attach/ue-context-test.c +++ b/tests/attach/ue-context-test.c @@ -362,6 +362,7 @@ static void test2_func(abts_case *tc, void *data) rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); +#if SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_UNPROTECTED /* Receive OLD UE Context Release Command */ enb_ue_s1ap_id = test_ue->enb_ue_s1ap_id; @@ -376,6 +377,7 @@ static void test2_func(abts_case *tc, void *data) ABTS_INT_EQUAL(tc, OGS_OK, rv); test_ue->enb_ue_s1ap_id = enb_ue_s1ap_id; +#endif /* Receive Authentication Request */ recvbuf = testenb_s1ap_read(s1ap); @@ -404,6 +406,23 @@ static void test2_func(abts_case *tc, void *data) rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); +#if SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_PROTECTED + /* Receive OLD UE Context Release Command */ + enb_ue_s1ap_id = test_ue->enb_ue_s1ap_id; + + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send OLD UE Context Release Complete */ + sendbuf = test_s1ap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + test_ue->enb_ue_s1ap_id = enb_ue_s1ap_id; +#endif + /* Receive Initial Context Setup Request + * Attach Accept + * Activate Default Bearer Context Request */ @@ -642,6 +661,7 @@ static void test3_func(abts_case *tc, void *data) rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); +#if SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_UNPROTECTED /* Receive OLD UE Context Release Command */ enb_ue_s1ap_id = test_ue->enb_ue_s1ap_id; @@ -656,6 +676,7 @@ static void test3_func(abts_case *tc, void *data) ABTS_INT_EQUAL(tc, OGS_OK, rv); test_ue->enb_ue_s1ap_id = enb_ue_s1ap_id; +#endif /* Receive Authentication Request */ recvbuf = testenb_s1ap_read(s1ap); @@ -684,6 +705,23 @@ static void test3_func(abts_case *tc, void *data) rv = testenb_s1ap_send(s1ap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); +#if SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_PROTECTED + /* Receive OLD UE Context Release Command */ + enb_ue_s1ap_id = test_ue->enb_ue_s1ap_id; + + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send OLD UE Context Release Complete */ + sendbuf = test_s1ap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + test_ue->enb_ue_s1ap_id = enb_ue_s1ap_id; +#endif + /* Receive Initial Context Setup Request + * Attach Accept + * Activate Default Bearer Context Request */ diff --git a/tests/common/context.c b/tests/common/context.c index 68b40d41a..a4954e252 100644 --- a/tests/common/context.c +++ b/tests/common/context.c @@ -327,7 +327,7 @@ int test_context_parse_config(void) ogs_5gs_tai1_list_t *list1 = NULL; ogs_5gs_tai2_list_t *list2 = NULL; - ogs_assert(self.num_of_nr_served_tai <= + ogs_assert(self.num_of_nr_served_tai < OGS_MAX_NUM_OF_SUPPORTED_TA); list0 = &self.nr_served_tai[self.num_of_nr_served_tai].list0; @@ -512,7 +512,7 @@ int test_context_parse_config(void) ogs_yaml_iter_recurse(&amf_iter, &plmn_support_array); do { const char *mnc = NULL, *mcc = NULL; - ogs_assert(self.num_of_plmn_support <= + ogs_assert(self.num_of_plmn_support < OGS_MAX_NUM_OF_PLMN); if (ogs_yaml_iter_type(&plmn_support_array) == @@ -570,7 +570,7 @@ int test_context_parse_config(void) ogs_assert( self.plmn_support[ self.num_of_plmn_support]. - num_of_s_nssai <= + num_of_s_nssai < OGS_MAX_NUM_OF_SLICE_SUPPORT); s_nssai = &self.plmn_support[ self.num_of_plmn_support].s_nssai[ @@ -807,7 +807,7 @@ int test_context_parse_config(void) ogs_eps_tai1_list_t *list1 = NULL; ogs_eps_tai2_list_t *list2 = NULL; - ogs_assert(self.num_of_e_served_tai <= + ogs_assert(self.num_of_e_served_tai < OGS_MAX_NUM_OF_SUPPORTED_TA); list0 = &self.e_served_tai[self.num_of_e_served_tai].list0; list1 = &self.e_served_tai[self.num_of_e_served_tai].list1; diff --git a/tests/common/context.h b/tests/common/context.h index 46b65ae04..d92466d07 100644 --- a/tests/common/context.h +++ b/tests/common/context.h @@ -65,7 +65,7 @@ typedef struct test_context_s { ogs_list_t gtpc_list; /* SMF GTPC Client List */ /* 5G PLMN Support */ - uint8_t num_of_plmn_support; + int num_of_plmn_support; struct { ogs_plmn_id_t plmn_id; int num_of_s_nssai; @@ -73,7 +73,7 @@ typedef struct test_context_s { } plmn_support[OGS_MAX_NUM_OF_PLMN]; /* Served EPC TAI */ - uint8_t num_of_e_served_tai; + int num_of_e_served_tai; struct { ogs_eps_tai0_list_t list0; ogs_eps_tai1_list_t list1; @@ -83,7 +83,7 @@ typedef struct test_context_s { ogs_eps_tai_t e_tai; /* Served 5GC TAI */ - uint8_t num_of_nr_served_tai; + int num_of_nr_served_tai; struct { ogs_5gs_tai0_list_t list0; ogs_5gs_tai1_list_t list1; diff --git a/tests/common/test-common.h b/tests/common/test-common.h index 7e443ad85..8b87b5462 100644 --- a/tests/common/test-common.h +++ b/tests/common/test-common.h @@ -74,6 +74,9 @@ extern "C" { #undef OGS_LOG_DOMAIN #define OGS_LOG_DOMAIN 1 +#define SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_UNPROTECTED 0 +#define SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_PROTECTED 1 + #undef OGS_TEST_INSIDE #ifdef __cplusplus diff --git a/tests/registration/auth-test.c b/tests/registration/auth-test.c index 72adc7715..b131d42dd 100644 --- a/tests/registration/auth-test.c +++ b/tests/registration/auth-test.c @@ -146,6 +146,7 @@ static void test1_func(abts_case *tc, void *data) rv = testgnb_ngap_send(ngap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); +#if SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_UNPROTECTED /* OLD Receive UEContextReleaseCommand */ recvbuf = testgnb_ngap_read(ngap); ABTS_PTR_NOTNULL(tc, recvbuf); @@ -159,6 +160,7 @@ static void test1_func(abts_case *tc, void *data) ABTS_PTR_NOTNULL(tc, sendbuf); rv = testgnb_ngap_send(ngap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); +#endif /* Receive Authentication request */ recvbuf = testgnb_ngap_read(ngap); diff --git a/tests/registration/ue-context-test.c b/tests/registration/ue-context-test.c index 1168ada19..e20a112e0 100644 --- a/tests/registration/ue-context-test.c +++ b/tests/registration/ue-context-test.c @@ -317,6 +317,7 @@ static void test2_func(abts_case *tc, void *data) rv = testgnb_ngap_send(ngap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); +#if SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_UNPROTECTED /* OLD Receive UEContextReleaseCommand */ recvbuf = testgnb_ngap_read(ngap); ABTS_PTR_NOTNULL(tc, recvbuf); @@ -330,6 +331,7 @@ static void test2_func(abts_case *tc, void *data) ABTS_PTR_NOTNULL(tc, sendbuf); rv = testgnb_ngap_send(ngap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); +#endif /* Receive Authentication request */ recvbuf = testgnb_ngap_read(ngap); @@ -357,6 +359,22 @@ static void test2_func(abts_case *tc, void *data) rv = testgnb_ngap_send(ngap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); +#if SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_PROTECTED + /* OLD Receive UEContextReleaseCommand */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_UEContextRelease, + test_ue->ngap_procedure_code); + + /* Send OLD UE Context Release Complete */ + sendbuf = testngap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); +#endif + /* Receive InitialContextSetupRequest + * Registration accept */ recvbuf = testgnb_ngap_read(ngap);