==== 友情提示的分割线 ====
最近收到不少朋友给我的消息,本来准备回复,结果发现微信公众号有个恶心的设置,就是超过一定时间就回复不了了。 老王对于大家所有的问题都会一一回复 ,所以,如果大家发现一两天后没有收到回复,请在公众号 右下角菜单 -> 有点意思 -> 有问有答 中提出。
==== 正文的分割线 ====
好了,开始今天的话题吧。今天的话题是个大家耳朵都听起茧的话题: TCP 的三次握手。不过呢,老王今天聊的这个话题,还是跟原来教科书上可能有些不一样,因为当我第一次接触到的时候,都觉得有点意思。所以老王今天就分享给大家 ~
TCP 是一个可靠的传输协议(相对 UDP 而言),他的可靠是建立在几个手段之上的,比如:三次握手、滑动窗口、超时重传等等。其中三次握手是最重要的手段之一。所谓的三次握手,就是在建立 TCP 连接的时候,客户端和服务器需要做几次通讯,确认相互之间的信息。具体的流程如下图:
当客户端发起三次握手的时候:
1 、先发起一个 SYN=1 的包,并且带一个序列号( Seq );
2 、服务器收到这个包以后,将这个数据放入到一个队列中,这个队列叫 syn_table 。并且发送一个返回包,作为响应,这个返回包有自己的序列号( Seq ),以及一个 Ack , Ack 的值就是客户端发来的 Seq 值加一;
3 、客户端收到返回信息以后,将服务器的 Seq 加一作为 Ack 又发给服务器;
4 、服务器收到这第三个包,验证没问题以后,就将这个连接放入到 request_sock_queue
,三次握手完成。
对于写程序的人而言,我们客户端在调用 connect 的时候,发起三次握手操作。当三次握手完成后,服务器从 request_sock_queue 中获取对应的连接,返回给 accept 对应的 fd 。
以上就是三次握手的原理以及程序对应的调用。
那问题就来了,如果,客户端是一个恶意的用户,他故意先发起一个第一个握手包,服务器收到以后放入等待队列,并返回确认。但是,客户端不再发送第三个确认包,会怎么样?服务器会进行多次重试发送(一般是五次, linux 系统有对应的配置 tcp_synack_retries )。
这带来的问题就是,服务器需要额外的开销,以及等待队列被占用。更严重的,就是如果有大量的客户端发起这种连接,我们的服务器的等待队列会很快就被占满(队列长队配置在: tcp_max_syn_backlog 中),导致后来的请求不再被服务器接收,从而阻止服务器服务其他用户。这就是著名的 Syn-Flood 攻击。
那我们有办法防止这个 Syn-Flood 攻击吗?答案当然是有的!
linux 似乎从 2.2 开始,就有 tcp_syncookies 这样一个配置,如果开启了这个配置,就会产生一个效果:
当 syn 等待队列满的时候,新来的请求就不再放入队列中,而是采用一个算法来解决。具体是什么算法呢,跟着老王往下看。
我们不是讲了三次握手的第二次是由服务器发送给客户端嘛, linux 很巧妙的在第二次握手的 Seq 上做了手脚。他将用户请求的参数(包括请求的地址、端口等),加上服务器的一个序列号等做了一次运算,得到了一个 32 位的无符号整数,并将这个整数作为 Seq 值发送给客户端。这个时候有两种情况发生:
1 、客户端是攻击者:客户端不再做出第三次握手的响应。这个时候对服务器而言,并没有任何损失,因为他不占用服务器任何资源;
2 、客户端是正常的用户:客户端会将这个 Seq 的值加一作为 Ack 返回给服务器。服务器拿着这个 Ack 值减一,进行刚才算法的逆运算进行校验,看是否得到和发出去的数据一致。如果能得到,则是一个有效的响应,否则就不是。
通过这样的一个算法,就能比较有效的区分是否是正常的请求,同时能减少服务器的压力和被攻击的可能。
====tcp_syncookies 说明的分割线 ====
tcp_syncookies (Boolean; since Linux 2.2)
Enable TCP syncookies. The kernel must be compiled withCONFIG_SYN_COOKIES. Send outsyncookies when the syn backlog queue of asocket overflows. The syncookies featureattempts to protect a socket from a SYN flood attack. This should be used as a last resort, if at all. This is a violation of the TCP protocol, and conflicts withother areas of TCP such as TCP extensions. It can cause problems for clients and relays. It is not recommended as atuning mechanism for heavily loaded servers to help with overloaded ormisconfigured conditions. For recommended alternatives see tcp_max_syn_backlog, tcp_synack_retries, and tcp_abort_on_overflow.
==== 具体算法的分割线 ====
u32 count = tcp_cookie_time();
return (cookie_hash(saddr, daddr, sport,dport, 0, 0) +
sseq+ (count << COOKIEBITS) +
((cookie_hash(saddr,daddr, sport, dport, count, 1) + data)
& COOKIEMASK));
这里的 count 没分钟加一,如果客户端返回的 count 超过 2 ,就是超时了。
好了,有了这样的算法是否也是就万无一失呢?
也不是!因为客户端可以发起第三个握手包进行后面的攻击,比如 DoS 、 DDoS 、 DRDoS 等等。老王下次就聊聊其他的这些攻击方法。
怎么样,觉得老王今天聊的如何?有兴趣的话,下周日继续吧 ~
原文 http://mp.weixin.qq.com/s/RbEELhDfIuBtiJo9H0c-Dw