惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Forbes - Security
Forbes - Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
P
Palo Alto Networks Blog
Martin Fowler
Martin Fowler
T
Threatpost
D
Docker
S
Schneier on Security
M
MIT News - Artificial intelligence
G
Google Developers Blog
L
LINUX DO - 热门话题
J
Java Code Geeks
月光博客
月光博客
博客园 - 三生石上(FineUI控件)
IT之家
IT之家
博客园 - Franky
C
Cyber Attacks, Cyber Crime and Cyber Security
K
Kaspersky official blog
Google DeepMind News
Google DeepMind News
N
News and Events Feed by Topic
V
Vulnerabilities – Threatpost
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
人人都是产品经理
人人都是产品经理
Spread Privacy
Spread Privacy
T
Tailwind CSS Blog
爱范儿
爱范儿
阮一峰的网络日志
阮一峰的网络日志
U
Unit 42
C
CERT Recently Published Vulnerability Notes
The GitHub Blog
The GitHub Blog
Simon Willison's Weblog
Simon Willison's Weblog
NISL@THU
NISL@THU
MongoDB | Blog
MongoDB | Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
H
Heimdal Security Blog
Recorded Future
Recorded Future
云风的 BLOG
云风的 BLOG
SecWiki News
SecWiki News
P
Privacy International News Feed
P
Proofpoint News Feed
O
OpenAI News
B
Blog
腾讯CDC
F
Full Disclosure
Apple Machine Learning Research
Apple Machine Learning Research
T
Tor Project blog
H
Hacker News: Front Page
Project Zero
Project Zero
Hugging Face - Blog
Hugging Face - Blog
C
Cisco Blogs
S
Security Affairs

卡瓦邦噶!

服务器高性能网络调优 | 卡瓦邦噶! 为何写作 | 卡瓦邦噶! 读《金阁寺》 | 卡瓦邦噶! 雨季又来 | 卡瓦邦噶! MTU Probe 引起的初始延迟 | 卡瓦邦噶! 3.5 秒的固定延迟问题 | 卡瓦邦噶! 学习网络的一点经验 | 卡瓦邦噶! ARP 问题诊断 | 卡瓦邦噶! 网络断断续续…… | 卡瓦邦噶! Piccolo P2P 镜像分发 | 卡瓦邦噶! 一起看电影 | 卡瓦邦噶! 《征服C指针》 | 卡瓦邦噶! 我的姥姥 | 卡瓦邦噶! Python的哲学 Python 3.5的新特性 学校不教的计算机课 垃圾回收(GC)的三种基本方式 在编程中体验纯粹的快乐 从《美丽新世界》谈自由 在快钱实习 迷人的嗓音和迷人的故事——《Sleepyhead》 Python 的十个自然语言处理工具 记一个愚蠢的bug 一年炉石传说的游戏体验 《以撒的结合:重生》网页版图鉴 分清 C++的指针、引用和数组 笑话三则 自由比皇帝更伟大——《悲惨世界》笔记 Git 10 周年访谈:Linus 讲述背后故事 用 0x3f3f3f3f 设定最大int值的优点 Joel给计算机系学生的建议 一个词法分析器的简单实现 怎样才算健康的生活方式 MacVim 配置攻略 学习培训课程的视频效果好吗? 语言的控制 可悲的大多数 CSS样式思维导图 2014年终总结 奥巴马成为首位写程序的美国总统 青岛老城区的下水道好在哪里? 做优秀 UI 的七个建议(第二部分) 选择爱情的骑士 Git简明教程 Java集合总览 读 《1984》 java问答:终极父类(六)——等待/唤醒和接口 Java 问答:终极父类(五)——toString() Hyperlapse快速视频背后的技术细节 平庸之恶 Java程序员须知的七个日志管理工具 苏州 逛书摊随想 关于考试作弊 你的工作不仅仅是编程 推荐在线学习Java的英文资源 关注女性命运——《千禧年三部曲》 用好你的幻灯片——《演说之禅》 使用ReentrantLock和Lambda表达式让同步更纯净 新手学编程,从哪里开始? 程序员都是工程师吗? 不要学习代码,要学会思考 Java 问答:终极父类(四)——hashCode() Java 问答:终极父类(三)——finalize()和 getClass() 程序员职业之路的选择 我是一名摄影家 写给何小树的城市指南 为什么一些语言会比别的快? Java的常见误区与细节 跟朋友在一起玩游戏 死神永生——读《三体》 五种类型的程序员 创业圣经——读《黑客与画家》 纪念加西亚·马尔克斯 Junit中处理异常的另一种方式:catch-exception Java8采用Martin Fowler的方法创建内部DSL Linux HotSopt虚拟机GC线程的CPU占用率 J2EE概念介绍 如何成为一名黑客 Java 问答:终极父类(二)——equals()方法 为什么我喜欢Java Java 问答:终极父类(一)——clone()方法 七个改变世界的Java项目 java中默认类型转换的小问题 传统与创新 欢迎来到互联网 莫言和马尔克斯——读《生死疲劳》 位运算的妙用 读《人为什么活着》 写博客教会我的事情 中国特色操作系统 2013年总结 《永不妥协》影评 恨不相逢未嫁时——《廊桥遗梦》影评 如何优雅地使用PPT 给明年依然年轻的我们 天才与柱子 黑客守则和黑客精神 Looking for Freedom——《被解救的姜戈》 简洁之道
探测 TCP 乱序问题
laixintao · 2024-03-05 · via 卡瓦邦噶!

TCP 协议是基于 IP 协议的。IP 协议不保证顺序,只能说尽力保证包的顺序。如果发生乱序,TCP 的性能就会下降很多。最近就遇到一个 TCP 下载速度很慢的问题,抓包分析发现有很多乱序的包。

