[HR] Support Xn/N2 handover for Home-Routed Roaming (Direct Forwarding only) (#2194)

This commit adds Xn and N2 handover procedures to the Home-Routed Roaming code.
Direct forwarding is now fully operational.

Indirect forwarding for N2 handovers is not yet supported.

To preserve the GTP-U header and extension header (even without QER)
along the source gNB -> V-UPF -> target gNB path, future work will create
PDRs without Outer Header Removal IE and FARs without Outer Header Creation IE
and implement the necessary UPF logic.
This commit is contained in:
Sukchan Lee
2025-07-14 22:03:32 +09:00
parent f21bc06054
commit 7b75746fe8
11 changed files with 278 additions and 85 deletions

View File

@@ -2328,11 +2328,11 @@ void smf_sess_create_indirect_data_forwarding(smf_sess_t *sess)
far->apply_action = OGS_PFCP_APPLY_ACTION_FORW; far->apply_action = OGS_PFCP_APPLY_ACTION_FORW;
qer = qos_flow->qer; qer = qos_flow->qer;
ogs_assert(qer); if (qer) {
ogs_pfcp_pdr_associate_qer(pdr, qer);
ogs_pfcp_pdr_associate_qer(pdr, qer); pdr->qfi = qos_flow->qfi;
}
pdr->qfi = qos_flow->qfi;
ogs_assert(sess->pfcp_node); ogs_assert(sess->pfcp_node);
if (sess->pfcp_node->up_function_features.ftup) { if (sess->pfcp_node->up_function_features.ftup) {

View File

@@ -1665,6 +1665,33 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e)
ogs_sbi_send_http_status_no_content( ogs_sbi_send_http_status_no_content(
stream)); stream));
break; break;
case SMF_UPDATE_STATE_ACTIVATED_FROM_XN_HANDOVER:
ogs_pkbuf_t *n2smbuf =
ngap_build_path_switch_request_ack_transfer(
sess);
ogs_assert(n2smbuf);
smf_sbi_send_sm_context_updated_data_n2smbuf(
sess, stream,
OpenAPI_n2_sm_info_type_PATH_SWITCH_REQ_ACK,
n2smbuf);
break;
case SMF_UPDATE_STATE_ACTIVATED_FROM_N2_HANDOVER:
if (smf_sess_have_indirect_data_forwarding(sess) ==
true) {
ogs_assert(OGS_OK ==
smf_5gc_pfcp_send_all_pdr_modification_request(
sess, stream,
OGS_PFCP_MODIFY_INDIRECT|
OGS_PFCP_MODIFY_REMOVE,
0,
ogs_local_conf()->
time.handover.duration));
}
smf_sbi_send_sm_context_updated_data_ho_state(
sess, stream, OpenAPI_ho_state_COMPLETED);
break;
case SMF_UPDATE_STATE_UE_REQ_MOD: case SMF_UPDATE_STATE_UE_REQ_MOD:
if (sess->amf_to_vsmf_modify_stream_id >= if (sess->amf_to_vsmf_modify_stream_id >=
OGS_MIN_POOL_ID && OGS_MIN_POOL_ID &&
@@ -2727,6 +2754,33 @@ void smf_gsm_state_wait_pfcp_deletion(ogs_fsm_t *s, smf_event_t *e)
ogs_sbi_send_http_status_no_content( ogs_sbi_send_http_status_no_content(
stream)); stream));
break; break;
case SMF_UPDATE_STATE_ACTIVATED_FROM_XN_HANDOVER:
ogs_pkbuf_t *n2smbuf =
ngap_build_path_switch_request_ack_transfer(
sess);
ogs_assert(n2smbuf);
smf_sbi_send_sm_context_updated_data_n2smbuf(
sess, stream,
OpenAPI_n2_sm_info_type_PATH_SWITCH_REQ_ACK,
n2smbuf);
break;
case SMF_UPDATE_STATE_ACTIVATED_FROM_N2_HANDOVER:
if (smf_sess_have_indirect_data_forwarding(sess) ==
true) {
ogs_assert(OGS_OK ==
smf_5gc_pfcp_send_all_pdr_modification_request(
sess, stream,
OGS_PFCP_MODIFY_INDIRECT|
OGS_PFCP_MODIFY_REMOVE,
0,
ogs_local_conf()->
time.handover.duration));
}
smf_sbi_send_sm_context_updated_data_ho_state(
sess, stream, OpenAPI_ho_state_COMPLETED);
break;
default: default:
ogs_fatal("Unknown state [0x%x]", e->h.sbi.state); ogs_fatal("Unknown state [0x%x]", e->h.sbi.state);
ogs_assert_if_reached(); ogs_assert_if_reached();

