世界 3 лет назад
Родитель
Сommit
730144cc26

+ 151 - 0
common/tun/gvisor.go

@@ -0,0 +1,151 @@
+package tun
+
+import (
+	"context"
+
+	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/log"
+	"github.com/sagernet/sing/common"
+	"github.com/sagernet/sing/common/bufio"
+	E "github.com/sagernet/sing/common/exceptions"
+	M "github.com/sagernet/sing/common/metadata"
+
+	"gvisor.dev/gvisor/pkg/tcpip"
+	"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
+	"gvisor.dev/gvisor/pkg/tcpip/header"
+	"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
+	"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
+	"gvisor.dev/gvisor/pkg/tcpip/stack"
+	"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
+	"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
+	"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
+	"gvisor.dev/gvisor/pkg/waiter"
+)
+
+const defaultNIC tcpip.NICID = 1
+
+var _ adapter.Service = (*GVisorTun)(nil)
+
+type GVisorTun struct {
+	ctx     context.Context
+	tunFd   uintptr
+	tunMtu  uint32
+	handler Handler
+	stack   *stack.Stack
+}
+
+func NewGVisor(ctx context.Context, tunFd uintptr, tunMtu uint32, handler Handler) *GVisorTun {
+	return &GVisorTun{
+		ctx:     ctx,
+		tunFd:   tunFd,
+		tunMtu:  tunMtu,
+		handler: handler,
+	}
+}
+
+func (t *GVisorTun) Start() error {
+	linkEndpoint, err := NewEndpoint(t.tunFd, t.tunMtu)
+	if err != nil {
+		return err
+	}
+	ipStack := stack.New(stack.Options{
+		NetworkProtocols: []stack.NetworkProtocolFactory{
+			ipv4.NewProtocol,
+			ipv6.NewProtocol,
+		},
+		TransportProtocols: []stack.TransportProtocolFactory{
+			tcp.NewProtocol,
+			udp.NewProtocol,
+			icmp.NewProtocol4,
+			icmp.NewProtocol6,
+		},
+	})
+	tErr := ipStack.CreateNIC(defaultNIC, linkEndpoint)
+	if tErr != nil {
+		return E.New("create nic: ", tErr)
+	}
+	ipStack.SetRouteTable([]tcpip.Route{
+		{Destination: header.IPv4EmptySubnet, NIC: defaultNIC},
+		{Destination: header.IPv6EmptySubnet, NIC: defaultNIC},
+	})
+	ipStack.SetSpoofing(defaultNIC, true)
+	ipStack.SetPromiscuousMode(defaultNIC, true)
+	bufSize := 20 * 1024
+	ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &tcpip.TCPReceiveBufferSizeRangeOption{
+		Min:     1,
+		Default: bufSize,
+		Max:     bufSize,
+	})
+	ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &tcpip.TCPSendBufferSizeRangeOption{
+		Min:     1,
+		Default: bufSize,
+		Max:     bufSize,
+	})
+	sOpt := tcpip.TCPSACKEnabled(true)
+	ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &sOpt)
+	mOpt := tcpip.TCPModerateReceiveBufferOption(true)
+	ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &mOpt)
+	tcpForwarder := tcp.NewForwarder(ipStack, 0, 1024, func(r *tcp.ForwarderRequest) {
+		var wq waiter.Queue
+		endpoint, err := r.CreateEndpoint(&wq)
+		if err != nil {
+			r.Complete(true)
+			return
+		}
+		r.Complete(false)
+		endpoint.SocketOptions().SetKeepAlive(true)
+		tcpConn := gonet.NewTCPConn(&wq, endpoint)
+		lAddr := tcpConn.RemoteAddr()
+		rAddr := tcpConn.LocalAddr()
+		if lAddr == nil || rAddr == nil {
+			tcpConn.Close()
+			return
+		}
+		go func() {
+			var metadata M.Metadata
+			metadata.Source = M.SocksaddrFromNet(lAddr)
+			metadata.Destination = M.SocksaddrFromNet(rAddr)
+			ctx := log.ContextWithID(t.ctx)
+			hErr := t.handler.NewConnection(ctx, tcpConn, metadata)
+			if hErr != nil {
+				t.handler.NewError(ctx, hErr)
+			}
+		}()
+	})
+	ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, func(id stack.TransportEndpointID, buffer *stack.PacketBuffer) bool {
+		return tcpForwarder.HandlePacket(id, buffer)
+	})
+	udpForwarder := udp.NewForwarder(ipStack, func(request *udp.ForwarderRequest) {
+		var wq waiter.Queue
+		endpoint, err := request.CreateEndpoint(&wq)
+		if err != nil {
+			return
+		}
+		udpConn := gonet.NewUDPConn(ipStack, &wq, endpoint)
+		lAddr := udpConn.RemoteAddr()
+		rAddr := udpConn.LocalAddr()
+		if lAddr == nil || rAddr == nil {
+			udpConn.Close()
+			return
+		}
+		go func() {
+			var metadata M.Metadata
+			metadata.Source = M.SocksaddrFromNet(lAddr)
+			metadata.Destination = M.SocksaddrFromNet(rAddr)
+			ctx := log.ContextWithID(t.ctx)
+			hErr := t.handler.NewPacketConnection(ctx, bufio.NewPacketConn(&bufio.UnbindPacketConn{ExtendedConn: bufio.NewExtendedConn(udpConn), Addr: M.SocksaddrFromNet(rAddr)}), metadata)
+			if hErr != nil {
+				t.handler.NewError(ctx, hErr)
+			}
+		}()
+	})
+	ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
+	t.stack = ipStack
+	return nil
+}
+
+func (t *GVisorTun) Close() error {
+	return common.Close(
+		common.PtrOrNil(t.stack),
+	)
+}

+ 16 - 0
common/tun/gvisor_linux.go

@@ -0,0 +1,16 @@
+//go:build linux
+
+package tun
+
+import (
+	"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
+	"gvisor.dev/gvisor/pkg/tcpip/stack"
+)
+
+func NewEndpoint(tunFd uintptr, tunMtu uint32) (stack.LinkEndpoint, error) {
+	return fdbased.New(&fdbased.Options{
+		FDs:                []int{int(tunFd)},
+		MTU:                tunMtu,
+		PacketDispatchMode: fdbased.PacketMMap,
+	})
+}

+ 9 - 0
common/tun/gvisor_nonlinux.go

@@ -0,0 +1,9 @@
+//go:build !linux
+
+package tun
+
+import "gvisor.dev/gvisor/pkg/tcpip/stack"
+
+func NewEndpoint(tunFd uintptr, tunMtu uint32) (stack.LinkEndpoint, error) {
+	return NewPosixEndpoint(tunFd, tunMtu)
+}

+ 118 - 0
common/tun/gvisor_posix.go

@@ -0,0 +1,118 @@
+package tun
+
+import (
+	"os"
+
+	"github.com/sagernet/sing/common"
+	"github.com/sagernet/sing/common/buf"
+	"github.com/sagernet/sing/common/rw"
+
+	gBuffer "gvisor.dev/gvisor/pkg/buffer"
+	"gvisor.dev/gvisor/pkg/tcpip"
+	"gvisor.dev/gvisor/pkg/tcpip/header"
+	"gvisor.dev/gvisor/pkg/tcpip/stack"
+)
+
+var _ stack.LinkEndpoint = (*PosixEndpoint)(nil)
+
+type PosixEndpoint struct {
+	fd         uintptr
+	mtu        uint32
+	file       *os.File
+	dispatcher stack.NetworkDispatcher
+}
+
+func NewPosixEndpoint(tunFd uintptr, tunMtu uint32) (stack.LinkEndpoint, error) {
+	return &PosixEndpoint{
+		fd:   tunFd,
+		mtu:  tunMtu,
+		file: os.NewFile(tunFd, "tun"),
+	}, nil
+}
+
+func (e *PosixEndpoint) MTU() uint32 {
+	return e.mtu
+}
+
+func (e *PosixEndpoint) MaxHeaderLength() uint16 {
+	return 0
+}
+
+func (e *PosixEndpoint) LinkAddress() tcpip.LinkAddress {
+	return ""
+}
+
+func (e *PosixEndpoint) Capabilities() stack.LinkEndpointCapabilities {
+	return stack.CapabilityNone
+}
+
+func (e *PosixEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
+	if dispatcher == nil && e.dispatcher != nil {
+		e.dispatcher = nil
+		return
+	}
+	if dispatcher != nil && e.dispatcher == nil {
+		e.dispatcher = dispatcher
+		go e.dispatchLoop()
+	}
+}
+
+func (e *PosixEndpoint) dispatchLoop() {
+	_buffer := buf.StackNewPacket()
+	defer common.KeepAlive(_buffer)
+	buffer := common.Dup(_buffer)
+	defer buffer.Release()
+	for {
+		n, err := e.file.Read(buffer.FreeBytes())
+		if err != nil {
+			break
+		}
+		var view gBuffer.View
+		view.Append(buffer.To(n))
+		pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
+			Payload:           view,
+			IsForwardedPacket: true,
+		})
+		defer pkt.DecRef()
+		var p tcpip.NetworkProtocolNumber
+		ipHeader, ok := pkt.Data().PullUp(1)
+		if !ok {
+			continue
+		}
+		switch header.IPVersion(ipHeader) {
+		case header.IPv4Version:
+			p = header.IPv4ProtocolNumber
+		case header.IPv6Version:
+			p = header.IPv6ProtocolNumber
+		default:
+			continue
+		}
+		e.dispatcher.DeliverNetworkPacket(p, pkt)
+	}
+}
+
+func (e *PosixEndpoint) IsAttached() bool {
+	return e.dispatcher != nil
+}
+
+func (e *PosixEndpoint) Wait() {
+}
+
+func (e *PosixEndpoint) ARPHardwareType() header.ARPHardwareType {
+	return header.ARPHardwareNone
+}
+
+func (e *PosixEndpoint) AddHeader(buffer *stack.PacketBuffer) {
+}
+
+func (e *PosixEndpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error) {
+	var n int
+	for _, packet := range pkts.AsSlice() {
+		_, err := rw.WriteV(e.fd, packet.Slices())
+		if err != nil {
+			return n, &tcpip.ErrAborted{}
+		}
+		n++
+	}
+	return n, nil
+}

