Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Программирование для начинающих и не только


Информационный Канал Subscribe.Ru

По материалам сайта www.gigabyte.iatp.org.ua

©Gigabyte 2004

Trace route своими руками

Trace route - одна из важнейших команд, использующихся при настройке и отладке сетей любых масштабов и топологий. В этой статье мы рассмотрим методы организации операции trace route при помощи Delphi.

Теория

Функция, Trace route позволяет выявлять последовательность шлюзов, через которую проходит IP-пакет на пути к пункту своего назначения, в том или ином виде представлена в любой современной операционной системе и реализована как при помощи программы командной строки так и в качестве библиотечной функции . Эта команда имеет очень много опций, большинство из которых в повседневной работе не применяются. Как обычно, имя машины, которое может быть задано в символической или числовой форме. Выходная информация - список машин, начиная с первого шлюза и заканчивая пунктом назначения.

Команда traceroute (traceroute.exe для Windows) работает путем установки поля времени жизни (числа переходов) исходящего пакета таким образом, чтобы это время истекало до того, как пакет достигнет пункта назначения. Когда время жизни истечет, текущий шлюз должен послать сообщение об ошибке на машину-источник. Каждое приращение поля времени жизни позволяет пакету пройти на один шлюз дальше.

Команда traceroute посылает для каждого значения поля времени жизни три пакета. Если промежуточный шлюз распределяет трафик по нескольким маршрутам, то эти пакеты могут возвращаться разными машинами, в этом случае traceroute просто печатает их все. Некоторые системы не посылают уведомлений о пакетах, время жизни которых истекло, а некоторые посылают уведомления, которые поступают обратно на машину-источник только после того, как traceroute перестала их ждать. Эти "таинственные шлюзы" обозначаются рядом звездочек. Даже если конкретный шлюз определить нельзя, traceroute все равно сможет увидеть следующие за ним узлы маршрута.

Практика

Практическая реализация команды traceroute в Delphi как всегда может быть реализована несколькими способами и наиболее простой из них это использование компонентов предоставляемых сторонними производителями (т.к. в компонентах Indy я не нашел соответствующего компонента, или может не там искал), и конечно же можно использовать Windows API. О достоинствах и недостатках той или иной реализации этой функции можно судить исходя из ваших потребностей, возможностей и времени. Так если вы даже отдаленно не представляете себе как надо работать с Windows API то лучшим вариантом для вас будет скачать из сети соответствующий компонент и использовать его. Этот же вариант можно использовать если вам срочно (в течении 2-3 часов) необходимо реализовать простенькое приложение, которое бы выполняло trace route. Но если вы не стеснены во времени или должны написать критическое по времени выполнения приложение, то тогда вам следует позаботится о правильной реализации функции traceroute с использованием Windows API. И конечно же после того, как вы основательно разобрались с WinAPI, то следующим шагом станет написание компонента которые бы брал на себя всю грязную работу оставляя вам только обработку чистых результатов отслеживания.

Windows API

К сожалению в Windows вы не сможете найти такую библиотеку, которая бы прямо экспортировала функцию traceroute (а если и найдете, то это будет уже изюминка Вашей Windows и не стоит ее использовать т.к. вы не знаете будет ли такая "изюминка" и в других Windows-ах).

Для реализации traceroute мы воспользуемся уже известной нам библиотекой icmp.dll[1]. Здесь же нам придется применить смекалку и воспользоваться алгоритмом приведенным в первом разделе для написания функции traceroute.

Итак, для написания функции которая бы отслеживала адреса шлюзов через которые переходит пакет мы для начала в Delphi организуем новое приложение, в котором разместим кнопку и компонент TListBox. Далее по алгоритму мы будем пинговать адрес (к примеру www.mail.ru: 194.67.57.51) с заданными параметром TTL = 1, и выведем результат, далее пингуем хост при TTL=2 3 4 и т.д. Применение такого алгоритма будет нам последовательно возвращать IP-адрес каждого шлюза через который прошел пакет. И для того, чтобы не возится каждый раз с открыванием и накрыванием хэндлов мы сперва напишем функцию PingAlt (несколько переработанный аналог функции ping из [1]), которая в качестве дополнительного параметра будет принимать структуру TOption_Information одним из полей которого и есть уже упомянутый TTL, параметр, который в нашем случае будет играть главную роль при работе нашей программы. Сама функция PingAlt выглядит следующим образом:


