1、如果服务端不想接受这次握手,它会怎么做呢?可能会出现这么几种情况:
- 不搭理这次连接,就当什么都没收到,什么都没发生。这种行为,也可以说是“装聋作哑”。
- 给予回复,明确拒绝。相当于有人伸手过来想握手,你一巴掌拍掉,真的是非常刚了。
第一种情况,因为服务端做了“静默丢包”,也就是虽然收到了 SYN,但是它直接丢弃了,也不给客户端回复任何消息。这也导致了一个问题,就是客户端无法分清楚这个 SYN 到底是下面哪种情况:
- 在网络上丢失了,服务端收不到,自然不会有回复;
- 对端收到了但没回,就是刚才说的“静默丢包”;
- 对端收到了也回了,但这个回包在网络中丢了。
2、测试
第一步,在服务端,执行下面的这条命令,让 Iptables 静默丢弃掉发往自己 80 端口的数据包:
Iptables -I INPUT -p tcp --dport 80 -j DROP
第二步,在客户端启动 tcpdump 抓包:
sudo tcpdump -i any -w telnet-80.pcap port 80
第三步,从客户端发起一次 telnet:
telnet 服务端IP 80
telnet 挂起的原因就在这里:握手请求一直没成功。客户端一共有 7 个 SYN 包发出,或者说,除了第一次 SYN,后续还有 6 次重试。客户端当然也不是“傻子”,这么多次都失败,就放弃了连接尝试,把失败的消息传递给了用户空间程序,然后就是 telnet 退出。
TCP 握手没响应的话,操作系统会做重试
在 Linux 中,这个设置是由内核参数 net.ipv4.tcp_syn_retries 控制的,默认值为 6
$ sudo sysctl net.ipv4.tcp_syn_retries
net.ipv4.tcp_syn_retries = 6
REJECT,这应该能让客户端立刻退出了。执行下面的这条命令,让 Iptables 拒绝发到 80 端口的数据包:
Iptables -I INPUT -p tcp --dport 80 -j REJECT
此时telnet会立即退出
查看iptables规则发现它自动补上了–reject-with icmp-port-unreachable,也就是说确实用 ICMP 消息做了回复。当然,你还可以把这个动作定义为–reject-with tcp-reset,那样的话就符合我们一开始的期望了。
sudo tcpdump -i any -w telnet-80-reject.pcap host 47.94.129.219 and port 80
配置iptables将端口Reset
iptables -I INPUT -p tcp --dport 80 -j REJECT --reject-with tcp-reset
3、TCP握手流程图
在上面这张图里,无论是客户端或者服务端,我们从上往下看,它要经历的各个 TCP 状态,都展示得十分清楚。我把这个过程解读如下:
SYN_SENT 这个状态,意味着当时这个连接请求(SYN 包),已经从这台 Windows 服务器发出,试图跟远端的 AD 域控制器进行连接。但由于对端迟迟没有回应 SYN+ACK 报文,那么客户端这个连接的状态,就只能“停留”在 SYN_SENT 状态,无法转化为 ESTABLISHED 状态。