ethernet_slirp: Add libslirp EthernetConnection backend

The existing Ethernet connection implementation is PCAP-based. This
allows bridging your virtual machine directly to the host's network, but
requires elevated permissions on your OS and appropriate drivers which
often aren't available on Wi-Fi cards.

This commit adds a libslirp-based backend that provides just enough
internal networking to handle TCP/IP out of the guest to host, LAN or
Internet. As such things like ICMP, IPX and NetBIOS don't work properly.

The use case for this connection is to give Internet access to the guest
for things like IRC, HTTP and other connections to online services.
This commit is contained in:
Jookia
2021-03-04 10:12:13 +11:00
parent f070d5a706
commit ee071c5853
13 changed files with 600 additions and 12 deletions

View File

@@ -98,7 +98,7 @@ XCode (on macOS, from the Terminal) to target macOS
First install the development tools, headers and libraries needed
sudo apt install automake gcc g++ make libncurses-dev nasm libsdl-net1.2-dev libsdl2-net-dev libpcap-dev fluidsynth libfluidsynth-dev libavdevice58 libavformat-dev libavcodec-dev libavcodec-extra libavcodec-extra58 libswscale-dev libfreetype-dev libxkbfile-dev libxrandr-dev
sudo apt install automake gcc g++ make libncurses-dev nasm libsdl-net1.2-dev libsdl2-net-dev libpcap-dev libslirp-dev fluidsynth libfluidsynth-dev libavdevice58 libavformat-dev libavcodec-dev libavcodec-extra libavcodec-extra58 libswscale-dev libfreetype-dev libxkbfile-dev libxrandr-dev
Then change to the directory where you unpacked the DOSBox-X source code, and run the following commands:
@@ -112,7 +112,7 @@ Alternatively you can also compile the SDL2 version by running the ./build-debug
First install the development tools, headers and libraries needed
sudo dnf group install "C Development Tools and Libraries"
sudo dnf install SDL_net-devel SDL2_net-devel libxkbfile-devel ncurses-devel libpcap-devel libpng-devel fluidsynth-devel freetype-devel nasm
sudo dnf install SDL_net-devel SDL2_net-devel libxkbfile-devel ncurses-devel libpcap-devel libslirp-devel libpng-devel fluidsynth-devel freetype-devel nasm
If you want to be able to record video, you will also need to install ffmpeg-devel which you can get from the optional rpmfusion-free repository.
Then change to the directory where you unpacked the DOSBox-X source code, and run the following commands:

View File

