wgengine/magicsock: make endpoint.bestAddr Geneve-aware (#16195)

This commit adds a new type to magicsock, epAddr, which largely ends up
replacing netip.AddrPort in packet I/O paths throughout, enabling
Geneve encapsulation over UDP awareness.

The conn.ReceiveFunc for UDP has been revamped to fix and more clearly
distinguish the different classes of packets we expect to receive: naked
STUN binding messages, naked disco, naked WireGuard, Geneve-encapsulated
disco, and Geneve-encapsulated WireGuard.

Prior to this commit, STUN matching logic in the RX path could swallow
a naked WireGuard packet if the keypair index, which is randomly
generated, happened to overlap with a subset of the STUN magic cookie.

Updates tailscale/corp#27502
Updates tailscale/corp#29326

Signed-off-by: Jordan Whited <jordan@tailscale.com>
This commit is contained in:
Jordan Whited
2025-06-06 09:46:29 -07:00
committed by GitHub
parent 3f7a9f82e3
commit 66ae8737f4
14 changed files with 604 additions and 386 deletions

View File

@@ -159,9 +159,13 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
return make([]byte, len+packet.GeneveFixedHeaderLength, cap+packet.GeneveFixedHeaderLength)
}
vni1 := virtualNetworkID{}
vni1.set(1)
cases := []struct {
name string
buffs [][]byte
vni virtualNetworkID
wantLens []int
wantGSO []int
}{
@@ -173,6 +177,15 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
wantLens: []int{1},
wantGSO: []int{0},
},
{
name: "one message no coalesce vni.isSet",
buffs: [][]byte{
withGeneveSpace(1, 1),
},
vni: vni1,
wantLens: []int{1 + packet.GeneveFixedHeaderLength},
wantGSO: []int{0},
},
{
name: "two messages equal len coalesce",
buffs: [][]byte{
@@ -182,6 +195,16 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
wantLens: []int{2},
wantGSO: []int{1},
},
{
name: "two messages equal len coalesce vni.isSet",
buffs: [][]byte{
withGeneveSpace(1, 2+packet.GeneveFixedHeaderLength),
withGeneveSpace(1, 1),
},
vni: vni1,
wantLens: []int{2 + (2 * packet.GeneveFixedHeaderLength)},
wantGSO: []int{1 + packet.GeneveFixedHeaderLength},
},
{
name: "two messages unequal len coalesce",
buffs: [][]byte{
@@ -191,6 +214,16 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
wantLens: []int{3},
wantGSO: []int{2},
},
{
name: "two messages unequal len coalesce vni.isSet",
buffs: [][]byte{
withGeneveSpace(2, 3+packet.GeneveFixedHeaderLength),
withGeneveSpace(1, 1),
},
vni: vni1,
wantLens: []int{3 + (2 * packet.GeneveFixedHeaderLength)},
wantGSO: []int{2 + packet.GeneveFixedHeaderLength},
},
{
name: "three messages second unequal len coalesce",
buffs: [][]byte{
@@ -201,6 +234,17 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
wantLens: []int{3, 2},
wantGSO: []int{2, 0},
},
{
name: "three messages second unequal len coalesce vni.isSet",
buffs: [][]byte{
withGeneveSpace(2, 3+(2*packet.GeneveFixedHeaderLength)),
withGeneveSpace(1, 1),
withGeneveSpace(2, 2),
},
vni: vni1,
wantLens: []int{3 + (2 * packet.GeneveFixedHeaderLength), 2 + packet.GeneveFixedHeaderLength},
wantGSO: []int{2 + packet.GeneveFixedHeaderLength, 0},
},
{
name: "three messages limited cap coalesce",
buffs: [][]byte{
@@ -211,6 +255,17 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
wantLens: []int{4, 2},
wantGSO: []int{2, 0},
},
{
name: "three messages limited cap coalesce vni.isSet",
buffs: [][]byte{
withGeneveSpace(2, 4+packet.GeneveFixedHeaderLength),
withGeneveSpace(2, 2),
withGeneveSpace(2, 2),
},
vni: vni1,
wantLens: []int{4 + (2 * packet.GeneveFixedHeaderLength), 2 + packet.GeneveFixedHeaderLength},
wantGSO: []int{2 + packet.GeneveFixedHeaderLength, 0},
},
}
for _, tt := range cases {
@@ -224,7 +279,7 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
msgs[i].Buffers = make([][]byte, 1)
msgs[i].OOB = make([]byte, 0, 2)
}
got := c.coalesceMessages(addr, tt.buffs, msgs, packet.GeneveFixedHeaderLength)
got := c.coalesceMessages(addr, tt.vni, tt.buffs, msgs, packet.GeneveFixedHeaderLength)
if got != len(tt.wantLens) {
t.Fatalf("got len %d want: %d", got, len(tt.wantLens))
}