diff --git a/BasiliskII/src/MacOSX/etherhelpertool.c b/BasiliskII/src/MacOSX/etherhelpertool.c new file mode 100644 index 00000000..392f871a --- /dev/null +++ b/BasiliskII/src/MacOSX/etherhelpertool.c @@ -0,0 +1,578 @@ +/* + * etherhelpertool.c - Reads and writes raw ethernet packets usng bpf + * interface. + * + * Copyright (C) 2010, Daniel Sumorok + * + * 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 +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#define STR_MAX 256 +#define MAX_ARGV 10 + +static int open_bpf(char *ifname); +static int open_tap(char *ifname); +static int retreive_auth_info(void); +static int main_loop(int sd, int use_bpf); +static int run_cmd(const char *cmd); +static void handler(int signum); +static int install_signal_handlers(); +static void do_exit(); + +static int remove_bridge = 0; +static const char *exec_name = "etherhelpertool"; + +int main(int argc, char **argv) +{ + char *if_name; + int ret = 255; + int sd; + int tapNum; + int use_bpf; + + if (argc != 2) { + return 255; + } + + if_name = argv[1]; + + do { + ret = retreive_auth_info(); + if (ret != 0) { + fprintf(stderr, "%s: authorization failed.\n", + exec_name); + ret = 254; + break; + } + + if (strncmp(if_name, "tap", 3) == 0) { + sd = open_tap(if_name); + use_bpf = 0; + } else { + sd = open_bpf(if_name); + use_bpf = 1; + } + + if (sd < 0) { + fprintf(stderr, "%s: open device failed.\n", + exec_name); + ret = 253; + break; + } + + if (install_signal_handlers() != 0) { + fprintf(stderr, + "%s: failed to install signal handers.\n", + exec_name); + ret = 252; + break; + } + + ret = main_loop(sd, use_bpf); + close(sd); + } while (0); + + do_exit(); + + return ret; +} + +static int main_loop(int sd, int use_bpf) +{ + fd_set readSet; + char *outgoing, *incoming; + unsigned short *out_len; + unsigned short *in_len; + int in_index, out_index; + u_int blen = 0; + int ret; + int fret = 0; + struct bpf_hdr *hdr; + int pkt_len; + int frame_len; + int pad; + char c = 0; + + if (use_bpf) { + if (ioctl(sd, BIOCGBLEN, &blen) < 0) { + fprintf(stderr, + "%s: ioctl() failed.\n", + exec_name); + return -1; + } + } else { + blen = 4096; + } + + incoming = malloc(blen); + if (incoming == NULL) { + fprintf(stderr, + "%s: malloc() failed.\n", + exec_name); + return -2; + } + + outgoing = malloc(blen); + if (outgoing == NULL) { + free(outgoing); + fprintf(stderr, + "%s: malloc() failed.\n", + exec_name); + return -3; + } + + in_index = 0; + out_index = 0; + + out_len = (unsigned short *)outgoing; + + /* Let our parent know we are ready for business. */ + write(0, &c, 1); + + while (1) { + int i; + FD_ZERO(&readSet); + FD_SET(0, &readSet); + FD_SET(sd, &readSet); + + ret = select(sd + 1, &readSet, NULL, NULL, NULL); + if (ret < 0) { + fprintf(stderr, + "%s: select() failed.\n", + exec_name); + fret = -4; + break; + } + + if (FD_ISSET(0, &readSet)) { + if (out_index < 2) { + ret = read(0, outgoing + out_index, 2-out_index); + } else { + ret = read(0, outgoing + out_index, *out_len - out_index + 2); + } + + if (ret < 1) { + if(ret < 0) { + fprintf(stderr, + "%s: read() failed.\n", + exec_name); + } + fret = -5; + break; + } + + out_index += ret; + if (out_index > 1) { + if ((*out_len + 2) > blen) { + fret = -6; + break; + } + + if (out_index == (*out_len + 2)) { + ret = write(sd, out_len + 1, *out_len); + if (ret != *out_len) { + fprintf(stderr, + "%s: write() failed.\n", + exec_name); + fret = -7; + break; + } + out_index = 0; + } + } + + } + + if (use_bpf && FD_ISSET(sd, &readSet)) { + int i; + + ret = read(sd, incoming, blen); + if (ret < 1) { + if(ret < 0) { + fprintf(stderr, + "%s: read() failed %d.\n", + exec_name, errno); + } + fret = -8; + break; + } + + hdr = (struct bpf_hdr *)incoming; + in_len = (unsigned short *)(incoming + 16); + + do { + pkt_len = hdr->bh_caplen; + frame_len = pkt_len + 18; + + if ((pkt_len < 0) || (frame_len > ret) || (frame_len < 0)) { + fret = -9; + break; + } + *in_len = pkt_len; + + if (write(0, in_len, pkt_len + 2) < (pkt_len + 2)) { + fret = -10; + break; + } + + if ((frame_len & 0x03) == 0) { + pad = 0; + } else { + pad = 4 - (frame_len & 0x03); + } + + ret -= (frame_len + pad); + hdr = (struct bpf_hdr *)((unsigned char *)hdr + frame_len + pad); + in_len = (unsigned short *)((unsigned char *)hdr + 16); + } while (ret > 0); + + if (fret != 0) { + fprintf(stderr, + "%s: fret == %d.\n", + exec_name, fret); + break; + } + } + + if (!use_bpf && FD_ISSET(sd, &readSet)) { + in_len = (unsigned short *)incoming; + + pkt_len = read(sd, in_len + 1, blen-2); + if (pkt_len < 14) { + fprintf(stderr, + "%s: read() returned %d.\n", + exec_name, pkt_len); + fret = -8; + break; + } + + *in_len = pkt_len; + if (write(0, in_len, pkt_len + 2) < (pkt_len + 2)) { + fprintf(stderr, + "%s: write() failed\n", + exec_name); + fret = -10; + break; + } + + } + + } + + free(incoming); + free(outgoing); + + return fret; +} + +static int retreive_auth_info(void) +{ + AuthorizationRef aRef; + OSStatus status; + AuthorizationRights myRights; + AuthorizationRights *newRights; + AuthorizationItem *myItem; + AuthorizationItem myItems[1]; + AuthorizationItemSet *mySet; + int i; + + status = AuthorizationCopyPrivilegedReference(&aRef, kAuthorizationFlagDefaults); + if (status != errAuthorizationSuccess) { + return -1; + } + + status = AuthorizationCopyInfo(aRef, NULL, &mySet); + if (status != errAuthorizationSuccess) { + AuthorizationFree(aRef, kAuthorizationFlagDestroyRights); + return -1; + } + + myItems[0].name = "system.privilege.admin"; + myItems[0].valueLength = 0; + myItems[0].value = NULL; + myItems[0].flags = 0; + + myRights.count = sizeof (myItems) / sizeof (myItems[0]); + myRights.items = myItems; + + status = AuthorizationCopyRights(aRef, &myRights, NULL, + kAuthorizationFlagExtendRights, + &newRights); + if (status != errAuthorizationSuccess) { + AuthorizationFreeItemSet(mySet); + AuthorizationFree(aRef, kAuthorizationFlagDestroyRights); + return -2; + } + + AuthorizationFreeItemSet(newRights); + AuthorizationFreeItemSet(mySet); + AuthorizationFree(aRef, kAuthorizationFlagDestroyRights); + + return 0; +} + +static int open_tap(char *ifname) +{ + char str[STR_MAX] = {0}; + char ifstr[STR_MAX] = {0}; + char *interface; + char *address = NULL; + char *netmask = NULL; + char *bridge = NULL; + char *bridged_if = NULL; + int sd; + + snprintf(ifstr, STR_MAX, "%s", ifname); + interface = strtok(ifstr, "/"); + bridge = strtok(NULL, "/"); + if (bridge != NULL) { + bridged_if = strtok(NULL, "/"); + } + interface = strtok(ifstr, ":"); + + address = strtok(NULL, ":"); + + if (address != NULL) { + netmask = strtok(NULL, ":"); + } + + snprintf(str, STR_MAX, "/dev/%s", interface); + + sd = open(str, O_RDWR); + if (sd < 0) { + fprintf(stderr, "%s: Failed to open %s\n", + exec_name, interface); + return -1; + } + + if (address == NULL) { + snprintf(str, STR_MAX, "/sbin/ifconfig %s up", interface); + } else if (netmask == NULL) { + snprintf(str, STR_MAX, "/sbin/ifconfig %s %s", + interface, address); + } else { + snprintf(str, STR_MAX, "/sbin/ifconfig %s %s netmask %s", + interface, address, netmask); + } + + if (run_cmd(str) != 0) { + fprintf(stderr, "%s: Failed to configure %s\n", + exec_name, interface); + close(sd); + return -1; + } + + if (bridge != NULL) { + /* Check to see if bridge is alread up */ + snprintf(str, STR_MAX, "/sbin/ifconfig %s", bridge); + if (run_cmd(str) == 0) { + /* bridge is already up */ + if (bridged_if != NULL) { + fprintf(stderr, "%s: Warning: %s already exists, so %s was not added.\n", + exec_name, bridge, bridged_if); + } + } else { + snprintf(str, STR_MAX, "/sbin/ifconfig %s create", bridge); + if (run_cmd(str) != 0) { + fprintf(stderr, "%s: Failed to create %s\n", + exec_name, bridge); + close(sd); + return -1; + } + remove_bridge = 1; + + snprintf(str, STR_MAX, "/sbin/ifconfig %s up", bridge); + if (run_cmd(str) != 0) { + fprintf(stderr, "%s: Failed to open %s\n", + exec_name, bridge); + close(sd); + return -1; + } + + if (bridged_if != NULL) { + snprintf(str, STR_MAX, "/sbin/ifconfig %s addm %s", + bridge, bridged_if); + if (run_cmd(str) != 0) { + fprintf(stderr, "%s: Failed to add %s to %s\n", + exec_name, bridged_if, bridge); + close(sd); + return -1; + } + } + + snprintf(str, STR_MAX, "/sbin/ifconfig %s addm %s", + bridge, interface); + if (run_cmd(str) != 0) { + fprintf(stderr, "%s: Failed to add %s to %s\n", + exec_name, interface, bridge); + close(sd); + return -1; + } + } + } + + return sd; +} + +static int open_bpf(char *ifname) +{ + u_int blen = 0; + struct ifreq ifreq; + u_int arg; + + int sd = open("/dev/bpf2", O_RDWR); + + if (sd < 0) { + return -1; + } + + if (ioctl(sd, BIOCGBLEN, &blen) < 0) { + close(sd); + return -2; + } + + bzero(&ifreq, sizeof(ifreq)); + strncpy(ifreq.ifr_name, ifname, IFNAMSIZ); + + arg = 0; + if (ioctl(sd, BIOCSETIF, &ifreq) < 0) { + close(sd); + return -3; + } + + arg = 0; + if (ioctl(sd, BIOCSSEESENT, &arg) < 0) { + close(sd); + return -4; + } + + arg = 1; + if (ioctl(sd, BIOCPROMISC, &arg) < 0) { + close(sd); + return -5; + } + + arg = 1; + if (ioctl(sd, BIOCIMMEDIATE, &arg) < 0) { + close(sd); + return -6; + } + + return sd; +} + +static int run_cmd(const char *cmd) { + char cmd_buffer[STR_MAX] = {0}; + char *argv[MAX_ARGV + 1] = {0}; + int i; + pid_t pid, waitpid; + int status = 0; + + /* Collect arguments */ + strncpy(cmd_buffer, cmd, STR_MAX-1); + + argv[0] = strtok(cmd_buffer, " "); + for (i=1; i +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +FILE * run_tool(const char *if_name, const char *tool_name); + +FILE * run_tool(const char *if_name, const char *tool_name) +{ + OSStatus auth_status; + FILE *fp = NULL; + char *args[] = {NULL, NULL, NULL}; + int ret; + char path_buffer[256]; + AuthorizationFlags auth_flags; + AuthorizationRef auth_ref; + AuthorizationItem auth_items[1]; + AuthorizationRights auth_rights; + CFBundleRef bundle_ref; + CFURLRef url_ref; + CFStringRef path_str; + CFStringRef tool_name_str; + char c; + + bundle_ref = CFBundleGetMainBundle(); + if(bundle_ref == NULL) { + return NULL; + } + + tool_name_str = CFStringCreateWithCString(NULL, tool_name, + kCFStringEncodingUTF8); + + url_ref = CFBundleCopyResourceURL(bundle_ref, tool_name_str, + NULL, NULL); + CFRelease(tool_name_str); + + if(url_ref == NULL) { + return NULL; + } + + path_str = CFURLCopyFileSystemPath(url_ref, kCFURLPOSIXPathStyle); + CFRelease(url_ref); + + if(path_str == NULL) { + return NULL; + } + + if(!CFStringGetCString(path_str, path_buffer, sizeof(path_buffer), + kCFStringEncodingUTF8)) { + CFRelease(path_str); + return NULL; + } + CFRelease(path_str); + + args[0] = (char *)tool_name; + args[1] = (char *)if_name; + + auth_flags = kAuthorizationFlagExtendRights | + kAuthorizationFlagInteractionAllowed | + kAuthorizationFlagPreAuthorize; + + auth_items[0].name = "system.privilege.admin"; + auth_items[0].valueLength = 0; + auth_items[0].value = NULL; + auth_items[0].flags = 0; + + auth_rights.count = sizeof (auth_items) / sizeof (auth_items[0]); + auth_rights.items = auth_items; + + auth_status = AuthorizationCreate(&auth_rights, + kAuthorizationEmptyEnvironment, + auth_flags, + &auth_ref); + + if (auth_status != errAuthorizationSuccess) { + fprintf(stderr, "%s: AuthorizationCreate() failed.\n", + __func__); + return NULL; + } + + auth_status = AuthorizationExecuteWithPrivileges(auth_ref, + path_buffer, + kAuthorizationFlagDefaults, + args + 1, + &fp); + + if (auth_status != errAuthorizationSuccess) { + fprintf(stderr, "%s: AuthorizationExecWithPrivileges() failed.\n", + __func__); + return NULL; + } + + if(fread(&c, 1, 1, fp) != 1) { + fclose(fp); + return NULL; + } + + return fp; +} diff --git a/BasiliskII/src/Unix/Linux/etherhelpertool.c b/BasiliskII/src/Unix/Linux/etherhelpertool.c new file mode 100644 index 00000000..5d6117bc --- /dev/null +++ b/BasiliskII/src/Unix/Linux/etherhelpertool.c @@ -0,0 +1,506 @@ +/* + * etherhelpertool.c - Reads and writes raw ethernet packets usng bpf + * interface. + * + * Copyright (C) 2010, Daniel Sumorok + * + * 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 +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define STR_MAX 256 +#define MAX_ARGV 10 + +static int remove_bridge = 0; +static char bridge_name[STR_MAX]; +static const char *exec_name = "etherhelpertool"; + +static int main_loop(int sd, int use_bpf); +static int open_tap(char *ifname); +static int run_cmd(const char *cmd); +static void handler(int signum); +static int install_signal_handlers(void); +static void do_exit(void); +static int open_bpf(char *ifname); + +int main(int argc, char **argv) +{ + char *if_name; + int ret = 255; + int sd = -1; + int tapNum; + int use_bpf; + + if (argc != 2) { + return 255; + } + + if_name = argv[1]; + + do { + if (strncmp(if_name, "tap", 3) == 0) { + sd = open_tap(if_name); + use_bpf = 0; + } else { + sd = open_bpf(if_name); + use_bpf = 1; + } + + if (sd < 0) { + fprintf(stderr, "%s: open device failed.\n", + exec_name); + ret = 253; + break; + } + + if (install_signal_handlers() != 0) { + fprintf(stderr, + "%s: failed to install signal handers.\n", + exec_name); + ret = 252; + break; + } + + ret = main_loop(sd, use_bpf); + close(sd); + } while (0); + + do_exit(); + + return ret; +} + +static int main_loop(int sd, int use_bpf) +{ + fd_set readSet; + char *outgoing, *incoming; + unsigned short *out_len; + unsigned short *in_len; + int in_index, out_index; + u_int blen = 0; + int ret; + int fret = 0; + int pkt_len; + int frame_len; + int pad; + char c = 0; + + blen = 4096; + + incoming = malloc(blen); + if (incoming == NULL) { + fprintf(stderr, + "%s: malloc() failed.\n", + exec_name); + return -2; + } + + outgoing = malloc(blen); + if (outgoing == NULL) { + free(outgoing); + fprintf(stderr, + "%s: malloc() failed.\n", + exec_name); + return -3; + } + + in_index = 0; + out_index = 0; + + out_len = (unsigned short *)outgoing; + + /* Let our parent know we are ready for business. */ + if(write(0, &c, 1) != 1) { + fprintf(stderr, "%s: Failed to notify main application: %s\n", + __func__, strerror(errno)); + } + + while (1) { + int i; + FD_ZERO(&readSet); + FD_SET(0, &readSet); + FD_SET(sd, &readSet); + + ret = select(sd + 1, &readSet, NULL, NULL, NULL); + if (ret < 0) { + fprintf(stderr, + "%s: select() failed.\n", + exec_name); + fret = -4; + break; + } + + if (FD_ISSET(0, &readSet)) { + if (out_index < 2) { + ret = read(0, outgoing + out_index, 2-out_index); + } else { + ret = read(0, outgoing + out_index, *out_len - out_index + 2); + } + + if (ret < 1) { + if(ret < 0) { + fprintf(stderr, + "%s: read() failed.\n", + exec_name); + } + fret = -5; + break; + } + + out_index += ret; + + if (out_index > 1) { + if ((*out_len + 2) > blen) { + fret = -6; + break; + } + + if (out_index == (*out_len + 2)) { + if(use_bpf) { + ret = write(sd, out_len + 1, *out_len); + if (ret != *out_len) { + fprintf(stderr, + "%s: write() failed.\n", + exec_name); + fret = -7; + break; + } + } else { + ret = write(sd, out_len + 1, *out_len); + if (ret != *out_len) { + fprintf(stderr, + "%s: write() failed.\n", + exec_name); + fret = -7; + break; + } + } + + out_index = 0; + } + } + + } + + if (FD_ISSET(sd, &readSet)) { + in_len = (unsigned short *)incoming; + + pkt_len = read(sd, in_len + 1, blen-2); + if (pkt_len < 14) { + fprintf(stderr, + "%s: read() returned %d.\n", + exec_name, pkt_len); + fret = -8; + break; + } + *in_len = pkt_len; + + if (write(0, in_len, pkt_len + 2) < (pkt_len + 2)) { + fprintf(stderr, + "%s: write() failed\n", + exec_name); + fret = -10; + break; + } + } + } + + free(incoming); + free(outgoing); + + return fret; +} + + +static int open_tap(char *ifname) +{ + char str[STR_MAX] = {0}; + char ifstr[STR_MAX] = {0}; + char *interface; + char *address = NULL; + char *netmask = NULL; + char *bridge = NULL; + char *bridged_if = NULL; + int sd; + struct ifreq ifr = {0}; + + snprintf(ifstr, STR_MAX, "%s", ifname); + interface = strtok(ifstr, "/"); + bridge = strtok(NULL, "/"); + if (bridge != NULL) { + bridged_if = strtok(NULL, "/"); + } + interface = strtok(ifstr, ":"); + + address = strtok(NULL, ":"); + + if (address != NULL) { + netmask = strtok(NULL, ":"); + } + + sd = open("/dev/net/tun", O_RDWR); + if (sd < 0) { + fprintf(stderr, "%s: Failed to open %s\n", + exec_name, interface); + return -1; + } + + snprintf(str, STR_MAX, "/dev/%s", interface); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + strncpy(ifr.ifr_name, interface, IFNAMSIZ); + + if(ioctl(sd, TUNSETIFF, (void *)&ifr) != 0) { + fprintf(stderr, "%s: ioctl(TUNSETIFF): %s\n", + __func__, strerror(errno)); + close(sd); + return -1; + } + + if (address == NULL) { + snprintf(str, STR_MAX, "/sbin/ifconfig %s up", interface); + } else if (netmask == NULL) { + snprintf(str, STR_MAX, "/sbin/ifconfig %s %s", + interface, address); + } else { + snprintf(str, STR_MAX, "/sbin/ifconfig %s %s netmask %s", + interface, address, netmask); + } + + if (run_cmd(str) != 0) { + fprintf(stderr, "%s: Failed to configure %s\n", + exec_name, interface); + close(sd); + return -1; + } + + if (bridge != NULL) { + /* Check to see if bridge is alread up */ + snprintf(str, STR_MAX, "/sbin/ifconfig %s", bridge); + if (run_cmd(str) == 0) { + /* bridge is already up */ + if (bridged_if != NULL) { + fprintf(stderr, "%s: Warning: %s already exists, so %s was not added.\n", + exec_name, bridge, bridged_if); + } + } else { + snprintf(str, STR_MAX, "/sbin/brctl addbr %s", bridge); + if (run_cmd(str) != 0) { + fprintf(stderr, "%s: Failed to create %s\n", + exec_name, bridge); + close(sd); + return -1; + } + remove_bridge = 1; + + strncpy(bridge_name, bridge, STR_MAX); + + snprintf(str, STR_MAX, "/sbin/ifconfig %s up", bridge); + if (run_cmd(str) != 0) { + fprintf(stderr, "%s: Failed to open %s\n", + exec_name, bridge); + close(sd); + return -1; + } + + if (bridged_if != NULL) { + snprintf(str, STR_MAX, "/sbin/brctl addif %s %s", + bridge, bridged_if); + if (run_cmd(str) != 0) { + fprintf(stderr, "%s: Failed to add %s to %s\n", + exec_name, bridged_if, bridge); + close(sd); + return -1; + } + } + + snprintf(str, STR_MAX, "/sbin/brctl addif %s %s", + bridge, interface); + if (run_cmd(str) != 0) { + fprintf(stderr, "%s: Failed to add %s to %s\n", + exec_name, interface, bridge); + close(sd); + return -1; + } + } + } + + return sd; +} + +static int run_cmd(const char *cmd) { + char cmd_buffer[STR_MAX] = {0}; + char *argv[MAX_ARGV + 1] = {0}; + int i; + pid_t pid, waitpid; + int status = 0; + + /* Collect arguments */ + strncpy(cmd_buffer, cmd, STR_MAX-1); + + argv[0] = strtok(cmd_buffer, " "); + for (i=1; i +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STR_MAX 1024 +#define MAX_ARGV 10 + +FILE * run_tool(const char *if_name, const char *tool_name) { + char cmd_buffer[STR_MAX] = {0}; + char * const argv[3] = {NULL, NULL, NULL}; + int i; + pid_t pid, waitpid; + int status = 0; + int fds[2]; + char c; + + if(socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) != 0) { + fprintf(stderr, "%s: socketpair() failed: %s\n", + __func__, strerror(errno)); + return NULL; + } + + ((const char**)argv)[0] = tool_name; + ((const char**)argv)[1] = if_name; + + /* Run sub process */ + pid = fork(); + if (pid == 0) { + /* Child process */ + fclose(stdout); + fclose(stdin); + dup2(fds[0], 0); + close(fds[1]); + close(fds[0]); + + if (execve(tool_name, argv, NULL) < 0) { + perror("execve"); + exit(1); + } + } + + + close(fds[0]); + + if(read(fds[1], &c, 1) < 1) { + close(fds[1]); + return NULL; + } + + return fdopen(fds[1], "rw"); +} diff --git a/BasiliskII/src/Unix/ether_unix.cpp b/BasiliskII/src/Unix/ether_unix.cpp index a9ca33ce..73cba7c7 100644 --- a/BasiliskII/src/Unix/ether_unix.cpp +++ b/BasiliskII/src/Unix/ether_unix.cpp @@ -41,6 +41,21 @@ #endif #include #include + +#ifdef ENABLE_MACOSX_ETHERHELPER + +#ifdef __APPLE__ +#include +#endif + +#ifdef __linux__ +#include +#endif + +#include +#include +#endif + #include #include #include @@ -49,6 +64,7 @@ #include #include #include +#include #if defined(__FreeBSD__) || defined(sgi) || (defined(__APPLE__) && defined(__MACH__)) #include @@ -93,9 +109,17 @@ enum { NET_IF_SHEEPNET, NET_IF_ETHERTAP, NET_IF_TUNTAP, - NET_IF_SLIRP + NET_IF_SLIRP, + 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"; @@ -122,6 +146,11 @@ static uint8 ether_addr[6]; // Our Ethernet address 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; @@ -135,6 +164,11 @@ 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 @@ -235,6 +269,9 @@ bool ether_init(void) // 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; @@ -249,6 +286,10 @@ bool ether_init(void) #ifdef HAVE_SLIRP else if (strcmp(name, "slirp") == 0) net_if_type = NET_IF_SLIRP; +#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; @@ -300,6 +341,25 @@ bool ether_init(void) 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 } if (net_if_type != NET_IF_SLIRP) { fd = open(dev_name, O_RDWR); @@ -750,6 +810,21 @@ static int16 ether_do_write(uint32 arg) write(slirp_input_fd, packet, len); return noErr; } else +#endif +#ifdef ENABLE_MACOSX_ETHERHELPER + if (net_if_type == NET_IF_ETHERHELPER) { + unsigned short pkt_len; + + pkt_len = len; + if (write(fd, &pkt_len, 2) < 2) { + return excessCollsns; + } + + if (write(fd, packet, len) < len) { + return excessCollsns; + } + return noErr; + } else #endif if (write(fd, packet, len) < 0) { D(bug("WARNING: Couldn't transmit packet\n")); @@ -884,6 +959,13 @@ static void *receive_func(void *arg) 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")); @@ -923,6 +1005,18 @@ void ether_do_interrupt(void) 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 { @@ -1049,3 +1143,144 @@ static int slirp_add_redir(const char *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