linux ip fragment,ip数据包分片函数ip_fragment分析

/*

*This IP datagram is too large to be sent in one piece.Break it up into

*smaller pieces (each of size equal to IP header plus

*a block of the data of the original IP data part) that will yet fit in a

*single device frame, and queue such a frame for sending.

*/

int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))

{

struct iphdr *iph;//ip头指针

int raw = 0;

int ptr;

struct net_device *dev;//网络设备指针

struct sk_buff *skb2;//新建一个skb的指针

unsigned int mtu, hlen, left, len, ll_rs;

int offset;//偏移

int not_last_frag;//不是最后一个分片

struct rtable *rt = (struct rtable*)skb->dst;

int err = 0;//出错?

dev = rt->u.dst.dev;//令dev等于在skb中的dev值

/*

*Point into the IP datagram header.

*/

iph = skb->nh.iph;//ip头指针指向skb中的ip头

if (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) {

icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,

htonl(dst_pmtu(&rt->u.dst)));

kfree_skb(skb);//貌似检查是否允许分片,如果不允许就直接释放skb,其他的几个没看懂

return -EMSGSIZE;//分组比接口的MTU(最大传输单元)大

}

/*

*Setup starting values.

*/

hlen = iph->ihl * 4;//计算ip头长度

mtu = dst_pmtu(&rt->u.dst) - hlen;/* Size of data space */

//这里的mtu应该是去掉ip头后,数据部分的最大长度

/* When frag_list is given, use it. First, check its validity:

* some transformers could create wrong frag_list or break existing

* one, it is not prohibited. In this case fall back to copying.

*

* LATER: this step can be merged to real generation of fragments,

* we can switch to copy when see the first bad fragment.

*/

//在skbuff.h中定义了如下#define skb_shinfo(SKB)((struct skb_shared_info *)((SKB)->end))

//在缓冲区数据的末尾,有一个数据结构skb_shared_info,

//它保存了数据块的附加信息。这个数据结构紧跟在end指针所指的地址之后(end指针指示数据的末尾)。

//下面是这个结构的定义:

//struct skb_shared_info {

//atomic_tdataref;

//unsigned intnr_frags;

//unsigned shorttso_size;

//unsigned shorttso_seqs;

//struct sk_buff*frag_list;

//skb_frag_tfrags[MAX_SKB_FRAGS];

//};

/***********************************

dataref表示数据块的“用户”数,这个值在下一节(克隆和拷贝缓冲区)中有描述。nf_frags,frag_list和frags用于存储IP分片。

skb_is_nonlinear函数用于测试一个缓冲区是否是分片的,而skb_linearize可以把分片组合成一个单一的缓冲区。组合分片涉及到

数据拷贝,它将严重影响系统性能。

需要注意的是:sk_buff中没有指向skb_shared_info结构的指针。如果要访问这个结构,就需要使用skb_info宏,这个宏简单地返

回end指针:

********************************/

if (skb_shinfo(skb)->frag_list) {

struct sk_buff *frag;//又定义了一个指向skb的指针,下面将会使用

int first_len = skb_pagelen(skb);

//skb_pagelen(skb)计算的就是skb中的自身数据的,函数如下

//static inline int skb_pagelen(const struct sk_buff *skb)

//{

//int i, len = 0;

//for (i = (int)skb_shinfo(skb)->nr_frags - 1; i >= 0; i--)

//len += skb_shinfo(skb)->frags[i].size;

//return len + skb_headlen(skb);

//}

/********************************************

在skb_pagelen里首先计算skb_shinfo(skb)->frags的长度,然后再加上skb_headlen()的值,得出的是什么呢?

是隶属于本skb的,不包含skb_shinfo(skb)->frag_list的skb数据的长度,也就是第一个skb的值了。这也就是为什么被命名为

first_len的原因

**********************************************/

if (first_len - hlen > mtu ||

((first_len - hlen) & 7) ||

(iph->frag_off & htons(IP_MF|IP_OFFSET)) ||

skb_cloned(skb))

goto slow_path;

//没看大明白,但从第一个来看,大于mtu就应当要分片了,

//goto slow_path应当为分片

//下面的for循环遍历frag_list指向的sk_buff结构的链表,

//检查其是否需要分片,如果需要分片,就goto slow_path

//我认为可能是为了保证每个分片都足够小,不超过mtu,

//应为不同网络要求的mtu不一定相同,可能存在重复分片的情况,所以要检查一下

//个人观点,仅供参考

for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {

/* Correct geometry. */

if (frag->len > mtu ||

((frag->len & 7) && frag->next) ||

skb_headroom(frag) < hlen)

goto slow_path;

/* Partially cloned skb? */

if (skb_shared(frag))

goto slow_path;

}

/* Everything is OK. Generate! */

//相关数据的初始化

err = 0;

offset = 0;//初始偏移为0

frag = skb_shinfo(skb)->frag_list;

//frag设置为指向frag_list的第一个的指针

skb_shinfo(skb)->frag_list = NULL;//原来的设为NULL

skb->data_len = first_len - skb_headlen(skb);//数据部分长度

skb->len = first_len;//ip数据包长度

iph->tot_len = htons(first_len);//总长度

iph->frag_off |= htons(IP_MF);//设置分片的指示位

ip_send_check(iph);//头部校验

for (;;) {

/* Prepare header of the next frame,

* before previous one went down. */

if (frag) {

//如果frag非空,设置frag指向的skb结构

frag->ip_summed = CHECKSUM_NONE;

frag->h.raw = frag->data;

frag->nh.raw = __skb_push(frag, hlen);

memcpy(frag->nh.raw, iph, hlen);

//复制ip头部

iph = frag->nh.iph;//指向新的ip头

iph->tot_len = htons(frag->len);

ip_copy_metadata(frag, skb);

//关于ip_copy_metadata函数,百度之据说是复制其他一些关于skb的设置

if (offset == 0)//偏移为0,表示为第一个分片包

ip_options_fragment(frag);//一般第一个分片包会加入一些选项

offset += skb->len - hlen;//计算下一个offset

iph->frag_off = htons(offset>>3);

//因为ip包中的offset一位表示一个字节,因此要右移3位,扩大8倍

if (frag->next != NULL)//如果不是最后一个分片,设置MF位为1

iph->frag_off |= htons(IP_MF);

/* Ready, complete checksum */

ip_send_check(iph);

}

err = output(skb);//发送函数

if (err || !frag)//如果出错或frag为空

break;

skb = frag;//frag赋予skb

frag = skb->next;//frag指向下一个

skb->next = NULL;//清除skb于frag的联系

}//for循环结束

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值