diff --git a/CHANGELOG.md b/CHANGELOG.md index d73c949a2..97acb5855 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ FEATURES [metrics](https://tendermint.readthedocs.io/projects/tools/en/develop/metrics.html) guide. - [p2p] Add IPv6 support to peering. +- [p2p] Add `external_address` to config to allow specifying the address for + peers to dial IMPROVEMENT - [rpc/client] Supports https and wss now. diff --git a/config/config.go b/config/config.go index e01819305..22cecf989 100644 --- a/config/config.go +++ b/config/config.go @@ -276,6 +276,9 @@ type P2PConfig struct { // Address to listen for incoming connections ListenAddress string `mapstructure:"laddr"` + // Address to advertise to peers for them to dial + ExternalAddress string `mapstructure:"external_address"` + // Comma separated list of seed nodes to connect to // We only use these if we can’t connect to peers in the addrbook Seeds string `mapstructure:"seeds"` @@ -340,6 +343,7 @@ type P2PConfig struct { func DefaultP2PConfig() *P2PConfig { return &P2PConfig{ ListenAddress: "tcp://0.0.0.0:26656", + ExternalAddress: "", UPNP: false, AddrBook: defaultAddrBookPath, AddrBookStrict: true, diff --git a/config/toml.go b/config/toml.go index 37ff4d7c1..084325baa 100644 --- a/config/toml.go +++ b/config/toml.go @@ -142,6 +142,12 @@ max_open_connections = {{ .RPC.MaxOpenConnections }} # Address to listen for incoming connections laddr = "{{ .P2P.ListenAddress }}" +# Address to advertise to peers for them to dial +# If empty, will use the same port as the laddr, +# and will introspect on the listener or use UPnP +# to figure out the address. +external_address = "{{ .P2P.ExternalAddress }}" + # Comma separated list of seed nodes to connect to seeds = "{{ .P2P.Seeds }}" diff --git a/node/node.go b/node/node.go index fc05fc32c..fa667d1dc 100644 --- a/node/node.go +++ b/node/node.go @@ -426,8 +426,7 @@ func (n *Node) OnStart() error { } // Create & add listener - protocol, address := cmn.ProtocolAndAddress(n.config.P2P.ListenAddress) - l := p2p.NewDefaultListener(protocol, address, n.config.P2P.UPNP, n.Logger.With("module", "p2p")) + l := p2p.NewDefaultListener(n.config.P2P, n.Logger.With("module", "p2p")) n.sw.AddListener(l) // Generate node PrivKey diff --git a/p2p/listener.go b/p2p/listener.go index cd548866b..339e26dd4 100644 --- a/p2p/listener.go +++ b/p2p/listener.go @@ -7,9 +7,10 @@ import ( "strings" "time" - "github.com/tendermint/tendermint/p2p/upnp" + "github.com/tendermint/tendermint/config" cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/p2p/upnp" ) // Listener is a network listener for stream-oriented protocols, providing @@ -59,8 +60,10 @@ func splitHostPort(addr string) (host string, port int) { // NewDefaultListener creates a new DefaultListener on lAddr, optionally trying // to determine external address using UPnP. -func NewDefaultListener(protocol string, lAddr string, UPNP bool, logger log.Logger) Listener { - // Local listen IP & port +func NewDefaultListener(cfg *config.P2PConfig, logger log.Logger) Listener { + + // Split protocol, address, and port. + protocol, lAddr := cmn.ProtocolAndAddress(cfg.ListenAddress) lAddrIP, lAddrPort := splitHostPort(lAddr) // Create listener @@ -88,17 +91,29 @@ func NewDefaultListener(protocol string, lAddr string, UPNP bool, logger log.Log panic(err) } + inAddrAny := lAddrIP == "" || lAddrIP == "0.0.0.0" + // Determine external address... var extAddr *NetAddress - if UPNP { + if cfg.UPNP { // If the lAddrIP is INADDR_ANY, try UPnP - if lAddrIP == "" || lAddrIP == "0.0.0.0" { + if inAddrAny { extAddr = getUPNPExternalAddress(lAddrPort, listenerPort, logger) } } + + if cfg.ExternalAddress != "" { + var err error + extAddr, err = NewNetAddressStringWithOptionalID(cfg.ExternalAddress) + if err != nil { + panic(fmt.Sprintf("Error in ExternalAddress: %v", err)) + } + } + // Otherwise just use the local address... if extAddr == nil { - extAddr = getNaiveExternalAddress(listenerPort, false, logger) + defaultToIPv4 := inAddrAny + extAddr = getNaiveExternalAddress(defaultToIPv4, listenerPort, false, logger) } if extAddr == nil { panic("Could not determine external address!") @@ -237,7 +252,7 @@ func isIpv6(ip net.IP) bool { } // TODO: use syscalls: see issue #712 -func getNaiveExternalAddress(port int, settleForLocal bool, logger log.Logger) *NetAddress { +func getNaiveExternalAddress(defaultToIPv4 bool, port int, settleForLocal bool, logger log.Logger) *NetAddress { addrs, err := net.InterfaceAddrs() if err != nil { panic(cmn.Fmt("Could not fetch interface addresses: %v", err)) @@ -248,7 +263,7 @@ func getNaiveExternalAddress(port int, settleForLocal bool, logger log.Logger) * if !ok { continue } - if !isIpv6(ipnet.IP) { + if defaultToIPv4 || !isIpv6(ipnet.IP) { v4 := ipnet.IP.To4() if v4 == nil || (!settleForLocal && v4[0] == 127) { // loopback @@ -263,5 +278,5 @@ func getNaiveExternalAddress(port int, settleForLocal bool, logger log.Logger) * // try again, but settle for local logger.Info("Node may not be connected to internet. Settling for local address") - return getNaiveExternalAddress(port, true, logger) + return getNaiveExternalAddress(defaultToIPv4, port, true, logger) } diff --git a/p2p/listener_test.go b/p2p/listener_test.go index 3d8e40731..3b61c98dc 100644 --- a/p2p/listener_test.go +++ b/p2p/listener_test.go @@ -4,12 +4,16 @@ import ( "bytes" "testing" + "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/log" ) func TestListener(t *testing.T) { // Create a listener - l := NewDefaultListener("tcp", ":8001", false, log.TestingLogger()) + cfg := &config.P2PConfig{ + ListenAddress: "tcp://:8001", + } + l := NewDefaultListener(cfg, log.TestingLogger()) // Dial the listener lAddr := l.ExternalAddress() diff --git a/p2p/pex/pex_reactor_test.go b/p2p/pex/pex_reactor_test.go index cdef5440a..dddad8f10 100644 --- a/p2p/pex/pex_reactor_test.go +++ b/p2p/pex/pex_reactor_test.go @@ -109,7 +109,10 @@ func TestPEXReactorRunning(t *testing.T) { addOtherNodeAddrToAddrBook(2, 1) for i, sw := range switches { - sw.AddListener(p2p.NewDefaultListener("tcp", sw.NodeInfo().ListenAddr, false, logger.With("pex", i))) + cfg := &config.P2PConfig{ + ListenAddress: fmt.Sprintf("tcp://%v", sw.NodeInfo().ListenAddr), + } + sw.AddListener(p2p.NewDefaultListener(cfg, logger.With("pex", i))) err := sw.Start() // start switch and reactors require.Nil(t, err) @@ -230,9 +233,9 @@ func TestPEXReactorUsesSeedsIfNeeded(t *testing.T) { ) seed.AddListener( p2p.NewDefaultListener( - "tcp", - seed.NodeInfo().ListenAddr, - false, + &config.P2PConfig{ + ListenAddress: fmt.Sprintf("tcp://%v", seed.NodeInfo().ListenAddr), + }, log.TestingLogger(), ), )