Path: blob/main/usr.sbin/bhyve/net_backend_slirp.c
107110 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2023, 2025 Mark Johnston <[email protected]>4*5* This software was developed by the University of Cambridge Computer6* Laboratory (Department of Computer Science and Technology) under Innovate7* UK project 105694, "Digital Security by Design (DSbD) Technology Platform8* Prototype".9*10* Redistribution and use in source and binary forms, with or without11* modification, are permitted provided that the following conditions12* are met:13* 1. Redistributions of source code must retain the above copyright14* notice, this list of conditions and the following disclaimer.15* 2. Redistributions in binary form must reproduce the above copyright16* notice, this list of conditions and the following disclaimer in the17* documentation and/or other materials provided with the distribution.18*19* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND20* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE21* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE22* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE23* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL24* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS25* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)26* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT27* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY28* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF29* SUCH DAMAGE.30*/3132/*33* The slirp backend enables unprivileged userspace networking via libslirp,34* which must be installed on the host system via pkg or the ports tree.35* libslirp.so is dlopen()ed into a helper process with which this backend36* communicates.37*38* Packets received from the guest (i.e., transmitted by the frontend, such as a39* virtio NIC device model) are injected into the slirp backend via slirp_send(),40* which sends the packet to the helper process.41*42* Packets to be transmitted to the guest (i.e., inserted into the frontend's43* receive buffers) are buffered in a per-interface socket pair and read by the44* mevent loop. Sockets instantiated by libslirp are monitored by a thread45* which uses poll() and slirp_pollfds_poll() to drive libslirp events; this46* thread also handles timeout events from the libslirp context.47*/4849#include <sys/socket.h>50#include <sys/wait.h>5152#include <assert.h>53#include <errno.h>54#include <signal.h>55#include <spawn.h>56#include <stdio.h>57#include <stdlib.h>58#include <string.h>59#include <unistd.h>6061#include "config.h"62#include "debug.h"63#include "mevent.h"64#include "net_utils.h"65#include "net_backends.h"66#include "net_backends_priv.h"6768#define DEFAULT_MTU 20486970struct slirp_priv {71int s;72pid_t helper;73struct mevent *mevp;74size_t mtu;75uint8_t *buf;76};7778extern char **environ;7980static int81slirp_init(struct net_backend *be, const char *devname __unused,82nvlist_t *nvl, net_be_rxeof_t cb, void *param)83{84struct slirp_priv *priv = NET_BE_PRIV(be);85nvlist_t *config;86posix_spawn_file_actions_t fa;87pid_t child;88const char **argv;89char sockname[32];90int error, s[2];91const char *mtu_value;92size_t mtu;9394if (socketpair(PF_LOCAL, SOCK_SEQPACKET | SOCK_NONBLOCK, 0, s) != 0) {95EPRINTLN("socketpair");96return (-1);97}9899/*100* The child will exit once its connection goes away, so make sure only101* one end is inherited by the child.102*/103if (posix_spawn_file_actions_init(&fa) != 0) {104EPRINTLN("posix_spawn_file_actions_init");105goto err;106}107if (posix_spawn_file_actions_addclose(&fa, s[0]) != 0) {108EPRINTLN("posix_spawn_file_actions_addclose");109posix_spawn_file_actions_destroy(&fa);110goto err;111}112113(void)snprintf(sockname, sizeof(sockname), "%d", s[1]);114argv = (const char *[]){115"/usr/libexec/bhyve-slirp-helper", "-S", sockname, NULL116};117error = posix_spawn(&child, "/usr/libexec/bhyve-slirp-helper",118&fa, NULL, __DECONST(char **, argv), environ);119posix_spawn_file_actions_destroy(&fa);120if (error != 0) {121EPRINTLN("posix_spawn(bhyve-slirp-helper): %s",122strerror(error));123goto err;124}125126config = nvlist_clone(nvl);127if (config == NULL) {128EPRINTLN("nvlist_clone");129goto err;130}131132mtu_value = get_config_value_node(config, "mtu");133if (mtu_value != NULL) {134if (net_parsemtu(mtu_value, &mtu)) {135EPRINTLN("Could not parse MTU");136goto err;137}138} else {139mtu = DEFAULT_MTU;140}141nvlist_add_number(config, "mtui", mtu);142143priv->mtu = mtu;144priv->buf = malloc(mtu);145if (priv->buf == NULL) {146EPRINTLN("Could not allocate buffer");147goto err;148}149150nvlist_add_string(config, "vmname", get_config_value("name"));151error = nvlist_send(s[0], config);152nvlist_destroy(config);153if (error != 0) {154EPRINTLN("nvlist_send");155goto err;156}157158be->fd = s[0];159priv->mevp = mevent_add_disabled(be->fd, EVF_READ, cb, param);160if (priv->mevp == NULL) {161EPRINTLN("Could not register event");162goto err;163}164165priv->helper = child;166priv->s = s[0];167(void)close(s[1]);168169return (0);170171err:172free(priv->buf);173(void)close(s[0]);174(void)close(s[1]);175return (-1);176}177178static ssize_t179slirp_send(struct net_backend *be, const struct iovec *iov, int iovcnt)180{181struct slirp_priv *priv = NET_BE_PRIV(be);182struct msghdr hdr;183184memset(&hdr, 0, sizeof(hdr));185hdr.msg_iov = __DECONST(struct iovec *, iov);186hdr.msg_iovlen = iovcnt;187return (sendmsg(priv->s, &hdr, MSG_EOR));188}189190static void191slirp_cleanup(struct net_backend *be)192{193struct slirp_priv *priv = NET_BE_PRIV(be);194195free(priv->buf);196197if (priv->helper > 0) {198int status;199200if (kill(priv->helper, SIGKILL) != 0) {201EPRINTLN("kill(bhyve-slirp-helper): %s",202strerror(errno));203return;204}205(void)waitpid(priv->helper, &status, 0);206}207}208209static ssize_t210slirp_peek_recvlen(struct net_backend *be)211{212struct slirp_priv *priv = NET_BE_PRIV(be);213ssize_t n;214215/*216* Copying into the buffer is totally unnecessary, but we don't217* implement MSG_TRUNC for SEQPACKET sockets.218*/219n = recv(priv->s, priv->buf, priv->mtu, MSG_PEEK | MSG_DONTWAIT);220if (n < 0)221return (errno == EWOULDBLOCK ? 0 : -1);222return (n);223}224225static ssize_t226slirp_recv(struct net_backend *be, const struct iovec *iov, int iovcnt)227{228struct slirp_priv *priv = NET_BE_PRIV(be);229struct msghdr hdr;230ssize_t n;231232hdr.msg_name = NULL;233hdr.msg_namelen = 0;234hdr.msg_iov = __DECONST(struct iovec *, iov);235hdr.msg_iovlen = iovcnt;236hdr.msg_control = NULL;237hdr.msg_controllen = 0;238hdr.msg_flags = 0;239n = recvmsg(priv->s, &hdr, MSG_DONTWAIT);240if (n < 0) {241if (errno == EWOULDBLOCK)242return (0);243return (-1);244}245assert((size_t)n <= priv->mtu);246return (n);247}248249static void250slirp_recv_enable(struct net_backend *be)251{252struct slirp_priv *priv = NET_BE_PRIV(be);253254mevent_enable(priv->mevp);255}256257static void258slirp_recv_disable(struct net_backend *be)259{260struct slirp_priv *priv = NET_BE_PRIV(be);261262mevent_disable(priv->mevp);263}264265static uint64_t266slirp_get_cap(struct net_backend *be __unused)267{268return (0);269}270271static int272slirp_set_cap(struct net_backend *be __unused, uint64_t features __unused,273unsigned int vnet_hdr_len __unused)274{275return ((features || vnet_hdr_len) ? -1 : 0);276}277278static struct net_backend slirp_backend = {279.prefix = "slirp",280.priv_size = sizeof(struct slirp_priv),281.init = slirp_init,282.cleanup = slirp_cleanup,283.send = slirp_send,284.peek_recvlen = slirp_peek_recvlen,285.recv = slirp_recv,286.recv_enable = slirp_recv_enable,287.recv_disable = slirp_recv_disable,288.get_cap = slirp_get_cap,289.set_cap = slirp_set_cap,290};291292DATA_SET(net_backend_set, slirp_backend);293294295