@@ -430,19 +430,12 @@ LDFLAGS="$LDFLAGS -L$pwd/vs2015/sdlnet/linux-host/lib"
CPPFLAGS="$CPPFLAGS -I$pwd -I$pwd/vs2015/sdlnet/linux-host/include -I$pwd/vs2015/sdlnet/linux-host/include/SDL"
CXXFLAGS="$CXXFLAGS -I$pwd -I$pwd/vs2015/sdlnet/linux-host/include -I$pwd/vs2015/sdlnet/linux-host/include/SDL"
dnl LIBRARY TEST: SDLnet initial test
AC_CHECK_HEADER(SDL_net.h,have_sdl_net_h=yes,)
if test x$enable_emscripten != xyes; then
dnl Some target detection and actions for them
case "$host" in
*-*-cygwin* | *-*-mingw32*)
LIBS="$LIBS -lwinmm -ldsound -limm32 -lole32 -loleaut32 -lversion -lsetupapi"
CXXFLAGS="$CXXFLAGS -mno-ms-bitfields"
if test x$have_sdl_net_h = xyes ; then
dnl HACK: We have to put SDL_net THEN winsock libraries or else the linker can't resolve things properly
LIBS="$LIBS -lSDL_net -lwsock32 -lws2_32 -lwinspool -lshlwapi -liphlpapi"
fi
dnl FEATURE: Whether to support direct parallel port passthrough
AC_DEFINE(C_DIRECTLPT, 1, [ Define to 1 if you want parallel passthrough support (Win32, Linux).])
dnl FEATURE: Whether to support direct serial port passthrough
@@ -930,11 +923,13 @@ if test -n "$SDL2_LIBS"; then
AC_DEFINE(C_SDL_NET,1)
AC_DEFINE(C_MODEM,1)
AC_DEFINE(C_IPX,1)
have_sdl_net=yes
elif test x$have_sdl_net_lib = xyes -a x$have_sdl_net_h = xyes ; then
LIBS="$LIBS -lSDL_net"
AC_DEFINE(C_SDL_NET,1)
AC_DEFINE(C_MODEM,1)
AC_DEFINE(C_IPX,1)
have_sdl_net=yes
else
AC_MSG_WARN([Can't find SDL_net, internal modem and ipx disabled])
fi
@@ -944,6 +939,7 @@ else
AC_DEFINE(C_SDL_NET,1)
AC_DEFINE(C_MODEM,1)
AC_DEFINE(C_IPX,1)
have_sdl_net=yes
else
AC_MSG_WARN([Can't find SDL_net, internal modem and ipx disabled])
fi
@@ -992,6 +988,24 @@ case "$host" in
;;
esac
dnl FEATURE: Whether to use libslirp, and enable userspace TCP/IP emulation
AH_TEMPLATE(C_SLIRP, [Define to 1 to enable userspace TCP/IP emulation, requires libslirp])
PKG_CHECK_MODULES(libslirp, slirp, [
have_slirp=yes
AC_DEFINE(C_SLIRP,1)
LIBS="$LIBS ${libslirp_LIBS}"
CFLAGS="$CFLAGS ${libslirp_CFLAGS}"
CPPFLAGS="$CPPFLAGS ${libslirp_CFLAGS}"], [
have_slirp=no
AC_MSG_WARN([Can't find libslirp, userspace TCP/IP emulation disabled])])
case "$host" in
*-*-cygwin* | *-*-mingw32*)
if test x$have_slirp = xyes; then
LIBS="$LIBS -lintl -liconv"
fi
;;
esac
if test x$enable_x11 != xno; then
dnl FEATURE: Whether to use X11 XKBlib
AH_TEMPLATE(C_X11_XKB,[define to 1 if you have XKBlib.h and X11 lib])
@@ -1137,6 +1151,18 @@ fi
dnl placeholder
AH_TEMPLATE(C_ICONV_WIN32,[Define to 1 to use Win32 functions in iconv backend])
dnl If networking is used, add networking libraries last on Windows
dnl so SDL_net, fluidsynth and libslirp successfully link to them
if test x$enable_emscripten != xyes; then
case "$host" in
*-*-cygwin* | *-*-mingw32*)
if test x$have_sdl_net = xyes -o x$enable_libfluidsynth = xyes -o x$have_slirp = xyes; then
LIBS="$LIBS -lwsock32 -lws2_32 -lwinspool -lshlwapi -liphlpapi"
fi
;;
esac
fi
AC_CONFIG_FILES([
Makefile
src/Makefile
@@ -1181,4 +1207,3 @@ chmod +x make-rpm.sh
# HACK: Write all PACKAGE defines to vs2015/config_package.h so Windows builds are kept in sync
grep -E 'PACKAGE|VERSION' config.h > $srcdir/vs2015/config_package.h

View File

@@ -90,6 +90,9 @@
/* Define to 1 to enable ethernet passthrough, requires libpcap */
#define C_PCAP 1
/* Define to 1 to enable userspace TCP/IP emulation, requires libslirp */
//#define C_SLIRP 1
/* Define to 1 to use opengl display output support */
#if !defined(C_SDL2)
#define C_OPENGL 1

View File

@@ -95,6 +95,9 @@
/* Define to 1 to enable ethernet passthrough, requires libpcap */
/* #undef C_PCAP */
/* Define to 1 to enable userspace TCP/IP emulation, requires libslirp */
/* #undef C_SLIRP */
/* Define to 1 to use opengl display output support */
//#define C_OPENGL 1

View File

@@ -117,6 +117,9 @@
/* Define to 1 to enable ethernet passthrough, requires libpcap */
#define C_PCAP 1
/* Define to 1 to enable userspace TCP/IP emulation, requires libslirp */
/* #undef C_SLIRP */
/* Define to 1 to use opengl display output support */
/*#define C_OPENGL 1*/

View File

@@ -136,7 +136,7 @@ INSTALL_SCRIPT = ${INSTALL}
INSTALL_STRIP_PROGRAM = $(install_sh) -c -s
LDFLAGS =
LIBOBJS =
LIBS = -lasound -lm -ldl -lpthread -L/usr/lib -Wl,-rpath,/usr/lib -lSDL -lncurses -lpng -lz -lpcap -lSDL_net -lX11 -lfluidsynth
LIBS = -lasound -lm -ldl -lpthread -L/usr/lib -Wl,-rpath,/usr/lib -lSDL -lncurses -lpng -lz -lpcap -lslirp -lSDL_net -lX11 -lfluidsynth
LTLIBOBJS =
MAKEINFO = ${SHELL} /usr/src/dosbox-x/missing makeinfo
MKDIR_P = /bin/mkdir -p

View File

@@ -3,4 +3,4 @@ AM_CPPFLAGS = -I$(top_srcdir)/include "-DRESDIR=\"$(resdir)\""
resdir = $(datarootdir)/dosbox-x
noinst_LIBRARIES = libmisc.a
libmisc_a_SOURCES = cross.cpp ethernet.cpp ethernet_pcap.cpp messages.cpp programs.cpp setup.cpp support.cpp regionalloctracking.cpp savestates.cpp shiftjis.cpp iconvpp.cpp
libmisc_a_SOURCES = cross.cpp ethernet.cpp ethernet_pcap.cpp ethernet_slirp.cpp messages.cpp programs.cpp setup.cpp support.cpp regionalloctracking.cpp savestates.cpp shiftjis.cpp iconvpp.cpp

View File

@@ -18,6 +18,7 @@
#include "ethernet.h"
#include "ethernet_pcap.h"
#include "ethernet_slirp.h"
#include <cstring>
#include "dosbox.h"
#include "control.h"
@@ -32,6 +33,13 @@ EthernetConnection* OpenEthernetConnection(const std::string& backend)
conn = ((EthernetConnection*)new PcapEthernetConnection);
settings = control->GetSection("ethernet, pcap");
}
#endif
#ifdef C_SLIRP
if (backend == "slirp")
{
conn = ((EthernetConnection*)new SlirpEthernetConnection);
settings = control->GetSection("ethernet, pcap"); /* Dummy section for now */
}
#endif
if (!conn)
{

415
src/misc/ethernet_slirp.cpp Normal file
View File

@@ -0,0 +1,415 @@
/*
* Copyright (C) 2021 The DOSBox Team
*
* 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
* (at your option) 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 "config.h"
#if C_SLIRP
#include "ethernet_slirp.h"
#include <time.h>
#include <algorithm>
#include "dosbox.h"
#ifdef WIN32
#if _WIN32_WINNT < 0x600
/* Very quick Windows XP-compatible inet_pton implementation */
int inet_pton_win(int af, const char* src, void* dst)
{
if(af == AF_INET)
{
unsigned long* num = (unsigned long*)dst;
*num = inet_addr(src);
}
LOG_MSG("SLIRP: inet_pton unimplemented for AF %i (source %s)", af, src);
return -1;
}
#else /* _WIN32_WINNT >= 0x600 */
/* Use proper inet_pton implementation if we have it */
#include <ws2tcpip.h>
#endif /* _WIN32_WINNT */
#else /* !WIN32 */
#include <arpa/inet.h>
#endif /* WIN32 */
/* Begin boilerplate to map libslirp's C-based callbacks to our C++
* object. This is done by passing our SlirpEthernetConnection as user data.
*/
ssize_t slirp_receive_packet(const void* buf, size_t len, void* opaque)
{
SlirpEthernetConnection* conn = (SlirpEthernetConnection*)opaque;
conn->ReceivePacket((const uint8_t*)buf, len);
return len;
}
void slirp_guest_error(const char* msg, void* opaque)
{
(void)opaque;
LOG_MSG("SLIRP: Slirp error: %s", msg);
}
int64_t slirp_clock_get_ns(void* opaque)
{
(void)opaque;
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
/* if clock_gettime fails we have more serious problems */
return ts.tv_nsec + (ts.tv_sec * 1e9);
}
void *slirp_timer_new(SlirpTimerCb cb, void* cb_opaque, void* opaque)
{
SlirpEthernetConnection* conn = (SlirpEthernetConnection*)opaque;
return conn->TimerNew(cb, cb_opaque);
}
void slirp_timer_free(void* timer, void* opaque)
{
SlirpEthernetConnection* conn = (SlirpEthernetConnection*)opaque;
struct slirp_timer *real_timer = (struct slirp_timer*)timer;
conn->TimerFree(real_timer);
}
void slirp_timer_mod(void* timer, int64_t expire_time, void* opaque)
{
SlirpEthernetConnection* conn = (SlirpEthernetConnection*)opaque;
struct slirp_timer *real_timer = (struct slirp_timer*)timer;
conn->TimerMod(real_timer, expire_time);
}
int slirp_add_poll(int fd, int events, void* opaque)
{
SlirpEthernetConnection* conn = (SlirpEthernetConnection*)opaque;
return conn->PollAdd(fd, events);
}
int slirp_get_revents(int idx, void* opaque)
{
SlirpEthernetConnection* conn = (SlirpEthernetConnection*)opaque;
return conn->PollGetSlirpRevents(idx);
}
void slirp_register_poll_fd(int fd, void* opaque)
{
SlirpEthernetConnection* conn = (SlirpEthernetConnection*)opaque;
conn->PollRegister(fd);
}
void slirp_unregister_poll_fd(int fd, void* opaque)
{
SlirpEthernetConnection* conn = (SlirpEthernetConnection*)opaque;
conn->PollUnregister(fd);
}
void slirp_notify(void* opaque)
{
(void)opaque;
return;
}
/* End boilerplate */
SlirpEthernetConnection::SlirpEthernetConnection()
: EthernetConnection()
{
slirp_callbacks.send_packet = slirp_receive_packet;
slirp_callbacks.guest_error = slirp_guest_error;
slirp_callbacks.clock_get_ns = slirp_clock_get_ns;
slirp_callbacks.timer_new = slirp_timer_new;
slirp_callbacks.timer_free = slirp_timer_free;
slirp_callbacks.timer_mod = slirp_timer_mod;
slirp_callbacks.register_poll_fd = slirp_register_poll_fd;
slirp_callbacks.unregister_poll_fd = slirp_unregister_poll_fd;
slirp_callbacks.notify = slirp_notify;
}
SlirpEthernetConnection::~SlirpEthernetConnection()
{
if(slirp) slirp_cleanup(slirp);
}
bool SlirpEthernetConnection::Initialize(Section* dosbox_config)
{
(void)dosbox_config; /* Ignore for now */
LOG_MSG("SLIRP: Slirp version: %s", slirp_version_string());
/* Config */
config.version = 1;
config.restricted = 0; /* Allow access to host */
config.disable_host_loopback = 0; /* Allow access to 127.0.0.1 */
config.if_mtu = 0; /* IF_MTU_DEFAULT */
config.if_mru = 0; /* IF_MRU_DEFAULT */
config.enable_emu = 0; /* Buggy, don't use */
/* IPv4 */
config.in_enabled = 1;
inet_pton(AF_INET, "10.0.2.0", &config.vnetwork);
inet_pton(AF_INET, "255.255.255.0", &config.vnetmask);
inet_pton(AF_INET, "10.0.2.2", &config.vhost);
inet_pton(AF_INET, "10.0.2.3", &config.vnameserver);
inet_pton(AF_INET, "10.0.2.15", &config.vdhcp_start);
/* IPv6 code is left here as reference but disabled as no DOS-era
* software supports it and might get confused by it */
config.in6_enabled = 0;
inet_pton(AF_INET6, "fec0::", &config.vprefix_addr6);
config.vprefix_len = 64;
inet_pton(AF_INET6, "fec0::2", &config.vhost6);
inet_pton(AF_INET6, "fec0::3", &config.vnameserver6);
/* DHCPv4, BOOTP, TFTP */
config.vhostname = "DOSBox-X";
config.vdnssearch = NULL;
config.vdomainname = NULL;
config.tftp_server_name = NULL;
config.tftp_path = NULL;
config.bootfile = NULL;
slirp = slirp_new(&config, &slirp_callbacks, this);
if(slirp)
{
LOG_MSG("SLIRP: Successfully initialized");
return true;
}
else
{
LOG_MSG("SLIRP: Failed to initialize");
return false;
}
}
void SlirpEthernetConnection::SendPacket(const uint8_t* packet, int len)
{
slirp_input(slirp, packet, len);
}
void SlirpEthernetConnection::GetPackets(std::function<void(const uint8_t*, int)> callback)
{
get_packet_callback = callback;
uint32_t timeout_ms = 0;
PollsClear();
PollsAddRegistered();
slirp_pollfds_fill(slirp, &timeout_ms, slirp_add_poll, this);
bool poll_failed = !PollsPoll(timeout_ms);
slirp_pollfds_poll(slirp, poll_failed, slirp_get_revents, this);
TimersRun();
}
void SlirpEthernetConnection::ReceivePacket(const uint8_t* packet, int len)
{
get_packet_callback(packet, len);
}
struct slirp_timer* SlirpEthernetConnection::TimerNew(SlirpTimerCb cb, void *cb_opaque)
{
struct slirp_timer* timer = new struct slirp_timer;
timer->expires = 0;
timer->cb = cb;
timer->cb_opaque = cb_opaque;
timers.push_back(timer);
return timer;
}
void SlirpEthernetConnection::TimerFree(struct slirp_timer* timer)
{
std::remove(timers.begin(), timers.end(), timer);
delete timer;
}
void SlirpEthernetConnection::TimerMod(struct slirp_timer* timer, int64_t expire_time)
{
/* expire_time is in milliseconds despite slirp wanting a nanosecond clock */
timer->expires = expire_time * 1e6;
}
void SlirpEthernetConnection::TimersRun()
{
int64_t now = slirp_clock_get_ns(NULL);
std::for_each(timers.begin(), timers.end(), [now](struct slirp_timer*& timer)
{
if(timer->expires && timer->expires < now)
{
timer->expires = 0;
timer->cb(timer->cb_opaque);
}
});
}
void SlirpEthernetConnection::TimersClear()
{
std::for_each(timers.begin(), timers.end(), [](struct slirp_timer*& timer)
{
delete timer;
});
timers.clear();
}
void SlirpEthernetConnection::PollRegister(int fd)
{
#ifdef WIN32
/* BUG: Skip this entirely on Win32 as libslirp gives us invalid fds. */
return;
#endif
PollUnregister(fd);
registered_fds.push_back(fd);
}
void SlirpEthernetConnection::PollUnregister(int fd)
{
std::remove(registered_fds.begin(), registered_fds.end(), fd);
}
void SlirpEthernetConnection::PollsAddRegistered()
{
std::for_each(registered_fds.begin(), registered_fds.end(), [this](int fd)
{
PollAdd(fd, SLIRP_POLL_IN | SLIRP_POLL_OUT);
});
}
/* Begin the bulk of the platform-specific code.
* This mostly involves handling data structures and mapping
* libslirp's view of our polling system to whatever we use
* internally.
* libslirp really wants poll() as it gives information about
* out of band TCP data and connection hang-ups.
* This is easy to do on Unix, but on other systems it needs
* custom implementations that give this data. */
#ifndef WIN32
void SlirpEthernetConnection::PollsClear()
{
polls.clear();
}
int SlirpEthernetConnection::PollAdd(int fd, int slirp_events)
{
int real_events = 0;
if(slirp_events & SLIRP_POLL_IN) real_events |= POLLIN;
if(slirp_events & SLIRP_POLL_OUT) real_events |= POLLOUT;
if(slirp_events & SLIRP_POLL_PRI) real_events |= POLLPRI;
struct pollfd new_poll;
new_poll.fd = fd;
new_poll.events = real_events;
polls.push_back(new_poll);
return (polls.size() - 1);
}
bool SlirpEthernetConnection::PollsPoll(uint32_t timeout_ms)
{
int ret = poll(polls.data(), polls.size(), timeout_ms);
return (ret > -1);
}
int SlirpEthernetConnection::PollGetSlirpRevents(int idx)
{
int real_revents = polls.at(idx).revents;
int slirp_revents = 0;
if(real_revents & POLLIN) slirp_revents |= SLIRP_POLL_IN;
if(real_revents & POLLOUT) slirp_revents |= SLIRP_POLL_OUT;
if(real_revents & POLLPRI) slirp_revents |= SLIRP_POLL_PRI;
if(real_revents & POLLERR) slirp_revents |= SLIRP_POLL_ERR;
if(real_revents & POLLHUP) slirp_revents |= SLIRP_POLL_HUP;
return slirp_revents;
}
#else
void SlirpEthernetConnection::PollsClear()
{
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_ZERO(&exceptfds);
}
int SlirpEthernetConnection::PollAdd(int fd, int slirp_events)
{
if(slirp_events & SLIRP_POLL_IN) FD_SET(fd, &readfds);
if(slirp_events & SLIRP_POLL_OUT) FD_SET(fd, &writefds);
if(slirp_events & SLIRP_POLL_PRI) FD_SET(fd, &exceptfds);
return fd;
}
bool SlirpEthernetConnection::PollsPoll(uint32_t timeout_ms)
{
struct timeval timeout;
timeout.tv_sec = timeout_ms / 1000;
timeout.tv_usec = (timeout_ms % 1000) * 1000;
int ret = select(0, &readfds, &writefds, &exceptfds, &timeout);
return (ret > -1);
}
int SlirpEthernetConnection::PollGetSlirpRevents(int idx)
{
/* Windows does not support poll(). It has WSAPoll() but this is
* reported as broken by libcurl and other projects, and Microsoft
* doesn't seem to want to fix this any time soon.
* glib provides g_poll() but that doesn't seem to work either.
* The solution I've made uses plain old select(), but checks for
* extra conditions and adds those to the flags we pass to libslirp.
* There's no one-to-one mapping of poll() flags on Windows, so here's
* my definition:
* SLIRP_POLL_HUP: The remote closed the socket gracefully.
* SLIRP_POLL_ERR: An exception happened or reading failed
* SLIRP_POLL_PRI: TCP Out-of-band data available
*/
int slirp_revents = 0;
if(FD_ISSET(idx, &readfds))
{
/* This code is broken on ReactOS peeking a closed socket
* will cause the next recv() to fail instead of acting
* normally. See CORE-17425 on their JIRA */
char buf[8];
int read = recv(idx, buf, sizeof(buf), MSG_PEEK);
int error = (read == SOCKET_ERROR) ? WSAGetLastError() : 0;
if(read > 0 || error == WSAEMSGSIZE)
{
slirp_revents |= SLIRP_POLL_IN;
}
else if(read == 0)
{
slirp_revents |= SLIRP_POLL_IN;
slirp_revents |= SLIRP_POLL_HUP;
}
else
{
slirp_revents |= SLIRP_POLL_IN;
slirp_revents |= SLIRP_POLL_ERR;
}
}
if(FD_ISSET(idx, &writefds))
{
slirp_revents |= SLIRP_POLL_OUT;
}
if(FD_ISSET(idx, &exceptfds))
{
u_long atmark = 0;
if(ioctlsocket(idx, SIOCATMARK, &atmark) == 0 && atmark == 1)
{
slirp_revents |= SLIRP_POLL_PRI;
}
else
{
slirp_revents |= SLIRP_POLL_ERR;
}
}
return slirp_revents;
}
#endif
#endif

120
src/misc/ethernet_slirp.h Normal file
View File

@@ -0,0 +1,120 @@
/*
* Copyright (C) 2021 The DOSBox Team
*
* 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
* (at your option) 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.
*/
#ifndef DOSBOX_ETHERNET_SLIRP_H
#define DOSBOX_ETHERNET_SLIRP_H
#include "config.h"
#if C_SLIRP
#include "ethernet.h"
#include <slirp/libslirp.h>
#include <list>
/*
* libslirp really wants a poll() API, so we'll use that when we're
* not on Windows. When we are on Windows, we'll fall back to using
* select() as well as some Windows APIs.
*
* For reference, QEMU seems to use glib's g_poll() API.
* This doesn't seem to work on Windows from my tests.
*/
#ifndef WIN32
#include <poll.h>
#endif
/** A libslirp timer
* libslirp has to simulate periodic tasks such as IPv6 router
* advertisements. It does this by giving us a callback and expiry
* time. We have to hold on to it and call the callback when the
* time is right.
*/
struct slirp_timer {
int64_t expires; /*!< When to fire the callback, in nanoseconds */
SlirpTimerCb cb; /*!< The callback to fire */
void* cb_opaque; /*!< Data libslirp wants us to pass to the callback */
};
/** A libslirp-based Ethernet connection
* This backend uses a virtual Ethernet device. Only TCP, UDP and some ICMP
* work over this interface. This is because libslirp terminates guest
* connections during routing and passes them to sockets created in the host.
*/
class SlirpEthernetConnection : public EthernetConnection {
public:
/* Boilerplate EthernetConnection interface */
SlirpEthernetConnection();
~SlirpEthernetConnection();
bool Initialize(Section* config);
void SendPacket(const uint8_t* packet, int len);
void GetPackets(std::function<void(const uint8_t*, int)> callback);
/* Called by libslirp when it has a packet for us */
void ReceivePacket(const uint8_t* packet, int len);
/* Called by libslirp to create, free and modify timers */
struct slirp_timer* TimerNew(SlirpTimerCb cb, void *cb_opaque);
void TimerFree(struct slirp_timer* timer);
void TimerMod(struct slirp_timer* timer, int64_t expire_time);
/* Called by libslirp to interact with our polling system */
int PollAdd(int fd, int slirp_events);
int PollGetSlirpRevents(int idx);
void PollRegister(int fd);
void PollUnregister(int fd);
private:
/* Runs and clears all the timers */
void TimersRun();
void TimersClear();
/* Builds a list of descriptors and polls them */
void PollsAddRegistered();
void PollsClear();
bool PollsPoll(uint32_t timeout_ms);
Slirp* slirp = nullptr; /*!< Handle to libslirp */
SlirpConfig config = { 0 }; /*!< Configuration passed to libslirp */
SlirpCb slirp_callbacks = { 0 }; /*!< Callbacks used by libslirp */
std::list<struct slirp_timer*> timers; /*!< Stored timers */
/** The GetPacket callback
* When libslirp has a new packet for us it calls ReceivePacket,
* but the EthernetConnection interface requires users to poll
* for new packets using GetPackets. We temporarily store the
* callback from GetPackets here for ReceivePacket.
* This might seem racy, but keep in mind we control when
* libslirp sends us packets via our polling system.
*/
std::function<void(const uint8_t*, int)> get_packet_callback;
std::list<int> registered_fds; /*!< File descriptors to watch */
#ifndef WIN32
std::vector<struct pollfd> polls; /*!< Descriptors for poll() */
#else
fd_set readfds; /*!< Read descriptors for select() */
fd_set writefds; /*!< Write descriptors for select() */
fd_set exceptfds; /*!< Exceptional descriptors for select() */
#endif
};
#endif
#endif

View File

@@ -133,6 +133,9 @@
/* Define to 1 to enable ethernet passthrough, requires libpcap */
#define C_PCAP 1
/* Define to 1 to enable userspace TCP/IP emulation, requires libslirp */
/* #undef C_SLIRP */
/* Set to 1 to enable SDL 1.x support */
#define C_SDL1 1

View File

@@ -1298,6 +1298,7 @@ copy "$(SolutionDir)\..\contrib\windows\shaders\*.*" "$(OutputPath)\shaders\"</C
<ClCompile Include="..\src\misc\ethernet.cpp" />
<ClCompile Include="..\src\misc\messages.cpp" />
<ClCompile Include="..\src\misc\ethernet_pcap.cpp" />
<ClCompile Include="..\src\misc\ethernet_slirp.cpp" />
<ClCompile Include="..\src\misc\programs.cpp" />
<ClCompile Include="..\src\ints\qcow2_disk.cpp" />
<ClCompile Include="..\src\misc\regionalloctracking.cpp" />
@@ -1640,6 +1641,7 @@ copy "$(SolutionDir)\..\contrib\windows\shaders\*.*" "$(OutputPath)\shaders\"</C
<ClInclude Include="..\src\libs\mt32\TVF.h" />
<ClInclude Include="..\src\libs\mt32\TVP.h" />
<ClInclude Include="..\src\misc\ethernet_pcap.h" />
<ClInclude Include="..\src\misc\ethernet_slirp.h" />
<ClInclude Include="..\src\output\direct3d\d3d_components.h" />
<ClInclude Include="..\src\output\direct3d\direct3d.h" />
<ClInclude Include="..\src\output\direct3d\hq2x_d3d.h" />

View File

@@ -621,6 +621,9 @@
<ClCompile Include="..\src\misc\ethernet_pcap.cpp">
<Filter>Sources\misc</Filter>
</ClCompile>
<ClCompile Include="..\src\misc\ethernet_slirp.cpp">
<Filter>Sources\misc</Filter>
</ClCompile>
<ClCompile Include="..\src\misc\regionalloctracking.cpp">
<Filter>Sources\misc</Filter>
</ClCompile>
@@ -1793,6 +1796,9 @@
<ClInclude Include="..\src\misc\ethernet_pcap.h">
<Filter>Sources\misc</Filter>
</ClInclude>
<ClInclude Include="..\src\misc\ethernet_slirp.h">
<Filter>Sources\misc</Filter>
</ClInclude>
<ClInclude Include="..\src\hardware\reSID\envelope.h">
<Filter>Sources\hardware\reSID</Filter>
</ClInclude>