use core::fmt::{self, Debug, Display, Formatter};
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Ipv4Address(pub [u8; 4]);
impl Ipv4Address {
#[must_use]
pub const fn octets(self) -> [u8; 4] {
self.0
}
}
impl From<core::net::Ipv4Addr> for Ipv4Address {
fn from(ip: core::net::Ipv4Addr) -> Self {
Self(ip.octets())
}
}
impl From<Ipv4Address> for core::net::Ipv4Addr {
fn from(ip: Ipv4Address) -> Self {
Self::from(ip.0)
}
}
impl From<[u8; 4]> for Ipv4Address {
fn from(octets: [u8; 4]) -> Self {
Self(octets)
}
}
impl Display for Ipv4Address {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let ip = core::net::Ipv4Addr::from(*self);
write!(f, "{}", ip)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Ipv6Address(pub [u8; 16]);
impl Ipv6Address {
#[must_use]
pub const fn octets(self) -> [u8; 16] {
self.0
}
}
impl From<core::net::Ipv6Addr> for Ipv6Address {
fn from(ip: core::net::Ipv6Addr) -> Self {
Self(ip.octets())
}
}
impl From<Ipv6Address> for core::net::Ipv6Addr {
fn from(ip: Ipv6Address) -> Self {
Self::from(ip.0)
}
}
impl From<[u8; 16]> for Ipv6Address {
fn from(octets: [u8; 16]) -> Self {
Self(octets)
}
}
impl Display for Ipv6Address {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let ip = core::net::Ipv6Addr::from(*self);
write!(f, "{}", ip)
}
}
#[derive(Clone, Copy)]
#[repr(C)]
pub union IpAddress {
pub addr: [u32; 4],
pub v4: Ipv4Address,
pub v6: Ipv6Address,
}
impl IpAddress {
pub const ZERO: Self = Self { addr: [0; 4] };
#[must_use]
pub const fn new_v4(octets: [u8; 4]) -> Self {
Self {
v4: Ipv4Address(octets),
}
}
#[must_use]
pub const fn new_v6(octets: [u8; 16]) -> Self {
Self {
v6: Ipv6Address(octets),
}
}
#[must_use]
pub unsafe fn into_core_addr(self, is_ipv6: bool) -> core::net::IpAddr {
if is_ipv6 {
core::net::IpAddr::V6(core::net::Ipv6Addr::from(unsafe { self.v6.octets() }))
} else {
core::net::IpAddr::V4(core::net::Ipv4Addr::from(unsafe { self.v4.octets() }))
}
}
}
impl Debug for IpAddress {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("IpAddress").finish()
}
}
impl Default for IpAddress {
fn default() -> Self {
Self::ZERO
}
}
impl From<core::net::IpAddr> for IpAddress {
fn from(t: core::net::IpAddr) -> Self {
match t {
core::net::IpAddr::V4(ip) => Self::new_v4(ip.octets()),
core::net::IpAddr::V6(ip) => Self::new_v6(ip.octets()),
}
}
}
impl From<core::net::Ipv4Addr> for IpAddress {
fn from(value: core::net::Ipv4Addr) -> Self {
Self::new_v4(value.octets())
}
}
impl From<core::net::Ipv6Addr> for IpAddress {
fn from(value: core::net::Ipv6Addr) -> Self {
Self::new_v6(value.octets())
}
}
impl From<[u8; 4]> for IpAddress {
fn from(octets: [u8; 4]) -> Self {
Self::new_v4(octets)
}
}
impl From<[u8; 16]> for IpAddress {
fn from(octets: [u8; 16]) -> Self {
Self::new_v6(octets)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct MacAddress(pub [u8; 32]);
impl MacAddress {
#[must_use]
pub const fn octets(self) -> [u8; 32] {
self.0
}
#[must_use]
pub fn into_ethernet_addr(self) -> [u8; 6] {
let mut buffer = [0; 6];
buffer.copy_from_slice(&self.octets()[6..]);
buffer
}
}
impl From<[u8; 6]> for MacAddress {
fn from(octets: [u8; 6]) -> Self {
let mut buffer = [0; 32];
buffer[..6].copy_from_slice(&octets);
Self(buffer)
}
}
impl From<MacAddress> for [u8; 6] {
fn from(MacAddress(o): MacAddress) -> Self {
[o[0], o[1], o[2], o[3], o[4], o[5]]
}
}
impl From<[u8; 32]> for MacAddress {
fn from(octets: [u8; 32]) -> Self {
Self(octets)
}
}
#[cfg(test)]
mod tests {
use super::*;
const TEST_IPV4: [u8; 4] = [91, 92, 93, 94];
const TEST_IPV6: [u8; 16] = [
101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
];
#[test]
fn test_ip_addr4_conversion() {
let uefi_addr = Ipv4Address(TEST_IPV4);
let core_addr = core::net::Ipv4Addr::from(uefi_addr);
assert_eq!(uefi_addr, Ipv4Address::from(core_addr));
}
#[test]
fn test_ip_addr6_conversion() {
let uefi_addr = Ipv6Address(TEST_IPV6);
let core_addr = core::net::Ipv6Addr::from(uefi_addr);
assert_eq!(uefi_addr, Ipv6Address::from(core_addr));
}
#[test]
fn test_ip_addr_conversion() {
let core_addr = core::net::IpAddr::V4(core::net::Ipv4Addr::from(TEST_IPV4));
let uefi_addr = IpAddress::from(core_addr);
assert_eq!(unsafe { uefi_addr.v4.0 }, TEST_IPV4);
let core_addr = core::net::IpAddr::V6(core::net::Ipv6Addr::from(TEST_IPV6));
let uefi_addr = IpAddress::from(core_addr);
assert_eq!(unsafe { uefi_addr.v6.0 }, TEST_IPV6);
}
#[test]
fn test_efi_ip_address_abi() {
#[repr(C, packed)]
struct PackedHelper<T>(T);
assert_eq!(align_of::<IpAddress>(), 4);
assert_eq!(size_of::<IpAddress>(), 16);
assert_eq!(align_of::<PackedHelper<IpAddress>>(), 1);
assert_eq!(size_of::<PackedHelper<IpAddress>>(), 16);
}
#[test]
fn test_promised_from_impls() {
{
let octets = [0_u8, 1, 2, 3];
assert_eq!(Ipv4Address::from(octets), Ipv4Address(octets));
let uefi_addr = IpAddress::from(octets);
assert_eq!(&octets, &unsafe { uefi_addr.v4.octets() });
}
{
let octets = [0_u8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
assert_eq!(Ipv6Address::from(octets), Ipv6Address(octets));
let uefi_addr = IpAddress::from(octets);
assert_eq!(&octets, &unsafe { uefi_addr.v6.octets() });
}
{
let octets = [7, 5, 3, 1];
let core_ipv4_addr = core::net::Ipv4Addr::from(octets);
assert_eq!(Ipv4Address::from(core_ipv4_addr).octets(), octets);
assert_eq!(
unsafe { IpAddress::from(core_ipv4_addr).v4.octets() },
octets
);
}
{
let octets = [7, 5, 3, 1, 6, 3, 8, 5, 2, 5, 2, 7, 3, 5, 2, 6];
let core_ipv6_addr = core::net::Ipv6Addr::from(octets);
assert_eq!(Ipv6Address::from(core_ipv6_addr).octets(), octets);
assert_eq!(
unsafe { IpAddress::from(core_ipv6_addr).v6.octets() },
octets
);
}
{
let octets = [8, 8, 2, 6];
let core_ip_addr = core::net::IpAddr::from(octets);
assert_eq!(unsafe { IpAddress::from(core_ip_addr).v4.octets() }, octets);
}
{
let octets = [8, 8, 2, 6, 6, 7];
let uefi_mac_addr = MacAddress::from(octets);
assert_eq!(uefi_mac_addr.octets()[0..6], octets);
let octets2: [u8; 6] = uefi_mac_addr.into();
assert_eq!(octets2, octets)
}
{
let octets = [
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,
0, 0, 0, 0, 42,
];
let uefi_mac_addr = MacAddress::from(octets);
assert_eq!(uefi_mac_addr.octets(), octets);
}
}
#[test]
fn test_uefi_flow() {
fn efi_retrieve_efi_ip_addr(addr: *mut IpAddress, is_ipv6: bool) {
let addr = unsafe { addr.as_mut().unwrap() };
unsafe {
addr.v4.0[0] = 42;
addr.v4.0[1] = 42;
addr.v4.0[2] = 42;
addr.v4.0[3] = 42;
}
if is_ipv6 {
unsafe {
addr.v6.0[14] = 42;
addr.v6.0[15] = 42;
}
}
}
fn high_level_retrieve_ip(is_ipv6: bool) -> core::net::IpAddr {
let mut efi_ip_addr = IpAddress::ZERO;
efi_retrieve_efi_ip_addr(&mut efi_ip_addr, is_ipv6);
unsafe { efi_ip_addr.into_core_addr(is_ipv6) }
}
let ipv4_addr = high_level_retrieve_ip(false);
let ipv4_addr: core::net::Ipv4Addr = match ipv4_addr {
core::net::IpAddr::V4(ipv4_addr) => ipv4_addr,
core::net::IpAddr::V6(_) => panic!("should not happen"),
};
assert_eq!(ipv4_addr.octets(), [42, 42, 42, 42]);
let ipv6_addr = high_level_retrieve_ip(true);
let ipv6_addr: core::net::Ipv6Addr = match ipv6_addr {
core::net::IpAddr::V6(ipv6_addr) => ipv6_addr,
core::net::IpAddr::V4(_) => panic!("should not happen"),
};
let expected = [42, 42, 42, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 42];
assert_eq!(ipv6_addr.octets(), expected);
}
}