1
1
// SPDX-License-Identifier: MIT OR Apache-2.0
2
2
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 } ;
5
10
use uefi:: { boot, Status } ;
6
11
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
+
7
61
pub fn test ( ) {
8
62
info ! ( "Testing the simple network protocol" ) ;
9
63
10
- let handles = boot:: find_handles :: < SimpleNetwork > ( ) . unwrap_or_default ( ) ;
64
+ let handles = boot:: find_handles :: < SimpleNetwork > ( ) . unwrap ( ) ;
11
65
12
66
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
+ ) ;
18
83
19
- // Check shutdown
20
- simple_network
21
- . shutdown ( )
22
- . expect ( "Failed to shutdown Simple Network" ) ;
84
+ // Check media
85
+ assert ! (
86
+ simple_network . mode ( ) . media_present && simple_network . mode ( ) . media_present_supported
87
+ ) ;
23
88
24
- // Check stop
25
- simple_network
26
- . stop ( )
27
- . expect ( "Failed to stop Simple Network" ) ;
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 ) ;
28
92
29
93
// Check start
30
94
simple_network
@@ -36,56 +100,50 @@ pub fn test() {
36
100
. initialize ( 0 , 0 )
37
101
. expect ( "Failed to initialize Simple Network" ) ;
38
102
39
- simple_network. reset_statistics ( ) . unwrap ( ) ;
103
+ // edk2 virtio-net driver does not support statistics, so
104
+ // allow UNSUPPORTED (same for collect_statistics below).
105
+ let res = simple_network. reset_statistics ( ) ;
106
+ assert ! ( res == Ok ( ( ) ) || res == Err ( Status :: UNSUPPORTED . into( ) ) ) ;
40
107
41
108
// Reading the interrupt status clears it
42
109
simple_network. get_interrupt_status ( ) . unwrap ( ) ;
43
110
44
111
// Set receive filters
45
112
simple_network
46
113
. receive_filters (
47
- ReceiveFlags :: UNICAST | ReceiveFlags :: MULTICAST | ReceiveFlags :: BROADCAST ,
114
+ ReceiveFlags :: UNICAST | ReceiveFlags :: BROADCAST ,
48
115
ReceiveFlags :: empty ( ) ,
49
116
false ,
50
117
None ,
51
118
)
52
119
. expect ( "Failed to set receive filters" ) ;
53
120
54
- // Check media
55
- if !simple_network. mode ( ) . media_present_supported || !simple_network. mode ( ) . media_present {
56
- continue ;
57
- }
58
-
59
- let payload = b"\0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \
60
- \x45 \x00 \
61
- \x00 \x21 \
62
- \x00 \x01 \
63
- \x00 \x00 \
64
- \x10 \
65
- \x11 \
66
- \x07 \x6a \
67
- \xc0 \xa8 \x11 \x0f \
68
- \xc0 \xa8 \x11 \x02 \
69
- \x54 \x45 \
70
- \x54 \x44 \
71
- \x00 \x0d \
72
- \xa9 \xe4 \
73
- \x04 \x01 \x02 \x03 \x04 ";
74
-
75
- let dest_addr = MacAddress ( [ 0xffu8 ; 32 ] ) ;
76
121
assert ! ( !simple_network
77
122
. get_interrupt_status( )
78
123
. unwrap( )
79
124
. contains( InterruptStatus :: TRANSMIT ) ) ;
80
125
81
- // 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
82
140
simple_network
83
141
. transmit (
84
142
simple_network. mode ( ) . media_header_size as usize ,
85
- payload ,
86
- None ,
143
+ & packet ,
144
+ Some ( src_addr ) ,
87
145
Some ( dest_addr) ,
88
- Some ( 0x0800 ) ,
146
+ Some ( ETHER_TYPE_IPV4 ) ,
89
147
)
90
148
. expect ( "Failed to transmit frame" ) ;
91
149
@@ -97,29 +155,36 @@ pub fn test() {
97
155
{ }
98
156
99
157
// Attempt to receive a frame
100
- let mut buffer = [ 0u8 ; 1500 ] ;
158
+ let mut recv_buffer = [ 0u8 ; 1500 ] ;
101
159
102
160
info ! ( "Waiting for the reception" ) ;
103
- if simple_network. receive ( & mut buffer , None , None , None , None )
161
+ if simple_network. receive ( & mut recv_buffer , None , None , None , None )
104
162
== Err ( Status :: NOT_READY . into ( ) )
105
163
{
106
164
boot:: stall ( 1_000_000 ) ;
107
165
108
166
simple_network
109
- . receive ( & mut buffer , None , None , None , None )
167
+ . receive ( & mut recv_buffer , None , None , None , None )
110
168
. unwrap ( ) ;
111
169
}
112
170
113
- assert_eq ! ( buffer[ 42 ..47 ] , [ 4 , 4 , 3 , 2 , 1 ] ) ;
114
-
115
171
// Get stats
116
- let stats = simple_network
117
- . collect_statistics ( )
118
- . expect ( "Failed to collect statistics" ) ;
119
- info ! ( "Stats: {:?}" , stats) ;
120
-
121
- // One frame should have been transmitted and one received
122
- assert_eq ! ( stats. tx_total_frames( ) . unwrap( ) , 1 ) ;
123
- assert_eq ! ( stats. rx_total_frames( ) . unwrap( ) , 1 ) ;
172
+ let res = simple_network. collect_statistics ( ) ;
173
+ match res {
174
+ Ok ( stats) => {
175
+ info ! ( "Stats: {:?}" , stats) ;
176
+
177
+ // One frame should have been transmitted and one received
178
+ assert_eq ! ( stats. tx_total_frames( ) . unwrap( ) , 1 ) ;
179
+ assert_eq ! ( stats. rx_total_frames( ) . unwrap( ) , 1 ) ;
180
+ }
181
+ Err ( e) => {
182
+ if e == Status :: UNSUPPORTED . into ( ) {
183
+ info ! ( "Stats: unsupported." ) ;
184
+ } else {
185
+ panic ! ( "{e}" ) ;
186
+ }
187
+ }
188
+ }
124
189
}
125
190
}
0 commit comments