Форум: "Сети";
Текущий архив: 2004.08.08;
Скачать: [xml.tar.bz2];
Внизподсчет контрольных сумм Найти похожие ветки
← →
ev (2004-06-08 19:55) [0]подскажите, как считаются контрольные суммы у пакетов Ethernet, IP, ICMP, TCP, UDP
← →
Verg © (2004-06-08 21:00) [1]Контрольная сумма.
С контрольной суммой в сетях TCP/IP имеется некоторая путаница.
Алгоритм ее подсчета, претендующий быть определением, изложен в
RFC про IP. Если написать программу по этому алгоритму, она будет
считать все, что угодно, но не ту контрольную сумму, которую
считают Ваши соседи на сети. Есть даже отдельный RFC про контрольную
сумму, RFC1071, но вопрос все равно остается мутным.
На самом деле, все просто. Контрольная сумма считается следующим
образом. Пусть есть блок из N байтов. Если N нечетное, то мы дополняем
этот блок еще одним, нулевым, байтом. Потом мы пробегаем этот блок
последовательно, вытаскивая из него по 16-битному слову. Каждое такое
слово мы считаем 16-битным числом с сетевым порядком байтов, и складываем
их в некотором 16-битном аккумуляторе. Биты, вылазящие из аккумулятора
вверх, в переполнение, мы прибавляем к младшему разряду аккумулятора.
Результат этого суммирования побитно инвертируется и становится, собственно,
значением подсчитанной контрольной суммы.
Такая контрольная сумма обладает некоторыми забавными свойствами.
Первое, если при суммировании 16-битных слов в каждом из них переставить
между собой байты, то мы получим ту же самую контрольную сумму, только
с переставленными байтами в ней. Это получается из-за симметрии между
младшим и старшим байтами контрольной суммы: перенос из младшего байта
уходит в старший байт, и наоборот. Поэтому при подсчете суммы мы можем
вообще забыть про разницу между внутренним и севевым порядками байт,
и контрольная сумма будет всегда получаться с сетевым порядком байт.
Вот переносимая функция, которая считает контрольную сумму:
ushort net_checksum( void *data, size_t len )
{
ulong sum = 0;
ushort *dp = data;
ushort sum_s;
int words = len >> 1;
while( words -- )
sum += *dp ++;
if( len & 1 )
sum += *(uchar*) dp;
sum = (ushort) sum + (sum >> 16) & 0xffff;
sum_s = (ushort) sum + (ushort)(sum >> 16);
return sum_s != 0xffff ? ~sum_s : sum_s;
}
Второе. Контрольная сумма непустого блока никогда не равна нулю.
Третье. Если мы возьмем блок данных, в котором отведено место под
контрольную сумму:
struct { ushort sum; opaque data; } my_data;
Потом обчексуммим этот блок:
my_data.sum = 0;
my_data.sum = net_checksum( &my_data, sizeof( my_data ) );
То контрольная сумма блока с уже положенной в него контрольной суммой
будет всегда равна 0xffff:
if( net_checksum( &my_data, sizeof( my_data ) ) != 0xffff )
printf( "CRC error!\n" );
Четвертое. Контрольные суммы можно складывать, не забывая добавлять
переносы к младшему биту. Это дает то же самое, как если бы из двух
блоков составить один длинный, и подсчитать контрольную сумму от него.
По очевидным причинам блок, который идет первым, должен иметь четную
длину. У такого сложения есть одна паталогия: если контрольные суммы
двух половинок взаимно комплементарны, то после сложения получится 0,
а после подсчета суммы от суммарного блока получилось бы 0xffff.
Это надо учитывать:
ushort net_chksum_add( ushort s1, ushort s2 )
{
ulong sum = (ulong) s1 + (ulong) s2;
ushort usum = (ushort) sum;
sum = sum > 0xffff ? usum + 1 : usum;
if( !sum && (s1 || s2) )
return 0xffff;
else
return sum;
}
Кстати, число 0xffff для контрольной суммы играет роль нуля: оно получается
при чексуммировании блока из одних нулей, и прибавление этого числа к
контрольной сумме ее не меняет.
И теперь, как этим пользоваться.
Перед отправкой пакета мы говорим:
ip -> ip_sum = 0;
ip -> ip_sum = net_checksum( ip, sizeof( ip_header ) );
При приеме мы проверяем:
if( net_checksum( ip, ip_hdr_size ) != 0xffff )
error( "Invalid checksum!" );
Copyright (c) by Alexander Pevzner (pzz@pzz.msk.ru), 1998-1999
← →
ev (2004-06-08 21:17) [2]большое спасибо :)
пойду реализовывать для микроконтроллера....
Страницы: 1 вся ветка
Форум: "Сети";
Текущий архив: 2004.08.08;
Скачать: [xml.tar.bz2];
Память: 0.47 MB
Время: 0.033 c