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