路由规则读的是包标记,而不是 conntrack
策略路由读的是包标记;conntrack 存的是另一个值,寿命更长。两者之间的复制是必须有人写的一步。
四项检查把一条可用的标记规则和一条可用的路由区分开来:规则读的是哪个标记,包在查找时是否还带着它,连接标记是否被恢复到后续包上,以及匹配规则的掩码与优先级是否允许它触发。计数器增加只能证明某条规则匹配了某个东西。它完全不能说明内核选路时包上带着什么。
两个标记住在不同的地方,按不同的时间表过期。
两个标记,两种寿命
包标记属于一个 skb,随它穿过各个钩子,并由策略路由的 fwmark 选择器消费。连接标记属于 conntrack 表项,在该流的所有包上持续存在,在有人把它复制回来之前,路由规则看不到它。把包标记保存进连接是一次存储;恢复它是另一个动作。两者都不会自动发生。
这就是为什么一套规则看起来自洽,路由决策却看到零:值被保存了,但没有任何东西把它恢复过来。
两者之间的复制是你自己写的一步
两条关键语句方向相反:一条把包的标记存进连接,另一条把连接的标记装载到包上。你需要哪一条,取决于分类相对于流发生的时机。设计可能只需要包标记,而因为某个绕行做法两者都用过就顺手加上 conntrack 标记,正是一条简单策略获得第二种失效模式的方式。
ct mark set meta mark meta mark set ct mark
把方向写在规则旁边,因为只看语法并不明显。第一行是存储,第二行是恢复,两者互不包含。
在消费它的那次查找之前完成分类
本地生成的流量和从 LAN 转发来的流量经过不同的钩子,并在不同时刻到达路由决策。对路由器自身连接有效的输出规则,并不能证明被转发客户端的包在查找之前已经被标记。把分类放在消费它的查找尚未发生的位置,并检查同一链中后面的规则是否会覆盖该值。链内顺序是配置的一部分,不是实现细节。
连接状态的持久化同样需要明确计划。如果只对流的第一个包做了分类,后续包就需要恢复保存的标记,否则它们会走未标记的路径。这就是描述连接的标记与描述包的标记之间的差别。
向路由提出一个带标记的问题
ip route get 10.77.0.10 mark 0x2
先检查规则和表,再带着标记查询路由,如果源地址或入接口会影响策略,也一并加上。普通的无标记查找回答的是另一个问题,并且会欣然报告你并不想要的那条路径。除了规则的值,还要检查它的掩码和优先级,并确认所选表里有一条可用的路由。
计数器增加是某条规则匹配了某个包的证据。它不是最终包在正确的决策点上带着预期标记的证据。
读标记、方向、时机和掩码。四项检查,路由就不再是谜。