mirror of https://github.com/ipxe/ipxe.git
[slirp] Add libslirp driver for Linux
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>pull/310/head
parent
916ebef198
commit
2b5d3f582f
|
@ -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 $@ $^
|
||||
|
|
|
@ -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,
|
||||
};
|
|
@ -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 )
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
|
@ -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>
|
||||
|
@ -31,7 +32,13 @@
|
|||
#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 );
|
||||
|
|
Loading…
Reference in New Issue