Content #
- 服务端添加拒绝的规则
iptables -I INPUT -p tcp --dport 80 -j REJECT
- 客户端手抓包
sudo tcpdump -i any -w telnet-80-reject.pcap host 47.94.129.219 and port 80
在客户端发起 telnet 服务端 IP 80。
- 客户端telnet登录
$ telnet 47.94.129.219 80
Trying 47.94.129.219...
telnet: connect to address 47.94.129.219: Connection refused
telnet: Unable to connect to remote host
telnet 立刻退出,奇怪的是,抓包文件里并没有期望的 TCP RST?
- 改一下抓包条件,只保留远端 IP,去掉端口的限制:
sudo tcpdump -i any -w telnet-80-reject.pcap host 47.94.129.219
再来看抓到的报文,很意外,居然对端回复了一个 ICMP 消息:Destination unreachable (Port unreachable)。而且,这个 ICMP 消息不仅通过 type=3 表示,这是一个“端口不可达”的错误消息,而且在它的 payload 里面,还携带了完整的 TCP 握手包的信息。而这个握手包,可是客户端发过来的。
如果我们回头再检查一下前面生成的 iptables 规则,它是这样的:
-A INPUT -p tcp -m tcp --dport 80 -j REJECT --reject-with icmp-port-unreachable
原来,它自动补上了–reject-with icmp-port-unreachable,也就是说确实用 ICMP 消息做了回复。当然,你还可以把这个动作定义为–reject-with tcp-reset,那样的话就符合我们一开始的期望了。
事实上,无论是收到 TCP RST 还是 ICMP port unreachable 消息,客户端的 connect() 调用都是返回 ECONNREFUSED,这就是 telnet 都报“connection refused”的深层次原因。
所以,这个握手失败的情况终于搞清楚了,它是这么发生的:
TCP 握手拒绝这个事,可以是 ICMP 报文来达成的。