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