open5gs/lib/sbi/path.c

1043 lines
34 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 "ogs-sbi.h"
static int sepp_discover_handler(
int status, ogs_sbi_response_t *response, void *data);
static void build_default_discovery_parameter(
ogs_sbi_request_t *request,
ogs_sbi_service_type_e service_type,
ogs_sbi_discovery_option_t *discovery_option);
int ogs_sbi_server_handler(ogs_sbi_request_t *request, void *data)
{
ogs_event_t *e = NULL;
int rv;
ogs_assert(request);
ogs_assert(data);
e = ogs_event_new(OGS_EVENT_SBI_SERVER);
ogs_assert(e);
e->sbi.request = request;
e->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_sbi_request_free(request);
ogs_event_free(e);
return OGS_ERROR;
}
return OGS_OK;
}
int ogs_sbi_client_handler(
int status, ogs_sbi_response_t *response, void *data)
{
ogs_event_t *e = NULL;
int rv;
if (status != OGS_OK) {
ogs_log_message(
status == OGS_DONE ? OGS_LOG_DEBUG : OGS_LOG_WARN, 0,
"ogs_sbi_client_handler() failed [%d]", status);
return OGS_ERROR;
}
ogs_assert(response);
e = ogs_event_new(OGS_EVENT_SBI_CLIENT);
ogs_assert(e);
e->sbi.response = response;
e->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_sbi_response_free(response);
ogs_event_free(e);
return OGS_ERROR;
}
return OGS_OK;
}
static int client_discover_cb(
int status, ogs_sbi_response_t *response, void *data)
{
int rv;
ogs_event_t *e = NULL;
ogs_sbi_xact_t *xact = NULL;
ogs_sbi_object_t *sbi_object = NULL;
ogs_sbi_service_type_e service_type = OGS_SBI_SERVICE_TYPE_NULL;
ogs_sbi_discovery_option_t *discovery_option = NULL;
OpenAPI_nf_type_e target_nf_type = OpenAPI_nf_type_NULL;
OpenAPI_nf_type_e requester_nf_type = OpenAPI_nf_type_NULL;
ogs_hash_index_t *hi = NULL;
char *producer_id = NULL;
xact = data;
ogs_assert(xact);
xact = ogs_sbi_xact_cycle(xact);
if (!xact) {
ogs_error("SBI transaction has already been removed");
if (response)
ogs_sbi_response_free(response);
return OGS_ERROR;
}
sbi_object = xact->sbi_object;
ogs_assert(sbi_object);
service_type = xact->service_type;
ogs_assert(service_type);
target_nf_type = ogs_sbi_service_type_to_nf_type(service_type);
ogs_assert(target_nf_type);
requester_nf_type = xact->requester_nf_type;
ogs_assert(requester_nf_type);
discovery_option = xact->discovery_option;
if (status != OGS_OK) {
ogs_log_message(
status == OGS_DONE ? OGS_LOG_DEBUG : OGS_LOG_WARN, 0,
"ogs_sbi_client_handler() failed [%d]", status);
ogs_sbi_xact_remove(xact);
return OGS_ERROR;
}
ogs_assert(response);
/* Check if 3gpp-Sbi-Producer-Id in HTTP2 Header */
for (hi = ogs_hash_first(response->http.headers);
hi; hi = ogs_hash_next(hi)) {
if (!ogs_strcasecmp(ogs_hash_this_key(hi), OGS_SBI_CUSTOM_PRODUCER_ID)) {
producer_id = ogs_hash_this_val(hi);
break;
}
}
/* Added newly discovered NF Instance */
if (producer_id) {
ogs_sbi_nf_instance_t *nf_instance =
ogs_sbi_nf_instance_find(producer_id);
if (!nf_instance) {
nf_instance = ogs_sbi_nf_instance_add();
ogs_assert(nf_instance);
ogs_sbi_nf_instance_set_id(nf_instance, producer_id);
ogs_sbi_nf_instance_set_type(nf_instance, target_nf_type);
switch (target_nf_type) {
case OpenAPI_nf_type_SMF:
if (discovery_option &&
discovery_option->num_of_snssais && discovery_option->dnn &&
discovery_option->tai_presence == true) {
/*
* If we assume that SMF is executed first and then AMF is executed,
* AMF will not have SMF information, so it needs to discover SMF
* through NFDiscovery instead of the subscription notification.
*
* Let's assume that in smfInfo, TAC is set to 1 and 2, and two SMFs are
* executed. In this case, TAI will be added to the discovery option and
* will be performed during the NFDiscovery process.
*
* If the first SMF is discovered with TAC 1 in conjunction with SCP,
* AMF will remember this SMF through Producer-Id. However, if the second
* SMF with TAC 2 is discovered, the previously discovered SMF with TAC 1
* will be selected.
*
* Therefore, to avoid such a situation, we reflect the contents of
* the discovery option in NFProfile. For SMF, we record the s_nssai, dnn,
* and tai information in the ogs_sbi_smf_info_t structure, which is created
* as ogs_sbi_nf_info_t. Then, when we try to find the second SMF
* with TAC 2, we compare these values in the amf_sbi_select_nf() function,
* allowing us to discover a new SMF.
*/
ogs_sbi_nf_info_t *nf_info = NULL;
ogs_sbi_smf_info_t *smf_info = NULL;
nf_info = ogs_sbi_nf_info_add(
&nf_instance->nf_info_list, OpenAPI_nf_type_SMF);
ogs_assert(nf_info);
smf_info = &nf_info->smf;
ogs_assert(smf_info);
smf_info->slice[0].dnn[0] =
ogs_strdup(discovery_option->dnn);
ogs_assert(smf_info->slice[0].dnn[0]);
smf_info->slice[0].num_of_dnn++;
memcpy(&smf_info->slice[0].s_nssai,
&discovery_option->snssais[0],
sizeof(ogs_s_nssai_t));
smf_info->num_of_slice++;
memcpy(&smf_info->nr_tai[0],
&discovery_option->tai, sizeof(ogs_5gs_tai_t));
smf_info->num_of_nr_tai++;
}
break;
default:
break;
}
ogs_sbi_nf_fsm_init(nf_instance);
ogs_info("[%s] (SCP-discover) NF registered [%s:%d]",
nf_instance->nf_type ?
OpenAPI_nf_type_ToString(nf_instance->nf_type) : "NULL",
nf_instance->id, nf_instance->reference_count);
} else {
ogs_warn("[%s] (SCP-discover) NF has already been added [%s:%d]",
nf_instance->nf_type ?
OpenAPI_nf_type_ToString(nf_instance->nf_type) : "NULL",
nf_instance->id, nf_instance->reference_count);
ogs_assert(OGS_FSM_STATE(&nf_instance->sm));
ogs_sbi_nf_fsm_tran(nf_instance, ogs_sbi_nf_state_registered);
}
OGS_SBI_SETUP_NF_INSTANCE(
sbi_object->service_type_array[service_type], nf_instance);
}
e = ogs_event_new(OGS_EVENT_SBI_CLIENT);
ogs_assert(e);
e->sbi.response = response;
e->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_sbi_response_free(response);
ogs_event_free(e);
return OGS_ERROR;
}
return OGS_OK;
}
int ogs_sbi_discover_and_send(ogs_sbi_xact_t *xact)
{
bool rc;
ogs_sbi_client_t *client = NULL, *scp_client = NULL;
ogs_sbi_nf_instance_t *nf_instance = NULL;
ogs_sbi_object_t *sbi_object = NULL;
ogs_sbi_service_type_e service_type = OGS_SBI_SERVICE_TYPE_NULL;
ogs_sbi_discovery_option_t *discovery_option = NULL;
ogs_sbi_request_t *request = NULL;
OpenAPI_nf_type_e target_nf_type = OpenAPI_nf_type_NULL;
OpenAPI_nf_type_e requester_nf_type = OpenAPI_nf_type_NULL;
char *apiroot = NULL;
sbi_object = xact->sbi_object;
ogs_assert(sbi_object);
service_type = xact->service_type;
ogs_assert(service_type);
target_nf_type = ogs_sbi_service_type_to_nf_type(service_type);
ogs_assert(target_nf_type);
requester_nf_type = xact->requester_nf_type;
ogs_assert(requester_nf_type);
request = xact->request;
ogs_assert(request);
discovery_option = xact->discovery_option;
/* SCP Availability */
if (ogs_sbi_self()->discovery_config.delegated ==
OGS_SBI_DISCOVERY_DELEGATED_AUTO) {
scp_client = NF_INSTANCE_CLIENT(ogs_sbi_self()->scp_instance);
} else if (ogs_sbi_self()->discovery_config.delegated ==
OGS_SBI_DISCOVERY_DELEGATED_YES) {
scp_client = NF_INSTANCE_CLIENT(ogs_sbi_self()->scp_instance);
ogs_assert(scp_client);
}
/* Target NF-Instance */
nf_instance = sbi_object->service_type_array[service_type].nf_instance;
if (!nf_instance) {
nf_instance = ogs_sbi_nf_instance_find_by_discovery_param(
target_nf_type, requester_nf_type, discovery_option);
if (nf_instance)
OGS_SBI_SETUP_NF_INSTANCE(
sbi_object->service_type_array[service_type], nf_instance);
}
/* Target Client */
if (request->h.uri == NULL) {
if (nf_instance) {
client = ogs_sbi_client_find_by_service_name(nf_instance,
request->h.service.name, request->h.api.version);
}
} else {
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, request->h.uri);
if (rc == false || scheme == OpenAPI_uri_scheme_NULL) {
ogs_error("Invalid URL [%s]", request->h.uri);
return OGS_ERROR;
}
client = ogs_sbi_client_find(scheme, fqdn, fqdn_port, addr, addr6);
ogs_free(fqdn);
ogs_freeaddrinfo(addr);
ogs_freeaddrinfo(addr6);
}
if (scp_client) {
/*************************
* INDIRECT COMMUNICATION
*************************/
build_default_discovery_parameter(
request, service_type, discovery_option);
if (client) {
/*
* If `client` instance is available,
* 3gpp-Sbi-Target-apiRoot is added to HTTP header.
*/
apiroot = ogs_sbi_client_apiroot(client);
ogs_assert(apiroot);
ogs_sbi_header_set(request->http.headers,
OGS_SBI_CUSTOM_TARGET_APIROOT, apiroot);
ogs_free(apiroot);
rc = ogs_sbi_client_send_via_scp_or_sepp(
scp_client, ogs_sbi_client_handler, request, xact);
ogs_expect(rc == true);
return (rc == true) ? OGS_OK : OGS_ERROR;
} else {
/*
* If no `client` instance,
*
* Discovery-*** is added to HTTP header.
*/
if (discovery_option &&
discovery_option->target_nf_instance_id) {
ogs_sbi_header_set(request->http.headers,
OGS_SBI_CUSTOM_DISCOVERY_TARGET_NF_INSTANCE_ID,
discovery_option->target_nf_instance_id);
} else if (nf_instance && nf_instance->id) {
ogs_sbi_header_set(request->http.headers,
OGS_SBI_CUSTOM_DISCOVERY_TARGET_NF_INSTANCE_ID,
nf_instance->id);
}
if (discovery_option && discovery_option->num_of_snssais) {
bool rc = false;
char *v = ogs_sbi_discovery_option_build_snssais(
discovery_option);
ogs_expect(v);
if (v) {
char *encoded = ogs_sbi_url_encode(v);
ogs_expect(encoded);
if (encoded) {
/*
* In http.params, the CURL library automatically encodes the URL.
* http.headers implements open5gs to directly encode URLs.
*
* Since it is http.headers,
* we need to encode `v` using ogs_sbi_url_encode();
*/
ogs_sbi_header_set(request->http.headers,
OGS_SBI_CUSTOM_DISCOVERY_SNSSAIS, encoded);
ogs_free(encoded);
rc = true;
}
ogs_free(v);
}
if (rc == false)
ogs_error("build failed: snssais(%d)[SST:%d SD:0x%x]",
discovery_option->num_of_snssais,
discovery_option->snssais[0].sst,
discovery_option->snssais[0].sd.v);
}
if (discovery_option && discovery_option->dnn) {
ogs_sbi_header_set(request->http.headers,
OGS_SBI_CUSTOM_DISCOVERY_DNN, discovery_option->dnn);
}
if (discovery_option && discovery_option->tai_presence) {
bool rc = false;
char *v = ogs_sbi_discovery_option_build_tai(discovery_option);
ogs_expect(v);
if (v) {
char *encoded = ogs_sbi_url_encode(v);
ogs_expect(encoded);
if (encoded) {
ogs_sbi_header_set(request->http.headers,
OGS_SBI_CUSTOM_DISCOVERY_TAI, encoded);
ogs_free(encoded);
rc = true;
}
ogs_free(v);
}
if (rc == false)
ogs_error("build failed: tai[PLMN_ID:%06x,TAC:%d]",
ogs_plmn_id_hexdump(
&discovery_option->tai.plmn_id),
discovery_option->tai.tac.v);
}
if (discovery_option && discovery_option->target_guami) {
bool rc = false;
char *v = ogs_sbi_discovery_option_build_guami(discovery_option);
ogs_expect(v);
if (v) {
char *encoded = ogs_sbi_url_encode(v);
ogs_expect(encoded);
if (encoded) {
ogs_sbi_header_set(request->http.headers,
OGS_SBI_CUSTOM_DISCOVERY_GUAMI, encoded);
ogs_free(encoded);
rc = true;
}
ogs_free(v);
}
if (rc == false)
ogs_error("build failed: guami[PLMN_ID:%06x,AMF_ID:%x]",
ogs_plmn_id_hexdump(
&discovery_option->target_guami->plmn_id),
ogs_amf_id_hexdump(
&discovery_option->target_guami->amf_id));
}
if (discovery_option &&
discovery_option->requester_features) {
char *v = ogs_uint64_to_string(
discovery_option->requester_features);
if (!v) {
ogs_error("ogs_uint64_to_string[0x%llx] failed",
(long long)discovery_option->requester_features);
return OGS_ERROR;
}
ogs_sbi_header_set(request->http.headers,
OGS_SBI_CUSTOM_DISCOVERY_REQUESTER_FEATURES, v);
ogs_free(v);
}
rc = ogs_sbi_client_send_via_scp_or_sepp(
scp_client, client_discover_cb, request, xact);
ogs_expect(rc == true);
return (rc == true) ? OGS_OK : OGS_ERROR;
}
} else if (client) {
/***********************
* DIRECT COMMUNICATION
***********************/
/* If `client` instance is available, use direct communication */
rc = ogs_sbi_send_request_to_client(
client, ogs_sbi_client_handler, request, xact);
ogs_expect(rc == true);
return (rc == true) ? OGS_OK : OGS_ERROR;
} else {
/**********************************************
* No SCP and Client, Use NRF for NF-Discovery
**********************************************/
return ogs_sbi_discover_only(xact);
}
return OGS_OK;
}
int ogs_sbi_discover_only(ogs_sbi_xact_t *xact)
{
ogs_sbi_nf_instance_t *nf_instance = NULL;
ogs_sbi_object_t *sbi_object = NULL;
ogs_sbi_service_type_e service_type = OGS_SBI_SERVICE_TYPE_NULL;
ogs_sbi_discovery_option_t *discovery_option = NULL;
OpenAPI_nf_type_e target_nf_type = OpenAPI_nf_type_NULL;
OpenAPI_nf_type_e requester_nf_type = OpenAPI_nf_type_NULL;
sbi_object = xact->sbi_object;
ogs_assert(sbi_object);
service_type = xact->service_type;
ogs_assert(service_type);
target_nf_type = ogs_sbi_service_type_to_nf_type(service_type);
ogs_assert(target_nf_type);
requester_nf_type = xact->requester_nf_type;
ogs_assert(requester_nf_type);
discovery_option = xact->discovery_option;
/* NRF NF-Instance */
nf_instance = ogs_sbi_self()->nrf_instance;
if (nf_instance) {
bool rc;
ogs_sbi_client_t *client = NULL;
ogs_sbi_request_t *request = NULL;
ogs_warn("Try to discover [%s]",
ogs_sbi_service_type_to_name(service_type));
client = NF_INSTANCE_CLIENT(nf_instance);
if (!client) {
ogs_error("No Client");
return OGS_NOTFOUND;
}
request = ogs_nnrf_disc_build_discover(
target_nf_type, requester_nf_type, discovery_option);
if (!request) {
ogs_error("ogs_nnrf_disc_build_discover() failed");
return OGS_ERROR;
}
rc = ogs_sbi_client_send_request(
client, ogs_sbi_client_handler, request, xact);
ogs_expect(rc == true);
ogs_sbi_request_free(request);
return (rc == true) ? OGS_OK : OGS_ERROR;
}
ogs_error("Cannot discover [%s]",
ogs_sbi_service_type_to_name(service_type));
return OGS_NOTFOUND;
}
bool ogs_sbi_send_request_to_nf_instance(
ogs_sbi_nf_instance_t *nf_instance, ogs_sbi_xact_t *xact)
{
bool rc;
ogs_sbi_request_t *request = NULL;
ogs_sbi_client_t *client = NULL;
ogs_sbi_object_t *sbi_object = NULL;
ogs_assert(xact);
sbi_object = xact->sbi_object;
ogs_assert(sbi_object);
request = xact->request;
ogs_assert(request);
ogs_assert(nf_instance);
if (request->h.uri == NULL) {
client = ogs_sbi_client_find_by_service_name(nf_instance,
request->h.service.name, request->h.api.version);
if (!client) {
ogs_error("[%s:%s] Cannot find client [%s:%s]",
OpenAPI_nf_type_ToString(nf_instance->nf_type),
nf_instance->id,
request->h.service.name, request->h.api.version);
ogs_sbi_xact_remove(xact);
return false;
}
} else {
/*********************************************************
*
* DEPRECATED
*
********************************************************/
ogs_fatal("[%s] %s", request->h.method, request->h.uri);
ogs_assert_if_reached();
#if 0
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;
char buf[OGS_ADDRSTRLEN];
rc = ogs_sbi_getaddr_from_uri(
&scheme, &fqdn, &fqdn_port, &addr, &addr6, request->h.uri);
if (rc == false || scheme == OpenAPI_uri_scheme_NULL) {
ogs_error("[%s:%s] Invalid URL [%s]",
OpenAPI_nf_type_ToString(nf_instance->nf_type),
nf_instance->id, request->h.uri);
return false;
}
client = ogs_sbi_client_find(scheme, fqdn, fqdn_port, addr, addr6);
if (!client) {
ogs_error("[%s:%s] Cannot find client [%s:%d]",
OpenAPI_nf_type_ToString(nf_instance->nf_type),
nf_instance->id,
OGS_ADDR(addr, buf), OGS_PORT(addr));
ogs_free(fqdn);
ogs_freeaddrinfo(addr);
ogs_freeaddrinfo(addr6);
return false;
}
ogs_free(fqdn);
ogs_freeaddrinfo(addr);
ogs_freeaddrinfo(addr6);
#endif
}
if (client->fqdn && ogs_sbi_fqdn_in_vplmn(client->fqdn) == true) {
ogs_sbi_client_t *sepp_client = NULL, *nrf_client = NULL;
/* Visited Network requires SEPP */
sepp_client = NF_INSTANCE_CLIENT(ogs_sbi_self()->sepp_instance);
nrf_client = NF_INSTANCE_CLIENT(ogs_sbi_self()->nrf_instance);
if (!sepp_client && !nrf_client) {
ogs_error("No SEPP(%p) and NRF(%p) [%s]",
sepp_client, nrf_client, client->fqdn);
ogs_sbi_xact_remove(xact);
return false;
} else if (!sepp_client) {
ogs_sbi_request_t *nrf_request = NULL;
xact->target_apiroot = ogs_sbi_client_apiroot(client);
if (!xact->target_apiroot) {
ogs_error("ogs_strdup(xact->target_apiroot) failed");
ogs_sbi_xact_remove(xact);
return false;
}
nrf_request = ogs_nnrf_disc_build_discover(
OpenAPI_nf_type_SEPP, xact->requester_nf_type, NULL);
if (!nrf_request) {
ogs_error("ogs_nnrf_disc_build_discover() failed");
ogs_sbi_xact_remove(xact);
return false;
}
rc = ogs_sbi_client_send_request(
nrf_client, sepp_discover_handler, nrf_request, xact);
if (rc == false) {
ogs_error("ogs_sbi_client_send_request() failed");
ogs_sbi_xact_remove(xact);
}
ogs_sbi_request_free(nrf_request);
return rc;
}
}
rc = ogs_sbi_send_request_to_client(
client, ogs_sbi_client_handler, request, xact);
if (rc == false) {
ogs_error("ogs_sbi_send_request_to_client() failed");
ogs_sbi_xact_remove(xact);
}
return rc;
}
bool ogs_sbi_send_request_to_client(
ogs_sbi_client_t *client, ogs_sbi_client_cb_f client_cb,
ogs_sbi_request_t *request, void *data)
{
bool rc;
ogs_sbi_client_t *scp_client = NULL, *sepp_client = NULL;
ogs_sbi_client_t *scp_or_sepp = NULL;
char *apiroot = NULL;
/*
* If the HTTP2 Server's EndPoint is known,
* 3gpp-Sbi-Target-apiRoot should always be included in the HTTP2 Request.
*/
ogs_assert(client);
ogs_assert(request);
scp_client = NF_INSTANCE_CLIENT(ogs_sbi_self()->scp_instance);
sepp_client = NF_INSTANCE_CLIENT(ogs_sbi_self()->sepp_instance);
if (scp_client && scp_client != client) {
/*************************
* INDIRECT COMMUNICATION
*************************/
scp_or_sepp = scp_client;
} else if (client->fqdn && ogs_sbi_fqdn_in_vplmn(client->fqdn) == true) {
/***************************
* SEPP for Visited Network
***************************/
if (sepp_client && sepp_client != client) {
scp_or_sepp = sepp_client;
} else {
ogs_error("No SEPP [%s]", client->fqdn);
return false;
}
}
if (scp_or_sepp) {
/* Added 3gpp-Sbi-Target-apiRoot to HTTP header */
apiroot = ogs_sbi_client_apiroot(client);
ogs_assert(apiroot);
ogs_sbi_header_set(request->http.headers,
OGS_SBI_CUSTOM_TARGET_APIROOT, apiroot);
ogs_free(apiroot);
rc = ogs_sbi_client_send_via_scp_or_sepp(
scp_or_sepp, client_cb, request, data);
ogs_expect(rc == true);
} else {
/***********************
* DIRECT COMMUNICATION
***********************/
/* Direct communication since `client' instance is always available */
rc = ogs_sbi_client_send_request(
client, client_cb, request, data);
ogs_expect(rc == true);
}
return rc;
}
bool ogs_sbi_send_notification_request(
ogs_sbi_service_type_e service_type,
ogs_sbi_discovery_option_t *discovery_option,
ogs_sbi_request_t *request, void *data)
{
bool rc;
ogs_sbi_client_t *client = NULL, *scp_client = NULL;
OpenAPI_nf_type_e target_nf_type = OpenAPI_nf_type_NULL;
ogs_assert(service_type);
target_nf_type = ogs_sbi_service_type_to_nf_type(service_type);
ogs_assert(target_nf_type);
ogs_assert(request);
scp_client = NF_INSTANCE_CLIENT(ogs_sbi_self()->scp_instance);
if (target_nf_type == OpenAPI_nf_type_NRF)
client = NF_INSTANCE_CLIENT(ogs_sbi_self()->nrf_instance);
else {
ogs_fatal("Not implemented[%s]",
ogs_sbi_service_type_to_name(service_type));
ogs_assert_if_reached();
}
if (scp_client) {
/*************************
* INDIRECT COMMUNICATION
*************************/
build_default_discovery_parameter(
request, service_type, discovery_option);
rc = ogs_sbi_client_send_via_scp_or_sepp(
scp_client, ogs_sbi_client_handler, request, data);
ogs_expect(rc == true);
} else if (client) {
/***********************
* DIRECT COMMUNICATION
***********************/
/* NRF is available */
rc = ogs_sbi_client_send_request(
client, ogs_sbi_client_handler, request, data);
ogs_expect(rc == true);
} else {
ogs_fatal("[%s:%s] Cannot send request [%s:%s:%s]",
client ? "CLIENT" : "No-CLIENT",
scp_client ? "SCP" : "No-SCP",
ogs_sbi_service_type_to_name(service_type),
request->h.service.name, request->h.api.version);
rc = false;
ogs_assert_if_reached();
}
return true;
}
bool ogs_sbi_send_response(ogs_sbi_stream_t *stream, int status)
{
ogs_sbi_message_t sendmsg;
ogs_sbi_response_t *response = NULL;
ogs_assert(stream);
memset(&sendmsg, 0, sizeof(sendmsg));
response = ogs_sbi_build_response(&sendmsg, status);
if (!response) {
ogs_error("ogs_sbi_build_response() failed");
return false;
}
return ogs_sbi_server_send_response(stream, response);
}
static int sepp_discover_handler(
int status, ogs_sbi_response_t *response, void *data)
{
int rv;
char *strerror = NULL;
ogs_sbi_message_t message;
ogs_sbi_xact_t *xact = data;
ogs_sbi_request_t *request = NULL;
ogs_sbi_client_t *scp_client = NULL, *sepp_client = NULL;
ogs_assert(xact);
request = xact->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_sbi_xact_remove(xact);
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;
}
/* Added 3gpp-Sbi-Target-apiRoot to HTTP header */
ogs_sbi_header_set(request->http.headers,
OGS_SBI_CUSTOM_TARGET_APIROOT, xact->target_apiroot);
/**********************************************************************
* SCP should be checked considering 'discovery.delegated:no' situation
**********************************************************************/
scp_client = NF_INSTANCE_CLIENT(ogs_sbi_self()->scp_instance);
if (false == ogs_sbi_client_send_via_scp_or_sepp(
scp_client ? scp_client : sepp_client,
ogs_sbi_client_handler, request, xact)) {
strerror = ogs_msprintf("ogs_sbi_client_send_via_scp_or_sepp() 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_free(strerror);
ogs_sbi_xact_remove(xact);
ogs_sbi_response_free(response);
ogs_sbi_message_free(&message);
return OGS_ERROR;
}
static void build_default_discovery_parameter(
ogs_sbi_request_t *request,
ogs_sbi_service_type_e service_type,
ogs_sbi_discovery_option_t *discovery_option)
{
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 *local_discovery_option = NULL;
ogs_assert(service_type);
target_nf_type = ogs_sbi_service_type_to_nf_type(service_type);
ogs_assert(target_nf_type);
requester_nf_type = NF_INSTANCE_TYPE(ogs_sbi_self()->nf_instance);
ogs_assert(requester_nf_type);
/*
* Insert one service-name in the discovery option
* in the function below.
*
* - ogs_sbi_xact_add()
* - ogs_sbi_send_notification_request()
*/
if (!discovery_option) {
local_discovery_option = ogs_sbi_discovery_option_new();
ogs_assert(local_discovery_option);
discovery_option = local_discovery_option;
}
if (!discovery_option->num_of_service_names) {
ogs_sbi_discovery_option_add_service_names(
discovery_option,
(char *)ogs_sbi_service_type_to_name(service_type));
}
ogs_sbi_header_set(request->http.headers,
OGS_SBI_CUSTOM_DISCOVERY_TARGET_NF_TYPE,
OpenAPI_nf_type_ToString(target_nf_type));
/* Instead of Discovery-requester-nf-type,
* Open5GS uses User-Agent for requester-nf-type */
if (discovery_option) {
if (discovery_option->requester_nf_instance_id) {
ogs_sbi_header_set(request->http.headers,
OGS_SBI_CUSTOM_DISCOVERY_REQUESTER_NF_INSTANCE_ID,
discovery_option->requester_nf_instance_id);
}
if (ogs_sbi_self()->discovery_config.
no_service_names == false &&
discovery_option->num_of_service_names) {
bool rc = false;
/* send array items separated by a comma */
char *v = ogs_sbi_discovery_option_build_service_names(
discovery_option);
ogs_expect(v);
if (v) {
char *encoded = ogs_sbi_url_encode(v);
ogs_expect(encoded);
if (encoded) {
/*
* In http.params, the CURL library automatically encodes the URL.
* http.headers implements open5gs to directly encode URLs.
*
* Since it is http.headers,
* we need to encode `v` using ogs_sbi_url_encode();
*/
ogs_sbi_header_set(request->http.headers,
OGS_SBI_CUSTOM_DISCOVERY_SERVICE_NAMES, encoded);
ogs_free(encoded);
rc = true;
}
ogs_free(v);
}
if (rc == false)
ogs_warn("invalid service names failed[%d:%s]",
discovery_option->num_of_service_names,
discovery_option->service_names[0]);
}
if (discovery_option->num_of_target_plmn_list) {
char *v = ogs_sbi_discovery_option_build_plmn_list(
discovery_option->target_plmn_list,
discovery_option->num_of_target_plmn_list);
if (v) {
ogs_sbi_header_set(request->http.headers,
OGS_SBI_CUSTOM_DISCOVERY_TARGET_PLMN_LIST, v);
ogs_free(v);
} else {
ogs_warn("invalid target-plmn-list failed[%d:%06x]",
discovery_option->num_of_target_plmn_list,
ogs_plmn_id_hexdump(
&discovery_option->target_plmn_list[0]));
}
}
if (discovery_option->num_of_requester_plmn_list) {
char *v = ogs_sbi_discovery_option_build_plmn_list(
discovery_option->requester_plmn_list,
discovery_option->num_of_requester_plmn_list);
if (v) {
ogs_sbi_header_set(request->http.headers,
OGS_SBI_CUSTOM_DISCOVERY_REQUESTER_PLMN_LIST, v);
ogs_free(v);
} else {
ogs_warn("invalid target-plmn-list failed[%d:%06x]",
discovery_option->num_of_requester_plmn_list,
ogs_plmn_id_hexdump(
&discovery_option->requester_plmn_list[0]));
}
}
}
if (local_discovery_option)
ogs_sbi_discovery_option_free(local_discovery_option);
}