+
+ minpacket = sizeof(*outip) + sizeof(*outdata) + optlen;
+
+#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
+ if (useicmp)
+ minpacket += 8; /* XXX magic number */
+ else
+#endif
+ minpacket += sizeof(*outudp);
+ packlen = minpacket; /* minimum sized packet */
+
+ /* Process destination and optional packet size */
+ switch (argc - optind) {
+
+ case 2:
+ packlen = xatoul_range(argv[optind + 1], minpacket, maxpacket);
+ /* Fall through */
+
+ case 1:
+ hostname = argv[optind];
+ hi = gethostinfo(hostname);
+ setsin(to, hi->addrs[0]);
+ if (hi->n > 1)
+ bb_error_msg("warning: %s has multiple addresses; using %s",
+ hostname, inet_ntoa(to->sin_addr));
+ hostname = hi->name;
+ hi->name = NULL;
+ freehostinfo(hi);
+ break;
+
+ default:
+ bb_show_usage();
+ }
+
+ /* Ensure the socket fds won't be 0, 1 or 2 */
+ bb_sanitize_stdio();
+
+ s = xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+
+#if TRACEROUTE_SO_DEBUG
+ if (op & OPT_DEBUG)
+ setsockopt(s, SOL_SOCKET, SO_DEBUG,
+ &const_int_1, sizeof(const_int_1));
+#endif
+ if (op & OPT_BYPASS_ROUTE)
+ setsockopt(s, SOL_SOCKET, SO_DONTROUTE,
+ &const_int_1, sizeof(const_int_1));
+
+ sndsock = xsocket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+#if defined(IP_OPTIONS)
+ if (lsrr > 0) {
+ unsigned char optlist[MAX_IPOPTLEN];
+
+ /* final hop */
+ gwlist[lsrr] = to->sin_addr.s_addr;
+ ++lsrr;
+
+ /* force 4 byte alignment */
+ optlist[0] = IPOPT_NOP;
+ /* loose source route option */
+ optlist[1] = IPOPT_LSRR;
+ i = lsrr * sizeof(gwlist[0]);
+ optlist[2] = i + 3;
+ /* Pointer to LSRR addresses */
+ optlist[3] = IPOPT_MINOFF;
+ memcpy(optlist + 4, gwlist, i);
+
+ if ((setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS,
+ (char *)optlist, i + sizeof(gwlist[0]))) < 0) {
+ bb_perror_msg_and_die("IP_OPTIONS");
+ }
+ }
+#endif /* IP_OPTIONS */
+#endif /* CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE */
+
+#ifdef SO_SNDBUF
+ if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, &packlen, sizeof(packlen)) < 0) {
+ bb_perror_msg_and_die("SO_SNDBUF");
+ }
+#endif
+#ifdef IP_HDRINCL
+ if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, &const_int_1, sizeof(const_int_1)) < 0
+ && errno != ENOPROTOOPT
+ ) {
+ bb_perror_msg_and_die("IP_HDRINCL");
+ }
+#else
+#ifdef IP_TOS
+ if (tos_str && setsockopt(sndsock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {
+ bb_perror_msg_and_die("setsockopt tos %d", tos);
+ }
+#endif
+#endif
+#if TRACEROUTE_SO_DEBUG
+ if (op & OPT_DEBUG)
+ setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
+ &const_int_1, sizeof(const_int_1));
+#endif
+ if (op & OPT_BYPASS_ROUTE)
+ setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
+ &const_int_1, sizeof(const_int_1));
+
+ /* Revert to non-privileged user after opening sockets */
+ xsetgid(getgid());
+ xsetuid(getuid());
+
+ outip = xzalloc(packlen);
+
+ outip->ip_v = IPVERSION;
+ if (tos_str)
+ outip->ip_tos = tos;
+ outip->ip_len = htons(packlen);
+ outip->ip_off = htons(off);
+ outp = (unsigned char *)(outip + 1);
+ outip->ip_dst = to->sin_addr;
+
+ outip->ip_hl = (outp - (unsigned char *)outip) >> 2;
+ ident = (getpid() & 0xffff) | 0x8000;
+#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
+ if (useicmp) {
+ outip->ip_p = IPPROTO_ICMP;
+ outicmp = (struct icmp *)outp;
+ outicmp->icmp_type = ICMP_ECHO;
+ outicmp->icmp_id = htons(ident);
+ outdata = (struct outdata *)(outp + 8); /* XXX magic number */
+ } else
+#endif
+ {
+ outip->ip_p = IPPROTO_UDP;
+ outudp = (struct udphdr *)outp;
+ outudp->source = htons(ident);
+ outudp->len = htons((uint16_t)(packlen - (sizeof(*outip) + optlen)));
+ outdata = (struct outdata *)(outudp + 1);
+ }
+
+ /* Get the interface address list */
+ n = ifaddrlist(&al);
+
+ /* Look for a specific device */
+ if (op & OPT_DEVICE) {
+ for (i = n; i > 0; --i, ++al)
+ if (strcmp(device, al->device) == 0)
+ goto found_dev;
+ bb_error_msg_and_die("can't find interface %s", device);
+ }
+ found_dev:
+
+ /* Determine our source address */
+ if (!(op & OPT_SOURCE)) {
+ /*
+ * If a device was specified, use the interface address.
+ * Otherwise, try to determine our source address.
+ */
+ if (op & OPT_DEVICE)
+ setsin(from, al->addr);
+ findsaddr(to, from);
+ } else {
+ hi = gethostinfo(source);
+ source = hi->name;
+ hi->name = NULL;
+ /*
+ * If the device was specified make sure it
+ * corresponds to the source address specified.
+ * Otherwise, use the first address (and warn if
+ * there are more than one).
+ */
+ if (op & OPT_DEVICE) {
+ for (i = hi->n, ap = hi->addrs; i > 0; --i, ++ap)
+ if (*ap == al->addr)
+ goto found_dev2;
+ bb_error_msg_and_die("%s is not on interface %s",
+ source, device);
+ found_dev2:
+ setsin(from, *ap);
+ } else {
+ setsin(from, hi->addrs[0]);
+ if (hi->n > 1)
+ bb_error_msg(
+ "warning: %s has multiple addresses; using %s",
+ source, inet_ntoa(from->sin_addr));
+ }
+ freehostinfo(hi);
+ }
+
+ outip->ip_src = from->sin_addr;
+#ifndef IP_HDRINCL
+ xbind(sndsock, (struct sockaddr *)from, sizeof(*from));
+#endif
+
+ printf("traceroute to %s (%s)", hostname, inet_ntoa(to->sin_addr));
+ if (op & OPT_SOURCE)
+ printf(" from %s", source);
+ printf(", %d hops max, %d byte packets\n", max_ttl, packlen);
+ fflush(stdout);
+
+ for (ttl = first_ttl; ttl <= max_ttl; ++ttl) {
+ uint32_t lastaddr = 0;
+ int gotlastaddr = 0;
+ int got_there = 0;
+ int unreachable = 0;
+ int sentfirst = 0;
+
+ printf("%2d ", ttl);
+ for (probe = 0; probe < nprobes; ++probe) {
+ int cc;
+ struct timeval t1, t2;
+ struct timezone tz;
+ struct ip *ip;
+
+ if (sentfirst && pausemsecs > 0)
+ usleep(pausemsecs * 1000);
+ (void)gettimeofday(&t1, &tz);
+ send_probe(++seq, ttl, &t1);
+ ++sentfirst;
+ while ((cc = wait_for_reply(s, from, &t1)) != 0) {
+ (void)gettimeofday(&t2, &tz);
+ i = packet_ok(packet, cc, from, seq);
+ /* Skip short packet */
+ if (i == 0)
+ continue;
+ if (!gotlastaddr ||
+ from->sin_addr.s_addr != lastaddr) {
+ print(packet, cc, from);
+ lastaddr = from->sin_addr.s_addr;
+ ++gotlastaddr;
+ }
+ printf(" %.3f ms", deltaT(&t1, &t2));
+ ip = (struct ip *)packet;
+ if (op & OPT_TTL_FLAG)
+ printf(" (%d)", ip->ip_ttl);
+ if (i == -2) {
+ if (ip->ip_ttl <= 1)
+ printf(" !");
+ ++got_there;
+ break;
+ }
+ /* time exceeded in transit */
+ if (i == -1)
+ break;
+ code = i - 1;
+ switch (code) {
+
+ case ICMP_UNREACH_PORT:
+ if (ip->ip_ttl <= 1)
+ printf(" !");
+ ++got_there;
+ break;
+
+ case ICMP_UNREACH_NET:
+ ++unreachable;
+ printf(" !N");
+ break;
+
+ case ICMP_UNREACH_HOST:
+ ++unreachable;
+ printf(" !H");
+ break;
+
+ case ICMP_UNREACH_PROTOCOL:
+ ++got_there;
+ printf(" !P");
+ break;
+
+ case ICMP_UNREACH_NEEDFRAG:
+ ++unreachable;
+ printf(" !F-%d", pmtu);
+ break;
+
+ case ICMP_UNREACH_SRCFAIL:
+ ++unreachable;
+ printf(" !S");
+ break;
+
+ case ICMP_UNREACH_FILTER_PROHIB:
+ case ICMP_UNREACH_NET_PROHIB: /* misuse */
+ ++unreachable;
+ printf(" !A");
+ break;
+
+ case ICMP_UNREACH_HOST_PROHIB:
+ ++unreachable;
+ printf(" !C");
+ break;
+
+ case ICMP_UNREACH_HOST_PRECEDENCE:
+ ++unreachable;
+ printf(" !V");
+ break;
+
+ case ICMP_UNREACH_PRECEDENCE_CUTOFF:
+ ++unreachable;
+ printf(" !C");
+ break;
+
+ case ICMP_UNREACH_NET_UNKNOWN:
+ case ICMP_UNREACH_HOST_UNKNOWN:
+ ++unreachable;
+ printf(" !U");
+ break;
+
+ case ICMP_UNREACH_ISOLATED:
+ ++unreachable;
+ printf(" !I");
+ break;
+
+ case ICMP_UNREACH_TOSNET:
+ case ICMP_UNREACH_TOSHOST:
+ ++unreachable;
+ printf(" !T");
+ break;
+
+ default:
+ ++unreachable;
+ printf(" !<%d>", code);
+ break;
+ }
+ break;
+ }
+ if (cc == 0)
+ printf(" *");
+ (void)fflush(stdout);
+ }
+ putchar('\n');
+ if (got_there ||
+ (unreachable > 0 && unreachable >= nprobes - 1))
+ break;
+ }
+ return 0;