默认路由到旁路由

十二月 05, 2025 更新: 九月 12, 2026 [网络与安全] #旁路由 #策略路由 #ip rule #路由表

直接修改默认路由

# 目标机器修改默认路由
sudo ip route replace default via 172.17.66.222 dev enp125s0f3

# 旁路由开启路由转发
sysctl -w net.ipv4.ip_forward=1
sysctl -w 'net.ipv4.ip_forward = 1'
sudo iptables -P FORWARD ACCPET

这种方式简单直接,缺点是没有按源/目的分流的灵活性。如果只想"覆盖"原默认路由而不想换表,也可以保留原路由、只调低新路由的 metric(数值小的优先):

ip route replace default via 10.0.0.1 dev eth0 metric 10

配置策略路由

# echo "2000 tor" >> /etc/iproute2/rt_tables     # 添加了这个映射后,table可以使用tor名称指定
ip route add default via 192.168.10.10 table 2000 

# 匹配所有流量到main表,但是不包括前缀是0的路由(即不匹配main中的默认路由)
sudo ip rule add priority 5000 from all table main suppress_prefixlength 0  
# 如果不加 not iif lo,本机产生的流量(如 curl localhost)也会查表 2000,可能导致无法访问 127.0.0.1??
# 测试添加了not iif lo 本机器产生的所有流量都不匹配这个流量了??
sudo ip rule add priority 5001 from all lookup 2000

上面两个 ?? 的答案(当时留下的疑问,后来弄清楚了):

路由表本身没有优先级,查找顺序由规则链决定

关键概念:

所以"main 表里的默认路由能匹配所有流量"是真的,但它只有当规则链走到 lookup main 时才会被查询。5001 的 pref(5000/5001)比 32766 小,在它之前就把流量带去了表 2000,main 的默认路由根本没机会被查。

高优先级默认路由会吞掉 main 的内网路由

坑:表 2000 里的 default 匹配所有流量(包括内网网段),而 main 表里那些更具体的路由(比如 192.168.4.0/22)只有在规则链走到 lookup main 时才有机会 —— 只要 5001 先命中,内网路由就永远轮不到,内网会断。

规则链的先后必须按"范围从精确到宽泛"排,两种做法:

做法 1:内网流量先查 main,其余才走自定义表

ip rule add priority 5000 from all table main suppress_prefixlength 0  # 内网直连兜底
ip rule add priority 5001 from all lookup 2000                         # 其余流量走旁路由 default

5000 只匹配 main 里的具体路由(suppress_prefixlength 0 屏蔽了 main 的默认路由),内网包在此命中即结束,不进代理;漏网的流量才落到 5001,进表 2000 的 default。

做法 2:把内网路由也复制进自定义表

ip route add 192.168.4.0/22 dev eth0 table 2000
ip route add default via 192.168.10.10 table 2000

表内查找是"最长前缀优先",具体网段永远比 0.0.0.0/0 先命中。推荐做法 1:规则链即决策树,语义清晰,不用维护两份路由。

实测:sing-box tun 就是同一套机制

本机 sing-box tun 模式(auto_route)生成的规则链:

0:    from all lookup local
5210: from all fwmark 0x80000/0xff0000 lookup main      # 打标记的包放行直连(防回流)
5270: from all lookup 52                                 # tailscale 直连,绕过代理
9000: from all to 172.18.0.0/30 lookup 2022              # tun 网段本身
9001: from all lookup 2022 suppress_prefixlength 0       # 表 2022 的具体路由兜底
9002: not from all dport 53 lookup main suppress_prefixlength 0  # 非 DNS 先看 main 具体路由(内网直连)
9002: from all iif singbox_tun goto 9010                 # 从 tun 回来的包跳过劫持,防环路
9003: not from all iif lo lookup 2022                    # 非本机流量全量进 2022
9003: from 0.0.0.0 iif lo lookup 2022                    # 本机未绑定地址的流量进代理
9003: from 172.18.0.0/30 iif lo lookup 2022              # 绑 tun 地址的流量进代理
32766: from all lookup main

table 2022 里就一条 default via 172.18.0.2 dev singbox_tun —— 这就是 sing-box 的"高优先级默认路由":靠 9002/9003 等规则把流量先引到 tun 网卡,优先级高于 32766 的 main。fwmark 0x80000 是 sing-box 给自己出站/回注的包打的标记(用 nftables),让它们直接走 main 直连、避免再被灌回 tun 造成环路。更完整的规则逐条解读见《sing-box tun配置》(2025/11/sing-box tun配置)。

勘误(2026-09-12):上面把整条规则链都当作 sing-box 生成、并把 fwmark 0x80000 说成 sing-box 的标记——归属弄错了。实测(全新 VM 只装 tailscale、无 sing-box):5210/5230/5250: from all fwmark 0x80000/0xff0000 三条规则由 tailscaled 安装(tailscale 给自己出站的 WireGuard 包打 0x80000 防回流),5270: from all lookup 52 也是 tailscale 的;sing-box 生成的只有 9000–9003 那几条(其自用 mark 走 default_mark 配置,源码未硬编码 0x80000)。原文规则链实为 tailscale + sing-box 两者叠加。

排查命令

ip rule show                                   # 看规则链顺序
ip route show table 2000                       # 看指定表的路由
ip route get 1.2.3.4                           # 模拟选路:直接告诉你命中了哪条规则、查了哪个表、走了哪条路由
ip route get 1.2.3.4 dport 53                  # 模拟 DNS 包(53 端口)走哪
ip route get 1.2.3.4 iif eth0                  # 模拟从 eth0 进来的转发包走哪
ip route get 1.2.3.4 mark 0x80000              # 模拟带 fwmark 标记的包走哪
ip monitor rule route                          # 实时观察规则/路由变化

配置dns

sudo tee -a /etc/systemd/resolved.conf<<EOF
DNS=223.5.5.5 8.8.8.8 8.8.4.4
FallbackDNS=1.1.1.1 1.0.0.1
# DNSStubListener=no
EOF

sudo systemctl restart systemd-resolved.service