xref: /freebsd/usr.sbin/bhyve/slirp/slirp-helper.c (revision 0e62ebd20172f67283bac9526c2aaeaffeb41b45)
1*0e62ebd2SMark Johnston /*-
2*0e62ebd2SMark Johnston  * SPDX-License-Identifier: BSD-2-Clause
3*0e62ebd2SMark Johnston  *
4*0e62ebd2SMark Johnston  * Copyright (c) 2023, 2025 Mark Johnston <markj@FreeBSD.org>
5*0e62ebd2SMark Johnston  *
6*0e62ebd2SMark Johnston  * This software was developed by the University of Cambridge Computer
7*0e62ebd2SMark Johnston  * Laboratory (Department of Computer Science and Technology) under Innovate
8*0e62ebd2SMark Johnston  * UK project 105694, "Digital Security by Design (DSbD) Technology Platform
9*0e62ebd2SMark Johnston  * Prototype".
10*0e62ebd2SMark Johnston  */
11*0e62ebd2SMark Johnston 
12*0e62ebd2SMark Johnston /*
13*0e62ebd2SMark Johnston  * A helper process which lets bhyve's libslirp-based network backend work
14*0e62ebd2SMark Johnston  * outside bhyve's Capsicum sandbox.  We are started with a SOCK_SEQPACKET
15*0e62ebd2SMark Johnston  * socket through which we pass and receive packets from the guest's frontend.
16*0e62ebd2SMark Johnston  *
17*0e62ebd2SMark Johnston  * At initialization time, we receive an nvlist over the socket which describes
18*0e62ebd2SMark Johnston  * the desired slirp configuration.
19*0e62ebd2SMark Johnston  */
20*0e62ebd2SMark Johnston 
21*0e62ebd2SMark Johnston #include <sys/nv.h>
22*0e62ebd2SMark Johnston #include <sys/socket.h>
23*0e62ebd2SMark Johnston 
24*0e62ebd2SMark Johnston #include <assert.h>
25*0e62ebd2SMark Johnston #include <capsicum_helpers.h>
26*0e62ebd2SMark Johnston #include <dlfcn.h>
27*0e62ebd2SMark Johnston #include <err.h>
28*0e62ebd2SMark Johnston #include <errno.h>
29*0e62ebd2SMark Johnston #include <fcntl.h>
30*0e62ebd2SMark Johnston #include <poll.h>
31*0e62ebd2SMark Johnston #include <pwd.h>
32*0e62ebd2SMark Johnston #include <stdio.h>
33*0e62ebd2SMark Johnston #include <stdlib.h>
34*0e62ebd2SMark Johnston #include <string.h>
35*0e62ebd2SMark Johnston #include <time.h>
36*0e62ebd2SMark Johnston #include <unistd.h>
37*0e62ebd2SMark Johnston 
38*0e62ebd2SMark Johnston #include "config.h"
39*0e62ebd2SMark Johnston #include "libslirp.h"
40*0e62ebd2SMark Johnston 
41*0e62ebd2SMark Johnston #define	SLIRP_MTU	2048
42*0e62ebd2SMark Johnston 
43*0e62ebd2SMark Johnston struct slirp_priv {
44*0e62ebd2SMark Johnston 	Slirp *slirp;		/* libslirp handle */
45*0e62ebd2SMark Johnston 	int sock;		/* data and control socket */
46*0e62ebd2SMark Johnston 	int wakeup[2];		/* used to wake up the pollfd thread */
47*0e62ebd2SMark Johnston 	struct pollfd *pollfds;
48*0e62ebd2SMark Johnston 	size_t npollfds;
49*0e62ebd2SMark Johnston 	size_t lastpollfd;
50*0e62ebd2SMark Johnston };
51*0e62ebd2SMark Johnston 
52*0e62ebd2SMark Johnston typedef int (*slirp_add_hostxfwd_p_t)(Slirp *,
53*0e62ebd2SMark Johnston     const struct sockaddr *, socklen_t, const struct sockaddr *, socklen_t,
54*0e62ebd2SMark Johnston     int);
55*0e62ebd2SMark Johnston typedef void (*slirp_cleanup_p_t)(Slirp *);
56*0e62ebd2SMark Johnston typedef void (*slirp_input_p_t)(Slirp *, const uint8_t *, int);
57*0e62ebd2SMark Johnston typedef Slirp *(*slirp_new_p_t)(const SlirpConfig *, const SlirpCb *, void *);
58*0e62ebd2SMark Johnston typedef void (*slirp_pollfds_fill_p_t)(Slirp *, uint32_t *timeout,
59*0e62ebd2SMark Johnston     SlirpAddPollCb, void *);
60*0e62ebd2SMark Johnston typedef void (*slirp_pollfds_poll_p_t)(Slirp *, int, SlirpGetREventsCb, void *);
61*0e62ebd2SMark Johnston 
62*0e62ebd2SMark Johnston /* Function pointer table, initialized by libslirp_init(). */
63*0e62ebd2SMark Johnston static slirp_add_hostxfwd_p_t slirp_add_hostxfwd_p;
64*0e62ebd2SMark Johnston static slirp_cleanup_p_t slirp_cleanup_p;
65*0e62ebd2SMark Johnston static slirp_input_p_t slirp_input_p;
66*0e62ebd2SMark Johnston static slirp_new_p_t slirp_new_p;
67*0e62ebd2SMark Johnston static slirp_pollfds_fill_p_t slirp_pollfds_fill_p;
68*0e62ebd2SMark Johnston static slirp_pollfds_poll_p_t slirp_pollfds_poll_p;
69*0e62ebd2SMark Johnston 
70*0e62ebd2SMark Johnston static int64_t
slirp_cb_clock_get_ns(void * param __unused)71*0e62ebd2SMark Johnston slirp_cb_clock_get_ns(void *param __unused)
72*0e62ebd2SMark Johnston {
73*0e62ebd2SMark Johnston 	struct timespec ts;
74*0e62ebd2SMark Johnston 	int error;
75*0e62ebd2SMark Johnston 
76*0e62ebd2SMark Johnston 	error = clock_gettime(CLOCK_MONOTONIC, &ts);
77*0e62ebd2SMark Johnston 	assert(error == 0);
78*0e62ebd2SMark Johnston 	return ((int64_t)(ts.tv_sec * 1000000000L + ts.tv_nsec));
79*0e62ebd2SMark Johnston }
80*0e62ebd2SMark Johnston 
81*0e62ebd2SMark Johnston static void
slirp_cb_notify(void * param)82*0e62ebd2SMark Johnston slirp_cb_notify(void *param)
83*0e62ebd2SMark Johnston {
84*0e62ebd2SMark Johnston 	struct slirp_priv *priv;
85*0e62ebd2SMark Johnston 
86*0e62ebd2SMark Johnston 	/* Wake up the poll thread.  We assume that priv->mtx is held here. */
87*0e62ebd2SMark Johnston 	priv = param;
88*0e62ebd2SMark Johnston 	(void)write(priv->wakeup[1], "M", 1);
89*0e62ebd2SMark Johnston }
90*0e62ebd2SMark Johnston 
91*0e62ebd2SMark Johnston static void
slirp_cb_register_poll_fd(int fd,void * param __unused)92*0e62ebd2SMark Johnston slirp_cb_register_poll_fd(int fd, void *param __unused)
93*0e62ebd2SMark Johnston {
94*0e62ebd2SMark Johnston 	const int one = 1;
95*0e62ebd2SMark Johnston 
96*0e62ebd2SMark Johnston 	(void)setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(int));
97*0e62ebd2SMark Johnston }
98*0e62ebd2SMark Johnston 
99*0e62ebd2SMark Johnston static ssize_t
slirp_cb_send_packet(const void * buf,size_t len,void * param)100*0e62ebd2SMark Johnston slirp_cb_send_packet(const void *buf, size_t len, void *param)
101*0e62ebd2SMark Johnston {
102*0e62ebd2SMark Johnston 	struct slirp_priv *priv;
103*0e62ebd2SMark Johnston 	ssize_t n;
104*0e62ebd2SMark Johnston 
105*0e62ebd2SMark Johnston 	priv = param;
106*0e62ebd2SMark Johnston 
107*0e62ebd2SMark Johnston 	assert(len <= SLIRP_MTU);
108*0e62ebd2SMark Johnston 	n = send(priv->sock, buf, len, MSG_EOR);
109*0e62ebd2SMark Johnston 	if (n < 0) {
110*0e62ebd2SMark Johnston 		warn("slirp_cb_send_packet: send");
111*0e62ebd2SMark Johnston 		return (n);
112*0e62ebd2SMark Johnston 	}
113*0e62ebd2SMark Johnston 	assert((size_t)n == len);
114*0e62ebd2SMark Johnston 
115*0e62ebd2SMark Johnston 	return (n);
116*0e62ebd2SMark Johnston }
117*0e62ebd2SMark Johnston 
118*0e62ebd2SMark Johnston static void
slirp_cb_unregister_poll_fd(int fd __unused,void * opaque __unused)119*0e62ebd2SMark Johnston slirp_cb_unregister_poll_fd(int fd __unused, void *opaque __unused)
120*0e62ebd2SMark Johnston {
121*0e62ebd2SMark Johnston }
122*0e62ebd2SMark Johnston 
123*0e62ebd2SMark Johnston /* Callbacks invoked from within libslirp. */
124*0e62ebd2SMark Johnston static const struct SlirpCb slirp_cbs = {
125*0e62ebd2SMark Johnston 	.clock_get_ns = slirp_cb_clock_get_ns,
126*0e62ebd2SMark Johnston 	.notify = slirp_cb_notify,
127*0e62ebd2SMark Johnston 	.register_poll_fd = slirp_cb_register_poll_fd,
128*0e62ebd2SMark Johnston 	.send_packet = slirp_cb_send_packet,
129*0e62ebd2SMark Johnston 	.unregister_poll_fd = slirp_cb_unregister_poll_fd,
130*0e62ebd2SMark Johnston };
131*0e62ebd2SMark Johnston 
132*0e62ebd2SMark Johnston static int
slirpev2pollev(int events)133*0e62ebd2SMark Johnston slirpev2pollev(int events)
134*0e62ebd2SMark Johnston {
135*0e62ebd2SMark Johnston 	int ret;
136*0e62ebd2SMark Johnston 
137*0e62ebd2SMark Johnston 	ret = 0;
138*0e62ebd2SMark Johnston 	if (events & SLIRP_POLL_IN)
139*0e62ebd2SMark Johnston 		ret |= POLLIN;
140*0e62ebd2SMark Johnston 	if (events & SLIRP_POLL_OUT)
141*0e62ebd2SMark Johnston 		ret |= POLLOUT;
142*0e62ebd2SMark Johnston 	if (events & SLIRP_POLL_PRI)
143*0e62ebd2SMark Johnston 		ret |= POLLPRI;
144*0e62ebd2SMark Johnston 	if (events & SLIRP_POLL_ERR)
145*0e62ebd2SMark Johnston 		ret |= POLLERR;
146*0e62ebd2SMark Johnston 	if (events & SLIRP_POLL_HUP)
147*0e62ebd2SMark Johnston 		ret |= POLLHUP;
148*0e62ebd2SMark Johnston 	return (ret);
149*0e62ebd2SMark Johnston }
150*0e62ebd2SMark Johnston 
151*0e62ebd2SMark Johnston static int
pollev2slirpev(int events)152*0e62ebd2SMark Johnston pollev2slirpev(int events)
153*0e62ebd2SMark Johnston {
154*0e62ebd2SMark Johnston 	int ret;
155*0e62ebd2SMark Johnston 
156*0e62ebd2SMark Johnston 	ret = 0;
157*0e62ebd2SMark Johnston 	if (events & POLLIN)
158*0e62ebd2SMark Johnston 		ret |= SLIRP_POLL_IN;
159*0e62ebd2SMark Johnston 	if (events & POLLOUT)
160*0e62ebd2SMark Johnston 		ret |= SLIRP_POLL_OUT;
161*0e62ebd2SMark Johnston 	if (events & POLLPRI)
162*0e62ebd2SMark Johnston 		ret |= SLIRP_POLL_PRI;
163*0e62ebd2SMark Johnston 	if (events & POLLERR)
164*0e62ebd2SMark Johnston 		ret |= SLIRP_POLL_ERR;
165*0e62ebd2SMark Johnston 	if (events & POLLHUP)
166*0e62ebd2SMark Johnston 		ret |= SLIRP_POLL_HUP;
167*0e62ebd2SMark Johnston 	return (ret);
168*0e62ebd2SMark Johnston }
169*0e62ebd2SMark Johnston 
170*0e62ebd2SMark Johnston static int
slirp_addpoll(struct slirp_priv * priv,int fd,int events)171*0e62ebd2SMark Johnston slirp_addpoll(struct slirp_priv *priv, int fd, int events)
172*0e62ebd2SMark Johnston {
173*0e62ebd2SMark Johnston 	struct pollfd *pollfd, *pollfds;
174*0e62ebd2SMark Johnston 	size_t i;
175*0e62ebd2SMark Johnston 
176*0e62ebd2SMark Johnston 	for (i = priv->lastpollfd + 1; i < priv->npollfds; i++)
177*0e62ebd2SMark Johnston 		if (priv->pollfds[i].fd == -1)
178*0e62ebd2SMark Johnston 			break;
179*0e62ebd2SMark Johnston 	if (i == priv->npollfds) {
180*0e62ebd2SMark Johnston 		const size_t POLLFD_GROW = 4;
181*0e62ebd2SMark Johnston 
182*0e62ebd2SMark Johnston 		priv->npollfds += POLLFD_GROW;
183*0e62ebd2SMark Johnston 		pollfds = realloc(priv->pollfds,
184*0e62ebd2SMark Johnston 		    sizeof(*pollfds) * priv->npollfds);
185*0e62ebd2SMark Johnston 		if (pollfds == NULL)
186*0e62ebd2SMark Johnston 			return (-1);
187*0e62ebd2SMark Johnston 		for (i = priv->npollfds - POLLFD_GROW; i < priv->npollfds; i++)
188*0e62ebd2SMark Johnston 			pollfds[i].fd = -1;
189*0e62ebd2SMark Johnston 		priv->pollfds = pollfds;
190*0e62ebd2SMark Johnston 
191*0e62ebd2SMark Johnston 		i = priv->npollfds - POLLFD_GROW;
192*0e62ebd2SMark Johnston 	}
193*0e62ebd2SMark Johnston 	pollfd = &priv->pollfds[i];
194*0e62ebd2SMark Johnston 	pollfd->fd = fd;
195*0e62ebd2SMark Johnston 	pollfd->events = slirpev2pollev(events);
196*0e62ebd2SMark Johnston 	pollfd->revents = 0;
197*0e62ebd2SMark Johnston 	priv->lastpollfd = i;
198*0e62ebd2SMark Johnston 
199*0e62ebd2SMark Johnston 	return ((int)i);
200*0e62ebd2SMark Johnston }
201*0e62ebd2SMark Johnston 
202*0e62ebd2SMark Johnston static int
slirp_addpoll_cb(int fd,int events,void * param)203*0e62ebd2SMark Johnston slirp_addpoll_cb(int fd, int events, void *param)
204*0e62ebd2SMark Johnston {
205*0e62ebd2SMark Johnston 	struct slirp_priv *priv;
206*0e62ebd2SMark Johnston 
207*0e62ebd2SMark Johnston 	priv = param;
208*0e62ebd2SMark Johnston 	return (slirp_addpoll(priv, fd, events));
209*0e62ebd2SMark Johnston }
210*0e62ebd2SMark Johnston 
211*0e62ebd2SMark Johnston static int
slirp_poll_revents(int idx,void * param)212*0e62ebd2SMark Johnston slirp_poll_revents(int idx, void *param)
213*0e62ebd2SMark Johnston {
214*0e62ebd2SMark Johnston 	struct slirp_priv *priv;
215*0e62ebd2SMark Johnston 	struct pollfd *pollfd;
216*0e62ebd2SMark Johnston 	short revents;
217*0e62ebd2SMark Johnston 
218*0e62ebd2SMark Johnston 	priv = param;
219*0e62ebd2SMark Johnston 	assert(idx >= 0);
220*0e62ebd2SMark Johnston 	assert((unsigned int)idx < priv->npollfds);
221*0e62ebd2SMark Johnston 	pollfd = &priv->pollfds[idx];
222*0e62ebd2SMark Johnston 	assert(pollfd->fd != -1);
223*0e62ebd2SMark Johnston 
224*0e62ebd2SMark Johnston 	/* The kernel may report POLLHUP even if we didn't ask for it. */
225*0e62ebd2SMark Johnston 	revents = pollfd->revents;
226*0e62ebd2SMark Johnston 	if ((pollfd->events & POLLHUP) == 0)
227*0e62ebd2SMark Johnston 		revents &= ~POLLHUP;
228*0e62ebd2SMark Johnston 	return (pollev2slirpev(revents));
229*0e62ebd2SMark Johnston }
230*0e62ebd2SMark Johnston 
231*0e62ebd2SMark Johnston /*
232*0e62ebd2SMark Johnston  * Main loop.  Poll libslirp's descriptors plus a couple of our own.
233*0e62ebd2SMark Johnston  */
234*0e62ebd2SMark Johnston static void
slirp_pollfd_loop(struct slirp_priv * priv)235*0e62ebd2SMark Johnston slirp_pollfd_loop(struct slirp_priv *priv)
236*0e62ebd2SMark Johnston {
237*0e62ebd2SMark Johnston 	struct pollfd *pollfds;
238*0e62ebd2SMark Johnston 	size_t npollfds;
239*0e62ebd2SMark Johnston 	uint32_t timeout;
240*0e62ebd2SMark Johnston 	int error;
241*0e62ebd2SMark Johnston 
242*0e62ebd2SMark Johnston 	for (;;) {
243*0e62ebd2SMark Johnston 		int input, wakeup;
244*0e62ebd2SMark Johnston 
245*0e62ebd2SMark Johnston 		for (size_t i = 0; i < priv->npollfds; i++)
246*0e62ebd2SMark Johnston 			priv->pollfds[i].fd = -1;
247*0e62ebd2SMark Johnston 		priv->lastpollfd = -1;
248*0e62ebd2SMark Johnston 
249*0e62ebd2SMark Johnston 		/* Register for notifications from slirp_cb_notify(). */
250*0e62ebd2SMark Johnston 		wakeup = slirp_addpoll(priv, priv->wakeup[0], POLLIN);
251*0e62ebd2SMark Johnston 		/* Register for input from our parent process. */
252*0e62ebd2SMark Johnston 		input = slirp_addpoll(priv, priv->sock, POLLIN | POLLRDHUP);
253*0e62ebd2SMark Johnston 
254*0e62ebd2SMark Johnston 		timeout = UINT32_MAX;
255*0e62ebd2SMark Johnston 		slirp_pollfds_fill_p(priv->slirp, &timeout, slirp_addpoll_cb,
256*0e62ebd2SMark Johnston 		    priv);
257*0e62ebd2SMark Johnston 
258*0e62ebd2SMark Johnston 		pollfds = priv->pollfds;
259*0e62ebd2SMark Johnston 		npollfds = priv->npollfds;
260*0e62ebd2SMark Johnston 		error = poll(pollfds, npollfds, timeout);
261*0e62ebd2SMark Johnston 		if (error == -1 && errno != EINTR)
262*0e62ebd2SMark Johnston 			err(1, "poll");
263*0e62ebd2SMark Johnston 		slirp_pollfds_poll_p(priv->slirp, error == -1,
264*0e62ebd2SMark Johnston 		    slirp_poll_revents, priv);
265*0e62ebd2SMark Johnston 
266*0e62ebd2SMark Johnston 		/*
267*0e62ebd2SMark Johnston 		 * If we were woken up by the notify callback, mask the
268*0e62ebd2SMark Johnston 		 * interrupt.
269*0e62ebd2SMark Johnston 		 */
270*0e62ebd2SMark Johnston 		if ((pollfds[wakeup].revents & POLLIN) != 0) {
271*0e62ebd2SMark Johnston 			ssize_t n;
272*0e62ebd2SMark Johnston 
273*0e62ebd2SMark Johnston 			do {
274*0e62ebd2SMark Johnston 				uint8_t b;
275*0e62ebd2SMark Johnston 
276*0e62ebd2SMark Johnston 				n = read(priv->wakeup[0], &b, 1);
277*0e62ebd2SMark Johnston 			} while (n == 1);
278*0e62ebd2SMark Johnston 			if (n != -1 || errno != EAGAIN)
279*0e62ebd2SMark Johnston 				err(1, "read");
280*0e62ebd2SMark Johnston 		}
281*0e62ebd2SMark Johnston 
282*0e62ebd2SMark Johnston 		/*
283*0e62ebd2SMark Johnston 		 * If new packets arrived from our parent, feed them to
284*0e62ebd2SMark Johnston 		 * libslirp.
285*0e62ebd2SMark Johnston 		 */
286*0e62ebd2SMark Johnston 		if ((pollfds[input].revents & (POLLHUP | POLLRDHUP)) != 0)
287*0e62ebd2SMark Johnston 			errx(1, "parent process closed connection");
288*0e62ebd2SMark Johnston 		if ((pollfds[input].revents & POLLIN) != 0) {
289*0e62ebd2SMark Johnston 			ssize_t n;
290*0e62ebd2SMark Johnston 
291*0e62ebd2SMark Johnston 			do {
292*0e62ebd2SMark Johnston 				uint8_t buf[SLIRP_MTU];
293*0e62ebd2SMark Johnston 
294*0e62ebd2SMark Johnston 				n = recv(priv->sock, buf, sizeof(buf),
295*0e62ebd2SMark Johnston 				    MSG_DONTWAIT);
296*0e62ebd2SMark Johnston 				if (n < 0) {
297*0e62ebd2SMark Johnston 					if (errno == EWOULDBLOCK)
298*0e62ebd2SMark Johnston 						break;
299*0e62ebd2SMark Johnston 					err(1, "recv");
300*0e62ebd2SMark Johnston 				}
301*0e62ebd2SMark Johnston 				slirp_input_p(priv->slirp, buf, (int)n);
302*0e62ebd2SMark Johnston 			} while (n >= 0);
303*0e62ebd2SMark Johnston 		}
304*0e62ebd2SMark Johnston 	}
305*0e62ebd2SMark Johnston }
306*0e62ebd2SMark Johnston 
307*0e62ebd2SMark Johnston static int
parse_addr(char * addr,struct sockaddr_in * sinp)308*0e62ebd2SMark Johnston parse_addr(char *addr, struct sockaddr_in *sinp)
309*0e62ebd2SMark Johnston {
310*0e62ebd2SMark Johnston 	char *port;
311*0e62ebd2SMark Johnston 	int error, porti;
312*0e62ebd2SMark Johnston 
313*0e62ebd2SMark Johnston 	memset(sinp, 0, sizeof(*sinp));
314*0e62ebd2SMark Johnston 	sinp->sin_family = AF_INET;
315*0e62ebd2SMark Johnston 	sinp->sin_len = sizeof(struct sockaddr_in);
316*0e62ebd2SMark Johnston 
317*0e62ebd2SMark Johnston 	port = strchr(addr, ':');
318*0e62ebd2SMark Johnston 	if (port == NULL)
319*0e62ebd2SMark Johnston 		return (EINVAL);
320*0e62ebd2SMark Johnston 	*port++ = '\0';
321*0e62ebd2SMark Johnston 
322*0e62ebd2SMark Johnston 	if (strlen(addr) > 0) {
323*0e62ebd2SMark Johnston 		error = inet_pton(AF_INET, addr, &sinp->sin_addr);
324*0e62ebd2SMark Johnston 		if (error != 1)
325*0e62ebd2SMark Johnston 			return (error == 0 ? EPFNOSUPPORT : errno);
326*0e62ebd2SMark Johnston 	} else {
327*0e62ebd2SMark Johnston 		sinp->sin_addr.s_addr = htonl(INADDR_ANY);
328*0e62ebd2SMark Johnston 	}
329*0e62ebd2SMark Johnston 
330*0e62ebd2SMark Johnston 	porti = strlen(port) > 0 ? atoi(port) : 0;
331*0e62ebd2SMark Johnston 	if (porti < 0 || porti > UINT16_MAX)
332*0e62ebd2SMark Johnston 		return (EINVAL);
333*0e62ebd2SMark Johnston 	sinp->sin_port = htons(porti);
334*0e62ebd2SMark Johnston 
335*0e62ebd2SMark Johnston 	return (0);
336*0e62ebd2SMark Johnston }
337*0e62ebd2SMark Johnston 
338*0e62ebd2SMark Johnston static int
parse_hostfwd_rule(const char * descr,int * is_udp,struct sockaddr * hostaddr,struct sockaddr * guestaddr)339*0e62ebd2SMark Johnston parse_hostfwd_rule(const char *descr, int *is_udp, struct sockaddr *hostaddr,
340*0e62ebd2SMark Johnston     struct sockaddr *guestaddr)
341*0e62ebd2SMark Johnston {
342*0e62ebd2SMark Johnston 	struct sockaddr_in *hostaddrp, *guestaddrp;
343*0e62ebd2SMark Johnston 	const char *proto;
344*0e62ebd2SMark Johnston 	char *p, *host, *guest;
345*0e62ebd2SMark Johnston 	int error;
346*0e62ebd2SMark Johnston 
347*0e62ebd2SMark Johnston 	error = 0;
348*0e62ebd2SMark Johnston 	*is_udp = 0;
349*0e62ebd2SMark Johnston 
350*0e62ebd2SMark Johnston 	p = strdup(descr);
351*0e62ebd2SMark Johnston 	if (p == NULL)
352*0e62ebd2SMark Johnston 		return (ENOMEM);
353*0e62ebd2SMark Johnston 
354*0e62ebd2SMark Johnston 	host = strchr(p, ':');
355*0e62ebd2SMark Johnston 	if (host == NULL) {
356*0e62ebd2SMark Johnston 		error = EINVAL;
357*0e62ebd2SMark Johnston 		goto out;
358*0e62ebd2SMark Johnston 	}
359*0e62ebd2SMark Johnston 	*host++ = '\0';
360*0e62ebd2SMark Johnston 
361*0e62ebd2SMark Johnston 	proto = p;
362*0e62ebd2SMark Johnston 	*is_udp = strcmp(proto, "udp") == 0;
363*0e62ebd2SMark Johnston 
364*0e62ebd2SMark Johnston 	guest = strchr(host, '-');
365*0e62ebd2SMark Johnston 	if (guest == NULL) {
366*0e62ebd2SMark Johnston 		error = EINVAL;
367*0e62ebd2SMark Johnston 		goto out;
368*0e62ebd2SMark Johnston 	}
369*0e62ebd2SMark Johnston 	*guest++ = '\0';
370*0e62ebd2SMark Johnston 
371*0e62ebd2SMark Johnston 	hostaddrp = (struct sockaddr_in *)(void *)hostaddr;
372*0e62ebd2SMark Johnston 	error = parse_addr(host, hostaddrp);
373*0e62ebd2SMark Johnston 	if (error != 0)
374*0e62ebd2SMark Johnston 		goto out;
375*0e62ebd2SMark Johnston 
376*0e62ebd2SMark Johnston 	guestaddrp = (struct sockaddr_in *)(void *)guestaddr;
377*0e62ebd2SMark Johnston 	error = parse_addr(guest, guestaddrp);
378*0e62ebd2SMark Johnston 	if (error != 0)
379*0e62ebd2SMark Johnston 		goto out;
380*0e62ebd2SMark Johnston 
381*0e62ebd2SMark Johnston out:
382*0e62ebd2SMark Johnston 	free(p);
383*0e62ebd2SMark Johnston 	return (error);
384*0e62ebd2SMark Johnston }
385*0e62ebd2SMark Johnston 
386*0e62ebd2SMark Johnston static void
config_one_hostfwd(Slirp * slirp,const char * rule)387*0e62ebd2SMark Johnston config_one_hostfwd(Slirp *slirp, const char *rule)
388*0e62ebd2SMark Johnston {
389*0e62ebd2SMark Johnston 	struct sockaddr hostaddr, guestaddr;
390*0e62ebd2SMark Johnston 	int error, is_udp;
391*0e62ebd2SMark Johnston 
392*0e62ebd2SMark Johnston 	error = parse_hostfwd_rule(rule, &is_udp, &hostaddr, &guestaddr);
393*0e62ebd2SMark Johnston 	if (error != 0)
394*0e62ebd2SMark Johnston 		errx(1, "unable to parse hostfwd rule '%s': %s", rule,
395*0e62ebd2SMark Johnston 		    strerror(error));
396*0e62ebd2SMark Johnston 
397*0e62ebd2SMark Johnston 	error = slirp_add_hostxfwd_p(slirp, &hostaddr, hostaddr.sa_len,
398*0e62ebd2SMark Johnston 	    &guestaddr, guestaddr.sa_len, is_udp ? SLIRP_HOSTFWD_UDP : 0);
399*0e62ebd2SMark Johnston 	if (error != 0)
400*0e62ebd2SMark Johnston 		errx(1, "Unable to add hostfwd rule '%s': %s", rule,
401*0e62ebd2SMark Johnston 		    strerror(errno));
402*0e62ebd2SMark Johnston }
403*0e62ebd2SMark Johnston 
404*0e62ebd2SMark Johnston /*
405*0e62ebd2SMark Johnston  * Drop privileges to the "nobody" user.  Ideally we'd chroot to somewhere like
406*0e62ebd2SMark Johnston  * /var/empty but libslirp might need to access /etc/resolv.conf.
407*0e62ebd2SMark Johnston  */
408*0e62ebd2SMark Johnston static void
drop_privs(void)409*0e62ebd2SMark Johnston drop_privs(void)
410*0e62ebd2SMark Johnston {
411*0e62ebd2SMark Johnston 	struct passwd *pw;
412*0e62ebd2SMark Johnston 
413*0e62ebd2SMark Johnston 	if (geteuid() != 0)
414*0e62ebd2SMark Johnston 		return;
415*0e62ebd2SMark Johnston 
416*0e62ebd2SMark Johnston 	pw = getpwnam("nobody");
417*0e62ebd2SMark Johnston 	if (pw == NULL)
418*0e62ebd2SMark Johnston 		err(1, "getpwnam(nobody) failed");
419*0e62ebd2SMark Johnston 	if (initgroups(pw->pw_name, pw->pw_gid) != 0)
420*0e62ebd2SMark Johnston 		err(1, "initgroups");
421*0e62ebd2SMark Johnston 	if (setgid(pw->pw_gid) != 0)
422*0e62ebd2SMark Johnston 		err(1, "setgid");
423*0e62ebd2SMark Johnston 	if (setuid(pw->pw_uid) != 0)
424*0e62ebd2SMark Johnston 		err(1, "setuid");
425*0e62ebd2SMark Johnston }
426*0e62ebd2SMark Johnston 
427*0e62ebd2SMark Johnston static void
libslirp_init(void)428*0e62ebd2SMark Johnston libslirp_init(void)
429*0e62ebd2SMark Johnston {
430*0e62ebd2SMark Johnston 	void *handle;
431*0e62ebd2SMark Johnston 
432*0e62ebd2SMark Johnston 	handle = dlopen("libslirp.so.0", RTLD_LAZY);
433*0e62ebd2SMark Johnston 	if (handle == NULL)
434*0e62ebd2SMark Johnston 		errx(1, "unable to open libslirp.so.0: %s", dlerror());
435*0e62ebd2SMark Johnston 
436*0e62ebd2SMark Johnston #define IMPORT_SYM(sym) do {					\
437*0e62ebd2SMark Johnston 	sym##_p = (sym##_p_t)dlsym(handle, #sym);		\
438*0e62ebd2SMark Johnston 	if (sym##_p == NULL)					\
439*0e62ebd2SMark Johnston 		errx(1, "failed to resolve %s", #sym);		\
440*0e62ebd2SMark Johnston } while (0)
441*0e62ebd2SMark Johnston 	IMPORT_SYM(slirp_add_hostxfwd);
442*0e62ebd2SMark Johnston 	IMPORT_SYM(slirp_cleanup);
443*0e62ebd2SMark Johnston 	IMPORT_SYM(slirp_input);
444*0e62ebd2SMark Johnston 	IMPORT_SYM(slirp_new);
445*0e62ebd2SMark Johnston 	IMPORT_SYM(slirp_pollfds_fill);
446*0e62ebd2SMark Johnston 	IMPORT_SYM(slirp_pollfds_poll);
447*0e62ebd2SMark Johnston #undef IMPORT_SYM
448*0e62ebd2SMark Johnston }
449*0e62ebd2SMark Johnston 
450*0e62ebd2SMark Johnston static void
usage(void)451*0e62ebd2SMark Johnston usage(void)
452*0e62ebd2SMark Johnston {
453*0e62ebd2SMark Johnston 	fprintf(stderr, "Usage: slirp-helper -S <socket>\n");
454*0e62ebd2SMark Johnston 	exit(1);
455*0e62ebd2SMark Johnston }
456*0e62ebd2SMark Johnston 
457*0e62ebd2SMark Johnston int
main(int argc,char ** argv)458*0e62ebd2SMark Johnston main(int argc, char **argv)
459*0e62ebd2SMark Johnston {
460*0e62ebd2SMark Johnston 	struct slirp_priv priv;
461*0e62ebd2SMark Johnston 	SlirpConfig slirpconfig;
462*0e62ebd2SMark Johnston 	Slirp *slirp;
463*0e62ebd2SMark Johnston 	nvlist_t *config;
464*0e62ebd2SMark Johnston 	const char *hostfwd, *vmname;
465*0e62ebd2SMark Johnston 	int ch, fd, sd;
466*0e62ebd2SMark Johnston 	bool restricted;
467*0e62ebd2SMark Johnston 
468*0e62ebd2SMark Johnston 	sd = -1;
469*0e62ebd2SMark Johnston 	while ((ch = getopt(argc, argv, "S:")) != -1) {
470*0e62ebd2SMark Johnston 		switch (ch) {
471*0e62ebd2SMark Johnston 		case 'S':
472*0e62ebd2SMark Johnston 			sd = atoi(optarg);
473*0e62ebd2SMark Johnston 			if (fcntl(sd, F_GETFD) == -1)
474*0e62ebd2SMark Johnston 				err(1, "invalid socket %s", optarg);
475*0e62ebd2SMark Johnston 			break;
476*0e62ebd2SMark Johnston 		default:
477*0e62ebd2SMark Johnston 			usage();
478*0e62ebd2SMark Johnston 			/* NOTREACHED */
479*0e62ebd2SMark Johnston 		}
480*0e62ebd2SMark Johnston 	}
481*0e62ebd2SMark Johnston 	argc -= optind;
482*0e62ebd2SMark Johnston 	argv += optind;
483*0e62ebd2SMark Johnston 
484*0e62ebd2SMark Johnston 	if (sd == -1)
485*0e62ebd2SMark Johnston 		usage();
486*0e62ebd2SMark Johnston 
487*0e62ebd2SMark Johnston 	/*
488*0e62ebd2SMark Johnston 	 * Clean the fd space: point stdio to /dev/null and keep our socket.
489*0e62ebd2SMark Johnston 	 */
490*0e62ebd2SMark Johnston 	fd = open("/dev/null", O_RDWR);
491*0e62ebd2SMark Johnston 	if (fd == -1)
492*0e62ebd2SMark Johnston 		err(1, "open(/dev/null)");
493*0e62ebd2SMark Johnston 	if (dup2(fd, STDIN_FILENO) == -1)
494*0e62ebd2SMark Johnston 		err(1, "dup2(stdin)");
495*0e62ebd2SMark Johnston 	if (dup2(fd, STDOUT_FILENO) == -1)
496*0e62ebd2SMark Johnston 		err(1, "dup2(stdout)");
497*0e62ebd2SMark Johnston 	if (dup2(fd, STDERR_FILENO) == -1)
498*0e62ebd2SMark Johnston 		err(1, "dup2(stderr)");
499*0e62ebd2SMark Johnston 	if (dup2(sd, 3) == -1)
500*0e62ebd2SMark Johnston 		err(1, "dup2(slirp socket)");
501*0e62ebd2SMark Johnston 	sd = 3;
502*0e62ebd2SMark Johnston 	closefrom(sd + 1);
503*0e62ebd2SMark Johnston 
504*0e62ebd2SMark Johnston 	memset(&priv, 0, sizeof(priv));
505*0e62ebd2SMark Johnston 	priv.sock = sd;
506*0e62ebd2SMark Johnston 	if (pipe2(priv.wakeup, O_CLOEXEC | O_NONBLOCK) != 0)
507*0e62ebd2SMark Johnston 		err(1, "pipe2");
508*0e62ebd2SMark Johnston 
509*0e62ebd2SMark Johnston 	/*
510*0e62ebd2SMark Johnston 	 * Apply the configuration we received from bhyve.
511*0e62ebd2SMark Johnston 	 */
512*0e62ebd2SMark Johnston 	config = nvlist_recv(sd, 0);
513*0e62ebd2SMark Johnston 	if (config == NULL)
514*0e62ebd2SMark Johnston 		err(1, "nvlist_recv");
515*0e62ebd2SMark Johnston 	vmname = get_config_value_node(config, "vmname");
516*0e62ebd2SMark Johnston 	if (vmname != NULL)
517*0e62ebd2SMark Johnston 		setproctitle("%s", vmname);
518*0e62ebd2SMark Johnston 	restricted = !get_config_bool_node_default(config, "open", false);
519*0e62ebd2SMark Johnston 
520*0e62ebd2SMark Johnston 	slirpconfig = (SlirpConfig){
521*0e62ebd2SMark Johnston 		.version = 4,
522*0e62ebd2SMark Johnston 		.if_mtu = SLIRP_MTU,
523*0e62ebd2SMark Johnston 		.restricted = restricted,
524*0e62ebd2SMark Johnston 		.in_enabled = true,
525*0e62ebd2SMark Johnston 		.vnetwork.s_addr = htonl(0x0a000200),	/* 10.0.2.0/24 */
526*0e62ebd2SMark Johnston 		.vnetmask.s_addr = htonl(0xffffff00),	/* 255.255.255.0 */
527*0e62ebd2SMark Johnston 		.vdhcp_start.s_addr = htonl(0x0a00020f),/* 10.0.2.15 */
528*0e62ebd2SMark Johnston 		.vhost.s_addr = htonl(0x0a000202),	/* 10.0.2.2 */
529*0e62ebd2SMark Johnston 		.vnameserver.s_addr = htonl(0x0a000203),/* 10.0.2.3 */
530*0e62ebd2SMark Johnston 		.enable_emu = false,
531*0e62ebd2SMark Johnston 	};
532*0e62ebd2SMark Johnston 	libslirp_init();
533*0e62ebd2SMark Johnston 	slirp = slirp_new_p(&slirpconfig, &slirp_cbs, &priv);
534*0e62ebd2SMark Johnston 
535*0e62ebd2SMark Johnston 	hostfwd = get_config_value_node(config, "hostfwd");
536*0e62ebd2SMark Johnston 	if (hostfwd != NULL) {
537*0e62ebd2SMark Johnston 		char *rules, *tofree;
538*0e62ebd2SMark Johnston 		const char *rule;
539*0e62ebd2SMark Johnston 
540*0e62ebd2SMark Johnston 		tofree = rules = strdup(hostfwd);
541*0e62ebd2SMark Johnston 		if (rules == NULL)
542*0e62ebd2SMark Johnston 			err(1, "strdup");
543*0e62ebd2SMark Johnston 		while ((rule = strsep(&rules, ";")) != NULL)
544*0e62ebd2SMark Johnston 			config_one_hostfwd(slirp, rule);
545*0e62ebd2SMark Johnston 		free(tofree);
546*0e62ebd2SMark Johnston 	}
547*0e62ebd2SMark Johnston 
548*0e62ebd2SMark Johnston 	priv.slirp = slirp;
549*0e62ebd2SMark Johnston 
550*0e62ebd2SMark Johnston 	/*
551*0e62ebd2SMark Johnston 	 * In restricted mode, we can enter a Capsicum sandbox without losing
552*0e62ebd2SMark Johnston 	 * functionality.
553*0e62ebd2SMark Johnston 	 */
554*0e62ebd2SMark Johnston 	if (restricted && caph_enter() != 0)
555*0e62ebd2SMark Johnston 		err(1, "caph_enter");
556*0e62ebd2SMark Johnston 
557*0e62ebd2SMark Johnston 	/*
558*0e62ebd2SMark Johnston 	 * Drop root privileges if we have them.
559*0e62ebd2SMark Johnston 	 */
560*0e62ebd2SMark Johnston 	drop_privs();
561*0e62ebd2SMark Johnston 
562*0e62ebd2SMark Johnston 	/*
563*0e62ebd2SMark Johnston 	 * Enter our main loop.  If bhyve goes away, we should observe a hangup
564*0e62ebd2SMark Johnston 	 * on the socket and exit.
565*0e62ebd2SMark Johnston 	 */
566*0e62ebd2SMark Johnston 	slirp_pollfd_loop(&priv);
567*0e62ebd2SMark Johnston 	/* NOTREACHED */
568*0e62ebd2SMark Johnston 
569*0e62ebd2SMark Johnston 	return (1);
570*0e62ebd2SMark Johnston }
571