feat: obtain external address for rendezvous (#2333)
* feat: obtain external address for rendezvous If the ext ip returned by geth is 127.0.0.1, it will attempt to obtain the external IP address via rendezvous and use that to register the ens record later * fix: failing test * fix: code review, and adding external ip address to logs
This commit is contained in:
parent
012e74fd79
commit
12ddb0739e
|
@ -104,7 +104,7 @@ func (r *Rendezvous) Stop() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Rendezvous) MakeRecord() (record enr.Record, err error) {
|
func (r *Rendezvous) MakeRecord(srv *ma.Multiaddr) (record enr.Record, err error) {
|
||||||
r.recordMu.Lock()
|
r.recordMu.Lock()
|
||||||
defer r.recordMu.Unlock()
|
defer r.recordMu.Unlock()
|
||||||
if r.record != nil {
|
if r.record != nil {
|
||||||
|
@ -116,7 +116,27 @@ func (r *Rendezvous) MakeRecord() (record enr.Record, err error) {
|
||||||
if r.identity == nil {
|
if r.identity == nil {
|
||||||
return record, errIdentityIsNil
|
return record, errIdentityIsNil
|
||||||
}
|
}
|
||||||
record.Set(enr.IP(r.node.IP()))
|
|
||||||
|
ip := r.node.IP()
|
||||||
|
if srv != nil && (ip.IsLoopback() || IsPrivate(ip)) { // If AdvertiseAddr is not specified, 127.0.0.1 might be returned
|
||||||
|
ctx, cancel := context.WithTimeout(r.rootCtx, requestTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
ipAddr, err := r.client.RemoteIp(ctx, *srv)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("could not obtain the external ip address", "err", err)
|
||||||
|
} else {
|
||||||
|
parsedIP := net.ParseIP(ipAddr)
|
||||||
|
if parsedIP != nil {
|
||||||
|
ip = parsedIP
|
||||||
|
log.Info("node's external IP address", "ipAddr", ipAddr)
|
||||||
|
} else {
|
||||||
|
log.Error("invalid ip address obtained from rendezvous server", "ipaddr", ipAddr, "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record.Set(enr.IP(ip))
|
||||||
record.Set(enr.TCP(r.node.TCP()))
|
record.Set(enr.TCP(r.node.TCP()))
|
||||||
record.Set(enr.UDP(r.node.UDP()))
|
record.Set(enr.UDP(r.node.UDP()))
|
||||||
// public key is added to ENR when ENR is signed
|
// public key is added to ENR when ENR is signed
|
||||||
|
@ -127,8 +147,7 @@ func (r *Rendezvous) MakeRecord() (record enr.Record, err error) {
|
||||||
return record, nil
|
return record, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Rendezvous) register(topic string, record enr.Record) error {
|
func (r *Rendezvous) register(srv ma.Multiaddr, topic string, record enr.Record) error {
|
||||||
srv := r.servers[rand.Intn(len(r.servers))] // nolint: gosec
|
|
||||||
ctx, cancel := context.WithTimeout(r.rootCtx, requestTimeout)
|
ctx, cancel := context.WithTimeout(r.rootCtx, requestTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
@ -146,7 +165,8 @@ func (r *Rendezvous) register(topic string, record enr.Record) error {
|
||||||
|
|
||||||
// Register renews registration in the specified server.
|
// Register renews registration in the specified server.
|
||||||
func (r *Rendezvous) Register(topic string, stop chan struct{}) error {
|
func (r *Rendezvous) Register(topic string, stop chan struct{}) error {
|
||||||
record, err := r.MakeRecord()
|
srv := r.getRandomServer()
|
||||||
|
record, err := r.MakeRecord(&srv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -155,7 +175,7 @@ func (r *Rendezvous) Register(topic string, stop chan struct{}) error {
|
||||||
ticker := time.NewTicker(r.registrationPeriod / 2)
|
ticker := time.NewTicker(r.registrationPeriod / 2)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
if err := r.register(topic, record); err == context.Canceled {
|
if err := r.register(srv, topic, record); err == context.Canceled {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +184,7 @@ func (r *Rendezvous) Register(topic string, stop chan struct{}) error {
|
||||||
case <-stop:
|
case <-stop:
|
||||||
return nil
|
return nil
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
if err := r.register(topic, record); err == context.Canceled {
|
if err := r.register(r.getRandomServer(), topic, record); err == context.Canceled {
|
||||||
return err
|
return err
|
||||||
} else if err == errDiscoveryIsStopped {
|
} else if err == errDiscoveryIsStopped {
|
||||||
return nil
|
return nil
|
||||||
|
@ -230,6 +250,10 @@ func (r *Rendezvous) Discover(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Rendezvous) getRandomServer() ma.Multiaddr {
|
||||||
|
return r.servers[rand.Intn(len(r.servers))] // nolint: gosec
|
||||||
|
}
|
||||||
|
|
||||||
func enrToNode(record enr.Record) (*discv5.Node, error) {
|
func enrToNode(record enr.Record) (*discv5.Node, error) {
|
||||||
var (
|
var (
|
||||||
key enode.Secp256k1
|
key enode.Secp256k1
|
||||||
|
@ -253,3 +277,27 @@ func enrToNode(record enr.Record) (*discv5.Node, error) {
|
||||||
_ = record.Load(&uport)
|
_ = record.Load(&uport)
|
||||||
return discv5.NewNode(nodeID, net.IP(ip), uint16(uport), uint16(tport)), nil
|
return discv5.NewNode(nodeID, net.IP(ip), uint16(uport), uint16(tport)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsPrivate reports whether ip is a private address, according to
|
||||||
|
// RFC 1918 (IPv4 addresses) and RFC 4193 (IPv6 addresses).
|
||||||
|
// Copied/Adapted from https://go-review.googlesource.com/c/go/+/272668/11/src/net/ip.go
|
||||||
|
// Copyright (c) The Go Authors. All rights reserved.
|
||||||
|
// @TODO: once Go 1.17 is released in Q42021, remove this function as it will become part of the language
|
||||||
|
func IsPrivate(ip net.IP) bool {
|
||||||
|
if ip4 := ip.To4(); ip4 != nil {
|
||||||
|
// Following RFC 4193, Section 3. Local IPv6 Unicast Addresses which says:
|
||||||
|
// The Internet Assigned Numbers Authority (IANA) has reserved the
|
||||||
|
// following three blocks of the IPv4 address space for private internets:
|
||||||
|
// 10.0.0.0 - 10.255.255.255 (10/8 prefix)
|
||||||
|
// 172.16.0.0 - 172.31.255.255 (172.16/12 prefix)
|
||||||
|
// 192.168.0.0 - 192.168.255.255 (192.168/16 prefix)
|
||||||
|
return ip4[0] == 10 ||
|
||||||
|
(ip4[0] == 172 && ip4[1]&0xf0 == 16) ||
|
||||||
|
(ip4[0] == 192 && ip4[1] == 168)
|
||||||
|
}
|
||||||
|
// Following RFC 4193, Section 3. Private Address Space which says:
|
||||||
|
// The Internet Assigned Numbers Authority (IANA) has reserved the
|
||||||
|
// following block of the IPv6 address space for local internets:
|
||||||
|
// FC00:: - FDFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF (FC00::/7 prefix)
|
||||||
|
return len(ip) == net.IPv6len && ip[0]&0xfe == 0xfc
|
||||||
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ func TestMakeRecordReturnsCachedRecord(t *testing.T) {
|
||||||
record := enr.Record{}
|
record := enr.Record{}
|
||||||
require.NoError(t, enode.SignV4(&record, identity))
|
require.NoError(t, enode.SignV4(&record, identity))
|
||||||
c := NewRendezvousWithENR(nil, record)
|
c := NewRendezvousWithENR(nil, record)
|
||||||
rst, err := c.MakeRecord()
|
rst, err := c.MakeRecord(nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, enode.V4ID{}.NodeAddr(&rst))
|
require.NotNil(t, enode.V4ID{}.NodeAddr(&rst))
|
||||||
require.Equal(t, enode.V4ID{}.NodeAddr(&record), enode.V4ID{}.NodeAddr(&rst))
|
require.Equal(t, enode.V4ID{}.NodeAddr(&record), enode.V4ID{}.NodeAddr(&rst))
|
||||||
|
@ -79,7 +79,7 @@ func TestRendezvousRegisterAndDiscoverExitGracefully(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, r.Start())
|
require.NoError(t, r.Start())
|
||||||
require.NoError(t, r.Stop())
|
require.NoError(t, r.Stop())
|
||||||
require.EqualError(t, errDiscoveryIsStopped, r.register("", enr.Record{}).Error())
|
require.EqualError(t, errDiscoveryIsStopped, r.register(r.getRandomServer(), "", enr.Record{}).Error())
|
||||||
_, err = r.discoverRequest(nil, "")
|
_, err = r.discoverRequest(nil, "")
|
||||||
require.EqualError(t, errDiscoveryIsStopped, err.Error())
|
require.EqualError(t, errDiscoveryIsStopped, err.Error())
|
||||||
}
|
}
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -51,7 +51,7 @@ require (
|
||||||
github.com/status-im/go-wakurelay-pubsub v0.4.3-0.20210729162817-adc68830282a
|
github.com/status-im/go-wakurelay-pubsub v0.4.3-0.20210729162817-adc68830282a
|
||||||
github.com/status-im/markdown v0.0.0-20201022101546-c0cbdd5763bf
|
github.com/status-im/markdown v0.0.0-20201022101546-c0cbdd5763bf
|
||||||
github.com/status-im/migrate/v4 v4.6.2-status.2
|
github.com/status-im/migrate/v4 v4.6.2-status.2
|
||||||
github.com/status-im/rendezvous v1.3.2
|
github.com/status-im/rendezvous v1.3.1-0.20210824184947-7c79c858170c
|
||||||
github.com/status-im/status-go/extkeys v1.1.2
|
github.com/status-im/status-go/extkeys v1.1.2
|
||||||
github.com/status-im/tcp-shaker v0.0.0-20191114194237-215893130501
|
github.com/status-im/tcp-shaker v0.0.0-20191114194237-215893130501
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -1113,8 +1113,8 @@ github.com/status-im/markdown v0.0.0-20201022101546-c0cbdd5763bf h1:0w1mjNUqEHoL
|
||||||
github.com/status-im/markdown v0.0.0-20201022101546-c0cbdd5763bf/go.mod h1:9yR8woqkJIHs3sf9pEjYvaGfmhsXR1leEMAX6+Z5y+M=
|
github.com/status-im/markdown v0.0.0-20201022101546-c0cbdd5763bf/go.mod h1:9yR8woqkJIHs3sf9pEjYvaGfmhsXR1leEMAX6+Z5y+M=
|
||||||
github.com/status-im/migrate/v4 v4.6.2-status.2 h1:SdC+sMDl/aI7vUlwD2qj2p7KsK4T60IS9z4/rYCCbI8=
|
github.com/status-im/migrate/v4 v4.6.2-status.2 h1:SdC+sMDl/aI7vUlwD2qj2p7KsK4T60IS9z4/rYCCbI8=
|
||||||
github.com/status-im/migrate/v4 v4.6.2-status.2/go.mod h1:c/kc90n47GZu/58nnz1OMLTf7uE4Da4gZP5qmU+A/v8=
|
github.com/status-im/migrate/v4 v4.6.2-status.2/go.mod h1:c/kc90n47GZu/58nnz1OMLTf7uE4Da4gZP5qmU+A/v8=
|
||||||
github.com/status-im/rendezvous v1.3.2 h1:eLTQ1EEg/qTsFDM6lwIf7tAFqiD3zEHnbWKaS3fvd/E=
|
github.com/status-im/rendezvous v1.3.1-0.20210824184947-7c79c858170c h1:l83ciN0A5LW7c6NMa/j0x2mH1vsv3hbAAUTEwZw6qF4=
|
||||||
github.com/status-im/rendezvous v1.3.2/go.mod h1:CK8B3kCbx3QrE0V64aAocU8oh9KRktoKSU0sqiF6MwI=
|
github.com/status-im/rendezvous v1.3.1-0.20210824184947-7c79c858170c/go.mod h1:CK8B3kCbx3QrE0V64aAocU8oh9KRktoKSU0sqiF6MwI=
|
||||||
github.com/status-im/resize v0.0.0-20201215164250-7c6d9f0d3088 h1:ClCAP2FPCvl8hGMhbUx/tq/sOu2wibztAa5jAvQEe4Q=
|
github.com/status-im/resize v0.0.0-20201215164250-7c6d9f0d3088 h1:ClCAP2FPCvl8hGMhbUx/tq/sOu2wibztAa5jAvQEe4Q=
|
||||||
github.com/status-im/resize v0.0.0-20201215164250-7c6d9f0d3088/go.mod h1:+92j1tN27DypDeBFxkg0uzkqfh1bNHTZe3Bv2PjvxpM=
|
github.com/status-im/resize v0.0.0-20201215164250-7c6d9f0d3088/go.mod h1:+92j1tN27DypDeBFxkg0uzkqfh1bNHTZe3Bv2PjvxpM=
|
||||||
github.com/status-im/status-go/extkeys v1.1.2 h1:FSjARgDathJ3rIapJt851LsIXP9Oyuu2M2jPJKuzloU=
|
github.com/status-im/status-go/extkeys v1.1.2 h1:FSjARgDathJ3rIapJt851LsIXP9Oyuu2M2jPJKuzloU=
|
||||||
|
|
|
@ -340,6 +340,9 @@ type NodeConfig struct {
|
||||||
|
|
||||||
// AdvertiseAddr is a public IP address the node wants to be found with.
|
// AdvertiseAddr is a public IP address the node wants to be found with.
|
||||||
// It is especially useful when using floating IPs attached to a server.
|
// It is especially useful when using floating IPs attached to a server.
|
||||||
|
// This configuration value is used by rendezvous protocol, and it's optional
|
||||||
|
// If no value is specified, it will attempt to determine the node's external
|
||||||
|
// IP address. A value can be specified in case the returned address is incorrect
|
||||||
AdvertiseAddr string
|
AdvertiseAddr string
|
||||||
|
|
||||||
// Name sets the instance name of the node. It must not contain the / character.
|
// Name sets the instance name of the node. It must not contain the / character.
|
||||||
|
|
|
@ -115,12 +115,46 @@ func (c Client) Discover(ctx context.Context, srv ma.Multiaddr, topic string, li
|
||||||
return val.Records, nil
|
return val.Records, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c Client) RemoteIp(ctx context.Context, srv ma.Multiaddr) (value string, err error) {
|
||||||
|
s, err := c.newStream(ctx, srv)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
if err = rlp.Encode(s, protocol.REMOTEIP); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rs := rlp.NewStream(s, 0)
|
||||||
|
typ, err := rs.Uint()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if protocol.MessageType(typ) != protocol.REMOTEIP_RESPONSE {
|
||||||
|
err = fmt.Errorf("expected %v as response, but got %v", protocol.REMOTEIP_RESPONSE, typ)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var val protocol.RemoteIpResponse
|
||||||
|
if err = rs.Decode(&val); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if val.Status != protocol.OK {
|
||||||
|
err = fmt.Errorf("remoteip request failed. status code %v", val.Status)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Debug("received response to remoteip request", "status", val.Status, "ip", val.IP)
|
||||||
|
value = val.IP
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (c Client) newStream(ctx context.Context, srv ma.Multiaddr) (rw network.Stream, err error) {
|
func (c Client) newStream(ctx context.Context, srv ma.Multiaddr) (rw network.Stream, err error) {
|
||||||
pid, err := srv.ValueForProtocol(ethv4.P_ETHv4)
|
pid, err := srv.ValueForProtocol(ethv4.P_ETHv4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
peerid, err := peer.IDB58Decode(pid)
|
peerid, err := peer.Decode(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@ const (
|
||||||
REGISTER_RESPONSE
|
REGISTER_RESPONSE
|
||||||
DISCOVER
|
DISCOVER
|
||||||
DISCOVER_RESPONSE
|
DISCOVER_RESPONSE
|
||||||
|
REMOTEIP
|
||||||
|
REMOTEIP_RESPONSE
|
||||||
|
|
||||||
OK ResponseStatus = 0
|
OK ResponseStatus = 0
|
||||||
E_INVALID_NAMESPACE ResponseStatus = 100
|
E_INVALID_NAMESPACE ResponseStatus = 100
|
||||||
|
@ -46,3 +48,11 @@ type DiscoverResponse struct {
|
||||||
Message string
|
Message string
|
||||||
Records []enr.Record
|
Records []enr.Record
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RemoteIp struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemoteIpResponse struct {
|
||||||
|
Status ResponseStatus
|
||||||
|
IP string
|
||||||
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"github.com/libp2p/go-libp2p-core/crypto"
|
"github.com/libp2p/go-libp2p-core/crypto"
|
||||||
"github.com/libp2p/go-libp2p-core/host"
|
"github.com/libp2p/go-libp2p-core/host"
|
||||||
"github.com/libp2p/go-libp2p-core/network"
|
"github.com/libp2p/go-libp2p-core/network"
|
||||||
|
"github.com/multiformats/go-multiaddr"
|
||||||
ma "github.com/multiformats/go-multiaddr"
|
ma "github.com/multiformats/go-multiaddr"
|
||||||
"github.com/status-im/rendezvous/protocol"
|
"github.com/status-im/rendezvous/protocol"
|
||||||
)
|
)
|
||||||
|
@ -132,7 +133,7 @@ func (srv *Server) startListener() error {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.SetReadDeadline(time.Now().Add(srv.readTimeout))
|
s.SetReadDeadline(time.Now().Add(srv.readTimeout))
|
||||||
resptype, resp, err := srv.msgParser(protocol.MessageType(typ), rs)
|
resptype, resp, err := srv.msgParser(s, protocol.MessageType(typ), rs)
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -198,7 +199,7 @@ type Decoder interface {
|
||||||
Decode(val interface{}) error
|
Decode(val interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) msgParser(typ protocol.MessageType, d Decoder) (resptype protocol.MessageType, resp interface{}, err error) {
|
func (srv *Server) msgParser(s network.Stream, typ protocol.MessageType, d Decoder) (resptype protocol.MessageType, resp interface{}, err error) {
|
||||||
switch typ {
|
switch typ {
|
||||||
case protocol.REGISTER:
|
case protocol.REGISTER:
|
||||||
var msg protocol.Register
|
var msg protocol.Register
|
||||||
|
@ -229,6 +230,14 @@ func (srv *Server) msgParser(typ protocol.MessageType, d Decoder) (resptype prot
|
||||||
metrics.ObserveDiscoveryDuration(time.Since(start).Seconds(), msg.Topic)
|
metrics.ObserveDiscoveryDuration(time.Since(start).Seconds(), msg.Topic)
|
||||||
metrics.ObserveDiscoverSize(float64(len(records)), msg.Topic)
|
metrics.ObserveDiscoverSize(float64(len(records)), msg.Topic)
|
||||||
return resptype, protocol.DiscoverResponse{Status: protocol.OK, Records: records}, nil
|
return resptype, protocol.DiscoverResponse{Status: protocol.OK, Records: records}, nil
|
||||||
|
case protocol.REMOTEIP:
|
||||||
|
resptype = protocol.REMOTEIP_RESPONSE
|
||||||
|
ip, err := s.Conn().RemoteMultiaddr().ValueForProtocol(multiaddr.P_IP4)
|
||||||
|
if err != nil {
|
||||||
|
metrics.CountError("remoteip")
|
||||||
|
return resptype, protocol.RemoteIpResponse{Status: protocol.E_INTERNAL_ERROR}, err
|
||||||
|
}
|
||||||
|
return resptype, protocol.RemoteIpResponse{Status: protocol.OK, IP: ip}, nil
|
||||||
default:
|
default:
|
||||||
metrics.CountError("unknown")
|
metrics.CountError("unknown")
|
||||||
// don't send the response
|
// don't send the response
|
||||||
|
|
|
@ -451,7 +451,7 @@ github.com/status-im/migrate/v4/database/postgres
|
||||||
github.com/status-im/migrate/v4/database/sqlcipher
|
github.com/status-im/migrate/v4/database/sqlcipher
|
||||||
github.com/status-im/migrate/v4/internal/url
|
github.com/status-im/migrate/v4/internal/url
|
||||||
github.com/status-im/migrate/v4/source/go_bindata
|
github.com/status-im/migrate/v4/source/go_bindata
|
||||||
# github.com/status-im/rendezvous v1.3.2
|
# github.com/status-im/rendezvous v1.3.1-0.20210824184947-7c79c858170c
|
||||||
github.com/status-im/rendezvous
|
github.com/status-im/rendezvous
|
||||||
github.com/status-im/rendezvous/protocol
|
github.com/status-im/rendezvous/protocol
|
||||||
github.com/status-im/rendezvous/server
|
github.com/status-im/rendezvous/server
|
||||||
|
|
Loading…
Reference in New Issue