mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-11 17:47:20 +10:00
Update bypass action behavior for auto redirect
This commit is contained in:
@@ -21,7 +21,7 @@ import (
|
||||
type Router interface {
|
||||
Lifecycle
|
||||
ConnectionRouter
|
||||
PreMatch(metadata InboundContext, context tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error)
|
||||
PreMatch(metadata InboundContext, context tun.DirectRouteContext, timeout time.Duration, supportBypass bool) (tun.DirectRouteDestination, error)
|
||||
ConnectionRouterEx
|
||||
RuleSet(tag string) (RuleSet, bool)
|
||||
Rules() []Rule
|
||||
|
||||
@@ -62,19 +62,19 @@ See `route-options` fields below.
|
||||
}
|
||||
```
|
||||
|
||||
`bypass` routes connection to the specified outbound.
|
||||
`bypass` bypasses sing-box at the kernel level for auto redirect connections in pre-match.
|
||||
|
||||
For tun connections in [pre-match](/configuration/shared/pre-match/),
|
||||
the connection will bypass sing-box and connect directly at the kernel level.
|
||||
|
||||
For non-tun connections and already established connections, the behavior is the same as `route`.
|
||||
For non-auto-redirect connections and already established connections,
|
||||
if `outbound` is specified, the behavior is the same as `route`;
|
||||
otherwise, the rule will be skipped.
|
||||
|
||||
#### outbound
|
||||
|
||||
==Required==
|
||||
|
||||
Tag of target outbound.
|
||||
|
||||
If not specified, the rule only matches in [pre-match](/configuration/shared/pre-match/)
|
||||
from auto redirect, and will be skipped in other contexts.
|
||||
|
||||
#### route-options Fields
|
||||
|
||||
See `route-options` fields below.
|
||||
|
||||
@@ -58,18 +58,16 @@ icon: material/new-box
|
||||
}
|
||||
```
|
||||
|
||||
`bypass` 将连接路由到指定出站。
|
||||
`bypass` 在预匹配中为 auto redirect 连接在内核层面绕过 sing-box。
|
||||
|
||||
对于[预匹配](/configuration/shared/pre-match/)中的 tun 连接,连接将在内核层面绕过 sing-box 直接连接。
|
||||
|
||||
对于非 tun 连接和已建立的连接,行为与 `route` 相同。
|
||||
对于非 auto redirect 连接和已建立的连接,如果指定了 `outbound`,行为与 `route` 相同;否则规则将被跳过。
|
||||
|
||||
#### outbound
|
||||
|
||||
==必填==
|
||||
|
||||
目标出站的标签。
|
||||
|
||||
如果未指定,规则仅在来自 auto redirect 的[预匹配](/configuration/shared/pre-match/)中匹配,在其他场景中将被跳过。
|
||||
|
||||
#### route-options 字段
|
||||
|
||||
参阅下方的 `route-options` 字段。
|
||||
|
||||
@@ -24,10 +24,14 @@ When a rule matches an action that requires an established connection, pre-match
|
||||
|
||||
Reject with TCP RST / ICMP unreachable.
|
||||
|
||||
See [reject](/configuration/route/rule_action/#reject) for details.
|
||||
|
||||
#### route
|
||||
|
||||
Route ICMP connections to the specified outbound for direct reply.
|
||||
|
||||
See [route](/configuration/route/rule_action/#route) for details.
|
||||
|
||||
#### bypass
|
||||
|
||||
!!! question "Since sing-box 1.13.0"
|
||||
@@ -37,3 +41,10 @@ Route ICMP connections to the specified outbound for direct reply.
|
||||
Only supported on Linux with `auto_redirect` enabled.
|
||||
|
||||
Bypass sing-box and connect directly at kernel level.
|
||||
|
||||
If `outbound` is not specified, the rule only matches in pre-match from auto redirect,
|
||||
and will be skipped in other contexts.
|
||||
|
||||
For all other contexts, bypass with `outbound` behaves like `route` action.
|
||||
|
||||
See [bypass](/configuration/route/rule_action/#bypass) for details.
|
||||
|
||||
@@ -22,10 +22,14 @@ icon: material/new-box
|
||||
|
||||
以 TCP RST / ICMP 不可达拒绝。
|
||||
|
||||
详情参阅 [reject](/configuration/route/rule_action/#reject)。
|
||||
|
||||
#### route
|
||||
|
||||
将 ICMP 连接路由到指定出站以直接回复。
|
||||
|
||||
详情参阅 [route](/configuration/route/rule_action/#route)。
|
||||
|
||||
#### bypass
|
||||
|
||||
!!! question "自 sing-box 1.13.0 起"
|
||||
@@ -35,3 +39,9 @@ icon: material/new-box
|
||||
仅支持 Linux,且需要启用 `auto_redirect`。
|
||||
|
||||
在内核层面绕过 sing-box 直接连接。
|
||||
|
||||
如果未指定 `outbound`,规则仅在来自 auto redirect 的预匹配中匹配,在其他场景中将被跳过。
|
||||
|
||||
对于其他所有场景,指定了 `outbound` 的 bypass 行为与 `route` 相同。
|
||||
|
||||
详情参阅 [bypass](/configuration/route/rule_action/#bypass)。
|
||||
|
||||
@@ -93,9 +93,6 @@ func (r *RuleAction) UnmarshalJSON(data []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if r.Action == C.RuleActionTypeBypass && r.BypassOptions.Outbound == "" {
|
||||
return E.New("missing outbound for bypass action")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@ func (t *Endpoint) PrepareConnection(network string, source M.Socksaddr, destina
|
||||
Network: network,
|
||||
Source: source,
|
||||
Destination: destination,
|
||||
}, routeContext, timeout)
|
||||
}, routeContext, timeout, false)
|
||||
if err != nil {
|
||||
switch {
|
||||
case rule.IsBypassed(err):
|
||||
|
||||
@@ -480,7 +480,7 @@ func (t *Inbound) PrepareConnection(network string, source M.Socksaddr, destinat
|
||||
Source: source,
|
||||
Destination: destination,
|
||||
InboundOptions: t.inboundOptions,
|
||||
}, routeContext, timeout)
|
||||
}, routeContext, timeout, false)
|
||||
if err != nil {
|
||||
switch {
|
||||
case rule.IsBypassed(err):
|
||||
@@ -541,7 +541,7 @@ func (t *autoRedirectHandler) PrepareConnection(network string, source M.Socksad
|
||||
Source: source,
|
||||
Destination: destination,
|
||||
InboundOptions: t.inboundOptions,
|
||||
}, routeContext, timeout)
|
||||
}, routeContext, timeout, true)
|
||||
if err != nil {
|
||||
switch {
|
||||
case rule.IsBypassed(err):
|
||||
|
||||
@@ -140,7 +140,7 @@ func (w *Endpoint) PrepareConnection(network string, source M.Socksaddr, destina
|
||||
Network: network,
|
||||
Source: source,
|
||||
Destination: destination,
|
||||
}, routeContext, timeout)
|
||||
}, routeContext, timeout, false)
|
||||
if err != nil {
|
||||
switch {
|
||||
case rule.IsBypassed(err):
|
||||
|
||||
@@ -95,7 +95,7 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
if deadline.NeedAdditionalReadDeadline(conn) {
|
||||
conn = deadline.NewConn(conn)
|
||||
}
|
||||
selectedRule, _, buffers, _, err := r.matchRule(ctx, &metadata, false, conn, nil)
|
||||
selectedRule, _, buffers, _, err := r.matchRule(ctx, &metadata, false, false, conn, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -114,6 +114,9 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
|
||||
return E.New("TCP is not supported by outbound: ", selectedOutbound.Tag())
|
||||
}
|
||||
case *R.RuleActionBypass:
|
||||
if action.Outbound == "" {
|
||||
break
|
||||
}
|
||||
var loaded bool
|
||||
selectedOutbound, loaded = r.outbound.Outbound(action.Outbound)
|
||||
if !loaded {
|
||||
@@ -223,7 +226,7 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
|
||||
conn = deadline.NewPacketConn(bufio.NewNetPacketConn(conn))
|
||||
}*/
|
||||
|
||||
selectedRule, _, _, packetBuffers, err := r.matchRule(ctx, &metadata, false, nil, conn)
|
||||
selectedRule, _, _, packetBuffers, err := r.matchRule(ctx, &metadata, false, false, nil, conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -243,6 +246,9 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
|
||||
return E.New("UDP is not supported by outbound: ", selectedOutbound.Tag())
|
||||
}
|
||||
case *R.RuleActionBypass:
|
||||
if action.Outbound == "" {
|
||||
break
|
||||
}
|
||||
var loaded bool
|
||||
selectedOutbound, loaded = r.outbound.Outbound(action.Outbound)
|
||||
if !loaded {
|
||||
@@ -289,8 +295,8 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Router) PreMatch(metadata adapter.InboundContext, routeContext tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error) {
|
||||
selectedRule, _, _, _, err := r.matchRule(r.ctx, &metadata, true, nil, nil)
|
||||
func (r *Router) PreMatch(metadata adapter.InboundContext, routeContext tun.DirectRouteContext, timeout time.Duration, supportBypass bool) (tun.DirectRouteDestination, error) {
|
||||
selectedRule, _, _, _, err := r.matchRule(r.ctx, &metadata, true, supportBypass, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -310,7 +316,20 @@ func (r *Router) PreMatch(metadata adapter.InboundContext, routeContext tun.Dire
|
||||
}
|
||||
return nil, action.Error(context.Background())
|
||||
case *R.RuleActionBypass:
|
||||
return nil, &R.BypassedError{Cause: tun.ErrBypass}
|
||||
if supportBypass {
|
||||
return nil, &R.BypassedError{Cause: tun.ErrBypass}
|
||||
}
|
||||
if routeContext == nil {
|
||||
return nil, nil
|
||||
}
|
||||
outbound, loaded := r.outbound.Outbound(action.Outbound)
|
||||
if !loaded {
|
||||
return nil, E.New("outbound not found: ", action.Outbound)
|
||||
}
|
||||
if !common.Contains(outbound.Network(), metadata.Network) {
|
||||
return nil, E.New(metadata.Network, " is not supported by outbound: ", action.Outbound)
|
||||
}
|
||||
directRouteOutbound = outbound.(adapter.DirectRouteOutbound)
|
||||
case *R.RuleActionRoute:
|
||||
if routeContext == nil {
|
||||
return nil, nil
|
||||
@@ -388,7 +407,7 @@ func (r *Router) PreMatch(metadata adapter.InboundContext, routeContext tun.Dire
|
||||
}
|
||||
|
||||
func (r *Router) matchRule(
|
||||
ctx context.Context, metadata *adapter.InboundContext, preMatch bool,
|
||||
ctx context.Context, metadata *adapter.InboundContext, preMatch bool, supportBypass bool,
|
||||
inputConn net.Conn, inputPacketConn N.PacketConn,
|
||||
) (
|
||||
selectedRule adapter.Rule, selectedRuleIndex int,
|
||||
@@ -591,8 +610,16 @@ match:
|
||||
actionType := currentRule.Action().Type()
|
||||
if actionType == C.RuleActionTypeRoute ||
|
||||
actionType == C.RuleActionTypeReject ||
|
||||
actionType == C.RuleActionTypeHijackDNS ||
|
||||
actionType == C.RuleActionTypeBypass {
|
||||
actionType == C.RuleActionTypeHijackDNS {
|
||||
selectedRule = currentRule
|
||||
selectedRuleIndex = currentRuleIndex
|
||||
break match
|
||||
}
|
||||
if actionType == C.RuleActionTypeBypass {
|
||||
bypassAction := currentRule.Action().(*R.RuleActionBypass)
|
||||
if !supportBypass && bypassAction.Outbound == "" {
|
||||
continue match
|
||||
}
|
||||
selectedRule = currentRule
|
||||
selectedRuleIndex = currentRuleIndex
|
||||
break match
|
||||
|
||||
@@ -183,6 +183,9 @@ func (r *RuleActionBypass) Type() string {
|
||||
}
|
||||
|
||||
func (r *RuleActionBypass) String() string {
|
||||
if r.Outbound == "" {
|
||||
return "bypass()"
|
||||
}
|
||||
var descriptions []string
|
||||
descriptions = append(descriptions, r.Outbound)
|
||||
descriptions = append(descriptions, r.Descriptions()...)
|
||||
|
||||
Reference in New Issue
Block a user