10 Commits

Author SHA1 Message Date
Neels Janosch Hofmeyr
082f8bf321 pick worker by local gtp port
Change-Id: I6fdfc1554d438c5388e152649ef085acf44d9072
2024-08-05 06:50:43 +02:00
Neels Janosch Hofmeyr
02ee722d77 osmo-pfcp-tool: add GTP flooding using io_uring
Freely copying from gtp-load-gen.c, implement GTP flooding in
osmo-pfcp-tool scripts.

Add dependency liburing.

This verbosely commented example script serves as a good explanation:
contrib/osmo-pfcp-tool-scripts/gtp_flood.vty

Related: SYS#6590
Change-Id: I332aa0e2efd55f6e357cde4752a3d8b584db531b
2024-08-05 06:50:43 +02:00
Neels Janosch Hofmeyr
018b72fa4a move pfcp_tool.h to include/osmocom/pfcptool/
Related: SYS#6590
Change-Id: If3e7cc4df3defd08df9e75965715a1be0388ed01
2024-08-05 06:10:38 +02:00
Neels Janosch Hofmeyr
2e8ac2e45a nft: batch nftables commands
Change-Id: Ib0a8e86b29bab1559d94fc55a89daa00ec670318
2024-08-05 06:10:38 +02:00
Neels Janosch Hofmeyr
9d5555b1fc pfcp_tool: add 'date'
Allow scripts to output timestamps to easily measure how long certain
actions took to complete. Related: measuring PFCP session management
performance bounds of osmo-upf.

Change-Id: I0486cc92ea298bb9926a0e5c26da17ba5970a72c
2024-08-05 06:10:38 +02:00
Neels Janosch Hofmeyr
5913ca5e1a n-sessions [4/4]: implement 'n <0-2147483647> session create'
Related: SYS#6590
Change-Id: I74a21cc31296ab89a2acda1da8ae9693c1992e66
2024-08-05 06:10:38 +02:00
Neels Janosch Hofmeyr
97632df922 n-sessions [3/4]: add poll arg to pfcp_tool_mainloop()
Allow calling in non-blocking mode, prepare for N sessions.

Related: SYS#6590
Change-Id: I20bb2803b28681face18ee665d8a1aad06d58091
2024-08-05 06:10:38 +02:00
Neels Janosch Hofmeyr
ccd2c61c50 n-sessions [2/4]: generalize function args
Change some VTY DEFUN into generally callable functions. Prepare for N
sessions commands.

Related: SYS#6590
Change-Id: I112206049e704b7adad7072b1f7953f7ee4f18ca
2024-08-05 06:10:38 +02:00
Neels Janosch Hofmeyr
510f865a79 n-sessions [1/4]: add generators for TEID and UE IP
Add the ability to establish a large number of sessions automatically.
Useful for load testing.

Related: SYS#6590
Change-Id: Iec164a222782d382aefe8d0342f398ebba1eac05
2024-08-05 06:10:15 +02:00
Neels Janosch Hofmeyr
e6b8353a7c always use specific PDR ids for access and core
Makes it easier to find the right one later.

Related: SYS#6590
Change-Id: Ic343494001c70a84f3402ce5749d08e729551b26
2024-08-05 06:09:34 +02:00
19 changed files with 1856 additions and 95 deletions

View File