+ 12 - 0
common/tun/tun.go

@@ -0,0 +1,12 @@
+package tun
+
+import (
+	E "github.com/sagernet/sing/common/exceptions"
+	N "github.com/sagernet/sing/common/network"
+)
+
+type Handler interface {
+	N.TCPConnectionHandler
+	N.UDPConnectionHandler
+	E.Handler
+}

+ 51 - 0
common/tun/tun_linux.go

@@ -0,0 +1,51 @@
+package tun
+
+import (
+	"net/netip"
+
+	"github.com/vishvananda/netlink"
+	"gvisor.dev/gvisor/pkg/tcpip/link/tun"
+)
+
+func Open(name string) (uintptr, error) {
+	tunFd, err := tun.Open(name)
+	if err != nil {
+		return 0, err
+	}
+	return uintptr(tunFd), nil
+}
+
+func Configure(name string, inet4Address netip.Prefix, inet6Address netip.Prefix, mtu uint32) error {
+	tunLink, err := netlink.LinkByName(name)
+	if err != nil {
+		return err
+	}
+
+	if inet4Address.IsValid() {
+		addr4, _ := netlink.ParseAddr(inet4Address.String())
+		err = netlink.AddrAdd(tunLink, addr4)
+		if err != nil {
+			return err
+		}
+	}
+
+	if inet6Address.IsValid() {
+		addr6, _ := netlink.ParseAddr(inet6Address.String())
+		err = netlink.AddrAdd(tunLink, addr6)
+		if err != nil {
+			return err
+		}
+	}
+
+	err = netlink.LinkSetMTU(tunLink, int(mtu))
+	if err != nil {
+		return err
+	}
+
+	err = netlink.LinkSetUp(tunLink)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}

+ 11 - 0
common/tun/tun_other.go

@@ -0,0 +1,11 @@
+//go:build !linux
+
+package tun
+
+import (
+	"os"
+)
+
+func Open(name string) (uintptr, error) {
+	return 0, os.ErrInvalid
+}

+ 2 - 0
constant/cgo.go

@@ -1,3 +1,5 @@
+//go:build cgo
+
 package constant
 
 const CGO_ENABLED = true

+ 1 - 0
constant/proxy.go

@@ -7,4 +7,5 @@ const (
 	TypeHTTP        = "http"
 	TypeMixed       = "mixed"
 	TypeShadowsocks = "shadowsocks"
+	TypeTun         = "tun"
 )

