tendermint/p2p/peer_set.go

228 lines
5.4 KiB
Go
Raw Normal View History

2014-07-08 00:02:04 -07:00
package p2p
import (
2015-07-12 10:54:34 -07:00
"net"
"strings"
2014-07-08 00:02:04 -07:00
"sync"
)
2014-12-23 19:31:24 -08:00
// IPeerSet has a (immutable) subset of the methods of PeerSet.
type IPeerSet interface {
Has(key string) bool
Get(key string) *Peer
2014-07-10 22:14:23 -07:00
List() []*Peer
Size() int
2014-07-10 22:14:23 -07:00
}
//-----------------------------------------------------------------------------
2015-07-12 10:54:34 -07:00
var (
maxPeersPerIPRange = [4]int{11, 7, 5, 3} // ...
)
2014-12-23 19:31:24 -08:00
// PeerSet is a special structure for keeping a table of peers.
// Iteration over the peers is super fast and thread-safe.
2015-07-13 13:17:47 -07:00
// We also track how many peers per IP range and avoid too many
2014-07-08 00:02:04 -07:00
type PeerSet struct {
2015-07-12 10:54:34 -07:00
mtx sync.Mutex
lookup map[string]*peerSetItem
list []*Peer
connectedIPs *nestedCounter
2014-07-08 00:02:04 -07:00
}
type peerSetItem struct {
peer *Peer
index int
}
func NewPeerSet() *PeerSet {
return &PeerSet{
2015-07-12 10:54:34 -07:00
lookup: make(map[string]*peerSetItem),
list: make([]*Peer, 0, 256),
connectedIPs: NewNestedCounter(),
2014-07-08 00:02:04 -07:00
}
}
2015-07-15 14:31:03 -07:00
// Returns false if peer with key (PubKeyEd25519) is already in set
2015-07-13 13:17:47 -07:00
// or if we have too many peers from the peer's IP range
2015-07-12 10:54:34 -07:00
func (ps *PeerSet) Add(peer *Peer) error {
2014-07-08 00:02:04 -07:00
ps.mtx.Lock()
defer ps.mtx.Unlock()
if ps.lookup[peer.Key] != nil {
2015-07-12 10:54:34 -07:00
return ErrSwitchDuplicatePeer
}
2015-07-13 13:17:47 -07:00
// ensure we havent maxed out connections for the peer's IP range yet
// and update the IP range counters
2015-07-13 16:00:01 -07:00
if !ps.incrIPRangeCounts(peer.Host) {
2015-07-12 10:54:34 -07:00
return ErrSwitchMaxPeersPerIPRange
2014-07-08 00:02:04 -07:00
}
2015-07-12 10:54:34 -07:00
2014-07-08 00:02:04 -07:00
index := len(ps.list)
// Appending is safe even with other goroutines
// iterating over the ps.list slice.
ps.list = append(ps.list, peer)
ps.lookup[peer.Key] = &peerSetItem{peer, index}
2015-07-12 10:54:34 -07:00
return nil
2014-07-08 00:02:04 -07:00
}
func (ps *PeerSet) Has(peerKey string) bool {
2014-07-10 22:14:23 -07:00
ps.mtx.Lock()
defer ps.mtx.Unlock()
_, ok := ps.lookup[peerKey]
2014-07-10 22:14:23 -07:00
return ok
}
func (ps *PeerSet) Get(peerKey string) *Peer {
ps.mtx.Lock()
defer ps.mtx.Unlock()
item, ok := ps.lookup[peerKey]
if ok {
return item.peer
} else {
return nil
}
}
2014-07-08 00:02:04 -07:00
func (ps *PeerSet) Remove(peer *Peer) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
item := ps.lookup[peer.Key]
2014-07-08 00:02:04 -07:00
if item == nil {
return
}
2015-07-13 16:00:01 -07:00
// update the IP range counters
ps.decrIPRangeCounts(peer.Host)
2014-07-08 00:02:04 -07:00
index := item.index
2014-07-08 08:34:49 -07:00
// Copy the list but without the last element.
// (we must copy because we're mutating the list)
newList := make([]*Peer, len(ps.list)-1)
copy(newList, ps.list)
2014-07-08 00:02:04 -07:00
// If it's the last peer, that's an easy special case.
if index == len(ps.list)-1 {
2014-07-08 08:34:49 -07:00
ps.list = newList
2015-01-21 13:40:26 -08:00
delete(ps.lookup, peer.Key)
2014-07-08 00:02:04 -07:00
return
}
2015-07-13 16:00:01 -07:00
2014-07-08 00:02:04 -07:00
// Move the last item from ps.list to "index" in list.
lastPeer := ps.list[len(ps.list)-1]
2015-01-21 13:40:26 -08:00
lastPeerKey := lastPeer.Key
lastPeerItem := ps.lookup[lastPeerKey]
2014-07-08 00:02:04 -07:00
newList[index] = lastPeer
lastPeerItem.index = index
ps.list = newList
delete(ps.lookup, peer.Key)
2015-07-13 16:00:01 -07:00
2014-07-08 00:02:04 -07:00
}
2014-07-10 22:14:23 -07:00
func (ps *PeerSet) Size() int {
ps.mtx.Lock()
defer ps.mtx.Unlock()
return len(ps.list)
}
2014-07-08 00:02:04 -07:00
// threadsafe list of peers.
func (ps *PeerSet) List() []*Peer {
ps.mtx.Lock()
defer ps.mtx.Unlock()
return ps.list
}
2015-07-12 10:54:34 -07:00
//-----------------------------------------------------------------------------
2015-07-13 13:17:47 -07:00
// track the number of IPs we're connected to for each IP address range
2015-07-12 10:54:34 -07:00
2015-07-13 13:17:47 -07:00
// forms an IP address hierarchy tree with counts
2015-07-12 10:54:34 -07:00
// the struct itself is not thread safe and should always only be accessed with the ps.mtx locked
type nestedCounter struct {
count int
children map[string]*nestedCounter
}
func NewNestedCounter() *nestedCounter {
nc := new(nestedCounter)
nc.children = make(map[string]*nestedCounter)
return nc
}
2015-07-13 13:17:47 -07:00
// Check if we have too many IPs in the IP range of the incoming connection
2015-07-12 10:54:34 -07:00
// Thread safe
func (ps *PeerSet) HasMaxForIPRange(conn net.Conn) (ok bool) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
ip, _, _ := net.SplitHostPort(conn.RemoteAddr().String())
2015-07-13 16:00:01 -07:00
ipBytes := strings.Split(ip, ".")
2015-07-12 10:54:34 -07:00
c := ps.connectedIPs
2015-07-13 16:00:01 -07:00
for i, ipByte := range ipBytes {
2015-07-12 10:54:34 -07:00
if c, ok = c.children[ipByte]; !ok {
return false
}
2015-07-13 16:00:01 -07:00
if maxPeersPerIPRange[i] <= c.count {
2015-07-12 10:54:34 -07:00
return true
}
}
return false
}
2015-07-13 16:00:01 -07:00
// Increments counts for this address' IP range
2015-07-12 10:54:34 -07:00
// Returns false if we already have enough connections
// Not thread safe (only called by ps.Add())
2015-07-13 16:00:01 -07:00
func (ps *PeerSet) incrIPRangeCounts(address string) bool {
addrParts := strings.Split(address, ".")
2015-07-12 10:54:34 -07:00
c := ps.connectedIPs
2015-07-13 16:00:01 -07:00
return incrNestedCounters(c, addrParts, 0)
2015-07-12 10:54:34 -07:00
}
2015-07-13 16:00:01 -07:00
// Recursively descend the IP hierarchy, checking if we have
// max peers for each range and incrementing if not.
// Returns false if incr failed because max peers reached for some range counter.
func incrNestedCounters(c *nestedCounter, ipBytes []string, index int) bool {
2015-07-12 10:54:34 -07:00
ipByte := ipBytes[index]
2015-07-13 16:00:01 -07:00
child := c.children[ipByte]
if child == nil {
child = NewNestedCounter()
c.children[ipByte] = child
}
if index+1 < len(ipBytes) {
if !incrNestedCounters(child, ipBytes, index+1) {
2015-07-12 10:54:34 -07:00
return false
}
}
2015-07-13 16:00:01 -07:00
if maxPeersPerIPRange[index] <= child.count {
2015-07-12 10:54:34 -07:00
return false
2015-07-13 16:00:01 -07:00
} else {
child.count += 1
return true
}
}
// Decrement counts for this address' IP range
func (ps *PeerSet) decrIPRangeCounts(address string) {
addrParts := strings.Split(address, ".")
c := ps.connectedIPs
decrNestedCounters(c, addrParts, 0)
}
// Recursively descend the IP hierarchy, decrementing by one.
// If the counter is zero, deletes the child.
func decrNestedCounters(c *nestedCounter, ipBytes []string, index int) {
ipByte := ipBytes[index]
child := c.children[ipByte]
if child == nil {
log.Error("p2p/peer_set decrNestedCounters encountered a missing child counter")
return
}
if index+1 < len(ipBytes) {
decrNestedCounters(child, ipBytes, index+1)
}
child.count -= 1
if child.count <= 0 {
delete(c.children, ipByte)
2015-07-12 10:54:34 -07:00
}
}