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