1pub mod announce_proc;
2pub mod announce_queue;
3pub mod dedup;
4pub mod inbound;
5pub mod ingress_control;
6pub mod jobs;
7pub mod outbound;
8pub mod pathfinder;
9pub mod rate_limit;
10pub mod tables;
11pub mod tunnel;
12pub mod types;
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::inbound::{
29 create_link_entry, create_reverse_entry, forward_transport_packet, route_proof_via_reverse,
30 route_via_link_table,
31};
32use self::ingress_control::IngressControl;
33use self::outbound::{route_outbound, should_transmit_announce};
34use self::pathfinder::{
35 decide_announce_multipath, extract_random_blob, timebase_from_random_blob, MultiPathDecision,
36};
37use self::rate_limit::AnnounceRateLimiter;
38use self::tables::{AnnounceEntry, DiscoveryPathRequest, LinkEntry, PathEntry, PathSet};
39use self::tunnel::TunnelTable;
40use self::types::{BlackholeEntry, InterfaceId, InterfaceInfo, TransportAction, TransportConfig};
41
42pub type PathTableRow = ([u8; 16], f64, [u8; 16], u8, f64, String);
43pub type RateTableRow = ([u8; 16], f64, u32, f64, Vec<f64>);
44
45pub struct TransportEngine {
50 config: TransportConfig,
51 path_table: BTreeMap<[u8; 16], PathSet>,
52 announce_table: BTreeMap<[u8; 16], AnnounceEntry>,
53 reverse_table: BTreeMap<[u8; 16], tables::ReverseEntry>,
54 link_table: BTreeMap<[u8; 16], LinkEntry>,
55 held_announces: BTreeMap<[u8; 16], AnnounceEntry>,
56 packet_hashlist: PacketHashlist,
57 rate_limiter: AnnounceRateLimiter,
58 path_states: BTreeMap<[u8; 16], u8>,
59 interfaces: BTreeMap<InterfaceId, InterfaceInfo>,
60 local_destinations: BTreeMap<[u8; 16], u8>,
61 blackholed_identities: BTreeMap<[u8; 16], BlackholeEntry>,
62 announce_queues: AnnounceQueues,
63 ingress_control: IngressControl,
64 tunnel_table: TunnelTable,
65 discovery_pr_tags: Vec<[u8; 32]>,
66 discovery_path_requests: BTreeMap<[u8; 16], DiscoveryPathRequest>,
67 announces_last_checked: f64,
69 tables_last_culled: f64,
70}
71
72impl TransportEngine {
73 pub fn new(config: TransportConfig) -> Self {
74 let packet_hashlist_max_entries = config.packet_hashlist_max_entries;
75 TransportEngine {
76 config,
77 path_table: BTreeMap::new(),
78 announce_table: BTreeMap::new(),
79 reverse_table: BTreeMap::new(),
80 link_table: BTreeMap::new(),
81 held_announces: BTreeMap::new(),
82 packet_hashlist: PacketHashlist::new(packet_hashlist_max_entries),
83 rate_limiter: AnnounceRateLimiter::new(),
84 path_states: BTreeMap::new(),
85 interfaces: BTreeMap::new(),
86 local_destinations: BTreeMap::new(),
87 blackholed_identities: BTreeMap::new(),
88 announce_queues: AnnounceQueues::new(),
89 ingress_control: IngressControl::new(),
90 tunnel_table: TunnelTable::new(),
91 discovery_pr_tags: Vec::new(),
92 discovery_path_requests: BTreeMap::new(),
93 announces_last_checked: 0.0,
94 tables_last_culled: 0.0,
95 }
96 }
97
98 pub fn register_interface(&mut self, info: InterfaceInfo) {
103 self.interfaces.insert(info.id, info);
104 }
105
106 pub fn deregister_interface(&mut self, id: InterfaceId) {
107 self.interfaces.remove(&id);
108 self.ingress_control.remove_interface(&id);
109 }
110
111 pub fn register_destination(&mut self, dest_hash: [u8; 16], dest_type: u8) {
116 self.local_destinations.insert(dest_hash, dest_type);
117 }
118
119 pub fn deregister_destination(&mut self, dest_hash: &[u8; 16]) {
120 self.local_destinations.remove(dest_hash);
121 }
122
123 pub fn has_path(&self, dest_hash: &[u8; 16]) -> bool {
128 self.path_table
129 .get(dest_hash)
130 .is_some_and(|ps| !ps.is_empty())
131 }
132
133 pub fn hops_to(&self, dest_hash: &[u8; 16]) -> Option<u8> {
134 self.path_table
135 .get(dest_hash)
136 .and_then(|ps| ps.primary())
137 .map(|e| e.hops)
138 }
139
140 pub fn next_hop(&self, dest_hash: &[u8; 16]) -> Option<[u8; 16]> {
141 self.path_table
142 .get(dest_hash)
143 .and_then(|ps| ps.primary())
144 .map(|e| e.next_hop)
145 }
146
147 pub fn next_hop_interface(&self, dest_hash: &[u8; 16]) -> Option<InterfaceId> {
148 self.path_table
149 .get(dest_hash)
150 .and_then(|ps| ps.primary())
151 .map(|e| e.receiving_interface)
152 }
153
154 pub fn mark_path_unresponsive(
164 &mut self,
165 dest_hash: &[u8; 16],
166 receiving_interface: Option<InterfaceId>,
167 ) {
168 if let Some(iface_id) = receiving_interface {
169 if let Some(info) = self.interfaces.get(&iface_id) {
170 if info.mode == constants::MODE_BOUNDARY {
171 return;
172 }
173 }
174 }
175
176 if let Some(ps) = self.path_table.get_mut(dest_hash) {
178 if ps.len() > 1 {
179 ps.failover(false); self.path_states.remove(dest_hash);
182 return;
183 }
184 }
185
186 self.path_states
187 .insert(*dest_hash, constants::STATE_UNRESPONSIVE);
188 }
189
190 pub fn mark_path_responsive(&mut self, dest_hash: &[u8; 16]) {
191 self.path_states
192 .insert(*dest_hash, constants::STATE_RESPONSIVE);
193 }
194
195 pub fn path_is_unresponsive(&self, dest_hash: &[u8; 16]) -> bool {
196 self.path_states.get(dest_hash) == Some(&constants::STATE_UNRESPONSIVE)
197 }
198
199 pub fn expire_path(&mut self, dest_hash: &[u8; 16]) {
200 if let Some(ps) = self.path_table.get_mut(dest_hash) {
201 ps.expire_all();
202 }
203 }
204
205 pub fn register_link(&mut self, link_id: [u8; 16], entry: LinkEntry) {
210 self.link_table.insert(link_id, entry);
211 }
212
213 pub fn validate_link(&mut self, link_id: &[u8; 16]) {
214 if let Some(entry) = self.link_table.get_mut(link_id) {
215 entry.validated = true;
216 }
217 }
218
219 pub fn remove_link(&mut self, link_id: &[u8; 16]) {
220 self.link_table.remove(link_id);
221 }
222
223 pub fn blackhole_identity(
229 &mut self,
230 identity_hash: [u8; 16],
231 now: f64,
232 duration_hours: Option<f64>,
233 reason: Option<String>,
234 ) {
235 let expires = match duration_hours {
236 Some(h) if h > 0.0 => now + h * 3600.0,
237 _ => 0.0, };
239 self.blackholed_identities.insert(
240 identity_hash,
241 BlackholeEntry {
242 created: now,
243 expires,
244 reason,
245 },
246 );
247 }
248
249 pub fn unblackhole_identity(&mut self, identity_hash: &[u8; 16]) -> bool {
251 self.blackholed_identities.remove(identity_hash).is_some()
252 }
253
254 pub fn is_blackholed(&self, identity_hash: &[u8; 16], now: f64) -> bool {
256 if let Some(entry) = self.blackholed_identities.get(identity_hash) {
257 if entry.expires == 0.0 || entry.expires > now {
258 return true;
259 }
260 }
261 false
262 }
263
264 pub fn blackholed_entries(&self) -> impl Iterator<Item = (&[u8; 16], &BlackholeEntry)> {
266 self.blackholed_identities.iter()
267 }
268
269 fn cull_blackholed(&mut self, now: f64) {
271 self.blackholed_identities
272 .retain(|_, entry| entry.expires == 0.0 || entry.expires > now);
273 }
274
275 pub fn handle_tunnel(
283 &mut self,
284 tunnel_id: [u8; 32],
285 interface: InterfaceId,
286 now: f64,
287 ) -> Vec<TransportAction> {
288 let mut actions = Vec::new();
289
290 if let Some(info) = self.interfaces.get_mut(&interface) {
292 info.tunnel_id = Some(tunnel_id);
293 }
294
295 let restored_paths = self.tunnel_table.handle_tunnel(tunnel_id, interface, now);
296
297 let max_paths = self.config.max_paths_per_destination;
299 for (dest_hash, tunnel_path) in &restored_paths {
300 let should_restore = match self.path_table.get(dest_hash).and_then(|ps| ps.primary()) {
301 Some(existing) => {
302 tunnel_path.hops <= existing.hops || existing.expires < now
304 }
305 None => true,
306 };
307
308 if should_restore {
309 let entry = PathEntry {
310 timestamp: tunnel_path.timestamp,
311 next_hop: tunnel_path.received_from,
312 hops: tunnel_path.hops,
313 expires: tunnel_path.expires,
314 random_blobs: tunnel_path.random_blobs.clone(),
315 receiving_interface: interface,
316 packet_hash: tunnel_path.packet_hash,
317 announce_raw: None,
318 };
319 self.path_table
320 .insert(*dest_hash, PathSet::from_single(entry, max_paths));
321 }
322 }
323
324 actions.push(TransportAction::TunnelEstablished {
325 tunnel_id,
326 interface,
327 });
328
329 actions
330 }
331
332 pub fn synthesize_tunnel(
340 &self,
341 identity: &rns_crypto::identity::Identity,
342 interface_id: InterfaceId,
343 rng: &mut dyn Rng,
344 ) -> Vec<TransportAction> {
345 let mut actions = Vec::new();
346
347 let interface_hash = if let Some(info) = self.interfaces.get(&interface_id) {
349 hash::full_hash(info.name.as_bytes())
350 } else {
351 return actions;
352 };
353
354 match tunnel::build_tunnel_synthesize_data(identity, &interface_hash, rng) {
355 Ok((data, _tunnel_id)) => {
356 let dest_hash = crate::destination::destination_hash(
357 "rnstransport",
358 &["tunnel", "synthesize"],
359 None,
360 );
361 actions.push(TransportAction::TunnelSynthesize {
362 interface: interface_id,
363 data,
364 dest_hash,
365 });
366 }
367 Err(e) => {
368 let _ = e;
370 }
371 }
372
373 actions
374 }
375
376 pub fn void_tunnel_interface(&mut self, tunnel_id: &[u8; 32]) {
378 self.tunnel_table.void_tunnel_interface(tunnel_id);
379 }
380
381 pub fn tunnel_table(&self) -> &TunnelTable {
383 &self.tunnel_table
384 }
385
386 fn has_local_clients(&self) -> bool {
392 self.interfaces.values().any(|i| i.is_local_client)
393 }
394
395 fn packet_filter(&self, packet: &RawPacket) -> bool {
399 if packet.transport_id.is_some()
401 && packet.flags.packet_type != constants::PACKET_TYPE_ANNOUNCE
402 {
403 if let Some(ref identity_hash) = self.config.identity_hash {
404 if packet.transport_id.as_ref() != Some(identity_hash) {
405 return false;
406 }
407 }
408 }
409
410 match packet.context {
412 constants::CONTEXT_KEEPALIVE
413 | constants::CONTEXT_RESOURCE_REQ
414 | constants::CONTEXT_RESOURCE_PRF
415 | constants::CONTEXT_RESOURCE
416 | constants::CONTEXT_CACHE_REQUEST
417 | constants::CONTEXT_CHANNEL => return true,
418 _ => {}
419 }
420
421 if packet.flags.destination_type == constants::DESTINATION_PLAIN
423 || packet.flags.destination_type == constants::DESTINATION_GROUP
424 {
425 if packet.flags.packet_type != constants::PACKET_TYPE_ANNOUNCE {
426 return packet.hops <= 1;
427 } else {
428 return false;
430 }
431 }
432
433 if !self.packet_hashlist.is_duplicate(&packet.packet_hash) {
435 return true;
436 }
437
438 if packet.flags.packet_type == constants::PACKET_TYPE_ANNOUNCE
440 && packet.flags.destination_type == constants::DESTINATION_SINGLE
441 {
442 return true;
443 }
444
445 false
446 }
447
448 pub fn handle_inbound(
456 &mut self,
457 raw: &[u8],
458 iface: InterfaceId,
459 now: f64,
460 rng: &mut dyn Rng,
461 ) -> Vec<TransportAction> {
462 let mut actions = Vec::new();
463
464 let mut packet = match RawPacket::unpack(raw) {
466 Ok(p) => p,
467 Err(_) => return actions, };
469
470 let original_raw = raw.to_vec();
472
473 packet.hops += 1;
475
476 let from_local_client = self
479 .interfaces
480 .get(&iface)
481 .map(|i| i.is_local_client)
482 .unwrap_or(false);
483 if from_local_client {
484 packet.hops = packet.hops.saturating_sub(1);
485 }
486
487 if !self.packet_filter(&packet) {
489 return actions;
490 }
491
492 let mut remember_hash = true;
494
495 if self.link_table.contains_key(&packet.destination_hash) {
496 remember_hash = false;
497 }
498 if packet.flags.packet_type == constants::PACKET_TYPE_PROOF
499 && packet.context == constants::CONTEXT_LRPROOF
500 {
501 remember_hash = false;
502 }
503
504 if remember_hash {
505 self.packet_hashlist.add(packet.packet_hash);
506 }
507
508 if packet.flags.destination_type == constants::DESTINATION_PLAIN
510 && packet.flags.transport_type == constants::TRANSPORT_BROADCAST
511 && self.has_local_clients()
512 {
513 if from_local_client {
514 actions.push(TransportAction::ForwardPlainBroadcast {
516 raw: packet.raw.clone(),
517 to_local: false,
518 exclude: Some(iface),
519 });
520 } else {
521 actions.push(TransportAction::ForwardPlainBroadcast {
523 raw: packet.raw.clone(),
524 to_local: true,
525 exclude: None,
526 });
527 }
528 }
529
530 if self.config.transport_enabled || self.config.identity_hash.is_some() {
532 if packet.transport_id.is_some()
533 && packet.flags.packet_type != constants::PACKET_TYPE_ANNOUNCE
534 {
535 if let Some(ref identity_hash) = self.config.identity_hash {
536 if packet.transport_id.as_ref() == Some(identity_hash) {
537 if let Some(path_entry) = self
538 .path_table
539 .get(&packet.destination_hash)
540 .and_then(|ps| ps.primary())
541 {
542 let next_hop = path_entry.next_hop;
543 let remaining_hops = path_entry.hops;
544 let outbound_interface = path_entry.receiving_interface;
545
546 let new_raw = forward_transport_packet(
547 &packet,
548 next_hop,
549 remaining_hops,
550 outbound_interface,
551 );
552
553 if packet.flags.packet_type == constants::PACKET_TYPE_LINKREQUEST {
555 let proof_timeout = now
556 + constants::LINK_ESTABLISHMENT_TIMEOUT_PER_HOP
557 * (remaining_hops.max(1) as f64);
558
559 let (link_id, link_entry) = create_link_entry(
560 &packet,
561 next_hop,
562 outbound_interface,
563 remaining_hops,
564 iface,
565 now,
566 proof_timeout,
567 );
568 self.link_table.insert(link_id, link_entry);
569 actions.push(TransportAction::LinkRequestReceived {
570 link_id,
571 destination_hash: packet.destination_hash,
572 receiving_interface: iface,
573 });
574 } else {
575 let (trunc_hash, reverse_entry) =
576 create_reverse_entry(&packet, outbound_interface, iface, now);
577 self.reverse_table.insert(trunc_hash, reverse_entry);
578 }
579
580 actions.push(TransportAction::SendOnInterface {
581 interface: outbound_interface,
582 raw: new_raw,
583 });
584
585 if let Some(entry) = self
587 .path_table
588 .get_mut(&packet.destination_hash)
589 .and_then(|ps| ps.primary_mut())
590 {
591 entry.timestamp = now;
592 }
593 }
594 }
595 }
596 }
597
598 if packet.flags.packet_type != constants::PACKET_TYPE_ANNOUNCE
600 && packet.flags.packet_type != constants::PACKET_TYPE_LINKREQUEST
601 && packet.context != constants::CONTEXT_LRPROOF
602 {
603 if let Some(link_entry) = self.link_table.get(&packet.destination_hash).cloned() {
604 if let Some((outbound_iface, new_raw)) =
605 route_via_link_table(&packet, &link_entry, iface)
606 {
607 self.packet_hashlist.add(packet.packet_hash);
609
610 actions.push(TransportAction::SendOnInterface {
611 interface: outbound_iface,
612 raw: new_raw,
613 });
614
615 if let Some(entry) = self.link_table.get_mut(&packet.destination_hash) {
617 entry.timestamp = now;
618 }
619 }
620 }
621 }
622 }
623
624 if packet.flags.packet_type == constants::PACKET_TYPE_ANNOUNCE {
626 self.process_inbound_announce(&packet, &original_raw, iface, now, rng, &mut actions);
627 }
628
629 if packet.flags.packet_type == constants::PACKET_TYPE_PROOF {
631 self.process_inbound_proof(&packet, iface, now, &mut actions);
632 }
633
634 if (packet.flags.packet_type == constants::PACKET_TYPE_LINKREQUEST
636 || packet.flags.packet_type == constants::PACKET_TYPE_DATA)
637 && self.local_destinations.contains_key(&packet.destination_hash)
638 {
639 actions.push(TransportAction::DeliverLocal {
640 destination_hash: packet.destination_hash,
641 raw: packet.raw.clone(),
642 packet_hash: packet.packet_hash,
643 receiving_interface: iface,
644 });
645 }
646
647 actions
648 }
649
650 fn process_inbound_announce(
655 &mut self,
656 packet: &RawPacket,
657 original_raw: &[u8],
658 iface: InterfaceId,
659 now: f64,
660 rng: &mut dyn Rng,
661 actions: &mut Vec<TransportAction>,
662 ) {
663 if packet.flags.destination_type != constants::DESTINATION_SINGLE {
664 return;
665 }
666
667 let has_ratchet = packet.flags.context_flag == constants::FLAG_SET;
668
669 let announce = match AnnounceData::unpack(&packet.data, has_ratchet) {
671 Ok(a) => a,
672 Err(_) => return,
673 };
674
675 let validated = match announce.validate(&packet.destination_hash) {
676 Ok(v) => v,
677 Err(_) => return,
678 };
679
680 if self.is_blackholed(&validated.identity_hash, now) {
682 return;
683 }
684
685 if self
687 .local_destinations
688 .contains_key(&packet.destination_hash)
689 {
690 log::debug!(
691 "Announce:skipping local destination {:02x}{:02x}{:02x}{:02x}..",
692 packet.destination_hash[0],
693 packet.destination_hash[1],
694 packet.destination_hash[2],
695 packet.destination_hash[3],
696 );
697 return;
698 }
699
700 if !self.has_path(&packet.destination_hash) {
702 if let Some(info) = self.interfaces.get(&iface) {
703 if info.ingress_control
704 && self.ingress_control.should_ingress_limit(
705 iface,
706 info.ia_freq,
707 info.started,
708 now,
709 )
710 {
711 self.ingress_control.hold_announce(
712 iface,
713 packet.destination_hash,
714 ingress_control::HeldAnnounce {
715 raw: original_raw.to_vec(),
716 hops: packet.hops,
717 receiving_interface: iface,
718 timestamp: now,
719 },
720 );
721 return;
722 }
723 }
724 }
725
726 let received_from = if let Some(transport_id) = packet.transport_id {
728 if self.config.transport_enabled {
730 if let Some(announce_entry) = self.announce_table.get_mut(&packet.destination_hash)
731 {
732 if packet.hops.checked_sub(1) == Some(announce_entry.hops) {
733 announce_entry.local_rebroadcasts += 1;
734 if announce_entry.retries > 0
735 && announce_entry.local_rebroadcasts
736 >= constants::LOCAL_REBROADCASTS_MAX
737 {
738 self.announce_table.remove(&packet.destination_hash);
739 }
740 }
741 if let Some(announce_entry) = self.announce_table.get(&packet.destination_hash)
743 {
744 if packet.hops.checked_sub(1) == Some(announce_entry.hops + 1)
745 && announce_entry.retries > 0
746 && now < announce_entry.retransmit_timeout
747 {
748 self.announce_table.remove(&packet.destination_hash);
749 }
750 }
751 }
752 }
753 transport_id
754 } else {
755 packet.destination_hash
756 };
757
758 let random_blob = match extract_random_blob(&packet.data) {
760 Some(b) => b,
761 None => return,
762 };
763
764 if packet.hops > constants::PATHFINDER_M {
766 return;
767 }
768
769 let announce_emitted = timebase_from_random_blob(&random_blob);
770
771 let existing_set = self.path_table.get(&packet.destination_hash);
773 let is_unresponsive = self.path_is_unresponsive(&packet.destination_hash);
774
775 let mp_decision = decide_announce_multipath(
776 existing_set,
777 packet.hops,
778 announce_emitted,
779 &random_blob,
780 &received_from,
781 is_unresponsive,
782 now,
783 self.config.prefer_shorter_path,
784 );
785
786 if mp_decision == MultiPathDecision::Reject {
787 log::debug!(
788 "Announce:path decision REJECT for dest={:02x}{:02x}{:02x}{:02x}..",
789 packet.destination_hash[0],
790 packet.destination_hash[1],
791 packet.destination_hash[2],
792 packet.destination_hash[3],
793 );
794 return;
795 }
796
797 let rate_blocked = if packet.context != constants::CONTEXT_PATH_RESPONSE {
799 if let Some(iface_info) = self.interfaces.get(&iface) {
800 self.rate_limiter.check_and_update(
801 &packet.destination_hash,
802 now,
803 iface_info.announce_rate_target,
804 iface_info.announce_rate_grace,
805 iface_info.announce_rate_penalty,
806 )
807 } else {
808 false
809 }
810 } else {
811 false
812 };
813
814 let interface_mode = self
816 .interfaces
817 .get(&iface)
818 .map(|i| i.mode)
819 .unwrap_or(constants::MODE_FULL);
820
821 let expires = compute_path_expires(now, interface_mode);
822
823 let existing_blobs = self
825 .path_table
826 .get(&packet.destination_hash)
827 .and_then(|ps| ps.find_by_next_hop(&received_from))
828 .map(|e| e.random_blobs.clone())
829 .unwrap_or_default();
830
831 let mut rng_bytes = [0u8; 8];
833 rng.fill_bytes(&mut rng_bytes);
834 let rng_value = (u64::from_le_bytes(rng_bytes) as f64) / (u64::MAX as f64);
835
836 let is_path_response = packet.context == constants::CONTEXT_PATH_RESPONSE;
837
838 let (path_entry, announce_entry) = announce_proc::process_validated_announce(
839 packet.destination_hash,
840 packet.hops,
841 &packet.data,
842 &packet.raw,
843 packet.packet_hash,
844 packet.flags.context_flag,
845 received_from,
846 iface,
847 now,
848 existing_blobs,
849 random_blob,
850 expires,
851 rng_value,
852 self.config.transport_enabled,
853 is_path_response,
854 rate_blocked,
855 Some(original_raw.to_vec()),
856 );
857
858 actions.push(TransportAction::CacheAnnounce {
860 packet_hash: packet.packet_hash,
861 raw: original_raw.to_vec(),
862 });
863
864 let max_paths = self.config.max_paths_per_destination;
866 if let Some(ps) = self.path_table.get_mut(&packet.destination_hash) {
867 ps.upsert(path_entry);
868 } else {
869 self.path_table.insert(
870 packet.destination_hash,
871 PathSet::from_single(path_entry, max_paths),
872 );
873 }
874
875 if let Some(tunnel_id) = self.interfaces.get(&iface).and_then(|i| i.tunnel_id) {
877 let blobs = self
878 .path_table
879 .get(&packet.destination_hash)
880 .and_then(|ps| ps.find_by_next_hop(&received_from))
881 .map(|e| e.random_blobs.clone())
882 .unwrap_or_default();
883 self.tunnel_table.store_tunnel_path(
884 &tunnel_id,
885 packet.destination_hash,
886 tunnel::TunnelPath {
887 timestamp: now,
888 received_from,
889 hops: packet.hops,
890 expires,
891 random_blobs: blobs,
892 packet_hash: packet.packet_hash,
893 },
894 now,
895 );
896 }
897
898 self.path_states.remove(&packet.destination_hash);
900
901 if let Some(ann) = announce_entry {
903 self.announce_table.insert(packet.destination_hash, ann);
904 }
905
906 actions.push(TransportAction::AnnounceReceived {
908 destination_hash: packet.destination_hash,
909 identity_hash: validated.identity_hash,
910 public_key: validated.public_key,
911 name_hash: validated.name_hash,
912 random_hash: validated.random_hash,
913 app_data: validated.app_data,
914 hops: packet.hops,
915 receiving_interface: iface,
916 });
917
918 actions.push(TransportAction::PathUpdated {
919 destination_hash: packet.destination_hash,
920 hops: packet.hops,
921 next_hop: received_from,
922 interface: iface,
923 });
924
925 if self.has_local_clients() {
927 actions.push(TransportAction::ForwardToLocalClients {
928 raw: packet.raw.clone(),
929 exclude: Some(iface),
930 });
931 }
932
933 if let Some(pr_entry) = self.discovery_path_requests_waiting(&packet.destination_hash) {
935 let entry = AnnounceEntry {
937 timestamp: now,
938 retransmit_timeout: now,
939 retries: constants::PATHFINDER_R,
940 received_from,
941 hops: packet.hops,
942 packet_raw: packet.raw.clone(),
943 packet_data: packet.data.clone(),
944 destination_hash: packet.destination_hash,
945 context_flag: packet.flags.context_flag,
946 local_rebroadcasts: 0,
947 block_rebroadcasts: true,
948 attached_interface: Some(pr_entry),
949 };
950 self.announce_table.insert(packet.destination_hash, entry);
951 }
952 }
953
954 fn discovery_path_requests_waiting(&mut self, dest_hash: &[u8; 16]) -> Option<InterfaceId> {
957 self.discovery_path_requests
958 .remove(dest_hash)
959 .map(|req| req.requesting_interface)
960 }
961
962 fn process_inbound_proof(
967 &mut self,
968 packet: &RawPacket,
969 iface: InterfaceId,
970 _now: f64,
971 actions: &mut Vec<TransportAction>,
972 ) {
973 if packet.context == constants::CONTEXT_LRPROOF {
974 if (self.config.transport_enabled)
976 && self.link_table.contains_key(&packet.destination_hash)
977 {
978 let link_entry = self.link_table.get(&packet.destination_hash).cloned();
979 if let Some(entry) = link_entry {
980 if packet.hops == entry.remaining_hops && iface == entry.next_hop_interface {
981 let mut new_raw = Vec::new();
984 new_raw.push(packet.raw[0]);
985 new_raw.push(packet.hops);
986 new_raw.extend_from_slice(&packet.raw[2..]);
987
988 if let Some(le) = self.link_table.get_mut(&packet.destination_hash) {
990 le.validated = true;
991 }
992
993 actions.push(TransportAction::LinkEstablished {
994 link_id: packet.destination_hash,
995 interface: entry.received_interface,
996 });
997
998 actions.push(TransportAction::SendOnInterface {
999 interface: entry.received_interface,
1000 raw: new_raw,
1001 });
1002 }
1003 }
1004 } else {
1005 actions.push(TransportAction::DeliverLocal {
1007 destination_hash: packet.destination_hash,
1008 raw: packet.raw.clone(),
1009 packet_hash: packet.packet_hash,
1010 receiving_interface: iface,
1011 });
1012 }
1013 } else {
1014 if self.config.transport_enabled {
1016 if let Some(reverse_entry) = self.reverse_table.remove(&packet.destination_hash) {
1017 if let Some(action) = route_proof_via_reverse(packet, &reverse_entry, iface) {
1018 actions.push(action);
1019 }
1020 }
1021 }
1022
1023 actions.push(TransportAction::DeliverLocal {
1025 destination_hash: packet.destination_hash,
1026 raw: packet.raw.clone(),
1027 packet_hash: packet.packet_hash,
1028 receiving_interface: iface,
1029 });
1030 }
1031 }
1032
1033 pub fn handle_outbound(
1039 &mut self,
1040 packet: &RawPacket,
1041 dest_type: u8,
1042 attached_interface: Option<InterfaceId>,
1043 now: f64,
1044 ) -> Vec<TransportAction> {
1045 let actions = route_outbound(
1046 &self.path_table,
1047 &self.interfaces,
1048 &self.local_destinations,
1049 packet,
1050 dest_type,
1051 attached_interface,
1052 now,
1053 );
1054
1055 self.packet_hashlist.add(packet.packet_hash);
1057
1058 if packet.flags.packet_type == constants::PACKET_TYPE_ANNOUNCE && packet.hops > 0 {
1060 self.gate_announce_actions(actions, &packet.destination_hash, packet.hops, now)
1061 } else {
1062 actions
1063 }
1064 }
1065
1066 fn gate_announce_actions(
1068 &mut self,
1069 actions: Vec<TransportAction>,
1070 dest_hash: &[u8; 16],
1071 hops: u8,
1072 now: f64,
1073 ) -> Vec<TransportAction> {
1074 let mut result = Vec::new();
1075 for action in actions {
1076 match action {
1077 TransportAction::SendOnInterface { interface, raw } => {
1078 let (bitrate, announce_cap) =
1079 if let Some(info) = self.interfaces.get(&interface) {
1080 (info.bitrate, info.announce_cap)
1081 } else {
1082 (None, constants::ANNOUNCE_CAP)
1083 };
1084 if let Some(send_action) = self.announce_queues.gate_announce(
1085 interface,
1086 raw,
1087 *dest_hash,
1088 hops,
1089 now,
1090 now,
1091 bitrate,
1092 announce_cap,
1093 ) {
1094 result.push(send_action);
1095 }
1096 }
1098 other => result.push(other),
1099 }
1100 }
1101 result
1102 }
1103
1104 pub fn tick(&mut self, now: f64, rng: &mut dyn Rng) -> Vec<TransportAction> {
1110 let mut actions = Vec::new();
1111
1112 if now > self.announces_last_checked + constants::ANNOUNCES_CHECK_INTERVAL {
1114 if let Some(ref identity_hash) = self.config.identity_hash {
1115 let ih = *identity_hash;
1116 let announce_actions = jobs::process_pending_announces(
1117 &mut self.announce_table,
1118 &mut self.held_announces,
1119 &ih,
1120 now,
1121 );
1122 let gated = self.gate_retransmit_actions(announce_actions, now);
1124 actions.extend(gated);
1125 }
1126 self.announces_last_checked = now;
1127 }
1128
1129 let mut queue_actions = self.announce_queues.process_queues(now, &self.interfaces);
1131 actions.append(&mut queue_actions);
1132
1133 let ic_interfaces = self.ingress_control.interfaces_with_held();
1135 for iface_id in ic_interfaces {
1136 let (ia_freq, started, ic_enabled) = match self.interfaces.get(&iface_id) {
1137 Some(info) => (info.ia_freq, info.started, info.ingress_control),
1138 None => continue,
1139 };
1140 if !ic_enabled {
1141 continue;
1142 }
1143 if let Some(held) = self
1144 .ingress_control
1145 .process_held_announces(iface_id, ia_freq, started, now)
1146 {
1147 let released_actions =
1148 self.handle_inbound(&held.raw, held.receiving_interface, now, rng);
1149 actions.extend(released_actions);
1150 }
1151 }
1152
1153 if now > self.tables_last_culled + constants::TABLES_CULL_INTERVAL {
1155 jobs::cull_path_table(&mut self.path_table, &self.interfaces, now);
1156 jobs::cull_reverse_table(&mut self.reverse_table, &self.interfaces, now);
1157 let (_culled, link_closed_actions) =
1158 jobs::cull_link_table(&mut self.link_table, &self.interfaces, now);
1159 actions.extend(link_closed_actions);
1160 jobs::cull_path_states(&mut self.path_states, &self.path_table);
1161 self.cull_blackholed(now);
1162 self.discovery_path_requests
1164 .retain(|_, req| now - req.timestamp < constants::DISCOVERY_PATH_REQUEST_TIMEOUT);
1165 self.tunnel_table
1167 .void_missing_interfaces(|id| self.interfaces.contains_key(id));
1168 self.tunnel_table.cull(now);
1169 self.tables_last_culled = now;
1170 }
1171
1172 if self.discovery_pr_tags.len() > constants::MAX_PR_TAGS {
1174 let start = self.discovery_pr_tags.len() - constants::MAX_PR_TAGS;
1175 self.discovery_pr_tags = self.discovery_pr_tags[start..].to_vec();
1176 }
1177
1178 actions
1179 }
1180
1181 fn gate_retransmit_actions(
1186 &mut self,
1187 actions: Vec<TransportAction>,
1188 now: f64,
1189 ) -> Vec<TransportAction> {
1190 let mut result = Vec::new();
1191 for action in actions {
1192 match action {
1193 TransportAction::SendOnInterface { interface, raw } => {
1194 let (dest_hash, hops) = Self::extract_announce_info(&raw);
1196 let (bitrate, announce_cap) =
1197 if let Some(info) = self.interfaces.get(&interface) {
1198 (info.bitrate, info.announce_cap)
1199 } else {
1200 (None, constants::ANNOUNCE_CAP)
1201 };
1202 if let Some(send_action) = self.announce_queues.gate_announce(
1203 interface,
1204 raw,
1205 dest_hash,
1206 hops,
1207 now,
1208 now,
1209 bitrate,
1210 announce_cap,
1211 ) {
1212 result.push(send_action);
1213 }
1214 }
1215 TransportAction::BroadcastOnAllInterfaces { raw, exclude } => {
1216 let (dest_hash, hops) = Self::extract_announce_info(&raw);
1217 let iface_ids: Vec<(InterfaceId, Option<u64>, f64)> = self
1220 .interfaces
1221 .iter()
1222 .filter(|(_, info)| info.out_capable)
1223 .filter(|(id, _)| {
1224 if let Some(ref ex) = exclude {
1225 **id != *ex
1226 } else {
1227 true
1228 }
1229 })
1230 .filter(|(_, info)| {
1231 should_transmit_announce(
1232 info,
1233 &dest_hash,
1234 hops,
1235 &self.local_destinations,
1236 &self.path_table,
1237 &self.interfaces,
1238 )
1239 })
1240 .map(|(id, info)| (*id, info.bitrate, info.announce_cap))
1241 .collect();
1242
1243 for (iface_id, bitrate, announce_cap) in iface_ids {
1244 if let Some(send_action) = self.announce_queues.gate_announce(
1245 iface_id,
1246 raw.clone(),
1247 dest_hash,
1248 hops,
1249 now,
1250 now,
1251 bitrate,
1252 announce_cap,
1253 ) {
1254 result.push(send_action);
1255 }
1256 }
1257 }
1258 other => result.push(other),
1259 }
1260 }
1261 result
1262 }
1263
1264 fn extract_announce_info(raw: &[u8]) -> ([u8; 16], u8) {
1266 if raw.len() < 18 {
1267 return ([0; 16], 0);
1268 }
1269 let header_type = (raw[0] >> 6) & 0x03;
1270 let hops = raw[1];
1271 if header_type == constants::HEADER_2 && raw.len() >= 34 {
1272 let mut dest = [0u8; 16];
1274 dest.copy_from_slice(&raw[18..34]);
1275 (dest, hops)
1276 } else {
1277 let mut dest = [0u8; 16];
1279 dest.copy_from_slice(&raw[2..18]);
1280 (dest, hops)
1281 }
1282 }
1283
1284 pub fn handle_path_request(
1297 &mut self,
1298 data: &[u8],
1299 interface_id: InterfaceId,
1300 now: f64,
1301 ) -> Vec<TransportAction> {
1302 let mut actions = Vec::new();
1303
1304 if data.len() < 16 {
1305 return actions;
1306 }
1307
1308 let mut destination_hash = [0u8; 16];
1309 destination_hash.copy_from_slice(&data[..16]);
1310
1311 let _requesting_transport_id = if data.len() > 32 {
1313 let mut id = [0u8; 16];
1314 id.copy_from_slice(&data[16..32]);
1315 Some(id)
1316 } else {
1317 None
1318 };
1319
1320 let tag_bytes = if data.len() > 32 {
1322 Some(&data[32..])
1323 } else if data.len() > 16 {
1324 Some(&data[16..])
1325 } else {
1326 None
1327 };
1328
1329 if let Some(tag) = tag_bytes {
1330 let tag_len = tag.len().min(16);
1331 let mut unique_tag = [0u8; 32];
1332 unique_tag[..16].copy_from_slice(&destination_hash);
1333 unique_tag[16..16 + tag_len].copy_from_slice(&tag[..tag_len]);
1334
1335 if self.discovery_pr_tags.contains(&unique_tag) {
1336 return actions; }
1338 self.discovery_pr_tags.push(unique_tag);
1339 } else {
1340 return actions; }
1342
1343 if self.local_destinations.contains_key(&destination_hash) {
1345 return actions;
1346 }
1347
1348 if self.config.transport_enabled && self.has_path(&destination_hash) {
1350 let path = self
1351 .path_table
1352 .get(&destination_hash)
1353 .unwrap()
1354 .primary()
1355 .unwrap()
1356 .clone();
1357
1358 if let Some(recv_info) = self.interfaces.get(&interface_id) {
1362 if recv_info.mode == constants::MODE_ROAMING
1363 && path.receiving_interface == interface_id
1364 {
1365 return actions;
1366 }
1367 }
1368
1369 if let Some(ref raw) = path.announce_raw {
1373 if let Some(existing) = self.announce_table.remove(&destination_hash) {
1375 self.held_announces.insert(destination_hash, existing);
1376 }
1377 let retransmit_timeout =
1378 if let Some(iface_info) = self.interfaces.get(&interface_id) {
1379 let base = now + constants::PATH_REQUEST_GRACE;
1380 if iface_info.mode == constants::MODE_ROAMING {
1381 base + constants::PATH_REQUEST_RG
1382 } else {
1383 base
1384 }
1385 } else {
1386 now + constants::PATH_REQUEST_GRACE
1387 };
1388
1389 let (packet_data, context_flag) = match RawPacket::unpack(raw) {
1390 Ok(parsed) => (parsed.data, parsed.flags.context_flag),
1391 Err(_) => {
1392 return actions;
1393 }
1394 };
1395
1396 let entry = AnnounceEntry {
1397 timestamp: now,
1398 retransmit_timeout,
1399 retries: constants::PATHFINDER_R,
1400 received_from: path.next_hop,
1401 hops: path.hops,
1402 packet_raw: raw.clone(),
1403 packet_data,
1404 destination_hash,
1405 context_flag,
1406 local_rebroadcasts: 0,
1407 block_rebroadcasts: true,
1408 attached_interface: Some(interface_id),
1409 };
1410
1411 self.announce_table.insert(destination_hash, entry);
1412 }
1413 } else if self.config.transport_enabled {
1414 let should_discover = self
1416 .interfaces
1417 .get(&interface_id)
1418 .map(|info| constants::DISCOVER_PATHS_FOR.contains(&info.mode))
1419 .unwrap_or(false);
1420
1421 if should_discover {
1422 self.discovery_path_requests.insert(
1424 destination_hash,
1425 DiscoveryPathRequest {
1426 timestamp: now,
1427 requesting_interface: interface_id,
1428 },
1429 );
1430
1431 for (_, iface_info) in self.interfaces.iter() {
1433 if iface_info.id != interface_id && iface_info.out_capable {
1434 actions.push(TransportAction::SendOnInterface {
1435 interface: iface_info.id,
1436 raw: data.to_vec(),
1437 });
1438 }
1439 }
1440 }
1441 }
1442
1443 actions
1444 }
1445
1446 pub fn path_table_entries(&self) -> impl Iterator<Item = (&[u8; 16], &PathEntry)> {
1452 self.path_table
1453 .iter()
1454 .filter_map(|(k, ps)| ps.primary().map(|e| (k, e)))
1455 }
1456
1457 pub fn path_table_sets(&self) -> impl Iterator<Item = (&[u8; 16], &PathSet)> {
1459 self.path_table.iter()
1460 }
1461
1462 pub fn interface_count(&self) -> usize {
1464 self.interfaces.len()
1465 }
1466
1467 pub fn link_table_count(&self) -> usize {
1469 self.link_table.len()
1470 }
1471
1472 pub fn rate_limiter(&self) -> &AnnounceRateLimiter {
1474 &self.rate_limiter
1475 }
1476
1477 pub fn interface_info(&self, id: &InterfaceId) -> Option<&InterfaceInfo> {
1479 self.interfaces.get(id)
1480 }
1481
1482 pub fn redirect_path(&mut self, dest_hash: &[u8; 16], interface: InterfaceId, now: f64) {
1485 if let Some(entry) = self
1486 .path_table
1487 .get_mut(dest_hash)
1488 .and_then(|ps| ps.primary_mut())
1489 {
1490 entry.receiving_interface = interface;
1491 entry.hops = 1;
1492 } else {
1493 let max_paths = self.config.max_paths_per_destination;
1494 self.path_table.insert(
1495 *dest_hash,
1496 PathSet::from_single(
1497 PathEntry {
1498 timestamp: now,
1499 next_hop: [0u8; 16],
1500 hops: 1,
1501 expires: now + 3600.0,
1502 random_blobs: Vec::new(),
1503 receiving_interface: interface,
1504 packet_hash: [0u8; 32],
1505 announce_raw: None,
1506 },
1507 max_paths,
1508 ),
1509 );
1510 }
1511 }
1512
1513 pub fn inject_path(&mut self, dest_hash: [u8; 16], entry: PathEntry) {
1515 let max_paths = self.config.max_paths_per_destination;
1516 self.path_table
1517 .insert(dest_hash, PathSet::from_single(entry, max_paths));
1518 }
1519
1520 pub fn drop_path(&mut self, dest_hash: &[u8; 16]) -> bool {
1522 self.path_table.remove(dest_hash).is_some()
1523 }
1524
1525 pub fn drop_all_via(&mut self, transport_hash: &[u8; 16]) -> usize {
1530 let mut removed = 0usize;
1531 for ps in self.path_table.values_mut() {
1532 let before = ps.len();
1533 ps.retain(|entry| &entry.next_hop != transport_hash);
1534 removed += before - ps.len();
1535 }
1536 self.path_table.retain(|_, ps| !ps.is_empty());
1537 removed
1538 }
1539
1540 pub fn drop_announce_queues(&mut self) {
1542 self.announce_table.clear();
1543 self.held_announces.clear();
1544 self.announce_queues = AnnounceQueues::new();
1545 self.ingress_control.clear();
1546 }
1547
1548 pub fn identity_hash(&self) -> Option<&[u8; 16]> {
1550 self.config.identity_hash.as_ref()
1551 }
1552
1553 pub fn transport_enabled(&self) -> bool {
1555 self.config.transport_enabled
1556 }
1557
1558 pub fn config(&self) -> &TransportConfig {
1560 &self.config
1561 }
1562
1563 pub fn set_packet_hashlist_max_entries(&mut self, max_entries: usize) {
1564 self.config.packet_hashlist_max_entries = max_entries;
1565 self.packet_hashlist = PacketHashlist::new(max_entries);
1566 }
1567
1568 pub fn get_path_table(&self, max_hops: Option<u8>) -> Vec<PathTableRow> {
1572 let mut result = Vec::new();
1573 for (dest_hash, ps) in self.path_table.iter() {
1574 if let Some(entry) = ps.primary() {
1575 if let Some(max) = max_hops {
1576 if entry.hops > max {
1577 continue;
1578 }
1579 }
1580 let iface_name = self
1581 .interfaces
1582 .get(&entry.receiving_interface)
1583 .map(|i| i.name.clone())
1584 .unwrap_or_else(|| {
1585 alloc::format!("Interface({})", entry.receiving_interface.0)
1586 });
1587 result.push((
1588 *dest_hash,
1589 entry.timestamp,
1590 entry.next_hop,
1591 entry.hops,
1592 entry.expires,
1593 iface_name,
1594 ));
1595 }
1596 }
1597 result
1598 }
1599
1600 pub fn get_rate_table(&self) -> Vec<RateTableRow> {
1603 self.rate_limiter
1604 .entries()
1605 .map(|(hash, entry)| {
1606 (
1607 *hash,
1608 entry.last,
1609 entry.rate_violations,
1610 entry.blocked_until,
1611 entry.timestamps.clone(),
1612 )
1613 })
1614 .collect()
1615 }
1616
1617 pub fn get_blackholed(&self) -> Vec<([u8; 16], f64, f64, Option<alloc::string::String>)> {
1620 self.blackholed_entries()
1621 .map(|(hash, entry)| (*hash, entry.created, entry.expires, entry.reason.clone()))
1622 .collect()
1623 }
1624
1625 pub fn active_destination_hashes(&self) -> alloc::collections::BTreeSet<[u8; 16]> {
1631 self.path_table.keys().copied().collect()
1632 }
1633
1634 pub fn active_packet_hashes(&self) -> Vec<[u8; 32]> {
1636 self.path_table
1637 .values()
1638 .flat_map(|ps| ps.iter().map(|p| p.packet_hash))
1639 .collect()
1640 }
1641
1642 pub fn cull_rate_limiter(
1645 &mut self,
1646 active: &alloc::collections::BTreeSet<[u8; 16]>,
1647 now: f64,
1648 ttl_secs: f64,
1649 ) -> usize {
1650 self.rate_limiter.cull_stale(active, now, ttl_secs)
1651 }
1652
1653 pub fn update_interface_freq(&mut self, id: InterfaceId, ia_freq: f64) {
1659 if let Some(info) = self.interfaces.get_mut(&id) {
1660 info.ia_freq = ia_freq;
1661 }
1662 }
1663
1664 pub fn held_announce_count(&self, interface: &InterfaceId) -> usize {
1666 self.ingress_control.held_count(interface)
1667 }
1668
1669 #[cfg(test)]
1674 #[allow(dead_code)]
1675 pub(crate) fn path_table(&self) -> &BTreeMap<[u8; 16], PathSet> {
1676 &self.path_table
1677 }
1678
1679 #[cfg(test)]
1680 #[allow(dead_code)]
1681 pub(crate) fn announce_table(&self) -> &BTreeMap<[u8; 16], AnnounceEntry> {
1682 &self.announce_table
1683 }
1684
1685 #[cfg(test)]
1686 #[allow(dead_code)]
1687 pub(crate) fn reverse_table(&self) -> &BTreeMap<[u8; 16], tables::ReverseEntry> {
1688 &self.reverse_table
1689 }
1690
1691 #[cfg(test)]
1692 #[allow(dead_code)]
1693 pub(crate) fn link_table_ref(&self) -> &BTreeMap<[u8; 16], LinkEntry> {
1694 &self.link_table
1695 }
1696}
1697
1698#[cfg(test)]
1699mod tests {
1700 use super::*;
1701 use crate::packet::PacketFlags;
1702
1703 fn make_config(transport_enabled: bool) -> TransportConfig {
1704 TransportConfig {
1705 transport_enabled,
1706 identity_hash: if transport_enabled {
1707 Some([0x42; 16])
1708 } else {
1709 None
1710 },
1711 prefer_shorter_path: false,
1712 max_paths_per_destination: 1,
1713 packet_hashlist_max_entries: constants::HASHLIST_MAXSIZE,
1714 }
1715 }
1716
1717 fn make_interface(id: u64, mode: u8) -> InterfaceInfo {
1718 InterfaceInfo {
1719 id: InterfaceId(id),
1720 name: String::from("test"),
1721 mode,
1722 out_capable: true,
1723 in_capable: true,
1724 bitrate: None,
1725 announce_rate_target: None,
1726 announce_rate_grace: 0,
1727 announce_rate_penalty: 0.0,
1728 announce_cap: constants::ANNOUNCE_CAP,
1729 is_local_client: false,
1730 wants_tunnel: false,
1731 tunnel_id: None,
1732 mtu: constants::MTU as u32,
1733 ingress_control: false,
1734 ia_freq: 0.0,
1735 started: 0.0,
1736 }
1737 }
1738
1739 #[test]
1740 fn test_empty_engine() {
1741 let engine = TransportEngine::new(make_config(false));
1742 assert!(!engine.has_path(&[0; 16]));
1743 assert!(engine.hops_to(&[0; 16]).is_none());
1744 assert!(engine.next_hop(&[0; 16]).is_none());
1745 }
1746
1747 #[test]
1748 fn test_register_deregister_interface() {
1749 let mut engine = TransportEngine::new(make_config(false));
1750 engine.register_interface(make_interface(1, constants::MODE_FULL));
1751 assert!(engine.interfaces.contains_key(&InterfaceId(1)));
1752
1753 engine.deregister_interface(InterfaceId(1));
1754 assert!(!engine.interfaces.contains_key(&InterfaceId(1)));
1755 }
1756
1757 #[test]
1758 fn test_register_deregister_destination() {
1759 let mut engine = TransportEngine::new(make_config(false));
1760 let dest = [0x11; 16];
1761 engine.register_destination(dest, constants::DESTINATION_SINGLE);
1762 assert!(engine.local_destinations.contains_key(&dest));
1763
1764 engine.deregister_destination(&dest);
1765 assert!(!engine.local_destinations.contains_key(&dest));
1766 }
1767
1768 #[test]
1769 fn test_path_state() {
1770 let mut engine = TransportEngine::new(make_config(false));
1771 let dest = [0x22; 16];
1772
1773 assert!(!engine.path_is_unresponsive(&dest));
1774
1775 engine.mark_path_unresponsive(&dest, None);
1776 assert!(engine.path_is_unresponsive(&dest));
1777
1778 engine.mark_path_responsive(&dest);
1779 assert!(!engine.path_is_unresponsive(&dest));
1780 }
1781
1782 #[test]
1783 fn test_boundary_exempts_unresponsive() {
1784 let mut engine = TransportEngine::new(make_config(false));
1785 engine.register_interface(make_interface(1, constants::MODE_BOUNDARY));
1786 let dest = [0xB1; 16];
1787
1788 engine.mark_path_unresponsive(&dest, Some(InterfaceId(1)));
1790 assert!(!engine.path_is_unresponsive(&dest));
1791 }
1792
1793 #[test]
1794 fn test_non_boundary_marks_unresponsive() {
1795 let mut engine = TransportEngine::new(make_config(false));
1796 engine.register_interface(make_interface(1, constants::MODE_FULL));
1797 let dest = [0xB2; 16];
1798
1799 engine.mark_path_unresponsive(&dest, Some(InterfaceId(1)));
1801 assert!(engine.path_is_unresponsive(&dest));
1802 }
1803
1804 #[test]
1805 fn test_expire_path() {
1806 let mut engine = TransportEngine::new(make_config(false));
1807 let dest = [0x33; 16];
1808
1809 engine.path_table.insert(
1810 dest,
1811 PathSet::from_single(
1812 PathEntry {
1813 timestamp: 1000.0,
1814 next_hop: [0; 16],
1815 hops: 2,
1816 expires: 9999.0,
1817 random_blobs: Vec::new(),
1818 receiving_interface: InterfaceId(1),
1819 packet_hash: [0; 32],
1820 announce_raw: None,
1821 },
1822 1,
1823 ),
1824 );
1825
1826 assert!(engine.has_path(&dest));
1827 engine.expire_path(&dest);
1828 assert!(engine.has_path(&dest));
1830 assert_eq!(engine.path_table[&dest].primary().unwrap().expires, 0.0);
1831 }
1832
1833 #[test]
1834 fn test_link_table_operations() {
1835 let mut engine = TransportEngine::new(make_config(false));
1836 let link_id = [0x44; 16];
1837
1838 engine.register_link(
1839 link_id,
1840 LinkEntry {
1841 timestamp: 100.0,
1842 next_hop_transport_id: [0; 16],
1843 next_hop_interface: InterfaceId(1),
1844 remaining_hops: 3,
1845 received_interface: InterfaceId(2),
1846 taken_hops: 2,
1847 destination_hash: [0xAA; 16],
1848 validated: false,
1849 proof_timeout: 200.0,
1850 },
1851 );
1852
1853 assert!(engine.link_table.contains_key(&link_id));
1854 assert!(!engine.link_table[&link_id].validated);
1855
1856 engine.validate_link(&link_id);
1857 assert!(engine.link_table[&link_id].validated);
1858
1859 engine.remove_link(&link_id);
1860 assert!(!engine.link_table.contains_key(&link_id));
1861 }
1862
1863 #[test]
1864 fn test_packet_filter_drops_plain_announce() {
1865 let engine = TransportEngine::new(make_config(false));
1866 let flags = PacketFlags {
1867 header_type: constants::HEADER_1,
1868 context_flag: constants::FLAG_UNSET,
1869 transport_type: constants::TRANSPORT_BROADCAST,
1870 destination_type: constants::DESTINATION_PLAIN,
1871 packet_type: constants::PACKET_TYPE_ANNOUNCE,
1872 };
1873 let packet =
1874 RawPacket::pack(flags, 0, &[0; 16], None, constants::CONTEXT_NONE, b"test").unwrap();
1875 assert!(!engine.packet_filter(&packet));
1876 }
1877
1878 #[test]
1879 fn test_packet_filter_allows_keepalive() {
1880 let engine = TransportEngine::new(make_config(false));
1881 let flags = PacketFlags {
1882 header_type: constants::HEADER_1,
1883 context_flag: constants::FLAG_UNSET,
1884 transport_type: constants::TRANSPORT_BROADCAST,
1885 destination_type: constants::DESTINATION_SINGLE,
1886 packet_type: constants::PACKET_TYPE_DATA,
1887 };
1888 let packet = RawPacket::pack(
1889 flags,
1890 0,
1891 &[0; 16],
1892 None,
1893 constants::CONTEXT_KEEPALIVE,
1894 b"test",
1895 )
1896 .unwrap();
1897 assert!(engine.packet_filter(&packet));
1898 }
1899
1900 #[test]
1901 fn test_packet_filter_drops_high_hop_plain() {
1902 let engine = TransportEngine::new(make_config(false));
1903 let flags = PacketFlags {
1904 header_type: constants::HEADER_1,
1905 context_flag: constants::FLAG_UNSET,
1906 transport_type: constants::TRANSPORT_BROADCAST,
1907 destination_type: constants::DESTINATION_PLAIN,
1908 packet_type: constants::PACKET_TYPE_DATA,
1909 };
1910 let mut packet =
1911 RawPacket::pack(flags, 0, &[0; 16], None, constants::CONTEXT_NONE, b"test").unwrap();
1912 packet.hops = 2;
1913 assert!(!engine.packet_filter(&packet));
1914 }
1915
1916 #[test]
1917 fn test_packet_filter_allows_duplicate_single_announce() {
1918 let mut engine = TransportEngine::new(make_config(false));
1919 let flags = PacketFlags {
1920 header_type: constants::HEADER_1,
1921 context_flag: constants::FLAG_UNSET,
1922 transport_type: constants::TRANSPORT_BROADCAST,
1923 destination_type: constants::DESTINATION_SINGLE,
1924 packet_type: constants::PACKET_TYPE_ANNOUNCE,
1925 };
1926 let packet = RawPacket::pack(
1927 flags,
1928 0,
1929 &[0; 16],
1930 None,
1931 constants::CONTEXT_NONE,
1932 &[0xAA; 64],
1933 )
1934 .unwrap();
1935
1936 engine.packet_hashlist.add(packet.packet_hash);
1938
1939 assert!(engine.packet_filter(&packet));
1941 }
1942
1943 #[test]
1944 fn test_packet_filter_fifo_eviction_allows_oldest_hash_again() {
1945 let mut engine = TransportEngine::new(make_config(false));
1946 engine.packet_hashlist = PacketHashlist::new(2);
1947
1948 let make_packet = |seed: u8| {
1949 let flags = PacketFlags {
1950 header_type: constants::HEADER_1,
1951 context_flag: constants::FLAG_UNSET,
1952 transport_type: constants::TRANSPORT_BROADCAST,
1953 destination_type: constants::DESTINATION_SINGLE,
1954 packet_type: constants::PACKET_TYPE_DATA,
1955 };
1956 RawPacket::pack(
1957 flags,
1958 0,
1959 &[seed; 16],
1960 None,
1961 constants::CONTEXT_NONE,
1962 &[seed; 4],
1963 )
1964 .unwrap()
1965 };
1966
1967 let packet1 = make_packet(1);
1968 let packet2 = make_packet(2);
1969 let packet3 = make_packet(3);
1970
1971 engine.packet_hashlist.add(packet1.packet_hash);
1972 engine.packet_hashlist.add(packet2.packet_hash);
1973 assert!(!engine.packet_filter(&packet1));
1974
1975 engine.packet_hashlist.add(packet3.packet_hash);
1976
1977 assert!(engine.packet_filter(&packet1));
1978 assert!(!engine.packet_filter(&packet2));
1979 assert!(!engine.packet_filter(&packet3));
1980 }
1981
1982 #[test]
1983 fn test_packet_filter_duplicate_does_not_refresh_recency() {
1984 let mut engine = TransportEngine::new(make_config(false));
1985 engine.packet_hashlist = PacketHashlist::new(2);
1986
1987 let make_packet = |seed: u8| {
1988 let flags = PacketFlags {
1989 header_type: constants::HEADER_1,
1990 context_flag: constants::FLAG_UNSET,
1991 transport_type: constants::TRANSPORT_BROADCAST,
1992 destination_type: constants::DESTINATION_SINGLE,
1993 packet_type: constants::PACKET_TYPE_DATA,
1994 };
1995 RawPacket::pack(
1996 flags,
1997 0,
1998 &[seed; 16],
1999 None,
2000 constants::CONTEXT_NONE,
2001 &[seed; 4],
2002 )
2003 .unwrap()
2004 };
2005
2006 let packet1 = make_packet(1);
2007 let packet2 = make_packet(2);
2008 let packet3 = make_packet(3);
2009
2010 engine.packet_hashlist.add(packet1.packet_hash);
2011 engine.packet_hashlist.add(packet2.packet_hash);
2012 engine.packet_hashlist.add(packet2.packet_hash);
2013 engine.packet_hashlist.add(packet3.packet_hash);
2014
2015 assert!(engine.packet_filter(&packet1));
2016 assert!(!engine.packet_filter(&packet2));
2017 assert!(!engine.packet_filter(&packet3));
2018 }
2019
2020 #[test]
2021 fn test_tick_retransmits_announce() {
2022 let mut engine = TransportEngine::new(make_config(true));
2023 engine.register_interface(make_interface(1, constants::MODE_FULL));
2024
2025 let dest = [0x55; 16];
2026 engine.announce_table.insert(
2027 dest,
2028 AnnounceEntry {
2029 timestamp: 100.0,
2030 retransmit_timeout: 100.0, retries: 0,
2032 received_from: [0xAA; 16],
2033 hops: 2,
2034 packet_raw: vec![0x01, 0x02],
2035 packet_data: vec![0xCC; 10],
2036 destination_hash: dest,
2037 context_flag: 0,
2038 local_rebroadcasts: 0,
2039 block_rebroadcasts: false,
2040 attached_interface: None,
2041 },
2042 );
2043
2044 let mut rng = rns_crypto::FixedRng::new(&[0x42; 32]);
2045 let actions = engine.tick(200.0, &mut rng);
2046
2047 assert!(!actions.is_empty());
2050 assert!(matches!(
2051 &actions[0],
2052 TransportAction::SendOnInterface { .. }
2053 ));
2054
2055 assert_eq!(engine.announce_table[&dest].retries, 1);
2057 }
2058
2059 #[test]
2060 fn test_blackhole_identity() {
2061 let mut engine = TransportEngine::new(make_config(false));
2062 let hash = [0xAA; 16];
2063 let now = 1000.0;
2064
2065 assert!(!engine.is_blackholed(&hash, now));
2066
2067 engine.blackhole_identity(hash, now, None, Some(String::from("test")));
2068 assert!(engine.is_blackholed(&hash, now));
2069 assert!(engine.is_blackholed(&hash, now + 999999.0)); assert!(engine.unblackhole_identity(&hash));
2072 assert!(!engine.is_blackholed(&hash, now));
2073 assert!(!engine.unblackhole_identity(&hash)); }
2075
2076 #[test]
2077 fn test_blackhole_with_duration() {
2078 let mut engine = TransportEngine::new(make_config(false));
2079 let hash = [0xBB; 16];
2080 let now = 1000.0;
2081
2082 engine.blackhole_identity(hash, now, Some(1.0), None); assert!(engine.is_blackholed(&hash, now));
2084 assert!(engine.is_blackholed(&hash, now + 3599.0)); assert!(!engine.is_blackholed(&hash, now + 3601.0)); }
2087
2088 #[test]
2089 fn test_cull_blackholed() {
2090 let mut engine = TransportEngine::new(make_config(false));
2091 let hash1 = [0xCC; 16];
2092 let hash2 = [0xDD; 16];
2093 let now = 1000.0;
2094
2095 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));
2101 assert!(engine.blackholed_identities.contains_key(&hash2));
2102 }
2103
2104 #[test]
2105 fn test_blackhole_blocks_announce() {
2106 use crate::announce::AnnounceData;
2107 use crate::destination::{destination_hash, name_hash};
2108
2109 let mut engine = TransportEngine::new(make_config(false));
2110 engine.register_interface(make_interface(1, constants::MODE_FULL));
2111
2112 let identity =
2113 rns_crypto::identity::Identity::new(&mut rns_crypto::FixedRng::new(&[0x55; 32]));
2114 let dest_hash = destination_hash("test", &["app"], Some(identity.hash()));
2115 let name_h = name_hash("test", &["app"]);
2116 let random_hash = [0x42u8; 10];
2117
2118 let (announce_data, _) =
2119 AnnounceData::pack(&identity, &dest_hash, &name_h, &random_hash, None, None).unwrap();
2120
2121 let flags = PacketFlags {
2122 header_type: constants::HEADER_1,
2123 context_flag: constants::FLAG_UNSET,
2124 transport_type: constants::TRANSPORT_BROADCAST,
2125 destination_type: constants::DESTINATION_SINGLE,
2126 packet_type: constants::PACKET_TYPE_ANNOUNCE,
2127 };
2128 let packet = RawPacket::pack(
2129 flags,
2130 0,
2131 &dest_hash,
2132 None,
2133 constants::CONTEXT_NONE,
2134 &announce_data,
2135 )
2136 .unwrap();
2137
2138 let now = 1000.0;
2140 engine.blackhole_identity(*identity.hash(), now, None, None);
2141
2142 let mut rng = rns_crypto::FixedRng::new(&[0x11; 32]);
2143 let actions = engine.handle_inbound(&packet.raw, InterfaceId(1), now, &mut rng);
2144
2145 assert!(actions
2147 .iter()
2148 .all(|a| !matches!(a, TransportAction::AnnounceReceived { .. })));
2149 assert!(actions
2150 .iter()
2151 .all(|a| !matches!(a, TransportAction::PathUpdated { .. })));
2152 }
2153
2154 #[test]
2155 fn test_tick_culls_expired_path() {
2156 let mut engine = TransportEngine::new(make_config(false));
2157 engine.register_interface(make_interface(1, constants::MODE_FULL));
2158
2159 let dest = [0x66; 16];
2160 engine.path_table.insert(
2161 dest,
2162 PathSet::from_single(
2163 PathEntry {
2164 timestamp: 100.0,
2165 next_hop: [0; 16],
2166 hops: 2,
2167 expires: 200.0,
2168 random_blobs: Vec::new(),
2169 receiving_interface: InterfaceId(1),
2170 packet_hash: [0; 32],
2171 announce_raw: None,
2172 },
2173 1,
2174 ),
2175 );
2176
2177 assert!(engine.has_path(&dest));
2178
2179 let mut rng = rns_crypto::FixedRng::new(&[0; 32]);
2180 engine.tick(300.0, &mut rng);
2182
2183 assert!(!engine.has_path(&dest));
2184 }
2185
2186 fn make_local_client_interface(id: u64) -> InterfaceInfo {
2191 InterfaceInfo {
2192 id: InterfaceId(id),
2193 name: String::from("local_client"),
2194 mode: constants::MODE_FULL,
2195 out_capable: true,
2196 in_capable: true,
2197 bitrate: None,
2198 announce_rate_target: None,
2199 announce_rate_grace: 0,
2200 announce_rate_penalty: 0.0,
2201 announce_cap: constants::ANNOUNCE_CAP,
2202 is_local_client: true,
2203 wants_tunnel: false,
2204 tunnel_id: None,
2205 mtu: constants::MTU as u32,
2206 ingress_control: false,
2207 ia_freq: 0.0,
2208 started: 0.0,
2209 }
2210 }
2211
2212 #[test]
2213 fn test_has_local_clients() {
2214 let mut engine = TransportEngine::new(make_config(false));
2215 assert!(!engine.has_local_clients());
2216
2217 engine.register_interface(make_interface(1, constants::MODE_FULL));
2218 assert!(!engine.has_local_clients());
2219
2220 engine.register_interface(make_local_client_interface(2));
2221 assert!(engine.has_local_clients());
2222
2223 engine.deregister_interface(InterfaceId(2));
2224 assert!(!engine.has_local_clients());
2225 }
2226
2227 #[test]
2228 fn test_local_client_hop_decrement() {
2229 let mut engine = TransportEngine::new(make_config(false));
2232 engine.register_interface(make_local_client_interface(1));
2233 engine.register_interface(make_interface(2, constants::MODE_FULL));
2234
2235 let dest = [0xAA; 16];
2237 engine.register_destination(dest, constants::DESTINATION_PLAIN);
2238
2239 let flags = PacketFlags {
2240 header_type: constants::HEADER_1,
2241 context_flag: constants::FLAG_UNSET,
2242 transport_type: constants::TRANSPORT_BROADCAST,
2243 destination_type: constants::DESTINATION_PLAIN,
2244 packet_type: constants::PACKET_TYPE_DATA,
2245 };
2246 let packet =
2248 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"hello").unwrap();
2249
2250 let mut rng = rns_crypto::FixedRng::new(&[0; 32]);
2251 let actions = engine.handle_inbound(&packet.raw, InterfaceId(1), 1000.0, &mut rng);
2252
2253 let deliver = actions
2256 .iter()
2257 .find(|a| matches!(a, TransportAction::DeliverLocal { .. }));
2258 assert!(deliver.is_some(), "Should deliver locally");
2259 }
2260
2261 #[test]
2262 fn test_plain_broadcast_from_local_client() {
2263 let mut engine = TransportEngine::new(make_config(false));
2265 engine.register_interface(make_local_client_interface(1));
2266 engine.register_interface(make_interface(2, constants::MODE_FULL));
2267
2268 let dest = [0xBB; 16];
2269 let flags = PacketFlags {
2270 header_type: constants::HEADER_1,
2271 context_flag: constants::FLAG_UNSET,
2272 transport_type: constants::TRANSPORT_BROADCAST,
2273 destination_type: constants::DESTINATION_PLAIN,
2274 packet_type: constants::PACKET_TYPE_DATA,
2275 };
2276 let packet =
2277 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"test").unwrap();
2278
2279 let mut rng = rns_crypto::FixedRng::new(&[0; 32]);
2280 let actions = engine.handle_inbound(&packet.raw, InterfaceId(1), 1000.0, &mut rng);
2281
2282 let forward = actions.iter().find(|a| {
2284 matches!(
2285 a,
2286 TransportAction::ForwardPlainBroadcast {
2287 to_local: false,
2288 ..
2289 }
2290 )
2291 });
2292 assert!(forward.is_some(), "Should forward to external interfaces");
2293 }
2294
2295 #[test]
2296 fn test_plain_broadcast_from_external() {
2297 let mut engine = TransportEngine::new(make_config(false));
2299 engine.register_interface(make_local_client_interface(1));
2300 engine.register_interface(make_interface(2, constants::MODE_FULL));
2301
2302 let dest = [0xCC; 16];
2303 let flags = PacketFlags {
2304 header_type: constants::HEADER_1,
2305 context_flag: constants::FLAG_UNSET,
2306 transport_type: constants::TRANSPORT_BROADCAST,
2307 destination_type: constants::DESTINATION_PLAIN,
2308 packet_type: constants::PACKET_TYPE_DATA,
2309 };
2310 let packet =
2311 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"test").unwrap();
2312
2313 let mut rng = rns_crypto::FixedRng::new(&[0; 32]);
2314 let actions = engine.handle_inbound(&packet.raw, InterfaceId(2), 1000.0, &mut rng);
2315
2316 let forward = actions.iter().find(|a| {
2318 matches!(
2319 a,
2320 TransportAction::ForwardPlainBroadcast { to_local: true, .. }
2321 )
2322 });
2323 assert!(forward.is_some(), "Should forward to local clients");
2324 }
2325
2326 #[test]
2327 fn test_no_plain_broadcast_bridging_without_local_clients() {
2328 let mut engine = TransportEngine::new(make_config(false));
2330 engine.register_interface(make_interface(1, constants::MODE_FULL));
2331 engine.register_interface(make_interface(2, constants::MODE_FULL));
2332
2333 let dest = [0xDD; 16];
2334 let flags = PacketFlags {
2335 header_type: constants::HEADER_1,
2336 context_flag: constants::FLAG_UNSET,
2337 transport_type: constants::TRANSPORT_BROADCAST,
2338 destination_type: constants::DESTINATION_PLAIN,
2339 packet_type: constants::PACKET_TYPE_DATA,
2340 };
2341 let packet =
2342 RawPacket::pack(flags, 0, &dest, None, constants::CONTEXT_NONE, b"test").unwrap();
2343
2344 let mut rng = rns_crypto::FixedRng::new(&[0; 32]);
2345 let actions = engine.handle_inbound(&packet.raw, InterfaceId(1), 1000.0, &mut rng);
2346
2347 let has_forward = actions
2349 .iter()
2350 .any(|a| matches!(a, TransportAction::ForwardPlainBroadcast { .. }));
2351 assert!(!has_forward, "No bridging without local clients");
2352 }
2353
2354 #[test]
2355 fn test_announce_forwarded_to_local_clients() {
2356 use crate::announce::AnnounceData;
2357 use crate::destination::{destination_hash, name_hash};
2358
2359 let mut engine = TransportEngine::new(make_config(false));
2360 engine.register_interface(make_interface(1, constants::MODE_FULL));
2361 engine.register_interface(make_local_client_interface(2));
2362
2363 let identity =
2364 rns_crypto::identity::Identity::new(&mut rns_crypto::FixedRng::new(&[0x77; 32]));
2365 let dest_hash = destination_hash("test", &["fwd"], Some(identity.hash()));
2366 let name_h = name_hash("test", &["fwd"]);
2367 let random_hash = [0x42u8; 10];
2368
2369 let (announce_data, _) =
2370 AnnounceData::pack(&identity, &dest_hash, &name_h, &random_hash, None, None).unwrap();
2371
2372 let flags = PacketFlags {
2373 header_type: constants::HEADER_1,
2374 context_flag: constants::FLAG_UNSET,
2375 transport_type: constants::TRANSPORT_BROADCAST,
2376 destination_type: constants::DESTINATION_SINGLE,
2377 packet_type: constants::PACKET_TYPE_ANNOUNCE,
2378 };
2379 let packet = RawPacket::pack(
2380 flags,
2381 0,
2382 &dest_hash,
2383 None,
2384 constants::CONTEXT_NONE,
2385 &announce_data,
2386 )
2387 .unwrap();
2388
2389 let mut rng = rns_crypto::FixedRng::new(&[0x11; 32]);
2390 let actions = engine.handle_inbound(&packet.raw, InterfaceId(1), 1000.0, &mut rng);
2391
2392 let forward = actions
2394 .iter()
2395 .find(|a| matches!(a, TransportAction::ForwardToLocalClients { .. }));
2396 assert!(
2397 forward.is_some(),
2398 "Should forward announce to local clients"
2399 );
2400
2401 match forward.unwrap() {
2403 TransportAction::ForwardToLocalClients { exclude, .. } => {
2404 assert_eq!(*exclude, Some(InterfaceId(1)));
2405 }
2406 _ => unreachable!(),
2407 }
2408 }
2409
2410 #[test]
2411 fn test_no_announce_forward_without_local_clients() {
2412 use crate::announce::AnnounceData;
2413 use crate::destination::{destination_hash, name_hash};
2414
2415 let mut engine = TransportEngine::new(make_config(false));
2416 engine.register_interface(make_interface(1, constants::MODE_FULL));
2417
2418 let identity =
2419 rns_crypto::identity::Identity::new(&mut rns_crypto::FixedRng::new(&[0x88; 32]));
2420 let dest_hash = destination_hash("test", &["nofwd"], Some(identity.hash()));
2421 let name_h = name_hash("test", &["nofwd"]);
2422 let random_hash = [0x42u8; 10];
2423
2424 let (announce_data, _) =
2425 AnnounceData::pack(&identity, &dest_hash, &name_h, &random_hash, None, None).unwrap();
2426
2427 let flags = PacketFlags {
2428 header_type: constants::HEADER_1,
2429 context_flag: constants::FLAG_UNSET,
2430 transport_type: constants::TRANSPORT_BROADCAST,
2431 destination_type: constants::DESTINATION_SINGLE,
2432 packet_type: constants::PACKET_TYPE_ANNOUNCE,
2433 };
2434 let packet = RawPacket::pack(
2435 flags,
2436 0,
2437 &dest_hash,
2438 None,
2439 constants::CONTEXT_NONE,
2440 &announce_data,
2441 )
2442 .unwrap();
2443
2444 let mut rng = rns_crypto::FixedRng::new(&[0x22; 32]);
2445 let actions = engine.handle_inbound(&packet.raw, InterfaceId(1), 1000.0, &mut rng);
2446
2447 let has_forward = actions
2449 .iter()
2450 .any(|a| matches!(a, TransportAction::ForwardToLocalClients { .. }));
2451 assert!(!has_forward, "No forward without local clients");
2452 }
2453
2454 #[test]
2455 fn test_local_client_exclude_from_forward() {
2456 use crate::announce::AnnounceData;
2457 use crate::destination::{destination_hash, name_hash};
2458
2459 let mut engine = TransportEngine::new(make_config(false));
2460 engine.register_interface(make_local_client_interface(1));
2461 engine.register_interface(make_local_client_interface(2));
2462
2463 let identity =
2464 rns_crypto::identity::Identity::new(&mut rns_crypto::FixedRng::new(&[0x99; 32]));
2465 let dest_hash = destination_hash("test", &["excl"], Some(identity.hash()));
2466 let name_h = name_hash("test", &["excl"]);
2467 let random_hash = [0x42u8; 10];
2468
2469 let (announce_data, _) =
2470 AnnounceData::pack(&identity, &dest_hash, &name_h, &random_hash, None, None).unwrap();
2471
2472 let flags = PacketFlags {
2473 header_type: constants::HEADER_1,
2474 context_flag: constants::FLAG_UNSET,
2475 transport_type: constants::TRANSPORT_BROADCAST,
2476 destination_type: constants::DESTINATION_SINGLE,
2477 packet_type: constants::PACKET_TYPE_ANNOUNCE,
2478 };
2479 let packet = RawPacket::pack(
2480 flags,
2481 0,
2482 &dest_hash,
2483 None,
2484 constants::CONTEXT_NONE,
2485 &announce_data,
2486 )
2487 .unwrap();
2488
2489 let mut rng = rns_crypto::FixedRng::new(&[0x33; 32]);
2490 let actions = engine.handle_inbound(&packet.raw, InterfaceId(1), 1000.0, &mut rng);
2492
2493 let forward = actions
2495 .iter()
2496 .find(|a| matches!(a, TransportAction::ForwardToLocalClients { .. }));
2497 assert!(forward.is_some());
2498 match forward.unwrap() {
2499 TransportAction::ForwardToLocalClients { exclude, .. } => {
2500 assert_eq!(*exclude, Some(InterfaceId(1)));
2501 }
2502 _ => unreachable!(),
2503 }
2504 }
2505
2506 fn make_tunnel_interface(id: u64) -> InterfaceInfo {
2511 InterfaceInfo {
2512 id: InterfaceId(id),
2513 name: String::from("tunnel_iface"),
2514 mode: constants::MODE_FULL,
2515 out_capable: true,
2516 in_capable: true,
2517 bitrate: None,
2518 announce_rate_target: None,
2519 announce_rate_grace: 0,
2520 announce_rate_penalty: 0.0,
2521 announce_cap: constants::ANNOUNCE_CAP,
2522 is_local_client: false,
2523 wants_tunnel: true,
2524 tunnel_id: None,
2525 mtu: constants::MTU as u32,
2526 ingress_control: false,
2527 ia_freq: 0.0,
2528 started: 0.0,
2529 }
2530 }
2531
2532 #[test]
2533 fn test_handle_tunnel_new() {
2534 let mut engine = TransportEngine::new(make_config(true));
2535 engine.register_interface(make_tunnel_interface(1));
2536
2537 let tunnel_id = [0xAA; 32];
2538 let actions = engine.handle_tunnel(tunnel_id, InterfaceId(1), 1000.0);
2539
2540 assert!(actions
2542 .iter()
2543 .any(|a| matches!(a, TransportAction::TunnelEstablished { .. })));
2544
2545 let info = engine.interface_info(&InterfaceId(1)).unwrap();
2547 assert_eq!(info.tunnel_id, Some(tunnel_id));
2548
2549 assert_eq!(engine.tunnel_table().len(), 1);
2551 }
2552
2553 #[test]
2554 fn test_announce_stores_tunnel_path() {
2555 use crate::announce::AnnounceData;
2556 use crate::destination::{destination_hash, name_hash};
2557
2558 let mut engine = TransportEngine::new(make_config(false));
2559 let mut iface = make_tunnel_interface(1);
2560 let tunnel_id = [0xBB; 32];
2561 iface.tunnel_id = Some(tunnel_id);
2562 engine.register_interface(iface);
2563
2564 engine.handle_tunnel(tunnel_id, InterfaceId(1), 1000.0);
2566
2567 let identity =
2569 rns_crypto::identity::Identity::new(&mut rns_crypto::FixedRng::new(&[0xCC; 32]));
2570 let dest_hash = destination_hash("test", &["tunnel"], Some(identity.hash()));
2571 let name_h = name_hash("test", &["tunnel"]);
2572 let random_hash = [0x42u8; 10];
2573
2574 let (announce_data, _) =
2575 AnnounceData::pack(&identity, &dest_hash, &name_h, &random_hash, None, None).unwrap();
2576
2577 let flags = PacketFlags {
2578 header_type: constants::HEADER_1,
2579 context_flag: constants::FLAG_UNSET,
2580 transport_type: constants::TRANSPORT_BROADCAST,
2581 destination_type: constants::DESTINATION_SINGLE,
2582 packet_type: constants::PACKET_TYPE_ANNOUNCE,
2583 };
2584 let packet = RawPacket::pack(
2585 flags,
2586 0,
2587 &dest_hash,
2588 None,
2589 constants::CONTEXT_NONE,
2590 &announce_data,
2591 )
2592 .unwrap();
2593
2594 let mut rng = rns_crypto::FixedRng::new(&[0xDD; 32]);
2595 engine.handle_inbound(&packet.raw, InterfaceId(1), 1000.0, &mut rng);
2596
2597 assert!(engine.has_path(&dest_hash));
2599
2600 let tunnel = engine.tunnel_table().get(&tunnel_id).unwrap();
2602 assert_eq!(tunnel.paths.len(), 1);
2603 assert!(tunnel.paths.contains_key(&dest_hash));
2604 }
2605
2606 #[test]
2607 fn test_tunnel_reattach_restores_paths() {
2608 let mut engine = TransportEngine::new(make_config(true));
2609 engine.register_interface(make_tunnel_interface(1));
2610
2611 let tunnel_id = [0xCC; 32];
2612 engine.handle_tunnel(tunnel_id, InterfaceId(1), 1000.0);
2613
2614 let dest = [0xDD; 16];
2616 engine.tunnel_table.store_tunnel_path(
2617 &tunnel_id,
2618 dest,
2619 tunnel::TunnelPath {
2620 timestamp: 1000.0,
2621 received_from: [0xEE; 16],
2622 hops: 3,
2623 expires: 1000.0 + constants::DESTINATION_TIMEOUT,
2624 random_blobs: Vec::new(),
2625 packet_hash: [0xFF; 32],
2626 },
2627 1000.0,
2628 );
2629
2630 engine.void_tunnel_interface(&tunnel_id);
2632
2633 engine.path_table.remove(&dest);
2635 assert!(!engine.has_path(&dest));
2636
2637 engine.register_interface(make_interface(2, constants::MODE_FULL));
2639 let actions = engine.handle_tunnel(tunnel_id, InterfaceId(2), 2000.0);
2640
2641 assert!(engine.has_path(&dest));
2643 let path = engine.path_table.get(&dest).unwrap().primary().unwrap();
2644 assert_eq!(path.hops, 3);
2645 assert_eq!(path.receiving_interface, InterfaceId(2));
2646
2647 assert!(actions
2649 .iter()
2650 .any(|a| matches!(a, TransportAction::TunnelEstablished { .. })));
2651 }
2652
2653 #[test]
2654 fn test_void_tunnel_interface() {
2655 let mut engine = TransportEngine::new(make_config(true));
2656 engine.register_interface(make_tunnel_interface(1));
2657
2658 let tunnel_id = [0xDD; 32];
2659 engine.handle_tunnel(tunnel_id, InterfaceId(1), 1000.0);
2660
2661 assert_eq!(
2663 engine.tunnel_table().get(&tunnel_id).unwrap().interface,
2664 Some(InterfaceId(1))
2665 );
2666
2667 engine.void_tunnel_interface(&tunnel_id);
2668
2669 assert_eq!(engine.tunnel_table().len(), 1);
2671 assert_eq!(
2672 engine.tunnel_table().get(&tunnel_id).unwrap().interface,
2673 None
2674 );
2675 }
2676
2677 #[test]
2678 fn test_tick_culls_tunnels() {
2679 let mut engine = TransportEngine::new(make_config(true));
2680 engine.register_interface(make_tunnel_interface(1));
2681
2682 let tunnel_id = [0xEE; 32];
2683 engine.handle_tunnel(tunnel_id, InterfaceId(1), 1000.0);
2684 assert_eq!(engine.tunnel_table().len(), 1);
2685
2686 let mut rng = rns_crypto::FixedRng::new(&[0; 32]);
2687
2688 engine.tick(
2690 1000.0 + constants::DESTINATION_TIMEOUT + constants::TABLES_CULL_INTERVAL + 1.0,
2691 &mut rng,
2692 );
2693
2694 assert_eq!(engine.tunnel_table().len(), 0);
2695 }
2696
2697 #[test]
2698 fn test_synthesize_tunnel() {
2699 let mut engine = TransportEngine::new(make_config(true));
2700 engine.register_interface(make_tunnel_interface(1));
2701
2702 let identity =
2703 rns_crypto::identity::Identity::new(&mut rns_crypto::FixedRng::new(&[0xFF; 32]));
2704 let mut rng = rns_crypto::FixedRng::new(&[0x11; 32]);
2705
2706 let actions = engine.synthesize_tunnel(&identity, InterfaceId(1), &mut rng);
2707
2708 assert_eq!(actions.len(), 1);
2710 match &actions[0] {
2711 TransportAction::TunnelSynthesize {
2712 interface,
2713 data,
2714 dest_hash,
2715 } => {
2716 assert_eq!(*interface, InterfaceId(1));
2717 assert_eq!(data.len(), tunnel::TUNNEL_SYNTH_LENGTH);
2718 let expected_dest = crate::destination::destination_hash(
2720 "rnstransport",
2721 &["tunnel", "synthesize"],
2722 None,
2723 );
2724 assert_eq!(*dest_hash, expected_dest);
2725 }
2726 _ => panic!("Expected TunnelSynthesize"),
2727 }
2728 }
2729
2730 fn make_path_request_data(dest_hash: &[u8; 16], tag: &[u8]) -> Vec<u8> {
2735 let mut data = Vec::new();
2736 data.extend_from_slice(dest_hash);
2737 data.extend_from_slice(tag);
2738 data
2739 }
2740
2741 #[test]
2742 fn test_path_request_forwarded_on_ap() {
2743 let mut engine = TransportEngine::new(make_config(true));
2744 engine.register_interface(make_interface(1, constants::MODE_ACCESS_POINT));
2745 engine.register_interface(make_interface(2, constants::MODE_FULL));
2746
2747 let dest = [0xD1; 16];
2748 let tag = [0x01; 16];
2749 let data = make_path_request_data(&dest, &tag);
2750
2751 let actions = engine.handle_path_request(&data, InterfaceId(1), 1000.0);
2752
2753 assert_eq!(actions.len(), 1);
2755 match &actions[0] {
2756 TransportAction::SendOnInterface { interface, .. } => {
2757 assert_eq!(*interface, InterfaceId(2));
2758 }
2759 _ => panic!("Expected SendOnInterface for forwarded path request"),
2760 }
2761 assert!(engine.discovery_path_requests.contains_key(&dest));
2763 }
2764
2765 #[test]
2766 fn test_path_request_not_forwarded_on_full() {
2767 let mut engine = TransportEngine::new(make_config(true));
2768 engine.register_interface(make_interface(1, constants::MODE_FULL));
2769 engine.register_interface(make_interface(2, constants::MODE_FULL));
2770
2771 let dest = [0xD2; 16];
2772 let tag = [0x02; 16];
2773 let data = make_path_request_data(&dest, &tag);
2774
2775 let actions = engine.handle_path_request(&data, InterfaceId(1), 1000.0);
2776
2777 assert!(actions.is_empty());
2779 assert!(!engine.discovery_path_requests.contains_key(&dest));
2780 }
2781
2782 #[test]
2783 fn test_roaming_loop_prevention() {
2784 let mut engine = TransportEngine::new(make_config(true));
2785 engine.register_interface(make_interface(1, constants::MODE_ROAMING));
2786
2787 let dest = [0xD3; 16];
2788 engine.path_table.insert(
2790 dest,
2791 PathSet::from_single(
2792 PathEntry {
2793 timestamp: 900.0,
2794 next_hop: [0xAA; 16],
2795 hops: 2,
2796 expires: 9999.0,
2797 random_blobs: Vec::new(),
2798 receiving_interface: InterfaceId(1),
2799 packet_hash: [0; 32],
2800 announce_raw: None,
2801 },
2802 1,
2803 ),
2804 );
2805
2806 let tag = [0x03; 16];
2807 let data = make_path_request_data(&dest, &tag);
2808
2809 let actions = engine.handle_path_request(&data, InterfaceId(1), 1000.0);
2810
2811 assert!(actions.is_empty());
2813 assert!(!engine.announce_table.contains_key(&dest));
2814 }
2815
2816 fn make_announce_raw(dest_hash: &[u8; 16], payload: &[u8]) -> Vec<u8> {
2818 let flags: u8 = 0x01; let mut raw = Vec::new();
2822 raw.push(flags);
2823 raw.push(0x02); raw.extend_from_slice(dest_hash);
2825 raw.push(constants::CONTEXT_NONE);
2826 raw.extend_from_slice(payload);
2827 raw
2828 }
2829
2830 #[test]
2831 fn test_path_request_populates_announce_entry_from_raw() {
2832 let mut engine = TransportEngine::new(make_config(true));
2833 engine.register_interface(make_interface(1, constants::MODE_FULL));
2834 engine.register_interface(make_interface(2, constants::MODE_FULL));
2835
2836 let dest = [0xD5; 16];
2837 let payload = vec![0xAB; 32]; let announce_raw = make_announce_raw(&dest, &payload);
2839
2840 engine.path_table.insert(
2841 dest,
2842 PathSet::from_single(
2843 PathEntry {
2844 timestamp: 900.0,
2845 next_hop: [0xBB; 16],
2846 hops: 2,
2847 expires: 9999.0,
2848 random_blobs: Vec::new(),
2849 receiving_interface: InterfaceId(2),
2850 packet_hash: [0; 32],
2851 announce_raw: Some(announce_raw.clone()),
2852 },
2853 1,
2854 ),
2855 );
2856
2857 let tag = [0x05; 16];
2858 let data = make_path_request_data(&dest, &tag);
2859 let _actions = engine.handle_path_request(&data, InterfaceId(1), 1000.0);
2860
2861 let entry = engine
2863 .announce_table
2864 .get(&dest)
2865 .expect("announce entry must exist");
2866 assert_eq!(entry.packet_raw, announce_raw);
2867 assert_eq!(entry.packet_data, payload);
2868 assert!(entry.block_rebroadcasts);
2869 }
2870
2871 #[test]
2872 fn test_path_request_skips_when_no_announce_raw() {
2873 let mut engine = TransportEngine::new(make_config(true));
2874 engine.register_interface(make_interface(1, constants::MODE_FULL));
2875 engine.register_interface(make_interface(2, constants::MODE_FULL));
2876
2877 let dest = [0xD6; 16];
2878
2879 engine.path_table.insert(
2880 dest,
2881 PathSet::from_single(
2882 PathEntry {
2883 timestamp: 900.0,
2884 next_hop: [0xCC; 16],
2885 hops: 1,
2886 expires: 9999.0,
2887 random_blobs: Vec::new(),
2888 receiving_interface: InterfaceId(2),
2889 packet_hash: [0; 32],
2890 announce_raw: None, },
2892 1,
2893 ),
2894 );
2895
2896 let tag = [0x06; 16];
2897 let data = make_path_request_data(&dest, &tag);
2898 let actions = engine.handle_path_request(&data, InterfaceId(1), 1000.0);
2899
2900 assert!(actions.is_empty());
2902 assert!(!engine.announce_table.contains_key(&dest));
2903 }
2904
2905 #[test]
2906 fn test_discovery_request_consumed_on_announce() {
2907 let mut engine = TransportEngine::new(make_config(true));
2908 engine.register_interface(make_interface(1, constants::MODE_ACCESS_POINT));
2909
2910 let dest = [0xD4; 16];
2911
2912 engine.discovery_path_requests.insert(
2914 dest,
2915 DiscoveryPathRequest {
2916 timestamp: 900.0,
2917 requesting_interface: InterfaceId(1),
2918 },
2919 );
2920
2921 let iface = engine.discovery_path_requests_waiting(&dest);
2923 assert_eq!(iface, Some(InterfaceId(1)));
2924
2925 assert!(!engine.discovery_path_requests.contains_key(&dest));
2927 assert_eq!(engine.discovery_path_requests_waiting(&dest), None);
2928 }
2929}