Implement PDU Session Release for Home-Routed Roaming and fix N4 step ordering in 4.3.4.2

- Add support for PDU Session Release in 3GPP TS 23.502 section 4.3.4.3
  Note: PCF-initiated release flow for Home-Routed Roaming is not implemented;
- Fix N4 release step ordering in 3GPP TS 23.502 section 4.3.4.2 UE or network requested PDU Session Release for Non-Roaming and Roaming with Local Breakout
This commit is contained in:
Sukchan Lee 2025-05-02 21:28:25 +09:00
parent d66d6f868a
commit ca61a901d5
45 changed files with 2442 additions and 722 deletions

View file

@ -2469,6 +2469,8 @@ void ogs_sbi_object_free(ogs_sbi_object_t *sbi_object)
if (sbi_object->nf_type_array[i].nf_instance_id)
ogs_free(sbi_object->nf_type_array[i].nf_instance_id);
}
if (sbi_object->home_nsmf_pdusession.nf_instance_id)
ogs_free(sbi_object->home_nsmf_pdusession.nf_instance_id);
}
ogs_sbi_xact_t *ogs_sbi_xact_add(

View file

@ -161,6 +161,8 @@ void ogs_sbi_message_free(ogs_sbi_message_t *message)
OpenAPI_release_data_free(message->ReleaseData);
if (message->ReleasedData)
OpenAPI_released_data_free(message->ReleasedData);
if (message->StatusNotification)
OpenAPI_status_notification_free(message->StatusNotification);
if (message->SessionManagementSubscriptionDataList) {
OpenAPI_lnode_t *node = NULL;
OpenAPI_list_for_each(message->SessionManagementSubscriptionDataList, node)
@ -1567,6 +1569,10 @@ static char *build_json(ogs_sbi_message_t *message)
} else if (message->ReleasedData) {
item = OpenAPI_released_data_convertToJSON(message->ReleasedData);
ogs_assert(item);
} else if (message->StatusNotification) {
item = OpenAPI_status_notification_convertToJSON(
message->StatusNotification);
ogs_assert(item);
} else if (message->SessionManagementSubscriptionDataList) {
OpenAPI_lnode_t *node = NULL;
@ -2509,6 +2515,57 @@ static int parse_json(ogs_sbi_message_t *message,
}
END
break;
CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS)
SWITCH(message->h.resource.component[2])
CASE(OGS_SBI_RESOURCE_NAME_MODIFY)
if (message->res_status == 0) {
message->VsmfUpdateData =
OpenAPI_vsmf_update_data_parseFromJSON(item);
if (!message->VsmfUpdateData) {
rv = OGS_ERROR;
ogs_error("JSON parse error");
}
} else if (message->res_status == OGS_SBI_HTTP_STATUS_OK) {
message->VsmfUpdatedData =
OpenAPI_vsmf_updated_data_parseFromJSON(item);
if (!message->VsmfUpdatedData) {
rv = OGS_ERROR;
ogs_error("JSON parse error");
}
} else if (message->res_status ==
OGS_SBI_HTTP_STATUS_BAD_REQUEST ||
message->res_status ==
OGS_SBI_HTTP_STATUS_FORBIDDEN ||
message->res_status ==
OGS_SBI_HTTP_STATUS_NOT_FOUND ||
message->res_status ==
OGS_SBI_HTTP_STATUS_INTERNAL_SERVER_ERROR ||
message->res_status ==
OGS_SBI_HTTP_STATUS_SERVICE_UNAVAILABLE ||
message->res_status ==
OGS_SBI_HTTP_STATUS_GATEWAY_TIMEOUT) {
message->VsmfUpdateError =
OpenAPI_vsmf_update_error_parseFromJSON(item);
if (!message->VsmfUpdateError) {
rv = OGS_ERROR;
ogs_error("JSON parse error");
}
}
break;
DEFAULT
if (message->res_status < 300) {
message->StatusNotification =
OpenAPI_status_notification_parseFromJSON(item);
if (!message->StatusNotification) {
rv = OGS_ERROR;
ogs_error("JSON parse error");
}
} else {
ogs_error("HTTP ERROR Status : %d",
message->res_status);
}
END
break;
DEFAULT
rv = OGS_ERROR;

View file

@ -121,7 +121,7 @@ extern "C" {
#define OGS_SBI_RESOURCE_NAME_RELEASE "release"
#define OGS_SBI_RESOURCE_NAME_PDU_SESSIONS "pdu-sessions"
#define OGS_SBI_RESOURCE_NAME_PDU_SESSION_STATUS "pdu-session-status"
#define OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS "vsmf-pdu-session"
#define OGS_SBI_RESOURCE_NAME_SM_POLICY_NOTIFY "sm-policy-notify"
#define OGS_SBI_RESOURCE_NAME_N1_N2_FAILURE_NOTIFY "n1-n2-failure-notify"
@ -576,6 +576,7 @@ typedef struct ogs_sbi_message_s {
OpenAPI_vsmf_update_error_t *VsmfUpdateError;
OpenAPI_release_data_t *ReleaseData;
OpenAPI_released_data_t *ReleasedData;
OpenAPI_status_notification_t *StatusNotification;
OpenAPI_list_t *SessionManagementSubscriptionDataList;
OpenAPI_n1_n2_message_transfer_req_data_t *N1N2MessageTransferReqData;
OpenAPI_n1_n2_message_transfer_rsp_data_t *N1N2MessageTransferRspData;

View file

@ -99,6 +99,7 @@
#include "model/ue_context_transfer_rsp_data.h"
#include "model/ue_reg_status_update_req_data.h"
#include "model/ue_reg_status_update_rsp_data.h"
#include "model/status_notification.h"
#include "custom/links.h"

View file

@ -475,9 +475,17 @@ ogs_nas_5gmm_cause_t gmm_handle_registration_update(
ogs_list_for_each(&amf_ue->sess_list, sess) {
if ((psimask & (1 << sess->psi)) == 0) {
if (SESSION_CONTEXT_IN_SMF(sess))
if (SESSION_CONTEXT_IN_SMF(sess)) {
amf_nsmf_pdusession_sm_context_param_t param;
memset(&param, 0, sizeof(param));
param.ue_location = true;
param.ue_timezone = true;
amf_sbi_send_release_session(
ran_ue, sess, AMF_RELEASE_SM_CONTEXT_REGISTRATION_ACCEPT);
ran_ue, sess,
AMF_RELEASE_SM_CONTEXT_REGISTRATION_ACCEPT, &param);
}
}
}
}
@ -799,9 +807,17 @@ ogs_nas_5gmm_cause_t gmm_handle_service_update(
ogs_list_for_each(&amf_ue->sess_list, sess) {
if ((psimask & (1 << sess->psi)) == 0) {
if (SESSION_CONTEXT_IN_SMF(sess))
if (SESSION_CONTEXT_IN_SMF(sess)) {
amf_nsmf_pdusession_sm_context_param_t param;
memset(&param, 0, sizeof(param));
param.ue_location = true;
param.ue_timezone = true;
amf_sbi_send_release_session(
ran_ue, sess, AMF_RELEASE_SM_CONTEXT_SERVICE_ACCEPT);
ran_ue, sess,
AMF_RELEASE_SM_CONTEXT_SERVICE_ACCEPT, &param);
}
}
}
}
@ -849,6 +865,7 @@ int gmm_handle_deregistration_request(amf_ue_t *amf_ue,
int r, state, xact_count = 0;
ran_ue_t *ran_ue = NULL;
ogs_nas_de_registration_type_t *de_registration_type = NULL;
amf_nsmf_pdusession_sm_context_param_t param;
ogs_assert(amf_ue);
ran_ue = ran_ue_find_by_id(amf_ue->ran_ue_id);
@ -883,7 +900,16 @@ int gmm_handle_deregistration_request(amf_ue_t *amf_ue,
xact_count = amf_sess_xact_count(amf_ue);
state = AMF_UE_INITIATED_DE_REGISTERED;
amf_sbi_send_release_all_sessions(ran_ue, amf_ue, state);
memset(&param, 0, sizeof(param));
param.cause = OpenAPI_cause_REL_DUE_TO_UNSPECIFIED_REASON;
param.ngApCause.group = NGAP_Cause_PR_nas;
param.ngApCause.value = NGAP_CauseNas_deregister;
param.gmm_cause = OGS_5GMM_CAUSE_REQUEST_ACCEPTED;
param.ue_location = true;
param.ue_timezone = true;
amf_sbi_send_release_all_sessions(ran_ue, amf_ue, state, &param);
if (!AMF_SESSION_RELEASE_PENDING(amf_ue) &&
amf_sess_xact_count(amf_ue) == xact_count) {

View file

@ -86,6 +86,8 @@ void gmm_state_de_registered(ogs_fsm_t *s, amf_event_t *e)
int r, state = 0, xact_count;
amf_nsmf_pdusession_sm_context_param_t param;
ogs_assert(s);
ogs_assert(e);
@ -316,7 +318,13 @@ void gmm_state_de_registered(ogs_fsm_t *s, amf_event_t *e)
AMF_NETWORK_INITIATED_EXPLICIT_DE_REGISTERED) {
xact_count = amf_sess_xact_count(amf_ue);
amf_sbi_send_release_all_sessions(NULL, amf_ue, state);
memset(&param, 0, sizeof(param));
param.ue_location = true;
param.ue_timezone = true;
amf_sbi_send_release_all_sessions(
NULL, amf_ue, state, &param);
if (!AMF_SESSION_RELEASE_PENDING(amf_ue) &&
amf_sess_xact_count(amf_ue) == xact_count) {
@ -598,9 +606,14 @@ void gmm_state_de_registered(ogs_fsm_t *s, amf_event_t *e)
}
xact_count = amf_sess_xact_count(amf_ue);
memset(&param, 0, sizeof(param));
param.ue_location = true;
param.ue_timezone = true;
amf_sbi_send_release_all_sessions(
ran_ue_find_by_id(amf_ue->ran_ue_id), amf_ue,
AMF_RELEASE_SM_CONTEXT_NO_STATE);
AMF_RELEASE_SM_CONTEXT_NO_STATE, &param);
if (!AMF_SESSION_RELEASE_PENDING(amf_ue) &&
amf_sess_xact_count(amf_ue) == xact_count) {
@ -649,6 +662,8 @@ void gmm_state_registered(ogs_fsm_t *s, amf_event_t *e)
ogs_sbi_message_t *sbi_message = NULL;
amf_nsmf_pdusession_sm_context_param_t param;
ogs_assert(s);
ogs_assert(e);
@ -910,7 +925,13 @@ void gmm_state_registered(ogs_fsm_t *s, amf_event_t *e)
AMF_NETWORK_INITIATED_EXPLICIT_DE_REGISTERED) {
int xact_count = amf_sess_xact_count(amf_ue);
amf_sbi_send_release_all_sessions(NULL, amf_ue, state);
memset(&param, 0, sizeof(param));
param.ue_location = true;
param.ue_timezone = true;
amf_sbi_send_release_all_sessions(
NULL, amf_ue, state, &param);
if (!AMF_SESSION_RELEASE_PENDING(amf_ue) &&
amf_sess_xact_count(amf_ue) == xact_count) {
@ -1077,9 +1098,12 @@ void gmm_state_registered(ogs_fsm_t *s, amf_event_t *e)
ogs_assert(r != OGS_ERROR);
} else {
memset(&param, 0, sizeof(param));
param.ue_location = true;
param.ue_timezone = true;
amf_sbi_send_release_all_sessions(
NULL, amf_ue, state);
NULL, amf_ue, state, &param);
if (!AMF_SESSION_RELEASE_PENDING(amf_ue) &&
amf_sess_xact_count(amf_ue) == xact_count) {
@ -1231,9 +1255,14 @@ void gmm_state_registered(ogs_fsm_t *s, amf_event_t *e)
}
xact_count = amf_sess_xact_count(amf_ue);
memset(&param, 0, sizeof(param));
param.ue_location = true;
param.ue_timezone = true;
amf_sbi_send_release_all_sessions(
ran_ue_find_by_id(amf_ue->ran_ue_id), amf_ue,
AMF_RELEASE_SM_CONTEXT_NO_STATE);
AMF_RELEASE_SM_CONTEXT_NO_STATE, &param);
if (!AMF_SESSION_RELEASE_PENDING(amf_ue) &&
amf_sess_xact_count(amf_ue) == xact_count) {
@ -1285,6 +1314,8 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e,
ogs_nas_5gs_message_t *nas_message = NULL;
ogs_nas_security_header_type_t h;
amf_nsmf_pdusession_sm_context_param_t param;
ogs_assert(e);
sess = amf_sess_find_by_id(e->sess_id);
@ -1458,9 +1489,13 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e,
OGS_FSM_TRAN(s, &gmm_state_registered);
} else {
memset(&param, 0, sizeof(param));
param.ue_location = true;
param.ue_timezone = true;
amf_sbi_send_release_all_sessions(
ran_ue, amf_ue, AMF_RELEASE_SM_CONTEXT_NO_STATE);
ran_ue, amf_ue,
AMF_RELEASE_SM_CONTEXT_NO_STATE, &param);
if (!AMF_SESSION_RELEASE_PENDING(amf_ue) &&
amf_sess_xact_count(amf_ue) == xact_count) {
@ -1582,8 +1617,12 @@ static void common_register_state(ogs_fsm_t *s, amf_event_t *e,
break;
}
memset(&param, 0, sizeof(param));
param.ue_location = true;
param.ue_timezone = true;
amf_sbi_send_release_all_sessions(
ran_ue, amf_ue, AMF_RELEASE_SM_CONTEXT_NO_STATE);
ran_ue, amf_ue, AMF_RELEASE_SM_CONTEXT_NO_STATE, &param);
if (!AMF_SESSION_RELEASE_PENDING(amf_ue) &&
amf_sess_xact_count(amf_ue) == xact_count) {
@ -2429,7 +2468,8 @@ void gmm_state_initial_context_setup(ogs_fsm_t *s, amf_event_t *e)
ogs_sbi_message_t *sbi_message = NULL;
gmm_configuration_update_command_param_t param;
gmm_configuration_update_command_param_t gmm_param;
amf_nsmf_pdusession_sm_context_param_t nsmf_param;
ogs_assert(s);
ogs_assert(e);
@ -2710,9 +2750,9 @@ void gmm_state_initial_context_setup(ogs_fsm_t *s, amf_event_t *e)
* Indication if the AMF wants to update these NAS parameters
* without triggering a UE Registration procedure.
*/
memset(&param, 0, sizeof(param));
param.nitz = 1;
r = nas_5gs_send_configuration_update_command(amf_ue, &param);
memset(&gmm_param, 0, sizeof(gmm_param));
gmm_param.nitz = 1;
r = nas_5gs_send_configuration_update_command(amf_ue, &gmm_param);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
@ -2751,8 +2791,13 @@ void gmm_state_initial_context_setup(ogs_fsm_t *s, amf_event_t *e)
break;
}
memset(&nsmf_param, 0, sizeof(nsmf_param));
nsmf_param.ue_location = true;
nsmf_param.ue_timezone = true;
amf_sbi_send_release_all_sessions(
ran_ue, amf_ue, AMF_RELEASE_SM_CONTEXT_NO_STATE);
ran_ue, amf_ue,
AMF_RELEASE_SM_CONTEXT_NO_STATE, &nsmf_param);
if (!AMF_SESSION_RELEASE_PENDING(amf_ue) &&
amf_sess_xact_count(amf_ue) == xact_count) {
@ -2864,6 +2909,8 @@ void gmm_state_exception(ogs_fsm_t *s, amf_event_t *e)
ogs_nas_security_header_type_t h;
ogs_sbi_message_t *sbi_message = NULL;
amf_nsmf_pdusession_sm_context_param_t param;
ogs_assert(s);
ogs_assert(e);
@ -2919,9 +2966,13 @@ void gmm_state_exception(ogs_fsm_t *s, amf_event_t *e)
xact_count = amf_sess_xact_count(amf_ue);
memset(&param, 0, sizeof(param));
param.ue_location = true;
param.ue_timezone = true;
amf_sbi_send_release_all_sessions(
ran_ue_find_by_id(amf_ue->ran_ue_id), amf_ue,
AMF_RELEASE_SM_CONTEXT_NO_STATE);
AMF_RELEASE_SM_CONTEXT_NO_STATE, &param);
if (!AMF_SESSION_RELEASE_PENDING(amf_ue) &&
amf_sess_xact_count(amf_ue) == xact_count) {
@ -3032,9 +3083,13 @@ void gmm_state_exception(ogs_fsm_t *s, amf_event_t *e)
OGS_FSM_TRAN(s, &gmm_state_registered);
} else {
memset(&param, 0, sizeof(param));
param.ue_location = true;
param.ue_timezone = true;
amf_sbi_send_release_all_sessions(
ran_ue, amf_ue, AMF_RELEASE_SM_CONTEXT_NO_STATE);
ran_ue, amf_ue,
AMF_RELEASE_SM_CONTEXT_NO_STATE, &param);
if (!AMF_SESSION_RELEASE_PENDING(amf_ue) &&
amf_sess_xact_count(amf_ue) == xact_count) {
@ -3129,8 +3184,14 @@ void gmm_state_exception(ogs_fsm_t *s, amf_event_t *e)
/* Continue with release command */
xact_count = amf_sess_xact_count(amf_ue);
memset(&param, 0, sizeof(param));
param.ue_location = true;
param.ue_timezone = true;
amf_sbi_send_release_all_sessions(
ran_ue, amf_ue, AMF_RELEASE_SM_CONTEXT_NO_STATE);
ran_ue, amf_ue,
AMF_RELEASE_SM_CONTEXT_NO_STATE, &param);
if (!AMF_SESSION_RELEASE_PENDING(amf_ue) &&
amf_sess_xact_count(amf_ue) == xact_count) {

View file

@ -1885,7 +1885,15 @@ int amf_namf_comm_handle_registration_status_update_request(
uint8_t psi = *(double *)node->data;
sess = amf_sess_find_by_psi(amf_ue, psi);
if (SESSION_CONTEXT_IN_SMF(sess)) {
amf_sbi_send_release_session(ran_ue, sess, AMF_RELEASE_SM_CONTEXT_NO_STATE);
amf_nsmf_pdusession_sm_context_param_t param;
memset(&param, 0, sizeof(param));
param.ue_location = true;
param.ue_timezone = true;
amf_sbi_send_release_session(
ran_ue, sess,
AMF_RELEASE_SM_CONTEXT_NO_STATE, &param);
} else {
ogs_error("[%s] No Session Context PSI[%d]",
amf_ue->supi, psi);

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2019-2024 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@ -256,18 +256,6 @@ 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;
if (param && param->nrf_uri) {
message.http.custom.nrf_uri =
ogs_msprintf("%s: \"%s\"",
@ -460,6 +448,8 @@ ogs_sbi_request_t *amf_nsmf_pdusession_build_release_sm_context(
OpenAPI_ng_ap_cause_t ngApCause;
OpenAPI_user_location_t ueLocation;
ogs_assert(param);
ogs_assert(sess);
ogs_assert(sess->sm_context_resource_uri);
@ -471,20 +461,18 @@ ogs_sbi_request_t *amf_nsmf_pdusession_build_release_sm_context(
memset(&SmContextReleaseData, 0, sizeof(SmContextReleaseData));
if (param) {
SmContextReleaseData.cause = param->cause;
SmContextReleaseData.cause = param->cause;
if (param->ngApCause.group) {
SmContextReleaseData.ng_ap_cause = &ngApCause;
memset(&ngApCause, 0, sizeof(ngApCause));
ngApCause.group = param->ngApCause.group;
ngApCause.value = param->ngApCause.value;
}
if (param->ngApCause.group) {
SmContextReleaseData.ng_ap_cause = &ngApCause;
memset(&ngApCause, 0, sizeof(ngApCause));
ngApCause.group = param->ngApCause.group;
ngApCause.value = param->ngApCause.value;
}
if (param->gmm_cause) {
SmContextReleaseData._5g_mm_cause_value = param->gmm_cause;
SmContextReleaseData.is__5g_mm_cause_value = true;
}
if (param->gmm_cause) {
SmContextReleaseData._5g_mm_cause_value = param->gmm_cause;
SmContextReleaseData.is__5g_mm_cause_value = true;
}
memset(&ueLocation, 0, sizeof(ueLocation));
@ -495,24 +483,30 @@ ogs_sbi_request_t *amf_nsmf_pdusession_build_release_sm_context(
goto end;
}
ueLocation.nr_location = ogs_sbi_build_nr_location(
&amf_ue->nr_tai, &amf_ue->nr_cgi);
if (!ueLocation.nr_location) {
ogs_error("No ueLocation.nr_location");
goto end;
}
ueLocation.nr_location->ue_location_timestamp =
ogs_sbi_gmtime_string(amf_ue->ue_location_timestamp);
if (!ueLocation.nr_location->ue_location_timestamp) {
ogs_error("No ueLocation.nr_location->ue_location_timestamp");
goto end;
if (param->ue_location) {
ueLocation.nr_location = ogs_sbi_build_nr_location(
&amf_ue->nr_tai, &amf_ue->nr_cgi);
if (!ueLocation.nr_location) {
ogs_error("No ueLocation.nr_location");
goto end;
}
ueLocation.nr_location->ue_location_timestamp =
ogs_sbi_gmtime_string(amf_ue->ue_location_timestamp);
if (!ueLocation.nr_location->ue_location_timestamp) {
ogs_error("No ueLocation.nr_location->ue_location_timestamp");
goto end;
}
SmContextReleaseData.ue_location = &ueLocation;
}
SmContextReleaseData.ue_location = &ueLocation;
SmContextReleaseData.ue_time_zone = ogs_sbi_timezone_string(ogs_timezone());
if (!SmContextReleaseData.ue_time_zone) {
ogs_error("No SmContextReleaseData.ue_time_zone");
goto end;
if (param->ue_timezone) {
SmContextReleaseData.ue_time_zone =
ogs_sbi_timezone_string(ogs_timezone());
if (!SmContextReleaseData.ue_time_zone) {
ogs_error("No SmContextReleaseData.ue_time_zone");
goto end;
}
}
message.SmContextReleaseData = &SmContextReleaseData;

View file

@ -1134,7 +1134,7 @@ int amf_nsmf_pdusession_handle_release_sm_context(amf_sess_t *sess, int state)
if (sess->old_gsm_type == OGS_NAS_5GS_PDU_SESSION_RELEASE_COMPLETE &&
sess->current_gsm_type ==
OGS_NAS_5GS_PDU_SESSION_ESTABLISHMENT_REQUEST) {
ogs_error("[%s:%d] Do not remove Session due to Reactivation-requested",
ogs_warn("[%s:%d] Session retained: reactivation has been requested",
amf_ue->supi, sess->psi);
/*

View file

@ -604,7 +604,7 @@ void amf_sbi_send_deactivate_all_ue_in_gnb(amf_gnb_t *gnb, int state)
}
void amf_sbi_send_release_session(
ran_ue_t *ran_ue, amf_sess_t *sess, int state)
ran_ue_t *ran_ue, amf_sess_t *sess, int state, void *data)
{
int r;
@ -613,7 +613,7 @@ void amf_sbi_send_release_session(
r = amf_sess_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NSMF_PDUSESSION, NULL,
amf_nsmf_pdusession_build_release_sm_context,
ran_ue, sess, state, NULL);
ran_ue, sess, state, data);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
@ -622,7 +622,7 @@ void amf_sbi_send_release_session(
}
void amf_sbi_send_release_all_sessions(
ran_ue_t *ran_ue, amf_ue_t *amf_ue, int state)
ran_ue_t *ran_ue, amf_ue_t *amf_ue, int state, void *data)
{
amf_sess_t *sess = NULL;
@ -630,7 +630,7 @@ void amf_sbi_send_release_all_sessions(
ogs_list_for_each(&amf_ue->sess_list, sess) {
if (SESSION_CONTEXT_IN_SMF(sess))
amf_sbi_send_release_session(ran_ue, sess, state);
amf_sbi_send_release_session(ran_ue, sess, state, data);
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2019-2024 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@ -98,9 +98,9 @@ void amf_sbi_send_deactivate_all_sessions(
void amf_sbi_send_deactivate_all_ue_in_gnb(amf_gnb_t *gnb, int state);
void amf_sbi_send_release_session(
ran_ue_t *ran_ue, amf_sess_t *sess, int state);
ran_ue_t *ran_ue, amf_sess_t *sess, int state, void *data);
void amf_sbi_send_release_all_sessions(
ran_ue_t *ran_ue, amf_ue_t *amf_ue, int state);
ran_ue_t *ran_ue, amf_ue_t *amf_ue, int state, void *data);
bool amf_sbi_send_n1_n2_failure_notify(
amf_sess_t *sess, OpenAPI_n1_n2_message_transfer_cause_e cause);

View file

@ -381,6 +381,9 @@ void nssf_home_remove(nssf_home_t *home)
if (home->nsi_id)
ogs_free(home->nsi_id);
/* Free SBI object memory */
ogs_sbi_object_free(&home->sbi);
ogs_pool_id_free(&nssf_home_pool, home);
}

View file

@ -1859,6 +1859,9 @@ void smf_sess_remove(smf_sess_t *sess)
if (sess->v_smf.client)
ogs_sbi_client_remove(sess->v_smf.client);
if (sess->n1smbuf)
ogs_pkbuf_free(sess->n1smbuf);
OGS_NAS_CLEAR_DATA(&sess->h_smf_authorized_qos_rules);
OGS_NAS_CLEAR_DATA(&sess->h_smf_authorized_qos_flow_descriptions);
OGS_NAS_CLEAR_DATA(&sess->h_smf_extended_protocol_configuration_options);

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2019-2024 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@ -64,6 +64,61 @@ typedef struct smf_ctf_config_s {
int smf_ctf_config_init(smf_ctf_config_t *ctf_config);
typedef struct smf_nsmf_pdusession_param_s {
OpenAPI_request_indication_e request_indication;
OpenAPI_cause_e cause;
struct {
int group;
int value;
} ngap_cause;
int gmm_cause;
int gsm_cause;
struct {
ED3(uint8_t ue_location:1;,
uint8_t ue_timezone:1;,
uint8_t spare:6;)
};
} smf_nsmf_pdusession_param_t;
/* HR flag bit */
#define SMF_UECM_FLAG_HR (1 << 7)
/* Base states (low bits only) */
#define SMF_UECM_STATE_NONE 0
#define SMF_UECM_STATE_REGISTERED 1
#define SMF_UECM_STATE_DEREG_BY_AMF 2
#define SMF_UECM_STATE_DEREG_BY_N1N2 3
/* HR variants (OR base state with HR flag) */
#define SMF_UECM_STATE_REGISTERED_HR \
(SMF_UECM_STATE_REGISTERED | SMF_UECM_FLAG_HR)
#define SMF_UECM_STATE_DEREG_BY_AMF_HR \
(SMF_UECM_STATE_DEREG_BY_AMF | SMF_UECM_FLAG_HR)
#define SMF_UECM_STATE_DEREG_BY_N1N2_HR \
(SMF_UECM_STATE_DEREG_BY_N1N2 | SMF_UECM_FLAG_HR)
/**
* Return true if the PDU session anchor SMF is in the HPLMN
* (Home-Routed Roaming, HR)
*/
static inline bool smf_uecm_anchor_in_hplmn(int state)
{
return !!(state & SMF_UECM_FLAG_HR);
}
/**
* Return true if the PDU session anchor SMF is in the VPLMN
* (Non-Roaming or Local Break-Out Roaming, LBO)
*/
static inline bool smf_uecm_anchor_in_vplmn(int state)
{
return !(state & SMF_UECM_FLAG_HR);
}
typedef struct smf_context_s {
smf_ctf_config_t ctf_config;
const char* diam_conf_path; /* SMF Diameter conf path */
@ -531,12 +586,6 @@ typedef struct smf_sess_s {
int pdu_session_resource_release;
} ngap_state;
#define SMF_UECM_STATE_NONE 0
#define SMF_UECM_STATE_REGISTERED 1
#define SMF_UECM_STATE_REGISTERED_BY_HOME_ROUTED_ROAMING 2
#define SMF_UECM_STATE_DEREGISTERED_BY_AMF 3
#define SMF_UECM_STATE_DEREGISTERED_BY_N1_N2_RELEASE 4
/* Handover */
struct {
bool prepared;
@ -584,6 +633,11 @@ typedef struct smf_sess_s {
bool n1_released;
bool n2_released;
ogs_pool_id_t amf_update_request_stream_id;
ogs_pool_id_t n1_n2_released_stream_id;
smf_nsmf_pdusession_param_t nsmf_param;
} smf_sess_t;
void smf_context_init(void);

View file

@ -77,6 +77,9 @@ const char *smf_event_get_name(smf_event_t *e)
case SMF_EVT_5GSM_TIMER:
return "SMF_EVT_5GSM_TIMER";
case SMF_EVT_SESSION_RELEASE:
return "SMF_EVT_SESSION_RELEASE";
default:
break;
}

View file

@ -62,6 +62,8 @@ typedef enum {
SMF_EVT_5GSM_MESSAGE,
SMF_EVT_5GSM_TIMER,
SMF_EVT_SESSION_RELEASE,
SMF_EVT_TOP,
} smf_event_e;
@ -99,6 +101,10 @@ typedef struct smf_event_s {
ogs_nas_5gs_message_t *message;
} nas;
struct {
int trigger;
} release;
ogs_pool_id_t sess_id;
} smf_event_t;

File diff suppressed because it is too large Load diff

View file

@ -1608,3 +1608,20 @@ cleanup:
return pkbuf;
}
ogs_pkbuf_t *gsmue_build_pdu_session_release_command(smf_sess_t *sess)
{
ogs_pkbuf_t *pkbuf = NULL;
ogs_nas_5gs_message_t message;
ogs_assert(sess);
memset(&message, 0, sizeof(message));
message.gsm.h.message_type = OGS_NAS_5GS_PDU_SESSION_RELEASE_COMMAND;
pkbuf = gsmue_encode_n1_sm_info(&message);
ogs_assert(pkbuf);
return pkbuf;
}

View file

@ -30,6 +30,7 @@ ogs_pkbuf_t *gsmue_encode_n1_sm_info(ogs_nas_5gs_message_t *message);
int gsmue_decode_n1_sm_info(ogs_nas_5gs_message_t *message, ogs_pkbuf_t *pkbuf);
ogs_pkbuf_t *gsmue_build_pdu_session_establishment_accept(smf_sess_t *sess);
ogs_pkbuf_t *gsmue_build_pdu_session_release_command(smf_sess_t *sess);
#ifdef __cplusplus
}

56
src/smf/local-path.c Normal file
View file

@ -0,0 +1,56 @@
/*
* Copyright (C) 2019,2020 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "local-path.h"
void smf_trigger_session_release(smf_sess_t *sess,
ogs_sbi_stream_t *stream, int pfcp_trigger)
{
int rv;
smf_event_t *e;
ogs_pool_id_t sid;
/* Ensure session is valid */
ogs_assert(sess);
/* Create a session release event */
e = smf_event_new(SMF_EVT_SESSION_RELEASE);
ogs_assert(e);
/* Assign session ID */
e->sess_id = sess->id;
/* Store the PFCP trigger in the SBI state field */
e->h.sbi.state = pfcp_trigger;
/* Attach SBI stream ID if provided */
if (stream) {
sid = ogs_sbi_id_from_stream(stream);
ogs_assert(sid >= OGS_MIN_POOL_ID &&
sid <= OGS_MAX_POOL_ID);
e->h.sbi.data = OGS_UINT_TO_POINTER(sid);
}
/* Push event to the application queue */
rv = ogs_queue_push(ogs_app()->queue, e);
if (rv != OGS_OK) {
ogs_error("ogs_queue_push failed:%d", rv);
ogs_event_free(e);
}
}

36
src/smf/local-path.h Normal file
View file

@ -0,0 +1,36 @@
/*
* Copyright (C) 2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef SMF_LOCAL_PATH_H
#define SMF_LOCAL_PATH_H
#include "context.h"
#ifdef __cplusplus
extern "C" {
#endif
void smf_trigger_session_release(smf_sess_t *sess,
ogs_sbi_stream_t *stream, int pfcp_trigger);
#ifdef __cplusplus
}
#endif
#endif /* SMF_LOCAL_PATH_H */

View file

@ -68,6 +68,7 @@ libsmf_sources = files('''
ngap-build.h
ngap-handler.h
ngap-path.h
local-path.h
metrics.h
init.c
@ -109,6 +110,7 @@ libsmf_sources = files('''
ngap-build.c
ngap-handler.c
ngap-path.c
local-path.c
metrics.c
'''.split())

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2019-2023 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@ -259,6 +259,7 @@ void smf_5gc_n4_handle_session_modification_response(
{
int status = 0;
uint64_t flags = 0;
int trigger = 0;
ogs_sbi_stream_t *stream = NULL;
smf_bearer_t *qos_flow = NULL;
@ -271,6 +272,7 @@ void smf_5gc_n4_handle_session_modification_response(
flags = xact->modify_flags;
ogs_assert(flags);
trigger = xact->delete_trigger;
/* 'stream' could be NULL in smf_qos_flow_binding() */
if (xact->assoc_stream_id >= OGS_MIN_POOL_ID &&
@ -420,7 +422,7 @@ void smf_5gc_n4_handle_session_modification_response(
smf_5gc_pfcp_send_all_pdr_modification_request(
sess, stream,
OGS_PFCP_MODIFY_INDIRECT|OGS_PFCP_MODIFY_REMOVE,
ogs_local_conf()->time.handover.duration));
0, ogs_local_conf()->time.handover.duration));
}
smf_sbi_send_sm_context_updated_data_ho_state(
@ -448,7 +450,7 @@ void smf_5gc_n4_handle_session_modification_response(
sess);
ogs_assert(param.n2smbuf);
smf_namf_comm_send_n1_n2_message_transfer(sess, &param);
smf_namf_comm_send_n1_n2_message_transfer(sess, NULL, &param);
} else {
ogs_fatal("Invalid flags [0x%llx]", (long long)flags);
ogs_assert_if_reached();
@ -482,7 +484,39 @@ void smf_5gc_n4_handle_session_modification_response(
param.skip_ind = true;
smf_namf_comm_send_n1_n2_message_transfer(sess, &param);
smf_namf_comm_send_n1_n2_message_transfer(sess, NULL, &param);
} else if (flags & OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING) {
int r;
ogs_assert(trigger);
if (trigger == OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED) {
r = smf_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NSMF_PDUSESSION, NULL,
smf_nsmf_pdusession_build_hsmf_update_data,
sess, stream, trigger, NULL);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else if (trigger ==
OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT) {
r = smf_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NSMF_PDUSESSION, NULL,
smf_nsmf_pdusession_build_hsmf_update_data,
sess, stream, trigger, NULL);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else if (trigger ==
OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT) {
r = smf_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NSMF_PDUSESSION, NULL,
smf_nsmf_pdusession_build_release_data,
sess, stream, trigger, NULL);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else {
ogs_fatal("Invalid delete trigger[%d]", trigger);
ogs_assert_if_reached();
}
} else {
smf_sbi_send_sm_context_updated_data_up_cnx_state(
sess, stream, OpenAPI_up_cnx_state_DEACTIVATED);
@ -511,7 +545,7 @@ void smf_5gc_n4_handle_session_modification_response(
smf_5gc_pfcp_send_all_pdr_modification_request(
sess, stream,
OGS_PFCP_MODIFY_INDIRECT|OGS_PFCP_MODIFY_CREATE,
0));
0, 0));
} else if (flags & OGS_PFCP_MODIFY_HANDOVER_CANCEL) {
smf_sbi_send_sm_context_updated_data_ho_state(
sess, stream, OpenAPI_ho_state_CANCELLED);
@ -548,7 +582,7 @@ void smf_5gc_n4_handle_session_modification_response(
NGAP_Cause_PR_nas, NGAP_CauseNas_normal_release);
ogs_assert(param.n2smbuf);
smf_namf_comm_send_n1_n2_message_transfer(sess, &param);
smf_namf_comm_send_n1_n2_message_transfer(sess, NULL, &param);
ogs_list_for_each_entry_safe(&sess->qos_flow_to_modify_list,
next, qos_flow, to_modify_node) {
@ -639,7 +673,7 @@ void smf_5gc_n4_handle_session_modification_response(
sess, true);
ogs_assert(param.n2smbuf);
smf_namf_comm_send_n1_n2_message_transfer(sess, &param);
smf_namf_comm_send_n1_n2_message_transfer(sess, NULL, &param);
} else {
ogs_fatal("Unknown flags [0x%llx]", (long long)flags);
@ -699,7 +733,7 @@ void smf_5gc_n4_handle_session_modification_response(
(flags & OGS_PFCP_MODIFY_QOS_MODIFY) ? true : false);
ogs_assert(param.n2smbuf);
smf_namf_comm_send_n1_n2_message_transfer(sess, &param);
smf_namf_comm_send_n1_n2_message_transfer(sess, NULL, &param);
} else if (flags & OGS_PFCP_MODIFY_UE_REQUESTED) {
ogs_pkbuf_t *n1smbuf = NULL, *n2smbuf = NULL;
@ -1359,7 +1393,7 @@ uint8_t smf_n4_handle_session_report_request(
param.n1n2_failure_txf_notif_uri = true;
smf_namf_comm_send_n1_n2_message_transfer(sess, &param);
smf_namf_comm_send_n1_n2_message_transfer(sess, NULL, &param);
break;
case OpenAPI_up_cnx_state_SUSPENDED:
ogs_error("[%s:%s] PDU Session had been SUSPENDED",
@ -1457,7 +1491,7 @@ uint8_t smf_n4_handle_session_report_request(
sess, NULL,
OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE|
OGS_PFCP_MODIFY_ERROR_INDICATION,
0));
0, 0));
}
}
return cause_value;

View file

@ -105,7 +105,7 @@ ogs_sbi_request_t *smf_namf_comm_build_n1_n2_message_transfer(
case SMF_NETWORK_REQUESTED_QOS_FLOW_MODIFICATION:
n2InfoContent.ngap_ie_type = OpenAPI_ngap_ie_type_PDU_RES_MOD_REQ;
break;
case SMF_NETWORK_REQUESTED_PDU_SESSION_RELEASE:
case SMF_UE_OR_NETWORK_REQUESTED_PDU_SESSION_RELEASE:
case SMF_ERROR_INDICATON_RECEIVED_FROM_5G_AN:
n2InfoContent.ngap_ie_type = OpenAPI_ngap_ie_type_PDU_RES_REL_CMD;
break;

View file

@ -31,7 +31,7 @@ typedef struct smf_n1_n2_message_transfer_param_s {
#define SMF_UE_REQUESTED_PDU_SESSION_ESTABLISHMENT 1
#define SMF_NETWORK_REQUESTED_PDU_SESSION_MODIFICATION 2
#define SMF_NETWORK_REQUESTED_QOS_FLOW_MODIFICATION 3
#define SMF_NETWORK_REQUESTED_PDU_SESSION_RELEASE 4
#define SMF_UE_OR_NETWORK_REQUESTED_PDU_SESSION_RELEASE 4
#define SMF_NETWORK_TRIGGERED_SERVICE_REQUEST 5
#define SMF_ERROR_INDICATON_RECEIVED_FROM_5G_AN 6
int state;

View file

@ -23,7 +23,8 @@
#include "namf-handler.h"
bool smf_namf_comm_handle_n1_n2_message_transfer(
smf_sess_t *sess, int state, ogs_sbi_message_t *recvmsg)
smf_sess_t *sess, ogs_sbi_stream_t *stream,
int state, ogs_sbi_message_t *recvmsg)
{
smf_ue_t *smf_ue = NULL;
OpenAPI_n1_n2_message_transfer_rsp_data_t *N1N2MessageTransferRspData;
@ -104,7 +105,7 @@ bool smf_namf_comm_handle_n1_n2_message_transfer(
}
break;
case SMF_NETWORK_REQUESTED_PDU_SESSION_RELEASE:
case SMF_UE_OR_NETWORK_REQUESTED_PDU_SESSION_RELEASE:
case SMF_ERROR_INDICATON_RECEIVED_FROM_5G_AN:
N1N2MessageTransferRspData = recvmsg->N1N2MessageTransferRspData;
@ -158,10 +159,12 @@ bool smf_namf_comm_handle_n1_n2_message_transfer(
param.n1n2_failure_txf_notif_uri = true;
smf_namf_comm_send_n1_n2_message_transfer(sess, &param);
smf_namf_comm_send_n1_n2_message_transfer(sess, NULL, &param);
} else if (N1N2MessageTransferRspData->cause ==
OpenAPI_n1_n2_message_transfer_cause_N1_N2_TRANSFER_INITIATED) {
/* Nothing */
if (stream)
sess->n1_n2_released_stream_id =
ogs_sbi_id_from_stream(stream);
} else {
ogs_error("Not implemented [cause:%d]",
N1N2MessageTransferRspData->cause);

View file

@ -27,7 +27,8 @@ extern "C" {
#include "context.h"
bool smf_namf_comm_handle_n1_n2_message_transfer(
smf_sess_t *sess, int state, ogs_sbi_message_t *recvmsg);
smf_sess_t *sess, ogs_sbi_stream_t *stream,
int state, ogs_sbi_message_t *recvmsg);
bool smf_namf_comm_handle_n1_n2_message_transfer_failure_notify(
ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg);

View file

@ -154,7 +154,7 @@ int ngap_handle_pdu_session_resource_setup_response_transfer(
OGS_PFCP_MODIFY_DL_ONLY|
OGS_PFCP_MODIFY_OUTER_HEADER_REMOVAL|
OGS_PFCP_MODIFY_ACTIVATE) :
(OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_ACTIVATE), 0));
(OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_ACTIVATE), 0, 0));
} else {
#if 0 /* Modified by pull request #1729 */
/* ACTIVATED Is NOT Included in RESPONSE */
@ -264,7 +264,7 @@ int ngap_handle_pdu_session_resource_setup_unsuccessful_transfer(
ogs_assert(OGS_OK ==
smf_5gc_pfcp_send_all_pdr_modification_request(
sess, stream,
OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE, 0));
OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE, 0, 0));
rv = OGS_OK;
cleanup:
@ -492,7 +492,7 @@ int ngap_handle_path_switch_request_transfer(
sess, stream,
OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_ACTIVATE|
OGS_PFCP_MODIFY_XN_HANDOVER|OGS_PFCP_MODIFY_END_MARKER,
0));
0, 0));
} else {
/* ACTIVATED Is NOT Included in RESPONSE */
ogs_assert(true == ogs_sbi_send_http_status_no_content(stream));
@ -712,7 +712,7 @@ int ngap_handle_handover_request_ack(
* ...
*/
OGS_PFCP_MODIFY_REMOVE|OGS_PFCP_MODIFY_CREATE,
0));
0, 0));
} else {
smf_sess_create_indirect_data_forwarding(sess);
@ -721,7 +721,7 @@ int ngap_handle_handover_request_ack(
smf_5gc_pfcp_send_all_pdr_modification_request(
sess, stream,
OGS_PFCP_MODIFY_INDIRECT|OGS_PFCP_MODIFY_CREATE,
0));
0, 0));
}
} else {
ogs_pkbuf_t *n2smbuf = ngap_build_handover_command_transfer(sess);

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2019,2020 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@ -326,8 +326,6 @@ end:
ogs_sbi_request_t *smf_npcf_smpolicycontrol_build_delete(
smf_sess_t *sess, void *data)
{
smf_npcf_smpolicycontrol_param_t *param = data;
smf_ue_t *smf_ue = NULL;
ogs_sbi_message_t message;
@ -355,70 +353,72 @@ ogs_sbi_request_t *smf_npcf_smpolicycontrol_build_delete(
memset(&ueLocation, 0, sizeof(ueLocation));
if (param) {
if (param->ran_nas_release.gmm_cause ||
param->ran_nas_release.gsm_cause ||
param->ran_nas_release.ngap_cause.group) {
if (sess->nsmf_param.gmm_cause ||
sess->nsmf_param.gsm_cause ||
sess->nsmf_param.ngap_cause.group) {
ranNasRelCauseList = OpenAPI_list_create();
if (!ranNasRelCauseList) {
ogs_error("No ranNasRelCauseList");
ranNasRelCauseList = OpenAPI_list_create();
if (!ranNasRelCauseList) {
ogs_error("No ranNasRelCauseList");
goto end;
}
ranNasRelCause = ogs_calloc(1, sizeof(*ranNasRelCause));
if (!ranNasRelCause) {
ogs_error("No ranNasRelCause");
goto end;
}
if (sess->nsmf_param.ngap_cause.group) {
OpenAPI_ng_ap_cause_t *ngApCause = NULL;
ranNasRelCause->ng_ap_cause = ngApCause =
ogs_calloc(1, sizeof(*ngApCause));
if (!ranNasRelCause->ng_ap_cause) {
ogs_error("No ranNasRelCause->ng_ap_cause");
if (ranNasRelCause)
ogs_free(ranNasRelCause);
goto end;
}
ranNasRelCause = ogs_calloc(1, sizeof(*ranNasRelCause));
if (!ranNasRelCause) {
ogs_error("No ranNasRelCause");
goto end;
}
if (param->ran_nas_release.ngap_cause.group) {
OpenAPI_ng_ap_cause_t *ngApCause = NULL;
ranNasRelCause->ng_ap_cause = ngApCause =
ogs_calloc(1, sizeof(*ngApCause));
if (!ranNasRelCause->ng_ap_cause) {
ogs_error("No ranNasRelCause->ng_ap_cause");
if (ranNasRelCause)
ogs_free(ranNasRelCause);
goto end;
}
ngApCause->group = param->ran_nas_release.ngap_cause.group;
ngApCause->value = param->ran_nas_release.ngap_cause.value;
}
ngApCause->group = sess->nsmf_param.ngap_cause.group;
ngApCause->value = sess->nsmf_param.ngap_cause.value;
}
if (sess->nsmf_param.gmm_cause) {
ranNasRelCause->is__5g_mm_cause = true;
ranNasRelCause->_5g_mm_cause = param->ran_nas_release.gmm_cause;
ranNasRelCause->_5g_mm_cause = sess->nsmf_param.gmm_cause;
}
if (sess->nsmf_param.gsm_cause) {
ranNasRelCause->is__5g_sm_cause = true;
ranNasRelCause->_5g_sm_cause = param->ran_nas_release.gsm_cause;
OpenAPI_list_add(ranNasRelCauseList, ranNasRelCause);
ranNasRelCause->_5g_sm_cause = sess->nsmf_param.gsm_cause;
}
if (param->ue_location) {
ueLocation.nr_location = ogs_sbi_build_nr_location(
&sess->nr_tai, &sess->nr_cgi);
if (!ueLocation.nr_location) {
ogs_error("ueLocation.nr_location");
goto end;
}
ueLocation.nr_location->ue_location_timestamp =
ogs_sbi_gmtime_string(sess->ue_location_timestamp);
if (!ueLocation.nr_location->ue_location_timestamp) {
ogs_error("ueLocation.nr_location->ue_location_timestamp");
goto end;
}
OpenAPI_list_add(ranNasRelCauseList, ranNasRelCause);
}
SmPolicyDeleteData.user_location_info = &ueLocation;
if (sess->nsmf_param.ue_location) {
ueLocation.nr_location = ogs_sbi_build_nr_location(
&sess->nr_tai, &sess->nr_cgi);
if (!ueLocation.nr_location) {
ogs_error("ueLocation.nr_location");
goto end;
}
if (param->ue_timezone) {
SmPolicyDeleteData.ue_time_zone =
ogs_sbi_timezone_string(ogs_timezone());
if (!SmPolicyDeleteData.ue_time_zone) {
ogs_error("SmPolicyDeleteData.ue_time_zone");
goto end;
}
ueLocation.nr_location->ue_location_timestamp =
ogs_sbi_gmtime_string(sess->ue_location_timestamp);
if (!ueLocation.nr_location->ue_location_timestamp) {
ogs_error("ueLocation.nr_location->ue_location_timestamp");
goto end;
}
SmPolicyDeleteData.user_location_info = &ueLocation;
}
if (sess->nsmf_param.ue_timezone) {
SmPolicyDeleteData.ue_time_zone =
ogs_sbi_timezone_string(ogs_timezone());
if (!SmPolicyDeleteData.ue_time_zone) {
ogs_error("SmPolicyDeleteData.ue_time_zone");
goto end;
}
}

View file

@ -26,27 +26,6 @@
extern "C" {
#endif
typedef struct smf_npcf_smpolicycontrol_param_s {
struct {
struct {
int group;
int value;
} ngap_cause;
int gmm_cause;
int gsm_cause;
} ran_nas_release;
OpenAPI_pdu_session_rel_cause_e pdu_sess_rel_cause;
union {
struct {
ED3(uint8_t ue_location:1;,
uint8_t ue_timezone:1;,
uint8_t spare:6;)
};
uint8_t indications;
};
} smf_npcf_smpolicycontrol_param_t;
ogs_sbi_request_t *smf_npcf_smpolicycontrol_build_create(
smf_sess_t *sess, void *data);
ogs_sbi_request_t *smf_npcf_smpolicycontrol_build_delete(

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2019-2024 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@ -20,6 +20,7 @@
#include "sbi-path.h"
#include "pfcp-path.h"
#include "nas-path.h"
#include "local-path.h"
#include "binding.h"
#include "npcf-handler.h"
@ -760,8 +761,6 @@ bool smf_npcf_smpolicycontrol_handle_terminate_notify(
smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg)
{
smf_ue_t *smf_ue = NULL;
smf_npcf_smpolicycontrol_param_t param;
int r;
ogs_assert(sess);
ogs_assert(stream);
@ -772,19 +771,8 @@ bool smf_npcf_smpolicycontrol_handle_terminate_notify(
ogs_assert(true == ogs_sbi_send_http_status_no_content(stream));
if (PCF_SM_POLICY_ASSOCIATED(sess)) {
memset(&param, 0, sizeof(param));
r = smf_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NPCF_SMPOLICYCONTROL, NULL,
smf_npcf_smpolicycontrol_build_delete,
sess, NULL, OGS_PFCP_DELETE_TRIGGER_PCF_INITIATED, &param);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else {
ogs_error("[%s:%d] No PolicyAssociationId. Forcibly remove SESSION",
smf_ue->supi, sess->psi);
SMF_SESS_CLEAR(sess);
}
smf_trigger_session_release(
sess, NULL, OGS_PFCP_DELETE_TRIGGER_PCF_INITIATED);
return true;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2024 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2024-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@ -19,7 +19,7 @@
#include "nsmf-build.h"
ogs_sbi_request_t *smf_nsmf_pdusession_build_create_pdu_session(
ogs_sbi_request_t *smf_nsmf_pdusession_build_create_data(
smf_sess_t *sess, void *data)
{
ogs_sbi_message_t message;
@ -131,14 +131,13 @@ ogs_sbi_request_t *smf_nsmf_pdusession_build_create_pdu_session(
OpenAPI_request_type_EXISTING_EMERGENCY_PDU_SESSION)
PduSessionCreateData.request_type = sess->request_type;
header.service.name = (char *)OGS_SBI_SERVICE_NAME_NSMF_CALLBACK;
header.service.name = (char *)OGS_SBI_SERVICE_NAME_NSMF_PDUSESSION;
header.api.version = (char *)OGS_SBI_API_V1;
header.resource.component[0] = smf_ue->supi;
header.resource.component[1] =
(char *)OGS_SBI_RESOURCE_NAME_PDU_SESSION_STATUS;
header.resource.component[2] = ogs_msprintf("%d", sess->psi);
if (!header.resource.component[2]) {
ogs_error("No header.resource.component[2]");
header.resource.component[0] =
(char *)OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS;
header.resource.component[1] = sess->sm_context_ref;
if (!header.resource.component[1]) {
ogs_error("No header.resource.component[1]");
goto end;
}
@ -287,17 +286,6 @@ ogs_sbi_request_t *smf_nsmf_pdusession_build_create_pdu_session(
message.http.accept = (char *)(OGS_SBI_CONTENT_JSON_TYPE ","
OGS_SBI_CONTENT_NGAP_TYPE "," OGS_SBI_CONTENT_PROBLEM_TYPE);
message.http.custom.callback =
(char *)OGS_SBI_CALLBACK_NSMF_PDUSESSION_STATUS_NOTIFY;
#if 0 /* Needs to be checked against AMF's nsmf-build.c */
if (param && param->nrf_uri) {
message.http.custom.nrf_uri =
ogs_msprintf("%s: \"%s\"",
OGS_SBI_SERVICE_NAME_NNRF_DISC, param->nrf_uri);
}
#endif
request = ogs_sbi_build_request(&message);
ogs_expect(request);
@ -335,6 +323,11 @@ end:
if (PduSessionCreateData.ue_time_zone)
ogs_free(PduSessionCreateData.ue_time_zone);
if (sess->n1smbuf) {
ogs_pkbuf_free(sess->n1smbuf);
sess->n1smbuf = NULL;
}
if (n1SmBufFromUe)
ogs_pkbuf_free(n1SmBufFromUe);
@ -343,3 +336,291 @@ end:
return request;
}
ogs_sbi_request_t *smf_nsmf_pdusession_build_hsmf_update_data(
smf_sess_t *sess, void *data)
{
ogs_sbi_message_t message;
ogs_sbi_request_t *request = NULL;
smf_ue_t *smf_ue = NULL;
OpenAPI_hsmf_update_data_t HsmfUpdateData;
OpenAPI_ng_ap_cause_t ngApCause;
OpenAPI_user_location_t ueLocation;
int rv;
ogs_nas_5gs_message_t nas_message;
ogs_pkbuf_t *n1SmBufFromUe = NULL;
OpenAPI_ref_to_binary_data_t n1SmInfoFromUe;
ogs_assert(sess);
smf_ue = smf_ue_find_by_id(sess->smf_ue_id);
ogs_assert(smf_ue);
memset(&message, 0, sizeof(message));
message.h.method = (char *)OGS_SBI_HTTP_METHOD_POST;
ogs_assert(sess->pdu_session_resource_uri);
message.h.uri = ogs_msprintf("%s/%s",
sess->pdu_session_resource_uri, OGS_SBI_RESOURCE_NAME_MODIFY);
ogs_assert(message.h.uri);
memset(&HsmfUpdateData, 0, sizeof(HsmfUpdateData));
memset(&ngApCause, 0, sizeof(ngApCause));
memset(&ueLocation, 0, sizeof(ueLocation));
HsmfUpdateData.request_indication = sess->nsmf_param.request_indication;
ogs_assert(HsmfUpdateData.request_indication);
HsmfUpdateData.cause = sess->nsmf_param.cause;
if (sess->nsmf_param.ngap_cause.group) {
HsmfUpdateData.ng_ap_cause = &ngApCause;
ngApCause.group = sess->nsmf_param.ngap_cause.group;
ngApCause.value = sess->nsmf_param.ngap_cause.value;
}
if (sess->nsmf_param.gmm_cause) {
HsmfUpdateData.is__5g_mm_cause_value = true;
HsmfUpdateData._5g_mm_cause_value = sess->nsmf_param.gmm_cause;
}
if (sess->nsmf_param.ue_location) {
ueLocation.nr_location = ogs_sbi_build_nr_location(
&sess->nr_tai, &sess->nr_cgi);
if (!ueLocation.nr_location) {
ogs_error("No ueLocation.nr_location");
goto end;
}
ueLocation.nr_location->ue_location_timestamp =
ogs_sbi_gmtime_string(sess->ue_location_timestamp);
if (!ueLocation.nr_location->ue_location_timestamp) {
ogs_error("No ue_location_timestamp");
goto end;
}
HsmfUpdateData.ue_location = &ueLocation;
}
if (sess->nsmf_param.ue_timezone) {
HsmfUpdateData.ue_time_zone = ogs_sbi_timezone_string(ogs_timezone());
if (!HsmfUpdateData.ue_time_zone) {
ogs_error("No ue_time_zone");
goto end;
}
}
if (sess->n1smbuf) {
rv = ogs_nas_5gsm_decode(&nas_message, sess->n1smbuf);
if (rv == OGS_OK) {
n1SmBufFromUe = gsmue_encode_n1_sm_info(&nas_message);
message.part[message.num_of_part].pkbuf = n1SmBufFromUe;
if (message.part[message.num_of_part].pkbuf) {
message.part[message.num_of_part].content_id =
(char *)OGS_SBI_CONTENT_5GNAS_SM_ID;
message.part[message.num_of_part].content_type =
(char *)OGS_SBI_CONTENT_5GNAS_TYPE;
message.num_of_part++;
n1SmInfoFromUe.content_id = (char *)OGS_SBI_CONTENT_5GNAS_SM_ID;
HsmfUpdateData.n1_sm_info_from_ue = &n1SmInfoFromUe;
} else {
ogs_error("gsm_encode_n1_sm_info() failed [%d]", rv);
ogs_log_hexdump(OGS_LOG_ERROR,
sess->n1smbuf->data, sess->n1smbuf->len);
}
} else {
ogs_error("ogs_nas_5gsm_decode() failed [%d]", rv);
ogs_log_hexdump(OGS_LOG_ERROR,
sess->n1smbuf->data, sess->n1smbuf->len);
}
}
message.HsmfUpdateData = &HsmfUpdateData;
request = ogs_sbi_build_request(&message);
ogs_expect(request);
end:
if (message.h.uri)
ogs_free(message.h.uri);
if (ueLocation.nr_location) {
if (ueLocation.nr_location->ue_location_timestamp)
ogs_free(ueLocation.nr_location->ue_location_timestamp);
ogs_sbi_free_nr_location(ueLocation.nr_location);
}
if (HsmfUpdateData.ue_time_zone)
ogs_free(HsmfUpdateData.ue_time_zone);
if (sess->n1smbuf) {
ogs_pkbuf_free(sess->n1smbuf);
sess->n1smbuf = NULL;
}
return request;
}
ogs_sbi_request_t *smf_nsmf_pdusession_build_vsmf_update_data(
smf_sess_t *sess, void *data)
{
ogs_sbi_message_t message;
ogs_sbi_request_t *request = NULL;
OpenAPI_vsmf_update_data_t VsmfUpdateData;
OpenAPI_ref_to_binary_data_t n1SmInfoToUe;
ogs_pkbuf_t *n1SmBufToUe = NULL;
ogs_assert(sess);
memset(&message, 0, sizeof(message));
message.h.method = (char *)OGS_SBI_HTTP_METHOD_POST;
ogs_assert(sess->vsmf_pdu_session_uri);
message.h.uri = ogs_msprintf("%s/%s",
sess->vsmf_pdu_session_uri, OGS_SBI_RESOURCE_NAME_MODIFY);
ogs_assert(message.h.uri);
memset(&VsmfUpdateData, 0, sizeof(VsmfUpdateData));
VsmfUpdateData.request_indication = sess->nsmf_param.request_indication;
ogs_assert(VsmfUpdateData.request_indication);
n1SmBufToUe = gsmue_build_pdu_session_release_command(sess);
ogs_assert(n1SmBufToUe);
n1SmInfoToUe.content_id = (char *)OGS_SBI_CONTENT_5GNAS_SM_ID;
VsmfUpdateData.n1_sm_info_to_ue = &n1SmInfoToUe;
message.part[message.num_of_part].pkbuf = n1SmBufToUe;
message.part[message.num_of_part].content_id =
(char *)OGS_SBI_CONTENT_5GNAS_SM_ID;
message.part[message.num_of_part].content_type =
(char *)OGS_SBI_CONTENT_5GNAS_TYPE;
message.num_of_part++;
message.VsmfUpdateData = &VsmfUpdateData;
request = ogs_sbi_build_request(&message);
ogs_expect(request);
end:
if (message.h.uri)
ogs_free(message.h.uri);
return request;
}
ogs_sbi_request_t *smf_nsmf_pdusession_build_release_data(
smf_sess_t *sess, void *data)
{
ogs_sbi_message_t message;
ogs_sbi_request_t *request = NULL;
smf_ue_t *smf_ue = NULL;
OpenAPI_release_data_t ReleaseData;
OpenAPI_ng_ap_cause_t ngApCause;
OpenAPI_user_location_t ueLocation;
ogs_assert(sess);
smf_ue = smf_ue_find_by_id(sess->smf_ue_id);
ogs_assert(smf_ue);
memset(&message, 0, sizeof(message));
message.h.method = (char *)OGS_SBI_HTTP_METHOD_POST;
ogs_assert(sess->pdu_session_resource_uri);
message.h.uri = ogs_msprintf("%s/%s",
sess->pdu_session_resource_uri, OGS_SBI_RESOURCE_NAME_RELEASE);
ogs_assert(message.h.uri);
memset(&ReleaseData, 0, sizeof(ReleaseData));
memset(&ngApCause, 0, sizeof(ngApCause));
memset(&ueLocation, 0, sizeof(ueLocation));
ReleaseData.cause = sess->nsmf_param.cause;
if (sess->nsmf_param.ngap_cause.group) {
ReleaseData.ng_ap_cause = &ngApCause;
ngApCause.group = sess->nsmf_param.ngap_cause.group;
ngApCause.value = sess->nsmf_param.ngap_cause.value;
}
if (sess->nsmf_param.gmm_cause) {
ReleaseData.is__5g_mm_cause_value = true;
ReleaseData._5g_mm_cause_value = sess->nsmf_param.gmm_cause;
}
if (sess->nsmf_param.ue_location) {
ueLocation.nr_location = ogs_sbi_build_nr_location(
&sess->nr_tai, &sess->nr_cgi);
if (!ueLocation.nr_location) {
ogs_error("No ueLocation.nr_location");
goto end;
}
ueLocation.nr_location->ue_location_timestamp =
ogs_sbi_gmtime_string(sess->ue_location_timestamp);
if (!ueLocation.nr_location->ue_location_timestamp) {
ogs_error("No ue_location_timestamp");
goto end;
}
ReleaseData.ue_location = &ueLocation;
}
if (sess->nsmf_param.ue_timezone) {
ReleaseData.ue_time_zone = ogs_sbi_timezone_string(ogs_timezone());
if (!ReleaseData.ue_time_zone) {
ogs_error("No ue_time_zone");
goto end;
}
}
message.ReleaseData = &ReleaseData;
request = ogs_sbi_build_request(&message);
ogs_expect(request);
end:
if (message.h.uri)
ogs_free(message.h.uri);
if (ueLocation.nr_location) {
if (ueLocation.nr_location->ue_location_timestamp)
ogs_free(ueLocation.nr_location->ue_location_timestamp);
ogs_sbi_free_nr_location(ueLocation.nr_location);
}
if (ReleaseData.ue_time_zone)
ogs_free(ReleaseData.ue_time_zone);
return request;
}
ogs_sbi_request_t *smf_nsmf_pdusession_build_status(
smf_sess_t *sess, void *data)
{
ogs_sbi_message_t message;
ogs_sbi_request_t *request = NULL;
OpenAPI_status_notification_t StatusNotification;
OpenAPI_status_info_t StatusInfo;
ogs_assert(sess);
ogs_assert(sess->vsmf_pdu_session_uri);
memset(&StatusInfo, 0, sizeof(StatusInfo));
StatusInfo.resource_status = OpenAPI_resource_status_RELEASED;
memset(&StatusNotification, 0, sizeof(StatusNotification));
StatusNotification.status_info = &StatusInfo;
memset(&message, 0, sizeof(message));
message.h.method = (char *)OGS_SBI_HTTP_METHOD_POST;
message.h.uri = sess->vsmf_pdu_session_uri;
message.StatusNotification = &StatusNotification;
request = ogs_sbi_build_request(&message);
ogs_expect(request);
return request;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2024 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2024-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@ -27,7 +27,16 @@
extern "C" {
#endif
ogs_sbi_request_t *smf_nsmf_pdusession_build_create_pdu_session(
ogs_sbi_request_t *smf_nsmf_pdusession_build_create_data(
smf_sess_t *sess, void *data);
ogs_sbi_request_t *smf_nsmf_pdusession_build_hsmf_update_data(
smf_sess_t *sess, void *data);
ogs_sbi_request_t *smf_nsmf_pdusession_build_vsmf_update_data(
smf_sess_t *sess, void *data);
ogs_sbi_request_t *smf_nsmf_pdusession_build_release_data(
smf_sess_t *sess, void *data);
ogs_sbi_request_t *smf_nsmf_pdusession_build_status(
smf_sess_t *sess, void *data);
#ifdef __cplusplus

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2019-2024 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@ -21,6 +21,7 @@
#include "nas-path.h"
#include "ngap-path.h"
#include "pfcp-path.h"
#include "local-path.h"
#include "nsmf-handler.h"
bool smf_nsmf_handle_create_sm_context(
@ -607,7 +608,6 @@ bool smf_nsmf_handle_update_sm_context(
smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_sbi_message_t *message)
{
int i;
int r;
smf_ue_t *smf_ue = NULL;
ogs_sbi_message_t sendmsg;
@ -638,6 +638,8 @@ bool smf_nsmf_handle_update_sm_context(
return false;
}
memset(&sess->nsmf_param, 0, sizeof(sess->nsmf_param));
if (SmContextUpdateData->ue_location &&
SmContextUpdateData->ue_location->nr_location) {
OpenAPI_nr_location_t *NrLocation =
@ -658,8 +660,21 @@ bool smf_nsmf_handle_update_sm_context(
ogs_plmn_id_hexdump(&sess->nr_cgi.plmn_id),
(long long)sess->nr_cgi.cell_id);
}
sess->nsmf_param.ue_location = true;
sess->nsmf_param.ue_timezone = true;
}
if (SmContextUpdateData->ng_ap_cause) {
sess->nsmf_param.ngap_cause.group =
SmContextUpdateData->ng_ap_cause->group;
sess->nsmf_param.ngap_cause.value =
SmContextUpdateData->ng_ap_cause->value;
}
sess->nsmf_param.gmm_cause =
SmContextUpdateData->_5g_mm_cause_value;
sess->nsmf_param.cause = SmContextUpdateData->cause;
if (SmContextUpdateData->n1_sm_msg) {
n1SmMsg = SmContextUpdateData->n1_sm_msg;
if (!n1SmMsg || !n1SmMsg->content_id) {
@ -684,14 +699,46 @@ bool smf_nsmf_handle_update_sm_context(
ogs_assert(gsm_header);
sess->pti = gsm_header->procedure_transaction_identity;
switch (gsm_header->message_type) {
case OGS_NAS_5GS_PDU_SESSION_RELEASE_REQUEST:
if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) {
/* Save N1 SM Message and send it to H-SMF */
if (sess->n1smbuf) ogs_pkbuf_free(sess->n1smbuf);
sess->n1smbuf = ogs_pkbuf_copy(n1smbuf);
ogs_assert(sess->n1smbuf);
/* UE Requested PDU Session Release */
sess->nsmf_param.request_indication =
OpenAPI_request_indication_UE_REQ_PDU_SES_REL;
/* Store Stream ID */
sess->amf_update_request_stream_id =
ogs_sbi_id_from_stream(stream);
ogs_assert(OGS_OK ==
smf_5gc_pfcp_send_all_pdr_modification_request(
sess, stream,
OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|
OGS_PFCP_MODIFY_UL_ONLY|
OGS_PFCP_MODIFY_DEACTIVATE,
OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED, 0));
} else {
n1smbuf = ogs_pkbuf_copy(n1smbuf);
ogs_assert(n1smbuf);
nas_5gs_send_to_gsm(sess, stream, n1smbuf);
}
break;
default:
/*
* NOTE : The pkbuf created in the SBI message will be removed
* from ogs_sbi_message_free().
* So it must be copied and push a event queue.
* Do not send PFCP Modification on PDU session release complete.
* PFCP Modification should only be sent on PDU session release request.
*/
n1smbuf = ogs_pkbuf_copy(n1smbuf);
ogs_assert(n1smbuf);
nas_5gs_send_to_gsm(sess, stream, n1smbuf);
n1smbuf = ogs_pkbuf_copy(n1smbuf);
ogs_assert(n1smbuf);
nas_5gs_send_to_gsm(sess, stream, n1smbuf);
}
return true;
@ -770,7 +817,8 @@ bool smf_nsmf_handle_update_sm_context(
ogs_assert(OGS_OK ==
smf_5gc_pfcp_send_all_pdr_modification_request(
sess, stream,
OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE, 0));
OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE,
0, 0));
}
} else if (SmContextUpdateData->up_cnx_state ==
@ -916,7 +964,7 @@ bool smf_nsmf_handle_update_sm_context(
sess, stream,
OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_ACTIVATE|
OGS_PFCP_MODIFY_N2_HANDOVER|OGS_PFCP_MODIFY_END_MARKER,
0));
0, 0));
} else {
char *strerror = ogs_msprintf(
"[%s:%d] No FAR Update", smf_ue->supi, sess->psi);
@ -949,7 +997,7 @@ bool smf_nsmf_handle_update_sm_context(
sess, stream,
OGS_PFCP_MODIFY_INDIRECT|OGS_PFCP_MODIFY_REMOVE|
OGS_PFCP_MODIFY_HANDOVER_CANCEL,
0));
0, 0));
} else {
smf_sbi_send_sm_context_updated_data_ho_state(
sess, stream, OpenAPI_ho_state_CANCELLED);
@ -983,39 +1031,30 @@ bool smf_nsmf_handle_update_sm_context(
ogs_assert(true ==
ogs_sbi_server_send_response(stream, response));
} else if (PCF_SM_POLICY_ASSOCIATED(sess)) {
smf_npcf_smpolicycontrol_param_t param;
memset(&param, 0, sizeof(param));
param.ue_location = true;
param.ue_timezone = true;
r = smf_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NPCF_SMPOLICYCONTROL, NULL,
smf_npcf_smpolicycontrol_build_delete,
sess, stream,
OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT, &param);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else if (UDM_SDM_SUBSCRIBED(sess)) {
ogs_warn("[%s:%d] No PolicyAssociationId. Forcibly remove SESSION",
smf_ue->supi, sess->psi);
r = smf_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NUDM_SDM, NULL,
smf_nudm_sdm_build_subscription_delete, sess, stream,
SMF_UECM_STATE_DEREGISTERED_BY_AMF, NULL);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else {
ogs_warn("[%s:%d] No UDM Subscription. Forcibly remove SESSION",
smf_ue->supi, sess->psi);
r = smf_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NUDM_UECM, NULL,
smf_nudm_uecm_build_deregistration, sess, stream,
SMF_UECM_STATE_DEREGISTERED_BY_AMF, NULL);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) {
/* Network Initiated PDU Session Release */
sess->nsmf_param.request_indication =
OpenAPI_request_indication_NW_REQ_PDU_SES_REL;
/* Remove N1 SM Message */
if (sess->n1smbuf) {
ogs_pkbuf_free(sess->n1smbuf);
sess->n1smbuf = NULL;
}
ogs_assert(OGS_OK ==
smf_5gc_pfcp_send_all_pdr_modification_request(
sess, stream,
OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|
OGS_PFCP_MODIFY_UL_ONLY|
OGS_PFCP_MODIFY_DEACTIVATE,
OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT, 0));
} else {
smf_trigger_session_release(
sess, stream,
OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT);
}
}
} else if (SmContextUpdateData->serving_nf_id) {
ogs_debug("Old amf_nf_id: %s, new amf_nf_id: %s",
@ -1045,8 +1084,6 @@ bool smf_nsmf_handle_update_sm_context(
bool smf_nsmf_handle_release_sm_context(
smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_sbi_message_t *message)
{
int r;
smf_npcf_smpolicycontrol_param_t param;
smf_ue_t *smf_ue = NULL;
OpenAPI_sm_context_release_data_t *SmContextReleaseData = NULL;
@ -1057,7 +1094,7 @@ bool smf_nsmf_handle_release_sm_context(
smf_ue = smf_ue_find_by_id(sess->smf_ue_id);
ogs_assert(smf_ue);
memset(&param, 0, sizeof(param));
memset(&sess->nsmf_param, 0, sizeof(sess->nsmf_param));
SmContextReleaseData = message->SmContextReleaseData;
if (SmContextReleaseData) {
@ -1084,52 +1121,25 @@ bool smf_nsmf_handle_release_sm_context(
(long long)sess->nr_cgi.cell_id);
}
param.ue_location = true;
param.ue_timezone = true;
sess->nsmf_param.ue_location = true;
sess->nsmf_param.ue_timezone = true;
}
if (SmContextReleaseData->ng_ap_cause) {
param.ran_nas_release.ngap_cause.group =
sess->nsmf_param.ngap_cause.group =
SmContextReleaseData->ng_ap_cause->group;
param.ran_nas_release.ngap_cause.value =
sess->nsmf_param.ngap_cause.value =
SmContextReleaseData->ng_ap_cause->value;
}
param.ran_nas_release.gmm_cause =
sess->nsmf_param.gmm_cause =
SmContextReleaseData->_5g_mm_cause_value;
}
if (PCF_SM_POLICY_ASSOCIATED(sess)) {
r = smf_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NPCF_SMPOLICYCONTROL, NULL,
smf_npcf_smpolicycontrol_build_delete,
sess, stream,
OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT, &param);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else if (UDM_SDM_SUBSCRIBED(sess)) {
ogs_warn("[%s:%d] No PolicyAssociationId. Forcibly remove SESSION",
smf_ue->supi, sess->psi);
r = smf_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NUDM_SDM, NULL,
smf_nudm_sdm_build_subscription_delete, sess, stream,
SMF_UECM_STATE_DEREGISTERED_BY_AMF, NULL);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else {
ogs_warn("[%s:%d] No UDM Subscription. Forcibly remove SESSION",
smf_ue->supi, sess->psi);
r = smf_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NUDM_UECM, NULL,
smf_nudm_uecm_build_deregistration, sess, stream,
SMF_UECM_STATE_DEREGISTERED_BY_AMF, NULL);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
sess->nsmf_param.cause = SmContextReleaseData->cause;
}
return true;
}
bool smf_nsmf_handle_create_pdu_session_in_hsmf(
bool smf_nsmf_handle_create_data_in_hsmf(
smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg)
{
bool rc;
@ -1548,7 +1558,7 @@ bool smf_nsmf_handle_create_pdu_session_in_hsmf(
return true;
}
bool smf_nsmf_handle_create_pdu_session_in_vsmf(
bool smf_nsmf_handle_create_data_in_vsmf(
smf_sess_t *sess, ogs_sbi_message_t *recvmsg)
{
int rv;
@ -2041,6 +2051,234 @@ bool smf_nsmf_handle_create_pdu_session_in_vsmf(
return true;
}
bool smf_nsmf_handle_hsmf_update_data(
smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_sbi_message_t *message)
{
int rv;
smf_ue_t *smf_ue = NULL;
OpenAPI_hsmf_update_data_t *HsmfUpdateData = NULL;
OpenAPI_ref_to_binary_data_t *n1SmInfoFromUe = NULL;
ogs_nas_5gs_message_t nas_message;
ogs_pkbuf_t *n1SmBufFromUe = NULL;
ogs_assert(stream);
ogs_assert(message);
ogs_assert(sess);
smf_ue = smf_ue_find_by_id(sess->smf_ue_id);
ogs_assert(smf_ue);
memset(&sess->nsmf_param, 0, sizeof(sess->nsmf_param));
HsmfUpdateData = message->HsmfUpdateData;
if (!HsmfUpdateData) {
ogs_error("[%s:%d] No HsmfUpdateData",
smf_ue->supi, sess->psi);
smf_sbi_send_hsmf_update_error(stream,
OGS_SBI_HTTP_STATUS_BAD_REQUEST, OGS_SBI_APP_ERRNO_NULL,
OGS_5GSM_CAUSE_INVALID_MANDATORY_INFORMATION,
"No HsmfUpdateData", smf_ue->supi, NULL);
return false;
}
if (!HsmfUpdateData->request_indication) {
ogs_error("[%s:%d] No requestIndication",
smf_ue->supi, sess->psi);
smf_sbi_send_hsmf_update_error(stream,
OGS_SBI_HTTP_STATUS_BAD_REQUEST, OGS_SBI_APP_ERRNO_NULL,
OGS_5GSM_CAUSE_INVALID_MANDATORY_INFORMATION,
"No requestIndication", smf_ue->supi, NULL);
return false;
}
sess->nsmf_param.request_indication = HsmfUpdateData->request_indication;
n1SmInfoFromUe = HsmfUpdateData->n1_sm_info_from_ue;
if (n1SmInfoFromUe) {
n1SmBufFromUe = ogs_sbi_find_part_by_content_id(
message, n1SmInfoFromUe->content_id);
if (n1SmBufFromUe) {
rv = gsmue_decode_n1_sm_info(&nas_message, n1SmBufFromUe);
if (rv != OGS_OK) {
ogs_error("[%s:%d] cannot decode N1 SM Content [%s]",
smf_ue->supi, sess->psi, n1SmInfoFromUe->content_id);
ogs_log_hexdump(OGS_LOG_ERROR,
n1SmBufFromUe->data, n1SmBufFromUe->len);
smf_sbi_send_hsmf_update_error(stream,
OGS_SBI_HTTP_STATUS_BAD_REQUEST, OGS_SBI_APP_ERRNO_NULL,
OGS_5GSM_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE,
"cannot decode N1 SM Content", smf_ue->supi, NULL);
return false;
}
}
}
if (HsmfUpdateData->ue_location &&
HsmfUpdateData->ue_location->nr_location) {
OpenAPI_nr_location_t *NrLocation =
HsmfUpdateData->ue_location->nr_location;
if (NrLocation->tai &&
NrLocation->tai->plmn_id && NrLocation->tai->tac &&
NrLocation->ncgi &&
NrLocation->ncgi->plmn_id && NrLocation->ncgi->nr_cell_id) {
ogs_sbi_parse_nr_location(
&sess->nr_tai, &sess->nr_cgi, NrLocation);
if (NrLocation->ue_location_timestamp)
ogs_sbi_time_from_string(&sess->ue_location_timestamp,
NrLocation->ue_location_timestamp);
ogs_debug(" TAI[PLMN_ID:%06x,TAC:%d]",
ogs_plmn_id_hexdump(&sess->nr_tai.plmn_id),
sess->nr_tai.tac.v);
ogs_debug(" NR_CGI[PLMN_ID:%06x,CELL_ID:0x%llx]",
ogs_plmn_id_hexdump(&sess->nr_cgi.plmn_id),
(long long)sess->nr_cgi.cell_id);
}
sess->nsmf_param.ue_location = true;
sess->nsmf_param.ue_timezone = true;
}
if (HsmfUpdateData->ng_ap_cause) {
sess->nsmf_param.ngap_cause.group =
HsmfUpdateData->ng_ap_cause->group;
sess->nsmf_param.ngap_cause.value =
HsmfUpdateData->ng_ap_cause->value;
}
sess->nsmf_param.gmm_cause = HsmfUpdateData->_5g_mm_cause_value;
sess->nsmf_param.cause = HsmfUpdateData->cause;
return true;
}
bool smf_nsmf_handle_vsmf_update_data(
smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_sbi_message_t *message)
{
int rv;
smf_ue_t *smf_ue = NULL;
OpenAPI_vsmf_update_data_t *VsmfUpdateData = NULL;
ogs_nas_5gs_message_t nas_message;
ogs_pkbuf_t *n1SmBufToUe = NULL;
OpenAPI_ref_to_binary_data_t *n1SmInfoToUe = NULL;
ogs_assert(stream);
ogs_assert(message);
ogs_assert(sess);
smf_ue = smf_ue_find_by_id(sess->smf_ue_id);
ogs_assert(smf_ue);
memset(&sess->nsmf_param, 0, sizeof(sess->nsmf_param));
VsmfUpdateData = message->VsmfUpdateData;
if (!VsmfUpdateData) {
ogs_error("[%s:%d] No VsmfUpdateData",
smf_ue->supi, sess->psi);
smf_sbi_send_vsmf_update_error(stream,
OGS_SBI_HTTP_STATUS_BAD_REQUEST, OGS_SBI_APP_ERRNO_NULL,
OGS_5GSM_CAUSE_INVALID_MANDATORY_INFORMATION,
"No VsmfUpdateData", smf_ue->supi, NULL);
return false;
}
if (!VsmfUpdateData->request_indication) {
ogs_error("[%s:%d] No requestIndication",
smf_ue->supi, sess->psi);
smf_sbi_send_vsmf_update_error(stream,
OGS_SBI_HTTP_STATUS_BAD_REQUEST, OGS_SBI_APP_ERRNO_NULL,
OGS_5GSM_CAUSE_INVALID_MANDATORY_INFORMATION,
"No requestIndication", smf_ue->supi, NULL);
return false;
}
sess->nsmf_param.request_indication = VsmfUpdateData->request_indication;
n1SmInfoToUe = VsmfUpdateData->n1_sm_info_to_ue;
if (n1SmInfoToUe) {
n1SmBufToUe = ogs_sbi_find_part_by_content_id(
message, n1SmInfoToUe->content_id);
if (n1SmBufToUe) {
rv = gsmue_decode_n1_sm_info(&nas_message, n1SmBufToUe);
if (rv != OGS_OK) {
ogs_error("[%s:%d] cannot decode N1 SM Content [%s]",
smf_ue->supi, sess->psi, n1SmInfoToUe->content_id);
ogs_log_hexdump(OGS_LOG_ERROR,
n1SmBufToUe->data, n1SmBufToUe->len);
smf_sbi_send_vsmf_update_error(stream,
OGS_SBI_HTTP_STATUS_BAD_REQUEST, OGS_SBI_APP_ERRNO_NULL,
OGS_5GSM_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE,
"cannot decode N1 SM Content", smf_ue->supi, NULL);
return false;
}
}
}
return true;
}
bool smf_nsmf_handle_release_data_in_hsmf(
smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_sbi_message_t *message)
{
smf_ue_t *smf_ue = NULL;
OpenAPI_release_data_t *ReleaseData = NULL;
ogs_assert(stream);
ogs_assert(message);
ogs_assert(sess);
smf_ue = smf_ue_find_by_id(sess->smf_ue_id);
ogs_assert(smf_ue);
memset(&sess->nsmf_param, 0, sizeof(sess->nsmf_param));
ReleaseData = message->ReleaseData;
if (ReleaseData) {
if (ReleaseData->ue_location &&
ReleaseData->ue_location->nr_location) {
OpenAPI_nr_location_t *NrLocation =
ReleaseData->ue_location->nr_location;
if (NrLocation->tai &&
NrLocation->tai->plmn_id && NrLocation->tai->tac &&
NrLocation->ncgi &&
NrLocation->ncgi->plmn_id && NrLocation->ncgi->nr_cell_id) {
ogs_sbi_parse_nr_location(
&sess->nr_tai, &sess->nr_cgi, NrLocation);
if (NrLocation->ue_location_timestamp)
ogs_sbi_time_from_string(&sess->ue_location_timestamp,
NrLocation->ue_location_timestamp);
ogs_debug(" TAI[PLMN_ID:%06x,TAC:%d]",
ogs_plmn_id_hexdump(&sess->nr_tai.plmn_id),
sess->nr_tai.tac.v);
ogs_debug(" NR_CGI[PLMN_ID:%06x,CELL_ID:0x%llx]",
ogs_plmn_id_hexdump(&sess->nr_cgi.plmn_id),
(long long)sess->nr_cgi.cell_id);
}
sess->nsmf_param.ue_location = true;
sess->nsmf_param.ue_timezone = true;
}
if (ReleaseData->ng_ap_cause) {
sess->nsmf_param.ngap_cause.group =
ReleaseData->ng_ap_cause->group;
sess->nsmf_param.ngap_cause.value =
ReleaseData->ng_ap_cause->value;
}
sess->nsmf_param.gmm_cause = ReleaseData->_5g_mm_cause_value;
sess->nsmf_param.cause = ReleaseData->cause;
}
return true;
}
bool smf_nsmf_callback_handle_sdm_data_change_notify(
ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg)
{

View file

@ -33,10 +33,19 @@ bool smf_nsmf_handle_update_sm_context(
bool smf_nsmf_handle_release_sm_context(
smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_sbi_message_t *message);
bool smf_nsmf_handle_create_pdu_session_in_hsmf(
bool smf_nsmf_handle_create_data_in_hsmf(
smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg);
bool smf_nsmf_handle_create_pdu_session_in_vsmf(
bool smf_nsmf_handle_create_data_in_vsmf(
smf_sess_t *sess, ogs_sbi_message_t *recvmsg);
bool smf_nsmf_handle_hsmf_update_data(
smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_sbi_message_t *message);
bool smf_nsmf_handle_vsmf_update_data(
smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_sbi_message_t *message);
bool smf_nsmf_handle_release_data_in_hsmf(
smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_sbi_message_t *message);
bool smf_nsmf_callback_handle_sdm_data_change_notify(
ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg);

View file

@ -611,7 +611,7 @@ int smf_5gc_pfcp_send_session_establishment_request(
int smf_5gc_pfcp_send_all_pdr_modification_request(
smf_sess_t *sess, ogs_sbi_stream_t *stream,
uint64_t flags, ogs_time_t duration)
uint64_t flags, int trigger, ogs_time_t duration)
{
int rv;
ogs_pfcp_xact_t *xact = NULL;
@ -636,6 +636,7 @@ int smf_5gc_pfcp_send_all_pdr_modification_request(
xact->local_seid = sess->smf_n4_seid;
xact->modify_flags = flags | OGS_PFCP_MODIFY_SESSION;
xact->delete_trigger = trigger;
ogs_list_init(&sess->pdr_to_modify_list);
ogs_list_for_each(&sess->pfcp.pdr_list, pdr)

View file

@ -39,7 +39,7 @@ int smf_5gc_pfcp_send_session_establishment_request(
smf_sess_t *sess, ogs_sbi_stream_t *stream, uint64_t flags);
int smf_5gc_pfcp_send_all_pdr_modification_request(
smf_sess_t *sess, ogs_sbi_stream_t *stream,
uint64_t flags, ogs_time_t duration);
uint64_t flags, int trigger, ogs_time_t duration);
int smf_5gc_pfcp_send_qos_flow_list_modification_request(
smf_sess_t *sess, ogs_sbi_stream_t *stream,
uint64_t flags, ogs_time_t duration);

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2019-2024 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@ -19,6 +19,7 @@
#include "sbi-path.h"
#include "pfcp-path.h"
#include "local-path.h"
#include "n4-handler.h"
@ -490,7 +491,6 @@ static void pfcp_restoration(ogs_pfcp_node_t *node)
static void reselect_upf(ogs_pfcp_node_t *node)
{
int r;
smf_ue_t *smf_ue = NULL;
ogs_pfcp_node_t *iter = NULL;
@ -523,27 +523,9 @@ static void reselect_upf(ogs_pfcp_node_t *node)
ogs_error("[%s:%s] EPC restoration is not implemented",
smf_ue->imsi_bcd, sess->session.name);
} else {
if (PCF_SM_POLICY_ASSOCIATED(sess)) {
smf_npcf_smpolicycontrol_param_t param;
ogs_info("[%s:%d] SMF-initiated Deletion",
smf_ue->supi, sess->psi);
ogs_assert(sess->sm_context_ref);
memset(&param, 0, sizeof(param));
r = smf_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NPCF_SMPOLICYCONTROL, NULL,
smf_npcf_smpolicycontrol_build_delete,
sess, NULL,
OGS_PFCP_DELETE_TRIGGER_SMF_INITIATED,
&param);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else {
ogs_error("[%s:%d] No PolicyAssociationId. "
"Forcibly remove SESSION",
smf_ue->supi, sess->psi);
SMF_SESS_CLEAR(sess);
}
smf_trigger_session_release(
sess, NULL,
OGS_PFCP_DELETE_TRIGGER_SMF_INITIATED);
}
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2019-2024 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@ -237,7 +237,8 @@ int smf_sbi_discover_and_send(
}
void smf_namf_comm_send_n1_n2_message_transfer(
smf_sess_t *sess, smf_n1_n2_message_transfer_param_t *param)
smf_sess_t *sess, ogs_sbi_stream_t *stream,
smf_n1_n2_message_transfer_param_t *param)
{
smf_ue_t *smf_ue = NULL;
ogs_sbi_xact_t *xact = NULL;
@ -269,6 +270,12 @@ void smf_namf_comm_send_n1_n2_message_transfer(
xact->state = param->state;
if (stream) {
xact->assoc_stream_id = ogs_sbi_id_from_stream(stream);
ogs_assert(xact->assoc_stream_id >= OGS_MIN_POOL_ID &&
xact->assoc_stream_id <= OGS_MAX_POOL_ID);
}
r = ogs_sbi_discover_and_send(xact);
if (r != OGS_OK) {
ogs_error("smf_namf_comm_send_n1_n2_message_transfer() failed");
@ -278,17 +285,17 @@ void smf_namf_comm_send_n1_n2_message_transfer(
}
void smf_namf_comm_send_n1_n2_pdu_establishment_reject(
smf_sess_t *sess)
smf_sess_t *sess, ogs_sbi_stream_t *stream)
{
smf_n1_n2_message_transfer_param_t param;
memset(&param, 0, sizeof(param));
param.state = SMF_NETWORK_REQUESTED_PDU_SESSION_RELEASE;
param.state = SMF_UE_OR_NETWORK_REQUESTED_PDU_SESSION_RELEASE;
param.n1smbuf = gsm_build_pdu_session_establishment_reject(sess,
OGS_5GSM_CAUSE_NETWORK_FAILURE);
ogs_assert(param.n1smbuf);
smf_namf_comm_send_n1_n2_message_transfer(sess, &param);
smf_namf_comm_send_n1_n2_message_transfer(sess, stream, &param);
}
void smf_sbi_send_sm_context_created_data(
@ -825,6 +832,92 @@ static int client_notify_cb(
return OGS_OK;
}
int smf_sbi_cleanup_session(
smf_sess_t *sess,
ogs_sbi_stream_t *stream,
int state,
smf_sbi_cleanup_mode_t mode)
{
smf_ue_t *smf_ue = NULL;
int r = OGS_ERROR;
ogs_assert(mode);
ogs_assert(sess);
smf_ue = smf_ue_find_by_id(sess->smf_ue_id);
ogs_assert(smf_ue);
ogs_assert(state);
switch (mode) {
case SMF_SBI_CLEANUP_MODE_POLICY_FIRST:
if (PCF_SM_POLICY_ASSOCIATED(sess)) {
r = smf_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NPCF_SMPOLICYCONTROL,
NULL,
smf_npcf_smpolicycontrol_build_delete,
sess, stream, state, NULL);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else if (UDM_SDM_SUBSCRIBED(sess)) {
ogs_error("[%s:%d] No PolicyAssociationId. Forcibly remove SESSION",
smf_ue->supi, sess->psi);
r = smf_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NUDM_SDM,
NULL,
smf_nudm_sdm_build_subscription_delete,
sess, stream, state, NULL);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else {
ogs_error("[%s:%d] No UDM Subscription. Forcibly remove SESSION",
smf_ue->supi, sess->psi);
r = smf_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NUDM_UECM,
NULL,
smf_nudm_uecm_build_deregistration,
sess, stream, state, NULL);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
}
break;
case SMF_SBI_CLEANUP_MODE_SUBSCRIPTION_FIRST:
if (UDM_SDM_SUBSCRIBED(sess)) {
r = smf_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NUDM_SDM,
NULL,
smf_nudm_sdm_build_subscription_delete,
sess, stream, state, NULL);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else {
ogs_error("[%s:%d] No UDM Subscription. Forcibly remove SESSION",
smf_ue->supi, sess->psi);
r = smf_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NUDM_UECM,
NULL,
smf_nudm_uecm_build_deregistration,
sess, stream, state, NULL);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
}
break;
case SMF_SBI_CLEANUP_MODE_CONTEXT_ONLY:
r = smf_sbi_discover_and_send(
OGS_SBI_SERVICE_TYPE_NUDM_UECM,
NULL,
smf_nudm_uecm_build_deregistration,
sess, stream, state, NULL);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
break;
}
return r;
}
bool smf_sbi_send_sm_context_status_notify(smf_sess_t *sess)
{
bool rc;
@ -907,3 +1000,156 @@ void smf_sbi_send_pdu_session_create_error(
if (n1SmBufToUe)
ogs_pkbuf_free(n1SmBufToUe);
}
void smf_sbi_send_hsmf_update_error(
ogs_sbi_stream_t *stream,
int status, ogs_sbi_app_errno_e err, int n1SmCause,
const char *title, const char *detail,
ogs_pkbuf_t *n1SmBufToUe)
{
ogs_sbi_message_t sendmsg;
ogs_sbi_response_t *response = NULL;
OpenAPI_hsmf_update_error_t HsmfUpdateError;
OpenAPI_problem_details_t problem;
OpenAPI_ref_to_binary_data_t n1SmMsgToUe;
ogs_assert(stream);
memset(&sendmsg, 0, sizeof(sendmsg));
memset(&problem, 0, sizeof(problem));
memset(&HsmfUpdateError, 0, sizeof(HsmfUpdateError));
if (status) {
problem.is_status = true;
problem.status = status;
}
problem.title = (char*)title;
problem.detail = (char*)detail;
if (err > OGS_SBI_APP_ERRNO_NULL && err < OGS_SBI_MAX_NUM_OF_APP_ERRNO)
problem.cause = (char*)ogs_sbi_app_strerror(err);
sendmsg.HsmfUpdateError = &HsmfUpdateError;
memset(&HsmfUpdateError, 0, sizeof(HsmfUpdateError));
HsmfUpdateError.error = &problem;
if (n1SmCause)
HsmfUpdateError.n1sm_cause = ogs_msprintf("%02x", n1SmCause);
if (n1SmBufToUe) {
HsmfUpdateError.n1_sm_info_to_ue = &n1SmMsgToUe;
n1SmMsgToUe.content_id = (char *)OGS_SBI_CONTENT_5GNAS_SM_ID;
sendmsg.part[0].content_id = (char *)OGS_SBI_CONTENT_5GNAS_SM_ID;
sendmsg.part[0].content_type = (char *)OGS_SBI_CONTENT_5GNAS_TYPE;
sendmsg.part[0].pkbuf = n1SmBufToUe;
sendmsg.num_of_part = 1;
}
response = ogs_sbi_build_response(&sendmsg, problem.status);
ogs_assert(response);
ogs_assert(true == ogs_sbi_server_send_response(stream, response));
if (HsmfUpdateError.n1sm_cause)
ogs_free(HsmfUpdateError.n1sm_cause);
if (n1SmBufToUe)
ogs_pkbuf_free(n1SmBufToUe);
}
void smf_sbi_send_vsmf_update_error(
ogs_sbi_stream_t *stream,
int status, ogs_sbi_app_errno_e err, int n1SmCause,
const char *title, const char *detail,
ogs_pkbuf_t *n1SmBufFromUe)
{
ogs_sbi_message_t sendmsg;
ogs_sbi_response_t *response = NULL;
OpenAPI_vsmf_update_error_t VsmfUpdateError;
OpenAPI_ext_problem_details_t problem;
OpenAPI_ref_to_binary_data_t n1SmMsgFromUe;
ogs_assert(stream);
memset(&sendmsg, 0, sizeof(sendmsg));
memset(&problem, 0, sizeof(problem));
memset(&VsmfUpdateError, 0, sizeof(VsmfUpdateError));
if (status) {
problem.is_status = true;
problem.status = status;
}
problem.title = (char*)title;
problem.detail = (char*)detail;
if (err > OGS_SBI_APP_ERRNO_NULL && err < OGS_SBI_MAX_NUM_OF_APP_ERRNO)
problem.cause = (char*)ogs_sbi_app_strerror(err);
sendmsg.VsmfUpdateError = &VsmfUpdateError;
memset(&VsmfUpdateError, 0, sizeof(VsmfUpdateError));
VsmfUpdateError.error = &problem;
if (n1SmCause)
VsmfUpdateError.n1sm_cause = ogs_msprintf("%02x", n1SmCause);
if (n1SmBufFromUe) {
VsmfUpdateError.n1_sm_info_from_ue = &n1SmMsgFromUe;
n1SmMsgFromUe.content_id = (char *)OGS_SBI_CONTENT_5GNAS_SM_ID;
sendmsg.part[0].content_id = (char *)OGS_SBI_CONTENT_5GNAS_SM_ID;
sendmsg.part[0].content_type = (char *)OGS_SBI_CONTENT_5GNAS_TYPE;
sendmsg.part[0].pkbuf = n1SmBufFromUe;
sendmsg.num_of_part = 1;
}
response = ogs_sbi_build_response(&sendmsg, problem.status);
ogs_assert(response);
ogs_assert(true == ogs_sbi_server_send_response(stream, response));
if (VsmfUpdateError.n1sm_cause)
ogs_free(VsmfUpdateError.n1sm_cause);
if (n1SmBufFromUe)
ogs_pkbuf_free(n1SmBufFromUe);
}
void smf_sbi_send_released_data(
smf_sess_t *sess, ogs_sbi_stream_t *stream)
{
OpenAPI_released_data_t ReleasedData;
ogs_sbi_message_t sendmsg;
ogs_sbi_response_t *response = NULL;
memset(&ReleasedData, 0, sizeof(ReleasedData));
memset(&sendmsg, 0, sizeof(sendmsg));
sendmsg.ReleasedData = &ReleasedData;
response = ogs_sbi_build_response(&sendmsg, OGS_SBI_HTTP_STATUS_OK);
ogs_assert(response);
ogs_assert(true == ogs_sbi_server_send_response(stream, response));
}
bool smf_sbi_send_status_notify(smf_sess_t *sess)
{
bool rc;
ogs_sbi_request_t *request = NULL;
ogs_sbi_client_t *client = NULL;
ogs_assert(sess);
client = sess->v_smf.client;
ogs_assert(client);
request = smf_nsmf_pdusession_build_status(sess, NULL);
if (!request) {
ogs_error("smf_nsmf_pdusession_build_status() failed");
return false;
}
rc = ogs_sbi_send_request_to_client(
client, client_notify_cb, request, NULL);
ogs_expect(rc == true);
ogs_sbi_request_free(request);
return rc;
}

View file

@ -43,9 +43,10 @@ int smf_sbi_discover_and_send(
smf_sess_t *sess, ogs_sbi_stream_t *stream, int state, void *data);
void smf_namf_comm_send_n1_n2_message_transfer(
smf_sess_t *sess, smf_n1_n2_message_transfer_param_t *param);
smf_sess_t *sess, ogs_sbi_stream_t *stream,
smf_n1_n2_message_transfer_param_t *param);
void smf_namf_comm_send_n1_n2_pdu_establishment_reject(
smf_sess_t *sess);
smf_sess_t *sess, ogs_sbi_stream_t *stream);
void smf_sbi_send_sm_context_created_data(
smf_sess_t *sess, ogs_sbi_stream_t *stream);
@ -104,6 +105,28 @@ void smf_sbi_send_sm_context_update_error(
OpenAPI_n2_sm_info_type_e n2_sm_info_type,
OpenAPI_up_cnx_state_e up_cnx_state);
/* Cleanup modes for session resources via SBI */
typedef enum {
SMF_SBI_CLEANUP_MODE_POLICY_FIRST = 1, /* Policy→Subscr→Context */
SMF_SBI_CLEANUP_MODE_SUBSCRIPTION_FIRST, /* Subscr→Context */
SMF_SBI_CLEANUP_MODE_CONTEXT_ONLY /* Context only */
} smf_sbi_cleanup_mode_t;
/**
* Send SBI request to clean up session resources.
*
* @param sess Session object pointer.
* @param stream SBI stream object pointer.
* @param state State code for the cleanup action.
* @param mode Cleanup mode to select the workflow.
* @return OGS_OK on success, else OGS_ERROR.
*/
int smf_sbi_cleanup_session(
smf_sess_t *sess,
ogs_sbi_stream_t *stream,
int state,
smf_sbi_cleanup_mode_t mode);
bool smf_sbi_send_sm_context_status_notify(smf_sess_t *sess);
void smf_sbi_send_pdu_session_created_data(
@ -114,6 +137,22 @@ void smf_sbi_send_pdu_session_create_error(
const char *title, const char *detail,
ogs_pkbuf_t *n1SmBufToUe);
void smf_sbi_send_hsmf_update_error(
ogs_sbi_stream_t *stream,
int status, ogs_sbi_app_errno_e err, int n1SmCause,
const char *title, const char *detail,
ogs_pkbuf_t *n1SmBufToUe);
void smf_sbi_send_vsmf_update_error(
ogs_sbi_stream_t *stream,
int status, ogs_sbi_app_errno_e err, int n1SmCause,
const char *title, const char *detail,
ogs_pkbuf_t *n1SmBufFromUe);
void smf_sbi_send_released_data(
smf_sess_t *sess, ogs_sbi_stream_t *stream);
bool smf_sbi_send_status_notify(smf_sess_t *sess);
#ifdef __cplusplus
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2019-2024 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@ -598,10 +598,12 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e)
if (!sbi_message.h.resource.component[1]) {
ogs_error("No pduSessionRef [%s]",
sbi_message.h.resource.component[1]);
smf_sbi_send_sm_context_update_error_log(
stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
smf_sbi_send_hsmf_update_error(stream,
OGS_SBI_HTTP_STATUS_BAD_REQUEST,
OGS_SBI_APP_ERRNO_NULL,
OGS_5GSM_CAUSE_INVALID_MANDATORY_INFORMATION,
"No pduSessionRef",
sbi_message.h.resource.component[1]);
sbi_message.h.resource.component[1], NULL);
break;
}
@ -610,9 +612,11 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e)
if (!sess) {
ogs_warn("Not found [%s]", sbi_message.h.uri);
smf_sbi_send_sm_context_update_error_log(
stream, OGS_SBI_HTTP_STATUS_NOT_FOUND,
"Not found", sbi_message.h.uri);
smf_sbi_send_hsmf_update_error(stream,
OGS_SBI_HTTP_STATUS_BAD_REQUEST,
OGS_SBI_APP_ERRNO_NULL,
OGS_5GSM_CAUSE_INVALID_MANDATORY_INFORMATION,
"Not found", sbi_message.h.uri, NULL);
}
break;
@ -634,6 +638,54 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e)
END
break;
DEFAULT
ogs_error("Invalid HTTP method [%s]", sbi_message.h.method);
ogs_assert(true ==
ogs_sbi_server_send_error(stream,
OGS_SBI_HTTP_STATUS_BAD_REQUEST, &sbi_message,
"Invalid HTTP method", sbi_message.h.method,
NULL));
END
if (sess) {
smf_ue = smf_ue_find_by_id(sess->smf_ue_id);
ogs_assert(smf_ue);
ogs_assert(OGS_FSM_STATE(&sess->sm));
e->sess_id = sess->id;
e->h.sbi.message = &sbi_message;
ogs_fsm_dispatch(&sess->sm, e);
}
break;
CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS)
SWITCH(sbi_message.h.method)
CASE(OGS_SBI_HTTP_METHOD_POST)
if (!sbi_message.h.resource.component[1]) {
ogs_error("No smContextRef [%s]",
sbi_message.h.resource.component[1]);
smf_sbi_send_vsmf_update_error(stream,
OGS_SBI_HTTP_STATUS_BAD_REQUEST,
OGS_SBI_APP_ERRNO_NULL,
OGS_5GSM_CAUSE_INVALID_MANDATORY_INFORMATION,
"No smContextRef",
sbi_message.h.resource.component[1], NULL);
break;
}
sess = smf_sess_find_by_sm_context_ref(
sbi_message.h.resource.component[1]);
if (!sess) {
ogs_warn("Not found [%s]", sbi_message.h.uri);
smf_sbi_send_vsmf_update_error(stream,
OGS_SBI_HTTP_STATUS_BAD_REQUEST,
OGS_SBI_APP_ERRNO_NULL,
OGS_5GSM_CAUSE_INVALID_MANDATORY_INFORMATION,
"Not found", sbi_message.h.uri, NULL);
}
break;
DEFAULT
ogs_error("Invalid HTTP method [%s]", sbi_message.h.method);
ogs_assert(true ==
@ -699,10 +751,13 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e)
SWITCH(sbi_message.h.resource.component[2])
CASE(OGS_SBI_RESOURCE_NAME_UPDATE)
#define ENABLE_PCF_INITIATED_SESSION_RELEASE 0
#if !ENABLE_PCF_INITIATED_SESSION_RELEASE
smf_npcf_smpolicycontrol_handle_update_notify(
sess, stream, &sbi_message);
break;
CASE(OGS_SBI_RESOURCE_NAME_TERMINATE)
#endif
smf_npcf_smpolicycontrol_handle_terminate_notify(
sess, stream, &sbi_message);
break;
@ -881,6 +936,7 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e)
CASE(OGS_SBI_SERVICE_NAME_NUDM_SDM)
CASE(OGS_SBI_SERVICE_NAME_NPCF_SMPOLICYCONTROL)
CASE(OGS_SBI_SERVICE_NAME_NAMF_COMM)
CASE(OGS_SBI_SERVICE_NAME_NSMF_PDUSESSION)
sbi_xact_id = OGS_POINTER_TO_UINT(e->h.sbi.data);
ogs_assert(sbi_xact_id >= OGS_MIN_POOL_ID &&
sbi_xact_id <= OGS_MAX_POOL_ID);
@ -957,13 +1013,15 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e)
ogs_assert(smf_ue);
if (state == SMF_UECM_STATE_REGISTERED ||
state ==SMF_UECM_STATE_REGISTERED_BY_HOME_ROUTED_ROAMING) {
state == SMF_UECM_STATE_REGISTERED_HR) {
/* SMF Registration */
if (sbi_message.res_status != OGS_SBI_HTTP_STATUS_OK &&
sbi_message.res_status != OGS_SBI_HTTP_STATUS_CREATED)
unknown_res_status = true;
} else if (state == SMF_UECM_STATE_DEREGISTERED_BY_AMF ||
state == SMF_UECM_STATE_DEREGISTERED_BY_N1_N2_RELEASE) {
} else if (state == SMF_UECM_STATE_DEREG_BY_AMF ||
state == SMF_UECM_STATE_DEREG_BY_AMF_HR ||
state == SMF_UECM_STATE_DEREG_BY_N1N2 ||
state == SMF_UECM_STATE_DEREG_BY_N1N2_HR) {
/* SMF Deregistration */
if (sbi_message.res_status != OGS_SBI_HTTP_STATUS_NO_CONTENT)
unknown_res_status = true;
@ -992,88 +1050,69 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e)
/* SMF Registration */
ogs_assert(stream);
ogs_assert(true == ogs_sbi_send_http_status_no_content(stream));
} else if (state ==
SMF_UECM_STATE_REGISTERED_BY_HOME_ROUTED_ROAMING) {
} else if (state == SMF_UECM_STATE_REGISTERED_HR) {
smf_sbi_send_pdu_session_created_data(sess, stream);
smf_metrics_inst_by_slice_add(
&sess->serving_plmn_id, &sess->s_nssai,
SMF_METR_CTR_SM_PDUSESSIONCREATIONSUCC, 1);
} else if (state == SMF_UECM_STATE_DEREGISTERED_BY_AMF) {
/* SMF Deregistration */
} else if (state == SMF_UECM_STATE_DEREG_BY_AMF) {
/*
* Handle deregistration states SMF_UECM_STATE_DEREG_BY_AMF and
* SMF_UECM_STATE_DEREG_BY_N1N2: context has been removed by AMF or
* by NAS (N1/N2) procedures, respectively.
*
* In 3GPP TS 23.501, two related flows must be coordinated:
* - PDU Session Release: Figure 4.3.4.2-1, steps 11 to 15
* - PDU Session Establishment: Figure 4.3.2.2.1-1, steps 16a to 16c
*
* Normally, step 11 (SMContextStatusNotify) informs the AMF that the SM
* Context has been released, and SMF would then delete the session state.
* However, if the AMF triggers a release while a new establishment is still
* underway (the PFCP Modification and NAS Registration in steps 16a16c),
* deleting the context too early causes those procedures to fail.
*
* To avoid this race, we defer step 11 until after steps 1215 complete:
* subscription termination, policy cleanup, and UDM deregistration. This
* delay allows both the old (released) context and the new (establishing)
* context to coexist in SMF, so that PFCP and NAS messages can still find
* the correct context. Only once all cleanup is done do we invoke the
* SMContextStatusNotify and remove the session state.
*
* This ensures:
* - PFCP Modification (step 16a) still locates its context
* - NAS Registration (steps 16b16c) can finish successfully
* - The final cleanup (deferred step 11) does not interrupt any in-flight
* procedures
*
* If you attempt to address this:
*
* Legacy flow:
* sess = smf_sess_find_by_psi(...);
* if (sess) { smf_sess_remove(sess); }
* sess = smf_sess_add_by_psi(...);
*
* - Do not use smf_sess_find_by_psi() to remove old contexts here.
* - Call only smf_sess_add_by_psi() to add the new session.
* - Since PSI duplicates may occur without an explicit release, you must
* design a separate cleanup mechanism (e.g., periodic purge, release
* tracking, or context deduplication) that safely detects and removes
* stale SM Context entries outside of this path.
*/
ogs_assert(stream);
ogs_assert(true == ogs_sbi_send_http_status_no_content(stream));
SMF_SESS_CLEAR(sess);
} else if (state == SMF_UECM_STATE_DEREGISTERED_BY_N1_N2_RELEASE) {
/* SMF Deregistration */
} else if (state == SMF_UECM_STATE_DEREG_BY_N1N2) {
ogs_assert(true == smf_sbi_send_sm_context_status_notify(sess));
SMF_SESS_CLEAR(sess);
} else if (state == SMF_UECM_STATE_DEREG_BY_AMF_HR) {
SMF_SESS_CLEAR(sess);
} else if (state == SMF_UECM_STATE_DEREG_BY_N1N2_HR) {
ogs_assert(true == smf_sbi_send_status_notify(sess));
SMF_SESS_CLEAR(sess);
}
break;
CASE(OGS_SBI_SERVICE_NAME_NSMF_PDUSESSION)
SWITCH(sbi_message.h.resource.component[0])
CASE(OGS_SBI_RESOURCE_NAME_PDU_SESSIONS)
SWITCH(sbi_message.h.method)
CASE(OGS_SBI_HTTP_METHOD_POST)
sbi_xact_id = OGS_POINTER_TO_UINT(e->h.sbi.data);
ogs_assert(sbi_xact_id >= OGS_MIN_POOL_ID &&
sbi_xact_id <= OGS_MAX_POOL_ID);
sbi_xact = ogs_sbi_xact_find_by_id(sbi_xact_id);
if (!sbi_xact) {
/* CLIENT_WAIT timer could remove SBI transaction
* before receiving SBI message */
ogs_error(
"SBI transaction has already been removed [%d]",
sbi_xact_id);
break;
}
sbi_object_id = sbi_xact->sbi_object_id;
ogs_assert(sbi_object_id >= OGS_MIN_POOL_ID &&
sbi_object_id <= OGS_MAX_POOL_ID);
ogs_sbi_xact_remove(sbi_xact);
sess = smf_sess_find_by_id(sbi_object_id);
if (!sess) {
ogs_error("Session has already been removed");
break;
}
smf_ue = smf_ue_find_by_id(sess->smf_ue_id);
ogs_assert(smf_ue);
SWITCH(sbi_message.h.resource.component[2])
CASE(OGS_SBI_RESOURCE_NAME_MODIFY)
CASE(OGS_SBI_RESOURCE_NAME_RELEASE)
ogs_error("Not Implemented");
break;
DEFAULT
if (smf_nsmf_handle_create_pdu_session_in_vsmf(
sess, &sbi_message) == false) {
ogs_error("[%s:%d] create_pdu_session "
"failed() [%d]",
smf_ue->supi, sess->psi,
sbi_message.res_status);
SMF_SESS_CLEAR(sess);
}
END
break;
DEFAULT
ogs_error("Invalid HTTP method [%s]", sbi_message.h.method);
ogs_assert_if_reached();
END
break;
DEFAULT
ogs_error("Invalid resource name [%s]",
sbi_message.h.resource.component[0]);
ogs_assert_if_reached();
END
break;
DEFAULT
ogs_error("Invalid service name [%s]", sbi_message.h.service.name);
ogs_assert_if_reached();
@ -1233,6 +1272,16 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e)
ogs_pkbuf_free(pkbuf);
break;
case SMF_EVT_SESSION_RELEASE:
sess = smf_sess_find_by_id(e->sess_id);
if (!sess) {
ogs_error("Session has already been removed");
break;
}
ogs_fsm_dispatch(&sess->sm, e);
break;
default:
ogs_error("No handler for event %s", smf_event_get_name(e));
break;

View file

@ -42,7 +42,7 @@ void smf_gsm_state_wait_epc_auth_release(ogs_fsm_t *s, smf_event_t *e);
void smf_gsm_state_wait_5gc_n1_n2_release(ogs_fsm_t *s, smf_event_t *e);
void smf_gsm_state_5gc_n1_n2_reject(ogs_fsm_t *s, smf_event_t *e);
void smf_gsm_state_5gc_session_will_deregister(ogs_fsm_t *s, smf_event_t *e);
void smf_gsm_state_epc_session_will_release(ogs_fsm_t *s, smf_event_t *e);
void smf_gsm_state_session_will_release(ogs_fsm_t *s, smf_event_t *e);
void smf_gsm_state_exception(ogs_fsm_t *s, smf_event_t *e);
void smf_pfcp_state_initial(ogs_fsm_t *s, smf_event_t *e);

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2019-2023 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@ -1599,7 +1599,11 @@ bson_t *test_db_new_simple(test_ue_t *test_ue)
"pre_emption_capability", BCON_INT32(1),
"}",
"}",
#if 0
"lbo_roaming_allowed", BCON_BOOL(true),
#else
"lbo_roaming_allowed", BCON_BOOL(false),
#endif
"}", "]",
"}", "]",
"security", "{",
@ -2085,6 +2089,11 @@ bson_t *test_db_new_ims(test_ue_t *test_ue)
"}",
"}",
"]",
#if 1
"lbo_roaming_allowed", BCON_BOOL(true),
#else
"lbo_roaming_allowed", BCON_BOOL(false),
#endif
"}",
"]",
"}", "]",

View file

@ -19,6 +19,8 @@
#include "test-common.h"
#define ENABLE_SMF_INITIATED_SESSION_RELEASE 0
static void test1_func(abts_case *tc, void *data)
{
int rv;
@ -258,6 +260,44 @@ static void test1_func(abts_case *tc, void *data)
ogs_pkbuf_free(recvbuf);
#endif
#if ENABLE_SMF_INITIATED_SESSION_RELEASE
/* Receive PDUSessionResourceReleaseCommand +
* DL NAS transport +
* PDU session release command */
recvbuf = testgnb_ngap_read(ngap);
ABTS_PTR_NOTNULL(tc, recvbuf);
testngap_recv(test_ue, recvbuf);
ABTS_INT_EQUAL(tc,
NGAP_ProcedureCode_id_PDUSessionResourceRelease,
test_ue->ngap_procedure_code);
/* Send PDUSessionResourceReleaseResponse */
sendbuf = testngap_build_pdu_session_resource_release_response(sess);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Send UplinkNASTransport +
* UL NAS trasnport +
* PDU session resource release complete */
sess->ul_nas_transport_param.request_type = 0;
sess->ul_nas_transport_param.dnn = 0;
sess->ul_nas_transport_param.s_nssai = 0;
sess->pdu_session_establishment_param.ssc_mode = 0;
sess->pdu_session_establishment_param.epco = 0;
gsmbuf = testgsm_build_pdu_session_release_complete(sess);
ABTS_PTR_NOTNULL(tc, gsmbuf);
gmmbuf = testgmm_build_ul_nas_transport(sess,
OGS_NAS_PAYLOAD_CONTAINER_N1_SM_INFORMATION, gsmbuf);
ABTS_PTR_NOTNULL(tc, gmmbuf);
sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
#endif
/* Send UEContextReleaseRequest */
sendbuf = testngap_build_ue_context_release_request(test_ue,
NGAP_Cause_PR_radioNetwork, NGAP_CauseRadioNetwork_user_inactivity,

View file

@ -20,6 +20,8 @@
#include "test-common.h"
#include "af/sbi-path.h"
#define ENABLE_PCF_INITIATED_SESSION_RELEASE 0
static void test1_func(abts_case *tc, void *data)
{
int rv;
@ -315,6 +317,7 @@ static void test1_func(abts_case *tc, void *data)
af_local_send_to_pcf(af_sess, &af_param,
af_npcf_policyauthorization_build_create);
#if !ENABLE_PCF_INITIATED_SESSION_RELEASE
/* Receive PDUSessionResourceModifyRequest +
* DL NAS transport +
* PDU session modification command */
@ -411,10 +414,48 @@ static void test1_func(abts_case *tc, void *data)
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
#else
/* Receive PDUSessionResourceReleaseCommand +
* DL NAS transport +
* PDU session release command */
recvbuf = testgnb_ngap_read(ngap);
ABTS_PTR_NOTNULL(tc, recvbuf);
testngap_recv(test_ue, recvbuf);
ABTS_INT_EQUAL(tc,
NGAP_ProcedureCode_id_PDUSessionResourceRelease,
test_ue->ngap_procedure_code);
/* Send PDUSessionResourceReleaseResponse */
sendbuf = testngap_build_pdu_session_resource_release_response(sess);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
/* Send UplinkNASTransport +
* UL NAS trasnport +
* PDU session resource release complete */
sess->ul_nas_transport_param.request_type = 0;
sess->ul_nas_transport_param.dnn = 0;
sess->ul_nas_transport_param.s_nssai = 0;
sess->pdu_session_establishment_param.ssc_mode = 0;
sess->pdu_session_establishment_param.epco = 0;
gsmbuf = testgsm_build_pdu_session_release_complete(sess);
ABTS_PTR_NOTNULL(tc, gsmbuf);
gmmbuf = testgmm_build_ul_nas_transport(sess,
OGS_NAS_PAYLOAD_CONTAINER_N1_SM_INFORMATION, gsmbuf);
ABTS_PTR_NOTNULL(tc, gmmbuf);
sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf);
ABTS_PTR_NOTNULL(tc, sendbuf);
rv = testgnb_ngap_send(ngap, sendbuf);
ABTS_INT_EQUAL(tc, OGS_OK, rv);
#endif
/* Wait for PDU session resource modify complete */
ogs_msleep(100);
#if !ENABLE_PCF_INITIATED_SESSION_RELEASE
/* Test Bearer Remove */
test_bearer_remove(qos_flow);
@ -431,6 +472,7 @@ static void test1_func(abts_case *tc, void *data)
/* Wait for PDU session resource modify complete */
ogs_msleep(100);
#endif
/* Send UEContextReleaseRequest */
sendbuf = testngap_build_ue_context_release_request(test_ue,