pkgsrc-Changes archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

CVS commit: pkgsrc/emulators/gxemul



Module Name:    pkgsrc
Committed By:   thorpej
Date:           Wed Oct  7 00:43:05 UTC 2020

Modified Files:
        pkgsrc/emulators/gxemul: Makefile distinfo
Added Files:
        pkgsrc/emulators/gxemul/patches: patch-doc_networking.html
            patch-src_devices_dev_dec21143.cc patch-src_devices_dev_ether.cc
            patch-src_devices_dev_le.cc patch-src_devices_dev_rtl8139c.cc
            patch-src_devices_dev_sgi_mec.cc patch-src_devices_dev_sn.cc
            patch-src_include_net.h patch-src_net_Makefile.skel
            patch-src_net_net.cc patch-src_net_net_ether.cc
            patch-src_net_net_ip.cc patch-src_net_net_tap.cc
            patch-src_old_main_emul.cc patch-src_old_main_emul_parse.cc

Log Message:
- 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.


To generate a diff of this commit:
cvs rdiff -u -r1.67 -r1.68 pkgsrc/emulators/gxemul/Makefile
cvs rdiff -u -r1.60 -r1.61 pkgsrc/emulators/gxemul/distinfo
cvs rdiff -u -r0 -r1.1 \
    pkgsrc/emulators/gxemul/patches/patch-doc_networking.html \
    pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_dec21143.cc \
    pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_ether.cc \
    pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_le.cc \
    pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_rtl8139c.cc \
    pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_sgi_mec.cc \
    pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_sn.cc \
    pkgsrc/emulators/gxemul/patches/patch-src_include_net.h \
    pkgsrc/emulators/gxemul/patches/patch-src_net_Makefile.skel \
    pkgsrc/emulators/gxemul/patches/patch-src_net_net.cc \
    pkgsrc/emulators/gxemul/patches/patch-src_net_net_ether.cc \
    pkgsrc/emulators/gxemul/patches/patch-src_net_net_ip.cc \
    pkgsrc/emulators/gxemul/patches/patch-src_net_net_tap.cc \
    pkgsrc/emulators/gxemul/patches/patch-src_old_main_emul.cc \
    pkgsrc/emulators/gxemul/patches/patch-src_old_main_emul_parse.cc

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: pkgsrc/emulators/gxemul/Makefile
diff -u pkgsrc/emulators/gxemul/Makefile:1.67 pkgsrc/emulators/gxemul/Makefile:1.68
--- pkgsrc/emulators/gxemul/Makefile:1.67       Fri Aug 23 13:13:49 2019
+++ pkgsrc/emulators/gxemul/Makefile    Wed Oct  7 00:43:04 2020
@@ -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/}
 

Index: pkgsrc/emulators/gxemul/distinfo
diff -u pkgsrc/emulators/gxemul/distinfo:1.60 pkgsrc/emulators/gxemul/distinfo:1.61
--- pkgsrc/emulators/gxemul/distinfo:1.60       Fri Aug 23 13:13:49 2019
+++ pkgsrc/emulators/gxemul/distinfo    Wed Oct  7 00:43:04 2020
@@ -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

Added files:

