fixeria submitted this change.

View Change



3 is the latest approved patch-set.
No files were changed between the latest approved patch-set and the submitted one.

Approvals: pespin: Looks good to me, approved Jenkins Builder: Verified laforge: Looks good to me, but someone else must approve
osmo-trx-proxy: initial app skeleton

osmo-trx-proxy is a C rewrite of the fake_trx.py: a virtual
TRX endpoint that BTS/MS-side L1 implementations (osmo-bts-trx,
trxcon) can connect to over the TRXC/TRXD protocol instead of real RF
hardware, useful for testing without SDR devices. This first commit
adds just the daemon skeleton (config context, VTY, logging).

Change-Id: Id2b12e5e7d053eea53338fbabc48131b9c46c82a
Related: OS#6672
---
M .gitignore
M Makefile.am
M configure.ac
A proxy/Makefile.am
A proxy/include/osmocom/proxy/logging.h
A proxy/include/osmocom/proxy/proxy.h
A proxy/include/osmocom/proxy/trx.h
A proxy/include/osmocom/proxy/vty.h
A proxy/src/Makefile.am
A proxy/src/logging.c
A proxy/src/main.c
A proxy/src/proxy.c
A proxy/src/trx.c
A proxy/src/vty.c
14 files changed, 871 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore
index 164e863..8c51628 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,7 @@
Transceiver52M/osmo-trx-ms-uhd
Transceiver52M/osmo-trx-ms-ipc
Transceiver52M/device/ipc/uhddev_ipc.cpp
+proxy/src/osmo-trx-proxy

.clang-format

diff --git a/Makefile.am b/Makefile.am
index 844d6e7..2585fdf 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -35,6 +35,13 @@
# Order must be preserved
SUBDIRS += \
libosmo-trx \
+ $(NULL)
+
+if ENABLE_PROXY
+SUBDIRS += proxy
+endif
+
+SUBDIRS += \
CommonLibs \
GSM \
Transceiver52M \
diff --git a/configure.ac b/configure.ac
index e18f943..fd71f47 100644
--- a/configure.ac
+++ b/configure.ac
@@ -162,6 +162,11 @@
[enable MS TRX])
])

+AC_ARG_WITH(proxy, [
+ AS_HELP_STRING([--with-proxy],
+ [enable osmo-trx-proxy [default=enabled]])
+])
+
AC_ARG_WITH(neon, [
AS_HELP_STRING([--with-neon],
[enable ARM NEON support])
@@ -288,6 +293,7 @@
AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])
AM_CONDITIONAL(ARCH_ARM_A15, [test "x$with_neon_vfpv4" = "xyes"])
AM_CONDITIONAL(ENABLE_MS_TRX, [test "x$with_mstrx" = "xyes"])
+AM_CONDITIONAL(ENABLE_PROXY, [test "x$with_proxy" != "xno"])

