7 {icmpcode_v6, recv_v6, NULL, NULL, NULL, NULL, 0,
8 IPPROTO_ICMPV6, IPPROTO_IPV6, IPV6_UNICAST_HOPS};
9 #endif
10 int datalen = sizeof(struct rec); /* значения по умолчанию */
11 int max_ttl = 30;
12 int nprobes = 3;
13 u_short dport = 32768 + 666;
14 int
15 main(int argc, char **argv)
16 {
17 int c;
18 struct addrinfo *ai;
19 opterr = 0; /* чтобы функция getopt() не записывала в stderr */
20 while ((с = getopt(argc, argv, "m:v")) != -1) {
21 switch (c) {
22 case 'm':
23 if ((max_ttl = atoi(optarg)) <= 1)
24 err_quit("invalid -m value");
25 break;
26 case 'v':
27 verbose++;
28 break;
29 case '?':
30 err_quit("unrecognized option: %c", c);
31 }
32 }
33 if (optind != argc - 1)
34 err_quit("usage: traceroute [ -m
35 host = argv[optind];
36 pid = getpid();
37 Signal(SIGALRM, sig_alrm);
38 ai = Host_serv(host, NULL, 0, 0);
39 printf("traceroute to %s (%s): %d hops max, %d data bytes\n",
40 ai->ai_canonname,
41 Sock_ntop_host(ai->ai_addr, ai->ai_addrlen);
42 max_ttl, datalen);
43 /* инициализация в зависимости от протокола */
44 if (ai->ai_family == AF_INET) {
45 pr = &proto_v4;
46 #ifdef IPV6
47 } else if (ai->ai_family == AF_INET6) {
48 pr = &proto_v6;
49 if (IN6_IS_ADDR_V4MAPPED
50 (&(((struct sockaddr_in6*)ai->ai_addr)->sin6_addr)))
51 err_quit("cannot traceroute IPv4-mapped IPv6 address");
52 #endif
53 } else
54 err_quit("unknown address family %d", ai->ai_family);
55 pr->sasend = ai->ai_addr; /* содержит адрес получателя */
56 pr->sarecv = Calloc(1, ai->ai_addrlen);
57 pr->salast = Calloc(1, ai->ai_addrlen);
58 pr->sabind = Calloc(1, ai->ai_addrlen);
59 pr->salen = ai->ai_addrlen;
60 traceloop();
61 exit(0);
62 }
2-9
Определяются две структуры proto
, одна для IPv4 и другая для IPv6, хотя указатели на структуры адреса сокета не размещаются в памяти до окончания выполнения данной функции.
10-13
Максимальное значение поля TTL или поля предельного количества транзитных узлов, используемое в программе, по умолчанию равно 30. Предусмотрен параметр командной строки -m
, чтобы пользователь мог поменять это значение. Для каждого значения TTL посылается три пробных пакета, но их количество также может быть изменено с помощью параметра командной строки. Изначально используется номер порта получателя 32 768 + 666, и каждый раз, когда посылается новая дейтаграмма UDP, это значение увеличивается на 1. Мы можем надеяться, что порты с такими номерами не используются на узле получателя в тот момент, когда приходит дейтаграмма, однако гарантии здесь нет.
19-37
Параметр командной строки -v позволяет вывести все остальные ICMP-сообщения.
38-58
Имя узла получателя или IP-адрес обрабатывается функцией host_serv
, возвращающей указатель на структуру addrinfo
. В зависимости от типа возвращенного адреса (IPv4 или IPv6) заканчивается инициализация структуры proto
, сохраняется указатель в глобальной переменной pr, а также размещается в памяти дополнительная структура адреса сокета соответствующего размера.
Функция traceloop
, приведенная в листинге 28.15, отправляет дейтаграммы и читает вернувшиеся ICMP-сообщения. Это основной цикл программы.