是否可以在任意 TCP 回复数据包中强制 fwmark 反射?

是否可以在任意 TCP 回复数据包中强制 fwmark 反射?

有人解决了 TCP 套接字连接回复中 fwmarks 丢失的问题吗?

背景:

我需要确保回复在收到请求的同一接口上发出,并且需要支持无法通过任何其他方式(例如基于源的路由)区分的网络出站接口;所以这个答案让我开始研究基于策略的路由。起初,似乎不适合我的情况根本不(使用 tcpdump 来观察接口上的流量,并指示 iptables 记录双向的数据包元数据),此时,我看到传入的数据包确实根据传入接口进行了标记,正如我所希望的那样。但传出的数据包没有保留相同的标记!我将测试用例从套接字连接简化为简单的 ping(ICMP echo),当我意识到 fwmark 丢失时,我做了更多研究并发现“fwmark_reflect”sysctl 参数。启用它,它确实解决了我的简化测试用例的问题,但当然,由于该 sysctl 仅涉及“内核产生的 IP 数据包,没有与之关联的用户套接字”,因此这对解决原始问题没有帮助:建立套接字连接,例如 ssh...

更多支持细节:

iptables 数据包标记规则:

-A 预路由 -i ens3 -j 标记 --set-mark 1 -A 预路由 -i ens4 -j MARK --set-mark 2

路由规则(有些是重复的,因为我正在尝试任何东西,出于绝望): #ip 规则列表 0:来自所有查找本地 0:来自所有 fwmark 0x1 查找 mgmt 0:来自所有 fwmark 0x2 查找 cust 10:来自所有 fwmark 0x1 查找mgmt 20:来自所有 fwmark 0x2 查找 cust 32766:来自所有查找 main 32767:来自所有查找 default 65536:来自所有 fwmark 0x2 oif ens4 查找 cust

#ip route list table mgmt
  default via 10.100.16.1 dev ens3 
#ip route list table cust

默认通过 10.100.16.1 dev ens4 ip 路由获取 221.194.47.243 mark 2 221.194.47.243 通过 10.100.16.1 dev ens4 src 10.100.16.13 mark 2

Iptables 的回显请求和回复的日志输出(完整往返已完成;远程确实反映成功接收):

[27314.322482] pIng IN=ens4 OUT= MAC=00:00:17:01:8c:ff:00:00:17:01:cf:b5:08:00 SRC=10.100.21.143 DST=10.100.16.13 LEN= 84 TOS=0x00 PREC=0x00 TTL=64 ID=18269 DF PROTO=ICMP 类型=8 代码=0 ID=14889 SEQ=1 标记=0x2

[27314.323446] pOng IN= OUT=ens4 SRC=10.100.16.13 DST=10.100.21.143 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=39688 PROTO=ICMP TYPE=0 CODE=0 ID=14889 SEQ=1 MARK =0x2

尝试与端口 22 进行套接字连接时类似记录的元数据(远程报告连接失败,因为当然原始 fwmark 丢失,路由表尝试将其发送到错误的接口):

[23305.165235] ens4-PRERT IN=恩斯4OUT= MAC=00:00:17:01:8c:ff:00:00:17:23:a9:ae:08:00 SRC=156.151.8.14 DST=10.100.16.13 LEN=60 TOS=0x10 PREC=0x20 TTL=41 ID=45069 DF PROTO=TCP SPT=5874 DPT=22 WINDOW=14600 RES=0x00 SYN URGP=0 MARK=0x2

[23305.169993] 输出 IN= OUT=恩斯3SRC=10.100.16.13 DST=156.151.8.14 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=0 DF PROTO=TCP SPT=22 DPT=5874 WINDOW=26844 RES=0x00 ACK SYN URGP=0

对于这篇文章的长度表示歉意。我已经在这个问题上绞尽脑汁有一段时间了,无法想出一种更简洁的方式来传达所有相关细节。非常感谢任何人能够提供的任何指导,即使是“这是不可能的”。干杯!

答案1

根据我的评论,我认为这是一个应该按照您的预期工作的解决方案。请注意,顺序与示例中的顺序相反Netfilter康马克链接,但由于您的决定是根据传入数据包而不是传出数据包做出的,因此这是正确的顺序。因此,删除之前的两条 MARK 规则(我将它们放回此处)并替换为:

# iptables -t mangle -A INPUT -i ens3 -j MARK --set-mark 1
# iptables -t mangle -A INPUT -i ens4 -j MARK --set-mark 2    
# iptables -t mangle -A INPUT -j CONNMARK --save-mark

# iptables -t mangle -A OUTPUT -j CONNMARK --restore-mark

为了对称和清晰起见,我使用 INPUT(在 PREROUTING 之后调用,但仅适用于本地流):这是针对本地连接,而不是转发。使用 PREROUTING 而不是 INPUT(但保留 OUTPUT)会得到相同的结果。

这使用跟踪所有流的 conntrack 来记住标记,并将其放回到本地生成的传出数据包中,因为 conntrack 知道它是同一流的一部分。

在 mangle 表中使用 OUTPUT 规则会触发重新路由检查(请参阅此Linux路由和netfilter原理图),这就是根据标记走线所需要的。

如果您使用其他-t mangle规则或者服务器也在转发,则必须更改规则。无论如何,它--save-mark可能应该在 INPUT 中最后一个,并且--restore-mark应该在 OUTPUT 中第一个。

该标记甚至是为非用户回复数据包(如 TCP RST)设置的,因此fwmark_reflect可能不再需要或可能会干扰,您应该进行测试。

相关内容