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