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 if ctx.packet.transport_id.is_none()
255 || ctx.packet.flags.packet_type == constants::PACKET_TYPE_ANNOUNCE
256 {
257 if ctx.packet.flags.packet_type == constants::PACKET_TYPE_DATA {
258 log::debug!(
259 "TransportForward: DATA dest={:02x}{:02x}{:02x}{:02x}.. not transport-addressed header={} iface={}",
260 ctx.packet.destination_hash[0],
261 ctx.packet.destination_hash[1],
262 ctx.packet.destination_hash[2],
263 ctx.packet.destination_hash[3],
264 ctx.packet.flags.header_type,
265 ctx.iface.0
266 );
267 }
268 return;
269 }
270
271 let Some(identity_hash) = self.config.identity_hash else {
272 return;
273 };
274 if ctx.packet.transport_id != Some(identity_hash) {
275 if ctx.packet.flags.packet_type == constants::PACKET_TYPE_DATA {
276 log::debug!(
277 "TransportForward: DATA dest={:02x}{:02x}{:02x}{:02x}.. transport mismatch got={:02x?} own={:02x?} iface={}",
278 ctx.packet.destination_hash[0],
279 ctx.packet.destination_hash[1],
280 ctx.packet.destination_hash[2],
281 ctx.packet.destination_hash[3],
282 ctx.packet.transport_id.as_ref().map(|id| &id[..4]),
283 &identity_hash[..4],
284 ctx.iface.0
285 );
286 }
287 return;
288 }
289
290 let Some(path_entry) = self
291 .path_table
292 .get(&ctx.packet.destination_hash)
293 .and_then(|ps| ps.primary())
294 else {
295 if ctx.packet.flags.packet_type == constants::PACKET_TYPE_DATA {
296 log::debug!(
297 "TransportForward: DATA dest={:02x}{:02x}{:02x}{:02x}.. addressed to us but no path iface={}",
298 ctx.packet.destination_hash[0],
299 ctx.packet.destination_hash[1],
300 ctx.packet.destination_hash[2],
301 ctx.packet.destination_hash[3],
302 ctx.iface.0
303 );
304 }
305 return;
306 };
307
308 let next_hop = path_entry.next_hop;
309 let remaining_hops = path_entry.hops;
310 let outbound_interface = path_entry.receiving_interface;
311 let outbound_is_local_client = self
312 .interfaces
313 .get(&outbound_interface)
314 .map(|info| info.is_local_client)
315 .unwrap_or(false);
316 let forwarded_remaining_hops = if outbound_is_local_client {
317 0
318 } else {
319 remaining_hops
320 };
321 if ctx.packet.flags.packet_type == constants::PACKET_TYPE_DATA {
322 log::debug!(
323 "TransportForward: DATA dest={:02x}{:02x}{:02x}{:02x}.. remaining_hops={} out_iface={} next_hop={:02x?}",
324 ctx.packet.destination_hash[0],
325 ctx.packet.destination_hash[1],
326 ctx.packet.destination_hash[2],
327 ctx.packet.destination_hash[3],
328 remaining_hops,
329 outbound_interface.0,
330 &next_hop[..4]
331 );
332 }
333 let mut new_raw = forward_transport_packet(
334 &ctx.packet,
335 next_hop,
336 forwarded_remaining_hops,
337 outbound_interface,
338 );
339 if self.config.local_hops_delta != 0
340 && ctx.from_local_client
341 && !outbound_is_local_client
342 && ctx.packet.hops == 0
343 && ctx.packet.flags.destination_type != constants::DESTINATION_PLAIN
344 && ctx.packet.flags.destination_type != constants::DESTINATION_GROUP
345 && new_raw.len() > 1
346 {
347 new_raw[1] = self.config.local_hops_delta;
348 }
349
350 if ctx.packet.flags.packet_type == constants::PACKET_TYPE_LINKREQUEST {
351 clamp_forwarded_link_request_mtu(
352 &mut new_raw,
353 ctx.packet.data().len() == constants::LINK_ECPUBSIZE + constants::LINK_MTU_SIZE,
354 self.interfaces.get(&ctx.iface).map(|info| info.mtu),
355 self.interfaces
356 .get(&outbound_interface)
357 .map(|info| info.mtu),
358 );
359 let extra_proof_timeout =
360 extra_link_proof_timeout(self.interfaces.get(&outbound_interface));
361 let proof_timeout = ctx.now
362 + constants::LINK_ESTABLISHMENT_TIMEOUT_PER_HOP * (remaining_hops.max(1) as f64)
363 + extra_proof_timeout;
364 let (link_id, link_entry) = create_link_entry(
365 &ctx.packet,
366 next_hop,
367 outbound_interface,
368 remaining_hops,
369 ctx.iface,
370 ctx.now,
371 proof_timeout,
372 );
373 self.link_table.insert(link_id, link_entry);
374 actions.push(TransportAction::LinkRequestReceived {
375 link_id,
376 destination_hash: ctx.packet.destination_hash,
377 receiving_interface: ctx.iface,
378 });
379 } else {
380 let (trunc_hash, reverse_entry) =
381 create_reverse_entry(&ctx.packet, outbound_interface, ctx.iface, ctx.now);
382 self.reverse_table.insert(trunc_hash, reverse_entry);
383 }
384
385 actions.push(TransportAction::SendOnInterface {
386 interface: outbound_interface,
387 raw: new_raw.into(),
388 });
389
390 if let Some(entry) = self
391 .path_table
392 .get_mut(&ctx.packet.destination_hash)
393 .and_then(|ps| ps.primary_mut())
394 {
395 entry.timestamp = ctx.now;
396 }
397 }
398
399 fn handle_link_table_routing(
400 &mut self,
401 ctx: &InboundPacketCtx,
402 actions: &mut Vec<TransportAction>,
403 ) {
404 if !self.config.transport_enabled && self.config.identity_hash.is_none() {
405 return;
406 }
407 if ctx.packet.flags.packet_type == constants::PACKET_TYPE_ANNOUNCE
408 || ctx.packet.flags.packet_type == constants::PACKET_TYPE_LINKREQUEST
409 || ctx.packet.context == constants::CONTEXT_LRPROOF
410 {
411 return;
412 }
413
414 let Some(link_entry) = self.link_table.get(&ctx.packet.destination_hash).cloned() else {
421 return;
422 };
423 if !link_entry.validated {
424 return;
425 }
426 let instance_local_link = self.interface_is_local_client(link_entry.next_hop_interface)
427 && self.interface_is_local_client(link_entry.received_interface);
428 let Some((outbound_iface, new_raw)) = super::inbound::route_via_link_table_shared(
429 &ctx.packet,
430 &link_entry,
431 ctx.iface,
432 LocalHopRewrite {
433 local_hops_delta: self.config.local_hops_delta,
434 from_local_client: ctx.from_local_client,
435 skip_local_hops_delta: instance_local_link,
436 },
437 ) else {
438 return;
441 };
442
443 if !ctx.hash_deferred {
446 self.packet_hashlist.add(ctx.packet.packet_hash);
447 }
448 actions.push(TransportAction::SendOnInterface {
449 interface: outbound_iface,
450 raw: new_raw,
451 });
452
453 if let Some(entry) = self.link_table.get_mut(&ctx.packet.destination_hash) {
454 entry.timestamp = ctx.now;
455 }
456 }
457
458 fn handle_inbound_announce(
459 &mut self,
460 ctx: &InboundPacketCtx,
461 rng: &mut dyn Rng,
462 announce_queue: Option<&mut AnnounceVerifyQueue>,
463 actions: &mut Vec<TransportAction>,
464 ) {
465 if ctx.packet.flags.packet_type != constants::PACKET_TYPE_ANNOUNCE {
466 return;
467 }
468
469 if let Some(queue) = announce_queue {
470 self.try_enqueue_announce(ctx, rng, queue, actions);
471 } else {
472 let original_raw = ctx
473 .original_raw
474 .as_deref()
475 .expect("announce packets retain original raw bytes");
476 self.process_inbound_announce(
477 &ctx.packet,
478 original_raw,
479 ctx.iface,
480 ctx.now,
481 rng,
482 actions,
483 );
484 }
485 }
486
487 fn handle_inbound_local_delivery(
488 &self,
489 ctx: &InboundPacketCtx,
490 actions: &mut Vec<TransportAction>,
491 ) {
492 if (ctx.packet.flags.packet_type == constants::PACKET_TYPE_LINKREQUEST
493 || ctx.packet.flags.packet_type == constants::PACKET_TYPE_DATA)
494 && self
498 .local_destinations
499 .contains_key(&ctx.packet.destination_hash)
500 {
501 let mut delivery_raw = ctx.packet.raw.clone();
502 if ctx.packet.context == constants::CONTEXT_LRRTT && delivery_raw.len() >= 2 {
506 delivery_raw[1] = ctx.packet.hops;
507 }
508 actions.push(TransportAction::DeliverLocal {
509 destination_hash: ctx.packet.destination_hash,
510 raw: PacketBytes::from(delivery_raw),
511 packet_hash: ctx.packet.packet_hash,
512 receiving_interface: ctx.iface,
513 });
514 }
515 }
516
517 fn process_inbound_announce(
522 &mut self,
523 packet: &RawPacket,
524 original_raw: &[u8],
525 iface: InterfaceId,
526 now: f64,
527 rng: &mut dyn Rng,
528 actions: &mut Vec<TransportAction>,
529 ) {
530 if packet.flags.destination_type != constants::DESTINATION_SINGLE {
531 return;
532 }
533
534 let has_ratchet = packet.flags.context_flag == constants::FLAG_SET;
535
536 let announce = match AnnounceData::unpack(packet.data(), has_ratchet) {
538 Ok(a) => a,
539 Err(_) => {
540 actions.push(TransportAction::ProtocolViolation { interface: iface });
541 return;
542 }
543 };
544
545 if self.should_hold_announce(packet, original_raw, iface, now) {
546 return;
547 }
548
549 let sig_cache_key = Self::announce_sig_cache_key(&packet.destination_hash, &announce);
550
551 let validated = if self.announce_sig_cache.contains(&sig_cache_key) {
552 announce.to_validated_with_cached_signature(&packet.destination_hash)
553 } else {
554 announce.validate(&packet.destination_hash).inspect(|_| {
555 self.announce_sig_cache.insert(sig_cache_key, now);
556 })
557 };
558 let validated = match validated {
559 Ok(v) => v,
560 Err(_) => {
561 self.record_invalid_announce(&announce, iface, now, actions);
562 return;
563 }
564 };
565
566 let received_from = self.announce_received_from(packet, now);
567 let random_blob = match extract_random_blob(packet.data()) {
568 Some(b) => b,
569 None => {
570 actions.push(TransportAction::ProtocolViolation { interface: iface });
571 return;
572 }
573 };
574 let announce_emitted = timebase_from_random_blob(&random_blob);
575
576 self.process_verified_announce(
577 VerifiedAnnounceCtx {
578 packet,
579 original_raw,
580 iface,
581 now,
582 validated,
583 received_from,
584 random_blob,
585 announce_emitted,
586 },
587 rng,
588 actions,
589 );
590 }
591
592 fn announce_raw_for_local_clients(&self, packet: &RawPacket) -> PacketBytes {
593 let Some(identity_hash) = self.config.identity_hash else {
594 return PacketBytes::from(packet.raw.clone());
595 };
596
597 if packet.raw.len() < 2 {
598 return PacketBytes::from(packet.raw.clone());
599 }
600
601 let payload_start = if packet.flags.header_type == constants::HEADER_2 {
602 18usize
603 } else {
604 2usize
605 };
606 if packet.raw.len() < payload_start {
607 return PacketBytes::from(packet.raw.clone());
608 }
609
610 let flags = (constants::HEADER_2 << 6)
611 | (constants::TRANSPORT_TRANSPORT << 4)
612 | (packet.raw[0] & 0x0F);
613 let mut raw = Vec::with_capacity(18 + packet.raw.len() - payload_start);
614 raw.push(flags);
615 raw.push(packet.hops);
616 raw.extend_from_slice(&identity_hash);
617 raw.extend_from_slice(&packet.raw[payload_start..]);
618 PacketBytes::from(raw)
619 }
620
621 pub(super) fn announce_sig_cache_key(
624 destination_hash: &[u8; 16],
625 announce: &AnnounceData,
626 ) -> [u8; 32] {
627 announce.signature_cache_key(destination_hash)
628 }
629
630 fn announce_received_from(&mut self, packet: &RawPacket, now: f64) -> [u8; 16] {
631 if let Some(transport_id) = packet.transport_id {
632 if self.config.transport_enabled {
633 if let Some(announce_entry) = self.announce_table.get_mut(&packet.destination_hash)
634 {
635 if packet.hops.checked_sub(1) == Some(announce_entry.hops) {
636 announce_entry.local_rebroadcasts += 1;
637 if announce_entry.retries > 0
638 && announce_entry.local_rebroadcasts
639 >= constants::LOCAL_REBROADCASTS_MAX
640 {
641 self.announce_table.remove(&packet.destination_hash);
642 }
643 }
644 if let Some(announce_entry) = self.announce_table.get(&packet.destination_hash)
645 {
646 if packet.hops.checked_sub(1) == Some(announce_entry.hops + 1)
647 && announce_entry.retries > 0
648 && now < announce_entry.retransmit_timeout
649 {
650 self.announce_table.remove(&packet.destination_hash);
651 }
652 }
653 }
654 }
655 transport_id
656 } else {
657 packet.destination_hash
658 }
659 }
660
661 fn should_hold_announce(
662 &mut self,
663 packet: &RawPacket,
664 original_raw: &[u8],
665 iface: InterfaceId,
666 now: f64,
667 ) -> bool {
668 if self.has_path(&packet.destination_hash) {
669 return false;
670 }
671 if self
672 .discovery_path_requests
673 .contains_key(&packet.destination_hash)
674 {
675 return false;
676 }
677 let Some(info) = self.interfaces.get(&iface) else {
678 return false;
679 };
680 if packet.context == constants::CONTEXT_PATH_RESPONSE
681 || !self.ingress_control.should_ingress_limit(
682 iface,
683 &info.ingress_control,
684 info.ia_freq,
685 info.started,
686 now,
687 )
688 {
689 return false;
690 }
691 self.ingress_control.hold_announce(
692 iface,
693 &info.ingress_control,
694 packet.destination_hash,
695 ingress_control::HeldAnnounce {
696 raw: original_raw.to_vec(),
697 hops: packet.hops,
698 receiving_interface: iface,
699 rx: RxMetadata {
700 rssi: packet.rssi,
701 snr: packet.snr,
702 },
703 timestamp: now,
704 },
705 );
706 true
707 }
708
709 fn try_enqueue_announce(
710 &mut self,
711 ctx: &InboundPacketCtx,
712 rng: &mut dyn Rng,
713 announce_queue: &mut AnnounceVerifyQueue,
714 actions: &mut Vec<TransportAction>,
715 ) {
716 if ctx.packet.flags.destination_type != constants::DESTINATION_SINGLE {
717 return;
718 }
719
720 let has_ratchet = ctx.packet.flags.context_flag == constants::FLAG_SET;
721 let announce = match AnnounceData::unpack(ctx.packet.data(), has_ratchet) {
722 Ok(a) => a,
723 Err(_) => {
724 actions.push(TransportAction::ProtocolViolation {
725 interface: ctx.iface,
726 });
727 return;
728 }
729 };
730
731 let received_from = self.announce_received_from(&ctx.packet, ctx.now);
732
733 if self
736 .local_destinations
737 .contains_key(&ctx.packet.destination_hash)
738 {
739 log::trace!(target: crate::logging::PATHING_LOG_TARGET,
740 "Announce:skipping local destination {:02x}{:02x}{:02x}{:02x}..",
741 ctx.packet.destination_hash[0],
742 ctx.packet.destination_hash[1],
743 ctx.packet.destination_hash[2],
744 ctx.packet.destination_hash[3],
745 );
746 return;
747 }
748
749 let original_raw = ctx
750 .original_raw
751 .as_deref()
752 .expect("announce packets retain original raw bytes");
753 if self.should_hold_announce(&ctx.packet, original_raw, ctx.iface, ctx.now) {
754 return;
755 }
756
757 let sig_cache_key = Self::announce_sig_cache_key(&ctx.packet.destination_hash, &announce);
758 if self.announce_sig_cache.contains(&sig_cache_key) {
759 let Ok(validated) =
760 announce.to_validated_with_cached_signature(&ctx.packet.destination_hash)
761 else {
762 self.record_invalid_announce(&announce, ctx.iface, ctx.now, actions);
763 return;
764 };
765 let random_blob = match extract_random_blob(ctx.packet.data()) {
766 Some(b) => b,
767 None => return,
768 };
769 let announce_emitted = timebase_from_random_blob(&random_blob);
770 self.process_verified_announce(
771 VerifiedAnnounceCtx {
772 packet: &ctx.packet,
773 original_raw,
774 iface: ctx.iface,
775 now: ctx.now,
776 validated,
777 received_from,
778 random_blob,
779 announce_emitted,
780 },
781 rng,
782 actions,
783 );
784 return;
785 }
786
787 if ctx.packet.context == constants::CONTEXT_PATH_RESPONSE {
788 let Ok(validated) = announce.validate(&ctx.packet.destination_hash) else {
789 self.record_invalid_announce(&announce, ctx.iface, ctx.now, actions);
790 return;
791 };
792 self.announce_sig_cache.insert(sig_cache_key, ctx.now);
793 let random_blob = match extract_random_blob(ctx.packet.data()) {
794 Some(b) => b,
795 None => return,
796 };
797 let announce_emitted = timebase_from_random_blob(&random_blob);
798 self.process_verified_announce(
799 VerifiedAnnounceCtx {
800 packet: &ctx.packet,
801 original_raw,
802 iface: ctx.iface,
803 now: ctx.now,
804 validated,
805 received_from,
806 random_blob,
807 announce_emitted,
808 },
809 rng,
810 actions,
811 );
812 return;
813 }
814
815 let random_blob = match extract_random_blob(ctx.packet.data()) {
816 Some(b) => b,
817 None => {
818 actions.push(TransportAction::ProtocolViolation {
819 interface: ctx.iface,
820 });
821 return;
822 }
823 };
824 let announce_emitted = timebase_from_random_blob(&random_blob);
825 let key = AnnounceVerifyKey {
826 destination_hash: ctx.packet.destination_hash,
827 random_blob,
828 received_from,
829 };
830 let pending = PendingAnnounce {
831 original_raw: original_raw.to_vec(),
832 packet: ctx.packet.clone(),
833 interface: ctx.iface,
834 received_from,
835 queued_at: ctx.now,
836 best_hops: ctx.packet.hops,
837 emission_ts: announce_emitted,
838 random_blob,
839 };
840 let _ = announce_queue.enqueue(key, pending);
841 }
842
843 pub fn complete_verified_announce(
844 &mut self,
845 pending: PendingAnnounce,
846 validated: crate::announce::ValidatedAnnounce,
847 sig_cache_key: [u8; 32],
848 now: f64,
849 rng: &mut dyn Rng,
850 ) -> Vec<TransportAction> {
851 self.announce_sig_cache.insert(sig_cache_key, now);
852 let mut actions = Vec::new();
853 self.process_verified_announce(
854 VerifiedAnnounceCtx {
855 packet: &pending.packet,
856 original_raw: &pending.original_raw,
857 iface: pending.interface,
858 now,
859 validated,
860 received_from: pending.received_from,
861 random_blob: pending.random_blob,
862 announce_emitted: pending.emission_ts,
863 },
864 rng,
865 &mut actions,
866 );
867 actions
868 }
869
870 pub fn clear_failed_verified_announce(&mut self, _sig_cache_key: [u8; 32], _now: f64) {}
871
872 fn process_verified_announce(
873 &mut self,
874 ctx: VerifiedAnnounceCtx<'_>,
875 rng: &mut dyn Rng,
876 actions: &mut Vec<TransportAction>,
877 ) {
878 if self.is_blackholed(&ctx.validated.identity_hash, ctx.now) {
879 return;
880 }
881 if ctx.packet.hops > constants::PATHFINDER_M {
882 return;
883 }
884
885 let existing_set = self.path_table.get(&ctx.packet.destination_hash);
886 let was_unknown_destination = existing_set.is_none_or(|ps| ps.is_empty());
887
888 if was_unknown_destination {
891 self.path_states.remove(&ctx.packet.destination_hash);
892 }
893
894 let is_unresponsive = self.path_is_unresponsive(&ctx.packet.destination_hash);
896
897 let current_gravity = existing_set
898 .and_then(|path_set| path_set.primary())
899 .and_then(|path| self.interfaces.get(&path.receiving_interface))
900 .map(|interface| interface.gravity);
901 let announce_gravity = self
902 .interfaces
903 .get(&ctx.iface)
904 .map(|interface| interface.gravity);
905 let higher_gravity_replacement = existing_set.is_some_and(|path_set| {
906 pathfinder::is_higher_gravity_replacement(
907 path_set,
908 ctx.packet.hops,
909 ctx.announce_emitted,
910 current_gravity,
911 announce_gravity,
912 )
913 });
914 let mp_decision = pathfinder::decide_announce_multipath_with_gravity(
915 existing_set,
916 ctx.packet.hops,
917 ctx.announce_emitted,
918 &ctx.random_blob,
919 &ctx.received_from,
920 is_unresponsive,
921 ctx.now,
922 self.config.prefer_shorter_path,
923 current_gravity,
924 announce_gravity,
925 );
926
927 if mp_decision == MultiPathDecision::Reject {
928 log::trace!(target: crate::logging::PATHING_LOG_TARGET,
929 "Announce:path decision REJECT for dest={:02x}{:02x}{:02x}{:02x}..",
930 ctx.packet.destination_hash[0],
931 ctx.packet.destination_hash[1],
932 ctx.packet.destination_hash[2],
933 ctx.packet.destination_hash[3],
934 );
935 return;
936 }
937 if higher_gravity_replacement {
938 log::log!(
939 target: crate::logging::PATHING_LOG_TARGET,
940 crate::logging::GRAVITY_UPDATE_LOG_LEVEL,
941 "Replacing path table entry for {:02x}{:02x}{:02x}{:02x}.. due to higher gravity ({:?}->{:?})",
942 ctx.packet.destination_hash[0],
943 ctx.packet.destination_hash[1],
944 ctx.packet.destination_hash[2],
945 ctx.packet.destination_hash[3],
946 current_gravity,
947 announce_gravity,
948 );
949 }
950
951 let rate_blocked = if ctx.packet.context != constants::CONTEXT_PATH_RESPONSE {
953 if let Some(iface_info) = self.interfaces.get(&ctx.iface) {
954 self.rate_limiter.check_and_update(
955 &ctx.packet.destination_hash,
956 ctx.now,
957 iface_info.announce_rate_target,
958 iface_info.announce_rate_grace,
959 iface_info.announce_rate_penalty,
960 )
961 } else {
962 false
963 }
964 } else {
965 false
966 };
967
968 let interface_mode = self
970 .interfaces
971 .get(&ctx.iface)
972 .map(|i| i.mode)
973 .unwrap_or(constants::MODE_FULL);
974
975 let expires = compute_path_expires(ctx.now, interface_mode);
976
977 let existing_blobs = self
979 .path_table
980 .get(&ctx.packet.destination_hash)
981 .and_then(|ps| ps.find_by_next_hop(&ctx.received_from))
982 .map(|e| e.random_blobs.clone())
983 .unwrap_or_default();
984
985 let mut rng_bytes = [0u8; 8];
987 rng.fill_bytes(&mut rng_bytes);
988 let rng_value = (u64::from_le_bytes(rng_bytes) as f64) / (u64::MAX as f64);
989
990 let is_path_response = ctx.packet.context == constants::CONTEXT_PATH_RESPONSE;
991
992 let (path_entry, announce_entry) = announce_proc::process_validated_announce(
993 ctx.packet.destination_hash,
994 ctx.packet.hops,
995 ctx.packet.data(),
996 &ctx.packet.raw,
997 ctx.packet.packet_hash,
998 ctx.packet.flags.context_flag,
999 ctx.received_from,
1000 ctx.iface,
1001 ctx.now,
1002 existing_blobs,
1003 ctx.random_blob,
1004 expires,
1005 rng_value,
1006 self.config.transport_enabled,
1007 is_path_response,
1008 rate_blocked,
1009 Some(ctx.original_raw.to_vec()),
1010 );
1011
1012 actions.push(TransportAction::CacheAnnounce {
1014 packet_hash: ctx.packet.packet_hash,
1015 raw: ctx.original_raw.to_vec().into(),
1016 });
1017
1018 match mp_decision {
1020 MultiPathDecision::ReplacePrimary => self.upsert_primary_path_destination(
1021 ctx.packet.destination_hash,
1022 path_entry,
1023 ctx.now,
1024 ),
1025 MultiPathDecision::AddAlternative => {
1026 self.upsert_path_destination(ctx.packet.destination_hash, path_entry, ctx.now)
1027 }
1028 MultiPathDecision::Reject => unreachable!("rejected decisions returned above"),
1029 }
1030
1031 if let Some(tunnel_id) = self.interfaces.get(&ctx.iface).and_then(|i| i.tunnel_id) {
1033 let blobs = self
1034 .path_table
1035 .get(&ctx.packet.destination_hash)
1036 .and_then(|ps| ps.find_by_next_hop(&ctx.received_from))
1037 .map(|e| e.random_blobs.clone())
1038 .unwrap_or_default();
1039 self.tunnel_table.store_tunnel_path(
1040 &tunnel_id,
1041 ctx.packet.destination_hash,
1042 tunnel::TunnelPath {
1043 timestamp: ctx.now,
1044 received_from: ctx.received_from,
1045 hops: ctx.packet.hops,
1046 expires,
1047 random_blobs: blobs,
1048 packet_hash: ctx.packet.packet_hash,
1049 },
1050 ctx.now,
1051 self.config.destination_timeout_secs,
1052 self.config.max_tunnel_destinations_total,
1053 );
1054 }
1055
1056 self.path_states.remove(&ctx.packet.destination_hash);
1059
1060 if let Some(ann) = announce_entry {
1062 self.insert_announce_entry(ctx.packet.destination_hash, ann, ctx.now);
1063 }
1064
1065 actions.push(TransportAction::AnnounceReceived(alloc::boxed::Box::new(
1067 types::ReceivedAnnounce {
1068 destination_hash: ctx.packet.destination_hash,
1069 identity_hash: ctx.validated.identity_hash,
1070 public_key: ctx.validated.public_key,
1071 name_hash: ctx.validated.name_hash,
1072 random_hash: ctx.validated.random_hash,
1073 ratchet: ctx.validated.ratchet,
1074 app_data: ctx.validated.app_data,
1075 hops: ctx.packet.hops,
1076 receiving_interface: ctx.iface,
1077 rx: RxMetadata {
1078 rssi: ctx.packet.rssi,
1079 snr: ctx.packet.snr,
1080 },
1081 },
1082 )));
1083
1084 actions.push(TransportAction::PathUpdated {
1085 destination_hash: ctx.packet.destination_hash,
1086 hops: ctx.packet.hops,
1087 next_hop: ctx.received_from,
1088 interface: ctx.iface,
1089 });
1090
1091 if self.has_local_clients() {
1093 actions.push(TransportAction::ForwardToLocalClients {
1094 raw: self.announce_raw_for_local_clients(ctx.packet),
1095 exclude: Some(ctx.iface),
1096 });
1097 }
1098
1099 if let Some(requesting_interfaces) =
1101 self.discovery_path_requests_waiting(&ctx.packet.destination_hash)
1102 {
1103 let entry = AnnounceEntry {
1105 timestamp: ctx.now,
1106 retransmit_timeout: ctx.now,
1107 retries: constants::PATHFINDER_R,
1108 received_from: ctx.received_from,
1109 hops: ctx.packet.hops,
1110 packet_raw: ctx.packet.raw.clone(),
1111 packet_data: ctx.packet.data().to_vec(),
1112 destination_hash: ctx.packet.destination_hash,
1113 context_flag: ctx.packet.flags.context_flag,
1114 local_rebroadcasts: 0,
1115 block_rebroadcasts: true,
1116 attached_interface: requesting_interfaces.first().copied(),
1117 };
1118 if let Some(identity_hash) = self.config.identity_hash {
1119 let raw = announce_proc::build_retransmit_announce(&entry, &identity_hash);
1120 for interface in requesting_interfaces.iter().skip(1) {
1121 actions.push(TransportAction::SendOnInterface {
1122 interface: *interface,
1123 raw: raw.clone().into(),
1124 });
1125 }
1126 }
1127 self.insert_announce_entry(ctx.packet.destination_hash, entry, ctx.now);
1128 }
1129 }
1130
1131 pub fn announce_sig_cache_contains(&self, sig_cache_key: &[u8; 32]) -> bool {
1132 self.announce_sig_cache.contains(sig_cache_key)
1133 }
1134
1135 pub(super) fn discovery_path_requests_waiting(
1138 &mut self,
1139 dest_hash: &[u8; 16],
1140 ) -> Option<Vec<InterfaceId>> {
1141 let request = self
1142 .discovery_path_requests
1143 .remove(dest_hash)
1144 .map(|req| req.requesting_interfaces);
1145 self.discovery_path_request_deadlines.remove(dest_hash);
1146 request
1147 }
1148
1149 fn process_inbound_proof(
1154 &mut self,
1155 ctx: &InboundPacketCtx,
1156 actions: &mut Vec<TransportAction>,
1157 ) {
1158 let packet = &ctx.packet;
1159 if packet.context == constants::CONTEXT_LRPROOF {
1160 if (self.config.transport_enabled)
1162 && self.link_table.contains_key(&packet.destination_hash)
1163 {
1164 let link_entry = self.link_table.get(&packet.destination_hash).cloned();
1165 if let Some(entry) = link_entry {
1166 let instance_local_link = self
1167 .interface_is_local_client(entry.next_hop_interface)
1168 && self.interface_is_local_client(entry.received_interface);
1169 if let Some((outbound_interface, new_raw)) = route_via_link_table(
1170 packet,
1171 &entry,
1172 ctx.iface,
1173 LocalHopRewrite {
1174 local_hops_delta: self.config.local_hops_delta,
1175 from_local_client: ctx.from_local_client,
1176 skip_local_hops_delta: instance_local_link,
1177 },
1178 ) {
1179 if let Some(le) = self.link_table.get_mut(&packet.destination_hash) {
1184 le.validated = true;
1185 }
1186
1187 actions.push(TransportAction::LinkEstablished {
1188 link_id: packet.destination_hash,
1189 interface: outbound_interface,
1190 });
1191
1192 actions.push(TransportAction::SendOnInterface {
1193 interface: outbound_interface,
1194 raw: new_raw.into(),
1195 });
1196 } else if link_route_hops_match(packet.hops, &entry, ctx.iface) {
1197 log::debug!(
1198 "Link request proof received on wrong interface {}, not transporting it (expected {} or {})",
1199 ctx.iface.0,
1200 entry.next_hop_interface.0,
1201 entry.received_interface.0,
1202 );
1203 } else {
1204 log::debug!("{}", lrproof_hop_mismatch_diagnostic(packet.hops, &entry));
1205 }
1206 }
1207 } else {
1208 let mut delivery_raw = packet.raw.clone();
1210 if delivery_raw.len() >= 2 {
1213 delivery_raw[1] = packet.hops;
1214 }
1215 actions.push(TransportAction::DeliverLocal {
1216 destination_hash: packet.destination_hash,
1217 raw: PacketBytes::from(delivery_raw),
1218 packet_hash: packet.packet_hash,
1219 receiving_interface: ctx.iface,
1220 });
1221 }
1222 } else {
1223 if self.config.transport_enabled {
1225 if let Some(reverse_entry) = self.reverse_table.remove(&packet.destination_hash) {
1226 let proof_for_local_client =
1227 self.interface_is_local_client(reverse_entry.receiving_interface);
1228 if let Some(action) = route_proof_via_reverse(
1229 packet,
1230 &reverse_entry,
1231 ctx.iface,
1232 LocalHopRewrite {
1233 local_hops_delta: self.config.local_hops_delta,
1234 from_local_client: ctx.from_local_client,
1235 skip_local_hops_delta: proof_for_local_client,
1236 },
1237 ) {
1238 actions.push(action);
1239 }
1240 }
1241 }
1242
1243 actions.push(TransportAction::DeliverLocal {
1245 destination_hash: packet.destination_hash,
1246 raw: PacketBytes::from(packet.raw.clone()),
1247 packet_hash: packet.packet_hash,
1248 receiving_interface: ctx.iface,
1249 });
1250 }
1251 }
1252}