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功能的路由器拥有两个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结构体时要禁止编译器进行对齐操作,因为该结构体的很多字段都是按位进行操作的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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)
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应用开发实战指南》。