diff --git a/tun_linux.go b/tun_linux.go index 06c4f92..5d7b9c6 100644 --- a/tun_linux.go +++ b/tun_linux.go @@ -39,6 +39,7 @@ type NativeTun struct { writeAccess sync.Mutex vnetHdr bool writeBuffer []byte + vnetHdrWriteBuf []byte gsoToWrite []int tcpGROTable *tcpGROTable udpGroAccess sync.Mutex diff --git a/tun_linux_gvisor.go b/tun_linux_gvisor.go index cb0561b..8adf4c5 100644 --- a/tun_linux_gvisor.go +++ b/tun_linux_gvisor.go @@ -3,6 +3,8 @@ package tun import ( + "fmt" + "github.com/sagernet/gvisor/pkg/rawfile" "github.com/sagernet/gvisor/pkg/tcpip/link/fdbased" "github.com/sagernet/gvisor/pkg/tcpip/stack" @@ -18,6 +20,37 @@ var _ GVisorTun = (*NativeTun)(nil) func (t *NativeTun) WritePacket(pkt *stack.PacketBuffer) (int, error) { iovecs := t.iovecsOutputDefault + if t.vnetHdr { + if t.vnetHdrWriteBuf == nil { + t.vnetHdrWriteBuf = make([]byte, virtioNetHdrLen) + } + vnetHdr := virtioNetHdr{} + if pkt.GSOOptions.Type != stack.GSONone { + vnetHdr.hdrLen = uint16(pkt.HeaderSize()) + if pkt.GSOOptions.NeedsCsum { + vnetHdr.flags = unix.VIRTIO_NET_HDR_F_NEEDS_CSUM + vnetHdr.csumStart = pkt.GSOOptions.L3HdrLen + vnetHdr.csumOffset = pkt.GSOOptions.CsumOffset + } + if uint16(pkt.Data().Size()) > pkt.GSOOptions.MSS { + switch pkt.GSOOptions.Type { + case stack.GSOTCPv4: + vnetHdr.gsoType = unix.VIRTIO_NET_HDR_GSO_TCPV4 + case stack.GSOTCPv6: + vnetHdr.gsoType = unix.VIRTIO_NET_HDR_GSO_TCPV6 + default: + panic(fmt.Sprintf("Unknown gso type: %v", pkt.GSOOptions.Type)) + } + vnetHdr.gsoSize = pkt.GSOOptions.MSS + } + } + if err := vnetHdr.encode(t.vnetHdrWriteBuf); err != nil { + return 0, err + } + iovec := unix.Iovec{Base: &t.vnetHdrWriteBuf[0]} + iovec.SetLen(virtioNetHdrLen) + iovecs = append(iovecs, iovec) + } var dataLen int for _, packetSlice := range pkt.AsSlices() { dataLen += len(packetSlice)