Magellan Linux

Diff of /trunk/mkinitrd-magellan/busybox/networking/traceroute.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 983 by niro, Fri Apr 24 18:33:46 2009 UTC revision 984 by niro, Sun May 30 11:32:42 2010 UTC
# Line 23  Line 23 
23   */   */
24    
25  /*  /*
26     * traceroute6
27     *
28     *      Modified for NRL 4.4BSD IPv6 release.
29     *      07/31/96 bgp
30     *
31     * Modified for Linux IPv6 by Pedro Roque <roque@di.fc.ul.pt>
32     * 31/07/1996
33     *
34     * As ICMP error messages for IPv6 now include more than 8 bytes
35     * UDP datagrams are now sent via an UDP socket instead of magic
36     * RAW socket tricks.
37     *
38     * Converted to busybox applet by Leonid Lisovskiy <lly@sf.net>
39     * 2009-11-16
40     */
41    
42    /*
43   * traceroute host  - trace the route ip packets follow going to "host".   * traceroute host  - trace the route ip packets follow going to "host".
44   *   *
45   * Attempt to trace the route an ip packet would follow to some   * Attempt to trace the route an ip packet would follow to some
# Line 211  Line 228 
228  #include <netinet/udp.h>  #include <netinet/udp.h>
229  #include <netinet/ip.h>  #include <netinet/ip.h>
230  #include <netinet/ip_icmp.h>  #include <netinet/ip_icmp.h>
231    #if ENABLE_FEATURE_IPV6
232    # include <netinet/ip6.h>
233    # include <netinet/icmp6.h>
234    # ifndef SOL_IPV6
235    #  define SOL_IPV6 IPPROTO_IPV6
236    # endif
237    #endif
238    
239  #include "libbb.h"  #include "libbb.h"
240  #include "inet_common.h"  #include "inet_common.h"
241    
   
 /*  
  * Definitions for internet protocol version 4.  
  * Per RFC 791, September 1981.  
  */  
 #define IPVERSION 4  
   
242  #ifndef IPPROTO_ICMP  #ifndef IPPROTO_ICMP
243  /* Grrrr.... */  # define IPPROTO_ICMP 1
 #define IPPROTO_ICMP 1  
244  #endif  #endif
245  #ifndef IPPROTO_IP  #ifndef IPPROTO_IP
246  #define IPPROTO_IP 0  # define IPPROTO_IP 0
247  #endif  #endif
248    
 /*  
  * 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 */  
 };  
249    
250  /*  #define OPT_STRING "FIlnrdvxt:i:m:p:q:s:w:z:f:" \
251   * UDP kernel structures and variables.      IF_FEATURE_TRACEROUTE_SOURCE_ROUTE("g:") \
252   */      "4" IF_TRACEROUTE6("6")
253  struct udpiphdr {  enum {
254   struct  ipovly ui_i;            /* overlaid ip structure */   OPT_DONT_FRAGMNT = (1 << 0),    /* F */
255   struct  udphdr ui_u;            /* udp header */   OPT_USE_ICMP     = (1 << 1) * ENABLE_FEATURE_TRACEROUTE_USE_ICMP, /* I */
256     OPT_TTL_FLAG     = (1 << 2),    /* l */
257     OPT_ADDR_NUM     = (1 << 3),    /* n */
258     OPT_BYPASS_ROUTE = (1 << 4),    /* r */
259     OPT_DEBUG        = (1 << 5),    /* d */
260     OPT_VERBOSE      = (1 << 6) * ENABLE_FEATURE_TRACEROUTE_VERBOSE, /* v */
261     OPT_IP_CHKSUM    = (1 << 7),    /* x */
262     OPT_TOS          = (1 << 8),    /* t */
263     OPT_DEVICE       = (1 << 9),    /* i */
264     OPT_MAX_TTL      = (1 << 10),   /* m */
265     OPT_PORT         = (1 << 11),   /* p */
266     OPT_NPROBES      = (1 << 12),   /* q */
267     OPT_SOURCE       = (1 << 13),   /* s */
268     OPT_WAITTIME     = (1 << 14),   /* w */
269     OPT_PAUSE_MS     = (1 << 15),   /* z */
270     OPT_FIRST_TTL    = (1 << 16),   /* f */
271     OPT_SOURCE_ROUTE = (1 << 17) * ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE, /* g */
272     OPT_IPV4         = (1 << (17+ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE)),   /* 4 */
273     OPT_IPV6         = (1 << (18+ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE)) * ENABLE_TRACEROUTE6, /* 6 */
274  };  };
275  #define ui_next         ui_i.ih_next  #define verbose (option_mask32 & OPT_VERBOSE)
276  #define ui_prev         ui_i.ih_prev  
277  #define ui_x1           ui_i.ih_x1  enum {
278  #define ui_pr           ui_i.ih_pr   SIZEOF_ICMP_HDR = 8,
279  #define ui_len          ui_i.ih_len   rcvsock = 3, /* receive (icmp) socket file descriptor */
280  #define ui_src          ui_i.ih_src   sndsock = 4, /* send (udp/icmp) socket file descriptor */
 #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;  
