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 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 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 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 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 191 slirp_cb_notify(void *param __unused) 192 { 193 } 194 195 static void 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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 638 slirp_get_cap(struct net_backend *be __unused) 639 { 640 return (0); 641 } 642 643 static int 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