Понимание функции контрольной суммы TCP

Я считаю, что функция контрольной суммы TCP делает следующее:

  1. Разбейте псевдоголовок и заголовок сегмента TCP и данные на 2-байтовые блоки.
  2. Добавьте однобайтовое дополнение 0 к концу последнего блока, если длина не 2 байта, чтобы сделать его 2 байта.
  3. Возьмите дополнение к сумме, чтобы получить контрольную сумму TCP.

Звучит достаточно просто. Поэтому я написал свой собственный универсальный checksum функция:

#include <inttypes.h>
#include <arpa/inet.h>

uint16_t checksum(uint16_t * data, int size) {
uint16_t sum = 0;

int i = 0, length = size / 2;

while (i < length) sum += data[i++];

if (size % 2) sum += data[i] & 0xFF00;

return htons(~sum);
}

Однако другие люди написали checksum функции, которые кажутся более сложными. Например:

uint16_t checksum(uint16_t * addr, int len) {
int nleft = len;
int sum = 0;

uint16_t * w = addr;
uint16_t answer = 0;

while (nleft > 1) {
sum += *w++;
nleft -= sizeof(uint16_t);
}

if (nleft == 1) {
*(uint8_t *) (&answer) = *(uint8_t *) w;
sum += answer;
}

sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
answer = ~sum;
return (answer);
}

У меня есть несколько вопросов относительно этого кода:

  1. Что значит утверждение *(uint8_t *) (&answer) = *(uint8_t *) w; на самом деле делать?
  2. Почему мы берем сумму как:

    sum = (sum >> 16) + (sum & 0xFFFF);
    sum += (sum >> 16);
    
  3. Способ вычисления контрольной суммы TCP изменился?

Я действительно не понимаю, почему мы делаем sum = (sum >> 16) + (sum & 0xFFFF), Рассматривать sum является 0xABCD:

0xABCD >> 16    == 0x0000

0xABCD & 0xFFFF == 0xABCD

0x0000 + 0xABCD == 0xABCD

Это кажется лишним шагом. То же самое касается следующего утверждения sum += (sum >> 16),

1

Решение

Функция контрольной суммы предназначена только для процессоров с прямым порядком байтов.

Первый цикл while оптимизирован по скорости.

&answer трюк загружает последний байт (если было нечетное количество байтов) в старший байт answerоставляя младший байт ноль, аналогично тому, что делает ваш код data[i] & 0xff00, Вот как это работает

1) take the address of answer      (&answer)
2) convert that to a byte pointer  (uint8_t *)
2a) on a big endian processor the first byte of a 16-bit quantity is the high byte
3) overwrite the high byte with the last byte of the data

Предполагается, что контрольная сумма вычисляется с добавлением переносов обратно. Здесь предполагается, что этот код выполняется на машине, где int 32-битный Следовательно, (sum & 0xffff) это 16-битная контрольная сумма, и (sum >> 16) биты переноса (если таковые имеются), которые должны быть добавлены обратно. Следовательно, строка

sum = (sum >> 16) + (sum & 0xffff);

корректирует сумму, чтобы включить переносы. Однако эта строка кода может сама генерировать еще один бит переноса. Итак, следующая строка sum += (sum >> 16) добавляет, что переносить (если таковые имеются) обратно в контрольную сумму.

Наконец, возьмите с собой дополнение к ответу. Обратите внимание, что htons не используется, поскольку вся функция неявно предполагает, что она работает на процессоре с прямым порядком байтов.

1

Другие решения

Что значит утверждение * (uint8_t *) (&ответ) = * (uint8_t *) w;
на самом деле делать?

Это бросает uint16_t в uint8_tтолько так 8 наиболее правильные биты копируются из w в answer, Рассматривать:

uint16_t x = 0x1234;
uint16_t* w = &x; // *w = // 0001001000110100

*(uint16_t *) (&answer) = *(uint16_t *) w; // answer = 0001001000110100

*(uint8_t *) (&answer) = *(uint8_t *) w;   // answer = 0000000000110100

Почему мы берем сумму как:

sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
answer = ~sum;

sum является 32 биты. 65536 ≡ 1 mod 65535, Итак Выражение переноса (sum & 0xffff) + (sum >> 16) уменьшает sum по модулю 65535, Это необходимо для добавления любого (возможного) результирующего переноса в итоговую сумму.

2

  1. *(uint8_t *) (&answer) = *(uint8_t *) w; На правой стороне, он преобразует w к uint8_t* и разыменовывает это. Усекает данные мусора, которые будут прочитаны при разыменовании uint16_t* указывая на последний байт. С левой стороны, он принимает адрес (указатель) answer и преобразует его в uint8_t* и разыменовывает это. Таким образом, он занимает первый байт, указанный w и присваивает значение первому байту ответа. По сути, эта строка делает 2. Add a one byte padding of 0s to the end of the last block if it's not 2 bytes long, to make it 2 bytes. Преобразования на левой стороне необходимы для поддержки систем с прямым порядком байтов … Я думаю.
1

  1. Это утверждение учитывает случай (см. RFC793 или RFC1701) где пакет имеет нечетное число байтов: [A,B] + [C,D] + ... + [Z,0] включив в сумму количество (answer) с 2 наиболее значимыми байтами как Z и 2 младших байта как 0. Помните + здесь всегда 1 дополнение дополнения.

  2. sum 32-разрядный аккумулятор Чтобы добавить дополнение 1, мы добавляем перенос после накопления битов. 2 наиболее значимых байта sum содержит биты переноса, если таковые имеются.

  3. Если вы посмотрите на RFC1701 вверху вы можете увидеть, какие RFC обновляют его. Нет никого, кто бы это заменил.

1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector