2 Commits

Author SHA1 Message Date
Neels Hofmeyr
3a43e59060 GTP mockup: list active GTP endecaps actions
In GTP mockup mode, keep a GTP device (struct upf_gtp_dev) so that
the GTP actions activated in mockup mode are listed on VTY.

Drop the global GTP mockup mode, and instead allow single GTP devices to
be mockup devices.

The GTP mockup was first introduced for VTY tests during 'make check'.
So far, in mockup mode, all GTP tunnel actions were simply cut short,
and no tunnels were ever listed as active.

TTCN3 tests do query osmo-upf to list currently active tunnels, so the
GTP mockup fails these tests. To allow running TTCN3 tests without
cap_net_admin privileges, always list (fake) active GTP tunnels.

Useful in a lab environment: send PFCP commands to osmo-upf and get a
listing of GTP tunnels that would have been active.

So far, osmo-upf always uses only the first GTP device configured, but
if we add support for multiple GTP devices, this patch allows putting
only single devices in mockup mode.

Related: SYS#5599
Change-Id: Ic09a5ccea24086eb04f46e6af669668e5fade752
2022-08-25 00:59:26 +02:00
Neels Janosch Hofmeyr
83e4e7a79e Allow running without a GTP dev
Allow running without opening a GTP dev for encapsulation/decapsulation.
Probe and open the mnl socket for talking to the GTP kernel module only
when actual GTP devices exist in the config.

A site that is only doing tunnel proxying via netfilter hence does not
require GTP support in the kernel.

Change-Id: Ibb79b3ce1906136f77a895ff6f691d72a92c9fb9
2022-08-25 00:59:03 +02:00
9 changed files with 82 additions and 66 deletions

View File

@@ -14,6 +14,6 @@ timer pfcp x24 5000
pfcp
local-addr 127.0.0.1
gtp
mockup
dev mockup test_apn
nft
mockup

View File

