diff --git a/configs/open5gs/amf.yaml.in b/configs/open5gs/amf.yaml.in index 40f91420f..0be636d17 100644 --- a/configs/open5gs/amf.yaml.in +++ b/configs/open5gs/amf.yaml.in @@ -304,4 +304,12 @@ pool: # o Message Wait Duration (3000 ms) # message: # duration: 3000 +# +# o Handover Wait Duration (Default : 300 ms) +# Time to wait for AMF to send UEContextReleaseCommand +# to the source gNB after receiving HandoverNotify +# +# o Handover Wait Duration (500ms) +# handover: +# duration: 500 time: diff --git a/configs/open5gs/mme.yaml.in b/configs/open5gs/mme.yaml.in index 232d8348c..dfba21014 100644 --- a/configs/open5gs/mme.yaml.in +++ b/configs/open5gs/mme.yaml.in @@ -417,4 +417,12 @@ sctp: # o Message Wait Duration (3000 ms) # message: # duration: 3000 +# +# o Handover Wait Duration (Default : 300 ms) +# Time to wait for MME to send UEContextReleaseCommand +# to the source eNB after receiving HandoverNotify +# +# o Handover Wait Duration (500ms) +# handover: +# duration: 500 time: diff --git a/configs/open5gs/smf.yaml.in b/configs/open5gs/smf.yaml.in index a1af636d8..618c9fffe 100644 --- a/configs/open5gs/smf.yaml.in +++ b/configs/open5gs/smf.yaml.in @@ -353,4 +353,13 @@ pool: # o Message Wait Duration (3000 ms) # message: # duration: 3000 +# +# o Handover Wait Duration (Default : 300 ms) +# Time to wait for SMF to send +# PFCP Session Modification Request(Remove Indirect Tunnel) to the UPF +# after sending Nsmf_PDUSession_UpdateSMContext Response(hoState:COMPLETED) +# +# o Handover Wait Duration (500ms) +# handover: +# duration: 500 time: diff --git a/lib/app/ogs-context.c b/lib/app/ogs-context.c index b73294cdd..dd7231cb7 100644 --- a/lib/app/ogs-context.c +++ b/lib/app/ogs-context.c @@ -203,6 +203,14 @@ static void app_context_prepare(void) */ self.time.message.duration = ogs_time_from_sec(10); + /* + * Handover Wait Duration : 300 ms (Default) + * + * Time to wait for AMF/MME to send UEContextReleaseCommand + * to the source gNB/eNB after receiving HandoverNotify + */ + self.time.handover.duration = ogs_time_from_msec(300); + regenerate_all_timer_duration(); } @@ -482,13 +490,31 @@ int ogs_app_context_parse_config(void) if (!strcmp(msg_key, "duration")) { const char *v = ogs_yaml_iter_value(&msg_iter); if (v) { - self.time.message.duration = + self.time.message.duration = ogs_time_from_msec(atoll(v)); regenerate_all_timer_duration(); } } else ogs_warn("unknown key `%s`", msg_key); } + } else if (!strcmp(time_key, "handover")) { + ogs_yaml_iter_t msg_iter; + ogs_yaml_iter_recurse(&time_iter, &msg_iter); + + while (ogs_yaml_iter_next(&msg_iter)) { + const char *msg_key = + ogs_yaml_iter_key(&msg_iter); + ogs_assert(msg_key); + + if (!strcmp(msg_key, "duration")) { + const char *v = ogs_yaml_iter_value(&msg_iter); + if (v) { + self.time.handover.duration = + ogs_time_from_msec(atoll(v)); + } + } else + ogs_warn("unknown key `%s`", msg_key); + } } else ogs_warn("unknown key `%s`", time_key); } diff --git a/lib/app/ogs-context.h b/lib/app/ogs-context.h index b141618df..739cc8823 100644 --- a/lib/app/ogs-context.h +++ b/lib/app/ogs-context.h @@ -146,6 +146,12 @@ typedef struct ogs_app_context_s { ogs_time_t no_heartbeat_duration; } pfcp; } message; + + struct { + ogs_time_t duration; + ogs_time_t complete_delay; + } handover; + } time; } ogs_app_context_t; diff --git a/lib/gtp/xact.c b/lib/gtp/xact.c index 71595c4c7..f5d15f01a 100644 --- a/lib/gtp/xact.c +++ b/lib/gtp/xact.c @@ -567,7 +567,7 @@ static void response_timeout(void *data) { char buf[OGS_ADDRSTRLEN]; ogs_gtp_xact_t *xact = data; - + ogs_assert(xact); ogs_assert(xact->gnode); @@ -618,7 +618,7 @@ static void holding_timeout(void *data) { char buf[OGS_ADDRSTRLEN]; ogs_gtp_xact_t *xact = data; - + ogs_assert(xact); ogs_assert(xact->gnode); diff --git a/lib/gtp/xact.h b/lib/gtp/xact.h index 3d8f3a409..94c85f6a3 100644 --- a/lib/gtp/xact.h +++ b/lib/gtp/xact.h @@ -99,6 +99,10 @@ typedef struct ogs_gtp_xact_s { #define OGS_GTP_RELEASE_S1_CONTEXT_REMOVE_BY_RESET_ALL 3 #define OGS_GTP_RELEASE_S1_CONTEXT_REMOVE_BY_RESET_PARTIAL 4 int release_action; + +#define OGS_GTP_DELETE_INDIRECT_HANDOVER_COMPLETE 1 +#define OGS_GTP_DELETE_INDIRECT_HANDOVER_CANCEL 2 + int delete_indirect_action; } ogs_gtp_xact_t; int ogs_gtp_xact_init(void); diff --git a/lib/pfcp/types.h b/lib/pfcp/types.h index 6150a3bdb..91236e2f6 100644 --- a/lib/pfcp/types.h +++ b/lib/pfcp/types.h @@ -487,6 +487,7 @@ ED5(uint8_t spare1:4;, uint8_t ipv4:1;) union { #define OGS_PFCP_DEFAULT_CHOOSE_ID 5 +#define OGS_PFCP_INDIRECT_DATA_FORWARDING_CHOOSE_ID 10 struct { ED4(uint8_t choose_id;, uint8_t spare2;, diff --git a/lib/pfcp/xact.c b/lib/pfcp/xact.c index 0164a0c46..0106e9979 100644 --- a/lib/pfcp/xact.c +++ b/lib/pfcp/xact.c @@ -41,6 +41,7 @@ static int ogs_pfcp_xact_delete(ogs_pfcp_xact_t *xact); static void response_timeout(void *data); static void holding_timeout(void *data); +static void delayed_commit_timeout(void *data); int ogs_pfcp_xact_init(void) { @@ -95,6 +96,10 @@ ogs_pfcp_xact_t *ogs_pfcp_xact_local_create(ogs_pfcp_node_t *node, ogs_assert(xact->tm_holding); xact->holding_rcount = ogs_app()->time.message.pfcp.n1_holding_rcount, + xact->tm_delayed_commit = ogs_timer_add( + ogs_app()->timer_mgr, delayed_commit_timeout, xact); + ogs_assert(xact->tm_delayed_commit); + ogs_list_add(xact->org == OGS_PFCP_LOCAL_ORIGINATOR ? &xact->node->local_list : &xact->node->remote_list, xact); @@ -141,6 +146,10 @@ ogs_pfcp_xact_t *ogs_pfcp_xact_remote_create( ogs_assert(xact->tm_holding); xact->holding_rcount = ogs_app()->time.message.pfcp.n1_holding_rcount, + xact->tm_delayed_commit = ogs_timer_add( + ogs_app()->timer_mgr, delayed_commit_timeout, xact); + ogs_assert(xact->tm_delayed_commit); + ogs_list_add(xact->org == OGS_PFCP_LOCAL_ORIGINATOR ? &xact->node->local_list : &xact->node->remote_list, xact); @@ -449,7 +458,6 @@ int ogs_pfcp_xact_update_rx(ogs_pfcp_xact_t *xact, uint8_t type) return OGS_OK; } - int ogs_pfcp_xact_commit(ogs_pfcp_xact_t *xact) { int rv; @@ -561,11 +569,20 @@ int ogs_pfcp_xact_commit(ogs_pfcp_xact_t *xact) return OGS_OK; } +void ogs_pfcp_xact_delayed_commit(ogs_pfcp_xact_t *xact, ogs_time_t duration) +{ + ogs_assert(xact); + ogs_assert(duration); + ogs_assert(xact->tm_delayed_commit); + + ogs_timer_start(xact->tm_delayed_commit, duration); +} + static void response_timeout(void *data) { char buf[OGS_ADDRSTRLEN]; ogs_pfcp_xact_t *xact = data; - + ogs_assert(xact); ogs_assert(xact->node); @@ -616,7 +633,7 @@ static void holding_timeout(void *data) { char buf[OGS_ADDRSTRLEN]; ogs_pfcp_xact_t *xact = data; - + ogs_assert(xact); ogs_assert(xact->node); @@ -644,6 +661,24 @@ static void holding_timeout(void *data) } } +static void delayed_commit_timeout(void *data) +{ + char buf[OGS_ADDRSTRLEN]; + ogs_pfcp_xact_t *xact = data; + + ogs_assert(xact); + ogs_assert(xact->node); + + ogs_debug("[%d] %s Delayed Send Timeout " + "for step %d type %d peer [%s]:%d", + xact->xid, + xact->org == OGS_PFCP_LOCAL_ORIGINATOR ? "LOCAL " : "REMOTE", + xact->step, xact->seq[xact->step-1].type, + OGS_ADDR(&xact->node->addr, buf), + OGS_PORT(&xact->node->addr)); + + ogs_pfcp_xact_commit(xact); +} int ogs_pfcp_xact_receive( ogs_pfcp_node_t *node, ogs_pfcp_header_t *h, ogs_pfcp_xact_t **xact) @@ -785,6 +820,8 @@ static int ogs_pfcp_xact_delete(ogs_pfcp_xact_t *xact) ogs_timer_delete(xact->tm_response); if (xact->tm_holding) ogs_timer_delete(xact->tm_holding); + if (xact->tm_delayed_commit) + ogs_timer_delete(xact->tm_delayed_commit); ogs_list_remove(xact->org == OGS_PFCP_LOCAL_ORIGINATOR ? &xact->node->local_list : &xact->node->remote_list, xact); diff --git a/lib/pfcp/xact.h b/lib/pfcp/xact.h index 46cab19fe..481947952 100644 --- a/lib/pfcp/xact.h +++ b/lib/pfcp/xact.h @@ -61,6 +61,8 @@ typedef struct ogs_pfcp_xact_s { ogs_timer_t *tm_holding; /**< Timer waiting for holding message */ uint8_t holding_rcount; + ogs_timer_t *tm_delayed_commit; /**< Timer waiting for commit xact */ + void *assoc_xact; /**< Associated GTP transaction */ ogs_pkbuf_t *gtpbuf; /**< GTP packet buffer */ @@ -82,6 +84,7 @@ typedef struct ogs_pfcp_xact_s { #define OGS_PFCP_MODIFY_ERROR_INDICATION ((uint64_t)1<<11) #define OGS_PFCP_MODIFY_XN_HANDOVER ((uint64_t)1<<12) #define OGS_PFCP_MODIFY_N2_HANDOVER ((uint64_t)1<<13) +#define OGS_PFCP_MODIFY_HANDOVER_CANCEL ((uint64_t)1<<14) uint64_t modify_flags; #define OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED 1 @@ -109,6 +112,7 @@ int ogs_pfcp_xact_update_tx(ogs_pfcp_xact_t *xact, int ogs_pfcp_xact_update_rx(ogs_pfcp_xact_t *xact, uint8_t type); int ogs_pfcp_xact_commit(ogs_pfcp_xact_t *xact); +void ogs_pfcp_xact_delayed_commit(ogs_pfcp_xact_t *xact, ogs_time_t duration); int ogs_pfcp_xact_receive(ogs_pfcp_node_t *node, ogs_pfcp_header_t *h, ogs_pfcp_xact_t **xact); diff --git a/src/amf/context.h b/src/amf/context.h index f12fa3ce2..d8599a48e 100644 --- a/src/amf/context.h +++ b/src/amf/context.h @@ -184,6 +184,8 @@ struct ran_ue_s { #define NGAP_UE_CTX_REL_NG_REMOVE_AND_UNLINK 2 #define NGAP_UE_CTX_REL_UE_CONTEXT_REMOVE 3 #define NGAP_UE_CTX_REL_NG_HANDOVER_COMPLETE 4 +#define NGAP_UE_CTX_REL_NG_HANDOVER_CANCEL 5 +#define NGAP_UE_CTX_REL_NG_HANDOVER_FAILURE 6 uint8_t ue_ctx_rel_action; /* Related Context */ diff --git a/src/amf/ngap-build.c b/src/amf/ngap-build.c index 69d780078..efd3f55ef 100644 --- a/src/amf/ngap-build.c +++ b/src/amf/ngap-build.c @@ -1909,6 +1909,7 @@ ogs_pkbuf_t *ngap_build_handover_request(ran_ue_t *target_ue) NGAP_PDUSessionResourceSetupListHOReq_t *PDUSessionList = NULL; NGAP_PDUSessionResourceSetupItemHOReq_t *PDUSessionItem = NULL; NGAP_AllowedNSSAI_t *AllowedNSSAI = NULL; + NGAP_MaskedIMEISV_t *MaskedIMEISV = NULL; NGAP_SourceToTarget_TransparentContainer_t *SourceToTarget_TransparentContainer = NULL; NGAP_GUAMI_t *GUAMI = NULL; @@ -2137,6 +2138,31 @@ ogs_pkbuf_t *ngap_build_handover_request(ran_ue_t *target_ue) } } + /* TS23.003 6.2.2 Composition of IMEISV + * + * The International Mobile station Equipment Identity and + * Software Version Number (IMEISV) is composed. + * + * TAC(8 digits) - SNR(6 digits) - SVN(2 digits) + * IMEISV(16 digits) ==> 8bytes + */ + if (amf_ue->masked_imeisv_len == OGS_MAX_IMEISV_LEN) { + ie = CALLOC(1, sizeof(NGAP_HandoverRequestIEs_t)); + ogs_assert(ie); + ASN_SEQUENCE_ADD(&HandoverRequest->protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_MaskedIMEISV; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_HandoverRequestIEs__value_PR_MaskedIMEISV; + + MaskedIMEISV = &ie->value.choice.MaskedIMEISV; + + MaskedIMEISV->size = amf_ue->masked_imeisv_len; + MaskedIMEISV->buf = CALLOC(MaskedIMEISV->size, sizeof(uint8_t)); + MaskedIMEISV->bits_unused = 0; + memcpy(MaskedIMEISV->buf, amf_ue->masked_imeisv, MaskedIMEISV->size); + } + ie = CALLOC(1, sizeof(NGAP_HandoverRequestIEs_t)); ogs_assert(ie); ASN_SEQUENCE_ADD(&HandoverRequest->protocolIEs, ie); diff --git a/src/amf/ngap-handler.c b/src/amf/ngap-handler.c index bd94b30ee..410e8597d 100644 --- a/src/amf/ngap-handler.c +++ b/src/amf/ngap-handler.c @@ -1274,18 +1274,25 @@ void ngap_handle_ue_context_release_action(ran_ue_t *ran_ue) ran_ue_remove(ran_ue); ogs_expect_or_return(amf_ue); -#if 0 - if (amf_ue_have_indirect_tunnel(amf_ue)) { - amf_gtp_send_delete_indirect_data_forwarding_tunnel_request( - amf_ue); - } else { - ogs_warn("Check your eNodeB"); - ogs_warn(" There is no INDIRECT TUNNEL"); - ogs_warn(" Packet could be dropped during S1-Handover"); - rv = amf_ue_clear_indirect_tunnel(amf_ue); - ogs_expect(rv == OGS_OK); - } -#endif + break; + case NGAP_UE_CTX_REL_NG_HANDOVER_CANCEL: + ogs_warn(" Action: NG handover cancel"); + + source_ue_deassociate_target_ue(ran_ue); + ran_ue_remove(ran_ue); + + ogs_expect_or_return(amf_ue); + ogs_expect_or_return(amf_ue->ran_ue); + + ngap_send_handover_cancel_ack(amf_ue->ran_ue); + break; + case NGAP_UE_CTX_REL_NG_HANDOVER_FAILURE: + ogs_warn(" Action: NG handover failure"); + + source_ue_deassociate_target_ue(ran_ue); + ran_ue_remove(ran_ue); + + ogs_expect_or_return(amf_ue); break; default: ogs_error("Invalid Action[%d]", ran_ue->ue_ctx_rel_action); @@ -2769,7 +2776,7 @@ void ngap_handle_handover_failure( ngap_send_ran_ue_context_release_command(target_ue, NGAP_Cause_PR_radioNetwork, NGAP_CauseRadioNetwork_ho_failure_in_target_5GC_ngran_node_or_target_system, - NGAP_UE_CTX_REL_NG_HANDOVER_COMPLETE, 0); + NGAP_UE_CTX_REL_NG_HANDOVER_FAILURE, 0); } void ngap_handle_handover_cancel( @@ -3144,6 +3151,12 @@ void ngap_handle_handover_notification( memcpy(&amf_ue->tai, &target_ue->saved.tai, sizeof(ogs_5gs_tai_t)); memcpy(&amf_ue->nr_cgi, &target_ue->saved.nr_cgi, sizeof(ogs_nr_cgi_t)); + ngap_send_ran_ue_context_release_command(source_ue, + NGAP_Cause_PR_radioNetwork, + NGAP_CauseRadioNetwork_successful_handover, + NGAP_UE_CTX_REL_NG_HANDOVER_COMPLETE, + ogs_app()->time.handover.duration); + ogs_list_for_each(&amf_ue->sess_list, sess) { memset(¶m, 0, sizeof(param)); param.hoState = OpenAPI_ho_state_COMPLETED; diff --git a/src/amf/nsmf-handler.c b/src/amf/nsmf-handler.c index 341aeb418..5d8b48611 100644 --- a/src/amf/nsmf-handler.c +++ b/src/amf/nsmf-handler.c @@ -410,30 +410,15 @@ int amf_nsmf_pdusession_handle_update_sm_context( target_ue = source_ue->target_ue; ogs_assert(target_ue); - ngap_send_handover_cancel_ack(source_ue); - ngap_send_ran_ue_context_release_command(target_ue, NGAP_Cause_PR_radioNetwork, NGAP_CauseRadioNetwork_handover_cancelled, - NGAP_UE_CTX_REL_NG_HANDOVER_COMPLETE, - ogs_time_from_msec(300)); + NGAP_UE_CTX_REL_NG_HANDOVER_CANCEL, 0); } } else if (state == AMF_UPDATE_SM_CONTEXT_HANDOVER_NOTIFY) { - if (SESSION_SYNC_DONE(amf_ue, state)) { - ran_ue_t *target_ue = NULL, *source_ue = NULL; - target_ue = amf_ue->ran_ue; - ogs_assert(target_ue); - source_ue = target_ue->source_ue; - ogs_assert(source_ue); - - ngap_send_ran_ue_context_release_command(source_ue, - NGAP_Cause_PR_radioNetwork, - NGAP_CauseRadioNetwork_successful_handover, - NGAP_UE_CTX_REL_NG_HANDOVER_COMPLETE, - ogs_time_from_msec(300)); - } + /* Nothing to do */ } else if (state == AMF_REMOVE_S1_CONTEXT_BY_LO_CONNREFUSED) { if (SESSION_SYNC_DONE(amf_ue, state)) { diff --git a/src/mme/mme-context.h b/src/mme/mme-context.h index 115e3b915..c39d5b049 100644 --- a/src/mme/mme-context.h +++ b/src/mme/mme-context.h @@ -263,7 +263,9 @@ struct enb_ue_s { #define S1AP_UE_CTX_REL_S1_REMOVE_AND_UNLINK 2 #define S1AP_UE_CTX_REL_UE_CONTEXT_REMOVE 3 #define S1AP_UE_CTX_REL_S1_HANDOVER_COMPLETE 4 -#define S1AP_UE_CTX_REL_S1_PAGING 5 +#define S1AP_UE_CTX_REL_S1_HANDOVER_CANCEL 5 +#define S1AP_UE_CTX_REL_S1_HANDOVER_FAILURE 6 +#define S1AP_UE_CTX_REL_S1_PAGING 7 uint8_t ue_ctx_rel_action; bool part_of_s1_reset_requested; @@ -491,7 +493,6 @@ struct mme_ue_s { #define MAX_NUM_OF_GTP_COUNTER 16 #define GTP_COUNTER_MODIFY_BEARER_BY_PATH_SWITCH 1 -#define GTP_COUNTER_MODIFY_BEARER_BY_HANDOVER_NOTIFY 2 struct { uint8_t request; uint8_t response; diff --git a/src/mme/mme-gtp-path.c b/src/mme/mme-gtp-path.c index 44ea285da..87e1b6320 100644 --- a/src/mme/mme-gtp-path.c +++ b/src/mme/mme-gtp-path.c @@ -515,13 +515,14 @@ void mme_gtp_send_create_indirect_data_forwarding_tunnel_request( } void mme_gtp_send_delete_indirect_data_forwarding_tunnel_request( - mme_ue_t *mme_ue) + mme_ue_t *mme_ue, int action) { int rv; ogs_gtp_header_t h; ogs_pkbuf_t *pkbuf = NULL; ogs_gtp_xact_t *xact = NULL; + ogs_assert(action); ogs_assert(mme_ue); memset(&h, 0, sizeof(ogs_gtp_header_t)); @@ -534,6 +535,7 @@ void mme_gtp_send_delete_indirect_data_forwarding_tunnel_request( xact = ogs_gtp_xact_local_create(mme_ue->gnode, &h, pkbuf, timeout, mme_ue); ogs_expect_or_return(xact); + xact->delete_indirect_action = action; rv = ogs_gtp_xact_commit(xact); ogs_expect(rv == OGS_OK); diff --git a/src/mme/mme-gtp-path.h b/src/mme/mme-gtp-path.h index cb558eb46..68c8ccfd5 100644 --- a/src/mme/mme-gtp-path.h +++ b/src/mme/mme-gtp-path.h @@ -49,7 +49,7 @@ void mme_gtp_send_downlink_data_notification_ack( void mme_gtp_send_create_indirect_data_forwarding_tunnel_request( mme_ue_t *mme_ue); void mme_gtp_send_delete_indirect_data_forwarding_tunnel_request( - mme_ue_t *mme_ue); + mme_ue_t *mme_ue, int action); void mme_gtp_send_bearer_resource_command( mme_bearer_t *bearer, ogs_nas_eps_message_t *nas_message); diff --git a/src/mme/mme-s11-handler.c b/src/mme/mme-s11-handler.c index 9226174de..cdeae18cf 100644 --- a/src/mme/mme-s11-handler.c +++ b/src/mme/mme-s11-handler.c @@ -253,7 +253,6 @@ void mme_s11_handle_modify_bearer_response( { int rv; uint8_t cause_value = 0; - enb_ue_t *source_ue = NULL, *target_ue = NULL; ogs_assert(xact); ogs_assert(rsp); @@ -294,19 +293,6 @@ void mme_s11_handle_modify_bearer_response( GTP_COUNTER_CHECK(mme_ue, GTP_COUNTER_MODIFY_BEARER_BY_PATH_SWITCH, s1ap_send_path_switch_ack(mme_ue); ); - - GTP_COUNTER_CHECK(mme_ue, GTP_COUNTER_MODIFY_BEARER_BY_HANDOVER_NOTIFY, - target_ue = mme_ue->enb_ue; - ogs_assert(target_ue); - source_ue = target_ue->source_ue; - ogs_assert(source_ue); - - s1ap_send_ue_context_release_command(source_ue, - S1AP_Cause_PR_radioNetwork, - S1AP_CauseRadioNetwork_successful_handover, - S1AP_UE_CTX_REL_S1_HANDOVER_COMPLETE, - ogs_time_from_msec(300)); - ); } void mme_s11_handle_delete_session_response( @@ -1065,8 +1051,11 @@ void mme_s11_handle_delete_indirect_data_forwarding_tunnel_response( { int rv; uint8_t cause_value = 0; + int action = 0; ogs_assert(xact); + action = xact->delete_indirect_action; + ogs_assert(action); ogs_assert(rsp); ogs_debug("Delete Indirect Data Forwarding Tunnel Response"); @@ -1098,6 +1087,15 @@ void mme_s11_handle_delete_indirect_data_forwarding_tunnel_response( mme_ue->mme_s11_teid, mme_ue->sgw_s11_teid); mme_ue_clear_indirect_tunnel(mme_ue); + + if (action == OGS_GTP_DELETE_INDIRECT_HANDOVER_COMPLETE) { + /* Nothing to do */ + } else if (action == OGS_GTP_DELETE_INDIRECT_HANDOVER_CANCEL) { + s1ap_send_handover_cancel_ack(mme_ue->enb_ue); + } else { + ogs_fatal("Invalid action = %d", action); + ogs_assert_if_reached(); + } } void mme_s11_handle_bearer_resource_failure_indication( diff --git a/src/mme/s1ap-build.c b/src/mme/s1ap-build.c index 7eb7299d6..ba08ed0dd 100644 --- a/src/mme/s1ap-build.c +++ b/src/mme/s1ap-build.c @@ -1810,6 +1810,7 @@ ogs_pkbuf_t *s1ap_build_handover_request( *Source_ToTarget_TransparentContainer = NULL; S1AP_UESecurityCapabilities_t *UESecurityCapabilities = NULL; S1AP_SecurityContext_t *SecurityContext = NULL; + S1AP_Masked_IMEISV_t *Masked_IMEISV = NULL; mme_ue_t *mme_ue = NULL; mme_sess_t *sess = NULL; @@ -1914,8 +1915,7 @@ ogs_pkbuf_t *s1ap_build_handover_request( ie->id = S1AP_ProtocolIE_ID_id_SecurityContext; ie->criticality = S1AP_Criticality_reject; - ie->value.present = - S1AP_HandoverRequestIEs__value_PR_SecurityContext; + ie->value.present = S1AP_HandoverRequestIEs__value_PR_SecurityContext; SecurityContext = &ie->value.choice.SecurityContext; @@ -2020,6 +2020,30 @@ ogs_pkbuf_t *s1ap_build_handover_request( memcpy(SecurityContext->nextHopParameter.buf, mme_ue->nh, SecurityContext->nextHopParameter.size); + /* TS23.003 6.2.2 Composition of IMEISV + * + * The International Mobile station Equipment Identity and + * Software Version Number (IMEISV) is composed. + * + * TAC(8 digits) - SNR(6 digits) - SVN(2 digits) + * IMEISV(16 digits) ==> 8bytes + */ + if (mme_ue->imeisv_len == OGS_MAX_IMEISV_LEN) { + ie = CALLOC(1, sizeof(S1AP_HandoverRequestIEs_t)); + ASN_SEQUENCE_ADD(&HandoverRequest->protocolIEs, ie); + + ie->id = S1AP_ProtocolIE_ID_id_Masked_IMEISV; + ie->criticality = S1AP_Criticality_ignore; + ie->value.present = S1AP_HandoverRequestIEs__value_PR_Masked_IMEISV; + + Masked_IMEISV = &ie->value.choice.Masked_IMEISV; + + Masked_IMEISV->size = mme_ue->masked_imeisv_len; + Masked_IMEISV->buf = CALLOC(Masked_IMEISV->size, sizeof(uint8_t)); + Masked_IMEISV->bits_unused = 0; + memcpy(Masked_IMEISV->buf, mme_ue->masked_imeisv, Masked_IMEISV->size); + } + return ogs_s1ap_encode(&pdu); } diff --git a/src/mme/s1ap-handler.c b/src/mme/s1ap-handler.c index 3f620a238..c2ef876b2 100644 --- a/src/mme/s1ap-handler.c +++ b/src/mme/s1ap-handler.c @@ -1262,14 +1262,47 @@ void s1ap_handle_ue_context_release_action(enb_ue_t *enb_ue) ogs_expect_or_return(mme_ue); if (mme_ue_have_indirect_tunnel(mme_ue) == true) { mme_gtp_send_delete_indirect_data_forwarding_tunnel_request( - mme_ue); + mme_ue, OGS_GTP_DELETE_INDIRECT_HANDOVER_COMPLETE); } else { ogs_warn("Check your eNodeB"); - ogs_warn(" There is no INDIRECT TUNNEL"); + ogs_warn(" No INDIRECT TUNNEL"); ogs_warn(" Packet could be dropped during S1-Handover"); mme_ue_clear_indirect_tunnel(mme_ue); } break; + case S1AP_UE_CTX_REL_S1_HANDOVER_CANCEL: + ogs_warn(" Action: S1 handover cancel"); + + source_ue_deassociate_target_ue(enb_ue); + enb_ue_remove(enb_ue); + + ogs_expect_or_return(mme_ue); + if (mme_ue_have_indirect_tunnel(mme_ue) == true) { + mme_gtp_send_delete_indirect_data_forwarding_tunnel_request( + mme_ue, OGS_GTP_DELETE_INDIRECT_HANDOVER_CANCEL); + } else { + ogs_warn("Check your eNodeB"); + ogs_warn(" No INDIRECT TUNNEL"); + ogs_warn(" Packet could be dropped during S1-Handover"); + mme_ue_clear_indirect_tunnel(mme_ue); + + ogs_expect_or_return(mme_ue->enb_ue); + s1ap_send_handover_cancel_ack(mme_ue->enb_ue); + } + break; + case S1AP_UE_CTX_REL_S1_HANDOVER_FAILURE: + ogs_warn(" Action: S1 handover failure"); + + source_ue_deassociate_target_ue(enb_ue); + enb_ue_remove(enb_ue); + + ogs_expect_or_return(mme_ue); + if (mme_ue_have_indirect_tunnel(mme_ue) == true) { + ogs_error("Check your eNodeB"); + ogs_error(" We found INDIRECT TUNNEL in HandoverFailure"); + mme_ue_clear_indirect_tunnel(mme_ue); + } + break; case S1AP_UE_CTX_REL_S1_PAGING: ogs_debug(" Action: S1 paging"); enb_ue_remove(enb_ue); @@ -1884,7 +1917,7 @@ void s1ap_handle_handover_failure(mme_enb_t *enb, ogs_s1ap_message_t *message) s1ap_send_ue_context_release_command( target_ue, S1AP_Cause_PR_radioNetwork, S1AP_CauseRadioNetwork_ho_failure_in_target_EPC_eNB_or_target_system, - S1AP_UE_CTX_REL_S1_HANDOVER_COMPLETE, 0); + S1AP_UE_CTX_REL_S1_HANDOVER_FAILURE, 0); } void s1ap_handle_handover_cancel(mme_enb_t *enb, ogs_s1ap_message_t *message) @@ -1948,13 +1981,10 @@ void s1ap_handle_handover_cancel(mme_enb_t *enb, ogs_s1ap_message_t *message) ogs_debug(" Target : ENB_UE_S1AP_ID[%d] MME_UE_S1AP_ID[%d]", target_ue->enb_ue_s1ap_id, target_ue->mme_ue_s1ap_id); - s1ap_send_handover_cancel_ack(source_ue); - s1ap_send_ue_context_release_command( target_ue, S1AP_Cause_PR_radioNetwork, S1AP_CauseRadioNetwork_handover_cancelled, - S1AP_UE_CTX_REL_S1_HANDOVER_COMPLETE, - ogs_time_from_msec(300)); + S1AP_UE_CTX_REL_S1_HANDOVER_CANCEL, 0); ogs_debug("Handover Cancel : " "UE[eNB-UE-S1AP-ID(%d)] --> eNB[%s:%d]", @@ -2146,22 +2176,20 @@ void s1ap_handle_handover_notification( memcpy(&mme_ue->tai, &target_ue->saved.tai, sizeof(ogs_eps_tai_t)); memcpy(&mme_ue->e_cgi, &target_ue->saved.e_cgi, sizeof(ogs_e_cgi_t)); - sess = mme_sess_first(mme_ue); - while (sess) { - bearer = mme_bearer_first(sess); - while (bearer) { + s1ap_send_ue_context_release_command(source_ue, + S1AP_Cause_PR_radioNetwork, + S1AP_CauseRadioNetwork_successful_handover, + S1AP_UE_CTX_REL_S1_HANDOVER_COMPLETE, + ogs_app()->time.handover.duration); + + ogs_list_for_each(&mme_ue->sess_list, sess) { + ogs_list_for_each(&sess->bearer_list, bearer) { bearer->enb_s1u_teid = bearer->target_s1u_teid; memcpy(&bearer->enb_s1u_ip, &bearer->target_s1u_ip, sizeof(ogs_ip_t)); - GTP_COUNTER_INCREMENT( - mme_ue, GTP_COUNTER_MODIFY_BEARER_BY_HANDOVER_NOTIFY); - mme_gtp_send_modify_bearer_request(bearer, 1); - - bearer = mme_bearer_next(bearer); } - sess = mme_sess_next(sess); } } diff --git a/src/sgwc/s11-handler.c b/src/sgwc/s11-handler.c index 83577ed3d..0a1f9427b 100644 --- a/src/sgwc/s11-handler.c +++ b/src/sgwc/s11-handler.c @@ -1083,9 +1083,7 @@ void sgwc_s11_handle_delete_indirect_data_forwarding_tunnel_request( sess->state.delete_indirect_tunnel = false; sgwc_pfcp_send_sess_modification_request( sess, s11_xact, gtpbuf, - OGS_PFCP_MODIFY_SESSION| - OGS_PFCP_MODIFY_INDIRECT| - OGS_PFCP_MODIFY_REMOVE); + OGS_PFCP_MODIFY_INDIRECT| OGS_PFCP_MODIFY_REMOVE); } } diff --git a/src/sgwc/sxa-handler.c b/src/sgwc/sxa-handler.c index 8a53375e9..5c0a8f375 100644 --- a/src/sgwc/sxa-handler.c +++ b/src/sgwc/sxa-handler.c @@ -437,15 +437,15 @@ void sgwc_sxa_handle_session_modification_response( } if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { - if (flags & OGS_PFCP_MODIFY_CREATE) { - s5c_xact = pfcp_xact->assoc_xact; - ogs_assert(s5c_xact); - - ogs_gtp_send_error_message( - s5c_xact, sess ? sess->pgw_s5c_teid : 0, - OGS_GTP_CREATE_BEARER_RESPONSE_TYPE, cause_value); - - } else if (flags & OGS_PFCP_MODIFY_REMOVE) { + /* + * You should not change the following order to support + * OGS_PFCP_MODIFY_REMOVE|OGS_PFCP_MODIFY_CREATE. + * + * 1. if (flags & OGS_PFCP_MODIFY_REMOVE) { + * 2. } else if (flags & OGS_PFCP_MODIFY_CREATE) { + * } + */ + if (flags & OGS_PFCP_MODIFY_REMOVE) { s5c_xact = pfcp_xact->assoc_xact; if (s5c_xact) { @@ -455,6 +455,14 @@ void sgwc_sxa_handle_session_modification_response( } sgwc_bearer_remove(bearer); + } else if (flags & OGS_PFCP_MODIFY_CREATE) { + s5c_xact = pfcp_xact->assoc_xact; + ogs_assert(s5c_xact); + + ogs_gtp_send_error_message( + s5c_xact, sess ? sess->pgw_s5c_teid : 0, + OGS_GTP_CREATE_BEARER_RESPONSE_TYPE, cause_value); + } else if (flags & OGS_PFCP_MODIFY_ACTIVATE) { if (flags & OGS_PFCP_MODIFY_UL_ONLY) { @@ -489,7 +497,97 @@ void sgwc_sxa_handle_session_modification_response( return; } - if (flags & OGS_PFCP_MODIFY_CREATE) { + /* + * You should not change the following order to support + * OGS_PFCP_MODIFY_REMOVE|OGS_PFCP_MODIFY_CREATE. + * + * 1. if (flags & OGS_PFCP_MODIFY_REMOVE) { + * 2. } else if (flags & OGS_PFCP_MODIFY_CREATE) { + * } + */ + if (flags & OGS_PFCP_MODIFY_REMOVE) { + if (flags & OGS_PFCP_MODIFY_INDIRECT) { + bool delete_indirect_tunnel_is_done; + + s11_xact = pfcp_xact->assoc_xact; + ogs_assert(s11_xact); + + sess->state.delete_indirect_tunnel = true; + + delete_indirect_tunnel_is_done = true; + ogs_list_for_each(&sgwc_ue->sess_list, sess) { + if (sess->state.delete_indirect_tunnel == false) + delete_indirect_tunnel_is_done = false; + } + + if (delete_indirect_tunnel_is_done == true) { + sgwc_tunnel_t *tunnel = NULL, *next_tunnel = NULL; + ogs_gtp_delete_indirect_data_forwarding_tunnel_response_t + *gtp_rsp = NULL; + + ogs_list_for_each(&sgwc_ue->sess_list, sess) { + ogs_list_for_each(&sess->bearer_list, bearer) { + ogs_list_for_each_safe(&bearer->tunnel_list, + next_tunnel, tunnel) { + if (tunnel->interface_type == + OGS_GTP_F_TEID_SGW_GTP_U_FOR_DL_DATA_FORWARDING || + tunnel->interface_type == + OGS_GTP_F_TEID_SGW_GTP_U_FOR_UL_DATA_FORWARDING) { + sgwc_tunnel_remove(tunnel); + } + } + } + } + + gtp_rsp = &send_message. + delete_indirect_data_forwarding_tunnel_response; + ogs_assert(gtp_rsp); + + memset(&send_message, 0, sizeof(ogs_gtp_message_t)); + + memset(&cause, 0, sizeof(cause)); + cause.value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; + + gtp_rsp->cause.presence = 1; + gtp_rsp->cause.data = &cause; + gtp_rsp->cause.len = sizeof(cause); + + send_message.h.type = + OGS_GTP_DELETE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE_TYPE; + send_message.h.teid = sgwc_ue->mme_s11_teid; + + pkbuf = ogs_gtp_build_msg(&send_message); + ogs_expect_or_return(pkbuf); + + rv = ogs_gtp_xact_update_tx(s11_xact, &send_message.h, pkbuf); + ogs_expect_or_return(rv == OGS_OK); + + rv = ogs_gtp_xact_commit(s11_xact); + ogs_expect(rv == OGS_OK); + } + + } else { + s5c_xact = pfcp_xact->assoc_xact; + + if (s5c_xact) { + ogs_assert(recv_message); + recv_message->h.type = OGS_GTP_DELETE_BEARER_RESPONSE_TYPE; + recv_message->h.teid = sess->pgw_s5c_teid; + + pkbuf = ogs_gtp_build_msg(recv_message); + ogs_expect_or_return(pkbuf); + + rv = ogs_gtp_xact_update_tx(s5c_xact, &recv_message->h, pkbuf); + ogs_expect_or_return(rv == OGS_OK); + + rv = ogs_gtp_xact_commit(s5c_xact); + ogs_expect(rv == OGS_OK); + } + + sgwc_bearer_remove(bearer); + } + + } else if (flags & OGS_PFCP_MODIFY_CREATE) { if (flags & OGS_PFCP_MODIFY_UL_ONLY) { ogs_gtp_create_bearer_request_t *gtp_req = NULL; ogs_gtp_f_teid_t sgw_s1u_teid; @@ -715,88 +813,6 @@ void sgwc_sxa_handle_session_modification_response( ogs_assert_if_reached(); } - } else if (flags & OGS_PFCP_MODIFY_REMOVE) { - if (flags & OGS_PFCP_MODIFY_INDIRECT) { - bool delete_indirect_tunnel_is_done; - - s11_xact = pfcp_xact->assoc_xact; - ogs_assert(s11_xact); - - sess->state.delete_indirect_tunnel = true; - - delete_indirect_tunnel_is_done = true; - ogs_list_for_each(&sgwc_ue->sess_list, sess) { - if (sess->state.delete_indirect_tunnel == false) - delete_indirect_tunnel_is_done = false; - } - - if (delete_indirect_tunnel_is_done == true) { - sgwc_tunnel_t *tunnel = NULL, *next_tunnel = NULL; - ogs_gtp_delete_indirect_data_forwarding_tunnel_response_t - *gtp_rsp = NULL; - - ogs_list_for_each(&sgwc_ue->sess_list, sess) { - ogs_list_for_each(&sess->bearer_list, bearer) { - ogs_list_for_each_safe(&bearer->tunnel_list, - next_tunnel, tunnel) { - if (tunnel->interface_type == - OGS_GTP_F_TEID_SGW_GTP_U_FOR_DL_DATA_FORWARDING || - tunnel->interface_type == - OGS_GTP_F_TEID_SGW_GTP_U_FOR_UL_DATA_FORWARDING) { - sgwc_tunnel_remove(tunnel); - } - } - } - } - - gtp_rsp = &send_message. - delete_indirect_data_forwarding_tunnel_response; - ogs_assert(gtp_rsp); - - memset(&send_message, 0, sizeof(ogs_gtp_message_t)); - - memset(&cause, 0, sizeof(cause)); - cause.value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; - - gtp_rsp->cause.presence = 1; - gtp_rsp->cause.data = &cause; - gtp_rsp->cause.len = sizeof(cause); - - send_message.h.type = - OGS_GTP_DELETE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE_TYPE; - send_message.h.teid = sgwc_ue->mme_s11_teid; - - pkbuf = ogs_gtp_build_msg(&send_message); - ogs_expect_or_return(pkbuf); - - rv = ogs_gtp_xact_update_tx(s11_xact, &send_message.h, pkbuf); - ogs_expect_or_return(rv == OGS_OK); - - rv = ogs_gtp_xact_commit(s11_xact); - ogs_expect(rv == OGS_OK); - } - - } else { - s5c_xact = pfcp_xact->assoc_xact; - - if (s5c_xact) { - ogs_assert(recv_message); - recv_message->h.type = OGS_GTP_DELETE_BEARER_RESPONSE_TYPE; - recv_message->h.teid = sess->pgw_s5c_teid; - - pkbuf = ogs_gtp_build_msg(recv_message); - ogs_expect_or_return(pkbuf); - - rv = ogs_gtp_xact_update_tx(s5c_xact, &recv_message->h, pkbuf); - ogs_expect_or_return(rv == OGS_OK); - - rv = ogs_gtp_xact_commit(s5c_xact); - ogs_expect(rv == OGS_OK); - } - - sgwc_bearer_remove(bearer); - } - } else if (flags & OGS_PFCP_MODIFY_ACTIVATE) { s11_xact = pfcp_xact->assoc_xact; ogs_assert(s11_xact); diff --git a/src/smf/context.c b/src/smf/context.c index 0e6680163..b09e72baf 100644 --- a/src/smf/context.c +++ b/src/smf/context.c @@ -1171,6 +1171,11 @@ void smf_sess_remove(smf_sess_t *sess) if (sess->upf_n3_addr6) ogs_freeaddrinfo(sess->upf_n3_addr6); + if (sess->handover.upf_dl_addr) + ogs_freeaddrinfo(sess->handover.upf_dl_addr); + if (sess->handover.upf_dl_addr6) + ogs_freeaddrinfo(sess->handover.upf_dl_addr6); + if (sess->pcf_id) ogs_free(sess->pcf_id); @@ -1371,6 +1376,118 @@ smf_bearer_t *smf_qos_flow_add(smf_sess_t *sess) return qos_flow; } +void smf_sess_create_indirect_data_forwarding(smf_sess_t *sess) +{ + smf_bearer_t *qos_flow = NULL; + + ogs_assert(sess); + + ogs_list_for_each(&sess->bearer_list, qos_flow) { + ogs_pfcp_pdr_t *pdr = NULL; + ogs_pfcp_far_t *far = NULL; + ogs_pfcp_qer_t *qer = NULL; + + ogs_assert(sess); + + pdr = ogs_pfcp_pdr_add(&sess->pfcp); + ogs_assert(pdr); + + pdr->src_if = OGS_PFCP_INTERFACE_ACCESS; + + pdr->outer_header_removal_len = 1; + if (sess->pdn.pdn_type == OGS_GTP_PDN_TYPE_IPV4) { + pdr->outer_header_removal.description = + OGS_PFCP_OUTER_HEADER_REMOVAL_GTPU_UDP_IPV4; + } else if (sess->pdn.pdn_type == OGS_GTP_PDN_TYPE_IPV6) { + pdr->outer_header_removal.description = + OGS_PFCP_OUTER_HEADER_REMOVAL_GTPU_UDP_IPV6; + } else if (sess->pdn.pdn_type == OGS_GTP_PDN_TYPE_IPV4V6) { + pdr->outer_header_removal.description = + OGS_PFCP_OUTER_HEADER_REMOVAL_GTPU_UDP_IP; + } else + ogs_assert_if_reached(); + + far = ogs_pfcp_far_add(&sess->pfcp); + ogs_assert(far); + + far->dst_if = OGS_PFCP_INTERFACE_ACCESS; + ogs_pfcp_pdr_associate_far(pdr, far); + + far->apply_action = OGS_PFCP_APPLY_ACTION_FORW; + + qer = qos_flow->qer; + ogs_assert(qer); + + ogs_pfcp_pdr_associate_qer(pdr, qer); + + pdr->qfi = qos_flow->qfi; + + ogs_assert(sess->pfcp_node); + if (sess->pfcp_node->up_function_features.ftup) { + pdr->f_teid.ch = 1; + pdr->f_teid.chid = 1; + pdr->f_teid.choose_id = OGS_PFCP_INDIRECT_DATA_FORWARDING_CHOOSE_ID; + pdr->f_teid_len = 2; + } else { + ogs_assert(sess->upf_n3_addr || sess->upf_n3_addr6); + + ogs_pfcp_sockaddr_to_f_teid( + sess->upf_n3_addr, sess->upf_n3_addr6, + &pdr->f_teid, &pdr->f_teid_len); + pdr->f_teid.teid = sess->upf_n3_teid; + } + + ogs_pfcp_ip_to_outer_header_creation( + &sess->handover.gnb_dl_ip, + &far->outer_header_creation, + &far->outer_header_creation_len); + far->outer_header_creation.teid = sess->handover.gnb_dl_teid; + + /* Indirect Data Forwarding PDRs is set to highest precedence + * (lowest precedence value) */ + pdr->precedence = 1; + } +} + +bool smf_sess_have_indirect_data_forwarding(smf_sess_t *sess) +{ + ogs_pfcp_pdr_t *pdr = NULL; + + ogs_assert(sess); + + ogs_list_for_each(&sess->pfcp.pdr_list, pdr) { + ogs_pfcp_far_t *far = pdr->far; + + ogs_assert(far); + + if ((pdr->src_if == OGS_PFCP_INTERFACE_ACCESS) && + (far->dst_if == OGS_PFCP_INTERFACE_ACCESS)) { + return true; + } + } + + return false; +} + +void smf_sess_delete_indirect_data_forwarding(smf_sess_t *sess) +{ + ogs_pfcp_pdr_t *pdr = NULL; + + ogs_assert(sess); + + ogs_list_for_each(&sess->pfcp.pdr_list, pdr) { + ogs_pfcp_far_t *far = pdr->far; + + ogs_assert(far); + + if ((pdr->src_if == OGS_PFCP_INTERFACE_ACCESS) && + (far->dst_if == OGS_PFCP_INTERFACE_ACCESS)) { + ogs_pfcp_pdr_remove(pdr); + ogs_pfcp_far_remove(far); + } + } +} + smf_bearer_t *smf_qos_flow_find_by_qfi(smf_sess_t *sess, uint8_t qfi) { smf_bearer_t *qos_flow = NULL; @@ -1525,9 +1642,13 @@ int smf_bearer_remove(smf_bearer_t *bearer) ogs_list_remove(&bearer->sess->bearer_list, bearer); + ogs_assert(bearer->dl_pdr); ogs_pfcp_pdr_remove(bearer->dl_pdr); + ogs_assert(bearer->ul_pdr); ogs_pfcp_pdr_remove(bearer->ul_pdr); + ogs_assert(bearer->dl_far); ogs_pfcp_far_remove(bearer->dl_far); + ogs_assert(bearer->ul_far); ogs_pfcp_far_remove(bearer->ul_far); if (bearer->qer) ogs_pfcp_qer_remove(bearer->qer); diff --git a/src/smf/context.h b/src/smf/context.h index ee6c548ad..418f6b03c 100644 --- a/src/smf/context.h +++ b/src/smf/context.h @@ -305,9 +305,20 @@ typedef struct smf_sess_s { /* Handover */ struct { bool prepared; + bool direct_available; + bool indirect_data_forwarding; + + /* NG-U UP Transport Information Saved Temporally */ uint32_t gnb_n3_teid; ogs_ip_t gnb_n3_ip; - } handover; /* Saved from N2-Handover Request Acknowledge */ + + /* Indirect DL Forwarding */ + uint32_t upf_dl_teid; + ogs_sockaddr_t *upf_dl_addr; + ogs_sockaddr_t *upf_dl_addr6; + uint32_t gnb_dl_teid; + ogs_ip_t gnb_dl_ip; + } handover; ogs_list_t bearer_list; @@ -358,6 +369,10 @@ smf_sess_t *smf_sess_find_by_error_indication_report( smf_ue_t *smf_ue, ogs_pfcp_tlv_error_indication_report_t *error_indication_report); +void smf_sess_create_indirect_data_forwarding(smf_sess_t *sess); +bool smf_sess_have_indirect_data_forwarding(smf_sess_t *sess); +void smf_sess_delete_indirect_data_forwarding(smf_sess_t *sess); + smf_bearer_t *smf_qos_flow_add(smf_sess_t *sess); smf_bearer_t *smf_qos_flow_find_by_qfi(smf_sess_t *sess, uint8_t qfi); smf_bearer_t *smf_qos_flow_find_by_pcc_rule_id( diff --git a/src/smf/n4-build.c b/src/smf/n4-build.c index 9ad2a68ff..55516b287 100644 --- a/src/smf/n4-build.c +++ b/src/smf/n4-build.c @@ -111,13 +111,18 @@ ogs_pkbuf_t *smf_n4_build_session_establishment_request( ogs_pkbuf_t *smf_n4_build_session_modification_request( uint8_t type, smf_sess_t *sess, uint64_t modify_flags) { - int i; + ogs_pfcp_pdr_t *pdr = NULL; ogs_pfcp_message_t pfcp_message; ogs_pfcp_session_modification_request_t *req = NULL; - ogs_pfcp_far_t *far = NULL; ogs_pkbuf_t *pkbuf = NULL; + int num_of_remove_pdr = 0; + int num_of_remove_far = 0; + int num_of_create_pdr = 0; + int num_of_create_far = 0; + int num_of_update_far = 0; + ogs_debug("Session Modification Request"); ogs_assert(sess); ogs_assert(modify_flags); @@ -125,14 +130,55 @@ ogs_pkbuf_t *smf_n4_build_session_modification_request( req = &pfcp_message.pfcp_session_modification_request; memset(&pfcp_message, 0, sizeof(ogs_pfcp_message_t)); - i = 0; - ogs_list_for_each(&sess->pfcp.far_list, far) { + ogs_list_for_each(&sess->pfcp.pdr_list, pdr) { + ogs_pfcp_far_t *far = pdr->far; + ogs_assert(far); - /* Update FAR - Only DL */ - if (far->dst_if == OGS_PFCP_INTERFACE_ACCESS) { + if (((modify_flags & + (OGS_PFCP_MODIFY_DL_ONLY| + OGS_PFCP_MODIFY_UL_ONLY| + OGS_PFCP_MODIFY_INDIRECT)) == 0) || - if (modify_flags & OGS_PFCP_MODIFY_ACTIVATE) { + ((modify_flags & OGS_PFCP_MODIFY_DL_ONLY) && + (pdr->src_if == OGS_PFCP_INTERFACE_CORE) && + (far->dst_if == OGS_PFCP_INTERFACE_ACCESS)) || + ((modify_flags & OGS_PFCP_MODIFY_UL_ONLY) && + (pdr->src_if == OGS_PFCP_INTERFACE_ACCESS) && + (far->dst_if == OGS_PFCP_INTERFACE_CORE)) || + + ((modify_flags & OGS_PFCP_MODIFY_INDIRECT) && + (pdr->src_if == OGS_PFCP_INTERFACE_ACCESS) && + (far->dst_if == OGS_PFCP_INTERFACE_ACCESS))) { + + if (modify_flags & OGS_PFCP_MODIFY_REMOVE) { + ogs_pfcp_tlv_remove_pdr_t *remove_pdr = NULL; + ogs_pfcp_tlv_remove_far_t *remove_far = NULL; + + remove_pdr = &req->remove_pdr[num_of_remove_pdr]; + remove_pdr->presence = 1; + remove_pdr->pdr_id.presence = 1; + remove_pdr->pdr_id.u16 = pdr->id; + num_of_remove_pdr++; + + remove_far = &req->remove_far[num_of_remove_far]; + remove_far->presence = 1; + remove_far->far_id.presence = 1; + remove_far->far_id.u32 = far->id; + num_of_remove_far++; + } else if (modify_flags & OGS_PFCP_MODIFY_CREATE) { + ogs_pfcp_build_create_pdr( + &req->create_pdr[num_of_create_pdr], + num_of_create_pdr, pdr); + + num_of_create_pdr++; + + ogs_pfcp_build_create_far( + &req->create_far[num_of_create_far], + num_of_create_far, far); + + num_of_create_far++; + } else if (modify_flags & OGS_PFCP_MODIFY_ACTIVATE) { if (far->apply_action & OGS_PFCP_APPLY_ACTION_FORW) { if (modify_flags & OGS_PFCP_MODIFY_END_MARKER) { @@ -140,19 +186,65 @@ ogs_pkbuf_t *smf_n4_build_session_modification_request( } ogs_pfcp_build_update_far_activate( - &req->update_far[i], i, far); + &req->update_far[num_of_update_far], + num_of_update_far, far); /* Clear all FAR flags */ far->smreq_flags.value = 0; - } + num_of_update_far++; + } } else if (modify_flags & OGS_PFCP_MODIFY_DEACTIVATE) { ogs_pfcp_build_update_far_deactivate( - &req->update_far[i], i, far); + &req->update_far[num_of_update_far], + num_of_update_far, far); + num_of_update_far++; + } else { + ogs_fatal("Invalid modify_flags = %lld", + (long long)modify_flags); + ogs_assert_if_reached(); + } + } + + } + +#if 0 + i = 0; + ogs_list_for_each(&sess->bearer_list, qos_flow) { + if (modify_flags & OGS_PFCP_MODIFY_ACTIVATE) { + + /* Update FAR - Only DL */ + if (qos_flow->dl_far) { + if (qos_flow->dl_far->apply_action & + OGS_PFCP_APPLY_ACTION_FORW) { + + if (modify_flags & OGS_PFCP_MODIFY_END_MARKER) { + qos_flow->dl_far-> + smreq_flags.send_end_marker_packets = 1; + } + + ogs_fatal("FAR = %d", qos_flow->dl_far->id); + + ogs_pfcp_build_update_far_activate( + &req->update_far[i], i, qos_flow->dl_far); + + /* Clear all FAR flags */ + qos_flow->dl_far->smreq_flags.value = 0; + + i++; + } + } + + } else if (modify_flags & OGS_PFCP_MODIFY_DEACTIVATE) { + if (qos_flow->dl_far) { + ogs_pfcp_build_update_far_deactivate( + &req->update_far[i], i, qos_flow->dl_far); + ogs_error("FAR = %d", qos_flow->dl_far->id); + i++; } - i++; } } +#endif pfcp_message.h.type = type; pkbuf = ogs_pfcp_build_msg(&pfcp_message); @@ -292,7 +384,8 @@ ogs_pkbuf_t *smf_n4_build_qos_flow_modification_request( OGS_PFCP_APPLY_ACTION_FORW) { if (modify_flags & OGS_PFCP_MODIFY_END_MARKER) { - qos_flow->dl_far->smreq_flags.send_end_marker_packets = 1; + qos_flow->dl_far-> + smreq_flags.send_end_marker_packets = 1; } ogs_pfcp_build_update_far_activate( diff --git a/src/smf/n4-handler.c b/src/smf/n4-handler.c index ab370747d..892c5fa51 100644 --- a/src/smf/n4-handler.c +++ b/src/smf/n4-handler.c @@ -264,15 +264,32 @@ void smf_5gc_n4_handle_session_modification_response( ogs_assert(sess->pfcp_node); if (sess->pfcp_node->up_function_features.ftup && pdr->f_teid_len) { - if (sess->upf_n3_addr) - ogs_freeaddrinfo(sess->upf_n3_addr); - if (sess->upf_n3_addr6) - ogs_freeaddrinfo(sess->upf_n3_addr6); - ogs_pfcp_f_teid_to_sockaddr( - &pdr->f_teid, pdr->f_teid_len, - &sess->upf_n3_addr, &sess->upf_n3_addr6); - sess->upf_n3_teid = pdr->f_teid.teid; + ogs_pfcp_far_t *far = pdr->far; + ogs_assert(far); + + if (far->dst_if == OGS_PFCP_INTERFACE_CORE) { + if (sess->upf_n3_addr) + ogs_freeaddrinfo(sess->upf_n3_addr); + if (sess->upf_n3_addr6) + ogs_freeaddrinfo(sess->upf_n3_addr6); + + ogs_pfcp_f_teid_to_sockaddr( + &pdr->f_teid, pdr->f_teid_len, + &sess->upf_n3_addr, &sess->upf_n3_addr6); + sess->upf_n3_teid = pdr->f_teid.teid; + } else if (far->dst_if == OGS_PFCP_INTERFACE_ACCESS) { + if (sess->handover.upf_dl_addr) + ogs_freeaddrinfo(sess->handover.upf_dl_addr); + if (sess->handover.upf_dl_addr6) + ogs_freeaddrinfo(sess->handover.upf_dl_addr6); + + ogs_pfcp_f_teid_to_sockaddr( + &pdr->f_teid, pdr->f_teid_len, + &sess->handover.upf_dl_addr, + &sess->handover.upf_dl_addr6); + sess->handover.upf_dl_teid = pdr->f_teid.teid; + } } } @@ -308,6 +325,13 @@ void smf_5gc_n4_handle_session_modification_response( OpenAPI_n2_sm_info_type_PATH_SWITCH_REQ_ACK, n2smbuf); } else if (flags & OGS_PFCP_MODIFY_N2_HANDOVER) { + if (smf_sess_have_indirect_data_forwarding(sess) == true) { + smf_5gc_pfcp_send_session_modification_request( + sess, stream, + OGS_PFCP_MODIFY_INDIRECT|OGS_PFCP_MODIFY_REMOVE, + ogs_app()->time.handover.duration); + } + smf_sbi_send_sm_context_updated_data_ho_state( sess, stream, OpenAPI_ho_state_COMPLETED); @@ -332,23 +356,61 @@ void smf_5gc_n4_handle_session_modification_response( smf_namf_comm_send_n1_n2_message_transfer(sess, ¶m); } else { - /* Only ACTIVING & DEACTIVATED is Included */ smf_sbi_send_sm_context_updated_data_up_cnx_state( sess, stream, OpenAPI_up_cnx_state_DEACTIVATED); } + /* + * You should not change the following order to support + * OGS_PFCP_MODIFY_REMOVE|OGS_PFCP_MODIFY_CREATE. + * + * 1. if (flags & OGS_PFCP_MODIFY_REMOVE) { + * 2. } else if (flags & OGS_PFCP_MODIFY_CREATE) { + * } + */ + } else if (flags & OGS_PFCP_MODIFY_REMOVE) { + if (flags & OGS_PFCP_MODIFY_INDIRECT) { + + smf_sess_delete_indirect_data_forwarding(sess); + + /* + * OGS_PFCP_MODIFY_CREATE remains. + * So now we do some extra work to create an indirect tunnel. + */ + if (flags & OGS_PFCP_MODIFY_CREATE) { + smf_sess_create_indirect_data_forwarding(sess); + + smf_5gc_pfcp_send_session_modification_request( + sess, stream, + OGS_PFCP_MODIFY_INDIRECT|OGS_PFCP_MODIFY_CREATE, + 0); + } else if (flags & OGS_PFCP_MODIFY_HANDOVER_CANCEL) { + smf_sbi_send_sm_context_updated_data_ho_state( + sess, stream, OpenAPI_ho_state_CANCELLED); + } + } } else if (flags & OGS_PFCP_MODIFY_CREATE) { - smf_n1_n2_message_transfer_param_t param; + if (flags & OGS_PFCP_MODIFY_INDIRECT) { + ogs_pkbuf_t *n2smbuf = ngap_build_handover_command_transfer(sess); + ogs_assert(n2smbuf); - memset(¶m, 0, sizeof(param)); - param.state = SMF_NETWORK_REQUESTED_QOS_FLOW_MODIFICATION; - param.n1smbuf = gsm_build_qos_flow_modification_command( - qos_flow, OGS_NAS_PROCEDURE_TRANSACTION_IDENTITY_UNASSIGNED); - ogs_assert(param.n1smbuf); - param.n2smbuf = ngap_build_qos_flow_resource_modify_request_transfer( - qos_flow); - ogs_assert(param.n2smbuf); + smf_sbi_send_sm_context_updated_data( + sess, stream, 0, OpenAPI_ho_state_PREPARED, + NULL, OpenAPI_n2_sm_info_type_HANDOVER_CMD, n2smbuf); - smf_namf_comm_send_n1_n2_message_transfer(sess, ¶m); + } else { + smf_n1_n2_message_transfer_param_t param; + + memset(¶m, 0, sizeof(param)); + param.state = SMF_NETWORK_REQUESTED_QOS_FLOW_MODIFICATION; + param.n1smbuf = gsm_build_qos_flow_modification_command(qos_flow, + OGS_NAS_PROCEDURE_TRANSACTION_IDENTITY_UNASSIGNED); + ogs_assert(param.n1smbuf); + param.n2smbuf = + ngap_build_qos_flow_resource_modify_request_transfer(qos_flow); + ogs_assert(param.n2smbuf); + + smf_namf_comm_send_n1_n2_message_transfer(sess, ¶m); + } } } @@ -619,12 +681,12 @@ void smf_epc_n4_handle_session_modification_response( return; } - if (flags & OGS_PFCP_MODIFY_CREATE) { - smf_gtp_send_create_bearer_request(bearer); - - } else if (flags & OGS_PFCP_MODIFY_REMOVE) { + if (flags & OGS_PFCP_MODIFY_REMOVE) { smf_bearer_remove(bearer); + } else if (flags & OGS_PFCP_MODIFY_CREATE) { + smf_gtp_send_create_bearer_request(bearer); + } else if (flags & OGS_PFCP_MODIFY_ACTIVATE) { /* Nothing */ } @@ -807,7 +869,9 @@ void smf_n4_handle_session_report_request( smf_5gc_pfcp_send_session_modification_request( error_indication_session, NULL, - OGS_PFCP_MODIFY_DEACTIVATE|OGS_PFCP_MODIFY_ERROR_INDICATION); + OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE| + OGS_PFCP_MODIFY_ERROR_INDICATION, + 0); } else { ogs_error("Not supported Report Type[%d]", report_type.value); diff --git a/src/smf/ngap-build.c b/src/smf/ngap-build.c index fd45f284e..451a4735f 100644 --- a/src/smf/ngap-build.c +++ b/src/smf/ngap-build.c @@ -31,6 +31,7 @@ ogs_pkbuf_t *ngap_build_pdu_session_resource_setup_request_transfer( NGAP_PDUSessionAggregateMaximumBitRate_t *PDUSessionAggregateMaximumBitRate; NGAP_UPTransportLayerInformation_t *UPTransportLayerInformation = NULL; NGAP_GTPTunnel_t *gTPTunnel = NULL; + NGAP_DataForwardingNotPossible_t *DataForwardingNotPossible = NULL; NGAP_PDUSessionType_t *PDUSessionType = NULL; NGAP_QosFlowSetupRequestList_t *QosFlowSetupRequestList = NULL; NGAP_QosFlowSetupRequestItem_t *QosFlowSetupRequestItem = NULL; @@ -49,6 +50,7 @@ ogs_pkbuf_t *ngap_build_pdu_session_resource_setup_request_transfer( if (sess->pdn.ambr.downlink || sess->pdn.ambr.uplink) { ie = CALLOC(1, sizeof(NGAP_PDUSessionResourceSetupRequestTransferIEs_t)); + ogs_assert(ie); ASN_SEQUENCE_ADD(&message.protocolIEs, ie); ie->id = NGAP_ProtocolIE_ID_id_PDUSessionAggregateMaximumBitRate; @@ -65,6 +67,7 @@ ogs_pkbuf_t *ngap_build_pdu_session_resource_setup_request_transfer( } ie = CALLOC(1, sizeof(NGAP_PDUSessionResourceSetupRequestTransferIEs_t)); + ogs_assert(ie); ASN_SEQUENCE_ADD(&message.protocolIEs, ie); ie->id = NGAP_ProtocolIE_ID_id_UL_NGU_UP_TNLInformation; @@ -74,6 +77,7 @@ ogs_pkbuf_t *ngap_build_pdu_session_resource_setup_request_transfer( UPTransportLayerInformation = &ie->value.choice.UPTransportLayerInformation; gTPTunnel = CALLOC(1, sizeof(struct NGAP_GTPTunnel)); + ogs_assert(gTPTunnel); UPTransportLayerInformation->present = NGAP_UPTransportLayerInformation_PR_gTPTunnel; UPTransportLayerInformation->choice.gTPTunnel = gTPTunnel; @@ -83,7 +87,24 @@ ogs_pkbuf_t *ngap_build_pdu_session_resource_setup_request_transfer( ogs_asn_ip_to_BIT_STRING(&upf_n3_ip, &gTPTunnel->transportLayerAddress); ogs_asn_uint32_to_OCTET_STRING(sess->upf_n3_teid, &gTPTunnel->gTP_TEID); + if (sess->handover.direct_available == false) { + ie = CALLOC(1, + sizeof(NGAP_PDUSessionResourceSetupRequestTransferIEs_t)); + ogs_assert(ie); + ASN_SEQUENCE_ADD(&message.protocolIEs, ie); + + ie->id = NGAP_ProtocolIE_ID_id_DataForwardingNotPossible; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_PDUSessionResourceSetupRequestTransferIEs__value_PR_DataForwardingNotPossible; + + DataForwardingNotPossible = &ie->value.choice.DataForwardingNotPossible; + + *DataForwardingNotPossible = + NGAP_DataForwardingNotPossible_data_forwarding_not_possible; + } + ie = CALLOC(1, sizeof(NGAP_PDUSessionResourceSetupRequestTransferIEs_t)); + ogs_assert(ie); ASN_SEQUENCE_ADD(&message.protocolIEs, ie); ie->id = NGAP_ProtocolIE_ID_id_PDUSessionType; @@ -109,6 +130,7 @@ ogs_pkbuf_t *ngap_build_pdu_session_resource_setup_request_transfer( } ie = CALLOC(1, sizeof(NGAP_PDUSessionResourceSetupRequestTransferIEs_t)); + ogs_assert(ie); ASN_SEQUENCE_ADD(&message.protocolIEs, ie); ie->id = NGAP_ProtocolIE_ID_id_QosFlowSetupRequestList; @@ -120,6 +142,7 @@ ogs_pkbuf_t *ngap_build_pdu_session_resource_setup_request_transfer( ogs_list_for_each(&sess->bearer_list, qos_flow) { QosFlowSetupRequestItem = CALLOC(1, sizeof(struct NGAP_QosFlowSetupRequestItem)); + ogs_assert(QosFlowSetupRequestItem); ASN_SEQUENCE_ADD(&QosFlowSetupRequestList->list, QosFlowSetupRequestItem); @@ -131,6 +154,7 @@ ogs_pkbuf_t *ngap_build_pdu_session_resource_setup_request_transfer( &qosFlowLevelQosParameters->allocationAndRetentionPriority; qosCharacteristics = &qosFlowLevelQosParameters->qosCharacteristics; nonDynamic5QI = CALLOC(1, sizeof(struct NGAP_NonDynamic5QIDescriptor)); + ogs_assert(nonDynamic5QI); qosCharacteristics->choice.nonDynamic5QI = nonDynamic5QI; qosCharacteristics->present = NGAP_QosCharacteristics_PR_nonDynamic5QI; @@ -165,6 +189,7 @@ ogs_pkbuf_t *ngap_build_pdu_session_resource_setup_request_transfer( qosFlowLevelQosParameters->gBR_QosInformation = gBR_QosInformation = CALLOC(1, sizeof(*gBR_QosInformation)); + ogs_assert(gBR_QosInformation); asn_uint642INTEGER(&gBR_QosInformation->maximumFlowBitRateDL, qos_flow->qos.mbr.downlink); @@ -339,34 +364,68 @@ ogs_pkbuf_t *ngap_build_handover_command_transfer(smf_sess_t *sess) { NGAP_HandoverCommandTransfer_t message; -#if 0 /* The following is optional. So I've removed */ - ogs_ip_t upf_n3_ip; - - NGAP_UPTransportLayerInformation_t *dLForwardingUP_TNLInformation = NULL; - NGAP_GTPTunnel_t *gTPTunnel = NULL; -#endif + ogs_ip_t upf_dl_ip; ogs_assert(sess); ogs_debug("HandoverCommandTransfer"); memset(&message, 0, sizeof(NGAP_HandoverCommandTransfer_t)); -#if 0 /* The following is optional. So I've removed */ - message.dLForwardingUP_TNLInformation = dLForwardingUP_TNLInformation = - CALLOC(1, sizeof(*dLForwardingUP_TNLInformation)); - ogs_assert(dLForwardingUP_TNLInformation); + if (sess->handover.indirect_data_forwarding == true) { + ogs_pfcp_pdr_t *pdr = NULL; - dLForwardingUP_TNLInformation->present = - NGAP_UPTransportLayerInformation_PR_gTPTunnel; - dLForwardingUP_TNLInformation->choice.gTPTunnel = gTPTunnel = - CALLOC(1, sizeof(*gTPTunnel)); - ogs_assert(gTPTunnel); + NGAP_UPTransportLayerInformation_t + *dLForwardingUP_TNLInformation = NULL; + NGAP_GTPTunnel_t *gTPTunnel = NULL; + NGAP_QosFlowToBeForwardedList_t *qosFlowToBeForwardedList = NULL; - ogs_sockaddr_to_ip(sess->upf_n3_addr, sess->upf_n3_addr6, &upf_n3_ip); - ogs_asn_ip_to_BIT_STRING(&upf_n3_ip, &gTPTunnel->transportLayerAddress); - ogs_asn_uint32_to_OCTET_STRING(sess->upf_n3_teid, &gTPTunnel->gTP_TEID); -#endif + message.dLForwardingUP_TNLInformation = dLForwardingUP_TNLInformation = + CALLOC(1, sizeof(*dLForwardingUP_TNLInformation)); + ogs_assert(dLForwardingUP_TNLInformation); - return ogs_asn_encode( - &asn_DEF_NGAP_HandoverCommandTransfer, &message); + dLForwardingUP_TNLInformation->present = + NGAP_UPTransportLayerInformation_PR_gTPTunnel; + dLForwardingUP_TNLInformation->choice.gTPTunnel = gTPTunnel = + CALLOC(1, sizeof(*gTPTunnel)); + ogs_assert(gTPTunnel); + + ogs_sockaddr_to_ip( + sess->handover.upf_dl_addr, sess->handover.upf_dl_addr6, + &upf_dl_ip); + ogs_asn_ip_to_BIT_STRING(&upf_dl_ip, &gTPTunnel->transportLayerAddress); + ogs_asn_uint32_to_OCTET_STRING( + sess->handover.upf_dl_teid, &gTPTunnel->gTP_TEID); + + ogs_list_for_each(&sess->pfcp.pdr_list, pdr) { + ogs_pfcp_far_t *far = pdr->far; + ogs_assert(far); + + if (pdr->src_if == OGS_PFCP_INTERFACE_ACCESS && + far->dst_if == OGS_PFCP_INTERFACE_ACCESS) { + NGAP_QosFlowToBeForwardedItem_t *qosFlowToBeForwardedItem; + NGAP_QosFlowIdentifier_t *qosFlowIdentifier = NULL; + + if (!qosFlowToBeForwardedList) { + message.qosFlowToBeForwardedList = + qosFlowToBeForwardedList = + CALLOC(1, sizeof(*qosFlowToBeForwardedList)); + ogs_assert(qosFlowToBeForwardedList); + } + + qosFlowToBeForwardedItem = + CALLOC(1, sizeof(*qosFlowToBeForwardedItem)); + ogs_assert(qosFlowToBeForwardedItem); + + ASN_SEQUENCE_ADD(&qosFlowToBeForwardedList->list, + qosFlowToBeForwardedItem); + + qosFlowIdentifier = + &qosFlowToBeForwardedItem->qosFlowIdentifier; + + *qosFlowIdentifier = pdr->qfi; + } + } + } + + return ogs_asn_encode(&asn_DEF_NGAP_HandoverCommandTransfer, &message); } diff --git a/src/smf/ngap-handler.c b/src/smf/ngap-handler.c index dd599ffe6..a93adb6e3 100644 --- a/src/smf/ngap-handler.c +++ b/src/smf/ngap-handler.c @@ -26,13 +26,11 @@ int ngap_handle_pdu_session_resource_setup_response_transfer( smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_pkbuf_t *pkbuf) { smf_ue_t *smf_ue = NULL; - smf_bearer_t *qos_flow = NULL; int rv, i; uint32_t gnb_n3_teid; ogs_ip_t gnb_n3_ip; - ogs_pfcp_far_t *dl_far = NULL; bool far_update = false; NGAP_PDUSessionResourceSetupResponseTransfer_t message; @@ -111,11 +109,11 @@ int ngap_handle_pdu_session_resource_setup_response_transfer( associatedQosFlowList->list.array[i]; if (associatedQosFlowItem) { - qos_flow = smf_qos_flow_find_by_qfi(sess, - associatedQosFlowItem->qosFlowIdentifier); + smf_bearer_t *qos_flow = smf_qos_flow_find_by_qfi( + sess, associatedQosFlowItem->qosFlowIdentifier); if (qos_flow) { - dl_far = qos_flow->dl_far; + 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; @@ -139,7 +137,8 @@ int ngap_handle_pdu_session_resource_setup_response_transfer( if (far_update) { smf_5gc_pfcp_send_session_modification_request( - sess, stream, OGS_PFCP_MODIFY_ACTIVATE); + sess, stream, OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_ACTIVATE, + 0); } else { /* ACTIVATED Is NOT Included in RESPONSE */ smf_sbi_send_http_status_no_content(stream); @@ -235,13 +234,11 @@ int ngap_handle_path_switch_request_transfer( smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_pkbuf_t *pkbuf) { smf_ue_t *smf_ue = NULL; - smf_bearer_t *qos_flow = NULL; int rv, i; uint32_t gnb_n3_teid; ogs_ip_t gnb_n3_ip; - ogs_pfcp_far_t *dl_far = NULL; bool far_update = false; NGAP_PathSwitchRequestTransfer_t message; @@ -311,11 +308,11 @@ int ngap_handle_path_switch_request_transfer( acceptedQosFlowItem = (NGAP_QosFlowAcceptedItem_t *) qosFlowAcceptedList->list.array[i]; if (acceptedQosFlowItem) { - qos_flow = smf_qos_flow_find_by_qfi(sess, - acceptedQosFlowItem->qosFlowIdentifier); + smf_bearer_t *qos_flow = smf_qos_flow_find_by_qfi( + sess, acceptedQosFlowItem->qosFlowIdentifier); if (qos_flow) { - dl_far = qos_flow->dl_far; + 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; @@ -340,8 +337,9 @@ int ngap_handle_path_switch_request_transfer( if (far_update) { smf_5gc_pfcp_send_session_modification_request( sess, stream, - OGS_PFCP_MODIFY_ACTIVATE | OGS_PFCP_MODIFY_XN_HANDOVER | - OGS_PFCP_MODIFY_END_MARKER); + OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_ACTIVATE| + OGS_PFCP_MODIFY_XN_HANDOVER|OGS_PFCP_MODIFY_END_MARKER, + 0); } else { /* ACTIVATED Is NOT Included in RESPONSE */ smf_sbi_send_http_status_no_content(stream); @@ -362,6 +360,9 @@ int ngap_handle_handover_required_transfer( NGAP_HandoverRequiredTransfer_t message; + NGAP_DirectForwardingPathAvailability_t + *directForwardingPathAvailability = NULL; + ogs_pkbuf_t *n2smbuf = NULL; ogs_assert(pkbuf); @@ -382,6 +383,13 @@ int ngap_handle_handover_required_transfer( goto cleanup; } + directForwardingPathAvailability = message.directForwardingPathAvailability; + if (directForwardingPathAvailability) { + sess->handover.direct_available = true; + } else { + sess->handover.direct_available = false; + } + n2smbuf = ngap_build_pdu_session_resource_setup_request_transfer(sess); ogs_assert(n2smbuf); @@ -400,20 +408,16 @@ int ngap_handle_handover_request_ack( smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_pkbuf_t *pkbuf) { smf_ue_t *smf_ue = NULL; - smf_bearer_t *qos_flow = NULL; int rv, i; - ogs_pfcp_far_t *dl_far = NULL; - NGAP_HandoverRequestAcknowledgeTransfer_t message; NGAP_UPTransportLayerInformation_t *dL_NGU_UP_TNLInformation = NULL; + NGAP_UPTransportLayerInformation_t *dLForwardingUP_TNLInformation = NULL; NGAP_QosFlowListWithDataForwarding_t *qosFlowSetupResponseList = NULL; NGAP_QosFlowItemWithDataForwarding_t *qosFlowSetupResponseItem = NULL; NGAP_GTPTunnel_t *gTPTunnel = NULL; - ogs_pkbuf_t *n2smbuf = NULL; - ogs_assert(pkbuf); ogs_assert(stream); @@ -456,23 +460,21 @@ int ngap_handle_handover_request_ack( goto cleanup; } - /* Store FAR */ ogs_asn_BIT_STRING_to_ip(&gTPTunnel->transportLayerAddress, &sess->handover.gnb_n3_ip); ogs_asn_OCTET_STRING_to_uint32(&gTPTunnel->gTP_TEID, &sess->handover.gnb_n3_teid); - sess->handover.prepared = true; qosFlowSetupResponseList = &message.qosFlowSetupResponseList; for (i = 0; i < qosFlowSetupResponseList->list.count; i++) { qosFlowSetupResponseItem = (NGAP_QosFlowItemWithDataForwarding_t *) qosFlowSetupResponseList->list.array[i]; if (qosFlowSetupResponseItem) { - qos_flow = smf_qos_flow_find_by_qfi(sess, - qosFlowSetupResponseItem->qosFlowIdentifier); + smf_bearer_t *qos_flow = smf_qos_flow_find_by_qfi( + sess, qosFlowSetupResponseItem->qosFlowIdentifier); if (qos_flow) { - dl_far = qos_flow->dl_far; + ogs_pfcp_far_t *dl_far = qos_flow->dl_far; ogs_assert(dl_far); dl_far->handover.prepared = true; @@ -487,12 +489,78 @@ int ngap_handle_handover_request_ack( } } - n2smbuf = ngap_build_handover_command_transfer(sess); - ogs_assert(n2smbuf); + dLForwardingUP_TNLInformation = message.dLForwardingUP_TNLInformation; + if (dLForwardingUP_TNLInformation) { + if (dLForwardingUP_TNLInformation->present != + NGAP_UPTransportLayerInformation_PR_gTPTunnel) { + ogs_error( + "[%s:%d] Unknown dLForwardingUP_TNLInformation->present [%d]", + smf_ue->supi, sess->psi, dL_NGU_UP_TNLInformation->present); + smf_sbi_send_sm_context_update_error(stream, + OGS_SBI_HTTP_STATUS_BAD_REQUEST, + "Unknown dLForwardingUP_TNLInformation->present", + smf_ue->supi, NULL, NULL); + goto cleanup; + } - smf_sbi_send_sm_context_updated_data( - sess, stream, 0, OpenAPI_ho_state_PREPARED, - NULL, OpenAPI_n2_sm_info_type_HANDOVER_CMD, n2smbuf); + gTPTunnel = dLForwardingUP_TNLInformation->choice.gTPTunnel; + if (!gTPTunnel) { + ogs_error("[%s:%d] No GTPTunnel", smf_ue->supi, sess->psi); + smf_sbi_send_sm_context_update_error(stream, + OGS_SBI_HTTP_STATUS_BAD_REQUEST, + "No GTPTunnel", smf_ue->supi, NULL, NULL); + goto cleanup; + } + + ogs_asn_BIT_STRING_to_ip(&gTPTunnel->transportLayerAddress, + &sess->handover.gnb_dl_ip); + ogs_asn_OCTET_STRING_to_uint32(&gTPTunnel->gTP_TEID, + &sess->handover.gnb_dl_teid); + + sess->handover.indirect_data_forwarding = true; + } + + sess->handover.prepared = true; + + if (sess->handover.indirect_data_forwarding == true) { + if (smf_sess_have_indirect_data_forwarding(sess) == true) { + ogs_error("We found redundant INDIRECT Tunnel"); + ogs_error("It will be automatically removed"); + + smf_5gc_pfcp_send_session_modification_request( + sess, stream, + OGS_PFCP_MODIFY_INDIRECT| + /* + * Firstly, OGS_PFCP_MODIFY_REMOVE is only appled. + * And then, after receiving PFCP response message, + * we can apply OGS_PFCP_MODIFY_CREATE. + * + * PFCP build is implemented as below. + * + * if OGS_PFCP_MODIFY_REMOVE + * else if OGS_PFCP_MODIFY_CREATE + * else if .. + * ... + */ + OGS_PFCP_MODIFY_REMOVE|OGS_PFCP_MODIFY_CREATE, + 0); + } else { + + smf_sess_create_indirect_data_forwarding(sess); + + smf_5gc_pfcp_send_session_modification_request( + sess, stream, + OGS_PFCP_MODIFY_INDIRECT|OGS_PFCP_MODIFY_CREATE, + 0); + } + } else { + ogs_pkbuf_t *n2smbuf = ngap_build_handover_command_transfer(sess); + ogs_assert(n2smbuf); + + smf_sbi_send_sm_context_updated_data( + sess, stream, 0, OpenAPI_ho_state_PREPARED, + NULL, OpenAPI_n2_sm_info_type_HANDOVER_CMD, n2smbuf); + } rv = OGS_OK; diff --git a/src/smf/nsmf-handler.c b/src/smf/nsmf-handler.c index 8419a617e..d55b90b1d 100644 --- a/src/smf/nsmf-handler.c +++ b/src/smf/nsmf-handler.c @@ -350,7 +350,9 @@ bool smf_nsmf_handle_update_sm_context( * Handle DEACTIVATED ********************************************************/ smf_5gc_pfcp_send_session_modification_request( - sess, stream, OGS_PFCP_MODIFY_DEACTIVATE); + sess, stream, + OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE, + 0); } else if (SmContextUpdateData->up_cnx_state == OpenAPI_up_cnx_state_ACTIVATING) { @@ -402,7 +404,6 @@ bool smf_nsmf_handle_update_sm_context( sendmsg.SmContextUpdatedData = &SmContextUpdatedData; memset(&SmContextUpdatedData, 0, sizeof(SmContextUpdatedData)); - /* Only ACTIVING & DEACTIVATED is Included */ SmContextUpdatedData.up_cnx_state = OpenAPI_up_cnx_state_ACTIVATING; SmContextUpdatedData.n2_sm_info_type = OpenAPI_n2_sm_info_type_PDU_RES_SETUP_REQ; @@ -484,8 +485,9 @@ bool smf_nsmf_handle_update_sm_context( if (far_update) { smf_5gc_pfcp_send_session_modification_request( sess, stream, - OGS_PFCP_MODIFY_ACTIVATE | OGS_PFCP_MODIFY_N2_HANDOVER | - OGS_PFCP_MODIFY_END_MARKER); + OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_ACTIVATE| + OGS_PFCP_MODIFY_N2_HANDOVER|OGS_PFCP_MODIFY_END_MARKER, + 0); } else { char *strerror = ogs_msprintf( "[%s:%d] No FAR Update", smf_ue->supi, sess->psi); @@ -512,8 +514,16 @@ bool smf_nsmf_handle_update_sm_context( dl_far->handover.prepared = false; } - smf_sbi_send_sm_context_updated_data_ho_state( - sess, stream, OpenAPI_ho_state_CANCELLED); + if (smf_sess_have_indirect_data_forwarding(sess) == true) { + smf_5gc_pfcp_send_session_modification_request( + sess, stream, + OGS_PFCP_MODIFY_INDIRECT|OGS_PFCP_MODIFY_REMOVE| + OGS_PFCP_MODIFY_HANDOVER_CANCEL, + 0); + } else { + smf_sbi_send_sm_context_updated_data_ho_state( + sess, stream, OpenAPI_ho_state_CANCELLED); + } } else { char *strerror = ogs_msprintf("[%s:%d] Invalid hoState [%d]", diff --git a/src/smf/pfcp-path.c b/src/smf/pfcp-path.c index effd6b452..344fd8e79 100644 --- a/src/smf/pfcp-path.c +++ b/src/smf/pfcp-path.c @@ -330,7 +330,8 @@ void smf_5gc_pfcp_send_session_establishment_request( } void smf_5gc_pfcp_send_session_modification_request( - smf_sess_t *sess, ogs_sbi_stream_t *stream, uint64_t flags) + smf_sess_t *sess, ogs_sbi_stream_t *stream, + uint64_t flags, ogs_time_t duration) { int rv; ogs_pkbuf_t *n4buf = NULL; @@ -354,8 +355,12 @@ void smf_5gc_pfcp_send_session_modification_request( xact->assoc_stream = stream; xact->modify_flags = flags | OGS_PFCP_MODIFY_SESSION; - rv = ogs_pfcp_xact_commit(xact); - ogs_expect(rv == OGS_OK); + if (duration) { + ogs_pfcp_xact_delayed_commit(xact, duration); + } else { + rv = ogs_pfcp_xact_commit(xact); + ogs_expect(rv == OGS_OK); + } } void smf_5gc_pfcp_send_qos_flow_modification_request(smf_bearer_t *qos_flow, diff --git a/src/smf/pfcp-path.h b/src/smf/pfcp-path.h index 728cd7412..346f01098 100644 --- a/src/smf/pfcp-path.h +++ b/src/smf/pfcp-path.h @@ -32,7 +32,8 @@ void smf_pfcp_close(void); void smf_5gc_pfcp_send_session_establishment_request( smf_sess_t *sess, ogs_sbi_stream_t *stream); void smf_5gc_pfcp_send_session_modification_request( - smf_sess_t *sess, ogs_sbi_stream_t *stream, uint64_t flags); + smf_sess_t *sess, ogs_sbi_stream_t *stream, + uint64_t flags, ogs_time_t duration); void smf_5gc_pfcp_send_qos_flow_modification_request(smf_bearer_t *qos_flow, ogs_sbi_stream_t *stream, uint64_t flags); void smf_5gc_pfcp_send_session_deletion_request( diff --git a/src/upf/gtp-path.c b/src/upf/gtp-path.c index 374e47812..218cb17dd 100644 --- a/src/upf/gtp-path.c +++ b/src/upf/gtp-path.c @@ -227,6 +227,7 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) struct ip *ip_h = NULL; ogs_pfcp_pdr_t *pdr = NULL; + ogs_pfcp_far_t *far = NULL; upf_sess_t *sess = NULL; ogs_pfcp_subnet_t *subnet = NULL; @@ -240,38 +241,60 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) /* TODO : Send Error Indication */ goto cleanup; } + ogs_assert(pdr->sess); sess = UPF_SESS(pdr->sess); ogs_assert(sess); - if (ip_h->ip_v == 4 && sess->ipv4) - subnet = sess->ipv4->subnet; - else if (ip_h->ip_v == 6 && sess->ipv6) - subnet = sess->ipv6->subnet; + far = pdr->far; + ogs_assert(far); - if (!subnet) { + if (far->dst_if == OGS_PFCP_INTERFACE_ACCESS) { + ogs_pfcp_up_handle_pdr(pdr, pkbuf, &report); + + if (report.type.downlink_data_report) { + ogs_error("Indirect Data Fowarding Buffered"); + + report.downlink_data.pdr_id = pdr->id; + if (pdr->qer && pdr->qer->qfi) + report.downlink_data.qfi = pdr->qer->qfi; /* for 5GC */ + + upf_pfcp_send_session_report_request(sess, &report); + } + + } else if (far->dst_if == OGS_PFCP_INTERFACE_CORE) { + if (ip_h->ip_v == 4 && sess->ipv4) + subnet = sess->ipv4->subnet; + else if (ip_h->ip_v == 6 && sess->ipv6) + subnet = sess->ipv6->subnet; + + if (!subnet) { #if 0 /* It's redundant log message */ - ogs_error("[DROP] Cannot find subnet V:%d, IPv4:%p, IPv6:%p", - ip_h->ip_v, sess->ipv4, sess->ipv6); - ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len); + ogs_error("[DROP] Cannot find subnet V:%d, IPv4:%p, IPv6:%p", + ip_h->ip_v, sess->ipv4, sess->ipv6); + ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len); #endif - goto cleanup; - } - - /* Check IPv6 */ - if (ogs_app()->parameter.no_slaac == 0 && ip_h->ip_v == 6) { - rv = upf_gtp_handle_slaac(sess, pkbuf); - if (rv == UPF_GTP_HANDLED) { goto cleanup; } - ogs_assert(rv == OGS_OK); - } - dev = subnet->dev; - ogs_assert(dev); - if (ogs_write(dev->fd, pkbuf->data, pkbuf->len) <= 0) { - ogs_log_message(OGS_LOG_ERROR, - ogs_socket_errno, "ogs_write() failed"); + /* Check IPv6 */ + if (ogs_app()->parameter.no_slaac == 0 && ip_h->ip_v == 6) { + rv = upf_gtp_handle_slaac(sess, pkbuf); + if (rv == UPF_GTP_HANDLED) { + goto cleanup; + } + ogs_assert(rv == OGS_OK); + } + + dev = subnet->dev; + ogs_assert(dev); + if (ogs_write(dev->fd, pkbuf->data, pkbuf->len) <= 0) { + ogs_log_message(OGS_LOG_ERROR, + ogs_socket_errno, "ogs_write() failed"); + } + } else { + ogs_fatal("Not implemented : FAR-DST_IF[%d]", far->dst_if); + ogs_assert_if_reached(); } } else { ogs_error("[DROP] Invalid GTPU Type [%d]", gtp_h->type); diff --git a/tests/common/context.h b/tests/common/context.h index cab01046d..655377e97 100644 --- a/tests/common/context.h +++ b/tests/common/context.h @@ -418,6 +418,14 @@ typedef struct test_sess_s { test_pdn_connectivity_param_t pdn_connectivity_param; test_esm_information_param_t esm_information_param; + struct { + bool data_forwarding_not_possible; + + /* Indirect DL Forwarding */ + uint32_t upf_dl_teid; + ogs_ip_t upf_dl_ip; + } handover; + ogs_list_t bearer_list; test_ue_t *test_ue; diff --git a/tests/common/gtpu.c b/tests/common/gtpu.c index 099cc3ba9..777ff52b1 100644 --- a/tests/common/gtpu.c +++ b/tests/common/gtpu.c @@ -327,8 +327,6 @@ int test_gtpu_send_error_indication( sess = bearer->sess; ogs_assert(sess); - ogs_assert(bearer); - if (bearer->qfi) { /* 5GC */ teid = sess->gnb_n3_teid; @@ -354,3 +352,38 @@ int test_gtpu_send_error_indication( return test_gtpu_send(node, bearer, >p_hdesc, &ext_hdesc, pkbuf); } + +int test_gtpu_send_indirect_data_forwarding( + ogs_socknode_t *node, test_bearer_t *bearer, ogs_pkbuf_t *pkbuf) +{ + test_sess_t *sess = NULL; + + ogs_gtp_header_t gtp_hdesc; + ogs_gtp_extension_header_t ext_hdesc; + + ogs_assert(bearer); + sess = bearer->sess; + ogs_assert(sess); + ogs_assert(pkbuf); + + memset(>p_hdesc, 0, sizeof(gtp_hdesc)); + memset(&ext_hdesc, 0, sizeof(ext_hdesc)); + + gtp_hdesc.type = OGS_GTPU_MSGTYPE_GPDU; + + if (bearer->qfi) { + gtp_hdesc.teid = sess->handover.upf_dl_teid; + ext_hdesc.qos_flow_identifier = bearer->qfi; + + } else if (bearer->ebi) { + + ogs_fatal("Not implmented EPC Indirect Tunnel"); + ogs_assert_if_reached(); + + } else { + ogs_fatal("No QFI[%d] and EBI[%d]", bearer->qfi, bearer->ebi); + ogs_assert_if_reached(); + } + + return test_gtpu_send(node, bearer, >p_hdesc, &ext_hdesc, pkbuf); +} diff --git a/tests/common/gtpu.h b/tests/common/gtpu.h index 36896aa8d..0b353a687 100644 --- a/tests/common/gtpu.h +++ b/tests/common/gtpu.h @@ -40,6 +40,8 @@ int test_gtpu_send_ping( int test_gtpu_send_slacc_rs(ogs_socknode_t *node, test_bearer_t *bearer); int test_gtpu_send_error_indication( ogs_socknode_t *node, test_bearer_t *bearer); +int test_gtpu_send_indirect_data_forwarding( + ogs_socknode_t *node, test_bearer_t *bearer, ogs_pkbuf_t *pkbuf); #ifdef __cplusplus } diff --git a/tests/common/ngap-build.c b/tests/common/ngap-build.c index d8fb04b97..a88809396 100644 --- a/tests/common/ngap-build.c +++ b/tests/common/ngap-build.c @@ -1607,7 +1607,6 @@ ogs_pkbuf_t *testngap_build_handover_request_ack(test_ue_t *test_ue) NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; NGAP_RAN_UE_NGAP_ID_t *RAN_UE_NGAP_ID = NULL; NGAP_PDUSessionResourceAdmittedList_t *PDUSessionList = NULL; - NGAP_PDUSessionResourceAdmittedItem_t *PDUSessionItem = NULL; NGAP_TargetToSource_TransparentContainer_t *TargetToSource_TransparentContainer = NULL; @@ -1657,6 +1656,7 @@ ogs_pkbuf_t *testngap_build_handover_request_ack(test_ue_t *test_ue) *RAN_UE_NGAP_ID = test_ue->ran_ue_ngap_id; ogs_list_for_each(&test_ue->sess_list, sess) { + NGAP_PDUSessionResourceAdmittedItem_t *PDUSessionItem = NULL; OCTET_STRING_t *transfer = NULL; ogs_pkbuf_t *n2smbuf = NULL; @@ -1672,8 +1672,7 @@ ogs_pkbuf_t *testngap_build_handover_request_ack(test_ue_t *test_ue) PDUSessionList = &ie->value.choice.PDUSessionResourceAdmittedList; } - PDUSessionItem = CALLOC(1, - sizeof(struct NGAP_PDUSessionResourceAdmittedItem)); + PDUSessionItem = CALLOC(1, sizeof(*PDUSessionItem)); ogs_assert(PDUSessionItem); ASN_SEQUENCE_ADD(&PDUSessionList->list, PDUSessionItem); @@ -2203,6 +2202,7 @@ static ogs_pkbuf_t *testngap_build_handover_request_ack_transfer( NGAP_HandoverRequestAcknowledgeTransfer_t message; NGAP_UPTransportLayerInformation_t *dL_NGU_UP_TNLInformation = NULL; + NGAP_UPTransportLayerInformation_t *dLForwardingUP_TNLInformation = NULL; NGAP_QosFlowListWithDataForwarding_t *qosFlowSetupResponseList = NULL; NGAP_QosFlowItemWithDataForwarding_t *qosFlowSetupResponseItem = NULL; NGAP_GTPTunnel_t *gTPTunnel = NULL; @@ -2214,7 +2214,7 @@ static ogs_pkbuf_t *testngap_build_handover_request_ack_transfer( dL_NGU_UP_TNLInformation->present = NGAP_UPTransportLayerInformation_PR_gTPTunnel; dL_NGU_UP_TNLInformation->choice.gTPTunnel = gTPTunnel = - CALLOC(1, sizeof(struct NGAP_GTPTunnel)); + CALLOC(1, sizeof(*gTPTunnel)); ogs_assert(gTPTunnel); ogs_assert(sess->gnb_n3_addr || sess->gnb_n3_addr6); @@ -2228,6 +2228,25 @@ static ogs_pkbuf_t *testngap_build_handover_request_ack_transfer( ogs_asn_ip_to_BIT_STRING(&ip, &gTPTunnel->transportLayerAddress); ogs_asn_uint32_to_OCTET_STRING(sess->gnb_n3_teid, &gTPTunnel->gTP_TEID); + if (sess->handover.data_forwarding_not_possible) { + NGAP_GTPTunnel_t *gTPTunnelForDLForwarding = NULL; + message.dLForwardingUP_TNLInformation = dLForwardingUP_TNLInformation = + CALLOC(1, sizeof(*dLForwardingUP_TNLInformation)); + ogs_assert(dLForwardingUP_TNLInformation); + + dLForwardingUP_TNLInformation->present = + NGAP_UPTransportLayerInformation_PR_gTPTunnel; + dLForwardingUP_TNLInformation->choice.gTPTunnel = + gTPTunnelForDLForwarding = + CALLOC(1, sizeof(*gTPTunnelForDLForwarding)); + ogs_assert(gTPTunnelForDLForwarding); + + ogs_asn_ip_to_BIT_STRING(&ip, + &gTPTunnelForDLForwarding->transportLayerAddress); + ogs_asn_uint32_to_OCTET_STRING(sess->gnb_n3_teid+10, + &gTPTunnelForDLForwarding->gTP_TEID); + } + qosFlowSetupResponseList = &message.qosFlowSetupResponseList; ogs_list_for_each(&sess->bearer_list, qos_flow) { diff --git a/tests/common/ngap-handler.c b/tests/common/ngap-handler.c index ef9915bb1..0154b9ca0 100644 --- a/tests/common/ngap-handler.c +++ b/tests/common/ngap-handler.c @@ -604,15 +604,24 @@ void testngap_handle_pdu_session_resource_release_command( void testngap_handle_handover_request( test_ue_t *test_ue, ogs_ngap_message_t *message) { - int rv, i; + int rv, i, j, k; char buf[OGS_ADDRSTRLEN]; + test_sess_t *sess = NULL; + NGAP_NGAP_PDU_t pdu; NGAP_InitiatingMessage_t *initiatingMessage = NULL; NGAP_HandoverRequest_t *HandoverRequest = NULL; NGAP_HandoverRequestIEs_t *ie = NULL; NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_PDUSessionResourceSetupListHOReq_t *PDUSessionList = NULL; + NGAP_PDUSessionResourceSetupItemHOReq_t *PDUSessionItem = NULL; + + NGAP_PDUSessionResourceSetupRequestTransfer_t n2sm_message; + NGAP_PDUSessionResourceSetupRequestTransferIEs_t *ie2 = NULL; + OCTET_STRING_t *transfer = NULL; + ogs_pkbuf_t *n2smbuf = NULL; ogs_assert(test_ue); ogs_assert(message); @@ -628,6 +637,8 @@ void testngap_handle_handover_request( case NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID: AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; break; + case NGAP_ProtocolIE_ID_id_PDUSessionResourceSetupListHOReq: + PDUSessionList = &ie->value.choice.PDUSessionResourceSetupListHOReq; default: break; } @@ -638,4 +649,299 @@ void testngap_handle_handover_request( asn_INTEGER2ulong(AMF_UE_NGAP_ID, (unsigned long *)&amf_ue_ngap_id); test_ue->amf_ue_ngap_id = (uint64_t)amf_ue_ngap_id; } + + if (PDUSessionList) { + for (j = 0; j < PDUSessionList->list.count; j++) { + PDUSessionItem = (NGAP_PDUSessionResourceSetupItemHOReq_t *) + PDUSessionList->list.array[j]; + ogs_assert(PDUSessionItem); + + sess = test_sess_find_by_psi( + test_ue, PDUSessionItem->pDUSessionID); + ogs_assert(sess); + + transfer = &PDUSessionItem->handoverRequestTransfer; + ogs_assert(transfer); + + n2smbuf = ogs_pkbuf_alloc(NULL, OGS_MAX_SDU_LEN); + ogs_assert(n2smbuf); + ogs_pkbuf_put_data(n2smbuf, transfer->buf, transfer->size); + + rv = ogs_asn_decode( + &asn_DEF_NGAP_PDUSessionResourceSetupRequestTransfer, + &n2sm_message, sizeof(n2sm_message), n2smbuf); + ogs_assert(rv == OGS_OK); + + for (k = 0; k < n2sm_message.protocolIEs.list.count; k++) { + ie2 = n2sm_message.protocolIEs.list.array[k]; + switch (ie2->id) { + case NGAP_ProtocolIE_ID_id_DataForwardingNotPossible: + sess->handover.data_forwarding_not_possible = true; + break; + default: + break; + } + } + + ogs_asn_free( + &asn_DEF_NGAP_PDUSessionResourceSetupRequestTransfer, + &n2sm_message); + + ogs_pkbuf_free(n2smbuf); + } + } +} + +void testngap_handle_handover_command( + test_ue_t *test_ue, ogs_ngap_message_t *message) +{ + int rv, i, j; + char buf[OGS_ADDRSTRLEN]; + + test_sess_t *sess = NULL; + + NGAP_NGAP_PDU_t pdu; + NGAP_SuccessfulOutcome_t *successfulOutcome = NULL; + NGAP_HandoverCommand_t *HandoverCommand = NULL; + + NGAP_HandoverCommandIEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_RAN_UE_NGAP_ID_t *RAN_UE_NGAP_ID = NULL; + NGAP_PDUSessionResourceHandoverList_t *PDUSessionList = NULL; + NGAP_PDUSessionResourceHandoverItem_t *PDUSessionItem = NULL; + + NGAP_HandoverCommandTransfer_t n2sm_message; + OCTET_STRING_t *transfer = NULL; + ogs_pkbuf_t *n2smbuf = NULL; + + ogs_assert(test_ue); + ogs_assert(message); + + successfulOutcome = message->choice.successfulOutcome; + ogs_assert(successfulOutcome); + HandoverCommand = &successfulOutcome->value.choice.HandoverCommand; + ogs_assert(HandoverCommand); + + for (i = 0; i < HandoverCommand->protocolIEs.list.count; i++) { + ie = HandoverCommand->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID: + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID: + RAN_UE_NGAP_ID = &ie->value.choice.RAN_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_PDUSessionResourceHandoverList: + PDUSessionList = + &ie->value.choice.PDUSessionResourceHandoverList; + default: + break; + } + } + + if (AMF_UE_NGAP_ID) { + uint64_t amf_ue_ngap_id; + asn_INTEGER2ulong(AMF_UE_NGAP_ID, (unsigned long *)&amf_ue_ngap_id); + test_ue->amf_ue_ngap_id = (uint64_t)amf_ue_ngap_id; + } + + if (RAN_UE_NGAP_ID) { + test_ue->ran_ue_ngap_id = *RAN_UE_NGAP_ID; + } + + if (PDUSessionList) { + for (j = 0; j < PDUSessionList->list.count; j++) { + NGAP_UPTransportLayerInformation_t *dLForwardingUP_TNLInformation; + + PDUSessionItem = (NGAP_PDUSessionResourceHandoverItem_t *) + PDUSessionList->list.array[j]; + ogs_assert(PDUSessionItem); + + sess = test_sess_find_by_psi( + test_ue, PDUSessionItem->pDUSessionID); + ogs_assert(sess); + + transfer = &PDUSessionItem->handoverCommandTransfer; + ogs_assert(transfer); + + n2smbuf = ogs_pkbuf_alloc(NULL, OGS_MAX_SDU_LEN); + ogs_assert(n2smbuf); + ogs_pkbuf_put_data(n2smbuf, transfer->buf, transfer->size); + + rv = ogs_asn_decode( + &asn_DEF_NGAP_HandoverCommandTransfer, + &n2sm_message, sizeof(n2sm_message), n2smbuf); + ogs_assert(rv == OGS_OK); + + dLForwardingUP_TNLInformation = + n2sm_message.dLForwardingUP_TNLInformation; + if (dLForwardingUP_TNLInformation) { + if (dLForwardingUP_TNLInformation->present == + NGAP_UPTransportLayerInformation_PR_gTPTunnel) { + NGAP_GTPTunnel_t *gTPTunnel = + dLForwardingUP_TNLInformation->choice.gTPTunnel; + if (gTPTunnel) { + ogs_asn_BIT_STRING_to_ip( + &gTPTunnel->transportLayerAddress, + &sess->handover.upf_dl_ip); + ogs_asn_OCTET_STRING_to_uint32( + &gTPTunnel->gTP_TEID, + &sess->handover.upf_dl_teid); + } + } + } + + ogs_asn_free( + &asn_DEF_NGAP_HandoverCommandTransfer, + &n2sm_message); + + ogs_pkbuf_free(n2smbuf); + } + } +} + +void testngap_handle_handover_preparation_failure( + test_ue_t *test_ue, ogs_ngap_message_t *message) +{ + int rv, i, j; + char buf[OGS_ADDRSTRLEN]; + + test_sess_t *sess = NULL; + + NGAP_NGAP_PDU_t pdu; + NGAP_UnsuccessfulOutcome_t *unsuccessfulOutcome = NULL; + NGAP_HandoverPreparationFailure_t *HandoverPreparationFailure = NULL; + + NGAP_HandoverPreparationFailureIEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_RAN_UE_NGAP_ID_t *RAN_UE_NGAP_ID = NULL; + + ogs_assert(test_ue); + ogs_assert(message); + + unsuccessfulOutcome = message->choice.unsuccessfulOutcome; + ogs_assert(unsuccessfulOutcome); + HandoverPreparationFailure = + &unsuccessfulOutcome->value.choice.HandoverPreparationFailure; + ogs_assert(HandoverPreparationFailure); + + for (i = 0; i < HandoverPreparationFailure->protocolIEs.list.count; i++) { + ie = HandoverPreparationFailure->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID: + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID: + RAN_UE_NGAP_ID = &ie->value.choice.RAN_UE_NGAP_ID; + break; + default: + break; + } + } + + if (AMF_UE_NGAP_ID) { + uint64_t amf_ue_ngap_id; + asn_INTEGER2ulong(AMF_UE_NGAP_ID, (unsigned long *)&amf_ue_ngap_id); + test_ue->amf_ue_ngap_id = (uint64_t)amf_ue_ngap_id; + } + + if (RAN_UE_NGAP_ID) { + test_ue->ran_ue_ngap_id = *RAN_UE_NGAP_ID; + } +} + +void testngap_handle_handover_cancel_ack( + test_ue_t *test_ue, ogs_ngap_message_t *message) +{ + int rv, i, j; + char buf[OGS_ADDRSTRLEN]; + + test_sess_t *sess = NULL; + + NGAP_NGAP_PDU_t pdu; + NGAP_SuccessfulOutcome_t *successfulOutcome = NULL; + NGAP_HandoverCancelAcknowledge_t *HandoverCancelAcknowledge = NULL; + + NGAP_HandoverCancelAcknowledgeIEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_RAN_UE_NGAP_ID_t *RAN_UE_NGAP_ID = NULL; + + ogs_assert(test_ue); + ogs_assert(message); + + successfulOutcome = message->choice.successfulOutcome; + ogs_assert(successfulOutcome); + HandoverCancelAcknowledge = &successfulOutcome->value.choice.HandoverCancelAcknowledge; + ogs_assert(HandoverCancelAcknowledge); + + for (i = 0; i < HandoverCancelAcknowledge->protocolIEs.list.count; i++) { + ie = HandoverCancelAcknowledge->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID: + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID: + RAN_UE_NGAP_ID = &ie->value.choice.RAN_UE_NGAP_ID; + break; + default: + break; + } + } + + if (AMF_UE_NGAP_ID) { + uint64_t amf_ue_ngap_id; + asn_INTEGER2ulong(AMF_UE_NGAP_ID, (unsigned long *)&amf_ue_ngap_id); + test_ue->amf_ue_ngap_id = (uint64_t)amf_ue_ngap_id; + } + + if (RAN_UE_NGAP_ID) { + test_ue->ran_ue_ngap_id = *RAN_UE_NGAP_ID; + } +} + +void testngap_handle_downlink_ran_status_transfer( + test_ue_t *test_ue, ogs_ngap_message_t *message) +{ + int i; + char buf[OGS_ADDRSTRLEN]; + + NGAP_NGAP_PDU_t pdu; + NGAP_InitiatingMessage_t *initiatingMessage = NULL; + NGAP_DownlinkRANStatusTransfer_t *DownlinkRANStatusTransfer = NULL; + + NGAP_DownlinkRANStatusTransferIEs_t *ie = NULL; + NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL; + NGAP_RAN_UE_NGAP_ID_t *RAN_UE_NGAP_ID = NULL; + + ogs_assert(test_ue); + ogs_assert(message); + + initiatingMessage = message->choice.initiatingMessage; + ogs_assert(initiatingMessage); + DownlinkRANStatusTransfer = + &initiatingMessage->value.choice.DownlinkRANStatusTransfer; + ogs_assert(DownlinkRANStatusTransfer); + + for (i = 0; i < DownlinkRANStatusTransfer->protocolIEs.list.count; i++) { + ie = DownlinkRANStatusTransfer->protocolIEs.list.array[i]; + switch (ie->id) { + case NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID: + AMF_UE_NGAP_ID = &ie->value.choice.AMF_UE_NGAP_ID; + break; + case NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID: + RAN_UE_NGAP_ID = &ie->value.choice.RAN_UE_NGAP_ID; + break; + default: + break; + } + } + + if (AMF_UE_NGAP_ID) { + uint64_t amf_ue_ngap_id; + asn_INTEGER2ulong(AMF_UE_NGAP_ID, (unsigned long *)&amf_ue_ngap_id); + test_ue->amf_ue_ngap_id = (uint64_t)amf_ue_ngap_id; + } + if (RAN_UE_NGAP_ID) { + test_ue->ran_ue_ngap_id = *RAN_UE_NGAP_ID; + } } diff --git a/tests/common/ngap-handler.h b/tests/common/ngap-handler.h index 1b7d596ee..7458cd88f 100644 --- a/tests/common/ngap-handler.h +++ b/tests/common/ngap-handler.h @@ -43,6 +43,14 @@ void testngap_handle_pdu_session_resource_release_command( void testngap_handle_handover_request( test_ue_t *test_ue, ogs_ngap_message_t *message); +void testngap_handle_handover_command( + test_ue_t *test_ue, ogs_ngap_message_t *message); +void testngap_handle_handover_preparation_failure( + test_ue_t *test_ue, ogs_ngap_message_t *message); +void testngap_handle_handover_cancel_ack( + test_ue_t *test_ue, ogs_ngap_message_t *message); +void testngap_handle_downlink_ran_status_transfer( + test_ue_t *test_ue, ogs_ngap_message_t *message); #ifdef __cplusplus } diff --git a/tests/common/ngap-path.c b/tests/common/ngap-path.c index 1c1641e92..b55a8feda 100644 --- a/tests/common/ngap-path.c +++ b/tests/common/ngap-path.c @@ -71,6 +71,7 @@ void testngap_recv(test_ue_t *test_ue, ogs_pkbuf_t *pkbuf) testngap_handle_handover_request(test_ue, pdu); break; case NGAP_ProcedureCode_id_DownlinkRANStatusTransfer: + testngap_handle_downlink_ran_status_transfer(test_ue, pdu); break; case NGAP_ProcedureCode_id_ErrorIndication: case NGAP_ProcedureCode_id_Paging: @@ -96,8 +97,10 @@ void testngap_recv(test_ue_t *test_ue, ogs_pkbuf_t *pkbuf) case NGAP_ProcedureCode_id_PathSwitchRequest: break; case NGAP_ProcedureCode_id_HandoverPreparation: + testngap_handle_handover_command(test_ue, pdu); break; case NGAP_ProcedureCode_id_HandoverCancel: + testngap_handle_handover_cancel_ack(test_ue, pdu); break; case NGAP_ProcedureCode_id_NGReset: break; @@ -118,6 +121,7 @@ void testngap_recv(test_ue_t *test_ue, ogs_pkbuf_t *pkbuf) case NGAP_ProcedureCode_id_NGSetup: break; case NGAP_ProcedureCode_id_HandoverPreparation: + testngap_handle_handover_preparation_failure(test_ue, pdu); break; default: ogs_error("Not implemented(choice:%d, proc:%d)", diff --git a/tests/common/s1ap-handler.c b/tests/common/s1ap-handler.c index 812596569..6ddd4048d 100644 --- a/tests/common/s1ap-handler.c +++ b/tests/common/s1ap-handler.c @@ -399,3 +399,178 @@ void tests1ap_handle_handover_request( if (MME_UE_S1AP_ID) test_ue->mme_ue_s1ap_id = *MME_UE_S1AP_ID; } + +void tests1ap_handle_handover_command( + test_ue_t *test_ue, ogs_s1ap_message_t *message) +{ + int i; + char buf[OGS_ADDRSTRLEN]; + + test_sess_t *sess = NULL; + + S1AP_S1AP_PDU_t pdu; + S1AP_SuccessfulOutcome_t *successfulOutcome = NULL; + S1AP_HandoverCommand_t *HandoverCommand = NULL; + + S1AP_HandoverCommandIEs_t *ie = NULL; + S1AP_MME_UE_S1AP_ID_t *MME_UE_S1AP_ID = NULL; + S1AP_ENB_UE_S1AP_ID_t *ENB_UE_S1AP_ID = NULL; + + ogs_assert(test_ue); + ogs_assert(message); + + successfulOutcome = message->choice.successfulOutcome; + ogs_assert(successfulOutcome); + HandoverCommand = &successfulOutcome->value.choice.HandoverCommand; + ogs_assert(HandoverCommand); + + for (i = 0; i < HandoverCommand->protocolIEs.list.count; i++) { + ie = HandoverCommand->protocolIEs.list.array[i]; + switch (ie->id) { + case S1AP_ProtocolIE_ID_id_MME_UE_S1AP_ID: + MME_UE_S1AP_ID = &ie->value.choice.MME_UE_S1AP_ID; + break; + case S1AP_ProtocolIE_ID_id_eNB_UE_S1AP_ID: + ENB_UE_S1AP_ID = &ie->value.choice.ENB_UE_S1AP_ID; + break; + default: + break; + } + } + + if (MME_UE_S1AP_ID) + test_ue->mme_ue_s1ap_id = *MME_UE_S1AP_ID; + if (ENB_UE_S1AP_ID) + test_ue->enb_ue_s1ap_id = *ENB_UE_S1AP_ID; +} + +void tests1ap_handle_handover_preparation_failure( + test_ue_t *test_ue, ogs_s1ap_message_t *message) +{ + int i; + char buf[OGS_ADDRSTRLEN]; + + test_sess_t *sess = NULL; + + S1AP_S1AP_PDU_t pdu; + S1AP_UnsuccessfulOutcome_t *unsuccessfulOutcome = NULL; + S1AP_HandoverPreparationFailure_t *HandoverPreparationFailure = NULL; + + S1AP_HandoverPreparationFailureIEs_t *ie = NULL; + S1AP_MME_UE_S1AP_ID_t *MME_UE_S1AP_ID = NULL; + S1AP_ENB_UE_S1AP_ID_t *ENB_UE_S1AP_ID = NULL; + + ogs_assert(test_ue); + ogs_assert(message); + + unsuccessfulOutcome = message->choice.unsuccessfulOutcome; + ogs_assert(unsuccessfulOutcome); + HandoverPreparationFailure = + &unsuccessfulOutcome->value.choice.HandoverPreparationFailure; + ogs_assert(HandoverPreparationFailure); + + for (i = 0; i < HandoverPreparationFailure->protocolIEs.list.count; i++) { + ie = HandoverPreparationFailure->protocolIEs.list.array[i]; + switch (ie->id) { + case S1AP_ProtocolIE_ID_id_MME_UE_S1AP_ID: + MME_UE_S1AP_ID = &ie->value.choice.MME_UE_S1AP_ID; + break; + case S1AP_ProtocolIE_ID_id_eNB_UE_S1AP_ID: + ENB_UE_S1AP_ID = &ie->value.choice.ENB_UE_S1AP_ID; + break; + default: + break; + } + } + + if (MME_UE_S1AP_ID) + test_ue->mme_ue_s1ap_id = *MME_UE_S1AP_ID; + if (ENB_UE_S1AP_ID) + test_ue->enb_ue_s1ap_id = *ENB_UE_S1AP_ID; +} + +void tests1ap_handle_handover_cancel_ack( + test_ue_t *test_ue, ogs_s1ap_message_t *message) +{ + int i; + char buf[OGS_ADDRSTRLEN]; + + test_sess_t *sess = NULL; + + S1AP_S1AP_PDU_t pdu; + S1AP_SuccessfulOutcome_t *successfulOutcome = NULL; + S1AP_HandoverCancelAcknowledge_t *HandoverCancelAcknowledge = NULL; + + S1AP_HandoverCancelAcknowledgeIEs_t *ie = NULL; + S1AP_MME_UE_S1AP_ID_t *MME_UE_S1AP_ID = NULL; + S1AP_ENB_UE_S1AP_ID_t *ENB_UE_S1AP_ID = NULL; + + ogs_assert(test_ue); + ogs_assert(message); + + successfulOutcome = message->choice.successfulOutcome; + ogs_assert(successfulOutcome); + HandoverCancelAcknowledge = + &successfulOutcome->value.choice.HandoverCancelAcknowledge; + ogs_assert(HandoverCancelAcknowledge); + + for (i = 0; i < HandoverCancelAcknowledge->protocolIEs.list.count; i++) { + ie = HandoverCancelAcknowledge->protocolIEs.list.array[i]; + switch (ie->id) { + case S1AP_ProtocolIE_ID_id_MME_UE_S1AP_ID: + MME_UE_S1AP_ID = &ie->value.choice.MME_UE_S1AP_ID; + break; + case S1AP_ProtocolIE_ID_id_eNB_UE_S1AP_ID: + ENB_UE_S1AP_ID = &ie->value.choice.ENB_UE_S1AP_ID; + break; + default: + break; + } + } + + if (MME_UE_S1AP_ID) + test_ue->mme_ue_s1ap_id = *MME_UE_S1AP_ID; + if (ENB_UE_S1AP_ID) + test_ue->enb_ue_s1ap_id = *ENB_UE_S1AP_ID; +} + +void tests1ap_handle_mme_status_transfer( + test_ue_t *test_ue, ogs_s1ap_message_t *message) +{ + int i; + + S1AP_S1AP_PDU_t pdu; + S1AP_InitiatingMessage_t *initiatingMessage = NULL; + S1AP_MMEStatusTransfer_t *MMEStatusTransfer = NULL; + + S1AP_MMEStatusTransferIEs_t *ie = NULL; + S1AP_MME_UE_S1AP_ID_t *MME_UE_S1AP_ID = NULL; + S1AP_ENB_UE_S1AP_ID_t *ENB_UE_S1AP_ID = NULL; + + ogs_assert(test_ue); + ogs_assert(message); + + initiatingMessage = message->choice.initiatingMessage; + ogs_assert(initiatingMessage); + MMEStatusTransfer = &initiatingMessage->value.choice.MMEStatusTransfer; + ogs_assert(MMEStatusTransfer); + + for (i = 0; i < MMEStatusTransfer->protocolIEs.list.count; i++) { + ie = MMEStatusTransfer->protocolIEs.list.array[i]; + switch (ie->id) { + case S1AP_ProtocolIE_ID_id_MME_UE_S1AP_ID: + MME_UE_S1AP_ID = &ie->value.choice.MME_UE_S1AP_ID; + break; + case S1AP_ProtocolIE_ID_id_eNB_UE_S1AP_ID: + ENB_UE_S1AP_ID = &ie->value.choice.ENB_UE_S1AP_ID; + break; + default: + break; + } + } + + if (MME_UE_S1AP_ID) + test_ue->mme_ue_s1ap_id = *MME_UE_S1AP_ID; + if (ENB_UE_S1AP_ID) + test_ue->enb_ue_s1ap_id = *ENB_UE_S1AP_ID; +} diff --git a/tests/common/s1ap-handler.h b/tests/common/s1ap-handler.h index 667a3a397..73d528e4f 100644 --- a/tests/common/s1ap-handler.h +++ b/tests/common/s1ap-handler.h @@ -40,6 +40,14 @@ void tests1ap_handle_e_rab_release_command( void tests1ap_handle_handover_request( test_ue_t *test_ue, ogs_s1ap_message_t *message); +void tests1ap_handle_handover_command( + test_ue_t *test_ue, ogs_s1ap_message_t *message); +void tests1ap_handle_handover_preparation_failure( + test_ue_t *test_ue, ogs_s1ap_message_t *message); +void tests1ap_handle_handover_cancel_ack( + test_ue_t *test_ue, ogs_s1ap_message_t *message); +void tests1ap_handle_mme_status_transfer( + test_ue_t *test_ue, ogs_s1ap_message_t *message); #ifdef __cplusplus } diff --git a/tests/common/s1ap-path.c b/tests/common/s1ap-path.c index 61670f9f4..25af15f69 100644 --- a/tests/common/s1ap-path.c +++ b/tests/common/s1ap-path.c @@ -73,6 +73,7 @@ void tests1ap_recv(test_ue_t *test_ue, ogs_pkbuf_t *pkbuf) tests1ap_handle_handover_request(test_ue, pdu); break; case S1AP_ProcedureCode_id_MMEStatusTransfer: + tests1ap_handle_mme_status_transfer(test_ue, pdu); break; case S1AP_ProcedureCode_id_Paging: break; @@ -98,8 +99,10 @@ void tests1ap_recv(test_ue_t *test_ue, ogs_pkbuf_t *pkbuf) case S1AP_ProcedureCode_id_PathSwitchRequest: break; case S1AP_ProcedureCode_id_HandoverPreparation: + tests1ap_handle_handover_command(test_ue, pdu); break; case S1AP_ProcedureCode_id_HandoverCancel: + tests1ap_handle_handover_cancel_ack(test_ue, pdu); break; case S1AP_ProcedureCode_id_Reset: break; diff --git a/tests/handover/5gc-n2-test.c b/tests/handover/5gc-n2-test.c index a0dcb36ae..9bf8882d1 100644 --- a/tests/handover/5gc-n2-test.c +++ b/tests/handover/5gc-n2-test.c @@ -19,7 +19,523 @@ #include "test-common.h" -static void test1_func(abts_case *tc, void *data) +static void failure_func(abts_case *tc, void *data) +{ + int rv; + ogs_socknode_t *ngap1, *ngap2; + ogs_socknode_t *gtpu1, *gtpu2; + ogs_pkbuf_t *gmmbuf; + ogs_pkbuf_t *gsmbuf; + ogs_pkbuf_t *nasbuf; + ogs_pkbuf_t *sendbuf; + ogs_pkbuf_t *recvbuf; + ogs_ngap_message_t message; + int i; + + uint8_t tmp[OGS_MAX_SDU_LEN]; + char *_gtp_payload = "34ff0024" + "0000000100000085 010002004500001c 0c0b000040015a7a 0a2d00010a2d0002" + "00000964cd7c291f"; + + ogs_nas_5gs_mobile_identity_suci_t mobile_identity_suci; + test_ue_t *test_ue = NULL; + test_sess_t *sess = NULL; + test_bearer_t *qos_flow = NULL; + + const char *_k_string = "70d49a71dd1a2b806a25abe0ef749f1e"; + uint8_t k[OGS_KEY_LEN]; + const char *_opc_string = "6f1bf53d624b3a43af6592854e2444c7"; + uint8_t opc[OGS_KEY_LEN]; + + mongoc_collection_t *collection = NULL; + bson_t *doc = NULL; + int64_t count = 0; + bson_error_t error; + const char *json = + "{" + "\"_id\" : { \"$oid\" : \"597223158b8861d7605378c6\" }, " + "\"imsi\" : \"901700000021309\"," + "\"ambr\" : { " + "\"uplink\" : { \"$numberLong\" : \"1024000\" }, " + "\"downlink\" : { \"$numberLong\" : \"1024000\" } " + "}," + "\"pdn\" : [" + "{" + "\"apn\" : \"internet\", " + "\"_id\" : { \"$oid\" : \"597223158b8861d7605378c6\" }, " + "\"ambr\" : {" + "\"uplink\" : { \"$numberLong\" : \"1024000\" }, " + "\"downlink\" : { \"$numberLong\" : \"1024000\" } " + "}," + "\"qos\" : { " + "\"qci\" : 9, " + "\"arp\" : { " + "\"priority_level\" : 8," + "\"pre_emption_vulnerability\" : 1, " + "\"pre_emption_capability\" : 1" + "} " + "}, " + "\"type\" : 2" + "}," + "{" + "\"apn\" : \"ims\", " + "\"_id\" : { \"$oid\" : \"597223158b8861d7605378c7\" }, " + "\"ambr\" : {" + "\"uplink\" : { \"$numberLong\" : \"1024000\" }, " + "\"downlink\" : { \"$numberLong\" : \"1024000\" } " + "}," + "\"qos\" : { " + "\"qci\" : 6, " + "\"arp\" : { " + "\"priority_level\" : 6," + "\"pre_emption_vulnerability\" : 1, " + "\"pre_emption_capability\" : 1" + "} " + "}, " + "\"type\" : 2," + "\"pcc_rule\" : [" + "{" + "\"_id\" : { \"$oid\" : \"599eb929c850caabcbfdcd2d\" }," + "\"qos\" : {" + "\"qci\" : 1," + "\"gbr\" : {" + "\"downlink\" : { \"$numberLong\" : \"64\" }," + "\"uplink\" : { \"$numberLong\" : \"44\" }" + "}," + "\"mbr\" : {" + "\"downlink\" : { \"$numberLong\" : \"64\" }," + "\"uplink\" : { \"$numberLong\" : \"44\" }" + "}," + "\"arp\" : {" + "\"priority_level\" : 3," + "\"pre_emption_vulnerability\" : 0," + "\"pre_emption_capability\" : 0 }" + "}," + "\"flow\" : [" + "{ \"direction\" : 2," + "\"description\" : \"permit out udp from 10.200.136.98/32 23454 to assigned 1-65535\"," + "\"_id\" : { \"$oid\" : \"599eb929c850caabcbfdcd31\" } }," + "{ \"direction\" : 1," + "\"description\" : \"permit out udp from 10.200.136.98/32 1-65535 to assigned 50020\"," + "\"_id\" : { \"$oid\" : \"599eb929c850caabcbfdcd30\" } }," + "{ \"direction\" : 2," + "\"description\" : \"permit out udp from 10.200.136.98/32 23455 to assigned 1-65535\"," + "\"_id\" : { \"$oid\" : \"599eb929c850caabcbfdcd2f\" } }," + "{ \"direction\" : 1," + "\"description\" : \"permit out udp from 10.200.136.98/32 1-65535 to assigned 50021\"," + "\"_id\" : { \"$oid\" : \"599eb929c850caabcbfdcd2e\" } }" + "]" + "}" + "]" + "}" + "]," + "\"security\" : { " + "\"k\" : \"70d49a71dd1a2b806a25abe0ef749f1e\", " + "\"opc\" : \"6f1bf53d624b3a43af6592854e2444c7\", " + "\"amf\" : \"8000\", " + "\"sqn\" : { \"$numberLong\" : \"25235952177090\" } " + "}, " + "\"subscribed_rau_tau_timer\" : 12," + "\"network_access_mode\" : 2, " + "\"subscriber_status\" : 0, " + "\"access_restriction_data\" : 32, " + "\"__v\" : 0 " + "}"; + + /* Setup Test UE & Session Context */ + memset(&mobile_identity_suci, 0, sizeof(mobile_identity_suci)); + + mobile_identity_suci.h.supi_format = OGS_NAS_5GS_SUPI_FORMAT_IMSI; + mobile_identity_suci.h.type = OGS_NAS_5GS_MOBILE_IDENTITY_SUCI; + mobile_identity_suci.routing_indicator1 = 0; + mobile_identity_suci.routing_indicator2 = 0xf; + mobile_identity_suci.routing_indicator3 = 0xf; + mobile_identity_suci.routing_indicator4 = 0xf; + mobile_identity_suci.protection_scheme_id = OGS_NAS_5GS_NULL_SCHEME; + mobile_identity_suci.home_network_pki_value = 0; + mobile_identity_suci.scheme_output[0] = 0; + mobile_identity_suci.scheme_output[1] = 0; + mobile_identity_suci.scheme_output[2] = 0x20; + mobile_identity_suci.scheme_output[3] = 0x31; + mobile_identity_suci.scheme_output[4] = 0x90; + + test_ue = test_ue_add_by_suci(&mobile_identity_suci, 13); + ogs_assert(test_ue); + + test_ue->nr_cgi.cell_id = 0x40001; + + test_ue->nas.registration.type = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; + test_ue->nas.registration.follow_on_request = 1; + test_ue->nas.registration.value = OGS_NAS_5GS_REGISTRATION_TYPE_INITIAL; + + OGS_HEX(_k_string, strlen(_k_string), test_ue->k); + OGS_HEX(_opc_string, strlen(_opc_string), test_ue->opc); + + /* Two gNB connects to AMF */ + ngap1 = testngap_client(AF_INET); + ABTS_PTR_NOTNULL(tc, ngap1); + + ngap2 = testngap_client(AF_INET); + ABTS_PTR_NOTNULL(tc, ngap2); + + /* Two gNB connects to UPF */ + gtpu1 = test_gtpu_server(1, AF_INET); + ABTS_PTR_NOTNULL(tc, gtpu1); + + gtpu2 = test_gtpu_server(2, AF_INET); + ABTS_PTR_NOTNULL(tc, gtpu2); + + /* NG-Setup Reqeust/Response for Source gNB */ + sendbuf = testngap_build_ng_setup_request(0x4000, 28); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* NG-Setup Reqeust/Response for Target gNB */ + sendbuf = testngap_build_ng_setup_request(0x4001, 28); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + recvbuf = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /********** Insert Subscriber in Database */ + collection = mongoc_client_get_collection( + ogs_mongoc()->client, ogs_mongoc()->name, "subscribers"); + ABTS_PTR_NOTNULL(tc, collection); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue->imsi)); + ABTS_PTR_NOTNULL(tc, doc); + + count = mongoc_collection_count ( + collection, MONGOC_QUERY_NONE, doc, 0, 0, NULL, &error); + if (count) { + ABTS_TRUE(tc, mongoc_collection_remove(collection, + MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) + } + bson_destroy(doc); + + doc = bson_new_from_json((const uint8_t *)json, -1, &error);; + ABTS_PTR_NOTNULL(tc, doc); + ABTS_TRUE(tc, mongoc_collection_insert(collection, + MONGOC_INSERT_NONE, doc, NULL, &error)); + bson_destroy(doc); + + doc = BCON_NEW("imsi", BCON_UTF8(test_ue->imsi)); + ABTS_PTR_NOTNULL(tc, doc); + do { + count = mongoc_collection_count ( + collection, MONGOC_QUERY_NONE, doc, 0, 0, NULL, &error); + } while (count == 0); + bson_destroy(doc); + + /* Send Registration request */ + test_ue->registration_request_param.guti = 1; + gmmbuf = testgmm_build_registration_request(test_ue, NULL); + ABTS_PTR_NOTNULL(tc, gmmbuf); + + test_ue->registration_request_param.gmm_capability = 1; + test_ue->registration_request_param.requested_nssai = 1; + test_ue->registration_request_param.last_visited_registered_tai = 1; + test_ue->registration_request_param.ue_usage_setting = 1; + nasbuf = testgmm_build_registration_request(test_ue, NULL); + ABTS_PTR_NOTNULL(tc, nasbuf); + + sendbuf = testngap_build_initial_ue_message(test_ue, gmmbuf, false, true); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Identity request */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send Identity response */ + gmmbuf = testgmm_build_identity_response(test_ue); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Authentication request */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send Authentication response */ + gmmbuf = testgmm_build_authentication_response(test_ue); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Security mode command */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send Security mode complete */ + gmmbuf = testgmm_build_security_mode_complete(test_ue, nasbuf); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive InitialContextSetupRequest + + * Registration accept */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_InitialContextSetup, + test_ue->ngap_procedure_code); + + /* Send UERadioCapabilityInfoIndication */ + sendbuf = testngap_build_ue_radio_capability_info_indication(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send InitialContextSetupResponse */ + sendbuf = testngap_build_initial_context_setup_response(test_ue, false); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send Registration complete */ + gmmbuf = testgmm_build_registration_complete(test_ue); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Configuration update command */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send PDU session establishment request */ + sess = test_sess_add_by_dnn_and_psi(test_ue, "internet", 5); + ogs_assert(sess); + + sess->ul_nas_transport_param.request_type = + OGS_NAS_5GS_REQUEST_TYPE_INITIAL; + sess->ul_nas_transport_param.dnn = 1; + sess->ul_nas_transport_param.s_nssai = 1; + + gsmbuf = testgsm_build_pdu_session_establishment_request(sess); + ABTS_PTR_NOTNULL(tc, gsmbuf); + gmmbuf = testgmm_build_ul_nas_transport(sess, + OGS_NAS_PAYLOAD_CONTAINER_N1_SM_INFORMATION, gsmbuf); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive PDUSessionResourceSetupRequest + + * DL NAS transport + + * PDU session establishment accept */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_PDUSessionResourceSetup, + test_ue->ngap_procedure_code); + + /* Send GTP-U ICMP Packet */ + qos_flow = test_qos_flow_find_by_qfi(sess, 1); + ogs_assert(qos_flow); + rv = test_gtpu_send_ping(gtpu1, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send PDUSessionResourceSetupResponse */ + sendbuf = testngap_sess_build_pdu_session_resource_setup_response(sess); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send GTP-U ICMP Packet */ + rv = test_gtpu_send_ping(gtpu1, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send PDU session establishment request */ + sess = test_sess_add_by_dnn_and_psi(test_ue, "ims", 6); + ogs_assert(sess); + + sess->ul_nas_transport_param.request_type = + OGS_NAS_5GS_REQUEST_TYPE_INITIAL; + sess->ul_nas_transport_param.dnn = 1; + sess->ul_nas_transport_param.s_nssai = 1; + + gsmbuf = testgsm_build_pdu_session_establishment_request(sess); + ABTS_PTR_NOTNULL(tc, gsmbuf); + gmmbuf = testgmm_build_ul_nas_transport(sess, + OGS_NAS_PAYLOAD_CONTAINER_N1_SM_INFORMATION, gsmbuf); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive PDU session establishment accept */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send PDUSessionResourceSetupResponse */ + sendbuf = testngap_sess_build_pdu_session_resource_setup_response(sess); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive PDU session modification command */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send PDU session resource modify response */ + qos_flow = test_qos_flow_find_by_qfi(sess, 2); + ogs_assert(qos_flow); + + sendbuf = testngap_build_pdu_session_resource_modify_response(qos_flow); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send PDU session resource modify complete */ + sess->ul_nas_transport_param.request_type = + OGS_NAS_5GS_REQUEST_TYPE_MODIFICATION_REQUEST; + sess->ul_nas_transport_param.dnn = 0; + sess->ul_nas_transport_param.s_nssai = 0; + + gsmbuf = testgsm_build_pdu_session_modification_complete(sess); + ABTS_PTR_NOTNULL(tc, gsmbuf); + gmmbuf = testgmm_build_ul_nas_transport(sess, + OGS_NAS_PAYLOAD_CONTAINER_N1_SM_INFORMATION, gsmbuf); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Waiting for creating dedicated QoS flow in PFCP protocol */ + ogs_msleep(100); + + /* Send GTP-U ICMP Packet */ + rv = test_gtpu_send_ping(gtpu1, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send HandoverRequired */ + sendbuf = testngap_build_handover_required( + test_ue, NGAP_HandoverType_intra5gs, + 0x4001, 28, + NGAP_Cause_PR_radioNetwork, + NGAP_CauseRadioNetwork_handover_desirable_for_radio_reason, + true); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive HandoverRequest */ + recvbuf = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send HandoverFailure */ + sendbuf = testngap_build_handover_failure(test_ue, + NGAP_Cause_PR_radioNetwork, NGAP_CauseRadioNetwork_unspecified); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive UEContextReleaseCommand */ + recvbuf = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send UEContextReleaseComplete */ + sendbuf = testngap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive HandoverPreparationFailure */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send UEContextReleaseRequest */ + sendbuf = testngap_build_ue_context_release_request(test_ue, + NGAP_Cause_PR_radioNetwork, NGAP_CauseRadioNetwork_user_inactivity, + false); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive UEContextReleaseCommand */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_UEContextRelease, + test_ue->ngap_procedure_code); + + /* Send UEContextReleaseComplete */ + sendbuf = testngap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + ogs_msleep(300); + + /********** Remove Subscriber in Database */ + doc = BCON_NEW("imsi", BCON_UTF8(test_ue->imsi)); + ABTS_PTR_NOTNULL(tc, doc); + ABTS_TRUE(tc, mongoc_collection_remove(collection, + MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) + bson_destroy(doc); + + mongoc_collection_destroy(collection); + + /* Two gNB disonncect from UPF */ + testgnb_gtpu_close(gtpu1); + testgnb_gtpu_close(gtpu2); + + /* Two gNB disonncect from AMF */ + testgnb_ngap_close(ngap1); + testgnb_ngap_close(ngap2); + + /* Clear Test UE Context */ + test_ue_remove(test_ue); +} + +static void direct_complete_func(abts_case *tc, void *data) { int rv; ogs_socknode_t *ngap1, *ngap2; @@ -494,7 +1010,7 @@ static void test1_func(abts_case *tc, void *data) ABTS_PTR_NOTNULL(tc, recvbuf); testngap_recv(test_ue, recvbuf); - /* Send HandoverRequestAcknoledge */ + /* Send HandoverRequestAcknowledge */ ogs_list_for_each(&test_ue->sess_list, sess) sess->gnb_n3_addr = test_self()->gnb2_addr; @@ -509,17 +1025,11 @@ static void test1_func(abts_case *tc, void *data) testngap_recv(test_ue, recvbuf); /* Send UplinkRANStatusTransfer */ - amf_ue_ngap_id = test_ue->amf_ue_ngap_id--; - ran_ue_ngap_id = test_ue->ran_ue_ngap_id--; - sendbuf = testngap_build_uplink_ran_status_transfer(test_ue); ABTS_PTR_NOTNULL(tc, sendbuf); rv = testgnb_ngap_send(ngap1, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - test_ue->amf_ue_ngap_id = amf_ue_ngap_id; - test_ue->ran_ue_ngap_id = ran_ue_ngap_id; - /* Receive DownlinkRANStatusTransfer */ recvbuf = testgnb_ngap_read(ngap2); ABTS_PTR_NOTNULL(tc, recvbuf); @@ -617,7 +1127,7 @@ static void test1_func(abts_case *tc, void *data) ABTS_PTR_NOTNULL(tc, recvbuf); testngap_recv(test_ue, recvbuf); - /* Send HandoverRequestAcknoledge */ + /* Send HandoverRequestAcknowledge */ ogs_list_for_each(&test_ue->sess_list, sess) sess->gnb_n3_addr = test_self()->gnb1_addr; @@ -632,17 +1142,11 @@ static void test1_func(abts_case *tc, void *data) testngap_recv(test_ue, recvbuf); /* Send UplinkRANStatusTransfer */ - amf_ue_ngap_id = test_ue->amf_ue_ngap_id--; - ran_ue_ngap_id = test_ue->ran_ue_ngap_id--; - sendbuf = testngap_build_uplink_ran_status_transfer(test_ue); ABTS_PTR_NOTNULL(tc, sendbuf); rv = testgnb_ngap_send(ngap2, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - test_ue->amf_ue_ngap_id = amf_ue_ngap_id; - test_ue->ran_ue_ngap_id = ran_ue_ngap_id; - /* Receive DownlinkRANStatusTransfer */ recvbuf = testgnb_ngap_read(ngap1); ABTS_PTR_NOTNULL(tc, recvbuf); @@ -769,7 +1273,7 @@ static void test1_func(abts_case *tc, void *data) test_ue_remove(test_ue); } -static void test2_func(abts_case *tc, void *data) +static void direct_cancel_func(abts_case *tc, void *data) { int rv; ogs_socknode_t *ngap1, *ngap2; @@ -792,9 +1296,6 @@ static void test2_func(abts_case *tc, void *data) test_sess_t *sess = NULL; test_bearer_t *qos_flow = NULL; - uint32_t ran_ue_ngap_id; - uint64_t amf_ue_ngap_id; - const char *_k_string = "70d49a71dd1a2b806a25abe0ef749f1e"; uint8_t k[OGS_KEY_LEN]; const char *_opc_string = "6f1bf53d624b3a43af6592854e2444c7"; @@ -1220,21 +1721,27 @@ static void test2_func(abts_case *tc, void *data) ABTS_PTR_NOTNULL(tc, recvbuf); testngap_recv(test_ue, recvbuf); - /* Send HandoverFailure */ - sendbuf = testngap_build_handover_failure(test_ue, - NGAP_Cause_PR_radioNetwork, NGAP_CauseRadioNetwork_unspecified); + /* Send HandoverRequestAcknowledge */ + ogs_list_for_each(&test_ue->sess_list, sess) + sess->gnb_n3_addr = test_self()->gnb2_addr; + + sendbuf = testngap_build_handover_request_ack(test_ue); ABTS_PTR_NOTNULL(tc, sendbuf); rv = testgnb_ngap_send(ngap2, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - /* Receive HandoverPreparationFailure */ - amf_ue_ngap_id = test_ue->amf_ue_ngap_id--; - + /* Receive HandoverCommand */ recvbuf = testgnb_ngap_read(ngap1); ABTS_PTR_NOTNULL(tc, recvbuf); testngap_recv(test_ue, recvbuf); - test_ue->amf_ue_ngap_id = amf_ue_ngap_id; + /* Send HandoverCancel */ + sendbuf = testngap_build_handover_cancel(test_ue, + NGAP_Cause_PR_radioNetwork, + NGAP_CauseRadioNetwork_txnrelocoverall_expiry); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); /* Receive UEContextReleaseCommand */ recvbuf = testgnb_ngap_read(ngap2); @@ -1247,10 +1754,12 @@ static void test2_func(abts_case *tc, void *data) rv = testgnb_ngap_send(ngap2, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - /* Send UEContextReleaseRequest */ - test_ue->amf_ue_ngap_id--; - test_ue->ran_ue_ngap_id--; + /* Recv HandoverCancelAcknowledge */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + /* Send UEContextReleaseRequest */ sendbuf = testngap_build_ue_context_release_request(test_ue, NGAP_Cause_PR_radioNetwork, NGAP_CauseRadioNetwork_user_inactivity, false); @@ -1295,7 +1804,7 @@ static void test2_func(abts_case *tc, void *data) test_ue_remove(test_ue); } -static void test3_func(abts_case *tc, void *data) +static void indirect_complete_func(abts_case *tc, void *data) { int rv; ogs_socknode_t *ngap1, *ngap2; @@ -1305,6 +1814,7 @@ static void test3_func(abts_case *tc, void *data) ogs_pkbuf_t *nasbuf; ogs_pkbuf_t *sendbuf; ogs_pkbuf_t *recvbuf; + ogs_pkbuf_t *pkbuf; ogs_ngap_message_t message; int i; @@ -1736,7 +2246,7 @@ static void test3_func(abts_case *tc, void *data) 0x4001, 28, NGAP_Cause_PR_radioNetwork, NGAP_CauseRadioNetwork_handover_desirable_for_radio_reason, - true); + false); ABTS_PTR_NOTNULL(tc, sendbuf); rv = testgnb_ngap_send(ngap1, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); @@ -1746,7 +2256,7 @@ static void test3_func(abts_case *tc, void *data) ABTS_PTR_NOTNULL(tc, recvbuf); testngap_recv(test_ue, recvbuf); - /* Send HandoverRequestAcknoledge */ + /* Send HandoverRequestAcknowledge */ ogs_list_for_each(&test_ue->sess_list, sess) sess->gnb_n3_addr = test_self()->gnb2_addr; @@ -1760,26 +2270,216 @@ static void test3_func(abts_case *tc, void *data) ABTS_PTR_NOTNULL(tc, recvbuf); testngap_recv(test_ue, recvbuf); - /* Send HandoverCancel */ - amf_ue_ngap_id = test_ue->amf_ue_ngap_id--; - ran_ue_ngap_id = test_ue->ran_ue_ngap_id--; + /* Send GTP-U ICMP Packet */ + sess = test_sess_find_by_psi(test_ue, 5); + ogs_assert(sess); + qos_flow = test_qos_flow_find_by_qfi(sess, 1); + ogs_assert(qos_flow); + rv = test_gtpu_send_ping(gtpu1, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); - sendbuf = testngap_build_handover_cancel(test_ue, - NGAP_Cause_PR_radioNetwork, - NGAP_CauseRadioNetwork_txnrelocoverall_expiry); + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + + /* Copy ICMP Packet */ + pkbuf = ogs_pkbuf_copy(recvbuf); + ABTS_PTR_NOTNULL(tc, pkbuf); + ogs_pkbuf_pull(pkbuf, OGS_GTPV1U_5GC_HEADER_LEN); + + ogs_pkbuf_free(recvbuf); + + /* Send GTP-U Packet with Indirect Data Forwarding */ + rv = test_gtpu_send_indirect_data_forwarding(gtpu1, qos_flow, pkbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu2); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send UplinkRANStatusTransfer */ + sendbuf = testngap_build_uplink_ran_status_transfer(test_ue); ABTS_PTR_NOTNULL(tc, sendbuf); rv = testgnb_ngap_send(ngap1, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - /* Recv HandoverCancelAcknowledge */ + /* Receive DownlinkRANStatusTransfer */ + recvbuf = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send HandoverNotify */ + test_ue->nr_cgi.cell_id = 0x40011; + sendbuf = testngap_build_handover_notify(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive End Mark */ + recvbuf = test_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Receive End Mark */ + recvbuf = test_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Receive End Mark */ + recvbuf = test_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Receive UEContextReleaseCommand */ + amf_ue_ngap_id = test_ue->amf_ue_ngap_id; + ran_ue_ngap_id = test_ue->ran_ue_ngap_id; + recvbuf = testgnb_ngap_read(ngap1); ABTS_PTR_NOTNULL(tc, recvbuf); testngap_recv(test_ue, recvbuf); - /* Receive UEContextReleaseCommand */ + /* Send UEContextReleaseComplete */ + sendbuf = testngap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + test_ue->amf_ue_ngap_id = amf_ue_ngap_id; test_ue->ran_ue_ngap_id = ran_ue_ngap_id; + /* Send GTP-U ICMP Packet */ + sess = test_sess_find_by_psi(test_ue, 5); + ogs_assert(sess); + qos_flow = test_qos_flow_find_by_qfi(sess, 1); + ogs_assert(qos_flow); + rv = test_gtpu_send_ping(gtpu2, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu2); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send GTP-U ICMP Packet */ + sess = test_sess_find_by_psi(test_ue, 6); + ogs_assert(sess); + qos_flow = test_qos_flow_find_by_qfi(sess, 1); + ogs_assert(qos_flow); + rv = test_gtpu_send_ping(gtpu2, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu2); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send GTP-U ICMP Packet */ + qos_flow = test_qos_flow_find_by_qfi(sess, 2); + ogs_assert(qos_flow); + rv = test_gtpu_send_ping(gtpu2, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu2); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send HandoverRequired */ + sendbuf = testngap_build_handover_required( + test_ue, NGAP_HandoverType_intra5gs, + 0x4000, 28, + NGAP_Cause_PR_radioNetwork, + NGAP_CauseRadioNetwork_handover_desirable_for_radio_reason, + false); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive HandoverRequest */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send HandoverRequestAcknowledge */ + ogs_list_for_each(&test_ue->sess_list, sess) + sess->gnb_n3_addr = test_self()->gnb1_addr; + + sendbuf = testngap_build_handover_request_ack(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive HandoverCommand */ + recvbuf = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send GTP-U ICMP Packet */ + sess = test_sess_find_by_psi(test_ue, 6); + ogs_assert(sess); + qos_flow = test_qos_flow_find_by_qfi(sess, 1); + ogs_assert(qos_flow); + rv = test_gtpu_send_ping(gtpu2, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu2); + ABTS_PTR_NOTNULL(tc, recvbuf); + + /* Copy ICMP Packet */ + pkbuf = ogs_pkbuf_copy(recvbuf); + ABTS_PTR_NOTNULL(tc, pkbuf); + ogs_pkbuf_pull(pkbuf, OGS_GTPV1U_5GC_HEADER_LEN); + + ogs_pkbuf_free(recvbuf); + + /* Send GTP-U Packet with Indirect Data Forwarding */ + rv = test_gtpu_send_indirect_data_forwarding(gtpu2, qos_flow, pkbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send UplinkRANStatusTransfer */ + sendbuf = testngap_build_uplink_ran_status_transfer(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive DownlinkRANStatusTransfer */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send HandoverNotify */ + test_ue->nr_cgi.cell_id = 0x40001; + sendbuf = testngap_build_handover_notify(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive End Mark */ + recvbuf = test_gtpu_read(gtpu2); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Receive End Mark */ + recvbuf = test_gtpu_read(gtpu2); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Receive End Mark */ + recvbuf = test_gtpu_read(gtpu2); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Receive UEContextReleaseCommand */ + amf_ue_ngap_id = test_ue->amf_ue_ngap_id; + ran_ue_ngap_id = test_ue->ran_ue_ngap_id; + recvbuf = testgnb_ngap_read(ngap2); ABTS_PTR_NOTNULL(tc, recvbuf); testngap_recv(test_ue, recvbuf); @@ -1790,10 +2490,47 @@ static void test3_func(abts_case *tc, void *data) rv = testgnb_ngap_send(ngap2, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - /* Send UEContextReleaseRequest */ - test_ue->amf_ue_ngap_id--; - test_ue->ran_ue_ngap_id--; + test_ue->amf_ue_ngap_id = amf_ue_ngap_id; + test_ue->ran_ue_ngap_id = ran_ue_ngap_id; + /* Send GTP-U ICMP Packet */ + sess = test_sess_find_by_psi(test_ue, 5); + ogs_assert(sess); + qos_flow = test_qos_flow_find_by_qfi(sess, 1); + ogs_assert(qos_flow); + rv = test_gtpu_send_ping(gtpu1, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send GTP-U ICMP Packet */ + sess = test_sess_find_by_psi(test_ue, 6); + ogs_assert(sess); + qos_flow = test_qos_flow_find_by_qfi(sess, 1); + ogs_assert(qos_flow); + rv = test_gtpu_send_ping(gtpu1, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send GTP-U ICMP Packet */ + qos_flow = test_qos_flow_find_by_qfi(sess, 2); + ogs_assert(qos_flow); + rv = test_gtpu_send_ping(gtpu1, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send UEContextReleaseRequest */ sendbuf = testngap_build_ue_context_release_request(test_ue, NGAP_Cause_PR_radioNetwork, NGAP_CauseRadioNetwork_user_inactivity, false); @@ -1838,13 +2575,723 @@ static void test3_func(abts_case *tc, void *data) test_ue_remove(test_ue); } +static void indirect_cancel_func(abts_case *tc, void *data) +{ + int rv; + ogs_socknode_t *ngap1, *ngap2; + ogs_socknode_t *gtpu1, *gtpu2; + ogs_pkbuf_t *gmmbuf; + ogs_pkbuf_t *gsmbuf; + ogs_pkbuf_t *nasbuf; + ogs_pkbuf_t *sendbuf; + ogs_pkbuf_t *recvbuf; + ogs_pkbuf_t *pkbuf; + ogs_ngap_message_t message; + int i; + + uint8_t tmp[OGS_MAX_SDU_LEN]; + char *_gtp_payload = "34ff0024" + "0000000100000085 010002004500001c 0c0b000040015a7a 0a2d00010a2d0002" + "00000964cd7c291f"; + + ogs_nas_5gs_mobile_identity_suci_t mobile_identity_suci; + test_ue_t *test_ue = NULL; + test_sess_t *sess = NULL; + test_bearer_t *qos_flow = NULL; + + uint32_t ran_ue_ngap_id; + uint64_t amf_ue_ngap_id; + + const char *_k_string = "70d49a71dd1a2b806a25abe0ef749f1e"; + uint8_t k[OGS_KEY_LEN]; + const char *_opc_string = "6f1bf53d624b3a43af6592854e2444c7"; + uint8_t opc[OGS_KEY_LEN]; + + mongoc_collection_t *collection = NULL; + bson_t *doc = NULL; + int64_t count = 0; + bson_error_t error; + const char *json = + "{" + "\"_id\" : { \"$oid\" : \"597223158b8861d7605378c6\" }, " + "\"imsi\" : \"901700000021309\"," + "\"ambr\" : { " + "\"uplink\" : { \"$numberLong\" : \"1024000\" }, " + "\"downlink\" : { \"$numberLong\" : \"1024000\" } " + "}," + "\"pdn\" : [" + "{" + "\"apn\" : \"internet\", " + "\"_id\" : { \"$oid\" : \"597223158b8861d7605378c6\" }, " + "\"ambr\" : {" + "\"uplink\" : { \"$numberLong\" : \"1024000\" }, " + "\"downlink\" : { \"$numberLong\" : \"1024000\" } " + "}," + "\"qos\" : { " + "\"qci\" : 9, " + "\"arp\" : { " + "\"priority_level\" : 8," + "\"pre_emption_vulnerability\" : 1, " + "\"pre_emption_capability\" : 1" + "} " + "}, " + "\"type\" : 2" + "}," + "{" + "\"apn\" : \"ims\", " + "\"_id\" : { \"$oid\" : \"597223158b8861d7605378c7\" }, " + "\"ambr\" : {" + "\"uplink\" : { \"$numberLong\" : \"1024000\" }, " + "\"downlink\" : { \"$numberLong\" : \"1024000\" } " + "}," + "\"qos\" : { " + "\"qci\" : 6, " + "\"arp\" : { " + "\"priority_level\" : 6," + "\"pre_emption_vulnerability\" : 1, " + "\"pre_emption_capability\" : 1" + "} " + "}, " + "\"type\" : 2," + "\"pcc_rule\" : [" + "{" + "\"_id\" : { \"$oid\" : \"599eb929c850caabcbfdcd2d\" }," + "\"qos\" : {" + "\"qci\" : 1," + "\"gbr\" : {" + "\"downlink\" : { \"$numberLong\" : \"64\" }," + "\"uplink\" : { \"$numberLong\" : \"44\" }" + "}," + "\"mbr\" : {" + "\"downlink\" : { \"$numberLong\" : \"64\" }," + "\"uplink\" : { \"$numberLong\" : \"44\" }" + "}," + "\"arp\" : {" + "\"priority_level\" : 3," + "\"pre_emption_vulnerability\" : 0," + "\"pre_emption_capability\" : 0 }" + "}," + "\"flow\" : [" + "{ \"direction\" : 2," + "\"description\" : \"permit out udp from 10.200.136.98/32 23454 to assigned 1-65535\"," + "\"_id\" : { \"$oid\" : \"599eb929c850caabcbfdcd31\" } }," + "{ \"direction\" : 1," + "\"description\" : \"permit out udp from 10.200.136.98/32 1-65535 to assigned 50020\"," + "\"_id\" : { \"$oid\" : \"599eb929c850caabcbfdcd30\" } }," + "{ \"direction\" : 2," + "\"description\" : \"permit out udp from 10.200.136.98/32 23455 to assigned 1-65535\"," + "\"_id\" : { \"$oid\" : \"599eb929c850caabcbfdcd2f\" } }," + "{ \"direction\" : 1," + "\"description\" : \"permit out udp from 10.200.136.98/32 1-65535 to assigned 50021\"," + "\"_id\" : { \"$oid\" : \"599eb929c850caabcbfdcd2e\" } }" + "]" + "}" + "]" + "}" + "]," + "\"security\" : { " + "\"k\" : \"70d49a71dd1a2b806a25abe0ef749f1e\", " + "\"opc\" : \"6f1bf53d624b3a43af6592854e2444c7\", " + "\"amf\" : \"8000\", " + "\"sqn\" : { \"$numberLong\" : \"25235952177090\" } " + "}, " + "\"subscribed_rau_tau_timer\" : 12," + "\"network_access_mode\" : 2, " + "\"subscriber_status\" : 0, " + "\"access_restriction_data\" : 32, " + "\"__v\" : 0 " + "}"; + + /* Setup Test UE & Session Context */ + memset(&mobile_identity_suci, 0, sizeof(mobile_identity_suci)); + + mobile_identity_suci.h.supi_format = OGS_NAS_5GS_SUPI_FORMAT_IMSI; + mobile_identity_suci.h.type = OGS_NAS_5GS_MOBILE_IDENTITY_SUCI; + mobile_identity_suci.routing_indicator1 = 0; + mobile_identity_suci.routing_indicator2 = 0xf; + mobile_identity_suci.routing_indicator3 = 0xf; + mobile_identity_suci.routing_indicator4 = 0xf; + mobile_identity_suci.protection_scheme_id = OGS_NAS_5GS_NULL_SCHEME; + mobile_identity_suci.home_network_pki_value = 0; + mobile_identity_suci.scheme_output[0] = 0; + mobile_identity_suci.scheme_output[1] = 0; + mobile_identity_suci.scheme_output[2] = 0x20; + mobile_identity_suci.scheme_output[3] = 0x31; + mobile_identity_suci.scheme_output[4] = 0x90; + + test_ue = test_ue_add_by_suci(&mobile_identity_suci, 13); + ogs_assert(test_ue); + + test_ue->nr_cgi.cell_id = 0x40001; + + test_ue->nas.registration.type = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; + test_ue->nas.registration.follow_on_request = 1; + test_ue->nas.registration.value = OGS_NAS_5GS_REGISTRATION_TYPE_INITIAL; + + OGS_HEX(_k_string, strlen(_k_string), test_ue->k); + OGS_HEX(_opc_string, strlen(_opc_string), test_ue->opc); + + /* Two gNB connects to AMF */ + ngap1 = testngap_client(AF_INET); + ABTS_PTR_NOTNULL(tc, ngap1); + + ngap2 = testngap_client(AF_INET); + ABTS_PTR_NOTNULL(tc, ngap2); + + /* Two gNB connects to UPF */ + gtpu1 = test_gtpu_server(1, AF_INET); + ABTS_PTR_NOTNULL(tc, gtpu1); + + gtpu2 = test_gtpu_server(2, AF_INET); + ABTS_PTR_NOTNULL(tc, gtpu2); + + /* NG-Setup Reqeust/Response for Source gNB */ + sendbuf = testngap_build_ng_setup_request(0x4000, 28); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* NG-Setup Reqeust/Response for Target gNB */ + sendbuf = testngap_build_ng_setup_request(0x4001, 28); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + recvbuf = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /********** Insert Subscriber in Database */ + collection = mongoc_client_get_collection( + ogs_mongoc()->client, ogs_mongoc()->name, "subscribers"); + ABTS_PTR_NOTNULL(tc, collection); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue->imsi)); + ABTS_PTR_NOTNULL(tc, doc); + + count = mongoc_collection_count ( + collection, MONGOC_QUERY_NONE, doc, 0, 0, NULL, &error); + if (count) { + ABTS_TRUE(tc, mongoc_collection_remove(collection, + MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) + } + bson_destroy(doc); + + doc = bson_new_from_json((const uint8_t *)json, -1, &error);; + ABTS_PTR_NOTNULL(tc, doc); + ABTS_TRUE(tc, mongoc_collection_insert(collection, + MONGOC_INSERT_NONE, doc, NULL, &error)); + bson_destroy(doc); + + doc = BCON_NEW("imsi", BCON_UTF8(test_ue->imsi)); + ABTS_PTR_NOTNULL(tc, doc); + do { + count = mongoc_collection_count ( + collection, MONGOC_QUERY_NONE, doc, 0, 0, NULL, &error); + } while (count == 0); + bson_destroy(doc); + + /* Send Registration request */ + test_ue->registration_request_param.guti = 1; + gmmbuf = testgmm_build_registration_request(test_ue, NULL); + ABTS_PTR_NOTNULL(tc, gmmbuf); + + test_ue->registration_request_param.gmm_capability = 1; + test_ue->registration_request_param.requested_nssai = 1; + test_ue->registration_request_param.last_visited_registered_tai = 1; + test_ue->registration_request_param.ue_usage_setting = 1; + nasbuf = testgmm_build_registration_request(test_ue, NULL); + ABTS_PTR_NOTNULL(tc, nasbuf); + + sendbuf = testngap_build_initial_ue_message(test_ue, gmmbuf, false, true); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Identity request */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send Identity response */ + gmmbuf = testgmm_build_identity_response(test_ue); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Authentication request */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send Authentication response */ + gmmbuf = testgmm_build_authentication_response(test_ue); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Security mode command */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send Security mode complete */ + gmmbuf = testgmm_build_security_mode_complete(test_ue, nasbuf); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive InitialContextSetupRequest + + * Registration accept */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_InitialContextSetup, + test_ue->ngap_procedure_code); + + /* Send UERadioCapabilityInfoIndication */ + sendbuf = testngap_build_ue_radio_capability_info_indication(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send InitialContextSetupResponse */ + sendbuf = testngap_build_initial_context_setup_response(test_ue, false); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send Registration complete */ + gmmbuf = testgmm_build_registration_complete(test_ue); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Configuration update command */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send PDU session establishment request */ + sess = test_sess_add_by_dnn_and_psi(test_ue, "internet", 5); + ogs_assert(sess); + + sess->ul_nas_transport_param.request_type = + OGS_NAS_5GS_REQUEST_TYPE_INITIAL; + sess->ul_nas_transport_param.dnn = 1; + sess->ul_nas_transport_param.s_nssai = 1; + + gsmbuf = testgsm_build_pdu_session_establishment_request(sess); + ABTS_PTR_NOTNULL(tc, gsmbuf); + gmmbuf = testgmm_build_ul_nas_transport(sess, + OGS_NAS_PAYLOAD_CONTAINER_N1_SM_INFORMATION, gsmbuf); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive PDUSessionResourceSetupRequest + + * DL NAS transport + + * PDU session establishment accept */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_PDUSessionResourceSetup, + test_ue->ngap_procedure_code); + + /* Send GTP-U ICMP Packet */ + qos_flow = test_qos_flow_find_by_qfi(sess, 1); + ogs_assert(qos_flow); + rv = test_gtpu_send_ping(gtpu1, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send PDUSessionResourceSetupResponse */ + sendbuf = testngap_sess_build_pdu_session_resource_setup_response(sess); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send GTP-U ICMP Packet */ + rv = test_gtpu_send_ping(gtpu1, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send PDU session establishment request */ + sess = test_sess_add_by_dnn_and_psi(test_ue, "ims", 6); + ogs_assert(sess); + + sess->ul_nas_transport_param.request_type = + OGS_NAS_5GS_REQUEST_TYPE_INITIAL; + sess->ul_nas_transport_param.dnn = 1; + sess->ul_nas_transport_param.s_nssai = 1; + + gsmbuf = testgsm_build_pdu_session_establishment_request(sess); + ABTS_PTR_NOTNULL(tc, gsmbuf); + gmmbuf = testgmm_build_ul_nas_transport(sess, + OGS_NAS_PAYLOAD_CONTAINER_N1_SM_INFORMATION, gsmbuf); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive PDU session establishment accept */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send PDUSessionResourceSetupResponse */ + sendbuf = testngap_sess_build_pdu_session_resource_setup_response(sess); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive PDU session modification command */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send PDU session resource modify response */ + qos_flow = test_qos_flow_find_by_qfi(sess, 2); + ogs_assert(qos_flow); + + sendbuf = testngap_build_pdu_session_resource_modify_response(qos_flow); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send PDU session resource modify complete */ + sess->ul_nas_transport_param.request_type = + OGS_NAS_5GS_REQUEST_TYPE_MODIFICATION_REQUEST; + sess->ul_nas_transport_param.dnn = 0; + sess->ul_nas_transport_param.s_nssai = 0; + + gsmbuf = testgsm_build_pdu_session_modification_complete(sess); + ABTS_PTR_NOTNULL(tc, gsmbuf); + gmmbuf = testgmm_build_ul_nas_transport(sess, + OGS_NAS_PAYLOAD_CONTAINER_N1_SM_INFORMATION, gsmbuf); + ABTS_PTR_NOTNULL(tc, gmmbuf); + sendbuf = testngap_build_uplink_nas_transport(test_ue, gmmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Waiting for creating dedicated QoS flow in PFCP protocol */ + ogs_msleep(100); + + /* Send GTP-U ICMP Packet */ + rv = test_gtpu_send_ping(gtpu1, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send HandoverRequired */ + sendbuf = testngap_build_handover_required( + test_ue, NGAP_HandoverType_intra5gs, + 0x4001, 28, + NGAP_Cause_PR_radioNetwork, + NGAP_CauseRadioNetwork_handover_desirable_for_radio_reason, + false); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive HandoverRequest */ + recvbuf = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send HandoverRequestAcknowledge */ + ogs_list_for_each(&test_ue->sess_list, sess) + sess->gnb_n3_addr = test_self()->gnb2_addr; + + sendbuf = testngap_build_handover_request_ack(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive HandoverCommand */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send GTP-U ICMP Packet */ + sess = test_sess_find_by_psi(test_ue, 6); + ogs_assert(sess); + qos_flow = test_qos_flow_find_by_qfi(sess, 2); + ogs_assert(qos_flow); + rv = test_gtpu_send_ping(gtpu1, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + + /* Copy ICMP Packet */ + pkbuf = ogs_pkbuf_copy(recvbuf); + ABTS_PTR_NOTNULL(tc, pkbuf); + ogs_pkbuf_pull(pkbuf, OGS_GTPV1U_5GC_HEADER_LEN); + + ogs_pkbuf_free(recvbuf); + + /* Send GTP-U Packet with Indirect Data Forwarding */ + rv = test_gtpu_send_indirect_data_forwarding(gtpu1, qos_flow, pkbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu2); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send UplinkRANStatusTransfer */ + sendbuf = testngap_build_uplink_ran_status_transfer(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive DownlinkRANStatusTransfer */ + recvbuf = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send HandoverNotify */ + test_ue->nr_cgi.cell_id = 0x40011; + sendbuf = testngap_build_handover_notify(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive End Mark */ + recvbuf = test_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Receive End Mark */ + recvbuf = test_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Receive End Mark */ + recvbuf = test_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Receive UEContextReleaseCommand */ + amf_ue_ngap_id = test_ue->amf_ue_ngap_id; + ran_ue_ngap_id = test_ue->ran_ue_ngap_id; + + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send UEContextReleaseComplete */ + sendbuf = testngap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + test_ue->amf_ue_ngap_id = amf_ue_ngap_id; + test_ue->ran_ue_ngap_id = ran_ue_ngap_id; + + /* Send GTP-U ICMP Packet */ + sess = test_sess_find_by_psi(test_ue, 5); + ogs_assert(sess); + qos_flow = test_qos_flow_find_by_qfi(sess, 1); + ogs_assert(qos_flow); + rv = test_gtpu_send_ping(gtpu2, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu2); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send GTP-U ICMP Packet */ + sess = test_sess_find_by_psi(test_ue, 6); + ogs_assert(sess); + qos_flow = test_qos_flow_find_by_qfi(sess, 1); + ogs_assert(qos_flow); + rv = test_gtpu_send_ping(gtpu2, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu2); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send GTP-U ICMP Packet */ + qos_flow = test_qos_flow_find_by_qfi(sess, 2); + ogs_assert(qos_flow); + rv = test_gtpu_send_ping(gtpu2, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu2); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send HandoverRequired */ + sendbuf = testngap_build_handover_required( + test_ue, NGAP_HandoverType_intra5gs, + 0x4000, 28, + NGAP_Cause_PR_radioNetwork, + NGAP_CauseRadioNetwork_handover_desirable_for_radio_reason, + false); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive HandoverRequest */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send HandoverRequestAcknowledge */ + ogs_list_for_each(&test_ue->sess_list, sess) + sess->gnb_n3_addr = test_self()->gnb1_addr; + + sendbuf = testngap_build_handover_request_ack(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive HandoverCommand */ + recvbuf = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send GTP-U ICMP Packet */ + sess = test_sess_find_by_psi(test_ue, 6); + ogs_assert(sess); + qos_flow = test_qos_flow_find_by_qfi(sess, 1); + ogs_assert(qos_flow); + rv = test_gtpu_send_ping(gtpu2, qos_flow, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu2); + ABTS_PTR_NOTNULL(tc, recvbuf); + + /* Copy ICMP Packet */ + pkbuf = ogs_pkbuf_copy(recvbuf); + ABTS_PTR_NOTNULL(tc, pkbuf); + ogs_pkbuf_pull(pkbuf, OGS_GTPV1U_5GC_HEADER_LEN); + + ogs_pkbuf_free(recvbuf); + + /* Send GTP-U Packet with Indirect Data Forwarding */ + rv = test_gtpu_send_indirect_data_forwarding(gtpu2, qos_flow, pkbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = testgnb_gtpu_read(gtpu1); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send HandoverCancel */ + sendbuf = testngap_build_handover_cancel(test_ue, + NGAP_Cause_PR_radioNetwork, + NGAP_CauseRadioNetwork_txnrelocoverall_expiry); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive UEContextReleaseCommand */ + recvbuf = testgnb_ngap_read(ngap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send UEContextReleaseComplete */ + sendbuf = testngap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap1, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Recv HandoverCancelAcknowledge */ + recvbuf = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + + /* Send UEContextReleaseRequest */ + sendbuf = testngap_build_ue_context_release_request(test_ue, + NGAP_Cause_PR_radioNetwork, NGAP_CauseRadioNetwork_user_inactivity, + false); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive UEContextReleaseCommand */ + recvbuf = testgnb_ngap_read(ngap2); + ABTS_PTR_NOTNULL(tc, recvbuf); + testngap_recv(test_ue, recvbuf); + ABTS_INT_EQUAL(tc, + NGAP_ProcedureCode_id_UEContextRelease, + test_ue->ngap_procedure_code); + + /* Send UEContextReleaseComplete */ + sendbuf = testngap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testgnb_ngap_send(ngap2, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + ogs_msleep(300); + + /********** Remove Subscriber in Database */ + doc = BCON_NEW("imsi", BCON_UTF8(test_ue->imsi)); + ABTS_PTR_NOTNULL(tc, doc); + ABTS_TRUE(tc, mongoc_collection_remove(collection, + MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) + bson_destroy(doc); + + mongoc_collection_destroy(collection); + + /* Two gNB disonncect from UPF */ + testgnb_gtpu_close(gtpu1); + testgnb_gtpu_close(gtpu2); + + /* Two gNB disonncect from AMF */ + testgnb_ngap_close(ngap1); + testgnb_ngap_close(ngap2); + + /* Clear Test UE Context */ + test_ue_remove(test_ue); +} + abts_suite *test_5gc_n2(abts_suite *suite) { suite = ADD_SUITE(suite) - abts_run_test(suite, test1_func, NULL); - abts_run_test(suite, test2_func, NULL); - abts_run_test(suite, test3_func, NULL); + abts_run_test(suite, failure_func, NULL); + abts_run_test(suite, direct_complete_func, NULL); + abts_run_test(suite, direct_cancel_func, NULL); + abts_run_test(suite, indirect_complete_func, NULL); + abts_run_test(suite, indirect_cancel_func, NULL); return suite; } diff --git a/tests/handover/epc-s1-test.c b/tests/handover/epc-s1-test.c index 8572ec024..4507d724f 100644 --- a/tests/handover/epc-s1-test.c +++ b/tests/handover/epc-s1-test.c @@ -443,17 +443,11 @@ static void test1_func(abts_case *tc, void *data) tests1ap_recv(test_ue, recvbuf); /* Send eNB Status Transfer */ - mme_ue_s1ap_id = test_ue->mme_ue_s1ap_id--; - enb_ue_s1ap_id = test_ue->enb_ue_s1ap_id--; - sendbuf = test_s1ap_build_enb_status_transfer(test_ue); ABTS_PTR_NOTNULL(tc, sendbuf); rv = testenb_s1ap_send(s1ap1, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - test_ue->mme_ue_s1ap_id = mme_ue_s1ap_id; - test_ue->enb_ue_s1ap_id = enb_ue_s1ap_id; - /* Receive MME Status Transfer */ recvbuf = testenb_s1ap_read(s1ap2); ABTS_PTR_NOTNULL(tc, recvbuf); @@ -569,17 +563,11 @@ static void test1_func(abts_case *tc, void *data) tests1ap_recv(test_ue, recvbuf); /* Send eNB Status Transfer */ - mme_ue_s1ap_id = test_ue->mme_ue_s1ap_id--; - enb_ue_s1ap_id = test_ue->enb_ue_s1ap_id--; - sendbuf = test_s1ap_build_enb_status_transfer(test_ue); ABTS_PTR_NOTNULL(tc, sendbuf); rv = testenb_s1ap_send(s1ap2, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - test_ue->mme_ue_s1ap_id = mme_ue_s1ap_id; - test_ue->enb_ue_s1ap_id = enb_ue_s1ap_id; - /* Receive MME Status Transfer */ recvbuf = testenb_s1ap_read(s1ap1); ABTS_PTR_NOTNULL(tc, recvbuf); @@ -672,9 +660,6 @@ static void test1_func(abts_case *tc, void *data) tests1ap_recv(test_ue, recvbuf); /* Send Handover Cancel */ - mme_ue_s1ap_id = test_ue->mme_ue_s1ap_id--; - enb_ue_s1ap_id = test_ue->enb_ue_s1ap_id--; - sendbuf = test_s1ap_build_handover_cancel(test_ue, S1AP_Cause_PR_radioNetwork, S1AP_CauseRadioNetwork_tS1relocprep_expiry); @@ -682,15 +667,7 @@ static void test1_func(abts_case *tc, void *data) rv = testenb_s1ap_send(s1ap1, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - /* Recv Handover Cancel Ack */ - recvbuf = testenb_s1ap_read(s1ap1); - ABTS_PTR_NOTNULL(tc, recvbuf); - tests1ap_recv(test_ue, recvbuf); - /* Receive UE Context Release Command */ - mme_ue_s1ap_id = test_ue->mme_ue_s1ap_id; - enb_ue_s1ap_id = test_ue->enb_ue_s1ap_id; - recvbuf = testenb_s1ap_read(s1ap2); ABTS_PTR_NOTNULL(tc, recvbuf); tests1ap_recv(test_ue, recvbuf); @@ -701,8 +678,10 @@ static void test1_func(abts_case *tc, void *data) rv = testenb_s1ap_send(s1ap2, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - test_ue->mme_ue_s1ap_id = mme_ue_s1ap_id; - test_ue->enb_ue_s1ap_id = enb_ue_s1ap_id; + /* Recv HandoverCancelAcknowledge */ + recvbuf = testenb_s1ap_read(s1ap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); ogs_msleep(300); @@ -742,9 +721,6 @@ static void test2_func(abts_case *tc, void *data) test_sess_t *sess = NULL; test_bearer_t *bearer = NULL; - uint32_t enb_ue_s1ap_id; - uint64_t mme_ue_s1ap_id; - const char *_k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; uint8_t k[OGS_KEY_LEN]; const char *_opc_string = "e8ed289deba952e4283b54e88e6183ca"; @@ -1125,9 +1101,6 @@ static void test2_func(abts_case *tc, void *data) tests1ap_recv(test_ue, recvbuf); /* Send Handover Cancel */ - mme_ue_s1ap_id = test_ue->mme_ue_s1ap_id--; - enb_ue_s1ap_id = test_ue->enb_ue_s1ap_id--; - sendbuf = test_s1ap_build_handover_cancel(test_ue, S1AP_Cause_PR_radioNetwork, S1AP_CauseRadioNetwork_tS1relocprep_expiry); @@ -1135,15 +1108,7 @@ static void test2_func(abts_case *tc, void *data) rv = testenb_s1ap_send(s1ap1, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - /* Recv Handover Cancel Ack */ - recvbuf = testenb_s1ap_read(s1ap1); - ABTS_PTR_NOTNULL(tc, recvbuf); - tests1ap_recv(test_ue, recvbuf); - /* Receive UE Context Release Command */ - mme_ue_s1ap_id = test_ue->mme_ue_s1ap_id; - enb_ue_s1ap_id = test_ue->enb_ue_s1ap_id; - recvbuf = testenb_s1ap_read(s1ap2); ABTS_PTR_NOTNULL(tc, recvbuf); tests1ap_recv(test_ue, recvbuf); @@ -1154,8 +1119,10 @@ static void test2_func(abts_case *tc, void *data) rv = testenb_s1ap_send(s1ap2, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - test_ue->mme_ue_s1ap_id = mme_ue_s1ap_id; - test_ue->enb_ue_s1ap_id = enb_ue_s1ap_id; + /* Recv Handover Cancel Ack */ + recvbuf = testenb_s1ap_read(s1ap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); ogs_msleep(300); @@ -1195,9 +1162,6 @@ static void test3_func(abts_case *tc, void *data) test_sess_t *sess = NULL; test_bearer_t *bearer = NULL; - uint32_t enb_ue_s1ap_id; - uint64_t mme_ue_s1ap_id; - const char *_k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; uint8_t k[OGS_KEY_LEN]; const char *_opc_string = "e8ed289deba952e4283b54e88e6183ca"; @@ -1569,27 +1533,22 @@ static void test3_func(abts_case *tc, void *data) rv = testenb_s1ap_send(s1ap2, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - /* Receive Handover Preparation Failure */ - mme_ue_s1ap_id = test_ue->mme_ue_s1ap_id--; - - recvbuf = testenb_s1ap_read(s1ap1); - ABTS_PTR_NOTNULL(tc, recvbuf); - tests1ap_recv(test_ue, recvbuf); - - test_ue->mme_ue_s1ap_id = mme_ue_s1ap_id; - /* Receive UE Context Release Command */ recvbuf = testenb_s1ap_read(s1ap2); ABTS_PTR_NOTNULL(tc, recvbuf); tests1ap_recv(test_ue, recvbuf); /* Send UE Context Release Complete */ - test_ue->enb_ue_s1ap_id++; sendbuf = test_s1ap_build_ue_context_release_complete(test_ue); ABTS_PTR_NOTNULL(tc, sendbuf); rv = testenb_s1ap_send(s1ap2, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); + /* Receive Handover Preparation Failure */ + recvbuf = testenb_s1ap_read(s1ap1); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + ogs_msleep(300); /********** Remove Subscriber in Database */