Эффективное программирование TCP-IP

       

Печать ICMP-сообщений


Далее рассмотрим форматирование и печать ICMP-сообщений. Это делает функция print_dg, показанная в листинге 4.4. Передаваемый этой функции буфер имеет структуру, показанную на рис. 4.21.

Из рис. 4.21 видно, что буфер содержит IP-заголовок, за которым идет собственно ICMP-сообщение.

Рис.4.21. ICMP-сообщение, передаваемое функции print_dg

Листинг 4.4. Функция printjdg

1    static void print_dg( char *dg, int len )

2    {

3    struct ip *ip;

4    struct icmp *icmp;

5    struct hostent *hp;

6    char *hname;

7    int hl;

8    static char *redirect_code[] =

9    {

10     "сеть", "хост",

11     "тип сервиса и сеть", "тип сервиса и хост"



12   };

13   static char *timexceed_code [ ] =

14   {

15     "транзите", "сборке"

16   }

17   static char *param_code[] =

18   {

19     "Плохой IP-заголовок", "Нет обязательной опции"

20   };

21   ip = ( struct ip * )dg;

22   if ( ip->ip_v !=4)

23   {

24     error( 0, 0, "IP-датаграмма не версии 4\n" );

25     return;

26   }

27   hl = ip->ip_hl « 2;  /* Длина IP-заголовка в байтах. */

28   if ( len < hl + ICMP_MINLEN )

29   {

30     error( 0, 0, "short datagram (%d bytes) from %s\n",

31     len, inet_ntoa( ip->ip_src ) );

32     return;

33   }

34   hp = gethostbyaddr( ( char * )&ip->ip_src, 4, AF_INET );

35   if ( hp == NULL )

36     hname = "";

37   else

38     hname = hp->h_name;

39   icmp = ( struct icmp * }( dg + hl );  /* ICMP-пакет. */

40   printf( "ICMP %s (%d) от %s (%s)\n",

41      get_type( icmp->icmp_type ),

42      icmp->icmp_type, hname, inet_ntoa( ip->ip_src ) );

43   if ( icmp->icmp_type == ICMP_UNREACH )

44     print_unreachable( icmp );

45   else if ( icmp->icmp_type == ICMP_REDIRECT )

46     printf( "\tПеренаправление на %s\n", icmp->icmp_code <=

47      redirect_code[ icmp->icmp_code ] : "Некорректный код" );


48   else if ( icmp->icmp_type == ICMP_TIMXCEED )

49     printf( "\tTTL == 0 при %s\n", icmp->icmp_code <= 1 ?

50      timexceed_code[ icmp->icmp_code] : "Некорректный код" );

51   else if ( icmp->icmp_type == ICMP_PARAMPROB )

52     printf ( "\t%s\n", icmp->icmp_code <= 1 ?

53      param_code[ icmp->icmp_code ] : "Некорректный код" );

54   }

Получение указателя на IP-заголовок и проверка корректности пакета

21 Записываем в переменную ip указатель на только что прочитанную датаграмму, приведенный к типу struct ip *.

22-26 Поле ip_v - это версия протокола IP. Если протокол не совпадает с IPv4, то печатаем сообщение об ошибке и выходим.

27-33 Поле ip_hl содержит длину заголовка в 32-байтных словах. Умножаем его на 4, чтобы получить длину в байтах, и сохраняем результат в переменной hl. Затем проверяем, что длина ICMP-сообщения не меньше минимально допустимой величины.

Получение имени хоста отправителя

34-38 Используем адрес источника в ICMP-сообщении, чтобы найти имя хоста отправителя. Если gethostbyaddr вернет NULL, то записываем в hname пустую строку, в обратном случае - имя хоста.

Пропуск IP-заголовка и печать отправителя и типа

39-42 Устанавливаем указатель icmp на первый байт, следующий за IP-заголовком. Этот указатель используется далее для получения типа ICMP-сообщения (icmp_type) и печати типа, адреса и имени хоста отправителя. Для получения ASCII-представления типа ICMP вызываем функцию get_type, текст которой приведен в листинге 4.5.

Печать информации, соответствующей типу

43-44 Если это одно из ICMP-сообщений о недоступности, то вызываем функцию print_unreachable (листинг 4.6) для печати дополнительной информации.

45-47 Если это сообщение о перенаправлении, то получаем тип перенаправления из поля icmp_code и печатаем его.

48-50 Если это сообщение об истечении времени существования, из поля icmp_code узнаем, произошло ли это во время транзита или сборки датаграммы, и печатаем результат.



51-53 Если это сообщение о некорректном параметре, из поля icmp_code определяем, в чем ошибка, и печатаем результат.

Функция get_type очевидна. Вы проверяете допустимость кода типа и возвращаете указатель на соответствующую строку (листинг 4.5).

Листинг 4.5. Функция getjype

1    static char *get_type( unsigned icmptype )

