当前位置:首页 > 文章列表 > 文章 > linux > ip rule 配合 fwmark 实现策略路由的设计

ip rule 配合 fwmark 实现策略路由的设计

来源:17golang原创 2026-11-13 19:00:47 0浏览 收藏

我在一台有两条出口的 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 负责“选表”,路由表负责“给出出口”。这样排障时不会把“规则命中了”误认为“下一跳一定可用”。

说明图:报文标记、RPDB 规则与独立路由表之间的静态关系
图1:静态结构说明图,查看 Netfilter 写入 nfmark、ip rule 读取 fwmark,以及表 100 返回出口路由的关系;这不是截图或运行证据。
# 给出口 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 可以把第一次判断保存下来,再为后续包恢复。我的经验是把“首次分类”和“恢复已有分类”分开写,避免每个包都重新依据临时条件覆盖标记。

说明图:报文 nfmark、连接 ctmark、fwmark 规则和出口路由表的静态关系
图2:连接标记关系说明图,查看 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”时,我不会先改网关,而是逐层缩小范围:

  1. 先看 ip rule show,确认 priority、fwmark 掩码和 table 编号;规则存在不等于优先级正确。
  2. 再看 mangle 链的 -v 计数,确认目标流量经过了 OUTPUT 或 PREROUTING,而不是只检查 FORWARD。
  3. 最后用带 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 保存和恢复连接级分类,否则后续包可能重新走默认路径。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Linux fanotify 与 inotify 怎样选择文件监控方案Linux fanotify 与 inotify 怎样选择文件监控方案
上一篇
Linux fanotify 与 inotify 怎样选择文件监控方案
tar 保留 xattr 与 ACL 的归档迁移清单
下一篇
tar 保留 xattr 与 ACL 的归档迁移清单
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    1015次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    1019次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    1144次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    1119次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    960次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码