/* * 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 #endif #ifdef __sun__ #define BSD_COMP 1 #endif #include #include #ifdef ENABLE_MACOSX_ETHERHELPER #ifdef __APPLE__ #include #endif #ifdef __linux__ #include #endif #include #include #endif #include #include #include #include #include #include #include #include #include #if defined(__FreeBSD__) || defined (__sun__) || defined(sgi) || (defined(__APPLE__) && defined(__MACH__)) #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 #ifdef HAVE_SLIRP #include "libslirp.h" #include "ctl.h" #endif #ifdef HAVE_LIBVDEPLUG extern "C" { #include } #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, NET_IF_VDE, NET_IF_ETHERHELPER }; #ifdef ENABLE_MACOSX_ETHERHELPER extern "C" { extern FILE * run_tool(const char *if_name, const char *tool_name); } #endif // 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 HAVE_LIBVDEPLUG static VDECONN *vde_conn; #endif #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 #ifdef ENABLE_MACOSX_ETHERHELPER static uint8 packet_buffer[2048]; #endif // Attached network protocols, maps protocol type to MacOS handler address static map 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); #ifdef ENABLE_MACOSX_ETHERHELPER static int get_mac_address(const char* dev, unsigned char *addr); static bool open_ether_helper(const std::string &if_name); static int read_packet(void); #endif /* * 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(ðer_thread_attr, 1); thread_active = (pthread_create(ðer_thread, ðer_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"); #ifdef ENABLE_MACOSX_ETHERHELPER std::string slave_dev; #endif 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; printf("selected Ethernet device type tap\n"); } #if ENABLE_TUNTAP else if (strcmp(name, "tun") == 0) { net_if_type = NET_IF_TUNTAP; printf("selected Ethernet device type tun\n"); } #endif #ifdef HAVE_SLIRP else if (strcmp(name, "slirp") == 0) { net_if_type = NET_IF_SLIRP; printf("selected Ethernet device type slirp\n"); } #endif #ifdef HAVE_LIBVDEPLUG else if (strcmp(name, "vde") == 0) { net_if_type = NET_IF_VDE; printf("selected Ethernet device type VDE\n"); } #endif #ifdef ENABLE_MACOSX_ETHERHELPER else if (strncmp(name, "etherhelper", 10) == 0) net_if_type = NET_IF_ETHERHELPER; #endif else { net_if_type = NET_IF_SHEEPNET; printf("selected Ethernet device type sheep_net\n"); } // 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; #ifdef ENABLE_MACOSX_ETHERHELPER case NET_IF_ETHERHELPER: { std::string device(name); size_t pos; pos = device.find('/'); if(pos != device.npos) { slave_dev = device.substr(pos + 1); } if(slave_dev.size() == 0) { WarningAlert("No network device specified."); return false; } return open_ether_helper(slave_dev); } #endif } #ifdef HAVE_LIBVDEPLUG //vde switch information int port = 0; char *init_group = NULL; mode_t mode = 0700; struct vde_open_args args = { .port = port, .group = init_group, .mode = mode, }; if (net_if_type == NET_IF_VDE) { /* calling vde open to open the vde connection to the vde switch */ vde_conn = vde_open(vde_sock, (char *)"macemu", &args); if (!vde_conn) { D(bug("VDE open failed\n")); return -1; } else { /* for select/poll when this fd receive data, there are * packets to recv(call vde_recv) */ fd = vde_datafd(vde_conn); } } #endif if (net_if_type != NET_IF_SLIRP && net_if_type != NET_IF_VDE) { 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 #ifdef HAVE_LIBVDEPLUG } else if (net_if_type == NET_IF_VDE) { 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); #ifdef HAVE_LIBVDEPLUG // Close vde_connection if (net_if_type == NET_IF_VDE) vde_close(vde_conn); #endif #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 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; #ifdef ENABLE_MACOSX_ETHERHELPER if (net_if_type == NET_IF_ETHERHELPER) { if (read_packet() < 1) { break; } } #endif 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 #ifdef ENABLE_MACOSX_ETHERHELPER if (net_if_type == NET_IF_ETHERHELPER) { unsigned short *pkt_len; uint32 p = packet; pkt_len = (unsigned short *)packet_buffer; length = *pkt_len; memcpy(Mac2HostAddr(packet), pkt_len + 1, length); ether_dispatch_packet(p, length); break; } else #endif { #ifdef HAVE_LIBVDEPLUG if (net_if_type == NET_IF_VDE) { length = vde_recv(vde_conn, Mac2HostAddr(packet), 1514, 0); } 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 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; } #ifdef ENABLE_MACOSX_ETHERHELPER static int get_mac_address(const char* dev, unsigned char *addr) { struct ifaddrs *ifaddrs, *next; int ret = -1; #ifdef __APPLE__ struct sockaddr_dl *sa; #endif #ifdef __linux__ struct sockaddr_ll *sa; #endif if (getifaddrs(&ifaddrs) != 0) { perror("getifaddrs"); return -1; } next = ifaddrs; while (next != NULL) { switch (next->ifa_addr->sa_family) { #ifdef __APPLE__ case AF_LINK: if (!strcmp(dev, next->ifa_name)) { sa = (struct sockaddr_dl *)next->ifa_addr; memcpy(addr, LLADDR(sa), 6); ret = 0; } break; #endif #ifdef __linux__ case AF_PACKET: if (!strcmp(dev, next->ifa_name)) { sa = (struct sockaddr_ll *)next->ifa_addr; memcpy(addr, sa->sll_addr, 6); ret = 0; } break; #endif default: break; } next = next->ifa_next; } freeifaddrs(ifaddrs); return ret; } static bool open_ether_helper(const std::string &if_name) { FILE *fp; char str[64]; std::string dev_name; size_t pos; fp = run_tool(if_name.c_str(), "etherhelpertool"); if (fp == NULL) { snprintf(str, sizeof(str), "Unable to run ether helper helper tool."); WarningAlert(str); return false; } pos = if_name.find('/'); dev_name = if_name; if(pos != if_name.npos) { dev_name.erase(pos); } if(strncmp(if_name.c_str(), "tap", 3) != 0) { if (get_mac_address(dev_name.c_str(), ether_addr) != 0) { snprintf(str, sizeof(str), "Unable to find interface %s.", dev_name.c_str()); WarningAlert(str); return false; } } else { /* There is something special about this address. */ pid_t p = getpid(); 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; } fd = dup(fileno(fp)); fclose(fp); if (start_thread() == false) { close(fd); fd = -1; return false; } return true; } static int read_packet() { int index; unsigned short *pkt_len; int ret = -1; pkt_len = (unsigned short *)packet_buffer; index = 0; while (1) { if (index < 2) { ret = read(fd, packet_buffer + index, 2 - index); } else { ret = read(fd, packet_buffer + index, *pkt_len - index + 2); } if (ret < 1) { fprintf(stderr, "%s: read() returned %d.\n", __func__, ret); break; } index += ret; if (index > 1) { if (*pkt_len > (sizeof(packet_buffer) + 2)) { fprintf(stderr, "%s: pkt_len (%d) too large.\n", __func__, *pkt_len); break; } if (index == (*pkt_len + 2)) { ret = *pkt_len; break; } } } return ret; } #endif