]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[slirp] Add libslirp driver for Linux
authorMichael Brown <mcb30@ipxe.org>
Tue, 2 Mar 2021 10:20:55 +0000 (10:20 +0000)
committerMichael Brown <mcb30@ipxe.org>
Tue, 2 Mar 2021 11:09:57 +0000 (11:09 +0000)
Add a driver using libslirp to provide a virtual network interface
without requiring root permissions on the host.  This simplifies the
process of running iPXE as a Linux userspace application with network
access.  For example:

  make bin-x86_64-linux/slirp.linux
  ./bin-x86_64-linux/slirp.linux --net slirp

libslirp will provide a built-in emulated DHCP server and NAT router.
Settings such as the boot filename may be controlled via command-line
options.  For example:

  ./bin-x86_64-linux/slirp.linux \
      --net slirp,filename=http://192.168.0.1/boot.ipxe

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/Makefile.linux
src/drivers/linux/slirp.c [new file with mode: 0644]
src/include/ipxe/errfile.h
src/include/ipxe/linux_api.h
src/include/ipxe/slirp.h [new file with mode: 0644]
src/interface/linux/linux_api.c

index 4a7837916395e4001edaae0ed154e238e3fe7677..09b2b1577d546bc7bd6800e6d5a07c9de3687fd7 100644 (file)
@@ -26,9 +26,21 @@ NON_AUTO_MEDIA = linux
 #
 LINUX_CFLAGS   += -Os -idirafter include -DSYMBOL_PREFIX=$(SYMBOL_PREFIX)
 
+# Check for libslirp
+#
+LIBSLIRP_TEST = $(CC) $(LINUX_CFLAGS) -x c /dev/null -nostartfiles \
+                     -include slirp/libslirp.h -lslirp \
+                     -o /dev/null >/dev/null 2>&1
+WITH_LIBSLIRP  := $(shell $(LIBSLIRP_TEST) && $(ECHO) yes)
+ifneq ($(WITH_LIBSLIRP),)
+LINUX_CFLAGS   += -DHAVE_LIBSLIRP
+LINUX_LDFLAGS  += -lslirp
+endif
+
 # Host API wrapper
 #
-$(BIN)/linux_api.o : interface/linux/linux_api.c $(MAKEDEPS)
+$(BIN)/linux_api.o : interface/linux/linux_api.c include/ipxe/linux_api.h \
+                    include/ipxe/slirp.h $(MAKEDEPS)
        $(QM)$(ECHO) "  [BUILD] $@"
        $(Q)$(CC) $(LINUX_CFLAGS) $(WORKAROUND_CFLAGS) -o $@ -c $<
 
@@ -36,4 +48,5 @@ $(BIN)/linux_api.o : interface/linux/linux_api.c $(MAKEDEPS)
 #
 $(BIN)/%.linux : $(BIN)/%.linux.tmp $(BIN)/linux_api.o
        $(QM)$(ECHO) "  [FINISH] $@"
