自己怎么做优惠券网站,手机网站建设费用,东台市住房和建设局网站,wordpress demo data文章目录 前言一、IP协议的简介二、IP数据报1.IP 数据报结构2.IP 数据报的分片解析3.IP 数据报的分片重装 三、IP 数据报的输出四、IP 数据报的输入 前言
IP 指网际互连协议#xff0c; Internet Protocol 的缩写#xff0c;是 TCP/IP 体系中的网络层协议。设计 IP 的目的是… 文章目录 前言一、IP协议的简介二、IP数据报1.IP 数据报结构2.IP 数据报的分片解析3.IP 数据报的分片重装 三、IP 数据报的输出四、IP 数据报的输入 前言
IP 指网际互连协议 Internet Protocol 的缩写是 TCP/IP 体系中的网络层协议。设计 IP 的目的是提高网络的可扩展性一是解决互联网问题实现大规模、异构网络的互联互通二是分割顶层网络应用和底层网络技术之间的耦合关系以利于两者的独立发展。根据端到端的设计原则IP 只为主机提供一种无连接、不可靠的、尽力而为的数据包传输服务。 一、IP协议的简介
IP 协议是整个 TCP/IP 协议族的核心也是构成互联网的基础。 IP 位于 TCP/IP 模型的网络层相当于 OSI 模型的网络层它可以向传输层提供各种协议的信息例如 TCP、 UDP 等对下可将 IP 信息包放到链路层通过以太网、令牌环网络等各种技术来传送。 为了能适应异构网络 IP 强调适应性、简洁性和可操作性并在可靠性做了一定的牺牲。
二、IP数据报
IP 层数据报也叫做 IP 数据报或者 IP 分组 IP 数据报组装在以太网帧中发送的它通常由两个部分组成即 IP 首部与数据区域 其中 IP 的首部是 20 字节大小数据区域理论上可以多达65535 个字节 由于以太网网络接口的最大传输单元为 1500所以一个完整的数据包不能超出 1500 字节大小。 (1) 版本 占 4 位指 IP 协议的版本。通信双方使用的 IP 协议版本必须一致。广泛使用的IP 协议版本号为 4即 IPv4。 (2) 首部长度 占 4 位可表示的最大十进制数值是 15。请注意这个字段所表示数的单位是 32 位字长1 个 32 位字长是 4 字节因此当 IP 的首部长度为 1111 时即十进制的 15首部长度就达到 60 字节。当 IP 分组的首部长度不是 4 字节的整数倍时必须利用最后的填充字段加以填充。因此数据部分永远在 4 字节的整数倍开始这样在实现 IP 协议时较为方便。首部长度限制为 60 字节的缺点是有时可能不够用。但这样做是希望用户尽量减少开销。最常用的首部长度就是 20 字节即首部长度为 0101这时不使用任何选项。 (3) 区分服务 占 8 位 用来获得更好的服务。这个字段在旧标准中叫做服务类型但实际上一直没有被使用过。 (4) 总长度 总长度指首部和数据之和的长度单位为字节。总长度字段为 16 位因此数据报的最大长度为 2^16-165534 字节。在 IP 层下面的每一种数据链路层都有自己的帧格式其中包括帧格式中的数据字段的最大长度这称为最大传送单元 MTU。当一个数据报封装成链路层的帧时此数据报的总长度即首部加上数据部分一定不能超过下面的数据链路层的 MTU 值。 (5) 标识identification 占 16 位 IP 软件在存储器中维持一个计数器每产生一个数据报计数器就加 1并将此值赋给标识字段。但这个“标识”并不是序号因为 IP 是无连接服务数据报不存在按序接收的问题。当数据报由于长度超过网络的 MTU 而必须分片时这个标识字段的值就被复制到所有的数据报的标识字段中。相同的标识字段的值使分片后的各数据报片最后能正确地重装成为原来的数据报。 (6) 标志flag 占 3 位但只有 2 位有意义的。
标志字段中的最低位记为 MFMore Fragment。 MF1 即表示后面“还有分片”的数据报。 MF0 表示这已是若干数据报片中的最后一个。标志字段中间的一位记为 DFDon’ t Fragment意思是“不能分片”。只有当 DF0时才允许分片。 (7) 片偏移 占 13 位片偏移指出较长的分组在分片后某片在原分组中的相对位置。也就是说相对用户数据字段的起点该片从何处开始。片偏移以 8 个字节为偏移单位。这就是说除了最后一个分片每个分片的长度一定是 8 字节64 位的整数倍。 (8) 生存时间 占 8 位生存时间字段常用的的英文缩写是 TTL(Time To Live)表明是数据报在网络中的寿命。由发出数据报的源点设置这个字段。其目的是防止无法交付的数据报无限制地在因特网中兜圈子因而白白消耗网络资源。最初的设计是以秒作为 TTL 的单位。每经过一个路由器时就把 TTL 减去数据报在路由器消耗掉的一段时间。若数据报在路由器消耗的时间小于 1 秒就把 TTL 值减 1。当 TTL 值为 0 时就丢弃这个数据报。后来把 TTL 字段的功能改为“跳数限制”但名称不变。路由器在转发数据报之前就把 TTL 值减 1.若 TTL 值减少到零就丢弃这个数据报不再转发。因此 TTL 的单位不再是秒而是跳数。 TTL 的意义是指明数据报在网络中至多可经过多少个路由器。显然数据报在网络上经过的路由器的最大数值是 255。 若把 TTL 的初始值设为 1就表示这个数据报只能在本局域网中传送。 (9) 协议 占 8 位协议字段指出此数据报携带的数据是使用何种协议以便使目的主机的IP 层知道应将数据部分上交给哪个处理过程。 (10) 首部检验和 占 16 位这个字段只检验数据报的首部但不包括数据部分。这是因为数据报每经过一个路由器路由器都要重新计算一下首部检验和一些字段如生存时间、标志、片偏移等都可能发生变化。不检验数据部分可减少计算的工作量。 (11) 源地址 占 32 位。 (12) 目的地址 占 32 位。 (13) 数据区域 这是 IP 数据报的最后的一个字段也是最重要的内容 lwIP 发送数据报是把该层的首部封装到数据包里面在 IP 层也是把 IP 首部封装在其中因为有数据区域才会有数据报首部的存在在大多数情况下 IP 数据报中的数据字段包含要交付给目标 IP 地址的运输层(TCP 协议或 UDP 协议)当然数据区域也可承载其他类型的报文如 ICMP 报文等。
1.IP 数据报结构
在 lwIP 中为了描述 IP 报文结构 它在 ip4.h 文件中定义了一个 ip_hdr 结构体来描述 IP数据报的内容
struct ip_hdr {
/* 版本号首部长度服务类型 */
PACK_STRUCT_FLD_8(u8_t _v_hl);
/* 服务类型 */
PACK_STRUCT_FLD_8(u8_t _tos);
/* 总长度(IP 首部数据区) */
PACK_STRUCT_FIELD(u16_t _len);
/* 数据包标识(编号) */
PACK_STRUCT_FIELD(u16_t _id);
/* 标志片偏移 */
PACK_STRUCT_FIELD(u16_t _offset);
/* IP 首部标志定义 */
#define IP_RF 0x8000U /* 保留 */
#define IP_DF 0x4000U /* 是否允许分片 */
#define IP_MF 0x2000U /* 后续是否还有更多分片 */
#define IP_OFFMASK 0x1fffU /* 片偏移域掩码 */
/* 生存时间(最大转发次数)协议类型(IGMP:1、 UDP:17、 TCP:6) */
PACK_STRUCT_FLD_8(u8_t _ttl);
/* 协议*/
PACK_STRUCT_FLD_8(u8_t _proto);
/* 校验和(IP 首部) */
PACK_STRUCT_FIELD(u16_t _chksum);
/* 源 IP 地址/目的 IP 地址 */
PACK_STRUCT_FLD_S(ip4_addr_p_t src);
PACK_STRUCT_FLD_S(ip4_addr_p_t dest);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END2.IP 数据报的分片解析
TCP/IP 协议栈为什么具备分片的概念因为应用程序处理的数据是不确定的可能超出网络接口最大传输单元为此 TCP/IP 协议栈引入了分片概念它是以 MTU 为界限对这个大型的数据切割成多个小型的数据包。这些小型的数据叫做 IP 的分组和分片它们在接收方进行重组处理这样接收方的应用程序接收到这个大型的数据了。总的来讲 IP 数据报的分片概念是为了解决 IP 数据报数据过大的问题而诞生。 注以太网最大传输单元 MTU 为 1500。 假设 IP 数据报整体的大小为 4000 字节 IP 首部默认为 20 字节 而数据区域为 3980。由于以太网最大传输单元为 1500 所以 lwIP 内核会把这个数据报进行分片处理。
第一个 IP 分片 分片数据大小 20IP 首部 1480数据区域。 标识 888。 标志 IP_MF 1 后续还有分片。 片偏移量 片偏移量是 0 单位是 8 字节 本片偏移量相当于 0 字节。第二片 IP 数据报 分片数据大小 20IP 首部 1480数据区域。 标识 888。 标志 IP_MF 1 后续还有分片。 片偏移量 片偏移量是 1851480/8 单位是 8 字节 本片偏移量相当于 1480 字节。第三片 IP 数据报 分片数据大小 20IP 首部 1020数据区域。 标识 888。 标志 IP_MF 0 后续没有分片。 片偏移量 片偏移量是 370185185 单位是 8 字节 本片偏移量相当于 2960 字节。 注这些分片的标识都是一致的而 IP_MF 表示后续有没有分片若 IP_MF 为 0则这个分片为最后一个分片。 从上图可以看出一个大型的 IP 数据包经过网络层处理它会被分成两个或者两个以上的 IP 分片这些分片的数据组合起来就是应用程序发送的数据与传输层的首部。 lwIP实现它的函数为 ip4_frag代码如下示例
/**
* 如果 IP 数据报对 netif 来说太大则将其分片,
将数据报切成 MTU 大小的块然后按顺序发送通过将 pbuf_ref 指向 p
* param p:要发送的 IP 数据包
* param netif:发送的 netif
* param dest:目的 IP 地址
* return ERR_OK:发送成功, err_t:其他
*/
err_t
ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest)
{
struct pbuf *rambuf;
#if !LWIP_NETIF_TX_SINGLE_PBUF
struct pbuf *newpbuf;
u16_t newpbuflen 0;
u16_t left_to_copy;
#endif
struct ip_hdr *original_iphdr;
struct ip_hdr *iphdr;
/* (1500 - 20)/8 偏移 185 */
const u16_t nfb (u16_t)((netif-mtu - IP_HLEN) / 8);
u16_t left, fragsize;
u16_t ofo;
int last;
u16_t poff IP_HLEN; /* IP 头部长度 */
u16_t tmp;
int mf_set;
original_iphdr (struct ip_hdr *)p-payload; /* 指向数据报 */
iphdr original_iphdr;
/* 判断 IP 头部是否为 20 */
if (IPH_HL_BYTES(iphdr) ! IP_HLEN) {
return ERR_VAL;
}
/* tmp 变量获取标志和片偏移数值 */
tmp lwip_ntohs(IPH_OFFSET(iphdr));
/* ofo 片偏移 */
ofo tmp IP_OFFMASK;
/* mf_set 分片标志 */
mf_set tmp IP_MF;
/* left 总长度减去 IP 头部等于有效数据长度 4000 - 20 3980 */
left (u16_t)(p-tot_len - IP_HLEN);
/* 判断 left 是否为有效数据 */
while (left) {
/* 判断有效数据和偏移数据大小 fragsize 1480 (3980 1480 ? 3980 : 1480) */
fragsize LWIP_MIN(left, (u16_t)(nfb * 8));
/* rambuf 申请 20 字节大小的内存块 */
rambuf pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
if (rambuf NULL) {
goto memerr;
}
/* 这个 rambuf 有效数据指针指向 original_iphdr 数据报 */
SMEMCPY(rambuf-payload, original_iphdr, IP_HLEN);
/* iphdr 指向有效区域地址 rambuf-payload */
iphdr (struct ip_hdr *)rambuf-payload;
/* left_to_copy 偏移数据大小(1480) */
left_to_copy fragsize;
while (left_to_copy) {
struct pbuf_custom_ref *pcr;
/* 当前 pbuf 中数据的长度,plen 3980 - 20 3960 */
u16_t plen (u16_t)(p-len - poff);
/* newpbuflen 1480 (1480 3960 ? 1480 : 3960) */
newpbuflen LWIP_MIN(left_to_copy, plen);
if (!newpbuflen) {
poff 0;
p p-next;
continue;
}
/* pcr 申请内存 */
pcr ip_frag_alloc_pbuf_custom_ref();
if (pcr NULL) {
pbuf_free(rambuf);
goto memerr;
}
/* newpbuf 申请内存 1480 字节
保存了这个数据区域偏移 poff 字节的数据(p-payload poff) */
newpbuf pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, pcr-pc,
(u8_t *)p-payload poff, newpbuflen);
if (newpbuf NULL) {
/* 释放内存 */
ip_frag_free_pbuf_custom_ref(pcr);
pbuf_free(rambuf);
goto memerr;
}
/* 增加 pbuf 的引用计数 */
pbuf_ref(p);
pcr-original p;
pcr-pc.custom_free_function ipfrag_free_pbuf_custom;
/* 将它添加到 rambuf 的链的末尾 */
pbuf_cat(rambuf, newpbuf);
/* left_to_copy 0 (1480 - 1480) */
left_to_copy (u16_t)(left_to_copy - newpbuflen);
if (left_to_copy) {
poff 0;
p p-next;
}
}
/* poff 1500 (20 1480) */
poff (u16_t)(poff newpbuflen);
/* last 0 (3980 (1500 - 20)) */
last (left netif-mtu - IP_HLEN);
/* 设置新的偏移量和 MF 标志 */
tmp (IP_OFFMASK (ofo));
/* 判断是否是最后一个分片 */
if (!last || mf_set) {
/* 最后一个片段设置了 MF 为 0 */
tmp tmp | IP_MF;
}
/* 分段偏移与标志字段 */
IPH_OFFSET_SET(iphdr, lwip_htons(tmp));
/* 设置数据报总长度 1500 (1480 20) */
IPH_LEN_SET(iphdr, lwip_htons((u16_t)(fragsize IP_HLEN)));
/* 校验为 0 */
IPH_CHKSUM_SET(iphdr, 0);
/* 发送 IP 数据报 */
netif-output(netif, rambuf, dest);
IPFRAG_STATS_INC(ip_frag.xmit);
/* rambuf 释放内存 */
pbuf_free(rambuf);
/* left 2500 (3980 - 1480) */
left (u16_t)(left - fragsize);
/* 片偏移 ofo 185(0 185) */
ofo (u16_t)(ofo nfb);
}
MIB2_STATS_INC(mib2.ipfragoks);
return ERR_OK;
memerr:
MIB2_STATS_INC(mib2.ipfragfails);
return ERR_MEM;
}
MIB2_STATS_INC(mib2.ipfragoks);
return ERR_OK;
memerr:
MIB2_STATS_INC(mib2.ipfragfails);
return ERR_MEM;
}
此函数非常简单首先判断这个大型数据包的有效区域总长度系统根据这个总长度划分数据区域接着申请 20sizeof(struct pbuf)字节的 rampbuf 来存储 IP 首部然后根据 poff 数值让被分片数据包的 payload 指针偏移 poff 大小它所指向的地址由 newpbuf 数据包的 payload指针指向最后调用 netif-output 函数发送该分片其他分片一样操作。 newpbuf 的 payload 指针指向的地址由左边的 payload 指针经过偏移得来的。
3.IP 数据报的分片重装
由于 IP 分组在网络传输过程中到达目的地点的时间是不确定的所以后面的分组可能比前面的分组先达到目的地点。 为此 lwIP 内核需要将接收到的分组暂存起来等所有的分组都接收完成之后再将数据传递给上层。 在 lwIP 中有专门的结构体负责缓存这些分组这个结构体为 ip_reassdata 重装数据链表该结构体在 ip4_frag.h 文件中定义
/* 重装数据结构体 */
struct ip_reassdata {
struct ip_reassdata *next; /* 指向下一个重装节点 */
struct pbuf *p; /* 指向分组的 pbuf */
struct ip_hdr iphdr; /* IP 数据报的首部 */
u16_t datagram_len; /* 已收到数据的长度 */
u8_t flags; /* 标志是否最后一个分组 */
u8_t timer; /* 超时间隔 */
};可以看到这些分片挂载到同一个重装节点上它们挂载之前是把 IP 首部的前 8 字节强制转换成三个字段其中 next_pbuf 指针用来链接这些 IP 分组形成了单向链表而 start和 end 字段用来描述分组的顺序 lwIP 系统根据这些数值对分组进行排序。
三、IP 数据报的输出
无论是 UDP 还是 TCP它们的数据段递交至网络层的接口是一致的这个接口函数如下所示 err_tip4_output_if_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,u8_t ttl, u8_t tos,u8_t proto, struct netif *netif){struct ip_hdr *iphdr;ip4_addr_t dest_addr;if (dest ! LWIP_IP_HDRINCL){u16_t ip_hlen IP_HLEN;/* 第一步 生成 IP 报头 */if (pbuf_header(p, IP_HLEN)){return ERR_BUF;}/* 第二步 iphdr 指向 IP 头部指针 */iphdr (struct ip_hdr *)p-payload;/* 设置生存时间(最大转发次数) */IPH_TTL_SET(iphdr, ttl);/* 设置协议类型(IGMP:1、 UDP:17、 TCP:6) */IPH_PROTO_SET(iphdr, proto);/* 设置目的 IP 地址 */ip4_addr_copy(iphdr-dest, *dest);/* 设置版本号设置首部长度 */IPH_VHL_SET(iphdr, 4, ip_hlen / 4);/* 服务类型 */IPH_TOS_SET(iphdr, tos);/* 设置总长度(IP 首部数据区) */IPH_LEN_SET(iphdr, lwip_htons(p-tot_len));/* 设置标志片偏移 */IPH_OFFSET_SET(iphdr, 0);/* 设置数据包标识(编号) */IPH_ID_SET(iphdr, lwip_htons(ip_id));/* 每发送一个数据包编号加一 */ip_id;/* 没有指定源 IP 地址 */if (src NULL){/* 将当前网络接口 IP 地址设置为源 IP 地址 */ip4_addr_copy(iphdr-src, *IP4_ADDR_ANY4);}else{/* 复制源 IP 地址 */ip4_addr_copy(iphdr-src, *src);}}else{/* IP 头部已经包含在 pbuf 中 */iphdr (struct ip_hdr *)p-payload;ip4_addr_copy(dest_addr, iphdr-dest);dest dest_addr;}IP_STATS_INC(ip.xmit);ip4_debug_print(p);/* 如果数据包总长度大于 MTU则分片发送 */if (netif-mtu (p-tot_len netif-mtu)){return ip4_frag(p, netif, dest);}/* 如果数据包总长度不大于 MTU则直接发送 */return netif-output(netif, p, dest);}此函数首先判断目标 IP 地址是否为 NULL若目标 IP 地址不为空则偏移 payload 指针添加 IP 首部偏移完成之后设置 IP 首部字段信息接着判断该数据包的总长度是否大于以太网传输单元若大于则调用 ip4_frag 函数对这个数据包分组并且逐一发送否则直接调用ethrap_output 函数把数据包递交给 ARP 层处理。
四、IP 数据报的输入
数据包提交给网络层之前系统需要判断接收到的数据包是 IP 数据包还是 ARP 数据包若接收到的是 IP 数据包则 lwIP 内核调用 ip4_input 函数处理这个数据包该函数如下所示
err_t
ip4_input(struct pbuf *p, struct netif *inp)
{
struct ip_hdr *iphdr;
struct netif *netif;
u16_t iphdr_hlen;
u16_t iphdr_len;
#if IP_ACCEPT_LINK_LAYER_ADDRESSING || LWIP_IGMP
int check_ip_src 1;
#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING || LWIP_IGMP */
IP_STATS_INC(ip.recv);
MIB2_STATS_INC(mib2.ipinreceives);
/* 识别 IP 报头 */
iphdr (struct ip_hdr *)p-payload;
/* 第一步判断版本是否为 IPv4 */
if (IPH_V(iphdr) ! 4)
{
ip4_debug_print(p);
pbuf_free(p); /* 释放空间 */
IP_STATS_INC(ip.err);
IP_STATS_INC(ip.drop);
MIB2_STATS_INC(mib2.ipinhdrerrors);
return ERR_OK;
}
/* 以 4 字节32 位字段获得 IP 头的长度 */
iphdr_hlen IPH_HL(iphdr);
/* 以字节计算 IP 报头长度 */
iphdr_hlen * 4;
/* 以字节为单位获取 ip 长度 */
iphdr_len lwip_ntohs(IPH_LEN(iphdr));
/* 修剪 pbuf。这对于 60 字节的数据包尤其需要。 */
if (iphdr_len p-tot_len)
{
pbuf_realloc(p, iphdr_len);
}
/* 第二步标头长度超过第一个 pbuf 长度或者 ip 长度超过总 pbuf 长度 */
if ((iphdr_hlen p-len) || (iphdr_len p-tot_len)
|| (iphdr_hlen IP_HLEN))
{
if (iphdr_hlen IP_HLEN)
{ }
if (iphdr_hlen p-len)
{ }
if (iphdr_len p-tot_len)
{ }
/* 释放空间 */
pbuf_free(p);
IP_STATS_INC(ip.lenerr);
IP_STATS_INC(ip.drop);
MIB2_STATS_INC(mib2.ipindiscards);
return ERR_OK;
}
/* 第三步验证校验和 */
#if CHECKSUM_CHECK_IP
/* 省略代码 */
#endif
/* 将源 IP 地址与目标 IP 地址复制到对齐的 ip_data.current_iphdr_src 和
ip_data.current_iphdr_dest */
ip_addr_copy_from_ip4(ip_data.current_iphdr_dest, iphdr-dest);
ip_addr_copy_from_ip4(ip_data.current_iphdr_src, iphdr-src);
/* 第四步匹配数据包和接口即这个数据包是否发给本地 */
if (ip4_addr_ismulticast(ip4_current_dest_addr()))
{
#if LWIP_IGMP
/* 省略代码 */
#else /* LWIP_IGMP */
/* 如果网卡已经挂载了和 IP 地址有效 */
if ((netif_is_up(inp)) (!ip4_addr_isany_val(*netif_ip4_addr(inp))))
{
netif inp;
}
else
{
netif NULL;
}
#endif /* LWIP_IGMP */
}
/* 如果数据报不是发给本地 */
else
{
int first 1;
netif inp;
do
{
/* 接口已启动并配置? */
if ((netif_is_up(netif))
(!ip4_addr_isany_val(*netif_ip4_addr(netif))))
{
/* 单播到此接口地址? */
if (ip4_addr_cmp(ip4_current_dest_addr(),
netif_ip4_addr(netif)) ||
/* 或广播在此接口网络地址? */
ip4_addr_isbroadcast(ip4_current_dest_addr(), netif)
#if LWIP_NETIF_LOOPBACK !LWIP_HAVE_LOOPIF
|| (ip4_addr_get_u32(ip4_current_dest_addr())
PP_HTONL(IPADDR_LOOPBACK))
#endif /* LWIP_NETIF_LOOPBACK !LWIP_HAVE_LOOPIF */
)
{
break;
}
#if LWIP_AUTOIP
if (autoip_accept_packet(netif, ip4_current_dest_addr()))
{
/* 跳出 if 循环 */
break;
}
#endif /* LWIP_AUTOIP */
}
if (first)
{
#if !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF
/* 检查一下目标 IP 地址是否是环回地址 */
if (ip4_addr_isloopback(ip4_current_dest_addr()))
{
netif NULL;
break;
}
#endif /* !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF */
first 0;
netif netif_list;
}
else
{
netif netif-next;
}
if (netif inp)
{
netif netif-next;
}
} while (netif ! NULL);
}
#if IP_ACCEPT_LINK_LAYER_ADDRESSING
if (netif NULL)
{
/* 远程端口是 DHCP 服务器? */
if (IPH_PROTO(iphdr) IP_PROTO_UDP)
{
struct udp_hdr *udphdr (struct udp_hdr *)
((u8_t *)iphdr iphdr_hlen);
if (IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(udphdr-dest))
{
netif inp;
check_ip_src 0;
}
}
}
#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
#if LWIP_IGMP || IP_ACCEPT_LINK_LAYER_ADDRESSING
if (check_ip_src
#if IP_ACCEPT_LINK_LAYER_ADDRESSING!ip4_addr_isany_val(*ip4_current_src_addr())
#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
)
#endif /* LWIP_IGMP || IP_ACCEPT_LINK_LAYER_ADDRESSING */
{
/* 第五步 IP 地址源 IP 地址不能是多播或者广播地址 */
if ((ip4_addr_isbroadcast(ip4_current_src_addr(), inp)) ||
(ip4_addr_ismulticast(ip4_current_src_addr())))
{
/* 释放空间 */
pbuf_free(p);
IP_STATS_INC(ip.drop);
MIB2_STATS_INC(mib2.ipinaddrerrors);
MIB2_STATS_INC(mib2.ipindiscards);
return ERR_OK;
}
}
/* 第六步如果还没找到对应的网卡数据包不是给我们的 */
if (netif NULL)
{
/* 路由转发或者丢弃。如果 IP_FORWARD 宏定义被使能则进行转发 */
#if IP_FORWARD
/* 非广播包 */
if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), inp))
{
/* 尝试在其他网卡上转发 IP 数据包 */
ip4_forward(p, iphdr, inp);
}
else
#endif /* IP_FORWARD */
{
IP_STATS_INC(ip.drop);
MIB2_STATS_INC(mib2.ipinaddrerrors);
MIB2_STATS_INC(mib2.ipindiscards);
}
/* 释放空间 */
pbuf_free(p);
return ERR_OK;
}
/* 第七步如果数据报由多个片段组成分片处理 */
if ((IPH_OFFSET(iphdr) PP_HTONS(IP_OFFMASK | IP_MF)) ! 0)
{
/* 重装数据报*/
p ip4_reass(p);
/* 如果重装没有完成 */
if (p NULL)
{
return ERR_OK;
}
/* 分片重装完成将数据报首部强制转换为 ip_hdr 类型 */
iphdr (struct ip_hdr *)p-payload;
}
#if IP_OPTIONS_ALLOWED 0
#if LWIP_IGMP
if ((iphdr_hlen IP_HLEN) (IPH_PROTO(iphdr) ! IP_PROTO_IGMP))
{
#else
/* 第八步如果 IP 数据报首部长度大于 20 字节就表示错误 */
if (iphdr_hlen IP_HLEN)
{
#endif /* LWIP_IGMP */
/* 释放空间 */
pbuf_free(p);
IP_STATS_INC(ip.opterr);
IP_STATS_INC(ip.drop);
/* u 不受支持的协议特性 */
MIB2_STATS_INC(mib2.ipinunknownprotos);
return ERR_OK;
}
#endif /* IP_OPTIONS_ALLOWED 0 */
/* 第九步 发送到上层协议 */
ip4_debug_print(p);
ip_data.current_netif netif;
ip_data.current_input_netif inp;
ip_data.current_ip4_header iphdr;
ip_data.current_ip_header_tot_len IPH_HL(iphdr) * 4;
#if LWIP_RAW
/* RAW API 输入 */
if (raw_input(p, inp) 0)
#endif /* LWIP_RAW */
{
/* 转移到有效载荷数据区域不需要检查 */
pbuf_header(p, -(s16_t)iphdr_hlen);
/* 根据 IP 数据报首部的协议的类型处理 */
switch (IPH_PROTO(iphdr))
{
#if LWIP_UDP
/* UDP 协议 */
case IP_PROTO_UDP:
#if LWIP_UDPLITE
case IP_PROTO_UDPLITE:
#endif /* LWIP_UDPLITE */
MIB2_STATS_INC(mib2.ipindelivers);
/* IP 层递交给网络层的函数 */
udp_input(p, inp);
break;
#endif /* LWIP_UDP */
#if LWIP_TCP
/* TCP 协议 */
case IP_PROTO_TCP:
MIB2_STATS_INC(mib2.ipindelivers);
/* IP 层递交给网络层的函数 */
tcp_input(p, inp);
break;
#endif /* LWIP_TCP */
pbuf_free(p);/* 释放空间*/
IP_STATS_INC(ip.proterr);
IP_STATS_INC(ip.drop);
MIB2_STATS_INC(mib2.ipinunknownprotos);
}
}
/* 全局变量清零*/
ip_data.current_netif NULL;
ip_data.current_input_netif NULL;
ip_data.current_ip4_header NULL;
ip_data.current_ip_header_tot_len 0;
ip4_addr_set_any(ip4_current_src_addr());
ip4_addr_set_any(ip4_current_dest_addr());
return ERR_OK;
}第一步判断 IP 数据报的版本是否是 IPv4如果不是那么 lwIP 会掉弃该数据报。 第二步判断标头长度超过第一个 pbuf 长度或者 ip 长度超过总 pbuf 长度如果是那么 lwIP 会丢弃该数据报。 第三步 验证校验和如果不正确 那么 lwIP 会掉弃该数据报。 第四步 匹配数据包和接口这个数据包是否发给本地。 第五步判断 IP 数据报是否是广播或者多播如果是 那么 lwIP 会丢弃该数据报。 第六步如果到了这一步没有发现网络接口 那么 lwIP 会丢弃该数据报。 第七步如果如 IP 数据报不能分片处理 那么 lwIP 会丢弃该数据报。 第八步如果 IP 数据报的 IP 首部大于 20 字节 那么 lwIP 会丢弃该数据报。 第九步 把数据包递交给上层。 第十步判断该数据报的协议为 TCP/UDP/ICMP/IGMP如果不是这四个协议则丢弃该 数据报。