From 6c35c2a9e8eff972816fee23501f3a14c57edf17 Mon Sep 17 00:00:00 2001 From: cebix <> Date: Thu, 12 Jul 2001 19:48:28 +0000 Subject: [PATCH] - Implemented AppleTalk-over-UDP tunnelling, activated by setting "udptunnel" to "true". This uses the BSD socket API, so it's fairly portable (currently only imeplemented under Unix, though). This works by sending raw Ethernet packets as UDP packets to a fixed port number ("udpport", default is 6066), using IP broadcasts to simulate Ethernet broad- and multicasts. Currently only tested with AppleTalk. --- BasiliskII/ChangeLog | 4 + BasiliskII/README | 19 ++ BasiliskII/src/AmigaOS/ether_amiga.cpp | 12 +- BasiliskII/src/BeOS/ether_beos.cpp | 66 ++--- BasiliskII/src/Unix/ether_unix.cpp | 210 ++++++++++------ BasiliskII/src/Unix/prefs_editor_gtk.cpp | 46 +++- BasiliskII/src/Unix/sysdeps.h | 3 + BasiliskII/src/ether.cpp | 302 ++++++++++++++++++++--- BasiliskII/src/include/ether.h | 32 ++- BasiliskII/src/include/user_strings.h | 2 + BasiliskII/src/prefs_items.cpp | 4 + BasiliskII/src/user_strings.cpp | 2 + 12 files changed, 539 insertions(+), 163 deletions(-) diff --git a/BasiliskII/ChangeLog b/BasiliskII/ChangeLog index 05e7c055..521ada12 100644 --- a/BasiliskII/ChangeLog +++ b/BasiliskII/ChangeLog @@ -10,6 +10,10 @@ V1.0 (snapshot) - - ADBInterrupt() is no longer called from the 60Hz interrupt but has its own interrupt flag, potentially increasing the smoothness of mouse movement + - ether.cpp: implemented relatively platform-independant "AppleTalk + over UDP" mode that doesn't require any special kernel modules or + network drivers but can only interconnect instances of Basilisk II; + this is enabled by setting "udptunnel" to true - Unix: windowed display mode supports different resolutions and color depths, which can be switched on-the-fly - Unix: Ctrl-F5 grabs mouse in windowed mode (enhanced compatibility diff --git a/BasiliskII/README b/BasiliskII/README index 32d78ac7..b805af9f 100644 --- a/BasiliskII/README +++ b/BasiliskII/README @@ -394,6 +394,25 @@ ether not an Ethernet device, Basilisk II will display a warning message and disable Ethernet networking. + See the next item for an alternative way to do networking with Basilisk II. + +udptunnel <"true" or "false"> + + Setting this to "true" enables a special network mode in which all network + packets sent by MacOS are tunnelled over UDP using the host operating + system's native TCP/IP stack. This only works with AppleTalk and can only + be used to connect computers running Basilisk II (and not, for example, for + connecting to an AppleShare server running on a real Mac), but it is + probably the easiest way to set up a network between two instances of + Basilisk II because the UDP tunnelling doesn't require any special kernel + modules or network add-ons. It relies on IP broadcasting, however, so + its range is limited. + +udpport + + This item specifies the IP port number to use for the "AppleTalk over UDP" + tunnel mode. The default is 6066. + rom This item specifies the file name of the Mac ROM file to be used by diff --git a/BasiliskII/src/AmigaOS/ether_amiga.cpp b/BasiliskII/src/AmigaOS/ether_amiga.cpp index dbb62b7e..1b3fa880 100644 --- a/BasiliskII/src/AmigaOS/ether_amiga.cpp +++ b/BasiliskII/src/AmigaOS/ether_amiga.cpp @@ -116,11 +116,11 @@ static int16 send_to_proc(uint32 what, uint32 pointer = 0, uint16 type = 0) * Initialization */ -void EtherInit(void) +bool ether_init(void) { // Do nothing if no Ethernet device specified if (PrefsFindString("ether") == NULL) - return; + return false; // Initialize protocol list NewList(&prot_list); @@ -151,8 +151,7 @@ void EtherInit(void) goto open_error; // Everything OK - net_open = true; - return; + return true; open_error: net_proc = NULL; @@ -160,6 +159,7 @@ open_error: DeleteMsgPort(reply_port); reply_port = NULL; } + return false; } @@ -167,7 +167,7 @@ open_error: * Deinitialization */ -void EtherExit(void) +void ether_exit(void) { // Stop process if (net_proc) { @@ -191,7 +191,7 @@ void EtherExit(void) void EtherReset(void) { // Remove all protocols - if (net_open) + if (net_proc) send_to_proc(MSG_CLEANUP); } diff --git a/BasiliskII/src/BeOS/ether_beos.cpp b/BasiliskII/src/BeOS/ether_beos.cpp index 0fdac67f..4af15699 100644 --- a/BasiliskII/src/BeOS/ether_beos.cpp +++ b/BasiliskII/src/BeOS/ether_beos.cpp @@ -105,11 +105,11 @@ static void remove_all_protocols(void) * Initialization */ -void EtherInit(void) +bool ether_init(void) { // Do nothing if no Ethernet device specified if (PrefsFindString("ether") == NULL) - return; + return false; // Find net_server team i_wanna_try_that_again: @@ -148,12 +148,12 @@ i_wanna_try_that_again: // It was found, so something else must be wrong if (sheep_net_found) { WarningAlert(GetString(STR_NO_NET_ADDON_WARN)); - return; + return false; } // Not found, inform the user if (!ChoiceAlert(GetString(STR_NET_CONFIG_MODIFY_WARN), GetString(STR_OK_BUTTON), GetString(STR_CANCEL_BUTTON))) - return; + return false; // Change the network config file and restart the network fin = fopen("/boot/home/config/settings/network", "r"); @@ -208,16 +208,16 @@ i_wanna_try_that_again: area_id handler_buffer; if ((handler_buffer = find_area("packet buffer")) < B_NO_ERROR) { WarningAlert(GetString(STR_NET_ADDON_INIT_FAILED)); - return; + return false; } if ((buffer_area = clone_area("local packet buffer", (void **)&net_buffer_ptr, B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, handler_buffer)) < B_NO_ERROR) { D(bug("EtherInit: couldn't clone packet area\n")); WarningAlert(GetString(STR_NET_ADDON_CLONE_FAILED)); - return; + return false; } if ((read_sem = create_sem(0, "ether read")) < B_NO_ERROR) { printf("FATAL: can't create Ethernet semaphore\n"); - return; + return false; } net_buffer_ptr->read_sem = read_sem; write_sem = net_buffer_ptr->write_sem; @@ -233,7 +233,7 @@ i_wanna_try_that_again: D(bug("Ethernet address %02x %02x %02x %02x %02x %02x\n", ether_addr[0], ether_addr[1], ether_addr[2], ether_addr[3], ether_addr[4], ether_addr[5])); // Everything OK - net_open = true; + return true; } @@ -241,27 +241,24 @@ i_wanna_try_that_again: * Deinitialization */ -void EtherExit(void) +void ether_exit(void) { - if (net_open) { + // Close communications with add-on + for (int i=0; iwrite[i].cmd = IN_USE | (DEACTIVATE_SHEEP_NET << 8); + release_sem(write_sem); - // Close communications with add-on - for (int i=0; iwrite[i].cmd = IN_USE | (DEACTIVATE_SHEEP_NET << 8); - release_sem(write_sem); + // Quit reception thread + ether_thread_active = false; + status_t result; + release_sem(read_sem); + wait_for_thread(read_thread, &result); - // Quit reception thread - ether_thread_active = false; - status_t result; - release_sem(read_sem); - wait_for_thread(read_thread, &result); + delete_sem(read_sem); + delete_area(buffer_area); - delete_sem(read_sem); - delete_area(buffer_area); - - // Remove all protocols - remove_all_protocols(); - } + // Remove all protocols + remove_all_protocols(); } @@ -364,30 +361,21 @@ int16 ether_write(uint32 wds) } else { // Copy packet to buffer - uint8 *start; - uint8 *bp = start = p->data; - for (;;) { - int len = ReadMacInt16(wds); - if (len == 0) - break; - Mac2Host_memcpy(bp, ReadMacInt32(wds + 2), len); - bp += len; - wds += 6; - } + int len = ether_wds_to_buffer(wds, p->data); // Set source address - memcpy(start + 6, ether_addr, 6); + memcpy(p->data + 6, ether_addr, 6); #if MONITOR bug("Sending Ethernet packet:\n"); - for (int i=0; i<(uint32)(bp-start); i++) { - bug("%02x ", start[i]); + for (int i=0; idata[i]); } bug("\n"); #endif // Notify add-on - p->length = (uint32)(bp - start); + p->length = len; p->cmd = IN_USE | (SHEEP_PACKET << 8); wr_pos = (wr_pos + 1) % WRITE_PACKET_COUNT; release_sem(write_sem); diff --git a/BasiliskII/src/Unix/ether_unix.cpp b/BasiliskII/src/Unix/ether_unix.cpp index aa9ba80f..a89dfe90 100644 --- a/BasiliskII/src/Unix/ether_unix.cpp +++ b/BasiliskII/src/Unix/ether_unix.cpp @@ -27,8 +27,10 @@ #include #include -#if defined(__FreeBSD__) +#include #include + +#if defined(__FreeBSD__) #include #endif @@ -63,6 +65,7 @@ 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 // Prototypes static void *receive_func(void *arg); @@ -106,11 +109,58 @@ static void remove_all_protocols(void) } +/* + * Start packet reception thread + */ + +static bool start_thread(void) +{ + if (sem_init(&int_ack, 0, 0) < 0) { + printf("WARNING: Cannot init semaphore"); + return false; + } + + pthread_attr_init(ðer_thread_attr); +#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) + if (geteuid() == 0) { + pthread_attr_setinheritsched(ðer_thread_attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(ðer_thread_attr, SCHED_FIFO); + struct sched_param fifo_param; + fifo_param.sched_priority = (sched_get_priority_min(SCHED_FIFO) + sched_get_priority_max(SCHED_FIFO)) / 2 + 1; + pthread_attr_setschedparam(ðer_thread_attr, &fifo_param); + } +#endif + + thread_active = (pthread_create(ðer_thread, ðer_thread_attr, receive_func, NULL) == 0); + if (!thread_active) { + printf("WARNING: Cannot start Ethernet thread"); + return false; + } + + return true; +} + + +/* + * Stop packet reception thread + */ + +static void stop_thread(void) +{ + if (thread_active) { + pthread_cancel(ether_thread); + pthread_join(ether_thread, NULL); + sem_destroy(&int_ack); + thread_active = false; + } +} + + /* * Initialization */ -void EtherInit(void) +bool ether_init(void) { int nonblock = 1; char str[256]; @@ -118,7 +168,7 @@ void EtherInit(void) // Do nothing if no Ethernet device specified const char *name = PrefsFindString("ether"); if (name == NULL) - return; + return false; // Is it Ethertap? is_ethertap = (strncmp(name, "tap", 3) == 0); @@ -162,41 +212,20 @@ void EtherInit(void) D(bug("Ethernet address %02x %02x %02x %02x %02x %02x\n", ether_addr[0], ether_addr[1], ether_addr[2], ether_addr[3], ether_addr[4], ether_addr[5])); // Start packet reception thread - if (sem_init(&int_ack, 0, 0) < 0) { - printf("WARNING: Cannot init semaphore"); + if (!start_thread()) goto open_error; - } - pthread_attr_init(ðer_thread_attr); -#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) - if (geteuid() == 0) { - pthread_attr_setinheritsched(ðer_thread_attr, PTHREAD_EXPLICIT_SCHED); - pthread_attr_setschedpolicy(ðer_thread_attr, SCHED_FIFO); - struct sched_param fifo_param; - fifo_param.sched_priority = (sched_get_priority_min(SCHED_FIFO) + sched_get_priority_max(SCHED_FIFO)) / 2 + 1; - pthread_attr_setschedparam(ðer_thread_attr, &fifo_param); - } -#endif - thread_active = (pthread_create(ðer_thread, ðer_thread_attr, receive_func, NULL) == 0); - if (!thread_active) { - printf("WARNING: Cannot start Ethernet thread"); - goto open_error; - } // Everything OK - net_open = true; - return; + return true; open_error: - if (thread_active) { - pthread_cancel(ether_thread); - pthread_join(ether_thread, NULL); - sem_destroy(&int_ack); - thread_active = false; - } + stop_thread(); + if (fd > 0) { close(fd); fd = -1; } + return false; } @@ -204,7 +233,7 @@ open_error: * Deinitialization */ -void EtherExit(void) +void ether_exit(void) { // Stop reception thread if (thread_active) { @@ -334,15 +363,7 @@ int16 ether_write(uint32 wds) len += 2; } #endif - for (;;) { - int w = ReadMacInt16(wds); - if (w == 0) - break; - Mac2Host_memcpy(p, ReadMacInt32(wds + 2), w); - len += w; - p += w; - wds += 6; - } + len += ether_wds_to_buffer(wds, p); #if MONITOR bug("Sending Ethernet packet:\n"); @@ -361,6 +382,29 @@ int16 ether_write(uint32 wds) } +/* + * Start UDP packet reception thread + */ + +bool ether_start_udp_thread(int socket_fd) +{ + fd = socket_fd; + udp_tunnel = true; + return start_thread(); +} + + +/* + * Stop UDP packet reception thread + */ + +void ether_stop_udp_thread(void) +{ + stop_thread(); + fd = -1; +} + + /* * Packet reception thread */ @@ -397,59 +441,73 @@ void EtherInterrupt(void) // Call protocol handler for received packets uint8 packet[1516]; + ssize_t length; for (;;) { - // Read packet from sheep_net device + if (udp_tunnel) { + + // Read packet from socket + struct sockaddr_in from; + socklen_t from_len = sizeof(from); + length = recvfrom(fd, packet, 1514, 0, (struct sockaddr *)&from, &from_len); + if (length < 14) + break; + ether_udp_read(packet, length, &from); + + } else { + + // Read packet from sheep_net device #if defined(__linux__) - ssize_t length = read(fd, packet, is_ethertap ? 1516 : 1514); + length = read(fd, packet, is_ethertap ? 1516 : 1514); #else - ssize_t length = read(fd, packet, 1514); + length = read(fd, packet, 1514); #endif - if (length < 14) - break; + if (length < 14) + break; #if MONITOR - bug("Receiving Ethernet packet:\n"); - for (int i=0; ihandler == 0) - continue; + // No default handler + if (prot->handler == 0) + continue; - // Copy header to RHA - Host2Mac_memcpy(ether_data + ed_RHA, p, 14); - D(bug(" header %08lx%04lx %08lx%04lx %04lx\n", ReadMacInt32(ether_data + ed_RHA), ReadMacInt16(ether_data + ed_RHA + 4), ReadMacInt32(ether_data + ed_RHA + 6), ReadMacInt16(ether_data + ed_RHA + 10), ReadMacInt16(ether_data + ed_RHA + 12))); + // Copy header to RHA + Host2Mac_memcpy(ether_data + ed_RHA, p, 14); + D(bug(" header %08x%04x %08x%04x %04x\n", ReadMacInt32(ether_data + ed_RHA), ReadMacInt16(ether_data + ed_RHA + 4), ReadMacInt32(ether_data + ed_RHA + 6), ReadMacInt16(ether_data + ed_RHA + 10), ReadMacInt16(ether_data + ed_RHA + 12))); - // Call protocol handler - M68kRegisters r; - r.d[0] = type; // Packet type - r.d[1] = length - 14; // Remaining packet length (without header, for ReadPacket) - r.a[0] = (uint32)p + 14; // Pointer to packet (host address, for ReadPacket) - r.a[3] = ether_data + ed_RHA + 14; // Pointer behind header in RHA - r.a[4] = ether_data + ed_ReadPacket; // Pointer to ReadPacket/ReadRest routines - D(bug(" calling protocol handler %08lx, type %08lx, length %08lx, data %08lx, rha %08lx, read_packet %08lx\n", prot->handler, r.d[0], r.d[1], r.a[0], r.a[3], r.a[4])); - Execute68k(prot->handler, &r); + // Call protocol handler + M68kRegisters r; + r.d[0] = type; // Packet type + r.d[1] = length - 14; // Remaining packet length (without header, for ReadPacket) + r.a[0] = (uint32)p + 14; // Pointer to packet (host address, for ReadPacket) + r.a[3] = ether_data + ed_RHA + 14; // Pointer behind header in RHA + r.a[4] = ether_data + ed_ReadPacket; // Pointer to ReadPacket/ReadRest routines + D(bug(" calling protocol handler %08x, type %08x, length %08x, data %08x, rha %08x, read_packet %08x\n", prot->handler, r.d[0], r.d[1], r.a[0], r.a[3], r.a[4])); + Execute68k(prot->handler, &r); + } } // Acknowledge interrupt to reception thread diff --git a/BasiliskII/src/Unix/prefs_editor_gtk.cpp b/BasiliskII/src/Unix/prefs_editor_gtk.cpp index 52e87a89..e16aa8a8 100644 --- a/BasiliskII/src/Unix/prefs_editor_gtk.cpp +++ b/BasiliskII/src/Unix/prefs_editor_gtk.cpp @@ -853,7 +853,23 @@ static void create_input_pane(GtkWidget *top) * "Serial/Network" pane */ -static GtkWidget *w_seriala, *w_serialb, *w_ether; +static GtkWidget *w_seriala, *w_serialb, *w_ether, *w_udp_port; + +// Set sensitivity of widgets +static void set_serial_sensitive(void) +{ +#if SUPPORTS_UDP_TUNNEL + gtk_widget_set_sensitive(w_ether, !PrefsFindBool("udptunnel")); + gtk_widget_set_sensitive(w_udp_port, PrefsFindBool("udptunnel")); +#endif +} + +// "Tunnel AppleTalk over IP" button toggled +static void tb_udptunnel(GtkWidget *widget) +{ + PrefsReplaceBool("udptunnel", GTK_TOGGLE_BUTTON(widget)->active); + set_serial_sensitive(); +} // Read settings from widgets and set preferences static void read_serial_settings(void) @@ -871,6 +887,10 @@ static void read_serial_settings(void) PrefsReplaceString("ether", str); else PrefsRemoveItem("ether"); + +#if SUPPORTS_UDP_TUNNEL + PrefsReplaceInt32("udpport", gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(w_udp_port))); +#endif } // Add names of serial devices @@ -954,8 +974,8 @@ static GList *add_ether_names(void) // Create "Serial/Network" pane static void create_serial_pane(GtkWidget *top) { - GtkWidget *box, *table, *label, *combo, *sep; - GList *glist = add_serial_names(); + GtkWidget *box, *hbox, *table, *label, *combo, *sep; + GtkObject *adj; box = make_pane(top, STR_SERIAL_NETWORK_PANE_TITLE); table = make_table(box, 2, 4); @@ -964,6 +984,7 @@ static void create_serial_pane(GtkWidget *top) gtk_widget_show(label); gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4); + GList *glist = add_serial_names(); combo = gtk_combo_new(); gtk_widget_show(combo); gtk_combo_set_popdown_strings(GTK_COMBO(combo), glist); @@ -1006,6 +1027,25 @@ static void create_serial_pane(GtkWidget *top) gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), str); gtk_table_attach(GTK_TABLE(table), combo, 1, 2, 3, 4, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), (GtkAttachOptions)0, 4, 4); w_ether = GTK_COMBO(combo)->entry; + +#if SUPPORTS_UDP_TUNNEL + make_checkbox(box, STR_UDPTUNNEL_CTRL, "udptunnel", GTK_SIGNAL_FUNC(tb_udptunnel)); + + hbox = gtk_hbox_new(FALSE, 4); + gtk_widget_show(hbox); + gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); + + label = gtk_label_new(GetString(STR_UDPPORT_CTRL)); + gtk_widget_show(label); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + adj = gtk_adjustment_new(PrefsFindInt32("udpport"), 1, 65535, 1, 5, 0); + w_udp_port = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 0.0, 0); + gtk_widget_show(w_udp_port); + gtk_box_pack_start(GTK_BOX(hbox), w_udp_port, FALSE, FALSE, 0); +#endif + + set_serial_sensitive(); } diff --git a/BasiliskII/src/Unix/sysdeps.h b/BasiliskII/src/Unix/sysdeps.h index 38e3a495..520ad4b5 100644 --- a/BasiliskII/src/Unix/sysdeps.h +++ b/BasiliskII/src/Unix/sysdeps.h @@ -103,6 +103,9 @@ /* ExtFS is supported */ #define SUPPORTS_EXTFS 1 +/* BSD socket API supported */ +#define SUPPORTS_UDP_TUNNEL 1 + /* Data types */ typedef unsigned char uint8; diff --git a/BasiliskII/src/ether.cpp b/BasiliskII/src/ether.cpp index cde954b3..2bed3fe2 100644 --- a/BasiliskII/src/ether.cpp +++ b/BasiliskII/src/ether.cpp @@ -32,18 +32,153 @@ #include "main.h" #include "macos_util.h" #include "emul_op.h" +#include "prefs.h" #include "ether.h" #include "ether_defs.h" +#if SUPPORTS_UDP_TUNNEL +#include +#include +#include +#include +#include +#endif + +#include + +#ifndef NO_STD_NAMESPACE +using std::map; +#endif + #define DEBUG 0 #include "debug.h" +#define MONITOR 0 + // Global variables -uint8 ether_addr[6]; // Ethernet address (set by EtherInit()) -bool net_open = false; // Flag: initialization succeeded, network device open (set by EtherInit()) +uint8 ether_addr[6]; // Ethernet address (set by ether_init()) +static bool net_open = false; // Flag: initialization succeeded, network device open (set by EtherInit()) -uint32 ether_data = 0; // Mac address of driver data in MacOS RAM +static bool udp_tunnel = false; // Flag: tunnelling AppleTalk over UDP using BSD socket API +static uint16 udp_port; +static int udp_socket = -1; + +// Mac address of driver data in MacOS RAM +uint32 ether_data = 0; + +// Attached network protocols for UDP tunneling, maps protocol type to MacOS handler address +static map udp_protocols; + + +/* + * Initialization + */ + +void EtherInit(void) +{ + net_open = false; + udp_tunnel = false; + +#if SUPPORTS_UDP_TUNNEL + // UDP tunnelling requested? + if (PrefsFindBool("udptunnel")) { + udp_tunnel = true; + udp_port = PrefsFindInt32("udpport"); + + // Open UDP socket + udp_socket = socket(PF_INET, SOCK_DGRAM, 0); + if (udp_socket < 0) { + perror("socket"); + return; + } + + // Bind to specified address and port + struct sockaddr_in sa; + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = INADDR_ANY; + sa.sin_port = htons(udp_port); + if (bind(udp_socket, (struct sockaddr *)&sa, sizeof(sa)) < 0) { + perror("bind"); + close(udp_socket); + udp_socket = -1; + return; + } + + // Retrieve local IP address (or at least one of them) + socklen_t sa_len = sizeof(sa); + getsockname(udp_socket, (struct sockaddr *)&sa, &sa_len); + uint32 udp_ip = sa.sin_addr.s_addr; + if (udp_ip == INADDR_ANY || udp_ip == INADDR_LOOPBACK) { + char name[256]; + gethostname(name, sizeof(name)); + struct hostent *local = gethostbyname(name); + if (local) + udp_ip = *(uint32 *)local->h_addr_list[0]; + } + udp_ip = ntohl(udp_ip); + + // Construct dummy Ethernet address from local IP address + ether_addr[0] = 'B'; + ether_addr[1] = '2'; + ether_addr[2] = udp_ip >> 24; + ether_addr[3] = udp_ip >> 16; + ether_addr[4] = udp_ip >> 8; + ether_addr[5] = udp_ip; + D(bug("Ethernet address %02x %02x %02x %02x %02x %02x\n", ether_addr[0], ether_addr[1], ether_addr[2], ether_addr[3], ether_addr[4], ether_addr[5])); + + // Set socket options + int on = 1; + setsockopt(udp_socket, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); + ioctl(udp_socket, FIONBIO, &on); + + // Start thread for packet reception + if (!ether_start_udp_thread(udp_socket)) { + close(udp_socket); + udp_socket = -1; + return; + } + + net_open = true; + } else +#endif + if (ether_init()) + net_open = true; +} + + +/* + * Deinitialization + */ + +void EtherExit(void) +{ + if (net_open) { +#if SUPPORTS_UDP_TUNNEL + if (udp_tunnel) { + if (udp_socket >= 0) { + ether_stop_udp_thread(); + close(udp_socket); + udp_socket = -1; + } + } else +#endif + ether_exit(); + net_open = false; + } +} + + +/* + * Check whether Ethernet address is AppleTalk broadcast address + */ + +static inline bool is_apple_talk_broadcast(uint8 *p) +{ + return p[0] == 0x09 && p[1] == 0x00 && p[2] == 0x07 + && p[3] == 0xff && p[4] == 0xff && p[5] == 0xff; +} /* @@ -61,7 +196,7 @@ int16 EtherOpen(uint32 pb, uint32 dce) if (r.a[0] == 0) return openErr; ether_data = r.a[0]; - D(bug(" data %08lx\n", ether_data)); + D(bug(" data %08x\n", ether_data)); WriteMacInt16(ether_data + ed_DeferredTask + qType, dtQType); WriteMacInt32(ether_data + ed_DeferredTask + dtAddr, ether_data + ed_Code); @@ -99,48 +234,98 @@ int16 EtherControl(uint32 pb, uint32 dce) uint16 code = ReadMacInt16(pb + csCode); D(bug("EtherControl %d\n", code)); switch (code) { - case 1: // KillIO + case 1: // KillIO return -1; case kENetAddMulti: // Add multicast address - D(bug("AddMulti %08lx%04x\n", ReadMacInt32(pb + eMultiAddr), ReadMacInt16(pb + eMultiAddr + 4))); - if (net_open) + D(bug(" AddMulti %08x%04x\n", ReadMacInt32(pb + eMultiAddr), ReadMacInt16(pb + eMultiAddr + 4))); + if (net_open && !udp_tunnel) return ether_add_multicast(pb); - else - return noErr; + return noErr; case kENetDelMulti: // Delete multicast address - D(bug("DelMulti %08lx%04x\n", ReadMacInt32(pb + eMultiAddr), ReadMacInt16(pb + eMultiAddr + 4))); - if (net_open) + D(bug(" DelMulti %08x%04x\n", ReadMacInt32(pb + eMultiAddr), ReadMacInt16(pb + eMultiAddr + 4))); + if (net_open && !udp_tunnel) return ether_del_multicast(pb); - else - return noErr; + return noErr; - case kENetAttachPH: // Attach protocol handler - D(bug("AttachPH prot %04x, handler %08lx\n", ReadMacInt16(pb + eProtType), ReadMacInt32(pb + ePointer))); - if (net_open) - return ether_attach_ph(ReadMacInt16(pb + eProtType), ReadMacInt32(pb + ePointer)); - else - return noErr; + case kENetAttachPH: { // Attach protocol handler + uint16 type = ReadMacInt16(pb + eProtType); + uint32 handler = ReadMacInt32(pb + ePointer); + D(bug(" AttachPH prot %04x, handler %08x\n", type, handler)); + if (net_open) { + if (udp_tunnel) { + if (udp_protocols.find(type) != udp_protocols.end()) + return lapProtErr; + udp_protocols[type] = handler; + } else + return ether_attach_ph(type, handler); + } + return noErr; + } - case kENetDetachPH: // Detach protocol handler - D(bug("DetachPH prot %04x\n", ReadMacInt16(pb + eProtType))); - if (net_open) - return ether_detach_ph(ReadMacInt16(pb + eProtType)); - else - return noErr; + case kENetDetachPH: { // Detach protocol handler + uint16 type = ReadMacInt16(pb + eProtType); + D(bug(" DetachPH prot %04x\n", type)); + if (net_open) { + if (udp_tunnel) { + if (udp_protocols.erase(type) == 0) + return lapProtErr; + } else + return ether_detach_ph(type); + } + return noErr; + } - case kENetWrite: // Transmit raw Ethernet packet - D(bug("EtherWrite\n")); - if (ReadMacInt16(ReadMacInt32(pb + ePointer)) < 14) + case kENetWrite: { // Transmit raw Ethernet packet + uint32 wds = ReadMacInt32(pb + ePointer); + D(bug(" EtherWrite\n")); + if (ReadMacInt16(wds) < 14) return eLenErr; // Header incomplete - if (net_open) - return ether_write(ReadMacInt32(pb + ePointer)); - else - return noErr; + if (net_open) { +#if SUPPORTS_UDP_TUNNEL + if (udp_tunnel) { + + // Copy packet to buffer + uint8 packet[1514]; + int len = ether_wds_to_buffer(wds, packet); + + // Set source address and extract destination address + memcpy(packet + 6, ether_addr, 6); + uint32 dest_ip; + if (packet[0] == 'B' && packet[1] == '2') + dest_ip = (packet[2] << 24) | (packet[3] << 16) | (packet[4] << 8) | packet[5]; + else if (is_apple_talk_broadcast(packet)) + dest_ip = INADDR_BROADCAST; + else + return eMultiErr; + +#if MONITOR + bug("Sending Ethernet packet:\n"); + for (int i=0; i remaining ? remaining : len; Host2Mac_memcpy(dest, *src, todo); *src += todo; @@ -181,3 +366,50 @@ void EtherReadPacket(uint8 **src, uint32 &dest, uint32 &len, uint32 &remaining) len -= todo; remaining -= todo; } + + +#if SUPPORTS_UDP_TUNNEL +/* + * Read packet from UDP socket + */ + +void ether_udp_read(uint8 *packet, int length, struct sockaddr_in *from) +{ + // Drop packets sent by us + if (memcmp(packet + 6, ether_addr, 6) == 0) + return; + +#if MONITOR + bug("Receiving Ethernet packet:\n"); + for (int i=0; i