procedure PingAlt(const Address,EchoString:PChar;var PingReply:TsmICMP_Echo_Reply;Options:TOption_Information;
 const PingTimeout:integer=5000);
var IpAddress:TIPAddr;
    icmpport:THandle;
begin
 IpAddress:=inet_addr(Address);
 if (ipaddress=INADDR_NONE) then raise Exception.Create('Function call inetaddr failed. with Address '+Address);
 icmpport:=IcmpCreateFile;
 if (icmpport=invalid_handle_value) then raise Exception.Create('icmpCreateFile Failed');
 icmpSendEcho (icmpport,ipaddress,EchoString,Length(EchoString),@Options,@PingReply,SizeOf(PingReply),PingTimeout);
 IcmpCloseHandle(icmpport);
end;

Кроме того, для вывода результатов работы мы напишем еще одну функцию, которая будет преобразовывать IP-адрес в понятный для нас вид:


function ip2string(ip_address:longint):string;
begin
  ip_address:=winsock.ntohl(ip_address);
  result:= inttostr(ip_address shr 24)+'.'+
           inttostr((ip_address shr 16) and $ff)+'.'+
           inttostr((ip_address shr 8) and $ff)+'.'+
           inttostr(ip_address and $ff);
end;

Теперь нам потребуется только правильно организовать цикл для отслеживания всех шлюзов, который можно написать следующим образом:


procedure TForm1.Button2Click(Sender: TObject);
var Options:TOption_Information;
    Reply:TsmICMP_Echo_Reply;
    i:integer;Buf:Array[0..127] of Char;
