mirror of
https://github.com/slackhq/nebula.git
synced 2026-08-15 22:57:03 +02:00
drop ECN support for this release
This commit is contained in:
+14
-140
@@ -3,13 +3,11 @@
|
||||
package udp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
"unsafe"
|
||||
@@ -56,39 +54,6 @@ func buildCmsg(level, typ int32, data []byte) []byte {
|
||||
return buf
|
||||
}
|
||||
|
||||
// TestParseRecvCmsgOuterECNFamily is the RX half of the dual-stack ECN fix:
|
||||
// parseRecvCmsg must read the outer ECN from whichever family the kernel
|
||||
// delivered, not from the socket family. On the default `::` dual-stack bind
|
||||
// a v4 peer's outer ECN arrives as an IP_TOS cmsg, which the old socket-family
|
||||
// gate ignored entirely.
|
||||
func TestParseRecvCmsgOuterECNFamily(t *testing.T) {
|
||||
tc := make([]byte, 4)
|
||||
binary.NativeEndian.PutUint32(tc, 0x02)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
ctrl []byte
|
||||
want byte
|
||||
}{
|
||||
{"ip_tos_ce", buildCmsg(int32(unix.IPPROTO_IP), int32(unix.IP_TOS), []byte{0x03}), 0x03},
|
||||
{"ip_tos_ect0", buildCmsg(int32(unix.IPPROTO_IP), int32(unix.IP_TOS), []byte{0x02}), 0x02},
|
||||
{"ipv6_tclass_ect0", buildCmsg(int32(unix.IPPROTO_IPV6), int32(unix.IPV6_TCLASS), tc), 0x02},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
hdr := &msghdr{Control: &c.ctrl[0]}
|
||||
setMsgControllen(hdr, len(c.ctrl))
|
||||
gso, ecn := parseRecvCmsg(hdr, false, true)
|
||||
if gso != 0 {
|
||||
t.Errorf("gso = %d, want 0 (no UDP_GRO cmsg present)", gso)
|
||||
}
|
||||
if ecn != c.want {
|
||||
t.Errorf("ecn = 0x%02x, want 0x%02x", ecn, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testLogger() *slog.Logger {
|
||||
return slog.New(slog.DiscardHandler)
|
||||
}
|
||||
@@ -125,7 +90,7 @@ func TestWriteBatchBadFamilyDeliversOthers(t *testing.T) {
|
||||
bufs := [][]byte{[]byte("AAA"), []byte("BBB"), []byte("CCC")}
|
||||
addrs := []netip.AddrPort{good, bad, good}
|
||||
|
||||
n, err := sender.WriteBatch(bufs, addrs, nil)
|
||||
n, err := sender.WriteBatch(bufs, addrs)
|
||||
if err != nil {
|
||||
t.Fatalf("WriteBatch returned error, want nil (bad dest should be isolated): %v", err)
|
||||
}
|
||||
@@ -151,93 +116,6 @@ func TestWriteBatchBadFamilyDeliversOthers(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestWriteBatchOuterTOSToV4Mapped is the TX half of the dual-stack ECN fix,
|
||||
// verified against a live kernel: WriteBatch on the default `::` dual-stack
|
||||
// socket, sending to a v4-mapped destination, must stamp the outer ECN via an
|
||||
// IP_TOS cmsg (not IPV6_TCLASS, which the kernel's v4 path ignores) so a v4
|
||||
// receiver actually sees it.
|
||||
func TestWriteBatchOuterTOSToV4Mapped(t *testing.T) {
|
||||
rx, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)})
|
||||
if err != nil {
|
||||
t.Skipf("cannot open v4 receiver (sandbox?): %v", err)
|
||||
}
|
||||
defer rx.Close()
|
||||
rxPort := rx.LocalAddr().(*net.UDPAddr).Port
|
||||
|
||||
// Ask the kernel to deliver the received outer TOS as ancillary data.
|
||||
rxRaw, err := rx.SyscallConn()
|
||||
if err != nil {
|
||||
t.Fatalf("SyscallConn: %v", err)
|
||||
}
|
||||
var soErr error
|
||||
if err := rxRaw.Control(func(fd uintptr) {
|
||||
soErr = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_RECVTOS, 1)
|
||||
}); err != nil || soErr != nil {
|
||||
t.Skipf("cannot enable IP_RECVTOS (sandbox/kernel?): ctrl=%v so=%v", err, soErr)
|
||||
}
|
||||
|
||||
c, err := NewListener(testLogger(), netip.IPv6Unspecified(), 0, false, 1)
|
||||
if err != nil {
|
||||
t.Skipf("cannot open dual-stack sender (sandbox?): %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
sender := c.(*StdConn)
|
||||
if sender.isV4 {
|
||||
t.Skipf("sender came up v4-only; need a dual-stack v6 socket for this test")
|
||||
}
|
||||
|
||||
// v4-mapped-in-v6 destination: routed through the kernel's IPv4 path.
|
||||
dst := netip.AddrPortFrom(netip.AddrFrom4([4]byte{127, 0, 0, 1}), uint16(rxPort))
|
||||
const wantECN = byte(0x02) // ECT(0)
|
||||
|
||||
if _, err := sender.WriteBatch([][]byte{[]byte("tos-probe")}, []netip.AddrPort{dst}, []byte{wantECN}); err != nil {
|
||||
t.Fatalf("WriteBatch: %v", err)
|
||||
}
|
||||
|
||||
// Read the datagram plus its ancillary TOS.
|
||||
rx.SetReadDeadline(time.Now().Add(3 * time.Second))
|
||||
payload := make([]byte, 128)
|
||||
oob := make([]byte, 512)
|
||||
var n, oobn int
|
||||
var rerr error
|
||||
if err := rxRaw.Read(func(fd uintptr) bool {
|
||||
n, oobn, _, _, rerr = unix.Recvmsg(int(fd), payload, oob, 0)
|
||||
if rerr == syscall.EAGAIN || rerr == syscall.EWOULDBLOCK {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}); err != nil {
|
||||
t.Fatalf("waiting for datagram failed (no delivery?): %v", err)
|
||||
}
|
||||
if rerr != nil {
|
||||
t.Fatalf("Recvmsg: %v", rerr)
|
||||
}
|
||||
if string(payload[:n]) != "tos-probe" {
|
||||
t.Fatalf("payload = %q, want %q", string(payload[:n]), "tos-probe")
|
||||
}
|
||||
|
||||
cmsgs, err := unix.ParseSocketControlMessage(oob[:oobn])
|
||||
if err != nil {
|
||||
t.Fatalf("ParseSocketControlMessage: %v", err)
|
||||
}
|
||||
found := false
|
||||
var gotTOS byte
|
||||
for _, m := range cmsgs {
|
||||
if m.Header.Level == unix.IPPROTO_IP && m.Header.Type == unix.IP_TOS && len(m.Data) >= 1 {
|
||||
found = true
|
||||
gotTOS = m.Data[0]
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("no IP_TOS cmsg delivered to v4 receiver — outer ECN did not land (%d cmsgs)", len(cmsgs))
|
||||
}
|
||||
if gotTOS&0x03 != wantECN {
|
||||
t.Errorf("received outer TOS = 0x%02x, want low-2-bits = 0x%02x", gotTOS, wantECN)
|
||||
} else {
|
||||
t.Logf("verified: v4 receiver saw outer TOS 0x%02x (ECN=0x%02x) from dual-stack sender", gotTOS, gotTOS&0x03)
|
||||
}
|
||||
}
|
||||
|
||||
// TestWriteBatchUnreachableDestDeliversOthers is the kernel-rejection twin of
|
||||
// TestWriteBatchBadFamilyDeliversOthers. A destination the kernel refuses outright (240.0.0.0/4 is reserved, so
|
||||
// the send returns EINVAL) fails its sendmmsg entry; WriteBatch must drop only that entry and still deliver
|
||||
@@ -264,7 +142,7 @@ func TestWriteBatchUnreachableDestDeliversOthers(t *testing.T) {
|
||||
addrs := []netip.AddrPort{good, good, bad, good, good}
|
||||
|
||||
// The bad destination is reported, but only after every other packet has been attempted.
|
||||
if _, err := sender.WriteBatch(bufs, addrs, nil); err == nil {
|
||||
if _, err := sender.WriteBatch(bufs, addrs); err == nil {
|
||||
t.Log("WriteBatch returned nil; kernel accepted the reserved address, delivery assertions still apply")
|
||||
}
|
||||
|
||||
@@ -317,11 +195,11 @@ func TestParseRecvCmsgCorruptLenNoPanic(t *testing.T) {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
hdr := &msghdr{Control: &c.ctrl[0]}
|
||||
setMsgControllen(hdr, len(c.ctrl))
|
||||
gso, ecn := parseRecvCmsg(hdr, true, true)
|
||||
gso := parseRecvCmsg(hdr)
|
||||
// The valid leading UDP_GRO cmsg (payload 0) must still parse;
|
||||
// the corrupt trailer just ends the walk.
|
||||
if gso != 0 || ecn != 0 {
|
||||
t.Errorf("parseRecvCmsg = (%d, %#x), want (0, 0)", gso, ecn)
|
||||
if gso != 0 {
|
||||
t.Errorf("parseRecvCmsg = %d, want 0", gso)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -367,16 +245,12 @@ func TestDeliverSegments(t *testing.T) {
|
||||
}
|
||||
|
||||
var got [][]byte
|
||||
meta := RxMeta{OuterECN: 0x2}
|
||||
deliverSegments(func(a netip.AddrPort, seg []byte, m RxMeta) {
|
||||
deliverSegments(func(a netip.AddrPort, seg []byte) {
|
||||
if a != from {
|
||||
t.Errorf("from = %v, want %v", a, from)
|
||||
}
|
||||
if m != meta {
|
||||
t.Errorf("meta = %+v, want %+v", m, meta)
|
||||
}
|
||||
got = append(got, seg)
|
||||
}, from, c.payload, c.segSize, meta)
|
||||
}, from, c.payload, c.segSize)
|
||||
|
||||
if len(got) != len(wantLens) {
|
||||
t.Fatalf("delivered %d segments, want %d", len(got), len(wantLens))
|
||||
@@ -481,7 +355,7 @@ func TestWriteBatchPartialSendRewind(t *testing.T) {
|
||||
return accept, nil
|
||||
}
|
||||
|
||||
written, err := w.WriteBatch(bufs, addrs, nil)
|
||||
written, err := w.WriteBatch(bufs, addrs)
|
||||
if err != nil {
|
||||
t.Fatalf("WriteBatch: %v", err)
|
||||
}
|
||||
@@ -534,7 +408,7 @@ func TestWriteBatchSkipUnroutableRunAccounting(t *testing.T) {
|
||||
return accept, nil
|
||||
}
|
||||
|
||||
written, err := w.WriteBatch(bufs, addrs, nil)
|
||||
written, err := w.WriteBatch(bufs, addrs)
|
||||
if err != nil {
|
||||
t.Fatalf("WriteBatch: %v", err)
|
||||
}
|
||||
@@ -591,7 +465,7 @@ func TestWriteBatchMidChunkRejectResumes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
written, err := w.WriteBatch(bufs, addrs, nil)
|
||||
written, err := w.WriteBatch(bufs, addrs)
|
||||
if err != nil {
|
||||
t.Fatalf("WriteBatch: %v", err)
|
||||
}
|
||||
@@ -648,7 +522,7 @@ func TestWriteBatchMidChunkEIODisablesGSOWithoutDup(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
written, err := w.WriteBatch(bufs, addrs, nil)
|
||||
written, err := w.WriteBatch(bufs, addrs)
|
||||
if err != nil {
|
||||
t.Fatalf("WriteBatch: %v", err)
|
||||
}
|
||||
@@ -676,7 +550,7 @@ func TestWriteBatchZeroProgress(t *testing.T) {
|
||||
w.sendFn = func(start, n int) (int, error) { return 0, nil }
|
||||
bufs := [][]byte{make([]byte, 100)}
|
||||
addrs := []netip.AddrPort{netip.MustParseAddrPort("127.0.0.1:4242")}
|
||||
if _, err := w.WriteBatch(bufs, addrs, nil); err == nil {
|
||||
if _, err := w.WriteBatch(bufs, addrs); err == nil {
|
||||
t.Fatal("WriteBatch = nil error on zero progress, want error")
|
||||
}
|
||||
}
|
||||
@@ -702,7 +576,7 @@ func TestWriteBatchEIODisablesGSOAndReplays(t *testing.T) {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
written, err := w.WriteBatch(bufs, addrs, nil)
|
||||
written, err := w.WriteBatch(bufs, addrs)
|
||||
if err != nil {
|
||||
t.Fatalf("WriteBatch: %v", err)
|
||||
}
|
||||
@@ -773,7 +647,7 @@ func TestGSOEngagesOnLoopback(t *testing.T) {
|
||||
addrs[i] = dst
|
||||
}
|
||||
|
||||
written, err := sc.WriteBatch(bufs, addrs, nil)
|
||||
written, err := sc.WriteBatch(bufs, addrs)
|
||||
if err != nil {
|
||||
t.Fatalf("WriteBatch: %v", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user