Add tun inbound for linux

This commit is contained in:
世界
2022-07-09 19:18:37 +08:00
parent c463d0cf80
commit 730144cc26
19 changed files with 623 additions and 22 deletions

151
common/tun/gvisor.go Normal file
View File

@@ -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),
)
}

View File

@@ -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,
})
}

View File

@@ -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
common/tun/gvisor_posix.go Normal file
View File

@@ -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
common/tun/tun.go Normal file
View File

@@ -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
common/tun/tun_linux.go Normal file
View File

@@ -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
common/tun/tun_other.go Normal file
View File

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