diff --git a/constant/rule.go b/constant/rule.go index 71441450..b565a39d 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -40,4 +40,5 @@ const ( const ( RuleActionRejectMethodDefault = "default" RuleActionRejectMethodDrop = "drop" + RuleActionRejectMethodReply = "reply" ) diff --git a/docs/configuration/route/rule_action.md b/docs/configuration/route/rule_action.md index c975af2b..a57de60f 100644 --- a/docs/configuration/route/rule_action.md +++ b/docs/configuration/route/rule_action.md @@ -2,6 +2,10 @@ icon: material/new-box --- +!!! quote "Changes in sing-box 1.13.0" + + :material-alert: [reject](#reject) + !!! quote "Changes in sing-box 1.12.0" :material-plus: [tls_fragment](#tls_fragment) @@ -42,6 +46,10 @@ See `route-options` fields below. ### reject +!!! quote "Changes in sing-box 1.13.0" + + Since sing-box 1.13.0, you can reject (or directly reply to) ICMP echo (ping) requests using `reject` action. + ```json { "action": "reject", @@ -58,9 +66,17 @@ For non-tun connections and already established connections, will just be closed #### method +For TCP and UDP connections: + - `default`: Reply with TCP RST for TCP connections, and ICMP port unreachable for UDP packets. - `drop`: Drop packets. +For ICMP echo requests: + +- `default`: Reply with ICMP host unreachable. +- `drop`: Drop packets. +- `reply`: Reply with ICMP echo reply. + #### no_drop If not enabled, `method` will be temporarily overwritten to `drop` after 50 triggers in 30s. diff --git a/docs/configuration/route/rule_action.zh.md b/docs/configuration/route/rule_action.zh.md index 0081e827..98ea1227 100644 --- a/docs/configuration/route/rule_action.zh.md +++ b/docs/configuration/route/rule_action.zh.md @@ -2,6 +2,10 @@ icon: material/new-box --- +!!! quote "sing-box 1.13.0 中的更改" + + :material-alert: [reject](#reject) + !!! quote "sing-box 1.12.0 中的更改" :material-plus: [tls_fragment](#tls_fragment) @@ -38,6 +42,10 @@ icon: material/new-box ### reject +!!! quote "sing-box 1.13.0 中的更改" + + 自 sing-box 1.13.0 起,您可以通过 `reject` 动作拒绝(或直接回复)ICMP 回显(ping)请求。 + ```json { "action": "reject", @@ -54,9 +62,17 @@ icon: material/new-box #### method +对于 TCP 和 UDP 连接: + - `default`: 对于 TCP 连接回复 RST,对于 UDP 包回复 ICMP 端口不可达。 - `drop`: 丢弃数据包。 +对于 ICMP 回显请求: + +- `default`: 回复 ICMP 主机不可达。 +- `drop`: 丢弃数据包。 +- `reply`: 回复以 ICMP 回显应答。 + #### no_drop 如果未启用,则 30 秒内触发 50 次后,`method` 将被暂时覆盖为 `drop`。 diff --git a/go.mod b/go.mod index 38f1fcb4..e4e3196c 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( github.com/sagernet/sing-shadowsocks v0.2.8 github.com/sagernet/sing-shadowsocks2 v0.2.1 github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 - github.com/sagernet/sing-tun v0.7.0-beta.1.0.20250826163040-4c43f4af12bf + github.com/sagernet/sing-tun v0.7.0-beta.1.0.20250827122908-b76e852f59b0 github.com/sagernet/sing-vmess v0.2.7 github.com/sagernet/smux v1.5.34-mod.2 github.com/sagernet/tailscale v1.80.3-mod.6 diff --git a/go.sum b/go.sum index 8c6984a5..9b50d11d 100644 --- a/go.sum +++ b/go.sum @@ -179,8 +179,8 @@ github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnq github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ= github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w= github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA= -github.com/sagernet/sing-tun v0.7.0-beta.1.0.20250826163040-4c43f4af12bf h1:KpV65GHomgqrhyKGbXFFQZZ5ZegGJKO/tdlY6K+QIgQ= -github.com/sagernet/sing-tun v0.7.0-beta.1.0.20250826163040-4c43f4af12bf/go.mod h1:LokZYuEV3crByjQc/XRohLgfNvybtXdx5qe/I4W6S7k= +github.com/sagernet/sing-tun v0.7.0-beta.1.0.20250827122908-b76e852f59b0 h1:Usid4HU1TKrtao2fv/wyubdOkBHpbHdwgU9KUzWXQMM= +github.com/sagernet/sing-tun v0.7.0-beta.1.0.20250827122908-b76e852f59b0/go.mod h1:LokZYuEV3crByjQc/XRohLgfNvybtXdx5qe/I4W6S7k= github.com/sagernet/sing-vmess v0.2.7 h1:2ee+9kO0xW5P4mfe6TYVWf9VtY8k1JhNysBqsiYj0sk= github.com/sagernet/sing-vmess v0.2.7/go.mod h1:5aYoOtYksAyS0NXDm0qKeTYW1yoE1bJVcv+XLcVoyJs= github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4= diff --git a/option/rule_action.go b/option/rule_action.go index 914edb84..e28b58eb 100644 --- a/option/rule_action.go +++ b/option/rule_action.go @@ -282,6 +282,7 @@ func (r *RejectActionOptions) UnmarshalJSON(bytes []byte) error { case "", C.RuleActionRejectMethodDefault: r.Method = C.RuleActionRejectMethodDefault case C.RuleActionRejectMethodDrop: + case C.RuleActionRejectMethodReply: default: return E.New("unknown reject method: " + r.Method) } diff --git a/route/route.go b/route/route.go index 9bca7304..a4f8701a 100644 --- a/route/route.go +++ b/route/route.go @@ -113,6 +113,9 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad } case *R.RuleActionReject: buf.ReleaseMulti(buffers) + if action.Method == C.RuleActionRejectMethodReply { + return E.New("reject method `reply` is not supported for TCP connections") + } return action.Error(ctx) case *R.RuleActionHijackDNS: for _, buffer := range buffers { @@ -228,6 +231,9 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m } case *R.RuleActionReject: N.ReleaseMultiPacketBuffer(packetBuffers) + if action.Method == C.RuleActionRejectMethodReply { + return E.New("reject method `reply` is not supported for UDP connections") + } return action.Error(ctx) case *R.RuleActionHijackDNS: return r.hijackDNSPacket(ctx, conn, packetBuffers, metadata, onClose) @@ -267,6 +273,16 @@ func (r *Router) PreMatch(metadata adapter.InboundContext, routeContext tun.Dire if selectedRule != nil { switch action := selectedRule.Action().(type) { case *R.RuleActionReject: + switch metadata.Network { + case N.NetworkTCP: + if action.Method == C.RuleActionRejectMethodReply { + return nil, E.New("reject method `reply` is not supported for TCP connections") + } + case N.NetworkUDP: + if action.Method == C.RuleActionRejectMethodReply { + return nil, E.New("reject method `reply` is not supported for UDP connections") + } + } return nil, action.Error(context.Background()) case *R.RuleActionRoute: if routeContext == nil { diff --git a/route/rule/rule_action.go b/route/rule/rule_action.go index 1d552718..75ae6de4 100644 --- a/route/rule/rule_action.go +++ b/route/rule/rule_action.go @@ -327,6 +327,8 @@ func (r *RuleActionReject) Error(ctx context.Context) error { returnErr = &RejectedError{tun.ErrReset} case C.RuleActionRejectMethodDrop: return &RejectedError{tun.ErrDrop} + case C.RuleActionRejectMethodReply: + return nil default: panic(F.ToString("unknown reject method: ", r.Method)) }