RFC: 1071
Оригинал: Computing the Internet Checksum
Категория: Не определено
Дата публикации:
Авторы: , ,
Перевод: Николай Малых

Статус документа

В этом документе дается обзор эффективности методов и алгоритмов расчета контрольных сумм Internet. Документ не является стандартом, но описывает ряд полезных методов реализации. Документ может распространяться свободно.

1. Введение

В этом документе обсуждаются методы эффективного расчета контрольных сумм, используемых стандартными протоколами Internet — IP, UDP и TCP.

Эффективность расчета контрольных сумм очень важна с точки зрения производительности. В результате эффективной реализации остальных компонент протокольной обработки расчет контрольных сумм стал, например, одним из факторов, ограничивающих производительность TCP. Обычно для процедур расчета контрольных сумм используется ручная оптимизация с учетом особенностей работы конкретного процессора — экономия доли микросекунды в расчете на один байт данных TCP может привести к существенному снижению суммарного расхода процессорного времени.

В общих чертах алгоритм расчета контрольной суммы очень прост:

  1. Соседние октеты информации, для которой создается контрольная сумма, объединяются в 16-битовые целые числа и для этих чисел формируется 16-битовое поразрядное дополнение до 1.

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

  3. Для проверки контрольной суммы вычисляется сумма поразрядных дополнений до 1 для того же набора октетов, включая поле контрольной суммы. Если в результате получается значение, все биты которого равны 1 (-0 в арифметике дополнений до 1), это говорит о корректности контрольной суммы.

Предположим, что контрольная сумма определяется для последовательности октетов A, B, C, D, ... , Y, Z. Будем использовать обозначение [a,b] для 16-битовых целых чисел a*256+b, где a и b являются байтами. Тогда сумма 16-битовых дополнений до 1 для этих байтов будет задаваться одним из выражений:

[A,B] +' [C,D] +' ... +' [Y,Z]              [1]
[A,B] +' [C,D] +' ... +' [Z,0]              [2]

где +' указывает сложение поразрядных дополнений до 1. Приведенные выше выражения относятся к последовательностям с четным и нечетным количеством байтов, соответственно.

В двоичных машинах сумма поразрядных дополнений до единицы должна вычисляться с использованием «кругового переноса», т. е, при переполнении старшего бита значение переносится в младший, как показано в примерах ниже.

В параграфе 2 рассматриваются свойства контрольной суммы, которые могут использоваться для ускорения расчетов. Параграф 3 содержит несколько числовых примеров для наиболее важных методов реализации. В параграфе 4 приведены несколько конкретных алгоритмов для использования с распространенными типами процессоров. Авторы признательны Van Jacobson и Charley Kline за их вклад в алгоритмы, опубликованные в этом параграфе.

Свойства контрольных сумм Internet изначально были рассмотрены Биллом Пламмером (Bill Plummer) в документе IEN-45, названном «Checksum Function Design». Поскольку документ IEN-45 не получил широкого распространения, он включен в качестве приложения к данному RFC.

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

