mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-06 21:57:36 +03:00
migrate platform-dependent UDP socket test code away from Ginkgo (#4797)
This commit is contained in:
parent
29ad06c8fa
commit
baee2b4aa0
7 changed files with 389 additions and 484 deletions
|
@ -1,80 +0,0 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: sys_conn_oob.go
|
||||
//
|
||||
// Generated by this command:
|
||||
//
|
||||
// mockgen -typed -package quic -self_package github.com/quic-go/quic-go -source sys_conn_oob.go -destination mock_batch_conn_test.go -mock_names batchConn=MockBatchConn
|
||||
//
|
||||
|
||||
// Package quic is a generated GoMock package.
|
||||
package quic
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
|
||||
gomock "go.uber.org/mock/gomock"
|
||||
ipv4 "golang.org/x/net/ipv4"
|
||||
)
|
||||
|
||||
// MockBatchConn is a mock of batchConn interface.
|
||||
type MockBatchConn struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockBatchConnMockRecorder
|
||||
isgomock struct{}
|
||||
}
|
||||
|
||||
// MockBatchConnMockRecorder is the mock recorder for MockBatchConn.
|
||||
type MockBatchConnMockRecorder struct {
|
||||
mock *MockBatchConn
|
||||
}
|
||||
|
||||
// NewMockBatchConn creates a new mock instance.
|
||||
func NewMockBatchConn(ctrl *gomock.Controller) *MockBatchConn {
|
||||
mock := &MockBatchConn{ctrl: ctrl}
|
||||
mock.recorder = &MockBatchConnMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockBatchConn) EXPECT() *MockBatchConnMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// ReadBatch mocks base method.
|
||||
func (m *MockBatchConn) ReadBatch(ms []ipv4.Message, flags int) (int, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ReadBatch", ms, flags)
|
||||
ret0, _ := ret[0].(int)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ReadBatch indicates an expected call of ReadBatch.
|
||||
func (mr *MockBatchConnMockRecorder) ReadBatch(ms, flags any) *MockBatchConnReadBatchCall {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadBatch", reflect.TypeOf((*MockBatchConn)(nil).ReadBatch), ms, flags)
|
||||
return &MockBatchConnReadBatchCall{Call: call}
|
||||
}
|
||||
|
||||
// MockBatchConnReadBatchCall wrap *gomock.Call
|
||||
type MockBatchConnReadBatchCall struct {
|
||||
*gomock.Call
|
||||
}
|
||||
|
||||
// Return rewrite *gomock.Call.Return
|
||||
func (c *MockBatchConnReadBatchCall) Return(arg0 int, arg1 error) *MockBatchConnReadBatchCall {
|
||||
c.Call = c.Call.Return(arg0, arg1)
|
||||
return c
|
||||
}
|
||||
|
||||
// Do rewrite *gomock.Call.Do
|
||||
func (c *MockBatchConnReadBatchCall) Do(f func([]ipv4.Message, int) (int, error)) *MockBatchConnReadBatchCall {
|
||||
c.Call = c.Call.Do(f)
|
||||
return c
|
||||
}
|
||||
|
||||
// DoAndReturn rewrite *gomock.Call.DoAndReturn
|
||||
func (c *MockBatchConnReadBatchCall) DoAndReturn(f func([]ipv4.Message, int) (int, error)) *MockBatchConnReadBatchCall {
|
||||
c.Call = c.Call.DoAndReturn(f)
|
||||
return c
|
||||
}
|
|
@ -61,9 +61,4 @@ type PacketHandler = packetHandler
|
|||
//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packet_handler_manager_test.go github.com/quic-go/quic-go PacketHandlerManager"
|
||||
type PacketHandlerManager = packetHandlerManager
|
||||
|
||||
// Need to use source mode for the batchConn, since reflect mode follows type aliases.
|
||||
// See https://github.com/golang/mock/issues/244 for details.
|
||||
//
|
||||
//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -package quic -self_package github.com/quic-go/quic-go -source sys_conn_oob.go -destination mock_batch_conn_test.go -mock_names batchConn=MockBatchConn"
|
||||
|
||||
//go:generate sh -c "go run go.uber.org/mock/mockgen -typed -package quic -self_package github.com/quic-go/quic-go -self_package github.com/quic-go/quic-go -destination mock_packetconn_test.go net PacketConn"
|
||||
|
|
|
@ -6,11 +6,11 @@ import (
|
|||
"errors"
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -18,64 +18,62 @@ var (
|
|||
errNotPermitted = &os.SyscallError{Syscall: "sendmsg", Err: unix.EPERM}
|
||||
)
|
||||
|
||||
var _ = Describe("forcing a change of send and receive buffer sizes", func() {
|
||||
It("forces a change of the receive buffer size", func() {
|
||||
func TestForcingReceiveBufferSize(t *testing.T) {
|
||||
if os.Getuid() != 0 {
|
||||
Skip("Must be root to force change the receive buffer size")
|
||||
t.Skip("Must be root to force change the receive buffer size")
|
||||
}
|
||||
|
||||
c, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
require.NoError(t, err)
|
||||
defer c.Close()
|
||||
syscallConn, err := c.(*net.UDPConn).SyscallConn()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
require.NoError(t, err)
|
||||
|
||||
const small = 256 << 10 // 256 KB
|
||||
Expect(forceSetReceiveBuffer(syscallConn, small)).To(Succeed())
|
||||
require.NoError(t, forceSetReceiveBuffer(syscallConn, small))
|
||||
|
||||
size, err := inspectReadBuffer(syscallConn)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// The kernel doubles this value (to allow space for bookkeeping overhead)
|
||||
Expect(size).To(Equal(2 * small))
|
||||
require.NoError(t, err)
|
||||
// the kernel doubles this value (to allow space for bookkeeping overhead)
|
||||
require.Equal(t, 2*small, size)
|
||||
|
||||
const large = 32 << 20 // 32 MB
|
||||
Expect(forceSetReceiveBuffer(syscallConn, large)).To(Succeed())
|
||||
require.NoError(t, forceSetReceiveBuffer(syscallConn, large))
|
||||
size, err = inspectReadBuffer(syscallConn)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// The kernel doubles this value (to allow space for bookkeeping overhead)
|
||||
Expect(size).To(Equal(2 * large))
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// the kernel doubles this value (to allow space for bookkeeping overhead)
|
||||
require.Equal(t, 2*large, size)
|
||||
}
|
||||
|
||||
It("forces a change of the send buffer size", func() {
|
||||
func TestForcingSendBufferSize(t *testing.T) {
|
||||
if os.Getuid() != 0 {
|
||||
Skip("Must be root to force change the send buffer size")
|
||||
t.Skip("Must be root to force change the send buffer size")
|
||||
}
|
||||
|
||||
c, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
require.NoError(t, err)
|
||||
defer c.Close()
|
||||
syscallConn, err := c.(*net.UDPConn).SyscallConn()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
require.NoError(t, err)
|
||||
|
||||
const small = 256 << 10 // 256 KB
|
||||
Expect(forceSetSendBuffer(syscallConn, small)).To(Succeed())
|
||||
require.NoError(t, forceSetSendBuffer(syscallConn, small))
|
||||
|
||||
size, err := inspectWriteBuffer(syscallConn)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// The kernel doubles this value (to allow space for bookkeeping overhead)
|
||||
Expect(size).To(Equal(2 * small))
|
||||
require.NoError(t, err)
|
||||
// the kernel doubles this value (to allow space for bookkeeping overhead)
|
||||
require.Equal(t, 2*small, size)
|
||||
|
||||
const large = 32 << 20 // 32 MB
|
||||
Expect(forceSetSendBuffer(syscallConn, large)).To(Succeed())
|
||||
require.NoError(t, forceSetSendBuffer(syscallConn, large))
|
||||
size, err = inspectWriteBuffer(syscallConn)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// The kernel doubles this value (to allow space for bookkeeping overhead)
|
||||
Expect(size).To(Equal(2 * large))
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// the kernel doubles this value (to allow space for bookkeeping overhead)
|
||||
require.Equal(t, 2*large, size)
|
||||
}
|
||||
|
||||
It("detects GSO errors", func() {
|
||||
Expect(isGSOError(errGSO)).To(BeTrue())
|
||||
Expect(isGSOError(nil)).To(BeFalse())
|
||||
Expect(isGSOError(errors.New("test"))).To(BeFalse())
|
||||
})
|
||||
})
|
||||
func TestGSOError(t *testing.T) {
|
||||
require.True(t, isGSOError(errGSO))
|
||||
require.False(t, isGSOError(nil))
|
||||
require.False(t, isGSOError(errors.New("test")))
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ func newConn(c OOBCapablePacketConn, supportsDF bool) (*oobConn, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
needsPacketInfo := false
|
||||
var needsPacketInfo bool
|
||||
if udpAddr, ok := c.LocalAddr().(*net.UDPAddr); ok && udpAddr.IP.IsUnspecified() {
|
||||
needsPacketInfo = true
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ package quic
|
|||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
|
@ -12,11 +13,255 @@ import (
|
|||
|
||||
"github.com/quic-go/quic-go/internal/protocol"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"go.uber.org/mock/gomock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func isIPv4(ip net.IP) bool { return ip.To4() != nil }
|
||||
|
||||
func runSysConnServer(t *testing.T, network string, addr *net.UDPAddr) (*net.UDPAddr, <-chan receivedPacket) {
|
||||
t.Helper()
|
||||
udpConn, err := net.ListenUDP(network, addr)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { udpConn.Close() })
|
||||
|
||||
oobConn, err := newConn(udpConn, true)
|
||||
require.NoError(t, err)
|
||||
require.True(t, oobConn.capabilities().DF)
|
||||
|
||||
packetChan := make(chan receivedPacket, 1)
|
||||
go func() {
|
||||
for {
|
||||
p, err := oobConn.ReadPacket()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
packetChan <- p
|
||||
}
|
||||
}()
|
||||
return udpConn.LocalAddr().(*net.UDPAddr), packetChan
|
||||
}
|
||||
|
||||
// sendUDPPacketWithECN opens a new UDP socket and sends one packet with the ECN set.
|
||||
// It returns the local address of the socket.
|
||||
func sendUDPPacketWithECN(t *testing.T, network string, addr *net.UDPAddr, setECN func(uintptr)) net.Addr {
|
||||
conn, err := net.DialUDP(network, nil, addr)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { conn.Close() })
|
||||
|
||||
rawConn, err := conn.SyscallConn()
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, rawConn.Control(func(fd uintptr) { setECN(fd) }))
|
||||
_, err = conn.Write([]byte("foobar"))
|
||||
require.NoError(t, err)
|
||||
return conn.LocalAddr()
|
||||
}
|
||||
|
||||
func TestReadECNFlagsIPv4(t *testing.T) {
|
||||
addr, packetChan := runSysConnServer(t, "udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
|
||||
|
||||
sentFrom := sendUDPPacketWithECN(t,
|
||||
"udp4",
|
||||
addr,
|
||||
func(fd uintptr) {
|
||||
require.NoError(t, unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_TOS, 2))
|
||||
},
|
||||
)
|
||||
|
||||
select {
|
||||
case p := <-packetChan:
|
||||
require.WithinDuration(t, time.Now(), p.rcvTime, scaleDuration(20*time.Millisecond))
|
||||
require.Equal(t, []byte("foobar"), p.data)
|
||||
require.Equal(t, sentFrom, p.remoteAddr)
|
||||
require.Equal(t, protocol.ECT0, p.ecn)
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("timeout waiting for packet")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadECNFlagsIPv6(t *testing.T) {
|
||||
addr, packetChan := runSysConnServer(t, "udp6", &net.UDPAddr{IP: net.IPv6loopback, Port: 0})
|
||||
|
||||
sentFrom := sendUDPPacketWithECN(t,
|
||||
"udp6",
|
||||
addr,
|
||||
func(fd uintptr) {
|
||||
require.NoError(t, unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_TCLASS, 3))
|
||||
},
|
||||
)
|
||||
|
||||
select {
|
||||
case p := <-packetChan:
|
||||
require.WithinDuration(t, time.Now(), p.rcvTime, scaleDuration(20*time.Millisecond))
|
||||
require.Equal(t, []byte("foobar"), p.data)
|
||||
require.Equal(t, sentFrom, p.remoteAddr)
|
||||
require.Equal(t, protocol.ECNCE, p.ecn)
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("timeout waiting for packet")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadECNFlagsDualStack(t *testing.T) {
|
||||
addr, packetChan := runSysConnServer(t, "udp", &net.UDPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 0})
|
||||
|
||||
// IPv4
|
||||
sentFrom := sendUDPPacketWithECN(t,
|
||||
"udp4",
|
||||
&net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: addr.Port},
|
||||
func(fd uintptr) {
|
||||
require.NoError(t, unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_TOS, 3))
|
||||
},
|
||||
)
|
||||
|
||||
select {
|
||||
case p := <-packetChan:
|
||||
require.True(t, isIPv4(p.remoteAddr.(*net.UDPAddr).IP))
|
||||
require.Equal(t, sentFrom.String(), p.remoteAddr.String())
|
||||
require.Equal(t, protocol.ECNCE, p.ecn)
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("timeout waiting for packet")
|
||||
}
|
||||
|
||||
// IPv6
|
||||
sentFrom = sendUDPPacketWithECN(t,
|
||||
"udp6",
|
||||
&net.UDPAddr{IP: net.IPv6loopback, Port: addr.Port},
|
||||
func(fd uintptr) {
|
||||
require.NoError(t, unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_TCLASS, 1))
|
||||
},
|
||||
)
|
||||
|
||||
select {
|
||||
case p := <-packetChan:
|
||||
require.Equal(t, sentFrom, p.remoteAddr)
|
||||
require.False(t, isIPv4(p.remoteAddr.(*net.UDPAddr).IP))
|
||||
require.Equal(t, protocol.ECT1, p.ecn)
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("timeout waiting for packet")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendPacketsWithECNOnIPv4(t *testing.T) {
|
||||
addr, packetChan := runSysConnServer(t, "udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
|
||||
|
||||
c, err := net.ListenUDP("udp4", nil)
|
||||
require.NoError(t, err)
|
||||
defer c.Close()
|
||||
|
||||
for _, val := range []protocol.ECN{protocol.ECNNon, protocol.ECT1, protocol.ECT0, protocol.ECNCE} {
|
||||
_, _, err = c.WriteMsgUDP([]byte("foobar"), appendIPv4ECNMsg([]byte{}, val), addr)
|
||||
require.NoError(t, err)
|
||||
select {
|
||||
case p := <-packetChan:
|
||||
require.Equal(t, []byte("foobar"), p.data)
|
||||
require.Equal(t, val, p.ecn)
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("timeout waiting for packet")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendPacketsWithECNOnIPv6(t *testing.T) {
|
||||
addr, packetChan := runSysConnServer(t, "udp6", &net.UDPAddr{IP: net.IPv6loopback, Port: 0})
|
||||
|
||||
c, err := net.ListenUDP("udp6", nil)
|
||||
require.NoError(t, err)
|
||||
defer c.Close()
|
||||
|
||||
for _, val := range []protocol.ECN{protocol.ECNNon, protocol.ECT1, protocol.ECT0, protocol.ECNCE} {
|
||||
_, _, err = c.WriteMsgUDP([]byte("foobar"), appendIPv6ECNMsg([]byte{}, val), addr)
|
||||
require.NoError(t, err)
|
||||
select {
|
||||
case p := <-packetChan:
|
||||
require.Equal(t, []byte("foobar"), p.data)
|
||||
require.Equal(t, val, p.ecn)
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("timeout waiting for packet")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSysConnPacketInfoIPv4(t *testing.T) {
|
||||
// need to listen on 0.0.0.0, otherwise we won't get the packet info
|
||||
addr, packetChan := runSysConnServer(t, "udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
|
||||
|
||||
conn, err := net.DialUDP("udp4", nil, addr)
|
||||
require.NoError(t, err)
|
||||
defer conn.Close()
|
||||
_, err = conn.Write([]byte("foobar"))
|
||||
require.NoError(t, err)
|
||||
|
||||
select {
|
||||
case p := <-packetChan:
|
||||
require.WithinDuration(t, time.Now(), p.rcvTime, scaleDuration(50*time.Millisecond))
|
||||
require.Equal(t, []byte("foobar"), p.data)
|
||||
require.Equal(t, conn.LocalAddr(), p.remoteAddr)
|
||||
require.True(t, p.info.addr.IsValid())
|
||||
require.True(t, isIPv4(p.info.addr.AsSlice()))
|
||||
require.Equal(t, net.IPv4(127, 0, 0, 1).String(), p.info.addr.String())
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("timeout waiting for packet")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSysConnPacketInfoIPv6(t *testing.T) {
|
||||
// need to listen on ::, otherwise we won't get the packet info
|
||||
addr, packetChan := runSysConnServer(t, "udp6", &net.UDPAddr{IP: net.IPv6zero, Port: 0})
|
||||
|
||||
conn, err := net.DialUDP("udp6", nil, addr)
|
||||
require.NoError(t, err)
|
||||
defer conn.Close()
|
||||
_, err = conn.Write([]byte("foobar"))
|
||||
require.NoError(t, err)
|
||||
|
||||
select {
|
||||
case p := <-packetChan:
|
||||
require.WithinDuration(t, time.Now(), p.rcvTime, scaleDuration(20*time.Millisecond))
|
||||
require.Equal(t, []byte("foobar"), p.data)
|
||||
require.Equal(t, conn.LocalAddr(), p.remoteAddr)
|
||||
require.NotNil(t, p.info)
|
||||
require.Equal(t, net.IPv6loopback, net.IP(p.info.addr.AsSlice()))
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("timeout waiting for packet")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSysConnPacketInfoDualStack(t *testing.T) {
|
||||
addr, packetChan := runSysConnServer(t, "udp", &net.UDPAddr{})
|
||||
|
||||
// IPv4
|
||||
conn4, err := net.DialUDP("udp4", nil, &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: addr.Port})
|
||||
require.NoError(t, err)
|
||||
defer conn4.Close()
|
||||
_, err = conn4.Write([]byte("foobar"))
|
||||
require.NoError(t, err)
|
||||
|
||||
select {
|
||||
case p := <-packetChan:
|
||||
require.True(t, isIPv4(p.remoteAddr.(*net.UDPAddr).IP))
|
||||
require.NotNil(t, p.info)
|
||||
require.True(t, p.info.addr.Is4())
|
||||
require.Equal(t, net.IPv4(127, 0, 0, 1).String(), p.info.addr.String())
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("timeout waiting for IPv4 packet")
|
||||
}
|
||||
|
||||
// IPv6
|
||||
conn6, err := net.DialUDP("udp6", nil, addr)
|
||||
require.NoError(t, err)
|
||||
defer conn6.Close()
|
||||
_, err = conn6.Write([]byte("foobar"))
|
||||
require.NoError(t, err)
|
||||
|
||||
select {
|
||||
case p := <-packetChan:
|
||||
require.False(t, isIPv4(p.remoteAddr.(*net.UDPAddr).IP))
|
||||
require.NotNil(t, p.info)
|
||||
require.Equal(t, net.IPv6loopback.String(), p.info.addr.String())
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("timeout waiting for IPv6 packet")
|
||||
}
|
||||
}
|
||||
|
||||
type oobRecordingConn struct {
|
||||
*net.UDPConn
|
||||
oobs [][]byte
|
||||
|
@ -27,312 +272,63 @@ func (c *oobRecordingConn) WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oob
|
|||
return c.UDPConn.WriteMsgUDP(b, oob, addr)
|
||||
}
|
||||
|
||||
func isIPv4(ip net.IP) bool { return ip.To4() != nil }
|
||||
type mockBatchConn struct {
|
||||
t *testing.T
|
||||
numMsgRead int
|
||||
|
||||
var _ = Describe("OOB Conn Test", func() {
|
||||
runServer := func(network, address string) (*net.UDPConn, <-chan receivedPacket) {
|
||||
addr, err := net.ResolveUDPAddr(network, address)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
udpConn, err := net.ListenUDP(network, addr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
oobConn, err := newConn(udpConn, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(oobConn.capabilities().DF).To(BeTrue())
|
||||
|
||||
packetChan := make(chan receivedPacket)
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
for {
|
||||
p, err := oobConn.ReadPacket()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
packetChan <- p
|
||||
}
|
||||
}()
|
||||
|
||||
return udpConn, packetChan
|
||||
callCounter int
|
||||
}
|
||||
|
||||
Context("reading ECN-marked packets", func() {
|
||||
sendPacketWithECN := func(network string, addr *net.UDPAddr, setECN func(uintptr)) net.Addr {
|
||||
conn, err := net.DialUDP(network, nil, addr)
|
||||
ExpectWithOffset(1, err).ToNot(HaveOccurred())
|
||||
rawConn, err := conn.SyscallConn()
|
||||
ExpectWithOffset(1, err).ToNot(HaveOccurred())
|
||||
ExpectWithOffset(1, rawConn.Control(func(fd uintptr) {
|
||||
setECN(fd)
|
||||
})).To(Succeed())
|
||||
_, err = conn.Write([]byte("foobar"))
|
||||
ExpectWithOffset(1, err).ToNot(HaveOccurred())
|
||||
return conn.LocalAddr()
|
||||
}
|
||||
var _ batchConn = &mockBatchConn{}
|
||||
|
||||
It("reads ECN flags on IPv4", func() {
|
||||
conn, packetChan := runServer("udp4", "localhost:0")
|
||||
defer conn.Close()
|
||||
|
||||
sentFrom := sendPacketWithECN(
|
||||
"udp4",
|
||||
conn.LocalAddr().(*net.UDPAddr),
|
||||
func(fd uintptr) {
|
||||
Expect(unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_TOS, 2)).To(Succeed())
|
||||
},
|
||||
)
|
||||
|
||||
var p receivedPacket
|
||||
Eventually(packetChan).Should(Receive(&p))
|
||||
Expect(p.rcvTime).To(BeTemporally("~", time.Now(), scaleDuration(20*time.Millisecond)))
|
||||
Expect(p.data).To(Equal([]byte("foobar")))
|
||||
Expect(p.remoteAddr).To(Equal(sentFrom))
|
||||
Expect(p.ecn).To(Equal(protocol.ECT0))
|
||||
})
|
||||
|
||||
It("reads ECN flags on IPv6", func() {
|
||||
conn, packetChan := runServer("udp6", "[::]:0")
|
||||
defer conn.Close()
|
||||
|
||||
sentFrom := sendPacketWithECN(
|
||||
"udp6",
|
||||
conn.LocalAddr().(*net.UDPAddr),
|
||||
func(fd uintptr) {
|
||||
Expect(unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_TCLASS, 3)).To(Succeed())
|
||||
},
|
||||
)
|
||||
|
||||
var p receivedPacket
|
||||
Eventually(packetChan).Should(Receive(&p))
|
||||
Expect(p.rcvTime).To(BeTemporally("~", time.Now(), scaleDuration(20*time.Millisecond)))
|
||||
Expect(p.data).To(Equal([]byte("foobar")))
|
||||
Expect(p.remoteAddr).To(Equal(sentFrom))
|
||||
Expect(p.ecn).To(Equal(protocol.ECNCE))
|
||||
})
|
||||
|
||||
It("reads ECN flags on a connection that supports both IPv4 and IPv6", func() {
|
||||
conn, packetChan := runServer("udp", "0.0.0.0:0")
|
||||
defer conn.Close()
|
||||
port := conn.LocalAddr().(*net.UDPAddr).Port
|
||||
|
||||
// IPv4
|
||||
sendPacketWithECN(
|
||||
"udp4",
|
||||
&net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: port},
|
||||
func(fd uintptr) {
|
||||
Expect(unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_TOS, 3)).To(Succeed())
|
||||
},
|
||||
)
|
||||
|
||||
var p receivedPacket
|
||||
Eventually(packetChan).Should(Receive(&p))
|
||||
Expect(isIPv4(p.remoteAddr.(*net.UDPAddr).IP)).To(BeTrue())
|
||||
Expect(p.ecn).To(Equal(protocol.ECNCE))
|
||||
|
||||
// IPv6
|
||||
sendPacketWithECN(
|
||||
"udp6",
|
||||
&net.UDPAddr{IP: net.IPv6loopback, Port: port},
|
||||
func(fd uintptr) {
|
||||
Expect(unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_TCLASS, 1)).To(Succeed())
|
||||
},
|
||||
)
|
||||
|
||||
Eventually(packetChan).Should(Receive(&p))
|
||||
Expect(isIPv4(p.remoteAddr.(*net.UDPAddr).IP)).To(BeFalse())
|
||||
Expect(p.ecn).To(Equal(protocol.ECT1))
|
||||
})
|
||||
|
||||
It("sends packets with ECN on IPv4", func() {
|
||||
conn, packetChan := runServer("udp4", "localhost:0")
|
||||
defer conn.Close()
|
||||
|
||||
c, err := net.ListenUDP("udp4", nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer c.Close()
|
||||
|
||||
for _, val := range []protocol.ECN{protocol.ECNNon, protocol.ECT1, protocol.ECT0, protocol.ECNCE} {
|
||||
_, _, err = c.WriteMsgUDP([]byte("foobar"), appendIPv4ECNMsg([]byte{}, val), conn.LocalAddr().(*net.UDPAddr))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
var p receivedPacket
|
||||
Eventually(packetChan).Should(Receive(&p))
|
||||
Expect(p.data).To(Equal([]byte("foobar")))
|
||||
Expect(p.ecn).To(Equal(val))
|
||||
}
|
||||
})
|
||||
|
||||
It("sends packets with ECN on IPv6", func() {
|
||||
conn, packetChan := runServer("udp6", "[::1]:0")
|
||||
defer conn.Close()
|
||||
|
||||
c, err := net.ListenUDP("udp6", nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer c.Close()
|
||||
|
||||
for _, val := range []protocol.ECN{protocol.ECNNon, protocol.ECT1, protocol.ECT0, protocol.ECNCE} {
|
||||
_, _, err = c.WriteMsgUDP([]byte("foobar"), appendIPv6ECNMsg([]byte{}, val), conn.LocalAddr().(*net.UDPAddr))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
var p receivedPacket
|
||||
Eventually(packetChan).Should(Receive(&p))
|
||||
Expect(p.data).To(Equal([]byte("foobar")))
|
||||
Expect(p.ecn).To(Equal(val))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Context("Packet Info conn", func() {
|
||||
sendPacket := func(network string, addr *net.UDPAddr) net.Addr {
|
||||
conn, err := net.DialUDP(network, nil, addr)
|
||||
ExpectWithOffset(1, err).ToNot(HaveOccurred())
|
||||
_, err = conn.Write([]byte("foobar"))
|
||||
ExpectWithOffset(1, err).ToNot(HaveOccurred())
|
||||
return conn.LocalAddr()
|
||||
}
|
||||
|
||||
It("reads packet info on IPv4", func() {
|
||||
conn, packetChan := runServer("udp4", ":0")
|
||||
defer conn.Close()
|
||||
|
||||
addr := conn.LocalAddr().(*net.UDPAddr)
|
||||
ip := net.ParseIP("127.0.0.1").To4()
|
||||
addr.IP = ip
|
||||
sentFrom := sendPacket("udp4", addr)
|
||||
|
||||
var p receivedPacket
|
||||
Eventually(packetChan).Should(Receive(&p))
|
||||
Expect(p.rcvTime).To(BeTemporally("~", time.Now(), scaleDuration(20*time.Millisecond)))
|
||||
Expect(p.data).To(Equal([]byte("foobar")))
|
||||
Expect(p.remoteAddr).To(Equal(sentFrom))
|
||||
Expect(p.info.addr.IsValid()).To(BeTrue())
|
||||
Expect(net.IP(p.info.addr.AsSlice())).To(Equal(ip))
|
||||
})
|
||||
|
||||
It("reads packet info on IPv6", func() {
|
||||
conn, packetChan := runServer("udp6", ":0")
|
||||
defer conn.Close()
|
||||
|
||||
addr := conn.LocalAddr().(*net.UDPAddr)
|
||||
ip := net.ParseIP("::1")
|
||||
addr.IP = ip
|
||||
sentFrom := sendPacket("udp6", addr)
|
||||
|
||||
var p receivedPacket
|
||||
Eventually(packetChan).Should(Receive(&p))
|
||||
Expect(p.rcvTime).To(BeTemporally("~", time.Now(), scaleDuration(20*time.Millisecond)))
|
||||
Expect(p.data).To(Equal([]byte("foobar")))
|
||||
Expect(p.remoteAddr).To(Equal(sentFrom))
|
||||
Expect(p.info).To(Not(BeNil()))
|
||||
Expect(net.IP(p.info.addr.AsSlice())).To(Equal(ip))
|
||||
})
|
||||
|
||||
It("reads packet info on a connection that supports both IPv4 and IPv6", func() {
|
||||
conn, packetChan := runServer("udp", ":0")
|
||||
defer conn.Close()
|
||||
port := conn.LocalAddr().(*net.UDPAddr).Port
|
||||
|
||||
// IPv4
|
||||
ip4 := net.ParseIP("127.0.0.1")
|
||||
sendPacket("udp4", &net.UDPAddr{IP: ip4, Port: port})
|
||||
|
||||
var p receivedPacket
|
||||
Eventually(packetChan).Should(Receive(&p))
|
||||
Expect(isIPv4(p.remoteAddr.(*net.UDPAddr).IP)).To(BeTrue())
|
||||
Expect(p.info).To(Not(BeNil()))
|
||||
Expect(p.info.addr.Is4()).To(BeTrue())
|
||||
ip := p.info.addr.As4()
|
||||
Expect(net.IP(ip[:])).To(Equal(ip4.To4()))
|
||||
|
||||
// IPv6
|
||||
ip6 := net.ParseIP("::1")
|
||||
sendPacket("udp6", &net.UDPAddr{IP: net.IPv6loopback, Port: port})
|
||||
|
||||
Eventually(packetChan).Should(Receive(&p))
|
||||
Expect(isIPv4(p.remoteAddr.(*net.UDPAddr).IP)).To(BeFalse())
|
||||
Expect(p.info).To(Not(BeNil()))
|
||||
Expect(net.IP(p.info.addr.AsSlice())).To(Equal(ip6))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Batch Reading", func() {
|
||||
var batchConn *MockBatchConn
|
||||
|
||||
BeforeEach(func() {
|
||||
batchConn = NewMockBatchConn(mockCtrl)
|
||||
})
|
||||
|
||||
It("reads multiple messages in one batch", func() {
|
||||
const numMsgRead = batchSize/2 + 1
|
||||
var counter int
|
||||
batchConn.EXPECT().ReadBatch(gomock.Any(), gomock.Any()).DoAndReturn(func(ms []ipv4.Message, flags int) (int, error) {
|
||||
Expect(ms).To(HaveLen(batchSize))
|
||||
for i := 0; i < numMsgRead; i++ {
|
||||
Expect(ms[i].Buffers).To(HaveLen(1))
|
||||
Expect(ms[i].Buffers[0]).To(HaveLen(protocol.MaxPacketBufferSize))
|
||||
data := []byte(fmt.Sprintf("message %d", counter))
|
||||
counter++
|
||||
func (c *mockBatchConn) ReadBatch(ms []ipv4.Message, _ int) (int, error) {
|
||||
require.Len(c.t, ms, batchSize)
|
||||
for i := 0; i < c.numMsgRead; i++ {
|
||||
require.Len(c.t, ms[i].Buffers, 1)
|
||||
require.Len(c.t, ms[i].Buffers[0], protocol.MaxPacketBufferSize)
|
||||
data := []byte(fmt.Sprintf("message %d", c.callCounter*c.numMsgRead+i))
|
||||
ms[i].Buffers[0] = data
|
||||
ms[i].N = len(data)
|
||||
}
|
||||
return numMsgRead, nil
|
||||
}).Times(2)
|
||||
c.callCounter++
|
||||
return c.numMsgRead, nil
|
||||
}
|
||||
|
||||
addr, err := net.ResolveUDPAddr("udp", "localhost:0")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
udpConn, err := net.ListenUDP("udp", addr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
func TestReadsMultipleMessagesInOneBatch(t *testing.T) {
|
||||
bc := &mockBatchConn{t: t, numMsgRead: batchSize/2 + 1}
|
||||
|
||||
udpConn := newUPDConnLocalhost(t)
|
||||
oobConn, err := newConn(udpConn, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
oobConn.batchConn = batchConn
|
||||
require.NoError(t, err)
|
||||
oobConn.batchConn = bc
|
||||
|
||||
for i := 0; i < batchSize+1; i++ {
|
||||
p, err := oobConn.ReadPacket()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(string(p.data)).To(Equal(fmt.Sprintf("message %d", i)))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, fmt.Sprintf("message %d", i), string(p.data))
|
||||
}
|
||||
require.Equal(t, 2, bc.callCounter)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Context("sending ECN-marked packets", func() {
|
||||
It("sets the ECN control message", func() {
|
||||
addr, err := net.ResolveUDPAddr("udp", "localhost:0")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
udpConn, err := net.ListenUDP("udp", addr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
func TestSysConnSendGSO(t *testing.T) {
|
||||
if !platformSupportsGSO {
|
||||
t.Skip("GSO not supported on this platform")
|
||||
}
|
||||
|
||||
udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
|
||||
require.NoError(t, err)
|
||||
c := &oobRecordingConn{UDPConn: udpConn}
|
||||
oobConn, err := newConn(c, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
require.NoError(t, err)
|
||||
require.True(t, oobConn.capabilities().GSO)
|
||||
|
||||
oob := make([]byte, 0, 123)
|
||||
oobConn.WritePacket([]byte("foobar"), addr, oob, 0, protocol.ECNCE)
|
||||
Expect(c.oobs).To(HaveLen(1))
|
||||
oobConn.WritePacket([]byte("foobar"), udpConn.LocalAddr(), oob, 3, protocol.ECNCE)
|
||||
require.Len(t, c.oobs, 1)
|
||||
oobMsg := c.oobs[0]
|
||||
Expect(oobMsg).ToNot(BeEmpty())
|
||||
Expect(oobMsg).To(HaveCap(cap(oob))) // check that it appended to oob
|
||||
expected := appendIPv4ECNMsg([]byte{}, protocol.ECNCE)
|
||||
Expect(oobMsg).To(Equal(expected))
|
||||
})
|
||||
})
|
||||
|
||||
if platformSupportsGSO {
|
||||
Context("GSO", func() {
|
||||
It("appends the GSO control message", func() {
|
||||
addr, err := net.ResolveUDPAddr("udp", "localhost:0")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
udpConn, err := net.ListenUDP("udp", addr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
c := &oobRecordingConn{UDPConn: udpConn}
|
||||
oobConn, err := newConn(c, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(oobConn.capabilities().GSO).To(BeTrue())
|
||||
|
||||
oob := make([]byte, 0, 123)
|
||||
oobConn.WritePacket([]byte("foobar"), addr, oob, 3, protocol.ECNCE)
|
||||
Expect(c.oobs).To(HaveLen(1))
|
||||
oobMsg := c.oobs[0]
|
||||
Expect(oobMsg).ToNot(BeEmpty())
|
||||
Expect(oobMsg).To(HaveCap(cap(oob))) // check that it appended to oob
|
||||
require.NotEmpty(t, oobMsg)
|
||||
require.Equal(t, cap(oob), cap(oobMsg)) // check that it appended to oob
|
||||
expected := appendUDPSegmentSizeMsg([]byte{}, 3)
|
||||
// Check that the first control message is the OOB control message.
|
||||
Expect(oobMsg[:len(expected)]).To(Equal(expected))
|
||||
})
|
||||
})
|
||||
require.Equal(t, expected, oobMsg[:len(expected)])
|
||||
}
|
||||
})
|
||||
|
|
|
@ -2,31 +2,31 @@ package quic
|
|||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/quic-go/quic-go/internal/protocol"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
var _ = Describe("Basic Conn Test", func() {
|
||||
It("reads a packet", func() {
|
||||
func TestBasicConn(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
|
||||
c := NewMockPacketConn(mockCtrl)
|
||||
addr := &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 1234}
|
||||
c.EXPECT().ReadFrom(gomock.Any()).DoAndReturn(func(b []byte) (int, net.Addr, error) {
|
||||
data := []byte("foobar")
|
||||
Expect(b).To(HaveLen(protocol.MaxPacketBufferSize))
|
||||
require.Equal(t, protocol.MaxPacketBufferSize, len(b))
|
||||
return copy(b, data), addr, nil
|
||||
})
|
||||
|
||||
conn, err := wrapConn(c)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
require.NoError(t, err)
|
||||
p, err := conn.ReadPacket()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(p.data).To(Equal([]byte("foobar")))
|
||||
Expect(p.rcvTime).To(BeTemporally("~", time.Now(), scaleDuration(100*time.Millisecond)))
|
||||
Expect(p.remoteAddr).To(Equal(addr))
|
||||
})
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []byte("foobar"), p.data)
|
||||
require.WithinDuration(t, time.Now(), p.rcvTime, scaleDuration(100*time.Millisecond))
|
||||
require.Equal(t, addr, p.remoteAddr)
|
||||
}
|
||||
|
|
|
@ -4,31 +4,27 @@ package quic
|
|||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var _ = Describe("Windows Conn Test", func() {
|
||||
It("works on IPv4", func() {
|
||||
addr, err := net.ResolveUDPAddr("udp4", "localhost:0")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
udpConn, err := net.ListenUDP("udp4", addr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
func TestWindowsConn(t *testing.T) {
|
||||
t.Run("IPv4", func(t *testing.T) {
|
||||
udpConn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
|
||||
require.NoError(t, err)
|
||||
conn, err := newConn(udpConn, true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(conn.Close()).To(Succeed())
|
||||
Expect(conn.capabilities().DF).To(BeTrue())
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, conn.Close())
|
||||
require.True(t, conn.capabilities().DF)
|
||||
})
|
||||
|
||||
It("works on IPv6", func() {
|
||||
addr, err := net.ResolveUDPAddr("udp6", "[::1]:0")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
udpConn, err := net.ListenUDP("udp6", addr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
t.Run("IPv6", func(t *testing.T) {
|
||||
udpConn, err := net.ListenUDP("udp6", &net.UDPAddr{IP: net.IPv6loopback, Port: 0})
|
||||
require.NoError(t, err)
|
||||
conn, err := newConn(udpConn, false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(conn.Close()).To(Succeed())
|
||||
Expect(conn.capabilities().DF).To(BeFalse())
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, conn.Close())
|
||||
require.False(t, conn.capabilities().DF)
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue