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