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
69typedef int (*slirp_add_hostxfwd_p_t)(Slirp *,
70    const struct sockaddr *, socklen_t, const struct sockaddr *, socklen_t,
71    int);
72typedef void (*slirp_cleanup_p_t)(Slirp *);
73typedef void (*slirp_input_p_t)(Slirp *, const uint8_t *, int);
74typedef Slirp *(*slirp_new_p_t)(const SlirpConfig *, const SlirpCb *, void *);
75typedef void (*slirp_pollfds_fill_p_t)(Slirp *, uint32_t *timeout,
76    SlirpAddPollCb, void *);
77typedef void (*slirp_pollfds_poll_p_t)(Slirp *, int, SlirpGetREventsCb, void *);
78
79/* Function pointer table, initialized by slirp_init_once(). */
80static slirp_add_hostxfwd_p_t slirp_add_hostxfwd_p;
81static slirp_cleanup_p_t slirp_cleanup_p;
82static slirp_input_p_t slirp_input_p;
83static slirp_new_p_t slirp_new_p;
84static slirp_pollfds_fill_p_t slirp_pollfds_fill_p;
85static slirp_pollfds_poll_p_t slirp_pollfds_poll_p;
86
87static int
88slirp_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
126err:
127	dlclose(handle);
128	handle = NULL;
129	return (-1);
130}
131
132struct 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
147static void
148slirp_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
158static void
159slirp_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
179static int64_t
180slirp_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
190static void
191slirp_cb_notify(void *param __unused)
192{
193}
194
195static void
196slirp_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
203static ssize_t
204slirp_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
222static void
223slirp_cb_unregister_poll_fd(int fd __unused, void *opaque __unused)
224{
225}
226
227/* Callbacks invoked from within libslirp. */
228static 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
236static int
237slirpev2pollev(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
255static int
256pollev2slirpev(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
274static int
275slirp_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
308static int
309slirp_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
320static void *
321slirp_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
361static int
362parse_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
392static int
393parse_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
435out:
436	free(p);
437	return (error);
438}
439
440static int
441config_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
464static 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				free(tofree);
507				goto err;
508			}
509		}
510		free(tofree);
511	}
512
513	error = socketpair(PF_LOCAL, SOCK_DGRAM, 0, priv->pipe);
514	if (error != 0) {
515		EPRINTLN("Unable to create pipe: %s", strerror(errno));
516		goto err;
517	}
518
519	/*
520	 * Try to avoid dropping buffered packets in slirp_cb_send_packet().
521	 */
522	sndbuf = 1024 * 1024;
523	error = setsockopt(priv->pipe[1], SOL_SOCKET, SO_SNDBUF, &sndbuf,
524	    sizeof(sndbuf));
525	if (error != 0) {
526		EPRINTLN("Could not set socket buffer size: %s",
527		    strerror(errno));
528		goto err;
529	}
530
531	be->fd = priv->pipe[0];
532	priv->mevp = mevent_add_disabled(be->fd, EVF_READ, cb, param);
533	if (priv->mevp == NULL) {
534		EPRINTLN("Could not register event");
535		goto err;
536	}
537
538	error = pthread_create(&priv->pollfd_td, NULL, slirp_pollfd_td_loop,
539	    priv);
540	if (error != 0) {
541		EPRINTLN("Unable to create pollfd thread: %s", strerror(error));
542		goto err;
543	}
544
545	return (0);
546
547err:
548	slirp_priv_cleanup(priv);
549	return (-1);
550}
551
552static ssize_t
553slirp_send(struct net_backend *be, const struct iovec *iov, int iovcnt)
554{
555	struct slirp_priv *priv = NET_BE_PRIV(be);
556
557	if (iovcnt == 1) {
558		/* We can avoid copying if there's a single segment. */
559		pthread_mutex_lock(&priv->mtx);
560		slirp_input_p(priv->slirp, iov->iov_base,
561		    (int)iov->iov_len);
562		pthread_mutex_unlock(&priv->mtx);
563		return (iov[0].iov_len);
564	} else {
565		uint8_t *pkt;
566		size_t pktlen;
567
568		pktlen = 0;
569		for (int i = 0; i < iovcnt; i++)
570			pktlen += iov[i].iov_len;
571		pkt = malloc(pktlen);
572		if (pkt == NULL)
573			return (-1);
574		pktlen = 0;
575		for (int i = 0; i < iovcnt; i++) {
576			memcpy(pkt + pktlen, iov[i].iov_base, iov[i].iov_len);
577			pktlen += iov[i].iov_len;
578		}
579		pthread_mutex_lock(&priv->mtx);
580		slirp_input_p(priv->slirp, pkt, (int)pktlen);
581		pthread_mutex_unlock(&priv->mtx);
582		free(pkt);
583		return (pktlen);
584	}
585}
586
587static void
588_slirp_cleanup(struct net_backend *be)
589{
590	struct slirp_priv *priv = NET_BE_PRIV(be);
591
592	slirp_priv_cleanup(priv);
593}
594
595static ssize_t
596slirp_peek_recvlen(struct net_backend *be)
597{
598	struct slirp_priv *priv = NET_BE_PRIV(be);
599	ssize_t n;
600
601	n = recv(priv->pipe[0], NULL, 0, MSG_PEEK | MSG_DONTWAIT | MSG_TRUNC);
602	if (n < 0)
603		return (errno == EWOULDBLOCK ? 0 : -1);
604	assert((size_t)n <= SLIRP_MTU);
605	return (n);
606}
607
608static ssize_t
609slirp_recv(struct net_backend *be, const struct iovec *iov, int iovcnt)
610{
611	struct slirp_priv *priv = NET_BE_PRIV(be);
612	ssize_t n;
613
614	n = readv(priv->pipe[0], iov, iovcnt);
615	if (n < 0)
616		return (-1);
617	assert(n <= SLIRP_MTU);
618	return (n);
619}
620
621static void
622slirp_recv_enable(struct net_backend *be)
623{
624	struct slirp_priv *priv = NET_BE_PRIV(be);
625
626	mevent_enable(priv->mevp);
627}
628
629static void
630slirp_recv_disable(struct net_backend *be)
631{
632	struct slirp_priv *priv = NET_BE_PRIV(be);
633
634	mevent_disable(priv->mevp);
635}
636
637static uint64_t
638slirp_get_cap(struct net_backend *be __unused)
639{
640	return (0);
641}
642
643static int
644slirp_set_cap(struct net_backend *be __unused, uint64_t features __unused,
645    unsigned int vnet_hdr_len __unused)
646{
647	return ((features || vnet_hdr_len) ? -1 : 0);
648}
649
650static struct net_backend slirp_backend = {
651	.prefix = "slirp",
652	.priv_size = sizeof(struct slirp_priv),
653	.init = _slirp_init,
654	.cleanup = _slirp_cleanup,
655	.send = slirp_send,
656	.peek_recvlen = slirp_peek_recvlen,
657	.recv = slirp_recv,
658	.recv_enable = slirp_recv_enable,
659	.recv_disable = slirp_recv_disable,
660	.get_cap = slirp_get_cap,
661	.set_cap = slirp_set_cap,
662};
663
664DATA_SET(net_backend_set, slirp_backend);
665