Подтвердить что ты не робот

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

Структура заголовка UDP, определенная в /usr/include/netinet/udp.h, выглядит следующим образом

struct udphdr
{
  u_int16_t source;
  u_int16_t dest;
  u_int16_t len;
  u_int16_t check;
};

Какое значение хранится в поле проверки заголовка? Как проверить правильность контрольной суммы? Я имел в виду, на каких данных рассчитывается контрольная сумма? (Это только заголовок udp или заголовок udp плюс полезная нагрузка, которая следует за ним?)

Спасибо.

4b9b3361

Ответ 1

Контрольная сумма UDP выполняется для всей полезной нагрузки, а также для других полей в заголовке и некоторых полей из заголовка IP. Псевдозаголовок составляется из IP-заголовка, чтобы выполнить вычисление (которое выполняется над этим псевдозаголовком, UDP-заголовком и полезной нагрузкой). Причина, по которой включается псевдо-заголовок, состоит в том, чтобы перехватывать пакеты, которые были направлены на неправильный IP-адрес.

По сути, на принимающей стороне все 16-битные слова заголовков плюс область данных складываются вместе (перенос в 16 бит), и результат проверяется по 0xffff.

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

Одна сумма дополнения - это не просто сумма всех значений одного дополнения. Это немного сложнее.

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


В качестве отступления, и это чистая гипотеза с моей стороны, но, вероятно, это можно было бы эффективно сделать, используя инструкцию ADC (добавить с переносом), а не ADD (как ни удивительно, добавить), или любые другие эквивалентные инструкции, которые были доступны на вашем процессоре в то время.

Если бы не было переноса, ADC просто добавил бы нулевой бит из переноса. В те дни, когда это было сделано (и да, к сожалению, я такой старый), память была гораздо большим ограничением, чем скоростью, не так много в наши дни, поэтому сохранение нескольких байтов в вашем коде могло бы поднять вас до уровень полубога-императора вселенной :-)


Обратите внимание, что вам никогда не приходилось беспокоиться о переносе во второй раз (или переносе двух вместе со следующим ADC, если вы используете этот метод, упомянутый в предыдущем абзаце), так как два самых больших 16-битных значения, при суммировании, производить (усечено из 0x1fffe) 0xfffe - добавление одного к этому никогда не вызовет другой перенос.

Как только вычисленная сумма одного дополнения вычислена, ее биты инвертированы и вставлены в пакет, это приведет к тому, что вычисление на принимающей стороне произведет 0xffff, при условии отсутствия ошибок при передаче, конечно.

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

RFC768 - это спецификация, в которой это подробно описано.

Ответ 2

Хороший и понятный пример расчета контрольной суммы UDP выполняется Гердом Хоффманом.

Вы можете google для "net-checksum.c Герд Хоффманн" или посмотрите здесь файл:

https://gist.github.com/fxlv/81209bbd150abfeaceb1f85ff076c9f3

Вы можете использовать функцию net_checksum_tcpudp, передать ей длину полезной нагрузки UDP, proto, src и dst IP, а затем самую полезную нагрузку UDP, и она пойдет правильно.

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

Ответ 3

Я искал в сети какой-то код, который вычислит заголовок udp (с псевдо-заголовком ip, как упомянуто выше).

Наконец я нашел пакет open-bsd dhclient.c:

https://github.com/openbsd/src/blob/master/sbin/dhclient/packet.c

проверить функцию assemble_udp_ip_header()