use crate::zerocopy_type;
zerocopy_type!(
pub struct Ipv4Addr {
inner: [u8; 4],
}
);
impl Ipv4Addr {
pub const UNSPECIFIED: Self = Self { inner: [0; 4] };
#[inline]
pub const fn octets(&self) -> [u8; 4] {
self.inner
}
#[inline]
pub const fn from_octets(bytes: [u8; 4]) -> Self {
Self { inner: bytes }
}
#[inline]
const fn into_core(self) -> core::net::Ipv4Addr {
core::net::Ipv4Addr::new(
self.inner[0],
self.inner[1],
self.inner[2],
self.inner[3],
)
}
#[inline]
pub const fn is_multicast(&self) -> bool {
self.into_core().is_multicast()
}
#[inline]
pub const fn is_broadcast(&self) -> bool {
self.into_core().is_broadcast()
}
#[inline]
pub const fn is_private(&self) -> bool {
self.into_core().is_private()
}
#[inline]
pub const fn is_loopback(&self) -> bool {
self.into_core().is_loopback()
}
#[inline]
pub const fn is_unicast(&self) -> bool {
!self.is_multicast() && !self.is_broadcast()
}
#[inline]
pub const fn is_link_local(&self) -> bool {
self.into_core().is_link_local()
}
#[inline]
pub const fn is_global(&self) -> bool {
!self.is_multicast()
&& !self.is_private()
&& !self.is_loopback()
&& !self.is_link_local()
&& !self.is_broadcast()
}
#[inline]
pub const fn is_documentation(&self) -> bool {
matches!(
self.octets(),
[192, 0, 2, _]
| [198, 51, 100, _]
| [203, 0, 113, _]
| [233, 252, 0, _]
)
}
#[inline]
pub const fn is_reserved(&self) -> bool {
self.octets()[0] & 240 == 240 && !self.is_broadcast()
}
}
impl From<core::net::Ipv4Addr> for Ipv4Addr {
#[inline]
fn from(ip4: core::net::Ipv4Addr) -> Self {
Self { inner: ip4.octets() }
}
}
impl From<Ipv4Addr> for core::net::Ipv4Addr {
#[inline]
fn from(ip4: Ipv4Addr) -> Self {
Self::from(ip4.inner)
}
}
zerocopy_type!(
pub struct Ipv6Addr {
inner: [u8; 16],
}
);
impl Ipv6Addr {
pub const UNSPECIFIED: Self = Self { inner: [0; 16] };
pub const LOCALHOST: Self =
Self { inner: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] };
#[inline]
pub const fn octets(&self) -> [u8; 16] {
self.inner
}
#[inline]
pub const fn from_octets(bytes: [u8; 16]) -> Self {
Self { inner: bytes }
}
#[inline]
pub const fn from_segments(words: [u16; 8]) -> Self {
let w0 = words[0].to_be_bytes();
let w1 = words[1].to_be_bytes();
let w2 = words[2].to_be_bytes();
let w3 = words[3].to_be_bytes();
let w4 = words[4].to_be_bytes();
let w5 = words[5].to_be_bytes();
let w6 = words[6].to_be_bytes();
let w7 = words[7].to_be_bytes();
Self {
inner: [
w0[0], w0[1], w1[0], w1[1], w2[0], w2[1], w3[0], w3[1], w4[0],
w4[1], w5[0], w5[1], w6[0], w6[1], w7[0], w7[1],
],
}
}
#[inline]
pub const fn segments(&self) -> [u16; 8] {
let [a, b, c, d, e, f, g, h] = unsafe {
core::mem::transmute::<[u8; 16], [u16; 8]>(self.octets())
};
[
u16::from_be(a),
u16::from_be(b),
u16::from_be(c),
u16::from_be(d),
u16::from_be(e),
u16::from_be(f),
u16::from_be(g),
u16::from_be(h),
]
}
#[inline]
const fn into_core(self) -> core::net::Ipv6Addr {
let segments = self.segments();
core::net::Ipv6Addr::new(
segments[0],
segments[1],
segments[2],
segments[3],
segments[4],
segments[5],
segments[6],
segments[7],
)
}
#[inline]
pub const fn is_multicast(&self) -> bool {
self.into_core().is_multicast()
}
#[inline]
pub const fn is_loopback(&self) -> bool {
self.into_core().is_loopback()
}
#[inline]
pub const fn is_unicast(&self) -> bool {
!self.is_multicast()
}
#[inline]
pub const fn is_unicast_link_local(&self) -> bool {
(self.segments()[0] & 0xffc0) == 0xfe80
}
#[inline]
pub const fn is_unique_local(&self) -> bool {
(self.segments()[0] & 0xfe00) == 0xfc00
}
#[inline]
pub const fn is_unicast_global(&self) -> bool {
!self.is_multicast()
&& !self.is_unicast_link_local()
&& !self.is_unique_local()
}
#[inline]
pub const fn is_documentation(&self) -> bool {
let segments = self.segments();
(segments[0] == 0x2001) && (segments[1] == 0xdb8)
}
}
impl From<core::net::Ipv6Addr> for Ipv6Addr {
#[inline]
fn from(ip6: core::net::Ipv6Addr) -> Self {
Self { inner: ip6.octets() }
}
}
impl From<Ipv6Addr> for core::net::Ipv6Addr {
#[inline]
fn from(ip6: Ipv6Addr) -> Self {
Self::from(ip6.inner)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn ipv4() {
let addr = Ipv4Addr::from_octets([192, 168, 1, 1]);
assert!(addr.is_private());
assert!(!addr.is_global());
assert!(!addr.is_multicast());
assert!(!addr.is_broadcast());
assert!(!addr.is_loopback());
assert!(addr.is_unicast());
assert!(!addr.is_link_local());
assert!(!addr.is_documentation());
assert!(!addr.is_reserved());
}
#[test]
fn ipv4_broadcast() {
let addr = Ipv4Addr::from_octets([255, 255, 255, 255]);
assert!(!addr.is_private());
assert!(!addr.is_global());
assert!(!addr.is_multicast());
assert!(addr.is_broadcast());
assert!(!addr.is_unicast());
assert!(!addr.is_loopback());
assert!(!addr.is_link_local());
assert!(!addr.is_documentation());
assert!(!addr.is_reserved());
}
#[test]
fn ipv4_loopback() {
let addr = Ipv4Addr::from_octets([127, 0, 0, 1]);
assert!(!addr.is_private());
assert!(!addr.is_global());
assert!(!addr.is_multicast());
assert!(!addr.is_broadcast());
assert!(addr.is_loopback());
assert!(addr.is_unicast());
assert!(!addr.is_link_local());
assert!(!addr.is_documentation());
assert!(!addr.is_reserved());
}
#[test]
fn ipv6() {
let addr = Ipv6Addr::from_octets([
0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
]);
assert!(!addr.is_multicast());
assert!(addr.is_unicast());
assert!(!addr.is_unicast_link_local());
assert!(!addr.is_unique_local());
assert!(addr.is_documentation());
assert!(addr.is_unicast_global());
}
#[test]
fn ipv6_link_local() {
let addr = Ipv6Addr::from_octets([
0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xde, 0xad, 0xbe, 0xef,
]);
assert!(!addr.is_multicast());
assert!(addr.is_unicast());
assert!(addr.is_unicast_link_local());
assert!(!addr.is_unique_local());
assert!(!addr.is_documentation());
assert!(!addr.is_unicast_global());
}
}