1mod bucket;
2mod entry;
3
4use std::net::IpAddr;
5
6use parking_lot::RwLock;
7
8use rand::seq::IndexedRandom;
9
10use karyon_net::Addr;
11
12use crate::discovery::kademlia::bloom::LocalBloom;
13
14pub use bucket::{
15 Bucket, BucketEntry, EntryStatusFlag, CONNECTED_ENTRY, DISCONNECTED_ENTRY, PENDING_ENTRY,
16 UNREACHABLE_ENTRY, UNSTABLE_ENTRY,
17};
18pub use entry::{xor_distance, Entry, Key};
19
20use bucket::BUCKET_SIZE;
21use entry::KEY_SIZE;
22
23const TABLE_SIZE: usize = 32;
25
26const DISTANCE_LIMIT: usize = 32;
28
29const MAX_MATCHED_SUBNET_IN_BUCKET: usize = 1;
31
32const MAX_MATCHED_SUBNET_IN_TABLE: usize = 6;
34
35#[derive(Debug)]
37pub enum AddEntryResult {
38 Added,
40 Exists,
42 Ignored,
44 Restricted,
46}
47
48#[derive(Debug)]
51pub struct RoutingTable {
52 key: Key,
53 buckets: RwLock<Vec<Bucket>>,
54}
55
56impl RoutingTable {
57 pub fn new(key: Key) -> Self {
59 let buckets: Vec<Bucket> = (0..TABLE_SIZE).map(|_| Bucket::new()).collect();
60 Self {
61 key,
62 buckets: RwLock::new(buckets),
63 }
64 }
65
66 pub fn add_entry(&self, entry: Entry) -> AddEntryResult {
72 let bucket_idx = match self.bucket_index(&entry.key) {
74 Some(i) => i,
75 None => return AddEntryResult::Ignored,
76 };
77
78 let mut buckets = self.buckets.write();
79
80 if buckets[bucket_idx].contains_key(&entry.key) {
82 return AddEntryResult::Exists;
83 }
84
85 if subnet_restricted(&buckets, bucket_idx, &entry) {
87 return AddEntryResult::Restricted;
88 }
89
90 let bucket = &mut buckets[bucket_idx];
91
92 if bucket.len() < BUCKET_SIZE {
94 bucket.add(&entry);
95 return AddEntryResult::Added;
96 }
97
98 let incompatible_entry = bucket.iter().find(|e| e.is_incompatible()).cloned();
100 if let Some(e) = incompatible_entry {
101 bucket.remove(&e.entry.key);
102 bucket.add(&entry);
103 return AddEntryResult::Added;
104 }
105
106 AddEntryResult::Ignored
108 }
109
110 pub fn contains_key(&self, key: &Key) -> bool {
112 let buckets = self.buckets.read();
113 let bucket_idx = match self.bucket_index(key) {
115 Some(bi) => bi,
116 None => return false,
117 };
118
119 let bucket = &buckets[bucket_idx];
120 bucket.contains_key(key)
121 }
122
123 pub fn update_entry(&self, key: &Key, entry_flag: EntryStatusFlag) {
128 let mut buckets = self.buckets.write();
129 let bucket_idx = match self.bucket_index(key) {
131 Some(bi) => bi,
132 None => return,
133 };
134
135 let bucket = &mut buckets[bucket_idx];
136 bucket.update_entry(key, entry_flag);
137 }
138
139 pub fn bucket_indexes(&self, target_key: &Key) -> Vec<usize> {
141 let mut indexes = vec![];
142
143 let bucket_idx = self.bucket_index(target_key).unwrap_or(0);
145
146 indexes.push(bucket_idx);
147
148 for i in 1..DISTANCE_LIMIT {
150 if bucket_idx >= i && bucket_idx - i >= 1 {
151 indexes.push(bucket_idx - i);
152 }
153
154 if bucket_idx + i < (TABLE_SIZE - 1) {
155 indexes.push(bucket_idx + i);
156 }
157 }
158
159 indexes
160 }
161
162 pub fn closest_entries(&self, target_key: &Key, max_entries: usize) -> Vec<Entry> {
164 self.closest_entries_filtered(target_key, max_entries, &LocalBloom::default())
165 }
166
167 pub fn closest_entries_filtered(
170 &self,
171 target_key: &Key,
172 max_entries: usize,
173 local: &LocalBloom,
174 ) -> Vec<Entry> {
175 let buckets = self.buckets.read();
176 let mut entries: Vec<Entry> = vec![];
177
178 'outer: for idx in self.bucket_indexes(target_key) {
179 let bucket = &buckets[idx];
180 for bucket_entry in bucket.iter() {
181 if bucket_entry.is_unreachable() || bucket_entry.is_unstable() {
182 continue;
183 }
184 if !local.matches(&bucket_entry.entry.protocols) {
185 continue;
186 }
187
188 entries.push(bucket_entry.entry.clone());
189 if entries.len() == max_entries {
190 break 'outer;
191 }
192 }
193 }
194
195 entries.sort_by(|a, b| {
196 xor_distance(target_key, &a.key).cmp(&xor_distance(target_key, &b.key))
197 });
198
199 entries
200 }
201
202 pub fn entries_with_item(&self, item: &[u8]) -> Vec<Entry> {
206 let buckets = self.buckets.read();
207 let mut entries = Vec::new();
208 for bucket in buckets.iter() {
209 for be in bucket.iter() {
210 if be.is_unreachable() || be.is_unstable() {
211 continue;
212 }
213 if be.entry.protocols.may_contain(item) {
214 entries.push(be.entry.clone());
215 }
216 }
217 }
218 entries
219 }
220
221 pub fn remove_entry(&self, key: &Key) {
223 let mut buckets = self.buckets.write();
224 let bucket_idx = match self.bucket_index(key) {
226 Some(bi) => bi,
227 None => return,
228 };
229
230 let bucket = &mut buckets[bucket_idx];
231 bucket.remove(key);
232 }
233
234 pub fn has_discovery_ip(&self, ip: &IpAddr) -> bool {
237 let buckets = self.buckets.read();
238 for bucket in buckets.iter() {
239 for be in bucket.iter() {
240 for a in be.entry.discovery_addrs.iter() {
241 if let Addr::Ip(entry_ip) = &a.addr {
242 if entry_ip == ip {
243 return true;
244 }
245 }
246 }
247 }
248 }
249 false
250 }
251
252 pub fn refresh_candidates(&self, per_bucket: usize) -> Vec<BucketEntry> {
255 let buckets = self.buckets.read();
256 let mut entries = Vec::new();
257 for bucket in buckets.iter() {
258 for entry in bucket
259 .iter()
260 .filter(|e| !e.is_connected() && !e.is_incompatible())
261 .take(per_bucket)
262 {
263 entries.push(entry.clone());
264 }
265 }
266 entries
267 }
268
269 pub fn random_entry(&self, entry_flag: EntryStatusFlag) -> Option<Entry> {
271 self.random_entry_filtered(entry_flag, &LocalBloom::default())
272 }
273
274 pub fn random_entry_filtered(
277 &self,
278 entry_flag: EntryStatusFlag,
279 local: &LocalBloom,
280 ) -> Option<Entry> {
281 let buckets = self.buckets.read();
282 for bucket in buckets.choose_multiple(&mut rand::rng(), buckets.len()) {
283 for entry in bucket.random_iter(bucket.len()) {
284 if entry.status & entry_flag == 0 {
285 continue;
286 }
287 if !local.matches(&entry.entry.protocols) {
288 continue;
289 }
290 return Some(entry.entry.clone());
291 }
292 }
293
294 None
295 }
296
297 fn bucket_index(&self, key: &Key) -> Option<usize> {
299 let distance = xor_distance(&self.key, key);
301
302 for (i, b) in distance.iter().enumerate() {
303 if *b != 0 {
304 let lz = i * 8 + b.leading_zeros() as usize;
305 let bits = KEY_SIZE * 8 - 1;
306 let idx = (bits - lz) / 8;
307 return Some(idx);
308 }
309 }
310 None
311 }
312}
313
314fn subnet_restricted(buckets: &[Bucket], idx: usize, entry: &Entry) -> bool {
321 let mut bucket_count = 0;
322 let mut table_count = 0;
323
324 for (i, bucket) in buckets.iter().enumerate() {
325 for e in bucket.iter() {
326 let matched = match (e.entry.primary_addr(), entry.primary_addr()) {
327 (Some(a), Some(b)) => subnet_match(a, b),
328 _ => false,
329 };
330 if matched {
331 if i == idx {
332 bucket_count += 1;
333 }
334 table_count += 1;
335 }
336
337 if bucket_count >= MAX_MATCHED_SUBNET_IN_BUCKET {
338 return true;
339 }
340 }
341
342 if table_count >= MAX_MATCHED_SUBNET_IN_TABLE {
343 return true;
344 }
345 }
346
347 false
348}
349
350fn subnet_match(addr: &Addr, other_addr: &Addr) -> bool {
352 if is_loopback_pair(addr, other_addr) {
354 return false;
355 }
356 match (addr, other_addr) {
357 (Addr::Ip(IpAddr::V4(ip)), Addr::Ip(IpAddr::V4(other_ip))) => {
359 ip.octets()[0..3] == other_ip.octets()[0..3]
360 }
361 (Addr::Ip(IpAddr::V6(ip)), Addr::Ip(IpAddr::V6(other_ip))) => {
363 ip.segments()[0..4] == other_ip.segments()[0..4]
364 }
365 _ => false,
366 }
367}
368
369fn is_loopback_pair(a: &Addr, b: &Addr) -> bool {
371 match (a, b) {
372 (Addr::Ip(a), Addr::Ip(b)) => a.is_loopback() && b.is_loopback(),
373 _ => false,
374 }
375}
376
377#[cfg(test)]
378mod tests {
379 use super::bucket::ALL_ENTRY;
380 use super::*;
381
382 use karyon_net::Addr;
383
384 use crate::{
385 discovery::kademlia::bloom::Bloom,
386 message::{PeerAddr, Protocol},
387 };
388
389 struct Setup {
390 local_key: Key,
391 keys: Vec<Key>,
392 }
393
394 fn new_entry(key: &Key, addr: &Addr, port: u16, discovery_port: u16) -> Entry {
395 Entry {
396 key: *key,
397 addrs: vec![PeerAddr {
398 addr: addr.clone(),
399 port,
400 protocol: Protocol::Tcp,
401 priority: 0,
402 }],
403 discovery_addrs: vec![
404 PeerAddr {
405 addr: addr.clone(),
406 port: discovery_port,
407 protocol: Protocol::Tcp,
408 priority: 0,
409 },
410 PeerAddr {
411 addr: addr.clone(),
412 port: discovery_port,
413 protocol: Protocol::Udp,
414 priority: 1,
415 },
416 ],
417 protocols: Bloom::default(),
418 }
419 }
420
421 impl Setup {
422 fn new() -> Self {
423 let keys = vec![
424 [
425 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
426 0, 0, 0, 0, 0, 1,
427 ],
428 [
429 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
430 1, 1, 0, 1, 1, 2,
431 ],
432 [
433 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
434 0, 0, 0, 0, 0, 3,
435 ],
436 [
437 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 1, 18, 0, 0, 0,
438 0, 0, 0, 0, 0, 4,
439 ],
440 [
441 223, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
442 0, 0, 0, 0, 0, 5,
443 ],
444 [
445 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 1, 18, 0, 0, 0,
446 0, 0, 0, 0, 0, 6,
447 ],
448 [
449 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 50, 1, 18, 0, 0,
450 0, 0, 0, 0, 0, 0, 7,
451 ],
452 [
453 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 50, 1, 18, 0, 0,
454 0, 0, 0, 0, 0, 0, 8,
455 ],
456 [
457 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10, 50, 1, 18, 0, 0,
458 0, 0, 0, 0, 0, 0, 9,
459 ],
460 ];
461
462 Self {
463 local_key: [
464 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
465 0, 0, 0, 0, 0, 0,
466 ],
467 keys,
468 }
469 }
470
471 fn entries(&self) -> Vec<Entry> {
472 let mut entries = vec![];
473 for (i, key) in self.keys.iter().enumerate() {
474 entries.push(new_entry(
475 key,
476 &Addr::Ip(format!("127.0.{i}.1").parse().unwrap()),
477 3000,
478 3010,
479 ));
480 }
481 entries
482 }
483
484 fn table(&self) -> RoutingTable {
485 let table = RoutingTable::new(self.local_key);
486
487 for entry in self.entries() {
488 let res = table.add_entry(entry);
489 assert!(matches!(res, AddEntryResult::Added));
490 }
491
492 table
493 }
494 }
495
496 #[test]
497 fn test_bucket_index() {
498 let setup = Setup::new();
499 let table = setup.table();
500
501 assert_eq!(table.bucket_index(&setup.local_key), None);
502 assert_eq!(table.bucket_index(&setup.keys[0]), Some(0));
503 assert_eq!(table.bucket_index(&setup.keys[1]), Some(5));
504 assert_eq!(table.bucket_index(&setup.keys[2]), Some(26));
505 assert_eq!(table.bucket_index(&setup.keys[3]), Some(11));
506 assert_eq!(table.bucket_index(&setup.keys[4]), Some(31));
507 assert_eq!(table.bucket_index(&setup.keys[5]), Some(11));
508 assert_eq!(table.bucket_index(&setup.keys[6]), Some(12));
509 assert_eq!(table.bucket_index(&setup.keys[7]), Some(13));
510 assert_eq!(table.bucket_index(&setup.keys[8]), Some(14));
511 }
512
513 #[test]
514 fn test_closest_entries() {
515 let setup = Setup::new();
516 let table = setup.table();
517 let entries = setup.entries();
518
519 assert_eq!(
520 table.closest_entries(&setup.keys[5], 8),
521 vec![
522 entries[5].clone(),
523 entries[3].clone(),
524 entries[1].clone(),
525 entries[6].clone(),
526 entries[7].clone(),
527 entries[8].clone(),
528 entries[2].clone(),
529 ]
530 );
531
532 assert_eq!(
533 table.closest_entries(&setup.keys[4], 2),
534 vec![entries[4].clone(), entries[2].clone()]
535 );
536 }
537
538 #[test]
539 fn test_random_entry() {
540 let setup = Setup::new();
541 let table = setup.table();
542 let entries = setup.entries();
543
544 let entry = table.random_entry(ALL_ENTRY);
545 assert!(entry.is_some());
546
547 let entry = table.random_entry(CONNECTED_ENTRY);
548 assert!(entry.is_none());
549
550 for entry in entries {
551 table.remove_entry(&entry.key);
552 }
553
554 let entry = table.random_entry(ALL_ENTRY);
555 assert!(entry.is_none());
556 }
557
558 #[test]
559 fn test_add_entries() {
560 let setup = Setup::new();
561 let table = setup.table();
562
563 let key = [
564 0, 0, 0, 0, 0, 0, 0, 1, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
565 0, 0, 5,
566 ];
567
568 let key2 = [
569 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
570 0, 0, 5,
571 ];
572
573 let entry1 = new_entry(&key, &Addr::Ip("240.120.3.1".parse().unwrap()), 3000, 3010);
574 assert!(matches!(
575 table.add_entry(entry1.clone()),
576 AddEntryResult::Added
577 ));
578
579 assert!(matches!(table.add_entry(entry1), AddEntryResult::Exists));
580
581 let entry2 = new_entry(&key2, &Addr::Ip("240.120.3.2".parse().unwrap()), 3000, 3010);
582 assert!(matches!(
583 table.add_entry(entry2),
584 AddEntryResult::Restricted
585 ));
586
587 let mut key: [u8; 32] = [0; 32];
588
589 for i in 0..BUCKET_SIZE {
590 key[i] += 1;
591 let entry = new_entry(
592 &key,
593 &Addr::Ip(format!("127.0.{i}.1").parse().unwrap()),
594 3000,
595 3010,
596 );
597 table.add_entry(entry);
598 }
599
600 key[BUCKET_SIZE] += 1;
601 let entry = new_entry(&key, &Addr::Ip("125.20.0.1".parse().unwrap()), 3000, 3010);
602 assert!(matches!(table.add_entry(entry), AddEntryResult::Ignored));
603 }
604
605 use std::net::{Ipv4Addr, Ipv6Addr};
606 #[test]
607 fn check_subnet_match() {
608 let addr_v4 = Addr::Ip(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
609 let other_addr_v4 = Addr::Ip(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 2)));
610
611 let addr_v6 = Addr::Ip(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)));
612 let other_addr_v6 = Addr::Ip(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2)));
613 let diff_addr_v6 = Addr::Ip(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb7, 0, 0, 0, 0, 0, 2)));
614
615 assert!(subnet_match(&addr_v4, &other_addr_v4));
616 assert!(subnet_match(&addr_v6, &other_addr_v6));
617 assert!(!subnet_match(&addr_v6, &diff_addr_v6));
618 }
619}