/* * (C) 2021-2022 by sysmocom - s.f.m.c. GmbH * All Rights Reserved. * * Author: Neels Janosch Hofmeyr * * 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 . * */ #include #include #include #include "pfcp_tool.h" struct g_pfcp_tool *g_pfcp_tool = NULL; struct osmo_tdef_group g_pfcp_tool_tdef_groups[] = { { .name = "pfcp", .tdefs = osmo_pfcp_tdefs, .desc = "PFCP" }, {} }; void g_pfcp_tool_alloc(void *ctx) { OSMO_ASSERT(g_pfcp_tool == NULL); g_pfcp_tool = talloc_zero(ctx, struct g_pfcp_tool); *g_pfcp_tool = (struct g_pfcp_tool){ .vty_cfg = { .local_ip = talloc_strdup(g_pfcp_tool, "0.0.0.0"), .local_port = OSMO_PFCP_PORT, }, }; INIT_LLIST_HEAD(&g_pfcp_tool->peers); } struct pfcp_tool_peer *pfcp_tool_peer_find(const struct osmo_sockaddr *remote_addr) { struct pfcp_tool_peer *peer; llist_for_each_entry(peer, &g_pfcp_tool->peers, entry) { if (osmo_sockaddr_cmp(&peer->remote_addr, remote_addr) == 0) return peer; } return NULL; } struct pfcp_tool_peer *pfcp_tool_peer_find_or_create(const struct osmo_sockaddr *remote_addr) { struct pfcp_tool_peer *peer = pfcp_tool_peer_find(remote_addr); if (peer) return peer; peer = talloc_zero(g_pfcp_tool, struct pfcp_tool_peer); peer->remote_addr = *remote_addr; peer->next_seid_state = 0x1234567; INIT_LLIST_HEAD(&peer->sessions); llist_add(&peer->entry, &g_pfcp_tool->peers); return peer; } struct pfcp_tool_session *pfcp_tool_session_find(struct pfcp_tool_peer *peer, uint64_t cp_seid) { struct pfcp_tool_session *session; llist_for_each_entry(session, &peer->sessions, entry) { if (session->cp_seid == cp_seid) return session; } return NULL; } struct pfcp_tool_session *pfcp_tool_session_find_or_create(struct pfcp_tool_peer *peer, uint64_t cp_seid, enum up_gtp_action_kind kind) { struct pfcp_tool_session *session = pfcp_tool_session_find(peer, cp_seid); if (session) return session; session = talloc(peer, struct pfcp_tool_session); *session = (struct pfcp_tool_session){ .peer = peer, .cp_seid = cp_seid, .kind = kind, }; llist_add(&session->entry, &peer->sessions); return session; } static void rx_assoc_setup_resp(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_msg *m) { if (m->ies.assoc_setup_resp.up_function_features_present) OSMO_LOG_PFCP_MSG(m, LOGL_NOTICE, "Associated. UP Peer features: %s\n", osmo_pfcp_bits_to_str_c(OTC_SELECT, m->ies.assoc_setup_resp.up_function_features.bits, osmo_pfcp_up_feature_strs)); if (m->ies.assoc_setup_resp.cp_function_features_present) OSMO_LOG_PFCP_MSG(m, LOGL_NOTICE, "Associated. CP Peer features: %s\n", osmo_pfcp_bits_to_str_c(OTC_SELECT, m->ies.assoc_setup_resp.cp_function_features.bits, osmo_pfcp_cp_feature_strs)); } static void rx_session_est_resp(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_msg *m) { struct pfcp_tool_peer *peer; struct pfcp_tool_session *session; enum osmo_pfcp_cause *cause = osmo_pfcp_msg_cause(m); if (!cause) { OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Session Establishment Response should contain a Cause\n"); return; } if (*cause != OSMO_PFCP_CAUSE_REQUEST_ACCEPTED) { OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Peer responds that Session Establishment failed\n"); return; } if (!m->h.seid_present) { OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Session Establishment Response should contain a SEID\n"); return; } if (!m->ies.session_est_resp.up_f_seid_present) { OSMO_LOG_PFCP_MSG(m, LOGL_ERROR, "Session Establishment Response without UP F-SEID\n"); return; } peer = pfcp_tool_peer_find(&m->remote_addr); if (!peer) return; session = pfcp_tool_session_find(peer, m->h.seid); if (!session) return; session->up_f_seid = m->ies.session_est_resp.up_f_seid; } void pfcp_tool_rx_msg(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_msg *m, struct osmo_pfcp_msg *req) { switch (m->h.message_type) { case OSMO_PFCP_MSGT_ASSOC_SETUP_RESP: rx_assoc_setup_resp(ep, m); break; case OSMO_PFCP_MSGT_SESSION_EST_RESP: rx_session_est_resp(ep, m); break; default: break; } } static void copy_msg(struct osmo_pfcp_msg *dst, const struct osmo_pfcp_msg *m) { *dst = *m; dst->encoded = NULL; dst->ctx.peer_use_token = NULL; dst->ctx.session_use_token = NULL; dst->ctx.resp_cb = NULL; } int peer_tx(struct pfcp_tool_peer *peer, struct osmo_pfcp_msg *m) { int rc; if (m->is_response) copy_msg(&peer->last_resp, m); else copy_msg(&peer->last_req, m); rc = osmo_pfcp_endpoint_tx(g_pfcp_tool->ep, m); return rc; } uint64_t peer_new_seid(struct pfcp_tool_peer *peer) { return peer->next_seid_state++; }