LWIP协议栈:IP协议

1. IP协议概述

IP 协议(Internet Protocol),又称之为网际协议,属于网络层。IP协议以IP地址作为唯一识别码,负责将数据从源主机发送到目标主机。

IP 协议是一种无连接的不可靠数据报交付协议,协议本身不提供任何的错误检查与恢复机制。 

1.1 IP地址

在互联网中,每一个主机都有一个唯一的IP地址作为身份识别标志。

(1)分类编址

IP地址可分为五类:A类、B类、C类、D类、E类,其组成如下图。

   各类IP地址的特点,如下图。

   (2)特殊IP地址

特殊IP地址用于特殊用途,不能分配给任何一个网络的主机使用。

受限广播地址  网络号、主机号全为1的地址(255.255.255.255),表示整个互联网内的主机。但是由于路由器禁止转发IP地址为255.255.255.255的数据包,这样的数据包只会在局域网内广播。
直接广播地址  主机号全为1的地址,表示局域网内的所有主机,
多播地址  D类地址属于多播地址,一个发送者,多个接收者。D类地址只能作为目标IP地址,不能作为源IP地址。
环回地址  A类地址中,127网段的所有地址都是环回地址,用来测试网络协议是否正常工作。譬如,ping 127.1.1.1可以测试本地TCP/IP协议是否正常工作。
本网络本主机  IP地址全为0的地址(0.0.0.0),表示本网络本主机。该地址只能作为源地址,用于本机IP地址不明确的情况。

 1.2 局域网、广域网、互联网

局域网(Local Area Network,缩写为 LAN),又称内网,覆盖局部区域的计算机网络。

