Рассчитать контрольную сумму Интернета (он же IP, он же RFC791) в C#

Интересно, что я могу найти реализации контрольной суммы в Интернете почти на всех языках, кроме C#. У кого-нибудь есть реализация, которой можно поделиться?

Помните, что интернет-протокол указывает, что:

"Поле контрольной суммы является 16-битным дополнением к сумме дополнения всех 16-битных слов в заголовке. Для целей вычисления контрольной суммы значение поля контрольной суммы равно нулю".

Более подробное объяснение можно найти у доктора Мат.

Есть несколько доступных указателей эффективности, но на данный момент меня это не особо беспокоит.

Пожалуйста, включите ваши тесты! (Редактировать: действительный комментарий, касающийся тестирования чужого кода - но я отказываюсь от протокола и у меня нет собственных тестовых векторов, и я предпочел бы выполнить его модульное тестирование, чем запустить в производство, чтобы увидеть, соответствует ли он тому, что используется в настоящее время!;-)

Изменить: Вот некоторые модульные тесты, которые я придумал. Они тестируют метод расширения, который выполняет итерацию всей коллекции байтов. Пожалуйста, прокомментируйте, если вы нашли ошибку в тестах.

[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);
}

3 ответа

Решение

Ну, я выкопал реализацию из старой базы кода, и она прошла тесты, которые я указал в вопросе, так что вот оно (как метод расширения):

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;
}

Я знал, что у меня это где-то хранится... http://cyb3rspy.wordpress.com/2008/03/27/ip-header-checksum-function-in-c/

Я сделал реализацию вычисления контрольной суммы заголовка IPv4, как определено в RFC 791.

Методы расширения

      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;

Расчет контрольной суммы

      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;
}
Другие вопросы по тегам