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) {
optionNamePasswordFile = "password-file"
optionNameAPIAddr = "api-addr"
optionNameP2PAddr = "p2p-addr"
optionNameNATAddr = "nat-addr"
optionNameP2PDisableWS = "p2p-disable-ws"
optionNameP2PDisableQUIC = "p2p-disable-quic"
optionNameEnableDebugAPI = "enable-debug-api"
......@@ -99,6 +100,7 @@ func (c *command) initStartCmd() (err error) {
APIAddr: c.config.GetString(optionNameAPIAddr),
DebugAPIAddr: debugAPIAddr,
Addr: c.config.GetString(optionNameP2PAddr),
NATAddr: c.config.GetString(optionNameNATAddr),
DisableWS: c.config.GetBool(optionNameP2PDisableWS),
DisableQUIC: c.config.GetBool(optionNameP2PDisableQUIC),
NetworkID: c.config.GetUint64(optionNameNetworkID),
......@@ -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(optionNameAPIAddr, ":8080", "HTTP API 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(optionNameP2PDisableQUIC, false, "disable P2P QUIC protocol")
cmd.Flags().StringSlice(optionNameBootnodes, nil, "initial nodes to connect to")
......
......@@ -67,6 +67,7 @@ type Options struct {
APIAddr string
DebugAPIAddr string
Addr string
NATAddr string
DisableWS bool
DisableQUIC bool
NetworkID uint64
......@@ -143,6 +144,7 @@ func NewBee(o Options) (*Bee, error) {
p2ps, err := libp2p.New(p2pCtx, signer, o.NetworkID, address, o.Addr, libp2p.Options{
PrivateKey: libp2pPrivateKey,
NATAddr: o.NATAddr,
DisableWS: o.DisableWS,
DisableQUIC: o.DisableQUIC,
Addressbook: addressbook,
......@@ -154,16 +156,18 @@ func NewBee(o Options) (*Bee, error) {
}
b.p2pService = p2ps
// wait for nat manager to init
logger.Debug("initializing NAT manager")
select {
case <-p2ps.NATManager().Ready():
// this is magic sleep to give NAT time to sync the mappings
// this is a hack, kind of alchemy and should be improved
time.Sleep(3 * time.Second)
logger.Debug("NAT manager initialized")
case <-time.After(10 * time.Second):
logger.Warning("NAT manager init timeout")
if natManager := p2ps.NATManager(); natManager != nil {
// wait for nat manager to init
logger.Debug("initializing NAT manager")
select {
case <-natManager.Ready():
// this is magic sleep to give NAT time to sync the mappings
// this is a hack, kind of alchemy and should be improved
time.Sleep(3 * time.Second)
logger.Debug("NAT manager initialized")
case <-time.After(10 * time.Second):
logger.Warning("NAT manager init timeout")
}
}
// Construct protocols.
......
......@@ -19,3 +19,7 @@ func (s *Service) HandshakeService() *handshake.Service {
func (s *Service) NewStreamForPeerID(peerID libp2ppeer.ID, protocolName, protocolVersion, streamName string) (network.Stream, error) {
return s.newStreamForPeerID(context.Background(), peerID, protocolName, protocolVersion, streamName)
}
type StaticAddressResolver = staticAddressResolver
var NewStaticAddressResolver = newStaticAddressResolver
......@@ -61,6 +61,7 @@ type Service struct {
type Options struct {
PrivateKey *ecdsa.PrivateKey
NATAddr string
DisableWS bool
DisableQUIC bool
LightNode bool
......@@ -119,14 +120,19 @@ func New(ctx context.Context, signer beecrypto.Signer, networkID uint64, overlay
opts := []libp2p.Option{
libp2p.ListenAddrStrings(listenAddrs...),
security,
libp2p.NATManager(func(n network.Network) basichost.NATManager {
natManager = basichost.NewNATManager(n)
return natManager
}),
// Use dedicated peerstore instead the global DefaultPeerstore
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 {
opts = append(opts,
libp2p.Identity((*crypto.Secp256k1PrivateKey)(o.PrivateKey)),
......@@ -163,9 +169,19 @@ func New(ctx context.Context, signer beecrypto.Signer, networkID uint64, overlay
return nil, fmt.Errorf("autonat: %w", err)
}
handshakeService, err := handshake.New(signer, &UpnpAddressResolver{
host: h,
}, overlay, networkID, o.LightNode, o.Logger)
var advertisableAddresser handshake.AdvertisableAddressResolver
if o.NATAddr == "" {
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 {
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