tailscale/wgengine/magicsock/magicsock_linux_test.go
Brad Fitzpatrick 5ef3713c9f cmd/vet: add subtestnames analyzer; fix all existing violations
Add a new vet analyzer that checks t.Run subtest names don't contain
characters requiring quoting when re-running via "go test -run". This
enforces the style guide rule: don't use spaces or punctuation in
subtest names.

The analyzer flags:
- Direct t.Run calls with string literal names containing spaces,
  regex metacharacters, quotes, or other problematic characters
- Table-driven t.Run(tt.name, ...) calls where tt ranges over a
  slice/map literal with bad name field values

Also fix all 978 existing violations across 81 test files, replacing
spaces with hyphens and shortening long sentence-like names to concise
hyphenated forms.

Updates #19242

Change-Id: Ib0ad96a111bd8e764582d1d4902fe2599454ab65
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2026-04-05 15:52:51 -07:00

225 lines
6.2 KiB
Go

// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
package magicsock
import (
"bytes"
"encoding/binary"
"net/netip"
"testing"
"golang.org/x/net/bpf"
"golang.org/x/sys/cpu"
"golang.org/x/sys/unix"
"tailscale.com/disco"
)
func TestParseUDPPacket(t *testing.T) {
src4 := netip.MustParseAddrPort("127.0.0.1:12345")
dst4 := netip.MustParseAddrPort("127.0.0.2:54321")
src6 := netip.MustParseAddrPort("[::1]:12345")
dst6 := netip.MustParseAddrPort("[::2]:54321")
udp4Packet := []byte{
// IPv4 header
0x45, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00,
0x40, 0x11, 0x00, 0x00,
0x7f, 0x00, 0x00, 0x01, // source ip
0x7f, 0x00, 0x00, 0x02, // dest ip
// UDP header
0x30, 0x39, // src port
0xd4, 0x31, // dest port
0x00, 0x12, // length; 8 bytes header + 10 bytes payload = 18 bytes
0x00, 0x00, // checksum; unused
// Payload: disco magic plus 4 bytes
0x54, 0x53, 0xf0, 0x9f, 0x92, 0xac, 0x00, 0x01, 0x02, 0x03,
}
udp6Packet := []byte{
// IPv6 header
0x60, 0x00, 0x00, 0x00,
0x00, 0x12, // payload length
0x11, // next header: UDP
0x00, // hop limit; unused
// Source IP
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
// Dest IP
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
// UDP header
0x30, 0x39, // src port
0xd4, 0x31, // dest port
0x00, 0x12, // length; 8 bytes header + 10 bytes payload = 18 bytes
0x00, 0x00, // checksum; unused
// Payload: disco magic plus 4 bytes
0x54, 0x53, 0xf0, 0x9f, 0x92, 0xac, 0x00, 0x01, 0x02, 0x03,
}
// Verify that parsing the UDP packet works correctly.
t.Run("IPv4", func(t *testing.T) {
src, dst, payload := parseUDPPacket(udp4Packet, false)
if src != src4 {
t.Errorf("src = %v; want %v", src, src4)
}
if dst != dst4 {
t.Errorf("dst = %v; want %v", dst, dst4)
}
if !bytes.HasPrefix(payload, []byte(disco.Magic)) {
t.Errorf("payload = %x; must start with %x", payload, disco.Magic)
}
})
t.Run("IPv6", func(t *testing.T) {
src, dst, payload := parseUDPPacket(udp6Packet, true)
if src != src6 {
t.Errorf("src = %v; want %v", src, src6)
}
if dst != dst6 {
t.Errorf("dst = %v; want %v", dst, dst6)
}
if !bytes.HasPrefix(payload, []byte(disco.Magic)) {
t.Errorf("payload = %x; must start with %x", payload, disco.Magic)
}
})
t.Run("Truncated", func(t *testing.T) {
truncateBy := func(b []byte, n int) []byte {
if n >= len(b) {
return nil
}
return b[:len(b)-n]
}
src, dst, payload := parseUDPPacket(truncateBy(udp4Packet, 11), false)
if payload != nil {
t.Errorf("payload = %x; want nil", payload)
}
if src.IsValid() || dst.IsValid() {
t.Errorf("src = %v, dst = %v; want invalid", src, dst)
}
src, dst, payload = parseUDPPacket(truncateBy(udp6Packet, 11), true)
if payload != nil {
t.Errorf("payload = %x; want nil", payload)
}
if src.IsValid() || dst.IsValid() {
t.Errorf("src = %v, dst = %v; want invalid", src, dst)
}
})
}
func TestEthernetProto(t *testing.T) {
htons := func(x uint16) int {
// Network byte order is big-endian; write the value as
// big-endian to a byte slice and read it back in the native
// endian-ness. This is a no-op on a big-endian platform and a
// byte swap on a little-endian platform.
var b [2]byte
binary.BigEndian.PutUint16(b[:], x)
return int(binary.NativeEndian.Uint16(b[:]))
}
if v4 := ethernetProtoIPv4(); v4 != htons(unix.ETH_P_IP) {
t.Errorf("ethernetProtoIPv4 = 0x%04x; want 0x%04x", v4, htons(unix.ETH_P_IP))
}
if v6 := ethernetProtoIPv6(); v6 != htons(unix.ETH_P_IPV6) {
t.Errorf("ethernetProtoIPv6 = 0x%04x; want 0x%04x", v6, htons(unix.ETH_P_IPV6))
}
// As a way to verify that the htons function is working correctly,
// assert that the ETH_P_IP value returned from our function matches
// the value defined in the unix package based on whether the host is
// big-endian (network byte order) or little-endian.
if cpu.IsBigEndian {
if v4 := ethernetProtoIPv4(); v4 != unix.ETH_P_IP {
t.Errorf("ethernetProtoIPv4 = 0x%04x; want 0x%04x", v4, unix.ETH_P_IP)
}
} else {
if v4 := ethernetProtoIPv4(); v4 == unix.ETH_P_IP {
t.Errorf("ethernetProtoIPv4 = 0x%04x; want 0x%04x", v4, htons(unix.ETH_P_IP))
} else {
t.Logf("ethernetProtoIPv4 = 0x%04x, correctly different from 0x%04x", v4, unix.ETH_P_IP)
}
}
}
func TestBpfDiscardV4(t *testing.T) {
// Good packet as a reference for what should not be rejected
udp4Packet := []byte{
// IPv4 header
0x45, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00,
0x40, 0x11, 0x00, 0x00,
0x7f, 0x00, 0x00, 0x01, // source ip
0x7f, 0x00, 0x00, 0x02, // dest ip
// UDP header
0x30, 0x39, // src port
0xd4, 0x31, // dest port
0x00, 0x12, // length; 8 bytes header + 10 bytes payload = 18 bytes
0x00, 0x00, // checksum; unused
// Payload: disco magic plus 32 bytes for key and 24 bytes for nonce
0x54, 0x53, 0xf0, 0x9f, 0x92, 0xac, 0x00, 0x01, 0x02, 0x03,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
}
vm, err := bpf.NewVM(magicsockFilterV4)
if err != nil {
t.Fatalf("failed creating BPF VM: %v", err)
}
tests := []struct {
name string
replace map[int]byte
accept bool
}{
{
name: "base-accepted-datagram",
replace: map[int]byte{},
accept: true,
},
{
name: "more-fragments",
replace: map[int]byte{
6: 0x20,
},
accept: false,
},
{
name: "some-fragment",
replace: map[int]byte{
7: 0x01,
},
accept: false,
},
}
udp4PacketChanged := make([]byte, len(udp4Packet))
copy(udp4PacketChanged, udp4Packet)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for k, v := range tt.replace {
udp4PacketChanged[k] = v
}
ret, err := vm.Run(udp4PacketChanged)
if err != nil {
t.Fatalf("BPF VM error: %v", err)
}
if (ret != 0) != tt.accept {
t.Errorf("expected accept=%v, got ret=%v", tt.accept, ret)
}
})
}
}