xref: /freebsd/usr.sbin/bhyve/net_backend_slirp.c (revision 1db64f89363c97858961c4df0b7d02f3223723cf)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2023 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 networking via libslirp, which must be
35  * installed on the host system via pkg or the ports tree.  bhyve dlopen()s
36  * libslirp.so upon instantiating the slirp backend.  Various network parameters
37  * are hard-coded in _slirp_init().
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  * Packets to be transmitted to the guest (i.e., inserted into the frontend's
42  * receive buffers) are buffered in a per-interface socket pair and read by the
43  * mevent loop.  Sockets instantiated by libslirp are monitored by a thread
44  * which uses poll() and slirp_pollfds_poll() to drive libslirp events; this
45  * thread also handles timeout events from the libslirp context.
46  */
47 
48 #include <sys/socket.h>
49 
50 #include <assert.h>
51 #include <capsicum_helpers.h>
52 #include <dlfcn.h>
53 #include <errno.h>
54 #include <poll.h>
55 #include <pthread.h>
56 #include <pthread_np.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 "libslirp.h"
65 #include "mevent.h"
66 #include "net_backends.h"
67 #include "net_backends_priv.h"
68 
69 typedef int (*slirp_add_hostxfwd_p_t)(Slirp *,
70     const struct sockaddr *, socklen_t, const struct sockaddr *, socklen_t,
71     int);
72 typedef void (*slirp_cleanup_p_t)(Slirp *);
73 typedef void (*slirp_input_p_t)(Slirp *, const uint8_t *, int);
74 typedef Slirp *(*slirp_new_p_t)(const SlirpConfig *, const SlirpCb *, void *);
75 typedef void (*slirp_pollfds_fill_p_t)(Slirp *, uint32_t *timeout,
76     SlirpAddPollCb, void *);
77 typedef void (*slirp_pollfds_poll_p_t)(Slirp *, int, SlirpGetREventsCb, void *);
78 
79 /* Function pointer table, initialized by slirp_init_once(). */
80 static slirp_add_hostxfwd_p_t slirp_add_hostxfwd_p;
81 static slirp_cleanup_p_t slirp_cleanup_p;
82 static slirp_input_p_t slirp_input_p;
83 static slirp_new_p_t slirp_new_p;
84 static slirp_pollfds_fill_p_t slirp_pollfds_fill_p;
85 static slirp_pollfds_poll_p_t slirp_pollfds_poll_p;
86 
87 static int
88 slirp_init_once(void)
89 {
90 	static void *handle = NULL;
91 
92 	if (handle != NULL)
93 		return (0);
94 	handle = dlopen("libslirp.so.0", RTLD_LAZY);
95 	if (handle == NULL) {
96 		EPRINTLN("Unable to open libslirp.so.0: %s", dlerror());
97 		return (-1);
98 	}
99 
100 #define IMPORT_SYM(sym) do {					\
101 	sym##_p = (sym##_p_t)dlsym(handle, #sym);		\
102 	if (sym##_p == NULL) {					\
103 		EPRINTLN("failed to resolve %s", #sym);		\
104 		goto err;					\
105 	}							\
106 } while (0)
107 	IMPORT_SYM(slirp_add_hostxfwd);
108 	IMPORT_SYM(slirp_cleanup);
109 	IMPORT_SYM(slirp_input);
110 	IMPORT_SYM(slirp_new);
111 	IMPORT_SYM(slirp_pollfds_fill);
112 	IMPORT_SYM(slirp_pollfds_poll);
113 #undef IMPORT_SYM
114 
115 	/*
116 	 * libslirp uses glib, which uses tzdata to format log messages.  Help
117 	 * it out.
118 	 *
119 	 * XXX-MJ glib will also look for charset files, not sure what we can do
120 	 * about that...
121 	 */
122 	caph_cache_tzdata();
123 
124 	return (0);
125 
126 err:
127 	dlclose(handle);
128 	handle = NULL;
129 	return (-1);
130 }
131 
132 struct slirp_priv {
133 	Slirp *slirp;
134 
135 #define	SLIRP_MTU	2048
136 	struct mevent *mevp;
137 	int pipe[2];
138 
139 	pthread_t pollfd_td;
140 	struct pollfd *pollfds;
141 	size_t npollfds;
142 
143 	/* Serializes libslirp calls. */
144 	pthread_mutex_t mtx;
145 };
146 
147 static void
148 slirp_priv_init(struct slirp_priv *priv)
149 {
150 	int error;
151 
152 	memset(priv, 0, sizeof(*priv));
153 	priv->pipe[0] = priv->pipe[1] = -1;
154 	error = pthread_mutex_init(&priv->mtx, NULL);
155 	assert(error == 0);
156 }
157 
158 static void
159 slirp_priv_cleanup(struct slirp_priv *priv)
160 {
161 	int error;
162 
163 	if (priv->pipe[0] != -1) {
164 		error = close(priv->pipe[0]);
165 		assert(error == 0);
166 	}
167 	if (priv->pipe[1] != -1) {
168 		error = close(priv->pipe[1]);
169 		assert(error == 0);
170 	}
171 	if (priv->mevp)
172 		mevent_delete(priv->mevp);
173 	if (priv->slirp != NULL)
174 		slirp_cleanup_p(priv->slirp);
175 	error = pthread_mutex_destroy(&priv->mtx);
176 	assert(error == 0);
177 }
178 
179 static int64_t
180 slirp_cb_clock_get_ns(void *param __unused)
181 {
182 	struct timespec ts;
183 	int error;
184 
185 	error = clock_gettime(CLOCK_MONOTONIC, &ts);
186 	assert(error == 0);
187 	return ((int64_t)(ts.tv_sec * 1000000000L + ts.tv_nsec));
188 }
189 
190 static void
191 slirp_cb_notify(void *param __unused)
192 {
193 }
194 
195 static void
196 slirp_cb_register_poll_fd(int fd, void *param __unused)
197 {
198 	const int one = 1;
199 
200 	(void)setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(int));
201 }
202 
203 static ssize_t
204 slirp_cb_send_packet(const void *buf, size_t len, void *param)
205 {
206 	struct slirp_priv *priv;
207 	ssize_t n;
208 
209 	priv = param;
210 
211 	assert(len <= SLIRP_MTU);
212 	n = send(priv->pipe[1], buf, len, 0);
213 	if (n < 0) {
214 		EPRINTLN("slirp_cb_send_packet: send: %s", strerror(errno));
215 		return (n);
216 	}
217 	assert((size_t)n == len);
218 
219 	return (n);
220 }
221 
222 static void
223 slirp_cb_unregister_poll_fd(int fd __unused, void *opaque __unused)
224 {
225 }
226 
227 /* Callbacks invoked from within libslirp. */
228 static const struct SlirpCb slirp_cbs = {
229 	.clock_get_ns = slirp_cb_clock_get_ns,
230 	.notify = slirp_cb_notify,
231 	.register_poll_fd = slirp_cb_register_poll_fd,
232 	.send_packet = slirp_cb_send_packet,
233 	.unregister_poll_fd = slirp_cb_unregister_poll_fd,
234 };
235 
236 static int
237 slirpev2pollev(int events)
238 {
239 	int ret;
240 
241 	ret = 0;
242 	if (events & SLIRP_POLL_IN)
243 		ret |= POLLIN;
244 	if (events & SLIRP_POLL_OUT)
245 		ret |= POLLOUT;
246 	if (events & SLIRP_POLL_PRI)
247 		ret |= POLLPRI;
248 	if (events & SLIRP_POLL_ERR)
249 		ret |= POLLERR;
250 	if (events & SLIRP_POLL_HUP)
251 		ret |= POLLHUP;
252 	return (ret);
253 }
254 
255 static int
256 pollev2slirpev(int events)
257 {
258 	int ret;
259 
260 	ret = 0;
261 	if (events & POLLIN)
262 		ret |= SLIRP_POLL_IN;
263 	if (events & POLLOUT)
264 		ret |= SLIRP_POLL_OUT;
265 	if (events & POLLPRI)
266 		ret |= SLIRP_POLL_PRI;
267 	if (events & POLLERR)
268 		ret |= SLIRP_POLL_ERR;
269 	if (events & POLLHUP)
270 		ret |= SLIRP_POLL_HUP;
271 	return (ret);
272 }
273 
274 static int
275 slirp_addpoll_cb(int fd, int events, void *param)
276 {
277 	struct slirp_priv *priv;
278 	struct pollfd *pollfd, *pollfds;
279 	size_t i;
280 
281 	priv = param;
282 
283 	for (i = 0; i < priv->npollfds; i++)
284 		if (priv->pollfds[i].fd == -1)
285 			break;
286 	if (i == priv->npollfds) {
287 		const size_t POLLFD_GROW = 4;
288 
289 		priv->npollfds += POLLFD_GROW;
290 		pollfds = realloc(priv->pollfds,
291 		    sizeof(*pollfds) * priv->npollfds);
292 		if (pollfds == NULL)
293 			return (-1);
294 		for (i = priv->npollfds - POLLFD_GROW; i < priv->npollfds; i++)
295 			pollfds[i].fd = -1;
296 		priv->pollfds = pollfds;
297 
298 		i = priv->npollfds - POLLFD_GROW;
299 	}
300 	pollfd = &priv->pollfds[i];
301 	pollfd->fd = fd;
302 	pollfd->events = slirpev2pollev(events);
303 	pollfd->revents = 0;
304 
305 	return ((int)i);
306 }
307 
308 static int
309 slirp_poll_revents(int idx, void *param)
310 {
311 	struct slirp_priv *priv;
312 	struct pollfd *pollfd;
313 
314 	priv = param;
315 	pollfd = &priv->pollfds[idx];
316 	assert(pollfd->fd != -1);
317 	return (pollev2slirpev(pollfd->revents));
318 }
319 
320 static void *
321 slirp_pollfd_td_loop(void *param)
322 {
323 	struct slirp_priv *priv;
324 	struct pollfd *pollfds;
325 	size_t npollfds;
326 	uint32_t timeout;
327 	int error;
328 
329 	pthread_set_name_np(pthread_self(), "slirp pollfd");
330 	priv = param;
331 
332 	pthread_mutex_lock(&priv->mtx);
333 	for (;;) {
334 		for (size_t i = 0; i < priv->npollfds; i++)
335 			priv->pollfds[i].fd = -1;
336 
337 		timeout = UINT32_MAX;
338 		slirp_pollfds_fill_p(priv->slirp, &timeout, slirp_addpoll_cb,
339 		    priv);
340 
341 		pollfds = priv->pollfds;
342 		npollfds = priv->npollfds;
343 		pthread_mutex_unlock(&priv->mtx);
344 		for (;;) {
345 			error = poll(pollfds, npollfds, timeout);
346 			if (error == -1) {
347 				if (errno != EINTR) {
348 					EPRINTLN("poll: %s", strerror(errno));
349 					exit(1);
350 				}
351 				continue;
352 			}
353 			break;
354 		}
355 		pthread_mutex_lock(&priv->mtx);
356 		slirp_pollfds_poll_p(priv->slirp, error == -1,
357 		    slirp_poll_revents, priv);
358 	}
359 }
360 
361 static int
362 parse_addr(char *addr, struct sockaddr_in *sinp)
363 {
364 	char *port;
365 	int error, porti;
366 
367 	memset(sinp, 0, sizeof(*sinp));
368 	sinp->sin_family = AF_INET;
369 	sinp->sin_len = sizeof(struct sockaddr_in);
370 
371 	port = strchr(addr, ':');
372 	if (port == NULL)
373 		return (EINVAL);
374 	*port++ = '\0';
375 
376 	if (strlen(addr) > 0) {
377 		error = inet_pton(AF_INET, addr, &sinp->sin_addr);
378 		if (error != 1)
379 			return (error == 0 ? EPFNOSUPPORT : errno);
380 	} else {
381 		sinp->sin_addr.s_addr = htonl(INADDR_ANY);
382 	}
383 
384 	porti = strlen(port) > 0 ? atoi(port) : 0;
385 	if (porti < 0 || porti > UINT16_MAX)
386 		return (EINVAL);
387 	sinp->sin_port = htons(porti);
388 
389 	return (0);
390 }
391 
392 static int
393 parse_hostfwd_rule(const char *descr, int *is_udp, struct sockaddr *hostaddr,
394     struct sockaddr *guestaddr)
395 {
396 	struct sockaddr_in *hostaddrp, *guestaddrp;
397 	const char *proto;
398 	char *p, *host, *guest;
399 	int error;
400 
401 	error = 0;
402 	*is_udp = 0;
403 
404 	p = strdup(descr);
405 	if (p == NULL)
406 		return (ENOMEM);
407 
408 	host = strchr(p, ':');
409 	if (host == NULL) {
410 		error = EINVAL;
411 		goto out;
412 	}
413 	*host++ = '\0';
414 
415 	proto = p;
416 	*is_udp = strcmp(proto, "udp") == 0;
417 
418 	guest = strchr(host, '-');
419 	if (guest == NULL) {
420 		error = EINVAL;
421 		goto out;
422 	}
423 	*guest++ = '\0';
424 
425 	hostaddrp = (struct sockaddr_in *)hostaddr;
426 	error = parse_addr(host, hostaddrp);
427 	if (error != 0)
428 		goto out;
429 
430 	guestaddrp = (struct sockaddr_in *)guestaddr;
431 	error = parse_addr(guest, guestaddrp);
432 	if (error != 0)
433 		goto out;
434 
435 out:
436 	free(p);
437 	return (error);
438 }
439 
440 static int
441 config_one_hostfwd(struct slirp_priv *priv, const char *rule)
442 {
443 	struct sockaddr hostaddr, guestaddr;
444 	int error, is_udp;
445 
446 	error = parse_hostfwd_rule(rule, &is_udp, &hostaddr, &guestaddr);
447 	if (error != 0) {
448 		EPRINTLN("Unable to parse hostfwd rule '%s': %s",
449 		    rule, strerror(error));
450 		return (error);
451 	}
452 
453 	error = slirp_add_hostxfwd_p(priv->slirp, &hostaddr, hostaddr.sa_len,
454 	    &guestaddr, guestaddr.sa_len, is_udp ? SLIRP_HOSTFWD_UDP : 0);
455 	if (error != 0) {
456 		EPRINTLN("Unable to add hostfwd rule '%s': %s",
457 		    rule, strerror(errno));
458 		return (error);
459 	}
460 
461 	return (0);
462 }
463 
464 static int
465 _slirp_init(struct net_backend *be, const char *devname __unused,
466     nvlist_t *nvl, net_be_rxeof_t cb, void *param)
467 {
468 	struct slirp_priv *priv = NET_BE_PRIV(be);
469 	SlirpConfig config = {
470 		.version = 4,
471 		.if_mtu = SLIRP_MTU,
472 		.restricted = true,
473 		.in_enabled = true,
474 		.vnetwork.s_addr = htonl(0x0a000200),	/* 10.0.2.0/24 */
475 		.vnetmask.s_addr = htonl(0xffffff00),
476 		.vdhcp_start.s_addr = htonl(0x0a00020f),/* 10.0.2.15 */
477 		.vhost.s_addr = htonl(0x0a000202),	/* 10.0.2.2 */
478 		.enable_emu = false,
479 	};
480 	const char *hostfwd;
481 	int error, sndbuf;
482 
483 	error = slirp_init_once();
484 	if (error != 0)
485 		return (error);
486 
487 	slirp_priv_init(priv);
488 
489 	priv->slirp = slirp_new_p(&config, &slirp_cbs, priv);
490 	if (priv->slirp == NULL) {
491 		EPRINTLN("Unable to create slirp instance");
492 		goto err;
493 	}
494 
495 	hostfwd = get_config_value_node(nvl, "hostfwd");
496 	if (hostfwd != NULL) {
497 		char *rules, *tofree;
498 		const char *rule;
499 
500 		tofree = rules = strdup(hostfwd);
501 		if (rules == NULL)
502 			goto err;
503 		while ((rule = strsep(&rules, ";")) != NULL) {
504 			error = config_one_hostfwd(priv, rule);
505 			if (error != 0)
506 				goto err;
507 		}
508 		free(tofree);
509 	}
510 
511 	error = socketpair(PF_LOCAL, SOCK_DGRAM, 0, priv->pipe);
512 	if (error != 0) {
513 		EPRINTLN("Unable to create pipe: %s", strerror(errno));
514 		goto err;
515 	}
516 
517 	/*
518 	 * Try to avoid dropping buffered packets in slirp_cb_send_packet().
519 	 */
520 	sndbuf = 1024 * 1024;
521 	error = setsockopt(priv->pipe[1], SOL_SOCKET, SO_SNDBUF, &sndbuf,
522 	    sizeof(sndbuf));
523 	if (error != 0) {
524 		EPRINTLN("Could not set socket buffer size: %s",
525 		    strerror(errno));
526 		goto err;
527 	}
528 
529 	be->fd = priv->pipe[0];
530 	priv->mevp = mevent_add_disabled(be->fd, EVF_READ, cb, param);
531 	if (priv->mevp == NULL) {
532 		EPRINTLN("Could not register event");
533 		goto err;
534 	}
535 
536 	error = pthread_create(&priv->pollfd_td, NULL, slirp_pollfd_td_loop,
537 	    priv);
538 	if (error != 0) {
539 		EPRINTLN("Unable to create pollfd thread: %s", strerror(error));
540 		goto err;
541 	}
542 
543 	return (0);
544 
545 err:
546 	slirp_priv_cleanup(priv);
547 	return (-1);
548 }
549 
550 static ssize_t
551 slirp_send(struct net_backend *be, const struct iovec *iov, int iovcnt)
552 {
553 	struct slirp_priv *priv = NET_BE_PRIV(be);
554 
555 	if (iovcnt == 1) {
556 		/* We can avoid copying if there's a single segment. */
557 		pthread_mutex_lock(&priv->mtx);
558 		slirp_input_p(priv->slirp, iov->iov_base,
559 		    (int)iov->iov_len);
560 		pthread_mutex_unlock(&priv->mtx);
561 		return (iov[0].iov_len);
562 	} else {
563 		uint8_t *pkt;
564 		size_t pktlen;
565 
566 		pktlen = 0;
567 		for (int i = 0; i < iovcnt; i++)
568 			pktlen += iov[i].iov_len;
569 		pkt = malloc(pktlen);
570 		if (pkt == NULL)
571 			return (-1);
572 		pktlen = 0;
573 		for (int i = 0; i < iovcnt; i++) {
574 			memcpy(pkt + pktlen, iov[i].iov_base, iov[i].iov_len);
575 			pktlen += iov[i].iov_len;
576 		}
577 		pthread_mutex_lock(&priv->mtx);
578 		slirp_input_p(priv->slirp, pkt, (int)pktlen);
579 		pthread_mutex_unlock(&priv->mtx);
580 		free(pkt);
581 		return (pktlen);
582 	}
583 }
584 
585 static void
586 _slirp_cleanup(struct net_backend *be)
587 {
588 	struct slirp_priv *priv = NET_BE_PRIV(be);
589 
590 	slirp_priv_cleanup(priv);
591 }
592 
593 static ssize_t
594 slirp_peek_recvlen(struct net_backend *be)
595 {
596 	struct slirp_priv *priv = NET_BE_PRIV(be);
597 	ssize_t n;
598 
599 	n = recv(priv->pipe[0], NULL, 0, MSG_PEEK | MSG_DONTWAIT | MSG_TRUNC);
600 	if (n < 0)
601 		return (errno == EWOULDBLOCK ? 0 : -1);
602 	assert((size_t)n <= SLIRP_MTU);
603 	return (n);
604 }
605 
606 static ssize_t
607 slirp_recv(struct net_backend *be, const struct iovec *iov, int iovcnt)
608 {
609 	struct slirp_priv *priv = NET_BE_PRIV(be);
610 	ssize_t n;
611 
612 	n = readv(priv->pipe[0], iov, iovcnt);
613 	if (n < 0)
614 		return (-1);
615 	assert(n <= SLIRP_MTU);
616 	return (n);
617 }
618 
619 static void
620 slirp_recv_enable(struct net_backend *be)
621 {
622 	struct slirp_priv *priv = NET_BE_PRIV(be);
623 
624 	mevent_enable(priv->mevp);
625 }
626 
627 static void
628 slirp_recv_disable(struct net_backend *be __unused)
629 {
630 	struct slirp_priv *priv = NET_BE_PRIV(be);
631 
632 	mevent_enable(priv->mevp);
633 }
634 
635 static uint64_t
636 slirp_get_cap(struct net_backend *be __unused)
637 {
638 	return (0);
639 }
640 
641 static int
642 slirp_set_cap(struct net_backend *be __unused, uint64_t features __unused,
643     unsigned int vnet_hdr_len __unused)
644 {
645 	return ((features || vnet_hdr_len) ? -1 : 0);
646 }
647 
648 static struct net_backend slirp_backend = {
649 	.prefix = "slirp",
650 	.priv_size = sizeof(struct slirp_priv),
651 	.init = _slirp_init,
652 	.cleanup = _slirp_cleanup,
653 	.send = slirp_send,
654 	.peek_recvlen = slirp_peek_recvlen,
655 	.recv = slirp_recv,
656 	.recv_enable = slirp_recv_enable,
657 	.recv_disable = slirp_recv_disable,
658 	.get_cap = slirp_get_cap,
659 	.set_cap = slirp_set_cap,
660 };
661 
662 DATA_SET(net_backend_set, slirp_backend);
663