checkpt, try to parse packets only once pt2

This commit is contained in:
JackDoan
2026-05-07 11:26:17 -05:00
parent 0375aff451
commit 5bdf645b0b
10 changed files with 1150 additions and 92 deletions

View File

@@ -0,0 +1,174 @@
package batch
import (
"net/netip"
"testing"
"github.com/slackhq/nebula/firewall"
)
// TestParseInboundParity asserts that ParseInbound + Key.Hydrate produces
// the same firewall.Packet that the lenient baseline parsers (which
// mirror outside.go's parseV4/parseV6 with incoming=true) produce for
// every shape we care about. Catches drift between the unified
// parse-then-hydrate flow and the production newPacket behavior so
// swapping one for the other is observably safe.
func TestParseInboundParity(t *testing.T) {
cases := []struct {
name string
pkt []byte
v6 bool
}{
{"tcp_v4", buildTCPv4Ports(1234, 443, 1000, tcpAck, []byte("payload")), false},
{"tcp_v4_psh", buildTCPv4Ports(1234, 443, 2000, tcpAckPsh, make([]byte, 1200)), false},
{"udp_v4", buildUDPv4(40000, 53, []byte("dnsquery")), false},
{"icmp_v4", buildICMPv4(), false},
{"tcp_v6", buildTCPv6(0, 5000, tcpAck, make([]byte, 800)), true},
{"udp_v6", buildUDPv6(40001, 53, []byte("v6dns")), true},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
var fpUnified, fpBaseline firewall.Packet
var parsed RxParsed
if err := ParseInbound(tc.pkt, &parsed); err != nil {
t.Fatalf("ParseInbound: %v", err)
}
parsed.Key.Hydrate(&fpUnified)
var ok bool
if tc.v6 {
ok = parseV6InboundBaseline(tc.pkt, &fpBaseline)
} else {
ok = parseV4InboundBaseline(tc.pkt, &fpBaseline)
}
if !ok {
t.Fatalf("baseline parse failed")
}
if fpUnified != fpBaseline {
t.Errorf("firewall.Packet mismatch:\n unified: %+v\n baseline: %+v", fpUnified, fpBaseline)
}
})
}
}
// TestParseInboundFlowKey checks that the coalescer hint the unified parser
// produces matches what parseTCPBase/parseUDP would produce on the same
// packet — same flowKey, ipHdrLen, payLen, etc. The hint is only valid
// when Kind is RxKindTCP/RxKindUDP.
func TestParseInboundFlowKey(t *testing.T) {
t.Run("tcp_v4", func(t *testing.T) {
pkt := buildTCPv4Ports(1234, 443, 5000, tcpAck, make([]byte, 800))
var parsed RxParsed
if err := ParseInbound(pkt, &parsed); err != nil {
t.Fatal(err)
}
if parsed.Kind != RxKindTCP {
t.Fatalf("kind=%v want TCP", parsed.Kind)
}
ref, ok := parseTCPBase(pkt)
if !ok {
t.Fatal("parseTCPBase failed")
}
if parsed.tcp != ref {
t.Errorf("parsedTCP mismatch:\n unified: %+v\n ref: %+v", parsed.tcp, ref)
}
})
t.Run("udp_v4", func(t *testing.T) {
pkt := buildUDPv4(40000, 53, []byte("dnsquery"))
var parsed RxParsed
if err := ParseInbound(pkt, &parsed); err != nil {
t.Fatal(err)
}
if parsed.Kind != RxKindUDP {
t.Fatalf("kind=%v want UDP", parsed.Kind)
}
ref, ok := parseUDP(pkt)
if !ok {
t.Fatal("parseUDP failed")
}
if parsed.udp != ref {
t.Errorf("parsedUDP mismatch:\n unified: %+v\n ref: %+v", parsed.udp, ref)
}
})
t.Run("tcp_v6", func(t *testing.T) {
pkt := buildTCPv6(0, 9000, tcpAck, make([]byte, 800))
var parsed RxParsed
if err := ParseInbound(pkt, &parsed); err != nil {
t.Fatal(err)
}
if parsed.Kind != RxKindTCP {
t.Fatalf("kind=%v want TCP", parsed.Kind)
}
ref, ok := parseTCPBase(pkt)
if !ok {
t.Fatal("parseTCPBase failed")
}
if parsed.tcp != ref {
t.Errorf("parsedTCP mismatch:\n unified: %+v\n ref: %+v", parsed.tcp, ref)
}
})
}
// TestParseInboundICMPPassthrough confirms ICMP packets populate the
// conntrack key (including the ICMP identifier in RemotePort) but stay
// RxKindPassthrough so the batcher writes them verbatim. After Hydrate
// the firewall.Packet form should match what the legacy parseV4 produced.
func TestParseInboundICMPPassthrough(t *testing.T) {
pkt := buildICMPv4()
// Stamp a non-zero identifier into the ICMP header so we can check
// RemotePort gets it.
pkt[20] = 8 // type=echo
pkt[24] = 0xab
pkt[25] = 0xcd
var parsed RxParsed
if err := ParseInbound(pkt, &parsed); err != nil {
t.Fatal(err)
}
if parsed.Kind != RxKindPassthrough {
t.Errorf("kind=%v want Passthrough", parsed.Kind)
}
var fp firewall.Packet
parsed.Key.Hydrate(&fp)
if fp.Protocol != firewall.ProtoICMP {
t.Errorf("Protocol=%d want %d", fp.Protocol, firewall.ProtoICMP)
}
if fp.RemotePort != 0xabcd {
t.Errorf("RemotePort=0x%x want 0xabcd", fp.RemotePort)
}
if fp.LocalPort != 0 {
t.Errorf("LocalPort=%d want 0", fp.LocalPort)
}
wantRemote := netip.MustParseAddr("10.0.0.1")
wantLocal := netip.MustParseAddr("10.0.0.2")
if fp.RemoteAddr != wantRemote || fp.LocalAddr != wantLocal {
t.Errorf("addrs: remote=%v local=%v want %v/%v", fp.RemoteAddr, fp.LocalAddr, wantRemote, wantLocal)
}
}
// TestParseInboundV4Fragment confirms a fragmented v4 packet fills the
// conntrack key with Fragment=true and falls into Passthrough on the
// coalescer side.
func TestParseInboundV4Fragment(t *testing.T) {
// Build a TCP packet then twiddle the IP flags to make it look like a
// non-first fragment (offset != 0).
pkt := buildTCPv4Ports(1234, 443, 1000, tcpAck, []byte("payload"))
// Set a non-zero fragment offset (bytes 6-7, low 13 bits).
pkt[6] = 0x00
pkt[7] = 0x10 // offset = 16 (in 8-byte units)
var parsed RxParsed
if err := ParseInbound(pkt, &parsed); err != nil {
t.Fatal(err)
}
if !parsed.Key.Fragment {
t.Error("Fragment=false, want true")
}
if parsed.Kind != RxKindPassthrough {
t.Errorf("kind=%v want Passthrough", parsed.Kind)
}
}