uefi_raw/
net.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! UEFI network types.
4//!
5//! The main exports of this module are:
6//! - [`MacAddress`]
7//! - [`IpAddress`]
8//! - [`Ipv4Address`]
9//! - [`Ipv6Address`]
10
11use core::fmt::{self, Debug, Display, Formatter};
12
13/// An IPv4 internet protocol address.
14///
15/// # Conversions and Relation to [`core::net`]
16///
17/// The following [`From`] implementations exist:
18///   - `[u8; 4]` -> [`Ipv4Address`]
19///   - [`core::net::Ipv4Addr`] -> [`Ipv4Address`]
20///   - [`core::net::IpAddr`] -> [`Ipv4Address`]
21#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
22#[repr(transparent)]
23pub struct Ipv4Address(pub [u8; 4]);
24
25impl Ipv4Address {
26    /// Returns the octets of the IP address.
27    #[must_use]
28    pub const fn octets(self) -> [u8; 4] {
29        self.0
30    }
31}
32
33impl From<core::net::Ipv4Addr> for Ipv4Address {
34    fn from(ip: core::net::Ipv4Addr) -> Self {
35        Self(ip.octets())
36    }
37}
38
39impl From<Ipv4Address> for core::net::Ipv4Addr {
40    fn from(ip: Ipv4Address) -> Self {
41        Self::from(ip.0)
42    }
43}
44
45impl From<[u8; 4]> for Ipv4Address {
46    fn from(octets: [u8; 4]) -> Self {
47        Self(octets)
48    }
49}
50
51impl Display for Ipv4Address {
52    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
53        let ip = core::net::Ipv4Addr::from(*self);
54        write!(f, "{}", ip)
55    }
56}
57
58/// An IPv6 internet protocol address.
59///
60/// # Conversions and Relation to [`core::net`]
61///
62/// The following [`From`] implementations exist:
63///   - `[u8; 16]` -> [`Ipv6Address`]
64///   - [`core::net::Ipv6Addr`] -> [`Ipv6Address`]
65///   - [`core::net::IpAddr`] -> [`Ipv6Address`]
66#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
67#[repr(transparent)]
68pub struct Ipv6Address(pub [u8; 16]);
69
70impl Ipv6Address {
71    /// Returns the octets of the IP address.
72    #[must_use]
73    pub const fn octets(self) -> [u8; 16] {
74        self.0
75    }
76}
77
78impl From<core::net::Ipv6Addr> for Ipv6Address {
79    fn from(ip: core::net::Ipv6Addr) -> Self {
80        Self(ip.octets())
81    }
82}
83
84impl From<Ipv6Address> for core::net::Ipv6Addr {
85    fn from(ip: Ipv6Address) -> Self {
86        Self::from(ip.0)
87    }
88}
89
90impl From<[u8; 16]> for Ipv6Address {
91    fn from(octets: [u8; 16]) -> Self {
92        Self(octets)
93    }
94}
95
96impl Display for Ipv6Address {
97    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
98        let ip = core::net::Ipv6Addr::from(*self);
99        write!(f, "{}", ip)
100    }
101}
102
103/// An IPv4 or IPv6 internet protocol address that is ABI compatible with EFI.
104///
105/// Corresponds to the `EFI_IP_ADDRESS` type in the UEFI specification. This
106/// type is defined in the same way as edk2 for compatibility with C code. Note
107/// that this is an untagged union, so there's no way to tell which type of
108/// address an `IpAddress` value contains without additional context.
109///
110/// # Conversions and Relation to [`core::net`]
111///
112/// The following [`From`] implementations exist:
113///   - `[u8; 4]` -> [`IpAddress`]
114///   - `[u8; 16]` -> [`IpAddress`]
115///   - [`core::net::Ipv4Addr`] -> [`IpAddress`]
116///   - [`core::net::Ipv6Addr`] -> [`IpAddress`]
117///   - [`core::net::IpAddr`] -> [`IpAddress`]
118#[derive(Clone, Copy)]
119#[repr(C)]
120pub union IpAddress {
121    /// This member serves to align the whole type to a 4 bytes as required by
122    /// the spec. Note that this is slightly different from `repr(align(4))`,
123    /// which would prevent placing this type in a packed structure.
124    pub addr: [u32; 4],
125
126    /// An IPv4 internet protocol address.
127    pub v4: Ipv4Address,
128
129    /// An IPv6 internet protocol address.
130    pub v6: Ipv6Address,
131}
132
133impl IpAddress {
134    /// Zeroed variant where all bytes are guaranteed to be initialized to zero.
135    pub const ZERO: Self = Self { addr: [0; 4] };
136
137    /// Construct a new IPv4 address.
138    ///
139    /// The type won't know that it is an IPv6 address and additional context
140    /// is needed.
141    ///
142    /// # Safety
143    /// The constructor only initializes the bytes needed for IPv4 addresses.
144    #[must_use]
145    pub const fn new_v4(octets: [u8; 4]) -> Self {
146        Self {
147            v4: Ipv4Address(octets),
148        }
149    }
150
151    /// Construct a new IPv6 address.
152    ///
153    /// The type won't know that it is an IPv6 address and additional context
154    /// is needed.
155    #[must_use]
156    pub const fn new_v6(octets: [u8; 16]) -> Self {
157        Self {
158            v6: Ipv6Address(octets),
159        }
160    }
161
162    /// Transforms this EFI type to the Rust standard library's type
163    /// [`core::net::IpAddr`].
164    ///
165    /// # Arguments
166    /// - `is_ipv6`: Whether the internal data should be interpreted as IPv6 or
167    ///   IPv4 address.
168    ///
169    /// # Safety
170    /// Callers must ensure that the `v4` field is valid if `is_ipv6` is false,
171    /// and that the `v6` field is valid if `is_ipv6` is true
172    #[must_use]
173    pub unsafe fn into_core_addr(self, is_ipv6: bool) -> core::net::IpAddr {
174        if is_ipv6 {
175            // SAFETY: Caller assumes that the underlying data is initialized.
176            core::net::IpAddr::V6(core::net::Ipv6Addr::from(unsafe { self.v6.octets() }))
177        } else {
178            // SAFETY: Caller assumes that the underlying data is initialized.
179            core::net::IpAddr::V4(core::net::Ipv4Addr::from(unsafe { self.v4.octets() }))
180        }
181    }
182}
183
184impl Debug for IpAddress {
185    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
186        // The type is an untagged union, so we don't know whether it contains
187        // an IPv4 or IPv6 address. It's also not safe to just print the whole
188        // 16 bytes, since they might not all be initialized.
189        f.debug_struct("IpAddress").finish()
190    }
191}
192
193impl Default for IpAddress {
194    fn default() -> Self {
195        Self::ZERO
196    }
197}
198
199impl From<core::net::IpAddr> for IpAddress {
200    fn from(t: core::net::IpAddr) -> Self {
201        match t {
202            core::net::IpAddr::V4(ip) => Self::new_v4(ip.octets()),
203            core::net::IpAddr::V6(ip) => Self::new_v6(ip.octets()),
204        }
205    }
206}
207
208impl From<core::net::Ipv4Addr> for IpAddress {
209    fn from(value: core::net::Ipv4Addr) -> Self {
210        Self::new_v4(value.octets())
211    }
212}
213
214impl From<core::net::Ipv6Addr> for IpAddress {
215    fn from(value: core::net::Ipv6Addr) -> Self {
216        Self::new_v6(value.octets())
217    }
218}
219
220impl From<[u8; 4]> for IpAddress {
221    fn from(octets: [u8; 4]) -> Self {
222        Self::new_v4(octets)
223    }
224}
225
226impl From<[u8; 16]> for IpAddress {
227    fn from(octets: [u8; 16]) -> Self {
228        Self::new_v6(octets)
229    }
230}
231
232/// UEFI Media Access Control (MAC) address.
233///
234/// UEFI supports multiple network protocols and hardware types, not just
235/// Ethernet. Some of them may use MAC addresses longer than 6 bytes. To be
236/// protocol-agnostic and future-proof, the UEFI spec chooses a maximum size
237/// that can hold any supported media access control address.
238///
239/// In most cases, this is just a typical `[u8; 6]` Ethernet style MAC
240/// address with the rest of the bytes being zero.
241///
242/// # Conversions and Relation to [`core::net`]
243///
244/// There is no matching type in [`core::net`] but the following [`From`]
245/// implementations exist:
246///   - `[u8; 6]` <-> [`MacAddress`]
247///   - `[u8; 32]` -> [`MacAddress`]
248#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
249#[repr(transparent)]
250pub struct MacAddress(pub [u8; 32]);
251
252impl MacAddress {
253    /// Returns the octets of the MAC address.
254    #[must_use]
255    pub const fn octets(self) -> [u8; 32] {
256        self.0
257    }
258
259    /// Interpret the MAC address as normal 6-byte MAC address, as used in
260    /// Ethernet.
261    #[must_use]
262    pub fn into_ethernet_addr(self) -> [u8; 6] {
263        let mut buffer = [0; 6];
264        buffer.copy_from_slice(&self.octets()[6..]);
265        buffer
266    }
267}
268
269// Normal Ethernet MAC address.
270impl From<[u8; 6]> for MacAddress {
271    fn from(octets: [u8; 6]) -> Self {
272        let mut buffer = [0; 32];
273        buffer[..6].copy_from_slice(&octets);
274        Self(buffer)
275    }
276}
277
278// Normal Ethernet MAC address.
279impl From<MacAddress> for [u8; 6] {
280    fn from(MacAddress(o): MacAddress) -> Self {
281        [o[0], o[1], o[2], o[3], o[4], o[5]]
282    }
283}
284
285// UEFI MAC addresses.
286impl From<[u8; 32]> for MacAddress {
287    fn from(octets: [u8; 32]) -> Self {
288        Self(octets)
289    }
290}
291
292#[cfg(test)]
293mod tests {
294    use super::*;
295
296    const TEST_IPV4: [u8; 4] = [91, 92, 93, 94];
297    const TEST_IPV6: [u8; 16] = [
298        101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
299    ];
300
301    /// Test round-trip conversion between `Ipv4Address` and `core::net::Ipv4Addr`.
302    #[test]
303    fn test_ip_addr4_conversion() {
304        let uefi_addr = Ipv4Address(TEST_IPV4);
305        let core_addr = core::net::Ipv4Addr::from(uefi_addr);
306        assert_eq!(uefi_addr, Ipv4Address::from(core_addr));
307    }
308
309    /// Test round-trip conversion between `Ipv6Address` and `core::net::Ipv6Addr`.
310    #[test]
311    fn test_ip_addr6_conversion() {
312        let uefi_addr = Ipv6Address(TEST_IPV6);
313        let core_addr = core::net::Ipv6Addr::from(uefi_addr);
314        assert_eq!(uefi_addr, Ipv6Address::from(core_addr));
315    }
316
317    /// Test conversion from `core::net::IpAddr` to `IpvAddress`.
318    ///
319    /// Note that conversion in the other direction is not possible.
320    #[test]
321    fn test_ip_addr_conversion() {
322        let core_addr = core::net::IpAddr::V4(core::net::Ipv4Addr::from(TEST_IPV4));
323        let uefi_addr = IpAddress::from(core_addr);
324        assert_eq!(unsafe { uefi_addr.v4.0 }, TEST_IPV4);
325
326        let core_addr = core::net::IpAddr::V6(core::net::Ipv6Addr::from(TEST_IPV6));
327        let uefi_addr = IpAddress::from(core_addr);
328        assert_eq!(unsafe { uefi_addr.v6.0 }, TEST_IPV6);
329    }
330
331    // Ensure that our IpAddress type can be put into a packed struct,
332    // even when it is normally 4 byte aligned.
333    #[test]
334    fn test_efi_ip_address_abi() {
335        #[repr(C, packed)]
336        struct PackedHelper<T>(T);
337
338        assert_eq!(align_of::<IpAddress>(), 4);
339        assert_eq!(size_of::<IpAddress>(), 16);
340
341        assert_eq!(align_of::<PackedHelper<IpAddress>>(), 1);
342        assert_eq!(size_of::<PackedHelper<IpAddress>>(), 16);
343    }
344
345    /// Tests the From-impls from the documentation.
346    #[test]
347    fn test_promised_from_impls() {
348        // octets -> Ipv4Address
349        {
350            let octets = [0_u8, 1, 2, 3];
351            assert_eq!(Ipv4Address::from(octets), Ipv4Address(octets));
352            let uefi_addr = IpAddress::from(octets);
353            assert_eq!(&octets, &unsafe { uefi_addr.v4.octets() });
354        }
355        // octets -> Ipv6Address
356        {
357            let octets = [0_u8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
358            assert_eq!(Ipv6Address::from(octets), Ipv6Address(octets));
359            let uefi_addr = IpAddress::from(octets);
360            assert_eq!(&octets, &unsafe { uefi_addr.v6.octets() });
361        }
362        // StdIpv4Addr -> Ipv4Address
363        {
364            let octets = [7, 5, 3, 1];
365            let core_ipv4_addr = core::net::Ipv4Addr::from(octets);
366            assert_eq!(Ipv4Address::from(core_ipv4_addr).octets(), octets);
367            assert_eq!(
368                unsafe { IpAddress::from(core_ipv4_addr).v4.octets() },
369                octets
370            );
371        }
372        // StdIpv6Addr -> Ipv6Address
373        {
374            let octets = [7, 5, 3, 1, 6, 3, 8, 5, 2, 5, 2, 7, 3, 5, 2, 6];
375            let core_ipv6_addr = core::net::Ipv6Addr::from(octets);
376            assert_eq!(Ipv6Address::from(core_ipv6_addr).octets(), octets);
377            assert_eq!(
378                unsafe { IpAddress::from(core_ipv6_addr).v6.octets() },
379                octets
380            );
381        }
382        // StdIpAddr -> IpAddress
383        {
384            let octets = [8, 8, 2, 6];
385            let core_ip_addr = core::net::IpAddr::from(octets);
386            assert_eq!(unsafe { IpAddress::from(core_ip_addr).v4.octets() }, octets);
387        }
388        // octets <-> MacAddress
389        {
390            let octets = [8, 8, 2, 6, 6, 7];
391            let uefi_mac_addr = MacAddress::from(octets);
392            assert_eq!(uefi_mac_addr.octets()[0..6], octets);
393            let octets2: [u8; 6] = uefi_mac_addr.into();
394            assert_eq!(octets2, octets)
395        }
396        // octets -> MacAddress
397        {
398            let octets = [
399                8_u8, 8, 2, 6, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 7, 0, 0, 0,
400                0, 0, 0, 0, 42,
401            ];
402            let uefi_mac_addr = MacAddress::from(octets);
403            assert_eq!(uefi_mac_addr.octets(), octets);
404        }
405    }
406
407    /// Tests the intended usage of net types in high-level APIs and how they
408    /// map to lower level UEFI types.
409    ///
410    /// TL;DR: High-level interfaces use core::net types whereas lower-level
411    /// interfaces use types from this module.
412    #[test]
413    fn test_uefi_flow() {
414        fn efi_retrieve_efi_ip_addr(addr: *mut IpAddress, is_ipv6: bool) {
415            let addr = unsafe { addr.as_mut().unwrap() };
416            // SAFETY: Alignment is guaranteed and memory is initialized.
417            unsafe {
418                addr.v4.0[0] = 42;
419                addr.v4.0[1] = 42;
420                addr.v4.0[2] = 42;
421                addr.v4.0[3] = 42;
422            }
423            if is_ipv6 {
424                unsafe {
425                    addr.v6.0[14] = 42;
426                    addr.v6.0[15] = 42;
427                }
428            }
429        }
430
431        fn high_level_retrieve_ip(is_ipv6: bool) -> core::net::IpAddr {
432            let mut efi_ip_addr = IpAddress::ZERO;
433            efi_retrieve_efi_ip_addr(&mut efi_ip_addr, is_ipv6);
434            unsafe { efi_ip_addr.into_core_addr(is_ipv6) }
435        }
436
437        let ipv4_addr = high_level_retrieve_ip(false);
438        let ipv4_addr: core::net::Ipv4Addr = match ipv4_addr {
439            core::net::IpAddr::V4(ipv4_addr) => ipv4_addr,
440            core::net::IpAddr::V6(_) => panic!("should not happen"),
441        };
442        assert_eq!(ipv4_addr.octets(), [42, 42, 42, 42]);
443
444        let ipv6_addr = high_level_retrieve_ip(true);
445        let ipv6_addr: core::net::Ipv6Addr = match ipv6_addr {
446            core::net::IpAddr::V6(ipv6_addr) => ipv6_addr,
447            core::net::IpAddr::V4(_) => panic!("should not happen"),
448        };
449        let expected = [42, 42, 42, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 42];
450        assert_eq!(ipv6_addr.octets(), expected);
451    }
452}