为什么我们的防火墙(Ubuntu 8.04)拒绝带有 RST 的最终数据包(FIN、ACK、PSH)

ern*_*lli 20 firewall iptables ubuntu-8.04

背景,很长一段时间以来,我们的防火墙都存在问题,有时会导致 HTTP 请求挂起部分加载,直到 TCP 超时。

在跟踪防火墙上的流量后,我注意到它仅在某些时间条件下发生,例如,在客户端在有效负载上发送第二个 ACK​​ 之前,网络服务器已经发送了整个响应。[SYN, SYN/ACK, ACK] 已交换,REQUEST 已发送并确认,第一个 RESPONSE 数据包已收到并确认,然后网络服务器一次性发送其余的响应正文(8 个数据包)包括最后一个 FIN,PSH)并且在客户端确认其中任何一个之前,防火墙拒绝向 Web 服务器发送 RST,并使客户端无限挂起。

这是来自防火墙两侧的数据包的整个wireshark跟踪。192.168.126.161 是客户端的私有 NAT'et IP 地址。172.16.1.2是网络服务器IP(不显示真实公网IP),10.1.1.1是防火墙外部IP(不显示真实公网IP)

2105 0.086275 192.168.126.161  172.16.1.2       TCP 37854 > http [SYN] Seq=0 Win=5840 Len=0 MSS=1460 SACK_PERM=1 TSV=89375083 TSER=0
2106 0.000066 10.1.1.1         172.16.1.2       TCP 37854 > http [SYN] Seq=0 Win=5840 Len=0 MSS=1460 SACK_PERM=1 TSV=89375083 TSER=0
2107 0.002643 172.16.1.2       10.1.1.1         TCP http > 37854 [SYN, ACK] Seq=0 Ack=1 Win=32768 Len=0 MSS=1460
2108 0.007705 172.16.1.2       192.168.126.161  TCP http > 37854 [SYN, ACK] Seq=0 Ack=1 Win=32768 Len=0 MSS=1460
2109 0.006301 192.168.126.161  172.16.1.2       TCP 37854 > http [ACK] Seq=1 Ack=1 Win=5840 Len=0
2110 0.000025 10.1.1.1         172.16.1.2       TCP 37854 > http [ACK] Seq=1 Ack=1 Win=5840 Len=0
2111 0.000007 192.168.126.161  172.16.1.2       HTTP GET /test/style.css HTTP/1.1 
2112 0.000015 10.1.1.1         172.16.1.2       HTTP GET /test/style.css HTTP/1.1 
2113 0.001536 172.16.1.2       10.1.1.1         TCP http > 37854 [ACK] Seq=1 Ack=111 Win=32658 Len=0
2114 0.000014 172.16.1.2       192.168.126.161  TCP http > 37854 [ACK] Seq=1 Ack=111 Win=32658 Len=0
2115 0.002274 172.16.1.2       10.1.1.1         HTTP HTTP/1.1 200 OK  (text/css)
2116 0.000025 172.16.1.2       192.168.126.161  HTTP HTTP/1.1 200 OK  (text/css)
2117 0.005689 192.168.126.161  172.16.1.2       TCP 37854 > http [ACK] Seq=111 Ack=1461 Win=8760 Len=0
2118 0.000024 10.1.1.1         172.16.1.2       TCP 37854 > http [ACK] Seq=111 Ack=1461 Win=8760 Len=0
2119 0.001536 172.16.1.2       10.1.1.1         HTTP Continuation or non-HTTP traffic
2120 0.000026 172.16.1.2       192.168.126.161  HTTP Continuation or non-HTTP traffic
2121 0.000007 172.16.1.2       10.1.1.1         HTTP Continuation or non-HTTP traffic
2122 0.000023 172.16.1.2       192.168.126.161  HTTP Continuation or non-HTTP traffic
2123 0.000313 172.16.1.2       10.1.1.1         HTTP Continuation or non-HTTP traffic
2124 0.000030 172.16.1.2       192.168.126.161  HTTP Continuation or non-HTTP traffic
2125 0.000007 172.16.1.2       10.1.1.1         HTTP Continuation or non-HTTP traffic
2126 0.000023 172.16.1.2       192.168.126.161  HTTP Continuation or non-HTTP traffic
2127 0.000009 172.16.1.2       10.1.1.1         HTTP Continuation or non-HTTP traffic
2128 0.000023 172.16.1.2       192.168.126.161  HTTP Continuation or non-HTTP traffic
2129 0.001108 172.16.1.2       10.1.1.1         HTTP Continuation or non-HTTP traffic
2130 0.000035 172.16.1.2       192.168.126.161  HTTP Continuation or non-HTTP traffic
2131 0.000008 172.16.1.2       10.1.1.1         HTTP Continuation or non-HTTP traffic
2132 0.000022 172.16.1.2       192.168.126.161  HTTP Continuation or non-HTTP traffic
2133 0.000007 172.16.1.2       10.1.1.1         HTTP Continuation or non-HTTP traffic
REJECT-->
2134 0.000089 10.1.1.1         172.16.1.2       TCP 37854 > http [RST] Seq=111 Win=0 Len=0
CLIENT FIRST ACK-->
2135 0.002421 192.168.126.161  172.16.1.2       TCP 37854 > http [ACK] Seq=111 Ack=2921 Win=11680 Len=0
2136 0.000033 10.1.1.1         172.16.1.2       TCP 37854 > http [ACK] Seq=111 Ack=2921 Win=11680 Len=0
2137 0.000007 192.168.126.161  172.16.1.2       TCP 37854 > http [ACK] Seq=111 Ack=4381 Win=14600 Len=0
2138 0.000014 10.1.1.1         172.16.1.2       TCP 37854 > http [ACK] Seq=111 Ack=4381 Win=14600 Len=0
2139 0.000008 192.168.126.161  172.16.1.2       TCP 37854 > http [ACK] Seq=111 Ack=5841 Win=17520 Len=0
2140 0.000014 10.1.1.1         172.16.1.2       TCP 37854 > http [ACK] Seq=111 Ack=5841 Win=17520 Len=0
2141 0.000007 192.168.126.161  172.16.1.2       TCP 37854 > http [ACK] Seq=111 Ack=7301 Win=20440 Len=0
2142 0.000013 10.1.1.1         172.16.1.2       TCP 37854 > http [ACK] Seq=111 Ack=7301 Win=20440 Len=0
2143 0.000007 192.168.126.161  172.16.1.2       TCP 37854 > http [ACK] Seq=111 Ack=8761 Win=23360 Len=0
2144 0.000015 10.1.1.1         172.16.1.2       TCP 37854 > http [ACK] Seq=111 Ack=8761 Win=23360 Len=0
2145 0.000007 192.168.126.161  172.16.1.2       TCP 37854 > http [ACK] Seq=111 Ack=10221 Win=26280 Len=0
2146 0.000013 10.1.1.1         172.16.1.2       TCP 37854 > http [ACK] Seq=111 Ack=10221 Win=26280 Len=0
2147 0.001059 192.168.126.161  172.16.1.2       TCP 37854 > http [ACK] Seq=111 Ack=11681 Win=29200 Len=0
2148 0.000018 10.1.1.1         172.16.1.2       TCP 37854 > http [ACK] Seq=111 Ack=11681 Win=29200 Len=0
Run Code Online (Sandbox Code Playgroud)

