UNIX: разработка сетевых приложений
Часть 312 из 399 Информация о книге
Подготовка msghdr для recvmsgМы записываем значения в неизменяемые поля структур19-24иmsghdr, которые будут передаваться функцииiovec.recvmsgБесконечный цикл для считывания всех ICMP-сообщенийОсновной цикл программы является бесконечным циклом, считывающим все пакеты, возвращаемые на символьный сокет ICMP. Вызывается функция25-37для регистрации времени получения пакета, а затем вызывается соответствующая функция протокола (gettimeofdayилиproc_v4) для обработки ICMP-сообщения.proc_v6В листинге 28.5 приведена функция
, вычисляющая разность двух структурtv_subи сохраняющая результат в первой из них.timevalЛистинг 28.5. Функция tv_sub: вычитание двух структур timeval
//lib.tv_sub.c1 #include "unp.h"2 void3 tv_sub(struct timeval *out, struct timeval *in)4 {5 if ((out->tv_usec -= in->tv_usec) < 0) { /* out -= in */6 --out->tv_sec;7 out->tv_usec += 1000000;8 }9 out->tv_sec -= in->tv_sec;10 }В листинге 28.6 приведена функция
, обрабатывающая все принимаемые сообщения ICMPv4. Можно также обратиться к рис. А.1, на котором изображен формат заголовка IPv4. Кроме того, следует осознавать, что к тому моменту, когда процесс получает на символьном сокете ICMP-сообщение, ядро уже проверило, что основные поля в заголовке IPv4 и в сообщении ICMPv4 действительны [128, с. 214, с. 311].proc_v4Листинг 28.6. Функция proc_v4: обработка сообщений ICMPv4
//ping/prov_v4.c1 #include "ping.h"2 void3 proc_v4(char *ptr, ssize_t len, struct msghdr *msg, struct timeval *tvrecv)4 {5 int hlen1, icmplen;6 double rtt;7 struct ip *ip;8 struct icmp *icmp;9 struct timeval *tvsend;10 ip = (struct ip*)ptr; /* начало IP-заголовка */11 hlen1 = ip->ip_hl << 2; /* длина IP-заголовка */12 if (ip->ip_p != IPPROTO_ICMP)13 return; /* не ICMP */14 icmp = (struct icmp*)(ptr + hlen1); /* начало ICMP-заголовка */15 if ((icmplen = len - hlen1) < 8)16 return; /* плохой пакет */17 if (icmp->icmp_type == ICMP_ECHOREPLY) {18 if (icmp->icmp_id != pid)19 return; /* это не ответ на наш ECHO_REQUEST */20 if (icmplen < 16)21 return; /* недостаточно данных */22 tvsend = (struct timeval*)icmp->icmp_data;23 tv_sub(tvrecv, tvsend);24 rtt = tvrecv->tv_sec * 1000.0 + tvrecv->tv_usec / 1000.0;25 printf("%d bytes from %s: seq=%u, ttl=%d, rtt=%.3f ms\n",26 icmplen, Sock_ntop_host(pr->sarecv, pr->salen),27 icmp->icmp_seq, ip->ip_ttl, rtt);28 } else if (verbose) {29 printf(" %d bytes from %s: type = %d, code = %d\n",30 icmplen, Sock_ntop_host(pr->sarecv, pr->salen),31 icmp->icmp_type, icmp->icmp_code);32 }33 }Извлечение указателя на ICMP-заголовокЗначение поля длины заголовка IPv4, умноженное на 4, дает размер заголовка IPv4 в байтах. (Следует помнить, что IPv4-заголовок может содержать параметры.) Это позволяет нам установить указатель icmp так, чтобы он указывал на начало ICMP-заголовка. Мы проверяем, относится ли данный пакет к протоколу ICMP и имеется ли в нем достаточно данных для проверки временной отметки, включенной нами в эхо-запрос. На рис. 28.3 приведены различные заголовки, указатели и длины, используемые в коде.10-16Рис. 28.3. Заголовки, указатели и длина при обработке ответов ICMPv4
Проверка эхо-ответа ICMPЕсли сообщение является эхо-ответом ICMP, то необходимо проверить поле идентификатора, чтобы выяснить, относится ли этот ответ к посланному данным процессом запросу. Если программа ping запущена на одном узле несколько раз, каждый процесс получает копии всех полученных ICMP-сообщений.17-21Путем вычитания времени отправки сообщения (содержащегося в части ICMP-ответа, отведенной под дополнительные данные) из текущего времени (на которое указывает аргумент функции22-27) вычисляется значение RTT. Время RTT преобразуется из микросекунд в миллисекунды и выводится на экран вместе с полем порядкового номера и полученным значением TTL. Поле порядкового номера позволяет пользователю проследить, не были ли пакеты пропущены, переупорядочены или дублированы, а значение TTL показывает количество транзитных узлов между двумя узлами.tvrecvВывод всех полученных ICMP-сообщений при включении параметра verboseЕсли пользователем указан параметр командной строки28-32, также выводятся поля типа и кода из всех других полученных ICMP-сообщений.-vОбработка сообщений ICMPv6 управляется функцией
, приведенной в листинге 28.8. Она аналогична функцииproc_v6, представленной в листинге 28.6. Однако поскольку символьные сокеты IPv6 не передают процессу заголовок IPv6, ограничение на количество транзитных узлов приходится получать в виде вспомогательных данных. Для этого нам приходится подготавливать сокет функциейproc_v4, представленной в листинге 28.7.init_v6
