| /* | |
| * Modified for NRL 4.4BSD IPv6 release. | |
| * 07/31/96 bgp | |
| * | |
| * Search for "#ifdef NRL" to find the changes. | |
| */ | |
| /* | |
| * Modified for Linux IPv6 by Pedro Roque <[email protected]> | |
| * 31/07/1996 | |
| * | |
| * As ICMP error messages for IPv6 now include more than 8 bytes | |
| * UDP datagrams are now sent via an UDP socket instead of magic | |
| * RAW socket tricks. | |
| * | |
| * Original copyright and comments left intact. They might not | |
| * match the code anymore. | |
| */ | |
| /*- | |
| * Copyright (c) 1990, 1993 | |
| * The Regents of the University of California. All rights reserved. | |
| * | |
| * This code is derived from software contributed to Berkeley by | |
| * Van Jacobson. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions | |
| * are met: | |
| * 1. Redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer. | |
| * 2. Redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in the | |
| * documentation and/or other materials provided with the distribution. | |
| * 3. All advertising materials mentioning features or use of this software | |
| * must display the following acknowledgement: | |
| * This product includes software developed by the University of | |
| * California, Berkeley and its contributors. | |
| * 4. Neither the name of the University nor the names of its contributors | |
| * may be used to endorse or promote products derived from this software | |
| * without specific prior written permission. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| * SUCH DAMAGE. | |
| */ | |
| #ifndef lint | |
| char copyright[] = | |
| "@(#) Copyright (c) 1990, 1993\n\ | |
| The Regents of the University of California. All rights reserved.\n"; | |
| #endif /* not lint */ | |
| /* | |
| * traceroute host - trace the route ip packets follow going to "host". | |
| * | |
| * Attempt to trace the route an ip packet would follow to some | |
| * internet host. We find out intermediate hops by launching probe | |
| * packets with a small ttl (time to live) then listening for an | |
| * icmp "time exceeded" reply from a gateway. We start our probes | |
| * with a ttl of one and increase by one until we get an icmp "port | |
| * unreachable" (which means we got to "host") or hit a max (which | |
| * defaults to 30 hops & can be changed with the -m flag). Three | |
| * probes (change with -q flag) are sent at each ttl setting and a | |
| * line is printed showing the ttl, address of the gateway and | |
| * round trip time of each probe. If the probe answers come from | |
| * different gateways, the address of each responding system will | |
| * be printed. If there is no response within a 5 sec. timeout | |
| * interval (changed with the -w flag), a "*" is printed for that | |
| * probe. | |
| * | |
| * Probe packets are UDP format. We don't want the destination | |
| * host to process them so the destination port is set to an | |
| * unlikely value (if some clod on the destination is using that | |
| * value, it can be changed with the -p flag). | |
| * | |
| * A sample use might be: | |
| * | |
| * [yak 71]% traceroute nis.nsf.net. | |
| * traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 56 byte packet | |
| * 1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms | |
| * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms | |
| * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms | |
| * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms | |
| * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms | |
| * 6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms | |
| * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms | |
| * 8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms | |
| * 9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms | |
| * 10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms | |
| * 11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms | |
| * | |
| * Note that lines 2 & 3 are the same. This is due to a buggy | |
| * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards | |
| * packets with a zero ttl. | |
| * | |
| * A more interesting example is: | |
| * | |
| * [yak 72]% traceroute allspice.lcs.mit.edu. | |
| * traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max | |
| * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms | |
| * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms | |
| * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms | |
| * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms | |
| * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms | |
| * 6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms | |
| * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms | |
| * 8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms | |
| * 9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms | |
| * 10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms | |
| * 11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms | |
| * 12 * * * | |
| * 13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms | |
| * 14 * * * | |
| * 15 * * * | |
| * 16 * * * | |
| * 17 * * * | |
| * 18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms | |
| * | |
| * (I start to see why I'm having so much trouble with mail to | |
| * MIT.) Note that the gateways 12, 14, 15, 16 & 17 hops away | |
| * either don't send ICMP "time exceeded" messages or send them | |
| * with a ttl too small to reach us. 14 - 17 are running the | |
| * MIT C Gateway code that doesn't send "time exceeded"s. God | |
| * only knows what's going on with 12. | |
| * | |
| * The silent gateway 12 in the above may be the result of a bug in | |
| * the 4.[23]BSD network code (and its derivatives): 4.x (x <= 3) | |
| * sends an unreachable message using whatever ttl remains in the | |
| * original datagram. Since, for gateways, the remaining ttl is | |
| * zero, the icmp "time exceeded" is guaranteed to not make it back | |
| * to us. The behavior of this bug is slightly more interesting | |
| * when it appears on the destination system: | |
| * | |
| * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms | |
| * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms | |
| * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms | |
| * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms | |
| * 5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms | |
| * 6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms | |
| * 7 * * * | |
| * 8 * * * | |
| * 9 * * * | |
| * 10 * * * | |
| * 11 * * * | |
| * 12 * * * | |
| * 13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms ! | |
| * | |
| * Notice that there are 12 "gateways" (13 is the final | |
| * destination) and exactly the last half of them are "missing". | |
| * What's really happening is that rip (a Sun-3 running Sun OS3.5) | |
| * is using the ttl from our arriving datagram as the ttl in its | |
| * icmp reply. So, the reply will time out on the return path | |
| * (with no notice sent to anyone since icmp's aren't sent for | |
| * icmp's) until we probe with a ttl that's at least twice the path | |
| * length. I.e., rip is really only 7 hops away. A reply that | |
| * returns with a ttl of 1 is a clue this problem exists. | |
| * Traceroute prints a "!" after the time if the ttl is <= 1. | |
| * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or | |
| * non-standard (HPUX) software, expect to see this problem | |
| * frequently and/or take care picking the target host of your | |
| * probes. | |
| * | |
| * Other possible annotations after the time are !H, !N, !P (got a host, | |
| * network or protocol unreachable, respectively), !S or !F (source | |
| * route failed or fragmentation needed -- neither of these should | |
| * ever occur and the associated gateway is busted if you see one). If | |
| * almost all the probes result in some kind of unreachable, traceroute | |
| * will give up and exit. | |
| * | |
| * Notes | |
| * ----- | |
| * This program must be run by root or be setuid. (I suggest that | |
| * you *don't* make it setuid -- casual use could result in a lot | |
| * of unnecessary traffic on our poor, congested nets.) | |
| * | |
| * This program requires a kernel mod that does not appear in any | |
| * system available from Berkeley: A raw ip socket using proto | |
| * IPPROTO_RAW must interpret the data sent as an ip datagram (as | |
| * opposed to data to be wrapped in a ip datagram). See the README | |
| * file that came with the source to this program for a description | |
| * of the mods I made to /sys/netinet/raw_ip.c. Your mileage may | |
| * vary. But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE | |
| * MODIFIED TO RUN THIS PROGRAM. | |
| * | |
| * The udp port usage may appear bizarre (well, ok, it is bizarre). | |
| * The problem is that an icmp message only contains 8 bytes of | |
| * data from the original datagram. 8 bytes is the size of a udp | |
| * header so, if we want to associate replies with the original | |
| * datagram, the necessary information must be encoded into the | |
| * udp header (the ip id could be used but there's no way to | |
| * interlock with the kernel's assignment of ip id's and, anyway, | |
| * it would have taken a lot more kernel hacking to allow this | |
| * code to set the ip id). So, to allow two or more users to | |
| * use traceroute simultaneously, we use this task's pid as the | |
| * source port (the high bit is set to move the port number out | |
| * of the "likely" range). To keep track of which probe is being | |
| * replied to (so times and/or hop counts don't get confused by a | |
| * reply that was delayed in transit), we increment the destination | |
| * port number before each probe. | |
| * | |
| * Don't use this as a coding example. I was trying to find a | |
| * routing problem and this code sort-of popped out after 48 hours | |
| * without sleep. I was amazed it ever compiled, much less ran. | |
| * | |
| * I stole the idea for this program from Steve Deering. Since | |
| * the first release, I've learned that had I attended the right | |
| * IETF working group meetings, I also could have stolen it from Guy | |
| * Almes or Matt Mathis. I don't know (or care) who came up with | |
| * the idea first. I envy the originators' perspicacity and I'm | |
| * glad they didn't keep the idea a secret. | |
| * | |
| * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or | |
| * enhancements to the original distribution. | |
| * | |
| * I've hacked up a round-trip-route version of this that works by | |
| * sending a loose-source-routed udp datagram through the destination | |
| * back to yourself. Unfortunately, SO many gateways botch source | |
| * routing, the thing is almost worthless. Maybe one day... | |
| * | |
| * -- Van Jacobson ([email protected]) | |
| * Tue Dec 20 03:50:13 PST 1988 | |
| */ | |
| #include <sys/param.h> | |
| #include <sys/time.h> | |
| #include <sys/socket.h> | |
| #include <sys/file.h> | |
| #include <sys/ioctl.h> | |
| #include <net/if.h> | |
| #if __linux__ | |
| #include <endian.h> | |
| #endif | |
| #include <netinet/in_systm.h> | |
| #include <netinet/in.h> | |
| #include <netinet/ip.h> | |
| #include <netinet/ip_icmp.h> | |
| #include <netinet/udp.h> | |
| #include <netinet/ip6.h> | |
| #include <netinet/icmp6.h> | |
| #include <linux/types.h> | |
| #ifdef CAPABILITIES | |
| #include <sys/capability.h> | |
| #endif | |
| #ifdef USE_IDN | |
| #include <idna.h> | |
| #include <locale.h> | |
| #endif | |
| #include <arpa/inet.h> | |
| #include <netdb.h> | |
| #include <stdio.h> | |
| #include <errno.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <unistd.h> | |
| #include "SNAPSHOT.h" | |
| #ifndef SOL_IPV6 | |
| #define SOL_IPV6 IPPROTO_IPV6 | |
| #endif | |
| #define MAXPACKET 65535 | |
| #define MAX_HOSTNAMELEN NI_MAXHOST | |
| #ifndef FD_SET | |
| #define NFDBITS (8*sizeof(fd_set)) | |
| #define FD_SETSIZE NFDBITS | |
| #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) | |
| #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) | |
| #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) | |
| #define FD_ZERO(p) memset((char *)(p), 0, sizeof(*(p))) | |
| #endif | |
| #define Fprintf (void)fprintf | |
| #define Printf (void)printf | |
| u_char packet[512]; /* last inbound (icmp) packet */ | |
| int wait_for_reply(int, struct sockaddr_in6 *, struct in6_addr *, int); | |
| int packet_ok(u_char *buf, int cc, struct sockaddr_in6 *from, | |
| struct in6_addr *to, int seq, struct timeval *); | |
| void send_probe(int seq, int ttl); | |
| double deltaT (struct timeval *, struct timeval *); | |
| void print(unsigned char *buf, int cc, struct sockaddr_in6 *from); | |
| void tvsub (struct timeval *, struct timeval *); | |
| void usage(void); | |
| int icmp_sock; /* receive (icmp) socket file descriptor */ | |
| int sndsock; /* send (udp) socket file descriptor */ | |
| struct timezone tz; /* leftover */ | |
| struct sockaddr_in6 whereto; /* Who to try to reach */ | |
| struct sockaddr_in6 saddr; | |
| struct sockaddr_in6 firsthop; | |
| char *source = NULL; | |
| char *device = NULL; | |
| char *hostname; | |
| int nprobes = 3; | |
| int max_ttl = 30; | |
| pid_t ident; | |
| u_short port = 32768+666; /* start udp dest port # for probe packets */ | |
| int options; /* socket options */ | |
| int verbose; | |
| int waittime = 5; /* time to wait for response (in seconds) */ | |
| int nflag; /* print addresses numerically */ | |
| struct pkt_format | |
| { | |
| __u32 ident; | |
| __u32 seq; | |
| struct timeval tv; | |
| }; | |
| char *sendbuff; | |
| int datalen = sizeof(struct pkt_format); | |
| int main(int argc, char *argv[]) | |
| { | |
| char pa[MAX_HOSTNAMELEN]; | |
| extern char *optarg; | |
| extern int optind; | |
| struct hostent *hp; | |
| struct sockaddr_in6 from, *to; | |
| int ch, i, on, probe, seq, tos, ttl; | |
| int socket_errno; | |
| icmp_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); | |
| socket_errno = errno; | |
| if (setuid(getuid())) { | |
| perror("traceroute6: setuid"); | |
| exit(-1); | |
| } | |
| #ifdef CAPABILITIES | |
| { | |
| cap_t caps = cap_init(); | |
| if (cap_set_proc(caps)) { | |
| perror("traceroute6: cap_set_proc"); | |
| exit(-1); | |
| } | |
| cap_free(caps); | |
| } | |
| #endif | |
| #ifdef USE_IDN | |
| setlocale(LC_ALL, ""); | |
| #endif | |
| on = 1; | |
| seq = tos = 0; | |
| to = (struct sockaddr_in6 *)&whereto; | |
| while ((ch = getopt(argc, argv, "dm:np:q:rs:t:w:vi:g:V")) != EOF) { | |
| switch(ch) { | |
| case 'd': | |
| options |= SO_DEBUG; | |
| break; | |
| case 'm': | |
| max_ttl = atoi(optarg); | |
| if (max_ttl <= 1) { | |
| Fprintf(stderr, | |
| "traceroute: max ttl must be >1.\n"); | |
| exit(1); | |
| } | |
| break; | |
| case 'n': | |
| nflag++; | |
| break; | |
| case 'p': | |
| port = atoi(optarg); | |
| if (port < 1) { | |
| Fprintf(stderr, | |
| "traceroute: port must be >0.\n"); | |
| exit(1); | |
| } | |
| break; | |
| case 'q': | |
| nprobes = atoi(optarg); | |
| if (nprobes < 1) { | |
| Fprintf(stderr, | |
| "traceroute: nprobes must be >0.\n"); | |
| exit(1); | |
| } | |
| break; | |
| case 'r': | |
| options |= SO_DONTROUTE; | |
| break; | |
| case 's': | |
| /* | |
| * set the ip source address of the outbound | |
| * probe (e.g., on a multi-homed host). | |
| */ | |
| source = optarg; | |
| break; | |
| case 'i': | |
| device = optarg; | |
| break; | |
| case 'g': | |
| Fprintf(stderr, "Sorry, rthdr is not yet supported\n"); | |
| break; | |
| case 'v': | |
| verbose++; | |
| break; | |
| case 'w': | |
| waittime = atoi(optarg); | |
| if (waittime <= 1) { | |
| Fprintf(stderr, | |
| "traceroute: wait must be >1 sec.\n"); | |
| exit(1); | |
| } | |
| break; | |
| case 'V': | |
| printf("traceroute6 utility, iputils-%s\n", SNAPSHOT); | |
| exit(0); | |
| default: | |
| usage(); | |
| } | |
| } | |
| argc -= optind; | |
| argv += optind; | |
| if (argc < 1) | |
| usage(); | |
| setlinebuf (stdout); | |
| (void) memset((char *)&whereto, 0, sizeof(whereto)); | |
| to->sin6_family = AF_INET6; | |
| to->sin6_port = htons(port); | |
| if (inet_pton(AF_INET6, *argv, &to->sin6_addr) > 0) { | |
| hostname = *argv; | |
| } else { | |
| char *idn = NULL; | |
| #ifdef USE_IDN | |
| if (idna_to_ascii_lz(*argv, &idn, 0) != IDNA_SUCCESS) | |
| idn = NULL; | |
| #endif | |
| hp = gethostbyname2(idn ? idn : *argv, AF_INET6); | |
| if (hp) { | |
| memmove((caddr_t)&to->sin6_addr, hp->h_addr, sizeof(to->sin6_addr)); | |
| hostname = (char *)hp->h_name; | |
| } else { | |
| (void)fprintf(stderr, | |
| "traceroute: unknown host %s\n", *argv); | |
| exit(1); | |
| } | |
| } | |
| firsthop = *to; | |
| if (*++argv) { | |
| datalen = atoi(*argv); | |
| /* Message for rpm maintainers: have _shame_. If you want | |
| * to fix something send the patch to me for sanity checking. | |
| * "datalen" patch is a shit. */ | |
| if (datalen == 0) | |
| datalen = sizeof(struct pkt_format); | |
| else if (datalen < (int)sizeof(struct pkt_format) || | |
| datalen >= MAXPACKET) { | |
| Fprintf(stderr, | |
| "traceroute: packet size must be %d <= s < %d.\n", | |
| (int)sizeof(struct pkt_format), MAXPACKET); | |
| exit(1); | |
| } | |
| } | |
| ident = getpid(); | |
| sendbuff = malloc(datalen); | |
| if (sendbuff == NULL) { | |
| fprintf(stderr, "malloc failed\n"); | |
| exit(1); | |
| } | |
| if (icmp_sock < 0) { | |
| errno = socket_errno; | |
| perror("traceroute6: icmp socket"); | |
| exit(1); | |
| } | |
| #ifdef IPV6_RECVPKTINFO | |
| setsockopt(icmp_sock, SOL_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); | |
| setsockopt(icmp_sock, SOL_IPV6, IPV6_2292PKTINFO, &on, sizeof(on)); | |
| #else | |
| setsockopt(icmp_sock, SOL_IPV6, IPV6_PKTINFO, &on, sizeof(on)); | |
| #endif | |
| if (options & SO_DEBUG) | |
| setsockopt(icmp_sock, SOL_SOCKET, SO_DEBUG, | |
| (char *)&on, sizeof(on)); | |
| if (options & SO_DONTROUTE) | |
| setsockopt(icmp_sock, SOL_SOCKET, SO_DONTROUTE, | |
| (char *)&on, sizeof(on)); | |
| #ifdef __linux__ | |
| on = 2; | |
| if (setsockopt(icmp_sock, SOL_RAW, IPV6_CHECKSUM, &on, sizeof(on)) < 0) { | |
| /* checksum should be enabled by default and setting this | |
| * option might fail anyway. | |
| */ | |
| fprintf(stderr, "setsockopt(RAW_CHECKSUM) failed - try to continue."); | |
| } | |
| #endif | |
| if ((sndsock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { | |
| perror("traceroute: UDP socket"); | |
| exit(5); | |
| } | |
| #ifdef SO_SNDBUF | |
| if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen, | |
| sizeof(datalen)) < 0) { | |
| perror("traceroute: SO_SNDBUF"); | |
| exit(6); | |
| } | |
| #endif /* SO_SNDBUF */ | |
| if (options & SO_DEBUG) | |
| (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG, | |
| (char *)&on, sizeof(on)); | |
| if (options & SO_DONTROUTE) | |
| (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, | |
| (char *)&on, sizeof(on)); | |
| if (source == NULL) { | |
| socklen_t alen; | |
| int probe_fd = socket(AF_INET6, SOCK_DGRAM, 0); | |
| if (probe_fd < 0) { | |
| perror("socket"); | |
| exit(1); | |
| } | |
| if (device) { | |
| if (setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1) | |
| perror("WARNING: interface is ignored"); | |
| } | |
| firsthop.sin6_port = htons(1025); | |
| if (connect(probe_fd, (struct sockaddr*)&firsthop, sizeof(firsthop)) == -1) { | |
| perror("connect"); | |
| exit(1); | |
| } | |
| alen = sizeof(saddr); | |
| if (getsockname(probe_fd, (struct sockaddr*)&saddr, &alen) == -1) { | |
| perror("getsockname"); | |
| exit(1); | |
| } | |
| saddr.sin6_port = 0; | |
| close(probe_fd); | |
| } else { | |
| (void) memset((char *)&saddr, 0, sizeof(saddr)); | |
| saddr.sin6_family = AF_INET6; | |
| if (inet_pton(AF_INET6, source, &saddr.sin6_addr) <= 0) | |
| { | |
| Printf("traceroute: unknown addr %s\n", source); | |
| exit(1); | |
| } | |
| } | |
| if (bind(sndsock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { | |
| perror ("traceroute: bind sending socket"); | |
| exit (1); | |
| } | |
| if (bind(icmp_sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { | |
| perror ("traceroute: bind icmp6 socket"); | |
| exit (1); | |
| } | |
| Fprintf(stderr, "traceroute to %s (%s)", hostname, | |
| inet_ntop(AF_INET6, &to->sin6_addr, pa, sizeof(pa))); | |
| Fprintf(stderr, " from %s", | |
| inet_ntop(AF_INET6, &saddr.sin6_addr, pa, sizeof(pa))); | |
| Fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, datalen); | |
| (void) fflush(stderr); | |
| for (ttl = 1; ttl <= max_ttl; ++ttl) { | |
| struct in6_addr lastaddr = {{{0,}}}; | |
| int got_there = 0; | |
| int unreachable = 0; | |
| Printf("%2d ", ttl); | |
| for (probe = 0; probe < nprobes; ++probe) { | |
| int cc, reset_timer; | |
| struct timeval t1, t2; | |
| struct timezone tz; | |
| struct in6_addr to; | |
| gettimeofday(&t1, &tz); | |
| send_probe(++seq, ttl); | |
| reset_timer = 1; | |
| while ((cc = wait_for_reply(icmp_sock, &from, &to, reset_timer)) != 0) { | |
| gettimeofday(&t2, &tz); | |
| if ((i = packet_ok(packet, cc, &from, &to, seq, &t1))) { | |
| reset_timer = 1; | |
| if (memcmp(&from.sin6_addr, &lastaddr, sizeof(from.sin6_addr))) { | |
| print(packet, cc, &from); | |
| memcpy(&lastaddr, | |
| &from.sin6_addr, | |
| sizeof(lastaddr)); | |
| } | |
| Printf(" %g ms", deltaT(&t1, &t2)); | |
| switch(i - 1) { | |
| case ICMP6_DST_UNREACH_NOPORT: | |
| ++got_there; | |
| break; | |
| case ICMP6_DST_UNREACH_NOROUTE: | |
| ++unreachable; | |
| Printf(" !N"); | |
| break; | |
| case ICMP6_DST_UNREACH_ADDR: | |
| ++unreachable; | |
| Printf(" !H"); | |
| break; | |
| case ICMP6_DST_UNREACH_ADMIN: | |
| ++unreachable; | |
| Printf(" !S"); | |
| break; | |
| } | |
| break; | |
| } else | |
| reset_timer = 0; | |
| } | |
| if (cc <= 0) | |
| Printf(" *"); | |
| (void) fflush(stdout); | |
| } | |
| putchar('\n'); | |
| if (got_there || | |
| (unreachable > 0 && unreachable >= nprobes-1)) | |
| exit(0); | |
| } | |
| return 0; | |
| } | |
| int | |
| wait_for_reply(sock, from, to, reset_timer) | |
| int sock; | |
| struct sockaddr_in6 *from; | |
| struct in6_addr *to; | |
| int reset_timer; | |
| { | |
| fd_set fds; | |
| static struct timeval wait; | |
| int cc = 0; | |
| char cbuf[512]; | |
| FD_ZERO(&fds); | |
| FD_SET(sock, &fds); | |
| if (reset_timer) { | |
| /* | |
| * traceroute could hang if someone else has a ping | |
| * running and our ICMP reply gets dropped but we don't | |
| * realize it because we keep waking up to handle those | |
| * other ICMP packets that keep coming in. To fix this, | |
| * "reset_timer" will only be true if the last packet that | |
| * came in was for us or if this is the first time we're | |
| * waiting for a reply since sending out a probe. Note | |
| * that this takes advantage of the select() feature on | |
| * Linux where the remaining timeout is written to the | |
| * struct timeval area. | |
| */ | |
| wait.tv_sec = waittime; | |
| wait.tv_usec = 0; | |
| } | |
| if (select(sock+1, &fds, (fd_set *)0, (fd_set *)0, &wait) > 0) { | |
| struct iovec iov; | |
| struct msghdr msg; | |
| iov.iov_base = packet; | |
| iov.iov_len = sizeof(packet); | |
| msg.msg_name = (void *)from; | |
| msg.msg_namelen = sizeof(*from); | |
| msg.msg_iov = &iov; | |
| msg.msg_iovlen = 1; | |
| msg.msg_flags = 0; | |
| msg.msg_control = cbuf; | |
| msg.msg_controllen = sizeof(cbuf); | |
| cc = recvmsg(icmp_sock, &msg, 0); | |
| if (cc >= 0) { | |
| struct cmsghdr *cmsg; | |
| struct in6_pktinfo *ipi; | |
| for (cmsg = CMSG_FIRSTHDR(&msg); | |
| cmsg; | |
| cmsg = CMSG_NXTHDR(&msg, cmsg)) { | |
| if (cmsg->cmsg_level != SOL_IPV6) | |
| continue; | |
| switch (cmsg->cmsg_type) { | |
| case IPV6_PKTINFO: | |
| #ifdef IPV6_2292PKTINFO | |
| case IPV6_2292PKTINFO: | |
| #endif | |
| ipi = (struct in6_pktinfo *)CMSG_DATA(cmsg); | |
| memcpy(to, ipi, sizeof(*to)); | |
| } | |
| } | |
| } | |
| } | |
| return(cc); | |
| } | |
| void send_probe(int seq, int ttl) | |
| { | |
| struct pkt_format *pkt = (struct pkt_format *) sendbuff; | |
| int i; | |
| pkt->ident = htonl(ident); | |
| pkt->seq = htonl(seq); | |
| gettimeofday(&pkt->tv, &tz); | |
| i = setsockopt(sndsock, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); | |
| if (i < 0) | |
| { | |
| perror("setsockopt"); | |
| exit(1); | |
| } | |
| do { | |
| i = sendto(sndsock, sendbuff, datalen, 0, | |
| (struct sockaddr *)&whereto, sizeof(whereto)); | |
| } while (i<0 && errno == ECONNREFUSED); | |
| if (i < 0 || i != datalen) { | |
| if (i<0) | |
| perror("sendto"); | |
| Printf("traceroute: wrote %s %d chars, ret=%d\n", hostname, | |
| datalen, i); | |
| (void) fflush(stdout); | |
| } | |
| } | |
| double deltaT(struct timeval *t1p, struct timeval *t2p) | |
| { | |
| register double dt; | |
| dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 + | |
| (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0; | |
| return (dt); | |
| } | |
| /* | |
| * Convert an ICMP "type" field to a printable string. | |
| */ | |
| char * pr_type(unsigned char t) | |
| { | |
| switch(t) { | |
| /* Unknown */ | |
| case 0: | |
| return "Error"; | |
| case 1: | |
| /* ICMP6_DST_UNREACH: */ | |
| return "Destination Unreachable"; | |
| case 2: | |
| /* ICMP6_PACKET_TOO_BIG: */ | |
| return "Packet Too Big"; | |
| case 3: | |
| /* ICMP6_TIME_EXCEEDED */ | |
| return "Time Exceeded in Transit"; | |
| case 4: | |
| /* ICMP6_PARAM_PROB */ | |
| return "Parameter Problem"; | |
| case 128: | |
| /* ICMP6_ECHO_REQUEST */ | |
| return "Echo Request"; | |
| case 129: | |
| /* ICMP6_ECHO_REPLY */ | |
| return "Echo Reply"; | |
| case 130: | |
| /* ICMP6_MEMBERSHIP_QUERY */ | |
| return "Membership Query"; | |
| case 131: | |
| /* ICMP6_MEMBERSHIP_REPORT */ | |
| return "Membership Report"; | |
| case 132: | |
| /* ICMP6_MEMBERSHIP_REDUCTION */ | |
| return "Membership Reduction"; | |
| case 133: | |
| /* ND_ROUTER_SOLICIT */ | |
| return "Router Solicitation"; | |
| case 134: | |
| /* ND_ROUTER_ADVERT */ | |
| return "Router Advertisement"; | |
| case 135: | |
| /* ND_NEIGHBOR_SOLICIT */ | |
| return "Neighbor Solicitation"; | |
| case 136: | |
| /* ND_NEIGHBOR_ADVERT */ | |
| return "Neighbor Advertisement"; | |
| case 137: | |
| /* ND_REDIRECT */ | |
| return "Redirect"; | |
| } | |
| return("OUT-OF-RANGE"); | |
| } | |
| int packet_ok(u_char *buf, int cc, struct sockaddr_in6 *from, | |
| struct in6_addr *to, int seq, | |
| struct timeval *tv) | |
| { | |
| struct icmp6_hdr *icp; | |
| u_char type, code; | |
| icp = (struct icmp6_hdr *) buf; | |
| type = icp->icmp6_type; | |
| code = icp->icmp6_code; | |
| if ((type == ICMP6_TIME_EXCEEDED && code == ICMP6_TIME_EXCEED_TRANSIT) || | |
| type == ICMP6_DST_UNREACH) | |
| { | |
| struct ip6_hdr *hip; | |
| struct udphdr *up; | |
| int nexthdr; | |
| hip = (struct ip6_hdr *) (icp + 1); | |
| up = (struct udphdr *)(hip+1); | |
| nexthdr = hip->ip6_nxt; | |
| if (nexthdr == 44) { | |
| nexthdr = *(unsigned char*)up; | |
| up++; | |
| } | |
| if (nexthdr == IPPROTO_UDP) | |
| { | |
| struct pkt_format *pkt; | |
| pkt = (struct pkt_format *) (up + 1); | |
| if (ntohl(pkt->ident) == ident && | |
| ntohl(pkt->seq) == seq) | |
| { | |
| *tv = pkt->tv; | |
| return (type == ICMP6_TIME_EXCEEDED ? -1 : code+1); | |
| } | |
| } | |
| } | |
| if (verbose) { | |
| unsigned char *p; | |
| char pa1[MAX_HOSTNAMELEN]; | |
| char pa2[MAX_HOSTNAMELEN]; | |
| int i; | |
| p = (unsigned char *) (icp + 1); | |
| Printf("\n%d bytes from %s to %s", cc, | |
| inet_ntop(AF_INET6, &from->sin6_addr, pa1, sizeof(pa1)), | |
| inet_ntop(AF_INET6, to, pa2, sizeof(pa2))); | |
| Printf(": icmp type %d (%s) code %d\n", type, pr_type(type), | |
| icp->icmp6_code); | |
| cc -= sizeof(struct icmp6_hdr); | |
| for (i = 0; i < cc ; i++) { | |
| if (i % 16 == 0) | |
| Printf("%04x:", i); | |
| if (i % 4 == 0) | |
| Printf(" "); | |
| Printf("%02x", 0xff & (unsigned)p[i]); | |
| if (i % 16 == 15 && i + 1 < cc) | |
| Printf("\n"); | |
| } | |
| Printf("\n"); | |
| } | |
| return(0); | |
| } | |
| void print(unsigned char *buf, int cc, struct sockaddr_in6 *from) | |
| { | |
| char pa[MAX_HOSTNAMELEN]; | |
| if (nflag) | |
| Printf(" %s", inet_ntop(AF_INET6, &from->sin6_addr, | |
| pa, sizeof(pa))); | |
| else | |
| { | |
| const char *hostname; | |
| struct hostent *hp; | |
| char *s = NULL; | |
| hostname = inet_ntop(AF_INET6, &from->sin6_addr, pa, sizeof(pa)); | |
| if ((hp = gethostbyaddr((char *)&from->sin6_addr, | |
| sizeof(from->sin6_addr), AF_INET6))) { | |
| #ifdef USE_IDN | |
| if (idna_to_unicode_lzlz(hp->h_name, &s, 0) != IDNA_SUCCESS) | |
| s = NULL; | |
| #endif | |
| } | |
| Printf(" %s (%s)", hp ? (s ? s : hp->h_name) : hostname, pa); | |
| free(s); | |
| } | |
| } | |
| /* | |
| * Subtract 2 timeval structs: out = out - in. | |
| * Out is assumed to be >= in. | |
| */ | |
| void | |
| tvsub(out, in) | |
| register struct timeval *out, *in; | |
| { | |
| if ((out->tv_usec -= in->tv_usec) < 0) { | |
| out->tv_sec--; | |
| out->tv_usec += 1000000; | |
| } | |
| out->tv_sec -= in->tv_sec; | |
| } | |
| void usage(void) | |
| { | |
| fprintf(stderr, | |
| "Usage: traceroute6 [-dnrvV] [-m max_ttl] [-p port#] [-q nqueries]\n\t\ | |
| [-s src_addr] [-t tos] [-w wait] host [data size]\n"); | |
| exit(1); | |
| } |