1use crate::control::queries;
4use crate::control::{ControlSocket, commands};
5use crate::discovery::is_punch_packet;
6use crate::node::decrypt_worker::{
7 DecryptFailureReport, DecryptFallback, DecryptJobBatcher, DecryptWorkerEvent,
8 DecryptWorkerFallbackReceivers,
9};
10use crate::node::handlers::encrypted::EncryptedFrameFastPath;
11use crate::node::wire::{
12 COMMON_PREFIX_SIZE, CommonPrefix, FMP_VERSION, PHASE_ESTABLISHED, PHASE_MSG1, PHASE_MSG2,
13};
14use crate::node::{AuthenticatedFmpPlaintext, Node, NodeEndpointCommand, NodeError};
15use crate::transport::PacketRx;
16use crate::transport::ReceivedPacket;
17use crate::upper::tun::TunOutboundRx;
18use std::time::{Duration, Instant};
19use tokio::sync::mpsc::Receiver;
20use tracing::{debug, info, trace, warn};
21
22mod budget;
23mod drain;
24
25#[cfg(test)]
26mod tests;
27
28use budget::*;
29use drain::*;
30
31impl Node {
32 pub async fn run_rx_loop(&mut self) -> Result<(), NodeError> {
52 let mut packet_rx = self.packet_rx.take().ok_or(NodeError::NotStarted)?;
53
54 let (mut tun_outbound_rx, _tun_guard) = match self.tun_outbound_rx.take() {
58 Some(rx) => (rx, None),
59 None => {
60 let (tx, rx) = tokio::sync::mpsc::channel(1);
61 (rx, Some(tx))
62 }
63 };
64
65 let (mut dns_identity_rx, _dns_guard) = match self.dns_identity_rx.take() {
68 Some(rx) => (rx, None),
69 None => {
70 let (tx, rx) = tokio::sync::mpsc::channel(1);
71 (rx, Some(tx))
72 }
73 };
74
75 let (mut endpoint_priority_command_rx, _endpoint_priority_command_guard) =
78 match self.endpoint_priority_command_rx.take() {
79 Some(rx) => (rx, None),
80 None => {
81 let (tx, rx) = tokio::sync::mpsc::channel(1);
82 (rx, Some(tx))
83 }
84 };
85 let (mut endpoint_command_rx, _endpoint_command_guard) =
86 match self.endpoint_command_rx.take() {
87 Some(rx) => (rx, None),
88 None => {
89 let (tx, rx) = tokio::sync::mpsc::channel(1);
90 (rx, Some(tx))
91 }
92 };
93
94 let (mut decrypt_fallback_rx, _decrypt_fallback_guard) =
98 match self.decrypt_fallback_rx.take() {
99 Some(rx) => (rx, None),
100 None => {
101 let (tx, rx) = crate::node::decrypt_worker::decrypt_worker_fallback_channels();
102 (rx, Some(tx))
103 }
104 };
105
106 let mut tick =
107 tokio::time::interval(Duration::from_secs(self.config.node.tick_interval_secs));
108 tick.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
109 let mut maintenance_state = RxLoopMaintenanceState::default();
110
111 let (control_tx, mut control_rx) =
113 tokio::sync::mpsc::channel::<crate::control::ControlMessage>(32);
114
115 if self.config.node.control.enabled {
116 let config = self.config.node.control.clone();
117 let tx = control_tx.clone();
118 tokio::spawn(async move {
119 match ControlSocket::bind(&config) {
120 Ok(socket) => {
121 socket.accept_loop(tx).await;
122 }
123 Err(e) => {
124 warn!(error = %e, "Failed to bind control socket");
125 }
126 }
127 });
128 }
129 drop(control_tx);
131
132 info!("RX event loop started");
133 crate::perf_profile::maybe_spawn_reporter();
135 tick.tick().await;
139
140 loop {
141 tokio::select! {
142 biased;
143 Some(event) = decrypt_fallback_rx.priority.recv() => {
154 let fallback_drained = self.drain_decrypt_priority_fallback(
155 &mut decrypt_fallback_rx.priority,
156 Some(event),
157 PACKET_DRAIN_BUDGET,
158 ).await;
159 let side_drained = self.drain_rx_loop_side_queues(
160 &mut tun_outbound_rx,
161 &mut endpoint_priority_command_rx,
162 &mut endpoint_command_rx,
163 SIDE_QUEUE_INTERLEAVE_BUDGET,
164 ).await;
165 if fallback_drained > 0 || side_drained.has_drained() {
166 maintenance_state.record_data_activity(Instant::now());
167 }
168 }
169 _ = tick.tick() => {
175 let drained = self.drain_rx_loop_data_queues(
176 &mut packet_rx,
177 &mut decrypt_fallback_rx,
178 &mut tun_outbound_rx,
179 &mut endpoint_priority_command_rx,
180 &mut endpoint_command_rx,
181 NON_PACKET_DRAIN_BUDGET,
182 ).await;
183 if drained.has_drained() {
184 maintenance_state.record_data_activity(Instant::now());
185 debug!(
186 drained = drained.total(),
187 drained_packets = drained.packets,
188 drained_tun = drained.tun,
189 drained_endpoint = drained.endpoint,
190 "Drained queued packets before rx-loop maintenance"
191 );
192 }
193 let maintenance_plan = maintenance_state.plan_maintenance(
194 drained,
195 Instant::now(),
196 RX_LOOP_RECENT_DATA_ACTIVITY_WINDOW,
197 RX_LOOP_SLOW_MAINTENANCE_IDLE_TIMEOUT,
198 RX_LOOP_SLOW_MAINTENANCE_BUSY_TIMEOUT,
199 );
200
201 let slow_timed_out = self.run_rx_loop_maintenance_tick(
202 maintenance_plan,
203 ).await;
204 maintenance_state.record_maintenance_result(
205 maintenance_plan.data_pressure(),
206 slow_timed_out,
207 );
208
209 let post_drained = self.drain_rx_loop_data_queues(
210 &mut packet_rx,
211 &mut decrypt_fallback_rx,
212 &mut tun_outbound_rx,
213 &mut endpoint_priority_command_rx,
214 &mut endpoint_command_rx,
215 PACKET_DRAIN_BUDGET,
216 ).await;
217 if post_drained.has_drained() {
218 maintenance_state.record_data_activity(Instant::now());
219 debug!(
220 drained = post_drained.total(),
221 drained_packets = post_drained.packets,
222 drained_tun = post_drained.tun,
223 drained_endpoint = post_drained.endpoint,
224 "Drained queued packets after rx-loop maintenance"
225 );
226 }
227 }
228 Some(event) = decrypt_fallback_rx.authenticated_bulk.recv(),
229 if authenticated_bulk_preempts_packet_rx(packet_rx.priority_ready_packets()) =>
230 {
231 let fallback_drained = self.drain_decrypt_fallback(
232 &mut decrypt_fallback_rx,
233 None,
234 Some(event),
235 None,
236 NON_PACKET_DRAIN_BUDGET,
237 ).await;
238 let side_drained = self.drain_rx_loop_side_queues(
239 &mut tun_outbound_rx,
240 &mut endpoint_priority_command_rx,
241 &mut endpoint_command_rx,
242 SIDE_QUEUE_INTERLEAVE_BUDGET,
243 ).await;
244 if fallback_drained > 0 || side_drained.has_drained() {
245 maintenance_state.record_data_activity(Instant::now());
246 }
247 }
248 packet = packet_rx.recv() => {
249 match packet {
250 Some(p) => {
251 let drained = self.drain_packet_rx(
252 &mut packet_rx,
253 &mut decrypt_fallback_rx,
254 Some(RxLoopSideQueues {
255 tun_outbound_rx: &mut tun_outbound_rx,
256 endpoint_priority_command_rx: &mut endpoint_priority_command_rx,
257 endpoint_command_rx: &mut endpoint_command_rx,
258 }),
259 Some(p),
260 PACKET_DRAIN_BUDGET,
261 ).await;
262 if drained > 0 {
263 maintenance_state.record_data_activity(Instant::now());
264 }
265 }
266 None => break, }
268 }
269 Some(command) = endpoint_priority_command_rx.recv() => {
270 let drained = self.drain_endpoint_commands(
271 &mut endpoint_priority_command_rx,
272 &mut endpoint_command_rx,
273 Some(command),
274 None,
275 NON_PACKET_DRAIN_BUDGET,
276 ).await;
277 if drained > 0 {
278 maintenance_state.record_data_activity(Instant::now());
279 }
280 }
281 Some(event) = decrypt_fallback_rx.bulk.recv() => {
282 let fallback_plan = fallback_drain_plan(
283 packet_rx.priority_ready_packets(),
284 decrypt_fallback_rx.bulk_queued_packets(),
285 );
286 let fallback_drained = self.drain_decrypt_fallback(
287 &mut decrypt_fallback_rx,
288 None,
289 None,
290 Some(event),
291 fallback_plan.trailing_budget,
292 ).await;
293 let side_drained = self.drain_rx_loop_side_queues(
294 &mut tun_outbound_rx,
295 &mut endpoint_priority_command_rx,
296 &mut endpoint_command_rx,
297 SIDE_QUEUE_INTERLEAVE_BUDGET,
298 ).await;
299 if fallback_drained > 0 || side_drained.has_drained() {
300 maintenance_state.record_data_activity(Instant::now());
301 }
302 }
303 Some(ipv6_packet) = tun_outbound_rx.recv() => {
304 let drained = self.drain_tun_outbound(
305 &mut tun_outbound_rx,
306 Some(ipv6_packet),
307 NON_PACKET_DRAIN_BUDGET,
308 ).await;
309 if drained > 0 {
310 maintenance_state.record_data_activity(Instant::now());
311 }
312 }
313 Some(identity) = dns_identity_rx.recv() => {
314 debug!(
315 node_addr = %identity.node_addr,
316 "Registering identity from DNS resolution"
317 );
318 self.register_identity(identity.node_addr, identity.pubkey);
319 }
320 Some(command) = endpoint_command_rx.recv() => {
321 let drained = self.drain_endpoint_commands(
322 &mut endpoint_priority_command_rx,
323 &mut endpoint_command_rx,
324 None,
325 Some(command),
326 NON_PACKET_DRAIN_BUDGET,
327 ).await;
328 if drained > 0 {
329 maintenance_state.record_data_activity(Instant::now());
330 }
331 }
332 Some((request, response_tx)) = control_rx.recv() => {
333 let response = if request.command.starts_with("show_") {
334 queries::dispatch(self, &request.command, request.params.as_ref())
335 } else {
336 commands::dispatch(
337 self,
338 &request.command,
339 request.params.as_ref(),
340 ).await
341 };
342 let _ = response_tx.send(response);
343 }
344 }
345 }
346
347 info!("RX event loop stopped (channel closed)");
348 Ok(())
349 }
350
351 async fn drain_rx_loop_data_queues(
352 &mut self,
353 packet_rx: &mut PacketRx,
354 decrypt_fallback_rx: &mut DecryptWorkerFallbackReceivers,
355 tun_outbound_rx: &mut TunOutboundRx,
356 endpoint_priority_command_rx: &mut Receiver<NodeEndpointCommand>,
357 endpoint_command_rx: &mut Receiver<NodeEndpointCommand>,
358 budget: usize,
359 ) -> RxLoopDataDrainStats {
360 let drained_packets = self
361 .drain_packet_rx(packet_rx, decrypt_fallback_rx, None, None, budget)
362 .await;
363 let non_packet_budget = non_packet_drain_budget(budget);
364 let drained_tun = self
365 .drain_tun_outbound(tun_outbound_rx, None, non_packet_budget)
366 .await;
367 let drained_endpoint = self
368 .drain_endpoint_commands(
369 endpoint_priority_command_rx,
370 endpoint_command_rx,
371 None,
372 None,
373 non_packet_budget,
374 )
375 .await;
376 RxLoopDataDrainStats::new(drained_packets, drained_tun, drained_endpoint)
377 }
378
379 async fn drain_packet_rx(
380 &mut self,
381 packet_rx: &mut PacketRx,
382 decrypt_fallback_rx: &mut DecryptWorkerFallbackReceivers,
383 mut side_queues: Option<RxLoopSideQueues<'_>>,
384 first_packet: Option<ReceivedPacket>,
385 budget: usize,
386 ) -> usize {
387 self.begin_endpoint_event_batch();
393 let side_queue_interleave_every = if side_queues.is_some() {
394 SIDE_QUEUE_INTERLEAVE_EVERY
395 } else {
396 0
397 };
398 let mut fallback_plan = fallback_drain_plan(
399 packet_rx.priority_ready_packets(),
400 decrypt_fallback_rx.bulk_queued_packets(),
401 );
402 let mut drain = PacketDrainCursor::new(
403 first_packet,
404 budget,
405 fallback_plan.interleave_every,
406 side_queue_interleave_every,
407 );
408 let mut decrypt_jobs = DecryptJobBatcher::new();
409 while let Some(action) = drain.next(packet_rx) {
410 match action {
411 PacketDrainAction::Packet(packet) => {
412 let action = self.begin_process_packet(packet);
413 match action {
414 PacketProcessAction::DecryptJob { job } => {
415 if let Some(workers) = self.decrypt_workers.as_ref() {
416 decrypt_jobs.push(workers, job);
417 }
418 }
419 PacketProcessAction::Done => {}
420 action => {
421 self.flush_decrypt_job_batcher(&mut decrypt_jobs);
422 self.finish_packet_process(action).await;
423 }
424 }
425 }
426 PacketDrainAction::InterleaveFallback => {
427 self.flush_decrypt_job_batcher(&mut decrypt_jobs);
428 fallback_plan = fallback_drain_plan(
429 packet_rx.priority_ready_packets(),
430 decrypt_fallback_rx.bulk_queued_packets(),
431 );
432 drain.reset_fallback_interleave_every(fallback_plan.interleave_every);
433 let drained = if decrypt_fallback_has_ready(decrypt_fallback_rx) {
434 self.drain_decrypt_fallback(
435 decrypt_fallback_rx,
436 None,
437 None,
438 None,
439 fallback_plan.interleave_budget,
440 )
441 .await
442 } else {
443 0
444 };
445 if drained == 0 {
446 drain.refund_empty_interleave_turn();
447 }
448 }
449 PacketDrainAction::InterleaveSideQueues => {
450 self.flush_decrypt_job_batcher(&mut decrypt_jobs);
451 let drained = if let Some(side_queues) = side_queues.as_mut() {
452 if rx_loop_side_queues_have_ready(side_queues) {
453 self.drain_rx_loop_side_queues(
454 side_queues.tun_outbound_rx,
455 side_queues.endpoint_priority_command_rx,
456 side_queues.endpoint_command_rx,
457 SIDE_QUEUE_INTERLEAVE_BUDGET,
458 )
459 .await
460 } else {
461 RxLoopDataDrainStats::default()
462 }
463 } else {
464 RxLoopDataDrainStats::default()
465 };
466 if !drained.has_drained() {
467 drain.refund_empty_interleave_turn();
468 }
469 }
470 }
471 }
472
473 self.flush_decrypt_job_batcher(&mut decrypt_jobs);
474 let drained = drain.drained();
475 if drained > 0 {
476 fallback_plan = fallback_drain_plan(
477 packet_rx.priority_ready_packets(),
478 decrypt_fallback_rx.bulk_queued_packets(),
479 );
480 self.drain_decrypt_fallback(
485 decrypt_fallback_rx,
486 None,
487 None,
488 None,
489 fallback_plan.trailing_budget.min(budget),
490 )
491 .await;
492 self.finish_endpoint_event_batch();
493 } else {
494 self.finish_endpoint_event_batch();
495 }
496 drained
497 }
498
499 async fn drain_rx_loop_side_queues(
500 &mut self,
501 tun_outbound_rx: &mut TunOutboundRx,
502 endpoint_priority_command_rx: &mut Receiver<NodeEndpointCommand>,
503 endpoint_command_rx: &mut Receiver<NodeEndpointCommand>,
504 budget: usize,
505 ) -> RxLoopDataDrainStats {
506 let (endpoint_budget, tun_budget) = split_side_queue_budget(budget);
507 let mut drained_endpoint = self
508 .drain_endpoint_commands(
509 endpoint_priority_command_rx,
510 endpoint_command_rx,
511 None,
512 None,
513 endpoint_budget,
514 )
515 .await;
516 let mut drained_tun = self
517 .drain_tun_outbound(tun_outbound_rx, None, tun_budget)
518 .await;
519
520 let endpoint_remainder = remaining_side_queue_budget(endpoint_budget, drained_endpoint);
521 let tun_remainder = remaining_side_queue_budget(tun_budget, drained_tun);
522 if endpoint_remainder > 0 && !tun_outbound_rx.is_empty() {
523 drained_tun += self
524 .drain_tun_outbound(tun_outbound_rx, None, endpoint_remainder)
525 .await;
526 }
527 if tun_remainder > 0
528 && (!endpoint_priority_command_rx.is_empty() || !endpoint_command_rx.is_empty())
529 {
530 drained_endpoint += self
531 .drain_endpoint_commands(
532 endpoint_priority_command_rx,
533 endpoint_command_rx,
534 None,
535 None,
536 tun_remainder,
537 )
538 .await;
539 }
540
541 RxLoopDataDrainStats::new(0, drained_tun, drained_endpoint)
542 }
543
544 async fn drain_tun_outbound(
545 &mut self,
546 tun_outbound_rx: &mut TunOutboundRx,
547 first_packet: Option<Vec<u8>>,
548 budget: usize,
549 ) -> usize {
550 let mut drain = SingleLaneDrainCursor::new(first_packet, budget);
551 while let Some(packet) = drain.next(tun_outbound_rx) {
552 self.handle_tun_outbound(packet).await;
553 }
554
555 drain.drained()
556 }
557
558 async fn drain_endpoint_commands(
559 &mut self,
560 endpoint_priority_command_rx: &mut Receiver<NodeEndpointCommand>,
561 endpoint_command_rx: &mut Receiver<NodeEndpointCommand>,
562 first_priority_command: Option<NodeEndpointCommand>,
563 first_bulk_command: Option<NodeEndpointCommand>,
564 budget: usize,
565 ) -> usize {
566 let mut drain =
567 PriorityBulkDrainCursor::new(first_priority_command, first_bulk_command, budget);
568 while let Some(command) = drain.next(endpoint_priority_command_rx, endpoint_command_rx) {
569 let drain_cost = command.drain_cost();
570 self.handle_endpoint_data_command(command).await;
571 drain.charge_extra(drain_cost.saturating_sub(1));
572 }
573
574 drain.drained()
575 }
576
577 async fn run_rx_loop_maintenance_tick(&mut self, plan: RxLoopMaintenancePlan) -> bool {
578 self.check_timeouts();
579 let now_ms = Self::now_ms();
580 self.check_link_heartbeats().await;
584 self.reload_peer_acl();
585 self.resend_pending_handshakes(now_ms).await;
586 self.resend_pending_rekeys(now_ms).await;
587 self.resend_pending_session_handshakes(now_ms).await;
588 self.resend_pending_session_msg3(now_ms).await;
589 self.purge_idle_sessions(now_ms);
590 self.purge_learned_routes(now_ms);
591 self.check_mmp_reports().await;
592 self.check_session_mmp_reports().await;
593 self.check_rekey().await;
594 self.check_session_rekey().await;
595 self.check_pending_lookups(now_ms).await;
596 self.poll_pending_connects().await;
597 self.process_pending_retries(now_ms).await;
598 self.poll_transport_discovery().await;
599 self.activate_connected_udp_sessions().await;
600 self.sample_transport_congestion();
601
602 let Some(slow_timeout) = plan.slow_timeout() else {
603 crate::perf_profile::record_event(
604 crate::perf_profile::Event::RxLoopSlowMaintenanceSkipped,
605 );
606 return false;
607 };
608
609 if tokio::time::timeout(slow_timeout, self.run_rx_loop_slow_maintenance_tick())
610 .await
611 .is_err()
612 {
613 crate::perf_profile::record_event(
614 crate::perf_profile::Event::RxLoopSlowMaintenanceTimeout,
615 );
616 self.mark_rx_loop_maintenance_timeout();
617 warn!(
618 timeout_ms = slow_timeout.as_millis() as u64,
619 data_pressure = plan.data_pressure(),
620 "RX loop slow maintenance timed out; continuing packet processing"
621 );
622 return true;
623 }
624 false
625 }
626
627 async fn run_rx_loop_slow_maintenance_tick(&mut self) {
628 if let Some(delay) = rx_loop_slow_maintenance_fault_delay() {
629 tokio::time::sleep(delay).await;
630 }
631
632 self.poll_nostr_discovery().await;
637 self.poll_lan_discovery().await;
638 self.poll_local_instance_discovery().await;
639 self.check_tree_state().await;
640 self.check_bloom_state().await;
641 self.compute_mesh_size();
642 self.record_stats_history();
643 }
644
645 async fn process_decrypt_worker_event(&mut self, event: DecryptWorkerEvent) {
650 event.record_queue_wait();
651 match event {
652 DecryptWorkerEvent::Plaintext(fallback) => {
653 self.process_decrypt_fallback(fallback).await;
654 }
655 DecryptWorkerEvent::PlaintextBatch(fallbacks) => {
656 for fallback in fallbacks {
657 self.process_decrypt_fallback(fallback).await;
658 }
659 }
660 DecryptWorkerEvent::AuthenticatedFmpReceive(receive) => {
661 self.process_authenticated_fmp_receive_from_worker(receive);
662 }
663 DecryptWorkerEvent::AuthenticatedSession(session) => {
664 self.process_authenticated_session_from_worker(session)
665 .await;
666 }
667 DecryptWorkerEvent::DirectSessionCommit(commit) => {
668 self.process_direct_session_commit_from_worker(commit).await;
669 }
670 DecryptWorkerEvent::DirectSessionCommitBatch(commits) => {
671 for commit in commits {
672 self.process_direct_session_commit_from_worker(commit).await;
673 }
674 }
675 DecryptWorkerEvent::DirectSessionData(direct) => {
676 self.process_direct_session_data_from_worker(direct).await;
677 }
678 DecryptWorkerEvent::FspDecryptFailure(report) => {
679 self.process_fsp_decrypt_failure_from_worker(report).await;
680 }
681 DecryptWorkerEvent::DecryptFailure(report) => {
682 self.process_decrypt_failure_report(report).await;
683 }
684 }
685 }
686
687 async fn process_decrypt_fallback(&mut self, fallback: DecryptFallback) {
688 let plaintext = &fallback.packet_data[fallback.fmp_plaintext_offset
689 ..fallback.fmp_plaintext_offset + fallback.fmp_plaintext_len];
690 self.process_authentic_fmp_plaintext(AuthenticatedFmpPlaintext::new(
691 fallback.source_peer,
692 fallback.transport_id,
693 &fallback.remote_addr,
694 fallback.timestamp_ms,
695 fallback.packet_len,
696 fallback.fmp_counter,
697 fallback.fmp_flags,
698 plaintext,
699 ))
700 .await;
701 }
702
703 async fn process_decrypt_failure_report(&mut self, report: DecryptFailureReport) {
704 debug!(
705 peer = %self.peer_display_name(report.source_peer.node_addr()),
706 counter = report.fmp_counter,
707 replay_highest = report.fmp_replay_highest,
708 "Worker FMP AEAD decryption failed"
709 );
710 self.handle_decrypt_failure_report(&report).await;
711 }
712
713 async fn drain_decrypt_priority_fallback(
720 &mut self,
721 priority_rx: &mut Receiver<DecryptWorkerEvent>,
722 first_event: Option<DecryptWorkerEvent>,
723 budget: usize,
724 ) -> usize {
725 self.begin_endpoint_event_batch();
726 let mut drain = SingleLaneDrainCursor::new(first_event, budget);
727 while let Some(event) = drain.next(priority_rx) {
728 self.process_decrypt_worker_event(event).await;
729 }
730 let drained = drain.drained();
731 self.finish_endpoint_event_batch();
732 drained
733 }
734
735 async fn drain_decrypt_fallback(
741 &mut self,
742 rx: &mut DecryptWorkerFallbackReceivers,
743 first_priority_event: Option<DecryptWorkerEvent>,
744 first_authenticated_bulk_event: Option<DecryptWorkerEvent>,
745 first_bulk_event: Option<DecryptWorkerEvent>,
746 budget: usize,
747 ) -> usize {
748 self.begin_endpoint_event_batch();
749 let mut drain = DecryptReturnDrainCursor::new(
750 first_priority_event,
751 first_authenticated_bulk_event,
752 first_bulk_event,
753 budget,
754 );
755 while let Some(event) =
756 drain.next(&mut rx.priority, &mut rx.authenticated_bulk, &mut rx.bulk)
757 {
758 rx.release_dequeued_event(&event);
759 let extra = event.packet_count().saturating_sub(1);
760 self.process_decrypt_worker_event(event).await;
761 drain.charge_extra(extra);
762 }
763 let drained = drain.drained();
764 self.finish_endpoint_event_batch();
765 drained
766 }
767
768 #[cfg(test)]
772 pub(in crate::node) async fn process_packet(&mut self, packet: ReceivedPacket) {
773 let action = self.begin_process_packet(packet);
774 self.finish_packet_process(action).await;
775 }
776
777 fn begin_process_packet(&mut self, packet: ReceivedPacket) -> PacketProcessAction {
778 let timer = crate::perf_profile::Timer::start(crate::perf_profile::Stage::ProcessPacket);
779 let priority_sized = packet.is_priority_sized();
780 let priority_count = u64::from(priority_sized);
781 let bulk_count = u64::from(!priority_sized);
782 crate::perf_profile::record_since_split_count(
783 crate::perf_profile::Stage::TransportQueueWait,
784 crate::perf_profile::Stage::TransportPriorityQueueWait,
785 crate::perf_profile::Stage::TransportBulkQueueWait,
786 packet.trace_enqueued_at,
787 1,
788 priority_count,
789 bulk_count,
790 );
791 crate::perf_profile::record_since_split_count(
792 crate::perf_profile::Stage::TransportRxLoopWait,
793 crate::perf_profile::Stage::TransportPriorityRxLoopWait,
794 crate::perf_profile::Stage::TransportBulkRxLoopWait,
795 packet.trace_rx_loop_owned_at,
796 1,
797 priority_count,
798 bulk_count,
799 );
800 if is_punch_packet(&packet.data) {
801 trace!(
802 transport_id = %packet.transport_id,
803 remote_addr = %packet.remote_addr,
804 bytes = packet.data.len(),
805 "Dropping stray punch probe/ack in FMP rx loop"
806 );
807 return PacketProcessAction::Done;
808 }
809 if packet.data.len() < COMMON_PREFIX_SIZE {
810 return PacketProcessAction::Done; }
812
813 let prefix = match CommonPrefix::parse(&packet.data) {
814 Some(p) => p,
815 None => return PacketProcessAction::Done, };
817 if matches!(prefix.phase, PHASE_MSG1 | PHASE_MSG2) {
818 debug!(
819 transport_id = %packet.transport_id,
820 remote_addr = %packet.remote_addr,
821 bytes = packet.data.len(),
822 phase = prefix.phase,
823 version = prefix.version,
824 "FMP handshake packet dispatch"
825 );
826 } else {
827 trace!(
828 transport_id = %packet.transport_id,
829 remote_addr = %packet.remote_addr,
830 bytes = packet.data.len(),
831 phase = prefix.phase,
832 version = prefix.version,
833 "FMP packet dispatch"
834 );
835 }
836
837 if prefix.version != FMP_VERSION {
838 debug!(
839 version = prefix.version,
840 transport_id = %packet.transport_id,
841 "Unknown FMP version, dropping"
842 );
843
844 let looks_like_fmp_phase =
852 matches!(prefix.phase, PHASE_ESTABLISHED | PHASE_MSG1 | PHASE_MSG2);
853 if looks_like_fmp_phase
854 && self.bootstrap_transports.contains(&packet.transport_id)
855 && let Some(npub) = self.bootstrap_transports.peer_npub(&packet.transport_id)
856 && let Some(handle) = self.nostr_discovery_handle()
857 {
858 let now_ms = Self::now_ms();
859 let cooldown_secs = handle.protocol_mismatch_cooldown_secs();
860 if handle.record_protocol_mismatch(npub, now_ms) {
861 warn!(
862 peer_npub = %npub,
863 transport_id = %packet.transport_id,
864 peer_version = prefix.version,
865 our_version = FMP_VERSION,
866 cooldown_secs,
867 "Nostr-discovered peer speaks a different FMP version; suppressing retraversal"
868 );
869 }
870 }
871 return PacketProcessAction::Done;
872 }
873
874 match prefix.phase {
875 PHASE_ESTABLISHED => match self.try_prepare_encrypted_frame_for_worker(packet) {
876 EncryptedFrameFastPath::Dispatch(job) => PacketProcessAction::DecryptJob { job },
877 EncryptedFrameFastPath::Dropped => PacketProcessAction::Done,
878 EncryptedFrameFastPath::Slow(packet) => {
879 PacketProcessAction::EncryptedSlow { packet, timer }
880 }
881 },
882 PHASE_MSG1 => PacketProcessAction::Msg1 { packet, timer },
883 PHASE_MSG2 => PacketProcessAction::Msg2 { packet, timer },
884 _ => {
885 debug!(
886 phase = prefix.phase,
887 transport_id = %packet.transport_id,
888 "Unknown FMP phase, dropping"
889 );
890 PacketProcessAction::Done
891 }
892 }
893 }
894
895 async fn finish_packet_process(&mut self, action: PacketProcessAction) {
896 match action {
897 PacketProcessAction::Done => {}
898 PacketProcessAction::DecryptJob { job } => {
899 if let Some(workers) = self.decrypt_workers.as_ref() {
900 workers.dispatch_job(job);
901 }
902 }
903 PacketProcessAction::EncryptedSlow {
904 packet,
905 timer: _timer,
906 } => {
907 self.handle_encrypted_frame_slow(packet).await;
908 }
909 PacketProcessAction::Msg1 {
910 packet,
911 timer: _timer,
912 } => {
913 self.handle_msg1(packet).await;
914 }
915 PacketProcessAction::Msg2 {
916 packet,
917 timer: _timer,
918 } => {
919 self.handle_msg2(packet).await;
920 }
921 }
922 }
923
924 fn flush_decrypt_job_batcher(&self, batcher: &mut DecryptJobBatcher) {
925 if let Some(workers) = self.decrypt_workers.as_ref() {
926 batcher.flush(workers);
927 }
928 }
929}