Skip to content

Commit 6bc27af

Browse files
committed
uefi-raw: add convenience for type IpAddress
This tightly integrates the type with the Rust standard IpAddr type. It allows the convenient usage in `uefi` in a later commit.
1 parent 7789a22 commit 6bc27af

File tree

2 files changed

+131
-13
lines changed

2 files changed

+131
-13
lines changed

uefi-raw/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
- Added `UsbIoProtocol`.
1616
- Added `Usb2HostControllerProtocol`.
1717
- Added `DevicePathProtocol::length()` properly constructing the `u16` value
18+
- Type `IpAddress` is now tightly integrated with `core::net::IpAddr`, e.g.,
19+
various `From` implementations are available.
1820

1921
## Changed
2022
- **Breaking:** Types `Ipv4Address` and `Ipv6Address` have been removed. They

uefi-raw/src/lib.rs

+129-13
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ impl From<Boolean> for bool {
113113
/// type is defined in the same way as edk2 for compatibility with C code. Note
114114
/// that this is an **untagged union**, so there's no way to tell which type of
115115
/// address an `IpAddress` value contains without additional context.
116+
///
117+
/// For convenience, this type is tightly integrated with the Rust standard
118+
/// library types [`IpAddr`], [`Ipv4Addr`], and [`IpV6Addr`].
116119
#[derive(Clone, Copy)]
117120
#[repr(C)]
118121
pub union IpAddress {
@@ -144,14 +147,66 @@ impl IpAddress {
144147
v6: Ipv6Addr::from(ip_addr),
145148
}
146149
}
150+
151+
/// Returns the octets of the union. Without additional context, it is not
152+
/// clear whether the octets represent an IPv4 or IPv6 address.
153+
pub const fn octets(&self) -> [u8; 16] {
154+
unsafe { self.v6.octets() }
155+
}
156+
157+
/// Returns a raw pointer to the IP address.
158+
#[must_use]
159+
pub const fn as_ptr(&self) -> *const Self {
160+
core::ptr::addr_of!(*self)
161+
}
162+
163+
/// Returns a raw mutable pointer to the IP address.
164+
#[must_use]
165+
pub fn as_ptr_mut(&mut self) -> *mut Self {
166+
core::ptr::addr_of_mut!(*self)
167+
}
168+
169+
/// Transforms this EFI type to the Rust standard libraries type.
170+
///
171+
/// # Arguments
172+
/// - `is_ipv6`: Whether the internal data should be interpreted as IPv6 or
173+
/// IPv4 address.
174+
pub fn to_ip_addr(self, is_ipv6: bool) -> IpAddr {
175+
if is_ipv6 {
176+
IpAddr::V6(Ipv6Addr::from(unsafe { self.v6.octets() }))
177+
} else {
178+
IpAddr::V4(Ipv4Addr::from(unsafe { self.v4.octets() }))
179+
}
180+
}
181+
182+
/// Returns the underlying data as [`Ipv4Addr`], if only the first four
183+
/// octets are used.
184+
///
185+
/// # Safety
186+
/// This function is not unsafe memory-wise but callers need to ensure with
187+
/// additional context that the IP is indeed an IPv4 address.
188+
pub unsafe fn as_ipv4(&self) -> Result<Ipv4Addr, Ipv6Addr> {
189+
let extra = self.octets()[4..].iter().any(|&x| x != 0);
190+
if extra {
191+
Ok(Ipv4Addr::from(unsafe { self.v4.octets() }))
192+
} else {
193+
Err(Ipv6Addr::from(unsafe { self.v6.octets() }))
194+
}
195+
}
196+
197+
/// Returns the underlying data as [`Ipv6Addr`].
198+
///
199+
/// # Safety
200+
/// This function is not unsafe memory-wise but callers need to ensure with
201+
/// additional context that the IP is indeed an IPv6 address.
202+
pub unsafe fn as_ipv6(&self) -> Ipv6Addr {
203+
Ipv6Addr::from(unsafe { self.v6.octets() })
204+
}
147205
}
148206

149207
impl Debug for IpAddress {
150208
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
151-
// The type is an untagged union, so we don't know whether it contains
152-
// an IPv4 or IPv6 address. It's also not safe to just print the whole
153-
// 16 bytes, since they might not all be initialized.
154-
f.debug_struct("IpAddress").finish()
209+
f.debug_tuple("IpAddress").field(&self.octets()).finish()
155210
}
156211
}
157212

