Skip to content

Commit c2d8bd9

Browse files
authored
Implement connection limiting (#15)
* Disable WebRTC transport to prevent unwanted MDNS listening and traffic * Implement connection limiting with sane defaults
1 parent 0a9235c commit c2d8bd9

File tree

3 files changed

+60
-0
lines changed

3 files changed

+60
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
- **Auto-discovery**: Automatic peer discovery via DHT, mDNS, and peer caching
100100
- **NAT traversal**: Built-in support for hole punching and relay connections
101101
- **Persistent peers**: Automatically caches and reconnects to known peers
102+
- **Connection limiting**: Smart connection manager prioritizes topic peers over routing peers (default: 25-35 connections)
102103

103104
<br/>
104105

client.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import (
3232
"github.com/libp2p/go-libp2p/p2p/discovery/mdns"
3333
drouting "github.com/libp2p/go-libp2p/p2p/discovery/routing"
3434
"github.com/libp2p/go-libp2p/p2p/net/conngater"
35+
"github.com/libp2p/go-libp2p/p2p/net/connmgr"
36+
"github.com/libp2p/go-libp2p/p2p/transport/tcp"
3537
"github.com/multiformats/go-multiaddr"
3638
manet "github.com/multiformats/go-multiaddr/net"
3739
)
@@ -232,6 +234,39 @@ func createPrivateIPConnectionGater(log logger, cancel context.CancelFunc) (*con
232234
func buildHostOptions(config Config, log logger, cancel context.CancelFunc) ([]libp2p.Option, error) {
233235
hostOpts := []libp2p.Option{libp2p.Identity(config.PrivateKey)}
234236

237+
// Explicitly configure only TCP transport to prevent WebRTC's mDNS usage
238+
// WebRTC transport uses mDNS (port 5353) for ICE candidate discovery
239+
// By only enabling TCP, we avoid any unwanted mDNS traffic
240+
hostOpts = append(hostOpts, libp2p.Transport(tcp.NewTCPTransport))
241+
log.Infof("Configured TCP-only transport (WebRTC disabled to prevent mDNS)")
242+
243+
// Configure connection manager to limit total connections
244+
maxConns := config.MaxConnections
245+
if maxConns == 0 {
246+
maxConns = 35 // Default high water mark
247+
}
248+
minConns := config.MinConnections
249+
if minConns == 0 {
250+
minConns = 25 // Default low water mark
251+
}
252+
gracePeriod := config.ConnectionGracePeriod
253+
if gracePeriod == 0 {
254+
gracePeriod = 20 * time.Second // Default grace period
255+
}
256+
257+
connMgr, err := connmgr.NewConnManager(
258+
minConns,
259+
maxConns,
260+
connmgr.WithGracePeriod(gracePeriod),
261+
)
262+
if err != nil {
263+
cancel()
264+
return nil, fmt.Errorf("failed to create connection manager: %w", err)
265+
}
266+
267+
hostOpts = append(hostOpts, libp2p.ConnectionManager(connMgr))
268+
log.Infof("Connection manager configured: min=%d, max=%d, grace=%v", minConns, maxConns, gracePeriod)
269+
235270
// Add connection gater to block private IPs if AllowPrivateIPs is false (default)
236271
if !config.AllowPrivateIPs {
237272
ipFilter, err := createPrivateIPConnectionGater(log, cancel)
@@ -494,6 +529,10 @@ func (c *client) Subscribe(topic string) <-chan Message {
494529
peerID := conn.RemotePeer()
495530
topicPeers := t.ListPeers()
496531
if slices.Contains(topicPeers, peerID) {
532+
// Tag topic peers with high value to protect from connection manager pruning
533+
c.host.ConnManager().TagPeer(peerID, fmt.Sprintf("topic:%s", topic), 100)
534+
c.logger.Debugf("Tagged topic peer %s for protection", peerID)
535+
497536
name := c.peerTracker.getName(peerID)
498537
addr := conn.RemoteMultiaddr().String()
499538
c.logger.Infof("[CONNECTED] Topic peer %s [%s] %s", peerID.String(), name, addr)
@@ -507,6 +546,8 @@ func (c *client) Subscribe(topic string) <-chan Message {
507546
peerID := conn.RemotePeer()
508547
topicPeers := t.ListPeers()
509548
if slices.Contains(topicPeers, peerID) {
549+
// Untag peer when they disconnect from topic
550+
c.host.ConnManager().UntagPeer(peerID, fmt.Sprintf("topic:%s", topic))
510551
c.logger.Infof("[DISCONNECTED] Lost connection to topic peer %s", peerID.String()[:16])
511552
}
512553
},

config.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,22 @@ type Config struct {
117117
// Default: false (private IPs filtered for production safety)
118118
// Set to true only for local development or private network deployments
119119
AllowPrivateIPs bool
120+
121+
// MaxConnections is the high water mark for total peer connections.
122+
// When this limit is reached, the connection manager will prune low-value connections
123+
// (DHT routing peers) while protecting high-value peers (topic mesh peers).
124+
// If not provided or zero, defaults to 35.
125+
// Recommended: 25-50 depending on available bandwidth and number of topics.
126+
MaxConnections int
127+
128+
// MinConnections is the low water mark for total peer connections.
129+
// The connection manager will not prune connections below this threshold.
130+
// If not provided or zero, defaults to 25.
131+
// Recommended: Set to MaxConnections - 10 for a reasonable buffer.
132+
MinConnections int
133+
134+
// ConnectionGracePeriod is the duration new connections are protected from pruning.
135+
// This prevents rapid connect/disconnect cycles.
136+
// If not provided or zero, defaults to 20 seconds.
137+
ConnectionGracePeriod time.Duration
120138
}

0 commit comments

Comments
 (0)