begin
ZeroMemory(@Options,SizeOf(Options));
FillChar(Buf,SizeOf(Buf),#123);
Options.Ttl:=255;
PingAlt('194.67.57.51',@Buf,Reply,Options,5000);
Listbox1.Items.Add(IntToStr(REply.RoundTripTime)+':'+IntToStr(Reply.Options.Ttl));
i:=1;
ZeroMemory(@Reply,SizeOf(Reply));
while (Reply.Status<>IP_SUCCESS) or (ip2string(REply.Address)<>'194.67.57.51') do
 begin
  options.Ttl:=i;
  PingAlt('194.67.57.51',@Buf,Reply,Options,5000);
  LIstBox1.Items.Add(ip2string(Reply.Address)+':'+IntToStr(Reply.RoundTripTime));
  inc(i);
  Application.ProcessMessages;
 end;
end;

Теперь после нажатия кнопки у нас в списке сперва появится строка с отпингованным временем и свойством TTL для адреса mail.ru. А дальше последовательно будут появляться адреса шлюзов по которым проходят пакет до конечного адреса. Все приложение готово.

Что еще осталось

Конечно описанное выше демонстрационное приложение далеко от идеала и по своим дизайнерским(рис.1) и по функциональным качествам. Так для использования этого приложение вам придется вводить только IP-адреса так как она пока что не в состоянии сама определить IP для хоста заданного именем. Для того, чтобы преодолеть этот недосмотр самым легким способом, мы можем воспользоваться методом WSGetHostByName из объекта GStack, определенного в модуле idStack, который поставляется вместе с Delphi. Если же вы желаете использовать только WinAPI, то лучший способ - заглянуть в модуль idStackWinsock.pas где расположен код реализации этой функции для Windows.

Листинг.1. Описание функций ICMP.DLL


unit icmp;
interface
uses windows;
Const
// IP_STATUS codes returned from IP APIs

   IP_STATUS_BASE                 = 11000;
   IP_SUCCESS                     = 0;
   IP_BUF_TOO_SMALL               = (IP_STATUS_BASE + 1);
   IP_DEST_NET_UNREACHABLE        = (IP_STATUS_BASE + 2);
   IP_DEST_HOST_UNREACHABLE       = (IP_STATUS_BASE + 3);
   IP_DEST_PROT_UNREACHABLE       = (IP_STATUS_BASE + 4);
   IP_DEST_PORT_UNREACHABLE       = (IP_STATUS_BASE + 5);
   IP_NO_RESOURCES                = (IP_STATUS_BASE + 6);
   IP_BAD_OPTION                  = (IP_STATUS_BASE + 7);
   IP_HW_ERROR                    = (IP_STATUS_BASE + 8);
   IP_PACKET_TOO_BIG              = (IP_STATUS_BASE + 9);
   IP_REQ_TIMED_OUT               = (IP_STATUS_BASE + 10);
   IP_BAD_REQ                     = (IP_STATUS_BASE + 11);
   IP_BAD_ROUTE                   = (IP_STATUS_BASE + 12);
   IP_TTL_EXPIRED_TRANSIT         = (IP_STATUS_BASE + 13);
   IP_TTL_EXPIRED_REASSEM         = (IP_STATUS_BASE + 14);
   IP_PARAM_PROBLEM               = (IP_STATUS_BASE + 15);
   IP_SOURCE_QUENCH               = (IP_STATUS_BASE + 16);
   IP_OPTION_TOO_BIG              = (IP_STATUS_BASE + 17);
   IP_BAD_DESTINATION             = (IP_STATUS_BASE + 18);

// The next group are status codes passed up on status indications to
// transport layer protocols.
   IP_ADDR_DELETED                = (IP_STATUS_BASE + 19);
   IP_SPEC_MTU_CHANGE             = (IP_STATUS_BASE + 20);
   IP_MTU_CHANGE                  = (IP_STATUS_BASE + 21);
   IP_UNLOAD                      = (IP_STATUS_BASE + 22);
   IP_GENERAL_FAILURE             = (IP_STATUS_BASE + 50);
   MAX_IP_STATUS                  = IP_GENERAL_FAILURE;
   IP_PENDING                     = (IP_STATUS_BASE + 255);

// Values used in the IP header Flags field.
   IP_FLAG_DF                     = $2;        //  Don't fragment this packet.

// Supported IP Option Types.
// These types define the options which may be used in the OptionsData field
// of the ip_option_information structure.  See RFC 791 for a complete
// description of each.
   IP_OPT_EOL                     = 0;         //  End of list option
   IP_OPT_NOP                     = 1;         //  No operation
   IP_OPT_SECURITY                = $82;       //  Security option
   IP_OPT_LSRR                    = $83;       //  Loose source route
   IP_OPT_SSRR                    = $89;       //  Strict source route
   IP_OPT_RR                      = $7;        //  Record route
   IP_OPT_TS                      = $44;       //  Timestamp
   IP_OPT_SID                     = $88;       //  Stream ID (obsolete)

   MAX_OPT_SIZE                   = 40;        //  Maximum length of IP options in bytes


Type

 TIPAddr=integer;     // An IP address.
 TIPMask=integer;     // An IP subnet mask.
 TIP_STATUS=Integer;  // Status code returned from IP APIs.

POption_Information=^TOption_Information;
TOption_Information=record
                     Ttl:byte;             // Time To Live
                     Tos:byte;             // Type Of Service
                     Flags:byte;           // IP header flags
                     OptionsSize:byte;     // Size in bytes of options data
                     OptionsData:pointer;  // Pointer to options data
                    end;
Picmp_echo_reply=^Ticmp_echo_reply;
Ticmp_echo_reply=record
                    Address:TipAddr;                // Replying address
                    Status:integer;                 // Reply IP_STATUS
                    RoundTripTime:integer;          // RTT in milliseconds
                    DataSize:word;                  // Reply data size in bytes
                    Reserved:word;                  // Reserved for system use
                    Data:pointer;                   // Pointer to the reply data
                    Options:Toption_Information;    // Reply options
                 end;
TsmICMP_Echo_Reply=record
                    Address:TipAddr;                // Replying address
                    Status:integer;                 // Reply IP_STATUS
                    RoundTripTime:integer;          // RTT in milliseconds
                    DataSize:word;                  // Reply data size in bytes
                    Reserved:word;                  // Reserved for system use
                    DataPtr:pointer;                // Pointer to the reply data
                    Options:Toption_Information;    // Reply options
                    Data: array[0..255] of Char;
                 end;

function IcmpCreateFile:Thandle; StdCall;
function IcmpCloseHandle(H:Thandle):Bool; StdCall;
function IcmpSendEcho(IcmpHandle:Thandle;DestinationAddress:TipAddr;
        RequestData:pointer;RequestSize:word;
                      RequestOptions:POption_Information;ReplyBuffer:pointer;
        ReplySize:integer;Timeout:integer):Integer; stdcall;
Implementation
function IcmpCreateFile;        external 'Icmp.Dll';
function IcmpCloseHandle;       external 'Icmp.Dll';
Function IcmpSendEcho;          external 'Icmp.Dll';
end.

©Gigabyte 2004

http://subscribe.ru/
http://subscribe.ru/feedback/
Подписан адрес:
Код этой рассылки: comp.soft.prog.programmershelp
Отписаться

В избранное