1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2023, 2025 Mark Johnston <markj@FreeBSD.org> 5 * 6 * This software was developed by the University of Cambridge Computer 7 * Laboratory (Department of Computer Science and Technology) under Innovate 8 * UK project 105694, "Digital Security by Design (DSbD) Technology Platform 9 * Prototype". 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 /* 34 * The slirp backend enables unprivileged userspace networking via libslirp, 35 * which must be installed on the host system via pkg or the ports tree. 36 * libslirp.so is dlopen()ed into a helper process with which this backend 37 * communicates. 38 * 39 * Packets received from the guest (i.e., transmitted by the frontend, such as a 40 * virtio NIC device model) are injected into the slirp backend via slirp_send(), 41 * which sends the packet to the helper process. 42 * 43 * Packets to be transmitted to the guest (i.e., inserted into the frontend's 44 * receive buffers) are buffered in a per-interface socket pair and read by the 45 * mevent loop. Sockets instantiated by libslirp are monitored by a thread 46 * which uses poll() and slirp_pollfds_poll() to drive libslirp events; this 47 * thread also handles timeout events from the libslirp context. 48 */ 49 50 #include <sys/socket.h> 51 #include <sys/wait.h> 52 53 #include <assert.h> 54 #include <errno.h> 55 #include <signal.h> 56 #include <spawn.h> 57 #include <stdio.h> 58 #include <stdlib.h> 59 #include <string.h> 60 #include <unistd.h> 61 62 #include "config.h" 63 #include "debug.h" 64 #include "mevent.h" 65 #include "net_backends.h" 66 #include "net_backends_priv.h" 67 68 #define SLIRP_MTU 2048 69 70 struct slirp_priv { 71 int s; 72 pid_t helper; 73 struct mevent *mevp; 74 }; 75 76 extern char **environ; 77 78 static int 79 slirp_init(struct net_backend *be, const char *devname __unused, 80 nvlist_t *nvl, net_be_rxeof_t cb, void *param) 81 { 82 struct slirp_priv *priv = NET_BE_PRIV(be); 83 nvlist_t *config; 84 posix_spawn_file_actions_t fa; 85 pid_t child; 86 const char **argv; 87 char sockname[32]; 88 int error, s[2]; 89 90 if (socketpair(PF_LOCAL, SOCK_SEQPACKET | SOCK_NONBLOCK, 0, s) != 0) { 91 EPRINTLN("socketpair"); 92 return (-1); 93 } 94 95 /* 96 * The child will exit once its connection goes away, so make sure only 97 * one end is inherited by the child. 98 */ 99 if (posix_spawn_file_actions_init(&fa) != 0) { 100 EPRINTLN("posix_spawn_file_actions_init"); 101 goto err; 102 } 103 if (posix_spawn_file_actions_addclose(&fa, s[0]) != 0) { 104 EPRINTLN("posix_spawn_file_actions_addclose"); 105 posix_spawn_file_actions_destroy(&fa); 106 goto err; 107 } 108 109 (void)snprintf(sockname, sizeof(sockname), "%d", s[1]); 110 argv = (const char *[]){ 111 "/usr/libexec/bhyve-slirp-helper", "-S", sockname, NULL 112 }; 113 error = posix_spawn(&child, "/usr/libexec/bhyve-slirp-helper", 114 &fa, NULL, __DECONST(char **, argv), environ); 115 posix_spawn_file_actions_destroy(&fa); 116 if (error != 0) { 117 EPRINTLN("posix_spawn(bhyve-slirp-helper): %s", 118 strerror(error)); 119 goto err; 120 } 121 122 config = nvlist_clone(nvl); 123 if (config == NULL) { 124 EPRINTLN("nvlist_clone"); 125 goto err; 126 } 127 nvlist_add_string(config, "vmname", get_config_value("name")); 128 error = nvlist_send(s[0], config); 129 nvlist_destroy(config); 130 if (error != 0) { 131 EPRINTLN("nvlist_send"); 132 goto err; 133 } 134 135 be->fd = s[0]; 136 priv->mevp = mevent_add_disabled(be->fd, EVF_READ, cb, param); 137 if (priv->mevp == NULL) { 138 EPRINTLN("Could not register event"); 139 goto err; 140 } 141 142 priv->helper = child; 143 priv->s = s[0]; 144 (void)close(s[1]); 145 146 return (0); 147 148 err: 149 (void)close(s[0]); 150 (void)close(s[1]); 151 return (-1); 152 } 153 154 static ssize_t 155 slirp_send(struct net_backend *be, const struct iovec *iov, int iovcnt) 156 { 157 struct slirp_priv *priv = NET_BE_PRIV(be); 158 struct msghdr hdr; 159 160 memset(&hdr, 0, sizeof(hdr)); 161 hdr.msg_iov = __DECONST(struct iovec *, iov); 162 hdr.msg_iovlen = iovcnt; 163 return (sendmsg(priv->s, &hdr, MSG_EOR)); 164 } 165 166 static void 167 slirp_cleanup(struct net_backend *be) 168 { 169 struct slirp_priv *priv = NET_BE_PRIV(be); 170 171 if (priv->helper > 0) { 172 int status; 173 174 if (kill(priv->helper, SIGKILL) != 0) { 175 EPRINTLN("kill(bhyve-slirp-helper): %s", 176 strerror(errno)); 177 return; 178 } 179 (void)waitpid(priv->helper, &status, 0); 180 } 181 } 182 183 static ssize_t 184 slirp_peek_recvlen(struct net_backend *be) 185 { 186 struct slirp_priv *priv = NET_BE_PRIV(be); 187 uint8_t buf[SLIRP_MTU]; 188 ssize_t n; 189 190 /* 191 * Copying into the buffer is totally unnecessary, but we don't 192 * implement MSG_TRUNC for SEQPACKET sockets. 193 */ 194 n = recv(priv->s, buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT); 195 if (n < 0) 196 return (errno == EWOULDBLOCK ? 0 : -1); 197 assert((size_t)n <= SLIRP_MTU); 198 return (n); 199 } 200 201 static ssize_t 202 slirp_recv(struct net_backend *be, const struct iovec *iov, int iovcnt) 203 { 204 struct slirp_priv *priv = NET_BE_PRIV(be); 205 struct msghdr hdr; 206 ssize_t n; 207 208 hdr.msg_name = NULL; 209 hdr.msg_namelen = 0; 210 hdr.msg_iov = __DECONST(struct iovec *, iov); 211 hdr.msg_iovlen = iovcnt; 212 hdr.msg_control = NULL; 213 hdr.msg_controllen = 0; 214 hdr.msg_flags = 0; 215 n = recvmsg(priv->s, &hdr, MSG_DONTWAIT); 216 if (n < 0) { 217 if (errno == EWOULDBLOCK) 218 return (0); 219 return (-1); 220 } 221 assert(n <= SLIRP_MTU); 222 return (n); 223 } 224 225 static void 226 slirp_recv_enable(struct net_backend *be) 227 { 228 struct slirp_priv *priv = NET_BE_PRIV(be); 229 230 mevent_enable(priv->mevp); 231 } 232 233 static void 234 slirp_recv_disable(struct net_backend *be) 235 { 236 struct slirp_priv *priv = NET_BE_PRIV(be); 237 238 mevent_disable(priv->mevp); 239 } 240 241 static uint64_t 242 slirp_get_cap(struct net_backend *be __unused) 243 { 244 return (0); 245 } 246 247 static int 248 slirp_set_cap(struct net_backend *be __unused, uint64_t features __unused, 249 unsigned int vnet_hdr_len __unused) 250 { 251 return ((features || vnet_hdr_len) ? -1 : 0); 252 } 253 254 static struct net_backend slirp_backend = { 255 .prefix = "slirp", 256 .priv_size = sizeof(struct slirp_priv), 257 .init = slirp_init, 258 .cleanup = slirp_cleanup, 259 .send = slirp_send, 260 .peek_recvlen = slirp_peek_recvlen, 261 .recv = slirp_recv, 262 .recv_enable = slirp_recv_enable, 263 .recv_disable = slirp_recv_disable, 264 .get_cap = slirp_get_cap, 265 .set_cap = slirp_set_cap, 266 }; 267 268 DATA_SET(net_backend_set, slirp_backend); 269