IP协议说明

IP协议说明IP 指网际互连协议 InternetProt 的缩写 是 TCP IP 体系中的网络层协议

大家好,欢迎来到IT知识分享网。


前言

IP 指网际互连协议, Internet Protocol 的缩写,是 TCP/IP 体系中的网络层协议。设计 IP 的目的是提高网络的可扩展性:一是解决互联网问题,实现大规模、异构网络的互联互通;二是分割顶层网络应用和底层网络技术之间的耦合关系,以利于两者的独立发展。根据端到端的设计原则,IP 只为主机提供一种无连接、不可靠的、尽力而为的数据包传输服务。


一、IP协议的简介

IP 协议是整个 TCP/IP 协议族的核心,也是构成互联网的基础。 IP 位于 TCP/IP 模型的网络层(相当于 OSI 模型的网络层),它可以向传输层提供各种协议的信息,例如 TCP、 UDP 等;对下可将 IP 信息包放到链路层,通过以太网、令牌环网络等各种技术来传送。 为了能适应异构网络, IP 强调适应性、简洁性和可操作性,并在可靠性做了一定的牺牲。

二、IP数据报

  1. 标志字段中的最低位记为 MF(More Fragment)。 MF=1 即表示后面“还有分片”的数据报。 MF=0 表示这已是若干数据报片中的最后一个。
  2. 标志字段中间的一位记为 DF(Don’ t Fragment),意思是“不能分片”。只有当 DF=0时才允许分片。
    (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_END 

2.IP 数据报的分片解析

  1. 第一个 IP 分片:
    分片数据大小: 20(IP 首部) + 1480(数据区域)。
    标识: 888。
    标志: IP_MF = 1 后续还有分片。
    片偏移量: 片偏移量是 0, 单位是 8 字节, 本片偏移量相当于 0 字节。



  2. 第二片 IP 数据报:
    分片数据大小: 20(IP 首部) + 1480(数据区域)。
    标识: 888。
    标志: IP_MF = 1 后续还有分片。
    片偏移量: 片偏移量是 185(1480/8), 单位是 8 字节, 本片偏移量相当于 1480 字节。



  3. 第三片 IP 数据报:
    分片数据大小: 20(IP 首部) + 1020(数据区域)。
    标识: 888。
    标志: IP_MF = 0, 后续没有分片。
    片偏移量: 片偏移量是 370(185+185), 单位是 8 字节, 本片偏移量相当于 2960 字节。
    注:这些分片的标识都是一致的,而 IP_MF 表示后续有没有分片,若 IP_MF 为 0,则这个分片为最后一个分片。
     IP 数据报分片示意图
    从上图可以看出,一个大型的 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; } 

3.IP 数据报的分片重装

/* 重装数据结构体 */ 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 分组重装示意图
可以看到,这些分片挂载到同一个重装节点上,它们挂载之前,是把 IP 首部的前 8 字节强制转换成三个字段,其中 next_pbuf 指针用来链接这些 IP 分组,形成了单向链表,而 start和 end 字段用来描述分组的顺序, lwIP 系统根据这些数值对分组进行排序。

三、IP 数据报的输出

无论是 UDP 还是 TCP,它们的数据段递交至网络层的接口是一致的,这个接口函数如下所示:

 err_t ip4_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); } 

函数 ip4_output_if_src()流程图
此函数首先判断目标 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; } 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/125889.html

(0)
上一篇 2025-09-23 15:20
下一篇 2025-09-23 15:26

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信