TCP checksum

计算 TCP 首部中使用的校验和

  • TCP checksum is a 16-bit field in TCP header used for error detection
  • Same as IP checksum,
    TCP checksum is the 16-bit one’s complement of the one’s
    complement sum of all 16-bit words in the computation data.
  • Checksum 图:
  • TCP checksum computing includes:
    • UDP 数据报和 TCP 段中使用的校验和的计算都包含一个 12 字节长的伪首部.
      12 bytes TCP 伪首部 (12 bytes TCP pseudo header,
      from the IP header and computed), includes:
    • ipHdr srcIP 4 bytes
    • ipHdr dstIP 4 bytes
    • 1 reserved byte: 0x00
    • 1 byte protocol: from ipHdr: 0x06 for TCP
    • 2 byte computed TCP length
    • TCP “Pseudo Header” For Checksum Calculation 图:
    • Original TCP segment(length: is above TCP length), might+ padding,
      includes:
    • TCP header
    • TCP data, includes:
      • TCP data
      • Padded as needed with zero bytes at the end to make a multiple
        of two bytes

TCP “Pseudo Header” For Checksum Calculation

Field Name Bytes Description
Source Address 4 The 32-bit IP address of the originator of the datagram, taken from the IP header
Destination Address 4 The 32-bit IP address of the intended recipient of the datagram, also from the IP header
Reserved 1 8 bits of zeroes
Protocol 1 The Protocol field from the IP header. This indicates what higher-layer protocol is carried in the IP datagram. Of course, we already know what this protocol is, it’s TCP! So, this field will normally have the value 6
TCP Length 2 The length of the TCP segment, including both header and data. Note that this is not a specific field in the TCP header; it is computed
/**
 * 计算 TCP 首部中使用的校验和
 * @param cleanChecksum true to cleanup original checksum before compute
 * @param ipHdr IP首部
 * @return in network byte order
 */
uint16_t computeChecksum(
    bool const cleanChecksum, IpHdr const* const ipHdr) const
{
    // get computed TCP length, also is original TCP segment length
    size_t const protocolSize = ipHdr->computeProtocolSize();
    size_t const n = 12 + protocolSize;
    uint8_t data[n];
    // ipHdr srcIP
    ::memcpy(data, &(ipHdr->sourceIpAddress), 4);
    // ipHdr dstIP
    ::memcpy(data + 4, &(ipHdr->destinationIpAddress), 4);
    // reserved
    data[8] = 0x00;
    // protocol
    data[9] = ipHdr->protocol;
    // computed TCP length
    data[10] = (protocolSize & 0xff00) >> 8;
    data[11] = protocolSize & 0xff;
    // original TCP segment
    tcphdr* const tcpHdr = reinterpret_cast<tcphdr*>(data + 12);
    ::memcpy(tcpHdr, this->tcpHdr, protocolSize);
    // cleanup original checksum when need
    if (cleanChecksum) {
        tcpHdr->check = 0;
    }
    // compute
    return ethpacket::ComputeChecksum(
        reinterpret_cast<uint16_t const*>(data), n);
}

Ethernet packet checksum

计算以太网包首部中使用的校验和

算法: first summing all numbers(every 16-bit, might with padding)
and adding the carry (or carries) to the result,
then compute ones' complement:
– 发送端或接收端计算发送端的校验和时, 需要首先把校验和字段设置为 0
– 然后累积每个 16-bit
– 然后将最终结果"折叠"成 16 bits
– 然后 ones' complement (算法: 所有按位取反)
– 最后只要低 16 bits, 最后结果是网络字节顺序

/**
 * 计算以太网包首部中使用的校验和
 * @param data data to compute, in network byte order
 * @param bytes data size in bytes
 * @return in network byte order
 */
uint16_t ComputeChecksum(uint16_t const* const data, size_t const bytes)
{
    // initialize sum to zero
    unsigned long sum = 0;
    // n to keep current left bytes
    size_t n;
    // data index for each 16-bit
    long dataIndex;
    // accumulate sum
    for (n = bytes, dataIndex = -1; n > 1; n -= 2) {
        sum += data[++dataIndex];
    }
    /*
     * if any bytes left, pad the bytes with 0x00 and add
     * 最后一个 16-bit, 网络字节序:
     * 最后一个字节(位于最后一个 16-bit高位): MSB, then 0x00,
     * 而 MSB 在低地址, 因此计算使用最后一个 16-bit 是 0x00 then 最后一个字节:
     * - 即最后一个字节,
     * - => ((data[++dataIndex] >> 8) & 0xff) ... but data[...] bad
     * - => ((data[++dataIndex]) & htons(0xff00) ... but data[...] bad
     * - => uint8_t const msb = (reinterpret_cast<uint8_t const* const>(data))[
     *          long(bytes) - 1];
     */
    if (n > 0) {
        uint8_t const msb = (reinterpret_cast<uint8_t const* const>(data))[
            long(bytes) - 1];
        sum += msb;
    }
    // fold sum to 16 bits: add carrier to result
    while (sum >> 16) {
        sum = (sum >> 16) + (sum & 0xffff);
    }
    /*
     * ones' complement:
     * The ones' complement of a binary number is defined as the value
     * obtained by inverting all the bits in the binary representation of the
     * number (swapping 0s for 1s and vice versa)
     */
    return uint16_t(~sum);
}