2    {

3    static  char  *type[] =

4    {

5      "Эхо-ответ",                        /* 0*/

6      "ICMP  Тип  1",                     /* 1*/

7      "ICMP  Тип  2",                     /* 2*/

8      "Пункт назначения недоступен",      /* 3*/

9      "Источник приостановлен",           /* 4*/

10     "Перенаправление",                  /* 5*/

11     "ICMP Тип  6",                      /* 6*/

12     "ICMP  Тип  7",                     /* 7*/

13     "Эхо-запрос",                       /* 8*/

14     "Отклик маршрутизатора",            /* 9*/

15     "Поиск  маршрутизаторов",           /* 10*/

16     "Истекло время существования",      /* 11*/

17     "Неверный  параметр",               /* 12*/

18     "Запрос  временного штампа",        /* 13*/

19     "Ответ на запрос временного штампа", /* 14*/

20     "Запрос информации",                /* 15*/

21     "Ответ  на  запрос  информации",    /* 16*/

22     "Запрос  маски  адреса",            /* 17*/

23     "Ответ  на  запрос маски  адреса"   /* 18*/

24   }

25   if ( icmptype < ( sizeof( type ) / sizeof ( type[ 0 ]) ) )

26   return type[ icmptype ];

27   return "НЕИЗВЕСТНЫЙ ТИП";

28   }

Последняя функция - это print_unreachable. ICMP-сообщения о недоступности содержат IP-заголовок и первые восемь байт той IP-датаграммы, из-за которой было сгенерировано сообщение о недоступности. Это позволяет узнать адреса и номера портов отправителя и предполагаемого получателя недоставленного сообщения.



Структура IP-датаграммы, прочитанной из простого сокета в составе ICMP-сообщения о недоступности, показана на рис. 4.22. Та часть, которую уже обработала функция print_dg, заштрихована, она не передается в print_unreachable. Приведены также входной параметр функции print_unreachable - icmp и локальные переменные ip и udp.



Рис. 4.22. ICMP-сообщение о недоступности

Функция print_unreachable извлекает информацию из заголовка и первых восьми байт включенной IP-датаграммы. Хотя вы пометили байты как UDP-заголовок, это мог быть и заголовок TCP: номера портов в обоих случаях находятся в одной и той же позиции. Формат UDP-заголовка показан на рис. 4.23.



Рис. 4.23. UDP-заголовок

Текст функции print_unreachable приведен в листинге 4.6.

Листинг4.6. Функцияprint_unreachable

1    static void print_unreachable( struct icmp *icmp )

2    {

3    struct ip *ip;

4    struct udphdr *udp;

5    char laddr[ 15 + 1 ] ;

6    static char *unreach[] =

7    {

8      "Сеть недоступна",                        /* 0 */

9      "Хост недоступен",                        /* 1 */

10     "Протокол недоступен",                    /* 2 */

11     "Порт недоступен",                        /* 3 */

12     "Нужна фрагментация, поднят бит DF",      /* 4 */

13     "Ошибка маршрутизации от источника",      /* 5 */

14     "Сеть назначения неизвестна",             /* 6 */

15     "Хост назначения неизвестен",             /* 7 */

16     "Хост источника изолирован",              /* 8 */

17     "Сеть назначения закрыта администратором ",    /* 9 */

18     "Хост назначения закрыт администратором ",     /* 10 */

19     "Сеть недоступна для типа сервиса",       /* 11 */

20     "Хост недоступен для типа сервиса",       /* 12 */

21     "Связь запрещена администратором",        /* 13 */

22     "Нарушение предшествования хостов",       /* 14 */

23     "Действует отсечка предшествования"       /* 15 */



24   };

25   ip = ( struct ip * )( ( char * )icmp + 8 );

26   udp = ( struct udphdr *)((char *)ip + (ip->ip_hl « 2 ) );

27   strcpy( laddr, inet_ntoa( ip->ip_src ) );

28   printf( "\t%s\n\tИст.: %s.%d, Назн.: %s.%d\n",

29   icmp->icmp_code < ( sizeof( unreach ) /

30   sizeof( unreach[ 0 ] ) )?

31   unreach[ icmp->icmp_code ] : "Некорректный код",

32   laddr, ntohs( udp->uh_sport ),

33   inet_ntoa( ip->ip_dst ), ntohs( udp->uh_dport ) );

34   }

Установка указателей и получение адреса источника

25-26 Начинаем с установки указателей ip и udp соответственно на IP-заголовок и первые восемь байт вложенной IP-датаграммы.

27 Копируем адрес источника из IP-заголовка в локальную переменную laddr.

Печать адресов, портов и типа сообщения

28-33 Печатаем адреса и номера портов источника и назначения, а также уточненный тип сообщения о недоступности.

В качестве примера использования программы ICMP приведено несколько юследних ICMP-сообщений, полученных при запуске traceroute (совет 35).

traceroute -q 1 netcom4.netcom.com

Опция -q 1 означает, что traceroute должна посылать пробный запрос только один раз, а не три, как принято по умолчанию.

ICMP Истекло время существования (11) от hl-0.mig-fl-gwl.icg.net

(165.236.144.110)

 TTL == 0 во время транзита

ICMP Истекло время существования (11) от sl0-0-0.dfw-tx-

gwl.icg.net (165.236.32.74)

 TTL == 0 во время транзита

ICMP Истекло время существования (11) от dfw-tx-gw2.icg.net

(163.179.1.133)

 TTL == 0 во время транзита

ICMP Пункт назначения недоступен (3) от netcom4.netcom.com

(199.183.9.104)

 Порт недоступен

 Ист. 205.184.-142.71.45935, Назн. 199.183.9.104.33441

Обычно нет необходимости следить с помощью icmp за работой traceroute, но это может быть очень полезно для поиска причин отсутствия связи.


Содержание раздела