macemu/BasiliskII/src/Unix/ether_unix.cpp
David O'Shea 385b49ba8e Avoid an all-zero Ethernet/MAC address when using TUN/TAP (fixes #154).
Previously, when "ether tun" configuration was used on a Linux host,
packets were sent with Ethernet/MAC address 00:00:00:00:00:00.  Under
CentOS 7 at least, this did not appear to cause any issues where the
tun interface on the host was configured with an IP address, but when
an attempt was made to bridge the tun interface, it was no longer
possible to establish IP communication between the emulated machine
and either the Linux host or a Windows NT Server 4 VM.

This fix causes an Ethernet/MAC address to be generated in the same
way when using TUN/TAP as is done for ethertap.
2017-12-28 22:49:01 +10:30

1052 lines
23 KiB
C++

/*
* ether_unix.cpp - Ethernet device driver, Unix specific stuff (Linux and FreeBSD)
*
* Basilisk II (C) 1997-2008 Christian Bauer
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "sysdeps.h"
/*
* NOTES concerning MacOS X issues:
* - poll() does not exist in 10.2.8, but is available in 10.4.4
* - select(), and very likely poll(), are not cancellation points. So
* the ethernet thread doesn't stop on exit. An explicit check is
* performed to workaround this problem.
*/
#if (defined __APPLE__ && defined __MACH__) || ! defined HAVE_POLL
#define USE_POLL 0
#else
#define USE_POLL 1
#endif
// Define to let the slirp library determine the right timeout for select()
#define USE_SLIRP_TIMEOUT 1
#ifdef HAVE_SYS_POLL_H
#include <sys/poll.h>
#endif
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <map>
#if defined(__FreeBSD__) || defined(sgi) || (defined(__APPLE__) && defined(__MACH__))
#include <net/if.h>
#endif
#if defined(HAVE_LINUX_IF_H) && defined(HAVE_LINUX_IF_TUN_H)
#include <linux/if.h>
#include <linux/if_tun.h>
#endif
#if defined(HAVE_NET_IF_H) && defined(HAVE_NET_IF_TUN_H)
#include <net/if.h>
#include <net/if_tun.h>
#endif
#ifdef HAVE_SLIRP
#include "libslirp.h"
#include "ctl.h"
#endif
#include "cpu_emulation.h"
#include "main.h"
#include "macos_util.h"
#include "prefs.h"
#include "user_strings.h"
#include "ether.h"
#include "ether_defs.h"
#ifndef NO_STD_NAMESPACE
using std::map;
#endif
#define DEBUG 0
#include "debug.h"
#define STATISTICS 0
#define MONITOR 0
// Ethernet device types
enum {
NET_IF_SHEEPNET,
NET_IF_ETHERTAP,
NET_IF_TUNTAP,
NET_IF_SLIRP
};
// Constants
#if ENABLE_TUNTAP
static const char ETHERCONFIG_FILE_NAME[] = DATADIR "/tunconfig";
#endif
// 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 udp_tunnel; // Flag: UDP tunnelling active, fd is the socket descriptor
static int net_if_type = -1; // Ethernet device type
static char *net_if_name = NULL; // TUN/TAP device name
static const char *net_if_script = NULL; // Network config script
static pthread_t slirp_thread; // Slirp reception thread
static bool slirp_thread_active = false; // Flag: Slirp reception threadinstalled
static int slirp_output_fd = -1; // fd of slirp output pipe
static int slirp_input_fds[2] = { -1, -1 }; // fds of slirp input pipe
#ifdef SHEEPSHAVER
static bool net_open = false; // Flag: initialization succeeded, network device open
static uint8 ether_addr[6]; // Our Ethernet address
#else
const bool ether_driver_opened = true; // Flag: is the MacOS driver opened?
#endif
// Attached network protocols, maps protocol type to MacOS handler address
static map<uint16, uint32> net_protocols;
// Prototypes
static void *receive_func(void *arg);
static void *slirp_receive_func(void *arg);
static int16 ether_do_add_multicast(uint8 *addr);
static int16 ether_do_del_multicast(uint8 *addr);
static int16 ether_do_write(uint32 arg);
static void ether_do_interrupt(void);
static void slirp_add_redirs();
static int slirp_add_redir(const char *redir_str);
/*
* Start packet reception thread
*/
static bool start_thread(void)
{
if (sem_init(&int_ack, 0, 0) < 0) {
printf("WARNING: Cannot init semaphore");
return false;
}
Set_pthread_attr(&ether_thread_attr, 1);
thread_active = (pthread_create(&ether_thread, &ether_thread_attr, receive_func, NULL) == 0);
if (!thread_active) {
printf("WARNING: Cannot start Ethernet thread");
return false;
}
#ifdef HAVE_SLIRP
if (net_if_type == NET_IF_SLIRP) {
slirp_thread_active = (pthread_create(&slirp_thread, NULL, slirp_receive_func, NULL) == 0);
if (!slirp_thread_active) {
printf("WARNING: Cannot start slirp reception thread\n");
return false;
}
}
#endif
return true;
}
/*
* Stop packet reception thread
*/
static void stop_thread(void)
{
#ifdef HAVE_SLIRP
if (slirp_thread_active) {
#ifdef HAVE_PTHREAD_CANCEL
pthread_cancel(slirp_thread);
#endif
pthread_join(slirp_thread, NULL);
slirp_thread_active = false;
}
#endif
if (thread_active) {
#ifdef HAVE_PTHREAD_CANCEL
pthread_cancel(ether_thread);
#endif
pthread_join(ether_thread, NULL);
sem_destroy(&int_ack);
thread_active = false;
}
}
/*
* 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] = 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
*/
bool ether_init(void)
{
int val;
char str[256];
// Do nothing if no Ethernet device specified
const char *name = PrefsFindString("ether");
if (name == NULL)
return false;
// 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
#ifdef HAVE_SLIRP
else if (strcmp(name, "slirp") == 0)
net_if_type = NET_IF_SLIRP;
#endif
else
net_if_type = NET_IF_SHEEPNET;
// Don't raise SIGPIPE, let errno be set to EPIPE
struct sigaction sigpipe_sa;
if (sigaction(SIGPIPE, NULL, &sigpipe_sa) == 0) {
assert(sigpipe_sa.sa_handler == SIG_DFL || sigpipe_sa.sa_handler == SIG_IGN);
sigfillset(&sigpipe_sa.sa_mask);
sigpipe_sa.sa_flags = 0;
sigpipe_sa.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sigpipe_sa, NULL);
}
#ifdef HAVE_SLIRP
// Initialize slirp library
if (net_if_type == NET_IF_SLIRP) {
if (slirp_init() < 0) {
sprintf(str, "%s", GetString(STR_SLIRP_NO_DNS_FOUND_WARN));
WarningAlert(str);
return false;
}
// Open slirp output pipe
int fds[2];
if (pipe(fds) < 0)
return false;
fd = fds[0];
slirp_output_fd = fds[1];
// Open slirp input pipe
if (pipe(slirp_input_fds) < 0)
return false;
// Set up port redirects
slirp_add_redirs();
}
#endif
// Open sheep_net or ethertap or TUN/TAP device
char dev_name[16];
switch (net_if_type) {
case NET_IF_ETHERTAP:
sprintf(dev_name, "/dev/%s", name);
break;
case NET_IF_TUNTAP:
strcpy(dev_name, "/dev/net/tun");
break;
case NET_IF_SHEEPNET:
strcpy(dev_name, "/dev/sheep_net");
break;
}
if (net_if_type != NET_IF_SLIRP) {
fd = open(dev_name, O_RDWR);
if (fd < 0) {
sprintf(str, GetString(STR_NO_SHEEP_NET_DRIVER_WARN), dev_name, strerror(errno));
WarningAlert(str);
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 = strdup(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 (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;
}
#endif
// Set nonblocking I/O
#ifdef USE_FIONBIO
int nonblock = 1;
if (ioctl(fd, FIONBIO, &nonblock) < 0) {
sprintf(str, GetString(STR_BLOCKING_NET_SOCKET_WARN), strerror(errno));
WarningAlert(str);
goto open_error;
}
#else
val = fcntl(fd, F_GETFL, 0);
if (val < 0 || fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) {
sprintf(str, GetString(STR_BLOCKING_NET_SOCKET_WARN), strerror(errno));
WarningAlert(str);
goto open_error;
}
#endif
// Get Ethernet address
if (net_if_type == NET_IF_ETHERTAP || net_if_type == NET_IF_TUNTAP) {
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;
ether_addr[2] = p >> 24;
ether_addr[3] = p >> 16;
ether_addr[4] = p >> 8;
ether_addr[5] = p;
#ifdef HAVE_SLIRP
} else if (net_if_type == NET_IF_SLIRP) {
ether_addr[0] = 0x52;
ether_addr[1] = 0x54;
ether_addr[2] = 0x00;
ether_addr[3] = 0x12;
ether_addr[4] = 0x34;
ether_addr[5] = 0x56;
#endif
} else
ioctl(fd, SIOCGIFADDR, ether_addr);
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 (!start_thread())
goto open_error;
// Everything OK
return true;
open_error:
stop_thread();
if (fd > 0) {
close(fd);
fd = -1;
}
if (slirp_input_fds[0] >= 0) {
close(slirp_input_fds[0]);
slirp_input_fds[0] = -1;
}
if (slirp_input_fds[1] >= 0) {
close(slirp_input_fds[1]);
slirp_input_fds[1] = -1;
}
if (slirp_output_fd >= 0) {
close(slirp_output_fd);
slirp_output_fd = -1;
}
return false;
}
/*
* Deinitialization
*/
void ether_exit(void)
{
// Stop reception threads
stop_thread();
// Shut down TUN/TAP interface
if (net_if_type == NET_IF_TUNTAP)
execute_network_script("down");
// Free TUN/TAP device name
if (net_if_name)
free(net_if_name);
// Close sheep_net device
if (fd > 0)
close(fd);
// Close slirp input buffer
if (slirp_input_fds[0] >= 0)
close(slirp_input_fds[0]);
if (slirp_input_fds[1] >= 0)
close(slirp_input_fds[1]);
// Close slirp output buffer
if (slirp_output_fd > 0)
close(slirp_output_fd);
#if STATISTICS
// Show statistics
printf("%ld messages put on write queue\n", num_wput);
printf("%ld error acks\n", num_error_acks);
printf("%ld packets transmitted (%ld raw, %ld normal)\n", num_tx_packets, num_tx_raw_packets, num_tx_normal_packets);
printf("%ld tx packets dropped because buffer full\n", num_tx_buffer_full);
printf("%ld packets received\n", num_rx_packets);
printf("%ld packets passed upstream (%ld Fast Path, %ld normal)\n", num_rx_fastpath + num_unitdata_ind, num_rx_fastpath, num_unitdata_ind);
printf("EtherIRQ called %ld times\n", num_ether_irq);
printf("%ld rx packets dropped due to low memory\n", num_rx_no_mem);
printf("%ld rx packets dropped because no stream found\n", num_rx_dropped);
printf("%ld rx packets dropped because stream not ready\n", num_rx_stream_not_ready);
printf("%ld rx packets dropped because no memory for unitdata_ind\n", num_rx_no_unitdata_mem);
#endif
}
/*
* Glue around low-level implementation
*/
#ifdef SHEEPSHAVER
// Error codes
enum {
eMultiErr = -91,
eLenErr = -92,
lapProtErr = -94,
excessCollsns = -95
};
// Initialize ethernet
void EtherInit(void)
{
net_open = false;
// Do nothing if the user disabled the network
if (PrefsFindBool("nonet"))
return;
net_open = ether_init();
}
// Exit ethernet
void EtherExit(void)
{
ether_exit();
net_open = false;
}
// Get ethernet hardware address
void AO_get_ethernet_address(uint32 arg)
{
uint8 *addr = Mac2HostAddr(arg);
if (net_open)
OTCopy48BitAddress(ether_addr, addr);
else {
addr[0] = 0x12;
addr[1] = 0x34;
addr[2] = 0x56;
addr[3] = 0x78;
addr[4] = 0x9a;
addr[5] = 0xbc;
}
D(bug("AO_get_ethernet_address: got address %02x%02x%02x%02x%02x%02x\n", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]));
}
// Add multicast address
void AO_enable_multicast(uint32 addr)
{
if (net_open)
ether_do_add_multicast(Mac2HostAddr(addr));
}
// Disable multicast address
void AO_disable_multicast(uint32 addr)
{
if (net_open)
ether_do_del_multicast(Mac2HostAddr(addr));
}
// Transmit one packet
void AO_transmit_packet(uint32 mp)
{
if (net_open) {
switch (ether_do_write(mp)) {
case noErr:
num_tx_packets++;
break;
case excessCollsns:
num_tx_buffer_full++;
break;
}
}
}
// Copy packet data from message block to linear buffer
static inline int ether_arg_to_buffer(uint32 mp, uint8 *p)
{
return ether_msgb_to_buffer(mp, p);
}
// Ethernet interrupt
void EtherIRQ(void)
{
D(bug("EtherIRQ\n"));
num_ether_irq++;
OTEnterInterrupt();
ether_do_interrupt();
OTLeaveInterrupt();
// Acknowledge interrupt to reception thread
D(bug(" EtherIRQ done\n"));
sem_post(&int_ack);
}
#else
// Add multicast address
int16 ether_add_multicast(uint32 pb)
{
return ether_do_add_multicast(Mac2HostAddr(pb + eMultiAddr));
}
// Disable multicast address
int16 ether_del_multicast(uint32 pb)
{
return ether_do_del_multicast(Mac2HostAddr(pb + eMultiAddr));
}
// Transmit one packet
int16 ether_write(uint32 wds)
{
return ether_do_write(wds);
}
// Copy packet data from WDS to linear buffer
static inline int ether_arg_to_buffer(uint32 wds, uint8 *p)
{
return ether_wds_to_buffer(wds, p);
}
// Dispatch packet to protocol handler
static void ether_dispatch_packet(uint32 p, uint32 length)
{
// Get packet type
uint16 type = ReadMacInt16(p + 12);
// Look for protocol
uint16 search_type = (type <= 1500 ? 0 : type);
if (net_protocols.find(search_type) == net_protocols.end())
return;
uint32 handler = net_protocols[search_type];
// No default handler
if (handler == 0)
return;
// Copy header to RHA
Mac2Mac_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] = p + 14; // Pointer to packet (Mac 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);
}
// Ethernet interrupt
void EtherInterrupt(void)
{
D(bug("EtherIRQ\n"));
ether_do_interrupt();
// Acknowledge interrupt to reception thread
D(bug(" EtherIRQ done\n"));
sem_post(&int_ack);
}
#endif
/*
* Reset
*/
void ether_reset(void)
{
net_protocols.clear();
}
/*
* Add multicast address
*/
static int16 ether_do_add_multicast(uint8 *addr)
{
switch (net_if_type) {
case NET_IF_ETHERTAP:
case NET_IF_SHEEPNET:
if (ioctl(fd, SIOCADDMULTI, addr) < 0) {
D(bug("WARNING: Couldn't enable multicast address\n"));
if (net_if_type == NET_IF_ETHERTAP) {
return noErr;
} else {
return eMultiErr;
}
}
return noErr;
default:
return noErr;
}
}
/*
* Delete multicast address
*/
static int16 ether_do_del_multicast(uint8 *addr)
{
switch (net_if_type) {
case NET_IF_ETHERTAP:
case NET_IF_SHEEPNET:
if (ioctl(fd, SIOCDELMULTI, addr) < 0) {
D(bug("WARNING: Couldn't disable multicast address\n"));
return eMultiErr;
}
return noErr;
default:
return noErr;
}
}
/*
* Attach protocol handler
*/
int16 ether_attach_ph(uint16 type, uint32 handler)
{
if (net_protocols.find(type) != net_protocols.end())
return lapProtErr;
net_protocols[type] = handler;
return noErr;
}
/*
* Detach protocol handler
*/
int16 ether_detach_ph(uint16 type)
{
if (net_protocols.erase(type) == 0)
return lapProtErr;
return noErr;
}
/*
* Transmit raw ethernet packet
*/
static int16 ether_do_write(uint32 arg)
{
// Copy packet to buffer
uint8 packet[1516], *p = packet;
int len = 0;
#if defined(__linux__)
if (net_if_type == NET_IF_ETHERTAP) {
*p++ = 0; // Linux ethertap discards the first 2 bytes
*p++ = 0;
len += 2;
}
#endif
len += ether_arg_to_buffer(arg, p);
#if MONITOR
bug("Sending Ethernet packet:\n");
for (int i=0; i<len; i++) {
bug("%02x ", packet[i]);
}
bug("\n");
#endif
// Transmit packet
#ifdef HAVE_SLIRP
if (net_if_type == NET_IF_SLIRP) {
const int slirp_input_fd = slirp_input_fds[1];
write(slirp_input_fd, &len, sizeof(len));
write(slirp_input_fd, packet, len);
return noErr;
} else
#endif
if (write(fd, packet, len) < 0) {
D(bug("WARNING: Couldn't transmit packet\n"));
return excessCollsns;
} else
return noErr;
}
/*
* 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;
}
/*
* SLIRP output buffer glue
*/
#ifdef HAVE_SLIRP
int slirp_can_output(void)
{
return 1;
}
void slirp_output(const uint8 *packet, int len)
{
write(slirp_output_fd, packet, len);
}
void *slirp_receive_func(void *arg)
{
const int slirp_input_fd = slirp_input_fds[0];
for (;;) {
// Wait for packets to arrive
fd_set rfds, wfds, xfds;
int nfds;
struct timeval tv;
// ... in the input queue
FD_ZERO(&rfds);
FD_SET(slirp_input_fd, &rfds);
tv.tv_sec = 0;
tv.tv_usec = 0;
if (select(slirp_input_fd + 1, &rfds, NULL, NULL, &tv) > 0) {
int len;
read(slirp_input_fd, &len, sizeof(len));
uint8 packet[1516];
assert(len <= sizeof(packet));
read(slirp_input_fd, packet, len);
slirp_input(packet, len);
}
// ... in the output queue
nfds = -1;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_ZERO(&xfds);
int timeout = slirp_select_fill(&nfds, &rfds, &wfds, &xfds);
#if ! USE_SLIRP_TIMEOUT
timeout = 10000;
#endif
tv.tv_sec = 0;
tv.tv_usec = timeout;
if (select(nfds + 1, &rfds, &wfds, &xfds, &tv) >= 0)
slirp_select_poll(&rfds, &wfds, &xfds);
#ifdef HAVE_PTHREAD_TESTCANCEL
// Explicit cancellation point if select() was not covered
// This seems to be the case on MacOS X 10.2
pthread_testcancel();
#endif
}
return NULL;
}
#else
int slirp_can_output(void)
{
return 0;
}
void slirp_output(const uint8 *packet, int len)
{
}
#endif
/*
* Packet reception thread
*/
static void *receive_func(void *arg)
{
for (;;) {
// Wait for packets to arrive
#if USE_POLL
struct pollfd pf = {fd, POLLIN, 0};
int res = poll(&pf, 1, -1);
#else
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
// A NULL timeout could cause select() to block indefinitely,
// even if it is supposed to be a cancellation point [MacOS X]
struct timeval tv = { 0, 20000 };
int res = select(fd + 1, &rfds, NULL, NULL, &tv);
#ifdef HAVE_PTHREAD_TESTCANCEL
pthread_testcancel();
#endif
if (res == 0 || (res == -1 && errno == EINTR))
continue;
#endif
if (res <= 0)
break;
if (ether_driver_opened) {
// Trigger Ethernet interrupt
D(bug(" packet received, triggering Ethernet interrupt\n"));
SetInterruptFlag(INTFLAG_ETHER);
TriggerInterrupt();
// Wait for interrupt acknowledge by EtherInterrupt()
sem_wait(&int_ack);
} else
Delay_usec(20000);
}
return NULL;
}
/*
* Ethernet interrupt - activate deferred tasks to call IODone or protocol handlers
*/
void ether_do_interrupt(void)
{
// Call protocol handler for received packets
EthernetPacket ether_packet;
uint32 packet = ether_packet.addr();
ssize_t length;
for (;;) {
#ifndef SHEEPSHAVER
if (udp_tunnel) {
// Read packet from socket
struct sockaddr_in from;
socklen_t from_len = sizeof(from);
length = recvfrom(fd, Mac2HostAddr(packet), 1514, 0, (struct sockaddr *)&from, &from_len);
if (length < 14)
break;
ether_udp_read(packet, length, &from);
} else
#endif
{
// Read packet from sheep_net device
#if defined(__linux__)
length = read(fd, Mac2HostAddr(packet), net_if_type == NET_IF_ETHERTAP ? 1516 : 1514);
#else
length = read(fd, Mac2HostAddr(packet), 1514);
#endif
if (length < 14)
break;
#if MONITOR
bug("Receiving Ethernet packet:\n");
for (int i=0; i<length; i++) {
bug("%02x ", ReadMacInt8(packet + i));
}
bug("\n");
#endif
// Pointer to packet data (Ethernet header)
uint32 p = packet;
#if defined(__linux__)
if (net_if_type == NET_IF_ETHERTAP) {
p += 2; // Linux ethertap has two random bytes before the packet
length -= 2;
}
#endif
// Dispatch packet
ether_dispatch_packet(p, length);
}
}
}
// Helper function for port forwarding
static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
{
const char *p, *p1;
int len;
p = *pp;
p1 = strchr(p, sep);
if (!p1)
return -1;
len = p1 - p;
p1++;
if (buf_size > 0) {
if (len > buf_size - 1)
len = buf_size - 1;
memcpy(buf, p, len);
buf[len] = '\0';
}
*pp = p1;
return 0;
}
// Set up port forwarding for slirp
static void slirp_add_redirs()
{
int index = 0;
const char *str;
while ((str = PrefsFindString("redir", index++)) != NULL) {
slirp_add_redir(str);
}
}
// Add a port forward/redirection for slirp
static int slirp_add_redir(const char *redir_str)
{
// code adapted from qemu source
struct in_addr guest_addr = {0};
int host_port, guest_port;
const char *p;
char buf[256];
int is_udp;
char *end;
char str[256];
p = redir_str;
if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
goto fail_syntax;
}
if (!strcmp(buf, "tcp") || buf[0] == '\0') {
is_udp = 0;
} else if (!strcmp(buf, "udp")) {
is_udp = 1;
} else {
goto fail_syntax;
}
if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
goto fail_syntax;
}
host_port = strtol(buf, &end, 0);
if (*end != '\0' || host_port < 1 || host_port > 65535) {
goto fail_syntax;
}
if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
goto fail_syntax;
}
// 0.0.0.0 doesn't seem to work, so default to a client address
// if none is specified
if (buf[0] == '\0' ?
!inet_aton(CTL_LOCAL, &guest_addr) :
!inet_aton(buf, &guest_addr)) {
goto fail_syntax;
}
guest_port = strtol(p, &end, 0);
if (*end != '\0' || guest_port < 1 || guest_port > 65535) {
goto fail_syntax;
}
if (slirp_redir(is_udp, host_port, guest_addr, guest_port) < 0) {
sprintf(str, "could not set up host forwarding rule '%s'", redir_str);
WarningAlert(str);
return -1;
}
return 0;
fail_syntax:
sprintf(str, "invalid host forwarding rule '%s'", redir_str);
WarningAlert(str);
return -1;
}