UNIX: разработка сетевых приложений
Листинг 28.9. Функция sig_alrm: обработчик сигнала SIGALRM
//ping/sig_alrm.c1 #include "ping.h"2 void3 sig_alrm(int signo)4 {5 (*pr->fsend)();6 alarm(1);7 return;8 }Функция
, приведенная в листинге 28.10, строит ICMPv4 сообщение эхо-запроса и записывает его в символьный сокет.send_v4Листинг 28.10. Функция send_v4: построение эхо-запроса ICMPv4 и его отправка
//ping/send_v4.c1 #include "ping.h"2 void3 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);18 }Формирование ICMP-сообщенияICMPv4 сообщение сформировано. В поле идентификатора установлен идентификатор нашего процесса, а порядковый номер установлен как глобальная переменная7-13, которая затем увеличивается на 1 для следующего пакета. Текущее время сохраняется в части данных ICMP-сообщения.nsetВычисление контрольной суммы ICMPДля вычисления контрольной суммы ICMP значение поля контрольной суммы устанавливается равным 0, затем вызывается функция14-16, а результат сохраняется в поле контрольной суммы. Контрольная сумма ICMPv4 вычисляется по ICMPv4-заголовку и всем следующим за ним данным.in_cksumОтправка дейтаграммыICMP-сообщение отправлено на символьный сокет. Поскольку параметр сокета17не установлен, ядро составляет заголовок IPv4 и добавляет его в начало нашего буфера.IP_HDRINCLКонтрольная сумма Интернета является суммой обратных кодов 16-разрядных значений. Если длина данных является нечетным числом, то для вычисления контрольной суммы к данным дописывается один нулевой байт. Перед вычислением контрольной суммы поле контрольной суммы должно быть установлено в 0. Такой алгоритм применяется для вычисления контрольных сумм IPv4, ICMPv4, IGMPv4, ICMPv6, UDP и TCP. В RFC 1071 [12] содержится дополнительная информация и несколько числовых примеров. В разделе 8.7 книги [128] более подробно рассказывается об этом алгоритме, а также приводится более эффективная его реализация. В нашем случае контрольную сумму вычисляет функция
, приведенная в листинге 28.11.in_cksumЛистинг 28.11. Функция in_cksum: вычисление контрольной суммы Интернета
//libfree/in_cksum.c1 uint16_t2 in_cksum(uint16_t *addr, int len)3 {4 int nleft = len;5 uint32_t sum = 0;6 uint16_t *w = addr;7 uint16_t answer = 0;8 /*9 * Наш алгоритм прост: к 32-разрядному аккумулятору sum мы добавляем10 * 16-разрядные слова, а затем записываем все биты переноса из старших11 * 16 разрядов в младшие 16 разрядов.12 */13 while (nleft > 1) {14 sum += *w++;15 nleft -= 2;16 }17 /* при необходимости добавляем четный байт */18 if (nleft == 1) {19 *(unsigned char*)(&answer) = *(unsigned char*)w;20 sum += answer;21 }22 /* перемещение битов переноса из старших 16 разрядов в младшие */23 sum = (sum >> 16) + (sum & 0xffff); /* добавление старших 16 к младшим */24 sum += (sum >> 16); /* добавление переноса */25 answer = ~sum; /* обрезаем по 16 разрядам */26 return(answer);27 }Алгоритм вычисления контрольной суммы ИнтернетаПервый цикл1-27вычисляет сумму всех 16-битовых значений. Если длина нечетная, то к сумме добавляется конечный байт. Алгоритм, приведенный в листинге 28.11, является простым алгоритмом, подходящим для программыwhile, но неудовлетворительным для больших объемов вычислений контрольных сумм, производимых ядром.pingПРИМЕЧАНИЕЭта функция взята из общедоступной версии программы ping, написанной Майком Мюссом (Mike Muuss).
Последней функцией нашей программы
является функцияping, приведенная в листинге 28.12, которая формирует и посылает эхо-запросы ICMPv6.send_v6Функция
аналогична функцииsend_v6, но обратите внимание, что она не вычисляет контрольную сумму. Как отмечалось ранее, поскольку для вычисления контрольной суммы ICMPv6 используется адрес отправителя из IPv6-заголовка, данная контрольная сумма вычисляется для нас ядром, после того как ядро выяснит адрес отправителя.send_v4