From bbe8a8788c9ad328b20813678952524e5bd27370 Mon Sep 17 00:00:00 2001 From: Koen Date: Thu, 17 Oct 2024 16:28:33 +0200 Subject: [PATCH] Add function to turn AddressRange into set of prefixes --- src/repository/resources/ipres.rs | 50 ++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/repository/resources/ipres.rs b/src/repository/resources/ipres.rs index 1874342..6a62102 100644 --- a/src/repository/resources/ipres.rs +++ b/src/repository/resources/ipres.rs @@ -12,6 +12,7 @@ use std::fmt::Display; use std::net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr}; use std::num::ParseIntError; use std::str::FromStr; +use std::cmp; use bcder::{decode, encode}; use bcder::{BitString, Mode, OctetString, Tag}; use bcder::decode::{ContentError, DecodeError}; @@ -1037,6 +1038,41 @@ impl AddressRange { } } + /// Convert a range into a set of prefixes + /// + /// Algorithm is based on that by ARIN + pub fn to_prefixes(self) -> Vec { + let mut start = self.min.to_bits(); + let end = self.max.to_bits(); + + let mut prefixes: Vec = vec![]; + + loop { + let addr_host_bits = start.trailing_zeros(); + let mut max_allowed = 128 - (start ^ end).leading_zeros(); + if end.trailing_ones() < max_allowed { + max_allowed -= 1; + } + + let same_bits = cmp::min(addr_host_bits, max_allowed); + let prefix_len = 128 - same_bits; + + assert!(prefix_len <= 128); + let prefix = Prefix::new(Addr::from(start), prefix_len as u8); + prefixes.push(prefix); + + let new_start = start + 2_u128.pow(same_bits); + + if new_start - 1 >= end { + break; + } + + start = new_start; + } + + prefixes + } + /// Formats the range as an IPv4 range. pub fn fmt_v4(self, f: &mut fmt::Formatter) -> fmt::Result { let min = self.min.to_v4(); @@ -2207,7 +2243,7 @@ impl From for VerificationError { //============ Tests ========================================================= #[cfg(test)] -mod test { +mod tests { use bcder::encode::Values; use super::*; @@ -2658,6 +2694,18 @@ mod test { 0x123f_ffff_ffff_ffff_ffff_ffff_ffff_ffff ); } + + #[test] + fn to_prefixes() { + let range = AddressRange::new( + Addr::from(0x0000_0000_0000_0000_0000_0000_0000_0001), + Addr::from(0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFE) + ); + + let prefixes = range.to_prefixes(); + + assert_eq!(254, prefixes.len()) + } } #[cfg(all(test, feature="compat"))]