- 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.
This commit is contained in:
cebix 2001-07-12 19:48:28 +00:00
parent 90c7198b75
commit 6c35c2a9e8
12 changed files with 539 additions and 163 deletions

View File

@ -10,6 +10,10 @@ V1.0 (snapshot) - <date>
- 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

View File

@ -394,6 +394,25 @@ ether <ethernet card description>
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 <IP port number>
This item specifies the IP port number to use for the "AppleTalk over UDP"
tunnel mode. The default is 6066.
rom <ROM file path>
This item specifies the file name of the Mac ROM file to be used by

View File

@ -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);
}

View File

@ -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; i<WRITE_PACKET_COUNT; i++)
net_buffer_ptr->write[i].cmd = IN_USE | (DEACTIVATE_SHEEP_NET << 8);
release_sem(write_sem);
// Close communications with add-on
for (int i=0; i<WRITE_PACKET_COUNT; i++)
net_buffer_ptr->write[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; i<len; i++) {
bug("%02x ", p->data[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);

View File

@ -27,8 +27,10 @@
#include <errno.h>
#include <stdio.h>
#if defined(__FreeBSD__)
#include <netinet/in.h>
#include <sys/socket.h>
#if defined(__FreeBSD__)
#include <net/if.h>
#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(&ether_thread_attr);
#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
if (geteuid() == 0) {
pthread_attr_setinheritsched(&ether_thread_attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&ether_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(&ether_thread_attr, &fifo_param);
}
#endif
thread_active = (pthread_create(&ether_thread, &ether_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(&ether_thread_attr);
#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
if (geteuid() == 0) {
pthread_attr_setinheritsched(&ether_thread_attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&ether_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(&ether_thread_attr, &fifo_param);
}
#endif
thread_active = (pthread_create(&ether_thread, &ether_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; i<length; i++) {
bug("%02x ", packet[i]);
}
bug("\n");
bug("Receiving Ethernet packet:\n");
for (int i=0; i<length; i++) {
bug("%02x ", packet[i]);
}
bug("\n");
#endif
// Pointer to packet data (Ethernet header)
uint8 *p = packet;
// Pointer to packet data (Ethernet header)
uint8 *p = packet;
#if defined(__linux__)
if (is_ethertap) {
p += 2; // Linux ethertap has two random bytes before the packet
length -= 2;
}
if (is_ethertap) {
p += 2; // Linux ethertap has two random bytes before the packet
length -= 2;
}
#endif
// Get packet type
uint16 type = ntohs(*(uint16 *)(p + 12));
// Get packet type
uint16 type = (p[12] << 8) | p[13];
// Look for protocol
NetProtocol *prot = find_protocol(type);
if (prot == NULL)
continue;
// Look for protocol
NetProtocol *prot = find_protocol(type);
if (prot == NULL)
continue;
// No default handler
if (prot->handler == 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

View File

@ -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();
}

View File

@ -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;

View File

@ -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 <netinet/in.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netdb.h>
#include <errno.h>
#endif
#include <map>
#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<uint16, uint32> 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<len; i++) {
bug("%02x ", packet[i]);
}
bug("\n");
#endif
// Send packet
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = htonl(dest_ip);
sa.sin_port = htons(udp_port);
if (sendto(udp_socket, packet, len, 0, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
D(bug("WARNING: Couldn't transmit packet\n"));
return excessCollsns;
}
} else
#endif
return ether_write(wds);
}
return noErr;
}
case kENetGetInfo: { // Get device information/statistics
D(bug("GetInfo buf %08lx, size %d\n", ReadMacInt32(pb + ePointer), ReadMacInt16(pb + eBuffSize)));
D(bug(" GetInfo buf %08x, size %d\n", ReadMacInt32(pb + ePointer), ReadMacInt16(pb + eBuffSize)));
// Collect info (only ethernet address)
uint8 buf[18];
@ -157,7 +342,7 @@ int16 EtherControl(uint32 pb, uint32 dce)
}
case kENetSetGeneral: // Set general mode (always in general mode)
D(bug("SetGeneral\n"));
D(bug(" SetGeneral\n"));
return noErr;
default:
@ -173,7 +358,7 @@ int16 EtherControl(uint32 pb, uint32 dce)
void EtherReadPacket(uint8 **src, uint32 &dest, uint32 &len, uint32 &remaining)
{
D(bug("EtherReadPacket src %08lx, dest %08lx, len %08lx, remaining %08lx\n", *src, dest, len, remaining));
D(bug("EtherReadPacket src %p, dest %08x, len %08x, remaining %08x\n", *src, dest, len, remaining));
uint32 todo = len > 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<length; i++) {
bug("%02x ", packet[i]);
}
bug("\n");
#endif
// Get packet type
uint16 type = (packet[12] << 8) | packet[13];
// Look for protocol
uint16 search_type = (type <= 1500 ? 0 : type);
if (udp_protocols.find(search_type) == udp_protocols.end())
return;
uint32 handler = udp_protocols[search_type];
if (handler == 0)
return;
// Copy header to RHA
Host2Mac_memcpy(ether_data + ed_RHA, packet, 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)packet + 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", handler, r.d[0], r.d[1], r.a[0], r.a[3], r.a[4]));
Execute68k(handler, &r);
}
#endif

View File

@ -21,24 +21,31 @@
#ifndef ETHER_H
#define ETHER_H
struct sockaddr_in;
extern void EtherInit(void);
extern void EtherExit(void);
extern int16 EtherOpen(uint32 pb, uint32 dce);
extern int16 EtherControl(uint32 pb, uint32 dce);
extern void EtherReadPacket(uint8 **src, uint32 &dest, uint32 &len, uint32 &remaining);
// System specific and internal functions/data
extern void EtherInit(void);
extern void EtherExit(void);
extern void EtherReset(void);
extern void EtherInterrupt(void);
extern bool ether_init(void);
extern void ether_exit(void);
extern int16 ether_add_multicast(uint32 pb);
extern int16 ether_del_multicast(uint32 pb);
extern int16 ether_attach_ph(uint16 type, uint32 handler);
extern int16 ether_detach_ph(uint16 type);
extern int16 ether_write(uint32 wds);
extern bool ether_start_udp_thread(int socket_fd);
extern void ether_stop_udp_thread(void);
extern void ether_udp_read(uint8 *packet, int length, struct sockaddr_in *from);
extern uint8 ether_addr[6]; // Ethernet address (set by EtherInit())
extern bool net_open; // Flag: initialization succeeded, network device open (set by EtherInit())
extern uint8 ether_addr[6]; // Ethernet address (set by ether_init())
// Ethernet driver data in MacOS RAM
enum {
@ -53,4 +60,21 @@ enum {
extern uint32 ether_data; // Mac address of driver data in MacOS RAM
// Copy packet data from WDS to linear buffer (must hold at least 1514 bytes),
// returns packet length
static inline int ether_wds_to_buffer(uint32 wds, uint8 *p)
{
int len = 0;
while (len < 1514) {
int w = ReadMacInt16(wds);
if (w == 0)
break;
Mac2Host_memcpy(p, ReadMacInt32(wds + 2), w);
len += w;
p += w;
wds += 6;
}
return len;
}
#endif

View File

@ -169,6 +169,8 @@ enum {
STR_ISPAR_CTRL,
STR_ETHER_ENABLE_CTRL,
STR_ETHERNET_IF_CTRL,
STR_UDPTUNNEL_CTRL,
STR_UDPPORT_CTRL,
STR_MEMORY_MISC_PANE_TITLE = 3600, // Memory/Misc pane
STR_RAMSIZE_SLIDER,

View File

@ -43,6 +43,8 @@ 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"},
{"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"},
{"bootdrive", TYPE_INT32, false, "boot drive number"},
{"bootdriver", TYPE_INT32, false, "boot driver number"},
@ -66,6 +68,8 @@ prefs_desc common_prefs_items[] = {
void AddPrefsDefaults(void)
{
SysAddSerialPrefs();
PrefsAddBool("udptunnel", false);
PrefsAddInt32("udpport", 6066);
PrefsAddInt32("bootdriver", 0);
PrefsAddInt32("bootdrive", 0);
PrefsAddInt32("ramsize", 8 * 1024 * 1024);

View File

@ -184,6 +184,8 @@ user_string_def common_strings[] = {
{STR_ISPAR_CTRL, "Parallel Device"},
{STR_ETHER_ENABLE_CTRL, "Enable Ethernet"},
{STR_ETHERNET_IF_CTRL, "Ethernet Interface"},
{STR_UDPTUNNEL_CTRL, "Tunnel AppleTalk over UDP"},
{STR_UDPPORT_CTRL, "UDP Port Number"},
{STR_MEMORY_MISC_PANE_TITLE, "Memory/Misc"},
{STR_RAMSIZE_SLIDER, "MacOS RAM Size:"},