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.
This commit is contained in:
parent
c1aa85d944
commit
834e986eaf
5 changed files with 210 additions and 44 deletions
|
@ -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
|
||||
|
|
127
sim_ether.c
127
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,10 +1515,15 @@ 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
|
||||
if (dev->eth_api == ETH_API_PCAP) {
|
||||
if (WAIT_OBJECT_0 == WaitForSingleObject (hWait, 250))
|
||||
sel_ret = 1;
|
||||
}
|
||||
if (dev->eth_api == ETH_API_UDP)
|
||||
#endif /* _WIN32 */
|
||||
if (1) {
|
||||
fd_set setl;
|
||||
struct timeval timeout;
|
||||
|
||||
|
@ -1532,9 +1537,9 @@ while (dev->handle) {
|
|||
else
|
||||
sel_ret = 1;
|
||||
if (sel_ret < 0 && errno != EINTR) break;
|
||||
}
|
||||
if (sel_ret > 0) {
|
||||
#endif /* _WIN32 */
|
||||
if (!dev->handle)
|
||||
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,7 +1841,37 @@ 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 {
|
||||
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);
|
||||
|
@ -1831,6 +1883,7 @@ else
|
|||
}
|
||||
dev->eth_api = ETH_API_PCAP;
|
||||
}
|
||||
}
|
||||
if (errbuf[0]) {
|
||||
msg = "Eth: open error - %s\r\n";
|
||||
printf (msg, errbuf);
|
||||
|
@ -1866,6 +1919,7 @@ if (1) {
|
|||
pthread_attr_t attr;
|
||||
|
||||
#if defined(_WIN32)
|
||||
if (dev->eth_api == ETH_API_PCAP)
|
||||
pcap_setmintocopy (dev->handle, 0);
|
||||
#endif
|
||||
ethq_init (&dev->read_queue, 200); /* initialize FIFO queue */
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
75
sim_sock.c
75
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,13 +642,15 @@ 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))) {
|
||||
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) {
|
||||
/* be generous and allow successful validations against variations of localhost addresses */
|
||||
if (((0 == strcmp("127.0.0.1", hostp)) &&
|
||||
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue