Skip to content

Commit 7d66ab4

Browse files
committed
xxx fix snp test: make it idempotent and remove magic values
Currently the packet is not received back. I know that the existing packet (application payload in udp packet in ipv4 packet) works. Therefore, anything must be wrong with the creation of the packet.
1 parent 34a9df2 commit 7d66ab4

File tree

2 files changed

+200
-47
lines changed

2 files changed

+200
-47
lines changed
+100-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,109 @@
11
// SPDX-License-Identifier: MIT OR Apache-2.0
22

3+
use alloc::vec::Vec;
4+
use core::net::Ipv4Addr;
5+
36
pub fn test() {
47
info!("Testing Network protocols");
58

6-
pxe::test();
7-
snp::test();
9+
// Test idempotence of network stuff
10+
for _ in 0..2 {
11+
#[cfg(feature = "pxe")]
12+
//pxe::test();
13+
snp::test();
14+
}
815
}
916

17+
#[cfg(feature = "pxe")]
1018
mod pxe;
1119
mod snp;
20+
21+
/// Computes the standard IPv4 or UDP checksum (one's complement)
22+
fn compute_ipv4_checksum(data: &[u8]) -> u16 {
23+
let mut sum = 0u32;
24+
25+
for chunk in data.chunks(2) {
26+
let word = if chunk.len() == 2 {
27+
u16::from_be_bytes([chunk[0], chunk[1]]) as u32
28+
} else {
29+
(chunk[0] as u32) << 8
30+
};
31+
sum = sum.wrapping_add(word);
32+
}
33+
34+
// Fold carry
35+
while (sum >> 16) != 0 {
36+
sum = (sum & 0xFFFF) + (sum >> 16);
37+
}
38+
39+
!sum as u16
40+
}
41+
42+
/// Builds the UDP pseudo-header used for UDP checksum calculation
43+
fn build_udp_pseudo_header(src_ip: &Ipv4Addr, dst_ip: &Ipv4Addr, udp_segment: &[u8]) -> Vec<u8> {
44+
let mut pseudo = Vec::with_capacity(12 + udp_segment.len());
45+
46+
// Pseudo-header: src IP, dst IP, zero, protocol, UDP length
47+
pseudo.extend_from_slice(&src_ip.octets());
48+
pseudo.extend_from_slice(&dst_ip.octets());
49+
pseudo.push(0);
50+
pseudo.push(0x11); // Protocol = UDP
51+
pseudo.extend_from_slice(&(udp_segment.len() as u16).to_be_bytes());
52+
53+
pseudo.extend_from_slice(udp_segment);
54+
55+
if pseudo.len() % 2 != 0 {
56+
pseudo.push(0); // Pad to even length
57+
}
58+
59+
pseudo
60+
}
61+
62+
fn build_ipv4_udp_packet(
63+
src_ip: Ipv4Addr,
64+
dst_ip: Ipv4Addr,
65+
src_port: u16,
66+
dst_port: u16,
67+
payload: &[u8],
68+
) -> Vec<u8> {
69+
let ip_header_len = 20;
70+
let udp_header_len = 8;
71+
let total_len = ip_header_len + udp_header_len + payload.len();
72+
73+
let mut packet = Vec::with_capacity(total_len);
74+
75+
// === IPv4 Header ===
76+
packet.push(0x45); // Version (4) + IHL (5)
77+
packet.push(0x00); // DSCP/ECN
78+
packet.extend_from_slice(&(total_len as u16).to_be_bytes()); // Total Length
79+
packet.extend_from_slice(&0x0001u16.to_be_bytes()); // Identification
80+
packet.extend_from_slice(&0x0000u16.to_be_bytes()); // Flags/Fragment offset
81+
packet.push(0x40); // TTL
82+
packet.push(0x11); // Protocol = UDP (17)
83+
packet.extend_from_slice(&[0x00, 0x00]); // Checksum placeholder
84+
packet.extend_from_slice(&src_ip.octets()); // Source IP
85+
packet.extend_from_slice(&dst_ip.octets()); // Destination IP
86+
87+
// Compute IPv4 header checksum
88+
let ip_checksum = compute_ipv4_checksum(&packet[..20]);
89+
packet[10..12].copy_from_slice(&ip_checksum.to_be_bytes());
90+
91+
// === UDP Header ===
92+
packet.extend_from_slice(&src_port.to_be_bytes()); // Source port
93+
packet.extend_from_slice(&dst_port.to_be_bytes()); // Destination port
94+
let udp_len = (udp_header_len + payload.len()) as u16;
95+
packet.extend_from_slice(&udp_len.to_be_bytes()); // UDP length
96+
packet.extend_from_slice(&[0x00, 0x00]); // UDP checksum placeholder
97+
98+
// === Payload ===
99+
packet.extend_from_slice(payload);
100+
101+
// === UDP Checksum ===
102+
let udp_offset = 20;
103+
let udp_packet = &packet[udp_offset..];
104+
let pseudo_header = build_udp_pseudo_header(&src_ip, &dst_ip, udp_packet);
105+
let udp_checksum = compute_ipv4_checksum(&pseudo_header);
106+
packet[udp_offset + 6..udp_offset + 8].copy_from_slice(&udp_checksum.to_be_bytes());
107+
108+
packet
109+
}