我一直在根据此图表挖掘和记录数据包遍历,似乎最后传入的数据包 2133 已通过 raw-PREROUTING、conntrack、mangle-PREROUTING 但随后丢失了。我的 iptables 中没有 REJECT 规则,我记录了所有 DROP 规则,但没有一个显示数据包 2133 丢失的位置。

我很想在传入过滤器上使用 TRACE 目标,但不幸的是 ubuntu 8.04 不支持 TRACE 目标。

所以我相信一些内部隐式路由/conntrack/mangling 规则适用,由于某种原因重置连接。也许流量触发了一些 DOS 保护,但我不知道在哪里配置/分析它。最令人沮丧的是,数据包被拒绝并且没有任何记录......

同样从 Windows 主机请求此文件 100% 工作,但它在某些 Linux 主机上失败并且所有请求的 99.9% 都通过,但有时数据包的时间会在我们的防火墙中触发此行为。

编辑 好的,现在我在 iptables 中添加了大量的日志记录,似乎发生了以下情况(仍然不知道为什么!)

对于成功穿过防火墙的数据包,采取以下步骤,表/步骤参考来自此处

Table 3-3 step

2     raw-pre
      conntrack
3     mangle-pre
4     [nat-pre]
5     routing-decision -> destination forward
6     mangle-fwd
7     filter-fwd
8     mangle-post
9     [nat-post]
Run Code Online (Sandbox Code Playgroud)

被拒绝的数据包 2133 遍历以下步骤:

Table 3-1 steps for the incoming FIN,ACK packet 2133
2     raw-pre
      conntrack
3     mangle-pre
4     [nat-pre]
5     routing-decision -> destination local
6     mangle-input
7     filter-input
8     local process emits RST -> webserver

Table 3-2 steps for the outgoing RST packet 2134 in response to 2133
1     raw-out
2     routing decision
      conntrack
3     mangle-out
      reroute-check
4     [nat-out]
5     filter-out
6     mangle-post
7     nat-post
Run Code Online (Sandbox Code Playgroud)