281  };  };
282    
283  /* Data section of the probe packet */  /* Data section of the probe packet */
284  typedef struct outdata {  struct outdata_t {
285   unsigned char seq;             /* sequence number of this packet */   unsigned char seq;             /* sequence number of this packet */
286   unsigned char ttl;             /* ttl packet left with */   unsigned char ttl;             /* ttl packet left with */
287  // UNUSED. Retaining to have the same packet size.  // UNUSED. Retaining to have the same packet size.
288   struct timeval tv_UNUSED PACKED; /* time packet left */   struct timeval tv_UNUSED PACKED; /* time packet left */
 } outdata_t;  
   
 struct IFADDRLIST {  
  uint32_t addr;  
  char device[sizeof(struct ifreq)];  
289  };  };
290    
291    #if ENABLE_TRACEROUTE6
292  /* Keep in sync with getopt32 call! */  struct outdata6_t {
293  #define OPT_DONT_FRAGMNT (1<<0)    /* F */   uint32_t ident6;
294  #define OPT_USE_ICMP     (1<<1)    /* I */   uint32_t seq6;
295  #define OPT_TTL_FLAG     (1<<2)    /* l */   struct timeval tv_UNUSED PACKED; /* time packet left */
296  #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)  
297  #endif  #endif
 #define nflag   (option_mask32 & OPT_ADDR_NUM)  
   
298    
299  struct globals {  struct globals {
300   struct ip *outip;               /* last output (udp) packet */   struct ip *outip;
301   struct udphdr *outudp;          /* last output (udp) packet */   struct outdata_t *outdata;
302   struct outdata *outdata;        /* last output (udp) packet */   len_and_sockaddr *dest_lsa;
   
 #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 */  
   
303   int packlen;                    /* total length of packet */   int packlen;                    /* total length of packet */
  int minpacket;                  /* min ip packet size */  
  int maxpacket; // 32 * 1024;    /* max ip packet size */  
304   int pmtu;                       /* Path MTU Discovery (RFC1191) */   int pmtu;                       /* Path MTU Discovery (RFC1191) */
305     uint32_t ident;
  char *hostname;  
   
  uint16_t ident;  
306   uint16_t port; // 32768 + 666;  /* start udp dest port # for probe packets */   uint16_t port; // 32768 + 666;  /* start udp dest port # for probe packets */
   
307   int waittime; // 5;             /* time to wait for response (in seconds) */   int waittime; // 5;             /* time to wait for response (in seconds) */
  int doipcksum; // 1;            /* calculate ip checksums by default */  
   
308  #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE  #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
309   int optlen;                     /* length of ip options */   int optlen;                     /* length of ip options */
310  #else  #else
311  #define optlen 0  #define optlen 0
312  #endif  #endif
313     unsigned char recv_pkt[512];    /* last inbound (icmp) packet */
  struct sockaddr_storage whereto;        /* Who to try to reach */  
  struct sockaddr_storage wherefrom;      /* Who we are */  
  /* last inbound (icmp) packet */  
  unsigned char packet[512];  
314  #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE  #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
315   /* Maximum number of gateways (include room for one noop) */   /* Maximum number of gateways (include room for one noop) */
316  #define NGATEWAYS ((int)((MAX_IPOPTLEN - IPOPT_MINOFF - 1) / sizeof(uint32_t)))  #define NGATEWAYS ((int)((MAX_IPOPTLEN - IPOPT_MINOFF - 1) / sizeof(uint32_t)))
# Line 356  struct globals { Line 321  struct globals {
321    
322  #define G (*ptr_to_globals)  #define G (*ptr_to_globals)
323  #define outip     (G.outip    )  #define outip     (G.outip    )
 #define outudp    (G.outudp   )  
324  #define outdata   (G.outdata  )  #define outdata   (G.outdata  )
325  #define outicmp   (G.outicmp  )  #define dest_lsa  (G.dest_lsa )
 #define rcvsock   (G.rcvsock  )  
 #define sndsock   (G.sndsock  )  
326  #define packlen   (G.packlen  )  #define packlen   (G.packlen  )
 #define minpacket (G.minpacket)  
 #define maxpacket (G.maxpacket)  
327  #define pmtu      (G.pmtu     )  #define pmtu      (G.pmtu     )
 #define hostname  (G.hostname )  
328  #define ident     (G.ident    )  #define ident     (G.ident    )
329  #define port      (G.port     )  #define port      (G.port     )
330  #define waittime  (G.waittime )  #define waittime  (G.waittime )
 #define doipcksum (G.doipcksum)  
331  #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE  #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
332  #define optlen    (G.optlen   )  # define optlen   (G.optlen   )
333  #endif  #endif
334  #define packet    (G.packet   )  #define recv_pkt  (G.recv_pkt )
 #define whereto   (G.whereto  )  
 #define wherefrom (G.wherefrom)  
335  #define gwlist    (G.gwlist   )  #define gwlist    (G.gwlist   )
336  #define INIT_G() do { \  #define INIT_G() do { \
337   SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \   SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
  maxpacket = 32 * 1024; \  
338   port = 32768 + 666; \   port = 32768 + 666; \
339   waittime = 5; \   waittime = 5; \
  doipcksum = 1; \  
340  } while (0)  } while (0)
341    
342    #define outicmp ((struct icmp *)(outip + 1))
343    #define outudp  ((struct udphdr *)(outip + 1))
344    
 /*  
  * 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;  
345    
346   fd = xsocket(AF_INET, SOCK_DGRAM, 0);  /* libbb candidate? tftp uses this idiom too */
347    static len_and_sockaddr* dup_sockaddr(const len_and_sockaddr *lsa)
  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(ifr.ifr_name, ifrp->ifr_name, sizeof(ifr.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)  
348  {  {
349   int i, n;   len_and_sockaddr *new_lsa = xzalloc(LSA_LEN_SIZE + lsa->len);
350   FILE *f;   memcpy(new_lsa, lsa, LSA_LEN_SIZE + lsa->len);
351   uint32_t mask;   return new_lsa;
  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);  
352  }  }
353    
 /*  
 "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]"  
   
 */  
354    
355  static int  static int
356  wait_for_reply(int sock, struct sockaddr_in *fromp)  wait_for_reply(len_and_sockaddr *from_lsa, struct sockaddr *to)
357  {  {
358   struct pollfd pfd[1];   struct pollfd pfd[1];
359   int cc = 0;   int read_len = 0;
  socklen_t fromlen = sizeof(*fromp);  
360    
361   pfd[0].fd = sock;   pfd[0].fd = rcvsock;
362   pfd[0].events = POLLIN;   pfd[0].events = POLLIN;
363   if (safe_poll(pfd, 1, waittime * 1000) > 0)   if (safe_poll(pfd, 1, waittime * 1000) > 0) {
364   cc = recvfrom(sock, packet, sizeof(packet), 0,   read_len = recv_from_to(rcvsock,
365      (struct sockaddr *)fromp, &fromlen);   recv_pkt, sizeof(recv_pkt),
366   return cc;   /*flags:*/ 0,
367     &from_lsa->u.sa, to, from_lsa->len);
368     }
369    
370     return read_len;
371  }  }
372    
373  /*  /*
# Line 581  in_cksum(uint16_t *addr, int len) Line 382  in_cksum(uint16_t *addr, int len)
382   int sum = 0;   int sum = 0;
383    
384   /*   /*
385   *  Our algorithm is simple, using a 32 bit accumulator (sum),   * Our algorithm is simple, using a 32 bit accumulator (sum),
386   *  we add sequential 16 bit words to it, and at the end, fold   * we add sequential 16 bit words to it, and at the end, fold
387   *  back all the carry bits from the top 16 bits into the lower   * back all the carry bits from the top 16 bits into the lower
388   *  16 bits.   * 16 bits.
389   */   */
390   while (nleft > 1)  {   while (nleft > 1) {
391   sum += *w++;   sum += *w++;
392   nleft -= 2;   nleft -= 2;
393   }   }
# Line 595  in_cksum(uint16_t *addr, int len) Line 396  in_cksum(uint16_t *addr, int len)
396   if (nleft == 1)   if (nleft == 1)
397   sum += *(unsigned char *)w;   sum += *(unsigned char *)w;
398    
399   /*   /* add back carry outs from top 16 bits to low 16 bits */
  * add back carry outs from top 16 bits to low 16 bits  
  */  
400   sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */   sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
401   sum += (sum >> 16);                     /* add carry */   sum += (sum >> 16);                     /* add carry */
402   answer = ~sum;                          /* truncate to 16 bits */   answer = ~sum;                          /* truncate to 16 bits */
403   return answer;   return answer;
404  }  }
405    
   
