ip rule 配合 fwmark 实现策略路由的设计
来源:17golang原创
时间:2026-11-13 19:00:47 426浏览 收藏
我在一台有两条出口的 Linux 主机上第一次用策略路由时,最容易混在一起的是三个对象:防火墙写入的 fwmark、负责匹配的 ip rule,以及真正保存下一跳的路由表。设计原则其实很简单:先在正确的链上给报文留下标记,再让 RPDB 按标记选择独立表,最后在独立表里只放这条出口需要的路由。
官方地址:https://man7.org/linux/man-pages/man8/ip-rule.8.html
下面用 0x1 表示“走出口 A”,用表 100 表示出口 A 的路由集合。示例地址是文档示意地址,不代表真实网络;重点是对象之间的边界和检查顺序。
先把 mark、ip rule 和路由表分开
ip rule 管理的是路由策略数据库(RPDB)。规则按 priority 从小到大匹配,命中后可以把查找交给某张路由表。fwmark 不是下一跳,也不是接口名称,它只是报文上的一个 32 位标记,供规则选择器读取。
我会先把数据关系画成三层:Netfilter 负责“写标记”,RPDB 负责“选表”,路由表负责“给出出口”。这样排障时不会把“规则命中了”误认为“下一跳一定可用”。

# 给出口 A 单独建立一张路由表;示例网段仅用于说明结构 ip route add table 100 192.0.2.0/24 dev eth1 src 192.0.2.10 ip route add table 100 default via 192.0.2.1 dev eth1 # priority 数字越小越先匹配,掩码只比较 mark 的低 8 位 ip rule add priority 100 fwmark 0x1/0xff table 100 # 查看 RPDB,确认自定义规则是否位于预期位置 ip rule show
这里的关键不是表号 100,而是显式 priority 和标记掩码。系统通常还会有 priority 0 的 local、32766 的 main、32767 的 default 规则;自定义规则若没有合适的优先级,可能看起来存在,却在预期之前或之后被其它规则处理。
用掩码给标记预留可扩展空间
如果只写 fwmark 1,初期很直观,但后面想同时表达出口、业务类型或回程域时容易重构。更稳妥的做法是约定字段:低 8 位存出口编号,高 8 位存业务域,规则只匹配自己负责的位。
# 只让低 8 位决定出口 A,其他业务位可以继续保留 ip rule add priority 100 fwmark 0x01/0xff table 100 # 只让高 8 位决定另一组策略,避免覆盖出口字段 ip rule add priority 110 fwmark 0x0100/0xff00 table 200 # 在 mangle 表的正确链上写入低 8 位;--set-mark 带掩码可避免改动其它位 iptables -t mangle -A OUTPUT -d 203.0.113.0/24 \ -j MARK --set-mark 0x01/0xff
掩码设计的收益是规则职责清楚,代价是必须把位段约定写进运维文档。不要让不同脚本随意复用同一位,否则 ip rule 命中结果会变得难以解释。Netfilter 手册也明确区分了报文 mark 与连接 mark;两者不是同一个字段。
本机流量和转发流量要在不同位置打标
我踩过的一个坑是只在 FORWARD 链上打标,然后用本机发起的连接测试。FORWARD 处理的是转发路径,本机进程产生的流量通常要在 OUTPUT 阶段完成标记;转发报文则常在 PREROUTING 阶段打标,才能影响后续路由查找。
# 本机进程发出的流量:在 OUTPUT 里按目标网段打标 iptables -t mangle -A OUTPUT -d 203.0.113.0/24 \ -j MARK --set-mark 0x01/0xff # 经过本机转发的流量:在 PREROUTING 里按入口接口打标 iptables -t mangle -A PREROUTING -i lan0 -d 203.0.113.0/24 \ -j MARK --set-mark 0x01/0xff # 用计数器观察规则是否真的收到匹配报文 iptables -t mangle -L OUTPUT -v -n iptables -t mangle -L PREROUTING -v -n
这两条链不是可以互换的“习惯写法”。打标点必须早于你要影响的路由决策,而且要与流量类型对应。还要考虑回程:只给去程选择出口,不代表返回包会自动沿同一策略回来;多出口场景通常需要对连接做一致性标记。
用 CONNMARK 让一条连接沿同一条策略
单个报文的 nfmark 会随着包处理而变化,而连接级的 ctmark 可以把第一次判断保存下来,再为后续包恢复。我的经验是把“首次分类”和“恢复已有分类”分开写,避免每个包都重新依据临时条件覆盖标记。

