Skip to content

Commit 53f5034

Browse files
committed
Fix system nat mapping
1 parent bf7110b commit 53f5034

File tree

4 files changed

+60
-23
lines changed

4 files changed

+60
-23
lines changed

gvisor.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func NewGVisor(
5757
return nil, E.New("gVisor stack is unsupported on current platform")
5858
}
5959

60-
return &GVisor{
60+
gStack := &GVisor{
6161
ctx: options.Context,
6262
tun: gTun,
6363
tunMtu: options.MTU,
@@ -66,8 +66,11 @@ func NewGVisor(
6666
router: options.Router,
6767
handler: options.Handler,
6868
logger: options.Logger,
69-
routeMapping: NewRouteMapping(options.UDPTimeout),
70-
}, nil
69+
}
70+
if gStack.router != nil {
71+
gStack.routeMapping = NewRouteMapping(options.Context, options.UDPTimeout)
72+
}
73+
return gStack, nil
7174
}
7275

7376
func (t *GVisor) Start() error {

route_mapping.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package tun
22

33
import (
4+
"context"
5+
46
"github.com/sagernet/sing/common"
57
"github.com/sagernet/sing/common/cache"
68
)
@@ -9,9 +11,10 @@ type RouteMapping struct {
911
status *cache.LruCache[RouteSession, RouteAction]
1012
}
1113

12-
func NewRouteMapping(maxAge int64) *RouteMapping {
14+
func NewRouteMapping(ctx context.Context, maxAge int64) *RouteMapping {
1315
return &RouteMapping{
1416
status: cache.New(
17+
cache.WithContext[RouteSession, RouteAction](ctx),
1518
cache.WithAge[RouteSession, RouteAction](maxAge),
1619
cache.WithUpdateAgeOnGet[RouteSession, RouteAction](),
1720
cache.WithEvict[RouteSession, RouteAction](func(key RouteSession, conn RouteAction) {

system.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ func NewSystem(options StackOptions) (Stack, error) {
6363
inet4Prefixes: options.Inet4Address,
6464
inet6Prefixes: options.Inet6Address,
6565
underPlatform: options.UnderPlatform,
66-
routeMapping: NewRouteMapping(options.UDPTimeout),
66+
}
67+
if stack.router != nil {
68+
stack.routeMapping = NewRouteMapping(options.Context, options.UDPTimeout)
6769
}
6870
if len(options.Inet4Address) > 0 {
6971
if options.Inet4Address[0].Bits() == 32 {
@@ -115,7 +117,7 @@ func (s *System) Start() error {
115117
s.tcpPort6 = M.SocksaddrFromNet(tcpListener.Addr()).Port
116118
go s.acceptLoop(tcpListener)
117119
}
118-
s.tcpNat = NewNat()
120+
s.tcpNat = NewNat(s.ctx, time.Second*time.Duration(s.udpTimeout))
119121
s.udpNat = udpnat.New[netip.AddrPort](s.ctx, s.udpTimeout, s.handler)
120122
go s.tunLoop()
121123
return nil
@@ -208,13 +210,14 @@ func (s *System) acceptLoop(listener net.Listener) {
208210
}
209211
}
210212
go func() {
211-
s.handler.NewConnection(s.ctx, conn, M.Metadata{
213+
_ = s.handler.NewConnection(s.ctx, conn, M.Metadata{
212214
Source: M.SocksaddrFromNetIP(session.Source),
213215
Destination: destination,
214216
})
215-
conn.Close()
216-
time.Sleep(time.Second)
217-
s.tcpNat.Revoke(connPort, session)
217+
if tcpConn, isTCPConn := conn.(*net.TCPConn); isTCPConn {
218+
_ = tcpConn.SetLinger(0)
219+
}
220+
_ = conn.Close()
218221
}()
219222
}
220223
}

system_nat.go

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package tun
22

33
import (
4+
"context"
45
"net/netip"
56
"sync"
7+
"time"
68
)
79

810
type TCPNat struct {
@@ -16,20 +18,54 @@ type TCPNat struct {
1618
type TCPSession struct {
1719
Source netip.AddrPort
1820
Destination netip.AddrPort
21+
LastActive time.Time
1922
}
2023

21-
func NewNat() *TCPNat {
22-
return &TCPNat{
24+
func NewNat(ctx context.Context, timeout time.Duration) *TCPNat {
25+
natMap := &TCPNat{
2326
portIndex: 10000,
2427
addrMap: make(map[netip.AddrPort]uint16),
2528
portMap: make(map[uint16]*TCPSession),
2629
}
30+
go natMap.loopCheckTimeout(ctx, timeout)
31+
return natMap
32+
}
33+
34+
func (n *TCPNat) loopCheckTimeout(ctx context.Context, timeout time.Duration) {
35+
ticker := time.NewTicker(timeout)
36+
defer ticker.Stop()
37+
for {
38+
select {
39+
case <-ticker.C:
40+
n.checkTimeout(timeout)
41+
case <-ctx.Done():
42+
return
43+
}
44+
}
45+
}
46+
47+
func (n *TCPNat) checkTimeout(timeout time.Duration) {
48+
now := time.Now()
49+
n.portAccess.Lock()
50+
defer n.portAccess.Unlock()
51+
n.addrAccess.Lock()
52+
defer n.addrAccess.Unlock()
53+
for natPort, session := range n.portMap {
54+
if now.Sub(session.LastActive) > timeout {
55+
delete(n.addrMap, session.Source)
56+
delete(n.portMap, natPort)
57+
}
58+
}
2759
}
2860

2961
func (n *TCPNat) LookupBack(port uint16) *TCPSession {
3062
n.portAccess.RLock()
31-
defer n.portAccess.RUnlock()
32-
return n.portMap[port]
63+
session := n.portMap[port]
64+
n.portAccess.RUnlock()
65+
if session != nil {
66+
session.LastActive = time.Now()
67+
}
68+
return session
3369
}
3470

3571
func (n *TCPNat) Lookup(source netip.AddrPort, destination netip.AddrPort) uint16 {
@@ -53,16 +89,8 @@ func (n *TCPNat) Lookup(source netip.AddrPort, destination netip.AddrPort) uint1
5389
n.portMap[nextPort] = &TCPSession{
5490
Source: source,
5591
Destination: destination,
92+
LastActive: time.Now(),
5693
}
5794
n.portAccess.Unlock()
5895
return nextPort
5996
}
60-
61-
func (n *TCPNat) Revoke(natPort uint16, session *TCPSession) {
62-
n.addrAccess.Lock()
63-
delete(n.addrMap, session.Source)
64-
n.addrAccess.Unlock()
65-
n.portAccess.Lock()
66-
delete(n.portMap, natPort)
67-
n.portAccess.Unlock()
68-
}

0 commit comments

Comments
 (0)