Skip to content

Commit

Permalink
ObsAddrManager: Infer external addresses for transports that share th…
Browse files Browse the repository at this point in the history
…e same listening address. (#2892)

* Remove this hack, the new observed address manager just works

* Infer addresses that share the same local thin waist

* Use Interface listen address as well
  • Loading branch information
MarcoPolo authored Aug 1, 2024
1 parent db41da3 commit 0cc0b2f
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 196 deletions.
74 changes: 0 additions & 74 deletions p2p/host/basic/basic_host.go
Original file line number Diff line number Diff line change
Expand Up @@ -939,7 +939,6 @@ func (h *BasicHost) AllAddrs() []ma.Multiaddr {
finalAddrs = append(finalAddrs, observedAddrs...)
}
finalAddrs = ma.Unique(finalAddrs)
finalAddrs = inferWebtransportAddrsFromQuic(finalAddrs)
return finalAddrs
}

Expand Down Expand Up @@ -984,79 +983,6 @@ func (h *BasicHost) addCertHashes(addrs []ma.Multiaddr) []ma.Multiaddr {
return addrs
}

var wtComponent = ma.StringCast("/webtransport")

// inferWebtransportAddrsFromQuic infers more webtransport addresses from QUIC addresses.
// This is useful when we discover our public QUIC address, but haven't discovered our public WebTransport addrs.
// If we see that we are listening on the same port for QUIC and WebTransport,
// we can be pretty sure that the WebTransport addr will be reachable if the
// QUIC one is.
// We assume the input is deduped.
func inferWebtransportAddrsFromQuic(in []ma.Multiaddr) []ma.Multiaddr {
// We need to check if we are listening on the same ip+port for QUIC and WebTransport.
// If not, there's nothing to do since we can't infer anything.

// Count the number of QUIC addrs, this will let us allocate just once at the beginning.
quicAddrCount := 0
for _, addr := range in {
if _, lastComponent := ma.SplitLast(addr); lastComponent.Protocol().Code == ma.P_QUIC_V1 {
quicAddrCount++
}
}
quicOrWebtransportAddrs := make(map[string]struct{}, quicAddrCount)
webtransportAddrs := make(map[string]struct{}, quicAddrCount)
foundSameListeningAddr := false
for _, addr := range in {
isWebtransport, numCertHashes := libp2pwebtransport.IsWebtransportMultiaddr(addr)
if isWebtransport {
for i := 0; i < numCertHashes; i++ {
// Remove certhashes
addr, _ = ma.SplitLast(addr)
}
webtransportAddrs[string(addr.Bytes())] = struct{}{}
// Remove webtransport component, now it's a multiaddr that ends in /quic-v1
addr, _ = ma.SplitLast(addr)
}

if _, lastComponent := ma.SplitLast(addr); lastComponent.Protocol().Code == ma.P_QUIC_V1 {
bytes := addr.Bytes()
if _, ok := quicOrWebtransportAddrs[string(bytes)]; ok {
foundSameListeningAddr = true
} else {
quicOrWebtransportAddrs[string(bytes)] = struct{}{}
}
}
}

if !foundSameListeningAddr {
return in
}

if len(webtransportAddrs) == 0 {
// No webtransport addresses, we aren't listening on any webtransport
// address, so we shouldn't add any.
return in
}

out := make([]ma.Multiaddr, 0, len(in)+(quicAddrCount-len(webtransportAddrs)))
for _, addr := range in {
// Add all the original addresses
out = append(out, addr)
if _, lastComponent := ma.SplitLast(addr); lastComponent.Protocol().Code == ma.P_QUIC_V1 {
// Convert quic to webtransport
addr = addr.Encapsulate(wtComponent)
if _, ok := webtransportAddrs[string(addr.Bytes())]; ok {
// We already have this address
continue
}
// Add the new inferred address
out = append(out, addr)
}
}

return out
}

