Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

32-bit UDP Checksum

0.00/5 (No votes)
30 Nov 2003 2  
An article on using 32-bit integers for checksum

Introduction

This article presents a checksum routine for UPD/IP packets using 32-bit groupings.

Background

See RFC 768 to read about the UDP protocol and the UDP checksum. In particular, you need to understand the pseudo header used for the UDP checksum. See RFC 1071 for a discourse on the theory of the internet checksum.

I use winpcap (http://winpcap.polito.it) to monitor a UDP data stream and I needed a checksum routine, but all the examples I found were based on inefficient 16-bit groupings of the bytes. However, in RFC 1071 I found three key principles that allow for a more efficient process:

First, the size of the groupings doesn't matter if you fold the result back into a 16 bit word at the end. In C it is not easy to check for integer overflow, so you need an accumulator that can hold all of the overflows from all the summing. For 32 bit groupings, the accumulator needs to be 64 bits. At the end of the process you can fold the 64 bits into 16 bits to get a valid checksum result.

Second, the byte order doesn't matter until the end if you are generating a checksum to insert into a packet. If you are only checking the checksum at the receiving end, the result should be 0xFFFF, so in this case the byte order doesn't matter at all. Thus, you save a little overhead by not calling ntohs() for each grouping of bytes.

Finally, zeroes don't affect the checksum result, so padding a leftover byte with zeroes to form a long integer is ok.

Using the code

To use the UDPchecksum() routine you can simply paste it into your winpcap project (or any project that has a packet capture tool) and rename the global pointers to match the names you choose. Winpcap fills a structure with the winpcap header information along with a buffer containing the captured packet. In my version these pointers were members of a class, but I removed that detail for this example. They must point to the winpcap entities that contain a header and a packet, respectively, before the call to UDPchecksum(). Of course, you could make the pointers private and pass them in the function call.

There are two cheap (efficient) tricks in this version:

First, the checksum pseudo header is created by mangling the IP header. The pseudo header consists of the following elements:

  • IP Source Address = 4 bytes
  • IP Destination Address = 4 bytes
  • Protocol = 2 bytes
  • UDP Length = 2 bytes

These must be added to the checksum to get the correct result. Since the IP Source and Destination addresses along with the Protocol field are contiguous and immediately precede the UDP section, we already have a good start. I simply copied the UPD Length field to the IP Checksum field of the IP header, and I zeroed out the IP Time to Live byte. Now the pseudo header is complete and contiguous with the UDP header. The checksum process starts at byte 22 of the packet, which is the beginning of the pseudo header I just assembled.

Second, I solved the problem of what to do when the data length is not a multiple of 4 bytes by replacing the four bytes immediately following the UDP packet with zeroes. These will either be the ethernet Frame Check Sequence or ethernet filler. Again, I don't mind mangling the leftover ethernet fields to simplify processing the UDP packet. Now, if a single byte remains to be read at the end of a UDP packet, three zeroes are read with it to form a 32 bit grouping, and there is no affect on the result.

The return from my version is 'true' if the checksum result is correct.

#define UDP_LEN 38        // position of UDP Header UDP Length field

#define IP_LIVE 22        // position of IP Header Time to Live field

#define IP_CHECKSUM 24        // position of IP Header IP Checksum field


// pointer to the header filled by winpcap for each captured packet

struct pcap_pkthdr    *pHeader;
// pointer to the buffer containing the packet captured by winpcap

u_char            *pPkt_data;

////////////////////////////////////////////////////////////////////////////

// UDPchecksum routine

//   expects pHeader to point to the winpcap pcap_pkthdr structure

//   expects pPkt_data to point to the captured packet buffer

//   returns true if the UDP portion of the packet has a valid checksum


bool UDPchecksum()
{
    // a 64 bit accumulator

    _int64 qwSum = 0;
    int nSize;
    // a byte pointer to allow pointing anywhere in the buffer

    // pHeader->caplen doesn't include the ethernet 

    // FCS and filler (4+ bytes at end)

    unsigned char *pB = (unsigned char *)(pPkt_data + pHeader->caplen);
    // pDW is a pointer to dwords for grouping bytes

    // initialize to point to the FCS (or filler if packet length < 46) 

    // after UDP

    ULONG *pDW  = (ULONG *)(pB);

    // The 4+ bytes received after UDP are ethernet FCS and 

    // filler - ok to mangle.

    // Put 0's after UDP for groupings of leftover bytes < 4 at

    // the end of UDP

    // (adding one, two, or three bytes == 0 into checksum will not 

    // affect the result)

    *pDW = 0;
    // construct the pseudo header starting at 22

    // (IP Source & Dest Addresses are already in place)

    *(pPkt_data + IP_LIVE) = 0;
    *(pPkt_data + IP_CHECKSUM + 1) = *(pPkt_data + UDP_LEN + 1);
    *(pPkt_data + IP_CHECKSUM) = *(pPkt_data + UDP_LEN);

    // point pDW to beginning of pseudo header

    pB = (pPkt_data + 22);
    pDW = (ULONG *)(pB);

    // set the size of bytes to inspect to the size of the UDP portion

    // plus the pseudo header starting at the IP header 'Time to Live' byte.

    dwSize = pHeader->caplen - IP_LIVE;

    while( nSize > 0 )  {
          // Add DWORDs into the accumulator

        // This loop should be 'unfolded' to increase speed

        qwSum +=  (ULONG) *pDW++;
        nSize -= 4;
    }
    //  Fold 64-bit sum to 16 bits

    while (qwSum >> 16)
       qwSum = (qwSum & 0xffff) + (qwSum >> 16);
    
    // a correct checksum result is 0xFFFF (at the receiving end)

    return (qwSum == 0xFFFF);
}

History

No changes yet!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here