-       $(Q)$(CC) $(LINUX_CFLAGS) $(WORKAROUND_CFLAGS) -o $@ $^
+       $(Q)$(CC) $(LINUX_CFLAGS) $(WORKAROUND_CFLAGS) $(LINUX_LDFLAGS) \
+               -o $@ $^
diff --git a/src/drivers/linux/slirp.c b/src/drivers/linux/slirp.c
new file mode 100644 (file)
index 0000000..8341c96
--- /dev/null
@@ -0,0 +1,552 @@
+/*
+ * Copyright (C) 2021 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/ethernet.h>
+#include <ipxe/if_ether.h>
+#include <ipxe/in.h>
+#include <ipxe/timer.h>
+#include <ipxe/retry.h>
+#include <ipxe/linux.h>
+#include <ipxe/linux_api.h>
+#include <ipxe/slirp.h>
+
+/** @file
+ *
+ * Linux Slirp network driver
+ *
+ */
+
+/** Maximum number of open file descriptors */
+#define SLIRP_MAX_FDS 128
+
+/** A Slirp network interface */
+struct slirp_nic {
+       /** The libslirp device object */
+       struct Slirp *slirp;
+       /** Polling file descriptor list */
+       struct pollfd pollfds[SLIRP_MAX_FDS];
+       /** Number of file descriptors */
+       unsigned int numfds;
+};
+
+/** A Slirp alarm timer */
+struct slirp_alarm {
+       /** Slirp network interface */
+       struct slirp_nic *slirp;
+       /** Retry timer */
+       struct retry_timer timer;
+       /** Callback function */
+       void ( __asmcall * callback ) ( void *opaque );
+       /** Opaque value for callback function */
+       void *opaque;
+};
+
+/** Default MAC address */
+static const uint8_t slirp_default_mac[ETH_ALEN] =
+       { 0x52, 0x54, 0x00, 0x12, 0x34, 0x56 };
+
+/******************************************************************************
+ *
+ * Slirp interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Send packet
+ *
+ * @v buf              Data buffer
+ * @v len              Length of data
+ * @v device           Device opaque pointer
+ * @ret len            Consumed length (or negative on error)
+ */
+static ssize_t __asmcall slirp_send_packet ( const void *buf, size_t len,
+                                            void *device ) {
+       struct net_device *netdev = device;
+       struct io_buffer *iobuf;
+
+       /* Allocate I/O buffer */
+       iobuf = alloc_iob ( len );
+       if ( ! iobuf )
+               return -1;
+
+       /* Populate I/O buffer */
+       memcpy ( iob_put ( iobuf, len ), buf, len );
+
+       /* Hand off to network stack */
+       netdev_rx ( netdev, iobuf );
+
+       return len;
+}
+
+/**
+ * Print an error message
+ *
+ * @v msg              Error message
+ * @v device           Device opaque pointer
+ */
+static void __asmcall slirp_guest_error ( const char *msg, void *device ) {
+       struct net_device *netdev = device;
+       struct slirp_nic *slirp = netdev->priv;
+
+       DBGC ( slirp, "SLIRP %p error: %s\n", slirp, msg );
+}
+
+/**
+ * Get virtual clock
+ *
+ * @v device           Device opaque pointer
+ * @ret clock_ns       Clock time in nanoseconds
+ */
+static int64_t __asmcall slirp_clock_get_ns ( void *device __unused ) {
+       int64_t time;
+
+       time = currticks();
+       return ( time * ( 1000000 / TICKS_PER_MS ) );
+}
+
+/**
+ * Handle timer expiry
+ *
+ * @v timer            Retry timer
+ * @v over             Failure indicator
+ */
+static void slirp_expired ( struct retry_timer *timer, int over __unused ) {
+       struct slirp_alarm *alarm =
+               container_of ( timer, struct slirp_alarm, timer );
+       struct slirp_nic *slirp = alarm->slirp;
+
+       /* Notify callback */
+       DBGC ( slirp, "SLIRP %p timer fired\n", slirp );
+       alarm->callback ( alarm->opaque );
+}
+
+/**
+ * Create a new timer
+ *
+ * @v callback         Timer callback
+ * @v opaque           Timer opaque pointer
+ * @v device           Device opaque pointer
+ * @ret timer          Timer
+ */
+static void * __asmcall
+slirp_timer_new ( void ( __asmcall * callback ) ( void *opaque ),
+                 void *opaque, void *device ) {
+       struct net_device *netdev = device;
+       struct slirp_nic *slirp = netdev->priv;
+       struct slirp_alarm *alarm;
+
+       /* Allocate timer */
+       alarm = malloc ( sizeof ( *alarm ) );
+       if ( ! alarm ) {
+               DBGC ( slirp, "SLIRP %p could not allocate timer\n", slirp );
+               return NULL;
+       }
+
+       /* Initialise timer */
+       memset ( alarm, 0, sizeof ( *alarm ) );
+       alarm->slirp = slirp;
+       timer_init ( &alarm->timer, slirp_expired, NULL );
+       alarm->callback = callback;
+       alarm->opaque = opaque;
+       DBGC ( slirp, "SLIRP %p timer %p has callback %p (%p)\n",
+              slirp, alarm, alarm->callback, alarm->opaque );
+
+       return alarm;
+}
+
+/**
+ * Delete a timer
+ *
+ * @v timer            Timer
+ * @v device           Device opaque pointer
+ */
+static void __asmcall slirp_timer_free ( void *timer, void *device ) {
+       struct net_device *netdev = device;
+       struct slirp_nic *slirp = netdev->priv;
+       struct slirp_alarm *alarm = timer;
+
+       /* Ignore timers that failed to allocate */
+       if ( ! alarm )
+               return;
+
+       /* Stop timer */
+       stop_timer ( &alarm->timer );
+
+       /* Free timer */
+       free ( alarm );
+       DBGC ( slirp, "SLIRP %p timer %p freed\n", slirp, alarm );
+}
+
+/**
+ * Set timer expiry time
+ *
+ * @v timer            Timer
+ * @v expire           Expiry time
+ * @v device           Device opaque pointer
+ */
+static void __asmcall slirp_timer_mod ( void *timer, int64_t expire,
+                                       void *device ) {
+       struct net_device *netdev = device;
+       struct slirp_nic *slirp = netdev->priv;
+       struct slirp_alarm *alarm = timer;
+       int64_t timeout_ms;
+       unsigned long timeout;
+
+       /* Ignore timers that failed to allocate */
+       if ( ! alarm )
+               return;
+
+       /* (Re)start timer */
+       timeout_ms = ( expire - ( currticks() / TICKS_PER_MS ) );
+       if ( timeout_ms < 0 )
+               timeout_ms = 0;
+       timeout = ( timeout_ms * TICKS_PER_MS );
+       start_timer_fixed ( &alarm->timer, timeout );
+       DBGC ( slirp, "SLIRP %p timer %p set for %ld ticks\n",
+              slirp, alarm, timeout );
+}
+
+/**
+ * Register file descriptor for polling
+ *
+ * @v fd               File descriptor
+ * @v device           Device opaque pointer
+ */
+static void __asmcall slirp_register_poll_fd ( int fd, void *device ) {
+       struct net_device *netdev = device;
+       struct slirp_nic *slirp = netdev->priv;
+
+       DBGC ( slirp, "SLIRP %p registered FD %d\n", slirp, fd );
+}
+
+/**
+ * Unregister file descriptor
+ *
+ * @v fd               File descriptor
+ * @v device           Device opaque pointer
+ */
+static void __asmcall slirp_unregister_poll_fd ( int fd, void *device ) {
+       struct net_device *netdev = device;
+       struct slirp_nic *slirp = netdev->priv;
+
+       DBGC ( slirp, "SLIRP %p unregistered FD %d\n", slirp, fd );
+}
+
+/**
+ * Notify that new events are ready
+ *
+ * @v device           Device opaque pointer
+ */
+static void __asmcall slirp_notify ( void *device ) {
+       struct net_device *netdev = device;
+       struct slirp_nic *slirp = netdev->priv;
+
+       DBGC2 ( slirp, "SLIRP %p notified\n", slirp );
+}
+
+/** Slirp callbacks */
+static struct slirp_callbacks slirp_callbacks = {
+       .send_packet            = slirp_send_packet,
+       .guest_error            = slirp_guest_error,
+       .clock_get_ns           = slirp_clock_get_ns,
+       .timer_new              = slirp_timer_new,
+       .timer_free             = slirp_timer_free,
+       .timer_mod              = slirp_timer_mod,
+       .register_poll_fd       = slirp_register_poll_fd,
+       .unregister_poll_fd     = slirp_unregister_poll_fd,
+       .notify                 = slirp_notify,
+};
+
+/******************************************************************************
+ *
+ * Network device interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Open network device
+ *
+ * @v netdev           Network device
+ * @ret rc             Return status code
+ */
+static int slirp_open ( struct net_device *netdev ) {
+       struct slirp_nic *slirp = netdev->priv;
+
+       /* Nothing to do */
+       DBGC ( slirp, "SLIRP %p opened\n", slirp );
+
+       return 0;
+}
+
+/**
+ * Close network device
+ *
+ * @v netdev           Network device
+ */
+static void slirp_close ( struct net_device *netdev ) {
+       struct slirp_nic *slirp = netdev->priv;
+
+       /* Nothing to do */
+       DBGC ( slirp, "SLIRP %p closed\n", slirp );
+}
+
+/**
+ * Transmit packet
+ *
+ * @v netdev           Network device
+ * @v iobuf            I/O buffer
+ * @ret rc             Return status code
+ */
+static int slirp_transmit ( struct net_device *netdev,
+                           struct io_buffer *iobuf ) {
+       struct slirp_nic *slirp = netdev->priv;
+
+       /* Transmit packet */
+       linux_slirp_input ( slirp->slirp, iobuf->data, iob_len ( iobuf ) );
+       netdev_tx_complete ( netdev, iobuf );
+
+       return 0;
+}
+
+/**
+ * Add polling file descriptor
+ *
+ * @v fd               File descriptor
+ * @v events           Events of interest
+ * @v device           Device opaque pointer
+ * @ret index          File descriptor index
+ */
+static int __asmcall slirp_add_poll ( int fd, int events, void *device ) {
+       struct net_device *netdev = device;
+       struct slirp_nic *slirp = netdev->priv;
+       struct pollfd *pollfd;
+       unsigned int index;
+
+       /* Fail if too many descriptors are registered */
+       if ( slirp->numfds >= SLIRP_MAX_FDS ) {
+               DBGC ( slirp, "SLIRP %p too many file descriptors\n", slirp );
+               return -1;
+       }
+
+       /* Populate polling file descriptor */
+       index = slirp->numfds++;
+       pollfd = &slirp->pollfds[index];
+       pollfd->fd = fd;
+       pollfd->events = 0;
+       if ( events & SLIRP_EVENT_IN )
+               pollfd->events |= POLLIN;
+       if ( events & SLIRP_EVENT_OUT )
+               pollfd->events |= POLLOUT;
+       if ( events & SLIRP_EVENT_PRI )
+               pollfd->events |= POLLPRI;
+       if ( events & SLIRP_EVENT_ERR )
+               pollfd->events |= POLLERR;
+       if ( events & SLIRP_EVENT_HUP )
+               pollfd->events |= ( POLLHUP | POLLRDHUP );
+       DBGCP ( slirp, "SLIRP %p polling FD %d event mask %#04x(%#04x)\n",
+               slirp, fd, events, pollfd->events );
+
+       return index;
+}
+
+/**
+ * Get returned events for a file descriptor
+ *
+ * @v index            File descriptor index
+ * @v device           Device opaque pointer
+ * @ret events         Returned events
+ */
+static int __asmcall slirp_get_revents ( int index, void *device ) {
+       struct net_device *netdev = device;
+       struct slirp_nic *slirp = netdev->priv;
+       int revents;
+       int events;
+
+       /* Ignore failed descriptors */
+       if ( index < 0 )
+               return 0;
+
+       /* Collect events */
+       revents = slirp->pollfds[index].revents;
+       events = 0;
+       if ( revents & POLLIN )
+               events |= SLIRP_EVENT_IN;
+       if ( revents & POLLOUT )
+               events |= SLIRP_EVENT_OUT;
+       if ( revents & POLLPRI )
+               events |= SLIRP_EVENT_PRI;
+       if ( revents & POLLERR )
+               events |= SLIRP_EVENT_ERR;
+       if ( revents & ( POLLHUP | POLLRDHUP ) )
+               events |= SLIRP_EVENT_HUP;
+       if ( events ) {
+               DBGC2 ( slirp, "SLIRP %p polled FD %d events %#04x(%#04x)\n",
+                       slirp, slirp->pollfds[index].fd, events, revents );
+       }
+
+       return events;
+}
+
+/**
+ * Poll for completed and received packets
+ *
+ * @v netdev           Network device
+ */
+static void slirp_poll ( struct net_device *netdev ) {
+       struct slirp_nic *slirp = netdev->priv;
+       uint32_t timeout = 0;
+       int ready;
+       int error;
+
+       /* Rebuild polling file descriptor list */
+       slirp->numfds = 0;
+       linux_slirp_pollfds_fill ( slirp->slirp, &timeout,
+                                  slirp_add_poll, netdev );
+
+       /* Poll descriptors */
+       ready = linux_poll ( slirp->pollfds, slirp->numfds, 0 );
+       error = ( ready == -1 );
+       linux_slirp_pollfds_poll ( slirp->slirp, error, slirp_get_revents,
+                                  netdev );
+
+       /* Record polling errors */
+       if ( error ) {
+               DBGC ( slirp, "SLIRP %p poll failed: %s\n",
+                      slirp, linux_strerror ( linux_errno ) );
+               netdev_rx_err ( netdev, NULL, -ELINUX ( linux_errno ) );
+       }
+}
+
+/** Network device operations */
+static struct net_device_operations slirp_operations = {
+       .open           = slirp_open,
+       .close          = slirp_close,
+       .transmit       = slirp_transmit,
+       .poll           = slirp_poll,
+};
+
+/******************************************************************************
+ *
+ * Linux driver interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Probe device
+ *
+ * @v linux            Linux device
+ * @v request          Device creation request
+ * @ret rc             Return status code
+ */
+static int slirp_probe ( struct linux_device *linux,
+                        struct linux_device_request *request ) {
+       struct net_device *netdev;
+       struct slirp_nic *slirp;
+       struct slirp_config config;
+       int rc;
+
+       /* Allocate device */
+       netdev = alloc_etherdev ( sizeof ( *slirp ) );
+       if ( ! netdev ) {
+               rc = -ENOMEM;
+               goto err_alloc;
+       }
+       netdev_init ( netdev, &slirp_operations );
+       linux_set_drvdata ( linux, netdev );
+       snprintf ( linux->dev.name, sizeof ( linux->dev.name ), "host" );
+       netdev->dev = &linux->dev;
+       memcpy ( netdev->hw_addr, slirp_default_mac, ETH_ALEN );
+       slirp = netdev->priv;
+       memset ( slirp, 0, sizeof ( *slirp ) );
+
+       /* Apply requested settings */
+       linux_apply_settings ( &request->settings,
+                              netdev_settings ( netdev ) );
+
+       /* Initialise default configuration (matching qemu) */
+       memset ( &config, 0, sizeof ( config ) );
+       config.version = 1;
+       config.in_enabled = true;
+       config.vnetwork.s_addr = htonl ( 0x0a000200 ); /* 10.0.2.0 */
+       config.vnetmask.s_addr = htonl ( 0xffffff00 ); /* 255.255.255.0 */
+       config.vhost.s_addr = htonl ( 0x0a000202 ); /* 10.0.2.2 */
+       config.in6_enabled = true;
+       config.vdhcp_start.s_addr = htonl ( 0x0a00020f ); /* 10.0.2.15 */
+       config.vnameserver.s_addr = htonl ( 0x0a000203 ); /* 10.0.2.3 */
+
+       /* Instantiate device */
+       slirp->slirp = linux_slirp_new ( &config, &slirp_callbacks, netdev );
+       if ( ! slirp->slirp ) {
+               DBGC ( slirp, "SLIRP could not instantiate\n" );
+               rc = -ENODEV;
+               goto err_new;
+       }
+
+       /* Register network device */
+       if ( ( rc = register_netdev ( netdev ) ) != 0 )
+               goto err_register;
+
+       /* Set link up since there is no concept of link state */
+       netdev_link_up ( netdev );
+
+       return 0;
+
+       unregister_netdev ( netdev );
+ err_register:
+       linux_slirp_cleanup ( slirp->slirp );
+ err_new:
+       netdev_nullify ( netdev );
+       netdev_put ( netdev );
+ err_alloc:
+       return rc;
+}
+
+/**
+ * Remove device
+ *
+ * @v linux            Linux device
+ */
+static void slirp_remove ( struct linux_device *linux ) {
+       struct net_device *netdev = linux_get_drvdata ( linux );
+       struct slirp_nic *slirp = netdev->priv;
+
+       /* Unregister network device */
+       unregister_netdev ( netdev );
+
+       /* Shut down device */
+       linux_slirp_cleanup ( slirp->slirp );
+
+       /* Free network device */
+       netdev_nullify ( netdev );
+       netdev_put ( netdev );
+}
+
+/** Slirp driver */
+struct linux_driver slirp_driver __linux_driver = {
+       .name = "slirp",
+       .probe = slirp_probe,
+       .remove = slirp_remove,
+       .can_probe = 1,
+};
index e3fc8fa0921540219ca45726d3dcecdb409e4f10..b5c5d185edf40e8a40d4b20e857b210eca1261ae 100644 (file)
@@ -212,6 +212,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #define ERRFILE_intelxlvf           ( ERRFILE_DRIVER | 0x00cd0000 )
 #define ERRFILE_usbblk              ( ERRFILE_DRIVER | 0x00ce0000 )
 #define ERRFILE_iphone              ( ERRFILE_DRIVER | 0x00cf0000 )
+#define ERRFILE_slirp               ( ERRFILE_DRIVER | 0x00d00000 )
 
 #define ERRFILE_aoe                    ( ERRFILE_NET | 0x00000000 )
 #define ERRFILE_arp                    ( ERRFILE_NET | 0x00010000 )
index ea247a61363cc28dbd5ccfeb4a51c65c1e4fb654..040b52f8c41d1ed78efcda2c7f2c7e8521385fa1 100644 (file)
@@ -50,6 +50,9 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #endif
 
 struct sockaddr;
+struct slirp_config;
+struct slirp_callbacks;
+struct Slirp;
 
 extern int linux_errno;
 extern int linux_argc;
@@ -82,5 +85,21 @@ extern ssize_t __asmcall linux_sendto ( int sockfd, const void *buf,
                                        const struct sockaddr *dest_addr,
                                        size_t addrlen );
 extern const char * __asmcall linux_strerror ( int linux_errno );
+extern struct Slirp * __asmcall
+linux_slirp_new ( const struct slirp_config *config,
+                 const struct slirp_callbacks *callbacks, void *opaque );
+extern void __asmcall linux_slirp_cleanup ( struct Slirp *slirp );
+extern void __asmcall linux_slirp_input ( struct Slirp *slirp,
+                                         const uint8_t *pkt, int pkt_len );
+extern void __asmcall
+linux_slirp_pollfds_fill ( struct Slirp *slirp, uint32_t *timeout,
+                          int ( __asmcall * add_poll ) ( int fd, int events,
+                                                         void *opaque ),
+                          void *opaque );
+extern void __asmcall
+linux_slirp_pollfds_poll ( struct Slirp *slirp, int select_error,
+                          int ( __asmcall * get_revents ) ( int idx,
+                                                            void *opaque ),
+                          void *opaque );
 
 #endif /* _IPXE_LINUX_API_H */
diff --git a/src/include/ipxe/slirp.h b/src/include/ipxe/slirp.h
new file mode 100644 (file)
index 0000000..4fb13b9
--- /dev/null
@@ -0,0 +1,155 @@
+#ifndef _IPXE_SLIRP_H
+#define _IPXE_SLIRP_H
+
+/** @file
+ *
+ * Linux Slirp network driver
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/** Ready to be read */
+#define SLIRP_EVENT_IN 0x01
+
+/** Ready to be written */
+#define SLIRP_EVENT_OUT 0x02
+
+/** Exceptional condition */
+#define SLIRP_EVENT_PRI 0x04
+
+/** Error condition */
+#define SLIRP_EVENT_ERR 0x08
+
+/** Hang up */
+#define SLIRP_EVENT_HUP 0x10
+
+/** Slirp device configuration */
+struct slirp_config {
+       /** Configuration version */
+       uint32_t version;
+       /** Restrict to host loopback connections only */
+       int restricted;
+       /** IPv4 is enabled */
+       bool in_enabled;
+       /** IPv4 network */
+       struct in_addr vnetwork;
+       /** IPv4 netmask */
+       struct in_addr vnetmask;
+       /** IPv4 host server address */
+       struct in_addr vhost;
+       /** IPv6 is enabled */
+       bool in6_enabled;
+       /** IPv6 prefix */
+       struct in6_addr vprefix_addr6;
+       /** IPv6 prefix length */
+       uint8_t vprefix_len;
+       /** IPv6 host server address */
+       struct in6_addr vhost6;
+       /** Client hostname */
+       const char *vhostname;
+       /** TFTP server name */
+       const char *tftp_server_name;
+       /** TFTP path prefix */
+       const char *tftp_path;
+       /** Boot filename */
+       const char *bootfile;
+       /** DHCPv4 start address */
+       struct in_addr vdhcp_start;
+       /** DNS IPv4 address */
+       struct in_addr vnameserver;
+       /** DNS IPv6 address */
+       struct in_addr vnameserver6;
+       /** DNS search list */
+       const char **vdnssearch;
+       /** Domain name */
+       const char *vdomainname;
+       /** Interface MTU */
+       size_t if_mtu;
+       /** Interface MRU */
+       size_t if_mru;
+       /** Disable host loopback connections */
+       bool disable_host_loopback;
+       /** Enable emulation (apparently unsafe) */
+       bool enable_emu;
+};
+
+/** Slirp device callbacks */
+struct slirp_callbacks {
+       /**
+        * Send packet
+        *
+        * @v buf               Data buffer
+        * @v len               Length of data
+        * @v device            Device opaque pointer
+        * @ret len             Consumed length (or negative on error)
+        */
+       ssize_t ( __asmcall * send_packet ) ( const void *buf, size_t len,
+                                             void *device );
+       /**
+        * Print an error message
+        *
+        * @v msg               Error message
+        * @v device            Device opaque pointer
+        */
+       void ( __asmcall * guest_error ) ( const char *msg, void *device );
+       /**
+        * Get virtual clock
+        *
+        * @v device            Device opaque pointer
+        * @ret clock_ns        Clock time in nanoseconds
+        */
+       int64_t ( __asmcall * clock_get_ns ) ( void *device );
+       /**
+        * Create a new timer
+        *
+        * @v callback          Timer callback
+        * @v opaque            Timer opaque pointer
+        * @v device            Device opaque pointer
+        * @ret timer           Timer
+        */
+       void * ( __asmcall * timer_new ) ( void ( __asmcall * callback )
+                                          ( void *opaque ),
+                                          void *opaque, void *device );
+       /**
+        * Delete a timer
+        *
+        * @v timer             Timer
+        * @v device            Device opaque pointer
+        */
+       void ( __asmcall * timer_free ) ( void *timer, void *device );
+       /**
+        * Set timer expiry time
+        *
+        * @v timer             Timer
+        * @v expire            Expiry time
+        * @v device            Device opaque pointer
+        */
+       void ( __asmcall * timer_mod ) ( void *timer, int64_t expire,
+                                        void *device );
+       /**
+        * Register file descriptor for polling
+        *
+        * @v fd                File descriptor
+        * @v device            Device opaque pointer
+        */
+       void ( __asmcall * register_poll_fd ) ( int fd, void *device );
+       /**
+        * Unregister file descriptor
+        *
+        * @v fd                File descriptor
+        * @v device            Device opaque pointer
+        */
+       void ( __asmcall * unregister_poll_fd ) ( int fd, void *device );
+       /**
+        * Notify that new events are ready
+        *
+        * @v device            Device opaque pointer
+        */
+       void ( __asmcall * notify ) ( void *device );
+};
+
+#endif /* _IPXE_SLIRP_H */
index 4ab3c6603abb3b87356f95a8cc595307fa58c150..1f44b532ba0a03cf7352849433ae86e639f372dd 100644 (file)
@@ -24,6 +24,7 @@
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
+#include <assert.h>
 #include <fcntl.h>
 #include <time.h>
 #include <poll.h>
 #include <sys/mman.h>
 #include <sys/socket.h>
 #include <sys/time.h>
+#include <netinet/in.h>
 #include <ipxe/linux_api.h>
+#include <ipxe/slirp.h>
+
+#ifdef HAVE_LIBSLIRP
+#include <slirp/libslirp.h>
+#endif
 
 /** @file
  *
@@ -345,6 +352,152 @@ const char * __asmcall linux_strerror ( int linux_errno ) {
        return strerror ( linux_errno );
 }
 
+/******************************************************************************
+ *
+ * libslirp wrappers
+ *
+ ******************************************************************************
+ */
+
+#ifdef HAVE_LIBSLIRP
+
+/**
+ * Wrap slirp_new()
+ *
+ */
+struct Slirp * __asmcall
+linux_slirp_new ( const struct slirp_config *config,
+                 const struct slirp_callbacks *callbacks, void *opaque ) {
+       const union {
+               struct slirp_callbacks callbacks;
+               SlirpCb cb;
+       } *u = ( ( typeof ( u ) ) callbacks );
+       SlirpConfig cfg;
+       Slirp *slirp;
+
+       /* Translate configuration */
+       memset ( &cfg, 0, sizeof ( cfg ) );
+       cfg.version = config->version;
+       cfg.restricted = config->restricted;
+       cfg.in_enabled = config->in_enabled;
+       cfg.vnetwork = config->vnetwork;
+       cfg.vnetmask = config->vnetmask;
+       cfg.vhost = config->vhost;
+       cfg.in6_enabled = config->in6_enabled;
+       memcpy ( &cfg.vprefix_addr6, &config->vprefix_addr6,
+                sizeof ( cfg.vprefix_addr6 ) );
+       cfg.vprefix_len = config->vprefix_len;
+       memcpy ( &cfg.vhost6, &config->vhost6, sizeof ( cfg.vhost6 ) );
+       cfg.vhostname = config->vhostname;
+       cfg.tftp_server_name = config->tftp_server_name;
+       cfg.tftp_path = config->tftp_path;
+       cfg.bootfile = config->bootfile;
+       cfg.vdhcp_start = config->vdhcp_start;
+       cfg.vnameserver = config->vnameserver;
+       memcpy ( &cfg.vnameserver6, &config->vnameserver6,
+                sizeof ( cfg.vnameserver6 ) );
+       cfg.vdnssearch = config->vdnssearch;
+       cfg.vdomainname = config->vdomainname;
+       cfg.if_mtu = config->if_mtu;
+       cfg.if_mru = config->if_mru;
+       cfg.disable_host_loopback = config->disable_host_loopback;
+       cfg.enable_emu = config->enable_emu;
+
+       /* Validate callback structure */
+       static_assert ( &u->cb.send_packet == &u->callbacks.send_packet );
+       static_assert ( &u->cb.guest_error == &u->callbacks.guest_error );
+       static_assert ( &u->cb.clock_get_ns == &u->callbacks.clock_get_ns );
+       static_assert ( &u->cb.timer_new == &u->callbacks.timer_new );
+       static_assert ( &u->cb.timer_free == &u->callbacks.timer_free );
+       static_assert ( &u->cb.timer_mod == &u->callbacks.timer_mod );
+       static_assert ( &u->cb.register_poll_fd ==
+                       &u->callbacks.register_poll_fd );
+       static_assert ( &u->cb.unregister_poll_fd ==
+                       &u->callbacks.unregister_poll_fd );
+       static_assert ( &u->cb.notify == &u->callbacks.notify );
+
+       /* Create device */
+       slirp = slirp_new ( &cfg, &u->cb, opaque );
+
+       return slirp;
+}
+
+/**
+ * Wrap slirp_cleanup()
+ *
+ */
+void __asmcall linux_slirp_cleanup ( struct Slirp *slirp ) {
+
+       slirp_cleanup ( slirp );
+}
+
+/**
+ * Wrap slirp_input()
+ *
+ */
+void __asmcall linux_slirp_input ( struct Slirp *slirp, const uint8_t *pkt,
+                                  int pkt_len ) {
+
+       slirp_input ( slirp, pkt, pkt_len );
+}
+
+/**
+ * Wrap slirp_pollfds_fill()
+ *
+ */
+void __asmcall
+linux_slirp_pollfds_fill ( struct Slirp *slirp, uint32_t *timeout,
+                          int ( __asmcall * add_poll ) ( int fd, int events,
+                                                         void *opaque ),
+                          void *opaque ) {
+
+       slirp_pollfds_fill ( slirp, timeout, add_poll, opaque );
+}
+
+/**
+ * Wrap slirp_pollfds_poll()
+ *
+ */
+void __asmcall
+linux_slirp_pollfds_poll ( struct Slirp *slirp, int select_error,
+                          int ( __asmcall * get_revents ) ( int idx,
+                                                            void *opaque ),
+                          void *opaque ) {
+
+       slirp_pollfds_poll ( slirp, select_error, get_revents, opaque );
+}
+
+#else /* HAVE_LIBSLIRP */
+
+struct Slirp * __asmcall
+linux_slirp_new ( const struct slirp_config *config,
+                 const struct slirp_callbacks *callbacks, void *opaque ) {
+       return NULL;
+}
+
+void __asmcall linux_slirp_cleanup ( struct Slirp *slirp ) {
+}
+
+void __asmcall linux_slirp_input ( struct Slirp *slirp, const uint8_t *pkt,
+                                  int pkt_len ) {
+}
+
+void __asmcall
+linux_slirp_pollfds_fill ( struct Slirp *slirp, uint32_t *timeout,
+                          int ( __asmcall * add_poll ) ( int fd, int events,
+                                                         void *opaque ),
+                          void *opaque ) {
+}
+
+void __asmcall
+linux_slirp_pollfds_poll ( struct Slirp *slirp, int select_error,
+                          int ( __asmcall * get_revents ) ( int idx,
+                                                            void *opaque ),
+                          void *opaque ) {
+}
+
+#endif /* HAVE_LIBSLIRP */
+
 /******************************************************************************
  *
  * Symbol aliases
@@ -371,3 +524,8 @@ PROVIDE_IPXE_SYM ( linux_socket );
 PROVIDE_IPXE_SYM ( linux_bind );
 PROVIDE_IPXE_SYM ( linux_sendto );
 PROVIDE_IPXE_SYM ( linux_strerror );
+PROVIDE_IPXE_SYM ( linux_slirp_new );
+PROVIDE_IPXE_SYM ( linux_slirp_cleanup );
+PROVIDE_IPXE_SYM ( linux_slirp_input );
+PROVIDE_IPXE_SYM ( linux_slirp_pollfds_fill );
+PROVIDE_IPXE_SYM ( linux_slirp_pollfds_poll );