网络发生了乱序,那就把锅甩给网络组的同事,但那不是我的风格。虽然我没有二层和三层设备的权限,无法排查哪里出了问题。但是我感觉我可以只从 TCP 的两端,探测出来乱序发生在哪一个节点上。

traceroute 的原理

traceroute 是一个很好的网络工具。之前在 mtr 的教程中也介绍过。

traceroute 的原理很精妙。它利用了 IP 协议本身的特性:每一个 IP 包都有一个 TTL 字段,表示这个包还能在网络中被转发多少次,每次路由器转发一个 IP 包就将其 -1,如果一个路由器发现 IP 包 -1 之后是 0 了,就直接丢弃,并且给 IP 包的 Source IP 发送一个 ICMP 包(包含此 hop 自己的 IP),说这个包气数已尽,无法送达目的地。

traceroute 原理

traceroute 想要知道去往另一个 IP,中间都会经过哪些 IP 节点。它发送一个 TTL=1 的 ping 包出去,包会挂在第一个 hop 上,第一个 hop 发回去 ICMP,traceroute 就知道了第一个 hop 的 IP 地址;它再发送一个 TTL=2 的 ping 包,就知道了第二个 hop 的 IP 地址…… 直到收到正确的 ping 响应,就算到头了。

mtr 类似于一个 traceroute + ping 的工具,还能告诉我们中间每一个 hop 的延迟。原理其实和 traceroute 是一样的,以下用 mtr 举例。

路由 path 的一致性问题

回到乱序的问题,最常见的问题是,中间某一跳到下一跳有多个节点可以选择。这时候,正确的情况下,对于 (src ip, src port, dst ip, dst port) 这样的四元组,应该固定走一条线路。就像 ECMP 的 hash 一样

ICMP Ping 没有端口的概念,但是对于 (src ip, dst ip),路线应该是一致的。即,我们在对某一个 IP mtr 的结果中,每一跳都应该只有一个唯一 IP。

mtr with ICMP, 每一跳都只有一个 IP

mtr 有 tcp traceroute 的功能,我用 tcpdump 看了下,原理和 traceroute 相同,只不过 IP 包的内容不是 ICMP 了,而是 TCP。路由器丢了 IP 包,不管里面是 TCP 还是 ICMP,都会发回来 ICMP 消息。于是 tcp traceroute,我们就可以用 tcp 包探测出来中间的转发节点。mtr 使用的 tcp 包是 syn 包,remote port 可以指定,但是 local port 每一个包都是随机选择的。这就导致,每一次发起探测,都是一个不同的四元组:(src ip, src port, dst ip, dst port) ,因为 src port 每次都在变,所以路径每次都是不一样的。这样的话相当于可以将路径中所有的路由设备 IP 都探测出来。

mtr with TCP, 每一跳可能有多个 IP

如图可以看出,一跳可能有多个 IP。

TCP multipath 探测

但是正确网络中的 TCP,如果 local port 也不变,那么相同的四元组,应该走同一条路,来尽量保持到达顺序和发送顺序一致。

利用这个原理,假设我们使用的 local port 一直保持不变,那么 TCP traceroute 的结果应该是,每一跳都只有一个 IP

mtr 没有提供固定 local ip 来 trace 的功能,我用 scapy 写了一个。

tcp multipath 探测

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

from scapy.all import *

hostname = "10.0.0.1"

# add timeout

for i in range(1, 20):

    pkt = IP(dst=hostname, ttl=i) / TCP(sport=22334, dport=9100)

    # Send the packet and get a reply

    reply = sr1(pkt, verbose=0, timeout=5)

    if reply is None:

        print("%d hop ??" % i)

        continue

    elif TCP in reply:

        # We've reached our destination

        print("Done!", reply.src)

        break

    else:

        # We're in the middle somewhere

        print("%d hops away: " % i, reply.src)

代码非常简单,就是发送 ttl 不同的包,然后检查收到的 ICMP 包。限制最多 20 次,如果 20 次还没到达终点就放弃。如果 reply 里面有 TCP 包,说明收到了正常的回复(即使是 RST 也算)。

另外,因为我们每次都用同一个 local port,所以不能用多线程,每次只能有一个探测存在。

实际测试发现,收到的 ICMP 响应并不是很稳定,有的时候 hop 4 丢了,有的时候 hop 5 丢了。可以使用一个 bash 循环多测试几次。

Shell

1

while true; do sudo python3 tra.py  >> traceroute.log; done

跑一段时间之后,我们可以查看这个日志文件,过滤掉错误的包 (??),然后排序之后,去重。理论上,每一跳都应该只有一个 IP 才对,如果有某一个 hop 出现了两个 IP,那说明就是出现了 multiple,会知道乱序。

1

2

3

4

5

6

7

8

9

10

$ grep -v '??' traceroute.log | grep -v Done | sort | uniq

1 hops away: 10.50.20.3

2 hops away: 10.60.40.50

3 hops away: 10.70.80.90

4 hops away: 10.80.70.60

5 hops away: 10.90.80.70

5 hops away: 10.90.80.71

6 hops away: 10.100.110.120

7 hops away: 10.110.120.130

8 hops away: 10.12.130.14

如上输出,在 hop 5 出现了两个 IP,说明是 hop 4 到 hop 5 的时候出现了 multipath。

在得到了这个结果之后,去和网络组的同事做了确认。结果发现这个不是导致问题的根因。不过觉得挺有意思的,稍作记录一下。

小技巧:traceroute 只能测一个方向,有些情况下去的方向和回来的方向可能不是同一个路线。所以无论是 traceroute 还是 tcp traceroute,不妨从两端都试试。说不行会发现新的线索。