/* * (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 #include #include #include int up_gtp_action_cmp(const struct up_gtp_action *a, const struct up_gtp_action *b) { int cmp; if (a == b) return 0; if (!a) return -1; if (!b) return 1; #define CMP_MEMB(MEMB) OSMO_CMP(a->MEMB, b->MEMB) if ((cmp = CMP_MEMB(kind))) return cmp; switch (a->kind) { case UP_GTP_U_TUNEND: if ((cmp = CMP_MEMB(tunend.access.local.teid))) return cmp; if ((cmp = CMP_MEMB(tunend.access.remote.teid))) return cmp; cmp = osmo_sockaddr_cmp(&a->tunend.access.remote.addr, &b->tunend.access.remote.addr); if (cmp) return cmp; cmp = osmo_sockaddr_cmp(&a->tunend.core.ue_local_addr, &b->tunend.core.ue_local_addr); if (cmp) return cmp; break; case UP_GTP_U_TUNMAP: if ((cmp = CMP_MEMB(tunmap.access.tun.local.teid))) return cmp; if ((cmp = CMP_MEMB(tunmap.access.tun.remote.teid))) return cmp; if ((cmp = CMP_MEMB(tunmap.core.tun.local.teid))) return cmp; if ((cmp = CMP_MEMB(tunmap.core.tun.remote.teid))) return cmp; break; default: break; } return 0; } static int up_gtp_action_enable_disable(struct up_gtp_action *a, bool enable) { struct upf_gtp_dev *gtp_dev; const struct osmo_sockaddr *gtp_addr; int rc; switch (a->kind) { case UP_GTP_U_TUNEND: if (g_upf->tunend.mockup) { LOG_UP_GTP_ACTION(a, LOGL_NOTICE, "tunend/mockup active, skipping GTP action %s\n", enable ? "enable" : "disable"); return 0; } /* Pick GTP device matching the local F-TEID set up for the GTP tunnel (it is on the Access side) */ gtp_addr = &a->tunend.access.local.addr; gtp_dev = upf_gtp_dev_find_by_local_addr(gtp_addr); if (!gtp_dev) { LOG_UP_GTP_ACTION(a, LOGL_ERROR, "No GTP device open for local address %s, cannot %s" " -- consider configuring 'tunend' / 'dev (create|use) foo %s'\n", osmo_sockaddr_to_str_c(OTC_SELECT, gtp_addr), enable ? "enable" : "disable", osmo_sockaddr_to_str_c(OTC_SELECT, gtp_addr)); return -EIO; } if (enable) rc = upf_gtp_dev_tunend_add(gtp_dev, &a->tunend); else rc = upf_gtp_dev_tunend_del(gtp_dev, &a->tunend); if (rc) { LOG_UP_GTP_ACTION(a, LOGL_ERROR, "Failed to %s GTP tunnel (rc=%d)\n", enable ? "enable" : "disable", rc); return rc; } LOG_UP_GTP_ACTION(a, LOGL_NOTICE, "%s tunend on dev %s\n", enable ? "Enabled" : "Disabled", gtp_dev->name); return 0; case UP_GTP_U_TUNMAP: if (g_upf->tunmap.mockup) { LOG_UP_GTP_ACTION(a, LOGL_NOTICE, "tunmap/mockup active, skipping nftables ruleset %s\n", enable ? "enable" : "disable"); return 0; } if (enable) rc = upf_nft_tunmap_create(&a->tunmap); else rc = upf_nft_tunmap_delete(&a->tunmap); if (rc) { LOG_UP_GTP_ACTION(a, LOGL_ERROR, "Failed to %s nft GTP tunnel mapping (rc=%d)\n", enable ? "enable" : "disable", rc); return rc; } LOG_UP_GTP_ACTION(a, LOGL_NOTICE, "%s tunmap, nft chain IDs: access--%u-> <-%u--core\n", enable ? "Enabled" : "Disabled", a->tunmap.access.chain_id, a->tunmap.core.chain_id); return 0; default: LOG_UP_GTP_ACTION(a, LOGL_ERROR, "Invalid action\n"); return -ENOTSUP; } } int up_gtp_action_enable(struct up_gtp_action *a) { return up_gtp_action_enable_disable(a, true); } int up_gtp_action_disable(struct up_gtp_action *a) { return up_gtp_action_enable_disable(a, false); } int up_gtp_action_to_str_buf(char *buf, size_t buflen, const struct up_gtp_action *a) { struct osmo_strbuf sb = { .buf = buf, .len = buflen }; switch (a->kind) { case UP_GTP_U_TUNEND: OSMO_STRBUF_PRINTF(sb, "GTP:tunend GTP-access-r:"); OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunend.access.remote.addr); OSMO_STRBUF_PRINTF(sb, " TEID-access-r:0x%"PRIx32, a->tunend.access.remote.teid); OSMO_STRBUF_PRINTF(sb, " GTP-access-l:"); OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunend.access.local.addr); OSMO_STRBUF_PRINTF(sb, " TEID-access-l:0x%"PRIx32" IP-core-l:", a->tunend.access.local.teid); OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunend.core.ue_local_addr); break; case UP_GTP_U_TUNMAP: OSMO_STRBUF_PRINTF(sb, "GTP:tunmap GTP-access-r:"); OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunmap.access.tun.remote.addr); OSMO_STRBUF_PRINTF(sb, " TEID-access-r:0x%"PRIx32, a->tunmap.access.tun.remote.teid); OSMO_STRBUF_PRINTF(sb, " GTP-access-l:"); OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunmap.access.tun.local.addr); OSMO_STRBUF_PRINTF(sb, " TEID-access-l:0x%"PRIx32, a->tunmap.access.tun.local.teid); OSMO_STRBUF_PRINTF(sb, " GTP-core-r:"); OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunmap.core.tun.remote.addr); OSMO_STRBUF_PRINTF(sb, " TEID-core-r:0x%"PRIx32, a->tunmap.core.tun.remote.teid); OSMO_STRBUF_PRINTF(sb, " GTP-core-l:"); OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, &a->tunmap.core.tun.local.addr); OSMO_STRBUF_PRINTF(sb, " TEID-core-l:0x%"PRIx32, a->tunmap.core.tun.local.teid); break; case UP_GTP_DROP: OSMO_STRBUF_PRINTF(sb, "GTP:drop"); break; default: OSMO_STRBUF_PRINTF(sb, "GTP:?"); break; } 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); OSMO_STRBUF_PRINTF(sb, " PDR-access:%d", a->pdr_access); OSMO_STRBUF_PRINTF(sb, " PDR-core:%d", a->pdr_core); return sb.chars_needed; } char *up_gtp_action_to_str_c(void *ctx, const struct up_gtp_action *a) { OSMO_NAME_C_IMPL(ctx, 128, "ERROR", up_gtp_action_to_str_buf, a) }