广域网((Wide Area Network,缩写为 WAN),又称外网、公网,连接不同区域的计算机网络进行通信。

互联网,由无数个局域网,通过广域网线路汇聚互联形成。

局域网、广域网、互联网三者间关系如下图所示。

  • 无线路由器为手机、电脑分配局域网IP(LAN-IP)。路由器的IP地址,由运营商分配(运营商的局部IP地址),该地址将被转换为广域网IP地址(WAN-IP)。广域网IP地址(WAN-IP)也需要转换为互联网公共IP地址(Global-IP),才能进入互联网。
  • 网络通信的IP地址转换过程:LAN-IP <—> WAN-IP <—> Global-IP。

1.3 网络地址转换

NAT(Network Address Translation),网络地址转换,其功能是实现局域网IP地址与广域网IP地址之间的转换,即完成LAN-IP <—> WAN-IP之间的转换。

示例:

  • 具有NAT功能的路由器拥有两个IP地址,一个内部地址,用于进行局域网内部的通信;一个外部地址,用于与广域网进行通信,由运营商分配。
  • 具有 NAT 功能的路由器会在其内部维护一个 NAT 转换表。当路由器收到局域网的IP数据报时,路由器会为该数据报分配一个它内部的 NAT 端口(譬如:port 6666),局域网主机IP地址(192.168.0.181:5555)与广域网IP地址(223.166.166.66:6666)形成一个映射。从而,局域网主机能够实现与广域网进行通信。
  • NAT转换中,路由器会每个连接的局域网主机分配唯一的NAT端口号,并回收失效端口号。

2. IP报文

2.1 IP报文格式

  •  版本:IP协议的版本号。IPv4的版本号为4,IPv6的版本号为6。
  • 首部长度:记录IP首部所占的空间,单位“字”。占4bit,因此IP首部的最大长度为15*4=60Byte。
  • 服务类型(TOS):用于区分不同的IP数据包。譬如,区分一些特别要求低时延、高吞吐量或可靠性的数据包;从而便于路由器为此类IP数据包提供更合理的路径。
  • 数据报长度:IP报文的总长度,IP首部+数据区域的长度,单位“字节”。该长度一般不超过以太网数据帧的最大长度(MTU_MAX=1500Byte),若超过,则需进行分片发送。若该长度小于MTU_MIN(46Byte),则需填充至MTU_MIN后再发送。
  • 标识:用于判断各个IP数据报分片是否属于同一个数据报。每发送一个IP数据报,该字段的值+1;属于同一个IP数据报的分片,该字段的值相等。
  • 标志:占3bit。BIT(0)保留未用;BIT(1)为1表示该数据报允许进行分片处理,为0表示禁止分片处理(此时,若数据报长度超过MTU_MAX,则丢弃该数据报);BIT(2)为0表示该分片是整个数据报的最后一个分片,反之则不是。
  • 分片偏移量:表示当前分片所携带的数据在整个IP数据报中的偏移量,以8Byte为单位。
  • 生存时间(TTL):每当IP数据报被一个路由器处理后,该字段的值减1;当减为0时,丢弃该数据报。从而确保IP数据报不会永远在网络中循环(譬如由于长时间的路由选择环路)。
  • 上层协议:表示IP数据报的数据部分应该交由哪个传输协议(TCP、UDP、)处理。
  • 首部校验和:IP数据报首部的校验和,用于帮助路由器检测收到的IP数据报的首部是否正确。
  • 源IP地址:源主机的IP地址。
  • 目标IP地址:目标主机的IP地址。
  • 选择:该字段不是必须组成部分。LWIP协议栈只识别选项字段,但是不会处理它的内容。
  • 数据区域:IP数据报所携带额数据。

P.S.:IPv4和IPv6的报文格式不同,此处记录的为IPv4报文格式。

2.2 IP报文数据结构定义

LWIP定义了ip_hdr结构体来描述IP报文的首部,同时定义了获取IP报文首部信息的宏定义、设置IP报文首部信息的宏定义。源码位置:lwip_2_1_2/src/core/ipv4/ip4_frag.c

定义ip_hdr结构体时要禁止编译器进行对齐操作,因为该结构体的很多字段都是按位进行操作的。

 1 PACK_STRUCT_BEGIN
 2 struct ip_hdr
 3 {
 4     /* 版本 / 首部长度 */
 5     PACK_STRUCT_FLD_8(u8_t _v_hl);
 6     /* 服务类型 */
 7     PACK_STRUCT_FLD_8(u8_t _tos);
 8     /* 数据报总长度 */
 9     PACK_STRUCT_FIELD(u16_t _len);
10     /* 标识字段 */
11     PACK_STRUCT_FIELD(u16_t _id);
12     /* 标志与偏移 */
13     PACK_STRUCT_FIELD(u16_t _offset);
14 #define IP_RF 0x8000U /* 保留的标志位 */
15 #define IP_DF 0x4000U /* 不分片标志位 */
16 #define IP_MF 0x2000U /* 更多分片标志 */
17 #define IP_OFFMASK 0x1fffU /* 用于分段的掩码 */
18     /* 生存时间 */
19     PACK_STRUCT_FLD_8(u8_t _ttl);
20     /* 上层协议*/
21     PACK_STRUCT_FLD_8(u8_t _proto);
22     /* 校验和 */
23     PACK_STRUCT_FIELD(u16_t _chksum);
24     /* 源 IP 地址与目标 IP 地址 */
25     PACK_STRUCT_FLD_S(ip4_addr_p_t src);
26     PACK_STRUCT_FLD_S(ip4_addr_p_t dest);
27 } PACK_STRUCT_STRUCT;
28 PACK_STRUCT_END
29 
30 
31 /* 获取 IP 数据报首部各个字段信息的宏 */
32 
33 //获取协议版本
34 #define IPH_V(hdr) ((hdr)->_v_hl >> 4)
35 //获取首部长度(字)
36 #define IPH_HL(hdr) ((hdr)->_v_hl & 0x0f)
37 //获取获取首部长度字节
38 #define IPH_HL_BYTES(hdr) ((u8_t)(IPH_HL(hdr) * 4))
39 //获取服务类型
40 #define IPH_TOS(hdr) ((hdr)->_tos)
41 //获取数据报长度
42 #define IPH_LEN(hdr) ((hdr)->_len)
43 //获取数据报标识
44 #define IPH_ID(hdr) ((hdr)->_id)
45 //获取分片标志位+偏移量
46 #define IPH_OFFSET(hdr) ((hdr)->_offset)
47 //获取偏移量大小(字节)
48 #define IPH_OFFSET_BYTES(hdr) \
49 ((u16_t)((lwip_ntohs(IPH_OFFSET(hdr)) & IP_OFFMASK) * 8U))
50 //获取生存时间
51 #define IPH_TTL(hdr) ((hdr)->_ttl)
52 //获取上层协议
53 #define IPH_PROTO(hdr) ((hdr)->_proto)
54 //获取校验和
55 #define IPH_CHKSUM(hdr) ((hdr)->_chksum)
56 
57 
58 /* 用于填写 IP 数据报首部的宏*/
59 
60 //设置版本号跟首部长度
61 #define IPH_VHL_SET(hdr, v, hl) \
62 (hdr)->_v_hl = (u8_t)((((v) << 4) | (hl)))
63 //设置服务类型
64 #define IPH_TOS_SET(hdr, tos) (hdr)->_tos = (tos)
65 //设置数据报总长度
66 #define IPH_LEN_SET(hdr, len) (hdr)->_len = (len)
67 //设置标识
68 #define IPH_ID_SET(hdr, id) (hdr)->_id = (id)
69 //设置分片标志与偏移量
70 #define IPH_OFFSET_SET(hdr, off) (hdr)->_offset = (off)
71 //设置生存时间
72 #define IPH_TTL_SET(hdr, ttl) (hdr)->_ttl = (u8_t)(ttl)
73 //设置上层协议
74 #define IPH_PROTO_SET(hdr, proto) (hdr)->_proto = (u8_t)(proto)
75 //设置校验和
76 #define IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum)
IP报文数据结构定义

 3. IP数据报分片

(1)分片处理的原因

IP数据报被传送至链路层,链路层将IP数据报封装成链路层帧,网卡硬件将链路层帧发送至目标主机。一个链路层帧能携带的最大数据量称为最大传送单元(MTU),不同网卡硬件的MTU可能不一样。当IP数据报的长度超过MTU,则需对其进行分片处理后,再发送。

(2)分片处理的原理

IP数据报的分片处理,是将IP数据报中的数据区域切割为若干个较小的IP数据报,并封装成单独的链路层帧后进行发送。

  • 每个分片的IP首部,除“标志”、“分片偏移量”这两个字段,其余都一样。
  • “分片偏移量”字段的单位是8Byte,因此分片的数据区域的长度也必须是8的整数倍。
  • 最后一个分片的标志位的BIT(2)为0,表示该分片为最后一个分片。
  • 所有分片在到达目标主机的IP层后,进行重装,形成一个完整的IP数据包。

(3)分片处理示例

主机发送一个4000Byte的IP数据报,链路层MTU = 1500Byte,需要对原始IP数据报进行分片处理。

原始IP数据报(4000Byte)= IP首部(20Byte)+ 数据区域(3980Byte),对数据区域进行切割,并填写每个分片IP首部的“标志”、“分片偏移量”字段。

第一个分片:“标志”字段BIT(2) = 1,分片偏移量为0,分片数据报长度(1500Byte)=  IP首部(20Byte)+ 数据区域(1480Byte);

第二个分片:“标志”字段BIT(2) = 1,分片偏移量为185(1480/8),分片数据报长度(1500Byte)=  IP首部(20Byte)+ 数据区域(1480Byte);

第三个分片:“标志”字段BIT(2) = 0,分片偏移量为370(185+185),分片数据报长度(1040Byte)=  IP首部(20Byte)+ 数据区域(1020Byte)。

 (4)分片处理的源码

分片处理的源码位于“lwip_2_1_2/src/core/ipv4/ip4_frag.c”,调用“ip4_frag()”函数对IP数据报进行分片处理并发送至目标主机。

4. IP数据报的发送与接收

IP数据报的发送与接收,源码位于:lwip_2_1_2/src/core/ipv4/ip4.c

4.1 IP数据报的发送

  •  传输层需要发送数据时,将需要发送的数据传递到网络层的IP协议,IP层调用“ip4_output()”函数发送IP数据报。
  • “ip4_output()”函数调用“ip4_route_src()”函数根据目标IP地址在网卡列表中选择一个合适的网卡,匹配条件:目标IP地址与网卡地址在一个子网内,或者目标IP地址等于网卡的网关地址。再调用“ip4_output_if()”函数发送IP数据报。
  • “ip4_output_if_src()”函数调整payload指针至IP数据报首部的起始地址,填写IP首部信息,并对IP数据报进行分类处理。若目标IP地址等于源主机的IP地址,则调用“netif_loop_output()”函数进行环回输入;若IP数据报长度超过MTU,则调用“ip4_frag()”函数进行分片发送;否则直接调用“netif->output ”接口将IP数据报传递给ARP协议进行发送。

4.2 IP数据报的接收

  • 链路层接收到一个IP数据报,通过“ethernet_input()”函数再传递到“ip4_input()”函数进行处理。
  • “ip4_input()”函数首先检查IP数据报首部的协议版本是否为IPv4,然后根据目标IP地址在本地网卡列表中寻找有效的目标网卡。若目标IP地址不等于主机地址,则通过“ip4_addr_isloopback()”函数判断目标IP地址是否为环回地址。若匹配到目标网卡,对于分片则调用“ip4_reass()”函数进行重装,然后将数据传递至指定的传输层协议。若没有找到目标网卡,对于广播包则直接删除该数据报,对于非广播包则可以通过“ip4_forward()”函数进行转发。

5. 参考资料

1. 野火《LwIP应用开发实战指南》。

猜你喜欢

转载自www.cnblogs.com/linfeng-learning/p/12456138.html