pespin has uploaded this change for review.

View Change

Split up_peer into pfcp_{node,entity}_peer

Match our data domain with the specifications in 3GPP TS 29.244:
"""
CP function: A node with a Control Plane function (see 3GPP TS 23.214 [2]) supporting one or more PFCP entities. A
Control Plane function, i.e. a Control Plane Node, is identified by the Node ID that is set to either an FQDN or an IP
address.

UP function: A node with a User Plane function (see 3GPP TS 23.214 [2]) supporting one or more PFCP entities. A
User Plane function, i.e. a User Plane Node, is identified by the Node ID that is set to either a FQDN or an IP address.

Node: Either a CP function or an UP function supporting one or more PFCP entities. A Node is identified by the Node
ID, which is set to either an FQDN or an IP address.

PFCP Entity: An endpoint in a CP (or UP) function supporting PFCP, that is identified by the IP address. The IP
address of a PFCP entity may or may not be the IP address included in the Node ID.
"""

In general, we have a "pfcp_node_peer 1-1 pfcp_entity_peer" relation, and
this is what so far is implemented here. In the future we can improve
code to support a "pfcp_node_peer 1-* pfcp_entity_peer" relation, which
can actually be implemented in 2 different ways (2 different features)
as explained in 3GPP TS 29.244 section 5.22.

Due to those 2 different features, it's actually difficult to figure out
where to install the FSM with the association state (entity vs node).
For now, leave it at the pfcp_entity_peer level, in order to simplify
the implementation and make the changes in this patch easier to follow.
Hence, pfcp_node_peer is so far only used to track Node-Id changes, and
is released as soon as all its PFCP Entities are released.

