From 834e986eafd6202e731b11221bf52e488f88b2af Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Thu, 5 Dec 2013 11:58:58 -0800 Subject: [PATCH] ETHER,VAX,PDP11: Added UDP as a link type for Ethernet packet connectivity. This will allow a single simulator to directly connect to HECnet systems without needing an external bridge program. --- README.md | 2 + sim_ether.c | 167 +++++++++++++++++++++++++++++++++++++++++----------- sim_ether.h | 5 +- sim_sock.c | 79 ++++++++++++++++++++++--- sim_sock.h | 1 + 5 files changed, 210 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index f7ea35ea..2bbd9817 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,8 @@ A remote console session will close when an EOF character is entered (i.e. ^D or when connected to serial ports). DHU11 (device VH) on Unibus systems now has 16 ports per multiplexer. XQ devices (DEQNA, DELQA and DELQA-T) are bootable on Qbus PDP11 simulators + XQ and XU devices (DEQNA, DELQA, DELQA-T, DEUNA and DELQA) devices can now + directly communicate to a remote device via UDP (i.e. a built-in HECnet bridge). MicroVAX 3900 and MicroVAX II have SET CPU AUTOBOOT option MicroVAX 3900 has a SET CPU MODEL=(MicroVAX|VAXServer) command to change between system types MicroVAX I has a SET CPU MODEL=(MicroVAX|VAXSTATION) command to change between system types diff --git a/sim_ether.c b/sim_ether.c index 143ee168..2abb47bb 100644 --- a/sim_ether.c +++ b/sim_ether.c @@ -1410,6 +1410,8 @@ static void eth_get_nic_hw_addr(ETH_DEV* dev, char *devname) { memset(&dev->host_nic_phy_hw_addr, 0, sizeof(dev->host_nic_phy_hw_addr)); dev->have_host_nic_phy_addr = 0; + if (dev->eth_api != ETH_API_PCAP) + return; #if defined(_WIN32) || defined(__CYGWIN__) if (!pcap_mac_if_win32(devname, dev->host_nic_phy_hw_addr)) dev->have_host_nic_phy_addr = 1; @@ -1426,8 +1428,6 @@ static void eth_get_nic_hw_addr(ETH_DEV* dev, char *devname) "egrep [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]", NULL}; - if (0 == strncmp("vde:", devname, 4)) - return; memset(command, 0, sizeof(command)); for (i=0; patterns[i] && (0 == dev->have_host_nic_phy_addr); ++i) { snprintf(command, sizeof(command)-1, "ifconfig %s | %s >NIC.hwaddr", devname, patterns[i]); @@ -1484,12 +1484,12 @@ ETH_DEV* volatile dev = (ETH_DEV*)arg; int status = 0; int sched_policy; struct sched_param sched_priority; -#if defined (_WIN32) -HANDLE hWait = pcap_getevent ((pcap_t*)dev->handle); -#else int sel_ret; int do_select = 0; -int select_fd = 0; +SOCKET select_fd = 0; +#if defined (_WIN32) +HANDLE hWait = (dev->eth_api == ETH_API_PCAP) ? pcap_getevent ((pcap_t*)dev->handle) : NULL; +#endif switch (dev->eth_api) { case ETH_API_PCAP: @@ -1500,11 +1500,11 @@ switch (dev->eth_api) { break; case ETH_API_TAP: case ETH_API_VDE: + case ETH_API_UDP: do_select = 1; select_fd = dev->fd_handle; break; } -#endif sim_debug(dev->dbit, dev->dptr, "Reader Thread Starting\n"); @@ -1515,26 +1515,31 @@ pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority); ++sched_priority.sched_priority; pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); -while (dev->handle) { +while (dev->fd_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(select_fd, &setl); - timeout.tv_sec = 0; - timeout.tv_usec = 250*1000; - sel_ret = select(1+select_fd, &setl, NULL, NULL, &timeout); + if (dev->eth_api == ETH_API_PCAP) { + if (WAIT_OBJECT_0 == WaitForSingleObject (hWait, 250)) + sel_ret = 1; } - else - sel_ret = 1; - if (sel_ret < 0 && errno != EINTR) break; - if (sel_ret > 0) { + if (dev->eth_api == ETH_API_UDP) #endif /* _WIN32 */ - if (!dev->handle) + if (1) { + fd_set setl; + struct timeval timeout; + + if (do_select) { + FD_ZERO(&setl); + FD_SET(select_fd, &setl); + timeout.tv_sec = 0; + timeout.tv_usec = 250*1000; + 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) { + if (!dev->fd_handle) break; /* dispatch read request queue available packets */ switch (dev->eth_api) { @@ -1579,6 +1584,23 @@ while (dev->handle) { } break; #endif /* USE_VDE_NETWORK */ + case ETH_API_UDP: + if (1) { + struct pcap_pkthdr header; + int len; + u_char buf[ETH_MAX_JUMBO_FRAME]; + + memset(&header, 0, sizeof(header)); + len = (int)sim_read_sock (select_fd, (char *)buf, (int32)sizeof(buf)); + if (len > 0) { + status = 1; + header.caplen = header.len = len; + _eth_callback((u_char *)dev, &header, buf); + } + else + status = 0; + } + break; } if ((status > 0) && (dev->asynch_io)) { int wakeup_needed; @@ -1616,7 +1638,7 @@ pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); sim_debug(dev->dbit, dev->dptr, "Writer Thread Starting\n"); pthread_mutex_lock (&dev->writer_lock); -while (dev->handle) { +while (dev->fd_handle) { pthread_cond_wait (&dev->writer_cond, &dev->writer_lock); while (NULL != (request = dev->write_requests)) { /* Pull buffer off request list */ @@ -1819,17 +1841,48 @@ else } #else strncpy(errbuf, "No support for vde: network devices", sizeof(errbuf)-1); -#endif /* !defined(__linux) && !defined(USE_BSDTUNTAP) */ +#endif /* defined(USE_VDE_NETWORK) */ } 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; + if (0 == strncmp("udp:", savname, 4)) { + char localport[CBUFSIZE], host[CBUFSIZE], port[CBUFSIZE]; + char hostport[2*CBUFSIZE]; + + if (!strcmp(savname, "udp:sourceport:remotehost:remoteport")) { + msg = "Eth: Must specify actual udp host and ports(i.e. udp:1224:somehost.com:2234)\r\n"; + printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + return SCPE_OPENERR; + } + + if (SCPE_OK != sim_parse_addr_ex (savname+4, host, sizeof(host), "localhost", port, sizeof(port), localport, sizeof(localport), NULL)) + return SCPE_OPENERR; + + if (localport[0] == '\0') + strcpy (localport, port); + sprintf (hostport, "%s:%s", host, port); + if ((SCPE_OK == sim_parse_addr (hostport, NULL, 0, NULL, NULL, 0, NULL, "localhost")) && + (0 == strcmp (localport, port))) { + msg = "Eth: Must specify different udp localhost ports\r\n"; + printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + return SCPE_OPENERR; + } + dev->fd_handle = sim_connect_sock_ex (localport, hostport, NULL, NULL, TRUE); + if (INVALID_SOCKET == dev->fd_handle) + return SCPE_OPENERR; + dev->eth_api = ETH_API_UDP; + } + 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->eth_api = ETH_API_PCAP; } - dev->eth_api = ETH_API_PCAP; } if (errbuf[0]) { msg = "Eth: open error - %s\r\n"; @@ -1866,7 +1919,8 @@ if (1) { pthread_attr_t attr; #if defined(_WIN32) - pcap_setmintocopy (dev->handle, 0); + if (dev->eth_api == ETH_API_PCAP) + pcap_setmintocopy (dev->handle, 0); #endif ethq_init (&dev->read_queue, 200); /* initialize FIFO queue */ pthread_mutex_init (&dev->lock, NULL); @@ -1918,7 +1972,7 @@ t_stat eth_close(ETH_DEV* dev) { char* msg = "Eth: closed %s\r\n"; pcap_t *pcap; -int pcap_fd; +SOCKET pcap_fd; /* make sure device exists */ if (!dev) return SCPE_UNATT; @@ -1965,6 +2019,14 @@ switch (dev->eth_api) { case ETH_API_VDE: vde_close((VDECONN*)pcap); break; +#endif + case ETH_API_UDP: + sim_close_sock(pcap_fd, TRUE); + break; +#ifdef USE_SLIRP_NETWORK + case ETH_API_NAT: + vde_close((VDECONN*)pcap); + break; #endif } printf (msg, dev->name); @@ -2178,6 +2240,9 @@ if ((packet->len >= ETH_MIN_PACKET) && (packet->len <= ETH_MAX_PACKET)) { status = 1; break; #endif + case ETH_API_UDP: + status = (((int32)packet->len == sim_write_sock (dev->fd_handle, (char *)packet->msg, (int32)packet->len)) ? 0 : -1); + break; } ++dev->packets_sent; /* basic bookkeeping */ /* On error, correct loopback bookkeeping */ @@ -2721,6 +2786,7 @@ switch (dev->eth_api) { #endif /* USE_BPF */ case ETH_API_TAP: case ETH_API_VDE: + case ETH_API_UDP: bpf_used = 0; to_me = 0; eth_packet_trace (dev, data, header->len, "received"); @@ -2902,6 +2968,23 @@ do { } break; #endif /* USE_VDE_NETWORK */ + case ETH_API_UDP: + if (1) { + struct pcap_pkthdr header; + int len; + u_char buf[ETH_MAX_JUMBO_FRAME]; + + memset(&header, 0, sizeof(header)); + len = (int)sim_read_sock (dev->fd_handle, buf, (int32)sizeof(buf)); + if (len > 0) { + status = 1; + header.caplen = header.len = len; + _eth_callback((u_char *)dev, &header, buf); + } + else + status = 0; + } + break; } } while ((status) && (0 == packet->len)); @@ -3225,6 +3308,20 @@ if (used < max) { } #endif +if (used < max) { + sprintf(list[used].name, "%s", "udp:sourceport:remotehost:remoteport"); + sprintf(list[used].desc, "%s", "Integrated UDP bridge support"); + ++used; + } + +#ifdef USE_SLIRP_NETWORK +if (used < max) { + sprintf(list[used].name, "%s", "nat:device"); + sprintf(list[used].desc, "%s", "Integrated User Mode NAT support"); + ++used; + } +#endif + return used; } diff --git a/sim_ether.h b/sim_ether.h index f1103d25..cff4d08e 100644 --- a/sim_ether.h +++ b/sim_ether.h @@ -67,6 +67,7 @@ #define SIM_ETHER_H #include "sim_defs.h" +#include "sim_sock.h" /* make common BSD code a bit easier to read in this file */ /* OS/X seems to define and compile using one of these BSD types */ @@ -212,11 +213,13 @@ typedef struct eth_item ETH_ITEM; 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) */ + SOCKET fd_handle; /* fd to kernel device (where needed) */ 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 */ +#define ETH_API_UDP 3 /* UDP API in use */ +#define ETH_API_NAT 4 /* NAT (SLiRP) API in use */ ETH_PCALLBACK read_callback; /* read callback function */ ETH_PCALLBACK write_callback; /* write callback function */ ETH_PACK* read_packet; /* read packet */ diff --git a/sim_sock.c b/sim_sock.c index 596f1bb8..986e70e0 100644 --- a/sim_sock.c +++ b/sim_sock.c @@ -551,12 +551,19 @@ char *hostp, *portp; char *endc; unsigned long portval; -if ((cptr == NULL) || (*cptr == 0)) - return SCPE_ARG; if ((host != NULL) && (host_len != 0)) memset (host, 0, host_len); if ((port != NULL) && (port_len != 0)) memset (port, 0, port_len); +if ((cptr == NULL) || (*cptr == 0)) { + if (((default_host == NULL) || (*default_host == 0)) && ((default_port == NULL) || (*default_port == 0))) + return SCPE_ARG; + if ((strlen(default_host) >= host_len) || (strlen(default_port) >= port_len)) + return SCPE_ARG; /* no room */ + strcpy (host, default_host); + strcpy (port, default_port); + return SCPE_OK; + } gbuf[sizeof(gbuf)-1] = '\0'; strncpy (gbuf, cptr, sizeof(gbuf)-1); hostp = gbuf; /* default addr */ @@ -622,7 +629,7 @@ if (host) { /* host wanted? */ } } if (validate_addr) { - struct addrinfo *ai_host, *ai_validate, *ai; + struct addrinfo *ai_host, *ai_validate, *ai, *aiv; t_stat status; if (hostp == NULL) @@ -635,11 +642,13 @@ if (validate_addr) { } status = SCPE_ARG; for (ai = ai_host; ai != NULL; ai = ai->ai_next) { - if ((ai->ai_addrlen == ai_validate->ai_addrlen) && - (ai->ai_family == ai_validate->ai_family) && - (0 == memcmp (ai->ai_addr, ai_validate->ai_addr, ai->ai_addrlen))) { - status = SCPE_OK; - break; + for (aiv = ai_validate; aiv != NULL; aiv = aiv->ai_next) { + if ((ai->ai_addrlen == aiv->ai_addrlen) && + (ai->ai_family == aiv->ai_family) && + (0 == memcmp (ai->ai_addr, aiv->ai_addr, ai->ai_addrlen))) { + status = SCPE_OK; + break; + } } } if (status != SCPE_OK) { @@ -657,6 +666,60 @@ if (validate_addr) { return SCPE_OK; } +/* sim_parse_addr_ex localport:host:port + + Presumption is that the input, if it doesn't contain a ':' character is a port specifier. + If the host field contains one or more colon characters (i.e. it is an IPv6 address), + the IPv6 address MUST be enclosed in square bracket characters (i.e. Domain Literal format) + + llll:w.x.y.z:rrrr + llll:name.domain.com:rrrr + llll::rrrr + rrrr + w.x.y.z:rrrr + [w.x.y.z]:rrrr + name.domain.com:rrrr + + Inputs: + cptr = pointer to input string + default_host + = optional pointer to default host if none specified + host_len = length of host buffer + default_port + = optional pointer to default port if none specified + port_len = length of port buffer + + Outputs: + host = pointer to buffer for IP address (may be NULL), 0 = none + port = pointer to buffer for IP port (may be NULL), 0 = none + localport + = pointer to buffer for local IP port (may be NULL), 0 = none + result = status (SCPE_OK on complete success or SCPE_ARG if + parsing can't happen due to bad syntax, a value is + out of range, a result can't fit into a result buffer, + a service name doesn't exist, or a validation name + doesn't match the parsed host) +*/ +t_stat sim_parse_addr_ex (const char *cptr, char *host, size_t hostlen, const char *default_host, char *port, size_t port_len, char *localport, size_t localport_len, const char *default_port) +{ +char *hostp; + +if ((localport != NULL) && (localport_len != 0)) + memset (localport, 0, localport_len); +hostp = strchr (cptr, ':'); +if ((hostp != NULL) && ((hostp[1] == '[') || (NULL != strchr (hostp+1, ':')))) { + if ((localport != NULL) && (localport_len != 0)) { + localport_len -= 1; + if (localport_len > (size_t)(hostp-cptr)) + localport_len = (size_t)(hostp-cptr); + memcpy (localport, cptr, localport_len); + } + return sim_parse_addr (hostp+1, host, hostlen, default_host, port, port_len, default_port, NULL); + } +return sim_parse_addr (cptr, host, hostlen, default_host, port, port_len, default_port, NULL); +} + + void sim_init_sock (void) { #if defined (_WIN32) diff --git a/sim_sock.h b/sim_sock.h index 6be04067..f7aa14b0 100644 --- a/sim_sock.h +++ b/sim_sock.h @@ -102,6 +102,7 @@ #endif t_stat sim_parse_addr (const char *cptr, char *host, size_t hostlen, const char *default_host, char *port, size_t port_len, const char *default_port, const char *validate_addr); +t_stat sim_parse_addr_ex (const char *cptr, char *host, size_t hostlen, const char *default_host, char *port, size_t port_len, char *localport, size_t local_port_len, const char *default_port); SOCKET sim_master_sock (const char *hostport, t_stat *parse_status); SOCKET sim_connect_sock (const char *hostport, const char *default_host, const char *default_port); SOCKET sim_connect_sock_ex (const char *sourcehostport, const char *hostport, const char *default_host, const char *default_port, t_bool datagram);