Re[4]: Протокол на основе UDP
От: fk0 Россия https://fk0.name
Дата: 28.02.10 13:38
Оценка:
Здравствуйте, netch80, Вы писали:

N>Здравствуйте, fk0, Вы писали:


T>>>CRC даёт хорошую защиту от сбоев оборудования. От преднамеренного изменения пакетов "человеком-посередине" не защищает.

fk0>> Жизненный опыт. CRC даёт хреновейшую защиту от "сбоев оборудования" при обрыве, когда с середины пакета и до конца все байты FFFF или 0000 не важно.
N>Прошу примера. Мне сложно себе представить, чтобы правильно вычисленная и уложенная CRC не замечатала такие ситуации.

Пример ниже. Прекрасно видно, что на случайных данных, на совершенно разных последовательностях (случайных), даже на коротких (несколко байт) пакетах можно нарваться на ситуацию, когда CRC совпадает, а хвост пакета (а то и весь пакет!) испорчен нулём или FF. Позапускай программу несколько раз (./a.out | less). Что характерно -- CRC должен быть равен заполняющему (всё портящему) байту. Так вот в реальном приборе, где проблемы были, там, догадайся...

Там CRC шёл в конце пакета

... и как следствие, портился "нужным" значением тоже.

Я для себя вывод сделал на всю жизнь. Ну кроме того, что долой самодельные протоколы... в данном случае протокол не самодельный. Нефиг CRC в конец закладывать или нужно иметь в концец чёткий маркер конца пакета. Да, длина там первым байтом в пакете ещё. CRC пакета с нулевой длиной -- правильно, ноль. Протокол, повторюсь, не самодельный, это проблема всего SMBUS (переименованный интелом I2C). И когда есть "обрывы" с заполнением конца FF-ами (характерная особенность I2C) и когда пакетов тысячи в секунду -- это проблема.

Да, конечно, 8-битный CRC надёжно защищает только от 8-и ошибочных битов подряд... 16-битный, соответственно, от 16-и. А там посылки короткие, по десятку байт, в протоколе 32 байта максимум. И что характерно, в такой ситуации 16-битовая сумма позволяет посылки короче 256 байт без ошибок...

#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

/* compute CRC-8 for SMBUS (x^8 + x^2 + x + 1) */
uint_fast8_t crc_byte(uint_fast8_t byte, uint_fast8_t crc)
{
uint_fast8_t b;
        b=byte^crc;
        if (b&(1<<0)) crc^=0x7;
        if (b&(1<<1)) crc^=0xe;
        if (b&(1<<2)) crc^=0x1c;
        if (b&(1<<3)) crc^=0x38;
        if (b&(1<<4)) crc^=0x70;
        if (b&(1<<5)) crc^=0xe0;
        if (b&(1<<6)) crc^=0xc7;
        if (b&(1<<7)) crc^=0x89;
        return crc;
}

/* dump packet data in hex form */
void dump(const char *data, size_t size)
{
size_t n;
    for (n=0; n<size; n++) printf("%2.2X", (unsigned char)data[n]);
    fputc('\n', stdout);
}


int main (int ac, char *av[])
{
unsigned plen;      /* packet length */
char packet[256];    /* data */
char crc;        /* CRC of data with size plen */

unsigned n, fill, start;
char copy[256];
char ncrc;

    srand(time(NULL));

    /* run test for each packet length from 1 to 255 bytes inclusive */
    for (plen=1; plen<256; plen++) {

        /* print progress */
        fprintf(stderr, "\rlen=%2.2X", plen);
    
        /* fill packet with random bytes and compute CRC */
        crc=0;
        for (n=0; n<plen; n++) {
            packet[n]=rand()%0x100;
            crc=crc_byte(packet[n], crc);
        }

        /* fill packet tail (from 0 to plen inclusive) with 0/0xFF */
        for (fill=0; fill<0x100; fill+=0xff) {

            /* starting position */
            for (start=0; start<plen; start++)  {

                /* make copy and fill tail */
                memcpy(copy, packet, start);
                memset(&copy[start], fill, plen-start);

                /* compute CRC of damaged packet */
                ncrc=0;
                for (n=0; n<plen; n++)
                    ncrc=crc_byte(copy[n], crc);

                /* check if crc doesn't protect data integrity */
                if (crc != ncrc) continue;
                if (memcmp(copy, packet, plen)==0) continue;

                /* crc matches, but data is different */
                printf("length=%3u, tail=%3u, fill=%2.2X, crc=%2.2X\n",
                    plen, plen-start, fill, (unsigned char)crc);
                dump(packet, plen);
                dump(copy, plen);
                puts("--");

            }
        }
        
    }

    fputc('\n', stderr);
    return 0;
}



fk0>> А контрольная сумма замечательно работает. Кто-то всерьёз полагает, что бред повсеместно приведённый в некоторой литературе, что дескать CRC не используется в TCP/IP ровно потому, что его трудно считать? Нет, он тривиально считается в аппаратуре, побитно...

N>Вообще-то TCP/IP начинался в условиях отсутствия такой аппаратуры. Хотя причину не держать таблицу в 512 байт я слабо понимаю. Может влиять ещё скорость вычисления.

Вообще-то CRC считается и бестабличными методами достаточно быстро, если такая задача стоит. Опять же смотри пример. Все возражения "почему не 16-32 бита" -- потому что блоки не по 4 килобайта, а по десятку байт. А 16 бит принципиально ситуацию тут не изменит вообще.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.