scion_sdk_address_manager/
allocator.rs1use std::net::IpAddr;
17
18use ipnet::IpNet;
19use rand::Rng as _;
20use rand_chacha::ChaCha8Rng;
21use thiserror::Error;
22
23use crate::allocator::addr_set::{AddrSet, AddrSetError};
24
25mod addr_set;
26pub(crate) mod dto;
27
28#[derive(Debug, Eq, PartialEq, Clone)]
34pub struct AddressAllocator {
35 v4: Vec<AddrSet>,
37 v6: Vec<AddrSet>,
39 rng: ChaCha8Rng,
41}
42
43#[derive(Debug, Error)]
45pub enum AllocatorCreationError {
46 #[error("prefixes containing more than 2^128 addresses are not supported")]
48 PrefixesTooLarge,
49}
50
51impl AddressAllocator {
52 pub fn new(prefixes: Vec<IpNet>, rng: ChaCha8Rng) -> Result<Self, AllocatorCreationError> {
58 let mut prefixes = prefixes;
60 prefixes.sort_by_key(|prefix| prefix.network());
61 let aggregated = IpNet::aggregate(&prefixes);
63 if aggregated
65 .iter()
66 .map(|p| 2u128.pow(p.prefix_len() as u32))
67 .try_fold(0_u128, |acc, x| acc.checked_add(x))
68 .is_none()
69 {
70 return Err(AllocatorCreationError::PrefixesTooLarge);
71 }
72
73 let mut v4 = Vec::new();
74 let mut v6 = Vec::new();
75
76 for prefix in aggregated {
77 match prefix.network() {
78 IpAddr::V4(_) => v4.push(AddrSet::new(prefix)),
79 IpAddr::V6(_) => v6.push(AddrSet::new(prefix)),
80 }
81 }
82
83 Ok(Self { v4, v6, rng })
84 }
85
86 fn mut_address_set(&mut self, address: &IpAddr) -> Result<&mut AddrSet, AddressAllocatorError> {
88 let res = match address {
89 IpAddr::V4(_) => self.v4.iter_mut().find(|set| set.prefix.contains(address)),
90 IpAddr::V6(_) => self.v6.iter_mut().find(|set| set.prefix.contains(address)),
91 };
92
93 res.ok_or(AddressAllocatorError::AddressNotInPrefix(*address))
94 }
95
96 pub fn free(&mut self, address: IpAddr) -> Result<(), AddressAllocatorError> {
100 self.mut_address_set(&address)?.insert(address)?;
101 Ok(())
102 }
103
104 fn allocate_specific(&mut self, address: IpAddr) -> Result<(), AddressAllocatorError> {
108 self.mut_address_set(&address)?.remove(address)?;
109 Ok(())
110 }
111
112 pub fn is_free(&self, address: IpAddr) -> bool {
114 match address {
115 IpAddr::V4(_) => self.v4.iter().any(|set| set.contains(address)),
116 IpAddr::V6(_) => self.v6.iter().any(|set| set.contains(address)),
117 }
118 }
119
120 pub fn allocate(&mut self, requested: IpAddr) -> Result<IpAddr, AddressAllocatorError> {
123 if requested.is_unspecified() {
124 match requested {
125 IpAddr::V4(_) => {
126 if self.free_v4() == 0 {
127 return Err(AddressAllocatorError::NoAddressesAvailable);
128 }
129 let mut n = self.rng.random_range(0..self.free_v4());
130 for set in self.v4.iter_mut() {
131 if n < set.len() {
132 let addr = set.nth_free(n).expect("Checked above");
133 self.allocate_specific(addr)?;
134 return Ok(addr);
135 }
136 n -= set.len();
137 }
138 }
139 IpAddr::V6(_) => {
140 if self.free_v6() == 0 {
141 return Err(AddressAllocatorError::NoAddressesAvailable);
142 }
143 let mut n = self.rng.random_range(0..self.free_v6());
144 for set in self.v6.iter_mut() {
145 if n < set.len() {
146 let addr = set.nth_free(n).expect("Checked above");
147 self.allocate_specific(addr)?;
148 return Ok(addr);
149 }
150 n -= set.len();
151 }
152 }
153 }
154 Err(AddressAllocatorError::NoAddressesAvailable)
155 } else {
156 self.allocate_specific(requested)?;
157 Ok(requested)
158 }
159 }
160
161 pub(crate) fn free_v4(&self) -> u128 {
162 self.v4.iter().map(|set| set.len()).sum::<u128>()
163 }
164
165 pub(crate) fn free_v6(&self) -> u128 {
166 self.v6.iter().map(|set| set.len()).sum::<u128>()
167 }
168}
169
170#[derive(Debug, Error, PartialEq, Eq)]
172pub enum AddressAllocatorError {
173 #[error("requested address {0} not in allocation prefixes")]
175 AddressNotInPrefix(IpAddr),
176 #[error("address {0} already allocated")]
178 AddressAlreadyAllocated(IpAddr),
179 #[error("address is already freed")]
181 AddressAlreadyFreed(IpAddr),
182 #[error("no addresses available")]
184 NoAddressesAvailable,
185}
186
187impl From<AddrSetError> for AddressAllocatorError {
188 fn from(err: AddrSetError) -> Self {
189 match err {
190 AddrSetError::AddressNotInPrefix(addr) => {
191 AddressAllocatorError::AddressNotInPrefix(addr)
192 }
193 AddrSetError::AddressAlreadyInSet(addr) => {
194 AddressAllocatorError::AddressAlreadyFreed(addr)
195 }
196 AddrSetError::AddressNotInSet(addr) => {
197 AddressAllocatorError::AddressAlreadyAllocated(addr)
198 }
199 AddrSetError::WrongIPVersion(_addr) => {
201 unreachable!("Allocator always supports both IPv4 and IPv6")
202 }
203 }
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use std::{
210 net::{IpAddr, Ipv4Addr, Ipv6Addr},
211 str::FromStr,
212 };
213
214 use rand::SeedableRng;
215
216 use super::*;
217
218 fn check_allocator_invariants(allocator: &AddressAllocator) {
220 for sets in [&allocator.v4, &allocator.v6] {
222 for set in sets {
223 for i in 1..set.free.ranges().len() {
224 assert!(
226 set.free.ranges()[i - 1].start < set.free.ranges()[i].start,
227 "{:?} Interval not ordered: {:?} and {:?}",
228 set.prefix,
229 set.free.ranges()[i - 1],
230 set.free.ranges()[i]
231 );
232
233 assert!(
235 set.free.ranges()[i - 1].end < set.free.ranges()[i].start,
236 "{:?} Intervals not disjoint: {:?} and {:?}",
237 set.prefix,
238 set.free.ranges()[i - 1],
239 set.free.ranges()[i]
240 );
241 }
242
243 for interval in set.free.ranges() {
245 assert!(
246 interval.start < interval.end,
247 "{:?} Invalid interval: {:?}",
248 set.prefix,
249 interval
250 );
251 }
252 }
253 }
254 }
255
256 #[test]
257 fn test_allocate_random() {
258 let prefixes = vec![
259 "192.168.0.0/24".parse().unwrap(),
260 "10.0.0.0/16".parse().unwrap(),
261 "2001:db8::/64".parse().unwrap(),
262 ];
263
264 let mut expected_length = 2u128.pow(8) + 2u128.pow(16) + 2u128.pow(64);
265
266 let mut allocator = AddressAllocator::new(prefixes, ChaCha8Rng::seed_from_u64(42)).unwrap();
267 let mut allocated = Vec::new();
268 for _ in 0..1000 {
270 let addr = allocator
271 .allocate(IpAddr::V4(Ipv4Addr::UNSPECIFIED))
272 .expect("Failed to allocate");
273 check_allocator_invariants(&allocator);
274 assert!(!allocator.is_free(addr));
275 assert!(addr.is_ipv4());
276 expected_length -= 1;
277 assert_eq!(allocator.free_v4() + allocator.free_v6(), expected_length);
278 allocated.push(addr);
279 }
280 for _ in 0..1000 {
282 let addr = allocator
283 .allocate(IpAddr::V6(Ipv6Addr::UNSPECIFIED))
284 .expect("Failed to allocate");
285 check_allocator_invariants(&allocator);
286 assert!(!allocator.is_free(addr));
287 assert!(addr.is_ipv6());
288 expected_length -= 1;
289 assert_eq!(allocator.free_v4() + allocator.free_v6(), expected_length);
290 allocated.push(addr);
291 }
292 for addr in allocated {
294 allocator.free(addr).unwrap();
295 check_allocator_invariants(&allocator);
296 expected_length += 1;
297 assert_eq!(allocator.free_v4() + allocator.free_v6(), expected_length);
298 assert!(allocator.is_free(addr));
299 }
300
301 for set in &allocator.v4 {
303 assert_eq!(1, set.free.ranges().len());
304 }
305 for set in &allocator.v6 {
306 assert_eq!(1, set.free.ranges().len());
307 }
308 }
309
310 #[test]
311 fn test_allocate_boundary() {
312 let testcases = vec![
313 (
315 "192.168.0.0/24".parse().unwrap(),
317 IpAddr::from_str("192.167.255.255").unwrap(),
319 IpAddr::from_str("192.168.1.0").unwrap(),
321 ),
322 (
324 "2001:db8::/64".parse().unwrap(),
326 IpAddr::from_str("2001:db7:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
328 IpAddr::from_str("2001:db9::0").unwrap(),
330 ),
331 ];
332
333 for (prefix, lower, upper) in testcases {
334 let mut allocator =
335 AddressAllocator::new(vec![prefix], ChaCha8Rng::seed_from_u64(42)).unwrap();
336
337 let fail = allocator
339 .allocate(lower)
340 .expect_err("Should not be able to allocate");
341 assert!(matches!(fail, AddressAllocatorError::AddressNotInPrefix(_)),);
342
343 let addr = allocator
344 .allocate(prefix.network())
345 .expect("Failed to allocate");
346 assert!(!allocator.is_free(addr));
347 assert!(std::mem::discriminant(&addr) == std::mem::discriminant(&prefix.network()));
348
349 let fail = allocator
350 .allocate(prefix.network())
351 .expect_err("Should not be able to allocate");
352 assert!(matches!(
353 fail,
354 AddressAllocatorError::AddressAlreadyAllocated(_)
355 ));
356
357 let fail = allocator
359 .allocate(upper)
360 .expect_err("Should not be able to allocate");
361 assert!(matches!(fail, AddressAllocatorError::AddressNotInPrefix(_)));
362
363 let addr = allocator
364 .allocate(prefix.broadcast())
365 .expect("Failed to allocate");
366 assert!(!allocator.is_free(addr));
367 assert!(std::mem::discriminant(&addr) == std::mem::discriminant(&prefix.network()));
368
369 let fail = allocator
370 .allocate(prefix.broadcast())
371 .expect_err("Should not be able to allocate");
372 assert!(matches!(
373 fail,
374 AddressAllocatorError::AddressAlreadyAllocated(_)
375 ));
376 }
377 }
378
379 #[test]
380 fn test_allocate_full() {
381 let mut allocator = AddressAllocator::new(
382 vec![
383 "192.168.0.0/31".parse().unwrap(),
384 "192.168.0.2/31".parse().unwrap(),
385 ],
386 ChaCha8Rng::seed_from_u64(42),
387 )
388 .unwrap();
389 for _ in 0..4 {
390 let addr = allocator
391 .allocate(IpAddr::V4(Ipv4Addr::UNSPECIFIED))
392 .expect("Failed to allocate");
393 assert!(addr.is_ipv4());
394 check_allocator_invariants(&allocator);
395 }
396 assert_eq!(allocator.free_v4(), 0);
397 let fail = allocator
398 .allocate(IpAddr::V4(Ipv4Addr::UNSPECIFIED))
399 .expect_err("Should not be able to allocate");
400 assert!(matches!(fail, AddressAllocatorError::NoAddressesAvailable));
401 }
402}