406  static void  static void
407  send_probe(int seq, int ttl)  send_probe(int seq, int ttl)
408  {  {
409   int cc;   int len, res;
410   struct udpiphdr *ui, *oui;   void *out;
  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;  
  }  
411    
412   /* Payload */   /* Payload */
413   outdata->seq = seq;  #if ENABLE_TRACEROUTE6
414   outdata->ttl = ttl;   if (dest_lsa->u.sa.sa_family == AF_INET6) {
415  // UNUSED: was storing gettimeofday's result there, but never ever checked it   struct outdata6_t *pkt = (struct outdata6_t *) outip;
416   /*memcpy(&outdata->tv, tp, sizeof(outdata->tv));*/   pkt->ident6 = htonl(ident);
417     pkt->seq6   = htonl(seq);
418  #if ENABLE_FEATURE_TRACEROUTE_USE_ICMP   /*gettimeofday(&pkt->tv, &tz);*/
  if (useicmp)  
  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;  
419   } else   } else
420  #endif  #endif
421   if (doipcksum) {   {
422   /* Checksum (we must save and restore ip header) */   outdata->seq = seq;
423   tip = *outip;   outdata->ttl = ttl;
424   ui = (struct udpiphdr *)outip;  // UNUSED: was storing gettimeofday's result there, but never ever checked it
425   oui = (struct udpiphdr *)&tip;   /*memcpy(&outdata->tv, tp, sizeof(outdata->tv));*/
426   /* Easier to zero and put back things that are ok */  
427   memset((char *)ui, 0, sizeof(ui->ui_i));   if (option_mask32 & OPT_USE_ICMP) {
428   ui->ui_src = oui->ui_src;   outicmp->icmp_seq = htons(seq);
429   ui->ui_dst = oui->ui_dst;  
430   ui->ui_pr = oui->ui_pr;   /* Always calculate checksum for icmp packets */
431   ui->ui_len = outudp->len;   outicmp->icmp_cksum = 0;
432   outudp->check = 0;   outicmp->icmp_cksum = in_cksum((uint16_t *)outicmp,
433   outudp->check = in_cksum((uint16_t *)ui, packlen);   packlen - (sizeof(*outip) + optlen));
434   if (outudp->check == 0)   if (outicmp->icmp_cksum == 0)
435   outudp->check = 0xffff;   outicmp->icmp_cksum = 0xffff;
436   *outip = tip;   }
437   }   }
438    
439  #if ENABLE_FEATURE_TRACEROUTE_VERBOSE  //BUG! verbose is (x & OPT_VERBOSE), not a counter!
440    #if 0 //ENABLE_FEATURE_TRACEROUTE_VERBOSE
441   /* XXX undocumented debugging hack */   /* XXX undocumented debugging hack */
442   if (verbose > 1) {   if (verbose > 1) {
443   const uint16_t *sp;   const uint16_t *sp;
# Line 693  send_probe(int seq, int ttl) Line 462  send_probe(int seq, int ttl)
462   }   }
463  #endif  #endif
464    
465  #if !defined(IP_HDRINCL) && defined(IP_TTL)  #if ENABLE_TRACEROUTE6
466   if (setsockopt(sndsock, IPPROTO_IP, IP_TTL,   if (dest_lsa->u.sa.sa_family == AF_INET6) {
467      (char *)&ttl, sizeof(ttl)) < 0) {   res = setsockopt(sndsock, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
468   bb_perror_msg_and_die("setsockopt ttl %d", ttl);   if (res < 0)
469   }   bb_perror_msg_and_die("setsockopt UNICAST_HOPS %d", ttl);
470     out = outip;
471     len = packlen;
472     } else
473  #endif  #endif
474     {
475   cc = xsendto(sndsock, (char *)outip,  #if defined IP_TTL
476      packlen, (struct sockaddr *)&whereto, sizeof(whereto));   res = setsockopt(sndsock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
477   if (cc != packlen)  {   if (res < 0)
478   bb_info_msg("wrote %s %d chars, ret=%d", hostname, packlen, cc);   bb_perror_msg_and_die("setsockopt ttl %d", ttl);
479    #endif
480     out = outicmp;
481     len = packlen - sizeof(*outip);
482     if (!(option_mask32 & OPT_USE_ICMP)) {
483     out = outdata;
484     len -= sizeof(*outudp);
485     set_nport(dest_lsa, htons(port + seq));
486     }
487   }   }
488    
489     res = xsendto(sndsock, out, len, &dest_lsa->u.sa, dest_lsa->len);
490     if (res != len)
491     bb_info_msg("sent %d octets, ret=%d", len, res);
492  }  }
493    
494  #if ENABLE_FEATURE_TRACEROUTE_VERBOSE  #if ENABLE_FEATURE_TRACEROUTE_VERBOSE
495  /*  /*
496   * Convert an ICMP "type" field to a printable string.   * Convert an ICMP "type" field to a printable string.
497   */   */
498  static inline const char *  static const char *
499  pr_type(unsigned char t)  pr_type(unsigned char t)
500  {  {
501   static const char *const ttab[] = {   static const char *const ttab[] = {
# Line 721  pr_type(unsigned char t) Line 505  pr_type(unsigned char t)
505   "Param Problem", "Timestamp",   "Timestamp Reply", "Info Request",   "Param Problem", "Timestamp",   "Timestamp Reply", "Info Request",
506   "Info Reply",   "Mask Request", "Mask Reply"   "Info Reply",   "Mask Request", "Mask Reply"
507   };   };
508    # if ENABLE_TRACEROUTE6
509     static const char *const ttab6[] = {
510    [0] "Error", "Dest Unreachable", "Packet Too Big", "Time Exceeded",
511    [4] "Param Problem",
512    [8] "Echo Request", "Echo Reply", "Membership Query", "Membership Report",
513    [12] "Membership Reduction", "Router Solicit", "Router Advert", "Neighbor Solicit",
514    [16] "Neighbor Advert", "Redirect",
515     };
516    
517   if (t > 18)   if (dest_lsa->u.sa.sa_family == AF_INET6) {
518     if (t < 5)
519     return ttab6[t];
520     if (t < 128 || t > ND_REDIRECT)
521     return "OUT-OF-RANGE";
522     return ttab6[(t & 63) + 8];
523     }
524    # endif
525     if (t >= ARRAY_SIZE(ttab))
526   return "OUT-OF-RANGE";   return "OUT-OF-RANGE";
527    
528   return ttab[t];   return ttab[t];
# Line 730  pr_type(unsigned char t) Line 530  pr_type(unsigned char t)
530  #endif  #endif
531    
532  #if !ENABLE_FEATURE_TRACEROUTE_VERBOSE  #if !ENABLE_FEATURE_TRACEROUTE_VERBOSE
533  #define packet_ok(buf, cc, from, seq) \  #define packet4_ok(read_len, from, seq) \
534   packet_ok(buf, cc, seq)   packet4_ok(read_len, seq)
535  #endif  #endif
536  static int  static int
537  packet_ok(unsigned char *buf, int cc, struct sockaddr_in *from, int seq)  packet4_ok(int read_len, const struct sockaddr_in *from, int seq)
538  {  {
539   struct icmp *icp;   const struct icmp *icp;
540   unsigned char type, code;   unsigned char type, code;
541   int hlen;   int hlen;
542   struct ip *ip;   const struct ip *ip;
543    
544   ip = (struct ip *) buf;   ip = (struct ip *) recv_pkt;
545   hlen = ip->ip_hl << 2;   hlen = ip->ip_hl << 2;
546   if (cc < hlen + ICMP_MINLEN) {   if (read_len < hlen + ICMP_MINLEN) {
547  #if ENABLE_FEATURE_TRACEROUTE_VERBOSE  #if ENABLE_FEATURE_TRACEROUTE_VERBOSE
548   if (verbose)   if (verbose)
549   printf("packet too short (%d bytes) from %s\n", cc,   printf("packet too short (%d bytes) from %s\n", read_len,
550   inet_ntoa(from->sin_addr));   inet_ntoa(from->sin_addr));
551  #endif  #endif
552   return 0;   return 0;
553   }   }
554   cc -= hlen;   read_len -= hlen;
555   icp = (struct icmp *)(buf + hlen);   icp = (struct icmp *)(recv_pkt + hlen);
556   type = icp->icmp_type;   type = icp->icmp_type;
557   code = icp->icmp_code;   code = icp->icmp_code;
558   /* Path MTU Discovery (RFC1191) */   /* Path MTU Discovery (RFC1191) */
559   if (code != ICMP_UNREACH_NEEDFRAG)   pmtu = 0;
560   pmtu = 0;   if (code == ICMP_UNREACH_NEEDFRAG)
  else {  
561   pmtu = ntohs(icp->icmp_nextmtu);   pmtu = ntohs(icp->icmp_nextmtu);
562   }  
563   if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||   if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS)
564      type == ICMP_UNREACH || type == ICMP_ECHOREPLY) {   || type == ICMP_UNREACH
565   struct ip *hip;   || type == ICMP_ECHOREPLY
566   struct udphdr *up;   ) {
567     const struct ip *hip;
568     const struct udphdr *up;
569    
570   hip = &icp->icmp_ip;   hip = &icp->icmp_ip;
571   hlen = hip->ip_hl << 2;   hlen = hip->ip_hl << 2;
572  #if ENABLE_FEATURE_TRACEROUTE_USE_ICMP   if (option_mask32 & OPT_USE_ICMP) {
  if (useicmp) {  
573   struct icmp *hicmp;   struct icmp *hicmp;
574    
575   /* XXX */   /* XXX */
576   if (type == ICMP_ECHOREPLY &&   if (type == ICMP_ECHOREPLY
577      icp->icmp_id == htons(ident) &&   && icp->icmp_id == htons(ident)
578      icp->icmp_seq == htons(seq))   && icp->icmp_seq == htons(seq)
579   return -2;   ) {
580     return ICMP_UNREACH_PORT+1;
581     }
582    
583   hicmp = (struct icmp *)((unsigned char *)hip + hlen);   hicmp = (struct icmp *)((unsigned char *)hip + hlen);
584   /* XXX 8 is a magic number */   if (hlen + SIZEOF_ICMP_HDR <= read_len
585   if (hlen + 8 <= cc &&   && hip->ip_p == IPPROTO_ICMP
586      hip->ip_p == IPPROTO_ICMP &&   && hicmp->icmp_id == htons(ident)
587      hicmp->icmp_id == htons(ident) &&   && hicmp->icmp_seq == htons(seq)
588      hicmp->icmp_seq == htons(seq))   ) {
589   return (type == ICMP_TIMXCEED ? -1 : code + 1);   return (type == ICMP_TIMXCEED ? -1 : code + 1);
590   } else   }
591  #endif   } else {
592   {   up = (struct udphdr *)((char *)hip + hlen);
593   up = (struct udphdr *)((unsigned char *)hip + hlen);   if (hlen + 12 <= read_len
594   /* XXX 8 is a magic number */   && hip->ip_p == IPPROTO_UDP
595   if (hlen + 12 <= cc &&  // Off: since we do not form the entire IP packet,
596      hip->ip_p == IPPROTO_UDP &&  // but defer it to kernel, we can't set source port,
597      up->source == htons(ident) &&  // and thus can't check it here in the reply
598      up->dest == htons(port + seq))   /* && up->source == htons(ident) */
599     && up->dest == htons(port + seq)
600     ) {
601   return (type == ICMP_TIMXCEED ? -1 : code + 1);   return (type == ICMP_TIMXCEED ? -1 : code + 1);
602     }
603   }   }
604   }   }
605  #if ENABLE_FEATURE_TRACEROUTE_VERBOSE  #if ENABLE_FEATURE_TRACEROUTE_VERBOSE
# Line 804  packet_ok(unsigned char *buf, int cc, st Line 609  packet_ok(unsigned char *buf, int cc, st
609    
610   printf("\n%d bytes from %s to "   printf("\n%d bytes from %s to "
611         "%s: icmp type %d (%s) code %d\n",         "%s: icmp type %d (%s) code %d\n",
612      cc, inet_ntoa(from->sin_addr),   read_len, inet_ntoa(from->sin_addr),
613      inet_ntoa(ip->ip_dst), type, pr_type(type), icp->icmp_code);   inet_ntoa(ip->ip_dst),
614   for (i = 4; i < cc; i += sizeof(*lp))   type, pr_type(type), icp->icmp_code);
615     for (i = 4; i < read_len; i += sizeof(*lp))
616   printf("%2d: x%8.8x\n", i, *lp++);   printf("%2d: x%8.8x\n", i, *lp++);
617   }   }
618  #endif  #endif
619   return 0;   return 0;
620  }  }
621    
622    #if ENABLE_TRACEROUTE6
623    # if !ENABLE_FEATURE_TRACEROUTE_VERBOSE
624    #define packet_ok(read_len, from_lsa, to, seq) \
625     packet_ok(read_len, from_lsa, seq)
626    # endif
627    static int
628    packet_ok(int read_len, len_and_sockaddr *from_lsa,
629     struct sockaddr *to,
630     int seq)
631    {
632     const struct icmp6_hdr *icp;
633     unsigned char type, code;
634    
635     if (from_lsa->u.sa.sa_family == AF_INET)
636     return packet4_ok(read_len, &from_lsa->u.sin, seq);
637    
638     icp = (struct icmp6_hdr *) recv_pkt;
639    
640     type = icp->icmp6_type;
641     code = icp->icmp6_code;
642    
643     if ((type == ICMP6_TIME_EXCEEDED && code == ICMP6_TIME_EXCEED_TRANSIT)
644     || type == ICMP6_DST_UNREACH
645     ) {
646     struct ip6_hdr *hip;
647     struct udphdr *up;
648     int nexthdr;
649    
650     hip = (struct ip6_hdr *)(icp + 1);
651     up  = (struct udphdr *) (hip + 1);
652     nexthdr = hip->ip6_nxt;
653    
654     if (nexthdr == IPPROTO_FRAGMENT) {
655     nexthdr = *(unsigned char*)up;
656     up++;
657     }
658     if (nexthdr == IPPROTO_UDP) {
659     struct outdata6_t *pkt;
660    
661     pkt = (struct outdata6_t *) (up + 1);
662    
663     if (ntohl(pkt->ident6) == ident
664     && ntohl(pkt->seq6) == seq
665     ) {
666     return (type == ICMP6_TIME_EXCEEDED ? -1 : (code<<8)+1);
667     }
668     }
669    
670     }
671    
672    # if ENABLE_FEATURE_TRACEROUTE_VERBOSE
673     if (verbose) {
674     unsigned char *p;
675     char pa1[MAXHOSTNAMELEN];
676     char pa2[MAXHOSTNAMELEN];
677     int i;
678    
679     p = (unsigned char *) (icp + 1);
680    
681     printf("\n%d bytes from %s to "
682           "%s: icmp type %d (%s) code %d\n",
683     read_len,
684     inet_ntop(AF_INET6, &from_lsa->u.sin6.sin6_addr, pa1, sizeof(pa1)),
685     inet_ntop(AF_INET6, &((struct sockaddr_in6*)to)->sin6_addr, pa2, sizeof(pa2)),
686     type, pr_type(type), icp->icmp6_code);
687    
688     read_len -= sizeof(struct icmp6_hdr);
689     for (i = 0; i < read_len ; i++) {
690     if (i % 16 == 0)
691     printf("%04x:", i);
692     if (i % 4 == 0)
693     bb_putchar(' ');
694     printf("%02x", p[i]);
695     if ((i % 16 == 15) && (i + 1 < read_len))
696     bb_putchar('\n');
697     }
698     bb_putchar('\n');
699     }
700    # endif
701    
702     return 0;
703    }
704    #else /* !ENABLE_TRACEROUTE6 */
705    static ALWAYS_INLINE int
706    packet_ok(int read_len,
707     len_and_sockaddr *from_lsa IF_NOT_FEATURE_TRACEROUTE_VERBOSE(UNUSED_PARAM),
708     struct sockaddr *to UNUSED_PARAM,
709     int seq)
710    {
711     return packet4_ok(read_len, &from_lsa->u.sin, seq);
712    }
713    #endif
714    
715  /*  /*
716   * Construct an Internet address representation.   * Construct an Internet address representation.
717   * If the nflag has been supplied, give   * If the -n flag has been supplied, give
718   * numeric value, otherwise try for symbolic name.   * numeric value, otherwise try for symbolic name.
719   */   */
720  static void  static void
721  print_inetname(struct sockaddr_in *from)  print_inetname(const struct sockaddr *from)
722  {  {
723   const char *ina;   char *ina = xmalloc_sockaddr2dotted_noport(from);
724    
725   ina = inet_ntoa(from->sin_addr);   if (option_mask32 & OPT_ADDR_NUM) {
726   if (nflag)   printf("  %s", ina);
727   printf(" %s", ina);   } else {
  else {  
728   char *n = NULL;   char *n = NULL;
729   if (from->sin_addr.s_addr != INADDR_ANY)  
730     if (from->sa_family != AF_INET
731     || ((struct sockaddr_in*)from)->sin_addr.s_addr != INADDR_ANY
732     ) {
733     /* Try to reverse resolve if it is not 0.0.0.0 */
734   n = xmalloc_sockaddr2host_noport((struct sockaddr*)from);   n = xmalloc_sockaddr2host_noport((struct sockaddr*)from);
735   printf(" %s (%s)", (n ? n : ina), ina);   }
736     printf("  %s (%s)", (n ? n : ina), ina);
737   free(n);   free(n);
738   }   }
739     free(ina);
740  }  }
741    
742  static void  static void
743  print(unsigned char *buf, int cc, struct sockaddr_in *from)  print(int read_len, const struct sockaddr *from, const struct sockaddr *to)
744  {  {
  struct ip *ip;  
  int hlen;  
   
  ip = (struct ip *) buf;  
  hlen = ip->ip_hl << 2;  
  cc -= hlen;  
   
745   print_inetname(from);   print_inetname(from);
 #if ENABLE_FEATURE_TRACEROUTE_VERBOSE  
  if (verbose)  
  printf(" %d bytes to %s", cc, inet_ntoa(ip->ip_dst));  
 #endif  
 }  
   
   
 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;  
  }  
   
  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;  
 }  
