Skip to content
This repository has been archived by the owner on May 26, 2022. It is now read-only.

fix: call InterceptSecured earlier; drop conn. #156

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions conn_bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package libp2pquic

import (
"context"
"crypto/rand"
"testing"

ic "github.com/libp2p/go-libp2p-core/crypto"
"github.com/libp2p/go-libp2p-core/peer"
ma "github.com/multiformats/go-multiaddr"
)

func BenchmarkInterceptSecured(b *testing.B) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marten-seemann @Stebalien

Figured we might as well write a little benchmark to know how much state is being constructed by QUIC after a successful handshake, which we could prevent by blocking denied connections earlier.

TL;DR: we're looking at a 20% difference in allocs (1000+ allocs), and 10% difference in bytes (50kB per op).

This PR

goos: darwin
goarch: amd64
pkg: github.com/libp2p/go-libp2p-quic-transport
BenchmarkInterceptSecured
BenchmarkInterceptSecured-8          694       1673938 ns/op      497751 B/op       4394 allocs/op
PASS

Process finished with exit code 0

PR #157

goos: darwin
goarch: amd64
pkg: github.com/libp2p/go-libp2p-quic-transport
BenchmarkInterceptSecured
BenchmarkInterceptSecured-8          670       1760618 ns/op      544251 B/op       5436 allocs/op
PASS

Process finished with exit code 0

priv1, _, _ := ic.GenerateEd25519Key(rand.Reader)
priv2, _, _ := ic.GenerateEd25519Key(rand.Reader)

id2, _ := peer.IDFromPrivateKey(priv2)
gater := &mockGater{blockedPeer: id2, blockAccept: false}

t1, _ := NewTransport(priv1, nil, gater)
t2, _ := NewTransport(priv2, nil, nil)

laddr, _ := ma.NewMultiaddr("/ip4/127.0.0.1/udp/0/quic")
l, _ := t2.Listen(laddr)
defer l.Close()

go func() {
for {
conn, err := l.Accept()
if err != nil {
return
}
_ = conn.Close()
}
}()

raddr := l.Multiaddr()