# 先恢复已有连接标记,避免后续报文重新分类 iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark # 新连接第一次命中条件时设置报文标记 iptables -t mangle -A PREROUTING -m conntrack --ctstate NEW \ -i lan0 -d 203.0.113.0/24 -j MARK --set-mark 0x01/0xff # 将本次报文的 nfmark 保存到连接 ctmark,供后续包恢复 iptables -t mangle -A PREROUTING -m mark --mark 0x01/0xff \ -j CONNMARK --save-mark --nfmask 0xff --ctmask 0xff
如果主机既转发又本机发包,还要为 OUTPUT 设计对应的 restore/save 位置;不要默认 PREROUTING 能覆盖本机产生的所有流量。使用 nftables 时也可以表达相同的标记关系,但字段命名和规则语法不同,迁移时要重新确认 hook 和查找时机。
按“规则—标记—路由”三层排查
出现“规则写了但没有走出口 A”时,我不会先改网关,而是逐层缩小范围:
- 先看
ip rule show,确认 priority、fwmark 掩码和 table 编号;规则存在不等于优先级正确。 - 再看 mangle 链的
-v计数,确认目标流量经过了 OUTPUT 或 PREROUTING,而不是只检查 FORWARD。 - 最后用带 mark 的路由查询对比主表查询,确认表 100 里确实有到目标网段的路由。
# 不发送真实报文,只查询指定标记下的路由选择 ip route get 203.0.113.8 mark 0x1 # 对照不带 mark 的默认查询,便于判断是否真的切换了表 ip route get 203.0.113.8 # 检查自定义表中的直连路由和默认路由 ip route show table 100
如果带 mark 和不带 mark 的结果相同,优先检查规则优先级、掩码是否匹配,以及表 100 是否有可用路由;如果规则命中但路由失败,再看网关、接口地址和回程路径。不要把 mark 当作安全边界,它只是路由选择输入,真正的过滤仍应由防火墙规则承担。
我的取舍:什么时候值得用 fwmark
当策略条件能稳定映射到源地址、入口接口、服务类别或连接状态,并且确实需要多张路由表时,fwmark 很灵活。它不适合被当成“所有网络问题的万能开关”:只有一条出口、没有转发、也不需要按业务分流时,普通 main 表通常更容易维护。
我最终会把配置拆成三份:标记规则说明“谁写入什么位”,ip rule 说明“哪些位选择哪张表”,路由表说明“这张表有哪些网段和下一跳”。三份内容能独立检查,变更时也更容易回滚。
常见问题
为什么 ip rule 明明存在却没有生效?
优先检查 priority 是否被更小数字的规则抢先处理,再确认 fwmark 掩码与实际 mark 一致,最后检查目标路由是否真的存在于指定 table。
只给首包设置 mark 够不够?
短链路测试可能看不出问题,但多包连接或回程流量通常需要 CONNMARK 保存和恢复连接级分类,否则后续包可能重新走默认路径。
-
426 收藏
-
387 收藏
-
242 收藏
-
238 收藏
-
402 收藏
-
239 收藏
-
358 收藏
-
289 收藏
-
312 收藏
-
263 收藏
-
464 收藏
-
224 收藏
-
324 收藏
-
388 收藏
-
428 收藏
-
255 收藏
-
441 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习