1
0
mirror of https://github.com/simh/simh.git synced 2026-01-25 11:46:37 +00:00

Notes For V3.1-0

RESTRICTION: The FP15 and XVM features of the PDP-15 are only partially
debugged.  Do NOT enable these features for normal operations.

1. New Features in 3.1-0

1.1 SCP and libraries

- Added simulated Ethernet support for VMS, FreeBSD, Mac OS/X.
- Added status return to tmxr_putc_ln.
- Added sim_putchar_s to handle possible output stalls.

1.2 All DECtapes

- Added "DECtape off reel" error stop.

1.3 All Asynchronous Consoles

- Added support for output congestion stall if using a Telnet connection.

1.4 PDP-1

- Added Type 23 parallel drum support.

1.5 PDP-8

- Added instruction history.
- Added TSC8-75 option support for ETOS.
- Added TD8E DECtape support.

1.6 PDP-18b

- Added instruction history.
- Changed PDP-9, PDP-15 API default to enabled.

1.7 PDP-11

- Added support for 18b only Qbus devices.
- Formalized bus and addressing definitions.
- Added control to enable/disable autoconfiguration.
- Added stub support for second Unibus Ethernet controller.

1.7 Interdata 32b

- Added instruction history.

1.8 Eclipse

- Added floating point support.
- Added programmable interval timer support.

1.9 H316

- Added DMA/DMC support.
- Added fixed head disk support.
- Added moving head disk support.
- Added magtape support.

1.10 IBM 1130 (Brian Knittel)

- Added support for physical card reader, using the Cardread
interface (www.ibm1130.org/sim/downloads).
- Added support for physical printer (flushes output buffer after
each line).

2. Bugs Fixed in 3.1-0

2.1 SCP and libraries

- Fixed numerous bugs in Ethernet library.

2.2 All DECtapes

- Fixed reverse checksum value in 'read all' mode.
- Simplified (and sped up) timing.

2.3 PDP-8

- Fixed bug in RX28 read status (found by Charles Dickman).
- Fixed RX28 double density write.

2.4 PDP-18b

- Fixed autoincrement bug in PDP-4, PDP-7, PDP-9.

2.5 PDP-11/VAX

- Revised RQ MB->LBN conversion for greater accuracy.
- Fixed bug in IO configuration (found by David Hittner).
- Fixed bug with multiple RQ RAUSER drives.
- Fixed bug in second Qbus Ethernet controller interrupts.

2.6 Nova/Eclipse

- Fixed bugs in DKP flag clear, map setup, map usage (Charles Owen).
- Fixed bug in MT, reset completes despite I/O reset (Charles Owen).
- Fixed bug in MT, space operations return word count (Charles Owen).

2.7 IBM 1130 (Brian Knittel)

- Fixed bug in setting carry bit in subtract and subtract double.
- Fixed timing problem in console printer simulation.

2.8 1620

- Fixed bug in branch digit (found by Dave Babcock).

3. New Features in 3.0 vs prior releases

3.1 SCP and Libraries

- Added ASSIGN/DEASSIGN (logical name) commands.
- Changed RESTORE to unconditionally detach files.
- Added E11 and TPC format support to magtape library.
- Fixed bug in SHOW CONNECTIONS.
- Added USE_ADDR64 support.

3.2 All magtapes

- Magtapes support SIMH format, E11 format, and TPC format (read only).
- SET <tape_unit> FORMAT=format sets the specified tape unit's format.
- SHOW <tape_unit> FORMAT displays the specified tape unit's format.
- Tape format can also be set as part of the ATTACH command, using
  the -F switch.

3.3 VAX

- VAX can be compiled without USE_INT64.
- If compiled with USE_INT64 and USE_ADDR64, RQ and TQ controllers support
  files > 2GB.
- VAX ROM has speed control (SET ROM DELAY/NODELAY).

3.4 PDP-1

- Added block loader format support to LOAD.
- Changed BOOT PTR to allow loading of all of the first bank of memory.
- The LOAD command takes an optional argument specifying the memory field
  to be loaded.
- The PTR BOOT command takes its starting memory field from the TA (address
  switch) register.

3.5 PDP-18b Family

