1use super::*;
2
3pub(super) fn extra_link_proof_timeout(interface: Option<&InterfaceInfo>) -> f64 {
4 interface
5 .and_then(|interface| interface.bitrate)
6 .filter(|bitrate| *bitrate > 0)
7 .map_or(0.0, |bitrate| {
8 (constants::MTU as f64 * 8.0) / bitrate as f64
9 })
10}
11
12impl TransportEngine {
13 fn record_invalid_announce(
14 &self,
15 announce: &AnnounceData,
16 interface: InterfaceId,
17 now: f64,
18 actions: &mut Vec<TransportAction>,
19 ) {
20 let identity_hash = crate::hash::truncated_hash(&announce.public_key);
21 if !self.is_blackholed(&identity_hash, now) {
22 actions.push(TransportAction::ProtocolViolation { interface });
23 }
24 }
25
26 pub fn accepts_inbound_frame(&self, frame: InboundFrame<'_>) -> bool {
29 self.prepare_inbound_packet(frame).is_some()
30 }
31
32 pub fn handle_inbound(
36 &mut self,
37 frame: InboundFrame<'_>,
38 rng: &mut dyn Rng,
39 ) -> Vec<TransportAction> {
40 self.handle_inbound_with_announce_queue(frame, rng, None)
41 }
42
43 pub fn handle_inbound_with_announce_queue(
44 &mut self,
45 frame: InboundFrame<'_>,
46 rng: &mut dyn Rng,
47 announce_queue: Option<&mut AnnounceVerifyQueue>,
48 ) -> Vec<TransportAction> {
49 let Some(ctx) = self.prepare_inbound_packet(frame) else {
50 return Vec::new();
51 };
52 let mut actions = Vec::new();
53
54 self.remember_inbound_packet_hash(&ctx.packet);
55 self.bridge_plain_broadcast(&ctx, &mut actions);
56 self.handle_transport_forwarding(&ctx, &mut actions);
57 self.handle_link_table_routing(&ctx, &mut actions);
58 self.handle_inbound_announce(&ctx, rng, announce_queue, &mut actions);
59
60 if ctx.packet.flags.packet_type == constants::PACKET_TYPE_PROOF {
61 self.process_inbound_proof(&ctx, &mut actions);
62 }
63
64 self.handle_inbound_local_delivery(&ctx, &mut actions);
65 actions
66 }
67
68 pub(super) fn prepare_inbound_packet(
69 &self,
70 frame: InboundFrame<'_>,
71 ) -> Option<InboundPacketCtx> {
72 let mut packet = RawPacket::unpack(frame.raw).ok()?;
73 let from_local_client = self
74 .interfaces
75 .get(&frame.iface)
76 .map(|i| i.is_local_client)
77 .unwrap_or(false);
78 packet.hops = packet.hops.checked_add(1)?;
79 packet.rssi = frame.rx.rssi;
80 packet.snr = frame.rx.snr;
81 if from_local_client {
82 packet.hops = packet.hops.saturating_sub(1);
83 }
84 if !self.packet_filter(&packet) {
85 return None;
86 }
87 let retain_original_raw = packet.flags.packet_type == constants::PACKET_TYPE_ANNOUNCE;
88 Some(InboundPacketCtx {
89 packet,
90 original_raw: if retain_original_raw {
91 Some(frame.raw.to_vec())
92 } else {
93 None
94 },
95 iface: frame.iface,
96 now: frame.now,
97 from_local_client,
98 })
99 }
100
101 fn remember_inbound_packet_hash(&mut self, packet: &RawPacket) {
102 let remember_hash = !(self.link_table.contains_key(&packet.destination_hash)
103 || (packet.flags.packet_type == constants::PACKET_TYPE_PROOF
104 && packet.context == constants::CONTEXT_LRPROOF));
105 if remember_hash {
106 self.packet_hashlist.add(packet.packet_hash);
107 }
108 }
109
110 fn bridge_plain_broadcast(&self, ctx: &InboundPacketCtx, actions: &mut Vec<TransportAction>) {
111 if ctx.packet.flags.destination_type != constants::DESTINATION_PLAIN
112 || ctx.packet.flags.transport_type != constants::TRANSPORT_BROADCAST
113 || !self.has_local_clients()
114 {
115 return;
116 }
117
118 if ctx.from_local_client {
119 actions.push(TransportAction::ForwardPlainBroadcast {
120 raw: PacketBytes::from(ctx.packet.raw.clone()),
121 to_local: false,
122 exclude: Some(ctx.iface),
123 });
124 } else {
125 actions.push(TransportAction::ForwardPlainBroadcast {
126 raw: PacketBytes::from(ctx.packet.raw.clone()),
127 to_local: true,
128 exclude: None,
129 });
130 }
131 }
132
133 fn handle_transport_forwarding(
134 &mut self,
135 ctx: &InboundPacketCtx,
136 actions: &mut Vec<TransportAction>,
137 ) {
138 if !(self.config.transport_enabled || self.config.identity_hash.is_some()) {
139 return;
140 }
141 if ctx.packet.transport_id.is_none()
142 || ctx.packet.flags.packet_type == constants::PACKET_TYPE_ANNOUNCE
143 {
144 if ctx.packet.flags.packet_type == constants::PACKET_TYPE_DATA {
145 log::debug!(
146 "TransportForward: DATA dest={:02x}{:02x}{:02x}{:02x}.. not transport-addressed header={} iface={}",
147 ctx.packet.destination_hash[0],
148 ctx.packet.destination_hash[1],
149 ctx.packet.destination_hash[2],
150 ctx.packet.destination_hash[3],
151 ctx.packet.flags.header_type,
152 ctx.iface.0
153 );
154 }
155 return;
156 }
157
158 let Some(identity_hash) = self.config.identity_hash else {
159 return;
160 };
161 if ctx.packet.transport_id != Some(identity_hash) {
162 if ctx.packet.flags.packet_type == constants::PACKET_TYPE_DATA {
163 log::debug!(
164 "TransportForward: DATA dest={:02x}{:02x}{:02x}{:02x}.. transport mismatch got={:02x?} own={:02x?} iface={}",
165 ctx.packet.destination_hash[0],
166 ctx.packet.destination_hash[1],
167 ctx.packet.destination_hash[2],
168 ctx.packet.destination_hash[3],
169 ctx.packet.transport_id.as_ref().map(|id| &id[..4]),
170 &identity_hash[..4],
171 ctx.iface.0
172 );
173 }
174 return;
175 }
176
177 let Some(path_entry) = self
178 .path_table
179 .get(&ctx.packet.destination_hash)
180 .and_then(|ps| ps.primary())
181 else {
182 if ctx.packet.flags.packet_type == constants::PACKET_TYPE_DATA {
183 log::debug!(
184 "TransportForward: DATA dest={:02x}{:02x}{:02x}{:02x}.. addressed to us but no path iface={}",
185 ctx.packet.destination_hash[0],
186 ctx.packet.destination_hash[1],
187 ctx.packet.destination_hash[2],
188 ctx.packet.destination_hash[3],
189 ctx.iface.0
190 );
191 }
192 return;
193 };
194
195 let next_hop = path_entry.next_hop;
196 let remaining_hops = path_entry.hops;
197 let outbound_interface = path_entry.receiving_interface;
198 let outbound_is_local_client = self
199 .interfaces
200 .get(&outbound_interface)
201 .map(|info| info.is_local_client)
202 .unwrap_or(false);
203 let forwarded_remaining_hops = if outbound_is_local_client {
204 0
205 } else {
206 remaining_hops
207 };
208 if ctx.packet.flags.packet_type == constants::PACKET_TYPE_DATA {
209 log::debug!(
210 "TransportForward: DATA dest={:02x}{:02x}{:02x}{:02x}.. remaining_hops={} out_iface={} next_hop={:02x?}",
211 ctx.packet.destination_hash[0],
212 ctx.packet.destination_hash[1],
213 ctx.packet.destination_hash[2],
214 ctx.packet.destination_hash[3],
215 remaining_hops,
216 outbound_interface.0,
217 &next_hop[..4]
218 );
219 }
220 let mut new_raw = forward_transport_packet(
221 &ctx.packet,
222 next_hop,
223 forwarded_remaining_hops,
224 outbound_interface,
225 );
226 if self.config.local_hops_delta != 0
227 && ctx.from_local_client
228 && !outbound_is_local_client
229 && ctx.packet.hops == 0
230 && ctx.packet.flags.destination_type != constants::DESTINATION_PLAIN
231 && ctx.packet.flags.destination_type != constants::DESTINATION_GROUP
232 && new_raw.len() > 1
233 {
234 new_raw[1] = self.config.local_hops_delta;
235 }
236
237 if ctx.packet.flags.packet_type == constants::PACKET_TYPE_LINKREQUEST {
238 let extra_proof_timeout =
239 extra_link_proof_timeout(self.interfaces.get(&outbound_interface));
240 let proof_timeout = ctx.now
241 + constants::LINK_ESTABLISHMENT_TIMEOUT_PER_HOP * (remaining_hops.max(1) as f64)
242 + extra_proof_timeout;
243 let (link_id, link_entry) = create_link_entry(
244 &ctx.packet,
245 next_hop,
246 outbound_interface,
247 remaining_hops,
248 ctx.iface,
249 ctx.now,
250 proof_timeout,
251 );
252 self.link_table.insert(link_id, link_entry);
253 actions.push(TransportAction::LinkRequestReceived {
254 link_id,
255 destination_hash: ctx.packet.destination_hash,
256 receiving_interface: ctx.iface,
257 });
258 } else {
259 let (trunc_hash, reverse_entry) =
260 create_reverse_entry(&ctx.packet, outbound_interface, ctx.iface, ctx.now);
261 self.reverse_table.insert(trunc_hash, reverse_entry);
262 }
263
264 actions.push(TransportAction::SendOnInterface {
265 interface: outbound_interface,
266 raw: new_raw.into(),
267 });
268
269 if let Some(entry) = self
270 .path_table
271 .get_mut(&ctx.packet.destination_hash)
272 .and_then(|ps| ps.primary_mut())
273 {
274 entry.timestamp = ctx.now;
275 }
276 }
277
278 fn handle_link_table_routing(
279 &mut self,
280 ctx: &InboundPacketCtx,
281 actions: &mut Vec<TransportAction>,
282 ) {
283 if !self.config.transport_enabled && self.config.identity_hash.is_none() {
284 return;
285 }
286 if ctx.packet.flags.packet_type == constants::PACKET_TYPE_ANNOUNCE
287 || ctx.packet.flags.packet_type == constants::PACKET_TYPE_LINKREQUEST
288 || ctx.packet.context == constants::CONTEXT_LRPROOF
289 {
290 return;
291 }
292
293 let Some(link_entry) = self.link_table.get(&ctx.packet.destination_hash).cloned() else {
300 return;
301 };
302 if !link_entry.validated {
303 return;
304 }
305 let instance_local_link = self.interface_is_local_client(link_entry.next_hop_interface)
306 && self.interface_is_local_client(link_entry.received_interface);
307 let Some((outbound_iface, new_raw)) = route_via_link_table(
308 &ctx.packet,
309 &link_entry,
310 ctx.iface,
311 LocalHopRewrite {
312 local_hops_delta: self.config.local_hops_delta,
313 from_local_client: ctx.from_local_client,
314 skip_local_hops_delta: instance_local_link,
315 },
316 ) else {
317 return;
320 };
321
322 self.packet_hashlist.add(ctx.packet.packet_hash);
323 actions.push(TransportAction::SendOnInterface {
324 interface: outbound_iface,
325 raw: new_raw.into(),
326 });
327
328 if let Some(entry) = self.link_table.get_mut(&ctx.packet.destination_hash) {
329 entry.timestamp = ctx.now;
330 }
331 }
332
333 fn handle_inbound_announce(
334 &mut self,
335 ctx: &InboundPacketCtx,
336 rng: &mut dyn Rng,
337 announce_queue: Option<&mut AnnounceVerifyQueue>,
338 actions: &mut Vec<TransportAction>,
339 ) {
340 if ctx.packet.flags.packet_type != constants::PACKET_TYPE_ANNOUNCE {
341 return;
342 }
343
344 if let Some(queue) = announce_queue {
345 self.try_enqueue_announce(ctx, rng, queue, actions);
346 } else {
347 let original_raw = ctx
348 .original_raw
349 .as_deref()
350 .expect("announce packets retain original raw bytes");
351 self.process_inbound_announce(
352 &ctx.packet,
353 original_raw,
354 ctx.iface,
355 ctx.now,
356 rng,
357 actions,
358 );
359 }
360 }
361
362 fn handle_inbound_local_delivery(
363 &self,
364 ctx: &InboundPacketCtx,
365 actions: &mut Vec<TransportAction>,
366 ) {
367 if (ctx.packet.flags.packet_type == constants::PACKET_TYPE_LINKREQUEST
368 || ctx.packet.flags.packet_type == constants::PACKET_TYPE_DATA)
369 && self
373 .local_destinations
374 .contains_key(&ctx.packet.destination_hash)
375 {
376 let mut delivery_raw = ctx.packet.raw.clone();
377 if ctx.packet.context == constants::CONTEXT_LRRTT && delivery_raw.len() >= 2 {
381 delivery_raw[1] = ctx.packet.hops;
382 }
383 actions.push(TransportAction::DeliverLocal {
384 destination_hash: ctx.packet.destination_hash,
385 raw: PacketBytes::from(delivery_raw),
386 packet_hash: ctx.packet.packet_hash,
387 receiving_interface: ctx.iface,
388 });
389 }
390 }
391
392 fn process_inbound_announce(
397 &mut self,
398 packet: &RawPacket,
399 original_raw: &[u8],
400 iface: InterfaceId,
401 now: f64,
402 rng: &mut dyn Rng,
403 actions: &mut Vec<TransportAction>,
404 ) {
405 if packet.flags.destination_type != constants::DESTINATION_SINGLE {
406 return;
407 }
408
409 let has_ratchet = packet.flags.context_flag == constants::FLAG_SET;
410
411 let announce = match AnnounceData::unpack(&packet.data, has_ratchet) {
413 Ok(a) => a,
414 Err(_) => {
415 actions.push(TransportAction::ProtocolViolation { interface: iface });
416 return;
417 }
418 };
419
420 if self.should_hold_announce(packet, original_raw, iface, now) {
421 return;
422 }
423
424 let sig_cache_key =
425 Self::announce_sig_cache_key(packet.destination_hash, &announce.signature);
426
427 let validated = if self.announce_sig_cache.contains(&sig_cache_key) {
428 announce.to_validated_unchecked()
429 } else {
430 match announce.validate(&packet.destination_hash) {
431 Ok(v) => {
432 self.announce_sig_cache.insert(sig_cache_key, now);
433 v
434 }
435 Err(_) => {
436 self.record_invalid_announce(&announce, iface, now, actions);
437 return;
438 }
439 }
440 };
441
442 let received_from = self.announce_received_from(packet, now);
443 let random_blob = match extract_random_blob(&packet.data) {
444 Some(b) => b,
445 None => {
446 actions.push(TransportAction::ProtocolViolation { interface: iface });
447 return;
448 }
449 };
450 let announce_emitted = timebase_from_random_blob(&random_blob);
451
452 self.process_verified_announce(
453 VerifiedAnnounceCtx {
454 packet,
455 original_raw,
456 iface,
457 now,
458 validated,
459 received_from,
460 random_blob,
461 announce_emitted,
462 },
463 rng,
464 actions,
465 );
466 }
467
468 fn announce_raw_for_local_clients(&self, packet: &RawPacket) -> PacketBytes {
469 let Some(identity_hash) = self.config.identity_hash else {
470 return PacketBytes::from(packet.raw.clone());
471 };
472
473 if packet.raw.len() < 2 {
474 return PacketBytes::from(packet.raw.clone());
475 }
476
477 let payload_start = if packet.flags.header_type == constants::HEADER_2 {
478 18usize
479 } else {
480 2usize
481 };
482 if packet.raw.len() < payload_start {
483 return PacketBytes::from(packet.raw.clone());
484 }
485
486 let flags = (constants::HEADER_2 << 6)
487 | (constants::TRANSPORT_TRANSPORT << 4)
488 | (packet.raw[0] & 0x0F);
489 let mut raw = Vec::with_capacity(18 + packet.raw.len() - payload_start);
490 raw.push(flags);
491 raw.push(packet.hops);
492 raw.extend_from_slice(&identity_hash);
493 raw.extend_from_slice(&packet.raw[payload_start..]);
494 PacketBytes::from(raw)
495 }
496
497 pub(super) fn announce_sig_cache_key(
498 destination_hash: [u8; 16],
499 signature: &[u8; 64],
500 ) -> [u8; 32] {
501 let mut material = [0u8; 80];
502 material[..16].copy_from_slice(&destination_hash);
503 material[16..].copy_from_slice(signature);
504 hash::full_hash(&material)
505 }
506
507 fn announce_received_from(&mut self, packet: &RawPacket, now: f64) -> [u8; 16] {
508 if let Some(transport_id) = packet.transport_id {
509 if self.config.transport_enabled {
510 if let Some(announce_entry) = self.announce_table.get_mut(&packet.destination_hash)
511 {
512 if packet.hops.checked_sub(1) == Some(announce_entry.hops) {
513 announce_entry.local_rebroadcasts += 1;
514 if announce_entry.retries > 0
515 && announce_entry.local_rebroadcasts
516 >= constants::LOCAL_REBROADCASTS_MAX
517 {
518 self.announce_table.remove(&packet.destination_hash);
519 }
520 }
521 if let Some(announce_entry) = self.announce_table.get(&packet.destination_hash)
522 {
523 if packet.hops.checked_sub(1) == Some(announce_entry.hops + 1)
524 && announce_entry.retries > 0
525 && now < announce_entry.retransmit_timeout
526 {
527 self.announce_table.remove(&packet.destination_hash);
528 }
529 }
530 }
531 }
532 transport_id
533 } else {
534 packet.destination_hash
535 }
536 }
537
538 fn should_hold_announce(
539 &mut self,
540 packet: &RawPacket,
541 original_raw: &[u8],
542 iface: InterfaceId,
543 now: f64,
544 ) -> bool {
545 if self.has_path(&packet.destination_hash) {
546 return false;
547 }
548 if self
549 .discovery_path_requests
550 .contains_key(&packet.destination_hash)
551 {
552 return false;
553 }
554 let Some(info) = self.interfaces.get(&iface) else {
555 return false;
556 };
557 if packet.context == constants::CONTEXT_PATH_RESPONSE
558 || !self.ingress_control.should_ingress_limit(
559 iface,
560 &info.ingress_control,
561 info.ia_freq,
562 info.started,
563 now,
564 )
565 {
566 return false;
567 }
568 self.ingress_control.hold_announce(
569 iface,
570 &info.ingress_control,
571 packet.destination_hash,
572 ingress_control::HeldAnnounce {
573 raw: original_raw.to_vec(),
574 hops: packet.hops,
575 receiving_interface: iface,
576 rx: RxMetadata {
577 rssi: packet.rssi,
578 snr: packet.snr,
579 },
580 timestamp: now,
581 },
582 );
583 true
584 }
585
586 fn try_enqueue_announce(
587 &mut self,
588 ctx: &InboundPacketCtx,
589 rng: &mut dyn Rng,
590 announce_queue: &mut AnnounceVerifyQueue,
591 actions: &mut Vec<TransportAction>,
592 ) {
593 if ctx.packet.flags.destination_type != constants::DESTINATION_SINGLE {
594 return;
595 }
596
597 let has_ratchet = ctx.packet.flags.context_flag == constants::FLAG_SET;
598 let announce = match AnnounceData::unpack(&ctx.packet.data, has_ratchet) {
599 Ok(a) => a,
600 Err(_) => {
601 actions.push(TransportAction::ProtocolViolation {
602 interface: ctx.iface,
603 });
604 return;
605 }
606 };
607
608 let received_from = self.announce_received_from(&ctx.packet, ctx.now);
609
610 if self
613 .local_destinations
614 .contains_key(&ctx.packet.destination_hash)
615 {
616 log::trace!(target: crate::logging::PATHING_LOG_TARGET,
617 "Announce:skipping local destination {:02x}{:02x}{:02x}{:02x}..",
618 ctx.packet.destination_hash[0],
619 ctx.packet.destination_hash[1],
620 ctx.packet.destination_hash[2],
621 ctx.packet.destination_hash[3],
622 );
623 return;
624 }
625
626 let original_raw = ctx
627 .original_raw
628 .as_deref()
629 .expect("announce packets retain original raw bytes");
630 if self.should_hold_announce(&ctx.packet, original_raw, ctx.iface, ctx.now) {
631 return;
632 }
633
634 let sig_cache_key =
635 Self::announce_sig_cache_key(ctx.packet.destination_hash, &announce.signature);
636 if self.announce_sig_cache.contains(&sig_cache_key) {
637 let validated = announce.to_validated_unchecked();
638 let random_blob = match extract_random_blob(&ctx.packet.data) {
639 Some(b) => b,
640 None => return,
641 };
642 let announce_emitted = timebase_from_random_blob(&random_blob);
643 self.process_verified_announce(
644 VerifiedAnnounceCtx {
645 packet: &ctx.packet,
646 original_raw,
647 iface: ctx.iface,
648 now: ctx.now,
649 validated,
650 received_from,
651 random_blob,
652 announce_emitted,
653 },
654 rng,
655 actions,
656 );
657 return;
658 }
659
660 if ctx.packet.context == constants::CONTEXT_PATH_RESPONSE {
661 let Ok(validated) = announce.validate(&ctx.packet.destination_hash) else {
662 self.record_invalid_announce(&announce, ctx.iface, ctx.now, actions);
663 return;
664 };
665 self.announce_sig_cache.insert(sig_cache_key, ctx.now);
666 let random_blob = match extract_random_blob(&ctx.packet.data) {
667 Some(b) => b,
668 None => return,
669 };
670 let announce_emitted = timebase_from_random_blob(&random_blob);
671 self.process_verified_announce(
672 VerifiedAnnounceCtx {
673 packet: &ctx.packet,
674 original_raw,
675 iface: ctx.iface,
676 now: ctx.now,
677 validated,
678 received_from,
679 random_blob,
680 announce_emitted,
681 },
682 rng,
683 actions,
684 );
685 return;
686 }
687
688 let random_blob = match extract_random_blob(&ctx.packet.data) {
689 Some(b) => b,
690 None => {
691 actions.push(TransportAction::ProtocolViolation {
692 interface: ctx.iface,
693 });
694 return;
695 }
696 };
697 let announce_emitted = timebase_from_random_blob(&random_blob);
698 let key = AnnounceVerifyKey {
699 destination_hash: ctx.packet.destination_hash,
700 random_blob,
701 received_from,
702 };
703 let pending = PendingAnnounce {
704 original_raw: original_raw.to_vec(),
705 packet: ctx.packet.clone(),
706 interface: ctx.iface,
707 received_from,
708 queued_at: ctx.now,
709 best_hops: ctx.packet.hops,
710 emission_ts: announce_emitted,
711 random_blob,
712 };
713 let _ = announce_queue.enqueue(key, pending);
714 }
715
716 pub fn complete_verified_announce(
717 &mut self,
718 pending: PendingAnnounce,
719 validated: crate::announce::ValidatedAnnounce,
720 sig_cache_key: [u8; 32],
721 now: f64,
722 rng: &mut dyn Rng,
723 ) -> Vec<TransportAction> {
724 self.announce_sig_cache.insert(sig_cache_key, now);
725 let mut actions = Vec::new();
726 self.process_verified_announce(
727 VerifiedAnnounceCtx {
728 packet: &pending.packet,
729 original_raw: &pending.original_raw,
730 iface: pending.interface,
731 now,
732 validated,
733 received_from: pending.received_from,
734 random_blob: pending.random_blob,
735 announce_emitted: pending.emission_ts,
736 },
737 rng,
738 &mut actions,
739 );
740 actions
741 }
742
743 pub fn clear_failed_verified_announce(&mut self, _sig_cache_key: [u8; 32], _now: f64) {}
744
745 fn process_verified_announce(
746 &mut self,
747 ctx: VerifiedAnnounceCtx<'_>,
748 rng: &mut dyn Rng,
749 actions: &mut Vec<TransportAction>,
750 ) {
751 if self.is_blackholed(&ctx.validated.identity_hash, ctx.now) {
752 return;
753 }
754 if ctx.packet.hops > constants::PATHFINDER_M {
755 return;
756 }
757
758 let existing_set = self.path_table.get(&ctx.packet.destination_hash);
759 let was_unknown_destination = existing_set.is_none_or(|ps| ps.is_empty());
760
761 if was_unknown_destination {
764 self.path_states.remove(&ctx.packet.destination_hash);
765 }
766
767 let is_unresponsive = self.path_is_unresponsive(&ctx.packet.destination_hash);
769
770 let current_gravity = existing_set
771 .and_then(|path_set| path_set.primary())
772 .and_then(|path| self.interfaces.get(&path.receiving_interface))
773 .map(|interface| interface.gravity);
774 let announce_gravity = self
775 .interfaces
776 .get(&ctx.iface)
777 .map(|interface| interface.gravity);
778 let higher_gravity_replacement = existing_set.is_some_and(|path_set| {
779 pathfinder::is_higher_gravity_replacement(
780 path_set,
781 ctx.packet.hops,
782 ctx.announce_emitted,
783 current_gravity,
784 announce_gravity,
785 )
786 });
787 let mp_decision = pathfinder::decide_announce_multipath_with_gravity(
788 existing_set,
789 ctx.packet.hops,
790 ctx.announce_emitted,
791 &ctx.random_blob,
792 &ctx.received_from,
793 is_unresponsive,
794 ctx.now,
795 self.config.prefer_shorter_path,
796 current_gravity,
797 announce_gravity,
798 );
799
800 if mp_decision == MultiPathDecision::Reject {
801 log::trace!(target: crate::logging::PATHING_LOG_TARGET,
802 "Announce:path decision REJECT for dest={:02x}{:02x}{:02x}{:02x}..",
803 ctx.packet.destination_hash[0],
804 ctx.packet.destination_hash[1],
805 ctx.packet.destination_hash[2],
806 ctx.packet.destination_hash[3],
807 );
808 return;
809 }
810 if higher_gravity_replacement {
811 log::log!(
812 target: crate::logging::PATHING_LOG_TARGET,
813 crate::logging::GRAVITY_UPDATE_LOG_LEVEL,
814 "Replacing path table entry for {:02x}{:02x}{:02x}{:02x}.. due to higher gravity ({:?}->{:?})",
815 ctx.packet.destination_hash[0],
816 ctx.packet.destination_hash[1],
817 ctx.packet.destination_hash[2],
818 ctx.packet.destination_hash[3],
819 current_gravity,
820 announce_gravity,
821 );
822 }
823
824 let rate_blocked = if ctx.packet.context != constants::CONTEXT_PATH_RESPONSE {
826 if let Some(iface_info) = self.interfaces.get(&ctx.iface) {
827 self.rate_limiter.check_and_update(
828 &ctx.packet.destination_hash,
829 ctx.now,
830 iface_info.announce_rate_target,
831 iface_info.announce_rate_grace,
832 iface_info.announce_rate_penalty,
833 )
834 } else {
835 false
836 }
837 } else {
838 false
839 };
840
841 let interface_mode = self
843 .interfaces
844 .get(&ctx.iface)
845 .map(|i| i.mode)
846 .unwrap_or(constants::MODE_FULL);
847
848 let expires = compute_path_expires(ctx.now, interface_mode);
849
850 let existing_blobs = self
852 .path_table
853 .get(&ctx.packet.destination_hash)
854 .and_then(|ps| ps.find_by_next_hop(&ctx.received_from))
855 .map(|e| e.random_blobs.clone())
856 .unwrap_or_default();
857
858 let mut rng_bytes = [0u8; 8];
860 rng.fill_bytes(&mut rng_bytes);
861 let rng_value = (u64::from_le_bytes(rng_bytes) as f64) / (u64::MAX as f64);
862
863 let is_path_response = ctx.packet.context == constants::CONTEXT_PATH_RESPONSE;
864
865 let (path_entry, announce_entry) = announce_proc::process_validated_announce(
866 ctx.packet.destination_hash,
867 ctx.packet.hops,
868 &ctx.packet.data,
869 &ctx.packet.raw,
870 ctx.packet.packet_hash,
871 ctx.packet.flags.context_flag,
872 ctx.received_from,
873 ctx.iface,
874 ctx.now,
875 existing_blobs,
876 ctx.random_blob,
877 expires,
878 rng_value,
879 self.config.transport_enabled,
880 is_path_response,
881 rate_blocked,
882 Some(ctx.original_raw.to_vec()),
883 );
884
885 actions.push(TransportAction::CacheAnnounce {
887 packet_hash: ctx.packet.packet_hash,
888 raw: ctx.original_raw.to_vec().into(),
889 });
890
891 match mp_decision {
893 MultiPathDecision::ReplacePrimary => self.upsert_primary_path_destination(
894 ctx.packet.destination_hash,
895 path_entry,
896 ctx.now,
897 ),
898 MultiPathDecision::AddAlternative => {
899 self.upsert_path_destination(ctx.packet.destination_hash, path_entry, ctx.now)
900 }
901 MultiPathDecision::Reject => unreachable!("rejected decisions returned above"),
902 }
903
904 if let Some(tunnel_id) = self.interfaces.get(&ctx.iface).and_then(|i| i.tunnel_id) {
906 let blobs = self
907 .path_table
908 .get(&ctx.packet.destination_hash)
909 .and_then(|ps| ps.find_by_next_hop(&ctx.received_from))
910 .map(|e| e.random_blobs.clone())
911 .unwrap_or_default();
912 self.tunnel_table.store_tunnel_path(
913 &tunnel_id,
914 ctx.packet.destination_hash,
915 tunnel::TunnelPath {
916 timestamp: ctx.now,
917 received_from: ctx.received_from,
918 hops: ctx.packet.hops,
919 expires,
920 random_blobs: blobs,
921 packet_hash: ctx.packet.packet_hash,
922 },
923 ctx.now,
924 self.config.destination_timeout_secs,
925 self.config.max_tunnel_destinations_total,
926 );
927 }
928
929 self.path_states.remove(&ctx.packet.destination_hash);
932
933 if let Some(ann) = announce_entry {
935 self.insert_announce_entry(ctx.packet.destination_hash, ann, ctx.now);
936 }
937
938 actions.push(TransportAction::AnnounceReceived {
940 destination_hash: ctx.packet.destination_hash,
941 identity_hash: ctx.validated.identity_hash,
942 public_key: ctx.validated.public_key,
943 name_hash: ctx.validated.name_hash,
944 random_hash: ctx.validated.random_hash,
945 ratchet: ctx.validated.ratchet,
946 app_data: ctx.validated.app_data,
947 hops: ctx.packet.hops,
948 receiving_interface: ctx.iface,
949 rx: RxMetadata {
950 rssi: ctx.packet.rssi,
951 snr: ctx.packet.snr,
952 },
953 });
954
955 actions.push(TransportAction::PathUpdated {
956 destination_hash: ctx.packet.destination_hash,
957 hops: ctx.packet.hops,
958 next_hop: ctx.received_from,
959 interface: ctx.iface,
960 });
961
962 if self.has_local_clients() {
964 actions.push(TransportAction::ForwardToLocalClients {
965 raw: self.announce_raw_for_local_clients(ctx.packet),
966 exclude: Some(ctx.iface),
967 });
968 }
969
970 if let Some(requesting_interfaces) =
972 self.discovery_path_requests_waiting(&ctx.packet.destination_hash)
973 {
974 let entry = AnnounceEntry {
976 timestamp: ctx.now,
977 retransmit_timeout: ctx.now,
978 retries: constants::PATHFINDER_R,
979 received_from: ctx.received_from,
980 hops: ctx.packet.hops,
981 packet_raw: ctx.packet.raw.clone(),
982 packet_data: ctx.packet.data.clone(),
983 destination_hash: ctx.packet.destination_hash,
984 context_flag: ctx.packet.flags.context_flag,
985 local_rebroadcasts: 0,
986 block_rebroadcasts: true,
987 attached_interface: requesting_interfaces.first().copied(),
988 };
989 if let Some(identity_hash) = self.config.identity_hash {
990 let raw = announce_proc::build_retransmit_announce(&entry, &identity_hash);
991 for interface in requesting_interfaces.iter().skip(1) {
992 actions.push(TransportAction::SendOnInterface {
993 interface: *interface,
994 raw: raw.clone().into(),
995 });
996 }
997 }
998 self.insert_announce_entry(ctx.packet.destination_hash, entry, ctx.now);
999 }
1000 }
1001
1002 pub fn announce_sig_cache_contains(&self, sig_cache_key: &[u8; 32]) -> bool {
1003 self.announce_sig_cache.contains(sig_cache_key)
1004 }
1005
1006 pub(super) fn discovery_path_requests_waiting(
1009 &mut self,
1010 dest_hash: &[u8; 16],
1011 ) -> Option<Vec<InterfaceId>> {
1012 let request = self
1013 .discovery_path_requests
1014 .remove(dest_hash)
1015 .map(|req| req.requesting_interfaces);
1016 self.discovery_path_request_deadlines.remove(dest_hash);
1017 request
1018 }
1019
1020 fn process_inbound_proof(
1025 &mut self,
1026 ctx: &InboundPacketCtx,
1027 actions: &mut Vec<TransportAction>,
1028 ) {
1029 let packet = &ctx.packet;
1030 if packet.context == constants::CONTEXT_LRPROOF {
1031 if (self.config.transport_enabled)
1033 && self.link_table.contains_key(&packet.destination_hash)
1034 {
1035 let link_entry = self.link_table.get(&packet.destination_hash).cloned();
1036 if let Some(entry) = link_entry {
1037 let instance_local_link = self
1038 .interface_is_local_client(entry.next_hop_interface)
1039 && self.interface_is_local_client(entry.received_interface);
1040 if let Some((outbound_interface, new_raw)) = route_via_link_table(
1041 packet,
1042 &entry,
1043 ctx.iface,
1044 LocalHopRewrite {
1045 local_hops_delta: self.config.local_hops_delta,
1046 from_local_client: ctx.from_local_client,
1047 skip_local_hops_delta: instance_local_link,
1048 },
1049 ) {
1050 if let Some(le) = self.link_table.get_mut(&packet.destination_hash) {
1055 le.validated = true;
1056 }
1057
1058 actions.push(TransportAction::LinkEstablished {
1059 link_id: packet.destination_hash,
1060 interface: outbound_interface,
1061 });
1062
1063 actions.push(TransportAction::SendOnInterface {
1064 interface: outbound_interface,
1065 raw: new_raw.into(),
1066 });
1067 } else if link_route_hops_match(packet.hops, &entry, ctx.iface) {
1068 log::debug!(
1069 "Link request proof received on wrong interface {}, not transporting it (expected {} or {})",
1070 ctx.iface.0,
1071 entry.next_hop_interface.0,
1072 entry.received_interface.0,
1073 );
1074 } else {
1075 log::debug!("{}", lrproof_hop_mismatch_diagnostic(packet.hops, &entry));
1076 }
1077 }
1078 } else {
1079 let mut delivery_raw = packet.raw.clone();
1081 if delivery_raw.len() >= 2 {
1084 delivery_raw[1] = packet.hops;
1085 }
1086 actions.push(TransportAction::DeliverLocal {
1087 destination_hash: packet.destination_hash,
1088 raw: PacketBytes::from(delivery_raw),
1089 packet_hash: packet.packet_hash,
1090 receiving_interface: ctx.iface,
1091 });
1092 }
1093 } else {
1094 if self.config.transport_enabled {
1096 if let Some(reverse_entry) = self.reverse_table.remove(&packet.destination_hash) {
1097 let proof_for_local_client =
1098 self.interface_is_local_client(reverse_entry.receiving_interface);
1099 if let Some(action) = route_proof_via_reverse(
1100 packet,
1101 &reverse_entry,
1102 ctx.iface,
1103 LocalHopRewrite {
1104 local_hops_delta: self.config.local_hops_delta,
1105 from_local_client: ctx.from_local_client,
1106 skip_local_hops_delta: proof_for_local_client,
1107 },
1108 ) {
1109 actions.push(action);
1110 }
1111 }
1112 }
1113
1114 actions.push(TransportAction::DeliverLocal {
1116 destination_hash: packet.destination_hash,
1117 raw: PacketBytes::from(packet.raw.clone()),
1118 packet_hash: packet.packet_hash,
1119 receiving_interface: ctx.iface,
1120 });
1121 }
1122 }
1123}