traceroute: rewrite. Do not emit raw IP packets, instead send UDP or ICMP
authorDenis Vlasenko <vda.linux@googlemail.com>
Sat, 24 Jan 2009 20:14:24 +0000 (20:14 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Sat, 24 Jan 2009 20:14:24 +0000 (20:14 -0000)
 packets and rely on the kernel to form IP headers, select source IP
 and interface. Doing it in traceroute wasn't working too good,
 and was bloating it.

function                                             old     new   delta
freehostinfo                                          29       -     -29
setsin                                                30       -     -30
in_cksum                                              57       -     -57
gethostinfo                                          165       -    -165
ifaddrlist                                           364       -    -364
traceroute_main                                     3713    2951    -762
------------------------------------------------------------------------------
(add/remove: 0/5 grow/shrink: 0/1 up/down: 0/-1407)         Total: -1407 bytes

networking/traceroute.c

index 958299aaa3943d2f4fbd6949435a4d73f6af80b9..244a74d6d28b0f7238bb5299da99719e0b932a61 100644 (file)
 #include "libbb.h"
 #include "inet_common.h"
 
-
-/*
- * Definitions for internet protocol version 4.
- * Per RFC 791, September 1981.
- */
-#define IPVERSION 4
-
 #ifndef IPPROTO_ICMP
 # define IPPROTO_ICMP 1
 #endif
 # define IPPROTO_IP 0
 #endif
 
-/*
- * Overlay for ip header used by other protocols (tcp, udp).
- */
-struct ipovly {
-       unsigned char  ih_x1[9];               /* (unused) */
-       unsigned char  ih_pr;                  /* protocol */
-       short   ih_len;                 /* protocol length */
-       struct  in_addr ih_src;         /* source internet address */
-       struct  in_addr ih_dst;         /* destination internet address */
+/* Keep in sync with getopt32 call! */
+enum {
+       OPT_DONT_FRAGMNT = (1 << 0),    /* F */
+       OPT_USE_ICMP     = (1 << 1) * ENABLE_FEATURE_TRACEROUTE_USE_ICMP, /* I */
+       OPT_TTL_FLAG     = (1 << 2),    /* l */
+       OPT_ADDR_NUM     = (1 << 3),    /* n */
+       OPT_BYPASS_ROUTE = (1 << 4),    /* r */
+       OPT_DEBUG        = (1 << 5),    /* d */
+       OPT_VERBOSE      = (1 << 6) * ENABLE_FEATURE_TRACEROUTE_VERBOSE, /* v */
+       OPT_IP_CHKSUM    = (1 << 7),    /* x */
+       OPT_TOS          = (1 << 8),    /* t */
+       OPT_DEVICE       = (1 << 9),    /* i */
+       OPT_MAX_TTL      = (1 << 10),   /* m */
+       OPT_PORT         = (1 << 11),   /* p */
+       OPT_NPROBES      = (1 << 12),   /* q */
+       OPT_SOURCE       = (1 << 13),   /* s */
+       OPT_WAITTIME     = (1 << 14),   /* w */
+       OPT_PAUSE_MS     = (1 << 15),   /* z */
+       OPT_FIRST_TTL    = (1 << 16),   /* f */
 };
+#define verbose (option_mask32 & OPT_VERBOSE)
 
-/*
- * UDP kernel structures and variables.
- */
-struct udpiphdr {
-       struct  ipovly ui_i;            /* overlaid ip structure */
-       struct  udphdr ui_u;            /* udp header */
-};
-#define ui_next         ui_i.ih_next
-#define ui_prev         ui_i.ih_prev
-#define ui_x1           ui_i.ih_x1
-#define ui_pr           ui_i.ih_pr
-#define ui_len          ui_i.ih_len
-#define ui_src          ui_i.ih_src
-#define ui_dst          ui_i.ih_dst
-#define ui_sport        ui_u.uh_sport
-#define ui_dport        ui_u.uh_dport
-#define ui_ulen         ui_u.uh_ulen
-#define ui_sum          ui_u.uh_sum
-
-
-/* Host name and address list */
-struct hostinfo {
-       char *name;
-       int n;
-       uint32_t *addrs;
+enum {
+       SIZEOF_ICMP_HDR = 8,
+       rcvsock = 3, /* receive (icmp) socket file descriptor */
+       sndsock = 4, /* send (udp/icmp) socket file descriptor */
 };
 
 /* Data section of the probe packet */
-typedef struct outdata {
+struct outdata_t {
        unsigned char seq;             /* sequence number of this packet */
        unsigned char ttl;             /* ttl packet left with */
 // UNUSED. Retaining to have the same packet size.
        struct timeval tv_UNUSED PACKED; /* time packet left */
-} outdata_t;
-
-struct IFADDRLIST {
-       uint32_t addr;
-       char device[sizeof(struct ifreq)];
 };
 
-
-/* Keep in sync with getopt32 call! */
-#define OPT_DONT_FRAGMNT (1<<0)    /* F */
-#define OPT_USE_ICMP     (1<<1)    /* I */
-#define OPT_TTL_FLAG     (1<<2)    /* l */
-#define OPT_ADDR_NUM     (1<<3)    /* n */
-#define OPT_BYPASS_ROUTE (1<<4)    /* r */
-#define OPT_DEBUG        (1<<5)    /* d */
-#define OPT_VERBOSE      (1<<6)    /* v */
-#define OPT_IP_CHKSUM    (1<<7)    /* x */
-#define OPT_TOS          (1<<8)    /* t */
-#define OPT_DEVICE       (1<<9)    /* i */
-#define OPT_MAX_TTL      (1<<10)   /* m */
-#define OPT_PORT         (1<<11)   /* p */
-#define OPT_NPROBES      (1<<12)   /* q */
-#define OPT_SOURCE       (1<<13)   /* s */
-#define OPT_WAITTIME     (1<<14)   /* w */
-#define OPT_PAUSE_MS     (1<<15)   /* z */
-#define OPT_FIRST_TTL    (1<<16)   /* f */
-
-#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
-/* use icmp echo instead of udp packets */
-#define useicmp (option_mask32 & OPT_USE_ICMP)
-#endif
-#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
-#define verbose (option_mask32 & OPT_VERBOSE)
-#endif
-#define nflag   (option_mask32 & OPT_ADDR_NUM)
-
-
 struct globals {
-       struct ip *outip;               /* last output (udp) packet */
-       struct udphdr *outudp;          /* last output (udp) packet */
-       struct outdata *outdata;        /* last output (udp) packet */
-
-#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
-       struct icmp *outicmp;           /* last output (icmp) packet */
-#endif
-
-       int rcvsock;                    /* receive (icmp) socket file descriptor */
-       int sndsock;                    /* send (udp/icmp) socket file descriptor */
-
+       struct ip *outip;
+       struct outdata_t *outdata;
+       len_and_sockaddr *dest_lsa;
        int packlen;                    /* total length of packet */
-       int minpacket;                  /* min ip packet size */
-       int maxpacket; // 32 * 1024;    /* max ip packet size */
        int pmtu;                       /* Path MTU Discovery (RFC1191) */
-
-       char *hostname;
-
        uint16_t ident;
        uint16_t port; // 32768 + 666;  /* start udp dest port # for probe packets */
-
        int waittime; // 5;             /* time to wait for response (in seconds) */
-       int doipcksum; // 1;            /* calculate ip checksums by default */
-
 #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
        int optlen;                     /* length of ip options */
 #else
 #define optlen 0
 #endif
-
-       struct sockaddr_storage whereto;        /* Who to try to reach */
-       struct sockaddr_storage wherefrom;      /* Who we are */
-       /* last inbound (icmp) packet */
-       unsigned char packet[512];
+       unsigned char recv_pkt[512];    /* last inbound (icmp) packet */
 #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
        /* Maximum number of gateways (include room for one noop) */
 #define NGATEWAYS ((int)((MAX_IPOPTLEN - IPOPT_MINOFF - 1) / sizeof(uint32_t)))
@@ -355,206 +283,39 @@ struct globals {
 
 #define G (*ptr_to_globals)
 #define outip     (G.outip    )
-#define outudp    (G.outudp   )
 #define outdata   (G.outdata  )
-#define outicmp   (G.outicmp  )
-#define rcvsock   (G.rcvsock  )
-#define sndsock   (G.sndsock  )
+#define dest_lsa  (G.dest_lsa )
 #define packlen   (G.packlen  )
-#define minpacket (G.minpacket)
-#define maxpacket (G.maxpacket)
 #define pmtu      (G.pmtu     )
-#define hostname  (G.hostname )
 #define ident     (G.ident    )
 #define port      (G.port     )
 #define waittime  (G.waittime )
-#define doipcksum (G.doipcksum)
 #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
-#define optlen    (G.optlen   )
+# define optlen   (G.optlen   )
 #endif
-#define packet    (G.packet   )
-#define whereto   (G.whereto  )
-#define wherefrom (G.wherefrom)
+#define recv_pkt  (G.recv_pkt )
 #define gwlist    (G.gwlist   )
 #define INIT_G() do { \
        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
-       maxpacket = 32 * 1024; \
        port = 32768 + 666; \
        waittime = 5; \
-       doipcksum = 1; \
 } while (0)
 
+#define outicmp ((struct icmp *)(outip + 1))
+#define outudp  ((struct udphdr *)(outip + 1))
 
-/*
- * Return the interface list
- */
-static int
-ifaddrlist(struct IFADDRLIST **ipaddrp)
-{
-       enum { IFREQ_BUFSIZE = (32 * 1024) / sizeof(struct ifreq) };
-
-       int fd, nipaddr;
-#ifdef HAVE_SOCKADDR_SA_LEN
-       int n;
-#endif
-       struct ifreq *ifrp, *ifend, *ifnext;
-       struct sockaddr_in *addr_sin;
-       struct IFADDRLIST *al;
-       struct ifconf ifc;
-       struct ifreq ifr;
-       /* Was on stack, but 32k is a bit too much: */
-       struct ifreq *ibuf = xmalloc(IFREQ_BUFSIZE * sizeof(ibuf[0]));
-       struct IFADDRLIST *st_ifaddrlist;
-
-       fd = xsocket(AF_INET, SOCK_DGRAM, 0);
-
-       ifc.ifc_len = IFREQ_BUFSIZE * sizeof(ibuf[0]);
-       ifc.ifc_buf = (caddr_t)ibuf;
-
-       if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0
-        || ifc.ifc_len < (int)sizeof(struct ifreq)
-       ) {
-               if (errno == EINVAL)
-                       bb_error_msg_and_die(
-                           "SIOCGIFCONF: ifreq struct too small (%u bytes)",
-                           (unsigned)(IFREQ_BUFSIZE * sizeof(ibuf[0])));
-               bb_perror_msg_and_die("SIOCGIFCONF");
-       }
-       ifrp = ibuf;
-       ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len);
-
-       nipaddr = 1 + (ifc.ifc_len / sizeof(struct ifreq));
-       st_ifaddrlist = xzalloc(nipaddr * sizeof(struct IFADDRLIST));
-       al = st_ifaddrlist;
-       nipaddr = 0;
-
-       for (; ifrp < ifend; ifrp = ifnext) {
-#ifdef HAVE_SOCKADDR_SA_LEN
-               n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
-               if (n < sizeof(*ifrp))
-                       ifnext = ifrp + 1;
-               else
-                       ifnext = (struct ifreq *)((char *)ifrp + n);
-               if (ifrp->ifr_addr.sa_family != AF_INET)
-                       continue;
-#else
-               ifnext = ifrp + 1;
-#endif
-               /*
-                * Need a template to preserve address info that is
-                * used below to locate the next entry.  (Otherwise,
-                * SIOCGIFFLAGS stomps over it because the requests
-                * are returned in a union.)
-                */
-               strncpy_IFNAMSIZ(ifr.ifr_name, ifrp->ifr_name);
-               if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) < 0) {
-                       if (errno == ENXIO)
-                               continue;
-                       bb_perror_msg_and_die("SIOCGIFFLAGS: %.*s",
-                           (int)sizeof(ifr.ifr_name), ifr.ifr_name);
-               }
-
-               /* Must be up */
-               if ((ifr.ifr_flags & IFF_UP) == 0)
-                       continue;
-
-               safe_strncpy(al->device, ifr.ifr_name, sizeof(ifr.ifr_name) + 1);
-#ifdef sun
-               /* Ignore sun virtual interfaces */
-               if (strchr(al->device, ':') != NULL)
-                       continue;
-#endif
-               ioctl_or_perror_and_die(fd, SIOCGIFADDR, (char *)&ifr,
-                               "SIOCGIFADDR: %s", al->device);
-
-               addr_sin = (struct sockaddr_in *)&ifr.ifr_addr;
-               al->addr = addr_sin->sin_addr.s_addr;
-               ++al;
-               ++nipaddr;
-       }
-       if (nipaddr == 0)
-               bb_error_msg_and_die("can't find any network interfaces");
-
-       free(ibuf);
-       close(fd);
-       *ipaddrp = st_ifaddrlist;
-       return nipaddr;
-}
-
-static void
-setsin(struct sockaddr_in *addr_sin, uint32_t addr)
-{
-       memset(addr_sin, 0, sizeof(*addr_sin));
-#ifdef HAVE_SOCKADDR_SA_LEN
-       addr_sin->sin_len = sizeof(*addr_sin);
-#endif
-       addr_sin->sin_family = AF_INET;
-       addr_sin->sin_addr.s_addr = addr;
-}
-
-/*
- * Return the source address for the given destination address
- */
-static void
-findsaddr(const struct sockaddr_in *to, struct sockaddr_in *from)
-{
-       int i, n;
-       FILE *f;
-       uint32_t mask;
-       uint32_t dest, tmask;
-       struct IFADDRLIST *al;
-       char buf[256], tdevice[256], device[256];
-
-       f = xfopen_for_read("/proc/net/route");
-
-       /* Find the appropriate interface */
-       n = 0;
-       mask = 0;
-       device[0] = '\0';
-       while (fgets(buf, sizeof(buf), f) != NULL) {
-               ++n;
-               if (n == 1 && strncmp(buf, "Iface", 5) == 0)
-                       continue;
-               i = sscanf(buf, "%255s %x %*s %*s %*s %*s %*s %x",
-                                       tdevice, &dest, &tmask);
-               if (i != 3)
-                       bb_error_msg_and_die("junk in buffer");
-               if ((to->sin_addr.s_addr & tmask) == dest
-                && (tmask > mask || mask == 0)
-               ) {
-                       mask = tmask;
-                       strcpy(device, tdevice);
-               }
-       }
-       fclose(f);
-
-       if (device[0] == '\0')
-               bb_error_msg_and_die("can't find interface");
-
-       /* Get the interface address list */
-       n = ifaddrlist(&al);
-
-       /* Find our appropriate source address */
-       for (i = n; i > 0; --i, ++al)
-               if (strcmp(device, al->device) == 0)
-                       break;
-       if (i <= 0)
-               bb_error_msg_and_die("can't find interface %s", device);
-
-       setsin(from, al->addr);
-}
 
 static int
-wait_for_reply(int sock, struct sockaddr_in *fromp)
+wait_for_reply(struct sockaddr_in *fromp)
 {
        struct pollfd pfd[1];
        int cc = 0;
        socklen_t fromlen = sizeof(*fromp);
 
-       pfd[0].fd = sock;
+       pfd[0].fd = rcvsock;
        pfd[0].events = POLLIN;
        if (safe_poll(pfd, 1, waittime * 1000) > 0)
-               cc = recvfrom(sock, packet, sizeof(packet), 0,
+               cc = recvfrom(rcvsock, recv_pkt, sizeof(recv_pkt), 0,
                            (struct sockaddr *)fromp, &fromlen);
        return cc;
 }
@@ -595,22 +356,8 @@ in_cksum(uint16_t *addr, int len)
 static void
 send_probe(int seq, int ttl)
 {
-       int cc;
-       struct udpiphdr *ui, *oui;
-       struct ip tip;
-
-       outip->ip_ttl = ttl;
-       outip->ip_id = htons(ident + seq);
-
-       /*
-        * In most cases, the kernel will recalculate the ip checksum.
-        * But we must do it anyway so that the udp checksum comes out right.
-        */
-       if (doipcksum) {
-               outip->ip_sum = in_cksum((uint16_t *)outip, sizeof(*outip) + optlen);
-               if (outip->ip_sum == 0)
-                       outip->ip_sum = 0xffff;
-       }
+       int len, res;
+       void *out;
 
        /* Payload */
        outdata->seq = seq;
@@ -618,39 +365,15 @@ send_probe(int seq, int ttl)
 // UNUSED: was storing gettimeofday's result there, but never ever checked it
        /*memcpy(&outdata->tv, tp, sizeof(outdata->tv));*/
 
-#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
-       if (useicmp)
+       if (option_mask32 & OPT_USE_ICMP) {
                outicmp->icmp_seq = htons(seq);
-       else
-#endif
-               outudp->dest = htons(port + seq);
 
-#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
-       if (useicmp) {
                /* Always calculate checksum for icmp packets */
                outicmp->icmp_cksum = 0;
                outicmp->icmp_cksum = in_cksum((uint16_t *)outicmp,
                                        packlen - (sizeof(*outip) + optlen));
                if (outicmp->icmp_cksum == 0)
                        outicmp->icmp_cksum = 0xffff;
-       } else
-#endif
-       if (doipcksum) {
-               /* Checksum (we must save and restore ip header) */
-               tip = *outip;
-               ui = (struct udpiphdr *)outip;
-               oui = (struct udpiphdr *)&tip;
-               /* Easier to zero and put back things that are ok */
-               memset(ui, 0, sizeof(ui->ui_i));
-               ui->ui_src = oui->ui_src;
-               ui->ui_dst = oui->ui_dst;
-               ui->ui_pr = oui->ui_pr;
-               ui->ui_len = outudp->len;
-               outudp->check = 0;
-               outudp->check = in_cksum((uint16_t *)ui, packlen);
-               if (outudp->check == 0)
-                       outudp->check = 0xffff;
-               *outip = tip;
        }
 
 //BUG! verbose is (x & OPT_VERBOSE), not a counter!
@@ -679,17 +402,25 @@ send_probe(int seq, int ttl)
        }
 #endif
 
-#if !defined(IP_HDRINCL) && defined(IP_TTL)
+#if defined(IP_TTL)
        if (setsockopt(sndsock, IPPROTO_IP, IP_TTL,
                                (char *)&ttl, sizeof(ttl)) < 0) {
                bb_perror_msg_and_die("setsockopt ttl %d", ttl);
        }
 #endif
 
-       cc = xsendto(sndsock, outip, packlen,
-                       (struct sockaddr *)&whereto, sizeof(whereto));
-       if (cc != packlen) {
-               bb_info_msg("sent %s %d octets, ret=%d", hostname, packlen, cc);
+       len = packlen - sizeof(*outip);
+       if (option_mask32 & OPT_USE_ICMP)
+               out = outicmp;
+       else {
+               out = outdata;
+               len -= sizeof(*outudp);
+               set_nport(dest_lsa, htons(port + seq));
+       }
+       res = xsendto(sndsock, out, len,
+                       (struct sockaddr *)&dest_lsa->u.sa, dest_lsa->len);
+       if (res != len) {
+               bb_info_msg("sent %d octets, ret=%d", len, res);
        }
 }
 
@@ -716,18 +447,18 @@ pr_type(unsigned char t)
 #endif
 
 #if !ENABLE_FEATURE_TRACEROUTE_VERBOSE
-#define packet_ok(buf, cc, from, seq) \
-       packet_ok(buf, cc, seq)
+#define packet_ok(cc, from, seq) \
+       packet_ok(cc, seq)
 #endif
 static int
-packet_ok(const unsigned char *buf, int cc, const struct sockaddr_in *from, int seq)
+packet_ok(int cc, const struct sockaddr_in *from, int seq)
 {
        const struct icmp *icp;
        unsigned char type, code;
        int hlen;
        const struct ip *ip;
 
-       ip = (struct ip *) buf;
+       ip = (struct ip *) recv_pkt;
        hlen = ip->ip_hl << 2;
        if (cc < hlen + ICMP_MINLEN) {
 #if ENABLE_FEATURE_TRACEROUTE_VERBOSE
@@ -738,7 +469,7 @@ packet_ok(const unsigned char *buf, int cc, const struct sockaddr_in *from, int
                return 0;
        }
        cc -= hlen;
-       icp = (struct icmp *)(buf + hlen);
+       icp = (struct icmp *)(recv_pkt + hlen);
        type = icp->icmp_type;
        code = icp->icmp_code;
        /* Path MTU Discovery (RFC1191) */
@@ -755,33 +486,37 @@ packet_ok(const unsigned char *buf, int cc, const struct sockaddr_in *from, int
 
                hip = &icp->icmp_ip;
                hlen = hip->ip_hl << 2;
-#if ENABLE_FEATURE_TRACEROUTE_USE_ICMP
-               if (useicmp) {
+               if (option_mask32 & OPT_USE_ICMP) {
                        struct icmp *hicmp;
 
                        /* XXX */
-                       if (type == ICMP_ECHOREPLY &&
-                           icp->icmp_id == htons(ident) &&
-                           icp->icmp_seq == htons(seq))
+                       if (type == ICMP_ECHOREPLY
+                        && icp->icmp_id == htons(ident)
+                        && icp->icmp_seq == htons(seq)
+                       ) {
                                return -2;
+                       }
 
                        hicmp = (struct icmp *)((unsigned char *)hip + hlen);
-                       /* XXX 8 is a magic number */
-                       if (hlen + 8 <= cc &&
-                           hip->ip_p == IPPROTO_ICMP &&
-                           hicmp->icmp_id == htons(ident) &&
-                           hicmp->icmp_seq == htons(seq))
+                       if (hlen + SIZEOF_ICMP_HDR <= cc
+                        && hip->ip_p == IPPROTO_ICMP
+                        && hicmp->icmp_id == htons(ident)
+                        && hicmp->icmp_seq == htons(seq)
+                       ) {
                                return (type == ICMP_TIMXCEED ? -1 : code + 1);
-               } else
-#endif
-               {
-                       up = (struct udphdr *)((unsigned char *)hip + hlen);
-                       /* XXX 8 is a magic number */
-                       if (hlen + 12 <= cc &&
-                           hip->ip_p == IPPROTO_UDP &&
-                           up->source == htons(ident) &&
-                           up->dest == htons(port + seq))
+                       }
+               } else {
+                       up = (struct udphdr *)((char *)hip + hlen);
+                       if (hlen + 12 <= cc
+                        && hip->ip_p == IPPROTO_UDP
+// Off: since we do not form the entire IP packet,
+// but defer it to kernel, we can't set source port,
+// and thus can't check it here in the reply
+                       /* && up->source == htons(ident) */
+                        && up->dest == htons(port + seq)
+                       ) {
                                return (type == ICMP_TIMXCEED ? -1 : code + 1);
+                       }
                }
        }
 #if ENABLE_FEATURE_TRACEROUTE_VERBOSE
@@ -812,85 +547,31 @@ print_inetname(const struct sockaddr_in *from)
        const char *ina;
 
        ina = inet_ntoa(from->sin_addr);
-       if (nflag)
-               printf(" %s", ina);
+       if (option_mask32 & OPT_ADDR_NUM)
+               printf("  %s", ina);
        else {
                char *n = NULL;
                if (from->sin_addr.s_addr != INADDR_ANY)
                        n = xmalloc_sockaddr2host_noport((struct sockaddr*)from);
-               printf(" %s (%s)", (n ? n : ina), ina);
+               printf("  %s (%s)", (n ? n : ina), ina);
                free(n);
        }
 }
 
 static void
-print(const unsigned char *buf, int cc, const struct sockaddr_in *from)
+print(int cc, const struct sockaddr_in *from)
 {
-       const struct ip *ip;
-       int hlen;
-
-       ip = (struct ip *) buf;
-       hlen = ip->ip_hl << 2;
-       cc -= hlen;
-
        print_inetname(from);
-#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
-       if (verbose)
-               printf(" %d bytes to %s", cc, inet_ntoa(ip->ip_dst));
-#endif
-}
+       if (verbose) {
+               const struct ip *ip;
+               int hlen;
 
-static struct hostinfo *
-gethostinfo(const char *host)
-{
-       int n;
-       struct hostent *hp;
-       struct hostinfo *hi;
-       char **p;
-       uint32_t addr, *ap;
-
-       hi = xzalloc(sizeof(*hi));
-       addr = inet_addr(host);
-       if (addr != 0xffffffff) {
-               hi->name = xstrdup(host);
-               hi->n = 1;
-               hi->addrs = xzalloc(sizeof(hi->addrs[0]));
-               hi->addrs[0] = addr;
-               return hi;
+               ip = (struct ip *) recv_pkt;
+               hlen = ip->ip_hl << 2;
+               cc -= hlen;
+               printf(" %d bytes to %s", cc, inet_ntoa(ip->ip_dst));
        }
-
-       hp = xgethostbyname(host);
-       if (hp->h_addrtype != AF_INET || hp->h_length != 4)
-               bb_perror_msg_and_die("bad host %s", host);
-       hi->name = xstrdup(hp->h_name);
-       for (n = 0, p = hp->h_addr_list; *p != NULL; ++n, ++p)
-               continue;
-       hi->n = n;
-       hi->addrs = xzalloc(n * sizeof(hi->addrs[0]));
-       for (ap = hi->addrs, p = hp->h_addr_list; *p != NULL; ++ap, ++p)
-               memcpy(ap, *p, sizeof(*ap));
-       return hi;
-}
-
-static void
-freehostinfo(struct hostinfo *hi)
-{
-       free(hi->name);
-       free(hi->addrs);
-       free(hi);
-}
-
-#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
-static void
-getaddr(uint32_t *ap, const char *host)
-{
-       struct hostinfo *hi;
-
-       hi = gethostinfo(host);
-       *ap = hi->addrs[0];
-       freehostinfo(hi);
 }
-#endif
 
 static void
 print_delta_ms(unsigned t1p, unsigned t2p)
@@ -900,46 +581,38 @@ print_delta_ms(unsigned t1p, unsigned t2p)
 }
 
 /*
-"Usage: %s [-dFIlnrvx] [-g gateway] [-i iface] [-f first_ttl]\n"
-"\t[-m max_ttl] [ -p port] [-q nqueries] [-s src_addr] [-t tos]\n"
-"\t[-w waittime] [-z pausemsecs] host [packetlen]"
+Usage: [-dFIlnrvx] [-g gateway] [-i iface] [-f first_ttl]
+[-m max_ttl] [ -p port] [-q nqueries] [-s src_addr] [-t tos]
+[-w waittime] [-z pausemsecs] host [packetlen]"
 */
 
 int traceroute_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int traceroute_main(int argc, char **argv)
 {
-       unsigned char *outp;
-       struct sockaddr_in *from;
-       struct sockaddr_in *to;
-       struct hostinfo *hi;
-       int ttl, probe, i;
+       int minpacket;
+       int ttl, i;
        int seq = 0;
        int tos = 0;
-       char *tos_str;
-       char *source;
+       int max_ttl = 30;
+       int nprobes = 3;
+       int first_ttl = 1;
+       unsigned pausemsecs = 0;
        unsigned op;
-#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
-       int lsrr = 0;
-#endif
-       struct IFADDRLIST *al;
+       char *source;
        char *device;
-       int max_ttl = 30;
+       char *tos_str;
        char *max_ttl_str;
        char *port_str;
-       int nprobes = 3;
        char *nprobes_str;
        char *waittime_str;
-       unsigned pausemsecs = 0;
        char *pausemsecs_str;
-       int first_ttl = 1;
        char *first_ttl_str;
 #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
        llist_t *source_route_list = NULL;
+       int lsrr = 0;
 #endif
 
        INIT_G();
-       from = (struct sockaddr_in *)&wherefrom;
-       to = (struct sockaddr_in *)&whereto;
 
 #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
        opt_complementary = "x-x:g::";
@@ -958,10 +631,10 @@ int traceroute_main(int argc, char **argv)
 #endif
        );
 
-       if (op & OPT_IP_CHKSUM) {
-               doipcksum = 0;
+#if 0 /* IGNORED */
+       if (op & OPT_IP_CHKSUM)
                bb_error_msg("warning: ip checksums disabled");
-       }
+#endif
        if (op & OPT_TOS)
                tos = xatou_range(tos_str, 0, 255);
        if (op & OPT_MAX_TTL)
@@ -988,42 +661,33 @@ int traceroute_main(int argc, char **argv)
 #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
        if (source_route_list) {
                while (source_route_list) {
+                       len_and_sockaddr *lsa;
+
                        if (lsrr >= NGATEWAYS)
                                bb_error_msg_and_die("no more than %d gateways", NGATEWAYS);
-                       getaddr(gwlist + lsrr, llist_pop(&source_route_list));
+                       lsa = xhost_and_af2sockaddr(llist_pop(&source_route_list), 0, AF_INET);
+                       gwlist[lsrr] = lsa->u.sin.sin_addr.s_addr;
+                       free(lsa);
                        ++lsrr;
                }
                optlen = (lsrr + 1) * sizeof(gwlist[0]);
        }
 #endif
 
-       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 */
+       minpacket = sizeof(*outip) + SIZEOF_ICMP_HDR + sizeof(*outdata) + optlen;
+       if (!(op & OPT_USE_ICMP))
+               minpacket += sizeof(*outudp) - SIZEOF_ICMP_HDR;
+       packlen = minpacket;
 
        /* Process destination and optional packet size */
        argv += optind;
        argc -= optind;
        switch (argc) {
        case 2:
-               packlen = xatoul_range(argv[1], minpacket, maxpacket);
+               packlen = xatoul_range(argv[1], minpacket, 32 * 1024);
                /* Fall through */
        case 1:
-               hostname = argv[0];
-               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);
+               dest_lsa = xhost2sockaddr(argv[0], port);
                break;
        default:
                bb_show_usage();
@@ -1032,8 +696,7 @@ int traceroute_main(int argc, char **argv)
        /* Ensure the socket fds won't be 0, 1 or 2 */
        bb_sanitize_stdio();
 
-       rcvsock = xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
-
+       xmove_fd(xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP), rcvsock);
 #if TRACEROUTE_SO_DEBUG
        if (op & OPT_DEBUG)
                setsockopt(rcvsock, SOL_SOCKET, SO_DEBUG,
@@ -1043,15 +706,17 @@ int traceroute_main(int argc, char **argv)
                setsockopt(rcvsock, SOL_SOCKET, SO_DONTROUTE,
                                &const_int_1, sizeof(const_int_1));
 
-       sndsock = xsocket(AF_INET, SOCK_RAW, IPPROTO_RAW);
-
+       if (op & OPT_USE_ICMP)
+               xmove_fd(xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP), sndsock);
+       else
+               xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), sndsock);
 #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;
+               gwlist[lsrr] = dest_lsa->u.sin.sin_addr.s_addr;
                ++lsrr;
 
                /* force 4 byte alignment */
@@ -1071,24 +736,20 @@ int traceroute_main(int argc, char **argv)
        }
 #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 ((op & OPT_TOS) && setsockopt(sndsock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {
                bb_perror_msg_and_die("setsockopt tos %d", tos);
        }
 #endif
