Linux下使用tcpkill工具重置NFS连接

有些时候在 Linux 系统中使用 NFS 挂载远程共享(使用默认的 TCP 方式)之后,偶尔会因为网络异常出现 NFS 连接出错的问题,这种情况下使用任何 ls 或者 df 等等常用的命令对 NFS 挂载目录进行简单的查看操作都可能卡顿几十秒乃至几分钟的时间。

此时如果在别的 Linux 客户机系统上又是可以正常访问 NFS 共享的,而 NFS 服务器端考虑到有多个客户机正在使用不方便直接重启服务,一般只能等待 NFS 挂载连接恢复正常或者重启客户端系统,这样还是很麻烦的,为此我专门找了个使用 tcpkill 命令重置 NFS 连接的方法分享给大家。

tcpkill 命令属于 Dsniff 嗅探工具包,Dsniff 本身是一个高级的网络嗅探器,Dsniff 可以将制造的数据包注入到网络,一般 Linux 系统中可以找到对应的 dsniff 软件包进行安装,我测试使用的 CentOS 6.1 64 位系统使用的下面的 RPM 安装包:

http://rpm.repo.onapp.com/ramdisk-hv/centos6/dsniff/

tcpkill 的工作原理是利用 libpcap 库监控符合过滤条件的 TCP 连接并等待,当该 TCP 连接上有数据传输时就会被 tcpkill 感知到,不过作为应用程序的 tcpkill 当然不能直接关闭 TCP 连接,此时就会构造一条 RST 报文发回去直接导致 TCP 连接异常关闭(其实 TCP RST 就是 GFW 对敏感网页进行屏蔽的老手段之一哈)。

对于 NFS 共享使用的 TCP 连接而言,通过 tcpkill 发送 RST 报文导致连接异常关闭之后,NFS 客户端就能知道原有的连接不可用,会自动重新开启新的 TCP 连接,这样就能达到我们需要的重置 NFS 连接的效果。

下面具体实验看看,首先在 NFS 服务器上使用 netstat 命令确认所有 TCP 方式挂载的 NFS 连接,这里使用 NFS 服务的 2049 端口进行过滤:

~ # netstat -anp 2>/dev/null | grep 2049
tcp        0      0 0.0.0.0:2049            0.0.0.0:*               LISTEN      -
tcp        0      0 192.168.1.56:2049       192.168.1.52:953        ESTABLISHED -
tcp        0      0 192.168.1.56:2049       192.168.1.129:824       ESTABLISHED -
udp        0      0 0.0.0.0:2049            0.0.0.0:*                           -

可以看到 NFS 服务器上有 192.168.1.52 和 192.168.1.129 这两个客户端正在连接,看看 tcpkill 命令的用法:

~ # tcpkill [-i interface] [-1...9] expression

tcpkill 的 -i 参数指定绑定哪个网卡,expression 就是 tcpdump 的过滤表达式参数,具体如何使用可以参考 tcpdump 的帮助信息:

http://www.tcpdump.org/tcpdump_man.html

我们就以关闭上面的 192.168.1.52 客户端使用的 NFS 连接为例:

~ # tcpkill -i eth0 host 192.168.1.52 and port 2049
tcpkill: listening on eth0 [host 192.168.1.52 and port 2049]

tcpkill 命令启动之后会一直等待新的数据传输,我们就可以在 NFS 客户端上随便运行 ls 或者 df 等命令查看 NFS 共享文件夹就可以让系统发送新的 NFS 数据(仍然是有问题的 NFS 连接所以可能会卡住),此时服务器端的 tcpkill 命令就有反应了:

~ # tcpkill -i eth0 host 192.168.1.52 and port 2049
tcpkill: listening on eth0 [host 192.168.1.52 and port 2049]
192.168.1.52:953 > 192.168.1.56:2049: R 1715581713:1715581713(0) win 0
192.168.1.52:953 > 192.168.1.56:2049: R 1715581896:1715581896(0) win 0
192.168.1.52:953 > 192.168.1.56:2049: R 1715582262:1715582262(0) win 0
192.168.1.56:2049 > 192.168.1.52:953: R 624540755:624540755(0) win 0
192.168.1.56:2049 > 192.168.1.52:953: R 624540919:624540919(0) win 0
192.168.1.56:2049 > 192.168.1.52:953: R 624541247:624541247(0) win 0
192.168.1.52:953 > 192.168.1.56:2049: R 1715581801:1715581801(0) win 0
192.168.1.52:953 > 192.168.1.56:2049: R 1715581984:1715581984(0) win 0
192.168.1.52:953 > 192.168.1.56:2049: R 1715582350:1715582350(0) win 0

可以看到 tcpkill 已经发送了 RST 包导致 NFS 客户端重新连接,如果客户端的 NFS 共享操作已经恢复正常了,就可以退出 tcpkill 命令了。

从上面的例子我们也会发现 tcpkill 默认是只能“关闭”活跃有数据传输的 TCP 连接的;对于非活跃的连接我们可能需要主动发送 SYN 包,并根据返回的 TCP 序列号构造新的 RST 包再次发送以达到关闭连接的效果,看起来已经有网友实现了一个支持关闭非活跃连接的 tcpkill 程序,有兴趣的朋友也可以关注看看哦。最后祝大家中秋佳节玩的开心 ^_^。





*