IPv4协议首部校验位算法实现
- IPv4与Ipv6的首部区别
- IPv4 数据包结构
- C算法实现
- Java算法实现
记录一哈, 嗅探路由数据转发的需求, 需要对IPv4的校验位进行重新计算填充.
特此记录下IPv4协议求和校验位的算法实现.
IPv4与Ipv6的首部区别
Ipv4协议包含首部校验和字段等信息, 导致路由转发时需要进行重新计算, 增加了延时成本, 在IPv6中取消了对应的校验和字段.
IPv4 数据包结构
这里用Wireshark随便抓个包看下Network Layer的Packet.
这里看到IPv4 首部的Packet
版本: 4
首部长度: 20字节
服务类型TOS: 0
报文总长度: 1440字节
生存时间TTL: 49
协议类型: TCP
首部校验和:
源IP地址:
目标IP地址:
Packet首部数据:
阐述下校验和的算法实现
- 在发送数据时,为了计算数IP据报的校验和, 需要把校验位置0. 即
0x0bc2 => 0x0000
Packet首部数据:
45 00 05 a0 c7 79 40 00 31 06 [00 00] 8c f9 22 16 c0 a8 01 65
首部以16位(2字节 无符号Short数据类型 )为单位划分, 进行求和
Packet首部数据:
4500 05a0 c779 4000 3106 [0000] 8cf9 2216 c0a8 0165
1 2 3 4 5 6 7 8 9 10 11 | 0x4500 +0x05a0 +0xc779 +0x4000 +0x3106 +0x0000 => header-checksum 已经重置为0 +0x8cf9 +0x2216 +0xc0a8 +0x0165 =0x2f43b |
- 由于校验位为两个字节,
0x2f43b 高位2 溢出, 这里需要采用反码加法运算, 通用的补码运算都是直接丢掉溢出的高位, 这里需要将高位的值在低位进行相加.
即0x0002 + 0xf43b = 0xf43d - 这里再将
0xf43d 进行取反即可.1111 0100 0011 1011 => 0000 1011 1100 0100 取反后的十六进制为0x0bc2 - 取反后的值与checksum校验即可.
C算法实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /*求校验和函数*/ USHORT CheckSum(USHORT *buffer, int size) { unsigned long cksum=0; while (size > 1) { cksum += *buffer++; size -= sizeof(USHORT); } if (size) { cksum += *(UCHAR*)buffer; } /*对每个16bit进行二进制反码求和*/ cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >>16); return (USHORT)(~cksum); } |
Java算法实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public static void main(String[] args) { // 这里偷懒, 直接用Netty提供的ByteBuf了 final ByteBuf buffer = Unpooled.wrappedBuffer(new byte[]{ (byte) 0x45, 0x00, 0x05, (byte) 0xa0, (byte) 0xc7, 0x7a, (byte) 0x40, 0x00, (byte) 0x31, 0x06, 0x00, 0x00, (byte) 0x8c, (byte) 0xf9, 0x22, 0x16, (byte) 0xc0, (byte) 0xa8, 0x01, 0x65 }); int sum = 0; while (buffer.isReadable()) { // 这里可以去看java.nio.Bits#makeShort // (short)((b1 << 8) | (b0 & 0xff)); sum += buffer.readUnsignedShort(); } // ipv4首部10个16位short值相加 // 反码加法(高位溢出, 低位相加) sum = (sum >> 16 & 0xffff) + sum & 0xffff; // 取反 sum = ~sum & 0xffff; final String checksum = Integer.toHexString(sum); // 这里用的guava依赖.. System.out.println("checksum -> " + Strings.padStart(checksum, 4, '0')); } |
参考文献:
http://blog.chinaunix.net/uid-23782786-id-4076612.html