1use crate::control::queries;
4use crate::control::{ControlMessage, ControlSenders, 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::{
15 AuthenticatedFmpPlaintext, EndpointSendBatchCommand, Node, NodeEndpointCommand, NodeError,
16};
17use crate::transport::PacketRx;
18use crate::transport::ReceivedPacket;
19use crate::upper::tun::TunOutboundRx;
20use std::time::{Duration, Instant};
21use tokio::sync::mpsc::Receiver;
22use tracing::{debug, info, trace, warn};
23
24mod budget;
25mod drain;
26
27#[cfg(test)]
28mod tests;
29
30use budget::*;
31use drain::*;
32
33impl Node {
34 pub async fn run_rx_loop(&mut self) -> Result<(), NodeError> {
54 let mut packet_rx = self.packet_rx.take().ok_or(NodeError::NotStarted)?;
55
56 let (mut tun_outbound_rx, _tun_guard) = match self.tun_outbound_rx.take() {
60 Some(rx) => (rx, None),
61 None => {
62 let (tx, rx) = tokio::sync::mpsc::channel(1);
63 (rx, Some(tx))
64 }
65 };
66
67 let (mut dns_identity_rx, _dns_guard) = match self.dns_identity_rx.take() {
70 Some(rx) => (rx, None),
71 None => {
72 let (tx, rx) = tokio::sync::mpsc::channel(1);
73 (rx, Some(tx))
74 }
75 };
76
77 let (mut endpoint_priority_command_rx, _endpoint_priority_command_guard) =
80 match self.endpoint_priority_command_rx.take() {
81 Some(rx) => (rx, None),
82 None => {
83 let (tx, rx) = tokio::sync::mpsc::channel(1);
84 (rx, Some(tx))
85 }
86 };
87 let (mut endpoint_command_rx, _endpoint_command_guard) =
88 match self.endpoint_command_rx.take() {
89 Some(rx) => (rx, None),
90 None => {
91 let (tx, rx) = tokio::sync::mpsc::channel(1);
92 (rx, Some(tx))
93 }
94 };
95 let (mut endpoint_bulk_feedback_rx, _endpoint_bulk_feedback_guard) =
96 match self.endpoint_bulk_feedback_rx.take() {
97 Some(rx) => (rx, None),
98 None => {
99 let (tx, rx) = tokio::sync::mpsc::channel(1);
100 (rx, Some(tx))
101 }
102 };
103
104 let (mut decrypt_fallback_rx, _decrypt_fallback_guard) =
108 match self.decrypt_fallback_rx.take() {
109 Some(rx) => (rx, None),
110 None => {
111 let (tx, rx) = crate::node::decrypt_worker::decrypt_worker_fallback_channels();
112 (rx, Some(tx))
113 }
114 };
115
116 let mut tick =
117 tokio::time::interval(Duration::from_secs(self.config.node.tick_interval_secs));
118 tick.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
119 let mut maintenance_state = RxLoopMaintenanceState::default();
120
121 let (control_query_tx, mut control_query_rx) =
125 tokio::sync::mpsc::channel::<ControlMessage>(32);
126 let (control_command_tx, mut control_command_rx) =
127 tokio::sync::mpsc::channel::<ControlMessage>(32);
128
129 if self.config.node.control.enabled {
130 let config = self.config.node.control.clone();
131 let senders = ControlSenders::new(control_query_tx.clone(), control_command_tx.clone());
132 tokio::spawn(async move {
133 match ControlSocket::bind(&config) {
134 Ok(socket) => {
135 socket.accept_loop_split(senders).await;
136 }
137 Err(e) => {
138 warn!(error = %e, "Failed to bind control socket");
139 }
140 }
141 });
142 }
143 drop(control_query_tx);
145 drop(control_command_tx);
146
147 info!("RX event loop started");
148 crate::perf_profile::maybe_spawn_reporter();
150 tick.tick().await;
154
155 loop {
156 tokio::select! {
157 biased;
158 Some(event) = decrypt_fallback_rx.priority.recv() => {
169 let fallback_drained = self.drain_decrypt_priority_fallback(
170 &mut decrypt_fallback_rx.priority,
171 Some(event),
172 PACKET_DRAIN_BUDGET,
173 ).await;
174 let side_drained = self.drain_rx_loop_side_queues(
175 &mut control_query_rx,
176 &mut tun_outbound_rx,
177 &mut endpoint_priority_command_rx,
178 &mut endpoint_command_rx,
179 SIDE_QUEUE_INTERLEAVE_BUDGET,
180 ).await;
181 if fallback_drained > 0 || side_drained.has_data_drained() {
182 maintenance_state.record_data_activity(Instant::now());
183 }
184 }
185 _ = tick.tick() => {
191 let drained = self.drain_rx_loop_data_queues(
192 &mut packet_rx,
193 &mut decrypt_fallback_rx,
194 &mut tun_outbound_rx,
195 &mut endpoint_priority_command_rx,
196 &mut endpoint_command_rx,
197 NON_PACKET_DRAIN_BUDGET,
198 ).await;
199 if drained.has_drained() {
200 maintenance_state.record_data_activity(Instant::now());
201 debug!(
202 drained = drained.total(),
203 drained_packets = drained.packets,
204 drained_tun = drained.tun,
205 drained_endpoint = drained.endpoint,
206 "Drained queued packets before rx-loop maintenance"
207 );
208 }
209 let maintenance_plan = maintenance_state.plan_maintenance(
210 drained,
211 Instant::now(),
212 RX_LOOP_RECENT_DATA_ACTIVITY_WINDOW,
213 RX_LOOP_SLOW_MAINTENANCE_IDLE_TIMEOUT,
214 RX_LOOP_SLOW_MAINTENANCE_BUSY_TIMEOUT,
215 );
216
217 let slow_timed_out = self.run_rx_loop_maintenance_tick(
218 maintenance_plan,
219 ).await;
220 maintenance_state.record_maintenance_result(
221 maintenance_plan.data_pressure(),
222 slow_timed_out,
223 );
224
225 let post_drained = self.drain_rx_loop_data_queues(
226 &mut packet_rx,
227 &mut decrypt_fallback_rx,
228 &mut tun_outbound_rx,
229 &mut endpoint_priority_command_rx,
230 &mut endpoint_command_rx,
231 PACKET_DRAIN_BUDGET,
232 ).await;
233 if post_drained.has_drained() {
234 maintenance_state.record_data_activity(Instant::now());
235 debug!(
236 drained = post_drained.total(),
237 drained_packets = post_drained.packets,
238 drained_tun = post_drained.tun,
239 drained_endpoint = post_drained.endpoint,
240 "Drained queued packets after rx-loop maintenance"
241 );
242 }
243 }
244 Some(event) = decrypt_fallback_rx.authenticated_bulk.recv(),
245 if authenticated_bulk_preempts_packet_rx(packet_rx.priority_ready_packets()) =>
246 {
247 let fallback_drained = self.drain_decrypt_fallback(
248 &mut decrypt_fallback_rx,
249 None,
250 Some(event),
251 None,
252 NON_PACKET_DRAIN_BUDGET,
253 ).await;
254 let side_drained = self.drain_rx_loop_side_queues(
255 &mut control_query_rx,
256 &mut tun_outbound_rx,
257 &mut endpoint_priority_command_rx,
258 &mut endpoint_command_rx,
259 SIDE_QUEUE_INTERLEAVE_BUDGET,
260 ).await;
261 if fallback_drained > 0 || side_drained.has_data_drained() {
262 maintenance_state.record_data_activity(Instant::now());
263 }
264 }
265 Some(message) = control_query_rx.recv() => {
266 self.drain_control_queries(
267 &mut control_query_rx,
268 Some(message),
269 NON_PACKET_DRAIN_BUDGET,
270 ).await;
271 }
272 Some(feedback) = endpoint_bulk_feedback_rx.recv() => {
273 let drained = self.drain_endpoint_bulk_send_feedback(
274 &mut endpoint_bulk_feedback_rx,
275 Some(feedback),
276 NON_PACKET_DRAIN_BUDGET,
277 );
278 if drained > 0 {
279 maintenance_state.record_data_activity(Instant::now());
280 }
281 }
282 packet = packet_rx.recv() => {
283 match packet {
284 Some(p) => {
285 let drained = self.drain_packet_rx(
286 &mut packet_rx,
287 &mut decrypt_fallback_rx,
288 Some(RxLoopSideQueues {
289 control_query_rx: &mut control_query_rx,
290 tun_outbound_rx: &mut tun_outbound_rx,
291 endpoint_priority_command_rx: &mut endpoint_priority_command_rx,
292 endpoint_command_rx: &mut endpoint_command_rx,
293 }),
294 Some(p),
295 PACKET_DRAIN_BUDGET,
296 ).await;
297 if drained > 0 {
298 maintenance_state.record_data_activity(Instant::now());
299 }
300 }
301 None => break, }
303 }
304 Some(command) = endpoint_priority_command_rx.recv() => {
305 let drained = self.drain_endpoint_commands(
306 &mut endpoint_priority_command_rx,
307 &mut endpoint_command_rx,
308 Some(command),
309 None,
310 NON_PACKET_DRAIN_BUDGET,
311 ).await;
312 if drained > 0 {
313 maintenance_state.record_data_activity(Instant::now());
314 }
315 }
316 Some(event) = decrypt_fallback_rx.bulk.recv() => {
317 let fallback_plan = fallback_drain_plan();
318 let fallback_drained = self.drain_decrypt_fallback(
319 &mut decrypt_fallback_rx,
320 None,
321 None,
322 Some(event),
323 fallback_plan.trailing_budget,
324 ).await;
325 let side_drained = self.drain_rx_loop_side_queues(
326 &mut control_query_rx,
327 &mut tun_outbound_rx,
328 &mut endpoint_priority_command_rx,
329 &mut endpoint_command_rx,
330 SIDE_QUEUE_INTERLEAVE_BUDGET,
331 ).await;
332 if fallback_drained > 0 || side_drained.has_data_drained() {
333 maintenance_state.record_data_activity(Instant::now());
334 }
335 }
336 Some(ipv6_packet) = tun_outbound_rx.recv() => {
337 let drained = self.drain_tun_outbound(
338 &mut tun_outbound_rx,
339 Some(ipv6_packet),
340 NON_PACKET_DRAIN_BUDGET,
341 ).await;
342 if drained > 0 {
343 maintenance_state.record_data_activity(Instant::now());
344 }
345 }
346 Some(identity) = dns_identity_rx.recv() => {
347 debug!(
348 node_addr = %identity.node_addr,
349 "Registering identity from DNS resolution"
350 );
351 self.register_identity(identity.node_addr, identity.pubkey);
352 }
353 Some(command) = endpoint_command_rx.recv() => {
354 let drained = self.drain_endpoint_commands(
355 &mut endpoint_priority_command_rx,
356 &mut endpoint_command_rx,
357 None,
358 Some(command),
359 NON_PACKET_DRAIN_BUDGET,
360 ).await;
361 if drained > 0 {
362 maintenance_state.record_data_activity(Instant::now());
363 }
364 }
365 Some((request, response_tx)) = control_command_rx.recv() => {
366 let response = commands::dispatch(
367 self,
368 &request.command,
369 request.params.as_ref(),
370 ).await;
371 let _ = response_tx.send(response);
372 }
373 }
374 }
375
376 info!("RX event loop stopped (channel closed)");
377 Ok(())
378 }
379
380 async fn drain_rx_loop_data_queues(
381 &mut self,
382 packet_rx: &mut PacketRx,
383 decrypt_fallback_rx: &mut DecryptWorkerFallbackReceivers,
384 tun_outbound_rx: &mut TunOutboundRx,
385 endpoint_priority_command_rx: &mut Receiver<NodeEndpointCommand>,
386 endpoint_command_rx: &mut Receiver<NodeEndpointCommand>,
387 budget: usize,
388 ) -> RxLoopDataDrainStats {
389 let drained_packets = self
390 .drain_packet_rx(packet_rx, decrypt_fallback_rx, None, None, budget)
391 .await;
392 let non_packet_budget = non_packet_drain_budget(budget);
393 let drained_tun = self
394 .drain_tun_outbound(tun_outbound_rx, None, non_packet_budget)
395 .await;
396 let drained_endpoint = self
397 .drain_endpoint_commands(
398 endpoint_priority_command_rx,
399 endpoint_command_rx,
400 None,
401 None,
402 non_packet_budget,
403 )
404 .await;
405 RxLoopDataDrainStats::new(drained_packets, drained_tun, drained_endpoint)
406 }
407
408 async fn drain_packet_rx(
409 &mut self,
410 packet_rx: &mut PacketRx,
411 decrypt_fallback_rx: &mut DecryptWorkerFallbackReceivers,
412 mut side_queues: Option<RxLoopSideQueues<'_>>,
413 first_packet: Option<ReceivedPacket>,
414 budget: usize,
415 ) -> usize {
416 self.begin_endpoint_event_batch();
422 let side_queue_interleave_every = if side_queues.is_some() {
423 SIDE_QUEUE_INTERLEAVE_EVERY
424 } else {
425 0
426 };
427 let fallback_plan = fallback_drain_plan();
428 let mut drain = PacketDrainCursor::new(
429 first_packet,
430 budget,
431 fallback_plan.interleave_every,
432 side_queue_interleave_every,
433 );
434 let mut decrypt_jobs = DecryptJobBatcher::new();
435 while let Some(action) = drain.next(packet_rx) {
436 match action {
437 PacketDrainAction::Packet(packet) => {
438 let action = self.begin_process_packet(packet);
439 match action {
440 PacketProcessAction::DecryptJob { job } => {
441 if let Some(workers) = self.decrypt_workers.as_ref() {
442 decrypt_jobs.push(workers, job);
443 }
444 }
445 PacketProcessAction::Done => {}
446 action => {
447 self.flush_decrypt_job_batcher(&mut decrypt_jobs);
448 self.finish_packet_process(action).await;
449 }
450 }
451 }
452 PacketDrainAction::InterleaveFallback => {
453 self.flush_decrypt_job_batcher(&mut decrypt_jobs);
454 let drained = if decrypt_fallback_has_ready(decrypt_fallback_rx) {
455 self.drain_decrypt_fallback(
456 decrypt_fallback_rx,
457 None,
458 None,
459 None,
460 fallback_plan.interleave_budget,
461 )
462 .await
463 } else {
464 0
465 };
466 if drained == 0 {
467 drain.refund_empty_interleave_turn();
468 }
469 }
470 PacketDrainAction::InterleaveSideQueues => {
471 self.flush_decrypt_job_batcher(&mut decrypt_jobs);
472 let drained = if let Some(side_queues) = side_queues.as_mut() {
473 if rx_loop_side_queues_have_ready(side_queues) {
474 self.drain_rx_loop_side_queues(
475 side_queues.control_query_rx,
476 side_queues.tun_outbound_rx,
477 side_queues.endpoint_priority_command_rx,
478 side_queues.endpoint_command_rx,
479 SIDE_QUEUE_INTERLEAVE_BUDGET,
480 )
481 .await
482 } else {
483 RxLoopDataDrainStats::default()
484 }
485 } else {
486 RxLoopDataDrainStats::default()
487 };
488 if !drained.has_drained() {
489 drain.refund_empty_interleave_turn();
490 }
491 }
492 }
493 }
494
495 self.flush_decrypt_job_batcher(&mut decrypt_jobs);
496 let drained = drain.drained();
497 if drained > 0 {
498 self.drain_decrypt_fallback(
503 decrypt_fallback_rx,
504 None,
505 None,
506 None,
507 fallback_plan.trailing_budget.min(budget),
508 )
509 .await;
510 self.finish_endpoint_event_batch();
511 } else {
512 self.finish_endpoint_event_batch();
513 }
514 drained
515 }
516
517 async fn drain_rx_loop_side_queues(
518 &mut self,
519 control_query_rx: &mut Receiver<ControlMessage>,
520 tun_outbound_rx: &mut TunOutboundRx,
521 endpoint_priority_command_rx: &mut Receiver<NodeEndpointCommand>,
522 endpoint_command_rx: &mut Receiver<NodeEndpointCommand>,
523 budget: usize,
524 ) -> RxLoopDataDrainStats {
525 let control_budget = budget.min(CONTROL_QUERY_INTERLEAVE_BUDGET);
526 let drained_control = self
527 .drain_control_queries(control_query_rx, None, control_budget)
528 .await;
529 let remaining_budget = budget.saturating_sub(drained_control);
530 let (endpoint_budget, tun_budget) = split_side_queue_budget(remaining_budget);
531 let mut drained_endpoint = self
532 .drain_endpoint_commands(
533 endpoint_priority_command_rx,
534 endpoint_command_rx,
535 None,
536 None,
537 endpoint_budget,
538 )
539 .await;
540 let mut drained_tun = self
541 .drain_tun_outbound(tun_outbound_rx, None, tun_budget)
542 .await;
543
544 let endpoint_remainder = remaining_side_queue_budget(endpoint_budget, drained_endpoint);
545 let tun_remainder = remaining_side_queue_budget(tun_budget, drained_tun);
546 if endpoint_remainder > 0 && !tun_outbound_rx.is_empty() {
547 drained_tun += self
548 .drain_tun_outbound(tun_outbound_rx, None, endpoint_remainder)
549 .await;
550 }
551 if tun_remainder > 0
552 && (!endpoint_priority_command_rx.is_empty() || !endpoint_command_rx.is_empty())
553 {
554 drained_endpoint += self
555 .drain_endpoint_commands(
556 endpoint_priority_command_rx,
557 endpoint_command_rx,
558 None,
559 None,
560 tun_remainder,
561 )
562 .await;
563 }
564
565 RxLoopDataDrainStats::with_control(0, drained_tun, drained_endpoint, drained_control)
566 }
567
568 async fn drain_control_queries(
569 &mut self,
570 control_query_rx: &mut Receiver<ControlMessage>,
571 first_message: Option<ControlMessage>,
572 budget: usize,
573 ) -> usize {
574 let mut drain = SingleLaneDrainCursor::new(first_message, budget);
575 while let Some((request, response_tx)) = drain.next(control_query_rx) {
576 let response = queries::dispatch(self, &request.command, request.params.as_ref());
577 let _ = response_tx.send(response);
578 }
579
580 drain.drained()
581 }
582
583 async fn drain_tun_outbound(
584 &mut self,
585 tun_outbound_rx: &mut TunOutboundRx,
586 first_packet: Option<Vec<u8>>,
587 budget: usize,
588 ) -> usize {
589 let mut drain = SingleLaneDrainCursor::new(first_packet, budget);
590 while let Some(packet) = drain.next(tun_outbound_rx) {
591 self.handle_tun_outbound(packet).await;
592 }
593
594 drain.drained()
595 }
596
597 async fn drain_endpoint_commands(
598 &mut self,
599 endpoint_priority_command_rx: &mut Receiver<NodeEndpointCommand>,
600 endpoint_command_rx: &mut Receiver<NodeEndpointCommand>,
601 first_priority_command: Option<NodeEndpointCommand>,
602 first_bulk_command: Option<NodeEndpointCommand>,
603 budget: usize,
604 ) -> usize {
605 let mut drain =
606 PriorityBulkDrainCursor::new(first_priority_command, first_bulk_command, budget);
607 while let Some(command) = drain.next(endpoint_priority_command_rx, endpoint_command_rx) {
608 let drain_cost = command.drain_cost();
609 match command.into_send_batch_oneway() {
610 Ok((batch, _lane)) => {
611 let mut batch_commands = vec![batch];
612 self.coalesce_endpoint_send_batch_commands(
613 &mut drain,
614 endpoint_priority_command_rx,
615 endpoint_command_rx,
616 &mut batch_commands,
617 );
618 self.handle_endpoint_send_batch_commands(batch_commands)
619 .await;
620 }
621 Err(command) => {
622 self.handle_endpoint_data_command(command).await;
623 }
624 }
625 drain.charge_extra(drain_cost.saturating_sub(1));
626 }
627
628 drain.drained()
629 }
630
631 fn drain_endpoint_bulk_send_feedback(
632 &mut self,
633 endpoint_bulk_feedback_rx: &mut Receiver<crate::node::EndpointBulkSendFeedback>,
634 first_feedback: Option<crate::node::EndpointBulkSendFeedback>,
635 budget: usize,
636 ) -> usize {
637 let mut drain = SingleLaneDrainCursor::new(first_feedback, budget);
638 while let Some(feedback) = drain.next(endpoint_bulk_feedback_rx) {
639 self.apply_endpoint_bulk_send_feedback(feedback);
640 }
641
642 drain.drained()
643 }
644
645 fn coalesce_endpoint_send_batch_commands(
646 &mut self,
647 drain: &mut PriorityBulkDrainCursor<NodeEndpointCommand>,
648 endpoint_priority_command_rx: &mut Receiver<NodeEndpointCommand>,
649 endpoint_command_rx: &mut Receiver<NodeEndpointCommand>,
650 batch_commands: &mut Vec<EndpointSendBatchCommand>,
651 ) {
652 let mut payloads = batch_commands
653 .iter()
654 .fold(0usize, |total, command| total.saturating_add(command.len()));
655 while payloads < ENDPOINT_COMMAND_COALESCE_MAX_PACKETS {
656 let Some(command) =
657 drain.next_bulk_if_no_priority(endpoint_priority_command_rx, endpoint_command_rx)
658 else {
659 break;
660 };
661 let drain_cost = command.drain_cost();
662 match command.into_send_batch_oneway() {
663 Ok((batch, _lane))
664 if batch_commands.last().is_some_and(|last| {
665 last.can_coalesce_with(&batch, ENDPOINT_COMMAND_COALESCE_MAX_PACKETS)
666 }) =>
667 {
668 payloads = payloads.saturating_add(batch.len());
669 batch_commands.push(batch);
670 drain.charge_extra(drain_cost.saturating_sub(1));
671 }
672 Ok((batch, lane)) => {
673 drain.defer_bulk(NodeEndpointCommand::SendBatchOneway {
674 command: batch,
675 lane,
676 });
677 break;
678 }
679 Err(command) => {
680 drain.defer_bulk(command);
681 break;
682 }
683 }
684 }
685 }
686
687 async fn run_rx_loop_maintenance_tick(&mut self, plan: RxLoopMaintenancePlan) -> bool {
688 self.check_timeouts();
689 let now_ms = Self::now_ms();
690 self.check_link_heartbeats().await;
694 self.reload_peer_acl();
695 self.resend_pending_handshakes(now_ms).await;
696 self.resend_pending_rekeys(now_ms).await;
697 self.resend_pending_session_handshakes(now_ms).await;
698 self.resend_pending_session_msg3(now_ms).await;
699 self.purge_idle_sessions(now_ms);
700 self.purge_learned_routes(now_ms);
701 self.check_mmp_reports().await;
702 self.check_session_mmp_reports().await;
703 self.check_rekey().await;
704 self.check_session_rekey().await;
705 self.check_pending_lookups(now_ms).await;
706 self.poll_pending_connects().await;
707 self.process_pending_retries(now_ms).await;
708 self.poll_transport_discovery().await;
709 self.activate_connected_udp_sessions().await;
710 self.sample_transport_congestion();
711
712 let Some(slow_timeout) = plan.slow_timeout() else {
713 crate::perf_profile::record_event(
714 crate::perf_profile::Event::RxLoopSlowMaintenanceSkipped,
715 );
716 return false;
717 };
718
719 if tokio::time::timeout(slow_timeout, self.run_rx_loop_slow_maintenance_tick())
720 .await
721 .is_err()
722 {
723 crate::perf_profile::record_event(
724 crate::perf_profile::Event::RxLoopSlowMaintenanceTimeout,
725 );
726 self.mark_rx_loop_maintenance_timeout();
727 warn!(
728 timeout_ms = slow_timeout.as_millis() as u64,
729 data_pressure = plan.data_pressure(),
730 "RX loop slow maintenance timed out; continuing packet processing"
731 );
732 return true;
733 }
734 false
735 }
736
737 async fn run_rx_loop_slow_maintenance_tick(&mut self) {
738 if let Some(delay) = rx_loop_slow_maintenance_fault_delay() {
739 tokio::time::sleep(delay).await;
740 }
741
742 self.poll_nostr_discovery().await;
747 self.poll_lan_discovery().await;
748 self.poll_local_instance_discovery().await;
749 self.check_tree_state().await;
750 self.check_bloom_state().await;
751 self.compute_mesh_size();
752 self.record_stats_history();
753 }
754
755 async fn process_decrypt_worker_event(&mut self, event: DecryptWorkerEvent) {
760 event.record_queue_wait();
761 match event {
762 DecryptWorkerEvent::Plaintext(fallback) => {
763 self.process_decrypt_fallback(fallback).await;
764 }
765 DecryptWorkerEvent::PlaintextBatch(fallbacks) => {
766 for fallback in fallbacks {
767 self.process_decrypt_fallback(fallback).await;
768 }
769 }
770 DecryptWorkerEvent::AuthenticatedFmpReceive(receive) => {
771 self.process_authenticated_fmp_receive_from_worker(receive);
772 }
773 DecryptWorkerEvent::AuthenticatedSession(session) => {
774 self.process_authenticated_session_from_worker(session)
775 .await;
776 }
777 DecryptWorkerEvent::AuthenticatedSessionBatch(sessions) => {
778 self.process_authenticated_session_batch_from_worker(sessions)
779 .await;
780 }
781 DecryptWorkerEvent::DirectSessionCommit(commit) => {
782 self.process_direct_session_commit_from_worker(commit).await;
783 }
784 DecryptWorkerEvent::DirectSessionCommitBatch(commits) => {
785 self.process_direct_session_commit_batch_from_worker(commits)
786 .await;
787 }
788 DecryptWorkerEvent::DirectSessionData(direct) => {
789 self.process_direct_session_data_from_worker(direct).await;
790 }
791 DecryptWorkerEvent::DirectSessionDataBatch(directs) => {
792 self.process_direct_session_data_batch_from_worker(directs)
793 .await;
794 }
795 DecryptWorkerEvent::FspDecryptFailure(report) => {
796 self.process_fsp_decrypt_failure_from_worker(report).await;
797 }
798 DecryptWorkerEvent::DecryptFailure(report) => {
799 self.process_decrypt_failure_report(report).await;
800 }
801 }
802 }
803
804 async fn process_decrypt_fallback(&mut self, fallback: DecryptFallback) {
805 let plaintext = &fallback.packet_data[fallback.fmp_plaintext_offset
806 ..fallback.fmp_plaintext_offset + fallback.fmp_plaintext_len];
807 self.process_authentic_fmp_plaintext(AuthenticatedFmpPlaintext::new(
808 fallback.source_peer,
809 fallback.transport_id,
810 &fallback.remote_addr,
811 fallback.timestamp_ms,
812 fallback.packet_len,
813 fallback.fmp_counter,
814 fallback.fmp_flags,
815 plaintext,
816 ))
817 .await;
818 }
819
820 async fn process_decrypt_failure_report(&mut self, report: DecryptFailureReport) {
821 debug!(
822 peer = %self.peer_display_name(report.source_peer.node_addr()),
823 counter = report.fmp_counter,
824 replay_highest = report.fmp_replay_highest,
825 "Worker FMP AEAD decryption failed"
826 );
827 self.handle_decrypt_failure_report(&report).await;
828 }
829
830 async fn drain_decrypt_priority_fallback(
837 &mut self,
838 priority_rx: &mut Receiver<DecryptWorkerEvent>,
839 first_event: Option<DecryptWorkerEvent>,
840 budget: usize,
841 ) -> usize {
842 self.begin_endpoint_event_batch();
843 let mut drain = SingleLaneDrainCursor::new(first_event, budget);
844 while let Some(event) = drain.next(priority_rx) {
845 self.process_decrypt_worker_event(event).await;
846 }
847 let drained = drain.drained();
848 self.finish_endpoint_event_batch();
849 drained
850 }
851
852 async fn drain_decrypt_fallback(
858 &mut self,
859 rx: &mut DecryptWorkerFallbackReceivers,
860 first_priority_event: Option<DecryptWorkerEvent>,
861 first_authenticated_bulk_event: Option<DecryptWorkerEvent>,
862 first_bulk_event: Option<DecryptWorkerEvent>,
863 budget: usize,
864 ) -> usize {
865 self.begin_endpoint_event_batch();
866 let mut drain = DecryptReturnDrainCursor::new(
867 first_priority_event,
868 first_authenticated_bulk_event,
869 first_bulk_event,
870 budget,
871 );
872 while let Some(event) =
873 drain.next(&mut rx.priority, &mut rx.authenticated_bulk, &mut rx.bulk)
874 {
875 rx.release_dequeued_event(&event);
876 let extra = event.packet_count().saturating_sub(1);
877 self.process_decrypt_worker_event(event).await;
878 drain.charge_extra(extra);
879 }
880 let drained = drain.drained();
881 self.finish_endpoint_event_batch();
882 drained
883 }
884
885 #[cfg(test)]
889 pub(in crate::node) async fn process_packet(&mut self, packet: ReceivedPacket) {
890 let action = self.begin_process_packet(packet);
891 self.finish_packet_process(action).await;
892 }
893
894 fn begin_process_packet(&mut self, packet: ReceivedPacket) -> PacketProcessAction {
895 let timer = crate::perf_profile::Timer::start(crate::perf_profile::Stage::ProcessPacket);
896 let priority_sized = packet.is_priority_sized();
897 let priority_count = u64::from(priority_sized);
898 let bulk_count = u64::from(!priority_sized);
899 crate::perf_profile::record_since_split_count(
900 crate::perf_profile::Stage::TransportQueueWait,
901 crate::perf_profile::Stage::TransportPriorityQueueWait,
902 crate::perf_profile::Stage::TransportBulkQueueWait,
903 packet.trace_enqueued_at,
904 1,
905 priority_count,
906 bulk_count,
907 );
908 crate::perf_profile::record_since_split_count(
909 crate::perf_profile::Stage::TransportRxLoopWait,
910 crate::perf_profile::Stage::TransportPriorityRxLoopWait,
911 crate::perf_profile::Stage::TransportBulkRxLoopWait,
912 packet.trace_rx_loop_owned_at,
913 1,
914 priority_count,
915 bulk_count,
916 );
917 if is_punch_packet(&packet.data) {
918 trace!(
919 transport_id = %packet.transport_id,
920 remote_addr = %packet.remote_addr,
921 bytes = packet.data.len(),
922 "Dropping stray punch probe/ack in FMP rx loop"
923 );
924 return PacketProcessAction::Done;
925 }
926 if packet.data.len() < COMMON_PREFIX_SIZE {
927 return PacketProcessAction::Done; }
929
930 let prefix = match CommonPrefix::parse(&packet.data) {
931 Some(p) => p,
932 None => return PacketProcessAction::Done, };
934 if matches!(prefix.phase, PHASE_MSG1 | PHASE_MSG2) {
935 debug!(
936 transport_id = %packet.transport_id,
937 remote_addr = %packet.remote_addr,
938 bytes = packet.data.len(),
939 phase = prefix.phase,
940 version = prefix.version,
941 "FMP handshake packet dispatch"
942 );
943 } else {
944 trace!(
945 transport_id = %packet.transport_id,
946 remote_addr = %packet.remote_addr,
947 bytes = packet.data.len(),
948 phase = prefix.phase,
949 version = prefix.version,
950 "FMP packet dispatch"
951 );
952 }
953
954 if prefix.version != FMP_VERSION {
955 debug!(
956 version = prefix.version,
957 transport_id = %packet.transport_id,
958 "Unknown FMP version, dropping"
959 );
960
961 let looks_like_fmp_phase =
969 matches!(prefix.phase, PHASE_ESTABLISHED | PHASE_MSG1 | PHASE_MSG2);
970 if looks_like_fmp_phase
971 && self.bootstrap_transports.contains(&packet.transport_id)
972 && let Some(npub) = self.bootstrap_transports.peer_npub(&packet.transport_id)
973 && let Some(handle) = self.nostr_discovery_handle()
974 {
975 let now_ms = Self::now_ms();
976 let cooldown_secs = handle.protocol_mismatch_cooldown_secs();
977 if handle.record_protocol_mismatch(npub, now_ms) {
978 warn!(
979 peer_npub = %npub,
980 transport_id = %packet.transport_id,
981 peer_version = prefix.version,
982 our_version = FMP_VERSION,
983 cooldown_secs,
984 "Nostr-discovered peer speaks a different FMP version; suppressing retraversal"
985 );
986 }
987 }
988 return PacketProcessAction::Done;
989 }
990
991 match prefix.phase {
992 PHASE_ESTABLISHED => match self.try_prepare_encrypted_frame_for_worker(packet) {
993 EncryptedFrameFastPath::Dispatch(job) => PacketProcessAction::DecryptJob { job },
994 EncryptedFrameFastPath::Dropped => PacketProcessAction::Done,
995 EncryptedFrameFastPath::Slow(packet) => {
996 PacketProcessAction::EncryptedSlow { packet, timer }
997 }
998 },
999 PHASE_MSG1 => PacketProcessAction::Msg1 { packet, timer },
1000 PHASE_MSG2 => PacketProcessAction::Msg2 { packet, timer },
1001 _ => {
1002 debug!(
1003 phase = prefix.phase,
1004 transport_id = %packet.transport_id,
1005 "Unknown FMP phase, dropping"
1006 );
1007 PacketProcessAction::Done
1008 }
1009 }
1010 }
1011
1012 async fn finish_packet_process(&mut self, action: PacketProcessAction) {
1013 match action {
1014 PacketProcessAction::Done => {}
1015 PacketProcessAction::DecryptJob { job } => {
1016 if let Some(workers) = self.decrypt_workers.as_ref() {
1017 workers.dispatch_job(job);
1018 }
1019 }
1020 PacketProcessAction::EncryptedSlow {
1021 packet,
1022 timer: _timer,
1023 } => {
1024 self.handle_encrypted_frame_slow(packet).await;
1025 }
1026 PacketProcessAction::Msg1 {
1027 packet,
1028 timer: _timer,
1029 } => {
1030 self.handle_msg1(packet).await;
1031 }
1032 PacketProcessAction::Msg2 {
1033 packet,
1034 timer: _timer,
1035 } => {
1036 self.handle_msg2(packet).await;
1037 }
1038 }
1039 }
1040
1041 fn flush_decrypt_job_batcher(&self, batcher: &mut DecryptJobBatcher) {
1042 if let Some(workers) = self.decrypt_workers.as_ref() {
1043 batcher.flush(workers);
1044 }
1045 }
1046}