From 7710322fd3899d7a02c112fc9a7dc7e46c73a2ba Mon Sep 17 00:00:00 2001 From: Dan Sumorok Date: Tue, 15 Mar 2016 20:03:59 -0400 Subject: [PATCH] Linux etherhelper support. --- BasiliskII/src/Unix/Linux/etherhelpertool.c | 624 ++++++++++++++++++++ BasiliskII/src/Unix/Linux/runtool.c | 58 ++ BasiliskII/src/Unix/ether_unix.cpp | 28 +- 3 files changed, 709 insertions(+), 1 deletion(-) create mode 100644 BasiliskII/src/Unix/Linux/etherhelpertool.c create mode 100644 BasiliskII/src/Unix/Linux/runtool.c diff --git a/BasiliskII/src/Unix/Linux/etherhelpertool.c b/BasiliskII/src/Unix/Linux/etherhelpertool.c new file mode 100644 index 00000000..980643bc --- /dev/null +++ b/BasiliskII/src/Unix/Linux/etherhelpertool.c @@ -0,0 +1,624 @@ +/* + * 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 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)) { + 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; + + 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 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 3c588dd1..26f90a7b 100644 --- a/BasiliskII/src/Unix/ether_unix.cpp +++ b/BasiliskII/src/Unix/ether_unix.cpp @@ -43,7 +43,16 @@ #include #ifdef ENABLE_MACOSX_ETHERHELPER + +#ifdef __APPLE__ #include +#endif + +#ifdef __linux__ +#include +#endif + +#include #include #endif @@ -1140,8 +1149,13 @@ 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; @@ -1150,6 +1164,7 @@ static int get_mac_address(const char* dev, unsigned char *addr) 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; @@ -1157,6 +1172,17 @@ static int get_mac_address(const char* dev, unsigned char *addr) 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; } @@ -1240,7 +1266,7 @@ static int read_packet() } index += ret; - + if (index > 1) { if (*pkt_len > (sizeof(packet_buffer) + 2)) { fprintf(stderr, "%s: pkt_len (%d) too large.\n", __func__, *pkt_len);