【lwip】09-IPv4协议&超全源码实现分析( 九 )

< IP_HLEN) { /* 长度校验 */LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output: LWIP_IP_HDRINCL but pbuf is too short\n"));IP_STATS_INC(ip.err);MIB2_STATS_INC(mib2.ipoutdiscards);return ERR_BUF;}iphdr = (struct ip_hdr *)p->payload;ip4_addr_copy(dest_addr, iphdr->dest); /* 获取目的IP地址 */dest = &dest_addr;}/* 状态记录 */IP_STATS_INC(ip.xmit);LWIP_DEBUGF(IP_DEBUG, ("ip4_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], (u16_t)netif->num));ip4_debug_print(p);#if ENABLE_LOOPBACK /* 环回功能 */if (ip4_addr_eq(dest, netif_ip4_addr(netif)) /* 目的IP为源网卡IP,则是环回 */#if !LWIP_HAVE_LOOPIF|| ip4_addr_isloopback(dest) /* 目的IP是环回IP字段,也是环回 */#endif /* !LWIP_HAVE_LOOPIF */) {/* 数据包环回,则不用通过数据链路层,直达对应网卡的环回链表即可 */LWIP_DEBUGF(IP_DEBUG, ("netif_loop_output()"));return netif_loop_output(netif, p);}#if LWIP_MULTICAST_TX_OPTIONSif ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) { /* 该pbuf是要环回的UDP组播 */netif_loop_output(netif, p); /* 环回到本网卡 */}#endif /* LWIP_MULTICAST_TX_OPTIONS */#endif /* ENABLE_LOOPBACK */#if IP_FRAG/* 如果接口mtu设置为0,不用分片 *//* 分片检查 */if (netif->mtu && (p->tot_len > netif->mtu)) { /* IP报文超出网卡MTU,则需要分片处理 */return ip4_frag(p, netif, dest); /* 需要分片处理 */}#endif /* IP_FRAG *//* 不需要分片处理 */LWIP_DEBUGF(IP_DEBUG, ("ip4_output_if: call netif->output()\n"));return netif->output(netif, p, dest); /* IP层发送数据包 。至此,IP报文处理完毕,下一步交给ARP或者直接到数据链路处理 。*/}9.7.5 IP数据报分片注意:lwip分片偏移不支持IP首部带选项字段的 。
从IP报文首部就可知,有分片概念 。
不是每个底层网卡都能承载每个 IP 数据报长度的报文 。如:

  • 以太网帧最大能承载 1500 个字节的数据 。
  • 某些广域网链路的帧可承载不超过 576 字节的数据 。
一个链路层帧能承载的最大数据量叫做最大传送单元(Maximum TransmissionUnit,MTU) 。
IP 数据报的分片偏移量是用 8 的整数倍记录的,所以每个数据报中的分片数据大小也必须是 8 的整数倍 。
IP数据报分片主要关注IP首部的标识字段、标志字段和分片偏移量字段 。具体往前看 。
参考图:
【lwip】09-IPv4协议&amp;超全源码实现分析

文章插图
相关源码实现在ip4_frag.c
相关宏:
  • LWIP_NETIF_TX_SINGLE_PBUF:分片是否支持新建一整个pbuf处理 。
    • 1:分片时,直接申请各个IP分片包的pbuf即可(含IP首部+数据区) 。
    • 0:分片时 , 申请各个分片的管理区,MEMP_FRAG_PBUF类型 。其数据结构为pbuf_custom_ref 。该数据结构包含本次IP分片包的原IP报文pbuf地址,释放引用的api,指向分片IP报文数据区的pbuf 。然后将这个分片IP首部的pbuf和这个IP报文数据区的pbuf拼接起来即可 。组成新的分片IP报文 。
相关数据结构:
pbuf_custom数据结构:
/** A custom pbuf that holds a reference to another pbuf, which is freed * when this custom pbuf is freed. This is used to create a custom PBUF_REF * that points into the original pbuf. */struct pbuf_custom_ref {/** 'base class' */struct pbuf_custom pc; /* 用户的控制区 。包含一个pbuf和一个释放该pbuf的api *//* 指向被引用的原始pbuf的指针 */struct pbuf *original;};pbuf_custom_ref数据结构:
struct pbuf_custom {/* The actual pbuf */struct pbuf pbuf;/**This function is called when pbuf_free deallocates this pbuf(_custom) */pbuf_free_custom_fn custom_free_function;};相关数据结构图:
  • 没开启LWIP_NETIF_TX_SINGLE_PBUF宏的IP分片报文数据结构:

【lwip】09-IPv4协议&amp;超全源码实现分析

文章插图
  • 开启LWIP_NETIF_TX_SINGLE_PBUF宏的分片IP报文数据结构(按简单的画):

【lwip】09-IPv4协议&amp;超全源码实现分析

文章插图
ip4_frag()