Index: pkgsrc/emulators/gxemul/patches/patch-doc_networking.html
diff -u /dev/null pkgsrc/emulators/gxemul/patches/patch-doc_networking.html:1.1
--- /dev/null   Wed Oct  7 00:43:05 2020
+++ pkgsrc/emulators/gxemul/patches/patch-doc_networking.html   Wed Oct  7 00:43:05 2020
@@ -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>
Index: pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_dec21143.cc
diff -u /dev/null pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_dec21143.cc:1.1
--- /dev/null   Wed Oct  7 00:43:05 2020
+++ pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_dec21143.cc   Wed Oct  7 00:43:05 2020
@@ -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);
Index: pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_ether.cc
diff -u /dev/null pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_ether.cc:1.1
--- /dev/null   Wed Oct  7 00:43:05 2020
+++ pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_ether.cc      Wed Oct  7 00:43:05 2020
@@ -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);
Index: pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_le.cc
diff -u /dev/null pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_le.cc:1.1
--- /dev/null   Wed Oct  7 00:43:05 2020
+++ pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_le.cc Wed Oct  7 00:43:05 2020
@@ -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);
+ }
+ 
Index: pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_rtl8139c.cc
diff -u /dev/null pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_rtl8139c.cc:1.1
--- /dev/null   Wed Oct  7 00:43:05 2020
+++ pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_rtl8139c.cc   Wed Oct  7 00:43:05 2020
@@ -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;
+ }
Index: pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_sgi_mec.cc
diff -u /dev/null pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_sgi_mec.cc:1.1
--- /dev/null   Wed Oct  7 00:43:05 2020
+++ pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_sgi_mec.cc    Wed Oct  7 00:43:05 2020
@@ -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);
+ }
+ 
+ 
Index: pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_sn.cc
diff -u /dev/null pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_sn.cc:1.1
--- /dev/null   Wed Oct  7 00:43:05 2020
+++ pkgsrc/emulators/gxemul/patches/patch-src_devices_dev_sn.cc Wed Oct  7 00:43:05 2020
@@ -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;
+ }
Index: pkgsrc/emulators/gxemul/patches/patch-src_include_net.h
diff -u /dev/null pkgsrc/emulators/gxemul/patches/patch-src_include_net.h:1.1
--- /dev/null   Wed Oct  7 00:43:05 2020
+++ pkgsrc/emulators/gxemul/patches/patch-src_include_net.h     Wed Oct  7 00:43:05 2020
@@ -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;
+ };
Index: pkgsrc/emulators/gxemul/patches/patch-src_net_Makefile.skel
diff -u /dev/null pkgsrc/emulators/gxemul/patches/patch-src_net_Makefile.skel:1.1
--- /dev/null   Wed Oct  7 00:43:05 2020
+++ pkgsrc/emulators/gxemul/patches/patch-src_net_Makefile.skel Wed Oct  7 00:43:05 2020
@@ -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)
+ 
Index: pkgsrc/emulators/gxemul/patches/patch-src_net_net.cc
diff -u /dev/null pkgsrc/emulators/gxemul/patches/patch-src_net_net.cc:1.1
--- /dev/null   Wed Oct  7 00:43:05 2020
+++ pkgsrc/emulators/gxemul/patches/patch-src_net_net.cc        Wed Oct  7 00:43:05 2020
@@ -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);
Index: pkgsrc/emulators/gxemul/patches/patch-src_net_net_ether.cc
diff -u /dev/null pkgsrc/emulators/gxemul/patches/patch-src_net_net_ether.cc:1.1
--- /dev/null   Wed Oct  7 00:43:05 2020
+++ pkgsrc/emulators/gxemul/patches/patch-src_net_net_ether.cc  Wed Oct  7 00:43:05 2020
@@ -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;
++}
Index: pkgsrc/emulators/gxemul/patches/patch-src_net_net_ip.cc
diff -u /dev/null pkgsrc/emulators/gxemul/patches/patch-src_net_net_ip.cc:1.1
--- /dev/null   Wed Oct  7 00:43:05 2020
+++ pkgsrc/emulators/gxemul/patches/patch-src_net_net_ip.cc     Wed Oct  7 00:43:05 2020
@@ -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);
+               }
+ 
Index: pkgsrc/emulators/gxemul/patches/patch-src_net_net_tap.cc
diff -u /dev/null pkgsrc/emulators/gxemul/patches/patch-src_net_net_tap.cc:1.1
--- /dev/null   Wed Oct  7 00:43:05 2020
+++ pkgsrc/emulators/gxemul/patches/patch-src_net_net_tap.cc    Wed Oct  7 00:43:05 2020
@@ -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;
++}
Index: pkgsrc/emulators/gxemul/patches/patch-src_old_main_emul.cc
diff -u /dev/null pkgsrc/emulators/gxemul/patches/patch-src_old_main_emul.cc:1.1
--- /dev/null   Wed Oct  7 00:43:05 2020
+++ pkgsrc/emulators/gxemul/patches/patch-src_old_main_emul.cc  Wed Oct  7 00:43:05 2020
@@ -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);
Index: pkgsrc/emulators/gxemul/patches/patch-src_old_main_emul_parse.cc
diff -u /dev/null pkgsrc/emulators/gxemul/patches/patch-src_old_main_emul_parse.cc:1.1
--- /dev/null   Wed Oct  7 00:43:05 2020
+++ pkgsrc/emulators/gxemul/patches/patch-src_old_main_emul_parse.cc    Wed Oct  7 00:43:05 2020
@@ -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);



Home | Main Index | Thread Index | Old Index