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 Ok(decoded) = self.decode_inbound(frame.raw.to_vec()) else {
50 return Vec::new();
51 };
52 self.handle_decoded_inbound_with_announce_queue(
53 DecodedInboundFrame {
54 decoded,
55 iface: frame.iface,
56 now: frame.now,
57 rx: frame.rx,
58 },
59 rng,
60 announce_queue,
61 )
62 }
63
64 pub fn decode_inbound(
75 &self,
76 raw: Vec<u8>,
77 ) -> Result<DecodedPacket, crate::packet::PacketError> {
78 let flags = raw
80 .first()
81 .map(|flags| crate::packet::PacketFlags::unpack(*flags));
82 if !flags.is_some_and(|flags| {
83 flags.packet_type == constants::PACKET_TYPE_DATA
84 && flags.destination_type == constants::DESTINATION_LINK
85 }) {
86 return DecodedPacket::unpack_owned(raw);
87 }
88 let mut packet = RawPacket::unpack_unhashed(raw)?;
89 let hash_deferred = self.forwards_link_packet_unhashed(&packet);
90 if !hash_deferred {
91 packet.hash_in_place();
92 }
93 Ok(DecodedPacket {
94 packet,
95 hash_deferred,
96 })
97 }
98
99 fn forwards_link_packet_unhashed(&self, packet: &RawPacket) -> bool {
105 packet.flags.packet_type == constants::PACKET_TYPE_DATA
106 && packet.flags.destination_type == constants::DESTINATION_LINK
107 && matches!(
108 packet.context,
109 constants::CONTEXT_KEEPALIVE
110 | constants::CONTEXT_RESOURCE_REQ
111 | constants::CONTEXT_RESOURCE
112 | constants::CONTEXT_CACHE_REQUEST
113 | constants::CONTEXT_CHANNEL
114 )
115 && (self.config.transport_enabled || self.config.identity_hash.is_some())
116 && self
117 .link_table
118 .get(&packet.destination_hash)
119 .is_some_and(|entry| entry.validated)
120 && !self
121 .local_destinations
122 .contains_key(&packet.destination_hash)
123 && !self.path_table.contains_key(&packet.destination_hash)
124 }
125
126 pub fn handle_decoded_inbound_with_announce_queue(
128 &mut self,
129 frame: DecodedInboundFrame,
130 rng: &mut dyn Rng,
131 announce_queue: Option<&mut AnnounceVerifyQueue>,
132 ) -> Vec<TransportAction> {
133 let Some(mut ctx) = self.prepare_decoded_packet(frame) else {
134 return Vec::new();
135 };
136 if ctx.hash_deferred && !self.forwards_link_packet_unhashed(&ctx.packet) {
139 ctx.packet.hash_in_place();
140 ctx.hash_deferred = false;
141 }
142 let mut actions = Vec::new();
143
144 self.remember_inbound_packet_hash(&ctx.packet);
145 self.bridge_plain_broadcast(&ctx, &mut actions);
146 self.handle_transport_forwarding(&ctx, &mut actions);
147 self.handle_link_table_routing(&ctx, &mut actions);
148 self.handle_inbound_announce(&ctx, rng, announce_queue, &mut actions);
149
150 if ctx.packet.flags.packet_type == constants::PACKET_TYPE_PROOF {
151 self.process_inbound_proof(&ctx, &mut actions);
152 }
153
154 self.handle_inbound_local_delivery(&ctx, &mut actions);
155 actions
156 }
157
158 pub(super) fn prepare_inbound_packet(
159 &self,
160 frame: InboundFrame<'_>,
161 ) -> Option<InboundPacketCtx> {
162 let decoded = DecodedPacket::unpack(frame.raw).ok()?;
163 self.prepare_decoded_packet(DecodedInboundFrame {
164 decoded,
165 iface: frame.iface,
166 now: frame.now,
167 rx: frame.rx,
168 })
169 }
170
171 pub(super) fn filtered_inbound_hops(
172 &self,
173 packet: &RawPacket,
174 iface: InterfaceId,
175 ) -> Option<(u8, bool)> {
176 let from_local_client = self
177 .interfaces
178 .get(&iface)
179 .is_some_and(|i| i.is_local_client);
180 let mut hops = packet.hops.checked_add(1)?;
181 if from_local_client {
182 hops = hops.saturating_sub(1);
183 }
184 self.packet_filter_at_hops(packet, hops)
185 .then_some((hops, from_local_client))
186 }
187
188 pub fn accepts_decoded_packet(&self, decoded: &DecodedPacket, iface: InterfaceId) -> bool {
189 self.filtered_inbound_hops(&decoded.packet, iface).is_some()
190 }
191
192 pub(super) fn prepare_decoded_packet(
193 &self,
194 frame: DecodedInboundFrame,
195 ) -> Option<InboundPacketCtx> {
196 let hash_deferred = frame.decoded.hash_deferred;
197 let mut packet = frame.decoded.packet;
198 let (hops, from_local_client) = self.filtered_inbound_hops(&packet, frame.iface)?;
199 packet.hops = hops;
200 packet.rssi = frame.rx.rssi;
201 packet.snr = frame.rx.snr;
202 let retain_original_raw = packet.flags.packet_type == constants::PACKET_TYPE_ANNOUNCE;
203 let original_raw = retain_original_raw.then(|| packet.raw.clone());
204 Some(InboundPacketCtx {
205 packet,
206 hash_deferred,
207 original_raw,
208 iface: frame.iface,
209 now: frame.now,
210 from_local_client,
211 })
212 }
213
214 fn remember_inbound_packet_hash(&mut self, packet: &RawPacket) {
215 let remember_hash = !(self.link_table.contains_key(&packet.destination_hash)
216 || (packet.flags.packet_type == constants::PACKET_TYPE_PROOF
217 && packet.context == constants::CONTEXT_LRPROOF));
218 if remember_hash {
219 self.packet_hashlist.add(packet.packet_hash);
220 }
221 }
222
223 fn bridge_plain_broadcast(&self, ctx: &InboundPacketCtx, actions: &mut Vec<TransportAction>) {
224 if ctx.packet.flags.destination_type != constants::DESTINATION_PLAIN
225 || ctx.packet.flags.transport_type != constants::TRANSPORT_BROADCAST
226 || !self.has_local_clients()
227 {
228 return;
229 }
230
231 if ctx.from_local_client {
232 actions.push(TransportAction::ForwardPlainBroadcast {
233 raw: PacketBytes::from(ctx.packet.raw.clone()),
234 to_local: false,
235 exclude: Some(ctx.iface),
236 });
237 } else {
238 actions.push(TransportAction::ForwardPlainBroadcast {
239 raw: PacketBytes::from(ctx.packet.raw.clone()),
240 to_local: true,
241 exclude: None,
242 });
243 }
244 }
245
246 fn handle_transport_forwarding(
247 &mut self,
248 ctx: &InboundPacketCtx,
249 actions: &mut Vec<TransportAction>,
250 ) {
251 if !(self.config.transport_enabled || self.config.identity_hash.is_some()) {
252 return;
253 }
254 let for_local_client = ctx.packet.flags.packet_type != constants::PACKET_TYPE_ANNOUNCE
259 && self
260 .path_table
261 .get(&ctx.packet.destination_hash)
262 .and_then(|ps| ps.primary())
263 .is_some_and(|entry| {
264 entry.hops == 0 && self.interface_is_local_client(entry.receiving_interface)
265 });
266 let transport_id = match ctx.packet.transport_id {
267 None if for_local_client => self.config.identity_hash,
268 transport_id => transport_id,
269 };
270 if transport_id.is_none() || ctx.packet.flags.packet_type == constants::PACKET_TYPE_ANNOUNCE
271 {
272 if ctx.packet.flags.packet_type == constants::PACKET_TYPE_DATA {
273 log::debug!(
274 "TransportForward: DATA dest={:02x}{:02x}{:02x}{:02x}.. not transport-addressed header={} iface={}",
275 ctx.packet.destination_hash[0],
276 ctx.packet.destination_hash[1],
277 ctx.packet.destination_hash[2],
278 ctx.packet.destination_hash[3],
279 ctx.packet.flags.header_type,
280 ctx.iface.0
281 );
282 }
283 return;
284 }
285
286 let Some(identity_hash) = self.config.identity_hash else {
287 return;
288 };
289 if transport_id != Some(identity_hash) {
290 if ctx.packet.flags.packet_type == constants::PACKET_TYPE_DATA {
291 log::debug!(
292 "TransportForward: DATA dest={:02x}{:02x}{:02x}{:02x}.. transport mismatch got={:02x?} own={:02x?} iface={}",
293 ctx.packet.destination_hash[0],
294 ctx.packet.destination_hash[1],
295 ctx.packet.destination_hash[2],
296 ctx.packet.destination_hash[3],
297 ctx.packet.transport_id.as_ref().map(|id| &id[..4]),
298 &identity_hash[..4],
299 ctx.iface.0
300 );
301 }
302 return;
303 }
304
305 let Some(path_entry) = self
306 .path_table
307 .get(&ctx.packet.destination_hash)
308 .and_then(|ps| ps.primary())
309 else {
310 if ctx.packet.flags.packet_type == constants::PACKET_TYPE_DATA {
311 log::debug!(
312 "TransportForward: DATA dest={:02x}{:02x}{:02x}{:02x}.. addressed to us but no path iface={}",
313 ctx.packet.destination_hash[0],
314 ctx.packet.destination_hash[1],
315 ctx.packet.destination_hash[2],
316 ctx.packet.destination_hash[3],
317 ctx.iface.0
318 );
319 }
320 return;
321 };
322
323 let next_hop = path_entry.next_hop;
324 let remaining_hops = path_entry.hops;
325 let outbound_interface = path_entry.receiving_interface;
326 let outbound_is_local_client = self
327 .interfaces
328 .get(&outbound_interface)
329 .map(|info| info.is_local_client)
330 .unwrap_or(false);
331 let forwarded_remaining_hops = if outbound_is_local_client {
332 0
333 } else {
334 remaining_hops
335 };
336 if ctx.packet.flags.packet_type == constants::PACKET_TYPE_DATA {
337 log::debug!(
338 "TransportForward: DATA dest={:02x}{:02x}{:02x}{:02x}.. remaining_hops={} out_iface={} next_hop={:02x?}",
339 ctx.packet.destination_hash[0],
340 ctx.packet.destination_hash[1],
341 ctx.packet.destination_hash[2],
342 ctx.packet.destination_hash[3],
343 remaining_hops,
344 outbound_interface.0,
345 &next_hop[..4]
346 );
347 }
348 let mut new_raw = forward_transport_packet(
349 &ctx.packet,
350 next_hop,
351 forwarded_remaining_hops,
352 outbound_interface,
353 );
354 if self.config.local_hops_delta != 0
355 && ctx.from_local_client
356 && !outbound_is_local_client
357 && ctx.packet.hops == 0
358 && ctx.packet.flags.destination_type != constants::DESTINATION_PLAIN
359 && ctx.packet.flags.destination_type != constants::DESTINATION_GROUP
360 && new_raw.len() > 1
361 {
362 new_raw[1] = self.config.local_hops_delta;
363 }
364
365 if ctx.packet.flags.packet_type == constants::PACKET_TYPE_LINKREQUEST {
366 clamp_forwarded_link_request_mtu(
367 &mut new_raw,
368 ctx.packet.data().len() == constants::LINK_ECPUBSIZE + constants::LINK_MTU_SIZE,
369 self.interfaces.get(&ctx.iface).map(|info| info.mtu),
370 self.interfaces
371 .get(&outbound_interface)
372 .map(|info| info.mtu),
373 );
374 let extra_proof_timeout =
375 extra_link_proof_timeout(self.interfaces.get(&outbound_interface));
376 let proof_timeout = ctx.now
377 + constants::LINK_ESTABLISHMENT_TIMEOUT_PER_HOP * (remaining_hops.max(1) as f64)
378 + extra_proof_timeout;
379 let (link_id, link_entry) = create_link_entry(
380 &ctx.packet,
381 next_hop,
382 outbound_interface,
383 remaining_hops,
384 ctx.iface,
385 ctx.now,
386 proof_timeout,
387 );
388 self.link_table.insert(link_id, link_entry);
389 actions.push(TransportAction::LinkRequestReceived {
390 link_id,
391 destination_hash: ctx.packet.destination_hash,
392 receiving_interface: ctx.iface,
393 });
394 } else {
395 let (trunc_hash, reverse_entry) =
396 create_reverse_entry(&ctx.packet, outbound_interface, ctx.iface, ctx.now);
397 self.reverse_table.insert(trunc_hash, reverse_entry);
398 }
399
400 actions.push(TransportAction::SendOnInterface {
401 interface: outbound_interface,
402 raw: new_raw.into(),
403 });
404
405 if let Some(entry) = self
406 .path_table
407 .get_mut(&ctx.packet.destination_hash)
408 .and_then(|ps| ps.primary_mut())
409 {
410 entry.timestamp = ctx.now;
411 }
412 }
413
414 fn handle_link_table_routing(
415 &mut self,
416 ctx: &InboundPacketCtx,
417 actions: &mut Vec<TransportAction>,
418 ) {
419 if !self.config.transport_enabled && self.config.identity_hash.is_none() {
420 return;
421 }
422 if ctx.packet.flags.packet_type == constants::PACKET_TYPE_ANNOUNCE
423 || ctx.packet.flags.packet_type == constants::PACKET_TYPE_LINKREQUEST
424 || ctx.packet.context == constants::CONTEXT_LRPROOF
425 {
426 return;
427 }
428
429 let Some(link_entry) = self.link_table.get(&ctx.packet.destination_hash).cloned() else {
436 return;
437 };
438 if !link_entry.validated {
439 return;
440 }
441 let instance_local_link = self.interface_is_local_client(link_entry.next_hop_interface)
442 && self.interface_is_local_client(link_entry.received_interface);
443 let Some((outbound_iface, new_raw)) = super::inbound::route_via_link_table_shared(
444 &ctx.packet,
445 &link_entry,
446 ctx.iface,
447 LocalHopRewrite {
448 local_hops_delta: self.config.local_hops_delta,
449 from_local_client: ctx.from_local_client,
450 skip_local_hops_delta: instance_local_link,
451 },
452 ) else {
453 return;
456 };
457
458 if !ctx.hash_deferred {
461 self.packet_hashlist.add(ctx.packet.packet_hash);
462 }
463 actions.push(TransportAction::SendOnInterface {
464 interface: outbound_iface,
465 raw: new_raw,
466 });
467
468 if let Some(entry) = self.link_table.get_mut(&ctx.packet.destination_hash) {
469 entry.timestamp = ctx.now;
470 }
471 }
472
473 fn handle_inbound_announce(
474 &mut self,
475 ctx: &InboundPacketCtx,
476 rng: &mut dyn Rng,
477 announce_queue: Option<&mut AnnounceVerifyQueue>,
478 actions: &mut Vec<TransportAction>,
479 ) {
480 if ctx.packet.flags.packet_type != constants::PACKET_TYPE_ANNOUNCE {
481 return;
482 }
483
484 if let Some(queue) = announce_queue {
485 self.try_enqueue_announce(ctx, rng, queue, actions);
486 } else {
487 let original_raw = ctx
488 .original_raw
489 .as_deref()
490 .expect("announce packets retain original raw bytes");
491 self.process_inbound_announce(
492 &ctx.packet,
493 original_raw,
494 ctx.iface,
495 ctx.now,
496 rng,
497 actions,
498 );
499 }
500 }
501
502 fn handle_inbound_local_delivery(
503 &self,
504 ctx: &InboundPacketCtx,
505 actions: &mut Vec<TransportAction>,
506 ) {
507 if (ctx.packet.flags.packet_type == constants::PACKET_TYPE_LINKREQUEST
508 || ctx.packet.flags.packet_type == constants::PACKET_TYPE_DATA)
509 && self
513 .local_destinations
514 .contains_key(&ctx.packet.destination_hash)
515 {
516 let mut delivery_raw = ctx.packet.raw.clone();
517 if ctx.packet.context == constants::CONTEXT_LRRTT && delivery_raw.len() >= 2 {
521 delivery_raw[1] = ctx.packet.hops;
522 }
523 actions.push(TransportAction::DeliverLocal {
524 destination_hash: ctx.packet.destination_hash,
525 raw: PacketBytes::from(delivery_raw),
526 packet_hash: ctx.packet.packet_hash,
527 receiving_interface: ctx.iface,
528 });
529 }
530 }
531
532 fn process_inbound_announce(
537 &mut self,
538 packet: &RawPacket,
539 original_raw: &[u8],
540 iface: InterfaceId,
541 now: f64,
542 rng: &mut dyn Rng,
543 actions: &mut Vec<TransportAction>,
544 ) {
545 if packet.flags.destination_type != constants::DESTINATION_SINGLE {
546 return;
547 }
548
549 let has_ratchet = packet.flags.context_flag == constants::FLAG_SET;
550
551 let announce = match AnnounceData::unpack(packet.data(), has_ratchet) {
553 Ok(a) => a,
554 Err(_) => {
555 actions.push(TransportAction::ProtocolViolation { interface: iface });
556 return;
557 }
558 };
559
560 if self.should_hold_announce(packet, original_raw, iface, now) {
561 return;
562 }
563
564 let sig_cache_key = Self::announce_sig_cache_key(&packet.destination_hash, &announce);
565
566 let validated = if self.announce_sig_cache.contains(&sig_cache_key) {
567 announce.to_validated_with_cached_signature(&packet.destination_hash)
568 } else {
569 announce.validate(&packet.destination_hash).inspect(|_| {
570 self.announce_sig_cache.insert(sig_cache_key, now);
571 })
572 };
573 let validated = match validated {
574 Ok(v) => v,
575 Err(_) => {
576 self.record_invalid_announce(&announce, iface, now, actions);
577 return;
578 }
579 };
580
581 let received_from = self.announce_received_from(packet, now);
582 let random_blob = match extract_random_blob(packet.data()) {
583 Some(b) => b,
584 None => {
585 actions.push(TransportAction::ProtocolViolation { interface: iface });
586 return;
587 }
588 };
589 let announce_emitted = timebase_from_random_blob(&random_blob);
590
591 self.process_verified_announce(
592 VerifiedAnnounceCtx {
593 packet,
594 original_raw,
595 iface,
596 now,
597 validated,
598 received_from,
599 random_blob,
600 announce_emitted,
601 },
602 rng,
603 actions,
604 );
605 }
606
607 fn announce_raw_for_local_clients(&self, packet: &RawPacket) -> PacketBytes {
608 let Some(identity_hash) = self.config.identity_hash else {
609 return PacketBytes::from(packet.raw.clone());
610 };
611
612 if packet.raw.len() < 2 {
613 return PacketBytes::from(packet.raw.clone());
614 }
615
616 let payload_start = if packet.flags.header_type == constants::HEADER_2 {
617 18usize
618 } else {
619 2usize
620 };
621 if packet.raw.len() < payload_start {
622 return PacketBytes::from(packet.raw.clone());
623 }
624
625 let flags = (constants::HEADER_2 << 6)
626 | (constants::TRANSPORT_TRANSPORT << 4)
627 | (packet.raw[0] & 0x0F);
628 let mut raw = Vec::with_capacity(18 + packet.raw.len() - payload_start);
629 raw.push(flags);
630 raw.push(packet.hops);
631 raw.extend_from_slice(&identity_hash);
632 raw.extend_from_slice(&packet.raw[payload_start..]);
633 PacketBytes::from(raw)
634 }
635
636 pub(super) fn announce_sig_cache_key(
639 destination_hash: &[u8; 16],
640 announce: &AnnounceData,
641 ) -> [u8; 32] {
642 announce.signature_cache_key(destination_hash)
643 }
644
645 fn announce_received_from(&mut self, packet: &RawPacket, now: f64) -> [u8; 16] {
646 if let Some(transport_id) = packet.transport_id {
647 if self.config.transport_enabled {
648 if let Some(announce_entry) = self.announce_table.get_mut(&packet.destination_hash)
649 {
650 if packet.hops.checked_sub(1) == Some(announce_entry.hops) {
651 announce_entry.local_rebroadcasts += 1;
652 if announce_entry.retries > 0
653 && announce_entry.local_rebroadcasts
654 >= constants::LOCAL_REBROADCASTS_MAX
655 {
656 self.announce_table.remove(&packet.destination_hash);
657 }
658 }
659 if let Some(announce_entry) = self.announce_table.get(&packet.destination_hash)
660 {
661 if packet.hops.checked_sub(1) == Some(announce_entry.hops + 1)
662 && announce_entry.retries > 0
663 && now < announce_entry.retransmit_timeout
664 {
665 self.announce_table.remove(&packet.destination_hash);
666 }
667 }
668 }
669 }
670 transport_id
671 } else {
672 packet.destination_hash
673 }
674 }
675
676 fn should_hold_announce(
677 &mut self,
678 packet: &RawPacket,
679 original_raw: &[u8],
680 iface: InterfaceId,
681 now: f64,
682 ) -> bool {
683 if self.has_path(&packet.destination_hash) {
684 return false;
685 }
686 if self
687 .discovery_path_requests
688 .contains_key(&packet.destination_hash)
689 {
690 return false;
691 }
692 let Some(info) = self.interfaces.get(&iface) else {
693 return false;
694 };
695 if packet.context == constants::CONTEXT_PATH_RESPONSE
696 || !self.ingress_control.should_ingress_limit(
697 iface,
698 &info.ingress_control,
699 info.ia_freq,
700 info.started,
701 now,
702 )
703 {
704 return false;
705 }
706 self.ingress_control.hold_announce(
707 iface,
708 &info.ingress_control,
709 packet.destination_hash,
710 ingress_control::HeldAnnounce {
711 raw: original_raw.to_vec(),
712 hops: packet.hops,
713 receiving_interface: iface,
714 rx: RxMetadata {
715 rssi: packet.rssi,
716 snr: packet.snr,
717 },
718 timestamp: now,
719 },
720 );
721 true
722 }
723
724 fn try_enqueue_announce(
725 &mut self,
726 ctx: &InboundPacketCtx,
727 rng: &mut dyn Rng,
728 announce_queue: &mut AnnounceVerifyQueue,
729 actions: &mut Vec<TransportAction>,
730 ) {
731 if ctx.packet.flags.destination_type != constants::DESTINATION_SINGLE {
732 return;
733 }
734
735 let has_ratchet = ctx.packet.flags.context_flag == constants::FLAG_SET;
736 let announce = match AnnounceData::unpack(ctx.packet.data(), has_ratchet) {
737 Ok(a) => a,
738 Err(_) => {
739 actions.push(TransportAction::ProtocolViolation {
740 interface: ctx.iface,
741 });
742 return;
743 }
744 };
745
746 let received_from = self.announce_received_from(&ctx.packet, ctx.now);
747
748 if self
751 .local_destinations
752 .contains_key(&ctx.packet.destination_hash)
753 {
754 log::trace!(target: crate::logging::PATHING_LOG_TARGET,
755 "Announce:skipping local destination {:02x}{:02x}{:02x}{:02x}..",
756 ctx.packet.destination_hash[0],
757 ctx.packet.destination_hash[1],
758 ctx.packet.destination_hash[2],
759 ctx.packet.destination_hash[3],
760 );
761 return;
762 }
763
764 let original_raw = ctx
765 .original_raw
766 .as_deref()
767 .expect("announce packets retain original raw bytes");
768 if self.should_hold_announce(&ctx.packet, original_raw, ctx.iface, ctx.now) {
769 return;
770 }
771
772 let sig_cache_key = Self::announce_sig_cache_key(&ctx.packet.destination_hash, &announce);
773 if self.announce_sig_cache.contains(&sig_cache_key) {
774 let Ok(validated) =
775 announce.to_validated_with_cached_signature(&ctx.packet.destination_hash)
776 else {
777 self.record_invalid_announce(&announce, ctx.iface, ctx.now, actions);
778 return;
779 };
780 let random_blob = match extract_random_blob(ctx.packet.data()) {
781 Some(b) => b,
782 None => return,
783 };
784 let announce_emitted = timebase_from_random_blob(&random_blob);
785 self.process_verified_announce(
786 VerifiedAnnounceCtx {
787 packet: &ctx.packet,
788 original_raw,
789 iface: ctx.iface,
790 now: ctx.now,
791 validated,
792 received_from,
793 random_blob,
794 announce_emitted,
795 },
796 rng,
797 actions,
798 );
799 return;
800 }
801
802 if ctx.packet.context == constants::CONTEXT_PATH_RESPONSE {
803 let Ok(validated) = announce.validate(&ctx.packet.destination_hash) else {
804 self.record_invalid_announce(&announce, ctx.iface, ctx.now, actions);
805 return;
806 };
807 self.announce_sig_cache.insert(sig_cache_key, ctx.now);
808 let random_blob = match extract_random_blob(ctx.packet.data()) {
809 Some(b) => b,
810 None => return,
811 };
812 let announce_emitted = timebase_from_random_blob(&random_blob);
813 self.process_verified_announce(
814 VerifiedAnnounceCtx {
815 packet: &ctx.packet,
816 original_raw,
817 iface: ctx.iface,
818 now: ctx.now,
819 validated,
820 received_from,
821 random_blob,
822 announce_emitted,
823 },
824 rng,
825 actions,
826 );
827 return;
828 }
829
830 let random_blob = match extract_random_blob(ctx.packet.data()) {
831 Some(b) => b,
832 None => {
833 actions.push(TransportAction::ProtocolViolation {
834 interface: ctx.iface,
835 });
836 return;
837 }
838 };
839 let announce_emitted = timebase_from_random_blob(&random_blob);
840 let key = AnnounceVerifyKey {
841 destination_hash: ctx.packet.destination_hash,
842 random_blob,
843 received_from,
844 };
845 let pending = PendingAnnounce {
846 original_raw: original_raw.to_vec(),
847 packet: ctx.packet.clone(),
848 interface: ctx.iface,
849 received_from,
850 queued_at: ctx.now,
851 best_hops: ctx.packet.hops,
852 emission_ts: announce_emitted,
853 random_blob,
854 };
855 let _ = announce_queue.enqueue(key, pending);
856 }
857
858 pub fn complete_verified_announce(
859 &mut self,
860 pending: PendingAnnounce,
861 validated: crate::announce::ValidatedAnnounce,
862 sig_cache_key: [u8; 32],
863 now: f64,
864 rng: &mut dyn Rng,
865 ) -> Vec<TransportAction> {
866 self.announce_sig_cache.insert(sig_cache_key, now);
867 let mut actions = Vec::new();
868 self.process_verified_announce(
869 VerifiedAnnounceCtx {
870 packet: &pending.packet,
871 original_raw: &pending.original_raw,
872 iface: pending.interface,
873 now,
874 validated,
875 received_from: pending.received_from,
876 random_blob: pending.random_blob,
877 announce_emitted: pending.emission_ts,
878 },
879 rng,
880 &mut actions,
881 );
882 actions
883 }
884
885 pub fn clear_failed_verified_announce(&mut self, _sig_cache_key: [u8; 32], _now: f64) {}
886
887 fn process_verified_announce(
888 &mut self,
889 ctx: VerifiedAnnounceCtx<'_>,
890 rng: &mut dyn Rng,
891 actions: &mut Vec<TransportAction>,
892 ) {
893 if self.is_blackholed(&ctx.validated.identity_hash, ctx.now) {
894 return;
895 }
896 if ctx.packet.hops > constants::PATHFINDER_M {
897 return;
898 }
899
900 let existing_set = self.path_table.get(&ctx.packet.destination_hash);
901 let was_unknown_destination = existing_set.is_none_or(|ps| ps.is_empty());
902
903 if was_unknown_destination {
906 self.path_states.remove(&ctx.packet.destination_hash);
907 }
908
909 let is_unresponsive = self.path_is_unresponsive(&ctx.packet.destination_hash);
911
912 let current_gravity = existing_set
913 .and_then(|path_set| path_set.primary())
914 .and_then(|path| self.interfaces.get(&path.receiving_interface))
915 .map(|interface| interface.gravity);
916 let announce_gravity = self
917 .interfaces
918 .get(&ctx.iface)
919 .map(|interface| interface.gravity);
920 let higher_gravity_replacement = existing_set.is_some_and(|path_set| {
921 pathfinder::is_higher_gravity_replacement(
922 path_set,
923 ctx.packet.hops,
924 ctx.announce_emitted,
925 ctx.now,
926 current_gravity,
927 announce_gravity,
928 )
929 });
930 let mp_decision = pathfinder::decide_announce_multipath_with_gravity(
931 existing_set,
932 ctx.packet.hops,
933 ctx.announce_emitted,
934 &ctx.random_blob,
935 &ctx.received_from,
936 is_unresponsive,
937 ctx.now,
938 self.config.prefer_shorter_path,
939 current_gravity,
940 announce_gravity,
941 );
942
943 if mp_decision == MultiPathDecision::Reject {
944 log::trace!(target: crate::logging::PATHING_LOG_TARGET,
945 "Announce:path decision REJECT for dest={:02x}{:02x}{:02x}{:02x}..",
946 ctx.packet.destination_hash[0],
947 ctx.packet.destination_hash[1],
948 ctx.packet.destination_hash[2],
949 ctx.packet.destination_hash[3],
950 );
951 return;
952 }
953 if higher_gravity_replacement {
954 log::log!(
955 target: crate::logging::PATHING_LOG_TARGET,
956 crate::logging::GRAVITY_UPDATE_LOG_LEVEL,
957 "Replacing path table entry for {:02x}{:02x}{:02x}{:02x}.. due to higher gravity ({:?}->{:?})",
958 ctx.packet.destination_hash[0],
959 ctx.packet.destination_hash[1],
960 ctx.packet.destination_hash[2],
961 ctx.packet.destination_hash[3],
962 current_gravity,
963 announce_gravity,
964 );
965 }
966
967 let rate_blocked = if ctx.packet.context != constants::CONTEXT_PATH_RESPONSE {
969 if let Some(iface_info) = self.interfaces.get(&ctx.iface) {
970 self.rate_limiter.check_and_update(
971 &ctx.packet.destination_hash,
972 ctx.now,
973 iface_info.announce_rate_target,
974 iface_info.announce_rate_grace,
975 iface_info.announce_rate_penalty,
976 )
977 } else {
978 false
979 }
980 } else {
981 false
982 };
983
984 let interface_mode = self
986 .interfaces
987 .get(&ctx.iface)
988 .map(|i| i.mode)
989 .unwrap_or(constants::MODE_FULL);
990
991 let expires = compute_path_expires(ctx.now, interface_mode);
992
993 let existing_blobs = self
995 .path_table
996 .get(&ctx.packet.destination_hash)
997 .and_then(|ps| ps.find_by_next_hop(&ctx.received_from))
998 .map(|e| e.random_blobs.clone())
999 .unwrap_or_default();
1000
1001 let mut rng_bytes = [0u8; 8];
1003 rng.fill_bytes(&mut rng_bytes);
1004 let rng_value = (u64::from_le_bytes(rng_bytes) as f64) / (u64::MAX as f64);
1005
1006 let is_path_response = ctx.packet.context == constants::CONTEXT_PATH_RESPONSE;
1007
1008 let (path_entry, announce_entry) = announce_proc::process_validated_announce(
1009 ctx.packet.destination_hash,
1010 ctx.packet.hops,
1011 ctx.packet.data(),
1012 &ctx.packet.raw,
1013 ctx.packet.packet_hash,
1014 ctx.packet.flags.context_flag,
1015 ctx.received_from,
1016 ctx.iface,
1017 ctx.now,
1018 existing_blobs,
1019 ctx.random_blob,
1020 expires,
1021 rng_value,
1022 self.config.transport_enabled,
1023 self.serves_local_client(ctx.iface),
1024 is_path_response,
1025 rate_blocked,
1026 Some(ctx.original_raw.to_vec()),
1027 );
1028
1029 actions.push(TransportAction::CacheAnnounce {
1031 packet_hash: ctx.packet.packet_hash,
1032 raw: ctx.original_raw.to_vec().into(),
1033 });
1034
1035 match mp_decision {
1037 MultiPathDecision::ReplacePrimary => self.upsert_primary_path_destination(
1038 ctx.packet.destination_hash,
1039 path_entry,
1040 ctx.now,
1041 ),
1042 MultiPathDecision::AddAlternative => {
1043 self.upsert_path_destination(ctx.packet.destination_hash, path_entry, ctx.now)
1044 }
1045 MultiPathDecision::Reject => unreachable!("rejected decisions returned above"),
1046 }
1047
1048 if let Some(tunnel_id) = self.interfaces.get(&ctx.iface).and_then(|i| i.tunnel_id) {
1050 let blobs = self
1051 .path_table
1052 .get(&ctx.packet.destination_hash)
1053 .and_then(|ps| ps.find_by_next_hop(&ctx.received_from))
1054 .map(|e| e.random_blobs.clone())
1055 .unwrap_or_default();
1056 self.tunnel_table.store_tunnel_path(
1057 &tunnel_id,
1058 ctx.packet.destination_hash,
1059 tunnel::TunnelPath {
1060 timestamp: ctx.now,
1061 received_from: ctx.received_from,
1062 hops: ctx.packet.hops,
1063 expires,
1064 random_blobs: blobs,
1065 packet_hash: ctx.packet.packet_hash,
1066 },
1067 ctx.now,
1068 self.config.destination_timeout_secs,
1069 self.config.max_tunnel_destinations_total,
1070 );
1071 }
1072
1073 self.path_states.remove(&ctx.packet.destination_hash);
1076
1077 if let Some(ann) = announce_entry {
1079 self.insert_announce_entry(ctx.packet.destination_hash, ann, ctx.now);
1080 } else if is_path_response && self.serves_local_client(ctx.iface) {
1081 if let Some((requesting_interface, _)) = self
1084 .pending_local_path_requests
1085 .remove(&ctx.packet.destination_hash)
1086 {
1087 let entry = AnnounceEntry {
1088 timestamp: ctx.now,
1089 retransmit_timeout: ctx.now,
1090 retries: constants::PATHFINDER_R,
1091 received_from: ctx.received_from,
1092 hops: ctx.packet.hops,
1093 packet_raw: ctx.packet.raw.clone(),
1094 packet_data: ctx.packet.data().to_vec(),
1095 destination_hash: ctx.packet.destination_hash,
1096 context_flag: ctx.packet.flags.context_flag,
1097 local_rebroadcasts: 0,
1098 block_rebroadcasts: true,
1099 attached_interface: Some(requesting_interface),
1100 };
1101 self.insert_announce_entry(ctx.packet.destination_hash, entry, ctx.now);
1102 }
1103 }
1104
1105 actions.push(TransportAction::AnnounceReceived(alloc::boxed::Box::new(
1107 types::ReceivedAnnounce {
1108 destination_hash: ctx.packet.destination_hash,
1109 identity_hash: ctx.validated.identity_hash,
1110 public_key: ctx.validated.public_key,
1111 name_hash: ctx.validated.name_hash,
1112 random_hash: ctx.validated.random_hash,
1113 ratchet: ctx.validated.ratchet,
1114 app_data: ctx.validated.app_data,
1115 hops: ctx.packet.hops,
1116 receiving_interface: ctx.iface,
1117 rx: RxMetadata {
1118 rssi: ctx.packet.rssi,
1119 snr: ctx.packet.snr,
1120 },
1121 },
1122 )));
1123
1124 actions.push(TransportAction::PathUpdated {
1125 destination_hash: ctx.packet.destination_hash,
1126 hops: ctx.packet.hops,
1127 next_hop: ctx.received_from,
1128 interface: ctx.iface,
1129 });
1130
1131 if self.has_local_clients() {
1133 actions.push(TransportAction::ForwardToLocalClients {
1134 raw: self.announce_raw_for_local_clients(ctx.packet),
1135 exclude: Some(ctx.iface),
1136 });
1137 }
1138
1139 if let Some(requesting_interfaces) =
1141 self.discovery_path_requests_waiting(&ctx.packet.destination_hash)
1142 {
1143 let entry = AnnounceEntry {
1145 timestamp: ctx.now,
1146 retransmit_timeout: ctx.now,
1147 retries: constants::PATHFINDER_R,
1148 received_from: ctx.received_from,
1149 hops: ctx.packet.hops,
1150 packet_raw: ctx.packet.raw.clone(),
1151 packet_data: ctx.packet.data().to_vec(),
1152 destination_hash: ctx.packet.destination_hash,
1153 context_flag: ctx.packet.flags.context_flag,
1154 local_rebroadcasts: 0,
1155 block_rebroadcasts: true,
1156 attached_interface: requesting_interfaces.first().copied(),
1157 };
1158 if let Some(identity_hash) = self.config.identity_hash {
1159 let raw = announce_proc::build_retransmit_announce(&entry, &identity_hash);
1160 for interface in requesting_interfaces.iter().skip(1) {
1161 actions.push(TransportAction::SendOnInterface {
1162 interface: *interface,
1163 raw: raw.clone().into(),
1164 });
1165 }
1166 }
1167 self.insert_announce_entry(ctx.packet.destination_hash, entry, ctx.now);
1168 }
1169 }
1170
1171 pub fn announce_sig_cache_contains(&self, sig_cache_key: &[u8; 32]) -> bool {
1172 self.announce_sig_cache.contains(sig_cache_key)
1173 }
1174
1175 pub(super) fn discovery_path_requests_waiting(
1178 &mut self,
1179 dest_hash: &[u8; 16],
1180 ) -> Option<Vec<InterfaceId>> {
1181 let request = self
1182 .discovery_path_requests
1183 .remove(dest_hash)
1184 .map(|req| req.requesting_interfaces);
1185 self.discovery_path_request_deadlines.remove(dest_hash);
1186 request
1187 }
1188
1189 fn process_inbound_proof(
1194 &mut self,
1195 ctx: &InboundPacketCtx,
1196 actions: &mut Vec<TransportAction>,
1197 ) {
1198 let packet = &ctx.packet;
1199 if packet.context == constants::CONTEXT_LRPROOF {
1200 let for_local_client_link =
1204 self.link_table
1205 .get(&packet.destination_hash)
1206 .is_some_and(|entry| {
1207 self.interface_is_local_client(entry.next_hop_interface)
1208 || self.interface_is_local_client(entry.received_interface)
1209 });
1210 if (self.config.transport_enabled || ctx.from_local_client || for_local_client_link)
1211 && self.link_table.contains_key(&packet.destination_hash)
1212 {
1213 let link_entry = self.link_table.get(&packet.destination_hash).cloned();
1214 if let Some(entry) = link_entry {
1215 let instance_local_link = self
1216 .interface_is_local_client(entry.next_hop_interface)
1217 && self.interface_is_local_client(entry.received_interface);
1218 if let Some((outbound_interface, new_raw)) = route_via_link_table(
1219 packet,
1220 &entry,
1221 ctx.iface,
1222 LocalHopRewrite {
1223 local_hops_delta: self.config.local_hops_delta,
1224 from_local_client: ctx.from_local_client,
1225 skip_local_hops_delta: instance_local_link,
1226 },
1227 ) {
1228 if let Some(le) = self.link_table.get_mut(&packet.destination_hash) {
1233 le.validated = true;
1234 }
1235
1236 actions.push(TransportAction::LinkEstablished {
1237 link_id: packet.destination_hash,
1238 interface: outbound_interface,
1239 });
1240
1241 actions.push(TransportAction::SendOnInterface {
1242 interface: outbound_interface,
1243 raw: new_raw.into(),
1244 });
1245 } else if link_route_hops_match(packet.hops, &entry, ctx.iface) {
1246 log::debug!(
1247 "Link request proof received on wrong interface {}, not transporting it (expected {} or {})",
1248 ctx.iface.0,
1249 entry.next_hop_interface.0,
1250 entry.received_interface.0,
1251 );
1252 } else {
1253 log::debug!("{}", lrproof_hop_mismatch_diagnostic(packet.hops, &entry));
1254 }
1255 }
1256 } else {
1257 let mut delivery_raw = packet.raw.clone();
1259 if delivery_raw.len() >= 2 {
1262 delivery_raw[1] = packet.hops;
1263 }
1264 actions.push(TransportAction::DeliverLocal {
1265 destination_hash: packet.destination_hash,
1266 raw: PacketBytes::from(delivery_raw),
1267 packet_hash: packet.packet_hash,
1268 receiving_interface: ctx.iface,
1269 });
1270 }
1271 } else {
1272 let proof_for_local_client = self
1276 .reverse_table
1277 .get(&packet.destination_hash)
1278 .is_some_and(|entry| self.interface_is_local_client(entry.receiving_interface));
1279 if self.config.transport_enabled || ctx.from_local_client || proof_for_local_client {
1280 if let Some(reverse_entry) = self.reverse_table.remove(&packet.destination_hash) {
1281 if let Some(action) = route_proof_via_reverse(
1282 packet,
1283 &reverse_entry,
1284 ctx.iface,
1285 LocalHopRewrite {
1286 local_hops_delta: self.config.local_hops_delta,
1287 from_local_client: ctx.from_local_client,
1288 skip_local_hops_delta: proof_for_local_client,
1289 },
1290 ) {
1291 actions.push(action);
1292 }
1293 }
1294 }
1295
1296 actions.push(TransportAction::DeliverLocal {
1298 destination_hash: packet.destination_hash,
1299 raw: PacketBytes::from(packet.raw.clone()),
1300 packet_hash: packet.packet_hash,
1301 receiving_interface: ctx.iface,
1302 });
1303 }
1304 }
1305}