diff --git a/configs/attach.yaml.in b/configs/attach.yaml.in new file mode 100644 index 000000000..6c174c9e4 --- /dev/null +++ b/configs/attach.yaml.in @@ -0,0 +1,335 @@ +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 + +mme: + freeDiameter: + identity: mme.localdomain + realm: localdomain + listen_on: 127.0.0.2 + 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: hss.localdomain + address: 127.0.0.8 + + s1ap: + server: + - address: 127.0.0.2 + gtpc: + server: + - address: 127.0.0.2 + client: + sgwc: + - address: 127.0.0.3 + smf: + - address: 127.0.0.4 + metrics: + server: + - address: 127.0.0.2 + port: 9090 + gummei: + - plmn_id: + mcc: 999 + mnc: 70 + mme_gid: 2 + mme_code: 1 + tai: + - plmn_id: + mcc: 999 + mnc: 70 + tac: 1 + security: + integrity_order : [ EIA2, EIA1, EIA0 ] + ciphering_order : [ EEA0, EEA1, EEA2 ] + network_name: + full: Open5GS + time: + t3412: + value: 540 + +sgwc: + gtpc: + server: + - address: 127.0.0.3 + pfcp: + server: + - address: 127.0.0.3 + client: + sgwu: + - address: 127.0.0.6 + +smf: +# sbi: +# server: +# - address: 127.0.0.4 +# port: 7777 +# client: +# scp: +# - uri: http://127.0.0.200: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: + scp: + - uri: http://127.0.0.200: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 + 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 + +sgwu: + pfcp: + server: + - address: 127.0.0.6 + gtpu: + server: + - address: 127.0.0.6 + +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 + +hss: + freeDiameter: + identity: hss.localdomain + realm: localdomain + listen_on: 127.0.0.8 + 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: mme.localdomain + address: 127.0.0.2 +pcrf: + freeDiameter: + identity: pcrf.localdomain + realm: localdomain + listen_on: 127.0.0.9 + 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: smf.localdomain + address: 127.0.0.4 + +nrf: + sbi: + server: + - address: 127.0.0.10 + port: 7777 + +scp: + sbi: + server: + - address: 127.0.0.200 + port: 7777 + client: + nrf: + - uri: http://127.0.0.10:7777 + +ausf: + sbi: + server: + - address: 127.0.0.11 + port: 7777 + client: + scp: + - uri: http://127.0.0.200: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: + scp: + - uri: http://127.0.0.200:7777 + +pcf: + sbi: + server: + - address: 127.0.0.13 + port: 7777 + client: + scp: + - uri: http://127.0.0.200:7777 + metrics: + server: + - address: 127.0.0.13 + port: 9090 + +nssf: + sbi: + server: + - address: 127.0.0.14 + port: 7777 + client: + scp: + - uri: http://127.0.0.200:7777 + nsi: + - uri: http://127.0.0.10:7777 + s_nssai: + sst: 1 +bsf: + sbi: + server: + - address: 127.0.0.15 + port: 7777 + client: + scp: + - uri: http://127.0.0.200:7777 + +udr: + sbi: + server: + - address: 127.0.0.20 + port: 7777 + client: + scp: + - uri: http://127.0.0.200:7777 diff --git a/configs/meson.build b/configs/meson.build index dd31e810e..a4f1b618e 100644 --- a/configs/meson.build +++ b/configs/meson.build @@ -32,6 +32,7 @@ conf_data.set('build_subprojects_freeDiameter_extensions_dir', example_conf = ''' sample.yaml + attach.yaml 310014.yaml csfb.yaml volte.yaml diff --git a/debian/changelog b/debian/changelog index 8983f34e3..9c0e886ef 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,39 @@ +open5gs (2.7.5) unstable; urgency=medium + + * Bug Fixed + + -- Sukchan Lee Sun, 30 Mar 2025 22:01:17 +0900 + +open5gs (2.7.5~oracular) oracular; urgency=medium + + * Bug Fixed + + -- Sukchan Lee Sun, 30 Mar 2025 22:00:22 +0900 + +open5gs (2.7.5~noble) noble; urgency=medium + + * Bug Fixed + + -- Sukchan Lee Sun, 30 Mar 2025 21:59:27 +0900 + +open5gs (2.7.5~jammy) jammy; urgency=medium + + * Bug Fixed + + -- Sukchan Lee Sun, 30 Mar 2025 21:58:02 +0900 + +open5gs (2.7.5~focal) focal; urgency=medium + + * Bug Fixed + + -- Sukchan Lee Sun, 30 Mar 2025 21:56:59 +0900 + +open5gs (2.7.5~bionic) bionic; urgency=medium + + * Bug Fixed + + -- Sukchan Lee Sun, 30 Mar 2025 21:55:48 +0900 + open5gs (2.7.2) unstable; urgency=medium * Bug Fixed diff --git a/docs/_docs/guide/01-quickstart.md b/docs/_docs/guide/01-quickstart.md index 05ad59b28..f47f75110 100644 --- a/docs/_docs/guide/01-quickstart.md +++ b/docs/_docs/guide/01-quickstart.md @@ -80,14 +80,14 @@ Import the public key used by the package management system. ```bash $ sudo apt update $ sudo apt install gnupg -$ curl -fsSL https://pgp.mongodb.com/server-6.0.asc | sudo gpg -o /usr/share/keyrings/mongodb-server-6.0.gpg --dearmor +$ curl -fsSL https://pgp.mongodb.com/server-8.0.asc | sudo gpg -o /usr/share/keyrings/mongodb-server-8.0.gpg --dearmor ``` -Create the list file /etc/apt/sources.list.d/mongodb-org-6.0.list for your version of Ubuntu. +Create the list file /etc/apt/sources.list.d/mongodb-org-8.0.list for your version of Ubuntu. On ubuntu 22.04 (Jammy) ```bash -$ echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-6.0.gpg] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/6.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list +$ echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/8.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list ``` Install the MongoDB packages. diff --git a/docs/_docs/guide/02-building-open5gs-from-sources.md b/docs/_docs/guide/02-building-open5gs-from-sources.md index 6b5de348f..c4f2baf00 100644 --- a/docs/_docs/guide/02-building-open5gs-from-sources.md +++ b/docs/_docs/guide/02-building-open5gs-from-sources.md @@ -18,14 +18,14 @@ Import the public key used by the package management system. ```bash $ sudo apt update $ sudo apt install gnupg -$ curl -fsSL https://pgp.mongodb.com/server-6.0.asc | sudo gpg -o /usr/share/keyrings/mongodb-server-6.0.gpg --dearmor +$ curl -fsSL https://pgp.mongodb.com/server-8.0.asc | sudo gpg -o /usr/share/keyrings/mongodb-server-8.0.gpg --dearmor ``` -Create the list file /etc/apt/sources.list.d/mongodb-org-6.0.list for your version of Ubuntu. +Create the list file /etc/apt/sources.list.d/mongodb-org-8.0.list for your version of Ubuntu. On ubuntu 22.04 (Jammy) ```bash -$ echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-6.0.gpg] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/6.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list +$ echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/8.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list ``` Install the MongoDB packages. diff --git a/docs/_docs/hardware/01-genodebs.md b/docs/_docs/hardware/01-genodebs.md index c9b0eadd0..bb35e04f1 100644 --- a/docs/_docs/hardware/01-genodebs.md +++ b/docs/_docs/hardware/01-genodebs.md @@ -55,6 +55,7 @@ If you have tested radio hardware from a vendor not listed with Open5GS, please * Nokia FW2FA Flexi Zone Mini-Macro Outdoor BTS, 2x20w Band 39 * Nokia FWGR Flexi Zone Mini-Macro Outdoor BTS, 2x20w Band 1 * Nokia FWHG Flexi Zone Indoor Pico BTS, 2x250 mW Band 7 + * Nokia FW2HHD Flexi Zone Multiband Indoor Pico BTS, Band 38/41(S/W TLS18SP_ENB) * Ruckus Q710 and Q910 ### 4G/5G Software Stacks + SDRs diff --git a/docs/_docs/tutorial/01-your-first-lte.md b/docs/_docs/tutorial/01-your-first-lte.md index 29c8584cb..029b27d98 100644 --- a/docs/_docs/tutorial/01-your-first-lte.md +++ b/docs/_docs/tutorial/01-your-first-lte.md @@ -165,8 +165,8 @@ $ make test The Open5GS package is available on the recent versions of *Ubuntu*. ```bash # Install the MongoDB Packages -$ curl -fsSL https://pgp.mongodb.com/server-6.0.asc | sudo gpg -o /usr/share/keyrings/mongodb-server-6.0.gpg --dearmor -$ echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-6.0.gpg] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/6.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list +$ curl -fsSL https://pgp.mongodb.com/server-8.0.asc | sudo gpg -o /usr/share/keyrings/mongodb-server-8.0.gpg --dearmor +$ echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/8.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list $ sudo apt update $ sudo apt install mongodb-org diff --git a/docs/_posts/2025-03-30-release-v2.7.5.md b/docs/_posts/2025-03-30-release-v2.7.5.md new file mode 100644 index 000000000..7e1b089a0 --- /dev/null +++ b/docs/_posts/2025-03-30-release-v2.7.5.md @@ -0,0 +1,50 @@ +--- +title: "v2.7.5 - Bug fixed" +date: 2025-03-30 22:05:00 +0900 +categories: + - Release +tags: + - News + - Release +head_inline: "" +--- + +# Open5GS Release Note Summary + +This release introduces numerous improvements and bug fixes across core network components, enhancing overall stability, performance, and security. Below is a concise overview of the key updates: + +## Session & Subscription Management +- **SMF Enhancements:** + Improved handling of SDM subscriptions to UDM during the PDU session lifetime. +- **Optional PLMN-ID:** + Added support for an optional PLMN-ID parameter in SDM GET queries (AMF/SMF). +- **5GMM Cause Reporting:** + AMF now sends a 5GMM cause in the request to SMF when initiating a session release. + +## Mobility & Authentication Improvements +- **Mobility Fixes:** + Corrected QoS values for 2G to 4G mobility and fixed issues with UE context release and cell reselection. +- **Security & Authentication:** + Enhanced UE authentication processes, prevented crashes during security mode command failures, and implemented HSS selection improvements. + +## Interface & Protocol Enhancements +- **Diameter & PFCP:** + - Integrated statistics into the main loop for Diameter interfaces, with enhanced debug logging for HSS and PCRF. + - Addressed multiple PFCP issues, including memory management, header handling, and correct TEID restoration. +- **NAS Module:** + Fixed a heap-buffer overflow vulnerability in NAS message decoding. + +## SBI and Other Module Updates +- **SBI Enhancements:** + Enabled custom User-Agent header information for HTTP/2 requests, support for custom port numbers, and direct NRF communication. +- **Additional Fixes:** + - Resolved UE context handling issues during handovers and state transitions. + - Implemented various cosmetic fixes, typo corrections, and documentation updates. + - Introduced support for new parameters and refined subscription conditions. + +Overall, this release emphasizes improved network reliability, enhanced security measures, and better support for 5G core operations while also addressing legacy issues. + +See [Release Note](https://github.com/open5gs/open5gs/releases/tag/v2.7.5) + +Download -- [v2.7.5.tar.gz](https://github.com/open5gs/open5gs/archive/v2.7.5.tar.gz) +{: .notice--info} diff --git a/lib/app/ogs-config.c b/lib/app/ogs-config.c index 44b0f3875..605ff192c 100644 --- a/lib/app/ogs-config.c +++ b/lib/app/ogs-config.c @@ -1310,7 +1310,7 @@ ogs_app_policy_conf_t *ogs_app_policy_conf_add( } ogs_app_policy_conf_t *ogs_app_policy_conf_find( - char *supi, ogs_plmn_id_t *plmn_id) + const char *supi, const ogs_plmn_id_t *plmn_id) { ogs_app_policy_conf_t *policy_conf; int i; @@ -1386,7 +1386,7 @@ void ogs_app_policy_conf_remove_all(void) } ogs_app_slice_conf_t *ogs_app_slice_conf_add( - ogs_app_policy_conf_t *policy_conf, ogs_s_nssai_t *s_nssai) + ogs_app_policy_conf_t *policy_conf, const ogs_s_nssai_t *s_nssai) { ogs_app_slice_conf_t *slice_conf = NULL; @@ -1416,7 +1416,7 @@ ogs_app_slice_conf_t *ogs_app_slice_conf_add( } ogs_app_slice_conf_t *ogs_app_slice_conf_find_by_s_nssai( - ogs_app_policy_conf_t *policy_conf, ogs_s_nssai_t *s_nssai) + ogs_app_policy_conf_t *policy_conf, const ogs_s_nssai_t *s_nssai) { ogs_app_slice_conf_t *slice_conf = NULL; @@ -1486,7 +1486,7 @@ int ogs_app_check_policy_conf(void) } ogs_app_session_conf_t *ogs_app_session_conf_add( - ogs_app_slice_conf_t *slice_conf, char *name) + ogs_app_slice_conf_t *slice_conf, const char *name) { ogs_app_session_conf_t *session_conf = NULL; @@ -1518,7 +1518,7 @@ ogs_app_session_conf_t *ogs_app_session_conf_add( return session_conf; } ogs_app_session_conf_t *ogs_app_session_conf_find_by_dnn( - ogs_app_slice_conf_t *slice_conf, char *name) + ogs_app_slice_conf_t *slice_conf, const char *name) { ogs_app_session_conf_t *session_conf = NULL; @@ -1561,7 +1561,8 @@ void ogs_app_session_conf_remove_all(ogs_app_slice_conf_t *slice_conf) } int ogs_app_config_session_data( - char *supi, ogs_plmn_id_t *plmn_id, ogs_s_nssai_t *s_nssai, char *dnn, + const char *supi, const ogs_plmn_id_t *plmn_id, + const ogs_s_nssai_t *s_nssai, const char *dnn, ogs_session_data_t *session_data) { ogs_app_policy_conf_t *policy_conf = NULL; diff --git a/lib/app/ogs-config.h b/lib/app/ogs-config.h index 13c30b8c4..b2cf131d4 100644 --- a/lib/app/ogs-config.h +++ b/lib/app/ogs-config.h @@ -208,27 +208,28 @@ int ogs_app_parse_session_conf( ogs_app_policy_conf_t *ogs_app_policy_conf_add( ogs_supi_range_t *supi_range, ogs_plmn_id_t *plmn_id); ogs_app_policy_conf_t *ogs_app_policy_conf_find( - char *supi, ogs_plmn_id_t *plmn_id); + const char *supi, const ogs_plmn_id_t *plmn_id); void ogs_app_policy_conf_remove(ogs_app_policy_conf_t *policy_conf); void ogs_app_policy_conf_remove_all(void); ogs_app_slice_conf_t *ogs_app_slice_conf_add( - ogs_app_policy_conf_t *policy_conf, ogs_s_nssai_t *s_nssai); + ogs_app_policy_conf_t *policy_conf, const ogs_s_nssai_t *s_nssai); ogs_app_slice_conf_t *ogs_app_slice_conf_find_by_s_nssai( - ogs_app_policy_conf_t *policy_conf, ogs_s_nssai_t *s_nssai); + ogs_app_policy_conf_t *policy_conf, const ogs_s_nssai_t *s_nssai); void ogs_app_slice_conf_remove(ogs_app_slice_conf_t *slice_conf); void ogs_app_slice_conf_remove_all(ogs_app_policy_conf_t *policy_conf); ogs_app_session_conf_t *ogs_app_session_conf_add( - ogs_app_slice_conf_t *slice_conf, char *name); + ogs_app_slice_conf_t *slice_conf, const char *name); ogs_app_session_conf_t *ogs_app_session_conf_find_by_dnn( - ogs_app_slice_conf_t *slice_conf, char *name); + ogs_app_slice_conf_t *slice_conf, const char *name); void ogs_app_session_conf_remove(ogs_app_session_conf_t *session_conf); void ogs_app_session_conf_remove_all( ogs_app_slice_conf_t *slice_conf); int ogs_app_config_session_data( - char *supi, ogs_plmn_id_t *plmn_id, ogs_s_nssai_t *s_nssai, char *dnn, + const char *supi, const ogs_plmn_id_t *plmn_id, + const ogs_s_nssai_t *s_nssai, const char *dnn, ogs_session_data_t *session_data); #ifdef __cplusplus diff --git a/lib/core/ogs-epoll.c b/lib/core/ogs-epoll.c index 326ae2c0c..ca210ab83 100644 --- a/lib/core/ogs-epoll.c +++ b/lib/core/ogs-epoll.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -232,22 +232,7 @@ static int epoll_process(ogs_pollset_t *pollset, ogs_time_t timeout) received = context->event_list[i].events; if (received & EPOLLERR) { - /* - * The libevent library has OGS_POLLOUT turned on in EPOLLERR. - * - * However, SIGPIPE can occur if write() is called - * when the peer connection is closed. - * - * Therefore, Open5GS turns off OGS_POLLOUT - * so that write() cannot be called in case of EPOLLERR. - * - * See also #2411 and #2312 - */ -#if 0 when = OGS_POLLIN|OGS_POLLOUT; -#else - when = OGS_POLLIN; -#endif } else if ((received & EPOLLHUP) && !(received & EPOLLRDHUP)) { when = OGS_POLLIN|OGS_POLLOUT; } else { diff --git a/lib/core/ogs-signal.c b/lib/core/ogs-signal.c index 3e51b4cdd..6b1acfac1 100644 --- a/lib/core/ogs-signal.c +++ b/lib/core/ogs-signal.c @@ -15,7 +15,7 @@ */ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -277,9 +277,20 @@ static void remove_sync_sigs(sigset_t *sig_mask) #ifdef SIGIOT sigdelset(sig_mask, SIGIOT); #endif + +/* + * SIGPIPE can occur if write() is called when the peer connection is closed. + * + * Therefore, Open5GS ignore SIGPIE signal + * + * See also #2411 and #2312 + */ +#if 0 #ifdef SIGPIPE sigdelset(sig_mask, SIGPIPE); #endif +#endif + #ifdef SIGSEGV sigdelset(sig_mask, SIGSEGV); #endif diff --git a/lib/dbi/session.c b/lib/dbi/session.c index 5ebc7822d..ea80883d2 100644 --- a/lib/dbi/session.c +++ b/lib/dbi/session.c @@ -19,7 +19,8 @@ #include "ogs-dbi.h" -int ogs_dbi_session_data(char *supi, ogs_s_nssai_t *s_nssai, char *dnn, +int ogs_dbi_session_data( + const char *supi, const ogs_s_nssai_t *s_nssai, const char *dnn, ogs_session_data_t *session_data) { int rv = OGS_OK; diff --git a/lib/dbi/session.h b/lib/dbi/session.h index 17e4ddd3e..727dda5ff 100644 --- a/lib/dbi/session.h +++ b/lib/dbi/session.h @@ -28,7 +28,8 @@ extern "C" { #endif -int ogs_dbi_session_data(char *supi, ogs_s_nssai_t *s_nssai, char *dnn, +int ogs_dbi_session_data( + const char *supi, const ogs_s_nssai_t *s_nssai, const char *dnn, ogs_session_data_t *session_data); #ifdef __cplusplus diff --git a/lib/diameter/s6a/message.h b/lib/diameter/s6a/message.h index 28d8fa984..56258f74c 100644 --- a/lib/diameter/s6a/message.h +++ b/lib/diameter/s6a/message.h @@ -36,6 +36,7 @@ extern "C" { #define OGS_DIAM_S6A_AVP_CODE_ALL_APN_CONFIG_INC_IND (1428) #define OGS_DIAM_S6A_AVP_CODE_APN_CONFIGURATION (1430) #define OGS_DIAM_S6A_AVP_CODE_MIP_HOME_AGENT_ADDRESS (334) +#define OGS_DIAM_S6A_AVP_CODE_MIP_HOME_AGENT_HOST (348) #define OGS_DIAM_S6A_AVP_CODE_SERVED_PARTY_IP_ADDRESS (848) #define OGS_DIAM_S6A_RAT_TYPE_WLAN 0 diff --git a/lib/metrics/prometheus/context.c b/lib/metrics/prometheus/context.c index 48bed6c4d..9972c2892 100644 --- a/lib/metrics/prometheus/context.c +++ b/lib/metrics/prometheus/context.c @@ -209,6 +209,7 @@ static _MHD_Result mhd_server_access_handler(void *cls, struct MHD_Connection *c if (strcmp(url, "/metrics") == 0) { buf = prom_collector_registry_bridge(PROM_COLLECTOR_REGISTRY_DEFAULT); rsp = MHD_create_response_from_buffer(strlen(buf), (void *)buf, MHD_RESPMEM_MUST_FREE); + MHD_add_response_header(rsp, "Content-Type", "text/plain; version=0.0.4; charset=utf-8"); ret = MHD_queue_response(connection, MHD_HTTP_OK, rsp); MHD_destroy_response(rsp); return ret; diff --git a/lib/nas/common/conv.c b/lib/nas/common/conv.c index fe57c4abf..4ce56e879 100644 --- a/lib/nas/common/conv.c +++ b/lib/nas/common/conv.c @@ -131,56 +131,66 @@ int ogs_nas_gprs_timer_3_from_sec( gprs_timer->unit = OGS_NAS_GPRS_TIMER_3_UNIT_MULTIPLES_OF_2_SS; gprs_timer->value = timer_value / 2; } else { - if (timer_value%60 != 0) { - ogs_error("Not multiples of 1 minute"); + if (timer_value%30 != 0) { + ogs_error("Not multiples of 30 seconds"); return OGS_ERROR; } - timer_value /= 60; /* multiples of 1 minute */ + timer_value /= 30; /* multiples of 30 seconds */ if (timer_value <= 31) { - gprs_timer->unit = OGS_NAS_GPRS_TIMER_3_UNIT_MULTIPLES_OF_1_MM; + gprs_timer->unit = OGS_NAS_GPRS_TIMER_3_UNIT_MULTIPLES_OF_30_SS; gprs_timer->value = timer_value; } else { - if (timer_value%10 != 0) { - ogs_error("Not multiples of decihours(= 10 minutes)"); + if (timer_value%2 != 0) { + ogs_error("Not multiples of 1 minute"); return OGS_ERROR; } - timer_value /= 10; /* multiples of decihours = 10 mintues */ + timer_value /= 2; /* multiples of 1 minute */ if (timer_value <= 31) { - gprs_timer->unit = OGS_NAS_GPRS_TIMER_3_UNIT_MULTIPLES_OF_10_MM; + gprs_timer->unit = OGS_NAS_GPRS_TIMER_3_UNIT_MULTIPLES_OF_1_MM; gprs_timer->value = timer_value; } else { - if (timer_value%6 != 0) { - ogs_error("Not multiples of 1 hour"); + if (timer_value%10 != 0) { + ogs_error("Not multiples of decihours(= 10 minutes)"); return OGS_ERROR; } - timer_value /= 6; /* multiples of 1 hour */ + timer_value /= 10; /* multiples of decihours = 10 mintues */ if (timer_value <= 31) { - gprs_timer->unit = - OGS_NAS_GPRS_TIMER_3_UNIT_MULTIPLES_OF_1_HH; + gprs_timer->unit = OGS_NAS_GPRS_TIMER_3_UNIT_MULTIPLES_OF_10_MM; gprs_timer->value = timer_value; } else { - if (timer_value%10 != 0) { - ogs_error("Not multiples of 10 hours"); + if (timer_value%6 != 0) { + ogs_error("Not multiples of 1 hour"); return OGS_ERROR; } - timer_value /= 10; /* multiples of 10 hours */ + timer_value /= 6; /* multiples of 1 hour */ if (timer_value <= 31) { gprs_timer->unit = - OGS_NAS_GPRS_TIMER_3_UNIT_MULTIPLES_OF_10_HH; + OGS_NAS_GPRS_TIMER_3_UNIT_MULTIPLES_OF_1_HH; gprs_timer->value = timer_value; } else { - if (timer_value%32 != 0) { + if (timer_value%10 != 0) { ogs_error("Not multiples of 10 hours"); return OGS_ERROR; } - timer_value /= 32; /* multiples of 320 hours */ + timer_value /= 10; /* multiples of 10 hours */ if (timer_value <= 31) { gprs_timer->unit = - OGS_NAS_GPRS_TIMER_3_UNIT_MULTIPLES_OF_320_HH; + OGS_NAS_GPRS_TIMER_3_UNIT_MULTIPLES_OF_10_HH; gprs_timer->value = timer_value; } else { - ogs_error("Overflow!"); - return OGS_ERROR; + if (timer_value%32 != 0) { + ogs_error("Not multiples of 10 hours"); + return OGS_ERROR; + } + timer_value /= 32; /* multiples of 320 hours */ + if (timer_value <= 31) { + gprs_timer->unit = + OGS_NAS_GPRS_TIMER_3_UNIT_MULTIPLES_OF_320_HH; + gprs_timer->value = timer_value; + } else { + ogs_error("Overflow!"); + return OGS_ERROR; + } } } } diff --git a/lib/pfcp/context.c b/lib/pfcp/context.c index 687e075b1..f69ee0506 100644 --- a/lib/pfcp/context.c +++ b/lib/pfcp/context.c @@ -1081,6 +1081,7 @@ int ogs_pfcp_node_merge(ogs_pfcp_node_t *node, ogs_freeaddrinfo(node->addr_list); node->addr_list = tmp_list; node->last_dns_refresh = now; + node->current_addr = NULL; tmp_list = NULL; } } @@ -1355,14 +1356,30 @@ ogs_pfcp_pdr_t *ogs_pfcp_pdr_find_or_add( return pdr; } -void ogs_pfcp_pdr_swap_teid(ogs_pfcp_pdr_t *pdr) +int ogs_pfcp_pdr_swap_teid(ogs_pfcp_pdr_t *pdr) { int i = 0; ogs_assert(pdr); + ogs_assert(pdr->f_teid_len > 0); ogs_assert(!pdr->f_teid.ch); - ogs_assert(pdr->f_teid.teid > 0 && - pdr->f_teid.teid <= ogs_pfcp_pdr_teid_pool.size); + + /* + * Issues #3747, #3574 + * + * This code validates the F-TEID (Fully encapsulated TEID) information + * element within a PDR structure before further processing the PFCP + * message. The validation ensures that the F-TEID is present and + * within acceptable limits defined by the system. + */ + if (pdr->f_teid.teid > 0 && + pdr->f_teid.teid <= ogs_pfcp_pdr_teid_pool.size) { + /* PASS OK */ + } else { + ogs_error("PDR-ID[%d] F-TEID LEN[%d] TEID[0x%x]", + pdr->id, pdr->f_teid_len, pdr->f_teid.teid); + return OGS_PFCP_CAUSE_MANDATORY_IE_INCORRECT; + } /* Find out the Array Index for the restored TEID. */ i = pdr_random_to_index[pdr->f_teid.teid]; @@ -1378,6 +1395,8 @@ void ogs_pfcp_pdr_swap_teid(ogs_pfcp_pdr_t *pdr) ogs_pfcp_pdr_teid_pool.array[i] = *(pdr->teid_node); *(pdr->teid_node) = pdr->f_teid.teid; } + + return OGS_PFCP_CAUSE_REQUEST_ACCEPTED; } void ogs_pfcp_object_teid_hash_set( @@ -2328,7 +2347,7 @@ ogs_pfcp_dev_t *ogs_pfcp_dev_add(const char *ifname) ogs_assert(dev); memset(dev, 0, sizeof *dev); - strcpy(dev->ifname, ifname); + ogs_cpystrn(dev->ifname, ifname, OGS_MAX_IFNAME_LEN-1); ogs_list_add(&self.dev_list, dev); @@ -2434,7 +2453,7 @@ ogs_pfcp_subnet_t *ogs_pfcp_subnet_add( } if (dnn) - strcpy(subnet->dnn, dnn); + ogs_cpystrn(subnet->dnn, dnn, OGS_MAX_DNN_LEN); ogs_pool_init(&subnet->pool, ogs_app()->pool.sess); diff --git a/lib/pfcp/context.h b/lib/pfcp/context.h index 70b253be3..cb97e7e23 100644 --- a/lib/pfcp/context.h +++ b/lib/pfcp/context.h @@ -436,7 +436,7 @@ ogs_pfcp_pdr_t *ogs_pfcp_pdr_find( ogs_pfcp_pdr_t *ogs_pfcp_pdr_find_or_add( ogs_pfcp_sess_t *sess, ogs_pfcp_pdr_id_t id); -void ogs_pfcp_pdr_swap_teid(ogs_pfcp_pdr_t *pdr); +int ogs_pfcp_pdr_swap_teid(ogs_pfcp_pdr_t *pdr); void ogs_pfcp_object_teid_hash_set( ogs_pfcp_object_type_e type, ogs_pfcp_pdr_t *pdr, diff --git a/lib/sbi/client.c b/lib/sbi/client.c index d9fef9bb9..89569d081 100644 --- a/lib/sbi/client.c +++ b/lib/sbi/client.c @@ -133,6 +133,9 @@ ogs_sbi_client_t *ogs_sbi_client_add( client->sslkeylog = ogs_strdup(ogs_sbi_self()->tls.client.sslkeylog); + if (ogs_sbi_self()->local_if) + client->local_if = ogs_strdup(ogs_sbi_self()->local_if); + ogs_debug("ogs_sbi_client_add [%s]", OpenAPI_uri_scheme_ToString(scheme)); OGS_OBJECT_REF(client); @@ -217,6 +220,8 @@ void ogs_sbi_client_remove(ogs_sbi_client_t *client) ogs_free(client->cert); if (client->sslkeylog) ogs_free(client->sslkeylog); + if (client->local_if) + ogs_free(client->local_if); if (client->fqdn) ogs_free(client->fqdn); @@ -558,6 +563,10 @@ static connection_t *connection_add( curl_easy_setopt(conn->easy, CURLOPT_RESOLVE, conn->resolve_list); } + if (client->local_if) { + curl_easy_setopt(conn->easy, CURLOPT_INTERFACE, client->local_if); + } + curl_easy_setopt(conn->easy, CURLOPT_PRIVATE, conn); curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb); curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, conn); @@ -640,11 +649,19 @@ static void connection_remove_all(ogs_sbi_client_t *client) static void connection_timer_expired(void *data) { connection_t *conn = NULL; + CURLcode res; + char *effective_url = NULL; conn = data; ogs_assert(conn); - ogs_error("Connection timer expired"); + ogs_error("Connection timer expired [METHOD:%s]", conn->method); + + res = curl_easy_getinfo(conn->easy, CURLINFO_EFFECTIVE_URL, &effective_url); + if ((res == CURLE_OK) && effective_url) + ogs_error("Effective URL: %s", effective_url); + else + ogs_error("curl_easy_getinfo() failed [%s]", curl_easy_strerror(res)); ogs_assert(conn->client_cb); conn->client_cb(OGS_TIMEUP, NULL, conn->data); diff --git a/lib/sbi/client.h b/lib/sbi/client.h index e9bfbc918..91aa1a862 100644 --- a/lib/sbi/client.h +++ b/lib/sbi/client.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -40,15 +40,15 @@ extern "C" { client = ((__cTX)->client); \ ogs_assert(client); \ if (client->fqdn) { \ - ogs_warn("NF EndPoint(fqdn) updated [%s:%d]", \ + ogs_warn("UnRef NF EndPoint(fqdn) [%s:%d]", \ client->fqdn, client->fqdn_port); \ } \ if (client->addr) { \ - ogs_warn("NF EndPoint(addr) updated [%s:%d]", \ + ogs_warn("UnRef NF EndPoint(addr) [%s:%d]", \ OGS_ADDR(client->addr, buf), OGS_PORT(client->addr)); \ } \ if (client->addr6) { \ - ogs_warn("NF EndPoint(addr6) updated [%s:%d]", \ + ogs_warn("UnRef NF EndPoint(addr6) [%s:%d]", \ OGS_ADDR(client->addr6, buf), OGS_PORT(client->addr6)); \ } \ ogs_sbi_client_remove(client); \ @@ -58,15 +58,15 @@ extern "C" { ((__cTX)->client) = (__pClient); \ ogs_debug("CLIENT Ref [%d]", (__pClient)->reference_count); \ if ((__pClient)->fqdn) { \ - ogs_info("NF EndPoint(fqdn) setup [%s:%d]", \ + ogs_info("Setup NF EndPoint(fqdn) [%s:%d]", \ (__pClient)->fqdn, (__pClient)->fqdn_port); \ } \ if ((__pClient)->addr) { \ - ogs_info("NF EndPoint(addr) setup [%s:%d]", \ + ogs_info("Setup NF EndPoint(addr) [%s:%d]", \ OGS_ADDR((__pClient)->addr, buf), OGS_PORT((__pClient)->addr)); \ } \ if ((__pClient)->addr6) { \ - ogs_info("NF EndPoint(addr6) setup [%s:%d]", \ + ogs_info("Setup NF EndPoint(addr6) [%s:%d]", \ OGS_ADDR((__pClient)->addr6, buf), \ OGS_PORT((__pClient)->addr6)); \ } \ @@ -81,6 +81,7 @@ typedef struct ogs_sbi_client_s { OpenAPI_uri_scheme_e scheme; bool insecure_skip_verify; char *cacert, *private_key, *cert, *sslkeylog; + char *local_if; char *fqdn; uint16_t fqdn_port; diff --git a/lib/sbi/context.c b/lib/sbi/context.c index f9740b5de..839ba0412 100644 --- a/lib/sbi/context.c +++ b/lib/sbi/context.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2024 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -256,7 +256,9 @@ int ogs_sbi_context_parse_config( const char *default_key = ogs_yaml_iter_key(&default_iter); ogs_assert(default_key); - if (!strcmp(default_key, "tls")) { + if (!strcmp(default_key, "interface")) { + self.local_if = ogs_yaml_iter_value(&default_iter); + } else if (!strcmp(default_key, "tls")) { ogs_yaml_iter_t tls_iter; ogs_yaml_iter_recurse(&default_iter, &tls_iter); while (ogs_yaml_iter_next(&tls_iter)) { @@ -1073,6 +1075,7 @@ ogs_sbi_client_t *ogs_sbi_context_parse_client_config(ogs_yaml_iter_t *iter) const char *client_private_key = NULL; const char *client_cert = NULL; const char *client_sslkeylog = NULL; + const char *local_if = NULL; bool rc; @@ -1116,6 +1119,8 @@ ogs_sbi_client_t *ogs_sbi_context_parse_client_config(ogs_yaml_iter_t *iter) client_cert = ogs_yaml_iter_value(iter); } else if (!strcmp(key, "client_sslkeylogfile")) { client_sslkeylog = ogs_yaml_iter_value(iter); + } else if (!strcmp(key, "interface")) { + local_if = ogs_yaml_iter_value(iter); } } @@ -1192,6 +1197,13 @@ ogs_sbi_client_t *ogs_sbi_context_parse_client_config(ogs_yaml_iter_t *iter) ogs_assert(client->sslkeylog); } + if (local_if) { + if (client->local_if) + ogs_free(client->local_if); + client->local_if = ogs_strdup(local_if); + ogs_assert(client->local_if); + } + if ((!client_private_key && client_cert) || (client_private_key && !client_cert)) { ogs_error("Either the private key or certificate is missing."); @@ -1381,7 +1393,19 @@ ogs_sbi_nf_instance_t *ogs_sbi_nf_instance_find(char *id) { ogs_sbi_nf_instance_t *nf_instance = NULL; - ogs_assert(id); + /* + * This is related to Issue #3093. + * + * We want to be able to use 'ogs_sbi_nf_instance_id_find(char *id)' + * even if the 'id' is NULL as in the use case below. + * + * ogs_sbi_nf_instance_find( + * sess->sbi.service_type_array[service_type].nf_instance_id)); + * + * To do so, we changed the 'assert(id)' to 'if (!id) return NULL', + * as shown below. + */ + if (!id) return NULL; ogs_list_for_each(&ogs_sbi_self()->nf_instance_list, nf_instance) { if (nf_instance->id && strcmp(nf_instance->id, id) == 0) @@ -2407,7 +2431,7 @@ ogs_sbi_client_t *ogs_sbi_client_find_by_service_name( } } - return nf_instance->client; + return NULL; } ogs_sbi_client_t *ogs_sbi_client_find_by_service_type( @@ -2430,10 +2454,21 @@ ogs_sbi_client_t *ogs_sbi_client_find_by_service_type( void ogs_sbi_object_free(ogs_sbi_object_t *sbi_object) { + int i; + ogs_assert(sbi_object); if (ogs_list_count(&sbi_object->xact_list)) ogs_error("SBI running [%d]", ogs_list_count(&sbi_object->xact_list)); + + for (i = 0; i < OGS_SBI_MAX_NUM_OF_SERVICE_TYPE; i++) { + if (sbi_object->service_type_array[i].nf_instance_id) + ogs_free(sbi_object->service_type_array[i].nf_instance_id); + } + for (i = 0; i < OGS_SBI_MAX_NUM_OF_NF_TYPE; i++) { + if (sbi_object->nf_type_array[i].nf_instance_id) + ogs_free(sbi_object->nf_type_array[i].nf_instance_id); + } } ogs_sbi_xact_t *ogs_sbi_xact_add( diff --git a/lib/sbi/context.h b/lib/sbi/context.h index 6d156adfd..f18986f56 100644 --- a/lib/sbi/context.h +++ b/lib/sbi/context.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2024 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -88,6 +88,8 @@ typedef struct ogs_sbi_context_s { } client; } tls; + const char *local_if; + ogs_list_t server_list; ogs_list_t client_list; @@ -201,8 +203,9 @@ typedef struct ogs_sbi_object_s { ogs_sbi_obj_type_e type; struct { - ogs_sbi_nf_instance_t *nf_instance; + char *nf_instance_id; +#if ENABLE_VALIDITY_TIMEOUT /* * Search.Result stored in nf_instance->time.validity_duration; * @@ -212,8 +215,10 @@ typedef struct ogs_sbi_object_s { * if no validityPeriod in SearchResult, validity_timeout is 0. */ ogs_time_t validity_timeout; - } service_type_array[OGS_SBI_MAX_NUM_OF_SERVICE_TYPE], - home_nsmf_pdusession; +#endif + } nf_type_array[OGS_SBI_MAX_NUM_OF_NF_TYPE], + service_type_array[OGS_SBI_MAX_NUM_OF_SERVICE_TYPE], + home_nsmf_pdusession; ogs_list_t xact_list; @@ -360,8 +365,8 @@ typedef struct ogs_sbi_sepp_info_s { } ogs_sbi_sepp_info_t; typedef struct ogs_sbi_amf_info_s { - uint8_t amf_set_id; - uint16_t amf_region_id; + uint16_t amf_set_id; + uint8_t amf_region_id; int num_of_guami; ogs_guami_t guami[OGS_MAX_NUM_OF_SERVED_GUAMI]; @@ -477,49 +482,77 @@ void ogs_sbi_client_associate(ogs_sbi_nf_instance_t *nf_instance); int ogs_sbi_default_client_port(OpenAPI_uri_scheme_e scheme); +#if ENABLE_VALIDITY_TIMEOUT #define OGS_SBI_SETUP_NF_INSTANCE(__cTX, __nFInstance) \ do { \ ogs_assert(__nFInstance); \ ogs_assert((__nFInstance)->id); \ + ogs_assert((__nFInstance)->nf_type); \ ogs_assert((__nFInstance)->t_validity); \ \ - if ((__cTX).nf_instance) { \ - ogs_warn("[%s] NF Instance updated [type:%s validity:%ds]", \ - ((__cTX).nf_instance)->id, \ - OpenAPI_nf_type_ToString(((__cTX).nf_instance)->nf_type), \ - ((__cTX).nf_instance)->time.validity_duration); \ + if ((__cTX).nf_instance_id) { \ + ogs_warn("[%s] Unlink NF Instance " \ + "[type:%s validity:%d timeout:%lds]", \ + ((__cTX).nf_instance_id), \ + OpenAPI_nf_type_ToString((__nFInstance)->nf_type), \ + (__nFInstance)->time.validity_duration, \ + (long)((__cTX).validity_timeout)); \ + ogs_free((__cTX).nf_instance_id); \ } \ \ - ((__cTX).nf_instance) = __nFInstance; \ + ((__cTX).nf_instance_id) = ogs_strdup((__nFInstance)->id); \ if ((__nFInstance)->time.validity_duration) { \ ((__cTX).validity_timeout) = (__nFInstance)->t_validity->timeout; \ } else { \ ((__cTX).validity_timeout) = 0; \ } \ - ogs_info("[%s] NF Instance setup [type:%s validity:%ds]", \ - (__nFInstance)->id, \ + ogs_info("[%s] Setup NF Instance [type:%s validity:%d timeout:%lds]", \ + ((__cTX).nf_instance_id), \ OpenAPI_nf_type_ToString((__nFInstance)->nf_type), \ - (__nFInstance)->time.validity_duration); \ + (__nFInstance)->time.validity_duration, \ + (long)((__cTX).validity_timeout)); \ } while(0) +#else +#define OGS_SBI_SETUP_NF_INSTANCE(__cTX, __nFInstance) \ + do { \ + ogs_assert(__nFInstance); \ + ogs_assert((__nFInstance)->id); \ + ogs_assert((__nFInstance)->nf_type); \ + \ + if ((__cTX).nf_instance_id) { \ + ogs_warn("[%s] Unlink NF Instance [type:%s]", \ + ((__cTX).nf_instance_id), \ + OpenAPI_nf_type_ToString((__nFInstance)->nf_type)); \ + ogs_free((__cTX).nf_instance_id); \ + } \ + \ + ((__cTX).nf_instance_id) = ogs_strdup((__nFInstance)->id); \ + ogs_info("[%s] Setup NF Instance [type:%s]", \ + ((__cTX).nf_instance_id), \ + OpenAPI_nf_type_ToString((__nFInstance)->nf_type)); \ + } while(0) +#endif /* - * Search.Result stored in nf_instance->time.validity_duration; + * Issue #3470 * - * validity_timeout = nf_instance->validity->timeout = - * ogs_get_monotonic_time() + nf_instance->time.validity_duration; + * Previously, nf_instance pointers were stored in nf_type_array and + * service_type_array. This led to a dangling pointer problem when an + * nf_instance was removed via ogs_sbi_nf_instance_remove(). * - * if no validityPeriod in SearchResult, validity_timeout is 0. + * To resolve this, we now store nf_instance_id instead, and use + * ogs_sbi_nf_instance_find(nf_instance_id) to verify the validity of an + * nf_instance. */ +#if ENABLE_VALIDITY_TIMEOUT #define OGS_SBI_GET_NF_INSTANCE(__cTX) \ ((__cTX).validity_timeout == 0 || \ (__cTX).validity_timeout > ogs_get_monotonic_time() ? \ - ((__cTX).nf_instance) : NULL) - -#define OGS_SBI_NF_INSTANCE_VALID(__nFInstance) \ - (((__nFInstance) && ((__nFInstance)->t_validity) && \ - ((__nFInstance)->time.validity_duration == 0 || \ - (__nFInstance)->t_validity->timeout > ogs_get_monotonic_time())) ? \ - true : false) + (ogs_sbi_nf_instance_find((__cTX).nf_instance_id)) : NULL) +#else +#define OGS_SBI_GET_NF_INSTANCE(__cTX) \ + ogs_sbi_nf_instance_find((__cTX).nf_instance_id) +#endif bool ogs_sbi_discovery_param_is_matched( ogs_sbi_nf_instance_t *nf_instance, diff --git a/lib/sbi/custom/links.c b/lib/sbi/custom/links.c index aa8ca1066..ea5baacfa 100644 --- a/lib/sbi/custom/links.c +++ b/lib/sbi/custom/links.c @@ -46,7 +46,8 @@ cJSON *ogs_sbi_links_convertToJSON(ogs_sbi_links_t *links) ogs_assert(linksJSON); cJSON_AddItemToObject(linksJSON, "items", itemsJSON); - cJSON_AddItemToObject(linksJSON, "self", selfJSON); + cJSON_AddItemToObject(linksJSON, "self", selfJSON); + cJSON_AddNumberToObject(linksJSON, "totalItemCount", cJSON_GetArraySize(itemsJSON)); /* root */ root = cJSON_CreateObject(); @@ -56,3 +57,57 @@ cJSON *ogs_sbi_links_convertToJSON(ogs_sbi_links_t *links) return root; } + +ogs_sbi_links_t *ogs_sbi_links_parseFromJSON(cJSON *json) +{ + ogs_sbi_links_t *links; + cJSON *_links = NULL; + cJSON *_items = NULL, *_item = NULL; + cJSON *_self = NULL; + + ogs_assert(json); + + _links = cJSON_GetObjectItemCaseSensitive(json, "_links"); + if (!_links) { + ogs_error("No _links"); + return NULL; + } + + _items = cJSON_GetObjectItemCaseSensitive(_links, "items"); + if (!_items) { + ogs_error("No items"); + return NULL; + } + + + links = ogs_malloc(sizeof(ogs_sbi_links_t)); + ogs_assert(links); + + memset(links, 0, sizeof(*links)); + links->items = OpenAPI_list_create(); + ogs_assert(links->items); + + + cJSON_ArrayForEach(_item, _items) { + cJSON *href; + char *link; + + href = cJSON_GetObjectItemCaseSensitive(_item, "href"); + if (href) { + link = cJSON_GetStringValue(href); + OpenAPI_list_add(links->items, ogs_strdup(link)); + } + } + + + _self = cJSON_GetObjectItemCaseSensitive(_links, "self"); + if (_self) { + cJSON *self_href; + + self_href = cJSON_GetObjectItemCaseSensitive(_self, "href"); + if (self_href) + links->self = ogs_strdup(cJSON_GetStringValue(self_href)); + } + + return links; +} diff --git a/lib/sbi/custom/links.h b/lib/sbi/custom/links.h index 8c87b370c..1e165a698 100644 --- a/lib/sbi/custom/links.h +++ b/lib/sbi/custom/links.h @@ -23,6 +23,7 @@ typedef struct ogs_sbi_links_s { } ogs_sbi_links_t; cJSON *ogs_sbi_links_convertToJSON(ogs_sbi_links_t *links); +ogs_sbi_links_t *ogs_sbi_links_parseFromJSON(cJSON *json); #ifdef __cplusplus } diff --git a/lib/sbi/message.c b/lib/sbi/message.c index 1ee71b6a1..51c6e8fad 100644 --- a/lib/sbi/message.c +++ b/lib/sbi/message.c @@ -63,6 +63,10 @@ void ogs_sbi_message_free(ogs_sbi_message_t *message) if (message->param.discovery_option) ogs_sbi_discovery_option_free(message->param.discovery_option); + /* Query parameters */ + for (i = 0; i < message->param.num_of_fields; i++) + ogs_free(message->param.fields[i]); + /* JSON Data */ if (message->NFProfile) OpenAPI_nf_profile_free(message->NFProfile); @@ -109,6 +113,8 @@ void ogs_sbi_message_free(ogs_sbi_message_t *message) message->Amf3GppAccessRegistrationModification); if (message->SmfRegistration) OpenAPI_smf_registration_free(message->SmfRegistration); + if (message->Nssai) + OpenAPI_nssai_free(message->Nssai); if (message->AccessAndMobilitySubscriptionData) OpenAPI_access_and_mobility_subscription_data_free( message->AccessAndMobilitySubscriptionData); @@ -219,6 +225,13 @@ void ogs_sbi_message_free(ogs_sbi_message_t *message) OpenAPI_ue_reg_status_update_req_data_free(message->UeRegStatusUpdateReqData); if (message->UeRegStatusUpdateRspData) OpenAPI_ue_reg_status_update_rsp_data_free(message->UeRegStatusUpdateRspData); + if (message->links) { + OpenAPI_clear_and_free_string_list(message->links->items); + if (message->links->self) + ogs_free(message->links->self); + + ogs_free(message->links); + } /* HTTP Part */ for (i = 0; i < message->num_of_part; i++) { @@ -706,6 +719,26 @@ ogs_sbi_request_t *ogs_sbi_build_request(ogs_sbi_message_t *message) if (homeSnssai.sd) ogs_free(homeSnssai.sd); } + if (message->param.num_of_fields) { + char *fields; + + fields = ogs_strdup(message->param.fields[0]); + if (!fields) { + ogs_error("ogs_strdup() failed"); + return NULL; + } + + for (i = 1; i < message->param.num_of_fields; i++) + fields = ogs_mstrcatf( + fields, ",%s", message->param.fields[i]); + + if (fields) { + ogs_sbi_header_set(request->http.params, + OGS_SBI_PARAM_FIELDS, fields); + ogs_free(fields); + } + + } if (message->param.ipv4addr) { ogs_sbi_header_set(request->http.params, OGS_SBI_PARAM_IPV4ADDR, message->param.ipv4addr); @@ -1094,6 +1127,30 @@ int ogs_sbi_parse_request( cJSON_Delete(item); } } + } else if (!strcmp(ogs_hash_this_key(hi), OGS_SBI_PARAM_FIELDS)) { + char *_v = ogs_hash_this_val(hi), *v = NULL; + char *token = NULL; + char *saveptr = NULL; + + v = ogs_strdup(_v); + ogs_assert(v); + + token = ogs_strtok_r(v, ",", &saveptr); + while (token != NULL) { + if (message->param.num_of_fields < OGS_SBI_MAX_NUM_OF_FIELDS) { + message->param.fields + [message->param.num_of_fields] = ogs_strdup(token); + ogs_assert(message->param.fields + [message->param.num_of_fields]); + message->param.num_of_fields++; + token = ogs_strtok_r(NULL, ",", &saveptr); + } else { + ogs_error("Fields in query exceed MAX_NUM_OF_FIELDS"); + break; + } + } + + ogs_free(v); } else if (!strcmp(ogs_hash_this_key(hi), OGS_SBI_PARAM_IPV4ADDR)) { message->param.ipv4addr = ogs_hash_this_val(hi); } else if (!strcmp(ogs_hash_this_key(hi), OGS_SBI_PARAM_IPV6PREFIX)) { @@ -1133,7 +1190,7 @@ int ogs_sbi_parse_request( ogs_sbi_parse_plmn_id( &message->param.tai.plmn_id, tai->plmn_id); message->param.tai.tac = - ogs_uint24_from_string(tai->tac); + ogs_uint24_from_string_hexadecimal(tai->tac); message->param.tai_presence = true; OpenAPI_tai_free(tai); } @@ -1401,6 +1458,9 @@ static char *build_json(ogs_sbi_message_t *message) } else if (message->SmfRegistration) { item = OpenAPI_smf_registration_convertToJSON(message->SmfRegistration); ogs_assert(item); + } else if (message->Nssai) { + item = OpenAPI_nssai_convertToJSON(message->Nssai); + ogs_assert(item); } else if (message->AccessAndMobilitySubscriptionData) { item = OpenAPI_access_and_mobility_subscription_data_convertToJSON( message->AccessAndMobilitySubscriptionData); @@ -1668,15 +1728,27 @@ static int parse_json(ogs_sbi_message_t *message, SWITCH(message->h.resource.component[0]) CASE(OGS_SBI_RESOURCE_NAME_NF_INSTANCES) - if (message->res_status < 300) { - message->NFProfile = - OpenAPI_nf_profile_parseFromJSON(item); - if (!message->NFProfile) { - rv = OGS_ERROR; - ogs_error("JSON parse error"); + if (message->h.resource.component[1]) { + if (message->res_status < 300) { + message->NFProfile = + OpenAPI_nf_profile_parseFromJSON(item); + if (!message->NFProfile) { + rv = OGS_ERROR; + ogs_error("JSON parse error"); + } + } else { + ogs_error("HTTP ERROR Status : %d", message->res_status); } } else { - ogs_error("HTTP ERROR Status : %d", message->res_status); + if (message->res_status < 300) { + message->links = ogs_sbi_links_parseFromJSON(item); + if (!message->links) { + rv = OGS_ERROR; + ogs_error("JSON parse error"); + } + } else { + ogs_error("HTTP ERROR Status : %d", message->res_status); + } } break; @@ -1907,6 +1979,18 @@ static int parse_json(ogs_sbi_message_t *message, CASE(OGS_SBI_SERVICE_NAME_NUDM_SDM) SWITCH(message->h.resource.component[1]) + CASE(OGS_SBI_RESOURCE_NAME_NSSAI) + if (message->res_status < 300) { + message->Nssai = OpenAPI_nssai_parseFromJSON(item); + if (!message->Nssai) { + rv = OGS_ERROR; + ogs_error("JSON parse error"); + } + } else { + ogs_error("HTTP ERROR Status : %d", message->res_status); + } + break; + CASE(OGS_SBI_RESOURCE_NAME_AM_DATA) if (message->res_status < 300) { message->AccessAndMobilitySubscriptionData = diff --git a/lib/sbi/message.h b/lib/sbi/message.h index 9e328337f..9d286aaac 100644 --- a/lib/sbi/message.h +++ b/lib/sbi/message.h @@ -93,6 +93,7 @@ extern "C" { #define OGS_SBI_RESOURCE_NAME_SM_DATA "sm-data" #define OGS_SBI_RESOURCE_NAME_SMF_SELECT_DATA "smf-select-data" #define OGS_SBI_RESOURCE_NAME_UE_CONTEXT_IN_SMF_DATA "ue-context-in-smf-data" +#define OGS_SBI_RESOURCE_NAME_NSSAI "nssai" #define OGS_SBI_RESOURCE_NAME_SMF_SELECTION_SUBSCRIPTION_DATA \ "smf-selection-subscription-data" #define OGS_SBI_RESOURCE_NAME_SDM_SUBSCRIPTIONS "sdm-subscriptions" @@ -352,11 +353,17 @@ extern "C" { #define OGS_SBI_PARAM_TAI "tai" #define OGS_SBI_PARAM_SLICE_INFO_REQUEST_FOR_PDU_SESSION \ "slice-info-request-for-pdu-session" +#define OGS_SBI_PARAM_FIELDS "fields" #define OGS_SBI_PARAM_IPV4ADDR "ipv4Addr" #define OGS_SBI_PARAM_IPV6PREFIX "ipv6Prefix" #define OGS_SBI_PARAM_HOME_PLMN_ID "home-plmn-id" #define OGS_SBI_PARAM_HNRF_URI "hnrf-uri" +#define OGS_SBI_PARAM_FIELDS_GPSIS "gpsis" +#define OGS_SBI_PARAM_FIELDS_SUBSCRIBED_UE_AMBR "subscribedUeAmbr" +#define OGS_SBI_PARAM_FIELDS_NSSAI "nssai" +#define OGS_SBI_MAX_NUM_OF_FIELDS 8 + #define OGS_SBI_CONTENT_JSON_TYPE \ OGS_SBI_APPLICATION_TYPE "/" OGS_SBI_APPLICATION_JSON_TYPE #define OGS_SBI_CONTENT_PROBLEM_TYPE \ @@ -411,6 +418,8 @@ extern "C" { "N5g-ddnmf_Discovery_MonitorUpdateResult" #define OGS_SBI_CALLBACK_N5G_DDNMF_DISCOVERY_MATCH_INFORMATION \ "N5g-ddnmf_Discovery_MatchInformation" +#define OGS_SBI_CALLBACK_NAMF_COMMUNICATION_ONN1N2TRANSFERFAILURE \ + "Namf_Communication_onN1N2TransferFailure" typedef struct ogs_sbi_header_s { char *method; @@ -493,6 +502,8 @@ typedef struct ogs_sbi_message_s { OpenAPI_nf_type_e nf_type; int limit; char *dnn; + int num_of_fields; + char *fields[OGS_SBI_MAX_NUM_OF_FIELDS]; /* Shared memory */ bool plmn_id_presence; @@ -536,6 +547,7 @@ typedef struct ogs_sbi_message_s { OpenAPI_amf3_gpp_access_registration_t *Amf3GppAccessRegistration; OpenAPI_amf3_gpp_access_registration_modification_t *Amf3GppAccessRegistrationModification; + OpenAPI_nssai_t *Nssai; OpenAPI_access_and_mobility_subscription_data_t *AccessAndMobilitySubscriptionData; OpenAPI_smf_selection_subscription_data_t *SmfSelectionSubscriptionData; diff --git a/lib/sbi/mhd-server.c b/lib/sbi/mhd-server.c index c2ca0a97b..76fd8e51d 100644 --- a/lib/sbi/mhd-server.c +++ b/lib/sbi/mhd-server.c @@ -32,6 +32,7 @@ static void server_final(void); static int server_start(ogs_sbi_server_t *server, int (*cb)(ogs_sbi_request_t *request, void *data)); +static void server_graceful_shutdown(ogs_sbi_server_t *server); static void server_stop(ogs_sbi_server_t *server); static bool server_send_rspmem_persistent( @@ -49,6 +50,7 @@ const ogs_sbi_server_actions_t ogs_mhd_server_actions = { server_final, server_start, + server_graceful_shutdown, server_stop, server_send_rspmem_persistent, @@ -287,6 +289,13 @@ static int server_start(ogs_sbi_server_t *server, return OGS_OK; } +static void server_graceful_shutdown(ogs_sbi_server_t *server) +{ + ogs_assert(server); + + /* No need to shutdown gracefully */ +} + static void server_stop(ogs_sbi_server_t *server) { ogs_assert(server); diff --git a/lib/sbi/nf-sm.c b/lib/sbi/nf-sm.c index 87aec7624..b457e1804 100644 --- a/lib/sbi/nf-sm.c +++ b/lib/sbi/nf-sm.c @@ -19,6 +19,93 @@ #include "ogs-sbi.h" +static void handle_nf_profile_retrieval( + char *nf_instance_id, + OpenAPI_nf_profile_t *NFProfile) +{ + ogs_sbi_nf_instance_t *nf_instance; + ogs_sbi_subscription_spec_t *subscription_spec = NULL; + bool save = false; + + ogs_assert(nf_instance_id); + ogs_assert(NFProfile); + + nf_instance = ogs_sbi_nf_instance_find(nf_instance_id); + if (nf_instance) { + /* already have this nf_instance; done */ + return; + } + + if (NF_INSTANCE_ID_IS_SELF(nf_instance_id)) { + /* don't save ourselves */ + return; + } + + nf_instance = ogs_sbi_nf_instance_add(); + ogs_assert(nf_instance); + + ogs_sbi_nf_instance_set_id(nf_instance, nf_instance_id); + + ogs_nnrf_nfm_handle_nf_profile(nf_instance, NFProfile); + + /* verify against our subscription list that we want to save this + * nf instance to our context */ + ogs_list_for_each(&ogs_sbi_self()->subscription_spec_list, subscription_spec) { + ogs_sbi_nf_service_t *nf_service = NULL; + + if (subscription_spec->subscr_cond.nf_type == nf_instance->nf_type) { + /* ok; save the nf_instance */ + save = true; + break; + } + + ogs_list_for_each(&nf_instance->nf_service_list, nf_service) { + if (subscription_spec->subscr_cond.service_name && + nf_service->name && + !strcmp(subscription_spec->subscr_cond.service_name, nf_service->name)) + { + /* ok; save the nf_instance */ + save = true; + break; + } + } + + if (save) + break; + } + + if (!save) { + ogs_sbi_nf_instance_remove(nf_instance); + } else { + ogs_sbi_nf_fsm_init(nf_instance); + ogs_info("[%s] (NRF-profile-get) NF registered", nf_instance->id); + ogs_sbi_client_associate(nf_instance); + } +} + +static void handle_nf_list_retrieval(ogs_sbi_links_t *links) +{ + ogs_sbi_header_t header; + ogs_sbi_message_t msg; + OpenAPI_lnode_t *node = NULL; + + OpenAPI_list_for_each(links->items, node) { + + memset(&header, 0, sizeof(header)); + header.uri = node->data; + + if (ogs_sbi_parse_header(&msg, &header) != OGS_OK) { + ogs_error("Cannot parse href: %s", header.uri); + continue; + } + + if (msg.h.resource.component[1]) + ogs_nnrf_nfm_send_nf_profile_get(msg.h.resource.component[1]); + + ogs_sbi_header_free(&header); + } +} + void ogs_sbi_nf_fsm_init(ogs_sbi_nf_instance_t *nf_instance) { ogs_event_t e; @@ -227,6 +314,8 @@ void ogs_sbi_nf_state_registered(ogs_fsm_t *s, ogs_event_t *e) subscription_spec->subscr_cond.nf_type, subscription_spec->subscr_cond.service_name); } + + ogs_nnrf_nfm_send_nf_list_retrieve(); } break; @@ -252,19 +341,64 @@ void ogs_sbi_nf_state_registered(ogs_fsm_t *s, ogs_event_t *e) SWITCH(message->h.resource.component[0]) CASE(OGS_SBI_RESOURCE_NAME_NF_INSTANCES) - if (message->res_status == OGS_SBI_HTTP_STATUS_NO_CONTENT || - message->res_status == OGS_SBI_HTTP_STATUS_OK) { - if (nf_instance->time.heartbeat_interval) - ogs_timer_start(nf_instance->t_no_heartbeat, - ogs_time_from_sec( - nf_instance->time.heartbeat_interval + - ogs_local_conf()->time.nf_instance. - no_heartbeat_margin)); + if (message->h.resource.component[1]) { + SWITCH(message->h.method) + CASE(OGS_SBI_HTTP_METHOD_PATCH) + if (message->res_status == OGS_SBI_HTTP_STATUS_NO_CONTENT || + message->res_status == OGS_SBI_HTTP_STATUS_OK) { + + if (nf_instance->time.heartbeat_interval) + ogs_timer_start(nf_instance->t_no_heartbeat, + ogs_time_from_sec( + nf_instance->time.heartbeat_interval + + ogs_local_conf()->time.nf_instance. + no_heartbeat_margin)); + + } else { + ogs_warn("[%s] HTTP response error [%d]", + NF_INSTANCE_ID(ogs_sbi_self()->nf_instance), + message->res_status); + OGS_FSM_TRAN(s, &ogs_sbi_nf_state_exception); + } + break; + + CASE(OGS_SBI_HTTP_METHOD_GET) + if (message->res_status == OGS_SBI_HTTP_STATUS_OK) { + if (!message->h.resource.component[1]) { + ogs_error("No NFInstanceId"); + break; + } + if (!message->NFProfile) { + ogs_error("No NFProfile"); + break; + } + handle_nf_profile_retrieval( + message->h.resource.component[1], + message->NFProfile); + } else { + ogs_warn("[%s] HTTP response error [%d]", + NF_INSTANCE_ID(ogs_sbi_self()->nf_instance), + message->res_status); + OGS_FSM_TRAN(s, &ogs_sbi_nf_state_exception); + } + break; + DEFAULT + ogs_error("Unknown method [%s]", message->h.method); + break; + END } else { - ogs_warn("[%s] HTTP response error [%d]", - NF_INSTANCE_ID(ogs_sbi_self()->nf_instance), - message->res_status); - OGS_FSM_TRAN(s, &ogs_sbi_nf_state_exception); + if (!message->links) { + ogs_warn("No links"); + break; + } + if (message->res_status != OGS_SBI_HTTP_STATUS_OK) { + ogs_warn("[%s] HTTP response error [%d]", + NF_INSTANCE_ID(ogs_sbi_self()->nf_instance), + message->res_status); + break; + } + + handle_nf_list_retrieval(message->links); } break; diff --git a/lib/sbi/nghttp2-server.c b/lib/sbi/nghttp2-server.c index 9fcbd108d..36c8f633f 100644 --- a/lib/sbi/nghttp2-server.c +++ b/lib/sbi/nghttp2-server.c @@ -30,6 +30,7 @@ static void server_final(void); static int server_start(ogs_sbi_server_t *server, int (*cb)(ogs_sbi_request_t *request, void *data)); +static void server_graceful_shutdown(ogs_sbi_server_t *server); static void server_stop(ogs_sbi_server_t *server); static bool server_send_rspmem_persistent( @@ -47,6 +48,7 @@ const ogs_sbi_server_actions_t ogs_nghttp2_server_actions = { server_final, server_start, + server_graceful_shutdown, server_stop, server_send_rspmem_persistent, @@ -442,6 +444,33 @@ static int server_start(ogs_sbi_server_t *server, return OGS_OK; } +/* Gracefully shutdown the server by sending GOAWAY to each session. */ +static void server_graceful_shutdown(ogs_sbi_server_t *server) +{ + ogs_sbi_session_t *sbi_sess = NULL; + ogs_sbi_session_t *next_sbi_sess = NULL; + int rv; + + /* Iterate over all active sessions in the server. */ + ogs_list_for_each_safe(&server->session_list, next_sbi_sess, sbi_sess) { + /* Submit a GOAWAY frame using the last stream ID. */ + rv = nghttp2_submit_goaway(sbi_sess->session, + NGHTTP2_FLAG_NONE, + sbi_sess->last_stream_id, + NGHTTP2_NO_ERROR, + NULL, 0); + if (rv != 0) { + ogs_error("nghttp2_submit_goaway() failed (%d:%s)", + rv, nghttp2_strerror(rv)); + } + + /* Send the GOAWAY frame to the client. */ + if (session_send(sbi_sess) != OGS_OK) { + ogs_error("session_send() failed during graceful shutdown"); + } + } +} + static void server_stop(ogs_sbi_server_t *server) { ogs_assert(server); diff --git a/lib/sbi/nnrf-build.c b/lib/sbi/nnrf-build.c index 84bf5c33b..8664068fd 100644 --- a/lib/sbi/nnrf-build.c +++ b/lib/sbi/nnrf-build.c @@ -839,12 +839,8 @@ static OpenAPI_smf_info_t *build_smf_info(ogs_sbi_nf_info_t *nf_info) ogs_uint24_to_0string(nf_info->smf.nr_tai[i].tac); if (!TaiItem->tac) { ogs_error("No TaiItem->tac"); - if (TaiItem) { - if (TaiItem->plmn_id) - ogs_sbi_free_plmn_id(TaiItem->plmn_id); - ogs_free(TaiItem); - } - free_smf_info(SmfInfo); + OpenAPI_tai_free(TaiItem); + OpenAPI_smf_info_free(SmfInfo); OpenAPI_list_free(TaiList); return NULL; } @@ -956,6 +952,7 @@ static OpenAPI_amf_info_t *build_amf_info(ogs_sbi_nf_info_t *nf_info) return NULL; } + /* Guami list */ guamiAmfInfoList = OpenAPI_list_create(); if (!guamiAmfInfoList) { ogs_error("No guamiAmfInfoList"); @@ -967,8 +964,8 @@ static OpenAPI_amf_info_t *build_amf_info(ogs_sbi_nf_info_t *nf_info) guamiAmfInfoItem = ogs_calloc(1, sizeof(*guamiAmfInfoItem)); if (!guamiAmfInfoItem) { - ogs_error("guamiAmfInfoItem"); - free_amf_info(AmfInfo); + ogs_error("No guamiAmfInfoItem"); + OpenAPI_amf_info_free(AmfInfo); OpenAPI_list_free(guamiAmfInfoList); return NULL; } @@ -976,23 +973,18 @@ static OpenAPI_amf_info_t *build_amf_info(ogs_sbi_nf_info_t *nf_info) guamiAmfInfoItem->plmn_id = ogs_sbi_build_plmn_id_nid(&nf_info->amf.guami[i].plmn_id); if (!guamiAmfInfoItem->plmn_id) { - ogs_error("guamiAmfInfoItem->plmn_id"); - if (guamiAmfInfoItem) - ogs_free(guamiAmfInfoItem); - free_amf_info(AmfInfo); + ogs_error("No guamiAmfInfoItem->plmn_id"); + OpenAPI_guami_free(guamiAmfInfoItem); + OpenAPI_amf_info_free(AmfInfo); OpenAPI_list_free(guamiAmfInfoList); return NULL; } guamiAmfInfoItem->amf_id = ogs_amf_id_to_string(&nf_info->amf.guami[i].amf_id); if (!guamiAmfInfoItem->amf_id) { - ogs_error("guamiAmfInfoItem->amf_id"); - if (guamiAmfInfoItem) { - if (guamiAmfInfoItem->plmn_id) - ogs_free(guamiAmfInfoItem->plmn_id); - ogs_free(guamiAmfInfoItem); - } - free_amf_info(AmfInfo); + ogs_error("No guamiAmfInfoItem->amf_id"); + OpenAPI_guami_free(guamiAmfInfoItem); + OpenAPI_amf_info_free(AmfInfo); OpenAPI_list_free(guamiAmfInfoList); return NULL; } @@ -1005,6 +997,8 @@ static OpenAPI_amf_info_t *build_amf_info(ogs_sbi_nf_info_t *nf_info) else OpenAPI_list_free(guamiAmfInfoList); + + /* TAI list */ TaiList = OpenAPI_list_create(); if (!TaiList) { ogs_error("No TaiList"); @@ -1014,7 +1008,7 @@ static OpenAPI_amf_info_t *build_amf_info(ogs_sbi_nf_info_t *nf_info) for (i = 0; i < nf_info->amf.num_of_nr_tai; i++) { TaiItem = ogs_calloc(1, sizeof(*TaiItem)); - if (!TaiList) { + if (!TaiItem) { ogs_error("No TaiItem"); free_amf_info(AmfInfo); OpenAPI_list_free(TaiList); @@ -1024,22 +1018,16 @@ static OpenAPI_amf_info_t *build_amf_info(ogs_sbi_nf_info_t *nf_info) &nf_info->amf.nr_tai[i].plmn_id); if (!TaiItem->plmn_id) { ogs_error("No TaiItem->plmn_id"); - if (TaiItem) - ogs_free(TaiItem); - free_amf_info(AmfInfo); + OpenAPI_tai_free(TaiItem); + OpenAPI_amf_info_free(AmfInfo); OpenAPI_list_free(TaiList); return NULL; } - TaiItem->tac = - ogs_uint24_to_0string(nf_info->amf.nr_tai[i].tac); + TaiItem->tac = ogs_uint24_to_0string(nf_info->amf.nr_tai[i].tac); if (!TaiItem->tac) { ogs_error("No TaiItem->tac"); - if (TaiItem) { - if (TaiItem->plmn_id) - ogs_sbi_free_plmn_id(TaiItem->plmn_id); - ogs_free(TaiItem); - } - free_amf_info(AmfInfo); + OpenAPI_tai_free(TaiItem); + OpenAPI_amf_info_free(AmfInfo); OpenAPI_list_free(TaiList); return NULL; } @@ -1052,6 +1040,8 @@ static OpenAPI_amf_info_t *build_amf_info(ogs_sbi_nf_info_t *nf_info) else OpenAPI_list_free(TaiList); + + /* TAI range list */ TaiRangeList = OpenAPI_list_create(); if (!TaiRangeList) { ogs_error("No TaiRangeList"); @@ -1696,9 +1686,6 @@ ogs_sbi_request_t *ogs_nnrf_nfm_build_status_subscribe( message.SubscriptionData = SubscriptionData; - message.http.custom.callback = - (char *)OGS_SBI_CALLBACK_NNRF_NFMANAGEMENT_NF_STATUS_NOTIFY; - request = ogs_sbi_build_request(&message); ogs_expect(request); @@ -1787,9 +1774,6 @@ ogs_sbi_request_t *ogs_nnrf_nfm_build_status_unsubscribe( message.h.method = (char *)OGS_SBI_HTTP_METHOD_DELETE; message.h.uri = subscription_data->resource_uri; - message.http.custom.callback = - (char *)OGS_SBI_CALLBACK_NNRF_NFMANAGEMENT_NF_STATUS_NOTIFY; - request = ogs_sbi_build_request(&message); ogs_expect(request); @@ -1817,6 +1801,24 @@ ogs_sbi_request_t *ogs_nnrf_nfm_build_profile_retrieve(char *nf_instance_id) return request; } +ogs_sbi_request_t *ogs_nnrf_nfm_build_nflist_retrieve(void) +{ + ogs_sbi_message_t message; + ogs_sbi_request_t *request = NULL; + + memset(&message, 0, sizeof(message)); + message.h.method = (char *)OGS_SBI_HTTP_METHOD_GET; + message.h.service.name = (char *)OGS_SBI_SERVICE_NAME_NNRF_NFM; + message.h.api.version = (char *)OGS_SBI_API_V1; + message.h.resource.component[0] = + (char *)OGS_SBI_RESOURCE_NAME_NF_INSTANCES; + + request = ogs_sbi_build_request(&message); + ogs_expect(request); + + return request; +} + ogs_sbi_request_t *ogs_nnrf_disc_build_discover( OpenAPI_nf_type_e target_nf_type, OpenAPI_nf_type_e requester_nf_type, diff --git a/lib/sbi/nnrf-build.h b/lib/sbi/nnrf-build.h index f4c65ca57..b0c6c6830 100644 --- a/lib/sbi/nnrf-build.h +++ b/lib/sbi/nnrf-build.h @@ -42,6 +42,7 @@ ogs_sbi_request_t *ogs_nnrf_nfm_build_status_update( ogs_sbi_request_t *ogs_nnrf_nfm_build_status_unsubscribe( ogs_sbi_subscription_data_t *subscription_data); ogs_sbi_request_t *ogs_nnrf_nfm_build_profile_retrieve(char *nf_instance_id); +ogs_sbi_request_t *ogs_nnrf_nfm_build_nflist_retrieve(void); ogs_sbi_request_t *ogs_nnrf_disc_build_discover( OpenAPI_nf_type_e target_nf_type, diff --git a/lib/sbi/nnrf-handler.c b/lib/sbi/nnrf-handler.c index 81c14c121..48da6db41 100644 --- a/lib/sbi/nnrf-handler.c +++ b/lib/sbi/nnrf-handler.c @@ -709,8 +709,8 @@ static void handle_amf_info( AmfInfo->amf_set_id); nf_info->amf.amf_region_id = ogs_uint64_from_string_hexadecimal( AmfInfo->amf_region_id); - GuamiList = AmfInfo->guami_list; + GuamiList = AmfInfo->guami_list; OpenAPI_list_for_each(GuamiList, node) { GuamiAmfInfoItem = node->data; if (GuamiAmfInfoItem) { @@ -741,7 +741,7 @@ static void handle_amf_info( } nr_tai = &nf_info->amf.nr_tai[nf_info->amf.num_of_nr_tai]; - ogs_assert(nr_tai); + ogs_sbi_parse_plmn_id(&nr_tai->plmn_id, TaiItem->plmn_id); nr_tai->tac = ogs_uint24_from_string_hexadecimal(TaiItem->tac); nf_info->amf.num_of_nr_tai++; diff --git a/lib/sbi/nnrf-path.c b/lib/sbi/nnrf-path.c index 3ecf5ba7e..68098ea2a 100644 --- a/lib/sbi/nnrf-path.c +++ b/lib/sbi/nnrf-path.c @@ -180,3 +180,45 @@ bool ogs_nnrf_nfm_send_nf_status_unsubscribe( return rc; } + +bool ogs_nnrf_nfm_send_nf_list_retrieve(void) +{ + bool rc; + ogs_sbi_request_t *request = NULL; + + request = ogs_nnrf_nfm_build_nflist_retrieve(); + if (!request) { + ogs_error("No Request"); + return false; + } + + rc = ogs_sbi_send_request_to_nrf( + OGS_SBI_SERVICE_TYPE_NNRF_NFM, NULL, + ogs_sbi_client_handler, request, ogs_sbi_self()->nf_instance); + ogs_expect(rc == true); + + ogs_sbi_request_free(request); + + return rc; +} + +bool ogs_nnrf_nfm_send_nf_profile_get(char *nf_instance_id) +{ + bool rc; + ogs_sbi_request_t *request = NULL; + + request = ogs_nnrf_nfm_build_profile_retrieve(nf_instance_id); + if (!request) { + ogs_error("No Request"); + return false; + } + + rc = ogs_sbi_send_request_to_nrf( + OGS_SBI_SERVICE_TYPE_NNRF_NFM, NULL, + ogs_sbi_client_handler, request, ogs_sbi_self()->nf_instance); + ogs_expect(rc == true); + + ogs_sbi_request_free(request); + + return rc; +} diff --git a/lib/sbi/nnrf-path.h b/lib/sbi/nnrf-path.h index c0986bf20..0c48ae4c5 100644 --- a/lib/sbi/nnrf-path.h +++ b/lib/sbi/nnrf-path.h @@ -37,6 +37,8 @@ bool ogs_nnrf_nfm_send_nf_status_update( ogs_sbi_subscription_data_t *subscription_data); bool ogs_nnrf_nfm_send_nf_status_unsubscribe( ogs_sbi_subscription_data_t *subscription_data); +bool ogs_nnrf_nfm_send_nf_list_retrieve(void); +bool ogs_nnrf_nfm_send_nf_profile_get(char *nf_instance_id); bool ogs_nnrf_nfm_send_to_nrf( ogs_sbi_client_t *client, ogs_sbi_client_cb_f client_cb, diff --git a/lib/sbi/path.c b/lib/sbi/path.c index 4c8c50e67..1c3e888e4 100644 --- a/lib/sbi/path.c +++ b/lib/sbi/path.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -282,7 +282,17 @@ int ogs_sbi_discover_and_send(ogs_sbi_xact_t *xact) ogs_assert(scp_client); } - /* Target NF-Instance */ +/* + * Issue #3470 + * + * Previously, nf_instance pointers were stored in nf_type_array and + * service_type_array. This led to a dangling pointer problem when an + * nf_instance was removed via ogs_sbi_nf_instance_remove(). + * + * To resolve this, we now store nf_instance_id instead, and use + * ogs_sbi_nf_instance_find(nf_instance_id) to verify the validity of an + * nf_instance. + */ nf_instance = OGS_SBI_GET_NF_INSTANCE( sbi_object->service_type_array[service_type]); ogs_debug("OGS_SBI_GET_NF_INSTANCE [nf_instance:%p,service_name:%s]", diff --git a/lib/sbi/server.c b/lib/sbi/server.c index e0bc83e8c..79ebfe231 100644 --- a/lib/sbi/server.c +++ b/lib/sbi/server.c @@ -156,6 +156,14 @@ int ogs_sbi_server_start_all( return OGS_OK; } +void ogs_sbi_server_graceful_shutdown_all(void) +{ + ogs_sbi_server_t *server = NULL, *next_server = NULL; + + ogs_list_for_each_safe(&ogs_sbi_self()->server_list, next_server, server) + ogs_sbi_server_actions.graceful_shutdown(server); +} + void ogs_sbi_server_stop_all(void) { ogs_sbi_server_t *server = NULL, *next_server = NULL; diff --git a/lib/sbi/server.h b/lib/sbi/server.h index 19db7d806..d20b30056 100644 --- a/lib/sbi/server.h +++ b/lib/sbi/server.h @@ -59,6 +59,7 @@ typedef struct ogs_sbi_server_actions_s { int (*start)(ogs_sbi_server_t *server, int (*cb)(ogs_sbi_request_t *request, void *data)); + void (*graceful_shutdown)(ogs_sbi_server_t *server); void (*stop)(ogs_sbi_server_t *server); bool (*send_rspmem_persistent)( @@ -87,6 +88,7 @@ void ogs_sbi_server_set_advertise( int ogs_sbi_server_start_all( int (*cb)(ogs_sbi_request_t *request, void *data)); +void ogs_sbi_server_graceful_shutdown_all(void); void ogs_sbi_server_stop_all(void); bool ogs_sbi_server_send_rspmem_persistent( diff --git a/meson.build b/meson.build index c54a57768..5e2663afa 100644 --- a/meson.build +++ b/meson.build @@ -16,7 +16,7 @@ # along with this program. If not, see . project('open5gs', 'c', 'cpp', - version : '2.7.2', + version : '2.7.5', license : 'AGPL-3.0-or-later', meson_version : '>= 0.43.0', default_options : [ @@ -25,7 +25,7 @@ project('open5gs', 'c', 'cpp', ], ) -libogslib_version = '2.7.2' +libogslib_version = '2.7.5' prefix = get_option('prefix') bindir = join_paths(prefix, get_option('bindir')) diff --git a/src/amf/context.c b/src/amf/context.c index c0aeb1ccb..670d61fe8 100644 --- a/src/amf/context.c +++ b/src/amf/context.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2024 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -1104,16 +1104,20 @@ int amf_context_nf_info(void) nf_info = ogs_sbi_nf_info_add( &nf_instance->nf_info_list, OpenAPI_nf_type_AMF); ogs_assert(nf_info); - nf_info->amf.amf_set_id = self.served_guami[next_new_i].amf_id.set2; - nf_info->amf.amf_region_id = self.served_guami[next_new_i].amf_id.region; + + nf_info->amf.amf_set_id = + ogs_amf_set_id(&self.served_guami[next_new_i].amf_id); + nf_info->amf.amf_region_id = + ogs_amf_region_id(&self.served_guami[next_new_i].amf_id); + next_found = false; info_i = 0; for (served_i = next_new_i; served_i < self.num_of_served_guami; served_i++) { - if (self.served_guami[served_i].amf_id.set2 == - nf_info->amf.amf_set_id && - self.served_guami[served_i].amf_id.region == - nf_info->amf.amf_region_id) { + if ((ogs_amf_set_id(&self.served_guami[served_i].amf_id) == + nf_info->amf.amf_set_id) && + (ogs_amf_region_id(&self.served_guami[served_i].amf_id) == + nf_info->amf.amf_region_id)) { nf_info->amf.guami[info_i] = self.served_guami[served_i]; nf_info->amf.num_of_guami++; info_i++; @@ -1121,21 +1125,25 @@ int amf_context_nf_info(void) if (!next_found) { int handled_i; for (handled_i = 0; handled_i < served_i; handled_i++) { - if (self.served_guami[handled_i].amf_id.set2 == - self.served_guami[served_i].amf_id.set2 && - self.served_guami[handled_i].amf_id.region == - self.served_guami[served_i].amf_id.region) { + if ((ogs_amf_set_id( + &self.served_guami[handled_i].amf_id) == + ogs_amf_set_id( + &self.served_guami[served_i].amf_id)) && + (ogs_amf_region_id( + &self.served_guami[handled_i].amf_id) == + ogs_amf_region_id( + &self.served_guami[served_i].amf_id))) { break; } - next_found = true; - next_new_i = served_i; + next_found = true; + next_new_i = served_i; } } } } - nf_info->amf.num_of_nr_tai = 0; - int i = 0, j = 0, k = 0, info_tai_i = 0; + + int i, j, k; for (i = 0; i < self.num_of_served_tai; i++) { ogs_5gs_tai0_list_t *list0 = &self.served_tai[i].list0; ogs_5gs_tai1_list_t *list1 = &self.served_tai[i].list1; @@ -1144,47 +1152,72 @@ int amf_context_nf_info(void) for (j = 0; list0->tai[j].num; j++) { for (k = 0; k < list0->tai[j].num; k++) { for (served_i = 0; served_i < info_i; served_i++) { + if (nf_info->amf.num_of_nr_tai >= OGS_MAX_NUM_OF_TAI) { + ogs_warn("Maximum number of TAI reached"); + break; + } + if (ogs_plmn_id_hexdump(&list0->tai[j].plmn_id) == - ogs_plmn_id_hexdump( - &nf_info->amf.guami[served_i].plmn_id)) { - nf_info->amf.nr_tai[info_tai_i].plmn_id = - list0->tai[j].plmn_id; - nf_info->amf.nr_tai[info_tai_i].tac = - list0->tai[j].tac[k]; + ogs_plmn_id_hexdump(&nf_info->amf.guami[served_i].plmn_id)) { + ogs_5gs_tai_t *tai = + &nf_info->amf.nr_tai[ + nf_info->amf.num_of_nr_tai]; + + tai->plmn_id = list0->tai[j].plmn_id; + tai->tac = list0->tai[j].tac[k]; + nf_info->amf.num_of_nr_tai++; - info_tai_i++; } } } } + + for (j = 0; list1->tai[j].num; j++) { - for (k = 0; k < list1->tai[j].num; k++) { - for (served_i = 0; served_i < info_i; served_i++) { - if (ogs_plmn_id_hexdump(&list1->tai[j].plmn_id) == - ogs_plmn_id_hexdump( - &nf_info->amf.guami[served_i].plmn_id)) { - nf_info->amf.nr_tai[info_tai_i].plmn_id = - list1->tai[j].plmn_id; - nf_info->amf.nr_tai[info_tai_i].tac.v = - list1->tai[j].tac.v+k; - nf_info->amf.num_of_nr_tai++; - info_tai_i++; - } + for (served_i = 0; served_i < info_i; served_i++) { + if (nf_info->amf.num_of_nr_tai_range >= OGS_MAX_NUM_OF_TAI) { + ogs_warn("Maximum number of TAI range reached"); + break; + } + + if (ogs_plmn_id_hexdump(&list1->tai[j].plmn_id) == + ogs_plmn_id_hexdump(&nf_info->amf.guami[served_i].plmn_id)) { + nf_info->amf.nr_tai_range[ + nf_info->amf.num_of_nr_tai_range].plmn_id = + list1->tai[j].plmn_id; + nf_info->amf.nr_tai_range[ + nf_info->amf.num_of_nr_tai_range].start[0].v = + list1->tai[j].tac.v; + nf_info->amf.nr_tai_range[ + nf_info->amf.num_of_nr_tai_range].end[0].v = + list1->tai[j].tac.v + list1->tai[j].num - 1; + /* Supported is only 1 TAC range per TAI */ + nf_info->amf.nr_tai_range[ + nf_info->amf.num_of_nr_tai_range].num_of_tac_range = 1; + + nf_info->amf.num_of_nr_tai_range++; } } } + if (list2->num) { for (j = 0; j < list2->num; j++) { for (served_i = 0; served_i < info_i; served_i++) { + if (nf_info->amf.num_of_nr_tai >= OGS_MAX_NUM_OF_TAI) { + ogs_warn("Maximum number of TAI reached"); + break; + } + if (ogs_plmn_id_hexdump(&list2->tai[j].plmn_id) == - ogs_plmn_id_hexdump( - &nf_info->amf.guami[served_i].plmn_id)) { - nf_info->amf.nr_tai[info_tai_i].plmn_id = - list2->tai[j].plmn_id; - nf_info->amf.nr_tai[info_tai_i].tac = - list2->tai[j].tac; + ogs_plmn_id_hexdump(&nf_info->amf.guami[served_i].plmn_id)) { + ogs_5gs_tai_t *tai = + &nf_info->amf.nr_tai[ + nf_info->amf.num_of_nr_tai]; + + tai->plmn_id = list2->tai[j].plmn_id; + tai->tac = list2->tai[j].tac; + nf_info->amf.num_of_nr_tai++; - info_tai_i++; } } } @@ -2230,20 +2263,37 @@ void source_ue_deassociate_target_ue(ran_ue_t *ran_ue) ogs_assert(source_ue->target_ue_id >= OGS_MIN_POOL_ID && source_ue->target_ue_id <= OGS_MAX_POOL_ID); - ogs_assert(target_ue->source_ue_id >= OGS_MIN_POOL_ID && - target_ue->source_ue_id <= OGS_MAX_POOL_ID); source_ue->target_ue_id = OGS_INVALID_POOL_ID; - target_ue->source_ue_id = OGS_INVALID_POOL_ID; + + if (target_ue) { + ogs_assert(target_ue->source_ue_id >= OGS_MIN_POOL_ID && + target_ue->source_ue_id <= OGS_MAX_POOL_ID); + target_ue->source_ue_id = OGS_INVALID_POOL_ID; + } else + ogs_error("Target-UE-ID [%d] has already been removed " + "(RAN_UE_S1AP_ID[%lld] AMF_UE_S1AP_ID[%lld])", + source_ue->target_ue_id, + (long long)source_ue->ran_ue_ngap_id, + (long long)source_ue->amf_ue_ngap_id); + } else if (ran_ue->source_ue_id >= OGS_MIN_POOL_ID && ran_ue->source_ue_id <= OGS_MAX_POOL_ID) { target_ue = ran_ue; source_ue = ran_ue_find_by_id(ran_ue->source_ue_id); - ogs_assert(source_ue->target_ue_id >= OGS_MIN_POOL_ID && - source_ue->target_ue_id <= OGS_MAX_POOL_ID); + if (source_ue) { + ogs_assert(source_ue->target_ue_id >= OGS_MIN_POOL_ID && + source_ue->target_ue_id <= OGS_MAX_POOL_ID); + source_ue->target_ue_id = OGS_INVALID_POOL_ID; + } else + ogs_error("Source-UE-ID [%d] has already been removed " + "(RAN_UE_S1AP_ID[%lld] AMF_UE_S1AP_ID[%lld])", + target_ue->source_ue_id, + (long long)target_ue->ran_ue_ngap_id, + (long long)target_ue->amf_ue_ngap_id); + ogs_assert(target_ue->source_ue_id >= OGS_MIN_POOL_ID && target_ue->source_ue_id <= OGS_MAX_POOL_ID); - source_ue->target_ue_id = OGS_INVALID_POOL_ID; target_ue->source_ue_id = OGS_INVALID_POOL_ID; } } @@ -2633,6 +2683,63 @@ uint8_t amf_selected_enc_algorithm(amf_ue_t *amf_ue) return 0; } +/* + * Save the sensitive (partial) context fields + * from the UE context into the memento + */ +void amf_ue_save_memento(amf_ue_t *amf_ue, amf_ue_memento_t *memento) +{ + ogs_assert(amf_ue); + ogs_assert(memento); + + memcpy(&memento->ue_security_capability, &amf_ue->ue_security_capability, + sizeof(memento->ue_security_capability)); + memcpy(&memento->ue_network_capability, &amf_ue->ue_network_capability, + sizeof(memento->ue_network_capability)); + memcpy(memento->rand, amf_ue->rand, OGS_RAND_LEN); + memcpy(memento->autn, amf_ue->autn, OGS_AUTN_LEN); + memcpy(memento->xres_star, amf_ue->xres_star, OGS_MAX_RES_LEN); + memcpy(memento->abba, amf_ue->abba, OGS_NAS_MAX_ABBA_LEN); + memento->abba_len = amf_ue->abba_len; + memcpy(memento->hxres_star, amf_ue->hxres_star, OGS_MAX_RES_LEN); + memcpy(memento->kamf, amf_ue->kamf, OGS_SHA256_DIGEST_SIZE); + memcpy(memento->knas_int, amf_ue->knas_int, OGS_SHA256_DIGEST_SIZE/2); + memcpy(memento->knas_enc, amf_ue->knas_enc, OGS_SHA256_DIGEST_SIZE/2); + memento->dl_count = amf_ue->dl_count; + memento->ul_count = amf_ue->ul_count.i32; + memcpy(memento->kgnb, amf_ue->kgnb, OGS_SHA256_DIGEST_SIZE); + memcpy(memento->nh, amf_ue->nh, OGS_SHA256_DIGEST_SIZE); + memento->selected_enc_algorithm = amf_ue->selected_enc_algorithm; + memento->selected_int_algorithm = amf_ue->selected_int_algorithm; +} + +/* Restore the sensitive context fields into the UE context */ +void amf_ue_restore_memento(amf_ue_t *amf_ue, const amf_ue_memento_t *memento) +{ + ogs_assert(amf_ue); + ogs_assert(memento); + + memcpy(&amf_ue->ue_security_capability, &memento->ue_security_capability, + sizeof(amf_ue->ue_security_capability)); + memcpy(&amf_ue->ue_network_capability, &memento->ue_network_capability, + sizeof(amf_ue->ue_network_capability)); + memcpy(amf_ue->rand, memento->rand, OGS_RAND_LEN); + memcpy(amf_ue->autn, memento->autn, OGS_AUTN_LEN); + memcpy(amf_ue->xres_star, memento->xres_star, OGS_MAX_RES_LEN); + memcpy(amf_ue->abba, memento->abba, OGS_NAS_MAX_ABBA_LEN); + amf_ue->abba_len = memento->abba_len; + memcpy(amf_ue->hxres_star, memento->hxres_star, OGS_MAX_RES_LEN); + memcpy(amf_ue->kamf, memento->kamf, OGS_SHA256_DIGEST_SIZE); + memcpy(amf_ue->knas_int, memento->knas_int, OGS_SHA256_DIGEST_SIZE/2); + memcpy(amf_ue->knas_enc, memento->knas_enc, OGS_SHA256_DIGEST_SIZE/2); + amf_ue->dl_count = memento->dl_count; + amf_ue->ul_count.i32 = memento->ul_count; + memcpy(amf_ue->kgnb, memento->kgnb, OGS_SHA256_DIGEST_SIZE); + memcpy(amf_ue->nh, memento->nh, OGS_SHA256_DIGEST_SIZE); + amf_ue->selected_enc_algorithm = memento->selected_enc_algorithm; + amf_ue->selected_int_algorithm = memento->selected_int_algorithm; +} + void amf_clear_subscribed_info(amf_ue_t *amf_ue) { int i, j; diff --git a/src/amf/context.h b/src/amf/context.h index d849ee7de..a9f7d91f7 100644 --- a/src/amf/context.h +++ b/src/amf/context.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -237,6 +237,67 @@ struct ran_ue_s { ogs_pool_id_t amf_ue_id; }; +typedef struct amf_ue_memento_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_ue_memento_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 context, 0 = disallow */ + bool can_restore_context; + + /* Memento of context fields */ + amf_ue_memento_t memento; + /* 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 @@ -1003,6 +1082,9 @@ 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_ue_save_memento(amf_ue_t *amf_ue, amf_ue_memento_t *memento); +void amf_ue_restore_memento(amf_ue_t *amf_ue, const amf_ue_memento_t *memento); + void amf_clear_subscribed_info(amf_ue_t *amf_ue); bool amf_update_allowed_nssai(amf_ue_t *amf_ue); diff --git a/src/amf/gmm-handler.c b/src/amf/gmm-handler.c index c3e87d8c0..1ab2b5f32 100644 --- a/src/amf/gmm-handler.c +++ b/src/amf/gmm-handler.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2024 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * diff --git a/src/amf/gmm-sm.c b/src/amf/gmm-sm.c index 78b79d0db..3c734d21f 100644 --- a/src/amf/gmm-sm.c +++ b/src/amf/gmm-sm.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019,2020 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -35,6 +35,24 @@ #undef OGS_LOG_DOMAIN #define OGS_LOG_DOMAIN __gmm_log_domain +#define AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s) do { \ + if ((amf_ue)->can_restore_context) { \ + /* Restore context if allowed */ \ + amf_ue_restore_memento((amf_ue), &((amf_ue)->memento)); \ + (amf_ue)->security_context_available = 1; \ + (amf_ue)->mac_failed = 0; \ + if (!OGS_FSM_CHECK(&amf_ue->sm, gmm_state_registered)) \ + OGS_FSM_TRAN((s), &gmm_state_registered); \ + ogs_warn("[%s] Failure in transaction; restoring context and " \ + "transitioning to REGISTERED.", (amf_ue)->supi); \ + } else { \ + /* Transition to exception state if not allowed */ \ + OGS_FSM_TRAN((s), &gmm_state_exception); \ + ogs_warn("[%s] Failure in transaction; no context " \ + "restoration.", (amf_ue)->supi); \ + } \ +} while (0) + typedef enum { GMM_COMMON_STATE_DEREGISTERED, GMM_COMMON_STATE_REGISTERED, @@ -43,7 +61,6 @@ typedef enum { static void common_register_state(ogs_fsm_t *s, amf_event_t *e, gmm_common_state_e state); - void gmm_state_initial(ogs_fsm_t *s, amf_event_t *e) { ogs_assert(s); @@ -1279,6 +1296,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_context = 1; + amf_ue_save_memento(amf_ue, &amf_ue->memento); + } else if (state == GMM_COMMON_STATE_DEREGISTERED) { + /* Transition from de-registered: do not restore */ + amf_ue->can_restore_context = 0; + } else + ogs_assert_if_reached(); + switch (e->h.id) { case AMF_EVENT_5GMM_MESSAGE: nas_message = e->nas.message; @@ -1321,7 +1348,7 @@ static void common_register_state(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); + AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s); break; } @@ -1382,7 +1409,7 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e, ran_ue, amf_ue, gmm_cause); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, gmm_state_exception); + AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s); break; } @@ -1395,7 +1422,7 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e, OGS_5GMM_CAUSE_NO_NETWORK_SLICES_AVAILABLE); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, gmm_state_exception); + AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s); break; } @@ -1459,7 +1486,7 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e, OGS_5GMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, gmm_state_exception); + AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s); break; } @@ -1471,7 +1498,7 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e, r = nas_5gs_send_service_reject(ran_ue, amf_ue, gmm_cause); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, gmm_state_exception); + AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s); break; } @@ -1481,7 +1508,7 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e, OGS_5GMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, gmm_state_exception); + AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s); break; } @@ -1491,7 +1518,7 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e, OGS_5GMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, gmm_state_exception); + AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s); break; } @@ -1510,7 +1537,7 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e, r = nas_5gs_send_service_reject(ran_ue, amf_ue, gmm_cause); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, gmm_state_exception); + AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s); break; } @@ -1526,7 +1553,7 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e, NGAP_CauseProtocol_semantic_error); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, gmm_state_exception); + AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s); break; } @@ -1542,7 +1569,7 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e, r = nas_5gs_send_gmm_reject(ran_ue, amf_ue, gmm_cause); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, gmm_state_exception); + AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s); break; } @@ -1551,7 +1578,7 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e, r = nas_5gs_send_gmm_reject(ran_ue, amf_ue, gmm_cause); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, gmm_state_exception); + AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s); break; } @@ -1581,7 +1608,7 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e, if (!h.integrity_protected || !SECURITY_CONTEXT_IS_VALID(amf_ue)) { ogs_error("No Security Context"); - OGS_FSM_TRAN(s, gmm_state_exception); + AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s); break; } @@ -1650,7 +1677,7 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e, case OGS_NAS_5GS_UL_NAS_TRANSPORT: if (!h.integrity_protected || !SECURITY_CONTEXT_IS_VALID(amf_ue)) { ogs_error("No Security Context"); - OGS_FSM_TRAN(s, gmm_state_exception); + AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s); break; } @@ -1726,10 +1753,12 @@ 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); + AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s); + break; } break; @@ -1740,7 +1769,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 +1822,9 @@ 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); - + AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s); break; + case OGS_NAS_5GS_REGISTRATION_REQUEST: ogs_warn("Registration request"); gmm_cause = gmm_handle_registration_request( @@ -1807,7 +1836,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); + AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s); break; } @@ -1846,7 +1875,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); + AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s); + break; } else { amf_ue->t3560.retry_count++; r = nas_5gs_send_authentication_request(amf_ue); @@ -1879,11 +1909,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); + AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s); break; } @@ -1897,7 +1928,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); + AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s); + break; } break; CASE(OGS_SBI_HTTP_METHOD_PUT) @@ -1909,7 +1941,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); + AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s); + break; } else { amf_ue->selected_int_algorithm = amf_selected_int_algorithm(amf_ue); @@ -1923,7 +1956,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); + AMF_RESTORE_CONTEXT_ON_FAILURE(amf_ue, s); break; } diff --git a/src/amf/init.c b/src/amf/init.c index fe477a024..2c5667488 100644 --- a/src/amf/init.c +++ b/src/amf/init.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -80,6 +80,9 @@ static void event_termination(void) ogs_list_for_each(&ogs_sbi_self()->nf_instance_list, nf_instance) ogs_sbi_nf_fsm_fini(nf_instance); + /* Gracefully shutdown the server by sending GOAWAY to each session. */ + ogs_sbi_server_graceful_shutdown_all(); + /* Starting holding timer */ t_termination_holding = ogs_timer_add(ogs_app()->timer_mgr, NULL, NULL); ogs_assert(t_termination_holding); diff --git a/src/amf/namf-build.c b/src/amf/namf-build.c index f4721f306..1a7ee2025 100644 --- a/src/amf/namf-build.c +++ b/src/amf/namf-build.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019,2020 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * diff --git a/src/amf/namf-handler.c b/src/amf/namf-handler.c index 2c2fc8f74..6399faa6d 100644 --- a/src/amf/namf-handler.c +++ b/src/amf/namf-handler.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * diff --git a/src/amf/ngap-sctp.c b/src/amf/ngap-sctp.c index fe74de607..e7e8a8b7a 100644 --- a/src/amf/ngap-sctp.c +++ b/src/amf/ngap-sctp.c @@ -160,14 +160,19 @@ void ngap_recv_handler(ogs_sock_t *sock) if (not->sn_assoc_change.sac_state == SCTP_COMM_UP) { ogs_debug("SCTP_COMM_UP"); - addr = ogs_calloc(1, sizeof(ogs_sockaddr_t)); - ogs_assert(addr); - memcpy(addr, &from, sizeof(ogs_sockaddr_t)); + if ((not->sn_assoc_change.sac_outbound_streams-1) >= 1) { + /* NEXT_ID(MAX >= MIN) */ + addr = ogs_calloc(1, sizeof(ogs_sockaddr_t)); + ogs_assert(addr); + memcpy(addr, &from, sizeof(ogs_sockaddr_t)); - ngap_event_push(AMF_EVENT_NGAP_LO_SCTP_COMM_UP, - sock, addr, NULL, - not->sn_assoc_change.sac_inbound_streams, - not->sn_assoc_change.sac_outbound_streams); + ngap_event_push(AMF_EVENT_NGAP_LO_SCTP_COMM_UP, + sock, addr, NULL, + not->sn_assoc_change.sac_inbound_streams, + not->sn_assoc_change.sac_outbound_streams); + } else + ogs_error("Invalid sn_assoc_change.sac_outbound_streams %d", + not->sn_assoc_change.sac_outbound_streams); } else if (not->sn_assoc_change.sac_state == SCTP_SHUTDOWN_COMP || not->sn_assoc_change.sac_state == SCTP_COMM_LOST) { diff --git a/src/amf/nnrf-handler.c b/src/amf/nnrf-handler.c index a2bac33d7..753938e9a 100644 --- a/src/amf/nnrf-handler.c +++ b/src/amf/nnrf-handler.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2022 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * diff --git a/src/amf/nsmf-build.c b/src/amf/nsmf-build.c index f7cf9c44f..e5bc208e2 100644 --- a/src/amf/nsmf-build.c +++ b/src/amf/nsmf-build.c @@ -256,6 +256,15 @@ ogs_sbi_request_t *amf_nsmf_pdusession_build_create_sm_context( message.http.accept = (char *)(OGS_SBI_CONTENT_JSON_TYPE "," OGS_SBI_CONTENT_NGAP_TYPE "," OGS_SBI_CONTENT_PROBLEM_TYPE); +/* + * Callback Header Configuration + * + * The 3gpp-Sbi-Callback HTTP header (per 3GPP TS 29.500 v17.9.0) indicates that + * a message is an asynchronous notification or callback. This header should be + * included only in HTTP POST requests that are callbacks (e.g., event or + * notification messages) and must not be added to regular service requests, + * such as registration (HTTP PUT) or subscription requests. + */ message.http.custom.callback = (char *)OGS_SBI_CALLBACK_NSMF_PDUSESSION_STATUS_NOTIFY; @@ -564,6 +573,18 @@ ogs_sbi_request_t *amf_nsmf_callback_build_n1_n2_failure_notify( message.N1N2MsgTxfrFailureNotification = &N1N2MsgTxfrFailureNotification; +/* + * Callback Header Configuration + * + * The 3gpp-Sbi-Callback HTTP header (per 3GPP TS 29.500 v17.9.0) indicates that + * a message is an asynchronous notification or callback. This header should be + * included only in HTTP POST requests that are callbacks (e.g., event or + * notification messages) and must not be added to regular service requests, + * such as registration (HTTP PUT) or subscription requests. + */ + message.http.custom.callback = + (char *)OGS_SBI_CALLBACK_NAMF_COMMUNICATION_ONN1N2TRANSFERFAILURE; + request = ogs_sbi_build_request(&message); ogs_expect(request); diff --git a/src/amf/nudm-build.c b/src/amf/nudm-build.c index db71e981d..ea58873c4 100644 --- a/src/amf/nudm-build.c +++ b/src/amf/nudm-build.c @@ -85,9 +85,6 @@ ogs_sbi_request_t *amf_nudm_uecm_build_registration( message.Amf3GppAccessRegistration = &Amf3GppAccessRegistration; - message.http.custom.callback = - (char *)OGS_SBI_CALLBACK_NUDM_UECM_DEREGISTRATION_NOTIFICATION; - request = ogs_sbi_build_request(&message); ogs_expect(request); @@ -243,9 +240,6 @@ ogs_sbi_request_t *amf_nudm_sdm_build_subscription(amf_ue_t *amf_ue, void *data) message.SDMSubscription = &SDMSubscription; - message.http.custom.callback = - (char *)OGS_SBI_CALLBACK_NUDM_SDM_NOTIFICATION; - request = ogs_sbi_build_request(&message); ogs_expect(request); diff --git a/src/amf/sbi-path.c b/src/amf/sbi-path.c index 6e438932e..2060e398b 100644 --- a/src/amf/sbi-path.c +++ b/src/amf/sbi-path.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2024 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * diff --git a/src/ausf/ausf-sm.c b/src/ausf/ausf-sm.c index 1ca5b5dc1..72e7c07d8 100644 --- a/src/ausf/ausf-sm.c +++ b/src/ausf/ausf-sm.c @@ -135,7 +135,8 @@ void ausf_state_operational(ogs_fsm_t *s, ausf_event_t *e) SWITCH(message.h.method) CASE(OGS_SBI_HTTP_METHOD_POST) if (message.AuthenticationInfo && - message.AuthenticationInfo->supi_or_suci) { + message.AuthenticationInfo->supi_or_suci && + strlen(message.AuthenticationInfo->supi_or_suci)) { ausf_ue = ausf_ue_find_by_suci_or_supi( message.AuthenticationInfo->supi_or_suci); if (!ausf_ue) { @@ -348,10 +349,10 @@ void ausf_state_operational(ogs_fsm_t *s, ausf_event_t *e) ogs_fsm_dispatch(&ausf_ue->sm, e); if (OGS_FSM_CHECK(&ausf_ue->sm, ausf_ue_state_exception)) { - ogs_error("[%s] State machine exception", ausf_ue->suci); + ogs_warn("[%s] State machine exception", ausf_ue->suci); ausf_ue_remove(ausf_ue); } else if (OGS_FSM_CHECK(&ausf_ue->sm, ausf_ue_state_deleted)) { - ogs_debug("[%s] AUSF-UE removed", ausf_ue->supi); + ogs_info("[%s] AUSF-UE removed", ausf_ue->supi); ausf_ue_remove(ausf_ue); } break; diff --git a/src/ausf/init.c b/src/ausf/init.c index c9d5b6cc5..f8ce8eea5 100644 --- a/src/ausf/init.c +++ b/src/ausf/init.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -65,6 +65,9 @@ static void event_termination(void) ogs_list_for_each(&ogs_sbi_self()->nf_instance_list, nf_instance) ogs_sbi_nf_fsm_fini(nf_instance); + /* Gracefully shutdown the server by sending GOAWAY to each session. */ + ogs_sbi_server_graceful_shutdown_all(); + /* Starting holding timer */ t_termination_holding = ogs_timer_add(ogs_app()->timer_mgr, NULL, NULL); ogs_assert(t_termination_holding); diff --git a/src/ausf/ue-sm.c b/src/ausf/ue-sm.c index 86fd9b03f..4022395a3 100644 --- a/src/ausf/ue-sm.c +++ b/src/ausf/ue-sm.c @@ -181,7 +181,9 @@ void ausf_ue_state_operational(ogs_fsm_t *s, ausf_event_t *e) ogs_sbi_server_send_error( stream, message->res_status, NULL, "HTTP response error", ausf_ue->suci, - message->ProblemDetails->cause)); + (message->ProblemDetails) ? + message->ProblemDetails->cause : NULL)); + OGS_FSM_TRAN(s, ausf_ue_state_exception); break; } diff --git a/src/bsf/init.c b/src/bsf/init.c index 118a50bd0..ea9394947 100644 --- a/src/bsf/init.c +++ b/src/bsf/init.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -67,6 +67,9 @@ static void event_termination(void) ogs_list_for_each(&ogs_sbi_self()->nf_instance_list, nf_instance) ogs_sbi_nf_fsm_fini(nf_instance); + /* Gracefully shutdown the server by sending GOAWAY to each session. */ + ogs_sbi_server_graceful_shutdown_all(); + /* Starting holding timer */ t_termination_holding = ogs_timer_add(ogs_app()->timer_mgr, NULL, NULL); ogs_assert(t_termination_holding); diff --git a/src/main.c b/src/main.c index 05cec9044..cf42c7cb8 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -60,10 +60,6 @@ static int check_signal(int signum) ogs_log_cycle(); break; - case SIGWINCH: - ogs_info("Signal-NUM[%d] received (%s)", - signum, ogs_signal_description_get(signum)); - break; case SIGUSR1: fprintf(stderr, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", diff --git a/src/mme/emm-build.c b/src/mme/emm-build.c index e9cc8e187..6a7a65bd1 100644 --- a/src/mme/emm-build.c +++ b/src/mme/emm-build.c @@ -297,9 +297,10 @@ ogs_pkbuf_t *emm_build_authentication_request(mme_ue_t *mme_ue) message.emm.h.protocol_discriminator = OGS_NAS_PROTOCOL_DISCRIMINATOR_EMM; message.emm.h.message_type = OGS_NAS_EPS_AUTHENTICATION_REQUEST; - authentication_request->nas_key_set_identifierasme.tsc = 0; + authentication_request->nas_key_set_identifierasme.tsc = + mme_ue->nas_eps.mme.tsc; authentication_request->nas_key_set_identifierasme.value = - mme_ue->nas_eps.ksi; + mme_ue->nas_eps.mme.ksi; memcpy(authentication_request->authentication_parameter_rand.rand, mme_ue->rand, OGS_RAND_LEN); memcpy(authentication_request->authentication_parameter_autn.autn, @@ -355,8 +356,8 @@ ogs_pkbuf_t *emm_build_security_mode_command(mme_ue_t *mme_ue) selected_nas_security_algorithms->type_of_ciphering_algorithm = mme_ue->selected_enc_algorithm; - nas_key_set_identifier->tsc = 0; - nas_key_set_identifier->value = 0; + nas_key_set_identifier->tsc = mme_ue->nas_eps.mme.tsc; + nas_key_set_identifier->value = mme_ue->nas_eps.mme.ksi; replayed_ue_security_capabilities->eea = mme_ue->ue_network_capability.eea; replayed_ue_security_capabilities->eia = mme_ue->ue_network_capability.eia; diff --git a/src/mme/emm-handler.c b/src/mme/emm-handler.c index de8e8be5c..f1c227211 100644 --- a/src/mme/emm-handler.c +++ b/src/mme/emm-handler.c @@ -78,13 +78,26 @@ int emm_handle_attach_request(enb_ue_t *enb_ue, mme_ue_t *mme_ue, memcpy(&mme_ue->nas_eps.attach, eps_attach_type, sizeof(ogs_nas_eps_attach_type_t)); mme_ue->nas_eps.type = MME_EPS_TYPE_ATTACH_REQUEST; - mme_ue->nas_eps.ksi = eps_attach_type->nas_key_set_identifier; - ogs_debug(" OGS_NAS_EPS TYPE[%d] KSI[%d]", - mme_ue->nas_eps.type, mme_ue->nas_eps.ksi); - ogs_debug(" ATTACH TSC[%d] KSI[%d] VALUE[%d]", + + ogs_debug(" ATTACH TYPE[%d] TSC[%d] KSI[%d] VALUE[%d]", + mme_ue->nas_eps.type, mme_ue->nas_eps.attach.tsc, mme_ue->nas_eps.attach.nas_key_set_identifier, mme_ue->nas_eps.attach.value); + + mme_ue->nas_eps.ue.tsc = eps_attach_type->tsc; + mme_ue->nas_eps.ue.ksi = eps_attach_type->nas_key_set_identifier; + ogs_debug(" OLD TSC[UE:%d,MME:%d] KSI[UE:%d,MME:%d]", + mme_ue->nas_eps.ue.tsc, mme_ue->nas_eps.mme.tsc, + mme_ue->nas_eps.ue.ksi, mme_ue->nas_eps.mme.ksi); + if (mme_ue->nas_eps.ue.ksi < OGS_NAS_KSI_NO_KEY_IS_AVAILABLE) { + mme_ue->nas_eps.mme.tsc = mme_ue->nas_eps.ue.tsc; + mme_ue->nas_eps.mme.ksi = mme_ue->nas_eps.ue.ksi; + } + ogs_debug(" NEW TSC[UE:%d,MME:%d] KSI[UE:%d,MME:%d]", + mme_ue->nas_eps.ue.tsc, mme_ue->nas_eps.mme.tsc, + mme_ue->nas_eps.ue.ksi, mme_ue->nas_eps.mme.ksi); + switch(mme_ue->nas_eps.attach.value){ case OGS_NAS_ATTACH_TYPE_EPS_ATTACH: ogs_debug(" Requested EPS_ATTACH_TYPE[1, EPS_ATTACH]"); @@ -99,6 +112,7 @@ int emm_handle_attach_request(enb_ue_t *enb_ue, mme_ue_t *mme_ue, ogs_error(" Invalid Requested EPS_ATTACH_TYPE[%d]", mme_ue->nas_eps.attach.value); } + /* * ATTACH_REQUEST * TAU_REQUEST @@ -365,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) @@ -457,15 +515,26 @@ int emm_handle_detach_request( mme_ue->nas_eps.type = MME_EPS_TYPE_DETACH_REQUEST_FROM_UE; mme_ue->detach_type = MME_DETACH_TYPE_REQUEST_FROM_UE; - mme_ue->nas_eps.ksi = detach_type->nas_key_set_identifier; - ogs_debug(" OGS_NAS_EPS TYPE[%d] KSI[%d]", - mme_ue->nas_eps.type, mme_ue->nas_eps.ksi); - ogs_debug(" DETACH TSC[%d] KSI[%d] SWITCH_OFF[%d] VALUE[%d]", - mme_ue->nas_eps.attach.tsc, + ogs_debug(" DETACH TYPE[%d] TSC[%d] KSI[%d] SWITCH_OFF[%d] VALUE[%d]", + mme_ue->nas_eps.type, + mme_ue->nas_eps.detach.tsc, mme_ue->nas_eps.detach.nas_key_set_identifier, mme_ue->nas_eps.detach.switch_off, mme_ue->nas_eps.attach.value); + mme_ue->nas_eps.ue.tsc = detach_type->tsc; + mme_ue->nas_eps.ue.ksi = detach_type->nas_key_set_identifier; + ogs_debug(" OLD TSC[UE:%d,MME:%d] KSI[UE:%d,MME:%d]", + mme_ue->nas_eps.ue.tsc, mme_ue->nas_eps.mme.tsc, + mme_ue->nas_eps.ue.ksi, mme_ue->nas_eps.mme.ksi); + if (mme_ue->nas_eps.ue.ksi < OGS_NAS_KSI_NO_KEY_IS_AVAILABLE) { + mme_ue->nas_eps.mme.tsc = mme_ue->nas_eps.ue.tsc; + mme_ue->nas_eps.mme.ksi = mme_ue->nas_eps.ue.ksi; + } + ogs_debug(" NEW TSC[UE:%d,MME:%d] KSI[UE:%d,MME:%d]", + mme_ue->nas_eps.ue.tsc, mme_ue->nas_eps.mme.tsc, + mme_ue->nas_eps.ue.ksi, mme_ue->nas_eps.mme.ksi); + switch (detach_request->detach_type.value) { /* 0 0 1 : EPS detach */ case OGS_NAS_DETACH_TYPE_FROM_UE_EPS_DETACH: @@ -517,14 +586,24 @@ int emm_handle_service_request( /* Set EPS Service */ mme_ue->nas_eps.type = MME_EPS_TYPE_SERVICE_REQUEST; - mme_ue->nas_eps.ksi = ksi_and_sequence_number->ksi; - ogs_debug(" OGS_NAS_EPS TYPE[%d] KSI[%d]", - mme_ue->nas_eps.type, mme_ue->nas_eps.ksi); - ogs_debug(" SERVICE TSC[%d] KSI[%d] VALUE[%d]", + ogs_debug(" SERVICE TYPE[%d] TSC[%d] KSI[%d] VALUE[%d]", + mme_ue->nas_eps.type, mme_ue->nas_eps.service.tsc, mme_ue->nas_eps.service.nas_key_set_identifier, mme_ue->nas_eps.service.value); + mme_ue->nas_eps.ue.ksi = ksi_and_sequence_number->ksi; + ogs_debug(" OLD TSC[UE:%d,MME:%d] KSI[UE:%d,MME:%d]", + mme_ue->nas_eps.ue.tsc, mme_ue->nas_eps.mme.tsc, + mme_ue->nas_eps.ue.ksi, mme_ue->nas_eps.mme.ksi); + if (mme_ue->nas_eps.ue.ksi < OGS_NAS_KSI_NO_KEY_IS_AVAILABLE) { + mme_ue->nas_eps.mme.tsc = mme_ue->nas_eps.ue.tsc; + mme_ue->nas_eps.mme.ksi = mme_ue->nas_eps.ue.ksi; + } + ogs_debug(" NEW TSC[UE:%d,MME:%d] KSI[UE:%d,MME:%d]", + mme_ue->nas_eps.ue.tsc, mme_ue->nas_eps.mme.tsc, + mme_ue->nas_eps.ue.ksi, mme_ue->nas_eps.mme.ksi); + /* * ATTACH_REQUEST * TAU_REQUEST @@ -609,15 +688,26 @@ int emm_handle_tau_request( memcpy(&mme_ue->nas_eps.update, eps_update_type, sizeof(ogs_nas_eps_update_type_t)); mme_ue->nas_eps.type = MME_EPS_TYPE_TAU_REQUEST; - mme_ue->nas_eps.ksi = eps_update_type->nas_key_set_identifier; - ogs_debug(" OGS_NAS_EPS TYPE[%d] KSI[%d]", - mme_ue->nas_eps.type, mme_ue->nas_eps.ksi); - ogs_debug(" UPDATE TSC[%d] KSI[%d] Active-flag[%d] VALUE[%d]", + ogs_debug(" UPDATE TYPE[%d] TSC[%d] KSI[%d] Active-flag[%d] VALUE[%d]", + mme_ue->nas_eps.type, mme_ue->nas_eps.update.tsc, mme_ue->nas_eps.update.nas_key_set_identifier, mme_ue->nas_eps.update.active_flag, mme_ue->nas_eps.update.value); + mme_ue->nas_eps.ue.tsc = eps_update_type->tsc; + mme_ue->nas_eps.ue.ksi = eps_update_type->nas_key_set_identifier; + ogs_debug(" OLD TSC[UE:%d,MME:%d] KSI[UE:%d,MME:%d]", + mme_ue->nas_eps.ue.tsc, mme_ue->nas_eps.mme.tsc, + mme_ue->nas_eps.ue.ksi, mme_ue->nas_eps.mme.ksi); + if (mme_ue->nas_eps.ue.ksi < OGS_NAS_KSI_NO_KEY_IS_AVAILABLE) { + mme_ue->nas_eps.mme.tsc = mme_ue->nas_eps.ue.tsc; + mme_ue->nas_eps.mme.ksi = mme_ue->nas_eps.ue.ksi; + } + ogs_debug(" NEW TSC[UE:%d,MME:%d] KSI[UE:%d,MME:%d]", + mme_ue->nas_eps.ue.tsc, mme_ue->nas_eps.mme.tsc, + mme_ue->nas_eps.ue.ksi, mme_ue->nas_eps.mme.ksi); + /* * ATTACH_REQUEST * TAU_REQUEST @@ -756,9 +846,24 @@ int emm_handle_extended_service_request( memcpy(&mme_ue->nas_eps.service, service_type, sizeof(ogs_nas_service_type_t)); mme_ue->nas_eps.type = MME_EPS_TYPE_EXTENDED_SERVICE_REQUEST; - mme_ue->nas_eps.ksi = service_type->nas_key_set_identifier; - ogs_debug(" OGS_NAS_EPS TYPE[%d] KSI[%d]", - mme_ue->nas_eps.type, mme_ue->nas_eps.ksi); + ogs_debug(" Extended SERVICE TYPE[%d] TSC[%d] KSI[%d] VALUE[%d]", + mme_ue->nas_eps.type, + mme_ue->nas_eps.service.tsc, + mme_ue->nas_eps.service.nas_key_set_identifier, + mme_ue->nas_eps.service.value); + + mme_ue->nas_eps.ue.tsc = service_type->tsc; + mme_ue->nas_eps.ue.ksi = service_type->nas_key_set_identifier; + ogs_debug(" OLD TSC[UE:%d,MME:%d] KSI[UE:%d,MME:%d]", + mme_ue->nas_eps.ue.tsc, mme_ue->nas_eps.mme.tsc, + mme_ue->nas_eps.ue.ksi, mme_ue->nas_eps.mme.ksi); + if (mme_ue->nas_eps.ue.ksi < OGS_NAS_KSI_NO_KEY_IS_AVAILABLE) { + mme_ue->nas_eps.mme.tsc = mme_ue->nas_eps.ue.tsc; + mme_ue->nas_eps.mme.ksi = mme_ue->nas_eps.ue.ksi; + } + ogs_debug(" NEW TSC[UE:%d,MME:%d] KSI[UE:%d,MME:%d]", + mme_ue->nas_eps.ue.tsc, mme_ue->nas_eps.mme.tsc, + mme_ue->nas_eps.ue.ksi, mme_ue->nas_eps.mme.ksi); /* * ATTACH_REQUEST diff --git a/src/mme/emm-handler.h b/src/mme/emm-handler.h index 0c5af4d07..f80da9cd2 100644 --- a/src/mme/emm-handler.h +++ b/src/mme/emm-handler.h @@ -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); diff --git a/src/mme/emm-sm.c b/src/mme/emm-sm.c index 20643b71d..94fd2e78d 100644 --- a/src/mme/emm-sm.c +++ b/src/mme/emm-sm.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2024 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -37,6 +37,24 @@ #undef OGS_LOG_DOMAIN #define OGS_LOG_DOMAIN __emm_log_domain +#define MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s) do { \ + if ((mme_ue)->can_restore_context) { \ + /* Restore context if allowed */ \ + mme_ue_restore_memento((mme_ue), &((mme_ue)->memento)); \ + (mme_ue)->security_context_available = 1; \ + (mme_ue)->mac_failed = 0; \ + if (!OGS_FSM_CHECK(&mme_ue->sm, emm_state_registered)) \ + OGS_FSM_TRAN((s), &emm_state_registered); \ + ogs_warn("[%s] Failure in transaction; restoring context and " \ + "transitioning to REGISTERED.", (mme_ue)->imsi_bcd); \ + } else { \ + /* Transition to exception state if not allowed */ \ + OGS_FSM_TRAN((s), &emm_state_exception); \ + ogs_warn("[%s] Failure in transaction; no context " \ + "restoration.", (mme_ue)->imsi_bcd); \ + } \ +} while (0) + typedef enum { EMM_COMMON_STATE_DEREGISTERED, EMM_COMMON_STATE_REGISTERED, @@ -45,7 +63,6 @@ typedef enum { static void common_register_state(ogs_fsm_t *s, mme_event_t *e, emm_common_state_e state); - void emm_state_initial(ogs_fsm_t *s, mme_event_t *e) { ogs_assert(s); @@ -304,6 +321,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_context = 1; + mme_ue_save_memento(mme_ue, &mme_ue->memento); + } else if (state == EMM_COMMON_STATE_DEREGISTERED) { + /* Transition from de-registered: do not restore */ + mme_ue->can_restore_context = 0; + } + switch (e->id) { case MME_EVENT_EMM_MESSAGE: message = e->nas_message; @@ -337,7 +363,7 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, OGS_NAS_EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, &emm_state_exception); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -345,7 +371,7 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, enb_ue, mme_ue, &message->emm.service_request); if (rv != OGS_OK) { ogs_error("emm_handle_service_request() failed"); - OGS_FSM_TRAN(s, emm_state_exception); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -355,7 +381,7 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, OGS_NAS_EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, &emm_state_exception); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -365,7 +391,7 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, OGS_NAS_EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, &emm_state_exception); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -375,7 +401,7 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, OGS_NAS_EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, &emm_state_exception); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -385,7 +411,7 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, OGS_NAS_EMM_CAUSE_NO_EPS_BEARER_CONTEXT_ACTIVATED); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, &emm_state_exception); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -411,7 +437,7 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, emm_state_exception); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -422,13 +448,13 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, &message->emm.identity_response); if (rv != OGS_OK) { ogs_error("emm_handle_identity_response() failed"); - OGS_FSM_TRAN(s, emm_state_exception); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } if (!MME_UE_HAVE_IMSI(mme_ue)) { ogs_error("No IMSI"); - OGS_FSM_TRAN(s, emm_state_exception); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -450,7 +476,7 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, 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); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -485,7 +511,7 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, OGS_NAS_ESM_CAUSE_PROTOCOL_ERROR_UNSPECIFIED); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, &emm_state_exception); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } } @@ -513,7 +539,7 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, &message->emm.tracking_area_update_request, e->pkbuf); if (rv != OGS_OK) { ogs_error("emm_handle_tau_request() failed"); - OGS_FSM_TRAN(s, emm_state_exception); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -531,7 +557,7 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, OGS_NAS_EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, &emm_state_exception); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } if (message->emm.tracking_area_update_request.presencemask & OGS_NAS_EPS_TRACKING_AREA_UPDATE_REQUEST_OLD_P_TMSI_SIGNATURE_TYPE) @@ -547,7 +573,7 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, OGS_NAS_EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, &emm_state_exception); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -557,7 +583,7 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, OGS_NAS_EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, emm_state_exception); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -567,7 +593,7 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, OGS_NAS_EMM_CAUSE_NO_EPS_BEARER_CONTEXT_ACTIVATED); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, &emm_state_exception); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -752,7 +778,7 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, enb_ue, mme_ue, &message->emm.extended_service_request); if (rv != OGS_OK) { ogs_error("emm_handle_extended_service_request() failed"); - OGS_FSM_TRAN(s, emm_state_exception); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -762,7 +788,7 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, OGS_NAS_EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, &emm_state_exception); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -772,7 +798,7 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, OGS_NAS_EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, emm_state_exception); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -782,7 +808,7 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, OGS_NAS_EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, &emm_state_exception); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -831,7 +857,7 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, OGS_NAS_EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, &emm_state_exception); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -874,7 +900,7 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e, OGS_NAS_EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); - OGS_FSM_TRAN(s, &emm_state_exception); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -897,7 +923,7 @@ static void common_register_state(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); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -906,7 +932,7 @@ static void common_register_state(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); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -915,7 +941,7 @@ static void common_register_state(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); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -1060,6 +1086,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 +1110,28 @@ 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); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); + break; } + 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,17 +1159,16 @@ 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); - + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); 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); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -1194,7 +1185,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); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -1203,7 +1194,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); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -1212,7 +1203,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); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); break; } @@ -1243,11 +1234,11 @@ 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); + MME_RESTORE_CONTEXT_ON_FAILURE(mme_ue, s); + break; } else { mme_ue->t3460.retry_count++; r = nas_eps_send_authentication_request(mme_ue); diff --git a/src/mme/mme-context.c b/src/mme/mme-context.c index fb824af1b..46de8485a 100644 --- a/src/mme/mme-context.c +++ b/src/mme/mme-context.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -3617,6 +3617,9 @@ mme_ue_t *mme_ue_add(enb_ue_t *enb_ue) mme_ue->csmap = NULL; mme_ue->vlr_ostream_id = 0; + /* Initialization */ + mme_ue->nas_eps.mme.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; + mme_ue_fsm_init(mme_ue); ogs_list_add(&self.mme_ue_list, mme_ue); @@ -4197,20 +4200,36 @@ void enb_ue_source_deassociate_target(enb_ue_t *enb_ue) ogs_assert(source_ue->target_ue_id >= OGS_MIN_POOL_ID && source_ue->target_ue_id <= OGS_MAX_POOL_ID); - ogs_assert(target_ue->source_ue_id >= OGS_MIN_POOL_ID && - target_ue->source_ue_id <= OGS_MAX_POOL_ID); source_ue->target_ue_id = OGS_INVALID_POOL_ID; - target_ue->source_ue_id = OGS_INVALID_POOL_ID; + + if (target_ue) { + ogs_assert(target_ue->source_ue_id >= OGS_MIN_POOL_ID && + target_ue->source_ue_id <= OGS_MAX_POOL_ID); + target_ue->source_ue_id = OGS_INVALID_POOL_ID; + } else + ogs_error("Target-UE-ID [%d] has already been removed " + "(ENB_UE_S1AP_ID[%d] MME_UE_S1AP_ID[%d])", + source_ue->target_ue_id, + source_ue->enb_ue_s1ap_id, source_ue->mme_ue_s1ap_id); + + } else if (enb_ue->source_ue_id >= OGS_MIN_POOL_ID && enb_ue->source_ue_id <= OGS_MAX_POOL_ID) { target_ue = enb_ue; source_ue = enb_ue_find_by_id(enb_ue->source_ue_id); - ogs_assert(source_ue->target_ue_id >= OGS_MIN_POOL_ID && - source_ue->target_ue_id <= OGS_MAX_POOL_ID); + if (source_ue) { + ogs_assert(source_ue->target_ue_id >= OGS_MIN_POOL_ID && + source_ue->target_ue_id <= OGS_MAX_POOL_ID); + source_ue->target_ue_id = OGS_INVALID_POOL_ID; + } else + ogs_error("Source-UE-ID [%d] has already been removed " + "(ENB_UE_S1AP_ID[%d] MME_UE_S1AP_ID[%d])", + target_ue->source_ue_id, + target_ue->enb_ue_s1ap_id, target_ue->mme_ue_s1ap_id); + ogs_assert(target_ue->source_ue_id >= OGS_MIN_POOL_ID && target_ue->source_ue_id <= OGS_MAX_POOL_ID); - source_ue->target_ue_id = OGS_INVALID_POOL_ID; target_ue->source_ue_id = OGS_INVALID_POOL_ID; } } @@ -4250,6 +4269,7 @@ void sgw_ue_source_deassociate_target(sgw_ue_t *sgw_ue) { sgw_ue_t *source_ue = NULL; sgw_ue_t *target_ue = NULL; + ogs_assert(sgw_ue); if (sgw_ue->target_ue_id >= OGS_MIN_POOL_ID && @@ -4259,20 +4279,33 @@ void sgw_ue_source_deassociate_target(sgw_ue_t *sgw_ue) ogs_assert(source_ue->target_ue_id >= OGS_MIN_POOL_ID && source_ue->target_ue_id <= OGS_MAX_POOL_ID); - ogs_assert(target_ue->source_ue_id >= OGS_MIN_POOL_ID && - target_ue->source_ue_id <= OGS_MAX_POOL_ID); source_ue->target_ue_id = OGS_INVALID_POOL_ID; - target_ue->source_ue_id = OGS_INVALID_POOL_ID; + + if (target_ue) { + ogs_assert(target_ue->source_ue_id >= OGS_MIN_POOL_ID && + target_ue->source_ue_id <= OGS_MAX_POOL_ID); + target_ue->source_ue_id = OGS_INVALID_POOL_ID; + } else + ogs_error("Target-UE-ID [%d] has already been removed " + "(SGW-S11-TEID[%d])", + source_ue->target_ue_id, source_ue->sgw_s11_teid); + } else if (sgw_ue->source_ue_id >= OGS_MIN_POOL_ID && sgw_ue->source_ue_id <= OGS_MAX_POOL_ID) { target_ue = sgw_ue; source_ue = sgw_ue_find_by_id(sgw_ue->source_ue_id); - ogs_assert(source_ue->target_ue_id >= OGS_MIN_POOL_ID && - source_ue->target_ue_id <= OGS_MAX_POOL_ID); + if (source_ue) { + ogs_assert(source_ue->target_ue_id >= OGS_MIN_POOL_ID && + source_ue->target_ue_id <= OGS_MAX_POOL_ID); + source_ue->target_ue_id = OGS_INVALID_POOL_ID; + } else + ogs_error("Source-UE-ID [%d] has already been removed " + "(SGW-S11-TEID[%d])", + target_ue->source_ue_id, target_ue->sgw_s11_teid); + ogs_assert(target_ue->source_ue_id >= OGS_MIN_POOL_ID && target_ue->source_ue_id <= OGS_MAX_POOL_ID); - source_ue->target_ue_id = OGS_INVALID_POOL_ID; target_ue->source_ue_id = OGS_INVALID_POOL_ID; } } @@ -5027,6 +5060,83 @@ uint8_t mme_selected_enc_algorithm(mme_ue_t *mme_ue) return 0; } +/* + * Save the sensitive (partial) context fields + * from the UE context into the memento + */ +void mme_ue_save_memento(mme_ue_t *mme_ue, mme_ue_memento_t *memento) +{ + ogs_assert(mme_ue); + ogs_assert(memento); + + memcpy(&memento->ue_network_capability, + &mme_ue->ue_network_capability, + sizeof(memento->ue_network_capability)); + memcpy(&memento->ms_network_capability, + &mme_ue->ms_network_capability, + sizeof(memento->ms_network_capability)); + memcpy(&memento->ue_additional_security_capability, + &mme_ue->ue_additional_security_capability, + sizeof(memento->ue_additional_security_capability)); + memcpy(memento->xres, mme_ue->xres, OGS_MAX_RES_LEN); + memento->xres_len = mme_ue->xres_len; + memcpy(memento->kasme, mme_ue->kasme, OGS_SHA256_DIGEST_SIZE); + memcpy(memento->rand, mme_ue->rand, OGS_RAND_LEN); + memcpy(memento->autn, mme_ue->autn, OGS_AUTN_LEN); + memcpy(memento->knas_int, mme_ue->knas_int, + OGS_SHA256_DIGEST_SIZE / 2); + memcpy(memento->knas_enc, mme_ue->knas_enc, + OGS_SHA256_DIGEST_SIZE / 2); + memento->dl_count = mme_ue->dl_count; + memento->ul_count = mme_ue->ul_count.i32; + memcpy(memento->kenb, mme_ue->kenb, OGS_SHA256_DIGEST_SIZE); + memcpy(memento->hash_mme, mme_ue->hash_mme, OGS_HASH_MME_LEN); + memento->nonceue = mme_ue->nonceue; + memento->noncemme = mme_ue->noncemme; + memento->gprs_ciphering_key_sequence_number = + mme_ue->gprs_ciphering_key_sequence_number; + memcpy(memento->nh, mme_ue->nh, OGS_SHA256_DIGEST_SIZE); + memento->selected_enc_algorithm = mme_ue->selected_enc_algorithm; + memento->selected_int_algorithm = mme_ue->selected_int_algorithm; +} + +/* Restore the sensitive context fields into the UE context */ +void mme_ue_restore_memento(mme_ue_t *mme_ue, const mme_ue_memento_t *memento) +{ + ogs_assert(mme_ue); + ogs_assert(memento); + + memcpy(&mme_ue->ue_network_capability, + &memento->ue_network_capability, + sizeof(mme_ue->ue_network_capability)); + memcpy(&mme_ue->ms_network_capability, + &memento->ms_network_capability, + sizeof(mme_ue->ms_network_capability)); + memcpy(&mme_ue->ue_additional_security_capability, + &memento->ue_additional_security_capability, + sizeof(mme_ue->ue_additional_security_capability)); + memcpy(mme_ue->xres, memento->xres, OGS_MAX_RES_LEN); + mme_ue->xres_len = memento->xres_len; + memcpy(mme_ue->kasme, memento->kasme, OGS_SHA256_DIGEST_SIZE); + memcpy(mme_ue->rand, memento->rand, OGS_RAND_LEN); + memcpy(mme_ue->autn, memento->autn, OGS_AUTN_LEN); + memcpy(mme_ue->knas_int, memento->knas_int, + OGS_SHA256_DIGEST_SIZE / 2); + memcpy(mme_ue->knas_enc, memento->knas_enc, + OGS_SHA256_DIGEST_SIZE / 2); + mme_ue->dl_count = memento->dl_count; + mme_ue->ul_count.i32 = memento->ul_count; + memcpy(mme_ue->kenb, memento->kenb, OGS_SHA256_DIGEST_SIZE); + memcpy(mme_ue->hash_mme, memento->hash_mme, OGS_HASH_MME_LEN); + mme_ue->nonceue = memento->nonceue; + mme_ue->noncemme = memento->noncemme; + mme_ue->gprs_ciphering_key_sequence_number = + memento->gprs_ciphering_key_sequence_number; + memcpy(mme_ue->nh, memento->nh, OGS_SHA256_DIGEST_SIZE); + mme_ue->selected_enc_algorithm = memento->selected_enc_algorithm; + mme_ue->selected_int_algorithm = memento->selected_int_algorithm; +} + static void stats_add_enb_ue(void) { mme_metrics_inst_global_inc(MME_METR_GLOB_GAUGE_ENB_UE); diff --git a/src/mme/mme-context.h b/src/mme/mme-context.h index 4452bbbed..406b9cf19 100644 --- a/src/mme/mme-context.h +++ b/src/mme/mme-context.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -342,6 +342,69 @@ struct sgw_ue_s { ogs_pool_id_t mme_ue_id; }; +typedef struct mme_ue_memento_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_ue_memento_t; + struct mme_ue_s { ogs_lnode_t lnode; ogs_pool_id_t id; @@ -355,7 +418,13 @@ struct mme_ue_s { #define MME_EPS_TYPE_DETACH_REQUEST_FROM_UE 5 #define MME_EPS_TYPE_DETACH_REQUEST_TO_UE 6 uint8_t type; - uint8_t ksi; + + struct { + ED3(uint8_t tsc:1;, + uint8_t ksi:3;, + uint8_t spare:4;) + } mme, ue; + ogs_nas_eps_attach_type_t attach; ogs_nas_eps_update_type_t update; ogs_nas_service_type_t service; @@ -446,30 +515,42 @@ struct mme_ue_s { ((__mME) && \ ((__mME)->security_context_available == 1) && \ ((__mME)->mac_failed == 0) && \ - ((__mME)->nas_eps.ksi != OGS_NAS_KSI_NO_KEY_IS_AVAILABLE)) + ((__mME)->nas_eps.ue.ksi != OGS_NAS_KSI_NO_KEY_IS_AVAILABLE)) #define CLEAR_SECURITY_CONTEXT(__mME) \ do { \ ogs_assert((__mME)); \ (__mME)->security_context_available = 0; \ (__mME)->mac_failed = 0; \ - (__mME)->nas_eps.ksi = 0; \ } while(0) int security_context_available; int mac_failed; + /* flag: 1 = allow restoration of context, 0 = disallow */ + bool can_restore_context; + + /* Memento of context fields */ + mme_ue_memento_t memento; + /* 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;, @@ -478,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 @@ -1128,6 +1215,9 @@ 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_ue_save_memento(mme_ue_t *mme_ue, mme_ue_memento_t *memento); +void mme_ue_restore_memento(mme_ue_t *mme_ue, const mme_ue_memento_t *memento); + #ifdef __cplusplus } #endif diff --git a/src/mme/mme-fd-path.c b/src/mme/mme-fd-path.c index ed7e1133c..75109ed0c 100644 --- a/src/mme/mme-fd-path.c +++ b/src/mme/mme-fd-path.c @@ -636,6 +636,9 @@ static int mme_s6a_subscription_data_from_avp(struct avp *avp, error++; } break; + case OGS_DIAM_S6A_AVP_CODE_MIP_HOME_AGENT_HOST: + ogs_error("Ignoring MIP-Home-Agent-Host..."); + break; default: ogs_error("Unknown AVP-Code:%d", hdr->avp_code); diff --git a/src/mme/mme-gn-build.c b/src/mme/mme-gn-build.c index b5a172980..44bfe364e 100644 --- a/src/mme/mme-gn-build.c +++ b/src/mme/mme-gn-build.c @@ -32,7 +32,7 @@ static int sess_fill_mm_context_decoded(mme_sess_t *sess, ogs_gtp1_mm_context_de *mmctx_dec = (ogs_gtp1_mm_context_decoded_t) { .gupii = 1, /* Integrity Protection not required */ .ugipai = 1, /* Ignore "Used GPRS integrity protection algorithm" field" */ - .ksi = mme_ue->nas_eps.ksi, + .ksi = mme_ue->nas_eps.mme.ksi, .sec_mode = OGS_GTP1_SEC_MODE_UMTS_KEY_AND_QUINTUPLETS, .num_vectors = 0, /* TODO: figure out how to fill the quintuplets */ .drx_param = { diff --git a/src/mme/mme-gn-handler.c b/src/mme/mme-gn-handler.c index a97521cb6..b120b713d 100644 --- a/src/mme/mme-gn-handler.c +++ b/src/mme/mme-gn-handler.c @@ -407,7 +407,7 @@ int mme_gn_handle_sgsn_context_response( ogs_min(gtp1_mm_ctx.ms_network_capability_len, sizeof(mme_ue->ms_network_capability) - 1)); /* TODO: how to fill first byte of mme_ue->ms_network_capability ? */ - mme_ue->nas_eps.ksi = gtp1_mm_ctx.ksi; + mme_ue->nas_eps.mme.ksi = gtp1_mm_ctx.ksi; /* 3GPP TS 33.401 A.10, A.11: */ mme_ue->noncemme = ogs_random32(); /* 3GPP TS 33.401 7.2.6.2 Establishment of keys for cryptographically protected radio bearers: */ diff --git a/src/mme/mme-s6a-handler.c b/src/mme/mme-s6a-handler.c index a2a2eb518..0f759b970 100644 --- a/src/mme/mme-s6a-handler.c +++ b/src/mme/mme-s6a-handler.c @@ -63,8 +63,12 @@ uint8_t mme_s6a_handle_aia( CLEAR_MME_UE_TIMER(mme_ue->t3460); - if (mme_ue->nas_eps.ksi == OGS_NAS_KSI_NO_KEY_IS_AVAILABLE) - mme_ue->nas_eps.ksi = 0; + if (mme_ue->nas_eps.mme.ksi < (OGS_NAS_KSI_NO_KEY_IS_AVAILABLE - 1)) + mme_ue->nas_eps.mme.ksi++; + else + mme_ue->nas_eps.mme.ksi = 0; + + mme_ue->nas_eps.ue.ksi = mme_ue->nas_eps.mme.ksi; return OGS_NAS_EMM_CAUSE_REQUEST_ACCEPTED; } diff --git a/src/mme/s1ap-handler.c b/src/mme/s1ap-handler.c index aa5f967ce..5cd055c85 100644 --- a/src/mme/s1ap-handler.c +++ b/src/mme/s1ap-handler.c @@ -2268,7 +2268,7 @@ void s1ap_handle_enb_direct_information_transfer( ogs_plmn_id_t plmn_id; ogs_nas_rai_t rai; uint16_t cell_id; - unsigned int i; + int i, r; mme_sgsn_t *sgsn = NULL; ogs_assert(enb); @@ -2293,7 +2293,15 @@ void s1ap_handle_enb_direct_information_transfer( /* Clang scan-build SA: NULL pointer dereference: Inter_SystemInformationTransferType=NULL if above * protocolIEs.list.count=0 in loop. */ - ogs_assert(Inter_SystemInformationTransferType); + if (!Inter_SystemInformationTransferType) { + ogs_warn("No Inter_SystemInformationTransferType"); + r = s1ap_send_error_indication(enb, NULL, NULL, + S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error); + ogs_expect(r == OGS_OK); + ogs_assert(r != OGS_ERROR); + return; + } + RIMTransfer = Inter_SystemInformationTransferType->choice.rIMTransfer; diff --git a/src/mme/s1ap-sctp.c b/src/mme/s1ap-sctp.c index 60d310ca4..5e08c62ea 100644 --- a/src/mme/s1ap-sctp.c +++ b/src/mme/s1ap-sctp.c @@ -160,14 +160,19 @@ void s1ap_recv_handler(ogs_sock_t *sock) if (not->sn_assoc_change.sac_state == SCTP_COMM_UP) { ogs_debug("SCTP_COMM_UP"); - addr = ogs_calloc(1, sizeof(ogs_sockaddr_t)); - ogs_assert(addr); - memcpy(addr, &from, sizeof(ogs_sockaddr_t)); + if ((not->sn_assoc_change.sac_outbound_streams-1) >= 1) { + /* NEXT_ID(MAX >= MIN) */ + addr = ogs_calloc(1, sizeof(ogs_sockaddr_t)); + ogs_assert(addr); + memcpy(addr, &from, sizeof(ogs_sockaddr_t)); - s1ap_event_push(MME_EVENT_S1AP_LO_SCTP_COMM_UP, - sock, addr, NULL, - not->sn_assoc_change.sac_inbound_streams, - not->sn_assoc_change.sac_outbound_streams); + s1ap_event_push(MME_EVENT_S1AP_LO_SCTP_COMM_UP, + sock, addr, NULL, + not->sn_assoc_change.sac_inbound_streams, + not->sn_assoc_change.sac_outbound_streams); + } else + ogs_error("Invalid sn_assoc_change.sac_outbound_streams %d", + not->sn_assoc_change.sac_outbound_streams); } else if (not->sn_assoc_change.sac_state == SCTP_SHUTDOWN_COMP || not->sn_assoc_change.sac_state == SCTP_COMM_LOST) { diff --git a/src/mme/sgsap-sctp.c b/src/mme/sgsap-sctp.c index e7c8d340c..c262a848c 100644 --- a/src/mme/sgsap-sctp.c +++ b/src/mme/sgsap-sctp.c @@ -118,10 +118,13 @@ static void recv_handler(ogs_sock_t *sock) if (not->sn_assoc_change.sac_state == SCTP_COMM_UP) { ogs_debug("SCTP_COMM_UP"); - sgsap_event_push(MME_EVENT_SGSAP_LO_SCTP_COMM_UP, - sock, NULL, NULL, - not->sn_assoc_change.sac_inbound_streams, - not->sn_assoc_change.sac_outbound_streams); + if ((not->sn_assoc_change.sac_outbound_streams-1) >= 1) { + /* NEXT_ID(MAX >= MIN) */ + sgsap_event_push(MME_EVENT_SGSAP_LO_SCTP_COMM_UP, + sock, NULL, NULL, + not->sn_assoc_change.sac_inbound_streams, + not->sn_assoc_change.sac_outbound_streams); + } } else if (not->sn_assoc_change.sac_state == SCTP_SHUTDOWN_COMP || not->sn_assoc_change.sac_state == SCTP_COMM_LOST) { diff --git a/src/nrf/init.c b/src/nrf/init.c index 0a726b426..ef350294e 100644 --- a/src/nrf/init.c +++ b/src/nrf/init.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -59,9 +59,8 @@ static ogs_timer_t *t_termination_holding = NULL; static void event_termination(void) { - /* - * Add business-login during Daemon termination - */ + /* Gracefully shutdown the server by sending GOAWAY to each session. */ + ogs_sbi_server_graceful_shutdown_all(); /* Start holding timer */ t_termination_holding = ogs_timer_add(ogs_app()->timer_mgr, NULL, NULL); diff --git a/src/nrf/nnrf-build.c b/src/nrf/nnrf-build.c index 38fcb0a11..94e86bc9c 100644 --- a/src/nrf/nnrf-build.c +++ b/src/nrf/nnrf-build.c @@ -81,6 +81,17 @@ ogs_sbi_request_t *nrf_nnrf_nfm_build_nf_status_notify( goto end; } } +/* + * Callback Header Configuration + * + * The 3gpp-Sbi-Callback HTTP header (per 3GPP TS 29.500 v17.9.0) indicates that + * a message is an asynchronous notification or callback. This header should be + * included only in HTTP POST requests that are callbacks (e.g., event or + * notification messages) and must not be added to regular service requests, + * such as registration (HTTP PUT) or subscription requests. + */ + message.http.custom.callback = + (char *)OGS_SBI_CALLBACK_NNRF_NFMANAGEMENT_NF_STATUS_NOTIFY; message.NotificationData = NotificationData; diff --git a/src/nssf/init.c b/src/nssf/init.c index 757bd4368..c4c92a9b4 100644 --- a/src/nssf/init.c +++ b/src/nssf/init.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -65,6 +65,9 @@ static void event_termination(void) ogs_list_for_each(&ogs_sbi_self()->nf_instance_list, nf_instance) ogs_sbi_nf_fsm_fini(nf_instance); + /* Gracefully shutdown the server by sending GOAWAY to each session. */ + ogs_sbi_server_graceful_shutdown_all(); + /* Starting holding timer */ t_termination_holding = ogs_timer_add(ogs_app()->timer_mgr, NULL, NULL); ogs_assert(t_termination_holding); diff --git a/src/pcf/context.c b/src/pcf/context.c index 67a6ed46a..f150a005a 100644 --- a/src/pcf/context.c +++ b/src/pcf/context.c @@ -805,36 +805,45 @@ int pcf_instance_get_load(void) ogs_pool_size(&pcf_ue_pool)); } -int pcf_db_qos_data(char *supi, - ogs_plmn_id_t *plmn_id, ogs_s_nssai_t *s_nssai, char *dnn, - ogs_session_data_t *session_data) +int pcf_get_session_data(const char *supi, + const ogs_plmn_id_t *plmn_id, + const ogs_s_nssai_t *s_nssai, + const char *dnn, + ogs_session_data_t *session_data, + int flags) { - int rv; - + int rv = OGS_OK; ogs_app_policy_conf_t *policy_conf = NULL; + /* Validate input parameters */ ogs_assert(supi); ogs_assert(s_nssai); ogs_assert(dnn); ogs_assert(session_data); + /* Initialize the session data structure */ memset(session_data, 0, sizeof(*session_data)); + /* Attempt to locate a policy configuration */ policy_conf = ogs_app_policy_conf_find(supi, plmn_id); if (policy_conf) { rv = ogs_app_config_session_data( supi, plmn_id, s_nssai, dnn, session_data); - if (rv != OGS_OK) - ogs_error("ogs_app_config_session_data() failed - " - "MCC[%d] MNC[%d] SST[%d] SD[0x%x] DNN[%s]", - ogs_plmn_id_mcc(plmn_id), ogs_plmn_id_mnc(plmn_id), - s_nssai->sst, s_nssai->sd.v, dnn); + if (rv != OGS_OK) { + if (!(flags & PCF_SESSION_DATA_FLAG_NO_ERROR_LOG)) + ogs_error("ogs_app_config_session_data() failed - " + "MCC[%d] MNC[%d] SST[%d] SD[0x%x] DNN[%s]", + ogs_plmn_id_mcc(plmn_id), ogs_plmn_id_mnc(plmn_id), + s_nssai->sst, s_nssai->sd.v, dnn); + } } else { rv = ogs_dbi_session_data(supi, s_nssai, dnn, session_data); - if (rv != OGS_OK) - ogs_error("ogs_dbi_session_data() failed - " - "SUPI[%s] SST[%d] SD[0x%x] DNN[%s]", - supi, s_nssai->sst, s_nssai->sd.v, dnn); + if (rv != OGS_OK) { + if (!(flags & PCF_SESSION_DATA_FLAG_NO_ERROR_LOG)) + ogs_error("ogs_dbi_session_data() failed - " + "SUPI[%s] SST[%d] SD[0x%x] DNN[%s]", + supi, s_nssai->sst, s_nssai->sd.v, dnn); + } } return rv; diff --git a/src/pcf/context.h b/src/pcf/context.h index 7eda1ed8f..e973bb6c7 100644 --- a/src/pcf/context.h +++ b/src/pcf/context.h @@ -214,9 +214,42 @@ pcf_app_t *pcf_app_find(uint32_t index); pcf_app_t *pcf_app_find_by_app_session_id(char *app_session_id); int pcf_instance_get_load(void); -int pcf_db_qos_data(char *supi, - ogs_plmn_id_t *plmn_id, ogs_s_nssai_t *s_nssai, char *dnn, - ogs_session_data_t *session_data); +/*------------------------------------------------------------------ + * Flag definition(s) for pcf_get_session_data(). + *------------------------------------------------------------------ + * Use PCF_SESSION_DATA_FLAG_NO_ERROR_LOG to suppress error logs. + */ +#define PCF_SESSION_DATA_FLAG_NO_ERROR_LOG 0x01 + +/*------------------------------------------------------------------ + * pcf_get_session_data - Retrieve session data from policy config or DB. + * + * This function retrieves session data for the given subscriber by + * first checking for a policy configuration; if none is found, it + * falls back to querying the database. + * + * Parameters: + * supi - Subscriber identifier (read-only). + * plmn_id - Pointer to a PLMN ID structure (read-only). + * s_nssai - Pointer to an S-NSSAI structure (read-only). + * dnn - Data network name (read-only). + * session_data - Pointer to the session data structure to be filled. + * flags - Flags to control behavior (e.g., error logging). + * + * Returns: + * OGS_OK on success, or an error code on failure. + * + * Note: + * To suppress error logging, pass the flag + * PCF_SESSION_DATA_FLAG_NO_ERROR_LOG in the flags parameter. + *------------------------------------------------------------------ + */ +int pcf_get_session_data(const char *supi, + const ogs_plmn_id_t *plmn_id, + const ogs_s_nssai_t *s_nssai, + const char *dnn, + ogs_session_data_t *session_data, + int flags); #ifdef __cplusplus } diff --git a/src/pcf/init.c b/src/pcf/init.c index d4ac258b5..cd1248319 100644 --- a/src/pcf/init.c +++ b/src/pcf/init.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -78,6 +78,9 @@ static void event_termination(void) ogs_list_for_each(&ogs_sbi_self()->nf_instance_list, nf_instance) ogs_sbi_nf_fsm_fini(nf_instance); + /* Gracefully shutdown the server by sending GOAWAY to each session. */ + ogs_sbi_server_graceful_shutdown_all(); + /* Starting holding timer */ t_termination_holding = ogs_timer_add(ogs_app()->timer_mgr, NULL, NULL); ogs_assert(t_termination_holding); diff --git a/src/pcf/npcf-handler.c b/src/pcf/npcf-handler.c index 930567ffc..b3c699e35 100644 --- a/src/pcf/npcf-handler.c +++ b/src/pcf/npcf-handler.c @@ -832,10 +832,10 @@ bool pcf_npcf_policyauthorization_handle_create(pcf_sess_t *sess, ogs_freeaddrinfo(addr); ogs_freeaddrinfo(addr6); - rv = pcf_db_qos_data( + rv = pcf_get_session_data( pcf_ue->supi, sess->home.presence == true ? &sess->home.plmn_id : NULL, - &sess->s_nssai, sess->dnn, &session_data); + &sess->s_nssai, sess->dnn, &session_data, 0); if (rv != OGS_OK) { strerror = ogs_msprintf("[%s:%d] Cannot find SUPI in DB", pcf_ue->supi, sess->psi); @@ -1280,10 +1280,10 @@ bool pcf_npcf_policyauthorization_handle_update( } } - rv = pcf_db_qos_data( + rv = pcf_get_session_data( pcf_ue->supi, sess->home.presence == true ? &sess->home.plmn_id : NULL, - &sess->s_nssai, sess->dnn, &session_data); + &sess->s_nssai, sess->dnn, &session_data, 0); if (rv != OGS_OK) { strerror = ogs_msprintf("[%s:%d] Cannot find SUPI in DB", pcf_ue->supi, sess->psi); diff --git a/src/pcf/sbi-path.c b/src/pcf/sbi-path.c index 4d358e45e..0e57adc07 100644 --- a/src/pcf/sbi-path.c +++ b/src/pcf/sbi-path.c @@ -331,10 +331,10 @@ bool pcf_sbi_send_smpolicycontrol_create_response( ogs_assert(pcf_ue->supi); ogs_assert(sess->dnn); - rv = pcf_db_qos_data( + rv = pcf_get_session_data( pcf_ue->supi, sess->home.presence == true ? &sess->home.plmn_id : NULL, - &sess->s_nssai, sess->dnn, &session_data); + &sess->s_nssai, sess->dnn, &session_data, 0); if (rv != OGS_OK) { strerror = ogs_msprintf("[%s:%d] Cannot find SUPI in DB", pcf_ue->supi, sess->psi); diff --git a/src/scp/context.c b/src/scp/context.c index 46f42ae3e..fd0aacce1 100644 --- a/src/scp/context.c +++ b/src/scp/context.c @@ -339,8 +339,6 @@ void scp_assoc_remove(scp_assoc_t *assoc) if (assoc->client) ogs_sbi_client_remove(assoc->client); - if (assoc->nrf_client) - ogs_sbi_client_remove(assoc->nrf_client); if (assoc->target_apiroot) ogs_free(assoc->target_apiroot); diff --git a/src/scp/context.h b/src/scp/context.h index e9420d63f..3ea300233 100644 --- a/src/scp/context.h +++ b/src/scp/context.h @@ -46,7 +46,6 @@ typedef struct scp_assoc_s { ogs_pool_id_t stream_id; ogs_sbi_client_t *client; - ogs_sbi_client_t *nrf_client; ogs_sbi_request_t *request; diff --git a/src/scp/init.c b/src/scp/init.c index e47e95852..400dece19 100644 --- a/src/scp/init.c +++ b/src/scp/init.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -67,6 +67,9 @@ static void event_termination(void) ogs_list_for_each(&ogs_sbi_self()->nf_instance_list, nf_instance) ogs_sbi_nf_fsm_fini(nf_instance); + /* Gracefully shutdown the server by sending GOAWAY to each session. */ + ogs_sbi_server_graceful_shutdown_all(); + /* Starting holding timer */ t_termination_holding = ogs_timer_add(ogs_app()->timer_mgr, NULL, NULL); ogs_assert(t_termination_holding); diff --git a/src/sepp/context.c b/src/sepp/context.c index f76ead345..5d9910ac7 100644 --- a/src/sepp/context.c +++ b/src/sepp/context.c @@ -567,8 +567,6 @@ void sepp_assoc_remove(sepp_assoc_t *assoc) if (assoc->client) ogs_sbi_client_remove(assoc->client); - if (assoc->nrf_client) - ogs_sbi_client_remove(assoc->nrf_client); ogs_pool_free(&sepp_assoc_pool, assoc); } diff --git a/src/sepp/context.h b/src/sepp/context.h index 81cc45d32..1507a33e2 100644 --- a/src/sepp/context.h +++ b/src/sepp/context.h @@ -85,7 +85,6 @@ typedef struct sepp_assoc_s { ogs_pool_id_t stream_id; ogs_sbi_client_t *client; - ogs_sbi_client_t *nrf_client; ogs_sbi_request_t *request; ogs_sbi_service_type_e service_type; diff --git a/src/sepp/init.c b/src/sepp/init.c index 9f0bbc1ee..5dd97753a 100644 --- a/src/sepp/init.c +++ b/src/sepp/init.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 by Sukchan Lee + * Copyright (C) 2023-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -72,6 +72,9 @@ static void event_termination(void) ogs_list_for_each(&sepp_self()->peer_list, sepp_node) sepp_handshake_fsm_fini(sepp_node); + /* Gracefully shutdown the server by sending GOAWAY to each session. */ + ogs_sbi_server_graceful_shutdown_all(); + /* Starting holding timer */ t_termination_holding = ogs_timer_add(ogs_app()->timer_mgr, NULL, NULL); ogs_assert(t_termination_holding); diff --git a/src/sgwu/sxa-handler.c b/src/sgwu/sxa-handler.c index 5153549ee..cb3b065a5 100644 --- a/src/sgwu/sxa-handler.c +++ b/src/sgwu/sxa-handler.c @@ -105,8 +105,11 @@ void sgwu_sxa_handle_session_establishment_request( * a new TEID for the first time, so performing a swap is not appropriate * in this case. */ - if (pdr->f_teid.ch == false && pdr->f_teid_len) - ogs_pfcp_pdr_swap_teid(pdr); + if (pdr->f_teid_len > 0 && pdr->f_teid.ch == false) { + cause_value = ogs_pfcp_pdr_swap_teid(pdr); + if (cause_value != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) + goto cleanup; + } } restoration_indication = true; } @@ -116,6 +119,7 @@ void sgwu_sxa_handle_session_establishment_request( if (OGS_ERROR == ogs_pfcp_setup_far_gtpu_node(far)) { ogs_fatal("CHECK CONFIGURATION: sgwu.gtpu"); ogs_fatal("ogs_pfcp_setup_far_gtpu_node() failed"); + cause_value = OGS_PFCP_CAUSE_SYSTEM_FAILURE; goto cleanup; } if (far->gnode) diff --git a/src/smf/gsm-sm.c b/src/smf/gsm-sm.c index c2aac7a62..fe6a59b40 100644 --- a/src/smf/gsm-sm.c +++ b/src/smf/gsm-sm.c @@ -578,7 +578,8 @@ void smf_gsm_state_wait_5gc_sm_policy_association(ogs_fsm_t *s, smf_event_t *e) ogs_sbi_server_send_error( stream, sbi_message->res_status, sbi_message, strerror, NULL, - sbi_message->ProblemDetails->cause)); + (sbi_message->ProblemDetails) ? + sbi_message->ProblemDetails->cause : NULL)); ogs_free(strerror); OGS_FSM_TRAN(s, smf_gsm_state_exception); @@ -1100,7 +1101,8 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) ogs_sbi_server_send_error( stream, sbi_message->res_status, sbi_message, strerror, NULL, - sbi_message->ProblemDetails->cause)); + (sbi_message->ProblemDetails) ? + sbi_message->ProblemDetails->cause : NULL)); ogs_free(strerror); OGS_FSM_TRAN(s, smf_gsm_state_exception); diff --git a/src/smf/init.c b/src/smf/init.c index 2e70e86ad..7de506cc0 100644 --- a/src/smf/init.c +++ b/src/smf/init.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -105,6 +105,9 @@ static void event_termination(void) ogs_list_for_each(&ogs_sbi_self()->nf_instance_list, nf_instance) ogs_sbi_nf_fsm_fini(nf_instance); + /* Gracefully shutdown the server by sending GOAWAY to each session. */ + ogs_sbi_server_graceful_shutdown_all(); + /* Starting holding timer */ t_termination_holding = ogs_timer_add(ogs_app()->timer_mgr, NULL, NULL); ogs_assert(t_termination_holding); diff --git a/src/smf/nsmf-handler.c b/src/smf/nsmf-handler.c index 777bd7935..b1e7b63ce 100644 --- a/src/smf/nsmf-handler.c +++ b/src/smf/nsmf-handler.c @@ -1360,7 +1360,7 @@ bool smf_nsmf_handle_create_pdu_session_in_hsmf( if (sess->remote_dl_ip.ipv4 && sess->remote_dl_ip.ipv6) sess->remote_dl_ip.len = OGS_IPV4V6_LEN; - sess->remote_dl_teid = ogs_uint64_from_string(vcnTunnelInfo->gtp_teid); + sess->remote_dl_teid = ogs_uint64_from_string_hexadecimal(vcnTunnelInfo->gtp_teid); ogs_debug("vcnTunnelInfo->ipv4 = 0x%x", sess->remote_dl_ip.addr); ogs_log_hexdump(OGS_LOG_DEBUG, sess->remote_dl_ip.addr6, OGS_IPV6_LEN); ogs_debug("vcnTunnelInfo->gtp_teid = 0x%x", sess->remote_dl_teid); @@ -1752,7 +1752,7 @@ bool smf_nsmf_handle_create_pdu_session_in_vsmf( if (sess->remote_ul_ip.ipv4 && sess->remote_ul_ip.ipv6) sess->remote_ul_ip.len = OGS_IPV4V6_LEN; - sess->remote_ul_teid = ogs_uint64_from_string(hcnTunnelInfo->gtp_teid); + sess->remote_ul_teid = ogs_uint64_from_string_hexadecimal(hcnTunnelInfo->gtp_teid); ogs_debug("hcnTunnelInfo->ipv4 = 0x%x", sess->remote_ul_ip.addr); ogs_log_hexdump(OGS_LOG_DEBUG, sess->remote_ul_ip.addr6, OGS_IPV6_LEN); ogs_debug("hcnTunnelInfo->gtp_teid = 0x%x", sess->remote_ul_teid); @@ -2029,7 +2029,7 @@ bool smf_nsmf_handle_create_pdu_session_in_vsmf( } if (PduSessionCreateError->n1sm_cause) - gsm_cause = ogs_uint64_from_string( + gsm_cause = ogs_uint64_from_string_hexadecimal( PduSessionCreateError->n1sm_cause); ogs_error("CreatePduSession() failed [%d] cause [%d]", diff --git a/src/smf/nudm-build.c b/src/smf/nudm-build.c index 1834290c3..5d1de7847 100644 --- a/src/smf/nudm-build.c +++ b/src/smf/nudm-build.c @@ -236,9 +236,6 @@ ogs_sbi_request_t *smf_nudm_sdm_build_subscription( message.SDMSubscription = &SDMSubscription; - message.http.custom.callback = - (char *)OGS_SBI_CALLBACK_NUDM_SDM_NOTIFICATION; - request = ogs_sbi_build_request(&message); ogs_expect(request); diff --git a/src/udm/init.c b/src/udm/init.c index 5714bad9a..1842e5a93 100644 --- a/src/udm/init.c +++ b/src/udm/init.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -65,6 +65,9 @@ static void event_termination(void) ogs_list_for_each(&ogs_sbi_self()->nf_instance_list, nf_instance) ogs_sbi_nf_fsm_fini(nf_instance); + /* Gracefully shutdown the server by sending GOAWAY to each session. */ + ogs_sbi_server_graceful_shutdown_all(); + /* Starting holding timer */ t_termination_holding = ogs_timer_add(ogs_app()->timer_mgr, NULL, NULL); ogs_assert(t_termination_holding); diff --git a/src/udm/nudm-handler.c b/src/udm/nudm-handler.c index c48db604b..264993c0d 100644 --- a/src/udm/nudm-handler.c +++ b/src/udm/nudm-handler.c @@ -75,7 +75,7 @@ bool udm_nudm_ueau_handle_get( r = udm_ue_sbi_discover_and_send(OGS_SBI_SERVICE_TYPE_NUDR_DR, NULL, udm_nudr_dr_build_authentication_subscription, - udm_ue, stream, NULL); + udm_ue, stream, UDM_SBI_NO_STATE, NULL); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); @@ -165,7 +165,7 @@ bool udm_nudm_ueau_handle_get( r = udm_ue_sbi_discover_and_send(OGS_SBI_SERVICE_TYPE_NUDR_DR, NULL, udm_nudr_dr_build_authentication_subscription, - udm_ue, stream, udm_ue->sqn); + udm_ue, stream, UDM_SBI_NO_STATE, udm_ue->sqn); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); } @@ -237,7 +237,7 @@ bool udm_nudm_ueau_handle_result_confirmation_inform( r = udm_ue_sbi_discover_and_send(OGS_SBI_SERVICE_TYPE_NUDR_DR, NULL, udm_nudr_dr_build_update_authentication_status, - udm_ue, stream, NULL); + udm_ue, stream, UDM_SBI_NO_STATE, NULL); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); @@ -346,7 +346,8 @@ bool udm_nudm_uecm_handle_amf_registration( message->Amf3GppAccessRegistration); r = udm_ue_sbi_discover_and_send(OGS_SBI_SERVICE_TYPE_NUDR_DR, NULL, - udm_nudr_dr_build_update_amf_context, udm_ue, stream, NULL); + udm_nudr_dr_build_update_amf_context, udm_ue, stream, + UDM_SBI_NO_STATE, NULL); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); @@ -473,7 +474,7 @@ bool udm_nudm_uecm_handle_amf_registration_update( r = udm_ue_sbi_discover_and_send(OGS_SBI_SERVICE_TYPE_NUDR_DR, NULL, udm_nudr_dr_build_patch_amf_context, - udm_ue, stream, PatchItemList); + udm_ue, stream, UDM_SBI_NO_STATE, PatchItemList); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); @@ -587,7 +588,8 @@ bool udm_nudm_uecm_handle_smf_registration( OpenAPI_smf_registration_copy(sess->smf_registration, SmfRegistration); r = udm_sess_sbi_discover_and_send(OGS_SBI_SERVICE_TYPE_NUDR_DR, NULL, - udm_nudr_dr_build_update_smf_context, sess, stream, NULL); + udm_nudr_dr_build_update_smf_context, sess, stream, + UDM_SBI_NO_STATE, NULL); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); @@ -608,7 +610,8 @@ bool udm_nudm_uecm_handle_smf_deregistration( ogs_assert(udm_ue); r = udm_sess_sbi_discover_and_send(OGS_SBI_SERVICE_TYPE_NUDR_DR, NULL, - udm_nudr_dr_build_delete_smf_context, sess, stream, NULL); + udm_nudr_dr_build_delete_smf_context, sess, stream, + UDM_SBI_NO_STATE, NULL); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); diff --git a/src/udm/nudr-build.c b/src/udm/nudr-build.c index ae0683a3a..2549ee5de 100644 --- a/src/udm/nudr-build.c +++ b/src/udm/nudr-build.c @@ -239,6 +239,12 @@ ogs_sbi_request_t *udm_nudr_dr_build_query_subscription_provisioned( (char *)OGS_SBI_RESOURCE_NAME_SMF_SELECTION_SUBSCRIPTION_DATA; break; + CASE(OGS_SBI_RESOURCE_NAME_NSSAI) + sendmsg.h.resource.component[4] = (char *)OGS_SBI_RESOURCE_NAME_AM_DATA; + sendmsg.param.fields[0] = (char *)OGS_SBI_RESOURCE_NAME_NSSAI; + sendmsg.param.num_of_fields = 1; + break; + DEFAULT END diff --git a/src/udm/nudr-handler.c b/src/udm/nudr-handler.c index 027482048..035f614ef 100644 --- a/src/udm/nudr-handler.c +++ b/src/udm/nudr-handler.c @@ -18,6 +18,7 @@ */ #include "nudr-handler.h" +#include "sbi-path.h" bool udm_nudr_dr_handle_subscription_authentication( udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg) @@ -89,7 +90,8 @@ bool udm_nudr_dr_handle_subscription_authentication( ogs_assert(true == ogs_sbi_server_send_error( stream, recvmsg->res_status, recvmsg, strerror, NULL, - recvmsg->ProblemDetails->cause)); + (recvmsg->ProblemDetails) ? + recvmsg->ProblemDetails->cause : NULL)); ogs_free(strerror); return false; } @@ -194,7 +196,8 @@ bool udm_nudr_dr_handle_subscription_authentication( ogs_assert(true == ogs_sbi_server_send_error( stream, recvmsg->res_status, recvmsg, strerror, NULL, - recvmsg->ProblemDetails->cause)); + (recvmsg->ProblemDetails) ? + recvmsg->ProblemDetails->cause : NULL)); ogs_free(strerror); return false; } @@ -283,7 +286,8 @@ bool udm_nudr_dr_handle_subscription_authentication( ogs_assert(true == ogs_sbi_server_send_error( stream, recvmsg->res_status, recvmsg, strerror, NULL, - recvmsg->ProblemDetails->cause)); + (recvmsg->ProblemDetails) ? + recvmsg->ProblemDetails->cause : NULL)); ogs_free(strerror); return false; } @@ -415,7 +419,8 @@ bool udm_nudr_dr_handle_subscription_context( ogs_assert(true == ogs_sbi_server_send_error(stream, recvmsg->res_status, NULL, "HTTP response error", udm_ue->supi, - recvmsg->ProblemDetails->cause)); + (recvmsg->ProblemDetails) ? + recvmsg->ProblemDetails->cause : NULL)); return false; } @@ -601,7 +606,8 @@ bool udm_nudr_dr_handle_subscription_context( } bool udm_nudr_dr_handle_subscription_provisioned( - udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg) + udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, int state, + ogs_sbi_message_t *recvmsg) { char *strerror = NULL; ogs_sbi_server_t *server = NULL; @@ -636,6 +642,29 @@ bool udm_nudr_dr_handle_subscription_provisioned( memset(&sendmsg, 0, sizeof(sendmsg)); + /* Check if original request was for /nudm-sdm/v2/{supi}/nssai */ + if (state == UDM_SBI_UE_PROVISIONED_NSSAI_ONLY) { + OpenAPI_nssai_t *Nssai = NULL; + Nssai = AccessAndMobilitySubscriptionData->nssai; + if (!Nssai) { + ogs_error("[%s] No Nssai", udm_ue->supi); + ogs_assert(true == + ogs_sbi_server_send_error( + stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, + recvmsg, "No Nssai", + udm_ue->supi, NULL)); + return false; + } + + sendmsg.Nssai = OpenAPI_nssai_copy(sendmsg.Nssai, Nssai); + response = ogs_sbi_build_response(&sendmsg, recvmsg->res_status); + ogs_assert(response); + ogs_assert(true == ogs_sbi_server_send_response(stream, response)); + OpenAPI_nssai_free(sendmsg.Nssai); + + break; + } + sendmsg.AccessAndMobilitySubscriptionData = OpenAPI_access_and_mobility_subscription_data_copy( sendmsg.AccessAndMobilitySubscriptionData, @@ -768,7 +797,8 @@ bool udm_nudr_dr_handle_smf_registration( ogs_assert(true == ogs_sbi_server_send_error(stream, recvmsg->res_status, NULL, "HTTP response error", udm_ue->supi, - recvmsg->ProblemDetails->cause)); + (recvmsg->ProblemDetails) ? + recvmsg->ProblemDetails->cause : NULL)); return false; } diff --git a/src/udm/nudr-handler.h b/src/udm/nudr-handler.h index 378d7b2f4..9642199ef 100644 --- a/src/udm/nudr-handler.h +++ b/src/udm/nudr-handler.h @@ -31,7 +31,8 @@ bool udm_nudr_dr_handle_subscription_authentication( bool udm_nudr_dr_handle_subscription_context( udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg); bool udm_nudr_dr_handle_subscription_provisioned( - udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg); + udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, int state, + ogs_sbi_message_t *recvmsg); bool udm_nudr_dr_handle_smf_registration( udm_sess_t *sess, ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg); diff --git a/src/udm/sbi-path.c b/src/udm/sbi-path.c index ad771e522..9bf385d2f 100644 --- a/src/udm/sbi-path.c +++ b/src/udm/sbi-path.c @@ -102,7 +102,7 @@ static int udm_sbi_discover_and_send( ogs_sbi_service_type_e service_type, ogs_sbi_discovery_option_t *discovery_option, ogs_sbi_build_f build, - void *context, ogs_sbi_stream_t *stream, void *data) + void *context, ogs_sbi_stream_t *stream, int state, void *data) { ogs_sbi_xact_t *xact = NULL; int r; @@ -123,6 +123,8 @@ static int udm_sbi_discover_and_send( return OGS_ERROR; } + xact->state = state; + if (stream) { xact->assoc_stream_id = ogs_sbi_id_from_stream(stream); ogs_assert(xact->assoc_stream_id >= OGS_MIN_POOL_ID && @@ -143,7 +145,7 @@ int udm_ue_sbi_discover_and_send( ogs_sbi_service_type_e service_type, ogs_sbi_discovery_option_t *discovery_option, ogs_sbi_request_t *(*build)(udm_ue_t *udm_ue, void *data), - udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, void *data) + udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, int state, void *data) { int r; @@ -151,7 +153,7 @@ int udm_ue_sbi_discover_and_send( r = udm_sbi_discover_and_send( udm_ue->id, &udm_ue->sbi, service_type, discovery_option, - (ogs_sbi_build_f)build, udm_ue, stream, data); + (ogs_sbi_build_f)build, udm_ue, stream, state, data); if (r != OGS_OK) { ogs_error("udm_ue_sbi_discover_and_send() failed"); ogs_assert(true == @@ -168,7 +170,7 @@ int udm_sess_sbi_discover_and_send( ogs_sbi_service_type_e service_type, ogs_sbi_discovery_option_t *discovery_option, ogs_sbi_request_t *(*build)(udm_sess_t *sess, void *data), - udm_sess_t *sess, ogs_sbi_stream_t *stream, void *data) + udm_sess_t *sess, ogs_sbi_stream_t *stream, int state, void *data) { int r; @@ -176,7 +178,7 @@ int udm_sess_sbi_discover_and_send( r = udm_sbi_discover_and_send( sess->id, &sess->sbi, service_type, discovery_option, - (ogs_sbi_build_f)build, sess, stream, data); + (ogs_sbi_build_f)build, sess, stream, state, data); if (r != OGS_OK) { ogs_error("udm_sess_sbi_discover_and_send() failed"); ogs_assert(true == diff --git a/src/udm/sbi-path.h b/src/udm/sbi-path.h index 886f6d9d3..32c2268ee 100644 --- a/src/udm/sbi-path.h +++ b/src/udm/sbi-path.h @@ -31,16 +31,20 @@ void udm_sbi_close(void); bool udm_sbi_send_request( ogs_sbi_nf_instance_t *nf_instance, ogs_sbi_xact_t *xact); + +#define UDM_SBI_NO_STATE 0 +#define UDM_SBI_UE_PROVISIONED_NSSAI_ONLY 1 + int udm_ue_sbi_discover_and_send( ogs_sbi_service_type_e service_type, ogs_sbi_discovery_option_t *discovery_option, ogs_sbi_request_t *(*build)(udm_ue_t *udm_ue, void *data), - udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, void *data); + udm_ue_t *udm_ue, ogs_sbi_stream_t *stream, int state, void *data); int udm_sess_sbi_discover_and_send( ogs_sbi_service_type_e service_type, ogs_sbi_discovery_option_t *discovery_option, ogs_sbi_request_t *(*build)(udm_sess_t *sess, void *data), - udm_sess_t *sess, ogs_sbi_stream_t *stream, void *data); + udm_sess_t *sess, ogs_sbi_stream_t *stream, int state, void *data); #ifdef __cplusplus } diff --git a/src/udm/udm-sm.c b/src/udm/udm-sm.c index 309f73d1c..1c1b0995f 100644 --- a/src/udm/udm-sm.c +++ b/src/udm/udm-sm.c @@ -176,16 +176,19 @@ void udm_state_operational(ogs_fsm_t *s, udm_event_t *e) udm_ue = udm_ue_find_by_suci_or_supi( message.h.resource.component[0]); if (!udm_ue) { - if (!strcmp(message.h.method, - OGS_SBI_HTTP_METHOD_POST)) { + SWITCH(message.h.method) + CASE(OGS_SBI_HTTP_METHOD_POST) + CASE(OGS_SBI_HTTP_METHOD_GET) udm_ue = udm_ue_add(message.h.resource.component[0]); if (!udm_ue) { ogs_error("Invalid Request [%s]", message.h.resource.component[0]); } - } else { + break; + + DEFAULT ogs_error("Invalid HTTP method [%s]", message.h.method); - } + END } } @@ -461,6 +464,8 @@ void udm_state_operational(ogs_fsm_t *s, udm_event_t *e) e->h.sbi.data = OGS_UINT_TO_POINTER(sbi_xact->assoc_stream_id); + e->h.sbi.state = sbi_xact->state; + ogs_sbi_xact_remove(sbi_xact); udm_ue = udm_ue_find_by_id(udm_ue_id); diff --git a/src/udm/ue-sm.c b/src/udm/ue-sm.c index 3e4261853..550aea30c 100644 --- a/src/udm/ue-sm.c +++ b/src/udm/ue-sm.c @@ -196,7 +196,17 @@ void udm_ue_state_operational(ogs_fsm_t *s, udm_event_t *e) r = udm_ue_sbi_discover_and_send( OGS_SBI_SERVICE_TYPE_NUDR_DR, NULL, udm_nudr_dr_build_query_subscription_provisioned, - udm_ue, stream, message); + udm_ue, stream, UDM_SBI_NO_STATE, message); + ogs_expect(r == OGS_OK); + ogs_assert(r != OGS_ERROR); + break; + + CASE(OGS_SBI_RESOURCE_NAME_NSSAI) + r = udm_ue_sbi_discover_and_send( + OGS_SBI_SERVICE_TYPE_NUDR_DR, NULL, + udm_nudr_dr_build_query_subscription_provisioned, + udm_ue, stream, UDM_SBI_UE_PROVISIONED_NSSAI_ONLY, + message); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); break; @@ -305,7 +315,7 @@ void udm_ue_state_operational(ogs_fsm_t *s, udm_event_t *e) SWITCH(message->h.resource.component[3]) CASE(OGS_SBI_RESOURCE_NAME_PROVISIONED_DATA) udm_nudr_dr_handle_subscription_provisioned( - udm_ue, stream, message); + udm_ue, stream, e->h.sbi.state, message); break; DEFAULT diff --git a/src/udr/init.c b/src/udr/init.c index 831e02916..87d1f9622 100644 --- a/src/udr/init.c +++ b/src/udr/init.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2023 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * This file is part of Open5GS. * @@ -68,6 +68,9 @@ static void event_termination(void) ogs_list_for_each(&ogs_sbi_self()->nf_instance_list, nf_instance) ogs_sbi_nf_fsm_fini(nf_instance); + /* Gracefully shutdown the server by sending GOAWAY to each session. */ + ogs_sbi_server_graceful_shutdown_all(); + /* Starting holding timer */ t_termination_holding = ogs_timer_add(ogs_app()->timer_mgr, NULL, NULL); ogs_assert(t_termination_holding); diff --git a/src/udr/nudr-handler.c b/src/udr/nudr-handler.c index 6a5fb5af2..ee58291b4 100644 --- a/src/udr/nudr-handler.c +++ b/src/udr/nudr-handler.c @@ -467,6 +467,9 @@ bool udr_nudr_dr_handle_subscription_provisioned( SWITCH(recvmsg->h.resource.component[4]) CASE(OGS_SBI_RESOURCE_NAME_AM_DATA) int i; + bool processGpsi = false; + bool processUeAmbr = false; + bool processNssai = false; OpenAPI_access_and_mobility_subscription_data_t AccessAndMobilitySubscriptionData; @@ -480,77 +483,110 @@ bool udr_nudr_dr_handle_subscription_provisioned( OpenAPI_lnode_t *node = NULL; - GpsiList = OpenAPI_list_create(); - for (i = 0; i < subscription_data.num_of_msisdn; i++) { - char *gpsi = ogs_msprintf("%s-%s", - OGS_ID_GPSI_TYPE_MSISDN, subscription_data.msisdn[i].bcd); - ogs_assert(gpsi); - OpenAPI_list_add(GpsiList, gpsi); - } - - SubscribedUeAmbr.uplink = ogs_sbi_bitrate_to_string( - subscription_data.ambr.uplink, OGS_SBI_BITRATE_KBPS); - SubscribedUeAmbr.downlink = ogs_sbi_bitrate_to_string( - subscription_data.ambr.downlink, OGS_SBI_BITRATE_KBPS); - - memset(&NSSAI, 0, sizeof(NSSAI)); - DefaultSingleNssaiList = OpenAPI_list_create(); - for (i = 0; i < subscription_data.num_of_slice; i++) { - slice_data = &subscription_data.slice[i]; - - if (slice_data->default_indicator == false) - continue; - - Snssai = ogs_calloc(1, sizeof(*Snssai)); - ogs_assert(Snssai); - - Snssai->sst = slice_data->s_nssai.sst; - Snssai->sd = ogs_s_nssai_sd_to_string(slice_data->s_nssai.sd); - - OpenAPI_list_add(DefaultSingleNssaiList, Snssai); - } - if (DefaultSingleNssaiList->count) { - NSSAI.default_single_nssais = DefaultSingleNssaiList; - } - - SingleNssaiList = OpenAPI_list_create(); - for (i = 0; i < subscription_data.num_of_slice; i++) { - slice_data = &subscription_data.slice[i]; - - if (slice_data->default_indicator == true) - continue; - - Snssai = ogs_calloc(1, sizeof(*Snssai)); - ogs_assert(Snssai); - - Snssai->sst = slice_data->s_nssai.sst; - Snssai->sd = ogs_s_nssai_sd_to_string(slice_data->s_nssai.sd); - - OpenAPI_list_add(SingleNssaiList, Snssai); - } - - if (DefaultSingleNssaiList->count) { - if (SingleNssaiList->count) { - NSSAI.single_nssais = SingleNssaiList; - } - } else { - if (SingleNssaiList->count) { - ogs_fatal("No Default S-NSSAI"); - ogs_assert_if_reached(); - } - } - memset(&AccessAndMobilitySubscriptionData, 0, sizeof(AccessAndMobilitySubscriptionData)); - if (GpsiList->count) - AccessAndMobilitySubscriptionData.gpsis = GpsiList; + memset(&SubscribedUeAmbr, 0, sizeof(SubscribedUeAmbr)); + memset(&NSSAI, 0, sizeof(NSSAI)); - AccessAndMobilitySubscriptionData.subscribed_ue_ambr = - &SubscribedUeAmbr; + /* Apply filtering based on fields query parameter */ + if (recvmsg->param.num_of_fields) { + for (i = 0; i < recvmsg->param.num_of_fields; i++) { + SWITCH(recvmsg->param.fields[i]) + CASE(OGS_SBI_PARAM_FIELDS_GPSIS) + processGpsi = true; + break; + CASE(OGS_SBI_PARAM_FIELDS_SUBSCRIBED_UE_AMBR) + processUeAmbr = true; + break; + CASE(OGS_SBI_PARAM_FIELDS_NSSAI) + processNssai = true; + break; + DEFAULT + ogs_error("Unexpected field! [%s]", + recvmsg->param.fields[i]); + END + } + } else { + processGpsi = true; + processUeAmbr = true; + processNssai = true; + } - if (DefaultSingleNssaiList->count) - AccessAndMobilitySubscriptionData.nssai = &NSSAI; + if (processGpsi) { + GpsiList = OpenAPI_list_create(); + for (i = 0; i < subscription_data.num_of_msisdn; i++) { + char *gpsi = ogs_msprintf("%s-%s", + OGS_ID_GPSI_TYPE_MSISDN, + subscription_data.msisdn[i].bcd); + ogs_assert(gpsi); + OpenAPI_list_add(GpsiList, gpsi); + } + + if (GpsiList->count) + AccessAndMobilitySubscriptionData.gpsis = GpsiList; + } + + if (processUeAmbr) { + SubscribedUeAmbr.uplink = ogs_sbi_bitrate_to_string( + subscription_data.ambr.uplink, OGS_SBI_BITRATE_KBPS); + SubscribedUeAmbr.downlink = ogs_sbi_bitrate_to_string( + subscription_data.ambr.downlink, OGS_SBI_BITRATE_KBPS); + + AccessAndMobilitySubscriptionData.subscribed_ue_ambr = + &SubscribedUeAmbr; + } + + if (processNssai) { + DefaultSingleNssaiList = OpenAPI_list_create(); + for (i = 0; i < subscription_data.num_of_slice; i++) { + slice_data = &subscription_data.slice[i]; + + if (slice_data->default_indicator == false) + continue; + + Snssai = ogs_calloc(1, sizeof(*Snssai)); + ogs_assert(Snssai); + + Snssai->sst = slice_data->s_nssai.sst; + Snssai->sd = ogs_s_nssai_sd_to_string(slice_data->s_nssai.sd); + + OpenAPI_list_add(DefaultSingleNssaiList, Snssai); + } + if (DefaultSingleNssaiList->count) { + NSSAI.default_single_nssais = DefaultSingleNssaiList; + } + + SingleNssaiList = OpenAPI_list_create(); + for (i = 0; i < subscription_data.num_of_slice; i++) { + slice_data = &subscription_data.slice[i]; + + if (slice_data->default_indicator == true) + continue; + + Snssai = ogs_calloc(1, sizeof(*Snssai)); + ogs_assert(Snssai); + + Snssai->sst = slice_data->s_nssai.sst; + Snssai->sd = ogs_s_nssai_sd_to_string(slice_data->s_nssai.sd); + + OpenAPI_list_add(SingleNssaiList, Snssai); + } + + if (DefaultSingleNssaiList->count) { + if (SingleNssaiList->count) { + NSSAI.single_nssais = SingleNssaiList; + } + } else { + if (SingleNssaiList->count) { + ogs_fatal("No Default S-NSSAI"); + ogs_assert_if_reached(); + } + } + + if (DefaultSingleNssaiList->count) + AccessAndMobilitySubscriptionData.nssai = &NSSAI; + } memset(&sendmsg, 0, sizeof(sendmsg)); sendmsg.AccessAndMobilitySubscriptionData = @@ -565,8 +601,10 @@ bool udr_nudr_dr_handle_subscription_provisioned( } OpenAPI_list_free(GpsiList); - ogs_free(SubscribedUeAmbr.uplink); - ogs_free(SubscribedUeAmbr.downlink); + if (SubscribedUeAmbr.uplink) + ogs_free(SubscribedUeAmbr.uplink); + if (SubscribedUeAmbr.downlink) + ogs_free(SubscribedUeAmbr.downlink); OpenAPI_list_for_each(DefaultSingleNssaiList, node) { OpenAPI_snssai_t *Snssai = node->data; diff --git a/src/upf/context.c b/src/upf/context.c index 84b1d3057..d7c5219cb 100644 --- a/src/upf/context.c +++ b/src/upf/context.c @@ -400,7 +400,6 @@ uint8_t upf_sess_set_ue_ip(upf_sess_t *sess, uint8_t cause_value = OGS_PFCP_CAUSE_REQUEST_ACCEPTED; ogs_assert(sess); - ogs_assert(session_type); ogs_assert(pdr); ogs_assert(pdr->ue_ip_addr_len); ue_ip = &pdr->ue_ip_addr; @@ -487,9 +486,10 @@ uint8_t upf_sess_set_ue_ip(upf_sess_t *sess, pdr->dnn ? pdr->dnn : ""); } } else { - ogs_warn("Cannot support PDN-Type[%d], [IPv4:%d IPv6:%d DNN:%s]", + ogs_error("Invalid PDN-Type[%d], [IPv4:%d IPv6:%d DNN:%s]", session_type, ue_ip->ipv4, ue_ip->ipv6, pdr->dnn ? pdr->dnn : ""); + return OGS_PFCP_CAUSE_SERVICE_NOT_SUPPORTED; } ogs_info("UE F-SEID[UP:0x%lx CP:0x%lx] " diff --git a/src/upf/n4-handler.c b/src/upf/n4-handler.c index 18d7e836b..cad00b705 100644 --- a/src/upf/n4-handler.c +++ b/src/upf/n4-handler.c @@ -162,8 +162,11 @@ void upf_n4_handle_session_establishment_request( * a new TEID for the first time, so performing a swap is not appropriate * in this case. */ - if (pdr->f_teid.ch == false && pdr->f_teid_len) - ogs_pfcp_pdr_swap_teid(pdr); + if (pdr->f_teid_len > 0 && pdr->f_teid.ch == false) { + cause_value = ogs_pfcp_pdr_swap_teid(pdr); + if (cause_value != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) + goto cleanup; + } } restoration_indication = true; } diff --git a/tests/attach/abts-main.c b/tests/attach/abts-main.c index 8a1654019..6a99e30f9 100644 --- a/tests/attach/abts-main.c +++ b/tests/attach/abts-main.c @@ -75,7 +75,7 @@ int main(int argc, const char *const argv[]) abts_suite *suite = NULL; atexit(terminate); - test_app_run(argc, argv, "sample.yaml", initialize); + test_app_run(argc, argv, "attach.yaml", initialize); for (i = 0; alltests[i].func; i++) suite = alltests[i].func(suite); diff --git a/tests/attach/auth-test.c b/tests/attach/auth-test.c index 622750b46..9a41434c6 100644 --- a/tests/attach/auth-test.c +++ b/tests/attach/auth-test.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * 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); diff --git a/tests/attach/crash-test.c b/tests/attach/crash-test.c index f51f9cc57..af6a69408 100644 --- a/tests/attach/crash-test.c +++ b/tests/attach/crash-test.c @@ -103,6 +103,7 @@ static void test3_func(abts_case *tc, void *data) } #endif +#if 0 static void test4_func(abts_case *tc, void *data) { int rv; @@ -140,7 +141,7 @@ static void test4_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x1079baf0; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; @@ -405,6 +406,41 @@ static void test4_func(abts_case *tc, void *data) test_ue_remove(test_ue); } +#endif + +static void test5_func(abts_case *tc, void *data) +{ + int rv; + ogs_socknode_t *s1ap; + ogs_pkbuf_t *sendbuf; + ogs_pkbuf_t *recvbuf; + + s1ap = tests1ap_client(AF_INET); + ABTS_PTR_NOTNULL(tc, s1ap); + + sendbuf = test_s1ap_build_s1_setup_request( + S1AP_ENB_ID_PR_macroENB_ID, 0x54f64); + ABTS_PTR_NOTNULL(tc, sendbuf); + + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + sendbuf = test_s1ap_build_malformed_enb_direct_information_transfer(0); + ABTS_PTR_NOTNULL(tc, sendbuf); + + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + testenb_s1ap_close(s1ap); +} abts_suite *test_crash(abts_suite *suite) { @@ -415,7 +451,39 @@ abts_suite *test_crash(abts_suite *suite) #if 0 /* Commenting to suppress error messages */ abts_run_test(suite, test3_func, NULL); #endif + +/* +Assume the UE has attached, the session has been created, and is in the +IDLE state with the UEContextRelease process. This may lead to the following +call flow: +1. TAU request without integrity protection +2. Authentication request/response +3. Security-mode command/complete + +The MME may be processed concurrently by the HSS (S6A) and the UE (S1AP) +as follows: + - Update-Location-Request + - Service request + - Service reject + - Delete Session Request + - Delete Session Response + - Update-Location-Answer + - UEContextReleaseCommand for Service reject + - TAU reject + - UEContextReleaseCommand for TAU reject + - UEContextReleaseComplete (for Service reject) + - UEContextReleaseComplete (for TAU reject) + +If the Update-Location-Answer is received before the Delete Session Response, +the session still exists, and a TAU accept may be received. This causes the +test procedure to wait indefinitely. Due to this issue, the test code has +been commented out. +*/ +#if 0 abts_run_test(suite, test4_func, NULL); +#endif + + abts_run_test(suite, test5_func, NULL); return suite; } diff --git a/tests/attach/guti-test.c b/tests/attach/guti-test.c index 1b4aeb685..fa6150b96 100644 --- a/tests/attach/guti-test.c +++ b/tests/attach/guti-test.c @@ -56,7 +56,7 @@ static void test1_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x1079baf0; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; @@ -1275,7 +1275,7 @@ static void test4_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x64010; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_EPS_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; diff --git a/tests/attach/issues-test.c b/tests/attach/issues-test.c index f8f4943b3..c790c600c 100644 --- a/tests/attach/issues-test.c +++ b/tests/attach/issues-test.c @@ -1911,7 +1911,7 @@ static void pull_3122_v270_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x1079baf0; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; diff --git a/tests/attach/simple-test.c b/tests/attach/simple-test.c index 355f3a04a..4ee96193d 100644 --- a/tests/attach/simple-test.c +++ b/tests/attach/simple-test.c @@ -56,7 +56,7 @@ static void test1_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x1079baf0; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; diff --git a/tests/attach/ue-context-test.c b/tests/attach/ue-context-test.c index 688d1344d..c5cfbf95d 100644 --- a/tests/attach/ue-context-test.c +++ b/tests/attach/ue-context-test.c @@ -217,7 +217,7 @@ static void test2_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x64010; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_EPS_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; @@ -518,7 +518,7 @@ static void test3_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x64010; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_EPS_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; diff --git a/tests/common/s1ap-build.c b/tests/common/s1ap-build.c index be58b697b..7b5bd9b0c 100644 --- a/tests/common/s1ap-build.c +++ b/tests/common/s1ap-build.c @@ -2196,3 +2196,28 @@ ogs_pkbuf_t *test_s1ap_build_oversized_message(int i) return pkbuf; } + +ogs_pkbuf_t *test_s1ap_build_malformed_enb_direct_information_transfer(int i) +{ + ogs_pkbuf_t *pkbuf = NULL; + const char *payload[TEST_S1AP_MAX_MESSAGE] = { + "0025" + "0110000001000000 1000008200000000 1000010000001000 0082000000001000" + "00", + "", + }; + + uint16_t len[TEST_S1AP_MAX_MESSAGE] = { + 35, + 0, + }; + + char hexbuf[OGS_HUGE_LEN]; + + pkbuf = ogs_pkbuf_alloc(NULL, OGS_MAX_SDU_LEN); + ogs_assert(pkbuf); + ogs_pkbuf_put_data(pkbuf, + ogs_hex_from_string(payload[i], hexbuf, sizeof(hexbuf)), len[i]); + + return pkbuf; +} diff --git a/tests/common/s1ap-build.h b/tests/common/s1ap-build.h index 4e672aa13..7ab278724 100644 --- a/tests/common/s1ap-build.h +++ b/tests/common/s1ap-build.h @@ -73,6 +73,7 @@ ogs_pkbuf_t *test_s1ap_build_malformed_s1_setup_request(int i); ogs_pkbuf_t *test_s1ap_build_malformed_enb_status_transfer(int i); ogs_pkbuf_t *test_s1ap_build_malformed_e_rab_modification_indication(int i); ogs_pkbuf_t *test_s1ap_build_oversized_message(int i); +ogs_pkbuf_t *test_s1ap_build_malformed_enb_direct_information_transfer(int i); #ifdef __cplusplus } diff --git a/tests/non3gpp/epdg-test.c b/tests/non3gpp/epdg-test.c index acf3415df..265e9912e 100644 --- a/tests/non3gpp/epdg-test.c +++ b/tests/non3gpp/epdg-test.c @@ -63,7 +63,7 @@ static void test1_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x1079baf; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; @@ -204,7 +204,7 @@ static void test2_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x1079baf; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; @@ -350,7 +350,7 @@ static void test3_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x1079baf; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; diff --git a/tests/registration/auth-test.c b/tests/registration/auth-test.c index b359baa6e..d739f2bbd 100644 --- a/tests/registration/auth-test.c +++ b/tests/registration/auth-test.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019,2020 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * 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)); diff --git a/tests/registration/reset-test.c b/tests/registration/reset-test.c index aeab6ad5b..4b7321053 100644 --- a/tests/registration/reset-test.c +++ b/tests/registration/reset-test.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019,2020 by Sukchan Lee + * Copyright (C) 2019-2025 by Sukchan Lee * * 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); diff --git a/tests/unit/nas-message-test.c b/tests/unit/nas-message-test.c index 5ec4d0dc3..6220b6d0e 100644 --- a/tests/unit/nas-message-test.c +++ b/tests/unit/nas-message-test.c @@ -410,9 +410,9 @@ static void ogs_nas_eps_message_test9(abts_case *tc, void *data) ABTS_INT_EQUAL(tc, OGS_ERROR, rv); rv = ogs_nas_gprs_timer_3_from_sec(&gprs_timer, 60*2); ABTS_INT_EQUAL(tc, OGS_OK, rv); - ABTS_INT_EQUAL(tc, OGS_NAS_GPRS_TIMER_3_UNIT_MULTIPLES_OF_1_MM, + ABTS_INT_EQUAL(tc, OGS_NAS_GPRS_TIMER_3_UNIT_MULTIPLES_OF_30_SS, gprs_timer.unit); - ABTS_INT_EQUAL(tc, 2, gprs_timer.value); + ABTS_INT_EQUAL(tc, 4, gprs_timer.value); rv = ogs_nas_gprs_timer_3_from_sec(&gprs_timer, 60*2+1); ABTS_INT_EQUAL(tc, OGS_ERROR, rv); rv = ogs_nas_gprs_timer_3_from_sec(&gprs_timer, 60*30); diff --git a/tests/volte/bearer-test.c b/tests/volte/bearer-test.c index 7cf94584d..e8a70d755 100644 --- a/tests/volte/bearer-test.c +++ b/tests/volte/bearer-test.c @@ -53,7 +53,7 @@ static void uni_directional_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x1079baf; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; @@ -297,7 +297,7 @@ static void bi_directional_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x1079baf; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; diff --git a/tests/volte/rx-test.c b/tests/volte/rx-test.c index 4875384f1..51f3972c0 100644 --- a/tests/volte/rx-test.c +++ b/tests/volte/rx-test.c @@ -59,7 +59,7 @@ static void test1_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x1079baf; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; @@ -571,7 +571,7 @@ static void test2_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x1079baf; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; @@ -922,7 +922,7 @@ static void test3_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x1079baf; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; @@ -1270,7 +1270,7 @@ static void test4_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x1079baf; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; @@ -1798,7 +1798,7 @@ static void test5_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x1079baf; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; @@ -2301,7 +2301,7 @@ static void test6_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x1079baf; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; @@ -2723,7 +2723,7 @@ static void test7_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x1079baf; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; @@ -3125,7 +3125,7 @@ static void test_issues3109_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x1079baf; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; @@ -3449,7 +3449,7 @@ static void test_issues3240_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x1079baf; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; diff --git a/tests/volte/session-test.c b/tests/volte/session-test.c index 51df557b2..7867c462d 100644 --- a/tests/volte/session-test.c +++ b/tests/volte/session-test.c @@ -53,7 +53,7 @@ static void test1_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x1079baf; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; diff --git a/tests/volte/simple-test.c b/tests/volte/simple-test.c index 4e834b515..fa8eb6ecb 100644 --- a/tests/volte/simple-test.c +++ b/tests/volte/simple-test.c @@ -59,7 +59,7 @@ static void test1_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x1079baf; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; diff --git a/tests/volte/video-test.c b/tests/volte/video-test.c index 6a8163376..5898c586d 100644 --- a/tests/volte/video-test.c +++ b/tests/volte/video-test.c @@ -57,7 +57,7 @@ static void test1_func(abts_case *tc, void *data) ogs_assert(test_ue); test_ue->e_cgi.cell_id = 0x1079baf; - test_ue->nas.ksi = 0; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc";