Категории
Самые читаемые
PochitayKnigi » Компьютеры и Интернет » Программное обеспечение » UNIX: разработка сетевых приложений - Уильям Стивенс

UNIX: разработка сетевых приложений - Уильям Стивенс

Читать онлайн UNIX: разработка сетевых приложений - Уильям Стивенс

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 206 207 208 209 210 211 212 213 214 ... 263
Перейти на страницу:

 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 msn",

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 = %dn",

30   icmplen, Sock_ntop_host(pr->sarecv, pr->salen),

31   icmp->icmp_type, icmp->icmp_code);

32  }

33 }

Извлечение указателя на ICMP-заголовок

10-16 Значение поля длины заголовка IPv4, умноженное на 4, дает размер заголовка IPv4 в байтах. (Следует помнить, что IPv4-заголовок может содержать параметры.) Это позволяет нам установить указатель icmp так, чтобы он указывал на начало ICMP-заголовка. Мы проверяем, относится ли данный пакет к протоколу ICMP и имеется ли в нем достаточно данных для проверки временной отметки, включенной нами в эхо-запрос. На рис. 28.3 приведены различные заголовки, указатели и длины, используемые в коде.

Рис. 28.3. Заголовки, указатели и длина при обработке ответов ICMPv4

Проверка эхо-ответа ICMP

17-21 Если сообщение является эхо-ответом ICMP, то необходимо проверить поле идентификатора, чтобы выяснить, относится ли этот ответ к посланному данным процессом запросу. Если программа ping запущена на одном узле несколько раз, каждый процесс получает копии всех полученных ICMP-сообщений.

22-27 Путем вычитания времени отправки сообщения (содержащегося в части ICMP-ответа, отведенной под дополнительные данные) из текущего времени (на которое указывает аргумент функции tvrecv) вычисляется значение RTT. Время RTT преобразуется из микросекунд в миллисекунды и выводится на экран вместе с полем порядкового номера и полученным значением TTL. Поле порядкового номера позволяет пользователю проследить, не были ли пакеты пропущены, переупорядочены или дублированы, а значение TTL показывает количество транзитных узлов между двумя узлами.

Вывод всех полученных ICMP-сообщений при включении параметра verbose

28-32 Если пользователем указан параметр командной строки -v, также выводятся поля типа и кода из всех других полученных ICMP-сообщений.

Обработка сообщений ICMPv6 управляется функцией proc_v6, приведенной в листинге 28.8. Она аналогична функции proc_v4, представленной в листинге 28.6. Однако поскольку символьные сокеты IPv6 не передают процессу заголовок IPv6, ограничение на количество транзитных узлов приходится получать в виде вспомогательных данных. Для этого нам приходится подготавливать сокет функцией init_v6, представленной в листинге 28.7.

Листинг 28.7. Функция init_v6: подготовка сокета

 1 void

 2 init_v6()

 3 {

 4 #ifdef IPV6

 5  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_RECVHOPLIMIT

17  /* RFC 3542 */

18  setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on));

19 #else

20  /* RFC 2292 */

21  setsockopt(sockfd, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof(on));

22 #endif

23 #endif

24 }

Приведенная в листинге 28.8 функция proc_v6 обрабатывает входящие пакеты.

Листинг 28.8. Функция proc_v6: обработка сообщений ICMPv6

//ping/proc_v6.c

 1 #include "ping.h"

 2 void

 3 proc_v6(char *ptr, ssize_t len, struct msghdr *msg, struct timeval* tvrecv)

 4 {

 5 #ifdef IPV6

 6  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   else

36    printf("%d", hlim);

37   printf(", rtt=%.3f msn", rtt);

38  } else if (verbose) {

39   printf(" %d bytes from type = %d, code = %dn",

40    len, Sock_ntop_host(pr->sarecv, pr->salen);

41   icmp6->icmp6, type, icmp6->icmp6_code);

42  }

43 #endif /* IPV6 */

44 }

Извлечение указателя на заголовок ICMPv6

11-13 Заголовок ICMPv6 возвращается внутри данных при чтении из сокета. (Напомним, что дополнительные заголовки IPv6, если они присутствуют, всегда возвращаются не как стандартные данные, а как вспомогательные.) На рис. 28.4 приведены различные заголовки, указатели и длина, используемые в коде.

Рис. 28.4. Заголовки, указатели и длина при обработке ответов ICMPv6

Проверка эхо-ответа ICMP

14-37 Если ICMP-сообщение является эхо-ответом, то чтобы убедиться, что ответ предназначен для нас, мы проверяем поле идентификатора. Если это подтверждается, то вычисляется значение RTT, которое затем выводится вместе с порядковым номером и предельным количеством транзитных узлов IPv4. Ограничение на количество транзитных узлов мы получаем из вспомогательных данных IPV6_HOPLIMIT.

Вывод всех полученных ICMP-сообщений при включении параметра verbose

38-42 Если пользователь указал параметр командной строки -v, выводятся также поля типа и кода всех остальных получаемых ICMP-сообщений.

Обработчиком сигнала SIGALRM является функция sig_alrm, приведенная в листинге 28.9. В листинге 28.4 функция readloop вызывает обработчик сигнала один раз для отправки первого пакета. Эта функция в зависимости от протокола вызывает функцию send_v4 или send_v6 для отправки эхо-запроса ICMP и далее программирует запуск другого сигнала SIGALRM через 1 с.

Листинг 28.9. Функция sig_alrm: обработчик сигнала SIGALRM

//ping/sig_alrm.c

 1 #include "ping.h"

 2 void

 3 sig_alrm(int signo)

 4 {

 5  (*pr->fsend)();

 6  alarm(1);

 7  return;

 8 }

Функция send_v4, приведенная в листинге 28.10, строит ICMPv4 сообщение эхо-запроса и записывает его в символьный сокет.

Листинг 28.10. Функция send_v4: построение эхо-запроса ICMPv4 и его отправка

//ping/send_v4.c

 1 #include "ping.h"

 2 void

 3 send_v4(void)

 4 {

 5  int len;

 6  struct icmp *icmp;

 7  icmp = (struct icmp*)sendbuf;

 8  icmp->icmp_type = ICMP_ECHO;

 9  icmp->icmp_code = 0;

10  icmp->icmp_id = pid;

11  icmp->icmp_seq = nsent++;

12  memset(icmp->icmp_data, 0xa5, datalen); /* заполнение по шаблону */

13  Gettimeofday((struct timeval*)icmp->icmp_data, NULL);

14  len = 8 + datalen; /* контрольная сумма по заголовку и данным */

15  icmp->icmp_cksum = 0;

16  icmp->icmp_cksum = in_cksum((u_short*)icmp, len);

17  Sendto(sockfd, sendbuf, len, 0, pr->sasend, pr->salen);

1 ... 206 207 208 209 210 211 212 213 214 ... 263
Перейти на страницу:
Тут вы можете бесплатно читать книгу UNIX: разработка сетевых приложений - Уильям Стивенс.
Комментарии