diff --git a/config/config.go b/config/config.go index 3f0cd85e91..8f1c210827 100644 --- a/config/config.go +++ b/config/config.go @@ -129,6 +129,8 @@ type Config struct { DialRanker network.DialRanker SwarmOpts []swarm.Option + + DisableIdentifyAddressDiscovery bool } func (cfg *Config) makeSwarm(eventBus event.Bus, enableMetrics bool) (*swarm.Swarm, error) { @@ -290,19 +292,20 @@ func (cfg *Config) addTransports() ([]fx.Option, error) { func (cfg *Config) newBasicHost(swrm *swarm.Swarm, eventBus event.Bus) (*bhost.BasicHost, error) { h, err := bhost.NewHost(swrm, &bhost.HostOpts{ - EventBus: eventBus, - ConnManager: cfg.ConnManager, - AddrsFactory: cfg.AddrsFactory, - NATManager: cfg.NATManager, - EnablePing: !cfg.DisablePing, - UserAgent: cfg.UserAgent, - ProtocolVersion: cfg.ProtocolVersion, - EnableHolePunching: cfg.EnableHolePunching, - HolePunchingOptions: cfg.HolePunchingOptions, - EnableRelayService: cfg.EnableRelayService, - RelayServiceOpts: cfg.RelayServiceOpts, - EnableMetrics: !cfg.DisableMetrics, - PrometheusRegisterer: cfg.PrometheusRegisterer, + EventBus: eventBus, + ConnManager: cfg.ConnManager, + AddrsFactory: cfg.AddrsFactory, + NATManager: cfg.NATManager, + EnablePing: !cfg.DisablePing, + UserAgent: cfg.UserAgent, + ProtocolVersion: cfg.ProtocolVersion, + EnableHolePunching: cfg.EnableHolePunching, + HolePunchingOptions: cfg.HolePunchingOptions, + EnableRelayService: cfg.EnableRelayService, + RelayServiceOpts: cfg.RelayServiceOpts, + EnableMetrics: !cfg.DisableMetrics, + PrometheusRegisterer: cfg.PrometheusRegisterer, + DisableIdentifyAddressDiscovery: cfg.DisableIdentifyAddressDiscovery, }) if err != nil { return nil, err diff --git a/core/peerstore/peerstore.go b/core/peerstore/peerstore.go index 93b5f4b028..0ef09df9fe 100644 --- a/core/peerstore/peerstore.go +++ b/core/peerstore/peerstore.go @@ -28,7 +28,7 @@ var ( // RecentlyConnectedAddrTTL is used when we recently connected to a peer. // It means that we are reasonably certain of the peer's address. - RecentlyConnectedAddrTTL = time.Minute * 30 + RecentlyConnectedAddrTTL = time.Minute * 15 // OwnObservedAddrTTL is used for our own external addresses observed by peers. // Deprecated: observed addresses are maintained till we disconnect from the peer which provided it diff --git a/libp2p_test.go b/libp2p_test.go index 7e4ac1b61e..343681be85 100644 --- a/libp2p_test.go +++ b/libp2p_test.go @@ -376,6 +376,12 @@ func TestAutoNATService(t *testing.T) { h.Close() } +func TestDisableIdentifyAddressDiscovery(t *testing.T) { + h, err := New(DisableIdentifyAddressDiscovery()) + require.NoError(t, err) + h.Close() +} + func TestMain(m *testing.M) { goleak.VerifyTestMain( m, diff --git a/options.go b/options.go index 747d6c55e6..79f09bfc4a 100644 --- a/options.go +++ b/options.go @@ -598,3 +598,14 @@ func SwarmOpts(opts ...swarm.Option) Option { return nil } } + +// DisableIdentifyAddressDiscovery disables address discovery using peer provided observed addresses +// in identify. If you know your public addresses upfront, the recommended way is to use +// AddressFactory to provide the external adddress to the host and using this option to disable +// discovery from identify as it is more error prone +func DisableIdentifyAddressDiscovery() Option { + return func(cfg *Config) error { + cfg.DisableIdentifyAddressDiscovery = true + return nil + } +} diff --git a/p2p/host/basic/basic_host.go b/p2p/host/basic/basic_host.go index d9cdee5abf..47b5359301 100644 --- a/p2p/host/basic/basic_host.go +++ b/p2p/host/basic/basic_host.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "net" + "slices" "sync" "time" @@ -53,6 +54,8 @@ var ( DefaultAddrsFactory = func(addrs []ma.Multiaddr) []ma.Multiaddr { return addrs } ) +const maxPeerRecordSize = 4096 // 4k to be compatible with rust-libp2p identify implementation + // AddrsFactory functions can be passed to New in order to override // addresses returned by Addrs. type AddrsFactory func([]ma.Multiaddr) []ma.Multiaddr @@ -161,6 +164,9 @@ type HostOpts struct { EnableMetrics bool // PrometheusRegisterer is the PrometheusRegisterer used for metrics PrometheusRegisterer prometheus.Registerer + + // DisableIdentifyAddressDiscovery disables address discovery using peer provided observed addresses in identify + DisableIdentifyAddressDiscovery bool } // NewHost constructs a new *BasicHost and activates it by attaching its stream and connection handlers to the given inet.Network. @@ -244,6 +250,9 @@ func NewHost(n network.Network, opts *HostOpts) (*BasicHost, error) { identify.WithMetricsTracer( identify.NewMetricsTracer(identify.WithRegisterer(opts.PrometheusRegisterer)))) } + if opts.DisableIdentifyAddressDiscovery { + idOpts = append(idOpts, identify.DisableObservedAddrManager()) + } h.ids, err = identify.NewIDService(h, idOpts...) if err != nil { @@ -482,15 +491,18 @@ func makeUpdatedAddrEvent(prev, current []ma.Multiaddr) *event.EvtLocalAddresses return &evt } -func (h *BasicHost) makeSignedPeerRecord(evt *event.EvtLocalAddressesUpdated) (*record.Envelope, error) { - current := make([]ma.Multiaddr, 0, len(evt.Current)) - for _, a := range evt.Current { - current = append(current, a.Address) +func (h *BasicHost) makeSignedPeerRecord(addrs []ma.Multiaddr) (*record.Envelope, error) { + // Limit the length of currentAddrs to ensure that our signed peer records aren't rejected + peerRecordSize := 64 // HostID + k, err := h.signKey.Raw() + if err != nil { + peerRecordSize += 2 * len(k) // 1 for signature, 1 for public key } - + // we want the final address list to be small for keeping the signed peer record in size + addrs = trimHostAddrList(addrs, maxPeerRecordSize-peerRecordSize-256) // 256 B of buffer rec := peer.PeerRecordFromAddrInfo(peer.AddrInfo{ ID: h.ID(), - Addrs: current, + Addrs: addrs, }) return record.Seal(rec, h.signKey) } @@ -513,7 +525,7 @@ func (h *BasicHost) background() { if !h.disableSignedPeerRecord { // add signed peer record to the event - sr, err := h.makeSignedPeerRecord(changeEvt) + sr, err := h.makeSignedPeerRecord(currentAddrs) if err != nil { log.Errorf("error creating a signed peer record from the set of current addresses, err=%s", err) return @@ -805,6 +817,7 @@ func (h *BasicHost) Addrs() []ma.Multiaddr { addrs[i] = addrWithCerthash } } + return addrs } @@ -997,6 +1010,58 @@ func inferWebtransportAddrsFromQuic(in []ma.Multiaddr) []ma.Multiaddr { return out } +func trimHostAddrList(addrs []ma.Multiaddr, maxSize int) []ma.Multiaddr { + totalSize := 0 + for _, a := range addrs { + totalSize += len(a.Bytes()) + } + if totalSize <= maxSize { + return addrs + } + + score := func(addr ma.Multiaddr) int { + var res int + if manet.IsPublicAddr(addr) { + res |= 1 << 12 + } else if !manet.IsIPLoopback(addr) { + res |= 1 << 11 + } + var protocolWeight int + ma.ForEach(addr, func(c ma.Component) bool { + switch c.Protocol().Code { + case ma.P_QUIC_V1: + protocolWeight = 5 + case ma.P_TCP: + protocolWeight = 4 + case ma.P_WSS: + protocolWeight = 3 + case ma.P_WEBTRANSPORT: + protocolWeight = 2 + case ma.P_WEBRTC_DIRECT: + protocolWeight = 1 + case ma.P_P2P: + return false + } + return true + }) + res |= 1 << protocolWeight + return res + } + + slices.SortStableFunc(addrs, func(a, b ma.Multiaddr) int { + return score(b) - score(a) // b-a for reverse order + }) + totalSize = 0 + for i, a := range addrs { + totalSize += len(a.Bytes()) + if totalSize > maxSize { + addrs = addrs[:i] + break + } + } + return addrs +} + // SetAutoNat sets the autonat service for the host. func (h *BasicHost) SetAutoNat(a autonat.AutoNAT) { h.addrMu.Lock() diff --git a/p2p/host/basic/basic_host_test.go b/p2p/host/basic/basic_host_test.go index 1fb3b4a397..c4f0680ea2 100644 --- a/p2p/host/basic/basic_host_test.go +++ b/p2p/host/basic/basic_host_test.go @@ -896,3 +896,55 @@ func TestInferWebtransportAddrsFromQuic(t *testing.T) { } } + +func TestTrimHostAddrList(t *testing.T) { + type testCase struct { + name string + in []ma.Multiaddr + threshold int + out []ma.Multiaddr + } + + tcpPublic := ma.StringCast("/ip4/1.1.1.1/tcp/1") + quicPublic := ma.StringCast("/ip4/1.1.1.1/udp/1/quic-v1") + + tcpPrivate := ma.StringCast("/ip4/192.168.1.1/tcp/1") + quicPrivate := ma.StringCast("/ip4/192.168.1.1/udp/1/quic-v1") + + tcpLocal := ma.StringCast("/ip4/127.0.0.1/tcp/1") + quicLocal := ma.StringCast("/ip4/127.0.0.1/udp/1/quic-v1") + + testCases := []testCase{ + { + name: "Public preferred over private", + in: []ma.Multiaddr{tcpPublic, quicPrivate}, + threshold: len(tcpLocal.Bytes()), + out: []ma.Multiaddr{tcpPublic}, + }, + { + name: "Public and private preffered over local", + in: []ma.Multiaddr{tcpPublic, tcpPrivate, quicLocal}, + threshold: len(tcpPublic.Bytes()) + len(tcpPrivate.Bytes()), + out: []ma.Multiaddr{tcpPublic, tcpPrivate}, + }, + { + name: "quic preferred over tcp", + in: []ma.Multiaddr{tcpPublic, quicPublic}, + threshold: len(quicPublic.Bytes()), + out: []ma.Multiaddr{quicPublic}, + }, + { + name: "no filtering on large threshold", + in: []ma.Multiaddr{tcpPublic, quicPublic, quicLocal, tcpLocal, tcpPrivate}, + threshold: 10000, + out: []ma.Multiaddr{tcpPublic, quicPublic, quicLocal, tcpLocal, tcpPrivate}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + got := trimHostAddrList(tc.in, tc.threshold) + require.ElementsMatch(t, got, tc.out) + }) + } +} diff --git a/p2p/protocol/identify/id.go b/p2p/protocol/identify/id.go index 6ff3095537..d85191835a 100644 --- a/p2p/protocol/identify/id.go +++ b/p2p/protocol/identify/id.go @@ -34,24 +34,27 @@ import ( var log = logging.Logger("net/identify") +var Timeout = 30 * time.Second // timeout on all incoming Identify interactions + const ( // ID is the protocol.ID of version 1.0.0 of the identify service. ID = "/ipfs/id/1.0.0" // IDPush is the protocol.ID of the Identify push protocol. // It sends full identify messages containing the current state of the peer. IDPush = "/ipfs/id/push/1.0.0" -) - -const ServiceName = "libp2p.identify" -const maxPushConcurrency = 32 - -var Timeout = 60 * time.Second // timeout on all incoming Identify interactions - -const ( - legacyIDSize = 2 * 1024 // 2k Bytes - signedIDSize = 8 * 1024 // 8K - maxMessages = 10 + ServiceName = "libp2p.identify" + + legacyIDSize = 2 * 1024 + signedIDSize = 8 * 1024 + maxOwnIdentifyMsgSize = 4096 // smaller than what we accept. This is 4k to be compatible with rust-libp2p + maxMessages = 10 + maxPushConcurrency = 32 + // number of addresses to keep for peers we have disconnected from for peerstore.RecentlyConnectedTTL time + // This number can be small as we already filter peer addresses based on whether the peer is connected to us over + // localhost, private IP or public IP address + recentlyConnectedPeerMaxAddrs = 20 + connectedPeerMaxAddrs = 500 ) var defaultUserAgent = "github.com/libp2p/go-libp2p" @@ -585,10 +588,18 @@ func readAllIDMessages(r pbio.Reader, finalMsg proto.Message) error { } func (ids *idService) updateSnapshot() (updated bool) { - addrs := ids.Host.Addrs() - slices.SortFunc(addrs, func(a, b ma.Multiaddr) int { return bytes.Compare(a.Bytes(), b.Bytes()) }) protos := ids.Host.Mux().Protocols() slices.Sort(protos) + + addrs := ids.Host.Addrs() + slices.SortFunc(addrs, func(a, b ma.Multiaddr) int { return bytes.Compare(a.Bytes(), b.Bytes()) }) + + usedSpace := len(ids.ProtocolVersion) + len(ids.UserAgent) + for i := 0; i < len(protos); i++ { + usedSpace += len(protos[i]) + } + addrs = trimHostAddrList(addrs, maxOwnIdentifyMsgSize-usedSpace-256) // 256 bytes of buffer + snapshot := identifySnapshot{ addrs: addrs, protocols: protos, @@ -809,7 +820,12 @@ func (ids *idService) consumeMessage(mes *pb.Identify, c network.Conn, isPush bo } else { addrs = lmaddrs } - ids.Host.Peerstore().AddAddrs(p, filterAddrs(addrs, c.RemoteMultiaddr()), ttl) + addrs = filterAddrs(addrs, c.RemoteMultiaddr()) + if len(addrs) > connectedPeerMaxAddrs { + addrs = addrs[:connectedPeerMaxAddrs] + } + + ids.Host.Peerstore().AddAddrs(p, addrs, ttl) // Finally, expire all temporary addrs. ids.Host.Peerstore().UpdateAddrs(p, peerstore.TempAddrTTL, 0) @@ -1017,15 +1033,32 @@ func (nn *netNotifiee) Disconnected(_ network.Network, c network.Conn) { ids.observedAddrMgr.removeConn(c) } + // Last disconnect. + // Undo the setting of addresses to peer.ConnectedAddrTTL we did + ids.addrMu.Lock() + defer ids.addrMu.Unlock() + + // This check MUST happen after acquiring the Lock as identify on a different connection + // might be trying to add addresses. switch ids.Host.Network().Connectedness(c.RemotePeer()) { case network.Connected, network.Limited: return } - // Last disconnect. - // Undo the setting of addresses to peer.ConnectedAddrTTL we did - ids.addrMu.Lock() - ids.Host.Peerstore().UpdateAddrs(c.RemotePeer(), peerstore.ConnectedAddrTTL, peerstore.RecentlyConnectedAddrTTL) - ids.addrMu.Unlock() + // peerstore returns the elements in a random order as it uses a map to store the addresses + addrs := ids.Host.Peerstore().Addrs(c.RemotePeer()) + n := len(addrs) + if n > recentlyConnectedPeerMaxAddrs { + // We want to always save the address we are connected to + for i, a := range addrs { + if a.Equal(c.RemoteMultiaddr()) { + addrs[i], addrs[0] = addrs[0], addrs[i] + } + } + n = recentlyConnectedPeerMaxAddrs + } + ids.Host.Peerstore().UpdateAddrs(c.RemotePeer(), peerstore.ConnectedAddrTTL, peerstore.TempAddrTTL) + ids.Host.Peerstore().AddAddrs(c.RemotePeer(), addrs[:n], peerstore.RecentlyConnectedAddrTTL) + ids.Host.Peerstore().UpdateAddrs(c.RemotePeer(), peerstore.TempAddrTTL, 0) } func (nn *netNotifiee) Listen(n network.Network, a ma.Multiaddr) {} @@ -1044,3 +1077,55 @@ func filterAddrs(addrs []ma.Multiaddr, remote ma.Multiaddr) []ma.Multiaddr { } return ma.FilterAddrs(addrs, manet.IsPublicAddr) } + +func trimHostAddrList(addrs []ma.Multiaddr, maxSize int) []ma.Multiaddr { + totalSize := 0 + for _, a := range addrs { + totalSize += len(a.Bytes()) + } + if totalSize <= maxSize { + return addrs + } + + score := func(addr ma.Multiaddr) int { + var res int + if manet.IsPublicAddr(addr) { + res |= 1 << 12 + } else if !manet.IsIPLoopback(addr) { + res |= 1 << 11 + } + var protocolWeight int + ma.ForEach(addr, func(c ma.Component) bool { + switch c.Protocol().Code { + case ma.P_QUIC_V1: + protocolWeight = 5 + case ma.P_TCP: + protocolWeight = 4 + case ma.P_WSS: + protocolWeight = 3 + case ma.P_WEBTRANSPORT: + protocolWeight = 2 + case ma.P_WEBRTC_DIRECT: + protocolWeight = 1 + case ma.P_P2P: + return false + } + return true + }) + res |= 1 << protocolWeight + return res + } + + slices.SortStableFunc(addrs, func(a, b ma.Multiaddr) int { + return score(b) - score(a) // b-a for reverse order + }) + totalSize = 0 + for i, a := range addrs { + totalSize += len(a.Bytes()) + if totalSize > maxSize { + addrs = addrs[:i] + break + } + } + return addrs +} diff --git a/p2p/protocol/identify/id_test.go b/p2p/protocol/identify/id_test.go index abdc9c83eb..a65d64f24e 100644 --- a/p2p/protocol/identify/id_test.go +++ b/p2p/protocol/identify/id_test.go @@ -114,8 +114,13 @@ func TestIDService(t *testing.T) { } // This test is highly timing dependent, waiting on timeouts/expiration. oldTTL := peerstore.RecentlyConnectedAddrTTL + oldTempTTL := peerstore.TempAddrTTL peerstore.RecentlyConnectedAddrTTL = 500 * time.Millisecond - t.Cleanup(func() { peerstore.RecentlyConnectedAddrTTL = oldTTL }) + peerstore.TempAddrTTL = 50 * time.Millisecond + t.Cleanup(func() { + peerstore.RecentlyConnectedAddrTTL = oldTTL + peerstore.TempAddrTTL = oldTempTTL + }) clk := mockClock.NewMock() swarm1 := swarmt.GenSwarm(t, swarmt.WithClock(clk)) @@ -615,8 +620,13 @@ func TestLargeIdentifyMessage(t *testing.T) { t.Skip("setting peerstore.RecentlyConnectedAddrTTL is racy") } oldTTL := peerstore.RecentlyConnectedAddrTTL + oldTempTTL := peerstore.TempAddrTTL peerstore.RecentlyConnectedAddrTTL = 500 * time.Millisecond - t.Cleanup(func() { peerstore.RecentlyConnectedAddrTTL = oldTTL }) + peerstore.TempAddrTTL = 50 * time.Millisecond + t.Cleanup(func() { + peerstore.RecentlyConnectedAddrTTL = oldTTL + peerstore.TempAddrTTL = oldTempTTL + }) clk := mockClock.NewMock() swarm1 := swarmt.GenSwarm(t, swarmt.WithClock(clk)) diff --git a/p2p/protocol/identify/obsaddr.go b/p2p/protocol/identify/obsaddr.go index daf0ece758..4437c4b011 100644 --- a/p2p/protocol/identify/obsaddr.go +++ b/p2p/protocol/identify/obsaddr.go @@ -25,7 +25,7 @@ var ActivationThresh = 4 // for adding to an ObservedAddrManager. var observedAddrManagerWorkerChannelSize = 16 -const maxExternalThinWaistAddrsPerLocalAddr = 5 +const maxExternalThinWaistAddrsPerLocalAddr = 3 // thinWaist is a struct that stores the address along with it's thin waist prefix and rest of the multiaddr type thinWaist struct { @@ -508,6 +508,7 @@ func (o *ObservedAddrManager) getNATType() (tcpNATType, udpNATType network.NATDe } } } + sort.Sort(sort.Reverse(sort.IntSlice(tcpCounts))) sort.Sort(sort.Reverse(sort.IntSlice(udpCounts))) @@ -521,14 +522,14 @@ func (o *ObservedAddrManager) getNATType() (tcpNATType, udpNATType network.NATDe // If the top elements cover more than 1/2 of all the observations, there's a > 50% chance that // hole punching based on outputs of observed address manager will succeed - if len(tcpCounts) > 0 { + if tcpTotal >= 3*maxExternalThinWaistAddrsPerLocalAddr { if tcpTopCounts >= tcpTotal/2 { tcpNATType = network.NATDeviceTypeCone } else { tcpNATType = network.NATDeviceTypeSymmetric } } - if len(udpCounts) > 0 { + if udpTotal >= 3*maxExternalThinWaistAddrsPerLocalAddr { if udpTopCounts >= udpTotal/2 { udpNATType = network.NATDeviceTypeCone } else { diff --git a/p2p/protocol/identify/obsaddr_test.go b/p2p/protocol/identify/obsaddr_test.go index 36176d15a6..9c2d8dee57 100644 --- a/p2p/protocol/identify/obsaddr_test.go +++ b/p2p/protocol/identify/obsaddr_test.go @@ -296,7 +296,7 @@ func TestObservedAddrManager(t *testing.T) { // At this point we have 10 groups of N / 10 with 10 observations for every connection // The output should remain stable require.Eventually(t, func() bool { - return len(o.Addrs()) == 15 + return len(o.Addrs()) == 3*maxExternalThinWaistAddrsPerLocalAddr }, 1*time.Second, 100*time.Millisecond) addrs := o.Addrs() for i := 0; i < 10; i++ { @@ -359,15 +359,14 @@ func TestObservedAddrManager(t *testing.T) { t.Run("getNATType", func(t *testing.T) { o := newObservedAddrMgr() defer o.Close() + observedWebTransport := ma.StringCast("/ip4/2.2.2.2/udp/1/quic-v1/webtransport") - c1 := newConn(webTransport4ListenAddr, ma.StringCast("/ip4/1.2.3.1/udp/1/quic-v1/webtransport")) - c2 := newConn(webTransport4ListenAddr, ma.StringCast("/ip4/1.2.3.2/udp/1/quic-v1/webtransport")) - c3 := newConn(webTransport4ListenAddr, ma.StringCast("/ip4/1.2.3.3/udp/1/quic-v1/webtransport")) - c4 := newConn(webTransport4ListenAddr, ma.StringCast("/ip4/1.2.3.4/udp/1/quic-v1/webtransport")) - o.Record(c1, observedWebTransport) - o.Record(c2, observedWebTransport) - o.Record(c3, observedWebTransport) - o.Record(c4, observedWebTransport) + var udpConns [5 * maxExternalThinWaistAddrsPerLocalAddr]connMultiaddrs + for i := 0; i < len(udpConns); i++ { + udpConns[i] = newConn(webTransport4ListenAddr, ma.StringCast(fmt.Sprintf("/ip4/1.2.3.%d/udp/1/quic-v1/webtransport", i))) + o.Record(udpConns[i], observedWebTransport) + time.Sleep(10 * time.Millisecond) + } require.Eventually(t, func() bool { return addrsEqual(o.Addrs(), []ma.Multiaddr{observedWebTransport}) }, 1*time.Second, 100*time.Millisecond) @@ -375,13 +374,6 @@ func TestObservedAddrManager(t *testing.T) { tcpNAT, udpNAT := o.getNATType() require.Equal(t, tcpNAT, network.NATDeviceTypeUnknown) require.Equal(t, udpNAT, network.NATDeviceTypeCone) - o.removeConn(c1) - o.removeConn(c2) - o.removeConn(c3) - o.removeConn(c4) - require.Eventually(t, func() bool { - return checkAllEntriesRemoved(o) - }, 1*time.Second, 100*time.Millisecond) }) t.Run("NATTypeSymmetric", func(t *testing.T) { o := newObservedAddrMgr() @@ -404,7 +396,7 @@ func TestObservedAddrManager(t *testing.T) { // At this point we have 20 groups with 5 observations for every connection // The output should remain stable require.Eventually(t, func() bool { - return len(o.Addrs()) == 10 + return len(o.Addrs()) == 2*maxExternalThinWaistAddrsPerLocalAddr }, 1*time.Second, 100*time.Millisecond) tcpNAT, udpNAT := o.getNATType() @@ -451,14 +443,12 @@ func TestObservedAddrManager(t *testing.T) { sub, err := bus.Subscribe(new(event.EvtNATDeviceTypeChanged)) require.NoError(t, err) observedWebTransport := ma.StringCast("/ip4/2.2.2.2/udp/1/quic-v1/webtransport") - c1 := newConn(webTransport4ListenAddr, ma.StringCast("/ip4/1.2.3.1/udp/1/quic-v1/webtransport")) - c2 := newConn(webTransport4ListenAddr, ma.StringCast("/ip4/1.2.3.2/udp/1/quic-v1/webtransport")) - c3 := newConn(webTransport4ListenAddr, ma.StringCast("/ip4/1.2.3.3/udp/1/quic-v1/webtransport")) - c4 := newConn(webTransport4ListenAddr, ma.StringCast("/ip4/1.2.3.4/udp/1/quic-v1/webtransport")) - o.Record(c1, observedWebTransport) - o.Record(c2, observedWebTransport) - o.Record(c3, observedWebTransport) - o.Record(c4, observedWebTransport) + var udpConns [5 * maxExternalThinWaistAddrsPerLocalAddr]connMultiaddrs + for i := 0; i < len(udpConns); i++ { + udpConns[i] = newConn(webTransport4ListenAddr, ma.StringCast(fmt.Sprintf("/ip4/1.2.3.%d/udp/1/quic-v1/webtransport", i))) + o.Record(udpConns[i], observedWebTransport) + time.Sleep(10 * time.Millisecond) + } require.Eventually(t, func() bool { return addrsEqual(o.Addrs(), []ma.Multiaddr{observedWebTransport}) }, 1*time.Second, 100*time.Millisecond) diff --git a/p2p/protocol/identify/opts.go b/p2p/protocol/identify/opts.go index c08c5f31de..bd0fd896b8 100644 --- a/p2p/protocol/identify/opts.go +++ b/p2p/protocol/identify/opts.go @@ -41,7 +41,7 @@ func WithMetricsTracer(tr MetricsTracer) Option { } // DisableObservedAddrManager disables the observed address manager. It also -// effectively disables the nat emitter and EvtPeerProtocolsUpdatedevents +// effectively disables the nat emitter and EvtNATDeviceTypeChanged func DisableObservedAddrManager() Option { return func(cfg *config) { cfg.disableObservedAddrManager = true