746    
747  static void   if (verbose) {
748  freehostinfo(struct hostinfo *hi)   char *ina = xmalloc_sockaddr2dotted_noport(to);
749  {  #if ENABLE_TRACEROUTE6
750   free(hi->name);   if (to->sa_family == AF_INET6) {
751   free(hi->addrs);   read_len -= sizeof(struct ip6_hdr);
752   free(hi);   } else
 }  
   
 #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);  
 }  
753  #endif  #endif
754     {
755     read_len -= ((struct ip*)recv_pkt)->ip_hl << 2;
756     }
757     printf(" %d bytes to %s", read_len, ina);
758     free(ina);
759     }
760    }
761    
762  static void  static void
763  print_delta_ms(unsigned t1p, unsigned t2p)  print_delta_ms(unsigned t1p, unsigned t2p)
764  {  {
765   unsigned tt = t2p - t1p;   unsigned tt = t2p - t1p;
766   printf("  %u.%03u ms", tt/1000, tt%1000);   printf("  %u.%03u ms", tt / 1000, tt % 1000);
767  }  }
768    
769  int traceroute_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;  /*
770  int traceroute_main(int argc, char **argv)   * Usage: [-dFIlnrvx] [-g gateway] [-i iface] [-f first_ttl]
771     * [-m max_ttl] [ -p port] [-q nqueries] [-s src_addr] [-t tos]
772     * [-w waittime] [-z pausemsecs] host [packetlen]"
773     */
774    static int
775    common_traceroute_main(int op, char **argv)
776  {  {
777   int code, n;   int i;
778   unsigned char *outp;   int minpacket;
  uint32_t *ap;  
  struct sockaddr_in *from;  
  struct sockaddr_in *to;  
  struct hostinfo *hi;  
  int ttl, probe, i;  
  int seq = 0;  
779   int tos = 0;   int tos = 0;
780   char *tos_str;   int max_ttl = 30;
781     int nprobes = 3;
782     int first_ttl = 1;
783     unsigned pausemsecs = 0;
784   char *source;   char *source;
  unsigned op;  
 #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE  
  int lsrr = 0;  
 #endif  
  uint16_t off = 0;  
  struct IFADDRLIST *al;  
785   char *device;   char *device;
786   int max_ttl = 30;   char *tos_str;
787   char *max_ttl_str;   char *max_ttl_str;
788   char *port_str;   char *port_str;
  int nprobes = 3;  
789   char *nprobes_str;   char *nprobes_str;
790   char *waittime_str;   char *waittime_str;
  unsigned pausemsecs = 0;  
791   char *pausemsecs_str;   char *pausemsecs_str;
  int first_ttl = 1;  
792   char *first_ttl_str;   char *first_ttl_str;
793  #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE  #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
794   llist_t *source_route_list = NULL;   llist_t *source_route_list = NULL;
795     int lsrr = 0;
796  #endif  #endif
797    #if ENABLE_TRACEROUTE6
798   INIT_G();   sa_family_t af;
  from = (struct sockaddr_in *)&wherefrom;  
  to = (struct sockaddr_in *)&whereto;  
   
  //opterr = 0;  
 #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE  
  opt_complementary = "x-x:g::";  
799  #else  #else
800   opt_complementary = "x-x";   enum { af = AF_INET };
801  #endif  #endif
802     int ttl;
803     int seq;
804     len_and_sockaddr *from_lsa;
805     struct sockaddr *lastaddr;
806     struct sockaddr *to;
807    
808   op = getopt32(argv, "FIlnrdvxt:i:m:p:q:s:w:z:f:"   INIT_G();
809  #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE  
810   "g:"   /* minimum 1 arg */
811  #endif   opt_complementary = "-1:x-x" IF_FEATURE_TRACEROUTE_SOURCE_ROUTE(":g::");
812     op |= getopt32(argv, OPT_STRING
813   , &tos_str, &device, &max_ttl_str, &port_str, &nprobes_str   , &tos_str, &device, &max_ttl_str, &port_str, &nprobes_str
814   , &source, &waittime_str, &pausemsecs_str, &first_ttl_str   , &source, &waittime_str, &pausemsecs_str, &first_ttl_str
815  #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE  #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
816   , &source_route_list   , &source_route_list
817  #endif  #endif
818   );   );
819     argv += optind;
820    
821   if (op & OPT_DONT_FRAGMNT)  #if 0 /* IGNORED */
822   off = IP_DF;   if (op & OPT_IP_CHKSUM)
  if (op & OPT_IP_CHKSUM) {  
  doipcksum = 0;  
823   bb_error_msg("warning: ip checksums disabled");   bb_error_msg("warning: ip checksums disabled");
824   }  #endif
825   if (op & OPT_TOS)   if (op & OPT_TOS)
826   tos = xatou_range(tos_str, 0, 255);   tos = xatou_range(tos_str, 0, 255);
827   if (op & OPT_MAX_TTL)   if (op & OPT_MAX_TTL)
# Line 989  int traceroute_main(int argc, char **arg Line 835  int traceroute_main(int argc, char **arg
835   * set the ip source address of the outbound   * set the ip source address of the outbound
836   * probe (e.g., on a multi-homed host).   * probe (e.g., on a multi-homed host).
837   */   */
838   if (getuid())   if (getuid() != 0)
839   bb_error_msg_and_die("-s %s: permission denied", source);   bb_error_msg_and_die(bb_msg_you_must_be_root);
840   }   }
841   if (op & OPT_WAITTIME)   if (op & OPT_WAITTIME)
842   waittime = xatou_range(waittime_str, 2, 24 * 60 * 60);   waittime = xatou_range(waittime_str, 1, 24 * 60 * 60);
843   if (op & OPT_PAUSE_MS)   if (op & OPT_PAUSE_MS)
844   pausemsecs = xatou_range(pausemsecs_str, 0, 60 * 60 * 1000);   pausemsecs = xatou_range(pausemsecs_str, 0, 60 * 60 * 1000);
845   if (op & OPT_FIRST_TTL)   if (op & OPT_FIRST_TTL)
846   first_ttl = xatou_range(first_ttl_str, 1, 255);   first_ttl = xatou_range(first_ttl_str, 1, max_ttl);
847    
848  #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE  #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
849   if (source_route_list) {   if (source_route_list) {
850   while (source_route_list) {   while (source_route_list) {
851     len_and_sockaddr *lsa;
852    
853   if (lsrr >= NGATEWAYS)   if (lsrr >= NGATEWAYS)
854   bb_error_msg_and_die("no more than %d gateways", NGATEWAYS);   bb_error_msg_and_die("no more than %d gateways", NGATEWAYS);
855   getaddr(gwlist + lsrr, llist_pop(&source_route_list));   lsa = xhost_and_af2sockaddr(llist_pop(&source_route_list), 0, AF_INET);
856     gwlist[lsrr] = lsa->u.sin.sin_addr.s_addr;
857     free(lsa);
858   ++lsrr;   ++lsrr;
859   }   }
860   optlen = (lsrr + 1) * sizeof(gwlist[0]);   optlen = (lsrr + 1) * sizeof(gwlist[0]);
861   }   }
862  #endif  #endif
863    
  if (first_ttl > max_ttl) {  
  bb_error_msg_and_die(  
     "first ttl (%d) may not be greater than max ttl (%d)",  
     first_ttl, max_ttl);  
  }  
   
  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 */  
   
864   /* Process destination and optional packet size */   /* Process destination and optional packet size */
865   switch (argc - optind) {   minpacket = sizeof(*outip) + SIZEOF_ICMP_HDR + sizeof(*outdata) + optlen;
866     if (!(op & OPT_USE_ICMP))
867   case 2:   minpacket += sizeof(*outudp) - SIZEOF_ICMP_HDR;
868   packlen = xatoul_range(argv[optind + 1], minpacket, maxpacket);  #if ENABLE_TRACEROUTE6
869   /* Fall through */   af = AF_UNSPEC;
870     if (op & OPT_IPV4)
871   case 1:   af = AF_INET;
872   hostname = argv[optind];   if (op & OPT_IPV6)
873   hi = gethostinfo(hostname);   af = AF_INET6;
874   setsin(to, hi->addrs[0]);   dest_lsa = xhost_and_af2sockaddr(argv[0], port, af);
875   if (hi->n > 1)   af = dest_lsa->u.sa.sa_family;
876   bb_error_msg("warning: %s has multiple addresses; using %s",   if (af == AF_INET6)
877   hostname, inet_ntoa(to->sin_addr));   minpacket = sizeof(struct outdata6_t);
878   hostname = hi->name;  #else
879   hi->name = NULL;   dest_lsa = xhost2sockaddr(argv[0], port);
880   freehostinfo(hi);  #endif
881   break;   packlen = minpacket;
882     if (argv[1])
883   default:   packlen = xatoul_range(argv[1], minpacket, 32 * 1024);
  bb_show_usage();  
  }  
884    
885   /* Ensure the socket fds won't be 0, 1 or 2 */   /* Ensure the socket fds won't be 0, 1 or 2 */
886   bb_sanitize_stdio();   bb_sanitize_stdio();
887    
888   rcvsock = xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP);  #if ENABLE_TRACEROUTE6
889     if (af == AF_INET6) {
890     xmove_fd(xsocket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6), rcvsock);
891    # ifdef IPV6_RECVPKTINFO
892     setsockopt(rcvsock, SOL_IPV6, IPV6_RECVPKTINFO,
893     &const_int_1, sizeof(const_int_1));
894     setsockopt(rcvsock, SOL_IPV6, IPV6_2292PKTINFO,
895     &const_int_1, sizeof(const_int_1));
896    # else
897     setsockopt(rcvsock, SOL_IPV6, IPV6_PKTINFO,
898     &const_int_1, sizeof(const_int_1));
899    # endif
900     } else
901    #endif
902     {
903     xmove_fd(xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP), rcvsock);
904     }
905    
906  #if TRACEROUTE_SO_DEBUG  #if TRACEROUTE_SO_DEBUG
907   if (op & OPT_DEBUG)   if (op & OPT_DEBUG)
# Line 1064  int traceroute_main(int argc, char **arg Line 912  int traceroute_main(int argc, char **arg
912   setsockopt(rcvsock, SOL_SOCKET, SO_DONTROUTE,   setsockopt(rcvsock, SOL_SOCKET, SO_DONTROUTE,
913   &const_int_1, sizeof(const_int_1));   &const_int_1, sizeof(const_int_1));
914    
915   sndsock = xsocket(AF_INET, SOCK_RAW, IPPROTO_RAW);  #if ENABLE_TRACEROUTE6
916     if (af == AF_INET6) {
917     static const int two = 2;
918     if (setsockopt(rcvsock, SOL_RAW, IPV6_CHECKSUM, &two, sizeof(two)) < 0)
919     bb_perror_msg_and_die("setsockopt RAW_CHECKSUM");
920     xmove_fd(xsocket(af, SOCK_DGRAM, 0), sndsock);
921     } else
922    #endif
923     {
924     if (op & OPT_USE_ICMP)
925     xmove_fd(xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP), sndsock);
926     else
927     xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), sndsock);
928    #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE && defined IP_OPTIONS
929     if (lsrr > 0) {
930     unsigned char optlist[MAX_IPOPTLEN];
931    
932     /* final hop */
933     gwlist[lsrr] = dest_lsa->u.sin.sin_addr.s_addr;
934     ++lsrr;
935    
936  #if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE   /* force 4 byte alignment */
937  #if defined(IP_OPTIONS)   optlist[0] = IPOPT_NOP;
938   if (lsrr > 0) {   /* loose source route option */
939   unsigned char optlist[MAX_IPOPTLEN];   optlist[1] = IPOPT_LSRR;
940     i = lsrr * sizeof(gwlist[0]);
941   /* final hop */   optlist[2] = i + 3;
942   gwlist[lsrr] = to->sin_addr.s_addr;   /* pointer to LSRR addresses */
943   ++lsrr;   optlist[3] = IPOPT_MINOFF;
944     memcpy(optlist + 4, gwlist, i);
945   /* force 4 byte alignment */  
946   optlist[0] = IPOPT_NOP;   if (setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS,
947   /* loose source route option */   (char *)optlist, i + sizeof(gwlist[0])) < 0) {
948   optlist[1] = IPOPT_LSRR;   bb_perror_msg_and_die("IP_OPTIONS");
949   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");  
950   }   }
951    #endif
952   }   }
 #endif /* IP_OPTIONS */  
 #endif /* CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE */  
