TCP重传分析

来源:互联网 发布:蓝鸥学校java培训 编辑:程序博客网 时间:2024/05/18 17:26

0x01 缘由

     最近在结合linux tcp/ip协议栈,以及上层socket编程来进行相关学习,学习过程中发现一些有趣的东西,但是也想做做记录。于是有了这篇文章。
     tcp超时重传机制:https://baike.baidu.com/item/TCP%E8%B6%85%E6%97%B6%E9%87%8D%E4%BC%A0%E6%9C%BA%E5%88%B6/2122456?fr=aladdin

0x02 工具介绍

     翻译:http://www.brendangregg.com/blog/2014-09-06/linux-ftrace-tcp-retransmit-tracing.html
     工具下载:https://github.com/laoar/tcpretrans
     对网络数据包分析工具较多,有tcpdump、tcpretrans、wireshark等工具。下面介绍一款不太熟悉的工具tcpretrans。
     包的细节和内核状态,如下图:
      
     tcpretrans是一个perf-tools脚本集,并使用ftrace来动态地调用tcp_retransmit_skb()内核函数。这种处理方式的资源开销是可以忽略不计的。它仅仅加了一些指令到重传路径。它也使用一些linux内核特征,ftrace 和kprobes,甚至不需要内核debuginfo。
     它不会记录和过滤每个包,如果使用tcpdump/libpcap/kerbel-packet-filter方式,可能因为流量大而变得痛苦。用一个跟踪方式意味着能够挖掘内核状态,然而在线路上利用tcpdump是做不到这点的。仅实现了包含内核状态列,其他列可以定制。
     -s选项将包含一个内核堆栈跟踪,当导致重传时:
    
     这种情况发生在,当收到一个包(ip_rcv()),处理TCP ACK(tcp_ack()),然后通过tcp_fastretrans_alert()触发。下图是一个tcp快速重传:
     
     下面是基于定时器的超时重传:
     
      栈的回调情况,以及tcp_write_timer_handler()。基于时间定时器的回传的情况比快速重传的情况更糟糕,因为它们的应用基于定时器的延迟。在Linux中通常是1000 ms。
     tcpretrans是基于ftrace的一个修改,不能在没有任何修改的系统工作。(也不支持IPv6)。为了挖掘一些细节,像点分制IP地址,我真的应该开发一个款类似SystemTap的工具。然而,我想尝试仅仅使用ftrace去做,使其更简单的在我的(Netflix 云)上使用。
     基本流程:
     1.用kprobes添加指令到tcp_retransmit_skb(),并且读取%di寄存器。
     2.假设skb指针指向%di寄存器(为确定的知道需要利用内核debuginfo,通畅不会在生产环境中使用)。在非X86系统,可能是另外一个寄存器,那么这个脚本需要修改为相关寄存器。
     3.等待1秒。
     4.用skb指针读内核tcp_retransmit_skb()缓存事件。
     5.通过skb指针读/proc/net/tcp和socket cache。
     6.假设一个长时间的会话的重传发生时,这个会话的细节我们仍然可以在/proc/net/tcp中读到。
     7.分析tcp_retransmit_skb()内核缓存事件,打印已经从/proc/net/tcp中读取的重传事件的细节内容。
     8.Goto 3。

     下面是蘑菇街工程师为了定位tcp重传问题,改进的一个版本tcpretrans。
     

0x03 tcp重传产生的原因

     1、网络丢包,没有收到对方ack,导致确认丢包;
     2、网络延迟太大,导致超时重传;

0x04 源码解读

     kernel 2.6.32,本来想详细分析TCP拥塞状态机 tcp_fastretrans_alert()(传送:http://blog.csdn.net/shanshanpt/article/details/22202259)函数,但是考虑里面的内容,暂时搁置,先看自己熟悉的一块,对ack包的处理情况。tcp_ack函数:
    博客其他文章有对tcp流重组算法等做了一个说明,类似linux内核也需要考虑类型的场景,现在单独调试tcp_ack处理过程,进一步加深印象:
     1.调试环境准备,前面有几篇文章讲述了调试linux内核环境,下面开启设置断点到tcp_ack()
     2.通过ssh连接guest机器,进行三次握手,当客户端发送syn后,guest机器,需要回复ack,这样就进入我们设置的断点,函数栈调用信息如下:
     
     本来想自己好好去理解源码段,看到有人已经分析得比较清楚了,站在别人的基础上看待问题,是一种快速学习的手段。
     传送:http://blog.csdn.net/zhangskd/article/details/7103336

0x05 问题

     显示networking disabled 解决办法
     第一步,先把网络停掉
     sudo service network-manager stop
     第二步,清理对应的网络状态文件
     sudo rm /var/lib/NetworkManager/NetworkManager.state
     第三步,启动网络即可
     sudo service network-manager start

0x06 总结

     我有时在想,学习linux网络协议栈的用处有啥?但是想想大部分网络设备产品和服务端程序,都涉及到相关知识点,希望以此开阔自己的视野。
原创粉丝点击