| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- //go:build windows
- package tun
- import (
- "errors"
- _ "unsafe"
- "golang.org/x/sys/windows"
- "golang.zx2c4.com/wintun"
- "gvisor.dev/gvisor/pkg/buffer"
- "gvisor.dev/gvisor/pkg/tcpip"
- "gvisor.dev/gvisor/pkg/tcpip/stack"
- )
- //go:linkname procyield runtime.procyield
- func procyield(cycles uint32)
- // WindowsTun is an object that handles tun network interface on Windows
- // current version is heavily stripped to do nothing more,
- // then create a network interface, to be provided as endpoint to gVisor ip stack
- type WindowsTun struct {
- options TunOptions
- adapter *wintun.Adapter
- session wintun.Session
- readWait windows.Handle
- MTU uint32
- }
- // WindowsTun implements Tun
- var _ Tun = (*WindowsTun)(nil)
- // WindowsTun implements GVisorTun
- var _ GVisorTun = (*WindowsTun)(nil)
- // WindowsTun implements GVisorDevice
- var _ GVisorDevice = (*WindowsTun)(nil)
- // NewTun creates a Wintun interface with the given name. Should a Wintun
- // interface with the same name exist, it tried to be reused.
- func NewTun(options TunOptions) (Tun, error) {
- // instantiate wintun adapter
- adapter, err := open(options.Name)
- if err != nil {
- return nil, err
- }
- // start the interface with ring buffer capacity of 8 MiB
- session, err := adapter.StartSession(0x800000)
- if err != nil {
- _ = adapter.Close()
- return nil, err
- }
- tun := &WindowsTun{
- options: options,
- adapter: adapter,
- session: session,
- readWait: session.ReadWaitEvent(),
- // there is currently no iphndl.dll support, which is the netlink library for windows
- // so there is nowhere to change MTU for the Wintun interface, and we take its default value
- MTU: wintun.PacketSizeMax,
- }
- return tun, nil
- }
- func open(name string) (*wintun.Adapter, error) {
- var guid *windows.GUID
- // try to open existing adapter by name
- adapter, err := wintun.OpenAdapter(name)
- if err == nil {
- return adapter, nil
- }
- // try to create adapter anew
- adapter, err = wintun.CreateAdapter(name, "Xray", guid)
- if err == nil {
- return adapter, nil
- }
- return nil, err
- }
- func (t *WindowsTun) Start() error {
- return nil
- }
- func (t *WindowsTun) Close() error {
- t.session.End()
- _ = t.adapter.Close()
- return nil
- }
- // WritePacket implements GVisorDevice method to write one packet to the tun device
- func (t *WindowsTun) WritePacket(packetBuffer *stack.PacketBuffer) tcpip.Error {
- // request buffer from Wintun
- packet, err := t.session.AllocateSendPacket(packetBuffer.Size())
- if err != nil {
- return &tcpip.ErrAborted{}
- }
- // copy the bytes of slices that compose the packet into the allocated buffer
- var index int
- for _, packetElement := range packetBuffer.AsSlices() {
- index += copy(packet[index:], packetElement)
- }
- // signal Wintun to send that buffer as the packet
- t.session.SendPacket(packet)
- return nil
- }
- // ReadPacket implements GVisorDevice method to read one packet from the tun device
- // It is expected that the method will not block, rather return ErrQueueEmpty when there is nothing on the line,
- // which will make the stack call Wait which should implement desired push-back
- func (t *WindowsTun) ReadPacket() (byte, *stack.PacketBuffer, error) {
- packet, err := t.session.ReceivePacket()
- if errors.Is(err, windows.ERROR_NO_MORE_ITEMS) {
- return 0, nil, ErrQueueEmpty
- }
- if err != nil {
- return 0, nil, err
- }
- version := packet[0] >> 4
- packetBuffer := buffer.MakeWithView(buffer.NewViewWithData(packet))
- return version, stack.NewPacketBuffer(stack.PacketBufferOptions{
- Payload: packetBuffer,
- IsForwardedPacket: true,
- OnRelease: func() {
- t.session.ReleaseReceivePacket(packet)
- },
- }), nil
- }
- func (t *WindowsTun) Wait() {
- procyield(1)
- _, _ = windows.WaitForSingleObject(t.readWait, windows.INFINITE)
- }
- func (t *WindowsTun) newEndpoint() (stack.LinkEndpoint, error) {
- return &LinkEndpoint{deviceMTU: t.options.MTU, device: t}, nil
- }
|