一文教会实战网络抓包和分析包( 七 )

  • 由于服务端配置了防火墙 , 屏蔽了客户端的 ACK 包 , 所以服务端一直处于 SYN_RECV 状态 , 没有进入 ESTABLISHED 状态 , tcpdump 之所以能抓到客户端的 ACK 包 , 是因为数据包进入系统的顺序是先进入 tcpudmp , 后经过 iptables;
  • 接着 , 服务端超时重传了 SYN、ACK 包 , 重传了 5 次后 , 也就是超过 tcp_synack_retries 的值(默认值是 5) , 然后就没有继续重传了 , 此时服务端的 TCP 连接主动中止了 , 所以刚才处于 SYN_RECV 状态的 TCP 连接断开了 , 而客户端依然处于ESTABLISHED 状态;
  • 虽然服务端 TCP 断开了 , 但过了一段时间 , 发现客户端依然处于ESTABLISHED 状态 , 于是就在客户端的 telnet 会话输入了 123456 字符;
  • 由于服务端的防火墙配置了屏蔽所有携带 ACK 标志位的 TCP 报文 , 客户端发送的数据报文 , 服务端并不会接收 , 而是丢弃(如果服务端没有设置防火墙 , 由于服务端已经断开连接 , 此时收到客户的发来的数据报文后 , 会回 RST 报文) 。客户端由于一直收不到数据报文的确认报文 , 所以触发超时重传 , 在超时重传过程中 , 每一次重传 , RTO 的值是指数增长的 , 所以持续了好长一段时间 , 客户端的 telnet 才报错退出了 , 此时共重传了 15 次 , 然后客户端的也断开了连接 。
  •  
    通过这一波分析 , 刚才的两个疑点已经解除了:
     
    • 服务端在重传 SYN、ACK 包时 , 超过了最大重传次数 tcp_synack_retries , 于是服务端的 TCP 连接主动断开了 。
    • 客户端向服务端发送数据报文时 , 如果迟迟没有收到数据包的确认报文 , 也会触发超时重传 , 一共重传了 15 次数据报文 ,  最后 telnet 就断开了连接 。
    TCP 第一次握手的 SYN 包超时重传最大次数是由 tcp_syn_retries 指定 , TCP 第二次握手的 SYN、ACK 包超时重传最大次数是由 tcp_synack_retries 指定 , 那 TCP 建立连接后的数据包最大超时重传次数是由什么参数指定呢?
     
    TCP 建立连接后的数据包传输 , 最大超时重传次数是由 tcp_retries2 指定 , 默认值是 15 次 , 如下:
    $ cat /proc/sys/net/ipv4/tcp_retries2 15
    如果 15 次重传都做完了 , TCP 就会告诉应用层说:“搞不定了 , 包怎么都传不过去!”
     
    那如果客户端不发送数据 , 什么时候才会断开处于 ESTABLISHED 状态的连接?
     
    这里就需要提到 TCP 的保活机制 。这个机制的原理是这样的:
    定义一个时间段 , 在这个时间段内 , 如果没有任何连接相关的活动 , TCP 保活机制会开始作用 , 每隔一个时间间隔 , 发送一个「探测报文」 , 该探测报文包含的数据非常少 , 如果连续几个探测报文都没有得到响应 , 则认为当前的 TCP 连接已经死亡 , 系统内核将错误信息通知给上层应用程序 。
    在 Linux 内核可以有对应的参数可以设置保活时间、保活探测的次数、保活探测的时间间隔 , 以下都为默认值:
    net.ipv4.tcp_keepalive_time=7200 net.ipv4.tcp_keepalive_intvl=75 net.ipv4.tcp_keepalive_probes=9
    • tcp_keepalive_time=7200:表示保活时间是 7200 秒(2小时) , 也就 2 小时内如果没有任何连接相关的活动 , 则会启动保活机制
    • tcp_keepalive_intvl=75:表示每次检测间隔 75 秒;
    • tcp_keepalive_probes=9:表示检测 9 次无响应 , 认为对方是不可达的 , 从而中断本次的连接 。
     
    也就是说在 Linux 系统中 , 最少需要经过 2 小时 11 分 15 秒才可以发现一个「死亡」连接 。
    一文教会实战网络抓包和分析包

    文章插图
     
    这个时间是有点长的 , 所以如果我抓包足够久 , 或许能抓到探测报文 。


    推荐阅读