//go:build with_gvisor && !windows package tailscale import ( "encoding/hex" "errors" "io" "os" "sync" "sync/atomic" singTun "github.com/sagernet/sing-tun" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/logger" wgTun "github.com/sagernet/wireguard-go/tun" ) type tunDeviceAdapter struct { tun singTun.Tun linuxTUN singTun.LinuxTUN events chan wgTun.Event mtu int logger logger.ContextLogger debugTun bool readCount atomic.Uint32 writeCount atomic.Uint32 closeOnce sync.Once } func newTunDeviceAdapter(tun singTun.Tun, mtu int, logger logger.ContextLogger) (wgTun.Device, error) { if tun == nil { return nil, os.ErrInvalid } if mtu == 0 { mtu = 1500 } adapter := &tunDeviceAdapter{ tun: tun, events: make(chan wgTun.Event, 1), mtu: mtu, logger: logger, debugTun: os.Getenv("SINGBOX_TS_TUN_DEBUG") != "", } if linuxTUN, ok := tun.(singTun.LinuxTUN); ok { adapter.linuxTUN = linuxTUN } adapter.events <- wgTun.EventUp return adapter, nil } func (a *tunDeviceAdapter) File() *os.File { return nil } func (a *tunDeviceAdapter) Read(bufs [][]byte, sizes []int, offset int) (count int, err error) { if a.linuxTUN != nil { n, err := a.linuxTUN.BatchRead(bufs, offset-singTun.PacketOffset, sizes) if err == nil { for i := 0; i < n; i++ { a.debugPacket("read", bufs[i][offset:offset+sizes[i]]) } } return n, err } if offset < singTun.PacketOffset { return 0, io.ErrShortBuffer } readBuf := bufs[0][offset-singTun.PacketOffset:] n, err := a.tun.Read(readBuf) if err == nil { if n < singTun.PacketOffset { return 0, io.ErrUnexpectedEOF } sizes[0] = n - singTun.PacketOffset a.debugPacket("read", readBuf[singTun.PacketOffset:n]) return 1, nil } if errors.Is(err, singTun.ErrTooManySegments) { err = wgTun.ErrTooManySegments } return 0, err } func (a *tunDeviceAdapter) Write(bufs [][]byte, offset int) (count int, err error) { if a.linuxTUN != nil { for i := range bufs { a.debugPacket("write", bufs[i][offset:]) } return a.linuxTUN.BatchWrite(bufs, offset) } for _, packet := range bufs { a.debugPacket("write", packet[offset:]) if singTun.PacketOffset > 0 { common.ClearArray(packet[offset-singTun.PacketOffset : offset]) singTun.PacketFillHeader(packet[offset-singTun.PacketOffset:], singTun.PacketIPVersion(packet[offset:])) } _, err = a.tun.Write(packet[offset-singTun.PacketOffset:]) if err != nil { return 0, err } } // WireGuard will not read count. return 0, nil } func (a *tunDeviceAdapter) MTU() (int, error) { return a.mtu, nil } func (a *tunDeviceAdapter) Name() (string, error) { return a.tun.Name() } func (a *tunDeviceAdapter) Events() <-chan wgTun.Event { return a.events } func (a *tunDeviceAdapter) Close() error { var err error a.closeOnce.Do(func() { close(a.events) err = a.tun.Close() }) return err } func (a *tunDeviceAdapter) BatchSize() int { if a.linuxTUN != nil { return a.linuxTUN.BatchSize() } return 1 } func (a *tunDeviceAdapter) debugPacket(direction string, packet []byte) { if !a.debugTun || a.logger == nil { return } var counter *atomic.Uint32 switch direction { case "read": counter = &a.readCount case "write": counter = &a.writeCount default: return } if counter.Add(1) > 8 { return } sample := packet if len(sample) > 64 { sample = sample[:64] } a.logger.Trace("tailscale tun ", direction, " len=", len(packet), " head=", hex.EncodeToString(sample)) }