diff --git a/doc/examples/osmo-upf/osmo-upf-mockup.cfg b/doc/examples/osmo-upf/osmo-upf-mockup.cfg index e943a57..9608ef0 100644 --- a/doc/examples/osmo-upf/osmo-upf-mockup.cfg +++ b/doc/examples/osmo-upf/osmo-upf-mockup.cfg @@ -14,6 +14,6 @@ timer pfcp x24 5000 pfcp local-addr 127.0.0.1 gtp - mockup + dev mockup test_apn nft mockup diff --git a/include/osmocom/upf/upf.h b/include/osmocom/upf/upf.h index 1f8355d..b2f80d3 100644 --- a/include/osmocom/upf/upf.h +++ b/include/osmocom/upf/upf.h @@ -29,6 +29,8 @@ #include #include +#include + 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; diff --git a/include/osmocom/upf/upf_gtp.h b/include/osmocom/upf/upf_gtp.h index 1e0df5e..74d5ab1 100644 --- a/include/osmocom/upf/upf_gtp.h +++ b/include/osmocom/upf/upf_gtp.h @@ -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 { @@ -72,7 +81,7 @@ int upf_gtp_tun_desc_cmp(const struct upf_gtp_tun_desc *a, const struct upf_gtp_ 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(); diff --git a/src/osmo-upf/up_gtp_action.c b/src/osmo-upf/up_gtp_action.c index 9b8d528..da0c00c 100644 --- a/src/osmo-upf/up_gtp_action.c +++ b/src/osmo-upf/up_gtp_action.c @@ -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? */ diff --git a/src/osmo-upf/upf.c b/src/osmo-upf/upf.c index db699c7..d3a9880 100644 --- a/src/osmo-upf/upf.c +++ b/src/osmo-upf/upf.c @@ -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; diff --git a/src/osmo-upf/upf_gtp.c b/src/osmo-upf/upf_gtp.c index d75e8d7..d5a6447 100644 --- a/src/osmo-upf/upf_gtp.c +++ b/src/osmo-upf/upf_gtp.c @@ -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,18 @@ 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"); @@ -212,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) @@ -228,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); @@ -363,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; @@ -429,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; @@ -453,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; } diff --git a/src/osmo-upf/upf_vty.c b/src/osmo-upf/upf_vty.c index 5227dba..af7adf7 100644 --- a/src/osmo-upf/upf_vty.c +++ b/src/osmo-upf/upf_vty.c @@ -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, >p_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, >p_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, >p_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); diff --git a/tests/upf.vty b/tests/upf.vty index d1cebe5..b3b64ab 100644 --- a/tests/upf.vty +++ b/tests/upf.vty @@ -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'