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