+#ifdef IP_DONTFRAG
+       if (op & OPT_DONT_FRAGMNT)
+               setsockopt(sndsock, IPPROTO_IP, IP_DONTFRAG,
+                               &const_int_1, sizeof(const_int_1));
 #endif
 #if TRACEROUTE_SO_DEBUG
        if (op & OPT_DEBUG)
@@ -1099,136 +760,82 @@ int traceroute_main(int argc, char **argv)
                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 (op & OPT_TOS)
-               outip->ip_tos = tos;
-       outip->ip_len = htons(packlen);
-       if (op & OPT_DONT_FRAGMNT)
-               outip->ip_off = htons(IP_DF);
-       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;
+       if (op & OPT_USE_ICMP) {
+               ident = getpid() | 0x8000;
                outicmp->icmp_type = ICMP_ECHO;
                outicmp->icmp_id = htons(ident);
-               outdata = (outdata_t *)(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 = (outdata_t *)(outudp + 1);
+               outdata = (struct outdata_t *)((char *)outicmp + SIZEOF_ICMP_HDR);
+       } else {
+               outdata = (struct outdata_t *)(outudp + 1);
        }
 
-       /* Look for a specific device */
-       if (op & OPT_DEVICE) {
-               /* Get the interface address list */
-               int n = ifaddrlist(&al);
-               for (; n > 0; --n, ++al)
-                       if (strcmp(device, al->device) == 0)
-                               goto found_dev;
-               bb_error_msg_and_die("can't find interface %s", device);
-       }
- found_dev:
+       if (op & OPT_DEVICE) /* hmm, do we need error check? */
+               setsockopt_bindtodevice(sndsock, device);
 
-       /* 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) {
-                       uint32_t *ap;
-                       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);
+       if (op & OPT_SOURCE) {
+               len_and_sockaddr *source_lsa = xdotted2sockaddr(source, 0);
+               /* Ping does this (why?) */
+               if (setsockopt(sndsock, IPPROTO_IP, IP_MULTICAST_IF,
+                               &source_lsa->u.sa, source_lsa->len))
+                       bb_error_msg_and_die("can't set multicast source interface");
+//TODO: we can query source port we bound to,
+// and check it in replies... if we care enough
+               xbind(sndsock, &source_lsa->u.sa, source_lsa->len);
+               free(source_lsa);
        }
 
