diff --git a/docs/networking.md b/docs/networking.md index d9ec124d..08917348 100644 --- a/docs/networking.md +++ b/docs/networking.md @@ -62,6 +62,7 @@ Backends `fetch` and `wisp` support a couple of special settings in `config.net_ | **dns_method** | str | DNS method to use, either `static` or `doh`. `static`: use built-in DNS server, `doh`: use [DNS-over-HTTPS](https://en.wikipedia.org/wiki/DNS_over_HTTPS) (DoH). Defaults to `static` for `fetch` and to `doh` for `wisp` backend. | | **doh_server** | str | Host name or IP address (and optional port number) of the DoH server if `dns_method` is `doh`. The value is expanded to the URL `https://DOH_SERVER/dns-query`. Default: `cloudflare-dns.com`. | | **cors_proxy** | str | CORS proxy server URL, do not use a proxy if undefined. Default: undefined (`fetch` backend only). | +| **mtu** | int | The MTU used for the virtual network. Increasing it can improve performance. This only works if the NIC type is `virtio`. Default: `1500` | #### Example `net_device` settings diff --git a/src/browser/fake_network.js b/src/browser/fake_network.js index e358f921..63923dec 100644 --- a/src/browser/fake_network.js +++ b/src/browser/fake_network.js @@ -45,18 +45,16 @@ const TCP_DYNAMIC_PORT_RANGE = TCP_DYNAMIC_PORT_END - TCP_DYNAMIC_PORT_START; const ETH_HEADER_SIZE = 14; const ETH_PAYLOAD_OFFSET = ETH_HEADER_SIZE; -const ETH_PAYLOAD_SIZE = 1500; +const MTU_DEFAULT = 1500; +//const ETH_PAYLOAD_SIZE = 1500; //mtu const ETH_TRAILER_SIZE = 4; -const ETH_FRAME_SIZE = ETH_HEADER_SIZE + ETH_PAYLOAD_SIZE + ETH_TRAILER_SIZE; const IPV4_HEADER_SIZE = 20; const IPV4_PAYLOAD_OFFSET = ETH_PAYLOAD_OFFSET + IPV4_HEADER_SIZE; -const IPV4_PAYLOAD_SIZE = ETH_PAYLOAD_SIZE - IPV4_HEADER_SIZE; +//const IPV4_PAYLOAD_SIZE = ETH_PAYLOAD_SIZE - IPV4_HEADER_SIZE; const UDP_HEADER_SIZE = 8; const UDP_PAYLOAD_OFFSET = IPV4_PAYLOAD_OFFSET + UDP_HEADER_SIZE; -const UDP_PAYLOAD_SIZE = IPV4_PAYLOAD_SIZE - UDP_HEADER_SIZE; const TCP_HEADER_SIZE = 20; const TCP_PAYLOAD_OFFSET = IPV4_PAYLOAD_OFFSET + TCP_HEADER_SIZE; -const TCP_PAYLOAD_SIZE = IPV4_PAYLOAD_SIZE - TCP_HEADER_SIZE; const ICMP_HEADER_SIZE = 4; const DEFAULT_DOH_SERVER = "cloudflare-dns.com"; @@ -161,15 +159,19 @@ class GrowableRingbuffer } } -export function create_eth_encoder_buf() +export function create_eth_encoder_buf(mtu = MTU_DEFAULT) { + const ETH_FRAME_SIZE = ETH_HEADER_SIZE + mtu + ETH_TRAILER_SIZE; + const IPV4_PAYLOAD_SIZE = mtu - IPV4_HEADER_SIZE; + const UDP_PAYLOAD_SIZE = IPV4_PAYLOAD_SIZE - UDP_HEADER_SIZE; + const eth_frame = new Uint8Array(ETH_FRAME_SIZE); const buffer = eth_frame.buffer; const offset = eth_frame.byteOffset; return { eth_frame: eth_frame, eth_frame_view: new DataView(buffer), - eth_payload_view: new DataView(buffer, offset + ETH_PAYLOAD_OFFSET, ETH_PAYLOAD_SIZE), + eth_payload_view: new DataView(buffer, offset + ETH_PAYLOAD_OFFSET, mtu), ipv4_payload_view: new DataView(buffer, offset + IPV4_PAYLOAD_OFFSET, IPV4_PAYLOAD_SIZE), udp_payload_view: new DataView(buffer, offset + UDP_PAYLOAD_OFFSET, UDP_PAYLOAD_SIZE), text_encoder: new TextEncoder() @@ -991,7 +993,7 @@ export function fake_tcp_connect(dport, adapter) throw new Error("pool of dynamic TCP port numbers exhausted, connection aborted"); } - let conn = new TCPConnection(); + let conn = new TCPConnection(adapter); conn.tuple = tuple; conn.hsrc = adapter.router_mac; @@ -1000,7 +1002,6 @@ export function fake_tcp_connect(dport, adapter) conn.hdest = adapter.vm_mac; conn.dport = dport; conn.pdest = adapter.vm_ip; - conn.net = adapter; adapter.tcp_conn[tuple] = conn; conn.connect(); return conn; @@ -1017,10 +1018,13 @@ export function fake_tcp_probe(dport, adapter) { /** * @constructor */ -export function TCPConnection() +export function TCPConnection(adapter) { + const IPV4_PAYLOAD_SIZE = (adapter.mtu || MTU_DEFAULT) - IPV4_HEADER_SIZE; + const TCP_PAYLOAD_SIZE = IPV4_PAYLOAD_SIZE - TCP_HEADER_SIZE; + this.state = TCP_STATE_CLOSED; - this.net = null; // The adapter is stored here + this.net = adapter; // The adapter is stored here this.send_buffer = new GrowableRingbuffer(2048, 0); this.send_chunk_buf = new Uint8Array(TCP_PAYLOAD_SIZE); this.in_active_close = false; diff --git a/src/browser/fetch_network.js b/src/browser/fetch_network.js index 5d0195a9..74f763f6 100644 --- a/src/browser/fetch_network.js +++ b/src/browser/fetch_network.js @@ -33,7 +33,8 @@ export function FetchNetworkAdapter(bus, config) this.dns_method = config.dns_method || "static"; this.doh_server = config.doh_server; this.tcp_conn = {}; - this.eth_encoder_buf = create_eth_encoder_buf(); + this.mtu = config.mtu; + this.eth_encoder_buf = create_eth_encoder_buf(this.mtu); this.fetch = (...args) => fetch(...args); // Ex: 'https://corsproxy.io/?' @@ -55,9 +56,8 @@ FetchNetworkAdapter.prototype.destroy = function() FetchNetworkAdapter.prototype.on_tcp_connection = function(packet, tuple) { if(packet.tcp.dport === 80) { - let conn = new TCPConnection(); + let conn = new TCPConnection(this); conn.state = TCP_STATE_SYN_RECEIVED; - conn.net = this; conn.on("data", on_data_http); conn.tuple = tuple; conn.accept(packet); diff --git a/src/browser/main.js b/src/browser/main.js index 4c2db9aa..cacc704f 100644 --- a/src/browser/main.js +++ b/src/browser/main.js @@ -2096,6 +2096,7 @@ function start_emulation(profile, query_args) settings.relay_url = query_args.get("relay_url"); settings.disable_jit = bool_arg(query_args.get("disable_jit")); settings.disable_audio = bool_arg(query_args.get("mute")); + settings.mtu = parseInt(query_args.get("mtu"), 10) || undefined; } if(!settings.relay_url) @@ -2255,7 +2256,8 @@ function start_emulation(profile, query_args) net_device: { type: settings.net_device_type || "ne2k", relay_url: settings.relay_url, - cors_proxy: settings.cors_proxy + cors_proxy: settings.cors_proxy, + mtu: settings.mtu }, autostart: true, diff --git a/src/browser/wisp_network.js b/src/browser/wisp_network.js index e0de942f..c3063218 100644 --- a/src/browser/wisp_network.js +++ b/src/browser/wisp_network.js @@ -36,8 +36,9 @@ export function WispNetworkAdapter(wisp_url, bus, config) this.dns_method = config.dns_method || "doh"; this.doh_server = config.doh_server; this.tcp_conn = {}; - this.eth_encoder_buf = create_eth_encoder_buf(); - + this.mtu = config.mtu; + this.eth_encoder_buf = create_eth_encoder_buf(this.mtu); + this.bus.register("net" + this.id + "-mac", function(mac) { this.vm_mac = new Uint8Array(mac.split(":").map(function(x) { return parseInt(x, 16); })); }, this); @@ -190,9 +191,8 @@ WispNetworkAdapter.prototype.destroy = function() */ WispNetworkAdapter.prototype.on_tcp_connection = function(packet, tuple) { - let conn = new TCPConnection(); + let conn = new TCPConnection(this); conn.state = TCP_STATE_SYN_RECEIVED; - conn.net = this; conn.tuple = tuple; conn.stream_id = this.last_stream++; this.tcp_conn[tuple] = conn; diff --git a/src/cpu.js b/src/cpu.js index 67659731..186e571a 100644 --- a/src/cpu.js +++ b/src/cpu.js @@ -1210,7 +1210,7 @@ CPU.prototype.init = function(settings, device_bus) } else if(settings.net_device.type === "virtio") { - this.devices.virtio_net = new VirtioNet(this, device_bus, settings.preserve_mac_from_state_image); + this.devices.virtio_net = new VirtioNet(this, device_bus, settings.preserve_mac_from_state_image, settings.net_device.mtu); } if(settings.fs9p) diff --git a/src/virtio_net.js b/src/virtio_net.js index ea6e08bd..ee7ad94c 100644 --- a/src/virtio_net.js +++ b/src/virtio_net.js @@ -24,8 +24,9 @@ const VIRTIO_NET_CTRL_MAC_ADDR_SET = 1; * @param {CPU} cpu * @param {BusConnector} bus * @param {Boolean} preserve_mac_from_state_image + * @param {Number} mtu */ -export function VirtioNet(cpu, bus, preserve_mac_from_state_image) +export function VirtioNet(cpu, bus, preserve_mac_from_state_image, mtu = 1500) { /** @const @type {BusConnector} */ this.bus = bus; @@ -177,7 +178,7 @@ export function VirtioNet(cpu, bus, preserve_mac_from_state_image) { bytes: 2, name: "mtu", - read: () => 1500, + read: () => mtu, write: data => {}, } ]) diff --git a/v86.d.ts b/v86.d.ts index 13c6ecbb..29f762ab 100644 --- a/v86.d.ts +++ b/v86.d.ts @@ -333,6 +333,7 @@ export interface V86Options { dns_method?: "static" | "doh"; doh_server?: string; cors_proxy?: string; + mtu?: number; }; }