默认路由到旁路由
十二月 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
上面两个 ?? 的答案(当时留下的疑问,后来弄清楚了):
not iif lo匹配的是"不是从 lo 进来的"包,即只匹配转发流量(从其他网卡进来的包),本机自己发出的流量(iif lo)不匹配。- 不加
not iif lo时本机流量也会查表 2000 —— 这正是"默认路由到旁路由"想要的行为(本机流量同样走旁路由)。127.0.0.1 不会受影响:pref 0 的 local 表在 5000/5001 之前就先命中了回环地址,根本轮不到查表 2000。 - 加了
not iif lo后本机流量不再匹配 2000,这是正确语义:本机流量会被"漏"到后面的 main 表直连。若希望本机也走旁路由,不要加not iif lo。
路由表本身没有优先级,查找顺序由规则链决定
关键概念:
- 路由表没有优先级。table 的 ID(2000、2022…)只是编号,不决定先后。
- 内核按
ip rule的 pref 从小到大逐条匹配:selector 命中 → 执行动作(lookup 表X→ 在该表内做最长前缀匹配);命中即终止;表内没命中则继续执行下一条规则。 - 默认规则链(IPv4):
0 local→32766 main→32767 default(实际是 unreachable,兜底丢弃)。 - 同一个 pref 允许多条规则(实测 sing-box 生成的 9002/9003 各有两条),同 pref 内按插入顺序比较。
所以"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