changeset 1157:f10898e637ea draft

IPv6 support is added into traceroute command. traceroute6 is made as an old toy of traceroute.
author Ashwini Sharma <ak.ashwini1981@gmail.com>
date Mon, 23 Dec 2013 07:15:13 -0600
parents faf7117c4489
children 81c64569c16e
files toys/pending/traceroute.c
diffstat 1 files changed, 401 insertions(+), 203 deletions(-) [+]
line wrap: on
line diff
--- a/toys/pending/traceroute.c	Mon Dec 23 06:49:38 2013 -0600
+++ b/toys/pending/traceroute.c	Mon Dec 23 07:15:13 2013 -0600
@@ -2,25 +2,30 @@
  *
  * Copyright 2012 Madhur Verma <mad.flexi@gmail.com>
  * Copyright 2013 Kyungwan Han <asura321@gmail.com>
+ * Copyright 2013 Bilal Qureshi <bilal.jmi@gmail.com>
+ * Copyright 2013 Ashwini Kumar <ak.ashwini@gmail.com>
  *
  * No Standard
 
-USE_TRACEROUTE(NEWTOY(traceroute, "<1>2f#<1>255=1z#<0>86400=0g*w#<0>86400=5t#<0>255=0s:q#<1>255=3p#<1>65535=33434m#<1>255=30rvndlIUF64", TOYFLAG_USR|TOYFLAG_BIN))
-
+USE_TRACEROUTE(NEWTOY(traceroute, "<1>2i:f#<1>255=1z#<0>86400=0g*w#<0>86400=5t#<0>255=0s:q#<1>255=3p#<1>65535=33434m#<1>255=30rvndlIUF64", TOYFLAG_STAYROOT|TOYFLAG_USR|TOYFLAG_BIN))
+USE_TRACEROUTE(OLDTOY(traceroute6,traceroute, OPTSTR_traceroute, TOYFLAG_STAYROOT|TOYFLAG_USR|TOYFLAG_BIN))
 config TRACEROUTE
   bool "traceroute"
-  default n
+  default y
   help
-    usage: traceroute [-FUIldnvr] [-f 1ST_TTL] [-m MAXTTL] [-p PORT] [-q PROBES]
+    usage: traceroute [-46FUIldnvr] [-f 1ST_TTL] [-m MAXTTL] [-p PORT] [-q PROBES]
     [-s SRC_IP] [-t TOS] [-w WAIT_SEC] [-g GATEWAY] [-i IFACE] [-z PAUSE_MSEC] HOST [BYTES]
+    
+    traceroute6 [-dnrv] [-m MAXTTL] [-p PORT] [-q PROBES][-s SRC_IP] [-t TOS] [-w WAIT_SEC] 
+      [-i IFACE] HOST [BYTES]
 
     Trace the route to HOST
 
-    -F    Set the don't fragment bit
-    -U    Use UDP datagrams instead of ICMP ECHO
-    -I    Use ICMP ECHO instead of UDP datagrams
-    -l    Display the TTL value of the returned packet
-    -f    Start from the 1ST_TTL hop (instead from 1)(RANGE 1 to 255)
+    -4,-6 Force IP or IPv6 name resolution 
+    -F    Set the don't fragment bit (supports IPV4 only)
+    -U    Use UDP datagrams instead of ICMP ECHO (supports IPV4 only)
+    -I    Use ICMP ECHO instead of UDP datagrams (supports IPV4 only)
+    -l    Display the TTL value of the returned packet (supports IPV4 only)
     -d    Set SO_DEBUG options to socket
     -n    Print numeric addresses
     -v    verbose
@@ -31,13 +36,17 @@
     -s    IP address to use as the source address
     -t    Type-of-service in probe packets (default 0)(RANGE 0 to 255)
     -w    Time in seconds to wait for a response (default 3)(RANGE 0 to 86400)
-    -g    Loose source route gateway (8 max)
-    -z    Pause Time in milisec (default 0)(RANGE 0 to 86400)
+    -g    Loose source route gateway (8 max) (supports IPV4 only)
+    -z    Pause Time in milisec (default 0)(RANGE 0 to 86400) (supports IPV4 only)
+    -f    Start from the 1ST_TTL hop (instead from 1)(RANGE 1 to 255) (supports IPV4 only)
+    -i    Specify a network interface to operate with
 */
 #define FOR_traceroute
 #include "toys.h"
 #include <netinet/udp.h>
 #include <netinet/ip_icmp.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
 
 GLOBALS(
   long max_ttl;
@@ -49,29 +58,33 @@
   struct arg_list *loose_source;
   long pause_time;
   long first_ttl;
+  char *iface;
+
+  uint32_t gw_list[9];
   int recv_sock;
   int snd_sock;
   unsigned msg_len;
-  struct ip *packet;
-  uint32_t gw_list[9];
+  char *packet;
   uint32_t ident;
+  int istraceroute6;
 )
 
+#ifndef SOL_IPV6
+# define SOL_IPV6 IPPROTO_IPV6
+#endif
 
