背景

记录网络分片相关的笔记,供参考,随性更新。。。

笔记

1、当上层发送的数据包大小超过mtu时,会进行分片(或分段)

2、通常情况下网络包分片(分段)在传输层(level 4,TCP或UDP)进行,即根据mtu大小将数据包分成mtu大小的段,然后然发送。

3、当传输层分段后,下发的数据包大小就肯定小于MTU了,此时IP层就不会再分片了。

问题:那什么情况下会在IP层分片呢?内核中有IP层分片的代码,不可能不用吧? 有如下情形:

  1. 在IP层进行转发时,当下一条的mtu比较小时,此时会在IP层分片。
  2. 使用IPSEC时,此时也可能在IP层分片。
  3. 内核中的某些bug导致。案例见后面。

4、tcp是基于流的传输协议。当上层发小包时,通常会在tcp层将小包数据组合成大包,并根据mtu对包进行分段,将其切割成mtu大小的数据包;

当上层发送大包时(大于MTU),则会直接进行分段。分段后的数据放在skb中,每个skb对应一个数据包,并链入sock结构的链表中。

5、内核中的sock对应于一个应用层的socket,每个sock包含一个skb的链表,每个skb中包含一个数据包,一个sock上发送的所有数据包逻辑上被看做放入了sock的发送缓冲区中(wmem),当向该sock对应的socket发送数据时,数据包被放入相应的发送缓冲区中,发送缓冲区大小增加。当skb被发送出去时,相应的发送缓冲区大小减少,最少为1,因为sock创建时会将其大小置为1,只有当sock被释放,同时发送缓冲区大小为0时,该sock才能被释放。

6、skb->frag_lists用于存放IP层的分片。由于通常在传输层已经分好,所以这里通常装的是传输层分号的数据包。具体原理为:

tcp/udp进行分段后,数据包被传入IP层,IP层会从sock的skb链表中取出第一个skb,然后将剩下的所有skb依次链入skb->frag_lists中,所以sock中的skb链表到IP层后,其实就只有一个skb了,其它的数据都放入这个skb的frag_lists中了。

在这个过程中,IP层还是会检查每个skb的大小,如果大于mtu,同时第一个skb中又没有设置UFO或GSO标志时,此时则会在IP层进行分片。

但会出现这样的情况么?每个skb不是都在传输层进行过分段了吗?怎么会出现大于Mtu的情况?

是的,正常情况下,的确不会这样,但如下场景中(其实是内核的一个bug)就会出现: 在开启UFO或GSO的情况下,先发送一些列的小包(比如1200,mtu为1500),然后再发送一个大包(1600),此时前面都没有带UFO或GSO标记,但最后一个大包却带上了UFO或GSO标记,在IP层将skb合并时,由于:

  1. 第一个skb中没有UFO或GSO标记,所以不会将大包继续下发到底层;
  2. 最后一个包大于mtu,此时IP会检查到,然后在IP层对齐进行分片。