UNIX: разработка сетевых приложений
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(см. листинг 29.12) возвращает следующий пакет из устройства захвата пакетов. Поскольку заголовки канального уровня различаются в зависимости от фактического типа устройства, мы применяем ветвление в зависимости от значения, возвращаемого функциейnext_pcap.pcap_datalinkПРИМЕЧАНИЕСдвиги на 4, 14 и 24 байта объясняются на рис. 31.9 [128]. Сдвиг, равный 24 байтам, показанный для заголовков SLIP и PPP, применяется в BSD/OS 2.1.
Несмотря на то, что в названии DLT_EN10MB фигурирует обозначение «10МВ», этот тип канального уровня используется для сетей Ethernet, в которых скорость передачи данных равна 100 Мбит/с.
Наша функция
(см. листинг 29.13) исследует пакет и проверяет поля в заголовках IP и UDP.udp_checkВ листинге 29.12 показана функция
, возвращающая следующий пакет из устройства захвата пакетов.next_pcapЛистинг 29.12. Функция next_pcap: возвращает следующий пакет
//udpcksum/pcap.c38 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);45 *len = hdr.caplen; /* длина захваченного пакета */46 return (ptr);47 }Мы вызываем библиотечную функцию43-44, возвращающую следующий пакет. Указатель на пакет является возвращаемым значением данной функции, а второй аргумент указывает на структуруpcap_next, которая тоже возвращается заполненной:pcap_pkthdrstruct pcap_pkthdr {struct timeval ts; /* временная метка */bpf_u_int32 caplen; /* длина захваченного фрагмента */bpf_u_int32 len; /* полная длина пакета, находящегося в канале */};Временная отметка относится к тому моменту, когда пакет был считан устройством захвата пакетов, в противоположность моменту фактической передачи пакета процессу, которая может произойти чуть позже. Переменная
содержит длину захваченных данных (вспомним, что в листинге 29.2 нашей переменнойcaplenбыло присвоено значение 200 и она являлась вторым аргументом функцииshaplenв листинге 29.5). Назначение устройства захвата пакетов состоит в захвате заголовков, а не всего содержимого каждого пакета. Переменнаяpcap_open_live— это полная длина пакета, находящегося в канале. Значениеlenбудет всегда меньше или равно значениюcaplen.lenПерехваченная часть пакета возвращается через указатель (аргумент функции), и возвращаемым значением функции является указатель на пакет. Следует помнить, что указатель на пакет указывает фактически на заголовок канального уровня, который представляет собой 14-байтовый заголовок Ethernet в случае кадра Ethernet или 4-байтовый псевдоканальный (pseudo-link) заголовок в случае закольцовки на себя.45-46Если мы посмотрим на библиотечную реализацию функции
, мы увидим, что между различными функциями существует некоторое «разделение труда», схематически изображенное на рис. 29.5. Наше приложение вызывает функцииpcap_next, среди которых есть как зависящие, так и не зависящие от устройства захвата пакетов. Например, мы показываем, что реализация BPF вызывает функциюpcap_, в то время как реализация DLPI вызывает функциюread, а реализация Linux вызываетgetmsg.recvfromРис. 29.5. Организация вызовов функций для чтения из библиотеки захвата пакетов
Наша функция
проверяет различные поля в заголовках IP и UDP. Она показана в листинге 29.13. Эту проверку необходимо выполнить, так как при получении пакета от устройства захвата пакетов уровень IP не замечает этого пакета. Для символьного сокета это не так.udp_checkДлина пакета должна включать хотя бы заголовки IP и UDP. Версия IP проверяется вместе с длиной и контрольной суммой заголовка IP. Если поле протокола указывает на дейтаграмму UDP, функция возвращает указатель на объединенный заголовок IP/UDP. В противном случае программа завершается, так как фильтр захвата пакетов, заданный при вызове функции44-61в листинге 29.5, не должен возвращать пакеты никакого другого типа.pcap_setfilterЛистинг 29.13. Функция udp_check: проверка полей в заголовках IP и UDP
//udpcksum/udpread.c38 struct udpiphdr*39 udp_check(char *ptr, int len)40 {41 int hlen;42 struct ip *ip;43 struct udpiphdr *ui;44 if (len < sizeof(struct ip) + sizeof(struct udphdr))45 err_quit("len = %d", len);46 /* минимальная проверка заголовка IP */
