multi: add automatic network address discovery.

This discovers the network address(es) of the daemon
through connected outbound peers. The address(es)
discovered are advertised to subsequent connecting peers.
This commit is contained in:
Donald Adu-Poku 2019-08-14 11:43:31 +00:00 committed by Dave Collins
parent 25c14e046a
commit 77a14a9ead
4 changed files with 193 additions and 10 deletions

View File

@ -1013,19 +1013,44 @@ func (a *AddrManager) AddLocalAddress(na *wire.NetAddress, priority AddressPrior
return nil
}
// HasLocalAddress asserts if the manager has the provided local address.
func (a *AddrManager) HasLocalAddress(na *wire.NetAddress) bool {
key := NetAddressKey(na)
a.lamtx.Lock()
_, ok := a.localAddresses[key]
a.lamtx.Unlock()
return ok
}
const (
// Unreachable represents a publicly unreachable connection state
// between two addresses.
Unreachable = 0
// Default represents the default connection state between
// two addresses.
Default = iota
// Teredo represents a connection state between two RFC4380 addresses.
Teredo
// Ipv6Weak represents a weak IPV6 connection state between two
// addresses.
Ipv6Weak
// Ipv4 represents an IPV4 connection state between two addreses.
Ipv4
// Ipv6Strong represents a connection state between two IPV6 addresses.
Ipv6Strong
// Private reprsents a connection state connect between two Tor addresses.
Private
)
// getReachabilityFrom returns the relative reachability of the provided local
// address to the provided remote address.
func getReachabilityFrom(localAddr, remoteAddr *wire.NetAddress) int {
const (
Unreachable = 0
Default = iota
Teredo
Ipv6Weak
Ipv4
Ipv6Strong
Private
)
if !IsRoutable(remoteAddr) {
return Unreachable
}
@ -1130,6 +1155,15 @@ func (a *AddrManager) GetBestLocalAddress(remoteAddr *wire.NetAddress) *wire.Net
return bestAddress
}
// IsPeerNaValid asserts if the the provided local address is routable
// and reachable from the peer that suggested it.
func (a *AddrManager) IsPeerNaValid(localAddr, remoteAddr *wire.NetAddress) bool {
net := getNetwork(localAddr)
reach := getReachabilityFrom(localAddr, remoteAddr)
return (net == IPv4Address && reach == Ipv4) || (net == IPv6Address &&
(reach == Ipv6Weak || reach == Ipv6Strong || reach == Teredo))
}
// New returns a new Decred address manager.
// Use Start to begin processing asynchronous address updates.
// The address manager uses lookupFunc for necessary DNS lookups.

View File

@ -118,6 +118,33 @@ func isOnionCatTor(na *wire.NetAddress) bool {
return onionCatNet.Contains(na.IP)
}
// NetworkAddress type is used to classify a network address.
type NetworkAddress int
const (
LocalAddress NetworkAddress = iota
IPv4Address
IPv6Address
OnionAddress
)
// getNetwork returns the network address type of the provided network address.
func getNetwork(na *wire.NetAddress) NetworkAddress {
switch {
case isLocal(na):
return LocalAddress
case isIPv4(na):
return IPv4Address
case isOnionCatTor(na):
return OnionAddress
default:
return IPv6Address
}
}
// isRFC1918 returns whether or not the passed address is part of the IPv4
// private network address space as defined by RFC1918 (10.0.0.0/8,
// 172.16.0.0/12, or 192.168.0.0/16).

View File

