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 void 88 checked_close(int *fdp) 89 { 90 int error; 91 92 if (*fdp != -1) { 93 error = close(*fdp); 94 assert(error == 0); 95 *fdp = -1; 96 } 97 } 98 99 static int 100 slirp_init_once(void) 101 { 102 static void *handle = NULL; 103 104 if (handle != NULL) 105 return (0); 106 handle = dlopen("libslirp.so.0", RTLD_LAZY); 107 if (handle == NULL) { 108 EPRINTLN("Unable to open libslirp.so.0: %s", dlerror()); 109 return (-1); 110 } 111 112 #define IMPORT_SYM(sym) do { \ 113 sym##_p = (sym##_p_t)dlsym(handle, #sym); \ 114 if (sym##_p == NULL) { \ 115 EPRINTLN("failed to resolve %s", #sym); \ 116 goto err; \ 117 } \ 118 } while (0) 119 IMPORT_SYM(slirp_add_hostxfwd); 120 IMPORT_SYM(slirp_cleanup); 121 IMPORT_SYM(slirp_input); 122 IMPORT_SYM(slirp_new); 123 IMPORT_SYM(slirp_pollfds_fill); 124 IMPORT_SYM(slirp_pollfds_poll); 125 #undef IMPORT_SYM 126 127 /* 128 * libslirp uses glib, which uses tzdata to format log messages. Help 129 * it out. 130 * 131 * XXX-MJ glib will also look for charset files, not sure what we can do 132 * about that... 133 */ 134 caph_cache_tzdata(); 135 136 return (0); 137 138 err: 139 dlclose(handle); 140 handle = NULL; 141 return (-1); 142 } 143 144 struct slirp_priv { 145 Slirp *slirp; 146 147 #define SLIRP_MTU 2048 148 struct mevent *mevp; 149 int pipe[2]; /* used to buffer data sent to the guest */ 150 int wakeup[2]; /* used to wake up the pollfd thread */ 151 152 pthread_t pollfd_td; 153 struct pollfd *pollfds; 154 size_t npollfds; 155 156 /* Serializes libslirp calls. */ 157 pthread_mutex_t mtx; 158 }; 159 160 static void 161 slirp_priv_init(struct slirp_priv *priv) 162 { 163 int error; 164 165 memset(priv, 0, sizeof(*priv)); 166 priv->pipe[0] = priv->pipe[1] = -1; 167 priv->wakeup[0] = priv->wakeup[1] = -1; 168 error = pthread_mutex_init(&priv->mtx, NULL); 169 assert(error == 0); 170 } 171 172 static void 173 slirp_priv_cleanup(struct slirp_priv *priv) 174 { 175 int error; 176 177 checked_close(&priv->pipe[0]); 178 checked_close(&priv->pipe[1]); 179 checked_close(&priv->wakeup[0]); 180 checked_close(&priv->wakeup[1]); 181 if (priv->mevp) 182 mevent_delete(priv->mevp); 183 if (priv->slirp != NULL) 184 slirp_cleanup_p(priv->slirp); 185 error = pthread_mutex_destroy(&priv->mtx); 186 assert(error == 0); 187 } 188 189 static int64_t 190 slirp_cb_clock_get_ns(void *param __unused) 191 { 192 struct timespec ts; 193 int error; 194 195 error = clock_gettime(CLOCK_MONOTONIC, &ts); 196 assert(error == 0); 197 return ((int64_t)(ts.tv_sec * 1000000000L + ts.tv_nsec)); 198 } 199 200 static void 201 slirp_cb_notify(void *param) 202 { 203 struct slirp_priv *priv; 204 205 /* Wake up the poll thread. We assume that priv->mtx is held here. */ 206 priv = param; 207 (void)write(priv->wakeup[1], "M", 1); 208 } 209 210 static void 211 slirp_cb_register_poll_fd(int fd, void *param __unused) 212 { 213 const int one = 1; 214 215 (void)setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(int)); 216 } 217 218 static ssize_t 219 slirp_cb_send_packet(const void *buf, size_t len, void *param) 220 { 221 struct slirp_priv *priv; 222 ssize_t n; 223 224 priv = param; 225 226 assert(len <= SLIRP_MTU); 227 n = send(priv->pipe[1], buf, len, 0); 228 if (n < 0) { 229 EPRINTLN("slirp_cb_send_packet: send: %s", strerror(errno)); 230 return (n); 231 } 232 assert((size_t)n == len); 233 234 return (n); 235 } 236 237 static void 238 slirp_cb_unregister_poll_fd(int fd __unused, void *opaque __unused) 239 { 240 } 241 242 /* Callbacks invoked from within libslirp. */ 243 static const struct SlirpCb slirp_cbs = { 244 .clock_get_ns = slirp_cb_clock_get_ns, 245 .notify = slirp_cb_notify, 246 .register_poll_fd = slirp_cb_register_poll_fd, 247 .send_packet = slirp_cb_send_packet, 248 .unregister_poll_fd = slirp_cb_unregister_poll_fd, 249 }; 250 251 static int 252 slirpev2pollev(int events) 253 { 254 int ret; 255 256 ret = 0; 257 if (events & SLIRP_POLL_IN) 258 ret |= POLLIN; 259 if (events & SLIRP_POLL_OUT) 260 ret |= POLLOUT; 261 if (events & SLIRP_POLL_PRI) 262 ret |= POLLPRI; 263 if (events & SLIRP_POLL_ERR) 264 ret |= POLLERR; 265 if (events & SLIRP_POLL_HUP) 266 ret |= POLLHUP; 267 return (ret); 268 } 269 270 static int 271 pollev2slirpev(int events) 272 { 273 int ret; 274 275 ret = 0; 276 if (events & POLLIN) 277 ret |= SLIRP_POLL_IN; 278 if (events & POLLOUT) 279 ret |= SLIRP_POLL_OUT; 280 if (events & POLLPRI) 281 ret |= SLIRP_POLL_PRI; 282 if (events & POLLERR) 283 ret |= SLIRP_POLL_ERR; 284 if (events & POLLHUP) 285 ret |= SLIRP_POLL_HUP; 286 return (ret); 287 } 288 289 static int 290 slirp_addpoll_cb(int fd, int events, void *param) 291 { 292 struct slirp_priv *priv; 293 struct pollfd *pollfd, *pollfds; 294 size_t i; 295 296 priv = param; 297 298 for (i = 0; i < priv->npollfds; i++) 299 if (priv->pollfds[i].fd == -1) 300 break; 301 if (i == priv->npollfds) { 302 const size_t POLLFD_GROW = 4; 303 304 priv->npollfds += POLLFD_GROW; 305 pollfds = realloc(priv->pollfds, 306 sizeof(*pollfds) * priv->npollfds); 307 if (pollfds == NULL) 308 return (-1); 309 for (i = priv->npollfds - POLLFD_GROW; i < priv->npollfds; i++) 310 pollfds[i].fd = -1; 311 priv->pollfds = pollfds; 312 313 i = priv->npollfds - POLLFD_GROW; 314 } 315 pollfd = &priv->pollfds[i]; 316 pollfd->fd = fd; 317 pollfd->events = slirpev2pollev(events); 318 pollfd->revents = 0; 319 320 return ((int)i); 321 } 322 323 static int 324 slirp_poll_revents(int idx, void *param) 325 { 326 struct slirp_priv *priv; 327 struct pollfd *pollfd; 328 short revents; 329 330 priv = param; 331 assert(idx >= 0); 332 assert((unsigned int)idx < priv->npollfds); 333 pollfd = &priv->pollfds[idx]; 334 assert(pollfd->fd != -1); 335 336 /* The kernel may report POLLHUP even if we didn't ask for it. */ 337 revents = pollfd->revents; 338 if ((pollfd->events & POLLHUP) == 0) 339 revents &= ~POLLHUP; 340 return (pollev2slirpev(revents)); 341 } 342 343 static void * 344 slirp_pollfd_td_loop(void *param) 345 { 346 struct slirp_priv *priv; 347 struct pollfd *pollfds; 348 size_t npollfds; 349 uint32_t timeout; 350 int error; 351 352 pthread_set_name_np(pthread_self(), "slirp pollfd"); 353 priv = param; 354 355 pthread_mutex_lock(&priv->mtx); 356 for (;;) { 357 int wakeup; 358 359 for (size_t i = 0; i < priv->npollfds; i++) 360 priv->pollfds[i].fd = -1; 361 362 /* Register for notifications from slirp_cb_notify(). */ 363 wakeup = slirp_addpoll_cb(priv->wakeup[0], POLLIN, priv); 364 365 timeout = UINT32_MAX; 366 slirp_pollfds_fill_p(priv->slirp, &timeout, slirp_addpoll_cb, 367 priv); 368 369 pollfds = priv->pollfds; 370 npollfds = priv->npollfds; 371 pthread_mutex_unlock(&priv->mtx); 372 error = poll(pollfds, npollfds, timeout); 373 if (error == -1 && errno != EINTR) { 374 EPRINTLN("poll: %s", strerror(errno)); 375 exit(1); 376 } 377 pthread_mutex_lock(&priv->mtx); 378 slirp_pollfds_poll_p(priv->slirp, error == -1, 379 slirp_poll_revents, priv); 380 381 /* 382 * If we were woken up by the notify callback, mask the 383 * interrupt. 384 */ 385 if ((pollfds[wakeup].revents & POLLIN) != 0) { 386 ssize_t n; 387 388 do { 389 uint8_t b; 390 391 n = read(priv->wakeup[0], &b, 1); 392 } while (n == 1); 393 if (n != -1 || errno != EAGAIN) { 394 EPRINTLN("read(wakeup): %s", strerror(errno)); 395 exit(1); 396 } 397 } 398 } 399 } 400 401 static int 402 parse_addr(char *addr, struct sockaddr_in *sinp) 403 { 404 char *port; 405 int error, porti; 406 407 memset(sinp, 0, sizeof(*sinp)); 408 sinp->sin_family = AF_INET; 409 sinp->sin_len = sizeof(struct sockaddr_in); 410 411 port = strchr(addr, ':'); 412 if (port == NULL) 413 return (EINVAL); 414 *port++ = '\0'; 415 416 if (strlen(addr) > 0) { 417 error = inet_pton(AF_INET, addr, &sinp->sin_addr); 418 if (error != 1) 419 return (error == 0 ? EPFNOSUPPORT : errno); 420 } else { 421 sinp->sin_addr.s_addr = htonl(INADDR_ANY); 422 } 423 424 porti = strlen(port) > 0 ? atoi(port) : 0; 425 if (porti < 0 || porti > UINT16_MAX) 426 return (EINVAL); 427 sinp->sin_port = htons(porti); 428 429 return (0); 430 } 431 432 static int 433 parse_hostfwd_rule(const char *descr, int *is_udp, struct sockaddr *hostaddr, 434 struct sockaddr *guestaddr) 435 { 436 struct sockaddr_in *hostaddrp, *guestaddrp; 437 const char *proto; 438 char *p, *host, *guest; 439 int error; 440 441 error = 0; 442 *is_udp = 0; 443 444 p = strdup(descr); 445 if (p == NULL) 446 return (ENOMEM); 447 448 host = strchr(p, ':'); 449 if (host == NULL) { 450 error = EINVAL; 451 goto out; 452 } 453 *host++ = '\0'; 454 455 proto = p; 456 *is_udp = strcmp(proto, "udp") == 0; 457 458 guest = strchr(host, '-'); 459 if (guest == NULL) { 460 error = EINVAL; 461 goto out; 462 } 463 *guest++ = '\0'; 464 465 hostaddrp = (struct sockaddr_in *)hostaddr; 466 error = parse_addr(host, hostaddrp); 467 if (error != 0) 468 goto out; 469 470 guestaddrp = (struct sockaddr_in *)guestaddr; 471 error = parse_addr(guest, guestaddrp); 472 if (error != 0) 473 goto out; 474 475 out: 476 free(p); 477 return (error); 478 } 479 480 static int 481 config_one_hostfwd(struct slirp_priv *priv, const char *rule) 482 { 483 struct sockaddr hostaddr, guestaddr; 484 int error, is_udp; 485 486 error = parse_hostfwd_rule(rule, &is_udp, &hostaddr, &guestaddr); 487 if (error != 0) { 488 EPRINTLN("Unable to parse hostfwd rule '%s': %s", 489 rule, strerror(error)); 490 return (error); 491 } 492 493 error = slirp_add_hostxfwd_p(priv->slirp, &hostaddr, hostaddr.sa_len, 494 &guestaddr, guestaddr.sa_len, is_udp ? SLIRP_HOSTFWD_UDP : 0); 495 if (error != 0) { 496 EPRINTLN("Unable to add hostfwd rule '%s': %s", 497 rule, strerror(errno)); 498 return (error); 499 } 500 501 return (0); 502 } 503 504 static int 505 _slirp_init(struct net_backend *be, const char *devname __unused, 506 nvlist_t *nvl, net_be_rxeof_t cb, void *param) 507 { 508 struct slirp_priv *priv = NET_BE_PRIV(be); 509 SlirpConfig config = { 510 .version = 4, 511 .if_mtu = SLIRP_MTU, 512 .restricted = true, 513 .in_enabled = true, 514 .vnetwork.s_addr = htonl(0x0a000200), /* 10.0.2.0/24 */ 515 .vnetmask.s_addr = htonl(0xffffff00), 516 .vdhcp_start.s_addr = htonl(0x0a00020f),/* 10.0.2.15 */ 517 .vhost.s_addr = htonl(0x0a000202), /* 10.0.2.2 */ 518 .enable_emu = false, 519 }; 520 const char *hostfwd; 521 int error, sndbuf; 522 523 error = slirp_init_once(); 524 if (error != 0) 525 return (error); 526 527 slirp_priv_init(priv); 528 529 priv->slirp = slirp_new_p(&config, &slirp_cbs, priv); 530 if (priv->slirp == NULL) { 531 EPRINTLN("Unable to create slirp instance"); 532 goto err; 533 } 534 535 hostfwd = get_config_value_node(nvl, "hostfwd"); 536 if (hostfwd != NULL) { 537 char *rules, *tofree; 538 const char *rule; 539 540 tofree = rules = strdup(hostfwd); 541 if (rules == NULL) 542 goto err; 543 while ((rule = strsep(&rules, ";")) != NULL) { 544 error = config_one_hostfwd(priv, rule); 545 if (error != 0) { 546 free(tofree); 547 goto err; 548 } 549 } 550 free(tofree); 551 } 552 553 error = socketpair(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0, priv->pipe); 554 if (error != 0) { 555 EPRINTLN("Unable to create pipe: %s", strerror(errno)); 556 goto err; 557 } 558 559 error = pipe2(priv->wakeup, O_CLOEXEC | O_NONBLOCK); 560 if (error != 0) { 561 EPRINTLN("Unable to create wakeup pipe: %s", strerror(errno)); 562 goto err; 563 } 564 565 /* 566 * Try to avoid dropping buffered packets in slirp_cb_send_packet(). 567 */ 568 sndbuf = 1024 * 1024; 569 error = setsockopt(priv->pipe[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, 570 sizeof(sndbuf)); 571 if (error != 0) { 572 EPRINTLN("Could not set socket buffer size: %s", 573 strerror(errno)); 574 goto err; 575 } 576 577 be->fd = priv->pipe[0]; 578 priv->mevp = mevent_add_disabled(be->fd, EVF_READ, cb, param); 579 if (priv->mevp == NULL) { 580 EPRINTLN("Could not register event"); 581 goto err; 582 } 583 584 error = pthread_create(&priv->pollfd_td, NULL, slirp_pollfd_td_loop, 585 priv); 586 if (error != 0) { 587 EPRINTLN("Unable to create pollfd thread: %s", strerror(error)); 588 goto err; 589 } 590 591 return (0); 592 593 err: 594 slirp_priv_cleanup(priv); 595 return (-1); 596 } 597 598 static ssize_t 599 slirp_send(struct net_backend *be, const struct iovec *iov, int iovcnt) 600 { 601 struct slirp_priv *priv = NET_BE_PRIV(be); 602 603 if (iovcnt == 1) { 604 /* We can avoid copying if there's a single segment. */ 605 pthread_mutex_lock(&priv->mtx); 606 slirp_input_p(priv->slirp, iov->iov_base, 607 (int)iov->iov_len); 608 pthread_mutex_unlock(&priv->mtx); 609 return (iov[0].iov_len); 610 } else { 611 uint8_t *pkt; 612 size_t pktlen; 613 614 pktlen = 0; 615 for (int i = 0; i < iovcnt; i++) 616 pktlen += iov[i].iov_len; 617 pkt = malloc(pktlen); 618 if (pkt == NULL) 619 return (-1); 620 pktlen = 0; 621 for (int i = 0; i < iovcnt; i++) { 622 memcpy(pkt + pktlen, iov[i].iov_base, iov[i].iov_len); 623 pktlen += iov[i].iov_len; 624 } 625 pthread_mutex_lock(&priv->mtx); 626 slirp_input_p(priv->slirp, pkt, (int)pktlen); 627 pthread_mutex_unlock(&priv->mtx); 628 free(pkt); 629 return (pktlen); 630 } 631 } 632 633 static void 634 _slirp_cleanup(struct net_backend *be) 635 { 636 struct slirp_priv *priv = NET_BE_PRIV(be); 637 638 slirp_priv_cleanup(priv); 639 } 640 641 static ssize_t 642 slirp_peek_recvlen(struct net_backend *be) 643 { 644 struct slirp_priv *priv = NET_BE_PRIV(be); 645 ssize_t n; 646 647 n = recv(priv->pipe[0], NULL, 0, MSG_PEEK | MSG_DONTWAIT | MSG_TRUNC); 648 if (n < 0) 649 return (errno == EWOULDBLOCK ? 0 : -1); 650 assert((size_t)n <= SLIRP_MTU); 651 return (n); 652 } 653 654 static ssize_t 655 slirp_recv(struct net_backend *be, const struct iovec *iov, int iovcnt) 656 { 657 struct slirp_priv *priv = NET_BE_PRIV(be); 658 struct msghdr hdr; 659 ssize_t n; 660 661 hdr.msg_name = NULL; 662 hdr.msg_namelen = 0; 663 hdr.msg_iov = __DECONST(struct iovec *, iov); 664 hdr.msg_iovlen = iovcnt; 665 hdr.msg_control = NULL; 666 hdr.msg_controllen = 0; 667 hdr.msg_flags = 0; 668 n = recvmsg(priv->pipe[0], &hdr, MSG_DONTWAIT); 669 if (n < 0) { 670 if (errno == EWOULDBLOCK) 671 return (0); 672 return (-1); 673 } 674 assert(n <= SLIRP_MTU); 675 return (n); 676 } 677 678 static void 679 slirp_recv_enable(struct net_backend *be) 680 { 681 struct slirp_priv *priv = NET_BE_PRIV(be); 682 683 mevent_enable(priv->mevp); 684 } 685 686 static void 687 slirp_recv_disable(struct net_backend *be) 688 { 689 struct slirp_priv *priv = NET_BE_PRIV(be); 690 691 mevent_disable(priv->mevp); 692 } 693 694 static uint64_t 695 slirp_get_cap(struct net_backend *be __unused) 696 { 697 return (0); 698 } 699 700 static int 701 slirp_set_cap(struct net_backend *be __unused, uint64_t features __unused, 702 unsigned int vnet_hdr_len __unused) 703 { 704 return ((features || vnet_hdr_len) ? -1 : 0); 705 } 706 707 static struct net_backend slirp_backend = { 708 .prefix = "slirp", 709 .priv_size = sizeof(struct slirp_priv), 710 .init = _slirp_init, 711 .cleanup = _slirp_cleanup, 712 .send = slirp_send, 713 .peek_recvlen = slirp_peek_recvlen, 714 .recv = slirp_recv, 715 .recv_enable = slirp_recv_enable, 716 .recv_disable = slirp_recv_disable, 717 .get_cap = slirp_get_cap, 718 .set_cap = slirp_set_cap, 719 }; 720 721 DATA_SET(net_backend_set, slirp_backend); 722