master
go 145 lines 6.22 KB
Raw
1 package libp2p
2
3 import (
4 "fmt"
5
6 "github.com/dustin/go-humanize"
7 "github.com/ipfs/kubo/config"
8 "github.com/ipfs/kubo/core/node/libp2p/fd"
9 "github.com/libp2p/go-libp2p"
10 rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager"
11 "github.com/pbnjay/memory"
12 )
13
14 var infiniteResourceLimits = rcmgr.InfiniteLimits.ToPartialLimitConfig().System
15
16 // This file defines implicit limit defaults used when Swarm.ResourceMgr.Enabled
17
18 // createDefaultLimitConfig creates LimitConfig to pass to libp2p's resource manager.
19 // The defaults follow the documentation in docs/libp2p-resource-management.md.
20 // Any changes in the logic here should be reflected there.
21 func createDefaultLimitConfig(cfg config.SwarmConfig) (limitConfig rcmgr.ConcreteLimitConfig, logMessageForStartup string, err error) {
22 maxMemoryDefault := uint64(memory.TotalMemory()) / 2
23 maxMemory := cfg.ResourceMgr.MaxMemory.WithDefault(maxMemoryDefault)
24
25 maxMemoryMB := maxMemory / (1024 * 1024)
26 maxFD := int(cfg.ResourceMgr.MaxFileDescriptors.WithDefault(int64(fd.GetNumFDs()) / 2))
27
28 // At least as of 2023-01-25, it's possible to open a connection that
29 // doesn't ask for any memory usage with the libp2p Resource Manager/Accountant
30 // (see https://github.com/libp2p/go-libp2p/issues/2010#issuecomment-1404280736).
31 // As a result, we can't currently rely on Memory limits to full protect us.
32 // Until https://github.com/libp2p/go-libp2p/issues/2010 is addressed,
33 // we take a proxy now of restricting to 1 inbound connection per MB.
34 // Note: this is more generous than go-libp2p's default autoscaled limits which do
35 // 64 connections per 1GB
36 // (see https://github.com/libp2p/go-libp2p/blob/master/p2p/host/resource-manager/limit_defaults.go#L357 ).
37 systemConnsInbound := int(1 * maxMemoryMB)
38
39 partialLimits := rcmgr.PartialLimitConfig{
40 System: rcmgr.ResourceLimits{
41 Memory: rcmgr.LimitVal64(maxMemory),
42 FD: rcmgr.LimitVal(maxFD),
43
44 Conns: rcmgr.Unlimited,
45 ConnsInbound: rcmgr.LimitVal(systemConnsInbound),
46 ConnsOutbound: rcmgr.Unlimited,
47
48 Streams: rcmgr.Unlimited,
49 StreamsOutbound: rcmgr.Unlimited,
50 StreamsInbound: rcmgr.Unlimited,
51 },
52
53 // Transient connections won't cause any memory to be accounted for by the resource manager/accountant.
54 // Only established connections do.
55 // As a result, we can't rely on System.Memory to protect us from a bunch of transient connection being opened.
56 // We limit the same values as the System scope, but only allow the Transient scope to take 25% of what is allowed for the System scope.
57 Transient: rcmgr.ResourceLimits{
58 Memory: rcmgr.LimitVal64(maxMemory / 4),
59 FD: rcmgr.LimitVal(maxFD / 4),
60
61 Conns: rcmgr.Unlimited,
62 ConnsInbound: rcmgr.LimitVal(systemConnsInbound / 4),
63 ConnsOutbound: rcmgr.Unlimited,
64
65 Streams: rcmgr.Unlimited,
66 StreamsInbound: rcmgr.Unlimited,
67 StreamsOutbound: rcmgr.Unlimited,
68 },
69
70 // Lets get out of the way of the allow list functionality.
71 // If someone specified "Swarm.ResourceMgr.Allowlist" we should let it go through.
72 AllowlistedSystem: infiniteResourceLimits,
73
74 AllowlistedTransient: infiniteResourceLimits,
75
76 // Keep it simple by not having Service, ServicePeer, Protocol, ProtocolPeer, Conn, or Stream limits.
77 ServiceDefault: infiniteResourceLimits,
78
79 ServicePeerDefault: infiniteResourceLimits,
80
81 ProtocolDefault: infiniteResourceLimits,
82
83 ProtocolPeerDefault: infiniteResourceLimits,
84
85 Conn: infiniteResourceLimits,
86
87 Stream: infiniteResourceLimits,
88
89 // Limit the resources consumed by a peer.
90 // This doesn't protect us against intentional DoS attacks since an attacker can easily spin up multiple peers.
91 // We specify this limit against unintentional DoS attacks (e.g., a peer has a bug and is sending too much traffic intentionally).
92 // In that case we want to keep that peer's resource consumption contained.
93 // To keep this simple, we only constrain inbound connections and streams.
94 PeerDefault: rcmgr.ResourceLimits{
95 Memory: rcmgr.Unlimited64,
96 FD: rcmgr.Unlimited,
97 Conns: rcmgr.Unlimited,
98 ConnsInbound: rcmgr.DefaultLimit,
99 ConnsOutbound: rcmgr.Unlimited,
100 Streams: rcmgr.Unlimited,
101 StreamsInbound: rcmgr.DefaultLimit,
102 StreamsOutbound: rcmgr.Unlimited,
103 },
104 }
105
106 scalingLimitConfig := rcmgr.DefaultLimits
107 libp2p.SetDefaultServiceLimits(&scalingLimitConfig)
108
109 // Anything set above in partialLimits that had a value of rcmgr.DefaultLimit will be overridden.
110 // Anything in scalingLimitConfig that wasn't defined in partialLimits above will be added (e.g., libp2p's default service limits).
111 partialLimits = partialLimits.Build(scalingLimitConfig.Scale(int64(maxMemory), maxFD)).ToPartialLimitConfig()
112
113 // Simple checks to override autoscaling ensuring limits make sense versus the connmgr values.
114 // There are ways to break this, but this should catch most problems already.
115 // We might improve this in the future.
116 // See: https://github.com/ipfs/kubo/issues/9545
117 if partialLimits.System.ConnsInbound > rcmgr.DefaultLimit && cfg.ConnMgr.Type.WithDefault(config.DefaultConnMgrType) != "none" {
118 maxInboundConns := int64(partialLimits.System.ConnsInbound)
119 if connmgrHighWaterTimesTwo := cfg.ConnMgr.HighWater.WithDefault(config.DefaultConnMgrHighWater) * 2; maxInboundConns < connmgrHighWaterTimesTwo {
120 maxInboundConns = connmgrHighWaterTimesTwo
121 }
122
123 if maxInboundConns < config.DefaultResourceMgrMinInboundConns {
124 maxInboundConns = config.DefaultResourceMgrMinInboundConns
125 }
126
127 // Scale System.StreamsInbound as well, but use the existing ratio of StreamsInbound to ConnsInbound
128 if partialLimits.System.StreamsInbound > rcmgr.DefaultLimit {
129 partialLimits.System.StreamsInbound = rcmgr.LimitVal(maxInboundConns * int64(partialLimits.System.StreamsInbound) / int64(partialLimits.System.ConnsInbound))
130 }
131 partialLimits.System.ConnsInbound = rcmgr.LimitVal(maxInboundConns)
132 }
133
134 msg := fmt.Sprintf(`
135 Computed default go-libp2p Resource Manager limits based on:
136 - 'Swarm.ResourceMgr.MaxMemory': %q
137 - 'Swarm.ResourceMgr.MaxFileDescriptors': %d
138
139 These can be inspected with 'ipfs swarm resources'.
140
141 `, humanize.Bytes(maxMemory), maxFD)
142
143 // We already have a complete value thus pass in an empty ConcreteLimitConfig.
144 return partialLimits.Build(rcmgr.ConcreteLimitConfig{}), msg, nil
145 }