From 9aeeebe04180377adbdc90a7f9aa7a36d219d0dd Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Thu, 21 May 2026 23:42:30 +0000 Subject: [PATCH 1/3] Missed some MAC options --- vm/devices/net/netvsp/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vm/devices/net/netvsp/src/lib.rs b/vm/devices/net/netvsp/src/lib.rs index d6fc9e62a2..e51cc0aba1 100644 --- a/vm/devices/net/netvsp/src/lib.rs +++ b/vm/devices/net/netvsp/src/lib.rs @@ -3486,7 +3486,9 @@ impl Adapter { rndisprot::Oid::OID_GEN_MAC_OPTIONS => { let options: u32 = rndisprot::MAC_OPTION_COPY_LOOKAHEAD_DATA | rndisprot::MAC_OPTION_TRANSFERS_NOT_PEND - | rndisprot::MAC_OPTION_NO_LOOPBACK; + | rndisprot::MAC_OPTION_NO_LOOPBACK + | rndisprot::MAC_OPTION_8021P_PRIORITY + | rndisprot::MAC_OPTION_8021Q_VLAN; writer.write(options.as_bytes())?; } rndisprot::Oid::OID_GEN_MEDIA_CONNECT_STATUS => { From c1fb40ce090c6ddc68acc20fb6ecbcb7952d90a9 Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Tue, 26 May 2026 23:24:37 +0000 Subject: [PATCH 2/3] Further test fix, probably got the offsets wrong. --- vm/devices/net/netvsp/src/lib.rs | 9 ++++---- vm/devices/net/netvsp/src/test.rs | 37 ++++++++++++++++--------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/vm/devices/net/netvsp/src/lib.rs b/vm/devices/net/netvsp/src/lib.rs index e51cc0aba1..59df3d02de 100644 --- a/vm/devices/net/netvsp/src/lib.rs +++ b/vm/devices/net/netvsp/src/lib.rs @@ -2610,11 +2610,10 @@ impl NetChannel { ppi = rest; } - metadata.l2_len = if metadata.vlan.is_some() { - net_backend::ETHERNET_VLAN_HEADER_LEN - } else { - net_backend::ETHERNET_HEADER_LEN - } as u8; + // The frame data always has a 14-byte Ethernet header; when + // VLAN is present it arrives out-of-band in the PPI (not inline + // in the frame), so l2_len is unconditionally 14. + metadata.l2_len = net_backend::ETHERNET_HEADER_LEN as u8; if metadata.flags.offload_tcp_checksum() || metadata.flags.offload_udp_checksum() { // The offset must be set if we're handling checksums; we already know from the above logic diff --git a/vm/devices/net/netvsp/src/test.rs b/vm/devices/net/netvsp/src/test.rs index ad0f1a2902..a2d7452a36 100644 --- a/vm/devices/net/netvsp/src/test.rs +++ b/vm/devices/net/netvsp/src/test.rs @@ -1619,7 +1619,7 @@ impl<'a> TestNicChannel<'a> { buf_writer.write(packet.as_bytes()).unwrap(); - const VLAN_TCP_HEADER_OFFSET: u16 = 38; // Ethernet (18) + IPv4 (20) + const VLAN_TCP_HEADER_OFFSET: u16 = 34; // Ethernet (14) + IPv4 (20); tag is in PPI only if tcp_checksum || udp_checksum { let checksum_info = rndisprot::TxTcpIpChecksumInfo::new_zeroed() .set_is_ipv4(true) @@ -6225,21 +6225,22 @@ async fn rndis_send_tcp_checksum_packet_zero_transport_header_offset_ipv6(driver ); } -fn build_vlan_ipv4_tcp_packet(vlan_id: u16) -> Vec { +fn build_vlan_ipv4_tcp_packet(_vlan_id: u16) -> Vec { + // Real guests (Windows/Linux netvsc) send the VLAN info ONLY in the + // PPI — the frame data has a standard 14-byte Ethernet header without + // the 802.1Q tag inline. let mut data = vec![0u8; 60]; - data[..6].copy_from_slice(&[0x10, 0x11, 0x12, 0x13, 0x14, 0x15]); - data[6..12].copy_from_slice(&[0x20, 0x21, 0x22, 0x23, 0x24, 0x25]); - data[12..14].copy_from_slice(&0x8100u16.to_be_bytes()); - data[14..16].copy_from_slice(&(vlan_id & 0x0fff).to_be_bytes()); - data[16..18].copy_from_slice(&0x0800u16.to_be_bytes()); + data[..6].copy_from_slice(&[0x10, 0x11, 0x12, 0x13, 0x14, 0x15]); // dst MAC + data[6..12].copy_from_slice(&[0x20, 0x21, 0x22, 0x23, 0x24, 0x25]); // src MAC + data[12..14].copy_from_slice(&0x0800u16.to_be_bytes()); // EtherType = IPv4 - data[18] = 0x45; // IPv4, 20-byte header - data[20..22].copy_from_slice(&(42u16).to_be_bytes()); - data[26] = 64; // TTL - data[27] = 6; // TCP + data[14] = 0x45; // IPv4, 20-byte header + data[16..18].copy_from_slice(&(42u16).to_be_bytes()); // total length + data[22] = 64; // TTL + data[23] = 6; // Protocol = TCP - data[38 + 12] = 0x50; // TCP data offset = 5 (20 bytes) + data[34 + 12] = 0x50; // TCP data offset = 5 (20 bytes) data } @@ -6302,12 +6303,12 @@ async fn rndis_send_tcp_checksum_packet_with_vlan_ppi(driver: DefaultDriver) { assert!(metadata.flags.offload_ip_header_checksum()); assert!(metadata.flags.is_ipv4()); assert_eq!( - metadata.l2_len, 18, - "VLAN-tagged packets must use an 18-byte L2 header" + metadata.l2_len, 14, + "VLAN tag is in PPI only; frame data has a standard 14-byte L2 header" ); assert_eq!( metadata.l3_len, 20, - "VLAN-tagged IPv4 packets must keep a 20-byte L3 header" + "IPv4 packets must keep a 20-byte L3 header" ); assert_eq!( read_netvsp_counter(&nic.channel, "queues/0/tx_vlan_packets").await, @@ -6375,12 +6376,12 @@ async fn rndis_send_lso_packet_with_vlan_ppi(driver: DefaultDriver) { assert!(metadata.flags.offload_ip_header_checksum()); assert!(metadata.flags.is_ipv4()); assert_eq!( - metadata.l2_len, 18, - "VLAN-tagged packets must use an 18-byte L2 header" + metadata.l2_len, 14, + "VLAN tag is in PPI only; frame data has a standard 14-byte L2 header" ); assert_eq!( metadata.l3_len, 20, - "VLAN-tagged IPv4 packets must keep a 20-byte L3 header" + "IPv4 packets must keep a 20-byte L3 header" ); assert_eq!(metadata.max_segment_size, 1460); assert_eq!( From edf0deff191daedb98c94f461dba7fe0bf9fbc46 Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Wed, 27 May 2026 17:49:50 +0000 Subject: [PATCH 3/3] Test cleanup --- vm/devices/net/netvsp/src/test.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/vm/devices/net/netvsp/src/test.rs b/vm/devices/net/netvsp/src/test.rs index a2d7452a36..c7bf166e15 100644 --- a/vm/devices/net/netvsp/src/test.rs +++ b/vm/devices/net/netvsp/src/test.rs @@ -6225,10 +6225,7 @@ async fn rndis_send_tcp_checksum_packet_zero_transport_header_offset_ipv6(driver ); } -fn build_vlan_ipv4_tcp_packet(_vlan_id: u16) -> Vec { - // Real guests (Windows/Linux netvsc) send the VLAN info ONLY in the - // PPI — the frame data has a standard 14-byte Ethernet header without - // the 802.1Q tag inline. +fn build_ipv4_tcp_packet() -> Vec { let mut data = vec![0u8; 60]; data[..6].copy_from_slice(&[0x10, 0x11, 0x12, 0x13, 0x14, 0x15]); // dst MAC @@ -6236,7 +6233,7 @@ fn build_vlan_ipv4_tcp_packet(_vlan_id: u16) -> Vec { data[12..14].copy_from_slice(&0x0800u16.to_be_bytes()); // EtherType = IPv4 data[14] = 0x45; // IPv4, 20-byte header - data[16..18].copy_from_slice(&(42u16).to_be_bytes()); // total length + data[16..18].copy_from_slice(&(46u16).to_be_bytes()); // total length data[22] = 64; // TTL data[23] = 6; // Protocol = TCP @@ -6284,7 +6281,7 @@ async fn rndis_send_tcp_checksum_packet_with_vlan_ppi(driver: DefaultDriver) { assert_eq!(initialize_complete.request_id, 123); assert_eq!(initialize_complete.status, rndisprot::STATUS_SUCCESS); - let data = build_vlan_ipv4_tcp_packet(37); + let data = build_ipv4_tcp_packet(); let vlan_info = rndisprot::EthVlanInfo::read_from_bytes(&(37u32 << 4).to_le_bytes()).unwrap(); channel .send_rndis_packet_offload_with_vlan(&data, true, false, false, vlan_info) @@ -6356,7 +6353,7 @@ async fn rndis_send_lso_packet_with_vlan_ppi(driver: DefaultDriver) { assert_eq!(initialize_complete.request_id, 123); assert_eq!(initialize_complete.status, rndisprot::STATUS_SUCCESS); - let data = build_vlan_ipv4_tcp_packet(91); + let data = build_ipv4_tcp_packet(); let vlan_info = rndisprot::EthVlanInfo::read_from_bytes(&(91u32 << 4).to_le_bytes()).unwrap(); channel .send_rndis_packet_offload_with_vlan(&data, false, false, true, vlan_info) @@ -7316,7 +7313,7 @@ async fn vlan_tx_counter_increments(driver: DefaultDriver) { .unwrap(); // Send a VLAN-tagged packet. - let data = build_vlan_ipv4_tcp_packet(42); + let data = build_ipv4_tcp_packet(); let vlan_info = rndisprot::EthVlanInfo::read_from_bytes(&(42u32 << 4).to_le_bytes()).unwrap(); channel .send_rndis_packet_offload_with_vlan(&data, true, false, false, vlan_info)