+ 8 - 3
go.mod

@@ -7,23 +7,28 @@ require (
 	github.com/goccy/go-json v0.9.8
 	github.com/logrusorgru/aurora v2.0.3+incompatible
 	github.com/oschwald/maxminddb-golang v1.9.0
-	github.com/sagernet/sing v0.0.0-20220709022704-8843420c7814
+	github.com/sagernet/sing v0.0.0-20220709090827-3e39af603559
 	github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649
 	github.com/sirupsen/logrus v1.8.1
 	github.com/spf13/cobra v1.5.0
 	github.com/stretchr/testify v1.8.0
+	github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86
 	golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
-	golang.org/x/net v0.0.0-20220706163947-c90051bbdb60
+	golang.org/x/net v0.0.0-20220708220712-1185a9018129
+	gvisor.dev/gvisor v0.0.0-20220708233959-72bdef768f07
 )
 
 require (
 	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/google/btree v1.0.1 // indirect
 	github.com/inconshreveable/mousetrap v1.0.0 // indirect
 	github.com/klauspost/cpuid/v2 v2.0.12 // indirect
 	github.com/kr/pretty v0.1.0 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
-	golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b // indirect
+	github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
+	golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d // indirect
+	golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
 	gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	lukechampine.com/blake3 v1.1.7 // indirect

+ 17 - 6
go.sum

@@ -6,6 +6,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/goccy/go-json v0.9.8 h1:DxXB6MLd6yyel7CLph8EwNIonUtVZd3Ue5iRcL4DQCE=
 github.com/goccy/go-json v0.9.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
+github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
 github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
@@ -23,8 +25,8 @@ github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/sagernet/sing v0.0.0-20220709022704-8843420c7814 h1:CqrZT6p8TeMaJfys/0Scl9lO5gu1VccUrBw8fAq3ZIk=
-github.com/sagernet/sing v0.0.0-20220709022704-8843420c7814/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c=
+github.com/sagernet/sing v0.0.0-20220709090827-3e39af603559 h1:o5/fAARzVV2PkEDzjOct1FRUdWVw7hPDFYY8drulH50=
+github.com/sagernet/sing v0.0.0-20220709090827-3e39af603559/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 h1:whNDUGOAX5GPZkSy4G3Gv9QyIgk5SXRyjkRuP7ohF8k=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649/go.mod h1:MuyT+9fEPjvauAv0fSE0a6Q+l0Tv2ZrAafTkYfnxBFw=
 github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
@@ -39,13 +41,20 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86 h1:7SWt9pGCMaw+N1ZhRsaLKaYNviFhxambdoaoYlDqz1w=
+github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
+github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
+github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/net v0.0.0-20220706163947-c90051bbdb60 h1:8NSylCMxLW4JvserAndSgFL7aPli6A68yf0bYFTcWCM=
-golang.org/x/net v0.0.0-20220706163947-c90051bbdb60/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.0.0-20220708220712-1185a9018129 h1:vucSRfWwTsoXro7P+3Cjlr6flUMtzCwzlvkxEQtHHB0=
+golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/FiaizQEK5Gu4Bq4JE8=
-golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d h1:/m5NbqQelATgoSPVC2Z23sR4kVNokFwDDyWh/3rGY+I=
+golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -53,5 +62,7 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gvisor.dev/gvisor v0.0.0-20220708233959-72bdef768f07 h1:EBa4BJfuxvdpvMGq6gw347MDptuFgqDHhuTEKxCCQ5U=
+gvisor.dev/gvisor v0.0.0-20220708233959-72bdef768f07/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
 lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
 lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=

+ 2 - 0
inbound/builder.go

@@ -34,6 +34,8 @@ func New(ctx context.Context, router adapter.Router, logger log.Logger, index in
 		return NewMixed(ctx, router, inboundLogger, options.Tag, options.MixedOptions), nil
 	case C.TypeShadowsocks:
 		return NewShadowsocks(ctx, router, inboundLogger, options.Tag, options.ShadowsocksOptions)
+	case C.TypeTun:
+		return NewTun(ctx, router, inboundLogger, options.Tag, options.TunOptions)
 	default:
 		return nil, E.New("unknown inbound type: ", options.Type)
 	}

+ 6 - 2
inbound/default.go

@@ -231,12 +231,16 @@ func (a *myInboundAdapter) newError(err error) {
 }
 
 func (a *myInboundAdapter) NewError(ctx context.Context, err error) {
+	NewError(a.logger, ctx, err)
+}
+
+func NewError(logger log.Logger, ctx context.Context, err error) {
 	common.Close(err)
 	if E.IsClosed(err) {
-		a.logger.WithContext(ctx).Debug("connection closed")
+		logger.WithContext(ctx).Debug("connection closed")
 		return
 	}
-	a.logger.WithContext(ctx).Error(err)
+	logger.WithContext(ctx).Error(err)
 }
 
 func (a *myInboundAdapter) writePacket(buffer *buf.Buffer, destination M.Socksaddr) error {

+ 142 - 0
inbound/tun.go

@@ -0,0 +1,142 @@
+//go:build !no_tun
+
+package inbound
+
+import (
+	"context"
+	"net"
+	"net/netip"
+	"os"
+	"runtime"
+	"strconv"
+	"strings"
+
+	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/tun"
+	C "github.com/sagernet/sing-box/constant"
+	"github.com/sagernet/sing-box/log"
+	"github.com/sagernet/sing-box/option"
+	E "github.com/sagernet/sing/common/exceptions"
+	F "github.com/sagernet/sing/common/format"
+	M "github.com/sagernet/sing/common/metadata"
+	N "github.com/sagernet/sing/common/network"
+)
+
+var _ adapter.Inbound = (*Tun)(nil)
+
+type Tun struct {
+	tag string
+
+	ctx     context.Context
+	router  adapter.Router
+	logger  log.Logger
+	options option.TunInboundOptions
+
+	tunFd uintptr
+	tun   *tun.GVisorTun
+}
+
+func NewTun(ctx context.Context, router adapter.Router, logger log.Logger, tag string, options option.TunInboundOptions) (*Tun, error) {
+	return &Tun{
+		tag:     tag,
+		ctx:     ctx,
+		router:  router,
+		logger:  logger,
+		options: options,
+	}, nil
+}
+
+func (t *Tun) Type() string {
+	return C.TypeTun
+}
+
+func (t *Tun) Tag() string {
+	return t.tag
+}
+
+func (t *Tun) Start() error {
+	tunName := t.options.InterfaceName
+	if tunName == "" {
+		tunName = mkInterfaceName()
+	}
+	var mtu uint32
+	if t.options.MTU != 0 {
+		mtu = t.options.MTU
+	} else {
+		mtu = 1500
+	}
+
+	tunFd, err := tun.Open(tunName)
+	if err != nil {
+		return E.Cause(err, "create tun interface")
+	}
+	err = tun.Configure(tunName, netip.Prefix(t.options.Inet4Address), netip.Prefix(t.options.Inet6Address), mtu)
+	if err != nil {
+		return E.Cause(err, "configure tun interface")
+	}
+	t.tunFd = tunFd
+	t.tun = tun.NewGVisor(t.ctx, tunFd, mtu, t)
+	err = t.tun.Start()
+	if err != nil {
+		return err
+	}
+	t.logger.Info("started at ", tunName)
+	return nil
+}
+
+func (t *Tun) Close() error {
+	return E.Errors(
+		t.tun.Close(),
+		os.NewFile(t.tunFd, "tun").Close(),
+	)
+}
+
+func (t *Tun) NewConnection(ctx context.Context, conn net.Conn, upstreamMetadata M.Metadata) error {
+	t.logger.WithContext(ctx).Info("inbound connection from ", upstreamMetadata.Source)
+	t.logger.WithContext(ctx).Info("inbound connection to ", upstreamMetadata.Destination)
+	var metadata adapter.InboundContext
+	metadata.Inbound = t.tag
+	metadata.Network = C.NetworkTCP
+	metadata.Source = upstreamMetadata.Source
+	metadata.Destination = upstreamMetadata.Destination
+	return t.router.RouteConnection(ctx, conn, metadata)
+}
+
+func (t *Tun) NewPacketConnection(ctx context.Context, conn N.PacketConn, upstreamMetadata M.Metadata) error {
+	t.logger.WithContext(ctx).Info("inbound packet connection from ", upstreamMetadata.Source)
+	t.logger.WithContext(ctx).Info("inbound packet connection to ", upstreamMetadata.Destination)
+	var metadata adapter.InboundContext
+	metadata.Inbound = t.tag
+	metadata.Network = C.NetworkUDP
+	metadata.Source = upstreamMetadata.Source
+	metadata.Destination = upstreamMetadata.Destination
+	return t.router.RoutePacketConnection(ctx, conn, metadata)
+}
+
+func (t *Tun) NewError(ctx context.Context, err error) {
+	NewError(t.logger, ctx, err)
+}
+
+func mkInterfaceName() (tunName string) {
+	switch runtime.GOOS {
+	case "darwin":
+		tunName = "utun"
+	default:
+		tunName = "tun"
+	}
+	interfaces, err := net.Interfaces()
+	if err != nil {
+		return
+	}
+	var tunIndex int
+	for _, netInterface := range interfaces {
+		if strings.HasPrefix(netInterface.Name, tunName) {
+			index, parseErr := strconv.ParseInt(netInterface.Name[len(tunName):], 10, 16)
+			if parseErr == nil {
+				tunIndex = int(index) + 1
+			}
+		}
+	}
+	tunName = F.ToString(tunName, tunIndex)
+	return
+}

+ 16 - 0
inbound/tun_disabled.go

@@ -0,0 +1,16 @@
+//go:build no_tun
+
+package inbound
+
+import (
+	"context"
+
+	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/log"
+	"github.com/sagernet/sing-box/option"
+	E "github.com/sagernet/sing/common/exceptions"
+)
+
+func NewTun(ctx context.Context, router adapter.Router, logger log.Logger, tag string, options option.TunInboundOptions) (adapter.Inbound, error) {
+	return nil, E.New("tun disabled in this build")
+}

+ 14 - 1
option/inbound.go

@@ -17,6 +17,7 @@ type _Inbound struct {
 	HTTPOptions        SimpleInboundOptions      `json:"-"`
 	MixedOptions       SimpleInboundOptions      `json:"-"`
 	ShadowsocksOptions ShadowsocksInboundOptions `json:"-"`
+	TunOptions         TunInboundOptions         `json:"-"`
 }
 
 type Inbound _Inbound
@@ -28,7 +29,8 @@ func (h Inbound) Equals(other Inbound) bool {
 		h.SocksOptions.Equals(other.SocksOptions) &&
 		h.HTTPOptions.Equals(other.HTTPOptions) &&
 		h.MixedOptions.Equals(other.MixedOptions) &&
-		h.ShadowsocksOptions.Equals(other.ShadowsocksOptions)
+		h.ShadowsocksOptions.Equals(other.ShadowsocksOptions) &&
+		h.TunOptions == other.TunOptions
 }
 
 func (h Inbound) MarshalJSON() ([]byte, error) {
@@ -44,6 +46,8 @@ func (h Inbound) MarshalJSON() ([]byte, error) {
 		v = h.MixedOptions
 	case C.TypeShadowsocks:
 		v = h.ShadowsocksOptions
+	case C.TypeTun:
+		v = h.TunOptions
 	default:
 		return nil, E.New("unknown inbound type: ", h.Type)
 	}
@@ -67,6 +71,8 @@ func (h *Inbound) UnmarshalJSON(bytes []byte) error {
 		v = &h.MixedOptions
 	case C.TypeShadowsocks:
 		v = &h.ShadowsocksOptions
+	case C.TypeTun:
+		v = &h.TunOptions
 	default:
 		return nil
 	}
@@ -132,3 +138,10 @@ type ShadowsocksDestination struct {
 	Password string `json:"password"`
 	ServerOptions
 }
+
+type TunInboundOptions struct {
+	InterfaceName string       `json:"interface_name"`
+	MTU           uint32       `json:"mtu,omitempty"`
+	Inet4Address  ListenPrefix `json:"inet4_address"`
+	Inet6Address  ListenPrefix `json:"inet6_address"`
+}

+ 24 - 0
option/types.go

@@ -155,3 +155,27 @@ func (d *Duration) UnmarshalJSON(bytes []byte) error {
 	*d = Duration(duration)
 	return nil
 }
+
+type ListenPrefix netip.Prefix
+
+func (p ListenPrefix) MarshalJSON() ([]byte, error) {
+	prefix := netip.Prefix(p)
+	if !prefix.IsValid() {
+		return json.Marshal("")
+	}
+	return json.Marshal(prefix.String())
+}
+
+func (p *ListenPrefix) UnmarshalJSON(bytes []byte) error {
+	var value string
+	err := json.Unmarshal(bytes, &value)
+	if err != nil {
+		return err
+	}
+	prefix, err := netip.ParsePrefix(value)
+	if err != nil {
+		return err
+	}
+	*p = ListenPrefix(prefix)
+	return nil
+}

+ 7 - 3
test/go.mod

@@ -5,11 +5,11 @@ go 1.18
 require (
 	github.com/docker/docker v20.10.17+incompatible
 	github.com/docker/go-connections v0.4.0
-	github.com/sagernet/sing v0.0.0-20220709022704-8843420c7814
+	github.com/sagernet/sing v0.0.0-20220709090827-3e39af603559
 	github.com/sagernet/sing-box v0.0.0
 	github.com/sirupsen/logrus v1.8.1
 	github.com/stretchr/testify v1.8.0
-	golang.org/x/net v0.0.0-20220706163947-c90051bbdb60
+	golang.org/x/net v0.0.0-20220708220712-1185a9018129
 )
 
 replace github.com/sagernet/sing-box => ../
@@ -22,6 +22,7 @@ require (
 	github.com/docker/go-units v0.4.0 // indirect
 	github.com/goccy/go-json v0.9.8 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
+	github.com/google/btree v1.0.1 // indirect
 	github.com/klauspost/cpuid/v2 v2.0.12 // indirect
 	github.com/kr/text v0.2.0 // indirect
 	github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
@@ -33,10 +34,13 @@ require (
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 // indirect
+	github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86 // indirect
+	github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
 	golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
-	golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b // indirect
+	golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d // indirect
 	golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	gotest.tools/v3 v3.3.0 // indirect
+	gvisor.dev/gvisor v0.0.0-20220708233959-72bdef768f07 // indirect
 	lukechampine.com/blake3 v1.1.7 // indirect
 )

+ 16 - 7
test/go.sum

@@ -21,10 +21,12 @@ github.com/goccy/go-json v0.9.8 h1:DxXB6MLd6yyel7CLph8EwNIonUtVZd3Ue5iRcL4DQCE=
 github.com/goccy/go-json v0.9.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
+github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
@@ -50,8 +52,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/sagernet/sing v0.0.0-20220709022704-8843420c7814 h1:CqrZT6p8TeMaJfys/0Scl9lO5gu1VccUrBw8fAq3ZIk=
-github.com/sagernet/sing v0.0.0-20220709022704-8843420c7814/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c=
+github.com/sagernet/sing v0.0.0-20220709090827-3e39af603559 h1:o5/fAARzVV2PkEDzjOct1FRUdWVw7hPDFYY8drulH50=
+github.com/sagernet/sing v0.0.0-20220709090827-3e39af603559/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 h1:whNDUGOAX5GPZkSy4G3Gv9QyIgk5SXRyjkRuP7ohF8k=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649/go.mod h1:MuyT+9fEPjvauAv0fSE0a6Q+l0Tv2ZrAafTkYfnxBFw=
 github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
@@ -64,6 +66,10 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86 h1:7SWt9pGCMaw+N1ZhRsaLKaYNviFhxambdoaoYlDqz1w=
+github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
+github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
+github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -78,20 +84,21 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20220706163947-c90051bbdb60 h1:8NSylCMxLW4JvserAndSgFL7aPli6A68yf0bYFTcWCM=
-golang.org/x/net v0.0.0-20220706163947-c90051bbdb60/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.0.0-20220708220712-1185a9018129 h1:vucSRfWwTsoXro7P+3Cjlr6flUMtzCwzlvkxEQtHHB0=
+golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/FiaizQEK5Gu4Bq4JE8=
-golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d h1:/m5NbqQelATgoSPVC2Z23sR4kVNokFwDDyWh/3rGY+I=
+golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U=
@@ -114,5 +121,7 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
 gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
 gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
+gvisor.dev/gvisor v0.0.0-20220708233959-72bdef768f07 h1:EBa4BJfuxvdpvMGq6gw347MDptuFgqDHhuTEKxCCQ5U=
+gvisor.dev/gvisor v0.0.0-20220708233959-72bdef768f07/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
 lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
 lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=