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