func trimHostAddrList(addrs []ma.Multiaddr, maxSize int) []ma.Multiaddr {
totalSize := 0
for _, a := range addrs {
Expand Down
73 changes: 0 additions & 73 deletions p2p/host/basic/basic_host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"io"
"reflect"
"sort"
"strings"
"sync"
"testing"
Expand Down Expand Up @@ -825,78 +824,6 @@ func TestNormalizeMultiaddr(t *testing.T) {
require.Equal(t, "/ip4/1.2.3.4/udp/9999/quic-v1/webtransport", h1.NormalizeMultiaddr(ma.StringCast("/ip4/1.2.3.4/udp/9999/quic-v1/webtransport/certhash/uEgNmb28")).String())
}

func TestInferWebtransportAddrsFromQuic(t *testing.T) {
type testCase struct {
name string
in []string
out []string
}

testCases := []testCase{
{
name: "Happy Path",
in: []string{"/ip4/0.0.0.0/udp/9999/quic-v1", "/ip4/0.0.0.0/udp/9999/quic-v1/webtransport", "/ip4/1.2.3.4/udp/9999/quic-v1"},
out: []string{"/ip4/0.0.0.0/udp/9999/quic-v1", "/ip4/0.0.0.0/udp/9999/quic-v1/webtransport", "/ip4/1.2.3.4/udp/9999/quic-v1", "/ip4/1.2.3.4/udp/9999/quic-v1/webtransport"},
},
{
name: "Happy Path With CertHashes",
in: []string{"/ip4/0.0.0.0/udp/9999/quic-v1", "/ip4/0.0.0.0/udp/9999/quic-v1/webtransport/certhash/uEgNmb28/certhash/uEgNmb28", "/ip4/1.2.3.4/udp/9999/quic-v1"},
out: []string{"/ip4/0.0.0.0/udp/9999/quic-v1", "/ip4/0.0.0.0/udp/9999/quic-v1/webtransport/certhash/uEgNmb28/certhash/uEgNmb28", "/ip4/1.2.3.4/udp/9999/quic-v1", "/ip4/1.2.3.4/udp/9999/quic-v1/webtransport"},
},
{
name: "Already discovered",
in: []string{"/ip4/0.0.0.0/udp/9999/quic-v1", "/ip4/0.0.0.0/udp/9999/quic-v1/webtransport", "/ip4/1.2.3.4/udp/9999/quic-v1", "/ip4/1.2.3.4/udp/9999/quic-v1/webtransport"},
out: []string{"/ip4/0.0.0.0/udp/9999/quic-v1", "/ip4/0.0.0.0/udp/9999/quic-v1/webtransport", "/ip4/1.2.3.4/udp/9999/quic-v1", "/ip4/1.2.3.4/udp/9999/quic-v1/webtransport"},
},
{
name: "Infer Many",
in: []string{"/ip4/0.0.0.0/udp/9999/quic-v1", "/ip4/0.0.0.0/udp/9999/quic-v1/webtransport", "/ip4/1.2.3.4/udp/9999/quic-v1", "/ip4/4.3.2.1/udp/9999/quic-v1"},
out: []string{"/ip4/0.0.0.0/udp/9999/quic-v1", "/ip4/0.0.0.0/udp/9999/quic-v1/webtransport", "/ip4/1.2.3.4/udp/9999/quic-v1", "/ip4/4.3.2.1/udp/9999/quic-v1", "/ip4/1.2.3.4/udp/9999/quic-v1/webtransport", "/ip4/4.3.2.1/udp/9999/quic-v1/webtransport"},
},
{
name: "No Common listeners",
in: []string{"/ip4/0.0.0.0/udp/9999/quic-v1", "/ip4/0.0.0.0/udp/1111/quic-v1/webtransport", "/ip4/1.2.3.4/udp/9999/quic-v1"},
out: []string{"/ip4/0.0.0.0/udp/9999/quic-v1", "/ip4/0.0.0.0/udp/1111/quic-v1/webtransport", "/ip4/1.2.3.4/udp/9999/quic-v1"},
},
{
name: "No WebTransport",
in: []string{"/ip4/0.0.0.0/udp/9999/quic-v1", "/ip4/1.2.3.4/udp/9999/quic-v1"},
out: []string{"/ip4/0.0.0.0/udp/9999/quic-v1", "/ip4/1.2.3.4/udp/9999/quic-v1"},
},
}

// Make sure the testCases are all valid multiaddrs
for _, tc := range testCases {
for _, addr := range tc.in {
_, err := ma.NewMultiaddr(addr)
require.NoError(t, err)
}
for _, addr := range tc.out {
_, err := ma.NewMultiaddr(addr)
require.NoError(t, err)
}
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
sort.StringSlice(tc.in).Sort()
sort.StringSlice(tc.out).Sort()
min := make([]ma.Multiaddr, 0, len(tc.in))
for _, addr := range tc.in {
min = append(min, ma.StringCast(addr))
}
outMa := inferWebtransportAddrsFromQuic(min)
outStr := make([]string, 0, len(outMa))
for _, addr := range outMa {
outStr = append(outStr, addr.String())
}
require.Equal(t, tc.out, outStr)
})

}

}

func TestTrimHostAddrList(t *testing.T) {
type testCase struct {
name string
Expand Down
44 changes: 44 additions & 0 deletions p2p/protocol/identify/obsaddr.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,48 @@ func (o *ObservedAddrManager) AddrsFor(addr ma.Multiaddr) (addrs []ma.Multiaddr)
return res
}

// appendInferredAddrs infers the external address of other transports that
// share the local thin waist with a transport that we have do observations for.
//
// e.g. If we have observations for a QUIC address on port 9000, and we are
// listening on the same interface and port 9000 for WebTransport, we can infer
// the external WebTransport address.
func (o *ObservedAddrManager) appendInferredAddrs(twToObserverSets map[string][]*observerSet, addrs []ma.Multiaddr) []ma.Multiaddr {
if twToObserverSets == nil {
twToObserverSets = make(map[string][]*observerSet)
for localTWStr := range o.externalAddrs {
twToObserverSets[localTWStr] = append(twToObserverSets[localTWStr], o.getTopExternalAddrs(localTWStr)...)
}
}
lAddrs, err := o.interfaceListenAddrs()
if err != nil {
log.Warnw("failed to get interface resolved listen addrs. Using just the listen addrs", "error", err)
lAddrs = nil
}
lAddrs = append(lAddrs, o.listenAddrs()...)
seenTWs := make(map[string]struct{})
for _, a := range lAddrs {
if _, ok := o.localAddrs[string(a.Bytes())]; ok {
// We already have this address in the list
continue
}
if _, ok := seenTWs[string(a.Bytes())]; ok {
// We've already added this
continue
}
seenTWs[string(a.Bytes())] = struct{}{}
a = o.normalize(a)
t, err := thinWaistForm(a)
if err != nil {
continue
}
for _, s := range twToObserverSets[string(t.TW.Bytes())] {
addrs = append(addrs, s.cacheMultiaddr(t.Rest))
}
}
return addrs
}

// Addrs return all activated observed addresses
func (o *ObservedAddrManager) Addrs() []ma.Multiaddr {
o.mu.RLock()
Expand All @@ -228,6 +270,8 @@ func (o *ObservedAddrManager) Addrs() []ma.Multiaddr {
addrs = append(addrs, s.cacheMultiaddr(t.Rest))
}
}

addrs = o.appendInferredAddrs(m, addrs)
return addrs
}

Expand Down
Loading

0 comments on commit 0cc0b2f

Please sign in to comment.