From a35b5af124bdf7249218dbed049a5f4448a9b8d2 Mon Sep 17 00:00:00 2001 From: Bostjan Meglic Date: Tue, 11 Feb 2025 13:38:29 +0100 Subject: [PATCH 01/10] [AMF] save the correct serving GUAMI for particular UE In case AMF is configured for multiple PLMN's, it would send a wrong GUAMI in the Registration Accept message to the UE, also in other NAS and SBI messages. Previously, it would only send the first configured PLMN. --- src/amf/gmm-handler.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/amf/gmm-handler.c b/src/amf/gmm-handler.c index c6710eeba..68ada928e 100644 --- a/src/amf/gmm-handler.c +++ b/src/amf/gmm-handler.c @@ -39,6 +39,7 @@ ogs_nas_5gmm_cause_t gmm_handle_registration_request(amf_ue_t *amf_ue, ogs_nas_5gs_registration_request_t *registration_request) { int served_tai_index = 0; + int i; uint8_t gmm_cause; ran_ue_t *ran_ue = NULL; @@ -164,6 +165,14 @@ ogs_nas_5gmm_cause_t gmm_handle_registration_request(amf_ue_t *amf_ue, ogs_nas_to_plmn_id(&amf_ue->home_plmn_id, &mobile_identity_suci->nas_plmn_id); + for (i = 0; i < amf_self()->num_of_served_guami; i++) { + if (!memcmp(&amf_ue->home_plmn_id, + &amf_self()->served_guami[i].plmn_id, + sizeof(amf_ue->home_plmn_id))) { + amf_ue->guami = &amf_self()->served_guami[i]; + } + } + gmm_cause = gmm_cause_from_access_control(&amf_ue->home_plmn_id); if (gmm_cause != OGS_5GMM_CAUSE_REQUEST_ACCEPTED) { ogs_error("Rejected by PLMN-ID access control"); From 3c1117d4fdd16a3a5ee6c37dcafc76b838da870a Mon Sep 17 00:00:00 2001 From: Sukchan Lee Date: Sun, 18 May 2025 14:31:52 +0900 Subject: [PATCH 02/10] [SBI] Fix parsing and serialization of _links "item" array (#3897) Previously, Open5GS assumed the _links map contained an array under the key "items". However, the 3GPP specification (TS29.510 section 4.9.4 and TS29.501 Table 6.1.6.2.25-1) defines this member name as "item". As a result, when interacting with vendor NRF implementations that use "item", Open5GS could not find the array and logged "No items", causing JSON errors. This change updates both serialization and parsing in lib/sbi/custom/links.c: - In ogs_sbi_links_convertToJSON(), replace the property name "items" with "item" when building JSON. - In ogs_sbi_links_parseFromJSON(), retrieve the array under "item" and adjust the error message to "No item" if the member is missing. With these corrections, Open5GS will correctly handle NRF responses using "item" and remain compliant with the indirect communication model defined by 3GPP. --- lib/sbi/custom/links.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/sbi/custom/links.c b/lib/sbi/custom/links.c index ea5baacfa..1b27bc30a 100644 --- a/lib/sbi/custom/links.c +++ b/lib/sbi/custom/links.c @@ -45,7 +45,7 @@ cJSON *ogs_sbi_links_convertToJSON(ogs_sbi_links_t *links) linksJSON = cJSON_CreateObject(); ogs_assert(linksJSON); - cJSON_AddItemToObject(linksJSON, "items", itemsJSON); + cJSON_AddItemToObject(linksJSON, "item", itemsJSON); cJSON_AddItemToObject(linksJSON, "self", selfJSON); cJSON_AddNumberToObject(linksJSON, "totalItemCount", cJSON_GetArraySize(itemsJSON)); @@ -73,9 +73,9 @@ ogs_sbi_links_t *ogs_sbi_links_parseFromJSON(cJSON *json) return NULL; } - _items = cJSON_GetObjectItemCaseSensitive(_links, "items"); + _items = cJSON_GetObjectItemCaseSensitive(_links, "item"); if (!_items) { - ogs_error("No items"); + ogs_error("No item"); return NULL; } From 8ec8832318e373d66f9de6cc058bfba22af58d0e Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Fri, 23 May 2025 20:03:19 +0200 Subject: [PATCH 03/10] [AMF] cosmetic: Fix trailing whitespace --- src/amf/ngap-build.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/amf/ngap-build.c b/src/amf/ngap-build.c index 0c4f6f72c..c915fcda7 100644 --- a/src/amf/ngap-build.c +++ b/src/amf/ngap-build.c @@ -1265,7 +1265,7 @@ ogs_pkbuf_t *ngap_build_ue_context_release_command( &UE_NGAP_IDs->choice.aMF_UE_NGAP_ID, ran_ue->amf_ue_ngap_id); } else { UE_NGAP_IDs->present = NGAP_UE_NGAP_IDs_PR_uE_NGAP_ID_pair; - UE_NGAP_IDs->choice.uE_NGAP_ID_pair = + UE_NGAP_IDs->choice.uE_NGAP_ID_pair = CALLOC(1, sizeof(NGAP_UE_NGAP_ID_pair_t)); asn_uint642INTEGER( &UE_NGAP_IDs->choice.uE_NGAP_ID_pair->aMF_UE_NGAP_ID, From 73976c938be20b5bac22d5242628cbfe62456ce5 Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Fri, 23 May 2025 20:03:44 +0200 Subject: [PATCH 04/10] [AMF] Fix order of IEs in NG Setup Failure Cause goes before TimeToWait accoding to 3GPP TS 38.413 ASN.1 definition "NGSetupFailureIEs NGAP-PROTOCOL-IES" and section 9.2.6.3. --- src/amf/ngap-build.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/amf/ngap-build.c b/src/amf/ngap-build.c index c915fcda7..c864a257e 100644 --- a/src/amf/ngap-build.c +++ b/src/amf/ngap-build.c @@ -171,7 +171,6 @@ ogs_pkbuf_t *ngap_build_ng_setup_failure( NGAP_NGSetupFailureIEs_t *ie = NULL; NGAP_Cause_t *Cause = NULL; - NGAP_TimeToWait_t *TimeToWait = NULL; ogs_debug(" Group[%d] Cause[%d] TimeToWait[%ld]", group, (int)cause, time_to_wait); @@ -189,17 +188,6 @@ ogs_pkbuf_t *ngap_build_ng_setup_failure( NGSetupFailure = &unsuccessfulOutcome->value.choice.NGSetupFailure; - if (time_to_wait > -1) { - ie = CALLOC(1, sizeof(NGAP_NGSetupFailureIEs_t)); - ASN_SEQUENCE_ADD(&NGSetupFailure->protocolIEs, ie); - - ie->id = NGAP_ProtocolIE_ID_id_TimeToWait; - ie->criticality = NGAP_Criticality_ignore; - ie->value.present = NGAP_NGSetupFailureIEs__value_PR_TimeToWait; - - TimeToWait = &ie->value.choice.TimeToWait; - } - ie = CALLOC(1, sizeof(NGAP_NGSetupFailureIEs_t)); ASN_SEQUENCE_ADD(&NGSetupFailure->protocolIEs, ie); @@ -211,8 +199,16 @@ ogs_pkbuf_t *ngap_build_ng_setup_failure( Cause->present = group; Cause->choice.radioNetwork = cause; - if (TimeToWait) - *TimeToWait = time_to_wait; + if (time_to_wait > -1) { + ie = CALLOC(1, sizeof(NGAP_NGSetupFailureIEs_t)); + ASN_SEQUENCE_ADD(&NGSetupFailure->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_TimeToWait; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_NGSetupFailureIEs__value_PR_TimeToWait; + + ie->value.choice.TimeToWait = time_to_wait; + } return ogs_ngap_encode(&pdu); } From e93bc6b5c8321efbe3d1edf269d2a033f00d12c3 Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Wed, 28 May 2025 20:40:27 +0200 Subject: [PATCH 05/10] [SBI] Log error code description upon query failure Sometimes (eg res=16) the conn->error buffer is left empty by curl, so also logging the name of the error code provides some extra useful information. --- lib/sbi/client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sbi/client.c b/lib/sbi/client.c index 89569d081..df9a1518e 100644 --- a/lib/sbi/client.c +++ b/lib/sbi/client.c @@ -754,7 +754,7 @@ static void check_multi_info(ogs_sbi_client_t *client) } } else - ogs_warn("[%d] %s", res, conn->error); + ogs_warn("%s (%d): %s", curl_easy_strerror(res), res, conn->error); ogs_assert(conn->client_cb); if (res == CURLE_OK) From 799103257b7076426f13d1f32e8ea8f6e02083da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Gradi=C5=A1ar?= <120629867+matejGradisar@users.noreply.github.com> Date: Sat, 31 May 2025 13:51:06 +0200 Subject: [PATCH 06/10] [AMF] Fix UE context transfer when only NRF is client (#3880) * [AMF] Fix UE context transfer when only NRF is client If UE context transfer is initiated and the new AMF does not get the old AMF from NRF or no UE context is retrieved from the old AMF, we do not want to reject UE registration. Send identity request instead. Test "transfer-error-case" is added into the commit. * [tests] Unite tests for UE context transfer All tests for UE context transfer with different configs are placed into test folder transfer. * [tests] Make two binaries for UE context transfer tests For each test config a different test binary is created. --- configs/meson.build | 1 + configs/transfer-error-case.yaml.in | 266 +++++++ lib/core/ogs-process.c | 14 +- lib/core/ogs-process.h | 4 + src/amf/amf-sm.c | 36 +- src/amf/nnrf-handler.c | 88 +++ src/amf/nnrf-handler.h | 2 + tests/app/5gc-init.c | 62 +- tests/app/app-init.c | 34 +- tests/app/epc-init.c | 14 +- tests/common/application.c | 41 +- tests/common/application.h | 10 +- tests/transfer/abts-error-main.c | 66 ++ tests/transfer/abts-main.c | 2 +- tests/transfer/meson.build | 11 + .../ue-context-transfer-error-case-test.c | 681 ++++++++++++++++++ 16 files changed, 1281 insertions(+), 51 deletions(-) create mode 100644 configs/transfer-error-case.yaml.in create mode 100644 tests/transfer/abts-error-main.c create mode 100644 tests/transfer/ue-context-transfer-error-case-test.c diff --git a/configs/meson.build b/configs/meson.build index a4f1b618e..6718af586 100644 --- a/configs/meson.build +++ b/configs/meson.build @@ -41,6 +41,7 @@ example_conf = ''' srsenb.yaml non3gpp.yaml transfer.yaml + transfer-error-case.yaml '''.split() foreach file : example_conf diff --git a/configs/transfer-error-case.yaml.in b/configs/transfer-error-case.yaml.in new file mode 100644 index 000000000..b22b05b4f --- /dev/null +++ b/configs/transfer-error-case.yaml.in @@ -0,0 +1,266 @@ +db_uri: mongodb://localhost/open5gs + +logger: + +test: + serving: + - plmn_id: + mcc: 999 + mnc: 70 + +global: + parameter: +# no_nrf: true + no_scp: true + no_sepp: true +# no_amf: true +# no_smf: true +# no_upf: true +# no_ausf: true +# no_udm: true +# no_pcf: true +# no_nssf: true +# no_bsf: true +# no_udr: true + no_mme: true + no_sgwc: true + no_sgwu: true + no_pcrf: true + no_hss: true + +smf: + sbi: + server: + - address: 127.0.0.4 + port: 7777 + client: + nrf: + - uri: http://127.0.0.10:7777 + pfcp: + server: + - address: 127.0.0.4 + client: + upf: + - address: 127.0.0.7 + gtpc: + server: + - address: 127.0.0.4 + gtpu: + server: + - address: 127.0.0.4 + metrics: + server: + - address: 127.0.0.4 + port: 9090 + session: + - subnet: 10.45.0.0/16 + gateway: 10.45.0.1 + - subnet: 2001:db8:cafe::/48 + gateway: 2001:db8:cafe::1 + dns: + - 8.8.8.8 + - 8.8.4.4 + - 2001:4860:4860::8888 + - 2001:4860:4860::8844 + mtu: 1400 + freeDiameter: + identity: smf.localdomain + realm: localdomain + listen_on: 127.0.0.4 + no_fwd: true + load_extension: + - module: @build_subprojects_freeDiameter_extensions_dir@/dbg_msg_dumps.fdx + conf: 0x8888 + - module: @build_subprojects_freeDiameter_extensions_dir@/dict_rfc5777.fdx + - module: @build_subprojects_freeDiameter_extensions_dir@/dict_mip6i.fdx + - module: @build_subprojects_freeDiameter_extensions_dir@/dict_nasreq.fdx + - module: @build_subprojects_freeDiameter_extensions_dir@/dict_nas_mipv6.fdx + - module: @build_subprojects_freeDiameter_extensions_dir@/dict_dcca.fdx + - module: @build_subprojects_freeDiameter_extensions_dir@/dict_dcca_3gpp/dict_dcca_3gpp.fdx + connect: + - identity: pcrf.localdomain + address: 127.0.0.9 + +amf: + sbi: + server: + - address: 127.0.0.5 + port: 7777 + client: + nrf: + - uri: http://127.0.0.10:7777 + ngap: + server: + - address: 127.0.0.5 + metrics: + server: + - address: 127.0.0.5 + port: 9090 + guami: + - plmn_id: + mcc: 999 + mnc: 70 + amf_id: + region: 2 + set: 1 + pointer: 31 + tai: + - plmn_id: + mcc: 999 + mnc: 70 + tac: 1 + plmn_support: + - plmn_id: + mcc: 999 + mnc: 70 + s_nssai: + - sst: 1 + security: + integrity_order : [ NIA2, NIA1, NIA0 ] + ciphering_order : [ NEA0, NEA1, NEA2 ] + network_name: + full: Open5GS + amf_name: open5gs-amf0 + time: + t3512: + value: 540 # 9 mintues * 60 = 540 seconds + +# amf #2 +amf: + sbi: + server: + - address: 127.0.0.50 + port: 7777 + client: + nrf: + - uri: http://127.0.0.10:7777 + ngap: + server: + - address: 127.0.0.50 + metrics: + server: + - address: 127.0.0.50 + port: 9090 + guami: + - plmn_id: + mcc: 999 + mnc: 70 + amf_id: + region: 2 + set: 1 + pointer: 30 + tai: + - plmn_id: + mcc: 999 + mnc: 70 + tac: 1 + plmn_support: + - plmn_id: + mcc: 999 + mnc: 70 + s_nssai: + - sst: 1 + security: + integrity_order : [ NIA2, NIA1, NIA0 ] + ciphering_order : [ NEA0, NEA1, NEA2 ] + network_name: + full: Open5GS + amf_name: open5gs-amf1 + time: + t3512: + value: 540 # 9 mintues * 60 = 540 seconds + +upf: + pfcp: + server: + - address: 127.0.0.7 + gtpu: + server: + - address: 127.0.0.7 + session: + - subnet: 10.45.0.0/16 + gateway: 10.45.0.1 + - subnet: 2001:db8:cafe::/48 + gateway: 2001:db8:cafe::1 + metrics: + server: + - address: 127.0.0.7 + port: 9090 + +nrf: + sbi: + server: + - address: 127.0.0.10 + port: 7777 + serving: + - plmn_id: + mcc: 999 + mnc: 70 + +ausf: + sbi: + server: + - address: 127.0.0.11 + port: 7777 + client: + nrf: + - uri: http://127.0.0.10:7777 + +udm: + hnet: + - id: 1 + scheme: 1 + key: @build_configs_dir@/open5gs/hnet/curve25519-1.key + - id: 2 + scheme: 2 + key: @build_configs_dir@/open5gs/hnet/secp256r1-2.key + sbi: + server: + - address: 127.0.0.12 + port: 7777 + client: + nrf: + - uri: http://127.0.0.10:7777 + +pcf: + sbi: + server: + - address: 127.0.0.13 + port: 7777 + client: + nrf: + - uri: http://127.0.0.10:7777 + metrics: + server: + - address: 127.0.0.13 + port: 9090 + +nssf: + sbi: + server: + - address: 127.0.0.14 + port: 7777 + client: + nrf: + - uri: http://127.0.0.10:7777 + nsi: + - uri: http://127.0.0.10:7777 + s_nssai: + sst: 1 +bsf: + sbi: + server: + - address: 127.0.0.15 + port: 7777 + client: + nrf: + - uri: http://127.0.0.10:7777 + +udr: + sbi: + server: + - address: 127.0.0.20 + port: 7777 + client: + nrf: + - uri: http://127.0.0.10:7777 diff --git a/lib/core/ogs-process.c b/lib/core/ogs-process.c index ce3972456..2b682bd6d 100644 --- a/lib/core/ogs-process.c +++ b/lib/core/ogs-process.c @@ -370,9 +370,15 @@ int ogs_proc_join(ogs_proc_t *const process, int *const out_return_code) } if (process->child != waitpid(process->child, &status, 0)) { + process->child = 0; + ogs_fatal("waitpid failed: %d", status); return OGS_ERROR; } + process->child = 0; + if (process->nf_name) + ogs_free(process->nf_name); + if (out_return_code) { if (WIFEXITED(status)) { *out_return_code = WEXITSTATUS(status); @@ -416,9 +422,13 @@ int ogs_proc_terminate(ogs_proc_t *const process) return OGS_ERROR; } #else - if (kill(process->child, SIGTERM) == -1) { - return OGS_ERROR; + + if (process->child) { + if (kill(process->child, SIGTERM) == -1) { + return OGS_ERROR; + } } + #endif return OGS_OK; diff --git a/lib/core/ogs-process.h b/lib/core/ogs-process.h index 784488fd3..0c0962582 100644 --- a/lib/core/ogs-process.h +++ b/lib/core/ogs-process.h @@ -73,6 +73,10 @@ typedef struct ogs_proc_s { unsigned long dwProcessId; #else pid_t child; + + // to force kill the right NF in tests if needed. + char *nf_name; + int index; #endif } ogs_proc_t; diff --git a/src/amf/amf-sm.c b/src/amf/amf-sm.c index 8aa8bc197..52edf505b 100644 --- a/src/amf/amf-sm.c +++ b/src/amf/amf-sm.c @@ -410,9 +410,11 @@ void amf_state_operational(ogs_fsm_t *s, amf_event_t *e) CASE(OGS_SBI_HTTP_METHOD_GET) if (sbi_message.res_status == OGS_SBI_HTTP_STATUS_OK) amf_nnrf_handle_nf_discover(sbi_xact, &sbi_message); - else + else { ogs_error("HTTP response error [%d]", sbi_message.res_status); + amf_nnrf_handle_failed_amf_discovery(sbi_xact); + } break; DEFAULT @@ -718,6 +720,10 @@ void amf_state_operational(ogs_fsm_t *s, amf_event_t *e) * To avoid double-free SBI xact, * we need to check ogs_sbi_xact_find_by_id() */ + + OpenAPI_nf_type_e requester_nf_type = OpenAPI_nf_type_NULL; + ogs_sbi_discovery_option_t *discovery_option = NULL; + sbi_xact_id = OGS_POINTER_TO_UINT(e->h.sbi.data); ogs_assert(sbi_xact_id >= OGS_MIN_POOL_ID && sbi_xact_id <= OGS_MAX_POOL_ID); @@ -737,6 +743,8 @@ void amf_state_operational(ogs_fsm_t *s, amf_event_t *e) sbi_object_id <= OGS_MAX_POOL_ID); service_type = sbi_xact->service_type; + requester_nf_type = sbi_xact->requester_nf_type; + discovery_option = sbi_xact->discovery_option; ogs_sbi_xact_remove(sbi_xact); @@ -754,6 +762,32 @@ void amf_state_operational(ogs_fsm_t *s, amf_event_t *e) ogs_error("[%s:%s] Cannot receive SBI message", amf_ue->supi, amf_ue->suci); + /* + * TS 23.502 + * 4.2.2.2.2 General Registration + * If the SUCI is not provided by the UE nor retrieved from the old AMF the Identity Request + * procedure is initiated by AMF sending an Identity Request message to the UE requesting the SUCI. + */ + + if (amf_ue->nas.message_type == OGS_NAS_5GS_REGISTRATION_REQUEST && + amf_ue->nas.registration.value == OGS_NAS_5GS_REGISTRATION_TYPE_INITIAL && + requester_nf_type == OpenAPI_nf_type_AMF && + discovery_option->guami_presence) { + + amf_ue->amf_ue_context_transfer_state = + UE_CONTEXT_INITIAL_STATE; + + if (!(AMF_UE_HAVE_SUCI(amf_ue) || + AMF_UE_HAVE_SUPI(amf_ue))) { + CLEAR_AMF_UE_TIMER(amf_ue->t3570); + rv = nas_5gs_send_identity_request(amf_ue); + ogs_expect(rv == OGS_OK); + ogs_assert(rv != OGS_ERROR); + + break; + } + } + r = nas_5gs_send_gmm_reject_from_sbi(amf_ue, OGS_SBI_HTTP_STATUS_GATEWAY_TIMEOUT); ogs_expect(r == OGS_OK); diff --git a/src/amf/nnrf-handler.c b/src/amf/nnrf-handler.c index 1f8d1414f..0f2e01d73 100644 --- a/src/amf/nnrf-handler.c +++ b/src/amf/nnrf-handler.c @@ -49,6 +49,7 @@ void amf_nnrf_handle_nf_discover( SearchResult = recvmsg->SearchResult; if (!SearchResult) { ogs_error("No SearchResult"); + amf_nnrf_handle_failed_amf_discovery(xact); return; } @@ -72,6 +73,34 @@ void amf_nnrf_handle_nf_discover( ogs_assert(amf_ue); ogs_error("[%s] (NF discover) No [%s]", amf_ue->suci, ogs_sbi_service_type_to_name(service_type)); + + /* + * TS 23.502 + * 4.2.2.2.2 General Registration + * If the SUCI is not provided by the UE nor retrieved from the old AMF the Identity Request + * procedure is initiated by AMF sending an Identity Request message to the UE requesting the SUCI. + */ + + if (amf_ue->nas.message_type == OGS_NAS_5GS_REGISTRATION_REQUEST && + amf_ue->nas.registration.value == OGS_NAS_5GS_REGISTRATION_TYPE_INITIAL && + requester_nf_type == OpenAPI_nf_type_AMF && + discovery_option->guami_presence) { + + amf_ue->amf_ue_context_transfer_state = + UE_CONTEXT_INITIAL_STATE; + + ogs_sbi_xact_remove(xact); + + if (!(AMF_UE_HAVE_SUCI(amf_ue) || + AMF_UE_HAVE_SUPI(amf_ue))) { + CLEAR_AMF_UE_TIMER(amf_ue->t3570); + r = nas_5gs_send_identity_request(amf_ue); + ogs_expect(r == OGS_OK); + ogs_assert(r != OGS_ERROR); + break; + } + } + r = nas_5gs_send_gmm_reject_from_sbi(amf_ue, OGS_SBI_HTTP_STATUS_GATEWAY_TIMEOUT); ogs_expect(r == OGS_OK); @@ -110,3 +139,62 @@ void amf_nnrf_handle_nf_discover( ogs_expect(true == amf_sbi_send_request(nf_instance, xact)); } + +void amf_nnrf_handle_failed_amf_discovery( + ogs_sbi_xact_t *sbi_xact) +{ + int r; + + OpenAPI_nf_type_e requester_nf_type = OpenAPI_nf_type_NULL; + ogs_sbi_discovery_option_t *discovery_option = NULL; + ogs_sbi_service_type_e service_type = OGS_SBI_SERVICE_TYPE_NULL; + ogs_sbi_object_t *sbi_object = NULL; + amf_ue_t *amf_ue = NULL; + + ogs_assert(sbi_xact); + sbi_object = sbi_xact->sbi_object; + ogs_assert(sbi_object); + service_type = sbi_xact->service_type; + ogs_assert(service_type); + requester_nf_type = sbi_xact->requester_nf_type; + ogs_assert(requester_nf_type); + + discovery_option = sbi_xact->discovery_option; + + ogs_assert(sbi_object->type > OGS_SBI_OBJ_BASE && + sbi_object->type < OGS_SBI_OBJ_TOP); + + if (sbi_object->type == OGS_SBI_OBJ_UE_TYPE) { + + amf_ue = (amf_ue_t *)sbi_object; + ogs_assert(amf_ue); + + /* + * TS 23.502 + * 4.2.2.2.2 General Registration + * If the SUCI is not provided by the UE nor retrieved from the old AMF the Identity Request + * procedure is initiated by AMF sending an Identity Request message to the UE requesting the SUCI. + */ + + if (amf_ue->nas.message_type == OGS_NAS_5GS_REGISTRATION_REQUEST && + amf_ue->nas.registration.value == OGS_NAS_5GS_REGISTRATION_TYPE_INITIAL && + requester_nf_type == OpenAPI_nf_type_AMF && + discovery_option->guami_presence) { + + amf_ue->amf_ue_context_transfer_state = + UE_CONTEXT_INITIAL_STATE; + + ogs_sbi_xact_remove(sbi_xact); + + if (!(AMF_UE_HAVE_SUCI(amf_ue) || + AMF_UE_HAVE_SUPI(amf_ue))) { + CLEAR_AMF_UE_TIMER(amf_ue->t3570); + r = nas_5gs_send_identity_request(amf_ue); + ogs_expect(r == OGS_OK); + ogs_assert(r != OGS_ERROR); + } + } + } + + return; +} diff --git a/src/amf/nnrf-handler.h b/src/amf/nnrf-handler.h index 9298339da..0b6db0069 100644 --- a/src/amf/nnrf-handler.h +++ b/src/amf/nnrf-handler.h @@ -28,6 +28,8 @@ extern "C" { void amf_nnrf_handle_nf_discover( ogs_sbi_xact_t *xact, ogs_sbi_message_t *recvmsg); +void amf_nnrf_handle_failed_amf_discovery( + ogs_sbi_xact_t *sbi_xact); #ifdef __cplusplus } diff --git a/tests/app/5gc-init.c b/tests/app/5gc-init.c index 2d6a05d2c..440ff7378 100644 --- a/tests/app/5gc-init.c +++ b/tests/app/5gc-init.c @@ -42,7 +42,7 @@ static void run_threads(const char *nf_name, int count, { int i; - threads[0] = test_child_create(nf_name, argv_out); + threads[0] = test_child_create(nf_name, 0, argv_out); for (i = 1; i < count; i++) { const char *idx_string = NULL;; @@ -62,7 +62,7 @@ static void run_threads(const char *nf_name, int count, argv_out[argv_out_idx + 1] = idx_string; argv_out[argv_out_idx + 2] = NULL; - threads[i] = test_child_create(nf_name, argv_out); + threads[i] = test_child_create(nf_name, i, argv_out); } // reset argv_out and remove the added "-k" parameter @@ -90,11 +90,11 @@ int app_initialize(const char *const argv[]) } if (ogs_global_conf()->parameter.no_nrf == 0) - nrf_thread = test_child_create("nrf", argv_out); + nrf_thread = test_child_create("nrf", 0, argv_out); if (ogs_global_conf()->parameter.no_scp == 0) - scp_thread = test_child_create("scp", argv_out); + scp_thread = test_child_create("scp", 0, argv_out); if (ogs_global_conf()->parameter.no_sepp == 0) - sepp_thread = test_child_create("sepp", argv_out); + sepp_thread = test_child_create("sepp", 0, argv_out); if (ogs_global_conf()->parameter.no_upf == 0) run_threads("upf", ogs_global_conf()->parameter.upf_count, @@ -139,29 +139,55 @@ void app_terminate(void) int i; for (i = 0; i < OGS_MAX_NF_INSTANCES; i++) { - if (amf_threads[i]) + if (amf_threads[i]) { ogs_thread_destroy(amf_threads[i]); - if (smf_threads[i]) + amf_threads[i] = NULL; + } + if (smf_threads[i]) { ogs_thread_destroy(smf_threads[i]); - if (upf_threads[i]) + smf_threads[i] = NULL; + } + if (upf_threads[i]) { ogs_thread_destroy(upf_threads[i]); - if (udr_threads[i]) + upf_threads[i] = NULL; + } + if (udr_threads[i]) { ogs_thread_destroy(udr_threads[i]); - if (nssf_threads[i]) + udr_threads[i] = NULL; + } + if (nssf_threads[i]) { ogs_thread_destroy(nssf_threads[i]); - if (bsf_threads[i]) + nssf_threads[i] = NULL; + } + if (bsf_threads[i]) { ogs_thread_destroy(bsf_threads[i]); - if (pcf_threads[i]) + bsf_threads[i] = NULL; + } + if (pcf_threads[i]) { ogs_thread_destroy(pcf_threads[i]); - if (udm_threads[i]) + pcf_threads[i] = NULL; + } + if (udm_threads[i]) { ogs_thread_destroy(udm_threads[i]); - if (ausf_threads[i]) + udm_threads[i] = NULL; + } + if (ausf_threads[i]) { ogs_thread_destroy(ausf_threads[i]); + ausf_threads[i] = NULL; + } + } + if (sepp_thread) { + ogs_thread_destroy(sepp_thread); + sepp_thread = NULL; + } + if (scp_thread) { + ogs_thread_destroy(scp_thread); + scp_thread = NULL; + } + if (nrf_thread) { + ogs_thread_destroy(nrf_thread); + nrf_thread = NULL; } - - if (sepp_thread) ogs_thread_destroy(sepp_thread); - if (scp_thread) ogs_thread_destroy(scp_thread); - if (nrf_thread) ogs_thread_destroy(nrf_thread); } void test_5gc_init(void) diff --git a/tests/app/app-init.c b/tests/app/app-init.c index f642cb8b6..0139c5c29 100644 --- a/tests/app/app-init.c +++ b/tests/app/app-init.c @@ -58,43 +58,43 @@ int app_initialize(const char *const argv[]) } if (ogs_global_conf()->parameter.no_nrf == 0) - nrf_thread = test_child_create("nrf", argv_out); + nrf_thread = test_child_create("nrf", 0, argv_out); if (ogs_global_conf()->parameter.no_scp == 0) - scp_thread = test_child_create("scp", argv_out); + scp_thread = test_child_create("scp", 0, argv_out); if (ogs_global_conf()->parameter.no_sepp == 0) - sepp_thread = test_child_create("sepp", argv_out); + sepp_thread = test_child_create("sepp", 0, argv_out); if (ogs_global_conf()->parameter.no_hss == 0) - hss_thread = test_child_create("hss", argv_out); + hss_thread = test_child_create("hss", 0, argv_out); if (ogs_global_conf()->parameter.no_pcrf == 0) - pcrf_thread = test_child_create("pcrf", argv_out); + pcrf_thread = test_child_create("pcrf", 0, argv_out); if (ogs_global_conf()->parameter.no_upf == 0) - upf_thread = test_child_create("upf", argv_out); + upf_thread = test_child_create("upf", 0, argv_out); if (ogs_global_conf()->parameter.no_sgwu == 0) - sgwu_thread = test_child_create("sgwu", argv_out); + sgwu_thread = test_child_create("sgwu", 0, argv_out); if (ogs_global_conf()->parameter.no_smf == 0) - smf_thread = test_child_create("smf", argv_out); + smf_thread = test_child_create("smf", 0, argv_out); if (ogs_global_conf()->parameter.no_sgwc == 0) - sgwc_thread = test_child_create("sgwc", argv_out); + sgwc_thread = test_child_create("sgwc", 0, argv_out); if (ogs_global_conf()->parameter.no_mme == 0) - mme_thread = test_child_create("mme", argv_out); + mme_thread = test_child_create("mme", 0, argv_out); if (ogs_global_conf()->parameter.no_amf == 0) - amf_thread = test_child_create("amf", argv_out); + amf_thread = test_child_create("amf", 0, argv_out); if (ogs_global_conf()->parameter.no_ausf == 0) - ausf_thread = test_child_create("ausf", argv_out); + ausf_thread = test_child_create("ausf", 0, argv_out); if (ogs_global_conf()->parameter.no_udm == 0) - udm_thread = test_child_create("udm", argv_out); + udm_thread = test_child_create("udm", 0, argv_out); if (ogs_global_conf()->parameter.no_pcf == 0) - pcf_thread = test_child_create("pcf", argv_out); + pcf_thread = test_child_create("pcf", 0, argv_out); if (ogs_global_conf()->parameter.no_nssf == 0) - nssf_thread = test_child_create("nssf", argv_out); + nssf_thread = test_child_create("nssf", 0, argv_out); if (ogs_global_conf()->parameter.no_bsf == 0) - bsf_thread = test_child_create("bsf", argv_out); + bsf_thread = test_child_create("bsf", 0, argv_out); if (ogs_global_conf()->parameter.no_udr == 0) - udr_thread = test_child_create("udr", argv_out); + udr_thread = test_child_create("udr", 0, argv_out); /* * Wait for all sockets listening diff --git a/tests/app/epc-init.c b/tests/app/epc-init.c index eaf3ab2c5..00a0ced47 100644 --- a/tests/app/epc-init.c +++ b/tests/app/epc-init.c @@ -48,22 +48,22 @@ int app_initialize(const char *const argv[]) } if (ogs_global_conf()->parameter.no_hss == 0) - hss_thread = test_child_create("hss", argv_out); + hss_thread = test_child_create("hss", 0, argv_out); if (ogs_global_conf()->parameter.no_pcrf == 0) - pcrf_thread = test_child_create("pcrf", argv_out); + pcrf_thread = test_child_create("pcrf", 0, argv_out); if (ogs_global_conf()->parameter.no_upf == 0) - upf_thread = test_child_create("upf", argv_out); + upf_thread = test_child_create("upf", 0, argv_out); if (ogs_global_conf()->parameter.no_sgwu == 0) - sgwu_thread = test_child_create("sgwu", argv_out); + sgwu_thread = test_child_create("sgwu", 0, argv_out); if (ogs_global_conf()->parameter.no_smf == 0) - smf_thread = test_child_create("smf", argv_out); + smf_thread = test_child_create("smf", 0, argv_out); if (ogs_global_conf()->parameter.no_sgwc == 0) - sgwc_thread = test_child_create("sgwc", argv_out); + sgwc_thread = test_child_create("sgwc", 0, argv_out); if (ogs_global_conf()->parameter.no_mme == 0) - mme_thread = test_child_create("mme", argv_out); + mme_thread = test_child_create("mme", 0, argv_out); /* * Wait for all sockets listening diff --git a/tests/common/application.c b/tests/common/application.c index 8817e1749..2a3d9b7c5 100644 --- a/tests/common/application.c +++ b/tests/common/application.c @@ -85,7 +85,8 @@ static int process_num = 0; static void child_main(void *data) { - const char **commandLine = data; + thread_data_t *thread_data = data; + const char **commandLine = thread_data->commandLine; ogs_proc_t *current = NULL; FILE *out = NULL; char buf[OGS_HUGE_LEN]; @@ -93,6 +94,10 @@ static void child_main(void *data) current = &process[process_num++]; + // get name and index of NF into the process. + current->nf_name = ogs_strdup(thread_data->nf_name); + current->index = thread_data->index; + if (process_num > MAX_CHILD_PROCESS) { ogs_fatal("Process limit reached"); ogs_assert_if_reached(); @@ -118,13 +123,17 @@ static void child_main(void *data) ogs_assert(ret == 0); } -ogs_thread_t *test_child_create(const char *name, const char *const argv[]) +ogs_thread_t *test_child_create(const char *name, int index, const char *const argv[]) { ogs_thread_t *child = NULL; + thread_data_t thread_data; + const char *commandLine[OGS_ARG_MAX]; int i = 0; char command[OGS_MAX_FILEPATH_LEN]; + memset(&thread_data, 0, sizeof(thread_data)); + while(argv[i] && i < 32) { commandLine[i] = argv[i]; i++; @@ -137,7 +146,11 @@ ogs_thread_t *test_child_create(const char *name, const char *const argv[]) name, OGS_DIR_SEPARATOR_S "open5gs-", name); commandLine[0] = command; - child = ogs_thread_create(child_main, commandLine); + thread_data.commandLine = commandLine; + thread_data.nf_name = name; + thread_data.index = index; + + child = ogs_thread_create(child_main, &thread_data); ogs_msleep(50); @@ -150,6 +163,26 @@ void test_child_terminate(void) ogs_proc_t *current = NULL; for (i = 0; i < process_num; i++) { current = &process[i]; - ogs_proc_terminate(current); + + if (current->stdin_file != 0 && current->child != 0) { + ogs_proc_terminate(current); + } } } + +void test_child_terminate_with_name(char *name, int index) +{ + int i; + ogs_proc_t *current = NULL; + for (i = 0; i < process_num; i++) { + current = &process[i]; + + if (!strcmp(current->nf_name, name) && + current->index == index && + current->stdin_file != 0 && + current->child != 0) { + ogs_proc_terminate(current); + break; + } + } +} \ No newline at end of file diff --git a/tests/common/application.h b/tests/common/application.h index 2d8502ea6..2eb09b6df 100644 --- a/tests/common/application.h +++ b/tests/common/application.h @@ -28,10 +28,18 @@ extern "C" { #endif +typedef struct { + const char **commandLine; + const char *nf_name; + int index; + pid_t child; +} thread_data_t; + void test_app_run(int argc, const char *const argv[], const char *name, void (*init)(const char * const argv[])); void test_child_terminate(void); -ogs_thread_t *test_child_create(const char *name, const char *const argv[]); +void test_child_terminate_with_name(char *name, int index); +ogs_thread_t *test_child_create(const char *name, int index, const char *const argv[]); #ifdef __cplusplus } diff --git a/tests/transfer/abts-error-main.c b/tests/transfer/abts-error-main.c new file mode 100644 index 000000000..bf1f1e6cd --- /dev/null +++ b/tests/transfer/abts-error-main.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "test-app.h" + +abts_suite * test_ue_context_transfer_error_case(abts_suite *suite); + +const struct testlist { + abts_suite *(*func)(abts_suite *suite); +} alltests_error[] = { + {test_ue_context_transfer_error_case}, + {NULL}, +}; + +static void terminate(void) +{ + ogs_msleep(50); + + test_child_terminate(); + app_terminate(); + + test_5gc_final(); + ogs_app_terminate(); +} + +static void initialize(const char *const argv[]) +{ + int rv; + + rv = ogs_app_initialize(NULL, NULL, argv); + ogs_assert(rv == OGS_OK); + test_5gc_init(); + + rv = app_initialize(argv); + ogs_assert(rv == OGS_OK); +} + +int main(int argc, const char *const argv[]) +{ + int i; + abts_suite *suite = NULL; + + atexit(terminate); + test_app_run(argc, argv, "transfer-error-case.yaml", initialize); + + for (i = 0; alltests_error[i].func; i++) + suite = alltests_error[i].func(suite); + + return abts_report(suite); +} diff --git a/tests/transfer/abts-main.c b/tests/transfer/abts-main.c index 15162bd98..8a35c2c47 100644 --- a/tests/transfer/abts-main.c +++ b/tests/transfer/abts-main.c @@ -63,4 +63,4 @@ int main(int argc, const char *const argv[]) suite = alltests[i].func(suite); return abts_report(suite); -} +} \ No newline at end of file diff --git a/tests/transfer/meson.build b/tests/transfer/meson.build index 7b9efa060..f11047f09 100644 --- a/tests/transfer/meson.build +++ b/tests/transfer/meson.build @@ -20,9 +20,20 @@ testapp_transfer_sources = files(''' ue-context-transfer-test.c '''.split()) +testapp_transfer_error_sources = files(''' + abts-error-main.c + ue-context-transfer-error-case-test.c +'''.split()) + testapp_transfer_exe = executable('transfer', sources : testapp_transfer_sources, c_args : testunit_core_cc_flags, dependencies : libtest5gc_dep) +testapp_transfer_error_exe = executable('transfer-error', + sources : testapp_transfer_error_sources, + c_args : testunit_core_cc_flags, + dependencies : libtest5gc_dep) + test('transfer', testapp_transfer_exe, is_parallel : false, suite: '5gc') +test('transfer-error', testapp_transfer_error_exe, is_parallel : false, suite: '5gc') \ No newline at end of file diff --git a/tests/transfer/ue-context-transfer-error-case-test.c b/tests/transfer/ue-context-transfer-error-case-test.c new file mode 100644 index 000000000..6588c64fc --- /dev/null +++ b/tests/transfer/ue-context-transfer-error-case-test.c @@ -0,0 +1,681 @@ +/* + * Copyright (C) 2019,2020 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + #include "test-common.h" + + static void test1_func_error(abts_case *tc, void *data) + { + int rv; + ogs_socknode_t *ngap1, *ngap2; + ogs_socknode_t *gtpu1, *gtpu2; + ogs_pkbuf_t *gmmbuf; + ogs_pkbuf_t *gsmbuf; + ogs_pkbuf_t *nasbuf; + ogs_pkbuf_t *sendbuf; + ogs_pkbuf_t *recvbuf; + ogs_pkbuf_t *recvbuf_target; + ogs_ngap_message_t message; + int i; + + uint8_t tmp[OGS_HUGE_LEN]; + char *_gtp_payload = "34ff0024" + "0000000100000085 010002004500001c 0c0b000040015a7a 0a2d00010a2d0002" + "00000964cd7c291f"; + + #define NUM_OF_TEST_UE_1 1 + + + /* ERROR case simulation, when no SCP in 5G core */ + + /* Test: 1 UE, Old AMF, Target AMF, + * register to Old AMF, PDU session establishment, session release, deregister, + * old AMF is killed (main point of this test) + * send registation request to Target AMF with Old AMF's 5G GUTI, + * UE context transfer can not be finished because old AMF is not found + * new AMF sends Identity Request and registration continues + * PDU session establishment, session release, deregister + */ + + ogs_nas_5gs_mobile_identity_suci_t mobile_identity_suci; + test_ue_t *test_ue[NUM_OF_TEST_UE_1]; + test_ue_t *test_ue_target[NUM_OF_TEST_UE_1]; + test_sess_t *sess = NULL; + test_bearer_t *qos_flow = NULL; + + bson_t *doc = NULL; + + /* First gNB connects to Target AMF */ + ngap2 = testngap_client(2, AF_INET); + ABTS_PTR_NOTNULL(tc, ngap2); + + /* Second gNB connects to Old AMF */ + ngap1 = testngap_client(1, AF_INET); + ABTS_PTR_NOTNULL(tc, ngap1); + + /* Two gNB connects to UPF */ + gtpu1 = test_gtpu_server(1, AF_INET); + ABTS_PTR_NOTNULL(tc, gtpu1); + + gtpu2 = test_gtpu_server(2, AF_INET); + ABTS_PTR_NOTNULL(tc, gtpu2); + + /* NG-Setup Reqeust/Response for Target gNB - with Target AMF */ + sendbuf = testngap_build_ng_setup_request(0x4000, 28); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + recvbuf = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* NG-Setup Reqeust/Response for Source gNB - with Old AMF */ + sendbuf = testngap_build_ng_setup_request(0x4001, 28); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + recvbuf_target = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf_target); + testngap_recv(test_ue, recvbuf_target); + + /* Register to Old AMF */ + + for (i = 0; i < NUM_OF_TEST_UE_1; i++) { + const char *scheme_output[] = { + "0000000001", + "0000000002", + "0000000003", + "0000000004", + "0000000005", + }; + + /* Setup Test UE & Session Context */ + memset(&mobile_identity_suci, 0, sizeof(mobile_identity_suci)); + + mobile_identity_suci.h.supi_format = OGS_NAS_5GS_SUPI_FORMAT_IMSI; + mobile_identity_suci.h.type = OGS_NAS_5GS_MOBILE_IDENTITY_SUCI; + mobile_identity_suci.routing_indicator1 = 0; + mobile_identity_suci.routing_indicator2 = 0xf; + mobile_identity_suci.routing_indicator3 = 0xf; + mobile_identity_suci.routing_indicator4 = 0xf; + mobile_identity_suci.protection_scheme_id = OGS_PROTECTION_SCHEME_NULL; + mobile_identity_suci.home_network_pki_value = 0; + + test_ue[i] = test_ue_add_by_suci( + &mobile_identity_suci, scheme_output[i]); + ogs_assert(test_ue[i]); + + test_ue[i]->nr_cgi.cell_id = 0x40001; + + test_ue[i]->nas.registration.tsc = 0; + test_ue[i]->nas.registration.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; + test_ue[i]->nas.registration.follow_on_request = 1; + test_ue[i]->nas.registration.value = OGS_NAS_5GS_REGISTRATION_TYPE_INITIAL; + + test_ue[i]->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; + test_ue[i]->opc_string = "e8ed289deba952e4283b54e88e6183ca"; + } + + for (i = 0; i < NUM_OF_TEST_UE_1; i++) { + if (i > 0) + test_ue[i]->ran_ue_ngap_id = test_ue[i-1]->ran_ue_ngap_id; + else + test_ue[i]->ran_ue_ngap_id = 0; + + /* pdu_id == 5 */ + + /* Send PDU session establishment request */ + sess = test_sess_add_by_dnn_and_psi(test_ue[i], "internet", 5); + ogs_assert(sess); + + /********** Insert Subscriber in Database */ + doc = test_db_new_simple(test_ue[i]); + ABTS_PTR_NOTNULL(tc, doc); + ABTS_INT_EQUAL(tc, OGS_OK, test_db_insert_ue(test_ue[i], doc)); + + /* Send Registration request - with SUCI */ + // test_ue[i]->registration_request_param.guti = 1; + gmmbuf = testgmm_build_registration_request(test_ue[i], NULL, false, false); + ABTS_PTR_NOTNULL(tc, gmmbuf); + + test_ue[i]->registration_request_param.gmm_capability = 1; + test_ue[i]->registration_request_param.s1_ue_network_capability = 1; + test_ue[i]->registration_request_param.requested_nssai = 1; + test_ue[i]->registration_request_param.last_visited_registered_tai = 1; + test_ue[i]->registration_request_param.ue_usage_setting = 1; + nasbuf = testgmm_build_registration_request(test_ue[i], NULL, false, false); + ABTS_PTR_NOTNULL(tc, nasbuf); + + sendbuf = testngap_build_initial_ue_message(test_ue[i], gmmbuf, + NGAP_RRCEstablishmentCause_mo_Signalling, false, true); + ABTS_PTR_NOTNULL(tc, sendbuf); + + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + #if 0 + /* Receive Identity request */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue[i], recvbuf); + + /* Send Identity response */ + gmmbuf = testgmm_build_identity_response(test_ue[i]); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue[i], gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + #endif + + /* Receive Authentication request */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue[i], recvbuf); + + /* Send Authentication response */ + gmmbuf = testgmm_build_authentication_response(test_ue[i]); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue[i], gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Security mode command */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue[i], recvbuf); + + /* Send Security mode complete */ + gmmbuf = testgmm_build_security_mode_complete(test_ue[i], nasbuf); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue[i], gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive InitialContextSetupRequest + + * Registration accept */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue[i], recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_InitialContextSetup, + test_ue[i]->ngap_procedure_code); + + /* Send UERadioCapabilityInfoIndication */ + sendbuf = testngap_build_ue_radio_capability_info_indication(test_ue[i]); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send InitialContextSetupResponse */ + sendbuf = testngap_build_initial_context_setup_response(test_ue[i], false); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send Registration complete */ + gmmbuf = testgmm_build_registration_complete(test_ue[i]); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue[i], gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Configuration update command */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue[i], recvbuf); + + 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[i], gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive PDUSessionResourceSetupRequest + + * DL NAS transport + + * PDU session establishment accept */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue[i], recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_PDUSessionResourceSetup, + test_ue[i]->ngap_procedure_code); + + /* Send PDUSessionResourceSetupResponse */ + sendbuf = testngap_sess_build_pdu_session_resource_setup_response(sess); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + } + + for (i = 0; i < NUM_OF_TEST_UE_1; i++) { + /* Send PDU session release request */ + sess = test_sess_find_by_psi(test_ue[i], 5); + ogs_assert(sess); + + /* Send PDU Session release request */ + sess->ul_nas_transport_param.request_type = 0; + sess->ul_nas_transport_param.dnn = 0; + sess->ul_nas_transport_param.s_nssai = 0; + + sess->pdu_session_establishment_param.ssc_mode = 0; + sess->pdu_session_establishment_param.epco = 0; + + gsmbuf = testgsm_build_pdu_session_release_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[i], gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive PDUSessionResourceReleaseCommand + + * DL NAS transport + + * PDU session release command */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue[i], recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_PDUSessionResourceRelease, + test_ue[i]->ngap_procedure_code); + + /* Send PDUSessionResourceReleaseResponse */ + sendbuf = testngap_build_pdu_session_resource_release_response(sess); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send UplinkNASTransport + + * UL NAS trasnport + + * PDU session resource release complete */ + sess->ul_nas_transport_param.request_type = 0; + sess->ul_nas_transport_param.dnn = 0; + sess->ul_nas_transport_param.s_nssai = 0; + + sess->pdu_session_establishment_param.ssc_mode = 0; + sess->pdu_session_establishment_param.epco = 0; + + gsmbuf = testgsm_build_pdu_session_release_complete(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[i], gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + } + + for (i = 0; i < NUM_OF_TEST_UE_1; i++) { + /* Send De-registration request */ + gmmbuf = testgmm_build_de_registration_request(test_ue[i], 1, true, true); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue[i], gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive UEContextReleaseCommand */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue[i], recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_UEContextRelease, + test_ue[i]->ngap_procedure_code); + + /* Send UEContextReleaseComplete */ + sendbuf = testngap_build_ue_context_release_complete(test_ue[i]); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + } + + /* + * Terminate Old AMF to simulate any kind of situation, where UE + * registers with GUTI from Old AMF which is not present anymore. + * Registration must not be rejected and must continue with + * identity request + */ + + test_child_terminate_with_name("amf", 0); + ogs_msleep(300); + + /* Register to Target AMF */ + + /* Make the same UE from scratch and give it the 5G GUTI from Old AMF */ + + for (i = 0; i < NUM_OF_TEST_UE_1; i++) { + const char *scheme_output_target[] = { + "0000000001", + "0000000002", + "0000000003", + "0000000004", + "0000000005", + }; + + /* Setup Test UE & Session Context */ + memset(&mobile_identity_suci, 0, sizeof(mobile_identity_suci)); + + mobile_identity_suci.h.supi_format = OGS_NAS_5GS_SUPI_FORMAT_IMSI; + mobile_identity_suci.h.type = OGS_NAS_5GS_MOBILE_IDENTITY_SUCI; + mobile_identity_suci.routing_indicator1 = 0; + mobile_identity_suci.routing_indicator2 = 0xf; + mobile_identity_suci.routing_indicator3 = 0xf; + mobile_identity_suci.routing_indicator4 = 0xf; + mobile_identity_suci.protection_scheme_id = OGS_PROTECTION_SCHEME_NULL; + mobile_identity_suci.home_network_pki_value = 0; + + test_ue_target[i] = test_ue_add_by_suci( + &mobile_identity_suci, scheme_output_target[i]); + ogs_assert(test_ue_target[i]); + + test_ue_target[i]->nr_cgi.cell_id = 0x40000; + + test_ue_target[i]->nas.registration.tsc = 0; + test_ue_target[i]->nas.registration.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; + test_ue_target[i]->nas.registration.follow_on_request = 1; + test_ue_target[i]->nas.registration.value = OGS_NAS_5GS_REGISTRATION_TYPE_INITIAL; + + test_ue_target[i]->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; + test_ue_target[i]->opc_string = "e8ed289deba952e4283b54e88e6183ca"; + } + + for (i = 0; i < NUM_OF_TEST_UE_1; i++) { + if (i > 0) + test_ue_target[i]->ran_ue_ngap_id = test_ue_target[i-1]->ran_ue_ngap_id; + else + test_ue_target[i]->ran_ue_ngap_id = 0; + + /* pdu_id == 6 */ + + /* Send PDU session establishment request */ + sess = test_sess_add_by_dnn_and_psi(test_ue_target[i], "internet", 6); + ogs_assert(sess); + + /********** Insert Subscriber in Database */ + doc = test_db_new_simple(test_ue_target[i]); + ABTS_PTR_NOTNULL(tc, doc); + ABTS_INT_EQUAL(tc, OGS_OK, test_db_insert_ue(test_ue_target[i], doc)); + + /* Set the 5G GUTI to Old AMF's 5G GUTI */ + test_ue_target[i]->nas_5gs_guti = test_ue[i]->nas_5gs_guti; + + /* Send Registration request */ + test_ue_target[i]->registration_request_param.guti = 1; + gmmbuf = testgmm_build_registration_request(test_ue_target[i], NULL, false, false); + ABTS_PTR_NOTNULL(tc, gmmbuf); + + test_ue_target[i]->registration_request_param.gmm_capability = 1; + test_ue_target[i]->registration_request_param.s1_ue_network_capability = 1; + test_ue_target[i]->registration_request_param.requested_nssai = 1; + test_ue_target[i]->registration_request_param.last_visited_registered_tai = 1; + test_ue_target[i]->registration_request_param.ue_usage_setting = 1; + nasbuf = testgmm_build_registration_request(test_ue_target[i], NULL, false, false); + ABTS_PTR_NOTNULL(tc, nasbuf); + + sendbuf = testngap_build_initial_ue_message(test_ue_target[i], gmmbuf, + NGAP_RRCEstablishmentCause_mo_Signalling, false, true); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Identity request */ + recvbuf_target = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf_target); + testngap_recv(test_ue_target[i], recvbuf_target); + + /* Send Identity response */ + gmmbuf = testgmm_build_identity_response(test_ue_target[i]); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue_target[i], gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Authentication request */ + recvbuf_target = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf_target); + testngap_recv(test_ue_target[i], recvbuf_target); + + /* Send Authentication response */ + gmmbuf = testgmm_build_authentication_response(test_ue_target[i]); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue_target[i], gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Security mode command */ + recvbuf_target = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf_target); + testngap_recv(test_ue_target[i], recvbuf_target); + + /* Send Security mode complete */ + gmmbuf = testgmm_build_security_mode_complete(test_ue_target[i], nasbuf); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue_target[i], gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive InitialContextSetupRequest + + * Registration accept */ + recvbuf_target = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf_target); + testngap_recv(test_ue_target[i], recvbuf_target); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_InitialContextSetup, + test_ue_target[i]->ngap_procedure_code); + + /* Send UERadioCapabilityInfoIndication */ + sendbuf = testngap_build_ue_radio_capability_info_indication(test_ue_target[i]); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send InitialContextSetupResponse */ + sendbuf = testngap_build_initial_context_setup_response(test_ue_target[i], false); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send Registration complete */ + gmmbuf = testgmm_build_registration_complete(test_ue_target[i]); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue_target[i], gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Configuration update command */ + recvbuf_target = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf_target); + testngap_recv(test_ue_target[i], recvbuf_target); + + 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_target[i], gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive PDUSessionResourceSetupRequest + + * DL NAS transport + + * PDU session establishment accept */ + recvbuf_target = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf_target); + testngap_recv(test_ue_target[i], recvbuf_target); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_PDUSessionResourceSetup, + test_ue_target[i]->ngap_procedure_code); + + /* Send PDUSessionResourceSetupResponse */ + sendbuf = testngap_sess_build_pdu_session_resource_setup_response(sess); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + } + + for (i = 0; i < NUM_OF_TEST_UE_1; i++) { + /* Send PDU session release request */ + sess = test_sess_find_by_psi(test_ue_target[i], 6); + ogs_assert(sess); + + /* Send PDU Session release request */ + sess->ul_nas_transport_param.request_type = 0; + sess->ul_nas_transport_param.dnn = 0; + sess->ul_nas_transport_param.s_nssai = 0; + + sess->pdu_session_establishment_param.ssc_mode = 0; + sess->pdu_session_establishment_param.epco = 0; + + gsmbuf = testgsm_build_pdu_session_release_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_target[i], gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive PDUSessionResourceReleaseCommand + + * DL NAS transport + + * PDU session release command */ + recvbuf_target = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf_target); + testngap_recv(test_ue_target[i], recvbuf_target); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_PDUSessionResourceRelease, + test_ue_target[i]->ngap_procedure_code); + + /* Send PDUSessionResourceReleaseResponse */ + sendbuf = testngap_build_pdu_session_resource_release_response(sess); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send UplinkNASTransport + + * UL NAS trasnport + + * PDU session resource release complete */ + sess->ul_nas_transport_param.request_type = 0; + sess->ul_nas_transport_param.dnn = 0; + sess->ul_nas_transport_param.s_nssai = 0; + + sess->pdu_session_establishment_param.ssc_mode = 0; + sess->pdu_session_establishment_param.epco = 0; + + gsmbuf = testgsm_build_pdu_session_release_complete(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_target[i], gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + } + + for (i = 0; i < NUM_OF_TEST_UE_1; i++) { + /* Send De-registration request */ + gmmbuf = testgmm_build_de_registration_request(test_ue_target[i], 1, true, true); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue_target[i], gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive UEContextReleaseCommand */ + recvbuf_target = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf_target); + testngap_recv(test_ue_target[i], recvbuf_target); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_UEContextRelease, + test_ue_target[i]->ngap_procedure_code); + + /* Send UEContextReleaseComplete */ + sendbuf = testngap_build_ue_context_release_complete(test_ue_target[i]); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + } + + ogs_msleep(300); + + for (i = 0; i < NUM_OF_TEST_UE_1; i++) { + /********** Remove Subscriber in Database */ + ABTS_INT_EQUAL(tc, OGS_OK, test_db_remove_ue(test_ue_target[i])); + } + + for (i = 0; i < NUM_OF_TEST_UE_1; i++) { + /********** Remove Subscriber in Database */ + ABTS_INT_EQUAL(tc, OGS_OK, test_db_remove_ue(test_ue[i])); + } + + /* Clear Test UE Context */ + test_ue_remove_all(); + + /* gNB disonncect from UPF */ + testgnb_gtpu_close(gtpu2); + /* gNB disonncect from UPF */ + testgnb_gtpu_close(gtpu1); + + /* gNB disonncect from Target AMF */ + testgnb_ngap_close(ngap2); + /* gNB disonncect from Old AMF */ + testgnb_ngap_close(ngap1); + } + + abts_suite *test_ue_context_transfer_error_case(abts_suite *suite) + { + suite = ADD_SUITE(suite) + + abts_run_test(suite, test1_func_error, NULL); + return suite; + } + From 78bdd63984b33798844da2276b6fb3b7e6b29f8f Mon Sep 17 00:00:00 2001 From: Sukchan Lee Date: Sat, 31 May 2025 20:51:41 +0900 Subject: [PATCH 07/10] [AMF] Follow-up on #3380 --- lib/core/ogs-process.c | 20 ++++++++++++++++---- src/amf/amf-sm.c | 7 +++---- src/amf/nnrf-handler.c | 2 +- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/lib/core/ogs-process.c b/lib/core/ogs-process.c index 2b682bd6d..359882519 100644 --- a/lib/core/ogs-process.c +++ b/lib/core/ogs-process.c @@ -371,13 +371,15 @@ int ogs_proc_join(ogs_proc_t *const process, int *const out_return_code) if (process->child != waitpid(process->child, &status, 0)) { process->child = 0; - ogs_fatal("waitpid failed: %d", status); + ogs_error("waitpid failed: %d", status); return OGS_ERROR; } process->child = 0; - if (process->nf_name) + if (process->nf_name) { ogs_free(process->nf_name); + process->nf_name = NULL; + } if (out_return_code) { if (WIFEXITED(status)) { @@ -425,6 +427,10 @@ int ogs_proc_terminate(ogs_proc_t *const process) if (process->child) { if (kill(process->child, SIGTERM) == -1) { + if (errno == ESRCH) { + /* No such process */ + return OGS_OK; + } return OGS_ERROR; } } @@ -444,8 +450,14 @@ int ogs_proc_kill(ogs_proc_t *const process) return OGS_ERROR; } #else - if (kill(process->child, SIGKILL) == -1) { - return OGS_ERROR; + if (process->child) { + if (kill(process->child, SIGKILL) == -1) { + if (errno == ESRCH) { + /* No such process */ + return OGS_OK; + } + return OGS_ERROR; + } } #endif diff --git a/src/amf/amf-sm.c b/src/amf/amf-sm.c index 52edf505b..8e2f5d673 100644 --- a/src/amf/amf-sm.c +++ b/src/amf/amf-sm.c @@ -78,6 +78,9 @@ void amf_state_operational(ogs_fsm_t *s, amf_event_t *e) ogs_sbi_response_t *sbi_response = NULL; ogs_sbi_message_t sbi_message; + OpenAPI_nf_type_e requester_nf_type = OpenAPI_nf_type_NULL; + ogs_sbi_discovery_option_t *discovery_option = NULL; + amf_sm_debug(e); ogs_assert(s); @@ -720,10 +723,6 @@ void amf_state_operational(ogs_fsm_t *s, amf_event_t *e) * To avoid double-free SBI xact, * we need to check ogs_sbi_xact_find_by_id() */ - - OpenAPI_nf_type_e requester_nf_type = OpenAPI_nf_type_NULL; - ogs_sbi_discovery_option_t *discovery_option = NULL; - sbi_xact_id = OGS_POINTER_TO_UINT(e->h.sbi.data); ogs_assert(sbi_xact_id >= OGS_MIN_POOL_ID && sbi_xact_id <= OGS_MAX_POOL_ID); diff --git a/src/amf/nnrf-handler.c b/src/amf/nnrf-handler.c index 0f2e01d73..0292aae27 100644 --- a/src/amf/nnrf-handler.c +++ b/src/amf/nnrf-handler.c @@ -71,7 +71,7 @@ void amf_nnrf_handle_nf_discover( case OGS_SBI_OBJ_UE_TYPE: amf_ue = (amf_ue_t *)sbi_object; ogs_assert(amf_ue); - ogs_error("[%s] (NF discover) No [%s]", amf_ue->suci, + ogs_warn("[%s] (NF discover) No [%s]", amf_ue->suci, ogs_sbi_service_type_to_name(service_type)); /* From db1887035a2f38684f688516b4925c6d5efc07eb Mon Sep 17 00:00:00 2001 From: Sukchan Lee Date: Sun, 1 Jun 2025 13:32:32 +0900 Subject: [PATCH 08/10] [SMF] Handle missing UPF gracefully in SMF session selection (#3907) In src/smf/context.c: - Wrap UPF selection logic in a conditional that checks if pfcp_node is non-NULL. - If no UPF is available (pfcp_node == NULL), log an error and assert that sess->pfcp_node remains NULL, instead of crashing. - Only call selected_upf_node() and set up the GTP session when a prior UPF entry exists. In src/smf/gn-handler.c: - After invoking smf_sess_select_upf(), verify sess->pfcp_node. - If no UPF was selected, log an error ("No UPF available for session") and return OGS_GTP1_CAUSE_SYSTEM_FAILURE instead of asserting. In src/smf/s5c-handler.c: - Mirror the same check for sess->pfcp_node after smf_sess_select_upf(). - If no UPF is available, log an error and return OGS_GTP2_CAUSE_SYSTEM_FAILURE. - If the selected UPF is not yet PFCP-associated, log a specific error message and return OGS_GTP2_CAUSE_REMOTE_PEER_NOT_RESPONDING. These changes ensure that SMF does not abort when no UPF is configured or associated; instead, it fails the session request with an appropriate GTP cause. --- src/smf/context.c | 21 +++++++++++++-------- src/smf/gn-handler.c | 13 +++++++++++-- src/smf/npcf-handler.c | 8 +++++++- src/smf/s5c-handler.c | 12 ++++++++++-- 4 files changed, 41 insertions(+), 13 deletions(-) diff --git a/src/smf/context.c b/src/smf/context.c index 4ff1dbb2e..81c9cb3c3 100644 --- a/src/smf/context.c +++ b/src/smf/context.c @@ -1197,14 +1197,19 @@ void smf_sess_select_upf(smf_sess_t *sess) ogs_pfcp_self()->pfcp_node = ogs_list_last(&ogs_pfcp_self()->pfcp_peer_list); - /* setup GTP session with selected UPF */ - ogs_pfcp_self()->pfcp_node = - selected_upf_node(ogs_pfcp_self()->pfcp_node, sess); - ogs_assert(ogs_pfcp_self()->pfcp_node); - OGS_SETUP_PFCP_NODE(sess, ogs_pfcp_self()->pfcp_node); - ogs_debug("UE using UPF on IP %s", - ogs_sockaddr_to_string_static( - ogs_pfcp_self()->pfcp_node->addr_list)); + if (ogs_pfcp_self()->pfcp_node) { + /* setup GTP session with selected UPF */ + ogs_pfcp_self()->pfcp_node = + selected_upf_node(ogs_pfcp_self()->pfcp_node, sess); + ogs_assert(ogs_pfcp_self()->pfcp_node); + OGS_SETUP_PFCP_NODE(sess, ogs_pfcp_self()->pfcp_node); + ogs_debug("UE using UPF on IP %s", + ogs_sockaddr_to_string_static( + ogs_pfcp_self()->pfcp_node->addr_list)); + } else { + ogs_error("No suitable UPF found for session"); + ogs_assert(sess->pfcp_node == NULL); + } } smf_sess_t *smf_sess_add_by_apn(smf_ue_t *smf_ue, char *apn, uint8_t rat_type) diff --git a/src/smf/gn-handler.c b/src/smf/gn-handler.c index c4c35b6ca..afe527285 100644 --- a/src/smf/gn-handler.c +++ b/src/smf/gn-handler.c @@ -284,10 +284,19 @@ uint8_t smf_gn_handle_create_pdp_context_request( /* Select PGW based on UE Location Information */ smf_sess_select_upf(sess); + /* Check if UPF selection was successful */ + if (!sess->pfcp_node) { + ogs_error("[%s:%s] No UPF available for session", + smf_ue->imsi_bcd, sess->session.name); + return OGS_GTP1_CAUSE_SYSTEM_FAILURE; + } + /* Check if selected PGW is associated with SMF */ - ogs_assert(sess->pfcp_node); - if (!OGS_FSM_CHECK(&sess->pfcp_node->sm, smf_pfcp_state_associated)) + if (!OGS_FSM_CHECK(&sess->pfcp_node->sm, smf_pfcp_state_associated)) { + ogs_error("[%s:%s] selected UPF is not assocated with SMF", + smf_ue->imsi_bcd, sess->session.name); return OGS_GTP1_CAUSE_NO_RESOURCES_AVAILABLE; + } if ((pfcp_cause = smf_sess_set_ue_ip(sess)) != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) { cause_value = gtp_cause_from_pfcp(pfcp_cause, 1); diff --git a/src/smf/npcf-handler.c b/src/smf/npcf-handler.c index 9c5d5810a..474168d17 100644 --- a/src/smf/npcf-handler.c +++ b/src/smf/npcf-handler.c @@ -475,8 +475,14 @@ bool smf_npcf_smpolicycontrol_handle_create( /* Select UPF based on UE Location Information */ smf_sess_select_upf(sess); + /* Check if UPF selection was successful */ + if (!sess->pfcp_node) { + ogs_error("[%s:%d] No UPF available for session", + smf_ue->supi, sess->psi); + return false; + } + /* Check if selected UPF is associated with SMF */ - ogs_assert(sess->pfcp_node); if (!OGS_FSM_CHECK(&sess->pfcp_node->sm, smf_pfcp_state_associated)) { ogs_error("[%s:%d] No associated UPF", smf_ue->supi, sess->psi); return false; diff --git a/src/smf/s5c-handler.c b/src/smf/s5c-handler.c index e6fe5ad79..237ef8d1c 100644 --- a/src/smf/s5c-handler.c +++ b/src/smf/s5c-handler.c @@ -261,10 +261,18 @@ uint8_t smf_s5c_handle_create_session_request( /* Select PGW based on UE Location Information */ smf_sess_select_upf(sess); + if (!sess->pfcp_node) { + ogs_error("[%s:%s] No UPF available for session", + smf_ue->imsi_bcd, sess->session.name); + return OGS_GTP2_CAUSE_SYSTEM_FAILURE; + } + /* Check if selected PGW is associated with SMF */ - ogs_assert(sess->pfcp_node); - if (!OGS_FSM_CHECK(&sess->pfcp_node->sm, smf_pfcp_state_associated)) + if (!OGS_FSM_CHECK(&sess->pfcp_node->sm, smf_pfcp_state_associated)) { + ogs_error("[%s:%s] selected UPF is not assocated with SMF", + smf_ue->imsi_bcd, sess->session.name); return OGS_GTP2_CAUSE_REMOTE_PEER_NOT_RESPONDING; + } /* UE IP Address */ paa = req->pdn_address_allocation.data; From 2daa44adab762c47a8cef69cc984946973a845b3 Mon Sep 17 00:00:00 2001 From: Sukchan Lee Date: Sun, 1 Jun 2025 15:44:00 +0900 Subject: [PATCH 09/10] [SMF] Include N2 ACK for unchanged tunnel on repeated PathSwitchRequest (#3909) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix missing N2 signaling when tunnel information is unchanged, causing AMF crash on repeated PathSwitchRequest When a second PathSwitchRequest arrives without any tunnel changes, the handler previously returned HTTP 204 No Content and omitted N2 information. This led to a fatal assertion in the AMF SM context, since it expected to receive updated N2 data even when the tunnel remained the same. This patch modifies ngap_handle_path_switch_request_transfer to build and send the N2 SM buffer in the “else” branch. It calls ngap_build_path_switch_request_ack_transfer to construct the Path Switch Request Acknowledge N2 message and then delivers it with smf_sbi_send_sm_context_updated_data_n2smbuf. A new test case is also added to verify that N2 signaling is correctly transmitted when tunnel parameters have not changed. --- src/smf/ngap-handler.c | 8 +- tests/handover/5gc-xn-test.c | 342 ++++++++++++++++++++++++++++++++++- 2 files changed, 346 insertions(+), 4 deletions(-) diff --git a/src/smf/ngap-handler.c b/src/smf/ngap-handler.c index 793c25268..c57c65ad5 100644 --- a/src/smf/ngap-handler.c +++ b/src/smf/ngap-handler.c @@ -489,8 +489,12 @@ int ngap_handle_path_switch_request_transfer( OGS_PFCP_MODIFY_XN_HANDOVER|OGS_PFCP_MODIFY_END_MARKER, 0)); } else { - /* ACTIVATED Is NOT Included in RESPONSE */ - ogs_assert(true == ogs_sbi_send_http_status_no_content(stream)); + ogs_pkbuf_t *n2smbuf = + ngap_build_path_switch_request_ack_transfer(sess); + ogs_assert(n2smbuf); + + smf_sbi_send_sm_context_updated_data_n2smbuf(sess, stream, + OpenAPI_n2_sm_info_type_PATH_SWITCH_REQ_ACK, n2smbuf); } rv = OGS_OK; diff --git a/tests/handover/5gc-xn-test.c b/tests/handover/5gc-xn-test.c index 330639e32..d8299056b 100644 --- a/tests/handover/5gc-xn-test.c +++ b/tests/handover/5gc-xn-test.c @@ -19,7 +19,7 @@ #include "test-common.h" -static void test1_func(abts_case *tc, void *data) +static void test_two_qos_flows(abts_case *tc, void *data) { int rv; ogs_socknode_t *ngap1, *ngap2; @@ -436,11 +436,349 @@ static void test1_func(abts_case *tc, void *data) test_ue_remove(test_ue); } +static void test_keep_tunnel(abts_case *tc, void *data) +{ + int rv; + ogs_socknode_t *ngap1, *ngap2; + ogs_socknode_t *gtpu1, *gtpu2; + ogs_pkbuf_t *gmmbuf; + ogs_pkbuf_t *gsmbuf; + ogs_pkbuf_t *nasbuf; + ogs_pkbuf_t *sendbuf; + ogs_pkbuf_t *recvbuf; + ogs_ngap_message_t message; + int i; + + uint8_t tmp[OGS_HUGE_LEN]; + char *_gtp_payload = "34ff0024" + "0000000100000085 010002004500001c 0c0b000040015a7a 0a2d00010a2d0002" + "00000964cd7c291f"; + + 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; + + /* Setup Test UE & Session Context */ + memset(&mobile_identity_suci, 0, sizeof(mobile_identity_suci)); + + mobile_identity_suci.h.supi_format = OGS_NAS_5GS_SUPI_FORMAT_IMSI; + mobile_identity_suci.h.type = OGS_NAS_5GS_MOBILE_IDENTITY_SUCI; + mobile_identity_suci.routing_indicator1 = 0; + mobile_identity_suci.routing_indicator2 = 0xf; + mobile_identity_suci.routing_indicator3 = 0xf; + mobile_identity_suci.routing_indicator4 = 0xf; + mobile_identity_suci.protection_scheme_id = OGS_PROTECTION_SCHEME_NULL; + mobile_identity_suci.home_network_pki_value = 0; + + test_ue = test_ue_add_by_suci(&mobile_identity_suci, "0000203190"); + ogs_assert(test_ue); + + test_ue->nr_cgi.cell_id = 0x40001; + + test_ue->nas.registration.tsc = 0; + test_ue->nas.registration.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; + test_ue->nas.registration.follow_on_request = 1; + test_ue->nas.registration.value = OGS_NAS_5GS_REGISTRATION_TYPE_INITIAL; + + test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; + test_ue->opc_string = "e8ed289deba952e4283b54e88e6183ca"; + + /* Two gNB connects to AMF */ + ngap1 = testngap_client(1, AF_INET); + ABTS_PTR_NOTNULL(tc, ngap1); + + ngap2 = testngap_client(1, AF_INET); + ABTS_PTR_NOTNULL(tc, ngap2); + + /* Two gNB connects to UPF */ + gtpu1 = test_gtpu_server(1, AF_INET); + ABTS_PTR_NOTNULL(tc, gtpu1); + + gtpu2 = test_gtpu_server(2, AF_INET); + ABTS_PTR_NOTNULL(tc, gtpu2); + + /* NG-Setup Reqeust/Response for Source gNB */ + sendbuf = testngap_build_ng_setup_request(0x4000, 28); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* NG-Setup Reqeust/Response for Target gNB */ + sendbuf = testngap_build_ng_setup_request(0x4001, 28); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + recvbuf = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /********** 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)); + + /* 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(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Identity request */ + recvbuf = testgnb_ngap_read(ngap1); + 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(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Authentication request */ + recvbuf = testgnb_ngap_read(ngap1); + 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(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Security mode command */ + recvbuf = testgnb_ngap_read(ngap1); + 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(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive InitialContextSetupRequest + + * Registration accept */ + recvbuf = testgnb_ngap_read(ngap1); + 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(ngap1, 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(ngap1, 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(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Configuration update command */ + recvbuf = testgnb_ngap_read(ngap1); + 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(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive PDUSessionResourceSetupRequest + + * DL NAS transport + + * PDU session establishment accept */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_PDUSessionResourceSetup, + test_ue->ngap_procedure_code); + + /* Send GTP-U ICMP Packet */ + qos_flow = test_qos_flow_find_by_qfi(sess, 1); + ogs_assert(qos_flow); + rv = test_gtpu_send_ping(gtpu1, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send PDUSessionResourceSetupResponse */ + sendbuf = testngap_sess_build_pdu_session_resource_setup_response(sess); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send GTP-U ICMP Packet */ + rv = test_gtpu_send_ping(gtpu1, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send Path Switch Request */ + test_ue->nr_cgi.cell_id = 0x40002; + test_ue->ran_ue_ngap_id++; + sess->gnb_n3_addr = test_self()->gnb2_addr; + + sendbuf = testngap_build_path_switch_request(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive End Mark */ + recvbuf = test_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Receive Path Switch Ack */ + recvbuf = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send GTP-U ICMP Packet */ + qos_flow = test_qos_flow_find_by_qfi(sess, 1); + ogs_assert(qos_flow); + rv = test_gtpu_send_ping(gtpu2, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu2); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send Path Switch Request */ + test_ue->nr_cgi.cell_id = 0x40001; + test_ue->ran_ue_ngap_id++; +#if 0 /* Use the same gNB N3 Address */ + sess->gnb_n3_addr = test_self()->gnb1_addr; +#endif + + sendbuf = testngap_build_path_switch_request(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Path Switch Ack */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send GTP-U ICMP Packet */ + qos_flow = test_qos_flow_find_by_qfi(sess, 1); + ogs_assert(qos_flow); + rv = test_gtpu_send_ping(gtpu2, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu2); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* 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(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive UEContextReleaseCommand */ + recvbuf = testgnb_ngap_read(ngap1); + 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(ngap1, 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)); + + /* Two gNB disonncect from UPF */ + testgnb_gtpu_close(gtpu1); + testgnb_gtpu_close(gtpu2); + + /* Two gNB disonncect from AMF */ + testgnb_ngap_close(ngap1); + testgnb_ngap_close(ngap2); + + /* Clear Test UE Context */ + test_ue_remove(test_ue); +} + abts_suite *test_5gc_xn(abts_suite *suite) { suite = ADD_SUITE(suite) - abts_run_test(suite, test1_func, NULL); + abts_run_test(suite, test_two_qos_flows, NULL); + abts_run_test(suite, test_keep_tunnel, NULL); return suite; } From 9f5d133657850e6167231527514ee1364d37a884 Mon Sep 17 00:00:00 2001 From: Sukchan Lee Date: Sun, 1 Jun 2025 16:17:12 +0900 Subject: [PATCH 10/10] [AMF/MME] Prevent AMF/MME crash when UE context is deleted (#3910) Prevent crashes when UE context is missing in AMF and MME by replacing direct assertions with conditional checks and error logging. Removed unconditional ogs_assert(ran_ue) in AMF's GMM handlers and ogs_assert(enb_ue) in MME's EMM handlers. Now, if the UE context lookup returns NULL, log an error (including SUPI/IMSI, NAS message type, and IDs), dump the NAS packet in hex for debugging, and exit the handler gracefully instead of aborting. --- src/amf/gmm-sm.c | 64 ++++++++++++++++++++++++++++++++++++++++++------ src/mme/emm-sm.c | 48 +++++++++++++++++++++++++++++++++--- 2 files changed, 101 insertions(+), 11 deletions(-) diff --git a/src/amf/gmm-sm.c b/src/amf/gmm-sm.c index e7cacf05c..82b3dd250 100644 --- a/src/amf/gmm-sm.c +++ b/src/amf/gmm-sm.c @@ -1311,8 +1311,6 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e, nas_message = e->nas.message; ogs_assert(nas_message); - ran_ue = ran_ue_find_by_id(amf_ue->ran_ue_id); - ogs_assert(ran_ue); ran_ue = ran_ue_find_by_id(amf_ue->ran_ue_id); if (!ran_ue) { ogs_error("No NG Context SUPI[%s] NAS-Type[%d] " @@ -1753,7 +1751,15 @@ void gmm_state_authentication(ogs_fsm_t *s, amf_event_t *e) ogs_assert(nas_message); ran_ue = ran_ue_find_by_id(amf_ue->ran_ue_id); - ogs_assert(ran_ue); + if (!ran_ue) { + ogs_error("No NG Context SUPI[%s] NAS-Type[%d] " + "RAN-UE-ID[%d:%p]", + amf_ue->supi, nas_message->gmm.h.message_type, + amf_ue->ran_ue_id, ran_ue_find_by_id(amf_ue->ran_ue_id)); + ogs_assert(e->pkbuf); + ogs_log_hexdump(OGS_LOG_ERROR, e->pkbuf->data, e->pkbuf->len); + break; + } h.type = e->nas.type; @@ -2112,7 +2118,15 @@ void gmm_state_security_mode(ogs_fsm_t *s, amf_event_t *e) ogs_assert(nas_message); ran_ue = ran_ue_find_by_id(amf_ue->ran_ue_id); - ogs_assert(ran_ue); + if (!ran_ue) { + ogs_error("No NG Context SUPI[%s] NAS-Type[%d] " + "RAN-UE-ID[%d:%p]", + amf_ue->supi, nas_message->gmm.h.message_type, + amf_ue->ran_ue_id, ran_ue_find_by_id(amf_ue->ran_ue_id)); + ogs_assert(e->pkbuf); + ogs_log_hexdump(OGS_LOG_ERROR, e->pkbuf->data, e->pkbuf->len); + break; + } h.type = e->nas.type; @@ -2657,7 +2671,15 @@ void gmm_state_initial_context_setup(ogs_fsm_t *s, amf_event_t *e) ogs_assert(nas_message); ran_ue = ran_ue_find_by_id(amf_ue->ran_ue_id); - ogs_assert(ran_ue); + if (!ran_ue) { + ogs_error("No NG Context SUPI[%s] NAS-Type[%d] " + "RAN-UE-ID[%d:%p]", + amf_ue->supi, nas_message->gmm.h.message_type, + amf_ue->ran_ue_id, ran_ue_find_by_id(amf_ue->ran_ue_id)); + ogs_assert(e->pkbuf); + ogs_log_hexdump(OGS_LOG_ERROR, e->pkbuf->data, e->pkbuf->len); + break; + } h.type = e->nas.type; @@ -2951,7 +2973,15 @@ void gmm_state_exception(ogs_fsm_t *s, amf_event_t *e) ogs_assert(nas_message); ran_ue = ran_ue_find_by_id(amf_ue->ran_ue_id); - ogs_assert(ran_ue); + if (!ran_ue) { + ogs_error("No NG Context SUPI[%s] NAS-Type[%d] " + "RAN-UE-ID[%d:%p]", + amf_ue->supi, nas_message->gmm.h.message_type, + amf_ue->ran_ue_id, ran_ue_find_by_id(amf_ue->ran_ue_id)); + ogs_assert(e->pkbuf); + ogs_log_hexdump(OGS_LOG_ERROR, e->pkbuf->data, e->pkbuf->len); + break; + } h.type = e->nas.type; @@ -3069,7 +3099,27 @@ void gmm_state_exception(ogs_fsm_t *s, amf_event_t *e) ogs_assert(sbi_message); ran_ue_t *ran_ue = ran_ue_find_by_id(amf_ue->ran_ue_id); - ogs_assert(ran_ue); + if (!ran_ue) { + int i; + ogs_error("No NG Context SUPI[%s] Status[%d] RAN-UE-ID[%d:%p]", + amf_ue->supi, sbi_message->res_status, + amf_ue->ran_ue_id, ran_ue_find_by_id(amf_ue->ran_ue_id)); + + if (sbi_message->h.method) + ogs_error(" h.method[%s]", sbi_message->h.method); + if (sbi_message->h.uri) + ogs_error(" h.uri[%s]", sbi_message->h.uri); + if (sbi_message->h.service.name) + ogs_error(" h.service.name[%s]", + sbi_message->h.service.name); + if (sbi_message->h.api.version) + ogs_error(" h.api.version[%s]", sbi_message->h.api.version); + for (i = 0; i < OGS_SBI_MAX_NUM_OF_RESOURCE_COMPONENT && + sbi_message->h.resource.component[i]; i++) + ogs_error(" h.resource.component[%s:%d]", + sbi_message->h.resource.component[i], i); + break; + } SWITCH(sbi_message->h.service.name) CASE(OGS_SBI_SERVICE_NAME_NAUSF_AUTH) diff --git a/src/mme/emm-sm.c b/src/mme/emm-sm.c index 725bdcb3c..764106b9c 100644 --- a/src/mme/emm-sm.c +++ b/src/mme/emm-sm.c @@ -1106,7 +1106,17 @@ void emm_state_authentication(ogs_fsm_t *s, mme_event_t *e) ogs_assert(message); enb_ue = enb_ue_find_by_id(mme_ue->enb_ue_id); - ogs_assert(enb_ue); + if (!enb_ue) { + ogs_error("No S1 Context IMSI[%s] NAS-Type[%d] " + "ENB-UE-ID[%d:%d][%p:%p]", + mme_ue->imsi_bcd, message->emm.h.message_type, + e->enb_ue_id, mme_ue->enb_ue_id, + enb_ue_find_by_id(e->enb_ue_id), + enb_ue_find_by_id(mme_ue->enb_ue_id)); + ogs_assert(e->pkbuf); + ogs_log_hexdump(OGS_LOG_ERROR, e->pkbuf->data, e->pkbuf->len); + break; + } switch (message->emm.h.message_type) { case OGS_NAS_EPS_AUTHENTICATION_RESPONSE: @@ -1288,7 +1298,17 @@ void emm_state_security_mode(ogs_fsm_t *s, mme_event_t *e) ogs_assert(message); enb_ue = enb_ue_find_by_id(mme_ue->enb_ue_id); - ogs_assert(enb_ue); + if (!enb_ue) { + ogs_error("No S1 Context IMSI[%s] NAS-Type[%d] " + "ENB-UE-ID[%d:%d][%p:%p]", + mme_ue->imsi_bcd, message->emm.h.message_type, + e->enb_ue_id, mme_ue->enb_ue_id, + enb_ue_find_by_id(e->enb_ue_id), + enb_ue_find_by_id(mme_ue->enb_ue_id)); + ogs_assert(e->pkbuf); + ogs_log_hexdump(OGS_LOG_ERROR, e->pkbuf->data, e->pkbuf->len); + break; + } if (message->emm.h.security_header_type == OGS_NAS_SECURITY_HEADER_FOR_SERVICE_REQUEST_MESSAGE) { @@ -1528,7 +1548,17 @@ void emm_state_initial_context_setup(ogs_fsm_t *s, mme_event_t *e) ogs_assert(message); enb_ue = enb_ue_find_by_id(mme_ue->enb_ue_id); - ogs_assert(enb_ue); + if (!enb_ue) { + ogs_error("No S1 Context IMSI[%s] NAS-Type[%d] " + "ENB-UE-ID[%d:%d][%p:%p]", + mme_ue->imsi_bcd, message->emm.h.message_type, + e->enb_ue_id, mme_ue->enb_ue_id, + enb_ue_find_by_id(e->enb_ue_id), + enb_ue_find_by_id(mme_ue->enb_ue_id)); + ogs_assert(e->pkbuf); + ogs_log_hexdump(OGS_LOG_ERROR, e->pkbuf->data, e->pkbuf->len); + break; + } xact_count = mme_ue_xact_count(mme_ue, OGS_GTP_LOCAL_ORIGINATOR); @@ -1820,7 +1850,17 @@ void emm_state_exception(ogs_fsm_t *s, mme_event_t *e) ogs_assert(message); enb_ue = enb_ue_find_by_id(mme_ue->enb_ue_id); - ogs_assert(enb_ue); + if (!enb_ue) { + ogs_error("No S1 Context IMSI[%s] NAS-Type[%d] " + "ENB-UE-ID[%d:%d][%p:%p]", + mme_ue->imsi_bcd, message->emm.h.message_type, + e->enb_ue_id, mme_ue->enb_ue_id, + enb_ue_find_by_id(e->enb_ue_id), + enb_ue_find_by_id(mme_ue->enb_ue_id)); + ogs_assert(e->pkbuf); + ogs_log_hexdump(OGS_LOG_ERROR, e->pkbuf->data, e->pkbuf->len); + break; + } h.type = e->nas_type;