1+ use super :: inbound_rate_limit:: InboundIpRateLimiter ;
12use super :: request_handler:: InboundRequestHandler ;
23use crate :: {
34 config:: Config ,
45 connection:: Connection ,
5- endpoint:: { Connecting , Endpoint } ,
6+ endpoint:: { Connecting , Endpoint , Incoming } ,
67 types:: { Address , DisconnectReason , PeerAffinity , PeerEvent , PeerInfo } ,
78 ConnectionOrigin , PeerId , Request , Response , Result ,
89} ;
@@ -11,6 +12,7 @@ use std::{
1112 collections:: { hash_map:: Entry , HashMap } ,
1213 convert:: Infallible ,
1314 sync:: { Arc , RwLock } ,
15+ time:: Instant ,
1416} ;
1517use tokio:: {
1618 sync:: { broadcast, mpsc, oneshot} ,
@@ -54,6 +56,10 @@ pub(crate) struct ConnectionManager {
5456 pending_dials : HashMap < PeerId , oneshot:: Receiver < Result < PeerId > > > ,
5557 dial_backoff_states : HashMap < PeerId , DialBackoffState > ,
5658
59+ /// Per-source-IP rate limiter for admitting new inbound connections.
60+ /// `None` when inbound rate limiting is disabled.
61+ inbound_rate_limiter : Option < InboundIpRateLimiter > ,
62+
5763 active_peers : ActivePeers ,
5864 known_peers : KnownPeers ,
5965
@@ -75,6 +81,9 @@ impl ConnectionManager {
7581 service : BoxCloneService < Request < Bytes > , Response < Bytes > , Infallible > ,
7682 ) -> ( Self , mpsc:: Sender < ConnectionManagerRequest > ) {
7783 let ( sender, receiver) = mpsc:: channel ( config. connection_manager_channel_capacity ( ) ) ;
84+ let inbound_rate_limiter = config. inbound_connection_rate_limit_per_ip ( ) . map ( |rate| {
85+ InboundIpRateLimiter :: new ( rate, config. inbound_connection_rate_limit_burst_per_ip ( ) )
86+ } ) ;
7887 (
7988 Self {
8089 config,
@@ -84,6 +93,7 @@ impl ConnectionManager {
8493 connection_handlers : JoinSet :: new ( ) ,
8594 pending_dials : HashMap :: default ( ) ,
8695 dial_backoff_states : HashMap :: default ( ) ,
96+ inbound_rate_limiter,
8797 active_peers,
8898 known_peers,
8999 service,
@@ -142,9 +152,9 @@ impl ConnectionManager {
142152 }
143153 }
144154 }
145- connecting = self . endpoint. accept( ) => {
146- if let Some ( connecting ) = connecting {
147- self . handle_incoming( connecting ) ;
155+ incoming = self . endpoint. accept( ) => {
156+ if let Some ( incoming ) = incoming {
157+ self . handle_incoming( incoming ) ;
148158 }
149159 } ,
150160 Some ( connecting_output) = self . pending_connections. join_next( ) => {
@@ -229,15 +239,48 @@ impl ConnectionManager {
229239 self . dial_peer ( address, peer_id, oneshot) ;
230240 }
231241
232- fn handle_incoming ( & mut self , connecting : Connecting ) {
242+ fn handle_incoming ( & mut self , incoming : Incoming ) {
233243 trace ! ( "received new incoming connection" ) ;
234244
235- self . pending_connections . spawn ( Self :: handle_incoming_task (
236- connecting,
237- self . config . clone ( ) ,
238- self . active_peers . clone ( ) ,
239- self . known_peers . clone ( ) ,
240- ) ) ;
245+ let remote_address = incoming. remote_address ( ) ;
246+
247+ // Validate source address, if enabled.
248+ let incoming = if self . config . require_inbound_address_validation ( ) {
249+ match incoming. validate_source ( ) {
250+ Some ( incoming) => incoming,
251+ None => return ,
252+ }
253+ } else {
254+ incoming
255+ } ;
256+
257+ // Apply per-source-IP inbound rate limit. Loopback sources are
258+ // exempt: they can only originate on this host (a remote attacker cannot
259+ // present a loopback source address), and exempting them avoids throttling
260+ // local/test topologies where many peers share a loopback address.
261+ if let Some ( limiter) = self . inbound_rate_limiter . as_mut ( ) {
262+ let ip = remote_address. ip ( ) ;
263+ if !ip. is_loopback ( ) && !limiter. check ( ip, Instant :: now ( ) ) {
264+ debug ! ( %remote_address, "dropping inbound connection: per-source-IP rate limit exceeded" ) ;
265+ incoming. ignore ( ) ;
266+ return ;
267+ }
268+ }
269+
270+ // Admit the connection and begin the handshake.
271+ match incoming. accept ( ) {
272+ Ok ( connecting) => {
273+ self . pending_connections . spawn ( Self :: handle_incoming_task (
274+ connecting,
275+ self . config . clone ( ) ,
276+ self . active_peers . clone ( ) ,
277+ self . known_peers . clone ( ) ,
278+ ) ) ;
279+ }
280+ Err ( e) => {
281+ debug ! ( %remote_address, "failed to accept inbound connection: {e}" ) ;
282+ }
283+ }
241284 }
242285
243286 async fn handle_incoming_task (
@@ -420,6 +463,11 @@ impl ConnectionManager {
420463 self . dial_peer ( address, Some ( peer. peer_id ) , sender) ;
421464 self . pending_dials . insert ( peer. peer_id , receiver) ;
422465 }
466+
467+ // Clean up rate limiter buckets.
468+ if let Some ( limiter) = self . inbound_rate_limiter . as_mut ( ) {
469+ limiter. evict_idle ( now) ;
470+ }
423471 }
424472
425473 #[ instrument( level = "trace" , skip_all, fields( peer_id = ?peer_id, address = ?address) ) ]
0 commit comments