Browse Source

Crude mac address translation, fixes networking in Windows 2000 and SerenityOS

Fabian 1 year ago
parent
commit
f33c7ca70b
4 changed files with 174 additions and 12 deletions
  1. 5 4
      src/browser/main.js
  2. 1 0
      src/browser/starter.js
  3. 1 1
      src/cpu.js
  4. 167 7
      src/ne2k.js

+ 5 - 4
src/browser/main.js

@@ -223,6 +223,7 @@
                 memory_size: 512 * 1024 * 1024,
                 state: { url: host + "serenity_state-v3.bin.zst", },
                 homepage: "https://serenityos.org/",
+                mac_address_translation: true,
             },
             {
                 id: "serenity-boot",
@@ -577,7 +578,7 @@
                 state: {
                     "url": host + "windows98_state.bin.zst",
                 },
-                preserve_mac_from_state_image: true,
+                mac_address_translation: true,
             },
             {
                 id: "windows98-boot",
@@ -683,7 +684,7 @@
                 state: {
                     "url": host + "reactos_state.bin.zst",
                 },
-                preserve_mac_from_state_image: true,
+                mac_address_translation: true,
                 name: "ReactOS",
                 homepage: "https://reactos.org/",
             },
@@ -1002,7 +1003,7 @@
             settings.initrd = infos.initrd;
             settings.cmdline = infos.cmdline;
             settings.bzimage_initrd_from_filesystem = infos.bzimage_initrd_from_filesystem;
-            settings.preserve_mac_from_state_image = infos.preserve_mac_from_state_image;
+            settings.mac_address_translation = infos.mac_address_translation;
 
             settings.acpi = (!infos.state && settings.acpi !== undefined) ? settings.acpi : infos.acpi;
             settings.memory_size = (!infos.state && settings.memory_size) ? settings.memory_size : infos.memory_size;
@@ -1238,7 +1239,7 @@
             "initial_state": settings.initial_state,
             "filesystem": settings.filesystem || {},
             "disable_speaker": disable_audio,
-            "preserve_mac_from_state_image": settings.preserve_mac_from_state_image,
+            "mac_address_translation": settings.mac_address_translation,
 
             "autostart": true,
         });

+ 1 - 0
src/browser/starter.js

@@ -265,6 +265,7 @@ V86Starter.prototype.continue_init = async function(emulator, options)
     settings.uart3 = options["uart3"];
     settings.cmdline = options["cmdline"];
     settings.preserve_mac_from_state_image = options["preserve_mac_from_state_image"];
