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