每一个“丑得人神共愤”的泡妞高 手都有一颗坚忍的心,这证明了人类 在绝境中毫不妥协的求生精神,反正丑都丑了,索性放开手脚大干一场,这就叫“无产阶级失去的是锁链,得到的是全世界”
2013年(130)
分类: LINUX
2013-05-23 14:09:34
本文转自
TCP状态转换图
TCP报文头
client server SYN_SENT --------- SYN J ---------> <-------- SYN K/ACK J+1 -- ESTABLISHED --------- ACK K+1 -------> ESTABLISHED
SYN包中可能携带以下信息
其中,2/3由RFC1323定义。Window Size的扩展,也称为Long Fat Pipe(即所谓长肥管道)。
收到客户端的SYN报文后,Server处于半开half-open状态,SYN攻击即Client不执行下一步响应ACK。内核中半开连接数有限,查看Linux内核backlog队列的默认长度:
# sysctl net.ipv4.tcp_max_syn_backlog net.ipv4.tcp_max_syn_backlog = 1024
在Socket API中,listen(sockfd, backlog)的第二个参数设置即为半开连接的队列长度,显然超过内核限定的队列长度没有意义。对于半开连接攻击(SYN flood),Linux中有一种名为SYN Cookie的保护措施。
当TCP实现发送SYN报文时,将为之选择一个ISN(Initial Sequence Number,初始序列号),RFC 793中指出ISN是一个32bit的计数器,每4ms加1。4.4BSD中,系统初始化时ISN被设置为1(与标准建议不符),每隔0.5秒增加 64000(这个计数器每8ms加1),另外每次建立连接后,ISN将增加64000。
选择ISN的目的在于防止网络中被延迟的分组在以后又被传送,导致连接的另一端对其做出错误的解释。
在Linux上可见超时时间大约为3分30秒:
$ date; telnet facebook.com 80; date Sat Nov 10 11:05:43 CST 2012 Trying 203.98.7.65... telnet: connect to address 203.98.7.65: Connection timed out Sat Nov 10 11:09:12 CST 2012
以下是对应的tcpdump抓包输出:
11:06:03.180678 IP 192.168.1.105.49026 > 203.98.7.65.http: Flags [S], seq 2074592499, win 5840, options [mss 1460,sackOK,TS val 9954586 ecr 0,nop,wscale 4], length 0 11:06:06.180978 IP 192.168.1.105.49026 > 203.98.7.65.http: Flags [S], seq 2074592499, win 5840, options [mss 1460,sackOK,TS val 9957587 ecr 0,nop,wscale 4], length 0 11:06:12.181220 IP 192.168.1.105.49026 > 203.98.7.65.http: Flags [S], seq 2074592499, win 5840, options [mss 1460,sackOK,TS val 9963587 ecr 0,nop,wscale 4], length 0 11:06:24.180853 IP 192.168.1.105.49026 > 203.98.7.65.http: Flags [S], seq 2074592499, win 5840, options [mss 1460,sackOK,TS val 9975587 ecr 0,nop,wscale 4], length 0 11:06:48.180291 IP 192.168.1.105.49026 > 203.98.7.65.http: Flags [S], seq 2074592499, win 5840, options [mss 1460,sackOK,TS val 9999587 ecr 0,nop,wscale 4], length 0 11:07:36.180339 IP 192.168.1.105.49026 > 203.98.7.65.http: Flags [S], seq 2074592499, win 5840, options [mss 1460,sackOK,TS val 10047587 ecr 0,nop,wscale 4], length 0
从tcpdump输出可见每次的超时时间:3、6、12、24、48、(96),超时规律即为: 3 * 2n,n取值为[0,5]。建立TCP连接的超时时间最长为:
timeout = 3 * (26 - 1) = 3 * 63 = 189
对比下《TCP/IP详解卷1》关于TCP连接超时的描述:典型伯克利实现有3次超时,第1次超时时间在5.59-5.93秒之间变化,第2次超时时间总是24.00秒,第3次超时时间为48秒。大多数伯克利实现的TCP连接总的超时时间约为75秒。
主动关闭连接的peer会进入TIME_WAIT状态。TIME_WAIT通常又叫做2MSL,MSL(Maximum Segment Lifetime)指报文的最大生存周期。TCP建立在IP之上,而IP报文的生存周期由TTL限制,IP头中的TTL字段占8bit,最大值为255, 表示一个IP报文可被传播的最大跳数(hop)。虽然TTL不是一个时间值,但它确实限制了IP报文在网络中的生存时间。RFC 1122建议MSL是2分钟,伯克利版本的TCP实现通常设置为30秒,通常我们认为MSL的取值为30秒到2分钟,因此TIME_WAIT的时间为1分 钟到4分钟。
TCP连接处于TIME_WAIT状态,可以防止最后发出的ACK报文丢失,此时另一端的TCP实现会重发最后的FIN。而处于TIME_WAIT状态的Socket Pair不能再被使用。所谓Socket Pair其实由5个元素组成:
(local-ip, local-port, remote-ip, remote-port, protocol)
在这里协议始终是TCP,也可认为Socket Pair由4元组构成。如果TCP连接处于TIME_WAIT,则4元组构成的连接只能在2MSL结束后才能使用。现实中这个限制还更为严格一些:处于 TIME_WAIT状态的端口默认情况下也不能使用。虽然可以通过设置SO_REUSEADDR来绕开这个限制,但TCP原则上仍需避免使用处于 TIME_WAIT状态的端口。
在Server开发中,如果中断Server进程,将导致TIME_WAIT连接的出现;而Server必然绑定在一个已知端口(如HTTP的 80),因此Server程序通常必然会设置SO_REUSEADDR。对Client程序而言则影响不大,虽然Client通常是主动关闭连接方,但 OS总可以选择一个新的端口建立与Server的连接(除非端口耗尽,即所有可用端口都处于TIME_WAIT)。
这里再总结下TIME_WAIT存在的意义:
所谓TCP半开状态(half-open),即指收到,并响应SYN/ACK后,对端不发送ACK。
所谓TCP半关闭状态(half-close),即指TCP连接的一方可以选择部分关闭连接。因为TCP是全双工连接,在一条连接上的读/写操作是 分开的,因此可以显示地关闭写通道,但还可以继续读取数据。应用程序用shutdown()而非close()关闭连接时,可选择关闭读或写或都关闭:
shutdown(sockfd, SHUT_WR);
如此调用将给对端发送FIN,说明我没有数据可写了,但还可以读数据(进入状态)。