Browse Source

don't blackhole packets when gating via InterceptAccept

pull/1424/head
Marten Seemann 4 years ago
parent
commit
f3091f346a
  1. 35
      p2p/transport/quic/conn_test.go
  2. 58
      p2p/transport/quic/filtered_conn.go
  3. 2
      p2p/transport/quic/listener.go
  4. 3
      p2p/transport/quic/reuse.go
  5. 16
      p2p/transport/quic/transport.go

35
p2p/transport/quic/conn_test.go

@ -13,7 +13,6 @@ import (
gomock "github.com/golang/mock/gomock"
ic "github.com/libp2p/go-libp2p-core/crypto"
n "github.com/libp2p/go-libp2p-core/network"
"github.com/libp2p/go-libp2p-core/peer"
tpt "github.com/libp2p/go-libp2p-core/transport"
@ -170,30 +169,37 @@ var _ = Describe("Connection", func() {
It("gates accepted connections", func() {
cg := NewMockConnectionGater(mockCtrl)
var allow bool
cg.EXPECT().InterceptAccept(gomock.Any()).DoAndReturn(func(n.ConnMultiaddrs) bool {
return allow
}).AnyTimes()
cg.EXPECT().InterceptAccept(gomock.Any())
serverTransport, err := NewTransport(serverKey, nil, cg)
Expect(err).ToNot(HaveOccurred())
ln := runServer(serverTransport, "/ip4/127.0.0.1/udp/0/quic")
defer ln.Close()
accepted := make(chan struct{})
go func() {
defer GinkgoRecover()
defer close(accepted)
_, err := ln.Accept()
Expect(err).ToNot(HaveOccurred())
}()
clientTransport, err := NewTransport(clientKey, nil, nil)
Expect(err).ToNot(HaveOccurred())
// make sure that connection attempts fails
clientTransport.(*transport).clientConfig.HandshakeTimeout = 250 * time.Millisecond
_, err = clientTransport.Dial(context.Background(), ln.Multiaddr(), serverID)
conn, err := clientTransport.Dial(context.Background(), ln.Multiaddr(), serverID)
Expect(err).ToNot(HaveOccurred())
_, err = conn.AcceptStream()
Expect(err).To(HaveOccurred())
Expect(err.(net.Error).Timeout()).To(BeTrue())
Expect(err.Error()).To(ContainSubstring("connection gated"))
// now allow the address and make sure the connection goes through
allow = true
cg.EXPECT().InterceptAccept(gomock.Any()).Return(true)
cg.EXPECT().InterceptSecured(gomock.Any(), gomock.Any(), gomock.Any()).Return(true)
clientTransport.(*transport).clientConfig.HandshakeTimeout = 2 * time.Second
conn, err := clientTransport.Dial(context.Background(), ln.Multiaddr(), serverID)
conn, err = clientTransport.Dial(context.Background(), ln.Multiaddr(), serverID)
Expect(err).ToNot(HaveOccurred())
conn.Close()
defer conn.Close()
Eventually(accepted).Should(BeClosed())
})
It("gates secured connections", func() {
@ -203,18 +209,19 @@ var _ = Describe("Connection", func() {
defer ln.Close()
cg := NewMockConnectionGater(mockCtrl)
cg.EXPECT().InterceptAccept(gomock.Any()).Return(true).AnyTimes()
cg.EXPECT().InterceptAccept(gomock.Any()).Return(true)
cg.EXPECT().InterceptSecured(gomock.Any(), gomock.Any(), gomock.Any())
clientTransport, err := NewTransport(clientKey, nil, cg)
Expect(err).ToNot(HaveOccurred())
// make sure that connection attempts fails
clientTransport.(*transport).clientConfig.HandshakeTimeout = 250 * time.Millisecond
_, err = clientTransport.Dial(context.Background(), ln.Multiaddr(), serverID)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("connection gated"))
// now allow the peerId and make sure the connection goes through
cg.EXPECT().InterceptAccept(gomock.Any()).Return(true)
cg.EXPECT().InterceptSecured(gomock.Any(), gomock.Any(), gomock.Any()).Return(true)
clientTransport.(*transport).clientConfig.HandshakeTimeout = 2 * time.Second
conn, err := clientTransport.Dial(context.Background(), ln.Multiaddr(), serverID)

58
p2p/transport/quic/filtered_conn.go

@ -1,58 +0,0 @@
package libp2pquic
import (
"net"
"github.com/libp2p/go-libp2p-core/connmgr"
ma "github.com/multiformats/go-multiaddr"
)
type connAddrs struct {
lmAddr ma.Multiaddr
rmAddr ma.Multiaddr
}
func (c *connAddrs) LocalMultiaddr() ma.Multiaddr {
return c.lmAddr
}
func (c *connAddrs) RemoteMultiaddr() ma.Multiaddr {
return c.rmAddr
}
type filteredConn struct {
net.PacketConn
lmAddr ma.Multiaddr
gater connmgr.ConnectionGater
}
func newFilteredConn(c net.PacketConn, gater connmgr.ConnectionGater) net.PacketConn {
lmAddr, err := toQuicMultiaddr(c.LocalAddr())
if err != nil {
panic(err)
}
return &filteredConn{PacketConn: c, gater: gater, lmAddr: lmAddr}
}
func (c *filteredConn) ReadFrom(b []byte) (n int, addr net.Addr, rerr error) {
for {
n, addr, rerr = c.PacketConn.ReadFrom(b)
// Short Header packet, see https://tools.ietf.org/html/draft-ietf-quic-invariants-07#section-4.2.
if n < 1 || b[0]&0x80 == 0 {
return
}
rmAddr, err := toQuicMultiaddr(addr)
if err != nil {
panic(err)
}
connAddrs := &connAddrs{lmAddr: c.lmAddr, rmAddr: rmAddr}
if c.gater.InterceptAccept(connAddrs) {
return
}
}
}

2
p2p/transport/quic/listener.go

@ -68,7 +68,7 @@ func (l *listener) Accept() (tpt.CapableConn, error) {
sess.CloseWithError(0, err.Error())
continue
}
if l.transport.gater != nil && !l.transport.gater.InterceptSecured(n.DirInbound, conn.remotePeerID, conn) {
if l.transport.gater != nil && !(l.transport.gater.InterceptAccept(conn) && l.transport.gater.InterceptSecured(n.DirInbound, conn.remotePeerID, conn)) {
sess.CloseWithError(errorCodeConnectionGating, "connection gated")
continue
}

3
p2p/transport/quic/reuse.go

@ -25,9 +25,6 @@ type reuseConn struct {
}
func newReuseConn(conn net.PacketConn, gater connmgr.ConnectionGater) *reuseConn {
if gater != nil {
conn = newFilteredConn(conn, gater)
}
return &reuseConn{PacketConn: conn}
}

16
p2p/transport/quic/transport.go

@ -182,14 +182,7 @@ func (t *transport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (tp
sess.CloseWithError(0, "")
return nil, err
}
connaddrs := &connAddrs{lmAddr: localMultiaddr, rmAddr: remoteMultiaddr}
if t.gater != nil && !t.gater.InterceptSecured(n.DirOutbound, p, connaddrs) {
sess.CloseWithError(errorCodeConnectionGating, "connection gated")
return nil, fmt.Errorf("secured connection gated")
}
return &conn{
conn := &conn{
sess: sess,
transport: t,
privKey: t.privKey,
@ -198,7 +191,12 @@ func (t *transport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (tp
remotePubKey: remotePubKey,
remotePeerID: p,
remoteMultiaddr: remoteMultiaddr,
}, nil
}
if t.gater != nil && !(t.gater.InterceptAccept(conn) && t.gater.InterceptSecured(n.DirOutbound, p, conn)) {
sess.CloseWithError(errorCodeConnectionGating, "connection gated")
return nil, fmt.Errorf("secured connection gated")
}
return conn, nil
}
// Don't use mafmt.QUIC as we don't want to dial DNS addresses. Just /ip{4,6}/udp/quic

Loading…
Cancel
Save