奇怪的是,现在步骤 5 中数据包 2133 的路由决策与其他数据包的路由决策不同。在分析有效的请求时,例如不会卡住,即使最后一个 FIN 也能正确路由。这似乎是内核中的一个错误,或者路由决策在某种程度上是有状态的。

编辑

可能导致这些问题的一件事是以下事实,流量在防火墙和本地 LAN 之间路由,因此客户端 LAN 没有通过 L2 直接连接到防火墙。

                +---------------------------+       +------------------+                         +------------------------+
                |                           |       |      Router      |   (   Lab network    )  |                        |
( Internet ) -- + eth1                 eth0 +-------+                  +-- (                  ) -+ Client 192.168.126.161 |
                | 10.1.1.1   192.168.60.254 |       |                  |   ( 192.168.126.0/24 )  |                        |
                +---------------------------+       +------------------+                         +------------------------+
Run Code Online (Sandbox Code Playgroud)

图中10.1.1.1代表防火墙的外部IP地址,其他地址均为实际使用的IP地址。

这是防火墙上的路由表:

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.1.1.0        0.0.0.0         255.255.255.240 U     0      0        0 eth1
192.168.126.0   0.0.0.0         255.255.255.0   U     0      0        0 eth0
192.168.60.0    0.0.0.0         255.255.255.0   U     0      0        0 eth0
0.0.0.0         10.1.1.15       0.0.0.0         UG    0      0        0 eth1
Run Code Online (Sandbox Code Playgroud)

注意10.1.1.0和默认的gw 10.1.1.15是编的,其余和用的完全一样。我必须手动添加 192.168.126.0/24 路由​​才能从 eth0 (192.168.60.254) 到达实验室网络。

这里有一些关于最后一个数据包 2133 的数据包遍历的大量日志,该数据包由于被路由到本地主机(例如防火墙)而被拒绝。

[16406874.374588] raw pre IN=eth1 OUT= MAC=00:02:b3:b9:ff:b5:00:90:1a:10:06:88:08:00 SRC=172.16.1.2 DST=10.1.1.1 LEN=1004 TOS=0x00 PREC=0x00 TTL=55 ID=13739 DF PROTO=TCP SPT=80 DPT=53497 WINDOW=5840 RES=0x00 ACK PSH FIN URGP=0 
[16406874.374625] mangle pre IN=eth1 OUT= MAC=00:02:b3:b9:ff:b5:00:90:1a:10:06:88:08:00 SRC=172.16.1.2 DST=10.1.1.1 LEN=1004 TOS=0x00 PREC=0x00 TTL=55 ID=13739 DF PROTO=TCP SPT=80 DPT=53497 WINDOW=5840 RES=0x00 ACK PSH FIN URGP=0 
[16406874.374667] mangle in IN=eth1 OUT= MAC=00:02:b3:b9:ff:b5:00:90:1a:10:06:88:08:00 SRC=172.16.1.2 DST=10.1.1.1 LEN=1004 TOS=0x00 PREC=0x00 TTL=55 ID=13739 DF PROTO=TCP SPT=80 DPT=53497 WINDOW=5840 RES=0x00 ACK PSH FIN URGP=0 
[16406874.374699] filter in IN=eth1 OUT= MAC=00:02:b3:b9:ff:b5:00:90:1a:10:06:88:08:00 SRC=172.16.1.2 DST=10.1.1.1 LEN=1004 TOS=0x00 PREC=0x00 TTL=55 ID=13739 DF PROTO=TCP SPT=80 DPT=53497 WINDOW=5840 RES=0x00 ACK PSH FIN URGP=0 
[16406874.374780] mangle out IN= OUT=eth1 SRC=10.1.1.1 DST=172.16.1.2 LEN=40 TOS=0x00 PREC=0x00 TTL=64 ID=0 DF PROTO=TCP SPT=53497 DPT=80 WINDOW=0 RES=0x00 RST URGP=0 
[16406874.374807] mangle post IN= OUT=eth1 SRC=10.1.1.1 DST=172.16.1.2 LEN=40 TOS=0x00 PREC=0x00 TTL=64 ID=0 DF PROTO=TCP SPT=53497 DPT=80 WINDOW=0 RES=0x00 RST URGP=0 
[16406874.378813] mangle pre IN=eth0 OUT= MAC=00:02:b3:b9:ff:b4:00:90:1a:10:0c:dd:08:00 SRC=192.168.126.161 DST=172.16.1.2 LEN=40 TOS=0x00 PREC=0x00 TTL=63 ID=35424 DF PROTO=TCP SPT=53497 DPT=80 WINDOW=11680 RES=0x00 ACK URGP=0 
[16406874.378863] mangle fwd IN=eth0 OUT=eth1 SRC=192.168.126.161 DST=172.16.1.2 LEN=40 TOS=0x00 PREC=0x00 TTL=62 ID=35424 DF PROTO=TCP SPT=53497 DPT=80 WINDOW=11680 RES=0x00 ACK URGP=0 
Run Code Online (Sandbox Code Playgroud)

