2020-04-26 19:36:05 +00:00
|
|
|
/*
|
2024-03-25 23:04:26 +00:00
|
|
|
* Copyright (C) 2019-2023 by Sukchan Lee <acetcom@gmail.com>
|
2020-04-26 19:36:05 +00:00
|
|
|
*
|
|
|
|
* 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/>.
|
|
|
|
*/
|
|
|
|
|
2020-08-13 00:31:22 +00:00
|
|
|
#include "sbi-path.h"
|
2020-04-26 19:36:05 +00:00
|
|
|
#include "pfcp-path.h"
|
2020-08-13 00:31:22 +00:00
|
|
|
|
2020-04-26 19:36:05 +00:00
|
|
|
#include "n4-handler.h"
|
|
|
|
|
2023-04-15 09:54:03 +00:00
|
|
|
static void pfcp_restoration(ogs_pfcp_node_t *node);
|
2023-04-04 12:22:03 +00:00
|
|
|
static void reselect_upf(ogs_pfcp_node_t *node);
|
2020-08-13 00:31:22 +00:00
|
|
|
static void node_timeout(ogs_pfcp_xact_t *xact, void *data);
|
|
|
|
|
2020-04-26 19:36:05 +00:00
|
|
|
void smf_pfcp_state_initial(ogs_fsm_t *s, smf_event_t *e)
|
|
|
|
{
|
|
|
|
int rv;
|
|
|
|
ogs_pfcp_node_t *node = NULL;
|
|
|
|
|
|
|
|
ogs_assert(s);
|
|
|
|
ogs_assert(e);
|
|
|
|
|
|
|
|
smf_sm_debug(e);
|
|
|
|
|
|
|
|
node = e->pfcp_node;
|
|
|
|
ogs_assert(node);
|
|
|
|
|
|
|
|
rv = ogs_pfcp_connect(
|
|
|
|
ogs_pfcp_self()->pfcp_sock, ogs_pfcp_self()->pfcp_sock6, node);
|
|
|
|
ogs_assert(rv == OGS_OK);
|
|
|
|
|
2020-08-26 03:05:01 +00:00
|
|
|
node->t_no_heartbeat = ogs_timer_add(ogs_app()->timer_mgr,
|
2020-07-27 01:02:40 +00:00
|
|
|
smf_timer_pfcp_no_heartbeat, node);
|
|
|
|
ogs_assert(node->t_no_heartbeat);
|
2020-04-26 19:36:05 +00:00
|
|
|
|
|
|
|
OGS_FSM_TRAN(s, &smf_pfcp_state_will_associate);
|
|
|
|
}
|
|
|
|
|
|
|
|
void smf_pfcp_state_final(ogs_fsm_t *s, smf_event_t *e)
|
|
|
|
{
|
|
|
|
ogs_pfcp_node_t *node = NULL;
|
|
|
|
ogs_assert(s);
|
|
|
|
ogs_assert(e);
|
|
|
|
|
|
|
|
smf_sm_debug(e);
|
|
|
|
|
|
|
|
node = e->pfcp_node;
|
|
|
|
ogs_assert(node);
|
|
|
|
|
2020-07-27 01:02:40 +00:00
|
|
|
ogs_timer_delete(node->t_no_heartbeat);
|
2020-04-26 19:36:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void smf_pfcp_state_will_associate(ogs_fsm_t *s, smf_event_t *e)
|
|
|
|
{
|
|
|
|
char buf[OGS_ADDRSTRLEN];
|
|
|
|
|
|
|
|
ogs_pfcp_node_t *node = NULL;
|
|
|
|
ogs_pfcp_xact_t *xact = NULL;
|
|
|
|
ogs_pfcp_message_t *message = NULL;
|
|
|
|
|
|
|
|
ogs_sockaddr_t *addr = NULL;
|
2023-07-10 12:13:28 +00:00
|
|
|
smf_sess_t *sess;
|
2020-04-26 19:36:05 +00:00
|
|
|
|
|
|
|
ogs_assert(s);
|
|
|
|
ogs_assert(e);
|
|
|
|
|
|
|
|
smf_sm_debug(e);
|
|
|
|
|
|
|
|
node = e->pfcp_node;
|
|
|
|
ogs_assert(node);
|
|
|
|
addr = node->sa_list;
|
|
|
|
ogs_assert(addr);
|
|
|
|
|
2022-08-12 05:03:53 +00:00
|
|
|
switch (e->h.id) {
|
2020-04-26 19:36:05 +00:00
|
|
|
case OGS_FSM_ENTRY_SIG:
|
2020-05-14 17:38:26 +00:00
|
|
|
if (node->t_association) {
|
|
|
|
ogs_timer_start(node->t_association,
|
2023-11-19 10:34:51 +00:00
|
|
|
ogs_local_conf()->time.message.pfcp.association_interval);
|
2020-04-26 19:36:05 +00:00
|
|
|
|
2020-08-13 00:31:22 +00:00
|
|
|
ogs_pfcp_cp_send_association_setup_request(node, node_timeout);
|
2020-05-14 17:38:26 +00:00
|
|
|
}
|
2020-04-26 19:36:05 +00:00
|
|
|
break;
|
2020-05-14 17:38:26 +00:00
|
|
|
|
2020-04-26 19:36:05 +00:00
|
|
|
case OGS_FSM_EXIT_SIG:
|
2020-05-14 17:38:26 +00:00
|
|
|
if (node->t_association) {
|
|
|
|
ogs_timer_stop(node->t_association);
|
|
|
|
}
|
2020-04-26 19:36:05 +00:00
|
|
|
break;
|
2020-05-14 17:38:26 +00:00
|
|
|
|
2020-04-26 19:36:05 +00:00
|
|
|
case SMF_EVT_N4_TIMER:
|
2022-08-12 05:03:53 +00:00
|
|
|
switch(e->h.timer_id) {
|
2020-05-18 21:00:37 +00:00
|
|
|
case SMF_TIMER_PFCP_ASSOCIATION:
|
2020-04-26 19:36:05 +00:00
|
|
|
node = e->pfcp_node;
|
|
|
|
ogs_assert(node);
|
|
|
|
|
2023-11-19 10:34:51 +00:00
|
|
|
ogs_warn("Retry association with peer [%s]:%d failed",
|
2020-04-26 19:36:05 +00:00
|
|
|
OGS_ADDR(addr, buf), OGS_PORT(addr));
|
|
|
|
|
2020-05-14 17:38:26 +00:00
|
|
|
ogs_assert(node->t_association);
|
2020-04-26 19:36:05 +00:00
|
|
|
ogs_timer_start(node->t_association,
|
2023-11-19 10:34:51 +00:00
|
|
|
ogs_local_conf()->time.message.pfcp.association_interval);
|
2020-04-26 19:36:05 +00:00
|
|
|
|
2020-08-13 00:31:22 +00:00
|
|
|
ogs_pfcp_cp_send_association_setup_request(node, node_timeout);
|
2020-04-26 19:36:05 +00:00
|
|
|
break;
|
2023-07-10 12:13:28 +00:00
|
|
|
case SMF_TIMER_PFCP_NO_ESTABLISHMENT_RESPONSE:
|
2024-03-24 00:46:25 +00:00
|
|
|
sess = smf_sess_cycle(e->sess);
|
2023-07-10 12:13:28 +00:00
|
|
|
if (!sess) {
|
|
|
|
ogs_warn("Session has already been removed");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ogs_fsm_dispatch(&sess->sm, e);
|
|
|
|
break;
|
2024-03-24 00:46:25 +00:00
|
|
|
case SMF_TIMER_PFCP_NO_DELETION_RESPONSE:
|
|
|
|
sess = smf_sess_cycle(e->sess);
|
|
|
|
if (!sess) {
|
|
|
|
ogs_warn("Session has already been removed");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
SMF_SESS_CLEAR(sess);
|
|
|
|
break;
|
2020-04-26 19:36:05 +00:00
|
|
|
default:
|
|
|
|
ogs_error("Unknown timer[%s:%d]",
|
2022-08-12 05:03:53 +00:00
|
|
|
smf_timer_get_name(e->h.timer_id), e->h.timer_id);
|
2020-04-26 19:36:05 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SMF_EVT_N4_MESSAGE:
|
|
|
|
message = e->pfcp_message;
|
|
|
|
ogs_assert(message);
|
|
|
|
xact = e->pfcp_xact;
|
|
|
|
ogs_assert(xact);
|
|
|
|
|
|
|
|
switch (message->h.type) {
|
2023-04-04 12:22:03 +00:00
|
|
|
case OGS_PFCP_HEARTBEAT_REQUEST_TYPE:
|
|
|
|
ogs_expect(true ==
|
|
|
|
ogs_pfcp_handle_heartbeat_request(node, xact,
|
|
|
|
&message->pfcp_heartbeat_request));
|
|
|
|
break;
|
|
|
|
case OGS_PFCP_HEARTBEAT_RESPONSE_TYPE:
|
|
|
|
ogs_expect(true ==
|
|
|
|
ogs_pfcp_handle_heartbeat_response(node, xact,
|
|
|
|
&message->pfcp_heartbeat_response));
|
|
|
|
break;
|
2020-04-26 19:36:05 +00:00
|
|
|
case OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE:
|
2020-08-13 00:31:22 +00:00
|
|
|
ogs_pfcp_cp_handle_association_setup_request(node, xact,
|
2020-04-26 19:36:05 +00:00
|
|
|
&message->pfcp_association_setup_request);
|
|
|
|
OGS_FSM_TRAN(s, smf_pfcp_state_associated);
|
|
|
|
break;
|
|
|
|
case OGS_PFCP_ASSOCIATION_SETUP_RESPONSE_TYPE:
|
2020-08-13 00:31:22 +00:00
|
|
|
ogs_pfcp_cp_handle_association_setup_response(node, xact,
|
2020-04-26 19:36:05 +00:00
|
|
|
&message->pfcp_association_setup_response);
|
|
|
|
OGS_FSM_TRAN(s, smf_pfcp_state_associated);
|
|
|
|
break;
|
|
|
|
default:
|
2020-09-07 02:51:07 +00:00
|
|
|
ogs_warn("cannot handle PFCP message type[%d]",
|
2020-04-26 19:36:05 +00:00
|
|
|
message->h.type);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ogs_error("Unknown event %s", smf_event_get_name(e));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void smf_pfcp_state_associated(ogs_fsm_t *s, smf_event_t *e)
|
|
|
|
{
|
|
|
|
char buf[OGS_ADDRSTRLEN];
|
|
|
|
|
|
|
|
ogs_pfcp_node_t *node = NULL;
|
|
|
|
ogs_pfcp_xact_t *xact = NULL;
|
|
|
|
ogs_pfcp_message_t *message = NULL;
|
|
|
|
|
|
|
|
ogs_sockaddr_t *addr = NULL;
|
|
|
|
smf_sess_t *sess = NULL;
|
|
|
|
|
|
|
|
ogs_assert(s);
|
|
|
|
ogs_assert(e);
|
|
|
|
|
|
|
|
smf_sm_debug(e);
|
|
|
|
|
|
|
|
node = e->pfcp_node;
|
|
|
|
ogs_assert(node);
|
|
|
|
addr = node->sa_list;
|
|
|
|
ogs_assert(addr);
|
|
|
|
|
2022-08-12 05:03:53 +00:00
|
|
|
switch (e->h.id) {
|
2020-04-26 19:36:05 +00:00
|
|
|
case OGS_FSM_ENTRY_SIG:
|
2022-08-25 18:46:00 +00:00
|
|
|
ogs_info("PFCP associated [%s]:%d",
|
|
|
|
OGS_ADDR(&node->addr, buf),
|
|
|
|
OGS_PORT(&node->addr));
|
2020-07-27 01:02:40 +00:00
|
|
|
ogs_timer_start(node->t_no_heartbeat,
|
2023-11-19 10:34:51 +00:00
|
|
|
ogs_local_conf()->time.message.pfcp.no_heartbeat_duration);
|
2023-04-04 12:22:03 +00:00
|
|
|
ogs_assert(OGS_OK ==
|
|
|
|
ogs_pfcp_send_heartbeat_request(node, node_timeout));
|
|
|
|
|
|
|
|
if (node->restoration_required == true) {
|
2023-04-15 09:54:03 +00:00
|
|
|
pfcp_restoration(node);
|
2023-04-04 12:22:03 +00:00
|
|
|
node->restoration_required = false;
|
|
|
|
ogs_error("PFCP restoration");
|
|
|
|
}
|
2020-04-26 19:36:05 +00:00
|
|
|
break;
|
|
|
|
case OGS_FSM_EXIT_SIG:
|
2022-08-25 18:46:00 +00:00
|
|
|
ogs_info("PFCP de-associated [%s]:%d",
|
|
|
|
OGS_ADDR(&node->addr, buf),
|
|
|
|
OGS_PORT(&node->addr));
|
2020-07-27 01:02:40 +00:00
|
|
|
ogs_timer_stop(node->t_no_heartbeat);
|
2020-04-26 19:36:05 +00:00
|
|
|
break;
|
|
|
|
case SMF_EVT_N4_MESSAGE:
|
|
|
|
message = e->pfcp_message;
|
|
|
|
ogs_assert(message);
|
|
|
|
xact = e->pfcp_xact;
|
|
|
|
ogs_assert(xact);
|
|
|
|
|
2022-06-28 02:19:57 +00:00
|
|
|
if (message->h.seid_presence && message->h.seid != 0) {
|
|
|
|
sess = smf_sess_find_by_seid(message->h.seid);
|
|
|
|
} else if (xact->local_seid) { /* rx no SEID or SEID=0 */
|
|
|
|
/* 3GPP TS 29.244 7.2.2.4.2: we receive SEID=0 under some
|
|
|
|
* conditions, such as cause "Session context not found". In those
|
|
|
|
* cases, we still want to identify the local session which
|
|
|
|
* originated the message, so try harder by using the SEID we
|
2022-06-30 01:53:19 +00:00
|
|
|
* locally stored in xact when sending the original request: */
|
2022-06-28 02:19:57 +00:00
|
|
|
sess = smf_sess_find_by_seid(xact->local_seid);
|
|
|
|
}
|
2022-04-14 01:30:58 +00:00
|
|
|
if (sess)
|
|
|
|
e->sess = sess;
|
2020-04-26 19:36:05 +00:00
|
|
|
|
|
|
|
switch (message->h.type) {
|
|
|
|
case OGS_PFCP_HEARTBEAT_REQUEST_TYPE:
|
2023-04-04 12:22:03 +00:00
|
|
|
ogs_expect(true ==
|
2021-06-06 13:35:46 +00:00
|
|
|
ogs_pfcp_handle_heartbeat_request(node, xact,
|
|
|
|
&message->pfcp_heartbeat_request));
|
2023-04-04 12:22:03 +00:00
|
|
|
if (node->restoration_required == true) {
|
|
|
|
if (node->t_association) {
|
|
|
|
/*
|
|
|
|
* node->t_association that the PFCP entity attempts an association.
|
|
|
|
*
|
|
|
|
* In this case, even if Remote PFCP entity is restarted,
|
|
|
|
* PFCP restoration must be performed after PFCP association.
|
|
|
|
*
|
|
|
|
* Otherwise, Session related PFCP cannot be initiated
|
|
|
|
* because the peer PFCP entity is in a de-associated state.
|
|
|
|
*/
|
|
|
|
OGS_FSM_TRAN(s, smf_pfcp_state_will_associate);
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the peer PFCP entity is performing the association,
|
|
|
|
* Restoration can be performed immediately.
|
|
|
|
*/
|
2023-04-15 09:54:03 +00:00
|
|
|
pfcp_restoration(node);
|
2023-04-04 12:22:03 +00:00
|
|
|
node->restoration_required = false;
|
|
|
|
ogs_error("PFCP restoration");
|
|
|
|
}
|
|
|
|
}
|
2020-04-26 19:36:05 +00:00
|
|
|
break;
|
|
|
|
case OGS_PFCP_HEARTBEAT_RESPONSE_TYPE:
|
2023-04-04 12:22:03 +00:00
|
|
|
ogs_expect(true ==
|
2021-06-06 13:35:46 +00:00
|
|
|
ogs_pfcp_handle_heartbeat_response(node, xact,
|
|
|
|
&message->pfcp_heartbeat_response));
|
2023-04-04 12:22:03 +00:00
|
|
|
if (node->restoration_required == true) {
|
|
|
|
/*
|
|
|
|
* node->t_association that the PFCP entity attempts an association.
|
|
|
|
*
|
|
|
|
* In this case, even if Remote PFCP entity is restarted,
|
|
|
|
* PFCP restoration must be performed after PFCP association.
|
|
|
|
*
|
|
|
|
* Otherwise, Session related PFCP cannot be initiated
|
|
|
|
* because the peer PFCP entity is in a de-associated state.
|
|
|
|
*/
|
|
|
|
if (node->t_association) {
|
|
|
|
OGS_FSM_TRAN(s, smf_pfcp_state_will_associate);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* If the peer PFCP entity is performing the association,
|
|
|
|
* Restoration can be performed immediately.
|
|
|
|
*/
|
2023-04-15 09:54:03 +00:00
|
|
|
pfcp_restoration(node);
|
2023-04-04 12:22:03 +00:00
|
|
|
node->restoration_required = false;
|
|
|
|
ogs_error("PFCP restoration");
|
|
|
|
}
|
|
|
|
}
|
2020-04-26 19:36:05 +00:00
|
|
|
break;
|
|
|
|
case OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE:
|
2022-08-25 18:46:00 +00:00
|
|
|
ogs_warn("PFCP[REQ] has already been associated [%s]:%d",
|
|
|
|
OGS_ADDR(&node->addr, buf),
|
|
|
|
OGS_PORT(&node->addr));
|
2020-08-13 00:31:22 +00:00
|
|
|
ogs_pfcp_cp_handle_association_setup_request(node, xact,
|
2020-04-26 19:36:05 +00:00
|
|
|
&message->pfcp_association_setup_request);
|
|
|
|
break;
|
|
|
|
case OGS_PFCP_ASSOCIATION_SETUP_RESPONSE_TYPE:
|
2022-08-25 18:46:00 +00:00
|
|
|
ogs_warn("PFCP[RSP] has already been associated [%s]:%d",
|
|
|
|
OGS_ADDR(&node->addr, buf),
|
|
|
|
OGS_PORT(&node->addr));
|
2020-08-13 00:31:22 +00:00
|
|
|
ogs_pfcp_cp_handle_association_setup_response(node, xact,
|
2020-04-26 19:36:05 +00:00
|
|
|
&message->pfcp_association_setup_response);
|
|
|
|
break;
|
|
|
|
case OGS_PFCP_SESSION_ESTABLISHMENT_RESPONSE_TYPE:
|
2022-06-30 01:35:03 +00:00
|
|
|
if (!message->h.seid_presence) ogs_error("No SEID");
|
|
|
|
|
2022-08-04 00:55:17 +00:00
|
|
|
if (!sess) {
|
|
|
|
ogs_gtp_xact_t *gtp_xact = xact->assoc_xact;
|
2022-10-25 12:22:14 +00:00
|
|
|
ogs_error("No Session");
|
2022-08-04 00:55:17 +00:00
|
|
|
if (!gtp_xact) {
|
|
|
|
ogs_error("No associated GTP transaction");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (gtp_xact->gtp_version == 1)
|
|
|
|
ogs_gtp1_send_error_message(gtp_xact, 0,
|
|
|
|
OGS_GTP1_CREATE_PDP_CONTEXT_RESPONSE_TYPE,
|
|
|
|
OGS_GTP1_CAUSE_CONTEXT_NOT_FOUND);
|
|
|
|
else
|
2024-03-25 23:04:26 +00:00
|
|
|
ogs_gtp2_send_error_message(gtp_xact, 0,
|
2022-08-04 00:55:17 +00:00
|
|
|
OGS_GTP2_CREATE_SESSION_RESPONSE_TYPE,
|
|
|
|
OGS_GTP2_CAUSE_CONTEXT_NOT_FOUND);
|
|
|
|
break;
|
|
|
|
}
|
2022-04-14 01:30:58 +00:00
|
|
|
ogs_fsm_dispatch(&sess->sm, e);
|
2020-04-26 19:36:05 +00:00
|
|
|
break;
|
2020-07-04 03:14:48 +00:00
|
|
|
|
2020-04-26 19:36:05 +00:00
|
|
|
case OGS_PFCP_SESSION_MODIFICATION_RESPONSE_TYPE:
|
2022-06-30 01:35:03 +00:00
|
|
|
if (!message->h.seid_presence) ogs_error("No SEID");
|
2022-06-30 00:11:38 +00:00
|
|
|
|
2020-08-13 00:31:22 +00:00
|
|
|
if (xact->epc)
|
2020-07-04 03:14:48 +00:00
|
|
|
smf_epc_n4_handle_session_modification_response(
|
[PFCP/GTP] Incorrect TEI/SEID=0 (#3043)
If eg. PCRF or AAA diameter link is not yet ready (eg. PCRF crashed),
and a client sends a CreateSessionRequest announcing its ow F-TEID,
then open5gs-smfd answers with Create Session Response Cause="Remote peer not responding",
but it is not setting the received F-TEID in the header of the response,
instead it sends with TEID=0.
As a result, the peer cannot match the CreateSessionResponse,
and needs to rely on its own timeout timer to figure out
that specific request failed.
See 3GPP TS 29.274 5.5 Usage of the GTPv2-C Header:
```
Bit 4 represents a "T" flag, which indicates if TEID field is present in the GTP-C header or not. If the "T" flag is
set to 0, then the TEID field shall not be present in the GTP-C header. If the "T" flag is set to 1, then the TEID
field shall immediately follow the Length field, in octets 5 to 8. Apart from the Echo Request, Echo Response
and Version Not Supported Indication messages, in all EPC specific messages the value of the "T" flag shall be
set to "1".
```
This happens with Delete Session Requests and can happen with any PFCP message.
I've fixed TEID/SEID to send the value in the reponse message as is if it was received.
2024-04-05 12:36:30 +00:00
|
|
|
sess, xact, e->gtp2_message, message);
|
2020-06-17 05:22:28 +00:00
|
|
|
else
|
2020-07-04 03:14:48 +00:00
|
|
|
smf_5gc_n4_handle_session_modification_response(
|
[PFCP/GTP] Incorrect TEI/SEID=0 (#3043)
If eg. PCRF or AAA diameter link is not yet ready (eg. PCRF crashed),
and a client sends a CreateSessionRequest announcing its ow F-TEID,
then open5gs-smfd answers with Create Session Response Cause="Remote peer not responding",
but it is not setting the received F-TEID in the header of the response,
instead it sends with TEID=0.
As a result, the peer cannot match the CreateSessionResponse,
and needs to rely on its own timeout timer to figure out
that specific request failed.
See 3GPP TS 29.274 5.5 Usage of the GTPv2-C Header:
```
Bit 4 represents a "T" flag, which indicates if TEID field is present in the GTP-C header or not. If the "T" flag is
set to 0, then the TEID field shall not be present in the GTP-C header. If the "T" flag is set to 1, then the TEID
field shall immediately follow the Length field, in octets 5 to 8. Apart from the Echo Request, Echo Response
and Version Not Supported Indication messages, in all EPC specific messages the value of the "T" flag shall be
set to "1".
```
This happens with Delete Session Requests and can happen with any PFCP message.
I've fixed TEID/SEID to send the value in the reponse message as is if it was received.
2024-04-05 12:36:30 +00:00
|
|
|
sess, xact, message);
|
2020-04-26 19:36:05 +00:00
|
|
|
break;
|
2020-07-04 03:14:48 +00:00
|
|
|
|
2020-04-26 19:36:05 +00:00
|
|
|
case OGS_PFCP_SESSION_DELETION_RESPONSE_TYPE:
|
2022-06-30 01:35:03 +00:00
|
|
|
if (!message->h.seid_presence) ogs_error("No SEID");
|
|
|
|
|
2022-08-04 00:55:17 +00:00
|
|
|
if (!sess) {
|
|
|
|
ogs_gtp_xact_t *gtp_xact = xact->assoc_xact;
|
2022-10-25 12:22:14 +00:00
|
|
|
ogs_error("No Session");
|
2022-08-04 00:55:17 +00:00
|
|
|
if (!gtp_xact) {
|
|
|
|
ogs_error("No associated GTP transaction");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (gtp_xact->gtp_version == 1)
|
|
|
|
ogs_gtp1_send_error_message(gtp_xact, 0,
|
|
|
|
OGS_GTP1_DELETE_PDP_CONTEXT_RESPONSE_TYPE,
|
|
|
|
OGS_GTP1_CAUSE_CONTEXT_NOT_FOUND);
|
|
|
|
else
|
2024-03-25 23:04:26 +00:00
|
|
|
ogs_gtp2_send_error_message(gtp_xact, 0,
|
2022-08-04 00:55:17 +00:00
|
|
|
OGS_GTP2_DELETE_SESSION_RESPONSE_TYPE,
|
|
|
|
OGS_GTP2_CAUSE_CONTEXT_NOT_FOUND);
|
|
|
|
break;
|
|
|
|
}
|
2022-04-20 12:42:18 +00:00
|
|
|
ogs_fsm_dispatch(&sess->sm, e);
|
2020-04-26 19:36:05 +00:00
|
|
|
break;
|
2020-07-04 03:14:48 +00:00
|
|
|
|
2021-01-18 16:48:35 +00:00
|
|
|
case OGS_PFCP_SESSION_REPORT_REQUEST_TYPE:
|
2022-06-30 01:35:03 +00:00
|
|
|
if (!message->h.seid_presence) ogs_error("No SEID");
|
2022-06-30 00:11:38 +00:00
|
|
|
|
2021-01-18 16:48:35 +00:00
|
|
|
smf_n4_handle_session_report_request(
|
[PFCP/GTP] Incorrect TEI/SEID=0 (#3043)
If eg. PCRF or AAA diameter link is not yet ready (eg. PCRF crashed),
and a client sends a CreateSessionRequest announcing its ow F-TEID,
then open5gs-smfd answers with Create Session Response Cause="Remote peer not responding",
but it is not setting the received F-TEID in the header of the response,
instead it sends with TEID=0.
As a result, the peer cannot match the CreateSessionResponse,
and needs to rely on its own timeout timer to figure out
that specific request failed.
See 3GPP TS 29.274 5.5 Usage of the GTPv2-C Header:
```
Bit 4 represents a "T" flag, which indicates if TEID field is present in the GTP-C header or not. If the "T" flag is
set to 0, then the TEID field shall not be present in the GTP-C header. If the "T" flag is set to 1, then the TEID
field shall immediately follow the Length field, in octets 5 to 8. Apart from the Echo Request, Echo Response
and Version Not Supported Indication messages, in all EPC specific messages the value of the "T" flag shall be
set to "1".
```
This happens with Delete Session Requests and can happen with any PFCP message.
I've fixed TEID/SEID to send the value in the reponse message as is if it was received.
2024-04-05 12:36:30 +00:00
|
|
|
sess, xact, message);
|
2021-01-18 16:48:35 +00:00
|
|
|
break;
|
|
|
|
|
2020-04-26 19:36:05 +00:00
|
|
|
default:
|
|
|
|
ogs_error("Not implemented PFCP message type[%d]",
|
|
|
|
message->h.type);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
case SMF_EVT_N4_TIMER:
|
2022-08-12 05:03:53 +00:00
|
|
|
switch(e->h.timer_id) {
|
2020-07-27 01:02:40 +00:00
|
|
|
case SMF_TIMER_PFCP_NO_HEARTBEAT:
|
2020-04-26 19:36:05 +00:00
|
|
|
node = e->pfcp_node;
|
|
|
|
ogs_assert(node);
|
|
|
|
|
2021-06-06 13:35:46 +00:00
|
|
|
ogs_assert(OGS_OK ==
|
|
|
|
ogs_pfcp_send_heartbeat_request(node, node_timeout));
|
2020-04-26 19:36:05 +00:00
|
|
|
break;
|
2023-07-10 12:13:28 +00:00
|
|
|
case SMF_TIMER_PFCP_NO_ESTABLISHMENT_RESPONSE:
|
2024-03-24 00:46:25 +00:00
|
|
|
sess = smf_sess_cycle(e->sess);
|
2023-07-10 12:13:28 +00:00
|
|
|
if (!sess) {
|
|
|
|
ogs_warn("Session has already been removed");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ogs_fsm_dispatch(&sess->sm, e);
|
|
|
|
break;
|
2024-03-24 00:46:25 +00:00
|
|
|
case SMF_TIMER_PFCP_NO_DELETION_RESPONSE:
|
|
|
|
sess = smf_sess_cycle(e->sess);
|
|
|
|
if (!sess) {
|
|
|
|
ogs_warn("Session has already been removed");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
SMF_SESS_CLEAR(sess);
|
|
|
|
break;
|
2020-04-26 19:36:05 +00:00
|
|
|
default:
|
|
|
|
ogs_error("Unknown timer[%s:%d]",
|
2022-08-12 05:03:53 +00:00
|
|
|
smf_timer_get_name(e->h.timer_id), e->h.timer_id);
|
2020-04-26 19:36:05 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SMF_EVT_N4_NO_HEARTBEAT:
|
|
|
|
ogs_warn("No Heartbeat from UPF [%s]:%d",
|
|
|
|
OGS_ADDR(addr, buf), OGS_PORT(addr));
|
2024-03-24 00:46:25 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* reselect_upf() should not be executed on node_timeout
|
|
|
|
* because the timer cannot be deleted in the timer expiration function.
|
|
|
|
*
|
|
|
|
* Note that reselct_upf contains SMF_SESS_CLEAR.
|
|
|
|
*/
|
|
|
|
node = e->pfcp_node;
|
|
|
|
ogs_assert(node);
|
|
|
|
reselect_upf(node);
|
|
|
|
|
2020-04-26 19:36:05 +00:00
|
|
|
OGS_FSM_TRAN(s, smf_pfcp_state_will_associate);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ogs_error("Unknown event %s", smf_event_get_name(e));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void smf_pfcp_state_exception(ogs_fsm_t *s, smf_event_t *e)
|
|
|
|
{
|
|
|
|
ogs_assert(s);
|
|
|
|
ogs_assert(e);
|
|
|
|
|
|
|
|
smf_sm_debug(e);
|
|
|
|
|
2022-08-12 05:03:53 +00:00
|
|
|
switch (e->h.id) {
|
2020-04-26 19:36:05 +00:00
|
|
|
case OGS_FSM_ENTRY_SIG:
|
|
|
|
break;
|
|
|
|
case OGS_FSM_EXIT_SIG:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ogs_error("Unknown event %s", smf_event_get_name(e));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-08-13 00:31:22 +00:00
|
|
|
|
2023-04-15 09:54:03 +00:00
|
|
|
static void pfcp_restoration(ogs_pfcp_node_t *node)
|
|
|
|
{
|
|
|
|
smf_ue_t *smf_ue = NULL;
|
|
|
|
|
|
|
|
char buf1[OGS_ADDRSTRLEN];
|
|
|
|
char buf2[OGS_ADDRSTRLEN];
|
|
|
|
|
|
|
|
ogs_list_for_each(&smf_self()->smf_ue_list, smf_ue) {
|
|
|
|
smf_sess_t *sess = NULL;
|
|
|
|
ogs_assert(smf_ue);
|
|
|
|
|
|
|
|
ogs_list_for_each(&smf_ue->sess_list, sess) {
|
|
|
|
ogs_assert(sess);
|
|
|
|
|
|
|
|
if (node == sess->pfcp_node) {
|
|
|
|
if (sess->epc) {
|
|
|
|
ogs_info("UE IMSI[%s] APN[%s] IPv4[%s] IPv6[%s]",
|
|
|
|
smf_ue->imsi_bcd, sess->session.name,
|
|
|
|
sess->ipv4 ?
|
|
|
|
OGS_INET_NTOP(&sess->ipv4->addr, buf1) : "",
|
|
|
|
sess->ipv6 ?
|
|
|
|
OGS_INET6_NTOP(&sess->ipv6->addr, buf2) : "");
|
|
|
|
ogs_assert(OGS_OK ==
|
|
|
|
smf_epc_pfcp_send_session_establishment_request(
|
|
|
|
sess, NULL,
|
|
|
|
OGS_PFCP_CREATE_RESTORATION_INDICATION));
|
|
|
|
} else {
|
|
|
|
ogs_info("UE SUPI[%s] DNN[%s] IPv4[%s] IPv6[%s]",
|
|
|
|
smf_ue->supi, sess->session.name,
|
|
|
|
sess->ipv4 ?
|
|
|
|
OGS_INET_NTOP(&sess->ipv4->addr, buf1) : "",
|
|
|
|
sess->ipv6 ?
|
|
|
|
OGS_INET6_NTOP(&sess->ipv6->addr, buf2) : "");
|
|
|
|
ogs_assert(OGS_OK ==
|
|
|
|
smf_5gc_pfcp_send_session_establishment_request(
|
|
|
|
sess, OGS_PFCP_CREATE_RESTORATION_INDICATION));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-04 12:22:03 +00:00
|
|
|
static void reselect_upf(ogs_pfcp_node_t *node)
|
2020-08-13 00:31:22 +00:00
|
|
|
{
|
2023-04-04 12:22:03 +00:00
|
|
|
int r;
|
2023-04-15 09:54:03 +00:00
|
|
|
smf_ue_t *smf_ue = NULL;
|
2023-04-04 12:22:03 +00:00
|
|
|
ogs_pfcp_node_t *iter = NULL;
|
2020-08-13 00:31:22 +00:00
|
|
|
|
2023-04-04 12:22:03 +00:00
|
|
|
ogs_assert(node);
|
2020-08-13 00:31:22 +00:00
|
|
|
|
2023-04-04 12:22:03 +00:00
|
|
|
if (node->restoration_required == true) {
|
|
|
|
ogs_error("UPF has already been restarted");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ogs_list_for_each(&ogs_pfcp_self()->pfcp_peer_list, iter) {
|
|
|
|
if (iter == node)
|
|
|
|
continue;
|
|
|
|
if (OGS_FSM_CHECK(&iter->sm, smf_pfcp_state_associated))
|
|
|
|
break;
|
|
|
|
}
|
2023-03-06 12:04:43 +00:00
|
|
|
|
2023-04-04 12:22:03 +00:00
|
|
|
if (iter == NULL) {
|
2023-11-19 10:34:51 +00:00
|
|
|
ogs_error("No UPF available");
|
2023-04-04 12:22:03 +00:00
|
|
|
return;
|
|
|
|
}
|
2023-03-06 12:04:43 +00:00
|
|
|
|
2023-04-15 09:54:03 +00:00
|
|
|
ogs_list_for_each(&smf_self()->smf_ue_list, smf_ue) {
|
2023-08-10 13:14:48 +00:00
|
|
|
smf_sess_t *sess = NULL, *next_sess = NULL;
|
2023-04-04 12:22:03 +00:00
|
|
|
|
2023-08-10 13:14:48 +00:00
|
|
|
ogs_list_for_each_safe(&smf_ue->sess_list, next_sess, sess) {
|
2023-03-06 12:04:43 +00:00
|
|
|
|
2023-04-15 09:54:03 +00:00
|
|
|
if (node == sess->pfcp_node) {
|
|
|
|
if (sess->epc) {
|
|
|
|
ogs_error("[%s:%s] EPC restoration is not implemented",
|
|
|
|
smf_ue->imsi_bcd, sess->session.name);
|
|
|
|
} else {
|
2023-08-10 13:14:48 +00:00
|
|
|
if (sess->policy_association_id) {
|
|
|
|
smf_npcf_smpolicycontrol_param_t param;
|
|
|
|
|
|
|
|
ogs_info("[%s:%d] SMF-initiated Deletion",
|
|
|
|
smf_ue->supi, sess->psi);
|
|
|
|
ogs_assert(sess->sm_context_ref);
|
|
|
|
memset(¶m, 0, sizeof(param));
|
|
|
|
r = smf_sbi_discover_and_send(
|
|
|
|
OGS_SBI_SERVICE_TYPE_NPCF_SMPOLICYCONTROL, NULL,
|
|
|
|
smf_npcf_smpolicycontrol_build_delete,
|
|
|
|
sess, NULL,
|
|
|
|
OGS_PFCP_DELETE_TRIGGER_SMF_INITIATED,
|
|
|
|
¶m);
|
|
|
|
ogs_expect(r == OGS_OK);
|
|
|
|
ogs_assert(r != OGS_ERROR);
|
|
|
|
} else {
|
|
|
|
ogs_error("[%s:%d] No PolicyAssociationId. "
|
|
|
|
"Forcibly remove SESSION",
|
|
|
|
smf_ue->supi, sess->psi);
|
|
|
|
SMF_SESS_CLEAR(sess);
|
|
|
|
}
|
2023-03-06 12:04:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-04-04 12:22:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void node_timeout(ogs_pfcp_xact_t *xact, void *data)
|
|
|
|
{
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
smf_event_t *e = NULL;
|
|
|
|
uint8_t type;
|
|
|
|
|
|
|
|
ogs_assert(xact);
|
|
|
|
type = xact->seq[0].type;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case OGS_PFCP_HEARTBEAT_REQUEST_TYPE:
|
|
|
|
ogs_assert(data);
|
2020-08-13 00:31:22 +00:00
|
|
|
|
|
|
|
e = smf_event_new(SMF_EVT_N4_NO_HEARTBEAT);
|
|
|
|
e->pfcp_node = data;
|
|
|
|
|
2020-08-26 03:05:01 +00:00
|
|
|
rv = ogs_queue_push(ogs_app()->queue, e);
|
2020-08-13 00:31:22 +00:00
|
|
|
if (rv != OGS_OK) {
|
2022-07-16 04:27:18 +00:00
|
|
|
ogs_error("ogs_queue_push() failed:%d", (int)rv);
|
2022-08-12 05:03:53 +00:00
|
|
|
ogs_event_free(e);
|
2020-08-13 00:31:22 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ogs_error("Not implemented [type:%d]", type);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|