From 94cf8ee0e0d2047420708a0a559d145f2c9acbb0 Mon Sep 17 00:00:00 2001 From: Sukchan Lee Date: Mon, 30 Jun 2025 10:07:06 +0900 Subject: [PATCH] [HR] Implement handling of UE-initiated PDU Session Modification (#2194) This commit consolidates the entire ue-mod feature branch into a single update on top of the latest home-routed code. --- configs/examples/gnb-001-01-ue-001-01.yaml.in | 2 +- .../examples/gnb-001-01-ue-315-010.yaml.in | 2 +- configs/examples/gnb-001-01-ue-999-70.yaml.in | 2 +- .../examples/gnb-315-010-ue-001-01.yaml.in | 2 +- .../examples/gnb-315-010-ue-315-010.yaml.in | 2 +- .../examples/gnb-315-010-ue-999-70.yaml.in | 2 +- configs/examples/gnb-999-70-ue-001-01.yaml.in | 2 +- .../examples/gnb-999-70-ue-315-010.yaml.in | 2 +- configs/examples/gnb-999-70-ue-999-70.yaml.in | 2 +- lib/pfcp/xact.h | 31 +- src/amf/npcf-build.c | 9 +- src/amf/nsmf-build.c | 27 +- src/nssf/nnssf-handler.c | 10 +- src/smf/context.c | 8 +- src/smf/context.h | 108 +- src/smf/gsm-build.c | 87 +- src/smf/gsm-handler.c | 446 +++--- src/smf/gsm-handler.h | 9 + src/smf/gsm-sm.c | 1313 ++++++++++++++--- src/smf/n4-handler.c | 365 ++++- src/smf/namf-handler.c | 35 +- src/smf/ngap-build.c | 53 +- src/smf/ngap-handler.c | 81 +- src/smf/npcf-build.c | 18 +- src/smf/nsmf-build.c | 261 ++-- src/smf/nsmf-handler.c | 682 ++++++--- src/smf/sbi-path.c | 28 +- src/smf/sbi-path.h | 83 ++ src/smf/smf-sm.c | 81 + tests/common/context.c | 11 +- tests/vonr/af-test.c | 28 + tests/vonr/session-test.c | 67 +- 32 files changed, 2989 insertions(+), 870 deletions(-) diff --git a/configs/examples/gnb-001-01-ue-001-01.yaml.in b/configs/examples/gnb-001-01-ue-001-01.yaml.in index c2f325c12..f06df82b2 100644 --- a/configs/examples/gnb-001-01-ue-001-01.yaml.in +++ b/configs/examples/gnb-001-01-ue-001-01.yaml.in @@ -332,7 +332,7 @@ af: port: 7777 client: scp: - - uri: http://127.0.0.200:7777 + - uri: http://127.0.2.200:7777 udr: sbi: diff --git a/configs/examples/gnb-001-01-ue-315-010.yaml.in b/configs/examples/gnb-001-01-ue-315-010.yaml.in index c9b53cc0e..30161fc37 100644 --- a/configs/examples/gnb-001-01-ue-315-010.yaml.in +++ b/configs/examples/gnb-001-01-ue-315-010.yaml.in @@ -332,7 +332,7 @@ af: port: 7777 client: scp: - - uri: http://127.0.0.200:7777 + - uri: http://127.0.3.200:7777 udr: sbi: diff --git a/configs/examples/gnb-001-01-ue-999-70.yaml.in b/configs/examples/gnb-001-01-ue-999-70.yaml.in index 8e6c5cf37..d2a77e6fb 100644 --- a/configs/examples/gnb-001-01-ue-999-70.yaml.in +++ b/configs/examples/gnb-001-01-ue-999-70.yaml.in @@ -327,7 +327,7 @@ af: port: 7777 client: scp: - - uri: http://127.0.0.200:7777 + - uri: http://127.0.1.200:7777 udr: sbi: diff --git a/configs/examples/gnb-315-010-ue-001-01.yaml.in b/configs/examples/gnb-315-010-ue-001-01.yaml.in index d5381e78f..9da20f4b7 100644 --- a/configs/examples/gnb-315-010-ue-001-01.yaml.in +++ b/configs/examples/gnb-315-010-ue-001-01.yaml.in @@ -332,7 +332,7 @@ af: port: 7777 client: scp: - - uri: http://127.0.0.200:7777 + - uri: http://127.0.2.200:7777 udr: sbi: diff --git a/configs/examples/gnb-315-010-ue-315-010.yaml.in b/configs/examples/gnb-315-010-ue-315-010.yaml.in index 868178bb9..369483df1 100644 --- a/configs/examples/gnb-315-010-ue-315-010.yaml.in +++ b/configs/examples/gnb-315-010-ue-315-010.yaml.in @@ -327,7 +327,7 @@ af: port: 7777 client: scp: - - uri: http://127.0.0.200:7777 + - uri: http://127.0.3.200:7777 udr: sbi: diff --git a/configs/examples/gnb-315-010-ue-999-70.yaml.in b/configs/examples/gnb-315-010-ue-999-70.yaml.in index 777417288..9fb664b2a 100644 --- a/configs/examples/gnb-315-010-ue-999-70.yaml.in +++ b/configs/examples/gnb-315-010-ue-999-70.yaml.in @@ -332,7 +332,7 @@ af: port: 7777 client: scp: - - uri: http://127.0.0.200:7777 + - uri: http://127.0.1.200:7777 udr: sbi: diff --git a/configs/examples/gnb-999-70-ue-001-01.yaml.in b/configs/examples/gnb-999-70-ue-001-01.yaml.in index 58ef19393..d068e11bf 100644 --- a/configs/examples/gnb-999-70-ue-001-01.yaml.in +++ b/configs/examples/gnb-999-70-ue-001-01.yaml.in @@ -332,7 +332,7 @@ af: port: 7777 client: scp: - - uri: http://127.0.0.200:7777 + - uri: http://127.0.2.200:7777 udr: sbi: diff --git a/configs/examples/gnb-999-70-ue-315-010.yaml.in b/configs/examples/gnb-999-70-ue-315-010.yaml.in index a3517ca28..7b381417e 100644 --- a/configs/examples/gnb-999-70-ue-315-010.yaml.in +++ b/configs/examples/gnb-999-70-ue-315-010.yaml.in @@ -332,7 +332,7 @@ af: port: 7777 client: scp: - - uri: http://127.0.0.200:7777 + - uri: http://127.0.3.200:7777 udr: sbi: diff --git a/configs/examples/gnb-999-70-ue-999-70.yaml.in b/configs/examples/gnb-999-70-ue-999-70.yaml.in index c4538d6f3..8a23185fb 100644 --- a/configs/examples/gnb-999-70-ue-999-70.yaml.in +++ b/configs/examples/gnb-999-70-ue-999-70.yaml.in @@ -327,7 +327,7 @@ af: port: 7777 client: scp: - - uri: http://127.0.0.200:7777 + - uri: http://127.0.1.200:7777 udr: sbi: diff --git a/lib/pfcp/xact.h b/lib/pfcp/xact.h index d41aedf96..bbdb879d2 100644 --- a/lib/pfcp/xact.h +++ b/lib/pfcp/xact.h @@ -102,21 +102,22 @@ typedef struct ogs_pfcp_xact_s { #define OGS_PFCP_MODIFY_QOS_DELETE ((uint64_t)1<<16) #define OGS_PFCP_MODIFY_OUTER_HEADER_REMOVAL ((uint64_t)1<<17) #define OGS_PFCP_MODIFY_ACTIVATE ((uint64_t)1<<18) -#define OGS_PFCP_MODIFY_DEACTIVATE ((uint64_t)1<<19) -#define OGS_PFCP_MODIFY_END_MARKER ((uint64_t)1<<20) -#define OGS_PFCP_MODIFY_ERROR_INDICATION ((uint64_t)1<<21) -#define OGS_PFCP_MODIFY_XN_HANDOVER ((uint64_t)1<<22) -#define OGS_PFCP_MODIFY_N2_HANDOVER ((uint64_t)1<<23) -#define OGS_PFCP_MODIFY_HANDOVER_CANCEL ((uint64_t)1<<24) -#define OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING ((uint64_t)1<<25) -#define OGS_PFCP_MODIFY_URR ((uint64_t)1<<26) /* type of trigger */ -#define OGS_PFCP_MODIFY_URR_MEAS_METHOD ((uint64_t)1<<27) -#define OGS_PFCP_MODIFY_URR_REPORT_TRIGGER ((uint64_t)1<<28) -#define OGS_PFCP_MODIFY_URR_QUOTA_VALIDITY_TIME ((uint64_t)1<<29) -#define OGS_PFCP_MODIFY_URR_VOLUME_QUOTA ((uint64_t)1<<30) -#define OGS_PFCP_MODIFY_URR_TIME_QUOTA ((uint64_t)1<<31) -#define OGS_PFCP_MODIFY_URR_VOLUME_THRESH ((uint64_t)1<<32) -#define OGS_PFCP_MODIFY_URR_TIME_THRESH ((uint64_t)1<<33) +#define OGS_PFCP_MODIFY_FROM_ACTIVATING ((uint64_t)1<<19) +#define OGS_PFCP_MODIFY_DEACTIVATE ((uint64_t)1<<20) +#define OGS_PFCP_MODIFY_END_MARKER ((uint64_t)1<<21) +#define OGS_PFCP_MODIFY_ERROR_INDICATION ((uint64_t)1<<22) +#define OGS_PFCP_MODIFY_XN_HANDOVER ((uint64_t)1<<23) +#define OGS_PFCP_MODIFY_N2_HANDOVER ((uint64_t)1<<24) +#define OGS_PFCP_MODIFY_HANDOVER_CANCEL ((uint64_t)1<<25) +#define OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING ((uint64_t)1<<26) +#define OGS_PFCP_MODIFY_URR ((uint64_t)1<<27) /* type of trigger */ +#define OGS_PFCP_MODIFY_URR_MEAS_METHOD ((uint64_t)1<<28) +#define OGS_PFCP_MODIFY_URR_REPORT_TRIGGER ((uint64_t)1<<29) +#define OGS_PFCP_MODIFY_URR_QUOTA_VALIDITY_TIME ((uint64_t)1<<30) +#define OGS_PFCP_MODIFY_URR_VOLUME_QUOTA ((uint64_t)1<<31) +#define OGS_PFCP_MODIFY_URR_TIME_QUOTA ((uint64_t)1<<32) +#define OGS_PFCP_MODIFY_URR_VOLUME_THRESH ((uint64_t)1<<33) +#define OGS_PFCP_MODIFY_URR_TIME_THRESH ((uint64_t)1<<34) uint64_t modify_flags; #define OGS_PFCP_DELETE_TRIGGER_LOCAL_INITIATED 1 diff --git a/src/amf/npcf-build.c b/src/amf/npcf-build.c index 366828f8d..ede3d4026 100644 --- a/src/amf/npcf-build.c +++ b/src/amf/npcf-build.c @@ -92,12 +92,9 @@ ogs_sbi_request_t *amf_npcf_am_policy_control_build_create( 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 (amf_ue->ue_location_timestamp) + ueLocation.nr_location->ue_location_timestamp = + ogs_sbi_gmtime_string(amf_ue->ue_location_timestamp); PolicyAssociationRequest.user_loc = &ueLocation; PolicyAssociationRequest.time_zone = diff --git a/src/amf/nsmf-build.c b/src/amf/nsmf-build.c index 9e4bcd568..fec193e7e 100644 --- a/src/amf/nsmf-build.c +++ b/src/amf/nsmf-build.c @@ -160,12 +160,9 @@ ogs_sbi_request_t *amf_nsmf_pdusession_build_create_sm_context( 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 ue_location_timestamp"); - goto end; - } + if (amf_ue->ue_location_timestamp) + ueLocation.nr_location->ue_location_timestamp = + ogs_sbi_gmtime_string(amf_ue->ue_location_timestamp); SmContextCreateData.ue_location = &ueLocation; SmContextCreateData.ue_time_zone = ogs_sbi_timezone_string(ogs_timezone()); @@ -391,12 +388,9 @@ ogs_sbi_request_t *amf_nsmf_pdusession_build_update_sm_context( 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 (amf_ue->ue_location_timestamp) + ueLocation.nr_location->ue_location_timestamp = + ogs_sbi_gmtime_string(amf_ue->ue_location_timestamp); SmContextUpdateData.ue_location = &ueLocation; } @@ -490,12 +484,9 @@ ogs_sbi_request_t *amf_nsmf_pdusession_build_release_sm_context( 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 (amf_ue->ue_location_timestamp) + ueLocation.nr_location->ue_location_timestamp = + ogs_sbi_gmtime_string(amf_ue->ue_location_timestamp); SmContextReleaseData.ue_location = &ueLocation; } diff --git a/src/nssf/nnssf-handler.c b/src/nssf/nnssf-handler.c index a2f60c49d..030a8b3e3 100644 --- a/src/nssf/nnssf-handler.c +++ b/src/nssf/nnssf-handler.c @@ -90,10 +90,6 @@ bool nssf_nnrf_nsselection_handle_get_from_amf_or_vnssf( nssf_home_t *home = nssf_home_find( &recvmsg->param.home_plmn_id, &recvmsg->param.home_snssai); if (!home) { - int r, i; - nssf_nnssf_nsselection_param_t param; - ogs_sbi_discovery_option_t *h_discovery_option = NULL; - home = nssf_home_add( &recvmsg->param.home_plmn_id, &recvmsg->param.home_snssai); if (!home) { @@ -105,6 +101,12 @@ bool nssf_nnrf_nsselection_handle_get_from_amf_or_vnssf( recvmsg->param.home_snssai.sd.v); goto cleanup; } + } + + if (!home->nrf_id || !home->nsi_id) { + int r, i; + nssf_nnssf_nsselection_param_t param; + ogs_sbi_discovery_option_t *h_discovery_option = NULL; h_discovery_option = ogs_sbi_discovery_option_new(); ogs_assert(h_discovery_option); diff --git a/src/smf/context.c b/src/smf/context.c index 871007cd6..165ab8c88 100644 --- a/src/smf/context.c +++ b/src/smf/context.c @@ -1867,8 +1867,8 @@ 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); + if (sess->n1SmBufFromUe) + ogs_pkbuf_free(sess->n1SmBufFromUe); OGS_NAS_CLEAR_DATA(&sess->h_smf_extended_protocol_configuration_options); sess->h_smf_gsm_cause = 0; @@ -1876,6 +1876,10 @@ void smf_sess_remove(smf_sess_t *sess) CLEAR_QOS_FLOWS_SETUP_LIST(sess->h_smf_qos_flows_setup_list); CLEAR_QOS_FLOWS_ADD_MOD_REQUEST_LIST( sess->h_smf_qos_flows_add_mod_request_list); + CLEAR_QOS_FLOWS_REL_REQUEST_LIST(sess->h_smf_qos_flows_rel_request_list); + + if (sess->pending_modification_xact) + ogs_sbi_xact_remove(sess->pending_modification_xact); /* Free SBI object memory */ ogs_sbi_object_free(&sess->sbi); diff --git a/src/smf/context.h b/src/smf/context.h index bb7b47225..e7b825f35 100644 --- a/src/smf/context.h +++ b/src/smf/context.h @@ -78,14 +78,25 @@ typedef struct smf_nsmf_pdusession_param_s { int gsm_cause; struct { - ED3(uint8_t ue_location:1;, + ED4(uint8_t serving_network:1;, + uint8_t ue_location:1;, uint8_t ue_timezone:1;, - uint8_t spare:6;) + uint8_t spare:4;) }; + uint32_t dl_teid; + ogs_ip_t dl_ip; + + OpenAPI_access_type_e an_type; + OpenAPI_rat_type_e rat_type; + + OpenAPI_up_cnx_state_e up_cnx_state; + #define QOS_RULE_CODE_FROM_PFCP_FLAGS(pfcp_flags) \ (pfcp_flags & OGS_PFCP_MODIFY_CREATE) ? \ OGS_NAS_QOS_CODE_CREATE_NEW_QOS_RULE : \ + (pfcp_flags & OGS_PFCP_MODIFY_REMOVE) ? \ + OGS_NAS_QOS_CODE_DELETE_EXISTING_QOS_RULE : \ (pfcp_flags & OGS_PFCP_MODIFY_TFT_NEW) ? \ OGS_NAS_QOS_CODE_CREATE_NEW_QOS_RULE : \ (pfcp_flags & OGS_PFCP_MODIFY_TFT_ADD) ? \ @@ -98,46 +109,16 @@ typedef struct smf_nsmf_pdusession_param_s { #define QOS_RULE_FLOW_DESCRIPTION_CODE_FROM_PFCP_FLAGS(pfcp_flags) \ (pfcp_flags & OGS_PFCP_MODIFY_CREATE) ? \ OGS_NAS_CREATE_NEW_QOS_FLOW_DESCRIPTION : \ + (pfcp_flags & OGS_PFCP_MODIFY_REMOVE) ? \ + OGS_NAS_DELETE_NEW_QOS_FLOW_DESCRIPTION : \ (pfcp_flags & OGS_PFCP_MODIFY_QOS_MODIFY) ? \ OGS_NAS_MODIFY_NEW_QOS_FLOW_DESCRIPTION : 0 uint8_t qos_flow_description_code; + + uint64_t pfcp_flags; + } 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 */ @@ -502,6 +483,7 @@ typedef struct smf_sess_s { OpenAPI_qos_flow_setup_item_free(qosFlowSetupItem); \ } \ OpenAPI_list_free((__lIST)); \ + (__lIST) = NULL; \ } while(0) OpenAPI_list_t *h_smf_qos_flows_setup_list; #define CLEAR_QOS_FLOWS_ADD_MOD_REQUEST_LIST(__lIST) \ @@ -515,8 +497,23 @@ typedef struct smf_sess_s { qosFlowAddModifyRequestItem); \ } \ OpenAPI_list_free((__lIST)); \ + (__lIST) = NULL; \ } while(0) OpenAPI_list_t *h_smf_qos_flows_add_mod_request_list; +#define CLEAR_QOS_FLOWS_REL_REQUEST_LIST(__lIST) \ + do { \ + OpenAPI_lnode_t *node = NULL; \ + OpenAPI_list_for_each((__lIST), node) { \ + OpenAPI_qos_flow_release_request_item_t \ + *qosFlowReleaseRequestItem = node->data; \ + if (qosFlowReleaseRequestItem) \ + OpenAPI_qos_flow_release_request_item_free( \ + qosFlowReleaseRequestItem); \ + } \ + OpenAPI_list_free((__lIST)); \ + (__lIST) = NULL; \ + } while(0) + OpenAPI_list_t *h_smf_qos_flows_rel_request_list; #define HOME_ROUTED_ROAMING_IN_HSMF(__sESS) \ ((__sESS) && (__sESS)->vsmf_pdu_session_uri) @@ -529,7 +526,7 @@ typedef struct smf_sess_s { * Keeps the n1SmMsg Content (n1smbuf) in the context of the V-SMF * for use when creating the n1SmBufFromUe to send to the H-SMF. */ - ogs_pkbuf_t *n1smbuf; + ogs_pkbuf_t *n1SmBufFromUe; /* PCF ID */ char *pcf_id; @@ -677,14 +674,43 @@ typedef struct smf_sess_s { ogs_pool_id_t smf_ue_id; + OpenAPI_resource_status_e resource_status; bool n1_released; bool n2_released; - ogs_pool_id_t amf_update_request_stream_id; - ogs_pool_id_t n1_n2_modified_stream_id; - ogs_pool_id_t n1_n2_released_stream_id; +/* + * Section 4.3.3.3 'UE or network requested PDU Session Modification + * (home-routed roaming)' + * - Step 1a: Nsmf_PDUSession_UpdateSMContext Request (AMF -> V-SMF): + * - Step 4a: Nsmf_PDUSession_UpdateSMContext Response (V-SMF -> AMF): + */ + ogs_pool_id_t amf_to_vsmf_modify_stream_id; +/* + * Section 4.3.3.3 'UE or network requested PDU Session Modification + * (home-routed roaming)' + * - Step 3: Nsmf_PDUSession_UpdateSMContext Request (V-SMF -> H-SMF): + * - Step 15: Nsmf_PDUSession_UpdateSMContext Response (V-SMF -> H-SMF): + */ + ogs_pool_id_t vsmf_to_hsmf_modify_stream_id; +/* + * Section 4.3.4.3 'UE or network requested PDU Session Release for + * Home-routed Roaming' + * - Step 1a: Nsmf_PDUSession_UpdateSMContext Request (AMF -> V-SMF): + * - Step 5b: Nsmf_PDUSession_UpdateSMContext Response (V-SMF -> AMF): + */ + ogs_pool_id_t amf_to_vsmf_release_stream_id; +/* + * Section 4.3.4.3 'UE or network requested PDU Session Release for + * Home-routed Roaming' + * - Step 3a: Nsmf_PDUSession_UpdateSMContext Request (V-SMF -> H-SMF): + * - Step 14: Nsmf_PDUSession_UpdateSMContext Response (V-SMF -> H-SMF): + */ + ogs_pool_id_t vsmf_to_hsmf_release_stream_id; smf_nsmf_pdusession_param_t nsmf_param; + bool establishment_accept_sent; + ogs_sbi_xact_t *pending_modification_xact; + } smf_sess_t; void smf_context_init(void); diff --git a/src/smf/gsm-build.c b/src/smf/gsm-build.c index 57aafe680..bae41cce8 100644 --- a/src/smf/gsm-build.c +++ b/src/smf/gsm-build.c @@ -270,6 +270,8 @@ ogs_pkbuf_t *gsm_build_pdu_session_establishment_accept(smf_sess_t *sess) pkbuf = ogs_nas_5gs_plain_encode(&message); ogs_assert(pkbuf); + sess->establishment_accept_sent = true; + cleanup: if (authorized_qos_rules->buffer) ogs_free(authorized_qos_rules->buffer); @@ -326,8 +328,8 @@ ogs_pkbuf_t *gsm_build_pdu_session_modification_command( ogs_nas_qos_flow_description_t qos_flow_description[OGS_NAS_MAX_NUM_OF_QOS_FLOW_DESCRIPTION]; - OpenAPI_qos_flow_add_modify_request_item_t *qosFlowAddModifyRequestItem = - NULL; + OpenAPI_qos_flow_add_modify_request_item_t *qosFlowAddModRequestItem = NULL; + OpenAPI_qos_flow_release_request_item_t *qosFlowRelRequestItem = NULL; OpenAPI_lnode_t *node = NULL; int num, len; @@ -352,24 +354,44 @@ ogs_pkbuf_t *gsm_build_pdu_session_modification_command( num = 0; OpenAPI_list_for_each( sess->h_smf_qos_flows_add_mod_request_list, node) { - qosFlowAddModifyRequestItem = node->data; - if (qosFlowAddModifyRequestItem) { + qosFlowAddModRequestItem = node->data; + if (qosFlowAddModRequestItem && + qosFlowAddModRequestItem->qos_rules) { ogs_nas_qos_rules_t qos_rules; len = ogs_base64_decode_len( - qosFlowAddModifyRequestItem->qos_rules); + qosFlowAddModRequestItem->qos_rules); ogs_assert(len); qos_rules.buffer = ogs_calloc(1, len); ogs_assert(qos_rules.buffer); - qos_rules.length = - ogs_base64_decode_binary( - qos_rules.buffer, - qosFlowAddModifyRequestItem->qos_rules); + qos_rules.length = ogs_base64_decode_binary( + qos_rules.buffer, qosFlowAddModRequestItem->qos_rules); ogs_assert(qos_rules.length); ogs_assert(1 == - ogs_nas_parse_qos_rules( - &qos_rule[num], &qos_rules)); + ogs_nas_parse_qos_rules(&qos_rule[num], &qos_rules)); + + ogs_free(qos_rules.buffer); + + num++; + } + } + + OpenAPI_list_for_each(sess->h_smf_qos_flows_rel_request_list, node) { + qosFlowRelRequestItem = node->data; + if (qosFlowRelRequestItem && qosFlowRelRequestItem->qos_rules) { + ogs_nas_qos_rules_t qos_rules; + + len = ogs_base64_decode_len(qosFlowRelRequestItem->qos_rules); + ogs_assert(len); + qos_rules.buffer = ogs_calloc(1, len); + ogs_assert(qos_rules.buffer); + qos_rules.length = ogs_base64_decode_binary( + qos_rules.buffer, qosFlowRelRequestItem->qos_rules); + ogs_assert(qos_rules.length); + + ogs_assert(1 == + ogs_nas_parse_qos_rules(&qos_rule[num], &qos_rules)); ogs_free(qos_rules.buffer); @@ -419,24 +441,53 @@ ogs_pkbuf_t *gsm_build_pdu_session_modification_command( num = 0; OpenAPI_list_for_each( sess->h_smf_qos_flows_add_mod_request_list, node) { - qosFlowAddModifyRequestItem = node->data; - if (qosFlowAddModifyRequestItem) { + qosFlowAddModRequestItem = node->data; + if (qosFlowAddModRequestItem && + qosFlowAddModRequestItem->qos_flow_description) { ogs_nas_qos_flow_descriptions_t qos_flow_descriptions; len = ogs_base64_decode_len( - qosFlowAddModifyRequestItem->qos_flow_description); + qosFlowAddModRequestItem->qos_flow_description); ogs_assert(len); qos_flow_descriptions.buffer = ogs_calloc(1, len); ogs_assert(qos_flow_descriptions.buffer); - qos_flow_descriptions.length = - ogs_base64_decode_binary( + qos_flow_descriptions.length = ogs_base64_decode_binary( qos_flow_descriptions.buffer, - qosFlowAddModifyRequestItem->qos_flow_description); + qosFlowAddModRequestItem->qos_flow_description); ogs_assert(qos_flow_descriptions.length); ogs_assert(1 == ogs_nas_parse_qos_flow_descriptions( - &qos_flow_description[num], &qos_flow_descriptions)); + &qos_flow_description[num], + &qos_flow_descriptions)); + + ogs_free(qos_flow_descriptions.buffer); + + num++; + } + } + + OpenAPI_list_for_each( + sess->h_smf_qos_flows_rel_request_list, node) { + qosFlowRelRequestItem = node->data; + if (qosFlowRelRequestItem && + qosFlowRelRequestItem->qos_flow_description) { + ogs_nas_qos_flow_descriptions_t qos_flow_descriptions; + + len = ogs_base64_decode_len( + qosFlowRelRequestItem->qos_flow_description); + ogs_assert(len); + qos_flow_descriptions.buffer = ogs_calloc(1, len); + ogs_assert(qos_flow_descriptions.buffer); + qos_flow_descriptions.length = ogs_base64_decode_binary( + qos_flow_descriptions.buffer, + qosFlowRelRequestItem->qos_flow_description); + ogs_assert(qos_flow_descriptions.length); + + ogs_assert(1 == + ogs_nas_parse_qos_flow_descriptions( + &qos_flow_description[num], + &qos_flow_descriptions)); ogs_free(qos_flow_descriptions.buffer); diff --git a/src/smf/gsm-handler.c b/src/smf/gsm-handler.c index d8e3a737f..aaf623bc7 100644 --- a/src/smf/gsm-handler.c +++ b/src/smf/gsm-handler.c @@ -211,22 +211,250 @@ static int reconfigure_packet_filter( } \ } while(0); +int gsm_handle_pdu_session_modification_qos_rules( + smf_sess_t *sess, + ogs_nas_qos_rules_t *requested_qos_rules, + uint64_t *pfcp_flags) +{ + smf_ue_t *smf_ue = NULL; + int i, j, num_of_rule = 0; + + ogs_nas_qos_rule_t qos_rule[OGS_NAS_MAX_NUM_OF_QOS_RULE]; + + ogs_assert(requested_qos_rules); + ogs_assert(pfcp_flags); + + ogs_assert(sess); + smf_ue = smf_ue_find_by_id(sess->smf_ue_id); + ogs_assert(smf_ue); + + num_of_rule = ogs_nas_parse_qos_rules(qos_rule, requested_qos_rules); + if (!num_of_rule) { + ogs_error("[%s:%d] Invalid modification request", + smf_ue->supi, sess->psi); + return OGS_ERROR; + } + + for (i = 0; i < num_of_rule; i++) { + smf_pf_t *pf = NULL; + smf_bearer_t *qos_flow = + smf_qos_flow_find_by_qfi(sess, qos_rule[i].identifier); + if (!qos_flow) { + ogs_error("No Qos Flow"); + continue; + } + + ogs_list_init(&qos_flow->pf_to_add_list); + + if (qos_rule[i].code == OGS_NAS_QOS_CODE_DELETE_EXISTING_QOS_RULE) { + smf_pf_remove_all(qos_flow); + + *pfcp_flags |= OGS_PFCP_MODIFY_REMOVE; + qos_flow_find_or_add(&sess->qos_flow_to_modify_list, + qos_flow, to_modify_node); + } else if (qos_rule[i].code == + OGS_NAS_QOS_CODE_CREATE_NEW_QOS_RULE || + qos_rule[i].code == + OGS_NAS_QOS_CODE_MODIFY_EXISTING_QOS_RULE_AND_ADD_PACKET_FILTERS || + qos_rule[i].code == + OGS_NAS_QOS_CODE_MODIFY_EXISTING_QOS_RULE_AND_REPLACE_ALL_PACKET_FILTERS) { + + if (qos_rule[i].code == OGS_NAS_QOS_CODE_CREATE_NEW_QOS_RULE || + qos_rule[i].code == OGS_NAS_QOS_CODE_MODIFY_EXISTING_QOS_RULE_AND_REPLACE_ALL_PACKET_FILTERS) + smf_pf_remove_all(qos_flow); + + for (j = 0; j < qos_rule[i].num_of_packet_filter && + j < OGS_MAX_NUM_OF_FLOW_IN_NAS; j++) { + + pf = smf_pf_add(qos_flow); + ogs_assert(pf); + + ogs_assert( + reconfigure_packet_filter(pf, &qos_rule[i], i) > 0); +/* + * Refer to lib/ipfw/ogs-ipfw.h + * Issue #338 + * + * + * TFT : Local REMOTE + * --> + * RULE : Source Destination + * + * + * TFT : Local REMOTE + * --> + * RULE : Source Destination + */ + if (pf->direction == OGS_FLOW_DOWNLINK_ONLY) + ogs_ipfw_rule_swap(&pf->ipfw_rule); + + if (pf->flow_description) + ogs_free(pf->flow_description); + +/* + * Issue #338 + * + * + * RULE : Source Destination + * --> + * GX : permit out from to + * PFCP : permit out from to + * + * + * RULE : Source Destination + * --> + * GX : permit out from to + * PFCP : permit out from to + */ + if (pf->direction == OGS_FLOW_UPLINK_ONLY) { + ogs_ipfw_rule_t tmp; + ogs_ipfw_copy_and_swap(&tmp, &pf->ipfw_rule); + pf->flow_description = + ogs_ipfw_encode_flow_description(&tmp); + ogs_assert(pf->flow_description); + } else { + pf->flow_description = + ogs_ipfw_encode_flow_description(&pf->ipfw_rule); + ogs_assert(pf->flow_description); + } + + if (qos_rule[i].code == + OGS_NAS_QOS_CODE_CREATE_NEW_QOS_RULE) { + *pfcp_flags |= OGS_PFCP_MODIFY_TFT_NEW; + } else if (qos_rule[i].code == + OGS_NAS_QOS_CODE_MODIFY_EXISTING_QOS_RULE_AND_ADD_PACKET_FILTERS) { + *pfcp_flags |= OGS_PFCP_MODIFY_TFT_ADD; + } else if (qos_rule[i].code == + OGS_NAS_QOS_CODE_MODIFY_EXISTING_QOS_RULE_AND_REPLACE_ALL_PACKET_FILTERS) { + *pfcp_flags |= OGS_PFCP_MODIFY_TFT_REPLACE; + } else + ogs_assert_if_reached(); + + qos_flow_find_or_add(&sess->qos_flow_to_modify_list, + qos_flow, to_modify_node); + + ogs_list_add( + &qos_flow->pf_to_add_list, &pf->to_add_node); + } + } else if (qos_rule[i].code == + OGS_NAS_QOS_CODE_MODIFY_EXISTING_QOS_RULE_AND_DELETE_PACKET_FILTERS) { + + qos_flow->num_of_pf_to_delete = 0; + for (j = 0; j < qos_rule[i].num_of_packet_filter && + j < OGS_MAX_NUM_OF_FLOW_IN_NAS; j++) { + + pf = smf_pf_find_by_identifier( + qos_flow, qos_rule[i].pf[j].identifier); + if (pf) { + qos_flow->pf_to_delete + [qos_flow->num_of_pf_to_delete++] = + qos_rule[i].pf[j].identifier; + smf_pf_remove(pf); + } + } + + if (ogs_list_count(&qos_flow->pf_list)) { + *pfcp_flags |= OGS_PFCP_MODIFY_TFT_DELETE; + qos_flow_find_or_add(&sess->qos_flow_to_modify_list, + qos_flow, to_modify_node); + } else { + *pfcp_flags |= OGS_PFCP_MODIFY_REMOVE; + qos_flow_find_or_add(&sess->qos_flow_to_modify_list, + qos_flow, to_modify_node); + } + } + + if (*pfcp_flags & + (OGS_PFCP_MODIFY_TFT_NEW|OGS_PFCP_MODIFY_TFT_ADD| + OGS_PFCP_MODIFY_TFT_REPLACE|OGS_PFCP_MODIFY_TFT_DELETE)) + smf_bearer_tft_update(qos_flow); + } + + return OGS_OK; +} + +int gsm_handle_pdu_session_modification_qos_flow_descriptions( + smf_sess_t *sess, + ogs_nas_qos_flow_descriptions_t *requested_qos_flow_descriptions, + uint64_t *pfcp_flags) +{ + smf_ue_t *smf_ue = NULL; + int i, j, num_of_description = 0; + + ogs_nas_qos_flow_description_t + qos_flow_description[OGS_NAS_MAX_NUM_OF_QOS_FLOW_DESCRIPTION]; + + ogs_assert(requested_qos_flow_descriptions); + ogs_assert(pfcp_flags); + + ogs_assert(sess); + smf_ue = smf_ue_find_by_id(sess->smf_ue_id); + ogs_assert(smf_ue); + + num_of_description = ogs_nas_parse_qos_flow_descriptions( + qos_flow_description, requested_qos_flow_descriptions); + if (!num_of_description) { + ogs_error("[%s:%d] Invalid modification request", + smf_ue->supi, sess->psi); + return OGS_ERROR; + } + + for (i = 0; i < num_of_description; i++) { + smf_bearer_t *qos_flow = + smf_qos_flow_find_by_qfi( + sess, qos_flow_description[i].identifier); + if (!qos_flow) { + ogs_error("No Qos Flow"); + continue; + } + + for (j = 0; j < qos_flow_description[i].num_of_parameter; j++) { + switch(qos_flow_description[i].param[j].identifier) { + case OGS_NAX_QOS_FLOW_PARAMETER_ID_5QI: + /* Nothing */ + break; + case OGS_NAX_QOS_FLOW_PARAMETER_ID_GFBR_UPLINK: + qos_flow->qos.gbr.uplink = ogs_nas_bitrate_to_uint64( + &qos_flow_description[i].param[j].br); + break; + case OGS_NAX_QOS_FLOW_PARAMETER_ID_GFBR_DOWNLINK: + qos_flow->qos.gbr.downlink = ogs_nas_bitrate_to_uint64( + &qos_flow_description[i].param[j].br); + break; + case OGS_NAX_QOS_FLOW_PARAMETER_ID_MFBR_UPLINK: + qos_flow->qos.mbr.uplink = ogs_nas_bitrate_to_uint64( + &qos_flow_description[i].param[j].br); + break; + case OGS_NAX_QOS_FLOW_PARAMETER_ID_MFBR_DOWNLINK: + qos_flow->qos.mbr.downlink = ogs_nas_bitrate_to_uint64( + &qos_flow_description[i].param[j].br); + break; + default: + ogs_fatal("Unknown qos_flow parameter identifier [%d]", + qos_flow_description[i].param[i].identifier); + ogs_assert_if_reached(); + } + } + + *pfcp_flags |= OGS_PFCP_MODIFY_QOS_MODIFY; + qos_flow_find_or_add(&sess->qos_flow_to_modify_list, + qos_flow, to_modify_node); + + if (*pfcp_flags & OGS_PFCP_MODIFY_QOS_MODIFY) + smf_bearer_qos_update(qos_flow); + } + + return OGS_OK; +} + int gsm_handle_pdu_session_modification_request( smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_nas_5gs_pdu_session_modification_request_t * pdu_session_modification_request) { - int i, j; - + int rv; uint64_t pfcp_flags = 0; - smf_bearer_t *qos_flow = NULL; - smf_pf_t *pf = NULL; - - ogs_nas_qos_rule_t qos_rule[OGS_NAS_MAX_NUM_OF_QOS_RULE]; - ogs_nas_qos_flow_description_t - qos_flow_description[OGS_NAS_MAX_NUM_OF_QOS_FLOW_DESCRIPTION]; - ogs_nas_qos_rules_t *requested_qos_rules = &pdu_session_modification_request->requested_qos_rules; ogs_nas_qos_flow_descriptions_t *requested_qos_flow_descriptions = @@ -250,197 +478,23 @@ int gsm_handle_pdu_session_modification_request( if (pdu_session_modification_request->presencemask & OGS_NAS_5GS_PDU_SESSION_MODIFICATION_REQUEST_REQUESTED_QOS_RULES_PRESENT) { - int num_of_rule = 0; - - num_of_rule = ogs_nas_parse_qos_rules(qos_rule, requested_qos_rules); - if (!num_of_rule) { - ogs_error("[%s:%d] Invalid modification request", - smf_ue->supi, sess->psi); + rv = gsm_handle_pdu_session_modification_qos_rules( + sess, requested_qos_rules, &pfcp_flags); + if (rv != OGS_OK) { + ogs_error("gsm_handle_pdu_session_modification_qos_rules() failed"); goto cleanup; } - - for (i = 0; i < num_of_rule; i++) { - qos_flow = smf_qos_flow_find_by_qfi( - sess, qos_rule[i].identifier); - if (!qos_flow) { - ogs_error("No Qos Flow"); - continue; - } - - ogs_list_init(&qos_flow->pf_to_add_list); - - if (qos_rule[i].code == OGS_NAS_QOS_CODE_DELETE_EXISTING_QOS_RULE) { - smf_pf_remove_all(qos_flow); - - pfcp_flags |= OGS_PFCP_MODIFY_REMOVE; - qos_flow_find_or_add(&sess->qos_flow_to_modify_list, - qos_flow, to_modify_node); - } else if (qos_rule[i].code == - OGS_NAS_QOS_CODE_CREATE_NEW_QOS_RULE || - qos_rule[i].code == - OGS_NAS_QOS_CODE_MODIFY_EXISTING_QOS_RULE_AND_ADD_PACKET_FILTERS || - qos_rule[i].code == - OGS_NAS_QOS_CODE_MODIFY_EXISTING_QOS_RULE_AND_REPLACE_ALL_PACKET_FILTERS) { - - if (qos_rule[i].code == OGS_NAS_QOS_CODE_CREATE_NEW_QOS_RULE || - qos_rule[i].code == OGS_NAS_QOS_CODE_MODIFY_EXISTING_QOS_RULE_AND_REPLACE_ALL_PACKET_FILTERS) - smf_pf_remove_all(qos_flow); - - for (j = 0; j < qos_rule[i].num_of_packet_filter && - j < OGS_MAX_NUM_OF_FLOW_IN_NAS; j++) { - - pf = smf_pf_add(qos_flow); - ogs_assert(pf); - - ogs_assert( - reconfigure_packet_filter(pf, &qos_rule[i], i) > 0); - /* - * Refer to lib/ipfw/ogs-ipfw.h - * Issue #338 - * - * - * TFT : Local REMOTE - * --> - * RULE : Source Destination - * - * - * TFT : Local REMOTE - * --> - * RULE : Source Destination - */ - if (pf->direction == OGS_FLOW_DOWNLINK_ONLY) - ogs_ipfw_rule_swap(&pf->ipfw_rule); - - if (pf->flow_description) - ogs_free(pf->flow_description); - - /* - * Issue #338 - * - * - * RULE : Source Destination - * --> - * GX : permit out from to - * PFCP : permit out from to - * - * - * RULE : Source Destination - * --> - * GX : permit out from to - * PFCP : permit out from to - */ - if (pf->direction == OGS_FLOW_UPLINK_ONLY) { - ogs_ipfw_rule_t tmp; - ogs_ipfw_copy_and_swap(&tmp, &pf->ipfw_rule); - pf->flow_description = - ogs_ipfw_encode_flow_description(&tmp); - ogs_assert(pf->flow_description); - } else { - pf->flow_description = - ogs_ipfw_encode_flow_description(&pf->ipfw_rule); - ogs_assert(pf->flow_description); - } - - if (qos_rule[i].code == - OGS_NAS_QOS_CODE_CREATE_NEW_QOS_RULE) { - pfcp_flags |= OGS_PFCP_MODIFY_TFT_NEW; - } else if (qos_rule[i].code == - OGS_NAS_QOS_CODE_MODIFY_EXISTING_QOS_RULE_AND_ADD_PACKET_FILTERS) { - pfcp_flags |= OGS_PFCP_MODIFY_TFT_ADD; - } else if (qos_rule[i].code == - OGS_NAS_QOS_CODE_MODIFY_EXISTING_QOS_RULE_AND_REPLACE_ALL_PACKET_FILTERS) { - pfcp_flags |= OGS_PFCP_MODIFY_TFT_REPLACE; - } else - ogs_assert_if_reached(); - - qos_flow_find_or_add(&sess->qos_flow_to_modify_list, - qos_flow, to_modify_node); - - ogs_list_add( - &qos_flow->pf_to_add_list, &pf->to_add_node); - } - } else if (qos_rule[i].code == - OGS_NAS_QOS_CODE_MODIFY_EXISTING_QOS_RULE_AND_DELETE_PACKET_FILTERS) { - - qos_flow->num_of_pf_to_delete = 0; - for (j = 0; j < qos_rule[i].num_of_packet_filter && - j < OGS_MAX_NUM_OF_FLOW_IN_NAS; j++) { - - pf = smf_pf_find_by_identifier( - qos_flow, qos_rule[i].pf[j].identifier); - if (pf) { - qos_flow->pf_to_delete - [qos_flow->num_of_pf_to_delete++] = - qos_rule[i].pf[j].identifier; - smf_pf_remove(pf); - } - } - - if (ogs_list_count(&qos_flow->pf_list)) { - pfcp_flags |= OGS_PFCP_MODIFY_TFT_DELETE; - qos_flow_find_or_add(&sess->qos_flow_to_modify_list, - qos_flow, to_modify_node); - } else { - pfcp_flags |= OGS_PFCP_MODIFY_REMOVE; - qos_flow_find_or_add(&sess->qos_flow_to_modify_list, - qos_flow, to_modify_node); - } - } - } } if (pdu_session_modification_request->presencemask & OGS_NAS_5GS_PDU_SESSION_MODIFICATION_REQUEST_REQUESTED_QOS_FLOW_DESCRIPTIONS_PRESENT) { - int num_of_description = 0; - - num_of_description = ogs_nas_parse_qos_flow_descriptions( - qos_flow_description, requested_qos_flow_descriptions); - if (!num_of_description) { - ogs_error("[%s:%d] Invalid modification request", - smf_ue->supi, sess->psi); + rv = gsm_handle_pdu_session_modification_qos_flow_descriptions( + sess, requested_qos_flow_descriptions, &pfcp_flags); + if (rv != OGS_OK) { + ogs_error("gsm_handle_pdu_session_modification_" + "qos_flow_descriptions() failed"); goto cleanup; } - - for (i = 0; i < num_of_description; i++) { - qos_flow = smf_qos_flow_find_by_qfi( - sess, qos_flow_description[i].identifier); - if (!qos_flow) { - ogs_error("No Qos Flow"); - continue; - } - - for (j = 0; j < qos_flow_description[i].num_of_parameter; j++) { - switch(qos_flow_description[i].param[j].identifier) { - case OGS_NAX_QOS_FLOW_PARAMETER_ID_5QI: - /* Nothing */ - break; - case OGS_NAX_QOS_FLOW_PARAMETER_ID_GFBR_UPLINK: - qos_flow->qos.gbr.uplink = ogs_nas_bitrate_to_uint64( - &qos_flow_description[i].param[j].br); - break; - case OGS_NAX_QOS_FLOW_PARAMETER_ID_GFBR_DOWNLINK: - qos_flow->qos.gbr.downlink = ogs_nas_bitrate_to_uint64( - &qos_flow_description[i].param[j].br); - break; - case OGS_NAX_QOS_FLOW_PARAMETER_ID_MFBR_UPLINK: - qos_flow->qos.mbr.uplink = ogs_nas_bitrate_to_uint64( - &qos_flow_description[i].param[j].br); - break; - case OGS_NAX_QOS_FLOW_PARAMETER_ID_MFBR_DOWNLINK: - qos_flow->qos.mbr.downlink = ogs_nas_bitrate_to_uint64( - &qos_flow_description[i].param[j].br); - break; - default: - ogs_fatal("Unknown qos_flow parameter identifier [%d]", - qos_flow_description[i].param[i].identifier); - ogs_assert_if_reached(); - } - } - - pfcp_flags |= OGS_PFCP_MODIFY_QOS_MODIFY; - qos_flow_find_or_add(&sess->qos_flow_to_modify_list, - qos_flow, to_modify_node); - } } if (ogs_list_count(&sess->qos_flow_to_modify_list) != 1) { @@ -463,14 +517,6 @@ int gsm_handle_pdu_session_modification_request( ogs_assert((pfcp_flags & OGS_PFCP_MODIFY_REMOVE) == 0); - if (pfcp_flags & - (OGS_PFCP_MODIFY_TFT_NEW|OGS_PFCP_MODIFY_TFT_ADD| - OGS_PFCP_MODIFY_TFT_REPLACE|OGS_PFCP_MODIFY_TFT_DELETE)) - smf_bearer_tft_update(qos_flow); - - if (pfcp_flags & OGS_PFCP_MODIFY_QOS_MODIFY) - smf_bearer_qos_update(qos_flow); - } else { ogs_fatal("Unknown PFCP-Flags : [0x%llx]", (long long)pfcp_flags); ogs_assert_if_reached(); diff --git a/src/smf/gsm-handler.h b/src/smf/gsm-handler.h index 15750aa93..5cabbe232 100644 --- a/src/smf/gsm-handler.h +++ b/src/smf/gsm-handler.h @@ -31,6 +31,15 @@ int gsm_handle_pdu_session_establishment_request( ogs_nas_5gs_pdu_session_establishment_request_t * pdu_session_establishment_request); +int gsm_handle_pdu_session_modification_qos_rules( + smf_sess_t *sess, + ogs_nas_qos_rules_t *requested_qos_rules, + uint64_t *pfcp_flags); +int gsm_handle_pdu_session_modification_qos_flow_descriptions( + smf_sess_t *sess, + ogs_nas_qos_flow_descriptions_t *requested_qos_flow_descriptions, + uint64_t *pfcp_flags); + int gsm_handle_pdu_session_modification_request( smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_nas_5gs_pdu_session_modification_request_t * diff --git a/src/smf/gsm-sm.c b/src/smf/gsm-sm.c index 2ffc39e0a..b2d371ba4 100644 --- a/src/smf/gsm-sm.c +++ b/src/smf/gsm-sm.c @@ -876,6 +876,7 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) smf_sess_t *sess = NULL; smf_bearer_t *qos_flow = NULL; ogs_pkbuf_t *pkbuf = NULL; + ogs_pkbuf_t *n2smbuf = NULL; ogs_pfcp_xact_t *pfcp_xact = NULL; ogs_pfcp_message_t *pfcp_message = NULL; @@ -1052,6 +1053,28 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) sess, stream, sbi_message); break; CASE(OGS_SBI_RESOURCE_NAME_RELEASE) + /* + * Network-requested PDU Session Release + * + * 1. V*: smf_nsmf_handle_release_sm_context + * 2. V*: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 3. V*: OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT + * 4. V: smf_nsmf_pdusession_build_release_data + * 5. H: smf_nsmf_handle_release_data_in_hsmf + * 6. H: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT + * 7. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 8. H: ogs_sbi_send_http_status_no_content + * 9. H: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_AMF_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 10. H: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 11. H: SMF_SESS_CLEAR(sess) + * 12. V: smf_nsmf_handle_release_data_in_hsmf + * 13. V: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT + * 14. V: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 15. V*: ogs_sbi_send_http_status_no_content + * 16. V*: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ smf_nsmf_handle_release_sm_context( sess, stream, sbi_message); @@ -1088,30 +1111,211 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) rc = smf_nsmf_handle_update_data_in_hsmf( sess, stream, sbi_message); if (rc == true) { - ogs_assert(sess->nsmf_param.request_indication); + switch (sess->nsmf_param.request_indication) { + case OpenAPI_request_indication_UE_REQ_PDU_SES_MOD: + if (sess->nsmf_param.up_cnx_state == + OpenAPI_up_cnx_state_DEACTIVATED) { + /* + * UE-requested PDU Session Modification(DEACTIVATED) + * + * For Home Routed Roaming, delegate PFCP deactivation to H-SMF by + * sending UP_CNX_STATE=DEACTIVATED via HsmfUpdateData. + * + * 1. V: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 2. V: smf_nsmf_pdusession_build_hsmf_update_data + * SMF_UPDATE_STATE_HR_DEACTIVATED + * 3. H*: smf_nsmf_handle_update_data_in_hsmf + * 4. H*: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 5. H*: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_DL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 6. H: ogs_sbi_send_http_status_no_content + * 7. V: case SMF_UPDATE_STATE_HR_DEACTIVATED: + * 8. V: smf_sbi_send_sm_context_updated_data_up_cnx_state( + * OpenAPI_up_cnx_state_DEACTIVATED) + */ + ogs_assert(OGS_OK == + smf_5gc_pfcp_send_all_pdr_modification_request( + sess, stream, + OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING| + OGS_PFCP_MODIFY_DL_ONLY| + OGS_PFCP_MODIFY_DEACTIVATE, 0, 0)); + } else if (sess->nsmf_param.up_cnx_state == + OpenAPI_up_cnx_state_ACTIVATING) { + /* + * UE-requested PDU Session Modification(ACTIVATING) + * + * 1. V: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 2. V: smf_nsmf_pdusession_build_hsmf_update_data + * SMF_UPDATE_STATE_HR_ACTIVATING + * 3. H*: smf_nsmf_handle_update_data_in_hsmf + * 4. H*: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 5. H*: ogs_sbi_send_http_status_no_content + * 6. V: ngap_build_pdu_session_resource_setup_request_transfer + * 7. V: smf_sbi_send_sm_context_updated_data( + * OpenAPI_up_cnx_state_ACTIVATING, + * OpenAPI_n2_sm_info_type_PDU_RES_SETUP_REQ, n2smbuf) + */ + ogs_assert(true == + ogs_sbi_send_http_status_no_content( + stream)); + } else if (sess->nsmf_param.up_cnx_state == + OpenAPI_up_cnx_state_ACTIVATED) { + /* + * UE-requested PDU Session Modification(ACTIVATED) + * + * 1. V: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 2. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_DL_ONLY| + * OGS_PFCP_MODIFY_OUTER_HEADER_REMOVAL|OGS_PFCP_MODIFY_ACTIVATE + * 3. V: if (sess->up_cnx_state == OpenAPI_up_cnx_state_ACTIVATING) + * pfcp_flags |= OGS_PFCP_MODIFY_FROM_ACTIVATING; + * 4. V: flags & OGS_PFCP_MODIFY_FROM_ACTIVATING ? + * SMF_UPDATE_STATE_HR_ACTIVATED_FROM_ACTIVATING : + * SMF_UPDATE_STATE_HR_ACTIVATED_FROM_NON_ACTIVATING, + * 5. V: smf_nsmf_pdusession_build_hsmf_update_data + * 6. H: smf_nsmf_handle_update_data_in_hsmf + * 7. H: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 8. H*: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_DL_ONLY| + * OGS_PFCP_MODIFY_ACTIVATE + * 9. H: ogs_sbi_send_http_status_no_content + * 10. V: case SMF_UPDATE_STATE_HR_ACTIVATED_FROM_ACTIVATING: + * sess->up_cnx_state = OpenAPI_up_cnx_state_ACTIVATED; + * smf_sbi_send_sm_context_updated_data_up_cnx_state( + * OpenAPI_up_cnx_state_ACTIVATED); + * case SMF_UPDATE_STATE_HR_ACTIVATED_FROM_NON_ACTIVATING: + * ogs_sbi_send_http_status_no_content + */ + bool far_update = false; - if (sess->nsmf_param.request_indication == - OpenAPI_request_indication_NW_REQ_PDU_SES_MOD) { - ogs_fatal("TODO"); + if (memcmp( + &sess->nsmf_param.dl_ip, + &sess->remote_dl_ip, + sizeof(sess->nsmf_param.dl_ip)) != 0 || + sess->nsmf_param.dl_teid != + sess->remote_dl_teid) + far_update = true; - ogs_assert(true == - ogs_sbi_send_http_status_no_content( - stream)); - } else { + ogs_list_for_each( + &sess->bearer_list, qos_flow) { + ogs_pfcp_far_t *dl_far = qos_flow->dl_far; + ogs_assert(dl_far); + + if (dl_far->apply_action != + OGS_PFCP_APPLY_ACTION_FORW) + far_update = true; + + dl_far->apply_action = + OGS_PFCP_APPLY_ACTION_FORW; + ogs_assert(OGS_OK == + ogs_pfcp_ip_to_outer_header_creation( + &sess->remote_dl_ip, + &dl_far->outer_header_creation, + &dl_far->outer_header_creation_len) + ); + dl_far->outer_header_creation.teid = + sess->remote_dl_teid; + } + + if (far_update) { + ogs_assert(OGS_OK == + smf_5gc_pfcp_send_all_pdr_modification_request( + sess, stream, + OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING| + OGS_PFCP_MODIFY_DL_ONLY| + OGS_PFCP_MODIFY_ACTIVATE, 0, 0)); + } else { + ogs_assert(true == + ogs_sbi_send_http_status_no_content( + stream)); + } + } else if (sess->nsmf_param.pfcp_flags) { + ogs_assert(true == + ogs_sbi_send_http_status_no_content( + stream)); + + ogs_assert(OGS_OK == + smf_5gc_pfcp_send_qos_flow_list_modification_request( + sess, NULL, + OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING| + OGS_PFCP_MODIFY_UE_REQUESTED| + sess->nsmf_param.pfcp_flags, 0)); + } else { + ogs_fatal("Invalid [upCnxState:%d]" + "[pfcp_flags:0x%llx]", + sess->nsmf_param.up_cnx_state, + (long long)sess->nsmf_param.pfcp_flags); + ogs_assert_if_reached(); + } + break; + case OpenAPI_request_indication_UE_REQ_PDU_SES_REL: + case OpenAPI_request_indication_NW_REQ_PDU_SES_REL: if (sess->nsmf_param.request_indication == OpenAPI_request_indication_UE_REQ_PDU_SES_REL) { + /* + * UE-requested PDU Session Release + * + * 1. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 2. V: OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 3. V: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 4. V: smf_nsmf_pdusession_build_hsmf_update_data + * 5. H*: smf_nsmf_handle_update_data_in_hsmf + * 6. H*: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 6. H*: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 7. H*: ogs_sbi_send_http_status_no_content + * 8. H*: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 9. H: smf_nsmf_pdusession_build_vsmf_update_data + * 10. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_5gc_n1_n2_release); + * 11. V: smf_nsmf_handle_update_data_in_vsmf + * 12. V: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 13. V: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 14. V: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 15. V: ngap_build_pdu_session_resource_release_command_transfer+ + * gsm_build_pdu_session_release_command + * 16 V: OGS_FSM_TRAN(&sess->sm, smf_gsm_state_wait_5gc_n1_n2_release) + * 17. V: ogs_sbi_send_http_status_no_content(stream) + * 18. V: case OpenAPI_n2_sm_info_type_PDU_RES_REL_RSP: + * case OGS_NAS_5GS_PDU_SESSION_RELEASE_COMPLETE: + * 19. V: ogs_sbi_send_http_status_no_content(vsmf_to_hsmf_release_stream) + * 20. V: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 21. H: case OGS_EVENT_SBI_CLIENT: + * 22. H: CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 23. H: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_N1N2_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 24. H: smf_sbi_send_status_notify+SMF_SESS_CLEAR(sess) + * 25. V: case OGS_EVENT_SBI_SERVER: + * 26. V: CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 27. V: ogs_sbi_send_http_status_no_content+ + * smf_sbi_send_sm_context_status_notify + * 28. V: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED; } else if (sess->nsmf_param.request_indication == OpenAPI_request_indication_NW_REQ_PDU_SES_REL) { + /* + * Network-requested PDU Session Release(DUPLICATED) + * + * 1. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 2. V: OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT, + * 3. V: OpenAPI_request_indication_NW_REQ_PDU_SES_REL + * 4. V: smf_nsmf_pdusession_build_hsmf_update_data + * 5. H*: smf_nsmf_handle_update_data_in_hsmf + * 6. H*: OpenAPI_request_indication_NW_REQ_PDU_SES_REL + * 6. H*: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT + * 7. H*: ogs_sbi_send_http_status_no_content + * 8. H*: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 9. H: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_AMF_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 10. H: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 11. H: SMF_SESS_CLEAR(sess) + * 12. V: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT + * 13. V: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 14. V: ogs_sbi_send_http_status_no_content + * 15. V: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT; - - } else { - ogs_fatal("Not implemented " - "[requestIndication:%d]", - sess->nsmf_param.request_indication); - ogs_assert_if_reached(); } /* * TS23.502 clause 4.3.4.3 UE or network requested PDU Session Release @@ -1135,6 +1339,10 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) stream)); OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion); + break; + default: + ogs_error("Unknown request_indication:%d", + sess->nsmf_param.request_indication); } } else { ogs_error("smf_nsmf_handle_update_data_in_hsmf() " @@ -1142,6 +1350,28 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) } break; CASE(OGS_SBI_RESOURCE_NAME_RELEASE) + /* + * Network-requested PDU Session Release + * + * 1. V: smf_nsmf_handle_release_sm_context + * 2. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 3. V: OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT + * 4. V: smf_nsmf_pdusession_build_release_data + * 5. H*: smf_nsmf_handle_release_data_in_hsmf + * 6. H*: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT + * 7. H*: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 8. H: ogs_sbi_send_http_status_no_content + * 9. H: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_AMF_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 10. H: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 11. H: SMF_SESS_CLEAR(sess) + * 12. V*: smf_nsmf_handle_release_data_in_hsmf + * 13. V*: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT + * 14. V*: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 15. V: ogs_sbi_send_http_status_no_content + * 16. V: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ smf_nsmf_handle_release_data_in_hsmf( sess, stream, sbi_message); e->h.sbi.state = @@ -1163,6 +1393,31 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) SWITCH(sbi_message->h.resource.component[2]) CASE(OGS_SBI_RESOURCE_NAME_MODIFY) + /* + * Network-requested PDU Session Modification + * + * 1. H: OpenAPI_request_indication_NW_REQ_PDU_SES_MOD + * QOS_RULE_CODE_FROM_PFCP_FLAGS + * QOS_RULE_FLOW_DESCRIPTION_CODE_FROM_PFCP_FLAGS + * 2. H: smf_nsmf_pdusession_build_vsmf_update_data + * 3. V*: smf_nsmf_handle_update_data_in_vsmf + * 4. V: gsm_build_pdu_session_modification_command+ + * ngap_build_pdu_session_resource_modify_request_transfer + * 5. V: OpenAPI_n2_sm_info_type_PDU_RES_MOD_RSP + * if (sess->up_cnx_state == OpenAPI_up_cnx_state_ACTIVATING) + * sess->up_cnx_state = OpenAPI_up_cnx_state_ACTIVATED; + * smf_sbi_send_sm_context_updated_data_up_cnx_state( + * OpenAPI_up_cnx_state_ACTIVATED) + * else + * ogs_sbi_send_http_status_no_content(stream) + * 6. V: ogs_sbi_send_http_status_no_content(stream) + * OGS_NAS_5GS_PDU_SESSION_MODIFICATION_COMPLETE: + * ogs_sbi_send_http_status_no_content(vsmf_to_hsmf_modify_stream)); + * 7. V: case OGS_EVENT_SBI_CLIENT + * CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 8. H: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING| + * OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_ACTIVATE + */ rc = smf_nsmf_handle_update_data_in_vsmf( sess, stream, sbi_message); @@ -1173,6 +1428,44 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) break; case OpenAPI_request_indication_UE_REQ_PDU_SES_REL: case OpenAPI_request_indication_NW_REQ_PDU_SES_REL: + /* + * UE-requested PDU Session Release + * + * 1. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 2. V: OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 3. V: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 4. V: smf_nsmf_pdusession_build_hsmf_update_data + * 5. H: smf_nsmf_handle_update_data_in_hsmf + * 6. H: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 6. H: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 7. H: ogs_sbi_send_http_status_no_content + * 8. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 9. H: smf_nsmf_pdusession_build_vsmf_update_data + * 10. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_5gc_n1_n2_release); + * 11. V*: smf_nsmf_handle_update_data_in_vsmf + * 12. V*: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 13. V*: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 14. V*: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 15. V: ngap_build_pdu_session_resource_release_command_transfer+ + * gsm_build_pdu_session_release_command + * 16 V: OGS_FSM_TRAN(&sess->sm, smf_gsm_state_wait_5gc_n1_n2_release) + * 17. V: ogs_sbi_send_http_status_no_content(stream) + * 18. V: case OpenAPI_n2_sm_info_type_PDU_RES_REL_RSP: + case OGS_NAS_5GS_PDU_SESSION_RELEASE_COMPLETE: + * 19. V: ogs_sbi_send_http_status_no_content(vsmf_to_hsmf_release_stream) + * 20. V: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 21. H: case OGS_EVENT_SBI_CLIENT: + * 22. H: CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 23. H: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_N1N2_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 24. H: smf_sbi_send_status_notify+SMF_SESS_CLEAR(sess) + * 25. V: case OGS_EVENT_SBI_SERVER: + * 26. V: CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 27. V: ogs_sbi_send_http_status_no_content+ + * smf_sbi_send_sm_context_status_notify + * 28. V: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED; OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion); @@ -1259,10 +1552,128 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) CASE(OGS_SBI_HTTP_METHOD_POST) SWITCH(sbi_message->h.resource.component[2]) CASE(OGS_SBI_RESOURCE_NAME_MODIFY) - if (e->h.sbi.state == - OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT) +/* + * PFCP delete triggers are defined in lib/pfcp/xact.h (values 1–7). + * To avoid overlap with OGS_PFCP_DELETE_TRIGGER_*, SMF states use: + * - UPDATE_STATE_BASE at 0x10–0x14 + * - UECM_STATE_BASE at 0x20–0x23 + * HR flag is bit 7 (0x80). + */ + switch (e->h.sbi.state) { + case OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED: + if (sess->amf_to_vsmf_release_stream_id >= + OGS_MIN_POOL_ID && + sess->amf_to_vsmf_release_stream_id <= + OGS_MAX_POOL_ID) + ogs_error("UE requested release stream ID [%d]" + "has not been used yet", + sess->amf_to_vsmf_release_stream_id); + /* Store Stream ID */ + sess->amf_to_vsmf_release_stream_id = + ogs_sbi_id_from_stream(stream); + break; + case OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT: + /* + * Network-requested PDU Session Release(DUPLICATED) + * + * 1. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 2. V: OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT, + * 3. V: OpenAPI_request_indication_NW_REQ_PDU_SES_REL + * 4. V: smf_nsmf_pdusession_build_hsmf_update_data + * 5. H: smf_nsmf_handle_update_data_in_hsmf + * 6. H: OpenAPI_request_indication_NW_REQ_PDU_SES_REL + * 6. H: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT + * 7. H: ogs_sbi_send_http_status_no_content + * 8. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 9. H: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_AMF_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 10. H: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 11. H: SMF_SESS_CLEAR(sess) + * 12. V*: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT + * 13. V*: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 14. V: ogs_sbi_send_http_status_no_content + * 15. V: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ OGS_FSM_TRAN(&sess->sm, &smf_gsm_state_wait_pfcp_deletion); + break; + case SMF_UPDATE_STATE_DEACTIVATED: + /* + * UE-requested PDU Session Modification(DEACTIVATED) + * + * For Home Routed Roaming, delegate PFCP deactivation to H-SMF by + * sending UP_CNX_STATE=DEACTIVATED via HsmfUpdateData. + * + * 1. V: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 2. V: smf_nsmf_pdusession_build_hsmf_update_data + * SMF_UPDATE_STATE_HR_DEACTIVATED + * 3. H: smf_nsmf_handle_update_data_in_hsmf + * 4. H: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 5. H: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_DL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 6. H: ogs_sbi_send_http_status_no_content + * 7. V*: case SMF_UPDATE_STATE_HR_DEACTIVATED: + * 8. V*: smf_sbi_send_sm_context_updated_data_up_cnx_state( + * OpenAPI_up_cnx_state_DEACTIVATED) + */ + smf_sbi_send_sm_context_updated_data_up_cnx_state( + sess, stream, + OpenAPI_up_cnx_state_DEACTIVATED); + break; + case SMF_UPDATE_STATE_ACTIVATING: + /* + * UE-requested PDU Session Modification(ACTIVATING) + * + * 1. V: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 2. V: smf_nsmf_pdusession_build_hsmf_update_data + * SMF_UPDATE_STATE_HR_ACTIVATING + * 3. H: smf_nsmf_handle_update_data_in_hsmf + * 4. H: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 5. H: ogs_sbi_send_http_status_no_content + * 6. V*: ngap_build_pdu_session_resource_setup_request_transfer + * 7. V*: smf_sbi_send_sm_context_updated_data( + * OpenAPI_up_cnx_state_ACTIVATING, + * OpenAPI_n2_sm_info_type_PDU_RES_SETUP_REQ, n2smbuf) + */ + n2smbuf = + ngap_build_pdu_session_resource_setup_request_transfer(sess); + ogs_assert(n2smbuf); + + smf_sbi_send_sm_context_updated_data( + sess, stream, + OpenAPI_up_cnx_state_ACTIVATING, 0, + NULL, + OpenAPI_n2_sm_info_type_PDU_RES_SETUP_REQ, + n2smbuf); + break; + case SMF_UPDATE_STATE_ACTIVATED_FROM_ACTIVATING: + sess->up_cnx_state = + OpenAPI_up_cnx_state_ACTIVATED; + smf_sbi_send_sm_context_updated_data_up_cnx_state( + sess, stream, + OpenAPI_up_cnx_state_ACTIVATED); + break; + case SMF_UPDATE_STATE_ACTIVATED_FROM_NON_ACTIVATING: + ogs_assert(true == + ogs_sbi_send_http_status_no_content( + stream)); + break; + case SMF_UPDATE_STATE_UE_REQ_MOD: + if (sess->amf_to_vsmf_modify_stream_id >= + OGS_MIN_POOL_ID && + sess->amf_to_vsmf_modify_stream_id <= + OGS_MAX_POOL_ID) + ogs_error("UE requested modification stream ID " + "[%d] has not been used yet", + sess->amf_to_vsmf_modify_stream_id); + sess->amf_to_vsmf_modify_stream_id = + ogs_sbi_id_from_stream(stream); + break; + default: + ogs_fatal("Unknown state [0x%x]", e->h.sbi.state); + ogs_assert_if_reached(); + } break; CASE(OGS_SBI_RESOURCE_NAME_RELEASE) if (e->h.sbi.state == @@ -1292,28 +1703,66 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) SWITCH(sbi_message->h.resource.component[2]) CASE(OGS_SBI_RESOURCE_NAME_MODIFY) - ogs_list_for_each_entry( - &sess->qos_flow_to_modify_list, - qos_flow, to_modify_node) { - ogs_pfcp_far_t *dl_far = qos_flow->dl_far; - ogs_assert(dl_far); + /* + * Network-requested PDU Session Modification + * + * 1. H: OpenAPI_request_indication_NW_REQ_PDU_SES_MOD + * QOS_RULE_CODE_FROM_PFCP_FLAGS + * QOS_RULE_FLOW_DESCRIPTION_CODE_FROM_PFCP_FLAGS + * 2. H: smf_nsmf_pdusession_build_vsmf_update_data + * 3. V: smf_nsmf_handle_update_data_in_vsmf + * 4. V: gsm_build_pdu_session_modification_command+ + * ngap_build_pdu_session_resource_modify_request_transfer + * 5. V: OpenAPI_n2_sm_info_type_PDU_RES_MOD_RSP + * if (sess->up_cnx_state == OpenAPI_up_cnx_state_ACTIVATING) + * sess->up_cnx_state = OpenAPI_up_cnx_state_ACTIVATED; + * smf_sbi_send_sm_context_updated_data_up_cnx_state( + * OpenAPI_up_cnx_state_ACTIVATED) + * else + * ogs_sbi_send_http_status_no_content(stream) + * 6. V: ogs_sbi_send_http_status_no_content(stream) + * OGS_NAS_5GS_PDU_SESSION_MODIFICATION_COMPLETE: + * ogs_sbi_send_http_status_no_content(vsmf_to_hsmf_modify_stream)); + * 7. V*: case OGS_EVENT_SBI_CLIENT + * CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 8. H*: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING| + * OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_ACTIVATE + */ + switch (e->h.sbi.state) { + case SMF_CREATE_STATE_NONE: + ogs_list_for_each_entry( + &sess->qos_flow_to_modify_list, + qos_flow, to_modify_node) { + ogs_pfcp_far_t *dl_far = qos_flow->dl_far; + ogs_assert(dl_far); + + dl_far->apply_action = OGS_PFCP_APPLY_ACTION_FORW; + ogs_assert(OGS_OK == + ogs_pfcp_ip_to_outer_header_creation( + &sess->remote_dl_ip, + &dl_far->outer_header_creation, + &dl_far->outer_header_creation_len)); + dl_far->outer_header_creation.teid = + sess->remote_dl_teid; + } - dl_far->apply_action = OGS_PFCP_APPLY_ACTION_FORW; ogs_assert(OGS_OK == - ogs_pfcp_ip_to_outer_header_creation( - &sess->remote_dl_ip, - &dl_far->outer_header_creation, - &dl_far->outer_header_creation_len)); - dl_far->outer_header_creation.teid = - sess->remote_dl_teid; + smf_5gc_pfcp_send_qos_flow_list_modification_request( + sess, NULL, + OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING| + OGS_PFCP_MODIFY_DL_ONLY| + OGS_PFCP_MODIFY_ACTIVATE, 0)); + break; + case SMF_UPDATE_STATE_NONE: + /* Nothing to do */ + break; + case SMF_REMOVE_STATE_NONE: + /* Nothing to do */ + break; + default: + ogs_fatal("Unknown state [%d]", e->h.sbi.state); + ogs_assert_if_reached(); } - - ogs_assert(OGS_OK == - smf_5gc_pfcp_send_qos_flow_list_modification_request( - sess, NULL, - OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING| - OGS_PFCP_MODIFY_DL_ONLY| - OGS_PFCP_MODIFY_ACTIVATE, 0)); break; DEFAULT ogs_error("Invalid resource name [%s]", @@ -1354,32 +1803,68 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) switch (nas_message->gsm.h.message_type) { case OGS_NAS_5GS_PDU_SESSION_MODIFICATION_REQUEST: - rv = gsm_handle_pdu_session_modification_request(sess, stream, - &nas_message->gsm.pdu_session_modification_request); - if (rv != OGS_OK) { - ogs_error("[%s:%d] Cannot handle NAS message", - smf_ue->supi, sess->psi); - OGS_FSM_TRAN(s, smf_gsm_state_exception); + if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) { + sess->nsmf_param.request_indication = + OpenAPI_request_indication_UE_REQ_PDU_SES_MOD; + + r = smf_sbi_discover_and_send( + OGS_SBI_SERVICE_TYPE_NSMF_PDUSESSION, NULL, + smf_nsmf_pdusession_build_hsmf_update_data, + sess, stream, SMF_UPDATE_STATE_UE_REQ_MOD, NULL); + ogs_expect(r == OGS_OK); + ogs_assert(r != OGS_ERROR); + } else { + rv = gsm_handle_pdu_session_modification_request(sess, stream, + &nas_message->gsm.pdu_session_modification_request); + if (rv != OGS_OK) { + ogs_error("[%s:%d] Cannot handle NAS message", + smf_ue->supi, sess->psi); + OGS_FSM_TRAN(s, smf_gsm_state_exception); + } } break; case OGS_NAS_5GS_PDU_SESSION_MODIFICATION_COMPLETE: ogs_assert(true == ogs_sbi_send_http_status_no_content(stream)); if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) { - ogs_sbi_stream_t *n1_n2_modified_stream = NULL; - if (sess->n1_n2_modified_stream_id >= OGS_MIN_POOL_ID && - sess->n1_n2_modified_stream_id <= OGS_MAX_POOL_ID) - n1_n2_modified_stream = + /* + * Network-requested PDU Session Modification + * + * 1. H: OpenAPI_request_indication_NW_REQ_PDU_SES_MOD + * QOS_RULE_CODE_FROM_PFCP_FLAGS + * QOS_RULE_FLOW_DESCRIPTION_CODE_FROM_PFCP_FLAGS + * 2. H: smf_nsmf_pdusession_build_vsmf_update_data + * 3. V: smf_nsmf_handle_update_data_in_vsmf + * 4. V: gsm_build_pdu_session_modification_command+ + * ngap_build_pdu_session_resource_modify_request_transfer + * 5. V: OpenAPI_n2_sm_info_type_PDU_RES_MOD_RSP + * if (sess->up_cnx_state == OpenAPI_up_cnx_state_ACTIVATING) + * sess->up_cnx_state = OpenAPI_up_cnx_state_ACTIVATED; + * smf_sbi_send_sm_context_updated_data_up_cnx_state( + * OpenAPI_up_cnx_state_ACTIVATED) + * else + * ogs_sbi_send_http_status_no_content(stream) + * 6. V*: ogs_sbi_send_http_status_no_content(stream) + * OGS_NAS_5GS_PDU_SESSION_MODIFICATION_COMPLETE: + * ogs_sbi_send_http_status_no_content(vsmf_to_hsmf_modify_stream)); + * 7. V: case OGS_EVENT_SBI_CLIENT + * CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 8. H: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING| + * OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_ACTIVATE + */ + ogs_sbi_stream_t *vsmf_to_hsmf_modify_stream = NULL; + if (sess->vsmf_to_hsmf_modify_stream_id >= OGS_MIN_POOL_ID && + sess->vsmf_to_hsmf_modify_stream_id <= OGS_MAX_POOL_ID) + vsmf_to_hsmf_modify_stream = ogs_sbi_stream_find_by_id( - sess->n1_n2_modified_stream_id); + sess->vsmf_to_hsmf_modify_stream_id); - if (n1_n2_modified_stream) { + if (vsmf_to_hsmf_modify_stream) { ogs_assert(true == ogs_sbi_send_http_status_no_content( - n1_n2_modified_stream)); - } else - ogs_error("No N1-N2 Modifed Stream [%d]", - sess->n1_n2_modified_stream_id); + vsmf_to_hsmf_modify_stream)); + sess->vsmf_to_hsmf_modify_stream_id = OGS_INVALID_POOL_ID; + } } break; @@ -1459,6 +1944,31 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) break; case OpenAPI_n2_sm_info_type_PDU_RES_MOD_RSP: + /* + * Network-requested PDU Session Modification + * + * 1. H: OpenAPI_request_indication_NW_REQ_PDU_SES_MOD + * QOS_RULE_CODE_FROM_PFCP_FLAGS + * QOS_RULE_FLOW_DESCRIPTION_CODE_FROM_PFCP_FLAGS + * 2. H: smf_nsmf_pdusession_build_vsmf_update_data + * 3. V: smf_nsmf_handle_update_data_in_vsmf + * 4. V: gsm_build_pdu_session_modification_command+ + * ngap_build_pdu_session_resource_modify_request_transfer + * 5. V*: OpenAPI_n2_sm_info_type_PDU_RES_MOD_RSP + * if (sess->up_cnx_state == OpenAPI_up_cnx_state_ACTIVATING) + * sess->up_cnx_state = OpenAPI_up_cnx_state_ACTIVATED; + * smf_sbi_send_sm_context_updated_data_up_cnx_state( + * OpenAPI_up_cnx_state_ACTIVATED) + * else + * ogs_sbi_send_http_status_no_content(stream) + * 6. V: ogs_sbi_send_http_status_no_content(stream) + * OGS_NAS_5GS_PDU_SESSION_MODIFICATION_COMPLETE: + * ogs_sbi_send_http_status_no_content(vsmf_to_hsmf_modify_stream)); + * 7. V: case OGS_EVENT_SBI_CLIENT + * CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 8. H: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING| + * OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_ACTIVATE + */ rv = ngap_handle_pdu_session_resource_modify_response_transfer( sess, stream, pkbuf); if (rv != OGS_OK) { @@ -1730,6 +2240,51 @@ void smf_gsm_state_wait_pfcp_deletion(ogs_fsm_t *s, smf_event_t *e) ogs_pkbuf_t *n1smbuf = NULL, *n2smbuf = NULL; if (HOME_ROUTED_ROAMING_IN_HSMF(sess)) { + /* + * UE-requested PDU Session Release + * + * 1. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 2. V: OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 3. V: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 4. V: smf_nsmf_pdusession_build_hsmf_update_data + * 5. H: smf_nsmf_handle_update_data_in_hsmf + * 6. H: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 6. H: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 7. H: ogs_sbi_send_http_status_no_content + * 8. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 9. H*: smf_nsmf_pdusession_build_vsmf_update_data + * 10. H*: OGS_FSM_TRAN(s, smf_gsm_state_wait_5gc_n1_n2_release); + * 11. V: smf_nsmf_handle_update_data_in_vsmf + * 12. V: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 13. V: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 14. V: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 15. V: ngap_build_pdu_session_resource_release_command_transfer+ + * gsm_build_pdu_session_release_command + * 16 V: OGS_FSM_TRAN(&sess->sm, smf_gsm_state_wait_5gc_n1_n2_release) + * 17. V: ogs_sbi_send_http_status_no_content(stream) + * 18. V: case OpenAPI_n2_sm_info_type_PDU_RES_REL_RSP: + case OGS_NAS_5GS_PDU_SESSION_RELEASE_COMPLETE: + * 19. V: ogs_sbi_send_http_status_no_content(vsmf_to_hsmf_release_stream) + * 20. V: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 21. H: case OGS_EVENT_SBI_CLIENT: + * 22. H: CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 23. H: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_N1N2_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 24. H: smf_sbi_send_status_notify+SMF_SESS_CLEAR(sess) + * 25. V: case OGS_EVENT_SBI_SERVER: + * 26. V: CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 27. V: ogs_sbi_send_http_status_no_content+ + * smf_sbi_send_sm_context_status_notify + * 28. V: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ + + if (sess->nsmf_param.request_indication != + OpenAPI_request_indication_UE_REQ_PDU_SES_REL) { + ogs_fatal("Invalid request_indication = %d", + sess->nsmf_param.request_indication); + ogs_assert_if_reached(); + } r = smf_sbi_discover_and_send( OGS_SBI_SERVICE_TYPE_NSMF_PDUSESSION, NULL, @@ -1739,22 +2294,63 @@ void smf_gsm_state_wait_pfcp_deletion(ogs_fsm_t *s, smf_event_t *e) ogs_assert(r != OGS_ERROR); } else if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) { + /* + * UE-requested PDU Session Release + * + * 1. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 2. V: OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 3. V: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 4. V: smf_nsmf_pdusession_build_hsmf_update_data + * 5. H: smf_nsmf_handle_update_data_in_hsmf + * 6. H: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 6. H: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 7. H: ogs_sbi_send_http_status_no_content + * 8. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 9. H: smf_nsmf_pdusession_build_vsmf_update_data + * 10. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_5gc_n1_n2_release); + * 11. V: smf_nsmf_handle_update_data_in_vsmf + * 12. V: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 13. V: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 14. V: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 15. V*: ngap_build_pdu_session_resource_release_command_transfer+ + * gsm_build_pdu_session_release_command + * 16 V*: OGS_FSM_TRAN(&sess->sm, smf_gsm_state_wait_5gc_n1_n2_release) + * 17. V: ogs_sbi_send_http_status_no_content(stream) + * 18. V: case OpenAPI_n2_sm_info_type_PDU_RES_REL_RSP: + case OGS_NAS_5GS_PDU_SESSION_RELEASE_COMPLETE: + * 19. V: ogs_sbi_send_http_status_no_content(vsmf_to_hsmf_release_stream) + * 20. V: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 21. H: case OGS_EVENT_SBI_CLIENT: + * 22. H: CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 23. H: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_N1N2_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 24. H: smf_sbi_send_status_notify+SMF_SESS_CLEAR(sess) + * 25. V: case OGS_EVENT_SBI_SERVER: + * 26. V: CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 27. V: ogs_sbi_send_http_status_no_content+ + * smf_sbi_send_sm_context_status_notify + * 28. V: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ smf_n1_n2_message_transfer_param_t param; - ogs_sbi_stream_t *amf_update_request_stream = NULL; + ogs_sbi_stream_t *amf_to_vsmf_release_stream = NULL; - if (sess->amf_update_request_stream_id >= + if (sess->amf_to_vsmf_release_stream_id >= OGS_MIN_POOL_ID && - sess->amf_update_request_stream_id <= + sess->amf_to_vsmf_release_stream_id <= OGS_MAX_POOL_ID) - amf_update_request_stream = + amf_to_vsmf_release_stream = ogs_sbi_stream_find_by_id( - sess->amf_update_request_stream_id); + sess->amf_to_vsmf_release_stream_id); - if (amf_update_request_stream) + if (amf_to_vsmf_release_stream) { ogs_assert(true == ogs_sbi_send_http_status_no_content( - amf_update_request_stream)); + amf_to_vsmf_release_stream)); + sess->amf_to_vsmf_release_stream_id = + OGS_INVALID_POOL_ID; + } memset(¶m, 0, sizeof(param)); param.state = @@ -1826,6 +2422,8 @@ void smf_gsm_state_wait_pfcp_deletion(ogs_fsm_t *s, smf_event_t *e) if (HOME_ROUTED_ROAMING_IN_HSMF(sess)) { /* + * Network-requested PDU Session Release(DUPLICATED) + * * TS23.502 clause 4.3.4.3 UE or network requested PDU Session Release * for Home-routed Roaming. * @@ -1833,6 +2431,25 @@ void smf_gsm_state_wait_pfcp_deletion(ogs_fsm_t *s, smf_event_t *e) * SHALL immediately send the Update Response, then issue PFCP Session * Deletion. This ordering is per the standard, even for duplicate * sessions, and may overlap with AMF’s concurrent Create Session. + * + * 1. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 2. V: OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT, + * 3. V: OpenAPI_request_indication_NW_REQ_PDU_SES_REL + * 4. V: smf_nsmf_pdusession_build_hsmf_update_data + * 5. H: smf_nsmf_handle_update_data_in_hsmf + * 6. H: OpenAPI_request_indication_NW_REQ_PDU_SES_REL + * 6. H: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT + * 7. H: ogs_sbi_send_http_status_no_content + * 8. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 9. H*: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_AMF_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 10. H*: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 11. H: SMF_SESS_CLEAR(sess) + * 12. V: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT + * 13. V: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 14. V: ogs_sbi_send_http_status_no_content + * 15. V: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); */ r = smf_sbi_cleanup_session( sess, NULL, @@ -1846,6 +2463,28 @@ void smf_gsm_state_wait_pfcp_deletion(ogs_fsm_t *s, smf_event_t *e) } else if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) { + /* + * Network-requested PDU Session Release(DUPLICATED) + * + * 1. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 2. V: OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT, + * 3. V: OpenAPI_request_indication_NW_REQ_PDU_SES_REL + * 4. V: smf_nsmf_pdusession_build_hsmf_update_data + * 5. H: smf_nsmf_handle_update_data_in_hsmf + * 6. H: OpenAPI_request_indication_NW_REQ_PDU_SES_REL + * 6. H: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT + * 7. H: ogs_sbi_send_http_status_no_content + * 8. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 9. H: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_AMF_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 10. H: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 11. H: SMF_SESS_CLEAR(sess) + * 12. V: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT + * 13. V: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 14. V*: ogs_sbi_send_http_status_no_content + * 15. V*: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ ogs_assert(true == ogs_sbi_send_http_status_no_content(stream)); OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); @@ -1867,13 +2506,34 @@ void smf_gsm_state_wait_pfcp_deletion(ogs_fsm_t *s, smf_event_t *e) OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT) { if (HOME_ROUTED_ROAMING_IN_HSMF(sess)) { - /* - * TS23.502 - * 4.3.4.3 UE or network requested PDU Session Release - * for Home-routed Roaming - * - * 1b. (Serving network initiated release) - */ + /* + * Network-requested PDU Session Release + * + * TS23.502 + * 4.3.4.3 UE or network requested PDU Session Release + * for Home-routed Roaming + * + * 1b. (Serving network initiated release) + * + * 1. V*: smf_nsmf_handle_release_sm_context + * 2. V*: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 3. V*: OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT + * 4. V: smf_nsmf_pdusession_build_release_data + * 5. H: smf_nsmf_handle_release_data_in_hsmf + * 6. H: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT + * 7. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 8. H*: ogs_sbi_send_http_status_no_content + * 9. H*: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_AMF_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 10. H*: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 11. H*: SMF_SESS_CLEAR(sess) + * 12. V: smf_nsmf_handle_release_data_in_hsmf + * 13. V: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT + * 14. V: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 15. V*: ogs_sbi_send_http_status_no_content + * 16. V*: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ ogs_assert(true == ogs_sbi_send_http_status_no_content(stream)); @@ -1889,6 +2549,28 @@ void smf_gsm_state_wait_pfcp_deletion(ogs_fsm_t *s, smf_event_t *e) } else if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) { + /* + * Network-requested PDU Session Release + * + * 1. V: smf_nsmf_handle_release_sm_context + * 2. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 3. V: OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT + * 4. V: smf_nsmf_pdusession_build_release_data + * 5. H: smf_nsmf_handle_release_data_in_hsmf + * 6. H: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT + * 7. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 8. H: ogs_sbi_send_http_status_no_content + * 9. H: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_AMF_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 10. H: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 11. H: SMF_SESS_CLEAR(sess) + * 12. V: smf_nsmf_handle_release_data_in_hsmf + * 13. V: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT + * 14. V: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 15. V*: ogs_sbi_send_http_status_no_content + * 16. V*: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ ogs_assert(true == ogs_sbi_send_http_status_no_content(stream)); OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); @@ -1978,6 +2660,90 @@ void smf_gsm_state_wait_pfcp_deletion(ogs_fsm_t *s, smf_event_t *e) stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, n1smbuf, OpenAPI_n2_sm_info_type_NULL, NULL); break; + + CASE(OGS_SBI_SERVICE_NAME_NSMF_PDUSESSION) + stream_id = OGS_POINTER_TO_UINT(e->h.sbi.data); + if (stream_id >= OGS_MIN_POOL_ID && stream_id <= OGS_MAX_POOL_ID) + stream = ogs_sbi_stream_find_by_id(stream_id); + + 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) + SWITCH(sbi_message->h.resource.component[2]) + CASE(OGS_SBI_RESOURCE_NAME_MODIFY) + switch (e->h.sbi.state) { + case OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED: +/* + * Handle unexpected arrival of Nsmf_PDUSession_UpdateSMContext Response + * (step 1a) after PFCP deletion triggered by UE (step 3a). + * In Home-routed Roaming (TS 23.502 Section 4.3.4.3) SMContext Response is + * expected before SMContext Request (step 3a), but races can invert order, + * causing smf_gsm_state_wait_pfcp_deletion on PFCP release (steps 4a/4b). + * Without handling the delayed Response here, no handler exists and a crash + * results. Storing the stream ID lets the Response be processed safely. + */ + if (sess->amf_to_vsmf_release_stream_id >= + OGS_MIN_POOL_ID && + sess->amf_to_vsmf_release_stream_id <= + OGS_MAX_POOL_ID) + ogs_error("UE requested release stream ID [%d]" + "has not been used yet", + sess->amf_to_vsmf_release_stream_id); + /* Store Stream ID */ + sess->amf_to_vsmf_release_stream_id = + ogs_sbi_id_from_stream(stream); + break; +/* + * Assume PDU session establishment and deregistration occur simultaneously. + * In Non-Roaming or LBO scenarios, no issues arise. However, in Home Routed + * Roaming, the following problem may occur: + * + * The final step of PDU session establishment is activation via the + * PDUSessionResourceSetupResponse. At this point, the V-SMF sends a + * /pdu-sessions/{xx}/modify request to the H-SMF. Meanwhile, the deregistration + * process advances the state to smf_gsm_state_wait_pfcp_deletion as part + * of the PFCP deletion procedure. + * + * When the H-SMF then sends the OGS_EVENT_SBI_CLIENT event, a corresponding + * handler is required to process this state. The code below implements this + * handler to address the race condition. + */ + case SMF_UPDATE_STATE_ACTIVATED_FROM_ACTIVATING: + sess->up_cnx_state = + OpenAPI_up_cnx_state_ACTIVATED; + smf_sbi_send_sm_context_updated_data_up_cnx_state( + sess, stream, + OpenAPI_up_cnx_state_ACTIVATED); + break; + case SMF_UPDATE_STATE_ACTIVATED_FROM_NON_ACTIVATING: + ogs_assert(true == + ogs_sbi_send_http_status_no_content( + stream)); + break; + default: + ogs_fatal("Unknown state [0x%x]", e->h.sbi.state); + ogs_assert_if_reached(); + } + break; + DEFAULT + ogs_error("Invalid resource name [%s]", + sbi_message->h.resource.component[2]); + ogs_assert_if_reached(); + 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("[%s:%d] Invalid API name [%s]", smf_ue->supi, sess->psi, sbi_message->h.service.name); @@ -2162,54 +2928,150 @@ void smf_gsm_state_wait_5gc_n1_n2_release(ogs_fsm_t *s, smf_event_t *e) SWITCH(sbi_message->h.service.name) CASE(OGS_SBI_SERVICE_NAME_NSMF_PDUSESSION) - SWITCH(sbi_message->h.resource.component[2]) - CASE(OGS_SBI_RESOURCE_NAME_MODIFY) - smf_nsmf_handle_update_sm_context(sess, stream, sbi_message); + SWITCH(sbi_message->h.resource.component[0]) + CASE(OGS_SBI_RESOURCE_NAME_SM_CONTEXTS) + SWITCH(sbi_message->h.resource.component[2]) + CASE(OGS_SBI_RESOURCE_NAME_MODIFY) + smf_nsmf_handle_update_sm_context( + sess, stream, sbi_message); + break; + /* + * Network-requested PDU Session Release + * + * 1. V*: smf_nsmf_handle_release_sm_context + * 2. V*: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 3. V*: OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT + * 4. V: smf_nsmf_pdusession_build_release_data + * 5. H: smf_nsmf_handle_release_data_in_hsmf + * 6. H: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT + * 7. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 8. H*: ogs_sbi_send_http_status_no_content + * 9. H*: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_AMF_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 10. H*: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 11. H*: SMF_SESS_CLEAR(sess) + * 12. V: smf_nsmf_handle_release_data_in_hsmf + * 13. V: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT + * 14. V: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 15. V*: ogs_sbi_send_http_status_no_content + * 16. V*: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ + CASE(OGS_SBI_RESOURCE_NAME_RELEASE) + smf_nsmf_handle_release_sm_context( + sess, stream, sbi_message); + + if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) { + 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_RELEASE_SM_CONTEXT, + 0)); + } else { + ogs_warn("[%s:%d] Session Release " + "[PFCP-Delete-Trigger:%d]", + smf_ue->supi, sess->psi, e->h.sbi.state); + + r = smf_sbi_cleanup_session( + sess, stream, + SMF_UECM_STATE_DEREG_BY_AMF, + SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + ogs_expect(r == OGS_OK); + ogs_assert(r != OGS_ERROR); + + OGS_FSM_TRAN(s, + smf_gsm_state_5gc_session_will_deregister); + } + break; + DEFAULT + ogs_error("Invalid resource name [%s]", + sbi_message->h.resource.component[2]); + ogs_assert(true == + ogs_sbi_server_send_error(stream, + OGS_SBI_HTTP_STATUS_BAD_REQUEST, sbi_message, + "Invalid resource name [%s]", + sbi_message->h.resource.component[2], NULL)); + OGS_FSM_TRAN(s, smf_gsm_state_exception); + END break; - CASE(OGS_SBI_RESOURCE_NAME_RELEASE) - /* - * Session release is initiated by a PDU Session Release Request. - * The AMF sends a PDUSessionReleaseCommand to the UE, but the UE - * does not send a PDU Session Release Complete or - * PDUSessionResourceReleaseResponse back to the AMF. - * - * As a result, the context remains in the - * smf_gsm_state_wait_5gc_n1_n2_release state. - * - * When the UE then sends a Deregistration request in this state, - * smf_nsmf_handle_release_sm_context() is invoked. - */ - smf_nsmf_handle_release_sm_context(sess, stream, sbi_message); - if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) { - 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_RELEASE_SM_CONTEXT, 0)); - } else { - ogs_warn("[%s:%d] Session Release [PFCP-Delete-Trigger:%d]", - smf_ue->supi, sess->psi, e->h.sbi.state); + CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + SWITCH(sbi_message->h.resource.component[2]) + CASE(OGS_SBI_RESOURCE_NAME_RELEASE) + ogs_error("Invalid resource name [%s]", + sbi_message->h.resource.component[2]); + ogs_assert(true == + ogs_sbi_server_send_error(stream, + OGS_SBI_HTTP_STATUS_BAD_REQUEST, sbi_message, + "Invalid resource name [%s]", + sbi_message->h.resource.component[2], NULL)); + OGS_FSM_TRAN(s, smf_gsm_state_exception); + break; + DEFAULT + /* + * UE-requested PDU Session Release + * + * 1. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 2. V: OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 3. V: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 4. V: smf_nsmf_pdusession_build_hsmf_update_data + * 5. H: smf_nsmf_handle_update_data_in_hsmf + * 6. H: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 6. H: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 7. H: ogs_sbi_send_http_status_no_content + * 8. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 9. H: smf_nsmf_pdusession_build_vsmf_update_data + * 10. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_5gc_n1_n2_release); + * 11. V: smf_nsmf_handle_update_data_in_vsmf + * 12. V: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 13. V: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 14. V: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 15. V: ngap_build_pdu_session_resource_release_command_transfer+ + * gsm_build_pdu_session_release_command + * 16 V: OGS_FSM_TRAN(&sess->sm, smf_gsm_state_wait_5gc_n1_n2_release) + * 17. V: ogs_sbi_send_http_status_no_content(stream) + * 18. V: case OpenAPI_n2_sm_info_type_PDU_RES_REL_RSP: + * case OGS_NAS_5GS_PDU_SESSION_RELEASE_COMPLETE: + * 19. V: ogs_sbi_send_http_status_no_content(vsmf_to_hsmf_release_stream) + * 20. V: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 21. H: case OGS_EVENT_SBI_CLIENT: + * 22. H: CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 23. H: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_N1N2_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 24. H: smf_sbi_send_status_notify+SMF_SESS_CLEAR(sess) + * 25. V*: case OGS_EVENT_SBI_SERVER: + * 26. V*: CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 27. V*: ogs_sbi_send_http_status_no_content+ + * smf_sbi_send_sm_context_status_notify + * 28. V*: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ + ogs_assert(true == + ogs_sbi_send_http_status_no_content(stream)); - r = smf_sbi_cleanup_session( - sess, stream, - SMF_UECM_STATE_DEREG_BY_AMF, - SMF_SBI_CLEANUP_MODE_POLICY_FIRST); - ogs_expect(r == OGS_OK); - ogs_assert(r != OGS_ERROR); - - OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); - } + sess->resource_status = OpenAPI_resource_status_RELEASED; + if (sess->n1_released == true && + sess->n2_released == true && + sess->resource_status == + OpenAPI_resource_status_RELEASED) { + ogs_assert(true == + smf_sbi_send_sm_context_status_notify(sess)); + OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + } + END break; + DEFAULT ogs_error("Invalid resource name [%s]", - sbi_message->h.resource.component[2]); + sbi_message->h.resource.component[0]); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, sbi_message, "Invalid resource name [%s]", - sbi_message->h.resource.component[2], NULL)); + sbi_message->h.resource.component[0], NULL)); OGS_FSM_TRAN(s, smf_gsm_state_exception); END break; @@ -2241,6 +3103,44 @@ void smf_gsm_state_wait_5gc_n1_n2_release(ogs_fsm_t *s, smf_event_t *e) CASE(OGS_SBI_HTTP_METHOD_POST) SWITCH(sbi_message->h.resource.component[2]) CASE(OGS_SBI_RESOURCE_NAME_MODIFY) + /* + * UE-requested PDU Session Release + * + * 1. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 2. V: OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 3. V: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 4. V: smf_nsmf_pdusession_build_hsmf_update_data + * 5. H: smf_nsmf_handle_update_data_in_hsmf + * 6. H: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 6. H: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 7. H: ogs_sbi_send_http_status_no_content + * 8. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 9. H: smf_nsmf_pdusession_build_vsmf_update_data + * 10. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_5gc_n1_n2_release); + * 11. V: smf_nsmf_handle_update_data_in_vsmf + * 12. V: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 13. V: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 14. V: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 15. V: ngap_build_pdu_session_resource_release_command_transfer+ + * gsm_build_pdu_session_release_command + * 16 V: OGS_FSM_TRAN(&sess->sm, smf_gsm_state_wait_5gc_n1_n2_release) + * 17. V: ogs_sbi_send_http_status_no_content(stream) + * 18. V: case OpenAPI_n2_sm_info_type_PDU_RES_REL_RSP: + * case OGS_NAS_5GS_PDU_SESSION_RELEASE_COMPLETE: + * 19. V: ogs_sbi_send_http_status_no_content(vsmf_to_hsmf_release_stream) + * 20. V: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 21. H*: case OGS_EVENT_SBI_CLIENT: + * 22. H*: CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 23. H*: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_N1N2_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 24. H: smf_sbi_send_status_notify+SMF_SESS_CLEAR(sess) + * 25. V: case OGS_EVENT_SBI_SERVER: + * 26. V: CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 27. V: ogs_sbi_send_http_status_no_content+ + * smf_sbi_send_sm_context_status_notify + * 28. V: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ r = smf_sbi_cleanup_session( sess, NULL, SMF_UECM_STATE_DEREG_BY_N1N2_HR, @@ -2400,35 +3300,82 @@ void smf_gsm_state_wait_5gc_n1_n2_release(ogs_fsm_t *s, smf_event_t *e) ogs_assert(true == ogs_sbi_send_http_status_no_content(stream)); + /* + * UE-requested PDU Session Release + * + * 1. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 2. V: OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 3. V: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 4. V: smf_nsmf_pdusession_build_hsmf_update_data + * 5. H: smf_nsmf_handle_update_data_in_hsmf + * 6. H: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 6. H: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 7. H: ogs_sbi_send_http_status_no_content + * 8. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 9. H: smf_nsmf_pdusession_build_vsmf_update_data + * 10. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_5gc_n1_n2_release); + * 11. V: smf_nsmf_handle_update_data_in_vsmf + * 12. V: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 13. V: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 14. V: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 15. V: ngap_build_pdu_session_resource_release_command_transfer+ + * gsm_build_pdu_session_release_command + * 16 V: OGS_FSM_TRAN(&sess->sm, smf_gsm_state_wait_5gc_n1_n2_release) + * 17. V*: ogs_sbi_send_http_status_no_content(stream) + * 18. V*: case OpenAPI_n2_sm_info_type_PDU_RES_REL_RSP: + * case OGS_NAS_5GS_PDU_SESSION_RELEASE_COMPLETE: + * 19. V*: ogs_sbi_send_http_status_no_content(vsmf_to_hsmf_release_stream) + * 20. V*: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 21. H: case OGS_EVENT_SBI_CLIENT: + * 22. H: CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 23. H: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_N1N2_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 24. H: smf_sbi_send_status_notify+SMF_SESS_CLEAR(sess) + * 25. V: case OGS_EVENT_SBI_SERVER: + * 26. V: CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 27. V: ogs_sbi_send_http_status_no_content+ + * smf_sbi_send_sm_context_status_notify + * 28. V: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ sess->n2_released = true; - if ((sess->n1_released) && (sess->n2_released)) { - if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) { - ogs_sbi_stream_t *n1_n2_released_stream = NULL; - if (sess->n1_n2_released_stream_id >= OGS_MIN_POOL_ID && - sess->n1_n2_released_stream_id <= OGS_MAX_POOL_ID) - n1_n2_released_stream = - ogs_sbi_stream_find_by_id( - sess->n1_n2_released_stream_id); + if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) { + ogs_sbi_stream_t *vsmf_to_hsmf_release_stream = NULL; + if (sess->vsmf_to_hsmf_release_stream_id >= + OGS_MIN_POOL_ID && + sess->vsmf_to_hsmf_release_stream_id <= OGS_MAX_POOL_ID) + vsmf_to_hsmf_release_stream = + ogs_sbi_stream_find_by_id( + sess->vsmf_to_hsmf_release_stream_id); - if (n1_n2_released_stream) - ogs_assert(true == - ogs_sbi_send_http_status_no_content( - n1_n2_released_stream)); - else - ogs_error("No N1-N2 Released Stream [%d]", - sess->n1_n2_released_stream_id); - } else { - r = smf_sbi_cleanup_session( - sess, NULL, - SMF_UECM_STATE_DEREG_BY_N1N2, - SMF_SBI_CLEANUP_MODE_POLICY_FIRST); - ogs_expect(r == OGS_OK); - ogs_assert(r != OGS_ERROR); + if (vsmf_to_hsmf_release_stream) { + ogs_assert(true == + ogs_sbi_send_http_status_no_content( + vsmf_to_hsmf_release_stream)); + sess->vsmf_to_hsmf_release_stream_id = + OGS_INVALID_POOL_ID; } - OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); - } + if (sess->n1_released == true && + sess->n2_released == true && + sess->resource_status == + OpenAPI_resource_status_RELEASED) { + ogs_assert(true == + smf_sbi_send_sm_context_status_notify(sess)); + OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + } + } else if (sess->n1_released == true && + sess->n2_released == true) { + r = smf_sbi_cleanup_session( + sess, NULL, + SMF_UECM_STATE_DEREG_BY_N1N2, + SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + ogs_expect(r == OGS_OK); + ogs_assert(r != OGS_ERROR); + OGS_FSM_TRAN(s, + smf_gsm_state_5gc_session_will_deregister); + } } else { ogs_fatal("Invalid state [%d]", ngap_state); ogs_assert_if_reached(); @@ -2457,32 +3404,75 @@ void smf_gsm_state_wait_5gc_n1_n2_release(ogs_fsm_t *s, smf_event_t *e) case OGS_NAS_5GS_PDU_SESSION_RELEASE_COMPLETE: ogs_assert(true == ogs_sbi_send_http_status_no_content(stream)); + /* + * UE-requested PDU Session Release + * + * 1. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 2. V: OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 3. V: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 4. V: smf_nsmf_pdusession_build_hsmf_update_data + * 5. H: smf_nsmf_handle_update_data_in_hsmf + * 6. H: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 6. H: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 7. H: ogs_sbi_send_http_status_no_content + * 8. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 9. H: smf_nsmf_pdusession_build_vsmf_update_data + * 10. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_5gc_n1_n2_release); + * 11. V: smf_nsmf_handle_update_data_in_vsmf + * 12. V: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 13. V: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 14. V: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 15. V: ngap_build_pdu_session_resource_release_command_transfer+ + * gsm_build_pdu_session_release_command + * 16 V: OGS_FSM_TRAN(&sess->sm, smf_gsm_state_wait_5gc_n1_n2_release) + * 17. V*: ogs_sbi_send_http_status_no_content(stream) + * 18. V*: case OpenAPI_n2_sm_info_type_PDU_RES_REL_RSP: + * case OGS_NAS_5GS_PDU_SESSION_RELEASE_COMPLETE: + * 19. V*: ogs_sbi_send_http_status_no_content(vsmf_to_hsmf_release_stream) + * 20. V*: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 21. H: case OGS_EVENT_SBI_CLIENT: + * 22. H: CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 23. H: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_N1N2_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 24. H: smf_sbi_send_status_notify+SMF_SESS_CLEAR(sess) + * 25. V: case OGS_EVENT_SBI_SERVER: + * 26. V: CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 27. V: ogs_sbi_send_http_status_no_content+ + * smf_sbi_send_sm_context_status_notify + * 28. V: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ sess->n1_released = true; - if ((sess->n1_released) && (sess->n2_released)) { - if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) { - ogs_sbi_stream_t *n1_n2_released_stream = NULL; - if (sess->n1_n2_released_stream_id >= OGS_MIN_POOL_ID && - sess->n1_n2_released_stream_id <= OGS_MAX_POOL_ID) - n1_n2_released_stream = - ogs_sbi_stream_find_by_id( - sess->n1_n2_released_stream_id); + if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) { + ogs_sbi_stream_t *vsmf_to_hsmf_release_stream = NULL; + if (sess->vsmf_to_hsmf_release_stream_id >= OGS_MIN_POOL_ID && + sess->vsmf_to_hsmf_release_stream_id <= OGS_MAX_POOL_ID) + vsmf_to_hsmf_release_stream = + ogs_sbi_stream_find_by_id( + sess->vsmf_to_hsmf_release_stream_id); - if (n1_n2_released_stream) - ogs_assert(true == - ogs_sbi_send_http_status_no_content( - n1_n2_released_stream)); - else - ogs_error("No N1-N2 Released Stream [%d]", - sess->n1_n2_released_stream_id); - } else { - r = smf_sbi_cleanup_session( - sess, NULL, - SMF_UECM_STATE_DEREG_BY_N1N2, - SMF_SBI_CLEANUP_MODE_POLICY_FIRST); - ogs_expect(r == OGS_OK); - ogs_assert(r != OGS_ERROR); + if (vsmf_to_hsmf_release_stream) { + ogs_assert(true == + ogs_sbi_send_http_status_no_content( + vsmf_to_hsmf_release_stream)); + sess->vsmf_to_hsmf_release_stream_id = OGS_INVALID_POOL_ID; } + if (sess->n1_released == true && + sess->n2_released == true && + sess->resource_status == OpenAPI_resource_status_RELEASED) { + ogs_assert(true == + smf_sbi_send_sm_context_status_notify(sess)); + OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + } + } else if (sess->n1_released == true && sess->n2_released == true) { + r = smf_sbi_cleanup_session( + sess, NULL, + SMF_UECM_STATE_DEREG_BY_N1N2, + SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + ogs_expect(r == OGS_OK); + ogs_assert(r != OGS_ERROR); + OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); } @@ -2685,35 +3675,14 @@ void smf_gsm_state_5gc_session_will_deregister(ogs_fsm_t *s, smf_event_t *e) END break; - CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) - SWITCH(sbi_message->h.resource.component[2]) - CASE(OGS_SBI_RESOURCE_NAME_RELEASE) - ogs_error("Invalid resource name [%s]", - sbi_message->h.resource.component[2]); - ogs_assert(true == - ogs_sbi_server_send_error(stream, - OGS_SBI_HTTP_STATUS_BAD_REQUEST, sbi_message, - "Invalid resource name [%s]", - sbi_message->h.resource.component[2], NULL)); - OGS_FSM_TRAN(s, smf_gsm_state_exception); - break; - DEFAULT - ogs_assert(true == - ogs_sbi_send_http_status_no_content(stream)); - ogs_assert(true == - smf_sbi_send_sm_context_status_notify(sess)); - OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); - END - break; - DEFAULT ogs_error("Invalid resource name [%s]", - sbi_message->h.resource.component[2]); + sbi_message->h.resource.component[0]); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, sbi_message, "Invalid resource name [%s]", - sbi_message->h.resource.component[2], NULL)); + sbi_message->h.resource.component[0], NULL)); OGS_FSM_TRAN(s, smf_gsm_state_exception); END break; diff --git a/src/smf/n4-handler.c b/src/smf/n4-handler.c index c396f330b..d3d1a6f2d 100644 --- a/src/smf/n4-handler.c +++ b/src/smf/n4-handler.c @@ -410,14 +410,93 @@ void smf_5gc_n4_handle_session_modification_response( if (flags & OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING) { if (flags & OGS_PFCP_MODIFY_ACTIVATE) { if (flags & OGS_PFCP_MODIFY_DL_ONLY) { - if (sess->up_cnx_state == OpenAPI_up_cnx_state_ACTIVATING) { - sess->up_cnx_state = OpenAPI_up_cnx_state_ACTIVATED; - smf_sbi_send_sm_context_updated_data_up_cnx_state( - sess, stream, OpenAPI_up_cnx_state_ACTIVATED); - } else { + /* + * UE-requested PDU Session Modification(ACTIVATED) + * + * 1. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_DL_ONLY| + * OGS_PFCP_MODIFY_OUTER_HEADER_REMOVAL|OGS_PFCP_MODIFY_ACTIVATE + * 2. V: if (sess->up_cnx_state == OpenAPI_up_cnx_state_ACTIVATING) + * pfcp_flags |= OGS_PFCP_MODIFY_FROM_ACTIVATING; + * 3. V*: flags & OGS_PFCP_MODIFY_FROM_ACTIVATING ? + * SMF_UPDATE_STATE_HR_ACTIVATED_FROM_ACTIVATING : + * SMF_UPDATE_STATE_HR_ACTIVATED_FROM_NON_ACTIVATING, + * 4. V*: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 5. V*: smf_nsmf_pdusession_build_hsmf_update_data + * 6. H: smf_nsmf_handle_update_data_in_hsmf + * 7. H: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 8. H: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_DL_ONLY| + * OGS_PFCP_MODIFY_ACTIVATE + * 9. H*: ogs_sbi_send_http_status_no_content + * 10. V: case SMF_UPDATE_STATE_HR_ACTIVATED_FROM_ACTIVATING: + * sess->up_cnx_state = OpenAPI_up_cnx_state_ACTIVATED; + * smf_sbi_send_sm_context_updated_data_up_cnx_state( + * OpenAPI_up_cnx_state_ACTIVATED); + * case SMF_UPDATE_STATE_HR_ACTIVATED_FROM_NON_ACTIVATING: + * ogs_sbi_send_http_status_no_content + */ + if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) { + ogs_assert(stream); + + sess->nsmf_param.request_indication = + OpenAPI_request_indication_UE_REQ_PDU_SES_MOD; + + sess->nsmf_param.up_cnx_state = + OpenAPI_up_cnx_state_ACTIVATED; + + sess->nsmf_param.serving_network = true; + + ogs_assert(OGS_OK == + ogs_sockaddr_to_ip( + sess->local_dl_addr, sess->local_dl_addr6, + &sess->nsmf_param.dl_ip)); + sess->nsmf_param.dl_teid = sess->local_dl_teid; + + sess->nsmf_param.an_type = sess->an_type; + sess->nsmf_param.rat_type = sess->sbi_rat_type; + + r = smf_sbi_discover_and_send( + OGS_SBI_SERVICE_TYPE_NSMF_PDUSESSION, NULL, + smf_nsmf_pdusession_build_hsmf_update_data, + sess, stream, + flags & OGS_PFCP_MODIFY_FROM_ACTIVATING ? + SMF_UPDATE_STATE_ACTIVATED_FROM_ACTIVATING : + SMF_UPDATE_STATE_ACTIVATED_FROM_NON_ACTIVATING, + NULL); + ogs_expect(r == OGS_OK); + ogs_assert(r != OGS_ERROR); + } else if (HOME_ROUTED_ROAMING_IN_HSMF(sess)) { + /* + * Network-requested PDU Session Modification + * + * 1. H: OpenAPI_request_indication_NW_REQ_PDU_SES_MOD + * QOS_RULE_CODE_FROM_PFCP_FLAGS + * QOS_RULE_FLOW_DESCRIPTION_CODE_FROM_PFCP_FLAGS + * 2. H: smf_nsmf_pdusession_build_vsmf_update_data + * 3. V: smf_nsmf_handle_update_data_in_vsmf + * 4. V: gsm_build_pdu_session_modification_command+ + * ngap_build_pdu_session_resource_modify_request_transfer + * 5. V: OpenAPI_n2_sm_info_type_PDU_RES_MOD_RSP + * if (sess->up_cnx_state == OpenAPI_up_cnx_state_ACTIVATING) + * sess->up_cnx_state = OpenAPI_up_cnx_state_ACTIVATED; + * smf_sbi_send_sm_context_updated_data_up_cnx_state( + * OpenAPI_up_cnx_state_ACTIVATED) + * else + * ogs_sbi_send_http_status_no_content(stream) + * 6. V: ogs_sbi_send_http_status_no_content(stream) + * OGS_NAS_5GS_PDU_SESSION_MODIFICATION_COMPLETE: + * ogs_sbi_send_http_status_no_content(n1_n2_modified_stream)); + * 7. V: case OGS_EVENT_SBI_CLIENT + * CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 8. H: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING| + * OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_ACTIVATE + * 9. H*: ogs_sbi_send_http_status_no_content + */ if (stream) ogs_assert(true == - ogs_sbi_send_http_status_no_content(stream)); + ogs_sbi_send_http_status_no_content(stream)); + } else { + ogs_fatal("Invalid flags [0x%llx]", (long long)flags); + ogs_assert_if_reached(); } } else if (flags & OGS_PFCP_MODIFY_UL_ONLY) { smf_n1_n2_message_transfer_param_t param; @@ -433,66 +512,252 @@ void smf_5gc_n4_handle_session_modification_response( ogs_assert(param.n2smbuf); smf_namf_comm_send_n1_n2_message_transfer(sess, NULL, ¶m); + + if (sess->pending_modification_xact) { + if (ogs_sbi_discover_and_send( + sess->pending_modification_xact) != OGS_OK) { + ogs_error("ogs_sbi_discover_and_send() failed"); + ogs_sbi_xact_remove(sess->pending_modification_xact); + } + + sess->pending_modification_xact = NULL; + } } else { ogs_fatal("Invalid flags [0x%llx]", (long long)flags); ogs_assert_if_reached(); } } else if (flags & OGS_PFCP_MODIFY_DEACTIVATE) { - ogs_assert(trigger); + if (flags & OGS_PFCP_MODIFY_DL_ONLY) { + /* + * UE-requested PDU Session Modification(DEACTIVATED) + * + * For Home Routed Roaming, delegate PFCP deactivation to H-SMF by + * sending UP_CNX_STATE=DEACTIVATED via HsmfUpdateData. + * + * 1. V: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 2. V: smf_nsmf_pdusession_build_hsmf_update_data + * SMF_UPDATE_STATE_HR_DEACTIVATED + * 3. H: smf_nsmf_handle_update_data_in_hsmf + * 4. H: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 5. H: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_DL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 6. H*: ogs_sbi_send_http_status_no_content + * 7. V: case SMF_UPDATE_STATE_HR_DEACTIVATED: + * 8. V: smf_sbi_send_sm_context_updated_data_up_cnx_state( + * OpenAPI_up_cnx_state_DEACTIVATED) + */ + ogs_assert(true == ogs_sbi_send_http_status_no_content(stream)); + } else if (flags & OGS_PFCP_MODIFY_UL_ONLY) { + 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); + if (trigger == OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED) { + /* + * UE-requested PDU Session Release + * + * 1. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 2. V: OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 3. V*: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 4. V*: smf_nsmf_pdusession_build_hsmf_update_data + * 5. H: smf_nsmf_handle_update_data_in_hsmf + * 6. H: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 6. H: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 7. H: ogs_sbi_send_http_status_no_content + * 8. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 9. H: smf_nsmf_pdusession_build_vsmf_update_data + * 10. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_5gc_n1_n2_release); + * 11. V: smf_nsmf_handle_update_data_in_vsmf + * 12. V: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 13. V: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 14. V: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 15. V: ngap_build_pdu_session_resource_release_command_transfer+ + * gsm_build_pdu_session_release_command + * 16 V: OGS_FSM_TRAN(&sess->sm, smf_gsm_state_wait_5gc_n1_n2_release) + 17. V: case OpenAPI_n2_sm_info_type_PDU_RES_REL_RSP: + case OGS_NAS_5GS_PDU_SESSION_RELEASE_COMPLETE: + * 18. V: ogs_sbi_send_http_status_no_content(n1_n2_released_stream) + * 19. V: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 20. H: case OGS_EVENT_SBI_CLIENT: + * 21. H: CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 22. H: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_N1N2_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 23. H: smf_sbi_send_status_notify+SMF_SESS_CLEAR(sess) + * 24. V: case OGS_EVENT_SBI_SERVER: + * 25. V: CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 26. V: ogs_sbi_send_http_status_no_content+ + * smf_sbi_send_sm_context_status_notify + * 27. V: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ + sess->nsmf_param.request_indication = + OpenAPI_request_indication_UE_REQ_PDU_SES_REL; + + 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) { + /* + * Network-requested PDU Session Release(DUPLICATED) + * + * 1. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 2. V: OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT, + * 3. V*: OpenAPI_request_indication_NW_REQ_PDU_SES_REL + * 4. V*: smf_nsmf_pdusession_build_hsmf_update_data + * 5. H: smf_nsmf_handle_update_data_in_hsmf + * 6. H: OpenAPI_request_indication_NW_REQ_PDU_SES_REL + * 6. H: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT + * 7. H: ogs_sbi_send_http_status_no_content + * 8. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 9. H: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_AMF_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 10. H: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 11. H: SMF_SESS_CLEAR(sess) + * 12. V: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT + * 13. V: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 14. V: ogs_sbi_send_http_status_no_content + * 15. V: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ + sess->nsmf_param.request_indication = + OpenAPI_request_indication_NW_REQ_PDU_SES_REL; + + 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) { + /* + * Network-requested PDU Session Release + * + * 1. V: smf_nsmf_handle_release_sm_context + * 2. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 3. V: OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT + * 4. V*: smf_nsmf_pdusession_build_release_data + * 5. H: smf_nsmf_handle_release_data_in_hsmf + * 6. H: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT + * 7. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 8. H: ogs_sbi_send_http_status_no_content + * 9. H: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_AMF_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 10. H: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 11. H: SMF_SESS_CLEAR(sess) + * 12. V: smf_nsmf_handle_release_data_in_hsmf + * 13. V: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT + * 14. V: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 15. V: ogs_sbi_send_http_status_no_content + * 16. V: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ + 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 { - ogs_fatal("Invalid delete trigger[%d]", trigger); + ogs_fatal("Invalid flags [0x%llx]", (long long)flags); ogs_assert_if_reached(); } - } else if (flags & OGS_PFCP_MODIFY_CREATE) { - if (flags & OGS_PFCP_MODIFY_NETWORK_REQUESTED) { - memset(&sess->nsmf_param, 0, sizeof(sess->nsmf_param)); + } else if ( + (flags & OGS_PFCP_MODIFY_REMOVE) || + (flags & OGS_PFCP_MODIFY_CREATE) || + (flags & + (OGS_PFCP_MODIFY_TFT_NEW|OGS_PFCP_MODIFY_TFT_ADD| + OGS_PFCP_MODIFY_TFT_REPLACE|OGS_PFCP_MODIFY_TFT_DELETE| + OGS_PFCP_MODIFY_QOS_MODIFY))) { + /* + * UE or Network requested PDU Session Modification + * + * 1. H*: OpenAPI_request_indication_NW_REQ_PDU_SES_MOD + * QOS_RULE_CODE_FROM_PFCP_FLAGS + * QOS_RULE_FLOW_DESCRIPTION_CODE_FROM_PFCP_FLAGS + * 2. H*: smf_nsmf_pdusession_build_vsmf_update_data + * 3. V: smf_nsmf_handle_update_data_in_vsmf + * 4. V: gsm_build_pdu_session_modification_command+ + * ngap_build_pdu_session_resource_modify_request_transfer + * 5. V: OpenAPI_n2_sm_info_type_PDU_RES_MOD_RSP + * if (sess->up_cnx_state == OpenAPI_up_cnx_state_ACTIVATING) + * sess->up_cnx_state = OpenAPI_up_cnx_state_ACTIVATED; + * smf_sbi_send_sm_context_updated_data_up_cnx_state( + * OpenAPI_up_cnx_state_ACTIVATED) + * else + * ogs_sbi_send_http_status_no_content(stream) + * 6. V: ogs_sbi_send_http_status_no_content(stream) + * OGS_NAS_5GS_PDU_SESSION_MODIFICATION_COMPLETE: + * ogs_sbi_send_http_status_no_content(n1_n2_modified_stream)); + * 7. V: case OGS_EVENT_SBI_CLIENT + * CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 8. H: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING| + * OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_ACTIVATE + */ + int state = 0; - /* Network Requested PDU Session Modification */ + memset(&sess->nsmf_param, 0, sizeof(sess->nsmf_param)); + if (flags & OGS_PFCP_MODIFY_NETWORK_REQUESTED) { sess->nsmf_param.request_indication = OpenAPI_request_indication_NW_REQ_PDU_SES_MOD; - sess->nsmf_param.qos_rule_code = - QOS_RULE_CODE_FROM_PFCP_FLAGS(flags); - sess->nsmf_param.qos_flow_description_code = - QOS_RULE_FLOW_DESCRIPTION_CODE_FROM_PFCP_FLAGS(flags); - - r = smf_sbi_discover_and_send( - OGS_SBI_SERVICE_TYPE_NSMF_PDUSESSION, NULL, - smf_nsmf_pdusession_build_vsmf_update_data, - sess, NULL, 0, NULL); - ogs_expect(r == OGS_OK); - ogs_assert(r != OGS_ERROR); - + } else if (flags & OGS_PFCP_MODIFY_UE_REQUESTED) { + sess->nsmf_param.request_indication = + OpenAPI_request_indication_UE_REQ_PDU_SES_MOD; } else { - ogs_fatal("Invalid PDR-Create flags [0x%llx]", - (long long)flags); + ogs_fatal("Invalid flags [0x%llx]", (long long)flags); ogs_assert_if_reached(); } + sess->nsmf_param.qos_rule_code = + QOS_RULE_CODE_FROM_PFCP_FLAGS(flags); + sess->nsmf_param.qos_flow_description_code = + QOS_RULE_FLOW_DESCRIPTION_CODE_FROM_PFCP_FLAGS(flags); + + if (flags & OGS_PFCP_MODIFY_REMOVE) { + if (flags & OGS_PFCP_MODIFY_INDIRECT) { + ogs_fatal("Invalid flags [0x%llx]", (long long)flags); + ogs_assert_if_reached(); + } + + state = SMF_REMOVE_STATE_NONE; + } else if (flags & OGS_PFCP_MODIFY_CREATE) { + state = SMF_CREATE_STATE_NONE; + } else if (flags & + (OGS_PFCP_MODIFY_TFT_NEW|OGS_PFCP_MODIFY_TFT_ADD| + OGS_PFCP_MODIFY_TFT_REPLACE|OGS_PFCP_MODIFY_TFT_DELETE| + OGS_PFCP_MODIFY_QOS_MODIFY)) { + state = SMF_UPDATE_STATE_NONE; + } + + r = smf_sbi_discover_and_send( + OGS_SBI_SERVICE_TYPE_NSMF_PDUSESSION, NULL, + smf_nsmf_pdusession_build_vsmf_update_data, + sess, NULL, state, NULL); + ogs_expect(r == OGS_OK); + ogs_assert(r != OGS_ERROR); + + if (state == SMF_REMOVE_STATE_NONE) { + smf_bearer_t *next = NULL; + ogs_list_for_each_entry_safe(&sess->qos_flow_to_modify_list, + next, qos_flow, to_modify_node) { + smf_sess_t *sess = smf_sess_find_by_id(qos_flow->sess_id); + ogs_assert(sess); + smf_metrics_inst_by_5qi_add( + &sess->serving_plmn_id, + &sess->s_nssai, + sess->session.qos.index, + SMF_METR_GAUGE_SM_QOSFLOWNBR, -1); + smf_bearer_remove(qos_flow); + } + } + } else { - ogs_fatal("Invalid flags [%lld]", (long long)flags); + ogs_fatal("Invalid flags [0x%llx]", (long long)flags); ogs_assert_if_reached(); } } else if (flags & OGS_PFCP_MODIFY_ACTIVATE) { diff --git a/src/smf/namf-handler.c b/src/smf/namf-handler.c index da5430e46..501218028 100644 --- a/src/smf/namf-handler.c +++ b/src/smf/namf-handler.c @@ -66,9 +66,17 @@ bool smf_namf_comm_handle_n1_n2_message_transfer( if (recvmsg->res_status == OGS_SBI_HTTP_STATUS_OK) { if (N1N2MessageTransferRspData->cause == OpenAPI_n1_n2_message_transfer_cause_N1_N2_TRANSFER_INITIATED) { - if (stream) - sess->n1_n2_modified_stream_id = + if (stream) { + if (sess->vsmf_to_hsmf_modify_stream_id >= + OGS_MIN_POOL_ID && + sess->vsmf_to_hsmf_modify_stream_id <= + OGS_MAX_POOL_ID) + ogs_error("N1 N2 modified stream ID [%d]" + "has not been used yet", + sess->vsmf_to_hsmf_modify_stream_id); + sess->vsmf_to_hsmf_modify_stream_id = ogs_sbi_id_from_stream(stream); + } } else { ogs_error("Not implemented [cause:%d]", N1N2MessageTransferRspData->cause); @@ -82,6 +90,18 @@ bool smf_namf_comm_handle_n1_n2_message_transfer( sess, recvmsg->http.location); else ogs_error("No HTTP Location"); + + if (stream) { + if (sess->vsmf_to_hsmf_modify_stream_id >= + OGS_MIN_POOL_ID && + sess->vsmf_to_hsmf_modify_stream_id <= + OGS_MAX_POOL_ID) + ogs_error("N1 N2 modified stream ID [%d]" + "has not been used yet", + sess->vsmf_to_hsmf_modify_stream_id); + sess->vsmf_to_hsmf_modify_stream_id = + ogs_sbi_id_from_stream(stream); + } } else { ogs_error("Not implemented [cause:%d]", N1N2MessageTransferRspData->cause); @@ -173,9 +193,16 @@ bool smf_namf_comm_handle_n1_n2_message_transfer( smf_namf_comm_send_n1_n2_message_transfer(sess, NULL, ¶m); } else if (N1N2MessageTransferRspData->cause == OpenAPI_n1_n2_message_transfer_cause_N1_N2_TRANSFER_INITIATED) { - if (stream) - sess->n1_n2_released_stream_id = + if (stream) { + if (sess->vsmf_to_hsmf_release_stream_id >= + OGS_MIN_POOL_ID && + sess->vsmf_to_hsmf_release_stream_id <= OGS_MAX_POOL_ID) + ogs_error("N1 N2 released stream ID [%d]" + "has not been used yet", + sess->vsmf_to_hsmf_release_stream_id); + sess->vsmf_to_hsmf_release_stream_id = ogs_sbi_id_from_stream(stream); + } } else { ogs_error("Not implemented [cause:%d]", N1N2MessageTransferRspData->cause); diff --git a/src/smf/ngap-build.c b/src/smf/ngap-build.c index 8aacd7c77..1776b8735 100644 --- a/src/smf/ngap-build.c +++ b/src/smf/ngap-build.c @@ -399,10 +399,10 @@ ogs_pkbuf_t *ngap_build_pdu_session_resource_modify_request_transfer( OpenAPI_list_for_each( sess->h_smf_qos_flows_add_mod_request_list, node) { OpenAPI_qos_flow_add_modify_request_item_t - *qosFlowAddModifyRequestItem = node->data; - if (qosFlowAddModifyRequestItem) { + *qosFlowAddModRequestItem = node->data; + if (qosFlowAddModRequestItem) { OpenAPI_qos_flow_profile_t *qosFlowProfile = - qosFlowAddModifyRequestItem->qos_flow_profile; + qosFlowAddModRequestItem->qos_flow_profile; if (qosFlowProfile) { ogs_qos_t qos; @@ -469,12 +469,12 @@ ogs_pkbuf_t *ngap_build_pdu_session_resource_modify_request_transfer( QosFlowAddOrModifyRequestItem); QosFlowAddOrModifyRequestItem->qosFlowIdentifier = - qosFlowAddModifyRequestItem->qfi; + qosFlowAddModRequestItem->qfi; QosFlowAddOrModifyRequestItem->qosFlowLevelQosParameters = CALLOC(1, sizeof(NGAP_QosFlowLevelQosParameters_t)); - ogs_assert( - QosFlowAddOrModifyRequestItem->qosFlowLevelQosParameters); + ogs_assert(QosFlowAddOrModifyRequestItem-> + qosFlowLevelQosParameters); fill_qos_level_parameters( QosFlowAddOrModifyRequestItem-> @@ -537,20 +537,43 @@ ogs_pkbuf_t *ngap_build_pdu_session_resource_release_request_transfer( QosFlowListWithCause = &ie->value.choice.QosFlowListWithCause; - ogs_list_for_each_entry( - &sess->qos_flow_to_modify_list, qos_flow, to_modify_node) { + /* Home-Routed V-SMF: QoS flow */ + if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) { + OpenAPI_lnode_t *node = NULL; + OpenAPI_list_for_each(sess->h_smf_qos_flows_rel_request_list, node) { + OpenAPI_qos_flow_release_request_item_t + *qosFlowRelRequestItem = node->data; + if (qosFlowRelRequestItem) { - QosFlowWithCauseItem = CALLOC(1, sizeof(*QosFlowWithCauseItem)); - ASN_SEQUENCE_ADD(&QosFlowListWithCause->list, QosFlowWithCauseItem); + QosFlowWithCauseItem = CALLOC(1, sizeof(*QosFlowWithCauseItem)); + ASN_SEQUENCE_ADD(&QosFlowListWithCause->list, + QosFlowWithCauseItem); - qosFlowIdentifier = &QosFlowWithCauseItem->qosFlowIdentifier; + qosFlowIdentifier = &QosFlowWithCauseItem->qosFlowIdentifier; - *qosFlowIdentifier = qos_flow->qfi; + *qosFlowIdentifier = qosFlowRelRequestItem->qfi; - Cause = &QosFlowWithCauseItem->cause; - Cause->present = group; - Cause->choice.radioNetwork = cause; + Cause = &QosFlowWithCauseItem->cause; + Cause->present = group; + Cause->choice.radioNetwork = cause; + } + } + } else { + ogs_list_for_each_entry( + &sess->qos_flow_to_modify_list, qos_flow, to_modify_node) { + QosFlowWithCauseItem = CALLOC(1, sizeof(*QosFlowWithCauseItem)); + ASN_SEQUENCE_ADD(&QosFlowListWithCause->list, QosFlowWithCauseItem); + + qosFlowIdentifier = &QosFlowWithCauseItem->qosFlowIdentifier; + + *qosFlowIdentifier = qos_flow->qfi; + + Cause = &QosFlowWithCauseItem->cause; + Cause->present = group; + Cause->choice.radioNetwork = cause; + + } } return ogs_asn_encode( diff --git a/src/smf/ngap-handler.c b/src/smf/ngap-handler.c index f897f197d..284810b54 100644 --- a/src/smf/ngap-handler.c +++ b/src/smf/ngap-handler.c @@ -28,7 +28,7 @@ int ngap_handle_pdu_session_resource_setup_response_transfer( smf_ue_t *smf_ue = NULL; smf_bearer_t *qos_flow = NULL; - int rv, i; + int rv, i, r; uint32_t remote_dl_teid; ogs_ip_t remote_dl_ip; @@ -162,26 +162,81 @@ int ngap_handle_pdu_session_resource_setup_response_transfer( } if (far_update) { + uint64_t pfcp_flags = OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_ACTIVATE; + if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) { + /* + * UE-requested PDU Session Modification(ACTIVATED) + * + * 1. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_DL_ONLY| + * OGS_PFCP_MODIFY_OUTER_HEADER_REMOVAL|OGS_PFCP_MODIFY_ACTIVATE + * 2. V*: if (sess->up_cnx_state == OpenAPI_up_cnx_state_ACTIVATING) + * pfcp_flags |= OGS_PFCP_MODIFY_FROM_ACTIVATING; + * 3. V: flags & OGS_PFCP_MODIFY_FROM_ACTIVATING ? + * SMF_UPDATE_STATE_HR_ACTIVATED_FROM_ACTIVATING : + * SMF_UPDATE_STATE_HR_ACTIVATED_FROM_NON_ACTIVATING, + * 4. V: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 5. V: smf_nsmf_pdusession_build_hsmf_update_data + * 6. H: smf_nsmf_handle_update_data_in_hsmf + * 7. H: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 8. H: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_DL_ONLY| + * OGS_PFCP_MODIFY_ACTIVATE + * 9. H: ogs_sbi_send_http_status_no_content + * 10. V: case SMF_UPDATE_STATE_HR_ACTIVATED_FROM_ACTIVATING: + * sess->up_cnx_state = OpenAPI_up_cnx_state_ACTIVATED; + * smf_sbi_send_sm_context_updated_data_up_cnx_state( + * OpenAPI_up_cnx_state_ACTIVATED); + * case SMF_UPDATE_STATE_HR_ACTIVATED_FROM_NON_ACTIVATING: + * ogs_sbi_send_http_status_no_content + */ + pfcp_flags |= OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING; + pfcp_flags |= OGS_PFCP_MODIFY_OUTER_HEADER_REMOVAL; + + if (sess->up_cnx_state == OpenAPI_up_cnx_state_ACTIVATING) + pfcp_flags |= OGS_PFCP_MODIFY_FROM_ACTIVATING; + } + ogs_assert(OGS_OK == smf_5gc_pfcp_send_all_pdr_modification_request( - sess, stream, - HOME_ROUTED_ROAMING_IN_VSMF(sess) ? - (OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING| - 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, 0)); + sess, stream, pfcp_flags, 0, 0)); } else { #if 0 /* Modified by pull request #1729 */ /* ACTIVATED Is NOT Included in RESPONSE */ ogs_assert(true == ogs_sbi_send_http_status_no_content(stream)); #else - if (sess->up_cnx_state == OpenAPI_up_cnx_state_ACTIVATING) { - sess->up_cnx_state = OpenAPI_up_cnx_state_ACTIVATED; - smf_sbi_send_sm_context_updated_data_up_cnx_state( - sess, stream, OpenAPI_up_cnx_state_ACTIVATED); + if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) { + sess->nsmf_param.request_indication = + OpenAPI_request_indication_UE_REQ_PDU_SES_MOD; + + sess->nsmf_param.up_cnx_state = OpenAPI_up_cnx_state_ACTIVATED; + + sess->nsmf_param.serving_network = true; + + ogs_assert(OGS_OK == + ogs_sockaddr_to_ip( + sess->local_dl_addr, sess->local_dl_addr6, + &sess->nsmf_param.dl_ip)); + sess->nsmf_param.dl_teid = sess->local_dl_teid; + + sess->nsmf_param.an_type = sess->an_type; + sess->nsmf_param.rat_type = sess->sbi_rat_type; + + r = smf_sbi_discover_and_send( + OGS_SBI_SERVICE_TYPE_NSMF_PDUSESSION, NULL, + smf_nsmf_pdusession_build_hsmf_update_data, sess, stream, + sess->up_cnx_state == OpenAPI_up_cnx_state_ACTIVATING ? + SMF_UPDATE_STATE_ACTIVATED_FROM_ACTIVATING : + SMF_UPDATE_STATE_ACTIVATED_FROM_NON_ACTIVATING, + NULL); + ogs_expect(r == OGS_OK); + ogs_assert(r != OGS_ERROR); } else { - ogs_assert(true == ogs_sbi_send_http_status_no_content(stream)); + if (sess->up_cnx_state == OpenAPI_up_cnx_state_ACTIVATING) { + sess->up_cnx_state = OpenAPI_up_cnx_state_ACTIVATED; + smf_sbi_send_sm_context_updated_data_up_cnx_state( + sess, stream, OpenAPI_up_cnx_state_ACTIVATED); + } else { + ogs_assert(true == ogs_sbi_send_http_status_no_content(stream)); + } } #endif } diff --git a/src/smf/npcf-build.c b/src/smf/npcf-build.c index c0b4f0632..20c9f7d11 100644 --- a/src/smf/npcf-build.c +++ b/src/smf/npcf-build.c @@ -165,12 +165,9 @@ ogs_sbi_request_t *smf_npcf_smpolicycontrol_build_create( 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; - } + if (sess->ue_location_timestamp) + ueLocation.nr_location->ue_location_timestamp = + ogs_sbi_gmtime_string(sess->ue_location_timestamp); SmPolicyContextData.user_location_info = &ueLocation; @@ -405,12 +402,9 @@ ogs_sbi_request_t *smf_npcf_smpolicycontrol_build_delete( 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; - } + if (sess->ue_location_timestamp) + ueLocation.nr_location->ue_location_timestamp = + ogs_sbi_gmtime_string(sess->ue_location_timestamp); SmPolicyDeleteData.user_location_info = &ueLocation; } diff --git a/src/smf/nsmf-build.c b/src/smf/nsmf-build.c index 755c9240b..8a57c1244 100644 --- a/src/smf/nsmf-build.c +++ b/src/smf/nsmf-build.c @@ -166,12 +166,9 @@ ogs_sbi_request_t *smf_nsmf_pdusession_build_create_data( 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; - } + if (sess->ue_location_timestamp) + ueLocation.nr_location->ue_location_timestamp = + ogs_sbi_gmtime_string(sess->ue_location_timestamp); PduSessionCreateData.ue_location = &ueLocation; PduSessionCreateData.ue_time_zone = ogs_sbi_timezone_string(ogs_timezone()); @@ -211,8 +208,8 @@ ogs_sbi_request_t *smf_nsmf_pdusession_build_create_data( goto end; } - ogs_assert(sess->n1smbuf); - rv = ogs_nas_5gsm_decode(&nas_message, sess->n1smbuf); + ogs_assert(sess->n1SmBufFromUe); + rv = ogs_nas_5gsm_decode(&nas_message, sess->n1SmBufFromUe); if (rv == OGS_OK) { n1SmBufFromUe = gsmue_encode_n1_sm_info(&nas_message); @@ -275,11 +272,11 @@ ogs_sbi_request_t *smf_nsmf_pdusession_build_create_data( } else { ogs_error("gsm_encode_n1_sm_info() failed [%d]", rv); ogs_log_hexdump(OGS_LOG_ERROR, - sess->n1smbuf->data, sess->n1smbuf->len); + sess->n1SmBufFromUe->data, sess->n1SmBufFromUe->len); } } else { ogs_error("ogs_nas_5gsm_decode() failed [%d]", rv); - ogs_log_hexdump(OGS_LOG_ERROR, sess->n1smbuf->data, sess->n1smbuf->len); + ogs_log_hexdump(OGS_LOG_ERROR, sess->n1SmBufFromUe->data, sess->n1SmBufFromUe->len); } message.PduSessionCreateData = &PduSessionCreateData; @@ -324,9 +321,9 @@ end: if (PduSessionCreateData.ue_time_zone) ogs_free(PduSessionCreateData.ue_time_zone); - if (sess->n1smbuf) { - ogs_pkbuf_free(sess->n1smbuf); - sess->n1smbuf = NULL; + if (sess->n1SmBufFromUe) { + ogs_pkbuf_free(sess->n1SmBufFromUe); + sess->n1SmBufFromUe = NULL; } if (n1SmBufFromUe) @@ -348,6 +345,7 @@ ogs_sbi_request_t *smf_nsmf_pdusession_build_hsmf_update_data( OpenAPI_hsmf_update_data_t HsmfUpdateData; OpenAPI_ng_ap_cause_t ngApCause; + OpenAPI_tunnel_info_t vcnTunnelInfo; OpenAPI_user_location_t ueLocation; int rv; @@ -368,6 +366,7 @@ ogs_sbi_request_t *smf_nsmf_pdusession_build_hsmf_update_data( memset(&HsmfUpdateData, 0, sizeof(HsmfUpdateData)); memset(&ngApCause, 0, sizeof(ngApCause)); + memset(&vcnTunnelInfo, 0, sizeof(vcnTunnelInfo)); memset(&ueLocation, 0, sizeof(ueLocation)); HsmfUpdateData.request_indication = sess->nsmf_param.request_indication; @@ -375,6 +374,8 @@ ogs_sbi_request_t *smf_nsmf_pdusession_build_hsmf_update_data( HsmfUpdateData.cause = sess->nsmf_param.cause; + HsmfUpdateData.up_cnx_state = sess->nsmf_param.up_cnx_state; + if (sess->nsmf_param.ngap_cause.group) { HsmfUpdateData.ng_ap_cause = &ngApCause; ngApCause.group = sess->nsmf_param.ngap_cause.group; @@ -386,6 +387,32 @@ ogs_sbi_request_t *smf_nsmf_pdusession_build_hsmf_update_data( HsmfUpdateData._5g_mm_cause_value = sess->nsmf_param.gmm_cause; } + if (sess->nsmf_param.serving_network) { + HsmfUpdateData.serving_network = + ogs_sbi_build_plmn_id_nid(&sess->nr_tai.plmn_id); + if (!HsmfUpdateData.serving_network) { + ogs_error("No serving_network"); + goto end; + } + } + + if (sess->nsmf_param.dl_ip.ipv4) + vcnTunnelInfo.ipv4_addr = ogs_ipv4_to_string( + sess->nsmf_param.dl_ip.addr); + + if (sess->nsmf_param.dl_ip.ipv6) + vcnTunnelInfo.ipv6_addr = ogs_ipv6addr_to_string( + sess->nsmf_param.dl_ip.addr6); + + if (vcnTunnelInfo.ipv4_addr || vcnTunnelInfo.ipv6_addr) { + vcnTunnelInfo.gtp_teid = ogs_uint32_to_0string( + sess->nsmf_param.dl_teid); + HsmfUpdateData.vcn_tunnel_info = &vcnTunnelInfo; + } + + HsmfUpdateData.an_type = sess->nsmf_param.an_type; + HsmfUpdateData.rat_type = sess->nsmf_param.rat_type; + if (sess->nsmf_param.ue_location) { ueLocation.nr_location = ogs_sbi_build_nr_location( &sess->nr_tai, &sess->nr_cgi); @@ -393,12 +420,9 @@ ogs_sbi_request_t *smf_nsmf_pdusession_build_hsmf_update_data( 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; - } + if (sess->ue_location_timestamp) + ueLocation.nr_location->ue_location_timestamp = + ogs_sbi_gmtime_string(sess->ue_location_timestamp); HsmfUpdateData.ue_location = &ueLocation; } @@ -410,8 +434,8 @@ ogs_sbi_request_t *smf_nsmf_pdusession_build_hsmf_update_data( } } - if (sess->n1smbuf) { - rv = ogs_nas_5gsm_decode(&nas_message, sess->n1smbuf); + if (sess->n1SmBufFromUe) { + rv = ogs_nas_5gsm_decode(&nas_message, sess->n1SmBufFromUe); if (rv == OGS_OK) { n1SmBufFromUe = gsmue_encode_n1_sm_info(&nas_message); @@ -428,12 +452,12 @@ ogs_sbi_request_t *smf_nsmf_pdusession_build_hsmf_update_data( } else { ogs_error("gsm_encode_n1_sm_info() failed [%d]", rv); ogs_log_hexdump(OGS_LOG_ERROR, - sess->n1smbuf->data, sess->n1smbuf->len); + sess->n1SmBufFromUe->data, sess->n1SmBufFromUe->len); } } else { ogs_error("ogs_nas_5gsm_decode() failed [%d]", rv); ogs_log_hexdump(OGS_LOG_ERROR, - sess->n1smbuf->data, sess->n1smbuf->len); + sess->n1SmBufFromUe->data, sess->n1SmBufFromUe->len); } } @@ -446,6 +470,16 @@ end: if (message.h.uri) ogs_free(message.h.uri); + if (HsmfUpdateData.serving_network) + ogs_sbi_free_plmn_id_nid(HsmfUpdateData.serving_network); + + if (vcnTunnelInfo.ipv4_addr) + ogs_free(vcnTunnelInfo.ipv4_addr); + if (vcnTunnelInfo.ipv6_addr) + ogs_free(vcnTunnelInfo.ipv6_addr); + if (vcnTunnelInfo.gtp_teid) + ogs_free(vcnTunnelInfo.gtp_teid); + if (ueLocation.nr_location) { if (ueLocation.nr_location->ue_location_timestamp) ogs_free(ueLocation.nr_location->ue_location_timestamp); @@ -454,9 +488,9 @@ end: if (HsmfUpdateData.ue_time_zone) ogs_free(HsmfUpdateData.ue_time_zone); - if (sess->n1smbuf) { - ogs_pkbuf_free(sess->n1smbuf); - sess->n1smbuf = NULL; + if (sess->n1SmBufFromUe) { + ogs_pkbuf_free(sess->n1SmBufFromUe); + sess->n1SmBufFromUe = NULL; } return request; @@ -474,8 +508,10 @@ ogs_sbi_request_t *smf_nsmf_pdusession_build_vsmf_update_data( ogs_pkbuf_t *n1SmBufToUe = NULL; OpenAPI_list_t *qosFlowsAddModRequestList = NULL; - OpenAPI_qos_flow_add_modify_request_item_t *qosFlowAddModifyRequestItem = + OpenAPI_qos_flow_add_modify_request_item_t *qosFlowAddModRequestItem = NULL; + OpenAPI_list_t *qosFlowsRelRequestList = NULL; + OpenAPI_qos_flow_release_request_item_t *qosFlowRelRequestItem = NULL; smf_bearer_t *qos_flow = NULL; @@ -501,6 +537,8 @@ ogs_sbi_request_t *smf_nsmf_pdusession_build_vsmf_update_data( qosFlowsAddModRequestList = OpenAPI_list_create(); ogs_assert(qosFlowsAddModRequestList); + qosFlowsRelRequestList = OpenAPI_list_create(); + ogs_assert(qosFlowsRelRequestList); int i = 0; @@ -513,14 +551,11 @@ ogs_sbi_request_t *smf_nsmf_pdusession_build_vsmf_update_data( OpenAPI_arp_t *Arp = NULL; OpenAPI_gbr_qos_flow_information_t *gbrQosFlowInfo = NULL; + char *encoded_qos_rules = NULL; + char *encoded_qos_flow_description = NULL; + ogs_assert(i < OGS_MAX_NUM_OF_BEARER); - qosFlowAddModifyRequestItem = - ogs_calloc(1, sizeof(*qosFlowAddModifyRequestItem)); - ogs_assert(qosFlowAddModifyRequestItem); - - qosFlowAddModifyRequestItem->qfi = qos_flow->qfi; - if (sess->nsmf_param.qos_rule_code) { ogs_nas_qos_rules_t authorized_qos_rules; ogs_nas_qos_rule_t qos_rule; @@ -541,10 +576,10 @@ ogs_sbi_request_t *smf_nsmf_pdusession_build_vsmf_update_data( } enc_len = ogs_base64_encode_len(authorized_qos_rules.length); - qosFlowAddModifyRequestItem->qos_rules = ogs_calloc(1, enc_len); - ogs_assert(qosFlowAddModifyRequestItem->qos_rules); + encoded_qos_rules = ogs_calloc(1, enc_len); + ogs_assert(encoded_qos_rules); ogs_base64_encode( - qosFlowAddModifyRequestItem->qos_rules, + encoded_qos_rules, authorized_qos_rules.buffer, authorized_qos_rules.length); @@ -574,15 +609,71 @@ ogs_sbi_request_t *smf_nsmf_pdusession_build_vsmf_update_data( enc_len = ogs_base64_encode_len( authorized_qos_flow_descriptions.length); - qosFlowAddModifyRequestItem->qos_flow_description = - ogs_calloc(1, enc_len); - ogs_assert(qosFlowAddModifyRequestItem->qos_flow_description); + encoded_qos_flow_description = ogs_calloc(1, enc_len); + ogs_assert(encoded_qos_flow_description); ogs_base64_encode( - qosFlowAddModifyRequestItem->qos_flow_description, + encoded_qos_flow_description, authorized_qos_flow_descriptions.buffer, authorized_qos_flow_descriptions.length); ogs_free(authorized_qos_flow_descriptions.buffer); + } + + if (sess->nsmf_param.qos_rule_code == + OGS_NAS_QOS_CODE_DELETE_EXISTING_QOS_RULE || + sess->nsmf_param.qos_flow_description_code == + OGS_NAS_DELETE_NEW_QOS_FLOW_DESCRIPTION) { + + if (sess->nsmf_param.qos_rule_code != + OGS_NAS_QOS_CODE_DELETE_EXISTING_QOS_RULE || + sess->nsmf_param.qos_flow_description_code != + OGS_NAS_DELETE_NEW_QOS_FLOW_DESCRIPTION) + ogs_error("Invalid qosRule[%d]/qosFlowDesc[%d]", + sess->nsmf_param.qos_rule_code, + sess->nsmf_param.qos_flow_description_code); + + qosFlowRelRequestItem = + ogs_calloc(1, sizeof(*qosFlowRelRequestItem)); + ogs_assert(qosFlowRelRequestItem); + + qosFlowRelRequestItem->qfi = qos_flow->qfi; + + qosFlowRelRequestItem->qos_rules = encoded_qos_rules; + qosFlowRelRequestItem->qos_flow_description = + encoded_qos_flow_description; + + OpenAPI_list_add(qosFlowsRelRequestList, qosFlowRelRequestItem); + + } else if (sess->nsmf_param.qos_rule_code || + sess->nsmf_param.qos_flow_description_code) { + Arp = ogs_calloc(1, sizeof(*Arp)); + ogs_assert(Arp); + if (qos_flow->qos.arp.pre_emption_capability == + OGS_5GC_PRE_EMPTION_ENABLED) + Arp->preempt_cap = + OpenAPI_preemption_capability_MAY_PREEMPT; + else if (qos_flow->qos.arp.pre_emption_capability == + OGS_5GC_PRE_EMPTION_DISABLED) + Arp->preempt_cap = + OpenAPI_preemption_capability_NOT_PREEMPT; + else { + ogs_error("No Arp->preempt_cap"); + goto end; + } + + if (qos_flow->qos.arp.pre_emption_vulnerability == + OGS_5GC_PRE_EMPTION_ENABLED) + Arp->preempt_vuln = + OpenAPI_preemption_vulnerability_PREEMPTABLE; + else if (qos_flow->qos.arp.pre_emption_vulnerability == + OGS_5GC_PRE_EMPTION_DISABLED) + Arp->preempt_vuln = + OpenAPI_preemption_vulnerability_NOT_PREEMPTABLE; + else { + ogs_error("No Arp->preempt_vuln"); + goto end; + } + Arp->priority_level = qos_flow->qos.arp.priority_level; if (qos_flow->qos.mbr.downlink && qos_flow->qos.mbr.uplink && qos_flow->qos.gbr.downlink && qos_flow->qos.gbr.uplink) { @@ -599,55 +690,47 @@ ogs_sbi_request_t *smf_nsmf_pdusession_build_vsmf_update_data( gbrQosFlowInfo->gua_fbr_dl = ogs_sbi_bitrate_to_string( qos_flow->qos.gbr.downlink, OGS_SBI_BITRATE_BPS); } + + qosFlowProfile = ogs_calloc(1, sizeof(*qosFlowProfile)); + ogs_assert(qosFlowProfile); + qosFlowProfile->arp = Arp; + qosFlowProfile->_5qi = qos_flow->qos.index; + qosFlowProfile->gbr_qos_flow_info = gbrQosFlowInfo; + + qosFlowAddModRequestItem = + ogs_calloc(1, sizeof(*qosFlowAddModRequestItem)); + ogs_assert(qosFlowAddModRequestItem); + + qosFlowAddModRequestItem->qfi = qos_flow->qfi; + + qosFlowAddModRequestItem->qos_rules = encoded_qos_rules; + qosFlowAddModRequestItem->qos_flow_description = + encoded_qos_flow_description; + + qosFlowAddModRequestItem->qos_flow_profile = qosFlowProfile; + + OpenAPI_list_add(qosFlowsAddModRequestList, + qosFlowAddModRequestItem); + } else { + ogs_error("Invalid qosRule[%d]/qosFlowDesc[%d]", + sess->nsmf_param.qos_rule_code, + sess->nsmf_param.qos_flow_description_code); } - Arp = ogs_calloc(1, sizeof(*Arp)); - ogs_assert(Arp); - if (qos_flow->qos.arp.pre_emption_capability == - OGS_5GC_PRE_EMPTION_ENABLED) - Arp->preempt_cap = OpenAPI_preemption_capability_MAY_PREEMPT; - else if (qos_flow->qos.arp.pre_emption_capability == - OGS_5GC_PRE_EMPTION_DISABLED) - Arp->preempt_cap = OpenAPI_preemption_capability_NOT_PREEMPT; - else { - ogs_error("No Arp->preempt_cap"); - goto end; - } - - if (qos_flow->qos.arp.pre_emption_vulnerability == - OGS_5GC_PRE_EMPTION_ENABLED) - Arp->preempt_vuln = - OpenAPI_preemption_vulnerability_PREEMPTABLE; - else if (qos_flow->qos.arp.pre_emption_vulnerability == - OGS_5GC_PRE_EMPTION_DISABLED) - Arp->preempt_vuln = - OpenAPI_preemption_vulnerability_NOT_PREEMPTABLE; - else { - ogs_error("No Arp->preempt_vuln"); - goto end; - } - Arp->priority_level = qos_flow->qos.arp.priority_level; - - qosFlowProfile = ogs_calloc(1, sizeof(*qosFlowProfile)); - ogs_assert(qosFlowProfile); - qosFlowProfile->arp = Arp; - qosFlowProfile->_5qi = qos_flow->qos.index; - qosFlowProfile->gbr_qos_flow_info = gbrQosFlowInfo; - - qosFlowAddModifyRequestItem->qos_flow_profile = qosFlowProfile; - - OpenAPI_list_add(qosFlowsAddModRequestList, - qosFlowAddModifyRequestItem); - - if (qosFlowsAddModRequestList->count) - VsmfUpdateData.qos_flows_add_mod_request_list = - qosFlowsAddModRequestList; - else - OpenAPI_list_free(qosFlowsAddModRequestList); - i++; } + if (qosFlowsAddModRequestList->count) + VsmfUpdateData.qos_flows_add_mod_request_list = + qosFlowsAddModRequestList; + else + OpenAPI_list_free(qosFlowsAddModRequestList); + + if (qosFlowsRelRequestList->count) + VsmfUpdateData.qos_flows_rel_request_list = qosFlowsRelRequestList; + else + OpenAPI_list_free(qosFlowsRelRequestList); + break; case OpenAPI_request_indication_NW_REQ_PDU_SES_REL: case OpenAPI_request_indication_UE_REQ_PDU_SES_REL: @@ -681,6 +764,7 @@ end: CLEAR_QOS_FLOWS_ADD_MOD_REQUEST_LIST( VsmfUpdateData.qos_flows_add_mod_request_list); + CLEAR_QOS_FLOWS_REL_REQUEST_LIST(VsmfUpdateData.qos_flows_rel_request_list); return request; } @@ -732,12 +816,9 @@ ogs_sbi_request_t *smf_nsmf_pdusession_build_release_data( 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; - } + if (sess->ue_location_timestamp) + ueLocation.nr_location->ue_location_timestamp = + ogs_sbi_gmtime_string(sess->ue_location_timestamp); ReleaseData.ue_location = &ueLocation; } diff --git a/src/smf/nsmf-handler.c b/src/smf/nsmf-handler.c index ade97c9d7..450e496e3 100644 --- a/src/smf/nsmf-handler.c +++ b/src/smf/nsmf-handler.c @@ -22,6 +22,7 @@ #include "ngap-path.h" #include "pfcp-path.h" #include "local-path.h" +#include "gsm-handler.h" #include "nsmf-handler.h" bool smf_nsmf_handle_create_sm_context( @@ -42,6 +43,7 @@ bool smf_nsmf_handle_create_sm_context( char *fqdn = NULL; uint16_t fqdn_port = 0; ogs_sockaddr_t *addr = NULL, *addr6 = NULL; + char *home_network_domain = NULL; OpenAPI_sm_context_create_data_t *SmContextCreateData = NULL; OpenAPI_nr_location_t *NrLocation = NULL; @@ -351,65 +353,63 @@ bool smf_nsmf_handle_create_sm_context( * the full DNN in LBO and non-roaming scenarios. If the Operator Identifier * is absent, the serving core network operator shall be assumed. */ - if (SmContextCreateData->dnn) { - char *home_network_domain = - ogs_home_network_domain_from_fqdn(SmContextCreateData->dnn); + home_network_domain = + ogs_home_network_domain_from_fqdn(SmContextCreateData->dnn); - if (home_network_domain) { - char dnn_network_identifer[OGS_MAX_DNN_LEN+1]; - uint16_t mcc = 0, mnc = 0; + if (home_network_domain) { + char dnn_network_identifer[OGS_MAX_DNN_LEN+1]; + uint16_t mcc = 0, mnc = 0; - ogs_assert(home_network_domain > SmContextCreateData->dnn); + ogs_assert(home_network_domain > SmContextCreateData->dnn); - ogs_cpystrn(dnn_network_identifer, SmContextCreateData->dnn, - ogs_min(OGS_MAX_DNN_LEN, - home_network_domain - SmContextCreateData->dnn)); + ogs_cpystrn(dnn_network_identifer, SmContextCreateData->dnn, + ogs_min(OGS_MAX_DNN_LEN, + home_network_domain - SmContextCreateData->dnn)); - if (sess->session.name) - ogs_free(sess->session.name); - sess->session.name = ogs_strdup(dnn_network_identifer); - ogs_assert(sess->session.name); + if (sess->session.name) + ogs_free(sess->session.name); + sess->session.name = ogs_strdup(dnn_network_identifer); + ogs_assert(sess->session.name); - if (sess->full_dnn) - ogs_free(sess->full_dnn); - sess->full_dnn = ogs_strdup(SmContextCreateData->dnn); - ogs_assert(sess->full_dnn); + if (sess->full_dnn) + ogs_free(sess->full_dnn); + sess->full_dnn = ogs_strdup(SmContextCreateData->dnn); + ogs_assert(sess->full_dnn); - mcc = ogs_plmn_id_mcc_from_fqdn(sess->full_dnn); - mnc = ogs_plmn_id_mnc_from_fqdn(sess->full_dnn); + mcc = ogs_plmn_id_mcc_from_fqdn(sess->full_dnn); + mnc = ogs_plmn_id_mnc_from_fqdn(sess->full_dnn); - /* - * To generate the Home PLMN ID of the SMF-UE, - * the length of the MNC is obtained - * by comparing the MNC part of the SUPI and full-DNN. - */ - if (mcc && mnc && - strncmp(smf_ue->supi, "imsi-", strlen("imsi-")) == 0) { - int mnc_len = 0; - char buf[OGS_PLMNIDSTRLEN]; + /* + * To generate the Home PLMN ID of the SMF-UE, + * the length of the MNC is obtained + * by comparing the MNC part of the SUPI and full-DNN. + */ + if (mcc && mnc && + strncmp(smf_ue->supi, "imsi-", strlen("imsi-")) == 0) { + int mnc_len = 0; + char buf[OGS_PLMNIDSTRLEN]; - ogs_snprintf(buf, OGS_PLMNIDSTRLEN, "%03d%02d", mcc, mnc); - if (strncmp(smf_ue->supi + 5, buf, strlen(buf)) == 0) - mnc_len = 2; + ogs_snprintf(buf, OGS_PLMNIDSTRLEN, "%03d%02d", mcc, mnc); + if (strncmp(smf_ue->supi + 5, buf, strlen(buf)) == 0) + mnc_len = 2; - ogs_snprintf(buf, OGS_PLMNIDSTRLEN, "%03d%03d", mcc, mnc); - if (strncmp(smf_ue->supi + 5, buf, strlen(buf)) == 0) - mnc_len = 3; + ogs_snprintf(buf, OGS_PLMNIDSTRLEN, "%03d%03d", mcc, mnc); + if (strncmp(smf_ue->supi + 5, buf, strlen(buf)) == 0) + mnc_len = 3; - /* Change Home PLMN for VPLMN */ - if (mnc_len == 2 || mnc_len == 3) - ogs_plmn_id_build(&sess->home_plmn_id, mcc, mnc, mnc_len); - } - } else { - if (sess->session.name) - ogs_free(sess->session.name); - sess->session.name = ogs_strdup(SmContextCreateData->dnn); - ogs_assert(sess->session.name); - - if (sess->full_dnn) - ogs_free(sess->full_dnn); - sess->full_dnn = NULL; + /* Change Home PLMN for VPLMN */ + if (mnc_len == 2 || mnc_len == 3) + ogs_plmn_id_build(&sess->home_plmn_id, mcc, mnc, mnc_len); } + } else { + if (sess->session.name) + ogs_free(sess->session.name); + sess->session.name = ogs_strdup(SmContextCreateData->dnn); + ogs_assert(sess->session.name); + + if (sess->full_dnn) + ogs_free(sess->full_dnn); + sess->full_dnn = NULL; } ogs_assert(SmContextCreateData->serving_nf_id); @@ -598,9 +598,9 @@ bool smf_nsmf_handle_create_sm_context( ul_pdr->precedence = OGS_PFCP_DEFAULT_PDR_PRECEDENCE; /* 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); + if (sess->n1SmBufFromUe) ogs_pkbuf_free(sess->n1SmBufFromUe); + sess->n1SmBufFromUe = ogs_pkbuf_copy(n1smbuf); + ogs_assert(sess->n1SmBufFromUe); ogs_assert(OGS_OK == smf_5gc_pfcp_send_session_establishment_request(sess, NULL, 0)); @@ -611,7 +611,7 @@ bool smf_nsmf_handle_create_sm_context( 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; @@ -644,6 +644,12 @@ bool smf_nsmf_handle_update_sm_context( memset(&sess->nsmf_param, 0, sizeof(sess->nsmf_param)); + /* Remove N1 SM Message From UE */ + if (sess->n1SmBufFromUe) { + ogs_pkbuf_free(sess->n1SmBufFromUe); + sess->n1SmBufFromUe = NULL; + } + if (SmContextUpdateData->ue_location && SmContextUpdateData->ue_location->nr_location) { OpenAPI_nr_location_t *NrLocation = @@ -703,47 +709,24 @@ 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)) { + if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) { + switch (gsm_header->message_type) { + case OGS_NAS_5GS_PDU_SESSION_MODIFICATION_REQUEST: + case OGS_NAS_5GS_PDU_SESSION_RELEASE_REQUEST: /* 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); + sess->n1SmBufFromUe = ogs_pkbuf_copy(n1smbuf); + ogs_assert(sess->n1SmBufFromUe); + break; - /* 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); + default: + break; } - break; - - default: - - /* - * 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; } else if (SmContextUpdateData->n2_sm_info) { @@ -779,6 +762,37 @@ bool smf_nsmf_handle_update_sm_context( return false; } + if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) { + /* + * UE-requested PDU Session Modification(ACTIVATED) + * + * 1. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_DL_ONLY| + * OGS_PFCP_MODIFY_OUTER_HEADER_REMOVAL|OGS_PFCP_MODIFY_ACTIVATE + * 2. V: if (sess->up_cnx_state == OpenAPI_up_cnx_state_ACTIVATING) + * pfcp_flags |= OGS_PFCP_MODIFY_FROM_ACTIVATING; + * 3. V: flags & OGS_PFCP_MODIFY_FROM_ACTIVATING ? + * SMF_UPDATE_STATE_HR_ACTIVATED_FROM_ACTIVATING : + * SMF_UPDATE_STATE_HR_ACTIVATED_FROM_NON_ACTIVATING, + * 4. V: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 5. V: smf_nsmf_pdusession_build_hsmf_update_data + * 6. H: smf_nsmf_handle_update_data_in_hsmf + * 7. H: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 8. H: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_DL_ONLY| + * OGS_PFCP_MODIFY_ACTIVATE + * 9. H: ogs_sbi_send_http_status_no_content + * 10. V: case SMF_UPDATE_STATE_HR_ACTIVATED_FROM_ACTIVATING: + * sess->up_cnx_state = OpenAPI_up_cnx_state_ACTIVATED; + * smf_sbi_send_sm_context_updated_data_up_cnx_state( + * OpenAPI_up_cnx_state_ACTIVATED); + * case SMF_UPDATE_STATE_HR_ACTIVATED_FROM_NON_ACTIVATING: + * ogs_sbi_send_http_status_no_content + */ + if (SmContextUpdateData->ue_location) + sess->nsmf_param.ue_location = true; + if (SmContextUpdateData->ue_time_zone) + sess->nsmf_param.ue_timezone = true; + } + /* * NOTE : The pkbuf created in the SBI message will be removed * from ogs_sbi_message_free(). @@ -801,28 +815,77 @@ bool smf_nsmf_handle_update_sm_context( ********************************************************/ if (sess->ngap_state.pdu_session_resource_release == SMF_NGAP_STATE_DELETE_TRIGGER_UE_REQUESTED) { - /* - * 1. UE->SMF: PDU session release request - * 2. PFCP Session Deletion Request/Response - * 3. AMF/SMF->UE : PDUSessionResourceReleaseCommand + - * PDU session release command - * sess->ngap_state.pdu_session_resource_release is set - * to SMF_NGAP_STATE_DELETE_TRIGGER_UE_REQUESTED - * 4. UE->AMF/SMF : PDUSessionResourceReleaseResponse - * - * If UE sends UEContextReleaseRequest to the AMF/SMF, - * there is no PFCP context in the SMF/UPF. - * - * So, PFCP deactivation is skipped. - */ +/* + * If UE initiates PDU Session Release, PFCP context is already removed. + * In this case, skip PFCP deactivation and only send UP_CNX_STATE=DEACTIVATED. + * + * Typical flow: + * 1. UE -> SMF: PDU Session Release Request + * 2. SMF -> UPF: PFCP Session Deletion + * 3. SMF -> UE : ReleaseCommand (NAS + NGAP) + * 4. UE -> SMF: PDU Session Release Response + * + * If UE sends UEContextReleaseRequest after step 4, + * PFCP session no longer exists in UPF. + */ smf_sbi_send_sm_context_updated_data_up_cnx_state( sess, stream, OpenAPI_up_cnx_state_DEACTIVATED); } else { - ogs_assert(OGS_OK == - smf_5gc_pfcp_send_all_pdr_modification_request( - sess, stream, - OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE, - 0, 0)); + if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) { + /* + * UE-requested PDU Session Modification(DEACTIVATED) + * + * For Home Routed Roaming, delegate PFCP deactivation to H-SMF by + * sending UP_CNX_STATE=DEACTIVATED via HsmfUpdateData. + * + * 1. V*: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 2. V*: smf_nsmf_pdusession_build_hsmf_update_data + * SMF_UPDATE_STATE_HR_DEACTIVATED + * 3. H: smf_nsmf_handle_update_data_in_hsmf + * 4. H: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 5. H: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_DL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 6. H: ogs_sbi_send_http_status_no_content + * 7. V: case SMF_UPDATE_STATE_HR_DEACTIVATED: + * 8. V: smf_sbi_send_sm_context_updated_data_up_cnx_state( + * OpenAPI_up_cnx_state_DEACTIVATED) + */ + sess->nsmf_param.request_indication = + OpenAPI_request_indication_UE_REQ_PDU_SES_MOD; + + sess->nsmf_param.up_cnx_state = + SmContextUpdateData->up_cnx_state; + + if (SmContextUpdateData->ue_location) + sess->nsmf_param.ue_location = true; + if (SmContextUpdateData->ue_time_zone) + sess->nsmf_param.ue_timezone = true; + + if (SmContextUpdateData->ng_ap_cause) { + OpenAPI_ng_ap_cause_t *ngApCause = + SmContextUpdateData->ng_ap_cause; + sess->nsmf_param.ngap_cause.group = ngApCause->group; + sess->nsmf_param.ngap_cause.value = ngApCause->value; + } + + r = smf_sbi_discover_and_send( + OGS_SBI_SERVICE_TYPE_NSMF_PDUSESSION, NULL, + smf_nsmf_pdusession_build_hsmf_update_data, + sess, stream, SMF_UPDATE_STATE_DEACTIVATED, NULL); + ogs_expect(r == OGS_OK); + ogs_assert(r != OGS_ERROR); + + } else { +/* + * For non-HRR sessions, directly send PFCP PDR modification with + * DL-only deactivation to the local UPF. + */ + ogs_assert(OGS_OK == + smf_5gc_pfcp_send_all_pdr_modification_request( + sess, stream, + OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE, + 0, 0)); + } } } else if (SmContextUpdateData->up_cnx_state == @@ -831,9 +894,6 @@ bool smf_nsmf_handle_update_sm_context( /********************************************************* * Handle ACTIVATING ********************************************************/ - OpenAPI_sm_context_updated_data_t SmContextUpdatedData; - OpenAPI_ref_to_binary_data_t n2SmInfo; - if (!OGS_FSM_CHECK(&sess->sm, smf_gsm_state_operational)) { /* * TS29.502 5.2.2.3.2.2 @@ -879,38 +939,45 @@ bool smf_nsmf_handle_update_sm_context( return false; } - memset(&sendmsg, 0, sizeof(sendmsg)); - sendmsg.SmContextUpdatedData = &SmContextUpdatedData; + if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) { + /* + * UE-requested PDU Session Modification(ACTIVATING) + * + * 1. V*: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 2. V*: smf_nsmf_pdusession_build_hsmf_update_data + * SMF_UPDATE_STATE_HR_ACTIVATING + * 3. H: smf_nsmf_handle_update_data_in_hsmf + * 4. H: OpenAPI_request_indication_UE_REQ_PDU_SES_MOD + * 5. H: ogs_sbi_send_http_status_no_content + * 6. V: ngap_build_pdu_session_resource_setup_request_transfer + * 7. V: smf_sbi_send_sm_context_updated_data( + * OpenAPI_up_cnx_state_ACTIVATING, + * OpenAPI_n2_sm_info_type_PDU_RES_SETUP_REQ, n2smbuf) + */ + sess->nsmf_param.request_indication = + OpenAPI_request_indication_UE_REQ_PDU_SES_MOD; - memset(&SmContextUpdatedData, 0, sizeof(SmContextUpdatedData)); - SmContextUpdatedData.up_cnx_state = OpenAPI_up_cnx_state_ACTIVATING; - SmContextUpdatedData.n2_sm_info_type = - OpenAPI_n2_sm_info_type_PDU_RES_SETUP_REQ; - SmContextUpdatedData.n2_sm_info = &n2SmInfo; + sess->nsmf_param.up_cnx_state = + SmContextUpdateData->up_cnx_state; - memset(&n2SmInfo, 0, sizeof(n2SmInfo)); - n2SmInfo.content_id = (char *)OGS_SBI_CONTENT_NGAP_SM_ID; + r = smf_sbi_discover_and_send( + OGS_SBI_SERVICE_TYPE_NSMF_PDUSESSION, NULL, + smf_nsmf_pdusession_build_hsmf_update_data, + sess, stream, SMF_UPDATE_STATE_ACTIVATING, NULL); + ogs_expect(r == OGS_OK); + ogs_assert(r != OGS_ERROR); - sendmsg.num_of_part = 0; + } else { + ogs_pkbuf_t *n2smbuf = + ngap_build_pdu_session_resource_setup_request_transfer( + sess); + ogs_assert(n2smbuf); - sendmsg.part[sendmsg.num_of_part].pkbuf = - ngap_build_pdu_session_resource_setup_request_transfer(sess); - if (sendmsg.part[sendmsg.num_of_part].pkbuf) { - sendmsg.part[sendmsg.num_of_part].content_id = - (char *)OGS_SBI_CONTENT_NGAP_SM_ID; - sendmsg.part[sendmsg.num_of_part].content_type = - (char *)OGS_SBI_CONTENT_NGAP_TYPE; - sendmsg.num_of_part++; + smf_sbi_send_sm_context_updated_data( + sess, stream, + OpenAPI_up_cnx_state_ACTIVATING, 0, NULL, + OpenAPI_n2_sm_info_type_PDU_RES_SETUP_REQ, n2smbuf); } - - response = ogs_sbi_build_response(&sendmsg, OGS_SBI_HTTP_STATUS_OK); - ogs_assert(response); - ogs_assert(true == ogs_sbi_server_send_response(stream, response)); - - for (i = 0; i < sendmsg.num_of_part; i++) - if (sendmsg.part[i].pkbuf) - ogs_pkbuf_free(sendmsg.part[i].pkbuf); - } else { char *strerror = ogs_msprintf("[%s:%d] Invalid upCnxState [%d]", smf_ue->supi, sess->psi, SmContextUpdateData->up_cnx_state); @@ -1037,16 +1104,28 @@ bool smf_nsmf_handle_update_sm_context( } else { 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; - } - + /* + * Network-requested PDU Session Release(DUPLICATED) + * + * 1. V*: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 2. V*: OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT, + * 3. V: OpenAPI_request_indication_NW_REQ_PDU_SES_REL + * 4. V: smf_nsmf_pdusession_build_hsmf_update_data + * 5. H: smf_nsmf_handle_update_data_in_hsmf + * 6. H: OpenAPI_request_indication_NW_REQ_PDU_SES_REL + * 6. H: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT + * 7. H: ogs_sbi_send_http_status_no_content + * 8. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 9. H: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_AMF_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 10. H: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 11. H: SMF_SESS_CLEAR(sess) + * 12. V: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT + * 13. V: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 14. V: ogs_sbi_send_http_status_no_content + * 15. V: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ ogs_assert(OGS_OK == smf_5gc_pfcp_send_all_pdr_modification_request( sess, stream, @@ -1164,6 +1243,8 @@ bool smf_nsmf_handle_create_data_in_hsmf( char *fqdn = NULL; uint16_t fqdn_port = 0; ogs_sockaddr_t *addr = NULL, *addr6 = NULL; + char *home_network_domain = NULL; + OpenAPI_pdu_session_create_data_t *PduSessionCreateData = NULL; OpenAPI_nr_location_t *NrLocation = NULL; OpenAPI_snssai_t *sNssai = NULL; @@ -1232,6 +1313,38 @@ bool smf_nsmf_handle_create_data_in_hsmf( return false; } + home_network_domain = + ogs_home_network_domain_from_fqdn(PduSessionCreateData->dnn); + + if (home_network_domain) { + char dnn_network_identifer[OGS_MAX_DNN_LEN+1]; + + ogs_assert(home_network_domain > PduSessionCreateData->dnn); + + ogs_cpystrn(dnn_network_identifer, PduSessionCreateData->dnn, + ogs_min(OGS_MAX_DNN_LEN, + home_network_domain - PduSessionCreateData->dnn)); + + if (sess->session.name) + ogs_free(sess->session.name); + sess->session.name = ogs_strdup(dnn_network_identifer); + ogs_assert(sess->session.name); + + if (sess->full_dnn) + ogs_free(sess->full_dnn); + sess->full_dnn = ogs_strdup(PduSessionCreateData->dnn); + ogs_assert(sess->full_dnn); + } else { + if (sess->session.name) + ogs_free(sess->session.name); + sess->session.name = ogs_strdup(PduSessionCreateData->dnn); + ogs_assert(sess->session.name); + + if (sess->full_dnn) + ogs_free(sess->full_dnn); + sess->full_dnn = NULL; + } + sNssai = PduSessionCreateData->s_nssai; if (!sNssai) { ogs_error("[%s:%d] No sNssai", smf_ue->supi, sess->psi); @@ -1374,7 +1487,9 @@ bool smf_nsmf_handle_create_data_in_hsmf( if (sess->remote_dl_ip.ipv4 && sess->remote_dl_ip.ipv6) sess->remote_dl_ip.len = OGS_IPV4V6_LEN; - sess->remote_dl_teid = ogs_uint64_from_string_hexadecimal(vcnTunnelInfo->gtp_teid); + if (vcnTunnelInfo->gtp_teid) + sess->remote_dl_teid = + ogs_uint64_from_string_hexadecimal(vcnTunnelInfo->gtp_teid); ogs_debug("vcnTunnelInfo->ipv4 = 0x%x", sess->remote_dl_ip.addr); ogs_log_hexdump(OGS_LOG_DEBUG, sess->remote_dl_ip.addr6, OGS_IPV6_LEN); ogs_debug("vcnTunnelInfo->gtp_teid = 0x%x", sess->remote_dl_teid); @@ -1763,10 +1878,14 @@ bool smf_nsmf_handle_created_data_in_vsmf( if (sess->remote_ul_ip.ipv4 && sess->remote_ul_ip.ipv6) sess->remote_ul_ip.len = OGS_IPV4V6_LEN; - sess->remote_ul_teid = ogs_uint64_from_string_hexadecimal(hcnTunnelInfo->gtp_teid); - ogs_debug("hcnTunnelInfo->ipv4 = 0x%x", sess->remote_ul_ip.addr); - ogs_log_hexdump(OGS_LOG_DEBUG, sess->remote_ul_ip.addr6, OGS_IPV6_LEN); - ogs_debug("hcnTunnelInfo->gtp_teid = 0x%x", sess->remote_ul_teid); + if (hcnTunnelInfo->gtp_teid) { + sess->remote_ul_teid = + ogs_uint64_from_string_hexadecimal(hcnTunnelInfo->gtp_teid); + ogs_debug("hcnTunnelInfo->ipv4 = 0x%x", sess->remote_ul_ip.addr); + ogs_log_hexdump(OGS_LOG_DEBUG, + sess->remote_ul_ip.addr6, OGS_IPV6_LEN); + ogs_debug("hcnTunnelInfo->gtp_teid = 0x%x", sess->remote_ul_teid); + } dl_pdr = qos_flow->dl_pdr; ogs_assert(dl_pdr); @@ -2015,11 +2134,16 @@ bool smf_nsmf_handle_update_data_in_hsmf( OpenAPI_hsmf_update_data_t *HsmfUpdateData = NULL; + OpenAPI_plmn_id_nid_t *servingNetwork = NULL; + OpenAPI_tunnel_info_t *vcnTunnelInfo = NULL; OpenAPI_ref_to_binary_data_t *n1SmInfoFromUe = NULL; ogs_nas_5gs_message_t nas_message; ogs_pkbuf_t *n1SmBufFromUe = NULL; + ogs_nas_5gs_pdu_session_modification_request_t + *pdu_session_modification_request = NULL; + ogs_assert(stream); ogs_assert(message); ogs_assert(sess); @@ -2051,27 +2175,57 @@ bool smf_nsmf_handle_update_data_in_hsmf( 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); + sess->nsmf_param.up_cnx_state = HsmfUpdateData->up_cnx_state; - if (n1SmBufFromUe) { - rv = gsmue_decode_n1_sm_info(&nas_message, n1SmBufFromUe); + vcnTunnelInfo = HsmfUpdateData->vcn_tunnel_info; + if (vcnTunnelInfo) { + if (vcnTunnelInfo->ipv4_addr) { + rv = ogs_ipv4_from_string( + &sess->nsmf_param.dl_ip.addr, vcnTunnelInfo->ipv4_addr); 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); + ogs_error("ogs_ipv4_from_string() [%s] failed", + vcnTunnelInfo->ipv4_addr); 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); + OGS_5GSM_CAUSE_INVALID_MANDATORY_INFORMATION, + "ogs_ipv4_from_string() failed", + vcnTunnelInfo->ipv4_addr, NULL); return false; } + sess->nsmf_param.dl_ip.ipv4 = 1; + sess->nsmf_param.dl_ip.len = OGS_IPV4_LEN; } + if (vcnTunnelInfo->ipv6_addr) { + rv = ogs_ipv6addr_from_string( + sess->nsmf_param.dl_ip.addr6, vcnTunnelInfo->ipv6_addr); + if (rv != OGS_OK) { + ogs_error("ogs_ipv6addr_from_string() [%s] failed", + vcnTunnelInfo->ipv6_addr); + smf_sbi_send_hsmf_update_error(stream, + OGS_SBI_HTTP_STATUS_BAD_REQUEST, OGS_SBI_APP_ERRNO_NULL, + OGS_5GSM_CAUSE_INVALID_MANDATORY_INFORMATION, + "ogs_ipv6addr_from_string() failed", + vcnTunnelInfo->ipv6_addr, NULL); + return false; + } + sess->nsmf_param.dl_ip.ipv6 = 1; + sess->nsmf_param.dl_ip.len = OGS_IPV6_LEN; + } + if (sess->remote_dl_ip.ipv4 && sess->remote_dl_ip.ipv6) + sess->remote_dl_ip.len = OGS_IPV4V6_LEN; + + if (vcnTunnelInfo->gtp_teid) + sess->nsmf_param.dl_teid = + ogs_uint64_from_string_hexadecimal(vcnTunnelInfo->gtp_teid); } + sess->nsmf_param.an_type = HsmfUpdateData->an_type; + sess->nsmf_param.rat_type = HsmfUpdateData->rat_type; + + servingNetwork = HsmfUpdateData->serving_network; + if (servingNetwork && servingNetwork->mcc && servingNetwork->mnc) + sess->nsmf_param.serving_network = true; + if (HsmfUpdateData->ue_location && HsmfUpdateData->ue_location->nr_location) { OpenAPI_nr_location_t *NrLocation = @@ -2108,6 +2262,76 @@ bool smf_nsmf_handle_update_data_in_hsmf( sess->nsmf_param.gmm_cause = HsmfUpdateData->_5g_mm_cause_value; sess->nsmf_param.cause = HsmfUpdateData->cause; + 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; + } + } + } + + switch(nas_message.gsm.h.message_type) { + case OGS_NAS_5GS_PDU_SESSION_MODIFICATION_REQUEST: + pdu_session_modification_request = + &nas_message.gsm.pdu_session_modification_request; + + if (pdu_session_modification_request->presencemask & + (OGS_NAS_5GS_PDU_SESSION_MODIFICATION_REQUEST_REQUESTED_QOS_RULES_PRESENT| + OGS_NAS_5GS_PDU_SESSION_MODIFICATION_REQUEST_REQUESTED_QOS_FLOW_DESCRIPTIONS_PRESENT)) + ogs_list_init(&sess->qos_flow_to_modify_list); + + if (pdu_session_modification_request->presencemask & + OGS_NAS_5GS_PDU_SESSION_MODIFICATION_REQUEST_REQUESTED_QOS_RULES_PRESENT) { + rv = gsm_handle_pdu_session_modification_qos_rules( + sess, + &pdu_session_modification_request->requested_qos_rules, + &sess->nsmf_param.pfcp_flags); + if (rv != OGS_OK) { + ogs_error("[%s:%d] Invalid requested_qos_rules", + 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, + "Invalid requested_qos_rules", smf_ue->supi, NULL); + return false; + } + } + + if (pdu_session_modification_request->presencemask & + OGS_NAS_5GS_PDU_SESSION_MODIFICATION_REQUEST_REQUESTED_QOS_FLOW_DESCRIPTIONS_PRESENT) { + rv = gsm_handle_pdu_session_modification_qos_flow_descriptions( + sess, + &pdu_session_modification_request-> + requested_qos_flow_descriptions, + &sess->nsmf_param.pfcp_flags); + if (rv != OGS_OK) { + ogs_error("[%s:%d] Invalid requested_qos_flow_descriptions", + 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, + "Invalid qos_flow_descriptions", smf_ue->supi, NULL); + return false; + } + } + break; + default: + break; + } + return true; } @@ -2120,6 +2344,7 @@ bool smf_nsmf_handle_update_data_in_vsmf( OpenAPI_vsmf_update_data_t *VsmfUpdateData = NULL; OpenAPI_list_t *qosFlowsAddModRequestList = NULL; + OpenAPI_list_t *qosFlowsRelRequestList = NULL; OpenAPI_qos_flow_profile_t *qosFlowProfile = NULL; OpenAPI_lnode_t *node = NULL; @@ -2196,15 +2421,13 @@ bool smf_nsmf_handle_update_data_in_vsmf( src = node->data; if (!src || !src->qfi || - !src->qos_rules || - !src->qos_flow_description || - !src->qos_flow_profile) { - ogs_error("[%s:%d] No src [%d:%s:%s]", + !(src->qos_flow_description || src->qos_flow_profile)) { + ogs_error("[%s:%d] No src [%d:%s:%p]", smf_ue->supi, sess->psi, src->qfi, - src->qos_rules ? - src->qos_rules : "NULL", src->qos_flow_description ? - src->qos_flow_description : "NULL"); + src->qos_flow_description : "NULL", + src->qos_flow_profile ? + src->qos_flow_profile : NULL); smf_sbi_send_vsmf_update_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, OGS_SBI_APP_ERRNO_NULL, OGS_5GSM_CAUSE_INVALID_MANDATORY_INFORMATION, @@ -2249,18 +2472,125 @@ bool smf_nsmf_handle_update_data_in_vsmf( else OpenAPI_list_free(qosFlowsAddModRequestList); + CLEAR_QOS_FLOWS_REL_REQUEST_LIST( + sess->h_smf_qos_flows_rel_request_list); + + qosFlowsRelRequestList = OpenAPI_list_create(); + ogs_assert(qosFlowsRelRequestList); + OpenAPI_list_for_each(VsmfUpdateData->qos_flows_rel_request_list, node) { + OpenAPI_qos_flow_release_request_item_t *dst = NULL, *src = NULL; + + src = node->data; + if (!src || + !src->qfi || + !src->qos_rules || + !src->qos_flow_description) { + ogs_error("[%s:%d] No src [%d:%s:%s]", + smf_ue->supi, sess->psi, src->qfi, + src->qos_rules ? + src->qos_rules : "NULL", + src->qos_flow_description ? + src->qos_flow_description : "NULL"); + 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 src", smf_ue->supi, NULL); + return false; + } + + dst = OpenAPI_qos_flow_release_request_item_copy(dst, src); + ogs_assert(dst); + OpenAPI_list_add(qosFlowsRelRequestList, dst); + } + + if (qosFlowsRelRequestList->count) + sess->h_smf_qos_flows_rel_request_list = qosFlowsRelRequestList; + else + OpenAPI_list_free(qosFlowsRelRequestList); + sess->pti = OGS_NAS_PROCEDURE_TRANSACTION_IDENTITY_UNASSIGNED; + /* + * Network-requested PDU Session Modification + * + * 1. H: OpenAPI_request_indication_NW_REQ_PDU_SES_MOD + * QOS_RULE_CODE_FROM_PFCP_FLAGS + * QOS_RULE_FLOW_DESCRIPTION_CODE_FROM_PFCP_FLAGS + * 2. H: smf_nsmf_pdusession_build_vsmf_update_data + * 3. V: smf_nsmf_handle_update_data_in_vsmf + * 4. V*: gsm_build_pdu_session_modification_command+ + * ngap_build_pdu_session_resource_modify_request_transfer + * 5. V: OpenAPI_n2_sm_info_type_PDU_RES_MOD_RSP + * if (sess->up_cnx_state == OpenAPI_up_cnx_state_ACTIVATING) + * sess->up_cnx_state = OpenAPI_up_cnx_state_ACTIVATED; + * smf_sbi_send_sm_context_updated_data_up_cnx_state( + * OpenAPI_up_cnx_state_ACTIVATED) + * else + * ogs_sbi_send_http_status_no_content(stream) + * 6. V: ogs_sbi_send_http_status_no_content(stream) + * OGS_NAS_5GS_PDU_SESSION_MODIFICATION_COMPLETE: + * ogs_sbi_send_http_status_no_content(n1_n2_modified_stream)); + * 7. V: case OGS_EVENT_SBI_CLIENT + * CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 8. H: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING| + * OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_ACTIVATE + */ memset(¶m, 0, sizeof(param)); + param.state = SMF_NETWORK_REQUESTED_QOS_FLOW_MODIFICATION; param.n1smbuf = gsm_build_pdu_session_modification_command(sess, 0, 0); ogs_assert(param.n1smbuf); - param.n2smbuf = - ngap_build_pdu_session_resource_modify_request_transfer(sess, - qosFlowProfile->gbr_qos_flow_info ? true : false); - ogs_assert(param.n2smbuf); - smf_namf_comm_send_n1_n2_message_transfer(sess, stream, ¶m); + if (sess->h_smf_qos_flows_rel_request_list) { + param.n2smbuf = + ngap_build_pdu_session_resource_release_request_transfer( + sess, NGAP_Cause_PR_nas, NGAP_CauseNas_normal_release); + ogs_assert(param.n2smbuf); + } else if (sess->h_smf_qos_flows_add_mod_request_list) { + param.n2smbuf = + ngap_build_pdu_session_resource_modify_request_transfer(sess, + qosFlowProfile->gbr_qos_flow_info ? true : false); + ogs_assert(param.n2smbuf); + } else + ogs_error("No LIST"); + + if (sess->nsmf_param.request_indication == + OpenAPI_request_indication_NW_REQ_PDU_SES_MOD) { + if (sess->establishment_accept_sent == true) { + smf_namf_comm_send_n1_n2_message_transfer(sess, stream, ¶m); + } else { + if (sess->pending_modification_xact) + ogs_sbi_xact_remove(sess->pending_modification_xact); + + sess->pending_modification_xact = + smf_namf_comm_create_n1_n2_message_xact( + sess, stream, ¶m); + } + } else if (sess->nsmf_param.request_indication == + OpenAPI_request_indication_UE_REQ_PDU_SES_MOD) { + ogs_sbi_stream_t *amf_to_vsmf_modify_stream = NULL; + if (sess->amf_to_vsmf_modify_stream_id >= OGS_MIN_POOL_ID && + sess->amf_to_vsmf_modify_stream_id <= OGS_MAX_POOL_ID) + amf_to_vsmf_modify_stream = + ogs_sbi_stream_find_by_id( + sess->amf_to_vsmf_modify_stream_id); + + if (amf_to_vsmf_modify_stream) { + smf_sbi_send_sm_context_updated_data_n1_n2_message( + sess, amf_to_vsmf_modify_stream, param.n1smbuf, + OpenAPI_n2_sm_info_type_PDU_RES_MOD_REQ, param.n2smbuf); + + sess->amf_to_vsmf_modify_stream_id = OGS_INVALID_POOL_ID; + + if (sess->vsmf_to_hsmf_modify_stream_id >= OGS_MIN_POOL_ID && + sess->vsmf_to_hsmf_modify_stream_id <= OGS_MAX_POOL_ID) + ogs_error("N1 N2 modified stream ID [%d]" + "has not been used yet", + sess->vsmf_to_hsmf_modify_stream_id); + sess->vsmf_to_hsmf_modify_stream_id = + ogs_sbi_id_from_stream(stream); + } + } break; case OpenAPI_request_indication_UE_REQ_PDU_SES_REL: diff --git a/src/smf/sbi-path.c b/src/smf/sbi-path.c index d9a9f0a7d..0c53d07e7 100644 --- a/src/smf/sbi-path.c +++ b/src/smf/sbi-path.c @@ -236,14 +236,13 @@ int smf_sbi_discover_and_send( return OGS_OK; } -void smf_namf_comm_send_n1_n2_message_transfer( +ogs_sbi_xact_t *smf_namf_comm_create_n1_n2_message_xact( 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; ogs_sbi_discovery_option_t *discovery_option = NULL; - int r; ogs_assert(sess); smf_ue = smf_ue_find_by_id(sess->smf_ue_id); @@ -264,8 +263,8 @@ void smf_namf_comm_send_n1_n2_message_transfer( (ogs_sbi_build_f)smf_namf_comm_build_n1_n2_message_transfer, sess, param); if (!xact) { - ogs_error("smf_namf_comm_send_n1_n2_message_transfer() failed"); - return; + ogs_error("ogs_sbi_xact_add() failed"); + return NULL; } xact->state = param->state; @@ -276,6 +275,27 @@ void smf_namf_comm_send_n1_n2_message_transfer( xact->assoc_stream_id <= OGS_MAX_POOL_ID); } + return xact; +} + +void smf_namf_comm_send_n1_n2_message_transfer( + smf_sess_t *sess, ogs_sbi_stream_t *stream, + smf_n1_n2_message_transfer_param_t *param) +{ + ogs_sbi_xact_t *xact = NULL; + int r; + + ogs_assert(sess); + ogs_assert(param); + ogs_assert(param->state); + ogs_assert(param->n1smbuf || param->n2smbuf); + + xact = smf_namf_comm_create_n1_n2_message_xact(sess, stream, param); + if (!xact) { + ogs_error("smf_namf_comm_create_n1_n2_message_xact() failed"); + return; + } + r = ogs_sbi_discover_and_send(xact); if (r != OGS_OK) { ogs_error("smf_namf_comm_send_n1_n2_message_transfer() failed"); diff --git a/src/smf/sbi-path.h b/src/smf/sbi-path.h index 307f0f54a..cebcf3c80 100644 --- a/src/smf/sbi-path.h +++ b/src/smf/sbi-path.h @@ -36,12 +36,95 @@ void smf_sbi_close(void); bool smf_sbi_send_request( ogs_sbi_nf_instance_t *nf_instance, ogs_sbi_xact_t *xact); + +/* + * PFCP delete triggers are defined in lib/pfcp/xact.h (values 1–7). + * To avoid overlap with OGS_PFCP_DELETE_TRIGGER_*, SMF states use: + * - UPDATE_STATE_BASE at 0x10–0x14 + * - UECM_STATE_BASE at 0x20–0x23 + * HR flag is bit 7 (0x80). + */ + +/* Common HR flag (bit 7) */ +#define SMF_STATE_HR (1U << 7) /* 0x80 */ + +/* Base offset for SMF_CREATE states */ +#define SMF_CREATE_STATE_BASE 0x10U /* CREATE at 0x10 */ + +#define SMF_CREATE_STATE_NONE \ + (SMF_CREATE_STATE_BASE + 0x00U) /* 0x00 */ + +/* Base offset for SMF_UPDATE states */ +#define SMF_UPDATE_STATE_BASE 0x20U /* UPDATE at 0x20–0x24 */ + +/* SMF_UPDATE base states */ +#define SMF_UPDATE_STATE_NONE \ + (SMF_UPDATE_STATE_BASE + 0x00U) /* 0x00 */ +#define SMF_UPDATE_STATE_ACTIVATING \ + (SMF_UPDATE_STATE_BASE + 0x01U) /* 0x01 */ +#define SMF_UPDATE_STATE_ACTIVATED_FROM_ACTIVATING \ + (SMF_UPDATE_STATE_BASE + 0x02U) /* 0x02 */ +#define SMF_UPDATE_STATE_ACTIVATED_FROM_NON_ACTIVATING \ + (SMF_UPDATE_STATE_BASE + 0x03U) /* 0x03 */ +#define SMF_UPDATE_STATE_DEACTIVATED \ + (SMF_UPDATE_STATE_BASE + 0x04U) /* 0x04 */ +#define SMF_UPDATE_STATE_UE_REQ_MOD \ + (SMF_UPDATE_STATE_BASE + 0x05U) /* 0x05 */ + +/* Base offset for SMF_REMOVE states */ +#define SMF_REMOVE_STATE_BASE 0x30U /* REMOVE at 0x30 */ + +#define SMF_REMOVE_STATE_NONE \ + (SMF_REMOVE_STATE_BASE + 0x00U) /* 0x00 */ + +/* Base offset for SMF_UECM states */ +#define SMF_UECM_STATE_BASE 0x40U /* UECM at 0x40–0x43 */ + +/* SMF_UECM base states */ +#define SMF_UECM_STATE_NONE \ + (SMF_UECM_STATE_BASE + 0x00U) /* 0x20 */ +#define SMF_UECM_STATE_REGISTERED \ + (SMF_UECM_STATE_BASE + 0x01U) /* 0x21 */ +#define SMF_UECM_STATE_DEREG_BY_AMF \ + (SMF_UECM_STATE_BASE + 0x02U) /* 0x22 */ +#define SMF_UECM_STATE_DEREG_BY_N1N2 \ + (SMF_UECM_STATE_BASE + 0x03U) /* 0x23 */ + +/* HR variants for SMF_UECM */ +#define SMF_UECM_STATE_REGISTERED_HR \ + (SMF_UECM_STATE_REGISTERED | SMF_STATE_HR) /* 0xC1 */ +#define SMF_UECM_STATE_DEREG_BY_AMF_HR \ + (SMF_UECM_STATE_DEREG_BY_AMF | SMF_STATE_HR) /* 0xC2 */ +#define SMF_UECM_STATE_DEREG_BY_N1N2_HR \ + (SMF_UECM_STATE_DEREG_BY_N1N2 | SMF_STATE_HR) /* 0xC3 */ + int smf_sbi_discover_and_send( ogs_sbi_service_type_e service_type, ogs_sbi_discovery_option_t *discovery_option, ogs_sbi_request_t *(*build)(smf_sess_t *sess, void *data), smf_sess_t *sess, ogs_sbi_stream_t *stream, int state, void *data); +/** + * 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_STATE_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_STATE_HR); +} + +ogs_sbi_xact_t *smf_namf_comm_create_n1_n2_message_xact( + smf_sess_t *sess, ogs_sbi_stream_t *stream, + smf_n1_n2_message_transfer_param_t *param); void smf_namf_comm_send_n1_n2_message_transfer( smf_sess_t *sess, ogs_sbi_stream_t *stream, smf_n1_n2_message_transfer_param_t *param); diff --git a/src/smf/smf-sm.c b/src/smf/smf-sm.c index 92e56373d..931dfeef2 100644 --- a/src/smf/smf-sm.c +++ b/src/smf/smf-sm.c @@ -1145,8 +1145,89 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e) 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) { + /* + * Network-requested PDU Session Release(DUPLICATED) + * + * 1. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 2. V: OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT, + * 3. V: OpenAPI_request_indication_NW_REQ_PDU_SES_REL + * 4. V: smf_nsmf_pdusession_build_hsmf_update_data + * 5. H: smf_nsmf_handle_update_data_in_hsmf + * 6. H: OpenAPI_request_indication_NW_REQ_PDU_SES_REL + * 6. H: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT + * 7. H: ogs_sbi_send_http_status_no_content + * 8. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 9. H: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_AMF_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 10. H: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 11. H*: SMF_SESS_CLEAR(sess) + * 12. V: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT + * 13. V: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 14. V: ogs_sbi_send_http_status_no_content + * 15. V: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + * + * Network-requested PDU Session Release + * + * 1. V: smf_nsmf_handle_release_sm_context + * 2. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 3. V: OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT + * 4. V: smf_nsmf_pdusession_build_release_data + * 5. H: smf_nsmf_handle_release_data_in_hsmf + * 6. H: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT + * 7. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 8. H: ogs_sbi_send_http_status_no_content + * 9. H: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_AMF_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 10. H: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 11. H*: SMF_SESS_CLEAR(sess) + * 12. V: smf_nsmf_handle_release_data_in_hsmf + * 13. V: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT + * 14. V: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 15. V: ogs_sbi_send_http_status_no_content + * 16. V: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ SMF_SESS_CLEAR(sess); } else if (state == SMF_UECM_STATE_DEREG_BY_N1N2_HR) { + /* + * UE-requested PDU Session Release + * + * 1. V: OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING|OGS_PFCP_MODIFY_UL_ONLY| + * OGS_PFCP_MODIFY_DEACTIVATE + * 2. V: OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 3. V: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 4. V: smf_nsmf_pdusession_build_hsmf_update_data + * 5. H: smf_nsmf_handle_update_data_in_hsmf + * 6. H: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 6. H: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 7. H: ogs_sbi_send_http_status_no_content + * 8. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 9. H: smf_nsmf_pdusession_build_vsmf_update_data + * 10. H: OGS_FSM_TRAN(s, smf_gsm_state_wait_5gc_n1_n2_release); + * 11. V: smf_nsmf_handle_update_data_in_vsmf + * 12. V: OpenAPI_request_indication_UE_REQ_PDU_SES_REL + * 13. V: e->h.sbi.state = OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED + * 14. V: OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion) + * 15. V: ngap_build_pdu_session_resource_release_command_transfer+ + * gsm_build_pdu_session_release_command + * 16 V: OGS_FSM_TRAN(&sess->sm, smf_gsm_state_wait_5gc_n1_n2_release) + * 17. V: ogs_sbi_send_http_status_no_content(stream) + * 18. V: case OpenAPI_n2_sm_info_type_PDU_RES_REL_RSP: + * case OGS_NAS_5GS_PDU_SESSION_RELEASE_COMPLETE: + * 19. V: ogs_sbi_send_http_status_no_content(n1_n2_released_stream) + * 20. V: OGS_FSM_TRAN(s, smf_gsm_state_5gc_session_will_deregister); + * 21. H: case OGS_EVENT_SBI_CLIENT: + * 22. H: CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 23. H: smf_sbi_cleanup_session(SMF_UECM_STATE_DEREG_BY_N1N2_HR + * SMF_SBI_CLEANUP_MODE_POLICY_FIRST); + * 24. H*: smf_sbi_send_status_notify+SMF_SESS_CLEAR(sess) + * 25. V: case OGS_EVENT_SBI_SERVER: + * 26. V: CASE(OGS_SBI_RESOURCE_NAME_VSMF_PDU_SESSIONS) + * 27. V: ogs_sbi_send_http_status_no_content+ + * smf_sbi_send_sm_context_status_notify + * 28. V: OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + */ ogs_assert(true == smf_sbi_send_status_notify(sess)); SMF_SESS_CLEAR(sess); } diff --git a/tests/common/context.c b/tests/common/context.c index 2ad12ad09..2ac7bc2d8 100644 --- a/tests/common/context.c +++ b/tests/common/context.c @@ -1886,7 +1886,7 @@ bson_t *test_db_new_session(test_ue_t *test_ue) "pre_emption_capability", BCON_INT32(1), "}", "}", -#if 1 +#if 0 "lbo_roaming_allowed", BCON_BOOL(true), #else "lbo_roaming_allowed", BCON_BOOL(false), @@ -1953,7 +1953,7 @@ bson_t *test_db_new_session(test_ue_t *test_ue) "description", BCON_UTF8("permit out udp from 10.200.136.98/32 1-65535 to assigned 50021"), "}", "]", "}", "]", -#if 1 +#if 0 "lbo_roaming_allowed", BCON_BOOL(true), #else "lbo_roaming_allowed", BCON_BOOL(false), @@ -2025,6 +2025,11 @@ bson_t *test_db_new_ims(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 "}", "{", "name", BCON_UTF8("ims"), @@ -2109,7 +2114,7 @@ bson_t *test_db_new_ims(test_ue_t *test_ue) "}", "}", "]", -#if 1 +#if 0 "lbo_roaming_allowed", BCON_BOOL(true), #else "lbo_roaming_allowed", BCON_BOOL(false), diff --git a/tests/vonr/af-test.c b/tests/vonr/af-test.c index 5f49fe8bd..306bb2751 100644 --- a/tests/vonr/af-test.c +++ b/tests/vonr/af-test.c @@ -3966,6 +3966,25 @@ static void test7_func(abts_case *tc, void *data) test_ue_remove(test_ue); } +/* + * Disable test8_func when running Home Routed Roaming tests, + * to prevent V-SMF from skipping Update Response (step 14) + * and avoid subsequent SBI timeout and PFCP No Context errors. + */ +#define HOME_ROUTED_ROAMING_TEST 0 + +#if !HOME_ROUTED_ROAMING_TEST +/** + * test8_func: + * - Simulates absence of N2 Resource Release Ack (step 8) + * and PDU Session Release Accept (step 11) from gNB/UE. + * - In Home Routed Roaming, V-SMF will not send the + * Nsmf_PDUSession_Update Response (step 14), causing + * the SBI client to timeout after 10 seconds. + * - Subsequent deregistration triggers a PFCP deactivation + * modification, but UPF has no session, leading to + * a No Context error. + */ static void test8_func(abts_case *tc, void *data) { int rv; @@ -4327,12 +4346,19 @@ static void test8_func(abts_case *tc, void *data) /* Clear Test UE Context */ test_ue_remove(test_ue); } +#endif abts_suite *test_af(abts_suite *suite) { suite = ADD_SUITE(suite) +/** + * abts_suite test_af: + * - Always run test1_func through test7_func. + * - Conditionally include test8_func based on + * HOME_ROUTED_ROAMING_TEST flag. + */ abts_run_test(suite, test1_func, NULL); abts_run_test(suite, test2_func, NULL); abts_run_test(suite, test3_func, NULL); @@ -4340,7 +4366,9 @@ abts_suite *test_af(abts_suite *suite) abts_run_test(suite, test5_func, NULL); abts_run_test(suite, test6_func, NULL); abts_run_test(suite, test7_func, NULL); +#if !HOME_ROUTED_ROAMING_TEST abts_run_test(suite, test8_func, NULL); +#endif return suite; } diff --git a/tests/vonr/session-test.c b/tests/vonr/session-test.c index 92cee8796..828b07b0e 100644 --- a/tests/vonr/session-test.c +++ b/tests/vonr/session-test.c @@ -1367,6 +1367,8 @@ static void test4_func(abts_case *tc, void *data) bson_t *doc = NULL; + NGAP_ProcedureCode_t ngap_procedure_code; + /* Setup Test UE & Session Context */ memset(&mobile_identity_suci, 0, sizeof(mobile_identity_suci)); @@ -1551,23 +1553,62 @@ static void test4_func(abts_case *tc, void *data) rv = testgnb_ngap_send(ngap, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - /* Receive PDUSessionResourceSetupRequest + + /* + * Receive PDUSessionResourceSetupRequest + * DL NAS transport + - * PDU session establishment accept */ + * PDU session establishment accept + * + * OR + * + * Receive PDUSessionResourceModifyRequest + + * DL NAS transport + + * PDU session modification command + */ recvbuf = testgnb_ngap_read(ngap); ABTS_PTR_NOTNULL(tc, recvbuf); testngap_recv(test_ue, recvbuf); - ABTS_INT_EQUAL(tc, - NGAP_ProcedureCode_id_PDUSessionResourceSetup, - test_ue->ngap_procedure_code); - /* Receive PDU session establishment accept */ + ngap_procedure_code = test_ue->ngap_procedure_code; + + /* + * Receive PDUSessionResourceSetupRequest + + * DL NAS transport + + * PDU session establishment accept + * + * OR + * + * Receive PDUSessionResourceModifyRequest + + * DL NAS transport + + * PDU session modification command + */ recvbuf = testgnb_ngap_read(ngap); ABTS_PTR_NOTNULL(tc, recvbuf); testngap_recv(test_ue, recvbuf); + + ngap_procedure_code += test_ue->ngap_procedure_code; + + /* + * Receive PDUSessionResourceSetupRequest + + * DL NAS transport + + * PDU session establishment accept + * + * OR + * + * Receive PDUSessionResourceModifyRequest + + * DL NAS transport + + * PDU session modification command + */ + recvbuf = testgnb_ngap_read(ngap); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + ngap_procedure_code += test_ue->ngap_procedure_code; + ABTS_INT_EQUAL(tc, - NGAP_ProcedureCode_id_PDUSessionResourceSetup, - test_ue->ngap_procedure_code); + NGAP_ProcedureCode_id_PDUSessionResourceSetup + + NGAP_ProcedureCode_id_PDUSessionResourceSetup + + NGAP_ProcedureCode_id_PDUSessionResourceModify, + ngap_procedure_code); /* Send GTP-U ICMP Packet */ qos_flow1 = test_qos_flow_find_by_qfi(sess5, 1); @@ -1601,16 +1642,6 @@ static void test4_func(abts_case *tc, void *data) ABTS_PTR_NOTNULL(tc, recvbuf); ogs_pkbuf_free(recvbuf); - /* Receive PDUSessionResourceModifyRequest + - * DL NAS transport + - * PDU session modification command */ - recvbuf = testgnb_ngap_read(ngap); - ABTS_PTR_NOTNULL(tc, recvbuf); - testngap_recv(test_ue, recvbuf); - ABTS_INT_EQUAL(tc, - NGAP_ProcedureCode_id_PDUSessionResourceModify, - test_ue->ngap_procedure_code); - /* Send PDU session resource modify response */ qos_flow2 = test_qos_flow_find_by_qfi(sess6, 2); ogs_assert(qos_flow2);