- Added PDP-4 EAE support.
- Added PDP-15 FP15 support.
- Added PDP-15 XVM support.
- Added PDP-15 "re-entrancy ECO".
- Added PDP-7, PDP-9, PDP-15 hardware RIM loader support in BOOT PTR.

4. Bugs Fixed in 3.0 vs prior releases

4.1 SCP and Libraries

- Fixed end of file problem in dep, idep.
- Fixed handling of trailing spaces in dep, idep.

4.2 VAX

- Fixed CVTfi bug: integer overflow not set if exponent out of range
- Fixed EMODx bugs:
  o First and second operands reversed
  o Separated fraction received wrong exponent
  o Overflow calculation on separated integer incorrect
  o Fraction not set to zero if exponent out of range
- Fixed interval timer and ROM access to pass power-up self-test even on very
  fast host processors (fixes from Mark Pizzolato).
- Fixed bug in user disk size (found by Chaskiel M Grundman).

4.3 1401

- Fixed mnemonic, instruction lengths, and reverse scan length check bug for MCS.
- Fixed MCE bug, BS off by 1 if zero suppress.
- Fixed chaining bug, D lost if return to SCP.
- Fixed H branch, branch occurs after continue.
- Added check for invalid 8 character MCW, LCA.
- Fixed magtape load-mode end of record response.
- Revised fetch to model hardware more closely.
- Fixed tape read end-of-record handling based on real 1401.
- Added diagnostic read (space forward).

4.4 Nova

- Fixed DSK variable size interaction with restore.
- Fixed bug in DSK set size routine.

4.5 PDP-1

- Fixed DT variable size interaction with restore.
- Updated CPU, line printer, standard devices to detect indefinite I/O wait.
- Fixed incorrect logical, missing activate, break in drum simulator.
- Fixed bugs in instruction decoding, overprinting for line printer.
- Fixed system hang if continue after PTR error.
- Fixed PTR to start/stop on successive rpa instructions.

4.6 PDP-11

- Fixed DT variable size interaction with restore.
- Fixed bug in MMR1 update (found by Tim Stark).
- Added XQ features and fixed bugs:
  o Corrected XQ interrupts on IE state transition (code by Tom Evans).
  o Added XQ interrupt clear on soft reset.
  o Removed XQ interrupt when setting XL or RL (multiple people).
  o Added SET/SHOW XQ STATS.
  o Added SHOW XQ FILTERS.
  o Added ability to split received packet into multiple buffers.
  o Added explicit runt and giant packet processing.
- Fixed bug in user disk size (found by Chaskiel M Grundman).

4.7 PDP-18B

- Fixed DT, RF variable size interaction with restore.
- Fixed MT bug in MTTR.
- Fixed bug in PDP-4 line printer overprinting.
- Fixed bug in PDP-15 memory protect/skip interaction.
- Fixed bug in RF set size routine.
- Increased PTP TIME for PDP-15 operating systems.
- Fixed priorities in PDP-15 API (differs from PDP-9).
- Fixed sign handling in PDP-15 EAE unsigned mul/div (differs from PDP-9).
- Fixed bug in CAF, clears API subsystem.

4.8 PDP-8

- Fixed DT, DF, RF, RX variable size interaction with restore.
- Fixed MT bug in SKTR.
- Fixed bug in DF, RF set size routine.

4.9 HP2100

- Fixed bug in DP (13210A controller only), DQ read status.
- Fixed bug in DP, DQ seek complete.
- Fixed DR drum sizes.
- Fixed DR variable capacity interaction with SAVE/RESTORE.

4.10 GRI

- Fixed bug in SC queue pointer management.

4.11 PDP-10

- Fixed bug in RP read header.

4.12 Ibm1130

- Fixed bugs found by APL 1130.

4.13 Altairz80

- Fixed bug in real-time clock on Windows host.

4.14 1620

- Fixed bug in immediate index add (found by Michael Short).
This commit is contained in:
Bob Supnik
2003-12-31 11:49:00 -08:00
committed by Mark Pizzolato
parent b2101ecdd4
commit 1da2d9452d
140 changed files with 17663 additions and 16338 deletions

View File