-       outip->ip_src = from->sin_addr;
-#ifndef IP_HDRINCL
-       xbind(sndsock, (struct sockaddr *)from, sizeof(*from));
-#endif
+       /* Revert to non-privileged user after opening sockets */
+       xsetgid(getgid());
+       xsetuid(getuid());
 
-       printf("traceroute to %s (%s)", hostname, inet_ntoa(to->sin_addr));
+       printf("traceroute to %s (%s)", argv[0],
+                       xmalloc_sockaddr2dotted_noport(&dest_lsa->u.sa));
        if (op & OPT_SOURCE)
                printf(" from %s", source);
        printf(", %d hops max, %d byte packets\n", max_ttl, packlen);
 
        for (ttl = first_ttl; ttl <= max_ttl; ++ttl) {
+//TODO: make it protocol agnostic (get rid of sockaddr_in)
+               struct sockaddr_in from;
                uint32_t lastaddr = 0;
-               int gotlastaddr = 0;
+               int probe;
+               int unreachable = 0; /* counter */
+               int gotlastaddr = 0; /* flags */
                int got_there = 0;
-               int unreachable = 0;
-               int sentfirst = 0;
+               int first = 1;
 
-               printf("%2d ", ttl);
+               printf("%2d", ttl);
                for (probe = 0; probe < nprobes; ++probe) {
                        int cc;
                        unsigned t1;
                        unsigned t2;
                        struct ip *ip;
 
-                       if (sentfirst && pausemsecs > 0)
+                       if (!first && pausemsecs > 0)
                                usleep(pausemsecs * 1000);
                        fflush(stdout);
 
                        t1 = monotonic_us();
                        send_probe(++seq, ttl);
-                       ++sentfirst;
+                       first = 0;
 
-                       while ((cc = wait_for_reply(rcvsock, from)) != 0) {
+                       while ((cc = wait_for_reply(&from)) != 0) {
                                t2 = monotonic_us();
-                               i = packet_ok(packet, cc, from, seq);
+                               i = packet_ok(cc, &from, seq);
                                /* Skip short packet */
                                if (i == 0)
                                        continue;
                                if (!gotlastaddr
-                                || from->sin_addr.s_addr != lastaddr
+                                || from.sin_addr.s_addr != lastaddr
                                ) {
-                                       print(packet, cc, from);
-                                       lastaddr = from->sin_addr.s_addr;
-                                       ++gotlastaddr;
+                                       print(cc, &from);
+                                       lastaddr = from.sin_addr.s_addr;
+                                       gotlastaddr = 1;
                                }
                                print_delta_ms(t1, t2);
-                               ip = (struct ip *)packet;
+                               ip = (struct ip *)recv_pkt;
                                if (op & OPT_TTL_FLAG)
                                        printf(" (%d)", ip->ip_ttl);
                                if (i == -2) {