Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

quic: add DisableReuseport option #1476

Merged
merged 3 commits into from
Aug 19, 2022
Merged
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
11 changes: 10 additions & 1 deletion p2p/transport/quic/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package libp2pquic

import (
"context"
"net"

ic "github.com/libp2p/go-libp2p-core/crypto"
"github.com/libp2p/go-libp2p-core/network"
Expand All @@ -12,9 +13,17 @@ import (
ma "github.com/multiformats/go-multiaddr"
)

type pConn interface {
net.PacketConn

// count conn reference
DecreaseCount()
IncreaseCount()
}

type conn struct {
quicConn quic.Connection
pconn *reuseConn
pconn pConn
transport *transport
scope network.ConnManagementScope

Expand Down
126 changes: 106 additions & 20 deletions p2p/transport/quic/conn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ import (

//go:generate sh -c "mockgen -package libp2pquic -destination mock_connection_gater_test.go github.com/libp2p/go-libp2p-core/connmgr ConnectionGater && goimports -w mock_connection_gater_test.go"

type connTestCase struct {
Name string
Options []Option
}

var connTestCases = []*connTestCase{
{"reuseport_on", []Option{}},
{"reuseport_off", []Option{DisableReuseport()}},
}

func createPeer(t *testing.T) (peer.ID, ic.PrivKey) {
var priv ic.PrivKey
var err error
Expand All @@ -52,20 +62,29 @@ func createPeer(t *testing.T) (peer.ID, ic.PrivKey) {

func runServer(t *testing.T, tr tpt.Transport, addr string) tpt.Listener {
t.Helper()

ln, err := tr.Listen(ma.StringCast(addr))
require.NoError(t, err)
return ln
}

func TestHandshake(t *testing.T) {
for _, tc := range connTestCases {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please introduce a separate function, so you don't have to indent everything here.

t.Run(tc.Name, func(t *testing.T) {
testHandshake(t, tc)
})
}
}

func testHandshake(t *testing.T, tc *connTestCase) {
serverID, serverKey := createPeer(t)
clientID, clientKey := createPeer(t)
serverTransport, err := NewTransport(serverKey, nil, nil, nil)
serverTransport, err := NewTransport(serverKey, nil, nil, nil, tc.Options...)
require.NoError(t, err)
defer serverTransport.(io.Closer).Close()

handshake := func(t *testing.T, ln tpt.Listener) {
clientTransport, err := NewTransport(clientKey, nil, nil, nil)
clientTransport, err := NewTransport(clientKey, nil, nil, nil, tc.Options...)
require.NoError(t, err)
defer clientTransport.(io.Closer).Close()
conn, err := clientTransport.Dial(context.Background(), ln.Multiaddr(), serverID)
Expand Down Expand Up @@ -100,22 +119,30 @@ func TestHandshake(t *testing.T) {
}

func TestResourceManagerSuccess(t *testing.T) {
for _, tc := range connTestCases {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here.

t.Run(tc.Name, func(t *testing.T) {
testResourceManagerSuccess(t, tc)
})
}
}

func testResourceManagerSuccess(t *testing.T, tc *connTestCase) {
serverID, serverKey := createPeer(t)
clientID, clientKey := createPeer(t)

ctrl := gomock.NewController(t)
defer ctrl.Finish()

serverRcmgr := mocknetwork.NewMockResourceManager(ctrl)
serverTransport, err := NewTransport(serverKey, nil, nil, serverRcmgr)
serverTransport, err := NewTransport(serverKey, nil, nil, serverRcmgr, tc.Options...)
require.NoError(t, err)
defer serverTransport.(io.Closer).Close()
ln, err := serverTransport.Listen(ma.StringCast("/ip4/127.0.0.1/udp/0/quic"))
require.NoError(t, err)
defer ln.Close()

clientRcmgr := mocknetwork.NewMockResourceManager(ctrl)
clientTransport, err := NewTransport(clientKey, nil, nil, clientRcmgr)
clientTransport, err := NewTransport(clientKey, nil, nil, clientRcmgr, tc.Options...)
require.NoError(t, err)
defer clientTransport.(io.Closer).Close()

Expand Down Expand Up @@ -143,12 +170,20 @@ func TestResourceManagerSuccess(t *testing.T) {
}

func TestResourceManagerDialDenied(t *testing.T) {
for _, tc := range connTestCases {
t.Run(tc.Name, func(t *testing.T) {
testResourceManagerDialDenied(t, tc)
})
}
}

func testResourceManagerDialDenied(t *testing.T, tc *connTestCase) {
_, clientKey := createPeer(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()

rcmgr := mocknetwork.NewMockResourceManager(ctrl)
clientTransport, err := NewTransport(clientKey, nil, nil, rcmgr)
clientTransport, err := NewTransport(clientKey, nil, nil, rcmgr, tc.Options...)
require.NoError(t, err)
defer clientTransport.(io.Closer).Close()

Expand All @@ -163,16 +198,25 @@ func TestResourceManagerDialDenied(t *testing.T) {

_, err = clientTransport.Dial(context.Background(), target, p)
require.ErrorIs(t, err, rerr)

}

func TestResourceManagerAcceptDenied(t *testing.T) {
for _, tc := range connTestCases {
t.Run(tc.Name, func(t *testing.T) {
testResourceManagerAcceptDenied(t, tc)
})
}
}

func testResourceManagerAcceptDenied(t *testing.T, tc *connTestCase) {
serverID, serverKey := createPeer(t)
clientID, clientKey := createPeer(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()

clientRcmgr := mocknetwork.NewMockResourceManager(ctrl)
clientTransport, err := NewTransport(clientKey, nil, nil, clientRcmgr)
clientTransport, err := NewTransport(clientKey, nil, nil, clientRcmgr, tc.Options...)
require.NoError(t, err)
defer clientTransport.(io.Closer).Close()

Expand All @@ -184,7 +228,7 @@ func TestResourceManagerAcceptDenied(t *testing.T) {
serverConnScope.EXPECT().SetPeer(clientID).Return(rerr),
serverConnScope.EXPECT().Done(),
)
serverTransport, err := NewTransport(serverKey, nil, nil, serverRcmgr)
serverTransport, err := NewTransport(serverKey, nil, nil, serverRcmgr, tc.Options...)
require.NoError(t, err)
defer serverTransport.(io.Closer).Close()
ln, err := serverTransport.Listen(ma.StringCast("/ip4/127.0.0.1/udp/0/quic"))
Expand Down Expand Up @@ -216,16 +260,24 @@ func TestResourceManagerAcceptDenied(t *testing.T) {
}

func TestStreams(t *testing.T) {
for _, tc := range connTestCases {
t.Run(tc.Name, func(t *testing.T) {
testStreams(t, tc)
})
}
}

func testStreams(t *testing.T, tc *connTestCase) {
serverID, serverKey := createPeer(t)
_, clientKey := createPeer(t)

serverTransport, err := NewTransport(serverKey, nil, nil, nil)
serverTransport, err := NewTransport(serverKey, nil, nil, nil, tc.Options...)
require.NoError(t, err)
defer serverTransport.(io.Closer).Close()
ln := runServer(t, serverTransport, "/ip4/127.0.0.1/udp/0/quic")
defer ln.Close()

clientTransport, err := NewTransport(clientKey, nil, nil, nil)
clientTransport, err := NewTransport(clientKey, nil, nil, nil, tc.Options...)
require.NoError(t, err)
defer clientTransport.(io.Closer).Close()
conn, err := clientTransport.Dial(context.Background(), ln.Multiaddr(), serverID)
Expand All @@ -248,16 +300,24 @@ func TestStreams(t *testing.T) {
}

func TestHandshakeFailPeerIDMismatch(t *testing.T) {
for _, tc := range connTestCases {
t.Run(tc.Name, func(t *testing.T) {
testHandshakeFailPeerIDMismatch(t, tc)
})
}
}

func testHandshakeFailPeerIDMismatch(t *testing.T, tc *connTestCase) {
_, serverKey := createPeer(t)
_, clientKey := createPeer(t)
thirdPartyID, _ := createPeer(t)

serverTransport, err := NewTransport(serverKey, nil, nil, nil)
serverTransport, err := NewTransport(serverKey, nil, nil, nil, tc.Options...)
require.NoError(t, err)
defer serverTransport.(io.Closer).Close()
ln := runServer(t, serverTransport, "/ip4/127.0.0.1/udp/0/quic")

clientTransport, err := NewTransport(clientKey, nil, nil, nil)
clientTransport, err := NewTransport(clientKey, nil, nil, nil, tc.Options...)
require.NoError(t, err)
// dial, but expect the wrong peer ID
_, err = clientTransport.Dial(context.Background(), ln.Multiaddr(), thirdPartyID)
Expand All @@ -282,6 +342,14 @@ func TestHandshakeFailPeerIDMismatch(t *testing.T) {
}

func TestConnectionGating(t *testing.T) {
for _, tc := range connTestCases {
t.Run(tc.Name, func(t *testing.T) {
testConnectionGating(t, tc)
})
}
}

func testConnectionGating(t *testing.T, tc *connTestCase) {
serverID, serverKey := createPeer(t)
_, clientKey := createPeer(t)

Expand All @@ -290,7 +358,7 @@ func TestConnectionGating(t *testing.T) {
cg := NewMockConnectionGater(mockCtrl)

t.Run("accepted connections", func(t *testing.T) {
serverTransport, err := NewTransport(serverKey, nil, cg, nil)
serverTransport, err := NewTransport(serverKey, nil, cg, nil, tc.Options...)
defer serverTransport.(io.Closer).Close()
require.NoError(t, err)
ln := runServer(t, serverTransport, "/ip4/127.0.0.1/udp/0/quic")
Expand All @@ -305,7 +373,7 @@ func TestConnectionGating(t *testing.T) {
require.NoError(t, err)
}()

clientTransport, err := NewTransport(clientKey, nil, nil, nil)
clientTransport, err := NewTransport(clientKey, nil, nil, nil, tc.Options...)
require.NoError(t, err)
defer clientTransport.(io.Closer).Close()
// make sure that connection attempts fails
Expand Down Expand Up @@ -335,7 +403,7 @@ func TestConnectionGating(t *testing.T) {
})

t.Run("secured connections", func(t *testing.T) {
serverTransport, err := NewTransport(serverKey, nil, nil, nil)
serverTransport, err := NewTransport(serverKey, nil, nil, nil, tc.Options...)
require.NoError(t, err)
defer serverTransport.(io.Closer).Close()
ln := runServer(t, serverTransport, "/ip4/127.0.0.1/udp/0/quic")
Expand All @@ -344,7 +412,7 @@ func TestConnectionGating(t *testing.T) {
cg := NewMockConnectionGater(mockCtrl)
cg.EXPECT().InterceptSecured(gomock.Any(), gomock.Any(), gomock.Any())

clientTransport, err := NewTransport(clientKey, nil, cg, nil)
clientTransport, err := NewTransport(clientKey, nil, cg, nil, tc.Options...)
require.NoError(t, err)
defer clientTransport.(io.Closer).Close()

Expand All @@ -363,16 +431,24 @@ func TestConnectionGating(t *testing.T) {
}

func TestDialTwo(t *testing.T) {
for _, tc := range connTestCases {
t.Run(tc.Name, func(t *testing.T) {
testDialTwo(t, tc)
})
}
}

func testDialTwo(t *testing.T, tc *connTestCase) {
serverID, serverKey := createPeer(t)
_, clientKey := createPeer(t)
serverID2, serverKey2 := createPeer(t)

serverTransport, err := NewTransport(serverKey, nil, nil, nil)
serverTransport, err := NewTransport(serverKey, nil, nil, nil, tc.Options...)
require.NoError(t, err)
defer serverTransport.(io.Closer).Close()
ln1 := runServer(t, serverTransport, "/ip4/127.0.0.1/udp/0/quic")
defer ln1.Close()
serverTransport2, err := NewTransport(serverKey2, nil, nil, nil)
serverTransport2, err := NewTransport(serverKey2, nil, nil, nil, tc.Options...)
require.NoError(t, err)
defer serverTransport2.(io.Closer).Close()
ln2 := runServer(t, serverTransport2, "/ip4/127.0.0.1/udp/0/quic")
Expand All @@ -398,7 +474,7 @@ func TestDialTwo(t *testing.T) {
}
}()

clientTransport, err := NewTransport(clientKey, nil, nil, nil)
clientTransport, err := NewTransport(clientKey, nil, nil, nil, tc.Options...)
require.NoError(t, err)
defer clientTransport.(io.Closer).Close()
c1, err := clientTransport.Dial(context.Background(), ln1.Multiaddr(), serverID)
Expand Down Expand Up @@ -435,6 +511,14 @@ func TestDialTwo(t *testing.T) {
}

func TestStatelessReset(t *testing.T) {
for _, tc := range connTestCases {
t.Run(tc.Name, func(t *testing.T) {
testStatelessReset(t, tc)
})
}
}

func testStatelessReset(t *testing.T, tc *connTestCase) {
origGarbageCollectInterval := garbageCollectInterval
origMaxUnusedDuration := maxUnusedDuration

Expand All @@ -449,7 +533,7 @@ func TestStatelessReset(t *testing.T) {
serverID, serverKey := createPeer(t)
_, clientKey := createPeer(t)

serverTransport, err := NewTransport(serverKey, nil, nil, nil)
serverTransport, err := NewTransport(serverKey, nil, nil, nil, tc.Options...)
require.NoError(t, err)
defer serverTransport.(io.Closer).Close()
ln := runServer(t, serverTransport, "/ip4/127.0.0.1/udp/0/quic")
Expand All @@ -466,7 +550,7 @@ func TestStatelessReset(t *testing.T) {
defer proxy.Close()

// establish a connection
clientTransport, err := NewTransport(clientKey, nil, nil, nil)
clientTransport, err := NewTransport(clientKey, nil, nil, nil, tc.Options...)
require.NoError(t, err)
defer clientTransport.(io.Closer).Close()
proxyAddr, err := toQuicMultiaddr(proxy.LocalAddr())
Expand Down Expand Up @@ -511,6 +595,8 @@ func TestStatelessReset(t *testing.T) {
require.Contains(t, rerr.Error(), "received a stateless reset")
}

// Hole punching is only expected to work with reuseport enabled.
// We don't need to test `DisableReuseport` option.
func TestHolePunching(t *testing.T) {
serverID, serverKey := createPeer(t)
clientID, clientKey := createPeer(t)
Expand Down
Loading