一次网络连通性监控误报问题诊断

背景

业务存在一个监控系统,需要监控一些设备的网络是否正常。不过最近发现经常会发生断网误报情况,所以深入探究一下问题原因。

排查过程

阶段一:一直困扰在Ping的请求是否通的误区

开始的时候,粗略看了一下源代码,再加上平时一直使用Ping作为网络探测的首要方式,所以一直认为只有Ping通,才认为网络是通的。

if (!PingUtil.ping(ip)) {
    return DISCONNECT;
}

但是事实并非如此,发现有一些主机,从监控主机ping依然是不通的,但是监控平台显示网络状态是正常的。

阶段二:仔细阅读代码,判断问题

这就说明判断对方主机是否断网的策略应该不止是Ping方式。
进入PingUtil.cmdPing源码,可以看到包含两种策

  • 策略一:使用JDK InetAddress的isReachable探测。
  • 策略二:如果策略一失败,则尝试使用Ping方法探测。
public static boolean ping(String ipAddress) {
    try {
        InetAddress inet = InetAddress.getByName(ipAddress);
        boolean isReachable = inet.isReachable(1000);
        if (isReachable) {
            return true;
        }
    } catch (IOException e) {
        logger.error("ping reachable error,ip:" + ipAddress, e);
    }

    String osName = System.getProperty("os.name");
    String cmd = null;
    int pingTimes = 4;
    if (osName.contains(WINDOWS)) {
        cmd = "ping -n " + pingTimes + " " + ipAddress;
    } else {
        cmd = "ping -c " + pingTimes + " " + ipAddress;
    }
    Process process = null;
    try {
        process = Runtime.getRuntime().exec(cmd);
    } catch (IOException e) {
        logger.error("ping exec error,ip:" + ipAddress, e);
    }
    if (process == null) {
        return false;
    }
    int connected = 0;
    StringBuilder content=new StringBuilder();
    try (BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
        String line = null;
        while ((line = in.readLine()) != null) {
            content.append(line).append(";");
            if (line.contains("ttl=") || line.contains("TTL=")) {
                connected++;
            }
        }
    } catch (IOException e) {
        logger.error("ping read error,ip:" + ipAddress, e);
    }

    return connected > 0;
}

回到源码,只能说明策略一在起作用,也就是InetAddress.isReachable
看一下代码的JDK注释

/**
     * Test whether that address is reachable. Best effort is made by the
     * implementation to try to reach the host, but firewalls and server
     * configuration may block requests resulting in a unreachable status
     * while some specific ports may be accessible.
     * A typical implementation will use ICMP ECHO REQUESTs if the
     * privilege can be obtained, otherwise it will try to establish
     * a TCP connection on port 7 (Echo) of the destination host.
     * <p>
     * The timeout value, in milliseconds, indicates the maximum amount of time
     * the try should take. If the operation times out before getting an
     * answer, the host is deemed unreachable. A negative value will result
     * in an IllegalArgumentException being thrown.
     *
     * @param   timeout the time, in milliseconds, before the call aborts
     * @return a {@code boolean} indicating if the address is reachable.
     * @throws IOException if a network error occurs
     * @throws  IllegalArgumentException if {@code timeout} is negative.
     * @since 1.5
     */
public boolean isReachable(int timeout) throws IOException {
    return isReachable(null, 0 , timeout);
}

有几个点要注意

  • 典型的实现是使用ICMP协议,不过这要求有Root权限,有资料显示是因为ICMP需要使用Raw Socket。
  • 如果没有权限,那么就会尝试使用TCP 7端口同远程host建立连接。

对于我们常规的java应用,都不是以Root权限运行的,所以依赖于同远程host建立TCP连接的方式。到这里,立马去机器上执行

telnet xx.xx.xx.xx 7

很遗憾,直接得到了Conenction Refused。
这里也补充一下端口7的说明,端口7是echo服务的,通常就是发起服务器什么,就会返回什么。但是由于存在安全隐患,该端口通常都是关闭的。


image.png

