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 ingress_control;
12pub mod jobs;
13
14use alloc::collections::BTreeMap;
15use alloc::string::String;
16use alloc::vec::Vec;
17
18use rns_crypto::Rng;
19
20use crate::announce::AnnounceData;
21use crate::constants;
22use crate::hash;
23use crate::packet::RawPacket;
24
25use self::announce_proc::compute_path_expires;
26use self::announce_queue::AnnounceQueues;
27use self::dedup::PacketHashlist;
28use self::tunnel::TunnelTable;
29use self::inbound::{
30 create_link_entry, create_reverse_entry, forward_transport_packet,
31 route_proof_via_reverse, route_via_link_table,
32};
33use self::outbound::{route_outbound, should_transmit_announce};
34use self::pathfinder::{
35 extract_random_blob, should_update_path, timebase_from_random_blob, PathDecision,
36};
37use self::ingress_control::IngressControl;
38use self::rate_limit::AnnounceRateLimiter;
39use self::tables::{AnnounceEntry, DiscoveryPathRequest, LinkEntry, PathEntry};
40use self::types::{BlackholeEntry, InterfaceId, InterfaceInfo, TransportAction, TransportConfig};
41
42pub struct TransportEngine {
47 config: TransportConfig,
48 path_table: BTreeMap<[u8; 16], PathEntry>,
49 announce_table: BTreeMap<[u8; 16], AnnounceEntry>,
50 reverse_table: BTreeMap<[u8; 16], tables::ReverseEntry>,
51 link_table: BTreeMap<[u8; 16], LinkEntry>,
52 held_announces: BTreeMap<[u8; 16], AnnounceEntry>,
53 packet_hashlist: PacketHashlist,
54 rate_limiter: AnnounceRateLimiter,
55 path_states: BTreeMap<[u8; 16], u8>,
56 interfaces: BTreeMap<InterfaceId, InterfaceInfo>,
57 local_destinations: BTreeMap<[u8; 16], u8>,
58 blackholed_identities: BTreeMap<[u8; 16], BlackholeEntry>,
59 announce_queues: AnnounceQueues,
60 ingress_control: IngressControl,
61 tunnel_table: TunnelTable,
62 discovery_pr_tags: Vec<[u8; 32]>,
63 discovery_path_requests: BTreeMap<[u8; 16], DiscoveryPathRequest>,
64 announces_last_checked: f64,
66 tables_last_culled: f64,
67}
68
69impl TransportEngine {
70 pub fn new(config: TransportConfig) -> Self {
71 TransportEngine {
72 config,
73 path_table: BTreeMap::new(),
74 announce_table: BTreeMap::new(),
75 reverse_table: BTreeMap::new(),
76 link_table: BTreeMap::new(),
77 held_announces: BTreeMap::new(),
78 packet_hashlist: PacketHashlist::new(constants::HASHLIST_MAXSIZE),
79 rate_limiter: AnnounceRateLimiter::new(),
80 path_states: BTreeMap::new(),
81 interfaces: BTreeMap::new(),
82 local_destinations: BTreeMap::new(),
83 blackholed_identities: BTreeMap::new(),
84 announce_queues: AnnounceQueues::new(),
85 ingress_control: IngressControl::new(),
86 tunnel_table: TunnelTable::new(),
87 discovery_pr_tags: Vec::new(),
88 discovery_path_requests: BTreeMap::new(),
89 announces_last_checked: 0.0,
90 tables_last_culled: 0.0,
91 }
92 }
93
94 pub fn register_interface(&mut self, info: InterfaceInfo) {
99 self.interfaces.insert(info.id, info);
100 }
101
102 pub fn deregister_interface(&mut self, id: InterfaceId) {
103 self.interfaces.remove(&id);
104 self.ingress_control.remove_interface(&id);
105 }
106
107 pub fn register_destination(&mut self, dest_hash: [u8; 16], dest_type: u8) {
112 self.local_destinations.insert(dest_hash, dest_type);
113 }
114
115 pub fn deregister_destination(&mut self, dest_hash: &[u8; 16]) {
116 self.local_destinations.remove(dest_hash);
117 }
118
119 pub fn has_path(&self, dest_hash: &[u8; 16]) -> bool {
124 self.path_table.contains_key(dest_hash)
125 }
126
127 pub fn hops_to(&self, dest_hash: &[u8; 16]) -> Option<u8> {
128 self.path_table.get(dest_hash).map(|e| e.hops)
129 }
130
131 pub fn next_hop(&self, dest_hash: &[u8; 16]) -> Option<[u8; 16]> {
132 self.path_table.get(dest_hash).map(|e| e.next_hop)
133 }
134
135 pub fn next_hop_interface(&self, dest_hash: &[u8; 16]) -> Option<InterfaceId> {
136 self.path_table.get(dest_hash).map(|e| e.receiving_interface)
137 }
138
139 pub fn mark_path_unresponsive(
149 &mut self,
150 dest_hash: &[u8; 16],
151 receiving_interface: Option<InterfaceId>,
152 ) {
153 if let Some(iface_id) = receiving_interface {
154 if let Some(info) = self.interfaces.get(&iface_id) {
155 if info.mode == constants::MODE_BOUNDARY {
156 return;
157 }
158 }
159 }
160 self.path_states
161 .insert(*dest_hash, constants::STATE_UNRESPONSIVE);
162 }
163
164 pub fn mark_path_responsive(&mut self, dest_hash: &[u8; 16]) {
165 self.path_states
166 .insert(*dest_hash, constants::STATE_RESPONSIVE);
167 }
168
169 pub fn path_is_unresponsive(&self, dest_hash: &[u8; 16]) -> bool {
170 self.path_states.get(dest_hash) == Some(&constants::STATE_UNRESPONSIVE)
171 }
172
173 pub fn expire_path(&mut self, dest_hash: &[u8; 16]) {
174 if let Some(entry) = self.path_table.get_mut(dest_hash) {
175 entry.timestamp = 0.0;
176 entry.expires = 0.0;
177 }
178 }
179
180 pub fn register_link(&mut self, link_id: [u8; 16], entry: LinkEntry) {
185 self.link_table.insert(link_id, entry);
186 }
187
188 pub fn validate_link(&mut self, link_id: &[u8; 16]) {
189 if let Some(entry) = self.link_table.get_mut(link_id) {
190 entry.validated = true;
191 }
192 }
193
194 pub fn remove_link(&mut self, link_id: &[u8; 16]) {
195 self.link_table.remove(link_id);
196 }
197
198 pub fn blackhole_identity(
204 &mut self,
205 identity_hash: [u8; 16],
206 now: f64,
207 duration_hours: Option<f64>,
208 reason: Option<String>,
209 ) {
210 let expires = match duration_hours {
211 Some(h) if h > 0.0 => now + h * 3600.0,
212 _ => 0.0, };
214 self.blackholed_identities.insert(
215 identity_hash,
216 BlackholeEntry {
217 created: now,
218 expires,
219 reason,
220 },
221 );
222 }
223
224 pub fn unblackhole_identity(&mut self, identity_hash: &[u8; 16]) -> bool {
226 self.blackholed_identities.remove(identity_hash).is_some()
227 }
228
229 pub fn is_blackholed(&self, identity_hash: &[u8; 16], now: f64) -> bool {
231 if let Some(entry) = self.blackholed_identities.get(identity_hash) {
232 if entry.expires == 0.0 || entry.expires > now {
233 return true;
234 }
235 }
236 false
237 }
238
239 pub fn blackholed_entries(&self) -> impl Iterator<Item = (&[u8; 16], &BlackholeEntry)> {
241 self.blackholed_identities.iter()
242 }
243
244 fn cull_blackholed(&mut self, now: f64) {
246 self.blackholed_identities.retain(|_, entry| {
247 entry.expires == 0.0 || entry.expires > now
248 });
249 }
250
251 pub fn handle_tunnel(
259 &mut self,
260 tunnel_id: [u8; 32],
261 interface: InterfaceId,
262 now: f64,
263 ) -> Vec<TransportAction> {
264 let mut actions = Vec::new();
265
266 if let Some(info) = self.interfaces.get_mut(&interface) {
268 info.tunnel_id = Some(tunnel_id);
269 }
270
271 let restored_paths = self.tunnel_table.handle_tunnel(tunnel_id, interface, now);
272
273 for (dest_hash, tunnel_path) in &restored_paths {
275 let should_restore = match self.path_table.get(dest_hash) {
276 Some(existing) => {
277 tunnel_path.hops <= existing.hops || existing.expires < now
279 }
280 None => true,
281 };
282
283 if should_restore {
284 self.path_table.insert(
285 *dest_hash,
286 PathEntry {
287 timestamp: tunnel_path.timestamp,
288 next_hop: tunnel_path.received_from,
289 hops: tunnel_path.hops,
290 expires: tunnel_path.expires,
291 random_blobs: tunnel_path.random_blobs.clone(),
292 receiving_interface: interface,
293 packet_hash: tunnel_path.packet_hash,
294 announce_raw: None,
295 },
296 );
297 }
298 }
299
300 actions.push(TransportAction::TunnelEstablished {
301 tunnel_id,
302 interface,
303 });
304
305 actions
306 }
307
308 pub fn synthesize_tunnel(
316 &self,
317 identity: &rns_crypto::identity::Identity,
318 interface_id: InterfaceId,
319 rng: &mut dyn Rng,
320 ) -> Vec<TransportAction> {
321 let mut actions = Vec::new();
322
323 let interface_hash = if let Some(info) = self.interfaces.get(&interface_id) {
325 hash::full_hash(info.name.as_bytes())
326 } else {
327 return actions;
328 };
329
330 match tunnel::build_tunnel_synthesize_data(identity, &interface_hash, rng) {
331 Ok((data, _tunnel_id)) => {
332 let dest_hash = crate::destination::destination_hash(
333 "rnstransport",
334 &["tunnel", "synthesize"],
335 None,
336 );
337 actions.push(TransportAction::TunnelSynthesize {
338 interface: interface_id,
339 data,
340 dest_hash,
341 });
342 }
343 Err(e) => {
344 let _ = e;
346 }
347 }
348
349 actions
350 }
351
352 pub fn void_tunnel_interface(&mut self, tunnel_id: &[u8; 32]) {
354 self.tunnel_table.void_tunnel_interface(tunnel_id);
355 }
356
357 pub fn tunnel_table(&self) -> &TunnelTable {
359 &self.tunnel_table
360 }
361
362 fn has_local_clients(&self) -> bool {
368 self.interfaces.values().any(|i| i.is_local_client)
369 }
370
371 fn packet_filter(&self, packet: &RawPacket) -> bool {
375 if packet.transport_id.is_some()
377 && packet.flags.packet_type != constants::PACKET_TYPE_ANNOUNCE
378 {
379 if let Some(ref identity_hash) = self.config.identity_hash {
380 if packet.transport_id.as_ref() != Some(identity_hash) {
381 return false;
382 }
383 }
384 }
385
386 match packet.context {
388 constants::CONTEXT_KEEPALIVE
389 | constants::CONTEXT_RESOURCE_REQ
390 | constants::CONTEXT_RESOURCE_PRF
391 | constants::CONTEXT_RESOURCE
392 | constants::CONTEXT_CACHE_REQUEST
393 | constants::CONTEXT_CHANNEL => return true,
394 _ => {}
395 }
396
397 if packet.flags.destination_type == constants::DESTINATION_PLAIN
399 || packet.flags.destination_type == constants::DESTINATION_GROUP
400 {
401 if packet.flags.packet_type != constants::PACKET_TYPE_ANNOUNCE {
402 return packet.hops <= 1;
403 } else {
404 return false;
406 }
407 }
408
409 if !self.packet_hashlist.is_duplicate(&packet.packet_hash) {
411 return true;
412 }
413
414 if packet.flags.packet_type == constants::PACKET_TYPE_ANNOUNCE
416 && packet.flags.destination_type == constants::DESTINATION_SINGLE
417 {
418 return true;
419 }
420
421 false
422 }
423
424 pub fn handle_inbound(
432 &mut self,
433 raw: &[u8],
434 iface: InterfaceId,
435 now: f64,
436 rng: &mut dyn Rng,
437 ) -> Vec<TransportAction> {
438 let mut actions = Vec::new();
439
440 let mut packet = match RawPacket::unpack(raw) {
442 Ok(p) => p,
443 Err(_) => return actions, };
445
446 let original_raw = raw.to_vec();
448
449 packet.hops += 1;
451
452 let from_local_client = self
455 .interfaces
456 .get(&iface)
457 .map(|i| i.is_local_client)
458 .unwrap_or(false);
459 if from_local_client {
460 packet.hops = packet.hops.saturating_sub(1);
461 }
462
463 if !self.packet_filter(&packet) {
465 return actions;
466 }
467
468 let mut remember_hash = true;
470
471 if self.link_table.contains_key(&packet.destination_hash) {
472 remember_hash = false;
473 }
474 if packet.flags.packet_type == constants::PACKET_TYPE_PROOF
475 && packet.context == constants::CONTEXT_LRPROOF
476 {
477 remember_hash = false;
478 }
479
480 if remember_hash {
481 self.packet_hashlist.add(packet.packet_hash);
482 }
483
484 if packet.flags.destination_type == constants::DESTINATION_PLAIN
486 && packet.flags.transport_type == constants::TRANSPORT_BROADCAST
487 && self.has_local_clients()
488 {
489 if from_local_client {
490 actions.push(TransportAction::ForwardPlainBroadcast {
492 raw: packet.raw.clone(),
493 to_local: false,
494 exclude: Some(iface),
495 });
496 } else {
497 actions.push(TransportAction::ForwardPlainBroadcast {
499 raw: packet.raw.clone(),
500 to_local: true,
501 exclude: None,
502 });
503 }
504 }
505
506 if self.config.transport_enabled || self.config.identity_hash.is_some() {
508 if packet.transport_id.is_some()
509 && packet.flags.packet_type != constants::PACKET_TYPE_ANNOUNCE
510 {
511 if let Some(ref identity_hash) = self.config.identity_hash {
512 if packet.transport_id.as_ref() == Some(identity_hash) {
513 if let Some(path_entry) = self.path_table.get(&packet.destination_hash) {
514 let next_hop = path_entry.next_hop;
515 let remaining_hops = path_entry.hops;
516 let outbound_interface = path_entry.receiving_interface;
517
518 let new_raw = forward_transport_packet(
519 &packet,
520 next_hop,
521 remaining_hops,
522 outbound_interface,
523 );
524
525 if packet.flags.packet_type == constants::PACKET_TYPE_LINKREQUEST {
527 let proof_timeout = now
528 + constants::LINK_ESTABLISHMENT_TIMEOUT_PER_HOP
529 * (remaining_hops.max(1) as f64);
530
531 let (link_id, link_entry) = create_link_entry(
532 &packet,
533 next_hop,
534 outbound_interface,
535 remaining_hops,
536 iface,
537 now,
538 proof_timeout,
539 );
540 self.link_table.insert(link_id, link_entry);
541 } else {
542 let (trunc_hash, reverse_entry) = create_reverse_entry(
543 &packet,
544 outbound_interface,
545 iface,
546 now,
547 );
548 self.reverse_table.insert(trunc_hash, reverse_entry);
549 }
550
551 actions.push(TransportAction::SendOnInterface {
552 interface: outbound_interface,
553 raw: new_raw,
554 });
555
556 if let Some(entry) = self.path_table.get_mut(&packet.destination_hash) {
558 entry.timestamp = now;
559 }
560 }
561 }
562 }
563 }
564
565 if packet.flags.packet_type != constants::PACKET_TYPE_ANNOUNCE
567 && packet.flags.packet_type != constants::PACKET_TYPE_LINKREQUEST
568 && packet.context != constants::CONTEXT_LRPROOF
569 {
570 if let Some(link_entry) = self.link_table.get(&packet.destination_hash).cloned() {
571 if let Some((outbound_iface, new_raw)) =
572 route_via_link_table(&packet, &link_entry, iface)
573 {
574 self.packet_hashlist.add(packet.packet_hash);
576
577 actions.push(TransportAction::SendOnInterface {
578 interface: outbound_iface,
579 raw: new_raw,
580 });
581
582 if let Some(entry) =
584 self.link_table.get_mut(&packet.destination_hash)
585 {
586 entry.timestamp = now;
587 }
588 }
589 }
590 }
591 }
592
593 if packet.flags.packet_type == constants::PACKET_TYPE_ANNOUNCE {
595 self.process_inbound_announce(&packet, &original_raw, iface, now, rng, &mut actions);
596 }
597
598 if packet.flags.packet_type == constants::PACKET_TYPE_PROOF {
600 self.process_inbound_proof(&packet, iface, now, &mut actions);
601 }
602
603 if packet.flags.packet_type == constants::PACKET_TYPE_LINKREQUEST
605 || packet.flags.packet_type == constants::PACKET_TYPE_DATA
606 {
607 if self.local_destinations.contains_key(&packet.destination_hash) {
608 actions.push(TransportAction::DeliverLocal {
609 destination_hash: packet.destination_hash,
610 raw: packet.raw.clone(),
611 packet_hash: packet.packet_hash,
612 });
613 }
614 }
615
616 actions
617 }
618
619 fn process_inbound_announce(
624 &mut self,
625 packet: &RawPacket,
626 original_raw: &[u8],
627 iface: InterfaceId,
628 now: f64,
629 rng: &mut dyn Rng,
630 actions: &mut Vec<TransportAction>,
631 ) {
632 if packet.flags.destination_type != constants::DESTINATION_SINGLE {
633 return;
634 }
635
636 let has_ratchet = packet.flags.context_flag == constants::FLAG_SET;
637
638 let announce = match AnnounceData::unpack(&packet.data, has_ratchet) {
640 Ok(a) => a,
641 Err(_) => return,
642 };
643
644 let validated = match announce.validate(&packet.destination_hash) {
645 Ok(v) => v,
646 Err(_) => return,
647 };
648
649 if self.is_blackholed(&validated.identity_hash, now) {
651 return;
652 }
653
654 if self.local_destinations.contains_key(&packet.destination_hash) {
656 return;
657 }
658
659 if !self.path_table.contains_key(&packet.destination_hash) {
661 if let Some(info) = self.interfaces.get(&iface) {
662 if info.ingress_control {
663 if self.ingress_control.should_ingress_limit(iface, info.ia_freq, info.started, now) {
664 self.ingress_control.hold_announce(
665 iface,
666 packet.destination_hash,
667 ingress_control::HeldAnnounce {
668 raw: original_raw.to_vec(),
669 hops: packet.hops,
670 receiving_interface: iface,
671 timestamp: now,
672 },
673 );
674 return;
675 }
676 }
677 }
678 }
679
680 let received_from = if let Some(transport_id) = packet.transport_id {
682 if self.config.transport_enabled {
684 if let Some(announce_entry) = self.announce_table.get_mut(&packet.destination_hash) {
685 if packet.hops.checked_sub(1) == Some(announce_entry.hops) {
686 announce_entry.local_rebroadcasts += 1;
687 if announce_entry.retries > 0
688 && announce_entry.local_rebroadcasts >= constants::LOCAL_REBROADCASTS_MAX
689 {
690 self.announce_table.remove(&packet.destination_hash);
691 }
692 }
693 if let Some(announce_entry) = self.announce_table.get(&packet.destination_hash) {
695 if packet.hops.checked_sub(1) == Some(announce_entry.hops + 1)
696 && announce_entry.retries > 0
697 {
698 if now < announce_entry.retransmit_timeout {
699 self.announce_table.remove(&packet.destination_hash);
700 }
701 }
702 }
703 }
704 }
705 transport_id
706 } else {
707 packet.destination_hash
708 };
709
710 let random_blob = match extract_random_blob(&packet.data) {
712 Some(b) => b,
713 None => return,
714 };
715
716 if packet.hops >= constants::PATHFINDER_M + 1 {
718 return;
719 }
720
721 let announce_emitted = timebase_from_random_blob(&random_blob);
722
723 let existing = self.path_table.get(&packet.destination_hash);
725 let is_unresponsive = self.path_is_unresponsive(&packet.destination_hash);
726
727 let decision = should_update_path(
728 existing,
729 packet.hops,
730 announce_emitted,
731 &random_blob,
732 is_unresponsive,
733 now,
734 );
735
736 if decision == PathDecision::Reject {
737 return;
738 }
739
740 let rate_blocked = if packet.context != constants::CONTEXT_PATH_RESPONSE {
742 if let Some(iface_info) = self.interfaces.get(&iface) {
743 self.rate_limiter.check_and_update(
744 &packet.destination_hash,
745 now,
746 iface_info.announce_rate_target,
747 iface_info.announce_rate_grace,
748 iface_info.announce_rate_penalty,
749 )
750 } else {
751 false
752 }
753 } else {
754 false
755 };
756
757 let interface_mode = self
759 .interfaces
760 .get(&iface)
761 .map(|i| i.mode)
762 .unwrap_or(constants::MODE_FULL);
763
764 let expires = compute_path_expires(now, interface_mode);
765
766 let existing_blobs = self
768 .path_table
769 .get(&packet.destination_hash)
770 .map(|e| e.random_blobs.clone())
771 .unwrap_or_default();
772
773 let mut rng_bytes = [0u8; 8];
775 rng.fill_bytes(&mut rng_bytes);
776 let rng_value = (u64::from_le_bytes(rng_bytes) as f64) / (u64::MAX as f64);
777
778 let is_path_response = packet.context == constants::CONTEXT_PATH_RESPONSE;
779
780 let (path_entry, announce_entry) = announce_proc::process_validated_announce(
781 packet.destination_hash,
782 packet.hops,
783 &packet.data,
784 &packet.raw,
785 packet.packet_hash,
786 packet.flags.context_flag,
787 received_from,
788 iface,
789 now,
790 existing_blobs,
791 random_blob,
792 expires,
793 rng_value,
794 self.config.transport_enabled,
795 is_path_response,
796 rate_blocked,
797 Some(original_raw.to_vec()),
798 );
799
800 actions.push(TransportAction::CacheAnnounce {
802 packet_hash: packet.packet_hash,
803 raw: original_raw.to_vec(),
804 });
805
806 self.path_table
808 .insert(packet.destination_hash, path_entry);
809
810 if let Some(tunnel_id) = self.interfaces.get(&iface).and_then(|i| i.tunnel_id) {
812 let blobs = self
813 .path_table
814 .get(&packet.destination_hash)
815 .map(|e| e.random_blobs.clone())
816 .unwrap_or_default();
817 self.tunnel_table.store_tunnel_path(
818 &tunnel_id,
819 packet.destination_hash,
820 tunnel::TunnelPath {
821 timestamp: now,
822 received_from,
823 hops: packet.hops,
824 expires,
825 random_blobs: blobs,
826 packet_hash: packet.packet_hash,
827 },
828 now,
829 );
830 }
831
832 self.path_states.remove(&packet.destination_hash);
834
835 if let Some(ann) = announce_entry {
837 self.announce_table.insert(packet.destination_hash, ann);
838 }
839
840 actions.push(TransportAction::AnnounceReceived {
842 destination_hash: packet.destination_hash,
843 identity_hash: validated.identity_hash,
844 public_key: validated.public_key,
845 name_hash: validated.name_hash,
846 random_hash: validated.random_hash,
847 app_data: validated.app_data,
848 hops: packet.hops,
849 receiving_interface: iface,
850 });
851
852 actions.push(TransportAction::PathUpdated {
853 destination_hash: packet.destination_hash,
854 hops: packet.hops,
855 next_hop: received_from,
856 interface: iface,
857 });
858
859 if self.has_local_clients() {
861 actions.push(TransportAction::ForwardToLocalClients {
862 raw: packet.raw.clone(),
863 exclude: Some(iface),
864 });
865 }
866
867 if let Some(pr_entry) = self.discovery_path_requests_waiting(&packet.destination_hash) {
869 let entry = AnnounceEntry {
871 timestamp: now,
872 retransmit_timeout: now,
873 retries: constants::PATHFINDER_R,
874 received_from,
875 hops: packet.hops,
876 packet_raw: packet.raw.clone(),
877 packet_data: packet.data.clone(),
878 destination_hash: packet.destination_hash,
879 context_flag: packet.flags.context_flag,
880 local_rebroadcasts: 0,
881 block_rebroadcasts: true,
882 attached_interface: Some(pr_entry),
883 };
884 self.announce_table
885 .insert(packet.destination_hash, entry);
886 }
887 }
888
889 fn discovery_path_requests_waiting(&mut self, dest_hash: &[u8; 16]) -> Option<InterfaceId> {
892 self.discovery_path_requests
893 .remove(dest_hash)
894 .map(|req| req.requesting_interface)
895 }
896
897 fn process_inbound_proof(
902 &mut self,
903 packet: &RawPacket,
904 iface: InterfaceId,
905 _now: f64,
906 actions: &mut Vec<TransportAction>,
907 ) {
908 if packet.context == constants::CONTEXT_LRPROOF {
909 if (self.config.transport_enabled) && self.link_table.contains_key(&packet.destination_hash)
911 {
912 let link_entry = self.link_table.get(&packet.destination_hash).cloned();
913 if let Some(entry) = link_entry {
914 if packet.hops == entry.remaining_hops
915 && iface == entry.next_hop_interface
916 {
917 let mut new_raw = Vec::new();
920 new_raw.push(packet.raw[0]);
921 new_raw.push(packet.hops);
922 new_raw.extend_from_slice(&packet.raw[2..]);
923
924 if let Some(le) =
926 self.link_table.get_mut(&packet.destination_hash)
927 {
928 le.validated = true;
929 }
930
931 actions.push(TransportAction::SendOnInterface {
932 interface: entry.received_interface,
933 raw: new_raw,
934 });
935 }
936 }
937 } else {
938 actions.push(TransportAction::DeliverLocal {
940 destination_hash: packet.destination_hash,
941 raw: packet.raw.clone(),
942 packet_hash: packet.packet_hash,
943 });
944 }
945 } else {
946 if self.config.transport_enabled {
948 if let Some(reverse_entry) =
949 self.reverse_table.remove(&packet.destination_hash)
950 {
951 if let Some(action) =
952 route_proof_via_reverse(packet, &reverse_entry, iface)
953 {
954 actions.push(action);
955 }
956 }
957 }
958
959 actions.push(TransportAction::DeliverLocal {
961 destination_hash: packet.destination_hash,
962 raw: packet.raw.clone(),
963 packet_hash: packet.packet_hash,
964 });
965 }
966 }
967
968 pub fn handle_outbound(
974 &mut self,
975 packet: &RawPacket,
976 dest_type: u8,
977 attached_interface: Option<InterfaceId>,
978 now: f64,
979 ) -> Vec<TransportAction> {
980 let actions = route_outbound(
981 &self.path_table,
982 &self.interfaces,
983 &self.local_destinations,
984 packet,
985 dest_type,
986 attached_interface,
987 now,
988 );
989
990 self.packet_hashlist.add(packet.packet_hash);
992
993 if packet.flags.packet_type == constants::PACKET_TYPE_ANNOUNCE && packet.hops > 0 {
995 self.gate_announce_actions(actions, &packet.destination_hash, packet.hops, now)
996 } else {
997 actions
998 }
999 }
1000
1001 fn gate_announce_actions(
1003 &mut self,
1004 actions: Vec<TransportAction>,
1005 dest_hash: &[u8; 16],
1006 hops: u8,
1007 now: f64,
1008 ) -> Vec<TransportAction> {
1009 let mut result = Vec::new();
1010 for action in actions {
1011 match action {
1012 TransportAction::SendOnInterface { interface, raw } => {
1013 let (bitrate, announce_cap) =
1014 if let Some(info) = self.interfaces.get(&interface) {
1015 (info.bitrate, info.announce_cap)
1016 } else {
1017 (None, constants::ANNOUNCE_CAP)
1018 };
1019 if let Some(send_action) = self.announce_queues.gate_announce(
1020 interface,
1021 raw,
1022 *dest_hash,
1023 hops,
1024 now,
1025 now,
1026 bitrate,
1027 announce_cap,
1028 ) {
1029 result.push(send_action);
1030 }
1031 }
1033 other => result.push(other),
1034 }
1035 }
1036 result
1037 }
1038
1039 pub fn tick(&mut self, now: f64, rng: &mut dyn Rng) -> Vec<TransportAction> {
1045 let mut actions = Vec::new();
1046
1047 if now > self.announces_last_checked + constants::ANNOUNCES_CHECK_INTERVAL {
1049 if let Some(ref identity_hash) = self.config.identity_hash {
1050 let ih = *identity_hash;
1051 let announce_actions = jobs::process_pending_announces(
1052 &mut self.announce_table,
1053 &mut self.held_announces,
1054 &ih,
1055 now,
1056 );
1057 let gated = self.gate_retransmit_actions(announce_actions, now);
1059 actions.extend(gated);
1060 }
1061 self.announces_last_checked = now;
1062 }
1063
1064 let mut queue_actions = self.announce_queues.process_queues(now, &self.interfaces);
1066 actions.append(&mut queue_actions);
1067
1068 let ic_interfaces = self.ingress_control.interfaces_with_held();
1070 for iface_id in ic_interfaces {
1071 let (ia_freq, started, ic_enabled) = match self.interfaces.get(&iface_id) {
1072 Some(info) => (info.ia_freq, info.started, info.ingress_control),
1073 None => continue,
1074 };
1075 if !ic_enabled {
1076 continue;
1077 }
1078 if let Some(held) = self.ingress_control.process_held_announces(iface_id, ia_freq, started, now) {
1079 let released_actions = self.handle_inbound(&held.raw, held.receiving_interface, now, rng);
1080 actions.extend(released_actions);
1081 }
1082 }
1083
1084 if now > self.tables_last_culled + constants::TABLES_CULL_INTERVAL {
1086 jobs::cull_path_table(&mut self.path_table, &self.interfaces, now);
1087 jobs::cull_reverse_table(&mut self.reverse_table, &self.interfaces, now);
1088 jobs::cull_link_table(&mut self.link_table, &self.interfaces, now);
1089 jobs::cull_path_states(&mut self.path_states, &self.path_table);
1090 self.cull_blackholed(now);
1091 self.discovery_path_requests
1093 .retain(|_, req| now - req.timestamp < constants::DISCOVERY_PATH_REQUEST_TIMEOUT);
1094 self.tunnel_table.void_missing_interfaces(|id| self.interfaces.contains_key(id));
1096 self.tunnel_table.cull(now);
1097 self.tables_last_culled = now;
1098 }
1099
1100 self.packet_hashlist.maybe_rotate();
1102
1103 if self.discovery_pr_tags.len() > constants::MAX_PR_TAGS {
1105 let start = self.discovery_pr_tags.len() - constants::MAX_PR_TAGS;
1106 self.discovery_pr_tags = self.discovery_pr_tags[start..].to_vec();
1107 }
1108
1109 actions
1110 }
1111
1112 fn gate_retransmit_actions(
1117 &mut self,
1118 actions: Vec<TransportAction>,
1119 now: f64,
1120 ) -> Vec<TransportAction> {
1121 let mut result = Vec::new();
1122 for action in actions {
1123 match action {
1124 TransportAction::SendOnInterface { interface, raw } => {
1125 let (dest_hash, hops) = Self::extract_announce_info(&raw);
1127 let (bitrate, announce_cap) =
1128 if let Some(info) = self.interfaces.get(&interface) {
1129 (info.bitrate, info.announce_cap)
1130 } else {
1131 (None, constants::ANNOUNCE_CAP)
1132 };
1133 if let Some(send_action) = self.announce_queues.gate_announce(
1134 interface,
1135 raw,
1136 dest_hash,
1137 hops,
1138 now,
1139 now,
1140 bitrate,
1141 announce_cap,
1142 ) {
1143 result.push(send_action);
1144 }
1145 }
1146 TransportAction::BroadcastOnAllInterfaces { raw, exclude } => {
1147 let (dest_hash, hops) = Self::extract_announce_info(&raw);
1148 let iface_ids: Vec<(InterfaceId, Option<u64>, f64)> = self
1151 .interfaces
1152 .iter()
1153 .filter(|(_, info)| info.out_capable)
1154 .filter(|(id, _)| {
1155 if let Some(ref ex) = exclude {
1156 **id != *ex
1157 } else {
1158 true
1159 }
1160 })
1161 .filter(|(_, info)| {
1162 should_transmit_announce(
1163 info,
1164 &dest_hash,
1165 hops,
1166 &self.local_destinations,
1167 &self.path_table,
1168 &self.interfaces,
1169 )
1170 })
1171 .map(|(id, info)| (*id, info.bitrate, info.announce_cap))
1172 .collect();
1173
1174 for (iface_id, bitrate, announce_cap) in iface_ids {
1175 if let Some(send_action) = self.announce_queues.gate_announce(
1176 iface_id,
1177 raw.clone(),
1178 dest_hash,
1179 hops,
1180 now,
1181 now,
1182 bitrate,
1183 announce_cap,
1184 ) {
1185 result.push(send_action);
1186 }
1187 }
1188 }
1189 other => result.push(other),
1190 }
1191 }
1192 result
1193 }
1194
1195 fn extract_announce_info(raw: &[u8]) -> ([u8; 16], u8) {
1197 if raw.len() < 18 {
1198 return ([0; 16], 0);
1199 }
1200 let header_type = (raw[0] >> 6) & 0x03;
1201 let hops = raw[1];
1202 if header_type == constants::HEADER_2 && raw.len() >= 34 {
1203 let mut dest = [0u8; 16];
1205 dest.copy_from_slice(&raw[18..34]);
1206 (dest, hops)
1207 } else {
1208 let mut dest = [0u8; 16];
1210 dest.copy_from_slice(&raw[2..18]);
1211 (dest, hops)
1212 }
1213 }
1214
1215 pub fn handle_path_request(
1228 &mut self,
1229 data: &[u8],
1230 interface_id: InterfaceId,
1231 now: f64,
1232 ) -> Vec<TransportAction> {
1233 let mut actions = Vec::new();
1234
1235 if data.len() < 16 {
1236 return actions;
1237 }
1238
1239 let mut destination_hash = [0u8; 16];
1240 destination_hash.copy_from_slice(&data[..16]);
1241
1242 let _requesting_transport_id = if data.len() > 32 {
1244 let mut id = [0u8; 16];
1245 id.copy_from_slice(&data[16..32]);
1246 Some(id)
1247 } else {
1248 None
1249 };
1250
1251 let tag_bytes = if data.len() > 32 {
1253 Some(&data[32..])
1254 } else if data.len() > 16 {
1255 Some(&data[16..])
1256 } else {
1257 None
1258 };
1259
1260 if let Some(tag) = tag_bytes {
1261 let tag_len = tag.len().min(16);
1262 let mut unique_tag = [0u8; 32];
1263 unique_tag[..16].copy_from_slice(&destination_hash);
1264 unique_tag[16..16 + tag_len].copy_from_slice(&tag[..tag_len]);
1265
1266 if self.discovery_pr_tags.contains(&unique_tag) {
1267 return actions; }
1269 self.discovery_pr_tags.push(unique_tag);
1270 } else {
1271 return actions; }
1273
1274 if self.local_destinations.contains_key(&destination_hash) {
1276 return actions;
1277 }
1278
1279 if self.config.transport_enabled && self.path_table.contains_key(&destination_hash) {
1281 let path = self.path_table.get(&destination_hash).unwrap().clone();
1282
1283 if let Some(recv_info) = self.interfaces.get(&interface_id) {
1287 if recv_info.mode == constants::MODE_ROAMING
1288 && path.receiving_interface == interface_id
1289 {
1290 return actions;
1291 }
1292 }
1293
1294 if let Some(existing) = self.announce_table.remove(&destination_hash) {
1296 self.held_announces.insert(destination_hash, existing);
1297 }
1298
1299 let retransmit_timeout = if let Some(iface_info) = self.interfaces.get(&interface_id) {
1300 let base = now + constants::PATH_REQUEST_GRACE;
1301 if iface_info.mode == constants::MODE_ROAMING {
1302 base + constants::PATH_REQUEST_RG
1303 } else {
1304 base
1305 }
1306 } else {
1307 now + constants::PATH_REQUEST_GRACE
1308 };
1309
1310 let entry = AnnounceEntry {
1311 timestamp: now,
1312 retransmit_timeout,
1313 retries: constants::PATHFINDER_R,
1314 received_from: path.next_hop,
1315 hops: path.hops,
1316 packet_raw: Vec::new(),
1317 packet_data: Vec::new(),
1318 destination_hash,
1319 context_flag: 0,
1320 local_rebroadcasts: 0,
1321 block_rebroadcasts: true,
1322 attached_interface: Some(interface_id),
1323 };
1324
1325 self.announce_table.insert(destination_hash, entry);
1326 } else if self.config.transport_enabled {
1327 let should_discover = self
1329 .interfaces
1330 .get(&interface_id)
1331 .map(|info| constants::DISCOVER_PATHS_FOR.contains(&info.mode))
1332 .unwrap_or(false);
1333
1334 if should_discover {
1335 self.discovery_path_requests.insert(
1337 destination_hash,
1338 DiscoveryPathRequest {
1339 timestamp: now,
1340 requesting_interface: interface_id,
1341 },
1342 );
1343
1344 for (_, iface_info) in self.interfaces.iter() {
1346 if iface_info.id != interface_id && iface_info.out_capable {
1347 actions.push(TransportAction::SendOnInterface {
1348 interface: iface_info.id,
1349 raw: data.to_vec(),
1350 });
1351 }
1352 }
1353 }
1354 }
1355
1356 actions
1357 }
1358
1359 pub fn path_table_entries(&self) -> impl Iterator<Item = (&[u8; 16], &PathEntry)> {
1365 self.path_table.iter()
1366 }
1367
1368 pub fn interface_count(&self) -> usize {
1370 self.interfaces.len()
1371 }
1372
1373 pub fn link_table_count(&self) -> usize {
1375 self.link_table.len()
1376 }
1377
1378 pub fn rate_limiter(&self) -> &AnnounceRateLimiter {
1380 &self.rate_limiter
1381 }
1382
1383 pub fn interface_info(&self, id: &InterfaceId) -> Option<&InterfaceInfo> {
1385 self.interfaces.get(id)
1386 }
1387
1388 pub fn redirect_path(&mut self, dest_hash: &[u8; 16], interface: InterfaceId, now: f64) {
1391 if let Some(entry) = self.path_table.get_mut(dest_hash) {
1392 entry.receiving_interface = interface;
1393 entry.hops = 1;
1394 } else {
1395 self.path_table.insert(*dest_hash, PathEntry {
1396 timestamp: now,
1397 next_hop: [0u8; 16],
1398 hops: 1,
1399 expires: now + 3600.0,
1400 random_blobs: Vec::new(),
1401 receiving_interface: interface,
1402 packet_hash: [0u8; 32],
1403 announce_raw: None,
1404 });
1405 }
1406 }
1407
1408 pub fn drop_path(&mut self, dest_hash: &[u8; 16]) -> bool {
1410 self.path_table.remove(dest_hash).is_some()
1411 }
1412
1413 pub fn drop_all_via(&mut self, transport_hash: &[u8; 16]) -> usize {
1415 let before = self.path_table.len();
1416 self.path_table.retain(|_, entry| &entry.next_hop != transport_hash);
1417 before - self.path_table.len()
1418 }
1419
1420 pub fn drop_announce_queues(&mut self) {
1422 self.announce_table.clear();
1423 self.held_announces.clear();
1424 self.announce_queues = AnnounceQueues::new();
1425 self.ingress_control.clear();
1426 }
1427
1428 pub fn identity_hash(&self) -> Option<&[u8; 16]> {
1430 self.config.identity_hash.as_ref()
1431 }
1432
1433 pub fn transport_enabled(&self) -> bool {
1435 self.config.transport_enabled
1436 }
1437
1438 pub fn config(&self) -> &TransportConfig {
1440 &self.config
1441 }
1442
1443 pub fn get_path_table(&self, max_hops: Option<u8>) -> Vec<([u8; 16], f64, [u8; 16], u8, f64, alloc::string::String)> {
1446 let mut result = Vec::new();
1447 for (dest_hash, entry) in self.path_table.iter() {
1448 if let Some(max) = max_hops {
1449 if entry.hops > max {
1450 continue;
1451 }
1452 }
1453 let iface_name = self.interfaces.get(&entry.receiving_interface)
1454 .map(|i| i.name.clone())
1455 .unwrap_or_else(|| alloc::format!("Interface({})", entry.receiving_interface.0));
1456 result.push((*dest_hash, entry.timestamp, entry.next_hop, entry.hops, entry.expires, iface_name));
1457 }
1458 result
1459 }
1460
1461 pub fn get_rate_table(&self) -> Vec<([u8; 16], f64, u32, f64, Vec<f64>)> {
1464 self.rate_limiter.entries()
1465 .map(|(hash, entry)| (*hash, entry.last, entry.rate_violations, entry.blocked_until, entry.timestamps.clone()))
1466 .collect()
1467 }
1468
1469 pub fn get_blackholed(&self) -> Vec<([u8; 16], f64, f64, Option<alloc::string::String>)> {
1472 self.blackholed_entries()
1473 .map(|(hash, entry)| (*hash, entry.created, entry.expires, entry.reason.clone()))
1474 .collect()
1475 }
1476
1477 pub fn update_interface_freq(&mut self, id: InterfaceId, ia_freq: f64) {
1483 if let Some(info) = self.interfaces.get_mut(&id) {
1484 info.ia_freq = ia_freq;
1485 }
1486 }
1487
1488 pub fn held_announce_count(&self, interface: &InterfaceId) -> usize {
1490 self.ingress_control.held_count(interface)
1491 }
1492
1493 #[cfg(test)]
1498 pub(crate) fn path_table(&self) -> &BTreeMap<[u8; 16], PathEntry> {
1499 &self.path_table
1500 }
1501
1502 #[cfg(test)]
1503 pub(crate) fn announce_table(&self) -> &BTreeMap<[u8; 16], AnnounceEntry> {
1504 &self.announce_table
1505 }
1506
1507 #[cfg(test)]
1508 pub(crate) fn reverse_table(&self) -> &BTreeMap<[u8; 16], tables::ReverseEntry> {
1509 &self.reverse_table
1510 }
1511
1512 #[cfg(test)]
1513 pub(crate) fn link_table_ref(&self) -> &BTreeMap<[u8; 16], LinkEntry> {
1514 &self.link_table
1515 }
1516}
1517
1518#[cfg(test)]
1519mod tests {
1520 use super::*;
1521 use crate::packet::PacketFlags;
1522
1523 fn make_config(transport_enabled: bool) -> TransportConfig {
1524 TransportConfig {
1525 transport_enabled,
1526 identity_hash: if transport_enabled {
1527 Some([0x42; 16])
1528 } else {
1529 None
1530 },
1531 }
1532 }
1533
1534 fn make_interface(id: u64, mode: u8) -> InterfaceInfo {
1535 InterfaceInfo {
1536 id: InterfaceId(id),
1537 name: String::from("test"),
1538 mode,
1539 out_capable: true,
1540 in_capable: true,
1541 bitrate: None,
1542 announce_rate_target: None,
1543 announce_rate_grace: 0,
1544 announce_rate_penalty: 0.0,
1545 announce_cap: constants::ANNOUNCE_CAP,
1546 is_local_client: false,
1547 wants_tunnel: false,
1548 tunnel_id: None,
1549 mtu: constants::MTU as u32,
1550 ingress_control: false,
1551 ia_freq: 0.0,
1552 started: 0.0,
1553 }
1554 }
1555
1556 #[test]
1557 fn test_empty_engine() {
1558 let engine = TransportEngine::new(make_config(false));
1559 assert!(!engine.has_path(&[0; 16]));
1560 assert!(engine.hops_to(&[0; 16]).is_none());
1561 assert!(engine.next_hop(&[0; 16]).is_none());
1562 }
1563
1564 #[test]
1565 fn test_register_deregister_interface() {
1566 let mut engine = TransportEngine::new(make_config(false));
1567 engine.register_interface(make_interface(1, constants::MODE_FULL));
1568 assert!(engine.interfaces.contains_key(&InterfaceId(1)));
1569
1570 engine.deregister_interface(InterfaceId(1));
1571 assert!(!engine.interfaces.contains_key(&InterfaceId(1)));
1572 }
1573
1574 #[test]
1575 fn test_register_deregister_destination() {
1576 let mut engine = TransportEngine::new(make_config(false));
1577 let dest = [0x11; 16];
1578 engine.register_destination(dest, constants::DESTINATION_SINGLE);
1579 assert!(engine.local_destinations.contains_key(&dest));
1580
1581 engine.deregister_destination(&dest);
1582 assert!(!engine.local_destinations.contains_key(&dest));
1583 }
1584
1585 #[test]
1586 fn test_path_state() {
1587 let mut engine = TransportEngine::new(make_config(false));
1588 let dest = [0x22; 16];
1589
1590 assert!(!engine.path_is_unresponsive(&dest));
1591
1592 engine.mark_path_unresponsive(&dest, None);
1593 assert!(engine.path_is_unresponsive(&dest));
1594
1595 engine.mark_path_responsive(&dest);
1596 assert!(!engine.path_is_unresponsive(&dest));
1597 }
1598
1599 #[test]
1600 fn test_boundary_exempts_unresponsive() {
1601 let mut engine = TransportEngine::new(make_config(false));
1602 engine.register_interface(make_interface(1, constants::MODE_BOUNDARY));
1603 let dest = [0xB1; 16];
1604
1605 engine.mark_path_unresponsive(&dest, Some(InterfaceId(1)));
1607 assert!(!engine.path_is_unresponsive(&dest));
1608 }
1609
1610 #[test]
1611 fn test_non_boundary_marks_unresponsive() {
1612 let mut engine = TransportEngine::new(make_config(false));
1613 engine.register_interface(make_interface(1, constants::MODE_FULL));
1614 let dest = [0xB2; 16];
1615
1616 engine.mark_path_unresponsive(&dest, Some(InterfaceId(1)));
1618 assert!(engine.path_is_unresponsive(&dest));
1619 }
1620
1621 #[test]
1622 fn test_expire_path() {
1623 let mut engine = TransportEngine::new(make_config(false));
1624 let dest = [0x33; 16];
1625
1626 engine.path_table.insert(
1627 dest,
1628 PathEntry {
1629 timestamp: 1000.0,
1630 next_hop: [0; 16],
1631 hops: 2,
1632 expires: 9999.0,
1633 random_blobs: Vec::new(),
1634 receiving_interface: InterfaceId(1),
1635 packet_hash: [0; 32],
1636 announce_raw: None,
1637 },
1638 );
1639
1640 assert!(engine.has_path(&dest));
1641 engine.expire_path(&dest);
1642 assert!(engine.has_path(&dest));
1644 assert_eq!(engine.path_table[&dest].expires, 0.0);
1645 }
1646
1647 #[test]
1648 fn test_link_table_operations() {
1649 let mut engine = TransportEngine::new(make_config(false));
1650 let link_id = [0x44; 16];
1651
1652 engine.register_link(
1653 link_id,
1654 LinkEntry {
1655 timestamp: 100.0,
1656 next_hop_transport_id: [0; 16],
1657 next_hop_interface: InterfaceId(1),
1658 remaining_hops: 3,
1659 received_interface: InterfaceId(2),
1660 taken_hops: 2,
1661 destination_hash: [0xAA; 16],
1662 validated: false,
1663 proof_timeout: 200.0,
1664 },
1665 );
1666
1667 assert!(engine.link_table.contains_key(&link_id));
1668 assert!(!engine.link_table[&link_id].validated);
1669
1670 engine.validate_link(&link_id);
1671 assert!(engine.link_table[&link_id].validated);
1672
1673 engine.remove_link(&link_id);
1674 assert!(!engine.link_table.contains_key(&link_id));
1675 }
1676
1677 #[test]
1678 fn test_packet_filter_drops_plain_announce() {
1679 let engine = TransportEngine::new(make_config(false));
1680 let flags = PacketFlags {
1681 header_type: constants::HEADER_1,
1682 context_flag: constants::FLAG_UNSET,
1683 transport_type: constants::TRANSPORT_BROADCAST,
1684 destination_type: constants::DESTINATION_PLAIN,
1685 packet_type: constants::PACKET_TYPE_ANNOUNCE,
1686 };
1687 let packet =
1688 RawPacket::pack(flags, 0, &[0; 16], None, constants::CONTEXT_NONE, b"test").unwrap();
1689 assert!(!engine.packet_filter(&packet));
1690 }
1691
1692 #[test]
1693 fn test_packet_filter_allows_keepalive() {
1694 let engine = TransportEngine::new(make_config(false));
1695 let flags = PacketFlags {
1696 header_type: constants::HEADER_1,
1697 context_flag: constants::FLAG_UNSET,
1698 transport_type: constants::TRANSPORT_BROADCAST,
1699 destination_type: constants::DESTINATION_SINGLE,
1700 packet_type: constants::PACKET_TYPE_DATA,
1701 };
1702 let packet = RawPacket::pack(
1703 flags,
1704 0,
1705 &[0; 16],
1706 None,
1707 constants::CONTEXT_KEEPALIVE,
1708 b"test",
1709 )
1710 .unwrap();
1711 assert!(engine.packet_filter(&packet));
1712 }
1713
1714 #[test]
1715 fn test_packet_filter_drops_high_hop_plain() {
1716 let engine = TransportEngine::new(make_config(false));
1717 let flags = PacketFlags {
1718 header_type: constants::HEADER_1,
1719 context_flag: constants::FLAG_UNSET,
1720 transport_type: constants::TRANSPORT_BROADCAST,
1721 destination_type: constants::DESTINATION_PLAIN,
1722 packet_type: constants::PACKET_TYPE_DATA,
1723 };
1724 let mut packet =
1725 RawPacket::pack(flags, 0, &[0; 16], None, constants::CONTEXT_NONE, b"test").unwrap();
1726 packet.hops = 2;
1727 assert!(!engine.packet_filter(&packet));
1728 }
1729
1730 #[test]
1731 fn test_packet_filter_allows_duplicate_single_announce() {
1732 let mut engine = TransportEngine::new(make_config(false));
1733 let flags = PacketFlags {
1734 header_type: constants::HEADER_1,
1735 context_flag: constants::FLAG_UNSET,
1736 transport_type: constants::TRANSPORT_BROADCAST,
1737 destination_type: constants::DESTINATION_SINGLE,
1738 packet_type: constants::PACKET_TYPE_ANNOUNCE,
1739 };
1740 let packet =
1741 RawPacket::pack(flags, 0, &[0; 16], None, constants::CONTEXT_NONE, &[0xAA; 64])
1742 .unwrap();
1743
1744 engine.packet_hashlist.add(packet.packet_hash);
1746
1747 assert!(engine.packet_filter(&packet));
1749 }
1750
1751 #[test]
1752 fn test_tick_retransmits_announce() {
1753 let mut engine = TransportEngine::new(make_config(true));
1754 engine.register_interface(make_interface(1, constants::MODE_FULL));
1755
1756 let dest = [0x55; 16];
1757 engine.announce_table.insert(
1758 dest,
1759 AnnounceEntry {
1760 timestamp: 100.0,
1761 retransmit_timeout: 100.0, retries: 0,
1763 received_from: [0xAA; 16],
1764 hops: 2,
1765 packet_raw: vec![0x01, 0x02],
1766 packet_data: vec![0xCC; 10],
1767 destination_hash: dest,
1768 context_flag: 0,
1769 local_rebroadcasts: 0,
1770 block_rebroadcasts: false,
1771 attached_interface: None,
1772 },
1773 );
1774
1775 let mut rng = rns_crypto::FixedRng::new(&[0x42; 32]);
1776 let actions = engine.tick(200.0, &mut rng);
1777
1778 assert!(!actions.is_empty());
1781 assert!(matches!(
1782 &actions[0],
1783 TransportAction::SendOnInterface { .. }
1784 ));
1785
1786 assert_eq!(engine.announce_table[&dest].retries, 1);
1788 }
1789
1790 #[test]
1791 fn test_blackhole_identity() {
1792 let mut engine = TransportEngine::new(make_config(false));
1793 let hash = [0xAA; 16];
1794 let now = 1000.0;
1795
1796 assert!(!engine.is_blackholed(&hash, now));
1797
1798 engine.blackhole_identity(hash, now, None, Some(String::from("test")));
1799 assert!(engine.is_blackholed(&hash, now));
1800 assert!(engine.is_blackholed(&hash, now + 999999.0)); assert!(engine.unblackhole_identity(&hash));
1803 assert!(!engine.is_blackholed(&hash, now));
1804 assert!(!engine.unblackhole_identity(&hash)); }
1806
1807 #[test]
1808 fn test_blackhole_with_duration() {
1809 let mut engine = TransportEngine::new(make_config(false));
1810 let hash = [0xBB; 16];
1811 let now = 1000.0;
1812
1813 engine.blackhole_identity(hash, now, Some(1.0), None); assert!(engine.is_blackholed(&hash, now));
1815 assert!(engine.is_blackholed(&hash, now + 3599.0)); assert!(!engine.is_blackholed(&hash, now + 3601.0)); }
1818
1819 #[test]
1820 fn test_cull_blackholed() {
1821 let mut engine = TransportEngine::new(make_config(false));
1822 let hash1 = [0xCC; 16];
1823 let hash2 = [0xDD; 16];
1824 let now = 1000.0;
1825
1826 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));
1832 assert!(engine.blackholed_identities.contains_key(&hash2));
1833 }
1834
1835 #[test]
1836 fn test_blackhole_blocks_announce() {
1837 use crate::announce::AnnounceData;
1838 use crate::destination::{destination_hash, name_hash};
1839
1840 let mut engine = TransportEngine::new(make_config(false));
1841 engine.register_interface(make_interface(1, constants::MODE_FULL));
1842
1843 let identity = rns_crypto::identity::Identity::new(&mut rns_crypto::FixedRng::new(&[0x55; 32]));
1844 let dest_hash = destination_hash("test", &["app"], Some(identity.hash()));
1845 let name_h = name_hash("test", &["app"]);
1846 let random_hash = [0x42u8; 10];
1847
1848 let (announce_data, _) = AnnounceData::pack(
1849 &identity, &dest_hash, &name_h, &random_hash, None, None,
1850 ).unwrap();
1851
1852 let flags = PacketFlags {
1853 header_type: constants::HEADER_1,
1854 context_flag: constants::FLAG_UNSET,
1855 transport_type: constants::TRANSPORT_BROADCAST,
1856 destination_type: constants::DESTINATION_SINGLE,
1857 packet_type: constants::PACKET_TYPE_ANNOUNCE,
1858 };
1859 let packet = RawPacket::pack(flags, 0, &dest_hash, None, constants::CONTEXT_NONE, &announce_data).unwrap();
1860
1861 let now = 1000.0;
1863 engine.blackhole_identity(*identity.hash(), now, None, None);
1864
1865 let mut rng = rns_crypto::FixedRng::new(&[0x11; 32]);
1866 let actions = engine.handle_inbound(&packet.raw, InterfaceId(1), now, &mut rng);
1867
1868 assert!(actions.iter().all(|a| !matches!(a, TransportAction::AnnounceReceived { .. })));
1870 assert!(actions.iter().all(|a| !matches!(a, TransportAction::PathUpdated { .. })));
1871 }
1872
1873 #[test]
1874 fn test_tick_culls_expired_path() {
1875 let mut engine = TransportEngine::new(make_config(false));
1876 engine.register_interface(make_interface(1, constants::MODE_FULL));
1877
1878 let dest = [0x66; 16];
1879 engine.path_table.insert(
1880 dest,
1881 PathEntry {
1882 timestamp: 100.0,
1883 next_hop: [0; 16],
1884 hops: 2,
1885 expires: 200.0,
1886 random_blobs: Vec::new(),
1887 receiving_interface: InterfaceId(1),
1888 packet_hash: [0; 32],
1889 announce_raw: None,
1890 },
1891 );
1892
1893 assert!(engine.has_path(&dest));
1894
1895 let mut rng = rns_crypto::FixedRng::new(&[0; 32]);
1896 engine.tick(300.0, &mut rng);
1898
1899 assert!(!engine.has_path(&dest));
1900 }
1901
1902 fn make_local_client_interface(id: u64) -> InterfaceInfo {
1907 InterfaceInfo {
1908 id: InterfaceId(id),
1909 name: String::from("local_client"),
1910 mode: constants::MODE_FULL,
1911 out_capable: true,
1912 in_capable: true,
1913 bitrate: None,
1914 announce_rate_target: None,
1915 announce_rate_grace: 0,
1916 announce_rate_penalty: 0.0,
1917 announce_cap: constants::ANNOUNCE_CAP,
1918 is_local_client: true,
1919 wants_tunnel: false,
1920 tunnel_id: None,
1921 mtu: constants::MTU as u32,
1922 ingress_control: false,
1923 ia_freq: 0.0,
1924 started: 0.0,
1925 }
1926 }
1927
1928 #[test]
1929 fn test_has_local_clients() {
1930 let mut engine = TransportEngine::new(make_config(false));
1931 assert!(!engine.has_local_clients());
1932
1933 engine.register_interface(make_interface(1, constants::MODE_FULL));
1934 assert!(!engine.has_local_clients());
1935
1936 engine.register_interface(make_local_client_interface(2));
1937 assert!(engine.has_local_clients());
1938
1939 engine.deregister_interface(InterfaceId(2));
1940 assert!(!engine.has_local_clients());
1941 }
1942
1943 #[test]
1944 fn test_local_client_hop_decrement() {
1945 let mut engine = TransportEngine::new(make_config(false));
1948 engine.register_interface(make_local_client_interface(1));
1949 engine.register_interface(make_interface(2, constants::MODE_FULL));
1950
1951 let dest = [0xAA; 16];
1953 engine.register_destination(dest, constants::DESTINATION_PLAIN);
1954
1955 let flags = PacketFlags {
1956 header_type: constants::HEADER_1,
1957 context_flag: constants::FLAG_UNSET,
1958 transport_type: constants::TRANSPORT_BROADCAST,
1959 destination_type: constants::DESTINATION_PLAIN,
1960 packet_type: constants::PACKET_TYPE_DATA,
1961 };
1962 let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"hello").unwrap();
1964
1965 let mut rng = rns_crypto::FixedRng::new(&[0; 32]);
1966 let actions = engine.handle_inbound(&packet.raw, InterfaceId(1), 1000.0, &mut rng);
1967
1968 let deliver = actions.iter().find(|a| matches!(a, TransportAction::DeliverLocal { .. }));
1971 assert!(deliver.is_some(), "Should deliver locally");
1972 }
1973
1974 #[test]
1975 fn test_plain_broadcast_from_local_client() {
1976 let mut engine = TransportEngine::new(make_config(false));
1978 engine.register_interface(make_local_client_interface(1));
1979 engine.register_interface(make_interface(2, constants::MODE_FULL));
1980
1981 let dest = [0xBB; 16];
1982 let flags = PacketFlags {
1983 header_type: constants::HEADER_1,
1984 context_flag: constants::FLAG_UNSET,
1985 transport_type: constants::TRANSPORT_BROADCAST,
1986 destination_type: constants::DESTINATION_PLAIN,
1987 packet_type: constants::PACKET_TYPE_DATA,
1988 };
1989 let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"test").unwrap();
1990
1991 let mut rng = rns_crypto::FixedRng::new(&[0; 32]);
1992 let actions = engine.handle_inbound(&packet.raw, InterfaceId(1), 1000.0, &mut rng);
1993
1994 let forward = actions.iter().find(|a| matches!(
1996 a, TransportAction::ForwardPlainBroadcast { to_local: false, .. }
1997 ));
1998 assert!(forward.is_some(), "Should forward to external interfaces");
1999 }
2000
2001 #[test]
2002 fn test_plain_broadcast_from_external() {
2003 let mut engine = TransportEngine::new(make_config(false));
2005 engine.register_interface(make_local_client_interface(1));
2006 engine.register_interface(make_interface(2, constants::MODE_FULL));
2007
2008 let dest = [0xCC; 16];
2009 let flags = PacketFlags {
2010 header_type: constants::HEADER_1,
2011 context_flag: constants::FLAG_UNSET,
2012 transport_type: constants::TRANSPORT_BROADCAST,
2013 destination_type: constants::DESTINATION_PLAIN,
2014 packet_type: constants::PACKET_TYPE_DATA,
2015 };
2016 let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"test").unwrap();
2017
2018 let mut rng = rns_crypto::FixedRng::new(&[0; 32]);
2019 let actions = engine.handle_inbound(&packet.raw, InterfaceId(2), 1000.0, &mut rng);
2020
2021 let forward = actions.iter().find(|a| matches!(
2023 a, TransportAction::ForwardPlainBroadcast { to_local: true, .. }
2024 ));
2025 assert!(forward.is_some(), "Should forward to local clients");
2026 }
2027
2028 #[test]
2029 fn test_no_plain_broadcast_bridging_without_local_clients() {
2030 let mut engine = TransportEngine::new(make_config(false));
2032 engine.register_interface(make_interface(1, constants::MODE_FULL));
2033 engine.register_interface(make_interface(2, constants::MODE_FULL));
2034
2035 let dest = [0xDD; 16];
2036 let flags = PacketFlags {
2037 header_type: constants::HEADER_1,
2038 context_flag: constants::FLAG_UNSET,
2039 transport_type: constants::TRANSPORT_BROADCAST,
2040 destination_type: constants::DESTINATION_PLAIN,
2041 packet_type: constants::PACKET_TYPE_DATA,
2042 };
2043 let packet = RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"test").unwrap();
2044
2045 let mut rng = rns_crypto::FixedRng::new(&[0; 32]);
2046 let actions = engine.handle_inbound(&packet.raw, InterfaceId(1), 1000.0, &mut rng);
2047
2048 let has_forward = actions.iter().any(|a| matches!(
2050 a, TransportAction::ForwardPlainBroadcast { .. }
2051 ));
2052 assert!(!has_forward, "No bridging without local clients");
2053 }
2054
2055 #[test]
2056 fn test_announce_forwarded_to_local_clients() {
2057 use crate::announce::AnnounceData;
2058 use crate::destination::{destination_hash, name_hash};
2059
2060 let mut engine = TransportEngine::new(make_config(false));
2061 engine.register_interface(make_interface(1, constants::MODE_FULL));
2062 engine.register_interface(make_local_client_interface(2));
2063
2064 let identity = rns_crypto::identity::Identity::new(&mut rns_crypto::FixedRng::new(&[0x77; 32]));
2065 let dest_hash = destination_hash("test", &["fwd"], Some(identity.hash()));
2066 let name_h = name_hash("test", &["fwd"]);
2067 let random_hash = [0x42u8; 10];
2068
2069 let (announce_data, _) = AnnounceData::pack(
2070 &identity, &dest_hash, &name_h, &random_hash, None, None,
2071 ).unwrap();
2072
2073 let flags = PacketFlags {
2074 header_type: constants::HEADER_1,
2075 context_flag: constants::FLAG_UNSET,
2076 transport_type: constants::TRANSPORT_BROADCAST,
2077 destination_type: constants::DESTINATION_SINGLE,
2078 packet_type: constants::PACKET_TYPE_ANNOUNCE,
2079 };
2080 let packet = RawPacket::pack(flags, 0, &dest_hash, None, constants::CONTEXT_NONE, &announce_data).unwrap();
2081
2082 let mut rng = rns_crypto::FixedRng::new(&[0x11; 32]);
2083 let actions = engine.handle_inbound(&packet.raw, InterfaceId(1), 1000.0, &mut rng);
2084
2085 let forward = actions.iter().find(|a| matches!(
2087 a, TransportAction::ForwardToLocalClients { .. }
2088 ));
2089 assert!(forward.is_some(), "Should forward announce to local clients");
2090
2091 match forward.unwrap() {
2093 TransportAction::ForwardToLocalClients { exclude, .. } => {
2094 assert_eq!(*exclude, Some(InterfaceId(1)));
2095 }
2096 _ => unreachable!(),
2097 }
2098 }
2099
2100 #[test]
2101 fn test_no_announce_forward_without_local_clients() {
2102 use crate::announce::AnnounceData;
2103 use crate::destination::{destination_hash, name_hash};
2104
2105 let mut engine = TransportEngine::new(make_config(false));
2106 engine.register_interface(make_interface(1, constants::MODE_FULL));
2107
2108 let identity = rns_crypto::identity::Identity::new(&mut rns_crypto::FixedRng::new(&[0x88; 32]));
2109 let dest_hash = destination_hash("test", &["nofwd"], Some(identity.hash()));
2110 let name_h = name_hash("test", &["nofwd"]);
2111 let random_hash = [0x42u8; 10];
2112
2113 let (announce_data, _) = AnnounceData::pack(
2114 &identity, &dest_hash, &name_h, &random_hash, None, None,
2115 ).unwrap();
2116
2117 let flags = PacketFlags {
2118 header_type: constants::HEADER_1,
2119 context_flag: constants::FLAG_UNSET,
2120 transport_type: constants::TRANSPORT_BROADCAST,
2121 destination_type: constants::DESTINATION_SINGLE,
2122 packet_type: constants::PACKET_TYPE_ANNOUNCE,
2123 };
2124 let packet = RawPacket::pack(flags, 0, &dest_hash, None, constants::CONTEXT_NONE, &announce_data).unwrap();
2125
2126 let mut rng = rns_crypto::FixedRng::new(&[0x22; 32]);
2127 let actions = engine.handle_inbound(&packet.raw, InterfaceId(1), 1000.0, &mut rng);
2128
2129 let has_forward = actions.iter().any(|a| matches!(
2131 a, TransportAction::ForwardToLocalClients { .. }
2132 ));
2133 assert!(!has_forward, "No forward without local clients");
2134 }
2135
2136 #[test]
2137 fn test_local_client_exclude_from_forward() {
2138 use crate::announce::AnnounceData;
2139 use crate::destination::{destination_hash, name_hash};
2140
2141 let mut engine = TransportEngine::new(make_config(false));
2142 engine.register_interface(make_local_client_interface(1));
2143 engine.register_interface(make_local_client_interface(2));
2144
2145 let identity = rns_crypto::identity::Identity::new(&mut rns_crypto::FixedRng::new(&[0x99; 32]));
2146 let dest_hash = destination_hash("test", &["excl"], Some(identity.hash()));
2147 let name_h = name_hash("test", &["excl"]);
2148 let random_hash = [0x42u8; 10];
2149
2150 let (announce_data, _) = AnnounceData::pack(
2151 &identity, &dest_hash, &name_h, &random_hash, None, None,
2152 ).unwrap();
2153
2154 let flags = PacketFlags {
2155 header_type: constants::HEADER_1,
2156 context_flag: constants::FLAG_UNSET,
2157 transport_type: constants::TRANSPORT_BROADCAST,
2158 destination_type: constants::DESTINATION_SINGLE,
2159 packet_type: constants::PACKET_TYPE_ANNOUNCE,
2160 };
2161 let packet = RawPacket::pack(flags, 0, &dest_hash, None, constants::CONTEXT_NONE, &announce_data).unwrap();
2162
2163 let mut rng = rns_crypto::FixedRng::new(&[0x33; 32]);
2164 let actions = engine.handle_inbound(&packet.raw, InterfaceId(1), 1000.0, &mut rng);
2166
2167 let forward = actions.iter().find(|a| matches!(
2169 a, TransportAction::ForwardToLocalClients { .. }
2170 ));
2171 assert!(forward.is_some());
2172 match forward.unwrap() {
2173 TransportAction::ForwardToLocalClients { exclude, .. } => {
2174 assert_eq!(*exclude, Some(InterfaceId(1)));
2175 }
2176 _ => unreachable!(),
2177 }
2178 }
2179
2180 fn make_tunnel_interface(id: u64) -> InterfaceInfo {
2185 InterfaceInfo {
2186 id: InterfaceId(id),
2187 name: String::from("tunnel_iface"),
2188 mode: constants::MODE_FULL,
2189 out_capable: true,
2190 in_capable: true,
2191 bitrate: None,
2192 announce_rate_target: None,
2193 announce_rate_grace: 0,
2194 announce_rate_penalty: 0.0,
2195 announce_cap: constants::ANNOUNCE_CAP,
2196 is_local_client: false,
2197 wants_tunnel: true,
2198 tunnel_id: None,
2199 mtu: constants::MTU as u32,
2200 ingress_control: false,
2201 ia_freq: 0.0,
2202 started: 0.0,
2203 }
2204 }
2205
2206 #[test]
2207 fn test_handle_tunnel_new() {
2208 let mut engine = TransportEngine::new(make_config(true));
2209 engine.register_interface(make_tunnel_interface(1));
2210
2211 let tunnel_id = [0xAA; 32];
2212 let actions = engine.handle_tunnel(tunnel_id, InterfaceId(1), 1000.0);
2213
2214 assert!(actions.iter().any(|a| matches!(
2216 a, TransportAction::TunnelEstablished { .. }
2217 )));
2218
2219 let info = engine.interface_info(&InterfaceId(1)).unwrap();
2221 assert_eq!(info.tunnel_id, Some(tunnel_id));
2222
2223 assert_eq!(engine.tunnel_table().len(), 1);
2225 }
2226
2227 #[test]
2228 fn test_announce_stores_tunnel_path() {
2229 use crate::announce::AnnounceData;
2230 use crate::destination::{destination_hash, name_hash};
2231
2232 let mut engine = TransportEngine::new(make_config(false));
2233 let mut iface = make_tunnel_interface(1);
2234 let tunnel_id = [0xBB; 32];
2235 iface.tunnel_id = Some(tunnel_id);
2236 engine.register_interface(iface);
2237
2238 engine.handle_tunnel(tunnel_id, InterfaceId(1), 1000.0);
2240
2241 let identity = rns_crypto::identity::Identity::new(&mut rns_crypto::FixedRng::new(&[0xCC; 32]));
2243 let dest_hash = destination_hash("test", &["tunnel"], Some(identity.hash()));
2244 let name_h = name_hash("test", &["tunnel"]);
2245 let random_hash = [0x42u8; 10];
2246
2247 let (announce_data, _) = AnnounceData::pack(
2248 &identity, &dest_hash, &name_h, &random_hash, None, None,
2249 ).unwrap();
2250
2251 let flags = PacketFlags {
2252 header_type: constants::HEADER_1,
2253 context_flag: constants::FLAG_UNSET,
2254 transport_type: constants::TRANSPORT_BROADCAST,
2255 destination_type: constants::DESTINATION_SINGLE,
2256 packet_type: constants::PACKET_TYPE_ANNOUNCE,
2257 };
2258 let packet = RawPacket::pack(flags, 0, &dest_hash, None, constants::CONTEXT_NONE, &announce_data).unwrap();
2259
2260 let mut rng = rns_crypto::FixedRng::new(&[0xDD; 32]);
2261 engine.handle_inbound(&packet.raw, InterfaceId(1), 1000.0, &mut rng);
2262
2263 assert!(engine.has_path(&dest_hash));
2265
2266 let tunnel = engine.tunnel_table().get(&tunnel_id).unwrap();
2268 assert_eq!(tunnel.paths.len(), 1);
2269 assert!(tunnel.paths.contains_key(&dest_hash));
2270 }
2271
2272 #[test]
2273 fn test_tunnel_reattach_restores_paths() {
2274 let mut engine = TransportEngine::new(make_config(true));
2275 engine.register_interface(make_tunnel_interface(1));
2276
2277 let tunnel_id = [0xCC; 32];
2278 engine.handle_tunnel(tunnel_id, InterfaceId(1), 1000.0);
2279
2280 let dest = [0xDD; 16];
2282 engine.tunnel_table.store_tunnel_path(
2283 &tunnel_id,
2284 dest,
2285 tunnel::TunnelPath {
2286 timestamp: 1000.0,
2287 received_from: [0xEE; 16],
2288 hops: 3,
2289 expires: 1000.0 + constants::DESTINATION_TIMEOUT,
2290 random_blobs: Vec::new(),
2291 packet_hash: [0xFF; 32],
2292 },
2293 1000.0,
2294 );
2295
2296 engine.void_tunnel_interface(&tunnel_id);
2298
2299 engine.path_table.remove(&dest);
2301 assert!(!engine.has_path(&dest));
2302
2303 engine.register_interface(make_interface(2, constants::MODE_FULL));
2305 let actions = engine.handle_tunnel(tunnel_id, InterfaceId(2), 2000.0);
2306
2307 assert!(engine.has_path(&dest));
2309 let path = engine.path_table.get(&dest).unwrap();
2310 assert_eq!(path.hops, 3);
2311 assert_eq!(path.receiving_interface, InterfaceId(2));
2312
2313 assert!(actions.iter().any(|a| matches!(
2315 a, TransportAction::TunnelEstablished { .. }
2316 )));
2317 }
2318
2319 #[test]
2320 fn test_void_tunnel_interface() {
2321 let mut engine = TransportEngine::new(make_config(true));
2322 engine.register_interface(make_tunnel_interface(1));
2323
2324 let tunnel_id = [0xDD; 32];
2325 engine.handle_tunnel(tunnel_id, InterfaceId(1), 1000.0);
2326
2327 assert_eq!(
2329 engine.tunnel_table().get(&tunnel_id).unwrap().interface,
2330 Some(InterfaceId(1))
2331 );
2332
2333 engine.void_tunnel_interface(&tunnel_id);
2334
2335 assert_eq!(engine.tunnel_table().len(), 1);
2337 assert_eq!(
2338 engine.tunnel_table().get(&tunnel_id).unwrap().interface,
2339 None
2340 );
2341 }
2342
2343 #[test]
2344 fn test_tick_culls_tunnels() {
2345 let mut engine = TransportEngine::new(make_config(true));
2346 engine.register_interface(make_tunnel_interface(1));
2347
2348 let tunnel_id = [0xEE; 32];
2349 engine.handle_tunnel(tunnel_id, InterfaceId(1), 1000.0);
2350 assert_eq!(engine.tunnel_table().len(), 1);
2351
2352 let mut rng = rns_crypto::FixedRng::new(&[0; 32]);
2353
2354 engine.tick(1000.0 + constants::DESTINATION_TIMEOUT + constants::TABLES_CULL_INTERVAL + 1.0, &mut rng);
2356
2357 assert_eq!(engine.tunnel_table().len(), 0);
2358 }
2359
2360 #[test]
2361 fn test_synthesize_tunnel() {
2362 let mut engine = TransportEngine::new(make_config(true));
2363 engine.register_interface(make_tunnel_interface(1));
2364
2365 let identity = rns_crypto::identity::Identity::new(&mut rns_crypto::FixedRng::new(&[0xFF; 32]));
2366 let mut rng = rns_crypto::FixedRng::new(&[0x11; 32]);
2367
2368 let actions = engine.synthesize_tunnel(&identity, InterfaceId(1), &mut rng);
2369
2370 assert_eq!(actions.len(), 1);
2372 match &actions[0] {
2373 TransportAction::TunnelSynthesize { interface, data, dest_hash } => {
2374 assert_eq!(*interface, InterfaceId(1));
2375 assert_eq!(data.len(), tunnel::TUNNEL_SYNTH_LENGTH);
2376 let expected_dest = crate::destination::destination_hash(
2378 "rnstransport", &["tunnel", "synthesize"], None,
2379 );
2380 assert_eq!(*dest_hash, expected_dest);
2381 }
2382 _ => panic!("Expected TunnelSynthesize"),
2383 }
2384 }
2385
2386 fn make_path_request_data(dest_hash: &[u8; 16], tag: &[u8]) -> Vec<u8> {
2391 let mut data = Vec::new();
2392 data.extend_from_slice(dest_hash);
2393 data.extend_from_slice(tag);
2394 data
2395 }
2396
2397 #[test]
2398 fn test_path_request_forwarded_on_ap() {
2399 let mut engine = TransportEngine::new(make_config(true));
2400 engine.register_interface(make_interface(1, constants::MODE_ACCESS_POINT));
2401 engine.register_interface(make_interface(2, constants::MODE_FULL));
2402
2403 let dest = [0xD1; 16];
2404 let tag = [0x01; 16];
2405 let data = make_path_request_data(&dest, &tag);
2406
2407 let actions = engine.handle_path_request(&data, InterfaceId(1), 1000.0);
2408
2409 assert_eq!(actions.len(), 1);
2411 match &actions[0] {
2412 TransportAction::SendOnInterface { interface, .. } => {
2413 assert_eq!(*interface, InterfaceId(2));
2414 }
2415 _ => panic!("Expected SendOnInterface for forwarded path request"),
2416 }
2417 assert!(engine.discovery_path_requests.contains_key(&dest));
2419 }
2420
2421 #[test]
2422 fn test_path_request_not_forwarded_on_full() {
2423 let mut engine = TransportEngine::new(make_config(true));
2424 engine.register_interface(make_interface(1, constants::MODE_FULL));
2425 engine.register_interface(make_interface(2, constants::MODE_FULL));
2426
2427 let dest = [0xD2; 16];
2428 let tag = [0x02; 16];
2429 let data = make_path_request_data(&dest, &tag);
2430
2431 let actions = engine.handle_path_request(&data, InterfaceId(1), 1000.0);
2432
2433 assert!(actions.is_empty());
2435 assert!(!engine.discovery_path_requests.contains_key(&dest));
2436 }
2437
2438 #[test]
2439 fn test_roaming_loop_prevention() {
2440 let mut engine = TransportEngine::new(make_config(true));
2441 engine.register_interface(make_interface(1, constants::MODE_ROAMING));
2442
2443 let dest = [0xD3; 16];
2444 engine.path_table.insert(
2446 dest,
2447 PathEntry {
2448 timestamp: 900.0,
2449 next_hop: [0xAA; 16],
2450 hops: 2,
2451 expires: 9999.0,
2452 random_blobs: Vec::new(),
2453 receiving_interface: InterfaceId(1),
2454 packet_hash: [0; 32],
2455 announce_raw: None,
2456 },
2457 );
2458
2459 let tag = [0x03; 16];
2460 let data = make_path_request_data(&dest, &tag);
2461
2462 let actions = engine.handle_path_request(&data, InterfaceId(1), 1000.0);
2463
2464 assert!(actions.is_empty());
2466 assert!(!engine.announce_table.contains_key(&dest));
2467 }
2468
2469 #[test]
2470 fn test_discovery_request_consumed_on_announce() {
2471 let mut engine = TransportEngine::new(make_config(true));
2472 engine.register_interface(make_interface(1, constants::MODE_ACCESS_POINT));
2473
2474 let dest = [0xD4; 16];
2475
2476 engine.discovery_path_requests.insert(
2478 dest,
2479 DiscoveryPathRequest {
2480 timestamp: 900.0,
2481 requesting_interface: InterfaceId(1),
2482 },
2483 );
2484
2485 let iface = engine.discovery_path_requests_waiting(&dest);
2487 assert_eq!(iface, Some(InterfaceId(1)));
2488
2489 assert!(!engine.discovery_path_requests.contains_key(&dest));
2491 assert_eq!(engine.discovery_path_requests_waiting(&dest), None);
2492 }
2493}