[AMF/MME] Fix security context restoration and state transition cleanup (#3756)

- Backup sensitive security context fields (e.g. xres, kasme, rand, autn,
  keys, counters) when transitioning from REGISTERED state.
- Set the can_restore_security_context flag in common_register_state()
  based on whether the transition originates from a REGISTERED or
  de-registered state.
- In emm_state_authentication(), restore the security context and revert
  to the REGISTERED state on authentication failure only if restoration
  is allowed; otherwise, transition to an exception state.
- Remove the redundant unconditional state transition in the cleanup block
  to prevent overriding a valid restoration.
This commit is contained in:
Sukchan Lee
2025-03-16 11:54:45 +09:00
parent e3dd98cd29
commit 106a9accd4
11 changed files with 858 additions and 146 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2019-2023 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@@ -2653,6 +2653,62 @@ uint8_t amf_selected_enc_algorithm(amf_ue_t *amf_ue)
return 0;
}
/* Backup the sensitive security context fields from the UE context */
void amf_backup_security_context(amf_ue_t *amf_ue,
amf_security_context_t *backup)
{
ogs_assert(amf_ue);
ogs_assert(backup);
memcpy(&backup->ue_security_capability, &amf_ue->ue_security_capability,
sizeof(backup->ue_security_capability));
memcpy(&backup->ue_network_capability, &amf_ue->ue_network_capability,
sizeof(backup->ue_network_capability));
memcpy(backup->rand, amf_ue->rand, OGS_RAND_LEN);
memcpy(backup->autn, amf_ue->autn, OGS_AUTN_LEN);
memcpy(backup->xres_star, amf_ue->xres_star, OGS_MAX_RES_LEN);
memcpy(backup->abba, amf_ue->abba, OGS_NAS_MAX_ABBA_LEN);
backup->abba_len = amf_ue->abba_len;
memcpy(backup->hxres_star, amf_ue->hxres_star, OGS_MAX_RES_LEN);
memcpy(backup->kamf, amf_ue->kamf, OGS_SHA256_DIGEST_SIZE);
memcpy(backup->knas_int, amf_ue->knas_int, OGS_SHA256_DIGEST_SIZE/2);
memcpy(backup->knas_enc, amf_ue->knas_enc, OGS_SHA256_DIGEST_SIZE/2);
backup->dl_count = amf_ue->dl_count;
backup->ul_count = amf_ue->ul_count.i32;
memcpy(backup->kgnb, amf_ue->kgnb, OGS_SHA256_DIGEST_SIZE);
memcpy(backup->nh, amf_ue->nh, OGS_SHA256_DIGEST_SIZE);
backup->selected_enc_algorithm = amf_ue->selected_enc_algorithm;
backup->selected_int_algorithm = amf_ue->selected_int_algorithm;
}
/* Restore the sensitive security context fields into the UE context */
void amf_restore_security_context(amf_ue_t *amf_ue,
const amf_security_context_t *backup)
{
ogs_assert(amf_ue);
ogs_assert(backup);
memcpy(&amf_ue->ue_security_capability, &backup->ue_security_capability,
sizeof(amf_ue->ue_security_capability));
memcpy(&amf_ue->ue_network_capability, &backup->ue_network_capability,
sizeof(amf_ue->ue_network_capability));
memcpy(amf_ue->rand, backup->rand, OGS_RAND_LEN);
memcpy(amf_ue->autn, backup->autn, OGS_AUTN_LEN);
memcpy(amf_ue->xres_star, backup->xres_star, OGS_MAX_RES_LEN);
memcpy(amf_ue->abba, backup->abba, OGS_NAS_MAX_ABBA_LEN);
amf_ue->abba_len = backup->abba_len;
memcpy(amf_ue->hxres_star, backup->hxres_star, OGS_MAX_RES_LEN);
memcpy(amf_ue->kamf, backup->kamf, OGS_SHA256_DIGEST_SIZE);
memcpy(amf_ue->knas_int, backup->knas_int, OGS_SHA256_DIGEST_SIZE/2);
memcpy(amf_ue->knas_enc, backup->knas_enc, OGS_SHA256_DIGEST_SIZE/2);
amf_ue->dl_count = backup->dl_count;
amf_ue->ul_count.i32 = backup->ul_count;
memcpy(amf_ue->kgnb, backup->kgnb, OGS_SHA256_DIGEST_SIZE);
memcpy(amf_ue->nh, backup->nh, OGS_SHA256_DIGEST_SIZE);
amf_ue->selected_enc_algorithm = backup->selected_enc_algorithm;
amf_ue->selected_int_algorithm = backup->selected_int_algorithm;
}
void amf_clear_subscribed_info(amf_ue_t *amf_ue)
{
int i, j;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2019-2023 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@@ -237,6 +237,67 @@ struct ran_ue_s {
ogs_pool_id_t amf_ue_id;
};
typedef struct amf_security_context_s {
/* UE security capability info: supported security features. */
ogs_nas_ue_security_capability_t ue_security_capability;
/* UE network capability info: supported network features. */
ogs_nas_ue_network_capability_t ue_network_capability;
/* Random challenge value */
uint8_t rand[OGS_RAND_LEN];
/* Authentication token */
uint8_t autn[OGS_AUTN_LEN];
/* Expected auth response. */
uint8_t xres_star[OGS_MAX_RES_LEN];
/* NAS backoff parameter value. */
uint8_t abba[OGS_NAS_MAX_ABBA_LEN];
uint8_t abba_len;
/* Hash of XRES*. */
uint8_t hxres_star[OGS_MAX_RES_LEN];
/* Key for AMF derived from NAS key. */
uint8_t kamf[OGS_SHA256_DIGEST_SIZE];
/* Integrity and ciphering keys */
uint8_t knas_int[OGS_SHA256_DIGEST_SIZE/2];
uint8_t knas_enc[OGS_SHA256_DIGEST_SIZE/2];
/* Downlink counter */
uint32_t dl_count;
/* Uplink counter (24-bit stored in uint32_t) */
uint32_t ul_count;
/* gNB key derived from kasme */
uint8_t kgnb[OGS_SHA256_DIGEST_SIZE];
/*
* Next Hop Channing Counter
*
* Note that the "nhcc" field is not included in the backup
* because it is a transient counter used only during next-hop key
* derivation. In our design, only the persistent keying material
* and related values that are required to recreate the security context
* are backed up. The nhcc value is recalculated or updated dynamically
* when the next hop key is derived (e.g. via ogs_kdf_nh_enb()),
* so it is not necessary to store it in the backup.
*
* If there is a requirement to preserve the exact nhcc value across state
* transitions, you could add it to the backup structure, but typically
* it is treated as a computed, temporary value that can be reinitialized
* safely without compromising the security context.
* struct {
* ED2(uint8_t nhcc_spare:5;,
* uint8_t nhcc:3;)
* };
*/
/* Next hop key */
uint8_t nh[OGS_SHA256_DIGEST_SIZE];
/* Selected algorithms (set by UDM/subscription) */
uint8_t selected_enc_algorithm;
uint8_t selected_int_algorithm;
} amf_security_context_t;
struct amf_ue_s {
ogs_sbi_object_t sbi;
ogs_pool_id_t id;
@@ -367,6 +428,12 @@ struct amf_ue_s {
int security_context_available;
int mac_failed;
/* flag: 1 = allow restoration of security context, 0 = disallow */
bool can_restore_security_context;
/* Backup of security context fields */
amf_security_context_t sec_backup;
/* Security Context */
ogs_nas_ue_security_capability_t ue_security_capability;
ogs_nas_ue_network_capability_t ue_network_capability;
@@ -391,20 +458,29 @@ struct amf_ue_s {
char *resource_uri;
ogs_sbi_client_t *client;
} confirmation_for_5g_aka;
/* Random challenge value */
uint8_t rand[OGS_RAND_LEN];
/* Authentication token */
uint8_t autn[OGS_AUTN_LEN];
/* Expected auth response. */
uint8_t xres_star[OGS_MAX_RES_LEN];
/* NAS backoff parameter value. */
uint8_t abba[OGS_NAS_MAX_ABBA_LEN];
uint8_t abba_len;
/* Hash of XRES*. */
uint8_t hxres_star[OGS_MAX_RES_LEN];
/* Key for AMF derived from NAS key. */
uint8_t kamf[OGS_SHA256_DIGEST_SIZE];
OpenAPI_auth_result_e auth_result;
/* Integrity and ciphering keys */
uint8_t knas_int[OGS_SHA256_DIGEST_SIZE/2];
uint8_t knas_enc[OGS_SHA256_DIGEST_SIZE/2];
/* Downlink counter */
uint32_t dl_count;
/* Uplink counter (24-bit stored in uint32_t) */
union {
struct {
ED3(uint8_t spare;,
@@ -413,14 +489,17 @@ struct amf_ue_s {
} __attribute__ ((packed));
uint32_t i32;
} ul_count;
/* gNB key derived from kasme */
uint8_t kgnb[OGS_SHA256_DIGEST_SIZE];
struct {
ED2(uint8_t nhcc_spare:5;,
uint8_t nhcc:3;) /* Next Hop Channing Counter */
};
/* Next hop key */
uint8_t nh[OGS_SHA256_DIGEST_SIZE]; /* NH Security Key */
/* Selected algorithms (set by UDM/subscription) */
/* defined in 'lib/nas/common/types.h'
* #define OGS_NAS_SECURITY_ALGORITHMS_NEA0 0
* #define OGS_NAS_SECURITY_ALGORITHMS_128_NEA1 1
@@ -1006,6 +1085,11 @@ int amf_m_tmsi_free(amf_m_tmsi_t *tmsi);
uint8_t amf_selected_int_algorithm(amf_ue_t *amf_ue);
uint8_t amf_selected_enc_algorithm(amf_ue_t *amf_ue);
void amf_backup_security_context(
amf_ue_t *amf_ue, amf_security_context_t *backup);
void amf_restore_security_context(
amf_ue_t *amf_ue, const amf_security_context_t *backup);
void amf_clear_subscribed_info(amf_ue_t *amf_ue);
bool amf_update_allowed_nssai(amf_ue_t *amf_ue);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2019,2020 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@@ -1279,6 +1279,16 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e,
ogs_assert(amf_ue);
}
/* If transition is from REGISTERED, allow restoration */
if (state == GMM_COMMON_STATE_REGISTERED) {
amf_ue->can_restore_security_context = 1;
amf_backup_security_context(amf_ue, &amf_ue->sec_backup);
} else if (state == GMM_COMMON_STATE_DEREGISTERED) {
/* Transition from de-registered: do not restore */
amf_ue->can_restore_security_context = 0;
} else
ogs_assert_if_reached();
switch (e->h.id) {
case AMF_EVENT_5GMM_MESSAGE:
nas_message = e->nas.message;
@@ -1726,10 +1736,11 @@ void gmm_state_authentication(ogs_fsm_t *s, amf_event_t *e)
amf_ue, &nas_message->gmm.authentication_response);
if (rv != OGS_OK) {
ogs_error("gmm_handle_authentication_response() failed");
r = nas_5gs_send_authentication_reject(amf_ue);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
OGS_FSM_TRAN(&amf_ue->sm, &gmm_state_exception);
goto cleanup;
}
break;
@@ -1740,7 +1751,7 @@ void gmm_state_authentication(ogs_fsm_t *s, amf_event_t *e)
authentication_failure_parameter;
ogs_assert(authentication_failure_parameter);
ogs_debug("[%s] Authentication failure [%d]", amf_ue->suci,
ogs_warn("[%s] Authentication failure [%d]", amf_ue->suci,
authentication_failure->gmm_cause);
amf_metrics_inst_by_cause_add(authentication_failure->gmm_cause,
@@ -1793,9 +1804,8 @@ void gmm_state_authentication(ogs_fsm_t *s, amf_event_t *e)
r = nas_5gs_send_authentication_reject(amf_ue);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
OGS_FSM_TRAN(&amf_ue->sm, &gmm_state_exception);
goto cleanup;
break;
case OGS_NAS_5GS_REGISTRATION_REQUEST:
ogs_warn("Registration request");
gmm_cause = gmm_handle_registration_request(
@@ -1807,8 +1817,7 @@ void gmm_state_authentication(ogs_fsm_t *s, amf_event_t *e)
r = nas_5gs_send_registration_reject(ran_ue, amf_ue, gmm_cause);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
OGS_FSM_TRAN(s, gmm_state_exception);
break;
goto cleanup;
}
r = amf_ue_sbi_discover_and_send(
@@ -1846,7 +1855,7 @@ void gmm_state_authentication(ogs_fsm_t *s, amf_event_t *e)
r = nas_5gs_send_authentication_reject(amf_ue);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
OGS_FSM_TRAN(&amf_ue->sm, &gmm_state_exception);
goto cleanup;
} else {
amf_ue->t3560.retry_count++;
r = nas_5gs_send_authentication_request(amf_ue);
@@ -1879,12 +1888,12 @@ void gmm_state_authentication(ogs_fsm_t *s, amf_event_t *e)
ogs_error("[%s] HTTP response error [%d]",
amf_ue->suci, sbi_message->res_status);
}
r = nas_5gs_send_gmm_reject_from_sbi(
amf_ue, sbi_message->res_status);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
OGS_FSM_TRAN(&amf_ue->sm, &gmm_state_exception);
break;
goto cleanup;
}
SWITCH(sbi_message->h.method)
@@ -1897,7 +1906,7 @@ void gmm_state_authentication(ogs_fsm_t *s, amf_event_t *e)
r = nas_5gs_send_authentication_reject(amf_ue);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
OGS_FSM_TRAN(&amf_ue->sm, &gmm_state_exception);
goto cleanup;
}
break;
CASE(OGS_SBI_HTTP_METHOD_PUT)
@@ -1909,7 +1918,7 @@ void gmm_state_authentication(ogs_fsm_t *s, amf_event_t *e)
r = nas_5gs_send_authentication_reject(amf_ue);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
OGS_FSM_TRAN(&amf_ue->sm, &gmm_state_exception);
goto cleanup;
} else {
amf_ue->selected_int_algorithm =
amf_selected_int_algorithm(amf_ue);
@@ -1923,8 +1932,7 @@ void gmm_state_authentication(ogs_fsm_t *s, amf_event_t *e)
"bypassed with NIA0",
amf_ue->selected_enc_algorithm,
amf_ue->selected_int_algorithm);
OGS_FSM_TRAN(&amf_ue->sm, &gmm_state_exception);
break;
goto cleanup;
}
OGS_FSM_TRAN(&amf_ue->sm, &gmm_state_security_mode);
@@ -2035,6 +2043,26 @@ void gmm_state_authentication(ogs_fsm_t *s, amf_event_t *e)
ogs_error("Unknown event[%s]", amf_event_get_name(e));
break;
}
return;
cleanup:
if (amf_ue->can_restore_security_context) {
/* If allowed, restore the security context */
amf_restore_security_context(amf_ue, &amf_ue->sec_backup);
amf_ue->security_context_available = 1;
amf_ue->mac_failed = 0;
OGS_FSM_TRAN(s, &gmm_state_registered);
ogs_warn("[%s] Auth failure in registered trans; "
"restoring context and going to REGISTERED.", amf_ue->supi);
} else {
/* Do not restore; transition to exception state */
OGS_FSM_TRAN(s, &gmm_state_exception);
ogs_warn("[%s] Auth failure in de-registered trans; "
"no context restoration.", amf_ue->supi);
}
}
void gmm_state_security_mode(ogs_fsm_t *s, amf_event_t *e)

View File

@@ -379,6 +379,50 @@ int emm_handle_attach_complete(
return r;
}
int emm_handle_authentication_response(
enb_ue_t *enb_ue, mme_ue_t *mme_ue,
ogs_nas_eps_authentication_response_t *authentication_response)
{
ogs_nas_authentication_response_parameter_t
*authentication_response_parameter =
&authentication_response->authentication_response_parameter;
ogs_assert(authentication_response);
ogs_assert(mme_ue);
ogs_assert(enb_ue);
ogs_debug("Authentication response");
ogs_debug(" IMSI[%s]", mme_ue->imsi_bcd);
CLEAR_MME_UE_TIMER(mme_ue->t3460);
if (authentication_response_parameter->length == 0 ||
memcmp(authentication_response_parameter->res, mme_ue->xres,
authentication_response_parameter->length) != 0) {
ogs_log_hexdump(OGS_LOG_WARN,
authentication_response_parameter->res,
authentication_response_parameter->length);
ogs_log_hexdump(OGS_LOG_WARN,
mme_ue->xres, OGS_MAX_RES_LEN);
return OGS_ERROR;
} else {
mme_ue->selected_int_algorithm = mme_selected_int_algorithm(mme_ue);
mme_ue->selected_enc_algorithm = mme_selected_enc_algorithm(mme_ue);
if (mme_ue->selected_int_algorithm ==
OGS_NAS_SECURITY_ALGORITHMS_EIA0) {
ogs_error("Encrypt[0x%x] can be skipped with EEA0, "
"but Integrity[0x%x] cannot be bypassed with EIA0",
mme_ue->selected_enc_algorithm,
mme_ue->selected_int_algorithm);
return OGS_ERROR;
}
}
return OGS_OK;
}
int emm_handle_identity_response(
enb_ue_t *enb_ue, mme_ue_t *mme_ue,
ogs_nas_eps_identity_response_t *identity_response)

View File

@@ -33,6 +33,10 @@ int emm_handle_attach_complete(
enb_ue_t *enb_ue, mme_ue_t *mme_ue,
ogs_nas_eps_attach_complete_t *attach_complete);
int emm_handle_authentication_response(
enb_ue_t *enb_ue, mme_ue_t *mme_ue,
ogs_nas_eps_authentication_response_t *authentication_response);
int emm_handle_identity_response(
enb_ue_t *enb_ue, mme_ue_t *mme_ue,
ogs_nas_eps_identity_response_t *identity_response);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2019-2024 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@@ -304,6 +304,15 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e,
mme_ue = mme_ue_find_by_id(e->mme_ue_id);
ogs_assert(mme_ue);
/* If transition is from REGISTERED, allow restoration */
if (state == EMM_COMMON_STATE_REGISTERED) {
mme_ue->can_restore_security_context = 1;
mme_backup_security_context(mme_ue, &mme_ue->sec_backup);
} else if (state == EMM_COMMON_STATE_DEREGISTERED) {
/* Transition from de-registered: do not restore */
mme_ue->can_restore_security_context = 0;
}
switch (e->id) {
case MME_EVENT_EMM_MESSAGE:
message = e->nas_message;
@@ -1060,6 +1069,8 @@ void emm_state_authentication(ogs_fsm_t *s, mme_event_t *e)
enb_ue_t *enb_ue = NULL;
ogs_nas_eps_message_t *message = NULL;
ogs_nas_eps_authentication_failure_t *authentication_failure = NULL;
ogs_assert(s);
ogs_assert(e);
@@ -1082,64 +1093,27 @@ void emm_state_authentication(ogs_fsm_t *s, mme_event_t *e)
switch (message->emm.h.message_type) {
case OGS_NAS_EPS_AUTHENTICATION_RESPONSE:
{
ogs_nas_eps_authentication_response_t *authentication_response =
&message->emm.authentication_response;
ogs_nas_authentication_response_parameter_t
*authentication_response_parameter =
&authentication_response->
authentication_response_parameter;
ogs_debug("Authentication response");
ogs_debug(" IMSI[%s]", mme_ue->imsi_bcd);
CLEAR_MME_UE_TIMER(mme_ue->t3460);
if (authentication_response_parameter->length == 0 ||
memcmp(authentication_response_parameter->res,
mme_ue->xres,
authentication_response_parameter->length) != 0) {
ogs_log_hexdump(OGS_LOG_WARN,
authentication_response_parameter->res,
authentication_response_parameter->length);
ogs_log_hexdump(OGS_LOG_WARN,
mme_ue->xres, OGS_MAX_RES_LEN);
rv = emm_handle_authentication_response(enb_ue, mme_ue,
&message->emm.authentication_response);
if (rv != OGS_OK) {
ogs_error("emm_handle_authentication_response() failed");
r = nas_eps_send_authentication_reject(mme_ue);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
OGS_FSM_TRAN(&mme_ue->sm, &emm_state_exception);
} else {
mme_ue->selected_int_algorithm =
mme_selected_int_algorithm(mme_ue);
mme_ue->selected_enc_algorithm =
mme_selected_enc_algorithm(mme_ue);
if (mme_ue->selected_int_algorithm ==
OGS_NAS_SECURITY_ALGORITHMS_EIA0) {
ogs_error("Encrypt[0x%x] can be skipped with EEA0, "
"but Integrity[0x%x] cannot be bypassed with EIA0",
mme_ue->selected_enc_algorithm,
mme_ue->selected_int_algorithm);
OGS_FSM_TRAN(&mme_ue->sm, &emm_state_exception);
break;
}
OGS_FSM_TRAN(&mme_ue->sm, &emm_state_security_mode);
goto cleanup;
}
OGS_FSM_TRAN(&mme_ue->sm, &emm_state_security_mode);
break;
}
case OGS_NAS_EPS_AUTHENTICATION_FAILURE:
{
ogs_nas_eps_authentication_failure_t *authentication_failure =
&message->emm.authentication_failure;
authentication_failure = &message->emm.authentication_failure;
ogs_nas_authentication_failure_parameter_t
*authentication_failure_parameter =
&authentication_failure->
authentication_failure_parameter;
ogs_debug("Authentication failure");
ogs_debug(" IMSI[%s] OGS_NAS_EMM_CAUSE[%d]", mme_ue->imsi_bcd,
ogs_warn("Authentication failure");
ogs_warn(" IMSI[%s] OGS_NAS_EMM_CAUSE[%d]", mme_ue->imsi_bcd,
authentication_failure->emm_cause);
CLEAR_MME_UE_TIMER(mme_ue->t3460);
@@ -1167,18 +1141,15 @@ void emm_state_authentication(ogs_fsm_t *s, mme_event_t *e)
r = nas_eps_send_authentication_reject(mme_ue);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
OGS_FSM_TRAN(&mme_ue->sm, &emm_state_exception);
goto cleanup;
break;
}
case OGS_NAS_EPS_ATTACH_REQUEST:
ogs_warn("[%s] Attach request", mme_ue->imsi_bcd);
rv = emm_handle_attach_request(
enb_ue, mme_ue, &message->emm.attach_request, e->pkbuf);
if (rv != OGS_OK) {
ogs_error("emm_handle_attach_request() failed");
OGS_FSM_TRAN(s, emm_state_exception);
break;
goto cleanup;
}
mme_s6a_send_air(enb_ue, mme_ue, NULL);
@@ -1194,8 +1165,7 @@ void emm_state_authentication(ogs_fsm_t *s, mme_event_t *e)
enb_ue, mme_ue, &message->emm.detach_request_from_ue);
if (rv != OGS_OK) {
ogs_error("emm_handle_detach_request() failed");
OGS_FSM_TRAN(s, emm_state_exception);
break;
goto cleanup;
}
if (!MME_UE_HAVE_IMSI(mme_ue)) {
@@ -1203,8 +1173,7 @@ void emm_state_authentication(ogs_fsm_t *s, mme_event_t *e)
ogs_assert(OGS_OK ==
nas_eps_send_service_reject(enb_ue, mme_ue,
OGS_NAS_EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK));
OGS_FSM_TRAN(s, &emm_state_exception);
break;
goto cleanup;
}
if (!SECURITY_CONTEXT_IS_VALID(mme_ue)) {
@@ -1212,8 +1181,7 @@ void emm_state_authentication(ogs_fsm_t *s, mme_event_t *e)
ogs_assert(OGS_OK ==
nas_eps_send_service_reject(enb_ue, mme_ue,
OGS_NAS_EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK));
OGS_FSM_TRAN(s, &emm_state_exception);
break;
goto cleanup;
}
/*
@@ -1243,11 +1211,10 @@ void emm_state_authentication(ogs_fsm_t *s, mme_event_t *e)
mme_timer_cfg(MME_TIMER_T3460)->max_count) {
ogs_warn("Retransmission of IMSI[%s] failed. "
"Stop retransmission", mme_ue->imsi_bcd);
OGS_FSM_TRAN(&mme_ue->sm, &emm_state_exception);
r = nas_eps_send_authentication_reject(mme_ue);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
goto cleanup;
} else {
mme_ue->t3460.retry_count++;
r = nas_eps_send_authentication_request(mme_ue);
@@ -1265,6 +1232,27 @@ void emm_state_authentication(ogs_fsm_t *s, mme_event_t *e)
ogs_error("Unknown event[%s]", mme_event_get_name(e));
break;
}
return;
cleanup:
if (mme_ue->can_restore_security_context) {
/* If allowed, restore the security context */
mme_restore_security_context(mme_ue, &mme_ue->sec_backup);
mme_ue->security_context_available = 1;
mme_ue->mac_failed = 0;
OGS_FSM_TRAN(s, &emm_state_registered);
ogs_warn("[%s] Auth failure in registered trans; "
"restoring context and going to REGISTERED.",
mme_ue->imsi_bcd);
} else {
/* Do not restore; transition to exception state */
OGS_FSM_TRAN(s, &emm_state_exception);
ogs_warn("[%s] Auth failure in de-registered trans; "
"no context restoration.", mme_ue->imsi_bcd);
}
}
void emm_state_security_mode(ogs_fsm_t *s, mme_event_t *e)

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2019-2023 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@@ -5030,6 +5030,82 @@ uint8_t mme_selected_enc_algorithm(mme_ue_t *mme_ue)
return 0;
}
/* Backup the sensitive security context fields from the UE context */
void mme_backup_security_context(
mme_ue_t *mme_ue, mme_security_context_t *backup)
{
ogs_assert(mme_ue);
ogs_assert(backup);
memcpy(&backup->ue_network_capability,
&mme_ue->ue_network_capability,
sizeof(backup->ue_network_capability));
memcpy(&backup->ms_network_capability,
&mme_ue->ms_network_capability,
sizeof(backup->ms_network_capability));
memcpy(&backup->ue_additional_security_capability,
&mme_ue->ue_additional_security_capability,
sizeof(backup->ue_additional_security_capability));
memcpy(backup->xres, mme_ue->xres, OGS_MAX_RES_LEN);
backup->xres_len = mme_ue->xres_len;
memcpy(backup->kasme, mme_ue->kasme, OGS_SHA256_DIGEST_SIZE);
memcpy(backup->rand, mme_ue->rand, OGS_RAND_LEN);
memcpy(backup->autn, mme_ue->autn, OGS_AUTN_LEN);
memcpy(backup->knas_int, mme_ue->knas_int,
OGS_SHA256_DIGEST_SIZE / 2);
memcpy(backup->knas_enc, mme_ue->knas_enc,
OGS_SHA256_DIGEST_SIZE / 2);
backup->dl_count = mme_ue->dl_count;
backup->ul_count = mme_ue->ul_count.i32;
memcpy(backup->kenb, mme_ue->kenb, OGS_SHA256_DIGEST_SIZE);
memcpy(backup->hash_mme, mme_ue->hash_mme, OGS_HASH_MME_LEN);
backup->nonceue = mme_ue->nonceue;
backup->noncemme = mme_ue->noncemme;
backup->gprs_ciphering_key_sequence_number =
mme_ue->gprs_ciphering_key_sequence_number;
memcpy(backup->nh, mme_ue->nh, OGS_SHA256_DIGEST_SIZE);
backup->selected_enc_algorithm = mme_ue->selected_enc_algorithm;
backup->selected_int_algorithm = mme_ue->selected_int_algorithm;
}
/* Restore the sensitive security context fields into the UE context */
void mme_restore_security_context(
mme_ue_t *mme_ue, const mme_security_context_t *backup)
{
ogs_assert(mme_ue);
ogs_assert(backup);
memcpy(&mme_ue->ue_network_capability,
&backup->ue_network_capability,
sizeof(mme_ue->ue_network_capability));
memcpy(&mme_ue->ms_network_capability,
&backup->ms_network_capability,
sizeof(mme_ue->ms_network_capability));
memcpy(&mme_ue->ue_additional_security_capability,
&backup->ue_additional_security_capability,
sizeof(mme_ue->ue_additional_security_capability));
memcpy(mme_ue->xres, backup->xres, OGS_MAX_RES_LEN);
mme_ue->xres_len = backup->xres_len;
memcpy(mme_ue->kasme, backup->kasme, OGS_SHA256_DIGEST_SIZE);
memcpy(mme_ue->rand, backup->rand, OGS_RAND_LEN);
memcpy(mme_ue->autn, backup->autn, OGS_AUTN_LEN);
memcpy(mme_ue->knas_int, backup->knas_int,
OGS_SHA256_DIGEST_SIZE / 2);
memcpy(mme_ue->knas_enc, backup->knas_enc,
OGS_SHA256_DIGEST_SIZE / 2);
mme_ue->dl_count = backup->dl_count;
mme_ue->ul_count.i32 = backup->ul_count;
memcpy(mme_ue->kenb, backup->kenb, OGS_SHA256_DIGEST_SIZE);
memcpy(mme_ue->hash_mme, backup->hash_mme, OGS_HASH_MME_LEN);
mme_ue->nonceue = backup->nonceue;
mme_ue->noncemme = backup->noncemme;
mme_ue->gprs_ciphering_key_sequence_number =
backup->gprs_ciphering_key_sequence_number;
memcpy(mme_ue->nh, backup->nh, OGS_SHA256_DIGEST_SIZE);
mme_ue->selected_enc_algorithm = backup->selected_enc_algorithm;
mme_ue->selected_int_algorithm = backup->selected_int_algorithm;
}
static void stats_add_enb_ue(void)
{
mme_metrics_inst_global_inc(MME_METR_GLOB_GAUGE_ENB_UE);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2019-2023 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@@ -342,6 +342,69 @@ struct sgw_ue_s {
ogs_pool_id_t mme_ue_id;
};
typedef struct mme_security_context_s {
/* UE network capability info: supported network features. */
ogs_nas_ue_network_capability_t ue_network_capability;
/* MS network capability info: supported network features. */
ogs_nas_ms_network_capability_t ms_network_capability;
/* UE additional security capability: extra security features. */
ogs_nas_ue_additional_security_capability_t
ue_additional_security_capability;
/* Expected response and its length */
uint8_t xres[OGS_MAX_RES_LEN];
uint8_t xres_len;
/* Derived key from HSS */
uint8_t kasme[OGS_SHA256_DIGEST_SIZE];
/* Random challenge value */
uint8_t rand[OGS_RAND_LEN];
/* Authentication token */
uint8_t autn[OGS_AUTN_LEN];
/* Integrity and ciphering keys */
uint8_t knas_int[OGS_SHA256_DIGEST_SIZE/2];
uint8_t knas_enc[OGS_SHA256_DIGEST_SIZE/2];
/* Downlink counter */
uint32_t dl_count;
/* Uplink counter (24-bit stored in uint32_t) */
uint32_t ul_count;
/* eNB key derived from kasme */
uint8_t kenb[OGS_SHA256_DIGEST_SIZE];
/* Hash used for NAS message integrity */
uint8_t hash_mme[OGS_HASH_MME_LEN];
/* Nonces for resynchronization */
uint32_t nonceue;
uint32_t noncemme;
/* GPRS ciphering key sequence number */
uint8_t gprs_ciphering_key_sequence_number;
/*
* Next Hop Channing Counter
*
* Note that the "nhcc" field is not included in the backup
* because it is a transient counter used only during next-hop key
* derivation. In our design, only the persistent keying material
* and related values that are required to recreate the security context
* are backed up. The nhcc value is recalculated or updated dynamically
* when the next hop key is derived (e.g. via ogs_kdf_nh_enb()),
* so it is not necessary to store it in the backup.
*
* If there is a requirement to preserve the exact nhcc value across state
* transitions, you could add it to the backup structure, but typically
* it is treated as a computed, temporary value that can be reinitialized
* safely without compromising the security context.
* struct {
* ED2(uint8_t nhcc_spare:5;,
* uint8_t nhcc:3;)
* };
*/
/* Next hop key */
uint8_t nh[OGS_SHA256_DIGEST_SIZE];
/* Selected algorithms (set by HSS/subscription) */
uint8_t selected_enc_algorithm;
uint8_t selected_int_algorithm;
} mme_security_context_t;
struct mme_ue_s {
ogs_lnode_t lnode;
ogs_pool_id_t id;
@@ -462,19 +525,32 @@ struct mme_ue_s {
int security_context_available;
int mac_failed;
/* flag: 1 = allow restoration of security context, 0 = disallow */
bool can_restore_security_context;
/* Backup of security context fields */
mme_security_context_t sec_backup;
/* Security Context */
ogs_nas_ue_network_capability_t ue_network_capability;
ogs_nas_ms_network_capability_t ms_network_capability;
ogs_nas_ue_additional_security_capability_t
ue_additional_security_capability;
/* Expected response and its length */
uint8_t xres[OGS_MAX_RES_LEN];
uint8_t xres_len;
/* Derived key from HSS */
uint8_t kasme[OGS_SHA256_DIGEST_SIZE];
/* Random challenge value */
uint8_t rand[OGS_RAND_LEN];
/* Authentication token */
uint8_t autn[OGS_AUTN_LEN];
/* Integrity and ciphering keys */
uint8_t knas_int[OGS_SHA256_DIGEST_SIZE/2];
uint8_t knas_enc[OGS_SHA256_DIGEST_SIZE/2];
/* Downlink counter */
uint32_t dl_count;
/* Uplink counter (24-bit stored in i32) */
union {
struct {
ED3(uint8_t spare;,
@@ -483,17 +559,23 @@ struct mme_ue_s {
} __attribute__ ((packed));
uint32_t i32;
} ul_count;
/* eNB key derived from kasme */
uint8_t kenb[OGS_SHA256_DIGEST_SIZE];
/* Hash used for NAS message integrity */
uint8_t hash_mme[OGS_HASH_MME_LEN];
/* Nonces for resynchronization */
uint32_t nonceue, noncemme;
/* GPRS ciphering key sequence number */
uint8_t gprs_ciphering_key_sequence_number;
struct {
ED2(uint8_t nhcc_spare:5;,
uint8_t nhcc:3;) /* Next Hop Channing Counter */
};
uint8_t nh[OGS_SHA256_DIGEST_SIZE]; /* NH Security Key */
/* Next hop key */
uint8_t nh[OGS_SHA256_DIGEST_SIZE];
/* Selected algorithms (set by HSS/subscription) */
/* defined in 'nas_ies.h'
* #define NAS_SECURITY_ALGORITHMS_EIA0 0
* #define NAS_SECURITY_ALGORITHMS_128_EEA1 1
@@ -1133,6 +1215,11 @@ void mme_ebi_pool_clear(mme_ue_t *mme_ue);
uint8_t mme_selected_int_algorithm(mme_ue_t *mme_ue);
uint8_t mme_selected_enc_algorithm(mme_ue_t *mme_ue);
void mme_backup_security_context(
mme_ue_t *mme_ue, mme_security_context_t *backup);
void mme_restore_security_context(
mme_ue_t *mme_ue, const mme_security_context_t *backup);
#ifdef __cplusplus
}
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@@ -345,11 +345,6 @@ static void test1_func(abts_case *tc, void *data)
ABTS_PTR_NOTNULL(tc, recvbuf);
tests1ap_recv(test_ue, recvbuf);
/* Receive UE Context Release Command */
recvbuf = testenb_s1ap_read(s1ap);
ABTS_PTR_NOTNULL(tc, recvbuf);
tests1ap_recv(test_ue, recvbuf);
/* Send Attach Request - No Integrity */
sess->pdn_connectivity_param.eit = 1;
sess->pdn_connectivity_param.pco = 1;
@@ -407,17 +402,6 @@ static void test1_func(abts_case *tc, void *data)
ABTS_PTR_NOTNULL(tc, recvbuf);
tests1ap_recv(test_ue, recvbuf);
/* Receive UE Context Release Command */
recvbuf = testenb_s1ap_read(s1ap);
ABTS_PTR_NOTNULL(tc, recvbuf);
tests1ap_recv(test_ue, recvbuf);
/* Send 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);
/*
* --- Immediately Re-attach Test ---
*
@@ -489,6 +473,30 @@ static void test1_func(abts_case *tc, void *data)
ABTS_PTR_NOTNULL(tc, recvbuf);
tests1ap_recv(test_ue, recvbuf);
/* Send Detach Request */
emmbuf = testemm_build_detach_request(test_ue, 1, true, false);
ABTS_PTR_NOTNULL(tc, emmbuf);
sendbuf = test_s1ap_build_initial_ue_message(
test_ue, emmbuf, S1AP_RRC_Establishment_Cause_mo_Signalling, true);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testenb_s1ap_send(s1ap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* 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;
/* Receive UE Context Release Command */
recvbuf = testenb_s1ap_read(s1ap);
ABTS_PTR_NOTNULL(tc, recvbuf);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2019,2020 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@@ -34,6 +34,7 @@ static void test1_func(abts_case *tc, void *data)
ogs_nas_5gs_mobile_identity_suci_t mobile_identity_suci;
test_ue_t *test_ue = NULL;
test_sess_t *sess = NULL;
test_bearer_t *qos_flow = NULL;
bson_t *doc = NULL;
@@ -72,7 +73,7 @@ static void test1_func(abts_case *tc, void *data)
ABTS_PTR_NOTNULL(tc, gtpu);
/* Send NG-Setup Reqeust */
sendbuf = testngap_build_ng_setup_request(0x4000, 27);
sendbuf = testngap_build_ng_setup_request(0x4000, 22);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
@@ -88,6 +89,237 @@ static void test1_func(abts_case *tc, void *data)
ABTS_INT_EQUAL(tc, OGS_OK, test_db_insert_ue(test_ue, doc));
/* Send Registration request */
test_ue->registration_request_param.guti = 1;
gmmbuf = testgmm_build_registration_request(test_ue, NULL, false, false);
ABTS_PTR_NOTNULL(tc, gmmbuf);
test_ue->registration_request_param.gmm_capability = 1;
test_ue->registration_request_param.s1_ue_network_capability = 1;
test_ue->registration_request_param.requested_nssai = 1;
test_ue->registration_request_param.last_visited_registered_tai = 1;
test_ue->registration_request_param.ue_usage_setting = 1;
nasbuf = testgmm_build_registration_request(test_ue, NULL, false, false);
ABTS_PTR_NOTNULL(tc, nasbuf);
sendbuf = testngap_build_initial_ue_message(test_ue, gmmbuf,
NGAP_RRCEstablishmentCause_mo_Signalling, false, true);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive Identity request */
recvbuf = testgnb_ngap_read(ngap);
ABTS_PTR_NOTNULL(tc, recvbuf);
testngap_recv(test_ue, recvbuf);
/* Send Identity response */
gmmbuf = testgmm_build_identity_response(test_ue);
ABTS_PTR_NOTNULL(tc, gmmbuf);
sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive Authentication request */
recvbuf = testgnb_ngap_read(ngap);
ABTS_PTR_NOTNULL(tc, recvbuf);
testngap_recv(test_ue, recvbuf);
/* Send Authentication response */
gmmbuf = testgmm_build_authentication_response(test_ue);
ABTS_PTR_NOTNULL(tc, gmmbuf);
sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive Security mode command */
recvbuf = testgnb_ngap_read(ngap);
ABTS_PTR_NOTNULL(tc, recvbuf);
testngap_recv(test_ue, recvbuf);
/* Send Security mode complete */
gmmbuf = testgmm_build_security_mode_complete(test_ue, nasbuf);
ABTS_PTR_NOTNULL(tc, gmmbuf);
sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive InitialContextSetupRequest +
* Registration accept */
recvbuf = testgnb_ngap_read(ngap);
ABTS_PTR_NOTNULL(tc, recvbuf);
testngap_recv(test_ue, recvbuf);
ABTS_INT_EQUAL(tc,
NGAP_ProcedureCode_id_InitialContextSetup,
test_ue->ngap_procedure_code);
/* Send UERadioCapabilityInfoIndication */
sendbuf = testngap_build_ue_radio_capability_info_indication(test_ue);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Send InitialContextSetupResponse */
sendbuf = testngap_build_initial_context_setup_response(test_ue, false);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Send Registration complete */
gmmbuf = testgmm_build_registration_complete(test_ue);
ABTS_PTR_NOTNULL(tc, gmmbuf);
sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive Configuration update command */
recvbuf = testgnb_ngap_read(ngap);
ABTS_PTR_NOTNULL(tc, recvbuf);
testngap_recv(test_ue, recvbuf);
/* Send PDU session establishment request */
sess = test_sess_add_by_dnn_and_psi(test_ue, "internet", 5);
ogs_assert(sess);
sess->ul_nas_transport_param.request_type =
OGS_NAS_5GS_REQUEST_TYPE_INITIAL;
sess->ul_nas_transport_param.dnn = 1;
sess->ul_nas_transport_param.s_nssai = 0;
sess->pdu_session_establishment_param.ssc_mode = 1;
sess->pdu_session_establishment_param.epco = 1;
gsmbuf = testgsm_build_pdu_session_establishment_request(sess);
ABTS_PTR_NOTNULL(tc, gsmbuf);
gmmbuf = testgmm_build_ul_nas_transport(sess,
OGS_NAS_PAYLOAD_CONTAINER_N1_SM_INFORMATION, gsmbuf);
ABTS_PTR_NOTNULL(tc, gmmbuf);
sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive PDUSessionResourceSetupRequest +
* DL NAS transport +
* PDU session establishment accept */
recvbuf = testgnb_ngap_read(ngap);
ABTS_PTR_NOTNULL(tc, recvbuf);
testngap_recv(test_ue, recvbuf);
ABTS_INT_EQUAL(tc,
NGAP_ProcedureCode_id_PDUSessionResourceSetup,
test_ue->ngap_procedure_code);
/* Send PDUSessionResourceSetupResponse */
sendbuf = testngap_sess_build_pdu_session_resource_setup_response(sess);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Send UEContextReleaseRequest */
sendbuf = testngap_build_ue_context_release_request(test_ue,
NGAP_Cause_PR_radioNetwork, NGAP_CauseRadioNetwork_user_inactivity,
true);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* 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 UEContextReleaseComplete */
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);
/* Send Registration request */
memset(&test_ue->registration_request_param, 0,
sizeof(test_ue->registration_request_param));
gmmbuf = testgmm_build_registration_request(test_ue, NULL, false, false);
ABTS_PTR_NOTNULL(tc, gmmbuf);
test_ue->registration_request_param.gmm_capability = 1;
test_ue->registration_request_param.s1_ue_network_capability = 1;
test_ue->registration_request_param.requested_nssai = 1;
test_ue->registration_request_param.last_visited_registered_tai = 1;
test_ue->registration_request_param.ue_usage_setting = 1;
nasbuf = testgmm_build_registration_request(test_ue, NULL, false, false);
ABTS_PTR_NOTNULL(tc, nasbuf);
sendbuf = testngap_build_initial_ue_message(test_ue, gmmbuf,
NGAP_RRCEstablishmentCause_mo_Signalling, false, true);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive Authentication request */
recvbuf = testgnb_ngap_read(ngap);
ABTS_PTR_NOTNULL(tc, recvbuf);
testngap_recv(test_ue, recvbuf);
/* Send Authentication failure - SYNCH failure */
gmmbuf = testgmm_build_authentication_failure(
test_ue, OGS_5GMM_CAUSE_SYNCH_FAILURE, 0x11223344);
ABTS_PTR_NOTNULL(tc, gmmbuf);
sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive Authentication request */
recvbuf = testgnb_ngap_read(ngap);
ABTS_PTR_NOTNULL(tc, recvbuf);
testngap_recv(test_ue, recvbuf);
/* Send Authentication failure - MAC failure */
gmmbuf = testgmm_build_authentication_failure(
test_ue, OGS_5GMM_CAUSE_MAC_FAILURE, 0);
ABTS_PTR_NOTNULL(tc, gmmbuf);
sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive Authentication reject */
recvbuf = testgnb_ngap_read(ngap);
ABTS_PTR_NOTNULL(tc, recvbuf);
testngap_recv(test_ue, recvbuf);
/* Send De-registration request */
gmmbuf = testgmm_build_de_registration_request(test_ue, 1, true, true);
ABTS_PTR_NOTNULL(tc, gmmbuf);
sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* 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 UEContextReleaseComplete */
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);
/* Send Registration request */
memset(&test_ue->registration_request_param, 0,
sizeof(test_ue->registration_request_param));
gmmbuf = testgmm_build_registration_request(test_ue, NULL, false, false);
ABTS_PTR_NOTNULL(tc, gmmbuf);
sendbuf = testngap_build_initial_ue_message(test_ue, gmmbuf,
@@ -137,50 +369,14 @@ static void test1_func(abts_case *tc, void *data)
NGAP_ProcedureCode_id_UEContextRelease,
test_ue->ngap_procedure_code);
/* Send Registration request */
gmmbuf = testgmm_build_registration_request(test_ue, NULL, false, false);
ABTS_PTR_NOTNULL(tc, gmmbuf);
sendbuf = testngap_build_initial_ue_message(test_ue, gmmbuf,
NGAP_RRCEstablishmentCause_mo_Signalling, false, true);
/* Send UEContextReleaseRequest */
sendbuf = testngap_build_ue_context_release_request(test_ue,
NGAP_Cause_PR_radioNetwork, NGAP_CauseRadioNetwork_user_inactivity,
true);
ABTS_PTR_NOTNULL(tc, sendbuf);
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);
testngap_recv(test_ue, recvbuf);
ABTS_INT_EQUAL(tc,
NGAP_ProcedureCode_id_UEContextRelease,
test_ue->ngap_procedure_code);
/* Send OLD UEContextReleaseComplete */
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 Authentication request */
recvbuf = testgnb_ngap_read(ngap);
ABTS_PTR_NOTNULL(tc, recvbuf);
testngap_recv(test_ue, recvbuf);
/* Send Authentication failure - MAC failure */
gmmbuf = testgmm_build_authentication_failure(
test_ue, OGS_5GMM_CAUSE_MAC_FAILURE, 0);
ABTS_PTR_NOTNULL(tc, gmmbuf);
sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive Authentication reject */
recvbuf = testgnb_ngap_read(ngap);
ABTS_PTR_NOTNULL(tc, recvbuf);
testngap_recv(test_ue, recvbuf);
/* Receive UEContextReleaseCommand */
recvbuf = testgnb_ngap_read(ngap);
ABTS_PTR_NOTNULL(tc, recvbuf);
@@ -195,6 +391,8 @@ static void test1_func(abts_case *tc, void *data)
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
ogs_msleep(300);
/********** Remove Subscriber in Database */
ABTS_INT_EQUAL(tc, OGS_OK, test_db_remove_ue(test_ue));

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2019,2020 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@@ -650,17 +650,161 @@ static void test3_func(abts_case *tc, void *data)
ABTS_PTR_NOTNULL(tc, recvbuf);
testngap_recv(test_ue, recvbuf);
#if 0 /* To reject UE registration */
/********** Insert Subscriber in Database */
doc = test_db_new_simple(test_ue);
ABTS_PTR_NOTNULL(tc, doc);
ABTS_INT_EQUAL(tc, OGS_OK, test_db_insert_ue(test_ue, doc));
#endif
/* Send Registration request */
gmmbuf = testgmm_build_registration_request(test_ue, NULL, false, false);
ABTS_PTR_NOTNULL(tc, gmmbuf);
test_ue->registration_request_param.gmm_capability = 1;
test_ue->registration_request_param.s1_ue_network_capability = 1;
test_ue->registration_request_param.requested_nssai = 1;
test_ue->registration_request_param.last_visited_registered_tai = 1;
test_ue->registration_request_param.ue_usage_setting = 1;
nasbuf = testgmm_build_registration_request(test_ue, NULL, false, false);
ABTS_PTR_NOTNULL(tc, nasbuf);
sendbuf = testngap_build_initial_ue_message(test_ue, gmmbuf,
NGAP_RRCEstablishmentCause_mo_Signalling, false, true);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive Authentication request */
recvbuf = testgnb_ngap_read(ngap);
ABTS_PTR_NOTNULL(tc, recvbuf);
testngap_recv(test_ue, recvbuf);
/* Send Authentication response */
gmmbuf = testgmm_build_authentication_response(test_ue);
ABTS_PTR_NOTNULL(tc, gmmbuf);
sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive Security mode command */
recvbuf = testgnb_ngap_read(ngap);
ABTS_PTR_NOTNULL(tc, recvbuf);
testngap_recv(test_ue, recvbuf);
/* Send Security mode complete */
gmmbuf = testgmm_build_security_mode_complete(test_ue, nasbuf);
ABTS_PTR_NOTNULL(tc, gmmbuf);
sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive InitialContextSetupRequest +
* Registration accept */
recvbuf = testgnb_ngap_read(ngap);
ABTS_PTR_NOTNULL(tc, recvbuf);
testngap_recv(test_ue, recvbuf);
ABTS_INT_EQUAL(tc,
NGAP_ProcedureCode_id_InitialContextSetup,
test_ue->ngap_procedure_code);
/* Send UERadioCapabilityInfoIndication */
sendbuf = testngap_build_ue_radio_capability_info_indication(test_ue);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Send InitialContextSetupResponse */
sendbuf = testngap_build_initial_context_setup_response(test_ue, false);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Send Registration complete */
gmmbuf = testgmm_build_registration_complete(test_ue);
ABTS_PTR_NOTNULL(tc, gmmbuf);
sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive Configuration update command */
recvbuf = testgnb_ngap_read(ngap);
ABTS_PTR_NOTNULL(tc, recvbuf);
testngap_recv(test_ue, recvbuf);
/* Send PDU session establishment request */
sess = test_sess_add_by_dnn_and_psi(test_ue, "internet", 5);
ogs_assert(sess);
sess->ul_nas_transport_param.request_type =
OGS_NAS_5GS_REQUEST_TYPE_INITIAL;
sess->ul_nas_transport_param.dnn = 1;
sess->ul_nas_transport_param.s_nssai = 1;
sess->pdu_session_establishment_param.ssc_mode = 1;
sess->pdu_session_establishment_param.epco = 1;
gsmbuf = testgsm_build_pdu_session_establishment_request(sess);
ABTS_PTR_NOTNULL(tc, gsmbuf);
gmmbuf = testgmm_build_ul_nas_transport(sess,
OGS_NAS_PAYLOAD_CONTAINER_N1_SM_INFORMATION, gsmbuf);
ABTS_PTR_NOTNULL(tc, gmmbuf);
sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Receive PDUSessionResourceSetupRequest +
* DL NAS transport +
* PDU session establishment accept */
recvbuf = testgnb_ngap_read(ngap);
ABTS_PTR_NOTNULL(tc, recvbuf);
testngap_recv(test_ue, recvbuf);
ABTS_INT_EQUAL(tc,
NGAP_ProcedureCode_id_PDUSessionResourceSetup,
test_ue->ngap_procedure_code);
/* Send PDUSessionResourceSetupResponse */
sendbuf = testngap_sess_build_pdu_session_resource_setup_response(sess);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Send De-registration request */
gmmbuf = testgmm_build_de_registration_request(test_ue, 1, true, true);
ABTS_PTR_NOTNULL(tc, gmmbuf);
sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* 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 UEContextReleaseComplete */
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);
/********** Remove Subscriber in Database */
ABTS_INT_EQUAL(tc, OGS_OK, test_db_remove_ue(test_ue));
/* Wait for removing subscriber */
ogs_msleep(100);
/* Send Registration request */
memset(&test_ue->registration_request_param, 0,
sizeof(test_ue->registration_request_param));
gmmbuf = testgmm_build_registration_request(test_ue, NULL, false, false);
ABTS_PTR_NOTNULL(tc, gmmbuf);
sendbuf = testngap_build_initial_ue_message(test_ue, gmmbuf,
NGAP_RRCEstablishmentCause_mo_Signalling, false, true);
ABTS_PTR_NOTNULL(tc, sendbuf);
@@ -709,11 +853,6 @@ static void test3_func(abts_case *tc, void *data)
ogs_msleep(300);
#if 0 /* To reject UE registration */
/********** Remove Subscriber in Database */
ABTS_INT_EQUAL(tc, OGS_OK, test_db_remove_ue(test_ue));
#endif
/* gNB disonncect from UPF */
testgnb_gtpu_close(gtpu);