2

Interestingly, I can find implementations for the Internet Checksum in almost every language except C#. Does anyone have an implementation to share?

Remember, the internet protocol specifies that:

"The checksum field is the 16 bit one's complement of the one's complement sum of all 16 bit words in the header. For purposes of computing the checksum, the value of the checksum field is zero."

More explanation can be found from Dr. Math.

There are some efficiency pointers available, but that's not really a large concern for me at this point.

Please include your tests! (Edit: Valid comment regarding testing someone else's code - but I am going off of the protocol and don't have test vectors of my own and would rather unit test it than put into production to see if it matches what is currently being used! ;-)

Edit: Here are some unit tests that I came up with. They test an extension method which iterates through the entire byte collection. Please comment if you find fault in the tests.

[TestMethod()]
public void InternetChecksum_SimplestValidValue_ShouldMatch()
{
    IEnumerable<byte> value = new byte[1]; // should work for any-length array of zeros
    ushort expected = 0xFFFF;
    
    ushort actual = value.InternetChecksum();
    
    Assert.AreEqual(expected, actual);
}

[TestMethod()]
public void InternetChecksum_ValidSingleByteExtreme_ShouldMatch()
{
    IEnumerable<byte> value = new byte[]{0xFF};
    ushort expected = 0xFF;

    ushort actual = value.InternetChecksum();

    Assert.AreEqual(expected, actual);
}

[TestMethod()]
public void InternetChecksum_ValidMultiByteExtrema_ShouldMatch()
{
    IEnumerable<byte> value = new byte[] { 0x00, 0xFF };
    ushort expected = 0xFF00;

    ushort actual = value.InternetChecksum();

    Assert.AreEqual(expected, actual);
}
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Pat
  • 16,515
  • 15
  • 95
  • 114
  • 2
    "Belongs on plzsendtehcodez.com". :-P (Sorry, couldn't resist after seeing the "please include your tests" reference; I'd expect that you'd write your own tests to validate other people's implementations.) – C. K. Young Feb 02 '10 at 22:09
  • Haven't tried it, but looks like NBit (http://www.zer7.com/software/nbit and on NuGet) might have methods for this CRC, among others. – Pat Nov 23 '13 at 00:43

3 Answers3

4

I knew I had this one stored away somewhere... http://cyb3rspy.wordpress.com/2008/03/27/ip-header-checksum-function-in-c/

Jesper Palm
  • 7,170
  • 31
  • 36
  • Thanks Jesper, but that implementation fails the InternetChecksum_ValidSingleByteExtreme_ShouldMatch test (actual = 65535). I adapted your implementation to my method call by prefixing it with int start = 0; var header = value.ToArray(); int length = header.Length - 1; Note: The length had to be less than header.Length because your loop accesses header[i+1] (an exception was thrown). – Pat Feb 02 '10 at 22:43
1

Well, I dug up an implementation from an old code base and it passes the tests I specified in the question, so here it is (as an extension method):

public static ushort InternetChecksum(this IEnumerable<byte> value)
{
    byte[] buffer = value.ToArray();
    int length = buffer.Length;
    int i = 0;
    UInt32 sum = 0;
    UInt32 data = 0;
    while (length > 1)
    {
        data = 0;
        data = (UInt32)(
        ((UInt32)(buffer[i]) << 8)
        |
        ((UInt32)(buffer[i + 1]) & 0xFF)
        );

        sum += data;
        if ((sum & 0xFFFF0000) > 0)
        {
            sum = sum & 0xFFFF;
            sum += 1;
        }

        i += 2;
        length -= 2;
    }

    if (length > 0)
    {
        sum += (UInt32)(buffer[i] << 8);
        //sum += (UInt32)(buffer[i]);
        if ((sum & 0xFFFF0000) > 0)
        {
            sum = sum & 0xFFFF;
            sum += 1;
        }
    }
    sum = ~sum;
    sum = sum & 0xFFFF;
    return (UInt16)sum;
}
Pat
  • 16,515
  • 15
  • 95
  • 114
0

I have made an implementation of the IPv4 header checksum calculation, as defined in RFC 791.

Extension Methods

public static ushort GetInternetChecksum(this ReadOnlySpan<byte> bytes)
    => CalculateChecksum(bytes, ignoreHeaderChecksum: true);
public static bool IsValidChecksum(this ReadOnlySpan<byte> bytes)
    // Should equal zero (valid)
    => CalculateChecksum(bytes, ignoreHeaderChecksum: false) == 0;

The Checksum Calculation

using System.Buffers.Binary;

private static ushort CalculateChecksum(ReadOnlySpan<byte> bytes, bool ignoreHeaderChecksum)
{
    ushort checksum = 0;
    for (int i = 0; i <= 18; i += 2)
    {
        // i = 0   e.g. [0..2] Version and Internal Header Length
        // i = 2   e.g. [2..4] Total Length
        // i = 4   e.g. [4..6] Identification
        // i = 6   e.g. [6..8] Flags and Fragmentation Offset
        // i = 8   e.g. [8..10] TTL and Protocol
        // i = 10  e.g. [10..12] Header Checksum
        // i = 12  e.g. [12..14] Source Address #1
        // i = 14  e.g. [14..16] Source Address #2
        // i = 16  e.g. [16..18] Destination Address #1
        // i = 18  e.g. [18..20] Destination Address #2

        if (ignoreHeaderChecksum && i == 10) continue;
        ushort value = BinaryPrimitives.ReadUInt16BigEndian(bytes[i..(i + 2)]);

        // Each time a carry occurs, we must add a 1 to the sum
        if (checksum + value > ushort.MaxValue)
        {
            checksum++;
        }

        checksum += value;
    }
    
    // One’s complement
    return (ushort)~checksum;
}
Svek
  • 12,350
  • 6
  • 38
  • 69