Commit edfa3734 authored by Janoš Guljaš's avatar Janoš Guljaš Committed by GitHub

add option to specify static nat address (#311)

parent d42ec589
...@@ -32,6 +32,7 @@ func (c *command) initStartCmd() (err error) { ...@@ -32,6 +32,7 @@ func (c *command) initStartCmd() (err error) {
optionNamePasswordFile = "password-file" optionNamePasswordFile = "password-file"
optionNameAPIAddr = "api-addr" optionNameAPIAddr = "api-addr"
optionNameP2PAddr = "p2p-addr" optionNameP2PAddr = "p2p-addr"
optionNameNATAddr = "nat-addr"
optionNameP2PDisableWS = "p2p-disable-ws" optionNameP2PDisableWS = "p2p-disable-ws"
optionNameP2PDisableQUIC = "p2p-disable-quic" optionNameP2PDisableQUIC = "p2p-disable-quic"
optionNameEnableDebugAPI = "enable-debug-api" optionNameEnableDebugAPI = "enable-debug-api"
...@@ -99,6 +100,7 @@ func (c *command) initStartCmd() (err error) { ...@@ -99,6 +100,7 @@ func (c *command) initStartCmd() (err error) {
APIAddr: c.config.GetString(optionNameAPIAddr), APIAddr: c.config.GetString(optionNameAPIAddr),
DebugAPIAddr: debugAPIAddr, DebugAPIAddr: debugAPIAddr,
Addr: c.config.GetString(optionNameP2PAddr), Addr: c.config.GetString(optionNameP2PAddr),
NATAddr: c.config.GetString(optionNameNATAddr),
DisableWS: c.config.GetBool(optionNameP2PDisableWS), DisableWS: c.config.GetBool(optionNameP2PDisableWS),
DisableQUIC: c.config.GetBool(optionNameP2PDisableQUIC), DisableQUIC: c.config.GetBool(optionNameP2PDisableQUIC),
NetworkID: c.config.GetUint64(optionNameNetworkID), NetworkID: c.config.GetUint64(optionNameNetworkID),
...@@ -157,6 +159,7 @@ func (c *command) initStartCmd() (err error) { ...@@ -157,6 +159,7 @@ func (c *command) initStartCmd() (err error) {
cmd.Flags().String(optionNamePasswordFile, "", "path to a file that contains password for decrypting keys") cmd.Flags().String(optionNamePasswordFile, "", "path to a file that contains password for decrypting keys")
cmd.Flags().String(optionNameAPIAddr, ":8080", "HTTP API listen address") cmd.Flags().String(optionNameAPIAddr, ":8080", "HTTP API listen address")
cmd.Flags().String(optionNameP2PAddr, ":7070", "P2P listen address") cmd.Flags().String(optionNameP2PAddr, ":7070", "P2P listen address")
cmd.Flags().String(optionNameNATAddr, "", "NAT exposed address")
cmd.Flags().Bool(optionNameP2PDisableWS, false, "disable P2P WebSocket protocol") cmd.Flags().Bool(optionNameP2PDisableWS, false, "disable P2P WebSocket protocol")
cmd.Flags().Bool(optionNameP2PDisableQUIC, false, "disable P2P QUIC protocol") cmd.Flags().Bool(optionNameP2PDisableQUIC, false, "disable P2P QUIC protocol")
cmd.Flags().StringSlice(optionNameBootnodes, nil, "initial nodes to connect to") cmd.Flags().StringSlice(optionNameBootnodes, nil, "initial nodes to connect to")
......
...@@ -67,6 +67,7 @@ type Options struct { ...@@ -67,6 +67,7 @@ type Options struct {
APIAddr string APIAddr string
DebugAPIAddr string DebugAPIAddr string
Addr string Addr string
NATAddr string
DisableWS bool DisableWS bool
DisableQUIC bool DisableQUIC bool
NetworkID uint64 NetworkID uint64
...@@ -143,6 +144,7 @@ func NewBee(o Options) (*Bee, error) { ...@@ -143,6 +144,7 @@ func NewBee(o Options) (*Bee, error) {
p2ps, err := libp2p.New(p2pCtx, signer, o.NetworkID, address, o.Addr, libp2p.Options{ p2ps, err := libp2p.New(p2pCtx, signer, o.NetworkID, address, o.Addr, libp2p.Options{
PrivateKey: libp2pPrivateKey, PrivateKey: libp2pPrivateKey,
NATAddr: o.NATAddr,
DisableWS: o.DisableWS, DisableWS: o.DisableWS,
DisableQUIC: o.DisableQUIC, DisableQUIC: o.DisableQUIC,
Addressbook: addressbook, Addressbook: addressbook,
...@@ -154,16 +156,18 @@ func NewBee(o Options) (*Bee, error) { ...@@ -154,16 +156,18 @@ func NewBee(o Options) (*Bee, error) {
} }
b.p2pService = p2ps b.p2pService = p2ps
// wait for nat manager to init if natManager := p2ps.NATManager(); natManager != nil {
logger.Debug("initializing NAT manager") // wait for nat manager to init
select { logger.Debug("initializing NAT manager")
case <-p2ps.NATManager().Ready(): select {
// this is magic sleep to give NAT time to sync the mappings case <-natManager.Ready():
// this is a hack, kind of alchemy and should be improved // this is magic sleep to give NAT time to sync the mappings
time.Sleep(3 * time.Second) // this is a hack, kind of alchemy and should be improved
logger.Debug("NAT manager initialized") time.Sleep(3 * time.Second)
case <-time.After(10 * time.Second): logger.Debug("NAT manager initialized")
logger.Warning("NAT manager init timeout") case <-time.After(10 * time.Second):
logger.Warning("NAT manager init timeout")
}
} }
// Construct protocols. // Construct protocols.
......
...@@ -19,3 +19,7 @@ func (s *Service) HandshakeService() *handshake.Service { ...@@ -19,3 +19,7 @@ func (s *Service) HandshakeService() *handshake.Service {
func (s *Service) NewStreamForPeerID(peerID libp2ppeer.ID, protocolName, protocolVersion, streamName string) (network.Stream, error) { func (s *Service) NewStreamForPeerID(peerID libp2ppeer.ID, protocolName, protocolVersion, streamName string) (network.Stream, error) {
return s.newStreamForPeerID(context.Background(), peerID, protocolName, protocolVersion, streamName) return s.newStreamForPeerID(context.Background(), peerID, protocolName, protocolVersion, streamName)
} }
type StaticAddressResolver = staticAddressResolver
var NewStaticAddressResolver = newStaticAddressResolver
...@@ -61,6 +61,7 @@ type Service struct { ...@@ -61,6 +61,7 @@ type Service struct {
type Options struct { type Options struct {
PrivateKey *ecdsa.PrivateKey PrivateKey *ecdsa.PrivateKey
NATAddr string
DisableWS bool DisableWS bool
DisableQUIC bool DisableQUIC bool
LightNode bool LightNode bool
...@@ -119,14 +120,19 @@ func New(ctx context.Context, signer beecrypto.Signer, networkID uint64, overlay ...@@ -119,14 +120,19 @@ func New(ctx context.Context, signer beecrypto.Signer, networkID uint64, overlay
opts := []libp2p.Option{ opts := []libp2p.Option{
libp2p.ListenAddrStrings(listenAddrs...), libp2p.ListenAddrStrings(listenAddrs...),
security, security,
libp2p.NATManager(func(n network.Network) basichost.NATManager {
natManager = basichost.NewNATManager(n)
return natManager
}),
// Use dedicated peerstore instead the global DefaultPeerstore // Use dedicated peerstore instead the global DefaultPeerstore
libp2p.Peerstore(libp2pPeerstore), libp2p.Peerstore(libp2pPeerstore),
} }
if o.NATAddr == "" {
opts = append(opts,
libp2p.NATManager(func(n network.Network) basichost.NATManager {
natManager = basichost.NewNATManager(n)
return natManager
}),
)
}
if o.PrivateKey != nil { if o.PrivateKey != nil {
opts = append(opts, opts = append(opts,
libp2p.Identity((*crypto.Secp256k1PrivateKey)(o.PrivateKey)), libp2p.Identity((*crypto.Secp256k1PrivateKey)(o.PrivateKey)),
...@@ -163,9 +169,19 @@ func New(ctx context.Context, signer beecrypto.Signer, networkID uint64, overlay ...@@ -163,9 +169,19 @@ func New(ctx context.Context, signer beecrypto.Signer, networkID uint64, overlay
return nil, fmt.Errorf("autonat: %w", err) return nil, fmt.Errorf("autonat: %w", err)
} }
handshakeService, err := handshake.New(signer, &UpnpAddressResolver{ var advertisableAddresser handshake.AdvertisableAddressResolver
host: h, if o.NATAddr == "" {
}, overlay, networkID, o.LightNode, o.Logger) advertisableAddresser = &UpnpAddressResolver{
host: h,
}
} else {
advertisableAddresser, err = newStaticAddressResolver(o.NATAddr)
if err != nil {
return nil, fmt.Errorf("static nat: %w", err)
}
}
handshakeService, err := handshake.New(signer, advertisableAddresser, overlay, networkID, o.LightNode, o.Logger)
if err != nil { if err != nil {
return nil, fmt.Errorf("handshake service: %w", err) return nil, fmt.Errorf("handshake service: %w", err)
} }
......
// Copyright 2020 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package libp2p
import (
"errors"
"fmt"
"net"
"strings"
"github.com/ethersphere/bee/pkg/p2p/libp2p/internal/handshake"
libp2ppeer "github.com/libp2p/go-libp2p-core/peer"
ma "github.com/multiformats/go-multiaddr"
)
type staticAddressResolver struct {
ipProto string
port string
}
func newStaticAddressResolver(addr string) (handshake.AdvertisableAddressResolver, error) {
host, port, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}
var ipProto string
if host != "" {
ip := net.ParseIP(host)
if ip == nil {
return nil, fmt.Errorf("invalid IP %q", host)
}
if ip.To4() != nil {
ipProto = "/ip4/" + ip.String()
} else {
ipProto = "/ip6/" + ip.String()
}
}
return &staticAddressResolver{
ipProto: ipProto,
port: port,
}, nil
}
func (r *staticAddressResolver) Resolve(observedAddress ma.Multiaddr) (ma.Multiaddr, error) {
observableAddrInfo, err := libp2ppeer.AddrInfoFromP2pAddr(observedAddress)
if err != nil {
return nil, err
}
if len(observableAddrInfo.Addrs) < 1 {
return nil, errors.New("invalid observed address")
}
observedAddrSplit := strings.Split(observableAddrInfo.Addrs[0].String(), "/")
// if address is not in a form of '/ipversion/ip/protocol/port/...` don't compare to addresses and return it
if len(observedAddrSplit) < 5 {
return observedAddress, nil
}
var ipProto string
if r.ipProto != "" {
ipProto = r.ipProto
} else {
ipProto = strings.Join(observedAddrSplit[:3], "/")
}
var port string
if r.port != "" {
port = r.port
} else {
port = observedAddrSplit[4]
}
a, err := ma.NewMultiaddr(ipProto + "/" + observedAddrSplit[3] + "/" + port)
if err != nil {
return nil, err
}
return buildUnderlayAddress(a, observableAddrInfo.ID)
}
// Copyright 2020 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package libp2p_test
import (
"testing"
"github.com/ethersphere/bee/pkg/p2p/libp2p"
ma "github.com/multiformats/go-multiaddr"
)
func TestStaticAddressResolver(t *testing.T) {
for _, tc := range []struct {
name string
natAddr string
observableAddress string
want string
}{
{
name: "replace port",
natAddr: ":30123",
observableAddress: "/ip4/127.0.0.1/tcp/7071/p2p/16Uiu2HAkyyGKpjBiCkVqCKoJa6RzzZw9Nr7hGogsMPcdad1KyMmd",
want: "/ip4/127.0.0.1/tcp/30123/p2p/16Uiu2HAkyyGKpjBiCkVqCKoJa6RzzZw9Nr7hGogsMPcdad1KyMmd",
},
{
name: "replace ip v4",
natAddr: "192.168.1.34:",
observableAddress: "/ip4/127.0.0.1/tcp/7071/p2p/16Uiu2HAkyyGKpjBiCkVqCKoJa6RzzZw9Nr7hGogsMPcdad1KyMmd",
want: "/ip4/192.168.1.34/tcp/7071/p2p/16Uiu2HAkyyGKpjBiCkVqCKoJa6RzzZw9Nr7hGogsMPcdad1KyMmd",
},
{
name: "replace ip v6",
natAddr: "[2001:db8::8a2e:370:1111]:",
observableAddress: "/ip6/2001:db8::8a2e:370:7334/tcp/7071/p2p/16Uiu2HAkyyGKpjBiCkVqCKoJa6RzzZw9Nr7hGogsMPcdad1KyMmd",
want: "/ip6/2001:db8::8a2e:370:1111/tcp/7071/p2p/16Uiu2HAkyyGKpjBiCkVqCKoJa6RzzZw9Nr7hGogsMPcdad1KyMmd",
},
{
name: "replace ip v4 with ip v6",
natAddr: "[2001:db8::8a2e:370:1111]:",
observableAddress: "/ip4/127.0.0.1/tcp/7071/p2p/16Uiu2HAkyyGKpjBiCkVqCKoJa6RzzZw9Nr7hGogsMPcdad1KyMmd",
want: "/ip6/2001:db8::8a2e:370:1111/tcp/7071/p2p/16Uiu2HAkyyGKpjBiCkVqCKoJa6RzzZw9Nr7hGogsMPcdad1KyMmd",
},
{
name: "replace ip v6 with ip v4",
natAddr: "192.168.1.34:",
observableAddress: "/ip6/2001:db8::8a2e:370:7334/tcp/7071/p2p/16Uiu2HAkyyGKpjBiCkVqCKoJa6RzzZw9Nr7hGogsMPcdad1KyMmd",
want: "/ip4/192.168.1.34/tcp/7071/p2p/16Uiu2HAkyyGKpjBiCkVqCKoJa6RzzZw9Nr7hGogsMPcdad1KyMmd",
},
{
name: "replace ip and port",
natAddr: "192.168.1.34:30777",
observableAddress: "/ip4/127.0.0.1/tcp/7071/p2p/16Uiu2HAkyyGKpjBiCkVqCKoJa6RzzZw9Nr7hGogsMPcdad1KyMmd",
want: "/ip4/192.168.1.34/tcp/30777/p2p/16Uiu2HAkyyGKpjBiCkVqCKoJa6RzzZw9Nr7hGogsMPcdad1KyMmd",
},
{
name: "replace ip v4 and port with ip v6",
natAddr: "[2001:db8::8a2e:370:1111]:30777",
observableAddress: "/ip4/127.0.0.1/tcp/7071/p2p/16Uiu2HAkyyGKpjBiCkVqCKoJa6RzzZw9Nr7hGogsMPcdad1KyMmd",
want: "/ip6/2001:db8::8a2e:370:1111/tcp/30777/p2p/16Uiu2HAkyyGKpjBiCkVqCKoJa6RzzZw9Nr7hGogsMPcdad1KyMmd",
},
{
name: "replace ip v6 and port with ip v4",
natAddr: "192.168.1.34:30777",
observableAddress: "/ip6/2001:db8::8a2e:370:7334/tcp/7071/p2p/16Uiu2HAkyyGKpjBiCkVqCKoJa6RzzZw9Nr7hGogsMPcdad1KyMmd",
want: "/ip4/192.168.1.34/tcp/30777/p2p/16Uiu2HAkyyGKpjBiCkVqCKoJa6RzzZw9Nr7hGogsMPcdad1KyMmd",
},
} {
t.Run(tc.name, func(t *testing.T) {
r, err := libp2p.NewStaticAddressResolver(tc.natAddr)
if err != nil {
t.Fatal(err)
}
observableAddress, err := ma.NewMultiaddr(tc.observableAddress)
if err != nil {
t.Fatal(err)
}
got, err := r.Resolve(observableAddress)
if err != nil {
t.Fatal(err)
}
if got.String() != tc.want {
t.Errorf("got %s, want %s", got, tc.want)
}
})
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment