Browse Source

go.mod,wgengine/magicsock: update wireguard-go (#16148)

Our conn.Bind implementation is updated to make Send() offset-aware for
future VXLAN/Geneve encapsulation support.

Updates tailscale/corp#27502

Signed-off-by: Jordan Whited <[email protected]>
Jordan Whited 9 months ago
parent
commit
5f35143d83

+ 1 - 1
go.mod

@@ -90,7 +90,7 @@ require (
 	github.com/tailscale/setec v0.0.0-20250205144240-8898a29c3fbb
 	github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976
 	github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6
-	github.com/tailscale/wireguard-go v0.0.0-20250304000100-91a0587fb251
+	github.com/tailscale/wireguard-go v0.0.0-20250530210235-65cd6eed7d7f
 	github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e
 	github.com/tc-hib/winres v0.2.1
 	github.com/tcnksm/go-httpstat v0.2.0

+ 2 - 2
go.sum

@@ -975,8 +975,8 @@ github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 h1:U
 github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ=
 github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6 h1:l10Gi6w9jxvinoiq15g8OToDdASBni4CyJOdHY1Hr8M=
 github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6/go.mod h1:ZXRML051h7o4OcI0d3AaILDIad/Xw0IkXaHM17dic1Y=
-github.com/tailscale/wireguard-go v0.0.0-20250304000100-91a0587fb251 h1:h/41LFTrwMxB9Xvvug0kRdQCU5TlV1+pAMQw0ZtDE3U=
-github.com/tailscale/wireguard-go v0.0.0-20250304000100-91a0587fb251/go.mod h1:BOm5fXUBFM+m9woLNBoxI9TaBXXhGNP50LX/TGIvGb4=
+github.com/tailscale/wireguard-go v0.0.0-20250530210235-65cd6eed7d7f h1:vg3PmQdq1BbB2V81iC1VBICQtfwbVGZ/4A/p7QKXTK0=
+github.com/tailscale/wireguard-go v0.0.0-20250530210235-65cd6eed7d7f/go.mod h1:BOm5fXUBFM+m9woLNBoxI9TaBXXhGNP50LX/TGIvGb4=
 github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e h1:zOGKqN5D5hHhiYUp091JqK7DPCqSARyUfduhGUY8Bek=
 github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e/go.mod h1:orPd6JZXXRyuDusYilywte7k094d7dycXXU5YnWsrwg=
 github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA=

+ 1 - 1
wgengine/magicsock/batching_conn.go

@@ -21,5 +21,5 @@ var (
 type batchingConn interface {
 	nettype.PacketConn
 	ReadBatch(msgs []ipv6.Message, flags int) (n int, err error)
-	WriteBatchTo(buffs [][]byte, addr netip.AddrPort) error
+	WriteBatchTo(buffs [][]byte, addr netip.AddrPort, offset int) error
 }

+ 5 - 4
wgengine/magicsock/batching_conn_linux.go

@@ -94,7 +94,7 @@ const (
 
 // coalesceMessages iterates msgs, coalescing them where possible while
 // maintaining datagram order. All msgs have their Addr field set to addr.
-func (c *linuxBatchingConn) coalesceMessages(addr *net.UDPAddr, buffs [][]byte, msgs []ipv6.Message) int {
+func (c *linuxBatchingConn) coalesceMessages(addr *net.UDPAddr, buffs [][]byte, msgs []ipv6.Message, offset int) int {
 	var (
 		base     = -1 // index of msg we are currently coalescing into
 		gsoSize  int  // segmentation size of msgs[base]
@@ -106,6 +106,7 @@ func (c *linuxBatchingConn) coalesceMessages(addr *net.UDPAddr, buffs [][]byte,
 		maxPayloadLen = maxIPv6PayloadLen
 	}
 	for i, buff := range buffs {
+		buff = buff[offset:]
 		if i > 0 {
 			msgLen := len(buff)
 			baseLenBefore := len(msgs[base].Buffers[0])
@@ -162,7 +163,7 @@ func (c *linuxBatchingConn) putSendBatch(batch *sendBatch) {
 	c.sendBatchPool.Put(batch)
 }
 
-func (c *linuxBatchingConn) WriteBatchTo(buffs [][]byte, addr netip.AddrPort) error {
+func (c *linuxBatchingConn) WriteBatchTo(buffs [][]byte, addr netip.AddrPort, offset int) error {
 	batch := c.getSendBatch()
 	defer c.putSendBatch(batch)
 	if addr.Addr().Is6() {
@@ -181,10 +182,10 @@ func (c *linuxBatchingConn) WriteBatchTo(buffs [][]byte, addr netip.AddrPort) er
 	)
 retry:
 	if c.txOffload.Load() {
-		n = c.coalesceMessages(batch.ua, buffs, batch.msgs)
+		n = c.coalesceMessages(batch.ua, buffs, batch.msgs, offset)
 	} else {
 		for i := range buffs {
-			batch.msgs[i].Buffers[0] = buffs[i]
+			batch.msgs[i].Buffers[0] = buffs[i][offset:]
 			batch.msgs[i].Addr = batch.ua
 			batch.msgs[i].OOB = batch.msgs[i].OOB[:0]
 		}

+ 17 - 12
wgengine/magicsock/batching_conn_linux_test.go

@@ -9,6 +9,7 @@ import (
 	"testing"
 
 	"golang.org/x/net/ipv6"
+	"tailscale.com/net/packet"
 )
 
 func setGSOSize(control *[]byte, gsoSize uint16) {
@@ -154,6 +155,10 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
 		getGSOSizeFromControl: getGSOSize,
 	}
 
+	withGeneveSpace := func(len, cap int) []byte {
+		return make([]byte, len+packet.GeneveFixedHeaderLength, cap+packet.GeneveFixedHeaderLength)
+	}
+
 	cases := []struct {
 		name     string
 		buffs    [][]byte
@@ -163,7 +168,7 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
 		{
 			name: "one message no coalesce",
 			buffs: [][]byte{
-				make([]byte, 1, 1),
+				withGeneveSpace(1, 1),
 			},
 			wantLens: []int{1},
 			wantGSO:  []int{0},
@@ -171,8 +176,8 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
 		{
 			name: "two messages equal len coalesce",
 			buffs: [][]byte{
-				make([]byte, 1, 2),
-				make([]byte, 1, 1),
+				withGeneveSpace(1, 2),
+				withGeneveSpace(1, 1),
 			},
 			wantLens: []int{2},
 			wantGSO:  []int{1},
@@ -180,8 +185,8 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
 		{
 			name: "two messages unequal len coalesce",
 			buffs: [][]byte{
-				make([]byte, 2, 3),
-				make([]byte, 1, 1),
+				withGeneveSpace(2, 3),
+				withGeneveSpace(1, 1),
 			},
 			wantLens: []int{3},
 			wantGSO:  []int{2},
@@ -189,9 +194,9 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
 		{
 			name: "three messages second unequal len coalesce",
 			buffs: [][]byte{
-				make([]byte, 2, 3),
-				make([]byte, 1, 1),
-				make([]byte, 2, 2),
+				withGeneveSpace(2, 3),
+				withGeneveSpace(1, 1),
+				withGeneveSpace(2, 2),
 			},
 			wantLens: []int{3, 2},
 			wantGSO:  []int{2, 0},
@@ -199,9 +204,9 @@ func Test_linuxBatchingConn_coalesceMessages(t *testing.T) {
 		{
 			name: "three messages limited cap coalesce",
 			buffs: [][]byte{
-				make([]byte, 2, 4),
-				make([]byte, 2, 2),
-				make([]byte, 2, 2),
+				withGeneveSpace(2, 4),
+				withGeneveSpace(2, 2),
+				withGeneveSpace(2, 2),
 			},
 			wantLens: []int{4, 2},
 			wantGSO:  []int{2, 0},
@@ -219,7 +224,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)
+			got := c.coalesceMessages(addr, tt.buffs, msgs, packet.GeneveFixedHeaderLength)
 			if got != len(tt.wantLens) {
 				t.Fatalf("got len %d want: %d", got, len(tt.wantLens))
 			}

+ 4 - 3
wgengine/magicsock/endpoint.go

@@ -927,7 +927,7 @@ var (
 	errPingTooBig  = errors.New("ping size too big")
 )
 
-func (de *endpoint) send(buffs [][]byte) error {
+func (de *endpoint) send(buffs [][]byte, offset int) error {
 	de.mu.Lock()
 	if de.expired {
 		de.mu.Unlock()
@@ -961,7 +961,7 @@ func (de *endpoint) send(buffs [][]byte) error {
 	}
 	var err error
 	if udpAddr.IsValid() {
-		_, err = de.c.sendUDPBatch(udpAddr, buffs)
+		_, err = de.c.sendUDPBatch(udpAddr, buffs, offset)
 
 		// If the error is known to indicate that the endpoint is no longer
 		// usable, clear the endpoint statistics so that the next send will
@@ -972,7 +972,7 @@ func (de *endpoint) send(buffs [][]byte) error {
 
 		var txBytes int
 		for _, b := range buffs {
-			txBytes += len(b)
+			txBytes += len(b[offset:])
 		}
 
 		switch {
@@ -993,6 +993,7 @@ func (de *endpoint) send(buffs [][]byte) error {
 		allOk := true
 		var txBytes int
 		for _, buff := range buffs {
+			buff = buff[offset:]
 			const isDisco = false
 			ok, _ := de.c.sendAddr(derpAddr, de.publicKey, buff, isDisco)
 			txBytes += len(buff)

+ 6 - 6
wgengine/magicsock/magicsock.go

@@ -1264,8 +1264,8 @@ func (c *Conn) networkDown() bool { return !c.networkUp.Load() }
 
 // Send implements conn.Bind.
 //
-// See https://pkg.go.dev/golang.zx2c4.com/wireguard/conn#Bind.Send
-func (c *Conn) Send(buffs [][]byte, ep conn.Endpoint) (err error) {
+// See https://pkg.go.dev/github.com/tailscale/wireguard-go/conn#Bind.Send
+func (c *Conn) Send(buffs [][]byte, ep conn.Endpoint, offset int) (err error) {
 	n := int64(len(buffs))
 	defer func() {
 		if err != nil {
@@ -1278,7 +1278,7 @@ func (c *Conn) Send(buffs [][]byte, ep conn.Endpoint) (err error) {
 		return errNetworkDown
 	}
 	if ep, ok := ep.(*endpoint); ok {
-		return ep.send(buffs)
+		return ep.send(buffs, offset)
 	}
 	// If it's not of type *endpoint, it's probably *lazyEndpoint, which means
 	// we don't actually know who the peer is and we're waiting for wireguard-go
@@ -1294,7 +1294,7 @@ var errNoUDP = errors.New("no UDP available on platform")
 
 var errUnsupportedConnType = errors.New("unsupported connection type")
 
-func (c *Conn) sendUDPBatch(addr netip.AddrPort, buffs [][]byte) (sent bool, err error) {
+func (c *Conn) sendUDPBatch(addr netip.AddrPort, buffs [][]byte, offset int) (sent bool, err error) {
 	isIPv6 := false
 	switch {
 	case addr.Addr().Is4():
@@ -1304,9 +1304,9 @@ func (c *Conn) sendUDPBatch(addr netip.AddrPort, buffs [][]byte) (sent bool, err
 		panic("bogus sendUDPBatch addr type")
 	}
 	if isIPv6 {
-		err = c.pconn6.WriteBatchTo(buffs, addr)
+		err = c.pconn6.WriteBatchTo(buffs, addr, offset)
 	} else {
-		err = c.pconn4.WriteBatchTo(buffs, addr)
+		err = c.pconn4.WriteBatchTo(buffs, addr, offset)
 	}
 	if err != nil {
 		var errGSO neterror.ErrUDPGSODisabled

+ 1 - 1
wgengine/magicsock/magicsock_test.go

@@ -3147,7 +3147,7 @@ func TestNetworkDownSendErrors(t *testing.T) {
 	defer conn.Close()
 
 	conn.SetNetworkUp(false)
-	if err := conn.Send([][]byte{{00}}, &lazyEndpoint{}); err == nil {
+	if err := conn.Send([][]byte{{00}}, &lazyEndpoint{}, 0); err == nil {
 		t.Error("expected error, got nil")
 	}
 	resp := httptest.NewRecorder()

+ 3 - 2
wgengine/magicsock/rebinding_conn.go

@@ -71,12 +71,13 @@ func (c *RebindingUDPConn) ReadFromUDPAddrPort(b []byte) (int, netip.AddrPort, e
 }
 
 // WriteBatchTo writes buffs to addr.
-func (c *RebindingUDPConn) WriteBatchTo(buffs [][]byte, addr netip.AddrPort) error {
+func (c *RebindingUDPConn) WriteBatchTo(buffs [][]byte, addr netip.AddrPort, offset int) error {
 	for {
 		pconn := *c.pconnAtomic.Load()
 		b, ok := pconn.(batchingConn)
 		if !ok {
 			for _, buf := range buffs {
+				buf = buf[offset:]
 				_, err := c.writeToUDPAddrPortWithInitPconn(pconn, buf, addr)
 				if err != nil {
 					return err
@@ -84,7 +85,7 @@ func (c *RebindingUDPConn) WriteBatchTo(buffs [][]byte, addr netip.AddrPort) err
 			}
 			return nil
 		}
-		err := b.WriteBatchTo(buffs, addr)
+		err := b.WriteBatchTo(buffs, addr, offset)
 		if err != nil {
 			if pconn != c.currentConn() {
 				continue

+ 3 - 3
wgengine/wgcfg/device_test.go

@@ -242,9 +242,9 @@ type noopBind struct{}
 func (noopBind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint16, err error) {
 	return nil, 1, nil
 }
-func (noopBind) Close() error                            { return nil }
-func (noopBind) SetMark(mark uint32) error               { return nil }
-func (noopBind) Send(b [][]byte, ep conn.Endpoint) error { return nil }
+func (noopBind) Close() error                                        { return nil }
+func (noopBind) SetMark(mark uint32) error                           { return nil }
+func (noopBind) Send(b [][]byte, ep conn.Endpoint, offset int) error { return nil }
 func (noopBind) ParseEndpoint(s string) (conn.Endpoint, error) {
 	return dummyEndpoint(s), nil
 }