open5gs/tests/af/af-sm.c

532 lines
20 KiB
C
Raw Normal View History

/*
* Copyright (C) 2019-2022 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"
#include "nnrf-handler.h"
#include "nbsf-handler.h"
#include "npcf-handler.h"
void af_state_initial(ogs_fsm_t *s, af_event_t *e)
{
af_sm_debug(e);
ogs_assert(s);
OGS_FSM_TRAN(s, &af_state_operational);
}
void af_state_final(ogs_fsm_t *s, af_event_t *e)
{
af_sm_debug(e);
ogs_assert(s);
}
void af_state_operational(ogs_fsm_t *s, af_event_t *e)
{
int rv;
af_sess_t *sess = NULL;
ogs_sbi_stream_t *stream = NULL;
ogs_sbi_request_t *request = NULL;
ogs_sbi_nf_instance_t *nf_instance = NULL;
2022-09-08 13:12:01 +00:00
ogs_sbi_subscription_data_t *subscription_data = NULL;
ogs_sbi_response_t *response = NULL;
ogs_sbi_message_t message;
ogs_sbi_xact_t *sbi_xact = NULL;
af_sm_debug(e);
ogs_assert(s);
switch (e->h.id) {
case OGS_FSM_ENTRY_SIG:
break;
case OGS_FSM_EXIT_SIG:
break;
case OGS_EVENT_SBI_SERVER:
request = e->h.sbi.request;
ogs_assert(request);
stream = e->h.sbi.data;
ogs_assert(stream);
rv = ogs_sbi_parse_request(&message, request);
if (rv != OGS_OK) {
/* 'sbi_message' buffer is released in ogs_sbi_parse_request() */
ogs_error("cannot parse HTTP sbi_message");
2021-06-06 13:35:46 +00:00
ogs_assert(true ==
ogs_sbi_server_send_error(
stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
NULL, "cannot parse HTTP sbi_message", NULL, NULL));
break;
}
if (strcmp(message.h.api.version, OGS_SBI_API_V1) != 0) {
ogs_error("Not supported version [%s]", message.h.api.version);
2021-06-06 13:35:46 +00:00
ogs_assert(true ==
ogs_sbi_server_send_error(
stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
&message, "Not supported version", NULL, NULL));
ogs_sbi_message_free(&message);
break;
}
SWITCH(message.h.service.name)
CASE(OGS_SBI_SERVICE_NAME_NNRF_NFM)
SWITCH(message.h.resource.component[0])
CASE(OGS_SBI_RESOURCE_NAME_NF_STATUS_NOTIFY)
SWITCH(message.h.method)
CASE(OGS_SBI_HTTP_METHOD_POST)
ogs_nnrf_nfm_handle_nf_status_notify(stream, &message);
break;
DEFAULT
ogs_error("Invalid HTTP method [%s]", message.h.method);
2021-06-06 13:35:46 +00:00
ogs_assert(true ==
ogs_sbi_server_send_error(stream,
OGS_SBI_HTTP_STATUS_FORBIDDEN, &message,
"Invalid HTTP method", message.h.method, NULL));
END
break;
DEFAULT
ogs_error("Invalid resource name [%s]",
message.h.resource.component[0]);
2021-06-06 13:35:46 +00:00
ogs_assert(true ==
ogs_sbi_server_send_error(stream,
OGS_SBI_HTTP_STATUS_BAD_REQUEST, &message,
"Invalid resource name",
message.h.resource.component[0], NULL));
END
break;
2021-11-14 12:07:56 +00:00
CASE(OGS_SBI_SERVICE_NAME_NPCF_POLICYAUTHORIZATION)
SWITCH(message.h.resource.component[0])
CASE(OGS_SBI_RESOURCE_NAME_APP_SESSIONS)
af_sess_t *app_session = NULL;
if (!message.h.resource.component[1]) {
ogs_error("[%s] Unknown app session id",
message.h.resource.component[1]);
ogs_assert(true ==
ogs_sbi_server_send_error(stream,
OGS_SBI_HTTP_STATUS_NOT_FOUND,
&message, "Not found",
message.h.resource.component[1], NULL));
2021-11-14 12:07:56 +00:00
break;
}
app_session = af_sess_find_by_af_app_session_id(
message.h.resource.component[1]);
if (!app_session) {
ogs_error("[%s] Unknown app session id",
message.h.resource.component[1]);
ogs_assert(true ==
ogs_sbi_server_send_error(stream,
OGS_SBI_HTTP_STATUS_NOT_FOUND,
&message, "Not found",
message.h.resource.component[1], NULL));
2021-11-14 12:07:56 +00:00
break;
}
SWITCH(message.h.resource.component[2])
CASE(OGS_SBI_RESOURCE_NAME_TERMINATE)
ogs_assert(true ==
ogs_sbi_send_http_status_no_content(stream));
af_sess_remove(app_session);
break;
DEFAULT
ogs_error("Invalid resource name [%s]",
message.h.resource.component[2]);
ogs_assert(true ==
ogs_sbi_server_send_error(stream,
OGS_SBI_HTTP_STATUS_BAD_REQUEST, &message,
"Invalid resource name",
message.h.resource.component[2], NULL));
2021-11-14 12:07:56 +00:00
END
break;
DEFAULT
ogs_error("Invalid resource name [%s]",
message.h.resource.component[0]);
ogs_assert(true ==
ogs_sbi_server_send_error(stream,
OGS_SBI_HTTP_STATUS_BAD_REQUEST, &message,
"Invalid resource name",
message.h.resource.component[0], NULL));
2021-11-14 12:07:56 +00:00
END
break;
DEFAULT
ogs_error("Invalid API name [%s]", message.h.service.name);
2021-06-06 13:35:46 +00:00
ogs_assert(true ==
ogs_sbi_server_send_error(stream,
OGS_SBI_HTTP_STATUS_BAD_REQUEST, &message,
"Invalid API name", message.h.service.name, NULL));
END
/* In lib/sbi/server.c, notify_completed() releases 'request' buffer. */
ogs_sbi_message_free(&message);
break;
case OGS_EVENT_SBI_CLIENT:
ogs_assert(e);
response = e->h.sbi.response;
ogs_assert(response);
rv = ogs_sbi_parse_response(&message, response);
if (rv != OGS_OK) {
ogs_error("cannot parse HTTP response");
ogs_sbi_message_free(&message);
ogs_sbi_response_free(response);
break;
}
if (strcmp(message.h.api.version, OGS_SBI_API_V1) != 0) {
ogs_error("Not supported version [%s]", message.h.api.version);
ogs_sbi_message_free(&message);
ogs_sbi_response_free(response);
break;
}
SWITCH(message.h.service.name)
CASE(OGS_SBI_SERVICE_NAME_NNRF_NFM)
SWITCH(message.h.resource.component[0])
CASE(OGS_SBI_RESOURCE_NAME_NF_INSTANCES)
nf_instance = e->h.sbi.data;
ogs_assert(nf_instance);
ogs_assert(OGS_FSM_STATE(&nf_instance->sm));
e->h.sbi.message = &message;
ogs_fsm_dispatch(&nf_instance->sm, e);
break;
CASE(OGS_SBI_RESOURCE_NAME_SUBSCRIPTIONS)
2022-09-08 13:12:01 +00:00
subscription_data = e->h.sbi.data;
ogs_assert(subscription_data);
SWITCH(message.h.method)
CASE(OGS_SBI_HTTP_METHOD_POST)
if (message.res_status == OGS_SBI_HTTP_STATUS_CREATED ||
message.res_status == OGS_SBI_HTTP_STATUS_OK) {
ogs_nnrf_nfm_handle_nf_status_subscribe(
2022-09-08 13:12:01 +00:00
subscription_data, &message);
} else {
ogs_error("HTTP response error : %d",
message.res_status);
}
break;
CASE(OGS_SBI_HTTP_METHOD_PATCH)
if (message.res_status == OGS_SBI_HTTP_STATUS_OK ||
message.res_status == OGS_SBI_HTTP_STATUS_NO_CONTENT) {
ogs_nnrf_nfm_handle_nf_status_update(
subscription_data, &message);
} else {
ogs_error("[%s] HTTP response error [%d]",
subscription_data->id ?
subscription_data->id : "Unknown",
message.res_status);
}
break;
CASE(OGS_SBI_HTTP_METHOD_DELETE)
if (message.res_status == OGS_SBI_HTTP_STATUS_NO_CONTENT) {
2022-09-08 13:12:01 +00:00
ogs_sbi_subscription_data_remove(subscription_data);
} else {
ogs_error("[%s] HTTP response error [%d]",
subscription_data->id ?
subscription_data->id : "Unknown",
message.res_status);
}
break;
DEFAULT
ogs_error("Invalid HTTP method [%s]", message.h.method);
ogs_assert_if_reached();
END
break;
DEFAULT
ogs_error("Invalid resource name [%s]",
message.h.resource.component[0]);
ogs_assert_if_reached();
END
break;
CASE(OGS_SBI_SERVICE_NAME_NNRF_DISC)
SWITCH(message.h.resource.component[0])
CASE(OGS_SBI_RESOURCE_NAME_NF_INSTANCES)
sbi_xact = e->h.sbi.data;
ogs_assert(sbi_xact);
SWITCH(message.h.method)
CASE(OGS_SBI_HTTP_METHOD_GET)
if (message.res_status == OGS_SBI_HTTP_STATUS_OK)
af_nnrf_handle_nf_discover(sbi_xact, &message);
else
ogs_error("HTTP response error [%d]",
message.res_status);
break;
DEFAULT
ogs_error("Invalid HTTP method [%s]", message.h.method);
ogs_assert_if_reached();
END
break;
DEFAULT
ogs_error("Invalid resource name [%s]",
message.h.resource.component[0]);
ogs_assert_if_reached();
END
break;
CASE(OGS_SBI_SERVICE_NAME_NBSF_MANAGEMENT)
SWITCH(message.h.resource.component[0])
CASE(OGS_SBI_RESOURCE_NAME_PCF_BINDINGS)
sbi_xact = e->h.sbi.data;
ogs_assert(sbi_xact);
sbi_xact = ogs_sbi_xact_cycle(sbi_xact);
if (!sbi_xact) {
/* CLIENT_WAIT timer could remove SBI transaction
* before receiving SBI message */
ogs_error("SBI transaction has already been removed");
break;
}
sess = (af_sess_t *)sbi_xact->sbi_object;
ogs_assert(sess);
ogs_sbi_xact_remove(sbi_xact);
SWITCH(message.h.method)
CASE(OGS_SBI_HTTP_METHOD_GET)
if (message.res_status == OGS_SBI_HTTP_STATUS_OK)
af_nbsf_management_handle_pcf_binding(sess, &message);
else
ogs_error("HTTP response error [%d]",
message.res_status);
break;
DEFAULT
ogs_error("Invalid HTTP method [%s]", message.h.method);
ogs_assert_if_reached();
END
break;
DEFAULT
ogs_error("Invalid resource name [%s]",
message.h.resource.component[0]);
ogs_assert_if_reached();
END
break;
CASE(OGS_SBI_SERVICE_NAME_NPCF_POLICYAUTHORIZATION)
SWITCH(message.h.resource.component[0])
CASE(OGS_SBI_RESOURCE_NAME_APP_SESSIONS)
sess = e->h.sbi.data;
ogs_assert(sess);
2021-11-14 12:07:56 +00:00
if (message.h.resource.component[1]) {
if (message.h.resource.component[2]) {
SWITCH(message.h.resource.component[2])
CASE(OGS_SBI_RESOURCE_NAME_DELETE)
af_sess_remove(sess);
break;
DEFAULT
ogs_error("Invalid resource name [%s]",
message.h.resource.component[2]);
ogs_assert_if_reached();
END
} else {
SWITCH(message.h.method)
CASE(OGS_SBI_HTTP_METHOD_PATCH)
if (message.res_status == OGS_SBI_HTTP_STATUS_OK)
af_npcf_policyauthorization_handle_update(
sess, &message);
else
ogs_error("HTTP response error [%d]",
message.res_status);
break;
DEFAULT
ogs_error("Invalid HTTP method [%s]",
message.h.method);
ogs_assert_if_reached();
END
}
} else {
SWITCH(message.h.method)
CASE(OGS_SBI_HTTP_METHOD_POST)
if (message.res_status == OGS_SBI_HTTP_STATUS_CREATED)
af_npcf_policyauthorization_handle_create(
sess, &message);
else
ogs_error("HTTP response error [%d]",
message.res_status);
break;
DEFAULT
ogs_error("Invalid HTTP method [%s]", message.h.method);
ogs_assert_if_reached();
END
}
break;
DEFAULT
ogs_error("Invalid resource name [%s]",
message.h.resource.component[0]);
ogs_assert_if_reached();
END
break;
DEFAULT
ogs_error("Invalid service name [%s]", message.h.service.name);
ogs_assert_if_reached();
END
ogs_sbi_message_free(&message);
ogs_sbi_response_free(response);
break;
case OGS_EVENT_SBI_TIMER:
ogs_assert(e);
switch(e->h.timer_id) {
case OGS_TIMER_NF_INSTANCE_REGISTRATION_INTERVAL:
case OGS_TIMER_NF_INSTANCE_HEARTBEAT_INTERVAL:
case OGS_TIMER_NF_INSTANCE_NO_HEARTBEAT:
case OGS_TIMER_NF_INSTANCE_VALIDITY:
nf_instance = e->h.sbi.data;
ogs_assert(nf_instance);
ogs_assert(OGS_FSM_STATE(&nf_instance->sm));
ogs_fsm_dispatch(&nf_instance->sm, e);
if (OGS_FSM_CHECK(&nf_instance->sm, ogs_sbi_nf_state_exception))
ogs_error("State machine exception [%d]", e->h.timer_id);
break;
case OGS_TIMER_SUBSCRIPTION_VALIDITY:
2022-09-08 13:12:01 +00:00
subscription_data = e->h.sbi.data;
ogs_assert(subscription_data);
2021-06-06 13:35:46 +00:00
ogs_assert(true ==
ogs_nnrf_nfm_send_nf_status_subscribe(
ogs_sbi_self()->nf_instance->nf_type,
subscription_data->req_nf_instance_id,
subscription_data->subscr_cond.nf_type,
subscription_data->subscr_cond.service_name));
ogs_error("[%s] Subscription validity expired",
2022-09-08 13:12:01 +00:00
subscription_data->id);
ogs_sbi_subscription_data_remove(subscription_data);
break;
case OGS_TIMER_SUBSCRIPTION_PATCH:
subscription_data = e->h.sbi.data;
ogs_assert(subscription_data);
ogs_assert(true ==
ogs_nnrf_nfm_send_nf_status_update(subscription_data));
ogs_info("[%s] Need to update Subscription",
subscription_data->id);
break;
case OGS_TIMER_SBI_CLIENT_WAIT:
2023-02-04 12:35:12 +00:00
/*
* ogs_pollset_poll() receives the time of the expiration
* of next timer as an argument. If this timeout is
* in very near future (1 millisecond), and if there are
* multiple events that need to be processed by ogs_pollset_poll(),
* these could take more than 1 millisecond for processing,
* resulting in the timer already passed the expiration.
*
* In case that another NF is under heavy load and responds
* to an SBI request with some delay of a few seconds,
* it can happen that ogs_pollset_poll() adds SBI responses
* to the event list for further processing,
* then ogs_timer_mgr_expire() is called which will add
* an additional event for timer expiration. When all events are
* processed one-by-one, the SBI xact would get deleted twice
* in a row, resulting in a crash.
*
* 1. ogs_pollset_poll()
* message was received and put into an event list,
* 2. ogs_timer_mgr_expire()
* add an additional event for timer expiration
* 3. message event is processed. (free SBI xact)
* 4. timer expiration event is processed. (double-free SBI xact)
*
* To avoid double-free SBI xact,
* we need to check ogs_sbi_xact_cycle()
*/
sbi_xact = ogs_sbi_xact_cycle(e->h.sbi.data);
[NF] Fix double-free crash when NF is under heavy load <nf>/init.c:<nf>_main() : ogs_pollset_poll() receives the time of the expiration of next timer as an argument. If this timeout is in very near future (1 millisecond), and if there are multiple events that need to be processed by ogs_pollset_poll(), these could take more than 1 millisecond for processing, resulting in the timer already passed the expiration. In case that another NF is under heavy load and responds to an SBI request with some delay of a few seconds, it can happen that ogs_pollset_poll() adds SBI responses to the event list for further processing, then ogs_timer_mgr_expire() is called which will add an additional event for timer expiration. When all events are processed one-by-one, the SBI xact would get deleted twice in a row, resulting in a crash. 0 __GI_abort () at ./stdlib/abort.c:107 1 0x00007f9de91693b1 in ?? () from /lib/x86_64-linux-gnu/libtalloc.so.2 2 0x00007f9de9a21745 in ogs_talloc_free (ptr=0x7f9d906c2c70, location=0x7f9de960bf41 "../lib/sbi/message.c:2423") at ../lib/core/ogs-memory.c:107 3 0x00007f9de95dbf31 in ogs_sbi_discovery_option_free (discovery_option=0x7f9d9090e670) at ../lib/sbi/message.c:2423 4 0x00007f9de95f7c47 in ogs_sbi_xact_remove (xact=0x7f9db630b630) at ../lib/sbi/context.c:1702 5 0x000055a482784846 in amf_state_operational (s=0x7f9d9488bbb0, e=0x7f9d90aecf20) at ../src/amf/amf-sm.c:604 6 0x00007f9de9a33cf0 in ogs_fsm_dispatch (fsm=0x7f9d9488bbb0, event=0x7f9d90aecf20) at ../lib/core/ogs-fsm.c:127 7 0x000055a48275b32e in amf_main (data=0x0) at ../src/amf/init.c:149 8 0x00007f9de9a249eb in thread_worker (arg=0x55a483d41d90) at ../lib/core/ogs-thread.c:67 9 0x00007f9de8fd2b43 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442 10 0x00007f9de9063bb4 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:100
2022-12-21 08:57:50 +00:00
if (!sbi_xact) {
ogs_error("SBI transaction has already been removed");
break;
}
stream = sbi_xact->assoc_stream;
/* Here, we should not use ogs_assert(stream)
* since 'namf-comm' service has no an associated stream. */
ogs_sbi_xact_remove(sbi_xact);
ogs_error("Cannot receive SBI message");
if (stream) {
2021-06-06 13:35:46 +00:00
ogs_assert(true ==
ogs_sbi_server_send_error(stream,
OGS_SBI_HTTP_STATUS_GATEWAY_TIMEOUT, NULL,
"Cannot receive SBI message", NULL, NULL));
}
break;
default:
ogs_error("Unknown timer[%s:%d]",
ogs_timer_get_name(e->h.timer_id), e->h.timer_id);
}
break;
case AF_EVENT_SBI_LOCAL:
ogs_assert(e);
switch(e->local_id) {
case AF_LOCAL_DISCOVER_AND_SEND:
af_sbi_discover_and_send(e->local.service_type, NULL,
e->local.build, e->sess, e->local.data);
break;
case AF_LOCAL_SEND_TO_PCF:
af_sbi_send_to_pcf(e->sess, e->local.data, e->local.build);
break;
default:
ogs_error("Unknown local[%s:%d]",
af_local_get_name(e->local_id), e->local_id);
}
break;
default:
ogs_error("No handler for event %s", af_event_get_name(e));
break;
}
}