diff --git a/vm/devices/net/netvsp/src/lib.rs b/vm/devices/net/netvsp/src/lib.rs index d6fc9e62a2..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 @@ -3486,7 +3485,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 => { diff --git a/vm/devices/net/netvsp/src/test.rs b/vm/devices/net/netvsp/src/test.rs index ad0f1a2902..c7bf166e15 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,19 @@ 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_ipv4_tcp_packet() -> Vec { 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(&(46u16).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 } @@ -6283,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) @@ -6302,12 +6300,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, @@ -6355,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) @@ -6375,12 +6373,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!( @@ -7315,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)