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 static int 77 slirp_init(struct net_backend *be, const char *devname __unused, 78 nvlist_t *nvl, net_be_rxeof_t cb, void *param) 79 { 80 extern char **environ; 81 struct slirp_priv *priv = NET_BE_PRIV(be); 82 nvlist_t *config; 83 posix_spawn_file_actions_t fa; 84 pid_t child; 85 const char **argv; 86 char sockname[32]; 87 int error, s[2]; 88 89 if (socketpair(PF_LOCAL, SOCK_SEQPACKET | SOCK_NONBLOCK, 0, s) != 0) { 90 EPRINTLN("socketpair"); 91 return (-1); 92 } 93 94 /* 95 * The child will exit once its connection goes away, so make sure only 96 * one end is inherited by the child. 97 */ 98 if (posix_spawn_file_actions_init(&fa) != 0) { 99 EPRINTLN("posix_spawn_file_actions_init"); 100 goto err; 101 } 102 if (posix_spawn_file_actions_addclose(&fa, s[0]) != 0) { 103 EPRINTLN("posix_spawn_file_actions_addclose"); 104 posix_spawn_file_actions_destroy(&fa); 105 goto err; 106 } 107 108 (void)snprintf(sockname, sizeof(sockname), "%d", s[1]); 109 argv = (const char *[]){ 110 "/usr/libexec/bhyve-slirp-helper", "-S", sockname, NULL 111 }; 112 error = posix_spawn(&child, "/usr/libexec/bhyve-slirp-helper", 113 &fa, NULL, __DECONST(char **, argv), environ); 114 posix_spawn_file_actions_destroy(&fa); 115 if (error != 0) { 116 EPRINTLN("posix_spawn(bhyve-slirp-helper): %s", 117 strerror(error)); 118 goto err; 119 } 120 121 config = nvlist_clone(nvl); 122 if (config == NULL) { 123 EPRINTLN("nvlist_clone"); 124 goto err; 125 } 126 nvlist_add_string(config, "vmname", get_config_value("name")); 127 error = nvlist_send(s[0], config); 128 nvlist_destroy(config); 129 if (error != 0) { 130 EPRINTLN("nvlist_send"); 131 goto err; 132 } 133 134 be->fd = s[0]; 135 priv->mevp = mevent_add_disabled(be->fd, EVF_READ, cb, param); 136 if (priv->mevp == NULL) { 137 EPRINTLN("Could not register event"); 138 goto err; 139 } 140 141 priv->helper = child; 142 priv->s = s[0]; 143 (void)close(s[1]); 144 145 return (0); 146 147 err: 148 (void)close(s[0]); 149 (void)close(s[1]); 150 return (-1); 151 } 152 153 static ssize_t 154 slirp_send(struct net_backend *be, const struct iovec *iov, int iovcnt) 155 { 156 struct slirp_priv *priv = NET_BE_PRIV(be); 157 struct msghdr hdr; 158 159 memset(&hdr, 0, sizeof(hdr)); 160 hdr.msg_iov = __DECONST(struct iovec *, iov); 161 hdr.msg_iovlen = iovcnt; 162 return (sendmsg(priv->s, &hdr, MSG_EOR)); 163 } 164 165 static void 166 slirp_cleanup(struct net_backend *be) 167 { 168 struct slirp_priv *priv = NET_BE_PRIV(be); 169 170 if (priv->helper > 0) { 171 int status; 172 173 if (kill(priv->helper, SIGKILL) != 0) { 174 EPRINTLN("kill(bhyve-slirp-helper): %s", 175 strerror(errno)); 176 return; 177 } 178 (void)waitpid(priv->helper, &status, 0); 179 } 180 } 181 182 static ssize_t 183 slirp_peek_recvlen(struct net_backend *be) 184 { 185 struct slirp_priv *priv = NET_BE_PRIV(be); 186 uint8_t buf[SLIRP_MTU]; 187 ssize_t n; 188 189 /* 190 * Copying into the buffer is totally unnecessary, but we don't 191 * implement MSG_TRUNC for SEQPACKET sockets. 192 */ 193 n = recv(priv->s, buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT); 194 if (n < 0) 195 return (errno == EWOULDBLOCK ? 0 : -1); 196 assert((size_t)n <= SLIRP_MTU); 197 return (n); 198 } 199 200 static ssize_t 201 slirp_recv(struct net_backend *be, const struct iovec *iov, int iovcnt) 202 { 203 struct slirp_priv *priv = NET_BE_PRIV(be); 204 struct msghdr hdr; 205 ssize_t n; 206 207 hdr.msg_name = NULL; 208 hdr.msg_namelen = 0; 209 hdr.msg_iov = __DECONST(struct iovec *, iov); 210 hdr.msg_iovlen = iovcnt; 211 hdr.msg_control = NULL; 212 hdr.msg_controllen = 0; 213 hdr.msg_flags = 0; 214 n = recvmsg(priv->s, &hdr, MSG_DONTWAIT); 215 if (n < 0) { 216 if (errno == EWOULDBLOCK) 217 return (0); 218 return (-1); 219 } 220 assert(n <= SLIRP_MTU); 221 return (n); 222 } 223 224 static void 225 slirp_recv_enable(struct net_backend *be) 226 { 227 struct slirp_priv *priv = NET_BE_PRIV(be); 228 229 mevent_enable(priv->mevp); 230 } 231 232 static void 233 slirp_recv_disable(struct net_backend *be) 234 { 235 struct slirp_priv *priv = NET_BE_PRIV(be); 236 237 mevent_disable(priv->mevp); 238 } 239 240 static uint64_t 241 slirp_get_cap(struct net_backend *be __unused) 242 { 243 return (0); 244 } 245 246 static int 247 slirp_set_cap(struct net_backend *be __unused, uint64_t features __unused, 248 unsigned int vnet_hdr_len __unused) 249 { 250 return ((features || vnet_hdr_len) ? -1 : 0); 251 } 252 253 static struct net_backend slirp_backend = { 254 .prefix = "slirp", 255 .priv_size = sizeof(struct slirp_priv), 256 .init = slirp_init, 257 .cleanup = slirp_cleanup, 258 .send = slirp_send, 259 .peek_recvlen = slirp_peek_recvlen, 260 .recv = slirp_recv, 261 .recv_enable = slirp_recv_enable, 262 .recv_disable = slirp_recv_disable, 263 .get_cap = slirp_get_cap, 264 .set_cap = slirp_set_cap, 265 }; 266 267 DATA_SET(net_backend_set, slirp_backend); 268