refactor: New tun udpnat

This commit is contained in:
世界
2026-07-16 21:35:01 +08:00
parent 64f833d8f7
commit c8ee3497b2
27 changed files with 428 additions and 102 deletions
+2 -2
View File
@@ -38,7 +38,7 @@ type socketDiagConn struct {
}
type uidProcessPathCache struct {
cache freelru.Cache[uint32, *uidProcessPaths]
cache *freelru.Cache[uint32, *uidProcessPaths]
}
type uidProcessPaths struct {
@@ -85,7 +85,7 @@ func socketDiagSettings(network string, source netip.AddrPort) (family, protocol
}
func newUIDProcessPathCache(ttl time.Duration) *uidProcessPathCache {
cache := common.Must1(freelru.NewSharded[uint32, *uidProcessPaths](64, maphash.NewHasher[uint32]().Hash32))
cache := common.Must1(freelru.New[uint32, *uidProcessPaths](64, maphash.NewHasher[uint32]().Hash32, true))
cache.SetLifetime(ttl)
return &uidProcessPathCache{cache: cache}
}
+2 -2
View File
@@ -42,7 +42,7 @@ type Client struct {
dnsCache adapter.DNSCacheStore
initDNSCacheFunc func() adapter.DNSCacheStore
logger logger.ContextLogger
cache freelru.Cache[dnsCacheKey, *dns.Msg]
cache *freelru.Cache[dnsCacheKey, *dns.Msg]
cacheLock compatible.Map[dnsCacheKey, chan struct{}]
backgroundRefresh compatible.Map[dnsCacheKey, struct{}]
}
@@ -104,7 +104,7 @@ func (c *Client) initializeMemoryCache() {
if c.disableCache || c.cache != nil {
return
}
c.cache = common.Must1(freelru.NewSharded[dnsCacheKey, *dns.Msg](c.cacheCapacity, maphash.NewHasher[dnsCacheKey]().Hash32))
c.cache = common.Must1(freelru.New[dnsCacheKey, *dns.Msg](c.cacheCapacity, maphash.NewHasher[dnsCacheKey]().Hash32, true))
}
func extractNegativeTTL(response *dns.Msg) (uint32, bool) {
+2 -2
View File
@@ -42,7 +42,7 @@ type Router struct {
rawRules []option.DNSRule
rules []adapter.DNSRule
defaultDomainStrategy C.DomainStrategy
dnsReverseMapping freelru.Cache[netip.Addr, string]
dnsReverseMapping *freelru.Cache[netip.Addr, string]
platformInterface adapter.PlatformInterface
legacyDNSMode bool
rulesAccess sync.RWMutex
@@ -110,7 +110,7 @@ func NewRouter(ctx context.Context, logFactory log.Factory, options option.DNSOp
Logger: router.logger,
})
if options.ReverseMapping {
router.dnsReverseMapping = common.Must1(freelru.NewSharded[netip.Addr, string](1024, maphash.NewHasher[netip.Addr]().Hash32))
router.dnsReverseMapping = common.Must1(freelru.New[netip.Addr, string](1024, maphash.NewHasher[netip.Addr]().Hash32, true))
}
return router, nil
}
+13 -7
View File
@@ -1,3 +1,9 @@
!!! quote "Changes in sing-box 1.14.0"
:material-plus: [udp_mapping](/configuration/shared/udp-nat/#udp_mapping)
:material-plus: [udp_filtering](/configuration/shared/udp-nat/#udp_filtering)
:material-plus: [udp_nat_max](/configuration/shared/udp-nat/#udp_nat_max)
!!! question "Since sing-box 1.11.0"
### Structure
@@ -24,7 +30,9 @@
"reserved": [0, 0, 0]
}
],
"udp_timeout": "",
... // UDP NAT Fields
"workers": 0,
... // Dial Fields
@@ -112,18 +120,16 @@ Disabled by default.
WireGuard reserved field bytes.
#### udp_timeout
UDP NAT expiration time.
`5m` will be used by default.
#### workers
WireGuard worker count.
CPU count is used by default.
### UDP NAT Fields
See [UDP NAT Fields](/configuration/shared/udp-nat/) for details.
### Dial Fields
See [Dial Fields](/configuration/shared/dial/) for details.
+13 -7
View File
@@ -1,3 +1,9 @@
!!! quote "sing-box 1.14.0 中的更改"
:material-plus: [udp_mapping](/zh/configuration/shared/udp-nat/#udp_mapping)
:material-plus: [udp_filtering](/zh/configuration/shared/udp-nat/#udp_filtering)
:material-plus: [udp_nat_max](/zh/configuration/shared/udp-nat/#udp_nat_max)
!!! question "自 sing-box 1.11.0 起"
### 结构
@@ -24,7 +30,9 @@
"reserved": [0, 0, 0]
}
],
"udp_timeout": "",
... // UDP NAT 字段
"workers": 0,
... // 拨号字段
@@ -114,18 +122,16 @@ WireGuard 对等方的列表。
对等方的保留字段字节。
#### udp_timeout
UDP NAT 过期时间。
默认使用 `5m`
#### workers
WireGuard worker 数量。
默认使用 CPU 数量。
### UDP NAT 字段
参阅 [UDP NAT 字段](/zh/configuration/shared/udp-nat/)。
### 拨号字段
参阅 [拨号字段](/zh/configuration/shared/dial/)。
+13 -1
View File
@@ -1,3 +1,9 @@
!!! quote "Changes in sing-box 1.14.0"
:material-plus: [udp_mapping](/configuration/shared/udp-nat/#udp_mapping)
:material-plus: [udp_filtering](/configuration/shared/udp-nat/#udp_filtering)
:material-plus: [udp_nat_max](/configuration/shared/udp-nat/#udp_nat_max)
!!! quote ""
Only supported on Linux.
@@ -11,7 +17,9 @@
... // Listen Fields
"network": "udp"
"network": "udp",
... // UDP NAT Fields
}
```
@@ -26,3 +34,7 @@ See [Listen Fields](/configuration/shared/listen/) for details.
Listen network, one of `tcp` `udp`.
Both if empty.
### UDP NAT Fields
See [UDP NAT Fields](/configuration/shared/udp-nat/) for details.
+13 -1
View File
@@ -1,3 +1,9 @@
!!! quote "sing-box 1.14.0 中的更改"
:material-plus: [udp_mapping](/zh/configuration/shared/udp-nat/#udp_mapping)
:material-plus: [udp_filtering](/zh/configuration/shared/udp-nat/#udp_filtering)
:material-plus: [udp_nat_max](/zh/configuration/shared/udp-nat/#udp_nat_max)
!!! quote ""
仅支持 Linux。
@@ -11,7 +17,9 @@
... // 监听字段
"network": "udp"
"network": "udp",
... // UDP NAT 字段
}
```
@@ -26,3 +34,7 @@
监听的网络协议,`tcp` `udp` 之一。
默认所有。
### UDP NAT 字段
参阅 [UDP NAT 字段](/zh/configuration/shared/udp-nat/)。
+11 -8
View File
@@ -8,7 +8,10 @@ icon: material/new-box
:material-plus: [exclude_mac_address](#exclude_mac_address)
:material-plus: [dns_mode](#dns_mode)
:material-plus: [dns_address](#dns_address)
:material-plus: [netns](#netns)
:material-plus: [netns](#netns)
:material-plus: [udp_mapping](/configuration/shared/udp-nat/#udp_mapping)
:material-plus: [udp_filtering](/configuration/shared/udp-nat/#udp_filtering)
:material-plus: [udp_nat_max](/configuration/shared/udp-nat/#udp_nat_max)
!!! quote "Changes in sing-box 1.13.3"
@@ -112,7 +115,9 @@ icon: material/new-box
"geoip-cn"
],
"endpoint_independent_nat": false,
"udp_timeout": "5m",
... // UDP NAT Fields
"stack": "system",
"include_interface": [
"lan0"
@@ -553,12 +558,6 @@ Enable endpoint-independent NAT.
Performance may degrade slightly, so it is not recommended to enable on when it is not needed.
#### udp_timeout
UDP NAT expiration time.
`5m` will be used by default.
#### stack
!!! quote "Changes in sing-box 1.8.0"
@@ -700,6 +699,10 @@ Hostnames that bypass the HTTP proxy.
Hostnames that use the HTTP proxy.
### UDP NAT Fields
See [UDP NAT Fields](/configuration/shared/udp-nat/) for details.
### Listen Fields
See [Listen Fields](/configuration/shared/listen/) for details.
+11 -8
View File
@@ -8,7 +8,10 @@ icon: material/new-box
:material-plus: [exclude_mac_address](#exclude_mac_address)
:material-plus: [dns_mode](#dns_mode)
:material-plus: [dns_address](#dns_address)
:material-plus: [netns](#netns)
:material-plus: [netns](#netns)
:material-plus: [udp_mapping](/zh/configuration/shared/udp-nat/#udp_mapping)
:material-plus: [udp_filtering](/zh/configuration/shared/udp-nat/#udp_filtering)
:material-plus: [udp_nat_max](/zh/configuration/shared/udp-nat/#udp_nat_max)
!!! quote "sing-box 1.13.3 中的更改"
@@ -113,7 +116,9 @@ icon: material/new-box
"geoip-cn"
],
"endpoint_independent_nat": false,
"udp_timeout": "5m",
... // UDP NAT 字段
"stack": "system",
"include_interface": [
"lan0"
@@ -533,12 +538,6 @@ sing-box DNS 模块,等价于一条
性能可能会略有下降,所以不建议在不需要的时候开启。
#### udp_timeout
UDP NAT 过期时间。
默认使用 `5m`
#### stack
!!! quote "sing-box 1.8.0 中的更改"
@@ -680,6 +679,10 @@ TCP/IP 栈。
代理的主机名列表。
### UDP NAT 字段
参阅 [UDP NAT 字段](/zh/configuration/shared/udp-nat/)。
### 监听字段
参阅 [监听字段](/zh/configuration/shared/listen/)。
+67
View File
@@ -0,0 +1,67 @@
---
icon: material/new-box
---
!!! quote "Changes in sing-box 1.14.0"
:material-plus: [udp_mapping](#udp_mapping)
:material-plus: [udp_filtering](#udp_filtering)
:material-plus: [udp_nat_max](#udp_nat_max)
### Structure
```json
{
"udp_timeout": "5m",
"udp_mapping": "endpoint_independent",
"udp_filtering": "endpoint_independent",
"udp_nat_max": 0
}
```
### Fields
#### udp_timeout
UDP NAT expiration time.
`5m` will be used by default.
#### udp_mapping
!!! question "Since sing-box 1.14.0"
UDP NAT mapping behavior.
| Value | Behavior |
|------------------------------|-------------------------------------------------------------------------------|
| `endpoint_independent` | Reuse the same mapping for the same source address and port for all destinations. |
| `address_dependent` | Use a separate mapping for each destination address. |
| `address_and_port_dependent` | Use a separate mapping for each destination address and port. |
`endpoint_independent` is used by default.
#### udp_filtering
!!! question "Since sing-box 1.14.0"
UDP NAT filtering behavior.
| Value | Behavior |
|------------------------------|-----------------------------------------------------------------------------|
| `endpoint_independent` | Accept packets from any remote endpoint. |
| `address_dependent` | Accept packets only from remote addresses to which packets have been sent. |
| `address_and_port_dependent` | Accept packets only from remote addresses and ports to which packets have been sent. |
`endpoint_independent` is used by default.
#### udp_nat_max
!!! question "Since sing-box 1.14.0"
Maximum number of UDP NAT sessions.
When the limit is reached, the least recently used session is closed.
When unset or set to `0`, `4096` is used on iOS. On other platforms, a value from `4096` to `16384` is selected based on total memory;
`16384` is used if total memory cannot be detected.
+67
View File
@@ -0,0 +1,67 @@
---
icon: material/new-box
---
!!! quote "sing-box 1.14.0 中的更改"
:material-plus: [udp_mapping](#udp_mapping)
:material-plus: [udp_filtering](#udp_filtering)
:material-plus: [udp_nat_max](#udp_nat_max)
### 结构
```json
{
"udp_timeout": "5m",
"udp_mapping": "endpoint_independent",
"udp_filtering": "endpoint_independent",
"udp_nat_max": 0
}
```
### 字段
#### udp_timeout
UDP NAT 过期时间。
默认使用 `5m`
#### udp_mapping
!!! question "自 sing-box 1.14.0 起"
UDP NAT 映射行为。
| 值 | 行为 |
|------------------------------|----------------------------------------------------------|
| `endpoint_independent` | 对相同的源地址和端口,所有目标复用同一映射。 |
| `address_dependent` | 每个目标地址使用单独的映射。 |
| `address_and_port_dependent` | 每个目标地址和端口使用单独的映射。 |
默认使用 `endpoint_independent`
#### udp_filtering
!!! question "自 sing-box 1.14.0 起"
UDP NAT 过滤行为。
| 值 | 行为 |
|------------------------------|----------------------------------------------------------|
| `endpoint_independent` | 接受来自任意远程端点的数据包。 |
| `address_dependent` | 仅接受来自已向其发送过数据包的远程地址的数据包。 |
| `address_and_port_dependent` | 仅接受来自已向其发送过数据包的远程地址和端口的数据包。 |
默认使用 `endpoint_independent`
#### udp_nat_max
!!! question "自 sing-box 1.14.0 起"
UDP NAT 会话的最大数量。
达到限制时,将关闭最近最少使用的会话。
未设置或设置为 `0` 时,iOS 使用 `4096`。其他平台根据总内存在 `4096``16384` 之间选择;
无法检测总内存时使用 `16384`
+2 -2
View File
@@ -43,7 +43,7 @@ require (
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a
github.com/sagernet/nftables v0.3.0-mod.4
github.com/sagernet/quic-go v0.59.0-sing-box-mod.4
github.com/sagernet/sing v0.8.12-0.20260715103206-ac5f044167e4
github.com/sagernet/sing v0.8.12-0.20260716111929-074fc9988b34
github.com/sagernet/sing-cloudflared v0.1.3-0.20260706062323-d9787e794aa3
github.com/sagernet/sing-mux v0.3.5
github.com/sagernet/sing-quic v0.6.4-0.20260709034545-e23afe1172dc
@@ -51,7 +51,7 @@ require (
github.com/sagernet/sing-shadowsocks2 v0.2.1
github.com/sagernet/sing-shadowtls v0.2.1
github.com/sagernet/sing-snell v0.0.0-20260710094516-a4e97ee24beb
github.com/sagernet/sing-tun v0.8.12-0.20260714150017-c54b06b8a670
github.com/sagernet/sing-tun v0.8.12-0.20260716125804-79ea1ac88855
github.com/sagernet/sing-usbip v0.0.0-20260616101517-efb91521eddb
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1
github.com/sagernet/smux v1.5.50-sing-box-mod.1
+4 -4
View File
@@ -270,8 +270,8 @@ github.com/sagernet/nftables v0.3.0-mod.4 h1:vnOtcDYeSXv2e5RoRuGH0lrpttQFJ8iC4IC
github.com/sagernet/nftables v0.3.0-mod.4/go.mod h1:8kslHG4VvYNihcco+i6uxIX7qbT8A56T0y5q7U44ZaQ=
github.com/sagernet/quic-go v0.59.0-sing-box-mod.4 h1:6qvrUW79S+CrPwWz6cMePXohgjHoKxLo3c+MDhNwc3o=
github.com/sagernet/quic-go v0.59.0-sing-box-mod.4/go.mod h1:OqILvS182CyOol5zNNo6bguvOGgXzV459+chpRaUC+4=
github.com/sagernet/sing v0.8.12-0.20260715103206-ac5f044167e4 h1:/CRUuZdfYnrt/8+IkcgrlOkrkSW6JTh0FesoIe99IXg=
github.com/sagernet/sing v0.8.12-0.20260715103206-ac5f044167e4/go.mod h1:olXxWQNqRW/l2Q6JI3b2Qmz8iQnIFlOeeH8bx6JhgUA=
github.com/sagernet/sing v0.8.12-0.20260716111929-074fc9988b34 h1:rgSs2ttiz8EaubsOt0SkzsqciY0m0PRp3w/fOisPoNo=
github.com/sagernet/sing v0.8.12-0.20260716111929-074fc9988b34/go.mod h1:olXxWQNqRW/l2Q6JI3b2Qmz8iQnIFlOeeH8bx6JhgUA=
github.com/sagernet/sing-cloudflared v0.1.3-0.20260706062323-d9787e794aa3 h1:3y6++yIa8XlDhxPkpR4p+7RUHVY2KTP9CPIGnWmOlO8=
github.com/sagernet/sing-cloudflared v0.1.3-0.20260706062323-d9787e794aa3/go.mod h1:XEqEDYRCAYLaoPjZ1ifVWJg5iWAJHL2gOAXe/PM28Cg=
github.com/sagernet/sing-mux v0.3.5 h1:RHnhVEc+SFqkrK4xMygYjDwwLhzp2Bj3lztSukONfhI=
@@ -286,8 +286,8 @@ github.com/sagernet/sing-shadowtls v0.2.1 h1:ZiHZdnEnP+YS73NMsxiZmIFCwNd0M4k7PkG
github.com/sagernet/sing-shadowtls v0.2.1/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
github.com/sagernet/sing-snell v0.0.0-20260710094516-a4e97ee24beb h1:VvU2/PZqP5tbKTDq0BxkhRO8ZnKI4UJzziakgBiP2Qg=
github.com/sagernet/sing-snell v0.0.0-20260710094516-a4e97ee24beb/go.mod h1:PcwzX/Xvqky0EP3kGt8OCjYb3R1pydenPHNQZcPZmXY=
github.com/sagernet/sing-tun v0.8.12-0.20260714150017-c54b06b8a670 h1:suDpFVVpMTyzdssMNP/8OSgaADlGyTtdbnPm4JwJsS0=
github.com/sagernet/sing-tun v0.8.12-0.20260714150017-c54b06b8a670/go.mod h1:QvarqUtHfj1ULaRR+6kZOS/OoCE+pYGq67A5tyIy+dQ=
github.com/sagernet/sing-tun v0.8.12-0.20260716125804-79ea1ac88855 h1:zP2Bb6NLl3/zV24mmBjcpWIdvOKTiMo2SS0eJ8WpYFU=
github.com/sagernet/sing-tun v0.8.12-0.20260716125804-79ea1ac88855/go.mod h1:SF7OPVtqIYY14T4KmnoHND5CFiMSUCTYxzVOT3nZUuc=
github.com/sagernet/sing-usbip v0.0.0-20260616101517-efb91521eddb h1:KEMbfexD4DvrQGYWwx6r+AwH9Veh8z6cnBZmtCS2G+0=
github.com/sagernet/sing-usbip v0.0.0-20260616101517-efb91521eddb/go.mod h1:D4CnJX3MNAAANhbQUxfIRgBdnvlTEaV7h6ojedcs+pw=
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1 h1:aSwUNYUkVyVvdmBSufR8/nRFonwJeKSIROxHcm5br9o=
+2
View File
@@ -139,6 +139,7 @@ nav:
- Multiplex: configuration/shared/multiplex.md
- V2Ray Transport: configuration/shared/v2ray-transport.md
- UDP over TCP: configuration/shared/udp-over-tcp.md
- UDP NAT Fields: configuration/shared/udp-nat.md
- TCP Brutal: configuration/shared/tcp-brutal.md
- Wi-Fi State: configuration/shared/wifi-state.md
- Neighbor Resolution: configuration/shared/neighbor.md
@@ -302,6 +303,7 @@ plugins:
Multiplex: 多路复用
V2Ray Transport: V2Ray 传输层
Wi-Fi State: Wi-Fi 状态
UDP NAT Fields: UDP NAT 字段
Network Namespace: 网络命名空间
Endpoint: 端点
+42
View File
@@ -87,6 +87,48 @@ type ListenOptions struct {
InboundOptions
}
type UDPNATBehavior uint8
const (
UDPNATBehaviorEndpointIndependent UDPNATBehavior = iota
UDPNATBehaviorAddressDependent
UDPNATBehaviorAddressAndPortDependent
)
func (b UDPNATBehavior) MarshalJSON() ([]byte, error) {
var value string
switch b {
case UDPNATBehaviorEndpointIndependent:
value = "endpoint_independent"
case UDPNATBehaviorAddressDependent:
value = "address_dependent"
case UDPNATBehaviorAddressAndPortDependent:
value = "address_and_port_dependent"
default:
return nil, E.New("unknown UDP NAT behavior: ", uint8(b))
}
return json.Marshal(value)
}
func (b *UDPNATBehavior) UnmarshalJSON(data []byte) error {
var value string
err := json.Unmarshal(data, &value)
if err != nil {
return err
}
switch value {
case "", "endpoint_independent":
*b = UDPNATBehaviorEndpointIndependent
case "address_dependent":
*b = UDPNATBehaviorAddressDependent
case "address_and_port_dependent":
*b = UDPNATBehaviorAddressAndPortDependent
default:
return E.New("unknown UDP NAT behavior: ", value)
}
return nil
}
type UDPTimeoutCompat badoption.Duration
func (c UDPTimeoutCompat) MarshalJSON() ([]byte, error) {
+4 -1
View File
@@ -6,5 +6,8 @@ type RedirectInboundOptions struct {
type TProxyInboundOptions struct {
ListenOptions
Network NetworkList `json:"network,omitempty"`
Network NetworkList `json:"network,omitempty"`
UDPMapping UDPNATBehavior `json:"udp_mapping,omitempty"`
UDPFiltering UDPNATBehavior `json:"udp_filtering,omitempty"`
UDPNATMax uint32 `json:"udp_nat_max,omitempty"`
}
+3
View File
@@ -45,6 +45,9 @@ type TunInboundOptions struct {
IncludeMACAddress badoption.Listable[string] `json:"include_mac_address,omitempty"`
ExcludeMACAddress badoption.Listable[string] `json:"exclude_mac_address,omitempty"`
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
UDPMapping UDPNATBehavior `json:"udp_mapping,omitempty"`
UDPFiltering UDPNATBehavior `json:"udp_filtering,omitempty"`
UDPNATMax uint32 `json:"udp_nat_max,omitempty"`
Stack string `json:"stack,omitempty"`
Platform *TunPlatformOptions `json:"platform,omitempty"`
InboundOptions
+12 -9
View File
@@ -7,15 +7,18 @@ import (
)
type WireGuardEndpointOptions struct {
System bool `json:"system,omitempty"`
Name string `json:"name,omitempty"`
MTU uint32 `json:"mtu,omitempty"`
Address badoption.Listable[netip.Prefix] `json:"address"`
PrivateKey string `json:"private_key"`
ListenPort uint16 `json:"listen_port,omitempty"`
Peers []WireGuardPeer `json:"peers,omitempty"`
UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"`
Workers int `json:"workers,omitempty"`
System bool `json:"system,omitempty"`
Name string `json:"name,omitempty"`
MTU uint32 `json:"mtu,omitempty"`
Address badoption.Listable[netip.Prefix] `json:"address"`
PrivateKey string `json:"private_key"`
ListenPort uint16 `json:"listen_port,omitempty"`
Peers []WireGuardPeer `json:"peers,omitempty"`
UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"`
UDPMapping UDPNATBehavior `json:"udp_mapping,omitempty"`
UDPFiltering UDPNATBehavior `json:"udp_filtering,omitempty"`
UDPNATMax uint32 `json:"udp_nat_max,omitempty"`
Workers int `json:"workers,omitempty"`
DialerOptions
}
+23 -4
View File
@@ -13,12 +13,13 @@ import (
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/control"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/udpnat2"
"github.com/sagernet/sing/service"
)
func RegisterTProxy(registry *inbound.Registry) {
@@ -31,7 +32,7 @@ type TProxy struct {
router adapter.Router
logger log.ContextLogger
listener *listener.Listener
udpNat *udpnat.Service
udpNat *tun.UDPNat
}
func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TProxyInboundOptions) (adapter.Inbound, error) {
@@ -47,7 +48,16 @@ func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLog
} else {
udpTimeout = C.UDPTimeout
}
tproxy.udpNat = udpnat.New(tproxy, tproxy.preparePacketConnection, udpTimeout, false)
networkManager := service.FromContext[adapter.NetworkManager](ctx)
tproxy.udpNat = tun.NewUDPNat(tun.UDPNatOptions{
Handler: tproxy,
Prepare: tproxy.preparePacketConnection,
Timeout: udpTimeout,
Mapping: tun.NATMapping(options.UDPMapping),
Filtering: tun.NATFiltering(options.UDPFiltering),
MaxSize: options.UDPNATMax,
InterfaceFinder: networkManager.InterfaceFinder(),
})
tproxy.listener = listener.New(listener.Options{
Context: ctx,
Logger: logger,
@@ -64,10 +74,19 @@ func (t *TProxy) Start(stage adapter.StartStage) error {
if stage != adapter.StartStateStart {
return nil
}
return t.listener.Start()
err := t.udpNat.Start()
if err != nil {
return err
}
err = t.listener.Start()
if err != nil {
_ = t.udpNat.Close()
}
return err
}
func (t *TProxy) Close() error {
_ = t.udpNat.Close()
return t.listener.Close()
}
+9
View File
@@ -42,6 +42,9 @@ type Inbound struct {
logger log.ContextLogger
tunOptions tun.Options
udpTimeout time.Duration
udpMapping tun.NATMapping
udpFiltering tun.NATFiltering
udpNATMax uint32
dnsHijackAddress []netip.Addr
stack string
tunIf tun.Tun
@@ -230,6 +233,9 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
EXP_MultiPendingPackets: multiPendingPackets,
},
udpTimeout: udpTimeout,
udpMapping: tun.NATMapping(options.UDPMapping),
udpFiltering: tun.NATFiltering(options.UDPFiltering),
udpNATMax: options.UDPNATMax,
stack: options.Stack,
platformInterface: platformInterface,
platformOptions: common.PtrValueOrDefault(options.Platform),
@@ -450,6 +456,9 @@ func (t *Inbound) Start(stage adapter.StartStage) error {
TunOptions: t.tunOptions,
UDPTimeout: t.udpTimeout,
ICMPTimeout: C.ICMPTimeout,
UDPMapping: t.udpMapping,
UDPFiltering: t.udpFiltering,
UDPNATMax: t.udpNATMax,
Handler: t,
Logger: t.logger,
ForwarderBindInterface: C.IsDarwin,
+12 -7
View File
@@ -73,14 +73,19 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
} else {
udpTimeout = C.UDPTimeout
}
networkManager := service.FromContext[adapter.NetworkManager](ctx)
wgEndpoint, err := wireguard.NewEndpoint(wireguard.EndpointOptions{
Context: ctx,
Logger: logger,
System: options.System,
Handler: ep,
UDPTimeout: udpTimeout,
ICMPTimeout: C.ICMPTimeout,
Dialer: outboundDialer,
Context: ctx,
Logger: logger,
System: options.System,
Handler: ep,
UDPTimeout: udpTimeout,
ICMPTimeout: C.ICMPTimeout,
UDPMapping: tun.NATMapping(options.UDPMapping),
UDPFiltering: tun.NATFiltering(options.UDPFiltering),
UDPNATMax: options.UDPNATMax,
InterfaceFinder: networkManager.InterfaceFinder(),
Dialer: outboundDialer,
CreateDialer: func(interfaceName string) N.Dialer {
return common.Must1(dialer.NewDefault(ctx, option.DialerOptions{
BindInterface: interfaceName,
+2 -2
View File
@@ -42,7 +42,7 @@ type Router struct {
ruleSetMap map[string]adapter.RuleSet
ruleSetUpdater *R.RuleSetUpdater
processSearcher process.Searcher
processCache freelru.Cache[processCacheKey, processCacheEntry]
processCache *freelru.Cache[processCacheKey, processCacheEntry]
neighborResolver adapter.NeighborResolver
pauseManager pause.Manager
trackers []adapter.ConnectionTracker
@@ -192,7 +192,7 @@ func (r *Router) Start(stage adapter.StartStage) error {
}
}
if r.processSearcher != nil {
processCache := common.Must1(freelru.NewSharded[processCacheKey, processCacheEntry](256, maphash.NewHasher[processCacheKey]().Hash32))
processCache := common.Must1(freelru.New[processCacheKey, processCacheEntry](256, maphash.NewHasher[processCacheKey]().Hash32, true))
processCache.SetLifetime(200 * time.Millisecond)
r.processCache = processCache
}
+17 -11
View File
@@ -6,6 +6,7 @@ import (
"time"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/control"
"github.com/sagernet/sing/common/logger"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/wireguard-go/device"
@@ -22,17 +23,22 @@ type Device interface {
}
type DeviceOptions struct {
Context context.Context
Logger logger.ContextLogger
System bool
Handler tun.Handler
UDPTimeout time.Duration
ICMPTimeout time.Duration
CreateDialer func(interfaceName string) N.Dialer
Name string
MTU uint32
Address []netip.Prefix
AllowedAddress []netip.Prefix
Context context.Context
Logger logger.ContextLogger
System bool
Handler tun.Handler
UDPTimeout time.Duration
ICMPTimeout time.Duration
UDPMapping tun.NATMapping
UDPFiltering tun.NATFiltering
UDPNATMax uint32
NetworkMonitor tun.NetworkUpdateMonitor
InterfaceFinder control.InterfaceFinder
CreateDialer func(interfaceName string) N.Dialer
Name string
MTU uint32
Address []netip.Prefix
AllowedAddress []netip.Prefix
}
func NewDevice(options DeviceOptions) (Device, error) {
+20 -1
View File
@@ -42,6 +42,7 @@ type stackDevice struct {
inet4Address netip.Addr
inet6Address netip.Addr
icmpForwarder *tun.ICMPForwarder
udpForwarder *tun.UDPForwarder
}
func newStackDevice(options DeviceOptions) (*stackDevice, error) {
@@ -79,7 +80,16 @@ func newStackDevice(options DeviceOptions) (*stackDevice, error) {
tunDevice.stack = ipStack
if options.Handler != nil {
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tun.NewTCPForwarder(options.Context, ipStack, options.Handler).HandlePacket)
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, tun.NewUDPForwarder(options.Context, ipStack, options.Handler, options.UDPTimeout).HandlePacket)
udpForwarder := tun.NewUDPForwarder(options.Context, ipStack, options.Handler, tun.UDPNatOptions{
Timeout: options.UDPTimeout,
Shared: true,
Mapping: options.UDPMapping,
Filtering: options.UDPFiltering,
MaxSize: options.UDPNATMax,
InterfaceFinder: options.InterfaceFinder,
})
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
tunDevice.udpForwarder = udpForwarder
icmpForwarder := tun.NewICMPForwarder(ipStack, options.Handler, options.Logger)
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber4, icmpForwarder.HandlePacket)
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber6, icmpForwarder.HandlePacket)
@@ -166,6 +176,12 @@ func (w *stackDevice) SetDevice(device *device.Device) {
}
func (w *stackDevice) Start() error {
if w.udpForwarder != nil {
err := w.udpForwarder.Start()
if err != nil {
return err
}
}
w.events <- wgTun.EventUp
return nil
}
@@ -245,6 +261,9 @@ func (w *stackDevice) Close() error {
if w.icmpForwarder != nil {
w.icmpForwarder.Close()
}
if w.udpForwarder != nil {
_ = w.udpForwarder.Close()
}
w.stack.Close()
for _, endpoint := range w.stack.CleanupEndpoints() {
endpoint.Abort()
+29 -1
View File
@@ -27,6 +27,7 @@ type systemStackDevice struct {
stack *stack.Stack
endpoint *deviceEndpoint
icmpForwarder *tun.ICMPForwarder
udpForwarder *tun.UDPForwarder
writeBufs [][]byte
closeOnce sync.Once
}
@@ -69,7 +70,17 @@ func newSystemStackDevice(options DeviceOptions) (*systemStackDevice, error) {
}
if options.Handler != nil {
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tun.NewTCPForwarder(options.Context, ipStack, options.Handler).HandlePacket)
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, tun.NewUDPForwarder(options.Context, ipStack, options.Handler, options.UDPTimeout).HandlePacket)
udpForwarder := tun.NewUDPForwarder(options.Context, ipStack, options.Handler, tun.UDPNatOptions{
Timeout: options.UDPTimeout,
Shared: true,
Mapping: options.UDPMapping,
Filtering: options.UDPFiltering,
MaxSize: options.UDPNATMax,
InterfaceFinder: options.InterfaceFinder,
ExcludeInterface: []string{options.Name},
})
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
stackDevice.udpForwarder = udpForwarder
icmpForwarder := tun.NewICMPForwarder(ipStack, options.Handler, options.Logger)
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber4, icmpForwarder.HandlePacket)
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber6, icmpForwarder.HandlePacket)
@@ -82,6 +93,20 @@ func (w *systemStackDevice) SetDevice(device *device.Device) {
w.endpoint.device = device
}
func (w *systemStackDevice) Start() error {
if w.udpForwarder != nil {
err := w.udpForwarder.Start()
if err != nil {
return err
}
}
err := w.systemDevice.Start()
if err != nil && w.udpForwarder != nil {
_ = w.udpForwarder.Close()
}
return err
}
func (w *systemStackDevice) Write(bufs [][]byte, offset int) (count int, err error) {
if w.batchDevice != nil {
w.writeBufs = w.writeBufs[:0]
@@ -118,6 +143,9 @@ func (w *systemStackDevice) Close() error {
if w.icmpForwarder != nil {
w.icmpForwarder.Close()
}
if w.udpForwarder != nil {
_ = w.udpForwarder.Close()
}
w.stack.Close()
for _, endpoint := range w.stack.CleanupEndpoints() {
endpoint.Abort()
+17 -12
View File
@@ -101,17 +101,21 @@ func NewEndpoint(options EndpointOptions) (*Endpoint, error) {
options.MTU = 1408
}
deviceOptions := DeviceOptions{
Context: options.Context,
Logger: options.Logger,
System: options.System,
Handler: options.Handler,
UDPTimeout: options.UDPTimeout,
ICMPTimeout: options.ICMPTimeout,
CreateDialer: options.CreateDialer,
Name: options.Name,
MTU: options.MTU,
Address: options.Address,
AllowedAddress: allowedAddresses,
Context: options.Context,
Logger: options.Logger,
System: options.System,
Handler: options.Handler,
UDPTimeout: options.UDPTimeout,
ICMPTimeout: options.ICMPTimeout,
UDPMapping: options.UDPMapping,
UDPFiltering: options.UDPFiltering,
UDPNATMax: options.UDPNATMax,
InterfaceFinder: options.InterfaceFinder,
CreateDialer: options.CreateDialer,
Name: options.Name,
MTU: options.MTU,
Address: options.Address,
AllowedAddress: allowedAddresses,
}
tunDevice, err := NewDevice(deviceOptions)
if err != nil {
@@ -231,8 +235,9 @@ func (e *Endpoint) Close() error {
e.device.Down()
e.device.Close()
e.device = nil
return nil
}
return nil
return e.tunDevice.Close()
}
func (e *Endpoint) Lookup(address netip.Addr) *device.Peer {
+16 -10
View File
@@ -6,6 +6,7 @@ import (
"time"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/control"
"github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
@@ -18,16 +19,21 @@ type EndpointOptions struct {
Handler tun.Handler
UDPTimeout time.Duration
ICMPTimeout time.Duration
Dialer N.Dialer
CreateDialer func(interfaceName string) N.Dialer
Name string
MTU uint32
Address []netip.Prefix
PrivateKey string
ListenPort uint16
ResolvePeer func(domain string) (netip.Addr, error)
Peers []PeerOptions
Workers int
UDPMapping tun.NATMapping
UDPFiltering tun.NATFiltering
UDPNATMax uint32
InterfaceFinder control.InterfaceFinder
Dialer N.Dialer
CreateDialer func(interfaceName string) N.Dialer
Name string
MTU uint32
Address []netip.Prefix
PrivateKey string
ListenPort uint16
ResolvePeer func(domain string) (netip.Addr, error)
Peers []PeerOptions
Workers int
}
type PeerOptions struct {