@@ -42,6 +42,7 @@ PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOPFCP, libosmo-pfcp >= 0.1.0)
PKG_CHECK_MODULES(LIBGTPNL, libgtpnl >= 1.2.0)
PKG_CHECK_MODULES(LIBNFTABLES, libnftables >= 1.0.2)
PKG_CHECK_MODULES(LIBURING, liburing)
dnl checks for header files
AC_HEADER_STDC
@@ -198,6 +199,7 @@ AC_OUTPUT(
include/Makefile
include/osmocom/Makefile
include/osmocom/upf/Makefile
include/osmocom/pfcptool/Makefile
src/Makefile
src/osmo-upf/Makefile
src/osmo-pfcp-tool/Makefile

View File

@@ -0,0 +1,51 @@
# Establish N PFCP sessions for tunend, and emit massive GTP traffic to the UPF
# to each established tunnel.
#
# osmo-pfcp-tool UPF "internet host"
# |GTP-ep -------GTP-----> GTP-ep|UE-IP-addr -------IP------> arbitrary-IP|
# |10.0.1.1 10.0.2.1|192.168.10.23 123.234.42.23|
# |10.0.1.2
# ^ ^ ^
# ^ | | |
# | | configure by configure by
# configure by from UPF 'ue ip' 'target ip',
# 'gtp ip' ("F-TEID=choose") 'target port'
# Configure one or more local GTP endpoints to emit GTP packets from.
# Established sessions will use these round-robin.
# These need to be local IP addresses for 'gtp flood' to work.
gtp local 127.0.1.1 10001
gtp local 127.0.1.2 10002
# use UE IP addresses from this range, +1 for each new UE:
# 192.168.0.1, 192.168.0.2, ...
ue ip range 192.168.23.1 192.168.23.254
# now associate with UPF and start N sessions.
pfcp-peer 127.0.0.11
tx assoc-setup-req
sleep 1
date
n 30 session create tunend
wait responses
# All sessions established
date
# For each established PFCP session, emit GTP packets
gtp flood
workers 2
flows-per-session 1
packets-per-flow 1000
# configure the generated GTP payload: send UDP packets from the UE address
# and these source UDP ports to these target addresses and target UDP ports.
# They are used round-robin.
# Source IP is the UE IP address.
payload source port udp range 10000 10010
payload target ip range 123.234.42.1 123.234.42.254
payload target port udp range 10000 10010
# All GTP is flowing.
# osmo-pfcp-tool will keep this up for as long as there still are active GTP flows,
# or until receiving a signal interrupt (ctrl-C).

View File

@@ -1,3 +1,4 @@
SUBDIRS = \
upf \
pfcptool \
$(NULL)

View File

@@ -0,0 +1,6 @@
noinst_HEADERS = \
checksum.h \
gtp_flood.h \
pfcp_tool.h \
range.h \
$(NULL)

View File

@@ -0,0 +1,13 @@
#pragma once
#include <stdint.h>
#include <netinet/in.h>
uint16_t ip_fast_csum(const void *iph, unsigned int ihl);
uint32_t csum_partial(const void *buff, int len, uint32_t wsum);
uint16_t ip_compute_csum(const void *buff, int len);
uint16_t csum_ipv6_magic(const struct in6_addr *saddr,
const struct in6_addr *daddr,
uint32_t len, uint8_t proto, uint32_t csum);
uint16_t csum_fold(uint32_t csum);

View File

@@ -0,0 +1,34 @@
#pragma once
#include <osmocom/core/socket.h>
/* According to 3GPP TS 29.060. */
struct gtp1u_hdr {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
uint8_t pn:1, s:1, e:1, spare:1, pt:1, version:3;
#else
uint8_t version:3, pt:1, spare:1, e:1, s:1, pn:1;
#endif
uint8_t type;
uint16_t length;
uint32_t tei;
};
struct gtp_flood;
struct udp_port;
struct gtp_flood *gtp_flood_alloc(void *ctx, unsigned int workers);
struct gtp_flood_flow_cfg {
struct udp_port *gtp_local;
struct osmo_sockaddr gtp_remote;
uint32_t gtp_remote_teid;
struct osmo_sockaddr payload_src;
struct osmo_sockaddr payload_dst;
unsigned int num_packets;
};
void gtp_flood_add_flow(struct gtp_flood *gtp_flood,
const struct gtp_flood_flow_cfg *flow_cfg);
void gtp_flood_start(struct gtp_flood *gtp_flood);
bool gtp_flood_is_busy(struct gtp_flood *gtp_flood);

View File

@@ -28,14 +28,22 @@
#include <osmocom/core/tdef.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/vty/command.h>
#include <osmocom/pfcp/pfcp_msg.h>
#include <osmocom/upf/up_gtp_action.h>
#include <osmocom/pfcptool/range.h>
struct osmo_tdef;
struct ctrl_handle;
enum pfcp_tool_vty_node {
PEER_NODE = _LAST_OSMOVTY_NODE + 1,
SESSION_NODE,
GTP_FLOOD_NODE,
};
extern struct osmo_tdef g_pfcp_tool_tdefs[];
extern struct osmo_tdef_group g_pfcp_tool_tdef_groups[];
@@ -90,6 +98,16 @@ struct pfcp_tool_session {
};
};
struct udp_port {
struct llist_head entry;
/* IP address and UDP port from user input */
struct osmo_sockaddr osa;
/* In case this is a locally bound port, this is the fd for the socket. */
struct osmo_fd ofd;
};
struct g_pfcp_tool {
struct ctrl_handle *ctrl;
@@ -100,6 +118,38 @@ struct g_pfcp_tool {
struct osmo_pfcp_endpoint *ep;
struct llist_head peers;
uint32_t next_teid_state;
struct {
struct range ip_range;
} ue;
struct {
/* list of struct udp_port */
struct llist_head gtp_local_addrs;
struct udp_port *gtp_local_addrs_next;
struct {
struct {
/* source address is always the UE IP address */
struct range udp_port_range;
} source;
struct {
struct range ip_range;
struct range udp_port_range;
} target;
} payload;
struct {
unsigned int workers;
unsigned int flows_per_session;
unsigned int packets_per_flow;
struct gtp_flood *state;
} flood;
} gtp;
};
extern struct g_pfcp_tool *g_pfcp_tool;
@@ -108,7 +158,7 @@ void g_pfcp_tool_alloc(void *ctx);
void pfcp_tool_vty_init_cfg();
void pfcp_tool_vty_init_cmds();
int pfcp_tool_mainloop();
int pfcp_tool_mainloop(int poll);
struct pfcp_tool_peer *pfcp_tool_peer_find_or_create(const struct osmo_sockaddr *remote_addr);
struct pfcp_tool_session *pfcp_tool_session_find_or_create(struct pfcp_tool_peer *peer, uint64_t cp_seid,
@@ -117,3 +167,12 @@ void pfcp_tool_rx_msg(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_msg *m, st
int peer_tx(struct pfcp_tool_peer *peer, struct osmo_pfcp_msg *m);
uint64_t peer_new_seid(struct pfcp_tool_peer *peer);
uint32_t pfcp_tool_new_teid(void);
int pfcp_tool_next_ue_addr(struct osmo_sockaddr *dst);
struct udp_port *pfcp_tool_have_udp_port_by_str(const struct osmo_sockaddr_str *addr, uint16_t fallback_port);
void pfcp_tool_gtp_flood_start(void);
int pfcp_tool_vty_go_parent(struct vty *vty);

View File

@@ -0,0 +1,38 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
struct osmo_sockaddr;
/* A value large enough for an IPv6 address (128bit) */
struct range_val {
uint64_t buf[2];
size_t size;
};
/* Manage stepping through a defined range of values large enough to handle IPv6 addresses. */
struct range {
struct range_val first;
struct range_val last;
struct range_val next;
bool wrapped;
};
/* struct range r = {};
* range_val_set_int(&r.first, 23);
* range_val_set_int(&r.last, 42);
* while (1) {
* range_next();
* unsigned int val = range_val_get_int(&r.next);
* printf("%u\n", val);
* }
*/
void range_next(struct range *r);
void range_val_set_int(struct range_val *rv, uint32_t val);
uint32_t range_val_get_int(const struct range_val *rv);
void range_val_set_addr(struct range_val *rv, const struct osmo_sockaddr *val);
void range_val_get_addr(struct osmo_sockaddr *dst, const struct range_val *rv);
void range_val_inc(struct range_val *rv);
int range_val_cmp(const struct range_val *a, const struct range_val *b);

View File

@@ -44,6 +44,7 @@ struct nft_ctx;
#define PORT_GTP1_U 2152
extern struct osmo_tdef_group g_upf_tdef_groups[];
extern struct osmo_tdef g_upf_nft_tdefs[];
struct pfcp_vty_cfg {
char *local_addr;

View File

@@ -11,6 +11,7 @@ AM_CFLAGS = \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOCTRL_CFLAGS) \
$(LIBOSMOPFCP_CFLAGS) \
$(LIBURING_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
@@ -19,19 +20,19 @@ AM_LDFLAGS = \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOPFCP_LIBS) \
$(LIBURING_LIBS) \
$(COVERAGE_LDFLAGS) \
$(NULL)
noinst_HEADERS = \
pfcp_tool.h \
$(NULL)
bin_PROGRAMS = \
osmo-pfcp-tool \
$(NULL)
osmo_pfcp_tool_SOURCES = \
checksum.c \
gtp_flood.c \
osmo_pfcp_tool_main.c \
pfcp_tool.c \
pfcp_tool_vty.c \
range.c \
$(NULL)

View File

@@ -0,0 +1,211 @@
/*
*
* INET An implementation of the TCP/IP protocol suite for the LINUX
* operating system. INET is implemented using the BSD Socket
* interface as the means of communication with the user level.
*
* IP/TCP/UDP checksumming routines
*
* Authors: Jorge Cwik, <jorge@laser.satlink.net>
* Arnt Gulbrandsen, <agulbra@nvg.unit.no>
* Tom May, <ftom@netcom.com>
* Andreas Schwab, <schwab@issan.informatik.uni-dortmund.de>
* Lots of code moved from tcp.c and ip.c; see those files
* for more names.
*
* 03/02/96 Jes Sorensen, Andreas Schwab, Roman Hodek:
* Fixed some nasty bugs, causing some horrible crashes.
* A: At some points, the sum (%0) was used as
* length-counter instead of the length counter
* (%1). Thanks to Roman Hodek for pointing this out.
* B: GCC seems to mess up if one uses too many
* data-registers to hold input values and one tries to
* specify d0 and d1 as scratch registers. Letting gcc
* choose these registers itself solves the problem.
*
* 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.
*/
/* Revised by Kenneth Albanowski for m68knommu. Basic problem: unaligned access
kills, so most of the assembly has to go. */
#if defined(__FreeBSD__)
#define _KERNEL /* needed on FreeBSD 10.x for s6_addr32 */
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/endian.h>
#endif
#include <osmocom/pfcptool/checksum.h>
#include <arpa/inet.h>
static inline unsigned short from32to16(unsigned int x)
{
/* add up 16-bit and 16-bit for 16+c bit */
x = (x & 0xffff) + (x >> 16);
/* add up carry.. */
x = (x & 0xffff) + (x >> 16);
return x;
}
static unsigned int do_csum(const unsigned char *buff, int len)
{
int odd;
unsigned int result = 0;
if (len <= 0)
goto out;
odd = 1 & (unsigned long) buff;
if (odd) {
#if BYTE_ORDER == LITTLE_ENDIAN
result += (*buff << 8);
#else
result = *buff;
#endif
len--;
buff++;
}
if (len >= 2) {
if (2 & (unsigned long) buff) {
result += *(unsigned short *) buff;
len -= 2;
buff += 2;
}
if (len >= 4) {
const unsigned char *end = buff + ((unsigned)len & ~3);
unsigned int carry = 0;
do {
unsigned int w = *(unsigned int *) buff;
buff += 4;
result += carry;
result += w;
carry = (w > result);
} while (buff < end);
result += carry;
result = (result & 0xffff) + (result >> 16);
}
if (len & 2) {
result += *(unsigned short *) buff;
buff += 2;
}
}
if (len & 1)
#if BYTE_ORDER == LITTLE_ENDIAN
result += *buff;
#else
result += (*buff << 8);
#endif
result = from32to16(result);
if (odd)
result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
out:
return result;
}
/*
* This is a version of ip_compute_csum() optimized for IP headers,
* which always checksum on 4 octet boundaries.
*/
uint16_t ip_fast_csum(const void *iph, unsigned int ihl)
{
return (uint16_t)~do_csum(iph, ihl*4);
}
/*
* computes the checksum of a memory block at buff, length len,
* and adds in "sum" (32-bit)
*
* returns a 32-bit number suitable for feeding into itself
* or csum_tcpudp_magic
*
* this function must be called with even lengths, except
* for the last fragment, which may be odd
*
* it's best to have buff aligned on a 32-bit boundary
*/
uint32_t csum_partial(const void *buff, int len, uint32_t wsum)
{
unsigned int sum = (unsigned int)wsum;
unsigned int result = do_csum(buff, len);
/* add in old sum, and carry.. */
result += sum;
if (sum > result)
result += 1;
return (uint32_t)result;
}
/*
* this routine is used for miscellaneous IP-like checksums, mainly
* in icmp.c
*/
uint16_t ip_compute_csum(const void *buff, int len)
{
return (uint16_t)~do_csum(buff, len);
}
uint16_t csum_ipv6_magic(const struct in6_addr *saddr,
const struct in6_addr *daddr,
uint32_t len, uint8_t proto, uint32_t csum)
{
int carry;
uint32_t ulen;
uint32_t uproto;
uint32_t sum = (uint32_t)csum;
sum += (uint32_t)saddr->s6_addr32[0];
carry = (sum < (uint32_t)saddr->s6_addr32[0]);
sum += carry;
sum += (uint32_t)saddr->s6_addr32[1];
carry = (sum < (uint32_t)saddr->s6_addr32[1]);
sum += carry;
sum += (uint32_t)saddr->s6_addr32[2];
carry = (sum < (uint32_t)saddr->s6_addr32[2]);
sum += carry;
sum += (uint32_t)saddr->s6_addr32[3];
carry = (sum < (uint32_t)saddr->s6_addr32[3]);
sum += carry;
sum += (uint32_t)daddr->s6_addr32[0];
carry = (sum < (uint32_t)daddr->s6_addr32[0]);
sum += carry;
sum += (uint32_t)daddr->s6_addr32[1];
carry = (sum < (uint32_t)daddr->s6_addr32[1]);
sum += carry;
sum += (uint32_t)daddr->s6_addr32[2];
carry = (sum < (uint32_t)daddr->s6_addr32[2]);
sum += carry;
sum += (uint32_t)daddr->s6_addr32[3];
carry = (sum < (uint32_t)daddr->s6_addr32[3]);
sum += carry;
ulen = (uint32_t)htonl((uint32_t) len);
sum += ulen;
carry = (sum < ulen);
sum += carry;
uproto = (uint32_t)htonl(proto);
sum += uproto;
carry = (sum < uproto);
sum += carry;
return csum_fold((uint32_t)sum);
}
/* fold a partial checksum */
uint16_t csum_fold(uint32_t csum)
{
uint32_t sum = (uint32_t)csum;
sum = (sum & 0xffff) + (sum >> 16);
sum = (sum & 0xffff) + (sum >> 16);
return (uint16_t)~sum;
}

View File

@@ -0,0 +1,355 @@
/* GTP-U traffic/load generator. Generates a configurable amount of UDP/IP flows using io_uring.
*
* Based on gtp-load-gen.c from https://gitea.osmocom.org/cellular-infrastructure/gtp-load-gen
* which is marked (C) 2021 by Harald Welte <laforge@osmocom.org>
*/
/*
* (C) 2024 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 <liburing.h>
#include <pthread.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/udp.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include <osmocom/pfcptool/gtp_flood.h>
#include <osmocom/pfcptool/pfcp_tool.h>
#include <osmocom/pfcptool/checksum.h>
/* number of packets per submit */
#define SLOW 0
#if SLOW
#define SWEET_SPOT 3
#else
#define SWEET_SPOT 4000
#endif
#define BUF_SIZE 1024
struct gtp_flood_worker;
struct gtp_flood {
/* allocated array */
struct gtp_flood_worker *workers;
unsigned int workers_count;
unsigned int next_worker;
int workers_started;
int workers_running;
};
struct gtp_flood_worker {
/* backpointer */
struct gtp_flood *gtp_flood;
/* list of struct gtp_flood_flow */
struct llist_head flows;
struct io_uring ring;
int fd;
pthread_t worker;
};
struct gtp_flood_flow {
struct llist_head entry;
/* backpointer */
struct gtp_flood_worker *worker;
struct gtp_flood_flow_cfg cfg;
/* for logging and included in generated payloads */
unsigned int id;
/* must live until completion */
struct iovec iov[1];
struct msghdr msgh;
/* flow-private packet buffer */
uint8_t *pkt_buf;
unsigned int sent_gtp_packets;
bool stop;
};
static void gtp_flood_worker_init(struct gtp_flood *gtp_flood, struct gtp_flood_worker *worker)
{
worker->gtp_flood = gtp_flood;
INIT_LLIST_HEAD(&worker->flows);
}
struct gtp_flood *gtp_flood_alloc(void *ctx, unsigned int workers)
{
struct gtp_flood *gtp_flood;
int i;
workers = OSMO_MAX(1, workers);
gtp_flood = talloc_zero(ctx, struct gtp_flood);
*gtp_flood = (struct gtp_flood){
.workers = talloc_array(gtp_flood, struct gtp_flood_worker, workers),
.workers_count = workers,
};
for (i = 0; i < gtp_flood->workers_count; i++)
gtp_flood_worker_init(gtp_flood, &gtp_flood->workers[i]);
LOGP(DLGLOBAL, LOGL_NOTICE, "workers: %u\n", gtp_flood->workers_count);
return gtp_flood;
}
static void gtp_flood_flow_init_payload(struct gtp_flood_flow *flow)
{
struct gtp1u_hdr *gtp_hdr = (void *)flow->pkt_buf;
*gtp_hdr = (struct gtp1u_hdr) {
.pn = 0,
.s = 0,
.e = 0,
.spare = 0,
.pt = 1,
.version = 1,
.type = 0xff, /* G-PDU */
.length = 0, /* filled in later */
.tei = htonl(flow->cfg.gtp_remote_teid),
};
uint8_t *cur = flow->pkt_buf + sizeof(*gtp_hdr);
/* FIXME: randomize this */
unsigned int udp_len = 1024;
struct iphdr *iph;
struct ip6_hdr *ip6h;
struct udphdr *uh;
struct osmo_sockaddr *src = &flow->cfg.payload_src;
struct osmo_sockaddr *dst = &flow->cfg.payload_dst;
if (src->u.sa.sa_family == AF_INET) {
iph = (struct iphdr *) cur;
cur += sizeof(*iph);
iph->ihl = 5;
iph->version = 4;
iph->tos = 0;
iph->tot_len = htons(udp_len + sizeof(struct udphdr) + sizeof(*iph));
iph->id = 0;
iph->frag_off = 0;
iph->ttl = 32;
iph->protocol = IPPROTO_UDP;
iph->saddr = src->u.sin.sin_addr.s_addr;
iph->daddr = dst->u.sin.sin_addr.s_addr;
iph->check = ip_fast_csum(iph, iph->ihl);
} else {
ip6h = (struct ip6_hdr *) cur;
cur += sizeof(*ip6h);
ip6h->ip6_flow = htonl((6 << 28));
ip6h->ip6_plen = htons(udp_len + sizeof(struct udphdr));
ip6h->ip6_nxt = IPPROTO_UDP;
ip6h->ip6_hlim = 32;
ip6h->ip6_src = src->u.sin6.sin6_addr;
ip6h->ip6_dst = dst->u.sin6.sin6_addr;
}
uh = (struct udphdr *) cur;
cur += sizeof(*uh);
uh->source = htons(osmo_sockaddr_port(&src->u.sa));
uh->dest = htons(osmo_sockaddr_port(&dst->u.sa));
uh->len = htons(udp_len);
uh->check = 0; // TODO
gtp_hdr->length = htons(udp_len + (cur - flow->pkt_buf) - sizeof(*gtp_hdr));
/* initialize this once, so we have it ready for each transmit */
flow->msgh.msg_name = &flow->cfg.gtp_remote.u.sa;
flow->msgh.msg_namelen = sizeof(flow->cfg.gtp_remote.u.sa);
flow->msgh.msg_iov = flow->iov;
flow->msgh.msg_iovlen = ARRAY_SIZE(flow->iov);
flow->msgh.msg_control = NULL;
flow->msgh.msg_controllen = 0;
flow->msgh.msg_flags = 0;
flow->iov[0].iov_base = flow->pkt_buf;
flow->iov[0].iov_len = udp_len + (cur - flow->pkt_buf);
/* write some payload */
struct osmo_strbuf sb = { .buf = (void *)cur, .len = udp_len };
OSMO_STRBUF_PRINTF(sb, "osmo-pfcp-tool gtp flood, emitted from %s to %s teid 0x%08x flow %u\n",
osmo_sockaddr_to_str_c(OTC_SELECT, &flow->cfg.gtp_local->osa),
osmo_sockaddr_to_str_c(OTC_SELECT, &flow->cfg.gtp_remote),
flow->cfg.gtp_remote_teid,
flow->id);
}
void gtp_flood_add_flow(struct gtp_flood *gtp_flood,
const struct gtp_flood_flow_cfg *flow_cfg)
{
static unsigned int next_flow_id = 0;
struct gtp_flood_worker *worker = NULL;
/* Place with a worker that handles this same port */
for (int i = 0; !worker && i < gtp_flood->workers_count; i++) {
struct gtp_flood_worker *w = &gtp_flood->workers[i];
struct gtp_flood_flow *f;
llist_for_each_entry (f, &w->flows, entry) {
if (f->cfg.gtp_local != flow_cfg->gtp_local)
continue;
worker = w;
break;
}
}
/* None found, then pick wichever one round robin */
if (!worker) {
gtp_flood->next_worker %= gtp_flood->workers_count;
worker = &gtp_flood->workers[gtp_flood->next_worker];
gtp_flood->next_worker++;
}
OSMO_ASSERT(worker);
struct gtp_flood_flow *flow = talloc_zero(gtp_flood, struct gtp_flood_flow);
flow->cfg = *flow_cfg;
flow->id = next_flow_id++;
flow->pkt_buf = talloc_zero_size(flow, BUF_SIZE);
OSMO_ASSERT(flow->pkt_buf);
flow->worker = worker;
llist_add_tail(&flow->entry, &worker->flows);
gtp_flood_flow_init_payload(flow);
}
/* transmit one packet for a given flow */
static void gtp_flow_tx_one(struct gtp_flood_flow *flow)
{
struct gtp_flood_worker *worker = flow->worker;
struct io_uring_sqe *sqe;
sqe = io_uring_get_sqe(&worker->ring);
OSMO_ASSERT(sqe);
io_uring_prep_sendmsg(sqe, flow->cfg.gtp_local->ofd.fd, &flow->msgh, 0);
//io_uring_sqe_set_data(sqe, flow);
flow->sent_gtp_packets++;
}
static void *gtp_flood_worker_thread(void *_worker)
{
struct gtp_flood_worker *worker = (struct gtp_flood_worker *)_worker;
struct gtp_flood *gtp_flood = worker->gtp_flood;
osmo_ctx_init(__func__);
gtp_flood->workers_started++;
gtp_flood->workers_running++;
LOGP(DLGLOBAL, LOGL_INFO, "gtp flood worker starting (%u started, %u running)\n",
gtp_flood->workers_started,
gtp_flood->workers_running);
while (1) {
uint32_t num_submitted = 0;
int num_pending;
bool all_ended = false;
#if SLOW
sleep(1);
#endif
/* prepare transmit submissions */
while (num_submitted < SWEET_SPOT) {
int submitted_was = num_submitted;
struct gtp_flood_flow *flow;
llist_for_each_entry (flow, &worker->flows, entry) {
if (flow->stop)
continue;
if (flow->cfg.num_packets
&& flow->sent_gtp_packets >= flow->cfg.num_packets) {
flow->stop = true;
continue;
}
gtp_flow_tx_one(flow);
num_submitted++;
}
/* No change in number of submitted PDUs, all flows are done. */
if (submitted_was == num_submitted) {
all_ended = true;
break;
}
}
/* actually submit; determines completions */
num_pending = io_uring_submit(&worker->ring);
/* process all completions */
for (int j = 0; j < num_pending; j++) {
struct io_uring_cqe *cqe;
int rc;
rc = io_uring_wait_cqe(&worker->ring, &cqe);
OSMO_ASSERT(rc >= 0);
//handle_completion(worker, cqe);
io_uring_cqe_seen(&worker->ring, cqe);
}
if (all_ended)
break;
}
gtp_flood->workers_running--;
LOGP(DLGLOBAL, LOGL_INFO, "gtp flood worker done (%u started, %u running)\n",
gtp_flood->workers_started,
gtp_flood->workers_running);
return NULL;
}
static void gtp_flood_worker_start(struct gtp_flood_worker *worker)
{
int rc;
rc = io_uring_queue_init(4000, &worker->ring, 0);
OSMO_ASSERT(rc >= 0);
rc = pthread_create(&worker->worker, NULL, gtp_flood_worker_thread, worker);
OSMO_ASSERT(rc >= 0);
}
void gtp_flood_start(struct gtp_flood *gtp_flood)
{
int i;
for (i = 0; i < gtp_flood->workers_count; i++)
gtp_flood_worker_start(&gtp_flood->workers[i]);
}
bool gtp_flood_is_busy(struct gtp_flood *gtp_flood)
{
if (!gtp_flood)
return false;
return gtp_flood->workers_started && gtp_flood->workers_running;
}

View File

@@ -42,7 +42,8 @@
#include <osmocom/pfcp/pfcp_endpoint.h>
#include "pfcp_tool.h"
#include <osmocom/pfcptool/pfcp_tool.h>
#include <osmocom/pfcptool/gtp_flood.h>
#define _GNU_SOURCE
#include <getopt.h>
@@ -206,7 +207,6 @@ static void signal_handler(int signum)
}
}
static struct vty_app_info pfcp_tool_vty_app_info = {
.name = "osmo-pfcp-tool",
.version = PACKAGE_VERSION,
@@ -216,6 +216,7 @@ static struct vty_app_info pfcp_tool_vty_app_info = {
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
"This is free software: you are free to change and redistribute it.\r\n"
"There is NO WARRANTY, to the extent permitted by law.\r\n",
.go_parent_cb = pfcp_tool_vty_go_parent,
};
static const struct log_info_cat pfcp_tool_default_categories[] = {
@@ -226,14 +227,15 @@ const struct log_info log_info = {
.num_cat = ARRAY_SIZE(pfcp_tool_default_categories),
};
int pfcp_tool_mainloop()
int pfcp_tool_mainloop(int poll)
{
int rc;
log_reset_context();
osmo_select_main_ctx(0);
rc = osmo_select_main_ctx(poll);
/* If the user hits Ctrl-C the third time, just terminate immediately. */
if (quit >= 3)
return 1;
return -1;
/* Has SIGTERM been received (and not yet been handled)? */
if (quit && !osmo_select_shutdown_requested()) {
@@ -243,7 +245,7 @@ int pfcp_tool_mainloop()
osmo_select_shutdown_request();
/* continue the main select loop until all write queues are serviced. */
}
return 0;
return rc;
}
int main(int argc, char **argv)
@@ -326,7 +328,7 @@ int main(int argc, char **argv)
}
}
pfcp_tool_mainloop();
pfcp_tool_mainloop(1);
pfcp_tool_vty_init_cmds();
@@ -338,17 +340,18 @@ int main(int argc, char **argv)
pfcp_tool_cmdline_config.command_file);
return 1;
}
printf("Done reading '%s', waiting for retransmission queue...\n",
printf("Done reading '%s', waiting for tasks to conclude...\n",
pfcp_tool_cmdline_config.command_file);
do {
if (pfcp_tool_mainloop())
if (pfcp_tool_mainloop(0) == -1)
break;
} while (osmo_pfcp_endpoint_retrans_queue_is_busy(g_pfcp_tool->ep));
printf("Done\n");
} while (osmo_pfcp_endpoint_retrans_queue_is_busy(g_pfcp_tool->ep)
|| gtp_flood_is_busy(g_pfcp_tool->gtp.flood.state));
LOGP(DLGLOBAL, LOGL_NOTICE, "Done\n");
} else {
printf("Listening for commands on VTY...\n");
do {
if (pfcp_tool_mainloop())
if (pfcp_tool_mainloop(0) == -1)
break;
} while (!osmo_select_shutdown_done());
}

