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