Re: [hatari-devel] Network support for Hatari

[ Thread Index | Date Index | More lists.tuxfamily.org/hatari-devel Archives ]


Small update, because i just realized that LOG_TRACE() in Hatari needs a trailing newline.

 

PS.: while looking after the STinG drivers, i think there is a problem. AFAIK, TOS 2.x/TOS 3.x sets a default interrupt level of 3, which would permanently block the interrupt i'm using. Any idea how to solve that?

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 90d2830f..577b8b03 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -74,11 +74,13 @@ endif(CapsImage_FOUND)
 
 link_directories(${CMAKE_CURRENT_BINARY_DIR}/debug
 		 ${CMAKE_CURRENT_BINARY_DIR}/falcon
+		 ${CMAKE_CURRENT_BINARY_DIR}/ethernet
 		 ${CMAKE_CURRENT_BINARY_DIR}/gui-sdl
 		 ${CMAKE_CURRENT_BINARY_DIR}/cpu)
 
 add_subdirectory(debug)
 add_subdirectory(falcon)
+add_subdirectory(ethernet)
 add_subdirectory(gui-sdl)
 add_subdirectory(cpu)
 
@@ -153,7 +155,7 @@ if(SDL2_OTHER_CFLAGS)
 	# message(STATUS "Additional CFLAGS of SDL: ${SDL2_OTHER_CFLAGS}")
 endif(SDL2_OTHER_CFLAGS)
 
-target_link_libraries(hatari Falcon UaeCpu GuiSdl Floppy Debug ${SDL2_LIBRARY})
+target_link_libraries(hatari Falcon UaeCpu GuiSdl Floppy Debug Ethernet ${SDL2_LIBRARY})
 
 if(Math_FOUND AND NOT APPLE)
 	target_link_libraries(hatari ${MATH_LIBRARY})
diff --git a/src/configuration.c b/src/configuration.c
index 15daca88..bb4a92d1 100644
--- a/src/configuration.c
+++ b/src/configuration.c
@@ -455,6 +455,18 @@ static const struct Config_Tag configs_Midi[] =
 	{ NULL , Error_Tag, NULL }
 };
 
+/* Used to load/save ETHERNET options */
+static const struct Config_Tag configs_Ethernet[] =
+{
+	{ "sType0", String_Tag, ConfigureParams.Ethernet[0].type },
+	{ "sTunnel0", String_Tag, ConfigureParams.Ethernet[0].tunnel },
+	{ "sHostIP0", String_Tag, ConfigureParams.Ethernet[0].ip_host },
+	{ "sAtariIP0", String_Tag, ConfigureParams.Ethernet[0].ip_atari },
+	{ "sNetmask0", String_Tag, ConfigureParams.Ethernet[0].netmask },
+	{ "sMAC0", String_Tag, ConfigureParams.Ethernet[0].mac_addr },
+	{ NULL , Error_Tag, NULL }
+};
+
 /* Used to load system options from old config files */
 static int nOldMachineType;
 static bool bOldRealTimeClock;
@@ -683,6 +695,25 @@ void Configuration_SetDefault(void)
 	strcpy(ConfigureParams.Midi.sMidiInPortName, "Off");
 	strcpy(ConfigureParams.Midi.sMidiOutPortName, "Off");
 
+	/* Set defaults for ETHERNET */
+	/* ETH[0] with some default values */
+	strcpy(ConfigureParams.Ethernet[0].type, "ptp");
+	strcpy(ConfigureParams.Ethernet[0].tunnel, "tap0");
+	strcpy(ConfigureParams.Ethernet[0].ip_host, "192.168.0.1");
+	strcpy(ConfigureParams.Ethernet[0].ip_atari, "192.168.0.2");
+	strcpy(ConfigureParams.Ethernet[0].netmask, "255.255.255.0");
+	strcpy(ConfigureParams.Ethernet[0].mac_addr, "00:41:45:54:48:30");
+	/* ETH[1] - ETH[MAX_ETH] are empty by default */
+	for (i = 1; i < MAX_ETH; i++)
+	{
+		ConfigureParams.Ethernet[i].type[0] = '\0';
+		ConfigureParams.Ethernet[i].tunnel[0] = '\0';
+		ConfigureParams.Ethernet[i].ip_host[0] = '\0';
+		ConfigureParams.Ethernet[i].ip_atari[0] = '\0';
+		ConfigureParams.Ethernet[i].netmask[0] = '\0';
+		ConfigureParams.Ethernet[i].mac_addr[0] = '\0';
+	}
+
 	/* Set defaults for Screen */
 	ConfigureParams.Screen.bFullScreen = false;
 	ConfigureParams.Screen.bKeepResolution = true;
@@ -1018,6 +1049,7 @@ void Configuration_Load(const char *psFileName)
 	Configuration_LoadSection(psFileName, configs_Rs232, "[RS232]");
 	Configuration_LoadSection(psFileName, configs_Printer, "[Printer]");
 	Configuration_LoadSection(psFileName, configs_Midi, "[Midi]");
+	Configuration_LoadSection(psFileName, configs_Ethernet, "[Network]");
 	Configuration_LoadSection(psFileName, configs_System, "[System]");
 	Configuration_LoadSection(psFileName, configs_Video, "[Video]");
 }
@@ -1075,6 +1107,7 @@ void Configuration_Save(void)
 	Configuration_SaveSection(sConfigFileName, configs_Rs232, "[RS232]");
 	Configuration_SaveSection(sConfigFileName, configs_Printer, "[Printer]");
 	Configuration_SaveSection(sConfigFileName, configs_Midi, "[Midi]");
+	Configuration_SaveSection(sConfigFileName, configs_Ethernet, "[Network]");
 	Configuration_SaveSection(sConfigFileName, configs_System, "[System]");
 	Configuration_SaveSection(sConfigFileName, configs_Video, "[Video]");
 }
diff --git a/src/cpu/hatari-glue.c b/src/cpu/hatari-glue.c
index 94dad131..d41b7970 100644
--- a/src/cpu/hatari-glue.c
+++ b/src/cpu/hatari-glue.c
@@ -80,8 +80,12 @@ int intlev(void)
 {
 	if ( pendingInterrupts & (1 << 6) )		/* MFP/DSP interrupt ? */
 		return 6;
+	else if ( pendingInterrupts & (1 << 5) )	/* SCC interrupt ? */
+		return 5;
 	else if ( pendingInterrupts & (1 << 4) )	/* VBL interrupt ? */
 		return 4;
+	else if ( pendingInterrupts & (1 << 3) )	/* VME interrupt ? */
+		return 3;
 	else if ( pendingInterrupts & (1 << 2) )	/* HBL interrupt ? */
 		return 2;
 
diff --git a/src/cpu/newcpu.c b/src/cpu/newcpu.c
index 5f3f2f55..508d8d41 100644
--- a/src/cpu/newcpu.c
+++ b/src/cpu/newcpu.c
@@ -2834,7 +2834,7 @@ static int iack_cycle(int nr)
 				pendingInterrupts &= ~( 1 << 6 );
 		}
 	}
