Help us shape the future of Go! Take the 2026 Go Developer Survey

p2plimits

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 18, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package p2plimits builds the libp2p resource manager a mesh component runs with: limits sized from its connection budget, a per-source-address connection cap and rate in place of libp2p's default of 8, applied to the address a connection comes from and not to a relay it comes through, and a wrapper that counts and logs every inbound connection the manager refuses. libp2p refuses a connection before any byte of TLS and, for its per-IP and rate-limit paths, without a trace event; a dialer sees the connection close or a QUIC CONNECTION_REFUSED, and nothing else records it.

Index

Constants

View Source
const (
	RefusedPerIP     = "per_ip_limit"
	RefusedRateLimit = "rate_limit"
	RefusedScope     = "scope_limit"
	RefusedOther     = "other"
)

Why the resource manager refused an inbound connection; the label values of a refusal counter.

View Source
const (
	StreamScopeProtocol     = "protocol"
	StreamScopeProtocolPeer = "protocol_peer"
	StreamScopeService      = "service"
	StreamScopeServicePeer  = "service_peer"
	StreamScopePeer         = "peer"
	// StreamScopeConn is the system or transient scope, where the manager
	// does not yet know the protocol.
	StreamScopeConn = "conn"
)

The scopes a stream refusal is counted under. libp2p's own gauge (libp2p_rcmgr_blocked_resources) carries the scope but not the protocol, and a hub needs the protocol to tell a flood from a limit set too low.

View Source
const ConnsPerSourceIPShare = 4

ConnsPerSourceIPShare is the fraction of the inbound connection budget one source address may hold when no cap is configured.

View Source
const QUICStreamsPerConnection = 256

QUICStreamsPerConnection is how many streams the far end may hold open on one QUIC connection, as go-libp2p configures quic-go; a stream opened past it waits for one to close, with no error at either end. yamux over TCP has no limit of its own; the resource manager's are the only ones. Every relayed connection to a peer is one stream on the relay's connection to it, so a service called through a router has at most this many relayed callers through it over QUIC.

View Source
const RefusalLogInterval = time.Minute

RefusalLogInterval bounds how often one source IP's refusals are logged; the counter carries the rate.

View Source
const StreamPressureWarnAt = QUICStreamsPerConnection * 9 / 10

StreamPressureWarnAt is the stream count at which a QUIC connection is reported: nine tenths of what it admits.

View Source
const StreamWindow = 256 << 10

