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
slirp_init(struct net_backend * be,const char * devname __unused,nvlist_t * nvl,net_be_rxeof_t cb,void * param)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
slirp_send(struct net_backend * be,const struct iovec * iov,int iovcnt)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
slirp_cleanup(struct net_backend * be)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
slirp_peek_recvlen(struct net_backend * be)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
slirp_recv(struct net_backend * be,const struct iovec * iov,int iovcnt)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
slirp_recv_enable(struct net_backend * be)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
slirp_recv_disable(struct net_backend * be)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
slirp_get_cap(struct net_backend * be __unused)242 slirp_get_cap(struct net_backend *be __unused)
243 {
244 return (0);
245 }
246
247 static int
slirp_set_cap(struct net_backend * be __unused,uint64_t features __unused,unsigned int vnet_hdr_len __unused)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