Чтение онлайн

ЖАНРЫ

UNIX: разработка сетевых приложений
Шрифт:
Выполнение инициализации для протокола

14-15
Мы выполняем функцию инициализации для выбранного протокола. Для IPv6 такая функция представлена в листинге 28.7.

Установка размера приемного буфера сокета

16-17
Пытаемся установить размер приемного буфера сокета, равный 61 440 байт (60x1024) — этот размер больше задаваемого по умолчанию. Это делается в расчете на случай, когда пользователь проверяет качество связи с помощью программы
ping
, обращаясь либо к широковещательному адресу IPv4, либо к групповому адресу. В обоих случаях может быть получено большое количество ответов. Увеличивая
размер буфера, мы уменьшаем вероятность того, что приемный буфер переполнится.

Отправка первого пакета

18
Запускаем обработчик сигнала, который, как мы увидим, посылает пакет и создает сигнал
SIGALRM
один раз в секунду. Обычно обработчик сигналов не запускается напрямую, как у нас, но это можно делать. Обработчик сигналов является обычной функцией языка С, просто в нормальных условиях он асинхронно запускается ядром.

Подготовка msghdr для recvmsg

19-24
Мы записываем значения в неизменяемые поля структур
msghdr
и
iovec
, которые будут передаваться функции
recvmsg
.

Бесконечный цикл для считывания всех ICMP-сообщений

25-37
Основной цикл программы является бесконечным циклом, считывающим все пакеты, возвращаемые на символьный сокет ICMP. Вызывается функция
gettimeofday
для регистрации времени получения пакета, а затем вызывается соответствующая функция протокола (
proc_v4
или
proc_v6
) для обработки ICMP-сообщения.

В листинге 28.5 приведена функция

tv_sub
, вычисляющая разность двух структур
timeval
и сохраняющая результат в первой из них.

Листинг 28.5. Функция tv_sub: вычитание двух структур timeval

//lib.tv_sub.c

1 #include "unp.h"

2 void

3 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 приведена функция

proc_v4
, обрабатывающая все принимаемые сообщения ICMPv4. Можно также обратиться к рис. А.1, на котором изображен формат заголовка IPv4. Кроме того, следует осознавать, что к тому моменту, когда процесс получает на символьном сокете ICMP-сообщение, ядро уже проверило, что основные поля в заголовке IPv4 и в сообщении ICMPv4 действительны [128, с. 214, с. 311].

Листинг 28.6.Функция proc_v4: обработка сообщений ICMPv4

//ping/prov_v4.c

1 #include "ping.h"

2 void

3 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-заголовок

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 показывает количество транзитных узлов между двумя узлами.

Поделиться с друзьями: