diff --git a/src/wire/ip.rs b/src/wire/ip.rs index 20071e0f..6469e487 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -184,6 +184,34 @@ impl Address { &Address::__Nonexhaustive => unreachable!() } } + + /// If `self` is a CIDR-compatible subnet mask, return `Some(prefix_len)`, + /// where `prefix_len` is the number of leading zeroes. Return `None` otherwise. + pub fn to_prefix_len(&self) -> Option { + let mut ones = true; + let mut prefix_len = 0; + for byte in self.as_bytes() { + let mut mask = 0x80; + for _ in 0..8 { + let one = *byte & mask != 0; + if ones { + // Expect 1s until first 0 + if one { + prefix_len += 1; + } else { + ones = false; + } + } else { + if one { + // 1 where 0 was expected + return None + } + } + mask >>= 1; + } + } + Some(prefix_len) + } } #[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))] @@ -1074,4 +1102,73 @@ pub(crate) mod test { fn endpoint_unspecified() { assert!(!Endpoint::UNSPECIFIED.is_specified()); } + + #[test] + #[cfg(feature = "proto-ipv4")] + fn to_prefix_len_ipv4() { + fn test_eq>(prefix_len: u8, mask: A) { + assert_eq!( + Some(prefix_len), + mask.into().to_prefix_len() + ); + } + + test_eq(0, Ipv4Address::new(0, 0, 0, 0)); + test_eq(1, Ipv4Address::new(128, 0, 0, 0)); + test_eq(2, Ipv4Address::new(192, 0, 0, 0)); + test_eq(3, Ipv4Address::new(224, 0, 0, 0)); + test_eq(4, Ipv4Address::new(240, 0, 0, 0)); + test_eq(5, Ipv4Address::new(248, 0, 0, 0)); + test_eq(6, Ipv4Address::new(252, 0, 0, 0)); + test_eq(7, Ipv4Address::new(254, 0, 0, 0)); + test_eq(8, Ipv4Address::new(255, 0, 0, 0)); + test_eq(9, Ipv4Address::new(255, 128, 0, 0)); + test_eq(10, Ipv4Address::new(255, 192, 0, 0)); + test_eq(11, Ipv4Address::new(255, 224, 0, 0)); + test_eq(12, Ipv4Address::new(255, 240, 0, 0)); + test_eq(13, Ipv4Address::new(255, 248, 0, 0)); + test_eq(14, Ipv4Address::new(255, 252, 0, 0)); + test_eq(15, Ipv4Address::new(255, 254, 0, 0)); + test_eq(16, Ipv4Address::new(255, 255, 0, 0)); + test_eq(17, Ipv4Address::new(255, 255, 128, 0)); + test_eq(18, Ipv4Address::new(255, 255, 192, 0)); + test_eq(19, Ipv4Address::new(255, 255, 224, 0)); + test_eq(20, Ipv4Address::new(255, 255, 240, 0)); + test_eq(21, Ipv4Address::new(255, 255, 248, 0)); + test_eq(22, Ipv4Address::new(255, 255, 252, 0)); + test_eq(23, Ipv4Address::new(255, 255, 254, 0)); + test_eq(24, Ipv4Address::new(255, 255, 255, 0)); + test_eq(25, Ipv4Address::new(255, 255, 255, 128)); + test_eq(26, Ipv4Address::new(255, 255, 255, 192)); + test_eq(27, Ipv4Address::new(255, 255, 255, 224)); + test_eq(28, Ipv4Address::new(255, 255, 255, 240)); + test_eq(29, Ipv4Address::new(255, 255, 255, 248)); + test_eq(30, Ipv4Address::new(255, 255, 255, 252)); + test_eq(31, Ipv4Address::new(255, 255, 255, 254)); + test_eq(32, Ipv4Address::new(255, 255, 255, 255)); + } + + #[cfg(feature = "proto-ipv4")] + fn to_prefix_len_ipv4_error() { + assert_eq!(None, IpAddress::from(Ipv4Address::new(255,255,255,1)).to_prefix_len()); + } + + #[test] + #[cfg(feature = "proto-ipv6")] + fn to_prefix_len_ipv6() { + fn test_eq>(prefix_len: u8, mask: A) { + assert_eq!( + Some(prefix_len), + mask.into().to_prefix_len() + ); + } + + test_eq(0, Ipv6Address::new(0, 0, 0, 0, 0, 0, 0, 0)); + test_eq(128, Ipv6Address::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff)); + } + + #[cfg(feature = "proto-ipv6")] + fn to_prefix_len_ipv6_error() { + assert_eq!(None, IpAddress::from(Ipv6Address::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0, 1)).to_prefix_len()); + } }