mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-03 20:27:35 +03:00
115 lines
3.2 KiB
Go
115 lines
3.2 KiB
Go
// +build darwin linux
|
|
|
|
package quic
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"syscall"
|
|
"time"
|
|
|
|
"golang.org/x/sys/unix"
|
|
|
|
"github.com/lucas-clemente/quic-go/internal/protocol"
|
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
|
)
|
|
|
|
const ecnMask uint8 = 0x3
|
|
|
|
func inspectReadBuffer(c net.PacketConn) (int, error) {
|
|
conn, ok := c.(interface {
|
|
SyscallConn() (syscall.RawConn, error)
|
|
})
|
|
if !ok {
|
|
return 0, errors.New("doesn't have a SyscallConn")
|
|
}
|
|
rawConn, err := conn.SyscallConn()
|
|
if err != nil {
|
|
return 0, fmt.Errorf("couldn't get syscall.RawConn: %w", err)
|
|
}
|
|
var size int
|
|
var serr error
|
|
if err := rawConn.Control(func(fd uintptr) {
|
|
size, serr = unix.GetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RCVBUF)
|
|
}); err != nil {
|
|
return 0, err
|
|
}
|
|
return size, serr
|
|
}
|
|
|
|
type ecnConn struct {
|
|
ECNCapablePacketConn
|
|
oobBuffer []byte
|
|
}
|
|
|
|
var _ connection = &ecnConn{}
|
|
|
|
func newConn(c ECNCapablePacketConn) (*ecnConn, error) {
|
|
rawConn, err := c.SyscallConn()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// We don't know if this a IPv4-only, IPv6-only or a IPv4-and-IPv6 connection.
|
|
// Try enabling receiving of ECN for both IP versions.
|
|
// We expect at least one of those syscalls to succeed.
|
|
var errIPv4, errIPv6 error
|
|
if err := rawConn.Control(func(fd uintptr) {
|
|
errIPv4 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_RECVTOS, 1)
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rawConn.Control(func(fd uintptr) {
|
|
errIPv6 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_RECVTCLASS, 1)
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
switch {
|
|
case errIPv4 == nil && errIPv6 == nil:
|
|
utils.DefaultLogger.Debugf("Activating reading of ECN bits for IPv4 and IPv6.")
|
|
case errIPv4 == nil && errIPv6 != nil:
|
|
utils.DefaultLogger.Debugf("Activating reading of ECN bits for IPv4.")
|
|
case errIPv4 != nil && errIPv6 == nil:
|
|
utils.DefaultLogger.Debugf("Activating reading of ECN bits for IPv6.")
|
|
case errIPv4 != nil && errIPv6 != nil:
|
|
return nil, errors.New("activating ECN failed for both IPv4 and IPv6")
|
|
}
|
|
return &ecnConn{
|
|
ECNCapablePacketConn: c,
|
|
oobBuffer: make([]byte, 128),
|
|
}, nil
|
|
}
|
|
|
|
func (c *ecnConn) ReadPacket() (*receivedPacket, error) {
|
|
buffer := getPacketBuffer()
|
|
// The packet size should not exceed protocol.MaxPacketBufferSize bytes
|
|
// If it does, we only read a truncated packet, which will then end up undecryptable
|
|
buffer.Data = buffer.Data[:protocol.MaxPacketBufferSize]
|
|
c.oobBuffer = c.oobBuffer[:cap(c.oobBuffer)]
|
|
n, oobn, _, addr, err := c.ECNCapablePacketConn.ReadMsgUDP(buffer.Data, c.oobBuffer)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ctrlMsgs, err := unix.ParseSocketControlMessage(c.oobBuffer[:oobn])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var ecn protocol.ECN
|
|
for _, ctrlMsg := range ctrlMsgs {
|
|
if ctrlMsg.Header.Level == unix.IPPROTO_IP && ctrlMsg.Header.Type == msgTypeIPTOS {
|
|
ecn = protocol.ECN(ctrlMsg.Data[0] & ecnMask)
|
|
break
|
|
}
|
|
if ctrlMsg.Header.Level == unix.IPPROTO_IPV6 && ctrlMsg.Header.Type == unix.IPV6_TCLASS {
|
|
ecn = protocol.ECN(ctrlMsg.Data[0] & ecnMask)
|
|
break
|
|
}
|
|
}
|
|
return &receivedPacket{
|
|
remoteAddr: addr,
|
|
rcvTime: time.Now(),
|
|
data: buffer.Data[:n],
|
|
ecn: ecn,
|
|
buffer: buffer,
|
|
}, nil
|
|
}
|