-#define ICMP_HD_SIZE    8
-#define send_icmp      ((struct icmp *)(TT.packet +   1))
-#define send_udp      ((struct udphdr *)(TT.packet + 1))
+#define ICMP_HD_SIZE4  8
+#define USEC           1000000ULL
 
 struct payload_s {
-  unsigned char seq;
-  unsigned char ttl;
-  struct timeval tv; 
+  uint32_t seq;
+  uint32_t ident;
 };
 
-static struct payload_s *send_data;
-static struct sockaddr_in dest, from;
+char addr_str[INET6_ADDRSTRLEN];
+struct sockaddr_storage dest;
 
-// Computes and returns checksum SUM of buffer P of length LEN
+//Compute checksum SUM of buffer P of length LEN
 static u_int16_t in_cksum(u_int16_t *p, u_int len)
 {
   u_int32_t sum = 0;
@@ -93,94 +106,131 @@
   return (~sum);
 }
 
-/*
- * sends a single probe packet with sequence SEQ and
- * time-to-live TTL
- */
-static void send_probe(int seq, int ttl)
+//sends a single probe packet with sequence(SEQ) and time-to-live(TTL)
+static void send_probe4(int seq, int ttl)
 {
   int res, len;
   void *out;
+  struct payload_s *send_data4 = (struct payload_s *)(TT.packet);
+  struct icmp *send_icmp4 = (struct icmp *)(TT.packet);
 
   if (toys.optflags & FLAG_U) {
-    send_data->seq = seq;
-    send_data->ttl = ttl;
-    dest.sin_port = TT.port + seq;
+    send_data4->seq = seq;
+    send_data4->ident = TT.ident;
+    ((struct sockaddr_in *)&dest)->sin_port = TT.port + seq;
+    out = send_data4;
   } else {
-    send_icmp->icmp_seq = htons(seq);
-    send_icmp->icmp_cksum = 0;
-    send_icmp->icmp_cksum = in_cksum((uint16_t *) send_icmp, TT.msg_len - sizeof(struct ip));
-    if (send_icmp->icmp_cksum == 0) send_icmp->icmp_cksum = 0xffff;
+    send_icmp4->icmp_type = ICMP_ECHO;
+    send_icmp4->icmp_id = htons(TT.ident);
+    send_icmp4->icmp_seq = htons(seq);
+    send_icmp4->icmp_cksum = 0;
+    send_icmp4->icmp_cksum = in_cksum((uint16_t *) send_icmp4, TT.msg_len);
+    if (send_icmp4->icmp_cksum == 0) send_icmp4->icmp_cksum = 0xffff;
+    out = send_icmp4;
   }
 
   res = setsockopt(TT.snd_sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
   if (res < 0) perror_exit("setsockopt ttl %d", ttl);
 
-  if (toys.optflags & FLAG_U) {
-    out = send_data;
-    len = sizeof(struct payload_s);
-  } else {
-    out = send_icmp;
-    len = TT.msg_len - sizeof(struct ip);
-  }
-  res = sendto(TT.snd_sock, out, len, 0, (struct sockaddr *) &dest, sizeof(dest));
+  len = TT.msg_len;
+  res = sendto(TT.snd_sock, out, len, 0, (struct sockaddr *) &dest, 
+      sizeof(struct sockaddr_in));
   if (res != len) perror_exit(" sendto");
 }
 
-static void resolve_addr(char *host, int type, int proto, struct sockaddr_in *sock)
+//sends a single probe packet with sequence(SEQ) and time-to-live(TTL)
+static void send_probe6(int seq, int ttl)
 {
-  struct addrinfo *rp, *info, hint;
+  void *out;
+  struct payload_s *send_data6 = (struct payload_s *) (TT.packet);
+
+  send_data6->seq = seq;
+  send_data6->ident = TT.ident;
+  ((struct sockaddr_in6 *)&dest)->sin6_port = TT.port;
+
+  if (setsockopt(TT.snd_sock, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl, 
+        sizeof(ttl)) < 0) error_exit("setsockopt ttl %d", ttl);
+
+  out = send_data6;
+
+  if (sendto(TT.snd_sock, out, TT.msg_len, 0,
+        (struct sockaddr *) &dest, sizeof(struct sockaddr_in6)) < 0)
+    perror_exit("sendto");
+}
+
+static void set_flag_dr(int sock)
+{
+  int set = 1;
+  if ((toys.optflags & FLAG_d) && (setsockopt(sock,SOL_SOCKET, SO_DEBUG,
+          &set, sizeof(set)) < 0)) perror_exit("SO_DEBUG failed ");
+
+  if ((toys.optflags & FLAG_r) && (setsockopt(sock, SOL_SOCKET, SO_DONTROUTE,
+          &set, sizeof(set)) < 0)) perror_exit("SO_DONTROUTE failed ");
+}
+
+static void bind_to_interface(int sock)
+{
+  struct ifreq ifr;
+
+  snprintf(ifr.ifr_name, IFNAMSIZ, "%s", TT.iface);
+  if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)))
+      perror_msg("can't bind to interface %s", TT.iface);
+}
+
+static void resolve_addr(char *host, int family, int type, int proto, void *sock)
+{
+  struct addrinfo *info, hint;
   int ret;
 
   memset(&hint, 0, sizeof(hint));
-  hint.ai_family = AF_INET;
+  hint.ai_family = family;
   hint.ai_socktype = type;
   hint.ai_protocol = proto;
 
   ret = getaddrinfo(host, NULL, &hint, &info);
-  if (ret || !info) perror_exit("bad address:  %s ", host);
+  if (ret || !info) error_exit("bad address:  %s ", host);
 
-  for (rp = info; rp; rp = rp->ai_next) {  
-    if (rp->ai_addrlen == sizeof(struct sockaddr_in)) {
-      memcpy(sock, rp->ai_addr, rp->ai_addrlen);
-      break;                               
-    }
-  }
-  freeaddrinfo(info);                      
-  if (!rp) perror_exit("Resolve failed ");
+  memcpy(sock, info->ai_addr, info->ai_addrlen);
+  freeaddrinfo(info);
 }
 
 static void do_trace()
 {
   int seq, fexit, ttl, tv = TT.wait_time * 1000;
   struct pollfd pfd[1];
+  struct sockaddr_storage from;
 
+  memset(&from, 0, sizeof(from));
   pfd[0].fd = TT.recv_sock;
   pfd[0].events = POLLIN;
+
   for (ttl = TT.first_ttl; ttl <= TT.max_ttl; ++ttl) {
-    int probe, dest_reach = 0;
+    int probe, dest_reach = 0, print_verbose = 1;
     struct timeval t1, t2;
-    struct sockaddr_in last;
+    struct sockaddr_storage last_addr;
 
-    memset(&last, 0, sizeof(last));
+    memset(&last_addr, 0, sizeof(last_addr));
     fexit = 0;
-    printf("%2d", ttl);
+    xprintf("%2d", ttl);
 
     for (probe = 0, seq = 0; probe < TT.ttl_probes; ++probe) {
       int res = 0, tleft;
 
       fflush(NULL);
-      if (probe && (toys.optflags & FLAG_z)) usleep(TT.pause_time * 1000);
+      if (!TT.istraceroute6)
+        if (probe && (toys.optflags & FLAG_z)) usleep(TT.pause_time * 1000);
 
-      send_probe(++seq, ttl);
+      if (!TT.istraceroute6) send_probe4(++seq, ttl);
+      else send_probe6(++seq, ttl);
       gettimeofday(&t1, NULL);
       t2 = t1;
 
       while ((tleft = (int)(tv - ((unsigned long long)(t2.tv_sec * 1000ULL 
                   + t2.tv_usec/1000) - (unsigned long long)(t1.tv_sec * 1000ULL
                     + t1.tv_usec/1000)))) >= 0) {
-        if (!(res = poll(pfd, 1, tleft))) {                         
-          printf("  *");                        
+        unsigned delta = 0;
+        if (!(res = poll(pfd, 1, tleft))) { 
+          xprintf("  *"); 
           break;
         }
         gettimeofday(&t2, NULL);
@@ -188,18 +238,21 @@
           if (errno != EINTR) perror_exit("poll");
           continue;
         }
+        delta = (t2.tv_sec * USEC + t2.tv_usec)
+          - (t1.tv_sec * USEC + t1.tv_usec);
 
         if (pfd[0].revents) {
-          unsigned addrlen = sizeof(from);
-          struct ip *rcv_pkt = (struct ip*) toybuf;
-          int rcv_len, pmtu = 0;
+          unsigned addrlen = sizeof(struct sockaddr_storage);
+          int rcv_len, icmp_res = 0;
 
-          rcv_len = recvfrom(TT.recv_sock, rcv_pkt, sizeof(toybuf),
+          rcv_len = recvfrom(TT.recv_sock, toybuf, sizeof(toybuf),
               MSG_DONTWAIT, (struct sockaddr *) &from, &addrlen);
           if (rcv_len <= 0) continue;
-          else {
+
+          if (!TT.istraceroute6) {
+            int pmtu = 0;
+            struct ip *rcv_pkt = (struct ip*) toybuf;
             struct icmp *ricmp;
-            int icmp_res = 0;
 
             ricmp = (struct icmp *) ((void*)rcv_pkt + (rcv_pkt->ip_hl << 2));
             if (ricmp->icmp_code == ICMP_UNREACH_NEEDFRAG)
@@ -210,234 +263,379 @@
                 || ricmp->icmp_type == ICMP_UNREACH
                 || ricmp->icmp_type == ICMP_ECHOREPLY) {
 
-              struct ip *hip;
               struct udphdr *hudp;
               struct icmp *hicmp;
+              struct ip *hip = &ricmp->icmp_ip;
 
-              hip = &ricmp->icmp_ip;
               if (toys.optflags & FLAG_U) {
-                hudp = (struct udphdr*) (hip + (hip->ip_hl << 2));
-                if ((hip->ip_hl << 2) + 12 <= (rcv_len - (rcv_pkt->ip_hl << 2))
+                hudp = (struct udphdr*) ((char*)hip + (hip->ip_hl << 2));
+                if ((hip->ip_hl << 2) + 12 <=(rcv_len - (rcv_pkt->ip_hl << 2))
                     && hip->ip_p == IPPROTO_UDP
-                    && hudp->dest == htons(TT.port + seq))
-                  icmp_res = (ricmp->icmp_type == ICMP_TIMXCEED ?-1 : ricmp->icmp_code);
+                    && hudp->dest == (TT.port + seq))
+                  icmp_res = (ricmp->icmp_type == ICMP_TIMXCEED ? -1 :
+                      ricmp->icmp_code);
               } else {
                 hicmp = (struct icmp *) ((void*)hip + (hip->ip_hl << 2));
-                if (ricmp->icmp_type == ICMP_ECHOREPLY && ricmp->icmp_id == htons(TT.ident)
-                    && ricmp->icmp_seq == htons(seq))
+                if (ricmp->icmp_type == ICMP_ECHOREPLY 
+                    && ricmp->icmp_id == ntohs(TT.ident)
+                    && ricmp->icmp_seq == ntohs(seq))
                   icmp_res = ICMP_UNREACH_PORT;
-
-                if ((hip->ip_hl << 2) + ICMP_HD_SIZE <= (rcv_len - (rcv_pkt->ip_hl << 2))
+                else if ((hip->ip_hl << 2) + ICMP_HD_SIZE4 
+                    <= (rcv_len - (rcv_pkt->ip_hl << 2))
                     && hip->ip_p == IPPROTO_ICMP
                     && hicmp->icmp_id == htons(TT.ident)
                     && hicmp->icmp_seq == htons(seq))
-                  icmp_res = (ricmp->icmp_type == ICMP_TIMXCEED ? -1 : ricmp->icmp_code);
+                  icmp_res = (ricmp->icmp_type == ICMP_TIMXCEED ? -1 : 
+                      ricmp->icmp_code);
               }
             }
             if (!icmp_res) continue;
 
             if (addrlen > 0) {
-              unsigned delta = (t2.tv_sec * 1000000ULL + t2.tv_usec)
-                - (t1.tv_sec * 1000000ULL + t1.tv_usec);
-
-              if (memcmp(&last.sin_addr, &from.sin_addr, sizeof(struct in_addr))) {
+              if (memcmp(&((struct sockaddr_in *)&last_addr)->sin_addr, 
+                    &((struct sockaddr_in *)&from)->sin_addr, 
+                    sizeof(struct in_addr))) {
                 if (!(toys.optflags & FLAG_n)) {
                   char host[NI_MAXHOST];
-                  if (!getnameinfo((struct sockaddr *) &from,
-                        sizeof(from), host, NI_MAXHOST, NULL, 0, 0))
-                    printf("  %s (", host);
-                  else printf(" %s (", inet_ntoa(from.sin_addr));
+                  if (!getnameinfo((struct sockaddr *) &from, 
+                        sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, 0))
+                    xprintf("  %s (", host);
+                  else xprintf(" %s (", inet_ntoa(
+                          ((struct sockaddr_in *)&from)->sin_addr));
                 }
-                printf(" %s", inet_ntoa(from.sin_addr));
-                if (!(toys.optflags & FLAG_n) ) printf(")");
-                last = from;
+                xprintf(" %s", inet_ntoa(
+                      ((struct sockaddr_in *)&from)->sin_addr));
+                if (!(toys.optflags & FLAG_n)) xprintf(")");
+                memcpy(&last_addr, &from, sizeof(from));
               }
-              printf("  %u.%03u ms", delta / 1000, delta % 1000);
-              if (toys.optflags & FLAG_l) printf(" (%d)", rcv_pkt->ip_ttl);
+              xprintf("  %u.%03u ms", delta / 1000, delta % 1000);
+              if (toys.optflags & FLAG_l) xprintf(" (%d)", rcv_pkt->ip_ttl);
               if (toys.optflags & FLAG_v) {
-                printf(" %d bytes from %s : icmp type %d code %d\t",
-                    rcv_len, inet_ntoa(from.sin_addr),
+                xprintf(" %d bytes from %s : icmp type %d code %d\t",
+                    rcv_len, inet_ntoa(((struct sockaddr_in *)&from)->sin_addr),
                     ricmp->icmp_type, ricmp->icmp_code);
               }
-            } else printf("\t!H");
+            } else xprintf("\t!H");
 
             switch (icmp_res) {
               case ICMP_UNREACH_PORT:
-                if (rcv_pkt->ip_ttl <= 1) printf(" !");
+                if (rcv_pkt->ip_ttl <= 1) xprintf(" !");
                 dest_reach = 1;
                 break;
               case ICMP_UNREACH_NET:
-                printf(" !N");
+                xprintf(" !N");
                 ++fexit;
                 break;
               case ICMP_UNREACH_HOST:
-                printf(" !H");
+                xprintf(" !H");
                 ++fexit;
                 break;
               case ICMP_UNREACH_PROTOCOL:
-                printf(" !P");
+                xprintf(" !P");
                 dest_reach = 1;
                 break;
               case ICMP_UNREACH_NEEDFRAG:
-                printf(" !F-%d", pmtu);
+                xprintf(" !F-%d", pmtu);
                 ++fexit;
                 break;
               case ICMP_UNREACH_SRCFAIL:
-                printf(" !S");
+                xprintf(" !S");
                 ++fexit;
                 break;
               case ICMP_UNREACH_FILTER_PROHIB:
               case ICMP_UNREACH_NET_PROHIB:
-                printf(" !A");
+                xprintf(" !A");
                 ++fexit;
                 break;
               case ICMP_UNREACH_HOST_PROHIB:
-                printf(" !C");
+                xprintf(" !C");
                 ++fexit;
                 break;
               case ICMP_UNREACH_HOST_PRECEDENCE:
-                printf(" !V");
+                xprintf(" !V");
                 ++fexit;
                 break;
               case ICMP_UNREACH_PRECEDENCE_CUTOFF:
-                printf(" !C");
+                xprintf(" !C");
                 ++fexit;
                 break;
               case ICMP_UNREACH_NET_UNKNOWN:  
               case ICMP_UNREACH_HOST_UNKNOWN:
-                printf(" !U");
+                xprintf(" !U");
                 ++fexit;
                 break;
               case ICMP_UNREACH_ISOLATED:
-                printf(" !I");
+                xprintf(" !I");
                 ++fexit;
                 break;
               case ICMP_UNREACH_TOSNET:
               case ICMP_UNREACH_TOSHOST:
-                printf(" !T");
+                xprintf(" !T");
                 ++fexit;
                 break;
+              default:
+                break;
+            }
+            break;
+          } else {
+            struct icmp6_hdr *ricmp  = (struct icmp6_hdr *) toybuf;
+
+            if ((ricmp->icmp6_type == ICMP6_TIME_EXCEEDED
+                  && ricmp->icmp6_code == ICMP6_TIME_EXCEED_TRANSIT)
+                || ricmp->icmp6_type == ICMP6_DST_UNREACH
+                || ricmp->icmp6_type == ICMP6_ECHO_REPLY) {
+
+              struct ip6_hdr *hip;
+              struct udphdr *hudp;
+              int hdr_next;
+
+              hip = (struct ip6_hdr *)(ricmp + 1);
+              hudp = (struct udphdr*) (hip + 1);
+              hdr_next = hip->ip6_nxt;
+              if (hdr_next == IPPROTO_FRAGMENT) {
+                hdr_next = *(unsigned char*)hudp;
+                hudp++;
+              }
+
+              if (hdr_next == IPPROTO_UDP) {
+                struct payload_s *pkt = (struct payload_s*)(hudp + 1);
+                if ((pkt->ident == TT.ident) && (pkt->seq == seq))
+                  icmp_res = (ricmp->icmp6_type == ICMP6_TIME_EXCEEDED) ? -1 :
+                    ricmp->icmp6_code;
+              }
+            }
+
+            if (!icmp_res) continue;
+            if (addrlen > 0) {
+              if (memcmp(&((struct sockaddr_in6 *)&last_addr)->sin6_addr, 
+                    &((struct sockaddr_in6 *)&from)->sin6_addr, 
+                    sizeof(struct in6_addr))) {
+                if (!(toys.optflags & FLAG_n)) {
+                  char host[NI_MAXHOST];
+                  if (!getnameinfo((struct sockaddr *) &from,
+                        sizeof(from), host, sizeof(host), NULL, 0, 0))
+                    xprintf("  %s (", host);
+                }
+                memset(addr_str, '\0', INET6_ADDRSTRLEN);
+                inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&from)->sin6_addr,
+                    addr_str, INET6_ADDRSTRLEN);
+                xprintf(" %s", addr_str);
+
+                if (!(toys.optflags & FLAG_n)) xprintf(")");
+                memcpy(&last_addr,&from,sizeof(from));
+              }
+
+              if (toys.optflags & FLAG_v) {
+                if(print_verbose){
+                  memset(addr_str, '\0', INET6_ADDRSTRLEN);
+                  inet_ntop(AF_INET6, &((struct sockaddr_in6 *)
+                        &from)->sin6_addr, addr_str, INET6_ADDRSTRLEN);
+                  rcv_len -= sizeof(struct ip6_hdr);
+                  xprintf(" %d bytes to %s ", rcv_len, addr_str);
+                }
+              }
+              xprintf("  %u.%03u ms", delta / 1000, delta % 1000);
+              delta = 0;
+
+            } else xprintf("\t!H");
+
+            switch (icmp_res) {
+              case ICMP6_DST_UNREACH_NOPORT:
+                ++fexit;
+                dest_reach = 1;
+                break;
+              case ICMP6_DST_UNREACH_NOROUTE:
+                xprintf(" !N");
+                ++fexit;
+                break;
+              case ICMP6_DST_UNREACH_ADDR:
+                xprintf(" !H");
+                ++fexit;
+                break;
+              case ICMP6_DST_UNREACH_ADMIN:
+                xprintf(" !S");
+                ++fexit;
+                break;
+              default:
+                break;
             }
             break;
           }
-        }
+        } //revents
       }
+      print_verbose = 0;
     }
     xputc('\n');
-    if (!memcmp(&from.sin_addr, &dest.sin_addr, sizeof(struct in_addr))
-        || dest_reach || (fexit >= TT.ttl_probes -1))
-      break;
+    if(!TT.istraceroute6) {
+      if (!memcmp(&((struct sockaddr_in *)&from)->sin_addr, 
+            &((struct sockaddr_in *)&dest)->sin_addr, sizeof(struct in_addr))
+          || dest_reach || (fexit && fexit >= TT.ttl_probes -1))
+        break;
+    } else if (dest_reach || (fexit > 0 && fexit >= TT.ttl_probes -1)) break;
   }
-
 }
 
 void traceroute_main(void)
 {
-  unsigned opt_len = 0, pack_size, tyser = 0;
-  int set = 1, lsrr = 0;
+  unsigned opt_len = 0, pack_size = 0, tyser = 0;
+  int lsrr = 0, set = 1;
+  
+  if(!(toys.optflags & FLAG_4) && 
+      (inet_pton(AF_INET6, toys.optargs[0], (void*)&dest)))
+    toys.optflags |= FLAG_6;
 
-  if (toys.optflags & FLAG_g) {
-    struct arg_list *node;
+  memset(&dest, 0, sizeof(dest));
+  if (toys.optflags & FLAG_6) TT.istraceroute6 = 1;
+  else TT.istraceroute6 = toys.which->name[10] == '6';
+
+  if(!TT.istraceroute6 && (toys.optflags & FLAG_g)) {
+      struct arg_list *node;
+
+      for (node = TT.loose_source; node; node = node->next, lsrr++) {
+        struct sockaddr_in sin;
 
-    for (node = TT.loose_source; node; node = node->next, lsrr++) {
-      struct sockaddr_in sin;
+        memset( &sin, 0, sizeof(sin));
+        if (lsrr >= 8) error_exit("no more than 8 gateways"); // NGATEWAYS
+        resolve_addr(node->arg, AF_INET, SOCK_STREAM, 0, &sin);
+        TT.gw_list[lsrr] = sin.sin_addr.s_addr;
+      }
+      opt_len = (lsrr + 1) * sizeof(TT.gw_list[0]);
+  } else TT.first_ttl = 1;
+
+  TT.msg_len = pack_size = ICMP_HD_SIZE4; //udp payload is also 8bytes
+  if (toys.optargs[1])
+    TT.msg_len = atolx_range(toys.optargs[1], pack_size, 32768);//max packet size
 
-      memset( &sin, 0, sizeof(sin));
-      if (lsrr >= 8) error_exit("no more than 8 gateways"); // NGATEWAYS
-      resolve_addr(node->arg, SOCK_STREAM, 0, &sin);
-      TT.gw_list[lsrr] = sin.sin_addr.s_addr;
-    }
-    opt_len = (lsrr + 1) * sizeof(TT.gw_list[0]);
+  TT.recv_sock = xsocket((TT.istraceroute6 ? AF_INET6 : AF_INET), SOCK_RAW,
+      (TT.istraceroute6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP));
+
+  if (TT.istraceroute6) {
+    int two = 2;
+#ifdef IPV6_RECVPKTINFO
+    setsockopt(TT.recv_sock, SOL_IPV6, IPV6_RECVPKTINFO, &set, 
+        sizeof(set));
+    setsockopt(TT.recv_sock, SOL_IPV6, IPV6_2292PKTINFO, &set, 
+        sizeof(set));
+#else
+    setsockopt(TT.recv_sock, SOL_IPV6, IPV6_PKTINFO, &set, sizeof(set));
+#endif
+
+    if (setsockopt(TT.recv_sock, SOL_RAW, IPV6_CHECKSUM, &two, 
+          sizeof(two)) < 0)  perror_exit("setsockopt RAW_CHECKSUM");
   }
 
-  pack_size = sizeof(struct ip) + opt_len;
-  pack_size += (toys.optflags & FLAG_U) ? sizeof(struct udphdr) 
-    + sizeof(struct payload_s) : ICMP_HD_SIZE;
+  set_flag_dr(TT.recv_sock);
+
+  if (!TT.istraceroute6) {
+    if (toys.optflags & FLAG_U) 
+      TT.snd_sock = xsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+    else TT.snd_sock = xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+
+    if (toys.optflags & FLAG_i) bind_to_interface(TT.snd_sock);
 
-  if (toys.optargs[1])
-    TT.msg_len = get_int_value(toys.optargs[1], pack_size, 32768);//max packet size
+    resolve_addr(toys.optargs[0], AF_INET, ((toys.optflags & FLAG_U) ? 
+          SOCK_DGRAM : SOCK_RAW), ((toys.optflags & FLAG_U) ? IPPROTO_UDP : 
+            IPPROTO_ICMP), &dest);
+    if (lsrr > 0) {
+      unsigned char optlist[MAX_IPOPTLEN];
+      unsigned size;
+
+      TT.gw_list[lsrr] = ((struct sockaddr_in *)&dest)->sin_addr.s_addr;
+      ++lsrr;
+
+      optlist[0] = IPOPT_NOP;
+      optlist[1] = IPOPT_LSRR;// loose source route option 
+      size = lsrr * sizeof(TT.gw_list[0]);
+      optlist[2] = size + 3;
+      optlist[3] = IPOPT_MINOFF;
+      memcpy(optlist + 4, TT.gw_list, size);
 
-  TT.msg_len = (TT.msg_len < pack_size) ? pack_size : TT.msg_len;
-  TT.recv_sock = xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
-  if (toys.optflags & FLAG_d
-      && (setsockopt(TT.recv_sock, SOL_SOCKET, SO_DEBUG, &set, sizeof(set)) < 0))
-    perror_exit("SO_DEBUG failed ");
-  if (toys.optflags & FLAG_r
-      && (setsockopt(TT.recv_sock, SOL_SOCKET, SO_DONTROUTE, &set, sizeof(set)) < 0))
-    perror_exit("SO_DONTROUTE failed ");
+      if (setsockopt(TT.snd_sock, IPPROTO_IP, IP_OPTIONS,
+            (char *)optlist, size + sizeof(TT.gw_list[0])) < 0)
+        perror_exit("LSRR IP_OPTIONS");
+    }
+  } else TT.snd_sock = xsocket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+
+  if (setsockopt(TT.snd_sock, SOL_SOCKET, SO_SNDBUF, &TT.msg_len, 
+        sizeof(TT.msg_len)) < 0) perror_exit("SO_SNDBUF failed ");
 
-  if (toys.optflags & FLAG_U) TT.snd_sock = xsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
-  else TT.snd_sock = xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+  if (!TT.istraceroute6) {
+    if ((toys.optflags & FLAG_t) && 
+        setsockopt(TT.snd_sock, IPPROTO_IP, IP_TOS, &tyser, sizeof(tyser)) < 0)
+      perror_exit("IP_TOS %d failed ", TT.tos);
+
+#ifdef IP_DONTFRAG
+    if ((toys.optflags & FLAG_F) &&
+        (setsockopt(TT.snd_sock, IPPROTO_IP, IP_DONTFRAG, &set, 
+                    sizeof(set)) < 0)) perror_exit("IP_DONTFRAG failed ");
+#endif
+  } else if (setsockopt(TT.snd_sock, IPPROTO_IPV6, IPV6_TCLASS, &TT.tos,
+        sizeof(TT.tos)) < 0) perror_exit("IPV6_TCLASS %d failed ", TT.tos);
+
+  set_flag_dr(TT.snd_sock);
+  TT.packet = xzalloc(TT.msg_len);
+  TT.ident = getpid();
 
-  resolve_addr(toys.optargs[0], ((toys.optflags & FLAG_U) ? SOCK_DGRAM : SOCK_RAW),
-      ((toys.optflags & FLAG_U) ? IPPROTO_UDP : IPPROTO_ICMP), &dest);
+  if (!TT.istraceroute6) {
+    if (!(toys.optflags & FLAG_U)) TT.ident |= 0x8000;
+    if (toys.optflags & FLAG_s) {
+      struct sockaddr_in source;
 
-  if (lsrr > 0) {
-    unsigned char optlist[MAX_IPOPTLEN];
-    unsigned size;
+      memset(&source, 0, sizeof(source));
+      if (!inet_aton(TT.src_ip, &(source.sin_addr)))
+        error_exit("bad address: %s", TT.src_ip);
+      if (setsockopt(TT.snd_sock, IPPROTO_IP, IP_MULTICAST_IF,
+            (struct sockaddr*)&source, sizeof(struct sockaddr_in)))
+        perror_exit("can't set multicast source interface");
+      if (bind(TT.snd_sock,(struct sockaddr*)&source, 
+            sizeof(struct sockaddr_in)) < 0) perror_exit("bind");
+    }
 
-    TT.gw_list[lsrr] = dest.sin_addr.s_addr;
-    ++lsrr;
+    if(TT.first_ttl > TT.max_ttl) 
+      error_exit("ERROR :Range for -f is 1 to %d (max ttl)", TT.max_ttl);
+
+    xprintf("traceroute to %s(%s)", toys.optargs[0], 
+           inet_ntoa(((struct sockaddr_in *)&dest)->sin_addr));
+  } else {
+    if (toys.optflags & FLAG_i) bind_to_interface(TT.snd_sock);
+
+    resolve_addr(toys.optargs[0], AF_INET6, SOCK_DGRAM, IPPROTO_UDP, &dest);
+    if (toys.optflags & FLAG_s) {
+      struct sockaddr_in6 source;
 
-    optlist[0] = IPOPT_NOP;
-    optlist[1] = IPOPT_LSRR;// loose source route option 
-    size = lsrr * sizeof(TT.gw_list[0]);
-    optlist[2] = size + 3;
-    optlist[3] = IPOPT_MINOFF;
-    memcpy(optlist + 4, TT.gw_list, size);
+      memset(&source, 0, sizeof(source));
+      if(inet_pton(AF_INET6, TT.src_ip, &(source.sin6_addr)) <= 0)
+        error_exit("bad address: %s", TT.src_ip);
+
+      if (bind(TT.snd_sock,(struct sockaddr*)&source, 
+            sizeof(struct sockaddr_in6)) < 0)
+        error_exit("bind: Cannot assign requested address");
+    } else {
+      struct sockaddr_in6 prb;
+      socklen_t len = sizeof(prb);
+      int p_fd = xsocket(AF_INET6, SOCK_DGRAM, 0);
+      if (toys.optflags & FLAG_i) bind_to_interface(p_fd);
 
-    if (setsockopt(TT.snd_sock, IPPROTO_IP, IP_OPTIONS,
-          (char *)optlist, size + sizeof(TT.gw_list[0])) < 0)
-      perror_exit("LSRR IP_OPTIONS");
+      ((struct sockaddr_in6 *)&dest)->sin6_port = htons(1025);
+      if (connect(p_fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_in6)) < 0)
+        perror_exit("can't connect to remote host");
+      if(getsockname(p_fd, (struct sockaddr *)&prb, &len)) 
+        error_exit("probe addr failed");
+      close(p_fd);
+      prb.sin6_port = 0;
+      if (bind(TT.snd_sock, (struct sockaddr*)&prb, 
+            sizeof(struct sockaddr_in6))) perror_exit("bind");
+      if (bind(TT.recv_sock, (struct sockaddr*)&prb, 
+            sizeof(struct sockaddr_in6))) perror_exit("bind");
+    }
+
+    inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&dest)->sin6_addr, 
+              addr_str, INET6_ADDRSTRLEN);
+    xprintf("traceroute6 to %s(%s)", toys.optargs[0], addr_str);
   }
 
