wintun: Switch to dynamic packet sizes

Signed-off-by: Simon Rozman <simon@rozman.si>
This commit is contained in:
Simon Rozman 2019-02-19 18:49:24 +01:00
parent 42c6d0e261
commit 4863089120

View file

@ -7,17 +7,18 @@ package tun
import ( import (
"errors" "errors"
"fmt"
"os" "os"
"unsafe" "unsafe"
"golang.zx2c4.com/wireguard/tun/wintun"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/tun/wintun"
) )
const ( const (
packetSizeMax = 1600 packetSizeMax uint32 = 0xeffc // Maximum packet size: 4 + packetSizeMax == 0xf000
packetExchangeMax = 256 // Number of packets that can be exchanged at a time packetExchangeMax uint32 = 256 // Number of packets that may be written at a time
packetExchangeAlignment uint32 = 16 // Number of bytes packets are aligned to in exchange buffers
packetExchangeSize uint32 = 0x100000 // Exchange buffer size (defaults to 1MiB)
) )
const ( const (
@ -27,28 +28,24 @@ const (
signalMax signalMax
) )
type tunPacket struct {
size uint32
data [packetSizeMax]byte
}
type tunRWQueue struct {
numPackets uint32
packets [packetExchangeMax]tunPacket
left bool
}
type nativeTun struct { type nativeTun struct {
wt *wintun.Wintun wt *wintun.Wintun
tunName string tunName string
signalName *uint16 signalName *uint16
tunFile *os.File tunFile *os.File
wrBuff tunRWQueue wrBuff [packetExchangeSize]byte
rdBuff tunRWQueue rdBuff [packetExchangeSize]byte
signals [signalMax]windows.Handle signals [signalMax]windows.Handle
rdNextPacket uint32 wrOffset uint32
events chan TUNEvent wrPacketNum uint32
errors chan error rdOffset uint32
rdAvailabe uint32
events chan TUNEvent
errors chan error
}
func packetAlign(size uint32) uint32 {
return (size + (packetExchangeAlignment - 1)) &^ (packetExchangeAlignment - 1)
} }
func CreateTUN(ifname string) (TUNDevice, error) { func CreateTUN(ifname string) (TUNDevice, error) {
@ -206,19 +203,20 @@ func (tun *nativeTun) Read(buff []byte, offset int) (int, error) {
} }
for { for {
if tun.rdNextPacket < tun.rdBuff.numPackets { if tun.rdOffset+4 <= tun.rdAvailabe {
// Get packet from the queue. // Get packet from the queue.
tunPacket := &tun.rdBuff.packets[tun.rdNextPacket] size := *(*uint32)(unsafe.Pointer(&tun.rdBuff[tun.rdOffset]))
tun.rdNextPacket++ pSize := packetAlign(4 + size)
if packetSizeMax < size || tun.rdAvailabe < tun.rdOffset+pSize {
if packetSizeMax < tunPacket.size {
// Invalid packet size. // Invalid packet size.
tun.rdAvailabe = 0
continue continue
} }
// Copy data. // Copy data.
copy(buff[offset:], tunPacket.data[:tunPacket.size]) copy(buff[offset:], (*(*[packetSizeMax]byte)(unsafe.Pointer(&tun.rdBuff[tun.rdOffset+4])))[:size])
return int(tunPacket.size), nil tun.rdOffset += pSize
return int(size), nil
} }
if tun.signals[signalDataAvail] == 0 { if tun.signals[signalDataAvail] == 0 {
@ -251,16 +249,16 @@ func (tun *nativeTun) Read(buff []byte, offset int) (int, error) {
} }
// Fill queue. // Fill queue.
const bufSize = int(unsafe.Sizeof(tun.rdBuff)) n, err := tun.tunFile.Read(tun.rdBuff[:])
n, err := tun.tunFile.Read((*[bufSize]byte)(unsafe.Pointer(&tun.rdBuff))[:]) if err != nil {
tun.rdNextPacket = 0
if n != bufSize || err != nil {
// TUN interface stopped, returned incomplete data, etc. // TUN interface stopped, returned incomplete data, etc.
// Retry. // Retry.
tun.rdBuff.numPackets = 0 tun.rdAvailabe = 0
tun.closeTUN() tun.closeTUN()
continue continue
} }
tun.rdOffset = 0
tun.rdAvailabe = uint32(n)
} }
} }
@ -268,42 +266,40 @@ func (tun *nativeTun) Read(buff []byte, offset int) (int, error) {
func (tun *nativeTun) flush() error { func (tun *nativeTun) flush() error {
// Flush write buffer. // Flush write buffer.
const bufSize = int(unsafe.Sizeof(tun.wrBuff)) _, err := tun.tunFile.Write(tun.wrBuff[:tun.wrOffset])
n, err := tun.tunFile.Write((*[bufSize]byte)(unsafe.Pointer(&tun.wrBuff))[:]) tun.wrPacketNum = 0
tun.wrBuff.numPackets = 0 tun.wrOffset = 0
if err != nil { if err != nil {
return err return err
} }
if n != bufSize {
return fmt.Errorf("%d byte(s) written, %d byte(s) expected", n, bufSize)
}
return nil return nil
} }
func (tun *nativeTun) putTunPacket(buff []byte) error { func (tun *nativeTun) putTunPacket(buff []byte) error {
size := len(buff) size := uint32(len(buff))
if size == 0 { if size == 0 {
return errors.New("Empty packet") return errors.New("Empty packet")
} }
if size > packetSizeMax { if size > packetSizeMax {
return errors.New("Packet too big") return errors.New("Packet too big")
} }
pSize := packetAlign(4 + size)
if tun.wrBuff.numPackets >= packetExchangeMax { if tun.wrPacketNum >= packetExchangeMax || tun.wrOffset+pSize >= packetExchangeSize {
// Queue is full -> flush first. // Exchange buffer is full -> flush first.
err := tun.flush() err := tun.flush()
if err != nil { if err != nil {
return err return err
} }
} }
// Push packet to the buffer. // Write packet to the exchange buffer.
tunPacket := &tun.wrBuff.packets[tun.wrBuff.numPackets] *(*uint32)(unsafe.Pointer(&tun.wrBuff[tun.wrOffset])) = size
tunPacket.size = uint32(size) copy((*(*[packetSizeMax]byte)(unsafe.Pointer(&tun.wrBuff[tun.wrOffset+4])))[:size], buff)
copy(tunPacket.data[:size], buff)
tun.wrBuff.numPackets++ tun.wrPacketNum++
tun.wrOffset += pSize
return nil return nil
} }