ip rule 配合 fwmark 实现策略路由的设计
我在一台有两条出口的 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 保存和恢复连接级分类,否则后续包可能重新走默认路径。
Linux fanotify 与 inotify 怎样选择文件监控方案
- 上一篇
- Linux fanotify 与 inotify 怎样选择文件监控方案
- 下一篇
- tar 保留 xattr 与 ACL 的归档迁移清单
-
- 文章 · linux | 25分钟前 |
- journalctl --after-cursor 从指定位置继续读取日志
- 358浏览 收藏
-
- 文章 · linux | 26分钟前 | Linux · 服务管理 · systemd Linux服务 Restart=on-failure SuccessExitStatus
- systemd.service SuccessExitStatus 自定义正常退出码的配置方法
- 289浏览 收藏
-
- 文章 · linux | 1天前 |
- Linux getconf 怎样查看系统支持的路径参数
- 464浏览 收藏
-
- 文章 · linux | 1天前 | Linux · Linux 文件监听 inotify IN_Q_OVERFLOW 状态重建
- inotify 出现 IN_Q_OVERFLOW 后怎么重建文件状态
- 224浏览 收藏
-
- 文章 · linux | 1天前 | Linux · overlayfs upperdir whiteout opaque directory lowerdir 容器层 OCI Image Spec
- OverlayFS 白化文件怎样影响容器层
- 324浏览 收藏
-
- 文章 · linux | 1天前 |
- Linux getfacl -n 怎样显示数字 UID 和 GID
- 388浏览 收藏
-
- 文章 · linux | 1天前 |
- AppArmor profile 怎样区分 enforce 与 complain
- 428浏览 收藏
-
- 文章 · linux | 1天前 | Linux · Linux 内存映射 procfs /proc/PID/maps 进程排查
- Linux procfs 怎样查看进程内存映射
- 441浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 1015次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 1019次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 1144次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 1119次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 960次使用
-
- 详解golang执行Linuxshell命令完整场景下的使用方法
- 2023-01-07 426浏览
-
- go程序部署到linux上运行的实现方法
- 2023-01-02 387浏览
-
- Linux系统下Go语言开发环境搭建
- 2023-01-07 242浏览
-
- 使用golang获取linux上文件的访问/创建/修改时间
- 2022-12-31 238浏览
-
- 在Linux系统中安装Go语言的详细教程
- 2022-12-29 402浏览

