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  }