diff --git a/lib/libbhyve/Makefile b/lib/libbhyve/Makefile --- a/lib/libbhyve/Makefile +++ b/lib/libbhyve/Makefile @@ -3,6 +3,7 @@ PACKAGE=lib${LIB} LIB= bhyve SRCS= config.c \ + net_backends.c \ pci.c INCS= bhyve/config.h \ bhyve/net_backends.h \ diff --git a/lib/libbhyve/net_backends.c b/lib/libbhyve/net_backends.c new file mode 100644 --- /dev/null +++ b/lib/libbhyve/net_backends.c @@ -0,0 +1,307 @@ +/* + * + * Copyright (c) 2019 Vincenzo Maffione + * Copyright (c) 2023 Mark Johnston + * Copyright (c) 2025 Bojan Novković + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +#include +#include +#include +#define NETMAP_WITH_LIBS +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "bhyve/config.h" +#include "bhyve/net_backends.h" +#include "bhyve/pci.h" +#include "internal.h" + +struct netbe_info { + const char *prefix; + int (*init_fds)(nvlist_t *); + bool (*validate_hotplug_request)(nvlist_t *); +}; + +static int +slirp_init_fds(nvlist_t *nvl) +{ + int error; + int sockpair_fds[2]; + + error = socketpair(PF_LOCAL, SOCK_SEQPACKET | SOCK_NONBLOCK, 0, + sockpair_fds); + if (error != 0) { + nvlist_add_stringf(nvl, "error", "Unable to create pipe: %s", + strerror(errno)); + return (error); + } + + nvlist_move_descriptor_array(nvl, "sockpair", sockpair_fds, + sizeof(sockpair_fds)); + + return (0); +} + +static bool +slirp_validate_hotplug_request(nvlist_t *nvl) +{ + if (!nvlist_exists_descriptor_array(nvl, "sockpair")) { + nvlist_add_string(nvl, "error", + "missing socket descriptor pair"); + return (false); + } + if (!nvlist_exists_descriptor_array(nvl, "pipe")) { + nvlist_add_string(nvl, "error", "missing pipe descriptor pair"); + return (false); + } + + return (true); +} + +static int +tap_init_fds(nvlist_t *nvl) +{ + int fd; + char tbuf[80]; + const char *devname; + + if (!nvlist_exists_string(nvl, "backend")) { + nvlist_add_string(nvl, "error", "missing backend parameter"); + return (-1); + } + + devname = nvlist_get_string(nvl, "backend"); + strcpy(tbuf, "/dev/"); + strlcat(tbuf, devname, sizeof(tbuf)); + fd = open(tbuf, O_RDWR); + if (fd == -1) { + nvlist_add_stringf(nvl, "error", + "open of tap device %s failed: %s", tbuf, strerror(errno)); + return (-1); + } + nvlist_move_descriptor(nvl, "devfd", fd); + + return (0); +} + +static bool +tap_validate_hotplug_request(nvlist_t *nvl) +{ + if (!nvlist_exists_descriptor(nvl, "devfd")) { + nvlist_add_string(nvl, "error", + "missing tap device file descriptor"); + return (false); + } + return (true); +} + +static int +ng_init_fds(nvlist_t *nvl) +{ + int csp, dsp; + const char *nodename; + + if (!nvlist_exists_string(nvl, "socket")) { + nvlist_add_string(nvl, "error", "missing socket path"); + return (-1); + } + nodename = nvlist_get_string(nvl, "socket"); + if (NgMkSockNode(nodename, &csp, &dsp) < 0) { + nvlist_add_stringf(nvl, "error", "can't get Netgraph sockets"); + return (-1); + } + + nvlist_move_descriptor(nvl, "csp", csp); + nvlist_move_descriptor(nvl, "dsp", dsp); + + return (0); +} + +static bool +ng_validate_hotplug_request(nvlist_t *nvl) +{ + if (!nvlist_exists_descriptor(nvl, "csp")) { + nvlist_add_string(nvl, "error", + "missing control socket descriptor"); + return (false); + } + if (!nvlist_exists_descriptor(nvl, "dsp")) { + nvlist_add_string(nvl, "error", + "missing data socket descriptor"); + return (false); + } + + return (true); +} + +static int +netmap_init_fds(nvlist_t *nvl) +{ + struct nm_desc *nmd; + const char *devname; + + if (!nvlist_exists_string(nvl, "backend")) { + nvlist_add_string(nvl, "error", "missing backend parameter"); + return (-1); + } + devname = nvlist_get_string(nvl, "backend"); + nmd = nm_open(devname, NULL, NETMAP_NO_TX_POLL, NULL); + if (nmd == NULL) { + nvlist_add_stringf(nvl, "error", + "Unable to nm_open(): interface '%s', errno (%s)", devname, + strerror(errno)); + return (-1); + } + nvlist_move_binary(nvl, "nm_desc", nmd, sizeof(*nmd)); + + return (0); +} + +static bool +netmap_validate_hotplug_request(nvlist_t *nvl) +{ + size_t size; + struct nm_desc *nmd; + + if (!nvlist_exists_binary(nvl, "nm_desc")) { + nvlist_add_string(nvl, "error", + "missing netmap descriptor structure"); + return (false); + } + + (void)nvlist_get_binary(nvl, "nm_desc", &size); + if (size != sizeof(*nmd)) { + nvlist_add_string(nvl, "error", + "mismatched netmap descriptor structure size"); + return (false); + } + + return (true); +} + +static struct netbe_info netbe_tap = { + .prefix = "tap", + .init_fds = tap_init_fds, + .validate_hotplug_request = tap_validate_hotplug_request +}; + +static struct netbe_info netbe_ngd = { + .prefix = "ngd", + .init_fds = tap_init_fds, + .validate_hotplug_request = tap_validate_hotplug_request +}; + +static struct netbe_info netbe_netmap = { + .prefix = "netmap", + .init_fds = netmap_init_fds, + .validate_hotplug_request = netmap_validate_hotplug_request +}; + +static struct netbe_info netbe_netgraph = { + .prefix = "netgraph", + .init_fds = ng_init_fds, + .validate_hotplug_request = ng_validate_hotplug_request +}; + +static struct netbe_info netbe_slirp = { + .prefix = "slirp", + .init_fds = slirp_init_fds, + .validate_hotplug_request = slirp_validate_hotplug_request +}; + +SET_DECLARE(net_backends_info, struct netbe_info); +DATA_SET(net_backends_info, netbe_netgraph); +DATA_SET(net_backends_info, netbe_slirp); +DATA_SET(net_backends_info, netbe_netmap); +DATA_SET(net_backends_info, netbe_tap); +DATA_SET(net_backends_info, netbe_ngd); + +static struct netbe_info * +find_netbe_info(const char *backend) +{ + struct netbe_info **nbe; + + SET_FOREACH(nbe, net_backends_info) { + if (strncmp(backend, (*nbe)->prefix, + strlen((*nbe)->prefix)) == 0) { + return (*nbe); + } + } + return (NULL); +} + +int +netbe_init_fds(nvlist_t *nvl) +{ + const char *backend; + struct netbe_info *nbi; + + if (!nvlist_exists_string(nvl, "backend")) { + nvlist_add_string(nvl, "error", "missing backend argument"); + return (-1); + } + backend = nvlist_get_string(nvl, "backend"); + nbi = find_netbe_info(backend); + if (nbi == NULL) { + nvlist_add_stringf(nvl, "error", "unknown backend '%s'", + backend); + return (-1); + } + + if (nbi->init_fds == NULL) + return (0); + + return (nbi->init_fds(nvl)); +} + +int +netbe_legacy_config(nvlist_t *nvl, const char *opts) +{ + char *backend, *cp; + + if (opts == NULL) + return (0); + + cp = strchr(opts, ','); + if (cp == NULL) { + set_config_value_node(nvl, "backend", opts); + return (0); + } + backend = strndup(opts, cp - opts); + set_config_value_node(nvl, "backend", backend); + free(backend); + return (pci_parse_legacy_config(nvl, cp + 1)); +} + +bool +netbe_validate_hotplug_request(nvlist_t *nvl) +{ + const char *backend; + struct netbe_info *nbi; + + if (!nvlist_exists_string(nvl, "backend")) { + nvlist_add_string(nvl, "error", "missing backend argument"); + return (-1); + } + backend = nvlist_get_string(nvl, "backend"); + nbi = find_netbe_info(backend); + + if (nbi == NULL || nbi->validate_hotplug_request == NULL) + return (0); + + return (nbi->validate_hotplug_request(nvl)); +} diff --git a/usr.sbin/bhyve/net_backend_netgraph.c b/usr.sbin/bhyve/net_backend_netgraph.c --- a/usr.sbin/bhyve/net_backend_netgraph.c +++ b/usr.sbin/bhyve/net_backend_netgraph.c @@ -53,7 +53,7 @@ { struct tap_priv *p = NET_BE_PRIV(be); struct ngm_connect ngc; - const char *value, *nodename; + const char *value; int sbsz; int ctrl_sock; int flags; @@ -74,7 +74,7 @@ value = get_config_value_node(nvl, "path"); if (value == NULL) { - EPRINTLN("path must be provided"); + nvlist_add_string(nvl, "error", "path must be provided"); return (-1); } strncpy(ngc.path, value, NG_PATHSIZ - 1); @@ -86,22 +86,18 @@ value = get_config_value_node(nvl, "peerhook"); if (value == NULL) { - EPRINTLN("peer hook must be provided"); + nvlist_add_string(nvl, "error", "peer hook must be provided"); return (-1); } strncpy(ngc.peerhook, value, NG_HOOKSIZ - 1); - nodename = get_config_value_node(nvl, "socket"); - if (NgMkSockNode(nodename, - &ctrl_sock, &be->fd) < 0) { - EPRINTLN("can't get Netgraph sockets"); - return (-1); - } + ctrl_sock = nvlist_take_descriptor(nvl, "csp"); + be->fd = nvlist_take_descriptor(nvl, "dsp"); if (NgSendMsg(ctrl_sock, ".", NGM_GENERIC_COOKIE, NGM_CONNECT, &ngc, sizeof(ngc)) < 0) { - EPRINTLN("can't connect to node"); + nvlist_add_string(nvl, "error", "can't connect to node"); close(ctrl_sock); goto error; } @@ -111,12 +107,12 @@ flags = fcntl(be->fd, F_GETFL); if (flags < 0) { - EPRINTLN("can't get socket flags"); + nvlist_add_string(nvl, "error", "can't get socket flags"); goto error; } if (fcntl(be->fd, F_SETFL, flags | O_NONBLOCK) < 0) { - EPRINTLN("can't set O_NONBLOCK flag"); + nvlist_add_string(nvl, "error", "can't set O_NONBLOCK flag"); goto error; } @@ -128,7 +124,8 @@ msbsz = sizeof(maxsbsz); if (sysctlbyname("kern.ipc.maxsockbuf", &maxsbsz, &msbsz, NULL, 0) < 0) { - EPRINTLN("can't get 'kern.ipc.maxsockbuf' value"); + nvlist_add_string(nvl, "error", + "can't get 'kern.ipc.maxsockbuf' value"); goto error; } @@ -142,13 +139,13 @@ if (setsockopt(be->fd, SOL_SOCKET, SO_SNDBUF, &sbsz, sizeof(sbsz)) < 0) { - EPRINTLN("can't set TX buffer size"); + nvlist_add_string(nvl, "error", "can't set TX buffer size"); goto error; } if (setsockopt(be->fd, SOL_SOCKET, SO_RCVBUF, &sbsz, sizeof(sbsz)) < 0) { - EPRINTLN("can't set RX buffer size"); + nvlist_add_string(nvl, "error", "can't set RX buffer size"); goto error; } @@ -163,7 +160,7 @@ p->mevp = mevent_add_disabled(be->fd, EVF_READ, cb, param); if (p->mevp == NULL) { - EPRINTLN("Could not register event"); + nvlist_add_string(nvl, "error", "Could not register event"); goto error; } diff --git a/usr.sbin/bhyve/net_backend_netmap.c b/usr.sbin/bhyve/net_backend_netmap.c --- a/usr.sbin/bhyve/net_backend_netmap.c +++ b/usr.sbin/bhyve/net_backend_netmap.c @@ -123,21 +123,15 @@ } static int -netmap_init(struct net_backend *be, const char *devname, - nvlist_t *nvl __unused, net_be_rxeof_t cb, void *param) +netmap_init(struct net_backend *be, const char *devname, nvlist_t *nvl, + net_be_rxeof_t cb, void *param) { struct netmap_priv *priv = NET_BE_PRIV(be); + size_t size; strlcpy(priv->ifname, devname, sizeof(priv->ifname)); priv->ifname[sizeof(priv->ifname) - 1] = '\0'; - - priv->nmd = nm_open(priv->ifname, NULL, NETMAP_NO_TX_POLL, NULL); - if (priv->nmd == NULL) { - EPRINTLN("Unable to nm_open(): interface '%s', errno (%s)", - devname, strerror(errno)); - return (-1); - } - + priv->nmd = nvlist_take_binary(nvl, "nm_desc", &size); priv->memid = priv->nmd->req.nr_arg2; priv->tx = NETMAP_TXRING(priv->nmd->nifp, 0); priv->rx = NETMAP_RXRING(priv->nmd->nifp, 0); @@ -147,7 +141,7 @@ priv->mevp = mevent_add_disabled(be->fd, EVF_READ, cb, param); if (priv->mevp == NULL) { - EPRINTLN("Could not register event"); + nvlist_add_string(nvl, "error", "Could not register event"); return (-1); } diff --git a/usr.sbin/bhyve/net_backend_slirp.c b/usr.sbin/bhyve/net_backend_slirp.c --- a/usr.sbin/bhyve/net_backend_slirp.c +++ b/usr.sbin/bhyve/net_backend_slirp.c @@ -88,12 +88,14 @@ pid_t child; const char **argv; char sockname[32]; - int error, s[2]; + int error, *s; + size_t mtu, nitems; const char *mtu_value; - size_t mtu; - if (socketpair(PF_LOCAL, SOCK_SEQPACKET | SOCK_NONBLOCK, 0, s) != 0) { - EPRINTLN("socketpair"); + s = nvlist_take_descriptor_array(nvl, "sockpair", &nitems); + if (nitems != 2) { + nvlist_add_string(nvl, "error", + "mismatched number of socket descriptors"); return (-1); } @@ -102,11 +104,13 @@ * one end is inherited by the child. */ if (posix_spawn_file_actions_init(&fa) != 0) { - EPRINTLN("posix_spawn_file_actions_init"); + nvlist_add_string(nvl, "error", + "posix_spawn_file_actions_init"); goto err; } if (posix_spawn_file_actions_addclose(&fa, s[0]) != 0) { - EPRINTLN("posix_spawn_file_actions_addclose"); + nvlist_add_string(nvl, "error", + "posix_spawn_file_actions_addclose"); posix_spawn_file_actions_destroy(&fa); goto err; } @@ -119,22 +123,23 @@ &fa, NULL, __DECONST(char **, argv), environ); posix_spawn_file_actions_destroy(&fa); if (error != 0) { - EPRINTLN("posix_spawn(bhyve-slirp-helper): %s", + nvlist_add_stringf(nvl, "error", "posix_spawn(bhyve-slirp-helper): %s", strerror(error)); goto err; } config = nvlist_clone(nvl); if (config == NULL) { - EPRINTLN("nvlist_clone"); + nvlist_add_string(nvl, "error", + "nvlist_clone"); goto err; } mtu_value = get_config_value_node(config, "mtu"); if (mtu_value != NULL) { if (net_parsemtu(mtu_value, &mtu)) { - EPRINTLN("Could not parse MTU"); - goto err; + nvlist_add_string(nvl, "error", "Could not parse MTU"); + goto err; } } else { mtu = DEFAULT_MTU; @@ -144,7 +149,7 @@ priv->mtu = mtu; priv->buf = malloc(mtu); if (priv->buf == NULL) { - EPRINTLN("Could not allocate buffer"); + nvlist_add_string(nvl, "error", "Could not allocate MTU buffer"); goto err; } @@ -152,14 +157,15 @@ error = nvlist_send(s[0], config); nvlist_destroy(config); if (error != 0) { - EPRINTLN("nvlist_send"); + nvlist_add_string(nvl, "error", + "nvlist_send"); goto err; } be->fd = s[0]; priv->mevp = mevent_add_disabled(be->fd, EVF_READ, cb, param); if (priv->mevp == NULL) { - EPRINTLN("Could not register event"); + nvlist_add_string(nvl, "error", "Could not register event"); goto err; } diff --git a/usr.sbin/bhyve/net_backends.c b/usr.sbin/bhyve/net_backends.c --- a/usr.sbin/bhyve/net_backends.c +++ b/usr.sbin/bhyve/net_backends.c @@ -85,11 +85,10 @@ } static int -tap_init(struct net_backend *be, const char *devname, - nvlist_t *nvl __unused, net_be_rxeof_t cb, void *param) +tap_init(struct net_backend *be, const char *devname __unused, nvlist_t *nvl, + net_be_rxeof_t cb, void *param) { struct tap_priv *priv = NET_BE_PRIV(be); - char tbuf[80]; int opt = 1, up = IFF_UP; #ifndef WITHOUT_CAPSICUM @@ -97,31 +96,25 @@ #endif if (cb == NULL) { - EPRINTLN("TAP backend requires non-NULL callback"); + nvlist_add_string(nvl, "error", + "TAP backend requires non-NULL callback"); return (-1); } - strcpy(tbuf, "/dev/"); - strlcat(tbuf, devname, sizeof(tbuf)); - - be->fd = open(tbuf, O_RDWR); - if (be->fd == -1) { - EPRINTLN("open of tap device %s failed", tbuf); - goto error; - } + be->fd = nvlist_take_descriptor(nvl, "devfd"); /* * Set non-blocking and register for read * notifications with the event loop */ if (ioctl(be->fd, FIONBIO, &opt) < 0) { - EPRINTLN("tap device O_NONBLOCK failed"); + nvlist_add_string(nvl, "error", "tap device O_NONBLOCK failed"); goto error; } if (strncmp("ngd", be->prefix, 3) && ioctl(be->fd, VMIO_SIOCSIFFLAGS, up)) { - EPRINTLN("tap device link up failed"); + nvlist_add_string(nvl, "error", "tap device link up failed"); goto error; } @@ -136,7 +129,7 @@ priv->mevp = mevent_add_disabled(be->fd, EVF_READ, cb, param); if (priv->mevp == NULL) { - EPRINTLN("Could not register event"); + nvlist_add_string(nvl, "error", "Could not register event"); goto error; } @@ -293,25 +286,6 @@ DATA_SET(net_backend_set, vmnet_backend); DATA_SET(net_backend_set, ngd_backend); -int -netbe_legacy_config(nvlist_t *nvl, const char *opts) -{ - char *backend, *cp; - - if (opts == NULL) - return (0); - - cp = strchr(opts, ','); - if (cp == NULL) { - set_config_value_node(nvl, "backend", opts); - return (0); - } - backend = strndup(opts, cp - opts); - set_config_value_node(nvl, "backend", backend); - free(backend); - return (pci_parse_legacy_config(nvl, cp + 1)); -} - /* * Initialize a backend and attach to the frontend. * This is called during frontend initialization. @@ -335,6 +309,7 @@ value = get_config_value_node(nvl, "backend"); if (value == NULL) { + nvlist_add_string(nvl, "error", "missing backend argument"); return (-1); } devname = strdup(value); @@ -367,6 +342,7 @@ *ret = NULL; if (tbe == NULL) { + nvlist_add_stringf(nvl, "error", "unknown backend '%s'", type); free(devname); return (EINVAL); }