/* * Copyright (C) 2019-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 int nf_discover_handler( int status, ogs_sbi_response_t *response, void *data); static int sepp_discover_handler( int status, ogs_sbi_response_t *response, void *data); static bool send_discover( ogs_sbi_client_t *client, ogs_sbi_client_cb_f client_cb, scp_assoc_t *assoc); static bool send_request( ogs_sbi_client_t *client, ogs_sbi_client_cb_f client_cb, ogs_sbi_request_t *request, bool do_not_remove_custom_header, scp_assoc_t *assoc); static void copy_request( ogs_sbi_request_t *target, ogs_sbi_request_t *source, bool do_not_remove_custom_header); int scp_sbi_open(void) { ogs_sbi_nf_instance_t *nf_instance = NULL, *nrf_instance = NULL; ogs_sbi_client_t *nrf_client = NULL, *next_scp = 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); /* * If the SCP is running in Model D, * it can send NFRegister/NFStatusSubscribe messages to the NRF. */ nrf_instance = ogs_sbi_self()->nrf_instance; nrf_client = NF_INSTANCE_CLIENT(ogs_sbi_self()->nrf_instance); if (nrf_client) { /* Initialize NRF NF Instance */ if (nrf_instance) ogs_sbi_nf_fsm_init(nrf_instance); } /* Check if Next-SCP's client */ if (ogs_sbi_self()->discovery_config.delegated == OGS_SBI_DISCOVERY_DELEGATED_AUTO) { next_scp = NF_INSTANCE_CLIENT(ogs_sbi_self()->scp_instance); } else if (ogs_sbi_self()->discovery_config.delegated == OGS_SBI_DISCOVERY_DELEGATED_YES) { next_scp = NF_INSTANCE_CLIENT(ogs_sbi_self()->scp_instance); ogs_assert(next_scp); } /* If the SCP has an NRF client and does not delegate to Next-SCP */ if (nrf_client && !next_scp) { /* Setup Subscription-Data */ ogs_sbi_subscription_spec_add(OpenAPI_nf_type_SEPP, NULL); ogs_sbi_subscription_spec_add(OpenAPI_nf_type_AMF, NULL); ogs_sbi_subscription_spec_add(OpenAPI_nf_type_AUSF, NULL); ogs_sbi_subscription_spec_add(OpenAPI_nf_type_BSF, NULL); ogs_sbi_subscription_spec_add(OpenAPI_nf_type_NSSF, NULL); ogs_sbi_subscription_spec_add(OpenAPI_nf_type_PCF, NULL); ogs_sbi_subscription_spec_add(OpenAPI_nf_type_SMF, NULL); ogs_sbi_subscription_spec_add(OpenAPI_nf_type_UDM, NULL); ogs_sbi_subscription_spec_add(OpenAPI_nf_type_UDR, NULL); } if (ogs_sbi_server_start_all(request_handler) != OGS_OK) return OGS_ERROR; return OGS_OK; } void scp_sbi_close(void) { ogs_sbi_client_stop_all(); ogs_sbi_server_stop_all(); } static int request_handler(ogs_sbi_request_t *request, void *data) { int rv; ogs_hash_index_t *hi; ogs_sbi_client_t *client = NULL, *nrf_client = NULL, *next_scp = NULL; ogs_sbi_client_t *sepp_client = NULL; ogs_sbi_stream_t *stream = data; OpenAPI_nf_type_e target_nf_type = OpenAPI_nf_type_NULL; OpenAPI_nf_type_e requester_nf_type = OpenAPI_nf_type_NULL; ogs_sbi_discovery_option_t *discovery_option = NULL; ogs_sbi_service_type_e service_type = OGS_SBI_SERVICE_TYPE_NULL; bool discovery_presence = false; scp_assoc_t *assoc = NULL; ogs_sbi_nf_instance_t *nf_instance = NULL; struct { char *target_apiroot; char *callback; char *nrf_uri; } headers = { NULL, NULL, NULL }; scp_event_t *e = NULL; ogs_assert(request); ogs_assert(request->h.uri); ogs_assert(stream); /* SCP Context */ assoc = scp_assoc_add(stream); if (!assoc) { ogs_error("scp_assoc_add() failed"); return OGS_ERROR; } /* Next-SCP client */ if (ogs_sbi_self()->discovery_config.delegated == OGS_SBI_DISCOVERY_DELEGATED_AUTO) { next_scp = NF_INSTANCE_CLIENT(ogs_sbi_self()->scp_instance); } else if (ogs_sbi_self()->discovery_config.delegated == OGS_SBI_DISCOVERY_DELEGATED_YES) { next_scp = NF_INSTANCE_CLIENT(ogs_sbi_self()->scp_instance); ogs_assert(next_scp); } /* NRF client */ nrf_client = NF_INSTANCE_CLIENT(ogs_sbi_self()->nrf_instance); /* Discovery Option */ discovery_option = assoc->discovery_option; ogs_assert(discovery_option); /* 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_USER_AGENT)) { if (val) requester_nf_type = OpenAPI_nf_type_FromString(val); } else if (!strcasecmp(key, OGS_SBI_CUSTOM_TARGET_APIROOT)) { headers.target_apiroot = val; } else if (!strcasecmp(key, OGS_SBI_CUSTOM_CALLBACK)) { headers.callback = val; } else if (!strcasecmp(key, OGS_SBI_CUSTOM_NRF_URI)) { headers.nrf_uri = val; } else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_TARGET_NF_TYPE)) { if (val) target_nf_type = OpenAPI_nf_type_FromString(val); } else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_REQUESTER_NF_TYPE)) { ogs_warn("Use User-Agent instead of Discovery-requester-nf-type"); } else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_TARGET_NF_INSTANCE_ID)) { ogs_sbi_discovery_option_set_target_nf_instance_id( discovery_option, val); } else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_REQUESTER_NF_INSTANCE_ID)) { ogs_sbi_discovery_option_set_requester_nf_instance_id( discovery_option, val); } else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_SERVICE_NAMES)) { if (val) ogs_sbi_discovery_option_parse_service_names( discovery_option, val); /* * So, we'll use the first item in service-names list. * * TS29.500 * 6.10 Support of Indirect Communication * 6.10.3 NF Discovery and Selection for indirect communication * with Delegated Discovery * 6.10.3.2 Conveyance of NF Discovery Factors * * If the NF service consumer includes more than one service name * in the 3gpp-Sbi-Discovery-service-names header, the service name * corresponding to the service request shall be listed * as the first service name in the header. * * NOTE 3: The SCP can assume that the service request corresponds * to the first service name in the header. */ if (discovery_option->num_of_service_names) { service_type = ogs_sbi_service_type_from_name( discovery_option->service_names[0]); } } else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_SNSSAIS)) { if (val) ogs_sbi_discovery_option_parse_snssais(discovery_option, val); } else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_GUAMI)) { if (val) ogs_sbi_discovery_option_parse_guami(discovery_option, val); } else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_DNN)) { ogs_sbi_discovery_option_set_dnn(discovery_option, val); } else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_TAI)) { if (val) ogs_sbi_discovery_option_parse_tai(discovery_option, val); } else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_TARGET_PLMN_LIST)) { if (val) discovery_option->num_of_target_plmn_list = ogs_sbi_discovery_option_parse_plmn_list( discovery_option->target_plmn_list, val); } else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_REQUESTER_PLMN_LIST)) { if (val) discovery_option->num_of_requester_plmn_list = ogs_sbi_discovery_option_parse_plmn_list( discovery_option->requester_plmn_list, val); } else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_REQUESTER_FEATURES)) { if (val) discovery_option->requester_features = ogs_uint64_from_string(val); } else { /* ':scheme' and ':authority' will be automatically filled in later */ } } /* Check if Discovery Parameter and Option */ discovery_presence = false; if (!requester_nf_type) { ogs_error("[%s] No User-Agent", request->h.uri); scp_assoc_remove(assoc); return OGS_ERROR; } if (target_nf_type || service_type) { if (!target_nf_type || !service_type) { ogs_error("[%s] No Mandatory Discovery [%d:%d]", request->h.uri, target_nf_type, service_type); scp_assoc_remove(assoc); return OGS_ERROR; } if (target_nf_type == OpenAPI_nf_type_NRF) client = NF_INSTANCE_CLIENT(ogs_sbi_self()->nrf_instance); else { if (discovery_option && discovery_option->target_nf_instance_id) { nf_instance = ogs_sbi_nf_instance_find( discovery_option->target_nf_instance_id); if (nf_instance) { client = ogs_sbi_client_find_by_service_type( nf_instance, service_type); if (!client) { ogs_error("[%s:%s] Cannot find client [%s:%s]", OpenAPI_nf_type_ToString(nf_instance->nf_type), nf_instance->id, OpenAPI_nf_type_ToString(target_nf_type), ogs_sbi_service_type_to_name(service_type)); } } } } discovery_presence = true; } /************************************** * Send REQUEST message to the Next-SCP **************************************/ if (next_scp) { if (false == send_request( next_scp, response_handler, request, true, assoc)) { ogs_error("send_request() failed"); scp_assoc_remove(assoc); return OGS_ERROR; } return OGS_OK; } /************************************ * Send REQUEST message to the CLIENT ************************************/ if (headers.target_apiroot || client) { /************************** * Check if SEPP is needed **************************/ if (headers.target_apiroot && ogs_sbi_fqdn_in_vplmn(headers.target_apiroot) == true) { /* Re-Use Custom Header(Target-apiRoot) from Target-apiRoot */ ogs_assert(!assoc->target_apiroot); assoc->target_apiroot = ogs_strdup(headers.target_apiroot); ogs_assert(assoc->target_apiroot); } else if (client && client->fqdn && ogs_sbi_fqdn_in_vplmn(client->fqdn) == true) { /* Generate Custom Header(Target-apiRoot) from Known-Client */ ogs_assert(!assoc->target_apiroot); assoc->target_apiroot = ogs_sbi_client_apiroot(client); ogs_assert(assoc->target_apiroot); } if (assoc->target_apiroot) { /* Visited Network requires SEPP */ sepp_client = NF_INSTANCE_CLIENT(ogs_sbi_self()->sepp_instance); if (!sepp_client && !nrf_client) { ogs_error("No SEPP(%p) and NRF(%p) [%s]", sepp_client, nrf_client, assoc->target_apiroot); scp_assoc_remove(assoc); return OGS_ERROR; } else if (!sepp_client) { assoc->request = request; ogs_assert(assoc->request); assoc->target_nf_type = OpenAPI_nf_type_SEPP;; ogs_assert(assoc->request); assoc->requester_nf_type = requester_nf_type; ogs_assert(assoc->request); if (false == send_discover( nrf_client, sepp_discover_handler, assoc)) { ogs_error("send_discover() failed"); scp_assoc_remove(assoc); return OGS_ERROR; } return OGS_OK; } } if (sepp_client) { /* Switch to the SEPP client */ client = sepp_client; } else if (headers.target_apiroot) { bool rc; OpenAPI_uri_scheme_e scheme = OpenAPI_uri_scheme_NULL; char *fqdn = NULL; uint16_t fqdn_port = 0; ogs_sockaddr_t *addr = NULL, *addr6 = NULL; /* 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); scp_assoc_remove(assoc); return OGS_ERROR; } client = ogs_sbi_client_find(scheme, fqdn, fqdn_port, addr, addr6); if (!client) { ogs_debug("%s: ogs_sbi_client_add()", OGS_FUNC); client = ogs_sbi_client_add( scheme, fqdn, fqdn_port, addr, addr6); if (!client) { ogs_error("%s: ogs_sbi_client_add() failed", OGS_FUNC); ogs_free(fqdn); ogs_freeaddrinfo(addr); ogs_freeaddrinfo(addr6); scp_assoc_remove(assoc); return OGS_ERROR; } } OGS_SBI_SETUP_CLIENT(assoc, client); ogs_free(fqdn); ogs_freeaddrinfo(addr); ogs_freeaddrinfo(addr6); } ogs_assert(client); if (false == send_request( client, response_handler, request, false, assoc)) { ogs_error("send_request() failed"); scp_assoc_remove(assoc); return OGS_ERROR; } return OGS_OK; } /******************************* * Send DISCOVERY message to NRF *******************************/ if (discovery_presence == true) { if (headers.nrf_uri) { char *key = NULL; char *nnrf_disc = NULL; char *nnrf_nfm = NULL; char *nnrf_oauth2 = NULL; char *tmp = NULL, *p = NULL; char *v_start = NULL, *v_end = NULL; tmp = ogs_strdup(headers.nrf_uri); ogs_assert(tmp); for (key = ogs_strtok_r(tmp, ": ", &p); key != NULL; key = ogs_strtok_r(NULL, ": ", &p)) { v_start = v_end = NULL; while (*p) { if (*p == ';') { if ((v_start && v_end) || !v_start) { p++; break; } } else if (*p == '"') { if (!v_start) v_start = p+1; else if (!v_end) v_end = p; } p++; } if (v_start && v_end) { SWITCH(key) CASE(OGS_SBI_SERVICE_NAME_NNRF_NFM) nnrf_nfm = ogs_strndup(v_start, v_end-v_start); break; CASE(OGS_SBI_SERVICE_NAME_NNRF_DISC) nnrf_disc = ogs_strndup(v_start, v_end-v_start); break; CASE(OGS_SBI_SERVICE_NAME_NNRF_OAUTH2) nnrf_oauth2 = ogs_strndup(v_start, v_end-v_start); break; DEFAULT END } } ogs_free(tmp); /* Find or Add Client Instance */ if (nnrf_disc) { bool rc; OpenAPI_uri_scheme_e scheme = OpenAPI_uri_scheme_NULL; char *fqdn = NULL; uint16_t fqdn_port = 0; ogs_sockaddr_t *addr = NULL, *addr6 = NULL; rc = ogs_sbi_getaddr_from_uri( &scheme, &fqdn, &fqdn_port, &addr, &addr6, nnrf_disc); if (rc == false || scheme == OpenAPI_uri_scheme_NULL) { ogs_error("Invalid nnrf-disc [%s]", nnrf_disc); scp_assoc_remove(assoc); return OGS_ERROR; } nrf_client = ogs_sbi_client_find( scheme, fqdn, fqdn_port, addr, addr6); if (!nrf_client) { ogs_debug("%s: ogs_sbi_client_add()", OGS_FUNC); nrf_client = ogs_sbi_client_add( scheme, fqdn, fqdn_port, addr, addr6); if (!nrf_client) { ogs_error("%s: ogs_sbi_client_add()", OGS_FUNC); ogs_free(fqdn); ogs_freeaddrinfo(addr); ogs_freeaddrinfo(addr6); scp_assoc_remove(assoc); return OGS_ERROR; } } OGS_SBI_SETUP_CLIENT(assoc, nrf_client); ogs_free(fqdn); ogs_freeaddrinfo(addr); ogs_freeaddrinfo(addr6); } if (nnrf_nfm) ogs_free(nnrf_nfm); if (nnrf_disc) ogs_free(nnrf_disc); if (nnrf_oauth2) ogs_free(nnrf_oauth2); } if (!nrf_client) { ogs_error("No NRF"); scp_assoc_remove(assoc); return OGS_ERROR; } assoc->request = request; ogs_assert(assoc->request); assoc->service_type = service_type; ogs_assert(assoc->service_type); assoc->target_nf_type = target_nf_type; ogs_assert(assoc->target_nf_type); assoc->requester_nf_type = requester_nf_type; ogs_assert(assoc->requester_nf_type); if (!discovery_option->num_of_service_names) { ogs_error("No service names"); scp_assoc_remove(assoc); return OGS_ERROR; } else if (discovery_option->num_of_service_names > 1) { /* * TS29.500 * 6.10.3 NF Discovery and Selection for indirect communication * with Delegated Discovery * 6.10.3.2 Conveyance of NF Discovery Factors * * If the NF service consumer includes more than one service name in the * 3gpp-Sbi-Discovery-service-names header, the service name corresponding * to the service request shall be listed as the first service name * in the header. * * NOTE 3: The SCP can assume that the service request corresponds * to the first service name in the header. */ int i; for (i = 1; i < discovery_option->num_of_service_names; i++) ogs_free(discovery_option->service_names[i]); discovery_option->num_of_service_names = 1; ogs_error("NOTE 3: The SCP can assume that the service request " "corresponds to the first service name in the header " "in TS29.500"); } if (false == send_discover(nrf_client, nf_discover_handler, assoc)) { ogs_error("send_discover() failed"); scp_assoc_remove(assoc); return OGS_ERROR; } return OGS_OK; } scp_assoc_remove(assoc); /*************************************** * Receive NOTIFICATION message from NRF ***************************************/ ogs_assert(request); ogs_assert(data); e = scp_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) { scp_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)); scp_assoc_remove(assoc); return OGS_ERROR; } ogs_assert(response); if (assoc->nf_service_producer) { if (assoc->nf_service_producer->id) ogs_sbi_header_set(response->http.headers, OGS_SBI_CUSTOM_PRODUCER_ID, assoc->nf_service_producer->id); else ogs_error("No NF-Instance ID"); } ogs_expect(true == ogs_sbi_server_send_response(stream, response)); scp_assoc_remove(assoc); return OGS_OK; } static int nf_discover_handler( int status, ogs_sbi_response_t *response, void *data) { int rv; char *strerror = NULL; ogs_sbi_message_t message; scp_assoc_t *assoc = data; ogs_sbi_stream_t *stream = NULL; ogs_sbi_request_t *request = NULL; ogs_sbi_service_type_e service_type = OGS_SBI_SERVICE_TYPE_NULL; OpenAPI_nf_type_e target_nf_type = OpenAPI_nf_type_NULL; OpenAPI_nf_type_e requester_nf_type = OpenAPI_nf_type_NULL; ogs_sbi_discovery_option_t *discovery_option = NULL; ogs_sbi_nf_instance_t *nf_instance = NULL; ogs_sbi_client_t *client = NULL; ogs_sbi_client_t *sepp_client = NULL; ogs_assert(assoc); stream = assoc->stream; ogs_assert(stream); request = assoc->request; ogs_assert(request); service_type = assoc->service_type; ogs_assert(service_type); target_nf_type = assoc->target_nf_type; ogs_assert(target_nf_type); requester_nf_type = assoc->requester_nf_type; ogs_assert(requester_nf_type); discovery_option = assoc->discovery_option; ogs_assert(discovery_option); if (status != OGS_OK) { ogs_log_message( status == OGS_DONE ? OGS_LOG_DEBUG : OGS_LOG_WARN, 0, "nf_discover_handler() failed [%d]", status); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL, "nf_discover_handler() failed", NULL)); scp_assoc_remove(assoc); return OGS_ERROR; } ogs_assert(response); rv = ogs_sbi_parse_response(&message, response); if (rv != OGS_OK) { strerror = ogs_msprintf("cannot parse HTTP response"); goto cleanup; } if (message.res_status != OGS_SBI_HTTP_STATUS_OK) { strerror = ogs_msprintf("NF-Discover failed [%d]", message.res_status); goto cleanup; } if (!message.SearchResult) { strerror = ogs_msprintf("No SearchResult"); goto cleanup; } ogs_nnrf_disc_handle_nf_discover_search_result(message.SearchResult); nf_instance = ogs_sbi_nf_instance_find_by_discovery_param( target_nf_type, requester_nf_type, discovery_option); if (!nf_instance) { strerror = ogs_msprintf("(NF discover) No NF-Instance [%s:%s]", ogs_sbi_service_type_to_name(service_type), OpenAPI_nf_type_ToString(requester_nf_type)); goto cleanup; } /* Store NF Service Producer */ assoc->nf_service_producer = nf_instance; ogs_assert(assoc->nf_service_producer); client = ogs_sbi_client_find_by_service_type(nf_instance, service_type); if (!client) { strerror = ogs_msprintf("(NF discover) No client [%s:%s]", ogs_sbi_service_type_to_name(service_type), OpenAPI_nf_type_ToString(requester_nf_type)); goto cleanup; } /************************** * Check if SEPP is needed **************************/ if (client->fqdn && ogs_sbi_fqdn_in_vplmn(client->fqdn) == true) { /* Visited Network requires SEPP */ sepp_client = NF_INSTANCE_CLIENT(ogs_sbi_self()->sepp_instance); if (!sepp_client) { ogs_error("No SEPP [%s]", client->fqdn); strerror = ogs_msprintf("No SEPP [%s]", client->fqdn); goto cleanup; } /* Generate Custom Header(Target-apiRoot) from Known-Client */ ogs_assert(!assoc->target_apiroot); assoc->target_apiroot = ogs_sbi_client_apiroot(client); ogs_assert(assoc->target_apiroot); client = sepp_client; } if (false == send_request( client, response_handler, request, false, assoc)) { strerror = ogs_msprintf("send_request() failed"); goto cleanup; } ogs_sbi_response_free(response); ogs_sbi_message_free(&message); return OGS_OK; cleanup: 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); scp_assoc_remove(assoc); ogs_sbi_response_free(response); ogs_sbi_message_free(&message); return OGS_ERROR; } static int sepp_discover_handler( int status, ogs_sbi_response_t *response, void *data) { int rv; char *strerror = NULL; ogs_sbi_message_t message; scp_assoc_t *assoc = data; ogs_sbi_stream_t *stream = NULL; ogs_sbi_request_t *request = NULL; ogs_sbi_client_t *sepp_client = NULL; ogs_assert(assoc); ogs_assert(assoc->target_apiroot); stream = assoc->stream; ogs_assert(stream); request = assoc->request; ogs_assert(request); if (status != OGS_OK) { ogs_log_message( status == OGS_DONE ? OGS_LOG_DEBUG : OGS_LOG_WARN, 0, "sepp_discover_handler() failed [%d]", status); ogs_assert(true == ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL, "sepp_discover_handler() failed", NULL)); scp_assoc_remove(assoc); return OGS_ERROR; } ogs_assert(response); rv = ogs_sbi_parse_response(&message, response); if (rv != OGS_OK) { strerror = ogs_msprintf("cannot parse HTTP response"); goto cleanup; } if (message.res_status != OGS_SBI_HTTP_STATUS_OK) { strerror = ogs_msprintf("NF-Discover failed [%d]", message.res_status); goto cleanup; } if (!message.SearchResult) { strerror = ogs_msprintf("No SearchResult"); goto cleanup; } ogs_nnrf_disc_handle_nf_discover_search_result(message.SearchResult); /***************************** * Check if SEPP is discovered *****************************/ sepp_client = NF_INSTANCE_CLIENT(ogs_sbi_self()->sepp_instance); if (!sepp_client) { strerror = ogs_msprintf("No SEPP"); goto cleanup; } if (false == send_request( sepp_client, response_handler, request, false, assoc)) { strerror = ogs_msprintf("send_request() failed"); goto cleanup; } ogs_sbi_response_free(response); ogs_sbi_message_free(&message); return OGS_OK; cleanup: 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); scp_assoc_remove(assoc); ogs_sbi_response_free(response); ogs_sbi_message_free(&message); return OGS_ERROR; } static bool send_discover( ogs_sbi_client_t *client, ogs_sbi_client_cb_f client_cb, scp_assoc_t *assoc) { bool rc; ogs_sbi_request_t *request = NULL; ogs_assert(client); ogs_assert(assoc); request = ogs_nnrf_disc_build_discover( assoc->target_nf_type, assoc->requester_nf_type, assoc->target_nf_type != OpenAPI_nf_type_SEPP ? assoc->discovery_option : NULL); if (!request) { ogs_error("ogs_nnrf_disc_build_discover() failed"); return false; } rc = ogs_sbi_client_send_request(client, client_cb, request, assoc); ogs_expect(rc == true); ogs_sbi_request_free(request); return rc; } static bool send_request( ogs_sbi_client_t *client, ogs_sbi_client_cb_f client_cb, ogs_sbi_request_t *request, bool do_not_remove_custom_header, scp_assoc_t *assoc) { bool rc; ogs_sbi_request_t scp_request; char *uri_apiroot = NULL; ogs_assert(client); ogs_assert(request); ogs_assert(assoc); /* Copy Request for sending SCP */ copy_request(&scp_request, request, do_not_remove_custom_header); ogs_assert(scp_request.http.headers); /* Added Custom Header(Target-apiRoot) */ if (assoc->target_apiroot) ogs_sbi_header_set(scp_request.http.headers, OGS_SBI_CUSTOM_TARGET_APIROOT, assoc->target_apiroot); /* Client ApiRoot */ uri_apiroot = ogs_sbi_client_apiroot(client); ogs_assert(uri_apiroot); /* Setup New URI */ scp_request.h.uri = ogs_msprintf("%s%s", uri_apiroot, request->h.uri); ogs_assert(scp_request.h.uri); /* Send the HTTP Request with New URI and HTTP Headers */ rc = ogs_sbi_client_send_request(client, client_cb, &scp_request, assoc); ogs_expect(rc == true); ogs_sbi_http_hash_free(scp_request.http.headers); ogs_free(scp_request.h.uri); ogs_free(uri_apiroot); return rc; } 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 - scp.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); } } }