@@ -176,6 +231,41 @@ impl From<IpAddr> for IpAddress {
176231
}
177232
}
178233

234+
impl From<&IpAddr> for IpAddress {
235+
fn from(t: &IpAddr) -> Self {
236+
match t {
237+
IpAddr::V4(ip) => Self {
238+
v4: Ipv4Addr::from(*ip),
239+
},
240+
IpAddr::V6(ip) => Self {
241+
v6: Ipv6Addr::from(*ip),
242+
},
243+
}
244+
}
245+
}
246+
247+
impl From<[u8; 4]> for IpAddress {
248+
fn from(value: [u8; 4]) -> Self {
249+
Self {
250+
v4: Ipv4Addr::from(value),
251+
}
252+
}
253+
}
254+
255+
impl From<[u8; 16]> for IpAddress {
256+
fn from(value: [u8; 16]) -> Self {
257+
Self {
258+
v6: Ipv6Addr::from(value),
259+
}
260+
}
261+
}
262+
263+
impl From<IpAddress> for [u8; 16] {
264+
fn from(value: IpAddress) -> Self {
265+
value.octets()
266+
}
267+
}
268+
179269
/// UEFI Media Access Control (MAC) address.
180270
///
181271
/// UEFI supports multiple network protocols and hardware types, not just
@@ -240,17 +330,43 @@ mod tests {
240330
assert_eq!(size_of::<Ipv6Addr>(), 16);
241331
assert_eq!(align_of::<Ipv6Addr>(), 1);
242332
}
243-
/// Test conversion from `core::net::IpAddr` to `IpvAddress`.
244-
///
245-
/// Note that conversion in the other direction is not possible.
333+
334+
#[test]
335+
fn ip_ptr() {
336+
let mut ip = IpAddress::new_v4([0; 4]);
337+
let ptr = ip.as_ptr_mut().cast::<u8>();
338+
unsafe {
339+
core::ptr::write(ptr, 192);
340+
core::ptr::write(ptr.add(1), 168);
341+
core::ptr::write(ptr.add(2), 42);
342+
core::ptr::write(ptr.add(3), 73);
343+
}
344+
unsafe { assert_eq!(ip.v4.octets(), [192, 168, 42, 73]) }
345+
}
346+
347+
/// Test conversion from [`IpAddr`] to [`IpAddress`].
246348
#[test]
247349
fn test_ip_addr_conversion() {
248-
let core_addr = IpAddr::V4(core::net::Ipv4Addr::from(TEST_IPV4));
249-
let uefi_addr = IpAddress::from(core_addr);
250-
assert_eq!(unsafe { uefi_addr.v4.octets() }, TEST_IPV4);
350+
// Reference: std types
351+
let core_ipv4 = IpAddr::from(TEST_IPV4);
352+
let core_ipv6 = IpAddr::from(TEST_IPV6);
353+
354+
// Test From [u8; N] constructors
355+
assert_eq!(IpAddress::from(TEST_IPV4).octets()[0..4], TEST_IPV4);
356+
assert_eq!(IpAddress::from(TEST_IPV6).octets(), TEST_IPV6);
357+
{
358+
let bytes: [u8; 16] = IpAddress::from(TEST_IPV6).into();
359+
assert_eq!(bytes, TEST_IPV6);
360+
}
361+
362+
// Test from std type constructors
363+
let efi_ipv4 = IpAddress::from(core_ipv4);
364+
assert_eq!(efi_ipv4.octets()[0..4], TEST_IPV4);
365+
assert_eq!(unsafe { efi_ipv4.as_ipv4().unwrap() }, core_ipv4);
251366

252-
let core_addr = IpAddr::V6(core::net::Ipv6Addr::from(TEST_IPV6));
253-
let uefi_addr = IpAddress::from(core_addr);
254-
assert_eq!(unsafe { uefi_addr.v6.octets() }, TEST_IPV6);
367+
let efi_ipv6 = IpAddress::from(core_ipv6);
368+
assert_eq!(efi_ipv6.octets(), TEST_IPV6);
369+
assert_eq!(unsafe { efi_ipv4.as_ipv4().unwrap_err() }, core_ipv6);
370+
assert_eq!(unsafe { efi_ipv4.as_ipv6() }, core_ipv6);
255371
}
256372
}

0 commit comments

Comments
 (0)