UNIX: разработка сетевых приложений
33 ui->ui_src.s_addr = ((struct sockaddr_in*)local)->sin_addr.s_addr;34 ui->ui_dst.s_addr = ((struct sockaddr_in*)dest)->sin_addr.s_addr;35 ui->ui_sport = ((struct sockaddr_in*)local)->sin_port;36 ui->ui_dport = ((struct sockaddr_in*)dest)->sin_port;37 ui->ui_ulen = ui->ui_len;38 if (zerosum == 0) {39 #if 1 /* заменить на if 0 для Solaris 2.x. x < 6 */40 if ((ui->ui_sum = m_cksum((u_int16_t*)in, userlen)) == 0)41 ui->ui_sum = 0xffff;42 #else43 ui->ui_sum = ui->ui_len;44 #endif45 }46 /* заполнение оставшейся части IP-заголовка */47 /* функция p_output() вычисляет и сохраняет контрольную сумму IP */48 ip->ip_v = IPVERSION;49 ip->ip_hl = sizeof(struct ip) >> 2;50 ip->ip_tos = 0;51 #if defined(linux) || defined(__OpenBSD__)52 ip->ip_len = htons(userlen); /* сетевой порядок байтов */53 #else54 ip->ip_len = userlen; /* порядок байтов узла */55 #endif56 ip->ip_id = 0; /* это пусть устанавливает уровень IP */57 ip->ip_off = 0; /* смещение флагов, флаги MF и DF */58 ip->ip_ttl = TTL_OUT;59 Sendto(rawfd, buf, userlen, 0, dest, destlen);60 }Инициализация указателей на заголовки пакетовУказатель24-26указывает на начало заголовка IP (структуруip), а указательipуказывает на то же место, но структураuiявляется объединением заголовков IP и UDP.udpiphdrОбнуление заголовкаМы явным образом записываем в заголовок нули, чтобы предотвратить учет случайного мусора, который мог остаться в буфере, при вычислении контрольной суммы.27Обновление значений длиныПеременная28-31— это длина дейтаграммы UDP: количество байтов пользовательских данных плюс размер заголовка UDP (8 байт). Переменнаяui_len(количество байтов пользовательских данных, которые следуют за заголовком UDP) увеличивается на 28 (20 байт на заголовок IP и 8 байт на заголовок UDP), для того чтобы соответствовать настоящему размеру дейтаграммы IP.userlenЗаполнение заголовка UDP и вычисление контрольной суммы UDPПри вычислении контрольной суммы UDP учитывается не только заголовок и данные UDP, но и поля заголовка IP. Эти дополнительные поля заголовка IP образуют то, что называется псевдозаголовком (pseudoheader). Включение псевдозаголовка обеспечивает дополнительную проверку на то, что если значение контрольной суммы верно, то дейтаграмма была доставлена на правильный узел и с правильным кодом протокола. В указанных строках располагаются операторы инициализации полей в IP-заголовке, формирующих псевдозаголовок. Данный фрагмент кода несколько запутан, но его объяснение приводится в разделе 23.6 [128]. Конечным результатом является запись контрольной суммы UDP в поле32-45, если не установлен флагui_sum(что соответствует наличию аргумента командной строки -0).zerosumЕсли при вычислении контрольной суммы получается 0, вместо него записывается значение
. В обратном коде эти числа совпадают, но протокол UDP устанавливает контрольную сумму в нуль, чтобы обозначить, что она вовсе не была вычислена. Обратите внимание, что в листинге 28.10 мы не проверяем, равно ли значение контрольной суммы нулю: дело в том, что в случае ICMPv4 нулевое значение контрольной суммы не означает ее отсутствия.0xffffПРИМЕЧАНИЕСледует отметить, что в Solaris 2.x, где x<6, в случаях, когда дейтаграммы UDP или сегменты TCP отправляются с символьного сокета при установленном параметре IP_HDRINCL, возникает ошибка. Контрольную сумму вычисляет ядро, а мы должны установить поле ui_sum равным длине дейтаграммы UDP.
Заполнение заголовка IPПоскольку мы установили параметр сокета36-49, нам следует заполнить большую часть полей в заголовке IP. (В разделе 28.3 обсуждается запись в символьный сокет при включенном параметреIP_HDRINCL.) Мы присваиваем полю идентификации нуль (IP_HDRINCL), что указывает IP на необходимость задания значения этого поля. IP также вычисляет контрольную сумму IP, а функцияip_idзаписывает дейтаграмму IP.sendtoПРИМЕЧАНИЕОбратите внимание, что поле ip_len может иметь либо сетевой порядок байтов, либо порядок байтов узла. Это типичная проблема с совместимостью, возникающая при использовании символьных сокетов.
Следующая функция — это
, показанная в листинге 29.11. Она вызывается из кода, представленного в листинге 29.6.udp_readЛистинг 29.11. Функция udp_read: чтение очередного пакета из устройства захвата пакетов
//udpcksum/udpread.c7 struct udpiphdr*8 udp_read(void)9 {10 int len;11 char *ptr;12 struct ether_header *eptr;13 for (;;) {14 ptr = next_pcap(&len);15 switch (datalink) {16 case DLT_NULL: /* заголовок обратной петли = 4 байта */17 return (udp_check(ptr + 4, len — 4));18 case DLT_EN10MB:19 eptr = (struct ether_header*)ptr;