tun: implement API for disabling UDP GRO on Linux

Certain device drivers (e.g. vxlan, geneve) do not properly handle
coalesced UDP packets later in the stack, resulting in packet loss.

Signed-off-by: Jordan Whited <jordan@tailscale.com>
This commit is contained in:
Jordan Whited 2024-04-29 09:15:56 -07:00 committed by Jordan Whited
parent 64040e6646
commit 03c5a0ccf7
2 changed files with 18 additions and 2 deletions

View file

@ -51,3 +51,11 @@ type Device interface {
// lifetime of a Device. // lifetime of a Device.
BatchSize() int BatchSize() int
} }
type LinuxDevice interface {
Device
// DisableUDPGRO disables UDP GRO if it is enabled. Certain device drivers
// (e.g. vxlan, geneve) do not properly handle coalesced UDP packets later
// in the stack, resulting in packet loss.
DisableUDPGRO()
}

View file

@ -49,10 +49,11 @@ type NativeTun struct {
readOpMu sync.Mutex // readOpMu guards readBuff readOpMu sync.Mutex // readOpMu guards readBuff
readBuff [virtioNetHdrLen + 65535]byte // if vnetHdr every read() is prefixed by virtioNetHdr readBuff [virtioNetHdrLen + 65535]byte // if vnetHdr every read() is prefixed by virtioNetHdr
writeOpMu sync.Mutex // writeOpMu guards toWrite, tcpGROTable writeOpMu sync.Mutex // writeOpMu guards the following fields
toWrite []int toWrite []int
tcpGROTable *tcpGROTable tcpGROTable *tcpGROTable
udpGROTable *udpGROTable udpGROTable *udpGROTable
udpGRO bool
} }
func (tun *NativeTun) File() *os.File { func (tun *NativeTun) File() *os.File {
@ -345,7 +346,7 @@ func (tun *NativeTun) Write(bufs [][]byte, offset int) (int, error) {
) )
tun.toWrite = tun.toWrite[:0] tun.toWrite = tun.toWrite[:0]
if tun.vnetHdr { if tun.vnetHdr {
err := handleGRO(bufs, offset, tun.tcpGROTable, tun.udpGROTable, tun.udpGSO, &tun.toWrite) err := handleGRO(bufs, offset, tun.tcpGROTable, tun.udpGROTable, tun.udpGRO, &tun.toWrite)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -502,6 +503,12 @@ func (tun *NativeTun) BatchSize() int {
return tun.batchSize return tun.batchSize
} }
func (tun *NativeTun) DisableUDPGRO() {
tun.writeOpMu.Lock()
tun.udpGRO = false
tun.writeOpMu.Unlock()
}
const ( const (
// TODO: support TSO with ECN bits // TODO: support TSO with ECN bits
tunTCPOffloads = unix.TUN_F_CSUM | unix.TUN_F_TSO4 | unix.TUN_F_TSO6 tunTCPOffloads = unix.TUN_F_CSUM | unix.TUN_F_TSO4 | unix.TUN_F_TSO6
@ -538,6 +545,7 @@ func (tun *NativeTun) initFromFlags(name string) error {
// tunUDPOffloads were added in Linux v6.2. We do not return an // tunUDPOffloads were added in Linux v6.2. We do not return an
// error if they are unsupported at runtime. // error if they are unsupported at runtime.
tun.udpGSO = unix.IoctlSetInt(int(fd), unix.TUNSETOFFLOAD, tunTCPOffloads|tunUDPOffloads) == nil tun.udpGSO = unix.IoctlSetInt(int(fd), unix.TUNSETOFFLOAD, tunTCPOffloads|tunUDPOffloads) == nil
tun.udpGRO = tun.udpGSO
} else { } else {
tun.batchSize = 1 tun.batchSize = 1
} }