953    
954  #ifdef SO_SNDBUF  #ifdef SO_SNDBUF
955   if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, &packlen, sizeof(packlen)) < 0) {   if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, &packlen, sizeof(packlen)) < 0) {
956   bb_perror_msg_and_die("SO_SNDBUF");   bb_perror_msg_and_die("SO_SNDBUF");
957   }   }
958  #endif  #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  
959  #ifdef IP_TOS  #ifdef IP_TOS
960   if (tos_str && setsockopt(sndsock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {   if ((op & OPT_TOS) && setsockopt(sndsock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {
961   bb_perror_msg_and_die("setsockopt tos %d", tos);   bb_perror_msg_and_die("setsockopt tos %d", tos);
962   }   }
963  #endif  #endif
964    #ifdef IP_DONTFRAG
965     if (op & OPT_DONT_FRAGMNT)
966     setsockopt(sndsock, IPPROTO_IP, IP_DONTFRAG,
967     &const_int_1, sizeof(const_int_1));
968  #endif  #endif
969  #if TRACEROUTE_SO_DEBUG  #if TRACEROUTE_SO_DEBUG
970   if (op & OPT_DEBUG)   if (op & OPT_DEBUG)
# Line 1120  int traceroute_main(int argc, char **arg Line 975  int traceroute_main(int argc, char **arg
975   setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,   setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
976   &const_int_1, sizeof(const_int_1));   &const_int_1, sizeof(const_int_1));
977    
  /* Revert to non-privileged user after opening sockets */  
  xsetgid(getgid());  
  xsetuid(getuid());  
   
978   outip = xzalloc(packlen);   outip = xzalloc(packlen);
979    
980   outip->ip_v = IPVERSION;   ident = getpid();
  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 = (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);  
  }  
   
  /* 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:  
981    
982   /* Determine our source address */   if (af == AF_INET) {
983   if (!(op & OPT_SOURCE)) {   if (op & OPT_USE_ICMP) {
984   /*   ident |= 0x8000;
985   * If a device was specified, use the interface address.   outicmp->icmp_type = ICMP_ECHO;
986   * Otherwise, try to determine our source address.   outicmp->icmp_id = htons(ident);
987   */   outdata = (struct outdata_t *)((char *)outicmp + SIZEOF_ICMP_HDR);
  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);  
988   } else {   } else {
989   setsin(from, hi->addrs[0]);   outdata = (struct outdata_t *)(outudp + 1);
  if (hi->n > 1)  
  bb_error_msg(  
  "warning: %s has multiple addresses; using %s",  
     source, inet_ntoa(from->sin_addr));  
990   }   }
  freehostinfo(hi);  
991   }   }
992    
993   outip->ip_src = from->sin_addr;   if (op & OPT_DEVICE) /* hmm, do we need error check? */
994  #ifndef IP_HDRINCL   setsockopt_bindtodevice(sndsock, device);
995   xbind(sndsock, (struct sockaddr *)from, sizeof(*from));  
996     if (op & OPT_SOURCE) {
997    #if ENABLE_TRACEROUTE6
998    // TODO: need xdotted_and_af2sockaddr?
999     len_and_sockaddr *source_lsa = xhost_and_af2sockaddr(source, 0, af);
1000    #else
1001     len_and_sockaddr *source_lsa = xdotted2sockaddr(source, 0);
1002    #endif
1003     /* Ping4 does this (why?) */
1004     if (af == AF_INET)
1005     if (setsockopt(sndsock, IPPROTO_IP, IP_MULTICAST_IF,
1006     &source_lsa->u.sa, source_lsa->len))
1007     bb_error_msg_and_die("can't set multicast source interface");
1008    //TODO: we can query source port we bound to,
1009    // and check it in replies... if we care enough
1010     xbind(sndsock, &source_lsa->u.sa, source_lsa->len);
1011     free(source_lsa);
1012     }
1013    #if ENABLE_TRACEROUTE6
1014     else if (af == AF_INET6) {
1015    //TODO: why we don't do it for IPv4?
1016     len_and_sockaddr *source_lsa;
1017    
1018     int probe_fd = xsocket(af, SOCK_DGRAM, 0);
1019     if (op & OPT_DEVICE)
1020     setsockopt_bindtodevice(probe_fd, device);
1021     set_nport(dest_lsa, htons(1025));
1022     /* dummy connect. makes kernel pick source IP (and port) */
1023     xconnect(probe_fd, &dest_lsa->u.sa, dest_lsa->len);
1024     set_nport(dest_lsa, htons(port));
1025    
1026     /* read IP and port */
1027     source_lsa = get_sock_lsa(probe_fd);
1028     if (source_lsa == NULL)
1029     bb_error_msg_and_die("can't get probe addr");
1030    
1031     close(probe_fd);
1032    
1033     /* bind our sockets to this IP (but not port) */
1034     set_nport(source_lsa, 0);
1035     xbind(sndsock, &source_lsa->u.sa, source_lsa->len);
1036     xbind(rcvsock, &source_lsa->u.sa, source_lsa->len);
1037    
1038     free(source_lsa);
1039     }
1040  #endif  #endif
1041    
1042   printf("traceroute to %s (%s)", hostname, inet_ntoa(to->sin_addr));   /* Revert to non-privileged user after opening sockets */
1043     xsetgid(getgid());
1044     xsetuid(getuid());
1045    
1046     printf("traceroute to %s (%s)", argv[0],
1047     xmalloc_sockaddr2dotted_noport(&dest_lsa->u.sa));
1048   if (op & OPT_SOURCE)   if (op & OPT_SOURCE)
1049   printf(" from %s", source);   printf(" from %s", source);
1050   printf(", %d hops max, %d byte packets\n", max_ttl, packlen);   printf(", %d hops max, %d byte packets\n", max_ttl, packlen);
  fflush(stdout);  
