[hatari-devel] Network support for Hatari

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


Hi,

 

the attached patch implements the ETHERNET native feature as it is implemented in ARAnyM, which should make it possible to use network functions when runnning mint (it uses the same nfeth.xif, no changes are needed there).

 

To test this, i've also made an hardisk image with some rudimentry tools that boots straight into Mint: http://tho-otto.de/download/hatari-mint.zip

 

The archive also includes the config file. This may need some changes regarding the ip addresses i'm using:

 

- 192.168.1.1 for the router

- 192.168.1.2 for the host

- 192.168.1.7 for the Hatari instance.

 

Unfortunately similar changes will be needed also in some files on the harddisk image:

 

c:/mint/network.cnf

c:/etc/resolv.conf

 

and in the script i use to bring up the bridge (attached below)

 

Some other notes:

 

- the harddisk image contains a freshly compiled mint kernel, with changes that are already pushed to the repo, but (due to bintray closing) are not yet downloadable as snapshots.

 

- In aranym, 2 small helper tools are used for certain configurations (aratapif and bpf_helper), which are not part of the patch. However that are independent programs, and i don't expect much problems to integrate them, but i'm just not very used to cmake to do that cleanly.

 

- ARAnyM uses AutoVector Interrupt #3 for the reception thread, and calls TriggerInt3() for this purpose. In Hatari, i translated that to a call to M68000_Exception(), i hope this is the right thing to do (but seems to work, as seen in https://www.atari-forum.com/viewtopic.php?p=416923#p416923 )

 

- no attempt is currently made to save/restore the state from snapshots, since i have no idea how to do that since open file descriptos are involved

 

- There seems to be a leak on the thread, which is not killed when resetting the emulator (this is also present in aranym). Looks like there is no SDL_KillThread() function in SDL2 anymore?

 

- I have only tested it on linux so far, but verified that the sources for win32/macos compile at least

 

- i also tried to avoid some of the c++isms (like declaration-after-statement), but it is possible that i missed some (activating the warning did not work, because that spits out also lots of warnings from header files).

 

