目录
一:概述
IP协议是TCP/IP协议簇的核心协议,处于链路层之上,传输层之下。IP层处理底层接收的ip数据包,封装上层协议数据包,并进行发送。(所有tcp,udp,icmp以及igmp协议都最终封装进ip包,以ip包的格式进行发送。)
二:数据结构
参见数据结构部分。
三:函数接口
ip.c文件是ip协议的主要实现文件。
函数名:ip_init(void)
功能:初始化ip层
操作:暂为空
函数名:ip_route(struct ip_addr *dest)
功能:根据给定的ip地址,找到相关的网络接口。
操作:该函数线性遍历网络接口链,如果给定ip掩码化之后等于某个掩码化后的网络接口ip地址,则匹配成功。返回接口。如果找不到,则返回默认接口
函数名:ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
功能:转发ip包。该函数只有在ip转发选项支持时才可使用。
操作:函数首先调用ip_route寻找输出网络接口,如果没有找到,则返回空。如果找到的接口与数据包进来的接口是同一个接口,则同样返回空接口。否则,将包的ttl减小一。如果ttl变为零,并且包类型不是icmp包,则调用icmp_time_exceeded发送icmp超时消息,同时返回空接口。如果以上过滤条件都通过,则重新调整包的校验和,然后使用刚才找到的接口发送出去。
函数名:ip_input(struct pbuf *p, struct netif *inp)
功能:处理输入的ip数据包
操作:首先将包头拷贝到本地(字节对齐)。之后,对包头进行验证:版本号是否合适;长度是否正确;校验和是否正确。当这些验证都通过后,进行接口的过滤。这里处理单播包,广播包以及组播包。如果是组播包,并且接口不为空,则判断该组播地址是否属于该接口加入的组播组。如果是,则敲定该接口,继续向下执行,否则,释放包,返回。如果是单播包,并且接口为空,则调用ip_forward进行转发。(上述过滤实现的功能为:如果进入的数据包为单播包,没有为其找到匹配的接口,则将其转发;如果进入的是组播包,没有为其找到匹配的接口,则释放;如果进入的是广播包,没有为其找到匹配的接口,则同样释放)。
进行到这里,说明我们已经为输入的数据包找到匹配的接口了。下一步,我们首先需要判断该ip包是否包含分片,如果包含,并且协议栈支持分片,则调用ip_reass进行组装。如果还有分片未收到,则返回,等待接收候选分片,否则,准备将数据包分发给上层:原始套接字数据包输入的处理;udp包输入的处理;tcp包输入的处理;icmp包输入的处理;igmp包输入的处理。对于非上述数据包类型,并且是非广播或组播包,则调用icmp_dest_unreach发送icmp目的端口不可达错误,释放pbuf。
函数名:ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
u8_t ttl, u8_t tos,
u8_t proto, struct netif *netif)
功能:在给定的网络接口上发送数据包
操作:根据参数填充ip包头的相关信息(这里,如果数据包是重传包,则不必重复设置),如果需要分片,则调用ip_frag进行分片操作。最后调用底层接口发送数据包。
函数名:ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
u8_t ttl, u8_t tos, u8_t proto)
功能:ip层的包输出接口
操作:调用ip_route函数选择输出的网络接口,然后调用ip_output_if完成ip包头的设置与数据的发送。
函数名:ip_debug_print(struct pbuf *p)
功能:打印ip层的调试信息
操作:
ip_frag.c:IP分片的实现文件。
函数名:copy_from_pbuf
功能:从pbuf的offset处拷贝len长字节的数据到buffer
操作:这里调用了memcpy函数
函数名:ip_reass_tmr
功能:重组定时器函数
操作:
函数名:ip_reass
功能:将输入的ip分片组装成完整的ip数据包
操作:首先判断ip定时器,如果为零,则说明缓冲中不存在数据包,此时我们将包头拷贝到缓冲中,并将定时器值设为预定的最大值,同时清除所有的映射位。
如果输入的分片与当前缓冲中的数据包分片匹配,则进行下面的分片组装操作:
找出组装缓冲中我们应该拷贝数据的偏移位置
如果偏移位置或者偏移位置与分片的大小的和超出了组装缓冲,则丢弃整个包
拷贝分片到组装缓冲中,从正确的偏移位置开始
更新映射位图(?)
如果是最后一个分片,设置最后一个分片标识,并计算整个包的长度
最后,我们检查是否将整个包放入缓冲中,这可以通过确认最后一个分片标识置位并且所有的映射位都被设置来确认。
确认整个包都缓冲下来后,分配一个pbuf pool 类型的pbuf,将缓冲中的数据拷贝到该pbuf中,返回该pbuf
函数名:ip_frag
功能:对过大的ip数据包进行分片,也就是ip_reass的反函数
操作:首先分配一个pbuf ref类型的pbuf,并将其total len设置为最大传输单元
将ip头拷贝到其中
While 还有剩余数据
如果剩余数据长度小于 mtu-ip_hdr,则将最后一个分片标识置位
设置新的偏移和标识
填充该分片,也就是将pbuf中的数据拷贝到分片缓冲中
如果是最后一个数据分片,则将pbuf ram 的长度减小的以适合剩余数据大小
分配一个pbuf ram 类型的pbuf,只是分配一个pbuf结构体作为头部
将该头部与拷贝的数据组装在一起通过底层接口发送出去
修正剩余数据大小,跳到循环处继续执行
回到While继续判断处理