sulogin: use bb_error_msg instead of bb_info_msg; better message
[oweals/busybox.git] / networking / traceroute.c
index 82bb0118c3d70f302ca01bb67fbbbf5ce741ab1a..eee4f8873a1230723cb05d2f23e896378742659d 100644 (file)
  *     Tue Dec 20 03:50:13 PST 1988
  */
 
-#define TRACEROUTE_SO_DEBUG 0
-
-/* TODO: undefs were uncommented - ??! we have config system for that! */
-/* probably ok to remove altogether */
-//#undef CONFIG_FEATURE_TRACEROUTE_VERBOSE
-//#define CONFIG_FEATURE_TRACEROUTE_VERBOSE
-//#undef CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE
-//#define CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE
-//#undef CONFIG_FEATURE_TRACEROUTE_USE_ICMP
-//#define CONFIG_FEATURE_TRACEROUTE_USE_ICMP
+//usage:#define traceroute_trivial_usage
+//usage:       "[-"IF_TRACEROUTE6("46")"FIlnrv] [-f 1ST_TTL] [-m MAXTTL] [-q PROBES] [-p PORT]\n"
+//usage:       "       [-t TOS] [-w WAIT_SEC]"
+//usage:       IF_FEATURE_TRACEROUTE_SOURCE_ROUTE(" [-g GATEWAY]")" [-s SRC_IP] [-i IFACE]\n"
+//usage:       "       [-z PAUSE_MSEC] HOST [BYTES]"
+//usage:#define traceroute_full_usage "\n\n"
+//usage:       "Trace the route to HOST\n"
+//usage:       IF_TRACEROUTE6(
+//usage:     "\n       -4,-6   Force IP or IPv6 name resolution"
+//usage:       )
+//usage:     "\n       -F      Set don't fragment bit"
+//usage:       IF_FEATURE_TRACEROUTE_USE_ICMP(
+//usage:     "\n       -I      Use ICMP ECHO instead of UDP datagrams"
+//usage:       )
+//usage:     "\n       -l      Display TTL value of the returned packet"
+//Currently disabled (TRACEROUTE_SO_DEBUG==0)
+////usage:     "\n     -d      Set SO_DEBUG options to socket"
+//usage:     "\n       -n      Print numeric addresses"
+//usage:     "\n       -r      Bypass routing tables, send directly to HOST"
+//usage:       IF_FEATURE_TRACEROUTE_VERBOSE(
+//usage:     "\n       -v      Verbose"
+//usage:       )
+//usage:     "\n       -f N    First number of hops (default 1)"
+//usage:     "\n       -m N    Max number of hops"
+//usage:     "\n       -q N    Number of probes per hop (default 3)"
+//usage:     "\n       -p N    Base UDP port number used in probes"
+//usage:     "\n               (default 33434)"
+//usage:     "\n       -s IP   Source address"
+//usage:     "\n       -i IFACE Source interface"
+//usage:     "\n       -t N    Type-of-service in probe packets (default 0)"
+//usage:     "\n       -w SEC  Time to wait for a response (default 3)"
+//usage:     "\n       -g IP   Loose source route gateway (8 max)"
+//usage:
+//usage:#define traceroute6_trivial_usage
+//usage:       "[-nrv] [-m MAXTTL] [-q PROBES] [-p PORT]\n"
+//usage:       "       [-t TOS] [-w WAIT_SEC] [-s SRC_IP] [-i IFACE]\n"
+//usage:       "       HOST [BYTES]"
+//usage:#define traceroute6_full_usage "\n\n"
+//usage:       "Trace the route to HOST\n"
+//Currently disabled (TRACEROUTE_SO_DEBUG==0)
+////usage:     "\n     -d      Set SO_DEBUG options to socket"
+//usage:     "\n       -n      Print numeric addresses"
+//usage:     "\n       -r      Bypass routing tables, send directly to HOST"
+//usage:       IF_FEATURE_TRACEROUTE_VERBOSE(
+//usage:     "\n       -v      Verbose"
+//usage:       )
+//usage:     "\n       -m N    Max number of hops"
+//usage:     "\n       -q N    Number of probes per hop (default 3)"
+//usage:     "\n       -p N    Base UDP port number used in probes"
+//usage:     "\n               (default 33434)"
+//usage:     "\n       -s IP   Source address"
+//usage:     "\n       -i IFACE Source interface"
+//usage:     "\n       -t N    Type-of-service in probe packets (default 0)"
+//usage:     "\n       -w SEC  Time wait for a response (default 3)"
 
+#define TRACEROUTE_SO_DEBUG 0
 
 #include <net/if.h>
 #include <arpa/inet.h>
 #endif
 
 
-#define OPT_STRING "FIlnrdvxt:i:m:p:q:s:w:z:f:" \
-                   IF_FEATURE_TRACEROUTE_SOURCE_ROUTE("g:") \
-                   "4" IF_TRACEROUTE6("6")
+#define OPT_STRING \
+       "FIlnrdvxt:i:m:p:q:s:w:z:f:" \
+       IF_FEATURE_TRACEROUTE_SOURCE_ROUTE("g:") \
+       "4" IF_TRACEROUTE6("6")
 enum {
        OPT_DONT_FRAGMNT = (1 << 0),    /* F */
        OPT_USE_ICMP     = (1 << 1) * ENABLE_FEATURE_TRACEROUTE_USE_ICMP, /* I */
@@ -343,66 +389,29 @@ struct globals {
 #define outudp  ((struct udphdr *)(outip + 1))
 
 
-/* libbb candidate? tftp uses this idiom too */
-static len_and_sockaddr* dup_sockaddr(const len_and_sockaddr *lsa)
-{
-       len_and_sockaddr *new_lsa = xzalloc(LSA_LEN_SIZE + lsa->len);
-       memcpy(new_lsa, lsa, LSA_LEN_SIZE + lsa->len);
-       return new_lsa;
-}
-
-
 static int
-wait_for_reply(len_and_sockaddr *from_lsa, struct sockaddr *to)
+wait_for_reply(len_and_sockaddr *from_lsa, struct sockaddr *to, unsigned *timestamp_us, int *left_ms)
 {
        struct pollfd pfd[1];
        int read_len = 0;
 
        pfd[0].fd = rcvsock;
        pfd[0].events = POLLIN;
-       if (safe_poll(pfd, 1, waittime * 1000) > 0) {
+       if (*left_ms >= 0 && safe_poll(pfd, 1, *left_ms) > 0) {
+               unsigned t;
+
                read_len = recv_from_to(rcvsock,
                                recv_pkt, sizeof(recv_pkt),
-                               /*flags:*/ 0,
+                               /*flags:*/ MSG_DONTWAIT,
                                &from_lsa->u.sa, to, from_lsa->len);
+               t = monotonic_us();
+               *left_ms -= (t - *timestamp_us) / 1000;
+               *timestamp_us = t;
        }
 
        return read_len;
 }
 
-/*
- * Checksum routine for Internet Protocol family headers (C Version)
- */
-static uint16_t
-in_cksum(uint16_t *addr, int len)
-{
-       int nleft = len;
-       uint16_t *w = addr;
-       uint16_t answer;
-       int sum = 0;
-
-       /*
-        * Our algorithm is simple, using a 32 bit accumulator (sum),
-        * we add sequential 16 bit words to it, and at the end, fold
-        * back all the carry bits from the top 16 bits into the lower
-        * 16 bits.
-        */
-       while (nleft > 1) {
-               sum += *w++;
-               nleft -= 2;
-       }
-
-       /* mop up an odd byte, if necessary */
-       if (nleft == 1)
-               sum += *(unsigned char *)w;
-
-       /* add back carry outs from top 16 bits to low 16 bits */
-       sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
-       sum += (sum >> 16);                     /* add carry */
-       answer = ~sum;                          /* truncate to 16 bits */
-       return answer;
-}
-
 static void
 send_probe(int seq, int ttl)
 {
@@ -429,7 +438,7 @@ send_probe(int seq, int ttl)
 
                        /* Always calculate checksum for icmp packets */
                        outicmp->icmp_cksum = 0;
-                       outicmp->icmp_cksum = in_cksum((uint16_t *)outicmp,
+                       outicmp->icmp_cksum = inet_cksum((uint16_t *)outicmp,
                                                packlen - (sizeof(*outip) + optlen));
                        if (outicmp->icmp_cksum == 0)
                                outicmp->icmp_cksum = 0xffff;
@@ -464,31 +473,31 @@ send_probe(int seq, int ttl)
 
 #if ENABLE_TRACEROUTE6
        if (dest_lsa->u.sa.sa_family == AF_INET6) {
-               res = setsockopt(sndsock, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
-               if (res < 0)
-                       bb_perror_msg_and_die("setsockopt UNICAST_HOPS %d", ttl);
+               res = setsockopt_int(sndsock, SOL_IPV6, IPV6_UNICAST_HOPS, ttl);
+               if (res != 0)
+                       bb_perror_msg_and_die("setsockopt(%s) %d", "UNICAST_HOPS", ttl);
                out = outip;
                len = packlen;
        } else
 #endif
        {
 #if defined IP_TTL
-               res = setsockopt(sndsock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
-               if (res < 0)
-                       bb_perror_msg_and_die("setsockopt ttl %d", ttl);
+               res = setsockopt_int(sndsock, IPPROTO_IP, IP_TTL, ttl);
+               if (res != 0)
+                       bb_perror_msg_and_die("setsockopt(%s) %d", "TTL", ttl);
 #endif
                out = outicmp;
                len = packlen - sizeof(*outip);
                if (!(option_mask32 & OPT_USE_ICMP)) {
                        out = outdata;
                        len -= sizeof(*outudp);
-                       set_nport(dest_lsa, htons(port + seq));
+                       set_nport(&dest_lsa->u.sa, htons(port + seq));
                }
        }
 
        res = xsendto(sndsock, out, len, &dest_lsa->u.sa, dest_lsa->len);
        if (res != len)
-               bb_info_msg("sent %d octets, ret=%d", len, res);
+               bb_error_msg("sent %d octets, ret=%d", len, res);
 }
 
 #if ENABLE_FEATURE_TRACEROUTE_VERBOSE
@@ -685,7 +694,7 @@ packet_ok(int read_len, len_and_sockaddr *from_lsa,
                        type, pr_type(type), icp->icmp6_code);
 
                read_len -= sizeof(struct icmp6_hdr);
-               for (i = 0; i < read_len ; i++) {
+               for (i = 0; i < read_len; i++) {
                        if (i % 16 == 0)
                                printf("%04x:", i);
                        if (i % 4 == 0)
@@ -774,9 +783,10 @@ print_delta_ms(unsigned t1p, unsigned t2p)
 static int
 common_traceroute_main(int op, char **argv)
 {
-       int i;
        int minpacket;
+#ifdef IP_TOS
        int tos = 0;
+#endif
        int max_ttl = 30;
        int nprobes = 3;
        int first_ttl = 1;
@@ -790,6 +800,7 @@ common_traceroute_main(int op, char **argv)
        char *waittime_str;
        char *pausemsecs_str;
        char *first_ttl_str;
+       char *dest_str;
 #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
        llist_t *source_route_list = NULL;
        int lsrr = 0;
@@ -822,8 +833,10 @@ common_traceroute_main(int op, char **argv)
        if (op & OPT_IP_CHKSUM)
                bb_error_msg("warning: ip checksums disabled");
 #endif
+#ifdef IP_TOS
        if (op & OPT_TOS)
                tos = xatou_range(tos_str, 0, 255);
+#endif
        if (op & OPT_MAX_TTL)
                max_ttl = xatou_range(max_ttl_str, 1, 255);
        if (op & OPT_PORT)
@@ -889,13 +902,10 @@ common_traceroute_main(int op, char **argv)
        if (af == AF_INET6) {
                xmove_fd(xsocket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6), rcvsock);
 # ifdef IPV6_RECVPKTINFO
-               setsockopt(rcvsock, SOL_IPV6, IPV6_RECVPKTINFO,
-                               &const_int_1, sizeof(const_int_1));
-               setsockopt(rcvsock, SOL_IPV6, IPV6_2292PKTINFO,
-                               &const_int_1, sizeof(const_int_1));
+               setsockopt_1(rcvsock, SOL_IPV6, IPV6_RECVPKTINFO);
+               setsockopt_1(rcvsock, SOL_IPV6, IPV6_2292PKTINFO);
 # else
-               setsockopt(rcvsock, SOL_IPV6, IPV6_PKTINFO,
-                               &const_int_1, sizeof(const_int_1));
+               setsockopt_1(rcvsock, SOL_IPV6, IPV6_PKTINFO);
 # endif
        } else
 #endif
@@ -905,18 +915,15 @@ common_traceroute_main(int op, char **argv)
 
 #if TRACEROUTE_SO_DEBUG
        if (op & OPT_DEBUG)
-               setsockopt(rcvsock, SOL_SOCKET, SO_DEBUG,
-                               &const_int_1, sizeof(const_int_1));
+               setsockopt_SOL_SOCKET_1(rcvsock, SO_DEBUG);
 #endif
        if (op & OPT_BYPASS_ROUTE)
-               setsockopt(rcvsock, SOL_SOCKET, SO_DONTROUTE,
-                               &const_int_1, sizeof(const_int_1));
+               setsockopt_SOL_SOCKET_1(rcvsock, SO_DONTROUTE);
 
 #if ENABLE_TRACEROUTE6
        if (af == AF_INET6) {
-               static const int two = 2;
-               if (setsockopt(rcvsock, SOL_RAW, IPV6_CHECKSUM, &two, sizeof(two)) < 0)
-                       bb_perror_msg_and_die("setsockopt RAW_CHECKSUM");
+               if (setsockopt_int(rcvsock, SOL_RAW, IPV6_CHECKSUM, 2) != 0)
+                       bb_perror_msg_and_die("setsockopt(%s)", "IPV6_CHECKSUM");
                xmove_fd(xsocket(af, SOCK_DGRAM, 0), sndsock);
        } else
 #endif
@@ -928,6 +935,7 @@ common_traceroute_main(int op, char **argv)
 #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE && defined IP_OPTIONS
                if (lsrr > 0) {
                        unsigned char optlist[MAX_IPOPTLEN];
+                       unsigned size;
 
                        /* final hop */
                        gwlist[lsrr] = dest_lsa->u.sin.sin_addr.s_addr;
@@ -937,14 +945,14 @@ common_traceroute_main(int op, char **argv)
                        optlist[0] = IPOPT_NOP;
                        /* loose source route option */
                        optlist[1] = IPOPT_LSRR;
-                       i = lsrr * sizeof(gwlist[0]);
-                       optlist[2] = i + 3;
+                       size = lsrr * sizeof(gwlist[0]);
+                       optlist[2] = size + 3;
                        /* pointer to LSRR addresses */
                        optlist[3] = IPOPT_MINOFF;
-                       memcpy(optlist + 4, gwlist, i);
+                       memcpy(optlist + 4, gwlist, size);
 
                        if (setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS,
-                                       (char *)optlist, i + sizeof(gwlist[0])) < 0) {
+                                       (char *)optlist, size + sizeof(gwlist[0])) < 0) {
                                bb_perror_msg_and_die("IP_OPTIONS");
                        }
                }
@@ -952,28 +960,25 @@ common_traceroute_main(int op, char **argv)
        }
 
 #ifdef SO_SNDBUF
-       if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, &packlen, sizeof(packlen)) < 0) {
-               bb_perror_msg_and_die("SO_SNDBUF");
+       if (setsockopt_SOL_SOCKET_int(sndsock, SO_SNDBUF, packlen) != 0) {
+               bb_perror_msg_and_die("setsockopt(%s)", "SO_SNDBUF");
        }
 #endif
 #ifdef IP_TOS
-       if ((op & OPT_TOS) && setsockopt(sndsock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {
-               bb_perror_msg_and_die("setsockopt tos %d", tos);
+       if ((op & OPT_TOS) && setsockopt_int(sndsock, IPPROTO_IP, IP_TOS, tos) != 0) {
+               bb_perror_msg_and_die("setsockopt(%s) %d", "TOS", tos);
        }
 #endif
 #ifdef IP_DONTFRAG
        if (op & OPT_DONT_FRAGMNT)
-               setsockopt(sndsock, IPPROTO_IP, IP_DONTFRAG,
-                               &const_int_1, sizeof(const_int_1));
+               setsockopt_1(sndsock, IPPROTO_IP, IP_DONTFRAG);
 #endif
 #if TRACEROUTE_SO_DEBUG
        if (op & OPT_DEBUG)
-               setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
-                               &const_int_1, sizeof(const_int_1));
+               setsockopt_SOL_SOCKET_1(sndsock, SO_DEBUG);
 #endif
        if (op & OPT_BYPASS_ROUTE)
-               setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
-                               &const_int_1, sizeof(const_int_1));
+               setsockopt_SOL_SOCKET_1(sndsock, SO_DONTROUTE);
 
        outip = xzalloc(packlen);
 
@@ -1018,10 +1023,10 @@ common_traceroute_main(int op, char **argv)
                int probe_fd = xsocket(af, SOCK_DGRAM, 0);
                if (op & OPT_DEVICE)
                        setsockopt_bindtodevice(probe_fd, device);
-               set_nport(dest_lsa, htons(1025));
+               set_nport(&dest_lsa->u.sa, htons(1025));
                /* dummy connect. makes kernel pick source IP (and port) */
                xconnect(probe_fd, &dest_lsa->u.sa, dest_lsa->len);
-               set_nport(dest_lsa, htons(port));
+               set_nport(&dest_lsa->u.sa, htons(port));
 
                /* read IP and port */
                source_lsa = get_sock_lsa(probe_fd);
@@ -1031,7 +1036,7 @@ common_traceroute_main(int op, char **argv)
                close(probe_fd);
 
                /* bind our sockets to this IP (but not port) */
-               set_nport(source_lsa, 0);
+               set_nport(&source_lsa->u.sa, 0);
                xbind(sndsock, &source_lsa->u.sa, source_lsa->len);
                xbind(rcvsock, &source_lsa->u.sa, source_lsa->len);
 
@@ -1043,13 +1048,17 @@ common_traceroute_main(int op, char **argv)
        xsetgid(getgid());
        xsetuid(getuid());
 
-       printf("traceroute to %s (%s)", argv[0],
-                       xmalloc_sockaddr2dotted_noport(&dest_lsa->u.sa));
+       dest_str = xmalloc_sockaddr2dotted_noport(&dest_lsa->u.sa);
+       printf("traceroute to %s (%s)", argv[0], dest_str);
+       if (ENABLE_FEATURE_CLEAN_UP) {
+               free(dest_str);
+       }
+
        if (op & OPT_SOURCE)
                printf(" from %s", source);
        printf(", %d hops max, %d byte packets\n", max_ttl, packlen);
 
-       from_lsa = dup_sockaddr(dest_lsa);
+       from_lsa = xmemdup(dest_lsa, LSA_LEN_SIZE + dest_lsa->len);
        lastaddr = xzalloc(dest_lsa->len);
        to = xzalloc(dest_lsa->len);
        seq = 0;
@@ -1058,28 +1067,34 @@ common_traceroute_main(int op, char **argv)
                int unreachable = 0; /* counter */
                int gotlastaddr = 0; /* flags */
                int got_there = 0;
-               int first = 1;
 
                printf("%2d", ttl);
                for (probe = 0; probe < nprobes; ++probe) {
                        int read_len;
                        unsigned t1;
                        unsigned t2;
+                       int left_ms;
                        struct ip *ip;
 
-                       if (!first && pausemsecs > 0)
-                               usleep(pausemsecs * 1000);
                        fflush_all();
+                       if (probe != 0 && pausemsecs > 0)
+                               usleep(pausemsecs * 1000);
 
-                       t1 = monotonic_us();
                        send_probe(++seq, ttl);
+                       t2 = t1 = monotonic_us();
+
+                       left_ms = waittime * 1000;
+                       while ((read_len = wait_for_reply(from_lsa, to, &t2, &left_ms)) != 0) {
+                               int icmp_code;
 
-                       first = 0;
-                       while ((read_len = wait_for_reply(from_lsa, to)) != 0) {
-                               t2 = monotonic_us();
-                               i = packet_ok(read_len, from_lsa, to, seq);
+                               /* Recv'ed a packet, or read error */
+                               /* t2 = monotonic_us() - set by wait_for_reply */
+
+                               if (read_len < 0)
+                                       continue;
+                               icmp_code = packet_ok(read_len, from_lsa, to, seq);
                                /* Skip short packet */
-                               if (i == 0)
+                               if (icmp_code == 0)
                                        continue;
 
                                if (!gotlastaddr
@@ -1098,10 +1113,10 @@ common_traceroute_main(int op, char **argv)
                                                printf(" (%d)", ip->ip_ttl);
 
                                /* time exceeded in transit */
-                               if (i == -1)
+                               if (icmp_code == -1)
                                        break;
-                               i--;
-                               switch (i) {
+                               icmp_code--;
+                               switch (icmp_code) {
 #if ENABLE_TRACEROUTE6
                                case ICMP6_DST_UNREACH_NOPORT << 8:
                                        got_there = 1;
@@ -1174,16 +1189,18 @@ common_traceroute_main(int op, char **argv)
                                        ++unreachable;
                                        break;
                                default:
-                                       printf(" !<%d>", i);
+                                       printf(" !<%d>", icmp_code);
                                        ++unreachable;
                                        break;
                                }
                                break;
-                       }
+                       } /* while (wait and read a packet) */
+
                        /* there was no packet at all? */
                        if (read_len == 0)
                                printf("  *");
-               }
+               } /* for (nprobes) */
+
                bb_putchar('\n');
                if (got_there
                 || (unreachable > 0 && unreachable >= nprobes - 1)
@@ -1192,6 +1209,12 @@ common_traceroute_main(int op, char **argv)
                }
        }
 
+       if (ENABLE_FEATURE_CLEAN_UP) {
+               free(to);
+               free(lastaddr);
+               free(from_lsa);
+       }
+
        return 0;
 }