-  if (setsockopt(TT.snd_sock, SOL_SOCKET, SO_SNDBUF, &TT.msg_len, sizeof(TT.msg_len)) < 0)
-    perror_exit("SO_SNDBUF failed ");
-
-  if ((toys.optflags & FLAG_t) && 
-      setsockopt(TT.snd_sock, IPPROTO_IP, IP_TOS, &tyser, sizeof(tyser)) < 0)
-    perror_exit("IP_TOS %d failed ", TT.tos);
-
-#ifdef IP_DONTFRAG
-  if ((toys.optflags & FLAG_F) &&
-      (setsockopt(TT.snd_sock, IPPROTO_IP, IP_DONTFRAG, &set, sizeof(set)) < 0))
-    perror_exit("IP_DONTFRAG failed ");
-#endif
-
-  if ((toys.optflags & FLAG_d)
-      && (setsockopt(TT.snd_sock, SOL_SOCKET, SO_DEBUG, &set, sizeof(set)) < 0))
-    perror_exit("SO_DEBUG failed ");
-  if ((toys.optflags & FLAG_r) &&
-      (setsockopt(TT.snd_sock, SOL_SOCKET, SO_DONTROUTE, &set, sizeof(set)) < 0))
-    perror_exit("SO_DONTROUTE failed ");
-
-
-  TT.packet = xmalloc(TT.msg_len);
-  TT.ident = getpid();
+  if (toys.optflags & FLAG_s) xprintf(" from %s",TT.src_ip);
+  xprintf(", %ld hops max, %u byte packets\n", TT.max_ttl, TT.msg_len);
 
