/* * Copyright (C) 2023 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 "sbi-path.h" static int request_handler(ogs_sbi_request_t *request, void *data); static int response_handler( int status, ogs_sbi_response_t *response, void *data); static void copy_request( ogs_sbi_request_t *target, ogs_sbi_request_t *source, bool do_not_remove_custom_header); int sepp_sbi_open(void) { ogs_sbi_nf_instance_t *nf_instance = NULL; /* Initialize SELF NF instance */ nf_instance = ogs_sbi_self()->nf_instance; ogs_assert(nf_instance); ogs_sbi_nf_fsm_init(nf_instance); /* Build NF instance information. It will be transmitted to NRF. */ ogs_sbi_nf_instance_build_default(nf_instance); /* Initialize NRF NF Instance */ nf_instance = ogs_sbi_self()->nrf_instance; if (nf_instance) ogs_sbi_nf_fsm_init(nf_instance); if (ogs_sbi_server_start_all(request_handler) != OGS_OK) return OGS_ERROR; return OGS_OK; } void sepp_sbi_close(void) { ogs_sbi_client_stop_all(); ogs_sbi_server_stop_all(); } bool sepp_n32c_handshake_send_security_capability_request( sepp_node_t *sepp_node, bool none) { bool rc; ogs_sbi_request_t *request = NULL; ogs_sbi_client_t *client = NULL; ogs_assert(sepp_node); client = sepp_node->client; if (!client) { ogs_error("No Client"); return false; } request = sepp_n32c_handshake_build_security_capability_request( sepp_node, none); if (!request) { ogs_error("sepp_n32c_handshake_build_exchange_capability() failed"); return false; } rc = ogs_sbi_client_send_request( client, ogs_sbi_client_handler, request, sepp_node); ogs_expect(rc == true); ogs_sbi_request_free(request); return rc; } void sepp_n32c_handshake_send_security_capability_response( sepp_node_t *sepp_node, ogs_sbi_stream_t *stream) { ogs_sbi_message_t sendmsg; ogs_sbi_response_t *response = NULL; OpenAPI_sec_negotiate_rsp_data_t SecNegotiateRspData; OpenAPI_list_t *PlmnIdList = NULL; OpenAPI_plmn_id_t *PlmnId = NULL; int i; OpenAPI_lnode_t *node = NULL; ogs_assert(sepp_self()->sender); ogs_assert(sepp_node); ogs_assert(stream); memset(&SecNegotiateRspData, 0, sizeof(SecNegotiateRspData)); SecNegotiateRspData.sender = sepp_self()->sender; SecNegotiateRspData.selected_sec_capability = sepp_node->negotiated_security_scheme; if (SecNegotiateRspData.selected_sec_capability != OpenAPI_security_capability_NONE) { if (sepp_node->target_apiroot_supported == true) { SecNegotiateRspData.is__3_gpp_sbi_target_api_root_supported = true; SecNegotiateRspData._3_gpp_sbi_target_api_root_supported = 1; } } PlmnIdList = OpenAPI_list_create(); ogs_assert(PlmnIdList); for (i = 0; i < ogs_local_conf()->num_of_serving_plmn_id; i++) { PlmnId = ogs_sbi_build_plmn_id(&ogs_local_conf()->serving_plmn_id[i]); ogs_assert(PlmnId); OpenAPI_list_add(PlmnIdList, PlmnId); } if (PlmnIdList->count) SecNegotiateRspData.plmn_id_list = PlmnIdList; else OpenAPI_list_free(PlmnIdList); SecNegotiateRspData.supported_features = ogs_uint64_to_string(sepp_node->supported_features); ogs_assert(SecNegotiateRspData.supported_features); memset(&sendmsg, 0, sizeof(sendmsg)); sendmsg.SecNegotiateRspData = &SecNegotiateRspData; response = ogs_sbi_build_response(&sendmsg, OGS_SBI_HTTP_STATUS_OK); ogs_assert(response); ogs_assert(true == ogs_sbi_server_send_response(stream, response)); OpenAPI_list_for_each(SecNegotiateRspData.plmn_id_list, node) { PlmnId = node->data; if (PlmnId) ogs_sbi_free_plmn_id(PlmnId); } OpenAPI_list_free(SecNegotiateRspData.plmn_id_list); if (SecNegotiateRspData.supported_features) ogs_free(SecNegotiateRspData.supported_features); } static int request_handler(ogs_sbi_request_t *request, void *data) { int rv; ogs_hash_index_t *hi; ogs_sbi_client_t *client = NULL, *scp_client = NULL; ogs_sbi_stream_t *stream = data; ogs_sbi_server_t *server = NULL; ogs_sbi_request_t sepp_request; char *apiroot = NULL, *newuri = NULL; sepp_assoc_t *assoc = NULL; struct { char *target_apiroot; char *callback; char *nrf_uri; } headers = { NULL, NULL, NULL }; sepp_event_t *e = NULL; ogs_assert(request); ogs_assert(request->h.uri); ogs_assert(stream); server = ogs_sbi_server_from_stream(stream); ogs_assert(server); /* Extract HTTP Header */ for (hi = ogs_hash_first(request->http.headers); hi; hi = ogs_hash_next(hi)) { char *key = (char *)ogs_hash_this_key(hi); char *val = ogs_hash_this_val(hi); if (!key || !val) { ogs_error("No Key[%s] Value[%s]", key, val); continue; } /* * * Each header field consists of a name followed by a colon (":") * and the field value. Field names are case-insensitive. */ if (!strcasecmp(key, OGS_SBI_CUSTOM_TARGET_APIROOT)) { headers.target_apiroot = val; } } if (headers.target_apiroot) { bool rc; sepp_node_t *sepp_node = NULL; bool do_not_remove_custom_header; assoc = sepp_assoc_add(stream); if (!assoc) { ogs_error("sepp_assoc_add() failed"); return OGS_ERROR; } do_not_remove_custom_header = true; if (ogs_sbi_fqdn_in_vplmn(headers.target_apiroot) == true) { uint16_t mcc = 0, mnc = 0; if (server->interface) { ogs_error("[DROP] Peer SEPP is using " "the wrong interface[%s]", server->interface); sepp_assoc_remove(assoc); return OGS_ERROR; } mcc = ogs_plmn_id_mcc_from_fqdn(headers.target_apiroot); ogs_assert(mcc); mnc = ogs_plmn_id_mnc_from_fqdn(headers.target_apiroot); ogs_assert(mnc); /* * Different PLMN : FROM c-SEPP TO p-SEPP */ sepp_node = sepp_node_find_by_plmn_id(mcc, mnc); if (!sepp_node) { ogs_error("Cannot find SEPP Peer Node [%s:%d:%d]", headers.target_apiroot, mcc, mnc); sepp_assoc_remove(assoc); return OGS_ERROR; } client = NF_INSTANCE_CLIENT(&sepp_node->n32f); if (!client) { client = NF_INSTANCE_CLIENT(sepp_node); if (!client) { ogs_error("No Client in SEPP Peer Node [%s:%d:%d]", headers.target_apiroot, mcc, mnc); sepp_assoc_remove(assoc); return OGS_ERROR; } } /* Client ApiRoot */ apiroot = ogs_sbi_client_apiroot(client); ogs_assert(apiroot); } else { /* * Same PLMN : From p-SEPP to NF via SCP */ OpenAPI_uri_scheme_e scheme = OpenAPI_uri_scheme_NULL; char *fqdn = NULL; uint16_t fqdn_port = 0; ogs_sockaddr_t *addr = NULL, *addr6 = NULL; if (server->interface == NULL) { if (ogs_sbi_server_first_by_interface( OGS_SBI_INTERFACE_NAME_SEPP) || ogs_sbi_server_first_by_interface( OGS_SBI_INTERFACE_NAME_N32F)) { ogs_error("[DROP] Peer SEPP is using " "the wrong interface[%s]", server->interface); sepp_assoc_remove(assoc); return OGS_ERROR; } } else { if (strcmp(server->interface, OGS_SBI_INTERFACE_NAME_SEPP) == 0) { if (ogs_sbi_server_first_by_interface( OGS_SBI_INTERFACE_NAME_N32F)) { ogs_error("[DROP] Peer SEPP is using " "the wrong interface[%s]", server->interface); sepp_assoc_remove(assoc); return OGS_ERROR; } } } /* Find or Add Client Instance */ rc = ogs_sbi_getaddr_from_uri( &scheme, &fqdn, &fqdn_port, &addr, &addr6, headers.target_apiroot); if (rc == false || scheme == OpenAPI_uri_scheme_NULL) { ogs_error("Invalid Target-apiRoot [%s]", headers.target_apiroot); sepp_assoc_remove(assoc); return OGS_ERROR; } client = ogs_sbi_client_find( scheme, fqdn, fqdn_port, addr, addr6); if (!client) { client = ogs_sbi_client_add( scheme, fqdn, fqdn_port, addr, addr6); ogs_assert(client); } OGS_SBI_SETUP_CLIENT(assoc, client); ogs_free(fqdn); ogs_freeaddrinfo(addr); ogs_freeaddrinfo(addr6); /* Get SCP client */ scp_client = NF_INSTANCE_CLIENT(ogs_sbi_self()->scp_instance); /* Client ApiRoot */ if (scp_client) { apiroot = ogs_sbi_client_apiroot(scp_client); ogs_assert(apiroot); /* Switch to the SCP's client */ client = scp_client; } else { apiroot = ogs_sbi_client_apiroot(client); ogs_assert(apiroot); /* Remove Target-apiRoot */ do_not_remove_custom_header = false; } } /* Setup New URI */ newuri = ogs_msprintf("%s%s", apiroot, request->h.uri); ogs_assert(newuri); ogs_free(apiroot); /* Copy Request for SEPP request */ copy_request(&sepp_request, request, do_not_remove_custom_header); ogs_assert(sepp_request.http.headers); /* Set New URI to SEPP request */ sepp_request.h.uri = newuri; ogs_assert(sepp_request.h.uri); /* Send the HTTP Request with New URI and HTTP Headers */ if (scp_client) { rc = ogs_sbi_client_send_via_scp_or_sepp( scp_client, response_handler, &sepp_request, assoc); ogs_expect(rc == true); } else { rc = ogs_sbi_client_send_request( client, response_handler, &sepp_request, assoc); ogs_expect(rc == true); } if (rc == false) { ogs_error("ogs_sbi_send_request_to_client() failed"); ogs_sbi_http_hash_free(sepp_request.http.headers); ogs_free(sepp_request.h.uri); sepp_assoc_remove(assoc); return OGS_ERROR; } ogs_sbi_http_hash_free(sepp_request.http.headers); ogs_free(sepp_request.h.uri); return OGS_OK; } /*************************************** * Receive NOTIFICATION message from NRF ***************************************/ ogs_assert(request); ogs_assert(data); if (server->interface && strcmp(server->interface, OGS_SBI_INTERFACE_NAME_N32F) == 0) { ogs_error("[DROP] Peer SEPP is using the wrong interface[%s]", server->interface); return OGS_ERROR; } e = sepp_event_new(OGS_EVENT_SBI_SERVER); ogs_assert(e); e->h.sbi.request = request; e->h.sbi.data = data; rv = ogs_queue_push(ogs_app()->queue, e); if (rv != OGS_OK) { ogs_error("ogs_queue_push() failed:%d", (int)rv); ogs_event_free(e); return OGS_ERROR; } return OGS_OK; } static int response_handler( int status, ogs_sbi_response_t *response, void *data) { sepp_assoc_t *assoc = data; ogs_sbi_stream_t *stream = NULL; ogs_assert(assoc); stream = assoc->stream; ogs_assert(stream); if (status != OGS_OK) { ogs_log_message( status == OGS_DONE ? OGS_LOG_DEBUG : OGS_LOG_WARN, 0, "response_handler() failed [%d]", status); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL, "response_handler() failed", NULL)); sepp_assoc_remove(assoc); return OGS_ERROR; } ogs_assert(response); ogs_expect(true == ogs_sbi_server_send_response(stream, response)); sepp_assoc_remove(assoc); return OGS_OK; } static void copy_request( ogs_sbi_request_t *target, ogs_sbi_request_t *source, bool do_not_remove_custom_header) { ogs_hash_index_t *hi; ogs_assert(source); ogs_assert(target); memset(target, 0, sizeof(*target)); /* HTTP method/params/content */ target->h.method = source->h.method; target->http.params = source->http.params; target->http.content = source->http.content; target->http.content_length = source->http.content_length; /* HTTP Headers * * To remove the followings, * Scheme - https * Authority - sepp.open5gs.org */ target->http.headers = ogs_hash_make(); ogs_assert(target->http.headers); /* Extract HTTP Header */ for (hi = ogs_hash_first(source->http.headers); hi; hi = ogs_hash_next(hi)) { char *key = (char *)ogs_hash_this_key(hi); char *val = ogs_hash_this_val(hi); if (!key || !val) { ogs_error("No Key[%s] Value[%s]", key, val); continue; } /* * * Each header field consists of a name followed by a colon (":") * and the field value. Field names are case-insensitive. */ if (do_not_remove_custom_header == false && !strcasecmp(key, OGS_SBI_CUSTOM_TARGET_APIROOT)) { } else if (do_not_remove_custom_header == false && !strncasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_COMMON, strlen(OGS_SBI_CUSTOM_DISCOVERY_COMMON))) { } else if (!strcasecmp(key, OGS_SBI_SCHEME)) { } else if (!strcasecmp(key, OGS_SBI_AUTHORITY)) { } else { ogs_sbi_header_set(target->http.headers, key, val); } } }