gitlab.com/jokerrs1/Sia@v1.3.2/modules/gateway/peersmanager.go (about) 1 package gateway 2 3 import ( 4 "github.com/NebulousLabs/Sia/build" 5 "github.com/NebulousLabs/Sia/modules" 6 "github.com/NebulousLabs/fastrand" 7 ) 8 9 // managedPeerManagerConnect is a blocking function which tries to connect to 10 // the input addreess as a peer. 11 func (g *Gateway) managedPeerManagerConnect(addr modules.NetAddress) { 12 g.log.Debugf("[PMC] [%v] Attempting connection", addr) 13 err := g.managedConnect(addr) 14 if err == errPeerExists { 15 // This peer is already connected to us. Safety around the 16 // oubound peers relates to the fact that we have picked out 17 // the outbound peers instead of allow the attacker to pick out 18 // the peers for us. Because we have made the selection, it is 19 // okay to set the peer as an outbound peer. 20 // 21 // The nodelist size check ensures that an attacker can't flood 22 // a new node with a bunch of inbound requests. Doing so would 23 // result in a nodelist that's entirely full of attacker nodes. 24 // There's not much we can do about that anyway, but at least 25 // we can hold off making attacker nodes 'outbound' peers until 26 // our nodelist has had time to fill up naturally. 27 g.mu.Lock() 28 p, exists := g.peers[addr] 29 if exists { 30 // Have to check it exists because we released the lock, a 31 // race condition could mean that the peer was disconnected 32 // before this code block was reached. 33 p.Inbound = false 34 if n, ok := g.nodes[p.NetAddress]; ok && !n.WasOutboundPeer { 35 n.WasOutboundPeer = true 36 g.nodes[n.NetAddress] = n 37 } 38 g.log.Debugf("[PMC] [SUCCESS] [%v] existing peer has been converted to outbound peer", addr) 39 } 40 g.mu.Unlock() 41 } else if err != nil { 42 g.log.Debugf("[PMC] [ERROR] [%v] WARN: removing peer because automatic connect failed: %v\n", addr, err) 43 44 // Remove the node, but only if there are enough nodes in the node list. 45 g.mu.Lock() 46 if len(g.nodes) > pruneNodeListLen { 47 g.removeNode(addr) 48 } 49 g.mu.Unlock() 50 } else { 51 g.log.Debugf("[PMC] [SUCCESS] [%v] peer successfully added", addr) 52 } 53 } 54 55 // numOutboundPeers returns the number of outbound peers in the gateway. 56 func (g *Gateway) numOutboundPeers() int { 57 n := 0 58 for _, p := range g.peers { 59 if !p.Inbound { 60 n++ 61 } 62 } 63 return n 64 } 65 66 // permanentPeerManager tries to keep the Gateway well-connected. As long as 67 // the Gateway is not well-connected, it tries to connect to random nodes. 68 func (g *Gateway) permanentPeerManager(closedChan chan struct{}) { 69 // Send a signal upon shutdown. 70 defer close(closedChan) 71 defer g.log.Debugln("INFO: [PPM] Permanent peer manager is shutting down") 72 73 // permanentPeerManager will attempt to connect to peers asynchronously, 74 // such that multiple connection attempts can be open at once, but a 75 // limited number. 76 connectionLimiterChan := make(chan struct{}, maxConcurrentOutboundPeerRequests) 77 78 g.log.Debugln("INFO: [PPM] Permanent peer manager has started") 79 80 for { 81 // Fetch the set of nodes to try. 82 g.mu.RLock() 83 nodes := g.buildPeerManagerNodeList() 84 g.mu.RUnlock() 85 if len(nodes) == 0 { 86 g.log.Debugln("[PPM] Node list is empty, sleeping") 87 if !g.managedSleep(noNodesDelay) { 88 return 89 } 90 continue 91 } 92 93 for _, addr := range nodes { 94 // Break as soon as we have enough outbound peers. 95 g.mu.RLock() 96 numOutboundPeers := g.numOutboundPeers() 97 isOutboundPeer := g.peers[addr] != nil && !g.peers[addr].Inbound 98 g.mu.RUnlock() 99 if numOutboundPeers >= wellConnectedThreshold { 100 g.log.Debugln("INFO: [PPM] Gateway has enough peers, sleeping.") 101 if !g.managedSleep(wellConnectedDelay) { 102 return 103 } 104 break 105 } 106 if isOutboundPeer { 107 // Skip current outbound peers. 108 if !g.managedSleep(acquiringPeersDelay) { 109 return 110 } 111 continue 112 } 113 114 g.log.Debugln("[PPM] Fetched a random node:", addr) 115 116 // We need at least some of our outbound peers to be remote peers. If 117 // we already have reached a certain threshold of outbound peers and 118 // this peer is a local peer, do not consider it for an outbound peer. 119 // Sleep briefly to prevent the gateway from hogging the CPU if all 120 // peers are local. 121 if numOutboundPeers >= maxLocalOutboundPeers && addr.IsLocal() && build.Release != "testing" { 122 g.log.Debugln("[PPM] Ignorning selected peer; this peer is local and we already have multiple outbound peers:", addr) 123 if !g.managedSleep(unwantedLocalPeerDelay) { 124 return 125 } 126 continue 127 } 128 129 // Try connecting to that peer in a goroutine. Do not block unless 130 // there are currently 3 or more peer connection attempts open at once. 131 // Before spawning the thread, make sure that there is enough room by 132 // throwing a struct into the buffered channel. 133 g.log.Debugln("[PPM] Trying to connect to a node:", addr) 134 connectionLimiterChan <- struct{}{} 135 go func(addr modules.NetAddress) { 136 // After completion, take the struct out of the channel so that the 137 // next thread may proceed. 138 defer func() { 139 <-connectionLimiterChan 140 }() 141 142 if err := g.threads.Add(); err != nil { 143 return 144 } 145 defer g.threads.Done() 146 // peerManagerConnect will handle all of its own logging. 147 g.managedPeerManagerConnect(addr) 148 }(addr) 149 150 // Wait a bit before trying the next peer. The peer connections are 151 // non-blocking, so they should be spaced out to avoid spinning up an 152 // uncontrolled number of threads and therefore peer connections. 153 if !g.managedSleep(acquiringPeersDelay) { 154 return 155 } 156 } 157 } 158 } 159 160 // buildPeerManagerNodeList returns the gateway's node list in the order that 161 // permanentPeerManager should attempt to connect to them. 162 func (g *Gateway) buildPeerManagerNodeList() []modules.NetAddress { 163 // flatten the node map, inserting in random order 164 nodes := make([]modules.NetAddress, len(g.nodes)) 165 perm := fastrand.Perm(len(nodes)) 166 for _, node := range g.nodes { 167 nodes[perm[0]] = node.NetAddress 168 perm = perm[1:] 169 } 170 171 // swap the outbound nodes to the front of the list 172 numOutbound := 0 173 for i, node := range nodes { 174 if g.nodes[node].WasOutboundPeer { 175 nodes[numOutbound], nodes[i] = nodes[i], nodes[numOutbound] 176 numOutbound++ 177 } 178 } 179 return nodes 180 }