diff options
author | thorpej <thorpej@pkgsrc.org> | 2020-10-07 00:43:04 +0000 |
---|---|---|
committer | thorpej <thorpej@pkgsrc.org> | 2020-10-07 00:43:04 +0000 |
commit | 250543d6af49908900257509450a16d14f01080d (patch) | |
tree | eb2f78adf238507f2b8a643df106d18de95f8a39 /emulators | |
parent | e001d5288290ef4c0a9031f501e75d6497568da0 (diff) | |
download | pkgsrc-250543d6af49908900257509450a16d14f01080d.tar.gz |
- Add tap(4) networking support to gxemul. See the networking documentation
for it's theory of operation in gxemul and how to configure it.
- Add address filtering to the LANCE and Tulip emulation.
These changes have already been upstreamed in the main gxemul repository.
Bump package version to gxemul-0.6.2nb1.
Diffstat (limited to 'emulators')
17 files changed, 2566 insertions, 2 deletions
diff --git a/emulators/gxemul/Makefile b/emulators/gxemul/Makefile index b3341ec46a9..aaadbd95624 100644 --- a/emulators/gxemul/Makefile +++ b/emulators/gxemul/Makefile @@ -1,6 +1,7 @@ -# $NetBSD: Makefile,v 1.67 2019/08/23 13:13:49 ryoon Exp $ +# $NetBSD: Makefile,v 1.68 2020/10/07 00:43:04 thorpej Exp $ DISTNAME= gxemul-0.6.2 +PKGREVISION= 1 CATEGORIES= emulators MASTER_SITES= ${MASTER_SITE_SOURCEFORGE:=gxemul/} diff --git a/emulators/gxemul/distinfo b/emulators/gxemul/distinfo index 9c416df4b05..48b59e47891 100644 --- a/emulators/gxemul/distinfo +++ b/emulators/gxemul/distinfo @@ -1,6 +1,21 @@ -$NetBSD: distinfo,v 1.60 2019/08/23 13:13:49 ryoon Exp $ +$NetBSD: distinfo,v 1.61 2020/10/07 00:43:04 thorpej Exp $ SHA1 (gxemul-0.6.2.tar.gz) = aabaeba783e70be952ab0056bf84d0f2b70c2155 RMD160 (gxemul-0.6.2.tar.gz) = ccac73d82446f89792b1fc803bee623813f3aab2 SHA512 (gxemul-0.6.2.tar.gz) = 4f389c509f9ecf39603ceed50e899e2bee285d3fefac9b3214076115ee71b5a7a68d1d92690b6debc8de5cf5f0303da83b3cc921a5c0b5eb4c7ad89baa730b59 Size (gxemul-0.6.2.tar.gz) = 5897883 bytes +SHA1 (patch-doc_networking.html) = dd7a1519a678196fd5a835317a32ba483630ece8 +SHA1 (patch-src_devices_dev_dec21143.cc) = 52f36741038c76a2dbafc7da6737e816aed5c9f9 +SHA1 (patch-src_devices_dev_ether.cc) = 00221e09530743e81faedcc75ee951fa853d0e2c +SHA1 (patch-src_devices_dev_le.cc) = a728e8008a7a9f33aaf95811a33ebac2cb86e80e +SHA1 (patch-src_devices_dev_rtl8139c.cc) = ee6dbba7c7c9c62c50493c476297ee5ac89d2b83 +SHA1 (patch-src_devices_dev_sgi_mec.cc) = 24b1259350faf60265df7958f0f680302f475e8e +SHA1 (patch-src_devices_dev_sn.cc) = e939521be1630f51e7ddc67abe90980de38e8837 +SHA1 (patch-src_include_net.h) = 4d31fcefe384fcc9d68255825240c89b45acc92e +SHA1 (patch-src_net_Makefile.skel) = 4738229a928b9cb5a2531dfc357297f91e9fdc09 +SHA1 (patch-src_net_net.cc) = 57397c9a8197ee25e7faa8c0733273014e3e0670 +SHA1 (patch-src_net_net_ether.cc) = ef7464dbb0812a9cb8d5be806db07cc19853fc1e +SHA1 (patch-src_net_net_ip.cc) = f5615f3b347e9bdcd256fa4b5b1594473fd2e5e4 +SHA1 (patch-src_net_net_tap.cc) = f913b3efb51bc4a8080420988d5fc845e8a38f73 +SHA1 (patch-src_old_main_emul.cc) = 0b1106745e7c5d320e93f9f7775d8ced6109c089 +SHA1 (patch-src_old_main_emul_parse.cc) = 23048bc3a0a83fd189b3bbd4656ef0e1a2c23b99 diff --git a/emulators/gxemul/patches/patch-doc_networking.html b/emulators/gxemul/patches/patch-doc_networking.html new file mode 100644 index 00000000000..c19f221cb98 --- /dev/null +++ b/emulators/gxemul/patches/patch-doc_networking.html @@ -0,0 +1,120 @@ +$NetBSD: patch-doc_networking.html,v 1.1 2020/10/07 00:43:05 thorpej Exp $ + +Document tap(4)-based networking. + +--- doc/networking.html.orig 2020-10-05 22:53:41.969487340 +0000 ++++ doc/networking.html 2020-10-05 22:54:17.309695478 +0000 +@@ -46,6 +46,7 @@ SUCH DAMAGE. + <p><br> + <ul> + <li><a href="#intro">Introduction</a> ++ <li><a href="#tap">Virtual Ethernet switch using a tap device</a> + <li><a href="#multihost">Network across multiple hosts</a> + <li><a href="#direct_example_1">Direct-access example 1: udp_snoop</a> + </ul> +@@ -60,9 +61,11 @@ SUCH DAMAGE. + <a name="intro"></a> + <h3>Introduction:</h3> + +-<p>GXemul's current networking layer supports two modes: ++<p>GXemul's current networking layer supports three modes: + + <p><ol> ++ <li>A vitual Ethernet switch built on top of a <i>tap</i> device. ++ <p> + <li>A NAT-like layer, which allows guest OSes to access the outside + internet world (IPv4 only, so far). When only one machine is being + emulated, the following default values apply to the guest OS:<pre> +@@ -78,7 +81,7 @@ SUCH DAMAGE. + ethernet packages from/to the emulator. + </ol> + +-<p><i>NOTE:</i> Both these modes have problems. The NAT-like layer is very ++<p><i>NOTE:</i> The latter two modes have problems. The NAT-like layer is very + "hackish" and was only meant as a proof-of-concept, to see if networking + like this would work with e.g. NetBSD as a guest OS. (If you are + interested in the technical details, and the reasons why NAT networking is +@@ -120,6 +123,83 @@ href="machine_decstation.html#netbsdpmax + + + ++<p><br> ++<a name="tap"></a> ++<h3>Virtual Ethernet switch using a <i>tap</i> device:</h3> ++ ++<p>The simplest way to emulate a real Ethernet network is using a <i>tap</i> ++device. In this mode, the emulator disables the simulated NAT and ++direct-access machinery and internally treats all emulated NICs as if ++they are on a single Ethernet switch. In this mode, packets destined for ++the guest's specific MAC address as well as Ethernet multicast and broadcast ++packets are send to the individual guest instances. Individual NIC ++emulations may also apply their own multicast filtering; multcast filtering ++is implemented for the DEC 21143 and Lance NICs. ++ ++<p>The <i>tap</i> interface on the host can be thought of as an upstream ++link on the virtual Ethernet switch. In addition to providing a "port" ++for the host, the <i>tap</i> interface can be bridged to a physical Ethernet ++port on the host, allowing the guests to access the host's connected LAN. ++ ++<p>Networking services such as DHCP and DNS must be provided either by ++the host or by the host's connected LAN. ++ ++<p>Support for the <i>tap</i> device was developed on NetBSD, but should ++also work on FreeBSD, OpenBSD, and Linux hosts. ++ ++<p>Here is a simple example: ++ ++<p><pre> ++<font color="#2020cf">! Configuration file for ++! virtual Ethernet switch networking ++! using a tap device.</font> ++ ++<b>net(</b> ++ <b>tapdev(</b><font color="#ff003f">"/dev/tap0"</font><b>)</b> ++<b>)</b> ++<b>machine(</b> ++ <b>name(<font color="#ff003f">"guest machine"</font>)</b> ++ ++ <b>type(<font color="#ff003f">"dec"</font>)</b> ++ <b>subtype(<font color="#ff003f">"5000/200"</font>)</b> ++ ++ <font color="#2020cf">! Add a disk, etc.</font> ++<b>)</b> ++</pre> ++ ++<p>Before starting the emulator, you will need to create the <i>tap</i> ++interface on the host. Here is an example for NetBSD: ++ ++<p><pre> ++<b>#ifconfig tap0 create up</b> ++</pre> ++ ++<p>If you wish to simply network the host and the guests together, then ++simply assign an IP address to the <i>tap</i> interface on the host: ++ ++<p><pre> ++<b># ifconfig tap0 10.0.0.254</b> ++</pre> ++ ++<p>You can now run a DHCP server on the host for the guests, or you can ++configure the guests manually. ++ ++<p>If instead you would like to bridge to the host's connected LAN, ++Here is an example for NetBSD: ++ ++<p><pre> ++<b># ifconfig bridge0 create up</b> ++<b># brconfig add tap0 add wm0</b> ++</pre> ++ ++<p>Although it <i>is</i> possible to have more than one machine per ++configuration file, I strongly recommend against it. Please use one ++configuration file for one emulated machine. Each configuration file ++must have a unique <i>tap</i> instance, and machines in separate ++configuration files must use bridged <i>tap</i> devices if they wish ++to communicate with each other as if on the same LAN. ++ ++ + + <p><br> + <a name="multihost"></a> diff --git a/emulators/gxemul/patches/patch-src_devices_dev_dec21143.cc b/emulators/gxemul/patches/patch-src_devices_dev_dec21143.cc new file mode 100644 index 00000000000..bd57987f55d --- /dev/null +++ b/emulators/gxemul/patches/patch-src_devices_dev_dec21143.cc @@ -0,0 +1,890 @@ +$NetBSD: patch-src_devices_dev_dec21143.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $ + +- Add support for tap(4)-based networking. +- Process the setup packet, and implement all of the Tulip receive + filtering modes. +- Add ugly hack to deal with PCI vs. CPU views of main memory differences + between systems. + +--- src/devices/dev_dec21143.cc.orig 2020-10-05 22:54:55.903897678 +0000 ++++ src/devices/dev_dec21143.cc 2020-10-05 22:55:15.711704852 +0000 +@@ -45,8 +45,6 @@ + * o) Handle _writes_ to MII registers. + * o) Make it work with modern Linux kernels (as a guest OS). + * o) Endianness for descriptors? If necessary. +- * o) Actually handle the "Setup" packet. +- * o) MAC filtering on incoming packets. + * o) Don't hardcode as many values. + */ + +@@ -76,16 +74,15 @@ + #define ROM_WIDTH 6 + + struct dec21143_data { ++ /* NIC common data */ ++ struct nic_data nic; ++ + struct interrupt irq; + int irq_was_asserted; + + /* PCI: */ + int pci_little_endian; + +- /* Ethernet address, and a network which we are connected to: */ +- uint8_t mac[6]; +- struct net *net; +- + /* SROM emulation: */ + uint8_t srom[1 << (ROM_WIDTH + 1)]; + int srom_curbit; +@@ -105,19 +102,66 @@ struct dec21143_data { + uint32_t reg[N_REGS]; + + /* Internal TX state: */ +- uint64_t cur_tx_addr; ++ uint32_t cur_tx_addr; + unsigned char *cur_tx_buf; + int cur_tx_buf_len; + int tx_idling; + int tx_idling_threshold; + + /* Internal RX state: */ +- uint64_t cur_rx_addr; ++ uint32_t cur_rx_addr; + unsigned char *cur_rx_buf; + int cur_rx_buf_len; + int cur_rx_offset; ++ ++ /* ++ * Receive filter information. We keep our own copy of ++ * the promiscuous flag because to implement some of the ++ * filtering modes, we need to tell the network layer that ++ * we want all packets. ++ */ ++ int (*drop_packet)(struct net *, struct dec21143_data *); ++ int allmulti; ++ int promiscuous; ++ int filter_needs_promiscuous; ++ uint8_t perfect_filter[6 * TULIP_MAXADDRS]; ++ ++ /* Only 16 bits are used per filter word. */ ++#define MCHASH_NWORDS (TULIP_MCHASHSIZE / 16) ++ uint32_t hash_filter[MCHASH_NWORDS]; ++ int hash_filter_saturated; ++ ++ /* ++ * XXX XXX XXX ++ * XXX UGLY HACK. Need a proper way to deal with ++ * XXX different PCI vs. CPU views of RAM. ++ * XXX XXX XXX ++ */ ++ uint32_t xxx_dma_to_phys_mask; + }; + ++/* XXX This is an UGLY hack. */ ++static uint64_t dma_to_phys(const struct dec21143_data *d, uint32_t dma_addr) ++{ ++ return dma_addr & d->xxx_dma_to_phys_mask; ++} ++ ++ ++static inline uint32_t load_le32(const uint8_t *buf) ++{ ++ return buf[0] | ((uint32_t)buf[1] << 8) | ++ ((uint32_t)buf[2] << 16) | ((uint32_t)buf[3] << 24); ++} ++ ++ ++static inline void store_le32(uint8_t *buf, uint32_t val) ++{ ++ buf[0] = (uint8_t)val; ++ buf[1] = (uint8_t)(val >> 8); ++ buf[2] = (uint8_t)(val >> 16); ++ buf[3] = (uint8_t)(val >> 24); ++} ++ + + /* Internal states during MII data stream decode: */ + #define MII_STATE_RESET 0 +@@ -130,6 +174,171 @@ struct dec21143_data { + + + /* ++ * The 21143 has multiple address matching modes: ++ * ++ * - Perfect Filtering: The chip interprets the descriptor buffer ++ * as a table of 16 MAC addresses that it should match. The ++ * station address and broadcast must be included in the list. ++ * ++ * - Hash Perfect Filtering: The chip interprets the descriptor buffer ++ * as a 512-bit hash table plus one perfect filter match. Multicast ++ * addresses only are matched against the hash table. ++ * ++ * - Inverse Filtering: Like Perfect Filtering, but the table is ++ * addresses NOT to match. ++ * ++ * - Hash-only Filtering: Like Hash Perfect, except without the Perfect. ++ * All addresses are matched against the hash table. ++ * ++ * The mode seleted by the TDCTL descriptor field is reflected in 3 ++ * read-only bits in the OPMODE register. ++ * ++ * We implement all 4 (NetBSD, at least, is known to use Perfect and ++ * Hash Perfect on the 21143; it also uses Hash-only on the 21140). ++ */ ++ ++#define TDCTL_Tx_FT_MASK (TDCTL_Tx_FT1|TDCTL_Tx_FT0) ++ ++#define dec21143_mchash(addr) \ ++ (net_ether_crc32_le((addr), 6) & (TULIP_MCHASHSIZE - 1)) ++ ++static int dec21143_drop_packet_perfect(struct net *net, ++ struct dec21143_data *d) ++{ ++ int i; ++ ++ for (i = 0; i < TULIP_MAXADDRS; i++) { ++ if (net_ether_eq(d->cur_rx_buf, &d->perfect_filter[6 * i])) { ++ /* Match! */ ++ return 0; ++ } ++ } ++ ++ return 1; ++} ++ ++static int dec21143_drop_packet_hashperfect(struct net *net, ++ struct dec21143_data *d) ++{ ++ ++ /* ++ * In this mode, we have the network layer match our station ++ * address, and we reflect the true promiscuous status there ++ * as well. This means that if it's not a multicast address, ++ * then it's already been sufficiently matched. ++ */ ++ if (! net_ether_multicast(d->cur_rx_buf)) ++ return 0; ++ ++ /* ++ * Note that the broadcast address is also checked against ++ * the hash table in this mode! ++ */ ++ ++ const uint32_t hash = dec21143_mchash(d->cur_rx_buf); ++ if (d->hash_filter[hash >> 4] & (1U << (hash & 0xf))) { ++ /* Match! */ ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static int dec21143_drop_packet_inverse(struct net *net, ++ struct dec21143_data *d) ++{ ++ return !dec21143_drop_packet_perfect(net, d); ++} ++ ++static int dec21143_drop_packet_hashonly(struct net *net, ++ struct dec21143_data *d) ++{ ++ const uint32_t hash = dec21143_mchash(d->cur_rx_buf); ++ if (d->hash_filter[hash >> 4] & (1U << (hash & 0xf))) { ++ /* Match! */ ++ return 0; ++ } ++ ++ return 1; ++} ++ ++ ++/* ++ * dec21143_rx_drop_packet(): ++ * ++ * Implement the logic to determine if we should drop a packet ++ * before paassing it to the guest. Returns 1 if the packet ++ * was dropped. ++ */ ++static int dec21143_rx_drop_packet(struct net *net, struct dec21143_data *d) ++{ ++ /* Only implement filtering if using a tap device. */ ++ if (net->tapdev == NULL) ++ return 0; ++ ++ /* ++ * We might have told the network layer that we're promiscuous ++ * due to the chosen filtering mode, so check the truth here. ++ */ ++ if (d->promiscuous) ++ return 0; ++ ++ /* ++ * If the guest wants all multicast (either all the bits are ++ * set or the OPMODE_PM bit is set), then check to see if we ++ * can short-circuit the checks. ++ */ ++ if (d->allmulti && net_ether_multicast(d->cur_rx_buf)) ++ return 0; ++ ++ /* ++ * Note that if we haven't gotten a setup packet yet, then ++ * d->drop_packet will be NULL. If this happens, we always ++ * drop. This is akin to the real hardware defaulting to ++ * Perfect filtering mode but not having any valid addresses ++ * in the list to match against. ++ */ ++ if (d->drop_packet == NULL || d->drop_packet(net, d)) { ++ /* Not for us; drop the packet. */ ++ free(d->cur_rx_buf); ++ d->cur_rx_buf = NULL; ++ d->cur_rx_buf_len = 0; ++ return 1; ++ } ++ ++ return 0; ++} ++ ++ ++/* ++ * dec21143_update_rx_mode(): ++ * ++ * Update promiscuous / allmulti indicators based on OPMODE ++ * and filter state. ++ */ ++static void dec21143_update_rx_mode(struct dec21143_data *d) ++{ ++ int opmode_pr = (d->reg[CSR_OPMODE / 8] & OPMODE_PR) != 0; ++ int opmode_pm = (d->reg[CSR_OPMODE / 8] & OPMODE_PM) != 0; ++ ++ debug("[ dec21143 rx mode: opmode_pr = %d ]\n", ++ opmode_pr); ++ debug("[ dec21143 rx mode: filter_needs_promiscuous = %d ]\n", ++ d->filter_needs_promiscuous); ++ debug("[ dec21143 rx mode: opmode_pm = %d ]\n", ++ opmode_pm); ++ debug("[ dec21143 rx mode: filter_saturated = %d ]\n", ++ d->hash_filter_saturated); ++ ++ d->promiscuous = opmode_pr; ++ d->nic.promiscuous_mode = ++ d->promiscuous || d->filter_needs_promiscuous; ++ ++ d->allmulti = opmode_pm || d->hash_filter_saturated; ++} ++ ++ ++/* + * dec21143_rx(): + * + * Receive a packet. (If there is no current packet, then check for newly +@@ -138,20 +347,23 @@ struct dec21143_data { + */ + int dec21143_rx(struct cpu *cpu, struct dec21143_data *d) + { +- uint64_t addr = d->cur_rx_addr, bufaddr; ++ uint32_t addr = d->cur_rx_addr, bufaddr; + unsigned char descr[16]; + uint32_t rdes0, rdes1, rdes2, rdes3; + int bufsize, buf1_size, buf2_size, i, writeback_len = 4, to_xfer; + + /* No current packet? Then check for new ones. */ +- if (d->cur_rx_buf == NULL) { ++ while (d->cur_rx_buf == NULL) { + /* Nothing available? Then abort. */ +- if (!net_ethernet_rx_avail(d->net, d)) ++ if (!net_ethernet_rx_avail(d->nic.net, &d->nic)) + return 0; + + /* Get the next packet into our buffer: */ +- net_ethernet_rx(d->net, d, &d->cur_rx_buf, +- &d->cur_rx_buf_len); ++ net_ethernet_rx(d->nic.net, &d->nic, ++ &d->cur_rx_buf, &d->cur_rx_buf_len); ++ ++ if (dec21143_rx_drop_packet(d->nic.net, d)) ++ continue; + + /* Append a 4 byte CRC: */ + d->cur_rx_buf_len += 4; +@@ -165,15 +377,14 @@ int dec21143_rx(struct cpu *cpu, struct + } + + /* fatal("{ dec21143_rx: base = 0x%08x }\n", (int)addr); */ +- addr &= 0x7fffffff; + +- if (!cpu->memory_rw(cpu, cpu->mem, addr, descr, sizeof(uint32_t), +- MEM_READ, PHYSICAL | NO_EXCEPTIONS)) { ++ if (!cpu->memory_rw(cpu, cpu->mem, dma_to_phys(d, addr), ++ descr, sizeof(uint32_t), MEM_READ, PHYSICAL | NO_EXCEPTIONS)) { + fatal("[ dec21143_rx: memory_rw failed! ]\n"); + return 0; + } + +- rdes0 = descr[0] + (descr[1]<<8) + (descr[2]<<16) + (descr[3]<<24); ++ rdes0 = load_le32(&descr[0]); + + /* Only use descriptors owned by the 21143: */ + if (!(rdes0 & TDSTAT_OWN)) { +@@ -181,16 +392,17 @@ int dec21143_rx(struct cpu *cpu, struct + return 0; + } + +- if (!cpu->memory_rw(cpu, cpu->mem, addr + sizeof(uint32_t), descr + +- sizeof(uint32_t), sizeof(uint32_t) * 3, MEM_READ, PHYSICAL | +- NO_EXCEPTIONS)) { ++ if (!cpu->memory_rw(cpu, cpu->mem, ++ dma_to_phys(d, addr + sizeof(uint32_t)), ++ descr + sizeof(uint32_t), sizeof(uint32_t) * 3, MEM_READ, ++ PHYSICAL | NO_EXCEPTIONS)) { + fatal("[ dec21143_rx: memory_rw failed! ]\n"); + return 0; + } + +- rdes1 = descr[4] + (descr[5]<<8) + (descr[6]<<16) + (descr[7]<<24); +- rdes2 = descr[8] + (descr[9]<<8) + (descr[10]<<16) + (descr[11]<<24); +- rdes3 = descr[12] + (descr[13]<<8) + (descr[14]<<16) + (descr[15]<<24); ++ rdes1 = load_le32(&descr[4]); ++ rdes2 = load_le32(&descr[8]); ++ rdes3 = load_le32(&descr[12]); + + buf1_size = rdes1 & TDCTL_SIZE1; + buf2_size = (rdes1 & TDCTL_SIZE2) >> TDCTL_SIZE2_SHIFT; +@@ -210,7 +422,6 @@ int dec21143_rx(struct cpu *cpu, struct + + debug("{ RX (%llx): 0x%08x 0x%08x 0x%x 0x%x: buf %i bytes at 0x%x }\n", + (long long)addr, rdes0, rdes1, rdes2, rdes3, bufsize, (int)bufaddr); +- bufaddr &= 0x7fffffff; + + /* Turn off all status bits, and give up ownership: */ + rdes0 = 0x00000000; +@@ -221,7 +432,7 @@ int dec21143_rx(struct cpu *cpu, struct + + /* DMA bytes from the packet into emulated physical memory: */ + for (i=0; i<to_xfer; i++) { +- cpu->memory_rw(cpu, cpu->mem, bufaddr + i, ++ cpu->memory_rw(cpu, cpu->mem, dma_to_phys(d, bufaddr + i), + d->cur_rx_buf + d->cur_rx_offset + i, 1, MEM_WRITE, + PHYSICAL | NO_EXCEPTIONS); + /* fatal(" %02x", d->cur_rx_buf[d->cur_rx_offset + i]); */ +@@ -253,19 +464,16 @@ int dec21143_rx(struct cpu *cpu, struct + } + + /* Descriptor writeback: */ +- descr[ 0] = rdes0; descr[ 1] = rdes0 >> 8; +- descr[ 2] = rdes0 >> 16; descr[ 3] = rdes0 >> 24; ++ store_le32(&descr[0], rdes0); + if (writeback_len > 1) { +- descr[ 4] = rdes1; descr[ 5] = rdes1 >> 8; +- descr[ 6] = rdes1 >> 16; descr[ 7] = rdes1 >> 24; +- descr[ 8] = rdes2; descr[ 9] = rdes2 >> 8; +- descr[10] = rdes2 >> 16; descr[11] = rdes2 >> 24; +- descr[12] = rdes3; descr[13] = rdes3 >> 8; +- descr[14] = rdes3 >> 16; descr[15] = rdes3 >> 24; ++ store_le32(&descr[4], rdes1); ++ store_le32(&descr[8], rdes2); ++ store_le32(&descr[12], rdes3); + } + +- if (!cpu->memory_rw(cpu, cpu->mem, addr, descr, sizeof(uint32_t) +- * writeback_len, MEM_WRITE, PHYSICAL | NO_EXCEPTIONS)) { ++ if (!cpu->memory_rw(cpu, cpu->mem, dma_to_phys(d, addr), descr, ++ sizeof(uint32_t) * writeback_len, MEM_WRITE, ++ PHYSICAL | NO_EXCEPTIONS)) { + fatal("[ dec21143_rx: memory_rw failed! ]\n"); + return 0; + } +@@ -275,6 +483,178 @@ int dec21143_rx(struct cpu *cpu, struct + + + /* ++ * dec21143_setup_copy_enaddr(): ++ * ++ * Copy an Ethernet address out of the setup packet. ++ */ ++static void dec21143_setup_copy_enaddr(uint8_t *enaddr, ++ const uint32_t *setup_packet) ++{ ++ int i; ++ ++ for (i = 0; i < 3; i++) { ++ enaddr[i*2 ] = (uint8_t)setup_packet[i]; ++ enaddr[i*2 + 1] = (uint8_t)(setup_packet[i] >> 8); ++ } ++} ++ ++ ++/* ++ * dec21143_setup_perfect(): ++ * ++ * Setup perfect filtering mode. ++ */ ++static void dec21143_setup_perfect(struct dec21143_data *d, ++ const uint32_t *setup_packet) ++{ ++ int i; ++ ++ for (i = 0; i < TULIP_MAXADDRS; i++) { ++ dec21143_setup_copy_enaddr(&d->perfect_filter[i * 6], ++ &setup_packet[i * 3]); ++ debug("dec21143 PERFECT[%d] %02x:%02x:%02x:%02x:%02x:%02x\n", ++ i, ++ d->perfect_filter[i*6 + 0], ++ d->perfect_filter[i*6 + 1], ++ d->perfect_filter[i*6 + 2], ++ d->perfect_filter[i*6 + 3], ++ d->perfect_filter[i*6 + 4], ++ d->perfect_filter[i*6 + 5]); ++ } ++ ++ d->drop_packet = dec21143_drop_packet_perfect; ++} ++ ++ ++/* ++ * dec21143_setup_hashperfect(): ++ * ++ * Setup hash-perfect filtering mode. ++ */ ++static void dec21143_setup_hashperfect(struct dec21143_data *d, ++ const uint32_t *setup_packet) ++{ ++ int i; ++ ++ debug("dec21143 HASHPERFECT:"); ++ for (i = 0; i < MCHASH_NWORDS; i++) { ++ if ((i % 8) == 0) ++ debug("\n\t"); ++ debug(" %04x", setup_packet[i]); ++ d->hash_filter[i] = setup_packet[i]; ++ d->hash_filter_saturated |= (d->hash_filter[i] == 0xffff); ++ } ++ debug("\n"); ++ ++ dec21143_setup_copy_enaddr(d->nic.mac_address, &setup_packet[39]); ++ debug("dec21143 HASHPERFECT %02x:%02x:%02x:%02x:%02x:%02x\n", ++ d->nic.mac_address[0], ++ d->nic.mac_address[1], ++ d->nic.mac_address[2], ++ d->nic.mac_address[3], ++ d->nic.mac_address[4], ++ d->nic.mac_address[5]); ++ ++ d->filter_needs_promiscuous = 0; ++ d->drop_packet = dec21143_drop_packet_hashperfect; ++} ++ ++ ++/* ++ * dec21143_setup_inverse(): ++ * ++ * Setup inverse filtering mode. ++ */ ++static void dec21143_setup_inverse(struct dec21143_data *d, ++ const uint32_t *setup_packet) ++{ ++ dec21143_setup_perfect(d, setup_packet); ++ debug("dec21143 INVERSE ^^^^\n"); ++ d->drop_packet = dec21143_drop_packet_inverse; ++} ++ ++ ++/* ++ * dec21143_setup_hashonly(): ++ * ++ * Setup hash-only filtering mode. ++ */ ++static void dec21143_setup_hashonly(struct dec21143_data *d, ++ const uint32_t *setup_packet) ++{ ++ int i; ++ ++ debug("dec21143 HASHONLY:"); ++ for (i = 0; i < MCHASH_NWORDS; i++) { ++ if ((i % 8) == 0) ++ fatal("\n\t"); ++ debug(" %04x", setup_packet[i]); ++ d->hash_filter[i] = setup_packet[i]; ++ d->hash_filter_saturated |= (d->hash_filter[i] == 0xffff); ++ } ++ debug("\n"); ++ ++ d->drop_packet = dec21143_drop_packet_hashonly; ++} ++ ++ ++/* ++ * dec21143_process_setup_packet(): ++ * ++ * Process the address filter setup packet. ++ */ ++static void dec21143_process_setup_packet(struct cpu *cpu, ++ struct dec21143_data *d, uint32_t tdctl, uint32_t bufaddr) ++{ ++ uint32_t setup_packet[TULIP_SETUP_PACKET_LEN / sizeof(uint32_t)]; ++ uint8_t *cp = (uint8_t *)setup_packet; ++ uint32_t tmp; ++ int i; ++ ++ if (!cpu->memory_rw(cpu, cpu->mem, dma_to_phys(d, bufaddr), cp, ++ TULIP_SETUP_PACKET_LEN, MEM_READ, PHYSICAL | NO_EXCEPTIONS)) { ++ fatal("[ dec21143_process_setup_packet: memory_rw failed! ]\n"); ++ return; ++ } ++ ++ /* Ensure host order of each word. */ ++ for (i = 0; i < TULIP_SETUP_PACKET_LEN; i += sizeof(uint32_t)) { ++ tmp = load_le32(&cp[i]); ++ setup_packet[i / sizeof(uint32_t)] = tmp; ++ } ++ ++ /* Defaults. */ ++ d->hash_filter_saturated = 0; ++ d->filter_needs_promiscuous = 1; ++ ++ d->reg[CSR_OPMODE / 8] &= ~(OPMODE_HP | OPMODE_HO | OPMODE_IF); ++ ++ switch (tdctl & TDCTL_Tx_FT_MASK) { ++ case TDCTL_Tx_FT_PERFECT: ++ dec21143_setup_perfect(d, setup_packet); ++ break; ++ ++ case TDCTL_Tx_FT_HASH: ++ dec21143_setup_hashperfect(d, setup_packet); ++ d->reg[CSR_OPMODE / 8] |= OPMODE_HP; ++ break; ++ ++ case TDCTL_Tx_FT_INVERSE: ++ dec21143_setup_inverse(d, setup_packet); ++ d->reg[CSR_OPMODE / 8] |= OPMODE_IF; ++ break; ++ ++ case TDCTL_Tx_FT_HASHONLY: ++ dec21143_setup_hashonly(d, setup_packet); ++ d->reg[CSR_OPMODE / 8] |= OPMODE_HO; ++ break; ++ } ++ ++ dec21143_update_rx_mode(d); ++} ++ ++ ++/* + * dec21143_tx(): + * + * Transmit a packet, if the guest OS has marked a descriptor as containing +@@ -282,20 +662,18 @@ int dec21143_rx(struct cpu *cpu, struct + */ + int dec21143_tx(struct cpu *cpu, struct dec21143_data *d) + { +- uint64_t addr = d->cur_tx_addr, bufaddr; ++ uint32_t addr = d->cur_tx_addr, bufaddr; + unsigned char descr[16]; + uint32_t tdes0, tdes1, tdes2, tdes3; + int bufsize, buf1_size, buf2_size, i; + +- addr &= 0x7fffffff; +- +- if (!cpu->memory_rw(cpu, cpu->mem, addr, descr, sizeof(uint32_t), +- MEM_READ, PHYSICAL | NO_EXCEPTIONS)) { ++ if (!cpu->memory_rw(cpu, cpu->mem, dma_to_phys(d, addr), descr, ++ sizeof(uint32_t), MEM_READ, PHYSICAL | NO_EXCEPTIONS)) { + fatal("[ dec21143_tx: memory_rw failed! ]\n"); + return 0; + } + +- tdes0 = descr[0] + (descr[1]<<8) + (descr[2]<<16) + (descr[3]<<24); ++ tdes0 = load_le32(&descr[0]); + + /* fatal("{ dec21143_tx: base=0x%08x, tdes0=0x%08x }\n", + (int)addr, (int)tdes0); */ +@@ -310,16 +688,17 @@ int dec21143_tx(struct cpu *cpu, struct + return 0; + } + +- if (!cpu->memory_rw(cpu, cpu->mem, addr + sizeof(uint32_t), descr + +- sizeof(uint32_t), sizeof(uint32_t) * 3, MEM_READ, PHYSICAL | +- NO_EXCEPTIONS)) { ++ if (!cpu->memory_rw(cpu, cpu->mem, ++ dma_to_phys(d, addr + sizeof(uint32_t)), ++ descr + sizeof(uint32_t), sizeof(uint32_t) * 3, MEM_READ, ++ PHYSICAL | NO_EXCEPTIONS)) { + fatal("[ dec21143_tx: memory_rw failed! ]\n"); + return 0; + } + +- tdes1 = descr[4] + (descr[5]<<8) + (descr[6]<<16) + (descr[7]<<24); +- tdes2 = descr[8] + (descr[9]<<8) + (descr[10]<<16) + (descr[11]<<24); +- tdes3 = descr[12] + (descr[13]<<8) + (descr[14]<<16) + (descr[15]<<24); ++ tdes1 = load_le32(&descr[4]); ++ tdes2 = load_le32(&descr[8]); ++ tdes3 = load_le32(&descr[12]); + + buf1_size = tdes1 & TDCTL_SIZE1; + buf2_size = (tdes1 & TDCTL_SIZE2) >> TDCTL_SIZE2_SHIFT; +@@ -338,10 +717,9 @@ int dec21143_tx(struct cpu *cpu, struct + } + + /* +- fatal("{ TX (%llx): 0x%08x 0x%08x 0x%x 0x%x: buf %i bytes at 0x%x }\n", +- (long long)addr, tdes0, tdes1, tdes2, tdes3, bufsize, (int)bufaddr); ++ fatal("{ TX (%x): 0x%08x 0x%08x 0x%x 0x%x: buf %i bytes at 0x%x }\n", ++ addr, tdes0, tdes1, tdes2, tdes3, bufsize, bufaddr); + */ +- bufaddr &= 0x7fffffff; + + /* Assume no error: */ + tdes0 &= ~ (TDSTAT_Tx_UF | TDSTAT_Tx_EC | TDSTAT_Tx_LC +@@ -350,13 +728,13 @@ int dec21143_tx(struct cpu *cpu, struct + if (tdes1 & TDCTL_Tx_SET) { + /* + * Setup Packet. +- * +- * TODO. For now, just ignore it, and pretend it worked. + */ + /* fatal("{ TX: setup packet }\n"); */ +- if (bufsize != 192) ++ if (bufsize != TULIP_SETUP_PACKET_LEN) + fatal("[ dec21143: setup packet len = %i, should be" +- " 192! ]\n", (int)bufsize); ++ " %d! ]\n", (int)bufsize, TULIP_SETUP_PACKET_LEN); ++ else ++ dec21143_process_setup_packet(cpu, d, tdes1, bufaddr); + if (tdes1 & TDCTL_Tx_IC) + d->reg[CSR_STATUS/8] |= STATUS_TI; + /* New descriptor values, according to the docs: */ +@@ -388,7 +766,8 @@ int dec21143_tx(struct cpu *cpu, struct + + /* "DMA" data from emulated physical memory into the buf: */ + for (i=0; i<bufsize; i++) { +- cpu->memory_rw(cpu, cpu->mem, bufaddr + i, ++ cpu->memory_rw(cpu, cpu->mem, ++ dma_to_phys(d, bufaddr + i), + d->cur_tx_buf + d->cur_tx_buf_len + i, 1, MEM_READ, + PHYSICAL | NO_EXCEPTIONS); + /* fatal(" %02x", d->cur_tx_buf[ +@@ -400,9 +779,9 @@ int dec21143_tx(struct cpu *cpu, struct + /* Last segment? Then actually transmit it: */ + if (tdes1 & TDCTL_Tx_LS) { + /* fatal("{ TX: data frame complete. }\n"); */ +- if (d->net != NULL) { +- net_ethernet_tx(d->net, d, d->cur_tx_buf, +- d->cur_tx_buf_len); ++ if (d->nic.net != NULL) { ++ net_ethernet_tx(d->nic.net, &d->nic, ++ d->cur_tx_buf, d->cur_tx_buf_len); + } else { + static int warn = 0; + if (!warn) +@@ -430,17 +809,13 @@ int dec21143_tx(struct cpu *cpu, struct + tdes0 |= TDSTAT_ES; + + /* Descriptor writeback: */ +- descr[ 0] = tdes0; descr[ 1] = tdes0 >> 8; +- descr[ 2] = tdes0 >> 16; descr[ 3] = tdes0 >> 24; +- descr[ 4] = tdes1; descr[ 5] = tdes1 >> 8; +- descr[ 6] = tdes1 >> 16; descr[ 7] = tdes1 >> 24; +- descr[ 8] = tdes2; descr[ 9] = tdes2 >> 8; +- descr[10] = tdes2 >> 16; descr[11] = tdes2 >> 24; +- descr[12] = tdes3; descr[13] = tdes3 >> 8; +- descr[14] = tdes3 >> 16; descr[15] = tdes3 >> 24; ++ store_le32(&descr[0], tdes0); ++ store_le32(&descr[4], tdes1); ++ store_le32(&descr[8], tdes2); ++ store_le32(&descr[12], tdes3); + +- if (!cpu->memory_rw(cpu, cpu->mem, addr, descr, sizeof(uint32_t) +- * 4, MEM_WRITE, PHYSICAL | NO_EXCEPTIONS)) { ++ if (!cpu->memory_rw(cpu, cpu->mem, dma_to_phys(d, addr), descr, ++ sizeof(uint32_t) * 4, MEM_WRITE, PHYSICAL | NO_EXCEPTIONS)) { + fatal("[ dec21143_tx: memory_rw failed! ]\n"); + return 0; + } +@@ -750,7 +1125,6 @@ static void srom_access(struct cpu *cpu, + */ + static void dec21143_reset(struct cpu *cpu, struct dec21143_data *d) + { +- int leaf; + + if (d->cur_rx_buf != NULL) + free(d->cur_rx_buf); +@@ -759,7 +1133,6 @@ static void dec21143_reset(struct cpu *c + d->cur_rx_buf = d->cur_tx_buf = NULL; + + memset(d->reg, 0, sizeof(uint32_t) * N_REGS); +- memset(d->srom, 0, sizeof(d->srom)); + memset(d->mii_phy_reg, 0, sizeof(d->mii_phy_reg)); + + /* Register values at reset, according to the manual: */ +@@ -772,35 +1145,8 @@ static void dec21143_reset(struct cpu *c + d->tx_idling_threshold = 10; + d->cur_rx_addr = d->cur_tx_addr = 0; + +- /* Version (= 1) and Chip count (= 1): */ +- d->srom[TULIP_ROM_SROM_FORMAT_VERION] = 1; +- d->srom[TULIP_ROM_CHIP_COUNT] = 1; +- + /* Set the MAC address: */ +- memcpy(d->srom + TULIP_ROM_IEEE_NETWORK_ADDRESS, d->mac, 6); +- +- leaf = 30; +- d->srom[TULIP_ROM_CHIPn_DEVICE_NUMBER(0)] = 0; +- d->srom[TULIP_ROM_CHIPn_INFO_LEAF_OFFSET(0)] = leaf & 255; +- d->srom[TULIP_ROM_CHIPn_INFO_LEAF_OFFSET(0)+1] = leaf >> 8; +- +- d->srom[leaf+TULIP_ROM_IL_SELECT_CONN_TYPE] = 0; /* Not used? */ +- d->srom[leaf+TULIP_ROM_IL_MEDIA_COUNT] = 2; +- leaf += TULIP_ROM_IL_MEDIAn_BLOCK_BASE; +- +- d->srom[leaf] = 7; /* descriptor length */ +- d->srom[leaf+1] = TULIP_ROM_MB_21142_SIA; +- d->srom[leaf+2] = TULIP_ROM_MB_MEDIA_100TX; +- /* here comes 4 bytes of GPIO control/data settings */ +- leaf += d->srom[leaf]; +- +- d->srom[leaf] = 15; /* descriptor length */ +- d->srom[leaf+1] = TULIP_ROM_MB_21142_MII; +- d->srom[leaf+2] = 0; /* PHY nr */ +- d->srom[leaf+3] = 0; /* len of select sequence */ +- d->srom[leaf+4] = 0; /* len of reset sequence */ +- /* 5,6, 7,8, 9,10, 11,12, 13,14 = unused by GXemul */ +- leaf += d->srom[leaf]; ++ memcpy(d->nic.mac_address, d->srom + TULIP_ROM_IEEE_NETWORK_ADDRESS, 6); + + /* MII PHY initial state: */ + d->mii_state = MII_STATE_RESET; +@@ -814,12 +1160,13 @@ static void dec21143_reset(struct cpu *c + DEVICE_ACCESS(dec21143) + { + struct dec21143_data *d = (struct dec21143_data *) extra; +- uint64_t idata = 0, odata = 0; ++ uint32_t idata = 0, odata = 0; + uint32_t oldreg = 0; + int regnr = relative_addr >> 3; + + if (writeflag == MEM_WRITE) +- idata = memory_readmax64(cpu, data, len | d->pci_little_endian); ++ idata = (uint32_t)memory_readmax64(cpu, data, ++ len | d->pci_little_endian); + + if ((relative_addr & 7) == 0 && regnr < N_REGS) { + if (writeflag == MEM_READ) { +@@ -916,8 +1263,15 @@ DEVICE_ACCESS(dec21143) + /* Turned off RX? Then go to stopped state: */ + d->reg[CSR_STATUS/8] &= ~STATUS_RS; + } ++ /* Maintain r/o filter mode bits: */ ++ d->reg[CSR_OPMODE/8] &= ++ ~(OPMODE_HP | OPMODE_HO | OPMODE_IF); ++ d->reg[CSR_OPMODE/8] |= oldreg & ++ (OPMODE_HP | OPMODE_HO | OPMODE_IF); + idata &= ~(OPMODE_HBD | OPMODE_SCR | OPMODE_PCS +- | OPMODE_PS | OPMODE_SF | OPMODE_TTM | OPMODE_FD); ++ | OPMODE_PS | OPMODE_SF | OPMODE_TTM | OPMODE_FD ++ | OPMODE_IF | OPMODE_HO | OPMODE_HP | OPMODE_PR ++ | OPMODE_PM); + if (idata & OPMODE_PNIC_IT) { + idata &= ~OPMODE_PNIC_IT; + d->tx_idling = d->tx_idling_threshold; +@@ -926,6 +1280,7 @@ DEVICE_ACCESS(dec21143) + fatal("[ dec21143: UNIMPLEMENTED OPMODE bits" + ": 0x%08x ]\n", (int)idata); + } ++ dec21143_update_rx_mode(d); + dev_dec21143_tick(cpu, extra); + } + break; +@@ -976,6 +1331,7 @@ DEVINIT(dec21143) + { + struct dec21143_data *d; + char name2[100]; ++ int leaf; + + CHECK_ALLOCATION(d = (struct dec21143_data *) malloc(sizeof(struct dec21143_data))); + memset(d, 0, sizeof(struct dec21143_data)); +@@ -983,15 +1339,80 @@ DEVINIT(dec21143) + INTERRUPT_CONNECT(devinit->interrupt_path, d->irq); + d->pci_little_endian = devinit->pci_little_endian; + +- net_generate_unique_mac(devinit->machine, d->mac); +- net_add_nic(devinit->machine->emul->net, d, d->mac); +- d->net = devinit->machine->emul->net; ++ /* XXX XXX XXX */ ++ switch (devinit->machine->machine_type) { ++ /* ++ * Footbridge systems -- this is actually configurable by ++ * system software, but this is the window setting that ++ * NetBSD uses. ++ */ ++ case MACHINE_CATS: ++ case MACHINE_NETWINDER: ++ d->xxx_dma_to_phys_mask = ~0x20000000; ++ break; ++ ++ /* ++ * V3 Semi PCI bus controller -- this is actually configurable ++ * by system sofware, but this is the value previously hard-coded ++ * for all platforms that did not work on Footbridge systems. ++ */ ++ case MACHINE_ALGOR: ++ /* FALLTHROUGH */ ++ ++ /* Other known users of dc21143 that came along for the ride. */ ++ case MACHINE_COBALT: ++ case MACHINE_PMPPC: ++ case MACHINE_PREP: ++ case MACHINE_MACPPC: ++ case MACHINE_MVMEPPC: ++ d->xxx_dma_to_phys_mask = 0x7fffffff; ++ break; ++ ++ default: ++ fatal("[ dec21143: default DMA mask for unhandled machine %d\n", ++ devinit->machine->machine_type); ++ d->xxx_dma_to_phys_mask = 0xffffffff; ++ } ++ ++ memset(d->srom, 0, sizeof(d->srom)); ++ ++ /* Version (= 1) and Chip count (= 1): */ ++ d->srom[TULIP_ROM_SROM_FORMAT_VERION] = 1; ++ d->srom[TULIP_ROM_CHIP_COUNT] = 1; ++ ++ leaf = 30; ++ d->srom[TULIP_ROM_CHIPn_DEVICE_NUMBER(0)] = 0; ++ d->srom[TULIP_ROM_CHIPn_INFO_LEAF_OFFSET(0)] = leaf & 255; ++ d->srom[TULIP_ROM_CHIPn_INFO_LEAF_OFFSET(0)+1] = leaf >> 8; ++ ++ d->srom[leaf+TULIP_ROM_IL_SELECT_CONN_TYPE] = 0; /* Not used? */ ++ d->srom[leaf+TULIP_ROM_IL_MEDIA_COUNT] = 2; ++ leaf += TULIP_ROM_IL_MEDIAn_BLOCK_BASE; ++ ++ d->srom[leaf] = 7; /* descriptor length */ ++ d->srom[leaf+1] = TULIP_ROM_MB_21142_SIA; ++ d->srom[leaf+2] = TULIP_ROM_MB_MEDIA_100TX; ++ /* here comes 4 bytes of GPIO control/data settings */ ++ leaf += d->srom[leaf]; ++ ++ d->srom[leaf] = 15; /* descriptor length */ ++ d->srom[leaf+1] = TULIP_ROM_MB_21142_MII; ++ d->srom[leaf+2] = 0; /* PHY nr */ ++ d->srom[leaf+3] = 0; /* len of select sequence */ ++ d->srom[leaf+4] = 0; /* len of reset sequence */ ++ /* 5,6, 7,8, 9,10, 11,12, 13,14 = unused by GXemul */ ++ leaf += d->srom[leaf]; ++ ++ net_generate_unique_mac(devinit->machine, d->nic.mac_address); ++ memcpy(d->srom + TULIP_ROM_IEEE_NETWORK_ADDRESS, d->nic.mac_address, 6); ++ net_add_nic(devinit->machine->emul->net, &d->nic); + + dec21143_reset(devinit->machine->cpus[0], d); + + snprintf(name2, sizeof(name2), "%s [%02x:%02x:%02x:%02x:%02x:%02x]", +- devinit->name, d->mac[0], d->mac[1], d->mac[2], d->mac[3], +- d->mac[4], d->mac[5]); ++ devinit->name, d->nic.mac_address[0], d->nic.mac_address[1], ++ d->nic.mac_address[2], d->nic.mac_address[3], ++ d->nic.mac_address[4], d->nic.mac_address[5]); + + memory_device_register(devinit->machine->memory, name2, + devinit->addr, 0x100, dev_dec21143_access, d, DM_DEFAULT, NULL); diff --git a/emulators/gxemul/patches/patch-src_devices_dev_ether.cc b/emulators/gxemul/patches/patch-src_devices_dev_ether.cc new file mode 100644 index 00000000000..678ac1cf7a0 --- /dev/null +++ b/emulators/gxemul/patches/patch-src_devices_dev_ether.cc @@ -0,0 +1,74 @@ +$NetBSD: patch-src_devices_dev_ether.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $ + +Add support for tap(4)-based networking. + +--- src/devices/dev_ether.cc.orig 2020-10-05 22:56:01.167967575 +0000 ++++ src/devices/dev_ether.cc 2020-10-05 22:56:17.330782903 +0000 +@@ -49,6 +49,8 @@ + #define DEV_ETHER_TICK_SHIFT 14 + + struct ether_data { ++ struct nic_data nic; ++ + unsigned char buf[DEV_ETHER_BUFFER_SIZE]; + unsigned char mac[6]; + +@@ -66,7 +68,7 @@ DEVICE_TICK(ether) + + d->status &= ~DEV_ETHER_STATUS_MORE_PACKETS_AVAILABLE; + if (cpu->machine->emul->net != NULL) +- r = net_ethernet_rx_avail(cpu->machine->emul->net, d); ++ r = net_ethernet_rx_avail(cpu->machine->emul->net, &d->nic); + if (r) + d->status |= DEV_ETHER_STATUS_MORE_PACKETS_AVAILABLE; + +@@ -147,7 +149,7 @@ DEVICE_ACCESS(ether) + else { + d->status &= ~DEV_ETHER_STATUS_PACKET_RECEIVED; + if (net_ethernet_rx(cpu->machine->emul->net, +- d, &incoming_ptr, &incoming_len)) { ++ &d->nic, &incoming_ptr, &incoming_len)) { + d->status |= + DEV_ETHER_STATUS_PACKET_RECEIVED; + if (incoming_len>DEV_ETHER_BUFFER_SIZE) +@@ -167,7 +169,7 @@ DEVICE_ACCESS(ether) + fatal("[ ether: SEND but no net? ]\n"); + else + net_ethernet_tx(cpu->machine->emul->net, +- d, d->buf, d->packet_len); ++ &d->nic, d->buf, d->packet_len); + d->status &= ~DEV_ETHER_STATUS_PACKET_RECEIVED; + dev_ether_tick(cpu, d); + break; +@@ -183,7 +185,7 @@ DEVICE_ACCESS(ether) + fatal("[ ether: read of MAC is not allowed! ]\n"); + } else { + // Write out the MAC address to the address given. +- cpu->memory_rw(cpu, cpu->mem, idata, d->mac, ++ cpu->memory_rw(cpu, cpu->mem, idata, d->nic.mac_address, + 6, MEM_WRITE, CACHE_NONE); + } + break; +@@ -221,9 +223,11 @@ DEVINIT(ether) + + INTERRUPT_CONNECT(devinit->interrupt_path, d->irq); + +- net_generate_unique_mac(devinit->machine, d->mac); ++ net_generate_unique_mac(devinit->machine, d->nic.mac_address); + snprintf(tmp, sizeof(tmp), "%02x:%02x:%02x:%02x:%02x:%02x", +- d->mac[0], d->mac[1], d->mac[2], d->mac[3], d->mac[4], d->mac[5]); ++ d->nic.mac_address[0], d->nic.mac_address[1], ++ d->nic.mac_address[2], d->nic.mac_address[3], ++ d->nic.mac_address[4], d->nic.mac_address[5]); + + snprintf(n1, nlen, "%s [%s]", devinit->name, tmp); + snprintf(n2, nlen, "%s [%s, control]", devinit->name, tmp); +@@ -237,7 +241,7 @@ DEVINIT(ether) + DEV_ETHER_LENGTH-DEV_ETHER_BUFFER_SIZE, dev_ether_access, (void *)d, + DM_DEFAULT, NULL); + +- net_add_nic(devinit->machine->emul->net, d, d->mac); ++ net_add_nic(devinit->machine->emul->net, &d->nic); + + machine_add_tickfunction(devinit->machine, + dev_ether_tick, d, DEV_ETHER_TICK_SHIFT); diff --git a/emulators/gxemul/patches/patch-src_devices_dev_le.cc b/emulators/gxemul/patches/patch-src_devices_dev_le.cc new file mode 100644 index 00000000000..8624436baa1 --- /dev/null +++ b/emulators/gxemul/patches/patch-src_devices_dev_le.cc @@ -0,0 +1,232 @@ +$NetBSD: patch-src_devices_dev_le.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $ + +- Add support for tap(4)-based networking. +- Process the setup packet, and implement address filtering. + +--- src/devices/dev_le.cc.orig 2020-10-05 22:56:40.808218933 +0000 ++++ src/devices/dev_le.cc 2020-10-05 22:56:55.751440817 +0000 +@@ -72,9 +72,10 @@ + + extern int quiet_mode; + +-#define LE_MODE_LOOP 4 +-#define LE_MODE_DTX 2 +-#define LE_MODE_DRX 1 ++#define LE_MODE_PROM 0x8000 ++#define LE_MODE_LOOP 0x0004 ++#define LE_MODE_DTX 0x0002 ++#define LE_MODE_DRX 0x0001 + + + #define N_REGISTERS 4 +@@ -83,6 +84,8 @@ extern int quiet_mode; + + + struct le_data { ++ struct nic_data nic; ++ + struct interrupt irq; + int irq_asserted; + +@@ -101,13 +104,14 @@ struct le_data { + uint32_t init_block_addr; + + uint16_t mode; +- uint64_t padr; /* MAC address */ +- uint64_t ladrf; ++ uint16_t ladrf[4]; + uint32_t rdra; /* receive descriptor ring address */ + int rlen; /* nr of rx descriptors */ + uint32_t tdra; /* transmit descriptor ring address */ + int tlen; /* nr ot tx descriptors */ + ++ int allmulti;/* receive all multicast packets */ ++ + /* Current rx and tx descriptor indices: */ + int rxp; + int txp; +@@ -157,6 +161,9 @@ static void le_write_16bit(struct le_dat + */ + static void le_chip_init(struct le_data *d) + { ++ uint16_t tmp; ++ uint8_t macaddr[6]; ++ + d->init_block_addr = (d->reg[1] & 0xffff) + ((d->reg[2] & 0xff) << 16); + if (d->init_block_addr & 1) + fatal("[ le: WARNING! initialization block address " +@@ -165,13 +172,36 @@ static void le_chip_init(struct le_data + debug("[ le: d->init_block_addr = 0x%06x ]\n", d->init_block_addr); + + d->mode = le_read_16bit(d, d->init_block_addr + 0); +- d->padr = le_read_16bit(d, d->init_block_addr + 2); +- d->padr += (le_read_16bit(d, d->init_block_addr + 4) << 16); +- d->padr += (le_read_16bit(d, d->init_block_addr + 6) << 32); +- d->ladrf = le_read_16bit(d, d->init_block_addr + 8); +- d->ladrf += (le_read_16bit(d, d->init_block_addr + 10) << 16); +- d->ladrf += (le_read_16bit(d, d->init_block_addr + 12) << 32); +- d->ladrf += (le_read_16bit(d, d->init_block_addr + 14) << 48); ++ ++ /* ++ * The MAC address is packed into the PADR field as 3 little-endian ++ * 16-bit words. ++ */ ++ tmp = le_read_16bit(d, d->init_block_addr + 2); ++ macaddr[0] = (uint8_t)(tmp); ++ macaddr[1] = (uint8_t)(tmp >> 8); ++ tmp = le_read_16bit(d, d->init_block_addr + 4); ++ macaddr[2] = (uint8_t)(tmp); ++ macaddr[3] = (uint8_t)(tmp >> 8); ++ tmp = le_read_16bit(d, d->init_block_addr + 6); ++ macaddr[4] = (uint8_t)(tmp); ++ macaddr[5] = (uint8_t)(tmp >> 8); ++ memcpy(d->nic.mac_address, macaddr, sizeof(d->nic.mac_address)); ++ ++ /* ++ * The muticast address filter is packed into the LADRF field ++ * as 4 little-endian 16-bit words. ++ */ ++ d->ladrf[0] = le_read_16bit(d, d->init_block_addr + 8); ++ d->ladrf[1] = le_read_16bit(d, d->init_block_addr + 10); ++ d->ladrf[2] = le_read_16bit(d, d->init_block_addr + 12); ++ d->ladrf[3] = le_read_16bit(d, d->init_block_addr + 14); ++ if (d->ladrf[0] == 0xffff && d->ladrf[1] == 0xffff && ++ d->ladrf[2] == 0xffff && d->ladrf[3] == 0xffff) ++ d->allmulti = 1; ++ else ++ d->allmulti = 0; ++ + d->rdra = le_read_16bit(d, d->init_block_addr + 16); + d->rdra += ((le_read_16bit(d, d->init_block_addr + 18) & 0xff) << 16); + d->rlen = 1 << ((le_read_16bit(d, d->init_block_addr + 18) >> 13) & 7); +@@ -179,13 +209,16 @@ static void le_chip_init(struct le_data + d->tdra += ((le_read_16bit(d, d->init_block_addr + 22) & 0xff) << 16); + d->tlen = 1 << ((le_read_16bit(d, d->init_block_addr + 22) >> 13) & 7); + +- debug("[ le: DEBUG: mode %04x ]\n", d->mode); +- debug("[ le: DEBUG: padr %016llx ]\n", (long long)d->padr); +- debug("[ le: DEBUG: ladrf %016llx ]\n", (long long)d->ladrf); +- debug("[ le: DEBUG: rdra %06llx ]\n", d->rdra); +- debug("[ le: DEBUG: rlen %3i ]\n", d->rlen); +- debug("[ le: DEBUG: tdra %06llx ]\n", d->tdra); +- debug("[ le: DEBUG: tlen %3i ]\n", d->tlen); ++ debug("[ le: DEBUG: mode %04x ]\n", d->mode); ++ debug("[ le: DEBUG: padr %02x:%02x:%02x:%02x:%02x:%02x ]\n", ++ macaddr[0], macaddr[1], macaddr[2], ++ macaddr[3], macaddr[4], macaddr[5]); ++ debug("[ le: DEBUG: ladrf %04x:%04x:%04x:%04x ]\n", ++ d->ladrf[0], d->ladrf[1], d->ladrf[2], d->ladrf[3]); ++ debug("[ le: DEBUG: rdra %06llx ]\n", d->rdra); ++ debug("[ le: DEBUG: rlen %3i ]\n", d->rlen); ++ debug("[ le: DEBUG: tdra %06llx ]\n", d->tdra); ++ debug("[ le: DEBUG: tlen %3i ]\n", d->tlen); + + /* Set TXON and RXON, unless they are disabled by 'mode': */ + if (d->mode & LE_MODE_DTX) +@@ -198,6 +231,9 @@ static void le_chip_init(struct le_data + else + d->reg[0] |= LE_RXON; + ++ /* Initialize promiscuous mode. */ ++ d->nic.promiscuous_mode = (d->mode & LE_MODE_PROM) ? 1 : 0; ++ + /* Go to the start of the descriptor rings: */ + d->rxp = d->txp = 0; + +@@ -308,7 +344,8 @@ static void le_tx(struct net *net, struc + * the packet. + */ + if (enp) { +- net_ethernet_tx(net, d, d->tx_packet, d->tx_packet_len); ++ net_ethernet_tx(net, &d->nic, d->tx_packet, ++ d->tx_packet_len); + + free(d->tx_packet); + d->tx_packet = NULL; +@@ -446,6 +483,62 @@ static void le_rx(struct net *net, struc + + + /* ++ * le_rx_drop_packet(): ++ * ++ * Implement the logic to determine if we should drop a packet ++ * before passing it to the guest. Returns 1 if the packet was ++ * dropped. ++ */ ++static int ++le_rx_drop_packet(struct net *net, struct le_data *d) ++{ ++ /* Only implement filtering if using a tap device. */ ++ if (net->tapdev == NULL) ++ return 0; ++ ++ /* ++ * The network layer has already checked for our MAC address ++ * or promiscuous mode. We just need to check the multicast ++ * filter or broadcast. ++ */ ++ ++ /* If the packet is not multicast, we know it should be received. */ ++ if (! net_ether_multicast(d->rx_packet)) ++ return 0; ++ ++ /* ++ * Optimization -- if the guest has set all of the filter ++ * bits, then we can skip additional checks. ++ */ ++ if (d->allmulti) ++ return 0; ++ ++ /* Check for broadcast. */ ++ if (net_ether_broadcast(d->rx_packet)) ++ return 0; ++ ++ /* ++ * Check the multicast address filter. We pass the address ++ * through the little-endian Ethernet CRC generator. The ++ * high-order 6 bits are the index into the 64-bit filter. ++ * The upper 2 bits select the 16-bit filter word, and the ++ * remaining 4 select the bit in the word. ++ */ ++ uint32_t crc = net_ether_crc32_le(d->rx_packet, 6); ++ crc >>= 26; ++ if (d->ladrf[crc >> 4] & (1 << (crc & 0xf))) ++ return 0; ++ ++ /* Not for us; drop the packet. */ ++ free(d->rx_packet); ++ d->rx_packet = NULL; ++ d->rx_packet_len = 0; ++ ++ return 1; ++} ++ ++ ++/* + * le_register_fix(): + */ + static void le_register_fix(struct net *net, struct le_data *d) +@@ -481,9 +574,12 @@ static void le_register_fix(struct net * + break; + + if (d->rx_packet == NULL && +- net_ethernet_rx_avail(net, d)) +- net_ethernet_rx(net, d, ++ net_ethernet_rx_avail(net, &d->nic)) { ++ net_ethernet_rx(net, &d->nic, + &d->rx_packet, &d->rx_packet_len); ++ if (le_rx_drop_packet(net, d)) ++ continue; ++ } + } while (d->rx_packet != NULL); + } + +@@ -813,6 +909,7 @@ void dev_le_init(struct machine *machine + + machine_add_tickfunction(machine, dev_le_tick, d, LE_TICK_SHIFT); + +- net_add_nic(machine->emul->net, d, &d->rom[0]); ++ memcpy(d->nic.mac_address, &d->rom[0], sizeof(d->nic.mac_address)); ++ net_add_nic(machine->emul->net, &d->nic); + } + diff --git a/emulators/gxemul/patches/patch-src_devices_dev_rtl8139c.cc b/emulators/gxemul/patches/patch-src_devices_dev_rtl8139c.cc new file mode 100644 index 00000000000..586268d7248 --- /dev/null +++ b/emulators/gxemul/patches/patch-src_devices_dev_rtl8139c.cc @@ -0,0 +1,51 @@ +$NetBSD: patch-src_devices_dev_rtl8139c.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $ + +Updates for tap(4)-related changes to Ethernet support. + +--- src/devices/dev_rtl8139c.cc.orig 2020-10-05 22:57:40.189047655 +0000 ++++ src/devices/dev_rtl8139c.cc 2020-10-05 22:57:53.936646578 +0000 +@@ -50,8 +50,9 @@ + #define EEPROM_SIZE 0x100 + + struct rtl8139c_data { ++ struct nic_data nic; ++ + struct interrupt irq; +- unsigned char macaddr[6]; + + /* Registers: */ + uint8_t rl_command; +@@ -205,25 +206,26 @@ DEVINIT(rtl8139c) + + INTERRUPT_CONNECT(devinit->interrupt_path, d->irq); + +- net_generate_unique_mac(devinit->machine, d->macaddr); ++ net_generate_unique_mac(devinit->machine, d->nic.mac_address); + + /* TODO: eeprom address width = 6 on 8129? */ + d->eeprom_address_width = 8; + d->eeprom_reg[0] = 0x8139; +- d->eeprom_reg[7] = d->macaddr[0] + (d->macaddr[1] << 8); +- d->eeprom_reg[8] = d->macaddr[2] + (d->macaddr[3] << 8); +- d->eeprom_reg[9] = d->macaddr[4] + (d->macaddr[5] << 8); ++ d->eeprom_reg[7] = d->nic.mac_address[0] + (d->nic.mac_address[1] << 8); ++ d->eeprom_reg[8] = d->nic.mac_address[2] + (d->nic.mac_address[3] << 8); ++ d->eeprom_reg[9] = d->nic.mac_address[4] + (d->nic.mac_address[5] << 8); + + CHECK_ALLOCATION(name2 = (char *) malloc(nlen)); + snprintf(name2, nlen, "%s [%02x:%02x:%02x:%02x:%02x:%02x]", +- devinit->name, d->macaddr[0], d->macaddr[1], d->macaddr[2], +- d->macaddr[3], d->macaddr[4], d->macaddr[5]); ++ devinit->name, d->nic.mac_address[0], d->nic.mac_address[1], ++ d->nic.mac_address[2], d->nic.mac_address[3], ++ d->nic.mac_address[4], d->nic.mac_address[5]); + + memory_device_register(devinit->machine->memory, name2, + devinit->addr, DEV_RTL8139C_LENGTH, dev_rtl8139c_access, (void *)d, + DM_DEFAULT, NULL); + +- net_add_nic(devinit->machine->emul->net, d, d->macaddr); ++ net_add_nic(devinit->machine->emul->net, &d->nic); + + return 1; + } diff --git a/emulators/gxemul/patches/patch-src_devices_dev_sgi_mec.cc b/emulators/gxemul/patches/patch-src_devices_dev_sgi_mec.cc new file mode 100644 index 00000000000..a50fc819f97 --- /dev/null +++ b/emulators/gxemul/patches/patch-src_devices_dev_sgi_mec.cc @@ -0,0 +1,69 @@ +$NetBSD: patch-src_devices_dev_sgi_mec.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $ + +Updates for tap(4)-related changes to Ethernet support. + +--- src/devices/dev_sgi_mec.cc.orig 2020-10-05 22:58:11.248005164 +0000 ++++ src/devices/dev_sgi_mec.cc 2020-10-05 22:58:31.644418157 +0000 +@@ -81,13 +81,13 @@ + #define N_RX_ADDRESSES 16 + + struct sgi_mec_data { ++ struct nic_data nic; ++ + uint64_t reg[DEV_SGI_MEC_LENGTH / sizeof(uint64_t)]; + + struct interrupt irq; + int prev_asserted; + +- unsigned char macaddr[6]; +- + unsigned char cur_tx_packet[MAX_TX_PACKET_LEN]; + int cur_tx_packet_len; + +@@ -157,8 +157,8 @@ static int mec_try_rx(struct cpu *cpu, s + } + + if (d->cur_rx_packet == NULL && +- net_ethernet_rx_avail(cpu->machine->emul->net, d)) +- net_ethernet_rx(cpu->machine->emul->net, d, ++ net_ethernet_rx_avail(cpu->machine->emul->net, &d->nic)) ++ net_ethernet_rx(cpu->machine->emul->net, &d->nic, + &d->cur_rx_packet, &d->cur_rx_packet_len); + + if (d->cur_rx_packet == NULL) +@@ -343,7 +343,7 @@ static int mec_try_tx(struct cpu *cpu, s + if (j < len) + fatal("[ mec_try_tx: not enough data? ]\n"); + +- net_ethernet_tx(cpu->machine->emul->net, d, ++ net_ethernet_tx(cpu->machine->emul->net, &d->nic, + d->cur_tx_packet, d->cur_tx_packet_len); + + /* see openbsd's if_mec.c for details */ +@@ -675,13 +675,14 @@ void dev_sgi_mec_init(struct machine *ma + memset(d, 0, sizeof(struct sgi_mec_data)); + + INTERRUPT_CONNECT(irq_path, d->irq); +- memcpy(d->macaddr, macaddr, 6); ++ memcpy(d->nic.mac_address, macaddr, 6); + mec_reset(d); + + CHECK_ALLOCATION(name2 = (char *) malloc(nlen)); + snprintf(name2, nlen, "mec [%02x:%02x:%02x:%02x:%02x:%02x]", +- d->macaddr[0], d->macaddr[1], d->macaddr[2], +- d->macaddr[3], d->macaddr[4], d->macaddr[5]); ++ d->nic.mac_address[0], d->nic.mac_address[1], ++ d->nic.mac_address[2], d->nic.mac_address[3], ++ d->nic.mac_address[4], d->nic.mac_address[5]); + + memory_device_register(mem, name2, baseaddr, + DEV_SGI_MEC_LENGTH, dev_sgi_mec_access, (void *)d, +@@ -690,7 +691,7 @@ void dev_sgi_mec_init(struct machine *ma + machine_add_tickfunction(machine, dev_sgi_mec_tick, d, + MEC_TICK_SHIFT); + +- net_add_nic(machine->emul->net, d, macaddr); ++ net_add_nic(machine->emul->net, &d->nic); + } + + diff --git a/emulators/gxemul/patches/patch-src_devices_dev_sn.cc b/emulators/gxemul/patches/patch-src_devices_dev_sn.cc new file mode 100644 index 00000000000..c25906f27b1 --- /dev/null +++ b/emulators/gxemul/patches/patch-src_devices_dev_sn.cc @@ -0,0 +1,40 @@ +$NetBSD: patch-src_devices_dev_sn.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $ + +Updates for tap(4)-related changes to Ethernet support. + +--- src/devices/dev_sn.cc.orig 2020-10-05 22:58:49.162038719 +0000 ++++ src/devices/dev_sn.cc 2020-10-05 22:59:04.524312162 +0000 +@@ -48,8 +48,8 @@ + #define DEV_SN_LENGTH 0x1000 + + struct sn_data { ++ struct nic_data nic; + struct interrupt irq; +- unsigned char macaddr[6]; + uint32_t reg[SONIC_NREGS]; + }; + +@@ -104,18 +104,19 @@ DEVINIT(sn) + + INTERRUPT_CONNECT(devinit->interrupt_path, d->irq); + +- net_generate_unique_mac(devinit->machine, d->macaddr); ++ net_generate_unique_mac(devinit->machine, d->nic.mac_address); + + CHECK_ALLOCATION(name2 = (char *) malloc(nlen)); + snprintf(name2, nlen, "%s [%02x:%02x:%02x:%02x:%02x:%02x]", +- devinit->name, d->macaddr[0], d->macaddr[1], d->macaddr[2], +- d->macaddr[3], d->macaddr[4], d->macaddr[5]); ++ devinit->name, d->nic.mac_address[0], d->nic.mac_address[1], ++ d->nic.mac_address[2], d->nic.mac_address[3], ++ d->nic.mac_address[4], d->nic.mac_address[5]); + + memory_device_register(devinit->machine->memory, name2, + devinit->addr, DEV_SN_LENGTH, + dev_sn_access, (void *)d, DM_DEFAULT, NULL); + +- net_add_nic(devinit->machine->emul->net, d, d->macaddr); ++ net_add_nic(devinit->machine->emul->net, &d->nic); + + return 1; + } diff --git a/emulators/gxemul/patches/patch-src_include_net.h b/emulators/gxemul/patches/patch-src_include_net.h new file mode 100644 index 00000000000..c73357e4bc5 --- /dev/null +++ b/emulators/gxemul/patches/patch-src_include_net.h @@ -0,0 +1,107 @@ +$NetBSD: patch-src_include_net.h,v 1.1 2020/10/07 00:43:05 thorpej Exp $ + +Add support for tap(4)-based networking. + +--- src/include/net.h.orig 2020-10-05 22:59:17.124851904 +0000 ++++ src/include/net.h 2020-10-05 22:59:31.468423021 +0000 +@@ -112,6 +112,15 @@ struct tcp_connection { + + /*****************************************************************************/ + ++/* Common data for emulated Ethernet NICs. */ ++ ++struct nic_data { ++ struct net *net; /* net we belong to */ ++ uint8_t mac_address[6]; /* our MAC address */ ++ int promiscuous_mode;/* receive all packets */ ++}; ++ ++/*****************************************************************************/ + + #define MAX_TCP_CONNECTIONS 100 + #define MAX_UDP_CONNECTIONS 100 +@@ -120,13 +129,17 @@ struct net { + /* The emul struct which this net belong to: */ + struct emul *emul; + ++ /* The network's tap device, if we're using tap: */ ++ const char *tapdev; ++ int tap_fd; ++ + /* The network's addresses: */ + struct in_addr netmask_ipv4; + int netmask_ipv4_len; + + /* NICs connected to this network: */ + int n_nics; +- void **nic_extra; /* one void * per NIC */ ++ struct nic_data **nic_data; /* one per NIC */ + + /* The "special machine": */ + unsigned char gateway_ipv4_addr[4]; +@@ -151,6 +164,18 @@ struct net { + struct remote_net *remote_nets; + }; + ++/* net_tap.c: */ ++void net_tap_rx_avail(struct net *net); ++void net_tap_tx(struct net *net, struct nic_data *nic, unsigned char *packet, ++ int len); ++int net_tap_init(struct net *net, const char *tapdev); ++ ++/* net_ether.cc */ ++int net_ether_eq(const uint8_t *a1, const uint8_t *a2); ++int net_ether_broadcast(const uint8_t *a); ++int net_ether_multicast(const uint8_t *a); ++uint32_t net_ether_crc32_le(const uint8_t *buf, size_t len); ++ + /* net_misc.c: */ + void net_debugaddr(void *addr, int type); + void net_generate_unique_mac(struct machine *, unsigned char *macbuf); +@@ -162,25 +187,27 @@ void net_ip_checksum(unsigned char *ip_h + void net_ip_tcp_checksum(unsigned char *tcp_header, int chksumoffset, + int tcp_len, unsigned char *srcaddr, unsigned char *dstaddr, + int udpflag); +-void net_ip_tcp_connectionreply(struct net *net, void *extra, ++void net_ip_tcp_connectionreply(struct net *net, struct nic_data *nic, + int con_id, int connecting, unsigned char *data, int datalen, int rst); +-void net_ip_broadcast(struct net *net, void *extra, ++void net_ip_broadcast(struct net *net, struct nic_data *nic, + unsigned char *packet, int len); +-void net_ip(struct net *net, void *extra, unsigned char *packet, int len); +-void net_udp_rx_avail(struct net *net, void *extra); +-void net_tcp_rx_avail(struct net *net, void *extra); ++void net_ip(struct net *net, struct nic_data *nic, unsigned char *packet, ++ int len); ++void net_udp_rx_avail(struct net *net, struct nic_data *nic); ++void net_tcp_rx_avail(struct net *net, struct nic_data *nic); + + /* net.c: */ + struct ethernet_packet_link *net_allocate_ethernet_packet_link( +- struct net *net, void *extra, size_t len); +-int net_ethernet_rx_avail(struct net *net, void *extra); +-int net_ethernet_rx(struct net *net, void *extra, ++ struct net *net, struct nic_data *nic, size_t len); ++int net_ethernet_rx_avail(struct net *net, struct nic_data *nic); ++int net_ethernet_rx(struct net *net, struct nic_data *nic, + unsigned char **packetp, int *lenp); +-void net_ethernet_tx(struct net *net, void *extra, ++void net_ethernet_tx(struct net *net, struct nic_data *nic, + unsigned char *packet, int len); + void net_dumpinfo(struct net *net); +-void net_add_nic(struct net *net, void *extra, unsigned char *macaddr); ++void net_add_nic(struct net *net, struct nic_data *nic); + struct net *net_init(struct emul *emul, int init_flags, ++ const char *tapdev, + const char *ipv4addr, int netipv4len, char **remote, int n_remote, + int local_port, const char *settings_prefix); + +@@ -195,7 +222,7 @@ struct ethernet_packet_link { + struct ethernet_packet_link *prev; + struct ethernet_packet_link *next; + +- void *extra; ++ struct nic_data *nic; + unsigned char *data; + int len; + }; diff --git a/emulators/gxemul/patches/patch-src_net_Makefile.skel b/emulators/gxemul/patches/patch-src_net_Makefile.skel new file mode 100644 index 00000000000..7e6abda9d83 --- /dev/null +++ b/emulators/gxemul/patches/patch-src_net_Makefile.skel @@ -0,0 +1,15 @@ +$NetBSD: patch-src_net_Makefile.skel,v 1.1 2020/10/07 00:43:05 thorpej Exp $ + +Add support for tap(4)-based networking. + +--- src/net/Makefile.skel.orig 2020-10-05 22:59:56.683092920 +0000 ++++ src/net/Makefile.skel 2020-10-05 23:00:09.053165122 +0000 +@@ -4,7 +4,7 @@ + + CXXFLAGS=$(CWARNINGS) $(COPTIM) $(XINCLUDE) $(DINCLUDE) + +-OBJS=net.o net_ip.o net_misc.o ++OBJS=net.o net_ip.o net_misc.o net_tap.o net_ether.o + + all: $(OBJS) + diff --git a/emulators/gxemul/patches/patch-src_net_net.cc b/emulators/gxemul/patches/patch-src_net_net.cc new file mode 100644 index 00000000000..0cadc3c45dd --- /dev/null +++ b/emulators/gxemul/patches/patch-src_net_net.cc @@ -0,0 +1,318 @@ +$NetBSD: patch-src_net_net.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $ + +Add support for tap(4)-based networking. + +--- src/net/net.cc.orig 2020-10-05 23:00:24.839832619 +0000 ++++ src/net/net.cc 2020-10-05 23:00:41.597469289 +0000 +@@ -30,10 +30,10 @@ + * (Read the README file in this directory for more details.) + * + * +- * NOTE: The 'extra' argument used in many functions in this file is a pointer +- * to something unique for each NIC (i.e. the NIC itself :-), so that if +- * multiple NICs are emulated concurrently, they will not get packets that +- * are meant for some other controller. ++ * NOTE: The 'nic' argument used in many functions in this file is a pointer ++ * to the nic_data for each NIC, so that if multiple NICs are emulated ++ * concurrently, they will not get packets that are meant for some other ++ * controller. + */ + + #include <stdio.h> +@@ -62,7 +62,7 @@ + * + * This routine allocates an ethernet_packet_link struct, and adds it at + * the end of the packet chain. A data buffer is allocated, and the data, +- * extra, and len fields of the link are set. ++ * nic, and len fields of the link are set. + * + * Note: The data buffer is not zeroed. + * +@@ -70,7 +70,7 @@ + * failure. + */ + struct ethernet_packet_link *net_allocate_ethernet_packet_link( +- struct net *net, void *extra, size_t len) ++ struct net *net, struct nic_data *nic, size_t len) + { + struct ethernet_packet_link *lp; + +@@ -78,7 +78,7 @@ struct ethernet_packet_link *net_allocat + malloc(sizeof(struct ethernet_packet_link))); + + lp->len = len; +- lp->extra = extra; ++ lp->nic = nic; + CHECK_ALLOCATION(lp->data = (unsigned char *) malloc(len)); + + lp->next = NULL; +@@ -116,7 +116,7 @@ struct ethernet_packet_link *net_allocat + * An ARP request with the same from and to IP addresses should be ignored. + * (This would be a host testing to see if there is an IP collision.) + */ +-static void net_arp(struct net *net, void *extra, ++static void net_arp(struct net *net, struct nic_data *nic, + unsigned char *packet, int len, int reverse) + { + int q; +@@ -161,7 +161,7 @@ static void net_arp(struct net *net, voi + break; + + lp = net_allocate_ethernet_packet_link( +- net, extra, 60 + 14); ++ net, nic, 60 + 14); + + /* Copy the old packet first: */ + memset(lp->data, 0, 60 + 14); +@@ -186,7 +186,7 @@ static void net_arp(struct net *net, voi + break; + case 3: /* Reverse Request */ + lp = net_allocate_ethernet_packet_link( +- net, extra, 60 + 14); ++ net, nic, 60 + 14); + + /* Copy the old packet first: */ + memset(lp->data, 0, 60 + 14); +@@ -242,7 +242,7 @@ static void net_arp(struct net *net, voi + /* + * net_ethernet_rx_avail(): + * +- * Return 1 if there is a packet available for this 'extra' pointer, otherwise ++ * Return 1 if there is a packet available for this nic, otherwise + * return 0. + * + * Appart from actually checking for incoming packets from the outside world, +@@ -250,12 +250,21 @@ static void net_arp(struct net *net, voi + * a return value telling us whether there is a packet or not, we don't + * actually get the packet. + */ +-int net_ethernet_rx_avail(struct net *net, void *extra) ++int net_ethernet_rx_avail(struct net *net, struct nic_data *nic) + { + if (net == NULL) + return 0; + + /* ++ * If we're using a tap device, check in with that and ++ * that's it. ++ */ ++ if (net->tapdev) { ++ net_tap_rx_avail(net); ++ return net_ethernet_rx(net, nic, NULL, NULL); ++ } ++ ++ /* + * If the network is distributed across multiple emulator processes, + * then receive incoming packets from those processes. + */ +@@ -282,7 +291,7 @@ int net_ethernet_rx_avail(struct net *ne + for (i=0; i<net->n_nics; i++) { + struct ethernet_packet_link *lp; + lp = net_allocate_ethernet_packet_link( +- net, net->nic_extra[i], res); ++ net, net->nic_data[i], res); + memcpy(lp->data, buf, res); + } + } +@@ -290,10 +299,10 @@ int net_ethernet_rx_avail(struct net *ne + } + + /* IP protocol specific: */ +- net_udp_rx_avail(net, extra); +- net_tcp_rx_avail(net, extra); ++ net_udp_rx_avail(net, nic); ++ net_tcp_rx_avail(net, nic); + +- return net_ethernet_rx(net, extra, NULL, NULL); ++ return net_ethernet_rx(net, nic, NULL, NULL); + } + + +@@ -309,11 +318,11 @@ int net_ethernet_rx_avail(struct net *ne + * available, 0 is returned. + * + * If packetp is NULL, then the search is aborted as soon as a packet with +- * the correct 'extra' field is found, and a 1 is returned, but as packetp ++ * the correct 'nic' field is found, and a 1 is returned, but as packetp + * is NULL we can't return the actual packet. (This is the internal form + * if net_ethernet_rx_avail().) + */ +-int net_ethernet_rx(struct net *net, void *extra, ++int net_ethernet_rx(struct net *net, struct nic_data *nic, + unsigned char **packetp, int *lenp) + { + struct ethernet_packet_link *lp, *prev; +@@ -321,12 +330,12 @@ int net_ethernet_rx(struct net *net, voi + if (net == NULL) + return 0; + +- /* Find the first packet which has the right 'extra' field. */ ++ /* Find the first packet which has the right 'nic' field. */ + + lp = net->first_ethernet_packet; + prev = NULL; + while (lp != NULL) { +- if (lp->extra == extra) { ++ if (lp->nic == nic) { + /* We found a packet for this controller! */ + if (packetp == NULL || lenp == NULL) + return 1; +@@ -368,7 +377,7 @@ int net_ethernet_rx(struct net *net, voi + * If the packet can be handled here, it will not necessarily be transmitted + * to the outside world. + */ +-void net_ethernet_tx(struct net *net, void *extra, ++void net_ethernet_tx(struct net *net, struct nic_data *nic, + unsigned char *packet, int len) + { + int i, eth_type, for_the_gateway; +@@ -376,8 +385,6 @@ void net_ethernet_tx(struct net *net, vo + if (net == NULL) + return; + +- for_the_gateway = !memcmp(packet, net->gateway_ethernet_addr, 6); +- + /* Drop too small packets: */ + if (len < 20) { + fatal("[ net_ethernet_tx: Warning: dropping tiny packet " +@@ -386,15 +393,26 @@ void net_ethernet_tx(struct net *net, vo + } + + /* ++ * If we're using a tap device, we send the packet that way ++ * and that's it. ++ */ ++ if (net->tapdev) { ++ net_tap_tx(net, nic, packet, len); ++ return; ++ } ++ ++ for_the_gateway = !memcmp(packet, net->gateway_ethernet_addr, 6); ++ ++ /* + * Copy this packet to all other NICs on this network (except if + * it is aimed specifically at the gateway's ethernet address): + */ +- if (!for_the_gateway && extra != NULL && net->n_nics > 0) { ++ if (!for_the_gateway && nic != NULL && net->n_nics > 0) { + for (i=0; i<net->n_nics; i++) +- if (extra != net->nic_extra[i]) { ++ if (nic != net->nic_data[i]) { + struct ethernet_packet_link *lp; + lp = net_allocate_ethernet_packet_link(net, +- net->nic_extra[i], len); ++ net->nic_data[i], len); + + /* Copy the entire packet: */ + memcpy(lp->data, packet, len); +@@ -438,7 +456,7 @@ void net_ethernet_tx(struct net *net, vo + if (eth_type == ETHERTYPE_IP) { + /* Routed via the gateway? */ + if (for_the_gateway) { +- net_ip(net, extra, packet, len); ++ net_ip(net, nic, packet, len); + return; + } + +@@ -446,7 +464,7 @@ void net_ethernet_tx(struct net *net, vo + if (packet[0] == 0xff && packet[1] == 0xff && + packet[2] == 0xff && packet[3] == 0xff && + packet[4] == 0xff && packet[5] == 0xff) { +- net_ip_broadcast(net, extra, packet, len); ++ net_ip_broadcast(net, nic, packet, len); + return; + } + +@@ -465,13 +483,13 @@ void net_ethernet_tx(struct net *net, vo + if (len != 42 && len != 60) + fatal("[ net_ethernet_tx: WARNING! unusual " + "ARP len (%i) ]\n", len); +- net_arp(net, extra, packet + 14, len - 14, 0); ++ net_arp(net, nic, packet + 14, len - 14, 0); + return; + } + + /* RARP: */ + if (eth_type == ETHERTYPE_REVARP) { +- net_arp(net, extra, packet + 14, len - 14, 1); ++ net_arp(net, nic, packet + 14, len - 14, 1); + return; + } + +@@ -595,21 +613,28 @@ static void parse_resolvconf(struct net + * Add a NIC to a network. (All NICs on a network will see each other's + * packets.) + */ +-void net_add_nic(struct net *net, void *extra, unsigned char *macaddr) ++void net_add_nic(struct net *net, struct nic_data *nic) + { + if (net == NULL) + return; + +- if (extra == NULL) { +- fprintf(stderr, "net_add_nic(): extra = NULL\n"); ++ if (nic == NULL) { ++ fprintf(stderr, "net_add_nic(): nic = NULL\n"); + exit(1); + } + +- net->n_nics ++; +- CHECK_ALLOCATION(net->nic_extra = (void **) +- realloc(net->nic_extra, sizeof(void *) * net->n_nics)); ++ /* ++ * Set up some of the basics for this NIC. We assume the ++ * device has set up all of the other fields. ++ */ ++ nic->net = net; ++ nic->promiscuous_mode = 0; ++ ++ net->n_nics++; ++ CHECK_ALLOCATION(net->nic_data = (struct nic_data **) ++ realloc(net->nic_data, sizeof(struct nic_data *) * net->n_nics)); + +- net->nic_extra[net->n_nics - 1] = extra; ++ net->nic_data[net->n_nics - 1] = nic; + } + + +@@ -661,6 +686,12 @@ void net_dumpinfo(struct net *net) + + debug_indentation(iadd); + ++ if (net->tapdev) { ++ debug("tap device: %s\n", net->tapdev); ++ debug_indentation(-iadd); ++ return; ++ } ++ + debug("simulated network: "); + net_debugaddr(&net->netmask_ipv4, NET_ADDR_IPV4); + debug("/%i", net->netmask_ipv4_len); +@@ -718,6 +749,7 @@ void net_dumpinfo(struct net *net) + * On failure, exit() is called. + */ + struct net *net_init(struct emul *emul, int init_flags, ++ const char *tapdev, + const char *ipv4addr, int netipv4len, + char **remote, int n_remote, int local_port, + const char *settings_prefix) +@@ -734,6 +766,19 @@ struct net *net_init(struct emul *emul, + /* Sane defaults: */ + net->timestamp = 0; + net->first_ethernet_packet = net->last_ethernet_packet = NULL; ++ net->tapdev = NULL; ++ net->tap_fd = -1; ++ ++ /* ++ * If we're using a tap device, attempt to initialize it and ++ * none of the other stuff. ++ */ ++ if (tapdev) { ++ if (! net_tap_init(net, tapdev)) ++ exit(1); ++ net_dumpinfo(net); ++ return net; ++ } + + #ifdef HAVE_INET_PTON + res = inet_pton(AF_INET, ipv4addr, &net->netmask_ipv4); diff --git a/emulators/gxemul/patches/patch-src_net_net_ether.cc b/emulators/gxemul/patches/patch-src_net_net_ether.cc new file mode 100644 index 00000000000..ed31349cfb6 --- /dev/null +++ b/emulators/gxemul/patches/patch-src_net_net_ether.cc @@ -0,0 +1,150 @@ +$NetBSD: patch-src_net_net_ether.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $ + +Add some generic Ethernet routines used for address filtering. + +--- /dev/null 2020-10-05 22:44:11.028207457 +0000 ++++ src/net/net_ether.cc 2020-10-05 23:01:48.744053911 +0000 +@@ -0,0 +1,143 @@ ++/* ++ * Copyright (C) 2020 Jason R. Thorpe. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The name of the author may not be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ */ ++ ++/* ++ * Common Ethernet support routines. ++ */ ++ ++#include <sys/types.h> ++#include <string.h> ++ ++#include "misc.h" ++#include "net.h" ++ ++ ++/* ++ * net_ether_eq(): ++ * ++ * Compare two Ethernet addresses for equality. ++ */ ++int net_ether_eq(const uint8_t *a1, const uint8_t *a2) ++{ ++ ++ return a1[5] == a2[5] && ++ a1[4] == a2[4] && ++ a1[3] == a2[3] && ++ a1[2] == a2[2] && ++ a1[1] == a2[1] && ++ a1[0] == a2[0]; ++} ++ ++ ++/* ++ * net_ether_broadcast(): ++ * ++ * Returns 1 if the specified destination address is the Ethernet ++ * broadcast address. ++ */ ++int net_ether_broadcast(const uint8_t *a) ++{ ++ static const uint8_t ether_broadcast[6] = ++ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; ++ ++ return net_ether_eq(a, ether_broadcast); ++} ++ ++ ++/* ++ * net_ether_multicast(): ++ * ++ * Returns 1 if the specfied destination address is an Ethernet ++ * multicast address. ++ * ++ * Note that this also matches Ethernet broadcast, which is just ++ * a special case of multicast. ++ */ ++int net_ether_multicast(const uint8_t *a) ++{ ++ return (*a & 0x01); ++} ++ ++ ++/* ++ * Copyright (c) 1982, 1989, 1993 ++ * The Regents of the University of California. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the University nor the names of its contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * @(#)if_ethersubr.c 8.2 (Berkeley) 4/4/96 ++ */ ++ ++ ++/* ++ * net_ether_crc32_le(): ++ * ++ * Fast table-driven little-endian Ethernet CRC generator. ++ */ ++uint32_t net_ether_crc32_le(const uint8_t *buf, size_t len) ++{ ++ static const uint32_t crctab[] = { ++ 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, ++ 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, ++ 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, ++ 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c ++ }; ++ uint32_t crc; ++ size_t i; ++ ++ crc = 0xffffffffU; /* initial value */ ++ ++ for (i = 0; i < len; i++) { ++ crc ^= buf[i]; ++ crc = (crc >> 4) ^ crctab[crc & 0xf]; ++ crc = (crc >> 4) ^ crctab[crc & 0xf]; ++ } ++ ++ return crc; ++} diff --git a/emulators/gxemul/patches/patch-src_net_net_ip.cc b/emulators/gxemul/patches/patch-src_net_net_ip.cc new file mode 100644 index 00000000000..89e8b9370c1 --- /dev/null +++ b/emulators/gxemul/patches/patch-src_net_net_ip.cc @@ -0,0 +1,238 @@ +$NetBSD: patch-src_net_net_ip.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $ + +Add support for tap(4)-based networking. + +--- src/net/net_ip.cc.orig 2020-10-05 23:02:21.375165006 +0000 ++++ src/net/net_ip.cc 2020-10-05 23:02:33.181030722 +0000 +@@ -155,7 +155,7 @@ void net_ip_tcp_checksum(unsigned char * + * 1c1d1e1f202122232425262728292a2b + * 2c2d2e2f3031323334353637 + */ +-static void net_ip_icmp(struct net *net, void *extra, ++static void net_ip_icmp(struct net *net, struct nic_data *nic, + unsigned char *packet, int len) + { + int type; +@@ -166,7 +166,7 @@ static void net_ip_icmp(struct net *net, + switch (type) { + case 8: /* ECHO request */ + debug("[ ICMP echo ]\n"); +- lp = net_allocate_ethernet_packet_link(net, extra, len); ++ lp = net_allocate_ethernet_packet_link(net, nic, len); + + /* Copy the old packet first: */ + memcpy(lp->data + 12, packet + 12, len - 12); +@@ -225,7 +225,7 @@ static void tcp_closeconnection(struct n + * This creates an ethernet packet for the guest OS with an ACK to the + * initial SYN packet. + */ +-void net_ip_tcp_connectionreply(struct net *net, void *extra, ++void net_ip_tcp_connectionreply(struct net *net, struct nic_data *nic, + int con_id, int connecting, unsigned char *data, int datalen, int rst) + { + struct ethernet_packet_link *lp; +@@ -238,7 +238,7 @@ void net_ip_tcp_connectionreply(struct n + net->tcp_connections[con_id].tcp_id ++; + tcp_length = 20 + option_len + datalen; + ip_len = 20 + tcp_length; +- lp = net_allocate_ethernet_packet_link(net, extra, 14 + ip_len); ++ lp = net_allocate_ethernet_packet_link(net, nic, 14 + ip_len); + + /* Ethernet header: */ + memcpy(lp->data + 0, net->tcp_connections[con_id].ethernet_address, 6); +@@ -376,7 +376,7 @@ void net_ip_tcp_connectionreply(struct n + * http://www.networksorcery.com/enp/protocol/tcp.htm + * http://www.tcpipguide.com/free/t_TCPIPTransmissionControlProtocolTCP.htm + */ +-static void net_ip_tcp(struct net *net, void *extra, ++static void net_ip_tcp(struct net *net, struct nic_data *nic, + unsigned char *packet, int len) + { + int con_id, free_con_id, i, res; +@@ -585,7 +585,7 @@ static void net_ip_tcp(struct net *net, + + if (rst) { + debug("[ 'rst': disconnecting TCP connection %i ]\n", con_id); +- net_ip_tcp_connectionreply(net, extra, con_id, 0, NULL, 0, 1); ++ net_ip_tcp_connectionreply(net, nic, con_id, 0, NULL, 0, 1); + tcp_closeconnection(net, con_id); + return; + } +@@ -596,7 +596,7 @@ static void net_ip_tcp(struct net *net, + "connection %i ]\n", con_id); + + /* Send an RST? (TODO, this is wrong...) */ +- net_ip_tcp_connectionreply(net, extra, con_id, 0, NULL, 0, 1); ++ net_ip_tcp_connectionreply(net, nic, con_id, 0, NULL, 0, 1); + + /* ... and forget about this connection: */ + tcp_closeconnection(net, con_id); +@@ -610,7 +610,7 @@ static void net_ip_tcp(struct net *net, + + /* Send an ACK: */ + net->tcp_connections[con_id].state = TCP_OUTSIDE_CONNECTED; +- net_ip_tcp_connectionreply(net, extra, con_id, 0, NULL, 0, 0); ++ net_ip_tcp_connectionreply(net, nic, con_id, 0, NULL, 0, 0); + net->tcp_connections[con_id].state = TCP_OUTSIDE_DISCONNECTED2; + return; + } +@@ -620,7 +620,7 @@ static void net_ip_tcp(struct net *net, + con_id); + + /* Send ACK: */ +- net_ip_tcp_connectionreply(net, extra, con_id, 0, NULL, 0, 0); ++ net_ip_tcp_connectionreply(net, nic, con_id, 0, NULL, 0, 0); + net->tcp_connections[con_id].state = TCP_OUTSIDE_DISCONNECTED2; + + /* Return and send FIN: */ +@@ -725,7 +725,7 @@ debug(" all acked\n"); + + ret: + /* Send an ACK (or FIN) to the guest OS: */ +- net_ip_tcp_connectionreply(net, extra, con_id, 0, NULL, 0, 0); ++ net_ip_tcp_connectionreply(net, nic, con_id, 0, NULL, 0, 0); + } + + +@@ -746,7 +746,7 @@ ret: + * srcport=fffc dstport=0035 length=0028 chksum=76b6 + * 43e20100000100000000000003667470066e6574627364036f726700001c0001 + */ +-static void net_ip_udp(struct net *net, void *extra, ++static void net_ip_udp(struct net *net, struct nic_data *nic, + unsigned char *packet, int len) + { + int con_id, free_con_id, i, srcport, dstport, udp_len; +@@ -882,7 +882,8 @@ static void net_ip_udp(struct net *net, + * + * Handle an IP packet, coming from the emulated NIC. + */ +-void net_ip(struct net *net, void *extra, unsigned char *packet, int len) ++void net_ip(struct net *net, struct nic_data *nic, unsigned char *packet, ++ int len) + { + #if 1 + int i; +@@ -913,13 +914,13 @@ void net_ip(struct net *net, void *extra + /* IPv4: */ + switch (packet[23]) { + case 1: /* ICMP */ +- net_ip_icmp(net, extra, packet, len); ++ net_ip_icmp(net, nic, packet, len); + break; + case 6: /* TCP */ +- net_ip_tcp(net, extra, packet, len); ++ net_ip_tcp(net, nic, packet, len); + break; + case 17:/* UDP */ +- net_ip_udp(net, extra, packet, len); ++ net_ip_udp(net, nic, packet, len); + break; + default: + fatal("[ net: IP: UNIMPLEMENTED protocol %i ]\n", +@@ -939,7 +940,7 @@ void net_ip(struct net *net, void *extra + * Read http://tools.ietf.org/html/rfc2131 for details on DHCP. + * (And http://users.telenet.be/mydotcom/library/network/dhcp.htm.) + */ +-static void net_ip_broadcast_dhcp(struct net *net, void *extra, ++static void net_ip_broadcast_dhcp(struct net *net, struct nic_data *nic, + unsigned char *packet, int len) + { + /* +@@ -1008,7 +1009,7 @@ static void net_ip_broadcast_dhcp(struct + fatal(" ]\n"); + + reply_len = 307; +- lp = net_allocate_ethernet_packet_link(net, extra, reply_len); ++ lp = net_allocate_ethernet_packet_link(net, nic, reply_len); + + /* From old packet, copy everything before options field: */ + memcpy(lp->data, packet, 278); +@@ -1130,7 +1131,7 @@ packet = lp->data; + * Handle an IP broadcast packet, coming from the emulated NIC. + * (This is usually a DHCP request, or similar.) + */ +-void net_ip_broadcast(struct net *net, void *extra, ++void net_ip_broadcast(struct net *net, struct nic_data *nic, + unsigned char *packet, int len) + { + unsigned char *p = (unsigned char *) &net->netmask_ipv4; +@@ -1193,7 +1194,7 @@ void net_ip_broadcast(struct net *net, v + packet[23] == 0x11 && /* UDP */ + packet[34] == 0 && packet[35] == 68 && /* DHCP client */ + packet[36] == 0 && packet[37] == 67) { /* DHCP server */ +- net_ip_broadcast_dhcp(net, extra, packet, len); ++ net_ip_broadcast_dhcp(net, nic, packet, len); + return; + } + +@@ -1222,7 +1223,7 @@ void net_ip_broadcast(struct net *net, v + * + * Receive any available UDP packets (from the outside world). + */ +-void net_udp_rx_avail(struct net *net, void *extra) ++void net_udp_rx_avail(struct net *net, struct nic_data *nic) + { + int received_packets_this_tick = 0; + int max_packets_this_tick = 200; +@@ -1326,7 +1327,7 @@ void net_udp_rx_avail(struct net *net, v + + ip_len = 20 + this_packets_data_length; + +- lp = net_allocate_ethernet_packet_link(net, extra, ++ lp = net_allocate_ethernet_packet_link(net, nic, + 14 + 20 + this_packets_data_length); + + /* Ethernet header: */ +@@ -1381,7 +1382,7 @@ void net_udp_rx_avail(struct net *net, v + * + * Receive any available TCP packets (from the outside world). + */ +-void net_tcp_rx_avail(struct net *net, void *extra) ++void net_tcp_rx_avail(struct net *net, struct nic_data *nic) + { + int received_packets_this_tick = 0; + int max_packets_this_tick = 200; +@@ -1445,7 +1446,7 @@ void net_tcp_rx_avail(struct net *net, v + net->tcp_connections[con_id].state = + TCP_OUTSIDE_CONNECTED; + debug("CHANGING TO TCP_OUTSIDE_CONNECTED\n"); +- net_ip_tcp_connectionreply(net, extra, con_id, 1, ++ net_ip_tcp_connectionreply(net, nic, con_id, 1, + NULL, 0, 0); + } + +@@ -1477,7 +1478,7 @@ void net_tcp_rx_avail(struct net *net, v + net->tcp_connections[con_id]. + incoming_buf_seqnr; + +- net_ip_tcp_connectionreply(net, extra, con_id, ++ net_ip_tcp_connectionreply(net, nic, con_id, + 0, net->tcp_connections[con_id]. + incoming_buf, + net->tcp_connections[con_id]. +@@ -1519,21 +1520,21 @@ void net_tcp_rx_avail(struct net *net, v + memcpy(net->tcp_connections[con_id].incoming_buf, + buf, res); + +- net_ip_tcp_connectionreply(net, extra, con_id, 0, ++ net_ip_tcp_connectionreply(net, nic, con_id, 0, + buf, res, 0); + } else if (res == 0) { + net->tcp_connections[con_id].state = + TCP_OUTSIDE_DISCONNECTED; + debug("CHANGING TO TCP_OUTSIDE_DISCONNECTED, read" + " res=0\n"); +- net_ip_tcp_connectionreply(net, extra, con_id, 0, ++ net_ip_tcp_connectionreply(net, nic, con_id, 0, + NULL, 0, 0); + } else { + net->tcp_connections[con_id].state = + TCP_OUTSIDE_DISCONNECTED; + fatal("CHANGING TO TCP_OUTSIDE_DISCONNECTED, " + "read res<=0, errno = %i\n", errno); +- net_ip_tcp_connectionreply(net, extra, con_id, 0, ++ net_ip_tcp_connectionreply(net, nic, con_id, 0, + NULL, 0, 0); + } + diff --git a/emulators/gxemul/patches/patch-src_net_net_tap.cc b/emulators/gxemul/patches/patch-src_net_net_tap.cc new file mode 100644 index 00000000000..cf7d9e061e5 --- /dev/null +++ b/emulators/gxemul/patches/patch-src_net_net_tap.cc @@ -0,0 +1,192 @@ +$NetBSD: patch-src_net_net_tap.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $ + +Add support for tap(4)-based networking. + +--- /dev/null 2020-10-05 22:44:11.028207457 +0000 ++++ src/net/net_tap.cc 2020-10-05 23:03:15.768748362 +0000 +@@ -0,0 +1,185 @@ ++/* ++ * Copyright (C) 2020 Jason R. Thorpe. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The name of the author may not be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ */ ++ ++/* ++ * Support for Ethernet tap interfaces. ++ * ++ * A single tap instance is used for the entire simulated network. ++ * We treat this as sort of virtual Ethernet switch, with the tap ++ * being the upstream port. This is very simple, conceptually, and ++ * fits in nicely with the rest of the network simulation model in ++ * GXemul. ++ * ++ * Use of the tap interface is completely optional, but if it is used ++ * the all of the virtual IP network support is bypassed completely. ++ */ ++ ++#include <sys/types.h> ++#include <sys/ioctl.h> ++#include <fcntl.h> ++#include <string.h> ++#include <unistd.h> ++ ++#include "misc.h" ++#include "net.h" ++ ++/* ++ * net_tap_rx_for_nic(): ++ * ++ * Receive a packet from the virtual Ethernet switch for this NIC. ++ */ ++static void net_tap_rx_for_nic(struct net *net, struct nic_data *nic, ++ unsigned char *buf, ssize_t size) ++{ ++ struct ethernet_packet_link *lp; ++ ++ /* ++ * We should deliver to the interface if: ++ * ++ * ==> The interface is in promiscuous mode. ++ * -- or -- ++ * ==> The packet is broadcast or multicast (the emulated device ++ * can further apply a multicast filter if it wishes). ++ * -- or -- ++ * ==> The destination MAC address matches the NIC MAC address. ++ * ++ * Note that normally a switch would not know if an interface ++ * is in promiscuous mode, but this is a bit of extra magic ++ * we implement because we can for the sake of convenience. ++ * Also, some emulated interfaces may want to see all packets ++ * so as to implement their own filtering logic. ++ * ++ * Also note that testing for multicast also catches the broadcast ++ * case. ++ */ ++ ++ if (nic->promiscuous_mode || ++ net_ether_multicast(buf) || net_ether_eq(nic->mac_address, buf)) { ++ lp = net_allocate_ethernet_packet_link(net, nic, (int)size); ++ memcpy(lp->data, buf, size); ++ } ++} ++ ++/* ++ * net_tap_rx_avail(): ++ * ++ * We poll the net-shared tap device and link up any available packets to ++ * their destination interfaces, acting like a virtual Ethernet switch. ++ */ ++void net_tap_rx_avail(struct net *net) ++{ ++ int received_packets_this_tick = 0; ++ int max_packets_this_tick = 200; ++ ++ for (;;) { ++ unsigned char buf[1518]; ++ ssize_t bytes_read; ++ int i; ++ ++ if (received_packets_this_tick > max_packets_this_tick) ++ break; ++ ++ /* Read one packet from the tap device. */ ++ bytes_read = read(net->tap_fd, buf, sizeof(buf)); ++ ++ if (bytes_read < 0) { ++ /* No more packets available on the tap. */ ++ break; ++ } ++ ++ /* ++ * Drop runt packets now; allow other layers to assume ++ * valid Ethernet frames. This really should be 64, but ++ * 20 is used in the transmit path. ++ */ ++ if (bytes_read < 20) ++ continue; ++ ++ for (i = 0; i < net->n_nics; i++) { ++ net_tap_rx_for_nic(net, net->nic_data[i], ++ buf, bytes_read); ++ } ++ } ++} ++ ++/* ++ * net_tap_tx(): ++ * ++ * Transmit an ethernet packet, as seen from the emulated ethernet controller, ++ * to the net-shared tap device. Even if the packet is destined only for ++ * a NIC on the local virtual Ethernet switch, we always send it to the ++ * tap device so that the host system can monitor traffic by running tcpdump ++ * on its view of the tap. ++ */ ++void net_tap_tx(struct net *net, struct nic_data *nic, ++ unsigned char *packet, int len) ++{ ++ int i; ++ ++ for (i = 0; i < net->n_nics; i++) { ++ if (nic == net->nic_data[i]) ++ continue; ++ net_tap_rx_for_nic(net, net->nic_data[i], packet, len); ++ } ++ ++ /* ++ * Don't bother checking for errors here. The tap driver in the ++ * kernel will either take the entire packet or none of it, and ++ * there isn't any useful error recovery for us anyway. ++ */ ++ write(net->tap_fd, packet, len); ++} ++ ++/* ++ * net_tap_init(): ++ * ++ * Initialize the tap interface. Returns 1 if successful, 0 otherwise. ++ */ ++int net_tap_init(struct net *net, const char *tapdev) ++{ ++ int fd; ++ int one = 1; ++ ++ fd = open(tapdev, O_RDWR); ++ if (fd < 0) { ++ fatal("[ net: unable to open tap device '%s': %s ]\n", ++ tapdev, strerror(errno)); ++ return 0; ++ } ++ ++ if (ioctl(fd, FIONBIO, &one) < 0) { ++ fatal("[ net: unable to set non-blocking mode on " ++ "tap device '%s': %s ]\n", tapdev, strerror(errno)); ++ close(fd); ++ return 0; ++ } ++ ++ net->tapdev = strdup(tapdev); ++ net->tap_fd = fd; ++ ++ return 1; ++} diff --git a/emulators/gxemul/patches/patch-src_old_main_emul.cc b/emulators/gxemul/patches/patch-src_old_main_emul.cc new file mode 100644 index 00000000000..d096fb655a2 --- /dev/null +++ b/emulators/gxemul/patches/patch-src_old_main_emul.cc @@ -0,0 +1,14 @@ +$NetBSD: patch-src_old_main_emul.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $ + +Add support for tap(4)-based networking. + +--- src/old_main/emul.cc.orig 2020-10-05 23:04:14.559513959 +0000 ++++ src/old_main/emul.cc 2020-10-05 23:04:26.418738637 +0000 +@@ -748,6 +748,7 @@ void emul_simple_init(struct emul *emul) + + /* Create a simple network: */ + emul->net = net_init(emul, NET_INIT_FLAG_GATEWAY, ++ NULL, + NET_DEFAULT_IPV4_MASK, + NET_DEFAULT_IPV4_LEN, + NULL, 0, 0, NULL); diff --git a/emulators/gxemul/patches/patch-src_old_main_emul_parse.cc b/emulators/gxemul/patches/patch-src_old_main_emul_parse.cc new file mode 100644 index 00000000000..e4240e1dd14 --- /dev/null +++ b/emulators/gxemul/patches/patch-src_old_main_emul_parse.cc @@ -0,0 +1,38 @@ +$NetBSD: patch-src_old_main_emul_parse.cc,v 1.1 2020/10/07 00:43:05 thorpej Exp $ + +Add support for tap(4)-based networking. + +--- src/old_main/emul_parse.cc.orig 2020-10-05 23:04:38.529354586 +0000 ++++ src/old_main/emul_parse.cc 2020-10-05 23:04:50.653298084 +0000 +@@ -197,6 +197,7 @@ static void read_one_word(FILE *f, char + #define PARSESTATE_NET 2 + #define PARSESTATE_MACHINE 3 + ++static char cur_net_tapdev[50]; + static char cur_net_ipv4net[50]; + static char cur_net_ipv4len[50]; + static char cur_net_local_port[10]; +@@ -315,6 +316,7 @@ static void parse__emul(struct emul *e, + line, EXPECT_LEFT_PARENTHESIS); + + /* Default net: */ ++ strlcpy(cur_net_tapdev, "", sizeof(cur_net_tapdev)); + strlcpy(cur_net_ipv4net, NET_DEFAULT_IPV4_MASK, + sizeof(cur_net_ipv4net)); + snprintf(cur_net_ipv4len, sizeof(cur_net_ipv4len), "%i", +@@ -391,6 +393,7 @@ static void parse__net(struct emul *e, F + sizeof(cur_net_local_port)); + + e->net = net_init(e, NET_INIT_FLAG_GATEWAY, ++ cur_net_tapdev[0] ? cur_net_tapdev : NULL, + cur_net_ipv4net, atoi(cur_net_ipv4len), + cur_net_remote, cur_net_n_remote, + atoi(cur_net_local_port), NULL); +@@ -410,6 +413,7 @@ static void parse__net(struct emul *e, F + return; + } + ++ WORD("tapdev", cur_net_tapdev); + WORD("ipv4net", cur_net_ipv4net); + WORD("ipv4len", cur_net_ipv4len); + WORD("local_port", cur_net_local_port); |