diff --git a/docs/configuration/inbound/tun.md b/docs/configuration/inbound/tun.md index 1d493d58..f8a9df3b 100644 --- a/docs/configuration/inbound/tun.md +++ b/docs/configuration/inbound/tun.md @@ -29,6 +29,7 @@ "fc00::/7" ], "endpoint_independent_nat": false, + "udp_timeout": "5m", "stack": "system", "include_interface": [ "lan0" diff --git a/docs/configuration/inbound/tun.zh.md b/docs/configuration/inbound/tun.zh.md index 7ea3a6a0..d0bef4ff 100644 --- a/docs/configuration/inbound/tun.zh.md +++ b/docs/configuration/inbound/tun.zh.md @@ -29,6 +29,7 @@ "fc00::/7" ], "endpoint_independent_nat": false, + "udp_timeout": "5m", "stack": "system", "include_interface": [ "lan0" diff --git a/docs/configuration/shared/listen.md b/docs/configuration/shared/listen.md index c1b1ed33..ae3ed6a4 100644 --- a/docs/configuration/shared/listen.md +++ b/docs/configuration/shared/listen.md @@ -7,7 +7,7 @@ "tcp_fast_open": false, "tcp_multi_path": false, "udp_fragment": false, - "udp_timeout": 300, + "udp_timeout": "5m", "detour": "another-in", "sniff": false, "sniff_override_destination": false, @@ -19,14 +19,14 @@ ### Fields -| Field | Available Context | -|--------------------------------|-------------------------------------------------------------------| -| `listen` | Needs to listen on TCP or UDP. | -| `listen_port` | Needs to listen on TCP or UDP. | -| `tcp_fast_open` | Needs to listen on TCP. | -| `tcp_multi_path` | Needs to listen on TCP. | -| `udp_timeout` | Needs to assemble UDP connections, currently Tun and Shadowsocks. | -| `udp_disable_domain_unmapping` | Needs to listen on UDP and accept domain UDP addresses. | +| Field | Available Context | +|--------------------------------|---------------------------------------------------------| +| `listen` | Needs to listen on TCP or UDP. | +| `listen_port` | Needs to listen on TCP or UDP. | +| `tcp_fast_open` | Needs to listen on TCP. | +| `tcp_multi_path` | Needs to listen on TCP. | +| `udp_timeout` | Needs to assemble UDP connections. | +| `udp_disable_domain_unmapping` | Needs to listen on UDP and accept domain UDP addresses. | #### listen @@ -56,7 +56,9 @@ Enable UDP fragmentation. #### udp_timeout -UDP NAT expiration time in seconds, default is 300 (5 minutes). +UDP NAT expiration time in seconds. + +`5m` is used by default. #### detour diff --git a/docs/configuration/shared/listen.zh.md b/docs/configuration/shared/listen.zh.md index b7fd7487..398c98c5 100644 --- a/docs/configuration/shared/listen.zh.md +++ b/docs/configuration/shared/listen.zh.md @@ -7,7 +7,7 @@ "tcp_fast_open": false, "tcp_multi_path": false, "udp_fragment": false, - "udp_timeout": 300, + "udp_timeout": "5m", "detour": "another-in", "sniff": false, "sniff_override_destination": false, @@ -18,13 +18,13 @@ ``` -| 字段 | 可用上下文 | -|-----------------------------------|-------------------------------------| -| `listen` | 需要监听 TCP 或 UDP。 | -| `listen_port` | 需要监听 TCP 或 UDP。 | -| `tcp_fast_open` | 需要监听 TCP。 | -| `tcp_multi_path` | 需要监听 TCP。 | -| `udp_timeout` | 需要组装 UDP 连接, 当前为 Tun 和 Shadowsocks。 | +| 字段 | 可用上下文 | +|------------------|-----------------| +| `listen` | 需要监听 TCP 或 UDP。 | +| `listen_port` | 需要监听 TCP 或 UDP。 | +| `tcp_fast_open` | 需要监听 TCP。 | +| `tcp_multi_path` | 需要监听 TCP。 | +| `udp_timeout` | 需要组装 UDP 连接。 | | ### 字段 @@ -57,7 +57,9 @@ #### udp_timeout -UDP NAT 过期时间,以秒为单位,默认为 300(5 分钟)。 +UDP NAT 过期时间,以秒为单位。 + +默认使用 `5m`。 #### detour diff --git a/inbound/direct.go b/inbound/direct.go index 08d0726a..7079a9f2 100644 --- a/inbound/direct.go +++ b/inbound/direct.go @@ -4,6 +4,7 @@ import ( "context" "net" "net/netip" + "time" "github.com/sagernet/sing-box/adapter" C "github.com/sagernet/sing-box/constant" @@ -47,13 +48,13 @@ func NewDirect(ctx context.Context, router adapter.Router, logger log.ContextLog inbound.overrideOption = 3 inbound.overrideDestination = M.Socksaddr{Port: options.OverridePort} } - var udpTimeout int64 + var udpTimeout time.Duration if options.UDPTimeout != 0 { - udpTimeout = options.UDPTimeout + udpTimeout = time.Duration(options.UDPTimeout) } else { - udpTimeout = int64(C.UDPTimeout.Seconds()) + udpTimeout = C.UDPTimeout } - inbound.udpNat = udpnat.New[netip.AddrPort](udpTimeout, adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound)) + inbound.udpNat = udpnat.New[netip.AddrPort](int64(udpTimeout.Seconds()), adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound)) inbound.connHandler = inbound inbound.packetHandler = inbound inbound.packetUpstream = inbound.udpNat diff --git a/inbound/shadowsocks.go b/inbound/shadowsocks.go index a45b6daf..ca15b8d8 100644 --- a/inbound/shadowsocks.go +++ b/inbound/shadowsocks.go @@ -4,6 +4,7 @@ import ( "context" "net" "os" + "time" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/mux" @@ -65,19 +66,19 @@ func newShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte return nil, err } - var udpTimeout int64 + var udpTimeout time.Duration if options.UDPTimeout != 0 { - udpTimeout = options.UDPTimeout + udpTimeout = time.Duration(options.UDPTimeout) } else { - udpTimeout = int64(C.UDPTimeout.Seconds()) + udpTimeout = C.UDPTimeout } switch { case options.Method == shadowsocks.MethodNone: - inbound.service = shadowsocks.NewNoneService(options.UDPTimeout, inbound.upstreamContextHandler()) + inbound.service = shadowsocks.NewNoneService(int64(udpTimeout.Seconds()), inbound.upstreamContextHandler()) case common.Contains(shadowaead.List, options.Method): - inbound.service, err = shadowaead.NewService(options.Method, nil, options.Password, udpTimeout, inbound.upstreamContextHandler()) + inbound.service, err = shadowaead.NewService(options.Method, nil, options.Password, int64(udpTimeout.Seconds()), inbound.upstreamContextHandler()) case common.Contains(shadowaead_2022.List, options.Method): - inbound.service, err = shadowaead_2022.NewServiceWithPassword(options.Method, options.Password, udpTimeout, inbound.upstreamContextHandler(), ntp.TimeFuncFromContext(ctx)) + inbound.service, err = shadowaead_2022.NewServiceWithPassword(options.Method, options.Password, int64(udpTimeout.Seconds()), inbound.upstreamContextHandler(), ntp.TimeFuncFromContext(ctx)) default: err = E.New("unsupported method: ", options.Method) } diff --git a/inbound/shadowsocks_multi.go b/inbound/shadowsocks_multi.go index c3c7d2ab..a291af4a 100644 --- a/inbound/shadowsocks_multi.go +++ b/inbound/shadowsocks_multi.go @@ -4,6 +4,7 @@ import ( "context" "net" "os" + "time" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/mux" @@ -53,25 +54,25 @@ func newShadowsocksMulti(ctx context.Context, router adapter.Router, logger log. if err != nil { return nil, err } - var udpTimeout int64 + var udpTimeout time.Duration if options.UDPTimeout != 0 { - udpTimeout = options.UDPTimeout + udpTimeout = time.Duration(options.UDPTimeout) } else { - udpTimeout = int64(C.UDPTimeout.Seconds()) + udpTimeout = C.UDPTimeout } var service shadowsocks.MultiService[int] if common.Contains(shadowaead_2022.List, options.Method) { service, err = shadowaead_2022.NewMultiServiceWithPassword[int]( options.Method, options.Password, - udpTimeout, + int64(udpTimeout.Seconds()), adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound), ntp.TimeFuncFromContext(ctx), ) } else if common.Contains(shadowaead.List, options.Method) { service, err = shadowaead.NewMultiService[int]( options.Method, - udpTimeout, + int64(udpTimeout.Seconds()), adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound)) } else { return nil, E.New("unsupported method: " + options.Method) diff --git a/inbound/shadowsocks_relay.go b/inbound/shadowsocks_relay.go index 44f39c9f..fbc2838a 100644 --- a/inbound/shadowsocks_relay.go +++ b/inbound/shadowsocks_relay.go @@ -4,6 +4,7 @@ import ( "context" "net" "os" + "time" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/mux" @@ -50,16 +51,16 @@ func newShadowsocksRelay(ctx context.Context, router adapter.Router, logger log. if err != nil { return nil, err } - var udpTimeout int64 + var udpTimeout time.Duration if options.UDPTimeout != 0 { - udpTimeout = options.UDPTimeout + udpTimeout = time.Duration(options.UDPTimeout) } else { - udpTimeout = int64(C.UDPTimeout.Seconds()) + udpTimeout = C.UDPTimeout } service, err := shadowaead_2022.NewRelayServiceWithPassword[int]( options.Method, options.Password, - udpTimeout, + int64(udpTimeout.Seconds()), adapter.NewUpstreamContextHandler(inbound.newConnection, inbound.newPacketConnection, inbound), ) if err != nil { diff --git a/inbound/tproxy.go b/inbound/tproxy.go index be421ad3..a074eb49 100644 --- a/inbound/tproxy.go +++ b/inbound/tproxy.go @@ -5,6 +5,7 @@ import ( "net" "net/netip" "syscall" + "time" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/redir" @@ -37,15 +38,15 @@ func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLog listenOptions: options.ListenOptions, }, } - var udpTimeout int64 + var udpTimeout time.Duration if options.UDPTimeout != 0 { - udpTimeout = options.UDPTimeout + udpTimeout = time.Duration(options.UDPTimeout) } else { - udpTimeout = int64(C.UDPTimeout.Seconds()) + udpTimeout = C.UDPTimeout } tproxy.connHandler = tproxy tproxy.oobPacketHandler = tproxy - tproxy.udpNat = udpnat.New[netip.AddrPort](udpTimeout, tproxy.upstreamContextHandler()) + tproxy.udpNat = udpnat.New[netip.AddrPort](int64(udpTimeout.Seconds()), tproxy.upstreamContextHandler()) tproxy.packetUpstream = tproxy.udpNat return tproxy } diff --git a/inbound/tun.go b/inbound/tun.go index 7d1f5199..9582ab3f 100644 --- a/inbound/tun.go +++ b/inbound/tun.go @@ -5,6 +5,7 @@ import ( "net" "strconv" "strings" + "time" "github.com/sagernet/sing-box/adapter" C "github.com/sagernet/sing-box/constant" @@ -42,11 +43,11 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger if tunMTU == 0 { tunMTU = 9000 } - var udpTimeout int64 + var udpTimeout time.Duration if options.UDPTimeout != 0 { - udpTimeout = options.UDPTimeout + udpTimeout = time.Duration(options.UDPTimeout) } else { - udpTimeout = int64(C.UDPTimeout.Seconds()) + udpTimeout = C.UDPTimeout } includeUID := uidToRange(options.IncludeUID) if len(options.IncludeUIDRange) > 0 { @@ -92,7 +93,7 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger TableIndex: 2022, }, endpointIndependentNat: options.EndpointIndependentNat, - udpTimeout: udpTimeout, + udpTimeout: int64(udpTimeout.Seconds()), stack: options.Stack, platformInterface: platformInterface, platformOptions: common.PtrValueOrDefault(options.Platform), diff --git a/option/inbound.go b/option/inbound.go index c61428ad..dd099ff9 100644 --- a/option/inbound.go +++ b/option/inbound.go @@ -1,6 +1,8 @@ package option import ( + "time" + "github.com/sagernet/sing-box/common/json" C "github.com/sagernet/sing-box/constant" E "github.com/sagernet/sing/common/exceptions" @@ -128,15 +130,27 @@ type InboundOptions struct { } type ListenOptions struct { - Listen *ListenAddress `json:"listen,omitempty"` - ListenPort uint16 `json:"listen_port,omitempty"` - TCPFastOpen bool `json:"tcp_fast_open,omitempty"` - TCPMultiPath bool `json:"tcp_multi_path,omitempty"` - UDPFragment *bool `json:"udp_fragment,omitempty"` - UDPFragmentDefault bool `json:"-"` - UDPTimeout int64 `json:"udp_timeout,omitempty"` - ProxyProtocol bool `json:"proxy_protocol,omitempty"` - ProxyProtocolAcceptNoHeader bool `json:"proxy_protocol_accept_no_header,omitempty"` - Detour string `json:"detour,omitempty"` + Listen *ListenAddress `json:"listen,omitempty"` + ListenPort uint16 `json:"listen_port,omitempty"` + TCPFastOpen bool `json:"tcp_fast_open,omitempty"` + TCPMultiPath bool `json:"tcp_multi_path,omitempty"` + UDPFragment *bool `json:"udp_fragment,omitempty"` + UDPFragmentDefault bool `json:"-"` + UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"` + ProxyProtocol bool `json:"proxy_protocol,omitempty"` + ProxyProtocolAcceptNoHeader bool `json:"proxy_protocol_accept_no_header,omitempty"` + Detour string `json:"detour,omitempty"` InboundOptions } + +type UDPTimeoutCompat Duration + +func (u *UDPTimeoutCompat) UnmarshalJSON(data []byte) error { + var valueNumber int64 + err := json.Unmarshal(data, &valueNumber) + if err == nil { + *u = UDPTimeoutCompat(time.Second * time.Duration(valueNumber)) + return nil + } + return json.Unmarshal(data, (*Duration)(u)) +} diff --git a/option/tun.go b/option/tun.go index 306d4541..c6b9219d 100644 --- a/option/tun.go +++ b/option/tun.go @@ -23,7 +23,7 @@ type TunInboundOptions struct { IncludePackage Listable[string] `json:"include_package,omitempty"` ExcludePackage Listable[string] `json:"exclude_package,omitempty"` EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"` - UDPTimeout int64 `json:"udp_timeout,omitempty"` + UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"` Stack string `json:"stack,omitempty"` Platform *TunPlatformOptions `json:"platform,omitempty"` InboundOptions