28
28
//! - `[u8; 6]` -> [`MacAddress`]
29
29
//! - `[u8; 32]` -> [`MacAddress`]
30
30
31
- use core:: fmt:: { Debug , Formatter } ;
32
31
use core:: net:: { IpAddr as StdIpAddr , Ipv4Addr as StdIpv4Addr , Ipv6Addr as StdIpv6Addr } ;
33
- use core:: { fmt, mem} ;
34
32
35
33
/// An IPv4 internet protocol address.
36
34
///
@@ -92,38 +90,27 @@ impl From<Ipv6Address> for StdIpv6Addr {
92
90
}
93
91
}
94
92
95
- /// EFI ABI-compatible union of an IPv4 or IPv6 internet protocol address.
96
- ///
97
- /// Corresponds to the `EFI_IP_ADDRESS` type in the UEFI specification. This
98
- /// type is defined in the same way as edk2 for compatibility with C code. Note
99
- /// that this is an **untagged union**, so there's no way to tell which type of
100
- /// address an `IpAddress` value contains without additional context.
93
+ /// EFI ABI-compatible union of an IPv4 or IPv6 internet protocol address,
94
+ /// corresponding to `EFI_IP_ADDRESS` type in the UEFI specification.
101
95
///
102
96
/// See the [module documentation] to get an overview over the relation to the
103
97
/// types from [`core::net`].
104
98
///
99
+ /// # UEFI Information
100
+ /// Corresponds to the `EFI_IP_ADDRESS` type in the UEFI specification. Instead
101
+ /// of using an untagged C union, we use this more rusty type. ABI-wise it is
102
+ /// the same but less cumbersome to work with in Rust.
103
+ ///
105
104
/// [module documentation]: self
106
- #[ derive( Clone , Copy ) ]
107
- #[ repr( C ) ]
108
- pub union IpAddress {
109
- /// An IPv4 internet protocol address.
110
- pub v4 : Ipv4Address ,
111
-
112
- /// An IPv6 internet protocol address.
113
- pub v6 : Ipv6Address ,
114
-
115
- /// This member serves to align the whole type to 4 bytes as required by
116
- /// the spec. Note that this is slightly different from `repr(align(4))`,
117
- /// which would prevent placing this type in a packed structure.
118
- align_helper : [ u32 ; 4 ] ,
119
- }
105
+ #[ derive( Debug , Clone , Copy ) ]
106
+ #[ repr( C , align( 4 ) ) ]
107
+ pub struct IpAddress ( pub [ u8 ; 16 ] ) ;
120
108
121
109
impl IpAddress {
122
110
/// Construct a new zeroed address.
123
111
#[ must_use]
124
112
pub const fn new_zeroed ( ) -> Self {
125
- // SAFETY: All bit patterns are valid.
126
- unsafe { mem:: zeroed ( ) }
113
+ Self ( [ 0 ; 16 ] )
127
114
}
128
115
129
116
/// Construct a new IPv4 address.
@@ -132,10 +119,9 @@ impl IpAddress {
132
119
/// is needed.
133
120
#[ must_use]
134
121
pub const fn new_v4 ( octets : [ u8 ; 4 ] ) -> Self {
135
- // Initialize all bytes to zero first.
136
- let mut obj = Self :: new_zeroed ( ) ;
137
- obj. v4 = Ipv4Address ( octets) ;
138
- obj
122
+ Self ( [
123
+ octets[ 0 ] , octets[ 1 ] , octets[ 2 ] , octets[ 3 ] , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
124
+ ] )
139
125
}
140
126
141
127
/// Construct a new IPv6 address.
@@ -144,17 +130,14 @@ impl IpAddress {
144
130
/// is needed.
145
131
#[ must_use]
146
132
pub const fn new_v6 ( octets : [ u8 ; 16 ] ) -> Self {
147
- // Initialize all bytes to zero first.
148
- let mut obj = Self :: new_zeroed ( ) ;
149
- obj. v6 = Ipv6Address ( octets) ;
150
- obj
133
+ Self ( octets)
151
134
}
152
135
153
136
/// Returns the octets of the union. Without additional context, it is not
154
137
/// clear whether the octets represent an IPv4 or IPv6 address.
155
138
#[ must_use]
156
139
pub const fn octets ( & self ) -> [ u8 ; 16 ] {
157
- unsafe { self . v6 . octets ( ) }
140
+ self . 0
158
141
}
159
142
160
143
/// Returns a raw pointer to the IP address.
@@ -181,11 +164,12 @@ impl IpAddress {
181
164
#[ must_use]
182
165
pub fn to_std_ip_addr ( self , is_ipv6 : bool ) -> StdIpAddr {
183
166
if is_ipv6 {
184
- StdIpAddr :: V6 ( StdIpv6Addr :: from ( unsafe { self . v6 . octets ( ) } ) )
167
+ StdIpAddr :: V6 ( StdIpv6Addr :: from ( self . octets ( ) ) )
185
168
} else {
186
169
let has_extra_bytes = self . octets ( ) [ 4 ..] . iter ( ) . any ( |& x| x != 0 ) ;
187
170
assert ! ( !has_extra_bytes) ;
188
- StdIpAddr :: V4 ( StdIpv4Addr :: from ( unsafe { self . v4 . octets ( ) } ) )
171
+ let octets: [ u8 ; 4 ] = self . octets ( ) [ ..4 ] . try_into ( ) . unwrap ( ) ;
172
+ StdIpAddr :: V4 ( StdIpv4Addr :: from ( octets) )
189
173
}
190
174
}
191
175
@@ -212,20 +196,7 @@ impl IpAddress {
212
196
/// additional context that the IP is indeed an IPv6 address.
213
197
#[ must_use]
214
198
pub unsafe fn as_ipv6 ( & self ) -> Ipv6Address {
215
- Ipv6Address :: from ( unsafe { self . v6 . octets ( ) } )
216
- }
217
- }
218
-
219
- impl Debug for IpAddress {
220
- fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
221
- f. debug_struct ( "IpAddress" )
222
- // SAFETY: All constructors ensure that all bytes are always
223
- // initialized.
224
- . field ( "v4" , & unsafe { self . v4 } )
225
- // SAFETY: All constructors ensure that all bytes are always
226
- // initialized.
227
- . field ( "v6" , & unsafe { self . v6 } )
228
- . finish ( )
199
+ Ipv6Address :: from ( self . octets ( ) )
229
200
}
230
201
}
231
202
@@ -336,6 +307,18 @@ mod tests {
336
307
101 , 102 , 103 , 104 , 105 , 106 , 107 , 108 , 109 , 110 , 111 , 112 , 113 , 114 , 115 , 116 ,
337
308
] ;
338
309
310
+ /// Ensures ABI-compatibility, as described here:
311
+ /// <https://github.com/tianocore/edk2/blob/b1887152024c7eb0cc7de735b0b57febd6099bf9/MdePkg/Include/Uefi/UefiBaseType.h#L100>
312
+ #[ test]
313
+ fn test_abi ( ) {
314
+ assert_eq ! ( size_of:: <IpAddress >( ) , 16 ) ;
315
+ assert_eq ! ( align_of:: <IpAddress >( ) , 4 ) ;
316
+ assert_eq ! ( size_of:: <Ipv4Address >( ) , 4 ) ;
317
+ assert_eq ! ( align_of:: <Ipv6Address >( ) , 1 ) ;
318
+ assert_eq ! ( size_of:: <Ipv6Address >( ) , 16 ) ;
319
+ assert_eq ! ( align_of:: <Ipv6Address >( ) , 1 ) ;
320
+ }
321
+
339
322
/// Test round-trip conversion between `Ipv4Address` and `StdIpv4Addr`.
340
323
#[ test]
341
324
fn test_ip_addr4_conversion ( ) {
@@ -359,11 +342,14 @@ mod tests {
359
342
fn test_ip_addr_conversion ( ) {
360
343
let core_addr = StdIpAddr :: V4 ( StdIpv4Addr :: from ( TEST_IPV4 ) ) ;
361
344
let uefi_addr = IpAddress :: from ( core_addr) ;
362
- assert_eq ! ( unsafe { uefi_addr. v4. 0 } , TEST_IPV4 ) ;
345
+ assert_eq ! (
346
+ unsafe { uefi_addr. try_as_ipv4( ) . unwrap( ) } . octets( ) ,
347
+ TEST_IPV4
348
+ ) ;
363
349
364
350
let core_addr = StdIpAddr :: V6 ( StdIpv6Addr :: from ( TEST_IPV6 ) ) ;
365
351
let uefi_addr = IpAddress :: from ( core_addr) ;
366
- assert_eq ! ( unsafe { uefi_addr. v6 . 0 } , TEST_IPV6 ) ;
352
+ assert_eq ! ( unsafe { uefi_addr. as_ipv6 ( ) } . octets ( ) , TEST_IPV6 ) ;
367
353
}
368
354
369
355
/// Tests the From-impls as described in the module description.
@@ -413,30 +399,18 @@ mod tests {
413
399
}
414
400
}
415
401
416
- /// Tests that all bytes are initialized and that the Debug print doesn't
417
- /// produce errors, when Miri executes this.
418
- #[ test]
419
- fn test_ip_address_debug_memory_safe ( ) {
420
- let uefi_addr = IpAddress :: new_v6 ( TEST_IPV6 ) ;
421
- std:: eprintln!( "{uefi_addr:#?}" ) ;
422
- }
423
-
424
402
/// Tests the expected flow of types in a higher-level UEFI API.
425
403
#[ test]
426
404
fn test_uefi_flow ( ) {
427
405
fn efi_retrieve_efi_ip_addr ( addr : & mut IpAddress , is_ipv6 : bool ) {
428
- // SAFETY: Alignment is guaranteed and memory is initialized.
429
- unsafe {
430
- addr. v4 . 0 [ 0 ] = 42 ;
431
- addr. v4 . 0 [ 1 ] = 42 ;
432
- addr. v4 . 0 [ 2 ] = 42 ;
433
- addr. v4 . 0 [ 3 ] = 42 ;
434
- }
406
+ addr. 0 [ 0 ] = 42 ;
407
+ addr. 0 [ 1 ] = 42 ;
408
+ addr. 0 [ 2 ] = 42 ;
409
+ addr. 0 [ 3 ] = 42 ;
410
+
435
411
if is_ipv6 {
436
- unsafe {
437
- addr. v6 . 0 [ 14 ] = 42 ;
438
- addr. v6 . 0 [ 15 ] = 42 ;
439
- }
412
+ addr. 0 [ 14 ] = 42 ;
413
+ addr. 0 [ 15 ] = 42 ;
440
414
}
441
415
}
442
416
0 commit comments