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

This commit is contained in:
Fabian 2022-07-21 22:33:59 +09:00
parent bca3648fd2
commit f33c7ca70b
4 changed files with 174 additions and 12 deletions

View file

@ -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,
});

View file

@ -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"])
{

View file

@ -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)

View file

@ -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;