Files
olm/device/middle_device_test.go

217 lines
4.9 KiB
Go

package device
import (
"net/netip"
"testing"
"github.com/fosrl/newt/bind"
"github.com/fosrl/newt/util"
)
// buildIPv4UDPPacket builds a minimal IPv4/UDP packet (no options) carrying payload.
func buildIPv4UDPPacket(payload []byte) []byte {
const ipHeaderLen = 20
const udpHeaderLen = 8
packet := make([]byte, ipHeaderLen+udpHeaderLen+len(payload))
packet[0] = 0x45 // version 4, IHL 5
packet[9] = 17 // protocol: UDP
copy(packet[ipHeaderLen+udpHeaderLen:], payload)
return packet
}
func TestExtractDestIP(t *testing.T) {
tests := []struct {
name string
packet []byte
wantIP string
wantOk bool
}{
{
name: "IPv4 packet",
packet: []byte{
0x45, 0x00, 0x00, 0x54, 0x00, 0x00, 0x40, 0x00,
0x40, 0x11, 0x00, 0x00, 0xc0, 0xa8, 0x01, 0x01,
0x0a, 0x1e, 0x1e, 0x1e, // Dest IP: 10.30.30.30
},
wantIP: "10.30.30.30",
wantOk: true,
},
{
name: "Too short packet",
packet: []byte{0x45, 0x00},
wantIP: "",
wantOk: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotIP, gotOk := extractDestIP(tt.packet)
if gotOk != tt.wantOk {
t.Errorf("extractDestIP() ok = %v, want %v", gotOk, tt.wantOk)
return
}
if tt.wantOk {
wantAddr := netip.MustParseAddr(tt.wantIP)
if gotIP != wantAddr {
t.Errorf("extractDestIP() ip = %v, want %v", gotIP, wantAddr)
}
}
})
}
}
func TestGetProtocol(t *testing.T) {
tests := []struct {
name string
packet []byte
wantProto uint8
wantOk bool
}{
{
name: "UDP packet",
packet: []byte{
0x45, 0x00, 0x00, 0x54, 0x00, 0x00, 0x40, 0x00,
0x40, 0x11, 0x00, 0x00, 0xc0, 0xa8, 0x01, 0x01, // Protocol: UDP (17) at byte 9
0x0a, 0x1e, 0x1e, 0x1e,
},
wantProto: 17,
wantOk: true,
},
{
name: "Too short",
packet: []byte{0x45, 0x00},
wantProto: 0,
wantOk: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotProto, gotOk := util.GetProtocol(tt.packet)
if gotOk != tt.wantOk {
t.Errorf("GetProtocol() ok = %v, want %v", gotOk, tt.wantOk)
return
}
if gotProto != tt.wantProto {
t.Errorf("GetProtocol() proto = %v, want %v", gotProto, tt.wantProto)
}
})
}
}
func TestIsLeakedMagicPacket(t *testing.T) {
request := make([]byte, bind.MagicTestRequestLen)
copy(request, bind.MagicTestRequest)
response := make([]byte, bind.MagicTestResponseLen)
copy(response, bind.MagicTestResponse)
tests := []struct {
name string
packet []byte
want bool
}{
{
name: "magic test request leaked into tunnel",
packet: buildIPv4UDPPacket(request),
want: true,
},
{
name: "magic test response leaked into tunnel",
packet: buildIPv4UDPPacket(response),
want: true,
},
{
name: "ordinary UDP payload",
packet: buildIPv4UDPPacket([]byte("just some ordinary application data")),
want: false,
},
{
name: "too short to be a packet",
packet: []byte{0x45, 0x00},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := isLeakedMagicPacket(tt.packet); got != tt.want {
t.Errorf("isLeakedMagicPacket() = %v, want %v", got, tt.want)
}
})
}
}
func BenchmarkExtractDestIP(b *testing.B) {
packet := []byte{
0x45, 0x00, 0x00, 0x54, 0x00, 0x00, 0x40, 0x00,
0x40, 0x11, 0x00, 0x00, 0xc0, 0xa8, 0x01, 0x01,
0x0a, 0x1e, 0x1e, 0x1e,
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
extractDestIP(packet)
}
}
func TestFilterDownstreamBufsNoDropIsAllocFree(t *testing.T) {
bufs := make([][]byte, 128)
for i := range bufs {
bufs[i] = buildIPv4UDPPacket(make([]byte, 1372))
}
allocs := testing.AllocsPerRun(1000, func() {
out := filterDownstreamBufs(bufs, nil, 0)
if len(out) != len(bufs) {
t.Fatalf("expected no packets dropped, got %d/%d", len(out), len(bufs))
}
})
if allocs != 0 {
t.Errorf("filterDownstreamBufs() with nothing to drop allocated %v times per call, want 0", allocs)
}
}
func TestFilterDownstreamBufsDropsMagicPacket(t *testing.T) {
request := make([]byte, bind.MagicTestRequestLen)
copy(request, bind.MagicTestRequest)
bufs := [][]byte{
buildIPv4UDPPacket([]byte("ordinary payload one")),
buildIPv4UDPPacket(request),
buildIPv4UDPPacket([]byte("ordinary payload two")),
}
out := filterDownstreamBufs(bufs, nil, 0)
if len(out) != 2 {
t.Fatalf("expected 1 packet dropped, got %d remaining", len(out))
}
}
func BenchmarkFilterDownstreamBufsNoDrop(b *testing.B) {
bufs := make([][]byte, 128)
for i := range bufs {
bufs[i] = buildIPv4UDPPacket(make([]byte, 1372))
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
filterDownstreamBufs(bufs, nil, 0)
}
}
func BenchmarkIsLeakedMagicPacket(b *testing.B) {
// A typical ~1400 byte ordinary application payload (the common case on the
// hot path - almost every real packet should look like this).
ordinary := buildIPv4UDPPacket(make([]byte, 1372))
b.ResetTimer()
for i := 0; i < b.N; i++ {
isLeakedMagicPacket(ordinary)
}
}