diff --git a/0readme_ethernet.txt b/0readme_ethernet.txt index 3e6f8998..eb1c66f0 100644 --- a/0readme_ethernet.txt +++ b/0readme_ethernet.txt @@ -133,13 +133,37 @@ OSX (Snow Leopard) Invoke the package installer tuntap_20090913.pkg Click through the various prompts accepting things and eventually installing the package. - # Run simulator and: + # Build and Run simulator and: sim> attach xq tap:tap0 sim> ! ifconfig tap0 192.168.6.1 netmask 255.255.255.0 - Simulated system uses IP address 192.168.6.2 and host uses 192.167.6.1 and things work. - As far as I can tell you must run as root for this to work. + Simulated system uses IP address 192.168.6.2 and host uses 192.167.6.1 + and things work. + You must run as root for this to work. +------------------------------------------------------------------------------- +An alternative to direct pcap and tun/tap networking on *nix environments is +VDE (Virtual Distributed Ethernet). + +Note 1: Using vde based networking is likely more flexible, but it isn't + nearly as efficient. Host OS overhead will always be higher when + vde networking is used as compared to native pcap and/or tun/tap + networking. +Note 2: Root access will likely be needed to configure or start the vde + environment prior to starting a simulator which may use it. +Note 3: Simulators running using VDE networking can run without root + privilege. + +Linux (Ubuntu 10.04): + apt-get install libvdeplug-dev + apt-get install vde2 + + vde_switch -s /tmp/switch1 -tap tap0 -m 666 + ifconfig tap0 192.168.6.1 netmask 255.255.255.0 up + + # Build and Run simulator and: + sim> attach xq vde:/tmp/switch1 #simulator uses IP address 192.168.6.2 + ------------------------------------------------------------------------------- Windows notes: @@ -354,6 +378,7 @@ Dave Change Log =============================================================================== + 30-Oct-11 MP Added support for vde (Virtual Distributed Ethernet) networking 29-Oct-11 MP Added support for integrated Tap networking interfaces on OSX 17-Aug-11 RMS Fix from Sergey Oboguev relating to XU and XQ Auto Config and vector assignments diff --git a/makefile b/makefile index 4004965d..c4be5123 100644 --- a/makefile +++ b/makefile @@ -13,6 +13,7 @@ # ifeq ($(WIN32),) #*nix Environments (&& cygwin) + GCC = gcc ifeq (SunOS,$(shell uname)) TEST = /bin/test else @@ -62,13 +63,18 @@ ifeq ($(WIN32),) NETWORK_CCDEFS = -DUSE_NETWORK NETWORK_LDFLAGS = -lpcap endif + ifeq (vde,$(shell if $(TEST) -e /usr/include/libvdeplug.h -a \( -e /usr/lib/libvdeplug.$(LIBEXT) -o -e /usr/lib64/libvdeplug.$(LIBEXT) \); then echo vde; fi)) + # Provide support for vde networking + NETWORK_CCDEFS += -DUSE_VDE_NETWORK + NETWORK_LDFLAGS += -lvdeplug + endif ifeq (tuntap,$(shell if $(TEST) -e /usr/include/linux/if_tun.h; then echo tuntap; fi)) # Provide support for Tap networking on Linux - NETWORK_TAP_CCDEFS = -DUSE_TAP_NETWORK + NETWORK_CCDEFS += -DUSE_TAP_NETWORK endif ifeq (bsdtuntap,$(shell if $(TEST) -e /usr/include/net/if_tun.h -o -e /Library/Extensions/tap.kext; then echo bsdtuntap; fi)) # Provide support for Tap networking - NETWORK_TAP_CCDEFS = -DUSE_TAP_NETWORK -DUSE_BSDTUNTAP + NETWORK_CCDEFS += -DUSE_TAP_NETWORK -DUSE_BSDTUNTAP endif ifneq (binexists,$(shell if $(TEST) -e BIN; then echo binexists; fi)) MKDIRBIN = if $(TEST) ! -e BIN; then mkdir BIN; fi @@ -79,6 +85,7 @@ ifeq ($(WIN32),) endif else #Win32 Environments (via MinGW32) + GCC = gcc GCC_Path := $(dir $(shell where gcc.exe)) ifeq ($(NOASYNCH),) ifeq (pthreads,$(shell if exist ..\pthreads\Pre-built.2\include\pthread.h echo pthreads)) @@ -119,7 +126,7 @@ else endif -CC = gcc -std=c99 -U__STRICT_ANSI__ -g -I . $(NETWORK_CCDEFS) $(NETWORK_TAP_CCDEFS) $(OS_CCDEFS) $(ROMS_OPT) +CC = $(GCC) -std=c99 -U__STRICT_ANSI__ -g -I . $(NETWORK_CCDEFS) $(OS_CCDEFS) $(ROMS_OPT) LDFLAGS = $(OS_LDFLAGS) $(NETWORK_LDFLAGS) # diff --git a/sim_ether.c b/sim_ether.c index 04692446..3ca7eefa 100644 --- a/sim_ether.c +++ b/sim_ether.c @@ -138,6 +138,15 @@ at open time. This functionality is only useful/needed on *nix platforms since native sharing of Windows NIC devices works with no external magic. + USE_VDE_NETWORK - Specifies that support for vde networking should be + included. This can be leveraged, along with OS bridging + capabilities to share a single LAN interface. It also + can allow a simulator to have useful networking + functionality when running without root access. This + allows device names of the form vde:/tmp/switch to be + specified at open time. This functionality is only + available on *nix platforms since the vde api isn't + available on Windows. NEED_PCAP_SENDPACKET - Specifies that you are using an older version of libpcap @@ -151,6 +160,11 @@ Modification history: + 30-Oct-11 MP Added support for vde (Virtual Distributed Ethernet) networking + 29-Oct-11 MP Added support for integrated Tap networking interfaces on OSX + 12-Aug-11 MP Cleaned up payload length determination + Fixed race condition detecting reflections when threaded + reading and writing is enabled 18-Apr-11 MP Fixed race condition with self loopback packets in multithreaded environments 09-Jan-11 MP Fixed missing crc data when USE_READER_THREAD is defined and @@ -793,6 +807,10 @@ void eth_show_dev (FILE* st, ETH_DEV* dev) #endif #endif /* USE_TAP_NETWORK */ +#ifdef USE_VDE_NETWORK +#include +#endif /* USE_VDE_NETWORK */ + /* Allows windows to look up user-defined adapter names */ #if defined(_WIN32) #include @@ -1092,40 +1110,42 @@ static void eth_get_nic_hw_addr(ETH_DEV* dev, char *devname) if (!pcap_mac_if_win32(devname, (UCHAR *)&dev->host_nic_phy_hw_addr)) dev->have_host_nic_phy_addr = 1; #elif !defined (__VMS) -{ - char command[1024]; - FILE *f; + if (1) { + char command[1024]; + FILE *f; - memset(command, 0, sizeof(command)); - snprintf(command, sizeof(command)-1, "ifconfig %s | grep [0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F] >NIC.hwaddr", devname); - system(command); - if (f = fopen("NIC.hwaddr", "r")) { - if (fgets(command, sizeof(command)-1, f)) { - char *p1, *p2; + if (0 == strncmp("vde:", devname, 4)) + return; + memset(command, 0, sizeof(command)); + snprintf(command, sizeof(command)-1, "ifconfig %s | grep [0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F] >NIC.hwaddr", devname); + system(command); + if (f = fopen("NIC.hwaddr", "r")) { + if (fgets(command, sizeof(command)-1, f)) { + char *p1, *p2; - p1 = strchr(command, ':'); - while (p1) { - p2 = strchr(p1+1, ':'); - if (p2 == p1+3) { - int mac_bytes[6]; - if (6 == sscanf(p1-2, "%02x:%02x:%02x:%02x:%02x:%02x", &mac_bytes[0], &mac_bytes[1], &mac_bytes[2], &mac_bytes[3], &mac_bytes[4], &mac_bytes[5])) { - dev->host_nic_phy_hw_addr[0] = mac_bytes[0]; - dev->host_nic_phy_hw_addr[1] = mac_bytes[1]; - dev->host_nic_phy_hw_addr[2] = mac_bytes[2]; - dev->host_nic_phy_hw_addr[3] = mac_bytes[3]; - dev->host_nic_phy_hw_addr[4] = mac_bytes[4]; - dev->host_nic_phy_hw_addr[5] = mac_bytes[5]; - dev->have_host_nic_phy_addr = 1; + p1 = strchr(command, ':'); + while (p1) { + p2 = strchr(p1+1, ':'); + if (p2 == p1+3) { + int mac_bytes[6]; + if (6 == sscanf(p1-2, "%02x:%02x:%02x:%02x:%02x:%02x", &mac_bytes[0], &mac_bytes[1], &mac_bytes[2], &mac_bytes[3], &mac_bytes[4], &mac_bytes[5])) { + dev->host_nic_phy_hw_addr[0] = mac_bytes[0]; + dev->host_nic_phy_hw_addr[1] = mac_bytes[1]; + dev->host_nic_phy_hw_addr[2] = mac_bytes[2]; + dev->host_nic_phy_hw_addr[3] = mac_bytes[3]; + dev->host_nic_phy_hw_addr[4] = mac_bytes[4]; + dev->host_nic_phy_hw_addr[5] = mac_bytes[5]; + dev->have_host_nic_phy_addr = 1; + } + break; + } + p1 = p2; } - break; } - p1 = p2; + fclose(f); + remove("NIC.hwaddr"); } } - fclose(f); - remove("NIC.hwaddr"); - } -} #endif } @@ -1148,84 +1168,116 @@ int sched_policy; struct sched_param sched_priority; #if defined (_WIN32) HANDLE hWait = pcap_getevent ((pcap_t*)dev->handle); -#endif -#if defined (MUST_DO_SELECT) -struct timeval timeout; +#else int sel_ret; -int pcap_fd; +int do_select = 0; +int select_fd; -if (dev->pcap_mode) - pcap_fd = pcap_get_selectable_fd((pcap_t *)dev->handle); -else - pcap_fd = dev->fd_handle; +switch (dev->eth_api) { + case ETH_API_PCAP: +#if defined (MUST_DO_SELECT) + do_select = 1; + select_fd = pcap_get_selectable_fd((pcap_t *)dev->handle); +#endif + break; + case ETH_API_TAP: + case ETH_API_VDE: + do_select = 1; + select_fd = dev->fd_handle; + break; + } #endif - sim_debug(dev->dbit, dev->dptr, "Reader Thread Starting\n"); +sim_debug(dev->dbit, dev->dptr, "Reader Thread Starting\n"); - /* Boost Priority for this I/O thread vs the CPU instruction execution - thread which in general won't be readily yielding the processor when - this thread needs to run */ - pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority); - ++sched_priority.sched_priority; - pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); +/* Boost Priority for this I/O thread vs the CPU instruction execution + thread which in general won't be readily yielding the processor when + this thread needs to run */ +pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority); +++sched_priority.sched_priority; +pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); - while ((dev->pcap_mode && dev->handle) || dev->fd_handle) { -#if defined (MUST_DO_SELECT) - fd_set setl; +while (dev->handle) { +#if defined (_WIN32) + if (WAIT_OBJECT_0 == WaitForSingleObject (hWait, 250)) { +#else + fd_set setl; + struct timeval timeout; + + if (do_select) { FD_ZERO(&setl); - FD_SET(pcap_fd, &setl); + FD_SET(select_fd, &setl); timeout.tv_sec = 0; timeout.tv_usec = 250*1000; - sel_ret = select(1+pcap_fd, &setl, NULL, NULL, &timeout); - if (sel_ret < 0 && errno != EINTR) break; - if (sel_ret > 0) { -#else /* !MUST_DO_SELECT */ -#if defined (_WIN32) - if (WAIT_OBJECT_0 == WaitForSingleObject (hWait, 250)) { -#else - if (1) { -#endif -#endif /* MUST_DO_SELECT */ - if ((dev->pcap_mode) && (!dev->handle)) - break; - if ((!dev->pcap_mode) && (!dev->fd_handle)) - break; - /* dispatch read request queue available packets */ - if (dev->pcap_mode) + sel_ret = select(1+select_fd, &setl, NULL, NULL, &timeout); + } + else + sel_ret = 1; + if (sel_ret < 0 && errno != EINTR) break; + if (sel_ret > 0) { +#endif /* _WIN32 */ + if (!dev->handle) + break; + /* dispatch read request queue available packets */ + switch (dev->eth_api) { + case ETH_API_PCAP: status = pcap_dispatch ((pcap_t*)dev->handle, -1, &_eth_callback, (u_char*)dev); + break; #ifdef USE_TAP_NETWORK - else { - struct pcap_pkthdr header; - int len; - u_char buf[ETH_MAX_JUMBO_FRAME]; + case ETH_API_TAP: + if (1) { + struct pcap_pkthdr header; + int len; + u_char buf[ETH_MAX_JUMBO_FRAME]; - memset(&header, 0, sizeof(header)); - len = read(dev->fd_handle, buf, sizeof(buf)); - if (len > 0) { - status = 1; - header.caplen = header.len = len; - _eth_callback((u_char *)dev, &header, buf); - } else { - status = 0; - } - } -#endif /* USE_TAP_NETWORK */ - - if ((status > 0) && (dev->asynch_io)) { - int wakeup_needed; - pthread_mutex_lock (&dev->lock); - wakeup_needed = (dev->read_queue.count != 0); - pthread_mutex_unlock (&dev->lock); - if (wakeup_needed) { - sim_debug(dev->dbit, dev->dptr, "Queueing automatic poll\n"); - sim_activate_abs (dev->dptr->units, dev->asynch_io_latency); + memset(&header, 0, sizeof(header)); + len = read(dev->fd_handle, buf, sizeof(buf)); + if (len > 0) { + status = 1; + header.caplen = header.len = len; + _eth_callback((u_char *)dev, &header, buf); + } + else + status = 0; } + break; +#endif /* USE_TAP_NETWORK */ +#ifdef USE_VDE_NETWORK + case ETH_API_VDE: + if (1) { + struct pcap_pkthdr header; + int len; + u_char buf[ETH_MAX_JUMBO_FRAME]; + + memset(&header, 0, sizeof(header)); + len = vde_recv((VDECONN *)dev->handle, buf, sizeof(buf), 0); + if (len > 0) { + status = 1; + header.caplen = header.len = len; + _eth_callback((u_char *)dev, &header, buf); + } + else + status = 0; + } + break; +#endif /* USE_VDE_NETWORK */ + } + if ((status > 0) && (dev->asynch_io)) { + int wakeup_needed; + + pthread_mutex_lock (&dev->lock); + wakeup_needed = (dev->read_queue.count != 0); + pthread_mutex_unlock (&dev->lock); + if (wakeup_needed) { + sim_debug(dev->dbit, dev->dptr, "Queueing automatic poll\n"); + sim_activate_abs (dev->dptr->units, dev->asynch_io_latency); } } + } } - sim_debug(dev->dbit, dev->dptr, "Reader Thread Exiting\n"); - return NULL; +sim_debug(dev->dbit, dev->dptr, "Reader Thread Exiting\n"); +return NULL; } static void * @@ -1236,225 +1288,257 @@ struct write_request *request; int sched_policy; struct sched_param sched_priority; - /* Boost Priority for this I/O thread vs the CPU instruction execution - thread which in general won't be readily yielding the processor when - this thread needs to run */ - pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority); - ++sched_priority.sched_priority; - pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); +/* Boost Priority for this I/O thread vs the CPU instruction execution + thread which in general won't be readily yielding the processor when + this thread needs to run */ +pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority); +++sched_priority.sched_priority; +pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); - sim_debug(dev->dbit, dev->dptr, "Writer Thread Starting\n"); +sim_debug(dev->dbit, dev->dptr, "Writer Thread Starting\n"); - pthread_mutex_lock (&dev->writer_lock); - while ((dev->pcap_mode && dev->handle) || dev->fd_handle) { - pthread_cond_wait (&dev->writer_cond, &dev->writer_lock); - while (request = dev->write_requests) { - /* Pull buffer off request list */ - dev->write_requests = request->next; - pthread_mutex_unlock (&dev->writer_lock); - - dev->write_status = _eth_write(dev, &request->packet, NULL); - - pthread_mutex_lock (&dev->writer_lock); - /* Put buffer on free buffer list */ - request->next = dev->write_buffers; - dev->write_buffers = request; +pthread_mutex_lock (&dev->writer_lock); +while (dev->handle) { + pthread_cond_wait (&dev->writer_cond, &dev->writer_lock); + while (request = dev->write_requests) { + /* Pull buffer off request list */ + dev->write_requests = request->next; + pthread_mutex_unlock (&dev->writer_lock); + + dev->write_status = _eth_write(dev, &request->packet, NULL); + + pthread_mutex_lock (&dev->writer_lock); + /* Put buffer on free buffer list */ + request->next = dev->write_buffers; + dev->write_buffers = request; } } - pthread_mutex_unlock (&dev->writer_lock); +pthread_mutex_unlock (&dev->writer_lock); - sim_debug(dev->dbit, dev->dptr, "Writer Thread Exiting\n"); - return NULL; +sim_debug(dev->dbit, dev->dptr, "Writer Thread Exiting\n"); +return NULL; } #endif t_stat eth_set_async (ETH_DEV *dev, int latency) { #if !defined(USE_READER_THREAD) || !defined(SIM_ASYNCH_IO) - char *msg = "Eth: can't operate asynchronously, must poll\r\n"; - printf ("%s", msg); - if (sim_log) fprintf (sim_log, "%s", msg); - return SCPE_NOFNC; +char *msg = "Eth: can't operate asynchronously, must poll\r\n"; +printf ("%s", msg); +if (sim_log) fprintf (sim_log, "%s", msg); +return SCPE_NOFNC; #else - int wakeup_needed; +int wakeup_needed; - dev->asynch_io = 1; - dev->asynch_io_latency = latency; - pthread_mutex_lock (&dev->lock); - wakeup_needed = (dev->read_queue.count != 0); - pthread_mutex_unlock (&dev->lock); - if (wakeup_needed) { - sim_debug(dev->dbit, dev->dptr, "Queueing automatic poll\n"); - sim_activate_abs (dev->dptr->units, dev->asynch_io_latency); +dev->asynch_io = 1; +dev->asynch_io_latency = latency; +pthread_mutex_lock (&dev->lock); +wakeup_needed = (dev->read_queue.count != 0); +pthread_mutex_unlock (&dev->lock); +if (wakeup_needed) { + sim_debug(dev->dbit, dev->dptr, "Queueing automatic poll\n"); + sim_activate_abs (dev->dptr->units, dev->asynch_io_latency); } #endif - return SCPE_OK; +return SCPE_OK; } t_stat eth_clr_async (ETH_DEV *dev) { #if !defined(USE_READER_THREAD) || !defined(SIM_ASYNCH_IO) - return SCPE_NOFNC; +return SCPE_NOFNC; #else - /* make sure device exists */ - if (!dev) return SCPE_UNATT; +/* make sure device exists */ +if (!dev) return SCPE_UNATT; - dev->asynch_io = 0; - return SCPE_OK; +dev->asynch_io = 0; +return SCPE_OK; #endif } t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit) { - int bufsz = (BUFSIZ < ETH_MAX_PACKET) ? ETH_MAX_PACKET : BUFSIZ; - char errbuf[PCAP_ERRBUF_SIZE]; - char temp[1024]; - char* savname = name; - int num; - char* msg; +int bufsz = (BUFSIZ < ETH_MAX_PACKET) ? ETH_MAX_PACKET : BUFSIZ; +char errbuf[PCAP_ERRBUF_SIZE]; +char temp[1024]; +char* savname = name; +int num; +char* msg; - if (bufsz < ETH_MAX_JUMBO_FRAME) - bufsz = ETH_MAX_JUMBO_FRAME; /* Enable handling of jumbo frames */ +if (bufsz < ETH_MAX_JUMBO_FRAME) + bufsz = ETH_MAX_JUMBO_FRAME; /* Enable handling of jumbo frames */ - /* initialize device */ - eth_zero(dev); +/* initialize device */ +eth_zero(dev); - /* translate name of type "ethX" to real device name */ - if ((strlen(name) == 4) - && (tolower(name[0]) == 'e') - && (tolower(name[1]) == 't') - && (tolower(name[2]) == 'h') - && isdigit(name[3]) - ) { - num = atoi(&name[3]); - savname = eth_getname(num, temp); +/* translate name of type "ethX" to real device name */ +if ((strlen(name) == 4) + && (tolower(name[0]) == 'e') + && (tolower(name[1]) == 't') + && (tolower(name[2]) == 'h') + && isdigit(name[3]) + ) { + num = atoi(&name[3]); + savname = eth_getname(num, temp); + if (savname == 0) /* didn't translate */ + return SCPE_OPENERR; + } +else { + /* are they trying to use device description? */ + savname = eth_getname_bydesc(name, temp); + if (savname == 0) { /* didn't translate */ + /* probably is not ethX and has no description */ + savname = eth_getname_byname(name, temp); if (savname == 0) /* didn't translate */ - return SCPE_OPENERR; - } else { - /* are they trying to use device description? */ - savname = eth_getname_bydesc(name, temp); - if (savname == 0) { /* didn't translate */ - /* probably is not ethX and has no description */ - savname = eth_getname_byname(name, temp); - if (savname == 0) /* didn't translate */ - savname = name; + savname = name; } } - /* attempt to connect device */ - memset(errbuf, 0, sizeof(errbuf)); - if (strncmp("tap:", savname, 4)) { +/* attempt to connect device */ +memset(errbuf, 0, sizeof(errbuf)); +if (0 == strncmp("tap:", savname, 4)) { + int tun = -1; /* TUN/TAP Socket */ + int on = 1; + char dev_name[64] = ""; + +#if defined(USE_TAP_NETWORK) + if (!strcmp(savname, "tap:tapN")) { + msg = "Eth: Must specify actual tap device name (i.e. tap:tap0)\r\n"; + printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + return SCPE_OPENERR; + } +#endif +#if defined(__linux) && defined(USE_TAP_NETWORK) + if ((tun = open("/dev/net/tun", O_RDWR)) >= 0) { + struct ifreq ifr; /* Interface Requests */ + + memset(&ifr, 0, sizeof(ifr)); + /* Set up interface flags */ + strcpy(ifr.ifr_name, savname+4); + ifr.ifr_flags = IFF_TAP|IFF_NO_PI; + + /* Send interface requests to TUN/TAP driver. */ + if (ioctl(tun, TUNSETIFF, &ifr) >= 0) { + if (ioctl(tun, FIONBIO, &on)) { + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); + close(tun); + } + else { + dev->fd_handle = tun; + strcpy(savname, ifr.ifr_name); + } + } + else + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); + } + else + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); +#elif defined(USE_BSDTUNTAP) && defined(USE_TAP_NETWORK) + snprintf(dev_name, sizeof(dev_name)-1, "/dev/%s", savname+4); + dev_name[sizeof(dev_name)-1] = '\0'; + + if ((tun = open(dev_name, O_RDWR)) >= 0) { + if (ioctl(tun, FIONBIO, &on)) { + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); + close(tun); + } + else { + dev->fd_handle = tun; + strcpy(savname, savname+4); + } +#if defined (__APPLE__) + if (1) { + struct ifreq ifr; + int s; + + memset (&ifr, 0, sizeof(ifr)); + ifr.ifr_addr.sa_family = AF_INET; + strncpy(ifr.ifr_name, savname+4, sizeof(ifr.ifr_name)); + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) { + if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) >= 0) { + ifr.ifr_flags |= IFF_UP; + ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr); + } + close(s); + } + } +#endif + } + else + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); +#else + strncpy(errbuf, "No support for tap: devices", sizeof(errbuf)-1); +#endif /* !defined(__linux) && !defined(USE_BSDTUNTAP) */ + if (0 == errbuf[0]) { + dev->eth_api = ETH_API_TAP; + dev->handle = (void *)1; /* Flag used to indicated open */ + } + } +else + if (0 == strncmp("vde:", savname, 4)) { +#if defined(USE_VDE_NETWORK) + struct vde_open_args voa; + + memset(&voa, 0, sizeof(voa)); + if (!strcmp(savname, "vde:vdedevice")) { + msg = "Eth: Must specify actual vde device name (i.e. vde:/tmp/switch)\r\n"; + printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + return SCPE_OPENERR; + } + if (!(dev->handle = (void*) vde_open(savname+4, "simh", &voa))) + strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); + else { + dev->eth_api = ETH_API_VDE; + dev->fd_handle = vde_datafd((VDECONN*)dev->handle); + } +#else + strncpy(errbuf, "No support for vde: network devices", sizeof(errbuf)-1); +#endif /* !defined(__linux) && !defined(USE_BSDTUNTAP) */ + } + else { dev->handle = (void*) pcap_open_live(savname, bufsz, ETH_PROMISC, PCAP_READ_TIMEOUT, errbuf); if (!dev->handle) { /* can't open device */ msg = "Eth: pcap_open_live error - %s\r\n"; printf (msg, errbuf); if (sim_log) fprintf (sim_log, msg, errbuf); return SCPE_OPENERR; - } - dev->pcap_mode = 1; - } else { - int tun = -1; /* TUN/TAP Socket */ - int on = 1; - char dev_name[64] = ""; - -#if defined(USE_TAP_NETWORK) - if (!strcmp(savname, "tap:tapN")) { - msg = "Eth: Must specify actual tap device name (i.e. tap:tap0)\r\n"; - printf (msg, errbuf); - if (sim_log) fprintf (sim_log, msg, errbuf); - return SCPE_OPENERR; - } -#endif -#if defined(__linux) && defined(USE_TAP_NETWORK) - if ((tun = open("/dev/net/tun", O_RDWR)) >= 0) { - struct ifreq ifr; /* Interface Requests */ - - memset(&ifr, 0, sizeof(ifr)); - /* Set up interface flags */ - strcpy(ifr.ifr_name, savname+4); - ifr.ifr_flags = IFF_TAP|IFF_NO_PI; - - // Send interface requests to TUN/TAP driver. - if (ioctl(tun, TUNSETIFF, &ifr) >= 0) { - if (ioctl(tun, FIONBIO, &on)) { - strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); - close(tun); - } else { - dev->fd_handle = tun; - strcpy(savname, ifr.ifr_name); - } - } else - strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); - } else - strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); -#elif defined(USE_BSDTUNTAP) && defined(USE_TAP_NETWORK) - snprintf(dev_name, sizeof(dev_name)-1, "/dev/%s", savname+4); - dev_name[sizeof(dev_name)-1] = '\0'; - - if ((tun = open(dev_name, O_RDWR)) >= 0) { - if (ioctl(tun, FIONBIO, &on)) { - strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); - close(tun); - } else { - dev->fd_handle = tun; - strcpy(savname, savname+4); } -#if defined (__APPLE__) - if (1) { - struct ifreq ifr; - int s; - - memset (&ifr, 0, sizeof(ifr)); - ifr.ifr_addr.sa_family = AF_INET; - strncpy(ifr.ifr_name, savname+4, sizeof(ifr.ifr_name)); - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) { - if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) >= 0) { - ifr.ifr_flags |= IFF_UP; - ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr); - } - close(s); - } - } -#endif - } else { - strncpy(errbuf, strerror(errno), sizeof(errbuf)-1); + dev->eth_api = ETH_API_PCAP; } -#else - strncpy(errbuf, "No support for tap: devices", sizeof(errbuf)-1); -#endif /* !defined(__linux) && !defined(USE_BSDTUNTAP) */ +if (errbuf[0]) { + msg = "Eth: open error - %s\r\n"; + printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + return SCPE_OPENERR; } - if (errbuf[0]) { - msg = "Eth: open error - %s\r\n"; - printf (msg, errbuf); - if (sim_log) fprintf (sim_log, msg, errbuf); - return SCPE_OPENERR; - } - msg = "Eth: opened OS device %s\r\n"; - printf (msg, savname); - if (sim_log) fprintf (sim_log, msg, savname); +msg = "Eth: opened OS device %s\r\n"; +printf (msg, savname); +if (sim_log) fprintf (sim_log, msg, savname); - /* get the NIC's hardware MAC address */ - eth_get_nic_hw_addr(dev, savname); +/* get the NIC's hardware MAC address */ +eth_get_nic_hw_addr(dev, savname); - /* save name of device */ - dev->name = malloc(strlen(savname)+1); - strcpy(dev->name, savname); +/* save name of device */ +dev->name = malloc(strlen(savname)+1); +strcpy(dev->name, savname); - /* save debugging information */ - dev->dptr = dptr; - dev->dbit = dbit; +/* save debugging information */ +dev->dptr = dptr; +dev->dbit = dbit; #if !defined(HAS_PCAP_SENDPACKET) && defined (xBSD) && !defined (__APPLE__) - /* Tell the kernel that the header is fully-formed when it gets it. - This is required in order to fake the src address. */ - if (dev->pcap_mode) { - int one = 1; - ioctl(pcap_fileno(dev->handle), BIOCSHDRCMPLT, &one); +/* Tell the kernel that the header is fully-formed when it gets it. + This is required in order to fake the src address. */ +if (dev->eth_api == ETH_API_PCAP) { + int one = 1; + ioctl(pcap_fileno(dev->handle), BIOCSHDRCMPLT, &one); } #endif /* xBSD */ #if defined (USE_READER_THREAD) - { +if (1) { pthread_attr_t attr; #if defined(_WIN32) @@ -1473,318 +1557,344 @@ t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit) } #else /* !defined (USE_READER_THREAD */ #ifdef USE_SETNONBLOCK - /* set ethernet device non-blocking so pcap_dispatch() doesn't hang */ - if (dev->pcap_mode && (pcap_setnonblock (dev->handle, 1, errbuf) == -1)) { - msg = "Eth: Failed to set non-blocking: %s\r\n"; - printf (msg, errbuf); - if (sim_log) fprintf (sim_log, msg, errbuf); +/* set ethernet device non-blocking so pcap_dispatch() doesn't hang */ +if ((dev->eth_api == ETH_API_PCAP) && (pcap_setnonblock (dev->handle, 1, errbuf) == -1)) { + msg = "Eth: Failed to set non-blocking: %s\r\n"; + printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); } #endif #endif /* !defined (USE_READER_THREAD */ #if defined (__APPLE__) - if (dev->pcap_mode) { - /* Deliver packets immediately, needed for OS X 10.6.2 and later - * (Snow-Leopard). - * See this thread on libpcap and Mac Os X 10.6 Snow Leopard on - * the tcpdump mailinglist: http://seclists.org/tcpdump/2010/q1/110 - */ - int v = 1; - ioctl(pcap_fileno(dev->handle), BIOCIMMEDIATE, &v); +if (dev->eth_api == ETH_API_PCAP) { + /* Deliver packets immediately, needed for OS X 10.6.2 and later + * (Snow-Leopard). + * See this thread on libpcap and Mac Os X 10.6 Snow Leopard on + * the tcpdump mailinglist: http://seclists.org/tcpdump/2010/q1/110 + */ + int v = 1; + ioctl(pcap_fileno(dev->handle), BIOCIMMEDIATE, &v); } #endif - return SCPE_OK; +return SCPE_OK; } t_stat eth_close(ETH_DEV* dev) { - char* msg = "Eth: closed %s\r\n"; - pcap_t *pcap; - int pcap_fd = dev->fd_handle; +char* msg = "Eth: closed %s\r\n"; +pcap_t *pcap; +int pcap_fd = dev->fd_handle; - /* make sure device exists */ - if (!dev) return SCPE_UNATT; +/* make sure device exists */ +if (!dev) return SCPE_UNATT; - /* close the device */ - pcap = (pcap_t *)dev->handle; - dev->handle = NULL; - dev->fd_handle = 0; - dev->have_host_nic_phy_addr = 0; +/* close the device */ +pcap = (pcap_t *)dev->handle; +dev->handle = NULL; +dev->fd_handle = 0; +dev->have_host_nic_phy_addr = 0; #if defined (USE_READER_THREAD) - pthread_join (dev->reader_thread, NULL); - pthread_mutex_destroy (&dev->lock); - pthread_cond_signal (&dev->writer_cond); - pthread_join (dev->writer_thread, NULL); - pthread_mutex_destroy (&dev->self_lock); - pthread_mutex_destroy (&dev->writer_lock); - pthread_cond_destroy (&dev->writer_cond); - if (1) { - struct write_request *buffer; - - while (buffer = dev->write_buffers) { - dev->write_buffers = buffer->next; - free(buffer); +pthread_join (dev->reader_thread, NULL); +pthread_mutex_destroy (&dev->lock); +pthread_cond_signal (&dev->writer_cond); +pthread_join (dev->writer_thread, NULL); +pthread_mutex_destroy (&dev->self_lock); +pthread_mutex_destroy (&dev->writer_lock); +pthread_cond_destroy (&dev->writer_cond); +if (1) { + struct write_request *buffer; + while (buffer = dev->write_buffers) { + dev->write_buffers = buffer->next; + free(buffer); } - while (buffer = dev->write_requests) { - dev->write_requests = buffer->next; - free(buffer); + while (buffer = dev->write_requests) { + dev->write_requests = buffer->next; + free(buffer); } } - ethq_destroy (&dev->read_queue); /* release FIFO queue */ +ethq_destroy (&dev->read_queue); /* release FIFO queue */ #endif - if (dev->pcap_mode) +switch (dev->eth_api) { + case ETH_API_PCAP: pcap_close(pcap); + break; #ifdef USE_TAP_NETWORK - else + case ETH_API_TAP: close(pcap_fd); + break; #endif - printf (msg, dev->name); - if (sim_log) fprintf (sim_log, msg, dev->name); +#ifdef USE_VDE_NETWORK + case ETH_API_VDE: + vde_close((VDECONN*)pcap); + break; +#endif + } +printf (msg, dev->name); +if (sim_log) fprintf (sim_log, msg, dev->name); - /* clean up the mess */ - free(dev->name); - eth_zero(dev); +/* clean up the mess */ +free(dev->name); +eth_zero(dev); - return SCPE_OK; +return SCPE_OK; } t_stat eth_check_address_conflict (ETH_DEV* dev, ETH_MAC* const mac) { - ETH_PACK send, recv; - t_stat status; - int responses = 0; - char mac_string[32]; +ETH_PACK send, recv; +t_stat status; +int responses = 0; +char mac_string[32]; - eth_mac_fmt(mac, mac_string); - sim_debug(dev->dbit, dev->dptr, "Determining Address Conflict for MAC address: %s\n", mac_string); +eth_mac_fmt(mac, mac_string); +sim_debug(dev->dbit, dev->dptr, "Determining Address Conflict for MAC address: %s\n", mac_string); - /* build a loopback forward request packet */ - memset (&send, 0, sizeof(ETH_PACK)); - send.len = ETH_MIN_PACKET; /* minimum packet size */ - memcpy(&send.msg[0], mac, sizeof(ETH_MAC)); /* target address */ - memcpy(&send.msg[6], mac, sizeof(ETH_MAC)); /* source address */ - send.msg[12] = 0x90; /* loopback packet type */ - send.msg[14] = 0; /* Offset */ - send.msg[16] = 2; /* Forward */ - memcpy(&send.msg[18], mac, sizeof(ETH_MAC)); /* Forward Destination */ - send.msg[24] = 1; /* Reply */ +/* build a loopback forward request packet */ +memset (&send, 0, sizeof(ETH_PACK)); +send.len = ETH_MIN_PACKET; /* minimum packet size */ +memcpy(&send.msg[0], mac, sizeof(ETH_MAC)); /* target address */ +memcpy(&send.msg[6], mac, sizeof(ETH_MAC)); /* source address */ +send.msg[12] = 0x90; /* loopback packet type */ +send.msg[14] = 0; /* Offset */ +send.msg[16] = 2; /* Forward */ +memcpy(&send.msg[18], mac, sizeof(ETH_MAC)); /* Forward Destination */ +send.msg[24] = 1; /* Reply */ - eth_filter(dev, 1, (ETH_MAC *)mac, 0, 0); +eth_filter(dev, 1, (ETH_MAC *)mac, 0, 0); - /* send the packet */ - status = _eth_write (dev, &send, NULL); - if (status != SCPE_OK) { - char *msg; - msg = "Eth: Error Transmitting packet: %s\r\n" - "You may need to run as root, or install a libpcap version\r\n" - "which is at least 0.9 from www.tcpdump.org\r\n"; - printf(msg, strerror(errno)); - if (sim_log) fprintf (sim_log, msg, strerror(errno)); - return status; +/* send the packet */ +status = _eth_write (dev, &send, NULL); +if (status != SCPE_OK) { + char *msg; + msg = "Eth: Error Transmitting packet: %s\r\n" + "You may need to run as root, or install a libpcap version\r\n" + "which is at least 0.9 from www.tcpdump.org\r\n"; + printf(msg, strerror(errno)); + if (sim_log) fprintf (sim_log, msg, strerror(errno)); + return status; } - sim_os_ms_sleep (300); /* time for a conflicting host to respond */ +sim_os_ms_sleep (300); /* time for a conflicting host to respond */ - /* empty the read queue and count the responses */ - do { - memset (&recv, 0, sizeof(ETH_PACK)); - status = eth_read (dev, &recv, NULL); - if (memcmp(send.msg, recv.msg, 14)== 0) - responses++; +/* empty the read queue and count the responses */ +do { + memset (&recv, 0, sizeof(ETH_PACK)); + status = eth_read (dev, &recv, NULL); + if (memcmp(send.msg, recv.msg, 14)== 0) + responses++; } while (recv.len > 0); - sim_debug(dev->dbit, dev->dptr, "Address Conflict = %d\n", responses); - return responses; +sim_debug(dev->dbit, dev->dptr, "Address Conflict = %d\n", responses); +return responses; } t_stat eth_reflect(ETH_DEV* dev) { - /* Test with an address no NIC should have. */ - /* We do this to avoid reflections from the wire, */ - /* in the event that a simulated NIC has a MAC address conflict. */ - ETH_MAC mac = {0xfe,0xff,0xff,0xff,0xff,0xfe}; +/* Test with an address no NIC should have. */ +/* We do this to avoid reflections from the wire, */ +/* in the event that a simulated NIC has a MAC address conflict. */ +ETH_MAC mac = {0xfe,0xff,0xff,0xff,0xff,0xfe}; - sim_debug(dev->dbit, dev->dptr, "Determining Reflections...\n"); +sim_debug(dev->dbit, dev->dptr, "Determining Reflections...\n"); - dev->reflections = 0; - dev->reflections = eth_check_address_conflict (dev, &mac); +dev->reflections = 0; +dev->reflections = eth_check_address_conflict (dev, &mac); - sim_debug(dev->dbit, dev->dptr, "Reflections = %d\n", dev->reflections); - return dev->reflections; +sim_debug(dev->dbit, dev->dptr, "Reflections = %d\n", dev->reflections); +return dev->reflections; } static t_stat _eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) { - int status = 1; /* default to failure */ +int status = 1; /* default to failure */ - /* make sure device exists */ - if (!dev) return SCPE_UNATT; +/* make sure device exists */ +if (!dev) return SCPE_UNATT; - /* make sure packet exists */ - if (!packet) return SCPE_ARG; +/* make sure packet exists */ +if (!packet) return SCPE_ARG; - /* make sure packet is acceptable length */ - if ((packet->len >= ETH_MIN_PACKET) && (packet->len <= ETH_MAX_PACKET)) { - int loopback_self_frame = LOOPBACK_SELF_FRAME(dev->physical_addr, packet->msg); +/* make sure packet is acceptable length */ +if ((packet->len >= ETH_MIN_PACKET) && (packet->len <= ETH_MAX_PACKET)) { + int loopback_self_frame = LOOPBACK_SELF_FRAME(dev->physical_addr, packet->msg); - eth_packet_trace (dev, packet->msg, packet->len, "writing"); + eth_packet_trace (dev, packet->msg, packet->len, "writing"); - /* record sending of loopback packet (done before actual send to avoid race conditions with receiver) */ - if (loopback_self_frame) { + /* record sending of loopback packet (done before actual send to avoid race conditions with receiver) */ + if (loopback_self_frame) { #ifdef USE_READER_THREAD - pthread_mutex_lock (&dev->self_lock); + pthread_mutex_lock (&dev->self_lock); #endif - dev->loopback_self_sent += dev->reflections; - dev->loopback_self_sent_total++; + dev->loopback_self_sent += dev->reflections; + dev->loopback_self_sent_total++; #ifdef USE_READER_THREAD - pthread_mutex_unlock (&dev->self_lock); + pthread_mutex_unlock (&dev->self_lock); #endif } /* dispatch write request (synchronous; no need to save write info to dev) */ - if (dev->pcap_mode) + switch (dev->eth_api) { + case ETH_API_PCAP: status = pcap_sendpacket((pcap_t*)dev->handle, (u_char*)packet->msg, packet->len); + break; #ifdef USE_TAP_NETWORK - else + case ETH_API_TAP: status = ((packet->len == write(dev->fd_handle, (void *)packet->msg, packet->len)) ? 0 : -1); + break; #endif - - /* On error, correct loopback bookkeeping */ - if ((status != 0) && loopback_self_frame) { -#ifdef USE_READER_THREAD - pthread_mutex_lock (&dev->self_lock); +#ifdef USE_VDE_NETWORK + case ETH_API_VDE: + status = vde_send((VDECONN*)dev->handle, (void *)packet->msg, packet->len, 0); + if ((status == packet->len) || (status == 0)) + status = 0; + else + if ((status == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) + status = 0; + else + status = 1; + break; #endif - dev->loopback_self_sent -= dev->reflections; - dev->loopback_self_sent_total--; + } + /* On error, correct loopback bookkeeping */ + if ((status != 0) && loopback_self_frame) { #ifdef USE_READER_THREAD - pthread_mutex_unlock (&dev->self_lock); + pthread_mutex_lock (&dev->self_lock); +#endif + dev->loopback_self_sent -= dev->reflections; + dev->loopback_self_sent_total--; +#ifdef USE_READER_THREAD + pthread_mutex_unlock (&dev->self_lock); #endif } } /* if packet->len */ - /* call optional write callback function */ - if (routine) - (routine)(status); +/* call optional write callback function */ +if (routine) + (routine)(status); - return ((status == 0) ? SCPE_OK : SCPE_IOERR); +return ((status == 0) ? SCPE_OK : SCPE_IOERR); } t_stat eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) { #ifdef USE_READER_THREAD - struct write_request *request; - int write_queue_size = 1; +struct write_request *request; +int write_queue_size = 1; - /* make sure device exists */ - if (!dev) return SCPE_UNATT; +/* make sure device exists */ +if (!dev) return SCPE_UNATT; - /* Get a buffer */ - pthread_mutex_lock (&dev->writer_lock); - if (request = dev->write_buffers) - dev->write_buffers = request->next; - pthread_mutex_unlock (&dev->writer_lock); - if (!request) - request = malloc(sizeof(*request)); +/* Get a buffer */ +pthread_mutex_lock (&dev->writer_lock); +if (request = dev->write_buffers) + dev->write_buffers = request->next; +pthread_mutex_unlock (&dev->writer_lock); +if (!request) + request = malloc(sizeof(*request)); - /* Copy buffer contents */ - request->packet.len = packet->len; - request->packet.used = packet->used; - request->packet.status = packet->status; - request->packet.crc_len = packet->crc_len; - memcpy(request->packet.msg, packet->msg, packet->len); +/* Copy buffer contents */ +request->packet.len = packet->len; +request->packet.used = packet->used; +request->packet.status = packet->status; +request->packet.crc_len = packet->crc_len; +memcpy(request->packet.msg, packet->msg, packet->len); - /* Insert buffer at the end of the write list (to make sure that */ - /* packets make it to the wire in the order they were presented here) */ - pthread_mutex_lock (&dev->writer_lock); - request->next = NULL; - if (dev->write_requests) { - struct write_request *last_request = dev->write_requests; +/* Insert buffer at the end of the write list (to make sure that */ +/* packets make it to the wire in the order they were presented here) */ +pthread_mutex_lock (&dev->writer_lock); +request->next = NULL; +if (dev->write_requests) { + struct write_request *last_request = dev->write_requests; + ++write_queue_size; + while (last_request->next) { + last_request = last_request->next; ++write_queue_size; - while (last_request->next) { - last_request = last_request->next; - ++write_queue_size; } - last_request->next = request; - } else + last_request->next = request; + } +else dev->write_requests = request; - if (write_queue_size > dev->write_queue_peak) - dev->write_queue_peak = write_queue_size; - pthread_mutex_unlock (&dev->writer_lock); +if (write_queue_size > dev->write_queue_peak) + dev->write_queue_peak = write_queue_size; +pthread_mutex_unlock (&dev->writer_lock); - /* Awaken writer thread to perform actual write */ - pthread_cond_signal (&dev->writer_cond); +/* Awaken writer thread to perform actual write */ +pthread_cond_signal (&dev->writer_cond); - /* Return with a status from some prior write */ - if (routine) - (routine)(dev->write_status); - return dev->write_status; +/* Return with a status from some prior write */ +if (routine) + (routine)(dev->write_status); +return dev->write_status; #else - return _eth_write(dev, packet, routine); +return _eth_write(dev, packet, routine); #endif } static int _eth_hash_lookup(ETH_MULTIHASH hash, const u_char* data) { - int key = 0x3f & (eth_crc32(0, data, 6) >> 26); +int key = 0x3f & (eth_crc32(0, data, 6) >> 26); - key ^= 0x3f; - return (hash[key>>3] & (1 << (key&0x7))); +key ^= 0x3f; +return (hash[key>>3] & (1 << (key&0x7))); } static int _eth_hash_validate(ETH_MAC *MultiCastList, int count, ETH_MULTIHASH hash) { - ETH_MULTIHASH lhash; - int i; +ETH_MULTIHASH lhash; +int i; - memset(lhash, 0, sizeof(lhash)); - for (i=0; i> 26); +memset(lhash, 0, sizeof(lhash)); +for (i=0; i> 26); - key ^= 0x3F; - printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X Key: %X, Byte: %X, Val: %X\n", - MultiCastList[i][0], MultiCastList[i][1], MultiCastList[i][2], MultiCastList[i][3], MultiCastList[i][4], MultiCastList[i][5], - key, key>>3, (1 << (key&0x7))); - lhash[key>>3] |= (1 << (key&0x7)); + key ^= 0x3F; + printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X Key: %X, Byte: %X, Val: %X\n", + MultiCastList[i][0], MultiCastList[i][1], MultiCastList[i][2], MultiCastList[i][3], MultiCastList[i][4], MultiCastList[i][5], + key, key>>3, (1 << (key&0x7))); + lhash[key>>3] |= (1 << (key&0x7)); } - if (memcmp(hash, lhash, sizeof(lhash))) { - printf("Inconsistent Computed Hash:\n"); - printf("Should be: %02X %02X %02X %02X %02X %02X %02X %02X\n", - hash[0], hash[1], hash[2], hash[3], - hash[4], hash[5], hash[6], hash[7]); - printf("Was: %02X %02X %02X %02X %02X %02X %02X %02X\n", - lhash[0], lhash[1], lhash[2], lhash[3], - lhash[4], lhash[5], lhash[6], lhash[7]); +if (memcmp(hash, lhash, sizeof(lhash))) { + printf("Inconsistent Computed Hash:\n"); + printf("Should be: %02X %02X %02X %02X %02X %02X %02X %02X\n", + hash[0], hash[1], hash[2], hash[3], + hash[4], hash[5], hash[6], hash[7]); + printf("Was: %02X %02X %02X %02X %02X %02X %02X %02X\n", + lhash[0], lhash[1], lhash[2], lhash[3], + lhash[4], lhash[5], lhash[6], lhash[7]); } - printf("Should be: %02X %02X %02X %02X %02X %02X %02X %02X\n", - hash[0], hash[1], hash[2], hash[3], - hash[4], hash[5], hash[6], hash[7]); - printf("Was: %02X %02X %02X %02X %02X %02X %02X %02X\n", - lhash[0], lhash[1], lhash[2], lhash[3], - lhash[4], lhash[5], lhash[6], lhash[7]); - return 0; +else { + printf("Should be: %02X %02X %02X %02X %02X %02X %02X %02X\n", + hash[0], hash[1], hash[2], hash[3], + hash[4], hash[5], hash[6], hash[7]); + printf("Was: %02X %02X %02X %02X %02X %02X %02X %02X\n", + lhash[0], lhash[1], lhash[2], lhash[3], + lhash[4], lhash[5], lhash[6], lhash[7]); + } +return 0; } static void _eth_test_multicast_hash() { - ETH_MAC tMacs[] = { - {0xAB, 0x00, 0x04, 0x01, 0xAC, 0x10}, - {0xAB, 0x00, 0x00, 0x04, 0x00, 0x00}, - {0x09, 0x00, 0x2B, 0x00, 0x00, 0x0F}, - {0x09, 0x00, 0x2B, 0x02, 0x01, 0x04}, - {0x09, 0x00, 0x2B, 0x02, 0x01, 0x07}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, - {0x01, 0x00, 0x5E, 0x00, 0x00, 0x01}}; - ETH_MULTIHASH thash = {0x01, 0x40, 0x00, 0x00, 0x48, 0x88, 0x40, 0x00}; +ETH_MAC tMacs[] = { + {0xAB, 0x00, 0x04, 0x01, 0xAC, 0x10}, + {0xAB, 0x00, 0x00, 0x04, 0x00, 0x00}, + {0x09, 0x00, 0x2B, 0x00, 0x00, 0x0F}, + {0x09, 0x00, 0x2B, 0x02, 0x01, 0x04}, + {0x09, 0x00, 0x2B, 0x02, 0x01, 0x07}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + {0x01, 0x00, 0x5E, 0x00, 0x00, 0x01}}; +ETH_MULTIHASH thash = {0x01, 0x40, 0x00, 0x00, 0x48, 0x88, 0x40, 0x00}; - _eth_hash_validate(tMacs, sizeof(tMacs)/sizeof(tMacs[0]), thash); +_eth_hash_validate(tMacs, sizeof(tMacs)/sizeof(tMacs[0]), thash); } /* The IP header */ @@ -1807,7 +1917,7 @@ struct IPHeader { uint16 checksum; /* IP checksum */ uint32 source_ip; /* Source Address */ uint32 dest_ip; /* Destination Address */ -}; + }; /* ICMP header */ struct ICMPHeader { @@ -1815,14 +1925,14 @@ struct ICMPHeader { uint8 code; /* Type sub code */ uint16 checksum; /* ICMP Checksum */ uint32 otherstuff[1];/* optional data */ -}; + }; struct UDPHeader { uint16 source_port; uint16 dest_port; uint16 length; /* The length of the entire UDP datagram, including both header and Data fields. */ uint16 checksum; -}; + }; struct TCPHeader { uint16 source_port; @@ -1844,7 +1954,7 @@ struct TCPHeader { uint16 checksum; uint16 urgent; uint16 otherstuff[1]; /* The rest of the packet */ -}; + }; #ifndef IPPROTO_TCP #define IPPROTO_TCP 6 /* tcp */ @@ -1859,484 +1969,516 @@ struct TCPHeader { static uint16 ip_checksum(uint16 *buffer, int size) { - unsigned long cksum = 0; +unsigned long cksum = 0; - /* Sum all the words together, adding the final byte if size is odd */ - while (size > 1) { - cksum += *buffer++; - size -= sizeof(*buffer); - } - if (size) { - uint8 endbytes[2]; +/* Sum all the words together, adding the final byte if size is odd */ +while (size > 1) { + cksum += *buffer++; + size -= sizeof(*buffer); +} +if (size) { + uint8 endbytes[2]; - endbytes[0] = *((uint8 *)buffer); - endbytes[1] = 0; - cksum += *((uint16 *)endbytes); + endbytes[0] = *((uint8 *)buffer); + endbytes[1] = 0; + cksum += *((uint16 *)endbytes); } - /* Do a little shuffling */ - cksum = (cksum >> 16) + (cksum & 0xffff); - cksum += (cksum >> 16); +/* Do a little shuffling */ +cksum = (cksum >> 16) + (cksum & 0xffff); +cksum += (cksum >> 16); - /* Return the bitwise complement of the resulting mishmash */ - return (uint16)(~cksum); +/* Return the bitwise complement of the resulting mishmash */ +return (uint16)(~cksum); } static uint16 pseudo_checksum(uint16 len, uint16 proto, uint16 *src_addr, uint16 *dest_addr, uint8 *buff) { - uint32 sum; +uint32 sum; - /* Sum the data first */ - sum = 0xffff&(~ip_checksum((uint16 *)buff, len)); +/* Sum the data first */ +sum = 0xffff&(~ip_checksum((uint16 *)buff, len)); - /* add the pseudo header which contains the IP source and destinationn addresses */ - sum += src_addr[0]; - sum += src_addr[1]; - sum += dest_addr[0]; - sum += dest_addr[1]; - /* and the protocol number and the length of the UDP packet */ - sum = sum + htons(proto) + htons(len); +/* add the pseudo header which contains the IP source and destinationn addresses */ +sum += src_addr[0]; +sum += src_addr[1]; +sum += dest_addr[0]; +sum += dest_addr[1]; +/* and the protocol number and the length of the UDP packet */ +sum = sum + htons(proto) + htons(len); - /* Do a little shuffling */ - sum = (sum >> 16) + (sum & 0xffff); - sum += (sum >> 16); +/* Do a little shuffling */ +sum = (sum >> 16) + (sum & 0xffff); +sum += (sum >> 16); - /* Return the bitwise complement of the resulting mishmash */ - return (uint16)(~sum); +/* Return the bitwise complement of the resulting mishmash */ +return (uint16)(~sum); } static void _eth_fix_ip_jumbo_offload(ETH_DEV* dev, const u_char* msg, int len) { - unsigned short* proto = (unsigned short*) &msg[12]; - struct IPHeader *IP; - struct TCPHeader *TCP = NULL; - struct UDPHeader *UDP; - struct ICMPHeader *ICMP; - uint16 orig_checksum; - uint16 payload_len; - uint16 mtu_payload; - uint16 ip_flags; - uint16 frag_offset; - struct pcap_pkthdr header; - uint16 orig_tcp_flags; +unsigned short* proto = (unsigned short*) &msg[12]; +struct IPHeader *IP; +struct TCPHeader *TCP = NULL; +struct UDPHeader *UDP; +struct ICMPHeader *ICMP; +uint16 orig_checksum; +uint16 payload_len; +uint16 mtu_payload; +uint16 ip_flags; +uint16 frag_offset; +struct pcap_pkthdr header; +uint16 orig_tcp_flags; - /* Only interested in IP frames */ - if (ntohs(*proto) != 0x0800) { - ++dev->jumbo_dropped; /* Non IP Frames are dropped */ - return; +/* Only interested in IP frames */ +if (ntohs(*proto) != 0x0800) { + ++dev->jumbo_dropped; /* Non IP Frames are dropped */ + return; } - IP = (struct IPHeader *)&msg[14]; - if (IP_VERSION(IP) != 4) { - ++dev->jumbo_dropped; /* Non IPv4 jumbo frames are dropped */ - return; +IP = (struct IPHeader *)&msg[14]; +if (IP_VERSION(IP) != 4) { + ++dev->jumbo_dropped; /* Non IPv4 jumbo frames are dropped */ + return; } - if ((IP_HLEN(IP) > len) || (ntohs(IP->total_len) > len)) { - ++dev->jumbo_dropped; /* Bogus header length frames are dropped */ - return; +if ((IP_HLEN(IP) > len) || (ntohs(IP->total_len) > len)) { + ++dev->jumbo_dropped; /* Bogus header length frames are dropped */ + return; } - if (IP_FRAG_OFFSET(IP) || IP_FRAG_MF(IP)) { - ++dev->jumbo_dropped; /* Previously fragmented, but currently jumbo sized frames are dropped */ - return; +if (IP_FRAG_OFFSET(IP) || IP_FRAG_MF(IP)) { + ++dev->jumbo_dropped; /* Previously fragmented, but currently jumbo sized frames are dropped */ + return; } - switch (IP->proto) { - case IPPROTO_UDP: - UDP = (struct UDPHeader *)(((char *)IP)+IP_HLEN(IP)); - if (ntohs(UDP->length) > (len-IP_HLEN(IP))) { - ++dev->jumbo_dropped; /* Bogus UDP packet length (packet contained length exceeds packet size) frames are dropped */ - return; - } - if (UDP->checksum == 0) - break; /* UDP Checksums are disabled */ - orig_checksum = UDP->checksum; - UDP->checksum = 0; - UDP->checksum = pseudo_checksum(ntohs(UDP->length), IPPROTO_UDP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)UDP); - if (orig_checksum != UDP->checksum) - eth_packet_trace (dev, msg, len, "reading jumbo UDP header Checksum Fixed"); - break; - case IPPROTO_ICMP: - ICMP = (struct ICMPHeader *)(((char *)IP)+IP_HLEN(IP)); - orig_checksum = ICMP->checksum; - ICMP->checksum = 0; - ICMP->checksum = ip_checksum((uint16 *)ICMP, ntohs(IP->total_len)-IP_HLEN(IP)); - if (orig_checksum != ICMP->checksum) - eth_packet_trace (dev, msg, len, "reading jumbo ICMP header Checksum Fixed"); - break; - case IPPROTO_TCP: - TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); - if ((TCP_DATA_OFFSET(TCP) > (len-IP_HLEN(IP))) || (TCP_DATA_OFFSET(TCP) < 20)) { - ++dev->jumbo_dropped; /* Bogus TCP packet header length (packet contained length exceeds packet size) frames are dropped */ - return; - } - /* We don't do anything with the TCP checksum since we're going to resegment the TCP data below */ - break; - default: - ++dev->jumbo_dropped; /* We onlt handle UDP, ICMP and TCP jumbo frames others are dropped */ +switch (IP->proto) { + case IPPROTO_UDP: + UDP = (struct UDPHeader *)(((char *)IP)+IP_HLEN(IP)); + if (ntohs(UDP->length) > (len-IP_HLEN(IP))) { + ++dev->jumbo_dropped; /* Bogus UDP packet length (packet contained length exceeds packet size) frames are dropped */ return; + } + if (UDP->checksum == 0) + break; /* UDP Checksums are disabled */ + orig_checksum = UDP->checksum; + UDP->checksum = 0; + UDP->checksum = pseudo_checksum(ntohs(UDP->length), IPPROTO_UDP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)UDP); + if (orig_checksum != UDP->checksum) + eth_packet_trace (dev, msg, len, "reading jumbo UDP header Checksum Fixed"); + break; + case IPPROTO_ICMP: + ICMP = (struct ICMPHeader *)(((char *)IP)+IP_HLEN(IP)); + orig_checksum = ICMP->checksum; + ICMP->checksum = 0; + ICMP->checksum = ip_checksum((uint16 *)ICMP, ntohs(IP->total_len)-IP_HLEN(IP)); + if (orig_checksum != ICMP->checksum) + eth_packet_trace (dev, msg, len, "reading jumbo ICMP header Checksum Fixed"); + break; + case IPPROTO_TCP: + TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); + if ((TCP_DATA_OFFSET(TCP) > (len-IP_HLEN(IP))) || (TCP_DATA_OFFSET(TCP) < 20)) { + ++dev->jumbo_dropped; /* Bogus TCP packet header length (packet contained length exceeds packet size) frames are dropped */ + return; + } + /* We don't do anything with the TCP checksum since we're going to resegment the TCP data below */ + break; + default: + ++dev->jumbo_dropped; /* We onlt handle UDP, ICMP and TCP jumbo frames others are dropped */ + return; } - /* Reasonable Checksums are now in the jumbo packet, but we've got to actually */ - /* deliver ONLY standard sized ethernet frames. Our job here is to now act as */ - /* a router might have to and fragment these IPv4 frames as they are delivered */ - /* into the virtual NIC. We do this by walking down the packet and dispatching */ - /* a chunk at a time recomputing an appropriate header for each chunk. For */ - /* datagram oriented protocols (UDP and ICMP) this is done by simple packet */ - /* fragmentation. For TCP this is done by breaking large packets into separate */ - /* TCP packets. */ - memset(&header, 0, sizeof(header)); - switch (IP->proto) { - case IPPROTO_UDP: - case IPPROTO_ICMP: - ++dev->jumbo_fragmented; - /* When we're performing LSO (Large Send Offload), we're given a - 'template' header which may not include a value being populated - in the IP header length (which is only 16 bits). - We process as payload everything which isn't known header data. */ - payload_len = len - (14 + IP_HLEN(IP)); - mtu_payload = ETH_MIN_JUMBO_FRAME - (14 + IP_HLEN(IP)); - frag_offset = 0; - while (payload_len > 0) { - ip_flags = frag_offset; - if (payload_len > mtu_payload) { - ip_flags |= IP_MF_FLAG; - IP->total_len = htons(((mtu_payload>>3)<<3) + IP_HLEN(IP)); - } else { - IP->total_len = htons(payload_len + IP_HLEN(IP)); +/* Reasonable Checksums are now in the jumbo packet, but we've got to actually */ +/* deliver ONLY standard sized ethernet frames. Our job here is to now act as */ +/* a router might have to and fragment these IPv4 frames as they are delivered */ +/* into the virtual NIC. We do this by walking down the packet and dispatching */ +/* a chunk at a time recomputing an appropriate header for each chunk. For */ +/* datagram oriented protocols (UDP and ICMP) this is done by simple packet */ +/* fragmentation. For TCP this is done by breaking large packets into separate */ +/* TCP packets. */ +memset(&header, 0, sizeof(header)); +switch (IP->proto) { + case IPPROTO_UDP: + case IPPROTO_ICMP: + ++dev->jumbo_fragmented; + /* When we're performing LSO (Large Send Offload), we're given a + 'template' header which may not include a value being populated + in the IP header length (which is only 16 bits). + We process as payload everything which isn't known header data. */ + payload_len = len - (14 + IP_HLEN(IP)); + mtu_payload = ETH_MIN_JUMBO_FRAME - (14 + IP_HLEN(IP)); + frag_offset = 0; + while (payload_len > 0) { + ip_flags = frag_offset; + if (payload_len > mtu_payload) { + ip_flags |= IP_MF_FLAG; + IP->total_len = htons(((mtu_payload>>3)<<3) + IP_HLEN(IP)); } - IP->flags = htons(ip_flags); - IP->checksum = 0; - IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); - header.caplen = header.len = 14 + ntohs(IP->total_len); - eth_packet_trace (dev, ((u_char *)IP)-14, header.len, "reading Datagram fragment"); + else { + IP->total_len = htons(payload_len + IP_HLEN(IP)); + } + IP->flags = htons(ip_flags); + IP->checksum = 0; + IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); + header.caplen = header.len = 14 + ntohs(IP->total_len); + eth_packet_trace (dev, ((u_char *)IP)-14, header.len, "reading Datagram fragment"); #if ETH_MIN_JUMBO_FRAME < ETH_MAX_PACKET - { /* Debugging is easier if we read packets directly with pcap - (i.e. we can use Wireshark to verify packet contents) - we don't want to do this all the time for 2 reasons: - 1) sending through pcap involves kernel transitions and - 2) if the current system reflects sent packets, the - recieving side will receive and process 2 copies of - any packets sent this way. */ - ETH_PACK pkt; + if (1) { + /* Debugging is easier if we read packets directly with pcap + (i.e. we can use Wireshark to verify packet contents) + we don't want to do this all the time for 2 reasons: + 1) sending through pcap involves kernel transitions and + 2) if the current system reflects sent packets, the + recieving side will receive and process 2 copies of + any packets sent this way. */ + ETH_PACK pkt; - memset(&pkt, 0, sizeof(pkt)); - memcpy(pkt.msg, ((u_char *)IP)-14, header.len); - pkt.len = header.len; - _eth_write(dev, &pkt, NULL); - } + memset(&pkt, 0, sizeof(pkt)); + memcpy(pkt.msg, ((u_char *)IP)-14, header.len); + pkt.len = header.len; + _eth_write(dev, &pkt, NULL); + } #else - _eth_callback((u_char *)dev, &header, ((u_char *)IP)-14); + _eth_callback((u_char *)dev, &header, ((u_char *)IP)-14); #endif - payload_len -= (ntohs(IP->total_len) - IP_HLEN(IP)); - frag_offset += (ntohs(IP->total_len) - IP_HLEN(IP))>>3; - if (payload_len > 0) { - /* Move the MAC and IP headers down to just prior to the next payload segment */ - memcpy(((u_char *)IP) + ntohs(IP->total_len) - (14 + IP_HLEN(IP)), ((u_char *)IP) - 14, 14 + IP_HLEN(IP)); - IP = (struct IPHeader *)(((u_char *)IP) + ntohs(IP->total_len) - IP_HLEN(IP)); + payload_len -= (ntohs(IP->total_len) - IP_HLEN(IP)); + frag_offset += (ntohs(IP->total_len) - IP_HLEN(IP))>>3; + if (payload_len > 0) { + /* Move the MAC and IP headers down to just prior to the next payload segment */ + memcpy(((u_char *)IP) + ntohs(IP->total_len) - (14 + IP_HLEN(IP)), ((u_char *)IP) - 14, 14 + IP_HLEN(IP)); + IP = (struct IPHeader *)(((u_char *)IP) + ntohs(IP->total_len) - IP_HLEN(IP)); } } - break; - case IPPROTO_TCP: - ++dev->jumbo_fragmented; - eth_packet_trace_ex (dev, ((u_char *)IP)-14, len, "Fragmenting Jumbo TCP segment", 1, dev->dbit); - TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); - orig_tcp_flags = ntohs(TCP->data_offset_and_flags); - /* When we're performing LSO (Large Send Offload), we're given a - 'template' header which may not include a value being populated - in the IP header length (which is only 16 bits). - We process as payload everything which isn't known header data. */ - payload_len = len - (14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); - mtu_payload = ETH_MIN_JUMBO_FRAME - (14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); - while (payload_len > 0) { - if (payload_len > mtu_payload) { - TCP->data_offset_and_flags = htons(orig_tcp_flags&~(TCP_PSH_FLAG|TCP_FIN_FLAG|TCP_RST_FLAG)); - IP->total_len = htons(mtu_payload + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); - } else { - TCP->data_offset_and_flags = htons(orig_tcp_flags); - IP->total_len = htons(payload_len + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); + break; + case IPPROTO_TCP: + ++dev->jumbo_fragmented; + eth_packet_trace_ex (dev, ((u_char *)IP)-14, len, "Fragmenting Jumbo TCP segment", 1, dev->dbit); + TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); + orig_tcp_flags = ntohs(TCP->data_offset_and_flags); + /* When we're performing LSO (Large Send Offload), we're given a + 'template' header which may not include a value being populated + in the IP header length (which is only 16 bits). + We process as payload everything which isn't known header data. */ + payload_len = len - (14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); + mtu_payload = ETH_MIN_JUMBO_FRAME - (14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); + while (payload_len > 0) { + if (payload_len > mtu_payload) { + TCP->data_offset_and_flags = htons(orig_tcp_flags&~(TCP_PSH_FLAG|TCP_FIN_FLAG|TCP_RST_FLAG)); + IP->total_len = htons(mtu_payload + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); } - IP->checksum = 0; - IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); - TCP->checksum = 0; - TCP->checksum = pseudo_checksum(ntohs(IP->total_len)-IP_HLEN(IP), IPPROTO_TCP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)TCP); - header.caplen = header.len = 14 + ntohs(IP->total_len); - eth_packet_trace_ex (dev, ((u_char *)IP)-14, header.len, "reading TCP segment", 1, dev->dbit); + else { + TCP->data_offset_and_flags = htons(orig_tcp_flags); + IP->total_len = htons(payload_len + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); + } + IP->checksum = 0; + IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); + TCP->checksum = 0; + TCP->checksum = pseudo_checksum(ntohs(IP->total_len)-IP_HLEN(IP), IPPROTO_TCP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)TCP); + header.caplen = header.len = 14 + ntohs(IP->total_len); + eth_packet_trace_ex (dev, ((u_char *)IP)-14, header.len, "reading TCP segment", 1, dev->dbit); #if ETH_MIN_JUMBO_FRAME < ETH_MAX_PACKET - { /* Debugging is easier if we read packets directly with pcap - (i.e. we can use Wireshark to verify packet contents) - we don't want to do this all the time for 2 reasons: - 1) sending through pcap involves kernel transitions and - 2) if the current system reflects sent packets, the - recieving side will receive and process 2 copies of - any packets sent this way. */ - ETH_PACK pkt; + if (1) { + /* Debugging is easier if we read packets directly with pcap + (i.e. we can use Wireshark to verify packet contents) + we don't want to do this all the time for 2 reasons: + 1) sending through pcap involves kernel transitions and + 2) if the current system reflects sent packets, the + recieving side will receive and process 2 copies of + any packets sent this way. */ + ETH_PACK pkt; - memset(&pkt, 0, sizeof(pkt)); - memcpy(pkt.msg, ((u_char *)IP)-14, header.len); - pkt.len = header.len; - _eth_write(dev, &pkt, NULL); - } + memset(&pkt, 0, sizeof(pkt)); + memcpy(pkt.msg, ((u_char *)IP)-14, header.len); + pkt.len = header.len; + _eth_write(dev, &pkt, NULL); + } #else - _eth_callback((u_char *)dev, &header, ((u_char *)IP)-14); + _eth_callback((u_char *)dev, &header, ((u_char *)IP)-14); #endif - payload_len -= (ntohs(IP->total_len) - (IP_HLEN(IP) + TCP_DATA_OFFSET(TCP))); - if (payload_len > 0) { - /* Move the MAC, IP and TCP headers down to just prior to the next payload segment */ - memcpy(((u_char *)IP) + ntohs(IP->total_len) - (14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)), ((u_char *)IP) - 14, 14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); - IP = (struct IPHeader *)(((u_char *)IP) + ntohs(IP->total_len) - (IP_HLEN(IP) + TCP_DATA_OFFSET(TCP))); - TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); - TCP->sequence_number = htonl(mtu_payload + ntohl(TCP->sequence_number)); + payload_len -= (ntohs(IP->total_len) - (IP_HLEN(IP) + TCP_DATA_OFFSET(TCP))); + if (payload_len > 0) { + /* Move the MAC, IP and TCP headers down to just prior to the next payload segment */ + memcpy(((u_char *)IP) + ntohs(IP->total_len) - (14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)), ((u_char *)IP) - 14, 14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); + IP = (struct IPHeader *)(((u_char *)IP) + ntohs(IP->total_len) - (IP_HLEN(IP) + TCP_DATA_OFFSET(TCP))); + TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); + TCP->sequence_number = htonl(mtu_payload + ntohl(TCP->sequence_number)); } } - break; + break; } } static void _eth_fix_ip_xsum_offload(ETH_DEV* dev, u_char* msg, int len) { - unsigned short* proto = (unsigned short*) &msg[12]; - struct IPHeader *IP; - struct TCPHeader *TCP; - struct UDPHeader *UDP; - struct ICMPHeader *ICMP; - uint16 orig_checksum; +unsigned short* proto = (unsigned short*) &msg[12]; +struct IPHeader *IP; +struct TCPHeader *TCP; +struct UDPHeader *UDP; +struct ICMPHeader *ICMP; +uint16 orig_checksum; - /* Only need to process locally originated packets */ - if ((!dev->have_host_nic_phy_addr) || (memcmp(msg+6, dev->host_nic_phy_hw_addr, 6))) - return; - /* Only interested in IP frames */ - if (ntohs(*proto) != 0x0800) - return; - IP = (struct IPHeader *)&msg[14]; - if (IP_VERSION(IP) != 4) - return; /* Only interested in IPv4 frames */ - if ((IP_HLEN(IP) > len) || (ntohs(IP->total_len) > len)) - return; /* Bogus header length */ - orig_checksum = IP->checksum; - IP->checksum = 0; - IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); - if (orig_checksum != IP->checksum) - eth_packet_trace (dev, msg, len, "reading IP header Checksum Fixed"); - if (IP_FRAG_OFFSET(IP) || IP_FRAG_MF(IP)) - return; /* Insufficient data to compute payload checksum */ - switch (IP->proto) { - case IPPROTO_UDP: - UDP = (struct UDPHeader *)(((char *)IP)+IP_HLEN(IP)); - if (ntohs(UDP->length) > (len-IP_HLEN(IP))) - return; /* packet contained length exceeds packet size */ - if (UDP->checksum == 0) - return; /* UDP Checksums are disabled */ - orig_checksum = UDP->checksum; - UDP->checksum = 0; - UDP->checksum = pseudo_checksum(ntohs(UDP->length), IPPROTO_UDP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)UDP); - if (orig_checksum != UDP->checksum) - eth_packet_trace (dev, msg, len, "reading UDP header Checksum Fixed"); - break; - case IPPROTO_TCP: - TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); - orig_checksum = TCP->checksum; - TCP->checksum = 0; - TCP->checksum = pseudo_checksum(ntohs(IP->total_len)-IP_HLEN(IP), IPPROTO_TCP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)TCP); - if (orig_checksum != TCP->checksum) - eth_packet_trace (dev, msg, len, "reading TCP header Checksum Fixed"); - break; - case IPPROTO_ICMP: - ICMP = (struct ICMPHeader *)(((char *)IP)+IP_HLEN(IP)); - orig_checksum = ICMP->checksum; - ICMP->checksum = 0; - ICMP->checksum = ip_checksum((uint16 *)ICMP, ntohs(IP->total_len)-IP_HLEN(IP)); - if (orig_checksum != ICMP->checksum) - eth_packet_trace (dev, msg, len, "reading ICMP header Checksum Fixed"); - break; +/* Only need to process locally originated packets */ +if ((!dev->have_host_nic_phy_addr) || (memcmp(msg+6, dev->host_nic_phy_hw_addr, 6))) + return; +/* Only interested in IP frames */ +if (ntohs(*proto) != 0x0800) + return; +IP = (struct IPHeader *)&msg[14]; +if (IP_VERSION(IP) != 4) + return; /* Only interested in IPv4 frames */ +if ((IP_HLEN(IP) > len) || (ntohs(IP->total_len) > len)) + return; /* Bogus header length */ +orig_checksum = IP->checksum; +IP->checksum = 0; +IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); +if (orig_checksum != IP->checksum) + eth_packet_trace (dev, msg, len, "reading IP header Checksum Fixed"); +if (IP_FRAG_OFFSET(IP) || IP_FRAG_MF(IP)) + return; /* Insufficient data to compute payload checksum */ +switch (IP->proto) { + case IPPROTO_UDP: + UDP = (struct UDPHeader *)(((char *)IP)+IP_HLEN(IP)); + if (ntohs(UDP->length) > (len-IP_HLEN(IP))) + return; /* packet contained length exceeds packet size */ + if (UDP->checksum == 0) + return; /* UDP Checksums are disabled */ + orig_checksum = UDP->checksum; + UDP->checksum = 0; + UDP->checksum = pseudo_checksum(ntohs(UDP->length), IPPROTO_UDP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)UDP); + if (orig_checksum != UDP->checksum) + eth_packet_trace (dev, msg, len, "reading UDP header Checksum Fixed"); + break; + case IPPROTO_TCP: + TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); + orig_checksum = TCP->checksum; + TCP->checksum = 0; + TCP->checksum = pseudo_checksum(ntohs(IP->total_len)-IP_HLEN(IP), IPPROTO_TCP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)TCP); + if (orig_checksum != TCP->checksum) + eth_packet_trace (dev, msg, len, "reading TCP header Checksum Fixed"); + break; + case IPPROTO_ICMP: + ICMP = (struct ICMPHeader *)(((char *)IP)+IP_HLEN(IP)); + orig_checksum = ICMP->checksum; + ICMP->checksum = 0; + ICMP->checksum = ip_checksum((uint16 *)ICMP, ntohs(IP->total_len)-IP_HLEN(IP)); + if (orig_checksum != ICMP->checksum) + eth_packet_trace (dev, msg, len, "reading ICMP header Checksum Fixed"); + break; } } static void _eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data) { - ETH_DEV* dev = (ETH_DEV*) info; +ETH_DEV* dev = (ETH_DEV*) info; +int to_me; +int from_me = 0; +int i; +int bpf_used; + +switch (dev->eth_api) { + case ETH_API_PCAP: #ifdef USE_BPF - int to_me = 1; - - /* AUTODIN II hash mode? */ - if ((dev->hash_filter) && (data[0] & 0x01) && (!dev->promiscuous) && (!dev->all_multicast)) - to_me = _eth_hash_lookup(dev->hash, data); -#else /* !USE_BPF */ - int to_me = 0; - int from_me = 0; - int i; - - eth_packet_trace (dev, data, header->len, "received"); -f - for (i = 0; i < dev->addr_count; i++) { - if (memcmp(data, dev->filter_address[i], 6) == 0) to_me = 1; - if (memcmp(&data[6], dev->filter_address[i], 6) == 0) from_me = 1; - } - - /* all multicast mode? */ - if (dev->all_multicast && (data[0] & 0x01)) to_me = 1; - - /* promiscuous mode? */ - if (dev->promiscuous) to_me = 1; - - /* AUTODIN II hash mode? */ - if ((dev->hash_filter) && (!to_me) && (data[0] & 0x01)) - to_me = _eth_hash_lookup(dev->hash, data); + bpf_used = 1; + to_me = 1; + /* AUTODIN II hash mode? */ + if ((dev->hash_filter) && (data[0] & 0x01) && (!dev->promiscuous) && (!dev->all_multicast)) + to_me = _eth_hash_lookup(dev->hash, data); + break; #endif /* USE_BPF */ + case ETH_API_TAP: + case ETH_API_VDE: + bpf_used = 0; + to_me = 0; + eth_packet_trace (dev, data, header->len, "received"); - /* detect reception of loopback packet to our physical address */ - if (LOOPBACK_SELF_FRAME(dev->physical_addr, data)) { + for (i = 0; i < dev->addr_count; i++) { + if (memcmp(data, dev->filter_address[i], 6) == 0) to_me = 1; + if (memcmp(&data[6], dev->filter_address[i], 6) == 0) from_me = 1; + } + + /* all multicast mode? */ + if (dev->all_multicast && (data[0] & 0x01)) to_me = 1; + + /* promiscuous mode? */ + if (dev->promiscuous) to_me = 1; + + /* AUTODIN II hash mode? */ + if ((dev->hash_filter) && (!to_me) && (data[0] & 0x01)) + to_me = _eth_hash_lookup(dev->hash, data); + break; + } + +/* detect reception of loopback packet to our physical address */ +if (LOOPBACK_SELF_FRAME(dev->physical_addr, data)) { #ifdef USE_READER_THREAD - pthread_mutex_lock (&dev->self_lock); + pthread_mutex_lock (&dev->self_lock); #endif - dev->loopback_self_rcvd_total++; - /* lower reflection count - if already zero, pass it on */ - if (dev->loopback_self_sent > 0) { - eth_packet_trace (dev, data, header->len, "ignored"); - dev->loopback_self_sent--; - to_me = 0; - } -#ifndef USE_BPF - else + dev->loopback_self_rcvd_total++; + /* lower reflection count - if already zero, pass it on */ + if (dev->loopback_self_sent > 0) { + eth_packet_trace (dev, data, header->len, "ignored"); + dev->loopback_self_sent--; + to_me = 0; + } + else + if (!bpf_used) from_me = 0; -#endif #ifdef USE_READER_THREAD - pthread_mutex_unlock (&dev->self_lock); + pthread_mutex_unlock (&dev->self_lock); #endif } -#ifdef USE_BPF - if (to_me) { -#else /* !USE_BPF */ - if (to_me && !from_me) { -#endif - if (header->len > ETH_MIN_JUMBO_FRAME) { - if (header->len <= header->caplen) /* Whole Frame captured? */ - _eth_fix_ip_jumbo_offload(dev, data, header->len); - else - ++dev->jumbo_truncated; - return; +if (bpf_used ? to_me : (to_me && !from_me)) { + if (header->len > ETH_MIN_JUMBO_FRAME) { + if (header->len <= header->caplen) /* Whole Frame captured? */ + _eth_fix_ip_jumbo_offload(dev, data, header->len); + else + ++dev->jumbo_truncated; + return; } #if defined (USE_READER_THREAD) - if (1) { - int crc_len = 0; - uint8 crc_data[4]; - uint32 len = header->len; - u_char* moved_data = NULL; + if (1) { + int crc_len = 0; + uint8 crc_data[4]; + uint32 len = header->len; + u_char* moved_data = NULL; - if (header->len < ETH_MIN_PACKET) { /* Pad runt packets before CRC append */ - moved_data = malloc(ETH_MIN_PACKET); - memcpy(moved_data, data, len); - memset(moved_data + len, 0, ETH_MIN_PACKET-len); - len = ETH_MIN_PACKET; - data = moved_data; + if (header->len < ETH_MIN_PACKET) { /* Pad runt packets before CRC append */ + moved_data = malloc(ETH_MIN_PACKET); + memcpy(moved_data, data, len); + memset(moved_data + len, 0, ETH_MIN_PACKET-len); + len = ETH_MIN_PACKET; + data = moved_data; } - /* If necessary, fix IP header checksums for packets originated locally */ - /* but were presumed to be traversing a NIC which was going to handle that task */ - /* This must be done before any needed CRC calculation */ - _eth_fix_ip_xsum_offload(dev, (u_char*)data, len); - - if (dev->need_crc) - crc_len = eth_get_packet_crc32_data(data, len, crc_data); - - eth_packet_trace (dev, data, len, "rcvqd"); - - pthread_mutex_lock (&dev->lock); - ethq_insert_data(&dev->read_queue, 2, data, 0, len, crc_len, crc_data, 0); - pthread_mutex_unlock (&dev->lock); - free(moved_data); - } -#else - /* set data in passed read packet */ - dev->read_packet->len = header->len; - memcpy(dev->read_packet->msg, data, header->len); - /* Handle runt case and pad with zeros. */ - /* The real NIC won't hand us runts from the wire, BUT we may be getting */ - /* some packets looped back before they actually traverse the wire */ - /* (by an internal bridge device for instance) */ - if (header->len < ETH_MIN_PACKET) { - memset(&dev->read_packet->msg[header->len], 0, ETH_MIN_PACKET-header->len); - dev->read_packet->len = ETH_MIN_PACKET; - } - /* If necessary, fix IP header checksums for packets originated by the local host */ + /* If necessary, fix IP header checksums for packets originated locally */ /* but were presumed to be traversing a NIC which was going to handle that task */ /* This must be done before any needed CRC calculation */ - _eth_fix_ip_xsum_offload(dev, dev->read_packet->msg, dev->read_packet->len); + _eth_fix_ip_xsum_offload(dev, (u_char*)data, len); + if (dev->need_crc) - dev->read_packet->crc_len = eth_add_packet_crc32(dev->read_packet->msg, dev->read_packet->len); - else - dev->read_packet->crc_len = 0; + crc_len = eth_get_packet_crc32_data(data, len, crc_data); - eth_packet_trace (dev, dev->read_packet->msg, dev->read_packet->len, "reading"); + eth_packet_trace (dev, data, len, "rcvqd"); - /* call optional read callback function */ - if (dev->read_callback) - (dev->read_callback)(0); + pthread_mutex_lock (&dev->lock); + ethq_insert_data(&dev->read_queue, 2, data, 0, len, crc_len, crc_data, 0); + pthread_mutex_unlock (&dev->lock); + free(moved_data); + } +#else /* !USE_READER_THREAD */ + /* set data in passed read packet */ + dev->read_packet->len = header->len; + memcpy(dev->read_packet->msg, data, header->len); + /* Handle runt case and pad with zeros. */ + /* The real NIC won't hand us runts from the wire, BUT we may be getting */ + /* some packets looped back before they actually traverse the wire */ + /* (by an internal bridge device for instance) */ + if (header->len < ETH_MIN_PACKET) { + memset(&dev->read_packet->msg[header->len], 0, ETH_MIN_PACKET-header->len); + dev->read_packet->len = ETH_MIN_PACKET; + } + /* If necessary, fix IP header checksums for packets originated by the local host */ + /* but were presumed to be traversing a NIC which was going to handle that task */ + /* This must be done before any needed CRC calculation */ + _eth_fix_ip_xsum_offload(dev, dev->read_packet->msg, dev->read_packet->len); + if (dev->need_crc) + dev->read_packet->crc_len = eth_add_packet_crc32(dev->read_packet->msg, dev->read_packet->len); + else + dev->read_packet->crc_len = 0; + + eth_packet_trace (dev, dev->read_packet->msg, dev->read_packet->len, "reading"); + + /* call optional read callback function */ + if (dev->read_callback) + (dev->read_callback)(0); #endif } } int eth_read(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) { - int status; +int status; - /* make sure device exists */ +/* make sure device exists */ - if (!dev) return 0; +if (!dev) return 0; - /* make sure packet exists */ - if (!packet) return 0; +/* make sure packet exists */ +if (!packet) return 0; - packet->len = 0; +packet->len = 0; #if !defined (USE_READER_THREAD) - /* set read packet */ - dev->read_packet = packet; +/* set read packet */ +dev->read_packet = packet; - /* set optional callback routine */ - dev->read_callback = routine; +/* set optional callback routine */ +dev->read_callback = routine; - /* dispatch read request to either receive a filtered packet or timeout */ - do { - if (dev->pcap_mode) +/* dispatch read request to either receive a filtered packet or timeout */ +do { + switch (dev->eth_api) { + case ETH_API_PCAP: status = pcap_dispatch((pcap_t*)dev->handle, 1, &_eth_callback, (u_char*)dev); + break; #ifdef USE_TAP_NETWORK - else { - struct pcap_pkthdr header; - int len; - u_char buf[ETH_MAX_JUMBO_FRAME]; + case ETH_API_TAP: + if (1) { + struct pcap_pkthdr header; + int len; + u_char buf[ETH_MAX_JUMBO_FRAME]; - memset(&header, 0, sizeof(header)); - len = read(dev->fd_handle, buf, sizeof(buf)); - if (len > 0) { - status = 1; - header.caplen = header.len = len; - _eth_callback((u_char *)dev, &header, buf); - } else { - status = 0; - } - } + memset(&header, 0, sizeof(header)); + len = read(dev->fd_handle, buf, sizeof(buf)); + if (len > 0) { + status = 1; + header.caplen = header.len = len; + _eth_callback((u_char *)dev, &header, buf); + } + else + status = 0; + } + break; #endif /* USE_TAP_NETWORK */ +#ifdef USE_VDE_NETWORK + case ETH_API_VDE: + if (1) { + struct pcap_pkthdr header; + int len; + u_char buf[ETH_MAX_JUMBO_FRAME]; + + memset(&header, 0, sizeof(header)); + len = vde_recv((VDECONN*)dev->handle, buf, sizeof(buf), 0); + if (len > 0) { + status = 1; + header.caplen = header.len = len; + _eth_callback((u_char *)dev, &header, buf); + } + else + status = 0; + } + break; +#endif /* USE_VDE_NETWORK */ + } } while ((status) && (0 == packet->len)); #else /* USE_READER_THREAD */ - status = 0; - pthread_mutex_lock (&dev->lock); - if (dev->read_queue.count > 0) { - ETH_ITEM* item = &dev->read_queue.item[dev->read_queue.head]; - packet->len = item->packet.len; - packet->crc_len = item->packet.crc_len; - memcpy(packet->msg, item->packet.msg, ((packet->len > packet->crc_len) ? packet->len : packet->crc_len)); - status = 1; - ethq_remove(&dev->read_queue); - } - pthread_mutex_unlock (&dev->lock); - if ((status) && (routine)) - routine(0); + status = 0; + pthread_mutex_lock (&dev->lock); + if (dev->read_queue.count > 0) { + ETH_ITEM* item = &dev->read_queue.item[dev->read_queue.head]; + packet->len = item->packet.len; + packet->crc_len = item->packet.crc_len; + memcpy(packet->msg, item->packet.msg, ((packet->len > packet->crc_len) ? packet->len : packet->crc_len)); + status = 1; + ethq_remove(&dev->read_queue); + } + pthread_mutex_unlock (&dev->lock); + if ((status) && (routine)) + routine(0); #endif - return status; +return status; } t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, @@ -2351,181 +2493,182 @@ t_stat eth_filter_hash(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, ETH_BOOL all_multicast, ETH_BOOL promiscuous, ETH_MULTIHASH* const hash) { - int i; - bpf_u_int32 bpf_subnet, bpf_netmask; - char buf[114+66*ETH_FILTER_MAX]; - char errbuf[PCAP_ERRBUF_SIZE]; - char mac[20]; - char* buf2; - t_stat status; +int i; +bpf_u_int32 bpf_subnet, bpf_netmask; +char buf[114+66*ETH_FILTER_MAX]; +char errbuf[PCAP_ERRBUF_SIZE]; +char mac[20]; +char* buf2; +t_stat status; #ifdef USE_BPF - struct bpf_program bpf; - char* msg; +struct bpf_program bpf; +char* msg; #endif - /* make sure device exists */ - if (!dev) return SCPE_UNATT; +/* make sure device exists */ +if (!dev) return SCPE_UNATT; - /* filter count OK? */ - if ((addr_count < 0) || (addr_count > ETH_FILTER_MAX)) - return SCPE_ARG; - else - if (!addresses) return SCPE_ARG; +/* filter count OK? */ +if ((addr_count < 0) || (addr_count > ETH_FILTER_MAX)) + return SCPE_ARG; +else + if (!addresses) return SCPE_ARG; - /* test reflections. This is done early in this routine since eth_reflect */ - /* calls eth_filter recursively and thus changes the state of the device. */ - if (dev->reflections == -1) - status = eth_reflect(dev); +/* test reflections. This is done early in this routine since eth_reflect */ +/* calls eth_filter recursively and thus changes the state of the device. */ +if (dev->reflections == -1) + status = eth_reflect(dev); - /* set new filter addresses */ - for (i = 0; i < addr_count; i++) - memcpy(dev->filter_address[i], addresses[i], sizeof(ETH_MAC)); - dev->addr_count = addr_count; +/* set new filter addresses */ +for (i = 0; i < addr_count; i++) + memcpy(dev->filter_address[i], addresses[i], sizeof(ETH_MAC)); +dev->addr_count = addr_count; - /* store other flags */ - dev->all_multicast = all_multicast; - dev->promiscuous = promiscuous; +/* store other flags */ +dev->all_multicast = all_multicast; +dev->promiscuous = promiscuous; - /* store multicast hash data */ - dev->hash_filter = (hash != NULL); - if (hash) { - memcpy(dev->hash, hash, sizeof(*hash)); - sim_debug(dev->dbit, dev->dptr, "Multicast Hash: %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n", - dev->hash[0], dev->hash[1], dev->hash[2], dev->hash[3], - dev->hash[4], dev->hash[5], dev->hash[6], dev->hash[7]); +/* store multicast hash data */ +dev->hash_filter = (hash != NULL); +if (hash) { + memcpy(dev->hash, hash, sizeof(*hash)); + sim_debug(dev->dbit, dev->dptr, "Multicast Hash: %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n", + dev->hash[0], dev->hash[1], dev->hash[2], dev->hash[3], + dev->hash[4], dev->hash[5], dev->hash[6], dev->hash[7]); } - /* print out filter information if debugging */ - if (dev->dptr->dctrl & dev->dbit) { - sim_debug(dev->dbit, dev->dptr, "Filter Set\n"); - for (i = 0; i < addr_count; i++) { - char mac[20]; - eth_mac_fmt(&dev->filter_address[i], mac); - sim_debug(dev->dbit, dev->dptr, " Addr[%d]: %s\n", i, mac); +/* print out filter information if debugging */ +if (dev->dptr->dctrl & dev->dbit) { + sim_debug(dev->dbit, dev->dptr, "Filter Set\n"); + for (i = 0; i < addr_count; i++) { + char mac[20]; + eth_mac_fmt(&dev->filter_address[i], mac); + sim_debug(dev->dbit, dev->dptr, " Addr[%d]: %s\n", i, mac); } - if (dev->all_multicast) - sim_debug(dev->dbit, dev->dptr, "All Multicast\n"); - if (dev->promiscuous) - sim_debug(dev->dbit, dev->dptr, "Promiscuous\n"); + if (dev->all_multicast) + sim_debug(dev->dbit, dev->dptr, "All Multicast\n"); + if (dev->promiscuous) + sim_debug(dev->dbit, dev->dptr, "Promiscuous\n"); } - /* setup BPF filters and other fields to minimize packet delivery */ - strcpy(buf, ""); +/* setup BPF filters and other fields to minimize packet delivery */ +strcpy(buf, ""); - /* construct destination filters - since the real ethernet interface was set - into promiscuous mode by eth_open(), we need to filter out the packets that - our simulated interface doesn't want. */ - if (!dev->promiscuous) { - for (i = 0; i < addr_count; i++) { - eth_mac_fmt(&dev->filter_address[i], mac); - if (!strstr(buf, mac)) /* eliminate duplicates */ - sprintf(&buf[strlen(buf)], "%s(ether dst %s)", (*buf) ? " or " : "((", mac); +/* construct destination filters - since the real ethernet interface was set + into promiscuous mode by eth_open(), we need to filter out the packets that + our simulated interface doesn't want. */ +if (!dev->promiscuous) { + for (i = 0; i < addr_count; i++) { + eth_mac_fmt(&dev->filter_address[i], mac); + if (!strstr(buf, mac)) /* eliminate duplicates */ + sprintf(&buf[strlen(buf)], "%s(ether dst %s)", (*buf) ? " or " : "((", mac); } - if (dev->all_multicast || dev->hash_filter) - sprintf(&buf[strlen(buf)], "%s(ether multicast)", (*buf) ? " or " : "(("); - if (strlen(buf) > 0) - sprintf(&buf[strlen(buf)], ")"); - } - - /* construct source filters - this prevents packets from being reflected back - by systems where WinPcap and libpcap cause packet reflections. Note that - some systems do not reflect packets at all. This *assumes* that the - simulated NIC will not send out packets with multicast source fields. */ - if ((addr_count > 0) && (dev->reflections > 0)) { - if (strlen(buf) > 0) - sprintf(&buf[strlen(buf)], " and "); - sprintf (&buf[strlen(buf)], "not ("); - buf2 = &buf[strlen(buf)]; - for (i = 0; i < addr_count; i++) { - if (dev->filter_address[i][0] & 0x01) continue; /* skip multicast addresses */ - eth_mac_fmt(&dev->filter_address[i], mac); - if (!strstr(buf2, mac)) /* eliminate duplicates */ - sprintf(&buf2[strlen(buf2)], "%s(ether src %s)", (*buf2) ? " or " : "", mac); - } - sprintf (&buf[strlen(buf)], ")"); - } + if (dev->all_multicast || dev->hash_filter) + sprintf(&buf[strlen(buf)], "%s(ether multicast)", (*buf) ? " or " : "(("); if (strlen(buf) > 0) sprintf(&buf[strlen(buf)], ")"); - /* When changing the Physical Address on a LAN interface, VMS sends out a - loopback packet with the source and destination addresses set to the same - value as the Physical Address which is being setup. This packet is - designed to find and help diagnose MAC address conflicts (which also - include DECnet address conflicts). Normally, this packet would not be - seen by the sender, only by the other machine that has the same Physical - Address (or possibly DECnet address). If the ethernet subsystem is - reflecting packets, the network startup will fail to start if it sees the - reflected packet, since it thinks another system is using this Physical - Address (or DECnet address). We have to let these packets through, so - that if another machine has the same Physical Address (or DECnet address) - that we can detect it. Both eth_write() and _eth_callback() help by - checking the reflection count - eth_write() adds the reflection count to - dev->loopback_self_sent, and _eth_callback() check the value - if the - dev->loopback_self_sent count is zero, then the packet has come from - another machine with the same address, and needs to be passed on to the - simulated machine. */ - memset(dev->physical_addr, 0, sizeof(ETH_MAC)); - dev->loopback_self_sent = 0; - /* check for physical address in filters */ - if ((addr_count) && (dev->reflections > 0)) { - for (i = 0; i < addr_count; i++) { - if (dev->filter_address[i][0]&1) - continue; /* skip all multicast addresses */ - eth_mac_fmt(&dev->filter_address[i], mac); - if (strcmp(mac, "00:00:00:00:00:00") != 0) { - memcpy(dev->physical_addr, &dev->filter_address[i], sizeof(ETH_MAC)); - /* let packets through where dst and src are the same as our physical address */ - sprintf (&buf[strlen(buf)], " or ((ether dst %s) and (ether src %s))", mac, mac); - break; + } + +/* construct source filters - this prevents packets from being reflected back + by systems where WinPcap and libpcap cause packet reflections. Note that + some systems do not reflect packets at all. This *assumes* that the + simulated NIC will not send out packets with multicast source fields. */ +if ((addr_count > 0) && (dev->reflections > 0)) { + if (strlen(buf) > 0) + sprintf(&buf[strlen(buf)], " and "); + sprintf (&buf[strlen(buf)], "not ("); + buf2 = &buf[strlen(buf)]; + for (i = 0; i < addr_count; i++) { + if (dev->filter_address[i][0] & 0x01) continue; /* skip multicast addresses */ + eth_mac_fmt(&dev->filter_address[i], mac); + if (!strstr(buf2, mac)) /* eliminate duplicates */ + sprintf(&buf2[strlen(buf2)], "%s(ether src %s)", (*buf2) ? " or " : "", mac); + } + sprintf (&buf[strlen(buf)], ")"); + } +if (strlen(buf) > 0) + sprintf(&buf[strlen(buf)], ")"); +/* When changing the Physical Address on a LAN interface, VMS sends out a + loopback packet with the source and destination addresses set to the same + value as the Physical Address which is being setup. This packet is + designed to find and help diagnose MAC address conflicts (which also + include DECnet address conflicts). Normally, this packet would not be + seen by the sender, only by the other machine that has the same Physical + Address (or possibly DECnet address). If the ethernet subsystem is + reflecting packets, the network startup will fail to start if it sees the + reflected packet, since it thinks another system is using this Physical + Address (or DECnet address). We have to let these packets through, so + that if another machine has the same Physical Address (or DECnet address) + that we can detect it. Both eth_write() and _eth_callback() help by + checking the reflection count - eth_write() adds the reflection count to + dev->loopback_self_sent, and _eth_callback() check the value - if the + dev->loopback_self_sent count is zero, then the packet has come from + another machine with the same address, and needs to be passed on to the + simulated machine. */ +memset(dev->physical_addr, 0, sizeof(ETH_MAC)); +dev->loopback_self_sent = 0; +/* check for physical address in filters */ +if ((addr_count) && (dev->reflections > 0)) { + for (i = 0; i < addr_count; i++) { + if (dev->filter_address[i][0]&1) + continue; /* skip all multicast addresses */ + eth_mac_fmt(&dev->filter_address[i], mac); + if (strcmp(mac, "00:00:00:00:00:00") != 0) { + memcpy(dev->physical_addr, &dev->filter_address[i], sizeof(ETH_MAC)); + /* let packets through where dst and src are the same as our physical address */ + sprintf (&buf[strlen(buf)], " or ((ether dst %s) and (ether src %s))", mac, mac); + break; } } } - if ((0 == strlen(buf)) && (!dev->promiscuous)) /* Empty filter means match nothing */ - strcpy(buf, "ether host fe:ff:ff:ff:ff:ff"); /* this should be a good match nothing filter */ - sim_debug(dev->dbit, dev->dptr, "BPF string is: |%s|\n", buf); +if ((0 == strlen(buf)) && (!dev->promiscuous)) /* Empty filter means match nothing */ + strcpy(buf, "ether host fe:ff:ff:ff:ff:ff"); /* this should be a good match nothing filter */ +sim_debug(dev->dbit, dev->dptr, "BPF string is: |%s|\n", buf); - /* get netmask, which is required for compiling */ - if (dev->pcap_mode && (pcap_lookupnet(dev->handle, &bpf_subnet, &bpf_netmask, errbuf)<0)) { - bpf_netmask = 0; - } +/* get netmask, which is required for compiling */ +if ((dev->eth_api == ETH_API_PCAP) && (pcap_lookupnet(dev->handle, &bpf_subnet, &bpf_netmask, errbuf)<0)) + bpf_netmask = 0; #ifdef USE_BPF - if (dev->pcap_mode) { - /* compile filter string */ - if ((status = pcap_compile(dev->handle, &bpf, buf, 1, bpf_netmask)) < 0) { +if (dev->eth_api == ETH_API_PCAP) { + /* compile filter string */ + if ((status = pcap_compile(dev->handle, &bpf, buf, 1, bpf_netmask)) < 0) { + sprintf(errbuf, "%s", pcap_geterr(dev->handle)); + msg = "Eth: pcap_compile error: %s\r\n"; + printf(msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + sim_debug(dev->dbit, dev->dptr, "Eth: pcap_compile error: %s\n", errbuf); + /* show erroneous BPF string */ + msg = "Eth: BPF string is: |%s|\r\n"; + printf (msg, buf); + if (sim_log) fprintf (sim_log, msg, buf); + } + else { + /* apply compiled filter string */ + if ((status = pcap_setfilter(dev->handle, &bpf)) < 0) { sprintf(errbuf, "%s", pcap_geterr(dev->handle)); - msg = "Eth: pcap_compile error: %s\r\n"; + msg = "Eth: pcap_setfilter error: %s\r\n"; printf(msg, errbuf); if (sim_log) fprintf (sim_log, msg, errbuf); - sim_debug(dev->dbit, dev->dptr, "Eth: pcap_compile error: %s\n", errbuf); - /* show erroneous BPF string */ - msg = "Eth: BPF string is: |%s|\r\n"; - printf (msg, buf); - if (sim_log) fprintf (sim_log, msg, buf); - } else { - /* apply compiled filter string */ - if ((status = pcap_setfilter(dev->handle, &bpf)) < 0) { - sprintf(errbuf, "%s", pcap_geterr(dev->handle)); - msg = "Eth: pcap_setfilter error: %s\r\n"; - printf(msg, errbuf); - if (sim_log) fprintf (sim_log, msg, errbuf); - sim_debug(dev->dbit, dev->dptr, "Eth: pcap_setfilter error: %s\n", errbuf); - } else { + sim_debug(dev->dbit, dev->dptr, "Eth: pcap_setfilter error: %s\n", errbuf); + } + else { #ifdef USE_SETNONBLOCK - /* set file non-blocking */ - status = pcap_setnonblock (dev->handle, 1, errbuf); + /* set file non-blocking */ + status = pcap_setnonblock (dev->handle, 1, errbuf); #endif /* USE_SETNONBLOCK */ } - pcap_freecode(&bpf); + pcap_freecode(&bpf); } #ifdef USE_READER_THREAD - pthread_mutex_lock (&dev->lock); - ethq_clear (&dev->read_queue); /* Empty FIFO Queue when filter list changes */ - pthread_mutex_unlock (&dev->lock); + pthread_mutex_lock (&dev->lock); + ethq_clear (&dev->read_queue); /* Empty FIFO Queue when filter list changes */ + pthread_mutex_unlock (&dev->lock); #endif } #endif /* USE_BPF */ - return SCPE_OK; +return SCPE_OK; } /* @@ -2549,149 +2692,157 @@ t_stat eth_filter_hash(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, */ int eth_host_devices(int used, int max, ETH_LIST* list) { - pcap_t* conn; - int i, j, datalink; - char errbuf[PCAP_ERRBUF_SIZE]; +pcap_t* conn; +int i, j, datalink; +char errbuf[PCAP_ERRBUF_SIZE]; - for (i=0; i sizeof(regval))) { - RegCloseKey (reghnd); - continue; + /* make sure value is the right type, bail if not acceptable */ + if ((regtype != REG_SZ) || (reglen > sizeof(regval))) { + RegCloseKey (reghnd); + continue; } - /* registry value seems OK, finish up and replace description */ - RegCloseKey (reghnd ); - sprintf (list[i].desc, "%s", regval); + /* registry value seems OK, finish up and replace description */ + RegCloseKey (reghnd ); + sprintf (list[i].desc, "%s", regval); } } /* for */ #endif #ifdef USE_TAP_NETWORK - if (used < max) { - list[used].num = used; +if (used < max) { + list[used].num = used; #if defined(__OpenBSD__) - sprintf(list[used].name, "%s", "tap:tunN"); + sprintf(list[used].name, "%s", "tap:tunN"); #else - sprintf(list[used].name, "%s", "tap:tapN"); + sprintf(list[used].name, "%s", "tap:tapN"); #endif - sprintf(list[used].desc, "%s", "Integrated Tun/Tap support"); - ++used; + sprintf(list[used].desc, "%s", "Integrated Tun/Tap support"); + ++used; + } +#endif +#ifdef USE_VDE_NETWORK +if (used < max) { + list[used].num = used; + sprintf(list[used].name, "%s", "vde:device"); + sprintf(list[used].desc, "%s", "Integrated VDE support"); + ++used; } #endif - return used; +return used; } int eth_devices(int max, ETH_LIST* list) { - int i = 0; +int i = 0; #ifndef DONT_USE_PCAP_FINDALLDEVS - pcap_if_t* alldevs; - pcap_if_t* dev; - char errbuf[PCAP_ERRBUF_SIZE]; +pcap_if_t* alldevs; +pcap_if_t* dev; +char errbuf[PCAP_ERRBUF_SIZE]; - /* retrieve the device list */ - if (pcap_findalldevs(&alldevs, errbuf) == -1) { - char* msg = "Eth: error in pcap_findalldevs: %s\r\n"; - printf (msg, errbuf); - if (sim_log) fprintf (sim_log, msg, errbuf); - } else { - /* copy device list into the passed structure */ - for (i=0, dev=alldevs; dev; dev=dev->next) { - if ((dev->flags & PCAP_IF_LOOPBACK) || (!strcmp("any", dev->name))) continue; - list[i].num = i; - sprintf(list[i].name, "%s", dev->name); - if (dev->description) - sprintf(list[i].desc, "%s", dev->description); - else - sprintf(list[i].desc, "%s", "No description available"); - if (i++ >= max) break; +/* retrieve the device list */ +if (pcap_findalldevs(&alldevs, errbuf) == -1) { + char* msg = "Eth: error in pcap_findalldevs: %s\r\n"; + printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + } +else { + /* copy device list into the passed structure */ + for (i=0, dev=alldevs; dev; dev=dev->next) { + if ((dev->flags & PCAP_IF_LOOPBACK) || (!strcmp("any", dev->name))) continue; + list[i].num = i; + sprintf(list[i].name, "%s", dev->name); + if (dev->description) + sprintf(list[i].desc, "%s", dev->description); + else + sprintf(list[i].desc, "%s", "No description available"); + if (i++ >= max) break; } - /* free device list */ - pcap_freealldevs(alldevs); + /* free device list */ + pcap_freealldevs(alldevs); } #endif - /* Add any host specific devices and/or validate those already found */ - i = eth_host_devices(i, max, list); +/* Add any host specific devices and/or validate those already found */ +i = eth_host_devices(i, max, list); - /* return device count */ - return i; +/* return device count */ +return i; } void eth_show_dev (FILE *st, ETH_DEV* dev) { - fprintf(st, "Ethernet Device:\n"); - if (!dev) { - fprintf(st, "-- Not Attached\n"); - return; +fprintf(st, "Ethernet Device:\n"); +if (!dev) { + fprintf(st, "-- Not Attached\n"); + return; } - fprintf(st, " Name: %s\n", dev->name); - fprintf(st, " Reflections: %d\n", dev->reflections); - fprintf(st, " Self Loopbacks Sent: %d\n", dev->loopback_self_sent_total); - fprintf(st, " Self Loopbacks Rcvd: %d\n", dev->loopback_self_rcvd_total); - if (dev->have_host_nic_phy_addr) { - char hw_mac[20]; +fprintf(st, " Name: %s\n", dev->name); +fprintf(st, " Reflections: %d\n", dev->reflections); +fprintf(st, " Self Loopbacks Sent: %d\n", dev->loopback_self_sent_total); +fprintf(st, " Self Loopbacks Rcvd: %d\n", dev->loopback_self_rcvd_total); +if (dev->have_host_nic_phy_addr) { + char hw_mac[20]; - eth_mac_fmt(&dev->host_nic_phy_hw_addr, hw_mac); - fprintf(st, " Host NIC Address: %s\n", hw_mac); + eth_mac_fmt(&dev->host_nic_phy_hw_addr, hw_mac); + fprintf(st, " Host NIC Address: %s\n", hw_mac); } - if (dev->jumbo_dropped) - fprintf(st, " Jumbo Dropped: %d\n", dev->jumbo_dropped); - if (dev->jumbo_fragmented) - fprintf(st, " Jumbo Fragmented: %d\n", dev->jumbo_fragmented); - if (dev->jumbo_truncated) - fprintf(st, " Jumbo Truncated: %d\n", dev->jumbo_truncated); +if (dev->jumbo_dropped) + fprintf(st, " Jumbo Dropped: %d\n", dev->jumbo_dropped); +if (dev->jumbo_fragmented) + fprintf(st, " Jumbo Fragmented: %d\n", dev->jumbo_fragmented); +if (dev->jumbo_truncated) + fprintf(st, " Jumbo Truncated: %d\n", dev->jumbo_truncated); #if defined(USE_READER_THREAD) - fprintf(st, " Asynch Interrupts: %s\n", dev->asynch_io?"Enabled":"Disabled"); - if (dev->asynch_io) - fprintf(st, " Interrupt Latency: %d uSec\n", dev->asynch_io_latency); - fprintf(st, " Read Queue: Count: %d\n", dev->read_queue.count); - fprintf(st, " Read Queue: High: %d\n", dev->read_queue.high); - fprintf(st, " Read Queue: Loss: %d\n", dev->read_queue.loss); - fprintf(st, " Peak Write Queue Size: %d\n", dev->write_queue_peak); +fprintf(st, " Asynch Interrupts: %s\n", dev->asynch_io?"Enabled":"Disabled"); +if (dev->asynch_io) + fprintf(st, " Interrupt Latency: %d uSec\n", dev->asynch_io_latency); +fprintf(st, " Read Queue: Count: %d\n", dev->read_queue.count); +fprintf(st, " Read Queue: High: %d\n", dev->read_queue.high); +fprintf(st, " Read Queue: Loss: %d\n", dev->read_queue.loss); +fprintf(st, " Peak Write Queue Size: %d\n", dev->write_queue_peak); #endif } #endif /* USE_NETWORK */ diff --git a/sim_ether.h b/sim_ether.h index de155548..1478c40c 100644 --- a/sim_ether.h +++ b/sim_ether.h @@ -28,6 +28,7 @@ Modification history: + 30-Oct-11 MP Added support for vde (Virtual Distributed Ethernet) networking 18-Apr-11 MP Fixed race condition with self loopback packets in multithreaded environments 09-Dec-10 MP Added support to determine if network address conflicts exist @@ -89,13 +90,13 @@ #if defined (USE_READER_THREAD) #if defined (USE_SETNONBLOCK) #undef USE_SETNONBLOCK -#endif +#endif /* USE_SETNONBLOCK */ #undef PCAP_READ_TIMEOUT #define PCAP_READ_TIMEOUT 15 -#if !defined (xBSD) && !defined(_WIN32) && !defined(VMS) -#define MUST_DO_SELECT -#endif +#if (!defined (xBSD) && !defined(_WIN32) && !defined(VMS)) || defined (USE_TAP_NETWORK) || defined (USE_VDE_NETWORK) +#define MUST_DO_SELECT 1 #endif +#endif /* USE_READER_THREAD */ /* USE_BPF is defined to let this code leverage the libpcap/OS kernel provided @@ -175,7 +176,10 @@ struct eth_device { char* name; /* name of ethernet device */ void* handle; /* handle of implementation-specific device */ int fd_handle; /* fd to kernel device (where needed) */ - int pcap_mode; /* Flag indicating if pcap API are being used to move packets */ + int eth_api; /* Designator for which API is being used to move packets */ +#define ETH_API_PCAP 0 /* Pcap API in use */ +#define ETH_API_TAP 1 /* tun/tap API in use */ +#define ETH_API_VDE 2 /* VDE API in use */ ETH_PCALLBACK read_callback; /* read callback function */ ETH_PCALLBACK write_callback; /* write callback function */ ETH_PACK* read_packet; /* read packet */