conn,device: provide 8 free bytes at packet head to conn.Bind.Send()

This enables a conn.Bind to bring its own encapsulating transport, e.g.
VXLAN/Geneve.

Updates tailscale/corp#27502

Signed-off-by: Jordan Whited <jordan@tailscale.com>
This commit is contained in:
Jordan Whited 2025-05-30 13:14:43 -07:00 committed by Jordan Whited
parent 022546570f
commit 65cd6eed7d
10 changed files with 58 additions and 39 deletions

View file

@ -341,7 +341,7 @@ func (e ErrUDPGSODisabled) Unwrap() error {
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()
blackhole := s.blackhole4
conn := s.ipv4
@ -384,7 +384,7 @@ func (s *StdNetBind) Send(bufs [][]byte, endpoint Endpoint) error {
)
retry:
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])
if err != nil && offload && errShouldDisableUDPGSO(err) {
offload = false
@ -401,7 +401,7 @@ retry:
} else {
for i := range bufs {
(*msgs)[i].Addr = ua
(*msgs)[i].Buffers[0] = bufs[i]
(*msgs)[i].Buffers[0] = bufs[i][offset:]
setSrcControl(&(*msgs)[i].OOB, endpoint.(*StdNetEndpoint))
}
err = s.send(conn, br, (*msgs)[:len(bufs)])
@ -450,7 +450,7 @@ const (
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 (
base = -1 // index of msg we are currently coalescing into
gsoSize int // segmentation size of msgs[base]
@ -462,6 +462,7 @@ func coalesceMessages(addr *net.UDPAddr, ep *StdNetEndpoint, bufs [][]byte, msgs
maxPayloadLen = maxIPv6PayloadLen
}
for i, buf := range bufs {
buf = buf[offset:]
if i > 0 {
msgLen := len(buf)
baseLenBefore := len(msgs[base].Buffers[0])

View file

@ -98,7 +98,7 @@ func Test_coalesceMessages(t *testing.T) {
msgs[i].Buffers = make([][]byte, 1)
msgs[i].OOB = make([]byte, 0, 2)
}
got := coalesceMessages(addr, &StdNetEndpoint{AddrPort: addr.AddrPort()}, tt.buffs, msgs, mockSetGSOSize)
got := coalesceMessages(addr, &StdNetEndpoint{AddrPort: addr.AddrPort()}, tt.buffs, 0, msgs, mockSetGSOSize)
if got != len(tt.wantLens) {
t.Fatalf("got len %d want: %d", got, len(tt.wantLens))
}

View file

@ -486,7 +486,7 @@ func (bind *afWinRingBind) Send(buf []byte, nend *WinRingEndpoint, isOpen *atomi
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)
if !ok {
return ErrWrongEndpointType
@ -494,6 +494,7 @@ func (bind *WinRingBind) Send(bufs [][]byte, endpoint Endpoint) error {
bind.mu.RLock()
defer bind.mu.RUnlock()
for _, buf := range bufs {
buf = buf[offset:]
switch nend.family {
case windows.AF_INET:
if bind.v4.blackhole {

View file

@ -107,8 +107,9 @@ func (c *ChannelBind) makeReceiveFunc(ch chan []byte) conn.ReceiveFunc {
}
}
func (c *ChannelBind) Send(bufs [][]byte, ep conn.Endpoint) error {
func (c *ChannelBind) Send(bufs [][]byte, ep conn.Endpoint, offset int) error {
for _, b := range bufs {
b = b[offset:]
select {
case <-c.closeSignal:
return net.ErrClosed

View file

@ -45,9 +45,11 @@ type Bind interface {
// This mark is passed to the kernel as the socket option SO_MARK.
SetMark(mark uint32) error
// Send writes one or more packets in bufs to address ep. The length of
// bufs must not exceed BatchSize().
Send(bufs [][]byte, ep Endpoint) error
// Send writes one or more packets in bufs to address ep. A nonzero offset
// can be used to instruct the Bind on where packet data begins in each
// 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(s string) (Endpoint, error)