对网络比较熟悉的童鞋都知道,当发送的ip报文长度超出了最大的传输单位MTU,且允许分片的情况下,就会对ip报文进行分片。在上层要发送数据时就会调用dst_output,dst_output就会调用ip_output,而ip_output就会调用ip_finish_output,在ip_finish_output把数据发送出去之前就会判断该报文是否进行分片。
static int ip_finish_output(struct sk_buff *skb)
{
if (skb->len > ip_skb_dst_mtu(skb) && !skb_is_gso(skb))
return ip_fragment(skb, ip_finish_output2);
else
return ip_finish_output2(skb);
}
从源码中可以看出,当报文的长度大于mtu,gso的长度不为0就会调用ip_fragment进行分片。否则就会调用ip_finish_output2把数据发送出去。
ip分片目前有两种分片方式:1、快速分片;2、慢速分片。在快速分片中,将数据分割成片段已经由传输层完成,三层只需将这写片段组成ip分片;而慢速分片则需要完成全部的工作,即对一个完整的ip数据报根据mtu值循环进行分片,直至完成。整个分片工作都在ip_fragment中完成。
int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
{
.......
struct rtable *rt = skb_rtable(skb);
int err = 0;
dev = rt->u.dst.dev;
......
/*
* 如果待分片IP数据包禁止分片,则调用
* icmp_send()向发送方发送一个原因为需要
* 分片而设置了不分片标志的目的不可达
* ICMP报文,并丢弃报文,即设置IP状态
* 为分片失败,释放skb,返回消息过长
* 错误码。
*/
if (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) {
IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
htonl(ip_skb_