- the ethernet_darwin.c is an older implementation, which imho isn't used anymore (ARAnyM also unconditionally defines ENABLE_BPF for macOS, which causes the newer implementation to be used).

 

 

 

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 90d2830f..7daace1d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -9,7 +9,8 @@ set(SOURCES
 	ncr5380.c paths.c  psg.c printer.c resolution.c rs232.c reset.c rtc.c
 	scandir.c scc.c stMemory.c screen.c screenConvert.c screenSnapShot.c
 	shortcut.c sound.c spec512.c statusbar.c str.c tos.c utils.c
-	vdi.c vme.c inffile.c video.c wavFormat.c xbios.c ymFormat.c lilo.c)
+	vdi.c vme.c inffile.c video.c wavFormat.c xbios.c ymFormat.c lilo.c
+	nf_ethernet.c ethernet_linux.c ethernet_darwin.c ethernet_macosx.c ethernet_win32.c fd_trans.c)
 
 # Disk image code is shared with the hmsa tool, so we put it into a library:
 add_library(Floppy createBlankImage.c dim.c msa.c st.c zip.c)
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_darwin.c b/src/ethernet_darwin.c
new file mode 100644
index 00000000..d6bbe61e
--- /dev/null
+++ b/src/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, "TunTap(%d): tapOpenOld %s", 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, "TunTap(%d): tapOpenOld %s", 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, "TunTap(%d): Bridge mode currently not supported '%s'", 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, "TunTap(%d): tunnel name undefined", handler->base.ethX);
+		if (handler->base.ethX == MAX_ETH - 1)
+			closeAuthorizationContext();
+		return false;
+	}
+
+	LOG_TRACE(TRACE_ETHERNET, "TunTap(%d): open('%s')", handler->base.ethX, devName);
+
+	handler->fd = tapOpen(handler, devName);
+	if (handler->fd < 0)
+	{
+		LOG_TRACE(LOG_WARN, "TunTap(%d): NO_NET_DRIVER_WARN '%s': %s", 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, "TunTap(%d): Authorization failed'%s'", 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, "TunTap(%d): ERROR: " TAP_INIT " failed (code %d). Ethernet disabled!", handler->base.ethX, result);
+		} else
+		{
+			failed = false;
+			LOG_TRACE(TRACE_ETHERNET, "TunTap(%d): " TAP_INIT " initialized OK", 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, "TunTap(%d): close", 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, "TunTap(%d): WARNING: Couldn't transmit packet", 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_linux.c b/src/ethernet_linux.c
new file mode 100644
index 00000000..a47d316e
--- /dev/null
+++ b/src/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, "TunTap(%d): tapOpenOld %s", 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, "TunTap(%d): tapOpenOld %s", 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, "TunTap(%d): Error opening /dev/net/tun. Check if module is loaded and privileges are OK", 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, "TunTap(%d): if opened %s", 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, "TunTap(%d): tunnel name undefined", handler->base.ethX);
+		return false;
+	}
+
+	LOG_TRACE(TRACE_ETHERNET, "TunTap(%d): open('%s')", handler->base.ethX, devName);
+
+	handler->fd = tapOpen(handler, devName);
+	if (handler->fd < 0)
+	{
+		LOG_TRACE(LOG_WARN, "TunTap(%d): NO_NET_DRIVER_WARN '%s': %s", 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, "TunTap(%d): ERROR: fork() failed. Ethernet disabled!", 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, "TunTap(%d): waiting for " TAP_INIT " at pid %d", handler->base.ethX, pid);
+
+	waitpid(pid, &status, 0);
+	failed = true;
+
+	if (WIFEXITED(status))
+	{
+		int err = WEXITSTATUS(status);
+
+		if (err == 255)
+		{
+			LOG_TRACE(LOG_WARN, "TunTap(%d): ERROR: " TAP_INIT " not found. Ethernet disabled!", handler->base.ethX);
+		} else if (err != 0)
+		{
+			LOG_TRACE(LOG_WARN, "TunTap(%d): ERROR: " TAP_INIT " failed (code %d). Ethernet disabled!", handler->base.ethX, err);
+		} else
+		{
+			failed = false;
+			LOG_TRACE(TRACE_ETHERNET, "TunTap(%d): " TAP_INIT " initialized OK", handler->base.ethX);
+		}
+	} else
+	{
+		LOG_TRACE(LOG_WARN, "TunTap(%d): ERROR: " TAP_INIT " could not be started. Ethernet disabled!", 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, "TunTap(%d): close", 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, "TunTap(%d): WARNING: Couldn't transmit packet", 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_macosx.c b/src/ethernet_macosx.c
new file mode 100644
index 00000000..f6261388
--- /dev/null
+++ b/src/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)", 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)
+	{
+		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)", 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)
+	{
+		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)", 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:", 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, "BPF(%d): debug mode=%d", handler->base.ethX, handler->debug);
+	}
+
+	if (handler->debug)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "BPF(%d): open() type=%s", handler->base.ethX, type);
+	}
+	if (strstr(type, "bridge") == NULL)
+	{
+		LOG_TRACE(LOG_WARN, "BPF(%d): unsupported type '%s'", 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, "BPF(%d): starting helper from <%s>", handler->base.ethX, exe_path);
+	}
+
+	pid = fork();
+
+	if (pid < 0)
+	{
+		LOG_TRACE(LOG_WARN, "BPF(%d): ERROR: fork() failed. Ethernet disabled!", handler->base.ethX);
+		close(sockfd);
+		return false;
+	} else if (pid == 0)
+	{
+		int result;
+
+		if (handler->debug)
+		{
+			LOG_TRACE(TRACE_ETHERNET, "BPF(%d): " ETH_HELPER " child running", handler->base.ethX);
+		}
+		result = execl(exe_path, exe_path, NULL);
+
+		_exit(result);
+	}
+
+	if (handler->debug)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "BPF(%d): waiting for " ETH_HELPER " (PID %d) to send file descriptor",
+				  handler->base.ethX, pid);
+	}
+	handler->fd = fd_receive(sockfd, pid);
+	close(sockfd);
+	if (handler->fd < 0)
+	{
+		LOG_TRACE(LOG_WARN, "BPF(%d): failed receiving file descriptor from " ETH_HELPER ".", handler->base.ethX);
+		return false;
+	}
+	if (handler->debug)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "BPF(%d): got file descriptor %d", 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, "BPF(%d): connecting with device %s", handler->base.ethX, dev_name);
+	}
+	if (ioctl(handler->fd, BIOCSETIF, &ifr) > 0)
+	{
+		LOG_TRACE(LOG_WARN, "BPF(%d): Failed associating to %s: %s", 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, "BPF(%d): enabling immediate mode", handler->base.ethX);
+	}
+	if (ioctl(handler->fd, BIOCIMMEDIATE, &immediate) == -1)
+	{
+		LOG_TRACE(LOG_WARN, "BPF(%d): Unable to set immediate mode: %s", 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, "BPF(%d): Unable to get buffer length: %s", handler->base.ethX, strerror(errno));
+		handler->base.close(&handler->base);
+		return false;
+	}
+	if (handler->debug)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "BPF(%d): buf_len=%d", 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, "BPF(%d): enabling promiscuous mode", handler->base.ethX);
+	}
+	promiscuous = 1;
+	if (ioctl(handler->fd, BIOCPROMISC, &promiscuous) == -1)
+	{
+		LOG_TRACE(LOG_WARN, "BPF(%d): Unable to set promiscuous mode: %s", 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, "BPF(%d): enabling header complete mode", handler->base.ethX);
+	}
+	complete = 1;
+	if (ioctl(handler->fd, BIOCGHDRCMPLT, &complete) == -1)
+	{
+		LOG_TRACE(LOG_WARN, "BPF(%d): Unable to set header complete mode: %s", handler->base.ethX, strerror(errno));
+		handler->base.close(&handler->base);
+		return false;
+	}
+
+	/* disable "see sent" mode */
+	if (handler->debug)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "BPF(%d): disabling see sent mode", handler->base.ethX);
+	}
+	seesent = 1;
+	if (ioctl(handler->fd, BIOCSSEESENT, &seesent) == -1)
+	{
+		LOG_TRACE(LOG_WARN, "BPF(%d): Unable to disable see sent mode: %s", 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, "BPF(%d): Invalid MAC address: %s", 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, "BPF(%d): Invalid IP address specified: %s", 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, "BPF(%d): setting filter program for MAC address %s", 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, "BPF(%d): Unable to load filter program: %s", handler->base.ethX, strerror(errno));
+			handler->base.close(&handler->base);
+			return false;
+		}
+		if (handler->debug)
+		{
+			LOG_TRACE(TRACE_ETHERNET, "BPF(%d): filter program load", handler->base.ethX);
+		}
+	} else
+	{
+		if (handler->debug)
+		{
+			LOG_TRACE(TRACE_ETHERNET, "BPF(%d): filter program skipped", 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, "BPF(%d): close", 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, "BPF(%d): recv(len=%d)", 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, "BPF(%d): %d bytes read => %d data bytes", 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, "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, "BPF(%d): using cached %d data bytes from previous read", 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, "BPF(%d): frame length %d bytes", handler->base.ethX, frame_len);
+				dump_bpf_buf("recv", handler->bpf_packet);
+			}
+			memcpy(buf, frame_start, frame_len);
+		} else
+		{
+
+			LOG_TRACE(TRACE_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!", handler->base.ethX, frame_len, len, handler->read_len);
+			LOG_TRACE(TRACE_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, "BPF(%d): send(len=%d)", handler->base.ethX, len);
+	}
+
+	if (len > 0)
+	{
+		if (handler->debug)
+		{
+			LOG_TRACE(TRACE_ETHERNET, "BPF(%d): send(len=%d)", 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, "BPF(%d): WARNING: Couldn't transmit packet", 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_win32.c b/src/ethernet_win32.c
new file mode 100644
index 00000000..62b1d2d9
--- /dev/null
+++ b/src/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, "WinTap: Error opening registry key: %s", 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, "WinTap: Error enumerating registry subkeys of key: %s", 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, "WinTap: Error opening registry key: %s", 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, "WinTap: Error opening registry key: %s", 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, "WinTap: Error enumerating registry subkeys of key: %s", 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, "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, "WinTap: found TAP device named \"%s\" ~ \"%s\"", 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, "WinTap(%d): tunnel name undefined", 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, "WinTap: ERROR: Could not find Windows tap device: %s", 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, "WinTap: ERROR: Could not open (%s) Windows tap device: %s", 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, "WinTap: tap device open %s [handle=%p]", 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, "WinTap: Read packet from %s", 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, "WinTap: Error while reading from %s: %s", handler->device,
+					  winerror(GetLastError()));
+			return -1;
+		}
+	}
+
+	handler->device_total_in += lenin;
+	LOG_TRACE(TRACE_ETHERNET, "WinTap: Read packet done (len %d)", (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, "WinTap: Writing packet of %d bytes to %s", 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, "WinTap: Error while writing to %s: %s", handler->device,
+					  winerror(GetLastError()));
+			return -1;
+		}
+	}
+
+	handler->device_total_out += lenout;
+	LOG_TRACE(TRACE_ETHERNET, "WinTap: Writing packet done");
+	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/fd_trans.c b/src/fd_trans.c
new file mode 100644
index 00000000..354fd602
--- /dev/null
+++ b/src/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/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/ethernet.h b/src/includes/ethernet.h
new file mode 100644
index 00000000..a37a0f4a
--- /dev/null
+++ b/src/includes/ethernet.h
@@ -0,0 +1,48 @@
+/*
+ * 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__)
+#  include "ethernet_win32.h"
+#elif defined(__APPLE__)
+#  define ENABLE_BPF 1
+#  include "ethernet_darwin.h"
+#  include "ethernet_macosx.h"
+#elif defined(__linux__)
+#  include "ethernet_linux.h"
+#endif
diff --git a/src/includes/ethernet_darwin.h b/src/includes/ethernet_darwin.h
new file mode 100644
index 00000000..4fc1e5d6
--- /dev/null
+++ b/src/includes/ethernet_darwin.h
@@ -0,0 +1,34 @@
+/*
+ * ethernet_darwin.h - 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/>
+ *
+ */
+
+#ifndef _ETHERNET_DARWIN_H
+#define _ETHERNET_DARWIN_H
+
+struct EthernetHandler *TunTapEthernetHandler(int eth_idx);
+
+#define ETHERNET_HANDLER_CLASSNAME TunTapEthernetHandler
+
+#endif /* _ETHERNET_DARWIN_H */
+
diff --git a/src/includes/ethernet_linux.h b/src/includes/ethernet_linux.h
new file mode 100644
index 00000000..16e56730
--- /dev/null
+++ b/src/includes/ethernet_linux.h
@@ -0,0 +1,32 @@
+/*
+ * ethernet_linux.h - 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/>
+ *
+ */
+
+#ifndef _ETHERNET_LINUX_H
+#define _ETHERNET_LINUX_H
+
+struct EthernetHandler *TunTapEthernetHandler(int eth_idx);
+
+#define ETHERNET_HANDLER_CLASSNAME TunTapEthernetHandler
+
+#endif /* _ETHERNET_LINUX_H */
+
diff --git a/src/includes/ethernet_macosx.h b/src/includes/ethernet_macosx.h
new file mode 100644
index 00000000..fbda5a82
--- /dev/null
+++ b/src/includes/ethernet_macosx.h
@@ -0,0 +1,34 @@
+/*
+ * ethernet_macosx.h - 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/>
+ */
+
+#ifndef _ETHERNET_MACOSX_H
+#define _ETHERNET_MACOSX_H
+
+struct EthernetHandler *BPFEthernetHandler(int eth_idx);
+
+#if ENABLE_BPF
+#undef ETHERNET_HANDLER_CLASSNAME
+#define ETHERNET_HANDLER_CLASSNAME BPFEthernetHandler
+#endif
+
+#endif /* _ETHERNET_MACOSX_H */
+
diff --git a/src/includes/ethernet_nfapi.h b/src/includes/ethernet_nfapi.h
new file mode 100644
index 00000000..d97bcaf4
--- /dev/null
+++ b/src/includes/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/includes/ethernet_win32.h b/src/includes/ethernet_win32.h
new file mode 100644
index 00000000..1822fe47
--- /dev/null
+++ b/src/includes/ethernet_win32.h
@@ -0,0 +1,31 @@
+/*
+ * ethernet_win32.h - Win32 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/>
+ *
+ */
+
+#ifndef _ETHERNET_CYGWIN_H
+#define _ETHERNET_CYGWIN_H
+
+struct EthernetHandler *WinTapEthernetHandler(int eth_idx);
+
+#define ETHERNET_HANDLER_CLASSNAME WinTapEthernetHandler
+
+#endif /* _ETHERNET_CYGWIN_H */
diff --git a/src/includes/fd_trans.h b/src/includes/fd_trans.h
new file mode 100644
index 00000000..7253f09b
--- /dev/null
+++ b/src/includes/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/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/nf_ethernet.c b/src/nf_ethernet.c
new file mode 100644
index 00000000..544e8897
--- /dev/null
+++ b/src/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", which, ethX, st_name_ptr, name_maxlen);
+
+	if (ethX < 0 || ethX >= MAX_ETH)
+	{
+		LOG_TRACE(LOG_WARN, "Ethernet: handler for %d not found", 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", 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", ethX);
+		return;
+	}
+	if (len > MAX_PACKET_SIZE)
+	{
+		LOG_TRACE(LOG_WARN, "readPacket() - length %d > %d", 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", 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, "WARNING: Couldn't transmit packet");
+	}
+}
+
+
+/*
+ *  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, " packet received (len %d), triggering ETHERNETDriver interrupt", (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, " waiting for int acknowledge with pending irq mask %02x", pending_interrupts);
+		SDL_SemWait(handler->intAck);
+		pending_interrupts &= ~(1 << handler->ethX);
+		LOG_TRACE(TRACE_ETHERNET, " int acknowledged, pending irq mask now %02x", pending_interrupts);
+	}
+
+	return 0;
+}
+
+
+/*
+ *  Stop packet reception thread
+ */
+static void stopThread(struct EthernetHandler *handler)
+{
+	if (handler && handler->handlingThread)
+	{
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: Stop thread");
+
+#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: Start thread");
+
+		if ((handler->intAck = SDL_CreateSemaphore(0)) == NULL)
+		{
+			LOG_TRACE(TRACE_ETHERNET, "WARNING: Cannot init semaphore");
+			return false;
+		}
+
+		handler->handlingThread = SDL_CreateThread(receiveFunc, "Ethernet", handler);
+		if (handler->handlingThread == NULL)
+		{
+			LOG_TRACE(TRACE_ETHERNET, "WARNING: Cannot start ETHERNETDriver thread");
+			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");
+		*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)", 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, "MAC Address of [ETH%d] in incorrect format", 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, "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)", dev_bit);
+					break;
+				}
+
+				handler = getHandler(ethX);
+
+				if (handler == NULL)
+				{
+					LOG_TRACE(LOG_WARN, "Ethernet: handler for %d not found", ethX);
+				} else
+				{
+					LOG_TRACE(TRACE_ETHERNET, "Ethernet: ETH%d IRQ acknowledged", ethX);
+					/* Acknowledge interrupt to reception thread */
+					SDL_SemPost(handler->intAck);
+				}
+			}
+		}
+		break;
+
+	case XIF_START:
+		if (!super)
+		{
+			LOG_TRACE(TRACE_NATFEATS, "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, "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, "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, "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", st_buf_ptr, buf_size);
+			readPacket(ethX, buf_ptr, buf_size);
+		}
+		break;
+
+	case XIF_WRITEBLOCK:
+		if (!super)
+		{
+			LOG_TRACE(TRACE_NATFEATS, "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", st_buf_ptr, buf_size);
+			sendPacket(ethX, buf_ptr, buf_size);
+		}
+		break;
+
+	case XIF_GET_IPHOST:
+		LOG_TRACE(TRACE_ETHERNET, "XIF_GET_IPHOST");
+		*retval = get_params(HOST_IP, stack);
+		break;
+
+	case XIF_GET_IPATARI:
+		LOG_TRACE(TRACE_ETHERNET, "XIF_GET_IPATARI");
+		*retval = get_params(ATARI_IP, stack);
+		break;
+
+	case XIF_GET_NETMASK:
+		LOG_TRACE(TRACE_ETHERNET, "XIF_GET_NETMASK");
+		*retval = get_params(NETMASK, stack);
+		break;
+
+	default:
+		LOG_TRACE(TRACE_ETHERNET, "Ethernet: unsupported function %d", subid);
+		*retval = GEMDOS_EINVFN;
+		break;
+	}
+
+	return true;
+}
+
+
+/*
+ *  Initialization
+ */
+void nf_ethernet_init(void)
+{
+	struct EthernetHandler *handler;
+	int i;
+
+	LOG_TRACE(TRACE_ETHERNET, "Ethernet: init");
+
+	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");
+
+	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/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 */

Attachment: araup
Description: application/shellscript



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