Skip to content

Commit

Permalink
Merge branch 'dev' of https://github.com/SagerNet/sing-tun into meta
Browse files Browse the repository at this point in the history
  • Loading branch information
wwqgtxx committed Apr 19, 2023
2 parents 56c8013 + 9c6e70b commit 049c668
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 46 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ require (
github.com/metacubex/gvisor v0.0.0-20230417114019-3c3ee672d60c
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
github.com/sagernet/sing v0.2.1
github.com/sagernet/sing v0.2.4-0.20230419153323-5fae6fa434c1
golang.org/x/net v0.8.0
golang.org/x/sys v0.6.0
golang.org/x/sys v0.7.0
)

require (
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.2.1 h1:r0STYeyfKBBtoAHsBtW1dQonxG+3Qidde7/1VAMhdn8=
github.com/sagernet/sing v0.2.1/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw=
github.com/sagernet/sing v0.2.4-0.20230419153323-5fae6fa434c1 h1:CdzNL25lzfVo0NMeghPqsupNsWvkzrbrUt5t8DoDPcQ=
github.com/sagernet/sing v0.2.4-0.20230419153323-5fae6fa434c1/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
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/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
9 changes: 6 additions & 3 deletions gvisor.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func NewGVisor(
return nil, E.New("gVisor stack is unsupported on current platform")
}

return &GVisor{
gStack := &GVisor{
ctx: options.Context,
tun: gTun,
tunMtu: options.MTU,
Expand All @@ -66,8 +66,11 @@ func NewGVisor(
router: options.Router,
handler: options.Handler,
logger: options.Logger,
routeMapping: NewRouteMapping(options.UDPTimeout),
}, nil
}
if gStack.router != nil {
gStack.routeMapping = NewRouteMapping(options.UDPTimeout)
}
return gStack, nil
}

func (t *GVisor) Start() error {
Expand Down
117 changes: 99 additions & 18 deletions monitor_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ import (
"net"
"net/netip"
"os"
"runtime"
"strings"
"sync"
"syscall"
"time"

"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
Expand Down Expand Up @@ -85,32 +84,114 @@ func (m *defaultInterfaceMonitor) checkUpdate() error {
if err != nil {
return err
}
var defaultInterface *net.Interface
for _, rawRouteMessage := range routeMessages {
routeMessage := rawRouteMessage.(*route.RouteMessage)
if len(routeMessage.Addrs) <= unix.RTAX_NETMASK {
continue
}
destination, isIPv4Destination := routeMessage.Addrs[unix.RTAX_DST].(*route.Inet4Addr)
if !isIPv4Destination {
continue
}
if destination.IP != netip.IPv4Unspecified().As4() {
continue
}
mask, isIPv4Mask := routeMessage.Addrs[unix.RTAX_NETMASK].(*route.Inet4Addr)
if !isIPv4Mask {
continue
}
ones, _ := net.IPMask(mask.IP[:]).Size()
if ones != 0 {
continue
}
routeInterface, err := net.InterfaceByIndex(routeMessage.Index)
if err != nil {
return err
}
if runtime.GOOS == "ios" && strings.HasPrefix(routeInterface.Name, "utun") {
if routeMessage.Flags&unix.RTF_UP == 0 {
continue
}
if routeMessage.Flags&unix.RTF_GATEWAY == 0 {
continue
}
if routeMessage.Flags&unix.RTF_IFSCOPE != 0 {
continue
}
if common.Any(common.FilterIsInstance(routeMessage.Addrs, func(it route.Addr) (*route.Inet4Addr, bool) {
addr, loaded := it.(*route.Inet4Addr)
return addr, loaded
}), func(addr *route.Inet4Addr) bool {
return addr.IP == netip.IPv4Unspecified().As4()
}) {
oldInterface := m.defaultInterfaceName
oldIndex := m.defaultInterfaceIndex
defaultInterface = routeInterface
break
}
if defaultInterface == nil {
defaultInterface, err = getDefaultInterfaceBySocket()
if err != nil {
return err
}
}
oldInterface := m.defaultInterfaceName
oldIndex := m.defaultInterfaceIndex
m.defaultInterfaceIndex = defaultInterface.Index
m.defaultInterfaceName = defaultInterface.Name
if oldInterface == m.defaultInterfaceName && oldIndex == m.defaultInterfaceIndex {
return nil
}
m.emit(EventInterfaceUpdate)
return nil
}

m.defaultInterfaceIndex = routeMessage.Index
m.defaultInterfaceName = routeInterface.Name
if oldInterface == m.defaultInterfaceName && oldIndex == m.defaultInterfaceIndex {
return nil
func getDefaultInterfaceBySocket() (*net.Interface, error) {
socketFd, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM, 0)
if err != nil {
return nil, E.Cause(err, "create file descriptor")
}
defer unix.Close(socketFd)
go unix.Connect(socketFd, &unix.SockaddrInet4{
Addr: [4]byte{10, 255, 255, 255},
Port: 80,
})
result := make(chan netip.Addr, 1)
go func() {
for {
sockname, sockErr := unix.Getsockname(socketFd)
if sockErr != nil {
break
}
sockaddr, isInet4Sockaddr := sockname.(*unix.SockaddrInet4)
if !isInet4Sockaddr {
break
}
addr := netip.AddrFrom4(sockaddr.Addr)
if addr.IsUnspecified() {
time.Sleep(time.Millisecond)
continue
}
result <- addr
break
}
}()
var selectedAddr netip.Addr
select {
case selectedAddr = <-result:
case <-time.After(time.Second):
return nil, os.ErrDeadlineExceeded
}
interfaces, err := net.Interfaces()
if err != nil {
return nil, E.Cause(err, "net.Interfaces")
}
for _, netInterface := range interfaces {
interfaceAddrs, err := netInterface.Addrs()
if err != nil {
return nil, E.Cause(err, "net.Interfaces.Addrs")
}
for _, interfaceAddr := range interfaceAddrs {
ipNet, isIPNet := interfaceAddr.(*net.IPNet)
if !isIPNet {
continue
}
if ipNet.Contains(selectedAddr.AsSlice()) {
return &netInterface, nil
}
m.emit(EventInterfaceUpdate)
return nil
}
}
return ErrNoRoute
return nil, E.New("no interface found for address ", selectedAddr)
}
15 changes: 9 additions & 6 deletions system.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ func NewSystem(options StackOptions) (Stack, error) {
inet4Prefixes: options.Inet4Address,
inet6Prefixes: options.Inet6Address,
underPlatform: options.UnderPlatform,
routeMapping: NewRouteMapping(options.UDPTimeout),
}
if stack.router != nil {
stack.routeMapping = NewRouteMapping(options.UDPTimeout)
}
if len(options.Inet4Address) > 0 {
if options.Inet4Address[0].Bits() == 32 {
Expand Down Expand Up @@ -115,7 +117,7 @@ func (s *System) Start() error {
s.tcpPort6 = M.SocksaddrFromNet(tcpListener.Addr()).Port
go s.acceptLoop(tcpListener)
}
s.tcpNat = NewNat()
s.tcpNat = NewNat(s.ctx, time.Second*time.Duration(s.udpTimeout))
s.udpNat = udpnat.New[netip.AddrPort](s.udpTimeout, s.handler)
go s.tunLoop()
return nil
Expand Down Expand Up @@ -208,13 +210,14 @@ func (s *System) acceptLoop(listener net.Listener) {
}
}
go func() {
s.handler.NewConnection(s.ctx, conn, M.Metadata{
_ = s.handler.NewConnection(s.ctx, conn, M.Metadata{
Source: M.SocksaddrFromNetIP(session.Source),
Destination: destination,
})
conn.Close()
time.Sleep(time.Second)
s.tcpNat.Revoke(connPort, session)
if tcpConn, isTCPConn := conn.(*net.TCPConn); isTCPConn {
_ = tcpConn.SetLinger(0)
}
_ = conn.Close()
}()
}
}
Expand Down
54 changes: 41 additions & 13 deletions system_nat.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package tun

import (
"context"
"net/netip"
"sync"
"time"
)

type TCPNat struct {
Expand All @@ -16,20 +18,54 @@ type TCPNat struct {
type TCPSession struct {
Source netip.AddrPort
Destination netip.AddrPort
LastActive time.Time
}

func NewNat() *TCPNat {
return &TCPNat{
func NewNat(ctx context.Context, timeout time.Duration) *TCPNat {
natMap := &TCPNat{
portIndex: 10000,
addrMap: make(map[netip.AddrPort]uint16),
portMap: make(map[uint16]*TCPSession),
}
go natMap.loopCheckTimeout(ctx, timeout)
return natMap
}

func (n *TCPNat) loopCheckTimeout(ctx context.Context, timeout time.Duration) {
ticker := time.NewTicker(timeout)
defer ticker.Stop()
for {
select {
case <-ticker.C:
n.checkTimeout(timeout)
case <-ctx.Done():
return
}
}
}

func (n *TCPNat) checkTimeout(timeout time.Duration) {
now := time.Now()
n.portAccess.Lock()
defer n.portAccess.Unlock()
n.addrAccess.Lock()
defer n.addrAccess.Unlock()
for natPort, session := range n.portMap {
if now.Sub(session.LastActive) > timeout {
delete(n.addrMap, session.Source)
delete(n.portMap, natPort)
}
}
}

func (n *TCPNat) LookupBack(port uint16) *TCPSession {
n.portAccess.RLock()
defer n.portAccess.RUnlock()
return n.portMap[port]
session := n.portMap[port]
n.portAccess.RUnlock()
if session != nil {
session.LastActive = time.Now()
}
return session
}

func (n *TCPNat) Lookup(source netip.AddrPort, destination netip.AddrPort) uint16 {
Expand All @@ -53,16 +89,8 @@ func (n *TCPNat) Lookup(source netip.AddrPort, destination netip.AddrPort) uint1
n.portMap[nextPort] = &TCPSession{
Source: source,
Destination: destination,
LastActive: time.Now(),
}
n.portAccess.Unlock()
return nextPort
}

func (n *TCPNat) Revoke(natPort uint16, session *TCPSession) {
n.addrAccess.Lock()
delete(n.addrMap, session.Source)
n.addrAccess.Unlock()
n.portAccess.Lock()
delete(n.portMap, natPort)
n.portAccess.Unlock()
}

0 comments on commit 049c668

Please sign in to comment.