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

LWIP_NETIF_TX_SINGLE_PBUF宏分支分析更加助于理解 。

  • 处于非LWIP_NETIF_TX_SINGLE_PBUF宏分支:如果读者需要分析,然后看不懂这个分支,可以看下我的笔记:
    • 先申请一个保存分片IP首部的pbuf:rambuf
    • 然后再申请一个pbuf_custom_ref数据结构的伪pbuf:pcr
    • 然后把未分片的IP报文的pbuf对应分片的数据区地址给到pcr->pc->pbuf->payload,共享数据区内存嘛 。
    • 然后把分片IP首部的pbuf和分片IP的数据pbuf拼接起来:pbuf_cat(rambuf, pcr->pc->pbuf->payload);,这样就组成了分片的IP报文了 。
  • 【【lwip】09-IPv4协议&超全源码实现分析】/** * Fragment an IP datagram if too large for the netif. * * Chop the datagram in MTU sized chunks and send them in order * by pointing PBUF_REFs into p. * * @param p ip packet to send * @param netif the netif on which to send * @param dest destination ip address to which to send * * @return ERR_OK if sent successfully, err_t otherwise */err_tip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest){struct pbuf *rambuf; /* 分片的pbuf结构 */#if !LWIP_NETIF_TX_SINGLE_PBUF/* 用于处理分片IP报文与原IP报文数据区pbuf内存共享使用 */struct pbuf *newpbuf;u16_t newpbuflen = 0;u16_t left_to_copy;#endifstruct ip_hdr *original_iphdr;struct ip_hdr *iphdr;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; /* 发送的数据在原始数据报pbuf中的偏移量 */u16_t tmp;int mf_set; /* 传入的pbuf是否是分片包,后续是否还有更多分片 。*/original_iphdr = (struct ip_hdr *)p->payload;iphdr = original_iphdr;if (IPH_HL_BYTES(iphdr) != IP_HLEN) { /* IP首部长度字段 *//* ip4_frag() 不支持IP首部带选项字段的IP报文分片 */return ERR_VAL;}LWIP_ERROR("ip4_frag(): pbuf too short", p->len >= IP_HLEN, return ERR_VAL);/* 保存原始的分片偏移量字段 */tmp = lwip_ntohs(IPH_OFFSET(iphdr));ofo = tmp & IP_OFFMASK;/* 判断pbuf包是否已经被分片,被分片后后续是否还有更多分片 。如果有 , 那么本次分片的最后一个分片不能标志后续没有更多分片 。*/mf_set = tmp & IP_MF;/* 获取还剩多少数据还没分片处理 */left = (u16_t)(p->tot_len - IP_HLEN);while (left) { /* 循环分片处理 *//* 当前分片需要填充的数据size */fragsize = LWIP_MIN(left, (u16_t)(nfb * 8));#if LWIP_NETIF_TX_SINGLE_PBUF /* 分片支持新建整个pbuf *//* 申请新的pbuf内存资源装载当前分片 */rambuf = pbuf_alloc(PBUF_IP, fragsize, PBUF_RAM);if (rambuf == NULL) {goto memerr;}LWIP_ASSERT("this needs a pbuf in one piece!",(rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));/* 拷贝IP报文数据区的数据到分片IP包中 */poff += pbuf_copy_partial(p, rambuf->payload, fragsize, poff);/* pbuf腾出IP首部空间 */if (pbuf_add_header(rambuf, IP_HLEN)) {pbuf_free(rambuf);goto memerr;}/* 填充原IP报文首部到当前分片 , 后面再处理分片的IP首部 */SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);iphdr = (struct ip_hdr *)rambuf->payload;#else /* LWIP_NETIF_TX_SINGLE_PBUF */ /* 分片支持数据区共享 。这个宏分支,感兴趣的同学可以看下 *//* 先申请分片IP首部的pbuf */rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);if (rambuf == NULL) {goto memerr;}LWIP_ASSERT("this needs a pbuf in one piece!",(rambuf->len >= (IP_HLEN)));/* 拷贝原IP报文首部到分片IP报文首部 */SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);iphdr = (struct ip_hdr *)rambuf->payload;/* 本次分片IP报文占用的size */left_to_copy = fragsize;while (left_to_copy) { /* 分片IP报文组装完毕为止 */struct pbuf_custom_ref *pcr; /* 引用原IP报文数据区pbuf的伪pbuf */u16_t plen = (u16_t)(p->len - poff); /* 原IP报文当前pbuf节点还剩多少数据没处理 */LWIP_ASSERT("p->len >= poff", p->len >= poff);newpbuflen = LWIP_MIN(left_to_copy, plen); /* 选出能处理的size */if (!newpbuflen) { /* 原IP报文当前pbuf所有数据已经分片处理完毕,可以处理下一个pbuf */poff = 0;p = p->next; /* 跳到下一个分片IP报文处理继续组装当前IP分片 */continue;}pcr = ip_frag_alloc_pbuf_custom_ref(); /* 为伪pbuf pcr申请资源 */if (pcr == NULL) {pbuf_free(rambuf);goto memerr;}/* 把原IP报文数据区当前分片的数据pbuf引用到pcr->pc->pbuf这个pbuf中 */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_ref(p); /* 当前pbuf引用值+1 */pcr->original = p; /* 保存当前pbuf地址到pcr */pcr->pc.custom_free_function = ipfrag_free_pbuf_custom; /* 专门的伪pbuf释放API *//* 拼接pbuf,把本次分片IP首部、数据区的各个pbuf都链在一起 */pbuf_cat(rambuf, newpbuf);left_to_copy = (u16_t)(left_to_copy - newpbuflen); /* 检查本次分片IP报文是否整理完毕 */if (left_to_copy) { /* 还没填充完毕,需要继续填充 */poff = 0;p = p->next;}}poff = (u16_t)(poff + newpbuflen); /* 分片在原IP报文中的偏移值更新 */#endif /* LWIP_NETIF_TX_SINGLE_PBUF *//* 更正分片IP首部 *//* 本次分片是否为最后一次分片 */last = (left <= netif->mtu - IP_HLEN);/* 设置新的偏移量和MF标志 */tmp = (IP_OFFMASK & (ofo));if (!last || mf_set) { /* 本函数分片处理的最后一片 且 传入的原IP报文不是分片报文 *//* 标志位第三位更多分片标记为0,已经为顶层原IP报文的最后一片 */tmp = tmp | IP_MF;}IPH_OFFSET_SET(iphdr, lwip_htons(tmp)); /* 重写分片IP首部的标志位字段+分片偏移量字段 */IPH_LEN_SET(iphdr, lwip_htons((u16_t)(fragsize + IP_HLEN))); /* 重写分片IP报文总长度 */IPH_CHKSUM_SET(iphdr, 0); /* 设置分片IP报文首部校验字段 , 默认为0 */#if CHECKSUM_GEN_IPIF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); /* 更新分片IP报文首部校验字段 */}#endif /* CHECKSUM_GEN_IP *//* 发送分片IP报文 */netif->output(netif, rambuf, dest);/* 分片状态信息记录 */IPFRAG_STATS_INC(ip_frag.xmit);/* Unfortunately we can't reuse rambuf - the hardware may still be* using the buffer. Instead we free it (and the ensuing chain) and* recreate it next time round the loop. If we're lucky the hardware* will have already sent the packet, the free will really free, and* there will be zero memory penalty.*//* 释放分片空间 。因为这rambuf只是分片函数内部处理创建的,所以调用netif->output()发送出去后要在这里释放掉 。*/pbuf_free(rambuf);/* 需要处理的分片待发送数据减少 */left = (u16_t)(left - fragsize);/* 分片偏移量增加 */ofo = (u16_t)(ofo + nfb);}MIB2_STATS_INC(mib2.ipfragoks);return ERR_OK;memerr:MIB2_STATS_INC(mib2.ipfragfails);return ERR_MEM;}

    推荐阅读