-	else if ( ( nr == 26 ) || ( nr == 28 ) )				/* HBL / VBL */
+	else if ( ( nr == 26 ) || ( nr == 27 ) || ( nr == 28 ) || ( nr == 29 ) )				/* HBL / VBL / VME / SCC */
 	{
 		if (cycle_exact)
 		{
diff --git a/src/debug/log.c b/src/debug/log.c
index eb437479..226af451 100644
--- a/src/debug/log.c
+++ b/src/debug/log.c
@@ -148,6 +148,8 @@ static flagname_t TraceFlags[] = {
 
 	{ TRACE_SCSIDRV		 , "scsidrv" } ,
 
+	{ TRACE_ETHERNET	 , "ethernet" } ,
+
 	{ TRACE_MEM		 , "mem" } ,
 
 	{ TRACE_VME		 , "vme" } ,
diff --git a/src/debug/log.h b/src/debug/log.h
index 9be233f9..7db70297 100644
--- a/src/debug/log.h
+++ b/src/debug/log.h
@@ -184,6 +184,8 @@ extern char *Log_MatchTrace(const char *text, int state);
 
 #define TRACE_VME		 (1ll<<55)
 
+#define TRACE_ETHERNET		 (1ll<<56)
+
 #define	TRACE_NONE		 (0)
 #define	TRACE_ALL		 (~0)
 
diff --git a/src/debug/natfeats.c b/src/debug/natfeats.c
index 517ed1e9..93fa14a0 100644
--- a/src/debug/natfeats.c
+++ b/src/debug/natfeats.c
@@ -22,6 +22,7 @@ const char Natfeats_fileid[] = "Hatari natfeats.c";
 #include "debugui.h"
 #include "statusbar.h"
 #include "nf_scsidrv.h"
+#include "nf_ethernet.h"
 #include "log.h"
 
 /* maximum input string length */
@@ -286,9 +287,12 @@ static const struct {
 	{ "NF_SHUTDOWN", true,  nf_shutdown },
 	{ "NF_EXIT",     false, nf_exit },
 	{ "NF_DEBUGGER", false, nf_debugger },
-	{ "NF_FASTFORWARD", false,  nf_fastforward }
+	{ "NF_FASTFORWARD", false,  nf_fastforward },
 #if defined(__linux__)        
-        ,{ "NF_SCSIDRV",  true, nf_scsidrv }
+	{ "NF_SCSIDRV",  true,  nf_scsidrv },
+#endif
+#ifdef WITH_NF_ETHERNET
+	{ "ETHERNET",    false, nf_ethernet },
 #endif
 };
 
diff --git a/src/ethernet/CMakeLists.txt b/src/ethernet/CMakeLists.txt
new file mode 100644
index 00000000..adab63b9
--- /dev/null
+++ b/src/ethernet/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_library(Ethernet
+	nf_ethernet.c ethernet_linux.c ethernet_darwin.c
+	ethernet_macosx.c ethernet_win32.c fd_trans.c)
diff --git a/src/ethernet/ethernet.h b/src/ethernet/ethernet.h
new file mode 100644
index 00000000..a54b64ee
--- /dev/null
+++ b/src/ethernet/ethernet.h
@@ -0,0 +1,55 @@
+/*
+ * ethernet.h - Ethernet Card Emulation
+ *
+ * Copyright (c) 2007 ARAnyM dev team (see AUTHORS)
+ *
+ * This file is part of the ARAnyM project which builds a new and powerful
+ * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
+ *
+ * ARAnyM 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.
+ *
+ * ARAnyM 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 ARAnyM. If not see <http://www.gnu.org/licenses/>
+ */
+
+#include "nf_ethernet.h"
+
+#define MAX_PACKET_SIZE	9000
+
+struct EthernetHandler {
+	int ethX;
+	ssize_t packet_length;
+	uint8_t packet[MAX_PACKET_SIZE+2];
+	SDL_Thread *handlingThread;	/* Packet reception thread */
+	SDL_sem *intAck;			/* Interrupt acknowledge semaphore */
+
+	bool (*open)(struct EthernetHandler *);
+	void (*close)(struct EthernetHandler *);
+	int (*recv)(struct EthernetHandler *, uint8_t *, int);
+	int (*send)(struct EthernetHandler *, const uint8_t *, int);
+};
+
+#if defined(__CYGWIN32__) || defined(__WIN32__)
+   struct EthernetHandler *WinTapEthernetHandler(int eth_idx);
+#  define ETHERNET_HANDLER_CLASSNAME WinTapEthernetHandler
+#elif defined(__APPLE__)
+#  define ENABLE_BPF 1
+#  if ENABLE_BPF
+      struct EthernetHandler *BPFEthernetHandler(int eth_idx);
+#     define ETHERNET_HANDLER_CLASSNAME BPFEthernetHandler
+#  else
+      struct EthernetHandler *TunTapEthernetHandler(int eth_idx);
+#     define ETHERNET_HANDLER_CLASSNAME TunTapEthernetHandler
+#  endif
+#elif defined(__linux__)
+   struct EthernetHandler *TunTapEthernetHandler(int eth_idx);
+#  define ETHERNET_HANDLER_CLASSNAME TunTapEthernetHandler
+#endif
diff --git a/src/ethernet/ethernet_darwin.c b/src/ethernet/ethernet_darwin.c
new file mode 100644
index 00000000..1a5ce1ef
--- /dev/null
+++ b/src/ethernet/ethernet_darwin.c
@@ -0,0 +1,352 @@
+/*
+ * ethernet_darwin.c - Darwin Ethernet support (via TUN/TAP driver)
+ *
+ * Copyright (c) 2007 ARAnyM dev team (see AUTHORS)
+ *
+ * Inspired by Bernie Meyer's UAE-JIT and Gwenole Beauchesne's Basilisk II-JIT
+ *
+ * This file is part of the ARAnyM project which builds a new and powerful
+ * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
+ *
+ * ARAnyM 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.
+ *
+ * ARAnyM 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 ARAnyM. If not see <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+#include "config.h"
+#include <SDL.h>
+#include <SDL_thread.h>
+#include "stMemory.h"
+#include "log.h"
+#include "gemdos_defines.h"
+#include "ethernet.h"
+#include "configuration.h"
+
+#if defined(WITH_NF_ETHERNET) && defined(__APPLE__) && !defined(ENABLE_BPF)
+
+#include "screen.h" /* bInFullScreen */
+#include "paths.h"
+
+#include <sys/wait.h>					/* waitpid() */
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <errno.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/Authorization.h>
+#include <Security/AuthorizationTags.h>
+
+/****************************
+ * Configuration zone begins
+ */
+
+#define TAP_SHELL	"/bin/sh"
+#define TAP_INIT	"aratapif.sh"
+#define TAP_MTU		"1500"
+
+/*
+ * Configuration zone ends
+ **************************/
+
+struct TunTapEthernetHandler
+{
+	struct EthernetHandler base;
+
+	int fd;
+};
+
+
+static OSStatus authStatus;
+static AuthorizationRef authorizationRef;
+static bool fullscreen;
+
+
+static int openAuthorizationContext(void)
+{
+	if (authorizationRef == NULL)
+	{
+		AuthorizationFlags authFlags = kAuthorizationFlagDefaults;	/* 1 */
+
+		authStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,	/* 3 */
+										 authFlags, &authorizationRef);	/* 4 */
+		if (authStatus != errAuthorizationSuccess)
+			return authStatus;
+
+		fullscreen = bInFullScreen;
+		if (fullscreen)
+			Screen_ReturnFromFullScreen();
+
+		AuthorizationItem authItems = { kAuthorizationRightExecute, 0,	/* 5 */
+			NULL, 0
+		};								/* 6 */
+		AuthorizationRights authRights = { 1, &authItems };	/* 7 */
+		authFlags = kAuthorizationFlagDefaults |	/* 8 */
+			kAuthorizationFlagInteractionAllowed |	/* 9 */
+			kAuthorizationFlagPreAuthorize |	/* 10 */
+			kAuthorizationFlagExtendRights;	/* 11 */
+		authStatus = AuthorizationCopyRights(authorizationRef, &authRights, NULL, authFlags, NULL);	/* 12 */
+	}
+	return authStatus;
+}
+
+
+static void closeAuthorizationContext(void)
+{
+	if (authorizationRef)
+	{
+		AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults);	/* 17 */
+		authorizationRef = NULL;
+
+		if (fullscreen)
+		{
+			Screen_EnterFullScreen();
+		}
+	}
+}
+
+
+static int executeScriptAsRoot(char *args[])
+{
+	OSStatus execStatus;
+	char app[FILENAME_MAX];
+	FILE *authCommunicationsPipe = NULL;
+	char execReadBuffer[128];
+	AuthorizationFlags authFlags = kAuthorizationFlagDefaults;	/* 13 */
+
+	/*
+	   CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
+	   CFURLGetFileSystemRepresentation(url, true, (uint8_t) *)app, MAXPATHLEN);
+	   printf("TunTap: Binary directory '%s'\n", app);
+	   CFRelease(url); */
+	strcpy(app, Paths_GetWorkingDir());
+	strcat(app, "/");
+	strcat(app, args[0]);
+	args[0] = app;
+
+	execStatus = AuthorizationExecuteWithPrivileges	/* 14 */
+		(authorizationRef, TAP_SHELL, authFlags, args,	/* 15 */
+		 &authCommunicationsPipe);		/* 16 */
+
+	if (execStatus == errAuthorizationSuccess)
+		for (;;)
+		{
+			int bytesRead = read(fileno(authCommunicationsPipe),
+								 execReadBuffer, sizeof(execReadBuffer));
+
+			if (bytesRead < 1)
+				break;
+			write(fileno(stdout), execReadBuffer, bytesRead);
+		}
+
+	return execStatus;
+}
+
+
+/*
+ * Allocate ETHERNETDriver TAP device, returns opened fd.
+ * Stores dev name in the first arg(must be large enough).
+ */
+static int tapOpenOld(struct TunTapEthernetHandler *handler, char *dev)
+{
+	char tapname[sizeof(ConfigureParams.Ethernet[0].tunnel) + 5];
+	int i;
+	int fd;
+
+	if (*dev)
+	{
+		snprintf(tapname, sizeof(tapname), "/dev/%s", dev);
+		tapname[sizeof(tapname) - 1] = '\0';
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): tapOpenOld %s\n", handler->base.ethX, tapname);
+		fd = open(tapname, O_RDWR);
+		if (fd > 0)
+			return fd;
+	}
+
+	for (i = 0; i < 255; i++)
+	{
+		sprintf(tapname, "/dev/tap%d", i);
+		/* Open device */
+		if ((fd = open(tapname, O_RDWR)) > 0)
+		{
+			sprintf(dev, "tap%d", i);
+			LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): tapOpenOld %s\n", handler->base.ethX, dev);
+			return fd;
+		}
+	}
+	return -1;
+}
+
+
+static int tapOpen(struct TunTapEthernetHandler *handler, char *dev)
+{
+	return tapOpenOld(handler, dev);
+}
+
+
+static bool TunTapEthernetHandler_open(struct EthernetHandler *_handler)
+{
+	struct TunTapEthernetHandler *handler = (struct TunTapEthernetHandler *) _handler;
+	/* int nonblock = 1; */
+	char *type = ConfigureParams.Ethernet[handler->base.ethX].type;
+	char *devName = ConfigureParams.Ethernet[handler->base.ethX].tunnel;
+	int auth;
+	bool failed;
+
+	handler->base.close(&handler->base);
+
+	if (strlen(type) == 0 || strcmp(ConfigureParams.Ethernet[handler->base.ethX].type, "none") == 0)
+	{
+		if (handler->base.ethX == MAX_ETH - 1)
+			closeAuthorizationContext();
+		return false;
+	}
+
+	/* if 'bridge' mode then we are done */
+	if (strstr(ConfigureParams.Ethernet[handler->base.ethX].type, "bridge") != NULL)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: TunTap(%d): Bridge mode currently not supported '%s'\n", handler->base.ethX, devName);
+		if (handler->base.ethX == MAX_ETH - 1)
+			closeAuthorizationContext();
+		return false;
+	}
+
+	/* get the tunnel nif name if provided */
+	if (strlen(devName) == 0)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): tunnel name undefined\n", handler->base.ethX);
+		if (handler->base.ethX == MAX_ETH - 1)
+			closeAuthorizationContext();
+		return false;
+	}
+
+	LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): open('%s')\n", handler->base.ethX, devName);
+
+	handler->fd = tapOpen(handler, devName);
+	if (handler->fd < 0)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: TunTap(%d): NO_NET_DRIVER_WARN '%s': %s\n", handler->base.ethX, devName, strerror(errno));
+		if (handler->base.ethX == MAX_ETH - 1)
+			closeAuthorizationContext();
+		return false;
+	}
+	auth = openAuthorizationContext();
+
+	if (auth)
+	{
+		handler->base.close(&handler->base);
+		LOG_TRACE(LOG_WARN, "Ethernet: TunTap(%d): Authorization failed'%s'\n", handler->base.ethX, devName);
+		return false;
+	}
+
+	failed = true;
+
+	{
+		/* the arguments _need_ to be placed into the child process */
+		/* memory (otherwise this does not work here) */
+		static char tap_mtu[] = TAP_MTU;
+		static char tap_init[] = TAP_INIT;
+		char *args[] = {
+			tap_init,
+			ConfigureParams.Ethernet[handler->base.ethX].tunnel,
+			ConfigureParams.Ethernet[handler->base.ethX].ip_host,
+			ConfigureParams.Ethernet[handler->base.ethX].ip_atari,
+			ConfigureParams.Ethernet[handler->base.ethX].netmask,
+			tap_mtu, NULL
+		};
+
+		int result = executeScriptAsRoot(args);
+
+		if (result != 0)
+		{
+			LOG_TRACE(LOG_WARN, "Ethernet: TunTap(%d): ERROR: " TAP_INIT " failed (code %d). Ethernet disabled!\n", handler->base.ethX, result);
+		} else
+		{
+			failed = false;
+			LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): " TAP_INIT " initialized OK\n", handler->base.ethX);
+		}
+	}
+
+	/* Close /dev/net/tun device if exec failed */
+	if (failed)
+	{
+		handler->base.close(&handler->base);
+		if (handler->base.ethX == MAX_ETH - 1)
+			closeAuthorizationContext();
+		return false;
+	}
+
+	/* Set nonblocking I/O */
+	/*ioctl(handler->fd, FIONBIO, &nonblock); */
+
+	if (handler->base.ethX == MAX_ETH - 1)
+		closeAuthorizationContext();
+	return true;
+}
+
+
+static void TunTapEthernetHandler_close(struct EthernetHandler *_handler)
+{
+	struct TunTapEthernetHandler *handler = (struct TunTapEthernetHandler *) _handler;
+	LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): close\n", handler->base.ethX);
+	/* Close /dev/net/tun device */
+	if (handler->fd > 0)
+	{
+		close(handler->fd);
+		handler->fd = -1;
+	}
+}
+
+
+static int TunTapEthernetHandler_recv(struct EthernetHandler *_handler, uint8_t *buf, int len)
+{
+	struct TunTapEthernetHandler *handler = (struct TunTapEthernetHandler *) _handler;
+	return read(handler->fd, buf, len);
+}
+
+
+static int TunTapEthernetHandler_send(struct EthernetHandler *_handler, const uint8_t *buf, int len)
+{
+	struct TunTapEthernetHandler *handler = (struct TunTapEthernetHandler *) _handler;
+	int res = write(handler->fd, buf, len);
+
+	if (res < 0)
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): WARNING: Couldn't transmit packet\n", handler->base.ethX);
+	return res;
+}
+
+
+struct EthernetHandler *TunTapEthernetHandler(int eth_idx)
+{
+	struct TunTapEthernetHandler *handler = (struct TunTapEthernetHandler *) malloc(sizeof(*handler));
+
+	handler->base.ethX = eth_idx;
+	handler->base.packet_length = 0;
+	handler->base.handlingThread = NULL;
+	handler->base.intAck = NULL;
+
+	handler->base.open = TunTapEthernetHandler_open;
+	handler->base.close = TunTapEthernetHandler_close;
+	handler->base.recv = TunTapEthernetHandler_recv;
+	handler->base.send = TunTapEthernetHandler_send;
+
+	handler->fd = -1;
+
+	return &handler->base;
+}
+
+
+#endif /* WITH_NF_ETHERNET */
diff --git a/src/ethernet/ethernet_linux.c b/src/ethernet/ethernet_linux.c
new file mode 100644
index 00000000..c1c60f11
--- /dev/null
+++ b/src/ethernet/ethernet_linux.c
@@ -0,0 +1,335 @@
+/*
+ * ethernet_linux.c - Linux Ethernet support (via TUN/TAP driver)
+ *
+ * Copyright (c) 2007 ARAnyM dev team (see AUTHORS)
+ *
+ * This file is part of the ARAnyM project which builds a new and powerful
+ * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
+ *
+ * ARAnyM 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.
+ *
+ * ARAnyM 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 ARAnyM. If not see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "config.h"
+#include <SDL.h>
+#include <SDL_thread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "stMemory.h"
+#include "log.h"
+#include "gemdos_defines.h"
+#include "ethernet.h"
+#include "configuration.h"
+
+#if defined(WITH_NF_ETHERNET) && defined (__linux__)
+
+#include <sys/poll.h>
+#include <sys/wait.h>					/* waitpid() */
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#define HAVE_LINUX_IF_H
+#define HAVE_LINUX_IF_TUN_H
+#define HAVE_SYS_SOCKET_H
+
+#if defined(HAVE_LINUX_IF_H) && defined(HAVE_LINUX_IF_TUN_H)
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#endif
+#if defined(HAVE_NET_IF_TUN_H)
+#include <net/if_tun.h>
+#endif
+
+#include <errno.h>
+
+/****************************
+ * Configuration zone begins
+ */
+
+#define TAP_INIT	"aratapif"
+#define TAP_MTU		"1500"
+
+/*
+ * Configuration zone ends
+ **************************/
+
+
+struct TunTapEthernetHandler {
+	struct EthernetHandler base;
+
+	int fd;
+};
+
+
+/*
+ * Allocate ETHERNETDriver TAP device, returns opened fd.
+ * Stores dev name in the first arg(must be large enough).
+ */
+static int tapOpenOld(struct TunTapEthernetHandler *handler, char *dev)
+{
+	char tapname[sizeof(ConfigureParams.Ethernet[0].tunnel) + 5];
+	int i, fd;
+
+	if (*dev)
+	{
+		snprintf(tapname, sizeof(tapname), "/dev/%s", dev);
+		tapname[sizeof(tapname) - 1] = '\0';
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): tapOpenOld %s\n", handler->base.ethX, tapname);
+		return open(tapname, O_RDWR);
+	}
+
+	for (i = 0; i < 255; i++)
+	{
+		sprintf(tapname, "/dev/tap%d", i);
+		/* Open device */
+		if ((fd = open(tapname, O_RDWR)) > 0)
+		{
+			sprintf(dev, "tap%d", i);
+			LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): tapOpenOld %s\n", handler->base.ethX, dev);
+			return fd;
+		}
+	}
+	return -1;
+}
+
+
+#ifdef HAVE_LINUX_IF_TUN_H				/* New driver support */
+
+/* pre 2.4.6 compatibility */
+#define OTUNSETNOCSUM  (('T'<< 8) | 200)
+#define OTUNSETDEBUG   (('T'<< 8) | 201)
+#define OTUNSETIFF     (('T'<< 8) | 202)
+#define OTUNSETPERSIST (('T'<< 8) | 203)
+#define OTUNSETOWNER   (('T'<< 8) | 204)
+
+static int tapOpen(struct TunTapEthernetHandler *handler, char *dev)
+{
+	struct ifreq ifr;
+
+	if ((handler->fd = open("/dev/net/tun", O_RDWR)) < 0)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: TunTap(%d): Error opening /dev/net/tun. Check if module is loaded and privileges are OK\n", handler->base.ethX);
+		return tapOpenOld(handler, dev);
+	}
+
+	memset(&ifr, 0, sizeof(ifr));
+
+	ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+	if (*dev)
+	{
+		int len = strlen(dev);
+		if (len >= IFNAMSIZ)
+			len = IFNAMSIZ - 1;
+		memcpy(ifr.ifr_name, dev, len);
+		ifr.ifr_name[len] = '\0';
+	}
+
+	if (ioctl(handler->fd, TUNSETIFF, (void *) &ifr) < 0)
+	{
+		if (errno != EBADFD)
+			goto failed;
+
+		/* Try old ioctl */
+		if (ioctl(handler->fd, OTUNSETIFF, (void *) &ifr) < 0)
+			goto failed;
+	}
+
+	strcpy(dev, ifr.ifr_name);
+
+	LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): if opened %s\n", handler->base.ethX, dev);
+	return handler->fd;
+
+  failed:
+	handler->base.close(&handler->base);
+	return -1;
+}
+
+#else
+
+static int tapOpen(struct TunTapEthernetHandler *handler, char *dev)
+{
+	return tapOpenOld(handler, dev);
+}
+
+#endif /* New driver support */
+
+
+static bool TunTapEthernetHandler_open(struct EthernetHandler *_handler)
+{
+	struct TunTapEthernetHandler *handler = (struct TunTapEthernetHandler *) _handler;
+	int pid;
+	int status;
+	bool failed;
+
+	/* int nonblock = 1; */
+	char *type = ConfigureParams.Ethernet[handler->base.ethX].type;
+	char *devName = ConfigureParams.Ethernet[handler->base.ethX].tunnel;
+
+	handler->base.close(&handler->base);
+
+	if (strcmp(type, "none") == 0 || strlen(type) == 0)
+	{
+		return false;
+	}
+
+	/* get the tunnel nif name if provided */
+	if (strlen(devName) == 0)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): tunnel name undefined\n", handler->base.ethX);
+		return false;
+	}
+
+	LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): open('%s')\n", handler->base.ethX, devName);
+
+	handler->fd = tapOpen(handler, devName);
+	if (handler->fd < 0)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: TunTap(%d): NO_NET_DRIVER_WARN '%s': %s\n", handler->base.ethX, devName, strerror(errno));
+		return false;
+	}
+
+	/* if 'bridge' mode then we are done */
+	if (strstr(ConfigureParams.Ethernet[handler->base.ethX].type, "bridge") != NULL)
+		return true;
+
+	pid = fork();
+
+	if (pid < 0)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: TunTap(%d): ERROR: fork() failed. Ethernet disabled!\n", handler->base.ethX);
+		handler->base.close(&handler->base);
+		return false;
+	}
+
+	if (pid == 0)
+	{
+		/* the arguments _need_ to be placed into the child process */
+		/* memory (otherwise this does not work here) */
+		static char tap_init[] = TAP_INIT;
+		static char tap_mtu[] = TAP_MTU;
+		char *args[] = {
+			tap_init,
+			ConfigureParams.Ethernet[handler->base.ethX].tunnel,
+			ConfigureParams.Ethernet[handler->base.ethX].ip_host,
+			ConfigureParams.Ethernet[handler->base.ethX].ip_atari,
+			ConfigureParams.Ethernet[handler->base.ethX].netmask,
+			tap_mtu, NULL
+		};
+		int result;
+
+		result = execvp(TAP_INIT, args);
+		_exit(result);
+	}
+
+	LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): waiting for " TAP_INIT " at pid %d\n", handler->base.ethX, pid);
+
+	waitpid(pid, &status, 0);
+	failed = true;
+
+	if (WIFEXITED(status))
+	{
+		int err = WEXITSTATUS(status);
+
+		if (err == 255)
+		{
+			LOG_TRACE(LOG_WARN, "Ethernet: TunTap(%d): ERROR: " TAP_INIT " not found. Ethernet disabled!\n", handler->base.ethX);
+		} else if (err != 0)
+		{
+			LOG_TRACE(LOG_WARN, "Ethernet: TunTap(%d): ERROR: " TAP_INIT " failed (code %d). Ethernet disabled!\n", handler->base.ethX, err);
+		} else
+		{
+			failed = false;
+			LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): " TAP_INIT " initialized OK\n", handler->base.ethX);
+		}
+	} else
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: TunTap(%d): ERROR: " TAP_INIT " could not be started. Ethernet disabled!\n", handler->base.ethX);
+	}
+
+	/* Close /dev/net/tun device if exec failed */
+	if (failed)
+	{
+		handler->base.close(&handler->base);
+		return false;
+	}
+
+	/* Set nonblocking I/O */
+	/* ioctl(handler->fd, FIONBIO, &nonblock); */
+
+	return true;
+}
+
+
+static void TunTapEthernetHandler_close(struct EthernetHandler *_handler)
+{
+	struct TunTapEthernetHandler *handler = (struct TunTapEthernetHandler *) _handler;
+
+	LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): close\n", handler->base.ethX);
+
+	/* Close /dev/net/tun device */
+	if (handler->fd > 0)
+	{
+		close(handler->fd);
+		handler->fd = -1;
+	}
+}
+
+
+static int TunTapEthernetHandler_recv(struct EthernetHandler *_handler, uint8_t *buf, int len)
+{
+	struct TunTapEthernetHandler *handler = (struct TunTapEthernetHandler *) _handler;
+
+	return read(handler->fd, buf, len);
+}
+
+
+static int TunTapEthernetHandler_send(struct EthernetHandler *_handler, const uint8_t *buf, int len)
+{
+	struct TunTapEthernetHandler *handler = (struct TunTapEthernetHandler *) _handler;
+	int res = write(handler->fd, buf, len);
+
+	if (res < 0)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: TunTap(%d): WARNING: Couldn't transmit packet\n", handler->base.ethX);
+	}
+	return res;
+}
+
+
+struct EthernetHandler *TunTapEthernetHandler(int eth_idx)
+{
+	struct TunTapEthernetHandler *handler = (struct TunTapEthernetHandler *) malloc(sizeof(*handler));
+
+	handler->base.ethX = eth_idx;
+	handler->base.packet_length = 0;
+	handler->base.handlingThread = NULL;
+	handler->base.intAck = NULL;
+
+	handler->base.open = TunTapEthernetHandler_open;
+	handler->base.close = TunTapEthernetHandler_close;
+	handler->base.recv = TunTapEthernetHandler_recv;
+	handler->base.send = TunTapEthernetHandler_send;
+
+	handler->fd = -1;
+
+	return &handler->base;
+}
+
+#endif /* WITH_NF_ETHERNET */
diff --git a/src/ethernet/ethernet_macosx.c b/src/ethernet/ethernet_macosx.c
new file mode 100644
index 00000000..d7045e57
--- /dev/null
+++ b/src/ethernet/ethernet_macosx.c
@@ -0,0 +1,688 @@
+/*
+ * ethernet_macosx.c - Mac OS X Ethernet support (via Berkley Packet Filter device)
+ *
+ * Copyright (c) 2007 ARAnyM dev team (see AUTHORS)
+ *
+ * This file is part of the ARAnyM project which builds a new and powerful
+ * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
+ *
+ * ARAnyM 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.
+ *
+ * ARAnyM 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 ARAnyM. If not see <http://www.gnu.org/licenses/>
+ */
+
+
+#include "config.h"
+#include <SDL.h>
+#include <SDL_thread.h>
+#include "stMemory.h"
+#include "log.h"
+#include "gemdos_defines.h"
+#include "ethernet.h"
+#include "configuration.h"
+
+#if defined(WITH_NF_ETHERNET) && defined(__APPLE__) && defined(ENABLE_BPF)
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+#include <sys/fcntl.h>
+#include <sys/select.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <net/bpf.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "fd_trans.h"
+
+#define ETH_HELPER "bpf_helper"
+
+struct BPFEthernetHandler
+{
+	struct EthernetHandler base;
+
+	bool debug;
+	int fd;
+	int buf_len;
+	struct bpf_hdr *bpf_buf;
+
+	/* variables used for looping over multiple packets */
+	int read_len;
+	struct bpf_hdr *bpf_packet;
+};
+
+/* BPF filter program to ensure only ethernet packets are processed which are for configured MAC */
+static struct bpf_insn bpf_filter_mac_insn[] = {
+	BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 0),                      /*      ld  P[0:4] */
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0xAAAAAAAA, 0, 3),      /*      jeq destMAC, CONT, NOK */
+	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 4),                      /* CONT:ld  P[4:2] */
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0xAAAA, 0, 1),          /*      jeq destMAC, OK, NOK */
+	BPF_STMT(BPF_RET + BPF_K, (u_int) -1),                      /* OK:  ret -1 */
+	BPF_STMT(BPF_RET + BPF_K, 0),                               /* NOK: ret 0 */
+};
+
+static struct bpf_program bpf_filter_mac = { sizeof(bpf_filter_mac_insn) / sizeof(struct bpf_insn), &bpf_filter_mac_insn[0] };
+
+/* BPF filter program to process multicast ethernet and configured MAC address packets */
+static struct bpf_insn bpf_filter_mcast_insn[] = {
+	BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 0),                      /*      ld  P[0:4] */
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0xAAAAAAAA, 0, 2),      /*      jeq destMAC, CONT, NOK1 */
+	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 4),                      /* CONT:ld  P[4:2] */
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0xAAAA, 2, 0),          /*      jeq destMAC, OK, NOK1 */
+	BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 0),                      /* NOK1:ld  P[0:1] */
+	BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0x01, 0, 1),           /*      if (A & 1) OK else NOK2 */
+	BPF_STMT(BPF_RET + BPF_K, (u_int) -1),                      /* OK:  ret -1 */
+	BPF_STMT(BPF_RET + BPF_K, 0),                               /* NOK2:ret 0 */
+};
+
+static struct bpf_program bpf_filter_mcast = { sizeof(bpf_filter_mcast_insn) / sizeof(struct bpf_insn), &bpf_filter_mcast_insn[0] };
+
+/* BPF filter program to process ethernet packets if the configured IP address matches */
+static struct bpf_insn bpf_filter_ip_insn[] = {
+	/* Check ethernet protocol (IP and ARP supported) */
+	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),                     /*          ld  P[12:2] */
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0800, 3, 0),          /*          jeq IP_Type, CONT_IP, CONT */
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0806, 0, 5),          /* CONT:    jeq IP_Type, CONT_ARP, NOK */
+	/* Load IP address from offset 38 */
+	BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 38),                     /* CONT_ARP:ld  P[38:4] */
+	BPF_JUMP(BPF_JMP + BPF_JA, 1, 1, 1),                        /*          jmp COMPARE */
+	/* Load IP address from offset 30 */
+	BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 30),                     /* CONT_IP: ld  P[30:4] */
+	/* Compare value with expected dest IP address */
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0xAAAAAAAA, 0, 1),      /* COMPARE: jeq destIP, OK, NOK */
+	BPF_STMT(BPF_RET + BPF_K, (u_int) -1),                      /* OK:      ret -1 */
+	BPF_STMT(BPF_RET + BPF_K, 0),                               /* NOK:     ret 0 */
+};
+
+static struct bpf_program bpf_filter_ip = { sizeof(bpf_filter_ip_insn) / sizeof(struct bpf_insn), &bpf_filter_ip_insn[0] };
+
+
+/* Ethernet frame */
+struct ethernet_frame
+{
+	unsigned char dst_addr[6];
+	unsigned char src_addr[6];
+	unsigned char type[2];
+	unsigned char payload;
+};
+
+/* ARP packet structure */
+struct arp_packet
+{
+	unsigned char hardware_type[2];
+	unsigned char proto_type[2];
+	unsigned char hardware_addr_length;
+	unsigned char proto_addr_length;
+	unsigned char opcode[2];
+	unsigned char src_mac[6];
+	unsigned char src_ip[4];
+	unsigned char dst_mac[6];
+	unsigned char dst_ip[4];
+};
+
+/* IP packet structure */
+struct ip_packet
+{
+	unsigned char version_hdr_len;
+	unsigned char services;
+	unsigned char tot_len[2];
+	unsigned char ident[2];
+	unsigned char flags_frag_offset[2];
+	unsigned char ttl;
+	unsigned char proto_type;
+	unsigned char hdr_chksum[2];
+	unsigned char src_ip[4];
+	unsigned char dst_ip[4];
+	unsigned char data;
+};
+
+static void dump_frame(const char *prefix, const struct ethernet_frame *frame)
+{
+	unsigned short int frame_type = (frame->type[0] << 8) | frame->type[1];
+
+	LOG_TRACE(TRACE_ETHERNET, "  %s: %02x:%02x:%02x:%02x:%02x:%02x > %02x:%02x:%02x:%02x:%02x:%02x  (Type %04x)\n", prefix,
+		frame->src_addr[0], frame->src_addr[1], frame->src_addr[2], frame->src_addr[3], frame->src_addr[4],
+		frame->src_addr[5], frame->dst_addr[0], frame->dst_addr[1], frame->dst_addr[2], frame->dst_addr[3],
+		frame->dst_addr[4], frame->dst_addr[5], frame_type);
+	if (frame_type == 0x0806) /* TYPE_ARP */
+	{
+		const struct arp_packet *arp = (const struct arp_packet *) &frame->payload;
+
+		LOG_TRACE(TRACE_ETHERNET, "  %s: ARP packet %d.%d.%d.%d > %d.%d.%d.%d  (Type %04x)\n", prefix,
+			arp->src_ip[0], arp->src_ip[1], arp->src_ip[2], arp->src_ip[3],
+			arp->dst_ip[0], arp->dst_ip[1], arp->dst_ip[2], arp->dst_ip[3],
+			(arp->proto_type[0] << 8) | arp->proto_type[1]);
+	} else if (frame_type == 0x0800) /* TYPE_IP */
+	{
+		const struct ip_packet *ip = (const struct ip_packet *) &frame->payload;
+		int tot_len = (ip->tot_len[0] << 8) + ip->tot_len[1];
+
+		LOG_TRACE(TRACE_ETHERNET, "  %s: IP packet %d.%d.%d.%d > %d.%d.%d.%d  (Type %04x Total %02x%02x = %d, Payload=%d)\n", prefix,
+			ip->src_ip[0], ip->src_ip[1], ip->src_ip[2], ip->src_ip[3],
+			ip->dst_ip[0], ip->dst_ip[1], ip->dst_ip[2], ip->dst_ip[3],
+			ip->proto_type, ip->tot_len[0], ip->tot_len[1], tot_len, tot_len - (int) sizeof(struct ip_packet) + 1);
+
+#if 0
+		for (unsigned char *ptr=&ip->data; ptr<(unsigned char*)&ip->data+tot_len; ptr++)
+			LOG_TRACE(TRACE_ETHERNET, ">%*s\n",  tot_len-(sizeof(struct ip_packet)-1), &ip->data);
+#endif
+	}
+}
+
+static void dump_bpf_buf(const char *prefix, struct bpf_hdr *bpf_buf)
+{
+	if (bpf_buf->bh_datalen > 0)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "  %s: %d bytes in buf:\n", prefix, bpf_buf->bh_datalen);
+
+		dump_frame(prefix, (const struct ethernet_frame *) ((char *) bpf_buf + bpf_buf->bh_hdrlen));
+	}
+}
+
+
+static void reset_read_pos(struct BPFEthernetHandler *handler)
+{
+	handler->read_len = 0;
+	handler->bpf_packet = NULL;
+}
+
+
+static bool BPFEthernetHandler_open(struct EthernetHandler *_handler)
+{
+	struct BPFEthernetHandler *handler = (struct BPFEthernetHandler *) _handler;
+	/* int nonblock = 1; */
+	char *type = ConfigureParams.Ethernet[handler->base.ethX].type;
+	char *dev_name = ConfigureParams.Ethernet[handler->base.ethX].tunnel;
+	int sockfd;
+	char exe_path[2048];
+	CFURLRef url;
+	CFURLRef url2;
+	pid_t pid;
+	struct ifreq ifr;
+	int immediate;
+	int promiscuous;
+	int complete;
+	int seesent;
+	int len;
+
+	handler->base.close(&handler->base);
+
+	if (strcmp(type, "none") == 0 || strlen(type) == 0)
+	{
+		return false;
+	}
+
+	handler->debug = strstr(type, "debug") != NULL;
+	if (handler->debug)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): debug mode=%d\n", handler->base.ethX, handler->debug);
+	}
+
+	if (handler->debug)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): open() type=%s\n", handler->base.ethX, type);
+	}
+	if (strstr(type, "bridge") == NULL)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): unsupported type '%s'\n", handler->base.ethX, type);
+		return false;
+	}
+
+	if ((sockfd = fd_setup_server()) < 0)
+	{
+		fprintf(stderr, "receiver: failed to create socket.\n");
+		return false;
+	}
+
+	/******************************************************
+	 Fork child process to get an open BPF file descriptor
+	 ******************************************************/
+	url = CFBundleCopyExecutableURL(CFBundleGetMainBundle());
+	url2 = CFURLCreateCopyDeletingLastPathComponent(0, url);
+
+	CFURLGetFileSystemRepresentation(url2, true, (uint8_t *) exe_path, sizeof(exe_path));
+	CFRelease(url2);
+	CFRelease(url);
+	len = (int)strlen(exe_path);
+	if (len > 0 && exe_path[len - 1] != '/')
+		strcat(exe_path, "/");
+	strcat(exe_path, ETH_HELPER);
+	if (handler->debug)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): starting helper from <%s>\n", handler->base.ethX, exe_path);
+	}
+
+	pid = fork();
+
+	if (pid < 0)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): ERROR: fork() failed. Ethernet disabled!\n", handler->base.ethX);
+		close(sockfd);
+		return false;
+	} else if (pid == 0)
+	{
+		int result;
+
+		if (handler->debug)
+		{
+			LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): " ETH_HELPER " child running\n", handler->base.ethX);
+		}
+		result = execl(exe_path, exe_path, NULL);
+
+		_exit(result);
+	}
+
+	if (handler->debug)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): waiting for " ETH_HELPER " (PID %d) to send file descriptor\n",
+				  handler->base.ethX, pid);
+	}
+	handler->fd = fd_receive(sockfd, pid);
+	close(sockfd);
+	if (handler->fd < 0)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): failed receiving file descriptor from " ETH_HELPER ".\n", handler->base.ethX);
+		return false;
+	}
+	if (handler->debug)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): got file descriptor %d\n", handler->base.ethX, handler->fd);
+	}
+
+
+	/******************************************************
+	 Configure BPF device
+	 ******************************************************/
+	/* associate with specified interface */
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, dev_name, IFNAMSIZ - 1);
+	if (handler->debug)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): connecting with device %s\n", handler->base.ethX, dev_name);
+	}
+	if (ioctl(handler->fd, BIOCSETIF, &ifr) > 0)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): Failed associating to %s: %s\n", handler->base.ethX, dev_name, strerror(errno));
+		handler->base.close(&handler->base);
+		return false;
+	}
+
+	/* activate immediate mode */
+	immediate = 1;
+
+	if (handler->debug)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): enabling immediate mode\n", handler->base.ethX);
+	}
+	if (ioctl(handler->fd, BIOCIMMEDIATE, &immediate) == -1)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): Unable to set immediate mode: %s\n", handler->base.ethX, strerror(errno));
+		handler->base.close(&handler->base);
+		return false;
+	}
+
+	/* request buffer length */
+	if (ioctl(handler->fd, BIOCGBLEN, &handler->buf_len) == -1)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): Unable to get buffer length: %s\n", handler->base.ethX, strerror(errno));
+		handler->base.close(&handler->base);
+		return false;
+	}
+	if (handler->debug)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): buf_len=%d\n", handler->base.ethX, handler->buf_len);
+	}
+	handler->bpf_buf = (struct bpf_hdr *) malloc(handler->buf_len);
+
+	/* activate promiscuous mode */
+	if (handler->debug)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): enabling promiscuous mode\n", handler->base.ethX);
+	}
+	promiscuous = 1;
+	if (ioctl(handler->fd, BIOCPROMISC, &promiscuous) == -1)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): Unable to set promiscuous mode: %s\n", handler->base.ethX, strerror(errno));
+#if 0
+		handler->base.close(&handler->base);
+		return false;
+#endif
+	}
+
+	/* activate "header complete" mode */
+	if (handler->debug)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): enabling header complete mode\n", handler->base.ethX);
+	}
+	complete = 1;
+	if (ioctl(handler->fd, BIOCGHDRCMPLT, &complete) == -1)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): Unable to set header complete mode: %s\n", handler->base.ethX, strerror(errno));
+		handler->base.close(&handler->base);
+		return false;
+	}
+
+	/* disable "see sent" mode */
+	if (handler->debug)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): disabling see sent mode\n", handler->base.ethX);
+	}
+	seesent = 1;
+	if (ioctl(handler->fd, BIOCSSEESENT, &seesent) == -1)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): Unable to disable see sent mode: %s\n", handler->base.ethX, strerror(errno));
+		handler->base.close(&handler->base);
+		return false;
+	}
+
+	if (strstr(type, "nofilter") == NULL)
+	{
+		/* convert and validate MAC address from configuration */
+		/* default MAC Address is just made up */
+		uint8_t mac_addr[6] = { '\0', 'A', 'E', 'T', 'H', 0 };
+
+		/* convert user-defined MAC Address from string to 6 bytes array */
+		char *mac_text = ConfigureParams.Ethernet[handler->base.ethX].mac_addr;
+		bool format_OK = false;
+		uint8_t ip_addr[4];
+		struct bpf_program *filter;
+
+		mac_addr[5] = handler->base.ethX + '0';
+		if (strlen(mac_text) == 2 * 6 + 5 && (mac_text[2] == ':' || mac_text[2] == '-'))
+		{
+			int matched;
+			int md[6] = { 0, 0, 0, 0, 0, 0 };
+
+			mac_text[2] = mac_text[5] = mac_text[8] = mac_text[11] = mac_text[14] = ':';
+			matched = sscanf(mac_text, "%02x:%02x:%02x:%02x:%02x:%02x",
+								 &md[0], &md[1], &md[2], &md[3], &md[4], &md[5]);
+
+			if (matched == 6)
+			{
+				int i;
+
+				for (i = 0; i < 6; i++)
+					mac_addr[i] = md[i];
+				format_OK = true;
+			}
+		}
+		if (!format_OK)
+		{
+			LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): Invalid MAC address: %s\n", handler->base.ethX, mac_text);
+			handler->base.close(&handler->base);
+			return false;
+		}
+
+		/* convert and validate specified IP address */
+
+		if (!inet_pton(AF_INET, ConfigureParams.Ethernet[handler->base.ethX].ip_atari, ip_addr))
+		{
+			LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): Invalid IP address specified: %s\n", handler->base.ethX,
+					  ConfigureParams.Ethernet[handler->base.ethX].ip_atari);
+			handler->base.close(&handler->base);
+			return false;
+		}
+
+		/* modify filter program to use specified IP address */
+		if (handler->debug)
+		{
+			LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): setting filter program for MAC address %s\n", handler->base.ethX,
+					  mac_text);
+		}
+
+		/* Select filter program according to chosen options */
+
+		if (strstr(type, "mcast"))
+		{
+			filter = &bpf_filter_mcast;
+
+			/* patch filter instructions to detect valid MAC address */
+			filter->bf_insns[1].k = (mac_addr[0] << 24) | (mac_addr[1] << 16) | (mac_addr[2] << 8) | mac_addr[3];
+			filter->bf_insns[3].k = (mac_addr[4] << 8) | mac_addr[5];
+		} else if (strstr(type, "ip"))
+		{
+			filter = &bpf_filter_ip;
+
+			/* patch filter instructions to detect valid IP address */
+			filter->bf_insns[6].k = (ip_addr[0] << 24) | (ip_addr[1] << 16) | (ip_addr[2] << 8) | ip_addr[3];
+
+		} else
+		{
+			filter = &bpf_filter_mac;
+			/* patch filter instructions to detect valid MAC address */
+			filter->bf_insns[1].k = (mac_addr[0] << 24) | (mac_addr[1] << 16) | (mac_addr[2] << 8) | mac_addr[3];
+			filter->bf_insns[3].k = (mac_addr[4] << 8) | mac_addr[5];
+		}
+
+		/* Enable filter program */
+		if (ioctl(handler->fd, BIOCSETF, filter) == -1)
+		{
+			LOG_TRACE(LOG_WARN, "Ethernet: BPF(%d): Unable to load filter program: %s\n", handler->base.ethX, strerror(errno));
+			handler->base.close(&handler->base);
+			return false;
+		}
+		if (handler->debug)
+		{
+			LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): filter program load\n", handler->base.ethX);
+		}
+	} else
+	{
+		if (handler->debug)
+		{
+			LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): filter program skipped\n", handler->base.ethX);
+		}
+	}
+
+
+	/* Reset bpf buffer read position (for handling multi packet) */
+	reset_read_pos(handler);
+
+	return true;
+}
+
+
+static void BPFEthernetHandler_close(struct EthernetHandler *_handler)
+{
+	struct BPFEthernetHandler *handler = (struct BPFEthernetHandler *) _handler;
+
+	if (handler->debug)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): close\n", handler->base.ethX);
+	}
+
+	reset_read_pos(handler);
+
+	if (handler->fd > 0)
+	{
+		close(handler->fd);
+		handler->fd = -1;
+	}
+
+	free(handler->bpf_buf);
+	handler->bpf_buf = NULL;
+
+	handler->debug = false;
+}
+
+
+static int BPFEthernetHandler_recv(struct EthernetHandler *_handler, uint8_t *buf, int len)
+{
+	struct BPFEthernetHandler *handler = (struct BPFEthernetHandler *) _handler;
+	char *ptr;
+
+	if (handler->debug)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): recv(len=%d)\n", handler->base.ethX, len);
+	}
+
+	/* No more cached packet in memory? */
+	if (handler->bpf_packet == NULL)
+	{
+		/* Read a packet from the ethernet device, timeout after 2s */
+		/* Initialize the file descriptor set. */
+		fd_set set;
+		struct timeval timeout;
+
+		FD_ZERO(&set);
+		FD_SET(handler->fd, &set);
+
+		/* Initialize the timeout data structure. */
+		timeout.tv_sec = 2;
+		timeout.tv_usec = 0;
+
+		/* select returns 0 if timeout, 1 if input available, -1 if error. */
+		if (select(FD_SETSIZE, &set, NULL, NULL, &timeout) == 1)
+		{
+			/* Read from BPF device */
+			handler->read_len = read(handler->fd, handler->bpf_buf, handler->buf_len);
+			if (handler->debug)
+			{
+				LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): %d bytes read => %d data bytes\n", handler->base.ethX, handler->read_len,
+						  handler->bpf_buf->bh_datalen);
+			}
+			if (handler->read_len > 0)
+			{
+				handler->bpf_packet = handler->bpf_buf;
+
+				if ((int)BPF_WORDALIGN(handler->bpf_buf->bh_hdrlen + handler->bpf_buf->bh_caplen) < handler->read_len)
+				{
+					if (handler->debug)
+					{
+						LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): More than one packet received by BPF\n"
+							  "   read_len = %d\n"
+							  "   bh_hdrlen=%d\n"
+							  "   bh_datalen=%d\n"
+							  "   bh_caplen=%d\n", handler->base.ethX, handler->read_len, handler->bpf_buf->bh_hdrlen,
+							  handler->bpf_buf->bh_datalen, handler->bpf_buf->bh_caplen);
+					}
+				}
+			}
+		}
+	} else
+	{
+		if (handler->debug)
+		{
+			LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): using cached %d data bytes from previous read\n", handler->base.ethX,
+					  handler->read_len);
+		}
+	}
+
+	ptr = (char *) handler->bpf_packet;
+
+	if (handler->bpf_packet && (ptr < ((char *) handler->bpf_buf + handler->read_len)))
+	{
+		char *frame_start = ptr + handler->bpf_packet->bh_hdrlen;
+		int frame_len = handler->bpf_packet->bh_caplen;
+
+		ptr += BPF_WORDALIGN(handler->bpf_packet->bh_hdrlen + handler->bpf_packet->bh_caplen);
+
+		/* Copy valid frame data */
+		if (frame_len <= len)
+		{
+			if (handler->debug)
+			{
+				LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): frame length %d bytes\n", handler->base.ethX, frame_len);
+				dump_bpf_buf("recv", handler->bpf_packet);
+			}
+			memcpy(buf, frame_start, frame_len);
+		} else
+		{
+
+			LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): Host side received %d bytes of data but only %d bytes expected by guest.\n"
+				"There are probably multiple packets in the received %d bytes.\n"
+				"Packet discarded!\n", handler->base.ethX, frame_len, len, handler->read_len);
+			LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): read_len = %d\n"
+				"   bh_hdrlen=%d\n"
+				"   bh_datalen=%d\n"
+				"   bh_caplen=%d\n", handler->base.ethX, handler->read_len, handler->bpf_packet->bh_hdrlen, handler->bpf_packet->bh_datalen,
+				handler->bpf_packet->bh_caplen);
+			frame_len = 0;
+		}
+
+		if (ptr < ((char *) handler->bpf_buf + handler->read_len))
+			handler->bpf_packet = (struct bpf_hdr *) ptr;
+		else
+			handler->bpf_packet = NULL;
+
+		return frame_len;
+	}
+	return 0;
+}
+
+
+static int BPFEthernetHandler_send(struct EthernetHandler *_handler, const uint8_t *buf, int len)
+{
+	struct BPFEthernetHandler *handler = (struct BPFEthernetHandler *) _handler;
+	int res = -1;
+
+	if (handler->debug)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): send(len=%d)\n", handler->base.ethX, len);
+	}
+
+	if (len > 0)
+	{
+		if (handler->debug)
+		{
+			LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): send(len=%d)\n", handler->base.ethX, len);
+			dump_frame("send", (const struct ethernet_frame *) buf);
+		}
+		res = write(handler->fd, buf, len);
+		if (res < 0)
+		{
+			if (handler->debug)
+			{
+				LOG_TRACE(TRACE_ETHERNET, "Ethernet: BPF(%d): WARNING: Couldn't transmit packet\n", handler->base.ethX);
+			}
+		}
+	}
+	return res;
+}
+
+
+
+struct EthernetHandler *BPFEthernetHandler(int eth_idx)
+{
+	struct BPFEthernetHandler *handler = (struct BPFEthernetHandler *) malloc(sizeof(*handler));
+
+	handler->base.ethX = eth_idx;
+	handler->base.packet_length = 0;
+	handler->base.handlingThread = NULL;
+	handler->base.intAck = NULL;
+
+	handler->base.open = BPFEthernetHandler_open;
+	handler->base.close = BPFEthernetHandler_close;
+	handler->base.recv = BPFEthernetHandler_recv;
+	handler->base.send = BPFEthernetHandler_send;
+
+	handler->debug = false;
+	handler->fd = -1;
+	handler->buf_len = 0;
+	handler->bpf_buf = NULL;
+	handler->read_len = 0;
+	handler->bpf_packet = NULL;
+
+	return &handler->base;
+}
+
+#endif /* WITH_NF_ETHERNET */
diff --git a/src/ethernet/ethernet_nfapi.h b/src/ethernet/ethernet_nfapi.h
new file mode 100644
index 00000000..d97bcaf4
--- /dev/null
+++ b/src/ethernet/ethernet_nfapi.h
@@ -0,0 +1,55 @@
+/*
+ * ARAnyM ethernet driver - header file.
+ *
+ * Copyright (c) 2002-2004 Standa and Petr of ARAnyM dev team (see AUTHORS)
+ *
+ * This file is part of the ARAnyM project which builds a new and powerful
+ * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
+ *
+ * ARAnyM 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.
+ *
+ * ARAnyM 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 ARAnyM. If not see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef _ARAETHER_NFAPI_H
+#define _ARAETHER_NFAPI_H
+
+/*
+   The code for the FreeMiNT driver was moved to the FreeMiNT CVS:
+   freemint/sys/sockets/xif/nfeth.
+
+   If you edit this file then you would need to synchronize it with the
+   driver in the freemint CVS (nfeth_nfapi.h).
+
+   if you change anything in the enum {} below you have to increase
+   this ARAETHER_NFAPI_VERSION!
+*/
+#define ARAETHER_NFAPI_VERSION	0x00000005
+
+enum {
+	GET_VERSION = 0,	/* no parameters, return NFAPI_VERSION in d0 */
+	XIF_INTLEVEL,		/* no parameters, return Interrupt Level in d0 */
+	XIF_IRQ,			/* acknowledge interrupt from host */
+	XIF_START,			/* (ethX), called on 'ifup', start receiver thread */
+	XIF_STOP,			/* (ethX), called on 'ifdown', stop the thread */
+	XIF_READLENGTH,		/* (ethX), return size of network data block to read */
+	XIF_READBLOCK,		/* (ethX, buffer, size), read block of network data */
+	XIF_WRITEBLOCK,		/* (ethX, buffer, size), write block of network data */
+	XIF_GET_MAC,		/* (ethX, buffer, size), return MAC HW addr in buffer */
+	XIF_GET_IPHOST,		/* (ethX, buffer, size), return IP address of host */
+	XIF_GET_IPATARI,	/* (ethX, buffer, size), return IP address of atari */
+	XIF_GET_NETMASK		/* (ethX, buffer, size), return IP netmask */
+};
+
+#define ETH(a)	(nfEtherID + a)
+
+#endif /* _ARAETHER_NFAPI_H */
diff --git a/src/ethernet/ethernet_win32.c b/src/ethernet/ethernet_win32.c
new file mode 100644
index 00000000..594c2a04
--- /dev/null
+++ b/src/ethernet/ethernet_win32.c
@@ -0,0 +1,456 @@
+/*
+    ethernet_win32.c -- Interaction with Windows tap driver in a Win32/Cygwin environment
+    Copyright (C) 2002-2004 Ivo Timmermans <ivo@xxxxxxxxxxxx>,
+                  2002-2004 Guus Sliepen <guus@xxxxxxxxxxxx>
+
+    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 see <http://www.gnu.org/licenses/>
+*/
+
+#include "config.h"
+#include <SDL.h>
+#include <SDL_thread.h>
+#include "stMemory.h"
+#include "log.h"
+#include "gemdos_defines.h"
+#include "ethernet.h"
+#include "configuration.h"
+
+#if defined(WITH_NF_ETHERNET) && (defined(__CYGWIN32__) || defined(__WIN32__))
+
+#define _WIN32_VERSION 0x501
+#undef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#if !defined(__CYGWIN__)
+#include <winsock2.h>
+#endif
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN /* to avoid redefinition in SDL headers */
+
+#include <winioctl.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#include <errno.h>
+
+#define MTU 1500
+
+/*============= */
+/* TAP IOCTLs */
+/*============= */
+
+#define TAP_CONTROL_CODE(request,method) \
+  CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
+
+#define TAP_IOCTL_GET_MAC               TAP_CONTROL_CODE (1, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_VERSION           TAP_CONTROL_CODE (2, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_MTU               TAP_CONTROL_CODE (3, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_INFO              TAP_CONTROL_CODE (4, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED)
+#define TAP_IOCTL_SET_MEDIA_STATUS      TAP_CONTROL_CODE (6, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_DHCP_MASQ      TAP_CONTROL_CODE (7, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_LOG_LINE          TAP_CONTROL_CODE (8, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_DHCP_SET_OPT   TAP_CONTROL_CODE (9, METHOD_BUFFERED)
+
+/*================= */
+/* Registry keys */
+/*================= */
+
+#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+/*====================== */
+/* Filesystem prefixes */
+/*====================== */
+
+#define USERMODEDEVICEDIR "\\\\.\\Global\\"
+#define SYSDEVICEDIR      "\\Device\\"
+#define USERDEVICEDIR     "\\DosDevices\\Global\\"
+#define TAPSUFFIX         ".tap"
+
+/*========================================================= */
+/* TAP_COMPONENT_ID -- This string defines the TAP driver */
+/* type -- different component IDs can reside in the system */
+/* simultaneously. */
+/*========================================================= */
+
+#define TAP_COMPONENT_ID "tap0801"
+
+
+struct WinTapEthernetHandler
+{
+	struct EthernetHandler base;
+
+	OVERLAPPED read_overlapped;
+	OVERLAPPED write_overlapped;
+	HANDLE device_handle;
+	char *device;
+	char *iface;
+
+	int device_total_in;
+	int device_total_out;
+};
+
+
+static char *winerror(int err)
+{
+	static char buf[1024];
+	char *newline;
+
+	if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+					   NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, sizeof(buf), NULL))
+	{
+		strncpy(buf, "(unable to format errormessage)", sizeof(buf));
+	}
+
+	if ((newline = strchr(buf, '\r')))
+		*newline = '\0';
+
+	return buf;
+}
+
+
+static bool is_tap_win32_dev(const char *guid)
+{
+	HKEY netcard_key;
+	LONG status;
+	DWORD len;
+	int i = 0;
+
+	status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, ADAPTER_KEY, 0, KEY_READ, &netcard_key);
+
+	if (status != ERROR_SUCCESS)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap: Error opening registry key: %s\n", ADAPTER_KEY);
+		return false;
+	}
+
+	for (;;)
+	{
+		char enum_name[256];
+		char unit_string[256];
+		HKEY unit_key;
+		char component_id_string[] = "ComponentId";
+		char component_id[256];
+		char net_cfg_instance_id_string[] = "NetCfgInstanceId";
+		char net_cfg_instance_id[256];
+		DWORD data_type;
+
+		len = sizeof(enum_name);
+		status = RegEnumKeyEx(netcard_key, i, enum_name, &len, NULL, NULL, NULL, NULL);
+
+		if (status == ERROR_NO_MORE_ITEMS)
+			break;
+		else if (status != ERROR_SUCCESS)
+		{
+			LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap: Error enumerating registry subkeys of key: %s\n", ADAPTER_KEY);
+			return false;
+		}
+
+		snprintf(unit_string, sizeof(unit_string), "%s\\%s", ADAPTER_KEY, enum_name);
+
+		status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, unit_string, 0, KEY_READ, &unit_key);
+
+		if (status != ERROR_SUCCESS)
+		{
+			LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap: Error opening registry key: %s\n", unit_string);
+			return false;
+		} else
+		{
+			len = sizeof(component_id);
+			status = RegQueryValueEx(unit_key, component_id_string, NULL, &data_type, (BYTE *) component_id, &len);
+
+			if (!(status != ERROR_SUCCESS || data_type != REG_SZ))
+			{
+				len = sizeof(net_cfg_instance_id);
+				status = RegQueryValueEx(unit_key,
+										 net_cfg_instance_id_string,
+										 NULL, &data_type, (BYTE *) net_cfg_instance_id, &len);
+
+				if (status == ERROR_SUCCESS && data_type == REG_SZ)
+				{
+					if (!strcmp(net_cfg_instance_id, guid))
+					{
+						RegCloseKey(unit_key);
+						RegCloseKey(netcard_key);
+						return true;
+					}
+				}
+			}
+			RegCloseKey(unit_key);
+		}
+		++i;
+	}
+
+	RegCloseKey(netcard_key);
+	return false;
+}
+
+
+static int get_device_guid(char *name, int name_size, char *actual_name, int actual_name_size)
+{
+	LONG status;
+	HKEY control_net_key;
+	DWORD len;
+	int i = 0;
+	int stop = 0;
+
+	status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, NETWORK_CONNECTIONS_KEY, 0, KEY_READ, &control_net_key);
+
+	if (status != ERROR_SUCCESS)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap: Error opening registry key: %s\n", NETWORK_CONNECTIONS_KEY);
+		return -1;
+	}
+
+	while (!stop)
+	{
+		char enum_name[256];
+		char connection_string[256];
+		HKEY connection_key;
+		char name_data[256];
+		DWORD name_type;
+		const char name_string[] = "Name";
+
+		len = sizeof(enum_name);
+		status = RegEnumKeyEx(control_net_key, i, enum_name, &len, NULL, NULL, NULL, NULL);
+
+		if (status == ERROR_NO_MORE_ITEMS)
+			break;
+		else if (status != ERROR_SUCCESS)
+		{
+			LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap: Error enumerating registry subkeys of key: %s\n", NETWORK_CONNECTIONS_KEY);
+			return -1;
+		}
+
+		snprintf(connection_string,
+				 sizeof(connection_string), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, enum_name);
+
+		status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, connection_string, 0, KEY_READ, &connection_key);
+
+		if (status == ERROR_SUCCESS)
+		{
+			len = sizeof(name_data);
+			status = RegQueryValueEx(connection_key, name_string, NULL, &name_type, (BYTE *) name_data, &len);
+
+			if (status != ERROR_SUCCESS || name_type != REG_SZ)
+			{
+				LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap: Error opening registry key: %s\\%s\\%s",
+						  NETWORK_CONNECTIONS_KEY, connection_string, name_string);
+				return -1;
+			} else
+			{
+				if (is_tap_win32_dev(enum_name))
+				{
+					LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap: found TAP device named \"%s\" ~ \"%s\"\n", name_data, actual_name);
+
+					snprintf(name, name_size, "%s", enum_name);
+					if (actual_name)
+					{
+						if (strcmp(actual_name, "") != 0)
+						{
+							if (strcmp(name_data, actual_name) != 0)
+							{
+								RegCloseKey(connection_key);
+								++i;
+								continue;
+							}
+						} else
+						{
+							snprintf(actual_name, actual_name_size, "%s", name_data);
+						}
+					}
+					stop = 1;
+				}
+			}
+
+			RegCloseKey(connection_key);
+		}
+		++i;
+	}
+
+	RegCloseKey(control_net_key);
+
+	if (stop == 0)
+		return -1;
+
+	return 0;
+}
+
+
+static bool WinTapEthernetHandler_open(struct EthernetHandler *_handler)
+{
+	struct WinTapEthernetHandler *handler = (struct WinTapEthernetHandler *) _handler;
+	char *type = ConfigureParams.Ethernet[handler->base.ethX].type;
+	char device_path[256];
+	char device_guid[256];
+	char name_buffer[256];
+
+	handler->base.close(&handler->base);
+
+	if (strcmp(type, "none") == 0 || strlen(type) == 0)
+	{
+		return false;
+	}
+
+	if (strlen(ConfigureParams.Ethernet[handler->base.ethX].tunnel) == 0)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap(%d): tunnel name undefined\n", handler->base.ethX);
+		return false;
+	}
+
+	strcpy(name_buffer, ConfigureParams.Ethernet[handler->base.ethX].tunnel);
+
+	if (get_device_guid(device_guid, sizeof(device_guid), name_buffer, sizeof(name_buffer)) < 0)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: WinTap(%d): ERROR: Could not find Windows tap device: %s\n", handler->base.ethX, winerror(GetLastError()));
+		return false;
+	}
+
+	/*
+	 * Open Windows TAP-Win32 adapter
+	 */
+	snprintf(device_path, sizeof(device_path), "%s%s%s", USERMODEDEVICEDIR, device_guid, TAPSUFFIX);
+
+	handler->device_handle =
+		CreateFile(device_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
+				   FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
+
+	if (handler->device_handle == INVALID_HANDLE_VALUE)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: WinTap(%d): ERROR: Could not open (%s) Windows tap device: %s\n", handler->base.ethX, device_path,
+				  winerror(GetLastError()));
+		return false;
+	}
+	handler->device = strdup(device_path);
+
+	handler->read_overlapped.Offset = 0;
+	handler->read_overlapped.OffsetHigh = 0;
+	handler->read_overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+	handler->write_overlapped.Offset = 0;
+	handler->write_overlapped.OffsetHigh = 0;
+	handler->write_overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+	LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap(%d): tap device open %s [handle=%p]\n", handler->base.ethX, device_path, (void *) handler->device_handle);
+	return true;
+}
+
+
+static void WinTapEthernetHandler_close(struct EthernetHandler *_handler)
+{
+	struct WinTapEthernetHandler *handler = (struct WinTapEthernetHandler *) _handler;
+
+	if (handler->device_handle != INVALID_HANDLE_VALUE)
+	{
+		CloseHandle(handler->device_handle);
+		handler->device_handle = INVALID_HANDLE_VALUE;
+	}
+	free(handler->device);
+	handler->device = NULL;
+}
+
+
+static int WinTapEthernetHandler_recv(struct EthernetHandler *_handler, uint8_t *buf, int len)
+{
+	struct WinTapEthernetHandler *handler = (struct WinTapEthernetHandler *) _handler;
+	DWORD lenin;
+	BOOL result;
+
+	LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap(%d): Read packet from %s\n", handler->base.ethX, handler->device);
+
+	result = ReadFile(handler->device_handle, buf, len, &lenin, &handler->read_overlapped);
+
+	if (!result)
+	{
+		DWORD error = GetLastError();
+
+		switch (error)
+		{
+		case ERROR_IO_PENDING:
+			WaitForSingleObject(handler->read_overlapped.hEvent, INFINITE);
+			result = GetOverlappedResult(handler->device_handle, &handler->read_overlapped, &lenin, 0);
+			if (result)
+				break;
+			/* fallthrough */
+		default:
+			LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap(%d): Error while reading from %s: %s\n", handler->base.ethX, handler->device,
+					  winerror(GetLastError()));
+			return -1;
+		}
+	}
+
+	handler->device_total_in += lenin;
+	LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap(%d): Read packet done (len %d)\n", handler->base.ethX, (int) lenin);
+	return lenin;
+}
+
+
+static int WinTapEthernetHandler_send(struct EthernetHandler *_handler, const uint8_t *buf, int len)
+{
+	struct WinTapEthernetHandler *handler = (struct WinTapEthernetHandler *) _handler;
+	DWORD lenout;
+	BOOL result;
+
+	LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap(%d): Writing packet of %d bytes to %s\n", handler->base.ethX, len, handler->device);
+
+	result = WriteFile(handler->device_handle, buf, len, &lenout, &handler->write_overlapped);
+	if (!result)
+	{
+		DWORD error = GetLastError();
+
+		switch (error)
+		{
+		case ERROR_IO_PENDING:
+			WaitForSingleObject(handler->write_overlapped.hEvent, INFINITE);
+			break;
+		default:
+			LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap(%d): Error while writing to %s: %s\n", handler->base.ethX, handler->device,
+					  winerror(GetLastError()));
+			return -1;
+		}
+	}
+
+	handler->device_total_out += lenout;
+	LOG_TRACE(TRACE_ETHERNET, "Ethernet: WinTap(%d): Writing packet done\n", handler->base.ethX);
+	return lenout;
+}
+
+
+struct EthernetHandler *WinTapEthernetHandler(int eth_idx)
+{
+	struct WinTapEthernetHandler *handler = (struct WinTapEthernetHandler *) malloc(sizeof(*handler));
+
+	handler->base.ethX = eth_idx;
+	handler->base.packet_length = 0;
+	handler->base.handlingThread = NULL;
+	handler->base.intAck = NULL;
+
+	handler->base.open = WinTapEthernetHandler_open;
+	handler->base.close = WinTapEthernetHandler_close;
+	handler->base.recv = WinTapEthernetHandler_recv;
+	handler->base.send = WinTapEthernetHandler_send;
+
+	handler->device_handle = INVALID_HANDLE_VALUE;
+	handler->device = NULL;
+	handler->iface = NULL;
+
+	handler->device_total_in = 0;
+	handler->device_total_out = 0;
+
+	return &handler->base;
+}
+
+#endif /* WITH_NF_ETHERNET */
diff --git a/src/ethernet/fd_trans.c b/src/ethernet/fd_trans.c
new file mode 100644
index 00000000..354fd602
--- /dev/null
+++ b/src/ethernet/fd_trans.c
@@ -0,0 +1,258 @@
+/*
+ *  fd_trans.c
+ *  Used to transfer a file descriptor between two Unix processes
+ *
+ *  Inspired by Amit Singhs description in the "Mac OS X manual":
+ *  http://topiks.org/mac-os-x/0321278542/ch09lev1sec11.html
+ *
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+/*
+ * only used by ethernet implementation for macOS so far
+ */
+#ifdef __APPLE__
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+
+
+#include "fd_trans.h"
+
+#define SOCKET_PATH "/tmp/fd_trans_socket"
+
+#define MSG_CONTROL_SIZE CMSG_SPACE(sizeof(int))
+
+
+
+static int fd_receive_using_sockfd(int *fd, int sockfd)
+{
+    ssize_t                ret;
+    u_char                 c;
+    int                    errcond = 0;
+    struct iovec           iovec[1];
+    struct msghdr          msg;
+    struct cmsghdr        *cmsghdrp;
+    union {
+        struct cmsghdr cmsghdr;
+        u_char         msg_control[MSG_CONTROL_SIZE];
+    }  cmsghdr_msg_control;
+
+    iovec[0].iov_base = (void*)&c;
+    iovec[0].iov_len = 1;
+
+    msg.msg_name = NULL;
+    msg.msg_namelen = 0;
+    msg.msg_iov = iovec;
+    msg.msg_iovlen = 1;
+    msg.msg_control = (void*)cmsghdr_msg_control.msg_control;
+    msg.msg_controllen = sizeof(cmsghdr_msg_control.msg_control);
+    msg.msg_flags = 0;
+
+    if ((ret = recvmsg(sockfd, &msg, 0)) <= 0)
+	{
+        perror("recvmsg");
+		*fd = -1;
+        return (int)ret;
+    }
+
+    cmsghdrp = CMSG_FIRSTHDR(&msg);
+    if (cmsghdrp == NULL)
+	{
+        *fd = -1;
+        return (int)ret;
+    }
+
+    if (cmsghdrp->cmsg_len != CMSG_LEN(sizeof(int)))
+        errcond++;
+
+    if (cmsghdrp->cmsg_level != SOL_SOCKET)
+        errcond++;
+
+    if (cmsghdrp->cmsg_type != SCM_RIGHTS)
+        errcond++;
+
+    if (errcond)
+	{
+        fprintf(stderr, "receiver: %d errors in received message\n", errcond);
+        *fd = -1;
+    } else
+        *fd = *((int *)CMSG_DATA(cmsghdrp));
+
+    return (int)ret;
+}
+
+
+int fd_setup_server(void)
+{
+    int sockfd;
+	socklen_t len;
+    struct sockaddr_un server_unix_addr;
+	const char *name = SOCKET_PATH;
+
+    if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
+    {
+        perror("socket");
+        return sockfd;
+    }
+
+    unlink(name);
+    bzero((char *)&server_unix_addr, sizeof(server_unix_addr));
+    server_unix_addr.sun_family = AF_LOCAL;
+    strcpy(server_unix_addr.sun_path, name);
+    len = (socklen_t)strlen(name) + 1;
+    len += sizeof(server_unix_addr.sun_family);
+
+    if (bind(sockfd, (struct sockaddr *)&server_unix_addr, len) < 0)
+    {
+        close(sockfd);
+        return -1;
+    }
+
+    return sockfd;
+}
+
+
+static int fd_send_using_sockfd(int fd, int sockfd)
+{
+    ssize_t                ret;
+    struct iovec           iovec[1];
+    struct msghdr          msg;
+    struct cmsghdr        *cmsghdrp;
+    union {
+        struct cmsghdr cmsghdr;
+        u_char         msg_control[MSG_CONTROL_SIZE];
+    } cmsghdr_msg_control;
+	static char iov_base[1] = { 0 };
+
+    iovec[0].iov_base = iov_base;
+    iovec[0].iov_len = 1;
+
+    msg.msg_name = NULL;       /* address (optional) */
+    msg.msg_namelen = 0;       /* size of address */
+    msg.msg_iov = iovec;       /* scatter/gather array */
+    msg.msg_iovlen = 1;        /* members in msg.msg_iov */
+    msg.msg_control = (void*)cmsghdr_msg_control.msg_control; /* ancillary data */
+    /* ancillary data buffer length */
+    msg.msg_controllen = sizeof(cmsghdr_msg_control.msg_control);
+    msg.msg_flags = 0;          /* flags on received message */
+
+    /* CMSG_FIRSTHDR() returns a pointer to the first cmsghdr structure in */
+    /* the ancillary data associated with the given msghdr structure */
+    cmsghdrp = CMSG_FIRSTHDR(&msg);
+
+    cmsghdrp->cmsg_len = CMSG_LEN(sizeof(int)); /* data byte count */
+    cmsghdrp->cmsg_level = SOL_SOCKET;          /* originating protocol */
+    cmsghdrp->cmsg_type = SCM_RIGHTS;           /* protocol-specified type */
+
+    /* CMSG_DATA() returns a pointer to the data array associated with */
+    /* the cmsghdr structure pointed to by cmsghdrp */
+    *((int *)CMSG_DATA(cmsghdrp)) = fd;
+
+    if ((ret = sendmsg(sockfd, &msg, 0)) < 0)
+    {
+        perror("sendmsg");
+        return (int)ret;
+    }
+
+    return 0;
+}
+
+
+void fd_send(int fd)
+{
+    int sockfd, len;
+    struct sockaddr_un server_unix_addr;
+	int i, ret = -1;
+
+    bzero((char *)&server_unix_addr, sizeof(server_unix_addr));
+    strcpy(server_unix_addr.sun_path, SOCKET_PATH);
+    server_unix_addr.sun_family = AF_LOCAL;
+    len = strlen(SOCKET_PATH) + 1;
+    len += sizeof(server_unix_addr.sun_family);
+
+    if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
+    {
+        perror("socket");
+        return;
+    }
+
+	for (i = 1; i < 3 && ret < 0; i++)
+	{
+		ret = connect(sockfd, (struct sockaddr *)&server_unix_addr, len);
+		if (ret < 0)
+		{
+			perror("sender: connect");
+			sleep(1);
+		}
+	}
+	if (ret >= 0)
+	{
+		/* Send file descriptor message */
+		if ((fd_send_using_sockfd(fd, sockfd) < 0))
+			fprintf(stderr, "sender: failed to send file descriptor (fd = %d)\n", fd);
+	} else
+	{
+		perror("sender: connect");
+	}
+	sleep(1);
+
+	close(sockfd);
+}
+
+
+int fd_receive(int sockfd, pid_t from)
+{
+    int fd = -1;
+	int status;
+	fd_set readfds;
+	struct timeval timeout;
+
+	/* Wait until a connection is established */
+	listen(sockfd, 0);
+
+	for (;;)
+	{
+		struct sockaddr_un cl_un;
+		socklen_t cl_un_len = sizeof(cl_un);
+
+		FD_ZERO(&readfds);
+		FD_SET(sockfd, &readfds);
+		timeout.tv_sec = 1;
+		timeout.tv_usec = 0;
+		if (select(sockfd + 1, &readfds, NULL, NULL, &timeout) >= 0 && FD_ISSET(sockfd, &readfds))
+		{
+			int csockfd = accept(sockfd, (struct sockaddr *)&cl_un, &cl_un_len);
+
+			if (csockfd < 0)
+			{
+				perror("accept");
+			}
+			else
+			{
+				int ret = fd_receive_using_sockfd(&fd, csockfd);
+				if (ret < 0)
+					fprintf(stderr, "receiver: failed to receive file descriptor\n");
+				break;
+			}
+		}
+
+		if (waitpid(from, &status, WNOHANG) > 0 && WIFEXITED(status))
+		{
+			fprintf(stderr, "accept: child process died\n");
+			break;
+		}
+	}
+
+	return fd;
+}
+
+#endif /* __APPLE__ */
diff --git a/src/ethernet/fd_trans.h b/src/ethernet/fd_trans.h
new file mode 100644
index 00000000..7253f09b
--- /dev/null
+++ b/src/ethernet/fd_trans.h
@@ -0,0 +1,20 @@
+/*
+ *  fd_trans.h
+ *  Used to transfer a file descriptor between two Unix processes
+ *
+ *  Inspired by Amit Singhs description in the "Mac OS X manual": 
+ *  http://topiks.org/mac-os-x/0321278542/ch09lev1sec11.html
+ *
+ */
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+int fd_receive(int sockfd, pid_t from);
+void fd_send(int fd);
+int fd_setup_server(void);
+
+#ifdef  __cplusplus
+}
+#endif
diff --git a/src/ethernet/nf_ethernet.c b/src/ethernet/nf_ethernet.c
new file mode 100644
index 00000000..1b3be922
--- /dev/null
+++ b/src/ethernet/nf_ethernet.c
@@ -0,0 +1,606 @@
+/*
+ * nf_ethernet.c - Ethernet Card Emulation
+ *
+ * Copyright (c) 2007 ARAnyM dev team (see AUTHORS)
+ *
+ * This file is part of the ARAnyM project which builds a new and powerful
+ * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
+ *
+ * ARAnyM 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.
+ *
+ * ARAnyM 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 ARAnyM. If not see <http://www.gnu.org/licenses/>
+ */
+
+#include "config.h"
+#include <SDL.h>
+#include <SDL_thread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "stMemory.h"
+#include "log.h"
+#include "gemdos_defines.h"
+#include "m68000.h"
+#include "configuration.h"
+
+#include "nf_ethernet.h"
+#include "ethernet_nfapi.h"
+#include "ethernet.h"
+
+/****************************
+ * Configuration zone begins
+ */
+
+
+/* Ethernet runs at interrupt level 3 by default but can be reconfigured */
+#if 1
+#define INTLEVEL	3
+#define TRIGGER_INTERRUPT	M68000_Exception(24 + INTLEVEL, M68000_EXC_SRC_AUTOVEC)
+#else
+#define INTLEVEL	5
+#define TRIGGER_INTERRUPT	M68000_Exception(24 + INTLEVEL, M68000_EXC_SRC_AUTOVEC)
+#endif
+
+/*
+ * Configuration zone ends
+ **************************/
+
+typedef enum { HOST_IP, ATARI_IP, NETMASK } GET_PAR;
+
+static volatile int pending_interrupts;
+
+static struct EthernetHandler *handlers[MAX_ETH];
+
+
+static uint32_t read_stack_long(uint32_t * stack)
+{
+	uint32_t value = STMemory_ReadLong(*stack);
+
+	*stack += SIZE_LONG;
+
+	return value;
+}
+
+
+static void *read_stack_pointer(uint32_t * stack)
+{
+	uint32_t ptr = read_stack_long(stack);
+
+	return ptr ? STMemory_STAddrToPointer(ptr) : 0;
+}
+
+
+static struct EthernetHandler *getHandler(int ethX)
+{
+	if (ethX >= 0 && ethX < MAX_ETH)
+	{
+		struct EthernetHandler *h = handlers[ethX];
+
+		if (h != NULL)
+		{
+			assert(h->ethX == ethX);
+			return h;
+		}
+	}
+
+	return NULL;
+}
+
+
+static int get_params(GET_PAR which, uint32_t stack)
+{
+	int ethX;
+	uint32_t st_name_ptr;
+	char *name_ptr;
+	uint32_t name_maxlen;
+	const char *text = NULL;
+
+	ethX = read_stack_long(&stack);
+	st_name_ptr = STMemory_ReadLong(stack);
+	name_ptr = read_stack_pointer(&stack);
+	name_maxlen = read_stack_long(&stack);
+
+	LOG_TRACE(TRACE_ETHERNET, "Ethernet: getPAR(%d) for eth%d to buffer at %x of size %d\n", which, ethX, st_name_ptr, name_maxlen);
+
+	if (ethX < 0 || ethX >= MAX_ETH)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: handler for %d not found\n", ethX);
+		return 0;
+	}
+	if (!STMemory_CheckAreaType(st_name_ptr, name_maxlen, ABFLAG_RAM))
+	{
+		Log_Printf(LOG_WARN, "ethernet: Invalid RAM range 0x%x+%i\n", st_name_ptr, name_maxlen);
+		return 0;
+	}
+
+	switch (which)
+	{
+	case HOST_IP:
+		text = ConfigureParams.Ethernet[ethX].ip_host;
+		break;
+	case ATARI_IP:
+		text = ConfigureParams.Ethernet[ethX].ip_atari;
+		break;
+	case NETMASK:
+		text = ConfigureParams.Ethernet[ethX].netmask;
+		break;
+	default:
+		text = "";
+		break;
+	}
+
+	strncpy(name_ptr, text, name_maxlen);
+	return strlen(text);
+}
+
+
+static int32_t readPacketLength(int ethX)
+{
+	struct EthernetHandler *handler = getHandler(ethX);
+
+	if (handler == NULL)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: handler for %d not found\n", ethX);
+		return 0;
+	}
+	return handler->packet_length;
+}
+
+
+/*
+ *  ETHERNETDriver ReadPacket routine
+ */
+
+static void readPacket(int ethX, unsigned char *buffer, uint32_t len)
+{
+	struct EthernetHandler *handler = getHandler(ethX);
+
+	if (handler == NULL)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: handler for %d not found\n", ethX);
+		return;
+	}
+	if (len > MAX_PACKET_SIZE)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: readPacket() - length %d > %d\n", len, MAX_PACKET_SIZE);
+		len = MAX_PACKET_SIZE;
+	}
+	memcpy(buffer, handler->packet, len);
+}
+
+
+/*
+ *  ETHERNETDriver writePacket routine
+ */
+
+static void sendPacket(int ethX, const unsigned char *buffer, uint32_t len)
+{
+	struct EthernetHandler *handler = getHandler(ethX);
+	uint8_t packetToWrite[MAX_PACKET_SIZE + 2];
+
+	if (handler == NULL)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: handler for %d not found\n", ethX);
+		return;
+	}
+
+	if (len > MAX_PACKET_SIZE)
+		len = MAX_PACKET_SIZE;
+	memcpy(packetToWrite, buffer, len);
+
+	/* Transmit packet */
+	if (handler->send(handler, packetToWrite, len) < 0)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: WARNING: Couldn't transmit packet\n");
+	}
+}
+
+
+/*
+ *  Packet reception thread
+ */
+static int receiveFunc(void *arg)
+{
+	struct EthernetHandler *handler = (struct EthernetHandler *) arg;
+
+	/* Call protocol handler for received packets */
+	for (;;)
+	{
+		/* Read packet device */
+		handler->packet_length = handler->recv(handler, handler->packet, MAX_PACKET_SIZE);
+
+		/* Trigger ETHERNETDriver interrupt (call the m68k side) */
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet:%d: packet received (len %d), triggering ETHERNETDriver interrupt\n", handler->ethX, (int) handler->packet_length);
+
+		pending_interrupts |= (1 << handler->ethX);
+		TRIGGER_INTERRUPT;
+
+		/* Wait for interrupt acknowledge (m68k network driver read interrupt to finish) */
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet:%d: waiting for int acknowledge with pending irq mask %02x\n", handler->ethX, pending_interrupts);
+		SDL_SemWait(handler->intAck);
+		pending_interrupts &= ~(1 << handler->ethX);
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet:%d: int acknowledged, pending irq mask now %02x\n", handler->ethX, pending_interrupts);
+	}
+
+	return 0;
+}
+
+
+/*
+ *  Stop packet reception thread
+ */
+static void stopThread(struct EthernetHandler *handler)
+{
+	if (handler && handler->handlingThread)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet:%d: Stop thread\n", handler->ethX);
+
+#ifdef FIXME
+		/* pthread_cancel(handlingThread); // FIXME: set the cancel flag. */
+		SDL_WaitThread(handler->handlingThread, NULL);
+		SDL_DestroySemaphore(handler->intAck);
+#endif
+		handler->handlingThread = NULL;
+	}
+}
+
+
+/*
+ *  Start packet reception thread
+ */
+static bool startThread(struct EthernetHandler *handler)
+{
+	if (handler->handlingThread == NULL)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet:%d: Start thread\n", handler->ethX);
+
+		if ((handler->intAck = SDL_CreateSemaphore(0)) == NULL)
+		{
+			Log_Printf(LOG_WARN, "Ethernet:%d: WARNING: Cannot init semaphore\n", handler->ethX);
+			return false;
+		}
+
+		handler->handlingThread = SDL_CreateThread(receiveFunc, "Ethernet", handler);
+		if (handler->handlingThread == NULL)
+		{
+			Log_Printf(LOG_WARN, "Ethernet:%d: WARNING: Cannot start ETHERNETDriver thread\n", handler->ethX);
+			return false;
+		}
+	}
+	return true;
+}
+
+
+/*
+ * Dispatcher for ETHERNET native features.
+ * Note that in ARAnyM, all of the functions below
+ * are only allowed to called from SuperVisor mode.
+ * This works because they are only issued from inside the kernel,
+ * through ioctl() on the device descriptor.
+ * In Hatari, to simplify things a bit, we allow a few query options
+ * to also be called from User mode.
+ */
+bool nf_ethernet(uint32_t stack, uint32_t subid, uint32_t *retval)
+{
+	int ethX;
+	struct EthernetHandler *handler;
+	bool super = ((M68000_GetSR() & SR_SUPERMODE) == SR_SUPERMODE);
+
+	*retval = 0;
+	switch (subid)
+	{
+	case GET_VERSION:
+		*retval = ARAETHER_NFAPI_VERSION;
+		break;
+
+	case XIF_INTLEVEL:					/* what interrupt level is used? */
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: getINTlevel\n");
+		*retval = INTLEVEL;
+		break;
+
+	case XIF_GET_MAC:					/* what is the MAC address? */
+		/* store MAC address to provided buffer */
+		{
+			uint32_t st_buf_ptr;
+			unsigned char *buf_ptr;
+			uint32_t buf_size;
+			char *ms;
+			bool format_OK;
+
+			/* default MAC Address is just made up */
+			uint8_t mac_addr[6] = { '\0', 'A', 'E', 'T', 'H', 0 };
+
+			ethX = read_stack_long(&stack);
+
+			handler = getHandler(ethX);
+
+			if (handler == NULL)
+			{
+				*retval = 0;			/* return FALSE if ethX not defined */
+				return true;
+			}
+
+			st_buf_ptr = STMemory_ReadLong(stack);
+			buf_ptr = read_stack_pointer(&stack);	/* destination buffer */
+			buf_size = read_stack_long(&stack);	/* buffer size */
+			LOG_TRACE(TRACE_ETHERNET, "Ethernet: getMAC(%d, %x, %d)\n", ethX, st_buf_ptr, (int) buf_size);
+
+			/* default MAC Address is just made up */
+			mac_addr[5] = '0' + ethX;
+
+			/* convert user-defined MAC Address from string to 6 bytes array */
+			ms = ConfigureParams.Ethernet[ethX].mac_addr;
+			format_OK = false;
+			if (strlen(ms) == 2 * 6 + 5 && (ms[2] == ':' || ms[2] == '-'))
+			{
+				int md[6] = { 0, 0, 0, 0, 0, 0 };
+				int matched;
+				int i;
+
+				ms[2] = ms[5] = ms[8] = ms[11] = ms[14] = ':';
+				matched = sscanf(ms, "%02x:%02x:%02x:%02x:%02x:%02x", &md[0], &md[1], &md[2], &md[3], &md[4], &md[5]);
+				if (matched == 6)
+				{
+					for (i = 0; i < 6; i++)
+						mac_addr[i] = md[i];
+					format_OK = true;
+				}
+			}
+			if (!format_OK)
+			{
+				LOG_TRACE(LOG_WARN, "Ethernet: MAC Address of [ETH%d] in incorrect format\n", ethX);
+			}
+			if (buf_size > sizeof(mac_addr))
+				buf_size = sizeof(mac_addr);
+			if (!STMemory_CheckAreaType(st_buf_ptr, buf_size, ABFLAG_RAM))
+			{
+				Log_Printf(LOG_WARN, "ethernet: Invalid RAM range 0x%x+%i\n", st_buf_ptr, buf_size);
+				*retval = 0;
+				M68000_BusError(st_buf_ptr, BUS_ERROR_WRITE, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA, 0);
+				return false;
+			} else
+			{
+				memcpy(buf_ptr, mac_addr, buf_size);
+				*retval = 1;			/* TRUE */
+			}
+		}
+		break;
+
+	case XIF_IRQ:						/* interrupt raised by native side thread polling tap0 interface */
+		if (!super)
+		{
+			LOG_TRACE(TRACE_NATFEATS, "Ethernet: ERROR: NF function called without supervisor mode\n");
+			M68000_Exception(8, M68000_EXC_SRC_CPU);
+			return false;
+		}
+		{
+			int dev_bit = read_stack_long(&stack);
+
+			if (dev_bit == 0)
+			{
+				/* dev_bit = 0 means "tell me what devices want me to serve their interrupts" */
+				*retval = pending_interrupts;
+			} else
+			{
+				/* otherwise the set bit means "I'm acknowledging this device's interrupt" */
+				ethX = -1;
+
+				switch (dev_bit)
+				{
+				case 0x01:
+					ethX = 0;
+					break;
+				case 0x02:
+					ethX = 1;
+					break;
+				case 0x04:
+					ethX = 2;
+					break;
+				case 0x08:
+					ethX = 3;
+					break;
+				case 0x10:
+					ethX = 4;
+					break;
+				case 0x20:
+					ethX = 5;
+					break;
+				case 0x40:
+					ethX = 6;
+					break;
+				case 0x80:
+					ethX = 7;
+					break;
+				default:
+					LOG_TRACE(LOG_WARN, "Ethernet: wrong XIF_IRQ(%d)\n", dev_bit);
+					break;
+				}
+
+				handler = getHandler(ethX);
+
+				if (handler == NULL)
+				{
+					LOG_TRACE(LOG_WARN, "Ethernet: handler for %d not found\n", ethX);
+				} else
+				{
+					LOG_TRACE(TRACE_ETHERNET, "Ethernet: ETH%d IRQ acknowledged\n", ethX);
+					/* Acknowledge interrupt to reception thread */
+					SDL_SemPost(handler->intAck);
+				}
+			}
+		}
+		break;
+
+	case XIF_START:
+		if (!super)
+		{
+			LOG_TRACE(TRACE_NATFEATS, "Ethernet: ERROR: NF function called without supervisor mode\n");
+			M68000_Exception(8, M68000_EXC_SRC_CPU);
+			return false;
+		}
+		ethX = read_stack_long(&stack);
+		handler = getHandler(ethX);
+		if (handler == NULL || startThread(handler) == false)
+		{
+			*retval = GEMDOS_EUNDEV;
+		}
+		break;
+
+	case XIF_STOP:
+		if (!super)
+		{
+			LOG_TRACE(TRACE_NATFEATS, "Ethernet: ERROR: NF function called without supervisor mode\n");
+			M68000_Exception(8, M68000_EXC_SRC_CPU);
+			return false;
+		}
+		ethX = read_stack_long(&stack);
+		stopThread(getHandler(ethX));
+		break;
+
+	case XIF_READLENGTH:
+		if (!super)
+		{
+			LOG_TRACE(TRACE_NATFEATS, "Ethernet: ERROR: NF function called without supervisor mode\n");
+			M68000_Exception(8, M68000_EXC_SRC_CPU);
+			return false;
+		}
+		ethX = read_stack_long(&stack);
+		*retval = readPacketLength(ethX);
+		break;
+
+	case XIF_READBLOCK:
+		if (!super)
+		{
+			LOG_TRACE(TRACE_NATFEATS, "Ethernet: ERROR: NF function called without supervisor mode\n");
+			M68000_Exception(8, M68000_EXC_SRC_CPU);
+			return false;
+		}
+		{
+			uint32_t st_buf_ptr;
+			unsigned char *buf_ptr;
+			uint32_t buf_size;
+
+			ethX = read_stack_long(&stack);
+			st_buf_ptr = STMemory_ReadLong(stack);
+			buf_ptr = read_stack_pointer(&stack);	/* destination buffer */
+			buf_size = read_stack_long(&stack);	/* buffer size */
+			if (!STMemory_CheckAreaType(st_buf_ptr, buf_size, ABFLAG_RAM))
+			{
+				Log_Printf(LOG_WARN, "ethernet: Invalid RAM range 0x%x+%i\n", st_buf_ptr, buf_size);
+				M68000_BusError(st_buf_ptr, BUS_ERROR_WRITE, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA, 0);
+				return false;
+			}
+			LOG_TRACE(TRACE_ETHERNET, "Ethernet: ReadPacket dest %08x, len %x\n", st_buf_ptr, buf_size);
+			readPacket(ethX, buf_ptr, buf_size);
+		}
+		break;
+
+	case XIF_WRITEBLOCK:
+		if (!super)
+		{
+			LOG_TRACE(TRACE_NATFEATS, "Ethernet: ERROR: NF function called without supervisor mode\n");
+			M68000_Exception(8, M68000_EXC_SRC_CPU);
+			return false;
+		}
+		{
+			uint32_t st_buf_ptr;
+			unsigned char *buf_ptr;
+			uint32_t buf_size;
+
+			ethX = read_stack_long(&stack);
+			st_buf_ptr = STMemory_ReadLong(stack);
+			buf_ptr = read_stack_pointer(&stack);	/* destination buffer */
+			buf_size = read_stack_long(&stack);	/* buffer size */
+			if (!STMemory_CheckAreaType(st_buf_ptr, buf_size, ABFLAG_RAM | ABFLAG_ROM))
+			{
+				Log_Printf(LOG_WARN, "ethernet: Invalid RAM range 0x%x+%i\n", st_buf_ptr, buf_size);
+				M68000_BusError(st_buf_ptr, BUS_ERROR_WRITE, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA, 0);
+				return false;
+			}
+			LOG_TRACE(TRACE_ETHERNET, "Ethernet: SendPacket src %08x, len %x\n", st_buf_ptr, buf_size);
+			sendPacket(ethX, buf_ptr, buf_size);
+		}
+		break;
+
+	case XIF_GET_IPHOST:
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: XIF_GET_IPHOST\n");
+		*retval = get_params(HOST_IP, stack);
+		break;
+
+	case XIF_GET_IPATARI:
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: XIF_GET_IPATARI\n");
+		*retval = get_params(ATARI_IP, stack);
+		break;
+
+	case XIF_GET_NETMASK:
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: XIF_GET_NETMASK\n");
+		*retval = get_params(NETMASK, stack);
+		break;
+
+	default:
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: unsupported function %d\n", subid);
+		*retval = GEMDOS_EINVFN;
+		break;
+	}
+
+	return true;
+}
+
+
+/*
+ *  Initialization
+ */
+void nf_ethernet_init(void)
+{
+	struct EthernetHandler *handler;
+	int i;
+
+	LOG_TRACE(TRACE_ETHERNET, "Ethernet: init\n");
+
+	pending_interrupts = 0;
+	for (i = 0; i < MAX_ETH; i++)
+	{
+		handler = ETHERNET_HANDLER_CLASSNAME(i);
+		if (!handler->open(handler))
+		{
+			free(handler);
+			handler = NULL;
+		}
+		handlers[i] = handler;
+	}
+}
+
+
+/*
+ *  Deinitialization
+ */
+void nf_ethernet_reset(void)
+{
+	int i;
+
+	LOG_TRACE(TRACE_ETHERNET, "Ethernet: reset\n");
+
+	for (i = 0; i < MAX_ETH; i++)
+	{
+		/* Stop reception thread */
+		struct EthernetHandler *handler = handlers[i];
+
+		if (handler)
+		{
+			stopThread(handler);
+			handler->close(handler);
+			free(handler);
+			handlers[i] = NULL;
+		}
+	}
+	pending_interrupts = 0;
+}
diff --git a/src/includes/configuration.h b/src/includes/configuration.h
index a400e2c8..4a47e340 100644
--- a/src/includes/configuration.h
+++ b/src/includes/configuration.h
@@ -331,6 +331,17 @@ typedef struct
   char sMidiOutPortName[FILENAME_MAX];
 } CNF_MIDI;
 
