diff --git a/BasiliskII/ChangeLog b/BasiliskII/ChangeLog index dfaf0d8b..ddb03f53 100644 --- a/BasiliskII/ChangeLog +++ b/BasiliskII/ChangeLog @@ -15,6 +15,8 @@ V1.0 (snapshot) - - Unix: the option "--config FILE" tells B2 to use a different config file - Unix: new prefs options "dsp" and "mixer" to set the OSS device names instead of the hardcoded '/dev/dsp' and '/dev/mixer' + - Unix: new ether prefs option 'tun' to use a TUN/TAP interface. The + configure script used can be overriden with "etherconfig" V1.0 (snapshot) - 15.Jan.2002 - added support for on-the-fly video resolution and depth switching, and diff --git a/BasiliskII/src/Unix/Makefile.in b/BasiliskII/src/Unix/Makefile.in index 47f13f41..51649770 100644 --- a/BasiliskII/src/Unix/Makefile.in +++ b/BasiliskII/src/Unix/Makefile.in @@ -72,6 +72,7 @@ install: $(APP) installdirs -$(INSTALL_DATA) $(APP).1 $(DESTDIR)$(man1dir)/$(APP).1 $(INSTALL_DATA) keycodes $(DESTDIR)$(datadir)/$(APP)/keycodes $(INSTALL_DATA) fbdevices $(DESTDIR)$(datadir)/$(APP)/fbdevices + $(INSTALL_DATA) tunconfig $(DESTDIR)$(datadir)/$(APP)/tunconfig installdirs: $(SHELL) mkinstalldirs $(DESTDIR)$(bindir) $(DESTDIR)$(man1dir) $(DESTDIR)$(datadir)/$(APP) @@ -81,6 +82,7 @@ uninstall: rm -f $(DESTDIR)$(man1dir)/$(APP).1 rm -f $(DESTDIR)$(datadir)/$(APP)/keycodes rm -f $(DESTDIR)$(datadir)/$(APP)/fbdevices + rm -f $(DESTDIR)$(datadir)/$(APP)/tunconfig rmdir $(DESTDIR)$(datadir)/$(APP) mostlyclean: diff --git a/BasiliskII/src/Unix/configure.ac b/BasiliskII/src/Unix/configure.ac index e460b0ed..4d151931 100644 --- a/BasiliskII/src/Unix/configure.ac +++ b/BasiliskII/src/Unix/configure.ac @@ -229,6 +229,7 @@ dnl Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS(unistd.h fcntl.h sys/types.h sys/time.h sys/mman.h mach/mach.h) AC_CHECK_HEADERS(readline.h history.h readline/readline.h readline/history.h) +AC_CHECK_HEADERS(linux/if.h linux/if_tun.h net/if.h net/if_tun.h) dnl Checks for typedefs, structures, and compiler characteristics. AC_C_BIGENDIAN @@ -412,6 +413,29 @@ AC_DEFUN(AC_TRANSLATE_DEFINE, [ fi ]) +dnl Check that the host supports TUN/TAP devices +AC_CACHE_CHECK([whether TUN/TAP is supported], + ac_cv_tun_tap_support, [ + AC_TRY_COMPILE([ + #if defined(HAVE_LINUX_IF_H) && defined(HAVE_LINUX_IF_TUN_H) + #include + #include + #endif + #if defined(HAVE_NET_IF_H) && defined(HAVE_NET_IF_TUN_H) + #include + #include + #endif + ], [ + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + ], + ac_cv_tun_tap_support=yes, ac_cv_tun_tap_support=no + ) +]) +AC_TRANSLATE_DEFINE(ENABLE_TUNTAP, "$ac_cv_tun_tap_support", + [Define if your system supports TUN/TAP devices.]) + dnl Various checks if the system supports vm_allocate() and the like functions. have_mach_vm=no if [[ "x$ac_cv_func_vm_allocate" = "xyes" -a "x$ac_cv_func_vm_deallocate" = "xyes" -a \ diff --git a/BasiliskII/src/Unix/ether_unix.cpp b/BasiliskII/src/Unix/ether_unix.cpp index bb22483f..a63941f7 100644 --- a/BasiliskII/src/Unix/ether_unix.cpp +++ b/BasiliskII/src/Unix/ether_unix.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,16 @@ #include #endif +#if defined(HAVE_LINUX_IF_H) && defined(HAVE_LINUX_IF_TUN_H) +#include +#include +#endif + +#if defined(HAVE_NET_IF_H) && defined(HAVE_NET_IF_TUN_H) +#include +#include +#endif + #include "cpu_emulation.h" #include "main.h" #include "macos_util.h" @@ -52,14 +63,26 @@ using std::map; #define MONITOR 0 +// Ethernet device types +enum { + NET_IF_SHEEPNET, + NET_IF_ETHERTAP, + NET_IF_TUNTAP, +}; + +// Constants +static const char ETHERCONFIG_FILE_NAME[] = DATADIR "/tunconfig"; + // Global variables static int fd = -1; // fd of sheep_net device static pthread_t ether_thread; // Packet reception thread static pthread_attr_t ether_thread_attr; // Packet reception thread attributes static bool thread_active = false; // Flag: Packet reception thread installed static sem_t int_ack; // Interrupt acknowledge semaphore -static bool is_ethertap; // Flag: Ethernet device is ethertap static bool udp_tunnel; // Flag: UDP tunnelling active, fd is the socket descriptor +static int net_if_type = -1; // Ethernet device type +static const char *net_if_name = NULL; // TUN/TAP device name +static const char *net_if_script = NULL; // Network config script // Attached network protocols, maps protocol type to MacOS handler address static map net_protocols; @@ -105,6 +128,35 @@ static void stop_thread(void) } +/* + * Execute network script up|down + */ + +static bool execute_network_script(const char *action) +{ + if (net_if_script == NULL || net_if_name == NULL) + return false; + + int pid = fork(); + if (pid >= 0) { + if (pid == 0) { + char *args[4]; + args[0] = (char *)net_if_script; + args[1] = (char *)net_if_name; + args[2] = (char *)action; + args[3] = NULL; + execv(net_if_script, args); + exit(1); + } + int status; + while (waitpid(pid, &status, 0) != pid); + return WIFEXITED(status) && WEXITSTATUS(status) == 0; + } + + return false; +} + + /* * Initialization */ @@ -119,15 +171,30 @@ bool ether_init(void) if (name == NULL) return false; - // Is it Ethertap? - is_ethertap = (strncmp(name, "tap", 3) == 0); + // Determine Ethernet device type + net_if_type = -1; + if (strncmp(name, "tap", 3) == 0) + net_if_type = NET_IF_ETHERTAP; +#if ENABLE_TUNTAP + else if (strcmp(name, "tun") == 0) + net_if_type = NET_IF_TUNTAP; +#endif + else + net_if_type = NET_IF_SHEEPNET; // Open sheep_net or ethertap device char dev_name[16]; - if (is_ethertap) + switch (net_if_type) { + case NET_IF_ETHERTAP: sprintf(dev_name, "/dev/%s", name); - else + break; + case NET_IF_TUNTAP: + sprintf(dev_name, "/dev/net/tun", name); + break; + case NET_IF_SHEEPNET: strcpy(dev_name, "/dev/sheep_net"); + break; + } fd = open(dev_name, O_RDWR); if (fd < 0) { sprintf(str, GetString(STR_NO_SHEEP_NET_DRIVER_WARN), dev_name, strerror(errno)); @@ -135,9 +202,43 @@ bool ether_init(void) goto open_error; } +#if ENABLE_TUNTAP + // Open TUN/TAP interface + if (net_if_type == NET_IF_TUNTAP) { + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + strcpy(ifr.ifr_name, "tun%d"); + if (ioctl(fd, TUNSETIFF, (void *) &ifr) != 0) { + sprintf(str, GetString(STR_SHEEP_NET_ATTACH_WARN), strerror(errno)); + WarningAlert(str); + goto open_error; + } + + // Get network config script file path + net_if_script = PrefsFindString("etherconfig"); + if (net_if_script == NULL) + net_if_script = ETHERCONFIG_FILE_NAME; + + // Start network script up + if (net_if_script == NULL) { + sprintf(str, GetString(STR_TUN_TAP_CONFIG_WARN), "script not found"); + WarningAlert(str); + goto open_error; + } + net_if_name = ifr.ifr_name; + if (!execute_network_script("up")) { + sprintf(str, GetString(STR_TUN_TAP_CONFIG_WARN), "script execute error"); + WarningAlert(str); + goto open_error; + } + D(bug("Connected to host network interface: %s\n", net_if_name)); + } +#endif + #if defined(__linux__) // Attach sheep_net to selected Ethernet card - if (!is_ethertap && ioctl(fd, SIOCSIFLINK, name) < 0) { + if (net_if_type == NET_IF_SHEEPNET && ioctl(fd, SIOCSIFLINK, name) < 0) { sprintf(str, GetString(STR_SHEEP_NET_ATTACH_WARN), strerror(errno)); WarningAlert(str); goto open_error; @@ -148,7 +249,7 @@ bool ether_init(void) ioctl(fd, FIONBIO, &nonblock); // Get Ethernet address - if (is_ethertap) { + if (net_if_type == NET_IF_ETHERTAP) { pid_t p = getpid(); // If configured for multicast, ethertap requires that the lower 32 bit of the Ethernet address are our PID ether_addr[0] = 0xfe; ether_addr[1] = 0xfd; @@ -170,6 +271,9 @@ bool ether_init(void) open_error: stop_thread(); + if (net_if_type == NET_IF_TUNTAP) + execute_network_script("down"); + if (fd > 0) { close(fd); fd = -1; @@ -214,9 +318,9 @@ void ether_reset(void) int16 ether_add_multicast(uint32 pb) { - if (ioctl(fd, SIOCADDMULTI, Mac2HostAddr(pb + eMultiAddr)) < 0) { + if (net_if_type != NET_IF_TUNTAP && ioctl(fd, SIOCADDMULTI, Mac2HostAddr(pb + eMultiAddr)) < 0) { D(bug("WARNING: Couldn't enable multicast address\n")); - if (is_ethertap) + if (net_if_type == NET_IF_ETHERTAP) return noErr; else return eMultiErr; @@ -231,7 +335,7 @@ int16 ether_add_multicast(uint32 pb) int16 ether_del_multicast(uint32 pb) { - if (ioctl(fd, SIOCDELMULTI, Mac2HostAddr(pb + eMultiAddr)) < 0) { + if (net_if_type != NET_IF_TUNTAP && ioctl(fd, SIOCDELMULTI, Mac2HostAddr(pb + eMultiAddr)) < 0) { D(bug("WARNING: Couldn't disable multicast address\n")); return eMultiErr; } else @@ -274,7 +378,7 @@ int16 ether_write(uint32 wds) uint8 packet[1516], *p = packet; int len = 0; #if defined(__linux__) - if (is_ethertap) { + if (net_if_type == NET_IF_ETHERTAP) { *p++ = 0; // Linux ethertap discards the first 2 bytes *p++ = 0; len += 2; @@ -375,7 +479,7 @@ void EtherInterrupt(void) // Read packet from sheep_net device #if defined(__linux__) - length = read(fd, packet, is_ethertap ? 1516 : 1514); + length = read(fd, packet, net_if_type == NET_IF_ETHERTAP ? 1516 : 1514); #else length = read(fd, packet, 1514); #endif @@ -393,7 +497,7 @@ void EtherInterrupt(void) // Pointer to packet data (Ethernet header) uint8 *p = packet; #if defined(__linux__) - if (is_ethertap) { + if (net_if_type == NET_IF_ETHERTAP) { p += 2; // Linux ethertap has two random bytes before the packet length -= 2; } diff --git a/BasiliskII/src/Unix/tunconfig b/BasiliskII/src/Unix/tunconfig new file mode 100755 index 00000000..d2adcaa3 --- /dev/null +++ b/BasiliskII/src/Unix/tunconfig @@ -0,0 +1,79 @@ +#!/bin/bash +########################################################################### +# Configuration of the tunN devices for usage with Basilisk II. +# (derived MOL tunconfig script) +# +# This script should be named /usr/share/BasiliskII/tunconfig (unless +# the default name has been changed with the 'etherconfig' keyword). +# +# Usage: tunconfig iface up|down +# +# If the linux box is configured as a firewall, the rules below might +# need some adjustments. +# +########################################################################### + +SUDO=/usr/bin/sudo +IPTABLES=/sbin/iptables + +######################################################### + +TUN_DEV=$1 +ACTION=$2 + +TUN_NUM=`echo $TUN_DEV | sed s/[^0-9]//g` +NET_NUM=`expr 40 + $TUN_NUM` +TUN_NET=172.20.$NET_NUM.0/24 +TUN_HOST=172.20.$NET_NUM.1 + +######################################################### +# Misc Checks +######################################################### + +[[ $# = 2 ]] || { + echo "Usage: tunconfig iface up|down" + exit 2 +} + +[[ "`id -u`" = "0" ]] && { + echo "---> $SUDO not necessary." 1>&2 + SUDO="" +} + +[[ -x $IPTABLES ]] && { + IPTABLES="$SUDO $IPTABLES" +} || { + echo "---> $IPTABLES not found." 1>&2 + IPTABLES=/bin/true +} + +$IPTABLES -L -n -t nat > /dev/null || exit 1 + +######################################################### +# Remove old (possibly stale) ruleset +######################################################### + +{ + $IPTABLES -t nat -D POSTROUTING -s $TUN_NET -d ! $TUN_NET -j MASQUERADE +} >& /dev/null + +######################################################### +# Bring down interface +######################################################### + +[[ "$ACTION" = down ]] && { + $SUDO /sbin/ifconfig $TUN_DEV down +} + +######################################################### +# Configure interface +######################################################### + +[[ "$ACTION" = up ]] && { + $SUDO /sbin/ifconfig $TUN_DEV $TUN_HOST + + # masquerade the tun network + $IPTABLES -t nat -A POSTROUTING -s $TUN_NET -d ! $TUN_NET -j MASQUERADE +} + +exit 0 diff --git a/BasiliskII/src/Unix/user_strings_unix.cpp b/BasiliskII/src/Unix/user_strings_unix.cpp index b18732e0..f3581135 100644 --- a/BasiliskII/src/Unix/user_strings_unix.cpp +++ b/BasiliskII/src/Unix/user_strings_unix.cpp @@ -48,6 +48,7 @@ user_string_def platform_strings[] = { {STR_NO_SHEEP_NET_DRIVER_WARN, "Cannot open %s (%s). Ethernet will not be available."}, {STR_SHEEP_NET_ATTACH_WARN, "Cannot attach to Ethernet card (%s). Ethernet will not be available."}, + {STR_TUN_TAP_CONFIG_WARN, "Cannot configure TUN/TAP device (%s). Ethernet will not be available."}, {STR_SCSI_DEVICE_OPEN_WARN, "Cannot open %s (%s). SCSI Manager access to this device will be disabled."}, {STR_SCSI_DEVICE_NOT_SCSI_WARN, "%s doesn't seem to comply to the Generic SCSI API. SCSI Manager access to this device will be disabled."}, {STR_NO_AUDIO_DEV_WARN, "Cannot open %s (%s). Audio output will be disabled."}, diff --git a/BasiliskII/src/Unix/user_strings_unix.h b/BasiliskII/src/Unix/user_strings_unix.h index fb97881a..4577592d 100644 --- a/BasiliskII/src/Unix/user_strings_unix.h +++ b/BasiliskII/src/Unix/user_strings_unix.h @@ -39,6 +39,7 @@ enum { STR_NO_SHEEP_NET_DRIVER_WARN, STR_SHEEP_NET_ATTACH_WARN, + STR_TUN_TAP_CONFIG_WARN, STR_SCSI_DEVICE_OPEN_WARN, STR_SCSI_DEVICE_NOT_SCSI_WARN, STR_NO_AUDIO_DEV_WARN, diff --git a/BasiliskII/src/prefs_items.cpp b/BasiliskII/src/prefs_items.cpp index 1f35663e..891d8147 100644 --- a/BasiliskII/src/prefs_items.cpp +++ b/BasiliskII/src/prefs_items.cpp @@ -43,6 +43,7 @@ prefs_desc common_prefs_items[] = { {"seriala", TYPE_STRING, false, "device name of Mac serial port A"}, {"serialb", TYPE_STRING, false, "device name of Mac serial port B"}, {"ether", TYPE_STRING, false, "device name of Mac ethernet adapter"}, + {"etherconfig", TYPE_STRING, false,"path of network config script"}, {"udptunnel", TYPE_BOOLEAN, false, "tunnel all network packets over UDP"}, {"udpport", TYPE_INT32, false, "IP port number for tunneling"}, {"rom", TYPE_STRING, false, "path of ROM file"},