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 set_fork_filter(&mut self, fork_filter: ForkFilter) -> ForkId {
278 self.fork_filter = fork_filter;
279 self.status.forkid = self.fork_filter.current();
280 self.status.forkid
281 }
282
283 pub(crate) fn on_incoming(
288 &mut self,
289 stream: TcpStream,
290 remote_addr: SocketAddr,
291 ) -> Result<SessionId, ExceedsSessionLimit> {
292 self.counter.ensure_pending_inbound()?;
293
294 let session_id = self.next_id();
295
296 trace!(
297 target: "net::session",
298 ?remote_addr,
299 ?session_id,
300 "new pending incoming session"
301 );
302
303 let (disconnect_tx, disconnect_rx) = oneshot::channel();
304 let pending_events = self.pending_sessions_tx.clone();
305 let secret_key = self.secret_key;
306 let hello_message = self.hello_message.clone();
307 let status = self.status;
308 let fork_filter = self.fork_filter.clone();
309 let extra_handlers = self.extra_protocols.on_incoming(remote_addr);
310 self.spawn(pending_session_with_timeout(
311 self.pending_session_timeout,
312 session_id,
313 remote_addr,
314 Direction::Incoming,
315 pending_events.clone(),
316 start_pending_incoming_session(
317 self.handshake.clone(),
318 self.eth_max_message_size,
319 disconnect_rx,
320 session_id,
321 stream,
322 pending_events,
323 remote_addr,
324 secret_key,
325 hello_message,
326 status,
327 fork_filter,
328 extra_handlers,
329 ),
330 ));
331
332 let handle = PendingSessionHandle {
333 disconnect_tx: Some(disconnect_tx),
334 direction: Direction::Incoming,
335 };
336 self.pending_sessions.insert(session_id, handle);
337 self.counter.inc_pending_inbound();
338 Ok(session_id)
339 }
340
341 pub fn dial_outbound(&mut self, remote_addr: SocketAddr, remote_peer_id: PeerId) {
343 if self.counter.ensure_pending_outbound().is_ok() {
345 let session_id = self.next_id();
346 let (disconnect_tx, disconnect_rx) = oneshot::channel();
347 let pending_events = self.pending_sessions_tx.clone();
348 let secret_key = self.secret_key;
349 let hello_message = self.hello_message.clone();
350 let fork_filter = self.fork_filter.clone();
351 let status = self.status;
352 let extra_handlers = self.extra_protocols.on_outgoing(remote_addr, remote_peer_id);
353 self.spawn(pending_session_with_timeout(
354 self.pending_session_timeout,
355 session_id,
356 remote_addr,
357 Direction::Outgoing(remote_peer_id),
358 pending_events.clone(),
359 start_pending_outbound_session(
360 self.handshake.clone(),
361 self.eth_max_message_size,
362 disconnect_rx,
363 pending_events,
364 session_id,
365 remote_addr,
366 remote_peer_id,
367 secret_key,
368 hello_message,
369 status,
370 fork_filter,
371 extra_handlers,
372 ),
373 ));
374
375 let handle = PendingSessionHandle {
376 disconnect_tx: Some(disconnect_tx),
377 direction: Direction::Outgoing(remote_peer_id),
378 };
379 self.pending_sessions.insert(session_id, handle);
380 self.counter.inc_pending_outbound();
381 }
382 }
383
384 pub fn disconnect(&self, node: PeerId, reason: Option<DisconnectReason>) {
389 if let Some(session) = self.active_sessions.get(&node) {
390 session.disconnect(reason);
391 }
392 }
393
394 pub fn disconnect_all(&self, reason: Option<DisconnectReason>) {
399 for session in self.active_sessions.values() {
400 session.disconnect(reason);
401 }
402 }
403
404 pub fn disconnect_all_pending(&mut self) {
406 for session in self.pending_sessions.values_mut() {
407 session.disconnect();
408 }
409 }
410
411 pub fn send_message(&self, peer_id: &PeerId, msg: PeerMessage<N>) {
418 if let Some(session) = self.active_sessions.get(peer_id) &&
419 !session.commands.send_message(msg)
420 {
421 self.metrics.total_outgoing_peer_messages_dropped.increment(1);
422 }
423 }
424
425 fn remove_pending_session(&mut self, id: &SessionId) -> Option<PendingSessionHandle> {
427 let session = self.pending_sessions.remove(id)?;
428 self.counter.dec_pending(&session.direction);
429 Some(session)
430 }
431
432 fn remove_active_session(&mut self, id: &PeerId) -> Option<ActiveSessionHandle<N>> {
434 let session = self.active_sessions.remove(id)?;
435 self.counter.dec_active(&session.direction);
436 Some(session)
437 }
438
439 pub(crate) fn try_disconnect_incoming_connection(
443 &self,
444 stream: TcpStream,
445 reason: DisconnectReason,
446 ) {
447 if !self.disconnections_counter.has_capacity() {
448 return
450 }
451
452 let guard = self.disconnections_counter.clone();
453 let secret_key = self.secret_key;
454
455 self.spawn(async move {
456 trace!(
457 target: "net::session",
458 "gracefully disconnecting incoming connection"
459 );
460 if let Ok(stream) = get_ecies_stream(stream, secret_key, Direction::Incoming).await {
461 let mut unauth = UnauthedP2PStream::new(stream);
462 let _ = unauth.send_disconnect(reason).await;
463 drop(guard);
464 }
465 });
466 }
467
468 pub(crate) fn poll(&mut self, cx: &mut Context<'_>) -> Poll<SessionEvent<N>> {
472 match self.active_session_rx.poll_next_unpin(cx) {
474 Poll::Pending => {}
475 Poll::Ready(None) => {
476 unreachable!("Manager holds both channel halves.")
477 }
478 Poll::Ready(Some(event)) => {
479 return match event {
480 ActiveSessionMessage::Disconnected { peer_id, remote_addr } => {
481 trace!(
482 target: "net::session",
483 ?peer_id,
484 "gracefully disconnected active session."
485 );
486 self.remove_active_session(&peer_id);
487 Poll::Ready(SessionEvent::Disconnected { peer_id, remote_addr })
488 }
489 ActiveSessionMessage::ClosedOnConnectionError {
490 peer_id,
491 remote_addr,
492 error,
493 } => {
494 trace!(target: "net::session", ?peer_id, %error,"closed session.");
495 self.remove_active_session(&peer_id);
496 Poll::Ready(SessionEvent::SessionClosedOnConnectionError {
497 remote_addr,
498 peer_id,
499 error,
500 })
501 }
502 ActiveSessionMessage::ValidMessage { peer_id, message } => {
503 Poll::Ready(SessionEvent::ValidMessage { peer_id, message })
504 }
505 ActiveSessionMessage::BadMessage { peer_id } => {
506 Poll::Ready(SessionEvent::BadMessage { peer_id })
507 }
508 ActiveSessionMessage::ProtocolBreach { peer_id } => {
509 Poll::Ready(SessionEvent::ProtocolBreach { peer_id })
510 }
511 }
512 }
513 }
514
515 let event = match self.pending_session_rx.poll_next_unpin(cx) {
517 Poll::Pending => return Poll::Pending,
518 Poll::Ready(None) => unreachable!("Manager holds both channel halves."),
519 Poll::Ready(Some(event)) => event,
520 };
521 match event {
522 PendingSessionEvent::Established {
523 session_id,
524 remote_addr,
525 local_addr,
526 peer_id,
527 capabilities,
528 mut conn,
529 status,
530 direction,
531 client_id,
532 peer_listen_port,
533 } => {
534 self.remove_pending_session(&session_id);
536
537 if self.active_sessions.contains_key(&peer_id) {
539 trace!(
540 target: "net::session",
541 ?session_id,
542 ?remote_addr,
543 ?peer_id,
544 ?direction,
545 "already connected"
546 );
547
548 self.spawn(async move {
549 let _ =
551 conn.into_inner().disconnect(DisconnectReason::AlreadyConnected).await;
552 });
553
554 return Poll::Ready(SessionEvent::AlreadyConnected {
555 peer_id,
556 remote_addr,
557 direction,
558 })
559 }
560
561 let (commands_tx, commands_rx) = mpsc::channel(self.session_command_buffer);
562 let (unbounded_tx, unbounded_rx) = mpsc::unbounded_channel();
563
564 let (to_session_tx, messages_rx) = mpsc::channel(self.session_command_buffer);
565
566 let messages = PeerRequestSender::new(peer_id, to_session_tx);
567
568 let timeout = Arc::new(AtomicU64::new(
569 self.initial_internal_request_timeout.as_millis() as u64,
570 ));
571
572 let version = conn.version();
574
575 let range_update_interval = (conn.version() >= EthVersion::Eth69).then(|| {
580 let start = tokio::time::Instant::now() + RANGE_UPDATE_INTERVAL;
581 let mut interval = tokio::time::interval_at(start, RANGE_UPDATE_INTERVAL);
582 interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
583 interval
584 });
585
586 let broadcast_items = BroadcastItemCounter::new();
592 let remote_range_info = status.block_range_update().map(|update| {
593 BlockRangeInfo::new(update.earliest, update.latest, update.latest_hash)
594 });
595
596 if self.reject_block_announcements {
597 conn.set_reject_block_announcements(true);
598 }
599
600 let session = ActiveSession {
601 next_id: 0,
602 remote_peer_id: peer_id,
603 remote_addr,
604 remote_capabilities: Arc::clone(&capabilities),
605 session_id,
606 commands_rx: ReceiverStream::new(commands_rx),
607 unbounded_rx,
608 unbounded_broadcast_msgs: self.metrics.total_unbounded_broadcast_msgs.clone(),
609 to_session_manager: self.active_session_tx.clone(),
610 pending_message_to_session: None,
611 internal_request_rx: ReceiverStream::new(messages_rx).fuse(),
612 inflight_requests: Default::default(),
613 conn,
614 queued_outgoing: QueuedOutgoingMessages::new(
615 self.metrics.queued_outgoing_messages.clone(),
616 broadcast_items.clone(),
617 ),
618 received_requests_from_remote: Default::default(),
619 internal_request_timeout_interval: request_timeout_interval(
620 self.initial_internal_request_timeout,
621 ),
622 internal_request_timeout: Arc::clone(&timeout),
623 protocol_breach_request_timeout: self.protocol_breach_request_timeout,
624 terminate_message: None,
625 range_info: remote_range_info.clone(),
626 local_range_info: self.local_range_info.clone(),
627 range_update_interval,
628 last_sent_latest_block: None,
629 };
630
631 let supports_snap = session.conn.supports_snap();
632 self.spawn(session);
633
634 let client_version = client_id.into();
635 let handle = ActiveSessionHandle {
636 status: status.clone(),
637 direction,
638 session_id,
639 remote_id: peer_id,
640 version,
641 established: Instant::now(),
642 capabilities: Arc::clone(&capabilities),
643 commands: SessionCommandSender::new(commands_tx, unbounded_tx, broadcast_items),
644 client_version: Arc::clone(&client_version),
645 remote_addr,
646 local_addr,
647 peer_listen_port,
648 };
649
650 self.active_sessions.insert(peer_id, handle);
651 self.counter.inc_active(&direction);
652
653 if direction.is_outgoing() {
654 self.metrics.total_dial_successes.increment(1);
655 }
656
657 Poll::Ready(SessionEvent::SessionEstablished {
658 peer_id,
659 remote_addr,
660 client_version,
661 version,
662 capabilities,
663 status,
664 messages,
665 direction,
666 timeout,
667 range_info: remote_range_info,
668 supports_snap,
669 })
670 }
671 PendingSessionEvent::Disconnected { remote_addr, session_id, direction, error } => {
672 trace!(
673 target: "net::session",
674 ?session_id,
675 ?remote_addr,
676 ?error,
677 "disconnected pending session"
678 );
679 self.remove_pending_session(&session_id);
680 match direction {
681 Direction::Incoming => {
682 Poll::Ready(SessionEvent::IncomingPendingSessionClosed {
683 remote_addr,
684 error,
685 })
686 }
687 Direction::Outgoing(peer_id) => {
688 Poll::Ready(SessionEvent::OutgoingPendingSessionClosed {
689 remote_addr,
690 peer_id,
691 error,
692 })
693 }
694 }
695 }
696 PendingSessionEvent::OutgoingConnectionError {
697 remote_addr,
698 session_id,
699 peer_id,
700 error,
701 } => {
702 trace!(
703 target: "net::session",
704 %error,
705 ?session_id,
706 ?remote_addr,
707 ?peer_id,
708 "connection refused"
709 );
710 self.remove_pending_session(&session_id);
711 Poll::Ready(SessionEvent::OutgoingConnectionError { remote_addr, peer_id, error })
712 }
713 PendingSessionEvent::EciesAuthError { remote_addr, session_id, error, direction } => {
714 trace!(
715 target: "net::session",
716 %error,
717 ?session_id,
718 ?remote_addr,
719 "ecies auth failed"
720 );
721 self.remove_pending_session(&session_id);
722 match direction {
723 Direction::Incoming => {
724 Poll::Ready(SessionEvent::IncomingPendingSessionClosed {
725 remote_addr,
726 error: Some(PendingSessionHandshakeError::Ecies(error)),
727 })
728 }
729 Direction::Outgoing(peer_id) => {
730 Poll::Ready(SessionEvent::OutgoingPendingSessionClosed {
731 remote_addr,
732 peer_id,
733 error: Some(PendingSessionHandshakeError::Ecies(error)),
734 })
735 }
736 }
737 }
738 }
739 }
740
741 pub(crate) fn update_advertised_block_range(&mut self, block_range_update: BlockRangeUpdate) {
749 self.status.earliest_block = Some(block_range_update.earliest);
750 self.status.latest_block = Some(block_range_update.latest);
751 self.status.blockhash = block_range_update.latest_hash;
752
753 self.local_range_info.update(
755 block_range_update.earliest,
756 block_range_update.latest,
757 block_range_update.latest_hash,
758 );
759 }
760}
761
762#[derive(Default, Debug, Clone)]
764struct DisconnectionsCounter(Arc<()>);
765
766impl DisconnectionsCounter {
767 const MAX_CONCURRENT_GRACEFUL_DISCONNECTIONS: usize = 15;
768
769 fn has_capacity(&self) -> bool {
772 Arc::strong_count(&self.0) <= Self::MAX_CONCURRENT_GRACEFUL_DISCONNECTIONS
773 }
774}
775
776#[derive(Debug)]
778pub enum SessionEvent<N: NetworkPrimitives> {
779 SessionEstablished {
783 peer_id: PeerId,
785 remote_addr: SocketAddr,
787 client_version: Arc<str>,
789 capabilities: Arc<Capabilities>,
791 version: EthVersion,
793 status: Arc<UnifiedStatus>,
795 messages: PeerRequestSender<PeerRequest<N>>,
797 direction: Direction,
799 timeout: Arc<AtomicU64>,
802 range_info: Option<BlockRangeInfo>,
804 supports_snap: bool,
806 },
807 AlreadyConnected {
809 peer_id: PeerId,
811 remote_addr: SocketAddr,
813 direction: Direction,
815 },
816 ValidMessage {
818 peer_id: PeerId,
820 message: PeerMessage<N>,
822 },
823 BadMessage {
825 peer_id: PeerId,
827 },
828 ProtocolBreach {
830 peer_id: PeerId,
832 },
833 IncomingPendingSessionClosed {
835 remote_addr: SocketAddr,
837 error: Option<PendingSessionHandshakeError>,
839 },
840 OutgoingPendingSessionClosed {
842 remote_addr: SocketAddr,
844 peer_id: PeerId,
846 error: Option<PendingSessionHandshakeError>,
848 },
849 OutgoingConnectionError {
851 remote_addr: SocketAddr,
853 peer_id: PeerId,
855 error: io::Error,
857 },
858 SessionClosedOnConnectionError {
860 peer_id: PeerId,
862 remote_addr: SocketAddr,
864 error: EthStreamError,
866 },
867 Disconnected {
869 peer_id: PeerId,
871 remote_addr: SocketAddr,
873 },
874}
875
876#[derive(Debug, thiserror::Error)]
878pub enum PendingSessionHandshakeError {
879 #[error(transparent)]
881 Eth(EthStreamError),
882 #[error(transparent)]
884 Ecies(ECIESError),
885 #[error("authentication timed out")]
887 Timeout,
888 #[error("Mandatory extra capability unsupported")]
890 UnsupportedExtraCapability,
891 #[error("unexpected identity in hello message: {0}")]
894 UnexpectedHandshakeIdentity(GotExpectedBoxed<PeerId>),
895}
896
897impl PendingSessionHandshakeError {
898 pub const fn as_disconnected(&self) -> Option<DisconnectReason> {
901 match self {
902 Self::Eth(eth_err) => eth_err.as_disconnected(),
903 Self::UnexpectedHandshakeIdentity(_) => {
904 Some(DisconnectReason::UnexpectedHandshakeIdentity)
905 }
906 _ => None,
907 }
908 }
909}
910
911#[derive(Debug, Clone, thiserror::Error)]
914#[error("session limit reached {0}")]
915pub struct ExceedsSessionLimit(pub(crate) u32);
916
917pub(crate) async fn pending_session_with_timeout<F, N: NetworkPrimitives>(
919 timeout: Duration,
920 session_id: SessionId,
921 remote_addr: SocketAddr,
922 direction: Direction,
923 events: mpsc::Sender<PendingSessionEvent<N>>,
924 f: F,
925) where
926 F: Future<Output = ()>,
927{
928 if tokio::time::timeout(timeout, f).await.is_err() {
929 trace!(target: "net::session", ?remote_addr, ?direction, "pending session timed out");
930 let event = PendingSessionEvent::Disconnected {
931 remote_addr,
932 session_id,
933 direction,
934 error: Some(PendingSessionHandshakeError::Timeout),
935 };
936 let _ = events.send(event).await;
937 }
938}
939
940#[expect(clippy::too_many_arguments)]
944pub(crate) async fn start_pending_incoming_session<N: NetworkPrimitives>(
945 handshake: Arc<dyn EthRlpxHandshake>,
946 eth_max_message_size: usize,
947 disconnect_rx: oneshot::Receiver<()>,
948 session_id: SessionId,
949 stream: TcpStream,
950 events: mpsc::Sender<PendingSessionEvent<N>>,
951 remote_addr: SocketAddr,
952 secret_key: SecretKey,
953 hello: HelloMessageWithProtocols,
954 status: UnifiedStatus,
955 fork_filter: ForkFilter,
956 extra_handlers: RlpxSubProtocolHandlers,
957) {
958 authenticate(
959 handshake,
960 eth_max_message_size,
961 disconnect_rx,
962 events,
963 stream,
964 session_id,
965 remote_addr,
966 secret_key,
967 Direction::Incoming,
968 hello,
969 status,
970 fork_filter,
971 extra_handlers,
972 )
973 .await
974}
975
976#[instrument(level = "trace", target = "net::network", skip_all, fields(%remote_addr, peer_id = ?remote_peer_id))]
978#[expect(clippy::too_many_arguments)]
979async fn start_pending_outbound_session<N: NetworkPrimitives>(
980 handshake: Arc<dyn EthRlpxHandshake>,
981 eth_max_message_size: usize,
982 disconnect_rx: oneshot::Receiver<()>,
983 events: mpsc::Sender<PendingSessionEvent<N>>,
984 session_id: SessionId,
985 remote_addr: SocketAddr,
986 remote_peer_id: PeerId,
987 secret_key: SecretKey,
988 hello: HelloMessageWithProtocols,
989 status: UnifiedStatus,
990 fork_filter: ForkFilter,
991 extra_handlers: RlpxSubProtocolHandlers,
992) {
993 let stream = match TcpStream::connect(remote_addr).await {
994 Ok(stream) => {
995 if let Err(err) = stream.set_nodelay(true) {
996 tracing::warn!(target: "net::session", "set nodelay failed: {:?}", err);
997 }
998 stream
999 }
1000 Err(error) => {
1001 let _ = events
1002 .send(PendingSessionEvent::OutgoingConnectionError {
1003 remote_addr,
1004 session_id,
1005 peer_id: remote_peer_id,
1006 error,
1007 })
1008 .await;
1009 return
1010 }
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::Outgoing(remote_peer_id),
1022 hello,
1023 status,
1024 fork_filter,
1025 extra_handlers,
1026 )
1027 .await
1028}
1029
1030#[expect(clippy::too_many_arguments)]
1032async fn authenticate<N: NetworkPrimitives>(
1033 handshake: Arc<dyn EthRlpxHandshake>,
1034 eth_max_message_size: usize,
1035 disconnect_rx: oneshot::Receiver<()>,
1036 events: mpsc::Sender<PendingSessionEvent<N>>,
1037 stream: TcpStream,
1038 session_id: SessionId,
1039 remote_addr: SocketAddr,
1040 secret_key: SecretKey,
1041 direction: Direction,
1042 hello: HelloMessageWithProtocols,
1043 status: UnifiedStatus,
1044 fork_filter: ForkFilter,
1045 extra_handlers: RlpxSubProtocolHandlers,
1046) {
1047 let local_addr = stream.local_addr().ok();
1048 let stream = match get_ecies_stream(stream, secret_key, direction).await {
1049 Ok(stream) => stream,
1050 Err(error) => {
1051 let _ = events
1052 .send(PendingSessionEvent::EciesAuthError {
1053 remote_addr,
1054 session_id,
1055 error,
1056 direction,
1057 })
1058 .await;
1059 return
1060 }
1061 };
1062
1063 let unauthed = UnauthedP2PStream::new(stream);
1064
1065 let auth = authenticate_stream(
1066 handshake,
1067 eth_max_message_size,
1068 unauthed,
1069 session_id,
1070 remote_addr,
1071 local_addr,
1072 direction,
1073 hello,
1074 status,
1075 fork_filter,
1076 extra_handlers,
1077 )
1078 .boxed();
1079
1080 match futures::future::select(disconnect_rx, auth).await {
1081 Either::Left((_, _)) => {
1082 let _ = events
1083 .send(PendingSessionEvent::Disconnected {
1084 remote_addr,
1085 session_id,
1086 direction,
1087 error: None,
1088 })
1089 .await;
1090 }
1091 Either::Right((res, _)) => {
1092 let _ = events.send(res).await;
1093 }
1094 }
1095}
1096
1097async fn get_ecies_stream<Io: AsyncRead + AsyncWrite + Unpin>(
1100 stream: Io,
1101 secret_key: SecretKey,
1102 direction: Direction,
1103) -> Result<ECIESStream<Io>, ECIESError> {
1104 match direction {
1105 Direction::Incoming => ECIESStream::incoming(stream, secret_key).await,
1106 Direction::Outgoing(remote_peer_id) => {
1107 ECIESStream::connect(stream, secret_key, remote_peer_id).await
1108 }
1109 }
1110}
1111
1112#[expect(clippy::too_many_arguments)]
1119async fn authenticate_stream<N: NetworkPrimitives>(
1120 handshake: Arc<dyn EthRlpxHandshake>,
1121 eth_max_message_size: usize,
1122 stream: UnauthedP2PStream<ECIESStream<TcpStream>>,
1123 session_id: SessionId,
1124 remote_addr: SocketAddr,
1125 local_addr: Option<SocketAddr>,
1126 direction: Direction,
1127 mut hello: HelloMessageWithProtocols,
1128 mut status: UnifiedStatus,
1129 fork_filter: ForkFilter,
1130 mut extra_handlers: RlpxSubProtocolHandlers,
1131) -> PendingSessionEvent<N> {
1132 extra_handlers.retain(|handler| hello.try_add_protocol(handler.protocol()).is_ok());
1134
1135 let authenticated_peer_id = stream.inner().remote_id();
1136
1137 let (mut p2p_stream, their_hello) = match stream.handshake(hello).await {
1139 Ok(stream_res) => stream_res,
1140 Err(err) => {
1141 return PendingSessionEvent::Disconnected {
1142 remote_addr,
1143 session_id,
1144 direction,
1145 error: Some(PendingSessionHandshakeError::Eth(err.into())),
1146 }
1147 }
1148 };
1149
1150 if their_hello.id != authenticated_peer_id {
1157 let _ = p2p_stream.disconnect(DisconnectReason::UnexpectedHandshakeIdentity).await;
1158
1159 return PendingSessionEvent::Disconnected {
1160 remote_addr,
1161 session_id,
1162 direction,
1163 error: Some(PendingSessionHandshakeError::UnexpectedHandshakeIdentity(
1164 GotExpected { got: their_hello.id, expected: authenticated_peer_id }.into(),
1165 )),
1166 }
1167 }
1168
1169 if !extra_handlers.is_empty() {
1171 while let Some(pos) = extra_handlers.iter().position(|handler| {
1173 p2p_stream
1174 .shared_capabilities()
1175 .ensure_matching_capability(&handler.protocol().cap)
1176 .is_err()
1177 }) {
1178 let handler = extra_handlers.remove(pos);
1179 if handler.on_unsupported_by_peer(
1180 p2p_stream.shared_capabilities(),
1181 direction,
1182 authenticated_peer_id,
1183 ) == OnNotSupported::Disconnect
1184 {
1185 return PendingSessionEvent::Disconnected {
1186 remote_addr,
1187 session_id,
1188 direction,
1189 error: Some(PendingSessionHandshakeError::UnsupportedExtraCapability),
1190 };
1191 }
1192 }
1193 }
1194
1195 let eth_version = match p2p_stream.shared_capabilities().eth_version() {
1197 Ok(version) => version,
1198 Err(err) => {
1199 return PendingSessionEvent::Disconnected {
1200 remote_addr,
1201 session_id,
1202 direction,
1203 error: Some(PendingSessionHandshakeError::Eth(err.into())),
1204 }
1205 }
1206 };
1207
1208 status.set_eth_version(eth_version);
1210
1211 let (conn, their_status) = if p2p_stream.shared_capabilities().len() == 1 {
1212 match handshake
1217 .handshake(&mut p2p_stream, status, fork_filter.clone(), HANDSHAKE_TIMEOUT)
1218 .await
1219 {
1220 Ok(their_status) => {
1221 let eth_stream =
1222 EthStream::with_max_message_size(eth_version, p2p_stream, eth_max_message_size);
1223 (eth_stream.into(), their_status)
1224 }
1225 Err(err) => {
1226 return PendingSessionEvent::Disconnected {
1227 remote_addr,
1228 session_id,
1229 direction,
1230 error: Some(PendingSessionHandshakeError::Eth(err)),
1231 }
1232 }
1233 }
1234 } else if p2p_stream.shared_capabilities().is_exact_eth_snap_v2() {
1235 match EthSnapStream::handshake(
1240 p2p_stream,
1241 status,
1242 fork_filter,
1243 handshake,
1244 eth_max_message_size,
1245 )
1246 .await
1247 {
1248 Ok((stream, their_status)) => (stream.into(), their_status),
1249 Err(err) => {
1250 return PendingSessionEvent::Disconnected {
1251 remote_addr,
1252 session_id,
1253 direction,
1254 error: Some(PendingSessionHandshakeError::Eth(err)),
1255 }
1256 }
1257 }
1258 } else {
1259 let mut multiplex_stream = RlpxProtocolMultiplexer::new(p2p_stream);
1261
1262 for handler in extra_handlers.into_iter() {
1264 let protocol = handler.protocol();
1265 let limits = handler.inbound_limits();
1266 let remote_peer_id = authenticated_peer_id;
1267
1268 multiplex_stream
1272 .install_protocol_with_limits(&protocol.cap, limits, move |conn| {
1273 handler.into_connection(direction, remote_peer_id, conn)
1274 })
1275 .expect("remaining handler capability was negotiated");
1276 }
1277
1278 let (multiplex_stream, their_status) = match multiplex_stream
1279 .into_eth_satellite_stream(status, fork_filter, handshake, eth_max_message_size)
1280 .await
1281 {
1282 Ok((multiplex_stream, their_status)) => (multiplex_stream, their_status),
1283 Err(err) => {
1284 return PendingSessionEvent::Disconnected {
1285 remote_addr,
1286 session_id,
1287 direction,
1288 error: Some(PendingSessionHandshakeError::Eth(err)),
1289 }
1290 }
1291 };
1292
1293 (multiplex_stream.into(), their_status)
1294 };
1295
1296 let peer_listen_port = (their_hello.port != 0).then_some(their_hello.port);
1298
1299 PendingSessionEvent::Established {
1300 session_id,
1301 remote_addr,
1302 local_addr,
1303 peer_id: authenticated_peer_id,
1304 capabilities: Arc::new(Capabilities::from(their_hello.capabilities)),
1305 status: Arc::new(their_status),
1306 conn,
1307 direction,
1308 client_id: their_hello.client_version,
1309 peer_listen_port,
1310 }
1311}