再次,我们的 fw 外部 IP 已替换为 10.1.1.1,NAT 网络外的 webserver ip 替换为 172.16.1.2

编辑突发新闻!

好的,最后一次尝试是删除 RST 数据包,非常非常有趣,我添加了一个 iptables 规则,该规则删除了所有目的地为我们在请求文件时遇到问题的网络服务器的 RST 数据包。然后它起作用了,例如上面日志中的最后一个 FIN,ACK,PSH 数据包 2133 被丢弃,但是由于 RST 被丢弃,网络服务器有时间获取所有 ACK 的蚂蚁,然后决定再次重新传输最后一个数据包,数据包 2133,现在它通过了防火墙,因为 contrack 模块现在已经看到从客户端返回的 ACK,并允许最后一个 ACK​​、带有最终有效载荷的 FIN 数据包。

所以这绝对是一个时间/窗口问题,这个特定的文件,随着来自客户端的 ACK 的时间,触发 conntrack 中的某些东西,拒绝来自网络服务器的最终数据包。

到目前为止,谷歌搜索和阅读内核文档没有发现任何可能导致这种行为的原因,下一步将是阅读路由/conntrack 模块的内核源代码。

问题解决了

好吧,至少现在我们确切地知道会发生什么,并且有了解决问题的变通方法。

Sergey 指出了非常有价值的 -m state --state INVALID 匹配规则,它对调试有很大帮助,我现在意识到没有明确的 INVALID 数据包规则的 iptables 设置是不完整的,所以有时似乎会发生奇怪的行为。

当启用登录 conntrack 模块以了解导致无效数据包的原因时,会发生什么很明显,我对此持怀疑态度。

[16659529.322465] nf_ct_tcp: SEQ is over the upper bound (over the window of the receiver) IN= OUT= SRC=172.16.1.2 DST=10.1.1.1 LEN=1004 TOS=0x00 PREC=0x00 TTL=55 ID=40874 DF PROTO=TCP SPT=80 DPT=55498 SEQ=658735108 ACK=1194081763 WINDOW=5840 RES=0x00 ACK PSH FIN URGP=0 
Run Code Online (Sandbox Code Playgroud)

同样,172.16.1.2 是外部网络服务器(行为不正确),10.1.1.1 是防火墙的外部地址。

网络服务器通过线路推送的数据多于客户端在接收窗口中公布的数据(conntrack 是全状态并验证这一点),似乎是当 FIN 数据包到达时 conntrack 退出,因为接收窗口实际上超出了很多早些时候。

我相信这可能是由于网络服务器上网卡中的 TCP 卸载不正确造成的。当我开始分析这个时,我在网络服务器上进行了捕获,并且根据 tcpdump/wireshark 跟踪记录,巨型帧是由内核中的 TCP 层写入的,然后通过网卡将其分割为 MTU=1500 的较小帧。所以显然这需要在网络服务器中解决,因为发送比接收器在其接收窗口中的广告更多的数据是不正确的 TCP 行为。

Polynomial 和 Sergey 都提供了有价值的输入,但 Sergey 向我指出了 conntrack/NAT 模块关于数据包遍历的确切行为。

Ser*_*sov 10

http://www.spinics.net/lists/netfilter/msg51408.html描述了类似的情况:一些本应由 NAT 处理的数据包以某种方式被标记为 INVALID 而不是 ESTABLISHED,并进入 INPUT 链。您应该添加一些规则-m state --state INVALID来检查这一点,http://www.spinics.net/lists/netfilter/msg51409.html 上的答案表明应始终删除此类 INVALID 数据包,因为未对它们正确执行 NAT ,因此它们中的地址可能是错误的。

如果你的有问题的数据包真的被标记为INVALID,添加iptables -I INPUT -m state --state INVALID -j DROP可能会解决这个问题(损坏的数据包不会到达本地进程,也不会引起RST响应,然后TCP会在超时后从丢失的数据包中恢复)。然后您可以尝试进一步调试问题,如http://www.spinics.net/lists/netfilter/msg51411.html 中所述

echo 255 >/proc/sys/net/netfilter/nf_conntrack_log_invalid
Run Code Online (Sandbox Code Playgroud)

(在这种特殊情况下,问题是由路径上的一些损坏的网络硬件引起的,可能与某些 TCP 校验和卸载损坏相结合。)