14 if (pr->finit)
15 (*pr->finit)();
16 size = 60 * 1024; /* setsockopt может завершиться с ошибкой */
17 setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
18 sig_alrm(SIGALRM); /* отправка первого пакета */
19 iov.iov_base = recvbuf;
20 iov.iov_len = sizeof(recvbuf);
21 msg.msg_name = pr->sarecv;
22 msg.msg_iov = &iov;
23 msg.msg_iovlen = 1;
24 msg.msg_control = controlbuf;
25 for (;;) {
26 msg.msg_namelen = pr->salen;
27 msg.msg_controllen = sizeof(controlbuf);
28 n = recvmsg(sockfd, &msg, 0);
29 if (n
30 if (errno == EINTR)
31 continue;
32 else
33 err_sys("recvmsg error");
24 }
35 Gettimeofday(&tval, NULL);
36 (*pr->fproc)(recvbuf, n, &msg, &tval);
37 }
38 }
Создание сокета
12-13
Создается символьный сокет, соответствующий выбранному протоколу. В вызове функции setuid
нашему эффективному идентификатору пользователя присваивается фактический идентификатор пользователя. Для создания символьных сокетов программа должна иметь права привилегированного пользователя, но когда символьный сокет уже создан, от этих прав можно отказаться. Всегда разумнее отказаться от лишних прав, если в них нет необходимости, например на тот случай, если в программе есть скрытая ошибка, которой кто-либо может воспользоваться.
Выполнение инициализации для протокола
14-15
Мы выполняем функцию инициализации для выбранного протокола. Для IPv6 такая функция представлена в листинге 28.7.
Установка размера приемного буфера сокета
16-17
Пытаемся установить размер приемного буфера сокета, равный 61 440 байт (60×1024) — этот размер больше задаваемого по умолчанию. Это делается в расчете на случай, когда пользователь проверяет качество связи с помощью программы 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)
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 ip_p != IPPROTO_ICMP)
13 return; /* не ICMP */
14 icmp = (struct icmp*)(ptr + hlen1); /* начало ICMP-заголовка */
15 if ((icmplen = len - hlen1)
16 return; /* плохой пакет */
17 if (icmp->icmp_type == ICMP_ECHOREPLY) {
18 if (icmp->icmp_id != pid)
19 return; /* это не ответ на наш ECHO_REQUEST */
20 if (icmplen
21 return; /* недостаточно данных */
Читать дальше
Конец ознакомительного отрывка
Купить книгу