IP分片分析 - 弦外之音

/ 0评 / 0

我们知道 经典的网络协议栈是分层设计的。

MAC 层 -> IP 层 -> TCP 层
MAC 层 -> IP 层 -> UDP 层

上面的 MAC 层 也叫以太网层,MAC + IP 层主要是为了解决 路由器,交换机的问题,处理的是数据传输的中间过程,例如 IP 层通过 路由协议 查找 radix 树之类的操作,决定数据包下一跳 发到哪个 路由器,然后数据转成 一串比特流送入网络 ,到达下一个路由器。

实际上,MAC层 跟 IP层耦合比较严重,IP 层 本来是 解决 Client Server 的问题,IP 层应该不用太关注 数据传输的中间过程,只需要知道 双方 IP 即可发送数据。

但是 IP 包的下一跳 又由路由协议,ARP表等决定,数据的传输中的路由器,各个路由器会把 数据包的 mac 地址不断更改为 下一个 路由器的 mac地址,然后数据转成 一串比特流送入网络 ,到达下一个路由器。但是数据包里面的目标IP地址不能变,改变的只是mac 地址。

所以 MAC 层 + IP 层,合起来就解决了 数据传输的中间过程。

那TCP 跟 UDP 是解决什么问题?TCP 跟 UDP 协议有端口字段,这两个协议用来区分 一台机器里面的不同服务程序。监听不同端口,实现不同服务。

TCP 比较重量级,里面实现了很多算法逻辑。

UDP 是轻量级的,留给用户层自定义,当初预留 udp 这么一个 socket的设计是非常精妙的,让调用层不需要重新编译内核就能基于 udp 自定义一个更适合特定的协议栈,早期 DPDK 还不成熟。



了解了这些网络背景之后,现在正式分析一下 IP 分片的情况。

IP 层的分片对于 TCP,UDP 来说是透明的,也就是说,你用 TCP socket,UDP socket 是感受不到这个分片的。

首先为什么需要分片?

这是因为,MAC 层,以太网的传输单元是有上限的,这个上限叫 MTU,全称 Maximum Transmission Unit (最大传输单元)。

如何查看 机器的 MTU?

window10 可以通过命令 netsh interface ipv4 show subinterfaces 查看 MTU ,如下:

linux 可以使用 ifconfig 命令查看网卡的 MTU,如下:

从上图可以看出,我的 ubuntu 的 MTU 是 1500 个字节。单位是字节。也就是他在 MAC 层发出来的数据包最大只能是 1500 个字节。

通常情况下 MAC 层的MTU 都是 1500 个字节,但是不排除早期一些路由器的 MTU 少于这个值

因为 MAC 最多只能发 1500 个字节,那如果我们 UDP 发送超过 1500个字节的数据会怎样的,下面就来测试一下。

我用 C 语言写一个UDP server,项目地址:百度网盘,提取码:1dyb 。

然后使用 ncat -vv 192.168.0.123 5189 -u 命令模拟客户端发送 udp 数据。

我在客户端 ncat 输入 11-aaa....aaa-22,中间是重复的 a 字符,一共 2004个字母,也就是发送2004字节的数据,如下:

然后利用 wireshark 抓包,输入 host 192.168.0.123 抓取 跟 192.168.0.123 有关的数据包,这个语法可以抓ARP,TCP,UDP,都行。

从上图可以看到,2004 字节的 数据包 被分成了两个 IP包进行发送。

这也是 UDP 的一个缺点,UDP 不像 TCP ,TCP 内部有一个算法,会找出传输路径中最小的MTU,然后基于这个 MTU 进行切分,避免在 IP 层分片,因为如果在 IP 层分片,一个IP分片丢失,会导致整一块数据重新传输。

这里用 UDP 举个例子,我们之前发送了一个 2004 字节的UDP数据包,被 IP 层分片了,分成两个IP包。IP A包 1480字节(IP头部占了20字节),IP B包 524字节。

首先,UDP 是没有 ACK 机制的,数据不可靠,我们肯定需要自己实现 ACK,我们给 这个 2004的 udp包打一个标示 ID 等于 1,如果规定时间没收到 对面发来的 ACK-1 ,就认为UDP丢包了,需要重新发送 2004 字节的UDP包。

但是实际的丢包情况是这样的,2004字节的UDP包被分成了 两个 IP 包,IP A包 1480字节(IP头部占了20字节),IP B包 524字节,实际的传输过程是,IP A包已经到达对面机器了,丢失的只是 IP B包,本来只需要重传 IP B包 524 个字节就够了。但是由于 IP 层的分片是透明的,UDP 层无法操作他的分片逻辑,UDP 层也无法知道 IP 层是否发生分片,这种需要着重讲一下,IP 层的分片可能发生在本机上,也可能在中间的路由器里发生分片,之前讲过 每个机器网卡的 MTU 可以不一样,有些机器的 MTU 就是比较小。

由于 IP 层的分片是透明的,导致本来只需重传 524 字节,最后要重传 2004 字节,所以 IP 层这个分片功能非常鸡肋。TCP 是怎么解决这个问题?TCP 三次握手会协商沟通,知道双方网卡的最小MTU,TCP基于这个 最小 MTU 内部自己做分片,避免 IP 层进行分片。TCP 也只是避免分片,他不能完全消除IP分片,因为路径中的最小MTU,TCP 不知道,TCP 协商只知道双方的MTU。

TCP 里面的分片 实际上 叫 MSS,全称 Maximum Segment Size,最大报文长度。是基于 MTU 实现的。

PS:一种通路MTU发现技术可以帮助 TCP 找出传输路径中 最小的 MTU 。但是,大多数 TCP 没有这样实现,是因为 互联网是动态路由选路,IP 包从 本机发出来,再到 对面的机器,中间的路由路径会随时产生改变,互联网里面的路由器是动态选路,尽量选出最优的,最快的路径。所以 TCP 不太好找出路径中最小的 MTU。

当然,严谨来说,也不是最小的MTU,而是基于 路径中的 MTU 信息决策出最佳的 MSS,这种说法有点难懂。但是 由于路径经常变,TCP 没法掌握全面的路径MTU信息,所以就无法找出最佳的MTU,直接简单点,用双方最小的MTU来搞。

所以如果用 UDP,也需要像 TCP 这样实现一套内部切片规则,尽量避免 IP 层进行分片。

补充,ping -l 4000 192.168.0.1 命令也可以导致 IP 分片,这个命令是发送 ICMP 数据包,同时带4000的data数据


©版权所属:知识星球:弦外之音,QQ:2338195090。

由于笔者的水平有限, 加之编写的同时还要参与开发工作,文中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如果读者有任何宝贵意见,可以加我微信 Loken1。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注