1pub mod types;
2pub mod tables;
3pub mod dedup;
4pub mod pathfinder;
5pub mod rate_limit;
6pub mod announce_proc;
7pub mod outbound;
8pub mod inbound;
9pub mod announce_queue;
10pub mod tunnel;
11pub mod jobs;
12
13use alloc::collections::BTreeMap;
14use alloc::vec::Vec;
15
16use rns_crypto::Rng;
17
18use crate::announce::AnnounceData;
19use crate::constants;
20use crate::hash;
21use crate::packet::RawPacket;
22
23use self::announce_proc::compute_path_expires;
24use self::announce_queue::AnnounceQueues;
25use self::dedup::PacketHashlist;
26use self::tunnel::TunnelTable;
27use self::inbound::{
28 create_link_entry, create_reverse_entry, forward_transport_packet,
29 route_proof_via_reverse, route_via_link_table,
30};
31use self::outbound::{route_outbound, should_transmit_announce};
32use self::pathfinder::{
33 extract_random_blob, should_update_path, timebase_from_random_blob, PathDecision,
34};
35use self::rate_limit::AnnounceRateLimiter;
36use self::tables::{AnnounceEntry, LinkEntry, PathEntry};
37use self::types::{BlackholeEntry, InterfaceId, InterfaceInfo, TransportAction, TransportConfig};
38
39pub struct TransportEngine {
44 config: TransportConfig,
45 path_table: BTreeMap<[u8; 16], PathEntry>,
46 announce_table: BTreeMap<[u8; 16], AnnounceEntry>,
47 reverse_table: BTreeMap<[u8; 16], tables::ReverseEntry>,
48 link_table: BTreeMap<[u8; 16], LinkEntry>,
49 held_announces: BTreeMap<[u8; 16], AnnounceEntry>,
50 packet_hashlist: PacketHashlist,
51 rate_limiter: AnnounceRateLimiter,
52 path_states: BTreeMap<[u8; 16], u8>,
53 path_requests: BTreeMap<[u8; 16], f64>,
54 interfaces: BTreeMap<InterfaceId, InterfaceInfo>,
55 local_destinations: BTreeMap<[u8; 16], u8>,
56 blackholed_identities: BTreeMap<[u8; 16], BlackholeEntry>,
57 announce_queues: AnnounceQueues,
58 tunnel_table: TunnelTable,
59 discovery_pr_tags: Vec<[u8; 32]>,
60 announces_last_checked: f64,
62 tables_last_culled: f64,
63 links_last_checked: f64,
64}
65
66impl TransportEngine {
67 pub fn new(config: TransportConfig) -> Self {
68 TransportEngine {
69 config,
70 path_table: BTreeMap::new(),
71 announce_table: BTreeMap::new(),
72 reverse_table: BTreeMap::new(),
73 link_table: BTreeMap::new(),
74 held_announces: BTreeMap::new(),
75 packet_hashlist: PacketHashlist::new(constants::HASHLIST_MAXSIZE),
76 rate_limiter: AnnounceRateLimiter::new(),
77 path_states: BTreeMap::new(),
78 path_requests: BTreeMap::new(),
79 interfaces: BTreeMap::new(),
80 local_destinations: BTreeMap::new(),
81 blackholed_identities: BTreeMap::new(),
82 announce_queues: AnnounceQueues::new(),
83 tunnel_table: TunnelTable::new(),
84 discovery_pr_tags: Vec::new(),
85 announces_last_checked: 0.0,
86 tables_last_culled: 0.0,
87 links_last_checked: 0.0,
88 }
89 }
90
91 pub fn register_interface(&mut self, info: InterfaceInfo) {
96 self.interfaces.insert(info.id, info);
97 }
98
99 pub fn deregister_interface(&mut self, id: InterfaceId) {
100 self.interfaces.remove(&id);
101 }
102
103 pub fn register_destination(&mut self, dest_hash: [u8; 16], dest_type: u8) {
108 self.local_destinations.insert(dest_hash, dest_type);
109 }
110
111 pub fn deregister_destination(&mut self, dest_hash: &[u8; 16]) {
112 self.local_destinations.remove(dest_hash);
113 }
114
115 pub fn has_path(&self, dest_hash: &[u8; 16]) -> bool {
120 self.path_table.contains_key(dest_hash)
121 }
122
123 pub fn hops_to(&self, dest_hash: &[u8; 16]) -> Option<u8> {
124 self.path_table.get(dest_hash).map(|e| e.hops)
125 }
126
127 pub fn next_hop(&self, dest_hash: &[u8; 16]) -> Option<[u8; 16]> {
128 self.path_table.get(dest_hash).map(|e| e.next_hop)
129 }
130
131 pub fn next_hop_interface(&self, dest_hash: &[u8; 16]) -> Option<InterfaceId> {
132 self.path_table.get(dest_hash).map(|e| e.receiving_interface)
133 }
134
135 pub fn mark_path_unresponsive(&mut self, dest_hash: &[u8; 16]) {
140 self.path_states
141 .insert(*dest_hash, constants::STATE_UNRESPONSIVE);
142 }
143
144 pub fn mark_path_responsive(&mut self, dest_hash: &[u8; 16]) {
145 self.path_states
146 .insert(*dest_hash, constants::STATE_RESPONSIVE);
147 }
148
149 pub fn path_is_unresponsive(&self, dest_hash: &[u8; 16]) -> bool {
150 self.path_states.get(dest_hash) == Some(&constants::STATE_UNRESPONSIVE)
151 }
152
153 pub fn expire_path(&mut self, dest_hash: &[u8; 16]) {
154 if let Some(entry) = self.path_table.get_mut(dest_hash) {
155 entry.timestamp = 0.0;
156 entry.expires = 0.0;
157 }
158 }
159
160 pub fn register_link(&mut self, link_id: [u8; 16], entry: LinkEntry) {
165 self.link_table.insert(link_id, entry);
166 }
167
168 pub fn validate_link(&mut self, link_id: &[u8; 16]) {
169 if let Some(entry) = self.link_table.get_mut(link_id) {
170 entry.validated = true;
171 }
172 }
173
174 pub fn remove_link(&mut self, link_id: &[u8; 16]) {
175 self.link_table.remove(link_id);
176 }
177
178 pub fn blackhole_identity(
184 &mut self,
185 identity_hash: [u8; 16],
186 now: f64,
187 duration_hours: Option<f64>,
188 reason: Option<String>,
189 ) {
190 let expires = match duration_hours {
191 Some(h) if h > 0.0 => now + h * 3600.0,
192 _ => 0.0, };
194 self.blackholed_identities.insert(
195 identity_hash,
196 BlackholeEntry {
197 created: now,
198 expires,
199 reason,
200 },
201 );
202 }
203
204 pub fn unblackhole_identity(&mut self, identity_hash: &[u8; 16]) -> bool {
206 self.blackholed_identities.remove(identity_hash).is_some()
207 }
208
209 pub fn is_blackholed(&self, identity_hash: &[u8; 16], now: f64) -> bool {
211 if let Some(entry) = self.blackholed_identities.get(identity_hash) {
212 if entry.expires == 0.0 || entry.expires > now {
213 return true;
214 }
215 }
216 false
217 }
218
219 pub fn blackholed_entries(&self) -> impl Iterator<Item = (&[u8; 16], &BlackholeEntry)> {
221 self.blackholed_identities.iter()
222 }
223
224 fn cull_blackholed(&mut self, now: f64) {
226 self.blackholed_identities.retain(|_, entry| {
227 entry.expires == 0.0 || entry.expires > now
228 });
229 }
230
231 pub fn handle_tunnel(
239 &mut self,
240 tunnel_id: [u8; 32],
241 interface: InterfaceId,
242 now: f64,
243 ) -> Vec<TransportAction> {
244 let mut actions = Vec::new();
245
246 if let Some(info) = self.interfaces.get_mut(&interface) {
248 info.tunnel_id = Some(tunnel_id);
249 }
250
251 let restored_paths = self.tunnel_table.handle_tunnel(tunnel_id, interface, now);
252
253 for (dest_hash, tunnel_path) in &restored_paths {
255 let should_restore = match self.path_table.get(dest_hash) {
256 Some(existing) => {
257 tunnel_path.hops <= existing.hops || existing.expires < now
259 }
260 None => true,
261 };
262
263 if should_restore {
264 self.path_table.insert(
265 *dest_hash,
266 PathEntry {
267 timestamp: tunnel_path.timestamp,
268 next_hop: tunnel_path.received_from,
269 hops: tunnel_path.hops,
270 expires: tunnel_path.expires,
271 random_blobs: tunnel_path.random_blobs.clone(),
272 receiving_interface: interface,
273 packet_hash: tunnel_path.packet_hash,
274 announce_raw: None,
275 },
276 );
277 }
278 }
279
280 actions.push(TransportAction::TunnelEstablished {
281 tunnel_id,
282 interface,
283 });
284
285 actions
286 }
287
288 pub fn synthesize_tunnel(
296 &self,
297 identity: &rns_crypto::identity::Identity,
298 interface_id: InterfaceId,
299 rng: &mut dyn Rng,
300 ) -> Vec<TransportAction> {
301 let mut actions = Vec::new();
302
303 let interface_hash = if let Some(info) = self.interfaces.get(&interface_id) {
305 hash::full_hash(info.name.as_bytes())
306 } else {
307 return actions;
308 };
309
310 match tunnel::build_tunnel_synthesize_data(identity, &interface_hash, rng) {
311 Ok((data, _tunnel_id)) => {
312 let dest_hash = crate::destination::destination_hash(
313 "rnstransport",
314 &["tunnel", "synthesize"],
315 None,
316 );
317 actions.push(TransportAction::TunnelSynthesize {
318 interface: interface_id,
319 data,
320 dest_hash,
321 });
322 }
323 Err(e) => {
324 let _ = e;
326 }
327 }
328
329 actions
330 }
331
332 pub fn void_tunnel_interface(&mut self, tunnel_id: &[u8; 32]) {
334 self.tunnel_table.void_tunnel_interface(tunnel_id);
335 }
336
337 pub fn tunnel_table(&self) -> &TunnelTable {
339 &self.tunnel_table
340 }
341
342 fn has_local_clients(&self) -> bool {
348 self.interfaces.values().any(|i| i.is_local_client)
349 }
350
351 fn packet_filter(&self, packet: &RawPacket) -> bool {
355 if packet.transport_id.is_some()
357 && packet.flags.packet_type != constants::PACKET_TYPE_ANNOUNCE
358 {
359 if let Some(ref identity_hash) = self.config.identity_hash {
360 if packet.transport_id.as_ref() != Some(identity_hash) {
361 return false;
362 }
363 }
364 }
365
366 match packet.context {
368 constants::CONTEXT_KEEPALIVE
369 | constants::CONTEXT_RESOURCE_REQ
370 | constants::CONTEXT_RESOURCE_PRF
371 | constants::CONTEXT_RESOURCE
372 | constants::CONTEXT_CACHE_REQUEST
373 | constants::CONTEXT_CHANNEL => return true,
374 _ => {}
375 }
376
377 if packet.flags.destination_type == constants::DESTINATION_PLAIN
379 || packet.flags.destination_type == constants::DESTINATION_GROUP
380 {
381 if packet.flags.packet_type != constants::PACKET_TYPE_ANNOUNCE {
382 return packet.hops <= 1;
383 } else {
384 return false;
386 }
387 }
388
389 if !self.packet_hashlist.is_duplicate(&packet.packet_hash) {
391 return true;
392 }
393
394 if packet.flags.packet_type == constants::PACKET_TYPE_ANNOUNCE
396 && packet.flags.destination_type == constants::DESTINATION_SINGLE
397 {
398 return true;
399 }
400
401 false
402 }
403
404 pub fn handle_inbound(
412 &mut self,
413 raw: &[u8],
414 iface: InterfaceId,
415 now: f64,
416 rng: &mut dyn Rng,
417 ) -> Vec<TransportAction> {
418 let mut actions = Vec::new();
419
420 let mut packet = match RawPacket::unpack(raw) {
422 Ok(p) => p,
423 Err(_) => return actions, };
425
426 let original_raw = raw.to_vec();
428
429 packet.hops += 1;
431
432 let from_local_client = self
435 .interfaces
436 .get(&iface)
437 .map(|i| i.is_local_client)
438 .unwrap_or(false);
439 if from_local_client {
440 packet.hops = packet.hops.saturating_sub(1);
441 }
442
443 if !self.packet_filter(&packet) {
445 return actions;
446 }
447
448 let mut remember_hash = true;
450
451 if self.link_table.contains_key(&packet.destination_hash) {
452 remember_hash = false;
453 }
454 if packet.flags.packet_type == constants::PACKET_TYPE_PROOF
455 && packet.context == constants::CONTEXT_LRPROOF
456 {
457 remember_hash = false;
458 }
459
460 if remember_hash {
461 self.packet_hashlist.add(packet.packet_hash);
462 }
463
464 if packet.flags.destination_type == constants::DESTINATION_PLAIN
466 && packet.flags.transport_type == constants::TRANSPORT_BROADCAST
467 && self.has_local_clients()
468 {
469 if from_local_client {
470 actions.push(TransportAction::ForwardPlainBroadcast {
472 raw: packet.raw.clone(),
473 to_local: false,
474 exclude: Some(iface),
475 });
476 } else {
477 actions.push(TransportAction::ForwardPlainBroadcast {
479 raw: packet.raw.clone(),
480 to_local: true,
481 exclude: None,
482 });
483 }
484 }
485
486 if self.config.transport_enabled || self.config.identity_hash.is_some() {
488 if packet.transport_id.is_some()
489 && packet.flags.packet_type != constants::PACKET_TYPE_ANNOUNCE
490 {
491 if let Some(ref identity_hash) = self.config.identity_hash {
492 if packet.transport_id.as_ref() == Some(identity_hash) {
493 if let Some(path_entry) = self.path_table.get(&packet.destination_hash) {
494 let next_hop = path_entry.next_hop;
495 let remaining_hops = path_entry.hops;
496 let outbound_interface = path_entry.receiving_interface;
497
498 let new_raw = forward_transport_packet(
499 &packet,
500 next_hop,
501 remaining_hops,
502 outbound_interface,
503 );
504
505 if packet.flags.packet_type == constants::PACKET_TYPE_LINKREQUEST {
507 let proof_timeout = now
508 + constants::LINK_ESTABLISHMENT_TIMEOUT_PER_HOP
509 * (remaining_hops.max(1) as f64);
510
511 let (link_id, link_entry) = create_link_entry(
512 &packet,
513 next_hop,
514 outbound_interface,
515 remaining_hops,
516 iface,
517 now,
518 proof_timeout,
519 );
520 self.link_table.insert(link_id, link_entry);
521 } else {
522 let (trunc_hash, reverse_entry) = create_reverse_entry(
523 &packet,
524 outbound_interface,
525 iface,
526 now,
527 );
528 self.reverse_table.insert(trunc_hash, reverse_entry);
529 }
530
531 actions.push(TransportAction::SendOnInterface {
532 interface: outbound_interface,
533 raw: new_raw,
534 });
535
536 if let Some(entry) = self.path_table.get_mut(&packet.destination_hash) {
538 entry.timestamp = now;
539 }
540 }
541 }
542 }
543 }
544
545 if packet.flags.packet_type != constants::PACKET_TYPE_ANNOUNCE
547 && packet.flags.packet_type != constants::PACKET_TYPE_LINKREQUEST
548 && packet.context != constants::CONTEXT_LRPROOF
549 {
550 if let Some(link_entry) = self.link_table.get(&packet.destination_hash).cloned() {
551 if let Some((outbound_iface, new_raw)) =
552 route_via_link_table(&packet, &link_entry, iface)
553 {
554 self.packet_hashlist.add(packet.packet_hash);
556
557 actions.push(TransportAction::SendOnInterface {
558 interface: outbound_iface,
559 raw: new_raw,
560 });
561
562 if let Some(entry) =
564 self.link_table.get_mut(&packet.destination_hash)
565 {
566 entry.timestamp = now;
567 }
568 }
569 }
570 }
571 }
572
573 if packet.flags.packet_type == constants::PACKET_TYPE_ANNOUNCE {
575 self.process_inbound_announce(&packet, &original_raw, iface, now, rng, &mut actions);
576 }
577
578 if packet.flags.packet_type == constants::PACKET_TYPE_PROOF {
580 self.process_inbound_proof(&packet, iface, now, &mut actions);
581 }
582
583 if packet.flags.packet_type == constants::PACKET_TYPE_LINKREQUEST
585 || packet.flags.packet_type == constants::PACKET_TYPE_DATA
586 {
587 if self.local_destinations.contains_key(&packet.destination_hash) {
588 actions.push(TransportAction::DeliverLocal {
589 destination_hash: packet.destination_hash,
590 raw: packet.raw.clone(),
591 packet_hash: packet.packet_hash,
592 });
593 }
594 }
595
596 actions
597 }
598
599 fn process_inbound_announce(
604 &mut self,
605 packet: &RawPacket,
606 original_raw: &[u8],
607 iface: InterfaceId,
608 now: f64,
609 rng: &mut dyn Rng,
610 actions: &mut Vec<TransportAction>,
611 ) {
612 if packet.flags.destination_type != constants::DESTINATION_SINGLE {
613 return;
614 }
615
616 let has_ratchet = packet.flags.context_flag == constants::FLAG_SET;
617
618 let announce = match AnnounceData::unpack(&packet.data, has_ratchet) {
620 Ok(a) => a,
621 Err(_) => return,
622 };
623
624 let validated = match announce.validate(&packet.destination_hash) {
625 Ok(v) => v,
626 Err(_) => return,
627 };
628
629 if self.is_blackholed(&validated.identity_hash, now) {
631 return;
632 }
633
634 if self.local_destinations.contains_key(&packet.destination_hash) {
636 return;
637 }
638
639 let received_from = if let Some(transport_id) = packet.transport_id {
641 if self.config.transport_enabled {
643 if let Some(announce_entry) = self.announce_table.get_mut(&packet.destination_hash) {
644 if packet.hops.checked_sub(1) == Some(announce_entry.hops) {
645 announce_entry.local_rebroadcasts += 1;
646 if announce_entry.retries > 0
647 && announce_entry.local_rebroadcasts >= constants::LOCAL_REBROADCASTS_MAX
648 {
649 self.announce_table.remove(&packet.destination_hash);
650 }
651 }
652 if let Some(announce_entry) = self.announce_table.get(&packet.destination_hash) {
654 if packet.hops.checked_sub(1) == Some(announce_entry.hops + 1)
655 && announce_entry.retries > 0
656 {
657 if now < announce_entry.retransmit_timeout {
658 self.announce_table.remove(&packet.destination_hash);
659 }
660 }
661 }
662 }
663 }
664 transport_id
665 } else {
666 packet.destination_hash
667 };
668
669 let random_blob = match extract_random_blob(&packet.data) {
671 Some(b) => b,
672 None => return,
673 };
674
675 if packet.hops >= constants::PATHFINDER_M + 1 {
677 return;
678 }
679
680 let announce_emitted = timebase_from_random_blob(&random_blob);
681
682 let existing = self.path_table.get(&packet.destination_hash);
684 let is_unresponsive = self.path_is_unresponsive(&packet.destination_hash);
685
686 let decision = should_update_path(
687 existing,
688 packet.hops,
689 announce_emitted,
690 &random_blob,
691 is_unresponsive,
692 now,
693 );
694
695 if decision == PathDecision::Reject {
696 return;
697 }
698
699 let rate_blocked = if packet.context != constants::CONTEXT_PATH_RESPONSE {
701 if let Some(iface_info) = self.interfaces.get(&iface) {
702 self.rate_limiter.check_and_update(
703 &packet.destination_hash,
704 now,
705 iface_info.announce_rate_target,
706 iface_info.announce_rate_grace,
707 iface_info.announce_rate_penalty,
708 )
709 } else {
710 false
711 }
712 } else {
713 false
714 };
715
716 let interface_mode = self
718 .interfaces
719 .get(&iface)
720 .map(|i| i.mode)
721 .unwrap_or(constants::MODE_FULL);
722
723 let expires = compute_path_expires(now, interface_mode);
724
725 let existing_blobs = self
727 .path_table
728 .get(&packet.destination_hash)
729 .map(|e| e.random_blobs.clone())
730 .unwrap_or_default();
731
732 let mut rng_bytes = [0u8; 8];
734 rng.fill_bytes(&mut rng_bytes);
735 let rng_value = (u64::from_le_bytes(rng_bytes) as f64) / (u64::MAX as f64);
736
737 let is_path_response = packet.context == constants::CONTEXT_PATH_RESPONSE;
738
739 let (path_entry, announce_entry) = announce_proc::process_validated_announce(
740 packet.destination_hash,
741 packet.hops,
742 &packet.data,
743 &packet.raw,
744 packet.packet_hash,
745 packet.flags.context_flag,
746 received_from,
747 iface,
748 now,
749 existing_blobs,
750 random_blob,
751 expires,
752 rng_value,
753 self.config.transport_enabled,
754 is_path_response,
755 rate_blocked,
756 Some(original_raw.to_vec()),
757 );
758
759 actions.push(TransportAction::CacheAnnounce {
761 packet_hash: packet.packet_hash,
762 raw: original_raw.to_vec(),
763 });
764
765 self.path_table
767 .insert(packet.destination_hash, path_entry);
768
769 if let Some(tunnel_id) = self.interfaces.get(&iface).and_then(|i| i.tunnel_id) {
771 let blobs = self
772 .path_table
773 .get(&packet.destination_hash)
774 .map(|e| e.random_blobs.clone())
775 .unwrap_or_default();
776 self.tunnel_table.store_tunnel_path(
777 &tunnel_id,
778 packet.destination_hash,
779 tunnel::TunnelPath {
780 timestamp: now,
781 received_from,
782 hops: packet.hops,
783 expires,
784 random_blobs: blobs,
785 packet_hash: packet.packet_hash,
786 },
787 now,
788 );
789 }
790
791 self.path_states.remove(&packet.destination_hash);
793
794 if let Some(ann) = announce_entry {
796 self.announce_table.insert(packet.destination_hash, ann);
797 }
798
799 actions.push(TransportAction::AnnounceReceived {
801 destination_hash: packet.destination_hash,
802 identity_hash: validated.identity_hash,
803 public_key: validated.public_key,
804 name_hash: validated.name_hash,
805 random_hash: validated.random_hash,
806 app_data: validated.app_data,
807 hops: packet.hops,
808 receiving_interface: iface,
809 });
810
811 actions.push(TransportAction::PathUpdated {
812 destination_hash: packet.destination_hash,
813 hops: packet.hops,
814 next_hop: received_from,
815 interface: iface,
816 });
817
818 if self.has_local_clients() {
820 actions.push(TransportAction::ForwardToLocalClients {
821 raw: packet.raw.clone(),
822 exclude: Some(iface),
823 });
824 }
825
826 if let Some(pr_entry) = self.discovery_path_requests_waiting(&packet.destination_hash) {
828 let entry = AnnounceEntry {
830 timestamp: now,
831 retransmit_timeout: now,
832 retries: constants::PATHFINDER_R,
833 received_from,
834 hops: packet.hops,
835 packet_raw: packet.raw.clone(),
836 packet_data: packet.data.clone(),
837 destination_hash: packet.destination_hash,
838 context_flag: packet.flags.context_flag,
839 local_rebroadcasts: 0,
840 block_rebroadcasts: true,
841 attached_interface: Some(pr_entry),
842 };
843 self.announce_table
844 .insert(packet.destination_hash, entry);
845 }
846 }
847
848 fn discovery_path_requests_waiting(&self, _dest_hash: &[u8; 16]) -> Option<InterfaceId> {
850 None
853 }
854
855 fn process_inbound_proof(
860 &mut self,
861 packet: &RawPacket,
862 iface: InterfaceId,
863 _now: f64,
864 actions: &mut Vec<TransportAction>,
865 ) {
866 if packet.context == constants::CONTEXT_LRPROOF {
867 if (self.config.transport_enabled) && self.link_table.contains_key(&packet.destination_hash)
869 {
870 let link_entry = self.link_table.get(&packet.destination_hash).cloned();
871 if let Some(entry) = link_entry {
872 if packet.hops == entry.remaining_hops
873 && iface == entry.next_hop_interface
874 {
875 let mut new_raw = Vec::new();
878 new_raw.push(packet.raw[0]);
879 new_raw.push(packet.hops);
880 new_raw.extend_from_slice(&packet.raw[2..]);
881
882 if let Some(le) =
884 self.link_table.get_mut(&packet.destination_hash)
885 {
886 le.validated = true;
887 }
888
889 actions.push(TransportAction::SendOnInterface {
890 interface: entry.received_interface,
891 raw: new_raw,
892 });
893 }
894 }
895 } else {
896 actions.push(TransportAction::DeliverLocal {
898 destination_hash: packet.destination_hash,
899 raw: packet.raw.clone(),
900 packet_hash: packet.packet_hash,
901 });
902 }
903 } else {
904 if self.config.transport_enabled {
906 if let Some(reverse_entry) =
907 self.reverse_table.remove(&packet.destination_hash)
908 {
909 if let Some(action) =
910 route_proof_via_reverse(packet, &reverse_entry, iface)
911 {
912 actions.push(action);
913 }
914 }
915 }
916
917 actions.push(TransportAction::DeliverLocal {
919 destination_hash: packet.destination_hash,
920 raw: packet.raw.clone(),
921 packet_hash: packet.packet_hash,
922 });
923 }
924 }
925
926 pub fn handle_outbound(
932 &mut self,
933 packet: &RawPacket,
934 dest_type: u8,
935 attached_interface: Option<InterfaceId>,
936 now: f64,
937 ) -> Vec<TransportAction> {
938 let actions = route_outbound(
939 &self.path_table,
940 &self.interfaces,
941 &self.local_destinations,
942 packet,
943 dest_type,
944 attached_interface,
945 now,
946 );
947
948 self.packet_hashlist.add(packet.packet_hash);
950
951 if packet.flags.packet_type == constants::PACKET_TYPE_ANNOUNCE && packet.hops > 0 {
953 self.gate_announce_actions(actions, &packet.destination_hash, packet.hops, now)
954 } else {
955 actions
956 }
957 }
958
959 fn gate_announce_actions(
961 &mut self,
962 actions: Vec<TransportAction>,
963 dest_hash: &[u8; 16],
964 hops: u8,
965 now: f64,
966 ) -> Vec<TransportAction> {
967 let mut result = Vec::new();
968 for action in actions {
969 match action {
970 TransportAction::SendOnInterface { interface, raw } => {
971 let (bitrate, announce_cap) =
972 if let Some(info) = self.interfaces.get(&interface) {
973 (info.bitrate, info.announce_cap)
974 } else {
975 (None, constants::ANNOUNCE_CAP)
976 };
977 if let Some(send_action) = self.announce_queues.gate_announce(
978 interface,
979 raw,
980 *dest_hash,
981 hops,
982 now,
983 now,
984 bitrate,
985 announce_cap,
986 ) {
987 result.push(send_action);
988 }
989 }
991 other => result.push(other),
992 }
993 }
994 result
995 }
996
997 pub fn tick(&mut self, now: f64, _rng: &mut dyn Rng) -> Vec<TransportAction> {
1003 let mut actions = Vec::new();
1004
1005 if now > self.announces_last_checked + constants::ANNOUNCES_CHECK_INTERVAL {
1007 if let Some(ref identity_hash) = self.config.identity_hash {
1008 let ih = *identity_hash;
1009 let announce_actions = jobs::process_pending_announces(
1010 &mut self.announce_table,
1011 &mut self.held_announces,
1012 &ih,
1013 now,
1014 );
1015 let gated = self.gate_retransmit_actions(announce_actions, now);
1017 actions.extend(gated);
1018 }
1019 self.announces_last_checked = now;
1020 }
1021
1022 let mut queue_actions = self.announce_queues.process_queues(now, &self.interfaces);
1024 actions.append(&mut queue_actions);
1025
1026 if now > self.tables_last_culled + constants::TABLES_CULL_INTERVAL {
1028 jobs::cull_path_table(&mut self.path_table, &self.interfaces, now);
1029 jobs::cull_reverse_table(&mut self.reverse_table, &self.interfaces, now);
1030 jobs::cull_link_table(&mut self.link_table, &self.interfaces, now);
1031 jobs::cull_path_states(&mut self.path_states, &self.path_table);
1032 self.cull_blackholed(now);
1033 self.tunnel_table.void_missing_interfaces(|id| self.interfaces.contains_key(id));
1035 self.tunnel_table.cull(now);
1036 self.tables_last_culled = now;
1037 }
1038
1039 self.packet_hashlist.maybe_rotate();
1041
1042 if self.discovery_pr_tags.len() > constants::MAX_PR_TAGS {
1044 let start = self.discovery_pr_tags.len() - constants::MAX_PR_TAGS;
1045 self.discovery_pr_tags = self.discovery_pr_tags[start..].to_vec();
1046 }
1047
1048 actions
1049 }
1050
1051 fn gate_retransmit_actions(
1056 &mut self,
1057 actions: Vec<TransportAction>,
1058 now: f64,
1059 ) -> Vec<TransportAction> {
1060 let mut result = Vec::new();
1061 for action in actions {
1062 match action {
1063 TransportAction::SendOnInterface { interface, raw } => {
1064 let (dest_hash, hops) = Self::extract_announce_info(&raw);
1066 let (bitrate, announce_cap) =
1067 if let Some(info) = self.interfaces.get(&interface) {
1068 (info.bitrate, info.announce_cap)
1069 } else {
1070 (None, constants::ANNOUNCE_CAP)
1071 };
1072 if let Some(send_action) = self.announce_queues.gate_announce(
1073 interface,
1074 raw,
1075 dest_hash,
1076 hops,
1077 now,
1078 now,
1079 bitrate,
1080 announce_cap,
1081 ) {
1082 result.push(send_action);
1083 }
1084 }
1085 TransportAction::BroadcastOnAllInterfaces { raw, exclude } => {
1086 let (dest_hash, hops) = Self::extract_announce_info(&raw);
1087 let iface_ids: Vec<(InterfaceId, Option<u64>, f64)> = self
1090 .interfaces
1091 .iter()
1092 .filter(|(_, info)| info.out_capable)
1093 .filter(|(id, _)| {
1094 if let Some(ref ex) = exclude {
1095 **id != *ex
1096 } else {
1097 true
1098 }
1099 })
1100 .filter(|(_, info)| {
1101 should_transmit_announce(
1102 info,
1103 &dest_hash,
1104 hops,
1105 &self.local_destinations,
1106 &self.path_table,
1107 )
1108 })
1109 .map(|(id, info)| (*id, info.bitrate, info.announce_cap))
1110 .collect();
1111
1112 for (iface_id, bitrate, announce_cap) in iface_ids {
1113 if let Some(send_action) = self.announce_queues.gate_announce(
1114 iface_id,
1115 raw.clone(),
1116 dest_hash,
1117 hops,
1118 now,
1119 now,
1120 bitrate,
1121 announce_cap,
1122 ) {
1123 result.push(send_action);
1124 }
1125 }
1126 }
1127 other => result.push(other),
1128 }
1129 }
1130 result
1131 }
1132
1133 fn extract_announce_info(raw: &[u8]) -> ([u8; 16], u8) {
1135 if raw.len() < 18 {
1136 return ([0; 16], 0);
1137 }
1138 let header_type = (raw[0] >> 6) & 0x03;
1139 let hops = raw[1];
1140 if header_type == constants::HEADER_2 && raw.len() >= 34 {
1141 let mut dest = [0u8; 16];
1143 dest.copy_from_slice(&raw[18..34]);
1144 (dest, hops)
1145 } else {
1146 let mut dest = [0u8; 16];
1148 dest.copy_from_slice(&raw[2..18]);
1149 (dest, hops)
1150 }
1151 }
1152
1153 pub fn handle_path_request(
1159 &mut self,
1160 data: &[u8],
1161 interface_id: InterfaceId,
1162 now: f64,
1163 ) -> Vec<TransportAction> {
1164 let actions = Vec::new();
1165
1166 if data.len() < 16 {
1167 return actions;
1168 }
1169
1170 let mut destination_hash = [0u8; 16];
1171 destination_hash.copy_from_slice(&data[..16]);
1172
1173 let _requesting_transport_id = if data.len() > 32 {
1175 let mut id = [0u8; 16];
1176 id.copy_from_slice(&data[16..32]);
1177 Some(id)
1178 } else {
1179 None
1180 };
1181
1182 let tag_bytes = if data.len() > 32 {
1184 Some(&data[32..])
1185 } else if data.len() > 16 {
1186 Some(&data[16..])
1187 } else {
1188 None
1189 };
1190
1191 if let Some(tag) = tag_bytes {
1192 let tag_len = tag.len().min(16);
1193 let mut unique_tag = [0u8; 32];
1194 unique_tag[..16].copy_from_slice(&destination_hash);
1195 unique_tag[16..16 + tag_len].copy_from_slice(&tag[..tag_len]);
1196
1197 if self.discovery_pr_tags.contains(&unique_tag) {
1198 return actions; }
1200 self.discovery_pr_tags.push(unique_tag);
1201 } else {
1202 return actions; }
1204
1205 if self.local_destinations.contains_key(&destination_hash) {
1207 return actions;
1210 }
1211
1212 if (self.config.transport_enabled) && self.path_table.contains_key(&destination_hash) {
1214 let path = self.path_table.get(&destination_hash).unwrap();
1215 let received_from = path.next_hop;
1216 let hops = path.hops;
1217
1218 if let Some(existing) = self.announce_table.remove(&destination_hash) {
1220 self.held_announces.insert(destination_hash, existing);
1221 }
1222
1223 let retransmit_timeout = if let Some(iface_info) = self.interfaces.get(&interface_id) {
1224 let base = now + constants::PATH_REQUEST_GRACE;
1225 if iface_info.mode == constants::MODE_ROAMING {
1226 base + constants::PATH_REQUEST_RG
1227 } else {
1228 base
1229 }
1230 } else {
1231 now + constants::PATH_REQUEST_GRACE
1232 };
1233
1234 let entry = AnnounceEntry {
1239 timestamp: now,
1240 retransmit_timeout,
1241 retries: constants::PATHFINDER_R,
1242 received_from,
1243 hops,
1244 packet_raw: Vec::new(), packet_data: Vec::new(),
1246 destination_hash,
1247 context_flag: 0,
1248 local_rebroadcasts: 0,
1249 block_rebroadcasts: true,
1250 attached_interface: Some(interface_id),
1251 };
1252
1253 self.announce_table.insert(destination_hash, entry);
1254 } else if self.config.transport_enabled {
1255 for (_, iface_info) in self.interfaces.iter() {
1257 if iface_info.id != interface_id && iface_info.out_capable {
1258 }
1262 }
1263 }
1264
1265 actions
1266 }
1267
1268 pub fn path_table_entries(&self) -> impl Iterator<Item = (&[u8; 16], &PathEntry)> {
1274 self.path_table.iter()
1275 }
1276
1277 pub fn interface_count(&self) -> usize {
1279 self.interfaces.len()
1280 }
1281
1282 pub fn link_table_count(&self) -> usize {
1284 self.link_table.len()
1285 }
1286
1287 pub fn rate_limiter(&self) -> &AnnounceRateLimiter {
1289 &self.rate_limiter
1290 }
1291
1292 pub fn interface_info(&self, id: &InterfaceId) -> Option<&InterfaceInfo> {
1294 self.interfaces.get(id)
1295 }
1296
1297 pub fn drop_path(&mut self, dest_hash: &[u8; 16]) -> bool {
1299 self.path_table.remove(dest_hash).is_some()
1300 }
1301
1302 pub fn drop_all_via(&mut self, transport_hash: &[u8; 16]) -> usize {
1304 let before = self.path_table.len();
1305 self.path_table.retain(|_, entry| &entry.next_hop != transport_hash);
1306 before - self.path_table.len()
1307 }
1308
1309 pub fn drop_announce_queues(&mut self) {
1311 self.announce_table.clear();
1312 self.held_announces.clear();
1313 self.announce_queues = AnnounceQueues::new();
1314 }
1315
1316 pub fn identity_hash(&self) -> Option<&[u8; 16]> {
1318 self.config.identity_hash.as_ref()
1319 }
1320
1321 pub fn transport_enabled(&self) -> bool {
1323 self.config.transport_enabled
1324 }
1325
1326 pub fn config(&self) -> &TransportConfig {
1328 &self.config
1329 }
1330
1331 pub fn get_path_table(&self, max_hops: Option<u8>) -> Vec<([u8; 16], f64, [u8; 16], u8, f64, alloc::string::String)> {
1334 let mut result = Vec::new();
1335 for (dest_hash, entry) in self.path_table.iter() {
1336 if let Some(max) = max_hops {
1337 if entry.hops > max {
1338 continue;
1339 }
1340 }
1341 let iface_name = self.interfaces.get(&entry.receiving_interface)
1342 .map(|i| i.name.clone())
1343 .unwrap_or_else(|| alloc::format!("Interface({})", entry.receiving_interface.0));
1344 result.push((*dest_hash, entry.timestamp, entry.next_hop, entry.hops, entry.expires, iface_name));
1345 }
1346 result
1347 }
1348
1349 pub fn get_rate_table(&self) -> Vec<([u8; 16], f64, u32, f64, Vec<f64>)> {
1352 self.rate_limiter.entries()
1353 .map(|(hash, entry)| (*hash, entry.last, entry.rate_violations, entry.blocked_until, entry.timestamps.clone()))
1354 .collect()
1355 }
1356
1357 pub fn get_blackholed(&self) -> Vec<([u8; 16], f64, f64, Option<alloc::string::String>)> {
1360 self.blackholed_entries()
1361 .map(|(hash, entry)| (*hash, entry.created, entry.expires, entry.reason.clone()))
1362 .collect()
1363 }
1364
1365 #[cfg(test)]
1370 pub(crate) fn path_table(&self) -> &BTreeMap<[u8; 16], PathEntry> {
1371 &self.path_table
1372 }
1373
1374 #[cfg(test)]
1375 pub(crate) fn announce_table(&self) -> &BTreeMap<[u8; 16], AnnounceEntry> {
1376 &self.announce_table
1377 }
1378
1379 #[cfg(test)]
1380 pub(crate) fn reverse_table(&self) -> &BTreeMap<[u8; 16], tables::ReverseEntry> {
1381 &self.reverse_table
1382 }
1383
1384 #[cfg(test)]
1385 pub(crate) fn link_table_ref(&self) -> &BTreeMap<[u8; 16], LinkEntry> {
1386 &self.link_table
1387 }
1388}
1389
1390#[cfg(test)]
1391mod tests {
1392 use super::*;
1393 use crate::packet::PacketFlags;
1394
1395 fn make_config(transport_enabled: bool) -> TransportConfig {
1396 TransportConfig {
1397 transport_enabled,
1398 identity_hash: if transport_enabled {
1399 Some([0x42; 16])
1400 } else {
1401 None
1402 },
1403 }
1404 }
1405
1406 fn make_interface(id: u64, mode: u8) -> InterfaceInfo {
1407 InterfaceInfo {
1408 id: InterfaceId(id),
1409 name: String::from("test"),
1410 mode,
1411 out_capable: true,
1412 in_capable: true,
1413 bitrate: None,
1414 announce_rate_target: None,
1415 announce_rate_grace: 0,
1416 announce_rate_penalty: 0.0,
1417 announce_cap: constants::ANNOUNCE_CAP,
1418 is_local_client: false,
1419 wants_tunnel: false,
1420 tunnel_id: None,
1421 }
1422 }
1423
1424 #[test]
1425 fn test_empty_engine() {
1426 let engine = TransportEngine::new(make_config(false));
1427 assert!(!engine.has_path(&[0; 16]));
1428 assert!(engine.hops_to(&[0; 16]).is_none());
1429 assert!(engine.next_hop(&[0; 16]).is_none());
1430 }
1431
1432 #[test]
1433 fn test_register_deregister_interface() {
1434 let mut engine = TransportEngine::new(make_config(false));
1435 engine.register_interface(make_interface(1, constants::MODE_FULL));
1436 assert!(engine.interfaces.contains_key(&InterfaceId(1)));
1437
1438 engine.deregister_interface(InterfaceId(1));
1439 assert!(!engine.interfaces.contains_key(&InterfaceId(1)));
1440 }
1441
1442 #[test]
1443 fn test_register_deregister_destination() {
1444 let mut engine = TransportEngine::new(make_config(false));
1445 let dest = [0x11; 16];
1446 engine.register_destination(dest, constants::DESTINATION_SINGLE);
1447 assert!(engine.local_destinations.contains_key(&dest));
1448
1449 engine.deregister_destination(&dest);
1450 assert!(!engine.local_destinations.contains_key(&dest));
1451 }
1452
1453 #[test]
1454 fn test_path_state() {
1455 let mut engine = TransportEngine::new(make_config(false));
1456 let dest = [0x22; 16];
1457
1458 assert!(!engine.path_is_unresponsive(&dest));
1459
1460 engine.mark_path_unresponsive(&dest);
1461 assert!(engine.path_is_unresponsive(&dest));
1462
1463 engine.mark_path_responsive(&dest);
1464 assert!(!engine.path_is_unresponsive(&dest));
1465 }
1466
1467 #[test]
1468 fn test_expire_path() {
1469 let mut engine = TransportEngine::new(make_config(false));
1470 let dest = [0x33; 16];
1471
1472 engine.path_table.insert(
1473 dest,
1474 PathEntry {
1475 timestamp: 1000.0,
1476 next_hop: [0; 16],
1477 hops: 2,
1478 expires: 9999.0,
1479 random_blobs: Vec::new(),
1480 receiving_interface: InterfaceId(1),
1481 packet_hash: [0; 32],
1482 announce_raw: None,
1483 },
1484 );
1485
1486 assert!(engine.has_path(&dest));
1487 engine.expire_path(&dest);
1488 assert!(engine.has_path(&dest));
1490 assert_eq!(engine.path_table[&dest].expires, 0.0);
1491 }
1492
1493 #[test]
1494 fn test_link_table_operations() {
1495 let mut engine = TransportEngine::new(make_config(false));
1496 let link_id = [0x44; 16];
1497
1498 engine.register_link(
1499 link_id,
1500 LinkEntry {
1501 timestamp: 100.0,
1502 next_hop_transport_id: [0; 16],
1503 next_hop_interface: InterfaceId(1),
1504 remaining_hops: 3,
1505 received_interface: InterfaceId(2),
1506 taken_hops: 2,
1507 destination_hash: [0xAA; 16],
1508 validated: false,
1509 proof_timeout: 200.0,
1510 },
1511 );
1512
1513 assert!(engine.link_table.contains_key(&link_id));
1514 assert!(!engine.link_table[&link_id].validated);
1515
1516 engine.validate_link(&link_id);
1517 assert!(engine.link_table[&link_id].validated);
1518
1519 engine.remove_link(&link_id);
1520 assert!(!engine.link_table.contains_key(&link_id));
1521 }
1522
1523 #[test]
1524 fn test_packet_filter_drops_plain_announce() {
1525 let engine = TransportEngine::new(make_config(false));
1526 let flags = PacketFlags {
1527 header_type: constants::HEADER_1,
1528 context_flag: constants::FLAG_UNSET,
1529 transport_type: constants::TRANSPORT_BROADCAST,
1530 destination_type: constants::DESTINATION_PLAIN,
1531 packet_type: constants::PACKET_TYPE_ANNOUNCE,
1532 };
1533 let packet =
1534 RawPacket::pack(flags, 0, &[0; 16], None, constants::CONTEXT_NONE, b"test").unwrap();
1535 assert!(!engine.packet_filter(&packet));
1536 }
1537
1538 #[test]
1539 fn test_packet_filter_allows_keepalive() {
1540 let engine = TransportEngine::new(make_config(false));
1541 let flags = PacketFlags {
1542 header_type: constants::HEADER_1,
1543 context_flag: constants::FLAG_UNSET,
1544 transport_type: constants::TRANSPORT_BROADCAST,
1545 destination_type: constants::DESTINATION_SINGLE,
1546 packet_type: constants::PACKET_TYPE_DATA,
1547 };
1548 let packet = RawPacket::pack(
1549 flags,
1550 0,
1551 &[0; 16],
1552 None,
1553 constants::CONTEXT_KEEPALIVE,
1554 b"test",
1555 )
1556 .unwrap();
1557 assert!(engine.packet_filter(&packet));
1558 }
1559
1560 #[test]
1561 fn test_packet_filter_drops_high_hop_plain() {
1562 let engine = TransportEngine::new(make_config(false));
1563 let flags = PacketFlags {
1564 header_type: constants::HEADER_1,
1565 context_flag: constants::FLAG_UNSET,
1566 transport_type: constants::TRANSPORT_BROADCAST,
1567 destination_type: constants::DESTINATION_PLAIN,
1568 packet_type: constants::PACKET_TYPE_DATA,
1569 };
1570 let mut packet =
1571 RawPacket::pack(flags, 0, &[0; 16], None, constants::CONTEXT_NONE, b"test").unwrap();
1572 packet.hops = 2;
1573 assert!(!engine.packet_filter(&packet));
1574 }
1575
1576 #[test]
1577 fn test_packet_filter_allows_duplicate_single_announce() {
1578 let mut engine = TransportEngine::new(make_config(false));
1579 let flags = PacketFlags {
1580 header_type: constants::HEADER_1,
1581 context_flag: constants::FLAG_UNSET,
1582 transport_type: constants::TRANSPORT_BROADCAST,
1583 destination_type: constants::DESTINATION_SINGLE,
1584 packet_type: constants::PACKET_TYPE_ANNOUNCE,
1585 };
1586 let packet =
1587 RawPacket::pack(flags, 0, &[0; 16], None, constants::CONTEXT_NONE, &[0xAA; 64])
1588 .unwrap();
1589
1590 engine.packet_hashlist.add(packet.packet_hash);
1592
1593 assert!(engine.packet_filter(&packet));
1595 }
1596
1597 #[test]
1598 fn test_tick_retransmits_announce() {
1599 let mut engine = TransportEngine::new(make_config(true));
1600 engine.register_interface(make_interface(1, constants::MODE_FULL));
1601
1602 let dest = [0x55; 16];
1603 engine.announce_table.insert(
1604 dest,
1605 AnnounceEntry {
1606 timestamp: 100.0,
1607 retransmit_timeout: 100.0, retries: 0,
1609 received_from: [0xAA; 16],
1610 hops: 2,
1611 packet_raw: vec![0x01, 0x02],
1612 packet_data: vec![0xCC; 10],
1613 destination_hash: dest,
1614 context_flag: 0,
1615 local_rebroadcasts: 0,
1616 block_rebroadcasts: false,
1617 attached_interface: None,
1618 },
1619 );
1620
1621 let mut rng = rns_crypto::FixedRng::new(&[0x42; 32]);
1622 let actions = engine.tick(200.0, &mut rng);
1623
1624 assert!(!actions.is_empty());
1627 assert!(matches!(
1628 &actions[0],
1629 TransportAction::SendOnInterface { .. }
1630 ));
1631
1632 assert_eq!(engine.announce_table[&dest].retries, 1);
1634 }
1635
1636 #[test]
1637 fn test_blackhole_identity() {
1638 let mut engine = TransportEngine::new(make_config(false));
1639 let hash = [0xAA; 16];
1640 let now = 1000.0;
1641
1642 assert!(!engine.is_blackholed(&hash, now));
1643
1644 engine.blackhole_identity(hash, now, None, Some(String::from("test")));
1645 assert!(engine.is_blackholed(&hash, now));
1646 assert!(engine.is_blackholed(&hash, now + 999999.0)); assert!(engine.unblackhole_identity(&hash));
1649 assert!(!engine.is_blackholed(&hash, now));
1650 assert!(!engine.unblackhole_identity(&hash)); }
1652
1653 #[test]
1654 fn test_blackhole_with_duration() {
1655 let mut engine = TransportEngine::new(make_config(false));
1656 let hash = [0xBB; 16];
1657 let now = 1000.0;
1658
1659 engine.blackhole_identity(hash, now, Some(1.0), None); assert!(engine.is_blackholed(&hash, now));
1661 assert!(engine.is_blackholed(&hash, now + 3599.0)); assert!(!engine.is_blackholed(&hash, now + 3601.0)); }
1664
1665 #[test]
1666 fn test_cull_blackholed() {
1667 let mut engine = TransportEngine::new(make_config(false));
1668 let hash1 = [0xCC; 16];
1669 let hash2 = [0xDD; 16];
1670 let now = 1000.0;
1671
1672 engine.blackhole_identity(hash1, now, Some(1.0), None); engine.blackhole_identity(hash2, now, None, None); engine.cull_blackholed(now + 4000.0); assert!(!engine.blackholed_identities.contains_key(&hash1));
1678 assert!(engine.blackholed_identities.contains_key(&hash2));
1679 }
1680
1681 #[test]
1682 fn test_blackhole_blocks_announce() {
1683 use crate::announce::AnnounceData;
1684 use crate::destination::{destination_hash, name_hash};
1685
1686 let mut engine = TransportEngine::new(make_config(false));
1687 engine.register_interface(make_interface(1, constants::MODE_FULL));
1688
1689 let identity = rns_crypto::identity::Identity::new(&mut rns_crypto::FixedRng::new(&[0x55; 32]));
1690 let dest_hash = destination_hash("test", &["app"], Some(identity.hash()));
1691 let name_h = name_hash("test", &["app"]);
1692 let random_hash = [0x42u8; 10];
1693
1694 let (announce_data, _) = AnnounceData::pack(
1695 &identity, &dest_hash, &name_h, &random_hash, None, None,
1696 ).unwrap();
1697
1698 let flags = PacketFlags {
1699 header_type: constants::HEADER_1,
1700 context_flag: constants::FLAG_UNSET,
1701 transport_type: constants::TRANSPORT_BROADCAST,
1702 destination_type: constants::DESTINATION_SINGLE,
1703 packet_type: constants::PACKET_TYPE_ANNOUNCE,
1704 };
1705 let packet = RawPacket::pack(flags, 0, &dest_hash, None, constants::CONTEXT_NONE, &announce_data).unwrap();
1706
1707 let now = 1000.0;
1709 engine.blackhole_identity(*identity.hash(), now, None, None);
1710
1711 let mut rng = rns_crypto::FixedRng::new(&[0x11; 32]);
1712 let actions = engine.handle_inbound(&packet.raw, InterfaceId(1), now, &mut rng);
1713
1714 assert!(actions.iter().all(|a| !matches!(a, TransportAction::AnnounceReceived { .. })));
1716 assert!(actions.iter().all(|a| !matches!(a, TransportAction::PathUpdated { .. })));
1717 }
1718
1719 #[test]
1720 fn test_tick_culls_expired_path() {
1721 let mut engine = TransportEngine::new(make_config(false));
1722 engine.register_interface(make_interface(1, constants::MODE_FULL));
1723
1724 let dest = [0x66; 16];
1725 engine.path_table.insert(
1726 dest,
1727 PathEntry {
1728 timestamp: 100.0,
1729 next_hop: [0; 16],
1730 hops: 2,
1731 expires: 200.0,
1732 random_blobs: Vec::new(),
1733 receiving_interface: InterfaceId(1),
1734 packet_hash: [0; 32],
1735 announce_raw: None,
1736 },
1737 );
1738
1739 assert!(engine.has_path(&dest));
1740
1741 let mut rng = rns_crypto::FixedRng::new(&[0; 32]);
1742 engine.tick(300.0, &mut rng);
1744
1745 assert!(!engine.has_path(&dest));
1746 }
1747
1748 fn make_local_client_interface(id: u64) -> InterfaceInfo {
1753 InterfaceInfo {
1754 id: InterfaceId(id),
1755 name: String::from("local_client"),
1756 mode: constants::MODE_FULL,
1757 out_capable: true,
1758 in_capable: true,
1759 bitrate: None,
1760 announce_rate_target: None,
1761 announce_rate_grace: 0,
1762 announce_rate_penalty: 0.0,
1763 announce_cap: constants::ANNOUNCE_CAP,
1764 is_local_client: true,
1765 wants_tunnel: false,
1766 tunnel_id: None,
1767 }
1768 }
1769
1770 #[test]
1771 fn test_has_local_clients() {
1772 let mut engine = TransportEngine::new(make_config(false));
1773 assert!(!engine.has_local_clients());
1774
1775 engine.register_interface(make_interface(1, constants::MODE_FULL));
1776 assert!(!engine.has_local_clients());
1777
1778 engine.register_interface(make_local_client_interface(2));
1779 assert!(engine.has_local_clients());
1780
1781 engine.deregister_interface(InterfaceId(2));
1782 assert!(!engine.has_local_clients());
1783 }
1784
1785 #[test]
1786 fn test_local_client_hop_decrement() {
1787 let mut engine = TransportEngine::new(make_config(false));
1790 engine.register_interface(make_local_client_interface(1));
1791 engine.register_interface(make_interface(2, constants::MODE_FULL));
1792
1793 let dest = [0xAA; 16];
1795 engine.register_destination(dest, constants::DESTINATION_PLAIN);
1796
1797 let flags = PacketFlags {
1798 header_type: constants::HEADER_1,
1799 context_flag: constants::FLAG_UNSET,
1800 transport_type: constants::TRANSPORT_BROADCAST,
1801 destination_type: constants::DESTINATION_PLAIN,
1802 packet_type: constants::PACKET_TYPE_DATA,
1803 };
1804 let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"hello").unwrap();
1806
1807 let mut rng = rns_crypto::FixedRng::new(&[0; 32]);
1808 let actions = engine.handle_inbound(&packet.raw, InterfaceId(1), 1000.0, &mut rng);
1809
1810 let deliver = actions.iter().find(|a| matches!(a, TransportAction::DeliverLocal { .. }));
1813 assert!(deliver.is_some(), "Should deliver locally");
1814 }
1815
1816 #[test]
1817 fn test_plain_broadcast_from_local_client() {
1818 let mut engine = TransportEngine::new(make_config(false));
1820 engine.register_interface(make_local_client_interface(1));
1821 engine.register_interface(make_interface(2, constants::MODE_FULL));
1822
1823 let dest = [0xBB; 16];
1824 let flags = PacketFlags {
1825 header_type: constants::HEADER_1,
1826 context_flag: constants::FLAG_UNSET,
1827 transport_type: constants::TRANSPORT_BROADCAST,
1828 destination_type: constants::DESTINATION_PLAIN,
1829 packet_type: constants::PACKET_TYPE_DATA,
1830 };
1831 let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"test").unwrap();
1832
1833 let mut rng = rns_crypto::FixedRng::new(&[0; 32]);
1834 let actions = engine.handle_inbound(&packet.raw, InterfaceId(1), 1000.0, &mut rng);
1835
1836 let forward = actions.iter().find(|a| matches!(
1838 a, TransportAction::ForwardPlainBroadcast { to_local: false, .. }
1839 ));
1840 assert!(forward.is_some(), "Should forward to external interfaces");
1841 }
1842
1843 #[test]
1844 fn test_plain_broadcast_from_external() {
1845 let mut engine = TransportEngine::new(make_config(false));
1847 engine.register_interface(make_local_client_interface(1));
1848 engine.register_interface(make_interface(2, constants::MODE_FULL));
1849
1850 let dest = [0xCC; 16];
1851 let flags = PacketFlags {
1852 header_type: constants::HEADER_1,
1853 context_flag: constants::FLAG_UNSET,
1854 transport_type: constants::TRANSPORT_BROADCAST,
1855 destination_type: constants::DESTINATION_PLAIN,
1856 packet_type: constants::PACKET_TYPE_DATA,
1857 };
1858 let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"test").unwrap();
1859
1860 let mut rng = rns_crypto::FixedRng::new(&[0; 32]);
1861 let actions = engine.handle_inbound(&packet.raw, InterfaceId(2), 1000.0, &mut rng);
1862
1863 let forward = actions.iter().find(|a| matches!(
1865 a, TransportAction::ForwardPlainBroadcast { to_local: true, .. }
1866 ));
1867 assert!(forward.is_some(), "Should forward to local clients");
1868 }
1869
1870 #[test]
1871 fn test_no_plain_broadcast_bridging_without_local_clients() {
1872 let mut engine = TransportEngine::new(make_config(false));
1874 engine.register_interface(make_interface(1, constants::MODE_FULL));
1875 engine.register_interface(make_interface(2, constants::MODE_FULL));
1876
1877 let dest = [0xDD; 16];
1878 let flags = PacketFlags {
1879 header_type: constants::HEADER_1,
1880 context_flag: constants::FLAG_UNSET,
1881 transport_type: constants::TRANSPORT_BROADCAST,
1882 destination_type: constants::DESTINATION_PLAIN,
1883 packet_type: constants::PACKET_TYPE_DATA,
1884 };
1885 let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"test").unwrap();
1886
1887 let mut rng = rns_crypto::FixedRng::new(&[0; 32]);
1888 let actions = engine.handle_inbound(&packet.raw, InterfaceId(1), 1000.0, &mut rng);
1889
1890 let has_forward = actions.iter().any(|a| matches!(
1892 a, TransportAction::ForwardPlainBroadcast { .. }
1893 ));
1894 assert!(!has_forward, "No bridging without local clients");
1895 }
1896
1897 #[test]
1898 fn test_announce_forwarded_to_local_clients() {
1899 use crate::announce::AnnounceData;
1900 use crate::destination::{destination_hash, name_hash};
1901
1902 let mut engine = TransportEngine::new(make_config(false));
1903 engine.register_interface(make_interface(1, constants::MODE_FULL));
1904 engine.register_interface(make_local_client_interface(2));
1905
1906 let identity = rns_crypto::identity::Identity::new(&mut rns_crypto::FixedRng::new(&[0x77; 32]));
1907 let dest_hash = destination_hash("test", &["fwd"], Some(identity.hash()));
1908 let name_h = name_hash("test", &["fwd"]);
1909 let random_hash = [0x42u8; 10];
1910
1911 let (announce_data, _) = AnnounceData::pack(
1912 &identity, &dest_hash, &name_h, &random_hash, None, None,
1913 ).unwrap();
1914
1915 let flags = PacketFlags {
1916 header_type: constants::HEADER_1,
1917 context_flag: constants::FLAG_UNSET,
1918 transport_type: constants::TRANSPORT_BROADCAST,
1919 destination_type: constants::DESTINATION_SINGLE,
1920 packet_type: constants::PACKET_TYPE_ANNOUNCE,
1921 };
1922 let packet = RawPacket::pack(flags, 0, &dest_hash, None, constants::CONTEXT_NONE, &announce_data).unwrap();
1923
1924 let mut rng = rns_crypto::FixedRng::new(&[0x11; 32]);
1925 let actions = engine.handle_inbound(&packet.raw, InterfaceId(1), 1000.0, &mut rng);
1926
1927 let forward = actions.iter().find(|a| matches!(
1929 a, TransportAction::ForwardToLocalClients { .. }
1930 ));
1931 assert!(forward.is_some(), "Should forward announce to local clients");
1932
1933 match forward.unwrap() {
1935 TransportAction::ForwardToLocalClients { exclude, .. } => {
1936 assert_eq!(*exclude, Some(InterfaceId(1)));
1937 }
1938 _ => unreachable!(),
1939 }
1940 }
1941
1942 #[test]
1943 fn test_no_announce_forward_without_local_clients() {
1944 use crate::announce::AnnounceData;
1945 use crate::destination::{destination_hash, name_hash};
1946
1947 let mut engine = TransportEngine::new(make_config(false));
1948 engine.register_interface(make_interface(1, constants::MODE_FULL));
1949
1950 let identity = rns_crypto::identity::Identity::new(&mut rns_crypto::FixedRng::new(&[0x88; 32]));
1951 let dest_hash = destination_hash("test", &["nofwd"], Some(identity.hash()));
1952 let name_h = name_hash("test", &["nofwd"]);
1953 let random_hash = [0x42u8; 10];
1954
1955 let (announce_data, _) = AnnounceData::pack(
1956 &identity, &dest_hash, &name_h, &random_hash, None, None,
1957 ).unwrap();
1958
1959 let flags = PacketFlags {
1960 header_type: constants::HEADER_1,
1961 context_flag: constants::FLAG_UNSET,
1962 transport_type: constants::TRANSPORT_BROADCAST,
1963 destination_type: constants::DESTINATION_SINGLE,
1964 packet_type: constants::PACKET_TYPE_ANNOUNCE,
1965 };
1966 let packet = RawPacket::pack(flags, 0, &dest_hash, None, constants::CONTEXT_NONE, &announce_data).unwrap();
1967
1968 let mut rng = rns_crypto::FixedRng::new(&[0x22; 32]);
1969 let actions = engine.handle_inbound(&packet.raw, InterfaceId(1), 1000.0, &mut rng);
1970
1971 let has_forward = actions.iter().any(|a| matches!(
1973 a, TransportAction::ForwardToLocalClients { .. }
1974 ));
1975 assert!(!has_forward, "No forward without local clients");
1976 }
1977
1978 #[test]
1979 fn test_local_client_exclude_from_forward() {
1980 use crate::announce::AnnounceData;
1981 use crate::destination::{destination_hash, name_hash};
1982
1983 let mut engine = TransportEngine::new(make_config(false));
1984 engine.register_interface(make_local_client_interface(1));
1985 engine.register_interface(make_local_client_interface(2));
1986
1987 let identity = rns_crypto::identity::Identity::new(&mut rns_crypto::FixedRng::new(&[0x99; 32]));
1988 let dest_hash = destination_hash("test", &["excl"], Some(identity.hash()));
1989 let name_h = name_hash("test", &["excl"]);
1990 let random_hash = [0x42u8; 10];
1991
1992 let (announce_data, _) = AnnounceData::pack(
1993 &identity, &dest_hash, &name_h, &random_hash, None, None,
1994 ).unwrap();
1995
1996 let flags = PacketFlags {
1997 header_type: constants::HEADER_1,
1998 context_flag: constants::FLAG_UNSET,
1999 transport_type: constants::TRANSPORT_BROADCAST,
2000 destination_type: constants::DESTINATION_SINGLE,
2001 packet_type: constants::PACKET_TYPE_ANNOUNCE,
2002 };
2003 let packet = RawPacket::pack(flags, 0, &dest_hash, None, constants::CONTEXT_NONE, &announce_data).unwrap();
2004
2005 let mut rng = rns_crypto::FixedRng::new(&[0x33; 32]);
2006 let actions = engine.handle_inbound(&packet.raw, InterfaceId(1), 1000.0, &mut rng);
2008
2009 let forward = actions.iter().find(|a| matches!(
2011 a, TransportAction::ForwardToLocalClients { .. }
2012 ));
2013 assert!(forward.is_some());
2014 match forward.unwrap() {
2015 TransportAction::ForwardToLocalClients { exclude, .. } => {
2016 assert_eq!(*exclude, Some(InterfaceId(1)));
2017 }
2018 _ => unreachable!(),
2019 }
2020 }
2021
2022 fn make_tunnel_interface(id: u64) -> InterfaceInfo {
2027 InterfaceInfo {
2028 id: InterfaceId(id),
2029 name: String::from("tunnel_iface"),
2030 mode: constants::MODE_FULL,
2031 out_capable: true,
2032 in_capable: true,
2033 bitrate: None,
2034 announce_rate_target: None,
2035 announce_rate_grace: 0,
2036 announce_rate_penalty: 0.0,
2037 announce_cap: constants::ANNOUNCE_CAP,
2038 is_local_client: false,
2039 wants_tunnel: true,
2040 tunnel_id: None,
2041 }
2042 }
2043
2044 #[test]
2045 fn test_handle_tunnel_new() {
2046 let mut engine = TransportEngine::new(make_config(true));
2047 engine.register_interface(make_tunnel_interface(1));
2048
2049 let tunnel_id = [0xAA; 32];
2050 let actions = engine.handle_tunnel(tunnel_id, InterfaceId(1), 1000.0);
2051
2052 assert!(actions.iter().any(|a| matches!(
2054 a, TransportAction::TunnelEstablished { .. }
2055 )));
2056
2057 let info = engine.interface_info(&InterfaceId(1)).unwrap();
2059 assert_eq!(info.tunnel_id, Some(tunnel_id));
2060
2061 assert_eq!(engine.tunnel_table().len(), 1);
2063 }
2064
2065 #[test]
2066 fn test_announce_stores_tunnel_path() {
2067 use crate::announce::AnnounceData;
2068 use crate::destination::{destination_hash, name_hash};
2069
2070 let mut engine = TransportEngine::new(make_config(false));
2071 let mut iface = make_tunnel_interface(1);
2072 let tunnel_id = [0xBB; 32];
2073 iface.tunnel_id = Some(tunnel_id);
2074 engine.register_interface(iface);
2075
2076 engine.handle_tunnel(tunnel_id, InterfaceId(1), 1000.0);
2078
2079 let identity = rns_crypto::identity::Identity::new(&mut rns_crypto::FixedRng::new(&[0xCC; 32]));
2081 let dest_hash = destination_hash("test", &["tunnel"], Some(identity.hash()));
2082 let name_h = name_hash("test", &["tunnel"]);
2083 let random_hash = [0x42u8; 10];
2084
2085 let (announce_data, _) = AnnounceData::pack(
2086 &identity, &dest_hash, &name_h, &random_hash, None, None,
2087 ).unwrap();
2088
2089 let flags = PacketFlags {
2090 header_type: constants::HEADER_1,
2091 context_flag: constants::FLAG_UNSET,
2092 transport_type: constants::TRANSPORT_BROADCAST,
2093 destination_type: constants::DESTINATION_SINGLE,
2094 packet_type: constants::PACKET_TYPE_ANNOUNCE,
2095 };
2096 let packet = RawPacket::pack(flags, 0, &dest_hash, None, constants::CONTEXT_NONE, &announce_data).unwrap();
2097
2098 let mut rng = rns_crypto::FixedRng::new(&[0xDD; 32]);
2099 engine.handle_inbound(&packet.raw, InterfaceId(1), 1000.0, &mut rng);
2100
2101 assert!(engine.has_path(&dest_hash));
2103
2104 let tunnel = engine.tunnel_table().get(&tunnel_id).unwrap();
2106 assert_eq!(tunnel.paths.len(), 1);
2107 assert!(tunnel.paths.contains_key(&dest_hash));
2108 }
2109
2110 #[test]
2111 fn test_tunnel_reattach_restores_paths() {
2112 let mut engine = TransportEngine::new(make_config(true));
2113 engine.register_interface(make_tunnel_interface(1));
2114
2115 let tunnel_id = [0xCC; 32];
2116 engine.handle_tunnel(tunnel_id, InterfaceId(1), 1000.0);
2117
2118 let dest = [0xDD; 16];
2120 engine.tunnel_table.store_tunnel_path(
2121 &tunnel_id,
2122 dest,
2123 tunnel::TunnelPath {
2124 timestamp: 1000.0,
2125 received_from: [0xEE; 16],
2126 hops: 3,
2127 expires: 1000.0 + constants::DESTINATION_TIMEOUT,
2128 random_blobs: Vec::new(),
2129 packet_hash: [0xFF; 32],
2130 },
2131 1000.0,
2132 );
2133
2134 engine.void_tunnel_interface(&tunnel_id);
2136
2137 engine.path_table.remove(&dest);
2139 assert!(!engine.has_path(&dest));
2140
2141 engine.register_interface(make_interface(2, constants::MODE_FULL));
2143 let actions = engine.handle_tunnel(tunnel_id, InterfaceId(2), 2000.0);
2144
2145 assert!(engine.has_path(&dest));
2147 let path = engine.path_table.get(&dest).unwrap();
2148 assert_eq!(path.hops, 3);
2149 assert_eq!(path.receiving_interface, InterfaceId(2));
2150
2151 assert!(actions.iter().any(|a| matches!(
2153 a, TransportAction::TunnelEstablished { .. }
2154 )));
2155 }
2156
2157 #[test]
2158 fn test_void_tunnel_interface() {
2159 let mut engine = TransportEngine::new(make_config(true));
2160 engine.register_interface(make_tunnel_interface(1));
2161
2162 let tunnel_id = [0xDD; 32];
2163 engine.handle_tunnel(tunnel_id, InterfaceId(1), 1000.0);
2164
2165 assert_eq!(
2167 engine.tunnel_table().get(&tunnel_id).unwrap().interface,
2168 Some(InterfaceId(1))
2169 );
2170
2171 engine.void_tunnel_interface(&tunnel_id);
2172
2173 assert_eq!(engine.tunnel_table().len(), 1);
2175 assert_eq!(
2176 engine.tunnel_table().get(&tunnel_id).unwrap().interface,
2177 None
2178 );
2179 }
2180
2181 #[test]
2182 fn test_tick_culls_tunnels() {
2183 let mut engine = TransportEngine::new(make_config(true));
2184 engine.register_interface(make_tunnel_interface(1));
2185
2186 let tunnel_id = [0xEE; 32];
2187 engine.handle_tunnel(tunnel_id, InterfaceId(1), 1000.0);
2188 assert_eq!(engine.tunnel_table().len(), 1);
2189
2190 let mut rng = rns_crypto::FixedRng::new(&[0; 32]);
2191
2192 engine.tick(1000.0 + constants::DESTINATION_TIMEOUT + constants::TABLES_CULL_INTERVAL + 1.0, &mut rng);
2194
2195 assert_eq!(engine.tunnel_table().len(), 0);
2196 }
2197
2198 #[test]
2199 fn test_synthesize_tunnel() {
2200 let mut engine = TransportEngine::new(make_config(true));
2201 engine.register_interface(make_tunnel_interface(1));
2202
2203 let identity = rns_crypto::identity::Identity::new(&mut rns_crypto::FixedRng::new(&[0xFF; 32]));
2204 let mut rng = rns_crypto::FixedRng::new(&[0x11; 32]);
2205
2206 let actions = engine.synthesize_tunnel(&identity, InterfaceId(1), &mut rng);
2207
2208 assert_eq!(actions.len(), 1);
2210 match &actions[0] {
2211 TransportAction::TunnelSynthesize { interface, data, dest_hash } => {
2212 assert_eq!(*interface, InterfaceId(1));
2213 assert_eq!(data.len(), tunnel::TUNNEL_SYNTH_LENGTH);
2214 let expected_dest = crate::destination::destination_hash(
2216 "rnstransport", &["tunnel", "synthesize"], None,
2217 );
2218 assert_eq!(*dest_hash, expected_dest);
2219 }
2220 _ => panic!("Expected TunnelSynthesize"),
2221 }
2222 }
2223}