为什么我们的防火墙(Ubuntu 8.04)用RST拒绝最后的数据包(FIN,ACK,PSH)

背景来说,很长一段时间我们遇到了防火墙的问题,有时会让HTTP请求部分加载,直到TCP超时。

在跟踪防火墙上的stream量之后,我注意到它只发生在特定的时间条件下,例如,当Web服务器在客户端已经在有效载荷上发送第二个ACK之前发送了整个响应时。 [SYN,SYN / ACK,ACK]已经被交换,REQUEST已经被发送并且被确认,并且第一个RESPONSE分组已经被接收并且被确认,然后web服务器一次性发送响应体的其余部分(8个分组包括最后一个FIN,PSH),并且在客户端已经确认了这些之前,防火墙拒绝RST向networking服务器发送请求,并使客户端挂起无限。

以下是来自防火墙两端的数据包的整个wireshark跟踪。 192.168.126.161是客户端的私有NAT'et IP地址。 172.16.1.2是networking服务器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 

我一直在挖掘和logging数据包遍历根据这个图表 ,似乎最后传入的数据包2133通过原始PREROUTING,conntrack,mangle-PREROUTING,但然后丢失。 我的iptables没有拒绝规则,我logging了所有的DROP规则,没有一个显示数据包2133丢失的地方。

我很想在传入的filter上使用TRACE目标,但不幸的是,Ubuntu 8.04不支持TRACE目标。

所以我相信一些内部的隐式路由/ conntrack / mangling规则适用于由于某种原因重置连接。 也许交通触发一些DOS保护,但我不知道在哪里configuration/分析。 最令人沮丧的是一个数据包被拒绝,没有任何东西被logging下来。

也请求这个文件在Windows主机上工作100%,但在某些Linux主机上失败,并且所有请求中有99.9%通过,但有时数据包的计时在我们的防火墙中触发这种行为。

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

对于成功穿越防火墙的数据包,将采取以下步骤,从此处的表/步骤引用

 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] 

被拒绝的分组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 

奇怪的是,步骤5中的分组2133的路由决定现在不同于其他分组的路由决定。 在分析有效的请求时,例如不会卡住,即使是最后一个FIN也能正确路由。 这看起来像是内核中的一个bug,或者路由决策在某种程度上是有状态的。

编辑

导致这些问题的一个原因是,stream量在防火墙和本地局域网之间进行路由,所以客户端局域网不通过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 ) | | +---------------------------+ +------------------+ +------------------------+ 

在这张图中,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 

请注意,10.1.1.0和默认gw 10.1.1.15组成,其余部分与使用的完全相同。 我必须手动添加192.168.126.0/24路由从eth0(192.168.60.254)到达实验室networking。

以下是在最后一个数据包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 

我们的fw外部IP再一次被10.1.1.1取代,NAT外部networking以外的web服务器的ip被172.16.1.2

编辑最新消息!

好,最后一个尝试是DROP的RST数据包,非常非常有趣,我添加了一个iptables的规则,将所有RST数据包destined到我们有问题的networking服务器请求文件。 然后它就起作用了,例如,上面的日志中的最后的FIN,ACK,PSH分组2133被丢弃,但是由于RST被丢弃,web服务器有时间让所有的ACK的ant决定重新发送最后一个分组,现在它通过防火墙,因为contrack模块现在已经看到ACK从客户端回来,并允许最后的ACK,FIN数据包与最终的有效载荷。

所以这肯定是一个时间/窗口的问题,这个特定的文件,从客户端的ACK的时间,触发东西conntrack拒绝最后的数据包从networking服务器。

到目前为止,使用googling和阅读内核文档没有任何可以导致这种行为,下一步将是读取路由/ conntrack模块的内核源代码。

问题解决了

那么,至less现在我们知道发生了什么,并有一个解决方法解决问题。

Sergey指出非常有价值的-m状态 – 状态INVALID匹配规则在debugging中帮了很多忙,现在我意识到一个没有对INVALID包明确规则的iptables设置是不完整的,所以有时会出现奇怪的行为。

在conntrack模块中启用logging导致无效数据包的原因时,会发生什么,这很明显,我对此有怀疑。

 [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 

172.16.1.2再次是外部networking服务器(performance不正确),10.1.1.1是防火墙的外部地址。

networking服务器通过networking推送的数据比客户端在接收窗口中公布的更多的数据(conntrack是state-full并validation了这一点),似乎是当FIN数据包到达时,conntrack保留下来,因为接收窗口实际上超出了早。

我相信这可能是由networking服务器上网卡上的错误TCP卸载引起的。 当我开始分析这个时,我在networking服务器上捕获了数据,根据tcpdump / wireshark trace,巨型帧被内核中的TCP层写入,然后被网卡分割成MTU = 1500的更小的帧。 所以显然这需要在Web服务器中处理,因为发送更多数据的TCP行为不正确,比接收方在接收窗口中发送的数据要多。

Polynomial和Sergey都提供了宝贵的意见,但是Sergey指出了conntrack / NAT模块关于数据包遍历的确切行为。

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数据包应该始终是DROPP,因为NAT没有正确执行,因此地址可能是错误的。

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

 echo 255 >/proc/sys/net/netfilter/nf_conntrack_log_invalid 

(在这种情况下,问题是由path上的一些破损的networking硬件造成的,可能与一些TCP校验和卸载破坏相结合)。

我已经看到在其他防火墙types的这种行为,行为是如此相同,我想我会把它扔在那里。

我遇到的问题是,防火墙与盒子上的临时端口NAT一样。 如果两者发生冲突,这将导致这种确切的行为,因为内核现在假定连接是为本地计算机devise的。 为此,您可以检查一些事情。 首先你是在iptables中指定出站端口configuration(使用–to-ports)? 或者你是否修改了机器上的临时端口范围:

 $ cat /proc/sys/net/ipv4/ip_local_port_range 

诊断你可以设置你的捕获,看看你是否看到任何其他请求使用相同的外部fw ip,端口组合3 * MSL时间之前的RST(〜180s我认为)。

虽然我还不确定是否有答案,但如果我处于这种状况,我会先排除,然后再看另外两件事情。

这很容易重现吗? 是否有可能从防火墙中捕获更多的诊断信息,并看到问题发生? 我会试图捕捉:

 $ netstat -anp $ cat /proc/net/ip_conntrack 

每一秒钟左右的时间,试图再现,看看是否有什么东西在港口本地绑定和假面舞台在问题上看起来像什么。

如果您防火墙RST出站做最后的ACK从内部客户端导致连接成功?

最后一件事,你看到所有的日志? 你已经检查过dmesg吗? 你有没有在syslogconfiguration的防火墙上将*。*设置为一个文件来确保?

让我知道你find了什么! 我非常感谢您在问题中提供的信息量,谢谢。