策略路由核心是分层设计:先按流量特征分类,再依优先级分发至专用路由表,最后确保回程对称;需定义多表、配置from/to规则、用fwmark实现应用级控制,并通过验证与持久化落地。

用 ip rule 构建复杂策略路由,核心不是堆规则,而是分层设计:先明确流量特征(谁、从哪来、去哪、带什么标记),再按优先级把它们“分发”到专用路由表,最后在每张表里配好路径。关键在于规则不打架、表不空转、回程对称。
一、按源IP网段做基础分流,隔离不同业务区域
这是最常用也最稳妥的起点。比如内网办公段(192.168.10.0/24)走主线路,研发测试段(172.16.20.0/24)走专线,访客Wi-Fi(10.0.100.0/24)走独立宽带:
- 先在
/etc/iproute2/rt_tables中定义三张表:100 table_office200 table_dev300 table_guest - 为每张表加直连路由和默认网关:
ip route add 192.168.10.0/24 dev eth0 src 192.168.10.1 table table_officeip route add default via 192.168.10.1 dev eth0 table table_office
(其余两表同理,对应各自网卡和网关) - 添加三条
from规则,显式指定 priority 避免冲突:ip rule add from 192.168.10.0/24 table table_office priority 100ip rule add from 172.16.20.0/24 table table_dev priority 101ip rule add from 10.0.100.0/24 table table_guest priority 102
二、用 iptables + fwmark 实现应用级细粒度隔离
当需要按进程、端口或协议区分流量时,仅靠 IP 不够。例如让所有 Docker 容器流量走专线,或让 SSH 管理流量强制走低延迟链路:
- 先用 iptables 在 OUTPUT 链打标记(以容器网络为例):
iptables -t mangle -A OUTPUT -s 172.17.0.0/16 -j MARK --set-mark 0x100 - 新增一条基于 mark 的 rule:
ip rule add fwmark 0x100 table table_dev priority 99(priority 设得比 from 规则更高,确保标记优先匹配) - 确认
table_dev中有对应路由,且包含该容器网段的反向直连条目:ip route add 172.17.0.0/16 dev docker0 scope link table table_dev
三、保证双向路径一致,避免连接失败
只配出向(from)是半套方案。若服务端响应包仍走 main 表返回,中间设备会因路径不对称而丢包。必须补全回程控制:
- 对提供服务的 IP 段,加
to规则指向同一张表:ip rule add to 192.168.10.0/24 table table_office priority 105ip rule add to 172.16.20.0/24 table table_dev priority 106 - 更稳妥的做法是在每张表中都加入本机所在子网的直连路由(scope link),避免依赖 main 表回包:
ip route add 192.168.10.0/24 dev eth0 scope link table table_office - 检查并调低反向路径校验(rp_filter),否则内核可能主动丢弃非对称包:
echo 2 > /proc/sys/net/ipv4/conf/all/rp_filter
(或写入/etc/sysctl.conf持久化)
四、验证与持久化,让配置真正落地
别只信命令执行成功,要验证实际路径;也别只写进 /etc/rc.local,它常因网络未就绪而失效:
- 查规则是否生效:
ip rule show看 priority 和 table 是否匹配预期 - 查某源 IP 的真实选路:
ip route get from 172.16.20.50 to 8.8.8.8 - 抓包确认接口走向:
tcpdump -i eth1 host 8.8.8.8 - Debian/Ubuntu 推荐在
/etc/network/interfaces的 iface 段加up指令;RHEL/CentOS 8+ 用/etc/sysconfig/network-scripts/rule-ethX文件;通用方案是写 systemd service,依赖network-online.target并加入重试逻辑



















