LwIP协议栈之IP协议

IP数据报头结构

IP层主要为信息包的接收分片数据包重装信息包的发送和转发三个内容。
IP数据报头结构如下所示,通常的IP数据报头长度为20个字节。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RW7SzZ6w-1596445349890)(images/14.jpg)]

  • 第一个字段是4bit的版本号,对于IPv4,该值为4;对于IPv6,该值为6;
  • 第二个4bit字段用于记录首部长度,以字为单位。对于不含任何选项字段的IP报头,则该长度值为5,其能描述的最大IP报头长度为15*4=60字节。
  • 对于8bit的服务类型字段,该字段主要用于描述该IP数据包急需的服务类型,如最小延时、最大吞吐量、最高可靠性、最小费用等。
  • 16位的总长度字段描述了整个IP数据报,包括IP数据报头的总字节数。理论上来说总长度最大可达65535字节,但底层链路不允许这么大的数据包出现在链路上,故在链路层往往会对大的IP数据包进行分片。
  • 16位标识字段用于标识IP层发送出去的每一份IP数据报,每发送一份报文,则该值加1.
  • 3位标志和13位片偏移字段用于在IP数据包分片时使用,LWIP的较高版本才支持IP分片功能;
  • TTL字段描述该IP数据包最多能被转发的次数,每经过一次转发,该值会减1,当该值为0时,一个ICMP报文会被返回至源主机;
  • 8位协议字段用来描述IP数据包是来自上层的哪个协议,该值为1表示为ICMP协议,该值为2表示ICMP协议,该值为6表示TCP协议,该值为17表示UDP协议;
  • 16位首部校验和只针对IP首部做校验,不关心其内部数据在传输过程中出错与否,对于数据的校验是上层协议负责的,如 ICMP、 IGMP、 TCP、 UDP 协议都会计算它们头部以及整个数据区的长度。
  • 最后是两个32位的IP地址。最后一个字段是任选字段,不同的协议会选择性的使用该字段。

IP数据报头源码分析

在LWIP中,使用结构体ip_hdr来描述IP数据报头的:

struct ip_hdr {
	PACK_STRUCT_FIELD(u16_t _v_hl_tos); // 前三个字段:版本号、首部长度、服务类型
	PACK_STRUCT_FIELD(u16_t _len); // 总长度
	PACK_STRUCT_FIELD(u16_t _id); // 标识字段
	PACK_STRUCT_FIELD(u16_t _offset); // 3 位标志和 13 位片偏移字段
#define IP_RF 0x8000 //
#define IP_DF 0x4000 // 不分组标识位掩码
#define IP_MF 0x2000 // 后续有分组到来标识位掩码
#define IP_OFFMASK 0x1fff // 获取 13 位片偏移字段的掩码
	PACK_STRUCT_FIELD(u16_t _ttl_proto); // TTL 字段和协议字段
	PACK_STRUCT_FIELD(u16_t _chksum); // 首部校验和字段
	PACK_STRUCT_FIELD(struct ip_addr src); // 源 IP 地址
	PACK_STRUCT_FIELD(struct ip_addr dest); // 目的 IP 地址
} PACK_STRUCT_STRUCT;

之前讲过,从以太网底层进来的数据包经过 ethernet_input 函数分发给 IP 模块或者 ARP模块,分发给 IP 模块是通过调用 ip_input 函数完成的,当然在递交前, ethernet_input 需要将数据包去掉以太网头。
现在来看看数据包传递给ip_input后,该函数进行了哪些方面的工作。

  1. 检查 IP 头部的版本号,如果该值不为 4,则立即丢弃该数据包。更高版本的 LWIP 协议栈可以支持 IPv6。
  2. 接下来函数检查 IP 数据报头是否只保存于一个 pbuf 中,如果不是 ,也直接丢弃该 IP 包,这是因为 LWIP 不允许 IP 数据包头被分装在不同的 pbuf 里面。
  3. 同时,函数检查 IP 报头中的总长度字段是否大于递交上来的数据包总长度,如果是,则说明存在传输错误,直接丢弃数据包。
  4. 然后利用函数inet_chksum对IP数据报头做校验,如果不通过则直接丢弃数据包;
  5. 接着需要对数据包进行截断操作,按照IP包头记录的总长度字段截取数据包,因为经过ethernet_input传递上来的数据包只被去除了以太网数据包头部,而对于可能存在的以太网填充字段和一定存在的以太网校验字段没做处理,截断之后得到完整的IP数据包。
  6. 然后函数检测IP数据包中的目的IP地址是否与本机的相符,本机的IP地址是保存在netif结构体变量中的,由于一个系统可能有多个网卡设备,意味着本机有着多个IP地址,这些netif结构体是被连接在netif_list链表上的。ip_input函数会遍历链表上的netif结构以找到匹配的IP地址,并记录该netif结构体变量,即记录该网卡;
  7. 再接下来,根据目标IP地址判断数据包是否为广播或多播IP数据包,LWIP不对这些类型的数据包进行响应。
  8. 之后就是ip_input最复杂最难理解的部分,即IP分片数据包的重装, ip_input 函数通过数据包的 3 位标志和 13 位片偏移字段判断发给自己的该 IP 包是不是分片包,如果是,则需要将该分片包暂存,等到接收完所有分片包后,统一将整个数据包递交给上层应用程序。
  9. 最后,ip_input函数根据IP数据包头内部的协议字段判断该数据包应该被递交给哪个上层协议,并调用相应的函数递交数据包。若是UDP协议,则调用udp_input函数;若是TCP协议,则调用tcp_input函数等。若都不是,则调用函数icmp_dest_unreach返回一个协议不可达ICMP数据包给主机,同时删除数据包。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值