mirror of
https://github.com/libp2p/go-libp2p.git
synced 2025-10-05 08:07:18 +08:00

This introduces addrsReachabilityTracker that tracks reachability on a set of addresses. It probes reachability for addresses periodically and has an exponential backoff in case there are too many errors or we don't have any valid autonatv2 peer. There's no smartness in the address selection logic currently. We just test all provided addresses. It also doesn't use the addresses provided by `AddrsFactory`, so currently there's no way to get a user provided address tested for reachability, something that would be a problem for dns addresses. I intend to introduce an alternative to `AddrsFactory`, something like, `AnnounceAddrs(addrs []ma.Multiaddr)` that's just appended to the set of addresses that we have, and check reachability for those addresses. There's only one method exposed in the BasicHost right now that's `ReachableAddrs() []ma.Multiadd`r that returns the host's reachable addrs. Users can also use the event `EvtHostReachableAddrsChanged` to be notified when any addrs reachability changes.
530 lines
18 KiB
Go
530 lines
18 KiB
Go
package basichost
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"slices"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/libp2p/go-libp2p/core/event"
|
|
"github.com/libp2p/go-libp2p/core/network"
|
|
"github.com/libp2p/go-libp2p/p2p/host/eventbus"
|
|
"github.com/libp2p/go-libp2p/p2p/protocol/autonatv2"
|
|
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 (*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 (*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
|
|
AutoNATClient autonatv2Client
|
|
Bus event.Bus
|
|
}
|
|
|
|
type addrsManagerTestCase struct {
|
|
*addrsManager
|
|
PushRelay func(relayAddrs []ma.Multiaddr)
|
|
PushReachability func(rch network.Reachability)
|
|
}
|
|
|
|
func newAddrsManagerTestCase(t *testing.T, args addrsManagerArgs) addrsManagerTestCase {
|
|
eb := args.Bus
|
|
if eb == nil {
|
|
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, args.AutoNATClient,
|
|
)
|
|
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)
|
|
|
|
t.Cleanup(am.Close)
|
|
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 TestAddrsManagerReachabilityEvent(t *testing.T) {
|
|
publicQUIC, _ := ma.NewMultiaddr("/ip4/1.2.3.4/udp/1234/quic-v1")
|
|
publicQUIC2, _ := ma.NewMultiaddr("/ip4/1.2.3.4/udp/1235/quic-v1")
|
|
publicTCP, _ := ma.NewMultiaddr("/ip4/1.2.3.4/tcp/1234")
|
|
|
|
bus := eventbus.NewBus()
|
|
|
|
sub, err := bus.Subscribe(new(event.EvtHostReachableAddrsChanged))
|
|
require.NoError(t, err)
|
|
defer sub.Close()
|
|
|
|
am := newAddrsManagerTestCase(t, addrsManagerArgs{
|
|
Bus: bus,
|
|
// currently they aren't being passed to the reachability tracker
|
|
ListenAddrs: func() []ma.Multiaddr { return []ma.Multiaddr{publicQUIC, publicQUIC2, publicTCP} },
|
|
AutoNATClient: mockAutoNATClient{
|
|
F: func(_ context.Context, reqs []autonatv2.Request) (autonatv2.Result, error) {
|
|
if reqs[0].Addr.Equal(publicQUIC) {
|
|
return autonatv2.Result{Addr: reqs[0].Addr, Idx: 0, Reachability: network.ReachabilityPublic}, nil
|
|
} else if reqs[0].Addr.Equal(publicTCP) || reqs[0].Addr.Equal(publicQUIC2) {
|
|
return autonatv2.Result{Addr: reqs[0].Addr, Idx: 0, Reachability: network.ReachabilityPrivate}, nil
|
|
}
|
|
return autonatv2.Result{}, errors.New("invalid")
|
|
},
|
|
},
|
|
})
|
|
|
|
reachableAddrs := []ma.Multiaddr{publicQUIC}
|
|
unreachableAddrs := []ma.Multiaddr{publicTCP, publicQUIC2}
|
|
select {
|
|
case e := <-sub.Out():
|
|
evt := e.(event.EvtHostReachableAddrsChanged)
|
|
require.ElementsMatch(t, reachableAddrs, evt.Reachable)
|
|
require.ElementsMatch(t, unreachableAddrs, evt.Unreachable)
|
|
require.ElementsMatch(t, reachableAddrs, am.ReachableAddrs())
|
|
case <-time.After(5 * time.Second):
|
|
t.Fatal("expected event for reachability change")
|
|
}
|
|
}
|
|
|
|
func TestRemoveIfNotInSource(t *testing.T) {
|
|
var addrs []ma.Multiaddr
|
|
for i := 0; i < 10; i++ {
|
|
addrs = append(addrs, ma.StringCast(fmt.Sprintf("/ip4/1.2.3.4/tcp/%d", i)))
|
|
}
|
|
slices.SortFunc(addrs, func(a, b ma.Multiaddr) int { return a.Compare(b) })
|
|
cases := []struct {
|
|
addrs []ma.Multiaddr
|
|
source []ma.Multiaddr
|
|
expected []ma.Multiaddr
|
|
}{
|
|
{},
|
|
{addrs: slices.Clone(addrs[:5]), source: nil, expected: nil},
|
|
{addrs: nil, source: addrs, expected: nil},
|
|
{addrs: []ma.Multiaddr{addrs[0]}, source: []ma.Multiaddr{addrs[0]}, expected: []ma.Multiaddr{addrs[0]}},
|
|
{addrs: slices.Clone(addrs), source: []ma.Multiaddr{addrs[0]}, expected: []ma.Multiaddr{addrs[0]}},
|
|
{addrs: slices.Clone(addrs), source: slices.Clone(addrs[5:]), expected: slices.Clone(addrs[5:])},
|
|
{addrs: slices.Clone(addrs[:5]), source: []ma.Multiaddr{addrs[0], addrs[2], addrs[8]}, expected: []ma.Multiaddr{addrs[0], addrs[2]}},
|
|
}
|
|
for i, tc := range cases {
|
|
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
|
addrs := removeNotInSource(tc.addrs, tc.source)
|
|
require.ElementsMatch(t, tc.expected, addrs, "%s\n%s", tc.expected, tc.addrs)
|
|
})
|
|
}
|
|
}
|
|
|
|
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))
|
|
}
|
|
b.Run("areAddrsDifferent", func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
areAddrsDifferent(addrs[:], addrs[:])
|
|
}
|
|
})
|
|
}
|
|
|
|
func BenchmarkRemoveIfNotInSource(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))
|
|
}
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
removeNotInSource(slices.Clone(addrs[:5]), addrs[:])
|
|
}
|
|
}
|