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:
parent
64040e6646
commit
03c5a0ccf7
2 changed files with 18 additions and 2 deletions
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue