lwip---(十)IP包分片与重装1

  较低版本的LWIP协议并不支数据包持数据包的分片与重装功能较高版本的LWIP协议,关于分片数据包的重装,采用了与标准协议中差别较大的方式来实现。它采用了一种更为简单的数据结构来组装分片包,但是这样导致的结果是组装过程源代码晦涩难懂,代码执行效率低。

  需要注意的是,在一般的嵌入式产品中,数据量是比较小的,基本不会出现数据分片的情况,而且嵌入式产品也不会在网络中充当路由器,实现数据包的重组、转发等功能。因此,较低版本的LWIP依然能在大多数应用中发挥其功能。

  首先,我们来看看标准协议中例举的BSD是怎么来实现数据包的重组的,再讲LWIP的重装机制与该机制对比讲解。

IP包分片与重装1《LwIP协议栈源码详解——TCP/IP协议的实现》

  标准中主要采用了两个数据结构来实现数据包的重装ipqipasfrag其中ipq是为某个将要重组的数据包建立的节点信息数据结构,包括目标IP地址,数据包编号等信息,该结构还有指针ipf_nextipf_prev使得该数据包的各个分片数据组成一个双向链表ipasfrag用于包装各个分片信息包。同时,将所有ipq结构体够成一个双向链表,便于某个ipq结构的查找、插入和删除操作。整个ipq构成的链表有一个固定的链表头,该表头不存储任何数据包的数据,只做标识用。

  在LWIP中,结构体ip_reassdata与上面的ipq起着类似的作用但结构成员大不相同ip_reassdata被用来构成的是单向链表,以实现对某个ip_reassdata结构的查找删除等操作。所有ip_reassdata结构构成的单向链表有一个头节点指针,即reassdatagrams,用于指向链表头。同时LWIP中没有类似ipasfrag的结构对收到的各个分片数据包进行封装,而是直接将分片数据包挂接在对应的ip_reassdata结构之后。至于怎样挂接,那是后话。

  现在来看看传说中的ip_reassdata结构,源代码:

struct ip_reassdata {
	struct ip_reassdata *next;    // 用于构建单向链表的指针
	struct pbuf *p;               // 该数据报的数据链表
	struct ip_hdr iphdr;          // 该数据报的IP报头
	u16_t datagram_len;           // 已经收到的数据报长度
	u8_t flags;                   // 是否收到最后一个分片包
	u8_t timer;                   // 设置超时间隔
};

  ip_reassdata主要用于描述一份正在被组装的数据报,完成数据包组装的函数叫做ip_reass,该函数以IP分片数据包为输入参数输出组装好的数据包指针或空指针,这里我们来看看它的操作流程

  首先判断该IP数据包的头部大小,目前LWIP不支持有IP选项的IP数据包,所以IP数据包头部大小只能为20个字节,如果大于该值,ip_reass直接丢弃该数据包后返回。

  然后,由于LWIP对所有ip_reassdata结构连接的pbufs个数总和有个上限限定,即IP_REASS_MAX_PBUFS,所以ip_reass检查当前数据包分片占用的pbufs个数,假设如果这么多个pbufs被连接在某个ip_reassdata后,所使用了的pbufs个数是否大于了我们刚才提的上限值,如果是大于,LWIP是不会允许这样的情况下将pbufs被连接入ip_reassdata的。此时用户可以有两种选择,一是直接删除数据包后返回二是删除ip_reassdata链表中生存时间最长的ip_reassdata结构体及其相关pbufs,直到有了足够的pbufs个数能使用。这种选择是通过宏定义IP_REASS_FREE_OLDEST来实现的。为什么要定义一个最大允许pbufs使用个数呢?不了解。

  到这步就可以进行分片数据报的插入操作了,首先要做的就是在链表reassdatagrams中找到对应的ip_reassdata结构体,此时又有两种情况:没有找到匹配的结构体,即该分片是第一个到来的分片,此时需要创建一个新的ip_reassdata结构体并插入链表reassdatagrams中;找到匹配的结构体,即在该分片到来之前已经有分片到来,此时要判断该分片是不是整个数据包的第一个分片,如果是,则用对应ip_reassdata结构体的iphdr字段记录该IP数据包头部。

  到这里,必然找到了分片数据包对应的ip_reassdata了,接着我们就判断该分片是不是整个数据包的最后一个分片包因为最后一个分片数据包的IP_MF位为0。如果是最后一个分片包,则设置对应ip_reassdataflag标志和datagram_len值,表示收到最后一个分片和设置整个数据包的长度值PS:第一个到来的分片包不一定是第一个分片包,最后一个到来的数据包也不一定是最后一个分片包。

  最后调用函数ip_reass_chain_frag_into_datagram_and_validate对分片数据包进行插入操作,这是我们后续会重点将的一个函数,先在这里打住。上面这个函数还会检测某个数据包是否被组装完毕,如果某个数据包被组装完毕,那ip_reass还要做以下工作:

  根据ip_reassdata结构找到第一个分片数据包,将ip_reassdata结构中的IP报头字段iphdr拷贝至第一个分片数据包头部(第一个分片数据包头部头部数据已经被覆盖,在讨论函数ip_reass_chain_frag_into_datagram_and_validate时我们会讲到),并重新计算校验和同时将第一个分片以后的各个分片数据包去掉头部信息,这个整个数据包就恢复出来了,然后将ip_reassdata结构从链表中删除,将重组后的数据包指针作为返回参数返回

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值