Apply Tailscale bind send headroom

This commit is contained in:
世界 2026-05-17 20:11:01 +08:00
parent 45cd03b8a1
commit 749db0015c
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
7 changed files with 50 additions and 32 deletions

View file

@ -334,7 +334,7 @@ func (e ErrUDPGSODisabled) Unwrap() error {
return e.RetryErr return e.RetryErr
} }
func (s *StdNetBind) Send(bufs [][]byte, endpoint Endpoint) error { func (s *StdNetBind) Send(bufs [][]byte, endpoint Endpoint, offset int) error {
s.mu.Lock() s.mu.Lock()
blackhole := s.blackhole4 blackhole := s.blackhole4
conn := s.ipv4 conn := s.ipv4
@ -377,7 +377,7 @@ func (s *StdNetBind) Send(bufs [][]byte, endpoint Endpoint) error {
) )
retry: retry:
if offload { if offload {
n := coalesceMessages(ua, endpoint.(*StdNetEndpoint), bufs, *msgs, setGSOSize) n := coalesceMessages(ua, endpoint.(*StdNetEndpoint), bufs, offset, *msgs, setGSOSize)
err = s.send(conn, br, (*msgs)[:n]) err = s.send(conn, br, (*msgs)[:n])
if err != nil && offload && errShouldDisableUDPGSO(err) { if err != nil && offload && errShouldDisableUDPGSO(err) {
offload = false offload = false
@ -394,7 +394,7 @@ retry:
} else { } else {
for i := range bufs { for i := range bufs {
(*msgs)[i].Addr = ua (*msgs)[i].Addr = ua
(*msgs)[i].Buffers[0] = bufs[i] (*msgs)[i].Buffers[0] = bufs[i][offset:]
setSrcControl(&(*msgs)[i].OOB, endpoint.(*StdNetEndpoint)) setSrcControl(&(*msgs)[i].OOB, endpoint.(*StdNetEndpoint))
} }
err = s.send(conn, br, (*msgs)[:len(bufs)]) err = s.send(conn, br, (*msgs)[:len(bufs)])
@ -443,7 +443,7 @@ const (
type setGSOFunc func(control *[]byte, gsoSize uint16) type setGSOFunc func(control *[]byte, gsoSize uint16)
func coalesceMessages(addr *net.UDPAddr, ep *StdNetEndpoint, bufs [][]byte, msgs []ipv6.Message, setGSO setGSOFunc) int { func coalesceMessages(addr *net.UDPAddr, ep *StdNetEndpoint, bufs [][]byte, offset int, msgs []ipv6.Message, setGSO setGSOFunc) int {
var ( var (
base = -1 // index of msg we are currently coalescing into base = -1 // index of msg we are currently coalescing into
gsoSize int // segmentation size of msgs[base] gsoSize int // segmentation size of msgs[base]
@ -455,6 +455,7 @@ func coalesceMessages(addr *net.UDPAddr, ep *StdNetEndpoint, bufs [][]byte, msgs
maxPayloadLen = maxIPv6PayloadLen maxPayloadLen = maxIPv6PayloadLen
} }
for i, buf := range bufs { for i, buf := range bufs {
buf = buf[offset:]
if i > 0 { if i > 0 {
msgLen := len(buf) msgLen := len(buf)
baseLenBefore := len(msgs[base].Buffers[0]) baseLenBefore := len(msgs[base].Buffers[0])

View file

@ -485,7 +485,7 @@ func (bind *afWinRingBind) Send(buf []byte, nend *WinRingEndpoint, isOpen *atomi
return winrio.SendEx(bind.rq, dataBuffer, 1, nil, addressBuffer, nil, nil, 0, 0) return winrio.SendEx(bind.rq, dataBuffer, 1, nil, addressBuffer, nil, nil, 0, 0)
} }
func (bind *WinRingBind) Send(bufs [][]byte, endpoint Endpoint) error { func (bind *WinRingBind) Send(bufs [][]byte, endpoint Endpoint, offset int) error {
nend, ok := endpoint.(*WinRingEndpoint) nend, ok := endpoint.(*WinRingEndpoint)
if !ok { if !ok {
return ErrWrongEndpointType return ErrWrongEndpointType
@ -493,6 +493,7 @@ func (bind *WinRingBind) Send(bufs [][]byte, endpoint Endpoint) error {
bind.mu.RLock() bind.mu.RLock()
defer bind.mu.RUnlock() defer bind.mu.RUnlock()
for _, buf := range bufs { for _, buf := range bufs {
buf = buf[offset:]
switch nend.family { switch nend.family {
case windows.AF_INET: case windows.AF_INET:
if bind.v4.blackhole { if bind.v4.blackhole {

View file

@ -45,9 +45,11 @@ type Bind interface {
// This mark is passed to the kernel as the socket option SO_MARK. // This mark is passed to the kernel as the socket option SO_MARK.
SetMark(mark uint32) error SetMark(mark uint32) error
// Send writes one or more packets in bufs to address ep. The length of // Send writes one or more packets in bufs to address ep. A nonzero offset
// bufs must not exceed BatchSize(). // can be used to instruct the Bind on where packet data begins in each
Send(bufs [][]byte, ep Endpoint) error // element of the bufs slice. Space preceding offset is free to use for
// additional encapsulation. The length of bufs must not exceed BatchSize().
Send(bufs [][]byte, ep Endpoint, offset int) error
// ParseEndpoint creates a new endpoint from a string. // ParseEndpoint creates a new endpoint from a string.
ParseEndpoint(s string) (Endpoint, error) ParseEndpoint(s string) (Endpoint, error)

View file

@ -27,9 +27,9 @@ const (
) )
const ( const (
MinMessageSize = MessageKeepaliveSize // minimum size of transport message (keepalive) MinMessageSize = MessageKeepaliveSize // minimum size of transport message (keepalive)
MaxMessageSize = MaxSegmentSize // maximum size of transport message MaxMessageSize = MaxSegmentSize // maximum size of transport message
MaxContentSize = MaxSegmentSize - MessageTransportSize // maximum size of transport message content MaxContentSize = MaxSegmentSize - MessageTransportSize - MessageEncapsulatingTransportSize // maximum size of transport message content
) )
/* Implementation constants */ /* Implementation constants */

View file

@ -60,13 +60,14 @@ const (
) )
const ( const (
MessageInitiationSize = 148 // size of handshake initiation message MessageInitiationSize = 148 // size of handshake initiation message
MessageResponseSize = 92 // size of response message MessageResponseSize = 92 // size of response message
MessageCookieReplySize = 64 // size of cookie reply message MessageCookieReplySize = 64 // size of cookie reply message
MessageTransportHeaderSize = 16 // size of data preceding content in transport message MessageTransportHeaderSize = 16 // size of data preceding content in transport message
MessageTransportSize = MessageTransportHeaderSize + poly1305.TagSize // size of empty transport MessageEncapsulatingTransportSize = 8 // size of optional, free (for use by conn.Bind.Send()) space preceding the transport header
MessageKeepaliveSize = MessageTransportSize // size of keepalive MessageTransportSize = MessageTransportHeaderSize + poly1305.TagSize // size of empty transport
MessageHandshakeSize = MessageInitiationSize // size of largest handshake related message MessageKeepaliveSize = MessageTransportSize // size of keepalive
MessageHandshakeSize = MessageInitiationSize // size of largest handshake related message
) )
const ( const (

View file

@ -113,6 +113,9 @@ func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
return peer, nil return peer, nil
} }
// SendBuffers sends buffers to peer. WireGuard packet data in each element of
// buffers must be preceded by MessageEncapsulatingTransportSize number of
// bytes.
func (peer *Peer) SendBuffers(buffers [][]byte) error { func (peer *Peer) SendBuffers(buffers [][]byte) error {
peer.device.net.RLock() peer.device.net.RLock()
defer peer.device.net.RUnlock() defer peer.device.net.RUnlock()
@ -133,7 +136,7 @@ func (peer *Peer) SendBuffers(buffers [][]byte) error {
} }
peer.endpoint.Unlock() peer.endpoint.Unlock()
err := peer.device.net.bind.Send(buffers, endpoint) err := peer.device.net.bind.Send(buffers, endpoint, MessageEncapsulatingTransportSize)
if err == nil { if err == nil {
var totalLen uint64 var totalLen uint64
for _, b := range buffers { for _, b := range buffers {

View file

@ -45,11 +45,15 @@ import (
*/ */
type QueueOutboundElement struct { type QueueOutboundElement struct {
buffer *[MaxMessageSize]byte // slice holding the packet data buffer *[MaxMessageSize]byte // slice holding the packet data
packet []byte // slice of "buffer" (always!) // packet is always a slice of "buffer". The starting offset in buffer
nonce uint64 // nonce for encryption // is either:
keypair *Keypair // keypair for encryption // a) MessageEncapsulatingTransportSize+MessageTransportHeaderSize (plaintext)
peer *Peer // related peer // b) 0 (post-encryption)
packet []byte
nonce uint64 // nonce for encryption
keypair *Keypair // keypair for encryption
peer *Peer // related peer
} }
type QueueOutboundElementsContainer struct { type QueueOutboundElementsContainer struct {
@ -123,14 +127,15 @@ func (peer *Peer) SendHandshakeInitiation(isRetry bool) error {
return err return err
} }
packet := make([]byte, MessageInitiationSize) buf := make([]byte, MessageEncapsulatingTransportSize+MessageInitiationSize)
packet := buf[MessageEncapsulatingTransportSize:]
_ = msg.marshal(packet) _ = msg.marshal(packet)
peer.cookieGenerator.AddMacs(packet) peer.cookieGenerator.AddMacs(packet)
peer.timersAnyAuthenticatedPacketTraversal() peer.timersAnyAuthenticatedPacketTraversal()
peer.timersAnyAuthenticatedPacketSent() peer.timersAnyAuthenticatedPacketSent()
err = peer.SendBuffers([][]byte{packet}) err = peer.SendBuffers([][]byte{buf})
if err != nil { if err != nil {
peer.device.log.Errorf("%v - Failed to send handshake initiation: %v", peer, err) peer.device.log.Errorf("%v - Failed to send handshake initiation: %v", peer, err)
} }
@ -152,7 +157,8 @@ func (peer *Peer) SendHandshakeResponse() error {
return err return err
} }
packet := make([]byte, MessageResponseSize) buf := make([]byte, MessageEncapsulatingTransportSize+MessageResponseSize)
packet := buf[MessageEncapsulatingTransportSize:]
_ = response.marshal(packet) _ = response.marshal(packet)
peer.cookieGenerator.AddMacs(packet) peer.cookieGenerator.AddMacs(packet)
@ -167,7 +173,7 @@ func (peer *Peer) SendHandshakeResponse() error {
peer.timersAnyAuthenticatedPacketSent() peer.timersAnyAuthenticatedPacketSent()
// TODO: allocation could be avoided // TODO: allocation could be avoided
err = peer.SendBuffers([][]byte{packet}) err = peer.SendBuffers([][]byte{buf})
if err != nil { if err != nil {
peer.device.log.Errorf("%v - Failed to send handshake response: %v", peer, err) peer.device.log.Errorf("%v - Failed to send handshake response: %v", peer, err)
} }
@ -184,10 +190,11 @@ func (device *Device) SendHandshakeCookie(initiatingElem *QueueHandshakeElement)
return err return err
} }
packet := make([]byte, MessageCookieReplySize) buf := make([]byte, MessageEncapsulatingTransportSize+MessageCookieReplySize)
packet := buf[MessageEncapsulatingTransportSize:]
_ = reply.marshal(packet) _ = reply.marshal(packet)
// TODO: allocation could be avoided // TODO: allocation could be avoided
device.net.bind.Send([][]byte{packet}, initiatingElem.endpoint) device.net.bind.Send([][]byte{buf}, initiatingElem.endpoint, MessageEncapsulatingTransportSize)
return nil return nil
} }
@ -220,7 +227,7 @@ func (device *Device) RoutineReadFromTUN() {
elemsByPeer = make(map[*Peer]*QueueOutboundElementsContainer, batchSize) elemsByPeer = make(map[*Peer]*QueueOutboundElementsContainer, batchSize)
count = 0 count = 0
sizes = make([]int, batchSize) sizes = make([]int, batchSize)
offset = MessageTransportHeaderSize offset = MessageEncapsulatingTransportSize + MessageTransportHeaderSize
) )
for i := range elems { for i := range elems {
@ -446,7 +453,7 @@ func (device *Device) RoutineEncryption(id int) {
for elemsContainer := range device.queue.encryption.c { for elemsContainer := range device.queue.encryption.c {
for _, elem := range elemsContainer.elems { for _, elem := range elemsContainer.elems {
// populate header fields // populate header fields
header := elem.buffer[:MessageTransportHeaderSize] header := elem.buffer[MessageEncapsulatingTransportSize : MessageEncapsulatingTransportSize+MessageTransportHeaderSize]
fieldType := header[0:4] fieldType := header[0:4]
fieldReceiver := header[4:8] fieldReceiver := header[4:8]
@ -469,6 +476,9 @@ func (device *Device) RoutineEncryption(id int) {
elem.packet, elem.packet,
nil, nil,
) )
// re-slice packet to include encapsulating transport space
elem.packet = elem.buffer[:MessageEncapsulatingTransportSize+len(elem.packet)]
} }
elemsContainer.Unlock() elemsContainer.Unlock()
} }