Эта простая контрольная сумма имеет множество чудесных математических свойств, которые могут быть использованы для вычисления расчетов. Эти свойства обсуждаются ниже.

  • A. Кумулятивность и ассоциативность
  • После того, как байты были распределены на четные и нечетные, суммирование может проводиться в любом порядке с возможностью разбиения на произвольные группы.

    Например, сумму [1] можно представить в форме:

    ( [A,B] +' [C,D] +' ... +' [J,0] )
           +' ( [0,K] +' ... +' [Y,Z] )               [3]
  • B. Независимость от порядка байтов
  • Сумма 16-битовых целых чисел может вычисляться для любого порядка байтов. Т. е., если мы рассчитаем сумму, сменив порядок байтов:

    [B,A] +' [D,C] +' ... +' [Z,Y]                   [4]

    результат будет отличаться от значения [1] только сменой порядка байтов! Для того, чтобы это стало более понятным, отметим, что в обоих случаях перенос происходит из бита 15 в бит 0 и из бита 7 в бит 8. Иными словами, смена порядка суммируемых байтов лишь приводит к смене порядка байтов результата, но сохраняет порядок битов в каждом байте результата.

    Следовательно, сумма может рассчитываться одинаково, независимо от используемого оборудованием порядка байтов («big-endian» или «little-endian»). В качестве примера предположим, что машина «little-endian» вычисляет контрольную сумму данных, хранящихся в памяти с использованием сетевого («big-endian») порядка байтов. Выборка каждого 16-битового слова будет приводить к смене мест байт в словах, что приведет к суммирования [4]; однако при сохранении результата в памяти снова произойдет смена мест байтов и будет восстановлен сетевой порядок.

    Смена мест байтов может явно использоваться для решения проблем, связанных с выравниванием по границе. Например, вторая группа в [3] может быть рассчитана, как:

    [K,L] +' ... +' [Z,0]

    если байты результата поменять местами до того, как они будут добавлены к сумме первой группы (см. пример ниже).

  • C. Параллельное суммирование
  • На машинах с размером слова, кратным 16 битам, можно использовать дополнительное увеличение скорости расчетов. Поскольку сложение ассоциативно, мы не обязаны складывать целые числа в порядке их следования в сообщении. Вместо этого мы можем складывать их «параллельно» используя более длинные машинные слова.

    Для параллельного расчета контрольной суммы просто выполняется операция поразрядного дополнения до 1 для стандартного размера машинного слова. Например, на 32-разрядных машинах мы можем складывать одновременно по 4 байта : [A,B,C,D]+'... После завершения расчета результат более длинное слово «вталкивается» в 16 битов путем сложения 16-битовых сегментов. При каждом сложении могут происходить переносы битов, которые следует учитывать.

    Поскольку порядок байтов не имеет значения, мы можем посчитать 32-битовых слов [D,C,B,A]+'... или [B,A,D,C]+'... и потом поменять местами байты окончательной 16-битовой суммы (см. примеры ниже). Допускаются любые перестановки, которые будут обеспечивать сложение всех четных байтов в один байт суммы, а всех нечетных — в другой байт.

Ниже описано еще несколько методов ускорения расчета контрольных сумм:

  • Отложенные переносы
  • Для некоторых типов процессоров эффективность расчета контрольных сумм может быть повышена за счет того, что добавление битов переноса осуществляется после завершения цикла суммирования.

    Можно складывать 16-битовые слова в 32-битовую переменную, чтобы все биты переноса складывались в старших 16 битах. Этот вариант позволяет отказаться от использования команд, понимающих перенос битов, но требует удвоенного числа операций сложения, поскольку складываются 32-битовые сегменты. Который из вариантов будет быстрее, зависит от системной архитектуры.

  • Использование циклов
  • Для повышения эффективности часто бывает полезно создание внутреннего цикла суммирования, выполняющего серию команд сложения за один проход. Этот метод часто дает существенное повышение эффективности, хотя может значительно усложнить логику программы.
  • Объединение суммирования и копирования данных
  • Кроме вычисления суммы время расходуется также на копирование данных из одного места памяти в другое. В обоих случаях узким местом является скорость шины памяти (например, скорость выборки данных из памяти). На некоторых машинах (особенно на сравнительно медленных и простых микрокомпьютерах) производительность можно существенно повысить за счет объединения операций суммирования и копирования при котором происходит только одна выборка из памяти для обеих операций.
  • Нарастающие обновления
  • В некоторых случаях можно избежать повторного вычисления всей контрольной суммы, если сменился только заголовок. Наиболее ярким примером может служить изменение поля TTL в заголовке IP при пересылке пакетов шлюзом, но есть и другие примеры (скажем, обновление source route). В таких случаях можно обновить контрольную сумму даже без просмотра сообщения или дейтаграммы.

    Для обновления контрольной суммы достаточно просто добавить к ней разность между новым и прежним значениями изменившегося 16-битового слова. Чтобы понять, как это работает, напомним, что каждое целое число имеет аддитивную инверсию, а операция сложения ассоциативна. Если исходное значение слова обозначить m, новое значение — m', а исходную контрольную сумму — C, тогда новая сумма C' будет равна:

    C' = C + (-m) + m' = C + (m' - m)

3. Численные примеры

Ниже рассматриваются явные примеры расчета сумм дополнений до 1 на машине с дополнением до 2. Примеры показывают, как одну и ту же сумму можно рассчитать путем сложения байтов, 16-битовых слов с нормальным и измененным порядком байтов, а также 32-битовых слов с 3 вариантами порядка байтов. Все числа задаются в шестнадцатеричном формате.

          Byte-by-byte    "Normal"  Swapped
                            Order    Order
Byte 0/1:    00   01        0001      0100
Byte 2/3:    f2   03        f203      03f2
Byte 4/5:    f4   f5        f4f5      f5f4
Byte 6/7:    f6   f7        f6f7      f7f6
            ---  ---       -----     -----
Sum1:       2dc  1f0       2ddf0     1f2dc
             dc   f0        ddf0      f2dc
Carrys:       1    2           2         1
             --   --        ----      ----
Sum2:        dd   f2        ddf2      f2dd
Final Swap:  dd   f2        ddf2      ddf2
Byte 0/1/2/3:  0001f203     010003f2       03f20100
Byte 4/5/6/7:  f4f5f6f7     f5f4f7f6       f7f6f5f4
               --------     --------       --------
Sum1:         0f4f7e8fa    0f6f4fbe8      0fbe8f6f4
Carries:              0            0              0
Top half:          f4f7         f6f4           fbe8
Bottom half:       e8fa         fbe8           f6f4
                  -----        -----          -----
Sum2:             1ddf1        1f2dc          1f2dc
                   ddf1         f2dc           f2dc
Carrys:               1            1              1
                   ----         ----           ----
Sum3:              ddf2         f2dd           f2dd
Final Swap:        ddf2         ddf2           ddf2

В заключение приводится пример суммирования в виде двух групп, из которых вторая начинается на нечетной границе.

           Byte-by-byte    Normal
                            Order
Byte 0/1:    00   01        0001
Byte 2/ :    f2  (00)       f200
            ---  ---       -----
Sum1:        f2   01        f201
Byte 4/5:    03   f4        03f4
Byte 6/7:    f5   f6        f5f6
Byte 8/:     f7  (00)       f700
            ---  ---       -----
Sum2:                      1f0ea
Sum2:                       f0ea
Carry:                         1
                           -----
Sum3:                       f0eb
Sum1:                       f201
Sum3 byte swapped:          ebf0
                           -----
Sum4:                      1ddf1
Sum4:                       ddf1
Carry:                         1
                           -----
Sum5:                       ddf2

4. Примеры реализации

Ниже приводятся примеры реализации алгоритма подсчета контрольных сумм Internet, которые доказали свою эффективность для разных типов CPU. В каждом случае приводится ядро алгоритма без включения дополнительного кода (например, свящывания подпрограмм).

4.1. Код на языке C

Приведенный ниже пример на языке C показывает расчет контрольной суммы с использованием внутреннего цикла сложения 16-битовых значений в 32-битовый «аккумулятор».

in 6
    {
        /* Расчет контрольной суммы Internet для count байтов,
         * начиная с addr.
         */
     register long sum = 0;
     while( count > 1 )  {
        /*  Внутренний цикл */
            sum += * (unsigned short) addr++;
            count -= 2;
    }
        /*  Прибавляем байт переноса, если он есть */
    if( count > 0 )
            sum += * (unsigned char *) addr;
        /*  поместим 32-битовую сумму в 16 битов */
    while (sum>>16)
        sum = (sum & 0xffff) + (sum >> 16);
    checksum = ~sum;
}

4.2. Motorola 68020

Ниже приведен пример ассемблерной реализации алгоритма для процессора Motorola 68020. Этот вариант использует суммирование 32-битовых значений в один прием и использует внутренний цикл сложения с 16 операциями. Для простоты была опущена логика дополнения последнего слова для случаев, когда число суммируемых байтов не кратно 4. Результат сохраняется в регистре d0.

При тактовой частоте процессора 20 МГц время расчета контрольной суммы составляет 134 мксек/кбайт. Разработал этот алгоритм Van Jacobson.

    movl    d1,d2
    lsrl    #6,d1       | count/64 = # число проходов цикла
    andl    #0x3c,d2    | Нахождение частей блока
    negl    d2
    andb    #0xf,cc     | Сброс X (расширенный флаг переноса)
    jmp     [email protected]
(2$-.-2:b,d2)  | Переход в цикл
1$:     | Начало внутреннего цикла...
    movl    [email protected]
+,d2     |  Выборка 32-битового слова
    addxl   d2,d0       |    Сложение слова и предыдущего переноса
    movl    [email protected]
+,d2     |  Выборка 32-битового слова
    addxl   d2,d0       |    Сложение слова и предыдущего переноса
        | ... еще 14 повторов
2$:
    dbra    d1,1$   | (Отметим, что dbra не воздействует на X)
    movl    d0,d1   | Вталкивание 32 битов суммы в 16 битов
    swap    d1      | (Отметим, что swap не воздействует на X)
    addxw   d1,d0
    jcc     3$
    addw    #1,d0
3$:
    andl    #0xffff,d0

4.3. Cray

Ниже приводится ассемблерная реализация алгоритма для процессора Cray, которую предложил Charley Kline. Расчет контрольной суммы производится как векторная операция, обеспечивающая одновременное сложение до 512 байтов с базовым блоком суммирования 32 бита. Для простоты из примера исключены фрагменты, обеспечивающие возможность работы с короткими блоками.

Регистр A1 содержит адрес 512-байтового блока памяти для контрольной суммы. Две первых копии данных загружаются в два векторных регистра. Один вектор сдвигается вправо на 32 бита, а для второго используется операция AND с 32-битовой маской. После этого векторы складываются. Поскольку все эти операции связаны в цепочку, они дают один результат на каждый цикл процессора. Далее производится сжатие (collaps) результирующего вектора в цикле, который прибавляет каждый элемент к скалярному регистру. В заключение выполняется перенос и результат помещается в 16 битов.

         EBM
         A0      A1
         VL      64            используются полные векторы
         S1      <32           формируется 32-битовая маска из правой части.
         A2      32
         V1      ,A0,1            загрузка пакета в V1
         V2      S1&V1            формирование "правых" 32 битов в V2.
         V3      V1>A2            формирование "левых" 32 битов в V3.
         V1      V2+V3            Сложение.
         A2      63            Подготовка к сжатию в скаляр.
         S1      0
         S4      <16           Form 16-bit mask from the right.
         A4      16
   CK$LOOP S2    V1,A2
         A2      A2-1
         A0      A2
         S1      S1+S2
         JAN     CK$LOOP
         S2      S1&S4           формирование " правых" 16 битов в S2
         S1      S1>A4           формирование " левых" 16 битов в S1
         S1      S1+S2
         S2      S1&S4           формирование " правых" 16 битов в S2
         S1      S1>A4           формирование " левых" 16 битов в S1
         S1      S1+S2
         S1      #S1            Получение дополнения до 1
         CMR            В этой точке S1 будет содержать контрольную сумму.

4.4. IBM 370

Следующий пример на ассемблере для процессора IBM 370 суммирует по 4 байта одновременно. Для простоты опущен код дополнения, используемых для выравнивания данных по 4-байтовой границе и обращения порядка байтов, когда это требуется. Результат сохраняется в регистре RCARRY.

Этот код на процессоре IBM 3090 давал время расчета 27 мксек/кбайт при расчете контрольной суммы байтов, содержащих только единицы. Время расчета снижается до 24.3 мксек/кбайт, если применить средства выравнивания слов (специальная обработка в начале и в конце, а при необходимости замена местами байтов при расчете с нечетной позиции).

   *      Регистры RADDR и RCOUNT содержат адрес и размер суммируемого блока.
   *
   *      (RCARRY, RSUM) должны быть парой регистров (четный/нечетный).
   *      (RCOUNT, RMOD) должны быть парой регистров (четный/нечетный).
   *
   CHECKSUM  SR    RSUM,RSUM       Сброс рабочих регистров.
             SR    RCARRY,RCARRY
             LA    RONE,1          Установка значения 1.
   *
             SRDA  RCOUNT,6        Count/64 в RCOUNT.
             AR    RCOUNT,RONE       +1 = # число циклов.
             SRL   RMOD,26         Размер частичного блока в RMOD.
             AR    RADDR,R3        Выравнивание для компенсации перехода
             S     RADDR,=F(64)      в цикл.
             SRL   RMOD,1          (RMOD/4)*2 - индекс "полуслов".
             LH    RMOD,DOPEVEC9(RMOD) используется специальный вектор для
             B     LOOP(RMOD)          смещения и перехода в цикл...
   *
   *             Внутренний цикл:
   *
   LOOP      AL    RSUM,0(,RADDR)   Сложить логические слова
             BC    12,*+6             Переход, если нет переноса
             AR    RCARRY,RONE        Добавит ь 1 переноса
             AL    RSUM,4(,RADDR)   Сложить логические слова
             BC    12,*+6             Branch i f no carry
             AR    RCARRY,RONE        Добавить 1 переноса
   *
   *                    ... еще 14 повторов ...
   *
             A     RADDR,=F'64'    Увеличить адресный указатель
             BCT   RCOUNT,LOOP     Перейти к Count
    *
    *            Прибавить переносы к сумме и "затолкнуть" в 16 битов
    *
             ALR   RCARRY,RSUM      Сложить слова SUM и CARRY
             BC    12,*+6              и учесть возможный перенос
             AR    RCARRY,RONE
             SRDL  RCARRY,16        Поместить 32-битовую сумму
             SRL   RSUM,16            в 16 битов
             ALR   RCARRY,RSUM
             C     RCARRY,=X'0000FFFF' Прибавить оставшийся перенос
             BNH   DONE
             S     RCARRY,=X'0000FFFF'
   DONE      X     RCARRY,=X'0000FFFF' Дополнить до 1

Литература

[1] Cerf, V.G. и Kahn, Robert E., «A Protocol for Packet Network Communications», IEEE Transactions on Communications, vol. COM-22, No. 5, Май 1974.
[2] Kahn, Robert E., «The Organization of Computer Resources into a Packet Radio Network», IEEE Transactions on Communications, vol. COM-25, no. 1, pp. 169-178, Январь 1977.
[3] Jacobs, Irwin, et al., «CPODA — A Demand Assignment Protocol for SatNet», Fifth Data Communications Symposium, September 27-9, 1977, Snowbird, Utah
[4] Bolt Beranek and Newman, Inc. «Specifications for the Interconnection of a Host and an IMP», Report 1822, Январь 1976 edition.
[5] Dean, Richard A., «Elements of Abstract Algebra», John Wyley and Sons, Inc., 1966
[6] Peterson, W. Wesley, «Error Correcting Codes», M.I.T. Press Cambridge MA, 4th edition, 1968.
[7] Avizienis, Algirdas, «A Study of the Effectiveness of Fault-Detecting Codes for Binary Arithmetic», Jet Propulsion Laboratory Technical Report No. 32-711, September 1, 1965.
[8] Kirstein, Peter, private communication
[9] Cerf, V. G. и Postel, Jonathan B., «Specification of Internetwork Transmission Control Program Version 3», University of Southern California Information Sciences Institute, Январь 1978.
[10] Digital Equipment Corporation, «PDP-10 Reference Handbook», 1970, pp. 114-5.
[11] Swanson, Robert, «Understanding Cyclic Redundancy Codes», Computer Design, November, 1975, pp. 93-99.
[12] Clements, Robert C., private communication.
[13] Conklin, Peter F., and Rodgers, David P., «Advanced Minicomputer Designed by Team Evaluation of Hardware/Software Tradeoffs», Computer Design, Апрель 1978, pp. 136-7.
2007 - 2022 © Русские переводы RFC, IETF, ISOC.