mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-03-31 10:47:35 +03:00
add support for writing the ECN control message (Linux, macOS)
This commit is contained in:
parent
a7f807856c
commit
f919473598
8 changed files with 91 additions and 3 deletions
13
send_conn.go
13
send_conn.go
|
@ -3,6 +3,7 @@ package quic
|
|||
import (
|
||||
"net"
|
||||
|
||||
"github.com/quic-go/quic-go/internal/protocol"
|
||||
"github.com/quic-go/quic-go/internal/utils"
|
||||
)
|
||||
|
||||
|
@ -42,10 +43,16 @@ func newSendConn(c rawConn, remote net.Addr, info packetInfo, logger utils.Logge
|
|||
}
|
||||
|
||||
oob := info.OOB()
|
||||
// add 32 bytes, so we can add the UDP_SEGMENT msg
|
||||
if remoteUDPAddr, ok := remote.(*net.UDPAddr); ok {
|
||||
if remoteUDPAddr.IP.To4() != nil {
|
||||
oob = appendIPv4ECNMsg(oob, protocol.ECT1)
|
||||
} else {
|
||||
oob = appendIPv6ECNMsg(oob, protocol.ECT1)
|
||||
}
|
||||
}
|
||||
// increase oob slice capacity, so we can add the UDP_SEGMENT and ECN control messages without allocating
|
||||
l := len(oob)
|
||||
oob = append(oob, make([]byte, 32)...)
|
||||
oob = oob[:l]
|
||||
oob = append(oob, make([]byte, 64)...)[:l]
|
||||
return &sconn{
|
||||
rawConn: c,
|
||||
localAddr: localAddr,
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/quic-go/quic-go/internal/protocol"
|
||||
"github.com/quic-go/quic-go/internal/utils"
|
||||
)
|
||||
|
||||
|
@ -52,3 +53,6 @@ func isRecvMsgSizeErr(err error) bool {
|
|||
// https://docs.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2
|
||||
return errors.Is(err, windows.WSAEMSGSIZE)
|
||||
}
|
||||
|
||||
func appendIPv4ECNMsg([]byte, protocol.ECN) []byte { return nil }
|
||||
func appendIPv6ECNMsg([]byte, protocol.ECN) []byte { return nil }
|
||||
|
|
|
@ -15,6 +15,8 @@ const (
|
|||
ipv4PKTINFO = unix.IP_RECVPKTINFO
|
||||
)
|
||||
|
||||
const ecnIPv4DataLen = 4
|
||||
|
||||
// ReadBatch only returns a single packet on OSX,
|
||||
// see https://godoc.org/golang.org/x/net/ipv4#PacketConn.ReadBatch.
|
||||
const batchSize = 1
|
||||
|
|
|
@ -14,6 +14,8 @@ const (
|
|||
ipv4PKTINFO = 0x7
|
||||
)
|
||||
|
||||
const ecnIPv4DataLen = 4
|
||||
|
||||
const batchSize = 8
|
||||
|
||||
func parseIPv4PktInfo(body []byte) (ip netip.Addr, _ uint32, ok bool) {
|
||||
|
|
|
@ -19,6 +19,8 @@ const (
|
|||
ipv4PKTINFO = unix.IP_PKTINFO
|
||||
)
|
||||
|
||||
const ecnIPv4DataLen = 4
|
||||
|
||||
const batchSize = 8 // needs to smaller than MaxUint8 (otherwise the type of oobConn.readPos has to be changed)
|
||||
|
||||
func forceSetReceiveBuffer(c syscall.RawConn, bytes int) error {
|
||||
|
|
|
@ -5,6 +5,8 @@ package quic
|
|||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/quic-go/quic-go/internal/protocol"
|
||||
)
|
||||
|
||||
func newConn(c net.PacketConn, supportsDF bool) (*basicConn, error) {
|
||||
|
@ -14,6 +16,9 @@ func newConn(c net.PacketConn, supportsDF bool) (*basicConn, error) {
|
|||
func inspectReadBuffer(any) (int, error) { return 0, nil }
|
||||
func inspectWriteBuffer(any) (int, error) { return 0, nil }
|
||||
|
||||
func appendIPv4ECNMsg([]byte, protocol.ECN) []byte { return nil }
|
||||
func appendIPv6ECNMsg([]byte, protocol.ECN) []byte { return nil }
|
||||
|
||||
type packetInfo struct {
|
||||
addr netip.Addr
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
|
@ -279,3 +280,32 @@ func (info *packetInfo) OOB() []byte {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func appendIPv4ECNMsg(b []byte, val protocol.ECN) []byte {
|
||||
startLen := len(b)
|
||||
b = append(b, make([]byte, unix.CmsgSpace(ecnIPv4DataLen))...)
|
||||
h := (*unix.Cmsghdr)(unsafe.Pointer(&b[startLen]))
|
||||
h.Level = syscall.IPPROTO_IP
|
||||
h.Type = unix.IP_TOS
|
||||
h.SetLen(unix.CmsgLen(ecnIPv4DataLen))
|
||||
|
||||
// UnixRights uses the private `data` method, but I *think* this achieves the same goal.
|
||||
offset := startLen + unix.CmsgSpace(0)
|
||||
b[offset] = uint8(val)
|
||||
return b
|
||||
}
|
||||
|
||||
func appendIPv6ECNMsg(b []byte, val protocol.ECN) []byte {
|
||||
startLen := len(b)
|
||||
const dataLen = 4
|
||||
b = append(b, make([]byte, unix.CmsgSpace(dataLen))...)
|
||||
h := (*unix.Cmsghdr)(unsafe.Pointer(&b[startLen]))
|
||||
h.Level = syscall.IPPROTO_IPV6
|
||||
h.Type = unix.IPV6_TCLASS
|
||||
h.SetLen(unix.CmsgLen(dataLen))
|
||||
|
||||
// UnixRights uses the private `data` method, but I *think* this achieves the same goal.
|
||||
offset := startLen + unix.CmsgSpace(0)
|
||||
b[offset] = uint8(val)
|
||||
return b
|
||||
}
|
||||
|
|
|
@ -139,6 +139,42 @@ var _ = Describe("OOB Conn Test", func() {
|
|||
Expect(utils.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() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue