mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-05-14 00:51:12 +03:00
Add tun inbound for linux
This commit is contained in:
151
common/tun/gvisor.go
Normal file
151
common/tun/gvisor.go
Normal 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),
|
||||
)
|
||||
}
|
||||
16
common/tun/gvisor_linux.go
Normal file
16
common/tun/gvisor_linux.go
Normal 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,
|
||||
})
|
||||
}
|
||||
9
common/tun/gvisor_nonlinux.go
Normal file
9
common/tun/gvisor_nonlinux.go
Normal 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
118
common/tun/gvisor_posix.go
Normal 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
12
common/tun/tun.go
Normal 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
51
common/tun/tun_linux.go
Normal 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
11
common/tun/tun_other.go
Normal file
@@ -0,0 +1,11 @@
|
||||
//go:build !linux
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func Open(name string) (uintptr, error) {
|
||||
return 0, os.ErrInvalid
|
||||
}
|
||||
Reference in New Issue
Block a user