View File

@@ -409,7 +409,36 @@ void smf_5gc_n4_handle_session_modification_response(
if (flags & OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING) { if (flags & OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING) {
if (flags & OGS_PFCP_MODIFY_ACTIVATE) { if (flags & OGS_PFCP_MODIFY_ACTIVATE) {
if (flags & OGS_PFCP_MODIFY_DL_ONLY) { if ((flags & OGS_PFCP_MODIFY_XN_HANDOVER) ||
(flags & OGS_PFCP_MODIFY_N2_HANDOVER)) {
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_XN_HANDOVER ?
SMF_UPDATE_STATE_ACTIVATED_FROM_XN_HANDOVER :
SMF_UPDATE_STATE_ACTIVATED_FROM_N2_HANDOVER,
NULL);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else if (flags & OGS_PFCP_MODIFY_DL_ONLY) {
/* /*
* UE-requested PDU Session Modification(ACTIVATED) * UE-requested PDU Session Modification(ACTIVATED)
* *

View File

@@ -307,12 +307,10 @@ ogs_pkbuf_t *ngap_build_pdu_session_resource_setup_request_transfer(
qos.arp.priority_level = qosFlowProfile->arp->priority_level; qos.arp.priority_level = qosFlowProfile->arp->priority_level;
if (qosFlowProfile->arp->preempt_cap == if (qosFlowProfile->arp->preempt_cap ==
OpenAPI_preemption_capability_NOT_PREEMPT) OpenAPI_preemption_capability_NOT_PREEMPT)
qos.arp.pre_emption_capability = qos.arp.pre_emption_capability = OGS_5GC_PRE_EMPTION_DISABLED;
OGS_5GC_PRE_EMPTION_DISABLED;
else if (qosFlowProfile->arp->preempt_cap == else if (qosFlowProfile->arp->preempt_cap ==
OpenAPI_preemption_capability_MAY_PREEMPT) OpenAPI_preemption_capability_MAY_PREEMPT)
qos.arp.pre_emption_capability = qos.arp.pre_emption_capability = OGS_5GC_PRE_EMPTION_ENABLED;
OGS_5GC_PRE_EMPTION_ENABLED;
else { else {
ogs_error("Invalid preempt_cap [%d]", ogs_error("Invalid preempt_cap [%d]",
qosFlowProfile->arp->preempt_cap); qosFlowProfile->arp->preempt_cap);
@@ -321,12 +319,10 @@ ogs_pkbuf_t *ngap_build_pdu_session_resource_setup_request_transfer(
if (qosFlowProfile->arp->preempt_vuln == if (qosFlowProfile->arp->preempt_vuln ==
OpenAPI_preemption_vulnerability_NOT_PREEMPTABLE) OpenAPI_preemption_vulnerability_NOT_PREEMPTABLE)
qos.arp.pre_emption_vulnerability = qos.arp.pre_emption_vulnerability = OGS_5GC_PRE_EMPTION_DISABLED;
OGS_5GC_PRE_EMPTION_DISABLED;
else if (qosFlowProfile->arp->preempt_vuln == else if (qosFlowProfile->arp->preempt_vuln ==
OpenAPI_preemption_vulnerability_PREEMPTABLE) OpenAPI_preemption_vulnerability_PREEMPTABLE)
qos.arp.pre_emption_vulnerability = qos.arp.pre_emption_vulnerability = OGS_5GC_PRE_EMPTION_ENABLED;
OGS_5GC_PRE_EMPTION_ENABLED;
else { else {
ogs_error("Invalid preempt_vuln [%d]", ogs_error("Invalid preempt_vuln [%d]",
qosFlowProfile->arp->preempt_vuln); qosFlowProfile->arp->preempt_vuln);

View File

@@ -163,7 +163,6 @@ int ngap_handle_pdu_session_resource_setup_response_transfer(
if (far_update) { if (far_update) {
uint64_t pfcp_flags = OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_ACTIVATE; 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) * UE-requested PDU Session Modification(ACTIVATED)
* *
@@ -188,6 +187,7 @@ int ngap_handle_pdu_session_resource_setup_response_transfer(
* case SMF_UPDATE_STATE_HR_ACTIVATED_FROM_NON_ACTIVATING: * case SMF_UPDATE_STATE_HR_ACTIVATED_FROM_NON_ACTIVATING:
* ogs_sbi_send_http_status_no_content * ogs_sbi_send_http_status_no_content
*/ */
if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) {
pfcp_flags |= OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING; pfcp_flags |= OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING;
pfcp_flags |= OGS_PFCP_MODIFY_OUTER_HEADER_REMOVAL; pfcp_flags |= OGS_PFCP_MODIFY_OUTER_HEADER_REMOVAL;
@@ -252,7 +252,7 @@ int ngap_handle_pdu_session_resource_setup_unsuccessful_transfer(
smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_pkbuf_t *pkbuf) smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_pkbuf_t *pkbuf)
{ {
smf_ue_t *smf_ue = NULL; smf_ue_t *smf_ue = NULL;
int rv; int r, rv;
NGAP_PDUSessionResourceSetupUnsuccessfulTransfer_t message; NGAP_PDUSessionResourceSetupUnsuccessfulTransfer_t message;
NGAP_Cause_t *Cause = NULL; NGAP_Cause_t *Cause = NULL;
@@ -292,6 +292,40 @@ int ngap_handle_pdu_session_resource_setup_unsuccessful_transfer(
Cause->present, (int)Cause->choice.radioNetwork); Cause->present, (int)Cause->choice.radioNetwork);
} }
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 = OpenAPI_up_cnx_state_DEACTIVATED;
sess->nsmf_param.ngap_cause.group = Cause->present;
sess->nsmf_param.ngap_cause.value = (int)Cause->choice.radioNetwork;
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 {
/* /*
* TS23.502 * TS23.502
* 4.2.3 Service Request procedures * 4.2.3 Service Request procedures
@@ -331,11 +365,11 @@ int ngap_handle_pdu_session_resource_setup_unsuccessful_transfer(
* has failed and set the upCnxState attribute to DEACTIVATED" * has failed and set the upCnxState attribute to DEACTIVATED"
* otherwise. * otherwise.
*/ */
ogs_assert(OGS_OK ==
ogs_assert(OGS_OK == smf_5gc_pfcp_send_all_pdr_modification_request(
smf_5gc_pfcp_send_all_pdr_modification_request( sess, stream,
sess, stream, OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE, 0, 0));
OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE, 0, 0)); }
rv = OGS_OK; rv = OGS_OK;
cleanup: cleanup:
@@ -462,7 +496,8 @@ int ngap_handle_path_switch_request_transfer(
smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_pkbuf_t *pkbuf) smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_pkbuf_t *pkbuf)
{ {
smf_ue_t *smf_ue = NULL; smf_ue_t *smf_ue = NULL;
int rv, i; smf_bearer_t *qos_flow = NULL;
int r, rv, i;
uint32_t remote_dl_teid; uint32_t remote_dl_teid;
ogs_ip_t remote_dl_ip; ogs_ip_t remote_dl_ip;
@@ -540,52 +575,96 @@ int ngap_handle_path_switch_request_transfer(
memcpy(&sess->remote_dl_ip, &remote_dl_ip, sizeof(sess->remote_dl_ip)); memcpy(&sess->remote_dl_ip, &remote_dl_ip, sizeof(sess->remote_dl_ip));
sess->remote_dl_teid = remote_dl_teid; sess->remote_dl_teid = remote_dl_teid;
qosFlowAcceptedList = &message.qosFlowAcceptedList; if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) {
for (i = 0; i < qosFlowAcceptedList->list.count; i++) { ogs_list_for_each(&sess->bearer_list, qos_flow) {
acceptedQosFlowItem = (NGAP_QosFlowAcceptedItem_t *) ogs_pfcp_far_t *dl_far = qos_flow->dl_far;
qosFlowAcceptedList->list.array[i]; ogs_assert(dl_far);
if (acceptedQosFlowItem) { if (dl_far->apply_action != OGS_PFCP_APPLY_ACTION_FORW) {
smf_bearer_t *qos_flow = smf_qos_flow_find_by_qfi( far_update = true;
sess, acceptedQosFlowItem->qosFlowIdentifier); }
if (qos_flow) { dl_far->apply_action = OGS_PFCP_APPLY_ACTION_FORW;
ogs_pfcp_far_t *dl_far = qos_flow->dl_far; ogs_assert(OGS_OK ==
ogs_assert(dl_far); ogs_pfcp_ip_to_outer_header_creation(
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, &sess->remote_dl_ip,
&dl_far->outer_header_creation, &dl_far->outer_header_creation,
&dl_far->outer_header_creation_len)); &dl_far->outer_header_creation_len));
dl_far->outer_header_creation.teid = sess->remote_dl_teid; dl_far->outer_header_creation.teid = sess->remote_dl_teid;
} else { }
ogs_error("[%s:%d] No QoS flow", smf_ue->supi, sess->psi); } else {
smf_sbi_send_sm_context_update_error_log( qosFlowAcceptedList = &message.qosFlowAcceptedList;
stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, for (i = 0; i < qosFlowAcceptedList->list.count; i++) {
"No QoS flow", smf_ue->supi); acceptedQosFlowItem = (NGAP_QosFlowAcceptedItem_t *)
goto cleanup; qosFlowAcceptedList->list.array[i];
if (acceptedQosFlowItem) {
smf_bearer_t *qos_flow = smf_qos_flow_find_by_qfi(
sess, acceptedQosFlowItem->qosFlowIdentifier);
if (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) { if (far_update) {
uint64_t pfcp_flags =
OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_ACTIVATE|
OGS_PFCP_MODIFY_XN_HANDOVER|OGS_PFCP_MODIFY_END_MARKER;
if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) {
pfcp_flags |= OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING;
pfcp_flags |= OGS_PFCP_MODIFY_OUTER_HEADER_REMOVAL;
}
ogs_assert(OGS_OK == ogs_assert(OGS_OK ==
smf_5gc_pfcp_send_all_pdr_modification_request( smf_5gc_pfcp_send_all_pdr_modification_request(
sess, stream, sess, stream, pfcp_flags, 0, 0));
OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_ACTIVATE|
OGS_PFCP_MODIFY_XN_HANDOVER|OGS_PFCP_MODIFY_END_MARKER,
0, 0));
} else { } else {
ogs_pkbuf_t *n2smbuf = if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) {
ngap_build_path_switch_request_ack_transfer(sess); sess->nsmf_param.request_indication =
ogs_assert(n2smbuf); OpenAPI_request_indication_UE_REQ_PDU_SES_MOD;
smf_sbi_send_sm_context_updated_data_n2smbuf(sess, stream, sess->nsmf_param.up_cnx_state = OpenAPI_up_cnx_state_ACTIVATED;
OpenAPI_n2_sm_info_type_PATH_SWITCH_REQ_ACK, n2smbuf);
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,
SMF_UPDATE_STATE_ACTIVATED_FROM_XN_HANDOVER,
NULL);
ogs_expect(r == OGS_OK);
ogs_assert(r != OGS_ERROR);
} else {
ogs_pkbuf_t *n2smbuf =
ngap_build_path_switch_request_ack_transfer(sess);
ogs_assert(n2smbuf);
smf_sbi_send_sm_context_updated_data_n2smbuf(sess, stream,
OpenAPI_n2_sm_info_type_PATH_SWITCH_REQ_ACK, n2smbuf);
}
} }
rv = OGS_OK; rv = OGS_OK;
@@ -650,6 +729,7 @@ int ngap_handle_handover_request_ack(
smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_pkbuf_t *pkbuf) smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_pkbuf_t *pkbuf)
{ {
smf_ue_t *smf_ue = NULL; smf_ue_t *smf_ue = NULL;
smf_bearer_t *qos_flow = NULL;
int rv, i; int rv, i;
NGAP_HandoverRequestAcknowledgeTransfer_t message; NGAP_HandoverRequestAcknowledgeTransfer_t message;
@@ -715,26 +795,28 @@ int ngap_handle_handover_request_ack(
ogs_asn_OCTET_STRING_to_uint32(&gTPTunnel->gTP_TEID, ogs_asn_OCTET_STRING_to_uint32(&gTPTunnel->gTP_TEID,
&sess->handover.remote_dl_teid); &sess->handover.remote_dl_teid);
qosFlowSetupResponseList = &message.qosFlowSetupResponseList; if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) {
for (i = 0; i < qosFlowSetupResponseList->list.count; i++) { ogs_list_for_each(&sess->bearer_list, qos_flow) {
qosFlowSetupResponseItem = (NGAP_QosFlowItemWithDataForwarding_t *) ogs_pfcp_far_t *dl_far = qos_flow->dl_far;
qosFlowSetupResponseList->list.array[i]; ogs_assert(dl_far);
if (qosFlowSetupResponseItem) {
smf_bearer_t *qos_flow = smf_qos_flow_find_by_qfi(
sess, qosFlowSetupResponseItem->qosFlowIdentifier);
if (qos_flow) { dl_far->handover.prepared = true;
ogs_pfcp_far_t *dl_far = qos_flow->dl_far; }
ogs_assert(dl_far); } else {
qosFlowSetupResponseList = &message.qosFlowSetupResponseList;
for (i = 0; i < qosFlowSetupResponseList->list.count; i++) {
qosFlowSetupResponseItem = (NGAP_QosFlowItemWithDataForwarding_t *)
qosFlowSetupResponseList->list.array[i];
if (qosFlowSetupResponseItem) {
smf_bearer_t *qos_flow = smf_qos_flow_find_by_qfi(
sess, qosFlowSetupResponseItem->qosFlowIdentifier);
dl_far->handover.prepared = true; if (qos_flow) {
ogs_pfcp_far_t *dl_far = qos_flow->dl_far;
ogs_assert(dl_far);
} else { dl_far->handover.prepared = true;
ogs_error("[%s:%d] No QoS flow", smf_ue->supi, sess->psi); }
smf_sbi_send_sm_context_update_error_log(
stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
"No QoS flow", smf_ue->supi);
goto cleanup;
} }
} }
} }

View File

@@ -854,7 +854,7 @@ bool smf_nsmf_handle_update_sm_context(
OpenAPI_request_indication_UE_REQ_PDU_SES_MOD; OpenAPI_request_indication_UE_REQ_PDU_SES_MOD;
sess->nsmf_param.up_cnx_state = sess->nsmf_param.up_cnx_state =
SmContextUpdateData->up_cnx_state; OpenAPI_up_cnx_state_DEACTIVATED;
if (SmContextUpdateData->ue_location) if (SmContextUpdateData->ue_location)
sess->nsmf_param.ue_location = true; sess->nsmf_param.ue_location = true;
@@ -1030,12 +1030,18 @@ bool smf_nsmf_handle_update_sm_context(
} }
if (far_update) { if (far_update) {
uint64_t pfcp_flags =
OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_ACTIVATE|
OGS_PFCP_MODIFY_N2_HANDOVER|OGS_PFCP_MODIFY_END_MARKER;
if (HOME_ROUTED_ROAMING_IN_VSMF(sess)) {
pfcp_flags |= OGS_PFCP_MODIFY_HOME_ROUTED_ROAMING;
pfcp_flags |= OGS_PFCP_MODIFY_OUTER_HEADER_REMOVAL;
}
ogs_assert(OGS_OK == ogs_assert(OGS_OK ==
smf_5gc_pfcp_send_all_pdr_modification_request( smf_5gc_pfcp_send_all_pdr_modification_request(
sess, stream, sess, stream, pfcp_flags, 0, 0));
OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_ACTIVATE|
OGS_PFCP_MODIFY_N2_HANDOVER|OGS_PFCP_MODIFY_END_MARKER,
0, 0));
} else { } else {
char *strerror = ogs_msprintf( char *strerror = ogs_msprintf(
"[%s:%d] No FAR Update", smf_ue->supi, sess->psi); "[%s:%d] No FAR Update", smf_ue->supi, sess->psi);

View File

@@ -66,10 +66,14 @@ bool smf_sbi_send_request(
(SMF_UPDATE_STATE_BASE + 0x02U) /* 0x02 */ (SMF_UPDATE_STATE_BASE + 0x02U) /* 0x02 */
#define SMF_UPDATE_STATE_ACTIVATED_FROM_NON_ACTIVATING \ #define SMF_UPDATE_STATE_ACTIVATED_FROM_NON_ACTIVATING \
(SMF_UPDATE_STATE_BASE + 0x03U) /* 0x03 */ (SMF_UPDATE_STATE_BASE + 0x03U) /* 0x03 */
#define SMF_UPDATE_STATE_DEACTIVATED \ #define SMF_UPDATE_STATE_ACTIVATED_FROM_XN_HANDOVER \
(SMF_UPDATE_STATE_BASE + 0x04U) /* 0x04 */ (SMF_UPDATE_STATE_BASE + 0x04U) /* 0x04 */
#define SMF_UPDATE_STATE_UE_REQ_MOD \ #define SMF_UPDATE_STATE_ACTIVATED_FROM_N2_HANDOVER \
(SMF_UPDATE_STATE_BASE + 0x05U) /* 0x05 */ (SMF_UPDATE_STATE_BASE + 0x05U) /* 0x05 */
#define SMF_UPDATE_STATE_DEACTIVATED \
(SMF_UPDATE_STATE_BASE + 0x06U) /* 0x06 */
#define SMF_UPDATE_STATE_UE_REQ_MOD \
(SMF_UPDATE_STATE_BASE + 0x07U) /* 0x07 */
/* Base offset for SMF_REMOVE states */ /* Base offset for SMF_REMOVE states */
#define SMF_REMOVE_STATE_BASE 0x30U /* REMOVE at 0x30 */ #define SMF_REMOVE_STATE_BASE 0x30U /* REMOVE at 0x30 */

View File

@@ -77,6 +77,21 @@ extern "C" {
#define SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_UNPROTECTED 0 #define SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_UNPROTECTED 0
#define SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_PROTECTED 1 #define SEND_UE_CONTEXT_RELEASE_COMMAND_IN_INTEGRITY_PROTECTED 1
/*
* [VONR]
* Disable vonr/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.
*
* [HANDOVER]]
* Normally, with 2 QoS Flows, the system waits for 2 End Markers.
* However, in Home Routed Roaming, the V-SMF uses only a single QoS Flow
* even when multiple QoS Flows are present.
* Therefore, only one End Marker should be awaited.
*/
#define HOME_ROUTED_ROAMING_TEST 0
#undef OGS_TEST_INSIDE #undef OGS_TEST_INSIDE
#ifdef __cplusplus #ifdef __cplusplus

View File

@@ -849,10 +849,12 @@ static void direct_complete_func(abts_case *tc, void *data)
ABTS_PTR_NOTNULL(tc, recvbuf); ABTS_PTR_NOTNULL(tc, recvbuf);
ogs_pkbuf_free(recvbuf); ogs_pkbuf_free(recvbuf);
#if !HOME_ROUTED_ROAMING_TEST
/* Receive End Mark */ /* Receive End Mark */
recvbuf = test_gtpu_read(gtpu1); recvbuf = test_gtpu_read(gtpu1);
ABTS_PTR_NOTNULL(tc, recvbuf); ABTS_PTR_NOTNULL(tc, recvbuf);
ogs_pkbuf_free(recvbuf); ogs_pkbuf_free(recvbuf);
#endif
/* Receive UEContextReleaseCommand */ /* Receive UEContextReleaseCommand */
amf_ue_ngap_id = test_ue->amf_ue_ngap_id; amf_ue_ngap_id = test_ue->amf_ue_ngap_id;
@@ -966,10 +968,12 @@ static void direct_complete_func(abts_case *tc, void *data)
ABTS_PTR_NOTNULL(tc, recvbuf); ABTS_PTR_NOTNULL(tc, recvbuf);
ogs_pkbuf_free(recvbuf); ogs_pkbuf_free(recvbuf);
#if !HOME_ROUTED_ROAMING_TEST
/* Receive End Mark */ /* Receive End Mark */
recvbuf = test_gtpu_read(gtpu2); recvbuf = test_gtpu_read(gtpu2);
ABTS_PTR_NOTNULL(tc, recvbuf); ABTS_PTR_NOTNULL(tc, recvbuf);
ogs_pkbuf_free(recvbuf); ogs_pkbuf_free(recvbuf);
#endif
/* Receive UEContextReleaseCommand */ /* Receive UEContextReleaseCommand */
amf_ue_ngap_id = test_ue->amf_ue_ngap_id; amf_ue_ngap_id = test_ue->amf_ue_ngap_id;
@@ -1881,10 +1885,12 @@ static void indirect_complete_func(abts_case *tc, void *data)
ABTS_PTR_NOTNULL(tc, recvbuf); ABTS_PTR_NOTNULL(tc, recvbuf);
ogs_pkbuf_free(recvbuf); ogs_pkbuf_free(recvbuf);
#if !HOME_ROUTED_ROAMING_TEST
/* Receive End Mark */ /* Receive End Mark */
recvbuf = test_gtpu_read(gtpu1); recvbuf = test_gtpu_read(gtpu1);
ABTS_PTR_NOTNULL(tc, recvbuf); ABTS_PTR_NOTNULL(tc, recvbuf);
ogs_pkbuf_free(recvbuf); ogs_pkbuf_free(recvbuf);
#endif
/* Receive UEContextReleaseCommand */ /* Receive UEContextReleaseCommand */
amf_ue_ngap_id = test_ue->amf_ue_ngap_id; amf_ue_ngap_id = test_ue->amf_ue_ngap_id;
@@ -2029,10 +2035,12 @@ static void indirect_complete_func(abts_case *tc, void *data)
ABTS_PTR_NOTNULL(tc, recvbuf); ABTS_PTR_NOTNULL(tc, recvbuf);
ogs_pkbuf_free(recvbuf); ogs_pkbuf_free(recvbuf);
#if !HOME_ROUTED_ROAMING_TEST
/* Receive End Mark */ /* Receive End Mark */
recvbuf = test_gtpu_read(gtpu2); recvbuf = test_gtpu_read(gtpu2);
ABTS_PTR_NOTNULL(tc, recvbuf); ABTS_PTR_NOTNULL(tc, recvbuf);
ogs_pkbuf_free(recvbuf); ogs_pkbuf_free(recvbuf);
#endif
/* Receive UEContextReleaseCommand */ /* Receive UEContextReleaseCommand */
amf_ue_ngap_id = test_ue->amf_ue_ngap_id; amf_ue_ngap_id = test_ue->amf_ue_ngap_id;
@@ -2534,10 +2542,12 @@ static void indirect_cancel_func(abts_case *tc, void *data)
ABTS_PTR_NOTNULL(tc, recvbuf); ABTS_PTR_NOTNULL(tc, recvbuf);
ogs_pkbuf_free(recvbuf); ogs_pkbuf_free(recvbuf);
#if !HOME_ROUTED_ROAMING_TEST
/* Receive End Mark */ /* Receive End Mark */
recvbuf = test_gtpu_read(gtpu1); recvbuf = test_gtpu_read(gtpu1);
ABTS_PTR_NOTNULL(tc, recvbuf); ABTS_PTR_NOTNULL(tc, recvbuf);
ogs_pkbuf_free(recvbuf); ogs_pkbuf_free(recvbuf);
#endif
/* Receive UEContextReleaseCommand */ /* Receive UEContextReleaseCommand */
amf_ue_ngap_id = test_ue->amf_ue_ngap_id; amf_ue_ngap_id = test_ue->amf_ue_ngap_id;

View File

@@ -318,10 +318,12 @@ static void test_two_qos_flows(abts_case *tc, void *data)
ABTS_PTR_NOTNULL(tc, recvbuf); ABTS_PTR_NOTNULL(tc, recvbuf);
ogs_pkbuf_free(recvbuf); ogs_pkbuf_free(recvbuf);
#if !HOME_ROUTED_ROAMING_TEST
/* Receive End Mark */ /* Receive End Mark */
recvbuf = test_gtpu_read(gtpu1); recvbuf = test_gtpu_read(gtpu1);
ABTS_PTR_NOTNULL(tc, recvbuf); ABTS_PTR_NOTNULL(tc, recvbuf);
ogs_pkbuf_free(recvbuf); ogs_pkbuf_free(recvbuf);
#endif
/* Receive Path Switch Ack */ /* Receive Path Switch Ack */
recvbuf = testgnb_ngap_read(ngap2); recvbuf = testgnb_ngap_read(ngap2);
@@ -365,10 +367,12 @@ static void test_two_qos_flows(abts_case *tc, void *data)
ABTS_PTR_NOTNULL(tc, recvbuf); ABTS_PTR_NOTNULL(tc, recvbuf);
ogs_pkbuf_free(recvbuf); ogs_pkbuf_free(recvbuf);
#if !HOME_ROUTED_ROAMING_TEST
/* Receive End Mark */ /* Receive End Mark */
recvbuf = test_gtpu_read(gtpu2); recvbuf = test_gtpu_read(gtpu2);
ABTS_PTR_NOTNULL(tc, recvbuf); ABTS_PTR_NOTNULL(tc, recvbuf);
ogs_pkbuf_free(recvbuf); ogs_pkbuf_free(recvbuf);
#endif
/* Receive Path Switch Ack */ /* Receive Path Switch Ack */
recvbuf = testgnb_ngap_read(ngap1); recvbuf = testgnb_ngap_read(ngap1);

View File

@@ -3966,13 +3966,6 @@ static void test7_func(abts_case *tc, void *data)
test_ue_remove(test_ue); 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 #if !HOME_ROUTED_ROAMING_TEST
/** /**
* test8_func: * test8_func: