mirror of
https://github.com/joncampbell123/dosbox-x.git
synced 2025-10-13 17:48:10 +08:00
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:
@@ -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:
|
||||
|
41
configure.ac
41
configure.ac
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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*/
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
415
src/misc/ethernet_slirp.cpp
Normal 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
120
src/misc/ethernet_slirp.h
Normal 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
|
@@ -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
|
||||
|
||||
|
@@ -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" />
|
||||
|
@@ -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>
|
||||
|
Reference in New Issue
Block a user