ipv6: address scope and is_global_unicast

The scope of an address is used when selecting the source IPv6 address
based on the destination address. As the scope is then also used for
unicast address, I made the scope only public in the crate instead of
outside the crate. Not sure if this should be public or not.

This commit also adds the `is_global_unicast` query function for IPv6
addresses.
This commit is contained in:
Thibaut Vandervelden 2023-11-30 16:00:38 +01:00
parent b310e3c366
commit 45f9838ad9
2 changed files with 76 additions and 0 deletions

View File

@ -25,6 +25,41 @@ pub const ADDR_SIZE: usize = 16;
/// [RFC 8200 § 2]: https://www.rfc-editor.org/rfc/rfc4291#section-2
pub const IPV4_MAPPED_PREFIX_SIZE: usize = ADDR_SIZE - 4; // 4 == ipv4::ADDR_SIZE , cannot DRY here because of dependency on a IPv4 module which is behind the feature
/// The [scope] of an address.
///
/// [scope]: https://www.rfc-editor.org/rfc/rfc4291#section-2.7
#[repr(u8)]
pub(crate) enum Scope {
/// Interface Local scope
InterfaceLocal = 0x1,
/// Link local scope
LinkLocal = 0x2,
/// Administratively configured
AdminLocal = 0x4,
/// Single site scope
SiteLocal = 0x5,
/// Organization scope
OrganizationLocal = 0x8,
/// Global scope
Global = 0xE,
/// Unknown scope
Unknown = 0xFF,
}
impl From<u8> for Scope {
fn from(value: u8) -> Self {
match value {
0x1 => Self::InterfaceLocal,
0x2 => Self::LinkLocal,
0x4 => Self::AdminLocal,
0x5 => Self::SiteLocal,
0x8 => Self::OrganizationLocal,
0xE => Self::Global,
_ => Self::Unknown,
}
}
}
/// A sixteen-octet IPv6 address.
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
pub struct Address(pub [u8; ADDR_SIZE]);
@ -143,6 +178,13 @@ impl Address {
!(self.is_multicast() || self.is_unspecified())
}
/// Query whether the IPv6 address is a [global unicast address].
///
/// [global unicast address]: https://datatracker.ietf.org/doc/html/rfc3587
pub const fn is_global_unicast(&self) -> bool {
(self.0[0] >> 5) == 0b001
}
/// Query whether the IPv6 address is a [multicast address].
///
/// [multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7
@ -228,6 +270,22 @@ impl Address {
])
}
/// Return the scope of the address.
pub(crate) fn scope(&self) -> Scope {
if self.is_multicast() {
return Scope::from(self.as_bytes()[1] & 0b1111);
}
if self.is_link_local() {
Scope::LinkLocal
} else if self.is_unique_local() || self.is_global_unicast() {
// ULA are considered global scope
Scope::Global
} else {
Scope::Unknown
}
}
/// Convert to an `IpAddress`.
///
/// Same as `.into()`, but works in `const`.
@ -840,6 +898,7 @@ mod test {
const LINK_LOCAL_ADDR: Address = Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
const UNIQUE_LOCAL_ADDR: Address = Address::new(0xfd00, 0, 0, 201, 1, 1, 1, 1);
const GLOBAL_UNICAST_ADDR: Address = Address::new(0x2001, 0xdb8, 0x3, 0, 0, 0, 0, 1);
#[test]
fn test_basic_multicast() {
@ -848,11 +907,13 @@ mod test {
assert!(!Address::LINK_LOCAL_ALL_ROUTERS.is_link_local());
assert!(!Address::LINK_LOCAL_ALL_ROUTERS.is_loopback());
assert!(!Address::LINK_LOCAL_ALL_ROUTERS.is_unique_local());
assert!(!Address::LINK_LOCAL_ALL_ROUTERS.is_global_unicast());
assert!(!Address::LINK_LOCAL_ALL_NODES.is_unspecified());
assert!(Address::LINK_LOCAL_ALL_NODES.is_multicast());
assert!(!Address::LINK_LOCAL_ALL_NODES.is_link_local());
assert!(!Address::LINK_LOCAL_ALL_NODES.is_loopback());
assert!(!Address::LINK_LOCAL_ALL_NODES.is_unique_local());
assert!(!Address::LINK_LOCAL_ALL_NODES.is_global_unicast());
}
#[test]
@ -862,6 +923,7 @@ mod test {
assert!(LINK_LOCAL_ADDR.is_link_local());
assert!(!LINK_LOCAL_ADDR.is_loopback());
assert!(!LINK_LOCAL_ADDR.is_unique_local());
assert!(!LINK_LOCAL_ADDR.is_global_unicast());
}
#[test]
@ -871,6 +933,7 @@ mod test {
assert!(!Address::LOOPBACK.is_link_local());
assert!(Address::LOOPBACK.is_loopback());
assert!(!Address::LOOPBACK.is_unique_local());
assert!(!Address::LOOPBACK.is_global_unicast());
}
#[test]
@ -880,6 +943,17 @@ mod test {
assert!(!UNIQUE_LOCAL_ADDR.is_link_local());
assert!(!UNIQUE_LOCAL_ADDR.is_loopback());
assert!(UNIQUE_LOCAL_ADDR.is_unique_local());
assert!(!UNIQUE_LOCAL_ADDR.is_global_unicast());
}
#[test]
fn test_global_unicast() {
assert!(!GLOBAL_UNICAST_ADDR.is_unspecified());
assert!(!GLOBAL_UNICAST_ADDR.is_multicast());
assert!(!GLOBAL_UNICAST_ADDR.is_link_local());
assert!(!GLOBAL_UNICAST_ADDR.is_loopback());
assert!(!GLOBAL_UNICAST_ADDR.is_unique_local());
assert!(GLOBAL_UNICAST_ADDR.is_global_unicast());
}
#[test]

View File

@ -190,6 +190,8 @@ pub use self::ipv4::{
Repr as Ipv4Repr, HEADER_LEN as IPV4_HEADER_LEN, MIN_MTU as IPV4_MIN_MTU,
};
#[cfg(feature = "proto-ipv6")]
pub(crate) use self::ipv6::Scope as Ipv6AddressScope;
#[cfg(feature = "proto-ipv6")]
pub use self::ipv6::{
Address as Ipv6Address, Cidr as Ipv6Cidr, Packet as Ipv6Packet, Repr as Ipv6Repr,