最近发现一种情况,从办公室网络能 ping 通一台数据中心的虚机,但是 ssh 却连不上,
后来发现这台虚机加上一条内部网络路由之后就能 ssh 了。那么,这是不是有点奇怪,
因为不可能是因为路由的问题,因为显然 ping 能通那么就表示路由一定是可达的。
现在就来看看到底是什么原因导致的。
假设办公室机器为 A,IP 为 10.1.24.155;目标虚机为 B,IP 为 10.110.19.89。
从 A ping B,并且在 A 上抓包。
ping 命令的输出如下:
抓包结果如下:
发现了吗?ping 是通了,但是响应的包的 src ip 是 36.110.208.44,并不是 B 的 IP,
而且从 tcpdump 抓包结果看,确实没有收到 src ip 为 B 的 ICMP reply 包。
现在来看下 ssh B 以及抓包结果。
ssh 命令及输出如下:
ssh 一直连接不上,而从 tcpdump 中可以看到一直在重试 SYN 包,直到发送 7 次后超时,
关于更多 ssh 超时方面的分析,可以参考[理解 SSH 客户端连接超时配置]/2017/02/18/ssh-connect-timeout/。
那么,ssh 的同时,在 B 上抓包看看
从 B 上抓包,可以看到 B 确实有回复 ACK 报文,但是为什么 A 没有收到呢?
来看看 B 的路由表
看出来了,唯一匹配 10.1.241.55 的就是默认路由,而默认路由的网关是 10.110.31.1,
这个 IP 是一个 LVS SNAT 服务器,上去看看。
在 ssh B 的同时,在 LVS SNAT 服务器上抓包:
可以看到,LVS 收到了来自 B 的 ACK 报文,并且做了 SNAT 转换(替换 src IP)之后发往机器 A,
但是,可以看到 src IP 是 36.110.208.44,这不正是从 A 上 ping B 的时候收到的 ICMP echo reply 包的源 IP 吗!
所以,进一步明朗了,虽然 ICMP echo reply 包中的 dst IP 和 echo request 的 src IP 不同,
但是却可以通过,而 TCP ACK 包中的 src IP 和 TCP SYN 包中的 dst IP 不同,却不能通过,
很显然是交换机或防火墙的限制。
那么到底是外网防火墙还是内网呢?
这需要弄明白 LVS 的包是从哪里出来的,查看 LVS SNAT 服务器的路由表:
可以看到,目标网段 10.0.0.0 会从 eth0 发出,并且网关是 10.110.31.254。
这里一句话解释一下 LVS SNAT 的工作原理:LVS 收到包之后,替换包的 src IP,然后转发该包,
和普通的发包类似,转发的过程会查找 Linux 内核路由表,所以,虽然这个包具有 eth1 的 IP,
其实却是从 eth0 发出。