# Disable Multi ARFCN support
AC_ARG_ENABLE(multi-arfcn,
@@ -375,6 +381,8 @@
libosmo-trx/src/Makefile \
libosmo-trx/libosmo-trx.pc \
libosmo-trx/Doxyfile.trx \
+ proxy/Makefile \
+ proxy/src/Makefile \
CommonLibs/Makefile \
GSM/Makefile \
Transceiver52M/Makefile \
diff --git a/proxy/Makefile.am b/proxy/Makefile.am
new file mode 100644
index 0000000..23a1d3a
--- /dev/null
+++ b/proxy/Makefile.am
@@ -0,0 +1,10 @@
+SUBDIRS = \
+ src \
+ $(NULL)
+
+noinst_HEADERS = \
+ include/osmocom/proxy/logging.h \
+ include/osmocom/proxy/proxy.h \
+ include/osmocom/proxy/trx.h \
+ include/osmocom/proxy/vty.h \
+ $(NULL)
diff --git a/proxy/include/osmocom/proxy/logging.h b/proxy/include/osmocom/proxy/logging.h
new file mode 100644
index 0000000..6e94ba0
--- /dev/null
+++ b/proxy/include/osmocom/proxy/logging.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <osmocom/core/logging.h>
+
+extern const struct log_info log_info;
+
+enum {
+ DPROXY, /* main/vty/config */
+ DTRXC, /* TRXC (control) + clock handling */
+ DTRXD, /* TRXD (burst data) handling / forwarding */
+};
diff --git a/proxy/include/osmocom/proxy/proxy.h b/proxy/include/osmocom/proxy/proxy.h
new file mode 100644
index 0000000..080ff53
--- /dev/null
+++ b/proxy/include/osmocom/proxy/proxy.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <stdbool.h>
+
+#include <osmocom/core/linuxlist.h>
+
+#define PROXY_DEFAULT_BIND_ADDR "0.0.0.0"
+
+/* Endpoints created when the config file defines none explicitly */
+#define PROXY_DEFAULT_REMOTE_ADDR "127.0.0.1"
+#define PROXY_DEFAULT_BTS_NAME "bts"
+#define PROXY_DEFAULT_BTS_PORT 5700
+#define PROXY_DEFAULT_MS_NAME "ms"
+#define PROXY_DEFAULT_MS_PORT 6700
+
+struct proxy_ctx {
+ char *bind_addr;
+ struct llist_head trx_list; /*!< struct proxy_trx::list */
+};
+
+extern struct proxy_ctx *g_proxy_ctx;
+
+struct proxy_ctx *proxy_ctx_alloc(void *talloc_ctx);
+void proxy_ctx_free(struct proxy_ctx *proxy);
+int proxy_ctx_add_defaults_if_empty(struct proxy_ctx *proxy);
diff --git a/proxy/include/osmocom/proxy/trx.h b/proxy/include/osmocom/proxy/trx.h
new file mode 100644
index 0000000..e687825
--- /dev/null
+++ b/proxy/include/osmocom/proxy/trx.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/logging.h>
+
+struct osmo_trx_ep;
+struct proxy_ctx;
+
+/*! One virtual transceiver endpoint */
+struct proxy_trx {
+ struct llist_head list;
+ char *name;
+ struct osmo_trx_ep *ep;
+};
+
+#define LOGP_TRX(trx, ss, level, fmt, args...) \
+ LOGP(ss, level, "(trx=%s) " fmt, (trx)->name, ##args)
+
+struct proxy_trx *proxy_trx_find(struct proxy_ctx *proxy, const char *name);
+struct proxy_trx *proxy_trx_alloc(struct proxy_ctx *proxy, const char *name);
+void proxy_trx_free(struct proxy_trx *trx);
+
+int proxy_trx_open(struct proxy_trx *trx);
+void proxy_trx_close(struct proxy_trx *trx);
diff --git a/proxy/include/osmocom/proxy/vty.h b/proxy/include/osmocom/proxy/vty.h
new file mode 100644
index 0000000..e23b350
--- /dev/null
+++ b/proxy/include/osmocom/proxy/vty.h
@@ -0,0 +1,3 @@
+#pragma once
+
+int proxy_vty_init(void);
diff --git a/proxy/src/Makefile.am b/proxy/src/Makefile.am
new file mode 100644
index 0000000..0350474
--- /dev/null
+++ b/proxy/src/Makefile.am
@@ -0,0 +1,29 @@
+AM_CPPFLAGS = \
+ -Wall \
+ -I$(top_srcdir)/proxy/include \
+ -I$(top_srcdir)/libosmo-trx/include \
+ -I$(top_builddir)/libosmo-trx/include \
+ $(NULL)
+
+AM_CFLAGS = \
+ $(LIBOSMOCORE_CFLAGS) \
+ $(LIBOSMOVTY_CFLAGS) \
+ $(LIBOSMOGSM_CFLAGS) \
+ $(NULL)
+
+bin_PROGRAMS = osmo-trx-proxy
+
+osmo_trx_proxy_SOURCES = \
+ main.c \
+ proxy.c \
+ vty.c \
+ trx.c \
+ logging.c \
+ $(NULL)
+
+osmo_trx_proxy_LDADD = \
+ $(top_builddir)/libosmo-trx/src/libosmo-trx.la \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOVTY_LIBS) \
+ $(LIBOSMOGSM_LIBS) \
+ $(NULL)
diff --git a/proxy/src/logging.c b/proxy/src/logging.c
new file mode 100644
index 0000000..b1d3fbb
--- /dev/null
+++ b/proxy/src/logging.c
@@ -0,0 +1,57 @@
+/*! \file src/logging.c
+ * osmo-trx-proxy: log category definitions. */
+
+/*
+ * (C) 2026 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+
+#include <osmocom/proxy/logging.h>
+
+static const struct log_info_cat default_categories[] = {
+ [DPROXY] = {
+ .name = "DPROXY",
+ .description = "Main generic category",
+ .loglevel = LOGL_INFO,
+ .enabled = 1,
+ },
+ [DTRXC] = {
+ .name = "DTRXC",
+ .description = "TRXC (control) interface + clock handling",
+ .color = "\033[1;33m",
+ .loglevel = LOGL_NOTICE,
+ .enabled = 1,
+ },
+ [DTRXD] = {
+ .name = "DTRXD",
+ .description = "TRXD (burst data) interface handling / forwarding",
+ .color = "\033[1;33m",
+ .loglevel = LOGL_NOTICE,
+ .enabled = 1,
+ },
+};
+
+const struct log_info log_info = {
+ .cat = default_categories,
+ .num_cat = ARRAY_SIZE(default_categories),
+};
diff --git a/proxy/src/main.c b/proxy/src/main.c
new file mode 100644
index 0000000..5ac023a
--- /dev/null
+++ b/proxy/src/main.c
@@ -0,0 +1,150 @@
+/*! \file src/main.c
+ * osmo-trx-proxy: main program entry point. */
+
+/*
+ * (C) 2026 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <getopt.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/application.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/cpu_sched_vty.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/ports.h>
+
+#include <osmocom/proxy/proxy.h>
+#include <osmocom/proxy/vty.h>
+#include <osmocom/proxy/trx.h>
+#include <osmocom/proxy/logging.h>
+
+void *g_talloc_ctx;
+static char *g_config_file = "osmo-trx-proxy.cfg";
+static volatile sig_atomic_t g_quit = 0;
+
+static void print_help(void)
+{
+ printf("Usage: osmo-trx-proxy [options]\n");
+ printf(" -h, --help This text\n");
+ printf(" -c, --config-file FILE Specify the configuration file (default: %s)\n",
+ g_config_file);
+}
+
+static void handle_options(int argc, char **argv)
+{
+ while (1) {
+ int option_index = 0;
+ static const struct option long_options[] = {
+ { "help", 0, 0, 'h' },
+ { "config-file", 1, 0, 'c' },
+ { 0, 0, 0, 0 }
+ };
+
+ int c = getopt_long(argc, argv, "hc:", long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_help();
+ exit(0);
+ case 'c':
+ g_config_file = optarg;
+ break;
+ default:
+ exit(2);
+ }
+ }
+}
+
+static void signal_handler(int signum)
+{
+ switch (signum) {
+ case SIGINT:
+ case SIGTERM:
+ g_quit = 1;
+ break;
+ default:
+ break;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int rc;
+
+ g_talloc_ctx = talloc_named_const(NULL, 0, "osmo-trx-proxy");
+ msgb_talloc_ctx_init(g_talloc_ctx, 0);
+
+ signal(SIGINT, &signal_handler);
+ signal(SIGTERM, &signal_handler);
+ osmo_init_ignore_signals();
+
+ g_proxy_ctx = proxy_ctx_alloc(g_talloc_ctx);
+ OSMO_ASSERT(g_proxy_ctx != NULL);
+
+ osmo_init_logging2(g_talloc_ctx, &log_info);
+ log_set_print_extended_timestamp(osmo_stderr_target, 1);
+ log_set_print_category_hex(osmo_stderr_target, 0);
+ log_set_print_category(osmo_stderr_target, 1);
+ log_set_print_level(osmo_stderr_target, 1);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
+ log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END);
+
+ proxy_vty_init();
+
+ handle_options(argc, argv);
+
+ rc = vty_read_config_file(g_config_file, NULL);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to open config file: '%s'\n", g_config_file);
+ return 2;
+ }
+
+ rc = telnet_init_default(g_talloc_ctx, NULL, OSMO_VTY_PORT_TRX);
+ if (rc < 0)
+ return 1;
+
+ osmo_cpu_sched_vty_apply_localthread();
+
+ /* Only apply the built-in BTS/MS defaults if the config file did not
+ * declare any 'ep' stanza of its own. */
+ rc = proxy_ctx_add_defaults_if_empty(g_proxy_ctx);
+ if (rc < 0)
+ return 1;
+
+ LOGP(DPROXY, LOGL_NOTICE, "osmo-trx-proxy started\n");
+
+ while (!g_quit)
+ osmo_select_main(0);
+
+ LOGP(DPROXY, LOGL_NOTICE, "Shutting down...\n");
+ proxy_ctx_free(g_proxy_ctx);
+ return 0;
+}
diff --git a/proxy/src/proxy.c b/proxy/src/proxy.c
new file mode 100644
index 0000000..b194bc9
--- /dev/null
+++ b/proxy/src/proxy.c
@@ -0,0 +1,101 @@
+/*! \file src/proxy.c
+ * osmo-trx-proxy: top-level daemon context (struct proxy_ctx). */
+
+/*
+ * (C) 2026 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/linuxlist.h>
+
+#include <osmocom/trx/ep.h>
+
+#include <osmocom/proxy/proxy.h>
+#include <osmocom/proxy/trx.h>
+#include <osmocom/proxy/logging.h>
+
+struct proxy_ctx *g_proxy_ctx = NULL;
+
+struct proxy_ctx *proxy_ctx_alloc(void *talloc_ctx)
+{
+ struct proxy_ctx *proxy;
+
+ proxy = talloc_zero(talloc_ctx, struct proxy_ctx);
+ if (proxy == NULL)
+ return NULL;
+
+ proxy->bind_addr = talloc_strdup(proxy, PROXY_DEFAULT_BIND_ADDR);
+ INIT_LLIST_HEAD(&proxy->trx_list);
+
+ return proxy;
+}
+
+void proxy_ctx_free(struct proxy_ctx *proxy)
+{
+ struct proxy_trx *trx, *trx2;
+
+ if (!proxy)
+ return;
+
+ llist_for_each_entry_safe(trx, trx2, &proxy->trx_list, list) {
+ proxy_trx_close(trx);
+ proxy_trx_free(trx);
+ }
+
+ talloc_free(proxy);
+}
+
+/*! Populate the config with the default BTS/MS endpoints (base ports 5700
+ * and 6700) and open them, iff the config file did not declare any
+ * endpoint explicitly.
+ * \returns 0 on success; negative on error (see proxy_trx_open()) */
+int proxy_ctx_add_defaults_if_empty(struct proxy_ctx *proxy)
+{
+ struct proxy_trx *trx;
+ int rc;
+
+ if (!llist_empty(&proxy->trx_list))
+ return 0;
+
+ LOGP(DPROXY, LOGL_NOTICE,
+ "No endpoints configured, creating defaults ('%s' and '%s')\n",
+ PROXY_DEFAULT_BTS_NAME, PROXY_DEFAULT_MS_NAME);
+
+ trx = proxy_trx_alloc(proxy, PROXY_DEFAULT_BTS_NAME);
+ osmo_trx_ep_set_raddr(trx->ep, PROXY_DEFAULT_REMOTE_ADDR);
+ osmo_trx_ep_set_base_port(trx->ep, PROXY_DEFAULT_BTS_PORT);
+ rc = proxy_trx_open(trx);
+ if (rc != 0)
+ return rc;
+
+ trx = proxy_trx_alloc(proxy, PROXY_DEFAULT_MS_NAME);
+ osmo_trx_ep_set_raddr(trx->ep, PROXY_DEFAULT_REMOTE_ADDR);
+ osmo_trx_ep_set_base_port(trx->ep, PROXY_DEFAULT_MS_PORT);
+ /* MS (trxcon) side has no use for TRX clock indications */
+ osmo_trx_ep_set_clock_socket(trx->ep, false);
+ rc = proxy_trx_open(trx);
+ if (rc != 0)
+ return rc;
+
+ return 0;
+}
diff --git a/proxy/src/trx.c b/proxy/src/trx.c
new file mode 100644
index 0000000..6eab496
--- /dev/null
+++ b/proxy/src/trx.c
@@ -0,0 +1,108 @@
+/*! \file src/trx.c
+ * osmo-trx-proxy: virtual transceiver management. */
+
+/*
+ * (C) 2026 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/linuxlist.h>
+
+#include <osmocom/trx/ep.h>
+
+#include <osmocom/proxy/proxy.h>
+#include <osmocom/proxy/trx.h>
+#include <osmocom/proxy/logging.h>
+
+struct proxy_trx *proxy_trx_alloc(struct proxy_ctx *proxy, const char *name)
+{
+ struct proxy_trx *trx;
+
+ trx = talloc_zero(proxy, struct proxy_trx);
+ if (trx == NULL)
+ return NULL;
+
+ trx->name = talloc_strdup(trx, name);
+ trx->ep = osmo_trx_ep_alloc(trx, 1);
+ if (trx->ep == NULL) {
+ talloc_free(trx);
+ return NULL;
+ }
+
+ osmo_trx_ep_set_priv(trx->ep, trx);
+ osmo_trx_ep_set_name(trx->ep, "%s", name);
+ osmo_trx_ep_set_log_cat(trx->ep, DPROXY);
+ osmo_trx_ep_set_mode(trx->ep, OSMO_TRX_EP_MODE_TRX);
+ osmo_trx_ep_set_laddr(trx->ep, proxy->bind_addr);
+ osmo_trx_ep_set_clock_socket(trx->ep, true);
+ osmo_trx_ep_set_ctrl_promisc(trx->ep, true);
+
+ llist_add_tail(&trx->list, &proxy->trx_list);
+
+ return trx;
+}
+
+struct proxy_trx *proxy_trx_find(struct proxy_ctx *proxy, const char *name)
+{
+ struct proxy_trx *trx;
+
+ llist_for_each_entry(trx, &proxy->trx_list, list) {
+ if (strcmp(trx->name, name) == 0)
+ return trx;
+ }
+
+ return NULL;
+}
+
+void proxy_trx_close(struct proxy_trx *trx)
+{
+ if (!trx)
+ return;
+ osmo_trx_ep_close(trx->ep);
+}
+
+void proxy_trx_free(struct proxy_trx *trx)
+{
+ if (!trx)
+ return;
+ osmo_trx_ep_free(trx->ep);
+ llist_del(&trx->list);
+ talloc_free(trx);
+}
+
+int proxy_trx_open(struct proxy_trx *trx)
+{
+ int rc;
+
+ rc = osmo_trx_ep_open(trx->ep);
+ if (rc < 0 && rc != -EALREADY) {
+ LOGP_TRX(trx, DPROXY, LOGL_ERROR,
+ "Failed to open TRX endpoint %s:%u\n",
+ osmo_trx_ep_get_raddr(trx->ep),
+ osmo_trx_ep_get_base_port(trx->ep));
+ return rc;
+ }
+
+ return 0;
+}
diff --git a/proxy/src/vty.c b/proxy/src/vty.c
new file mode 100644
index 0000000..086fe50
--- /dev/null
+++ b/proxy/src/vty.c
@@ -0,0 +1,337 @@
+/*! \file src/vty.c
+ * osmo-trx-proxy: VTY configuration interface. */
+
+/*
+ * (C) 2026 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/misc.h>
+#include <osmocom/vty/logging.h>
+#include <osmocom/vty/cpu_sched_vty.h>
+
+#include <osmocom/trx/ep.h>
+
+#include <osmocom/proxy/proxy.h>
+#include <osmocom/proxy/vty.h>
+#include <osmocom/proxy/trx.h>
+
+extern void *g_talloc_ctx;
+
+enum proxy_vty_node {
+ PROXY_NODE = _LAST_OSMOVTY_NODE + 1,
+ EP_NODE,
+};
+
+static struct cmd_node proxy_node = {
+ PROXY_NODE,
+ "%s(config-proxy)# ",
+ 1,
+};
+
+static struct cmd_node ep_node = {
+ EP_NODE,
+ "%s(config-proxy-ep)# ",
+ 1,
+};
+
+static int dummy_config_write(struct vty *vty)
+{
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_proxy,
+ cfg_proxy_cmd,
+ "proxy",
+ "Configure osmo-trx-proxy\n")
+{
+ vty->node = PROXY_NODE;
+ vty->index = g_proxy_ctx;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_proxy_bind_addr,
+ cfg_proxy_bind_addr_cmd,
+ "bind-addr " VTY_IPV46_CMD,
+ "Set the default local bind address for endpoints without one of their own\n"
+ "IPv4 Address\n"
+ "IPv6 Address\n")
+{
+ struct proxy_ctx *proxy = vty->index;
+
+ osmo_talloc_replace_string(proxy, &proxy->bind_addr, argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_proxy_ep,
+ cfg_proxy_ep_cmd,
+ "ep NAME",
+ "Configure a virtual TRX endpoint\n"
+ "Endpoint name (used for logging)\n")
+{
+ struct proxy_ctx *proxy = vty->index;
+ struct proxy_trx *trx = proxy_trx_find(proxy, argv[0]);
+
+ if (!trx)
+ trx = proxy_trx_alloc(proxy, argv[0]);
+
+ vty->node = EP_NODE;
+ vty->index = trx;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_proxy_ep,
+ cfg_no_proxy_ep_cmd,
+ "no ep NAME",
+ NO_STR "Remove a virtual TRX endpoint, closing its sockets if currently open\n"
+ "Endpoint name\n")
+{
+ struct proxy_ctx *proxy = vty->index;
+ struct proxy_trx *trx = proxy_trx_find(proxy, argv[0]);
+
+ if (!trx) {
+ vty_out(vty, "%% Endpoint '%s' does not exist%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ proxy_trx_free(trx);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ep_remote_addr,
+ cfg_ep_remote_addr_cmd,
+ "remote-addr " VTY_IPV46_CMD,
+ "Set the remote (L1 peer) IP address\n"
+ "IPv4 Address\n"
+ "IPv6 Address\n")
+{
+ struct proxy_trx *trx = vty->index;
+
+ osmo_trx_ep_set_raddr(trx->ep, argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ep_bind_addr,
+ cfg_ep_bind_addr_cmd,
+ "bind-addr " VTY_IPV46_CMD,
+ "Set the local bind address for this endpoint, overriding the 'proxy' default\n"
+ "IPv4 Address\n"
+ "IPv6 Address\n")
+{
+ struct proxy_trx *trx = vty->index;
+
+ osmo_trx_ep_set_laddr(trx->ep, argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ep_base_port,
+ cfg_ep_base_port_cmd,
+ "base-port <1-65535>",
+ "Set the TRX base port\n"
+ "Base port (clock: +0, per channel N: ctrl +2N+1, data +2N+2)\n")
+{
+ struct proxy_trx *trx = vty->index;
+
+ osmo_trx_ep_set_base_port(trx->ep, atoi(argv[0]));
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ep_num_chans,
+ cfg_ep_num_chans_cmd,
+ "num-chans <1-16>",
+ "Set the number of channels (1 + number of child transceivers)\n"
+ "Number of channels\n")
+{
+ struct proxy_trx *trx = vty->index;
+ int num_chans = atoi(argv[0]);
+ int rc;
+
+ rc = osmo_trx_ep_set_num_chans(trx->ep, num_chans);
+ if (rc) {
+ vty_out(vty, "%% osmo_trx_ep_set_num_chans(%d) failed: rc=%d%s",
+ num_chans, rc, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ep_clock_socket,
+ cfg_ep_clock_socket_cmd,
+ "clock-socket",
+ "Enable the clock socket for this endpoint (default)\n")
+{
+ struct proxy_trx *trx = vty->index;
+
+ osmo_trx_ep_set_clock_socket(trx->ep, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ep_no_clock_socket,
+ cfg_ep_no_clock_socket_cmd,
+ "no clock-socket",
+ NO_STR "Disable the clock socket for this endpoint\n")
+{
+ struct proxy_trx *trx = vty->index;
+
+ osmo_trx_ep_set_clock_socket(trx->ep, false);
+
+ return CMD_SUCCESS;
+}
+
+static int config_write_proxy(struct vty *vty)
+{
+ struct proxy_trx *trx;
+
+ vty_out(vty, "proxy%s", VTY_NEWLINE);
+ if (g_proxy_ctx->bind_addr)
+ vty_out(vty, " bind-addr %s%s", g_proxy_ctx->bind_addr, VTY_NEWLINE);
+
+ llist_for_each_entry(trx, &g_proxy_ctx->trx_list, list) {
+ const char *raddr = osmo_trx_ep_get_raddr(trx->ep);
+ const char *laddr = osmo_trx_ep_get_laddr(trx->ep);
+
+ vty_out(vty, " ep %s%s", trx->name, VTY_NEWLINE);
+ if (raddr)
+ vty_out(vty, " remote-addr %s%s", raddr, VTY_NEWLINE);
+ if (laddr && strcmp(laddr, g_proxy_ctx->bind_addr) != 0)
+ vty_out(vty, " bind-addr %s%s", laddr, VTY_NEWLINE);
+ vty_out(vty, " base-port %u%s", osmo_trx_ep_get_base_port(trx->ep), VTY_NEWLINE);
+ vty_out(vty, " num-chans %u%s", osmo_trx_ep_get_num_chans(trx->ep), VTY_NEWLINE);
+
+ if (!osmo_trx_ep_get_clock_socket(trx->ep))
+ vty_out(vty, " no clock-socket%s", VTY_NEWLINE);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_proxy,
+ show_proxy_cmd,
+ "show proxy",
+ SHOW_STR "Display configured virtual TRX endpoints\n")
+{
+ struct proxy_trx *trx;
+
+ llist_for_each_entry(trx, &g_proxy_ctx->trx_list, list) {
+ vty_out(vty, "Endpoint '%s': %s:%u, %u channel(s), %s%s",
+ trx->name,
+ osmo_trx_ep_get_raddr(trx->ep),
+ osmo_trx_ep_get_base_port(trx->ep),
+ osmo_trx_ep_get_num_chans(trx->ep),
+ osmo_trx_ep_is_open(trx->ep) ? "open" : "closed",
+ VTY_NEWLINE);
+ }
+
+ return CMD_SUCCESS;
+}
+
+/*! Called when leaving EP_NODE (interactively via 'exit'/'end', or on dedent
+ * while reading a config file): validate the endpoint config and open it,
+ * unless it is already open. Returns 0 on success, non-zero if the node
+ * should not be left (so the user/config can fix the problem). */
+static int proxy_vty_ep_node_exit(struct vty *vty, struct proxy_trx *trx)
+{
+ int rc = proxy_trx_open(trx);
+
+ if (rc < 0 && rc != -EALREADY) {
+ vty_out(vty, "%% Endpoint '%s': failed to open%s",
+ trx->name, VTY_NEWLINE);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int proxy_vty_go_parent(struct vty *vty)
+{
+ switch (vty->node) {
+ case PROXY_NODE:
+ vty->node = CONFIG_NODE;
+ break;
+ case EP_NODE:
+ if (proxy_vty_ep_node_exit(vty, vty->index) != 0)
+ break;
+ vty->node = PROXY_NODE;
+ vty->index = g_proxy_ctx;
+ break;
+ default:
+ vty->node = CONFIG_NODE;
+ }
+
+ return vty->node;
+}
+
+static struct vty_app_info g_vty_info = {
+ .name = "OsmoTRXProxy",
+ .copyright =
+ "Copyright (C) 2026 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>\r\n"
+ "Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>\r\n"
+ "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 = proxy_vty_go_parent,
+};
+
+int proxy_vty_init(void)
+{
+ g_vty_info.tall_ctx = g_talloc_ctx;
+ vty_init(&g_vty_info);
+
+ logging_vty_add_cmds();
+ osmo_talloc_vty_add_cmds();
+ osmo_cpu_sched_vty_init(g_talloc_ctx);
+
+ install_element_ve(&show_proxy_cmd);
+
+ install_element(CONFIG_NODE, &cfg_proxy_cmd);
+ install_node(&proxy_node, config_write_proxy);
+ install_element(PROXY_NODE, &cfg_proxy_bind_addr_cmd);
+ install_element(PROXY_NODE, &cfg_proxy_ep_cmd);
+ install_element(PROXY_NODE, &cfg_no_proxy_ep_cmd);
+
+ install_node(&ep_node, dummy_config_write);
+ install_element(EP_NODE, &cfg_ep_remote_addr_cmd);
+ install_element(EP_NODE, &cfg_ep_bind_addr_cmd);
+ install_element(EP_NODE, &cfg_ep_base_port_cmd);
+ install_element(EP_NODE, &cfg_ep_num_chans_cmd);
+ install_element(EP_NODE, &cfg_ep_clock_socket_cmd);
+ install_element(EP_NODE, &cfg_ep_no_clock_socket_cmd);
+
+ return 0;
+}

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

Gerrit-MessageType: merged
Gerrit-Project: osmo-trx
Gerrit-Branch: master
Gerrit-Change-Id: Id2b12e5e7d053eea53338fbabc48131b9c46c82a
Gerrit-Change-Number: 43603
Gerrit-PatchSet: 6
Gerrit-Owner: fixeria <vyanitskiy@sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <vyanitskiy@sysmocom.de>
Gerrit-Reviewer: laforge <laforge@osmocom.org>
Gerrit-Reviewer: pespin <pespin@sysmocom.de>