@@ -29,6 +29,8 @@
#include <osmocom/core/select.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/upf/upf_gtp.h>
struct osmo_tdef;
struct ctrl_handle;
@@ -47,9 +49,7 @@ struct pfcp_vty_cfg {
struct gtp_vty_cfg_dev {
struct llist_head entry;
/* If true, osmo-upf creates the GTP device on startup. If false, the GTP device was created by the user, and we
* just plug into it. */
bool create;
enum upf_gtp_dev_kind kind;
/* GTP device name as shown by 'ip link', e.g. 'apn0' */
char *dev_name;
@@ -76,9 +76,6 @@ struct g_upf {
/* Tunnel encaps decaps via GTP kernel module */
struct {
/* if true, don't actually send commands to the GTP kernel module, just return success. */
bool mockup;
/* GTP devices as in osmo-upf.cfg */
struct gtp_vty_cfg vty_cfg;

View File

@@ -36,12 +36,21 @@
#define PORT_GTP1_C 2123
#define PORT_GTP1_U 2152
enum upf_gtp_dev_kind {
/* "use an existing GTP device" -- the kernel GTP device was created by the user, and we just plug into it. */
UPF_GTP_DEV_USE = 0,
/* "create this GTP device" -- osmo-upf creates the kernel GTP device on startup, and deletes it on shutdown. */
UPF_GTP_DEV_CREATE,
/* "test osmo-upf without requiring cap_net_admin privileges" -- act as if using a GTP device, but no kernel
* device is actually opened, no commands will be sent out, all actions will simply noop and return success, and
* GTP tunnels appear as if active but do not actually exist. */
UPF_GTP_DEV_MOCKUP,
};
struct upf_gtp_dev {
struct llist_head entry;
/* If true, osmo-upf created this GTP device on startup and will destroy it on program exit. If false, the user
* has created the device and osmo-upf will not destroy it. */
bool created;
enum upf_gtp_dev_kind kind;
char *name;
struct {
@@ -69,10 +78,10 @@ struct upf_gtp_tun_desc {
int upf_gtp_tun_desc_cmp(const struct upf_gtp_tun_desc *a, const struct upf_gtp_tun_desc *b);
int upf_gtp_genl_open();
int upf_gtp_genl_ensure_open();
void upf_gtp_genl_close();
int upf_gtp_dev_open(const char *name, bool create_gtp_dev, const char *local_addr, bool listen_for_gtpv0,
int upf_gtp_dev_open(const char *name, enum upf_gtp_dev_kind kind, const char *local_addr, bool listen_for_gtpv0,
bool sgsn_mode);
struct upf_gtp_dev *upf_gtp_dev_find_by_name(const char *name);
struct upf_gtp_dev *upf_gtp_dev_first();

View File

@@ -331,9 +331,6 @@ int main(int argc, char **argv)
}
}
if (upf_gtp_genl_open())
return -1;
if (upf_gtp_devs_open())
return -1;

View File

@@ -83,12 +83,6 @@ static int up_gtp_action_enable_disable(struct up_gtp_action *a, bool enable)
switch (a->kind) {
case UP_GTP_U_ENDECAPS:
if (g_upf->gtp.mockup) {
LOG_UP_GTP_ACTION(a, LOGL_NOTICE, "gtp/mockup active, skipping GTP action %s\n",
enable ? "enable" : "disable");
return 0;
}
/* use the first available GTP device.
* TODO: select by interface name?
*/

View File

@@ -91,7 +91,7 @@ int upf_gtp_devs_open()
struct gtp_vty_cfg_dev *d;
llist_for_each_entry(d, &c->devs, entry) {
if (upf_gtp_dev_open(d->dev_name, d->create, d->local_addr, false, false))
if (upf_gtp_dev_open(d->dev_name, d->kind, d->local_addr, false, false))
return -1;
}
return 0;

View File

@@ -46,6 +46,10 @@ int upf_gtp_dev_to_str_buf(char *buf, size_t buflen, const struct upf_gtp_dev *d
uint16_t v0_port;
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
OSMO_STRBUF_PRINTF(sb, "%s", dev->name ? : "null");
if (dev->kind == UPF_GTP_DEV_MOCKUP) {
OSMO_STRBUF_PRINTF(sb, " [mockup]");
return sb.chars_needed;
}
if (dev->name && dev->ifidx)
OSMO_STRBUF_PRINTF(sb, " [%u]", dev->ifidx);
if (dev->sgsn_mode)
@@ -98,7 +102,7 @@ static int upf_gtp_dev_destruct(struct upf_gtp_dev *dev);
/* Allocate state for one GTP device, add to g_upf->gtp.devs and return the created device. If state for the device of
* that name already exists, do nothing and return NULL. */
static struct upf_gtp_dev *upf_gtp_dev_alloc(const char *name, const char *local_addr)
static struct upf_gtp_dev *upf_gtp_dev_alloc(const char *name, enum upf_gtp_dev_kind kind, const char *local_addr)
{
struct upf_gtp_dev *dev = upf_gtp_dev_find_by_name(name);
struct osmo_sockaddr_str addr_conv;
@@ -109,6 +113,7 @@ static struct upf_gtp_dev *upf_gtp_dev_alloc(const char *name, const char *local
}
dev = talloc(g_upf, struct upf_gtp_dev);
*dev = (struct upf_gtp_dev){
.kind = kind,
.name = talloc_strdup(dev, name),
.gtpv0.ofd.fd = -1,
.gtpv1.ofd.fd = -1,
@@ -158,7 +163,8 @@ static int dev_resolve_ifidx(struct upf_gtp_dev *dev)
return 0;
}
int upf_gtp_dev_open(const char *name, bool create_gtp_dev, const char *local_addr, bool listen_for_gtpv0, bool sgsn_mode)
int upf_gtp_dev_open(const char *name, enum upf_gtp_dev_kind kind, const char *local_addr, bool listen_for_gtpv0,
bool sgsn_mode)
{
const struct osmo_sockaddr any = {
.u.sin = {
@@ -172,17 +178,24 @@ int upf_gtp_dev_open(const char *name, bool create_gtp_dev, const char *local_ad
int rc;
struct upf_gtp_dev *dev;
if (g_upf->gtp.mockup) {
LOGP(DGTP, LOGL_NOTICE, "gtp/mockup active: not opening GTP device '%s'\n", name);
return 0;
}
dev = upf_gtp_dev_alloc(name, local_addr);
dev = upf_gtp_dev_alloc(name, kind, local_addr);
if (!dev)
return -EIO;
dev->sgsn_mode = sgsn_mode;
if (kind == UPF_GTP_DEV_MOCKUP) {
LOG_GTP_DEV(dev, LOGL_NOTICE,
"Created mockup GTP device: not opening kernel GTP device. FOR TESTING PURPOSES ONLY.\n");
return 0;
}
rc = upf_gtp_genl_ensure_open();
if (rc) {
LOG_GTP_DEV(dev, LOGL_ERROR, "Cannot set up GTP device, failed to open mnl_socket\n");
return rc;
}
if (listen_for_gtpv0) {
rc = osmo_sock_init_osa_ofd(&dev->gtpv0.ofd, SOCK_DGRAM, 0, &dev->gtpv0.local_addr, &any,
OSMO_SOCK_F_BIND);
@@ -206,7 +219,7 @@ int upf_gtp_dev_open(const char *name, bool create_gtp_dev, const char *local_ad
}
LOG_GTP_DEV(dev, LOGL_DEBUG, "GTPv1 bound\n");
if (create_gtp_dev) {
if (kind == UPF_GTP_DEV_CREATE) {
int gtp0_fd = listen_for_gtpv0 ? dev->gtpv0.ofd.fd : -1;
int gtp1_fd = dev->gtpv1.ofd.fd;
if (dev->sgsn_mode)
@@ -222,7 +235,6 @@ int upf_gtp_dev_open(const char *name, bool create_gtp_dev, const char *local_ad
}
LOG_GTP_DEV(dev, LOGL_NOTICE, "created GTP device\n");
dev->created = true;
}
rc = dev_resolve_ifidx(dev);
@@ -255,13 +267,8 @@ void upf_gtp_genl_close()
}
/* Open an MNL socket which allows to create and remove GTP devices (requires CAP_NET_ADMIN). */
int upf_gtp_genl_open()
int upf_gtp_genl_ensure_open()
{
if (g_upf->gtp.mockup) {
LOGP(DGTP, LOGL_NOTICE, "gtp/mockup active: not opening mnl_socket\n");
return 0;
}
/* Already open? */
if (g_upf->gtp.nl && g_upf->gtp.genl_id >= 0)
return 0;
@@ -362,6 +369,11 @@ int upf_gtp_tun_activate(struct upf_gtp_tun *tun)
if (tun->active)
return -EALREADY;
if (tun->dev->kind == UPF_GTP_DEV_MOCKUP) {
tun->active = true;
return 0;
}
t = upf_gtp_tun_to_gtp_tunnel(tun);
if (!t)
return -ENOTSUP;
@@ -428,6 +440,11 @@ static int upf_gtp_tun_deactivate(struct upf_gtp_tun *tun)
return -EINVAL;
}
if (tun->dev->kind == UPF_GTP_DEV_MOCKUP) {
tun->active = false;
return 0;
}
t = upf_gtp_tun_to_gtp_tunnel(tun);
if (!t)
return -EINVAL;
@@ -452,7 +469,7 @@ static int upf_gtp_dev_destruct(struct upf_gtp_dev *dev)
/* osmo_fd_close() is a noop if ofd.fd == -1 */
osmo_fd_close(&dev->gtpv0.ofd);
osmo_fd_close(&dev->gtpv1.ofd);
if (dev->created)
if (dev->kind == UPF_GTP_DEV_CREATE)
upf_gtp_dev_delete(dev);
return 0;
}

View File

@@ -97,17 +97,21 @@ static int config_write_gtp(struct vty *vty)
struct gtp_vty_cfg_dev *d;
vty_out(vty, "gtp%s", VTY_NEWLINE);
if (g_upf->gtp.mockup)
vty_out(vty, " mockup%s", VTY_NEWLINE);
llist_for_each_entry(d, &gtp_vty.devs, entry) {
if (d->create) {
switch (d->kind) {
case UPF_GTP_DEV_USE:
vty_out(vty, " dev use %s%s", d->dev_name, VTY_NEWLINE);
break;
case UPF_GTP_DEV_CREATE:
vty_out(vty, " dev create %s", d->dev_name);
if (d->local_addr)
vty_out(vty, " %s", d->local_addr);
vty_out(vty, "%s", VTY_NEWLINE);
} else {
vty_out(vty, " dev use %s%s", d->dev_name, VTY_NEWLINE);
break;
default:
case UPF_GTP_DEV_MOCKUP:
vty_out(vty, " dev mockup %s%s", d->dev_name, VTY_NEWLINE);
break;
}
}
return CMD_SUCCESS;
@@ -115,23 +119,6 @@ static int config_write_gtp(struct vty *vty)
#define DEV_STR "Configure the GTP device to use for encaps/decaps.\n"
DEFUN(cfg_gtp_mockup, cfg_gtp_mockup_cmd,
"mockup",
"don't actually send commands to the GTP kernel module, just return success\n")
{
g_upf->gtp.mockup = true;
return CMD_SUCCESS;
}
DEFUN(cfg_gtp_no_mockup, cfg_gtp_no_mockup_cmd,
"no mockup",
NO_STR
"operate GTP kernel module normally\n")
{
g_upf->gtp.mockup = false;
return CMD_SUCCESS;
}
DEFUN(cfg_gtp_dev_create, cfg_gtp_dev_create_cmd,
"dev create DEVNAME [LISTEN_ADDR]",
DEV_STR
@@ -143,7 +130,7 @@ DEFUN(cfg_gtp_dev_create, cfg_gtp_dev_create_cmd,
"IPv4 or IPv6 address to listen on, omit for ANY\n")
{
struct gtp_vty_cfg_dev *d = talloc_zero(g_upf, struct gtp_vty_cfg_dev);
d->create = true;
d->kind = UPF_GTP_DEV_CREATE;
d->dev_name = talloc_strdup(d, argv[0]);
if (argc > 1)
d->local_addr = talloc_strdup(d, argv[1]);
@@ -159,13 +146,27 @@ DEFUN(cfg_gtp_dev_use, cfg_gtp_dev_use_cmd,
"device name, e.g. 'apn0'\n")
{
struct gtp_vty_cfg_dev *d = talloc_zero(g_upf, struct gtp_vty_cfg_dev);
d->create = false;
d->kind = UPF_GTP_DEV_USE;
d->dev_name = talloc_strdup(d, argv[0]);
llist_add(&d->entry, &gtp_vty.devs);
vty_out(vty, "Added GTP device %s (use existing)%s", d->dev_name, VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN(cfg_gtp_dev_mockup, cfg_gtp_dev_mockup_cmd,
"dev mockup DEVNAME",
DEV_STR
"Add GTP device, as an internal mockup that does not run actual Linux kernel commands, just returns success\n"
"device name, e.g. 'apn0'\n")
{
struct gtp_vty_cfg_dev *d = talloc_zero(g_upf, struct gtp_vty_cfg_dev);
d->kind = UPF_GTP_DEV_MOCKUP;
d->dev_name = talloc_strdup(d, argv[0]);
llist_add(&d->entry, &gtp_vty.devs);
vty_out(vty, "Added GTP device %s (mockup)%s", d->dev_name, VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN(cfg_gtp_dev_del, cfg_gtp_dev_del_cmd,
"dev delete DEVNAME",
DEV_STR
@@ -355,10 +356,9 @@ void upf_vty_init()
install_node(&cfg_gtp_node, config_write_gtp);
install_element(CONFIG_NODE, &cfg_gtp_cmd);
install_element(GTP_NODE, &cfg_gtp_mockup_cmd);
install_element(GTP_NODE, &cfg_gtp_no_mockup_cmd);
install_element(GTP_NODE, &cfg_gtp_dev_create_cmd);
install_element(GTP_NODE, &cfg_gtp_dev_use_cmd);
install_element(GTP_NODE, &cfg_gtp_dev_mockup_cmd);
install_element(GTP_NODE, &cfg_gtp_dev_del_cmd);
install_node(&cfg_nft_node, config_write_nft);

View File

@@ -18,6 +18,7 @@ OsmoUPF(config-gtp)# list
...
dev create DEVNAME [LISTEN_ADDR]
dev use DEVNAME
dev mockup DEVNAME
dev delete DEVNAME
OsmoUPF(config-gtp)# dev?
@@ -25,6 +26,7 @@ OsmoUPF(config-gtp)# dev?
OsmoUPF(config-gtp)# dev ?
create Add GTP device, creating a new Linux kernel GTP device. Will listen on GTPv1 port 2152 and GTPv0 port 3386 on the specified interface, or on ANY if LISTEN_ADDR is omitted.
use Add GTP device, using an existing Linux kernel GTP device, e.g. created by 'gtp-link'
mockup Add GTP device, as an internal mockup that does not run actual Linux kernel commands, just returns success
delete Remove a GTP device from the configuration, and delete the Linux kernel GTP device if it was created here.
OsmoUPF(config-gtp)# dev create ?
DEVNAME device name, e.g. 'apn0'