既然TCP端口7不通,而且Ping也不通,那究竟如何判断网络是正常的呢? 还是要求助源码。
github上找了一下InetAddress的实现(https://github.com/frohoff/jdk8u-dev-jdk/blob/master/src/solaris/native/java/net/Inet4AddressImpl.c

image.png

仿佛发现了新大陆一样,发现即使是connection refused,也可以认为网络是可达的。
对connnection refused可以解释为

  1. 发送一个TCP SYN packet 给远端机器。
  2. 然后收到一个 TCP RST packet 响应。

也就是说请求可以到达远端机器,至于远端给的是可以连接的响应,还是拒绝的响应,至少可以证明链路是通的。
这里有一点需要注意,有一些特殊情况下,链路中间的防火墙会拦截TCP SYN packet,然后给一个TCP RST响应。所以InetAddress.isReachable通常是局域网内的判断。
这里以百度的一个ip为例,发现的确向对方TCP端口7发送了请求,但是并未收到响应,所以此时根据InetAddress.isReachable得到的结果就是false了。


image.png

阶段三:问题验证,事实说话

关于InetAddress.isReachable方法,还是需要验证一下。

本地验证

就将源码单独运行一下,本地开启wireshark,抓包验证。

public static void main(String[] args) throws IOException, InterruptedException {
    while (true) {
        InetAddress inet = InetAddress.getByName("192.168.2.116");
        boolean isReachable = inet.isReachable(1000);
        System.out.println(isReachable);
        Thread.sleep(3000);
    }
}

验证1:非root账号运行

直接使用Idea Run程序运行,


image.png

注意右下角,可以看到如前面阶段二所述,是给远端 TCP 7发送建连接请求,并且也收到了一个RST响应,而且判断结果是true。

验证2:使用Root账户运行

image.png

在这种情况下,使用Mac Root账号执行,然后抓包后,发现是使用的ICMP协议了。

验证3:使用非Root账户运行,并且模拟端口7不可达。
linux环境下,可以使用iptables做一些路由。如果是Mac电脑,需要使用pfctl工具(Mac下使用pfctl)。
修改pfctl,将发送给远端端口7的包drop掉。

block drop out proto tcp from any to 192.168.2.116 port 7
image.png

可以看到此时的InetAddress.isReachable返回了false。

监控服务器验证

linux服务器上可以借助tcpdump进行抓包。

抓取icmp包

命令

sudo tcpdump -c 5 -nn -i 网卡 icmp

可以Ping通的情况下,应该包含request请求和reply响应。


image.png

如果无法ping通的话,只包含了request请求。

抓取tcp端口7的包

命令

sudo tcpdump -nn tcp port 7 |grep '目标ip'

执行结果(右上角是tcpdump的抓包结果,右下角是wireshark的结果)


image.png

说明:

  • Flag说明
    | | 标志类型 | 描述 |
    | --- | --- | --- |
    | S | SYN | Connection Start |
    | F | FIN | Connection Finish |
    | P | PUSH | Data push |
    | R | RST | Connection reset |
    | . | ACK | Acknowledgment |

可以看到通过tcp端口7发送SYN包,收到一个RST包。这样按照JDK说明,是可以判定网络可达的。

阶段四: 优化断网监控策略

Action 1:

从监控集群到目标主机的链路来看,依赖tcp和icmp两种协议的数据包,所以需要保障双向(从目标主机到监控集群、从监控集群到目标主机)的路由策略,不能拦截数据包。这一步需要网络运维同学配合完成。

Action 2:

原有的监控策略是依赖InetAddress.isReachable的,也就是从监控集群发出Ping请求或者是请求tcp 7端口。两种方式都属于监控集群主动监控。 在当前情况下,由于程序是非Root启动,所以会首先依赖tcp 7端口的响应。但是当偶尔发生丢包或者超时,就会被误认为断网。
所以增加一种校验策略,由于目标主机是会定期上传心跳,而上传心跳也就意味着设备可以通过网络访问到日志平台,可以基于心跳时间判断网络是否正常。

调整后的策略

  1. 判断最后一次心跳是否在允许的时间区间内。
  2. 如果心跳不正常,使用InetAddress.isReachable进行判断。
  3. 如果InetAddress.isReachable不通,尝试直接执行Ping命令。

只有当上述三种策略都不通的情况下,则认定目标主机已经断网。

总结

  1. 运维人员依赖监控告警,如果频繁的误报,会影响大家的判断,所以要尽可能减少误报,增加准确率。当然依赖心跳会引入告警及时性的问题。
  2. 网络是非常复杂的,在判定条件上,可以综合多种方式一起判断。同时要善于使用抓包工具进行验证。
  3. 对于源码问题,一定要找到问题根源,并加以验证。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容

  • 主目录见:Android高级进阶知识(这是总目录索引)[written by 无心追求] TCP问题分析 网络的五...
    ZJ_Rocky阅读 1,576评论 0 5
  • 个人认为,Goodboy1881先生的TCP /IP 协议详解学习博客系列博客是一部非常精彩的学习笔记,这虽然只是...
    贰零壹柒_fc10阅读 5,054评论 0 8
  • TCP问题分析 网络的五层协议 物理层 数据链路层 网络层,IP协议,ICMP协议(ping) 传输层,传输层有两...
    大大大大大先生阅读 10,785评论 0 6
  • http 响应常见状态码 100-199 : 表示成功接收请求, 要求客户端继续提交下一次请求才能完成整个处理过程...
    _往后_阅读 416评论 0 1
  • 基础网络 ​ 用于日常和网络相关工作的人员交流 掌握基础部分 : (路由 交换)...
    苗小笨_be86阅读 564评论 0 1