mirror of
https://github.com/slackhq/nebula.git
synced 2026-07-01 19:10:29 +02:00
it works
This commit is contained in:
@@ -40,11 +40,6 @@ type MultiCoalescer struct {
|
||||
arena *util.Arena
|
||||
}
|
||||
|
||||
// DefaultMultiArenaCap is the recommended arena capacity for a Multi-lane
|
||||
// batcher: 64 slots × 65535 bytes ≈ 4 MiB, enough to hold one recvmmsg
|
||||
// burst worth of MTU-sized packets without the arena growing.
|
||||
const DefaultMultiArenaCap = initialSlots * 65535
|
||||
|
||||
// NewMultiCoalescer builds a multi-lane batcher. tcpEnabled lets the caller
|
||||
// opt out of TCP coalescing (e.g. when the queue can't do TSO); udpEnabled
|
||||
// likewise gates UDP coalescing (only enable when USO was negotiated).
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/slackhq/nebula/test"
|
||||
"github.com/slackhq/nebula/util"
|
||||
)
|
||||
|
||||
// TestMultiCoalescerRoutesByProto confirms TCP/UDP/other land in the right
|
||||
@@ -11,7 +12,7 @@ import (
|
||||
// else (ICMP here) falls through to plain Write.
|
||||
func TestMultiCoalescerRoutesByProto(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
m := NewMultiCoalescer(w, test.NewLogger(), NewArena(0), true, true)
|
||||
m := NewMultiCoalescer(w, test.NewLogger(), util.NewArena(0), true, true)
|
||||
|
||||
tcpPay := make([]byte, 1200)
|
||||
udpPay := make([]byte, 1200)
|
||||
@@ -53,7 +54,7 @@ func TestMultiCoalescerRoutesByProto(t *testing.T) {
|
||||
// the kernel via the passthrough lane rather than being lost.
|
||||
func TestMultiCoalescerDisabledUDPFallsThrough(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
m := NewMultiCoalescer(w, test.NewLogger(), NewArena(0), true, false) // TSO on, USO off
|
||||
m := NewMultiCoalescer(w, test.NewLogger(), util.NewArena(0), true, false) // TSO on, USO off
|
||||
|
||||
if err := m.Commit(buildUDPv4(1000, 53, make([]byte, 800))); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -75,7 +76,7 @@ func TestMultiCoalescerDisabledUDPFallsThrough(t *testing.T) {
|
||||
// TestMultiCoalescerDisabledTCPFallsThrough mirrors the TSO=off case.
|
||||
func TestMultiCoalescerDisabledTCPFallsThrough(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
m := NewMultiCoalescer(w, test.NewLogger(), NewArena(0), false, true) // TSO off, USO on
|
||||
m := NewMultiCoalescer(w, test.NewLogger(), util.NewArena(0), false, true) // TSO off, USO on
|
||||
|
||||
pay := make([]byte, 1200)
|
||||
if err := m.Commit(buildTCPv4(1000, tcpAck, pay)); err != nil {
|
||||
|
||||
@@ -3,7 +3,6 @@ package batch
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/slackhq/nebula/udp"
|
||||
"github.com/slackhq/nebula/util"
|
||||
)
|
||||
|
||||
@@ -16,12 +15,6 @@ type Passthrough struct {
|
||||
cursor int
|
||||
}
|
||||
|
||||
const passthroughBaseNumSlots = 128
|
||||
|
||||
// DefaultPassthroughArenaCap is the recommended arena capacity for a
|
||||
// standalone Passthrough batcher: 128 slots × udp.MTU ≈ 1.1 MiB.
|
||||
const DefaultPassthroughArenaCap = passthroughBaseNumSlots * udp.MTU
|
||||
|
||||
func NewPassthrough(w io.Writer, slots int, arena *util.Arena) *Passthrough {
|
||||
return &Passthrough{
|
||||
out: w,
|
||||
|
||||
@@ -7,14 +7,18 @@ import (
|
||||
|
||||
"github.com/slackhq/nebula/overlay/tio"
|
||||
"github.com/slackhq/nebula/test"
|
||||
"github.com/slackhq/nebula/util"
|
||||
"github.com/slackhq/nebula/wire"
|
||||
)
|
||||
|
||||
// nopTunWriter is a zero-alloc tio.GSOWriter for benchmarks. Discards
|
||||
// everything but satisfies the interface the coalescer detects.
|
||||
type nopTunWriter struct{}
|
||||
|
||||
func (nopTunWriter) Write(p []byte) (int, error) { return len(p), nil }
|
||||
func (nopTunWriter) WriteGSO(hdr []byte, transportHdr []byte, pays [][]byte, _ tio.GSOProto) error {
|
||||
func (nopTunWriter) Write(p []byte) (int, error) { return len(p), nil }
|
||||
func (nopTunWriter) Read(_ []wire.TunPacket, _ []byte) (int, error) { return 0, nil }
|
||||
func (nopTunWriter) Close() error { return nil }
|
||||
func (nopTunWriter) WriteGSO(hdr []byte, transportHdr []byte, pays [][]byte, _ wire.GSOProto) error {
|
||||
return nil
|
||||
}
|
||||
func (nopTunWriter) Capabilities() tio.Capabilities {
|
||||
@@ -71,7 +75,7 @@ func buildICMPv4() []byte {
|
||||
// between batches, and reports per-packet cost.
|
||||
func runCommitBench(b *testing.B, pkts [][]byte, batchSize int) {
|
||||
b.Helper()
|
||||
c := NewTCPCoalescer(nopTunWriter{}, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(nopTunWriter{}, test.NewLogger(), util.NewArena(0))
|
||||
b.ReportAllocs()
|
||||
b.SetBytes(int64(len(pkts[0])))
|
||||
b.ResetTimer()
|
||||
@@ -140,7 +144,7 @@ func BenchmarkCommitNonCoalesceableTCP(b *testing.B) {
|
||||
// is the bench that shows the savings of skipping the lane's re-parse.
|
||||
func runMultiCommitBench(b *testing.B, pkts [][]byte, batchSize int) {
|
||||
b.Helper()
|
||||
m := NewMultiCoalescer(nopTunWriter{}, test.NewLogger(), NewArena(0), true, true)
|
||||
m := NewMultiCoalescer(nopTunWriter{}, test.NewLogger(), util.NewArena(0), true, true)
|
||||
b.ReportAllocs()
|
||||
b.SetBytes(int64(len(pkts[0])))
|
||||
b.ResetTimer()
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
|
||||
"github.com/slackhq/nebula/overlay/tio"
|
||||
"github.com/slackhq/nebula/test"
|
||||
"github.com/slackhq/nebula/util"
|
||||
"github.com/slackhq/nebula/wire"
|
||||
)
|
||||
|
||||
// fakeTunWriter records plain Writes and WriteGSO calls without touching a
|
||||
@@ -53,7 +55,12 @@ func (w *fakeTunWriter) Write(p []byte) (int, error) {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (w *fakeTunWriter) WriteGSO(hdr []byte, transportHdr []byte, pays [][]byte, _ tio.GSOProto) error {
|
||||
// Read and Close exist solely to satisfy tio.Queue; coalescer tests never
|
||||
// invoke them.
|
||||
func (w *fakeTunWriter) Read(_ []wire.TunPacket, _ []byte) (int, error) { return 0, nil }
|
||||
func (w *fakeTunWriter) Close() error { return nil }
|
||||
|
||||
func (w *fakeTunWriter) WriteGSO(hdr []byte, transportHdr []byte, pays [][]byte, _ wire.GSOProto) error {
|
||||
hcopy := make([]byte, len(hdr)+len(transportHdr))
|
||||
copy(hcopy, hdr)
|
||||
copy(hcopy[len(hdr):], transportHdr)
|
||||
@@ -128,7 +135,7 @@ const (
|
||||
|
||||
func TestCoalescerPassthroughWhenGSOUnavailable(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: false}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pkt := buildTCPv4(1000, tcpAck, []byte("hello"))
|
||||
if err := c.Commit(pkt); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -147,7 +154,7 @@ func TestCoalescerPassthroughWhenGSOUnavailable(t *testing.T) {
|
||||
|
||||
func TestCoalescerNonTCPPassthrough(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pkt := make([]byte, 28)
|
||||
pkt[0] = 0x45
|
||||
binary.BigEndian.PutUint16(pkt[2:4], 28)
|
||||
@@ -167,7 +174,7 @@ func TestCoalescerNonTCPPassthrough(t *testing.T) {
|
||||
|
||||
func TestCoalescerSeedThenFlushAlone(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pkt := buildTCPv4(1000, tcpAck, make([]byte, 1000))
|
||||
if err := c.Commit(pkt); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -194,7 +201,7 @@ func TestCoalescerSeedThenFlushAlone(t *testing.T) {
|
||||
|
||||
func TestCoalescerCoalescesAdjacentACKs(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pay := make([]byte, 1200)
|
||||
if err := c.Commit(buildTCPv4(1000, tcpAck, pay)); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -234,7 +241,7 @@ func TestCoalescerCoalescesAdjacentACKs(t *testing.T) {
|
||||
|
||||
func TestCoalescerRejectsSeqGap(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pay := make([]byte, 1200)
|
||||
if err := c.Commit(buildTCPv4(1000, tcpAck, pay)); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -253,7 +260,7 @@ func TestCoalescerRejectsSeqGap(t *testing.T) {
|
||||
|
||||
func TestCoalescerRejectsFlagMismatch(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pay := make([]byte, 1200)
|
||||
if err := c.Commit(buildTCPv4(1000, tcpAck, pay)); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -274,7 +281,7 @@ func TestCoalescerRejectsFlagMismatch(t *testing.T) {
|
||||
|
||||
func TestCoalescerRejectsFIN(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
fin := buildTCPv4(1000, tcpAck|tcpFin, []byte("x"))
|
||||
if err := c.Commit(fin); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -290,7 +297,7 @@ func TestCoalescerRejectsFIN(t *testing.T) {
|
||||
|
||||
func TestCoalescerShortLastSegmentClosesChain(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
full := make([]byte, 1200)
|
||||
half := make([]byte, 500)
|
||||
if err := c.Commit(buildTCPv4(1000, tcpAck, full)); err != nil {
|
||||
@@ -325,7 +332,7 @@ func TestCoalescerShortLastSegmentClosesChain(t *testing.T) {
|
||||
|
||||
func TestCoalescerPSHFinalizesChain(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pay := make([]byte, 1200)
|
||||
if err := c.Commit(buildTCPv4(1000, tcpAck, pay)); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -355,7 +362,7 @@ func TestCoalescerPSHFinalizesChain(t *testing.T) {
|
||||
// coalescer drops it the sender's push signal never reaches the receiver.
|
||||
func TestCoalescerPropagatesPSHFromAppended(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pay := make([]byte, 1200)
|
||||
// Seed has no PSH; second segment carries PSH and seals the chain.
|
||||
if err := c.Commit(buildTCPv4(1000, tcpAck, pay)); err != nil {
|
||||
@@ -383,7 +390,7 @@ func TestCoalescerPropagatesPSHFromAppended(t *testing.T) {
|
||||
|
||||
func TestCoalescerRejectsDifferentFlow(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pay := make([]byte, 1200)
|
||||
p1 := buildTCPv4(1000, tcpAck, pay)
|
||||
p2 := buildTCPv4(2200, tcpAck, pay)
|
||||
@@ -405,7 +412,7 @@ func TestCoalescerRejectsDifferentFlow(t *testing.T) {
|
||||
|
||||
func TestCoalescerRejectsIPOptions(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pay := make([]byte, 500)
|
||||
pkt := buildTCPv4(1000, tcpAck, pay)
|
||||
// Bump IHL to 6 to simulate 4 bytes of IP options. Don't actually add
|
||||
@@ -425,7 +432,7 @@ func TestCoalescerRejectsIPOptions(t *testing.T) {
|
||||
|
||||
func TestCoalescerCapBySegments(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pay := make([]byte, 512)
|
||||
seq := uint32(1000)
|
||||
for i := 0; i < tcpCoalesceMaxSegs+5; i++ {
|
||||
@@ -449,7 +456,7 @@ func TestCoalescerCapBySegments(t *testing.T) {
|
||||
// flows coalesce independently in a single Flush.
|
||||
func TestCoalescerMultipleFlowsInSameBatch(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pay := make([]byte, 1200)
|
||||
|
||||
// Flow A: sport 1000. Flow B: sport 3000.
|
||||
@@ -506,7 +513,7 @@ func TestCoalescerMultipleFlowsInSameBatch(t *testing.T) {
|
||||
// writing passthrough packets synchronously.
|
||||
func TestCoalescerPreservesArrivalOrder(t *testing.T) {
|
||||
w := &orderedFakeWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
// Sequence: coalesceable TCP, ICMP (passthrough), coalesceable TCP on
|
||||
// a different flow. Expected emit order: gso(X), plain(ICMP), gso(Y).
|
||||
pay := make([]byte, 1200)
|
||||
@@ -549,7 +556,12 @@ func (w *orderedFakeWriter) Write(p []byte) (int, error) {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (w *orderedFakeWriter) WriteGSO(hdr []byte, transportHdr []byte, pays [][]byte, _ tio.GSOProto) error {
|
||||
// Read and Close exist solely to satisfy tio.Queue; order tests never
|
||||
// invoke them.
|
||||
func (w *orderedFakeWriter) Read(_ []wire.TunPacket, _ []byte) (int, error) { return 0, nil }
|
||||
func (w *orderedFakeWriter) Close() error { return nil }
|
||||
|
||||
func (w *orderedFakeWriter) WriteGSO(hdr []byte, transportHdr []byte, pays [][]byte, _ wire.GSOProto) error {
|
||||
w.events = append(w.events, "gso")
|
||||
return nil
|
||||
}
|
||||
@@ -574,7 +586,7 @@ func stringSliceEq(a, b []string) bool {
|
||||
// packet (SYN) mid-flow only flushes its own flow, not others.
|
||||
func TestCoalescerInterleavedFlowsPreserveOrdering(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pay := make([]byte, 1200)
|
||||
|
||||
// Flow A two segments.
|
||||
@@ -679,7 +691,7 @@ func buildTCPv6(tcLow byte, seq uint32, flags byte, payload []byte) []byte {
|
||||
// retains ECE on the wire.
|
||||
func TestCoalescerCoalescesEceFlow(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pay := make([]byte, 1200)
|
||||
flags := byte(tcpAck | tcpEce)
|
||||
if err := c.Commit(buildTCPv4(1000, flags, pay)); err != nil {
|
||||
@@ -708,7 +720,7 @@ func TestCoalescerCoalescesEceFlow(t *testing.T) {
|
||||
// in-flow segment seeds a new slot rather than extending the prior burst.
|
||||
func TestCoalescerCwrSealsFlow(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pay := make([]byte, 1200)
|
||||
if err := c.Commit(buildTCPv4(1000, tcpAck, pay)); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -741,7 +753,7 @@ func TestCoalescerCwrSealsFlow(t *testing.T) {
|
||||
// a CE-echoing window or none.
|
||||
func TestCoalescerEceMismatchReseeds(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pay := make([]byte, 1200)
|
||||
if err := c.Commit(buildTCPv4(1000, tcpAck|tcpEce, pay)); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -766,7 +778,7 @@ func TestCoalescerEceMismatchReseeds(t *testing.T) {
|
||||
// CE-marked packet still coalesces, and the merged superpacket carries CE.
|
||||
func TestCoalescerMergesCEMark(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pay := make([]byte, 1200)
|
||||
if err := c.Commit(buildTCPv4WithToS(ecnECT0, 1000, tcpAck, pay)); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -797,7 +809,7 @@ func TestCoalescerMergesCEMark(t *testing.T) {
|
||||
// headersMatch did not also relax DSCP — different DSCP must still split.
|
||||
func TestCoalescerDscpMismatchReseeds(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pay := make([]byte, 1200)
|
||||
// Same ECN (Not-ECT), different DSCP (0x10 vs 0x20 in upper 6 bits).
|
||||
tosA := byte(0x10<<2) | ecnNotECT
|
||||
@@ -820,7 +832,7 @@ func TestCoalescerDscpMismatchReseeds(t *testing.T) {
|
||||
// TestCoalescerCoalescesEceFlow.
|
||||
func TestCoalescerIPv6CoalescesEceFlow(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pay := make([]byte, 1200)
|
||||
flags := byte(tcpAck | tcpEce)
|
||||
if err := c.Commit(buildTCPv6(0, 1000, flags, pay)); err != nil {
|
||||
@@ -851,7 +863,7 @@ func TestCoalescerIPv6CoalescesEceFlow(t *testing.T) {
|
||||
// seen had the wire never reordered.
|
||||
func TestCoalescerSortsReorderedSeedsAndMerges(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pay := make([]byte, 1200)
|
||||
// Arrival order: seq 1000, 3400, 2200. The 3400 seeds a separate slot
|
||||
// because 3400 != nextSeq=2200, then 2200 fails to extend the 3400 slot
|
||||
@@ -887,7 +899,7 @@ func TestCoalescerSortsReorderedSeedsAndMerges(t *testing.T) {
|
||||
// without any cross-flow contamination.
|
||||
func TestCoalescerSortAcrossFlowsMergesEachIndependently(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pay := make([]byte, 1200)
|
||||
// Flow A (sport 1000) seq 100, 1300; flow B (sport 3000) seq 500, 1700.
|
||||
// Arrival: A.1300, B.1700, A.100, B.500 — every flow reordered.
|
||||
@@ -938,7 +950,7 @@ func TestCoalescerSortAcrossFlowsMergesEachIndependently(t *testing.T) {
|
||||
// boundary by an arbitrary number of segments.
|
||||
func TestCoalescerSortKeepsPSHBoundary(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pay := make([]byte, 1200)
|
||||
// Seq 1000 (no PSH) + 2200 (PSH) → seal one slot with PSH set.
|
||||
// Seq 3400 (no PSH) is contiguous to 3400 from seq 2200+1200; without
|
||||
@@ -966,7 +978,7 @@ func TestCoalescerSortKeepsPSHBoundary(t *testing.T) {
|
||||
// is sorted/merged independently.
|
||||
func TestCoalescerSortKeepsPassthroughBarrier(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pay := make([]byte, 1200)
|
||||
// First two segments seed S1 (then a 3400 reorder seeds S2).
|
||||
if err := c.Commit(buildTCPv4(1000, tcpAck, pay)); err != nil {
|
||||
@@ -999,7 +1011,7 @@ func TestCoalescerSortKeepsPassthroughBarrier(t *testing.T) {
|
||||
// TestCoalescerMergesCEMark. ECN bits live in TC[1:0] = byte 1 mask 0x30.
|
||||
func TestCoalescerIPv6MergesCEMark(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), NewArena(0))
|
||||
c := NewTCPCoalescer(w, test.NewLogger(), util.NewArena(0))
|
||||
pay := make([]byte, 1200)
|
||||
// tcLow is the low 4 bits of TC; ECN occupies the bottom 2 of those.
|
||||
if err := c.Commit(buildTCPv6(ecnECT0, 1000, tcpAck, pay)); err != nil {
|
||||
|
||||
@@ -3,6 +3,8 @@ package batch
|
||||
import (
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
|
||||
"github.com/slackhq/nebula/util"
|
||||
)
|
||||
|
||||
// buildUDPv4 builds a minimal IPv4+UDP packet with the given payload and ports.
|
||||
@@ -60,7 +62,7 @@ func buildUDPv6(sport, dport uint16, payload []byte) []byte {
|
||||
|
||||
func TestUDPCoalescerPassthroughWhenGSOUnavailable(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: false}
|
||||
c := NewUDPCoalescer(w, NewArena(0))
|
||||
c := NewUDPCoalescer(w, util.NewArena(0))
|
||||
pkt := buildUDPv4(1000, 53, make([]byte, 100))
|
||||
if err := c.Commit(pkt); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -78,7 +80,7 @@ func TestUDPCoalescerPassthroughWhenGSOUnavailable(t *testing.T) {
|
||||
|
||||
func TestUDPCoalescerNonUDPPassthrough(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewUDPCoalescer(w, NewArena(0))
|
||||
c := NewUDPCoalescer(w, util.NewArena(0))
|
||||
// ICMP packet
|
||||
pkt := make([]byte, 28)
|
||||
pkt[0] = 0x45
|
||||
@@ -99,7 +101,7 @@ func TestUDPCoalescerNonUDPPassthrough(t *testing.T) {
|
||||
|
||||
func TestUDPCoalescerSeedThenFlushAlone(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewUDPCoalescer(w, NewArena(0))
|
||||
c := NewUDPCoalescer(w, util.NewArena(0))
|
||||
pkt := buildUDPv4(1000, 53, make([]byte, 800))
|
||||
if err := c.Commit(pkt); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -116,7 +118,7 @@ func TestUDPCoalescerSeedThenFlushAlone(t *testing.T) {
|
||||
|
||||
func TestUDPCoalescerCoalescesEqualSized(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewUDPCoalescer(w, NewArena(0))
|
||||
c := NewUDPCoalescer(w, util.NewArena(0))
|
||||
pay := make([]byte, 1200)
|
||||
for i := 0; i < 3; i++ {
|
||||
if err := c.Commit(buildUDPv4(1000, 53, pay)); err != nil {
|
||||
@@ -156,7 +158,7 @@ func TestUDPCoalescerCoalescesEqualSized(t *testing.T) {
|
||||
// Last segment may be shorter, sealing the chain.
|
||||
func TestUDPCoalescerShortLastSegmentSeals(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewUDPCoalescer(w, NewArena(0))
|
||||
c := NewUDPCoalescer(w, util.NewArena(0))
|
||||
full := make([]byte, 1200)
|
||||
tail := make([]byte, 600)
|
||||
if err := c.Commit(buildUDPv4(1000, 53, full)); err != nil {
|
||||
@@ -189,7 +191,7 @@ func TestUDPCoalescerShortLastSegmentSeals(t *testing.T) {
|
||||
// A larger-than-gsoSize packet cannot extend the slot — it reseeds.
|
||||
func TestUDPCoalescerLargerThanSeedReseeds(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewUDPCoalescer(w, NewArena(0))
|
||||
c := NewUDPCoalescer(w, util.NewArena(0))
|
||||
if err := c.Commit(buildUDPv4(1000, 53, make([]byte, 800))); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -207,7 +209,7 @@ func TestUDPCoalescerLargerThanSeedReseeds(t *testing.T) {
|
||||
// Different 5-tuples must not coalesce.
|
||||
func TestUDPCoalescerDifferentFlowsKeepSeparate(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewUDPCoalescer(w, NewArena(0))
|
||||
c := NewUDPCoalescer(w, util.NewArena(0))
|
||||
pay := make([]byte, 800)
|
||||
if err := c.Commit(buildUDPv4(1000, 53, pay)); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -238,7 +240,7 @@ func TestUDPCoalescerDifferentFlowsKeepSeparate(t *testing.T) {
|
||||
// Caps at udpCoalesceMaxSegs.
|
||||
func TestUDPCoalescerCapsAtMaxSegs(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewUDPCoalescer(w, NewArena(0))
|
||||
c := NewUDPCoalescer(w, util.NewArena(0))
|
||||
pay := make([]byte, 100)
|
||||
for i := 0; i < udpCoalesceMaxSegs+5; i++ {
|
||||
if err := c.Commit(buildUDPv4(1000, 53, pay)); err != nil {
|
||||
@@ -264,7 +266,7 @@ func TestUDPCoalescerCapsAtMaxSegs(t *testing.T) {
|
||||
// CE marks on appended segments must be merged into the seed's IP TOS.
|
||||
func TestUDPCoalescerMergesCEMark(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewUDPCoalescer(w, NewArena(0))
|
||||
c := NewUDPCoalescer(w, util.NewArena(0))
|
||||
pay := make([]byte, 800)
|
||||
pkt0 := buildUDPv4(1000, 53, pay) // ECN=00
|
||||
pkt1 := buildUDPv4(1000, 53, pay)
|
||||
@@ -293,7 +295,7 @@ func TestUDPCoalescerMergesCEMark(t *testing.T) {
|
||||
// IPv6 path: same flow, equal-sized → coalesced.
|
||||
func TestUDPCoalescerIPv6Coalesces(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewUDPCoalescer(w, NewArena(0))
|
||||
c := NewUDPCoalescer(w, util.NewArena(0))
|
||||
pay := make([]byte, 1200)
|
||||
for i := 0; i < 3; i++ {
|
||||
if err := c.Commit(buildUDPv6(1000, 53, pay)); err != nil {
|
||||
@@ -329,7 +331,7 @@ func TestUDPCoalescerIPv6Coalesces(t *testing.T) {
|
||||
// DSCP differences must reseed (headers don't match outside ECN).
|
||||
func TestUDPCoalescerDSCPMismatchReseeds(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewUDPCoalescer(w, NewArena(0))
|
||||
c := NewUDPCoalescer(w, util.NewArena(0))
|
||||
pay := make([]byte, 800)
|
||||
pkt0 := buildUDPv4(1000, 53, pay)
|
||||
pkt1 := buildUDPv4(1000, 53, pay)
|
||||
@@ -351,7 +353,7 @@ func TestUDPCoalescerDSCPMismatchReseeds(t *testing.T) {
|
||||
// Fragmented IPv4 must not be coalesced.
|
||||
func TestUDPCoalescerFragmentedIPv4PassesThrough(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewUDPCoalescer(w, NewArena(0))
|
||||
c := NewUDPCoalescer(w, util.NewArena(0))
|
||||
pkt := buildUDPv4(1000, 53, make([]byte, 200))
|
||||
binary.BigEndian.PutUint16(pkt[6:8], 0x2000) // MF=1
|
||||
if err := c.Commit(pkt); err != nil {
|
||||
@@ -368,7 +370,7 @@ func TestUDPCoalescerFragmentedIPv4PassesThrough(t *testing.T) {
|
||||
// IPv4 with options is not admissible (we require IHL=5).
|
||||
func TestUDPCoalescerIPv4WithOptionsPassesThrough(t *testing.T) {
|
||||
w := &fakeTunWriter{gsoEnabled: true}
|
||||
c := NewUDPCoalescer(w, NewArena(0))
|
||||
c := NewUDPCoalescer(w, util.NewArena(0))
|
||||
pkt := buildUDPv4(1000, 53, make([]byte, 200))
|
||||
pkt[0] = 0x46 // IHL = 6 (24-byte IPv4 header — has options)
|
||||
if err := c.Commit(pkt); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user