Рассчитать контрольную сумму Интернета (он же 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;
}