uefi-test-runner/src/proto/network/snp.rs

+100-45
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,94 @@
11
// SPDX-License-Identifier: MIT OR Apache-2.0
22

3-
use uefi::proto::network::snp::{InterruptStatus, ReceiveFlags, SimpleNetwork};
4-
use uefi::proto::network::MacAddress;
3+
use crate::proto::network::build_ipv4_udp_packet;
4+
use alloc::string::ToString;
5+
use core::net::Ipv4Addr;
6+
use uefi::boot::{OpenProtocolAttributes, OpenProtocolParams};
7+
use uefi::proto::device_path::text::{AllowShortcuts, DisplayOnly};
8+
use uefi::proto::device_path::DevicePath;
9+
use uefi::proto::network::snp::{InterruptStatus, NetworkState, ReceiveFlags, SimpleNetwork};
510
use uefi::{boot, Status};
611

12+
fn compute_ipv4_checksum(header: &[u8]) -> u16 {
13+
assert_eq!(header.len() % 2, 0);
14+
let sum = header
15+
.chunks(2)
16+
.map(|chunk| u16::from_be_bytes([chunk[0], chunk[1]]) as u32)
17+
.sum::<u32>();
18+
19+
let carry_add = (sum & 0xFFFF) + (sum >> 16);
20+
!(carry_add as u16)
21+
}
22+
23+
fn build_ipv4_packet_with_payload(
24+
src_ip: Ipv4Addr,
25+
dest_ip: Ipv4Addr,
26+
payload: [u8; 2],
27+
) -> [u8; 22] {
28+
let mut packet = [0u8; 22];
29+
let len = packet.len() as u16;
30+
31+
// IPv4 header
32+
// Version = 4, IHL = 5
33+
packet[0] = 0x45;
34+
// DSCP/ECN
35+
packet[1] = 0x00;
36+
// Total length
37+
packet[2..4].copy_from_slice(&(len.to_be_bytes()));
38+
// Identification
39+
packet[4..6].copy_from_slice(&0u16.to_be_bytes());
40+
// Flags (DF), Fragment offset
41+
packet[6..8].copy_from_slice(&0x4000u16.to_be_bytes());
42+
// TTL
43+
packet[8] = 0x40;
44+
// Protocol (UDP)
45+
packet[9] = 0x11;
46+
// Checksum placeholder at [10..12]
47+
packet[12..16].copy_from_slice(&src_ip.octets()); // Source IP
48+
packet[16..20].copy_from_slice(&dest_ip.octets()); // Destination IP
49+
50+
// Calculate checksum
51+
let checksum = compute_ipv4_checksum(&packet[0..20]);
52+
packet[10..12].copy_from_slice(&checksum.to_be_bytes());
53+
54+
// Payload
55+
packet[20] = payload[0];
56+
packet[21] = payload[1];
57+
58+
packet
59+
}
60+
761
pub fn test() {
862
info!("Testing the simple network protocol");
963

10-
let handles = boot::find_handles::<SimpleNetwork>().unwrap_or_default();
64+
let handles = boot::find_handles::<SimpleNetwork>().unwrap();
1165

1266
for handle in handles {
13-
let simple_network = boot::open_protocol_exclusive::<SimpleNetwork>(handle);
14-
if simple_network.is_err() {
15-
continue;
16-
}
17-
let simple_network = simple_network.unwrap();
67+
// Buggy firmware; although it should be there, we have to test if the
68+
// protocol is actually installed.
69+
let simple_network = match boot::open_protocol_exclusive::<SimpleNetwork>(handle) {
70+
Ok(snp) => snp,
71+
Err(e) => {
72+
log::debug!("Handle {handle:?} doesn't actually support SNP; skipping");
73+
continue;
74+
}
75+
};
76+
let simple_network_dvp = boot::open_protocol_exclusive::<DevicePath>(handle).unwrap();
77+
debug!(
78+
"Testing network device: {}",
79+
simple_network_dvp
80+
.to_string(DisplayOnly(false), AllowShortcuts(false))
81+
.unwrap()
82+
);
1883

19-
// Check shutdown
20-
let res = simple_network.shutdown();
21-
assert!(res == Ok(()) || res == Err(Status::NOT_STARTED.into()));
84+
// Check media
85+
assert!(
86+
simple_network.mode().media_present && simple_network.mode().media_present_supported
87+
);
2288

23-
// Check stop
24-
let res = simple_network.stop();
25-
assert!(res == Ok(()) || res == Err(Status::NOT_STARTED.into()));
89+
// Ensure network is not started yet. If it is started, another test
90+
// didn't clean up properly.
91+
assert_eq!(simple_network.mode().state, NetworkState::STOPPED);
2692

2793
// Check start
2894
simple_network
@@ -52,41 +118,32 @@ pub fn test() {
52118
)
53119
.expect("Failed to set receive filters");
54120

55-
// Check media
56-
if !simple_network.mode().media_present_supported || !simple_network.mode().media_present {
57-
continue;
58-
}
59-
60-
let payload = b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
61-
\x45\x00\
62-
\x00\x21\
63-
\x00\x01\
64-
\x00\x00\
65-
\x10\
66-
\x11\
67-
\x07\x6a\
68-
\xc0\xa8\x11\x0f\
69-
\xc0\xa8\x11\x02\
70-
\x54\x45\
71-
\x54\x44\
72-
\x00\x0d\
73-
\xa9\xe4\
74-
\x04\x01\x02\x03\x04";
75-
76-
let dest_addr = MacAddress([0xffu8; 32]);
77121
assert!(!simple_network
78122
.get_interrupt_status()
79123
.unwrap()
80124
.contains(InterruptStatus::TRANSMIT));
81125

82-
// Send the frame
126+
// Broadcast address (send to self)
127+
let src_addr = simple_network.mode().current_address;
128+
let dest_addr = simple_network.mode().broadcast_address;
129+
const ETHER_TYPE_IPV4: u16 = 0x0800;
130+
131+
// IPv4 can be arbitrary, not used for routing here.
132+
let src_ip = Ipv4Addr::new(192, 168, 17, 15);
133+
let dst_ip = Ipv4Addr::new(192, 168, 17, 2);
134+
let src_port = 0x5445; // "TE"
135+
let dst_port = 0x5444; // "TD"
136+
let payload = 0x1337_u16.to_ne_bytes();
137+
let packet = build_ipv4_udp_packet(src_ip, dst_ip, src_port, dst_port, &payload);
138+
139+
// Send the frame to ourselves
83140
simple_network
84141
.transmit(
85142
simple_network.mode().media_header_size as usize,
86-
payload,
87-
None,
143+
&packet,
144+
Some(src_addr),
88145
Some(dest_addr),
89-
Some(0x0800),
146+
Some(ETHER_TYPE_IPV4),
90147
)
91148
.expect("Failed to transmit frame");
92149

@@ -98,21 +155,19 @@ pub fn test() {
98155
{}
99156

100157
// Attempt to receive a frame
101-
let mut buffer = [0u8; 1500];
158+
let mut recv_buffer = [0u8; 1500];
102159

103160
info!("Waiting for the reception");
104-
if simple_network.receive(&mut buffer, None, None, None, None)
161+
if simple_network.receive(&mut recv_buffer, None, None, None, None)
105162
== Err(Status::NOT_READY.into())
106163
{
107164
boot::stall(1_000_000);
108165

109166
simple_network
110-
.receive(&mut buffer, None, None, None, None)
167+
.receive(&mut recv_buffer, None, None, None, None)
111168
.unwrap();
112169
}
113170

114-
assert_eq!(buffer[42..47], [4, 4, 3, 2, 1]);
115-
116171
// Get stats
117172
let res = simple_network.collect_statistics();
118173
match res {

0 commit comments

Comments
 (0)