View File

@@ -26,7 +26,8 @@
#include <osmocom/pfcp/pfcp_endpoint.h>
#include "pfcp_tool.h"
#include <osmocom/pfcptool/pfcp_tool.h>
#include <osmocom/pfcptool/gtp_flood.h>
struct g_pfcp_tool *g_pfcp_tool = NULL;
@@ -45,9 +46,16 @@ void g_pfcp_tool_alloc(void *ctx)
.local_ip = talloc_strdup(g_pfcp_tool, "0.0.0.0"),
.local_port = OSMO_PFCP_PORT,
},
.next_teid_state = 23,
.gtp.flood = {
.workers = 1,
.flows_per_session = 1,
.packets_per_flow = 0,
},
};
INIT_LLIST_HEAD(&g_pfcp_tool->peers);
INIT_LLIST_HEAD(&g_pfcp_tool->gtp.gtp_local_addrs);
}
struct pfcp_tool_peer *pfcp_tool_peer_find(const struct osmo_sockaddr *remote_addr)
@@ -176,6 +184,9 @@ int peer_tx(struct pfcp_tool_peer *peer, struct osmo_pfcp_msg *m)
else
copy_msg(&peer->last_req, m);
rc = osmo_pfcp_endpoint_tx(g_pfcp_tool->ep, m);
if (rc) {
LOGP(DLPFCP, LOGL_ERROR, "Failed to transmit PFCP: %s\n", strerror(-rc));
}
return rc;
}
@@ -183,3 +194,153 @@ uint64_t peer_new_seid(struct pfcp_tool_peer *peer)
{
return peer->next_seid_state++;
}
uint32_t pfcp_tool_new_teid(void)
{
return g_pfcp_tool->next_teid_state++;
}
int pfcp_tool_next_ue_addr(struct osmo_sockaddr *dst)
{
range_next(&g_pfcp_tool->ue.ip_range);
if (g_pfcp_tool->ue.ip_range.wrapped) {
LOGP(DLGLOBAL, LOGL_ERROR, "insufficient UE IP addresses, wrapped back to first\n");
g_pfcp_tool->ue.ip_range.wrapped = false;
}
range_val_get_addr(dst, &g_pfcp_tool->ue.ip_range.next);
return 0;
}
struct udp_port *pfcp_tool_have_udp_port_by_osa(const struct osmo_sockaddr *_osa, uint16_t fallback_port)
{
struct udp_port *port;
/* copy osa and have a non-const pointer */
struct osmo_sockaddr osa_mutable = *_osa;
struct osmo_sockaddr *osa = &osa_mutable;
if (!osmo_sockaddr_port(&osa->u.sa))
osmo_sockaddr_set_port(&osa->u.sa, fallback_port);
OSMO_ASSERT(osmo_sockaddr_port(&osa->u.sa));
llist_for_each_entry (port, &g_pfcp_tool->gtp.gtp_local_addrs, entry) {
if (osmo_sockaddr_cmp(&port->osa, osa) == 0) {
return port;
}
}
LOGP(DLGLOBAL, LOGL_NOTICE, "new port UDP %s\n", osmo_sockaddr_to_str_c(OTC_SELECT, osa));
port = talloc_zero(g_pfcp_tool, struct udp_port);
port->osa = *osa;
/* create and bind socket */
int rc;
rc = osmo_sock_init_osa(SOCK_DGRAM, IPPROTO_UDP, &port->osa, NULL, OSMO_SOCK_F_BIND);
if (rc < 0) {
LOGP(DLGLOBAL, LOGL_ERROR, "Failed to bind socket on UDP %s\n",
osmo_sockaddr_to_str_c(OTC_SELECT, &port->osa));
exit(1);
}
port->ofd.fd = rc;
LOGP(DLGLOBAL, LOGL_NOTICE, "bound UDP %s fd=%d\n",
osmo_sockaddr_to_str_c(OTC_SELECT, &port->osa), rc);
llist_add_tail(&port->entry, &g_pfcp_tool->gtp.gtp_local_addrs);
return port;
}
struct udp_port *pfcp_tool_have_udp_port_by_str(const struct osmo_sockaddr_str *addr, uint16_t fallback_port)
{
struct osmo_sockaddr osa;
if (osmo_sockaddr_str_to_osa(addr, &osa))
return NULL;
return pfcp_tool_have_udp_port_by_osa(&osa, fallback_port);
}
static bool add_session_to_gtp_flow(struct gtp_flood *gf, struct pfcp_tool_session *session)
{
struct range *r;
struct gtp_flood_flow_cfg cfg = {};
const struct pfcp_tool_gtp_tun *tun_access;
const struct osmo_sockaddr_str *ue_local_addr = NULL;
switch (session->kind) {
case UP_GTP_U_TUNEND:
tun_access = &session->tunend.access;
ue_local_addr = &session->tunend.core.ue_local_addr;
break;
case UP_GTP_U_TUNMAP:
tun_access = &session->tunmap.access;
break;
default:
OSMO_ASSERT(false);
}
/* The 'local' and 'remote' naming clashes here. struct pfcp_tool_gtp_tun names its items from the UPF's point
* of view: so the GTP port of osmo-pfcp-tool is 'remote'.
* Here we are setting up a port to emit GTP from osmo-pfcp-tool, the same port is called 'gtp_local'.
*/
cfg.gtp_local = pfcp_tool_have_udp_port_by_str(&tun_access->remote.addr, 2152);
if (!cfg.gtp_local) {
LOGP(DLGLOBAL, LOGL_ERROR, "Cannot set up GTP flow for session, failed to setup local GTP port\n");
return false;
}
/* The 'local' and 'remote' naming clashes here. struct pfcp_tool_gtp_tun names its items from the UPF's point
* of view: so the GTP port of UPF is 'local'.
* Here we are reading the GTP port that the UPF has opened to receive GTP traffic, the same port is called
* 'gtp_remote' in cfg.
*/
if (osmo_sockaddr_str_to_osa(&tun_access->local.addr, &cfg.gtp_remote)) {
LOGP(DLGLOBAL, LOGL_ERROR, "Cannot set up GTP flow for session, failed to identify UPF's GTP port\n");
return false;
}
if (!osmo_sockaddr_port(&cfg.gtp_remote.u.sa))
osmo_sockaddr_set_port(&cfg.gtp_remote.u.sa, 2152);
cfg.gtp_remote_teid = tun_access->local.teid;
if (ue_local_addr) {
osmo_sockaddr_str_to_osa(ue_local_addr, &cfg.payload_src);
} else if (pfcp_tool_next_ue_addr(&cfg.payload_src)) {
LOGP(DLGLOBAL, LOGL_ERROR, "Cannot set up GTP flow for session, failed to identify UE's IP address\n");
return false;
}
r = &g_pfcp_tool->gtp.payload.source.udp_port_range;
range_next(r);
osmo_sockaddr_set_port(&cfg.payload_src.u.sa, range_val_get_int(&r->next));
r = &g_pfcp_tool->gtp.payload.target.ip_range;
range_next(r);
range_val_get_addr(&cfg.payload_dst, &r->next);
r = &g_pfcp_tool->gtp.payload.target.udp_port_range;
range_next(r);
osmo_sockaddr_set_port(&cfg.payload_dst.u.sa, range_val_get_int(&r->next));
cfg.num_packets = g_pfcp_tool->gtp.flood.packets_per_flow;
gtp_flood_add_flow(gf, &cfg);
return true;
}
void pfcp_tool_gtp_flood_start(void)
{
struct gtp_flood *gf;
struct pfcp_tool_peer *peer;
if (g_pfcp_tool->gtp.flood.state) {
LOGP(DLGLOBAL, LOGL_ERROR,
"another 'gtp flood' is still running; currently we can run only one gtp flood at a time\n");
return;
}
gf = g_pfcp_tool->gtp.flood.state = gtp_flood_alloc(g_pfcp_tool, g_pfcp_tool->gtp.flood.workers);
llist_for_each_entry(peer, &g_pfcp_tool->peers, entry) {
struct pfcp_tool_session *session;
llist_for_each_entry(session, &peer->sessions, entry) {
add_session_to_gtp_flow(gf, session);
}
}
gtp_flood_start(gf);
}

View File

@@ -34,12 +34,7 @@
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
#include "pfcp_tool.h"
enum pfcp_tool_vty_node {
PEER_NODE = _LAST_OSMOVTY_NODE + 1,
SESSION_NODE,
};
#include <osmocom/pfcptool/pfcp_tool.h>
DEFUN(c_local_addr, c_local_addr_cmd,
"local-addr IP_ADDR",
@@ -124,7 +119,7 @@ DEFUN(c_sleep, c_sleep_cmd,
/* Still operate the message pump while waiting for time to pass */
while (t.active && !osmo_select_shutdown_done()) {
if (pfcp_tool_mainloop())
if (pfcp_tool_mainloop(0) == -1)
break;
}
@@ -134,6 +129,191 @@ DEFUN(c_sleep, c_sleep_cmd,
return CMD_SUCCESS;
}
DEFUN(c_date, c_date_cmd,
"date",
"print a timestamp\n")
{
struct timeval tv = {};
struct tm tm = {};
osmo_gettimeofday(&tv, NULL);
localtime_r(&tv.tv_sec, &tm);
vty_out(vty,"%04d-%02d-%02d,%02d:%02d:%02d.%03d%s",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
(int)(tv.tv_usec / 1000), VTY_NEWLINE);
vty_flush(vty);
return CMD_SUCCESS;
}
static bool parse_ip_to_range_val(struct vty *vty, struct range_val *val, const char *ip_addr_str)
{
struct osmo_sockaddr_str a;
struct osmo_sockaddr osa;
if (osmo_sockaddr_str_from_str(&a, ip_addr_str, 0)
|| osmo_sockaddr_str_to_osa(&a, &osa)) {
vty_out(vty, "%% Error: invalid IP address: '%s'%s", ip_addr_str, VTY_NEWLINE);
return false;
}
range_val_set_addr(val, &osa);
return true;
}
static bool parse_ip_range(struct vty *vty, struct range *dst, const char *argv[])
{
return parse_ip_to_range_val(vty, &dst->first, argv[0])
&& parse_ip_to_range_val(vty, &dst->last, argv[1]);
}
#define IP_RANGE_STR \
"Set a range with first and last address\n" \
"First IP address in the range, 1.2.3.4 or 1:2:3::4\n" \
"Last IP address in the range, 1.2.3.4 or 1:2:3::4\n"
DEFUN(c_ue_ip_range, c_ue_ip_range_cmd,
"ue ip range IP_ADDR_FIRST IP_ADDR_LAST",
"UE\n"
"Set the IP address range used for the UE IP addresses\n"
IP_RANGE_STR
)
{
if (!parse_ip_range(vty, &g_pfcp_tool->ue.ip_range, &argv[0]))
return CMD_WARNING;
return CMD_SUCCESS;
}
#define GTP_STR "Configure GTP\n"
#define GTP_IP_STR "Add a GTP IP address\n"
#define IP_ADDR_STR "IP address, 1.2.3.4 or 1:2:3:4::1\n"
DEFUN(c_gtp_local, c_gtp_local_cmd,
"gtp local IP_ADDR [<1-65535>]",
GTP_STR
"Add a local GTP port, to emit GTP traffic from\n"
IP_ADDR_STR
"Specific UDP port to use, 2152 if omitted\n")
{
const char *ip_str = argv[0];
uint16_t port_nr;
struct osmo_sockaddr_str addr_str;
struct udp_port *p;
if (argc > 1)
port_nr = atoi(argv[1]);
else
port_nr = 2152;
if (osmo_sockaddr_str_from_str(&addr_str, ip_str, port_nr))
goto invalid_ip;
vty_out(vty, "local GTP port: " OSMO_SOCKADDR_STR_FMT "%s", OSMO_SOCKADDR_STR_FMT_ARGS(&addr_str), VTY_NEWLINE);
p = pfcp_tool_have_udp_port_by_str(&addr_str, port_nr);
if (!p)
goto invalid_ip;
return CMD_SUCCESS;
invalid_ip:
vty_out(vty, "%% Error: invalid IP address: '%s'%s", ip_str, VTY_NEWLINE);
return CMD_WARNING;
}
static struct cmd_node gtp_flood_node = {
GTP_FLOOD_NODE,
"%s(gtp-flood)# ",
1,
};
DEFUN(gtp_flood, gtp_flood_cmd,
"gtp flood",
GTP_STR
"Setup GTP traffic\n")
{
vty->node = GTP_FLOOD_NODE;
return CMD_SUCCESS;
}
DEFUN(gtpf_workers, gtpf_workers_cmd,
"workers <1-999>",
"Number of worker threads to launch, to emit GTP traffic from\n"
"Number\n")
{
g_pfcp_tool->gtp.flood.workers = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(gtpf_flows, gtpf_flows_cmd,
"flows-per-session <1-999999>",
"Set number of GTP packet flows emitted per PFCP session's GTP tunnel\n"
"Number\n")
{
g_pfcp_tool->gtp.flood.flows_per_session = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(gtpf_packets, gtpf_packets_cmd,
"packets-per-flow (<1-2000000000>|infinite)",
"Set number of GTP packet flows emitted per PFCP session's GTP tunnel\n"
"Number\n")
{
unsigned int val;
if (!strcmp("infinite", argv[0]))
val = 0;
else
val = atoi(argv[0]);
g_pfcp_tool->gtp.flood.packets_per_flow = val;
return CMD_SUCCESS;
}
#define PAYLOAD_STR "Configure GTP payload\n"
#define RANGE_STR \
"Set a range to cycle through\n" \
"First value of the range\n" \
"Last value of the range\n"
DEFUN(gtpf_payload_src_port, gtpf_payload_src_port_cmd,
"payload source port udp range <1-65535> <1-65535>",
PAYLOAD_STR
"Source port of payload packets. Note: the source IP will always be the UE IP for that session.\n"
"Port\n"
"UDP port\n"
RANGE_STR)
{
range_val_set_int(&g_pfcp_tool->gtp.payload.source.udp_port_range.first,
atoi(argv[0]));
range_val_set_int(&g_pfcp_tool->gtp.payload.source.udp_port_range.last,
atoi(argv[1]));
return CMD_SUCCESS;
}
#define PAYLOAD_TARGET_STR PAYLOAD_STR "Target of payload packets\n"
DEFUN(gtpf_payload_target_ip, gtpf_payload_target_ip_cmd,
"payload target ip range IP_ADDR_FIRST IP_ADDR_LAST",
PAYLOAD_TARGET_STR
"IP address\n"
RANGE_STR)
{
if (!parse_ip_range(vty, &g_pfcp_tool->gtp.payload.target.ip_range, &argv[0]))
return CMD_WARNING;
return CMD_SUCCESS;
}
DEFUN(gtpf_payload_target_port, gtpf_payload_target_port_cmd,
"payload target port udp range <1-65535> <1-65535>",
PAYLOAD_STR
"Target port of payload packets\n"
"Port\n"
"UDP port\n"
RANGE_STR)
{
range_val_set_int(&g_pfcp_tool->gtp.payload.target.udp_port_range.first,
atoi(argv[0]));
range_val_set_int(&g_pfcp_tool->gtp.payload.target.udp_port_range.last,
atoi(argv[1]));
return CMD_SUCCESS;
}
static struct cmd_node peer_node = {
PEER_NODE,
"%s(peer)# ",
@@ -397,9 +577,16 @@ DEFUN(s_f_teid_choose, s_f_teid_choose_cmd,
return CMD_SUCCESS;
}
int session_tunend_tx_est_req(struct vty *vty, const char **argv, int argc)
enum pdr_id_fixed {
PDR_ID_CORE = 1,
PDR_ID_ACCESS = 2,
};
const char * const gtp_ip_core = "10.99.0.1";
const char * const fallback_gtp_ip_access = "10.99.0.2";
int session_tunend_tx_est_req(struct pfcp_tool_session *session, bool forw, osmo_pfcp_resp_cb resp_cb)
{
struct pfcp_tool_session *session = vty->index;
struct pfcp_tool_peer *peer = session->peer;
int rc;
struct osmo_pfcp_msg *m;
@@ -412,19 +599,19 @@ int session_tunend_tx_est_req(struct vty *vty, const char **argv, int argc)
OSMO_ASSERT(session->kind == UP_GTP_U_TUNEND);
if (!g_pfcp_tool->ep) {
vty_out(vty, "Endpoint not configured%s", VTY_NEWLINE);
LOGP(DLGLOBAL, LOGL_ERROR, "PFCP endpoint not configured\n");
return CMD_WARNING;
}
if (argc > 0 && !strcmp("drop", argv[0]))
osmo_pfcp_bits_set(aa.bits, OSMO_PFCP_APPLY_ACTION_DROP, true);
else
if (forw)
osmo_pfcp_bits_set(aa.bits, OSMO_PFCP_APPLY_ACTION_FORW, true);
else
osmo_pfcp_bits_set(aa.bits, OSMO_PFCP_APPLY_ACTION_DROP, true);
#define STR_TO_ADDR(DST, SRC) do { \
if (osmo_sockaddr_str_to_sockaddr(&SRC, &DST.u.sas)) { \
vty_out(vty, "Error in " #SRC ": " OSMO_SOCKADDR_STR_FMT "%s", \
OSMO_SOCKADDR_STR_FMT_ARGS(&SRC), VTY_NEWLINE); \
LOGP(DLGLOBAL, LOGL_ERROR, "Error in " #SRC ": " OSMO_SOCKADDR_STR_FMT "\n", \
OSMO_SOCKADDR_STR_FMT_ARGS(&SRC)); \
return CMD_WARNING; \
} \
} while (0)
@@ -464,6 +651,10 @@ int session_tunend_tx_est_req(struct vty *vty, const char **argv, int argc)
osmo_pfcp_ip_addrs_set(&cp_f_seid.ip_addr, osmo_pfcp_endpoint_get_local_addr(g_pfcp_tool->ep));
m = osmo_pfcp_msg_alloc_tx_req(OTC_SELECT, &peer->remote_addr, OSMO_PFCP_MSGT_SESSION_EST_REQ);
m->ctx.resp_cb = resp_cb;
m->ctx.priv = session;
m->h.seid_present = true;
/* the UPF has yet to assign a SEID for itself, no matter what SEID we (the CPF) use for this session */
m->h.seid = 0;
@@ -475,7 +666,7 @@ int session_tunend_tx_est_req(struct vty *vty, const char **argv, int argc)
.create_pdr_count = 2,
.create_pdr = {
{
.pdr_id = 1,
.pdr_id = PDR_ID_CORE,
.precedence = 255,
.pdi = {
.source_iface = OSMO_PFCP_SOURCE_IFACE_CORE,
@@ -492,7 +683,7 @@ int session_tunend_tx_est_req(struct vty *vty, const char **argv, int argc)
.far_id = 1,
},
{
.pdr_id = 2,
.pdr_id = PDR_ID_ACCESS,
.precedence = 255,
.pdi = {
.source_iface = OSMO_PFCP_SOURCE_IFACE_ACCESS,
@@ -531,16 +722,13 @@ int session_tunend_tx_est_req(struct vty *vty, const char **argv, int argc)
};
rc = peer_tx(peer, m);
if (rc) {
vty_out(vty, "Failed to transmit: %s%s", strerror(-rc), VTY_NEWLINE);
if (rc)
return CMD_WARNING;
}
return CMD_SUCCESS;
}
int session_tunmap_tx_est_req(struct vty *vty, const char **argv, int argc)
int session_tunmap_tx_est_req(struct pfcp_tool_session *session, bool forw, osmo_pfcp_resp_cb resp_cb)
{
struct pfcp_tool_session *session = vty->index;
struct pfcp_tool_peer *peer = session->peer;
int rc;
struct osmo_pfcp_msg *m;
@@ -556,14 +744,14 @@ int session_tunmap_tx_est_req(struct vty *vty, const char **argv, int argc)
struct osmo_pfcp_ie_apply_action aa = {};
if (!g_pfcp_tool->ep) {
vty_out(vty, "Endpoint not configured%s", VTY_NEWLINE);
LOGP(DLGLOBAL, LOGL_ERROR, "PFCP endpoint not configured\n");
return CMD_WARNING;
}
if (argc > 0 && !strcmp("drop", argv[0]))
osmo_pfcp_bits_set(aa.bits, OSMO_PFCP_APPLY_ACTION_DROP, true);
else
if (forw)
osmo_pfcp_bits_set(aa.bits, OSMO_PFCP_APPLY_ACTION_FORW, true);
else
osmo_pfcp_bits_set(aa.bits, OSMO_PFCP_APPLY_ACTION_DROP, true);
if (session->tunmap.access.local.teid == 0) {
f_teid_access_local = (struct osmo_pfcp_ie_f_teid){
@@ -625,6 +813,10 @@ int session_tunmap_tx_est_req(struct vty *vty, const char **argv, int argc)
osmo_pfcp_ip_addrs_set(&cp_f_seid.ip_addr, osmo_pfcp_endpoint_get_local_addr(g_pfcp_tool->ep));
m = osmo_pfcp_msg_alloc_tx_req(OTC_SELECT, &peer->remote_addr, OSMO_PFCP_MSGT_SESSION_EST_REQ);
m->ctx.resp_cb = resp_cb;
m->ctx.priv = session;
m->h.seid_present = true;
m->h.seid = 0;
/* GTP tunmap: remove header from both directions, and add header in both directions */
@@ -635,7 +827,7 @@ int session_tunmap_tx_est_req(struct vty *vty, const char **argv, int argc)
.create_pdr_count = 2,
.create_pdr = {
{
.pdr_id = 1,
.pdr_id = PDR_ID_CORE,
.precedence = 255,
.pdi = {
.source_iface = OSMO_PFCP_SOURCE_IFACE_CORE,
@@ -650,7 +842,7 @@ int session_tunmap_tx_est_req(struct vty *vty, const char **argv, int argc)
.far_id = 1,
},
{
.pdr_id = 2,
.pdr_id = PDR_ID_ACCESS,
.precedence = 255,
.pdi = {
.source_iface = OSMO_PFCP_SOURCE_IFACE_ACCESS,
@@ -691,10 +883,8 @@ int session_tunmap_tx_est_req(struct vty *vty, const char **argv, int argc)
};
rc = peer_tx(peer, m);
if (rc) {
vty_out(vty, "Failed to transmit: %s%s", strerror(-rc), VTY_NEWLINE);
if (rc)
return CMD_WARNING;
}
return CMD_SUCCESS;
}
@@ -705,24 +895,20 @@ DEFUN(session_tx_est_req, session_tx_est_req_cmd,
"Set FAR to DROP = 1\n")
{
struct pfcp_tool_session *session = vty->index;
bool forw = (argc == 0 || !strcmp("forw", argv[0]));
switch (session->kind) {
case UP_GTP_U_TUNEND:
return session_tunend_tx_est_req(vty, argv, argc);
return session_tunend_tx_est_req(session, forw, NULL);
case UP_GTP_U_TUNMAP:
return session_tunmap_tx_est_req(vty, argv, argc);
return session_tunmap_tx_est_req(session, forw, NULL);
default:
vty_out(vty, "unknown gtp action%s", VTY_NEWLINE);
return CMD_WARNING;
}
}
DEFUN(session_tx_mod_req, session_tx_mod_req_cmd,
"tx session-mod-req far [(forw|drop)]",
TX_STR "Send a Session Modification Request\n"
"Set FAR to FORW = 1\n"
"Set FAR to DROP = 1\n")
int session_tunmap_tx_mod_req(struct pfcp_tool_session *session, bool forw, osmo_pfcp_resp_cb resp_cb)
{
struct pfcp_tool_session *session = vty->index;
struct pfcp_tool_peer *peer = session->peer;
int rc;
struct osmo_pfcp_msg *m;
@@ -730,14 +916,14 @@ DEFUN(session_tx_mod_req, session_tx_mod_req_cmd,
struct osmo_pfcp_ie_f_seid cp_f_seid;
if (!g_pfcp_tool->ep) {
vty_out(vty, "Endpoint not configured%s", VTY_NEWLINE);
LOGP(DLGLOBAL, LOGL_ERROR, "PFCP endpoint not configured\n");
return CMD_WARNING;
}
if (argc > 0 && !strcmp("drop", argv[0]))
osmo_pfcp_bits_set(aa.bits, OSMO_PFCP_APPLY_ACTION_DROP, true);
else
if (forw)
osmo_pfcp_bits_set(aa.bits, OSMO_PFCP_APPLY_ACTION_FORW, true);
else
osmo_pfcp_bits_set(aa.bits, OSMO_PFCP_APPLY_ACTION_DROP, true);
cp_f_seid = (struct osmo_pfcp_ie_f_seid){
.seid = session->cp_seid,
@@ -745,6 +931,10 @@ DEFUN(session_tx_mod_req, session_tx_mod_req_cmd,
osmo_pfcp_ip_addrs_set(&cp_f_seid.ip_addr, osmo_pfcp_endpoint_get_local_addr(g_pfcp_tool->ep));
m = osmo_pfcp_msg_alloc_tx_req(OTC_SELECT, &peer->remote_addr, OSMO_PFCP_MSGT_SESSION_MOD_REQ);
m->ctx.resp_cb = resp_cb;
m->ctx.priv = session;
m->h.seid_present = true;
m->h.seid = session->up_f_seid.seid;
m->ies.session_mod_req = (struct osmo_pfcp_msg_session_mod_req){
@@ -766,13 +956,25 @@ DEFUN(session_tx_mod_req, session_tx_mod_req_cmd,
};
rc = peer_tx(peer, m);
if (rc) {
vty_out(vty, "Failed to transmit: %s%s", strerror(-rc), VTY_NEWLINE);
if (rc)
return CMD_WARNING;
}
return CMD_SUCCESS;
}
DEFUN(session_tx_mod_req, session_tx_mod_req_cmd,
"tx session-mod-req far [(forw|drop)]",
TX_STR "Send a Session Modification Request\n"
"Set FAR to FORW = 1\n"
"Set FAR to DROP = 1\n")
{
struct pfcp_tool_session *session = vty->index;
bool forw = (argc == 0 || !strcmp("forw", argv[0]));
int rc = session_tunmap_tx_mod_req(session, forw, NULL);
if (rc != CMD_SUCCESS)
vty_out(vty, "Failed to send Session Modification Request%s", VTY_NEWLINE);
return rc;
}
DEFUN(session_tx_del_req, session_tx_del_req_cmd,
"tx session-del-req",
TX_STR "Send a Session Deletion Request\n")
@@ -799,6 +1001,327 @@ DEFUN(session_tx_del_req, session_tx_del_req_cmd,
return CMD_SUCCESS;
}
/* N SESSIONS */
static int responses_pending = 0;
DEFUN(wait_responses, wait_responses_cmd,
"wait responses",
"Let some time pass until events have occured\n"
"Wait for all PFCP responses for pending PFCP requests\n")
{
if (!responses_pending) {
vty_out(vty, "no responses pending, not waiting.%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
vty_out(vty, "waiting for %d responses...%s", responses_pending, VTY_NEWLINE);
vty_flush(vty);
/* Still operate the message pump while waiting for time to pass */
while (!osmo_select_shutdown_done()) {
if (pfcp_tool_mainloop(0) == -1)
break;
if (responses_pending == 0)
break;
}
vty_out(vty, "...done waiting for responses%s", VTY_NEWLINE);
vty_flush(vty);
return CMD_SUCCESS;
}
/* N SESSIONS TUNEND */
int one_session_mod_tunend_resp_cb(struct osmo_pfcp_msg *req, struct osmo_pfcp_msg *rx_resp, const char *errmsg)
{
responses_pending--;
return 0;
}
int one_session_mod_tunend(struct pfcp_tool_session *session)
{
int rc;
rc = session_tunmap_tx_mod_req(session, true, one_session_mod_tunend_resp_cb);
if (rc == CMD_SUCCESS)
responses_pending++;
return rc;
}
void est_resp_get_created_f_teid(struct pfcp_tool_gtp_tun_ep *dst, const struct osmo_pfcp_msg *rx_resp, uint16_t pdr_id)
{
int i;
const struct osmo_pfcp_msg_session_est_resp *r;
if (rx_resp->h.message_type != OSMO_PFCP_MSGT_SESSION_EST_RESP)
return;
r = &rx_resp->ies.session_est_resp;
for (i = 0; i < r->created_pdr_count; i++) {
const struct osmo_pfcp_ie_created_pdr *p = &r->created_pdr[i];
if (p->pdr_id != pdr_id)
continue;
if (!p->local_f_teid_present)
continue;
osmo_sockaddr_str_from_sockaddr(&dst->addr,
&p->local_f_teid.fixed.ip_addr.v4.u.sas);
dst->teid = p->local_f_teid.fixed.teid;
}
}
int one_session_create_tunend_resp_cb(struct osmo_pfcp_msg *req, struct osmo_pfcp_msg *rx_resp, const char *errmsg)
{
struct pfcp_tool_session *session = req->ctx.priv;
enum osmo_pfcp_cause *cause;
const struct osmo_pfcp_msg_session_est_resp *r = NULL;
if (rx_resp)
r = &rx_resp->ies.session_est_resp;
responses_pending--;
if (errmsg)
LOGP(DLPFCP, LOGL_ERROR, "%s\n", errmsg);
cause = rx_resp ? osmo_pfcp_msg_cause(rx_resp) : NULL;
if (!cause || *cause != OSMO_PFCP_CAUSE_REQUEST_ACCEPTED)
return 0;
/* store SEID */
if (r && r->up_f_seid_present)
session->up_f_seid = r->up_f_seid;
/* store access local F-TEID */
est_resp_get_created_f_teid(&session->tunend.access.local, rx_resp, PDR_ID_ACCESS);
/* Success response, now continue with second step: Session Mod to set the CORE's remote side GTP */
one_session_mod_tunend(session);
return 0;
}
static int one_session_create_tunend(struct pfcp_tool_peer *peer)
{
struct pfcp_tool_session *session;
struct pfcp_tool_tunend *te;
struct pfcp_tool_gtp_tun_ep *dst;
struct osmo_sockaddr osa;
int rc;
session = pfcp_tool_session_find_or_create(peer, peer_new_seid(peer), UP_GTP_U_TUNEND);
te = &session->tunend;
/* Access: set remote GTP address from UPF's point of view.
* A local GTP port from osmo-pfcp-tool's point of view. */
dst = &te->access.remote;
if (llist_empty(&g_pfcp_tool->gtp.gtp_local_addrs)) {
/* No local GTP ports configured, just set an arbitrary address. */
if (osmo_sockaddr_str_from_str2(&dst->addr, fallback_gtp_ip_access)) {
LOGP(DLGLOBAL, LOGL_ERROR, "Error setting Access side GTP IP address from %s\n",
osmo_quote_cstr_c(OTC_SELECT, fallback_gtp_ip_access, -1));
return CMD_WARNING;
}
} else {
/* next local GTP address to emit from */
struct udp_port *next;
next = g_pfcp_tool->gtp.gtp_local_addrs_next;
if (next) {
/* Move on by one gtp_local_addr. If past the end of the list, wrap back to the start below. */
if (next->entry.next == &g_pfcp_tool->gtp.gtp_local_addrs)
next = NULL;
else
next = container_of(next->entry.next, struct udp_port, entry);
}
if (!next)
next = llist_first_entry_or_null(&g_pfcp_tool->gtp.gtp_local_addrs, struct udp_port,
entry);
OSMO_ASSERT(next);
g_pfcp_tool->gtp.gtp_local_addrs_next = next;
if (osmo_sockaddr_str_from_osa(&dst->addr, &next->osa)) {
LOGP(DLGLOBAL, LOGL_ERROR, "Error setting Access side GTP IP address from %s\n",
osmo_sockaddr_to_str_c(OTC_SELECT, &next->osa));
return CMD_WARNING;
}
}
LOGP(DLGLOBAL, LOGL_NOTICE, "session picked gtp local " OSMO_SOCKADDR_STR_FMT "\n", OSMO_SOCKADDR_STR_FMT_ARGS(&dst->addr));
dst->teid = pfcp_tool_new_teid();
/* Set UE address */
te->access.local = (struct pfcp_tool_gtp_tun_ep){};
pfcp_tool_next_ue_addr(&osa);
if (osmo_sockaddr_str_from_osa(&te->core.ue_local_addr, &osa)) {
LOGP(DLGLOBAL, LOGL_ERROR, "Error setting UE address from %s\n",
osmo_sockaddr_to_str_c(OTC_SELECT, &osa));
return CMD_WARNING;
};
/* Send initial Session Establishment Request */
rc = session_tunend_tx_est_req(session, false, one_session_create_tunend_resp_cb);
if (rc == CMD_SUCCESS)
responses_pending++;
return rc;
}
/* N SESSIONS TUNMAP */
int one_session_mod_tunmap_resp_cb(struct osmo_pfcp_msg *req, struct osmo_pfcp_msg *rx_resp, const char *errmsg)
{
responses_pending--;
return 0;
}
int one_session_mod_tunmap(struct pfcp_tool_session *session)
{
struct pfcp_tool_gtp_tun_ep *dst;
int rc;
dst = &session->tunmap.core.remote;
if (osmo_sockaddr_str_from_str2(&dst->addr, gtp_ip_core)) {
LOGP(DLGLOBAL, LOGL_ERROR, "Error setting GTP IP address from %s\n",
osmo_quote_cstr_c(OTC_SELECT, gtp_ip_core, -1));
return CMD_WARNING;
}
dst->teid = pfcp_tool_new_teid();
rc = session_tunmap_tx_mod_req(session, true, one_session_mod_tunmap_resp_cb);
if (rc == CMD_SUCCESS)
responses_pending++;
return rc;
}
int one_session_create_tunmap_resp_cb(struct osmo_pfcp_msg *req, struct osmo_pfcp_msg *rx_resp, const char *errmsg)
{
struct pfcp_tool_session *session = req->ctx.priv;
enum osmo_pfcp_cause *cause;
responses_pending--;
if (errmsg)
LOGP(DLPFCP, LOGL_ERROR, "%s\n", errmsg);
cause = rx_resp ? osmo_pfcp_msg_cause(rx_resp) : NULL;
if (!cause || *cause != OSMO_PFCP_CAUSE_REQUEST_ACCEPTED)
return 0;
/* store SEID */
if (rx_resp->ies.session_est_resp.up_f_seid_present)
session->up_f_seid = rx_resp->ies.session_est_resp.up_f_seid;
/* store local F-TEIDs */
est_resp_get_created_f_teid(&session->tunmap.access.local, rx_resp, PDR_ID_ACCESS);
est_resp_get_created_f_teid(&session->tunmap.core.local, rx_resp, PDR_ID_CORE);
/* Success response, now continue with second step: Session Mod to set the CORE's remote side GTP */
one_session_mod_tunmap(session);
return 0;
}
static int one_session_create_tunmap(struct pfcp_tool_peer *peer)
{
struct pfcp_tool_session *session;
struct pfcp_tool_tunmap *tm;
struct pfcp_tool_gtp_tun_ep *dst;
int rc;
session = pfcp_tool_session_find_or_create(peer, peer_new_seid(peer), UP_GTP_U_TUNMAP);
tm = &session->tunmap;
/* Access: set remote GTP address */
dst = &tm->access.remote;
if (osmo_sockaddr_str_from_str2(&dst->addr, fallback_gtp_ip_access)) {
LOGP(DLGLOBAL, LOGL_ERROR, "Error setting GTP IP address from %s\n",
osmo_quote_cstr_c(OTC_SELECT, fallback_gtp_ip_access, -1));
return CMD_WARNING;
}
dst->teid = pfcp_tool_new_teid();
/* Core: set remote GTP address */
dst = &tm->core.remote;
if (osmo_sockaddr_str_from_str2(&dst->addr, gtp_ip_core)) {
LOGP(DLGLOBAL, LOGL_ERROR, "Error setting GTP IP address from %s\n",
osmo_quote_cstr_c(OTC_SELECT, gtp_ip_core, -1));
return CMD_WARNING;
}
dst->teid = pfcp_tool_new_teid();
/* Set local F-TEIDs == CHOOSE */
tm->access.local = (struct pfcp_tool_gtp_tun_ep){};
tm->core.local = (struct pfcp_tool_gtp_tun_ep){};
/* Send initial Session Establishment Request */
rc = session_tunmap_tx_est_req(session, false, one_session_create_tunmap_resp_cb);
if (rc == CMD_SUCCESS)
responses_pending++;
return rc;
return CMD_WARNING;
}
static void n_sessions_create(struct vty *vty, struct pfcp_tool_peer *peer, int n, enum up_gtp_action_kind kind)
{
int i;
for (i = 0; i < n; i++) {
int rc;
if (kind == UP_GTP_U_TUNMAP)
rc = one_session_create_tunmap(peer);
else
rc = one_session_create_tunend(peer);
if (rc != CMD_SUCCESS)
break;
/* handle any pending select work */
while (!osmo_select_shutdown_done()) {
rc = pfcp_tool_mainloop(1);
/* quit requested */
if (rc < 0)
return;
/* no fd needed service */
if (rc == 0)
break;
}
/* Every N created sessions, wait for pending responses */
if (!(i & 0x3f) && responses_pending) {
vty_out(vty, "waiting for %d responses...%s", responses_pending, VTY_NEWLINE);
vty_flush(vty);
while (!osmo_select_shutdown_done()) {
if (pfcp_tool_mainloop(0) == -1)
break;
if (responses_pending == 0)
break;
}
}
}
}
static void n_sessions_delete(struct pfcp_tool_peer *peer, int n, enum up_gtp_action_kind kind)
{
}
DEFUN(n_sessions, n_sessions_cmd,
"n (<0-2147483647>|all) session (create|delete) (tunend|tunmap)",
"Batch run\n"
"Perform the action N times, or on all available entries\n"
"In a batch run, create and later delete a number of sessions at once.\n"
"Create N new sessions\n"
"Delete N sessions created earlier\n"
TUNEND_STR TUNMAP_STR)
{
struct pfcp_tool_peer *peer = vty->index;
int n = (strcmp("all", argv[0]) == 0? -1 : atoi(argv[0]));
bool create = (strcmp("create", argv[1]) == 0);
enum up_gtp_action_kind kind;
if (!strcmp(argv[2], "tunmap"))
kind = UP_GTP_U_TUNMAP;
else
kind = UP_GTP_U_TUNEND;
if (create)
n_sessions_create(vty, peer, n, kind);
else
n_sessions_delete(peer, n, kind);
return CMD_SUCCESS;
}
static void install_ve_and_config(struct cmd_element *cmd)
{
install_element_ve(cmd);
@@ -818,23 +1341,54 @@ void pfcp_tool_vty_init_cmds()
OSMO_ASSERT(g_pfcp_tool != NULL);
install_ve_and_config(&c_sleep_cmd);
install_ve_and_config(&c_date_cmd);
install_ve_and_config(&wait_responses_cmd);
install_ve_and_config(&peer_cmd);
install_node(&peer_node, NULL);
install_element(PEER_NODE, &c_sleep_cmd);
install_element(PEER_NODE, &c_date_cmd);
install_element(PEER_NODE, &peer_tx_heartbeat_cmd);
install_element(PEER_NODE, &peer_tx_assoc_setup_req_cmd);
install_element(PEER_NODE, &peer_retrans_req_cmd);
install_element(PEER_NODE, &n_sessions_cmd);
install_element(PEER_NODE, &wait_responses_cmd);
install_element(PEER_NODE, &session_cmd);
install_element(PEER_NODE, &session_endecaps_cmd);
install_node(&session_node, NULL);
install_element(SESSION_NODE, &c_sleep_cmd);
install_element(SESSION_NODE, &c_date_cmd);
install_element(SESSION_NODE, &session_tx_est_req_cmd);
install_element(SESSION_NODE, &session_tx_mod_req_cmd);
install_element(SESSION_NODE, &session_tx_del_req_cmd);
install_element(SESSION_NODE, &s_ue_cmd);
install_element(SESSION_NODE, &s_f_teid_cmd);
install_element(SESSION_NODE, &s_f_teid_choose_cmd);
install_ve_and_config(&c_gtp_local_cmd);
install_ve_and_config(&c_ue_ip_range_cmd);
install_ve_and_config(&gtp_flood_cmd);
install_node(&gtp_flood_node, NULL);
install_element(GTP_FLOOD_NODE, &gtpf_workers_cmd);
install_element(GTP_FLOOD_NODE, &gtpf_flows_cmd);
install_element(GTP_FLOOD_NODE, &gtpf_packets_cmd);
install_element(GTP_FLOOD_NODE, &gtpf_payload_src_port_cmd);
install_element(GTP_FLOOD_NODE, &gtpf_payload_target_ip_cmd);
install_element(GTP_FLOOD_NODE, &gtpf_payload_target_port_cmd);
}
int pfcp_tool_vty_go_parent(struct vty *vty)
{
switch (vty->node) {
case GTP_FLOOD_NODE:
/* exiting a 'gtp flood' configuration, start the flooding */
pfcp_tool_gtp_flood_start();
break;
default:
break;
}
return 0;
}

111
src/osmo-pfcp-tool/range.c Normal file
View File

@@ -0,0 +1,111 @@
/*
* (C) 2024 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 <osmocom/core/socket.h>
#include <osmocom/core/utils.h>
#include <osmocom/pfcptool/range.h>
void range_val_set_int(struct range_val *rv, uint32_t val)
{
*rv = (struct range_val){
.buf = { (uint64_t)val, 0 },
.size = sizeof(val),
};
}
uint32_t range_val_get_int(const struct range_val *rv)
{
return rv->buf[0];
}
void range_val_set_addr(struct range_val *rv, const struct osmo_sockaddr *val)
{
*rv = (struct range_val){};
rv->size = osmo_sockaddr_to_octets((void *)rv->buf, sizeof(rv->buf), val);
#if !OSMO_IS_BIG_ENDIAN
for (int i = 0; i < rv->size / 2; i++) {
uint8_t *rvbuf = (void *)rv->buf;
uint8_t tmp = rvbuf[i];
rvbuf[i] = rvbuf[rv->size - 1 - i];
rvbuf[rv->size - 1 - i] = tmp;
}
#endif
}
void range_val_get_addr(struct osmo_sockaddr *dst, const struct range_val *rv)
{
void *buf;
#if OSMO_IS_BIG_ENDIAN
buf = rv->buf;
#else
int i;
uint8_t rev[sizeof(rv->buf)];
uint8_t *val = (void *)rv->buf;
for (i = 0; i < rv->size; i++)
rev[i] = val[rv->size - 1 - i];
buf = rev;
#endif
osmo_sockaddr_from_octets(dst, buf, rv->size);
}
void range_val_inc(struct range_val *rv)
{
uint64_t was = rv->buf[0];
rv->buf[0]++;
if (rv->buf[0] < was)
rv->buf[1]++;
}
int range_val_cmp(const struct range_val *a, const struct range_val *b)
{
int rc;
if (a == b)
return 0;
if (!a)
return -1;
if (!b)
return 1;
rc = OSMO_CMP(a->buf[0], b->buf[0]);
if (rc)
return rc;
rc = OSMO_CMP(a->buf[1], b->buf[1]);
if (rc)
return rc;
return OSMO_CMP(a->size, b->size);
}
void range_next(struct range *r)
{
if (range_val_cmp(&r->next, &r->first) < 0) {
r->next = r->first;
return;
}
if (range_val_cmp(&r->next, &r->last) >= 0) {
r->next = r->first;
r->wrapped = true;
return;
}
range_val_inc(&r->next);
}

View File

@@ -36,8 +36,20 @@
struct g_upf *g_upf = NULL;
struct osmo_tdef g_upf_nft_tdefs[] = {
{ .T = -32, .default_val = 1000, .unit = OSMO_TDEF_MS,
.desc = "How long to wait for more nft rulesets before flushing in batch",
},
{ .T = -33, .default_val = 1, .unit = OSMO_TDEF_CUSTOM,
.desc = "When reaching this nr of queued nft rulesets, flush the queue",
.max_val = 128,
},
{}
};
struct osmo_tdef_group g_upf_tdef_groups[] = {
{ "pfcp", "PFCP endpoint timers", osmo_pfcp_tdefs, },
{ "nft", "netfilter timers", g_upf_nft_tdefs, },
{}
};

View File

@@ -26,6 +26,7 @@
#include <osmocom/core/talloc.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/timer.h>
#include <osmocom/upf/upf.h>
#include <osmocom/upf/upf_nft.h>
@@ -59,9 +60,10 @@ static char *upf_nft_ruleset_vmap_init(void *ctx, const char *table_name, int pr
table_name);
}
static int upf_nft_run(const char *ruleset)
static int upf_nft_run_now(const char *ruleset)
{
int rc;
const int logmax = 256;
if (g_upf->tunmap.mockup) {
LOGP(DNFT, LOGL_NOTICE, "tunmap/mockup active: not running nft ruleset: '%s'\n", ruleset);
@@ -81,14 +83,135 @@ static int upf_nft_run(const char *ruleset)
return -EIO;
}
LOGP(DNFT, LOGL_DEBUG, "run nft ruleset: %s\n", osmo_quote_str_c(OTC_SELECT, ruleset, -1));
if (log_check_level(DNFT, LOGL_DEBUG)) {
size_t l = strlen(ruleset);
LOGP(DNFT, LOGL_DEBUG, "ran nft ruleset, %zu chars: \"%s%s\"\n",
l,
osmo_escape_cstr_c(OTC_SELECT, ruleset, OSMO_MIN(logmax, l)),
l > logmax ? "..." : "");
}
return 0;
}
struct nft_queue {
struct osmo_tdef *flush_time_tdef;
struct osmo_tdef *ruleset_max_tdef;
struct osmo_strbuf sb;
/* 128 NFT rulesets amount to about 110 kb of char */
char buf[1<<17];
unsigned int ruleset_count;
struct osmo_timer_list timer;
};
static void nft_queue_clear_buf(struct nft_queue *q)
{
q->sb = (struct osmo_strbuf){ .buf = q->buf, .len = sizeof(q->buf) };
q->buf[0] = '\0';
}
static void nft_queue_init(void *ctx, struct nft_queue *q,
struct osmo_tdef *flush_time_tdef,
struct osmo_tdef *ruleset_max_tdef)
{
*q = (struct nft_queue){
.flush_time_tdef = flush_time_tdef,
.ruleset_max_tdef = ruleset_max_tdef,
};
nft_queue_clear_buf(q);
}
static void nft_queue_flush(struct nft_queue *q, const char *reason)
{
static unsigned int flush_count = 0;
static unsigned int ruleset_count = 0;
/* We will now flush the queue empty. A timer needs to run only when the next pending entry is added. */
osmo_timer_del(&q->timer);
/* Nothing to send? */
if (!q->sb.chars_needed)
return;
flush_count++;
ruleset_count += q->ruleset_count;
LOGP(DNFT, LOGL_INFO, "Flushing NFT ruleset queue: %s: n:%u strlen:%zu (flush count: %u avg rules per flush: %s)\n",
reason,
q->ruleset_count, q->sb.chars_needed,
flush_count, osmo_int_to_float_str_c(OTC_SELECT, 10 * ruleset_count / flush_count, 1));
q->ruleset_count = 0;
upf_nft_run_now(q->sb.buf);
nft_queue_clear_buf(q);
}
static void nft_queue_flush_cb(void *q)
{
nft_queue_flush(q, "timeout");
}
static int nft_enqueue(struct nft_queue *q,
int (*tunmap_to_str_buf)(char *buf, size_t len, struct upf_tunmap *tunmap),
struct upf_tunmap *tunmap)
{
int ruleset_max;
struct osmo_strbuf q_sb_was = q->sb;
OSMO_STRBUF_APPEND(q->sb, tunmap_to_str_buf, tunmap);
/* is that being cut off? then revert the addition. This should never happen in practice. */
if (q->sb.chars_needed >= q->sb.len) {
q->sb = q_sb_was;
if (q->sb.pos)
*q->sb.pos = '\0';
nft_queue_flush(q, "reached max nr of chars");
OSMO_STRBUF_APPEND(q->sb, tunmap_to_str_buf, tunmap);
}
/* Append separator -- no problem if that gets cut off. */
OSMO_STRBUF_PRINTF(q->sb, "\n");
q->ruleset_count++;
LOGP(DNFT, LOGL_INFO, "Added NFT ruleset to queue: n:%u strlen:%zu\n",
q->ruleset_count, q->sb.chars_needed);
/* Added a rule, see if it has reached ruleset_max. */
ruleset_max = osmo_tdef_get(q->ruleset_max_tdef, q->ruleset_max_tdef->T, OSMO_TDEF_CUSTOM, 128);
if (q->ruleset_count >= ruleset_max) {
nft_queue_flush(q, "reached max nr of rules");
return 0;
}
/* Item added. If the timer is not running yet, schedule a flush in given timeout */
if (!osmo_timer_pending(&q->timer)) {
struct osmo_tdef *t;
unsigned long us;
osmo_timer_setup(&q->timer, nft_queue_flush_cb, q);
t = q->flush_time_tdef;
us = osmo_tdef_get(t, t->T, OSMO_TDEF_US, 100000);
osmo_timer_schedule(&q->timer, us / 1000000, us % 1000000);
}
return 0;
}
static void nft_queue_free(struct nft_queue *q)
{
osmo_timer_del(&q->timer);
}
static struct nft_queue g_nft_queue = {};
int upf_nft_init()
{
int rc;
nft_queue_init(g_upf, &g_nft_queue,
osmo_tdef_get_entry(g_upf_nft_tdefs, -32),
osmo_tdef_get_entry(g_upf_nft_tdefs, -33));
/* Always set up the default settings, also in mockup mode, so that the VTY reflects sane values */
if (!g_upf->tunmap.table_name)
g_upf->tunmap.table_name = talloc_strdup(g_upf, "osmo-upf");
@@ -106,7 +229,7 @@ int upf_nft_init()
return -EIO;
}
rc = upf_nft_run(upf_nft_tunmap_get_table_init_str(OTC_SELECT));
rc = upf_nft_run_now(upf_nft_tunmap_get_table_init_str(OTC_SELECT));
if (rc) {
LOGP(DNFT, LOGL_ERROR, "Failed to create nft table %s\n",
osmo_quote_str_c(OTC_SELECT, g_upf->tunmap.table_name, -1));
@@ -114,7 +237,7 @@ int upf_nft_init()
}
LOGP(DNFT, LOGL_NOTICE, "Created nft table %s\n", osmo_quote_str_c(OTC_SELECT, g_upf->tunmap.table_name, -1));
rc = upf_nft_run(upf_nft_tunmap_get_vmap_init_str(OTC_SELECT));
rc = upf_nft_run_now(upf_nft_tunmap_get_vmap_init_str(OTC_SELECT));
if (rc) {
LOGP(DNFT, LOGL_ERROR, "Failed to initialize nft verdict map in table %s\n", g_upf->tunmap.table_name);
return rc;
@@ -124,6 +247,7 @@ int upf_nft_init()
int upf_nft_free()
{
nft_queue_free(&g_nft_queue);
if (!g_upf->tunmap.nft_ctx)
return 0;
nft_ctx_free(g_upf->tunmap.nft_ctx);
@@ -263,11 +387,6 @@ static int upf_nft_ruleset_tunmap_create_buf(char *buf, size_t buflen, const str
return sb.chars_needed;
}
static char *upf_nft_ruleset_tunmap_create_c(void *ctx, const struct upf_nft_args *args)
{
OSMO_NAME_C_IMPL(ctx, 1024, "ERROR", upf_nft_ruleset_tunmap_create_buf, args)
}
static int upf_nft_ruleset_tunmap_delete_buf(char *buf, size_t buflen, const struct upf_nft_args *args)
{
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
@@ -280,11 +399,6 @@ static int upf_nft_ruleset_tunmap_delete_buf(char *buf, size_t buflen, const str
return sb.chars_needed;
}
static char *upf_nft_ruleset_tunmap_delete_c(void *ctx, const struct upf_nft_args *args)
{
OSMO_NAME_C_IMPL(ctx, 512, "ERROR", upf_nft_ruleset_tunmap_delete_buf, args)
}
int upf_nft_tunmap_to_str_buf(char *buf, size_t buflen, const struct upf_tunmap *tunmap)
{
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
@@ -344,18 +458,28 @@ char *upf_nft_tunmap_get_vmap_init_str(void *ctx)
g_upf->tunmap.priority_post);
}
char *upf_nft_tunmap_get_ruleset_str(void *ctx, struct upf_tunmap *tunmap)
int upf_nft_tunmap_get_ruleset_str_buf(char *buf, size_t len, struct upf_tunmap *tunmap)
{
struct upf_nft_args args;
upf_nft_args_from_tunmap(&args, tunmap);
return upf_nft_ruleset_tunmap_create_c(ctx, &args);
return upf_nft_ruleset_tunmap_create_buf(buf, len, &args);
}
char *upf_nft_tunmap_get_ruleset_str(void *ctx, struct upf_tunmap *tunmap)
{
OSMO_NAME_C_IMPL(ctx, 1024, "ERROR", upf_nft_tunmap_get_ruleset_str_buf, tunmap)
}
int upf_nft_tunmap_get_ruleset_del_str_buf(char *buf, size_t len, struct upf_tunmap *tunmap)
{
struct upf_nft_args args;
upf_nft_args_from_tunmap(&args, tunmap);
return upf_nft_ruleset_tunmap_delete_buf(buf, len, &args);
}
char *upf_nft_tunmap_get_ruleset_del_str(void *ctx, struct upf_tunmap *tunmap)
{
struct upf_nft_args args;
upf_nft_args_from_tunmap(&args, tunmap);
return upf_nft_ruleset_tunmap_delete_c(ctx, &args);
OSMO_NAME_C_IMPL(ctx, 1024, "ERROR", upf_nft_tunmap_get_ruleset_del_str_buf, tunmap)
}
static int upf_nft_tunmap_ensure_chain_id(struct upf_nft_tun *tun)
@@ -373,10 +497,10 @@ int upf_nft_tunmap_create(struct upf_tunmap *tunmap)
if (upf_nft_tunmap_ensure_chain_id(&tunmap->access)
|| upf_nft_tunmap_ensure_chain_id(&tunmap->core))
return -ENOSPC;
return upf_nft_run(upf_nft_tunmap_get_ruleset_str(OTC_SELECT, tunmap));
return nft_enqueue(&g_nft_queue, upf_nft_tunmap_get_ruleset_str_buf, tunmap);
}
int upf_nft_tunmap_delete(struct upf_tunmap *tunmap)
{
return upf_nft_run(upf_nft_tunmap_get_ruleset_del_str(OTC_SELECT, tunmap));
return nft_enqueue(&g_nft_queue, upf_nft_tunmap_get_ruleset_del_str_buf, tunmap);
}

View File

@@ -8,7 +8,7 @@ DLPFCP NOTICE PFCP endpoint: recovery timestamp = 0x83aa7e80 (0 seconds since UN
[test override] nft_run_cmd_from_buffer():
add table inet osmo-upf { flags owner; };
DNFT DEBUG run nft ruleset: "add table inet osmo-upf { flags owner; };\n"
DNFT DEBUG ran nft ruleset, 42 chars: "add table inet osmo-upf { flags owner; };\n"
DNFT NOTICE Created nft table "osmo-upf"
[test override] nft_run_cmd_from_buffer():
@@ -19,7 +19,7 @@ add map inet osmo-upf tunmap-post { typeof meta mark : verdict; };
add rule inet osmo-upf pre udp dport 2152 ip daddr . @ih,32,32 vmap @tunmap-pre;
add rule inet osmo-upf post meta mark vmap @tunmap-post;
DNFT DEBUG run nft ruleset: "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; };\nadd map inet osmo-upf tunmap-post { typeof meta mark : verdict; };\nadd rule inet osmo-upf pre udp dport 2152 ip daddr . @ih,32,32 vmap @tunmap-pre;\nadd rule inet osmo-upf post meta mark vmap @tunmap-post;\n"
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
@@ -58,6 +58,8 @@ DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: Active PDR set: + PDR-1{src:Core T
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: GTP actions: 0 previously active; want active: 1
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: want: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x100 GTP-access-l:1.1.1.1 TEID-access-l:0x2 GTP-core-r:13.14.15.16 TEID-core-r:0x101 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){INIT}: enabling: GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x100 GTP-access-l:1.1.1.1 TEID-access-l:0x2 GTP-core-r:13.14.15.16 TEID-core-r:0x101 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
DNFT INFO Added NFT ruleset to queue: n:1 strlen:847
DNFT INFO Flushing NFT ruleset queue: reached max nr of rules: n:1 strlen:847 (flush count: 1 avg rules per flush: 1)
[test override] nft_run_cmd_from_buffer():
add chain inet osmo-upf tunmap-pre-1;
@@ -73,7 +75,8 @@ add rule inet osmo-upf tunmap-post-2 ip saddr set 1.1.1.1 udp sport set 2152 @ih
add element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x1 : jump tunmap-pre-2 };
add element inet osmo-upf tunmap-post { 2 : jump tunmap-post-2 };
DNFT DEBUG run nft ruleset: "add chain inet osmo-upf tunmap-pre-1;\nadd rule inet osmo-upf tunmap-pre-1 ip daddr set 13.14.15.16 meta mark set 1 counter accept;\nadd chain inet osmo-upf tunmap-post-1;\nadd rule inet osmo-upf tunmap-post-1 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x101 counter accept;\nadd element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x2 : jump tunmap-pre-1 };\nadd element inet osmo-upf tunmap-post { 1 : jump tunmap-post-1 };\nadd chain inet osmo-upf tunmap-pre-2;\nadd rule inet osmo-upf tunmap-pre-2 ip daddr set 5.6.7.8 meta mark set 2 counter accept;\nadd chain inet osmo-upf tunmap-post-2;\nadd rule inet osmo-upf tunmap-post-2 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x100 counter accept;\nadd element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x1 : jump tunmap-pre-2 };\nadd element inet osmo-upf tunmap-post { 2 : jump tunmap-post-2 };\n"
DNFT DEBUG ran nft ruleset, 847 chars: "add chain inet osmo-upf tunmap-pre-1;\nadd rule inet osmo-upf tunmap-pre-1 ip daddr set 13.14.15.16 meta mark set 1 counter accept;\nadd chain inet osmo-upf tunmap-post-1;\nadd rule inet osmo-upf tunmap-post-1 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32..."
DGTP NOTICE GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x100 GTP-access-l:1.1.1.1 TEID-access-l:0x2 GTP-core-r:13.14.15.16 TEID-core-r:0x101 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: Enabled tunmap, nft chain IDs: access--1-> <-2--core
[test override] PFCP tx:
@@ -123,6 +126,8 @@ DSESSION DEBUG up_session(1-2-3-4-0x2){INIT}: Active PDR set: + PDR-1{src:Core T
DSESSION DEBUG up_session(1-2-3-4-0x2){INIT}: GTP actions: 0 previously active; want active: 1
DSESSION DEBUG up_session(1-2-3-4-0x2){INIT}: want: 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){INIT}: enabling: 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
DNFT INFO Added NFT ruleset to queue: n:1 strlen:847
DNFT INFO Flushing NFT ruleset queue: reached max nr of rules: n:1 strlen:847 (flush count: 2 avg rules per flush: 1)
[test override] nft_run_cmd_from_buffer():
add chain inet osmo-upf tunmap-pre-3;
@@ -138,7 +143,8 @@ add rule inet osmo-upf tunmap-post-4 ip saddr set 1.1.1.1 udp sport set 2152 @ih
add element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x3 : jump tunmap-pre-4 };
add element inet osmo-upf tunmap-post { 4 : jump tunmap-post-4 };
DNFT DEBUG run nft ruleset: "add chain inet osmo-upf tunmap-pre-3;\nadd rule inet osmo-upf tunmap-pre-3 ip daddr set 13.14.15.16 meta mark set 3 counter accept;\nadd chain inet osmo-upf tunmap-post-3;\nadd rule inet osmo-upf tunmap-post-3 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x103 counter accept;\nadd element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x4 : jump tunmap-pre-3 };\nadd element inet osmo-upf tunmap-post { 3 : jump tunmap-post-3 };\nadd chain inet osmo-upf tunmap-pre-4;\nadd rule inet osmo-upf tunmap-pre-4 ip daddr set 5.6.7.8 meta mark set 4 counter accept;\nadd chain inet osmo-upf tunmap-post-4;\nadd rule inet osmo-upf tunmap-post-4 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x102 counter accept;\nadd element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x3 : jump tunmap-pre-4 };\nadd element inet osmo-upf tunmap-post { 4 : jump tunmap-post-4 };\n"
DNFT DEBUG ran nft ruleset, 847 chars: "add chain inet osmo-upf tunmap-pre-3;\nadd rule inet osmo-upf tunmap-pre-3 ip daddr set 13.14.15.16 meta mark set 3 counter accept;\nadd chain inet osmo-upf tunmap-post-3;\nadd rule inet osmo-upf tunmap-post-3 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32..."
DGTP NOTICE 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: Enabled tunmap, nft chain IDs: access--3-> <-4--core
[test override] PFCP tx:
@@ -182,6 +188,8 @@ DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: Received Event UP_SESSION_E
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
DSESSION NOTICE 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)
[test override] nft_run_cmd_from_buffer():
delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x2 };
@@ -193,7 +201,8 @@ delete element inet osmo-upf tunmap-post { 2 };
delete chain inet osmo-upf tunmap-pre-2;
delete chain inet osmo-upf tunmap-post-2;
DNFT DEBUG run nft ruleset: "delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x2 };\ndelete element inet osmo-upf tunmap-post { 1 };\ndelete chain inet osmo-upf tunmap-pre-1;\ndelete chain inet osmo-upf tunmap-post-1;\ndelete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x1 };\ndelete element inet osmo-upf tunmap-post { 2 };\ndelete chain inet osmo-upf tunmap-pre-2;\ndelete chain inet osmo-upf tunmap-post-2;\n"
DNFT DEBUG ran nft ruleset, 381 chars: "delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x2 };\ndelete element inet osmo-upf tunmap-post { 1 };\ndelete chain inet osmo-upf tunmap-pre-1;\ndelete chain inet osmo-upf tunmap-post-1;\ndelete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x1 };\ndelete ..."
DGTP NOTICE GTP:tunmap GTP-access-r:5.6.7.8 TEID-access-r:0x100 GTP-access-l:1.1.1.1 TEID-access-l:0x2 GTP-core-r:13.14.15.16 TEID-core-r:0x101 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: Disabled tunmap, nft chain IDs: access--1-> <-2--core
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
@@ -231,6 +240,8 @@ DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: Active PDR set: + PDR-1{src:Core T
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: GTP actions: 0 previously active; want active: 1
DSESSION DEBUG up_session(1-2-3-4-0x1){INIT}: want: 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){INIT}: enabling: 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
DNFT INFO Added NFT ruleset to queue: n:1 strlen:847
DNFT INFO Flushing NFT ruleset queue: reached max nr of rules: n:1 strlen:847 (flush count: 4 avg rules per flush: 1)
[test override] nft_run_cmd_from_buffer():
add chain inet osmo-upf tunmap-pre-1;
@@ -246,7 +257,8 @@ add rule inet osmo-upf tunmap-post-2 ip saddr set 1.1.1.1 udp sport set 2152 @ih
add element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x1 : jump tunmap-pre-2 };
add element inet osmo-upf tunmap-post { 2 : jump tunmap-post-2 };
DNFT DEBUG run nft ruleset: "add chain inet osmo-upf tunmap-pre-1;\nadd rule inet osmo-upf tunmap-pre-1 ip daddr set 13.14.15.16 meta mark set 1 counter accept;\nadd chain inet osmo-upf tunmap-post-1;\nadd rule inet osmo-upf tunmap-post-1 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x105 counter accept;\nadd element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x2 : jump tunmap-pre-1 };\nadd element inet osmo-upf tunmap-post { 1 : jump tunmap-post-1 };\nadd chain inet osmo-upf tunmap-pre-2;\nadd rule inet osmo-upf tunmap-pre-2 ip daddr set 5.6.7.8 meta mark set 2 counter accept;\nadd chain inet osmo-upf tunmap-post-2;\nadd rule inet osmo-upf tunmap-post-2 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x104 counter accept;\nadd element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x1 : jump tunmap-pre-2 };\nadd element inet osmo-upf tunmap-post { 2 : jump tunmap-post-2 };\n"
DNFT DEBUG ran nft ruleset, 847 chars: "add chain inet osmo-upf tunmap-pre-1;\nadd rule inet osmo-upf tunmap-pre-1 ip daddr set 13.14.15.16 meta mark set 1 counter accept;\nadd chain inet osmo-upf tunmap-post-1;\nadd rule inet osmo-upf tunmap-post-1 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32..."
DGTP NOTICE 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: Enabled tunmap, nft chain IDs: access--1-> <-2--core
[test override] PFCP tx:
@@ -296,6 +308,8 @@ DSESSION DEBUG up_session(1-2-3-4-0x3){INIT}: Active PDR set: + PDR-1{src:Core T
DSESSION DEBUG up_session(1-2-3-4-0x3){INIT}: GTP actions: 0 previously active; want active: 1
DSESSION DEBUG up_session(1-2-3-4-0x3){INIT}: want: 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){INIT}: enabling: 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
DNFT INFO Added NFT ruleset to queue: n:1 strlen:847
DNFT INFO Flushing NFT ruleset queue: reached max nr of rules: n:1 strlen:847 (flush count: 5 avg rules per flush: 1)
[test override] nft_run_cmd_from_buffer():
add chain inet osmo-upf tunmap-pre-5;
@@ -311,7 +325,8 @@ add rule inet osmo-upf tunmap-post-6 ip saddr set 1.1.1.1 udp sport set 2152 @ih
add element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x5 : jump tunmap-pre-6 };
add element inet osmo-upf tunmap-post { 6 : jump tunmap-post-6 };
DNFT DEBUG run nft ruleset: "add chain inet osmo-upf tunmap-pre-5;\nadd rule inet osmo-upf tunmap-pre-5 ip daddr set 13.14.15.16 meta mark set 5 counter accept;\nadd chain inet osmo-upf tunmap-post-5;\nadd rule inet osmo-upf tunmap-post-5 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x107 counter accept;\nadd element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x6 : jump tunmap-pre-5 };\nadd element inet osmo-upf tunmap-post { 5 : jump tunmap-post-5 };\nadd chain inet osmo-upf tunmap-pre-6;\nadd rule inet osmo-upf tunmap-pre-6 ip daddr set 5.6.7.8 meta mark set 6 counter accept;\nadd chain inet osmo-upf tunmap-post-6;\nadd rule inet osmo-upf tunmap-post-6 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32 set 0x106 counter accept;\nadd element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x5 : jump tunmap-pre-6 };\nadd element inet osmo-upf tunmap-post { 6 : jump tunmap-post-6 };\n"
DNFT DEBUG ran nft ruleset, 847 chars: "add chain inet osmo-upf tunmap-pre-5;\nadd rule inet osmo-upf tunmap-pre-5 ip daddr set 13.14.15.16 meta mark set 5 counter accept;\nadd chain inet osmo-upf tunmap-post-5;\nadd rule inet osmo-upf tunmap-post-5 ip saddr set 1.1.1.1 udp sport set 2152 @ih,32,32..."
DGTP NOTICE 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: Enabled tunmap, nft chain IDs: access--5-> <-6--core
[test override] PFCP tx:
@@ -348,6 +363,8 @@ DSESSION DEBUG up_session(1-2-3-4-0x3){ESTABLISHED}: Removing from parent up_pee
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
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: 6 avg rules per flush: 1)
[test override] nft_run_cmd_from_buffer():
delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x6 };
@@ -359,7 +376,8 @@ delete element inet osmo-upf tunmap-post { 6 };
delete chain inet osmo-upf tunmap-pre-6;
delete chain inet osmo-upf tunmap-post-6;
DNFT DEBUG run nft ruleset: "delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x6 };\ndelete element inet osmo-upf tunmap-post { 5 };\ndelete chain inet osmo-upf tunmap-pre-5;\ndelete chain inet osmo-upf tunmap-post-5;\ndelete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x5 };\ndelete element inet osmo-upf tunmap-post { 6 };\ndelete chain inet osmo-upf tunmap-pre-6;\ndelete chain inet osmo-upf tunmap-post-6;\n"
DNFT DEBUG ran nft ruleset, 381 chars: "delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x6 };\ndelete element inet osmo-upf tunmap-post { 5 };\ndelete chain inet osmo-upf tunmap-pre-5;\ndelete chain inet osmo-upf tunmap-post-5;\ndelete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x5 };\ndelete ..."
DGTP NOTICE 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: Disabled tunmap, nft chain IDs: access--5-> <-6--core
DSESSION DEBUG up_session(1-2-3-4-0x3){ESTABLISHED}: Freeing instance
DSESSION DEBUG up_session(1-2-3-4-0x3){ESTABLISHED}: Deallocated
@@ -368,6 +386,8 @@ DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: Removing from parent up_pee
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
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: 7 avg rules per flush: 1)
[test override] nft_run_cmd_from_buffer():
delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x2 };
@@ -379,7 +399,8 @@ delete element inet osmo-upf tunmap-post { 2 };
delete chain inet osmo-upf tunmap-pre-2;
delete chain inet osmo-upf tunmap-post-2;
DNFT DEBUG run nft ruleset: "delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x2 };\ndelete element inet osmo-upf tunmap-post { 1 };\ndelete chain inet osmo-upf tunmap-pre-1;\ndelete chain inet osmo-upf tunmap-post-1;\ndelete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x1 };\ndelete element inet osmo-upf tunmap-post { 2 };\ndelete chain inet osmo-upf tunmap-pre-2;\ndelete chain inet osmo-upf tunmap-post-2;\n"
DNFT DEBUG ran nft ruleset, 381 chars: "delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x2 };\ndelete element inet osmo-upf tunmap-post { 1 };\ndelete chain inet osmo-upf tunmap-pre-1;\ndelete chain inet osmo-upf tunmap-post-1;\ndelete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x1 };\ndelete ..."
DGTP NOTICE 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: Disabled tunmap, nft chain IDs: access--1-> <-2--core
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: Freeing instance
DSESSION DEBUG up_session(1-2-3-4-0x1){ESTABLISHED}: Deallocated
@@ -388,6 +409,8 @@ DSESSION DEBUG up_session(1-2-3-4-0x2){ESTABLISHED}: Removing from parent up_pee
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
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: 8 avg rules per flush: 1)
[test override] nft_run_cmd_from_buffer():
delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x4 };
@@ -399,7 +422,8 @@ delete element inet osmo-upf tunmap-post { 4 };
delete chain inet osmo-upf tunmap-pre-4;
delete chain inet osmo-upf tunmap-post-4;
DNFT DEBUG run nft ruleset: "delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x4 };\ndelete element inet osmo-upf tunmap-post { 3 };\ndelete chain inet osmo-upf tunmap-pre-3;\ndelete chain inet osmo-upf tunmap-post-3;\ndelete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x3 };\ndelete element inet osmo-upf tunmap-post { 4 };\ndelete chain inet osmo-upf tunmap-pre-4;\ndelete chain inet osmo-upf tunmap-post-4;\n"
DNFT DEBUG ran nft ruleset, 381 chars: "delete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x4 };\ndelete element inet osmo-upf tunmap-post { 3 };\ndelete chain inet osmo-upf tunmap-pre-3;\ndelete chain inet osmo-upf tunmap-post-3;\ndelete element inet osmo-upf tunmap-pre { 1.1.1.1 . 0x3 };\ndelete ..."
DGTP NOTICE 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