@@ -277,4 +277,57 @@ default via 198.51.100.1 dev eth1
277277这对于具有多张网卡或接入了多个网络的服务器尤为重要,否则可能会导致客户端无法与服务器建立连接。
278278
279279该方法的一个局限性是,它只能为本机运行的服务(进程)提供「源进源出」的能力,无法满足通过本机转发的流量(如经过 NAT 的转发)的需求。
280- 这种需求需要结合使用防火墙对数据包的 mark、连接跟踪器(conntrack)的 connmark 标记和路由规则的 ` fwmark ` 匹配条件综合实现。
280+ 这种需求需要结合使用数据包的防火墙标记(mark)、[ 连接跟踪器(conntrack)] ( firewall.md#conntrack ) 的 [ connmark 标记] ( firewall.md#conntrack-mark ) 和路由规则的 ` fwmark ` 匹配条件综合实现。
281+
282+ ### 源进源出 + 转发 {#source-based-routing-with-forwarding}
283+
284+ 本需求的一个典型场景是,路由器具有多个上游(WAN 接口)或接入了多个运营商,同时对 LAN 侧的主机提供 NAT 转发上网功能。
285+
286+ 本节内容需要结合防火墙规则和 conntrack 的知识,建议先对[ 防火墙] ( firewall.md ) 章节中的相关内容有初步了解。
287+
288+ 使用基于源地址的路由规则只能为本机发出的数据包实现「源进源出」,而无法满足通过本机转发的流量的需求。
289+ 这是因为前文的方式依赖于这项假设:建立连接的数据包的目的地址必然是对应接口上的地址,那么对于回包来说,源地址自然也是对应接口上的地址,那么基于源地址决定从哪个接口发出就能保证「源进源出」。
290+ 但对于转发的数据包来说,回包的源地址不再对应到接口上了,因此源地址匹配的方式就不顶用了。
291+
292+ 此时,我们可以结合使用防火墙标记(fwmark)和 conntrack 标记(connmark)来实现「源进源出」的需求。
293+ 具体做法分为几个部分:
294+
295+ - 在初次确定上游接口的时候,为数据包打上对应的防火墙标记(mark),并将该标记保存到 CT 中(connmark)。
296+ - 若一个待转发的数据包(PREROUTING)匹配某条连接,那么就从 CT 中将对应的 connmark 还原到数据包上作为 fwmark。
297+ - 在路由规则中使用 ` fwmark ` 条件匹配对应的防火墙标记,进入对应的路由表,从而实现「源进源出」。
298+
299+ 作为例子,本文假设路由器有两个 WAN 接口 ` eth0 ` 和 ` eth1 ` ,分别指定防火墙标记为 100 和 101,那么路由规则可以这样写:
300+
301+ ``` shell title="ip rule"
302+ 0: from all lookup local
303+ 1: from all lookup main suppress_prefixlength 0
304+ 3: from all fwmark 100 lookup eth0
305+ 3: from all fwmark 101 lookup eth1
306+ 32766: from all lookup main
307+ 32767: from all lookup default
308+ ```
309+
310+ 然后在防火墙规则中配置打标和还原标记的规则:
311+
312+ ``` shell title="iptables"
313+ * mangle
314+ :PREROUTING ACCEPT [0:0]
315+ :INPUT ACCEPT [0:0]
316+ :FORWARD ACCEPT [0:0]
317+ :OUTPUT ACCEPT [0:0]
318+ :POSTROUTING ACCEPT [0:0]
319+ # 若数据包属于已建立连接,恢复 connmark 到 fwmark
320+ -A PREROUTING -j CONNMARK --restore-mark
321+ -A PREROUTING -m mark ! --mark 0 -j RETURN
322+
323+ # 初次确定上游接口,打标并保存到 conntrack
324+ -A PREROUTING -i eth0 -j MARK --set-mark 100
325+ -A PREROUTING -i eth1 -j MARK --set-mark 101
326+ -A PREROUTING -j CONNMARK --save-mark
327+ COMMIT
328+ ```
329+
330+ 前两条规则会尝试从 CT 中恢复 connmark 到 fwmark,如果恢复成功,那么数据包的标记就不再为零,可以直接 RETURN,避免重复打标。
331+ 若标记仍为零,则表示这是一个新连接的数据包,需要根据输入接口打上对应的标记,并保存到 CT 中以备后续使用。
332+
333+ 注意到,尽管打标的规则仅使用了 ` -i ` (输入接口)条件,对于从 LAN 侧向 WAN 侧发起的连接来说,在收到第一个回包时,输入接口自然就是对应的 WAN 接口,因此打标是正确的。
0 commit comments