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


这里提取版本3分析:

  • 前期先确保4字节对齐,如果不是4字节对齐,就补到4字节对齐 。
  • 后面采用32 bit累加 。溢出后,在低位+1 。
    • 为什么?:这里读者可能会有个疑问,IP数据包的校验和不是要求16 bit求和的吗?这里为什么能用32 bit求和?
    • 答:起始要求是16 bit,但是实际计算时只要大于16 bit即可,因为到最后,可以把高位折叠加到低位 。
    • 例子:按32bit累加,溢出就在低位+1 。其实就是两组两个(高、低)16 bit对应累加,低16 bit累加的进位给高16 bit里加回1了 。而高16 bit累加的进位在底16 bit里加回1了(手动) 。这样,累加到最后剩下32bit 。把高16bit和低16bit进行累加,进位再加1即可快速得到16bit的校验和 。
  • 数据后部分可能不是8字节对齐,所以剩余的字节也需要16bit校验和处理 。
思路图:
【lwip】09-IPv4协议&超全源码实现分析

文章插图
由于目的是16 bit的校验和 。其实可以看成两组2个8bit对应相加,低8bit组进位给高8bit组,高8bit组进位给低8bit组 。所以相加值是对应高、低8bit相互独立的 。
而下面函数就是利用这个特性,如果首字节为奇地址,先单独取出来放到t的高地址,因为后续的统计字节顺序是返的 。等待全部统计完毕后,再把两个字节顺序调换即可 。
如果是偶地址开始,那符合校验和规则,最后不需要调换字节顺序 。
#if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 *//** * An optimized checksum routine. Basically, it uses loop-unrolling on * the checksum loop, treating the head and tail bytes specially, whereas * the inner loop acts on 8 bytes at a time. * * @arg start of buffer to be checksummed. May be an odd byte address. * @len number of bytes in the buffer to be checksummed. * @return host order (!) lwip checksum (non-inverted Internet sum) * * by Curt McDowell, Broadcom Corp. December 8th, 2005 */u16_tlwip_standard_chksum(const void *dataptr, int len){const u8_t *pb = (const u8_t *)dataptr; /* 取数据的地址 */const u16_t *ps;u16_t t = 0;const u32_t *pl;u32_t sum = 0, tmp;int odd = ((mem_ptr_t)pb & 1); /* 判断是否为奇地址 */if (odd && len > 0) { /* 如果不是2直接对齐 *//* 缓存奇地址上的字节,存于 t 的高位 。数据地址偏移为偶,2字节对齐 。*//* 存到高位是为了和后面字节序保持一致,方便在最后一次性更正 。*/((u8_t *)&t)[1] = *pb++;len--; /* 字节数-1 */}/* 2字节对齐的数据起始地址 */ps = (const u16_t *)(const void *)pb;if (((mem_ptr_t)ps & 3) && len > 1) {/* 如果不是4字节对齐 *//* 把多出来的两字节保存到sum */sum += *ps++;len -= 2;}/* 4字节对齐的数据起始地址 */pl = (const u32_t *)(const void *)ps;while (len > 7){tmp = sum + *pl++;/* ping */if (tmp < sum) {tmp++;/* 溢出,手动+1 */}sum = tmp + *pl++;/* pong */if (sum < tmp) {sum++;/* 溢出,手动+1 */}len -= 8;}/* 折叠高、低16bit */sum = FOLD_U32T(sum);/* 处理剩余的字节 */ps = (const u16_t *)pl;/* 2字节处理 */while (len > 1) {sum += *ps++;len -= 2;}/* 剩余单字节 */if (len > 0) {/* 补到前面t的低位 */((u8_t *)&t)[0] = *(const u8_t *)ps;}sum += t;/* 把t也一起16bit校验和 *//* 两次折叠高、低16bit */sum = FOLD_U32T(sum);sum = FOLD_U32T(sum);if (odd) { /* 因为前面是从第二个字节和第三个字节开始进行统计的,字节序反了,这里在结果更正 。*/sum = SWAP_BYTES_IN_WORD(sum);}return (u16_t)sum; /* 返回校验和 */}#endif9.3.12 源IP字段占用32 bit 。
为源主机的IP地址 。
9.3.13 目标IP字段占用32 bit 。
为目标主机的IP地址 。
9.3.14 选项字段0到40字节 。
对于IP数据报首部来说,其大小必须为4字节的整数倍 。

经验总结扩展阅读