@@ -1,6 +1,5 @@
/* sim_ether.c: OS-dependent network routines
------------------------------------------------------------------------------
Copyright (c) 2002-2003, David T. Hittner
Permission is hereby granted, free of charge, to any person obtaining a
@@ -53,6 +52,27 @@
Modification history:
15-Dec-03 MP polished generic libpcap support.
05-Dec-03 DTH Genericized eth_devices() and #ifdefs
03-Dec-03 MP Added Solaris support
02-Dec-03 DTH Corrected decnet fix to use reflection counting
01-Dec-03 DTH Added BPF source filtering and reflection counting
28-Nov-03 DTH Rewrote eth_devices using universal pcap_findalldevs()
25-Nov-03 DTH Verified DECNET_FIX, reversed ifdef to mainstream code
19-Nov-03 MP Fixed BPF functionality on Linux/BSD.
17-Nov-03 DTH Added xBSD simplification
14-Nov-03 DTH Added #ifdef DECNET_FIX for problematic duplicate detection code
13-Nov-03 DTH Merged in __FreeBSD__ support
21-Oct-03 MP Added enriched packet dumping for debugging
20-Oct-03 MP Added support for multiple ethernet devices on VMS
20-Sep-03 Ankan Add VMS support (Alpha only)
29-Sep-03 MP Changed separator character in eth_fmt_mac to be ":" to
format ethernet addresses the way the BPF compile engine
wants to see them.
Added BPF support to filter packets
Added missing printf in eth_close
07-Jun-03 MP Added WIN32 support for DECNET duplicate address detection.
06-Jun-03 MP Fixed formatting of Ethernet Protocol Type in eth_packet_trace
30-May-03 DTH Changed WIN32 to _WIN32 for consistency
07-Mar-03 MP Fixed Linux implementation of PacketGetAdapterNames to also
work on Red Hat 6.2-sparc and Debian 3.0r1-sparc.
@@ -102,19 +122,29 @@
20-Aug-02 DTH Created Sim_Ether for O/S independant ethernet implementation
------------------------------------------------------------------------------
Work left to do:
1) Addition of other host Operating Systems (VMS, MAC, etc..)
2) Possible efficiency increase by using BPF filtering
------------------------------------------------------------------------------
*/
#include <ctype.h>
#include "sim_ether.h"
#include "sim_sock.h"
extern FILE *sim_log;
/* 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 */
#if defined(__NetBSD__) || defined (__OpenBSD__) || defined (__FreeBSD__)
#define xBSD 1
#endif
/* make common winpcap code a bit easier to read in this file */
#if defined(_WIN32) || defined(VMS)
#define WIN_PCAP 1
#define HAS_PCAP_SENDPACKET 1
#define PCAP_READ_TIMEOUT -1
#else
#define PCAP_READ_TIMEOUT 1
#endif
/*============================================================================*/
/* OS-independant ethernet routines */
/*============================================================================*/
@@ -122,7 +152,7 @@ extern FILE *sim_log;
void eth_mac_fmt(ETH_MAC* mac, char* buff)
{
uint8* m = (uint8*) mac;
sprintf(buff, "%02X-%02X-%02X-%02X-%02X-%02X", m[0], m[1], m[2], m[3], m[4], m[5]);
sprintf(buff, "%02X:%02X:%02X:%02X:%02X:%02X", m[0], m[1], m[2], m[3], m[4], m[5]);
return;
}
@@ -183,8 +213,7 @@ uint32 eth_crc32(uint32 crc, const void* vbuf, size_t len)
return(crc ^ mask);
}
void eth_packet_trace(ETH_PACK* packet, char* msg)
void eth_packet_trace_ex(ETH_PACK* packet, char* msg, int dmp)
{
char src[20];
char dst[20];
@@ -192,8 +221,43 @@ void eth_packet_trace(ETH_PACK* packet, char* msg)
uint32 crc = eth_crc32(0, packet->msg, packet->len);
eth_mac_fmt((ETH_MAC*)&packet->msg[0], dst);
eth_mac_fmt((ETH_MAC*)&packet->msg[6], src);
printf("Eth: %s dst: %s src: %s protocol: %d len: %d crc: %X\n",
msg, dst, src, *proto, packet->len, crc);
printf("Eth: %s dst: %s src: %s proto: 0x%04X len: %d crc: %X\r\n",
msg, dst, src, ntohs(*proto), packet->len, crc);
if (dmp) {
int i, same, group, sidx, oidx;
char outbuf[80], strbuf[18];
static char hex[] = "0123456789ABCDEF";
for (i=same=0; i<packet->len; i += 16) {
if ((i > 0) && (0 == memcmp(&packet->msg[i], &packet->msg[i-16], 16))) {
++same;
continue;
}
if (same > 0) {
printf("%04X thru %04X same as above\r\n", i-(16*same), i-1);
same = 0;
}
group = (((packet->len - i) > 16) ? 16 : (packet->len - i));
for (sidx=oidx=0; sidx<group; ++sidx) {
outbuf[oidx++] = ' ';
outbuf[oidx++] = hex[(packet->msg[i+sidx]>>4)&0xf];
outbuf[oidx++] = hex[packet->msg[i+sidx]&0xf];
if (isprint(packet->msg[i+sidx]))
strbuf[sidx] = packet->msg[i+sidx];
else
strbuf[sidx] = '.';
}
outbuf[oidx] = '\0';
strbuf[sidx] = '\0';
printf("%04X%-48s %s\r\n", i, outbuf, strbuf);
}
if (same > 0)
printf("%04X thru %04X same as above\r\n", i-(16*same), packet->len-1);
}
}
void eth_packet_trace(ETH_PACK* packet, char* msg)
{
eth_packet_trace_ex(packet, msg, packet->len > 1514);
}
char* eth_getname(int number, char* name)
@@ -211,14 +275,14 @@ void eth_zero(ETH_DEV* dev)
{
/* set all members to NULL OR 0 */
memset(dev, 0, sizeof(ETH_DEV));
dev->reflections = -1; /* not established yet */
}
/*============================================================================*/
/* Non-implemented versions */
/*============================================================================*/
#if !defined (_WIN32) && !defined(linux) && !defined(__NetBSD__) && \
!defined (__OpenBSD__) || !defined (USE_NETWORK)
#if !defined (USE_NETWORK)
t_stat eth_open (ETH_DEV* dev, char* name)
{return SCPE_NOFNC;}
t_stat eth_close (ETH_DEV* dev)
@@ -235,22 +299,29 @@ int eth_devices (int max, ETH_LIST* dev)
#else /* endif unimplemented */
/*============================================================================*/
/* WIN32, Linux, NetBSD, and OpenBSD routines */
/* Uses WinPcap and libpcap packages */
/* WIN32, Linux, and xBSD routines use WinPcap and libpcap packages */
/* OpenVMS Alpha uses a WinPcap port and an associated execlet */
/*============================================================================*/
#include <ctype.h>
#include <pcap.h>
#ifdef _WIN32
#include <packet32.h>
#endif /* _WIN32 */
#if defined (__NetBSD__) || defined (__OpenBSD__)
#include <string.h>
#ifdef xBSD
#include <sys/ioctl.h>
#include <net/bpf.h>
#endif /* __NetBSD__ || __OpenBSD__*/
#if defined (linux) || defined(__NetBSD__) || defined (__OpenBSD__)
#include <fcntl.h>
#endif /* linux || __NetBSD__ || __OpenBSD__ */
#endif /* xBSD */
#if !defined (HAS_PCAP_SENDPACKET)
/* libpcap has no function to write a packet, so we need to implement
pcap_sendpacket() for compatibility with the WinPcap base code.
Return value: 0=Success, -1=Failure */
int pcap_sendpacket(pcap_t* handle, u_char* msg, int len)
{
#if defined (linux)
return (send(pcap_fileno(handle), msg, len, 0) == len)? 0 : -1;
#else
return (write(pcap_fileno(handle), msg, len) == len)? 0 : -1;
#endif /* linux */
}
#endif /* !HAS_PCAP_SENDPACKET */
t_stat eth_open(ETH_DEV* dev, char* name)
{
@@ -259,6 +330,7 @@ t_stat eth_open(ETH_DEV* dev, char* name)
char temp[1024];
char* savname = name;
int num;
char* msg;
/* initialize device */
eth_zero(dev);
@@ -276,46 +348,53 @@ t_stat eth_open(ETH_DEV* dev, char* name)
/* attempt to connect device */
memset(errbuf, 0, sizeof(errbuf));
dev->handle = (void*) pcap_open_live(savname, bufsz, ETH_PROMISC, -1, errbuf);
dev->handle = (void*) pcap_open_live(savname, bufsz, ETH_PROMISC, PCAP_READ_TIMEOUT, errbuf);
if (!dev->handle) { /* can't open device */
printf ("Eth: pcap_open_live error - %s\n", errbuf);
if (sim_log) fprintf (sim_log, "Eth: pcap_open_live error - %s\n", errbuf);
msg = "Eth: pcap_open_live error - %s\r\n";
printf (msg, errbuf);
if (sim_log) fprintf (sim_log, msg, errbuf);
return SCPE_OPENERR;
} else {
printf ("Eth: opened %s\n", savname);
if (sim_log) fprintf (sim_log, "Eth: opened %s\n", savname);
msg = "Eth: opened %s\r\n";
printf (msg, savname);
if (sim_log) fprintf (sim_log, msg, savname);
}
/* save name of device */
dev->name = malloc(strlen(savname)+1);
strcpy(dev->name, savname);
#if defined (__NetBSD__) || defined(__OpenBSD__)
#if defined (xBSD)
/* Tell the kernel that the header is fully-formed when it gets it.
This is required in order to fake the src address.
Code is embedded in braces to create a scope for the local variable */
This is required in order to fake the src address. */
{
int one = 1;
ioctl(pcap_fileno(dev->handle), BIOCSHDRCMPLT, &one);
int one = 1;
ioctl(pcap_fileno(dev->handle), BIOCSHDRCMPLT, &one);
}
#endif /* __NetBSD__ || __OpenBSD__ */
#if defined(linux) || defined(__NetBSD__) || defined (__OpenBSD__)
/* set file non-blocking */
fcntl(pcap_fileno(dev->handle), F_SETFL, fcntl(pcap_fileno(dev->handle), F_GETFL, 0) | O_NONBLOCK);
#endif /* linux || __NetBSD__ || __OpenBSD__ */
#endif /* xBSD */
#if !defined (WIN_PCAP)
/* set ethernet device non-blocking so pcap_dispatch() doesn't hang */
if (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
return SCPE_OK;
}
t_stat eth_close(ETH_DEV* dev)
{
char* msg = "Eth: closed %s\r\n";
/* make sure device exists */
if (!dev) return SCPE_UNATT;
/* close the device */
pcap_close(dev->handle);
if (sim_log) fprintf (sim_log, "Eth: closed %s\n", dev->name);
printf (msg, dev->name);
if (sim_log) fprintf (sim_log, msg, dev->name);
/* clean up the mess */
free(dev->name);
@@ -324,6 +403,43 @@ t_stat eth_close(ETH_DEV* dev)
return SCPE_OK;
}
t_stat eth_reflect(ETH_DEV* dev, ETH_MAC mac)
{
ETH_PACK send, recv;
t_stat status;
int i;
/* build a 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 */
for (i=14; i<send.len; i++)
send.msg[i] = 32 + i; /* gibberish */
/* send the packet */
status = eth_write (dev, &send, NULL);
/* empty the read queue and count the reflections */
dev->reflections = 0;
do {
memset (&recv, 0, sizeof(ETH_PACK));
status = eth_read (dev, &recv, NULL);
if (memcmp(send.msg, recv.msg, ETH_MIN_PACKET)== 0)
dev->reflections++;
} while (recv.len > 0);
#ifdef ETH_DEBUG
{
char* msg = "Eth: reflections = %d\r\n";
printf (msg, dev->reflections);
if (sim_log) fprintf (sim_log, msg, dev->reflections);
}
#endif
return dev->reflections;
}
t_stat eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine)
{
int status = 1; /* default to failure */
@@ -341,6 +457,11 @@ t_stat eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine)
eth_packet_trace (packet, "writing");
#endif
status = pcap_sendpacket((pcap_t*)dev->handle, (u_char*)packet->msg, packet->len);
/* detect sending of decnet loopback packet */
if ((status == 0) && DECNET_SELF_FRAME(dev->decnet_addr, packet->msg))
dev->decnet_self_sent += dev->reflections;
} /* if packet->len */
/* call optional write callback function */
@@ -353,42 +474,18 @@ t_stat eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine)
void eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data)
{
ETH_DEV* dev = (ETH_DEV*) info;
int to_me = 1;
/* receive packet filter */
int to_me = 0;
int from_me = 0;
int i;
for (i = 0; i < ETH_FILTER_MAX; i++) {
if (memcmp(data, dev->filter_address[i], 6) == 0) to_me = 1;
#ifdef _WIN32
/*
WinPcap has a known bug/feature that whenever a packet is transmitted,
it is looped back into the receive buffers. This is not consistant with the
behavior of real ethernet adapters, so the extra packets must be disposed of.
/* detect sending of decnet loopback packet */
if (DECNET_SELF_FRAME(dev->decnet_addr, data)) {
/* lower reflection count - if already zero, pass it on */
if (dev->decnet_self_sent > 0) {
dev->decnet_self_sent--;
to_me = 0;
}
}
This behavior is seen when starting DECNET; DECNET broadcasts a packet
with the same source and destination addresses to make sure that no other
ethernet adapter on the network is using the DECNET address that it wants.
If it sees this test packet coming back in, it assumes that another node on
the network has the same DECNET address and refuses to start, giving an
"Invalid media address" error.
This code section was ifdef'd for _WIN32 only to allow other OS's a chance to
properly implement the above behavior. If it breaks the ethernet simulator
on other platforms, remove the ifdef so that it will affect your platform,
and then notify the author so that he can fix the ifdef. :-)
*/
if (memcmp(&data[6], dev->filter_address[i], 6) == 0) from_me = 1;
#endif
} /* for */
/* all multicast mode? */
if (dev->all_multicast && (data[0] & 0x01)) to_me = 1;
/* promiscuous mode? */
if (dev->promiscuous) to_me = 1;
if (to_me && !from_me) {
if (to_me) {
/* set data in passed read packet */
dev->read_packet->len = header->len;
@@ -401,8 +498,7 @@ void eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char*
/* call optional read callback function */
if (dev->read_callback)
(dev->read_callback)(0);
} /* if to_me && !from_me */
}
}
t_stat eth_read(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine)
@@ -422,7 +518,7 @@ t_stat eth_read(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine)
/* set optional callback routine */
dev->read_callback = routine;
/* dispatch read request to either receive a packet (after filtering) or timeout */
/* dispatch read request to either receive a filtered packet or timeout */
do {
status = pcap_dispatch((pcap_t*)dev->handle, 1, &eth_callback, (u_char*)dev);
} while ((status) && (0 == packet->len));
@@ -433,6 +529,14 @@ t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* addresses,
ETH_BOOL all_multicast, ETH_BOOL promiscuous)
{
int i;
bpf_u_int32 bpf_subnet, bpf_netmask;
char buf[110+66*ETH_FILTER_MAX];
char errbuf[PCAP_ERRBUF_SIZE];
struct bpf_program bpf;
char mac[20];
char* buf2;
char* msg;
t_stat status;
/* make sure device exists */
if (!dev) return SCPE_UNATT;
@@ -444,7 +548,7 @@ t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* addresses,
if (!addresses) return SCPE_ARG;
/* clear filter array */
memset(dev->filter_address, 0, sizeof(ETH_MAC) * ETH_FILTER_MAX);
memset(dev->filter_address, 0, sizeof(dev->filter_address));
/* set new filter addresses */
for (i = 0; i < addr_count; i++)
@@ -454,178 +558,185 @@ t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* addresses,
dev->all_multicast = all_multicast;
dev->promiscuous = promiscuous;
#ifdef ETH_DEBUG
printf("Eth: Filter Set\r\n");
for (i = 0; i < addr_count; i++) {
char mac[20];
eth_mac_fmt(&dev->filter_address[i], mac);
printf(" Addr[%d]: %s\r\n", i, mac);
}
printf("%s%s\r\n", dev->all_multicast ? " All Multicast\r\n" : "",
dev->promiscuous ? " Promiscuous\r\n" : "");
#endif
/* test reflections */
if (dev->reflections == -1)
status = eth_reflect(dev, dev->filter_address[0]);
/* 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);
}
if (dev->all_multicast)
sprintf(&buf[strlen(buf)], "%s(ether multicast)", (*buf) ? " or " : "");
}
/* 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)], ")");
}
/* When starting, DECnet sends out a packet with the source and destination
addresses set to the same value as the DECnet MAC address. This packet is
designed to find and help diagnose DECnet address conflicts. Normally, this
packet would not be seen by the sender, only by the other machine that has
the same DECnet address. If the ethernet subsystem is reflecting packets,
DECnet will fail to start if it sees the reflected packet, since it thinks
another system is using this DECnet address. We have to let these packets
through, so that if another machine has the same 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->decnet_self_sent, and eth_callback() check the value - if the
dev->decnet_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->decnet_addr, 0, sizeof(ETH_MAC));
/* check for decnet address in filters */
if ((addr_count) && (dev->reflections > 0)) {
for (i = 0; i < addr_count; i++) {
eth_mac_fmt(&dev->filter_address[i], mac);
if (memcmp(mac, "AA:00:04", 8) == 0) {
memcpy(dev->decnet_addr, &dev->filter_address[i], sizeof(ETH_MAC));
/* let packets through where dst and src are the same as our decnet address */
sprintf (&buf[strlen(buf)], " or ((ether dst %s) and (ether src %s))", mac, mac);
break;
}
}
}
#ifdef ETH_DEBUG
msg = "Eth: BPF string is: |%s|\r\n";
printf (msg, buf);
if (sim_log) fprintf (sim_log, msg, buf);
#endif
/* get netmask, which is required for compiling */
if (pcap_lookupnet(dev->handle, &bpf_subnet, &bpf_netmask, errbuf)<0) {
bpf_netmask = 0;
}
/* 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);
/* 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);
} else {
#if !defined (WIN_PCAP)
/* set file non-blocking */
status = pcap_setnonblock (dev->handle, 1, errbuf);
#endif
}
pcap_freecode(&bpf);
}
return SCPE_OK;
}
/*
The libpcap provided API pcap_findalldevs() on most platforms, will
leverage the getifaddrs() API if it is available in preference to
alternate platform specific methods of determining the interface list.
A limitation of getifaddrs() is that it returns only interfaces which
have associated addresses. This may not include all of the interesting
interfaces that we are interested in since a host may have dedicated
interfaces for a simulator, which is otherwise unused by the host.
One could hand craft the the build of libpcap to specifically use
alternate methods to implement pcap_findalldevs(). However, this can
get tricky, and would then result in a sort of deviant libpcap.
Additionally, we're really only interested in Ethernet LAN type
interfaces here. It would be nice if the flags provided by pcap_if_t
actually told us if an interface was a Point to Point or a LAN interface
and if it was an ethernet. This isn't implemented anywhere.
This routine exists to allow platform specific code to validate and/or
extend the set of available interfaces to include any that are not
returned by pcap_findalldevs.
*/
int eth_host_devices(int used, int max, ETH_LIST* list)
{
return used;
}
int eth_devices(int max, ETH_LIST* list)
{
int i, index, len;
uint8 buffer[2048];
uint8 buffer2[2048];
uint8* cptr = buffer2;
unsigned long size = sizeof(buffer);
unsigned long ret;
pcap_if_t* alldevs;
pcap_if_t* dev;
int i;
char errbuf[PCAP_ERRBUF_SIZE];
/* get names of devices from packet driver */
ret = PacketGetAdapterNames(buffer, &size);
/* device names in ascii or unicode format? */
if ((buffer[1] == 0) && (buffer[3] == 0)) { /* unicode.. <sigh> */
int i = 0;
int cptr_inc = 2;
/* want to use buffer for scanning, so copy to buffer2 */
memcpy (buffer2, buffer, sizeof(buffer));
/* convert unicode to ascii (assuming every other byte is zero) */
while (cptr < (buffer2 + sizeof(buffer2))) {
buffer[i] = *cptr;
if ((buffer[i] == 0) && (buffer[i-1] == 0)) { /* end of unicode devices */
/* descriptions are in ascii, so change increment */
cptr_inc = 1;
}
cptr += cptr_inc;
i++;
/* 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);
}
/* scan ascii string and load list*/
index = 0;
cptr = buffer;
/* extract device names and numbers */
while (len = strlen(cptr)) {
list[index].num = index;
strcpy(list[index].name, cptr);
cptr += len + 1;
index++;
}
cptr += 2;
/* extract device descriptions */
for (i=0; i < index; i++) {
len = strlen(cptr);
strcpy(list[i].desc, cptr);
cptr += len + 1;
}
return index; /* count of devices */
/* Add any host specific devices and/or validate those already found */
i = eth_host_devices(i, max, list);
/* return device count */
return i;
}
#endif /* (_WIN32 || linux || __NetBSD__ || __OpenBSD__) && USE_NETWORK */
/*============================================================================*/
/* linux-specific code */
/*============================================================================*/
#if defined (linux) && defined (USE_NETWORK)
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <features.h> /* for the glibc version number */
#if (__GLIBC__ >= 2 && __GLIBC_MINOR >= 1) || __GLIBC__ >= 3
#include <netpacket/packet.h>
#include <net/ethernet.h> /* the L2 protocols */
#else /*__GLIBC__*/
#include <asm/types.h>
#include <linux/if.h>
#include <linux/if_arp.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h> /* The L2 protocols */
#endif /*__GLIBC__*/
int pcap_sendpacket(pcap_t* handle, u_char* msg, int len)
{
return (send(pcap_fileno(handle), msg, len, 0) == len)?0:-1;
}
int PacketGetAdapterNames(char* buffer, unsigned long* size)
{
struct ifreq ifr;
int iindex = 1;
int sock = socket(PF_PACKET, SOCK_RAW, 0);
int ptr = 0;
ifr.ifr_ifindex = iindex;
while (ioctl(sock, SIOCGIFNAME, &ifr) == 0) {
/* Only use ethernet interfaces */
if ((0 == ioctl(sock, SIOCGIFHWADDR, &ifr)) &&
(ifr.ifr_hwaddr.sa_family == ARPHRD_ETHER)) {
strcpy(buffer+ptr, ifr.ifr_name);
ptr += strlen(buffer+ptr)+1;
}
ifr.ifr_ifindex = ++iindex;
}
if (ptr == 0) { /* Found any Ethernet Interfaces? */
/* No, so try some good guesses since the SIOCGIFNAME ioctl
doesn't always return the ethernet interfaces, at least not
Debian or Red Hat running on sparc boxes. */
for (iindex=0; iindex < 10; ++iindex) {
sprintf(ifr.ifr_name, "eth%d", iindex);
if ((0 == ioctl(sock, SIOCGIFHWADDR, &ifr)) &&
(ifr.ifr_hwaddr.sa_family == ARPHRD_ETHER)) {
strcpy(buffer+ptr, ifr.ifr_name);
ptr += strlen(buffer+ptr)+1;
}
}
}
close(sock);
buffer[ptr++] = '\0';
buffer[ptr++] = '\0';
*size = ptr;
}
#endif /* linux && USE_NETWORK */
/*============================================================================*/
/* NetBSD/OpenBSD-specific code */
/*============================================================================*/
#if (defined (__NetBSD__) || defined(__OpenBSD__)) && defined (USE_NETWORK)
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <ifaddrs.h>
#include <string.h>
int pcap_sendpacket(pcap_t* handle, u_char* msg, int len)
{
return (write(pcap_fileno(handle), msg, len) == len)?0:-1;
}
int PacketGetAdapterNames(char* buffer, unsigned long* size)
{
const struct sockaddr_dl *sdl;
struct ifaddrs *ifap, *ifa;
char *p;
int ptr = 0;
if (getifaddrs(&ifap) != 0) {
*size = 0;
return (0);
}
p = NULL;
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
if (ifa->ifa_addr->sa_family != AF_LINK)
continue;
if (p && strcmp(p, ifa->ifa_name) == 0)
continue;
sdl = (const struct sockaddr_dl *) ifa->ifa_addr;
if (sdl->sdl_type != IFT_ETHER)
continue;
strcpy(buffer+ptr, ifa->ifa_name);
ptr += strlen(ifa->ifa_name)+1;
}
freeifaddrs(ifap);
buffer[ptr++] = '\0';
buffer[ptr++] = '\0';
*size = ptr;
return (ptr);
}
#endif /* (__NetBSD__ || __OpenBSD__) && USE_NETWORK */
#endif /* USE_NETWORK */