mirror of
https://github.com/libp2p/go-libp2p.git
synced 2025-09-27 12:32:26 +08:00
442 lines
15 KiB
Go
442 lines
15 KiB
Go
package basichost
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/libp2p/go-libp2p/core/event"
|
|
"github.com/libp2p/go-libp2p/core/network"
|
|
"github.com/libp2p/go-libp2p/p2p/host/eventbus"
|
|
ma "github.com/multiformats/go-multiaddr"
|
|
manet "github.com/multiformats/go-multiaddr/net"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestAppendNATAddrs(t *testing.T) {
|
|
if1, if2 := ma.StringCast("/ip4/192.168.0.100"), ma.StringCast("/ip4/1.1.1.1")
|
|
ifaceAddrs := []ma.Multiaddr{if1, if2}
|
|
tcpListenAddr, udpListenAddr := ma.StringCast("/ip4/0.0.0.0/tcp/1"), ma.StringCast("/ip4/0.0.0.0/udp/2/quic-v1")
|
|
cases := []struct {
|
|
Name string
|
|
Listen ma.Multiaddr
|
|
Nat ma.Multiaddr
|
|
ObsAddrFunc func(ma.Multiaddr) []ma.Multiaddr
|
|
Expected []ma.Multiaddr
|
|
}{
|
|
{
|
|
Name: "nat map success",
|
|
// nat mapping success, obsaddress ignored
|
|
Listen: ma.StringCast("/ip4/0.0.0.0/udp/1/quic-v1"),
|
|
Nat: ma.StringCast("/ip4/1.1.1.1/udp/10/quic-v1"),
|
|
ObsAddrFunc: func(_ ma.Multiaddr) []ma.Multiaddr {
|
|
return []ma.Multiaddr{ma.StringCast("/ip4/2.2.2.2/udp/100/quic-v1")}
|
|
},
|
|
Expected: []ma.Multiaddr{ma.StringCast("/ip4/1.1.1.1/udp/10/quic-v1")},
|
|
},
|
|
{
|
|
Name: "nat map failure",
|
|
// nat mapping fails, obs addresses added
|
|
Listen: ma.StringCast("/ip4/0.0.0.0/tcp/1"),
|
|
Nat: nil,
|
|
ObsAddrFunc: func(a ma.Multiaddr) []ma.Multiaddr {
|
|
ipC, _ := ma.SplitFirst(a)
|
|
ip := ipC.Multiaddr()
|
|
switch {
|
|
case ip.Equal(if1):
|
|
return []ma.Multiaddr{ma.StringCast("/ip4/2.2.2.2/tcp/100")}
|
|
case ip.Equal(if2):
|
|
return []ma.Multiaddr{ma.StringCast("/ip4/3.3.3.3/tcp/100")}
|
|
default:
|
|
return []ma.Multiaddr{}
|
|
}
|
|
},
|
|
Expected: []ma.Multiaddr{ma.StringCast("/ip4/2.2.2.2/tcp/100"), ma.StringCast("/ip4/3.3.3.3/tcp/100")},
|
|
},
|
|
{
|
|
Name: "if addrs ignored if not listening on unspecified",
|
|
// nat mapping fails, obs addresses added
|
|
Listen: ma.StringCast("/ip4/192.168.1.1/tcp/1"),
|
|
Nat: nil,
|
|
ObsAddrFunc: func(a ma.Multiaddr) []ma.Multiaddr {
|
|
ipC, _ := ma.SplitFirst(a)
|
|
ip := ipC.Multiaddr()
|
|
switch {
|
|
case ip.Equal(if1):
|
|
return []ma.Multiaddr{ma.StringCast("/ip4/2.2.2.2/tcp/100")}
|
|
case ip.Equal(if2):
|
|
return []ma.Multiaddr{ma.StringCast("/ip4/3.3.3.3/tcp/100")}
|
|
case ip.Equal(ma.StringCast("/ip4/192.168.1.1")):
|
|
return []ma.Multiaddr{ma.StringCast("/ip4/4.4.4.4/tcp/100")}
|
|
default:
|
|
return []ma.Multiaddr{}
|
|
}
|
|
},
|
|
Expected: []ma.Multiaddr{ma.StringCast("/ip4/4.4.4.4/tcp/100")},
|
|
},
|
|
{
|
|
Name: "nat map success but CGNAT",
|
|
// nat addr added, obs address added with nat provided port
|
|
Listen: tcpListenAddr,
|
|
Nat: ma.StringCast("/ip4/100.100.1.1/tcp/100"),
|
|
ObsAddrFunc: func(a ma.Multiaddr) []ma.Multiaddr {
|
|
ipC, _ := ma.SplitFirst(a)
|
|
ip := ipC.Multiaddr()
|
|
if ip.Equal(if1) {
|
|
return []ma.Multiaddr{ma.StringCast("/ip4/2.2.2.2/tcp/20")}
|
|
}
|
|
return []ma.Multiaddr{ma.StringCast("/ip4/3.3.3.3/tcp/30")}
|
|
},
|
|
Expected: []ma.Multiaddr{
|
|
ma.StringCast("/ip4/100.100.1.1/tcp/100"),
|
|
ma.StringCast("/ip4/2.2.2.2/tcp/20"),
|
|
ma.StringCast("/ip4/3.3.3.3/tcp/30"),
|
|
},
|
|
},
|
|
{
|
|
Name: "uses unspecified address for obs address",
|
|
// observed address manager should be queries with both specified and unspecified addresses
|
|
// udp observed addresses are mapped to unspecified addresses
|
|
Listen: udpListenAddr,
|
|
Nat: nil,
|
|
ObsAddrFunc: func(a ma.Multiaddr) []ma.Multiaddr {
|
|
if manet.IsIPUnspecified(a) {
|
|
return []ma.Multiaddr{ma.StringCast("/ip4/3.3.3.3/udp/20/quic-v1")}
|
|
}
|
|
return []ma.Multiaddr{ma.StringCast("/ip4/2.2.2.2/udp/20/quic-v1")}
|
|
},
|
|
Expected: []ma.Multiaddr{
|
|
ma.StringCast("/ip4/2.2.2.2/udp/20/quic-v1"),
|
|
ma.StringCast("/ip4/3.3.3.3/udp/20/quic-v1"),
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.Name, func(t *testing.T) {
|
|
as := &addrsManager{
|
|
natManager: &mockNatManager{
|
|
GetMappingFunc: func(_ ma.Multiaddr) ma.Multiaddr {
|
|
return tc.Nat
|
|
},
|
|
},
|
|
observedAddrsManager: &mockObservedAddrs{
|
|
ObservedAddrsForFunc: tc.ObsAddrFunc,
|
|
},
|
|
}
|
|
res := as.appendNATAddrs(nil, []ma.Multiaddr{tc.Listen}, ifaceAddrs)
|
|
res = ma.Unique(res)
|
|
require.ElementsMatch(t, tc.Expected, res, "%s\n%s", tc.Expected, res)
|
|
})
|
|
}
|
|
}
|
|
|
|
type mockNatManager struct {
|
|
GetMappingFunc func(addr ma.Multiaddr) ma.Multiaddr
|
|
}
|
|
|
|
func (m *mockNatManager) Close() error {
|
|
return nil
|
|
}
|
|
|
|
func (m *mockNatManager) GetMapping(addr ma.Multiaddr) ma.Multiaddr {
|
|
if m.GetMappingFunc == nil {
|
|
return nil
|
|
}
|
|
return m.GetMappingFunc(addr)
|
|
}
|
|
|
|
func (m *mockNatManager) HasDiscoveredNAT() bool {
|
|
return true
|
|
}
|
|
|
|
var _ NATManager = &mockNatManager{}
|
|
|
|
type mockObservedAddrs struct {
|
|
OwnObservedAddrsFunc func() []ma.Multiaddr
|
|
ObservedAddrsForFunc func(ma.Multiaddr) []ma.Multiaddr
|
|
}
|
|
|
|
func (m *mockObservedAddrs) OwnObservedAddrs() []ma.Multiaddr {
|
|
return m.OwnObservedAddrsFunc()
|
|
}
|
|
|
|
func (m *mockObservedAddrs) ObservedAddrsFor(local ma.Multiaddr) []ma.Multiaddr {
|
|
return m.ObservedAddrsForFunc(local)
|
|
}
|
|
|
|
type addrsManagerArgs struct {
|
|
NATManager NATManager
|
|
AddrsFactory AddrsFactory
|
|
ObservedAddrsManager observedAddrsManager
|
|
ListenAddrs func() []ma.Multiaddr
|
|
}
|
|
|
|
type addrsManagerTestCase struct {
|
|
*addrsManager
|
|
PushRelay func(relayAddrs []ma.Multiaddr)
|
|
PushReachability func(rch network.Reachability)
|
|
}
|
|
|
|
func newAddrsManagerTestCase(t *testing.T, args addrsManagerArgs) addrsManagerTestCase {
|
|
eb := eventbus.NewBus()
|
|
if args.AddrsFactory == nil {
|
|
args.AddrsFactory = func(addrs []ma.Multiaddr) []ma.Multiaddr { return addrs }
|
|
}
|
|
addrsUpdatedChan := make(chan struct{}, 1)
|
|
am, err := newAddrsManager(
|
|
eb, args.NATManager, args.AddrsFactory, args.ListenAddrs, nil, args.ObservedAddrsManager, addrsUpdatedChan,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, am.Start())
|
|
raEm, err := eb.Emitter(new(event.EvtAutoRelayAddrsUpdated), eventbus.Stateful)
|
|
require.NoError(t, err)
|
|
|
|
rchEm, err := eb.Emitter(new(event.EvtLocalReachabilityChanged), eventbus.Stateful)
|
|
require.NoError(t, err)
|
|
|
|
return addrsManagerTestCase{
|
|
addrsManager: am,
|
|
PushRelay: func(relayAddrs []ma.Multiaddr) {
|
|
err := raEm.Emit(event.EvtAutoRelayAddrsUpdated{RelayAddrs: relayAddrs})
|
|
require.NoError(t, err)
|
|
},
|
|
PushReachability: func(rch network.Reachability) {
|
|
err := rchEm.Emit(event.EvtLocalReachabilityChanged{Reachability: rch})
|
|
require.NoError(t, err)
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestAddrsManager(t *testing.T) {
|
|
lhquic := ma.StringCast("/ip4/127.0.0.1/udp/1/quic-v1")
|
|
lhtcp := ma.StringCast("/ip4/127.0.0.1/tcp/1")
|
|
|
|
publicQUIC := ma.StringCast("/ip4/1.2.3.4/udp/1/quic-v1")
|
|
publicTCP := ma.StringCast("/ip4/1.2.3.4/tcp/1")
|
|
|
|
t.Run("only nat", func(t *testing.T) {
|
|
am := newAddrsManagerTestCase(t, addrsManagerArgs{
|
|
NATManager: &mockNatManager{
|
|
GetMappingFunc: func(addr ma.Multiaddr) ma.Multiaddr {
|
|
if _, err := addr.ValueForProtocol(ma.P_UDP); err == nil {
|
|
return publicQUIC
|
|
}
|
|
return nil
|
|
},
|
|
},
|
|
ListenAddrs: func() []ma.Multiaddr { return []ma.Multiaddr{lhquic, lhtcp} },
|
|
})
|
|
am.triggerAddrsUpdate()
|
|
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
|
expected := []ma.Multiaddr{publicQUIC, lhquic, lhtcp}
|
|
assert.ElementsMatch(collect, am.Addrs(), expected, "%s\n%s", am.Addrs(), expected)
|
|
}, 5*time.Second, 50*time.Millisecond)
|
|
})
|
|
|
|
t.Run("nat and observed addrs", func(t *testing.T) {
|
|
// nat mapping for udp, observed addrs for tcp
|
|
am := newAddrsManagerTestCase(t, addrsManagerArgs{
|
|
NATManager: &mockNatManager{
|
|
GetMappingFunc: func(addr ma.Multiaddr) ma.Multiaddr {
|
|
if _, err := addr.ValueForProtocol(ma.P_UDP); err == nil {
|
|
return publicQUIC
|
|
}
|
|
return nil
|
|
},
|
|
},
|
|
ObservedAddrsManager: &mockObservedAddrs{
|
|
ObservedAddrsForFunc: func(addr ma.Multiaddr) []ma.Multiaddr {
|
|
if _, err := addr.ValueForProtocol(ma.P_TCP); err == nil {
|
|
return []ma.Multiaddr{publicTCP}
|
|
}
|
|
return nil
|
|
},
|
|
},
|
|
ListenAddrs: func() []ma.Multiaddr { return []ma.Multiaddr{lhquic, lhtcp} },
|
|
})
|
|
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
|
expected := []ma.Multiaddr{lhquic, lhtcp, publicQUIC, publicTCP}
|
|
assert.ElementsMatch(collect, am.Addrs(), expected, "%s\n%s", am.Addrs(), expected)
|
|
}, 5*time.Second, 50*time.Millisecond)
|
|
})
|
|
t.Run("nat returns unspecified addr", func(t *testing.T) {
|
|
quicPort1 := ma.StringCast("/ip4/3.3.3.3/udp/1/quic-v1")
|
|
quicPort2 := ma.StringCast("/ip4/3.3.3.3/udp/2/quic-v1")
|
|
// port from nat, IP from observed addr
|
|
am := newAddrsManagerTestCase(t, addrsManagerArgs{
|
|
NATManager: &mockNatManager{
|
|
GetMappingFunc: func(addr ma.Multiaddr) ma.Multiaddr {
|
|
if addr.Equal(lhquic) {
|
|
return ma.StringCast("/ip4/0.0.0.0/udp/2/quic-v1")
|
|
}
|
|
return nil
|
|
},
|
|
},
|
|
ObservedAddrsManager: &mockObservedAddrs{
|
|
ObservedAddrsForFunc: func(addr ma.Multiaddr) []ma.Multiaddr {
|
|
if addr.Equal(lhquic) {
|
|
return []ma.Multiaddr{quicPort1}
|
|
}
|
|
return nil
|
|
},
|
|
},
|
|
ListenAddrs: func() []ma.Multiaddr { return []ma.Multiaddr{lhquic} },
|
|
})
|
|
expected := []ma.Multiaddr{lhquic, quicPort2}
|
|
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
|
assert.ElementsMatch(collect, am.Addrs(), expected, "%s\n%s", am.Addrs(), expected)
|
|
}, 5*time.Second, 50*time.Millisecond)
|
|
})
|
|
t.Run("only observed addrs", func(t *testing.T) {
|
|
am := newAddrsManagerTestCase(t, addrsManagerArgs{
|
|
ObservedAddrsManager: &mockObservedAddrs{
|
|
ObservedAddrsForFunc: func(addr ma.Multiaddr) []ma.Multiaddr {
|
|
if addr.Equal(lhtcp) {
|
|
return []ma.Multiaddr{publicTCP}
|
|
}
|
|
if addr.Equal(lhquic) {
|
|
return []ma.Multiaddr{publicQUIC}
|
|
}
|
|
return nil
|
|
},
|
|
},
|
|
ListenAddrs: func() []ma.Multiaddr { return []ma.Multiaddr{lhquic, lhtcp} },
|
|
})
|
|
am.triggerAddrsUpdate()
|
|
expected := []ma.Multiaddr{lhquic, lhtcp, publicTCP, publicQUIC}
|
|
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
|
assert.ElementsMatch(collect, am.Addrs(), expected, "%s\n%s", am.Addrs(), expected)
|
|
}, 5*time.Second, 50*time.Millisecond)
|
|
})
|
|
|
|
t.Run("observed addrs limit", func(t *testing.T) {
|
|
quicAddrs := []ma.Multiaddr{
|
|
ma.StringCast("/ip4/1.2.3.4/udp/1/quic-v1"),
|
|
ma.StringCast("/ip4/1.2.3.4/udp/2/quic-v1"),
|
|
ma.StringCast("/ip4/1.2.3.4/udp/3/quic-v1"),
|
|
ma.StringCast("/ip4/1.2.3.4/udp/4/quic-v1"),
|
|
ma.StringCast("/ip4/1.2.3.4/udp/5/quic-v1"),
|
|
ma.StringCast("/ip4/1.2.3.4/udp/6/quic-v1"),
|
|
ma.StringCast("/ip4/1.2.3.4/udp/7/quic-v1"),
|
|
ma.StringCast("/ip4/1.2.3.4/udp/8/quic-v1"),
|
|
ma.StringCast("/ip4/1.2.3.4/udp/9/quic-v1"),
|
|
ma.StringCast("/ip4/1.2.3.4/udp/10/quic-v1"),
|
|
}
|
|
am := newAddrsManagerTestCase(t, addrsManagerArgs{
|
|
ObservedAddrsManager: &mockObservedAddrs{
|
|
ObservedAddrsForFunc: func(_ ma.Multiaddr) []ma.Multiaddr {
|
|
return quicAddrs
|
|
},
|
|
},
|
|
ListenAddrs: func() []ma.Multiaddr { return []ma.Multiaddr{lhquic} },
|
|
})
|
|
am.triggerAddrsUpdate()
|
|
expected := []ma.Multiaddr{lhquic}
|
|
expected = append(expected, quicAddrs[:maxObservedAddrsPerListenAddr]...)
|
|
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
|
assert.ElementsMatch(collect, am.Addrs(), expected, "%s\n%s", am.Addrs(), expected)
|
|
}, 5*time.Second, 50*time.Millisecond)
|
|
})
|
|
t.Run("public addrs removed when private", func(t *testing.T) {
|
|
am := newAddrsManagerTestCase(t, addrsManagerArgs{
|
|
ObservedAddrsManager: &mockObservedAddrs{
|
|
ObservedAddrsForFunc: func(_ ma.Multiaddr) []ma.Multiaddr {
|
|
return []ma.Multiaddr{publicQUIC}
|
|
},
|
|
},
|
|
ListenAddrs: func() []ma.Multiaddr { return []ma.Multiaddr{lhquic, lhtcp} },
|
|
})
|
|
|
|
// remove public addrs
|
|
am.PushReachability(network.ReachabilityPrivate)
|
|
relayAddr := ma.StringCast("/ip4/1.2.3.4/udp/1/quic-v1/p2p/QmdXGaeGiVA745XorV1jr11RHxB9z4fqykm6xCUPX1aTJo/p2p-circuit")
|
|
am.PushRelay([]ma.Multiaddr{relayAddr})
|
|
|
|
expectedAddrs := []ma.Multiaddr{relayAddr, lhquic, lhtcp}
|
|
expectedAllAddrs := []ma.Multiaddr{publicQUIC, lhquic, lhtcp}
|
|
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
|
assert.ElementsMatch(collect, am.Addrs(), expectedAddrs, "%s\n%s", am.Addrs(), expectedAddrs)
|
|
assert.ElementsMatch(collect, am.DirectAddrs(), expectedAllAddrs, "%s\n%s", am.DirectAddrs(), expectedAllAddrs)
|
|
}, 5*time.Second, 50*time.Millisecond)
|
|
|
|
// add public addrs
|
|
am.PushReachability(network.ReachabilityPublic)
|
|
|
|
expectedAddrs = expectedAllAddrs
|
|
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
|
assert.ElementsMatch(collect, am.Addrs(), expectedAddrs, "%s\n%s", am.Addrs(), expectedAddrs)
|
|
assert.ElementsMatch(collect, am.DirectAddrs(), expectedAllAddrs, "%s\n%s", am.DirectAddrs(), expectedAllAddrs)
|
|
}, 5*time.Second, 50*time.Millisecond)
|
|
})
|
|
|
|
t.Run("addrs factory gets relay addrs", func(t *testing.T) {
|
|
relayAddr := ma.StringCast("/ip4/1.2.3.4/udp/1/quic-v1/p2p/QmdXGaeGiVA745XorV1jr11RHxB9z4fqykm6xCUPX1aTJo/p2p-circuit")
|
|
publicQUIC2 := ma.StringCast("/ip4/1.2.3.4/udp/2/quic-v1")
|
|
am := newAddrsManagerTestCase(t, addrsManagerArgs{
|
|
AddrsFactory: func(addrs []ma.Multiaddr) []ma.Multiaddr {
|
|
for _, a := range addrs {
|
|
if a.Equal(relayAddr) {
|
|
return []ma.Multiaddr{publicQUIC2}
|
|
}
|
|
}
|
|
return nil
|
|
},
|
|
ObservedAddrsManager: &mockObservedAddrs{
|
|
ObservedAddrsForFunc: func(_ ma.Multiaddr) []ma.Multiaddr {
|
|
return []ma.Multiaddr{publicQUIC}
|
|
},
|
|
},
|
|
ListenAddrs: func() []ma.Multiaddr { return []ma.Multiaddr{lhquic, lhtcp} },
|
|
})
|
|
am.PushReachability(network.ReachabilityPrivate)
|
|
am.PushRelay([]ma.Multiaddr{relayAddr})
|
|
|
|
expectedAddrs := []ma.Multiaddr{publicQUIC2}
|
|
expectedAllAddrs := []ma.Multiaddr{publicQUIC, lhquic, lhtcp}
|
|
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
|
assert.ElementsMatch(collect, am.Addrs(), expectedAddrs, "%s\n%s", am.Addrs(), expectedAddrs)
|
|
assert.ElementsMatch(collect, am.DirectAddrs(), expectedAllAddrs, "%s\n%s", am.DirectAddrs(), expectedAllAddrs)
|
|
}, 5*time.Second, 50*time.Millisecond)
|
|
})
|
|
|
|
t.Run("updates addresses on signaling", func(t *testing.T) {
|
|
updateChan := make(chan struct{})
|
|
am := newAddrsManagerTestCase(t, addrsManagerArgs{
|
|
AddrsFactory: func(_ []ma.Multiaddr) []ma.Multiaddr {
|
|
select {
|
|
case <-updateChan:
|
|
return []ma.Multiaddr{publicQUIC}
|
|
default:
|
|
return []ma.Multiaddr{publicTCP}
|
|
}
|
|
},
|
|
ListenAddrs: func() []ma.Multiaddr { return []ma.Multiaddr{lhquic, lhtcp} },
|
|
})
|
|
require.Contains(t, am.Addrs(), publicTCP)
|
|
require.NotContains(t, am.Addrs(), publicQUIC)
|
|
close(updateChan)
|
|
am.triggerAddrsUpdate()
|
|
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
|
assert.Contains(collect, am.Addrs(), publicQUIC)
|
|
assert.NotContains(collect, am.Addrs(), publicTCP)
|
|
}, 1*time.Second, 50*time.Millisecond)
|
|
})
|
|
}
|
|
|
|
func BenchmarkAreAddrsDifferent(b *testing.B) {
|
|
var addrs [10]ma.Multiaddr
|
|
for i := 0; i < len(addrs); i++ {
|
|
addrs[i] = ma.StringCast(fmt.Sprintf("/ip4/1.1.1.%d/tcp/1", i))
|
|
}
|
|
am := &addrsManager{}
|
|
b.Run("areAddrsDifferent", func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
am.areAddrsDifferent(addrs[:], addrs[:])
|
|
}
|
|
})
|
|
}
|