Browse Source

p2p: prevent connections from same ip

pull/1520/head
Ethan Buchman 7 years ago
committed by Alexander Simmerl
parent
commit
1fe41be929
No known key found for this signature in database GPG Key ID: 4694E95C9CC61BDA
4 changed files with 71 additions and 20 deletions
  1. +23
    -5
      p2p/errors.go
  2. +10
    -0
      p2p/peer.go
  3. +23
    -6
      p2p/peer_set.go
  4. +15
    -9
      p2p/switch.go

+ 23
- 5
p2p/errors.go View File

@ -1,14 +1,32 @@
package p2p package p2p
import ( import (
"errors"
"fmt" "fmt"
) )
var (
ErrSwitchDuplicatePeer = errors.New("Duplicate peer")
ErrSwitchConnectToSelf = errors.New("Connect to self")
)
type ErrSwitchDuplicatePeerID struct {
ID ID
}
func (e ErrSwitchDuplicatePeerID) Error() string {
return fmt.Errorf("Duplicate peer ID %v", e.ID)
}
type ErrSwitchDuplicatePeerIP struct {
Addr string
}
func (e ErrSwitchDuplicatePeerIP) Error() string {
return fmt.Errorf("Duplicate peer IP %v", e.Addr)
}
type ErrSwitchConnectToSelf struct {
Addr *NetAddress
}
func (e ErrSwitchConnectToSelf) Error() string {
return fmt.Errorf("Connect to self: %v", e.Addr)
}
type ErrSwitchAuthenticationFailure struct { type ErrSwitchAuthenticationFailure struct {
Dialed *NetAddress Dialed *NetAddress


+ 10
- 0
p2p/peer.go View File

@ -17,6 +17,7 @@ type Peer interface {
cmn.Service cmn.Service
ID() ID // peer's cryptographic ID ID() ID // peer's cryptographic ID
RemoteIP() string // remote IP of the connection
IsOutbound() bool // did we dial the peer IsOutbound() bool // did we dial the peer
IsPersistent() bool // do we redial this peer when we disconnect IsPersistent() bool // do we redial this peer when we disconnect
NodeInfo() NodeInfo // peer's info NodeInfo() NodeInfo // peer's info
@ -45,6 +46,15 @@ func (pc peerConn) ID() ID {
return PubKeyToID(pc.conn.(*tmconn.SecretConnection).RemotePubKey()) return PubKeyToID(pc.conn.(*tmconn.SecretConnection).RemotePubKey())
} }
// Return the IP from the connection RemoteAddr
func (pc peerConn) RemoteIP() string {
host, _, err := net.SplitHostPort(pc.conn.RemoteAddr().String())
if err != nil {
panic(err)
}
return host
}
// peer implements Peer. // peer implements Peer.
// //
// Before using a peer, you will need to perform a handshake on connection. // Before using a peer, you will need to perform a handshake on connection.


+ 23
- 6
p2p/peer_set.go View File

@ -7,6 +7,7 @@ import (
// IPeerSet has a (immutable) subset of the methods of PeerSet. // IPeerSet has a (immutable) subset of the methods of PeerSet.
type IPeerSet interface { type IPeerSet interface {
Has(key ID) bool Has(key ID) bool
HasIP(ip string) bool
Get(key ID) Peer Get(key ID) Peer
List() []Peer List() []Peer
Size() int Size() int
@ -17,9 +18,10 @@ type IPeerSet interface {
// PeerSet is a special structure for keeping a table of peers. // PeerSet is a special structure for keeping a table of peers.
// Iteration over the peers is super fast and thread-safe. // Iteration over the peers is super fast and thread-safe.
type PeerSet struct { type PeerSet struct {
mtx sync.Mutex
lookup map[ID]*peerSetItem
list []Peer
mtx sync.Mutex
lookup map[ID]*peerSetItem
lookupIP map[string]struct{}
list []Peer
} }
type peerSetItem struct { type peerSetItem struct {
@ -30,8 +32,9 @@ type peerSetItem struct {
// NewPeerSet creates a new peerSet with a list of initial capacity of 256 items. // NewPeerSet creates a new peerSet with a list of initial capacity of 256 items.
func NewPeerSet() *PeerSet { func NewPeerSet() *PeerSet {
return &PeerSet{ return &PeerSet{
lookup: make(map[ID]*peerSetItem),
list: make([]Peer, 0, 256),
lookup: make(map[ID]*peerSetItem),
lookupIP: make(map[string]struct{}),
list: make([]Peer, 0, 256),
} }
} }
@ -41,7 +44,10 @@ func (ps *PeerSet) Add(peer Peer) error {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
if ps.lookup[peer.ID()] != nil { if ps.lookup[peer.ID()] != nil {
return ErrSwitchDuplicatePeer
return ErrSwitchDuplicatePeerID{peer.ID()}
}
if _, ok := ps.lookupIP[peer.RemoteIP()]; ok {
return ErrSwitchDuplicatePeerIP{peer.RemoteIP()}
} }
index := len(ps.list) index := len(ps.list)
@ -49,6 +55,7 @@ func (ps *PeerSet) Add(peer Peer) error {
// iterating over the ps.list slice. // iterating over the ps.list slice.
ps.list = append(ps.list, peer) ps.list = append(ps.list, peer)
ps.lookup[peer.ID()] = &peerSetItem{peer, index} ps.lookup[peer.ID()] = &peerSetItem{peer, index}
ps.lookupIP[peer.RemoteIP()] = struct{}{}
return nil return nil
} }
@ -61,6 +68,15 @@ func (ps *PeerSet) Has(peerKey ID) bool {
return ok return ok
} }
// HasIP returns true iff the PeerSet contains
// the peer referred to by this IP address.
func (ps *PeerSet) HasIP(peerIP string) bool {
ps.mtx.Lock()
_, ok := ps.lookupIP[peerIP]
ps.mtx.Unlock()
return ok
}
// Get looks up a peer by the provided peerKey. // Get looks up a peer by the provided peerKey.
func (ps *PeerSet) Get(peerKey ID) Peer { func (ps *PeerSet) Get(peerKey ID) Peer {
ps.mtx.Lock() ps.mtx.Lock()
@ -76,6 +92,7 @@ func (ps *PeerSet) Get(peerKey ID) Peer {
func (ps *PeerSet) Remove(peer Peer) { func (ps *PeerSet) Remove(peer Peer) {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
delete(ps.lookupIP[peer.RemoteIP()])
item := ps.lookup[peer.ID()] item := ps.lookup[peer.ID()]
if item == nil { if item == nil {
return return


+ 15
- 9
p2p/switch.go View File

@ -403,7 +403,7 @@ func (sw *Switch) DialPeersAsync(addrBook AddrBook, peers []string, persistent b
sw.randomSleep(0) sw.randomSleep(0)
err := sw.DialPeerWithAddress(addr, persistent) err := sw.DialPeerWithAddress(addr, persistent)
if err != nil { if err != nil {
switch err {
switch err.(type) {
case ErrSwitchConnectToSelf, ErrSwitchDuplicatePeer: case ErrSwitchConnectToSelf, ErrSwitchDuplicatePeer:
sw.Logger.Debug("Error dialing peer", "err", err) sw.Logger.Debug("Error dialing peer", "err", err)
default: default:
@ -534,6 +534,8 @@ func (sw *Switch) addPeer(pc peerConn) error {
return err return err
} }
// dont connect to multiple peers on the same IP
// NOTE: if AuthEnc==false, we don't have a peerID until after the handshake. // NOTE: if AuthEnc==false, we don't have a peerID until after the handshake.
// If AuthEnc==true then we already know the ID and could do the checks first before the handshake, // If AuthEnc==true then we already know the ID and could do the checks first before the handshake,
// but it's simple to just deal with both cases the same after the handshake. // but it's simple to just deal with both cases the same after the handshake.
@ -564,20 +566,24 @@ func (sw *Switch) addPeer(pc peerConn) error {
// Avoid self // Avoid self
if sw.nodeKey.ID() == peerID { if sw.nodeKey.ID() == peerID {
addr := peerNodeInfo.NetAddress() addr := peerNodeInfo.NetAddress()
// remove the given address from the address book if we added it earlier
// remove the given address from the address book
// and add to our addresses to avoid dialing again
sw.addrBook.RemoveAddress(addr) sw.addrBook.RemoveAddress(addr)
// add the given address to the address book to avoid dialing ourselves
// again this is our public address
sw.addrBook.AddOurAddress(addr) sw.addrBook.AddOurAddress(addr)
return ErrSwitchConnectToSelf
return ErrSwitchConnectToSelf{addr}
} }
// Avoid duplicate // Avoid duplicate
if sw.peers.Has(peerID) { if sw.peers.Has(peerID) {
return ErrSwitchDuplicatePeer
return ErrSwitchDuplicatePeerID{peerID}
}
// check ips for both the connection addr and the self reported addr
if sw.peers.HasIP(addr) {
return ErrSwitchDuplicatePeerIP{addr}
}
if sw.peers.HasIP(peerNodeInfo.ListenAddr) {
return ErrSwitchDuplicatePeerIP{peerNodeInfo.ListenAddr}
} }
// Filter peer against ID white list // Filter peer against ID white list


Loading…
Cancel
Save