1mod active;
4mod conn;
5mod counter;
6mod handle;
7#[cfg(test)]
8mod tests;
9mod types;
10pub use types::BlockRangeInfo;
11
12use crate::{
13 message::PeerMessage,
14 metrics::SessionManagerMetrics,
15 protocol::{IntoRlpxSubProtocol, OnNotSupported, RlpxSubProtocolHandlers, RlpxSubProtocols},
16 session::active::ActiveSession,
17};
18use active::QueuedOutgoingMessages;
19use alloy_primitives::map::{FbBuildHasher, HashMap};
20use counter::SessionCounter;
21use futures::{
22 future::{self, Either},
23 io, stream, FutureExt, StreamExt,
24};
25use reth_ecies::{stream::ECIESStream, ECIESError};
26use reth_eth_wire::{
27 errors::EthStreamError, handshake::EthRlpxHandshake, multiplex::RlpxProtocolMultiplexer,
28 BlockRangeUpdate, Capabilities, DisconnectReason, EthSnapStream, EthStream, EthVersion,
29 HelloMessageWithProtocols, NetworkPrimitives, UnauthedP2PStream, UnifiedStatus,
30 HANDSHAKE_TIMEOUT,
31};
32use reth_ethereum_forks::{ForkFilter, ForkId, ForkTransition, Head};
33use reth_metrics::common::mpsc::MeteredPollSender;
34use reth_network_api::{PeerRequest, PeerRequestSender};
35use reth_network_peers::PeerId;
36use reth_network_types::SessionsConfig;
37use reth_primitives_traits::{GotExpected, GotExpectedBoxed};
38use reth_tasks::Runtime;
39use rustc_hash::FxHashMap;
40use secp256k1::SecretKey;
41use std::{
42 future::Future,
43 net::SocketAddr,
44 sync::{atomic::AtomicU64, Arc},
45 task::{Context, Poll},
46 time::{Duration, Instant},
47};
48use tokio::{
49 io::{AsyncRead, AsyncWrite},
50 net::TcpStream,
51 sync::{mpsc, oneshot, watch},
52};
53use tokio_stream::wrappers::ReceiverStream;
54use tokio_util::sync::PollSender;
55use tracing::{instrument, trace};
56
57use crate::session::active::{
58 request_timeout_interval, BroadcastItemCounter, RANGE_UPDATE_INTERVAL,
59};
60pub use conn::EthRlpxConnection;
61use handle::SessionCommandSender;
62pub use handle::{
63 ActiveSessionHandle, ActiveSessionMessage, PendingSessionEvent, PendingSessionHandle,
64 SessionCommand,
65};
66pub use reth_network_api::{Direction, PeerInfo};
67
68#[derive(Debug, Clone, Copy, PartialOrd, PartialEq, Eq, Hash)]
70pub struct SessionId(usize);
71
72#[must_use = "Session Manager must be polled to process session events."]
74#[derive(Debug)]
75pub struct SessionManager<N: NetworkPrimitives> {
76 next_id: usize,
78 counter: SessionCounter,
80 initial_internal_request_timeout: Duration,
83 protocol_breach_request_timeout: Duration,
86 pending_session_timeout: Duration,
88 secret_key: SecretKey,
90 status: UnifiedStatus,
92 hello_message: HelloMessageWithProtocols,
94 fork_filter: ForkFilter,
96 session_command_buffer: usize,
98 executor: Runtime,
100 pending_sessions: FxHashMap<SessionId, PendingSessionHandle>,
105 active_sessions: HashMap<PeerId, ActiveSessionHandle<N>, FbBuildHasher<64>>,
107 pending_sessions_tx: mpsc::Sender<PendingSessionEvent<N>>,
112 pending_session_rx: ReceiverStream<PendingSessionEvent<N>>,
114 active_session_tx: MeteredPollSender<ActiveSessionMessage<N>>,
119 active_session_rx: ReceiverStream<ActiveSessionMessage<N>>,
121 extra_protocols: RlpxSubProtocols,
123 disconnections_counter: DisconnectionsCounter,
125 metrics: SessionManagerMetrics,
127 handshake: Arc<dyn EthRlpxHandshake>,
129 eth_max_message_size: usize,
131 local_range_info: BlockRangeInfo,
134 reject_block_announcements: bool,
137}
138
139impl<N: NetworkPrimitives> SessionManager<N> {
142 #[expect(clippy::too_many_arguments)]
144 pub fn new(
145 secret_key: SecretKey,
146 config: SessionsConfig,
147 executor: Runtime,
148 status: UnifiedStatus,
149 hello_message: HelloMessageWithProtocols,
150 fork_filter: ForkFilter,
151 extra_protocols: RlpxSubProtocols,
152 handshake: Arc<dyn EthRlpxHandshake>,
153 eth_max_message_size: usize,
154 reject_block_announcements: bool,
155 ) -> Self {
156 let (pending_sessions_tx, pending_sessions_rx) = mpsc::channel(config.session_event_buffer);
157 let (active_session_tx, active_session_rx) = mpsc::channel(config.session_event_buffer);
158 let active_session_tx = PollSender::new(active_session_tx);
159
160 let local_range_info = BlockRangeInfo::new(
162 status.earliest_block.unwrap_or_default(),
163 status.latest_block.unwrap_or_default(),
164 status.blockhash,
165 );
166
167 Self {
168 next_id: 0,
169 counter: SessionCounter::new(config.limits),
170 initial_internal_request_timeout: config.initial_internal_request_timeout,
171 protocol_breach_request_timeout: config.protocol_breach_request_timeout,
172 pending_session_timeout: config.pending_session_timeout,
173 secret_key,
174 status,
175 hello_message,
176 fork_filter,
177 session_command_buffer: config.session_command_buffer,
178 executor,
179 pending_sessions: Default::default(),
180 active_sessions: Default::default(),
181 pending_sessions_tx,
182 pending_session_rx: ReceiverStream::new(pending_sessions_rx),
183 active_session_tx: MeteredPollSender::new(active_session_tx, "network_active_session"),
184 active_session_rx: ReceiverStream::new(active_session_rx),
185 extra_protocols,
186 disconnections_counter: Default::default(),
187 metrics: Default::default(),
188 handshake,
189 eth_max_message_size,
190 local_range_info,
191 reject_block_announcements,
192 }
193 }
194
195 pub(crate) const fn fork_id(&self) -> ForkId {
197 self.fork_filter.current()
198 }
199
200 pub fn is_valid_fork_id(&self, fork_id: ForkId) -> bool {
203 self.fork_filter.validate(fork_id).is_ok()
204 }
205
206 const fn next_id(&mut self) -> SessionId {
208 let id = self.next_id;
209 self.next_id += 1;
210 SessionId(id)
211 }
212
213 pub const fn status(&self) -> UnifiedStatus {
215 self.status
216 }
217
218 pub const fn secret_key(&self) -> SecretKey {
220 self.secret_key
221 }
222
223 pub const fn active_sessions(
225 &self,
226 ) -> &HashMap<PeerId, ActiveSessionHandle<N>, FbBuildHasher<64>> {
227 &self.active_sessions
228 }
229
230 pub fn hello_message(&self) -> HelloMessageWithProtocols {
232 self.hello_message.clone()
233 }
234
235 pub(crate) fn add_rlpx_sub_protocol(&mut self, protocol: impl IntoRlpxSubProtocol) {
237 self.extra_protocols.push(protocol)
238 }
239
240 #[inline]
242 pub(crate) fn num_pending_connections(&self) -> usize {
243 self.pending_sessions.len()
244 }
245
246 fn spawn<F>(&self, f: F)
249 where
250 F: Future<Output = ()> + Send + 'static,
251 {
252 self.executor.spawn_task(f);
253 }
254
255 pub(crate) fn on_status_update(&mut self, head: Head) -> Option<ForkTransition> {
260 self.status.blockhash = head.hash;
261 self.status.total_difficulty = Some(head.total_difficulty);
262 let transition = self.fork_filter.set_head(head);
263 self.status.forkid = self.fork_filter.current();
264 self.status.latest_block = Some(head.number);
265
266 transition
267 }
268
269 pub(crate) fn set_fork_filter(&mut self, fork_filter: ForkFilter) -> ForkId {
281 self.fork_filter = fork_filter;
282 self.status.forkid = self.fork_filter.current();
283 self.status.forkid
284 }
285
286 pub(crate) fn on_incoming(
291 &mut self,
292 stream: TcpStream,
293 remote_addr: SocketAddr,
294 ) -> Result<SessionId, ExceedsSessionLimit> {
295 self.counter.ensure_pending_inbound()?;
296
297 let session_id = self.next_id();
298
299 trace!(
300 target: "net::session",
301 ?remote_addr,
302 ?session_id,
303 "new pending incoming session"
304 );
305
306 let (disconnect_tx, disconnect_rx) = oneshot::channel();
307 let pending_events = self.pending_sessions_tx.clone();
308 let secret_key = self.secret_key;
309 let hello_message = self.hello_message.clone();
310 let status = self.status;
311 let fork_filter = self.fork_filter.clone();
312 let extra_handlers = self.extra_protocols.on_incoming(remote_addr);
313 self.spawn(pending_session_with_timeout(
314 self.pending_session_timeout,
315 session_id,
316 remote_addr,
317 Direction::Incoming,
318 pending_events.clone(),
319 start_pending_incoming_session(
320 self.handshake.clone(),
321 self.eth_max_message_size,
322 disconnect_rx,
323 session_id,
324 stream,
325 pending_events,
326 remote_addr,
327 secret_key,
328 hello_message,
329 status,
330 fork_filter,
331 extra_handlers,
332 ),
333 ));
334
335 let handle = PendingSessionHandle {
336 disconnect_tx: Some(disconnect_tx),
337 direction: Direction::Incoming,
338 };
339 self.pending_sessions.insert(session_id, handle);
340 self.counter.inc_pending_inbound();
341 Ok(session_id)
342 }
343
344 pub fn dial_outbound(&mut self, remote_addr: SocketAddr, remote_peer_id: PeerId) {
347 if let Err(err) = self.try_dial_outbound(remote_addr, remote_peer_id) {
348 trace!(target: "net", %err, "Outgoing connection rejected, capacity already reached.");
349 }
350 }
351
352 pub(crate) fn try_dial_outbound(
354 &mut self,
355 remote_addr: SocketAddr,
356 remote_peer_id: PeerId,
357 ) -> Result<(), ExceedsSessionLimit> {
358 self.counter.ensure_pending_outbound()?;
359 let session_id = self.next_id();
360 let (disconnect_tx, disconnect_rx) = oneshot::channel();
361 let pending_events = self.pending_sessions_tx.clone();
362 let secret_key = self.secret_key;
363 let hello_message = self.hello_message.clone();
364 let fork_filter = self.fork_filter.clone();
365 let status = self.status;
366 let extra_handlers = self.extra_protocols.on_outgoing(remote_addr, remote_peer_id);
367 self.spawn(pending_session_with_timeout(
368 self.pending_session_timeout,
369 session_id,
370 remote_addr,
371 Direction::Outgoing(remote_peer_id),
372 pending_events.clone(),
373 start_pending_outbound_session(
374 self.handshake.clone(),
375 self.eth_max_message_size,
376 disconnect_rx,
377 pending_events,
378 session_id,
379 remote_addr,
380 remote_peer_id,
381 secret_key,
382 hello_message,
383 status,
384 fork_filter,
385 extra_handlers,
386 ),
387 ));
388
389 let handle = PendingSessionHandle {
390 disconnect_tx: Some(disconnect_tx),
391 direction: Direction::Outgoing(remote_peer_id),
392 };
393 self.pending_sessions.insert(session_id, handle);
394 self.counter.inc_pending_outbound();
395 Ok(())
396 }
397
398 pub fn disconnect(&self, node: PeerId, reason: Option<DisconnectReason>) {
403 if let Some(session) = self.active_sessions.get(&node) {
404 session.disconnect(reason);
405 }
406 }
407
408 pub fn disconnect_all(&self, reason: Option<DisconnectReason>) {
413 for session in self.active_sessions.values() {
414 session.disconnect(reason);
415 }
416 }
417
418 pub fn disconnect_all_pending(&mut self) {
420 for session in self.pending_sessions.values_mut() {
421 session.disconnect();
422 }
423 }
424
425 pub fn send_message(&self, peer_id: &PeerId, msg: PeerMessage<N>) {
432 if let Some(session) = self.active_sessions.get(peer_id) &&
433 !session.commands.send_message(msg)
434 {
435 self.metrics.total_outgoing_peer_messages_dropped.increment(1);
436 }
437 }
438
439 fn remove_pending_session(&mut self, id: &SessionId) -> Option<PendingSessionHandle> {
441 let session = self.pending_sessions.remove(id)?;
442 self.counter.dec_pending(&session.direction);
443 Some(session)
444 }
445
446 fn cancel_pending_outbound(&mut self, peer_id: PeerId) {
448 self.pending_sessions.retain(|_, session| {
449 if session.direction == Direction::Outgoing(peer_id) {
450 session.disconnect();
451 self.counter.dec_pending(&session.direction);
452 return false
453 }
454 true
455 });
456 }
457
458 fn disconnect_pending_connection(&self, conn: EthRlpxConnection<N>, reason: DisconnectReason) {
460 self.spawn(async move {
461 let _ =
462 tokio::time::timeout(HANDSHAKE_TIMEOUT, conn.into_inner().disconnect(reason)).await;
463 });
464 }
465
466 fn remove_active_session(&mut self, id: &PeerId) -> Option<ActiveSessionHandle<N>> {
468 let session = self.active_sessions.remove(id)?;
469 self.counter.dec_active(&session.direction);
470 Some(session)
471 }
472
473 pub(crate) fn try_disconnect_incoming_connection(
477 &self,
478 stream: TcpStream,
479 reason: DisconnectReason,
480 ) {
481 if !self.disconnections_counter.has_capacity() {
482 return
484 }
485
486 let guard = self.disconnections_counter.clone();
487 let secret_key = self.secret_key;
488
489 self.spawn(async move {
490 trace!(
491 target: "net::session",
492 "gracefully disconnecting incoming connection"
493 );
494 if let Ok(stream) = get_ecies_stream(stream, secret_key, Direction::Incoming).await {
495 let mut unauth = UnauthedP2PStream::new(stream);
496 let _ = unauth.send_disconnect(reason).await;
497 drop(guard);
498 }
499 });
500 }
501
502 pub(crate) fn poll(
506 &mut self,
507 cx: &mut Context<'_>,
508 has_inbound_capacity: impl Fn(PeerId) -> bool,
509 ) -> Poll<SessionEvent<N>> {
510 match self.active_session_rx.poll_next_unpin(cx) {
512 Poll::Pending => {}
513 Poll::Ready(None) => {
514 unreachable!("Manager holds both channel halves.")
515 }
516 Poll::Ready(Some(event)) => {
517 return match event {
518 ActiveSessionMessage::Disconnected { peer_id, remote_addr } => {
519 trace!(
520 target: "net::session",
521 ?peer_id,
522 "gracefully disconnected active session."
523 );
524 self.remove_active_session(&peer_id);
525 Poll::Ready(SessionEvent::Disconnected { peer_id, remote_addr })
526 }
527 ActiveSessionMessage::ClosedOnConnectionError {
528 peer_id,
529 remote_addr,
530 error,
531 } => {
532 trace!(target: "net::session", ?peer_id, %error,"closed session.");
533 self.remove_active_session(&peer_id);
534 Poll::Ready(SessionEvent::SessionClosedOnConnectionError {
535 remote_addr,
536 peer_id,
537 error,
538 })
539 }
540 ActiveSessionMessage::ValidMessage { peer_id, message } => {
541 Poll::Ready(SessionEvent::ValidMessage { peer_id, message })
542 }
543 ActiveSessionMessage::BadMessage { peer_id } => {
544 Poll::Ready(SessionEvent::BadMessage { peer_id })
545 }
546 ActiveSessionMessage::ProtocolBreach { peer_id } => {
547 Poll::Ready(SessionEvent::ProtocolBreach { peer_id })
548 }
549 }
550 }
551 }
552
553 let event = loop {
555 let event = match self.pending_session_rx.poll_next_unpin(cx) {
556 Poll::Pending => return Poll::Pending,
557 Poll::Ready(None) => unreachable!("Manager holds both channel halves."),
558 Poll::Ready(Some(event)) => event,
559 };
560 let session_id = match &event {
561 PendingSessionEvent::Established { session_id, .. } |
562 PendingSessionEvent::Disconnected { session_id, .. } |
563 PendingSessionEvent::OutgoingConnectionError { session_id, .. } |
564 PendingSessionEvent::EciesAuthError { session_id, .. } => session_id,
565 };
566 if self.remove_pending_session(session_id).is_some() {
568 break event
569 }
570 if let PendingSessionEvent::Established { conn, .. } = event {
571 self.disconnect_pending_connection(conn, DisconnectReason::AlreadyConnected);
572 }
573 };
574 match event {
575 PendingSessionEvent::Established {
576 session_id,
577 remote_addr,
578 local_addr,
579 peer_id,
580 capabilities,
581 mut conn,
582 status,
583 direction,
584 client_id,
585 peer_listen_port,
586 } => {
587 if self.active_sessions.contains_key(&peer_id) {
589 trace!(
590 target: "net::session",
591 ?session_id,
592 ?remote_addr,
593 ?peer_id,
594 ?direction,
595 "already connected"
596 );
597
598 self.disconnect_pending_connection(conn, DisconnectReason::AlreadyConnected);
599
600 return Poll::Ready(SessionEvent::AlreadyConnected {
601 peer_id,
602 remote_addr,
603 direction,
604 })
605 }
606
607 if direction.is_incoming() {
608 if !has_inbound_capacity(peer_id) {
609 self.disconnect_pending_connection(conn, DisconnectReason::TooManyPeers);
610 return Poll::Ready(SessionEvent::IncomingPendingSessionClosed {
611 remote_addr,
612 error: None,
613 })
614 }
615 self.cancel_pending_outbound(peer_id);
616 }
617
618 let (commands_tx, commands_rx) = mpsc::channel(self.session_command_buffer);
619 let (unbounded_tx, unbounded_rx) = mpsc::unbounded_channel();
620
621 let (to_session_tx, messages_rx) = mpsc::channel(self.session_command_buffer);
622
623 let messages = PeerRequestSender::new(peer_id, to_session_tx);
624
625 let timeout = Arc::new(AtomicU64::new(
626 self.initial_internal_request_timeout.as_millis() as u64,
627 ));
628
629 let version = conn.version();
631
632 let range_update_interval = (conn.version() >= EthVersion::Eth69).then(|| {
637 let start = tokio::time::Instant::now() + RANGE_UPDATE_INTERVAL;
638 let mut interval = tokio::time::interval_at(start, RANGE_UPDATE_INTERVAL);
639 interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
640 interval
641 });
642
643 let broadcast_items = BroadcastItemCounter::new();
649 let remote_range_info = status.block_range_update().map(|update| {
650 BlockRangeInfo::new(update.earliest, update.latest, update.latest_hash)
651 });
652
653 if self.reject_block_announcements {
654 conn.set_reject_block_announcements(true);
655 }
656
657 let session = ActiveSession {
658 next_id: 0,
659 remote_peer_id: peer_id,
660 remote_addr,
661 remote_capabilities: Arc::clone(&capabilities),
662 session_id,
663 commands_rx: ReceiverStream::new(commands_rx),
664 unbounded_rx,
665 unbounded_broadcast_msgs: self.metrics.total_unbounded_broadcast_msgs.clone(),
666 to_session_manager: self.active_session_tx.clone(),
667 pending_message_to_session: None,
668 internal_request_rx: ReceiverStream::new(messages_rx).fuse(),
669 inflight_requests: Default::default(),
670 conn,
671 queued_outgoing: QueuedOutgoingMessages::new(
672 self.metrics.queued_outgoing_messages.clone(),
673 broadcast_items.clone(),
674 ),
675 received_requests_from_remote: Default::default(),
676 internal_request_timeout_interval: request_timeout_interval(
677 self.initial_internal_request_timeout,
678 ),
679 internal_request_timeout: Arc::clone(&timeout),
680 protocol_breach_request_timeout: self.protocol_breach_request_timeout,
681 terminate_message: None,
682 range_info: remote_range_info.clone(),
683 local_range_info: self.local_range_info.clone(),
684 range_update_interval,
685 last_sent_latest_block: None,
686 };
687
688 let supports_snap = session.conn.supports_snap();
689 self.spawn(session);
690
691 let client_version = client_id.into();
692 let handle = ActiveSessionHandle {
693 status: status.clone(),
694 direction,
695 session_id,
696 remote_id: peer_id,
697 version,
698 established: Instant::now(),
699 capabilities: Arc::clone(&capabilities),
700 commands: SessionCommandSender::new(commands_tx, unbounded_tx, broadcast_items),
701 client_version: Arc::clone(&client_version),
702 remote_addr,
703 local_addr,
704 peer_listen_port,
705 };
706
707 self.active_sessions.insert(peer_id, handle);
708 self.counter.inc_active(&direction);
709
710 if direction.is_outgoing() {
711 self.metrics.total_dial_successes.increment(1);
712 }
713
714 Poll::Ready(SessionEvent::SessionEstablished {
715 peer_id,
716 remote_addr,
717 client_version,
718 version,
719 capabilities,
720 status,
721 messages,
722 direction,
723 timeout,
724 range_info: remote_range_info,
725 supports_snap,
726 })
727 }
728 PendingSessionEvent::Disconnected { remote_addr, session_id, direction, error } => {
729 trace!(
730 target: "net::session",
731 ?session_id,
732 ?remote_addr,
733 ?error,
734 "disconnected pending session"
735 );
736 match direction {
737 Direction::Incoming => {
738 Poll::Ready(SessionEvent::IncomingPendingSessionClosed {
739 remote_addr,
740 error,
741 })
742 }
743 Direction::Outgoing(peer_id) => {
744 Poll::Ready(SessionEvent::OutgoingPendingSessionClosed {
745 remote_addr,
746 peer_id,
747 error,
748 })
749 }
750 }
751 }
752 PendingSessionEvent::OutgoingConnectionError {
753 remote_addr,
754 session_id,
755 peer_id,
756 error,
757 } => {
758 trace!(
759 target: "net::session",
760 %error,
761 ?session_id,
762 ?remote_addr,
763 ?peer_id,
764 "connection refused"
765 );
766 Poll::Ready(SessionEvent::OutgoingConnectionError { remote_addr, peer_id, error })
767 }
768 PendingSessionEvent::EciesAuthError { remote_addr, session_id, error, direction } => {
769 trace!(
770 target: "net::session",
771 %error,
772 ?session_id,
773 ?remote_addr,
774 "ecies auth failed"
775 );
776 match direction {
777 Direction::Incoming => {
778 Poll::Ready(SessionEvent::IncomingPendingSessionClosed {
779 remote_addr,
780 error: Some(PendingSessionHandshakeError::Ecies(error)),
781 })
782 }
783 Direction::Outgoing(peer_id) => {
784 Poll::Ready(SessionEvent::OutgoingPendingSessionClosed {
785 remote_addr,
786 peer_id,
787 error: Some(PendingSessionHandshakeError::Ecies(error)),
788 })
789 }
790 }
791 }
792 }
793 }
794
795 pub(crate) fn update_advertised_block_range(&mut self, block_range_update: BlockRangeUpdate) {
803 self.status.earliest_block = Some(block_range_update.earliest);
804 self.status.latest_block = Some(block_range_update.latest);
805 self.status.blockhash = block_range_update.latest_hash;
806
807 self.local_range_info.update(
809 block_range_update.earliest,
810 block_range_update.latest,
811 block_range_update.latest_hash,
812 );
813 }
814}
815
816#[derive(Default, Debug, Clone)]
818struct DisconnectionsCounter(Arc<()>);
819
820impl DisconnectionsCounter {
821 const MAX_CONCURRENT_GRACEFUL_DISCONNECTIONS: usize = 15;
822
823 fn has_capacity(&self) -> bool {
826 Arc::strong_count(&self.0) <= Self::MAX_CONCURRENT_GRACEFUL_DISCONNECTIONS
827 }
828}
829
830#[derive(Debug)]
832pub enum SessionEvent<N: NetworkPrimitives> {
833 SessionEstablished {
837 peer_id: PeerId,
839 remote_addr: SocketAddr,
841 client_version: Arc<str>,
843 capabilities: Arc<Capabilities>,
845 version: EthVersion,
847 status: Arc<UnifiedStatus>,
849 messages: PeerRequestSender<PeerRequest<N>>,
851 direction: Direction,
853 timeout: Arc<AtomicU64>,
856 range_info: Option<BlockRangeInfo>,
858 supports_snap: bool,
860 },
861 AlreadyConnected {
863 peer_id: PeerId,
865 remote_addr: SocketAddr,
867 direction: Direction,
869 },
870 ValidMessage {
872 peer_id: PeerId,
874 message: PeerMessage<N>,
876 },
877 BadMessage {
879 peer_id: PeerId,
881 },
882 ProtocolBreach {
884 peer_id: PeerId,
886 },
887 IncomingPendingSessionClosed {
889 remote_addr: SocketAddr,
891 error: Option<PendingSessionHandshakeError>,
893 },
894 OutgoingPendingSessionClosed {
896 remote_addr: SocketAddr,
898 peer_id: PeerId,
900 error: Option<PendingSessionHandshakeError>,
902 },
903 OutgoingConnectionError {
905 remote_addr: SocketAddr,
907 peer_id: PeerId,
909 error: io::Error,
911 },
912 SessionClosedOnConnectionError {
914 peer_id: PeerId,
916 remote_addr: SocketAddr,
918 error: EthStreamError,
920 },
921 Disconnected {
923 peer_id: PeerId,
925 remote_addr: SocketAddr,
927 },
928}
929
930#[derive(Debug, thiserror::Error)]
932pub enum PendingSessionHandshakeError {
933 #[error(transparent)]
935 Eth(EthStreamError),
936 #[error(transparent)]
938 Ecies(ECIESError),
939 #[error("authentication timed out")]
941 Timeout,
942 #[error("Mandatory extra capability unsupported")]
944 UnsupportedExtraCapability,
945 #[error("unexpected identity in hello message: {0}")]
948 UnexpectedHandshakeIdentity(GotExpectedBoxed<PeerId>),
949}
950
951impl PendingSessionHandshakeError {
952 pub const fn as_disconnected(&self) -> Option<DisconnectReason> {
955 match self {
956 Self::Eth(eth_err) => eth_err.as_disconnected(),
957 Self::UnexpectedHandshakeIdentity(_) => {
958 Some(DisconnectReason::UnexpectedHandshakeIdentity)
959 }
960 _ => None,
961 }
962 }
963}
964
965#[derive(Debug, Clone, thiserror::Error)]
968#[error("session limit reached {0}")]
969pub struct ExceedsSessionLimit(pub(crate) u32);
970
971pub(crate) async fn pending_session_with_timeout<F, N: NetworkPrimitives>(
973 timeout: Duration,
974 session_id: SessionId,
975 remote_addr: SocketAddr,
976 direction: Direction,
977 events: mpsc::Sender<PendingSessionEvent<N>>,
978 f: F,
979) where
980 F: Future<Output = ()>,
981{
982 if tokio::time::timeout(timeout, f).await.is_err() {
983 trace!(target: "net::session", ?remote_addr, ?direction, "pending session timed out");
984 let event = PendingSessionEvent::Disconnected {
985 remote_addr,
986 session_id,
987 direction,
988 error: Some(PendingSessionHandshakeError::Timeout),
989 };
990 let _ = events.send(event).await;
991 }
992}
993
994#[expect(clippy::too_many_arguments)]
998pub(crate) async fn start_pending_incoming_session<N: NetworkPrimitives>(
999 handshake: Arc<dyn EthRlpxHandshake>,
1000 eth_max_message_size: usize,
1001 disconnect_rx: oneshot::Receiver<()>,
1002 session_id: SessionId,
1003 stream: TcpStream,
1004 events: mpsc::Sender<PendingSessionEvent<N>>,
1005 remote_addr: SocketAddr,
1006 secret_key: SecretKey,
1007 hello: HelloMessageWithProtocols,
1008 status: UnifiedStatus,
1009 fork_filter: ForkFilter,
1010 extra_handlers: RlpxSubProtocolHandlers,
1011) {
1012 authenticate(
1013 handshake,
1014 eth_max_message_size,
1015 disconnect_rx,
1016 events,
1017 stream,
1018 session_id,
1019 remote_addr,
1020 secret_key,
1021 Direction::Incoming,
1022 hello,
1023 status,
1024 fork_filter,
1025 extra_handlers,
1026 )
1027 .await
1028}
1029
1030#[instrument(level = "trace", target = "net::network", skip_all, fields(%remote_addr, peer_id = ?remote_peer_id))]
1032#[expect(clippy::too_many_arguments)]
1033async fn start_pending_outbound_session<N: NetworkPrimitives>(
1034 handshake: Arc<dyn EthRlpxHandshake>,
1035 eth_max_message_size: usize,
1036 mut disconnect_rx: oneshot::Receiver<()>,
1037 events: mpsc::Sender<PendingSessionEvent<N>>,
1038 session_id: SessionId,
1039 remote_addr: SocketAddr,
1040 remote_peer_id: PeerId,
1041 secret_key: SecretKey,
1042 hello: HelloMessageWithProtocols,
1043 status: UnifiedStatus,
1044 fork_filter: ForkFilter,
1045 extra_handlers: RlpxSubProtocolHandlers,
1046) {
1047 let connection = tokio::select! {
1048 _ = &mut disconnect_rx => {
1049 let _ = events.send(PendingSessionEvent::Disconnected {
1050 remote_addr,
1051 session_id,
1052 direction: Direction::Outgoing(remote_peer_id),
1053 error: None,
1054 }).await;
1055 return
1056 },
1057 connection = TcpStream::connect(remote_addr) => connection,
1058 };
1059 let stream = match connection {
1060 Ok(stream) => {
1061 if let Err(err) = stream.set_nodelay(true) {
1062 tracing::warn!(target: "net::session", "set nodelay failed: {:?}", err);
1063 }
1064 stream
1065 }
1066 Err(error) => {
1067 let _ = events
1068 .send(PendingSessionEvent::OutgoingConnectionError {
1069 remote_addr,
1070 session_id,
1071 peer_id: remote_peer_id,
1072 error,
1073 })
1074 .await;
1075 return
1076 }
1077 };
1078 authenticate(
1079 handshake,
1080 eth_max_message_size,
1081 disconnect_rx,
1082 events,
1083 stream,
1084 session_id,
1085 remote_addr,
1086 secret_key,
1087 Direction::Outgoing(remote_peer_id),
1088 hello,
1089 status,
1090 fork_filter,
1091 extra_handlers,
1092 )
1093 .await
1094}
1095
1096#[expect(clippy::too_many_arguments)]
1098async fn authenticate<N: NetworkPrimitives>(
1099 handshake: Arc<dyn EthRlpxHandshake>,
1100 eth_max_message_size: usize,
1101 disconnect_rx: oneshot::Receiver<()>,
1102 events: mpsc::Sender<PendingSessionEvent<N>>,
1103 stream: TcpStream,
1104 session_id: SessionId,
1105 remote_addr: SocketAddr,
1106 secret_key: SecretKey,
1107 direction: Direction,
1108 hello: HelloMessageWithProtocols,
1109 status: UnifiedStatus,
1110 fork_filter: ForkFilter,
1111 extra_handlers: RlpxSubProtocolHandlers,
1112) {
1113 let local_addr = stream.local_addr().ok();
1114 let auth = async move {
1115 let stream = match get_ecies_stream(stream, secret_key, direction).await {
1116 Ok(stream) => stream,
1117 Err(error) => {
1118 return PendingSessionEvent::EciesAuthError {
1119 remote_addr,
1120 session_id,
1121 error,
1122 direction,
1123 }
1124 }
1125 };
1126
1127 let unauthed = UnauthedP2PStream::new(stream);
1128
1129 authenticate_stream(
1130 handshake,
1131 eth_max_message_size,
1132 unauthed,
1133 session_id,
1134 remote_addr,
1135 local_addr,
1136 direction,
1137 hello,
1138 status,
1139 fork_filter,
1140 extra_handlers,
1141 )
1142 .await
1143 }
1144 .boxed();
1145
1146 match futures::future::select(disconnect_rx, auth).await {
1147 Either::Left((_, _)) => {
1148 let _ = events
1149 .send(PendingSessionEvent::Disconnected {
1150 remote_addr,
1151 session_id,
1152 direction,
1153 error: None,
1154 })
1155 .await;
1156 }
1157 Either::Right((res, _)) => {
1158 let _ = events.send(res).await;
1159 }
1160 }
1161}
1162
1163async fn get_ecies_stream<Io: AsyncRead + AsyncWrite + Unpin>(
1166 stream: Io,
1167 secret_key: SecretKey,
1168 direction: Direction,
1169) -> Result<ECIESStream<Io>, ECIESError> {
1170 match direction {
1171 Direction::Incoming => ECIESStream::incoming(stream, secret_key).await,
1172 Direction::Outgoing(remote_peer_id) => {
1173 ECIESStream::connect(stream, secret_key, remote_peer_id).await
1174 }
1175 }
1176}
1177
1178#[expect(clippy::too_many_arguments)]
1185async fn authenticate_stream<N: NetworkPrimitives>(
1186 handshake: Arc<dyn EthRlpxHandshake>,
1187 eth_max_message_size: usize,
1188 stream: UnauthedP2PStream<ECIESStream<TcpStream>>,
1189 session_id: SessionId,
1190 remote_addr: SocketAddr,
1191 local_addr: Option<SocketAddr>,
1192 direction: Direction,
1193 mut hello: HelloMessageWithProtocols,
1194 mut status: UnifiedStatus,
1195 fork_filter: ForkFilter,
1196 mut extra_handlers: RlpxSubProtocolHandlers,
1197) -> PendingSessionEvent<N> {
1198 extra_handlers.retain(|handler| hello.try_add_protocol(handler.protocol()).is_ok());
1200
1201 let authenticated_peer_id = stream.inner().remote_id();
1202
1203 let (mut p2p_stream, their_hello) = match stream.handshake(hello).await {
1205 Ok(stream_res) => stream_res,
1206 Err(err) => {
1207 return PendingSessionEvent::Disconnected {
1208 remote_addr,
1209 session_id,
1210 direction,
1211 error: Some(PendingSessionHandshakeError::Eth(err.into())),
1212 }
1213 }
1214 };
1215
1216 if their_hello.id != authenticated_peer_id {
1223 let _ = p2p_stream.disconnect(DisconnectReason::UnexpectedHandshakeIdentity).await;
1224
1225 return PendingSessionEvent::Disconnected {
1226 remote_addr,
1227 session_id,
1228 direction,
1229 error: Some(PendingSessionHandshakeError::UnexpectedHandshakeIdentity(
1230 GotExpected { got: their_hello.id, expected: authenticated_peer_id }.into(),
1231 )),
1232 }
1233 }
1234
1235 if !extra_handlers.is_empty() {
1237 while let Some(pos) = extra_handlers.iter().position(|handler| {
1239 p2p_stream
1240 .shared_capabilities()
1241 .ensure_matching_capability(&handler.protocol().cap)
1242 .is_err()
1243 }) {
1244 let handler = extra_handlers.remove(pos);
1245 if handler.on_unsupported_by_peer(
1246 p2p_stream.shared_capabilities(),
1247 direction,
1248 authenticated_peer_id,
1249 ) == OnNotSupported::Disconnect
1250 {
1251 return PendingSessionEvent::Disconnected {
1252 remote_addr,
1253 session_id,
1254 direction,
1255 error: Some(PendingSessionHandshakeError::UnsupportedExtraCapability),
1256 };
1257 }
1258 }
1259 }
1260
1261 let eth_version = match p2p_stream.shared_capabilities().eth_version() {
1263 Ok(version) => version,
1264 Err(err) => {
1265 return PendingSessionEvent::Disconnected {
1266 remote_addr,
1267 session_id,
1268 direction,
1269 error: Some(PendingSessionHandshakeError::Eth(err.into())),
1270 }
1271 }
1272 };
1273
1274 status.set_eth_version(eth_version);
1276
1277 let (conn, their_status) = if p2p_stream.shared_capabilities().len() == 1 {
1278 match handshake
1283 .handshake(&mut p2p_stream, status, fork_filter.clone(), HANDSHAKE_TIMEOUT)
1284 .await
1285 {
1286 Ok(their_status) => {
1287 let eth_stream =
1288 EthStream::with_max_message_size(eth_version, p2p_stream, eth_max_message_size);
1289 (eth_stream.into(), their_status)
1290 }
1291 Err(err) => {
1292 return PendingSessionEvent::Disconnected {
1293 remote_addr,
1294 session_id,
1295 direction,
1296 error: Some(PendingSessionHandshakeError::Eth(err)),
1297 }
1298 }
1299 }
1300 } else if p2p_stream.shared_capabilities().is_exact_eth_snap_v2() {
1301 match EthSnapStream::handshake(
1306 p2p_stream,
1307 status,
1308 fork_filter,
1309 handshake,
1310 eth_max_message_size,
1311 )
1312 .await
1313 {
1314 Ok((stream, their_status)) => (stream.into(), their_status),
1315 Err(err) => {
1316 return PendingSessionEvent::Disconnected {
1317 remote_addr,
1318 session_id,
1319 direction,
1320 error: Some(PendingSessionHandshakeError::Eth(err)),
1321 }
1322 }
1323 }
1324 } else {
1325 let mut multiplex_stream = RlpxProtocolMultiplexer::new(p2p_stream);
1326 let (protocol_start, protocol_ready) = watch::channel(false);
1327
1328 for handler in extra_handlers.into_iter() {
1331 let protocol = handler.protocol();
1332 let limits = handler.inbound_limits();
1333 let remote_peer_id = authenticated_peer_id;
1334 let mut protocol_ready = protocol_ready.clone();
1335 multiplex_stream
1338 .install_protocol_with_limits(&protocol.cap, limits, move |conn| {
1339 stream::once(async move {
1340 protocol_ready
1342 .wait_for(|started| *started)
1343 .await
1344 .is_ok()
1345 .then(|| handler.into_connection(direction, remote_peer_id, conn))
1346 })
1347 .filter_map(future::ready)
1348 .flatten()
1349 })
1350 .expect("remaining handler capability was negotiated");
1351 }
1352
1353 let (multiplex_stream, their_status) = match multiplex_stream
1354 .into_eth_satellite_stream(status, fork_filter, handshake, eth_max_message_size)
1355 .await
1356 {
1357 Ok((multiplex_stream, their_status)) => (multiplex_stream, their_status),
1358 Err(err) => {
1359 return PendingSessionEvent::Disconnected {
1360 remote_addr,
1361 session_id,
1362 direction,
1363 error: Some(PendingSessionHandshakeError::Eth(err)),
1364 }
1365 }
1366 };
1367 protocol_start.send_replace(true);
1368
1369 (multiplex_stream.into(), their_status)
1370 };
1371
1372 let peer_listen_port = (their_hello.port != 0).then_some(their_hello.port);
1374
1375 PendingSessionEvent::Established {
1376 session_id,
1377 remote_addr,
1378 local_addr,
1379 peer_id: authenticated_peer_id,
1380 capabilities: Arc::new(Capabilities::from(their_hello.capabilities)),
1381 status: Arc::new(their_status),
1382 conn,
1383 direction,
1384 client_id: their_hello.client_version,
1385 peer_listen_port,
1386 }
1387}