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