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
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


+ 10
- 0
p2p/peer.go View File

@ -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.


+ 23
- 6
p2p/peer_set.go View File

@ -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


+ 15
- 9
p2p/switch.go View File

@ -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


Loading…
Cancel
Save