1. 流量分拣的三级流程

当一个 IP 包准备发出(或进入)时,内核会按照以下顺序进行“三审制”:

第一级:分拣员 (ip rule)

这是最高层。内核会查阅规则列表(从优先级 0 开始往后查)。

  • 它问: “这个包是谁发的?发给谁的?带了什么标记(fwmark)?”
  • 它做: “根据规则,你应该去查 Table X。”
  • 排查重点: ip rule list。看你的包是否命中了一个特定的 lookup 动作。

第二级:地图 (ip route table)

一旦 ip rule 指向了一个 Table(比如 netbirdmain),内核就会进入这个特定的表里查地图。

  • 它问: “这个表里有比 0.0.0.0/0 更具体的路径(比如 192.168.3.0/24)吗?”
  • 它做: * 如果找到了精确匹配:直接发出,流程结束。
    • 如果没找到(或被 suppress 抑制):退回第一级,继续看下一条 ip rule
  • 排查重点: ip route show table <name>

第三级:出口 (Device/Interface)

地图指出了出口(如 dev wt0dev ens33)。

  • 它问: “这个网卡现在状态是对的吗?网关能通吗?”
  • 排查重点: ip addrping <gateway_ip>

2. 故障排查的“标准套路”

当你发现一个 IP(比如 192.168.3.6)走得不对,或者不通时,请按以下步骤“逆向侦破”:

第一步:询问内核的最终判决

不要自己去人肉查表,直接问内核:

ip route get 192.168.3.6

这条命令会直接告诉你:内核打算从哪个 Table 出发、从哪个接口出去、源 IP 是哪个。

  • 如果这里显示的网卡不对,说明路由表或规则有问题。

第二步:追踪路由表来源

如果 ip route get 显示它走了一个你没听过的 Table(比如 table 110),去查它的名字:

cat /etc/iproute2/rt_tables | grep 110
# 然后看这个表的所有内容
ip route show table 110

第三步:确认规则拦截 (Rule Check)

查看 ip rule list,重点寻找那些带 notfwmark 或者 suppress 关键字的规则。

  • 常见坑点: 像 NetBird 这种工具会用 suppress_prefixlength 0 来故意跳过主路由表的默认网关,强制流量滑落到后面的自定义表里。

第四步:物理链路与抓包

如果路由查出来没问题,但就是 Ping 不通:

# 看看包到底从哪个网卡飞出去了
tcpdump -i <网卡名> host 192.168.3.6
  • 如果 tcpdump 有包发出但没回包:对端路由器没写回程路由,或者防火墙拦了。
  • _如果 tcpdump 压根没包:流量在更底层的 iptables/nftables 被拦截了。

3. 核心关系图解

netbird 配置后 ip rule 关系。

ip rule list
0:      from all lookup local
105:    from all lookup main suppress_prefixlength 0
110:    not from all fwmark 0x1bd00 lookup netbird
32766:  from all lookup main
32767:  from all lookup default
graph TD
    Start((数据包产生)) --> R0[<b>ip rule 0</b><br/>lookup local]
    R0 -->|未命中/非本地| R105[<b>ip rule 105</b><br/>lookup main]
    
    subgraph Table_Main [Routing Table: main]
        M1[是否有精确路由?<br/>例如: 192.168.1.0/24]
        M2[是否有默认路由?<br/>0.0.0.0/0]
    end

    R105 --> M1
    M1 -->|是| Exit1([从 ens35/ens33 发出])
    M1 -->|否| M2
    
    M2 -->|命中默认路由| Suppress{是否存在<br/>suppress_prefixlength 0?}
    Suppress -->|是: 抑制并回溯| R110[<b>ip rule 110</b><br/>lookup netbird]
    
    subgraph Table_Netbird [Routing Table: netbird]
        N1[是否有 VPN 路由?<br/>例如: 192.168.3.0/24]
    end

    R110 --> N1
    N1 -->|是| Exit2([封装入 wt0 隧道])
    N1 -->|否| R32766[<b>ip rule 32766</b><br/>lookup main]
    
    R32766 --> M2
    M2 -->|无抑制再次查找| Exit3([从默认网关 192.168.1.1 发出])

    style R105 fill:#f9f,stroke:#333,stroke-width:2px
    style R110 fill:#bbf,stroke:#333,stroke-width:2px
    style Suppress fill:#fff4dd,stroke:#d4a017,stroke-width:2px

总结建议

在你的场景中(NetBird + 多网卡):

  1. 优先用 ip route get <IP> 定位 Table。
  2. ip route show table <TABLE_NAME> 确认路径。
  3. ip rule list 理解流量为什么会被导向那个 Table。