b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_, err := t1.Dial(context.Background(), raddr, id2)
if err == nil {
b.Fatal("expected error")
}
}
}
17 changes: 8 additions & 9 deletions conn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,20 @@ import (

quicproxy "github.com/lucas-clemente/quic-go/integrationtests/tools/proxy"
ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr-net"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

type mockGater struct {
lk sync.Mutex
acceptAll bool
blockAccept bool
blockedPeer peer.ID
}

func (c *mockGater) InterceptAccept(addrs network.ConnMultiaddrs) bool {
func (c *mockGater) InterceptAccept(_ network.ConnMultiaddrs) (allow bool) {
c.lk.Lock()
defer c.lk.Unlock()
return c.acceptAll || !manet.IsIPLoopback(addrs.RemoteMultiaddr())
return !c.blockAccept
}

func (c *mockGater) InterceptPeerDial(p peer.ID) (allow bool) {
Expand Down Expand Up @@ -199,10 +197,10 @@ var _ = Describe("Connection", func() {
Eventually(done).Should(BeClosed())
})

It("gates accepted connections", func() {
It("gates allows/denies connections on accept", func() {
testMA, err := ma.NewMultiaddr("/ip4/127.0.0.1/udp/1234/quic")
Expect(err).ToNot(HaveOccurred())
cg := &mockGater{}
cg := &mockGater{blockAccept: true}
Expect(cg.InterceptAccept(&connAddrs{rmAddr: testMA})).To(BeFalse())

serverTransport, err := NewTransport(serverKey, nil, cg)
Expand All @@ -222,8 +220,9 @@ var _ = Describe("Connection", func() {
// now allow the address and make sure the connection goes through
clientTransport.(*transport).clientConfig.HandshakeTimeout = 2 * time.Second
cg.lk.Lock()
cg.acceptAll = true
cg.blockAccept = false
cg.lk.Unlock()

conn, err := clientTransport.Dial(context.Background(), ln.Multiaddr(), serverID)
Expect(err).ToNot(HaveOccurred())
conn.Close()
Expand All @@ -235,7 +234,7 @@ var _ = Describe("Connection", func() {
ln := runServer(serverTransport, "/ip4/127.0.0.1/udp/0/quic")
defer ln.Close()

cg := &mockGater{acceptAll: true, blockedPeer: serverID}
cg := &mockGater{blockedPeer: serverID}
clientTransport, err := NewTransport(clientKey, nil, cg)
Expect(err).ToNot(HaveOccurred())

Expand Down
3 changes: 1 addition & 2 deletions filtered_conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ func (c *filteredConn) ReadFrom(b []byte) (n int, addr net.Addr, rerr error) {
}

connAddrs := &connAddrs{lmAddr: c.lmAddr, rmAddr: rmAddr}

if c.gater.InterceptAccept(connAddrs) {
if c.gater != nil && c.gater.InterceptAccept(connAddrs) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed a potential nil pointer panic here.

return
}
}
Expand Down
63 changes: 41 additions & 22 deletions transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,31 @@ package libp2pquic

import (
"context"
"crypto/x509"
"errors"
"fmt"
"io"
"net"

"github.com/libp2p/go-libp2p-core/connmgr"
n "github.com/libp2p/go-libp2p-core/network"

"github.com/minio/sha256-simd"
"golang.org/x/crypto/hkdf"

logging "github.com/ipfs/go-log"
"github.com/libp2p/go-libp2p-core/connmgr"
ic "github.com/libp2p/go-libp2p-core/crypto"
"github.com/libp2p/go-libp2p-core/network"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/libp2p/go-libp2p-core/pnet"
tpt "github.com/libp2p/go-libp2p-core/transport"

p2ptls "github.com/libp2p/go-libp2p-tls"
quic "github.com/lucas-clemente/quic-go"

ma "github.com/multiformats/go-multiaddr"
mafmt "github.com/multiformats/go-multiaddr-fmt"
manet "github.com/multiformats/go-multiaddr-net"

logging "github.com/ipfs/go-log"

"github.com/lucas-clemente/quic-go"
"github.com/minio/sha256-simd"
)

var log = logging.Logger("quic-transport")
Expand Down Expand Up @@ -141,28 +145,55 @@ func NewTransport(key ic.PrivKey, psk pnet.PSK, gater connmgr.ConnectionGater) (

// Dial dials a new QUIC connection
func (t *transport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (tpt.CapableConn, error) {
network, host, err := manet.DialArgs(raddr)
netwrk, host, err := manet.DialArgs(raddr)
if err != nil {
return nil, err
}
addr, err := net.ResolveUDPAddr(network, host)

addr, err := net.ResolveUDPAddr(netwrk, host)
if err != nil {
return nil, err
}

remoteMultiaddr, err := toQuicMultiaddr(addr)
if err != nil {
return nil, err
}
tlsConf, keyCh := t.identity.ConfigForPeer(p)
pconn, err := t.connManager.Dial(network, addr)

pconn, err := t.connManager.Dial(netwrk, addr)
if err != nil {
return nil, err
}

localMultiaddr, err := toQuicMultiaddr(pconn.LocalAddr())
if err != nil {
pconn.DecreaseCount()
return nil, err
}

connaddrs := &connAddrs{lmAddr: localMultiaddr, rmAddr: remoteMultiaddr}

tlsConf, keyCh := t.identity.ConfigForPeer(p)
vpc := tlsConf.VerifyPeerCertificate
tlsConf.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
// delegate to the original peer certificate verification function.
if err := vpc(rawCerts, verifiedChains); err != nil {
return err
}

if t.gater != nil && !t.gater.InterceptSecured(network.DirOutbound, p, connaddrs) {
return fmt.Errorf("gater denied secured connection to peer: %s", p.Pretty())
}
return nil
}

// now perform the QUIC dial.
sess, err := quic.DialContext(ctx, pconn, addr, host, tlsConf, t.clientConfig)
if err != nil {
pconn.DecreaseCount()
return nil, err
}

// Should be ready by this point, don't block.
var remotePubKey ic.PubKey
select {
Expand All @@ -178,18 +209,6 @@ func (t *transport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (tp
pconn.DecreaseCount()
}()

localMultiaddr, err := toQuicMultiaddr(pconn.LocalAddr())
if err != nil {
pconn.DecreaseCount()
return nil, err
}

connaddrs := &connAddrs{lmAddr: localMultiaddr, rmAddr: remoteMultiaddr}
if t.gater != nil && !t.gater.InterceptSecured(n.DirOutbound, p, connaddrs) {
pconn.DecreaseCount()
return nil, fmt.Errorf("secured connection gated")
}

return &conn{
sess: sess,
transport: t,
Expand Down