From 1fe41be9294662d08641ae2f7048c689a9f800cb Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Sat, 28 Apr 2018 16:45:08 -0400 Subject: [PATCH] p2p: prevent connections from same ip --- p2p/errors.go | 28 +++++++++++++++++++++++----- p2p/peer.go | 10 ++++++++++ p2p/peer_set.go | 29 +++++++++++++++++++++++------ p2p/switch.go | 24 +++++++++++++++--------- 4 files changed, 71 insertions(+), 20 deletions(-) diff --git a/p2p/errors.go b/p2p/errors.go index f4a09e6c..31450778 100644 --- a/p2p/errors.go +++ b/p2p/errors.go @@ -1,14 +1,32 @@ package p2p import ( - "errors" "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 { Dialed *NetAddress diff --git a/p2p/peer.go b/p2p/peer.go index b9c8f8b4..1b5ec0ae 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -17,6 +17,7 @@ type Peer interface { cmn.Service ID() ID // peer's cryptographic ID + RemoteIP() string // remote IP of the connection IsOutbound() bool // did we dial the peer IsPersistent() bool // do we redial this peer when we disconnect NodeInfo() NodeInfo // peer's info @@ -45,6 +46,15 @@ func (pc peerConn) ID() ID { 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. // // Before using a peer, you will need to perform a handshake on connection. diff --git a/p2p/peer_set.go b/p2p/peer_set.go index a4565ea1..3ac48ea5 100644 --- a/p2p/peer_set.go +++ b/p2p/peer_set.go @@ -7,6 +7,7 @@ import ( // IPeerSet has a (immutable) subset of the methods of PeerSet. type IPeerSet interface { Has(key ID) bool + HasIP(ip string) bool Get(key ID) Peer List() []Peer Size() int @@ -17,9 +18,10 @@ type IPeerSet interface { // PeerSet is a special structure for keeping a table of peers. // Iteration over the peers is super fast and thread-safe. 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 { @@ -30,8 +32,9 @@ type peerSetItem struct { // NewPeerSet creates a new peerSet with a list of initial capacity of 256 items. func NewPeerSet() *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() defer ps.mtx.Unlock() 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) @@ -49,6 +55,7 @@ func (ps *PeerSet) Add(peer Peer) error { // iterating over the ps.list slice. ps.list = append(ps.list, peer) ps.lookup[peer.ID()] = &peerSetItem{peer, index} + ps.lookupIP[peer.RemoteIP()] = struct{}{} return nil } @@ -61,6 +68,15 @@ func (ps *PeerSet) Has(peerKey ID) bool { 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. func (ps *PeerSet) Get(peerKey ID) Peer { ps.mtx.Lock() @@ -76,6 +92,7 @@ func (ps *PeerSet) Get(peerKey ID) Peer { func (ps *PeerSet) Remove(peer Peer) { ps.mtx.Lock() defer ps.mtx.Unlock() + delete(ps.lookupIP[peer.RemoteIP()]) item := ps.lookup[peer.ID()] if item == nil { return diff --git a/p2p/switch.go b/p2p/switch.go index f62e5f99..61c9ce96 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -403,7 +403,7 @@ func (sw *Switch) DialPeersAsync(addrBook AddrBook, peers []string, persistent b sw.randomSleep(0) err := sw.DialPeerWithAddress(addr, persistent) if err != nil { - switch err { + switch err.(type) { case ErrSwitchConnectToSelf, ErrSwitchDuplicatePeer: sw.Logger.Debug("Error dialing peer", "err", err) default: @@ -534,6 +534,8 @@ func (sw *Switch) addPeer(pc peerConn) error { 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. // 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. @@ -564,20 +566,24 @@ func (sw *Switch) addPeer(pc peerConn) error { // Avoid self if sw.nodeKey.ID() == peerID { 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) - - // add the given address to the address book to avoid dialing ourselves - // again this is our public address sw.addrBook.AddOurAddress(addr) - - return ErrSwitchConnectToSelf + return ErrSwitchConnectToSelf{addr} } // Avoid duplicate 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