1051    
1052     from_lsa = dup_sockaddr(dest_lsa);
1053     lastaddr = xzalloc(dest_lsa->len);
1054     to = xzalloc(dest_lsa->len);
1055     seq = 0;
1056   for (ttl = first_ttl; ttl <= max_ttl; ++ttl) {   for (ttl = first_ttl; ttl <= max_ttl; ++ttl) {
1057   uint32_t lastaddr = 0;   int probe;
1058   int gotlastaddr = 0;   int unreachable = 0; /* counter */
1059     int gotlastaddr = 0; /* flags */
1060   int got_there = 0;   int got_there = 0;
1061   int unreachable = 0;   int first = 1;
  int sentfirst = 0;  
1062    
1063   printf("%2d ", ttl);   printf("%2d", ttl);
1064   for (probe = 0; probe < nprobes; ++probe) {   for (probe = 0; probe < nprobes; ++probe) {
1065   int cc;   int read_len;
1066   unsigned t1;   unsigned t1;
1067   unsigned t2;   unsigned t2;
1068   struct ip *ip;   struct ip *ip;
1069    
1070   if (sentfirst && pausemsecs > 0)   if (!first && pausemsecs > 0)
1071   usleep(pausemsecs * 1000);   usleep(pausemsecs * 1000);
1072     fflush_all();
1073    
1074   t1 = monotonic_us();   t1 = monotonic_us();
1075   send_probe(++seq, ttl);   send_probe(++seq, ttl);
1076   ++sentfirst;  
1077   while ((cc = wait_for_reply(rcvsock, from)) != 0) {   first = 0;
1078     while ((read_len = wait_for_reply(from_lsa, to)) != 0) {
1079   t2 = monotonic_us();   t2 = monotonic_us();
1080   i = packet_ok(packet, cc, from, seq);   i = packet_ok(read_len, from_lsa, to, seq);
1081   /* Skip short packet */   /* Skip short packet */
1082   if (i == 0)   if (i == 0)
1083   continue;   continue;
1084   if (!gotlastaddr ||  
1085      from->sin_addr.s_addr != lastaddr) {   if (!gotlastaddr
1086   print(packet, cc, from);   || (memcmp(lastaddr, &from_lsa->u.sa, from_lsa->len) != 0)
1087   lastaddr = from->sin_addr.s_addr;   ) {
1088   ++gotlastaddr;   print(read_len, &from_lsa->u.sa, to);
1089     memcpy(lastaddr, &from_lsa->u.sa, from_lsa->len);
1090     gotlastaddr = 1;
1091   }   }
1092    
1093   print_delta_ms(t1, t2);   print_delta_ms(t1, t2);
1094   ip = (struct ip *)packet;   ip = (struct ip *)recv_pkt;
1095   if (op & OPT_TTL_FLAG)  
1096   printf(" (%d)", ip->ip_ttl);   if (from_lsa->u.sa.sa_family == AF_INET)
1097   if (i == -2) {   if (op & OPT_TTL_FLAG)
1098   if (ip->ip_ttl <= 1)   printf(" (%d)", ip->ip_ttl);
1099   printf(" !");  
  ++got_there;  
  break;  
  }  
1100   /* time exceeded in transit */   /* time exceeded in transit */
1101   if (i == -1)   if (i == -1)
1102   break;   break;
1103   code = i - 1;   i--;
1104   switch (code) {   switch (i) {
1105    #if ENABLE_TRACEROUTE6
1106     case ICMP6_DST_UNREACH_NOPORT << 8:
1107     got_there = 1;
1108     break;
1109    #endif
1110   case ICMP_UNREACH_PORT:   case ICMP_UNREACH_PORT:
1111   if (ip->ip_ttl <= 1)   if (ip->ip_ttl <= 1)
1112   printf(" !");   printf(" !");
1113   ++got_there;   got_there = 1;
1114   break;   break;
1115    
1116   case ICMP_UNREACH_NET:   case ICMP_UNREACH_NET:
1117   ++unreachable;  #if ENABLE_TRACEROUTE6 && (ICMP6_DST_UNREACH_NOROUTE != ICMP_UNREACH_NET)
1118     case ICMP6_DST_UNREACH_NOROUTE << 8:
1119    #endif
1120   printf(" !N");   printf(" !N");
1121     ++unreachable;
1122   break;   break;
   
1123   case ICMP_UNREACH_HOST:   case ICMP_UNREACH_HOST:
1124   ++unreachable;  #if ENABLE_TRACEROUTE6
1125     case ICMP6_DST_UNREACH_ADDR << 8:
1126    #endif
1127   printf(" !H");   printf(" !H");
1128     ++unreachable;
1129   break;   break;
   
1130   case ICMP_UNREACH_PROTOCOL:   case ICMP_UNREACH_PROTOCOL:
  ++got_there;  
1131   printf(" !P");   printf(" !P");
1132     got_there = 1;
1133   break;   break;
   
1134   case ICMP_UNREACH_NEEDFRAG:   case ICMP_UNREACH_NEEDFRAG:
  ++unreachable;  
1135   printf(" !F-%d", pmtu);   printf(" !F-%d", pmtu);
1136     ++unreachable;
1137   break;   break;
   
1138   case ICMP_UNREACH_SRCFAIL:   case ICMP_UNREACH_SRCFAIL:
1139   ++unreachable;  #if ENABLE_TRACEROUTE6
1140     case ICMP6_DST_UNREACH_ADMIN << 8:
1141    #endif
1142   printf(" !S");   printf(" !S");
1143     ++unreachable;
1144   break;   break;
   
1145   case ICMP_UNREACH_FILTER_PROHIB:   case ICMP_UNREACH_FILTER_PROHIB:
1146   case ICMP_UNREACH_NET_PROHIB:   /* misuse */   case ICMP_UNREACH_NET_PROHIB:   /* misuse */
  ++unreachable;  
1147   printf(" !A");   printf(" !A");
1148     ++unreachable;
1149   break;   break;
   
1150   case ICMP_UNREACH_HOST_PROHIB:   case ICMP_UNREACH_HOST_PROHIB:
  ++unreachable;  
1151   printf(" !C");   printf(" !C");
1152     ++unreachable;
1153   break;   break;
   
1154   case ICMP_UNREACH_HOST_PRECEDENCE:   case ICMP_UNREACH_HOST_PRECEDENCE:
  ++unreachable;  
1155   printf(" !V");   printf(" !V");
1156     ++unreachable;
1157   break;   break;
   
1158   case ICMP_UNREACH_PRECEDENCE_CUTOFF:   case ICMP_UNREACH_PRECEDENCE_CUTOFF:
  ++unreachable;  
1159   printf(" !C");   printf(" !C");
1160     ++unreachable;
1161   break;   break;
   
1162   case ICMP_UNREACH_NET_UNKNOWN:   case ICMP_UNREACH_NET_UNKNOWN:
1163   case ICMP_UNREACH_HOST_UNKNOWN:   case ICMP_UNREACH_HOST_UNKNOWN:
  ++unreachable;  
1164   printf(" !U");   printf(" !U");
1165     ++unreachable;
1166   break;   break;
   
1167   case ICMP_UNREACH_ISOLATED:   case ICMP_UNREACH_ISOLATED:
  ++unreachable;  
1168   printf(" !I");   printf(" !I");
1169     ++unreachable;
1170   break;   break;
   
1171   case ICMP_UNREACH_TOSNET:   case ICMP_UNREACH_TOSNET:
1172   case ICMP_UNREACH_TOSHOST:   case ICMP_UNREACH_TOSHOST:
  ++unreachable;  
1173   printf(" !T");   printf(" !T");
1174     ++unreachable;
1175   break;   break;
   
1176   default:   default:
1177     printf(" !<%d>", i);
1178   ++unreachable;   ++unreachable;
  printf(" !<%d>", code);  
1179   break;   break;
1180   }   }
1181   break;   break;
1182   }   }
1183   if (cc == 0)   /* there was no packet at all? */
1184   printf(" *");   if (read_len == 0)
1185   (void)fflush(stdout);   printf("  *");
1186   }   }
1187   bb_putchar('\n');   bb_putchar('\n');
1188   if (got_there ||   if (got_there
1189      (unreachable > 0 && unreachable >= nprobes - 1))   || (unreachable > 0 && unreachable >= nprobes - 1)
1190     ) {
1191   break;   break;
1192     }
1193   }   }
1194    
1195   return 0;   return 0;
1196  }  }
1197    
1198    int traceroute_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1199    int traceroute_main(int argc UNUSED_PARAM, char **argv)
1200    {
1201     return common_traceroute_main(0, argv);
1202    }
1203    
1204    #if ENABLE_TRACEROUTE6
1205    int traceroute6_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1206    int traceroute6_main(int argc UNUSED_PARAM, char **argv)
1207    {
1208     return common_traceroute_main(OPT_IPV6, argv);
1209    }
1210    #endif

Legend:
Removed from v.983  
changed lines
  Added in v.984