UNIX: разработка сетевых приложений
Часть 313 из 399 Информация о книге
Листинг 28.7. Функция init_v6: подготовка сокета
1 void2 init_v6()3 {4 #ifdef IPV65 int on = 1;6 if (verbose == 0) {7 /* установка фильтра, пропускающего только пакеты ICMP6_ECHO_REPLY. еслине включен параметр verbose (вывод всех ICMP-сообщений) */8 struct icmp6_filter myfilt;9 ICMP6_FILTER_SETBLOCKALL(&myfilt);10 ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &myfilt);11 setsockopt(sockfd, IPPROTO_IPV6, ICMP6_FILTER, &myfilt,12 sizeof(myfilt));13 /* игнорируем ошибку, потому что фильтр - необязательная оптимизация */14 }15 /* следующую ошибку тоже игнорируем; придется обойтись без выводаограничения на количество транзитных узлов */16 #ifdef IPV6_RECVHOPLIMIT17 /* RFC 3542 */18 setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on));19 #else20 /* RFC 2292 */21 setsockopt(sockfd, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof(on));22 #endif23 #endif24 }Приведенная в листинге 28.8 функция
обрабатывает входящие пакеты.proc_v6Листинг 28.8. Функция proc_v6: обработка сообщений ICMPv6
//ping/proc_v6.c1 #include "ping.h"2 void3 proc_v6(char *ptr, ssize_t len, struct msghdr *msg, struct timeval* tvrecv)4 {5 #ifdef IPV66 double rtt;7 struct icmp6_hdr *icmp6;8 struct timeval *tvsend;9 struct cmsghdr *cmsg;10 int hlim;11 icmp6 = (struct icmp6_hdr*)ptr;12 if (len < 8)13 return; /* плохой пакет */14 if (icmp6->icmp6_type == ICMP6_ECHO_REPLY) {15 if (icmp6->icmp6_id != pid)16 return; /* это не ответ на наш ECHO_REQUEST */17 if (len < 16)18 return; /* недостаточно данных */19 tvsend = (struct timeval*)(icmp6 + 1);20 tv_sub(tvrecv, tvsend);21 rtt = tvrecv->tv_sec * 1000.0 + tvrecv->tv_usec / 1000.0;22 hlim = -1;23 for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;24 cmsg = CMSG_NXTHDR(msg, cmsg)) {25 if (cmsg->cmsg_level == IPPROTO_IPV6 &&26 cmsg->cmsg_type == IPV6_HOPLIMIT) {27 hlim = *(u_int32_t*)CMSG_DATA(cmsg);28 break;29 }30 }31 printf("%d bytes from %s; seq=%u, hlim=",32 len, Sock_ntop__host(pr->sarecv, pr->salen), icmp6->icmp6_seq);33 if (hlim == -1)34 printf("???"); /* отсутствуют вспомогательные данные */35 else36 printf("%d", hlim);37 printf(", rtt=%.3f ms\n", rtt);38 } else if (verbose) {39 printf(" %d bytes from type = %d, code = %d\n",40 len, Sock_ntop_host(pr->sarecv, pr->salen);41 icmp6->icmp6, type, icmp6->icmp6_code);42 }43 #endif /* IPV6 */44 }Извлечение указателя на заголовок ICMPv6Заголовок ICMPv6 возвращается внутри данных при чтении из сокета. (Напомним, что дополнительные заголовки IPv6, если они присутствуют, всегда возвращаются не как стандартные данные, а как вспомогательные.) На рис. 28.4 приведены различные заголовки, указатели и длина, используемые в коде.11-13Рис. 28.4. Заголовки, указатели и длина при обработке ответов ICMPv6
Проверка эхо-ответа ICMPЕсли ICMP-сообщение является эхо-ответом, то чтобы убедиться, что ответ предназначен для нас, мы проверяем поле идентификатора. Если это подтверждается, то вычисляется значение RTT, которое затем выводится вместе с порядковым номером и предельным количеством транзитных узлов IPv4. Ограничение на количество транзитных узлов мы получаем из вспомогательных данных14-37.IPV6_HOPLIMITВывод всех полученных ICMP-сообщений при включении параметра verboseЕсли пользователь указал параметр командной строки38-42, выводятся также поля типа и кода всех остальных получаемых ICMP-сообщений.-vОбработчиком сигнала SIGALRM является функция
, приведенная в листинге 28.9. В листинге 28.4 функция readloop вызывает обработчик сигнала один раз для отправки первого пакета. Эта функция в зависимости от протокола вызывает функциюsig_alrmилиsend_v4для отправки эхо-запроса ICMP и далее программирует запуск другого сигналаsend_v6через 1 с.SIGALRM
