1use crate::node::{BaseNode, IPNode};
2use crate::utils::{canonicalize_ipnet, ipnet_to_bits};
3use ipnet::IpNet;
4use std::collections::HashSet;
5
6#[derive(Debug, Clone)]
7pub struct IpRadixTree {
8 pub root: IPNode,
9 pub acl_mode: bool,
10}
11
12impl IpRadixTree {
13 pub fn new(acl_mode: bool) -> Self {
14 IpRadixTree {
15 root: IPNode::new(),
16 acl_mode,
17 }
18 }
19 pub fn insert(&mut self, network: IpNet) -> Option<String> {
20 let canonical = canonicalize_ipnet(&network);
21
22 if self.acl_mode && self.get(&canonical).is_some() {
24 return None; }
26
27 let mut node = &mut self.root;
28 let bits = ipnet_to_bits(&canonical);
29 for &bit in &bits {
30 node = node
31 .children
32 .entry(u64::from(bit))
33 .or_insert_with(|| Box::new(IPNode::new()));
34 }
35 node.network = Some(canonical);
36
37 if self.acl_mode {
39 node.clear();
40 }
41
42 Some(canonical.to_string())
43 }
44 pub fn get(&self, net: &IpNet) -> Option<String> {
45 let canonical = canonicalize_ipnet(net);
46 let mut node = &self.root;
47 let bits = ipnet_to_bits(&canonical);
48 let mut best: Option<&IpNet> = None;
49 if let Some(n) = &node.network
50 && n.contains(&canonical.network())
51 && n.prefix_len() <= canonical.prefix_len()
52 {
53 best = Some(n);
54 }
55 for &bit in &bits {
56 if let Some(child) = node.children.get(&u64::from(bit)) {
57 node = child;
58 if let Some(n) = &node.network
59 && n.contains(&canonical.network())
60 && n.prefix_len() <= canonical.prefix_len()
61 {
62 best = Some(n);
63 }
64 } else {
65 break;
66 }
67 }
68 best.map(|n| n.to_string())
69 }
70 pub fn delete(&mut self, network: IpNet) -> bool {
71 let canonical = canonicalize_ipnet(&network);
72 Self::delete_rec(&mut self.root, &ipnet_to_bits(&canonical), 0, &canonical)
73 }
74 fn delete_rec(node: &mut IPNode, bits: &[u8], depth: usize, network: &IpNet) -> bool {
75 if depth == bits.len() {
76 if node.network.as_ref() == Some(network) {
77 node.network = None;
78 return true;
79 }
80 return false;
81 }
82 let bit = bits[depth];
83 if let Some(child) = node.children.get_mut(&u64::from(bit)) {
84 let deleted = Self::delete_rec(child, bits, depth + 1, network);
85 if child.children.is_empty() && child.network.is_none() {
86 node.children.remove(&u64::from(bit));
87 }
88 return deleted;
89 }
90 false
91 }
92 pub fn prune(&mut self) -> usize {
93 self.root.prune()
94 }
95
96 pub fn hosts(&self) -> HashSet<String> {
98 self.root.all_hosts()
99 }
100 pub fn defrag(&mut self) -> (HashSet<String>, HashSet<String>) {
102 let mut cleaned = HashSet::new();
103 let mut new = HashSet::new();
104 loop {
105 let pairs = self.root.mergeable_pairs();
106 if pairs.is_empty() {
107 break;
108 }
109 for (left_net, right_net, supernet) in pairs {
110 cleaned.insert(left_net.to_string());
111 cleaned.insert(right_net.to_string());
112 new.insert(supernet.to_string());
113 self.delete(left_net);
114 self.delete(right_net);
115 self.insert(supernet);
116 }
117 }
118 let overlap: HashSet<_> = cleaned.intersection(&new).cloned().collect();
120 for k in &overlap {
121 cleaned.remove(k);
122 new.remove(k);
123 }
124 (cleaned, new)
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131 use crate::utils::{canonicalize_ipnet, ipnet_to_bits};
132 use ipnet::IpNet;
133 use std::str::FromStr;
134
135 #[test]
138 fn test_insert_and_get_ipv4_and_ipv6() {
139 let mut tree = IpRadixTree::new(false);
140 let net_v4 = IpNet::from_str("8.8.8.0/24").unwrap();
141 let net_v6 = IpNet::from_str("dead::/64").unwrap();
142 let hash_v4 = tree.insert(net_v4);
143 assert_eq!(hash_v4, Some(net_v4.to_string()), "insert(8.8.8.0/24) hash");
144 let hash_v6 = tree.insert(net_v6);
145 assert_eq!(hash_v6, Some(net_v6.to_string()), "insert(dead::/64) hash");
146 assert_eq!(
147 tree.get(&IpNet::from_str("8.8.8.8/32").unwrap()),
148 Some(net_v4.to_string())
149 );
150 assert_eq!(
151 tree.get(&IpNet::from_str("dead::beef/128").unwrap()),
152 Some(net_v6.to_string())
153 );
154 assert_eq!(tree.get(&IpNet::from_str("1.1.1.1/32").unwrap()), None);
155 assert_eq!(tree.get(&IpNet::from_str("cafe::beef/128").unwrap()), None);
156 }
157
158 #[test]
161 fn test_insert_network_with_host_bits() {
162 let mut tree = IpRadixTree::new(false);
163 let net = IpNet::from_str("192.168.1.0/24").unwrap();
165 let hash = tree.insert(net);
166 assert_eq!(hash, Some(net.to_string()), "insert(192.168.1.0/24) hash");
167 assert_eq!(
168 tree.get(&IpNet::from_str("192.168.1.1/32").unwrap()),
169 Some(net.to_string())
170 );
171 assert_eq!(tree.get(&IpNet::from_str("192.168.2.1/32").unwrap()), None);
172 }
173
174 #[test]
177 fn test_insert_single_ip() {
178 let mut tree = IpRadixTree::new(false);
179 let net = IpNet::from_str("10.0.0.1/32").unwrap();
180 let hash = tree.insert(net);
181 assert_eq!(hash, Some(net.to_string()), "insert(10.0.0.1/32) hash");
182 assert_eq!(
183 tree.get(&IpNet::from_str("10.0.0.1/32").unwrap()),
184 Some(net.to_string())
185 );
186 assert_eq!(tree.get(&IpNet::from_str("10.0.0.2/32").unwrap()), None);
187 }
188
189 #[test]
192 fn test_overlapping_and_longest_prefix() {
193 let mut tree = IpRadixTree::new(false);
194 let net1 = IpNet::from_str("10.0.0.0/8").unwrap();
195 let net2 = IpNet::from_str("10.1.0.0/16").unwrap();
196 let net3 = IpNet::from_str("10.1.2.0/24").unwrap();
197 let hash3 = tree.insert(net3);
198 assert_eq!(hash3, Some(net3.to_string()), "insert(10.1.2.0/24) hash");
199 let hash2 = tree.insert(net2);
200 assert_eq!(hash2, Some(net2.to_string()), "insert(10.1.0.0/16) hash");
201 let hash1 = tree.insert(net1);
202 assert_eq!(hash1, Some(net1.to_string()), "insert(10.0.0.0/8) hash");
203 assert_eq!(
204 tree.get(&IpNet::from_str("10.1.2.3/32").unwrap()),
205 Some(net3.to_string())
206 );
207 assert_eq!(
208 tree.get(&IpNet::from_str("10.1.2.3/30").unwrap()),
209 Some(net3.to_string())
210 );
211 assert_eq!(
212 tree.get(&IpNet::from_str("10.1.3.3/32").unwrap()),
213 Some(net2.to_string())
214 );
215 assert_eq!(
216 tree.get(&IpNet::from_str("10.1.3.3/30").unwrap()),
217 Some(net2.to_string())
218 );
219 assert_eq!(
220 tree.get(&IpNet::from_str("10.2.2.2/32").unwrap()),
221 Some(net1.to_string())
222 );
223 assert_eq!(
224 tree.get(&IpNet::from_str("10.2.2.2/30").unwrap()),
225 Some(net1.to_string())
226 );
227 }
228
229 #[test]
232 fn test_ipv6_longest_prefix() {
233 let mut tree = IpRadixTree::new(false);
234 let net1 = IpNet::from_str("2001:db8::/32").unwrap();
236 let net2 = IpNet::from_str("2001:db8:abcd::/48").unwrap();
237 let net3 = IpNet::from_str("2001:db8:abcd:1234::/64").unwrap();
238 let net4 = IpNet::from_str("2001:db8:abcd:1234:5678::/80").unwrap();
239 let hash4 = tree.insert(IpNet::from_str("2001:db8:abcd:1234:5678:9abc::1/80").unwrap()); assert_eq!(
242 hash4,
243 Some(net4.to_string()),
244 "insert(2001:db8:abcd:1234:5678::/80) hash"
245 );
246 let hash3 = tree.insert(IpNet::from_str("2001:db8:abcd:1234::/64").unwrap()); assert_eq!(
248 hash3,
249 Some(net3.to_string()),
250 "insert(2001:db8:abcd:1234::/64) hash"
251 );
252 let hash2 = tree.insert(IpNet::from_str("2001:db8:abcd::/48").unwrap()); assert_eq!(
254 hash2,
255 Some(net2.to_string()),
256 "insert(2001:db8:abcd::/48) hash"
257 );
258 let hash1 = tree.insert(IpNet::from_str("2001:db8::1/32").unwrap()); assert_eq!(hash1, Some(net1.to_string()), "insert(2001:db8::/32) hash");
260
261 assert_eq!(
264 tree.get(&IpNet::from_str("2001:db8:abcd:1234:5678:9abc::dead/128").unwrap()),
265 Some(net4.to_string())
266 );
267 assert_eq!(
269 tree.get(&IpNet::from_str("2001:db8:abcd:1234::beef/128").unwrap()),
270 Some(net3.to_string())
271 );
272 assert_eq!(
274 tree.get(&IpNet::from_str("2001:db8:abcd::cafe/128").unwrap()),
275 Some(net2.to_string())
276 );
277 assert_eq!(
279 tree.get(&IpNet::from_str("2001:db8::1234/128").unwrap()),
280 Some(net1.to_string())
281 );
282
283 assert_eq!(
285 tree.get(&IpNet::from_str("2001:db8:abcd:1234:5678:9abc::dead/81").unwrap()),
286 Some(net4.to_string())
287 );
288 assert_eq!(
289 tree.get(&IpNet::from_str("2001:db8:abcd:1234::beef/65").unwrap()),
290 Some(net3.to_string())
291 );
292 assert_eq!(
293 tree.get(&IpNet::from_str("2001:db8:abcd::cafe/49").unwrap()),
294 Some(net2.to_string())
295 );
296 assert_eq!(
297 tree.get(&IpNet::from_str("2001:db8::1234/33").unwrap()),
298 Some(net1.to_string())
299 );
300
301 assert_eq!(
303 tree.get(&IpNet::from_str("2001:dead::1/128").unwrap()),
304 None
305 );
306 assert_eq!(tree.get(&IpNet::from_str("3000::/32").unwrap()), None);
307 }
308
309 #[test]
312 fn test_deletion_and_querying() {
313 let mut tree = IpRadixTree::new(false);
314 let net1 = IpNet::from_str("192.168.0.0/16").unwrap();
316 let net2 = IpNet::from_str("192.168.1.0/24").unwrap();
317 let net3 = IpNet::from_str("192.168.1.128/25").unwrap();
318 let hash1 = tree.insert(net1);
319 assert_eq!(hash1, Some(net1.to_string()), "insert(192.168.0.0/16) hash");
320 let hash2 = tree.insert(net2);
321 assert_eq!(hash2, Some(net2.to_string()), "insert(192.168.1.0/24) hash");
322 let hash3 = tree.insert(net3);
323 assert_eq!(
324 hash3,
325 Some(net3.to_string()),
326 "insert(192.168.1.128/25) hash"
327 );
328 assert_eq!(
330 tree.get(&IpNet::from_str("192.168.1.128/25").unwrap()),
331 Some(net3.to_string())
332 ); assert_eq!(
334 tree.get(&IpNet::from_str("192.168.1.129/32").unwrap()),
335 Some(net3.to_string())
336 );
337 assert_eq!(
338 tree.get(&IpNet::from_str("192.168.1.0/24").unwrap()),
339 Some(net2.to_string())
340 );
341 assert!(tree.delete(IpNet::from_str("192.168.1.128/25").unwrap()));
343 assert_eq!(
345 tree.get(&IpNet::from_str("192.168.1.128/25").unwrap()),
346 Some(net2.to_string())
347 );
348 assert_eq!(
349 tree.get(&IpNet::from_str("192.168.1.129/32").unwrap()),
350 Some(net2.to_string())
351 );
352 assert!(tree.delete(IpNet::from_str("192.168.1.0/24").unwrap()));
354 assert_eq!(
356 tree.get(&IpNet::from_str("192.168.1.129/32").unwrap()),
357 Some(net1.to_string())
358 );
359 assert!(tree.delete(IpNet::from_str("192.168.0.0/16").unwrap()));
361 assert_eq!(
363 tree.get(&IpNet::from_str("192.168.1.129/32").unwrap()),
364 None
365 );
366
367 let netv6_1 = IpNet::from_str("2001:db8::/32").unwrap();
369 let netv6_2 = IpNet::from_str("2001:db8:abcd::/48").unwrap();
370 let netv6_3 = IpNet::from_str("2001:db8:abcd:1234::/64").unwrap();
371 let hashv6_1 = tree.insert(netv6_1);
372 assert_eq!(
373 hashv6_1,
374 Some(netv6_1.to_string()),
375 "insert(2001:db8::/32) hash"
376 );
377 let hashv6_2 = tree.insert(netv6_2);
378 assert_eq!(
379 hashv6_2,
380 Some(netv6_2.to_string()),
381 "insert(2001:db8:abcd::/48) hash"
382 );
383 let hashv6_3 = tree.insert(netv6_3);
384 assert_eq!(
385 hashv6_3,
386 Some(netv6_3.to_string()),
387 "insert(2001:db8:abcd:1234::/64) hash"
388 );
389 assert_eq!(
391 tree.get(&IpNet::from_str("2001:db8:abcd:1234::/64").unwrap()),
392 Some(netv6_3.to_string())
393 ); assert_eq!(
395 tree.get(&IpNet::from_str("2001:db8:abcd::/48").unwrap()),
396 Some(netv6_2.to_string())
397 ); assert_eq!(
399 tree.get(&IpNet::from_str("2001:db8::/32").unwrap()),
400 Some(netv6_1.to_string())
401 ); assert!(tree.delete(IpNet::from_str("2001:db8:abcd:1234::/64").unwrap()));
404 assert_eq!(
405 tree.get(&IpNet::from_str("2001:db8:abcd:1234::/64").unwrap()),
406 Some(netv6_2.to_string())
407 );
408 assert!(tree.delete(IpNet::from_str("2001:db8:abcd::/48").unwrap()));
410 assert_eq!(
411 tree.get(&IpNet::from_str("2001:db8:abcd:1234::/64").unwrap()),
412 Some(netv6_1.to_string())
413 );
414 assert!(tree.delete(IpNet::from_str("2001:db8::/32").unwrap()));
416 assert_eq!(
417 tree.get(&IpNet::from_str("2001:db8:abcd:1234::/64").unwrap()),
418 None
419 );
420 }
421
422 #[test]
425 fn test_insert_with_custom_data_types() {
426 let mut tree = IpRadixTree::new(false);
427 let net = IpNet::from_str("8.8.8.0/24").unwrap();
428 let hash = tree.insert(net);
429 assert_eq!(hash, Some(net.to_string()), "insert(8.8.8.0/24) hash");
430 assert_eq!(
431 tree.get(&IpNet::from_str("8.8.8.8/32").unwrap()),
432 Some(net.to_string())
433 );
434 }
435
436 #[test]
439 fn test_insert_and_get_zero_prefix() {
440 let mut tree = IpRadixTree::new(false);
441 let net = IpNet::from_str("0.0.0.0/0").unwrap();
442 let hash = tree.insert(net);
443 assert_eq!(hash, Some(net.to_string()), "insert(0.0.0.0/0) hash");
444 assert_eq!(
445 tree.get(&IpNet::from_str("1.2.3.4/32").unwrap()),
446 Some(net.to_string())
447 );
448 assert_eq!(
449 tree.get(&IpNet::from_str("255.255.255.255/32").unwrap()),
450 Some(net.to_string())
451 );
452 }
453
454 #[test]
457 fn test_insert_and_get_ipv6_zero_prefix() {
458 let mut tree = IpRadixTree::new(false);
459 let net = IpNet::from_str("::/0").unwrap();
460 let hash = tree.insert(net);
461 assert_eq!(hash, Some(net.to_string()), "insert(::/0) hash");
462 assert_eq!(
463 tree.get(&IpNet::from_str("::1/128").unwrap()),
464 Some(net.to_string())
465 );
466 assert_eq!(
467 tree.get(&IpNet::from_str("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128").unwrap()),
468 Some(net.to_string())
469 );
470 }
471
472 #[test]
475 fn test_insert_and_get_multiple_types() {
476 let mut tree = IpRadixTree::new(false);
477 let net4 = IpNet::from_str("1.2.3.4/32").unwrap();
478 let net6 = IpNet::from_str("dead::beef/128").unwrap();
479 let hash4 = tree.insert(net4);
480 assert_eq!(hash4, Some(net4.to_string()), "insert(1.2.3.4/32) hash");
481 let hash6 = tree.insert(net6);
482 assert_eq!(hash6, Some(net6.to_string()), "insert(dead::beef/128) hash");
483 assert_eq!(
484 tree.get(&IpNet::from_str("1.2.3.4/32").unwrap()),
485 Some(net4.to_string())
486 );
487 assert_eq!(
488 tree.get(&IpNet::from_str("dead::beef/128").unwrap()),
489 Some(net6.to_string())
490 );
491 }
492
493 #[test]
496 fn test_insert_and_get_with_host_bits_ipv6() {
497 let mut tree = IpRadixTree::new(false);
498 let inserted = IpNet::from_str("dead:beef::1/120").unwrap();
500 let normalized = IpNet::from_str("dead:beef::0/120").unwrap();
501 let hash = tree.insert(inserted);
502 assert_eq!(
503 hash,
504 Some(normalized.to_string()),
505 "insert(dead:beef::0/120) hash"
506 );
507 assert_eq!(
509 tree.get(&IpNet::from_str("dead:beef::2/128").unwrap()),
510 Some(normalized.to_string())
511 );
512 assert_eq!(
513 tree.get(&IpNet::from_str("dead:beef::ff/128").unwrap()),
514 Some(normalized.to_string())
515 );
516 assert_eq!(
518 tree.get(&IpNet::from_str("dead:beef::2/121").unwrap()),
519 Some(normalized.to_string())
520 );
521 assert_eq!(
523 tree.get(&IpNet::from_str("dead:beef::100/128").unwrap()),
524 None
525 );
526 assert!(tree.delete(IpNet::from_str("dead:beef::ff/120").unwrap()));
528 assert_eq!(
530 tree.get(&IpNet::from_str("dead:beef::2/128").unwrap()),
531 None
532 );
533 assert_eq!(
534 tree.get(&IpNet::from_str("dead:beef::ff/128").unwrap()),
535 None
536 );
537 assert_eq!(
538 tree.get(&IpNet::from_str("dead:beef::2/121").unwrap()),
539 None
540 );
541 }
542
543 #[test]
546 fn test_query_by_network() {
547 let mut tree = IpRadixTree::new(false);
548 let net_v4 = IpNet::from_str("8.8.8.0/24").unwrap();
549 let net_v6 = IpNet::from_str("dead::/64").unwrap();
550 let hash_v4 = tree.insert(net_v4);
551 assert_eq!(hash_v4, Some(net_v4.to_string()), "insert(8.8.8.0/24) hash");
552 let hash_v6 = tree.insert(net_v6);
553 assert_eq!(hash_v6, Some(net_v6.to_string()), "insert(dead::/64) hash");
554 assert_eq!(
556 tree.get(&IpNet::from_str("8.8.8.0/24").unwrap()),
557 Some(net_v4.to_string())
558 );
559 assert_eq!(
560 tree.get(&IpNet::from_str("dead::/64").unwrap()),
561 Some(net_v6.to_string())
562 );
563 assert_eq!(tree.get(&IpNet::from_str("8.8.9.0/24").unwrap()), None);
565 assert_eq!(tree.get(&IpNet::from_str("cafe::/64").unwrap()), None);
566 }
567
568 #[test]
570 fn test_query_by_overlapping_networks() {
571 let mut tree = IpRadixTree::new(false);
572 let net1 = IpNet::from_str("10.0.0.0/8").unwrap();
573 let net2 = IpNet::from_str("10.1.0.0/16").unwrap();
574 let net3 = IpNet::from_str("10.1.2.0/24").unwrap();
575 let hash1 = tree.insert(net1);
576 assert_eq!(hash1, Some(net1.to_string()), "insert(10.0.0.0/8) hash");
577 let hash2 = tree.insert(net2);
578 assert_eq!(hash2, Some(net2.to_string()), "insert(10.1.0.0/16) hash");
579 let hash3 = tree.insert(net3);
580 assert_eq!(hash3, Some(net3.to_string()), "insert(10.1.2.0/24) hash");
581 assert_eq!(
583 tree.get(&IpNet::from_str("10.1.2.0/24").unwrap()),
584 Some(net3.to_string())
585 );
586 assert_eq!(
587 tree.get(&IpNet::from_str("10.1.0.0/16").unwrap()),
588 Some(net2.to_string())
589 );
590 assert_eq!(
591 tree.get(&IpNet::from_str("10.0.0.0/8").unwrap()),
592 Some(net1.to_string())
593 );
594 assert_eq!(
596 tree.get(&IpNet::from_str("10.1.2.0/26").unwrap()),
597 Some(net3.to_string())
598 );
599 assert_eq!(
600 tree.get(&IpNet::from_str("10.1.0.0/22").unwrap()),
601 Some(net2.to_string())
602 );
603 assert_eq!(
604 tree.get(&IpNet::from_str("10.1.0.0/12").unwrap()),
605 Some(net1.to_string())
606 );
607 assert_eq!(tree.get(&IpNet::from_str("10.0.0.0/7").unwrap()), None);
609 assert_eq!(tree.get(&IpNet::from_str("11.0.0.0/8").unwrap()), None);
610 }
611
612 #[test]
615 fn test_query_by_child_networks_longest_prefix() {
616 let mut tree = IpRadixTree::new(false);
617 let net1 = IpNet::from_str("192.168.0.0/16").unwrap();
619 let net2 = IpNet::from_str("192.168.1.0/24").unwrap();
620 let net3 = IpNet::from_str("192.168.1.128/25").unwrap();
621 let hash1 = tree.insert(net1);
622 assert_eq!(hash1, Some(net1.to_string()), "insert(192.168.0.0/16) hash");
623 let hash2 = tree.insert(net2);
624 assert_eq!(hash2, Some(net2.to_string()), "insert(192.168.1.0/24) hash");
625 let hash3 = tree.insert(net3);
626 assert_eq!(
627 hash3,
628 Some(net3.to_string()),
629 "insert(192.168.1.128/25) hash"
630 );
631 assert_eq!(
633 tree.get(&IpNet::from_str("192.168.1.128/26").unwrap()),
634 Some(net3.to_string())
635 ); assert_eq!(
637 tree.get(&IpNet::from_str("192.168.1.0/25").unwrap()),
638 Some(net2.to_string())
639 ); assert_eq!(
641 tree.get(&IpNet::from_str("192.168.2.0/24").unwrap()),
642 Some(net1.to_string())
643 ); assert_eq!(tree.get(&IpNet::from_str("10.0.0.0/8").unwrap()), None);
646
647 let netv6_1 = IpNet::from_str("2001:db8::/32").unwrap();
649 let netv6_2 = IpNet::from_str("2001:db8:abcd::/48").unwrap();
650 let netv6_3 = IpNet::from_str("2001:db8:abcd:1234::/64").unwrap();
651 let hashv6_1 = tree.insert(netv6_1);
652 assert_eq!(
653 hashv6_1,
654 Some(netv6_1.to_string()),
655 "insert(2001:db8::/32) hash"
656 );
657 let hashv6_2 = tree.insert(netv6_2);
658 assert_eq!(
659 hashv6_2,
660 Some(netv6_2.to_string()),
661 "insert(2001:db8:abcd::/48) hash"
662 );
663 let hashv6_3 = tree.insert(netv6_3);
664 assert_eq!(
665 hashv6_3,
666 Some(netv6_3.to_string()),
667 "insert(2001:db8:abcd:1234::/64) hash"
668 );
669 assert_eq!(
670 tree.get(&IpNet::from_str("2001:db8:abcd:1234::/80").unwrap()),
671 Some(netv6_3.to_string())
672 ); assert_eq!(
674 tree.get(&IpNet::from_str("2001:db8:abcd::/56").unwrap()),
675 Some(netv6_2.to_string())
676 ); assert_eq!(
678 tree.get(&IpNet::from_str("2001:db8::/40").unwrap()),
679 Some(netv6_1.to_string())
680 ); assert_eq!(tree.get(&IpNet::from_str("2001:dead::/32").unwrap()), None); }
683
684 #[test]
687 fn test_ipv4_ipv6_same_bits_overlap() {
688 let mut tree = IpRadixTree::new(false);
689 let net4 = IpNet::from_str("1.0.0.0/30").unwrap();
691 let net6 = IpNet::from_str("100::/30").unwrap();
692 let expected_bits = vec![
694 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
695 0,
696 ];
697 assert_eq!(
698 ipnet_to_bits(&net4),
699 expected_bits,
700 "net4 bits did not match expected"
701 );
702 assert_eq!(
703 ipnet_to_bits(&net6),
704 expected_bits,
705 "net6 bits did not match expected"
706 );
707
708 let hash4 = tree.insert(net4);
709 assert_eq!(hash4, Some(net4.to_string()), "insert(1.0.0.0/30) hash");
710 assert_eq!(
711 tree.get(&IpNet::from_str("1.0.0.1/30").unwrap()),
712 Some(net4.to_string())
713 );
714
715 let hash6 = tree.insert(net6);
716 assert_eq!(hash6, Some(net6.to_string()), "insert(100::/30) hash");
717 assert_eq!(
718 tree.get(&IpNet::from_str("100::1/30").unwrap()),
719 Some(net6.to_string())
720 );
721
722 assert_eq!(tree.get(&IpNet::from_str("1.0.0.1/30").unwrap()), None);
724 assert_eq!(
725 tree.get(&IpNet::from_str("100::1/30").unwrap()),
726 Some(net6.to_string())
727 );
728 }
729
730 #[test]
731 fn test_mergeable_pairs_two_children() {
732 let net1 = IpNet::from_str("192.168.0.0/24").unwrap();
733 let net2 = IpNet::from_str("192.168.1.0/24").unwrap();
734 let supernet = IpNet::from_str("192.168.0.0/23").unwrap();
735 let mut parent = IPNode::new();
736 let mut child1 = Box::new(IPNode::new());
737 child1.network = Some(net1);
738 let mut child2 = Box::new(IPNode::new());
739 child2.network = Some(net2);
740 parent.children.insert(0, child1);
741 parent.children.insert(1, child2);
742
743 let pairs = parent.mergeable_pairs();
744 assert_eq!(pairs.len(), 1);
745 let (left, right, merged) = &pairs[0];
746 assert!(
748 (left == &net1 && right == &net2) || (left == &net2 && right == &net1),
749 "Pair should be the two /24s"
750 );
751 assert_eq!(merged, &supernet, "Merged should be the /23");
752 }
753
754 #[test]
755 fn test_ipradix_defrag_merge() {
756 let mut tree = IpRadixTree::new(false);
757 let net1 = IpNet::from_str("192.168.0.0/24").unwrap();
758 let net2 = IpNet::from_str("192.168.1.0/24").unwrap();
759 let supernet = IpNet::from_str("192.168.0.0/23").unwrap();
760 tree.insert(net1);
761 tree.insert(net2);
762 let ip1 = IpNet::from_str("192.168.0.1/32").unwrap();
764 let ip2 = IpNet::from_str("192.168.1.1/32").unwrap();
765 assert_eq!(tree.get(&ip1), Some(net1.to_string()));
766 assert_eq!(tree.get(&ip2), Some(net2.to_string()));
767 let (cleaned, new) = tree.defrag();
769 assert!(cleaned.contains(&net1.to_string()));
771 assert!(cleaned.contains(&net2.to_string()));
772 assert!(new.contains(&supernet.to_string()));
773 assert_eq!(tree.get(&ip1), Some(supernet.to_string()));
775 assert_eq!(tree.get(&ip2), Some(supernet.to_string()));
776 }
777
778 #[test]
779 fn test_clear_method() {
780 use crate::node::BaseNode;
781
782 let mut tree = IpRadixTree::new(false);
783
784 let mut networks = vec![
786 "10.0.0.0/8",
787 "10.1.0.0/16",
788 "10.1.1.0/24",
789 "10.1.2.0/24",
790 "10.1.1.128/25",
791 "10.1.1.192/26",
792 "10.1.1.224/27",
793 "10.1.1.240/28",
794 "192.168.0.0/16",
795 "192.168.1.0/24",
796 ];
797
798 use rand::seq::SliceRandom;
800 use rand::thread_rng;
801 networks.shuffle(&mut thread_rng());
802
803 for net_str in &networks {
804 let net = IpNet::from_str(net_str).unwrap();
805 tree.insert(net);
806 }
807
808 for net_str in &networks {
810 let net = IpNet::from_str(net_str).unwrap();
811 assert!(
812 tree.get(&net).is_some(),
813 "Network {} should be present",
814 net_str
815 );
816 }
817
818 let target_net = IpNet::from_str("10.1.1.0/24").unwrap();
820 let canonical = canonicalize_ipnet(&target_net);
821 let mut node = &mut tree.root;
822 let bits = ipnet_to_bits(&canonical);
823 for &bit in &bits {
824 node = node
825 .children
826 .get_mut(&u64::from(bit))
827 .expect("Node should exist");
828 }
829
830 let cleared_hosts = node.clear();
832
833 let expected_cleared = vec![
835 "10.1.1.128/25",
836 "10.1.1.192/26",
837 "10.1.1.224/27",
838 "10.1.1.240/28",
839 ];
840
841 assert_eq!(
842 cleared_hosts.len(),
843 expected_cleared.len(),
844 "Should have cleared {} networks, got {}: {:?}",
845 expected_cleared.len(),
846 cleared_hosts.len(),
847 cleared_hosts
848 );
849
850 for expected in &expected_cleared {
852 assert!(
853 cleared_hosts.contains(&expected.to_string()),
854 "Should have cleared {}",
855 expected
856 );
857 }
858
859 for cleared in &expected_cleared {
861 let net = IpNet::from_str(cleared).unwrap();
862 let result = tree.get(&net);
863 assert!(
864 result.is_none() || result == Some("10.1.1.0/24".to_string()),
865 "Cleared network {} should not be accessible or should fall back to parent",
866 cleared
867 );
868 }
869
870 assert_eq!(tree.get(&target_net), Some("10.1.1.0/24".to_string()));
872
873 let unrelated = vec![
875 "10.0.0.0/8",
876 "10.1.0.0/16",
877 "10.1.2.0/24",
878 "192.168.0.0/16",
879 "192.168.1.0/24",
880 ];
881 for net_str in &unrelated {
882 let net = IpNet::from_str(net_str).unwrap();
883 assert_eq!(
884 tree.get(&net),
885 Some(net_str.to_string()),
886 "Unrelated network {} should still be accessible",
887 net_str
888 );
889 }
890 }
891
892 #[test]
893 fn test_acl_mode_skip_existing() {
894 let mut tree = IpRadixTree::new(true);
895
896 let net = IpNet::from_str("192.168.1.0/24").unwrap();
898 let result1 = tree.insert(net);
899 assert_eq!(result1, Some("192.168.1.0/24".to_string()));
900
901 let result2 = tree.insert(net);
903 assert_eq!(result2, None);
904
905 let other_net = IpNet::from_str("10.0.0.0/8").unwrap();
907 let result3 = tree.insert(other_net);
908 assert_eq!(result3, Some("10.0.0.0/8".to_string()));
909
910 assert_eq!(tree.get(&net), Some("192.168.1.0/24".to_string()));
912 assert_eq!(tree.get(&other_net), Some("10.0.0.0/8".to_string()));
913 }
914
915 #[test]
916 fn test_acl_mode_skip_children() {
917 let mut tree = IpRadixTree::new(true);
918
919 let parent = IpNet::from_str("192.168.0.0/16").unwrap();
921 assert_eq!(tree.insert(parent), Some("192.168.0.0/16".to_string()));
922 assert_eq!(tree.get(&parent), Some("192.168.0.0/16".to_string()));
923
924 let child = IpNet::from_str("192.168.1.0/24").unwrap();
926 assert_eq!(tree.insert(child), None);
927
928 assert_eq!(tree.get(&child), Some("192.168.0.0/16".to_string()));
930
931 let parent_v6 = IpNet::from_str("2001:db8::/32").unwrap();
933 assert_eq!(tree.insert(parent_v6), Some("2001:db8::/32".to_string()));
934
935 let child_v6 = IpNet::from_str("2001:db8:abcd::/48").unwrap();
936 assert_eq!(tree.insert(child_v6), None);
937 assert_eq!(tree.get(&child_v6), Some("2001:db8::/32".to_string()));
938 }
939
940 #[test]
941 fn test_acl_mode_clear_children() {
942 let mut tree = IpRadixTree::new(true);
943
944 let child1 = IpNet::from_str("192.168.1.0/24").unwrap();
946 let child2 = IpNet::from_str("192.168.2.0/24").unwrap();
947 let child3 = IpNet::from_str("192.168.3.0/24").unwrap();
948
949 tree.insert(child1);
950 tree.insert(child2);
951 tree.insert(child3);
952
953 assert_eq!(tree.get(&child1), Some("192.168.1.0/24".to_string()));
955 assert_eq!(tree.get(&child2), Some("192.168.2.0/24".to_string()));
956 assert_eq!(tree.get(&child3), Some("192.168.3.0/24".to_string()));
957
958 let parent = IpNet::from_str("192.168.0.0/16").unwrap();
960 let result = tree.insert(parent);
961 assert_eq!(result, Some("192.168.0.0/16".to_string()));
962
963 assert_eq!(tree.get(&parent), Some("192.168.0.0/16".to_string()));
965
966 assert_eq!(tree.get(&child1), Some("192.168.0.0/16".to_string()));
968 assert_eq!(tree.get(&child2), Some("192.168.0.0/16".to_string()));
969 assert_eq!(tree.get(&child3), Some("192.168.0.0/16".to_string()));
970
971 let child_v6_1 = IpNet::from_str("2001:db8:abcd::/48").unwrap();
973 let child_v6_2 = IpNet::from_str("2001:db8:beef::/48").unwrap();
974
975 tree.insert(child_v6_1);
976 tree.insert(child_v6_2);
977
978 assert_eq!(
979 tree.get(&child_v6_1),
980 Some("2001:db8:abcd::/48".to_string())
981 );
982 assert_eq!(
983 tree.get(&child_v6_2),
984 Some("2001:db8:beef::/48".to_string())
985 );
986
987 let parent_v6 = IpNet::from_str("2001:db8::/32").unwrap();
989 let result_v6 = tree.insert(parent_v6);
990 assert_eq!(result_v6, Some("2001:db8::/32".to_string()));
991
992 assert_eq!(tree.get(&child_v6_1), Some("2001:db8::/32".to_string()));
994 assert_eq!(tree.get(&child_v6_2), Some("2001:db8::/32".to_string()));
995 }
996}