Skip to content

路由器与规则

路由器是核心决策引擎。它将连接与规则进行匹配并执行动作。与 Xray-core 中规则仅选择出站标签不同,sing-box 的规则产生动作,可以执行嗅探、DNS 解析、路由、拒绝或 DNS 劫持。

源码: route/router.go, route/route.go, route/rule/

Router 结构体

go
type Router struct {
    ctx               context.Context
    logger            log.ContextLogger
    inbound           adapter.InboundManager
    outbound          adapter.OutboundManager
    dns               adapter.DNSRouter
    dnsTransport      adapter.DNSTransportManager
    connection        adapter.ConnectionManager
    network           adapter.NetworkManager
    rules             []adapter.Rule
    ruleSets          []adapter.RuleSet
    ruleSetMap        map[string]adapter.RuleSet
    processSearcher   process.Searcher
    neighborResolver  adapter.NeighborResolver
    trackers          []adapter.ConnectionTracker
}

连接路由流程

RouteConnectionEx (TCP)

go
func (r *Router) RouteConnectionEx(ctx, conn, metadata, onClose) {
    err := r.routeConnection(ctx, conn, metadata, onClose)
    if err != nil {
        N.CloseOnHandshakeFailure(conn, onClose, err)
    }
}

routeConnection(内部方法)

  1. Detour 检查: 如果设置了 metadata.InboundDetour,则注入到该入站
  2. Mux/UoT 检查: 拒绝已弃用的全局 mux/UoT 地址
  3. 规则匹配: 调用 matchRule() 查找匹配的规则
  4. 动作分派:
    • RuleActionRoute -> 查找出站,验证 TCP 支持
    • RuleActionBypass -> 直接或出站绕过
    • RuleActionReject -> 返回错误
    • RuleActionHijackDNS -> 作为 DNS 流处理
  5. 默认出站: 如果没有规则匹配,使用默认出站
  6. 连接追踪: 用追踪器包装(Clash API 统计)
  7. 移交: 调用 outbound.NewConnectionEx()connectionManager.NewConnection()

规则匹配 (matchRule)

核心匹配循环:

go
func (r *Router) matchRule(ctx, metadata, preMatch, supportBypass, inputConn, inputPacketConn) (
    selectedRule, selectedRuleIndex, buffers, packetBuffers, fatalErr,
) {
    // 步骤 1: 进程发现
    if r.processSearcher != nil && metadata.ProcessInfo == nil {
        processInfo, _ := process.FindProcessInfo(r.processSearcher, ...)
        metadata.ProcessInfo = processInfo
    }

    // 步骤 2: 邻居解析(MAC 地址、主机名)
    if r.neighborResolver != nil && metadata.SourceMACAddress == nil {
        mac, _ := r.neighborResolver.LookupMAC(metadata.Source.Addr)
        hostname, _ := r.neighborResolver.LookupHostname(metadata.Source.Addr)
    }

    // 步骤 3: FakeIP 查询
    if metadata.Destination.Addr.IsValid() && r.dnsTransport.FakeIP() != nil {
        domain, loaded := r.dnsTransport.FakeIP().Store().Lookup(metadata.Destination.Addr)
        if loaded {
            metadata.OriginDestination = metadata.Destination
            metadata.Destination = M.Socksaddr{Fqdn: domain, Port: metadata.Destination.Port}
            metadata.FakeIP = true
        }
    }

    // 步骤 4: 反向 DNS 查询
    if metadata.Domain == "" {
        domain, loaded := r.dns.LookupReverseMapping(metadata.Destination.Addr)
        if loaded { metadata.Domain = domain }
    }

    // 步骤 5: 规则遍历
    for currentRuleIndex, currentRule := range r.rules {
        metadata.ResetRuleCache()
        if !currentRule.Match(metadata) {
            continue
        }

        // 应用规则中的路由选项
        // ...

        // 执行动作
        switch action := currentRule.Action().(type) {
        case *R.RuleActionSniff:
            // 窥探数据,设置 metadata.Protocol/Domain
        case *R.RuleActionResolve:
            // DNS 解析,设置 metadata.DestinationAddresses
        case *R.RuleActionRoute:
            selectedRule = currentRule
            break match
        case *R.RuleActionReject:
            selectedRule = currentRule
            break match
        case *R.RuleActionHijackDNS:
            selectedRule = currentRule
            break match
        case *R.RuleActionBypass:
            selectedRule = currentRule
            break match
        }
    }
}

