1mod active;
4mod conn;
5mod counter;
6mod handle;
7#[cfg(test)]
8mod tests;
9mod types;
10pub use types::BlockRangeInfo;
11
12use crate::{
13 budget::DEFAULT_BUDGET_PRIORITIZED_EVENTS,
14 message::PeerMessage,
15 metrics::SessionManagerMetrics,
16 protocol::{IntoRlpxSubProtocol, OnNotSupported, RlpxSubProtocolHandlers, RlpxSubProtocols},
17 session::active::ActiveSession,
18};
19use active::QueuedOutgoingMessages;
20use alloy_primitives::map::{FbBuildHasher, HashMap};
21use counter::SessionCounter;
22use futures::{
23 future::{self, Either},
24 io, stream, FutureExt, StreamExt,
25};
26use reth_ecies::{stream::ECIESStream, ECIESError};
27use reth_eth_wire::{
28 errors::EthStreamError, handshake::EthRlpxHandshake, multiplex::RlpxProtocolMultiplexer,
29 BlockRangeUpdate, Capabilities, DisconnectReason, EthSnapStream, EthStream, EthVersion,
30 HelloMessageWithProtocols, NetworkPrimitives, UnauthedP2PStream, UnifiedStatus,
31 HANDSHAKE_TIMEOUT,
32};
33use reth_ethereum_forks::{ForkFilter, ForkId, ForkTransition, Head};
34use reth_metrics::common::mpsc::MeteredPollSender;
35use reth_network_api::{PeerRequest, PeerRequestSender};
36use reth_network_peers::PeerId;
37use reth_network_types::SessionsConfig;
38use reth_primitives_traits::{GotExpected, GotExpectedBoxed};
39use reth_tasks::Runtime;
40use rustc_hash::FxHashMap;
41use secp256k1::SecretKey;
42use std::{
43 future::Future,
44 net::SocketAddr,
45 sync::{atomic::AtomicU64, Arc},
46 task::{Context, Poll},
47 time::{Duration, Instant},
48};
49use tokio::{
50 io::{AsyncRead, AsyncWrite},
51 net::TcpStream,
52 sync::{mpsc, oneshot, watch},
53};
54use tokio_stream::wrappers::ReceiverStream;
55use tokio_util::sync::PollSender;
56use tracing::{instrument, trace};
57
58use crate::session::active::{
59 request_timeout_interval, BroadcastItemCounter, RANGE_UPDATE_INTERVAL,
60};
61pub use conn::EthRlpxConnection;
62use handle::SessionCommandSender;
63pub use handle::{
64 ActiveSessionHandle, ActiveSessionMessage, PendingSessionEvent, PendingSessionHandle,
65 SessionCommand,
66};
67pub use reth_network_api::{Direction, PeerInfo};
68
69#[derive(Debug, Clone, Copy, PartialOrd, PartialEq, Eq, Hash)]
71pub struct SessionId(usize);
72
73#[must_use = "Session Manager must be polled to process session events."]
75#[derive(Debug)]
76pub struct SessionManager<N: NetworkPrimitives> {
77 next_id: usize,
79 counter: SessionCounter,
81 initial_internal_request_timeout: Duration,
84 protocol_breach_request_timeout: Duration,
87 pending_session_timeout: Duration,
89 secret_key: SecretKey,
91 status: UnifiedStatus,
93 hello_message: HelloMessageWithProtocols,
95 fork_filter: ForkFilter,
97 session_command_buffer: usize,
99 executor: Runtime,
101 pending_sessions: FxHashMap<SessionId, PendingSessionHandle>,
106 active_sessions: HashMap<PeerId, ActiveSessionHandle<N>, FbBuildHasher<64>>,
108 pending_sessions_tx: mpsc::Sender<PendingSessionEvent<N>>,
113 pending_session_rx: ReceiverStream<PendingSessionEvent<N>>,
115 active_session_tx: MeteredPollSender<ActiveSessionMessage<N>>,
120 active_session_rx: ReceiverStream<ActiveSessionMessage<N>>,
122 active_events_in_row: u32,
124 extra_protocols: RlpxSubProtocols,
126 disconnections_counter: DisconnectionsCounter,
128 metrics: SessionManagerMetrics,
130 handshake: Arc<dyn EthRlpxHandshake>,
132 eth_max_message_size: usize,
134 p2p_max_payload_size: usize,
136 local_range_info: BlockRangeInfo,
139 reject_block_announcements: bool,
142}
143
144impl<N: NetworkPrimitives> SessionManager<N> {
147 #[expect(clippy::too_many_arguments)]
149 pub fn new(
150 secret_key: SecretKey,
151 config: SessionsConfig,
152 executor: Runtime,
153 status: UnifiedStatus,
154 hello_message: HelloMessageWithProtocols,
155 fork_filter: ForkFilter,
156 extra_protocols: RlpxSubProtocols,
157 handshake: Arc<dyn EthRlpxHandshake>,
158 eth_max_message_size: usize,
159 p2p_max_payload_size: usize,
160 reject_block_announcements: bool,
161 ) -> Self {
162 let (pending_sessions_tx, pending_sessions_rx) = mpsc::channel(config.session_event_buffer);
163 let (active_session_tx, active_session_rx) = mpsc::channel(config.session_event_buffer);
164 let active_session_tx = PollSender::new(active_session_tx);
165
166 let local_range_info = BlockRangeInfo::new(
168 status.earliest_block.unwrap_or_default(),
169 status.latest_block.unwrap_or_default(),
170 status.blockhash,
171 );
172
173 Self {
174 next_id: 0,
175 counter: SessionCounter::new(config.limits),
176 initial_internal_request_timeout: config.initial_internal_request_timeout,
177 protocol_breach_request_timeout: config.protocol_breach_request_timeout,
178 pending_session_timeout: config.pending_session_timeout,
179 secret_key,
180 status,
181 hello_message,
182 fork_filter,
183 session_command_buffer: config.session_command_buffer,
184 executor,
185 pending_sessions: Default::default(),
186 active_sessions: Default::default(),
187 pending_sessions_tx,
188 pending_session_rx: ReceiverStream::new(pending_sessions_rx),
189 active_session_tx: MeteredPollSender::new(active_session_tx, "network_active_session"),
190 active_session_rx: ReceiverStream::new(active_session_rx),
191 active_events_in_row: 0,
192 extra_protocols,
193 disconnections_counter: Default::default(),
194 metrics: Default::default(),
195 handshake,
196 eth_max_message_size,
197 p2p_max_payload_size,
198 local_range_info,
199 reject_block_announcements,
200 }
201 }
202
203 pub(crate) const fn fork_id(&self) -> ForkId {
205 self.fork_filter.current()
206 }
207
208 pub fn is_valid_fork_id(&self, fork_id: ForkId) -> bool {
211 self.fork_filter.validate(fork_id).is_ok()
212 }
213
214 const fn next_id(&mut self) -> SessionId {
216 let id = self.next_id;
217 self.next_id += 1;
218 SessionId(id)
219 }
220
221 pub const fn status(&self) -> UnifiedStatus {
223 self.status
224 }
225
226 pub const fn secret_key(&self) -> SecretKey {
228 self.secret_key
229 }
230
231 pub const fn active_sessions(
233 &self,
234 ) -> &HashMap<PeerId, ActiveSessionHandle<N>, FbBuildHasher<64>> {
235 &self.active_sessions
236 }
237
238 pub fn hello_message(&self) -> HelloMessageWithProtocols {
240 self.hello_message.clone()
241 }
242
243 pub(crate) fn add_rlpx_sub_protocol(&mut self, protocol: impl IntoRlpxSubProtocol) {
245 self.extra_protocols.push(protocol)
246 }
247
248 #[inline]
250 pub(crate) fn num_pending_connections(&self) -> usize {
251 self.pending_sessions.len()
252 }
253
254 fn spawn<F>(&self, f: F)
257 where
258 F: Future<Output = ()> + Send + 'static,
259 {
260 self.executor.spawn_task(f);
261 }
262
263 pub(crate) fn on_status_update(&mut self, head: Head) -> Option<ForkTransition> {
268 self.status.blockhash = head.hash;
269 self.status.total_difficulty = Some(head.total_difficulty);
270 let transition = self.fork_filter.set_head(head);
271 self.status.forkid = self.fork_filter.current();
272 self.status.latest_block = Some(head.number);
273
274 transition
275 }
276
277 pub(crate) fn set_fork_filter(&mut self, fork_filter: ForkFilter) -> ForkId {
289 self.fork_filter = fork_filter;
290 self.status.forkid = self.fork_filter.current();
291 self.status.forkid
292 }
293
294 pub(crate) fn on_incoming(
299 &mut self,
300 stream: TcpStream,
301 remote_addr: SocketAddr,
302 ) -> Result<SessionId, ExceedsSessionLimit> {
303 self.counter.ensure_pending_inbound()?;
304
305 let session_id = self.next_id();
306
307 trace!(
308 target: "net::session",
309 ?remote_addr,
310 ?session_id,
311 "new pending incoming session"
312 );
313
314 let (disconnect_tx, disconnect_rx) = oneshot::channel();
315 let pending_events = self.pending_sessions_tx.clone();
316 let secret_key = self.secret_key;
317 let hello_message = self.hello_message.clone();
318 let status = self.status;
319 let fork_filter = self.fork_filter.clone();
320 let extra_handlers = self.extra_protocols.on_incoming(remote_addr);
321 self.spawn(pending_session_with_timeout(
322 self.pending_session_timeout,
323 session_id,
324 remote_addr,
325 Direction::Incoming,
326 pending_events.clone(),
327 start_pending_incoming_session(
328 self.handshake.clone(),
329 self.eth_max_message_size,
330 self.p2p_max_payload_size,
331 disconnect_rx,
332 session_id,
333 stream,
334 pending_events,
335 remote_addr,
336 secret_key,
337 hello_message,
338 status,
339 fork_filter,
340 extra_handlers,
341 ),
342 ));
343
344 let handle = PendingSessionHandle {
345 disconnect_tx: Some(disconnect_tx),
346 direction: Direction::Incoming,
347 };
348 self.pending_sessions.insert(session_id, handle);
349 self.counter.inc_pending_inbound();
350 Ok(session_id)
351 }
352
353 pub fn dial_outbound(&mut self, remote_addr: SocketAddr, remote_peer_id: PeerId) {
356 if let Err(err) = self.try_dial_outbound(remote_addr, remote_peer_id) {
357 trace!(target: "net", %err, "Outgoing connection rejected, capacity already reached.");
358 }
359 }
360
361 pub(crate) fn try_dial_outbound(
363 &mut self,
364 remote_addr: SocketAddr,
365 remote_peer_id: PeerId,
366 ) -> Result<(), ExceedsSessionLimit> {
367 self.counter.ensure_pending_outbound()?;
368 let session_id = self.next_id();
369 let (disconnect_tx, disconnect_rx) = oneshot::channel();
370 let pending_events = self.pending_sessions_tx.clone();
371 let secret_key = self.secret_key;
372 let hello_message = self.hello_message.clone();
373 let fork_filter = self.fork_filter.clone();
374 let status = self.status;
375 let extra_handlers = self.extra_protocols.on_outgoing(remote_addr, remote_peer_id);
376 self.spawn(pending_session_with_timeout(
377 self.pending_session_timeout,
378 session_id,
379 remote_addr,
380 Direction::Outgoing(remote_peer_id),
381 pending_events.clone(),
382 start_pending_outbound_session(
383 self.handshake.clone(),
384 self.eth_max_message_size,
385 self.p2p_max_payload_size,
386 disconnect_rx,
387 pending_events,
388 session_id,
389 remote_addr,
390 remote_peer_id,
391 secret_key,
392 hello_message,
393 status,
394 fork_filter,
395 extra_handlers,
396 ),
397 ));
398
399 let handle = PendingSessionHandle {
400 disconnect_tx: Some(disconnect_tx),
401 direction: Direction::Outgoing(remote_peer_id),
402 };
403 self.pending_sessions.insert(session_id, handle);
404 self.counter.inc_pending_outbound();
405 Ok(())
406 }
407
408 pub fn disconnect(&self, node: PeerId, reason: Option<DisconnectReason>) {
413 if let Some(session) = self.active_sessions.get(&node) {
414 session.disconnect(reason);
415 }
416 }
417
418 pub fn disconnect_all(&self, reason: Option<DisconnectReason>) {
423 for session in self.active_sessions.values() {
424 session.disconnect(reason);
425 }
426 }
427
428 pub fn disconnect_all_pending(&mut self) {
430 for session in self.pending_sessions.values_mut() {
431 session.disconnect();
432 }
433 }
434
435 pub fn send_message(&self, peer_id: &PeerId, msg: PeerMessage<N>) {
442 if let Some(session) = self.active_sessions.get(peer_id) &&
443 !session.commands.send_message(msg)
444 {
445 self.metrics.total_outgoing_peer_messages_dropped.increment(1);
446 }
447 }
448
449 fn remove_pending_session(&mut self, id: &SessionId) -> Option<PendingSessionHandle> {
451 let session = self.pending_sessions.remove(id)?;
452 self.counter.dec_pending(&session.direction);
453 Some(session)
454 }
455
456 fn cancel_pending_outbound(&mut self, peer_id: PeerId) {
458 self.pending_sessions.retain(|_, session| {
459 if session.direction == Direction::Outgoing(peer_id) {
460 session.disconnect();
461 self.counter.dec_pending(&session.direction);
462 return false
463 }
464 true
465 });
466 }
467
468 fn disconnect_pending_connection(&self, conn: EthRlpxConnection<N>, reason: DisconnectReason) {
470 self.spawn(async move {
471 let _ =
472 tokio::time::timeout(HANDSHAKE_TIMEOUT, conn.into_inner().disconnect(reason)).await;
473 });
474 }
475
476 fn remove_active_session(&mut self, id: &PeerId) -> Option<ActiveSessionHandle<N>> {
478 let session = self.active_sessions.remove(id)?;
479 self.counter.dec_active(&session.direction);
480 Some(session)
481 }
482
483 pub(crate) fn try_disconnect_incoming_connection(
487 &self,
488 stream: TcpStream,
489 reason: DisconnectReason,
490 ) {
491 if !self.disconnections_counter.has_capacity() {
492 return
494 }
495
496 let guard = self.disconnections_counter.clone();
497 let secret_key = self.secret_key;
498
499 self.spawn(async move {
500 trace!(
501 target: "net::session",
502 "gracefully disconnecting incoming connection"
503 );
504 if let Ok(stream) = get_ecies_stream(stream, secret_key, Direction::Incoming).await {
505 let mut unauth = UnauthedP2PStream::new(stream);
506 let _ = unauth.send_disconnect(reason).await;
507 drop(guard);
508 }
509 });
510 }
511
512 pub(crate) fn poll(
520 &mut self,
521 cx: &mut Context<'_>,
522 inbound_session_rejection: impl Fn(PeerId) -> Option<DisconnectReason>,
523 ) -> Poll<SessionEvent<N>> {
524 if self.active_events_in_row >= DEFAULT_BUDGET_PRIORITIZED_EVENTS {
525 self.active_events_in_row = 0;
526 if let Poll::Ready(event) =
527 self.poll_pending_session_event(cx, &inbound_session_rejection)
528 {
529 return Poll::Ready(event)
530 }
531 }
532
533 if let Poll::Ready(event) = self.poll_active_session_event(cx) {
534 self.active_events_in_row += 1;
535 return Poll::Ready(event)
536 }
537
538 self.active_events_in_row = 0;
539 self.poll_pending_session_event(cx, inbound_session_rejection)
540 }
541
542 fn poll_active_session_event(&mut self, cx: &mut Context<'_>) -> Poll<SessionEvent<N>> {
544 match self.active_session_rx.poll_next_unpin(cx) {
545 Poll::Pending => {}
546 Poll::Ready(None) => {
547 unreachable!("Manager holds both channel halves.")
548 }
549 Poll::Ready(Some(event)) => {
550 return match event {
551 ActiveSessionMessage::Disconnected { peer_id, remote_addr } => {
552 trace!(
553 target: "net::session",
554 ?peer_id,
555 "gracefully disconnected active session."
556 );
557 self.remove_active_session(&peer_id);
558 Poll::Ready(SessionEvent::Disconnected { peer_id, remote_addr })
559 }
560 ActiveSessionMessage::ClosedOnConnectionError {
561 peer_id,
562 remote_addr,
563 error,
564 } => {
565 trace!(target: "net::session", ?peer_id, %error,"closed session.");
566 self.remove_active_session(&peer_id);
567 Poll::Ready(SessionEvent::SessionClosedOnConnectionError {
568 remote_addr,
569 peer_id,
570 error,
571 })
572 }
573 ActiveSessionMessage::ValidMessage { peer_id, message } => {
574 Poll::Ready(SessionEvent::ValidMessage { peer_id, message })
575 }
576 ActiveSessionMessage::BadMessage { peer_id } => {
577 Poll::Ready(SessionEvent::BadMessage { peer_id })
578 }
579 ActiveSessionMessage::ProtocolBreach { peer_id } => {
580 Poll::Ready(SessionEvent::ProtocolBreach { peer_id })
581 }
582 }
583 }
584 }
585 Poll::Pending
586 }
587
588 fn poll_pending_session_event(
590 &mut self,
591 cx: &mut Context<'_>,
592 inbound_session_rejection: impl Fn(PeerId) -> Option<DisconnectReason>,
593 ) -> Poll<SessionEvent<N>> {
594 let event = loop {
595 let event = match self.pending_session_rx.poll_next_unpin(cx) {
596 Poll::Pending => return Poll::Pending,
597 Poll::Ready(None) => unreachable!("Manager holds both channel halves."),
598 Poll::Ready(Some(event)) => event,
599 };
600 let session_id = match &event {
601 PendingSessionEvent::Established { session_id, .. } |
602 PendingSessionEvent::Disconnected { session_id, .. } |
603 PendingSessionEvent::OutgoingConnectionError { session_id, .. } |
604 PendingSessionEvent::EciesAuthError { session_id, .. } => session_id,
605 };
606 if self.remove_pending_session(session_id).is_some() {
608 break event
609 }
610 if let PendingSessionEvent::Established { conn, .. } = event {
611 self.disconnect_pending_connection(conn, DisconnectReason::AlreadyConnected);
612 }
613 };
614 match event {
615 PendingSessionEvent::Established {
616 session_id,
617 remote_addr,
618 local_addr,
619 peer_id,
620 capabilities,
621 mut conn,
622 status,
623 direction,
624 client_id,
625 peer_listen_port,
626 } => {
627 if self.active_sessions.contains_key(&peer_id) {
629 trace!(
630 target: "net::session",
631 ?session_id,
632 ?remote_addr,
633 ?peer_id,
634 ?direction,
635 "already connected"
636 );
637
638 self.disconnect_pending_connection(conn, DisconnectReason::AlreadyConnected);
639
640 return Poll::Ready(SessionEvent::AlreadyConnected {
641 peer_id,
642 remote_addr,
643 direction,
644 })
645 }
646
647 if direction.is_incoming() {
648 if let Some(reason) = inbound_session_rejection(peer_id) {
649 self.disconnect_pending_connection(conn, reason);
650 return Poll::Ready(SessionEvent::IncomingPendingSessionClosed {
651 remote_addr,
652 error: None,
653 })
654 }
655 self.cancel_pending_outbound(peer_id);
656 }
657
658 let (commands_tx, commands_rx) = mpsc::channel(self.session_command_buffer);
659 let (unbounded_tx, unbounded_rx) = mpsc::unbounded_channel();
660
661 let (to_session_tx, messages_rx) = mpsc::channel(self.session_command_buffer);
662
663 let messages = PeerRequestSender::new(peer_id, to_session_tx);
664
665 let timeout = Arc::new(AtomicU64::new(
666 self.initial_internal_request_timeout.as_millis() as u64,
667 ));
668
669 let version = conn.version();
671
672 let range_update_interval = (conn.version() >= EthVersion::Eth69).then(|| {
677 let start = tokio::time::Instant::now() + RANGE_UPDATE_INTERVAL;
678 let mut interval = tokio::time::interval_at(start, RANGE_UPDATE_INTERVAL);
679 interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
680 interval
681 });
682
683 let broadcast_items = BroadcastItemCounter::new();
689 let remote_range_info = status.block_range_update().map(|update| {
690 BlockRangeInfo::new(update.earliest, update.latest, update.latest_hash)
691 });
692
693 if self.reject_block_announcements {
694 conn.set_reject_block_announcements(true);
695 }
696
697 let session = ActiveSession {
698 next_id: 0,
699 remote_peer_id: peer_id,
700 remote_addr,
701 remote_capabilities: Arc::clone(&capabilities),
702 session_id,
703 commands_rx: ReceiverStream::new(commands_rx),
704 unbounded_rx,
705 unbounded_broadcast_msgs: self.metrics.total_unbounded_broadcast_msgs.clone(),
706 to_session_manager: self.active_session_tx.clone(),
707 pending_message_to_session: None,
708 internal_request_rx: ReceiverStream::new(messages_rx).fuse(),
709 inflight_requests: Default::default(),
710 conn,
711 queued_outgoing: QueuedOutgoingMessages::new(
712 self.metrics.queued_outgoing_messages.clone(),
713 broadcast_items.clone(),
714 ),
715 received_requests_from_remote: Default::default(),
716 internal_request_timeout_interval: request_timeout_interval(
717 self.initial_internal_request_timeout,
718 ),
719 internal_request_timeout: Arc::clone(&timeout),
720 protocol_breach_request_timeout: self.protocol_breach_request_timeout,
721 terminate_message: None,
722 range_info: remote_range_info.clone(),
723 local_range_info: self.local_range_info.clone(),
724 range_update_interval,
725 last_sent_latest_block: None,
726 };
727
728 let supports_snap = session.conn.supports_snap();
729 self.spawn(session);
730
731 let client_version = client_id.into();
732 let handle = ActiveSessionHandle {
733 status: status.clone(),
734 direction,
735 session_id,
736 remote_id: peer_id,
737 version,
738 established: Instant::now(),
739 capabilities: Arc::clone(&capabilities),
740 commands: SessionCommandSender::new(commands_tx, unbounded_tx, broadcast_items),
741 client_version: Arc::clone(&client_version),
742 remote_addr,
743 local_addr,
744 peer_listen_port,
745 };
746
747 self.active_sessions.insert(peer_id, handle);
748 self.counter.inc_active(&direction);
749
750 if direction.is_outgoing() {
751 self.metrics.total_dial_successes.increment(1);
752 }
753
754 Poll::Ready(SessionEvent::SessionEstablished {
755 peer_id,
756 remote_addr,
757 client_version,
758 version,
759 capabilities,
760 status,
761 messages,
762 direction,
763 timeout,
764 range_info: remote_range_info,
765 supports_snap,
766 })
767 }
768 PendingSessionEvent::Disconnected { remote_addr, session_id, direction, error } => {
769 trace!(
770 target: "net::session",
771 ?session_id,
772 ?remote_addr,
773 ?error,
774 "disconnected pending session"
775 );
776 match direction {
777 Direction::Incoming => {
778 Poll::Ready(SessionEvent::IncomingPendingSessionClosed {
779 remote_addr,
780 error,
781 })
782 }
783 Direction::Outgoing(peer_id) => {
784 Poll::Ready(SessionEvent::OutgoingPendingSessionClosed {
785 remote_addr,
786 peer_id,
787 error,
788 })
789 }
790 }
791 }
792 PendingSessionEvent::OutgoingConnectionError {
793 remote_addr,
794 session_id,
795 peer_id,
796 error,
797 } => {
798 trace!(
799 target: "net::session",
800 %error,
801 ?session_id,
802 ?remote_addr,
803 ?peer_id,
804 "connection refused"
805 );
806 Poll::Ready(SessionEvent::OutgoingConnectionError { remote_addr, peer_id, error })
807 }
808 PendingSessionEvent::EciesAuthError { remote_addr, session_id, error, direction } => {
809 trace!(
810 target: "net::session",
811 %error,
812 ?session_id,
813 ?remote_addr,
814 "ecies auth failed"
815 );
816 match direction {
817 Direction::Incoming => {
818 Poll::Ready(SessionEvent::IncomingPendingSessionClosed {
819 remote_addr,
820 error: Some(PendingSessionHandshakeError::Ecies(error)),
821 })
822 }
823 Direction::Outgoing(peer_id) => {
824 Poll::Ready(SessionEvent::OutgoingPendingSessionClosed {
825 remote_addr,
826 peer_id,
827 error: Some(PendingSessionHandshakeError::Ecies(error)),
828 })
829 }
830 }
831 }
832 }
833 }
834
835 pub(crate) fn update_advertised_block_range(&mut self, block_range_update: BlockRangeUpdate) {
843 self.status.earliest_block = Some(block_range_update.earliest);
844 self.status.latest_block = Some(block_range_update.latest);
845 self.status.blockhash = block_range_update.latest_hash;
846
847 self.local_range_info.update(
849 block_range_update.earliest,
850 block_range_update.latest,
851 block_range_update.latest_hash,
852 );
853 }
854}
855
856#[derive(Default, Debug, Clone)]
858struct DisconnectionsCounter(Arc<()>);
859
860impl DisconnectionsCounter {
861 const MAX_CONCURRENT_GRACEFUL_DISCONNECTIONS: usize = 15;
862
863 fn has_capacity(&self) -> bool {
866 Arc::strong_count(&self.0) <= Self::MAX_CONCURRENT_GRACEFUL_DISCONNECTIONS
867 }
868}
869
870#[derive(Debug)]
872pub enum SessionEvent<N: NetworkPrimitives> {
873 SessionEstablished {
877 peer_id: PeerId,
879 remote_addr: SocketAddr,
881 client_version: Arc<str>,
883 capabilities: Arc<Capabilities>,
885 version: EthVersion,
887 status: Arc<UnifiedStatus>,
889 messages: PeerRequestSender<PeerRequest<N>>,
891 direction: Direction,
893 timeout: Arc<AtomicU64>,
896 range_info: Option<BlockRangeInfo>,
898 supports_snap: bool,
900 },
901 AlreadyConnected {
903 peer_id: PeerId,
905 remote_addr: SocketAddr,
907 direction: Direction,
909 },
910 ValidMessage {
912 peer_id: PeerId,
914 message: PeerMessage<N>,
916 },
917 BadMessage {
919 peer_id: PeerId,
921 },
922 ProtocolBreach {
924 peer_id: PeerId,
926 },
927 IncomingPendingSessionClosed {
929 remote_addr: SocketAddr,
931 error: Option<PendingSessionHandshakeError>,
933 },
934 OutgoingPendingSessionClosed {
936 remote_addr: SocketAddr,
938 peer_id: PeerId,
940 error: Option<PendingSessionHandshakeError>,
942 },
943 OutgoingConnectionError {
945 remote_addr: SocketAddr,
947 peer_id: PeerId,
949 error: io::Error,
951 },
952 SessionClosedOnConnectionError {
954 peer_id: PeerId,
956 remote_addr: SocketAddr,
958 error: EthStreamError,
960 },
961 Disconnected {
963 peer_id: PeerId,
965 remote_addr: SocketAddr,
967 },
968}
969
970#[derive(Debug, thiserror::Error)]
972pub enum PendingSessionHandshakeError {
973 #[error(transparent)]
975 Eth(EthStreamError),
976 #[error(transparent)]
978 Ecies(ECIESError),
979 #[error("authentication timed out")]
981 Timeout,
982 #[error("Mandatory extra capability unsupported")]
984 UnsupportedExtraCapability,
985 #[error("unexpected identity in hello message: {0}")]
988 UnexpectedHandshakeIdentity(GotExpectedBoxed<PeerId>),
989}
990
991impl PendingSessionHandshakeError {
992 pub const fn as_disconnected(&self) -> Option<DisconnectReason> {
995 match self {
996 Self::Eth(eth_err) => eth_err.as_disconnected(),
997 Self::UnexpectedHandshakeIdentity(_) => {
998 Some(DisconnectReason::UnexpectedHandshakeIdentity)
999 }
1000 _ => None,
1001 }
1002 }
1003}
1004
1005#[derive(Debug, Clone, thiserror::Error)]
1008#[error("session limit reached {0}")]
1009pub struct ExceedsSessionLimit(pub(crate) u32);
1010
1011pub(crate) async fn pending_session_with_timeout<F, N: NetworkPrimitives>(
1013 timeout: Duration,
1014 session_id: SessionId,
1015 remote_addr: SocketAddr,
1016 direction: Direction,
1017 events: mpsc::Sender<PendingSessionEvent<N>>,
1018 f: F,
1019) where
1020 F: Future<Output = ()>,
1021{
1022 if tokio::time::timeout(timeout, f).await.is_err() {
1023 trace!(target: "net::session", ?remote_addr, ?direction, "pending session timed out");
1024 let event = PendingSessionEvent::Disconnected {
1025 remote_addr,
1026 session_id,
1027 direction,
1028 error: Some(PendingSessionHandshakeError::Timeout),
1029 };
1030 let _ = events.send(event).await;
1031 }
1032}
1033
1034#[expect(clippy::too_many_arguments)]
1038pub(crate) async fn start_pending_incoming_session<N: NetworkPrimitives>(
1039 handshake: Arc<dyn EthRlpxHandshake>,
1040 eth_max_message_size: usize,
1041 p2p_max_payload_size: usize,
1042 disconnect_rx: oneshot::Receiver<()>,
1043 session_id: SessionId,
1044 stream: TcpStream,
1045 events: mpsc::Sender<PendingSessionEvent<N>>,
1046 remote_addr: SocketAddr,
1047 secret_key: SecretKey,
1048 hello: HelloMessageWithProtocols,
1049 status: UnifiedStatus,
1050 fork_filter: ForkFilter,
1051 extra_handlers: RlpxSubProtocolHandlers,
1052) {
1053 authenticate(
1054 handshake,
1055 eth_max_message_size,
1056 p2p_max_payload_size,
1057 disconnect_rx,
1058 events,
1059 stream,
1060 session_id,
1061 remote_addr,
1062 secret_key,
1063 Direction::Incoming,
1064 hello,
1065 status,
1066 fork_filter,
1067 extra_handlers,
1068 )
1069 .await
1070}
1071
1072#[instrument(level = "trace", target = "net::network", skip_all, fields(%remote_addr, peer_id = ?remote_peer_id))]
1074#[expect(clippy::too_many_arguments)]
1075async fn start_pending_outbound_session<N: NetworkPrimitives>(
1076 handshake: Arc<dyn EthRlpxHandshake>,
1077 eth_max_message_size: usize,
1078 p2p_max_payload_size: usize,
1079 mut disconnect_rx: oneshot::Receiver<()>,
1080 events: mpsc::Sender<PendingSessionEvent<N>>,
1081 session_id: SessionId,
1082 remote_addr: SocketAddr,
1083 remote_peer_id: PeerId,
1084 secret_key: SecretKey,
1085 hello: HelloMessageWithProtocols,
1086 status: UnifiedStatus,
1087 fork_filter: ForkFilter,
1088 extra_handlers: RlpxSubProtocolHandlers,
1089) {
1090 let connection = tokio::select! {
1091 _ = &mut disconnect_rx => {
1092 let _ = events.send(PendingSessionEvent::Disconnected {
1093 remote_addr,
1094 session_id,
1095 direction: Direction::Outgoing(remote_peer_id),
1096 error: None,
1097 }).await;
1098 return
1099 },
1100 connection = TcpStream::connect(remote_addr) => connection,
1101 };
1102 let stream = match connection {
1103 Ok(stream) => {
1104 if let Err(err) = stream.set_nodelay(true) {
1105 tracing::warn!(target: "net::session", "set nodelay failed: {:?}", err);
1106 }
1107 stream
1108 }
1109 Err(error) => {
1110 let _ = events
1111 .send(PendingSessionEvent::OutgoingConnectionError {
1112 remote_addr,
1113 session_id,
1114 peer_id: remote_peer_id,
1115 error,
1116 })
1117 .await;
1118 return
1119 }
1120 };
1121 authenticate(
1122 handshake,
1123 eth_max_message_size,
1124 p2p_max_payload_size,
1125 disconnect_rx,
1126 events,
1127 stream,
1128 session_id,
1129 remote_addr,
1130 secret_key,
1131 Direction::Outgoing(remote_peer_id),
1132 hello,
1133 status,
1134 fork_filter,
1135 extra_handlers,
1136 )
1137 .await
1138}
1139
1140#[expect(clippy::too_many_arguments)]
1142async fn authenticate<N: NetworkPrimitives>(
1143 handshake: Arc<dyn EthRlpxHandshake>,
1144 eth_max_message_size: usize,
1145 p2p_max_payload_size: usize,
1146 disconnect_rx: oneshot::Receiver<()>,
1147 events: mpsc::Sender<PendingSessionEvent<N>>,
1148 stream: TcpStream,
1149 session_id: SessionId,
1150 remote_addr: SocketAddr,
1151 secret_key: SecretKey,
1152 direction: Direction,
1153 hello: HelloMessageWithProtocols,
1154 status: UnifiedStatus,
1155 fork_filter: ForkFilter,
1156 extra_handlers: RlpxSubProtocolHandlers,
1157) {
1158 let local_addr = stream.local_addr().ok();
1159 let auth = async move {
1160 let stream = match get_ecies_stream(stream, secret_key, direction).await {
1161 Ok(stream) => stream,
1162 Err(error) => {
1163 return PendingSessionEvent::EciesAuthError {
1164 remote_addr,
1165 session_id,
1166 error,
1167 direction,
1168 }
1169 }
1170 };
1171
1172 let unauthed = UnauthedP2PStream::new(stream).with_max_payload_size(p2p_max_payload_size);
1173
1174 authenticate_stream(
1175 handshake,
1176 eth_max_message_size,
1177 unauthed,
1178 session_id,
1179 remote_addr,
1180 local_addr,
1181 direction,
1182 hello,
1183 status,
1184 fork_filter,
1185 extra_handlers,
1186 )
1187 .await
1188 }
1189 .boxed();
1190
1191 match futures::future::select(disconnect_rx, auth).await {
1192 Either::Left((_, _)) => {
1193 let _ = events
1194 .send(PendingSessionEvent::Disconnected {
1195 remote_addr,
1196 session_id,
1197 direction,
1198 error: None,
1199 })
1200 .await;
1201 }
1202 Either::Right((res, _)) => {
1203 let _ = events.send(res).await;
1204 }
1205 }
1206}
1207
1208async fn get_ecies_stream<Io: AsyncRead + AsyncWrite + Unpin>(
1211 stream: Io,
1212 secret_key: SecretKey,
1213 direction: Direction,
1214) -> Result<ECIESStream<Io>, ECIESError> {
1215 match direction {
1216 Direction::Incoming => ECIESStream::incoming(stream, secret_key).await,
1217 Direction::Outgoing(remote_peer_id) => {
1218 ECIESStream::connect(stream, secret_key, remote_peer_id).await
1219 }
1220 }
1221}
1222
1223#[expect(clippy::too_many_arguments)]
1230async fn authenticate_stream<N: NetworkPrimitives>(
1231 handshake: Arc<dyn EthRlpxHandshake>,
1232 eth_max_message_size: usize,
1233 stream: UnauthedP2PStream<ECIESStream<TcpStream>>,
1234 session_id: SessionId,
1235 remote_addr: SocketAddr,
1236 local_addr: Option<SocketAddr>,
1237 direction: Direction,
1238 mut hello: HelloMessageWithProtocols,
1239 mut status: UnifiedStatus,
1240 fork_filter: ForkFilter,
1241 mut extra_handlers: RlpxSubProtocolHandlers,
1242) -> PendingSessionEvent<N> {
1243 extra_handlers.retain(|handler| hello.try_add_protocol(handler.protocol()).is_ok());
1245
1246 let authenticated_peer_id = stream.inner().remote_id();
1247
1248 let (mut p2p_stream, their_hello) = match stream.handshake(hello).await {
1250 Ok(stream_res) => stream_res,
1251 Err(err) => {
1252 return PendingSessionEvent::Disconnected {
1253 remote_addr,
1254 session_id,
1255 direction,
1256 error: Some(PendingSessionHandshakeError::Eth(err.into())),
1257 }
1258 }
1259 };
1260
1261 if their_hello.id != authenticated_peer_id {
1268 let _ = p2p_stream.disconnect(DisconnectReason::UnexpectedHandshakeIdentity).await;
1269
1270 return PendingSessionEvent::Disconnected {
1271 remote_addr,
1272 session_id,
1273 direction,
1274 error: Some(PendingSessionHandshakeError::UnexpectedHandshakeIdentity(
1275 GotExpected { got: their_hello.id, expected: authenticated_peer_id }.into(),
1276 )),
1277 }
1278 }
1279
1280 if !extra_handlers.is_empty() {
1282 while let Some(pos) = extra_handlers.iter().position(|handler| {
1284 p2p_stream
1285 .shared_capabilities()
1286 .ensure_matching_capability(&handler.protocol().cap)
1287 .is_err()
1288 }) {
1289 let handler = extra_handlers.remove(pos);
1290 if handler.on_unsupported_by_peer(
1291 p2p_stream.shared_capabilities(),
1292 direction,
1293 authenticated_peer_id,
1294 ) == OnNotSupported::Disconnect
1295 {
1296 return PendingSessionEvent::Disconnected {
1297 remote_addr,
1298 session_id,
1299 direction,
1300 error: Some(PendingSessionHandshakeError::UnsupportedExtraCapability),
1301 };
1302 }
1303 }
1304 }
1305
1306 let eth_version = match p2p_stream.shared_capabilities().eth_version() {
1308 Ok(version) => version,
1309 Err(err) => {
1310 return PendingSessionEvent::Disconnected {
1311 remote_addr,
1312 session_id,
1313 direction,
1314 error: Some(PendingSessionHandshakeError::Eth(err.into())),
1315 }
1316 }
1317 };
1318
1319 status.set_eth_version(eth_version);
1321
1322 let (conn, their_status) = if p2p_stream.shared_capabilities().len() == 1 {
1323 match handshake
1328 .handshake(&mut p2p_stream, status, fork_filter.clone(), HANDSHAKE_TIMEOUT)
1329 .await
1330 {
1331 Ok(their_status) => {
1332 let eth_stream =
1333 EthStream::with_max_message_size(eth_version, p2p_stream, eth_max_message_size);
1334 (eth_stream.into(), their_status)
1335 }
1336 Err(err) => {
1337 return PendingSessionEvent::Disconnected {
1338 remote_addr,
1339 session_id,
1340 direction,
1341 error: Some(PendingSessionHandshakeError::Eth(err)),
1342 }
1343 }
1344 }
1345 } else if p2p_stream.shared_capabilities().is_exact_eth_snap_v2() {
1346 match EthSnapStream::handshake(
1351 p2p_stream,
1352 status,
1353 fork_filter,
1354 handshake,
1355 eth_max_message_size,
1356 )
1357 .await
1358 {
1359 Ok((stream, their_status)) => (stream.into(), their_status),
1360 Err(err) => {
1361 return PendingSessionEvent::Disconnected {
1362 remote_addr,
1363 session_id,
1364 direction,
1365 error: Some(PendingSessionHandshakeError::Eth(err)),
1366 }
1367 }
1368 }
1369 } else {
1370 let mut multiplex_stream = RlpxProtocolMultiplexer::new(p2p_stream);
1371 let (protocol_start, protocol_ready) = watch::channel(false);
1372
1373 for handler in extra_handlers.into_iter() {
1376 let protocol = handler.protocol();
1377 let limits = handler.inbound_limits();
1378 let remote_peer_id = authenticated_peer_id;
1379 let mut protocol_ready = protocol_ready.clone();
1380 multiplex_stream
1383 .install_protocol_with_limits(&protocol.cap, limits, move |conn| {
1384 stream::once(async move {
1385 protocol_ready
1387 .wait_for(|started| *started)
1388 .await
1389 .is_ok()
1390 .then(|| handler.into_connection(direction, remote_peer_id, conn))
1391 })
1392 .filter_map(future::ready)
1393 .flatten()
1394 })
1395 .expect("remaining handler capability was negotiated");
1396 }
1397
1398 let (multiplex_stream, their_status) = match multiplex_stream
1399 .into_eth_satellite_stream(status, fork_filter, handshake, eth_max_message_size)
1400 .await
1401 {
1402 Ok((multiplex_stream, their_status)) => (multiplex_stream, their_status),
1403 Err(err) => {
1404 return PendingSessionEvent::Disconnected {
1405 remote_addr,
1406 session_id,
1407 direction,
1408 error: Some(PendingSessionHandshakeError::Eth(err)),
1409 }
1410 }
1411 };
1412 protocol_start.send_replace(true);
1413
1414 (multiplex_stream.into(), their_status)
1415 };
1416
1417 let peer_listen_port = (their_hello.port != 0).then_some(their_hello.port);
1419
1420 PendingSessionEvent::Established {
1421 session_id,
1422 remote_addr,
1423 local_addr,
1424 peer_id: authenticated_peer_id,
1425 capabilities: Arc::new(Capabilities::from(their_hello.capabilities)),
1426 status: Arc::new(their_status),
1427 conn,
1428 direction,
1429 client_id: their_hello.client_version,
1430 peer_listen_port,
1431 }
1432}