45 *len = hdr.caplen; /* длина захваченного пакета */
46 return (ptr);
47 }
43-44
Мы вызываем библиотечную функцию pcap_next
, возвращающую следующий пакет. Указатель на пакет является возвращаемым значением данной функции, а второй аргумент указывает на структуру pcap_pkthdr
, которая тоже возвращается заполненной:
struct pcap_pkthdr {
struct timeval ts; /* временная метка */
bpf_u_int32 caplen; /* длина захваченного фрагмента */
bpf_u_int32 len; /* полная длина пакета, находящегося в канале */
};
Временная отметка относится к тому моменту, когда пакет был считан устройством захвата пакетов, в противоположность моменту фактической передачи пакета процессу, которая может произойти чуть позже. Переменная caplen
содержит длину захваченных данных (вспомним, что в листинге 29.2 нашей переменной shaplen
было присвоено значение 200 и она являлась вторым аргументом функции pcap_open_live
в листинге 29.5). Назначение устройства захвата пакетов состоит в захвате заголовков, а не всего содержимого каждого пакета. Переменная len
— это полная длина пакета, находящегося в канале. Значение caplen
будет всегда меньше или равно значению len
.
45-46
Перехваченная часть пакета возвращается через указатель (аргумент функции), и возвращаемым значением функции является указатель на пакет. Следует помнить, что указатель на пакет указывает фактически на заголовок канального уровня, который представляет собой 14-байтовый заголовок Ethernet в случае кадра Ethernet или 4-байтовый псевдоканальный (pseudo-link) заголовок в случае закольцовки на себя.
Если мы посмотрим на библиотечную реализацию функции pcap_next
, мы увидим, что между различными функциями существует некоторое «разделение труда», схематически изображенное на рис. 29.5. Наше приложение вызывает функции pcap_
, среди которых есть как зависящие, так и не зависящие от устройства захвата пакетов. Например, мы показываем, что реализация BPF вызывает функцию read
, в то время как реализация DLPI вызывает функцию getmsg
, а реализация Linux вызывает recvfrom
.
Рис. 29.5. Организация вызовов функций для чтения из библиотеки захвата пакетов
Наша функция udp_check
проверяет различные поля в заголовках IP и UDP. Она показана в листинге 29.13. Эту проверку необходимо выполнить, так как при получении пакета от устройства захвата пакетов уровень IP не замечает этого пакета. Для символьного сокета это не так.
44-61
Длина пакета должна включать хотя бы заголовки IP и UDP. Версия IP проверяется вместе с длиной и контрольной суммой заголовка IP. Если поле протокола указывает на дейтаграмму UDP, функция возвращает указатель на объединенный заголовок IP/UDP. В противном случае программа завершается, так как фильтр захвата пакетов, заданный при вызове функции pcap_setfilter
в листинге 29.5, не должен возвращать пакеты никакого другого типа.
Листинг 29.13. Функция udp_check: проверка полей в заголовках IP и UDP
//udpcksum/udpread.c
38 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 */
47 ip = (struct ip*)ptr;
48 if (ip->ip_v != IPVERSION)
49 err_quit("ip_v = %d", ip->ip_v);
50 hlen = ip->ip_hl << 2;
51 if (hlen < sizeof(struct ip))
52 err_quit("ip_hl = %d", ip->ip_hl);
53 if (len < hlen + sizeof(struct udphdr))
54 err_quit("len = %d, hlen = %d", len, hlen);
55 if ((ip->ip_sum = in_cksum((u_short )ip, hlen)) != 0)
56 err_quit("ip checksum error");
57 if (ip->ip_p == IPPROTO_UDP) {
58 ui = (struct udpiphdr*)ip;
59 return (ui);
60 } else
61 err_quit("not a UDP packet");
62 }