-  if (toys.optflags & FLAG_U) send_data = (struct payload_s *) (send_udp + 1);
-  else {
-    TT.ident |= 0x8000;
-    send_icmp->icmp_type = ICMP_ECHO;
-    send_icmp->icmp_id = htons(TT.ident);
-  }
-  if (toys.optflags & FLAG_s) {
-    struct sockaddr_in *source =  xzalloc(sizeof(struct sockaddr_in));
-    inet_aton(TT.src_ip, &source->sin_addr);
-    if (setsockopt(TT.snd_sock, IPPROTO_IP, IP_MULTICAST_IF, 
-          (struct sockaddr*)source, sizeof(*source)))
-      perror_exit("can't set multicast source interface");
-    if (bind(TT.snd_sock,(struct sockaddr*)source, sizeof(*source)))
-      perror_exit("bind");
-    free(source);
-  }
-
-  if(TT.first_ttl > TT.max_ttl) 
-    error_exit("ERROR :Range for -f is 1 to %d (max ttl)", TT.max_ttl);
-
-  printf("traceroute to %s(%s)", toys.optargs[0], inet_ntoa(dest.sin_addr));
-  if (toys.optflags & FLAG_s) printf(" from %s", TT.src_ip);
-  printf(", %ld hops max, %u byte packets\n", TT.max_ttl, TT.msg_len);
-  
   do_trace();
 }