StreamWindow is the receive window yamux reserves for a stream when it opens (go-yamux's initialStreamWindow); a stream that carries a fast transfer to a slow reader may grow it, up to the muxer's maximum.

View Source
const TransportQUIC = "quic-v1"

TransportQUIC is the transport name libp2p gives a QUIC connection; WebTransport runs over QUIC with the same stream limit.

Variables

View Source
var (
	ErrPerSourceConns = errors.New("connections per ip limit exceeded")
	ErrPerSourceRate  = errors.New("rate limit exceeded")
)

What Observed answers a direct inbound connection with past the per-source cap or rate; the texts are libp2p's own for the same refusals.

Reasons lists every refusal label, for a counter to pre-register.

Functions

func DefaultConnsPerSourceIP

func DefaultConnsPerSourceIP(highWaterMark int) int

DefaultConnsPerSourceIP is the per-source-address cap derived from a connection manager high watermark. Members share addresses (pod SNAT, Cloud NAT, offices, a load generator), and the handshake, not the address, is the trust boundary.

func NewResourceManager

func NewResourceManager(limits rcmgr.ConcreteLimitConfig, opts ...rcmgr.Option) (network.ResourceManager, error)

NewResourceManager is libp2p's resource manager with the given limits and its own per-address limiters switched off: Observe applies the per-source cap and rate, which lets it tell a connection that comes from an address apart from one that comes through a relay. opts are passed on, for a MetricsReporter among others.

func RefusalReason

func RefusalReason(err error) string

RefusalReason maps the resource manager's errors to the counter's labels. The per-IP and rate-limit paths return plain strings, not sentinel errors.

func Streams

func Streams(n int) rcmgr.ResourceLimits

Streams is a stream budget of n each way, with the memory the streams reserve: yamux reserves one receive window per open stream through the resource manager, so a budget of streams is a budget of memory, and libp2p's default for the latter is scaled from host memory, which on the testnet's routers was 1.1 GiB and full at 1500 members over TCP. The memory is a reservation, released with the stream, and allocated only as data arrives and waits to be read.

Types

type Observed

type Observed struct {
	network.ResourceManager
	// contains filtered or unexported fields
}

Observed applies the per-source cap and rate to the inbound connections that come from an address, and counts and logs what it or the wrapped manager refuses. A connection that comes through a relay carries the relay's address, not its source's: libp2p charged it to the relay, so a service called by many members through a router saw them all as one address and refused most. Such a connection is bounded by the manager's connection budget and its per-peer scope, and at the relay by the relay's own per-peer and per-address budgets, so no per-source rule is applied to it here. Loopback is exempt, as in libp2p's default, so a local mesh never meets the cap. Observed keeps the wrapped manager's connection limit visible to libp2p's connmgr sanity check.

func New

func New(limits rcmgr.ConcreteLimitConfig, perIP int, refused *prometheus.CounterVec, warnf func(format string, args ...any), opts ...rcmgr.Option) (*Observed, error)

New is NewResourceManager wrapped in Observe: the manager a component's host runs with. A cap under one would refuse every peer, so it is an error here rather than a silent listener.

func Observe

func Observe(inner network.ResourceManager, perIP int, refused *prometheus.CounterVec, warnf func(format string, args ...any)) *Observed

Observe wraps inner with a per-source cap of perIP. refused is counted by reason (see Reasons) and warnf written once per source address per RefusalLogInterval; a nil warnf only counts.

func (*Observed) GetConnLimit

func (m *Observed) GetConnLimit() int

GetConnLimit is what libp2p compares the connection manager's high water mark against at host construction.

func (*Observed) LastLoggedAt

func (m *Observed) LastLoggedAt(source string) (time.Time, bool)

LastLoggedAt is when source was last logged, and whether it is remembered.

func (*Observed) OpenConnection

func (m *Observed) OpenConnection(dir network.Direction, usefd bool, endpoint multiaddr.Multiaddr) (network.ConnManagementScope, error)

func (*Observed) SetClock

func (m *Observed) SetClock(now func() time.Time)

SetClock replaces the clock the log limiter reads; for tests.

func (*Observed) SourceConns

func (m *Observed) SourceConns(addr netip.Addr) int

SourceConns is how many inbound connections from addr are open.

func (*Observed) SourcesLogged

func (m *Observed) SourcesLogged() int

SourcesLogged is how many source addresses the log limiter remembers.

type StreamPressure

type StreamPressure struct {
	// contains filtered or unexported fields
}

StreamPressure reports the connection of a host that carries the most streams, by transport, and warns once per peer per RefusalLogInterval when a QUIC connection is within reach of its stream limit: the next stream between the two waits, which a relay turns into a hop that hangs until its connect timeout and a caller into a dial that times out, neither of which says why.

func ObserveStreamPressure

func ObserveStreamPressure(conns func() []network.Conn, busiest *prometheus.GaugeVec, warnf func(format string, args ...any)) *StreamPressure

ObserveStreamPressure returns a sampler over conns. busiest carries the label transport; a nil warnf only samples.

func (*StreamPressure) Run

func (p *StreamPressure) Run(ctx context.Context, interval time.Duration)

Run samples every interval until ctx ends.

func (*StreamPressure) Sample

func (p *StreamPressure) Sample()

Sample reads every connection once.

func (*StreamPressure) SetClock

func (p *StreamPressure) SetClock(now func() time.Time)

SetClock replaces the clock the log limiter reads; for tests.

type StreamRefusals

type StreamRefusals struct {
	// contains filtered or unexported fields
}

StreamRefusals counts the streams the resource manager refused, by scope and protocol (or service), and the memory reservations it refused, and logs each once per RefusalLogInterval. It is libp2p's MetricsReporter; the Allow side is a no-op.

func CountStreamRefusals

func CountStreamRefusals(refused *prometheus.CounterVec, memoryRefused prometheus.Counter, warnf func(format string, args ...any)) *StreamRefusals

CountStreamRefusals returns a reporter for rcmgr.WithMetrics. refused carries the labels scope and protocol; memoryRefused counts the memory reservations refused, which the stream muxer makes one of per stream it opens and the relay one of per connection it relays; a nil warnf only counts.

func (*StreamRefusals) AllowConn

func (s *StreamRefusals) AllowConn(network.Direction, bool)

Connections and memory are observed elsewhere (Observed and libp2p's own gauges); allowances are not of interest.

func (*StreamRefusals) AllowMemory

func (s *StreamRefusals) AllowMemory(int)

func (*StreamRefusals) AllowPeer

func (s *StreamRefusals) AllowPeer(peer.ID)

func (*StreamRefusals) AllowProtocol

func (s *StreamRefusals) AllowProtocol(protocol.ID)

func (*StreamRefusals) AllowService

func (s *StreamRefusals) AllowService(string)

func (*StreamRefusals) AllowStream

func (s *StreamRefusals) AllowStream(peer.ID, network.Direction)

func (*StreamRefusals) BlockConn

func (s *StreamRefusals) BlockConn(network.Direction, bool)

func (*StreamRefusals) BlockMemory

func (s *StreamRefusals) BlockMemory(size int)

BlockMemory is a memory reservation the manager refused: the muxer's window for a new stream, or the relay's buffers for a connection it would relay. The budget it ran into is libp2p_rcmgr_limit{resource= "memory"} on the scope libp2p_rcmgr_memory shows full.

func (*StreamRefusals) BlockPeer

func (s *StreamRefusals) BlockPeer(peer.ID)

func (*StreamRefusals) BlockProtocol

func (s *StreamRefusals) BlockProtocol(proto protocol.ID)

func (*StreamRefusals) BlockProtocolPeer

func (s *StreamRefusals) BlockProtocolPeer(proto protocol.ID, _ peer.ID)

func (*StreamRefusals) BlockService

func (s *StreamRefusals) BlockService(svc string)

func (*StreamRefusals) BlockServicePeer

func (s *StreamRefusals) BlockServicePeer(svc string, _ peer.ID)

func (*StreamRefusals) BlockStream

func (s *StreamRefusals) BlockStream(_ peer.ID, _ network.Direction)

func (*StreamRefusals) SetClock

func (s *StreamRefusals) SetClock(now func() time.Time)

SetClock replaces the clock the log interval is measured on.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL