-
Notifications
You must be signed in to change notification settings - Fork 197
Expand file tree
/
Copy pathflow_nat.go
More file actions
121 lines (107 loc) · 3.14 KB
/
Copy pathflow_nat.go
File metadata and controls
121 lines (107 loc) · 3.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package tun
import (
"net/netip"
"runtime"
"sync"
"github.com/sagernet/sing-tun/gtcpip/header"
"github.com/sagernet/sing/contrab/maphash"
)
const (
natSelectorMin = 49152
natSelectorMax = 65535
)
type portNAT struct {
port Port
hasher maphash.Hasher[flowKey]
shardMask uint32
shards []natShard
selectorStart uint16
selectorCount uint16
counter uint32
pending [][]byte
}
type natShard struct {
access sync.RWMutex
flows map[flowKey]*forwardFlow
}
func newPortNAT(port Port) *portNAT {
shardCount := 1
for shardCount < runtime.GOMAXPROCS(0) {
shardCount <<= 1
}
nat := &portNAT{
port: port,
hasher: maphash.NewHasher[flowKey](),
shardMask: uint32(shardCount - 1),
shards: make([]natShard, shardCount),
}
if rangedPort, isRanged := port.(PortWithSelectorRange); isRanged {
nat.selectorStart, nat.selectorCount = rangedPort.PortSelectorRange()
}
for i := range nat.shards {
nat.shards[i].flows = make(map[flowKey]*forwardFlow)
}
return nat
}
func (n *portNAT) shard(key flowKey) *natShard {
return &n.shards[n.hasher.Hash32(key)&n.shardMask]
}
func (n *portNAT) lookup(key flowKey) *forwardFlow {
shard := n.shard(key)
shard.access.RLock()
flow := shard.flows[key]
shard.access.RUnlock()
return flow
}
func (n *portNAT) insert(key flowKey, flow *forwardFlow) {
shard := n.shard(key)
shard.access.Lock()
shard.flows[key] = flow
shard.access.Unlock()
}
func (n *portNAT) delete(key flowKey) {
shard := n.shard(key)
shard.access.Lock()
delete(shard.flows, key)
shard.access.Unlock()
}
func (n *portNAT) reverseKeyFor(protocol uint8, portAddress, serverAddress netip.Addr, serverPort, selector uint16) flowKey {
if protocol == uint8(header.ICMPv4ProtocolNumber) || protocol == uint8(header.ICMPv6ProtocolNumber) {
return flowKey{
protocol: protocol,
source: netip.AddrPortFrom(serverAddress, selector),
destination: netip.AddrPortFrom(portAddress, selector),
}
}
return flowKey{
protocol: protocol,
source: netip.AddrPortFrom(serverAddress, serverPort),
destination: netip.AddrPortFrom(portAddress, selector),
}
}
func (n *portNAT) selectorRange(protocol uint8) (uint16, uint32) {
if n.selectorCount == 0 ||
protocol == uint8(header.ICMPv4ProtocolNumber) || protocol == uint8(header.ICMPv6ProtocolNumber) {
return natSelectorMin, natSelectorMax - natSelectorMin + 1
}
return n.selectorStart, uint32(n.selectorCount)
}
func (n *portNAT) allocateSelector(protocol uint8, portAddress, serverAddress netip.Addr, serverPort, clientSelector uint16) (uint16, flowKey, bool) {
rangeStart, rangeCount := n.selectorRange(protocol)
if clientSelector != 0 &&
clientSelector >= rangeStart && uint32(clientSelector-rangeStart) < rangeCount {
key := n.reverseKeyFor(protocol, portAddress, serverAddress, serverPort, clientSelector)
if n.lookup(key) == nil {
return clientSelector, key, true
}
}
for range rangeCount {
n.counter++
candidate := rangeStart + uint16(n.counter%rangeCount)
key := n.reverseKeyFor(protocol, portAddress, serverAddress, serverPort, candidate)
if n.lookup(key) == nil {
return candidate, key, true
}
}
return 0, flowKey{}, false
}