+    settings.mac_address_translation = options["mac_address_translation"];
 
     if(options["network_adapter"])
     {

+ 1 - 1
src/cpu.js

@@ -874,7 +874,7 @@ CPU.prototype.init = function(settings, device_bus)
 
         if(settings.enable_ne2k)
         {
-            this.devices.net = new Ne2k(this, device_bus, settings.preserve_mac_from_state_image);
+            this.devices.net = new Ne2k(this, device_bus, settings.preserve_mac_from_state_image, settings.mac_address_translation);
         }
 
         if(settings.fs9p)

+ 167 - 7
src/ne2k.js

@@ -56,13 +56,154 @@ const NE2K_LOG_PACKETS = false;
 /** @const */ var STOP_PAGE = 0x80;
 
 
+// Search and replace MAC addresses in ethernet, arp and dhcp packets.
+// Used after restoring an OS from memory dump, so that multiple instances of
+// that OS can run at the same time with different external MAC addresses.
+// Crude but seems to work.
+function translate_mac_address(packet, search_mac, replacement_mac)
+{
+    if(packet[0] === search_mac[0] &&
+       packet[1] === search_mac[1] &&
+       packet[2] === search_mac[2] &&
+       packet[3] === search_mac[3] &&
+       packet[4] === search_mac[4] &&
+       packet[5] === search_mac[5])
+    {
+        dbg_log("Replace mac in eth destination field", LOG_NET);
+
+        packet[0] = replacement_mac[0];
+        packet[1] = replacement_mac[1];
+        packet[2] = replacement_mac[2];
+        packet[3] = replacement_mac[3];
+        packet[4] = replacement_mac[4];
+        packet[5] = replacement_mac[5];
+    }
+
+    if(packet[6 + 0] === search_mac[0] &&
+       packet[6 + 1] === search_mac[1] &&
+       packet[6 + 2] === search_mac[2] &&
+       packet[6 + 3] === search_mac[3] &&
+       packet[6 + 4] === search_mac[4] &&
+       packet[6 + 5] === search_mac[5])
+    {
+        dbg_log("Replace mac in eth source field", LOG_NET);
+
+        packet[6 + 0] = replacement_mac[0];
+        packet[6 + 1] = replacement_mac[1];
+        packet[6 + 2] = replacement_mac[2];
+        packet[6 + 3] = replacement_mac[3];
+        packet[6 + 4] = replacement_mac[4];
+        packet[6 + 5] = replacement_mac[5];
+    }
+
+    const ethertype = packet[12] << 8 | packet[13];
+
+    if(ethertype === 0x0800)
+    {
+        // ipv4
+        const ipv4_packet = packet.subarray(14);
+        const ipv4_version = ipv4_packet[0] >> 4;
+
+        if(ipv4_version !== 4)
+        {
+            dbg_log("Expected ipv4.version==4 but got: " + ipv4_version, LOG_NET);
+            return;
+        }
+
+        const ipv4_ihl = ipv4_packet[0] & 0xF;
+        dbg_assert(ipv4_ihl === 5, "TODO: ihl!=5");
+
+        const ipv4_proto = ipv4_packet[9];
+        if(ipv4_proto === 0x11)
+        {
+            // udp
+            const udp_packet = ipv4_packet.subarray(5 * 4);
+            const source_port = udp_packet[0] << 8 | udp_packet[1];
+            const destination_port = udp_packet[2] << 8 | udp_packet[3];
+            const checksum = udp_packet[6] << 8 | udp_packet[7];
+
+            dbg_log("udp srcport=" + source_port + " dstport=" + destination_port + " checksum=" + h(checksum, 4), LOG_NET);
+
+            if(source_port === 67 || destination_port === 67)
+            {
+                // dhcp
+                const dhcp_packet = udp_packet.subarray(8);
+
+                if(dhcp_packet[28 + 0] === search_mac[0] &&
+                   dhcp_packet[28 + 1] === search_mac[1] &&
+                   dhcp_packet[28 + 2] === search_mac[2] &&
+                   dhcp_packet[28 + 3] === search_mac[3] &&
+                   dhcp_packet[28 + 4] === search_mac[4] &&
+                   dhcp_packet[28 + 5] === search_mac[5])
+                {
+                    dbg_log("Replace mac in dhcp.chaddr", LOG_NET);
+
+                    dhcp_packet[28 + 0] = replacement_mac[0];
+                    dhcp_packet[28 + 1] = replacement_mac[1];
+                    dhcp_packet[28 + 2] = replacement_mac[2];
+                    dhcp_packet[28 + 3] = replacement_mac[3];
+                    dhcp_packet[28 + 4] = replacement_mac[4];
+                    dhcp_packet[28 + 5] = replacement_mac[5];
+
+                    udp_packet[6] = udp_packet[7] = 0; // zero udp checksum
+
+                }
+            }
+        }
+        else
+        {
+            // tcp, ...
+        }
+    }
+    else if(ethertype === 0x0806)
+    {
+        // arp
+        const arp_packet = packet.subarray(14);
+        dbg_log("arp oper=" + arp_packet[7] + " " + format_mac(arp_packet.subarray(8, 8+6)) + " " + format_mac(arp_packet.subarray(18, 18+6)), LOG_NET);
+
+        if(arp_packet[8 + 0] === search_mac[0] &&
+           arp_packet[8 + 1] === search_mac[1] &&
+           arp_packet[8 + 2] === search_mac[2] &&
+           arp_packet[8 + 3] === search_mac[3] &&
+           arp_packet[8 + 4] === search_mac[4] &&
+           arp_packet[8 + 5] === search_mac[5])
+        {
+            dbg_log("Replace mac in arp.sha", LOG_NET);
+
+            arp_packet[8 + 0] = replacement_mac[0];
+            arp_packet[8 + 1] = replacement_mac[1];
+            arp_packet[8 + 2] = replacement_mac[2];
+            arp_packet[8 + 3] = replacement_mac[3];
+            arp_packet[8 + 4] = replacement_mac[4];
+            arp_packet[8 + 5] = replacement_mac[5];
+        }
+    }
+    else
+    {
+        // TODO: ipv6, ...
+    }
+}
+
+function format_mac(mac)
+{
+    return [
+        mac[0].toString(16).padStart(2, "0"),
+        mac[1].toString(16).padStart(2, "0"),
+        mac[2].toString(16).padStart(2, "0"),
+        mac[3].toString(16).padStart(2, "0"),
+        mac[4].toString(16).padStart(2, "0"),
+        mac[5].toString(16).padStart(2, "0"),
+    ].join(":");
+}
+
 /**
  * @constructor
  * @param {CPU} cpu
  * @param {BusConnector} bus
  * @param {Boolean} preserve_mac_from_state_image
+ * @param {Boolean} mac_address_translation
  */
-function Ne2k(cpu, bus, preserve_mac_from_state_image)
+function Ne2k(cpu, bus, preserve_mac_from_state_image, mac_address_translation)
 {
     /** @const @type {CPU} */
     this.cpu = cpu;
@@ -71,6 +212,7 @@ function Ne2k(cpu, bus, preserve_mac_from_state_image)
     this.pci = cpu.devices.pci;
 
     this.preserve_mac_from_state_image = preserve_mac_from_state_image;
+    this.mac_address_translation = mac_address_translation;
 
     /** @const @type {BusConnector} */
     this.bus = bus;
@@ -127,6 +269,10 @@ function Ne2k(cpu, bus, preserve_mac_from_state_image)
         Math.random() * 255 | 0,
     ]);
 
