Следует отметить, что в Solaris 2.x, где x<6, в случаях, когда дейтаграммы UDP или сегменты TCP отправляются с символьного сокета при установленном параметре IP_HDRINCL, возникает ошибка. Контрольную сумму вычисляет ядро, а мы должны установить поле ui_sum равным длине дейтаграммы UDP.
36-49
Поскольку мы установили параметр сокета IP_HDRINCL
, нам следует заполнить большую часть полей в заголовке IP. (В разделе 28.3 обсуждается запись в символьный сокет при включенном параметре IP_HDRINCL
.) Мы присваиваем полю идентификации нуль (ip_id
), что указывает IP на необходимость задания значения этого поля. IP также вычисляет контрольную сумму IP, а функция sendto
записывает дейтаграмму IP.
Обратите внимание, что поле ip_len может иметь либо сетевой порядок байтов, либо порядок байтов узла. Это типичная проблема с совместимостью, возникающая при использовании символьных сокетов.
Следующая функция — это udp_read
, показанная в листинге 29.11. Она вызывается из кода, представленного в листинге 29.6.
Листинг 29.11. Функция udp_read: чтение очередного пакета из устройства захвата пакетов
//udpcksum/udpread.c
7 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;
20 if (ntohs(eptr->ether_type) != ETHERTYPE_IP)
21 err_quit("Ethernet type not IP", ntohs(eptr->ether_type));
22 return (udp_check(ptr + 14, len — 14));
23 case DLT_SLIP: /* заголовок SLIP = 24 байта */
24 return (udp_check(ptr + 24, len — 24));
25 case DLT_PPP: /* заголовок PPP = 24 байта */
26 return (udp_check(ptr + 24, len — 24));
27 default:
28 err_quit("unsupported datalink (%d)", datalink);
29 }
30 }
31 }
14-29
Наша функция next_pcap
(см. листинг 29.12) возвращает следующий пакет из устройства захвата пакетов. Поскольку заголовки канального уровня различаются в зависимости от фактического типа устройства, мы применяем ветвление в зависимости от значения, возвращаемого функцией pcap_datalink
.
Сдвиги на 4, 14 и 24 байта объясняются на рис. 31.9 [128]. Сдвиг, равный 24 байтам, показанный для заголовков SLIP и PPP, применяется в BSD/OS 2.1.
Несмотря на то, что в названии DLT_EN10MB фигурирует обозначение «10МВ», этот тип канального уровня используется для сетей Ethernet, в которых скорость передачи данных равна 100 Мбит/с.
Наша функция udp_check
(см. листинг 29.13) исследует пакет и проверяет поля в заголовках IP и UDP.
В листинге 29.12 показана функция next_pcap
, возвращающая следующий пакет из устройства захвата пакетов.
Листинг 29.12. Функция next_pcap: возвращает следующий пакет
//udpcksum/pcap.c
38 char*
39 next_pcap(int *len)
40 {
41 char *ptr;
42 struct pcap_pkthdr hdr;
43 /* продолжаем следить, пока пакет не будет готов */
44 while ((ptr = (char*)pcap_next(pd, &hdr)) == NULL);