规则动作

Route(终端动作)

go
type RuleActionRoute struct {
    Outbound string
    RuleActionRouteOptions
}

type RuleActionRouteOptions struct {
    OverrideAddress         M.Socksaddr
    OverridePort            uint16
    NetworkStrategy         *C.NetworkStrategy
    NetworkType             []C.InterfaceType
    FallbackNetworkType     []C.InterfaceType
    FallbackDelay           time.Duration
    UDPDisableDomainUnmapping bool
    UDPConnect              bool
    UDPTimeout              time.Duration
    TLSFragment             bool
    TLSRecordFragment       bool
}

Sniff(非终端动作)

go
type RuleActionSniff struct {
    StreamSniffers []sniff.StreamSniffer
    PacketSniffers []sniff.PacketSniffer
    SnifferNames   []string
    Timeout        time.Duration
    OverrideDestination bool
}

嗅探会窥探连接数据以检测协议和域名。对于 TCP,使用 sniff.PeekStream()。对于 UDP,使用 sniff.PeekPacket()

Resolve(非终端动作)

go
type RuleActionResolve struct {
    Server       string
    Strategy     C.DomainStrategy
    DisableCache bool
    RewriteTTL   *uint32
    ClientSubnet netip.Prefix
}

对目标域名进行 DNS 解析,并将 IP 存储在 metadata.DestinationAddresses 中。

Reject(终端动作)

go
type RuleActionReject struct {
    Method string  // "default", "drop", "reply"
}

HijackDNS(终端动作)

拦截连接并将其作为 DNS 查询处理,转发到 DNS 路由器。

Bypass(终端动作)

go
type RuleActionBypass struct {
    Outbound string
    RuleActionRouteOptions
}

Rule 接口

go
type Rule interface {
    HeadlessRule
    SimpleLifecycle
    Type() string
    Action() RuleAction
}

type HeadlessRule interface {
    Match(metadata *InboundContext) bool
    String() string
}

规则类型

  • DefaultRule: 带有条件 + 动作的标准规则
  • LogicalRule: 子规则的 AND/OR 组合

条件项

每个条件检查元数据的一个方面:

条件字段匹配方式
domain目标域名完整匹配、后缀、关键词、正则
ip_cidr目标 IPCIDR 范围
source_ip_cidr源 IPCIDR 范围
port目标端口精确匹配或范围
source_port源端口精确匹配或范围
protocol嗅探到的协议精确匹配
networkTCP/UDP精确匹配
inbound入站标签精确匹配
outbound当前出站精确匹配
package_nameAndroid 包名精确匹配
process_name进程名称精确匹配
process_path进程路径精确匹配或正则
user / user_id操作系统用户精确匹配
clash_modeClash API 模式精确匹配
wifi_ssid / wifi_bssidWIFI 状态精确匹配
network_type接口类型wifi/cellular/ethernet/other
network_is_expensive计费网络布尔值
network_is_constrained受限网络布尔值
ip_is_private私有 IP布尔值
ip_accept_anyIP 已解析布尔值
source_mac_address源 MAC 地址精确匹配
source_hostname源主机名域名匹配
query_typeDNS 查询类型A/AAAA 等
rule_set规则集匹配委托匹配
auth_user代理认证用户精确匹配
clientTLS 客户端 (JA3)精确匹配

规则集

规则集是从本地文件或远程 URL 加载的规则集合:

go
type RuleSet interface {
    Name() string
    StartContext(ctx, startContext) error
    PostStart() error
    Metadata() RuleSetMetadata
    ExtractIPSet() []*netipx.IPSet
    IncRef() / DecRef()  // 引用计数
    HeadlessRule         // 可用作条件
}

本地规则集

.srs 二进制文件(sing-box 规则集格式)加载。

远程规则集

从 URL 下载,缓存并自动更新。多个规则集并发下载(最多 5 个并行)。

DNS 路由

DNS 查询通过 dns.Router 单独路由:

go
type DNSRule interface {
    Rule
    WithAddressLimit() bool
    MatchAddressLimit(metadata *InboundContext) bool
}

DNS 规则具有额外的能力,可以匹配响应地址(用于过滤不需要的 DNS 响应)。