From 0a90b89cc0bed1f2f63239ec3ac2d967552928f8 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Tue, 15 Apr 2025 09:20:23 +0200 Subject: [PATCH 01/11] xtask: explicitly set MAC address of QEMU net device This helps to identify the network interface in integration tests. --- xtask/src/qemu.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xtask/src/qemu.rs b/xtask/src/qemu.rs index 20cc7094d..b8aed7f05 100644 --- a/xtask/src/qemu.rs +++ b/xtask/src/qemu.rs @@ -498,7 +498,8 @@ pub fn run_qemu(arch: UefiArch, opt: &QemuOpt) -> Result<()> { "-netdev", "user,id=net0,net=192.168.17.0/24,tftp=uefi-test-runner/tftp/,bootfile=fake-boot-file", "-device", - "virtio-net-pci,netdev=net0", + // Some integration tests depend on this specific MAC. + "virtio-net-pci,netdev=net0,mac=52:54:00:00:00:01", ]); Some(net::EchoService::start()) } else { From 005802d9b26b5c97cd3d930fbb98839c00f080ad Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 31 May 2025 10:12:30 +0200 Subject: [PATCH 02/11] uefi-test-runner: SNP: PXE feature gate --- uefi-test-runner/src/proto/network/mod.rs | 3 +++ uefi-test-runner/src/proto/network/snp.rs | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/uefi-test-runner/src/proto/network/mod.rs b/uefi-test-runner/src/proto/network/mod.rs index 0e21db626..16b5747e5 100644 --- a/uefi-test-runner/src/proto/network/mod.rs +++ b/uefi-test-runner/src/proto/network/mod.rs @@ -5,6 +5,9 @@ pub fn test() { http::test(); pxe::test(); + // Currently, we are in the unfortunate situation that the SNP test + // depends on the PXE test, as it assigns an IPv4 address to the + // interface via DHCP. snp::test(); } diff --git a/uefi-test-runner/src/proto/network/snp.rs b/uefi-test-runner/src/proto/network/snp.rs index 9ce416d9c..507445639 100644 --- a/uefi-test-runner/src/proto/network/snp.rs +++ b/uefi-test-runner/src/proto/network/snp.rs @@ -7,6 +7,11 @@ use uefi::proto::network::snp::{InterruptStatus, ReceiveFlags, SimpleNetwork}; use uefi::{Status, boot}; pub fn test() { + // This test currently depends on the PXE test running first. + if cfg!(not(feature = "pxe")) { + return; + } + info!("Testing the simple network protocol"); let handles = boot::find_handles::().unwrap_or_default(); From 0512dc3c00aef28ce079f71a372b7e9db15c73d6 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 31 May 2025 17:20:25 +0200 Subject: [PATCH 03/11] uefi-test-runner: SNP: decouple finding handle from test Although the changes look big, most things were just reordered (moved out of the loop body) and decoupled. The function finding the proper handle verifies the interface has the right MAC, to prevent failure and ease debugging. --- uefi-test-runner/src/proto/network/snp.rs | 227 ++++++++++++---------- 1 file changed, 128 insertions(+), 99 deletions(-) diff --git a/uefi-test-runner/src/proto/network/snp.rs b/uefi-test-runner/src/proto/network/snp.rs index 507445639..ef5d40625 100644 --- a/uefi-test-runner/src/proto/network/snp.rs +++ b/uefi-test-runner/src/proto/network/snp.rs @@ -2,71 +2,102 @@ use core::time::Duration; +use uefi::boot::ScopedProtocol; use uefi::proto::network::MacAddress; use uefi::proto::network::snp::{InterruptStatus, ReceiveFlags, SimpleNetwork}; use uefi::{Status, boot}; -pub fn test() { - // This test currently depends on the PXE test running first. - if cfg!(not(feature = "pxe")) { - return; - } +/// The MAC address configured for the interface. +const EXPECTED_MAC: [u8; 6] = [0x52, 0x54, 0, 0, 0, 0x1]; - info!("Testing the simple network protocol"); +fn find_network_device() -> Option> { + let mut maybe_handle = None; let handles = boot::find_handles::().unwrap_or_default(); + // We iterate over all handles until we found the right network device. for handle in handles { - let simple_network = boot::open_protocol_exclusive::(handle); - if simple_network.is_err() { + let Ok(handle) = boot::open_protocol_exclusive::(handle) else { continue; - } - let simple_network = simple_network.unwrap(); - - // Check shutdown - let res = simple_network.shutdown(); - assert!(res == Ok(()) || res == Err(Status::NOT_STARTED.into())); + }; - // Check stop - let res = simple_network.stop(); - assert!(res == Ok(()) || res == Err(Status::NOT_STARTED.into())); + // Check media is present + if !bool::from(handle.mode().media_present_supported) + || !bool::from(handle.mode().media_present) + { + continue; + } - // Check start - simple_network - .start() - .expect("Failed to start Simple Network"); + // Check MAC address + let has_mac = handle.mode().current_address.0[0..6] == EXPECTED_MAC + && handle.mode().permanent_address.0[0..6] == EXPECTED_MAC; + if !has_mac { + continue; + } - // Check initialize - simple_network - .initialize(0, 0) - .expect("Failed to initialize Simple Network"); + maybe_handle.replace(handle); + } - // edk2 virtio-net driver does not support statistics, so - // allow UNSUPPORTED (same for collect_statistics below). - let res = simple_network.reset_statistics(); - assert!(res == Ok(()) || res == Err(Status::UNSUPPORTED.into())); + maybe_handle +} - // Reading the interrupt status clears it - simple_network.get_interrupt_status().unwrap(); +pub fn test() { + // This test currently depends on the PXE test running first. + if cfg!(not(feature = "pxe")) { + return; + } - // Set receive filters - simple_network - .receive_filters( - ReceiveFlags::UNICAST | ReceiveFlags::BROADCAST, - ReceiveFlags::empty(), - false, - None, - ) - .expect("Failed to set receive filters"); - - // Check media - if !bool::from(simple_network.mode().media_present_supported) - || !bool::from(simple_network.mode().media_present) - { - continue; - } + info!("Testing the simple network protocol"); - let payload = b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ + // The handle to our specific network device, as the test requires also a + // specific environment. We do not test all possible handles. + let simple_network = find_network_device().unwrap_or_else(|| panic!( + "Failed to find SNP handle for network device with MAC address {:x}:{:x}:{:x}:{:x}:{:x}:{:x}", + EXPECTED_MAC[0], + EXPECTED_MAC[1], + EXPECTED_MAC[2], + EXPECTED_MAC[3], + EXPECTED_MAC[4], + EXPECTED_MAC[5] + )); + + // Check shutdown + let res = simple_network.shutdown(); + assert!(res == Ok(()) || res == Err(Status::NOT_STARTED.into())); + + // Check stop + let res = simple_network.stop(); + assert!(res == Ok(()) || res == Err(Status::NOT_STARTED.into())); + + // Check start + simple_network + .start() + .expect("Failed to start Simple Network"); + + // Check initialize + simple_network + .initialize(0, 0) + .expect("Failed to initialize Simple Network"); + + // edk2 virtio-net driver does not support statistics, so + // allow UNSUPPORTED (same for collect_statistics below). + let res = simple_network.reset_statistics(); + assert!(res == Ok(()) || res == Err(Status::UNSUPPORTED.into())); + + // Reading the interrupt status clears it + simple_network.get_interrupt_status().unwrap(); + + // Set receive filters + simple_network + .receive_filters( + ReceiveFlags::UNICAST | ReceiveFlags::BROADCAST, + ReceiveFlags::empty(), + false, + None, + ) + .expect("Failed to set receive filters"); + + let payload = b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \x45\x00\ \x00\x21\ \x00\x01\ @@ -82,64 +113,62 @@ pub fn test() { \xa9\xe4\ \x04\x01\x02\x03\x04"; - let dest_addr = MacAddress([0xffu8; 32]); - assert!( - !simple_network - .get_interrupt_status() - .unwrap() - .contains(InterruptStatus::TRANSMIT) - ); - - // Send the frame - simple_network - .transmit( - simple_network.mode().media_header_size as usize, - payload, - None, - Some(dest_addr), - Some(0x0800), - ) - .expect("Failed to transmit frame"); - - info!("Waiting for the transmit"); - while !simple_network + let dest_addr = MacAddress([0xffu8; 32]); + assert!( + !simple_network .get_interrupt_status() .unwrap() .contains(InterruptStatus::TRANSMIT) - {} - - // Attempt to receive a frame - let mut buffer = [0u8; 1500]; + ); + + // Send the frame + simple_network + .transmit( + simple_network.mode().media_header_size as usize, + payload, + None, + Some(dest_addr), + Some(0x0800), + ) + .expect("Failed to transmit frame"); + + info!("Waiting for the transmit"); + while !simple_network + .get_interrupt_status() + .unwrap() + .contains(InterruptStatus::TRANSMIT) + {} + + // Attempt to receive a frame + let mut buffer = [0u8; 1500]; + + info!("Waiting for the reception"); + if simple_network.receive(&mut buffer, None, None, None, None) == Err(Status::NOT_READY.into()) + { + boot::stall(Duration::from_secs(1)); - info!("Waiting for the reception"); - if simple_network.receive(&mut buffer, None, None, None, None) - == Err(Status::NOT_READY.into()) - { - boot::stall(Duration::from_secs(1)); - - simple_network - .receive(&mut buffer, None, None, None, None) - .unwrap(); - } + simple_network + .receive(&mut buffer, None, None, None, None) + .unwrap(); + } - assert_eq!(buffer[42..47], [4, 4, 3, 2, 1]); + assert_eq!(buffer[42..47], [4, 4, 3, 2, 1]); - // Get stats - let res = simple_network.collect_statistics(); - match res { - Ok(stats) => { - info!("Stats: {:?}", stats); + // Get stats + let res = simple_network.collect_statistics(); + match res { + Ok(stats) => { + info!("Stats: {:?}", stats); - // One frame should have been transmitted and one received - assert_eq!(stats.tx_total_frames().unwrap(), 1); - assert_eq!(stats.rx_total_frames().unwrap(), 1); - } - Err(e) => { - if e == Status::UNSUPPORTED.into() { - info!("Stats: unsupported."); - } else { - panic!("{e}"); - } + // One frame should have been transmitted and one received + assert_eq!(stats.tx_total_frames().unwrap(), 1); + assert_eq!(stats.rx_total_frames().unwrap(), 1); + } + Err(e) => { + if e == Status::UNSUPPORTED.into() { + info!("Stats: unsupported."); + } else { + panic!("{e}"); } } } From fe84413295ec40c29f9c0a6b1f4e077a90b75201 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 31 May 2025 18:05:30 +0200 Subject: [PATCH 04/11] uefi-test-runner: SNP: improve doc (what's going on) --- uefi-test-runner/src/proto/network/snp.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/uefi-test-runner/src/proto/network/snp.rs b/uefi-test-runner/src/proto/network/snp.rs index ef5d40625..35b160baf 100644 --- a/uefi-test-runner/src/proto/network/snp.rs +++ b/uefi-test-runner/src/proto/network/snp.rs @@ -41,6 +41,8 @@ fn find_network_device() -> Option> { maybe_handle } +/// This test sends a simple UDP/IP packet to the `EchoService` (created by +/// `cargo xtask run`) and receives its response. pub fn test() { // This test currently depends on the PXE test running first. if cfg!(not(feature = "pxe")) { @@ -74,7 +76,6 @@ pub fn test() { .start() .expect("Failed to start Simple Network"); - // Check initialize simple_network .initialize(0, 0) .expect("Failed to initialize Simple Network"); @@ -97,6 +98,12 @@ pub fn test() { ) .expect("Failed to set receive filters"); + // EthernetFrame(IPv4Packet(UDPPacket(Payload))). + // The ethernet frame header will be filled by `transmit()`. + // The UDP packet contains the byte sequence `4, 4, 3, 2, 1`. + // + // The packet is sent to the `EchoService` created by + // `cargo xtask run`. It runs on UDP port 21572. let payload = b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \x45\x00\ \x00\x21\ @@ -152,6 +159,7 @@ pub fn test() { .unwrap(); } + // Check payload in UDP packet that was reversed by our EchoService. assert_eq!(buffer[42..47], [4, 4, 3, 2, 1]); // Get stats From f9e383adf59c684e39b659e8138c8097225bcbbe Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 31 May 2025 18:05:38 +0200 Subject: [PATCH 05/11] uefi-test-runner: SNP: improve idempotence of test snp::test() can now be called multiple times in a row --- uefi-test-runner/src/proto/network/snp.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/uefi-test-runner/src/proto/network/snp.rs b/uefi-test-runner/src/proto/network/snp.rs index 35b160baf..d51345419 100644 --- a/uefi-test-runner/src/proto/network/snp.rs +++ b/uefi-test-runner/src/proto/network/snp.rs @@ -6,6 +6,7 @@ use uefi::boot::ScopedProtocol; use uefi::proto::network::MacAddress; use uefi::proto::network::snp::{InterruptStatus, ReceiveFlags, SimpleNetwork}; use uefi::{Status, boot}; +use uefi_raw::protocol::network::snp::NetworkState; /// The MAC address configured for the interface. const EXPECTED_MAC: [u8; 6] = [0x52, 0x54, 0, 0, 0, 0x1]; @@ -63,15 +64,12 @@ pub fn test() { EXPECTED_MAC[5] )); - // Check shutdown - let res = simple_network.shutdown(); - assert!(res == Ok(()) || res == Err(Status::NOT_STARTED.into())); - - // Check stop - let res = simple_network.stop(); - assert!(res == Ok(()) || res == Err(Status::NOT_STARTED.into())); + assert_eq!( + simple_network.mode().state, + NetworkState::STOPPED, + "Should be in stopped state" + ); - // Check start simple_network .start() .expect("Failed to start Simple Network"); @@ -180,4 +178,7 @@ pub fn test() { } } } + + simple_network.stop().unwrap(); + simple_network.shutdown().unwrap(); } From 01ec4fe96681e1fd41c42cdec82309e421ad10c5 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 31 May 2025 18:14:39 +0200 Subject: [PATCH 06/11] uefi-test-runner: SNP: add more constants --- uefi-test-runner/src/proto/network/snp.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uefi-test-runner/src/proto/network/snp.rs b/uefi-test-runner/src/proto/network/snp.rs index d51345419..84318e46c 100644 --- a/uefi-test-runner/src/proto/network/snp.rs +++ b/uefi-test-runner/src/proto/network/snp.rs @@ -10,6 +10,7 @@ use uefi_raw::protocol::network::snp::NetworkState; /// The MAC address configured for the interface. const EXPECTED_MAC: [u8; 6] = [0x52, 0x54, 0, 0, 0, 0x1]; +const ETHERNET_PROTOCOL_IPV4: u16 = 0x0800; fn find_network_device() -> Option> { let mut maybe_handle = None; @@ -118,7 +119,6 @@ pub fn test() { \xa9\xe4\ \x04\x01\x02\x03\x04"; - let dest_addr = MacAddress([0xffu8; 32]); assert!( !simple_network .get_interrupt_status() @@ -132,8 +132,8 @@ pub fn test() { simple_network.mode().media_header_size as usize, payload, None, - Some(dest_addr), - Some(0x0800), + Some(simple_network.mode().broadcast_address), + Some(ETHERNET_PROTOCOL_IPV4), ) .expect("Failed to transmit frame"); From e99f02bd642a6a66ad31cad9231702887f2be5fc Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 31 May 2025 18:20:57 +0200 Subject: [PATCH 07/11] uefi-test-runner: SNP: add verbose receive function The new function makes debugging of unexpected packages (e.g. ARP) much easier detectable. --- uefi-test-runner/src/proto/network/snp.rs | 54 +++++++++++++++++++---- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/uefi-test-runner/src/proto/network/snp.rs b/uefi-test-runner/src/proto/network/snp.rs index 84318e46c..f1cf2fb0f 100644 --- a/uefi-test-runner/src/proto/network/snp.rs +++ b/uefi-test-runner/src/proto/network/snp.rs @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 +use core::ops::DerefMut; use core::time::Duration; use uefi::boot::ScopedProtocol; @@ -43,6 +44,47 @@ fn find_network_device() -> Option> { maybe_handle } +/// Receives the next IPv4 packet and prints corresponding metadata. +/// +/// Returns the length of the response. +fn receive(simple_network: &mut SimpleNetwork, buffer: &mut [u8]) -> uefi::Result { + // Wait for a bit to ensure that the previous packet has been processed. + boot::stall(Duration::from_millis(500)); + + let mut recv_src_mac = MacAddress([0; 32]); + let mut recv_dst_mac = MacAddress([0; 32]); + let mut recv_ethernet_protocol = 0; + + let res = simple_network.receive( + buffer, + None, + Some(&mut recv_src_mac), + Some(&mut recv_dst_mac), + Some(&mut recv_ethernet_protocol), + ); + + // To simplify debugging when receive an unexpected packet, we print the + // necessary info. This is especially useful if an unexpected IPv4 or ARP + // packet is received, which can easily happen when fiddling around with + // this test. + res.inspect(|_| { + debug!("Received:"); + debug!(" src_mac = {:x?}", &recv_src_mac.0[0..6]); + debug!(" dst_mac = {:x?}", &recv_dst_mac.0[0..6]); + debug!(" ethernet_proto=0x{:x?}", recv_ethernet_protocol); + + // Assert the ethernet frame was sent to the expected interface. + { + // UEFI reports proper DST MAC + assert_eq!(recv_dst_mac.0[0..6], EXPECTED_MAC); + } + + // Ensure that we do not accidentally get an ARP packet, which we + // do not expect in this test. + assert_eq!(recv_ethernet_protocol, ETHERNET_PROTOCOL_IPV4) + }) +} + /// This test sends a simple UDP/IP packet to the `EchoService` (created by /// `cargo xtask run`) and receives its response. pub fn test() { @@ -55,7 +97,7 @@ pub fn test() { // The handle to our specific network device, as the test requires also a // specific environment. We do not test all possible handles. - let simple_network = find_network_device().unwrap_or_else(|| panic!( + let mut simple_network = find_network_device().unwrap_or_else(|| panic!( "Failed to find SNP handle for network device with MAC address {:x}:{:x}:{:x}:{:x}:{:x}:{:x}", EXPECTED_MAC[0], EXPECTED_MAC[1], @@ -148,14 +190,8 @@ pub fn test() { let mut buffer = [0u8; 1500]; info!("Waiting for the reception"); - if simple_network.receive(&mut buffer, None, None, None, None) == Err(Status::NOT_READY.into()) - { - boot::stall(Duration::from_secs(1)); - - simple_network - .receive(&mut buffer, None, None, None, None) - .unwrap(); - } + let n = receive(simple_network.deref_mut(), &mut buffer).unwrap(); + debug!("Reply has {n} bytes"); // Check payload in UDP packet that was reversed by our EchoService. assert_eq!(buffer[42..47], [4, 4, 3, 2, 1]); From 0d5af99a03a636d50dde932298859575935a7816 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 31 May 2025 09:51:42 +0200 Subject: [PATCH 08/11] uefi-test-runner: SNP: use smoltcp to UDP packet Properly deconstruct the response. This improves the maintainability of this test and to better understand what is going on. --- Cargo.lock | 45 +++++++++++++++++++++++ uefi-test-runner/Cargo.toml | 1 + uefi-test-runner/src/proto/network/snp.rs | 14 ++++++- 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index bae4f4c8f..67dc33745 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -345,12 +345,31 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.5.0" @@ -438,6 +457,12 @@ dependencies = [ "crc", ] +[[package]] +name = "managed" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" + [[package]] name = "mbrman" version = "0.6.0" @@ -750,6 +775,25 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "smoltcp" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad095989c1533c1c266d9b1e8d70a1329dd3723c3edac6d03bbd67e7bf6f4bb" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "cfg-if", + "heapless", + "managed", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "subtle" version = "2.6.1" @@ -950,6 +994,7 @@ version = "0.2.0" dependencies = [ "log", "qemu-exit", + "smoltcp", "uefi", "uefi-raw", ] diff --git a/uefi-test-runner/Cargo.toml b/uefi-test-runner/Cargo.toml index 3913de22a..fe534ac92 100644 --- a/uefi-test-runner/Cargo.toml +++ b/uefi-test-runner/Cargo.toml @@ -8,6 +8,7 @@ edition = "2024" [dependencies] uefi-raw = { path = "../uefi-raw" } uefi = { path = "../uefi", features = ["alloc", "global_allocator", "panic_handler", "logger", "qemu", "log-debugcon"] } +smoltcp = { version = "0.12.0", default-features = false, features = ["medium-ethernet", "proto-ipv4", "socket-udp"] } log.workspace = true diff --git a/uefi-test-runner/src/proto/network/snp.rs b/uefi-test-runner/src/proto/network/snp.rs index f1cf2fb0f..2e04d86b8 100644 --- a/uefi-test-runner/src/proto/network/snp.rs +++ b/uefi-test-runner/src/proto/network/snp.rs @@ -77,6 +77,13 @@ fn receive(simple_network: &mut SimpleNetwork, buffer: &mut [u8]) -> uefi::Resul { // UEFI reports proper DST MAC assert_eq!(recv_dst_mac.0[0..6], EXPECTED_MAC); + + // Ethernet frame header reports proper DST MAC + let recv_frame = smoltcp::wire::EthernetFrame::new_checked(&buffer).unwrap(); + assert_eq!( + recv_frame.dst_addr(), + smoltcp::wire::EthernetAddress::from_bytes(&EXPECTED_MAC) + ); } // Ensure that we do not accidentally get an ARP packet, which we @@ -194,7 +201,12 @@ pub fn test() { debug!("Reply has {n} bytes"); // Check payload in UDP packet that was reversed by our EchoService. - assert_eq!(buffer[42..47], [4, 4, 3, 2, 1]); + { + let recv_frame = smoltcp::wire::EthernetFrame::new_checked(&buffer).unwrap(); + let recv_ipv4 = smoltcp::wire::Ipv4Packet::new_checked(recv_frame.payload()).unwrap(); + let udp_packet = smoltcp::wire::UdpPacket::new_checked(recv_ipv4.payload()).unwrap(); + assert_eq!(udp_packet.payload(), &[4, 4, 3, 2, 1]); + } // Get stats let res = simple_network.collect_statistics(); From ba68eae9cd7159bdd60bfd641a08b926a8309268 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 31 May 2025 19:54:41 +0200 Subject: [PATCH 09/11] uefi-integration-test: PXE: log IP addresses --- uefi-test-runner/src/proto/network/pxe.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/uefi-test-runner/src/proto/network/pxe.rs b/uefi-test-runner/src/proto/network/pxe.rs index 48d9c69f8..2c8b3fd1b 100644 --- a/uefi-test-runner/src/proto/network/pxe.rs +++ b/uefi-test-runner/src/proto/network/pxe.rs @@ -26,8 +26,11 @@ pub fn test() { assert!(base_code.mode().dhcp_ack_received()); let dhcp_ack: &DhcpV4Packet = base_code.mode().dhcp_ack().as_ref(); - let server_ip = dhcp_ack.bootp_si_addr; - let server_ip = IpAddress::new_v4(server_ip); + + info!("DHCP: Server IP: {:?}", dhcp_ack.bootp_si_addr); + info!("DHCP: Client IP: {:?}", dhcp_ack.bootp_yi_addr); + + let server_ip = IpAddress::new_v4(dhcp_ack.bootp_si_addr); const EXAMPLE_FILE_NAME: &[u8] = b"example-file.txt\0"; const EXAMPLE_FILE_CONTENT: &[u8] = b"Hello world!"; From 8cfeedc07d4c1c40c8e32752aacd6be1c98ebd3a Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 31 May 2025 19:56:04 +0200 Subject: [PATCH 10/11] uefi-test-runner: SNP: remove magic ethernet frame value We still have the unfortunate situation that the SNP test depends on DHCP of the PXE test, but now it is much clearer how the UDP packet is sent via Ethernet and how the echo service is used. --- uefi-test-runner/src/proto/network/snp.rs | 79 +++++++++++++++-------- 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/uefi-test-runner/src/proto/network/snp.rs b/uefi-test-runner/src/proto/network/snp.rs index 2e04d86b8..1cf34b8e0 100644 --- a/uefi-test-runner/src/proto/network/snp.rs +++ b/uefi-test-runner/src/proto/network/snp.rs @@ -2,7 +2,9 @@ use core::ops::DerefMut; use core::time::Duration; - +use smoltcp::wire::{ + ETHERNET_HEADER_LEN, EthernetFrame, IPV4_HEADER_LEN, Ipv4Packet, UDP_HEADER_LEN, UdpPacket, +}; use uefi::boot::ScopedProtocol; use uefi::proto::network::MacAddress; use uefi::proto::network::snp::{InterruptStatus, ReceiveFlags, SimpleNetwork}; @@ -146,27 +148,52 @@ pub fn test() { ) .expect("Failed to set receive filters"); - // EthernetFrame(IPv4Packet(UDPPacket(Payload))). - // The ethernet frame header will be filled by `transmit()`. - // The UDP packet contains the byte sequence `4, 4, 3, 2, 1`. - // - // The packet is sent to the `EchoService` created by - // `cargo xtask run`. It runs on UDP port 21572. - let payload = b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ - \x45\x00\ - \x00\x21\ - \x00\x01\ - \x00\x00\ - \x10\ - \x11\ - \x07\x6a\ - \xc0\xa8\x11\x0f\ - \xc0\xa8\x11\x02\ - \x54\x45\ - \x54\x44\ - \x00\x0d\ - \xa9\xe4\ - \x04\x01\x02\x03\x04"; + // High-level payload to send to destination + let payload = [ + 4_u8, /* Number of elements for echo service */ + 1, 2, 3, 4, + ]; + let frame = { + // IP that was obtained by PXE test running earlier + // TODO we should make these tests not depend on each other. + let src_ip = smoltcp::wire::Ipv4Address::new(192, 168, 17, 15); + let dst_ip = smoltcp::wire::Ipv4Address::new(192, 168, 17, 2); + + let udp_packet_len = UDP_HEADER_LEN + payload.len(); + let ipv4_packet_len = IPV4_HEADER_LEN + udp_packet_len; + let frame_len = ETHERNET_HEADER_LEN + ipv4_packet_len; + + let mut buffer = vec![0u8; frame_len]; + + let mut frame = EthernetFrame::new_unchecked(buffer.as_mut_slice()); + // Ethertype, SRC MAC, and DST MAC will be set by SNP's transmit(). + + let ipv4_packet_buffer = &mut frame.payload_mut()[0..ipv4_packet_len]; + let mut ipv4_packet = Ipv4Packet::new_unchecked(ipv4_packet_buffer); + ipv4_packet.set_header_len(IPV4_HEADER_LEN as u8 /* no extensions */); + ipv4_packet.set_total_len(ipv4_packet_len as u16); + ipv4_packet.set_hop_limit(16); + ipv4_packet.set_next_header(smoltcp::wire::IpProtocol::Udp); + ipv4_packet.set_dont_frag(true); + ipv4_packet.set_ident(0x1337); + ipv4_packet.set_version(4); + ipv4_packet.set_src_addr(src_ip); + ipv4_packet.set_dst_addr(dst_ip); + + let mut udp_packet = UdpPacket::new_unchecked(ipv4_packet.payload_mut()); + udp_packet.set_len(udp_packet_len as u16); + udp_packet.set_src_port(21573); + udp_packet.set_dst_port(21572); + udp_packet.payload_mut().copy_from_slice(&payload); + assert!(udp_packet.check_len().is_ok()); + + udp_packet.fill_checksum(&src_ip.into(), &dst_ip.into()); + // Do this last, as it depends on the other checksum. + ipv4_packet.fill_checksum(); + assert!(ipv4_packet.check_len().is_ok()); + + buffer + }; assert!( !simple_network @@ -179,7 +206,7 @@ pub fn test() { simple_network .transmit( simple_network.mode().media_header_size as usize, - payload, + &frame, None, Some(simple_network.mode().broadcast_address), Some(ETHERNET_PROTOCOL_IPV4), @@ -202,9 +229,9 @@ pub fn test() { // Check payload in UDP packet that was reversed by our EchoService. { - let recv_frame = smoltcp::wire::EthernetFrame::new_checked(&buffer).unwrap(); - let recv_ipv4 = smoltcp::wire::Ipv4Packet::new_checked(recv_frame.payload()).unwrap(); - let udp_packet = smoltcp::wire::UdpPacket::new_checked(recv_ipv4.payload()).unwrap(); + let recv_frame = EthernetFrame::new_checked(&buffer).unwrap(); + let recv_ipv4 = Ipv4Packet::new_checked(recv_frame.payload()).unwrap(); + let udp_packet = UdpPacket::new_checked(recv_ipv4.payload()).unwrap(); assert_eq!(udp_packet.payload(), &[4, 4, 3, 2, 1]); } From 507ed36219a47649e0d29ef96014dae2406ba692 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 31 May 2025 20:37:48 +0200 Subject: [PATCH 11/11] ci: fix --- uefi-test-runner/src/proto/network/snp.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/uefi-test-runner/src/proto/network/snp.rs b/uefi-test-runner/src/proto/network/snp.rs index 1cf34b8e0..361cae891 100644 --- a/uefi-test-runner/src/proto/network/snp.rs +++ b/uefi-test-runner/src/proto/network/snp.rs @@ -254,6 +254,10 @@ pub fn test() { } } - simple_network.stop().unwrap(); + // Workaround for OVMF firmware. `stop()` works in CI on x86_64, but not + // x86 or aarch64. + if simple_network.mode().state == NetworkState::STARTED { + simple_network.stop().unwrap(); + } simple_network.shutdown().unwrap(); }