github.com/deso-protocol/core@v1.2.9/lib/connection_manager.go (about) 1 package lib 2 3 import ( 4 "math" 5 "net" 6 "strconv" 7 "sync/atomic" 8 "time" 9 10 "github.com/btcsuite/btcd/addrmgr" 11 chainlib "github.com/btcsuite/btcd/blockchain" 12 "github.com/btcsuite/btcd/wire" 13 "github.com/decred/dcrd/lru" 14 "github.com/deso-protocol/go-deadlock" 15 "github.com/golang/glog" 16 "github.com/pkg/errors" 17 ) 18 19 // connection_manager.go contains most of the logic for creating and managing 20 // connections with peers. A good place to start is the Start() function. 21 22 const ( 23 // These values behave as -1 when added to a uint. To decrement a uint 24 // atomically you need to do use these values. 25 26 // Uint64Dec decrements a uint64 by one. 27 Uint64Dec = ^uint64(0) 28 // Uint32Dec decrements a uint32 by one. 29 Uint32Dec = ^uint32(0) 30 ) 31 32 type ConnectionManager struct { 33 // Keep a reference to the Server. 34 // TODO: I'm pretty sure we can make it so that the ConnectionManager and the Peer 35 // doesn't need a reference to the Server object. But for now we keep things lazy. 36 srv *Server 37 38 // When --connectips is set, we don't connect to anything from the addrmgr. 39 connectIps []string 40 41 // The address manager keeps track of peer addresses we're aware of. When 42 // we need to connect to a new outbound peer, it chooses one of the addresses 43 // it's aware of at random and provides it to us. 44 addrMgr *addrmgr.AddrManager 45 // The interfaces we listen on for new incoming connections. 46 listeners []net.Listener 47 // The parameters we are initialized with. 48 params *DeSoParams 49 // The target number of outbound peers we want to have. 50 targetOutboundPeers uint32 51 // The maximum number of inbound peers we allow. 52 maxInboundPeers uint32 53 // When true, only one connection per IP is allowed. Prevents eclipse attacks 54 // among other things. 55 limitOneInboundConnectionPerIP bool 56 57 // Keep track of the nonces we've sent in our version messages so 58 // we can prevent connections to ourselves. 59 sentNonces lru.Cache 60 61 // This section defines the data structures for storing all the 62 // peers we're aware of. 63 // 64 // A count of the number active connections we have for each IP group. 65 // We use this to ensure we don't connect to more than one outbound 66 // peer from the same IP group. We need a mutex on it because it's used 67 // concurrently by many goroutines to figure out if outbound connections 68 // should be made to particular addresses. 69 70 mtxOutboundConnIPGroups deadlock.Mutex 71 outboundConnIPGroups map[string]int 72 // The peer maps map peer ID to peers for various types of peer connections. 73 // 74 // A persistent peer is typically one we got through a commandline argument. 75 // The reason it's called persistent is because we maintain a connection to 76 // it, and retry the connection if it fails. 77 mtxPeerMaps deadlock.RWMutex 78 persistentPeers map[uint64]*Peer 79 outboundPeers map[uint64]*Peer 80 inboundPeers map[uint64]*Peer 81 // Track the number of outbound peers we have so that this value can 82 // be accessed concurrently when deciding whether or not to add more 83 // outbound peers. 84 numOutboundPeers uint32 85 numInboundPeers uint32 86 numPersistentPeers uint32 87 88 // We keep track of the addresses for the outbound peers so that we can 89 // avoid choosing them in the address manager. We need a mutex on this 90 // guy because many goroutines will be querying the address manager 91 // at once. 92 mtxConnectedOutboundAddrs deadlock.RWMutex 93 connectedOutboundAddrs map[string]bool 94 95 // Used to set peer ids. Must be incremented atomically. 96 peerIndex uint64 97 98 serverMessageQueue chan *ServerMessage 99 100 // Keeps track of the network time, which is the median of all of our 101 // peers' time. 102 timeSource chainlib.MedianTimeSource 103 104 // Events that can happen to a peer. 105 newPeerChan chan *Peer 106 donePeerChan chan *Peer 107 108 // stallTimeoutSeconds is how long we wait to receive responses from Peers 109 // for certain types of messages. 110 stallTimeoutSeconds uint64 111 112 minFeeRateNanosPerKB uint64 113 114 // More chans we might want. modifyRebroadcastInv chan interface{} 115 shutdown int32 116 } 117 118 func NewConnectionManager( 119 _params *DeSoParams, _addrMgr *addrmgr.AddrManager, _listeners []net.Listener, 120 _connectIps []string, _timeSource chainlib.MedianTimeSource, 121 _targetOutboundPeers uint32, _maxInboundPeers uint32, 122 _limitOneInboundConnectionPerIP bool, 123 _stallTimeoutSeconds uint64, 124 _minFeeRateNanosPerKB uint64, 125 _serverMessageQueue chan *ServerMessage, 126 _srv *Server) *ConnectionManager { 127 128 return &ConnectionManager{ 129 srv: _srv, 130 params: _params, 131 addrMgr: _addrMgr, 132 listeners: _listeners, 133 connectIps: _connectIps, 134 // We keep track of the last N nonces we've sent in order to detect 135 // self connections. 136 sentNonces: lru.NewCache(1000), 137 timeSource: _timeSource, 138 139 //newestBlock: _newestBlock, 140 141 // Initialize the peer data structures. 142 outboundConnIPGroups: make(map[string]int), 143 persistentPeers: make(map[uint64]*Peer), 144 outboundPeers: make(map[uint64]*Peer), 145 inboundPeers: make(map[uint64]*Peer), 146 connectedOutboundAddrs: make(map[string]bool), 147 148 // Initialize the channels. 149 newPeerChan: make(chan *Peer), 150 donePeerChan: make(chan *Peer), 151 152 targetOutboundPeers: _targetOutboundPeers, 153 maxInboundPeers: _maxInboundPeers, 154 limitOneInboundConnectionPerIP: _limitOneInboundConnectionPerIP, 155 serverMessageQueue: _serverMessageQueue, 156 stallTimeoutSeconds: _stallTimeoutSeconds, 157 minFeeRateNanosPerKB: _minFeeRateNanosPerKB, 158 } 159 } 160 161 func (cmgr *ConnectionManager) GetAddrManager() *addrmgr.AddrManager { 162 return cmgr.addrMgr 163 } 164 165 // Check if the address passed shares a group with any addresses already in our 166 // data structures. 167 func (cmgr *ConnectionManager) isRedundantGroupKey(na *wire.NetAddress) bool { 168 groupKey := addrmgr.GroupKey(na) 169 170 cmgr.mtxOutboundConnIPGroups.Lock() 171 numGroupsForKey := cmgr.outboundConnIPGroups[groupKey] 172 cmgr.mtxOutboundConnIPGroups.Unlock() 173 174 if numGroupsForKey != 0 && numGroupsForKey != 1 { 175 glog.V(2).Infof("isRedundantGroupKey: Found numGroupsForKey != (0 or 1). Is (%d) "+ 176 "instead for addr (%s) and group key (%s). This "+ 177 "should never happen.", numGroupsForKey, na.IP.String(), groupKey) 178 } 179 180 if numGroupsForKey == 0 { 181 return false 182 } 183 return true 184 } 185 186 func (cmgr *ConnectionManager) addToGroupKey(na *wire.NetAddress) { 187 groupKey := addrmgr.GroupKey(na) 188 189 cmgr.mtxOutboundConnIPGroups.Lock() 190 cmgr.outboundConnIPGroups[groupKey]++ 191 cmgr.mtxOutboundConnIPGroups.Unlock() 192 } 193 194 func (cmgr *ConnectionManager) subFromGroupKey(na *wire.NetAddress) { 195 groupKey := addrmgr.GroupKey(na) 196 197 cmgr.mtxOutboundConnIPGroups.Lock() 198 cmgr.outboundConnIPGroups[groupKey]-- 199 cmgr.mtxOutboundConnIPGroups.Unlock() 200 } 201 202 func (cmgr *ConnectionManager) getRandomAddr() *wire.NetAddress { 203 for tries := 0; tries < 100; tries++ { 204 // Lock the address map since multiple threads will be trying to read 205 // and modify it at the same time. 206 cmgr.mtxConnectedOutboundAddrs.RLock() 207 addr := cmgr.addrMgr.GetAddress() 208 cmgr.mtxConnectedOutboundAddrs.RUnlock() 209 210 if addr == nil { 211 glog.V(2).Infof("ConnectionManager.getRandomAddr: addr from GetAddressWithExclusions was nil") 212 break 213 } 214 215 if cmgr.connectedOutboundAddrs[addrmgr.NetAddressKey(addr.NetAddress())] { 216 glog.V(2).Infof("ConnectionManager.getRandomAddr: Not choosing already connected address %v:%v", addr.NetAddress().IP, addr.NetAddress().Port) 217 continue 218 } 219 220 // We can only have one outbound address per /16. This is similar to 221 // Bitcoin and we do it to prevent Sybil attacks. 222 if cmgr.isRedundantGroupKey(addr.NetAddress()) { 223 glog.V(2).Infof("ConnectionManager.getRandomAddr: Not choosing address due to redundant group key %v:%v", addr.NetAddress().IP, addr.NetAddress().Port) 224 continue 225 } 226 227 glog.V(2).Infof("ConnectionManager.getRandomAddr: Returning %v:%v at %d iterations", 228 addr.NetAddress().IP, addr.NetAddress().Port, tries) 229 return addr.NetAddress() 230 } 231 232 glog.V(2).Infof("ConnectionManager.getRandomAddr: Returning nil") 233 return nil 234 } 235 236 func _delayRetry(retryCount int, persistentAddrForLogging *wire.NetAddress) { 237 // No delay if we haven't tried yet or if the number of retries isn't positive. 238 if retryCount <= 0 { 239 time.Sleep(time.Second) 240 return 241 } 242 numSecs := int(math.Pow(2.0, float64(retryCount))) 243 retryDelay := time.Duration(numSecs) * time.Second 244 245 if persistentAddrForLogging != nil { 246 glog.V(1).Infof("Retrying connection to outbound persistent peer: "+ 247 "(%s:%d) in (%d) seconds.", persistentAddrForLogging.IP.String(), 248 persistentAddrForLogging.Port, numSecs) 249 } else { 250 glog.V(2).Infof("Retrying connection to outbound non-persistent peer in (%d) seconds.", numSecs) 251 } 252 time.Sleep(retryDelay) 253 } 254 255 func (cmgr *ConnectionManager) enoughOutboundPeers() bool { 256 val := atomic.LoadUint32(&cmgr.numOutboundPeers) 257 if val > cmgr.targetOutboundPeers { 258 glog.Errorf("enoughOutboundPeers: Connected to too many outbound "+ 259 "peers: (%d). Should be "+ 260 "no more than (%d).", val, cmgr.targetOutboundPeers) 261 return true 262 } 263 264 if val == cmgr.targetOutboundPeers { 265 return true 266 } 267 return false 268 } 269 270 // Chooses a random address and tries to connect to it. Repeats this proocess until 271 // it finds a peer that can pass version negotiation. 272 func (cmgr *ConnectionManager) _getOutboundConn(persistentAddr *wire.NetAddress) net.Conn { 273 // If a persistentAddr was provided then the connection is a persistent 274 // one. 275 isPersistent := (persistentAddr != nil) 276 retryCount := 0 277 for { 278 // We want to start backing off exponentially once we've gone through enough 279 // unsuccessful retries. However, we want to give more slack to non-persistent 280 // peers before we start backing off, which is why it's not as cut and dry as 281 // just delaying based on the raw number of retries. 282 adjustedRetryCount := retryCount 283 if !isPersistent { 284 // If the address is not persistent, only start backing off once there 285 // has been a large number of failed attempts in a row as this likely indicates 286 // that there's a connection issue we need to wait out. 287 adjustedRetryCount = retryCount - 5 288 } 289 _delayRetry(adjustedRetryCount, persistentAddr) 290 retryCount++ 291 292 // If the connection manager is saturated with non-persistent 293 // outbound peers, no need to keep trying non-persistent outbound 294 // connections. 295 if !isPersistent && cmgr.enoughOutboundPeers() { 296 glog.V(1).Infof("Dropping connection request to non-persistent outbound " + 297 "peer because we have enough of them.") 298 return nil 299 } 300 301 // If we don't have a persistentAddr, pick one from our addrmgr. 302 ipNetAddr := persistentAddr 303 if ipNetAddr == nil { 304 ipNetAddr = cmgr.getRandomAddr() 305 } 306 if ipNetAddr == nil { 307 // This should never happen but if it does, sleep a bit and try again. 308 glog.V(1).Infof("_getOutboundConn: No valid addresses to connect to.") 309 time.Sleep(time.Second) 310 continue 311 } 312 313 netAddr := net.TCPAddr{ 314 IP: ipNetAddr.IP, 315 Port: int(ipNetAddr.Port), 316 } 317 318 // If the peer is not persistent, update the addrmgr. 319 glog.V(1).Infof("Attempting to connect to addr: %v", netAddr) 320 if !isPersistent { 321 cmgr.addrMgr.Attempt(ipNetAddr) 322 } 323 var err error 324 conn, err := net.DialTimeout(netAddr.Network(), netAddr.String(), cmgr.params.DialTimeout) 325 if err != nil { 326 // If we failed to connect to this peer, get a new address and try again. 327 glog.V(1).Infof("Connection to addr (%v) failed: %v", netAddr, err) 328 continue 329 } 330 331 // We were able to dial successfully so we'll break out now. 332 glog.V(1).Infof("Connected to addr: %v", netAddr) 333 334 // If this was a non-persistent outbound connection, mark the address as 335 // connected in the addrmgr. 336 if !isPersistent { 337 cmgr.addrMgr.Connected(ipNetAddr) 338 } 339 340 // We made a successful outbound connection so return. 341 return conn 342 } 343 } 344 345 func IPToNetAddr(ipStr string, addrMgr *addrmgr.AddrManager, params *DeSoParams) (*wire.NetAddress, error) { 346 port := params.DefaultSocketPort 347 host, portstr, err := net.SplitHostPort(ipStr) 348 if err != nil { 349 // No port specified so leave port=default and set 350 // host to the ipStr. 351 host = ipStr 352 } else { 353 pp, err := strconv.ParseUint(portstr, 10, 16) 354 if err != nil { 355 return nil, errors.Wrapf(err, "IPToNetAddr: Can not parse port from %s for ip", ipStr) 356 } 357 port = uint16(pp) 358 } 359 netAddr, err := addrMgr.HostToNetAddress(host, port, 0) 360 if err != nil { 361 return nil, errors.Wrapf(err, "IPToNetAddr: Can not parse port from %s for ip", ipStr) 362 } 363 return netAddr, nil 364 } 365 366 // Connect either an INBOUND or OUTBOUND peer. If conn == nil, then we will set up 367 // an OUTBOUND peer. Otherwise we will use the conn to create an INBOUND 368 // peer. If the connectoin is OUTBOUND and the persistentAddr is set, then 369 // we will connect only to that addr. Otherwise, we will use the addrmgr to 370 // randomly select addrs and create OUTBOUND connections with them until 371 // we find a worthy peer. 372 func (cmgr *ConnectionManager) ConnectPeer(conn net.Conn, persistentAddr *wire.NetAddress) { 373 // If we don't have a connection object then we will try and make an 374 // outbound connection to a peer to get one. 375 isOutbound := false 376 if conn == nil { 377 isOutbound = true 378 } 379 isPersistent := (persistentAddr != nil) 380 retryCount := 0 381 for { 382 if isPersistent { 383 _delayRetry(retryCount, persistentAddr) 384 } 385 retryCount++ 386 387 // If this is an outbound peer, create an outbound connection. 388 if isOutbound { 389 conn = cmgr._getOutboundConn(persistentAddr) 390 } 391 392 if conn == nil { 393 // Conn should only be nil if this is a non-persistent outbound peer. 394 if isPersistent { 395 glog.Errorf("ConnectPeer: Got a nil connection for a persistent peer. This should never happen: (%s)", persistentAddr.IP.String()) 396 } 397 398 // If we end up without a connection object, it implies we had enough 399 // outbound peers so just return. 400 return 401 } 402 403 // At this point conn is set so create a peer object to do 404 // a version negotiation. 405 na, err := IPToNetAddr(conn.RemoteAddr().String(), cmgr.addrMgr, cmgr.params) 406 if err != nil { 407 glog.Errorf("ConnectPeer: Problem calling ipToNetAddr for addr: (%s) err: (%v)", conn.RemoteAddr().String(), err) 408 409 // If we get an error in the conversion and this is an 410 // outbound connection, keep trying it. Otherwise, just return. 411 if isOutbound { 412 continue 413 } 414 return 415 } 416 peer := NewPeer(conn, isOutbound, na, isPersistent, 417 cmgr.stallTimeoutSeconds, 418 cmgr.minFeeRateNanosPerKB, 419 cmgr.params, 420 cmgr.srv.incomingMessages, cmgr, cmgr.srv) 421 422 if err := peer.NegotiateVersion(cmgr.params.VersionNegotiationTimeout); err != nil { 423 glog.Errorf("ConnectPeer: Problem negotiating version with peer with addr: (%s) err: (%v)", conn.RemoteAddr().String(), err) 424 425 // If we have an error in the version negotiation we disconnect 426 // from this peer. 427 peer.conn.Close() 428 429 // If the connection is outbound, then 430 // we try a new connection until we get one that works. Otherwise 431 // we break. 432 if isOutbound { 433 continue 434 } 435 return 436 } 437 peer._logVersionSuccess() 438 439 // If the version negotiation worked and we have an outbound non-persistent 440 // connection, mark the address as good in the addrmgr. 441 if isOutbound && !isPersistent { 442 cmgr.addrMgr.Good(na) 443 } 444 445 // We connected to the peer and it passed its version negotiation. 446 // Handle the next steps in the main loop. 447 cmgr.newPeerChan <- peer 448 449 // Once we've successfully connected to a valid peer we're done. The connection 450 // manager will handle starting the peer and, if this is an outbound peer and 451 // the peer later disconnects, 452 // it will potentially try and reconnect the peer or replace the peer with 453 // a new one so that we always maintain a fixed number of outbound peers. 454 return 455 } 456 } 457 458 func (cmgr *ConnectionManager) _initiateOutboundConnections() { 459 // This is a hack to make outbound connections go away. 460 if cmgr.targetOutboundPeers == 0 { 461 return 462 } 463 if len(cmgr.connectIps) > 0 { 464 // Connect to addresses passed via the --connectips flag. These addresses 465 // are persistent in the sense that if we disconnect from one, we will 466 // try to reconnect to the same one. 467 for _, connectIp := range cmgr.connectIps { 468 ipNetAddr, err := IPToNetAddr(connectIp, cmgr.addrMgr, cmgr.params) 469 if err != nil { 470 glog.Error(errors.Errorf("Couldn't connect to IP %v: %v", connectIp, err)) 471 continue 472 } 473 474 go func(na *wire.NetAddress) { 475 cmgr.ConnectPeer(nil, na) 476 }(ipNetAddr) 477 } 478 return 479 } 480 // Only connect to addresses from the addrmgr if we don't specify --connectips. 481 // These addresses are *not* persistent, meaning if we disconnect from one we'll 482 // try a different one. 483 // 484 // TODO: We should try more addresses than we need initially to increase the 485 // speed at which we saturate our outbound connections. The ConnectionManager 486 // will handle the disconnection from peers once we have enough outbound 487 // connections. I had this as the logic before but removed it because it caused 488 // contention of the addrMgr's lock. 489 for ii := 0; ii < int(cmgr.targetOutboundPeers); ii++ { 490 go cmgr.ConnectPeer(nil, nil) 491 } 492 } 493 494 func (cmgr *ConnectionManager) _isFromRedundantInboundIPAddress(addrToCheck net.Addr) bool { 495 cmgr.mtxPeerMaps.RLock() 496 defer cmgr.mtxPeerMaps.RUnlock() 497 498 // Loop through all the peers to see if any have the same IP 499 // address. This map is normally pretty small so doing this 500 // every time a Peer connects should be fine. 501 netAddr, err := IPToNetAddr(addrToCheck.String(), cmgr.addrMgr, cmgr.params) 502 if err != nil { 503 // Return true in case we have an error. We do this because it 504 // will result in the peer connection not being accepted, which 505 // is desired in this case. 506 glog.Warningf(errors.Wrapf(err, 507 "ConnectionManager._isFromRedundantInboundIPAddress: Problem parsing "+ 508 "net.Addr to wire.NetAddress so marking as redundant and not "+ 509 "making connection").Error()) 510 return true 511 } 512 if netAddr == nil { 513 glog.Warningf("ConnectionManager._isFromRedundantInboundIPAddress: " + 514 "address was nil after parsing so marking as redundant and not " + 515 "making connection") 516 return true 517 } 518 // If the IP is a localhost IP let it slide. This is useful for testing fake 519 // nodes on a local machine. 520 // TODO: Should this be a flag? 521 if net.IP([]byte{127, 0, 0, 1}).Equal(netAddr.IP) { 522 glog.V(1).Infof("ConnectionManager._isFromRedundantInboundIPAddress: Allowing " + 523 "localhost IP address to connect") 524 return false 525 } 526 for _, peer := range cmgr.inboundPeers { 527 // If the peer's IP is equal to the passed IP then we have found a duplicate 528 // inbound connection 529 if peer.netAddr.IP.Equal(netAddr.IP) { 530 return true 531 } 532 } 533 534 // If we get here then no duplicate inbound IPs were found. 535 return false 536 } 537 538 func (cmgr *ConnectionManager) _handleInboundConnections() { 539 for _, outerListener := range cmgr.listeners { 540 go func(ll net.Listener) { 541 for { 542 conn, err := ll.Accept() 543 if atomic.LoadInt32(&cmgr.shutdown) != 0 { 544 glog.Info("_handleInboundConnections: Ignoring connection due to shutdown") 545 return 546 } 547 if err != nil { 548 glog.Errorf("_handleInboundConnections: Can't accept connection: %v", err) 549 continue 550 } 551 552 // As a quick check, reject the peer if we have too many already. Note that 553 // this check isn't perfect but we have a later check at the end after doing 554 // a version negotiation that will properly reject the peer if this check 555 // messes up e.g. due to a concurrency issue. 556 // 557 // TODO: We should instead have eviction logic here to prevent 558 // someone from monopolizing a node's inbound connections. 559 numInboundPeers := atomic.LoadUint32(&cmgr.numInboundPeers) 560 if numInboundPeers > cmgr.maxInboundPeers { 561 562 glog.Infof("Rejecting INBOUND peer (%s) due to max inbound peers (%d) hit.", 563 conn.RemoteAddr().String(), cmgr.maxInboundPeers) 564 conn.Close() 565 566 continue 567 } 568 569 // If we want to limit inbound connections to one per IP address, check to 570 // make sure this address isn't already connected. 571 if cmgr.limitOneInboundConnectionPerIP && 572 cmgr._isFromRedundantInboundIPAddress(conn.RemoteAddr()) { 573 574 glog.Infof("Rejecting INBOUND peer (%s) due to already having an "+ 575 "inbound connection from the same IP with "+ 576 "limit_one_inbound_connection_per_ip set.", 577 conn.RemoteAddr().String()) 578 conn.Close() 579 580 continue 581 } 582 583 go cmgr.ConnectPeer(conn, nil) 584 } 585 }(outerListener) 586 } 587 } 588 589 // GetAllPeers holds the mtxPeerMaps lock for reading and returns a list containing 590 // pointers to all the active peers. 591 func (cmgr *ConnectionManager) GetAllPeers() []*Peer { 592 cmgr.mtxPeerMaps.RLock() 593 defer cmgr.mtxPeerMaps.RUnlock() 594 595 allPeers := []*Peer{} 596 for _, pp := range cmgr.persistentPeers { 597 allPeers = append(allPeers, pp) 598 } 599 for _, pp := range cmgr.outboundPeers { 600 allPeers = append(allPeers, pp) 601 } 602 for _, pp := range cmgr.inboundPeers { 603 allPeers = append(allPeers, pp) 604 } 605 606 return allPeers 607 } 608 609 func (cmgr *ConnectionManager) RandomPeer() *Peer { 610 cmgr.mtxPeerMaps.RLock() 611 defer cmgr.mtxPeerMaps.RUnlock() 612 613 // Prefer persistent peers over all other peers. 614 if len(cmgr.persistentPeers) > 0 { 615 // Maps iterate randomly so this should be sufficient. 616 for _, pp := range cmgr.persistentPeers { 617 return pp 618 } 619 } 620 621 // Prefer outbound peers over inbound peers. 622 if len(cmgr.outboundPeers) > 0 { 623 // Maps iterate randomly so this should be sufficient. 624 for _, pp := range cmgr.outboundPeers { 625 return pp 626 } 627 } 628 629 // If we don't have any other type of peer, use an inbound peer. 630 if len(cmgr.inboundPeers) > 0 { 631 // Maps iterate randomly so this should be sufficient. 632 for _, pp := range cmgr.inboundPeers { 633 return pp 634 } 635 } 636 637 return nil 638 } 639 640 // Update our data structures to add this peer. 641 func (cmgr *ConnectionManager) addPeer(pp *Peer) { 642 // Acquire the mtxPeerMaps lock for writing. 643 cmgr.mtxPeerMaps.Lock() 644 defer cmgr.mtxPeerMaps.Unlock() 645 646 // Figure out what list this peer belongs to. 647 var peerList map[uint64]*Peer 648 if pp.isPersistent { 649 peerList = cmgr.persistentPeers 650 atomic.AddUint32(&cmgr.numPersistentPeers, 1) 651 } else if pp.isOutbound { 652 peerList = cmgr.outboundPeers 653 654 // If this is a non-persistent outbound peer and if 655 // the peer was not previously in our data structures then 656 // increment the count for this IP group and increment the 657 // number of outbound peers. Also add the peer's address to 658 // our map. 659 if _, ok := peerList[pp.ID]; !ok { 660 cmgr.addToGroupKey(pp.netAddr) 661 atomic.AddUint32(&cmgr.numOutboundPeers, 1) 662 663 cmgr.mtxConnectedOutboundAddrs.Lock() 664 cmgr.connectedOutboundAddrs[addrmgr.NetAddressKey(pp.netAddr)] = true 665 cmgr.mtxConnectedOutboundAddrs.Unlock() 666 } 667 } else { 668 // This is an inbound peer. 669 atomic.AddUint32(&cmgr.numInboundPeers, 1) 670 peerList = cmgr.inboundPeers 671 } 672 673 peerList[pp.ID] = pp 674 } 675 676 // Update our data structures to remove this peer. 677 func (cmgr *ConnectionManager) RemovePeer(pp *Peer) { 678 // Acquire the mtxPeerMaps lock for writing. 679 cmgr.mtxPeerMaps.Lock() 680 defer cmgr.mtxPeerMaps.Unlock() 681 682 // Figure out what list this peer belongs to. 683 var peerList map[uint64]*Peer 684 if pp.isPersistent { 685 peerList = cmgr.persistentPeers 686 atomic.AddUint32(&cmgr.numPersistentPeers, Uint32Dec) 687 } else if pp.isOutbound { 688 peerList = cmgr.outboundPeers 689 690 // If this is a non-persistent outbound peer and if 691 // the peer was previously in our data structures then 692 // decrement the outbound group count and the number of 693 // outbound peers. 694 if _, ok := peerList[pp.ID]; ok { 695 cmgr.subFromGroupKey(pp.netAddr) 696 atomic.AddUint32(&cmgr.numOutboundPeers, Uint32Dec) 697 698 cmgr.mtxConnectedOutboundAddrs.Lock() 699 delete(cmgr.connectedOutboundAddrs, addrmgr.NetAddressKey(pp.netAddr)) 700 cmgr.mtxConnectedOutboundAddrs.Unlock() 701 } 702 } else { 703 // This is an inbound peer. 704 atomic.AddUint32(&cmgr.numInboundPeers, Uint32Dec) 705 peerList = cmgr.inboundPeers 706 } 707 708 // Update the last seen time before we finish removing the peer. 709 cmgr.addrMgr.Connected(pp.netAddr) 710 711 // Remove the peer from our data structure. 712 delete(peerList, pp.ID) 713 } 714 715 func (cmgr *ConnectionManager) _maybeReplacePeer(pp *Peer) { 716 // If the peer was outbound, replace her with a 717 // new peer to maintain a fixed number of outbound connections. 718 if pp.isOutbound { 719 // If the peer is not persistent then we don't want to pass an 720 // address to connectPeer. The lack of an address will cause it 721 // to choose random addresses from the addrmgr until one works. 722 na := pp.netAddr 723 if !pp.isPersistent { 724 na = nil 725 } 726 go cmgr.ConnectPeer(nil, na) 727 } 728 } 729 730 func (cmgr *ConnectionManager) _logOutboundPeerData() { 731 numOutboundPeers := int(atomic.LoadUint32(&cmgr.numOutboundPeers)) 732 numInboundPeers := int(atomic.LoadUint32(&cmgr.numInboundPeers)) 733 numPersistentPeers := int(atomic.LoadUint32(&cmgr.numPersistentPeers)) 734 glog.V(1).Infof("Num peers: OUTBOUND(%d) INBOUND(%d) PERSISTENT(%d)", numOutboundPeers, numInboundPeers, numPersistentPeers) 735 736 cmgr.mtxOutboundConnIPGroups.Lock() 737 for _, vv := range cmgr.outboundConnIPGroups { 738 if vv != 0 && vv != 1 { 739 glog.V(1).Infof("_logOutboundPeerData: Peer group count != (0 or 1). "+ 740 "Is (%d) instead. This "+ 741 "should never happen.", vv) 742 } 743 } 744 cmgr.mtxOutboundConnIPGroups.Unlock() 745 } 746 747 func (cmgr *ConnectionManager) Stop() { 748 if atomic.AddInt32(&cmgr.shutdown, 1) != 1 { 749 glog.Warningf("ConnectionManager.Stop is already in the process of " + 750 "shutting down") 751 return 752 } 753 glog.Info("ConnectionManager.Stop: Gracefully shutting down ConnectionManager") 754 755 // Close all of the listeners. 756 for _, listener := range cmgr.listeners { 757 _ = listener.Close() 758 } 759 } 760 761 func (cmgr *ConnectionManager) Start() { 762 // Below is a basic description of the ConnectionManager's main loop: 763 // 764 // We have listeners (for inbound connections) and we have an addrmgr (for outbound connections). 765 // Specify TargetOutbound connections we want to have. 766 // Create TargetOutbound connection objects each with their own id. 767 // Add these connection objects to a map of some sort. 768 // Initiate TargetOutbound connections to peers using the addrmgr. 769 // When a connection fails, remove that connection from the map and try another connection in its place. Wait for that connection to return. Repeat. 770 // - If a connection has failed a few times then add a retryduration (since we're probably out of addresses). 771 // - If you can't connect to a node because the addrmgr returned nil, wait some amount of time and then try again. 772 // When a connection succeeds: 773 // - Send the peer a version message. 774 // - Read a version message from the peer. 775 // - Wait for the above two steps to return. 776 // - If the above steps don't return, then disconnect from the peer as above. Try to reconnect to another peer. 777 // If the steps above succeed 778 // - Have the peer enter a switch statement listening for all kinds of messages. 779 // - Send addr and getaddr messages as appropriate. 780 781 // Initiate outbound connections with peers either using the --connectips passed 782 // in or using the addrmgr. 783 cmgr._initiateOutboundConnections() 784 785 // Accept inbound connections from peers on our listeners. 786 cmgr._handleInboundConnections() 787 788 glog.Infof("Full node socket initialized") 789 790 for { 791 // Log some data for each event. 792 cmgr._logOutboundPeerData() 793 794 select { 795 case pp := <-cmgr.newPeerChan: 796 { 797 // We have successfully connected to a peer and it passed its version 798 // negotiation. 799 800 // if this is a non-persistent outbound peer and we already have enough 801 // outbound peers, then don't bother adding this one. 802 if !pp.isPersistent && pp.isOutbound && cmgr.enoughOutboundPeers() { 803 // TODO: Make this less verbose 804 glog.V(1).Infof("Dropping peer because we already have enough outbound peer connections.") 805 pp.conn.Close() 806 continue 807 } 808 809 // If this is a non-persistent outbound peer and the group key 810 // overlaps with another peer we're already connected to then 811 // abort mission. We only connect to one peer per IP group in 812 // order to prevent Sybil attacks. 813 if pp.isOutbound && 814 !pp.isPersistent && 815 cmgr.isRedundantGroupKey(pp.netAddr) { 816 817 // TODO: Make this less verbose 818 glog.Infof("Rejecting OUTBOUND NON-PERSISTENT peer (%v) with "+ 819 "redundant group key (%s).", 820 pp, addrmgr.GroupKey(pp.netAddr)) 821 822 pp.conn.Close() 823 cmgr._maybeReplacePeer(pp) 824 continue 825 } 826 827 // Check that we have not exceeded the maximum number of inbound 828 // peers allowed. 829 // 830 // TODO: We should instead have eviction logic to prevent 831 // someone from monopolizing a node's inbound connections. 832 numInboundPeers := atomic.LoadUint32(&cmgr.numInboundPeers) 833 if !pp.isOutbound && numInboundPeers > cmgr.maxInboundPeers { 834 835 // TODO: Make this less verbose 836 glog.Infof("Rejecting INBOUND peer (%v) due to max inbound peers (%d) hit.", 837 pp, cmgr.maxInboundPeers) 838 839 pp.conn.Close() 840 continue 841 } 842 843 // Now we can add the peer to our data structures. 844 pp._logAddPeer() 845 cmgr.addPeer(pp) 846 847 // Start the peer's message loop. 848 pp.Start() 849 850 // Signal the server about the new Peer in case it wants to do something with it. 851 cmgr.serverMessageQueue <- &ServerMessage{ 852 Peer: pp, 853 Msg: &MsgDeSoNewPeer{}, 854 } 855 856 } 857 case pp := <-cmgr.donePeerChan: 858 { 859 // By the time we get here, it can be assumed that the Peer's Disconnect function 860 // has already been called, since that is what's responsible for adding the peer 861 // to this queue in the first place. 862 863 glog.V(1).Infof("Done with peer (%v).", pp) 864 865 if !pp.PeerManuallyRemovedFromConnectionManager { 866 // Remove the peer from our data structures. 867 cmgr.RemovePeer(pp) 868 869 // Potentially replace the peer. For example, if the Peer was an outbound Peer 870 // then we want to find a new peer in order to maintain our TargetOutboundPeers. 871 cmgr._maybeReplacePeer(pp) 872 } 873 874 // Signal the server about the Peer being done in case it wants to do something 875 // with it. 876 cmgr.serverMessageQueue <- &ServerMessage{ 877 Peer: pp, 878 Msg: &MsgDeSoDonePeer{}, 879 } 880 } 881 } 882 } 883 }