Related: SYS#7719
Change-Id: Ie64be4fd55157e9646e250f8cbe93ba3f3c19b17
---
M include/osmocom/upf/Makefile.am
A include/osmocom/upf/pfcp_entity_peer.h
A include/osmocom/upf/pfcp_node_peer.h
M include/osmocom/upf/up_endpoint.h
D include/osmocom/upf/up_peer.h
M include/osmocom/upf/up_session.h
M src/osmo-upf/Makefile.am
A src/osmo-upf/pfcp_entity_peer.c
A src/osmo-upf/pfcp_node_peer.c
M src/osmo-upf/up_endpoint.c
M src/osmo-upf/up_gtp_action.c
D src/osmo-upf/up_peer.c
M src/osmo-upf/up_session.c
M src/osmo-upf/upf.c
M src/osmo-upf/upf_vty.c
M tests/unique_ids/unique_ids_test.c
M tests/unique_ids/unique_ids_test.err
17 files changed, 1,152 insertions(+), 850 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/osmo-upf refs/changes/70/41470/1
diff --git a/include/osmocom/upf/Makefile.am b/include/osmocom/upf/Makefile.am
index e8eda55..9350ee8 100644
--- a/include/osmocom/upf/Makefile.am
+++ b/include/osmocom/upf/Makefile.am
@@ -1,7 +1,8 @@
noinst_HEADERS = \
+ pfcp_entity_peer.h \
+ pfcp_node_peer.h \
netinst.h \
up_endpoint.h \
- up_peer.h \
up_session.h \
upf.h \
upf_gtp.h \
diff --git a/include/osmocom/upf/pfcp_entity_peer.h b/include/osmocom/upf/pfcp_entity_peer.h
new file mode 100644
index 0000000..d2727e5
--- /dev/null
+++ b/include/osmocom/upf/pfcp_entity_peer.h
@@ -0,0 +1,82 @@
+/* 3GPP TS 29.244 PFCP Entity
+ * (C) 2021-2025 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved.
+ *
+ * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/hashtable.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/use_count.h>
+
+#include <osmocom/pfcp/pfcp_msg.h>
+
+enum pfcp_entity_peer_event {
+ PFCP_ENTITY_PEER_EV_RX_ASSOC_SETUP_REQ,
+ PFCP_ENTITY_PEER_EV_RX_ASSOC_UPD_REQ,
+ PFCP_ENTITY_PEER_EV_RX_ASSOC_REL_REQ,
+ PFCP_ENTITY_PEER_EV_RX_SESSION_EST_REQ,
+ PFCP_ENTITY_PEER_EV_HEARTBEAT_FAILURE,
+ PFCP_ENTITY_PEER_EV_USE_COUNT_ZERO,
+ PFCP_ENTITY_PEER_EV_SESSION_TERM,
+};
+
+struct pfcp_entity_peer {
+ /* item in pfcp_node_peer->entity_list */
+ struct llist_head entry;
+
+ struct osmo_fsm_inst *fi;
+ /* backpointer */
+ struct pfcp_node_peer *node_peer;
+
+ /* peer's remote address */
+ struct osmo_sockaddr remote_addr;
+ uint32_t remote_recovery_timestamp;
+
+ uint32_t next_seq_nr;
+
+ struct osmo_fsm_inst *heartbeat_fi;
+
+ struct osmo_use_count use_count;
+ struct osmo_use_count_entry use_count_buf[5];
+
+ DECLARE_HASHTABLE(sessions_by_up_seid, 10);
+ DECLARE_HASHTABLE(sessions_by_cp_seid, 10);
+};
+
+struct pfcp_entity_peer *pfcp_entity_peer_alloc(struct pfcp_node_peer *node_peer, const struct osmo_sockaddr *remote_addr);
+void pfcp_entity_peer_free(struct pfcp_entity_peer *peer);
+
+void pfcp_entity_peer_set_msg_ctx(struct pfcp_entity_peer *entity_peer, struct osmo_pfcp_msg *m);
+void pfcp_entity_peer_remove_msg_ctx(struct pfcp_entity_peer *peer, struct osmo_pfcp_msg *m);
+
+char *pfcp_entity_peer_remote_addr_str(struct pfcp_entity_peer *entity_peer);
+
+struct osmo_pfcp_msg *pfcp_entity_peer_init_tx(struct pfcp_entity_peer *entity_peer,
+ struct osmo_pfcp_msg *in_reply_to,
+ enum osmo_pfcp_message_type message_type);
+
+struct up_session *pfcp_entity_peer_find_up_session_by_up_seid(struct pfcp_entity_peer *entity_peer, uint64_t up_seid);
+struct up_session *pfcp_entity_peer_find_up_session_by_cp_f_seid(struct pfcp_entity_peer *entity_peer, const struct osmo_pfcp_ie_f_seid *cp_f_seid);
+
diff --git a/include/osmocom/upf/pfcp_node_peer.h b/include/osmocom/upf/pfcp_node_peer.h
new file mode 100644
index 0000000..a28eea9
--- /dev/null
+++ b/include/osmocom/upf/pfcp_node_peer.h
@@ -0,0 +1,64 @@
+/* 3GPP TS 29.244 PFCP Node
+ * (C) 2021-2025 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved.
+ *
+ * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/hashtable.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/use_count.h>
+
+#include <osmocom/pfcp/pfcp_msg.h>
+
+struct pfcp_node_peer {
+ /* item in up_endpoint->pfcp_node_peer_list */
+ struct llist_head entry;
+
+ /* list of struct pfcp_entity_peer. */
+ struct llist_head entity_list;
+
+ struct osmo_fsm_inst *fi;
+ /* backpointer */
+ struct up_endpoint *up_endpoint;
+
+ /* peer's remote address */
+ struct osmo_pfcp_ie_node_id node_id;
+
+ struct osmo_pfcp_ie_up_function_features local_up_features;
+ struct osmo_pfcp_ie_cp_function_features peer_cp_features;
+
+ struct osmo_use_count use_count;
+ struct osmo_use_count_entry use_count_buf[5];
+};
+
+struct pfcp_node_peer *pfcp_node_peer_alloc(struct up_endpoint *up_endpoint, const struct osmo_pfcp_ie_node_id *node_id);
+void pfcp_node_peer_free(struct pfcp_node_peer *peer);
+
+struct pfcp_entity_peer *pfcp_node_peer_find_entity_by_remote_addr(struct pfcp_node_peer *peer, const struct osmo_sockaddr *remote_addr);
+char *pfcp_node_peer_node_id_str(struct pfcp_node_peer *node_peer);
+
+#define pfcp_node_peer_get(PFCP_NODE_PEER, USE) OSMO_ASSERT(osmo_use_count_get_put(&(PFCP_NODE_PEER)->use_count, USE, 1) == 0)
+#define pfcp_node_peer_put(PFCP_NODE_PEER, USE) OSMO_ASSERT(osmo_use_count_get_put(&(PFCP_NODE_PEER)->use_count, USE, -1) == 0)
+
+#define LOG_PNP(PFCP_NODE_PEER, LEVEL, FMT, ARGS...) \
+ LOGP(DGTP, LEVEL, "pfcp_node_peer(%s): " FMT, pfcp_node_peer_node_id_str(PFCP_NODE_PEER), ##ARGS)
diff --git a/include/osmocom/upf/up_endpoint.h b/include/osmocom/upf/up_endpoint.h
index 2aed25a..03fd23d 100644
--- a/include/osmocom/upf/up_endpoint.h
+++ b/include/osmocom/upf/up_endpoint.h
@@ -1,5 +1,5 @@
/*
- * (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * (C) 2021-2025 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved.
*
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
@@ -30,14 +30,16 @@
struct osmo_pfcp_endpoint;
struct osmo_sockaddr;

+struct pfcp_node_peer;
+
#define UP_USE_MSG_RX "msg-rx"
#define UP_USE_MSG_TX "msg-tx"

struct up_endpoint {
struct osmo_pfcp_endpoint *pfcp_ep;

- /* list of struct up_peer. */
- struct llist_head peers;
+ /* list of struct pfcp_node_peer. */
+ struct llist_head pfcp_node_peer_list;
/* hashtable of (struct up_session) with key up_seid.
* Allows quick access to sessions (and its endpoint as backpointer)
* with a given up_seid. */
@@ -50,4 +52,6 @@
int up_endpoint_bind(struct up_endpoint *up_ep);
void up_endpoint_free(struct up_endpoint **ep);

+struct pfcp_node_peer *up_endpoint_find_pfcp_node_peer(const struct up_endpoint *up_ep,
+ const struct osmo_pfcp_ie_node_id *node_id);
uint64_t up_endpoint_next_up_seid(struct up_endpoint *ep);
diff --git a/include/osmocom/upf/up_peer.h b/include/osmocom/upf/up_peer.h
deleted file mode 100644
index 559b87c..0000000
--- a/include/osmocom/upf/up_peer.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
- * All Rights Reserved.
- *
- * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
- *
- * SPDX-License-Identifier: GPL-2.0+
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#pragma once
-
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/hashtable.h>
-#include <osmocom/core/socket.h>
-#include <osmocom/core/use_count.h>
-
-#include <osmocom/pfcp/pfcp_msg.h>
-
-enum up_peer_event {
- UP_PEER_EV_RX_ASSOC_SETUP_REQ,
- UP_PEER_EV_RX_ASSOC_UPD_REQ,
- UP_PEER_EV_RX_ASSOC_REL_REQ,
- UP_PEER_EV_RX_SESSION_EST_REQ,
- UP_PEER_EV_HEARTBEAT_FAILURE,
- UP_PEER_EV_USE_COUNT_ZERO,
- UP_PEER_EV_SESSION_TERM,
-};
-
-struct up_peer {
- /* item in up_endpoint->peers */
- struct llist_head entry;
-
- struct osmo_fsm_inst *fi;
- /* backpointer */
- struct up_endpoint *up_endpoint;
-
- /* peer's remote address */
- struct osmo_sockaddr remote_addr;
- struct osmo_pfcp_ie_node_id remote_node_id;
- uint32_t remote_recovery_timestamp;
-
- struct osmo_pfcp_ie_up_function_features local_up_features;
- struct osmo_pfcp_ie_cp_function_features peer_cp_features;
-
- uint32_t next_seq_nr;
-
- struct osmo_fsm_inst *heartbeat_fi;
-
- struct osmo_use_count use_count;
- struct osmo_use_count_entry use_count_buf[5];
-
- DECLARE_HASHTABLE(sessions_by_up_seid, 10);
- DECLARE_HASHTABLE(sessions_by_cp_seid, 10);
-};
-
-struct up_peer *up_peer_find_or_add(struct up_endpoint *up_ep, const struct osmo_sockaddr *remote_addr);
-struct up_peer *up_peer_find(struct up_endpoint *up_ep, const struct osmo_sockaddr *remote_addr);
-
-void up_peer_set_msg_ctx(struct up_peer *peer, struct osmo_pfcp_msg *m);
-
-char *up_peer_remote_addr_str(struct up_peer *peer);
-
-struct osmo_pfcp_msg *up_peer_init_tx(struct up_peer *peer, struct osmo_pfcp_msg *in_reply_to,
- enum osmo_pfcp_message_type message_type);
-
-void up_peer_free(struct up_peer *peer);
diff --git a/include/osmocom/upf/up_session.h b/include/osmocom/upf/up_session.h
index a20ae06..d08a95e 100644
--- a/include/osmocom/upf/up_session.h
+++ b/include/osmocom/upf/up_session.h
@@ -32,7 +32,7 @@

struct osmo_fsm_inst;
struct osmo_pfcp_msg;
-struct up_peer;
+struct pfcp_entity_peer;

enum up_session_fsm_event {
UP_SESSION_EV_RX_SESSION_EST_REQ,
@@ -50,7 +50,7 @@

struct osmo_fsm_inst *fi;
/* backpointer */
- struct up_peer *up_peer;
+ struct pfcp_entity_peer *entity_peer;

struct osmo_pfcp_ie_f_seid cp_f_seid;
uint64_t up_seid;
@@ -69,10 +69,7 @@
struct llist_head active_gtp_actions;
};

-struct up_session *up_session_find_or_add(struct up_peer *peer, const struct osmo_pfcp_ie_f_seid *cp_f_seid);
-struct up_session *up_session_find_by_up_seid(struct up_peer *peer, uint64_t up_seid);
-struct up_session *up_session_find_by_cp_f_seid(struct up_peer *peer, const struct osmo_pfcp_ie_f_seid *cp_f_seid);
-
+struct up_session *up_session_alloc(struct pfcp_entity_peer *entity_peer, const struct osmo_pfcp_ie_f_seid *cp_f_seid);
void up_session_set_msg_ctx(struct up_session *session, struct osmo_pfcp_msg *m);

char *up_session_gtp_status(struct up_session *session);
diff --git a/src/osmo-upf/Makefile.am b/src/osmo-upf/Makefile.am
index be72bc2..21ca4b7 100644
--- a/src/osmo-upf/Makefile.am
+++ b/src/osmo-upf/Makefile.am
@@ -26,10 +26,11 @@
$(NULL)

libupf_la_SOURCES = \
+ pfcp_entity_peer.c \
+ pfcp_node_peer.c \
netinst.c \
up_endpoint.c \
up_gtp_action.c \
- up_peer.c \
up_session.c \
upf.c \
upf_gtp.c \
diff --git a/src/osmo-upf/pfcp_entity_peer.c b/src/osmo-upf/pfcp_entity_peer.c
new file mode 100644
index 0000000..db02f78
--- /dev/null
+++ b/src/osmo-upf/pfcp_entity_peer.c
@@ -0,0 +1,557 @@
+/*
+ * (C) 2021-2025 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved.
+ *
+ * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/core/utils.h>
+
+#include <osmocom/pfcp/pfcp_msg.h>
+#include <osmocom/pfcp/pfcp_endpoint.h>
+
+#include <osmocom/upf/upf.h>
+#include <osmocom/upf/pfcp_entity_peer.h>
+#include <osmocom/upf/pfcp_node_peer.h>
+#include <osmocom/upf/up_endpoint.h>
+#include <osmocom/upf/up_session.h>
+
+enum pfcp_entity_peer_fsm_state {
+ PFCP_ENTITY_PEER_ST_NOT_ASSOCIATED,
+ PFCP_ENTITY_PEER_ST_ASSOCIATED,
+ PFCP_ENTITY_PEER_ST_GRACEFUL_RELEASE,
+ PFCP_ENTITY_PEER_ST_WAIT_USE_COUNT,
+};
+
+static const struct value_string pfcp_entity_peer_fsm_event_names[] = {
+ OSMO_VALUE_STRING(PFCP_ENTITY_PEER_EV_RX_ASSOC_SETUP_REQ),
+ OSMO_VALUE_STRING(PFCP_ENTITY_PEER_EV_RX_ASSOC_UPD_REQ),
+ OSMO_VALUE_STRING(PFCP_ENTITY_PEER_EV_RX_ASSOC_REL_REQ),
+ OSMO_VALUE_STRING(PFCP_ENTITY_PEER_EV_RX_SESSION_EST_REQ),
+ OSMO_VALUE_STRING(PFCP_ENTITY_PEER_EV_HEARTBEAT_FAILURE),
+ OSMO_VALUE_STRING(PFCP_ENTITY_PEER_EV_USE_COUNT_ZERO),
+ OSMO_VALUE_STRING(PFCP_ENTITY_PEER_EV_SESSION_TERM),
+ {}
+};
+
+static struct osmo_fsm pfcp_entity_peer_fsm;
+
+static const struct osmo_tdef_state_timeout pfcp_entity_peer_fsm_timeouts[32] = {
+ [PFCP_ENTITY_PEER_ST_GRACEFUL_RELEASE] = { .T = -21 },
+};
+
+/* Transition to a state, using the T timer defined in pfcp_entity_peer_fsm_timeouts.
+ * Assumes local variable fi exists. */
+#define pfcp_entity_peer_fsm_state_chg(state) \
+ osmo_tdef_fsm_inst_state_chg(fi, state, \
+ pfcp_entity_peer_fsm_timeouts, \
+ osmo_pfcp_tdefs, \
+ 5)
+
+static int pfcp_entity_peer_use_cb(struct osmo_use_count_entry *e, int32_t old_use_count, const char *file, int line)
+{
+ struct pfcp_entity_peer *peer = e->use_count->talloc_object;
+ int32_t total;
+ int level;
+
+ if (!e->use)
+ return -EINVAL;
+
+ total = osmo_use_count_total(&peer->use_count);
+
+ if (total == 0
+ || (total == 1 && old_use_count == 0 && e->count == 1))
+ level = LOGL_INFO;
+ else
+ level = LOGL_DEBUG;
+
+ LOGPFSMSLSRC(peer->fi, DREF, level, file, line,
+ "%s %s: now used by %s\n",
+ (e->count - old_use_count) > 0 ? "+" : "-", e->use,
+ osmo_use_count_to_str_c(OTC_SELECT, &peer->use_count));
+
+ if (e->count < 0)
+ return -ERANGE;
+
+ if (total == 0)
+ osmo_fsm_inst_dispatch(peer->fi, PFCP_ENTITY_PEER_EV_USE_COUNT_ZERO, NULL);
+ return 0;
+}
+
+char *pfcp_entity_peer_remote_addr_str(struct pfcp_entity_peer *peer)
+{
+ struct osmo_sockaddr remote_addr = peer->remote_addr;
+
+ /* Zero the port, it is not interesting information. The port for PFCP is defined fixed, and there is no use
+ * printing it in the logs */
+ osmo_sockaddr_set_port(&remote_addr.u.sa, 0);
+
+ return osmo_sockaddr_to_str_c(OTC_SELECT, &remote_addr);
+}
+
+static void pfcp_entity_peer_update_id(struct pfcp_entity_peer *peer)
+{
+ osmo_fsm_inst_update_id_f_sanitize(peer->fi, '-', "%s", pfcp_entity_peer_remote_addr_str(peer));
+ LOGPFSML(peer->fi, LOGL_DEBUG, "Updated id\n");
+}
+
+struct pfcp_entity_peer *pfcp_entity_peer_alloc(struct pfcp_node_peer *node_peer, const struct osmo_sockaddr *remote_addr)
+{
+ struct pfcp_entity_peer *entity_peer;
+
+ struct osmo_fsm_inst *fi = osmo_fsm_inst_alloc(&pfcp_entity_peer_fsm, node_peer, NULL, LOGL_DEBUG, NULL);
+ OSMO_ASSERT(fi);
+
+ entity_peer = talloc(fi, struct pfcp_entity_peer);
+ OSMO_ASSERT(entity_peer);
+ fi->priv = entity_peer;
+
+ *entity_peer = (struct pfcp_entity_peer) {
+ .fi = fi,
+ .node_peer = node_peer,
+ .remote_addr = *remote_addr,
+ .heartbeat_fi = NULL /* FIXME */,
+ .use_count = {
+ .talloc_object = entity_peer,
+ .use_cb = pfcp_entity_peer_use_cb,
+ },
+ };
+ osmo_use_count_make_static_entries(&entity_peer->use_count, entity_peer->use_count_buf, ARRAY_SIZE(entity_peer->use_count_buf));
+ hash_init(entity_peer->sessions_by_up_seid);
+ hash_init(entity_peer->sessions_by_cp_seid);
+
+ pfcp_entity_peer_update_id(entity_peer);
+
+ pfcp_node_peer_get(node_peer, fi->id);
+ llist_add(&entity_peer->entry, &node_peer->entity_list);
+ return entity_peer;
+}
+
+struct up_session *pfcp_entity_peer_find_up_session_by_up_seid(struct pfcp_entity_peer *entity_peer, uint64_t up_seid)
+{
+ struct up_session *session;
+ hash_for_each_possible(entity_peer->sessions_by_up_seid, session, node_by_up_seid, up_seid) {
+ if (up_seid == session->up_seid)
+ return session;
+ }
+ return NULL;
+}
+
+struct up_session *pfcp_entity_peer_find_up_session_by_cp_f_seid(struct pfcp_entity_peer *entity_peer, const struct osmo_pfcp_ie_f_seid *cp_f_seid)
+{
+ struct up_session *session;
+ hash_for_each_possible(entity_peer->sessions_by_cp_seid, session, node_by_cp_seid, cp_f_seid->seid) {
+ if (osmo_pfcp_ie_f_seid_cmp(&session->cp_f_seid, cp_f_seid) == 0)
+ return session;
+ }
+ return NULL;
+}
+
+static int pfcp_entity_peer_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+ //struct pfcp_entity_peer *peer = fi->priv;
+ /* Return 1 to terminate FSM instance, 0 to keep running */
+ return 1;
+}
+
+void pfcp_entity_peer_set_msg_ctx(struct pfcp_entity_peer *peer, struct osmo_pfcp_msg *m)
+{
+ OSMO_ASSERT(!m->ctx.peer_fi);
+
+ m->ctx.peer_fi = peer->fi;
+ m->ctx.peer_use_count = &peer->use_count;
+ m->ctx.peer_use_token = (m->rx ? UP_USE_MSG_RX : UP_USE_MSG_TX);
+ OSMO_ASSERT(osmo_use_count_get_put(m->ctx.peer_use_count, m->ctx.peer_use_token, 1) == 0);
+}
+
+void pfcp_entity_peer_remove_msg_ctx(struct pfcp_entity_peer *peer, struct osmo_pfcp_msg *m)
+{
+ OSMO_ASSERT(m->ctx.peer_fi && m->ctx.peer_fi == peer->fi);
+ OSMO_ASSERT(osmo_use_count_get_put(m->ctx.peer_use_count, m->ctx.peer_use_token, -1) == 0);
+ m->ctx.peer_fi = NULL;
+ m->ctx.peer_use_count = NULL;
+ m->ctx.peer_use_token = NULL;
+}
+
+struct osmo_pfcp_msg *pfcp_entity_peer_init_tx(struct pfcp_entity_peer *peer,
+ struct osmo_pfcp_msg *in_reply_to,
+ enum osmo_pfcp_message_type message_type)
+{
+ struct osmo_pfcp_msg *tx;
+ if (in_reply_to)
+ tx = osmo_pfcp_msg_alloc_tx_resp(OTC_SELECT, in_reply_to, message_type);
+ else
+ tx = osmo_pfcp_msg_alloc_tx_req(OTC_SELECT, &peer->remote_addr, message_type);
+ pfcp_entity_peer_set_msg_ctx(peer, tx);
+ return tx;
+}
+
+static int pfcp_entity_peer_tx_assoc_setup_resp(struct pfcp_entity_peer *entity_peer, struct osmo_pfcp_msg *m, enum osmo_pfcp_cause cause)
+{
+ struct osmo_pfcp_msg *resp;
+
+ resp = pfcp_entity_peer_init_tx(entity_peer, m, OSMO_PFCP_MSGT_ASSOC_SETUP_RESP);
+
+ resp->ies.assoc_setup_resp = (struct osmo_pfcp_msg_assoc_setup_resp) {
+ .cause = cause,
+ .recovery_time_stamp = osmo_pfcp_endpoint_get_recovery_timestamp(g_upf->pfcp.ep->pfcp_ep),
+ .up_function_features_present = true,
+ .up_function_features = entity_peer->node_peer->local_up_features,
+ };
+
+ if (osmo_pfcp_endpoint_tx(entity_peer->node_peer->up_endpoint->pfcp_ep, resp)) {
+ OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Error sending response to this message,"
+ " cannot associate with peer\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static int pfcp_entity_peer_tx_assoc_rel_resp(struct pfcp_entity_peer *peer, struct osmo_pfcp_msg *m, enum osmo_pfcp_cause cause)
+{
+ struct osmo_pfcp_msg *resp;
+
+ resp = pfcp_entity_peer_init_tx(peer, m, OSMO_PFCP_MSGT_ASSOC_RELEASE_RESP);
+
+ resp->ies.assoc_release_resp = (struct osmo_pfcp_msg_assoc_release_resp) {
+ .cause = cause,
+ };
+
+ if (osmo_pfcp_endpoint_tx(peer->node_peer->up_endpoint->pfcp_ep, resp)) {
+ OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Error sending response to this message\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static void pfcp_entity_peer_clear_sessions(struct pfcp_entity_peer *peer)
+{
+ struct up_session *session;
+ int bkt;
+ struct hlist_node *tmp;
+ int count = 0;
+ hash_for_each_safe(peer->sessions_by_up_seid, bkt, tmp, session, node_by_up_seid) {
+ count += up_session_discard(session);
+ }
+ if (count)
+ LOGPFSML(peer->fi, LOGL_NOTICE, "terminated %d sessions\n", count);
+}
+
+static void pfcp_entity_peer_rx_assoc_setup_req(struct pfcp_entity_peer *peer, struct osmo_pfcp_msg *m)
+{
+ struct osmo_fsm_inst *fi = peer->fi;
+ enum osmo_pfcp_cause cause = OSMO_PFCP_CAUSE_REQUEST_ACCEPTED;
+
+ if (fi->state == PFCP_ENTITY_PEER_ST_ASSOCIATED) {
+ /* Retransmissions of the ACK response happen in pfcp_endpoint.c. So if we get this, it is a genuine
+ * duplicate association setup request. We could reject it. But why. Just "replace" with the new
+ * association. Continue. */
+ /* If the peer has restarted, it has forgotten about all sessions. */
+ if (peer->remote_recovery_timestamp != m->ies.assoc_setup_req.recovery_time_stamp) {
+ LOGPFSML(fi, LOGL_NOTICE, "another Association Setup Request, with different Recovery Timestamp."
+ " Clearing sessions, sending ACK.\n");
+ pfcp_entity_peer_clear_sessions(peer);
+ } else {
+ LOGPFSML(fi, LOGL_NOTICE, "another Association Setup Request, with same Recovery Timestamp."
+ " Keeping sessions, sending ACK.\n");
+ }
+ } else if (pfcp_entity_peer_fsm_state_chg(PFCP_ENTITY_PEER_ST_ASSOCIATED)) {
+ /* Not allowed to transition to ST_ASSOCIATED */
+ cause = OSMO_PFCP_CAUSE_REQUEST_REJECTED;
+ } else {
+ /* Successfully transitioned to ST_ASSOCIATED */
+ peer->remote_recovery_timestamp = m->ies.assoc_setup_req.recovery_time_stamp;
+ if (m->ies.assoc_setup_req.cp_function_features_present)
+ peer->node_peer->peer_cp_features = m->ies.assoc_setup_req.cp_function_features;
+ }
+
+ if (pfcp_entity_peer_tx_assoc_setup_resp(peer, m, cause)
+ || cause != OSMO_PFCP_CAUSE_REQUEST_ACCEPTED)
+ pfcp_entity_peer_fsm_state_chg(PFCP_ENTITY_PEER_ST_WAIT_USE_COUNT);
+
+ LOGPFSML(fi, LOGL_NOTICE, "Peer associated, Node-Id=%s. Local UP features: [%s]; Peer CP features: [%s]\n",
+ osmo_pfcp_ie_node_id_to_str_c(OTC_SELECT, &peer->node_peer->node_id),
+ osmo_pfcp_bits_to_str_c(OTC_SELECT, peer->node_peer->local_up_features.bits, osmo_pfcp_up_feature_strs),
+ osmo_pfcp_bits_to_str_c(OTC_SELECT, peer->node_peer->peer_cp_features.bits, osmo_pfcp_cp_feature_strs));
+}
+
+static void pfcp_entity_peer_rx_assoc_rel_req(struct pfcp_entity_peer *peer, struct osmo_pfcp_msg *m)
+{
+ struct osmo_fsm_inst *fi = peer->fi;
+ pfcp_entity_peer_tx_assoc_rel_resp(peer, m, OSMO_PFCP_CAUSE_REQUEST_ACCEPTED);
+ pfcp_entity_peer_fsm_state_chg(PFCP_ENTITY_PEER_ST_WAIT_USE_COUNT);
+}
+
+static void pfcp_entity_peer_rx_session_est_req(struct pfcp_entity_peer *peer, struct osmo_pfcp_msg *m)
+{
+ enum osmo_pfcp_cause cause = OSMO_PFCP_CAUSE_REQUEST_ACCEPTED;
+ struct osmo_pfcp_msg *resp;
+ struct up_session *session = pfcp_entity_peer_find_up_session_by_cp_f_seid(peer, &m->ies.session_est_req.cp_f_seid);
+ if (!session)
+ session = up_session_alloc(peer, &m->ies.session_est_req.cp_f_seid);
+ if (!session) {
+ cause = OSMO_PFCP_CAUSE_NO_RESOURCES_AVAILABLE;
+ goto nack_response;
+ }
+
+ up_session_set_msg_ctx(session, m);
+
+ if (osmo_fsm_inst_dispatch(session->fi, UP_SESSION_EV_RX_SESSION_EST_REQ, m)) {
+ cause = OSMO_PFCP_CAUSE_REQUEST_REJECTED;
+ goto nack_response;
+ }
+ return;
+
+nack_response:
+ resp = pfcp_entity_peer_init_tx(peer, m, OSMO_PFCP_MSGT_SESSION_EST_RESP);
+ resp->h.seid = m->ies.session_est_req.cp_f_seid.seid;
+ resp->h.seid_present = true;
+ resp->ies.session_est_resp = (struct osmo_pfcp_msg_session_est_resp){
+ .cause = cause,
+ };
+ osmo_pfcp_endpoint_tx(peer->node_peer->up_endpoint->pfcp_ep, resp);
+ if (session)
+ up_session_discard(session);
+}
+
+static void pfcp_entity_peer_not_associated_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct pfcp_entity_peer *peer = fi->priv;
+
+ switch (event) {
+
+ case PFCP_ENTITY_PEER_EV_RX_ASSOC_SETUP_REQ:
+ pfcp_entity_peer_rx_assoc_setup_req(peer, data);
+ break;
+
+ case PFCP_ENTITY_PEER_EV_USE_COUNT_ZERO:
+ /* Not associated and no pending messages. discard peer. */
+ pfcp_entity_peer_fsm_state_chg(PFCP_ENTITY_PEER_ST_WAIT_USE_COUNT);
+ return;
+
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static void pfcp_entity_peer_associated_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct pfcp_entity_peer *peer = fi->priv;
+
+ switch (event) {
+
+ case PFCP_ENTITY_PEER_EV_RX_ASSOC_SETUP_REQ:
+ pfcp_entity_peer_rx_assoc_setup_req(peer, data);
+ break;
+
+ case PFCP_ENTITY_PEER_EV_RX_ASSOC_UPD_REQ:
+ // FIXME
+ break;
+
+ case PFCP_ENTITY_PEER_EV_RX_SESSION_EST_REQ:
+ pfcp_entity_peer_rx_session_est_req(peer, data);
+ break;
+
+ case PFCP_ENTITY_PEER_EV_HEARTBEAT_FAILURE:
+ // FIXME
+ break;
+
+ case PFCP_ENTITY_PEER_EV_USE_COUNT_ZERO:
+ /* Stay associated. */
+ return;
+
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static void pfcp_entity_peer_associated_onleave(struct osmo_fsm_inst *fi, uint32_t next_state)
+{
+ struct pfcp_entity_peer *peer = fi->priv;
+ if (next_state != PFCP_ENTITY_PEER_ST_ASSOCIATED)
+ LOGPFSML(fi, LOGL_NOTICE, "Peer %s released\n",
+ osmo_pfcp_ie_node_id_to_str_c(OTC_SELECT, &peer->node_peer->node_id));
+}
+
+static void pfcp_entity_peer_graceful_release_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ //struct pfcp_entity_peer *peer = fi->priv;
+ // FIXME
+}
+
+static void pfcp_entity_peer_graceful_release_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct pfcp_entity_peer *peer = fi->priv;
+
+ switch (event) {
+
+ case PFCP_ENTITY_PEER_EV_HEARTBEAT_FAILURE:
+ pfcp_entity_peer_fsm_state_chg(PFCP_ENTITY_PEER_ST_WAIT_USE_COUNT);
+ break;
+
+ case PFCP_ENTITY_PEER_EV_USE_COUNT_ZERO:
+ /* When there are still sessions, stay around. */
+ if (!hash_empty(peer->sessions_by_up_seid))
+ return;
+ pfcp_entity_peer_fsm_state_chg(PFCP_ENTITY_PEER_ST_WAIT_USE_COUNT);
+ return;
+
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static void pfcp_entity_peer_wait_use_count_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct pfcp_entity_peer *peer = fi->priv;
+ pfcp_entity_peer_clear_sessions(peer);
+ if (!osmo_use_count_total(&peer->use_count))
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
+}
+
+static void pfcp_entity_peer_wait_use_count_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct pfcp_entity_peer *peer = fi->priv;
+ switch (event) {
+
+ case PFCP_ENTITY_PEER_EV_USE_COUNT_ZERO:
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
+ return;
+
+ case PFCP_ENTITY_PEER_EV_RX_ASSOC_SETUP_REQ:
+ pfcp_entity_peer_rx_assoc_setup_req(peer, data);
+ break;
+
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+#define S(x) (1 << (x))
+
+static const struct osmo_fsm_state pfcp_entity_peer_fsm_states[] = {
+ [PFCP_ENTITY_PEER_ST_NOT_ASSOCIATED] = {
+ .name = "NOT_ASSOCIATED",
+ .in_event_mask = 0
+ | S(PFCP_ENTITY_PEER_EV_RX_ASSOC_SETUP_REQ)
+ | S(PFCP_ENTITY_PEER_EV_USE_COUNT_ZERO)
+ ,
+ .out_state_mask = 0
+ | S(PFCP_ENTITY_PEER_ST_ASSOCIATED)
+ | S(PFCP_ENTITY_PEER_ST_WAIT_USE_COUNT)
+ ,
+ .action = pfcp_entity_peer_not_associated_action,
+ },
+ [PFCP_ENTITY_PEER_ST_ASSOCIATED] = {
+ .name = "ASSOCIATED",
+ .in_event_mask = 0
+ | S(PFCP_ENTITY_PEER_EV_RX_ASSOC_SETUP_REQ)
+ | S(PFCP_ENTITY_PEER_EV_RX_ASSOC_UPD_REQ)
+ | S(PFCP_ENTITY_PEER_EV_RX_SESSION_EST_REQ)
+ | S(PFCP_ENTITY_PEER_EV_HEARTBEAT_FAILURE)
+ | S(PFCP_ENTITY_PEER_EV_USE_COUNT_ZERO)
+ ,
+ .out_state_mask = 0
+ | S(PFCP_ENTITY_PEER_ST_ASSOCIATED)
+ | S(PFCP_ENTITY_PEER_ST_GRACEFUL_RELEASE)
+ | S(PFCP_ENTITY_PEER_ST_WAIT_USE_COUNT)
+ ,
+ .action = pfcp_entity_peer_associated_action,
+ .onleave = pfcp_entity_peer_associated_onleave,
+ },
+ [PFCP_ENTITY_PEER_ST_GRACEFUL_RELEASE] = {
+ .name = "GRACEFUL_RELEASE",
+ .in_event_mask = 0
+ | S(PFCP_ENTITY_PEER_EV_HEARTBEAT_FAILURE)
+ | S(PFCP_ENTITY_PEER_EV_USE_COUNT_ZERO)
+ ,
+ .out_state_mask = 0
+ | S(PFCP_ENTITY_PEER_ST_WAIT_USE_COUNT)
+ ,
+ .onenter = pfcp_entity_peer_graceful_release_onenter,
+ .action = pfcp_entity_peer_graceful_release_action,
+ },
+ [PFCP_ENTITY_PEER_ST_WAIT_USE_COUNT] = {
+ .name = "WAIT_USE_COUNT",
+ .in_event_mask = 0
+ | S(PFCP_ENTITY_PEER_EV_USE_COUNT_ZERO)
+ | S(PFCP_ENTITY_PEER_EV_RX_ASSOC_SETUP_REQ)
+ ,
+ .out_state_mask = 0
+ | S(PFCP_ENTITY_PEER_ST_ASSOCIATED)
+ ,
+ .onenter = pfcp_entity_peer_wait_use_count_onenter,
+ .action = pfcp_entity_peer_wait_use_count_action,
+ },
+};
+
+void pfcp_entity_peer_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
+{
+ struct pfcp_entity_peer *entity_peer = fi->priv;
+ LOGPFSML(fi, LOGL_NOTICE, "Peer removed\n");
+ llist_del(&entity_peer->entry);
+ osmo_pfcp_endpoint_invalidate_ctx(entity_peer->node_peer->up_endpoint->pfcp_ep, fi);
+ pfcp_node_peer_put(entity_peer->node_peer, fi->id);
+}
+
+static void pfcp_entity_peer_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case PFCP_ENTITY_PEER_EV_SESSION_TERM:
+ /* ignore */
+ return;
+ case PFCP_ENTITY_PEER_EV_RX_ASSOC_REL_REQ:
+ pfcp_entity_peer_rx_assoc_rel_req(fi->priv, data);
+ return;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
+static struct osmo_fsm pfcp_entity_peer_fsm = {
+ .name = "pfcp_entity_peer",
+ .log_subsys = DPEER,
+ .states = pfcp_entity_peer_fsm_states,
+ .num_states = ARRAY_SIZE(pfcp_entity_peer_fsm_states),
+ .event_names = pfcp_entity_peer_fsm_event_names,
+ .timer_cb = pfcp_entity_peer_fsm_timer_cb,
+ .cleanup = pfcp_entity_peer_fsm_cleanup,
+ .allstate_event_mask = 0
+ | S(PFCP_ENTITY_PEER_EV_RX_ASSOC_REL_REQ)
+ | S(PFCP_ENTITY_PEER_EV_SESSION_TERM)
+ ,
+ .allstate_action = pfcp_entity_peer_allstate_action,
+};
+
+static __attribute__((constructor)) void pfcp_entity_peer_fsm_register(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&pfcp_entity_peer_fsm) == 0);
+}
+
+void pfcp_entity_peer_free(struct pfcp_entity_peer *peer)
+{
+ if (!peer)
+ return;
+ osmo_fsm_inst_term(peer->fi, OSMO_FSM_TERM_REGULAR, NULL);
+}
diff --git a/src/osmo-upf/pfcp_node_peer.c b/src/osmo-upf/pfcp_node_peer.c
new file mode 100644
index 0000000..f4c7025
--- /dev/null
+++ b/src/osmo-upf/pfcp_node_peer.c
@@ -0,0 +1,144 @@
+/*
+ * (C) 2021-2025 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved.
+ *
+ * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/core/utils.h>
+
+#include <osmocom/pfcp/pfcp_msg.h>
+#include <osmocom/pfcp/pfcp_endpoint.h>
+
+#include <osmocom/upf/upf.h>
+#include <osmocom/upf/pfcp_entity_peer.h>
+#include <osmocom/upf/pfcp_node_peer.h>
+#include <osmocom/upf/up_endpoint.h>
+#include <osmocom/upf/up_session.h>
+
+
+static int pfcp_node_peer_use_cb(struct osmo_use_count_entry *e, int32_t old_use_count, const char *file, int line)
+{
+ struct pfcp_node_peer *peer = e->use_count->talloc_object;
+ int32_t total;
+ int level;
+
+ if (!peer) /* already in pfcp_node_peer_free() */
+ return 0;
+
+ if (!e->use)
+ return -EINVAL;
+
+ total = osmo_use_count_total(&peer->use_count);
+
+ if (total == 0
+ || (total == 1 && old_use_count == 0 && e->count == 1))
+ level = LOGL_INFO;
+ else
+ level = LOGL_DEBUG;
+
+ LOGPSRC(DREF, level, file, line,
+ "pfcp_node_peer(%s): %s %s: now used by %s\n",
+ pfcp_node_peer_node_id_str(peer),
+ (e->count - old_use_count) > 0 ? "+" : "-", e->use,
+ osmo_use_count_to_str_c(OTC_SELECT, &peer->use_count));
+
+ if (e->count < 0)
+ return -ERANGE;
+
+ if (total == 0)
+ pfcp_node_peer_free(peer);
+ return 0;
+}
+
+char *pfcp_node_peer_node_id_str(struct pfcp_node_peer *node_peer)
+{
+ struct osmo_pfcp_ie_node_id node_id = node_peer->node_id;
+
+ switch (node_id.type) {
+ case OSMO_PFCP_NODE_ID_T_IPV4:
+ case OSMO_PFCP_NODE_ID_T_IPV6:
+ /* Zero the port, it is not interesting information. The port for PFCP is defined fixed, and there is no use
+ * printing it in the logs */
+ osmo_sockaddr_set_port(&node_id.ip.u.sa, 0);
+ break;
+ default:
+ break;
+ }
+
+ return osmo_pfcp_ie_node_id_to_str_c(OTC_SELECT, &node_id);
+}
+
+
+struct pfcp_node_peer *pfcp_node_peer_alloc(struct up_endpoint *up_endpoint, const struct osmo_pfcp_ie_node_id *node_id)
+{
+ struct pfcp_node_peer *node_peer;
+
+ node_peer = talloc(up_endpoint, struct pfcp_node_peer);
+ OSMO_ASSERT(node_peer);
+
+ *node_peer = (struct pfcp_node_peer) {
+ .up_endpoint = up_endpoint,
+ .node_id = *node_id,
+ .use_count = {
+ .talloc_object = node_peer,
+ .use_cb = pfcp_node_peer_use_cb,
+ },
+ };
+ osmo_use_count_make_static_entries(&node_peer->use_count, node_peer->use_count_buf, ARRAY_SIZE(node_peer->use_count_buf));
+ INIT_LLIST_HEAD(&node_peer->entity_list);
+
+ osmo_pfcp_bits_set(node_peer->local_up_features.bits, OSMO_PFCP_UP_FEAT_BUNDL, true);
+ osmo_pfcp_bits_set(node_peer->local_up_features.bits, OSMO_PFCP_UP_FEAT_RTTL, true);
+ osmo_pfcp_bits_set(node_peer->local_up_features.bits, OSMO_PFCP_UP_FEAT_FTUP, true);
+
+ llist_add(&node_peer->entry, &up_endpoint->pfcp_node_peer_list);
+ return node_peer;
+}
+
+struct pfcp_entity_peer *pfcp_node_peer_find_entity_by_remote_addr(struct pfcp_node_peer *node_peer, const struct osmo_sockaddr *remote_addr)
+{
+ struct pfcp_entity_peer *entity_peer;
+ llist_for_each_entry(entity_peer, &node_peer->entity_list, entry) {
+ if (osmo_sockaddr_cmp(&entity_peer->remote_addr, remote_addr))
+ continue;
+ return entity_peer;
+ }
+ return NULL;
+}
+
+void pfcp_node_peer_free(struct pfcp_node_peer *node_peer)
+{
+ if (!node_peer)
+ return;
+
+ LOG_PNP(node_peer, LOGL_NOTICE, "removed\n");
+
+ struct pfcp_entity_peer *entity_peer;
+ /* Avoid recursive free over user_count reaching zero while removing last pfcp_entity_peer: */
+ node_peer->use_count.talloc_object = NULL;
+ while ((entity_peer = llist_first_entry_or_null(&node_peer->entity_list, struct pfcp_entity_peer, entry)))
+ pfcp_entity_peer_free(entity_peer);
+
+ llist_del(&node_peer->entry);
+}
diff --git a/src/osmo-upf/up_endpoint.c b/src/osmo-upf/up_endpoint.c
index f400bdc..f53dc50 100644
--- a/src/osmo-upf/up_endpoint.c
+++ b/src/osmo-upf/up_endpoint.c
@@ -24,37 +24,54 @@
#include <osmocom/pfcp/pfcp_endpoint.h>
#include <osmocom/pfcp/pfcp_msg.h>

+#include <osmocom/upf/pfcp_entity_peer.h>
+#include <osmocom/upf/pfcp_node_peer.h>
#include <osmocom/upf/up_endpoint.h>
-#include <osmocom/upf/up_peer.h>
#include <osmocom/upf/up_session.h>

+static struct pfcp_entity_peer *up_endp_find_msg_entity_peer(const struct up_endpoint *up_ep, struct osmo_pfcp_msg *m)
+{
+ struct pfcp_node_peer *node_peer;
+
+ /* Look up over remote PFCP Entity identified by remote IP address and our local PFCP Entity */
+ llist_for_each_entry(node_peer, &up_ep->pfcp_node_peer_list, entry) {
+ struct pfcp_entity_peer *entity_peer;
+ llist_for_each_entry(entity_peer, &node_peer->entity_list, entry) {
+ if (osmo_sockaddr_cmp(&entity_peer->remote_addr, &m->remote_addr))
+ continue;
+ return entity_peer;
+ }
+ }
+ return NULL;
+}
+
static void up_endpoint_set_msg_ctx(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_msg *m, struct osmo_pfcp_msg *req)
{
struct up_endpoint *up_ep = osmo_pfcp_endpoint_get_cfg(ep)->priv;
- struct up_peer *peer;
+ struct pfcp_entity_peer *entity_peer = NULL;

/* If this is a response to an earlier request, just take the msg context from the request message. */
if (req) {
if (!m->ctx.peer_fi && req->ctx.peer_fi)
- up_peer_set_msg_ctx(req->ctx.peer_fi->priv, m);
+ pfcp_entity_peer_set_msg_ctx(req->ctx.peer_fi->priv, m);
if (!m->ctx.session_fi && req->ctx.session_fi)
- up_session_set_msg_ctx(req->ctx.session_fi->priv, m);
+ pfcp_entity_peer_set_msg_ctx(req->ctx.session_fi->priv, m);
}

- /* From the remote address, find the matching peer instance */
- if (!m->ctx.peer_fi) {
- peer = up_peer_find(up_ep, &m->remote_addr);
- if (peer) {
- up_peer_set_msg_ctx(peer, m);
- }
+ /* From the Node Id, find the matching PFCP Node */
+ if (m->ctx.peer_fi) {
+ entity_peer = m->ctx.peer_fi->priv;
} else {
- peer = m->ctx.peer_fi->priv;
+ entity_peer = up_endp_find_msg_entity_peer(up_ep, m);
+ if (entity_peer)
+ pfcp_entity_peer_set_msg_ctx(entity_peer, m);
}

+
/* Find a session, if the header is parsed yet and contains a SEID */
- if (peer && !m->ctx.session_fi && m->h.seid_present) {
+ if (entity_peer && !m->ctx.session_fi && m->h.seid_present) {
struct up_session *session;
- session = up_session_find_by_up_seid(peer, m->h.seid);
+ session = pfcp_entity_peer_find_up_session_by_up_seid(entity_peer, m->h.seid);
if (session) {
up_session_set_msg_ctx(session, m);
}
@@ -79,14 +96,39 @@
up_ep_rx_not_impl_req(up_ep, m, OSMO_PFCP_MSGT_PFD_MGMT_RESP, OSMO_PFCP_CAUSE_SERVICE_NOT_SUPPORTED);
}

-static void up_ep_rx_assoc_setup_req(struct up_endpoint *up_ep, const struct osmo_pfcp_msg *m)
+static void up_ep_rx_assoc_setup_req(struct up_endpoint *up_ep, struct osmo_pfcp_msg *m)
{
- struct up_peer *peer = m->ctx.peer_fi ? m->ctx.peer_fi->priv : NULL;
- if (!peer) {
- peer = up_peer_find_or_add(up_ep, &m->remote_addr);
- OSMO_ASSERT(peer);
+ struct pfcp_entity_peer *entity_peer = m->ctx.peer_fi ? m->ctx.peer_fi->priv : NULL;
+ struct osmo_pfcp_ie_node_id *node_id = osmo_pfcp_msg_node_id(m);
+ struct pfcp_node_peer *node_peer;
+ OSMO_ASSERT(node_id);
+
+ /* If PFCP entity belonged to a different PFCP Node (ie. Node Id changed), drop
+ * old entity and re-create a new one on the new Node: */
+ if (entity_peer && osmo_pfcp_ie_node_id_cmp(&entity_peer->node_peer->node_id, node_id)) {
+ OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "PFCP Entity Node-Id changed: %s -> %s!\n",
+ osmo_pfcp_ie_node_id_to_str_c(OTC_SELECT, &entity_peer->node_peer->node_id),
+ osmo_pfcp_ie_node_id_to_str_c(OTC_SELECT, node_id));
+ pfcp_entity_peer_remove_msg_ctx(entity_peer, m);
+ pfcp_entity_peer_free(entity_peer);
+ entity_peer = NULL;
}
- osmo_fsm_inst_dispatch(peer->fi, UP_PEER_EV_RX_ASSOC_SETUP_REQ, (void *)m);
+
+ if (!entity_peer) {
+ OSMO_ASSERT(node_id);
+ node_peer = up_endpoint_find_pfcp_node_peer(up_ep, node_id);
+ if (!node_peer) {
+ node_peer = pfcp_node_peer_alloc(up_ep, node_id);
+ OSMO_ASSERT(node_peer);
+ }
+ entity_peer = pfcp_node_peer_find_entity_by_remote_addr(node_peer, &m->remote_addr);
+ if (!entity_peer) {
+ entity_peer = pfcp_entity_peer_alloc(node_peer, &m->remote_addr);
+ OSMO_ASSERT(entity_peer);
+ }
+ pfcp_entity_peer_set_msg_ctx(entity_peer, m);
+ }
+ osmo_fsm_inst_dispatch(entity_peer->fi, PFCP_ENTITY_PEER_EV_RX_ASSOC_SETUP_REQ, (void *)m);
}

static void up_ep_rx_assoc_upd_req(struct up_endpoint *up_ep, const struct osmo_pfcp_msg *m)
@@ -98,7 +140,7 @@
osmo_pfcp_endpoint_tx(up_ep->pfcp_ep, tx);
return;
}
- osmo_fsm_inst_dispatch(m->ctx.peer_fi, UP_PEER_EV_RX_ASSOC_UPD_REQ, (void *)m);
+ osmo_fsm_inst_dispatch(m->ctx.peer_fi, PFCP_ENTITY_PEER_EV_RX_ASSOC_UPD_REQ, (void *)m);
}

static void up_ep_rx_assoc_rel_req(struct up_endpoint *up_ep, const struct osmo_pfcp_msg *m)
@@ -110,7 +152,7 @@
osmo_pfcp_endpoint_tx(up_ep->pfcp_ep, tx);
return;
}
- osmo_fsm_inst_dispatch(m->ctx.peer_fi, UP_PEER_EV_RX_ASSOC_REL_REQ, (void *)m);
+ osmo_fsm_inst_dispatch(m->ctx.peer_fi, PFCP_ENTITY_PEER_EV_RX_ASSOC_REL_REQ, (void *)m);
}

static void up_ep_rx_node_report_req(struct up_endpoint *up_ep, const struct osmo_pfcp_msg *m)
@@ -134,7 +176,7 @@
osmo_pfcp_endpoint_tx(up_ep->pfcp_ep, tx);
return;
}
- osmo_fsm_inst_dispatch(m->ctx.peer_fi, UP_PEER_EV_RX_SESSION_EST_REQ, (void *)m);
+ osmo_fsm_inst_dispatch(m->ctx.peer_fi, PFCP_ENTITY_PEER_EV_RX_SESSION_EST_REQ, (void *)m);
}

static void up_ep_rx_session_mod_req(struct up_endpoint *up_ep, const struct osmo_pfcp_msg *m)
@@ -238,7 +280,7 @@
struct osmo_pfcp_endpoint_cfg cfg;
struct up_endpoint *up_ep;
up_ep = talloc_zero(ctx, struct up_endpoint);
- INIT_LLIST_HEAD(&up_ep->peers);
+ INIT_LLIST_HEAD(&up_ep->pfcp_node_peer_list);
hash_init(up_ep->sessions_by_up_seid);

cfg = (struct osmo_pfcp_endpoint_cfg){
@@ -262,6 +304,18 @@
return osmo_pfcp_endpoint_bind(up_ep->pfcp_ep);
}

+struct pfcp_node_peer *up_endpoint_find_pfcp_node_peer(const struct up_endpoint *up_ep,
+ const struct osmo_pfcp_ie_node_id *node_id)
+{
+ struct pfcp_node_peer *node_peer;
+ llist_for_each_entry(node_peer, &up_ep->pfcp_node_peer_list, entry) {
+ if (osmo_pfcp_ie_node_id_cmp(&node_peer->node_id, node_id))
+ continue;
+ return node_peer;
+ }
+ return NULL;
+}
+
static struct up_session *up_endpoint_find_session(struct up_endpoint *ep, uint64_t up_seid)
{
struct up_session *session;
@@ -286,11 +340,11 @@

void up_endpoint_free(struct up_endpoint **_ep)
{
- struct up_peer *peer;
+ struct pfcp_node_peer *node_peer;
struct up_endpoint *ep = *_ep;

- while ((peer = llist_first_entry_or_null(&ep->peers, struct up_peer, entry)))
- up_peer_free(peer);
+ while ((node_peer = llist_first_entry_or_null(&ep->pfcp_node_peer_list, struct pfcp_node_peer, entry)))
+ pfcp_node_peer_free(node_peer);

osmo_pfcp_endpoint_free(&ep->pfcp_ep);
*_ep = NULL;
diff --git a/src/osmo-upf/up_gtp_action.c b/src/osmo-upf/up_gtp_action.c
index f8b8e58..8ae2c3e 100644
--- a/src/osmo-upf/up_gtp_action.c
+++ b/src/osmo-upf/up_gtp_action.c
@@ -28,7 +28,7 @@

#include <osmocom/upf/upf.h>
#include <osmocom/upf/up_gtp_action.h>
-#include <osmocom/upf/up_peer.h>
+#include <osmocom/upf/pfcp_entity_peer.h>
#include <osmocom/upf/up_session.h>

int up_gtp_action_cmp(const struct up_gtp_action *a, const struct up_gtp_action *b)
@@ -188,7 +188,7 @@
}
if (a->session)
OSMO_STRBUF_PRINTF(sb, " PFCP-peer:%s SEID-l:0x%"PRIx64,
- up_peer_remote_addr_str(a->session->up_peer), a->session->up_seid);
+ pfcp_entity_peer_remote_addr_str(a->session->entity_peer), a->session->up_seid);
OSMO_STRBUF_PRINTF(sb, " PDR-access:%d", a->pdr_access);
OSMO_STRBUF_PRINTF(sb, " PDR-core:%d", a->pdr_core);
return sb.chars_needed;
diff --git a/src/osmo-upf/up_peer.c b/src/osmo-upf/up_peer.c
deleted file mode 100644
index 08f7d12..0000000
--- a/src/osmo-upf/up_peer.c
+++ /dev/null
@@ -1,544 +0,0 @@
-/*
- * (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
- * All Rights Reserved.
- *
- * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
- *
- * SPDX-License-Identifier: GPL-2.0+
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <errno.h>
-
-#include <osmocom/core/fsm.h>
-#include <osmocom/core/logging.h>
-#include <osmocom/core/tdef.h>
-#include <osmocom/core/utils.h>
-
-#include <osmocom/pfcp/pfcp_msg.h>
-#include <osmocom/pfcp/pfcp_endpoint.h>
-
-#include <osmocom/upf/upf.h>
-#include <osmocom/upf/up_peer.h>
-#include <osmocom/upf/up_endpoint.h>
-#include <osmocom/upf/up_session.h>
-
-enum up_peer_fsm_state {
- UP_PEER_ST_NOT_ASSOCIATED,
- UP_PEER_ST_ASSOCIATED,
- UP_PEER_ST_GRACEFUL_RELEASE,
- UP_PEER_ST_WAIT_USE_COUNT,
-};
-
-static const struct value_string up_peer_fsm_event_names[] = {
- OSMO_VALUE_STRING(UP_PEER_EV_RX_ASSOC_SETUP_REQ),
- OSMO_VALUE_STRING(UP_PEER_EV_RX_ASSOC_UPD_REQ),
- OSMO_VALUE_STRING(UP_PEER_EV_RX_ASSOC_REL_REQ),
- OSMO_VALUE_STRING(UP_PEER_EV_RX_SESSION_EST_REQ),
- OSMO_VALUE_STRING(UP_PEER_EV_HEARTBEAT_FAILURE),
- OSMO_VALUE_STRING(UP_PEER_EV_USE_COUNT_ZERO),
- OSMO_VALUE_STRING(UP_PEER_EV_SESSION_TERM),
- {}
-};
-
-static struct osmo_fsm up_peer_fsm;
-
-static const struct osmo_tdef_state_timeout up_peer_fsm_timeouts[32] = {
- [UP_PEER_ST_GRACEFUL_RELEASE] = { .T = -21 },
-};
-
-/* Transition to a state, using the T timer defined in up_peer_fsm_timeouts.
- * Assumes local variable fi exists. */
-#define up_peer_fsm_state_chg(state) \
- osmo_tdef_fsm_inst_state_chg(fi, state, \
- up_peer_fsm_timeouts, \
- osmo_pfcp_tdefs, \
- 5)
-
-static int up_peer_use_cb(struct osmo_use_count_entry *e, int32_t old_use_count, const char *file, int line)
-{
- struct up_peer *peer = e->use_count->talloc_object;
- int32_t total;
- int level;
-
- if (!e->use)
- return -EINVAL;
-
- total = osmo_use_count_total(&peer->use_count);
-
- if (total == 0
- || (total == 1 && old_use_count == 0 && e->count == 1))
- level = LOGL_INFO;
- else
- level = LOGL_DEBUG;
-
- LOGPFSMSLSRC(peer->fi, DREF, level, file, line,
- "%s %s: now used by %s\n",
- (e->count - old_use_count) > 0 ? "+" : "-", e->use,
- osmo_use_count_to_str_c(OTC_SELECT, &peer->use_count));
-
- if (e->count < 0)
- return -ERANGE;
-
- if (total == 0)
- osmo_fsm_inst_dispatch(peer->fi, UP_PEER_EV_USE_COUNT_ZERO, NULL);
- return 0;
-}
-
-char *up_peer_remote_addr_str(struct up_peer *peer)
-{
- struct osmo_sockaddr remote_addr = peer->remote_addr;
-
- /* Zero the port, it is not interesting information. The port for PFCP is defined fixed, and there is no use
- * printing it in the logs */
- osmo_sockaddr_set_port(&remote_addr.u.sa, 0);
-
- return osmo_sockaddr_to_str_c(OTC_SELECT, &remote_addr);
-}
-
-static void up_peer_update_id(struct up_peer *peer)
-{
- osmo_fsm_inst_update_id_f_sanitize(peer->fi, '-', "%s", up_peer_remote_addr_str(peer));
- LOGPFSML(peer->fi, LOGL_DEBUG, "Updated id\n");
-}
-
-static struct up_peer *up_peer_add(struct up_endpoint *up_endpoint, const struct osmo_sockaddr *remote_addr)
-{
- struct up_peer *peer;
-
- struct osmo_fsm_inst *fi = osmo_fsm_inst_alloc(&up_peer_fsm, up_endpoint, NULL, LOGL_DEBUG, NULL);
- OSMO_ASSERT(fi);
-
- peer = talloc(fi, struct up_peer);
- OSMO_ASSERT(peer);
- fi->priv = peer;
-
- *peer = (struct up_peer) {
- .fi = fi,
- .up_endpoint = up_endpoint,
- .remote_addr = *remote_addr,
- .heartbeat_fi = NULL /* FIXME */,
- .use_count = {
- .talloc_object = peer,
- .use_cb = up_peer_use_cb,
- },
- };
- osmo_use_count_make_static_entries(&peer->use_count, peer->use_count_buf, ARRAY_SIZE(peer->use_count_buf));
- hash_init(peer->sessions_by_up_seid);
- hash_init(peer->sessions_by_cp_seid);
-
- osmo_pfcp_bits_set(peer->local_up_features.bits, OSMO_PFCP_UP_FEAT_BUNDL, true);
- osmo_pfcp_bits_set(peer->local_up_features.bits, OSMO_PFCP_UP_FEAT_RTTL, true);
- osmo_pfcp_bits_set(peer->local_up_features.bits, OSMO_PFCP_UP_FEAT_FTUP, true);
-
- up_peer_update_id(peer);
-
- llist_add(&peer->entry, &up_endpoint->peers);
- return peer;
-}
-
-struct up_peer *up_peer_find(struct up_endpoint *up_endpoint, const struct osmo_sockaddr *remote_addr)
-{
- struct up_peer *peer;
- llist_for_each_entry(peer, &up_endpoint->peers, entry) {
- if (osmo_sockaddr_cmp(&peer->remote_addr, remote_addr))
- continue;
- return peer;
- }
- return NULL;
-}
-
-struct up_peer *up_peer_find_or_add(struct up_endpoint *up_endpoint, const struct osmo_sockaddr *remote_addr)
-{
- struct up_peer *peer = up_peer_find(up_endpoint, remote_addr);
- if (peer)
- return peer;
- return up_peer_add(up_endpoint, remote_addr);
-}
-
-static int up_peer_fsm_timer_cb(struct osmo_fsm_inst *fi)
-{
- //struct up_peer *peer = fi->priv;
- /* Return 1 to terminate FSM instance, 0 to keep running */
- return 1;
-}
-
-void up_peer_set_msg_ctx(struct up_peer *peer, struct osmo_pfcp_msg *m)
-{
- OSMO_ASSERT(!m->ctx.peer_fi);
-
- m->ctx.peer_fi = peer->fi;
- m->ctx.peer_use_count = &peer->use_count;
- m->ctx.peer_use_token = (m->rx ? UP_USE_MSG_RX : UP_USE_MSG_TX);
- OSMO_ASSERT(osmo_use_count_get_put(m->ctx.peer_use_count, m->ctx.peer_use_token, 1) == 0);
-}
-
-struct osmo_pfcp_msg *up_peer_init_tx(struct up_peer *peer, struct osmo_pfcp_msg *in_reply_to,
- enum osmo_pfcp_message_type message_type)
-{
- struct osmo_pfcp_msg *tx;
- if (in_reply_to)
- tx = osmo_pfcp_msg_alloc_tx_resp(OTC_SELECT, in_reply_to, message_type);
- else
- tx = osmo_pfcp_msg_alloc_tx_req(OTC_SELECT, &peer->remote_addr, message_type);
- up_peer_set_msg_ctx(peer, tx);
- return tx;
-}
-
-static int up_peer_tx_assoc_setup_resp(struct up_peer *peer, struct osmo_pfcp_msg *m, enum osmo_pfcp_cause cause)
-{
- struct osmo_pfcp_msg *resp;
-
- resp = up_peer_init_tx(peer, m, OSMO_PFCP_MSGT_ASSOC_SETUP_RESP);
-
- resp->ies.assoc_setup_resp = (struct osmo_pfcp_msg_assoc_setup_resp) {
- .cause = cause,
- .recovery_time_stamp = osmo_pfcp_endpoint_get_recovery_timestamp(g_upf->pfcp.ep->pfcp_ep),
- .up_function_features_present = true,
- .up_function_features = peer->local_up_features,
- };
-
- if (osmo_pfcp_endpoint_tx(peer->up_endpoint->pfcp_ep, resp)) {
- OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Error sending response to this message,"
- " cannot associate with peer\n");
- return -EIO;
- }
- return 0;
-}
-
-static int up_peer_tx_assoc_rel_resp(struct up_peer *peer, struct osmo_pfcp_msg *m, enum osmo_pfcp_cause cause)
-{
- struct osmo_pfcp_msg *resp;
-
- resp = up_peer_init_tx(peer, m, OSMO_PFCP_MSGT_ASSOC_RELEASE_RESP);
-
- resp->ies.assoc_release_resp = (struct osmo_pfcp_msg_assoc_release_resp) {
- .cause = cause,
- };
-
- if (osmo_pfcp_endpoint_tx(peer->up_endpoint->pfcp_ep, resp)) {
- OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Error sending response to this message\n");
- return -EIO;
- }
- return 0;
-}
-
-static void up_peer_clear_sessions(struct up_peer *peer)
-{
- struct up_session *session;
- int bkt;
- struct hlist_node *tmp;
- int count = 0;
- hash_for_each_safe(peer->sessions_by_up_seid, bkt, tmp, session, node_by_up_seid) {
- count += up_session_discard(session);
- }
- if (count)
- LOGPFSML(peer->fi, LOGL_NOTICE, "terminated %d sessions\n", count);
-}
-
-static void up_peer_rx_assoc_setup_req(struct up_peer *peer, struct osmo_pfcp_msg *m)
-{
- struct osmo_fsm_inst *fi = peer->fi;
- enum osmo_pfcp_cause cause = OSMO_PFCP_CAUSE_REQUEST_ACCEPTED;
-
- if (fi->state == UP_PEER_ST_ASSOCIATED) {
- /* Retransmissions of the ACK response happen in pfcp_endpoint.c. So if we get this, it is a genuine
- * duplicate association setup request. We could reject it. But why. Just "replace" with the new
- * association. Continue. */
- /* If the peer has restarted, it has forgotten about all sessions. */
- if (peer->remote_recovery_timestamp != m->ies.assoc_setup_req.recovery_time_stamp) {
- LOGPFSML(fi, LOGL_NOTICE, "another Association Setup Request, with different Recovery Timestamp."
- " Clearing sessions, sending ACK.\n");
- up_peer_clear_sessions(peer);
- } else {
- LOGPFSML(fi, LOGL_NOTICE, "another Association Setup Request, with same Recovery Timestamp."
- " Keeping sessions, sending ACK.\n");
- }
- } else if (up_peer_fsm_state_chg(UP_PEER_ST_ASSOCIATED)) {
- /* Not allowed to transition to ST_ASSOCIATED */
- cause = OSMO_PFCP_CAUSE_REQUEST_REJECTED;
- } else {
- /* Successfully transitioned to ST_ASSOCIATED */
- peer->remote_recovery_timestamp = m->ies.assoc_setup_req.recovery_time_stamp;
- peer->remote_node_id = m->ies.assoc_setup_req.node_id;
- if (m->ies.assoc_setup_req.cp_function_features_present)
- peer->peer_cp_features = m->ies.assoc_setup_req.cp_function_features;
- }
-
- if (up_peer_tx_assoc_setup_resp(peer, m, cause)
- || cause != OSMO_PFCP_CAUSE_REQUEST_ACCEPTED)
- up_peer_fsm_state_chg(UP_PEER_ST_WAIT_USE_COUNT);
-
- LOGPFSML(fi, LOGL_NOTICE, "Peer associated, Node-Id=%s. Local UP features: [%s]; Peer CP features: [%s]\n",
- osmo_pfcp_ie_node_id_to_str_c(OTC_SELECT, &peer->remote_node_id),
- osmo_pfcp_bits_to_str_c(OTC_SELECT, peer->local_up_features.bits, osmo_pfcp_up_feature_strs),
- osmo_pfcp_bits_to_str_c(OTC_SELECT, peer->peer_cp_features.bits, osmo_pfcp_cp_feature_strs));
-}
-
-static void up_peer_rx_assoc_rel_req(struct up_peer *peer, struct osmo_pfcp_msg *m)
-{
- struct osmo_fsm_inst *fi = peer->fi;
- up_peer_tx_assoc_rel_resp(peer, m, OSMO_PFCP_CAUSE_REQUEST_ACCEPTED);
- up_peer_fsm_state_chg(UP_PEER_ST_WAIT_USE_COUNT);
-}
-
-static void up_peer_rx_session_est_req(struct up_peer *peer, struct osmo_pfcp_msg *m)
-{
- enum osmo_pfcp_cause cause = OSMO_PFCP_CAUSE_REQUEST_ACCEPTED;
- struct osmo_pfcp_msg *resp;
- struct up_session *session = up_session_find_or_add(peer, &m->ies.session_est_req.cp_f_seid);
-
- if (!session) {
- cause = OSMO_PFCP_CAUSE_NO_RESOURCES_AVAILABLE;
- goto nack_response;
- }
-
- up_session_set_msg_ctx(session, m);
-
- if (osmo_fsm_inst_dispatch(session->fi, UP_SESSION_EV_RX_SESSION_EST_REQ, m)) {
- cause = OSMO_PFCP_CAUSE_REQUEST_REJECTED;
- goto nack_response;
- }
- return;
-
-nack_response:
- resp = up_peer_init_tx(peer, m, OSMO_PFCP_MSGT_SESSION_EST_RESP);
- resp->h.seid = m->ies.session_est_req.cp_f_seid.seid;
- resp->h.seid_present = true;
- resp->ies.session_est_resp = (struct osmo_pfcp_msg_session_est_resp){
- .cause = cause,
- };
- osmo_pfcp_endpoint_tx(peer->up_endpoint->pfcp_ep, resp);
- if (session)
- up_session_discard(session);
-}
-
-static void up_peer_not_associated_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct up_peer *peer = fi->priv;
-
- switch (event) {
-
- case UP_PEER_EV_RX_ASSOC_SETUP_REQ:
- up_peer_rx_assoc_setup_req(peer, data);
- break;
-
- case UP_PEER_EV_USE_COUNT_ZERO:
- /* Not associated and no pending messages. discard peer. */
- up_peer_fsm_state_chg(UP_PEER_ST_WAIT_USE_COUNT);
- return;
-
- default:
- OSMO_ASSERT(false);
- }
-}
-
-static void up_peer_associated_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct up_peer *peer = fi->priv;
-
- switch (event) {
-
- case UP_PEER_EV_RX_ASSOC_SETUP_REQ:
- up_peer_rx_assoc_setup_req(peer, data);
- break;
-
- case UP_PEER_EV_RX_ASSOC_UPD_REQ:
- // FIXME
- break;
-
- case UP_PEER_EV_RX_SESSION_EST_REQ:
- up_peer_rx_session_est_req(peer, data);
- break;
-
- case UP_PEER_EV_HEARTBEAT_FAILURE:
- // FIXME
- break;
-
- case UP_PEER_EV_USE_COUNT_ZERO:
- /* Stay associated. */
- return;
-
- default:
- OSMO_ASSERT(false);
- }
-}
-
-static void up_peer_associated_onleave(struct osmo_fsm_inst *fi, uint32_t next_state)
-{
- struct up_peer *peer = fi->priv;
- if (next_state != UP_PEER_ST_ASSOCIATED)
- LOGPFSML(fi, LOGL_NOTICE, "Peer %s released\n",
- osmo_pfcp_ie_node_id_to_str_c(OTC_SELECT, &peer->remote_node_id));
-}
-
-static void up_peer_graceful_release_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
-{
- //struct up_peer *peer = fi->priv;
- // FIXME
-}
-
-static void up_peer_graceful_release_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct up_peer *peer = fi->priv;
-
- switch (event) {
-
- case UP_PEER_EV_HEARTBEAT_FAILURE:
- up_peer_fsm_state_chg(UP_PEER_ST_WAIT_USE_COUNT);
- break;
-
- case UP_PEER_EV_USE_COUNT_ZERO:
- /* When there are still sessions, stay around. */
- if (!hash_empty(peer->sessions_by_up_seid))
- return;
- up_peer_fsm_state_chg(UP_PEER_ST_WAIT_USE_COUNT);
- return;
-
- default:
- OSMO_ASSERT(false);
- }
-}
-
-static void up_peer_wait_use_count_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
-{
- struct up_peer *peer = fi->priv;
- up_peer_clear_sessions(peer);
- if (!osmo_use_count_total(&peer->use_count))
- osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
-}
-
-static void up_peer_wait_use_count_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct up_peer *peer = fi->priv;
- switch (event) {
-
- case UP_PEER_EV_USE_COUNT_ZERO:
- osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
- return;
-
- case UP_PEER_EV_RX_ASSOC_SETUP_REQ:
- up_peer_rx_assoc_setup_req(peer, data);
- break;
-
- default:
- OSMO_ASSERT(false);
- }
-}
-
-#define S(x) (1 << (x))
-
-static const struct osmo_fsm_state up_peer_fsm_states[] = {
- [UP_PEER_ST_NOT_ASSOCIATED] = {
- .name = "NOT_ASSOCIATED",
- .in_event_mask = 0
- | S(UP_PEER_EV_RX_ASSOC_SETUP_REQ)
- | S(UP_PEER_EV_USE_COUNT_ZERO)
- ,
- .out_state_mask = 0
- | S(UP_PEER_ST_ASSOCIATED)
- | S(UP_PEER_ST_WAIT_USE_COUNT)
- ,
- .action = up_peer_not_associated_action,
- },
- [UP_PEER_ST_ASSOCIATED] = {
- .name = "ASSOCIATED",
- .in_event_mask = 0
- | S(UP_PEER_EV_RX_ASSOC_SETUP_REQ)
- | S(UP_PEER_EV_RX_ASSOC_UPD_REQ)
- | S(UP_PEER_EV_RX_SESSION_EST_REQ)
- | S(UP_PEER_EV_HEARTBEAT_FAILURE)
- | S(UP_PEER_EV_USE_COUNT_ZERO)
- ,
- .out_state_mask = 0
- | S(UP_PEER_ST_ASSOCIATED)
- | S(UP_PEER_ST_GRACEFUL_RELEASE)
- | S(UP_PEER_ST_WAIT_USE_COUNT)
- ,
- .action = up_peer_associated_action,
- .onleave = up_peer_associated_onleave,
- },
- [UP_PEER_ST_GRACEFUL_RELEASE] = {
- .name = "GRACEFUL_RELEASE",
- .in_event_mask = 0
- | S(UP_PEER_EV_HEARTBEAT_FAILURE)
- | S(UP_PEER_EV_USE_COUNT_ZERO)
- ,
- .out_state_mask = 0
- | S(UP_PEER_ST_WAIT_USE_COUNT)
- ,
- .onenter = up_peer_graceful_release_onenter,
- .action = up_peer_graceful_release_action,
- },
- [UP_PEER_ST_WAIT_USE_COUNT] = {
- .name = "WAIT_USE_COUNT",
- .in_event_mask = 0
- | S(UP_PEER_EV_USE_COUNT_ZERO)
- | S(UP_PEER_EV_RX_ASSOC_SETUP_REQ)
- ,
- .out_state_mask = 0
- | S(UP_PEER_ST_ASSOCIATED)
- ,
- .onenter = up_peer_wait_use_count_onenter,
- .action = up_peer_wait_use_count_action,
- },
-};
-
-void up_peer_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
-{
- struct up_peer *peer = fi->priv;
- LOGPFSML(fi, LOGL_NOTICE, "Peer removed\n");
- llist_del(&peer->entry);
-}
-
-static void up_peer_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- switch (event) {
- case UP_PEER_EV_SESSION_TERM:
- /* ignore */
- return;
- case UP_PEER_EV_RX_ASSOC_REL_REQ:
- up_peer_rx_assoc_rel_req(fi->priv, data);
- return;
- default:
- OSMO_ASSERT(false);
- }
-}
-
-static struct osmo_fsm up_peer_fsm = {
- .name = "up_peer",
- .log_subsys = DPEER,
- .states = up_peer_fsm_states,
- .num_states = ARRAY_SIZE(up_peer_fsm_states),
- .event_names = up_peer_fsm_event_names,
- .timer_cb = up_peer_fsm_timer_cb,
- .cleanup = up_peer_fsm_cleanup,
- .allstate_event_mask = 0
- | S(UP_PEER_EV_RX_ASSOC_REL_REQ)
- | S(UP_PEER_EV_SESSION_TERM)
- ,
- .allstate_action = up_peer_allstate_action,
-};
-
-static __attribute__((constructor)) void up_peer_fsm_register(void)
-{
- OSMO_ASSERT(osmo_fsm_register(&up_peer_fsm) == 0);
-}
-
-void up_peer_free(struct up_peer *peer)
-{
- osmo_fsm_inst_term(peer->fi, OSMO_FSM_TERM_REGULAR, NULL);
-}
diff --git a/src/osmo-upf/up_session.c b/src/osmo-upf/up_session.c
index b96fd49..5f406d5 100644
--- a/src/osmo-upf/up_session.c
+++ b/src/osmo-upf/up_session.c
@@ -31,7 +31,8 @@

#include <osmocom/upf/upf.h>
#include <osmocom/upf/up_endpoint.h>
-#include <osmocom/upf/up_peer.h>
+#include <osmocom/upf/pfcp_entity_peer.h>
+#include <osmocom/upf/pfcp_node_peer.h>
#include <osmocom/upf/up_session.h>
#include <osmocom/upf/up_gtp_action.h>
#include <osmocom/upf/netinst.h>
@@ -41,7 +42,7 @@
void up_session_set_msg_ctx(struct up_session *session, struct osmo_pfcp_msg *m)
{
if (!m->ctx.peer_fi)
- up_peer_set_msg_ctx(session->up_peer, m);
+ pfcp_entity_peer_set_msg_ctx(session->entity_peer, m);

OSMO_ASSERT(!m->ctx.session_fi);

@@ -92,7 +93,7 @@
struct osmo_pfcp_msg *up_session_init_tx(struct up_session *session, struct osmo_pfcp_msg *in_reply_to,
enum osmo_pfcp_message_type message_type)
{
- struct osmo_pfcp_msg *tx = up_peer_init_tx(session->up_peer, in_reply_to, message_type);
+ struct osmo_pfcp_msg *tx = pfcp_entity_peer_init_tx(session->entity_peer, in_reply_to, message_type);
tx->h.seid = session->cp_f_seid.seid;
tx->h.seid_present = true;
up_session_set_msg_ctx(session, tx);
@@ -130,7 +131,7 @@
* to another, and where PFCP most probably happens on an entirely different interface, but may make
* things simpler for lab testing. */
if (osmo_pfcp_ip_addrs_set(local_addr,
- osmo_pfcp_endpoint_get_local_addr(session->up_peer->up_endpoint->pfcp_ep))) {
+ osmo_pfcp_endpoint_get_local_addr(session->entity_peer->node_peer->up_endpoint->pfcp_ep))) {
LOGPFSML(session->fi, LOGL_ERROR, "Invalid local address in pfcp_endpoint cfg\n");
return OSMO_PFCP_CAUSE_SYSTEM_FAILURE;
}
@@ -600,7 +601,7 @@
static void up_session_est(struct up_session *session, struct osmo_pfcp_msg *m)
{
struct osmo_fsm_inst *fi = session->fi;
- struct up_peer *peer = session->up_peer;
+ struct pfcp_entity_peer *peer = session->entity_peer;
struct osmo_pfcp_msg_session_est_req *req = &m->ies.session_est_req;
struct osmo_pfcp_msg_session_est_resp *resp;
struct osmo_pfcp_msg *tx;
@@ -632,10 +633,10 @@

/* Success, send ACK */
osmo_pfcp_ie_f_seid_set(&resp->up_f_seid, session->up_seid,
- osmo_pfcp_endpoint_get_local_addr(peer->up_endpoint->pfcp_ep));
+ osmo_pfcp_endpoint_get_local_addr(peer->node_peer->up_endpoint->pfcp_ep));
resp->up_f_seid_present = true;

- rc = osmo_pfcp_endpoint_tx(peer->up_endpoint->pfcp_ep, tx);
+ rc = osmo_pfcp_endpoint_tx(peer->node_peer->up_endpoint->pfcp_ep, tx);
if (rc) {
/* sending ACK failed, discard session. It might seem like a good idea to keep the session around,
* because the creation succeeded, only the ACK failed. But in the greater scheme of things, if we
@@ -648,7 +649,7 @@

nack_response:
resp->created_pdr_count = 0;
- osmo_pfcp_endpoint_tx(peer->up_endpoint->pfcp_ep, tx);
+ osmo_pfcp_endpoint_tx(peer->node_peer->up_endpoint->pfcp_ep, tx);
/* No matter if sending the NACK succeeded or not, discard the session. */
up_session_fsm_state_chg(UP_SESSION_ST_WAIT_USE_COUNT);
}
@@ -656,7 +657,7 @@
static void up_session_mod(struct up_session *session, struct osmo_pfcp_msg *m)
{
struct osmo_fsm_inst *fi = session->fi;
- struct up_peer *peer = session->up_peer;
+ struct pfcp_entity_peer *peer = session->entity_peer;
struct osmo_pfcp_msg_session_mod_req *req = &m->ies.session_mod_req;
struct osmo_pfcp_msg_session_mod_resp *resp;
struct osmo_pfcp_msg *tx;
@@ -730,7 +731,7 @@
goto nack_response;

/* Success, send ACK */
- if (osmo_pfcp_endpoint_tx(peer->up_endpoint->pfcp_ep, tx)) {
+ if (osmo_pfcp_endpoint_tx(peer->node_peer->up_endpoint->pfcp_ep, tx)) {
/* sending ACK failed, discard session. It might seem like a good idea to keep the session around,
* because the modification succeeded, only the ACK failed. But in the greater scheme of things, if we
* cannot ACK to the PFCP peer, all is lost. Rather not keep stale sessions around. */
@@ -743,7 +744,7 @@

nack_response:
resp->created_pdr_count = 0;
- osmo_pfcp_endpoint_tx(peer->up_endpoint->pfcp_ep, tx);
+ osmo_pfcp_endpoint_tx(peer->node_peer->up_endpoint->pfcp_ep, tx);
/* No matter if sending the NACK succeeded or not, discard the session. */
up_session_fsm_state_chg(UP_SESSION_ST_WAIT_USE_COUNT);
}
@@ -751,14 +752,14 @@
static void up_session_del(struct up_session *session, struct osmo_pfcp_msg *m)
{
struct osmo_fsm_inst *fi = session->fi;
- struct up_peer *peer = session->up_peer;
+ struct pfcp_entity_peer *peer = session->entity_peer;
struct osmo_pfcp_msg *tx;

tx = up_session_init_tx(session, m, OSMO_PFCP_MSGT_SESSION_DEL_RESP);
tx->ies.session_del_resp = (struct osmo_pfcp_msg_session_del_resp){
.cause = OSMO_PFCP_CAUSE_REQUEST_ACCEPTED
};
- osmo_pfcp_endpoint_tx(peer->up_endpoint->pfcp_ep, tx);
+ osmo_pfcp_endpoint_tx(peer->node_peer->up_endpoint->pfcp_ep, tx);
/* No matter if sending the deletion ACK succeeded or not, discard the session. */
up_session_fsm_state_chg(UP_SESSION_ST_WAIT_USE_COUNT);
}
@@ -978,7 +979,7 @@
static void up_session_update_id(struct up_session *session)
{
osmo_fsm_inst_update_id_f_sanitize(session->fi, '-', "%s-0x%" PRIx64,
- up_peer_remote_addr_str(session->up_peer),
+ pfcp_entity_peer_remote_addr_str(session->entity_peer),
session->up_seid);
LOGPFSML(session->fi, LOGL_DEBUG, "Updated id\n");
}
@@ -988,15 +989,15 @@
return cp_seid + up_seid;
}

-static struct up_session *up_session_add(struct up_peer *peer, const struct osmo_pfcp_ie_f_seid *cp_f_seid)
+struct up_session *up_session_alloc(struct pfcp_entity_peer *entity_peer, const struct osmo_pfcp_ie_f_seid *cp_f_seid)
{
struct up_session *session;
- uint64_t up_seid = up_endpoint_next_up_seid(peer->up_endpoint);
+ uint64_t up_seid = up_endpoint_next_up_seid(entity_peer->node_peer->up_endpoint);

if (!up_seid)
return NULL;

- struct osmo_fsm_inst *fi = osmo_fsm_inst_alloc_child(&up_session_fsm, peer->fi, UP_PEER_EV_SESSION_TERM);
+ struct osmo_fsm_inst *fi = osmo_fsm_inst_alloc_child(&up_session_fsm, entity_peer->fi, PFCP_ENTITY_PEER_EV_SESSION_TERM);
OSMO_ASSERT(fi);

session = talloc(fi, struct up_session);
@@ -1005,7 +1006,7 @@

*session = (struct up_session) {
.fi = fi,
- .up_peer = peer,
+ .entity_peer = entity_peer,
.cp_f_seid = *cp_f_seid,
.up_seid = up_seid,
.use_count = {
@@ -1021,43 +1022,12 @@
LOGPFSML(session->fi, LOGL_INFO, "Allocated new UP-SEID: 0x%" PRIx64 "\n", session->up_seid);
up_session_update_id(session);

- hash_add(peer->sessions_by_up_seid, &session->node_by_up_seid, session->up_seid);
- hash_add(peer->sessions_by_cp_seid, &session->node_by_cp_seid, session->cp_f_seid.seid);
- hash_add(peer->up_endpoint->sessions_by_up_seid, &session->ep_node_by_up_seid, session->up_seid);
+ hash_add(entity_peer->sessions_by_up_seid, &session->node_by_up_seid, session->up_seid);
+ hash_add(entity_peer->sessions_by_cp_seid, &session->node_by_cp_seid, session->cp_f_seid.seid);
+ hash_add(entity_peer->node_peer->up_endpoint->sessions_by_up_seid, &session->ep_node_by_up_seid, session->up_seid);
return session;
}

-struct up_session *up_session_find_or_add(struct up_peer *peer, const struct osmo_pfcp_ie_f_seid *cp_f_seid)
-{
- struct up_session *session;
- OSMO_ASSERT(cp_f_seid);
- session = up_session_find_by_cp_f_seid(peer, cp_f_seid);
- if (session)
- return session;
-
- return up_session_add(peer, cp_f_seid);
-}
-
-struct up_session *up_session_find_by_up_seid(struct up_peer *peer, uint64_t up_seid)
-{
- struct up_session *session;
- hash_for_each_possible(peer->sessions_by_up_seid, session, node_by_up_seid, up_seid) {
- if (up_seid == session->up_seid)
- return session;
- }
- return NULL;
-}
-
-struct up_session *up_session_find_by_cp_f_seid(struct up_peer *peer, const struct osmo_pfcp_ie_f_seid *cp_f_seid)
-{
- struct up_session *session;
- hash_for_each_possible(peer->sessions_by_cp_seid, session, node_by_cp_seid, cp_f_seid->seid) {
- if (osmo_pfcp_ie_f_seid_cmp(&session->cp_f_seid, cp_f_seid) == 0)
- return session;
- }
- return NULL;
-}
-
static bool action_is_forw(const struct osmo_pfcp_ie_apply_action *aa)
{
return osmo_pfcp_bits_get(aa->bits, OSMO_PFCP_APPLY_ACTION_FORW)
@@ -1519,7 +1489,7 @@
return sb.chars_needed;
}
OSMO_STRBUF_PRINTF(sb, "peer:%s SEID-r:0x%"PRIx64" SEID-l:0x%"PRIx64" state:%s",
- up_peer_remote_addr_str(session->up_peer),
+ pfcp_entity_peer_remote_addr_str(session->entity_peer),
session->cp_f_seid.seid, session->up_seid,
osmo_fsm_inst_state_name(session->fi));
return sb.chars_needed;
diff --git a/src/osmo-upf/upf.c b/src/osmo-upf/upf.c
index e442d1c..0c3a364 100644
--- a/src/osmo-upf/upf.c
+++ b/src/osmo-upf/upf.c
@@ -29,7 +29,6 @@

#include <osmocom/upf/upf.h>
#include <osmocom/upf/up_endpoint.h>
-#include <osmocom/upf/up_peer.h>
#include <osmocom/upf/up_session.h>
#include <osmocom/upf/up_gtp_action.h>
#include <osmocom/upf/upf_gtp.h>
diff --git a/src/osmo-upf/upf_vty.c b/src/osmo-upf/upf_vty.c
index 9c5f7f0..cc446b3 100644
--- a/src/osmo-upf/upf_vty.c
+++ b/src/osmo-upf/upf_vty.c
@@ -31,10 +31,11 @@
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>

+#include <osmocom/upf/pfcp_entity_peer.h>
+#include <osmocom/upf/pfcp_node_peer.h>
#include <osmocom/upf/upf.h>
#include <osmocom/upf/upf_gtp.h>
#include <osmocom/upf/up_endpoint.h>
-#include <osmocom/upf/up_peer.h>
#include <osmocom/upf/up_session.h>
#include <osmocom/upf/up_gtp_action.h>
#include <osmocom/upf/netinst.h>
@@ -403,27 +404,31 @@
SHOW_STR
"List all sessions' PDR and FAR status\n")
{
- struct up_peer *peer;
+ struct pfcp_node_peer *node_peer;
int active_count = 0;
int inactive_count = 0;
- llist_for_each_entry(peer, &g_upf->pfcp.ep->peers, entry) {
- struct up_session *session;
- int bkt;
- hash_for_each(peer->sessions_by_up_seid, bkt, session, node_by_up_seid) {
- struct pdr *pdr;
- llist_for_each_entry(pdr, &session->pdrs, entry) {
- if (!pdr->active) {
- vty_out(vty, "%s: inactive: %s%s%s%s",
- session->fi->id, pdr_to_str_c(OTC_SELECT, pdr),
- pdr->inactive_reason ? ": " : "",
- pdr->inactive_reason ? : "",
- VTY_NEWLINE);
- inactive_count++;
- } else {
- vty_out(vty, "%s: active: %s%s",
- session->fi->id, pdr_to_str_c(OTC_SELECT, pdr),
- VTY_NEWLINE);
- active_count++;
+
+ llist_for_each_entry(node_peer, &g_upf->pfcp.ep->pfcp_node_peer_list, entry) {
+ struct pfcp_entity_peer *entity_peer;
+ llist_for_each_entry(entity_peer, &node_peer->entity_list, entry) {
+ struct up_session *session;
+ int bkt;
+ hash_for_each(entity_peer->sessions_by_up_seid, bkt, session, node_by_up_seid) {
+ struct pdr *pdr;
+ llist_for_each_entry(pdr, &session->pdrs, entry) {
+ if (!pdr->active) {
+ vty_out(vty, "%s: inactive: %s%s%s%s",
+ session->fi->id, pdr_to_str_c(OTC_SELECT, pdr),
+ pdr->inactive_reason ? ": " : "",
+ pdr->inactive_reason ? : "",
+ VTY_NEWLINE);
+ inactive_count++;
+ } else {
+ vty_out(vty, "%s: active: %s%s",
+ session->fi->id, pdr_to_str_c(OTC_SELECT, pdr),
+ VTY_NEWLINE);
+ active_count++;
+ }
}
}
}
@@ -437,17 +442,20 @@
SHOW_STR
"Active GTP tunnels, both tunend and tunmap\n")
{
- struct up_peer *peer;
+ struct pfcp_node_peer *node_peer;
int count = 0;

- llist_for_each_entry(peer, &g_upf->pfcp.ep->peers, entry) {
- struct up_session *session;
- int bkt;
- hash_for_each(peer->sessions_by_up_seid, bkt, session, node_by_up_seid) {
- struct up_gtp_action *a;
- llist_for_each_entry(a, &session->active_gtp_actions, entry) {
- vty_out(vty, "%s%s", up_gtp_action_to_str_c(OTC_SELECT, a), VTY_NEWLINE);
- count++;
+ llist_for_each_entry(node_peer, &g_upf->pfcp.ep->pfcp_node_peer_list, entry) {
+ struct pfcp_entity_peer *entity_peer;
+ llist_for_each_entry(entity_peer, &node_peer->entity_list, entry) {
+ struct up_session *session;
+ int bkt;
+ hash_for_each(entity_peer->sessions_by_up_seid, bkt, session, node_by_up_seid) {
+ struct up_gtp_action *a;
+ llist_for_each_entry(a, &session->active_gtp_actions, entry) {
+ vty_out(vty, "%s%s", up_gtp_action_to_str_c(OTC_SELECT, a), VTY_NEWLINE);
+ count++;
+ }
}
}
}
@@ -460,25 +468,28 @@
SHOW_STR
"PFCP Session status\n")
{
- struct up_peer *peer;
+ struct pfcp_node_peer *node_peer;
int inactive_count = 0;
int active_count = 0;
int fully_active_count = 0;

- llist_for_each_entry(peer, &g_upf->pfcp.ep->peers, entry) {
- struct up_session *session;
- int bkt;
- hash_for_each(peer->sessions_by_up_seid, bkt, session, node_by_up_seid) {
- vty_out(vty, "%s %s%s",
- up_session_to_str_c(OTC_SELECT, session),
- up_session_gtp_status(session), VTY_NEWLINE);
- if (up_session_is_active(session)) {
- if (up_session_is_fully_active(session, NULL, NULL))
- fully_active_count++;
- else
- active_count++;
- } else {
- inactive_count++;
+ llist_for_each_entry(node_peer, &g_upf->pfcp.ep->pfcp_node_peer_list, entry) {
+ struct pfcp_entity_peer *entity_peer;
+ llist_for_each_entry(entity_peer, &node_peer->entity_list, entry) {
+ struct up_session *session;
+ int bkt;
+ hash_for_each(entity_peer->sessions_by_up_seid, bkt, session, node_by_up_seid) {
+ vty_out(vty, "%s %s%s",
+ up_session_to_str_c(OTC_SELECT, session),
+ up_session_gtp_status(session), VTY_NEWLINE);
+ if (up_session_is_active(session)) {
+ if (up_session_is_fully_active(session, NULL, NULL))
+ fully_active_count++;
+ else
+ active_count++;
+ } else {
+ inactive_count++;
+ }
}
}
}
diff --git a/tests/unique_ids/unique_ids_test.c b/tests/unique_ids/unique_ids_test.c
index 1a9b2b3..6dcc825 100644
--- a/tests/unique_ids/unique_ids_test.c
+++ b/tests/unique_ids/unique_ids_test.c
@@ -32,10 +32,11 @@

#include <osmocom/pfcp/pfcp_endpoint.h>

+#include <osmocom/upf/pfcp_entity_peer.h>
+#include <osmocom/upf/pfcp_node_peer.h>
#include <osmocom/upf/upf.h>
#include <osmocom/upf/netinst.h>
#include <osmocom/upf/up_endpoint.h>
-#include <osmocom/upf/up_peer.h>
#include <osmocom/upf/up_session.h>
#include <osmocom/upf/up_gtp_action.h>

@@ -105,12 +106,48 @@
return &osa;
}

-static struct up_peer *have_peer(const char *remote_addr, uint16_t port)
+static struct osmo_pfcp_ie_node_id *str2node_id(enum osmo_pfcp_node_id_type type, const char *addr)
{
- return up_peer_find_or_add(g_upf->pfcp.ep, str2addr(remote_addr, port));
+ static struct osmo_pfcp_ie_node_id node_id;
+
+ node_id.type = type;
+ switch (node_id.type) {
+ case OSMO_PFCP_NODE_ID_T_IPV4:
+ case OSMO_PFCP_NODE_ID_T_IPV6:
+ node_id.ip = *str2addr(addr, 0);
+ break;
+ case OSMO_PFCP_NODE_ID_T_FQDN:
+ OSMO_STRLCPY_ARRAY(node_id.fqdn, addr);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ break;
+ }
+ return &node_id;
}

-static struct osmo_pfcp_msg *new_pfcp_msg_for_osmo_upf_rx(struct up_peer *from_peer, enum osmo_pfcp_message_type msg_type)
+static struct pfcp_entity_peer *have_pfcp_entity_peer(enum osmo_pfcp_node_id_type type, const char *remote_addr)
+{
+ struct pfcp_node_peer *node_peer;
+ struct pfcp_entity_peer *entity_peer;
+ struct osmo_pfcp_ie_node_id *node_id = str2node_id(type, remote_addr);
+ struct osmo_sockaddr *osa = str2addr(remote_addr, 0);
+
+ node_peer = up_endpoint_find_pfcp_node_peer(g_upf->pfcp.ep, node_id);
+ if (!node_peer) {
+ node_peer = pfcp_node_peer_alloc(g_upf->pfcp.ep, node_id);
+ OSMO_ASSERT(node_peer);
+ }
+
+ entity_peer = pfcp_node_peer_find_entity_by_remote_addr(node_peer, osa);
+ if (!entity_peer) {
+ entity_peer = pfcp_entity_peer_alloc(node_peer, osa);
+ OSMO_ASSERT(entity_peer);
+ }
+ return entity_peer;
+}
+
+static struct osmo_pfcp_msg *new_pfcp_msg_for_osmo_upf_rx(struct pfcp_entity_peer *from_peer, enum osmo_pfcp_message_type msg_type)
{
/* pfcp_endpoint discards received messages immediately after dispatching; in this test, allocate them in
* OTC_SELECT so they get discarded on the next select_poll().
@@ -122,11 +159,11 @@
return m;
}

-static void peer_assoc(struct up_peer *peer)
+static void peer_assoc(struct pfcp_entity_peer *peer)
{
struct osmo_pfcp_msg *m = new_pfcp_msg_for_osmo_upf_rx(peer, OSMO_PFCP_MSGT_ASSOC_SETUP_REQ);
m->ies.assoc_setup_req.recovery_time_stamp = 1234;
- osmo_fsm_inst_dispatch(peer->fi, UP_PEER_EV_RX_ASSOC_SETUP_REQ, m);
+ osmo_fsm_inst_dispatch(peer->fi, PFCP_ENTITY_PEER_EV_RX_ASSOC_SETUP_REQ, m);
select_poll();
}

@@ -134,7 +171,7 @@
static int next_cp_seid = 0x100;

/* Send a PFCP Session Establishment Request, and return the created session */
-static struct up_session *session_est_tunmap(struct up_peer *peer)
+static struct up_session *session_est_tunmap(struct pfcp_entity_peer *peer)
{
struct osmo_pfcp_msg *m;

@@ -255,10 +292,10 @@
},
};

- osmo_fsm_inst_dispatch(peer->fi, UP_PEER_EV_RX_SESSION_EST_REQ, m);
+ osmo_fsm_inst_dispatch(peer->fi, PFCP_ENTITY_PEER_EV_RX_SESSION_EST_REQ, m);
select_poll();

- return up_session_find_by_up_seid(peer, last_up_seid);
+ return pfcp_entity_peer_find_up_session_by_up_seid(peer, last_up_seid);
}

static void session_del(struct up_session *session)
@@ -267,7 +304,7 @@

log_assert(session);

- m = new_pfcp_msg_for_osmo_upf_rx(session->up_peer, OSMO_PFCP_MSGT_SESSION_DEL_REQ);
+ m = new_pfcp_msg_for_osmo_upf_rx(session->entity_peer, OSMO_PFCP_MSGT_SESSION_DEL_REQ);
m->h.seid_present = true;
m->h.seid = session->up_seid;

@@ -277,23 +314,26 @@

static void dump_state(void)
{
- struct up_peer *peer;
+ struct pfcp_node_peer *node_peer;
log("\n state:\n");
- llist_for_each_entry(peer, &g_upf->pfcp.ep->peers, entry) {
- struct up_session *session;
- int bkt;
- log(" | peer %s %s\n", peer->fi->name, osmo_fsm_inst_state_name(peer->fi));
- hash_for_each(peer->sessions_by_up_seid, bkt, session, node_by_up_seid) {
- struct up_gtp_action *a;
- llist_for_each_entry(a, &session->active_gtp_actions, entry) {
- if (a->kind != UP_GTP_U_TUNMAP)
- continue;
- log(" | session[%s]: UP-SEID 0x%"PRIx64"; chain_id access=%u core=%u;"
- " local TEID access=0x%x core=0x%x\n",
- osmo_fsm_inst_state_name(session->fi),
- session->up_seid,
- a->tunmap.access.chain_id, a->tunmap.core.chain_id,
- a->tunmap.access.tun.local.teid, a->tunmap.core.tun.local.teid);
+ llist_for_each_entry(node_peer, &g_upf->pfcp.ep->pfcp_node_peer_list, entry) {
+ struct pfcp_entity_peer *entity_peer;
+ llist_for_each_entry(entity_peer, &node_peer->entity_list, entry) {
+ struct up_session *session;
+ int bkt;
+ log(" | peer %s %s\n", entity_peer->fi->name, osmo_fsm_inst_state_name(entity_peer->fi));
+ hash_for_each(entity_peer->sessions_by_up_seid, bkt, session, node_by_up_seid) {
+ struct up_gtp_action *a;
+ llist_for_each_entry(a, &session->active_gtp_actions, entry) {
+ if (a->kind != UP_GTP_U_TUNMAP)
+ continue;
+ log(" | session[%s]: UP-SEID 0x%"PRIx64"; chain_id access=%u core=%u;"
+ " local TEID access=0x%x core=0x%x\n",
+ osmo_fsm_inst_state_name(session->fi),
+ session->up_seid,
+ a->tunmap.access.chain_id, a->tunmap.core.chain_id,
+ a->tunmap.access.tun.local.teid, a->tunmap.core.tun.local.teid);
+ }
}
}
}
@@ -302,7 +342,7 @@

static void test_skip_used_id(void)
{
- struct up_peer *peer;
+ struct pfcp_entity_peer *entity_peer;
struct up_session *s1;
uint64_t s1_up_seid;
struct up_session *s2;
@@ -313,8 +353,8 @@
setup(__func__);

log("PFCP Associate peer\n");
- peer = have_peer("1.2.3.4", 1234);
- peer_assoc(peer);
+ entity_peer = have_pfcp_entity_peer(OSMO_PFCP_NODE_ID_T_IPV4, "1.2.3.4");
+ peer_assoc(entity_peer);
dump_state();

/* Make sure to start out all IDs with 1 */
@@ -323,7 +363,7 @@
g_upf->tunmap.next_chain_id_state = 0;

log("set up tunmap, which assigns first UP-SEID 0x1, local-TEID 0x1 and 0x2, chain_ids 1 and 2\n");
- s1 = session_est_tunmap(peer);
+ s1 = session_est_tunmap(entity_peer);
dump_state();

log_assert(s1->up_seid == 1);
@@ -342,7 +382,7 @@
g_upf->tunmap.next_chain_id_state = 0;

log("set up second tunmap, should use distinct IDs\n");
- s2 = session_est_tunmap(peer);
+ s2 = session_est_tunmap(entity_peer);
dump_state();

log_assert(s2->up_seid == 2);
@@ -359,7 +399,7 @@
s1_up_seid = s1->up_seid;
session_del(s1);
dump_state();
- log_assert(up_session_find_by_up_seid(peer, s1_up_seid) == NULL);
+ log_assert(pfcp_entity_peer_find_up_session_by_up_seid(entity_peer, s1_up_seid) == NULL);
log("\n");

log("again wrap all ID state back to 1\n");
@@ -368,7 +408,7 @@
g_upf->tunmap.next_chain_id_state = 0;

log("set up third tunmap, should now re-use same IDs as the first session\n");
- s3 = session_est_tunmap(peer);
+ s3 = session_est_tunmap(entity_peer);
dump_state();

log_assert(s3->up_seid == 1);
@@ -382,7 +422,7 @@
log("\n");

log("set up 4th tunmap; chain_id state would use 3 and 4, but they are in use, so should assign 5 and 6\n");
- s4 = session_est_tunmap(peer);
+ s4 = session_est_tunmap(entity_peer);
dump_state();

log_assert(s4->up_seid == 3);
diff --git a/tests/unique_ids/unique_ids_test.err b/tests/unique_ids/unique_ids_test.err
index 7ac7e59..2083dac 100644
--- a/tests/unique_ids/unique_ids_test.err
+++ b/tests/unique_ids/unique_ids_test.err
@@ -22,32 +22,33 @@
DNFT DEBUG ran nft ruleset, 465 chars: "add chain inet osmo-upf pre { type filter hook prerouting priority -300; policy accept; };\nadd chain inet osmo-upf post { type filter hook postrouting priority 400; policy accept; };\nadd map inet osmo-upf tunmap-pre { typeof ip daddr . @ih,32,32 : verdict;..."

PFCP Associate peer
-DPEER DEBUG up_peer{NOT_ASSOCIATED}: Allocated
-DPEER DEBUG up_peer(1-2-3-4){NOT_ASSOCIATED}: Updated id
-DPEER DEBUG up_peer(1-2-3-4){NOT_ASSOCIATED}: Received Event UP_PEER_EV_RX_ASSOC_SETUP_REQ
-DPEER DEBUG up_peer(1-2-3-4){NOT_ASSOCIATED}: State change to ASSOCIATED (no timeout)
-DREF INFO up_peer(1-2-3-4){ASSOCIATED}: + msg-tx: now used by 1 (msg-tx)
+DPEER DEBUG pfcp_entity_peer{NOT_ASSOCIATED}: Allocated
+DPEER DEBUG pfcp_entity_peer(1-2-3-4){NOT_ASSOCIATED}: Updated id
+DREF INFO pfcp_node_peer(v4:1.2.3.4): + 1-2-3-4: now used by 1 (1-2-3-4)
+DPEER DEBUG pfcp_entity_peer(1-2-3-4){NOT_ASSOCIATED}: Received Event PFCP_ENTITY_PEER_EV_RX_ASSOC_SETUP_REQ
+DPEER DEBUG pfcp_entity_peer(1-2-3-4){NOT_ASSOCIATED}: State change to ASSOCIATED (no timeout)
+DREF INFO pfcp_entity_peer(1-2-3-4){ASSOCIATED}: + msg-tx: now used by 1 (msg-tx)

[test override] PFCP tx:
PFCPv1 ASSOC_SETUP_RESP hdr={seq=0} ies={ 'Node ID'=v4:unsupported family 0 'Cause'=Request accepted (success) 'Recovery Time Stamp'=2208988800 'UP Function Features'=FTUP+BUNDL+RTTL }

-DREF INFO up_peer(1-2-3-4){ASSOCIATED}: - msg-tx: now used by 0 (-)
-DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_USE_COUNT_ZERO
-DPEER NOTICE up_peer(1-2-3-4){ASSOCIATED}: Peer associated, Node-Id=v4:unsupported family 0. Local UP features: [FTUP+BUNDL+RTTL]; Peer CP features: [-]
+DREF INFO pfcp_entity_peer(1-2-3-4){ASSOCIATED}: - msg-tx: now used by 0 (-)
+DPEER DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: Received Event PFCP_ENTITY_PEER_EV_USE_COUNT_ZERO
+DPEER NOTICE pfcp_entity_peer(1-2-3-4){ASSOCIATED}: Peer associated, Node-Id=v4:1.2.3.4. Local UP features: [FTUP+BUNDL+RTTL]; Peer CP features: [-]

state:
- | peer up_peer(1-2-3-4) ASSOCIATED
+ | peer pfcp_entity_peer(1-2-3-4) ASSOCIATED

set up tunmap, which assigns first UP-SEID 0x1, local-TEID 0x1 and 0x2, chain_ids 1 and 2
-DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_RX_SESSION_EST_REQ
+DPEER DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: Received Event PFCP_ENTITY_PEER_EV_RX_SESSION_EST_REQ
DSESSION DEBUG up_session(1-2-3-4){INIT}: Allocated
-DSESSION DEBUG up_session(1-2-3-4){INIT}: is child of up_peer(1-2-3-4)
+DSESSION DEBUG up_session(1-2-3-4){INIT}: is child of pfcp_entity_peer(1-2-3-4)
DSESSION INFO up_session(1-2-3-4){INIT}: Allocated new UP-SEID: 0x1
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: Updated id
-DREF INFO up_peer(1-2-3-4){ASSOCIATED}: + msg-rx: now used by 1 (msg-rx)
+DREF INFO pfcp_entity_peer(1-2-3-4){ASSOCIATED}: + msg-rx: now used by 1 (msg-rx)
DREF INFO up_session(1-2-3-4-0x1){INIT}: + msg-rx: now used by 1 (msg-rx)
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: Received Event UP_SESSION_EV_RX_SESSION_EST_REQ
-DREF DEBUG up_peer(1-2-3-4){ASSOCIATED}: + msg-tx: now used by 2 (msg-rx,msg-tx)
+DREF DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: + msg-tx: now used by 2 (msg-rx,msg-tx)
DREF DEBUG up_session(1-2-3-4-0x1){INIT}: + msg-tx: now used by 2 (msg-rx,msg-tx)
DSESSION INFO up_session(1-2-3-4-0x1){INIT}: Allocated new local F-TEID TEID-0x1,v4:1.1.1.1
DSESSION INFO up_session(1-2-3-4-0x1){INIT}: New PDR-1{src:Core TEID-0x1,v4:1.1.1.1 decaps-GTP_U_UDP_IPV4} --> FAR-1{FORW dst:Access,GTP_U_UDP_IPV4,TEID:0x100,v4:5.6.7.8}
@@ -85,16 +86,16 @@
osmo-upf created session 0x1

DREF DEBUG up_session(1-2-3-4-0x1){INIT}: - msg-tx: now used by 1 (msg-rx)
-DREF DEBUG up_peer(1-2-3-4){ASSOCIATED}: - msg-tx: now used by 1 (msg-rx)
+DREF DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: - msg-tx: now used by 1 (msg-rx)
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: State change to ESTABLISHED (no timeout)
DSESSION INFO up_session(1-2-3-4-0x1){ESTABLISHED}: Session established: peer:1.2.3.4 SEID-r:0x100 SEID-l:0x1 state:ESTABLISHED PDR-active:2/2 FAR-active:2/2 GTP-active:1
DREF INFO up_session(1-2-3-4-0x1){ESTABLISHED}: - msg-rx: now used by 0 (-)
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: Received Event UP_SESSION_EV_USE_COUNT_ZERO
-DREF INFO up_peer(1-2-3-4){ASSOCIATED}: - msg-rx: now used by 0 (-)
-DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_USE_COUNT_ZERO
+DREF INFO pfcp_entity_peer(1-2-3-4){ASSOCIATED}: - msg-rx: now used by 0 (-)
+DPEER DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: Received Event PFCP_ENTITY_PEER_EV_USE_COUNT_ZERO

state:
- | peer up_peer(1-2-3-4) ASSOCIATED
+ | peer pfcp_entity_peer(1-2-3-4) ASSOCIATED
| session[ESTABLISHED]: UP-SEID 0x1; chain_id access=1 core=2; local TEID access=0x2 core=0x1

assert(s1->up_seid == 1)
@@ -107,15 +108,15 @@

simulate wrapping of IDs back to 1
set up second tunmap, should use distinct IDs
-DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_RX_SESSION_EST_REQ
+DPEER DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: Received Event PFCP_ENTITY_PEER_EV_RX_SESSION_EST_REQ
DSESSION DEBUG up_session(1-2-3-4){INIT}: Allocated
-DSESSION DEBUG up_session(1-2-3-4){INIT}: is child of up_peer(1-2-3-4)
+DSESSION DEBUG up_session(1-2-3-4){INIT}: is child of pfcp_entity_peer(1-2-3-4)
DSESSION INFO up_session(1-2-3-4){INIT}: Allocated new UP-SEID: 0x2
DSESSION DEBUG up_session(1-2-3-4-0x2){INIT}: Updated id
-DREF INFO up_peer(1-2-3-4){ASSOCIATED}: + msg-rx: now used by 1 (msg-rx)
+DREF INFO pfcp_entity_peer(1-2-3-4){ASSOCIATED}: + msg-rx: now used by 1 (msg-rx)
DREF INFO up_session(1-2-3-4-0x2){INIT}: + msg-rx: now used by 1 (msg-rx)
DSESSION DEBUG up_session(1-2-3-4-0x2){INIT}: Received Event UP_SESSION_EV_RX_SESSION_EST_REQ
-DREF DEBUG up_peer(1-2-3-4){ASSOCIATED}: + msg-tx: now used by 2 (msg-rx,msg-tx)
+DREF DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: + msg-tx: now used by 2 (msg-rx,msg-tx)
DREF DEBUG up_session(1-2-3-4-0x2){INIT}: + msg-tx: now used by 2 (msg-rx,msg-tx)
DSESSION INFO up_session(1-2-3-4-0x2){INIT}: Allocated new local F-TEID TEID-0x3,v4:1.1.1.1
DSESSION INFO up_session(1-2-3-4-0x2){INIT}: New PDR-1{src:Core TEID-0x3,v4:1.1.1.1 decaps-GTP_U_UDP_IPV4} --> FAR-1{FORW dst:Access,GTP_U_UDP_IPV4,TEID:0x102,v4:5.6.7.8}
@@ -153,16 +154,16 @@
osmo-upf created session 0x2

DREF DEBUG up_session(1-2-3-4-0x2){INIT}: - msg-tx: now used by 1 (msg-rx)
-DREF DEBUG up_peer(1-2-3-4){ASSOCIATED}: - msg-tx: now used by 1 (msg-rx)
+DREF DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: - msg-tx: now used by 1 (msg-rx)
DSESSION DEBUG up_session(1-2-3-4-0x2){INIT}: State change to ESTABLISHED (no timeout)
DSESSION INFO up_session(1-2-3-4-0x2){ESTABLISHED}: Session established: peer:1.2.3.4 SEID-r:0x101 SEID-l:0x2 state:ESTABLISHED PDR-active:2/2 FAR-active:2/2 GTP-active:1
DREF INFO up_session(1-2-3-4-0x2){ESTABLISHED}: - msg-rx: now used by 0 (-)
DSESSION DEBUG up_session(1-2-3-4-0x2){ESTABLISHED}: Received Event UP_SESSION_EV_USE_COUNT_ZERO
-DREF INFO up_peer(1-2-3-4){ASSOCIATED}: - msg-rx: now used by 0 (-)
-DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_USE_COUNT_ZERO
+DREF INFO pfcp_entity_peer(1-2-3-4){ASSOCIATED}: - msg-rx: now used by 0 (-)
+DPEER DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: Received Event PFCP_ENTITY_PEER_EV_USE_COUNT_ZERO

state:
- | peer up_peer(1-2-3-4) ASSOCIATED
+ | peer pfcp_entity_peer(1-2-3-4) ASSOCIATED
| session[ESTABLISHED]: UP-SEID 0x1; chain_id access=1 core=2; local TEID access=0x2 core=0x1
| session[ESTABLISHED]: UP-SEID 0x2; chain_id access=3 core=4; local TEID access=0x4 core=0x3

@@ -177,7 +178,7 @@
drop first tunmap (up_session(1-2-3-4-0x1))
assert(session)
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: Received Event UP_SESSION_EV_RX_SESSION_DEL_REQ
-DREF INFO up_peer(1-2-3-4){ASSOCIATED}: + msg-tx: now used by 1 (msg-tx)
+DREF INFO pfcp_entity_peer(1-2-3-4){ASSOCIATED}: + msg-tx: now used by 1 (msg-tx)
DREF INFO up_session(1-2-3-4-0x1){ESTABLISHED}: + msg-tx: now used by 1 (msg-tx)

[test override] PFCP tx:
@@ -185,8 +186,8 @@

DREF INFO up_session(1-2-3-4-0x1){ESTABLISHED}: - msg-tx: now used by 0 (-)
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: Received Event UP_SESSION_EV_USE_COUNT_ZERO
-DREF INFO up_peer(1-2-3-4){ASSOCIATED}: - msg-tx: now used by 0 (-)
-DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_USE_COUNT_ZERO
+DREF INFO pfcp_entity_peer(1-2-3-4){ASSOCIATED}: - msg-tx: now used by 0 (-)
+DPEER DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: Received Event PFCP_ENTITY_PEER_EV_USE_COUNT_ZERO
DSESSION INFO up_session(1-2-3-4-0x1){ESTABLISHED}: Session releasing: peer:1.2.3.4 SEID-r:0x100 SEID-l:0x1 state:ESTABLISHED PDR-active:2/2 FAR-active:2/2 GTP-active:1
DNFT INFO Added NFT ruleset to queue: n:1 strlen:381
DNFT INFO Flushing NFT ruleset queue: reached max nr of rules: n:1 strlen:381 (flush count: 3 avg rules per flush: 1)
@@ -207,29 +208,29 @@
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: State change to WAIT_USE_COUNT (no timeout)
DSESSION DEBUG up_session(1-2-3-4-0x1){WAIT_USE_COUNT}: GTP actions: 0 previously active; want active: 0
DSESSION DEBUG up_session(1-2-3-4-0x1){WAIT_USE_COUNT}: Terminating (cause = OSMO_FSM_TERM_REGULAR)
-DSESSION DEBUG up_session(1-2-3-4-0x1){WAIT_USE_COUNT}: Removing from parent up_peer(1-2-3-4)
+DSESSION DEBUG up_session(1-2-3-4-0x1){WAIT_USE_COUNT}: Removing from parent pfcp_entity_peer(1-2-3-4)
DSESSION DEBUG up_session(1-2-3-4-0x1){WAIT_USE_COUNT}: GTP actions: 0 previously active; want active: 0
DSESSION DEBUG up_session(1-2-3-4-0x1){WAIT_USE_COUNT}: Freeing instance
DSESSION DEBUG up_session(1-2-3-4-0x1){WAIT_USE_COUNT}: Deallocated
-DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_SESSION_TERM
+DPEER DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: Received Event PFCP_ENTITY_PEER_EV_SESSION_TERM

state:
- | peer up_peer(1-2-3-4) ASSOCIATED
+ | peer pfcp_entity_peer(1-2-3-4) ASSOCIATED
| session[ESTABLISHED]: UP-SEID 0x2; chain_id access=3 core=4; local TEID access=0x4 core=0x3

-assert(up_session_find_by_up_seid(peer, s1_up_seid) == NULL)
+assert(pfcp_entity_peer_find_up_session_by_up_seid(entity_peer, s1_up_seid) == NULL)

again wrap all ID state back to 1
set up third tunmap, should now re-use same IDs as the first session
-DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_RX_SESSION_EST_REQ
+DPEER DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: Received Event PFCP_ENTITY_PEER_EV_RX_SESSION_EST_REQ
DSESSION DEBUG up_session(1-2-3-4){INIT}: Allocated
-DSESSION DEBUG up_session(1-2-3-4){INIT}: is child of up_peer(1-2-3-4)
+DSESSION DEBUG up_session(1-2-3-4){INIT}: is child of pfcp_entity_peer(1-2-3-4)
DSESSION INFO up_session(1-2-3-4){INIT}: Allocated new UP-SEID: 0x1
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: Updated id
-DREF INFO up_peer(1-2-3-4){ASSOCIATED}: + msg-rx: now used by 1 (msg-rx)
+DREF INFO pfcp_entity_peer(1-2-3-4){ASSOCIATED}: + msg-rx: now used by 1 (msg-rx)
DREF INFO up_session(1-2-3-4-0x1){INIT}: + msg-rx: now used by 1 (msg-rx)
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: Received Event UP_SESSION_EV_RX_SESSION_EST_REQ
-DREF DEBUG up_peer(1-2-3-4){ASSOCIATED}: + msg-tx: now used by 2 (msg-rx,msg-tx)
+DREF DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: + msg-tx: now used by 2 (msg-rx,msg-tx)
DREF DEBUG up_session(1-2-3-4-0x1){INIT}: + msg-tx: now used by 2 (msg-rx,msg-tx)
DSESSION INFO up_session(1-2-3-4-0x1){INIT}: Allocated new local F-TEID TEID-0x1,v4:1.1.1.1
DSESSION INFO up_session(1-2-3-4-0x1){INIT}: New PDR-1{src:Core TEID-0x1,v4:1.1.1.1 decaps-GTP_U_UDP_IPV4} --> FAR-1{FORW dst:Access,GTP_U_UDP_IPV4,TEID:0x104,v4:5.6.7.8}
@@ -267,16 +268,16 @@
osmo-upf created session 0x1

DREF DEBUG up_session(1-2-3-4-0x1){INIT}: - msg-tx: now used by 1 (msg-rx)
-DREF DEBUG up_peer(1-2-3-4){ASSOCIATED}: - msg-tx: now used by 1 (msg-rx)
+DREF DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: - msg-tx: now used by 1 (msg-rx)
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: State change to ESTABLISHED (no timeout)
DSESSION INFO up_session(1-2-3-4-0x1){ESTABLISHED}: Session established: peer:1.2.3.4 SEID-r:0x102 SEID-l:0x1 state:ESTABLISHED PDR-active:2/2 FAR-active:2/2 GTP-active:1
DREF INFO up_session(1-2-3-4-0x1){ESTABLISHED}: - msg-rx: now used by 0 (-)
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: Received Event UP_SESSION_EV_USE_COUNT_ZERO
-DREF INFO up_peer(1-2-3-4){ASSOCIATED}: - msg-rx: now used by 0 (-)
-DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_USE_COUNT_ZERO
+DREF INFO pfcp_entity_peer(1-2-3-4){ASSOCIATED}: - msg-rx: now used by 0 (-)
+DPEER DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: Received Event PFCP_ENTITY_PEER_EV_USE_COUNT_ZERO

state:
- | peer up_peer(1-2-3-4) ASSOCIATED
+ | peer pfcp_entity_peer(1-2-3-4) ASSOCIATED
| session[ESTABLISHED]: UP-SEID 0x1; chain_id access=1 core=2; local TEID access=0x2 core=0x1
| session[ESTABLISHED]: UP-SEID 0x2; chain_id access=3 core=4; local TEID access=0x4 core=0x3

@@ -289,15 +290,15 @@
assert(a->tunmap.core.chain_id == 2)

set up 4th tunmap; chain_id state would use 3 and 4, but they are in use, so should assign 5 and 6
-DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_RX_SESSION_EST_REQ
+DPEER DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: Received Event PFCP_ENTITY_PEER_EV_RX_SESSION_EST_REQ
DSESSION DEBUG up_session(1-2-3-4){INIT}: Allocated
-DSESSION DEBUG up_session(1-2-3-4){INIT}: is child of up_peer(1-2-3-4)
+DSESSION DEBUG up_session(1-2-3-4){INIT}: is child of pfcp_entity_peer(1-2-3-4)
DSESSION INFO up_session(1-2-3-4){INIT}: Allocated new UP-SEID: 0x3
DSESSION DEBUG up_session(1-2-3-4-0x3){INIT}: Updated id
-DREF INFO up_peer(1-2-3-4){ASSOCIATED}: + msg-rx: now used by 1 (msg-rx)
+DREF INFO pfcp_entity_peer(1-2-3-4){ASSOCIATED}: + msg-rx: now used by 1 (msg-rx)
DREF INFO up_session(1-2-3-4-0x3){INIT}: + msg-rx: now used by 1 (msg-rx)
DSESSION DEBUG up_session(1-2-3-4-0x3){INIT}: Received Event UP_SESSION_EV_RX_SESSION_EST_REQ
-DREF DEBUG up_peer(1-2-3-4){ASSOCIATED}: + msg-tx: now used by 2 (msg-rx,msg-tx)
+DREF DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: + msg-tx: now used by 2 (msg-rx,msg-tx)
DREF DEBUG up_session(1-2-3-4-0x3){INIT}: + msg-tx: now used by 2 (msg-rx,msg-tx)
DSESSION INFO up_session(1-2-3-4-0x3){INIT}: Allocated new local F-TEID TEID-0x5,v4:1.1.1.1
DSESSION INFO up_session(1-2-3-4-0x3){INIT}: New PDR-1{src:Core TEID-0x5,v4:1.1.1.1 decaps-GTP_U_UDP_IPV4} --> FAR-1{FORW dst:Access,GTP_U_UDP_IPV4,TEID:0x106,v4:5.6.7.8}
@@ -335,16 +336,16 @@
osmo-upf created session 0x3

DREF DEBUG up_session(1-2-3-4-0x3){INIT}: - msg-tx: now used by 1 (msg-rx)
-DREF DEBUG up_peer(1-2-3-4){ASSOCIATED}: - msg-tx: now used by 1 (msg-rx)
+DREF DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: - msg-tx: now used by 1 (msg-rx)
DSESSION DEBUG up_session(1-2-3-4-0x3){INIT}: State change to ESTABLISHED (no timeout)
DSESSION INFO up_session(1-2-3-4-0x3){ESTABLISHED}: Session established: peer:1.2.3.4 SEID-r:0x103 SEID-l:0x3 state:ESTABLISHED PDR-active:2/2 FAR-active:2/2 GTP-active:1
DREF INFO up_session(1-2-3-4-0x3){ESTABLISHED}: - msg-rx: now used by 0 (-)
DSESSION DEBUG up_session(1-2-3-4-0x3){ESTABLISHED}: Received Event UP_SESSION_EV_USE_COUNT_ZERO
-DREF INFO up_peer(1-2-3-4){ASSOCIATED}: - msg-rx: now used by 0 (-)
-DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Received Event UP_PEER_EV_USE_COUNT_ZERO
+DREF INFO pfcp_entity_peer(1-2-3-4){ASSOCIATED}: - msg-rx: now used by 0 (-)
+DPEER DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: Received Event PFCP_ENTITY_PEER_EV_USE_COUNT_ZERO

state:
- | peer up_peer(1-2-3-4) ASSOCIATED
+ | peer pfcp_entity_peer(1-2-3-4) ASSOCIATED
| session[ESTABLISHED]: UP-SEID 0x3; chain_id access=5 core=6; local TEID access=0x6 core=0x5
| session[ESTABLISHED]: UP-SEID 0x1; chain_id access=1 core=2; local TEID access=0x2 core=0x1
| session[ESTABLISHED]: UP-SEID 0x2; chain_id access=3 core=4; local TEID access=0x4 core=0x3
@@ -357,9 +358,10 @@
assert(a->tunmap.access.chain_id == 5)
assert(a->tunmap.core.chain_id == 6)

-DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Terminating (cause = OSMO_FSM_TERM_REGULAR)
+DGTP NOTICE pfcp_node_peer(v4:1.2.3.4): removed
+DPEER DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: Terminating (cause = OSMO_FSM_TERM_REGULAR)
DSESSION DEBUG up_session(1-2-3-4-0x3){ESTABLISHED}: Terminating (cause = OSMO_FSM_TERM_PARENT)
-DSESSION DEBUG up_session(1-2-3-4-0x3){ESTABLISHED}: Removing from parent up_peer(1-2-3-4)
+DSESSION DEBUG up_session(1-2-3-4-0x3){ESTABLISHED}: Removing from parent pfcp_entity_peer(1-2-3-4)
DSESSION DEBUG up_session(1-2-3-4-0x3){ESTABLISHED}: GTP actions: 1 previously active; want active: 0
DSESSION DEBUG up_session(1-2-3-4-0x3){ESTABLISHED}: active: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x106 GTP-access-l:1.1.1.1 TEID-access-l:0x6 GTP-core-r:13.14.15.16 TEID-core-r:0x107 GTP-core-l:1.1.1.1 TEID-core-l:0x5 PFCP-peer:1.2.3.4 SEID-l:0x3 PDR-access:2 PDR-core:1
DSESSION DEBUG up_session(1-2-3-4-0x3){ESTABLISHED}: disabling: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x106 GTP-access-l:1.1.1.1 TEID-access-l:0x6 GTP-core-r:13.14.15.16 TEID-core-r:0x107 GTP-core-l:1.1.1.1 TEID-core-l:0x5 PFCP-peer:1.2.3.4 SEID-l:0x3 PDR-access:2 PDR-core:1
@@ -382,7 +384,7 @@
DSESSION DEBUG up_session(1-2-3-4-0x3){ESTABLISHED}: Freeing instance
DSESSION DEBUG up_session(1-2-3-4-0x3){ESTABLISHED}: Deallocated
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: Terminating (cause = OSMO_FSM_TERM_PARENT)
-DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: Removing from parent up_peer(1-2-3-4)
+DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: Removing from parent pfcp_entity_peer(1-2-3-4)
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: GTP actions: 1 previously active; want active: 0
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: active: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x104 GTP-access-l:1.1.1.1 TEID-access-l:0x2 GTP-core-r:13.14.15.16 TEID-core-r:0x105 GTP-core-l:1.1.1.1 TEID-core-l:0x1 PFCP-peer:1.2.3.4 SEID-l:0x1 PDR-access:2 PDR-core:1
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: disabling: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x104 GTP-access-l:1.1.1.1 TEID-access-l:0x2 GTP-core-r:13.14.15.16 TEID-core-r:0x105 GTP-core-l:1.1.1.1 TEID-core-l:0x1 PFCP-peer:1.2.3.4 SEID-l:0x1 PDR-access:2 PDR-core:1
@@ -405,7 +407,7 @@
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: Freeing instance
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: Deallocated
DSESSION DEBUG up_session(1-2-3-4-0x2){ESTABLISHED}: Terminating (cause = OSMO_FSM_TERM_PARENT)
-DSESSION DEBUG up_session(1-2-3-4-0x2){ESTABLISHED}: Removing from parent up_peer(1-2-3-4)
+DSESSION DEBUG up_session(1-2-3-4-0x2){ESTABLISHED}: Removing from parent pfcp_entity_peer(1-2-3-4)
DSESSION DEBUG up_session(1-2-3-4-0x2){ESTABLISHED}: GTP actions: 1 previously active; want active: 0
DSESSION DEBUG up_session(1-2-3-4-0x2){ESTABLISHED}: active: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x102 GTP-access-l:1.1.1.1 TEID-access-l:0x4 GTP-core-r:13.14.15.16 TEID-core-r:0x103 GTP-core-l:1.1.1.1 TEID-core-l:0x3 PFCP-peer:1.2.3.4 SEID-l:0x2 PDR-access:2 PDR-core:1
DSESSION DEBUG up_session(1-2-3-4-0x2){ESTABLISHED}: disabling: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x102 GTP-access-l:1.1.1.1 TEID-access-l:0x4 GTP-core-r:13.14.15.16 TEID-core-r:0x103 GTP-core-l:1.1.1.1 TEID-core-l:0x3 PFCP-peer:1.2.3.4 SEID-l:0x2 PDR-access:2 PDR-core:1
@@ -427,9 +429,9 @@
DGTP INFO GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x102 GTP-access-l:1.1.1.1 TEID-access-l:0x4 GTP-core-r:13.14.15.16 TEID-core-r:0x103 GTP-core-l:1.1.1.1 TEID-core-l:0x3 PFCP-peer:1.2.3.4 SEID-l:0x2 PDR-access:2 PDR-core:1: Disabled tunmap, nft chain IDs: access--3-> <-4--core
DSESSION DEBUG up_session(1-2-3-4-0x2){ESTABLISHED}: Freeing instance
DSESSION DEBUG up_session(1-2-3-4-0x2){ESTABLISHED}: Deallocated
-DPEER NOTICE up_peer(1-2-3-4){ASSOCIATED}: Peer removed
-DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Freeing instance
-DPEER DEBUG up_peer(1-2-3-4){ASSOCIATED}: Deallocated
+DPEER NOTICE pfcp_entity_peer(1-2-3-4){ASSOCIATED}: Peer removed
+DPEER DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: Freeing instance
+DPEER DEBUG pfcp_entity_peer(1-2-3-4){ASSOCIATED}: Deallocated
[test override] nft_ctx_free()
assert(ctx == fake_nft_ctx)


To view, visit change 41470. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-MessageType: newchange
Gerrit-Project: osmo-upf
Gerrit-Branch: master
Gerrit-Change-Id: Ie64be4fd55157e9646e250f8cbe93ba3f3c19b17
Gerrit-Change-Number: 41470
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin@sysmocom.de>