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::{future::Either, io, FutureExt, StreamExt};
22use reth_ecies::{stream::ECIESStream, ECIESError};
23use reth_eth_wire::{
24 errors::EthStreamError, handshake::EthRlpxHandshake, multiplex::RlpxProtocolMultiplexer,
25 BlockRangeUpdate, Capabilities, DisconnectReason, EthSnapStream, EthStream, EthVersion,
26 HelloMessageWithProtocols, NetworkPrimitives, UnauthedP2PStream, UnifiedStatus,
27 HANDSHAKE_TIMEOUT,
28};
29use reth_ethereum_forks::{ForkFilter, ForkId, ForkTransition, Head};
30use reth_metrics::common::mpsc::MeteredPollSender;
31use reth_network_api::{PeerRequest, PeerRequestSender};
32use reth_network_peers::PeerId;
33use reth_network_types::SessionsConfig;
34use reth_primitives_traits::{GotExpected, GotExpectedBoxed};
35use reth_tasks::Runtime;
36use rustc_hash::FxHashMap;
37use secp256k1::SecretKey;
38use std::{
39 future::Future,
40 net::SocketAddr,
41 sync::{atomic::AtomicU64, Arc},
42 task::{Context, Poll},
43 time::{Duration, Instant},
44};
45use tokio::{
46 io::{AsyncRead, AsyncWrite},
47 net::TcpStream,
48 sync::{mpsc, oneshot},
49};
50use tokio_stream::wrappers::ReceiverStream;
51use tokio_util::sync::PollSender;
52use tracing::{instrument, trace};
53
54use crate::session::active::{
55 request_timeout_interval, BroadcastItemCounter, RANGE_UPDATE_INTERVAL,
56};
57pub use conn::EthRlpxConnection;
58use handle::SessionCommandSender;
59pub use handle::{
60 ActiveSessionHandle, ActiveSessionMessage, PendingSessionEvent, PendingSessionHandle,
61 SessionCommand,
62};
63pub use reth_network_api::{Direction, PeerInfo};
64
65#[derive(Debug, Clone, Copy, PartialOrd, PartialEq, Eq, Hash)]
67pub struct SessionId(usize);
68
69#[must_use = "Session Manager must be polled to process session events."]
71#[derive(Debug)]
72pub struct SessionManager<N: NetworkPrimitives> {
73 next_id: usize,
75 counter: SessionCounter,
77 initial_internal_request_timeout: Duration,
80 protocol_breach_request_timeout: Duration,
83 pending_session_timeout: Duration,
85 secret_key: SecretKey,
87 status: UnifiedStatus,
89 hello_message: HelloMessageWithProtocols,
91 fork_filter: ForkFilter,
93 session_command_buffer: usize,
95 executor: Runtime,
97 pending_sessions: FxHashMap<SessionId, PendingSessionHandle>,
102 active_sessions: HashMap<PeerId, ActiveSessionHandle<N>, FbBuildHasher<64>>,
104 pending_sessions_tx: mpsc::Sender<PendingSessionEvent<N>>,
109 pending_session_rx: ReceiverStream<PendingSessionEvent<N>>,
111 active_session_tx: MeteredPollSender<ActiveSessionMessage<N>>,
116 active_session_rx: ReceiverStream<ActiveSessionMessage<N>>,
118 extra_protocols: RlpxSubProtocols,
120 disconnections_counter: DisconnectionsCounter,
122 metrics: SessionManagerMetrics,
124 handshake: Arc<dyn EthRlpxHandshake>,
126 eth_max_message_size: usize,
128 local_range_info: BlockRangeInfo,
131 reject_block_announcements: bool,
134}
135
136impl<N: NetworkPrimitives> SessionManager<N> {
139 #[expect(clippy::too_many_arguments)]
141 pub fn new(
142 secret_key: SecretKey,
143 config: SessionsConfig,
144 executor: Runtime,
145 status: UnifiedStatus,
146 hello_message: HelloMessageWithProtocols,
147 fork_filter: ForkFilter,
148 extra_protocols: RlpxSubProtocols,
149 handshake: Arc<dyn EthRlpxHandshake>,
150 eth_max_message_size: usize,
151 reject_block_announcements: bool,
152 ) -> Self {
153 let (pending_sessions_tx, pending_sessions_rx) = mpsc::channel(config.session_event_buffer);
154 let (active_session_tx, active_session_rx) = mpsc::channel(config.session_event_buffer);
155 let active_session_tx = PollSender::new(active_session_tx);
156
157 let local_range_info = BlockRangeInfo::new(
159 status.earliest_block.unwrap_or_default(),
160 status.latest_block.unwrap_or_default(),
161 status.blockhash,
162 );
163
164 Self {
165 next_id: 0,
166 counter: SessionCounter::new(config.limits),
167 initial_internal_request_timeout: config.initial_internal_request_timeout,
168 protocol_breach_request_timeout: config.protocol_breach_request_timeout,
169 pending_session_timeout: config.pending_session_timeout,
170 secret_key,
171 status,
172 hello_message,
173 fork_filter,
174 session_command_buffer: config.session_command_buffer,
175 executor,
176 pending_sessions: Default::default(),
177 active_sessions: Default::default(),
178 pending_sessions_tx,
179 pending_session_rx: ReceiverStream::new(pending_sessions_rx),
180 active_session_tx: MeteredPollSender::new(active_session_tx, "network_active_session"),
181 active_session_rx: ReceiverStream::new(active_session_rx),
182 extra_protocols,
183 disconnections_counter: Default::default(),
184 metrics: Default::default(),
185 handshake,
186 eth_max_message_size,
187 local_range_info,
188 reject_block_announcements,
189 }
190 }
191
192 pub(crate) const fn fork_id(&self) -> ForkId {
194 self.fork_filter.current()
195 }
196
197 pub fn is_valid_fork_id(&self, fork_id: ForkId) -> bool {
200 self.fork_filter.validate(fork_id).is_ok()
201 }
202
203 const fn next_id(&mut self) -> SessionId {
205 let id = self.next_id;
206 self.next_id += 1;
207 SessionId(id)
208 }
209
210 pub const fn status(&self) -> UnifiedStatus {
212 self.status
213 }
214
215 pub const fn secret_key(&self) -> SecretKey {
217 self.secret_key
218 }
219
220 pub const fn active_sessions(
222 &self,
223 ) -> &HashMap<PeerId, ActiveSessionHandle<N>, FbBuildHasher<64>> {
224 &self.active_sessions
225 }
226
227 pub fn hello_message(&self) -> HelloMessageWithProtocols {
229 self.hello_message.clone()
230 }
231
232 pub(crate) fn add_rlpx_sub_protocol(&mut self, protocol: impl IntoRlpxSubProtocol) {
234 self.extra_protocols.push(protocol)
235 }
236
237 #[inline]
239 pub(crate) fn num_pending_connections(&self) -> usize {
240 self.pending_sessions.len()
241 }
242
243 fn spawn<F>(&self, f: F)
246 where
247 F: Future<Output = ()> + Send + 'static,
248 {
249 self.executor.spawn_task(f);
250 }
251
252 pub(crate) fn on_status_update(&mut self, head: Head) -> Option<ForkTransition> {
257 self.status.blockhash = head.hash;
258 self.status.total_difficulty = Some(head.total_difficulty);
259 let transition = self.fork_filter.set_head(head);
260 self.status.forkid = self.fork_filter.current();
261 self.status.latest_block = Some(head.number);
262
263 transition
264 }
265
266 pub(crate) fn on_incoming(
271 &mut self,
272 stream: TcpStream,
273 remote_addr: SocketAddr,
274 ) -> Result<SessionId, ExceedsSessionLimit> {
275 self.counter.ensure_pending_inbound()?;
276
277 let session_id = self.next_id();
278
279 trace!(
280 target: "net::session",
281 ?remote_addr,
282 ?session_id,
283 "new pending incoming session"
284 );
285
286 let (disconnect_tx, disconnect_rx) = oneshot::channel();
287 let pending_events = self.pending_sessions_tx.clone();
288 let secret_key = self.secret_key;
289 let hello_message = self.hello_message.clone();
290 let status = self.status;
291 let fork_filter = self.fork_filter.clone();
292 let extra_handlers = self.extra_protocols.on_incoming(remote_addr);
293 self.spawn(pending_session_with_timeout(
294 self.pending_session_timeout,
295 session_id,
296 remote_addr,
297 Direction::Incoming,
298 pending_events.clone(),
299 start_pending_incoming_session(
300 self.handshake.clone(),
301 self.eth_max_message_size,
302 disconnect_rx,
303 session_id,
304 stream,
305 pending_events,
306 remote_addr,
307 secret_key,
308 hello_message,
309 status,
310 fork_filter,
311 extra_handlers,
312 ),
313 ));
314
315 let handle = PendingSessionHandle {
316 disconnect_tx: Some(disconnect_tx),
317 direction: Direction::Incoming,
318 };
319 self.pending_sessions.insert(session_id, handle);
320 self.counter.inc_pending_inbound();
321 Ok(session_id)
322 }
323
324 pub fn dial_outbound(&mut self, remote_addr: SocketAddr, remote_peer_id: PeerId) {
326 if self.counter.ensure_pending_outbound().is_ok() {
328 let session_id = self.next_id();
329 let (disconnect_tx, disconnect_rx) = oneshot::channel();
330 let pending_events = self.pending_sessions_tx.clone();
331 let secret_key = self.secret_key;
332 let hello_message = self.hello_message.clone();
333 let fork_filter = self.fork_filter.clone();
334 let status = self.status;
335 let extra_handlers = self.extra_protocols.on_outgoing(remote_addr, remote_peer_id);
336 self.spawn(pending_session_with_timeout(
337 self.pending_session_timeout,
338 session_id,
339 remote_addr,
340 Direction::Outgoing(remote_peer_id),
341 pending_events.clone(),
342 start_pending_outbound_session(
343 self.handshake.clone(),
344 self.eth_max_message_size,
345 disconnect_rx,
346 pending_events,
347 session_id,
348 remote_addr,
349 remote_peer_id,
350 secret_key,
351 hello_message,
352 status,
353 fork_filter,
354 extra_handlers,
355 ),
356 ));
357
358 let handle = PendingSessionHandle {
359 disconnect_tx: Some(disconnect_tx),
360 direction: Direction::Outgoing(remote_peer_id),
361 };
362 self.pending_sessions.insert(session_id, handle);
363 self.counter.inc_pending_outbound();
364 }
365 }
366
367 pub fn disconnect(&self, node: PeerId, reason: Option<DisconnectReason>) {
372 if let Some(session) = self.active_sessions.get(&node) {
373 session.disconnect(reason);
374 }
375 }
376
377 pub fn disconnect_all(&self, reason: Option<DisconnectReason>) {
382 for session in self.active_sessions.values() {
383 session.disconnect(reason);
384 }
385 }
386
387 pub fn disconnect_all_pending(&mut self) {
389 for session in self.pending_sessions.values_mut() {
390 session.disconnect();
391 }
392 }
393
394 pub fn send_message(&self, peer_id: &PeerId, msg: PeerMessage<N>) {
401 if let Some(session) = self.active_sessions.get(peer_id) &&
402 !session.commands.send_message(msg)
403 {
404 self.metrics.total_outgoing_peer_messages_dropped.increment(1);
405 }
406 }
407
408 fn remove_pending_session(&mut self, id: &SessionId) -> Option<PendingSessionHandle> {
410 let session = self.pending_sessions.remove(id)?;
411 self.counter.dec_pending(&session.direction);
412 Some(session)
413 }
414
415 fn remove_active_session(&mut self, id: &PeerId) -> Option<ActiveSessionHandle<N>> {
417 let session = self.active_sessions.remove(id)?;
418 self.counter.dec_active(&session.direction);
419 Some(session)
420 }
421
422 pub(crate) fn try_disconnect_incoming_connection(
426 &self,
427 stream: TcpStream,
428 reason: DisconnectReason,
429 ) {
430 if !self.disconnections_counter.has_capacity() {
431 return
433 }
434
435 let guard = self.disconnections_counter.clone();
436 let secret_key = self.secret_key;
437
438 self.spawn(async move {
439 trace!(
440 target: "net::session",
441 "gracefully disconnecting incoming connection"
442 );
443 if let Ok(stream) = get_ecies_stream(stream, secret_key, Direction::Incoming).await {
444 let mut unauth = UnauthedP2PStream::new(stream);
445 let _ = unauth.send_disconnect(reason).await;
446 drop(guard);
447 }
448 });
449 }
450
451 pub(crate) fn poll(&mut self, cx: &mut Context<'_>) -> Poll<SessionEvent<N>> {
455 match self.active_session_rx.poll_next_unpin(cx) {
457 Poll::Pending => {}
458 Poll::Ready(None) => {
459 unreachable!("Manager holds both channel halves.")
460 }
461 Poll::Ready(Some(event)) => {
462 return match event {
463 ActiveSessionMessage::Disconnected { peer_id, remote_addr } => {
464 trace!(
465 target: "net::session",
466 ?peer_id,
467 "gracefully disconnected active session."
468 );
469 self.remove_active_session(&peer_id);
470 Poll::Ready(SessionEvent::Disconnected { peer_id, remote_addr })
471 }
472 ActiveSessionMessage::ClosedOnConnectionError {
473 peer_id,
474 remote_addr,
475 error,
476 } => {
477 trace!(target: "net::session", ?peer_id, %error,"closed session.");
478 self.remove_active_session(&peer_id);
479 Poll::Ready(SessionEvent::SessionClosedOnConnectionError {
480 remote_addr,
481 peer_id,
482 error,
483 })
484 }
485 ActiveSessionMessage::ValidMessage { peer_id, message } => {
486 Poll::Ready(SessionEvent::ValidMessage { peer_id, message })
487 }
488 ActiveSessionMessage::BadMessage { peer_id } => {
489 Poll::Ready(SessionEvent::BadMessage { peer_id })
490 }
491 ActiveSessionMessage::ProtocolBreach { peer_id } => {
492 Poll::Ready(SessionEvent::ProtocolBreach { peer_id })
493 }
494 }
495 }
496 }
497
498 let event = match self.pending_session_rx.poll_next_unpin(cx) {
500 Poll::Pending => return Poll::Pending,
501 Poll::Ready(None) => unreachable!("Manager holds both channel halves."),
502 Poll::Ready(Some(event)) => event,
503 };
504 match event {
505 PendingSessionEvent::Established {
506 session_id,
507 remote_addr,
508 local_addr,
509 peer_id,
510 capabilities,
511 mut conn,
512 status,
513 direction,
514 client_id,
515 peer_listen_port,
516 } => {
517 self.remove_pending_session(&session_id);
519
520 if self.active_sessions.contains_key(&peer_id) {
522 trace!(
523 target: "net::session",
524 ?session_id,
525 ?remote_addr,
526 ?peer_id,
527 ?direction,
528 "already connected"
529 );
530
531 self.spawn(async move {
532 let _ =
534 conn.into_inner().disconnect(DisconnectReason::AlreadyConnected).await;
535 });
536
537 return Poll::Ready(SessionEvent::AlreadyConnected {
538 peer_id,
539 remote_addr,
540 direction,
541 })
542 }
543
544 let (commands_tx, commands_rx) = mpsc::channel(self.session_command_buffer);
545 let (unbounded_tx, unbounded_rx) = mpsc::unbounded_channel();
546
547 let (to_session_tx, messages_rx) = mpsc::channel(self.session_command_buffer);
548
549 let messages = PeerRequestSender::new(peer_id, to_session_tx);
550
551 let timeout = Arc::new(AtomicU64::new(
552 self.initial_internal_request_timeout.as_millis() as u64,
553 ));
554
555 let version = conn.version();
557
558 let range_update_interval = (conn.version() >= EthVersion::Eth69).then(|| {
563 let start = tokio::time::Instant::now() + RANGE_UPDATE_INTERVAL;
564 let mut interval = tokio::time::interval_at(start, RANGE_UPDATE_INTERVAL);
565 interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
566 interval
567 });
568
569 let broadcast_items = BroadcastItemCounter::new();
575 let remote_range_info = status.block_range_update().map(|update| {
576 BlockRangeInfo::new(update.earliest, update.latest, update.latest_hash)
577 });
578
579 if self.reject_block_announcements {
580 conn.set_reject_block_announcements(true);
581 }
582
583 let session = ActiveSession {
584 next_id: 0,
585 remote_peer_id: peer_id,
586 remote_addr,
587 remote_capabilities: Arc::clone(&capabilities),
588 session_id,
589 commands_rx: ReceiverStream::new(commands_rx),
590 unbounded_rx,
591 unbounded_broadcast_msgs: self.metrics.total_unbounded_broadcast_msgs.clone(),
592 to_session_manager: self.active_session_tx.clone(),
593 pending_message_to_session: None,
594 internal_request_rx: ReceiverStream::new(messages_rx).fuse(),
595 inflight_requests: Default::default(),
596 conn,
597 queued_outgoing: QueuedOutgoingMessages::new(
598 self.metrics.queued_outgoing_messages.clone(),
599 broadcast_items.clone(),
600 ),
601 received_requests_from_remote: Default::default(),
602 internal_request_timeout_interval: request_timeout_interval(
603 self.initial_internal_request_timeout,
604 ),
605 internal_request_timeout: Arc::clone(&timeout),
606 protocol_breach_request_timeout: self.protocol_breach_request_timeout,
607 terminate_message: None,
608 range_info: remote_range_info.clone(),
609 local_range_info: self.local_range_info.clone(),
610 range_update_interval,
611 last_sent_latest_block: None,
612 };
613
614 let supports_snap = session.conn.supports_snap();
615 self.spawn(session);
616
617 let client_version = client_id.into();
618 let handle = ActiveSessionHandle {
619 status: status.clone(),
620 direction,
621 session_id,
622 remote_id: peer_id,
623 version,
624 established: Instant::now(),
625 capabilities: Arc::clone(&capabilities),
626 commands: SessionCommandSender::new(commands_tx, unbounded_tx, broadcast_items),
627 client_version: Arc::clone(&client_version),
628 remote_addr,
629 local_addr,
630 peer_listen_port,
631 };
632
633 self.active_sessions.insert(peer_id, handle);
634 self.counter.inc_active(&direction);
635
636 if direction.is_outgoing() {
637 self.metrics.total_dial_successes.increment(1);
638 }
639
640 Poll::Ready(SessionEvent::SessionEstablished {
641 peer_id,
642 remote_addr,
643 client_version,
644 version,
645 capabilities,
646 status,
647 messages,
648 direction,
649 timeout,
650 range_info: remote_range_info,
651 supports_snap,
652 })
653 }
654 PendingSessionEvent::Disconnected { remote_addr, session_id, direction, error } => {
655 trace!(
656 target: "net::session",
657 ?session_id,
658 ?remote_addr,
659 ?error,
660 "disconnected pending session"
661 );
662 self.remove_pending_session(&session_id);
663 match direction {
664 Direction::Incoming => {
665 Poll::Ready(SessionEvent::IncomingPendingSessionClosed {
666 remote_addr,
667 error,
668 })
669 }
670 Direction::Outgoing(peer_id) => {
671 Poll::Ready(SessionEvent::OutgoingPendingSessionClosed {
672 remote_addr,
673 peer_id,
674 error,
675 })
676 }
677 }
678 }
679 PendingSessionEvent::OutgoingConnectionError {
680 remote_addr,
681 session_id,
682 peer_id,
683 error,
684 } => {
685 trace!(
686 target: "net::session",
687 %error,
688 ?session_id,
689 ?remote_addr,
690 ?peer_id,
691 "connection refused"
692 );
693 self.remove_pending_session(&session_id);
694 Poll::Ready(SessionEvent::OutgoingConnectionError { remote_addr, peer_id, error })
695 }
696 PendingSessionEvent::EciesAuthError { remote_addr, session_id, error, direction } => {
697 trace!(
698 target: "net::session",
699 %error,
700 ?session_id,
701 ?remote_addr,
702 "ecies auth failed"
703 );
704 self.remove_pending_session(&session_id);
705 match direction {
706 Direction::Incoming => {
707 Poll::Ready(SessionEvent::IncomingPendingSessionClosed {
708 remote_addr,
709 error: Some(PendingSessionHandshakeError::Ecies(error)),
710 })
711 }
712 Direction::Outgoing(peer_id) => {
713 Poll::Ready(SessionEvent::OutgoingPendingSessionClosed {
714 remote_addr,
715 peer_id,
716 error: Some(PendingSessionHandshakeError::Ecies(error)),
717 })
718 }
719 }
720 }
721 }
722 }
723
724 pub(crate) fn update_advertised_block_range(&mut self, block_range_update: BlockRangeUpdate) {
732 self.status.earliest_block = Some(block_range_update.earliest);
733 self.status.latest_block = Some(block_range_update.latest);
734 self.status.blockhash = block_range_update.latest_hash;
735
736 self.local_range_info.update(
738 block_range_update.earliest,
739 block_range_update.latest,
740 block_range_update.latest_hash,
741 );
742 }
743}
744
745#[derive(Default, Debug, Clone)]
747struct DisconnectionsCounter(Arc<()>);
748
749impl DisconnectionsCounter {
750 const MAX_CONCURRENT_GRACEFUL_DISCONNECTIONS: usize = 15;
751
752 fn has_capacity(&self) -> bool {
755 Arc::strong_count(&self.0) <= Self::MAX_CONCURRENT_GRACEFUL_DISCONNECTIONS
756 }
757}
758
759#[derive(Debug)]
761pub enum SessionEvent<N: NetworkPrimitives> {
762 SessionEstablished {
766 peer_id: PeerId,
768 remote_addr: SocketAddr,
770 client_version: Arc<str>,
772 capabilities: Arc<Capabilities>,
774 version: EthVersion,
776 status: Arc<UnifiedStatus>,
778 messages: PeerRequestSender<PeerRequest<N>>,
780 direction: Direction,
782 timeout: Arc<AtomicU64>,
785 range_info: Option<BlockRangeInfo>,
787 supports_snap: bool,
789 },
790 AlreadyConnected {
792 peer_id: PeerId,
794 remote_addr: SocketAddr,
796 direction: Direction,
798 },
799 ValidMessage {
801 peer_id: PeerId,
803 message: PeerMessage<N>,
805 },
806 BadMessage {
808 peer_id: PeerId,
810 },
811 ProtocolBreach {
813 peer_id: PeerId,
815 },
816 IncomingPendingSessionClosed {
818 remote_addr: SocketAddr,
820 error: Option<PendingSessionHandshakeError>,
822 },
823 OutgoingPendingSessionClosed {
825 remote_addr: SocketAddr,
827 peer_id: PeerId,
829 error: Option<PendingSessionHandshakeError>,
831 },
832 OutgoingConnectionError {
834 remote_addr: SocketAddr,
836 peer_id: PeerId,
838 error: io::Error,
840 },
841 SessionClosedOnConnectionError {
843 peer_id: PeerId,
845 remote_addr: SocketAddr,
847 error: EthStreamError,
849 },
850 Disconnected {
852 peer_id: PeerId,
854 remote_addr: SocketAddr,
856 },
857}
858
859#[derive(Debug, thiserror::Error)]
861pub enum PendingSessionHandshakeError {
862 #[error(transparent)]
864 Eth(EthStreamError),
865 #[error(transparent)]
867 Ecies(ECIESError),
868 #[error("authentication timed out")]
870 Timeout,
871 #[error("Mandatory extra capability unsupported")]
873 UnsupportedExtraCapability,
874 #[error("unexpected identity in hello message: {0}")]
877 UnexpectedHandshakeIdentity(GotExpectedBoxed<PeerId>),
878}
879
880impl PendingSessionHandshakeError {
881 pub const fn as_disconnected(&self) -> Option<DisconnectReason> {
884 match self {
885 Self::Eth(eth_err) => eth_err.as_disconnected(),
886 Self::UnexpectedHandshakeIdentity(_) => {
887 Some(DisconnectReason::UnexpectedHandshakeIdentity)
888 }
889 _ => None,
890 }
891 }
892}
893
894#[derive(Debug, Clone, thiserror::Error)]
897#[error("session limit reached {0}")]
898pub struct ExceedsSessionLimit(pub(crate) u32);
899
900pub(crate) async fn pending_session_with_timeout<F, N: NetworkPrimitives>(
902 timeout: Duration,
903 session_id: SessionId,
904 remote_addr: SocketAddr,
905 direction: Direction,
906 events: mpsc::Sender<PendingSessionEvent<N>>,
907 f: F,
908) where
909 F: Future<Output = ()>,
910{
911 if tokio::time::timeout(timeout, f).await.is_err() {
912 trace!(target: "net::session", ?remote_addr, ?direction, "pending session timed out");
913 let event = PendingSessionEvent::Disconnected {
914 remote_addr,
915 session_id,
916 direction,
917 error: Some(PendingSessionHandshakeError::Timeout),
918 };
919 let _ = events.send(event).await;
920 }
921}
922
923#[expect(clippy::too_many_arguments)]
927pub(crate) async fn start_pending_incoming_session<N: NetworkPrimitives>(
928 handshake: Arc<dyn EthRlpxHandshake>,
929 eth_max_message_size: usize,
930 disconnect_rx: oneshot::Receiver<()>,
931 session_id: SessionId,
932 stream: TcpStream,
933 events: mpsc::Sender<PendingSessionEvent<N>>,
934 remote_addr: SocketAddr,
935 secret_key: SecretKey,
936 hello: HelloMessageWithProtocols,
937 status: UnifiedStatus,
938 fork_filter: ForkFilter,
939 extra_handlers: RlpxSubProtocolHandlers,
940) {
941 authenticate(
942 handshake,
943 eth_max_message_size,
944 disconnect_rx,
945 events,
946 stream,
947 session_id,
948 remote_addr,
949 secret_key,
950 Direction::Incoming,
951 hello,
952 status,
953 fork_filter,
954 extra_handlers,
955 )
956 .await
957}
958
959#[instrument(level = "trace", target = "net::network", skip_all, fields(%remote_addr, peer_id = ?remote_peer_id))]
961#[expect(clippy::too_many_arguments)]
962async fn start_pending_outbound_session<N: NetworkPrimitives>(
963 handshake: Arc<dyn EthRlpxHandshake>,
964 eth_max_message_size: usize,
965 disconnect_rx: oneshot::Receiver<()>,
966 events: mpsc::Sender<PendingSessionEvent<N>>,
967 session_id: SessionId,
968 remote_addr: SocketAddr,
969 remote_peer_id: PeerId,
970 secret_key: SecretKey,
971 hello: HelloMessageWithProtocols,
972 status: UnifiedStatus,
973 fork_filter: ForkFilter,
974 extra_handlers: RlpxSubProtocolHandlers,
975) {
976 let stream = match TcpStream::connect(remote_addr).await {
977 Ok(stream) => {
978 if let Err(err) = stream.set_nodelay(true) {
979 tracing::warn!(target: "net::session", "set nodelay failed: {:?}", err);
980 }
981 stream
982 }
983 Err(error) => {
984 let _ = events
985 .send(PendingSessionEvent::OutgoingConnectionError {
986 remote_addr,
987 session_id,
988 peer_id: remote_peer_id,
989 error,
990 })
991 .await;
992 return
993 }
994 };
995 authenticate(
996 handshake,
997 eth_max_message_size,
998 disconnect_rx,
999 events,
1000 stream,
1001 session_id,
1002 remote_addr,
1003 secret_key,
1004 Direction::Outgoing(remote_peer_id),
1005 hello,
1006 status,
1007 fork_filter,
1008 extra_handlers,
1009 )
1010 .await
1011}
1012
1013#[expect(clippy::too_many_arguments)]
1015async fn authenticate<N: NetworkPrimitives>(
1016 handshake: Arc<dyn EthRlpxHandshake>,
1017 eth_max_message_size: usize,
1018 disconnect_rx: oneshot::Receiver<()>,
1019 events: mpsc::Sender<PendingSessionEvent<N>>,
1020 stream: TcpStream,
1021 session_id: SessionId,
1022 remote_addr: SocketAddr,
1023 secret_key: SecretKey,
1024 direction: Direction,
1025 hello: HelloMessageWithProtocols,
1026 status: UnifiedStatus,
1027 fork_filter: ForkFilter,
1028 extra_handlers: RlpxSubProtocolHandlers,
1029) {
1030 let local_addr = stream.local_addr().ok();
1031 let stream = match get_ecies_stream(stream, secret_key, direction).await {
1032 Ok(stream) => stream,
1033 Err(error) => {
1034 let _ = events
1035 .send(PendingSessionEvent::EciesAuthError {
1036 remote_addr,
1037 session_id,
1038 error,
1039 direction,
1040 })
1041 .await;
1042 return
1043 }
1044 };
1045
1046 let unauthed = UnauthedP2PStream::new(stream);
1047
1048 let auth = authenticate_stream(
1049 handshake,
1050 eth_max_message_size,
1051 unauthed,
1052 session_id,
1053 remote_addr,
1054 local_addr,
1055 direction,
1056 hello,
1057 status,
1058 fork_filter,
1059 extra_handlers,
1060 )
1061 .boxed();
1062
1063 match futures::future::select(disconnect_rx, auth).await {
1064 Either::Left((_, _)) => {
1065 let _ = events
1066 .send(PendingSessionEvent::Disconnected {
1067 remote_addr,
1068 session_id,
1069 direction,
1070 error: None,
1071 })
1072 .await;
1073 }
1074 Either::Right((res, _)) => {
1075 let _ = events.send(res).await;
1076 }
1077 }
1078}
1079
1080async fn get_ecies_stream<Io: AsyncRead + AsyncWrite + Unpin>(
1083 stream: Io,
1084 secret_key: SecretKey,
1085 direction: Direction,
1086) -> Result<ECIESStream<Io>, ECIESError> {
1087 match direction {
1088 Direction::Incoming => ECIESStream::incoming(stream, secret_key).await,
1089 Direction::Outgoing(remote_peer_id) => {
1090 ECIESStream::connect(stream, secret_key, remote_peer_id).await
1091 }
1092 }
1093}
1094
1095#[expect(clippy::too_many_arguments)]
1102async fn authenticate_stream<N: NetworkPrimitives>(
1103 handshake: Arc<dyn EthRlpxHandshake>,
1104 eth_max_message_size: usize,
1105 stream: UnauthedP2PStream<ECIESStream<TcpStream>>,
1106 session_id: SessionId,
1107 remote_addr: SocketAddr,
1108 local_addr: Option<SocketAddr>,
1109 direction: Direction,
1110 mut hello: HelloMessageWithProtocols,
1111 mut status: UnifiedStatus,
1112 fork_filter: ForkFilter,
1113 mut extra_handlers: RlpxSubProtocolHandlers,
1114) -> PendingSessionEvent<N> {
1115 extra_handlers.retain(|handler| hello.try_add_protocol(handler.protocol()).is_ok());
1117
1118 let authenticated_peer_id = stream.inner().remote_id();
1119
1120 let (mut p2p_stream, their_hello) = match stream.handshake(hello).await {
1122 Ok(stream_res) => stream_res,
1123 Err(err) => {
1124 return PendingSessionEvent::Disconnected {
1125 remote_addr,
1126 session_id,
1127 direction,
1128 error: Some(PendingSessionHandshakeError::Eth(err.into())),
1129 }
1130 }
1131 };
1132
1133 if their_hello.id != authenticated_peer_id {
1140 let _ = p2p_stream.disconnect(DisconnectReason::UnexpectedHandshakeIdentity).await;
1141
1142 return PendingSessionEvent::Disconnected {
1143 remote_addr,
1144 session_id,
1145 direction,
1146 error: Some(PendingSessionHandshakeError::UnexpectedHandshakeIdentity(
1147 GotExpected { got: their_hello.id, expected: authenticated_peer_id }.into(),
1148 )),
1149 }
1150 }
1151
1152 if !extra_handlers.is_empty() {
1154 while let Some(pos) = extra_handlers.iter().position(|handler| {
1156 p2p_stream
1157 .shared_capabilities()
1158 .ensure_matching_capability(&handler.protocol().cap)
1159 .is_err()
1160 }) {
1161 let handler = extra_handlers.remove(pos);
1162 if handler.on_unsupported_by_peer(
1163 p2p_stream.shared_capabilities(),
1164 direction,
1165 authenticated_peer_id,
1166 ) == OnNotSupported::Disconnect
1167 {
1168 return PendingSessionEvent::Disconnected {
1169 remote_addr,
1170 session_id,
1171 direction,
1172 error: Some(PendingSessionHandshakeError::UnsupportedExtraCapability),
1173 };
1174 }
1175 }
1176 }
1177
1178 let eth_version = match p2p_stream.shared_capabilities().eth_version() {
1180 Ok(version) => version,
1181 Err(err) => {
1182 return PendingSessionEvent::Disconnected {
1183 remote_addr,
1184 session_id,
1185 direction,
1186 error: Some(PendingSessionHandshakeError::Eth(err.into())),
1187 }
1188 }
1189 };
1190
1191 status.set_eth_version(eth_version);
1193
1194 let (conn, their_status) = if p2p_stream.shared_capabilities().len() == 1 {
1195 match handshake
1200 .handshake(&mut p2p_stream, status, fork_filter.clone(), HANDSHAKE_TIMEOUT)
1201 .await
1202 {
1203 Ok(their_status) => {
1204 let eth_stream =
1205 EthStream::with_max_message_size(eth_version, p2p_stream, eth_max_message_size);
1206 (eth_stream.into(), their_status)
1207 }
1208 Err(err) => {
1209 return PendingSessionEvent::Disconnected {
1210 remote_addr,
1211 session_id,
1212 direction,
1213 error: Some(PendingSessionHandshakeError::Eth(err)),
1214 }
1215 }
1216 }
1217 } else if p2p_stream.shared_capabilities().is_exact_eth_snap_v2() {
1218 match EthSnapStream::handshake(
1223 p2p_stream,
1224 status,
1225 fork_filter,
1226 handshake,
1227 eth_max_message_size,
1228 )
1229 .await
1230 {
1231 Ok((stream, their_status)) => (stream.into(), their_status),
1232 Err(err) => {
1233 return PendingSessionEvent::Disconnected {
1234 remote_addr,
1235 session_id,
1236 direction,
1237 error: Some(PendingSessionHandshakeError::Eth(err)),
1238 }
1239 }
1240 }
1241 } else {
1242 let mut multiplex_stream = RlpxProtocolMultiplexer::new(p2p_stream);
1244
1245 for handler in extra_handlers.into_iter() {
1247 let protocol = handler.protocol();
1248 let limits = handler.inbound_limits();
1249 let remote_peer_id = authenticated_peer_id;
1250
1251 multiplex_stream
1255 .install_protocol_with_limits(&protocol.cap, limits, move |conn| {
1256 handler.into_connection(direction, remote_peer_id, conn)
1257 })
1258 .expect("remaining handler capability was negotiated");
1259 }
1260
1261 let (multiplex_stream, their_status) = match multiplex_stream
1262 .into_eth_satellite_stream(status, fork_filter, handshake, eth_max_message_size)
1263 .await
1264 {
1265 Ok((multiplex_stream, their_status)) => (multiplex_stream, their_status),
1266 Err(err) => {
1267 return PendingSessionEvent::Disconnected {
1268 remote_addr,
1269 session_id,
1270 direction,
1271 error: Some(PendingSessionHandshakeError::Eth(err)),
1272 }
1273 }
1274 };
1275
1276 (multiplex_stream.into(), their_status)
1277 };
1278
1279 let peer_listen_port = (their_hello.port != 0).then_some(their_hello.port);
1281
1282 PendingSessionEvent::Established {
1283 session_id,
1284 remote_addr,
1285 local_addr,
1286 peer_id: authenticated_peer_id,
1287 capabilities: Arc::new(Capabilities::from(their_hello.capabilities)),
1288 status: Arc::new(their_status),
1289 conn,
1290 direction,
1291 client_id: their_hello.client_version,
1292 peer_listen_port,
1293 }
1294}