clibpcaptcpdumplibnet

Libnet TCP checksum error


I am trying to build a TCP packet using libnet library. I use '0' for autocomputation of checksum value in the libnet_build_tcp function. However, it seems the checksum ignores the pseudo-header when being computed resulting in a useless packet because of checksum error. Does anyone know how to solve this?


Solution

  • As far as I can see in the code, as long as you haven't used libnet_toggle_checksum(l, ptag, 1); your 0 in the sum parameter of libnet_build_tcp() should be causing it to autocompute a checksum for you by calling libnet_pblock_setflags(p, LIBNET_PBLOCK_DO_CHECKSUM).

    I couldn't really tell you how to solve it but since you are the one building the packet maybe you could opt for creating your own checksum?

    The TCP pseudoheader is a 12 byte field containing the source and destination IP addresses which are both 4 bytes each, a reserved field which is 2 bytes and always set to zeros, and a 4 byte TCP segment length which is equal to the size of the TCP header + the payload length.

    You could create something like this:

    First create your variables:

    int bytesRead;
    int pseudoHeaderLength;
    int payloadLength;
    int tcpHdrLength;
    int tcpSegmentLength;
    int ipHdrLength;
    int headerLengths;
    int ethIPHdrLengths;
    
    struct ethhdr *ethHdr;
    struct iphdr *ipHdr;
    struct tcphdr *tcpHdr;
    
    u_int8_t *pkt_data;
    u_int8_t pseudoHeader[12];
    u_int8_t packetBuffer[1600];
    u_int16_t newChecksum;
    

    Then create the actual function where b->len is the total size of the packet (just add all the headers + the payload and get the total size) and you would just have to memcpy your header and data to pkt_data:

    ethHdr = (struct ethhdr*)pkt_data;
    ipHdr = (struct iphdr*)(pkt_data + ETH_HLEN);
    
    ipHdrLength = ipHdr->ihl * 4;
    
    ethIPHdrLengths = ETH_HLEN + ipHdrLength;
    
    tcpHdr = (struct tcphdr*)(pkt_data + ethIPHdrLengths);
    
    tcpHdrLength = tcpHdr->doff * 4;
    
    headerLengths = ethIPHdrLengths + tcpHdrLength;
    
    payloadLength = b->len - headerLengths;
    
    tcpSegmentLength = tcpHdrLength + payloadLength;
    
    pseudoHeaderLength = tcpSegmentLength + 12;
    
    pseudoHeader[0] = pkt_data[30];
    pseudoHeader[1] = pkt_data[31];
    pseudoHeader[2] = pkt_data[32];
    pseudoHeader[3] = pkt_data[33];
    pseudoHeader[4] = pkt_data[26];
    pseudoHeader[5] = pkt_data[27];
    pseudoHeader[6] = pkt_data[28];
    pseudoHeader[7] = pkt_data[29];
    pseudoHeader[8] = 0x00;
    pseudoHeader[9] = 0x06;
    pseudoHeader[10] = (tcpSegmentLength >> 8) & 0xFF;
    pseudoHeader[11] = tcpSegmentLength & 0xFF;
    
    bytesRead = 0;
    
    for(i = 0; i < 12; i++) {
        packetBuffer[bytesRead] = pseudoHeader[i];
        bytesRead++;
    }
    
    for(i = ethIPHdrLengths; i < headerLengths; i++) {
        packetBuffer[bytesRead] = pkt_data[i];
        bytesRead++;
    }
    
    for(i = b->len - payloadLength; i < b->len; i++) {
        packetBuffer[bytesRead] = pkt_data[i];
        bytesRead++;
    }
    
    newChecksum = checksum((uint16_t *)packetBuffer, pseudoHeaderLength);
    

    And just use the checksum function provided by https://www.rfc-editor.org/rfc/rfc1071 to calculate the checksum over the buffer:

    uint16_t checksum(uint16_t *addr, int len)
    {
    
    int count = len;
    register uint32_t sum = 0;
    uint16_t answer = 0;
    
    while (count > 1) {
        sum += *(addr++);
        count -= 2;
    }
    
    if (count > 0) {
        sum += *(uint8_t *) addr;
    }
    
    while (sum >> 16) {
        sum = (sum & 0xffff) + (sum >> 16);
    }
    
    answer = ~sum;
    
    return (answer);
    }
    

    I use this in a realistic environment to calculate checksums at 5 million PPS.