diff --git a/README.md b/README.md index e4f6126c0..b07b522f0 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ If you find Open5GS useful for work, please consider supporting this Open Source - + @@ -21,7 +21,7 @@ If you find Open5GS useful for work, please consider supporting this Open Source - + diff --git a/src/smf/context.c b/src/smf/context.c index ab2ecc070..07e05e8c1 100644 --- a/src/smf/context.c +++ b/src/smf/context.c @@ -1053,6 +1053,9 @@ smf_sess_t *smf_sess_add_by_apn(smf_ue_t *smf_ue, char *apn, uint8_t rat_type) sess->gtp_rat_type = rat_type; ogs_assert(sess->gtp_rat_type); + /* Set EPC */ + sess->epc = true; + memset(&e, 0, sizeof(e)); e.sess = sess; ogs_fsm_create(&sess->sm, smf_gsm_state_initial, smf_gsm_state_final); diff --git a/src/smf/context.h b/src/smf/context.h index a9b0e5b3d..eabd28619 100644 --- a/src/smf/context.h +++ b/src/smf/context.h @@ -228,6 +228,8 @@ typedef struct smf_sess_s { uint32_t s6b_sta_err; /* S6B CCA RXed error code */ } sm_data; + bool epc; /**< EPC or 5GC */ + ogs_pfcp_sess_t pfcp; /* PFCP session context */ uint64_t smpolicycontrol_features; /* SBI features */ diff --git a/src/smf/gsm-sm.c b/src/smf/gsm-sm.c index 14302ae0e..6df57a3ca 100644 --- a/src/smf/gsm-sm.c +++ b/src/smf/gsm-sm.c @@ -66,7 +66,8 @@ static uint8_t gtp_cause_from_diameter(uint8_t gtp_version, } } -static void send_gtp_create_err_msg(const smf_sess_t *sess, ogs_gtp_xact_t *gtp_xact, uint8_t gtp_cause) +static void send_gtp_create_err_msg(const smf_sess_t *sess, + ogs_gtp_xact_t *gtp_xact, uint8_t gtp_cause) { if (gtp_xact->gtp_version == 1) ogs_gtp1_send_error_message(gtp_xact, sess->sgw_s5c_teid, @@ -76,7 +77,8 @@ static void send_gtp_create_err_msg(const smf_sess_t *sess, ogs_gtp_xact_t *gtp_ OGS_GTP2_CREATE_SESSION_RESPONSE_TYPE, gtp_cause); } -static void send_gtp_delete_err_msg(const smf_sess_t *sess, ogs_gtp_xact_t *gtp_xact, uint8_t gtp_cause) +static void send_gtp_delete_err_msg(const smf_sess_t *sess, + ogs_gtp_xact_t *gtp_xact, uint8_t gtp_cause) { if (gtp_xact->gtp_version == 1) ogs_gtp1_send_error_message(gtp_xact, sess->sgw_s5c_teid, @@ -92,10 +94,11 @@ static bool send_ccr_init_req_gx_gy(smf_sess_t *sess, smf_event_t *e) if (use_gy == -1) { ogs_error("No Gy Diameter Peer"); - /* TODO: drop Gx connection here, possibly move to another "releasing" state! */ + /* TODO: drop Gx connection here, + * possibly move to another "releasing" state! */ uint8_t gtp_cause = (e->gtp_xact->gtp_version == 1) ? - OGS_GTP1_CAUSE_NO_RESOURCES_AVAILABLE : - OGS_GTP2_CAUSE_UE_NOT_AUTHORISED_BY_OCS_OR_EXTERNAL_AAA_SERVER; + OGS_GTP1_CAUSE_NO_RESOURCES_AVAILABLE : + OGS_GTP2_CAUSE_UE_NOT_AUTHORISED_BY_OCS_OR_EXTERNAL_AAA_SERVER; send_gtp_create_err_msg(sess, e->gtp_xact, gtp_cause); return false; } @@ -105,7 +108,8 @@ static bool send_ccr_init_req_gx_gy(smf_sess_t *sess, smf_event_t *e) OGS_DIAM_GX_CC_REQUEST_TYPE_INITIAL_REQUEST); if (use_gy == 1) { - /* Gy is available, set up session for the bearer before accepting it towards the UE */ + /* Gy is available, + * set up session for the bearer before accepting it towards the UE */ sess->sm_data.gy_ccr_init_in_flight = true; smf_gy_send_ccr(sess, e->gtp_xact, OGS_DIAM_GY_CC_REQUEST_TYPE_INITIAL_REQUEST); @@ -121,10 +125,11 @@ static bool send_ccr_termination_req_gx_gy_s6b(smf_sess_t *sess, smf_event_t *e) if (use_gy == -1) { ogs_error("No Gy Diameter Peer"); - /* TODO: drop Gx connection here, possibly move to another "releasing" state! */ + /* TODO: drop Gx connection here, + * possibly move to another "releasing" state! */ uint8_t gtp_cause = (e->gtp_xact->gtp_version == 1) ? - OGS_GTP1_CAUSE_NO_RESOURCES_AVAILABLE : - OGS_GTP2_CAUSE_UE_NOT_AUTHORISED_BY_OCS_OR_EXTERNAL_AAA_SERVER; + OGS_GTP1_CAUSE_NO_RESOURCES_AVAILABLE : + OGS_GTP2_CAUSE_UE_NOT_AUTHORISED_BY_OCS_OR_EXTERNAL_AAA_SERVER; send_gtp_delete_err_msg(sess, e->gtp_xact, gtp_cause); return false; } @@ -140,7 +145,8 @@ static bool send_ccr_termination_req_gx_gy_s6b(smf_sess_t *sess, smf_event_t *e) OGS_DIAM_GX_CC_REQUEST_TYPE_TERMINATION_REQUEST); if (use_gy == 1) { - /* Gy is available, set up session for the bearer before accepting it towards the UE */ + /* Gy is available, + * set up session for the bearer before accepting it towards the UE */ sess->sm_data.gy_ccr_term_in_flight = true; smf_gy_send_ccr(sess, e->gtp_xact, OGS_DIAM_GY_CC_REQUEST_TYPE_TERMINATION_REQUEST); @@ -148,15 +154,83 @@ static bool send_ccr_termination_req_gx_gy_s6b(smf_sess_t *sess, smf_event_t *e) return true; } +static bool send_sbi_message_from_delete_trigger( + smf_sess_t *sess, ogs_sbi_stream_t *stream, int trigger) +{ + ogs_sbi_message_t sendmsg; + ogs_sbi_response_t *response = NULL; + + if (trigger == OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED) { + ogs_pkbuf_t *n1smbuf = NULL, *n2smbuf = NULL; + + n1smbuf = gsm_build_pdu_session_release_command( + sess, OGS_5GSM_CAUSE_REGULAR_DEACTIVATION); + ogs_assert(n1smbuf); + + n2smbuf = ngap_build_pdu_session_resource_release_command_transfer( + sess, SMF_NGAP_STATE_DELETE_TRIGGER_UE_REQUESTED, + NGAP_Cause_PR_nas, NGAP_CauseNas_normal_release); + ogs_assert(n2smbuf); + + ogs_assert(stream); + smf_sbi_send_sm_context_updated_data_n1_n2_message(sess, stream, + n1smbuf, OpenAPI_n2_sm_info_type_PDU_RES_REL_CMD, n2smbuf); + } else if (trigger == OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT || + trigger == OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT) { + memset(&sendmsg, 0, sizeof(sendmsg)); + + response = ogs_sbi_build_response( + &sendmsg, OGS_SBI_HTTP_STATUS_NO_CONTENT); + ogs_assert(response); + + ogs_assert(stream); + ogs_assert(true == ogs_sbi_server_send_response(stream, response)); + } else if (trigger == OGS_PFCP_DELETE_TRIGGER_PCF_INITIATED) { + smf_n1_n2_message_transfer_param_t param; + + memset(¶m, 0, sizeof(param)); + param.state = SMF_NETWORK_REQUESTED_PDU_SESSION_RELEASE; + param.n2smbuf = + ngap_build_pdu_session_resource_release_command_transfer( + sess, SMF_NGAP_STATE_DELETE_TRIGGER_PCF_INITIATED, + NGAP_Cause_PR_nas, NGAP_CauseNas_normal_release); + ogs_assert(param.n2smbuf); + + param.skip_ind = true; + + smf_namf_comm_send_n1_n2_message_transfer(sess, ¶m); + } else { + ogs_fatal("Unknown trigger [%d]", trigger); + ogs_assert_if_reached(); + } + + return true; +} + void smf_gsm_state_initial(ogs_fsm_t *s, smf_event_t *e) { - ogs_assert(s); - ogs_assert(e && e->sess); - smf_sess_t *sess = e->sess; + int rv; + char *strerror = NULL; + smf_ue_t *smf_ue = NULL; + smf_sess_t *sess = NULL; + + ogs_gtp1_message_t *gtp1_message = NULL; + ogs_gtp2_message_t *gtp2_message = NULL; uint8_t gtp1_cause, gtp2_cause; + ogs_nas_5gs_message_t *nas_message = NULL; + + ogs_sbi_stream_t *stream = NULL; + ogs_sbi_message_t *sbi_message = NULL; + + ogs_assert(s); + ogs_assert(e); + smf_sm_debug(e); + sess = e->sess; + ogs_assert(sess); + switch (e->id) { case OGS_FSM_ENTRY_SIG: /* reset state: */ @@ -164,12 +238,16 @@ void smf_gsm_state_initial(ogs_fsm_t *s, smf_event_t *e) sess->sm_data.gy_ccr_init_in_flight = false; sess->sm_data.gx_cca_init_err = ER_DIAMETER_SUCCESS; sess->sm_data.gy_cca_init_err = ER_DIAMETER_SUCCESS; - if (!sess->gtp_rat_type) /* 5gc */ - OGS_FSM_TRAN(s, &smf_gsm_state_operational); + break; + + case OGS_FSM_EXIT_SIG: break; case SMF_EVT_GN_MESSAGE: - switch(e->gtp1_message->h.type) { + gtp1_message = e->gtp1_message; + ogs_assert(gtp1_message); + + switch(gtp1_message->h.type) { case OGS_GTP1_CREATE_PDP_CONTEXT_REQUEST_TYPE: gtp1_cause = smf_gn_handle_create_pdp_context_request(sess, e->gtp_xact, @@ -179,12 +257,15 @@ void smf_gsm_state_initial(ogs_fsm_t *s, smf_event_t *e) return; } if (send_ccr_init_req_gx_gy(sess, e) == true) - OGS_FSM_TRAN(s, &smf_gsm_state_initial_wait_auth); + OGS_FSM_TRAN(s, smf_gsm_state_wait_epc_auth_initial); } break; case SMF_EVT_S5C_MESSAGE: - switch(e->gtp2_message->h.type) { + gtp2_message = e->gtp2_message; + ogs_assert(gtp2_message); + + switch(gtp2_message->h.type) { case OGS_GTP2_CREATE_SESSION_REQUEST_TYPE: gtp2_cause = smf_s5c_handle_create_session_request(sess, e->gtp_xact, @@ -196,39 +277,134 @@ void smf_gsm_state_initial(ogs_fsm_t *s, smf_event_t *e) switch (sess->gtp_rat_type) { case OGS_GTP2_RAT_TYPE_EUTRAN: if (send_ccr_init_req_gx_gy(sess, e) == true) - OGS_FSM_TRAN(s, &smf_gsm_state_initial_wait_auth); + OGS_FSM_TRAN(s, smf_gsm_state_wait_epc_auth_initial); break; case OGS_GTP2_RAT_TYPE_WLAN: smf_s6b_send_aar(sess, e->gtp_xact); - OGS_FSM_TRAN(s, &smf_gsm_state_initial_wait_auth); + OGS_FSM_TRAN(s, smf_gsm_state_wait_epc_auth_initial); break; default: ogs_error("Unknown RAT Type [%d]", sess->gtp_rat_type); ogs_assert_if_reached(); } break; + + default: + ogs_error("Not implmeneted(type:%d)", gtp2_message->h.type); } break; + + case SMF_EVT_SBI_SERVER: + sbi_message = e->sbi.message; + ogs_assert(sbi_message); + stream = e->sbi.data; + ogs_assert(stream); + + SWITCH(sbi_message->h.service.name) + CASE(OGS_SBI_SERVICE_NAME_NSMF_PDUSESSION) + SWITCH(sbi_message->h.resource.component[2]) + CASE(OGS_SBI_RESOURCE_NAME_MODIFY) + CASE(OGS_SBI_RESOURCE_NAME_RELEASE) + ogs_error("Invalid resource name [%s]", + sbi_message->h.resource.component[2]); + ogs_assert(true == + ogs_sbi_server_send_error(stream, + OGS_SBI_HTTP_STATUS_BAD_REQUEST, sbi_message, + "Invalid resource name [%s]", + sbi_message->h.resource.component[2])); + OGS_FSM_TRAN(s, smf_gsm_state_exception); + break; + DEFAULT + if (smf_nsmf_handle_create_sm_context( + sess, stream, sbi_message) == false) { + ogs_error("smf_nsmf_handle_create_sm_context() failed"); + OGS_FSM_TRAN(s, smf_gsm_state_exception); + } + END + break; + + DEFAULT + ogs_error("Invalid API name [%s]", sbi_message->h.service.name); + ogs_assert(true == + ogs_sbi_server_send_error(stream, + OGS_SBI_HTTP_STATUS_BAD_REQUEST, sbi_message, + "Invalid API name", sbi_message->h.service.name)); + OGS_FSM_TRAN(s, smf_gsm_state_exception); + END + break; + + case SMF_EVT_5GSM_MESSAGE: + nas_message = e->nas.message; + ogs_assert(nas_message); + sess = e->sess; + ogs_assert(sess); + stream = e->sbi.data; + ogs_assert(stream); + smf_ue = sess->smf_ue; + ogs_assert(smf_ue); + + switch (nas_message->gsm.h.message_type) { + case OGS_NAS_5GS_PDU_SESSION_ESTABLISHMENT_REQUEST: + rv = gsm_handle_pdu_session_establishment_request(sess, stream, + &nas_message->gsm.pdu_session_establishment_request); + if (rv != OGS_OK) { + ogs_error("[%s:%d] Cannot handle NAS message", + smf_ue->supi, sess->psi); + OGS_FSM_TRAN(s, smf_gsm_state_exception); + break; + } + + OGS_FSM_TRAN(s, smf_gsm_state_wait_5gc_sm_policy_association); + break; + + default: + strerror = ogs_msprintf("Unknown message [%d]", + nas_message->gsm.h.message_type); + ogs_assert(strerror); + + ogs_error("%s", strerror); + ogs_assert(true == + ogs_sbi_server_send_error(stream, + OGS_SBI_HTTP_STATUS_BAD_REQUEST, NULL, strerror, NULL)); + ogs_free(strerror); + + OGS_FSM_TRAN(s, smf_gsm_state_exception); + } + break; + default: + ogs_error("No handler for event %s", smf_event_get_name(e)); + break; } } -void smf_gsm_state_initial_wait_auth(ogs_fsm_t *s, smf_event_t *e) +void smf_gsm_state_wait_epc_auth_initial(ogs_fsm_t *s, smf_event_t *e) { - ogs_assert(e && e->sess); - smf_sess_t *sess = e->sess; + smf_sess_t *sess = NULL; + + ogs_diam_gy_message_t *gy_message = NULL; + ogs_diam_gx_message_t *gx_message = NULL; uint32_t diam_err; + ogs_assert(s); + ogs_assert(e); + smf_sm_debug(e); + sess = e->sess; + ogs_assert(sess); + switch (e->id) { case SMF_EVT_GX_MESSAGE: - switch(e->gx_message->cmd_code) { + gx_message = e->gx_message; + ogs_assert(gx_message); + + switch(gx_message->cmd_code) { case OGS_DIAM_GX_CMD_CODE_CREDIT_CONTROL: - switch(e->gx_message->cc_request_type) { + switch(gx_message->cc_request_type) { case OGS_DIAM_GX_CC_REQUEST_TYPE_INITIAL_REQUEST: ogs_assert(e->gtp_xact); diam_err = smf_gx_handle_cca_initial_request(sess, - e->gx_message, e->gtp_xact); + gx_message, e->gtp_xact); sess->sm_data.gx_ccr_init_in_flight = false; sess->sm_data.gx_cca_init_err = diam_err; goto test_can_proceed; @@ -238,13 +414,16 @@ void smf_gsm_state_initial_wait_auth(ogs_fsm_t *s, smf_event_t *e) break; case SMF_EVT_GY_MESSAGE: - switch(e->gy_message->cmd_code) { + gy_message = e->gy_message; + ogs_assert(gy_message); + + switch(gy_message->cmd_code) { case OGS_DIAM_GY_CMD_CODE_CREDIT_CONTROL: - switch(e->gy_message->cc_request_type) { + switch(gy_message->cc_request_type) { case OGS_DIAM_GY_CC_REQUEST_TYPE_INITIAL_REQUEST: ogs_assert(e->gtp_xact); diam_err = smf_gy_handle_cca_initial_request(sess, - e->gy_message, e->gtp_xact); + gy_message, e->gtp_xact); sess->sm_data.gy_ccr_init_in_flight = false; sess->sm_data.gy_cca_init_err = diam_err; goto test_can_proceed; @@ -266,112 +445,29 @@ test_can_proceed: diam_err = sess->sm_data.gy_cca_init_err; if (diam_err == ER_DIAMETER_SUCCESS) { - OGS_FSM_TRAN(s, &smf_gsm_state_initial_wait_pfcp_establishment); + OGS_FSM_TRAN(s, &smf_gsm_state_wait_pfcp_establishment); ogs_assert(OGS_OK == - smf_epc_pfcp_send_session_establishment_request(sess, e->gtp_xact)); + smf_epc_pfcp_send_session_establishment_request( + sess, e->gtp_xact)); } else { - /* FIXME: tear down Gx/Gy session if its sm_data.*init_err == ER_DIAMETER_SUCCESS */ - uint8_t gtp_cause = gtp_cause_from_diameter(e->gtp_xact->gtp_version, - diam_err, NULL); + /* FIXME: tear down Gx/Gy session + * if its sm_data.*init_err == ER_DIAMETER_SUCCESS */ + uint8_t gtp_cause = gtp_cause_from_diameter( + e->gtp_xact->gtp_version, diam_err, NULL); send_gtp_create_err_msg(sess, e->gtp_xact, gtp_cause); } } } -void smf_gsm_state_initial_wait_pfcp_establishment(ogs_fsm_t *s, smf_event_t *e) +void smf_gsm_state_wait_5gc_sm_policy_association(ogs_fsm_t *s, smf_event_t *e) { - ogs_assert(e && e->sess); - smf_sess_t *sess = e->sess; - uint8_t pfcp_cause, gtp_cause; - - smf_sm_debug(e); - - switch (e->id) { - case SMF_EVT_N4_MESSAGE: - switch (e->pfcp_message->h.type) { - case OGS_PFCP_SESSION_ESTABLISHMENT_RESPONSE_TYPE: - if (e->pfcp_xact->epc) { - ogs_gtp_xact_t *gtp_xact = e->pfcp_xact->assoc_xact; - pfcp_cause = smf_epc_n4_handle_session_establishment_response( - sess, e->pfcp_xact, - &e->pfcp_message->pfcp_session_establishment_response); - if (pfcp_cause != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) { - /* FIXME: tear down Gy and Gx */ - gtp_cause = gtp_cause_from_pfcp(pfcp_cause, gtp_xact->gtp_version); - send_gtp_create_err_msg(sess, e->gtp_xact, gtp_cause); - return; - } - switch (gtp_xact->gtp_version) { - case 1: - ogs_assert(OGS_OK == - smf_gtp1_send_create_pdp_context_response( - sess, gtp_xact)); - break; - case 2: - ogs_assert(OGS_OK == - smf_gtp2_send_create_session_response( - sess, gtp_xact)); - break; - } - if (sess->gtp_rat_type == OGS_GTP2_RAT_TYPE_WLAN) { - /* - * TS23.214 - * 6.3.1.7 Procedures with modification of bearer - * p50 - * 2. ... - * For "PGW/MME initiated bearer deactivation procedure", - * PGW-C shall indicate PGW-U to stop counting and stop - * forwarding downlink packets for the affected bearer(s). - */ - ogs_assert(OGS_OK == - smf_epc_pfcp_send_deactivation(sess, - OGS_GTP2_CAUSE_RAT_CHANGED_FROM_3GPP_TO_NON_3GPP)); - } - smf_bearer_binding(sess); - } else { -#if 0 - /* This is currently not happening, since 5gc isn't yet properly integrated and moves directly to operational state */ - smf_n1_n2_message_transfer_param_t param; - pfcp_cause = smf_5gc_n4_handle_session_establishment_response( - sess, e->pfcp_xact, - &e->pfcp_message->pfcp_session_establishment_response); - if (pfcp_cause != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) - return; - memset(¶m, 0, sizeof(param)); - param.state = SMF_UE_REQUESTED_PDU_SESSION_ESTABLISHMENT; - param.n1smbuf = gsm_build_pdu_session_establishment_accept(sess); - ogs_assert(param.n1smbuf); - param.n2smbuf = ngap_build_pdu_session_resource_setup_request_transfer( - sess); - ogs_assert(param.n2smbuf); - smf_namf_comm_send_n1_n2_message_transfer(sess, ¶m); -#endif - } - OGS_FSM_TRAN(s, &smf_gsm_state_operational); - break; - } - break; - } -} - -void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) -{ - int rv, ngap_state; char *strerror = NULL; smf_ue_t *smf_ue = NULL; smf_sess_t *sess = NULL; - ogs_pkbuf_t *pkbuf = NULL; - - ogs_nas_5gs_message_t *nas_message = NULL; ogs_sbi_stream_t *stream = NULL; ogs_sbi_message_t *sbi_message = NULL; - smf_n1_n2_message_transfer_param_t param; - uint8_t pfcp_cause; - uint8_t gtp1_cause, gtp2_cause; - bool release; - int state = 0; ogs_assert(s); @@ -385,110 +481,9 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) switch (e->id) { case OGS_FSM_ENTRY_SIG: break; - case OGS_FSM_EXIT_SIG: break; - case SMF_EVT_GN_MESSAGE: - switch(e->gtp1_message->h.type) { - case OGS_GTP1_DELETE_PDP_CONTEXT_REQUEST_TYPE: - gtp1_cause = smf_gn_handle_delete_pdp_context_request(sess, - e->gtp_xact, - &e->gtp1_message->delete_pdp_context_request); - if (gtp1_cause != OGS_GTP1_CAUSE_REQUEST_ACCEPTED) { - ogs_gtp1_send_error_message(e->gtp_xact, sess->sgw_s5c_teid, - OGS_GTP1_DELETE_PDP_CONTEXT_RESPONSE_TYPE, gtp1_cause); - return; - } - OGS_FSM_TRAN(s, &smf_gsm_state_release_wait_pfcp_deletion); - } - break; - - case SMF_EVT_S5C_MESSAGE: - switch(e->gtp2_message->h.type) { - case OGS_GTP2_DELETE_SESSION_REQUEST_TYPE: - gtp2_cause = smf_s5c_handle_delete_session_request(sess, e->gtp_xact, - &e->gtp2_message->delete_session_request); - if (gtp2_cause != OGS_GTP2_CAUSE_REQUEST_ACCEPTED) { - ogs_gtp2_send_error_message(e->gtp_xact, sess->sgw_s5c_teid, - OGS_GTP2_DELETE_SESSION_RESPONSE_TYPE, gtp2_cause); - return; - } - OGS_FSM_TRAN(s, &smf_gsm_state_release_wait_pfcp_deletion); - break; - case OGS_GTP2_DELETE_BEARER_RESPONSE_TYPE: - release = smf_s5c_handle_delete_bearer_response( - sess, e->gtp_xact, &e->gtp2_message->delete_bearer_response); - if (release) { - e->gtp_xact = NULL; - OGS_FSM_TRAN(s, &smf_gsm_state_release_wait_pfcp_deletion); - } - break; - } - break; - - case SMF_EVT_N4_MESSAGE: - switch (e->pfcp_message->h.type) { - case OGS_PFCP_SESSION_ESTABLISHMENT_RESPONSE_TYPE: - /* This is left here for 5gc sessions to properly receive messages, - since startup transitions are not implemented yet for 5gc - sessions. See #if0 in smf_gsm_state_initial_wait_pfcp_establishment() */ - assert(!e->pfcp_xact->epc); - pfcp_cause = smf_5gc_n4_handle_session_establishment_response( - sess, e->pfcp_xact, - &e->pfcp_message->pfcp_session_establishment_response); - if (pfcp_cause != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) - return; - memset(¶m, 0, sizeof(param)); - param.state = SMF_UE_REQUESTED_PDU_SESSION_ESTABLISHMENT; - param.n1smbuf = gsm_build_pdu_session_establishment_accept(sess); - ogs_assert(param.n1smbuf); - param.n2smbuf = ngap_build_pdu_session_resource_setup_request_transfer( - sess); - ogs_assert(param.n2smbuf); - smf_namf_comm_send_n1_n2_message_transfer(sess, ¶m); - break; - case OGS_PFCP_SESSION_DELETION_RESPONSE_TYPE: - /* This is left here for 5gc sessions to properly receive messages, - since tear down transitions are not implemented yet for 5gc - sessions. See #if0 in smf_gsm_state_initial_wait_pfcp_deletion() */ - assert(!e->pfcp_xact->epc); - smf_5gc_n4_handle_session_deletion_response(sess, - e->pfcp_xact, &e->pfcp_message->pfcp_session_deletion_response); - break; - } - break; - - case SMF_EVT_SBI_SERVER: - sbi_message = e->sbi.message; - ogs_assert(sbi_message); - stream = e->sbi.data; - ogs_assert(stream); - - SWITCH(sbi_message->h.service.name) - CASE(OGS_SBI_SERVICE_NAME_NSMF_PDUSESSION) - SWITCH(sbi_message->h.resource.component[2]) - CASE(OGS_SBI_RESOURCE_NAME_MODIFY) - smf_nsmf_handle_update_sm_context(sess, stream, sbi_message); - break; - CASE(OGS_SBI_RESOURCE_NAME_RELEASE) - smf_nsmf_handle_release_sm_context(sess, stream, sbi_message); - break; - DEFAULT - smf_nsmf_handle_create_sm_context(sess, stream, sbi_message); - break; - END - break; - - DEFAULT - ogs_error("Invalid API name [%s]", sbi_message->h.service.name); - ogs_assert(true == - ogs_sbi_server_send_error(stream, - OGS_SBI_HTTP_STATUS_BAD_REQUEST, sbi_message, - "Invalid API name", sbi_message->h.service.name)); - END - break; - case SMF_EVT_SBI_CLIENT: sbi_message = e->sbi.message; ogs_assert(sbi_message); @@ -522,8 +517,9 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) } if (smf_nudm_sdm_handle_get( - sess, stream, sbi_message) != true) { - OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + sess, stream, sbi_message) == false) { + ogs_error("smf_nudm_sdm_handle_get() failed"); + OGS_FSM_TRAN(s, smf_gsm_state_exception); } break; @@ -539,6 +535,7 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) OGS_SBI_HTTP_STATUS_BAD_REQUEST, sbi_message, strerror, NULL)); ogs_free(strerror); + OGS_FSM_TRAN(s, smf_gsm_state_exception); END break; @@ -549,7 +546,23 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) SWITCH(sbi_message->h.resource.component[0]) CASE(OGS_SBI_RESOURCE_NAME_SM_POLICIES) - if (!sbi_message->h.resource.component[1]) { + if (sbi_message->h.resource.component[1]) { + strerror = ogs_msprintf("[%s:%d] " + "Unknown resource name [%s]", + smf_ue->supi, sess->psi, + sbi_message->h.resource.component[1]); + ogs_assert(strerror); + + ogs_error("%s", strerror); + if (stream) + ogs_assert(true == + ogs_sbi_server_send_error(stream, + OGS_SBI_HTTP_STATUS_BAD_REQUEST, + sbi_message, strerror, NULL)); + ogs_free(strerror); + + OGS_FSM_TRAN(s, smf_gsm_state_exception); + } else { ogs_assert(stream); if (sbi_message->res_status != OGS_SBI_HTTP_STATUS_CREATED) { @@ -565,11 +578,299 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) sbi_message->res_status, sbi_message, strerror, NULL)); ogs_free(strerror); + + OGS_FSM_TRAN(s, smf_gsm_state_exception); break; } - smf_npcf_smpolicycontrol_handle_create( - sess, stream, state, sbi_message); + if (smf_npcf_smpolicycontrol_handle_create( + sess, stream, state, sbi_message) == true) { + OGS_FSM_TRAN(s, + &smf_gsm_state_wait_pfcp_establishment); + } else { + ogs_error( + "smf_npcf_smpolicycontrol_handle_create() failed"); + OGS_FSM_TRAN(s, smf_gsm_state_exception); + } + } + break; + + DEFAULT + strerror = ogs_msprintf("[%s:%d] Invalid resource name [%s]", + smf_ue->supi, sess->psi, + sbi_message->h.resource.component[0]); + ogs_assert(strerror); + + ogs_error("%s", strerror); + ogs_assert(true == + ogs_sbi_server_send_error(stream, + OGS_SBI_HTTP_STATUS_BAD_REQUEST, + sbi_message, strerror, NULL)); + ogs_free(strerror); + + OGS_FSM_TRAN(s, smf_gsm_state_exception); + END + break; + + DEFAULT + ogs_error("[%s:%d] Invalid API name [%s]", + smf_ue->supi, sess->psi, sbi_message->h.service.name); + ogs_assert_if_reached(); + END + break; + + default: + ogs_error("No handler for event %s", smf_event_get_name(e)); + break; + } +} + + +void smf_gsm_state_wait_pfcp_establishment(ogs_fsm_t *s, smf_event_t *e) +{ + smf_sess_t *sess = NULL; + uint8_t pfcp_cause, gtp_cause; + smf_n1_n2_message_transfer_param_t param; + + ogs_pfcp_xact_t *pfcp_xact = NULL; + ogs_pfcp_message_t *pfcp_message = NULL; + + ogs_assert(s); + ogs_assert(e); + + smf_sm_debug(e); + + sess = e->sess; + ogs_assert(sess); + + switch (e->id) { + case SMF_EVT_N4_MESSAGE: + pfcp_xact = e->pfcp_xact; + ogs_assert(pfcp_xact); + pfcp_message = e->pfcp_message; + ogs_assert(pfcp_message); + + switch (pfcp_message->h.type) { + case OGS_PFCP_SESSION_ESTABLISHMENT_RESPONSE_TYPE: + if (pfcp_xact->epc) { + ogs_gtp_xact_t *gtp_xact = pfcp_xact->assoc_xact; + ogs_assert(gtp_xact); + + pfcp_cause = smf_epc_n4_handle_session_establishment_response( + sess, pfcp_xact, + &pfcp_message->pfcp_session_establishment_response); + if (pfcp_cause != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) { + /* FIXME: tear down Gy and Gx */ + gtp_cause = gtp_cause_from_pfcp( + pfcp_cause, gtp_xact->gtp_version); + send_gtp_create_err_msg(sess, e->gtp_xact, gtp_cause); + return; + } + switch (gtp_xact->gtp_version) { + case 1: + ogs_assert(OGS_OK == + smf_gtp1_send_create_pdp_context_response( + sess, gtp_xact)); + break; + case 2: + ogs_assert(OGS_OK == + smf_gtp2_send_create_session_response( + sess, gtp_xact)); + break; + } + if (sess->gtp_rat_type == OGS_GTP2_RAT_TYPE_WLAN) { + /* + * TS23.214 + * 6.3.1.7 Procedures with modification of bearer + * p50 + * 2. ... + * For "PGW/MME initiated bearer deactivation procedure", + * PGW-C shall indicate PGW-U to stop counting and stop + * forwarding downlink packets for the affected bearer(s). + */ + ogs_assert(OGS_OK == + smf_epc_pfcp_send_deactivation(sess, + OGS_GTP2_CAUSE_RAT_CHANGED_FROM_3GPP_TO_NON_3GPP)); + } + smf_bearer_binding(sess); + } else { + pfcp_cause = smf_5gc_n4_handle_session_establishment_response( + sess, pfcp_xact, + &pfcp_message->pfcp_session_establishment_response); + if (pfcp_cause != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) + return; + memset(¶m, 0, sizeof(param)); + param.state = SMF_UE_REQUESTED_PDU_SESSION_ESTABLISHMENT; + param.n1smbuf = + gsm_build_pdu_session_establishment_accept(sess); + ogs_assert(param.n1smbuf); + param.n2smbuf = + ngap_build_pdu_session_resource_setup_request_transfer( + sess); + ogs_assert(param.n2smbuf); + smf_namf_comm_send_n1_n2_message_transfer(sess, ¶m); + } + + OGS_FSM_TRAN(s, &smf_gsm_state_operational); + break; + + default: + ogs_error("cannot handle PFCP message type[%d]", + pfcp_message->h.type); + } + } +} + +void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) +{ + int rv, ngap_state; + char *strerror = NULL; + smf_ue_t *smf_ue = NULL; + smf_sess_t *sess = NULL; + ogs_pkbuf_t *pkbuf = NULL; + + ogs_nas_5gs_message_t *nas_message = NULL; + + ogs_sbi_stream_t *stream = NULL; + ogs_sbi_message_t *sbi_message = NULL; + + ogs_gtp1_message_t *gtp1_message = NULL; + ogs_gtp2_message_t *gtp2_message = NULL; + uint8_t gtp1_cause, gtp2_cause; + bool release; + + ogs_assert(s); + ogs_assert(e); + + smf_sm_debug(e); + + sess = e->sess; + ogs_assert(sess); + + switch (e->id) { + case OGS_FSM_ENTRY_SIG: + break; + + case OGS_FSM_EXIT_SIG: + break; + + case SMF_EVT_GN_MESSAGE: + gtp1_message = e->gtp1_message; + ogs_assert(gtp1_message); + + switch(gtp1_message->h.type) { + case OGS_GTP1_DELETE_PDP_CONTEXT_REQUEST_TYPE: + gtp1_cause = smf_gn_handle_delete_pdp_context_request(sess, + e->gtp_xact, + >p1_message->delete_pdp_context_request); + if (gtp1_cause != OGS_GTP1_CAUSE_REQUEST_ACCEPTED) { + ogs_gtp1_send_error_message(e->gtp_xact, sess->sgw_s5c_teid, + OGS_GTP1_DELETE_PDP_CONTEXT_RESPONSE_TYPE, gtp1_cause); + return; + } + OGS_FSM_TRAN(s, &smf_gsm_state_wait_pfcp_deletion); + } + break; + + case SMF_EVT_S5C_MESSAGE: + gtp2_message = e->gtp2_message; + ogs_assert(gtp2_message); + + switch(gtp2_message->h.type) { + case OGS_GTP2_DELETE_SESSION_REQUEST_TYPE: + gtp2_cause = smf_s5c_handle_delete_session_request( + sess, e->gtp_xact, + >p2_message->delete_session_request); + if (gtp2_cause != OGS_GTP2_CAUSE_REQUEST_ACCEPTED) { + ogs_gtp2_send_error_message(e->gtp_xact, sess->sgw_s5c_teid, + OGS_GTP2_DELETE_SESSION_RESPONSE_TYPE, gtp2_cause); + return; + } + OGS_FSM_TRAN(s, &smf_gsm_state_wait_pfcp_deletion); + break; + case OGS_GTP2_DELETE_BEARER_RESPONSE_TYPE: + release = smf_s5c_handle_delete_bearer_response( + sess, e->gtp_xact, &e->gtp2_message->delete_bearer_response); + if (release) { + e->gtp_xact = NULL; + OGS_FSM_TRAN(s, &smf_gsm_state_wait_pfcp_deletion); + } + break; + + default: + ogs_error("Not implmeneted(type:%d)", gtp2_message->h.type); + } + break; + + case SMF_EVT_SBI_SERVER: + sbi_message = e->sbi.message; + ogs_assert(sbi_message); + stream = e->sbi.data; + ogs_assert(stream); + + SWITCH(sbi_message->h.service.name) + CASE(OGS_SBI_SERVICE_NAME_NSMF_PDUSESSION) + SWITCH(sbi_message->h.resource.component[2]) + CASE(OGS_SBI_RESOURCE_NAME_MODIFY) + smf_nsmf_handle_update_sm_context(sess, stream, sbi_message); + break; + CASE(OGS_SBI_RESOURCE_NAME_RELEASE) + smf_nsmf_handle_release_sm_context(sess, stream, sbi_message); + break; + DEFAULT + ogs_error("Invalid resource name [%s]", + sbi_message->h.resource.component[2]); + ogs_assert(true == + ogs_sbi_server_send_error(stream, + OGS_SBI_HTTP_STATUS_BAD_REQUEST, sbi_message, + "Invalid resource name [%s]", + sbi_message->h.resource.component[2])); + OGS_FSM_TRAN(s, smf_gsm_state_exception); + END + break; + + DEFAULT + ogs_error("Invalid API name [%s]", sbi_message->h.service.name); + ogs_assert(true == + ogs_sbi_server_send_error(stream, + OGS_SBI_HTTP_STATUS_BAD_REQUEST, sbi_message, + "Invalid API name", sbi_message->h.service.name)); + OGS_FSM_TRAN(s, smf_gsm_state_exception); + END + break; + + case SMF_EVT_SBI_CLIENT: + sbi_message = e->sbi.message; + ogs_assert(sbi_message); + + sess = e->sess; + ogs_assert(sess); + smf_ue = sess->smf_ue; + ogs_assert(smf_ue); + + SWITCH(sbi_message->h.service.name) + CASE(OGS_SBI_SERVICE_NAME_NPCF_SMPOLICYCONTROL) + stream = e->sbi.data; + + SWITCH(sbi_message->h.resource.component[0]) + CASE(OGS_SBI_RESOURCE_NAME_SM_POLICIES) + if (!sbi_message->h.resource.component[1]) { + ogs_assert(stream); + strerror = ogs_msprintf( + "[%s:%d] HTTP response error [%d]", + smf_ue->supi, sess->psi, + sbi_message->res_status); + ogs_assert(strerror); + + ogs_error("%s", strerror); + ogs_assert(true == + ogs_sbi_server_send_error( + stream, sbi_message->res_status, + sbi_message, strerror, NULL)); + ogs_free(strerror); + + OGS_FSM_TRAN(s, smf_gsm_state_exception); + break; } else { SWITCH(sbi_message->h.resource.component[2]) CASE(OGS_SBI_RESOURCE_NAME_DELETE) @@ -582,10 +883,12 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) ogs_assert(strerror); ogs_error("%s", strerror); ogs_free(strerror); + OGS_FSM_TRAN(s, smf_gsm_state_exception); + break; } - smf_npcf_smpolicycontrol_handle_delete( - sess, stream, state, sbi_message); + OGS_FSM_TRAN(&sess->sm, + &smf_gsm_state_wait_pfcp_deletion); break; DEFAULT @@ -602,6 +905,7 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) OGS_SBI_HTTP_STATUS_BAD_REQUEST, sbi_message, strerror, NULL)); ogs_free(strerror); + OGS_FSM_TRAN(s, smf_gsm_state_exception); END } break; @@ -618,6 +922,7 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) OGS_SBI_HTTP_STATUS_BAD_REQUEST, sbi_message, strerror, NULL)); ogs_free(strerror); + OGS_FSM_TRAN(s, smf_gsm_state_exception); END break; @@ -654,16 +959,6 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) ogs_assert(smf_ue); switch (nas_message->gsm.h.message_type) { - case OGS_NAS_5GS_PDU_SESSION_ESTABLISHMENT_REQUEST: - rv = gsm_handle_pdu_session_establishment_request(sess, stream, - &nas_message->gsm.pdu_session_establishment_request); - if (rv != OGS_OK) { - ogs_error("[%s:%d] Cannot handle NAS message", - smf_ue->supi, sess->psi); - OGS_FSM_TRAN(s, smf_gsm_state_exception); - } - break; - case OGS_NAS_5GS_PDU_SESSION_MODIFICATION_REQUEST: rv = gsm_handle_pdu_session_modification_request(sess, stream, &nas_message->gsm.pdu_session_modification_request); @@ -773,12 +1068,6 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) OGS_FSM_TRAN(s, smf_gsm_state_exception); - } else if ( - ngap_state == SMF_NGAP_STATE_DELETE_TRIGGER_UE_REQUESTED || - ngap_state == SMF_NGAP_STATE_DELETE_TRIGGER_PCF_INITIATED) { - - ogs_assert(true == ogs_sbi_send_http_status_no_content(stream)); - } else if (ngap_state == SMF_NGAP_STATE_ERROR_INDICATION_RECEIVED_FROM_5G_AN) { smf_n1_n2_message_transfer_param_t param; @@ -838,11 +1127,20 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) } } -void smf_gsm_state_release_wait_pfcp_deletion(ogs_fsm_t *s, smf_event_t *e) +void smf_gsm_state_wait_pfcp_deletion(ogs_fsm_t *s, smf_event_t *e) { + int status; + smf_sess_t *sess = NULL; + + ogs_sbi_stream_t *stream = NULL; + + ogs_pfcp_xact_t *pfcp_xact = NULL; + ogs_pfcp_message_t *pfcp_message = NULL; + uint8_t pfcp_cause, gtp_cause; - ogs_gtp_xact_t *gtp_xact; + ogs_gtp_xact_t *gtp_xact = NULL; + ogs_assert(s); ogs_assert(e); @@ -853,8 +1151,21 @@ void smf_gsm_state_release_wait_pfcp_deletion(ogs_fsm_t *s, smf_event_t *e) switch (e->id) { case OGS_FSM_ENTRY_SIG: - ogs_assert(OGS_OK == - smf_epc_pfcp_send_session_deletion_request(sess, e->gtp_xact)); + /* Since `pfcp_xact->epc` is not avaiable, + * we'll use `sess->epc` */ + if (sess->epc) { + /* EPC */ + ogs_assert(OGS_OK == + smf_epc_pfcp_send_session_deletion_request(sess, e->gtp_xact)); + } else { + /* 5GC */ + stream = e->sbi.data; + ogs_assert(stream); + + ogs_assert(OGS_OK == + smf_5gc_pfcp_send_session_deletion_request( + sess, stream, e->sbi.state)); + } break; case OGS_FSM_EXIT_SIG: @@ -865,50 +1176,99 @@ void smf_gsm_state_release_wait_pfcp_deletion(ogs_fsm_t *s, smf_event_t *e) break; /* ignore */ case SMF_EVT_N4_MESSAGE: - gtp_xact = e->pfcp_xact->assoc_xact; - switch (e->pfcp_message->h.type) { + pfcp_xact = e->pfcp_xact; + ogs_assert(pfcp_xact); + pfcp_message = e->pfcp_message; + ogs_assert(pfcp_message); + + switch (pfcp_message->h.type) { case OGS_PFCP_SESSION_DELETION_RESPONSE_TYPE: - if (e->pfcp_xact->epc) { - gtp_xact = e->pfcp_xact->assoc_xact; - pfcp_cause = smf_epc_n4_handle_session_deletion_response(sess, - e->pfcp_xact, &e->pfcp_message->pfcp_session_deletion_response); + if (pfcp_xact->epc) { + gtp_xact = pfcp_xact->assoc_xact; + + pfcp_cause = smf_epc_n4_handle_session_deletion_response( + sess, pfcp_xact, + &pfcp_message->pfcp_session_deletion_response); if (pfcp_cause != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) { /* FIXME: tear down Gy and Gx */ - gtp_cause = gtp_cause_from_pfcp(pfcp_cause, gtp_xact->gtp_version); + ogs_assert(gtp_xact); + gtp_cause = gtp_cause_from_pfcp( + pfcp_cause, gtp_xact->gtp_version); send_gtp_delete_err_msg(sess, gtp_xact, gtp_cause); - return; + break; } e->gtp_xact = gtp_xact; if (send_ccr_termination_req_gx_gy_s6b(sess, e) == true) - OGS_FSM_TRAN(s, &smf_gsm_state_release_wait_auth); + OGS_FSM_TRAN(s, &smf_gsm_state_wait_epc_auth_release); /* else: free session? */ } else { -#if 0 - /* This is currently not happening, since 5gc isn't yet properly - integrated and doesn't move to this state */ - smf_5gc_n4_handle_session_deletion_response(sess, - e->pfcp_xact, &message->pfcp_session_deletion_response); - return; /* TODO: implement handling of errors here */ -#endif + int trigger; + + stream = pfcp_xact->assoc_stream; + trigger = pfcp_xact->delete_trigger; + ogs_assert(trigger); + + ogs_pfcp_xact_commit(pfcp_xact); + + status = smf_5gc_n4_handle_session_deletion_response( + sess, stream, trigger, + &pfcp_message->pfcp_session_deletion_response); + if (status != OGS_SBI_HTTP_STATUS_OK) { + ogs_error( + "smf_5gc_n4_handle_session_deletion_response() failed"); + break; + } + + if (send_sbi_message_from_delete_trigger( + sess, stream, trigger) == true) { + + if (trigger == OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED || + trigger == OGS_PFCP_DELETE_TRIGGER_PCF_INITIATED) { + + OGS_FSM_TRAN(s, smf_gsm_state_wait_5gc_n1_n2_release); + + } else if (trigger == + OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT || + trigger == + OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT) { + + OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + + } else if (trigger == + OGS_PFCP_DELETE_TRIGGER_PCF_INITIATED) { + + } else { + ogs_fatal("Unknown trigger [%d]", trigger); + ogs_assert_if_reached(); + } + } } break; - } - return; - default: - ogs_error("Unknown event %s", smf_event_get_name(e)); - break; + default: + ogs_error("cannot handle PFCP message type[%d]", + pfcp_message->h.type); + } } } -void smf_gsm_state_release_wait_auth(ogs_fsm_t *s, smf_event_t *e) +void smf_gsm_state_wait_epc_auth_release(ogs_fsm_t *s, smf_event_t *e) { - ogs_assert(e && e->sess); - smf_sess_t *sess = e->sess; + smf_sess_t *sess = NULL; + + ogs_diam_gx_message_t *gx_message = NULL; + ogs_diam_gy_message_t *gy_message = NULL; + ogs_diam_s6b_message_t *s6b_message = NULL; uint32_t diam_err; + ogs_assert(s); + ogs_assert(e); + smf_sm_debug(e); + sess = e->sess; + ogs_assert(sess); + switch (e->id) { case OGS_FSM_ENTRY_SIG: /* reset state: */ @@ -923,12 +1283,15 @@ void smf_gsm_state_release_wait_auth(ogs_fsm_t *s, smf_event_t *e) break; /* ignore */ case SMF_EVT_GX_MESSAGE: - switch(e->gx_message->cmd_code) { + gx_message = e->gx_message; + ogs_assert(gx_message); + + switch(gx_message->cmd_code) { case OGS_DIAM_GX_CMD_CODE_CREDIT_CONTROL: - switch(e->gx_message->cc_request_type) { + switch(gx_message->cc_request_type) { case OGS_DIAM_GX_CC_REQUEST_TYPE_TERMINATION_REQUEST: diam_err = smf_gx_handle_cca_termination_request(sess, - e->gx_message, e->gtp_xact); + gx_message, e->gtp_xact); sess->sm_data.gx_ccr_term_in_flight = false; sess->sm_data.gx_cca_term_err = diam_err; goto test_can_proceed; @@ -938,13 +1301,16 @@ void smf_gsm_state_release_wait_auth(ogs_fsm_t *s, smf_event_t *e) break; case SMF_EVT_GY_MESSAGE: - switch(e->gy_message->cmd_code) { + gy_message = e->gy_message; + ogs_assert(gy_message); + + switch(gy_message->cmd_code) { case OGS_DIAM_GY_CMD_CODE_CREDIT_CONTROL: - switch(e->gy_message->cc_request_type) { + switch(gy_message->cc_request_type) { case OGS_DIAM_GY_CC_REQUEST_TYPE_TERMINATION_REQUEST: ogs_assert(e->gtp_xact); diam_err = smf_gy_handle_cca_termination_request(sess, - e->gy_message, e->gtp_xact); + gy_message, e->gtp_xact); sess->sm_data.gy_ccr_term_in_flight = false; sess->sm_data.gy_cca_term_err = diam_err; goto test_can_proceed; @@ -954,7 +1320,10 @@ void smf_gsm_state_release_wait_auth(ogs_fsm_t *s, smf_event_t *e) break; case SMF_EVT_S6B_MESSAGE: - switch(e->s6b_message->cmd_code) { + s6b_message = e->s6b_message; + ogs_assert(s6b_message); + + switch(s6b_message->cmd_code) { case OGS_DIAM_S6B_CMD_SESSION_TERMINATION: sess->sm_data.s6b_str_in_flight = false; /* TODO: validate error code from message below: */ @@ -987,15 +1356,19 @@ test_can_proceed: */ switch (e->gtp_xact->gtp_version) { case 1: - ogs_assert(OGS_OK == smf_gtp1_send_delete_pdp_context_response(sess, e->gtp_xact)); + ogs_assert(OGS_OK == + smf_gtp1_send_delete_pdp_context_response( + sess, e->gtp_xact)); break; case 2: - ogs_assert(OGS_OK == smf_gtp2_send_delete_session_response(sess, e->gtp_xact)); + ogs_assert(OGS_OK == + smf_gtp2_send_delete_session_response( + sess, e->gtp_xact)); break; } } else { - uint8_t gtp_cause = gtp_cause_from_diameter(e->gtp_xact->gtp_version, - diam_err, NULL); + uint8_t gtp_cause = gtp_cause_from_diameter( + e->gtp_xact->gtp_version, diam_err, NULL); send_gtp_delete_err_msg(sess, e->gtp_xact, gtp_cause); } } @@ -1003,6 +1376,176 @@ test_can_proceed: } } +void smf_gsm_state_wait_5gc_n1_n2_release(ogs_fsm_t *s, smf_event_t *e) +{ + int ngap_state; + char *strerror = NULL; + + smf_ue_t *smf_ue = NULL; + smf_sess_t *sess = NULL; + ogs_pkbuf_t *pkbuf = NULL; + + ogs_nas_5gs_message_t *nas_message = NULL; + + ogs_sbi_stream_t *stream = NULL; + ogs_sbi_message_t *sbi_message = NULL; + + ogs_assert(s); + ogs_assert(e); + + smf_sm_debug(e); + + sess = e->sess; + ogs_assert(sess); + + switch (e->id) { + case OGS_FSM_ENTRY_SIG: + break; + case OGS_FSM_EXIT_SIG: + break; + + case SMF_EVT_SBI_SERVER: + sbi_message = e->sbi.message; + ogs_assert(sbi_message); + stream = e->sbi.data; + ogs_assert(stream); + + SWITCH(sbi_message->h.service.name) + CASE(OGS_SBI_SERVICE_NAME_NSMF_PDUSESSION) + SWITCH(sbi_message->h.resource.component[2]) + CASE(OGS_SBI_RESOURCE_NAME_MODIFY) + smf_nsmf_handle_update_sm_context(sess, stream, sbi_message); + break; + DEFAULT + ogs_error("Invalid resource name [%s]", + sbi_message->h.resource.component[2]); + ogs_assert(true == + ogs_sbi_server_send_error(stream, + OGS_SBI_HTTP_STATUS_BAD_REQUEST, sbi_message, + "Invalid resource name [%s]", + sbi_message->h.resource.component[2])); + OGS_FSM_TRAN(s, smf_gsm_state_exception); + END + break; + + DEFAULT + ogs_error("Invalid API name [%s]", sbi_message->h.service.name); + ogs_assert(true == + ogs_sbi_server_send_error(stream, + OGS_SBI_HTTP_STATUS_BAD_REQUEST, sbi_message, + "Invalid API name", sbi_message->h.service.name)); + OGS_FSM_TRAN(s, smf_gsm_state_exception); + END + break; + + case SMF_EVT_SBI_CLIENT: + sbi_message = e->sbi.message; + ogs_assert(sbi_message); + + sess = e->sess; + ogs_assert(sess); + smf_ue = sess->smf_ue; + ogs_assert(smf_ue); + + SWITCH(sbi_message->h.service.name) + CASE(OGS_SBI_SERVICE_NAME_NAMF_COMM) + SWITCH(sbi_message->h.resource.component[0]) + CASE(OGS_SBI_RESOURCE_NAME_UE_CONTEXTS) + smf_namf_comm_handle_n1_n2_message_transfer( + sess, e->sbi.state, sbi_message); + break; + + DEFAULT + ogs_error("[%s:%d] Invalid resource name [%s]", + smf_ue->supi, sess->psi, + sbi_message->h.resource.component[0]); + ogs_assert_if_reached(); + END + break; + + DEFAULT + ogs_error("[%s:%d] Invalid API name [%s]", + smf_ue->supi, sess->psi, sbi_message->h.service.name); + ogs_assert_if_reached(); + END + break; + + case SMF_EVT_NGAP_MESSAGE: + sess = e->sess; + ogs_assert(sess); + stream = e->sbi.data; + ogs_assert(stream); + smf_ue = sess->smf_ue; + ogs_assert(smf_ue); + pkbuf = e->pkbuf; + ogs_assert(pkbuf); + ogs_assert(e->ngap.type); + + switch (e->ngap.type) { + case OpenAPI_n2_sm_info_type_PDU_RES_REL_RSP: + ngap_state = sess->ngap_state.pdu_session_resource_release; + + if (ngap_state == SMF_NGAP_STATE_NONE) { + strerror = ogs_msprintf( + "[%s:%d] No PDUSessionResourceReleaseRequest", + smf_ue->supi, sess->psi); + ogs_assert(strerror); + + ogs_error("%s", strerror); + ogs_assert(true == + ogs_sbi_server_send_error(stream, + OGS_SBI_HTTP_STATUS_BAD_REQUEST, NULL, strerror, NULL)); + ogs_free(strerror); + + OGS_FSM_TRAN(s, smf_gsm_state_exception); + + } else if ( + ngap_state == SMF_NGAP_STATE_DELETE_TRIGGER_UE_REQUESTED || + ngap_state == SMF_NGAP_STATE_DELETE_TRIGGER_PCF_INITIATED) { + + ogs_assert(true == ogs_sbi_send_http_status_no_content(stream)); + + } else { + ogs_fatal("Invalid state [%d]", ngap_state); + ogs_assert_if_reached(); + } + break; + default: + ogs_error("Unknown message[%d]", e->ngap.type); + } + break; + + case SMF_EVT_5GSM_MESSAGE: + nas_message = e->nas.message; + ogs_assert(nas_message); + sess = e->sess; + ogs_assert(sess); + stream = e->sbi.data; + ogs_assert(stream); + smf_ue = sess->smf_ue; + ogs_assert(smf_ue); + + switch (nas_message->gsm.h.message_type) { + case OGS_NAS_5GS_PDU_SESSION_RELEASE_COMPLETE: + ogs_assert(true == ogs_sbi_send_http_status_no_content(stream)); + ogs_assert(true == smf_sbi_send_sm_context_status_notify(sess)); + OGS_FSM_TRAN(s, &smf_gsm_state_session_will_release); + break; + default: + strerror = ogs_msprintf("Unknown message [%d]", + nas_message->gsm.h.message_type); + ogs_assert(strerror); + + ogs_error("%s", strerror); + ogs_assert(true == + ogs_sbi_server_send_error(stream, + OGS_SBI_HTTP_STATUS_BAD_REQUEST, NULL, strerror, NULL)); + ogs_free(strerror); + } + break; + } +} + void smf_gsm_state_session_will_release(ogs_fsm_t *s, smf_event_t *e) { smf_sess_t *sess = NULL; @@ -1030,7 +1573,9 @@ void smf_gsm_state_session_will_release(ogs_fsm_t *s, smf_event_t *e) void smf_gsm_state_exception(ogs_fsm_t *s, smf_event_t *e) { + smf_ue_t *smf_ue = NULL; smf_sess_t *sess = NULL; + ogs_assert(s); ogs_assert(e); @@ -1038,9 +1583,13 @@ void smf_gsm_state_exception(ogs_fsm_t *s, smf_event_t *e) sess = e->sess; ogs_assert(sess); + smf_ue = sess->smf_ue; + ogs_assert(smf_ue); switch (e->id) { case OGS_FSM_ENTRY_SIG: + ogs_error("[%s:%d] State machine exception", smf_ue->supi, sess->psi); + SMF_SESS_CLEAR(sess); break; case OGS_FSM_EXIT_SIG: diff --git a/src/smf/n4-handler.c b/src/smf/n4-handler.c index 67066f18c..33f63e536 100644 --- a/src/smf/n4-handler.c +++ b/src/smf/n4-handler.c @@ -141,7 +141,8 @@ static int sbi_status_from_pfcp(uint8_t pfcp_cause) return OGS_SBI_HTTP_STATUS_INTERNAL_SERVER_ERROR; } -/* Returns OGS_PFCP_CAUSE_REQUEST_ACCEPTED on success, other cause value on failure */ +/* Returns OGS_PFCP_CAUSE_REQUEST_ACCEPTED on success, + * other cause value on failure */ uint8_t smf_5gc_n4_handle_session_establishment_response( smf_sess_t *sess, ogs_pfcp_xact_t *xact, ogs_pfcp_session_establishment_response_t *rsp) @@ -599,29 +600,17 @@ void smf_5gc_n4_handle_session_modification_response( } } -void smf_5gc_n4_handle_session_deletion_response( - smf_sess_t *sess, ogs_pfcp_xact_t *xact, +int smf_5gc_n4_handle_session_deletion_response( + smf_sess_t *sess, ogs_sbi_stream_t *stream, int trigger, ogs_pfcp_session_deletion_response_t *rsp) { int status = 0; - int trigger; - - ogs_sbi_stream_t *stream = NULL; - - ogs_sbi_message_t sendmsg; - ogs_sbi_response_t *response = NULL; - - ogs_assert(xact); - ogs_assert(rsp); ogs_debug("Session Deletion Response [5gc]"); - stream = xact->assoc_stream; - trigger = xact->delete_trigger; + ogs_assert(rsp); ogs_assert(trigger); - ogs_pfcp_xact_commit(xact); - status = OGS_SBI_HTTP_STATUS_OK; if (!sess) { @@ -662,59 +651,16 @@ void smf_5gc_n4_handle_session_deletion_response( ogs_error("%s", strerror); ogs_free(strerror); - return; + return status; } ogs_assert(sess); - if (trigger == OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED) { - ogs_pkbuf_t *n1smbuf = NULL, *n2smbuf = NULL; - - n1smbuf = gsm_build_pdu_session_release_command( - sess, OGS_5GSM_CAUSE_REGULAR_DEACTIVATION); - ogs_assert(n1smbuf); - - n2smbuf = ngap_build_pdu_session_resource_release_command_transfer( - sess, SMF_NGAP_STATE_DELETE_TRIGGER_UE_REQUESTED, - NGAP_Cause_PR_nas, NGAP_CauseNas_normal_release); - ogs_assert(n2smbuf); - - ogs_assert(stream); - smf_sbi_send_sm_context_updated_data_n1_n2_message(sess, stream, - n1smbuf, OpenAPI_n2_sm_info_type_PDU_RES_REL_CMD, n2smbuf); - } else if (trigger == OGS_PFCP_DELETE_TRIGGER_AMF_UPDATE_SM_CONTEXT || - trigger == OGS_PFCP_DELETE_TRIGGER_AMF_RELEASE_SM_CONTEXT) { - memset(&sendmsg, 0, sizeof(sendmsg)); - - response = ogs_sbi_build_response( - &sendmsg, OGS_SBI_HTTP_STATUS_NO_CONTENT); - ogs_assert(response); - - ogs_assert(stream); - ogs_assert(true == ogs_sbi_server_send_response(stream, response)); - - OGS_FSM_TRAN(&sess->sm, smf_gsm_state_session_will_release); - } else if (trigger == OGS_PFCP_DELETE_TRIGGER_PCF_INITIATED) { - smf_n1_n2_message_transfer_param_t param; - - memset(¶m, 0, sizeof(param)); - param.state = SMF_NETWORK_REQUESTED_PDU_SESSION_RELEASE; - param.n2smbuf = - ngap_build_pdu_session_resource_release_command_transfer( - sess, SMF_NGAP_STATE_DELETE_TRIGGER_PCF_INITIATED, - NGAP_Cause_PR_nas, NGAP_CauseNas_normal_release); - ogs_assert(param.n2smbuf); - - param.skip_ind = true; - - smf_namf_comm_send_n1_n2_message_transfer(sess, ¶m); - } else { - ogs_fatal("Unknown trigger [%d]", trigger); - ogs_assert_if_reached(); - } + return status; } -/* Returns OGS_PFCP_CAUSE_REQUEST_ACCEPTED on success, other cause value on failure */ +/* Returns OGS_PFCP_CAUSE_REQUEST_ACCEPTED on success, + * other cause value on failure */ uint8_t smf_epc_n4_handle_session_establishment_response( smf_sess_t *sess, ogs_pfcp_xact_t *xact, ogs_pfcp_session_establishment_response_t *rsp) @@ -1109,7 +1055,8 @@ uint8_t smf_epc_n4_handle_session_deletion_response( bearer = smf_default_bearer_in_sess(sess); for (i = 0; i < OGS_ARRAY_SIZE(rsp->usage_report); i++) { - ogs_pfcp_tlv_usage_report_session_deletion_response_t *use_rep = &rsp->usage_report[i]; + ogs_pfcp_tlv_usage_report_session_deletion_response_t *use_rep = + &rsp->usage_report[i]; uint32_t urr_id; ogs_pfcp_volume_measurement_t volume; if (use_rep->presence == 0) @@ -1119,7 +1066,8 @@ uint8_t smf_epc_n4_handle_session_deletion_response( urr_id = use_rep->urr_id.u32; if (!bearer || !bearer->urr || bearer->urr->id != urr_id) continue; - ogs_pfcp_parse_volume_measurement(&volume, &use_rep->volume_measurement); + ogs_pfcp_parse_volume_measurement( + &volume, &use_rep->volume_measurement); if (volume.ulvol) sess->gy.ul_octets += volume.uplink_volume; if (volume.dlvol) @@ -1270,7 +1218,8 @@ void smf_n4_handle_session_report_request( if (report_type.usage_report) { bearer = smf_default_bearer_in_sess(sess); for (i = 0; i < OGS_ARRAY_SIZE(pfcp_req->usage_report); i++) { - ogs_pfcp_tlv_usage_report_session_report_request_t *use_rep = &pfcp_req->usage_report[i]; + ogs_pfcp_tlv_usage_report_session_report_request_t *use_rep = + &pfcp_req->usage_report[i]; uint32_t urr_id; ogs_pfcp_volume_measurement_t volume; if (use_rep->presence == 0) @@ -1280,7 +1229,8 @@ void smf_n4_handle_session_report_request( urr_id = use_rep->urr_id.u32; if (!bearer || !bearer->urr || bearer->urr->id != urr_id) continue; - ogs_pfcp_parse_volume_measurement(&volume, &use_rep->volume_measurement); + ogs_pfcp_parse_volume_measurement( + &volume, &use_rep->volume_measurement); if (volume.ulvol) sess->gy.ul_octets += volume.uplink_volume; if (volume.dlvol) @@ -1289,7 +1239,8 @@ void smf_n4_handle_session_report_request( } switch(smf_use_gy_iface()) { case 1: - smf_gy_send_ccr(sess, pfcp_xact, OGS_DIAM_GY_CC_REQUEST_TYPE_UPDATE_REQUEST); + smf_gy_send_ccr(sess, pfcp_xact, + OGS_DIAM_GY_CC_REQUEST_TYPE_UPDATE_REQUEST); break; case -1: ogs_error("No Gy Diameter Peer"); diff --git a/src/smf/n4-handler.h b/src/smf/n4-handler.h index 00b53009b..40c38c70a 100644 --- a/src/smf/n4-handler.h +++ b/src/smf/n4-handler.h @@ -32,8 +32,8 @@ uint8_t smf_5gc_n4_handle_session_establishment_response( void smf_5gc_n4_handle_session_modification_response( smf_sess_t *sess, ogs_pfcp_xact_t *xact, ogs_pfcp_session_modification_response_t *rsp); -void smf_5gc_n4_handle_session_deletion_response( - smf_sess_t *sess, ogs_pfcp_xact_t *xact, +int smf_5gc_n4_handle_session_deletion_response( + smf_sess_t *sess, ogs_sbi_stream_t *stream, int trigger, ogs_pfcp_session_deletion_response_t *rsp); uint8_t smf_epc_n4_handle_session_establishment_response( diff --git a/src/smf/npcf-handler.c b/src/smf/npcf-handler.c index 7a7849724..8258edaa1 100644 --- a/src/smf/npcf-handler.c +++ b/src/smf/npcf-handler.c @@ -590,20 +590,6 @@ cleanup: return false; } -bool smf_npcf_smpolicycontrol_handle_delete( - smf_sess_t *sess, ogs_sbi_stream_t *stream, int state, - ogs_sbi_message_t *recvmsg) -{ - int trigger = state; - - ogs_assert(trigger); - - ogs_assert(OGS_OK == - smf_5gc_pfcp_send_session_deletion_request(sess, stream, trigger)); - - return true; -} - bool smf_npcf_smpolicycontrol_handle_update_notify( smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg) { diff --git a/src/smf/npcf-handler.h b/src/smf/npcf-handler.h index babe698b1..d9426ebee 100644 --- a/src/smf/npcf-handler.h +++ b/src/smf/npcf-handler.h @@ -29,9 +29,6 @@ extern "C" { bool smf_npcf_smpolicycontrol_handle_create( smf_sess_t *sess, ogs_sbi_stream_t *stream, int state, ogs_sbi_message_t *recvmsg); -bool smf_npcf_smpolicycontrol_handle_delete( - smf_sess_t *sess, ogs_sbi_stream_t *stream, int state, - ogs_sbi_message_t *recvmsg); bool smf_npcf_smpolicycontrol_handle_update_notify( smf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg); bool smf_npcf_smpolicycontrol_handle_terminate_notify( diff --git a/src/smf/smf-sm.c b/src/smf/smf-sm.c index e42ae0fba..297ef6dcf 100644 --- a/src/smf/smf-sm.c +++ b/src/smf/smf-sm.c @@ -245,7 +245,7 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e) sess, gtp_xact, >p1_message.update_pdp_context_request); break; case OGS_GTP1_ERROR_INDICATION_TYPE: - /* TS 29.060 10.1.1.4 dst port shall be the user plane port (2152) */ + /* TS 29.060 10.1.1.4 dst port shall be the userplane port (2152) */ ogs_error("Rx unexpected Error Indication in GTPC port"); break; default: @@ -514,10 +514,6 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e) e->sess = sess; e->sbi.message = &sbi_message; ogs_fsm_dispatch(&sess->sm, e); - if (OGS_FSM_CHECK(&sess->sm, smf_gsm_state_exception)) { - ogs_error("[%s] State machine exception", smf_ue->supi); - SMF_SESS_CLEAR(sess); - } } break; @@ -743,10 +739,6 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e) e->sbi.message = &sbi_message; ogs_fsm_dispatch(&sess->sm, e); - if (OGS_FSM_CHECK(&sess->sm, smf_gsm_state_exception)) { - ogs_error("[%s] State machine exception", smf_ue->supi); - SMF_SESS_CLEAR(sess); - } break; DEFAULT @@ -830,22 +822,8 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e) ogs_assert(sess); ogs_assert(OGS_FSM_STATE(&sess->sm)); - sess->pti = nas_message.gsm.h.procedure_transaction_identity; - - switch (nas_message.gsm.h.message_type) { - case OGS_NAS_5GS_PDU_SESSION_RELEASE_COMPLETE: - ogs_assert(true == ogs_sbi_send_http_status_no_content(stream)); - ogs_assert(true == smf_sbi_send_sm_context_status_notify(sess)); - SMF_SESS_CLEAR(sess); - break; - default: - e->nas.message = &nas_message; - ogs_fsm_dispatch(&sess->sm, e); - if (OGS_FSM_CHECK(&sess->sm, smf_gsm_state_exception)) { - ogs_error("State machine exception"); - SMF_SESS_CLEAR(sess); - } - } + e->nas.message = &nas_message; + ogs_fsm_dispatch(&sess->sm, e); ogs_pkbuf_free(pkbuf); break; @@ -863,10 +841,6 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e) ogs_assert(OGS_FSM_STATE(&sess->sm)); ogs_fsm_dispatch(&sess->sm, e); - if (OGS_FSM_CHECK(&sess->sm, smf_gsm_state_exception)) { - ogs_error("State machine exception"); - SMF_SESS_CLEAR(sess); - } ogs_pkbuf_free(pkbuf); break; diff --git a/src/smf/smf-sm.h b/src/smf/smf-sm.h index a580bd9c7..68dffdd7a 100644 --- a/src/smf/smf-sm.h +++ b/src/smf/smf-sm.h @@ -43,11 +43,13 @@ void smf_nf_state_exception(ogs_fsm_t *s, smf_event_t *e); void smf_gsm_state_initial(ogs_fsm_t *s, smf_event_t *e); void smf_gsm_state_final(ogs_fsm_t *s, smf_event_t *e); -void smf_gsm_state_initial_wait_auth(ogs_fsm_t *s, smf_event_t *e); -void smf_gsm_state_initial_wait_pfcp_establishment(ogs_fsm_t *s, smf_event_t *e); +void smf_gsm_state_wait_epc_auth_initial(ogs_fsm_t *s, smf_event_t *e); +void smf_gsm_state_wait_5gc_sm_policy_association(ogs_fsm_t *s, smf_event_t *e); +void smf_gsm_state_wait_pfcp_establishment(ogs_fsm_t *s, smf_event_t *e); void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e); -void smf_gsm_state_release_wait_pfcp_deletion(ogs_fsm_t *s, smf_event_t *e); -void smf_gsm_state_release_wait_auth(ogs_fsm_t *s, smf_event_t *e); +void smf_gsm_state_wait_pfcp_deletion(ogs_fsm_t *s, smf_event_t *e); +void smf_gsm_state_wait_epc_auth_release(ogs_fsm_t *s, smf_event_t *e); +void smf_gsm_state_wait_5gc_n1_n2_release(ogs_fsm_t *s, smf_event_t *e); void smf_gsm_state_session_will_release(ogs_fsm_t *s, smf_event_t *e); void smf_gsm_state_exception(ogs_fsm_t *s, smf_event_t *e); diff --git a/src/upf/rule-match.h b/src/upf/rule-match.h index 3a40d6cf2..c920338ce 100644 --- a/src/upf/rule-match.h +++ b/src/upf/rule-match.h @@ -27,8 +27,6 @@ extern "C" { #endif upf_sess_t *upf_sess_find_by_ue_ip_address(ogs_pkbuf_t *pkbuf); -ogs_pfcp_rule_t *upf_pdr_rule_find_by_packet( - ogs_pfcp_pdr_t *pdr, ogs_pkbuf_t *pkbuf); #ifdef __cplusplus }