+    // Used for mac address translation
+    // The mac the OS thinks it has
+    this.mac_address_in_state = null;
+
     for(var i = 0; i < 6; i++)
     {
         this.memory[i << 1] = this.memory[i << 1 | 1] = this.mac[i];
@@ -137,12 +283,7 @@ function Ne2k(cpu, bus, preserve_mac_from_state_image)
     this.memory[14 << 1] = this.memory[14 << 1 | 1] = 0x57;
     this.memory[15 << 1] = this.memory[15 << 1 | 1] = 0x57;
 
-    dbg_log("Mac: " + h(this.mac[0], 2) + ":" +
-                      h(this.mac[1], 2) + ":" +
-                      h(this.mac[2], 2) + ":" +
-                      h(this.mac[3], 2) + ":" +
-                      h(this.mac[4], 2) + ":" +
-                      h(this.mac[5], 2), LOG_NET);
+    dbg_log("Mac: " + format_mac(this.mac), LOG_NET);
 
     this.rsar = 0;
 
@@ -186,6 +327,11 @@ function Ne2k(cpu, bus, preserve_mac_from_state_image)
                 dbg_log(hex_dump(data));
             }
 
+            if(this.mac_address_in_state)
+            {
+                translate_mac_address(data, this.mac_address_in_state, this.mac);
+            }
+
             this.bus.send("net0-send", data);
             this.bus.send("eth-transmit-end", [data.length]);
             this.cr &= ~4;
@@ -803,6 +949,15 @@ Ne2k.prototype.set_state = function(state)
         this.mac = state[15];
         this.memory = state[16];
     }
+    else if(this.mac_address_translation)
+    {
+        this.mac_address_in_state = state[15];
+        this.memory = state[16];
+
+        dbg_log("Using mac address translation" +
+            " guest_os_mac=" + format_mac(this.mac_address_in_state) +
+            " real_mac=" + format_mac(this.mac), LOG_NET);
+    }
 };
 
 Ne2k.prototype.do_interrupt = function(ir_mask)
@@ -969,6 +1124,11 @@ Ne2k.prototype.receive = function(data)
         return;
     }
 
+    if(this.mac_address_in_state)
+    {
+        translate_mac_address(data, this.mac, this.mac_address_in_state);
+    }
+
     var packet_length = Math.max(60, data.length);
 
     var offset = this.curpg << 8;