You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

196 lines
4.6 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. package p2p
  2. import (
  3. "net"
  4. "strings"
  5. "sync"
  6. )
  7. // IPeerSet has a (immutable) subset of the methods of PeerSet.
  8. type IPeerSet interface {
  9. Has(key string) bool
  10. Get(key string) *Peer
  11. List() []*Peer
  12. Size() int
  13. }
  14. //-----------------------------------------------------------------------------
  15. var (
  16. maxPeersPerIPRange = [4]int{11, 7, 5, 3} // ...
  17. )
  18. // PeerSet is a special structure for keeping a table of peers.
  19. // Iteration over the peers is super fast and thread-safe.
  20. // We also track how many peers per ip range and avoid too many
  21. type PeerSet struct {
  22. mtx sync.Mutex
  23. lookup map[string]*peerSetItem
  24. list []*Peer
  25. connectedIPs *nestedCounter
  26. }
  27. type peerSetItem struct {
  28. peer *Peer
  29. index int
  30. }
  31. func NewPeerSet() *PeerSet {
  32. return &PeerSet{
  33. lookup: make(map[string]*peerSetItem),
  34. list: make([]*Peer, 0, 256),
  35. connectedIPs: NewNestedCounter(),
  36. }
  37. }
  38. // Returns false if peer with key (uuid) is already in set
  39. // or if we have too many peers from the peer's ip range
  40. func (ps *PeerSet) Add(peer *Peer) error {
  41. ps.mtx.Lock()
  42. defer ps.mtx.Unlock()
  43. if ps.lookup[peer.Key] != nil {
  44. return ErrSwitchDuplicatePeer
  45. }
  46. // ensure we havent maxed out connections for the peer's ip range yet
  47. // and update the ip range counters
  48. if !ps.updateIPRangeCounts(peer.Host) {
  49. return ErrSwitchMaxPeersPerIPRange
  50. }
  51. index := len(ps.list)
  52. // Appending is safe even with other goroutines
  53. // iterating over the ps.list slice.
  54. ps.list = append(ps.list, peer)
  55. ps.lookup[peer.Key] = &peerSetItem{peer, index}
  56. return nil
  57. }
  58. func (ps *PeerSet) Has(peerKey string) bool {
  59. ps.mtx.Lock()
  60. defer ps.mtx.Unlock()
  61. _, ok := ps.lookup[peerKey]
  62. return ok
  63. }
  64. func (ps *PeerSet) Get(peerKey string) *Peer {
  65. ps.mtx.Lock()
  66. defer ps.mtx.Unlock()
  67. item, ok := ps.lookup[peerKey]
  68. if ok {
  69. return item.peer
  70. } else {
  71. return nil
  72. }
  73. }
  74. func (ps *PeerSet) Remove(peer *Peer) {
  75. ps.mtx.Lock()
  76. defer ps.mtx.Unlock()
  77. item := ps.lookup[peer.Key]
  78. if item == nil {
  79. return
  80. }
  81. index := item.index
  82. // Copy the list but without the last element.
  83. // (we must copy because we're mutating the list)
  84. newList := make([]*Peer, len(ps.list)-1)
  85. copy(newList, ps.list)
  86. // If it's the last peer, that's an easy special case.
  87. if index == len(ps.list)-1 {
  88. ps.list = newList
  89. delete(ps.lookup, peer.Key)
  90. return
  91. }
  92. // Move the last item from ps.list to "index" in list.
  93. lastPeer := ps.list[len(ps.list)-1]
  94. lastPeerKey := lastPeer.Key
  95. lastPeerItem := ps.lookup[lastPeerKey]
  96. newList[index] = lastPeer
  97. lastPeerItem.index = index
  98. ps.list = newList
  99. delete(ps.lookup, peer.Key)
  100. }
  101. func (ps *PeerSet) Size() int {
  102. ps.mtx.Lock()
  103. defer ps.mtx.Unlock()
  104. return len(ps.list)
  105. }
  106. // threadsafe list of peers.
  107. func (ps *PeerSet) List() []*Peer {
  108. ps.mtx.Lock()
  109. defer ps.mtx.Unlock()
  110. return ps.list
  111. }
  112. //-----------------------------------------------------------------------------
  113. // track the number of ips we're connected to for each ip address range
  114. // forms an ip address hierarchy tree with counts
  115. // the struct itself is not thread safe and should always only be accessed with the ps.mtx locked
  116. type nestedCounter struct {
  117. count int
  118. children map[string]*nestedCounter
  119. }
  120. func NewNestedCounter() *nestedCounter {
  121. nc := new(nestedCounter)
  122. nc.children = make(map[string]*nestedCounter)
  123. return nc
  124. }
  125. // Check if we have too many ips in the ip range of the incoming connection
  126. // Thread safe
  127. func (ps *PeerSet) HasMaxForIPRange(conn net.Conn) (ok bool) {
  128. ps.mtx.Lock()
  129. defer ps.mtx.Unlock()
  130. ip, _, _ := net.SplitHostPort(conn.RemoteAddr().String())
  131. spl := strings.Split(ip, ".")
  132. c := ps.connectedIPs
  133. for i, ipByte := range spl {
  134. if c, ok = c.children[ipByte]; !ok {
  135. return false
  136. }
  137. if c.count == maxPeersPerIPRange[i] {
  138. return true
  139. }
  140. }
  141. return false
  142. }
  143. // Update counts for this address' ip range
  144. // Returns false if we already have enough connections
  145. // Not thread safe (only called by ps.Add())
  146. func (ps *PeerSet) updateIPRangeCounts(address string) bool {
  147. spl := strings.Split(address, ".")
  148. c := ps.connectedIPs
  149. return updateNestedCountRecursive(c, spl, 0)
  150. }
  151. // recursively descend the ip hierarchy, checking if we have
  152. // max peers for each range and updating if not
  153. func updateNestedCountRecursive(c *nestedCounter, ipBytes []string, index int) bool {
  154. if index == len(ipBytes) {
  155. return true
  156. }
  157. ipByte := ipBytes[index]
  158. if c2, ok := c.children[ipByte]; !ok {
  159. c2 = NewNestedCounter()
  160. c.children[ipByte] = c2
  161. c = c2
  162. } else {
  163. c = c2
  164. if c.count == maxPeersPerIPRange[index] {
  165. return false
  166. }
  167. }
  168. if !updateNestedCountRecursive(c, ipBytes, index+1) {
  169. return false
  170. }
  171. c.count += 1
  172. return true
  173. }