/* * Copyright (C) 2019 by Sukchan Lee * * This file is part of Open5GS. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "context.h" #include "timer.h" #include "pfcp-path.h" #include "gtp-path.h" #include "n4-handler.h" #include "bearer-binding.h" #include "sbi-path.h" static uint8_t gtp_cause_from_pfcp(uint8_t pfcp_cause) { switch (pfcp_cause) { case OGS_PFCP_CAUSE_REQUEST_ACCEPTED: return OGS_GTP_CAUSE_REQUEST_ACCEPTED; case OGS_PFCP_CAUSE_REQUEST_REJECTED: return OGS_GTP_CAUSE_REQUEST_REJECTED_REASON_NOT_SPECIFIED; case OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND: return OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; case OGS_PFCP_CAUSE_MANDATORY_IE_MISSING: return OGS_GTP_CAUSE_MANDATORY_IE_MISSING; case OGS_PFCP_CAUSE_CONDITIONAL_IE_MISSING: return OGS_GTP_CAUSE_CONDITIONAL_IE_MISSING; case OGS_PFCP_CAUSE_INVALID_LENGTH: return OGS_GTP_CAUSE_INVALID_LENGTH; case OGS_PFCP_CAUSE_MANDATORY_IE_INCORRECT: return OGS_GTP_CAUSE_MANDATORY_IE_INCORRECT; case OGS_PFCP_CAUSE_INVALID_FORWARDING_POLICY: case OGS_PFCP_CAUSE_INVALID_F_TEID_ALLOCATION_OPTION: return OGS_GTP_CAUSE_INVALID_MESSAGE_FORMAT; case OGS_PFCP_CAUSE_NO_ESTABLISHED_PFCP_ASSOCIATION: return OGS_GTP_CAUSE_REMOTE_PEER_NOT_RESPONDING; case OGS_PFCP_CAUSE_RULE_CREATION_MODIFICATION_FAILURE: return OGS_GTP_CAUSE_SEMANTIC_ERROR_IN_THE_TFT_OPERATION; case OGS_PFCP_CAUSE_PFCP_ENTITY_IN_CONGESTION: return OGS_GTP_CAUSE_GTP_C_ENTITY_CONGESTION; case OGS_PFCP_CAUSE_NO_RESOURCES_AVAILABLE: return OGS_GTP_CAUSE_NO_RESOURCES_AVAILABLE; case OGS_PFCP_CAUSE_SERVICE_NOT_SUPPORTED: return OGS_GTP_CAUSE_SERVICE_NOT_SUPPORTED; case OGS_PFCP_CAUSE_SYSTEM_FAILURE: return OGS_GTP_CAUSE_SYSTEM_FAILURE; default: return OGS_GTP_CAUSE_SYSTEM_FAILURE; } return OGS_GTP_CAUSE_SYSTEM_FAILURE; } static int sbi_status_from_pfcp(uint8_t pfcp_cause) { switch (pfcp_cause) { case OGS_PFCP_CAUSE_REQUEST_ACCEPTED: return OGS_SBI_HTTP_STATUS_OK; case OGS_PFCP_CAUSE_REQUEST_REJECTED: return OGS_SBI_HTTP_STATUS_FORBIDDEN; case OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND: return OGS_SBI_HTTP_STATUS_NOT_FOUND; case OGS_PFCP_CAUSE_MANDATORY_IE_MISSING: case OGS_PFCP_CAUSE_CONDITIONAL_IE_MISSING: case OGS_PFCP_CAUSE_INVALID_LENGTH: case OGS_PFCP_CAUSE_MANDATORY_IE_INCORRECT: case OGS_PFCP_CAUSE_INVALID_FORWARDING_POLICY: case OGS_PFCP_CAUSE_INVALID_F_TEID_ALLOCATION_OPTION: case OGS_PFCP_CAUSE_RULE_CREATION_MODIFICATION_FAILURE: case OGS_PFCP_CAUSE_PFCP_ENTITY_IN_CONGESTION: case OGS_PFCP_CAUSE_NO_RESOURCES_AVAILABLE: return OGS_SBI_HTTP_STATUS_BAD_REQUEST; case OGS_PFCP_CAUSE_NO_ESTABLISHED_PFCP_ASSOCIATION: return OGS_SBI_HTTP_STATUS_GATEWAY_TIMEOUT; case OGS_PFCP_CAUSE_SERVICE_NOT_SUPPORTED: return OGS_SBI_HTTP_STATUS_SERVICE_UNAVAILABLE; case OGS_PFCP_CAUSE_SYSTEM_FAILURE: return OGS_SBI_HTTP_STATUS_INTERNAL_SERVER_ERROR; default: return OGS_SBI_HTTP_STATUS_INTERNAL_SERVER_ERROR; } return OGS_SBI_HTTP_STATUS_INTERNAL_SERVER_ERROR; } void smf_n4_handle_association_setup_request( ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, ogs_pfcp_association_setup_request_t *req) { int i; ogs_assert(xact); ogs_assert(node); ogs_assert(req); smf_pfcp_send_association_setup_response( xact, OGS_PFCP_CAUSE_REQUEST_ACCEPTED); ogs_pfcp_gtpu_resource_remove_all(&node->gtpu_resource_list); for (i = 0; i < OGS_MAX_NUM_OF_GTPU_RESOURCE; i++) { ogs_pfcp_tlv_user_plane_ip_resource_information_t *message = &req->user_plane_ip_resource_information[i]; ogs_pfcp_user_plane_ip_resource_info_t info; if (message->presence == 0) break; ogs_pfcp_parse_user_plane_ip_resource_info(&info, message); ogs_pfcp_gtpu_resource_add(&node->gtpu_resource_list, &info); } } void smf_n4_handle_association_setup_response( ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, ogs_pfcp_association_setup_response_t *rsp) { int i; ogs_assert(xact); ogs_pfcp_xact_commit(xact); ogs_assert(node); ogs_assert(rsp); ogs_pfcp_gtpu_resource_remove_all(&node->gtpu_resource_list); for (i = 0; i < OGS_MAX_NUM_OF_GTPU_RESOURCE; i++) { ogs_pfcp_tlv_user_plane_ip_resource_information_t *message = &rsp->user_plane_ip_resource_information[i]; ogs_pfcp_user_plane_ip_resource_info_t info; if (message->presence == 0) break; ogs_pfcp_parse_user_plane_ip_resource_info(&info, message); ogs_pfcp_gtpu_resource_add(&node->gtpu_resource_list, &info); } } void smf_n4_handle_heartbeat_request( ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, ogs_pfcp_heartbeat_request_t *req) { ogs_assert(xact); ogs_pfcp_send_heartbeat_response(xact); } void smf_n4_handle_heartbeat_response( ogs_pfcp_node_t *node, ogs_pfcp_xact_t *xact, ogs_pfcp_heartbeat_response_t *rsp) { ogs_assert(xact); ogs_pfcp_xact_commit(xact); ogs_timer_start(node->t_no_heartbeat, ogs_config()->time.message.pfcp.no_heartbeat_duration); } void smf_5gc_n4_handle_session_establishment_response( smf_sess_t *sess, ogs_pfcp_xact_t *xact, ogs_pfcp_session_establishment_response_t *rsp) { ogs_pfcp_f_seid_t *up_f_seid = NULL; ogs_assert(xact); ogs_assert(rsp); ogs_pfcp_xact_commit(xact); if (!sess) { ogs_warn("No Context"); return; } if (rsp->up_f_seid.presence == 0) { ogs_error("No UP F-SEID"); return; } if (rsp->cause.presence) { if (rsp->cause.u8 != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) { ogs_error("PFCP Cause [%d] : Not Accepted", rsp->cause.u8); return; } } else { ogs_error("No Cause"); return; } ogs_assert(sess); /* UP F-SEID */ up_f_seid = rsp->up_f_seid.data; ogs_assert(up_f_seid); sess->upf_n4_seid = be64toh(up_f_seid->seid); /* UPDATE_UpCnxState - ACTIVATING */ sess->ueUpCnxState = OpenAPI_up_cnx_state_ACTIVATING; sess->smfUpCnxState = OpenAPI_up_cnx_state_ACTIVATING; smf_sbi_discover_and_send(OpenAPI_nf_type_AMF, sess, NULL, smf_namf_comm_build_n1_n2_message_transfer); #if 0 smf_bearer_binding(sess); #endif } void smf_5gc_n4_handle_session_modification_response( smf_sess_t *sess, ogs_pfcp_xact_t *xact, ogs_pfcp_session_modification_response_t *rsp) { int status = 0; ogs_sbi_session_t *session = NULL; ogs_assert(xact); ogs_assert(rsp); session = xact->assoc_session; ogs_assert(session); ogs_pfcp_xact_commit(xact); status = OGS_SBI_HTTP_STATUS_OK; if (!sess) { ogs_warn("No Context"); status = OGS_SBI_HTTP_STATUS_NOT_FOUND; } if (rsp->cause.presence) { if (rsp->cause.u8 != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) { ogs_warn("PFCP Cause [%d] : Not Accepted", rsp->cause.u8); status = sbi_status_from_pfcp(rsp->cause.u8); } } else { ogs_error("No Cause"); status = OGS_SBI_HTTP_STATUS_BAD_REQUEST; } if (status != OGS_SBI_HTTP_STATUS_OK) { char *strerror = ogs_msprintf( "PFCP Cause [%d] : Not Accepted", rsp->cause.u8); smf_sbi_send_sm_context_update_error(session, status, strerror, NULL, NULL, NULL); ogs_free(strerror); return; } ogs_assert(sess); /* UPDATE_UpCnxState - SYNC */ sess->smfUpCnxState = sess->ueUpCnxState; smf_sbi_send_sm_context_updated_data(sess, session); } void smf_5gc_n4_handle_session_deletion_response( smf_sess_t *sess, ogs_pfcp_xact_t *xact, ogs_pfcp_session_deletion_response_t *rsp) { int status = 0; int trigger; ogs_sbi_session_t *session = NULL; ogs_sbi_message_t sendmsg; ogs_sbi_response_t *response = NULL; ogs_assert(xact); ogs_assert(rsp); session = xact->assoc_session; ogs_assert(session); trigger = xact->trigger; ogs_assert(trigger); ogs_pfcp_xact_commit(xact); status = OGS_SBI_HTTP_STATUS_OK; if (!sess) { ogs_warn("No Context"); status = OGS_SBI_HTTP_STATUS_NOT_FOUND; } if (rsp->cause.presence) { if (rsp->cause.u8 != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) { ogs_warn("PFCP Cause [%d] : Not Accepted", rsp->cause.u8); status = sbi_status_from_pfcp(rsp->cause.u8); } } else { ogs_error("No Cause"); status = OGS_SBI_HTTP_STATUS_BAD_REQUEST; } if (status != OGS_SBI_HTTP_STATUS_OK) { char *strerror = ogs_msprintf( "PFCP Cause [%d] : Not Accepted", rsp->cause.u8); ogs_sbi_server_send_error(session, status, NULL, NULL, NULL); ogs_free(strerror); return; } ogs_assert(sess); if (trigger == OGS_PFCP_5GC_DELETE_TRIGGER_UE_REQUESTED) { smf_sbi_send_sm_context_updated_data_in_session_deletion(sess, session); } else { memset(&sendmsg, 0, sizeof(sendmsg)); response = ogs_sbi_build_response( &sendmsg, OGS_SBI_HTTP_STATUS_NO_CONTENT); ogs_assert(response); ogs_sbi_server_send_response(session, response); SMF_SESS_CLEAR(sess); } } void smf_epc_n4_handle_session_establishment_response( smf_sess_t *sess, ogs_pfcp_xact_t *xact, ogs_pfcp_session_establishment_response_t *rsp) { uint8_t cause_value = 0; ogs_gtp_xact_t *gtp_xact = NULL; ogs_pfcp_f_seid_t *up_f_seid = NULL; ogs_assert(xact); ogs_assert(rsp); gtp_xact = xact->assoc_xact; ogs_assert(gtp_xact); ogs_pfcp_xact_commit(xact); cause_value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; if (!sess) { ogs_warn("No Context"); cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; } if (rsp->up_f_seid.presence == 0) { ogs_error("No UP F-SEID"); cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; } if (rsp->cause.presence) { if (rsp->cause.u8 != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) { ogs_warn("PFCP Cause [%d] : Not Accepted", rsp->cause.u8); cause_value = gtp_cause_from_pfcp(rsp->cause.u8); } } else { ogs_error("No Cause"); cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; } if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { ogs_gtp_send_error_message(gtp_xact, sess ? sess->sgw_s5c_teid : 0, OGS_GTP_CREATE_SESSION_RESPONSE_TYPE, cause_value); return; } ogs_assert(sess); /* UP F-SEID */ up_f_seid = rsp->up_f_seid.data; ogs_assert(up_f_seid); sess->upf_n4_seid = be64toh(up_f_seid->seid); smf_gtp_send_create_session_response(sess, gtp_xact); smf_bearer_binding(sess); } void smf_epc_n4_handle_session_modification_response( smf_sess_t *sess, ogs_pfcp_xact_t *xact, ogs_pfcp_session_modification_response_t *rsp) { smf_bearer_t *bearer = NULL; ogs_assert(xact); ogs_assert(rsp); bearer = xact->data; ogs_assert(bearer); ogs_pfcp_xact_commit(xact); if (bearer->pfcp_epc_modify.remove) smf_bearer_remove(bearer); } void smf_epc_n4_handle_session_deletion_response( smf_sess_t *sess, ogs_pfcp_xact_t *xact, ogs_pfcp_session_deletion_response_t *rsp) { uint8_t cause_value = 0; ogs_gtp_xact_t *gtp_xact = NULL; ogs_assert(xact); ogs_assert(rsp); gtp_xact = xact->assoc_xact; ogs_assert(gtp_xact); ogs_pfcp_xact_commit(xact); cause_value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; if (!sess) { ogs_warn("No Context"); cause_value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; } if (rsp->cause.presence) { if (rsp->cause.u8 != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) { ogs_warn("PFCP Cause[%d] : Not Accepted", rsp->cause.u8); cause_value = gtp_cause_from_pfcp(rsp->cause.u8); } } else { ogs_error("No Cause"); cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; } if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { ogs_gtp_send_error_message(gtp_xact, sess ? sess->sgw_s5c_teid : 0, OGS_GTP_DELETE_SESSION_RESPONSE_TYPE, cause_value); return; } ogs_assert(sess); smf_gtp_send_delete_session_response(sess, gtp_xact); SMF_SESS_CLEAR(sess); }