@ -129,6 +129,7 @@ type config struct {
OnionProxyUser string `long:"onionuser" description:"Username for onion proxy server"`
OnionProxyPass string `long:"onionpass" default-mask:"-" description:"Password for onion proxy server"`
NoOnion bool `long:"noonion" description:"Disable connecting to tor hidden services"`
NoDiscoverIP bool `long:"nodiscoverip" description:"Disable automatic network address discovery"`
TorIsolation bool `long:"torisolation" description:"Enable Tor stream isolation by randomizing user credentials for each connection."`
TestNet bool `long:"testnet" description:"Use the test network"`
SimNet bool `long:"simnet" description:"Use the simulation test network"`

121
server.go
View File

@ -129,6 +129,11 @@ type peerState struct {
persistentPeers map[int32]*serverPeer
banned map[string]time.Time
outboundGroups map[string]int
// suggestions represents public network address suggestions from outbound
// peers.
suggestions map[addrmgr.NetworkAddress]map[string]int32
suggestionsMtx sync.Mutex
}
// ConnectionsWithIP returns the number of connections with the given IP.
@ -178,6 +183,46 @@ func (ps *peerState) forAllPeers(closure func(sp *serverPeer)) {
ps.forAllOutboundPeers(closure)
}
// ResolveLocalAddress picks the best suggested network address from available
// options, per the network interface key provided. The best suggestion, if
// found, is added as a local address.
func (ps *peerState) ResolveLocalAddress(netKey addrmgr.NetworkAddress, addrMgr *addrmgr.AddrManager, services wire.ServiceFlag, port uint16) {
ps.suggestionsMtx.Lock()
count := len(ps.suggestions[netKey])
if count == 0 {
ps.suggestionsMtx.Unlock()
return
}
var bestSuggestion string
var bestTally int32
for suggestion, tally := range ps.suggestions[netKey] {
switch {
case bestSuggestion == "", tally > bestTally:
bestSuggestion = suggestion
bestTally = tally
}
}
ps.suggestionsMtx.Unlock()
// A valid best address suggestion must have at least two outbound peers
// concluding on the same result.
if bestTally < 2 {
return
}
na, err := addrMgr.HostToNetAddress(bestSuggestion, port, services)
if err != nil {
amgrLog.Errorf("unable to generate network address using host %v: "+
"%v", bestSuggestion, err)
return
}
if !addrMgr.HasLocalAddress(na) {
addrMgr.AddLocalAddress(na, addrmgr.ManualPrio)
}
}
// server provides a Decred server for handling communications to and from
// Decred peers.
type server struct {
@ -254,6 +299,10 @@ type serverPeer struct {
// The following chans are used to sync blockmanager and server.
txProcessed chan struct{}
blockProcessed chan struct{}
// peerNa is network address of the peer connected to.
peerNa *wire.NetAddress
peerNaMtx sync.Mutex
}
// newServerPeer returns a new serverPeer instance. The peer needs to be set by
@ -442,6 +491,12 @@ func (sp *serverPeer) OnVersion(p *peer.Peer, msg *wire.MsgVersion) *wire.MsgRej
addrManager.Good(remoteAddr)
}
if !sp.Inbound() {
sp.peerNaMtx.Lock()
sp.peerNa = &msg.AddrYou
sp.peerNaMtx.Unlock()
}
// Choose whether or not to relay transactions.
sp.setDisableRelayTx(msg.DisableRelayTx)
@ -1361,6 +1416,68 @@ func (s *server) handleAddPeerMsg(state *peerState, sp *serverPeer) bool {
} else {
state.outboundPeers[sp.ID()] = sp
}
// Fetch the suggested public ip from the outbound peer if
// there are no prevailing conditions to disable automatic
// network address discovery.
//
// The conditions to disable automatic network address
// discovery are:
// - If there is a proxy set (--proxy, --onion).
// - If automatic network address discovery is explicitly
// disabled (--nodiscoverip).
// - If there is an external ip explicitly set (--externalip).
// - If listening has been disabled (--nolisten, listen
// disabled because of --connect, etc).
// - If Universal Plug and Play is enabled (--upnp).
// - If the active network is simnet or regnet.
if (cfg.Proxy != "" || cfg.OnionProxy != "") ||
cfg.NoDiscoverIP || len(cfg.ExternalIPs) > 0 ||
(cfg.DisableListen || len(cfg.Listeners) == 0) || cfg.Upnp ||
activeNetParams.Name == simNetParams.Name ||
activeNetParams.Name == regNetParams.Name {
return true
}
sp.peerNaMtx.Lock()
na := sp.peerNa
sp.peerNaMtx.Unlock()
if na == nil {
return true
}
if !s.addrManager.IsPeerNaValid(na, sp.NA()) {
return true
}
port, err := strconv.ParseUint(activeNetParams.DefaultPort, 10, 16)
if err != nil {
srvrLog.Errorf("unabled to parse active network port: %v", err)
return true
}
id := na.IP.String()
switch {
case na.IP.To4() != nil:
state.suggestionsMtx.Lock()
state.suggestions[addrmgr.IPv4Address][id]++
state.suggestionsMtx.Unlock()
state.ResolveLocalAddress(addrmgr.IPv4Address, s.addrManager,
s.services, uint16(port))
case na.IP.To16() != nil:
state.suggestionsMtx.Lock()
state.suggestions[addrmgr.IPv6Address][id]++
state.suggestionsMtx.Unlock()
state.ResolveLocalAddress(addrmgr.IPv6Address, s.addrManager,
s.services, uint16(port))
default:
return true
}
}
return true
@ -1769,6 +1886,10 @@ func (s *server) peerHandler() {
outboundPeers: make(map[int32]*serverPeer),
banned: make(map[string]time.Time),
outboundGroups: make(map[string]int),
suggestions: map[addrmgr.NetworkAddress]map[string]int32{
addrmgr.IPv4Address: make(map[string]int32),
addrmgr.IPv6Address: make(map[string]int32),
},
}
if !cfg.DisableDNSSeed {