From 758f4de4d4ef6636b7da0315b1b80db2859d0e9b Mon Sep 17 00:00:00 2001 From: Volker Ruppert Date: Sun, 12 Mar 2017 07:48:08 +0000 Subject: [PATCH] Rewrite of the network driver plugin code. - Removed "pseudo device plugin" containing common code and specific drivers. - Moved network module base classes and module handling to the Bochs core. Now loading network driver plugins in netmod.cc. - Moved shared networking code used by the 'vnet' driver and 'bxhub' from netmod.cc/.h to new files netutil.cc/.h. - Created separate plugins for each network driver implementation. - Modified Bochs plugin system to support new plugin type PLUGTYPE_NETWORK. --- bochs/CHANGES | 2 + bochs/Makefile.in | 10 +- bochs/configure | 8 +- bochs/configure.in | 8 +- bochs/extplugin.h | 1 + bochs/iodev/devices.cc | 8 +- bochs/iodev/iodev.h | 15 - bochs/iodev/network/Makefile.in | 49 +- bochs/iodev/network/eth_fbsd.cc | 15 + bochs/iodev/network/eth_linux.cc | 17 +- bochs/iodev/network/eth_null.cc | 15 + bochs/iodev/network/eth_slirp.cc | 22 +- bochs/iodev/network/eth_socket.cc | 15 + bochs/iodev/network/eth_tap.cc | 21 +- bochs/iodev/network/eth_tuntap.cc | 21 +- bochs/iodev/network/eth_vde.cc | 21 +- bochs/iodev/network/eth_vnet.cc | 24 +- bochs/iodev/network/eth_win32.cc | 17 +- bochs/iodev/network/netmod.cc | 998 ++---------------------------- bochs/iodev/network/netmod.h | 135 +--- bochs/iodev/network/netutil.cc | 856 +++++++++++++++++++++++++ bochs/iodev/network/netutil.h | 149 +++++ bochs/misc/bxhub.cc | 1 + bochs/plugin.cc | 64 +- bochs/plugin.h | 24 +- 25 files changed, 1384 insertions(+), 1132 deletions(-) create mode 100644 bochs/iodev/network/netutil.cc create mode 100644 bochs/iodev/network/netutil.h diff --git a/bochs/CHANGES b/bochs/CHANGES index 100119a68..6bb635ba0 100644 --- a/bochs/CHANGES +++ b/bochs/CHANGES @@ -46,6 +46,8 @@ Detailed change log : - Networking - Added ethernet module 'socket', designed to interconnect Bochs instances with external program 'bxhub' (simulating a multi-port ethernet hub). + - Now creating separate plugins for each network driver implementation + (slirp, win32, etc.). - Sound - Added PCM output data resampling in a separate thread. The resampler requires either libsamplerate or the SoX resampler library installed. diff --git a/bochs/Makefile.in b/bochs/Makefile.in index da53f3f1b..15c272dd4 100644 --- a/bochs/Makefile.in +++ b/bochs/Makefile.in @@ -253,8 +253,8 @@ bximage@EXE@: misc/bximage.o misc/hdimage.o misc/vmware3.o misc/vmware4.o misc/v niclist@EXE@: misc/niclist.o @LINK_CONSOLE@ misc/niclist.o -bxhub@EXE@: misc/bxhub.o misc/netmod.o - @LINK_CONSOLE@ misc/bxhub.o misc/netmod.o @BXHUB_LINK_OPTS@ +bxhub@EXE@: misc/bxhub.o misc/netutil.o + @LINK_CONSOLE@ misc/bxhub.o misc/netutil.o @BXHUB_LINK_OPTS@ # compile with console CXXFLAGS, not gui CXXFLAGS misc/bximage.o: $(srcdir)/misc/bximage.cc $(srcdir)/misc/bswap.h \ @@ -285,9 +285,9 @@ misc/bxhub.o: $(srcdir)/misc/bxhub.cc $(srcdir)/iodev/network/netmod.h \ $(srcdir)/misc/bxcompat.h $(CC) @DASH@c $(BX_INCDIRS) $(CXXFLAGS_CONSOLE) $(srcdir)/misc/bxhub.cc @OFP@$@ -misc/netmod.o: $(srcdir)/iodev/network/netmod.cc $(srcdir)/iodev/network/netmod.h \ - $(srcdir)/misc/bxcompat.h - $(CXX) @DASH@c $(BX_INCDIRS) @BXHUB_FLAG@ $(CXXFLAGS_CONSOLE) $(srcdir)/iodev/network/netmod.cc @OFP@$@ +misc/netutil.o: $(srcdir)/iodev/network/netmod.cc $(srcdir)/iodev/network/netutil.h \ + $(srcdir)/iodev/network/netmod.h $(srcdir)/misc/bxcompat.h + $(CXX) @DASH@c $(BX_INCDIRS) @BXHUB_FLAG@ $(CXXFLAGS_CONSOLE) $(srcdir)/iodev/network/netutil.cc @OFP@$@ # compile with console CFLAGS, not gui CXXFLAGS misc/niclist.o: $(srcdir)/misc/niclist.c diff --git a/bochs/configure b/bochs/configure index db4dd2e04..8dd9951ae 100755 --- a/bochs/configure +++ b/bochs/configure @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Id: configure.in 13079 2017-02-20 18:21:19Z vruppert . +# From configure.in Id: configure.in 13087 2017-02-27 22:26:43Z vruppert . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69. # @@ -23627,7 +23627,7 @@ fi ;; esac if test $can_compile_slirp = 1; then - NETLOW_OBJS="$NETLOW_OBJS eth_slirp.o \$(SLIRP_OBJS)" + NETLOW_OBJS="$NETLOW_OBJS eth_slirp.o" ethernet_modules="$ethernet_modules slirp" $as_echo "#define BX_NETMOD_SLIRP 1" >>confdefs.h @@ -23744,10 +23744,8 @@ fi ;; esac - if test "$bx_plugins" = 0; then - NETWORK_LIB_VAR='iodev/network/libnetwork.a' + NETWORK_LIB_VAR='iodev/network/libnetwork.a' - fi $as_echo "#define BX_NETWORKING 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ethernet modules" >&5 diff --git a/bochs/configure.in b/bochs/configure.in index 79c126e2e..d6b858c29 100644 --- a/bochs/configure.in +++ b/bochs/configure.in @@ -1702,7 +1702,7 @@ if test "$networking" = yes; then ;; esac if test $can_compile_slirp = 1; then - NETLOW_OBJS="$NETLOW_OBJS eth_slirp.o \$(SLIRP_OBJS)" + NETLOW_OBJS="$NETLOW_OBJS eth_slirp.o" ethernet_modules="$ethernet_modules slirp" AC_DEFINE(BX_NETMOD_SLIRP, 1) NETLOW_OBJS="$NETLOW_OBJS eth_socket.o" @@ -1779,10 +1779,8 @@ if test "$networking" = yes; then ;; esac - if test "$bx_plugins" = 0; then - NETWORK_LIB_VAR='iodev/network/libnetwork.a' - AC_SUBST(NETWORK_LIB_VAR) - fi + NETWORK_LIB_VAR='iodev/network/libnetwork.a' + AC_SUBST(NETWORK_LIB_VAR) AC_DEFINE(BX_NETWORKING, 1) AC_MSG_CHECKING(for ethernet modules) AC_MSG_RESULT($ethernet_modules) diff --git a/bochs/extplugin.h b/bochs/extplugin.h index deea04e76..6eff67804 100644 --- a/bochs/extplugin.h +++ b/bochs/extplugin.h @@ -33,6 +33,7 @@ enum plugintype_t { PLUGTYPE_STANDARD, PLUGTYPE_OPTIONAL, PLUGTYPE_SOUND, + PLUGTYPE_NETWORK, PLUGTYPE_USER }; diff --git a/bochs/iodev/devices.cc b/bochs/iodev/devices.cc index b5fc64995..94f52bb53 100644 --- a/bochs/iodev/devices.cc +++ b/bochs/iodev/devices.cc @@ -28,6 +28,7 @@ #include "iodev/virt_timer.h" #include "iodev/slowdown_timer.h" #include "iodev/sound/soundmod.h" +#include "iodev/network/netmod.h" #define LOG_THIS bx_devices. @@ -92,9 +93,6 @@ void bx_devices_c::init_stubs() #if BX_SUPPORT_PCIUSB pluginUsbDevCtl = &stubUsbDevCtl; #endif -#if BX_NETWORKING - pluginNetModCtl = &stubNetModCtl; -#endif } void bx_devices_c::init(BX_MEM_C *newmem) @@ -161,7 +159,7 @@ void bx_devices_c::init(BX_MEM_C *newmem) #if BX_NETWORKING network_enabled = is_network_enabled(); if (network_enabled) - PLUG_load_plugin(netmod, PLUGTYPE_CORE); + bx_netmod_ctl.init(); #endif #if BX_SUPPORT_SOUNDLOW sound_enabled = is_sound_enabled(); @@ -390,7 +388,7 @@ void bx_devices_c::exit() PLUG_unload_plugin(hdimage); #if BX_NETWORKING if (network_enabled) - PLUG_unload_plugin(netmod); + bx_netmod_ctl.exit(); #endif #if BX_SUPPORT_SOUNDLOW if (sound_enabled) diff --git a/bochs/iodev/iodev.h b/bochs/iodev/iodev.h index 9c4fd7fbc..f05e40378 100644 --- a/bochs/iodev/iodev.h +++ b/bochs/iodev/iodev.h @@ -343,15 +343,6 @@ public: } }; -#if BX_NETWORKING -class BOCHSAPI bx_netmod_ctl_stub_c : public bx_devmodel_c { -public: - virtual void* init_module(bx_list_c *base, void* rxh, void* rxstat, bx_devmodel_c *dev) { - STUBFUNC(netmod_ctl, init_module); return NULL; - } -}; -#endif - class BOCHSAPI bx_devices_c : public logfunctions { public: bx_devices_c(); @@ -447,9 +438,6 @@ public: #if BX_SUPPORT_PCIUSB bx_usb_devctl_stub_c *pluginUsbDevCtl; #endif -#if BX_NETWORKING - bx_netmod_ctl_stub_c *pluginNetModCtl; -#endif // stub classes that the pointers (above) can point to until a plugin is // loaded @@ -479,9 +467,6 @@ public: #if BX_SUPPORT_PCIUSB bx_usb_devctl_stub_c stubUsbDevCtl; #endif -#if BX_NETWORKING - bx_netmod_ctl_stub_c stubNetModCtl; -#endif // Some info to pass to devices which can handled bulk IO. This allows // the interface to remain the same for IO devices which can't handle diff --git a/bochs/iodev/network/Makefile.in b/bochs/iodev/network/Makefile.in index fc5c60282..42cfbfc95 100644 --- a/bochs/iodev/network/Makefile.in +++ b/bochs/iodev/network/Makefile.in @@ -79,12 +79,15 @@ SLIRP_OBJS = \ BX_INCDIRS = -I.. -I../.. -I$(srcdir)/.. -I$(srcdir)/../.. -I../../@INSTRUMENT_DIR@ -I$(srcdir)/../../@INSTRUMENT_DIR@ LOCAL_CXXFLAGS = $(MCH_CFLAGS) +OBJS_THAT_CANNOT_BE_PLUGINS = netmod.o + OBJS_THAT_CAN_BE_PLUGINS = \ @NETDEV_OBJS@ \ - netmod.o + $(NETLOW_OBJS) OBJS_THAT_SUPPORT_OTHER_PLUGINS = \ - $(NETLOW_OBJS) + netutil.o \ + $(SLIRP_OBJS) NONPLUGIN_OBJS = @IODEV_EXT_NON_PLUGIN_OBJS@ PLUGIN_OBJS = @IODEV_EXT_PLUGIN_OBJS@ @@ -96,7 +99,8 @@ plugins: @PLUGIN_TARGET_2@ plugins_gcc: $(PLUGIN_OBJS:@PLUGIN_LIBNAME_TRANSFORMATION@) -plugins_msvc: bx_netmod.dll $(NETDEV_DLL_TARGETS) +plugins_msvc: $(NETDEV_DLL_TARGETS) bx_eth_null.dll bx_eth_slirp.dll \ + bx_eth_socket.dll bx_eth_vnet.dll bx_eth_win32.dll libnetwork.a: $(NONPLUGIN_OBJS) @RMCOMMAND@ libnetwork.a @@ -115,16 +119,31 @@ libbx_%.la: %.lo $(LIBTOOL) --mode=link --tag CXX $(CXX) -module $< -o $@ -rpath $(PLUGIN_PATH) # special link rules for plugins that require more than one object file -libbx_netmod.la: netmod.lo $(NETLOW_OBJS:.o=.lo) - $(LIBTOOL) --mode=link --tag CXX $(CXX) -module netmod.lo $(NETLOW_OBJS:.o=.lo) -o libbx_netmod.la -rpath $(PLUGIN_PATH) +libbx_eth_slirp.la: eth_slirp.lo $(SLIRP_OBJS:.o=.lo) + $(LIBTOOL) --mode=link --tag CXX $(CXX) -module eth_slirp.lo $(SLIRP_OBJS:.o=.lo) -o libbx_eth_slirp.la -rpath $(PLUGIN_PATH) + +libbx_eth_vnet.la: eth_vnet.lo netutil.lo + $(LIBTOOL) --mode=link --tag CXX $(CXX) -module eth_vnet.lo netutil.lo -o libbx_eth_vnet.la -rpath $(PLUGIN_PATH) #### building DLLs for win32 (Cygwin and MinGW/MSYS) bx_%.dll: %.o $(CXX) $(CXXFLAGS) -shared -o $@ $< $(WIN32_DLL_IMPORT_LIBRARY) # special link rules for plugins with Cygwin, MinGW/MSYS and MSVC nmake -bx_netmod.dll: netmod.o $(NETLOW_OBJS) - @LINK_DLL@ netmod.o $(NETLOW_OBJS) $(WIN32_DLL_IMPORT_LIBRARY) $(NETMOD_LINK_OPTS@LINK_VAR@) +bx_eth_null.dll: eth_null.o + @LINK_DLL@ eth_null.o $(WIN32_DLL_IMPORT_LIBRARY) + +bx_eth_slirp.dll: eth_slirp.o $(SLIRP_OBJS) + @LINK_DLL@ eth_slirp.o $(SLIRP_OBJS) $(WIN32_DLL_IMPORT_LIBRARY) $(NETMOD_LINK_OPTS@LINK_VAR@) + +bx_eth_socket.dll: eth_socket.o + @LINK_DLL@ eth_socket.o $(WIN32_DLL_IMPORT_LIBRARY) $(NETMOD_LINK_OPTS@LINK_VAR@) + +bx_eth_vnet.dll: eth_vnet.o netutil.o + @LINK_DLL@ eth_vnet.o netutil.o $(WIN32_DLL_IMPORT_LIBRARY) $(NETMOD_LINK_OPTS@LINK_VAR@) + +bx_eth_win32.dll: eth_win32.o + @LINK_DLL@ eth_win32.o $(WIN32_DLL_IMPORT_LIBRARY) $(NETMOD_LINK_OPTS@LINK_VAR@) bx_e1000.dll: e1000.o @LINK_DLL@ e1000.o $(WIN32_DLL_IMPORT_LIBRARY) @@ -218,7 +237,7 @@ eth_vnet.o: eth_vnet.@CPP_SUFFIX@ ../iodev.h ../../bochs.h ../../config.h \ ../../gui/siminterface.h ../../cpudb.h ../../gui/paramtree.h \ ../../memory/memory-bochs.h ../../pc_system.h ../../gui/gui.h \ ../../instrument/stubs/instrument.h ../../plugin.h ../../extplugin.h \ - ../../param_names.h netmod.h + ../../param_names.h netmod.h netutil.h eth_win32.o: eth_win32.@CPP_SUFFIX@ ../iodev.h ../../bochs.h ../../config.h \ ../../osdep.h ../../bx_debug/debug.h ../../config.h ../../osdep.h \ ../../gui/siminterface.h ../../cpudb.h ../../gui/paramtree.h \ @@ -237,6 +256,12 @@ netmod.o: netmod.@CPP_SUFFIX@ ../iodev.h ../../bochs.h ../../config.h ../../osde ../../memory/memory-bochs.h ../../pc_system.h ../../gui/gui.h \ ../../instrument/stubs/instrument.h ../../plugin.h ../../extplugin.h \ ../../param_names.h netmod.h +netutil.o: netutil.@CPP_SUFFIX@ ../iodev.h ../../bochs.h ../../config.h \ + ../../osdep.h ../../bx_debug/debug.h ../../config.h ../../osdep.h \ + ../../gui/siminterface.h ../../cpudb.h ../../gui/paramtree.h \ + ../../memory/memory-bochs.h ../../pc_system.h ../../gui/gui.h \ + ../../instrument/stubs/instrument.h ../../plugin.h ../../extplugin.h \ + ../../param_names.h netmod.h netutil.h pcipnic.o: pcipnic.@CPP_SUFFIX@ ../iodev.h ../../bochs.h ../../config.h \ ../../osdep.h ../../bx_debug/debug.h ../../config.h ../../osdep.h \ ../../gui/siminterface.h ../../cpudb.h ../../gui/paramtree.h \ @@ -428,7 +453,7 @@ eth_vnet.lo: eth_vnet.@CPP_SUFFIX@ ../iodev.h ../../bochs.h ../../config.h \ ../../gui/siminterface.h ../../cpudb.h ../../gui/paramtree.h \ ../../memory/memory-bochs.h ../../pc_system.h ../../gui/gui.h \ ../../instrument/stubs/instrument.h ../../plugin.h ../../extplugin.h \ - ../../param_names.h netmod.h + ../../param_names.h netmod.h netutil.h eth_win32.lo: eth_win32.@CPP_SUFFIX@ ../iodev.h ../../bochs.h ../../config.h \ ../../osdep.h ../../bx_debug/debug.h ../../config.h ../../osdep.h \ ../../gui/siminterface.h ../../cpudb.h ../../gui/paramtree.h \ @@ -447,6 +472,12 @@ netmod.lo: netmod.@CPP_SUFFIX@ ../iodev.h ../../bochs.h ../../config.h ../../osd ../../memory/memory-bochs.h ../../pc_system.h ../../gui/gui.h \ ../../instrument/stubs/instrument.h ../../plugin.h ../../extplugin.h \ ../../param_names.h netmod.h +netutil.lo: netutil.@CPP_SUFFIX@ ../iodev.h ../../bochs.h ../../config.h \ + ../../osdep.h ../../bx_debug/debug.h ../../config.h ../../osdep.h \ + ../../gui/siminterface.h ../../cpudb.h ../../gui/paramtree.h \ + ../../memory/memory-bochs.h ../../pc_system.h ../../gui/gui.h \ + ../../instrument/stubs/instrument.h ../../plugin.h ../../extplugin.h \ + ../../param_names.h netmod.h netutil.h pcipnic.lo: pcipnic.@CPP_SUFFIX@ ../iodev.h ../../bochs.h ../../config.h \ ../../osdep.h ../../bx_debug/debug.h ../../config.h ../../osdep.h \ ../../gui/siminterface.h ../../cpudb.h ../../gui/paramtree.h \ diff --git a/bochs/iodev/network/eth_fbsd.cc b/bochs/iodev/network/eth_fbsd.cc index 6afee3d04..f2a78bb5d 100644 --- a/bochs/iodev/network/eth_fbsd.cc +++ b/bochs/iodev/network/eth_fbsd.cc @@ -53,6 +53,21 @@ #if BX_NETWORKING && BX_NETMOD_FBSD +// network driver plugin entry points + +int CDECL libfbsd_net_plugin_init(plugin_t *plugin, plugintype_t type) +{ + // Nothing here yet + return 0; // Success +} + +void CDECL libfbsd_net_plugin_fini(void) +{ + // Nothing here yet +} + +// network driver implementation + #define LOG_THIS netdev-> #define BX_ETH_FBSD_LOGGING 0 diff --git a/bochs/iodev/network/eth_linux.cc b/bochs/iodev/network/eth_linux.cc index 707a655f4..a2cfe2c11 100644 --- a/bochs/iodev/network/eth_linux.cc +++ b/bochs/iodev/network/eth_linux.cc @@ -2,7 +2,7 @@ // $Id$ ///////////////////////////////////////////////////////////////////////// // -// Copyright (C) 2001-2011 The Bochs Project +// Copyright (C) 2001-2017 The Bochs Project // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -45,6 +45,21 @@ #if BX_NETWORKING && BX_NETMOD_LINUX +// network driver plugin entry points + +int CDECL liblinux_net_plugin_init(plugin_t *plugin, plugintype_t type) +{ + // Nothing here yet + return 0; // Success +} + +void CDECL liblinux_net_plugin_fini(void) +{ + // Nothing here yet +} + +// network driver implementation + #define LOG_THIS netdev-> extern "C" { diff --git a/bochs/iodev/network/eth_null.cc b/bochs/iodev/network/eth_null.cc index 472fd6e92..fb022578a 100644 --- a/bochs/iodev/network/eth_null.cc +++ b/bochs/iodev/network/eth_null.cc @@ -36,6 +36,21 @@ #if BX_NETWORKING +// network driver plugin entry points + +int CDECL libnull_net_plugin_init(plugin_t *plugin, plugintype_t type) +{ + // Nothing here yet + return 0; // Success +} + +void CDECL libnull_net_plugin_fini(void) +{ + // Nothing here yet +} + +// network driver implementation + #define LOG_THIS netdev-> #define BX_ETH_NULL_LOGGING 1 diff --git a/bochs/iodev/network/eth_slirp.cc b/bochs/iodev/network/eth_slirp.cc index 01fd659dd..784b7dc3c 100644 --- a/bochs/iodev/network/eth_slirp.cc +++ b/bochs/iodev/network/eth_slirp.cc @@ -2,7 +2,7 @@ // $Id$ ///////////////////////////////////////////////////////////////////////// // -// Copyright (C) 2014-2016 The Bochs Project +// Copyright (C) 2014-2017 The Bochs Project // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -31,12 +31,28 @@ #include "slirp/slirp.h" #include "slirp/libslirp.h" +static unsigned int bx_slirp_instances; + +// network driver plugin entry points + +int CDECL libslirp_net_plugin_init(plugin_t *plugin, plugintype_t type) +{ + bx_slirp_instances = 0; + return 0; // Success +} + +void CDECL libslirp_net_plugin_fini(void) +{ + // Nothing here yet +} + +// network driver implementation + #define LOG_THIS netdev-> #define MAX_HOSTFWD 5 static int rx_timer_index = BX_NULL_TIMER_HANDLE; -static unsigned int bx_slirp_instances = 0; fd_set rfds, wfds, xfds; int nfds; @@ -382,7 +398,7 @@ void slirp_output(void *this_ptr, const Bit8u *pkt, int pkt_len) void bx_slirp_pktmover_c::receive(void *pkt, unsigned pkt_len) { if (this->rxstat(this->netdev) & BX_NETDEV_RXREADY) { - if (pkt_len < 60) pkt_len = 60; + if (pkt_len < MIN_RX_PACKET_LEN) pkt_len = MIN_RX_PACKET_LEN; this->rxh(this->netdev, pkt, pkt_len); } else { BX_ERROR(("device not ready to receive data")); diff --git a/bochs/iodev/network/eth_socket.cc b/bochs/iodev/network/eth_socket.cc index 335c3f43d..ddb3e4bd9 100644 --- a/bochs/iodev/network/eth_socket.cc +++ b/bochs/iodev/network/eth_socket.cc @@ -57,6 +57,21 @@ #if BX_NETWORKING && BX_NETMOD_SOCKET +// network driver plugin entry points + +int CDECL libsocket_net_plugin_init(plugin_t *plugin, plugintype_t type) +{ + // Nothing here yet + return 0; // Success +} + +void CDECL libsocket_net_plugin_fini(void) +{ + // Nothing here yet +} + +// network driver implementation + #define LOG_THIS netdev-> extern "C" { diff --git a/bochs/iodev/network/eth_tap.cc b/bochs/iodev/network/eth_tap.cc index 49d3d6ea0..510357cf5 100644 --- a/bochs/iodev/network/eth_tap.cc +++ b/bochs/iodev/network/eth_tap.cc @@ -88,6 +88,21 @@ #if BX_NETWORKING && BX_NETMOD_TAP +// network driver plugin entry points + +int CDECL libtap_net_plugin_init(plugin_t *plugin, plugintype_t type) +{ + // Nothing here yet + return 0; // Success +} + +void CDECL libtap_net_plugin_fini(void) +{ + // Nothing here yet +} + +// network driver implementation + #define LOG_THIS netdev-> #include @@ -406,9 +421,9 @@ void bx_tap_pktmover_c::rx_timer() } #endif BX_DEBUG(("eth_tap: got packet: %d bytes, dst=%x:%x:%x:%x:%x:%x, src=%x:%x:%x:%x:%x:%x\n", nbytes, rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3], rxbuf[4], rxbuf[5], rxbuf[6], rxbuf[7], rxbuf[8], rxbuf[9], rxbuf[10], rxbuf[11])); - if (nbytes < 60) { - BX_INFO(("packet too short (%d), padding to 60", nbytes)); - nbytes = 60; + if (nbytes < MIN_RX_PACKET_LEN) { + BX_INFO(("packet too short (%d), padding to %d", nbytes, MIN_RX_PACKET_LEN)); + nbytes = MIN_RX_PACKET_LEN; } if (this->rxstat(this->netdev) & BX_NETDEV_RXREADY) { this->rxh(this->netdev, rxbuf, nbytes); diff --git a/bochs/iodev/network/eth_tuntap.cc b/bochs/iodev/network/eth_tuntap.cc index ff65e9bf1..c1c705f3a 100644 --- a/bochs/iodev/network/eth_tuntap.cc +++ b/bochs/iodev/network/eth_tuntap.cc @@ -32,6 +32,21 @@ #if BX_NETWORKING && BX_NETMOD_TUNTAP +// network driver plugin entry points + +int CDECL libtuntap_net_plugin_init(plugin_t *plugin, plugintype_t type) +{ + // Nothing here yet + return 0; // Success +} + +void CDECL libtuntap_net_plugin_fini(void) +{ + // Nothing here yet +} + +// network driver implementation + #define LOG_THIS netdev-> #include @@ -331,9 +346,9 @@ void bx_tuntap_pktmover_c::rx_timer() } #endif BX_DEBUG(("eth_tuntap: got packet: %d bytes, dst=%02x:%02x:%02x:%02x:%02x:%02x, src=%02x:%02x:%02x:%02x:%02x:%02x", nbytes, rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3], rxbuf[4], rxbuf[5], rxbuf[6], rxbuf[7], rxbuf[8], rxbuf[9], rxbuf[10], rxbuf[11])); - if (nbytes < 60) { - BX_INFO(("packet too short (%d), padding to 60", nbytes)); - nbytes = 60; + if (nbytes < MIN_RX_PACKET_LEN) { + BX_INFO(("packet too short (%d), padding to %d", nbytes, MIN_RX_PACKET_LEN)); + nbytes = MIN_RX_PACKET_LEN; } if (this->rxstat(this->netdev) & BX_NETDEV_RXREADY) { this->rxh(this->netdev, rxbuf, nbytes); diff --git a/bochs/iodev/network/eth_vde.cc b/bochs/iodev/network/eth_vde.cc index 26237bb48..e3dcbea65 100644 --- a/bochs/iodev/network/eth_vde.cc +++ b/bochs/iodev/network/eth_vde.cc @@ -34,6 +34,21 @@ #if BX_NETWORKING && BX_NETMOD_VDE +// network driver plugin entry points + +int CDECL libvde_net_plugin_init(plugin_t *plugin, plugintype_t type) +{ + // Nothing here yet + return 0; // Success +} + +void CDECL libvde_net_plugin_fini(void) +{ + // Nothing here yet +} + +// network driver implementation + #define LOG_THIS netdev-> #include @@ -250,9 +265,9 @@ void bx_vde_pktmover_c::rx_timer() } #endif BX_DEBUG(("eth_vde: got packet: %d bytes, dst=%x:%x:%x:%x:%x:%x, src=%x:%x:%x:%x:%x:%x\n", nbytes, rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3], rxbuf[4], rxbuf[5], rxbuf[6], rxbuf[7], rxbuf[8], rxbuf[9], rxbuf[10], rxbuf[11])); - if (nbytes < 60) { - BX_INFO(("packet too short (%d), padding to 60", nbytes)); - nbytes = 60; + if (nbytes < MIN_RX_PACKET_LEN) { + BX_INFO(("packet too short (%d), padding to %d", nbytes, MIN_RX_PACKET_LEN)); + nbytes = MIN_RX_PACKET_LEN; } if (this->rxstat(this->netdev) & BX_NETDEV_RXREADY) { this->rxh(this->netdev, rxbuf, nbytes); diff --git a/bochs/iodev/network/eth_vnet.cc b/bochs/iodev/network/eth_vnet.cc index b9f0a7449..12fe3f349 100644 --- a/bochs/iodev/network/eth_vnet.cc +++ b/bochs/iodev/network/eth_vnet.cc @@ -2,7 +2,7 @@ // $Id$ ///////////////////////////////////////////////////////////////////////// // -// Copyright (C) 2005-2017 The Bochs Project +// Copyright (C) 2004-2017 The Bochs Project // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -35,9 +35,27 @@ #include "iodev.h" #include "netmod.h" +#include "netutil.h" #if BX_NETWORKING +static unsigned int bx_vnet_instances; + +// network driver plugin entry points + +int CDECL libvnet_net_plugin_init(plugin_t *plugin, plugintype_t type) +{ + bx_vnet_instances = 0; + return 0; // Success +} + +void CDECL libvnet_net_plugin_fini(void) +{ + // Nothing here yet +} + +// network driver implementation + #define LOG_THIS netdev-> #define BX_ETH_VNET_LOGGING 1 @@ -53,8 +71,6 @@ #include #endif -static unsigned int bx_vnet_instances = 0; - ///////////////////////////////////////////////////////////////////////// // handler to send/receive packets ///////////////////////////////////////////////////////////////////////// @@ -347,7 +363,7 @@ void bx_vnet_pktmover_c::rx_timer(void) void bx_vnet_pktmover_c::host_to_guest(Bit8u *buf, unsigned io_len, unsigned l3type) { - Bit8u localbuf[60]; + Bit8u localbuf[MIN_RX_PACKET_LEN]; if (io_len < 14) { BX_PANIC(("host_to_guest: io_len < 14!")); diff --git a/bochs/iodev/network/eth_win32.cc b/bochs/iodev/network/eth_win32.cc index 4428c01c5..693152b4c 100644 --- a/bochs/iodev/network/eth_win32.cc +++ b/bochs/iodev/network/eth_win32.cc @@ -42,6 +42,21 @@ #if BX_NETWORKING && BX_NETMOD_WIN32 +// network driver plugin entry points + +int CDECL libwin32_net_plugin_init(plugin_t *plugin, plugintype_t type) +{ + // Nothing here yet + return 0; // Success +} + +void CDECL libwin32_net_plugin_fini(void) +{ + // Nothing here yet +} + +// network driver implementation + #ifndef __CYGWIN__ #include #endif @@ -391,7 +406,7 @@ void bx_win32_pktmover_c::rx_timer(void) if(memcmp(pPacket, cMacAddr, 6) == 0 || memcmp(pPacket, broadcast_macaddr, 6) == 0) { pktlen = hdr->bh_caplen; - if (pktlen < 60) pktlen = 60; + if (pktlen < MIN_RX_PACKET_LEN) pktlen = MIN_RX_PACKET_LEN; #if BX_ETH_WIN32_LOGGING write_pktlog_txt(pktlog_txt, pPacket, pktlen, 1); #endif diff --git a/bochs/iodev/network/netmod.cc b/bochs/iodev/network/netmod.cc index 11c873894..50bd4c479 100644 --- a/bochs/iodev/network/netmod.cc +++ b/bochs/iodev/network/netmod.cc @@ -24,62 +24,40 @@ // Peter Grehan (grehan@iprg.nokia.com) coded the initial version of the // NE2000/ether stuff. -// Define BX_PLUGGABLE in files that can be compiled into plugins. For -// platforms that require a special tag on exported symbols, BX_PLUGGABLE -// is used to know when we are exporting symbols and when we are importing. -#define BX_PLUGGABLE - -#ifdef BXHUB -#include "config.h" -#else #include "iodev.h" -#endif #if BX_NETWORKING #include "netmod.h" -#if !defined(WIN32) || defined(__CYGWIN__) -#include /* ntohs, htons */ -#else -#include -#endif +#define LOG_THIS bx_netmod_ctl. -#ifdef BXHUB -#include "misc/bxcompat.h" -#else - -#define LOG_THIS theNetModCtl-> - -bx_netmod_ctl_c* theNetModCtl = NULL; - -int CDECL libnetmod_LTX_plugin_init(plugin_t *plugin, plugintype_t type) -{ - if (type == PLUGTYPE_CORE) { - theNetModCtl = new bx_netmod_ctl_c; - bx_devices.pluginNetModCtl = theNetModCtl; - return 0; // Success - } else { - return -1; - } -} - -void CDECL libnetmod_LTX_plugin_fini(void) -{ - delete theNetModCtl; -} +bx_netmod_ctl_c bx_netmod_ctl; bx_netmod_ctl_c::bx_netmod_ctl_c() { put("netmodctl", "NETCTL"); } +void bx_netmod_ctl_c::init(void) +{ + // Nothing here yet +} + +void bx_netmod_ctl_c::exit(void) +{ + eth_locator_c::cleanup(); +} + void* bx_netmod_ctl_c::init_module(bx_list_c *base, void *rxh, void *rxstat, bx_devmodel_c *netdev) { eth_pktmover_c *ethmod; // Attach to the selected ethernet module const char *modname = SIM->get_param_enum("ethmod", base)->get_selected(); + if (!eth_locator_c::module_present(modname)) { + PLUG_load_net_plugin(modname); + } ethmod = eth_locator_c::create(modname, SIM->get_param_string("ethdev", base)->getptr(), (const char *) SIM->get_param_string("mac", base)->getptr(), @@ -113,32 +91,46 @@ eth_locator_c::eth_locator_c(const char *type) this->type = type; } -extern class bx_null_locator_c bx_null_match; -#if BX_NETMOD_FBSD -extern class bx_fbsd_locator_c bx_fbsd_match; +eth_locator_c::~eth_locator_c() +{ + eth_locator_c *ptr = 0; + + if (this == all) { + all = all->next; + } else { + ptr = all; + while (ptr != NULL) { + if (ptr->next != this) { + ptr = ptr->next; + } else { + break; + } + } + } + if (ptr) { + ptr->next = this->next; + } +} + +bx_bool eth_locator_c::module_present(const char *type) +{ + eth_locator_c *ptr = 0; + + for (ptr = all; ptr != NULL; ptr = ptr->next) { + if (strcmp(type, ptr->type) == 0) + return 1; + } + return 0; +} + +void eth_locator_c::cleanup() +{ +#if BX_PLUGINS + while (all != NULL) { + PLUG_unload_net_plugin(all->type); + } #endif -#if BX_NETMOD_LINUX -extern class bx_linux_locator_c bx_linux_match; -#endif -#if BX_NETMOD_WIN32 -extern class bx_win32_locator_c bx_win32_match; -#endif -#if BX_NETMOD_TAP -extern class bx_tap_locator_c bx_tap_match; -#endif -#if BX_NETMOD_TUNTAP -extern class bx_tuntap_locator_c bx_tuntap_match; -#endif -#if BX_NETMOD_VDE -extern class bx_vde_locator_c bx_vde_match; -#endif -#if BX_NETMOD_SLIRP -extern class bx_slirp_locator_c bx_slirp_match; -#endif -#if BX_NETMOD_SOCKET -extern class bx_socket_locator_c bx_socket_match; -#endif -extern class bx_vnet_locator_c bx_vnet_match; +} // // Called by ethernet chip emulations to locate and create a pktmover @@ -150,73 +142,13 @@ eth_locator_c::create(const char *type, const char *netif, eth_rx_handler_t rxh, eth_rx_status_t rxstat, bx_devmodel_c *dev, const char *script) { -#ifdef eth_static_constructors - for (eth_locator_c *p = all; p != NULL; p = p->next) { - if (strcmp(type, p->type) == 0) - return (p->allocate(netif, macaddr, rxh, rxstat, dev, script)); - } -#else eth_locator_c *ptr = 0; - if (!strcmp(type, "null")) { - ptr = (eth_locator_c *) &bx_null_match; + for (ptr = all; ptr != NULL; ptr = ptr->next) { + if (strcmp(type, ptr->type) == 0) + return (ptr->allocate(netif, macaddr, rxh, rxstat, dev, script)); } -#if BX_NETMOD_FBSD - { - if (!strcmp(type, "fbsd")) - ptr = (eth_locator_c *) &bx_fbsd_match; - } -#endif -#if BX_NETMOD_LINUX - { - if (!strcmp(type, "linux")) - ptr = (eth_locator_c *) &bx_linux_match; - } -#endif -#if BX_NETMOD_TUNTAP - { - if (!strcmp(type, "tuntap")) - ptr = (eth_locator_c *) &bx_tuntap_match; - } -#endif -#if BX_NETMOD_VDE - { - if (!strcmp(type, "vde")) - ptr = (eth_locator_c *) &bx_vde_match; - } -#endif -#if BX_NETMOD_SLIRP - { - if (!strcmp(type, "slirp")) - ptr = (eth_locator_c *) &bx_slirp_match; - } -#endif -#if BX_NETMOD_TAP - { - if (!strcmp(type, "tap")) - ptr = (eth_locator_c *) &bx_tap_match; - } -#endif -#if BX_NETMOD_WIN32 - { - if(!strcmp(type, "win32")) - ptr = (eth_locator_c *) &bx_win32_match; - } -#endif -#if BX_NETMOD_SOCKET - if (!strcmp(type, "socket")) { - ptr = (eth_locator_c *) &bx_socket_match; - } -#endif - if (!strcmp(type, "vnet")) { - ptr = (eth_locator_c *) &bx_vnet_match; - } - if (ptr) { - return (ptr->allocate(netif, macaddr, rxh, rxstat, dev, script)); - } -#endif - - return (NULL); + return NULL; } #if (BX_NETMOD_TAP==1) || (BX_NETMOD_TUNTAP==1) || (BX_NETMOD_VDE==1) @@ -291,819 +223,5 @@ void write_pktlog_txt(FILE *pktlog_txt, const Bit8u *buf, unsigned len, bx_bool fprintf(pktlog_txt, "--\n"); fflush(pktlog_txt); } -#endif - -Bit16u ip_checksum(const Bit8u *buf, unsigned buf_len) -{ - Bit32u sum = 0; - unsigned n; - - for (n = 0; n < buf_len; n++) { - if (n & 1) { - sum += (Bit32u)(*buf++); - } else { - sum += (Bit32u)(*buf++) << 8; - } - } - while (sum > 0xffff) { - sum = (sum >> 16) + (sum & 0xffff); - } - - return (Bit16u)sum; -} - -// DHCP server - -#define BOOTREQUEST 1 -#define BOOTREPLY 2 - -#define BOOTPOPT_PADDING 0 -#define BOOTPOPT_END 255 -#define BOOTPOPT_SUBNETMASK 1 -#define BOOTPOPT_TIMEOFFSET 2 -#define BOOTPOPT_ROUTER_OPTION 3 -#define BOOTPOPT_DOMAIN_NAMESERVER 6 -#define BOOTPOPT_HOST_NAME 12 -#define BOOTPOPT_DOMAIN_NAME 15 -#define BOOTPOPT_MAX_DATAGRAM_SIZE 22 -#define BOOTPOPT_DEFAULT_IP_TTL 23 -#define BOOTPOPT_BROADCAST_ADDRESS 28 -#define BOOTPOPT_ARPCACHE_TIMEOUT 35 -#define BOOTPOPT_DEFAULT_TCP_TTL 37 -#define BOOTPOPT_NTP_SERVER 42 -#define BOOTPOPT_NETBIOS_NAMESERVER 44 -#define BOOTPOPT_X_FONTSERVER 48 -#define BOOTPOPT_REQUESTED_IP_ADDRESS 50 -#define BOOTPOPT_IP_ADDRESS_LEASE_TIME 51 -#define BOOTPOPT_OPTION_OVRLOAD 52 -#define BOOTPOPT_DHCP_MESSAGETYPE 53 -#define BOOTPOPT_SERVER_IDENTIFIER 54 -#define BOOTPOPT_PARAMETER_REQUEST_LIST 55 -#define BOOTPOPT_MAX_DHCP_MESSAGE_SIZE 57 -#define BOOTPOPT_RENEWAL_TIME 58 -#define BOOTPOPT_REBINDING_TIME 59 -#define BOOTPOPT_CLASS_IDENTIFIER 60 -#define BOOTPOPT_CLIENT_IDENTIFIER 61 - -#define DHCPDISCOVER 1 -#define DHCPOFFER 2 -#define DHCPREQUEST 3 -#define DHCPDECLINE 4 -#define DHCPACK 5 -#define DHCPNAK 6 -#define DHCPRELEASE 7 -#define DHCPINFORM 8 - -#define DEFAULT_LEASE_TIME 28800 - -// TFTP server support by EaseWay - -#define TFTP_RRQ 1 -#define TFTP_WRQ 2 -#define TFTP_DATA 3 -#define TFTP_ACK 4 -#define TFTP_ERROR 5 -#define TFTP_OPTACK 6 - -#define TFTP_OPTION_OCTET 0x1 -#define TFTP_OPTION_BLKSIZE 0x2 -#define TFTP_OPTION_TSIZE 0x4 -#define TFTP_OPTION_TIMEOUT 0x8 - -#define TFTP_DEFAULT_BLKSIZE 512 -#define TFTP_DEFAULT_TIMEOUT 5 - -static const Bit8u subnetmask_ipv4addr[4] = {0xff,0xff,0xff,0x00}; -static const Bit8u broadcast_ipv4addr1[4] = {0xff,0xff,0xff,0xff}; - -void vnet_prepare_reply(Bit8u *replybuf, unsigned l3type, dhcp_cfg_t *dhcpc) -{ - ethernet_header_t *ethhdr = (ethernet_header_t *)replybuf; - - memcpy(ethhdr->dst_mac_addr, dhcpc->guest_macaddr, ETHERNET_MAC_ADDR_LEN); - memcpy(ethhdr->src_mac_addr, dhcpc->host_macaddr, ETHERNET_MAC_ADDR_LEN); - ethhdr->type = htons(l3type); -} - -bx_bool vnet_process_arp_request(const Bit8u *buf, Bit8u *reply, dhcp_cfg_t *dhcp) -{ - arp_header_t *arprhdr = (arp_header_t *)(reply + sizeof(ethernet_header_t)); - - if (!memcmp(&buf[22], dhcp->guest_macaddr, 6)) { - memcpy(dhcp->guest_ipv4addr, &buf[28], 4); - if (!memcmp(&buf[38], dhcp->host_ipv4addr, 4)) { - memset(reply, 0, MIN_RX_PACKET_LEN); - memcpy(arprhdr, &buf[14], 6); - arprhdr->opcode = htons(ARP_OPCODE_REPLY); - memcpy((Bit8u *)arprhdr+8, dhcp->host_macaddr, ETHERNET_MAC_ADDR_LEN); - memcpy((Bit8u *)arprhdr+14, dhcp->host_ipv4addr, 4); - memcpy((Bit8u *)arprhdr+18, dhcp->guest_macaddr, ETHERNET_MAC_ADDR_LEN); - memcpy((Bit8u *)arprhdr+24, dhcp->guest_ipv4addr, 4); - return 1; - } - } - return 0; -} - -bx_bool vnet_process_icmp_echo(const Bit8u *l3pkt, unsigned l3header_len, - const Bit8u *l4pkt, unsigned l4pkt_len, - Bit8u *reply) -{ - if ((14U+l3header_len+l4pkt_len) > ICMP_ECHO_PACKET_MAX) { - return 0; - } - memcpy(&reply[14], l3pkt, l3header_len); - memcpy(&reply[14+l3header_len], l4pkt, l4pkt_len); - reply[14+l3header_len+0] = 0x00; // echo reply - put_net2(&reply[14+l3header_len+2],0); - put_net2(&reply[14+l3header_len+2], - ip_checksum(&reply[14+l3header_len],l4pkt_len) ^ (Bit16u)0xffff); - return 1; -} - -int vnet_process_dhcp(bx_devmodel_c *netdev, const Bit8u *data, unsigned data_len, Bit8u *reply, dhcp_cfg_t *dhcp) -{ - const Bit8u *opts; - unsigned opts_len; - unsigned extcode; - unsigned extlen; - const Bit8u *extdata; - unsigned dhcpmsgtype = 0; - bx_bool found_serverid = false; - bx_bool found_leasetime = false; - bx_bool found_guest_ipaddr = false; - bx_bool found_host_name = false; - Bit32u leasetime = BX_MAX_BIT32U; - const Bit8u *dhcpreqparams = NULL; - unsigned dhcpreqparams_len = 0; - Bit8u dhcpreqparam_default[8]; - bx_bool dhcpreqparam_default_validflag = false; - unsigned dhcpreqparams_default_len = 0; - Bit8u *replyopts; - Bit8u replybuf[576]; - char *hostname = NULL; - unsigned hostname_len = 0; - - if (data_len < (236U+4U)) return 0; - if (data[0] != BOOTREQUEST) return 0; - if (data[1] != 1 || data[2] != 6) return 0; - if (memcmp(&data[28U], dhcp->guest_macaddr, 6)) return 0; - if (data[236] != 0x63 || data[237] != 0x82 || - data[238] != 0x53 || data[239] != 0x63) return 0; - - opts = &data[240]; - opts_len = data_len - 240U; - - while (1) { - if (opts_len < 1) { - BX_ERROR(("dhcp: invalid request")); - return 0; - } - extcode = *opts++; - opts_len--; - - if (extcode == BOOTPOPT_PADDING) continue; - if (extcode == BOOTPOPT_END) break; - if (opts_len < 1) { - BX_ERROR(("dhcp: invalid request")); - return 0; - } - extlen = *opts++; - opts_len--; - if (opts_len < extlen) { - BX_ERROR(("dhcp: invalid request")); - return 0; - } - extdata = opts; - opts += extlen; - opts_len -= extlen; - - switch (extcode) - { - case BOOTPOPT_DHCP_MESSAGETYPE: - if (extlen != 1) - break; - dhcpmsgtype = *extdata; - break; - case BOOTPOPT_PARAMETER_REQUEST_LIST: - if (extlen < 1) - break; - dhcpreqparams = extdata; - dhcpreqparams_len = extlen; - break; - case BOOTPOPT_SERVER_IDENTIFIER: - if (extlen != 4) - break; - if (memcmp(extdata, dhcp->host_ipv4addr, 4)) { - BX_INFO(("dhcp: request to another server")); - return 0; - } - found_serverid = true; - break; - case BOOTPOPT_IP_ADDRESS_LEASE_TIME: - if (extlen != 4) - break; - leasetime = get_net4(&extdata[0]); - found_leasetime = true; - break; - case BOOTPOPT_REQUESTED_IP_ADDRESS: - if (extlen != 4) - break; - if (!memcmp(extdata, dhcp->default_guest_ipv4addr,4)) { - found_guest_ipaddr = true; - memcpy(dhcp->guest_ipv4addr, dhcp->default_guest_ipv4addr, 4); - } - break; - case BOOTPOPT_HOST_NAME: - if (extlen < 1) - break; - hostname = (char*)malloc(extlen); - memcpy(hostname, extdata, extlen); - hostname_len = extlen; - found_host_name = true; - break; - default: - BX_ERROR(("extcode %d not supported yet", extcode)); - break; - } - } - - memset(&dhcpreqparam_default,0,sizeof(dhcpreqparam_default)); - memset(&replybuf[0],0,sizeof(replybuf)); - replybuf[0] = BOOTREPLY; - replybuf[1] = 1; - replybuf[2] = 6; - memcpy(&replybuf[4],&data[4],4); - memcpy(&replybuf[16], dhcp->default_guest_ipv4addr, 4); - memcpy(&replybuf[20], dhcp->host_ipv4addr, 4); - memcpy(&replybuf[28],&data[28],6); - memcpy(&replybuf[44],"vnet",4); - memcpy(&replybuf[108],"pxelinux.0",10); - replybuf[236] = 0x63; - replybuf[237] = 0x82; - replybuf[238] = 0x53; - replybuf[239] = 0x63; - replyopts = &replybuf[240]; - opts_len = sizeof(replybuf)/sizeof(replybuf[0])-240; - switch (dhcpmsgtype) { - case DHCPDISCOVER: - BX_DEBUG(("dhcp server: DHCPDISCOVER")); - // reset guest address; answer must be broadcasted to unconfigured IP - memcpy(dhcp->guest_ipv4addr, broadcast_ipv4addr1, 4); - *replyopts ++ = BOOTPOPT_DHCP_MESSAGETYPE; - *replyopts ++ = 1; - *replyopts ++ = DHCPOFFER; - opts_len -= 3; - dhcpreqparam_default[0] = BOOTPOPT_IP_ADDRESS_LEASE_TIME; - dhcpreqparam_default[1] = BOOTPOPT_SERVER_IDENTIFIER; - if (found_host_name) { - dhcpreqparam_default[2] = BOOTPOPT_HOST_NAME; - } - dhcpreqparam_default_validflag = true; - break; - case DHCPREQUEST: - BX_DEBUG(("dhcp server: DHCPREQUEST")); - // check ciaddr. - if (found_serverid || found_guest_ipaddr || (!memcmp(&data[12], dhcp->default_guest_ipv4addr, 4))) { - *replyopts ++ = BOOTPOPT_DHCP_MESSAGETYPE; - *replyopts ++ = 1; - *replyopts ++ = DHCPACK; - opts_len -= 3; - dhcpreqparam_default[0] = BOOTPOPT_IP_ADDRESS_LEASE_TIME; - if (!found_serverid) { - dhcpreqparam_default[1] = BOOTPOPT_SERVER_IDENTIFIER; - } - dhcpreqparam_default_validflag = true; - } else { - *replyopts ++ = BOOTPOPT_DHCP_MESSAGETYPE; - *replyopts ++ = 1; - *replyopts ++ = DHCPNAK; - opts_len -= 3; - if (found_leasetime) { - dhcpreqparam_default[dhcpreqparams_default_len++] = BOOTPOPT_IP_ADDRESS_LEASE_TIME; - dhcpreqparam_default_validflag = true; - } - if (!found_serverid) { - dhcpreqparam_default[dhcpreqparams_default_len++] = BOOTPOPT_SERVER_IDENTIFIER; - dhcpreqparam_default_validflag = true; - } - } - break; - default: - BX_ERROR(("dhcp server: unsupported message type %u",dhcpmsgtype)); - return 0; - } - - while (1) { - while (dhcpreqparams_len-- > 0) { - switch (*dhcpreqparams++) { - case BOOTPOPT_SUBNETMASK: - BX_DEBUG(("provide BOOTPOPT_SUBNETMASK")); - if (opts_len < 6) { - BX_ERROR(("option buffer is insufficient")); - return 0; - } - opts_len -= 6; - *replyopts ++ = BOOTPOPT_SUBNETMASK; - *replyopts ++ = 4; - memcpy(replyopts,subnetmask_ipv4addr,4); - replyopts += 4; - break; - case BOOTPOPT_ROUTER_OPTION: - BX_DEBUG(("provide BOOTPOPT_ROUTER_OPTION")); - if (opts_len < 6) { - BX_ERROR(("option buffer is insufficient")); - return 0; - } - opts_len -= 6; - *replyopts ++ = BOOTPOPT_ROUTER_OPTION; - *replyopts ++ = 4; - memcpy(replyopts, dhcp->host_ipv4addr, 4); - replyopts += 4; - break; - case BOOTPOPT_DOMAIN_NAMESERVER: - if (dhcp->dns_ipv4addr[0] != 0) { - BX_DEBUG(("provide BOOTPOPT_DOMAIN_NAMESERVER")); - if (opts_len < 6) { - BX_ERROR(("option buffer is insufficient")); - return 0; - } - opts_len -= 6; - *replyopts ++ = BOOTPOPT_DOMAIN_NAMESERVER; - *replyopts ++ = 4; - memcpy(replyopts, dhcp->dns_ipv4addr, 4); - replyopts += 4; - } - break; - case BOOTPOPT_BROADCAST_ADDRESS: - BX_DEBUG(("provide BOOTPOPT_BROADCAST_ADDRESS")); - if (opts_len < 6) { - BX_ERROR(("option buffer is insufficient")); - return 0; - } - opts_len -= 6; - *replyopts ++ = BOOTPOPT_BROADCAST_ADDRESS; - *replyopts ++ = 4; - memcpy(replyopts, dhcp->host_ipv4addr, 3); - replyopts += 3; - *replyopts ++ = 0xff; - break; - case BOOTPOPT_IP_ADDRESS_LEASE_TIME: - BX_DEBUG(("provide BOOTPOPT_IP_ADDRESS_LEASE_TIME")); - if (opts_len < 6) { - BX_ERROR(("option buffer is insufficient")); - return 0; - } - opts_len -= 6; - *replyopts ++ = BOOTPOPT_IP_ADDRESS_LEASE_TIME; - *replyopts ++ = 4; - if (leasetime < DEFAULT_LEASE_TIME) { - put_net4(replyopts, leasetime); - } else { - put_net4(replyopts, DEFAULT_LEASE_TIME); - } - replyopts += 4; - break; - case BOOTPOPT_SERVER_IDENTIFIER: - BX_DEBUG(("provide BOOTPOPT_SERVER_IDENTIFIER")); - if (opts_len < 6) { - BX_ERROR(("option buffer is insufficient")); - return 0; - } - opts_len -= 6; - *replyopts ++ = BOOTPOPT_SERVER_IDENTIFIER; - *replyopts ++ = 4; - memcpy(replyopts, dhcp->host_ipv4addr,4); - replyopts += 4; - break; - case BOOTPOPT_RENEWAL_TIME: - BX_DEBUG(("provide BOOTPOPT_RENEWAL_TIME")); - if (opts_len < 6) { - BX_ERROR(("option buffer is insufficient")); - return 0; - } - opts_len -= 6; - *replyopts ++ = BOOTPOPT_RENEWAL_TIME; - *replyopts ++ = 4; - put_net4(replyopts, 600); - replyopts += 4; - break; - case BOOTPOPT_REBINDING_TIME: - BX_DEBUG(("provide BOOTPOPT_REBINDING_TIME")); - if (opts_len < 6) { - BX_ERROR(("option buffer is insufficient")); - return 0; - } - opts_len -= 6; - *replyopts ++ = BOOTPOPT_REBINDING_TIME; - *replyopts ++ = 4; - put_net4(replyopts, 1800); - replyopts += 4; - break; - case BOOTPOPT_HOST_NAME: - if (hostname != NULL) { - BX_DEBUG(("provide BOOTPOPT_HOST_NAME")); - if (opts_len < (hostname_len + 2)) { - free(hostname); - BX_ERROR(("option buffer is insufficient")); - return 0; - } - opts_len -= (hostname_len + 2); - *replyopts ++ = BOOTPOPT_HOST_NAME; - *replyopts ++ = hostname_len; - memcpy(replyopts, hostname, hostname_len); - replyopts += hostname_len; - free(hostname); - hostname = NULL; - break; - } - default: - if (*(dhcpreqparams-1) != 0) { - BX_ERROR(("dhcp server: requested parameter %u not supported yet",*(dhcpreqparams-1))); - } - break; - } - } - - if (!dhcpreqparam_default_validflag) break; - dhcpreqparams = &dhcpreqparam_default[0]; - dhcpreqparams_len = sizeof(dhcpreqparam_default); - dhcpreqparam_default_validflag = false; - } - - if (opts_len < 1) { - BX_ERROR(("option buffer is insufficient")); - return 0; - } - opts_len -= 2; - *replyopts ++ = BOOTPOPT_END; - - opts_len = replyopts - &replybuf[0]; - if (opts_len < (236U+64U)) { - opts_len = (236U+64U); // BOOTP - } - if (opts_len < (548U)) { - opts_len = 548U; // DHCP - } - memcpy(reply, replybuf, opts_len); - return opts_len; -} - -// TFTP support - -typedef struct tftp_session { - char filename[BX_PATHNAME_LEN]; - Bit16u tid; - bx_bool write; - unsigned options; - size_t tsize_val; - unsigned blksize_val; - unsigned timeout_val; - unsigned timestamp; - struct tftp_session *next; -} tftp_session_t; - -tftp_session_t *tftp_sessions = NULL; - -tftp_session_t *tftp_new_session(Bit16u req_tid, bx_bool mode, const char *tpath, const char *tname) -{ - tftp_session_t *s = new tftp_session_t; - s->tid = req_tid; - s->write = mode; - s->options = 0; - s->blksize_val = TFTP_DEFAULT_BLKSIZE; - s->timeout_val = TFTP_DEFAULT_TIMEOUT; - s->next = tftp_sessions; - tftp_sessions = s; - if ((strlen(tname) > 0) && ((strlen(tpath) + strlen(tname)) < BX_PATHNAME_LEN)) { - sprintf(s->filename, "%s/%s", tpath, tname); - } else { - s->filename[0] = 0; - } - return s; -} - -tftp_session_t *tftp_find_session(Bit16u tid) -{ - tftp_session_t *s = tftp_sessions; - while (s != NULL) { - if (s->tid != tid) - s = s->next; - else - break; - } - return s; -} - -void tftp_remove_session(tftp_session_t *s) -{ - tftp_session_t *last; - - if (tftp_sessions == s) { - tftp_sessions = s->next; - } else { - last = tftp_sessions; - while (last != NULL) { - if (last->next != s) - last = last->next; - else - break; - } - if (last) { - last->next = s->next; - } - } - delete s; -} - -void tftp_update_timestamp(tftp_session_t *s) -{ -#ifndef BXHUB - s->timestamp = (unsigned)(bx_pc_system.time_usec() / 1000000); -#else - s->timestamp = (unsigned)time(NULL); -#endif -} - -void tftp_timeout_check() -{ -#ifndef BXHUB - unsigned curtime = (unsigned)(bx_pc_system.time_usec() / 1000000); -#else - unsigned curtime = (unsigned)time(NULL); -#endif - tftp_session_t *next, *s = tftp_sessions; - - while (s != NULL) { - if ((curtime - s->timestamp) > s->timeout_val) { - next = s->next; - tftp_remove_session(s); - s = next; - } else { - s = s->next; - } - } -} - -int tftp_send_error(Bit8u *buffer, unsigned code, const char *msg, tftp_session_t *s) -{ - put_net2(buffer, TFTP_ERROR); - put_net2(buffer + 2, code); - strcpy((char*)buffer + 4, msg); - if (s != NULL) { - tftp_remove_session(s); - } - return (strlen(msg) + 5); -} - -int tftp_send_data(Bit8u *buffer, unsigned block_nr, tftp_session_t *s) -{ - char msg[BX_PATHNAME_LEN]; - int rd; - - FILE *fp = fopen(s->filename, "rb"); - if (!fp) { - sprintf(msg, "File not found: %s", s->filename); - return tftp_send_error(buffer, 1, msg, s); - } - - if (fseek(fp, (block_nr - 1) * s->blksize_val, SEEK_SET) < 0) { - fclose(fp); - return tftp_send_error(buffer, 3, "Block not seekable", s); - } - - rd = fread(buffer + 4, 1, s->blksize_val, fp); - fclose(fp); - - if (rd < 0) { - return tftp_send_error(buffer, 3, "Block not readable", s); - } - - put_net2(buffer, TFTP_DATA); - put_net2(buffer + 2, block_nr); - if (rd < (int)s->blksize_val) { - tftp_remove_session(s); - } else { - tftp_update_timestamp(s); - } - return (rd + 4); -} - -int tftp_send_ack(Bit8u *buffer, unsigned block_nr) -{ - put_net2(buffer, TFTP_ACK); - put_net2(buffer + 2, block_nr); - return 4; -} - -int tftp_send_optack(Bit8u *buffer, tftp_session_t *s) -{ - Bit8u *p = buffer; - put_net2(p, TFTP_OPTACK); - p += 2; - if (s->options & TFTP_OPTION_TSIZE) { - *p++='t'; *p++='s'; *p++='i'; *p++='z'; *p++='e'; *p++='\0'; - sprintf((char *)p, "%lu", (unsigned long)s->tsize_val); - p += strlen((const char *)p) + 1; - } - if (s->options & TFTP_OPTION_BLKSIZE) { - *p++='b'; *p++='l'; *p++='k'; *p++='s'; *p++='i'; *p++='z'; *p++='e'; *p++='\0'; - sprintf((char *)p, "%u", s->blksize_val); - p += strlen((const char *)p) + 1; - } - if (s->options & TFTP_OPTION_TIMEOUT) { - *p++='t'; *p++='i'; *p++='m'; *p++='e'; *p++='o'; *p++='u'; *p++='t'; *p++='\0'; - sprintf((char *)p, "%u", s->timeout_val); - p += strlen((const char *)p) + 1; - } - tftp_update_timestamp(s); - return (p - buffer); -} - -void tftp_parse_options(bx_devmodel_c *netdev, const char *mode, const Bit8u *data, - unsigned data_len, tftp_session_t *s) -{ - while (mode < (const char*)data + data_len) { - if (memcmp(mode, "octet\0", 6) == 0) { - s->options |= TFTP_OPTION_OCTET; - mode += 6; - } else if (memcmp(mode, "tsize\0", 6) == 0) { - s->options |= TFTP_OPTION_TSIZE; // size needed - mode += 6; - if (s->write) { - s->tsize_val = atoi(mode); - } - mode += strlen(mode)+1; - } else if (memcmp(mode, "blksize\0", 8) == 0) { - s->options |= TFTP_OPTION_BLKSIZE; - mode += 8; - s->blksize_val = atoi(mode); - if (s->blksize_val > TFTP_BUFFER_SIZE) { - BX_ERROR(("tftp req: blksize value %d not supported - using %d instead", - s->blksize_val, TFTP_BUFFER_SIZE)); - s->blksize_val = TFTP_BUFFER_SIZE; - } - mode += strlen(mode)+1; - } else if (memcmp(mode, "timeout\0", 8) == 0) { - s->options |= TFTP_OPTION_TIMEOUT; - mode += 8; - s->timeout_val = atoi(mode); - if ((s->timeout_val < 1) || (s->timeout_val > 255)) { - BX_ERROR(("tftp req: timeout value %d not supported - using %d instead", - s->timeout_val, TFTP_DEFAULT_TIMEOUT)); - s->timeout_val = TFTP_DEFAULT_TIMEOUT; - } - mode += strlen(mode)+1; - } else { - BX_ERROR(("tftp req: unknown option %s", mode)); - break; - } - } -} - -int vnet_process_tftp(bx_devmodel_c *netdev, const Bit8u *data, unsigned data_len, Bit16u req_tid, Bit8u *reply, const char *tftp_rootdir) -{ - FILE *fp; - unsigned block_nr; - unsigned tftp_len; - tftp_session_t *s; - - tftp_timeout_check(); - s = tftp_find_session(req_tid); - switch (get_net2(data)) { - case TFTP_RRQ: - { - if (s != NULL) { - tftp_remove_session(s); - } - strncpy((char*)reply, (const char*)data + 2, data_len - 2); - reply[data_len - 4] = 0; - - s = tftp_new_session(req_tid, 0, tftp_rootdir, (const char*)reply); - if (strlen(s->filename) == 0) { - return tftp_send_error(reply, 1, "Illegal file name", s); - } - // options - if (strlen((char*)reply) < data_len - 2) { - const char *mode = (const char*)data + 2 + strlen((char*)reply) + 1; - tftp_parse_options(netdev, mode, data, data_len, s); - } - if (!(s->options & TFTP_OPTION_OCTET)) { - return tftp_send_error(reply, 4, "Unsupported transfer mode", NULL); - } - if (s->options & TFTP_OPTION_TSIZE) { - struct stat stbuf; - if (stat(s->filename, &stbuf) < 0) { - s->options &= ~TFTP_OPTION_TSIZE; - } else { - s->tsize_val = (size_t)stbuf.st_size; - BX_DEBUG(("tftp filesize: %lu", (unsigned long)s->tsize_val)); - } - } - if ((s->options & ~TFTP_OPTION_OCTET) > 0) { - return tftp_send_optack(reply, s); - } else { - return tftp_send_data(reply, 1, s); - } - } - break; - - case TFTP_WRQ: - { - if (s != NULL) { - tftp_remove_session(s); - } - strncpy((char*)reply, (const char*)data + 2, data_len - 2); - reply[data_len - 4] = 0; - - s = tftp_new_session(req_tid, 1, tftp_rootdir, (const char*)reply); - if (strlen(s->filename) == 0) { - return tftp_send_error(reply, 1, "Illegal file name", s); - } - // options - if (strlen((char*)reply) < data_len - 2) { - const char *mode = (const char*)data + 2 + strlen((char*)reply) + 1; - tftp_parse_options(netdev, mode, data, data_len, s); - } - if (!(s->options & TFTP_OPTION_OCTET)) { - return tftp_send_error(reply, 4, "Unsupported transfer mode", NULL); - } - - fp = fopen(s->filename, "rb"); - if (fp) { - fclose(fp); - return tftp_send_error(reply, 6, "File exists", s); - } - fp = fopen(s->filename, "wb"); - if (!fp) { - return tftp_send_error(reply, 2, "Access violation", s); - } - fclose(fp); - - if ((s->options & ~TFTP_OPTION_OCTET) > 0) { - return tftp_send_optack(reply, s); - } else { - tftp_update_timestamp(s); - return tftp_send_ack(reply, 0); - } - } - break; - - case TFTP_DATA: - if (s != NULL) { - if (s->write == 1) { - block_nr = get_net2(data + 2); - strncpy((char*)reply, (const char*)data + 4, data_len - 4); - tftp_len = data_len - 4; - reply[tftp_len] = 0; - if (tftp_len <= s->blksize_val) { - fp = fopen(s->filename, "ab"); - if (!fp) { - return tftp_send_error(reply, 2, "Access violation", s); - } - if (fseek(fp, (block_nr - 1) * TFTP_BUFFER_SIZE, SEEK_SET) < 0) { - fclose(fp); - return tftp_send_error(reply, 3, "Block not seekable", s); - } - fwrite(reply, 1, tftp_len, fp); - fclose(fp); - if (tftp_len < s->blksize_val) { - tftp_remove_session(s); - } else { - tftp_update_timestamp(s); - } - return tftp_send_ack(reply, block_nr); - } else { - return tftp_send_error(reply, 4, "Illegal request", s); - } - } else { - return tftp_send_error(reply, 4, "Illegal request", s); - } - } else { - return tftp_send_error(reply, 5, "Unknown transfer ID", s); - } - break; - - case TFTP_ACK: - if (s != NULL) { - if (s->write == 0) { - return tftp_send_data(reply, get_net2(data + 2) + 1, s); - } else { - return tftp_send_error(reply, 4, "Illegal request", s); - } - } - break; - - case TFTP_ERROR: - if (s != NULL) { - tftp_remove_session(s); - } - break; - - default: - BX_ERROR(("TFTP unknown opt %d", get_net2(data))); - } - return 0; -} #endif /* if BX_NETWORKING */ diff --git a/bochs/iodev/network/netmod.h b/bochs/iodev/network/netmod.h index 0547d46c3..d1a034b52 100644 --- a/bochs/iodev/network/netmod.h +++ b/bochs/iodev/network/netmod.h @@ -29,12 +29,16 @@ #ifndef BXHUB // Pseudo device that loads the lowlevel networking module -class bx_netmod_ctl_c : public bx_netmod_ctl_stub_c { +class BOCHSAPI bx_netmod_ctl_c : public logfunctions { public: bx_netmod_ctl_c(); virtual ~bx_netmod_ctl_c() {} + void init(void); + void exit(void); virtual void* init_module(bx_list_c *base, void* rxh, void* rxstat, bx_devmodel_c *dev); }; + +BOCHSAPI extern bx_netmod_ctl_c bx_netmod_ctl; #endif #define BX_PACKET_BUFSIZE 2048 // Enough for an ether frame @@ -49,120 +53,14 @@ public: // this should not be smaller than an arp reply with an ethernet header #define MIN_RX_PACKET_LEN 60 -#define ETHERNET_MAC_ADDR_LEN 6 -#define ETHERNET_TYPE_IPV4 0x0800 -#define ETHERNET_TYPE_ARP 0x0806 - -#define ARP_OPCODE_REQUEST 1 -#define ARP_OPCODE_REPLY 2 -#define ARP_OPCODE_REV_REQUEST 3 -#define ARP_OPCODE_REV_REPLY 4 - -#define ICMP_ECHO_PACKET_MAX 128 - -#define TFTP_BUFFER_SIZE 1024 - -#if defined(_MSC_VER) -#pragma pack(push, 1) -#elif defined(__MWERKS__) && defined(macintosh) -#pragma options align=packed -#endif - -typedef struct ethernet_header { -#if defined(_MSC_VER) && (_MSC_VER>=1300) - __declspec(align(1)) -#endif - Bit8u dst_mac_addr[ETHERNET_MAC_ADDR_LEN]; - Bit8u src_mac_addr[ETHERNET_MAC_ADDR_LEN]; - Bit16u type; -} -#if !defined(_MSC_VER) - GCC_ATTRIBUTE((packed)) -#endif -ethernet_header_t; - -typedef struct arp_header { -#if defined(_MSC_VER) && (_MSC_VER>=1300) - __declspec(align(1)) -#endif - Bit16u hw_addr_space; - Bit16u proto_addr_space; - Bit8u hw_addr_len; - Bit8u proto_addr_len; - Bit16u opcode; - /* HW address of sender */ - /* Protocol address of sender */ - /* HW address of target*/ - /* Protocol address of target */ -} -#if !defined(_MSC_VER) - GCC_ATTRIBUTE((packed)) -#endif -arp_header_t; - -typedef struct ip_header { -#if defined(_MSC_VER) && (_MSC_VER>=1300) - __declspec(align(1)) -#endif -#ifdef BX_LITTLE_ENDIAN - Bit8u header_len : 4; - Bit8u version : 4; -#else - Bit8u version : 4; - Bit8u header_len : 4; -#endif - Bit8u tos; - Bit16u total_len; - Bit16u id; - Bit16u frag_offs; - Bit8u ttl; - Bit8u protocol; - Bit16u checksum; - Bit32u src_addr; - Bit32u dst_addr; -} -#if !defined(_MSC_VER) - GCC_ATTRIBUTE((packed)) -#endif -ip_header_t; - -typedef struct udp_header { -#if defined(_MSC_VER) && (_MSC_VER>=1300) - __declspec(align(1)) -#endif - Bit16u src_port; - Bit16u dst_port; - Bit16u length; - Bit16u checksum; -} -#if !defined(_MSC_VER) - GCC_ATTRIBUTE((packed)) -#endif -udp_header_t; - -#if defined(_MSC_VER) -#pragma pack(pop) -#elif defined(__MWERKS__) && defined(macintosh) -#pragma options align=reset -#endif - typedef void (*eth_rx_handler_t)(void *arg, const void *buf, unsigned len); typedef Bit32u (*eth_rx_status_t)(void *arg); -typedef struct { - Bit8u host_macaddr[6]; - Bit8u guest_macaddr[6]; - Bit8u host_ipv4addr[4]; - Bit8u default_guest_ipv4addr[4]; - Bit8u guest_ipv4addr[4]; - Bit8u dns_ipv4addr[4]; -} dhcp_cfg_t; - static const Bit8u broadcast_macaddr[6] = {0xff,0xff,0xff,0xff,0xff,0xff}; #ifndef BXHUB int execute_script(bx_devmodel_c *netdev, const char *name, char* arg1); -void write_pktlog_txt(FILE *pktlog_txt, const Bit8u *buf, unsigned len, bx_bool host_to_guest); +void BOCHSAPI_MSVCONLY write_pktlog_txt(FILE *pktlog_txt, const Bit8u *buf, unsigned len, bx_bool host_to_guest); #endif BX_CPP_INLINE Bit16u get_net2(const Bit8u *buf) @@ -193,21 +91,6 @@ BX_CPP_INLINE void put_net4(Bit8u *buf,Bit32u data) *(buf+3) = (Bit8u)(data & 0xff); } -// vnet code shared with bxhub -#ifdef BXHUB -#define bx_devmodel_c void -#endif -Bit16u ip_checksum(const Bit8u *buf, unsigned buf_len); -void vnet_prepare_reply(Bit8u *replybuf, unsigned l3type, dhcp_cfg_t *dhcpc); -bx_bool vnet_process_arp_request(const Bit8u *buf, Bit8u *reply, dhcp_cfg_t *dhcp); -bx_bool vnet_process_icmp_echo(const Bit8u *l3pkt, unsigned l3header_len, - const Bit8u *l4pkt, unsigned l4pkt_len, - Bit8u *reply); -int vnet_process_dhcp(bx_devmodel_c *netdev, const Bit8u *data, unsigned data_len, - Bit8u *reply, dhcp_cfg_t *dhcp); -int vnet_process_tftp(bx_devmodel_c *netdev, const Bit8u *data, unsigned data_len, - Bit16u req_tid, Bit8u *reply, const char *tftp_rootdir); - #ifndef BXHUB // // The eth_pktmover class is used by ethernet chip emulations @@ -233,8 +116,10 @@ protected: // their name. Chip emulations use the static 'create' method // to locate and instantiate a pktmover class. // -class eth_locator_c { +class BOCHSAPI_MSVCONLY eth_locator_c { public: + static bx_bool module_present(const char *type); + static void cleanup(); static eth_pktmover_c *create(const char *type, const char *netif, const char *macaddr, eth_rx_handler_t rxh, @@ -243,7 +128,7 @@ public: const char *script); protected: eth_locator_c(const char *type); - virtual ~eth_locator_c() {} + virtual ~eth_locator_c(); virtual eth_pktmover_c *allocate(const char *netif, const char *macaddr, eth_rx_handler_t rxh, diff --git a/bochs/iodev/network/netutil.cc b/bochs/iodev/network/netutil.cc new file mode 100644 index 000000000..bd4b4b4e2 --- /dev/null +++ b/bochs/iodev/network/netutil.cc @@ -0,0 +1,856 @@ +///////////////////////////////////////////////////////////////////////// +// $Id$ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2004-2017 The Bochs Project +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// + +// netutil.cc - shared code for eth_vnet.cc and bxhub.cc + +#define BX_PLUGGABLE + +#ifdef BXHUB +#include "config.h" +#else +#include "iodev.h" +#endif + +#if BX_NETWORKING + +#include "netmod.h" +#include "netutil.h" + +#ifdef BXHUB +#include "misc/bxcompat.h" +#else +#define LOG_THIS netdev-> +#endif + +Bit16u ip_checksum(const Bit8u *buf, unsigned buf_len) +{ + Bit32u sum = 0; + unsigned n; + + for (n = 0; n < buf_len; n++) { + if (n & 1) { + sum += (Bit32u)(*buf++); + } else { + sum += (Bit32u)(*buf++) << 8; + } + } + while (sum > 0xffff) { + sum = (sum >> 16) + (sum & 0xffff); + } + + return (Bit16u)sum; +} + +// DHCP server + +#define BOOTREQUEST 1 +#define BOOTREPLY 2 + +#define BOOTPOPT_PADDING 0 +#define BOOTPOPT_END 255 +#define BOOTPOPT_SUBNETMASK 1 +#define BOOTPOPT_TIMEOFFSET 2 +#define BOOTPOPT_ROUTER_OPTION 3 +#define BOOTPOPT_DOMAIN_NAMESERVER 6 +#define BOOTPOPT_HOST_NAME 12 +#define BOOTPOPT_DOMAIN_NAME 15 +#define BOOTPOPT_MAX_DATAGRAM_SIZE 22 +#define BOOTPOPT_DEFAULT_IP_TTL 23 +#define BOOTPOPT_BROADCAST_ADDRESS 28 +#define BOOTPOPT_ARPCACHE_TIMEOUT 35 +#define BOOTPOPT_DEFAULT_TCP_TTL 37 +#define BOOTPOPT_NTP_SERVER 42 +#define BOOTPOPT_NETBIOS_NAMESERVER 44 +#define BOOTPOPT_X_FONTSERVER 48 +#define BOOTPOPT_REQUESTED_IP_ADDRESS 50 +#define BOOTPOPT_IP_ADDRESS_LEASE_TIME 51 +#define BOOTPOPT_OPTION_OVRLOAD 52 +#define BOOTPOPT_DHCP_MESSAGETYPE 53 +#define BOOTPOPT_SERVER_IDENTIFIER 54 +#define BOOTPOPT_PARAMETER_REQUEST_LIST 55 +#define BOOTPOPT_MAX_DHCP_MESSAGE_SIZE 57 +#define BOOTPOPT_RENEWAL_TIME 58 +#define BOOTPOPT_REBINDING_TIME 59 +#define BOOTPOPT_CLASS_IDENTIFIER 60 +#define BOOTPOPT_CLIENT_IDENTIFIER 61 + +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 +#define DHCPINFORM 8 + +#define DEFAULT_LEASE_TIME 28800 + +// TFTP server support by EaseWay + +#define TFTP_RRQ 1 +#define TFTP_WRQ 2 +#define TFTP_DATA 3 +#define TFTP_ACK 4 +#define TFTP_ERROR 5 +#define TFTP_OPTACK 6 + +#define TFTP_OPTION_OCTET 0x1 +#define TFTP_OPTION_BLKSIZE 0x2 +#define TFTP_OPTION_TSIZE 0x4 +#define TFTP_OPTION_TIMEOUT 0x8 + +#define TFTP_DEFAULT_BLKSIZE 512 +#define TFTP_DEFAULT_TIMEOUT 5 + +static const Bit8u subnetmask_ipv4addr[4] = {0xff,0xff,0xff,0x00}; +static const Bit8u broadcast_ipv4addr1[4] = {0xff,0xff,0xff,0xff}; + +void vnet_prepare_reply(Bit8u *replybuf, unsigned l3type, dhcp_cfg_t *dhcpc) +{ + ethernet_header_t *ethhdr = (ethernet_header_t *)replybuf; + + memcpy(ethhdr->dst_mac_addr, dhcpc->guest_macaddr, ETHERNET_MAC_ADDR_LEN); + memcpy(ethhdr->src_mac_addr, dhcpc->host_macaddr, ETHERNET_MAC_ADDR_LEN); + put_net2((Bit8u*)ðhdr->type, l3type); +} + +bx_bool vnet_process_arp_request(const Bit8u *buf, Bit8u *reply, dhcp_cfg_t *dhcp) +{ + arp_header_t *arprhdr = (arp_header_t *)(reply + sizeof(ethernet_header_t)); + + if (!memcmp(&buf[22], dhcp->guest_macaddr, 6)) { + memcpy(dhcp->guest_ipv4addr, &buf[28], 4); + if (!memcmp(&buf[38], dhcp->host_ipv4addr, 4)) { + memset(reply, 0, MIN_RX_PACKET_LEN); + memcpy(arprhdr, &buf[14], 6); + put_net2((Bit8u*)&arprhdr->opcode, ARP_OPCODE_REPLY); + memcpy((Bit8u *)arprhdr+8, dhcp->host_macaddr, ETHERNET_MAC_ADDR_LEN); + memcpy((Bit8u *)arprhdr+14, dhcp->host_ipv4addr, 4); + memcpy((Bit8u *)arprhdr+18, dhcp->guest_macaddr, ETHERNET_MAC_ADDR_LEN); + memcpy((Bit8u *)arprhdr+24, dhcp->guest_ipv4addr, 4); + return 1; + } + } + return 0; +} + +bx_bool vnet_process_icmp_echo(const Bit8u *l3pkt, unsigned l3header_len, + const Bit8u *l4pkt, unsigned l4pkt_len, + Bit8u *reply) +{ + if ((14U+l3header_len+l4pkt_len) > ICMP_ECHO_PACKET_MAX) { + return 0; + } + memcpy(&reply[14], l3pkt, l3header_len); + memcpy(&reply[14+l3header_len], l4pkt, l4pkt_len); + reply[14+l3header_len+0] = 0x00; // echo reply + put_net2(&reply[14+l3header_len+2],0); + put_net2(&reply[14+l3header_len+2], + ip_checksum(&reply[14+l3header_len],l4pkt_len) ^ (Bit16u)0xffff); + return 1; +} + +int vnet_process_dhcp(bx_devmodel_c *netdev, const Bit8u *data, unsigned data_len, Bit8u *reply, dhcp_cfg_t *dhcp) +{ + const Bit8u *opts; + unsigned opts_len; + unsigned extcode; + unsigned extlen; + const Bit8u *extdata; + unsigned dhcpmsgtype = 0; + bx_bool found_serverid = false; + bx_bool found_leasetime = false; + bx_bool found_guest_ipaddr = false; + bx_bool found_host_name = false; + Bit32u leasetime = BX_MAX_BIT32U; + const Bit8u *dhcpreqparams = NULL; + unsigned dhcpreqparams_len = 0; + Bit8u dhcpreqparam_default[8]; + bx_bool dhcpreqparam_default_validflag = false; + unsigned dhcpreqparams_default_len = 0; + Bit8u *replyopts; + Bit8u replybuf[576]; + char *hostname = NULL; + unsigned hostname_len = 0; + + if (data_len < (236U+4U)) return 0; + if (data[0] != BOOTREQUEST) return 0; + if (data[1] != 1 || data[2] != 6) return 0; + if (memcmp(&data[28U], dhcp->guest_macaddr, 6)) return 0; + if (data[236] != 0x63 || data[237] != 0x82 || + data[238] != 0x53 || data[239] != 0x63) return 0; + + opts = &data[240]; + opts_len = data_len - 240U; + + while (1) { + if (opts_len < 1) { + BX_ERROR(("dhcp: invalid request")); + return 0; + } + extcode = *opts++; + opts_len--; + + if (extcode == BOOTPOPT_PADDING) continue; + if (extcode == BOOTPOPT_END) break; + if (opts_len < 1) { + BX_ERROR(("dhcp: invalid request")); + return 0; + } + extlen = *opts++; + opts_len--; + if (opts_len < extlen) { + BX_ERROR(("dhcp: invalid request")); + return 0; + } + extdata = opts; + opts += extlen; + opts_len -= extlen; + + switch (extcode) + { + case BOOTPOPT_DHCP_MESSAGETYPE: + if (extlen != 1) + break; + dhcpmsgtype = *extdata; + break; + case BOOTPOPT_PARAMETER_REQUEST_LIST: + if (extlen < 1) + break; + dhcpreqparams = extdata; + dhcpreqparams_len = extlen; + break; + case BOOTPOPT_SERVER_IDENTIFIER: + if (extlen != 4) + break; + if (memcmp(extdata, dhcp->host_ipv4addr, 4)) { + BX_INFO(("dhcp: request to another server")); + return 0; + } + found_serverid = true; + break; + case BOOTPOPT_IP_ADDRESS_LEASE_TIME: + if (extlen != 4) + break; + leasetime = get_net4(&extdata[0]); + found_leasetime = true; + break; + case BOOTPOPT_REQUESTED_IP_ADDRESS: + if (extlen != 4) + break; + if (!memcmp(extdata, dhcp->default_guest_ipv4addr,4)) { + found_guest_ipaddr = true; + memcpy(dhcp->guest_ipv4addr, dhcp->default_guest_ipv4addr, 4); + } + break; + case BOOTPOPT_HOST_NAME: + if (extlen < 1) + break; + hostname = (char*)malloc(extlen); + memcpy(hostname, extdata, extlen); + hostname_len = extlen; + found_host_name = true; + break; + default: + BX_ERROR(("extcode %d not supported yet", extcode)); + break; + } + } + + memset(&dhcpreqparam_default,0,sizeof(dhcpreqparam_default)); + memset(&replybuf[0],0,sizeof(replybuf)); + replybuf[0] = BOOTREPLY; + replybuf[1] = 1; + replybuf[2] = 6; + memcpy(&replybuf[4],&data[4],4); + memcpy(&replybuf[16], dhcp->default_guest_ipv4addr, 4); + memcpy(&replybuf[20], dhcp->host_ipv4addr, 4); + memcpy(&replybuf[28],&data[28],6); + memcpy(&replybuf[44],"vnet",4); + memcpy(&replybuf[108],"pxelinux.0",10); + replybuf[236] = 0x63; + replybuf[237] = 0x82; + replybuf[238] = 0x53; + replybuf[239] = 0x63; + replyopts = &replybuf[240]; + opts_len = sizeof(replybuf)/sizeof(replybuf[0])-240; + switch (dhcpmsgtype) { + case DHCPDISCOVER: + BX_DEBUG(("dhcp server: DHCPDISCOVER")); + // reset guest address; answer must be broadcasted to unconfigured IP + memcpy(dhcp->guest_ipv4addr, broadcast_ipv4addr1, 4); + *replyopts ++ = BOOTPOPT_DHCP_MESSAGETYPE; + *replyopts ++ = 1; + *replyopts ++ = DHCPOFFER; + opts_len -= 3; + dhcpreqparam_default[0] = BOOTPOPT_IP_ADDRESS_LEASE_TIME; + dhcpreqparam_default[1] = BOOTPOPT_SERVER_IDENTIFIER; + if (found_host_name) { + dhcpreqparam_default[2] = BOOTPOPT_HOST_NAME; + } + dhcpreqparam_default_validflag = true; + break; + case DHCPREQUEST: + BX_DEBUG(("dhcp server: DHCPREQUEST")); + // check ciaddr. + if (found_serverid || found_guest_ipaddr || (!memcmp(&data[12], dhcp->default_guest_ipv4addr, 4))) { + *replyopts ++ = BOOTPOPT_DHCP_MESSAGETYPE; + *replyopts ++ = 1; + *replyopts ++ = DHCPACK; + opts_len -= 3; + dhcpreqparam_default[0] = BOOTPOPT_IP_ADDRESS_LEASE_TIME; + if (!found_serverid) { + dhcpreqparam_default[1] = BOOTPOPT_SERVER_IDENTIFIER; + } + dhcpreqparam_default_validflag = true; + } else { + *replyopts ++ = BOOTPOPT_DHCP_MESSAGETYPE; + *replyopts ++ = 1; + *replyopts ++ = DHCPNAK; + opts_len -= 3; + if (found_leasetime) { + dhcpreqparam_default[dhcpreqparams_default_len++] = BOOTPOPT_IP_ADDRESS_LEASE_TIME; + dhcpreqparam_default_validflag = true; + } + if (!found_serverid) { + dhcpreqparam_default[dhcpreqparams_default_len++] = BOOTPOPT_SERVER_IDENTIFIER; + dhcpreqparam_default_validflag = true; + } + } + break; + default: + BX_ERROR(("dhcp server: unsupported message type %u",dhcpmsgtype)); + return 0; + } + + while (1) { + while (dhcpreqparams_len-- > 0) { + switch (*dhcpreqparams++) { + case BOOTPOPT_SUBNETMASK: + BX_DEBUG(("provide BOOTPOPT_SUBNETMASK")); + if (opts_len < 6) { + BX_ERROR(("option buffer is insufficient")); + return 0; + } + opts_len -= 6; + *replyopts ++ = BOOTPOPT_SUBNETMASK; + *replyopts ++ = 4; + memcpy(replyopts,subnetmask_ipv4addr,4); + replyopts += 4; + break; + case BOOTPOPT_ROUTER_OPTION: + BX_DEBUG(("provide BOOTPOPT_ROUTER_OPTION")); + if (opts_len < 6) { + BX_ERROR(("option buffer is insufficient")); + return 0; + } + opts_len -= 6; + *replyopts ++ = BOOTPOPT_ROUTER_OPTION; + *replyopts ++ = 4; + memcpy(replyopts, dhcp->host_ipv4addr, 4); + replyopts += 4; + break; + case BOOTPOPT_DOMAIN_NAMESERVER: + if (dhcp->dns_ipv4addr[0] != 0) { + BX_DEBUG(("provide BOOTPOPT_DOMAIN_NAMESERVER")); + if (opts_len < 6) { + BX_ERROR(("option buffer is insufficient")); + return 0; + } + opts_len -= 6; + *replyopts ++ = BOOTPOPT_DOMAIN_NAMESERVER; + *replyopts ++ = 4; + memcpy(replyopts, dhcp->dns_ipv4addr, 4); + replyopts += 4; + } + break; + case BOOTPOPT_BROADCAST_ADDRESS: + BX_DEBUG(("provide BOOTPOPT_BROADCAST_ADDRESS")); + if (opts_len < 6) { + BX_ERROR(("option buffer is insufficient")); + return 0; + } + opts_len -= 6; + *replyopts ++ = BOOTPOPT_BROADCAST_ADDRESS; + *replyopts ++ = 4; + memcpy(replyopts, dhcp->host_ipv4addr, 3); + replyopts += 3; + *replyopts ++ = 0xff; + break; + case BOOTPOPT_IP_ADDRESS_LEASE_TIME: + BX_DEBUG(("provide BOOTPOPT_IP_ADDRESS_LEASE_TIME")); + if (opts_len < 6) { + BX_ERROR(("option buffer is insufficient")); + return 0; + } + opts_len -= 6; + *replyopts ++ = BOOTPOPT_IP_ADDRESS_LEASE_TIME; + *replyopts ++ = 4; + if (leasetime < DEFAULT_LEASE_TIME) { + put_net4(replyopts, leasetime); + } else { + put_net4(replyopts, DEFAULT_LEASE_TIME); + } + replyopts += 4; + break; + case BOOTPOPT_SERVER_IDENTIFIER: + BX_DEBUG(("provide BOOTPOPT_SERVER_IDENTIFIER")); + if (opts_len < 6) { + BX_ERROR(("option buffer is insufficient")); + return 0; + } + opts_len -= 6; + *replyopts ++ = BOOTPOPT_SERVER_IDENTIFIER; + *replyopts ++ = 4; + memcpy(replyopts, dhcp->host_ipv4addr,4); + replyopts += 4; + break; + case BOOTPOPT_RENEWAL_TIME: + BX_DEBUG(("provide BOOTPOPT_RENEWAL_TIME")); + if (opts_len < 6) { + BX_ERROR(("option buffer is insufficient")); + return 0; + } + opts_len -= 6; + *replyopts ++ = BOOTPOPT_RENEWAL_TIME; + *replyopts ++ = 4; + put_net4(replyopts, 600); + replyopts += 4; + break; + case BOOTPOPT_REBINDING_TIME: + BX_DEBUG(("provide BOOTPOPT_REBINDING_TIME")); + if (opts_len < 6) { + BX_ERROR(("option buffer is insufficient")); + return 0; + } + opts_len -= 6; + *replyopts ++ = BOOTPOPT_REBINDING_TIME; + *replyopts ++ = 4; + put_net4(replyopts, 1800); + replyopts += 4; + break; + case BOOTPOPT_HOST_NAME: + if (hostname != NULL) { + BX_DEBUG(("provide BOOTPOPT_HOST_NAME")); + if (opts_len < (hostname_len + 2)) { + free(hostname); + BX_ERROR(("option buffer is insufficient")); + return 0; + } + opts_len -= (hostname_len + 2); + *replyopts ++ = BOOTPOPT_HOST_NAME; + *replyopts ++ = hostname_len; + memcpy(replyopts, hostname, hostname_len); + replyopts += hostname_len; + free(hostname); + hostname = NULL; + break; + } + default: + if (*(dhcpreqparams-1) != 0) { + BX_ERROR(("dhcp server: requested parameter %u not supported yet",*(dhcpreqparams-1))); + } + break; + } + } + + if (!dhcpreqparam_default_validflag) break; + dhcpreqparams = &dhcpreqparam_default[0]; + dhcpreqparams_len = sizeof(dhcpreqparam_default); + dhcpreqparam_default_validflag = false; + } + + if (opts_len < 1) { + BX_ERROR(("option buffer is insufficient")); + return 0; + } + opts_len -= 2; + *replyopts ++ = BOOTPOPT_END; + + opts_len = replyopts - &replybuf[0]; + if (opts_len < (236U+64U)) { + opts_len = (236U+64U); // BOOTP + } + if (opts_len < (548U)) { + opts_len = 548U; // DHCP + } + memcpy(reply, replybuf, opts_len); + return opts_len; +} + +// TFTP support + +typedef struct tftp_session { + char filename[BX_PATHNAME_LEN]; + Bit16u tid; + bx_bool write; + unsigned options; + size_t tsize_val; + unsigned blksize_val; + unsigned timeout_val; + unsigned timestamp; + struct tftp_session *next; +} tftp_session_t; + +tftp_session_t *tftp_sessions = NULL; + +tftp_session_t *tftp_new_session(Bit16u req_tid, bx_bool mode, const char *tpath, const char *tname) +{ + tftp_session_t *s = new tftp_session_t; + s->tid = req_tid; + s->write = mode; + s->options = 0; + s->blksize_val = TFTP_DEFAULT_BLKSIZE; + s->timeout_val = TFTP_DEFAULT_TIMEOUT; + s->next = tftp_sessions; + tftp_sessions = s; + if ((strlen(tname) > 0) && ((strlen(tpath) + strlen(tname)) < BX_PATHNAME_LEN)) { + sprintf(s->filename, "%s/%s", tpath, tname); + } else { + s->filename[0] = 0; + } + return s; +} + +tftp_session_t *tftp_find_session(Bit16u tid) +{ + tftp_session_t *s = tftp_sessions; + while (s != NULL) { + if (s->tid != tid) + s = s->next; + else + break; + } + return s; +} + +void tftp_remove_session(tftp_session_t *s) +{ + tftp_session_t *last; + + if (tftp_sessions == s) { + tftp_sessions = s->next; + } else { + last = tftp_sessions; + while (last != NULL) { + if (last->next != s) + last = last->next; + else + break; + } + if (last) { + last->next = s->next; + } + } + delete s; +} + +void tftp_update_timestamp(tftp_session_t *s) +{ +#ifndef BXHUB + s->timestamp = (unsigned)(bx_pc_system.time_usec() / 1000000); +#else + s->timestamp = (unsigned)time(NULL); +#endif +} + +void tftp_timeout_check() +{ +#ifndef BXHUB + unsigned curtime = (unsigned)(bx_pc_system.time_usec() / 1000000); +#else + unsigned curtime = (unsigned)time(NULL); +#endif + tftp_session_t *next, *s = tftp_sessions; + + while (s != NULL) { + if ((curtime - s->timestamp) > s->timeout_val) { + next = s->next; + tftp_remove_session(s); + s = next; + } else { + s = s->next; + } + } +} + +int tftp_send_error(Bit8u *buffer, unsigned code, const char *msg, tftp_session_t *s) +{ + put_net2(buffer, TFTP_ERROR); + put_net2(buffer + 2, code); + strcpy((char*)buffer + 4, msg); + if (s != NULL) { + tftp_remove_session(s); + } + return (strlen(msg) + 5); +} + +int tftp_send_data(Bit8u *buffer, unsigned block_nr, tftp_session_t *s) +{ + char msg[BX_PATHNAME_LEN]; + int rd; + + FILE *fp = fopen(s->filename, "rb"); + if (!fp) { + sprintf(msg, "File not found: %s", s->filename); + return tftp_send_error(buffer, 1, msg, s); + } + + if (fseek(fp, (block_nr - 1) * s->blksize_val, SEEK_SET) < 0) { + fclose(fp); + return tftp_send_error(buffer, 3, "Block not seekable", s); + } + + rd = fread(buffer + 4, 1, s->blksize_val, fp); + fclose(fp); + + if (rd < 0) { + return tftp_send_error(buffer, 3, "Block not readable", s); + } + + put_net2(buffer, TFTP_DATA); + put_net2(buffer + 2, block_nr); + if (rd < (int)s->blksize_val) { + tftp_remove_session(s); + } else { + tftp_update_timestamp(s); + } + return (rd + 4); +} + +int tftp_send_ack(Bit8u *buffer, unsigned block_nr) +{ + put_net2(buffer, TFTP_ACK); + put_net2(buffer + 2, block_nr); + return 4; +} + +int tftp_send_optack(Bit8u *buffer, tftp_session_t *s) +{ + Bit8u *p = buffer; + put_net2(p, TFTP_OPTACK); + p += 2; + if (s->options & TFTP_OPTION_TSIZE) { + *p++='t'; *p++='s'; *p++='i'; *p++='z'; *p++='e'; *p++='\0'; + sprintf((char *)p, "%lu", (unsigned long)s->tsize_val); + p += strlen((const char *)p) + 1; + } + if (s->options & TFTP_OPTION_BLKSIZE) { + *p++='b'; *p++='l'; *p++='k'; *p++='s'; *p++='i'; *p++='z'; *p++='e'; *p++='\0'; + sprintf((char *)p, "%u", s->blksize_val); + p += strlen((const char *)p) + 1; + } + if (s->options & TFTP_OPTION_TIMEOUT) { + *p++='t'; *p++='i'; *p++='m'; *p++='e'; *p++='o'; *p++='u'; *p++='t'; *p++='\0'; + sprintf((char *)p, "%u", s->timeout_val); + p += strlen((const char *)p) + 1; + } + tftp_update_timestamp(s); + return (p - buffer); +} + +void tftp_parse_options(bx_devmodel_c *netdev, const char *mode, const Bit8u *data, + unsigned data_len, tftp_session_t *s) +{ + while (mode < (const char*)data + data_len) { + if (memcmp(mode, "octet\0", 6) == 0) { + s->options |= TFTP_OPTION_OCTET; + mode += 6; + } else if (memcmp(mode, "tsize\0", 6) == 0) { + s->options |= TFTP_OPTION_TSIZE; // size needed + mode += 6; + if (s->write) { + s->tsize_val = atoi(mode); + } + mode += strlen(mode)+1; + } else if (memcmp(mode, "blksize\0", 8) == 0) { + s->options |= TFTP_OPTION_BLKSIZE; + mode += 8; + s->blksize_val = atoi(mode); + if (s->blksize_val > TFTP_BUFFER_SIZE) { + BX_ERROR(("tftp req: blksize value %d not supported - using %d instead", + s->blksize_val, TFTP_BUFFER_SIZE)); + s->blksize_val = TFTP_BUFFER_SIZE; + } + mode += strlen(mode)+1; + } else if (memcmp(mode, "timeout\0", 8) == 0) { + s->options |= TFTP_OPTION_TIMEOUT; + mode += 8; + s->timeout_val = atoi(mode); + if ((s->timeout_val < 1) || (s->timeout_val > 255)) { + BX_ERROR(("tftp req: timeout value %d not supported - using %d instead", + s->timeout_val, TFTP_DEFAULT_TIMEOUT)); + s->timeout_val = TFTP_DEFAULT_TIMEOUT; + } + mode += strlen(mode)+1; + } else { + BX_ERROR(("tftp req: unknown option %s", mode)); + break; + } + } +} + +int vnet_process_tftp(bx_devmodel_c *netdev, const Bit8u *data, unsigned data_len, Bit16u req_tid, Bit8u *reply, const char *tftp_rootdir) +{ + FILE *fp; + unsigned block_nr; + unsigned tftp_len; + tftp_session_t *s; + + tftp_timeout_check(); + s = tftp_find_session(req_tid); + switch (get_net2(data)) { + case TFTP_RRQ: + { + if (s != NULL) { + tftp_remove_session(s); + } + strncpy((char*)reply, (const char*)data + 2, data_len - 2); + reply[data_len - 4] = 0; + + s = tftp_new_session(req_tid, 0, tftp_rootdir, (const char*)reply); + if (strlen(s->filename) == 0) { + return tftp_send_error(reply, 1, "Illegal file name", s); + } + // options + if (strlen((char*)reply) < data_len - 2) { + const char *mode = (const char*)data + 2 + strlen((char*)reply) + 1; + tftp_parse_options(netdev, mode, data, data_len, s); + } + if (!(s->options & TFTP_OPTION_OCTET)) { + return tftp_send_error(reply, 4, "Unsupported transfer mode", NULL); + } + if (s->options & TFTP_OPTION_TSIZE) { + struct stat stbuf; + if (stat(s->filename, &stbuf) < 0) { + s->options &= ~TFTP_OPTION_TSIZE; + } else { + s->tsize_val = (size_t)stbuf.st_size; + BX_DEBUG(("tftp filesize: %lu", (unsigned long)s->tsize_val)); + } + } + if ((s->options & ~TFTP_OPTION_OCTET) > 0) { + return tftp_send_optack(reply, s); + } else { + return tftp_send_data(reply, 1, s); + } + } + break; + + case TFTP_WRQ: + { + if (s != NULL) { + tftp_remove_session(s); + } + strncpy((char*)reply, (const char*)data + 2, data_len - 2); + reply[data_len - 4] = 0; + + s = tftp_new_session(req_tid, 1, tftp_rootdir, (const char*)reply); + if (strlen(s->filename) == 0) { + return tftp_send_error(reply, 1, "Illegal file name", s); + } + // options + if (strlen((char*)reply) < data_len - 2) { + const char *mode = (const char*)data + 2 + strlen((char*)reply) + 1; + tftp_parse_options(netdev, mode, data, data_len, s); + } + if (!(s->options & TFTP_OPTION_OCTET)) { + return tftp_send_error(reply, 4, "Unsupported transfer mode", NULL); + } + + fp = fopen(s->filename, "rb"); + if (fp) { + fclose(fp); + return tftp_send_error(reply, 6, "File exists", s); + } + fp = fopen(s->filename, "wb"); + if (!fp) { + return tftp_send_error(reply, 2, "Access violation", s); + } + fclose(fp); + + if ((s->options & ~TFTP_OPTION_OCTET) > 0) { + return tftp_send_optack(reply, s); + } else { + tftp_update_timestamp(s); + return tftp_send_ack(reply, 0); + } + } + break; + + case TFTP_DATA: + if (s != NULL) { + if (s->write == 1) { + block_nr = get_net2(data + 2); + strncpy((char*)reply, (const char*)data + 4, data_len - 4); + tftp_len = data_len - 4; + reply[tftp_len] = 0; + if (tftp_len <= s->blksize_val) { + fp = fopen(s->filename, "ab"); + if (!fp) { + return tftp_send_error(reply, 2, "Access violation", s); + } + if (fseek(fp, (block_nr - 1) * TFTP_BUFFER_SIZE, SEEK_SET) < 0) { + fclose(fp); + return tftp_send_error(reply, 3, "Block not seekable", s); + } + fwrite(reply, 1, tftp_len, fp); + fclose(fp); + if (tftp_len < s->blksize_val) { + tftp_remove_session(s); + } else { + tftp_update_timestamp(s); + } + return tftp_send_ack(reply, block_nr); + } else { + return tftp_send_error(reply, 4, "Illegal request", s); + } + } else { + return tftp_send_error(reply, 4, "Illegal request", s); + } + } else { + return tftp_send_error(reply, 5, "Unknown transfer ID", s); + } + break; + + case TFTP_ACK: + if (s != NULL) { + if (s->write == 0) { + return tftp_send_data(reply, get_net2(data + 2) + 1, s); + } else { + return tftp_send_error(reply, 4, "Illegal request", s); + } + } + break; + + case TFTP_ERROR: + if (s != NULL) { + tftp_remove_session(s); + } + break; + + default: + BX_ERROR(("TFTP unknown opt %d", get_net2(data))); + } + return 0; +} + +#endif /* if BX_NETWORKING */ diff --git a/bochs/iodev/network/netutil.h b/bochs/iodev/network/netutil.h new file mode 100644 index 000000000..6bf3946e6 --- /dev/null +++ b/bochs/iodev/network/netutil.h @@ -0,0 +1,149 @@ +///////////////////////////////////////////////////////////////////////// +// $Id$ +///////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2004-2017 The Bochs Project +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// + +// netutil.h - shared code for eth_vnet.cc and bxhub.cc + +#ifndef BX_NETUTIL_H +#define BX_NETUTIL_H + +#define ETHERNET_MAC_ADDR_LEN 6 +#define ETHERNET_TYPE_IPV4 0x0800 +#define ETHERNET_TYPE_ARP 0x0806 + +#define ARP_OPCODE_REQUEST 1 +#define ARP_OPCODE_REPLY 2 +#define ARP_OPCODE_REV_REQUEST 3 +#define ARP_OPCODE_REV_REPLY 4 + +#define ICMP_ECHO_PACKET_MAX 128 + +#define TFTP_BUFFER_SIZE 1024 + +#if defined(_MSC_VER) +#pragma pack(push, 1) +#elif defined(__MWERKS__) && defined(macintosh) +#pragma options align=packed +#endif + +typedef struct ethernet_header { +#if defined(_MSC_VER) && (_MSC_VER>=1300) + __declspec(align(1)) +#endif + Bit8u dst_mac_addr[ETHERNET_MAC_ADDR_LEN]; + Bit8u src_mac_addr[ETHERNET_MAC_ADDR_LEN]; + Bit16u type; +} +#if !defined(_MSC_VER) + GCC_ATTRIBUTE((packed)) +#endif +ethernet_header_t; + +typedef struct arp_header { +#if defined(_MSC_VER) && (_MSC_VER>=1300) + __declspec(align(1)) +#endif + Bit16u hw_addr_space; + Bit16u proto_addr_space; + Bit8u hw_addr_len; + Bit8u proto_addr_len; + Bit16u opcode; + /* HW address of sender */ + /* Protocol address of sender */ + /* HW address of target*/ + /* Protocol address of target */ +} +#if !defined(_MSC_VER) + GCC_ATTRIBUTE((packed)) +#endif +arp_header_t; + +typedef struct ip_header { +#if defined(_MSC_VER) && (_MSC_VER>=1300) + __declspec(align(1)) +#endif +#ifdef BX_LITTLE_ENDIAN + Bit8u header_len : 4; + Bit8u version : 4; +#else + Bit8u version : 4; + Bit8u header_len : 4; +#endif + Bit8u tos; + Bit16u total_len; + Bit16u id; + Bit16u frag_offs; + Bit8u ttl; + Bit8u protocol; + Bit16u checksum; + Bit32u src_addr; + Bit32u dst_addr; +} +#if !defined(_MSC_VER) + GCC_ATTRIBUTE((packed)) +#endif +ip_header_t; + +typedef struct udp_header { +#if defined(_MSC_VER) && (_MSC_VER>=1300) + __declspec(align(1)) +#endif + Bit16u src_port; + Bit16u dst_port; + Bit16u length; + Bit16u checksum; +} +#if !defined(_MSC_VER) + GCC_ATTRIBUTE((packed)) +#endif +udp_header_t; + +#if defined(_MSC_VER) +#pragma pack(pop) +#elif defined(__MWERKS__) && defined(macintosh) +#pragma options align=reset +#endif + +// DHCP configuration structure +typedef struct { + Bit8u host_macaddr[6]; + Bit8u guest_macaddr[6]; + Bit8u host_ipv4addr[4]; + Bit8u default_guest_ipv4addr[4]; + Bit8u guest_ipv4addr[4]; + Bit8u dns_ipv4addr[4]; +} dhcp_cfg_t; + +// vnet functions shared with bxhub +#ifdef BXHUB +#define bx_devmodel_c void +#endif +Bit16u ip_checksum(const Bit8u *buf, unsigned buf_len); +void vnet_prepare_reply(Bit8u *replybuf, unsigned l3type, dhcp_cfg_t *dhcpc); +bx_bool vnet_process_arp_request(const Bit8u *buf, Bit8u *reply, dhcp_cfg_t *dhcp); +bx_bool vnet_process_icmp_echo(const Bit8u *l3pkt, unsigned l3header_len, + const Bit8u *l4pkt, unsigned l4pkt_len, + Bit8u *reply); +int vnet_process_dhcp(bx_devmodel_c *netdev, const Bit8u *data, unsigned data_len, + Bit8u *reply, dhcp_cfg_t *dhcp); +int vnet_process_tftp(bx_devmodel_c *netdev, const Bit8u *data, unsigned data_len, + Bit16u req_tid, Bit8u *reply, const char *tftp_rootdir); + +#endif diff --git a/bochs/misc/bxhub.cc b/bochs/misc/bxhub.cc index dfe290d55..79804a9ee 100644 --- a/bochs/misc/bxhub.cc +++ b/bochs/misc/bxhub.cc @@ -57,6 +57,7 @@ typedef int SOCKET; #include "misc/bxcompat.h" #include "osdep.h" #include "iodev/network/netmod.h" +#include "iodev/network/netutil.h" #define BXHUB_MAX_CLIENTS 6 diff --git a/bochs/plugin.cc b/bochs/plugin.cc index 7c7181b9f..883fb8dfa 100644 --- a/bochs/plugin.cc +++ b/bochs/plugin.cc @@ -42,14 +42,18 @@ #define GUI_PLUGIN_FINI_FMT_STRING "lib%s_gui_plugin_fini" #define SOUND_PLUGIN_INIT_FMT_STRING "lib%s_sound_plugin_init" #define SOUND_PLUGIN_FINI_FMT_STRING "lib%s_sound_plugin_fini" +#define NET_PLUGIN_INIT_FMT_STRING "lib%s_net_plugin_init" +#define NET_PLUGIN_FINI_FMT_STRING "lib%s_net_plugin_fini" #define PLUGIN_PATH "" #ifndef WIN32 #define PLUGIN_FILENAME_FORMAT "libbx_%s.so" #define SOUND_PLUGIN_FILENAME_FORMAT "libbx_sound%s.so" +#define NET_PLUGIN_FILENAME_FORMAT "libbx_eth_%s.so" #else #define PLUGIN_FILENAME_FORMAT "bx_%s.dll" #define SOUND_PLUGIN_FILENAME_FORMAT "bx_sound%s.dll" +#define NET_PLUGIN_FILENAME_FORMAT "bx_eth_%s.dll" #endif logfunctions *pluginlog; @@ -334,10 +338,12 @@ void plugin_load(char *name, plugintype_t type) plugin->initialized = 0; char plugin_filename[BX_PATHNAME_LEN], tmpname[BX_PATHNAME_LEN]; - if (type != PLUGTYPE_SOUND) { - sprintf(tmpname, PLUGIN_FILENAME_FORMAT, name); - } else { + if (type == PLUGTYPE_SOUND) { sprintf(tmpname, SOUND_PLUGIN_FILENAME_FORMAT, name); + } else if (type == PLUGTYPE_NETWORK) { + sprintf(tmpname, NET_PLUGIN_FILENAME_FORMAT, name); + } else { + sprintf(tmpname, PLUGIN_FILENAME_FORMAT, name); } sprintf(plugin_filename, "%s%s", PLUGIN_PATH, tmpname); @@ -383,6 +389,8 @@ void plugin_load(char *name, plugintype_t type) sprintf(tmpname, GUI_PLUGIN_INIT_FMT_STRING, name); } else if (type == PLUGTYPE_SOUND) { sprintf(tmpname, SOUND_PLUGIN_INIT_FMT_STRING, name); + } else if (type == PLUGTYPE_NETWORK) { + sprintf(tmpname, NET_PLUGIN_INIT_FMT_STRING, name); } else if (type != PLUGTYPE_USER) { sprintf(tmpname, PLUGIN_INIT_FMT_STRING, name); } else { @@ -406,6 +414,8 @@ void plugin_load(char *name, plugintype_t type) sprintf(tmpname, GUI_PLUGIN_FINI_FMT_STRING, name); } else if (type == PLUGTYPE_SOUND) { sprintf(tmpname, SOUND_PLUGIN_FINI_FMT_STRING, name); + } else if (type == PLUGTYPE_NETWORK) { + sprintf(tmpname, NET_PLUGIN_FINI_FMT_STRING, name); } else if (type != PLUGTYPE_USER) { sprintf(tmpname, PLUGIN_FINI_FMT_STRING, name); } else { @@ -799,6 +809,7 @@ typedef struct { #define BUILTIN_GUI_PLUGIN_ENTRY(mod) {#mod, PLUGTYPE_GUI, lib##mod##_gui_plugin_init, lib##mod##_gui_plugin_fini, 0} #define BUILTIN_OPT_PLUGIN_ENTRY(mod) {#mod, PLUGTYPE_OPTIONAL, lib##mod##_LTX_plugin_init, lib##mod##_LTX_plugin_fini, 0} #define BUILTIN_SND_PLUGIN_ENTRY(mod) {#mod, PLUGTYPE_SOUND, lib##mod##_sound_plugin_init, lib##mod##_sound_plugin_fini, 0} +#define BUILTIN_NET_PLUGIN_ENTRY(mod) {#mod, PLUGTYPE_NETWORK, lib##mod##_net_plugin_init, lib##mod##_net_plugin_fini, 0} static builtin_plugin_t builtin_plugins[] = { #if BX_WITH_AMIGAOS @@ -905,6 +916,34 @@ static builtin_plugin_t builtin_plugins[] = { BUILTIN_SND_PLUGIN_ENTRY(win), #endif BUILTIN_SND_PLUGIN_ENTRY(file), +#endif +#if BX_NETWORKING +#if BX_NETMOD_FBSD + BUILTIN_NET_PLUGIN_ENTRY(fbsd), +#endif +#if BX_NETMOD_LINUX + BUILTIN_NET_PLUGIN_ENTRY(linux), +#endif + BUILTIN_NET_PLUGIN_ENTRY(null), +#if BX_NETMOD_SLIRP + BUILTIN_NET_PLUGIN_ENTRY(slirp), +#endif +#if BX_NETMOD_SOCKET + BUILTIN_NET_PLUGIN_ENTRY(socket), +#endif +#if BX_NETMOD_TAP + BUILTIN_NET_PLUGIN_ENTRY(tap), +#endif +#if BX_NETMOD_TUNTAP + BUILTIN_NET_PLUGIN_ENTRY(tuntap), +#endif +#if BX_NETMOD_VDE + BUILTIN_NET_PLUGIN_ENTRY(vde), +#endif + BUILTIN_NET_PLUGIN_ENTRY(vnet), +#if BX_NETMOD_WIN32 + BUILTIN_NET_PLUGIN_ENTRY(win32), +#endif #endif {"NULL", PLUGTYPE_GUI, NULL, NULL, 0} }; @@ -965,6 +1004,25 @@ int bx_unload_snd_plugin(const char *name) } #endif +#if BX_NETWORKING +int bx_unload_net_plugin(const char *name) +{ + int i = 0; + while (strcmp(builtin_plugins[i].name, "NULL")) { + if ((!strcmp(name, builtin_plugins[i].name)) && + (builtin_plugins[i].type == PLUGTYPE_NETWORK)) { + if (builtin_plugins[i].status == 1) { + builtin_plugins[i].plugin_fini(); + builtin_plugins[i].status = 0; + } + return 1; + } + i++; + }; + return 0; +} +#endif + #endif } diff --git a/bochs/plugin.h b/bochs/plugin.h index 18ec244b7..9266b1127 100644 --- a/bochs/plugin.h +++ b/bochs/plugin.h @@ -59,7 +59,6 @@ extern "C" { #define BX_PLUGIN_PCI_IDE "pci_ide" #define BX_PLUGIN_SB16 "sb16" #define BX_PLUGIN_ES1370 "es1370" -#define BX_PLUGIN_NETMOD "netmod" #define BX_PLUGIN_NE2K "ne2k" #define BX_PLUGIN_EXTFPUIRQ "extfpuirq" #define BX_PLUGIN_PCIDEV "pcidev" @@ -88,10 +87,12 @@ extern "C" { #define PLUG_load_gui_plugin(name) bx_load_plugin(name,PLUGTYPE_GUI) #define PLUG_load_opt_plugin(name) bx_load_plugin(name,PLUGTYPE_OPTIONAL) #define PLUG_load_snd_plugin(name) bx_load_plugin(name,PLUGTYPE_SOUND) +#define PLUG_load_net_plugin(name) bx_load_plugin(name,PLUGTYPE_NETWORK) #define PLUG_load_user_plugin(name) {bx_load_plugin(name,PLUGTYPE_USER);} #define PLUG_unload_plugin(name) {bx_unload_plugin(#name,1);} #define PLUG_unload_opt_plugin(name) bx_unload_plugin(name,1) #define PLUG_unload_snd_plugin(name) bx_unload_plugin(name,1) +#define PLUG_unload_net_plugin(name) bx_unload_plugin(name,1) #define PLUG_unload_user_plugin(name) {bx_unload_plugin(name,1);} #define DEV_register_ioread_handler(b,c,d,e,f) pluginRegisterIOReadHandler(b,c,d,e,f) @@ -116,9 +117,11 @@ extern "C" { #define PLUG_load_gui_plugin(name) bx_load_plugin2(name,PLUGTYPE_GUI) #define PLUG_load_opt_plugin(name) bx_load_plugin2(name,PLUGTYPE_OPTIONAL) #define PLUG_load_snd_plugin(name) bx_load_plugin2(name,PLUGTYPE_SOUND) +#define PLUG_load_net_plugin(name) bx_load_plugin2(name,PLUGTYPE_NETWORK) #define PLUG_unload_plugin(name) {lib##name##_LTX_plugin_fini();} #define PLUG_unload_opt_plugin(name) bx_unload_opt_plugin(name,1); #define PLUG_unload_snd_plugin(name) bx_unload_snd_plugin(name); +#define PLUG_unload_net_plugin(name) bx_unload_net_plugin(name); #define DEV_register_ioread_handler(b,c,d,e,f) bx_devices.register_io_read_handler(b,c,d,e,f) #define DEV_register_iowrite_handler(b,c,d,e,f) bx_devices.register_io_write_handler(b,c,d,e,f) @@ -269,7 +272,7 @@ extern "C" { ///////// Networking module macro #define DEV_net_init_module(a,b,c,d) \ - ((eth_pktmover_c*)bx_devices.pluginNetModCtl->init_module(a,(void*)b,(void*)c,d)) + ((eth_pktmover_c*)bx_netmod_ctl.init_module(a,(void*)b,(void*)c,d)) ///////// Gameport macro #define DEV_gameport_set_enabled(a) bx_devices.pluginGameport->set_enabled(a) @@ -379,6 +382,9 @@ int plugin_init(plugin_t *plugin, plugintype_t type); #define DECLARE_PLUGIN_INIT_FINI_FOR_SOUND_MODULE(mod) \ extern "C" __declspec(dllexport) int __cdecl lib##mod##_sound_plugin_init(plugin_t *plugin, plugintype_t type); \ extern "C" __declspec(dllexport) void __cdecl lib##mod##_sound_plugin_fini(void); +#define DECLARE_PLUGIN_INIT_FINI_FOR_NET_MODULE(mod) \ + extern "C" __declspec(dllexport) int __cdecl lib##mod##_net_plugin_init(plugin_t *plugin, plugintype_t type); \ + extern "C" __declspec(dllexport) void __cdecl lib##mod##_net_plugin_fini(void); #else #define DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(mod) \ int CDECL lib##mod##_LTX_plugin_init(plugin_t *plugin, plugintype_t type); \ @@ -389,6 +395,9 @@ int plugin_init(plugin_t *plugin, plugintype_t type); #define DECLARE_PLUGIN_INIT_FINI_FOR_SOUND_MODULE(mod) \ int CDECL lib##mod##_sound_plugin_init(plugin_t *plugin, plugintype_t type); \ void CDECL lib##mod##_sound_plugin_fini(void); +#define DECLARE_PLUGIN_INIT_FINI_FOR_NET_MODULE(mod) \ + int CDECL lib##mod##_net_plugin_init(plugin_t *plugin, plugintype_t type); \ + void CDECL lib##mod##_net_plugin_fini(void); #endif // device plugins @@ -451,6 +460,17 @@ DECLARE_PLUGIN_INIT_FINI_FOR_SOUND_MODULE(oss) DECLARE_PLUGIN_INIT_FINI_FOR_SOUND_MODULE(osx) DECLARE_PLUGIN_INIT_FINI_FOR_SOUND_MODULE(sdl) DECLARE_PLUGIN_INIT_FINI_FOR_SOUND_MODULE(win) +// network driver plugins +DECLARE_PLUGIN_INIT_FINI_FOR_NET_MODULE(fbsd) +DECLARE_PLUGIN_INIT_FINI_FOR_NET_MODULE(linux) +DECLARE_PLUGIN_INIT_FINI_FOR_NET_MODULE(null) +DECLARE_PLUGIN_INIT_FINI_FOR_NET_MODULE(slirp) +DECLARE_PLUGIN_INIT_FINI_FOR_NET_MODULE(socket) +DECLARE_PLUGIN_INIT_FINI_FOR_NET_MODULE(tap) +DECLARE_PLUGIN_INIT_FINI_FOR_NET_MODULE(tuntap) +DECLARE_PLUGIN_INIT_FINI_FOR_NET_MODULE(vde) +DECLARE_PLUGIN_INIT_FINI_FOR_NET_MODULE(vnet) +DECLARE_PLUGIN_INIT_FINI_FOR_NET_MODULE(win32) #ifdef __cplusplus