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 <jordan@tailscale.com>
This commit is contained in:
Jordan Whited 2025-06-02 13:22:28 -07:00 committed by GitHub
parent c9a5d638e9
commit 5f35143d83
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 43 additions and 35 deletions

2
go.mod
View File

@ -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

4
go.sum
View File

@ -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=

View File

@ -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
}

View File

@ -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]
}

View File

@ -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))
}

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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
}