Skip to content

Commit 0b4867c

Browse files
feat: quic,webtransport: enable both quic-draft29 and quic-v1 addrs on quic. only quic-v1 on webtransport (#1881)
* transport.Listener returns a list of multiaddrs * Support both QUIC versions in QUIC transport * Support only QUIC v1 in webtransport * Update dialMatcher * Update tests * Only use draft 29 when dialing if the server is a draft 29 server * Removes QUIC draft 29 addrs if we have a QUIC v1 addr * Lint fix * Add changes to deterministic certhashes after rebase * Update p2p/transport/quic/options.go Co-authored-by: Marten Seemann <martenseemann@gmail.com> * Update p2p/transport/quic/listener.go Co-authored-by: Marten Seemann <martenseemann@gmail.com> * Update p2p/transport/quic/quic_multiaddr.go Co-authored-by: Marten Seemann <martenseemann@gmail.com> * Stylize QUIC correctly * Update doc around ListenClose * Preallocate a bit extra to avoid paying for an allocation later * Keep a list of multiaddrs, then join * PR nits * Close transport or listener just once * Update go-multiaddr Co-authored-by: Marten Seemann <martenseemann@gmail.com>
1 parent f4ddf59 commit 0b4867c

34 files changed

+579
-245
lines changed

core/transport/transport.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ type Listener interface {
9191
Accept() (CapableConn, error)
9292
Close() error
9393
Addr() net.Addr
94-
Multiaddr() ma.Multiaddr
94+
Multiaddrs() []ma.Multiaddr
9595
}
9696

9797
// TransportNetwork is an inet.Network with methods for managing transports.

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ require (
3838
github.com/minio/sha256-simd v1.0.0
3939
github.com/mr-tron/base58 v1.2.0
4040
github.com/multiformats/go-base32 v0.1.0
41-
github.com/multiformats/go-multiaddr v0.7.0
41+
github.com/multiformats/go-multiaddr v0.8.0
4242
github.com/multiformats/go-multiaddr-dns v0.3.1
4343
github.com/multiformats/go-multiaddr-fmt v0.1.0
4444
github.com/multiformats/go-multibase v0.1.1

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,8 +373,8 @@ github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ8
373373
github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=
374374
github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo=
375375
github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4=
376-
github.com/multiformats/go-multiaddr v0.7.0 h1:gskHcdaCyPtp9XskVwtvEeQOG465sCohbQIirSyqxrc=
377-
github.com/multiformats/go-multiaddr v0.7.0/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs=
376+
github.com/multiformats/go-multiaddr v0.8.0 h1:aqjksEcqK+iD/Foe1RRFsGZh8+XFiGo7FgUCZlpv3LU=
377+
github.com/multiformats/go-multiaddr v0.8.0/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs=
378378
github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A=
379379
github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk=
380380
github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E=

p2p/host/autonat/dialpolicy_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ func (l *mockL) Accept() (transport.CapableConn, error) {
4747
<-l.ctx.Done()
4848
return nil, errors.New("expected in mocked test")
4949
}
50-
func (l *mockL) Close() error { return nil }
51-
func (l *mockL) Addr() net.Addr { return nil }
52-
func (l *mockL) Multiaddr() multiaddr.Multiaddr { return l.addr }
50+
func (l *mockL) Close() error { return nil }
51+
func (l *mockL) Addr() net.Addr { return nil }
52+
func (l *mockL) Multiaddrs() []multiaddr.Multiaddr { return []multiaddr.Multiaddr{l.addr} }
5353

5454
func TestSkipDial(t *testing.T) {
5555
ctx, cancel := context.WithCancel(context.Background())

p2p/net/swarm/swarm.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,8 +237,14 @@ func (s *Swarm) close() {
237237
s.transports.m = nil
238238
s.transports.Unlock()
239239

240-
var wg sync.WaitGroup
240+
// Dedup transports that may be listening on multiple protocols
241+
transportsToClose := make(map[transport.Transport]struct{}, len(transports))
241242
for _, t := range transports {
243+
transportsToClose[t] = struct{}{}
244+
}
245+
246+
var wg sync.WaitGroup
247+
for t := range transportsToClose {
242248
if closer, ok := t.(io.Closer); ok {
243249
wg.Add(1)
244250
go func(c io.Closer) {

p2p/net/swarm/swarm_addr.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ func (s *Swarm) ListenAddresses() []ma.Multiaddr {
1616
}
1717

1818
func (s *Swarm) listenAddressesNoLock() []ma.Multiaddr {
19-
addrs := make([]ma.Multiaddr, 0, len(s.listeners.m))
19+
addrs := make([]ma.Multiaddr, 0, len(s.listeners.m)+10) // A bit extra so we may avoid an extra allocation in the for loop below.
2020
for l := range s.listeners.m {
21-
addrs = append(addrs, l.Multiaddr())
21+
addrs = append(addrs, l.Multiaddrs()...)
2222
}
2323
return addrs
2424
}

p2p/net/swarm/swarm_addr_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ func TestDialAddressSelection(t *testing.T) {
9898
require.Equal(t, tcpTr, s.TransportForDialing(ma.StringCast("/ip4/127.0.0.1/tcp/1234")))
9999
require.Equal(t, quicTr, s.TransportForDialing(ma.StringCast("/ip4/127.0.0.1/udp/1234/quic")))
100100
require.Equal(t, circuitTr, s.TransportForDialing(ma.StringCast(fmt.Sprintf("/ip4/127.0.0.1/udp/1234/quic/p2p-circuit/p2p/%s", id))))
101-
require.Equal(t, webtransportTr, s.TransportForDialing(ma.StringCast(fmt.Sprintf("/ip4/127.0.0.1/udp/1234/quic/webtransport/certhash/%s", certHash))))
101+
require.Equal(t, webtransportTr, s.TransportForDialing(ma.StringCast(fmt.Sprintf("/ip4/127.0.0.1/udp/1234/quic-v1/webtransport/certhash/%s", certHash))))
102102
require.Nil(t, s.TransportForDialing(ma.StringCast("/ip4/1.2.3.4")))
103103
require.Nil(t, s.TransportForDialing(ma.StringCast("/ip4/1.2.3.4/tcp/443/ws")))
104104
}

p2p/net/swarm/swarm_dial.go

Lines changed: 74 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/libp2p/go-libp2p/core/peer"
1313
"github.com/libp2p/go-libp2p/core/peerstore"
1414
"github.com/libp2p/go-libp2p/core/transport"
15+
"github.com/lucas-clemente/quic-go"
1516

1617
ma "github.com/multiformats/go-multiaddr"
1718
madns "github.com/multiformats/go-multiaddr-dns"
@@ -439,15 +440,17 @@ func (s *Swarm) filterKnownUndialables(p peer.ID, addrs []ma.Multiaddr) []ma.Mul
439440
}
440441
}
441442

442-
return maybeRemoveWebTransportAddrs(ma.FilterAddrs(addrs,
443-
func(addr ma.Multiaddr) bool { return !ma.Contains(ourAddrs, addr) },
444-
s.canDial,
445-
// TODO: Consider allowing link-local addresses
446-
func(addr ma.Multiaddr) bool { return !manet.IsIP6LinkLocal(addr) },
447-
func(addr ma.Multiaddr) bool {
448-
return s.gater == nil || s.gater.InterceptAddrDial(p, addr)
449-
},
450-
))
443+
return maybeRemoveWebTransportAddrs(
444+
maybeRemoveQUICDraft29(
445+
ma.FilterAddrs(addrs,
446+
func(addr ma.Multiaddr) bool { return !ma.Contains(ourAddrs, addr) },
447+
s.canDial,
448+
// TODO: Consider allowing link-local addresses
449+
func(addr ma.Multiaddr) bool { return !manet.IsIP6LinkLocal(addr) },
450+
func(addr ma.Multiaddr) bool {
451+
return s.gater == nil || s.gater.InterceptAddrDial(p, addr)
452+
},
453+
)))
451454
}
452455

453456
// limitedDial will start a dial to the given peer when
@@ -536,9 +539,32 @@ func isWebTransport(addr ma.Multiaddr) bool {
536539
return err == nil
537540
}
538541

539-
func isQUIC(addr ma.Multiaddr) bool {
540-
_, err := addr.ValueForProtocol(ma.P_QUIC)
541-
return err == nil && !isWebTransport(addr)
542+
func quicVersion(addr ma.Multiaddr) (quic.VersionNumber, bool) {
543+
found := false
544+
foundWebTransport := false
545+
var version quic.VersionNumber
546+
ma.ForEach(addr, func(c ma.Component) bool {
547+
switch c.Protocol().Code {
548+
case ma.P_QUIC:
549+
version = quic.VersionDraft29
550+
found = true
551+
return true
552+
case ma.P_QUIC_V1:
553+
version = quic.Version1
554+
found = true
555+
return true
556+
case ma.P_WEBTRANSPORT:
557+
version = quic.Version1
558+
foundWebTransport = true
559+
return false
560+
default:
561+
return true
562+
}
563+
})
564+
if foundWebTransport {
565+
return 0, false
566+
}
567+
return version, found
542568
}
543569

544570
// If we have QUIC addresses, we don't want to dial WebTransport addresses.
@@ -548,7 +574,7 @@ func isQUIC(addr ma.Multiaddr) bool {
548574
func maybeRemoveWebTransportAddrs(addrs []ma.Multiaddr) []ma.Multiaddr {
549575
var hasQuic, hasWebTransport bool
550576
for _, addr := range addrs {
551-
if isQUIC(addr) {
577+
if _, isQuic := quicVersion(addr); isQuic {
552578
hasQuic = true
553579
}
554580
if isWebTransport(addr) {
@@ -568,3 +594,38 @@ func maybeRemoveWebTransportAddrs(addrs []ma.Multiaddr) []ma.Multiaddr {
568594
}
569595
return addrs[:c]
570596
}
597+
598+
// If we have QUIC V1 addresses, we don't want to dial QUIC draft29 addresses.
599+
// This is a similar hack to the above. If we add one more hack like this, let's
600+
// define a `Filterer` interface like the `Resolver` interface that transports
601+
// can optionally implement if they want to filter the multiaddrs.
602+
//
603+
// This mutates the input
604+
func maybeRemoveQUICDraft29(addrs []ma.Multiaddr) []ma.Multiaddr {
605+
var hasQuicV1, hasQuicDraft29 bool
606+
for _, addr := range addrs {
607+
v, isQuic := quicVersion(addr)
608+
if !isQuic {
609+
continue
610+
}
611+
612+
if v == quic.Version1 {
613+
hasQuicV1 = true
614+
}
615+
if v == quic.VersionDraft29 {
616+
hasQuicDraft29 = true
617+
}
618+
}
619+
if !hasQuicDraft29 || !hasQuicV1 {
620+
return addrs
621+
}
622+
var c int
623+
for _, addr := range addrs {
624+
if v, isQuic := quicVersion(addr); isQuic && v == quic.VersionDraft29 {
625+
continue
626+
}
627+
addrs[c] = addr
628+
c++
629+
}
630+
return addrs[:c]
631+
}

p2p/net/swarm/swarm_dial_test.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,23 @@ func TestAddrResolutionRecursive(t *testing.T) {
196196
func TestRemoveWebTransportAddrs(t *testing.T) {
197197
tcpAddr := ma.StringCast("/ip4/9.5.6.4/tcp/1234")
198198
quicAddr := ma.StringCast("/ip4/1.2.3.4/udp/443/quic")
199-
webtransportAddr := ma.StringCast("/ip4/1.2.3.4/udp/443/quic/webtransport")
199+
webtransportAddr := ma.StringCast("/ip4/1.2.3.4/udp/443/quic-v1/webtransport")
200200

201201
require.Equal(t, []ma.Multiaddr{tcpAddr, quicAddr}, maybeRemoveWebTransportAddrs([]ma.Multiaddr{tcpAddr, quicAddr}))
202202
require.Equal(t, []ma.Multiaddr{tcpAddr, webtransportAddr}, maybeRemoveWebTransportAddrs([]ma.Multiaddr{tcpAddr, webtransportAddr}))
203203
require.Equal(t, []ma.Multiaddr{tcpAddr, quicAddr}, maybeRemoveWebTransportAddrs([]ma.Multiaddr{tcpAddr, webtransportAddr, quicAddr}))
204204
require.Equal(t, []ma.Multiaddr{quicAddr}, maybeRemoveWebTransportAddrs([]ma.Multiaddr{quicAddr, webtransportAddr}))
205205
require.Equal(t, []ma.Multiaddr{webtransportAddr}, maybeRemoveWebTransportAddrs([]ma.Multiaddr{webtransportAddr}))
206206
}
207+
208+
func TestRemoveQuicDraft29(t *testing.T) {
209+
tcpAddr := ma.StringCast("/ip4/9.5.6.4/tcp/1234")
210+
quicDraft29Addr := ma.StringCast("/ip4/1.2.3.4/udp/443/quic")
211+
quicV1Addr := ma.StringCast("/ip4/1.2.3.4/udp/443/quic-v1")
212+
213+
require.Equal(t, []ma.Multiaddr{tcpAddr, quicV1Addr}, maybeRemoveQUICDraft29([]ma.Multiaddr{tcpAddr, quicV1Addr}))
214+
require.Equal(t, []ma.Multiaddr{tcpAddr, quicDraft29Addr}, maybeRemoveQUICDraft29([]ma.Multiaddr{tcpAddr, quicDraft29Addr}))
215+
require.Equal(t, []ma.Multiaddr{tcpAddr, quicV1Addr}, maybeRemoveQUICDraft29([]ma.Multiaddr{tcpAddr, quicDraft29Addr, quicV1Addr}))
216+
require.Equal(t, []ma.Multiaddr{quicV1Addr}, maybeRemoveQUICDraft29([]ma.Multiaddr{quicV1Addr, quicDraft29Addr}))
217+
require.Equal(t, []ma.Multiaddr{quicDraft29Addr}, maybeRemoveQUICDraft29([]ma.Multiaddr{quicDraft29Addr}))
218+
}

p2p/net/swarm/swarm_listen.go

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,27 @@ func (s *Swarm) Listen(addrs ...ma.Multiaddr) error {
3737
return nil
3838
}
3939

40-
// ListenClose stop and delete listeners for all of the given addresses.
40+
// ListenClose stop and delete listeners for all of the given addresses. If an
41+
// any address belongs to one of the addreses a Listener provides, then the
42+
// Listener will close for *all* addresses it provides. For example if you close
43+
// and address with `/quic`, then the QUIC listener will close and also close
44+
// any `/quic-v1` address.
4145
func (s *Swarm) ListenClose(addrs ...ma.Multiaddr) {
42-
var listenersToClose []transport.Listener
46+
listenersToClose := make(map[transport.Listener]struct{}, len(addrs))
4347

4448
s.listeners.Lock()
4549
for l := range s.listeners.m {
46-
if !containsMultiaddr(addrs, l.Multiaddr()) {
50+
if !containsSomeMultiaddr(addrs, l.Multiaddrs()) {
4751
continue
4852
}
4953

5054
delete(s.listeners.m, l)
51-
listenersToClose = append(listenersToClose, l)
55+
listenersToClose[l] = struct{}{}
5256
}
5357
s.listeners.cacheEOL = time.Time{}
5458
s.listeners.Unlock()
5559

56-
for _, l := range listenersToClose {
60+
for l := range listenersToClose {
5761
l.Close()
5862
}
5963
}
@@ -92,11 +96,13 @@ func (s *Swarm) AddListenAddr(a ma.Multiaddr) error {
9296
s.listeners.cacheEOL = time.Time{}
9397
s.listeners.Unlock()
9498

95-
maddr := list.Multiaddr()
99+
maddrs := list.Multiaddrs()
96100

97101
// signal to our notifiees on listen.
98102
s.notifyAll(func(n network.Notifiee) {
99-
n.Listen(s, maddr)
103+
for _, maddr := range maddrs {
104+
n.Listen(s, maddr)
105+
}
100106
})
101107

102108
go func() {
@@ -116,7 +122,9 @@ func (s *Swarm) AddListenAddr(a ma.Multiaddr) error {
116122

117123
// signal to our notifiees on listen close.
118124
s.notifyAll(func(n network.Notifiee) {
119-
n.ListenClose(s, maddr)
125+
for _, maddr := range maddrs {
126+
n.ListenClose(s, maddr)
127+
}
120128
})
121129
s.refs.Done()
122130
}()
@@ -147,11 +155,16 @@ func (s *Swarm) AddListenAddr(a ma.Multiaddr) error {
147155
return nil
148156
}
149157

150-
func containsMultiaddr(addrs []ma.Multiaddr, addr ma.Multiaddr) bool {
151-
for _, a := range addrs {
152-
if addr == a {
158+
func containsSomeMultiaddr(hayStack []ma.Multiaddr, needles []ma.Multiaddr) bool {
159+
seenSet := make(map[string]struct{}, len(needles))
160+
for _, a := range needles {
161+
seenSet[string(a.Bytes())] = struct{}{}
162+
}
163+
for _, a := range hayStack {
164+
if _, found := seenSet[string(a.Bytes())]; found {
153165
return true
154166
}
155167
}
156168
return false
169+
157170
}

0 commit comments

Comments
 (0)