open5gs/src/scp/sbi-path.c

1015 lines
32 KiB
C

/*
* Copyright (C) 2019-2023 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#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;
}
/*
* <RFC 2616>
* 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_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;
}
/*
* <RFC 2616>
* 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);
}
}
}