+/* ethernet support */
+#define MAX_ETH		4
+typedef struct {
+  char type[32];
+  char tunnel[16];
+  char ip_host[16];
+  char ip_atari[16];
+  char netmask[16];
+  char mac_addr[18];
+} CNF_ETHERNET;
+
 
 /* Dialog System */
 typedef enum
@@ -427,6 +438,7 @@ typedef struct
   CNF_MIDI Midi;
   CNF_SYSTEM System;
   CNF_VIDEO Video;
+  CNF_ETHERNET Ethernet[MAX_ETH];
 } CNF_PARAMS;
 
 
diff --git a/src/includes/nf_ethernet.h b/src/includes/nf_ethernet.h
new file mode 100644
index 00000000..c948b897
--- /dev/null
+++ b/src/includes/nf_ethernet.h
@@ -0,0 +1,36 @@
+/*
+ * nf_ethernet.h - Ethernet Card Emulation
+ *
+ * Copyright (c) 2007 ARAnyM dev team (see AUTHORS)
+ *
+ * This file is part of the ARAnyM project which builds a new and powerful
+ * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
+ *
+ * ARAnyM 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.
+ *
+ * ARAnyM 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 ARAnyM. If not see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef HATARI_ETHERNET_H
+#define HATARI_ETHERNET_H
+
+#if defined(__linux__) || defined(__CYGWIN32__) || defined(__WIN32__) || defined(__APPLE__)
+
+#define WITH_NF_ETHERNET 1
+
+bool nf_ethernet(Uint32 stack, Uint32 subid, Uint32 *retval);
+void nf_ethernet_init(void);
+void nf_ethernet_reset(void);
+
+#endif
+
+#endif /* HATARI_ETHERNET_H */
diff --git a/src/reset.c b/src/reset.c
index 3f668be5..9b548069 100644
--- a/src/reset.c
+++ b/src/reset.c
@@ -42,6 +42,7 @@ const char Reset_fileid[] = "Hatari reset.c";
 #include "debugcpu.h"
 #include "debugdsp.h"
 #include "nf_scsidrv.h"
+#include "nf_ethernet.h"
 
 /*-----------------------------------------------------------------------*/
 /**
@@ -116,7 +117,12 @@ static int Reset_ST(bool bCold)
 	Midi_Reset();
 
 #if defined(__linux__)
-        nf_scsidrv_reset();
+	nf_scsidrv_reset();
+#endif
+
+#ifdef WITH_NF_ETHERNET
+	nf_ethernet_reset();
+	nf_ethernet_init();
 #endif
 
 	/* Start HBL, Timer B and VBL interrupts with a 0 cycle delay */


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/