traceroute6.c revision 64276
162637Skris/* $KAME: traceroute6.c,v 1.29 2000/06/12 16:29:18 itojun Exp $ */ 262637Skris 355163Sshin/* 455163Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 555163Sshin * All rights reserved. 655163Sshin * 755163Sshin * Redistribution and use in source and binary forms, with or without 855163Sshin * modification, are permitted provided that the following conditions 955163Sshin * are met: 1055163Sshin * 1. Redistributions of source code must retain the above copyright 1155163Sshin * notice, this list of conditions and the following disclaimer. 1255163Sshin * 2. Redistributions in binary form must reproduce the above copyright 1355163Sshin * notice, this list of conditions and the following disclaimer in the 1455163Sshin * documentation and/or other materials provided with the distribution. 1555163Sshin * 3. Neither the name of the project nor the names of its contributors 1655163Sshin * may be used to endorse or promote products derived from this software 1755163Sshin * without specific prior written permission. 1855163Sshin * 1955163Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2055163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2155163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2255163Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2355163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2455163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2555163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2655163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2755163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2855163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2955163Sshin * SUCH DAMAGE. 3055163Sshin */ 3155163Sshin 3255163Sshin/*- 3355163Sshin * Copyright (c) 1990, 1993 3455163Sshin * The Regents of the University of California. All rights reserved. 3555163Sshin * 3655163Sshin * This code is derived from software contributed to Berkeley by 3755163Sshin * Van Jacobson. 3855163Sshin * 3955163Sshin * Redistribution and use in source and binary forms, with or without 4055163Sshin * modification, are permitted provided that the following conditions 4155163Sshin * are met: 4255163Sshin * 1. Redistributions of source code must retain the above copyright 4355163Sshin * notice, this list of conditions and the following disclaimer. 4455163Sshin * 2. Redistributions in binary form must reproduce the above copyright 4555163Sshin * notice, this list of conditions and the following disclaimer in the 4655163Sshin * documentation and/or other materials provided with the distribution. 4755163Sshin * 3. All advertising materials mentioning features or use of this software 4855163Sshin * must display the following acknowledgement: 4955163Sshin * This product includes software developed by the University of 5055163Sshin * California, Berkeley and its contributors. 5155163Sshin * 4. Neither the name of the University nor the names of its contributors 5255163Sshin * may be used to endorse or promote products derived from this software 5355163Sshin * without specific prior written permission. 5455163Sshin * 5555163Sshin * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 5655163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 5755163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5855163Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 5955163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 6055163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 6155163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 6255163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 6355163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 6455163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 6555163Sshin * SUCH DAMAGE. 6655163Sshin */ 6755163Sshin 6855163Sshin#ifndef lint 6955163Sshinstatic char copyright[] = 7055163Sshin"@(#) Copyright (c) 1990, 1993\n\ 7155163Sshin The Regents of the University of California. All rights reserved.\n"; 7255163Sshin#endif /* not lint */ 7355163Sshin 7455163Sshin#ifndef lint 7562637Skris#if 0 7655163Sshinstatic char sccsid[] = "@(#)traceroute.c 8.1 (Berkeley) 6/6/93"; 7762637Skris#endif 7862637Skrisstatic const char rcsid[] = 7962637Skris "$FreeBSD: head/usr.sbin/traceroute6/traceroute6.c 64276 2000-08-05 06:21:12Z kris $"; 8055163Sshin#endif /* not lint */ 8155163Sshin 8255163Sshin/* 8355163Sshin * traceroute host - trace the route ip packets follow going to "host". 8455163Sshin * 8555163Sshin * Attempt to trace the route an ip packet would follow to some 8655163Sshin * internet host. We find out intermediate hops by launching probe 8755163Sshin * packets with a small ttl (time to live) then listening for an 8855163Sshin * icmp "time exceeded" reply from a gateway. We start our probes 8955163Sshin * with a ttl of one and increase by one until we get an icmp "port 9055163Sshin * unreachable" (which means we got to "host") or hit a max (which 9155163Sshin * defaults to 30 hops & can be changed with the -m flag). Three 9255163Sshin * probes (change with -q flag) are sent at each ttl setting and a 9355163Sshin * line is printed showing the ttl, address of the gateway and 9455163Sshin * round trip time of each probe. If the probe answers come from 9555163Sshin * different gateways, the address of each responding system will 9655163Sshin * be printed. If there is no response within a 5 sec. timeout 9755163Sshin * interval (changed with the -w flag), a "*" is printed for that 9855163Sshin * probe. 9955163Sshin * 10055163Sshin * Probe packets are UDP format. We don't want the destination 10155163Sshin * host to process them so the destination port is set to an 10255163Sshin * unlikely value (if some clod on the destination is using that 10355163Sshin * value, it can be changed with the -p flag). 10455163Sshin * 10555163Sshin * A sample use might be: 10655163Sshin * 10755163Sshin * [yak 71]% traceroute nis.nsf.net. 10855163Sshin * traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 56 byte packet 10955163Sshin * 1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms 11055163Sshin * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms 11155163Sshin * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms 11255163Sshin * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms 11355163Sshin * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms 11455163Sshin * 6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms 11555163Sshin * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms 11655163Sshin * 8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms 11755163Sshin * 9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms 11855163Sshin * 10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms 11955163Sshin * 11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms 12055163Sshin * 12155163Sshin * Note that lines 2 & 3 are the same. This is due to a buggy 12255163Sshin * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards 12355163Sshin * packets with a zero ttl. 12455163Sshin * 12555163Sshin * A more interesting example is: 12655163Sshin * 12755163Sshin * [yak 72]% traceroute allspice.lcs.mit.edu. 12855163Sshin * traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max 12955163Sshin * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms 13055163Sshin * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms 13155163Sshin * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms 13255163Sshin * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms 13355163Sshin * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms 13455163Sshin * 6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms 13555163Sshin * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms 13655163Sshin * 8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms 13755163Sshin * 9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms 13855163Sshin * 10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms 13955163Sshin * 11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms 14055163Sshin * 12 * * * 14155163Sshin * 13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms 14255163Sshin * 14 * * * 14355163Sshin * 15 * * * 14455163Sshin * 16 * * * 14555163Sshin * 17 * * * 14655163Sshin * 18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms 14755163Sshin * 14855163Sshin * (I start to see why I'm having so much trouble with mail to 14955163Sshin * MIT.) Note that the gateways 12, 14, 15, 16 & 17 hops away 15055163Sshin * either don't send ICMP "time exceeded" messages or send them 15155163Sshin * with a ttl too small to reach us. 14 - 17 are running the 15255163Sshin * MIT C Gateway code that doesn't send "time exceeded"s. God 15355163Sshin * only knows what's going on with 12. 15455163Sshin * 15555163Sshin * The silent gateway 12 in the above may be the result of a bug in 15655163Sshin * the 4.[23]BSD network code (and its derivatives): 4.x (x <= 3) 15755163Sshin * sends an unreachable message using whatever ttl remains in the 15855163Sshin * original datagram. Since, for gateways, the remaining ttl is 15955163Sshin * zero, the icmp "time exceeded" is guaranteed to not make it back 16055163Sshin * to us. The behavior of this bug is slightly more interesting 16155163Sshin * when it appears on the destination system: 16255163Sshin * 16355163Sshin * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms 16455163Sshin * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms 16555163Sshin * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms 16655163Sshin * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms 16755163Sshin * 5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms 16855163Sshin * 6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms 16955163Sshin * 7 * * * 17055163Sshin * 8 * * * 17155163Sshin * 9 * * * 17255163Sshin * 10 * * * 17355163Sshin * 11 * * * 17455163Sshin * 12 * * * 17555163Sshin * 13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms ! 17655163Sshin * 17755163Sshin * Notice that there are 12 "gateways" (13 is the final 17855163Sshin * destination) and exactly the last half of them are "missing". 17955163Sshin * What's really happening is that rip (a Sun-3 running Sun OS3.5) 18055163Sshin * is using the ttl from our arriving datagram as the ttl in its 18155163Sshin * icmp reply. So, the reply will time out on the return path 18255163Sshin * (with no notice sent to anyone since icmp's aren't sent for 18355163Sshin * icmp's) until we probe with a ttl that's at least twice the path 18455163Sshin * length. I.e., rip is really only 7 hops away. A reply that 18555163Sshin * returns with a ttl of 1 is a clue this problem exists. 18655163Sshin * Traceroute prints a "!" after the time if the ttl is <= 1. 18755163Sshin * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or 18855163Sshin * non-standard (HPUX) software, expect to see this problem 18955163Sshin * frequently and/or take care picking the target host of your 19055163Sshin * probes. 19155163Sshin * 19255163Sshin * Other possible annotations after the time are !H, !N, !P (got a host, 19355163Sshin * network or protocol unreachable, respectively), !S or !F (source 19455163Sshin * route failed or fragmentation needed -- neither of these should 19555163Sshin * ever occur and the associated gateway is busted if you see one). If 19655163Sshin * almost all the probes result in some kind of unreachable, traceroute 19755163Sshin * will give up and exit. 19855163Sshin * 19955163Sshin * Notes 20055163Sshin * ----- 20155163Sshin * This program must be run by root or be setuid. (I suggest that 20255163Sshin * you *don't* make it setuid -- casual use could result in a lot 20355163Sshin * of unnecessary traffic on our poor, congested nets.) 20455163Sshin * 20555163Sshin * This program requires a kernel mod that does not appear in any 20655163Sshin * system available from Berkeley: A raw ip socket using proto 20755163Sshin * IPPROTO_RAW must interpret the data sent as an ip datagram (as 20855163Sshin * opposed to data to be wrapped in a ip datagram). See the README 20955163Sshin * file that came with the source to this program for a description 21055163Sshin * of the mods I made to /sys/netinet/raw_ip.c. Your mileage may 21155163Sshin * vary. But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE 21255163Sshin * MODIFIED TO RUN THIS PROGRAM. 21355163Sshin * 21455163Sshin * The udp port usage may appear bizarre (well, ok, it is bizarre). 21555163Sshin * The problem is that an icmp message only contains 8 bytes of 21655163Sshin * data from the original datagram. 8 bytes is the size of a udp 21755163Sshin * header so, if we want to associate replies with the original 21855163Sshin * datagram, the necessary information must be encoded into the 21955163Sshin * udp header (the ip id could be used but there's no way to 22055163Sshin * interlock with the kernel's assignment of ip id's and, anyway, 22155163Sshin * it would have taken a lot more kernel hacking to allow this 22255163Sshin * code to set the ip id). So, to allow two or more users to 22355163Sshin * use traceroute simultaneously, we use this task's pid as the 22455163Sshin * source port (the high bit is set to move the port number out 22555163Sshin * of the "likely" range). To keep track of which probe is being 22655163Sshin * replied to (so times and/or hop counts don't get confused by a 22755163Sshin * reply that was delayed in transit), we increment the destination 22855163Sshin * port number before each probe. 22955163Sshin * 23055163Sshin * Don't use this as a coding example. I was trying to find a 23155163Sshin * routing problem and this code sort-of popped out after 48 hours 23255163Sshin * without sleep. I was amazed it ever compiled, much less ran. 23355163Sshin * 23455163Sshin * I stole the idea for this program from Steve Deering. Since 23555163Sshin * the first release, I've learned that had I attended the right 23655163Sshin * IETF working group meetings, I also could have stolen it from Guy 23755163Sshin * Almes or Matt Mathis. I don't know (or care) who came up with 23855163Sshin * the idea first. I envy the originators' perspicacity and I'm 23955163Sshin * glad they didn't keep the idea a secret. 24055163Sshin * 24155163Sshin * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or 24255163Sshin * enhancements to the original distribution. 24355163Sshin * 24455163Sshin * I've hacked up a round-trip-route version of this that works by 24555163Sshin * sending a loose-source-routed udp datagram through the destination 24655163Sshin * back to yourself. Unfortunately, SO many gateways botch source 24755163Sshin * routing, the thing is almost worthless. Maybe one day... 24855163Sshin * 24955163Sshin * -- Van Jacobson (van@helios.ee.lbl.gov) 25055163Sshin * Tue Dec 20 03:50:13 PST 1988 25155163Sshin */ 25255163Sshin 25355163Sshin#include <sys/param.h> 25455163Sshin#include <sys/time.h> 25555163Sshin#include <sys/socket.h> 25655163Sshin#include <sys/uio.h> 25755163Sshin#include <sys/file.h> 25855163Sshin#include <sys/ioctl.h> 25955163Sshin 26055163Sshin#include <netinet/in.h> 26155163Sshin 26255163Sshin#include <arpa/inet.h> 26355163Sshin 26455163Sshin#include <netdb.h> 26555163Sshin#include <stdio.h> 26655163Sshin#include <err.h> 26755163Sshin#include <errno.h> 26855163Sshin#include <stdlib.h> 26955163Sshin#include <string.h> 27055163Sshin#include <unistd.h> 27155163Sshin 27255163Sshin#include <netinet/ip6.h> 27355163Sshin#include <netinet/icmp6.h> 27455163Sshin#include <netinet/udp.h> 27555163Sshin 27655163Sshin#ifdef IPSEC 27755163Sshin#include <net/route.h> 27855163Sshin#include <netinet6/ipsec.h> 27955163Sshin#endif 28055163Sshin 28162637Skris#define DUMMY_PORT 10010 28255163Sshin 28355163Sshin#define MAXPACKET 65535 /* max ip packet size */ 28455163Sshin#ifndef MAXHOSTNAMELEN 28562637Skris#define MAXHOSTNAMELEN 64 28655163Sshin#endif 28755163Sshin 28855163Sshin#ifndef FD_SET 28962637Skris#define NFDBITS (8*sizeof(fd_set)) 29062637Skris#define FD_SETSIZE NFDBITS 29162637Skris#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) 29262637Skris#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) 29362637Skris#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) 29462637Skris#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p))) 29555163Sshin#endif 29655163Sshin 29762637Skris#define Fprintf (void)fprintf 29862637Skris#define Sprintf (void)sprintf 29962637Skris#define Printf (void)printf 30055163Sshin 30162637Skris#ifndef HAVE_GETIPNODEBYNAME 30262637Skris#define getipnodebyname(x, y, z, u) gethostbyname2((x), (y)) 30362637Skris#define freehostent(x) 30462637Skris#endif 30562637Skris 30655163Sshin/* 30755163Sshin * format of a (udp) probe packet. 30855163Sshin */ 30955163Sshinstruct opacket { 31055163Sshin u_char seq; /* sequence number of this packet */ 31155163Sshin u_char hops; /* hop limit of the packet */ 31255163Sshin struct timeval tv; /* time packet left */ 31355163Sshin}; 31455163Sshin 31555163Sshinu_char packet[512]; /* last inbound (icmp) packet */ 31655163Sshinstruct opacket *outpacket; /* last output (udp) packet */ 31755163Sshin 31855163Sshinint main __P((int, char *[])); 31955163Sshinint wait_for_reply __P((int, struct msghdr *)); 32062637Skris#ifdef IPSEC 32162637Skris#ifdef IPSEC_POLICY_IPSEC 32255163Sshinint setpolicy __P((int so, char *policy)); 32362637Skris#endif 32462637Skris#endif 32555163Sshinvoid send_probe __P((int, int)); 32655163Sshinstruct udphdr *get_udphdr __P((struct ip6_hdr *, u_char *)); 32755163Sshinint get_hoplim __P((struct msghdr *)); 32855163Sshindouble deltaT __P((struct timeval *, struct timeval *)); 32955163Sshinchar *pr_type __P((int)); 33055163Sshinint packet_ok __P((struct msghdr *, int, int)); 33155163Sshinvoid print __P((struct msghdr *, int)); 33255163Sshinvoid tvsub __P((struct timeval *, struct timeval *)); 33362637Skrisconst char *inetname __P((struct sockaddr *)); 33455163Sshinvoid usage __P((void)); 33555163Sshin 33655163Sshinint rcvsock; /* receive (icmp) socket file descriptor */ 33755163Sshinint sndsock; /* send (udp) socket file descriptor */ 33855163Sshinstruct timezone tz; /* leftover */ 33955163Sshin 34055163Sshinstruct msghdr rcvmhdr; 34155163Sshinstruct iovec rcviov[2]; 34255163Sshinint rcvhlim; 34355163Sshinstruct in6_pktinfo *rcvpktinfo; 34455163Sshin 34555163Sshinstruct sockaddr_in6 Src, Dst, Rcv; 34655163Sshinint datalen; /* How much data */ 34762637Skris/* XXX: 2064 = 127(max hops in type 0 rthdr) * sizeof(ip6_hdr) + 16(margin) */ 34862637Skrischar rtbuf[2064]; 34962637Skris#ifdef USE_RFC2292BIS 35062637Skrisstruct ip6_rthdr *rth; 35162637Skris#endif 35255163Sshinstruct cmsghdr *cmsg; 35355163Sshin 35455163Sshinchar *source = 0; 35555163Sshinchar *hostname; 35655163Sshin 35755163Sshinint nprobes = 3; 35862637Skrisint first_hop = 1; 35955163Sshinint max_hops = 30; 36055163Sshinu_short ident; 36155163Sshinu_short port = 32768+666; /* start udp dest port # for probe packets */ 36255163Sshinint options; /* socket options */ 36355163Sshinint verbose; 36455163Sshinint waittime = 5; /* time to wait for response (in seconds) */ 36555163Sshinint nflag; /* print addresses numerically */ 36655163Sshinint lflag; /* print both numerical address & hostname */ 36755163Sshin 36862637Skris#ifdef KAME_SCOPEID 36962637Skrisconst int niflag = NI_WITHSCOPEID; 37062637Skris#else 37162637Skrisconst int niflag = 0; 37262637Skris#endif 37355163Sshin 37455163Sshinint 37555163Sshinmain(argc, argv) 37655163Sshin int argc; 37755163Sshin char *argv[]; 37855163Sshin{ 37955163Sshin struct hostent *hp; 38062637Skris int error; 38155658Sshin struct addrinfo hints, *res; 38262637Skris int ch, i, on, probe, seq, hops, rcvcmsglen; 38362637Skris static u_char *rcvcmsgbuf; 38462637Skris char hbuf[NI_MAXHOST], src0[NI_MAXHOST]; 38555163Sshin 38657439Sshin /* 38757439Sshin * Receive ICMP 38857439Sshin */ 38957439Sshin if ((rcvsock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { 39062637Skris perror("socket(ICMPv6)"); 39157439Sshin exit(5); 39257439Sshin } 39362637Skris 39462637Skris /* set a minimum set of socket options */ 39562637Skris on = 1; 39662637Skris /* specify to tell receiving interface */ 39762637Skris#ifdef IPV6_RECVPKTINFO 39862637Skris if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, 39962637Skris sizeof(on)) < 0) 40062637Skris err(1, "setsockopt(IPV6_RECVPKTINFO)"); 40162637Skris#else /* old adv. API */ 40262637Skris if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_PKTINFO, &on, 40362637Skris sizeof(on)) < 0) 40462637Skris err(1, "setsockopt(IPV6_PKTINFO)"); 40562637Skris#endif 40662637Skris 40762637Skris /* specify to tell value of hoplimit field of received IP6 hdr */ 40862637Skris#ifdef IPV6_RECVHOPLIMIT 40962637Skris if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, 41062637Skris sizeof(on)) < 0) 41162637Skris err(1, "setsockopt(IPV6_RECVHOPLIMIT)"); 41262637Skris#else /* old adv. API */ 41362637Skris if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, 41462637Skris sizeof(on)) < 0) 41562637Skris err(1, "setsockopt(IPV6_HOPLIMIT)"); 41662637Skris#endif 41762637Skris 41862637Skris /* revoke privs */ 41962637Skris seteuid(getuid()); 42057439Sshin setuid(getuid()); 42157439Sshin 42255163Sshin seq = 0; 42362637Skris 42462637Skris while ((ch = getopt(argc, argv, "df:g:lm:np:q:rs:w:v")) != EOF) 42555163Sshin switch(ch) { 42655163Sshin case 'd': 42755163Sshin options |= SO_DEBUG; 42855163Sshin break; 42962637Skris case 'f': 43062637Skris first_hop = atoi(optarg); 43162637Skris if (first_hop > max_hops) { 43262637Skris Fprintf(stderr, 43362637Skris "traceroute6: min hoplimit must be <= %d.\n", max_hops); 43462637Skris exit(1); 43562637Skris } 43655163Sshin break; 43755163Sshin case 'g': 43855163Sshin hp = getipnodebyname(optarg, AF_INET6, 0, &h_errno); 43955163Sshin if (hp == NULL) { 44055163Sshin Fprintf(stderr, 44155163Sshin "traceroute6: unknown host %s\n", optarg); 44255163Sshin exit(1); 44355163Sshin } 44462637Skris#ifdef USE_RFC2292BIS 44562637Skris if (rth == NULL) { 44662637Skris /* 44762637Skris * XXX: We can't detect the number of 44862637Skris * intermediate nodes yet. 44962637Skris */ 45062637Skris if ((rth = inet6_rth_init((void *)rtbuf, 45162637Skris sizeof(rtbuf), 45262637Skris IPV6_RTHDR_TYPE_0, 45362637Skris 0)) == NULL) { 45462637Skris Fprintf(stderr, 45562637Skris "inet6_rth_init failed.\n"); 45662637Skris exit(1); 45762637Skris } 45862637Skris } 45962637Skris if (inet6_rth_add((void *)rth, 46062637Skris (struct in6_addr *)hp->h_addr)) { 46162637Skris Fprintf(stderr, 46262637Skris "inet6_rth_add failed for %s\n", 46362637Skris optarg); 46462637Skris exit(1); 46562637Skris } 46662637Skris#else /* old advanced API */ 46755163Sshin if (cmsg == NULL) 46855163Sshin cmsg = inet6_rthdr_init(rtbuf, IPV6_RTHDR_TYPE_0); 46955163Sshin inet6_rthdr_add(cmsg, (struct in6_addr *)hp->h_addr, IPV6_RTHDR_LOOSE); 47062637Skris#endif 47162637Skris freehostent(hp); 47255163Sshin break; 47362637Skris case 'l': 47462637Skris lflag++; 47562637Skris break; 47655163Sshin case 'm': 47755163Sshin max_hops = atoi(optarg); 47862637Skris if (max_hops < first_hop) { 47955163Sshin Fprintf(stderr, 48062637Skris "traceroute6: max hoplimit must be >= %d.\n", first_hop); 48155163Sshin exit(1); 48255163Sshin } 48355163Sshin break; 48455163Sshin case 'n': 48555163Sshin nflag++; 48655163Sshin break; 48755163Sshin case 'p': 48855163Sshin port = atoi(optarg); 48955163Sshin if (port < 1) { 49055163Sshin Fprintf(stderr, 49155163Sshin "traceroute6: port must be >0.\n"); 49255163Sshin exit(1); 49355163Sshin } 49455163Sshin break; 49555163Sshin case 'q': 49655163Sshin nprobes = atoi(optarg); 49755163Sshin if (nprobes < 1) { 49855163Sshin Fprintf(stderr, 49955163Sshin "traceroute6: nprobes must be >0.\n"); 50055163Sshin exit(1); 50155163Sshin } 50255163Sshin break; 50355163Sshin case 'r': 50455163Sshin options |= SO_DONTROUTE; 50555163Sshin break; 50655163Sshin case 's': 50755163Sshin /* 50855163Sshin * set the ip source address of the outbound 50955163Sshin * probe (e.g., on a multi-homed host). 51055163Sshin */ 51155163Sshin source = optarg; 51255163Sshin break; 51355163Sshin case 'v': 51455163Sshin verbose++; 51555163Sshin break; 51655163Sshin case 'w': 51755163Sshin waittime = atoi(optarg); 51855163Sshin if (waittime <= 1) { 51955163Sshin Fprintf(stderr, 52055163Sshin "traceroute6: wait must be >1 sec.\n"); 52155163Sshin exit(1); 52255163Sshin } 52355163Sshin break; 52455163Sshin default: 52555163Sshin usage(); 52655163Sshin } 52755163Sshin argc -= optind; 52855163Sshin argv += optind; 52955163Sshin 53055163Sshin if (argc < 1) 53155163Sshin usage(); 53255163Sshin 53362637Skris#if 1 53455163Sshin setvbuf(stdout, NULL, _IOLBF, BUFSIZ); 53562637Skris#else 53662637Skris setlinebuf (stdout); 53762637Skris#endif 53855163Sshin 53955658Sshin memset(&hints, 0, sizeof(hints)); 54055658Sshin hints.ai_family = PF_INET6; 54155658Sshin hints.ai_socktype = SOCK_RAW; 54255658Sshin hints.ai_protocol = IPPROTO_ICMPV6; 54355658Sshin hints.ai_flags = AI_CANONNAME; 54455658Sshin error = getaddrinfo(*argv, NULL, &hints, &res); 54555658Sshin if (error) { 54655658Sshin (void)fprintf(stderr, 54755658Sshin "traceroute6: %s\n", gai_strerror(error)); 54855658Sshin exit(1); 54955163Sshin } 55062637Skris if (res->ai_addrlen != sizeof(Dst)) { 55162637Skris (void)fprintf(stderr, 55262637Skris "traceroute6: size of sockaddr mismatch\n"); 55362637Skris exit(1); 55462637Skris } 55555658Sshin memcpy(&Dst, res->ai_addr, res->ai_addrlen); 55655658Sshin hostname = res->ai_canonname ? strdup(res->ai_canonname) : *argv; 55755163Sshin 55855163Sshin if (*++argv) 55955163Sshin datalen = atoi(*argv); 56055163Sshin if (datalen < 0 || datalen >= MAXPACKET - sizeof(struct opacket)) { 56155163Sshin Fprintf(stderr, 56255163Sshin "traceroute6: packet size must be 0 <= s < %ld.\n", 56355163Sshin (long)(MAXPACKET - sizeof(struct opacket))); 56455163Sshin exit(1); 56555163Sshin } 56655163Sshin datalen += sizeof(struct opacket); 56755163Sshin outpacket = (struct opacket *)malloc((unsigned)datalen); 56855163Sshin if (! outpacket) { 56962637Skris perror("malloc"); 57055163Sshin exit(1); 57155163Sshin } 57255163Sshin (void) bzero((char *)outpacket, datalen); 57355163Sshin 57455163Sshin /* initialize msghdr for receiving packets */ 57555163Sshin rcviov[0].iov_base = (caddr_t)packet; 57655163Sshin rcviov[0].iov_len = sizeof(packet); 57762637Skris rcvmhdr.msg_name = (caddr_t)&Rcv; 57862637Skris rcvmhdr.msg_namelen = sizeof(Rcv); 57955163Sshin rcvmhdr.msg_iov = rcviov; 58055163Sshin rcvmhdr.msg_iovlen = 1; 58162637Skris rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) 58262637Skris + CMSG_SPACE(sizeof(int)); 58362637Skris if ((rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) { 58462637Skris Fprintf(stderr, "traceroute6: malloc failed\n"); 58562637Skris exit(1); 58662637Skris } 58755163Sshin rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf; 58862637Skris rcvmhdr.msg_controllen = rcvcmsglen; 58955163Sshin 59055163Sshin if (options & SO_DEBUG) 59155163Sshin (void) setsockopt(rcvsock, SOL_SOCKET, SO_DEBUG, 59255163Sshin (char *)&on, sizeof(on)); 59355163Sshin if (options & SO_DONTROUTE) 59455163Sshin (void) setsockopt(rcvsock, SOL_SOCKET, SO_DONTROUTE, 59555163Sshin (char *)&on, sizeof(on)); 59655163Sshin#ifdef IPSEC 59755163Sshin#ifdef IPSEC_POLICY_IPSEC 59855163Sshin /* 59955163Sshin * do not raise error even if setsockopt fails, kernel may have ipsec 60055163Sshin * turned off. 60155163Sshin */ 60255163Sshin if (setpolicy(rcvsock, "in bypass") < 0) 60364276Skris errx(1, "%s", ipsec_strerror()); 60455163Sshin if (setpolicy(rcvsock, "out bypass") < 0) 60564276Skris errx(1, "%s", ipsec_strerror()); 60662637Skris#else 60762637Skris { 60862637Skris int level = IPSEC_LEVEL_NONE; 60962637Skris 61062637Skris (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL, &level, 61162637Skris sizeof(level)); 61262637Skris (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_ESP_NETWORK_LEVEL, &level, 61362637Skris sizeof(level)); 61462637Skris#ifdef IP_AUTH_TRANS_LEVEL 61562637Skris (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL, &level, 61662637Skris sizeof(level)); 61762637Skris#else 61862637Skris (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_AUTH_LEVEL, &level, 61962637Skris sizeof(level)); 62062637Skris#endif 62162637Skris#ifdef IP_AUTH_NETWORK_LEVEL 62262637Skris (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_AUTH_NETWORK_LEVEL, &level, 62362637Skris sizeof(level)); 62462637Skris#endif 62562637Skris } 62655163Sshin#endif /*IPSEC_POLICY_IPSEC*/ 62755163Sshin#endif /*IPSEC*/ 62855163Sshin 62955163Sshin /* 63055163Sshin * Send UDP 63155163Sshin */ 63255163Sshin if ((sndsock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { 63362637Skris perror("socket(SOCK_DGRAM)"); 63455163Sshin exit(5); 63555163Sshin } 63662637Skris#ifdef SO_SNDBUF 63755163Sshin if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen, 63855163Sshin sizeof(datalen)) < 0) { 63962637Skris perror("setsockopt(SO_SNDBUF)"); 64055163Sshin exit(6); 64155163Sshin } 64262637Skris#endif /* SO_SNDBUF */ 64355163Sshin if (options & SO_DEBUG) 64455163Sshin (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG, 64555163Sshin (char *)&on, sizeof(on)); 64655163Sshin if (options & SO_DONTROUTE) 64755163Sshin (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE, 64855163Sshin (char *)&on, sizeof(on)); 64962637Skris#ifdef USE_RFC2292BIS 65062637Skris if (rth) {/* XXX: there is no library to finalize the header... */ 65162637Skris rth->ip6r_len = rth->ip6r_segleft * 2; 65262637Skris if (setsockopt(sndsock, IPPROTO_IPV6, IPV6_RTHDR, 65362637Skris (void *)rth, (rth->ip6r_len + 1) << 3)) { 65462637Skris Fprintf(stderr, "setsockopt(IPV6_RTHDR): %s\n", 65562637Skris strerror(errno)); 65662637Skris exit(1); 65762637Skris } 65862637Skris } 65962637Skris#else /* old advanced API */ 66055163Sshin if (cmsg != NULL) { 66155163Sshin inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE); 66262637Skris if (setsockopt(sndsock, IPPROTO_IPV6, IPV6_PKTOPTIONS, 66362637Skris rtbuf, cmsg->cmsg_len) < 0) { 66462637Skris Fprintf(stderr, "setsockopt(IPV6_PKTOPTIONS): %s\n", 66562637Skris strerror(errno)); 66662637Skris exit(1); 66762637Skris } 66855163Sshin } 66962637Skris#endif /* USE_RFC2292BIS */ 67055163Sshin#ifdef IPSEC 67155163Sshin#ifdef IPSEC_POLICY_IPSEC 67255163Sshin /* 67355163Sshin * do not raise error even if setsockopt fails, kernel may have ipsec 67455163Sshin * turned off. 67555163Sshin */ 67655163Sshin if (setpolicy(sndsock, "in bypass") < 0) 67764276Skris errx(1, "%s", ipsec_strerror()); 67855163Sshin if (setpolicy(sndsock, "out bypass") < 0) 67964276Skris errx(1, "%s", ipsec_strerror()); 68062637Skris#else 68162637Skris { 68262637Skris int level = IPSEC_LEVEL_BYPASS; 68362637Skris 68462637Skris (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL, &level, 68562637Skris sizeof(level)); 68662637Skris (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_ESP_NETWORK_LEVEL, &level, 68762637Skris sizeof(level)); 68862637Skris#ifdef IP_AUTH_TRANS_LEVEL 68962637Skris (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL, &level, 69062637Skris sizeof(level)); 69162637Skris#else 69262637Skris (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_AUTH_LEVEL, &level, 69362637Skris sizeof(level)); 69462637Skris#endif 69562637Skris#ifdef IP_AUTH_NETWORK_LEVEL 69662637Skris (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_AUTH_NETWORK_LEVEL, &level, 69762637Skris sizeof(level)); 69862637Skris#endif 69962637Skris } 70055163Sshin#endif /*IPSEC_POLICY_IPSEC*/ 70155163Sshin#endif /*IPSEC*/ 70255163Sshin 70355163Sshin /* 70455163Sshin * Source selection 70555163Sshin */ 70662637Skris bzero(&Src, sizeof(Src)); 70755163Sshin if (source) { 70862637Skris struct addrinfo hints, *res; 70962637Skris int error; 71062637Skris 71162637Skris memset(&hints, 0, sizeof(hints)); 71262637Skris hints.ai_family = AF_INET6; 71362637Skris hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 71462637Skris hints.ai_flags = AI_NUMERICHOST; 71562637Skris error = getaddrinfo(source, "0", &hints, &res); 71662637Skris if (error) { 71762637Skris Printf("traceroute6: %s: %s\n", source, 71862637Skris gai_strerror(error)); 71955163Sshin exit(1); 72055163Sshin } 72162637Skris if (res->ai_addrlen > sizeof(Src)) { 72262637Skris Printf("traceroute6: %s: %s\n", source, 72362637Skris gai_strerror(error)); 72462637Skris exit(1); 72562637Skris } 72662637Skris memcpy(&Src, res->ai_addr, res->ai_addrlen); 72762637Skris freeaddrinfo(res); 72855163Sshin } else { 72955163Sshin struct sockaddr_in6 Nxt; 73055163Sshin int dummy, len; 73155163Sshin 73255163Sshin Nxt = Dst; 73355163Sshin Nxt.sin6_port = htons(DUMMY_PORT); 73455163Sshin if (cmsg != NULL) 73555163Sshin bcopy(inet6_rthdr_getaddr(cmsg, 1), &Nxt.sin6_addr, 73655163Sshin sizeof(Nxt.sin6_addr)); 73755163Sshin if ((dummy = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { 73862637Skris perror("socket"); 73962637Skris exit(1); 74055163Sshin } 74162637Skris if (connect(dummy, (struct sockaddr *)&Nxt, Nxt.sin6_len) < 0) { 74255163Sshin perror("connect"); 74362637Skris exit(1); 74462637Skris } 74562637Skris len = sizeof(Src); 74662637Skris if (getsockname(dummy, (struct sockaddr *)&Src, &len) < 0) { 74755163Sshin perror("getsockname"); 74862637Skris exit(1); 74955163Sshin } 75062637Skris if (getnameinfo((struct sockaddr *)&Src, Src.sin6_len, 75162637Skris src0, sizeof(src0), NULL, 0, 75262637Skris NI_NUMERICHOST | niflag)) { 75362637Skris Fprintf(stderr, "getnameinfo failed for source\n"); 75462637Skris exit(1); 75562637Skris } 75662637Skris source = src0; 75762637Skris close(dummy); 75855163Sshin } 75962637Skris 76062637Skris#if 1 76155163Sshin ident = (getpid() & 0xffff) | 0x8000; 76262637Skris#else 76362637Skris ident = 0; /*let the kernel pick one*/ 76462637Skris#endif 76555163Sshin Src.sin6_port = htons(ident); 76662637Skris if (bind(sndsock, (struct sockaddr *)&Src, Src.sin6_len) < 0) { 76762637Skris perror("bind"); 76862637Skris exit(1); 76955163Sshin } 77055163Sshin 77162637Skris if (ident == 0) { 77262637Skris int len; 77362637Skris 77462637Skris len = sizeof(Src); 77562637Skris if (getsockname(sndsock, (struct sockaddr *)&Src, &i) < 0) { 77662637Skris perror("getsockname"); 77762637Skris exit(1); 77862637Skris } 77962637Skris ident = ntohs(Src.sin6_port); 78062637Skris } 78162637Skris 78255163Sshin /* 78355163Sshin * Message to users 78455163Sshin */ 78562637Skris if (getnameinfo((struct sockaddr *)&Dst, Dst.sin6_len, hbuf, 78662637Skris sizeof(hbuf), NULL, 0, NI_NUMERICHOST | niflag)) 78762637Skris strcpy(hbuf, "(invalid)"); 78862637Skris Fprintf(stderr, "traceroute6"); 78962637Skris Fprintf(stderr, " to %s (%s)", hostname, hbuf); 79055163Sshin if (source) 79155163Sshin Fprintf(stderr, " from %s", source); 79262637Skris Fprintf(stderr, 79362637Skris ", %d hops max, %d byte packets\n", 79462637Skris max_hops, datalen); 79555163Sshin (void) fflush(stderr); 79655163Sshin 79762637Skris if (first_hop > 1) 79862637Skris Printf("Skipping %d intermediate hops\n", first_hop - 1); 79962637Skris 80055163Sshin /* 80155163Sshin * Main loop 80255163Sshin */ 80362637Skris for (hops = first_hop; hops <= max_hops; ++hops) { 80455163Sshin struct in6_addr lastaddr; 80555163Sshin int got_there = 0; 80655163Sshin int unreachable = 0; 80755163Sshin 80855163Sshin Printf("%2d ", hops); 80955163Sshin bzero(&lastaddr, sizeof(lastaddr)); 81055163Sshin for (probe = 0; probe < nprobes; ++probe) { 81155163Sshin int cc; 81255163Sshin struct timeval t1, t2; 81355163Sshin struct timezone tz; 81455163Sshin 81555163Sshin (void) gettimeofday(&t1, &tz); 81655163Sshin send_probe(++seq, hops); 81755163Sshin while ((cc = wait_for_reply(rcvsock, &rcvmhdr))) { 81855163Sshin (void) gettimeofday(&t2, &tz); 81955163Sshin if ((i = packet_ok(&rcvmhdr, cc, seq))) { 82055163Sshin if (! IN6_ARE_ADDR_EQUAL(&Rcv.sin6_addr, 82155163Sshin &lastaddr)) { 82255163Sshin print(&rcvmhdr, cc); 82355163Sshin lastaddr = Rcv.sin6_addr; 82455163Sshin } 82555163Sshin Printf(" %g ms", deltaT(&t1, &t2)); 82655163Sshin switch(i - 1) { 82755163Sshin case ICMP6_DST_UNREACH_NOROUTE: 82855163Sshin ++unreachable; 82955163Sshin Printf(" !N"); 83055163Sshin break; 83155163Sshin case ICMP6_DST_UNREACH_ADMIN: 83255163Sshin ++unreachable; 83355163Sshin Printf(" !P"); 83455163Sshin break; 83555163Sshin case ICMP6_DST_UNREACH_NOTNEIGHBOR: 83655163Sshin ++unreachable; 83755163Sshin Printf(" !S"); 83855163Sshin break; 83955163Sshin case ICMP6_DST_UNREACH_ADDR: 84055163Sshin ++unreachable; 84155163Sshin Printf(" !A"); 84255163Sshin break; 84355163Sshin case ICMP6_DST_UNREACH_NOPORT: 84455163Sshin if (rcvhlim >= 0 && 84555163Sshin rcvhlim <= 1) 84655163Sshin Printf(" !"); 84755163Sshin ++got_there; 84855163Sshin break; 84955163Sshin } 85055163Sshin break; 85155163Sshin } 85255163Sshin } 85355163Sshin if (cc == 0) 85455163Sshin Printf(" *"); 85555163Sshin (void) fflush(stdout); 85655163Sshin } 85755163Sshin putchar('\n'); 85855163Sshin if (got_there || 85955163Sshin (unreachable > 0 && unreachable >= ((nprobes + 1) / 2))) { 86055163Sshin exit(0); 86155163Sshin } 86255163Sshin } 86355163Sshin 86455163Sshin exit(0); 86555163Sshin} 86655163Sshin 86755163Sshinint 86855163Sshinwait_for_reply(sock, mhdr) 86955163Sshin int sock; 87055163Sshin struct msghdr *mhdr; 87155163Sshin{ 87255163Sshin fd_set fds; 87355163Sshin struct timeval wait; 87455163Sshin int cc = 0; 87555163Sshin 87655163Sshin FD_ZERO(&fds); 87755163Sshin FD_SET(sock, &fds); 87855163Sshin wait.tv_sec = waittime; wait.tv_usec = 0; 87955163Sshin 88055163Sshin if (select(sock+1, &fds, (fd_set *)0, (fd_set *)0, &wait) > 0) 88155163Sshin cc = recvmsg(rcvsock, mhdr, 0); 88255163Sshin 88355163Sshin return(cc); 88455163Sshin} 88555163Sshin 88655163Sshin#ifdef IPSEC 88755163Sshin#ifdef IPSEC_POLICY_IPSEC 88855163Sshinint 88955163Sshinsetpolicy(so, policy) 89055163Sshin int so; 89155163Sshin char *policy; 89255163Sshin{ 89355163Sshin char *buf; 89455163Sshin 89555163Sshin buf = ipsec_set_policy(policy, strlen(policy)); 89655163Sshin if (buf == NULL) { 89762637Skris warnx("%s", ipsec_strerror()); 89855163Sshin return -1; 89955163Sshin } 90055163Sshin (void)setsockopt(so, IPPROTO_IPV6, IPV6_IPSEC_POLICY, 90155163Sshin buf, ipsec_get_policylen(buf)); 90255163Sshin 90355163Sshin free(buf); 90455163Sshin 90555163Sshin return 0; 90655163Sshin} 90755163Sshin#endif 90855163Sshin#endif 90955163Sshin 91055163Sshinvoid 91155163Sshinsend_probe(seq, hops) 91255163Sshin int seq, hops; 91355163Sshin{ 91455163Sshin struct opacket *op = outpacket; 91555163Sshin int i; 91655163Sshin 91755163Sshin if(setsockopt(sndsock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 91855163Sshin (char *)&hops, sizeof(hops)) < 0) { 91955163Sshin perror("setsockopt IPV6_UNICAST_HOPS"); 92055163Sshin } 92155163Sshin 92255163Sshin Dst.sin6_port = htons(port + seq); 92355163Sshin 92455163Sshin op->seq = seq; 92555163Sshin op->hops = hops; 92655163Sshin (void) gettimeofday(&op->tv, &tz); 92755163Sshin 92855163Sshin i = sendto(sndsock, (char *)outpacket, datalen , 0, 92962637Skris (struct sockaddr *)&Dst, Dst.sin6_len); 93055163Sshin if (i < 0 || i != datalen) { 93155163Sshin if (i<0) 93255163Sshin perror("sendto"); 93355163Sshin Printf("traceroute6: wrote %s %d chars, ret=%d\n", hostname, 93455163Sshin datalen, i); 93555163Sshin (void) fflush(stdout); 93655163Sshin } 93755163Sshin} 93855163Sshin 93955163Sshinint 94055163Sshinget_hoplim(mhdr) 94155163Sshin struct msghdr *mhdr; 94255163Sshin{ 94355163Sshin struct cmsghdr *cm; 94455163Sshin 94555163Sshin for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; 94655163Sshin cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { 94755163Sshin if (cm->cmsg_level == IPPROTO_IPV6 && 94855163Sshin cm->cmsg_type == IPV6_HOPLIMIT && 94955163Sshin cm->cmsg_len == CMSG_LEN(sizeof(int))) 95055163Sshin return(*(int *)CMSG_DATA(cm)); 95155163Sshin } 95255163Sshin 95355163Sshin return(-1); 95455163Sshin} 95555163Sshin 95655163Sshindouble 95755163SshindeltaT(t1p, t2p) 95855163Sshin struct timeval *t1p, *t2p; 95955163Sshin{ 96055163Sshin register double dt; 96155163Sshin 96255163Sshin dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 + 96355163Sshin (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0; 96455163Sshin return (dt); 96555163Sshin} 96655163Sshin 96755163Sshin 96855163Sshin/* 96955163Sshin * Convert an ICMP "type" field to a printable string. 97055163Sshin */ 97155163Sshinchar * 97255163Sshinpr_type(t0) 97355163Sshin int t0; 97455163Sshin{ 97555163Sshin u_char t = t0 & 0xff; 97655163Sshin char *cp; 97755163Sshin 97855163Sshin switch (t) { 97955163Sshin case ICMP6_DST_UNREACH: 98055163Sshin cp = "Destination Unreachable"; 98155163Sshin break; 98255163Sshin case ICMP6_PACKET_TOO_BIG: 98355163Sshin cp = "Pakcet Too Big"; 98455163Sshin break; 98555163Sshin case ICMP6_TIME_EXCEEDED: 98655163Sshin cp = "Time Exceeded"; 98755163Sshin break; 98855163Sshin case ICMP6_PARAM_PROB: 98955163Sshin cp = "Parameter Problem"; 99055163Sshin break; 99155163Sshin case ICMP6_ECHO_REQUEST: 99255163Sshin cp = "Echo Request"; 99355163Sshin break; 99455163Sshin case ICMP6_ECHO_REPLY: 99555163Sshin cp = "Echo Reply"; 99655163Sshin break; 99755163Sshin case ICMP6_MEMBERSHIP_QUERY: 99855163Sshin cp = "Group Membership Query"; 99955163Sshin break; 100055163Sshin case ICMP6_MEMBERSHIP_REPORT: 100155163Sshin cp = "Group Membership Report"; 100255163Sshin break; 100355163Sshin case ICMP6_MEMBERSHIP_REDUCTION: 100455163Sshin cp = "Group Membership Reduction"; 100555163Sshin break; 100655163Sshin case ND_ROUTER_SOLICIT: 100755163Sshin cp = "Router Solicitation"; 100855163Sshin break; 100955163Sshin case ND_ROUTER_ADVERT: 101055163Sshin cp = "Router Advertisement"; 101155163Sshin break; 101255163Sshin case ND_NEIGHBOR_SOLICIT: 101355163Sshin cp = "Neighbor Solicitation"; 101455163Sshin break; 101555163Sshin case ND_NEIGHBOR_ADVERT: 101655163Sshin cp = "Neighbor Advertisement"; 101755163Sshin break; 101855163Sshin case ND_REDIRECT: 101962637Skris cp = "Redirect"; 102055163Sshin break; 102155163Sshin default: 102255163Sshin cp = "Unknown"; 102355163Sshin break; 102455163Sshin } 102555163Sshin return cp; 102655163Sshin} 102755163Sshin 102855163Sshin 102955163Sshinint 103055163Sshinpacket_ok(mhdr, cc, seq) 103155163Sshin struct msghdr *mhdr; 103255163Sshin int cc; 103355163Sshin int seq; 103455163Sshin{ 103555163Sshin register struct icmp6_hdr *icp; 103655163Sshin struct sockaddr_in6 *from = (struct sockaddr_in6 *)mhdr->msg_name; 103755163Sshin u_char type, code; 103855163Sshin char *buf = (char *)mhdr->msg_iov[0].iov_base; 103955163Sshin struct cmsghdr *cm; 104055163Sshin int *hlimp; 104162637Skris char hbuf[NI_MAXHOST]; 104255163Sshin 104362637Skris#ifdef OLDRAWSOCKET 104462637Skris int hlen; 104562637Skris struct ip6_hdr *ip; 104662637Skris#endif 104762637Skris 104862637Skris#ifdef OLDRAWSOCKET 104962637Skris ip = (struct ip6_hdr *) buf; 105062637Skris hlen = sizeof(struct ip6_hdr); 105162637Skris if (cc < hlen + sizeof(struct icmp6_hdr)) { 105262637Skris if (verbose) { 105362637Skris if (getnameinfo((struct sockaddr *)from, from->sin6_len, 105462637Skris hbuf, sizeof(hbuf), NULL, 0, 105562637Skris NI_NUMERICHOST | niflag) != 0) 105662637Skris strcpy(hbuf, "invalid"); 105762637Skris Printf("packet too short (%d bytes) from %s\n", cc, 105862637Skris hbuf); 105962637Skris } 106062637Skris return (0); 106162637Skris } 106262637Skris cc -= hlen; 106362637Skris icp = (struct icmp6_hdr *)(buf + hlen); 106462637Skris#else 106555163Sshin if (cc < sizeof(struct icmp6_hdr)) { 106662637Skris if (verbose) { 106762637Skris if (getnameinfo((struct sockaddr *)from, from->sin6_len, 106862637Skris hbuf, sizeof(hbuf), NULL, 0, 106962637Skris NI_NUMERICHOST | niflag) != 0) 107062637Skris strcpy(hbuf, "invalid"); 107162637Skris Printf("data too short (%d bytes) from %s\n", cc, hbuf); 107262637Skris } 107355163Sshin return(0); 107455163Sshin } 107555163Sshin icp = (struct icmp6_hdr *)buf; 107662637Skris#endif 107755163Sshin /* get optional information via advanced API */ 107855163Sshin rcvpktinfo = NULL; 107955163Sshin hlimp = NULL; 108055163Sshin for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm; 108155163Sshin cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) { 108255163Sshin if (cm->cmsg_level == IPPROTO_IPV6 && 108355163Sshin cm->cmsg_type == IPV6_PKTINFO && 108455163Sshin cm->cmsg_len == 108555163Sshin CMSG_LEN(sizeof(struct in6_pktinfo))) 108655163Sshin rcvpktinfo = (struct in6_pktinfo *)(CMSG_DATA(cm)); 108755163Sshin 108855163Sshin if (cm->cmsg_level == IPPROTO_IPV6 && 108955163Sshin cm->cmsg_type == IPV6_HOPLIMIT && 109055163Sshin cm->cmsg_len == CMSG_LEN(sizeof(int))) 109155163Sshin hlimp = (int *)CMSG_DATA(cm); 109255163Sshin } 109355163Sshin if (rcvpktinfo == NULL || hlimp == NULL) { 109455163Sshin warnx("failed to get received hop limit or packet info"); 109562637Skris#if 0 109655163Sshin return(0); 109762637Skris#else 109862637Skris rcvhlim = 0; /*XXX*/ 109962637Skris#endif 110055163Sshin } 110162637Skris else 110262637Skris rcvhlim = *hlimp; 110355163Sshin 110455163Sshin type = icp->icmp6_type; 110555163Sshin code = icp->icmp6_code; 110655163Sshin if ((type == ICMP6_TIME_EXCEEDED && code == ICMP6_TIME_EXCEED_TRANSIT) 110755163Sshin || type == ICMP6_DST_UNREACH) { 110855163Sshin struct ip6_hdr *hip; 110955163Sshin struct udphdr *up; 111055163Sshin 111155163Sshin hip = (struct ip6_hdr *)(icp + 1); 111255163Sshin if ((up = get_udphdr(hip, (u_char *)(buf + cc))) == NULL) { 111355163Sshin if (verbose) 111455163Sshin warnx("failed to get upper layer header"); 111555163Sshin return(0); 111655163Sshin } 111755163Sshin if (up->uh_sport == htons(ident) && 111855163Sshin up->uh_dport == htons(port+seq)) 111955163Sshin return (type == ICMP6_TIME_EXCEEDED ? -1 : code + 1); 112055163Sshin } 112155163Sshin if (verbose) { 112255163Sshin int i; 112362637Skris u_int8_t *p; 112462637Skris char sbuf[NI_MAXHOST+1], dbuf[INET6_ADDRSTRLEN]; 112555163Sshin 112662637Skris if (getnameinfo((struct sockaddr *)from, from->sin6_len, 112762637Skris sbuf, sizeof(sbuf), NULL, 0, NI_NUMERICHOST | niflag) != 0) 112862637Skris strcpy(sbuf, "invalid"); 112962637Skris Printf("\n%d bytes from %s to %s", cc, sbuf, 113062637Skris rcvpktinfo ? inet_ntop(AF_INET6, &rcvpktinfo->ipi6_addr, 113162637Skris dbuf, sizeof(dbuf)) 113262637Skris : "?"); 113355163Sshin Printf(": icmp type %d (%s) code %d\n", type, pr_type(type), 113455163Sshin icp->icmp6_code); 113562637Skris p = (u_int8_t *)(icp + 1); 113662637Skris#define WIDTH 16 113762637Skris for (i = 0; i < cc; i++) { 113862637Skris if (i % WIDTH == 0) 113962637Skris Printf("%04x:", i); 114062637Skris if (i % 4 == 0) 114162637Skris Printf(" "); 114262637Skris Printf("%02x", p[i]); 114362637Skris if (i % WIDTH == WIDTH - 1) 114462637Skris Printf("\n"); 114562637Skris } 114662637Skris if (cc % WIDTH != 0) 114762637Skris Printf("\n"); 114855163Sshin } 114955163Sshin return(0); 115055163Sshin} 115155163Sshin 115255163Sshin/* 115355163Sshin * Increment pointer until find the UDP header. 115455163Sshin */ 115555163Sshinstruct udphdr * 115655163Sshinget_udphdr(ip6, lim) 115755163Sshin struct ip6_hdr *ip6; 115855163Sshin u_char *lim; 115955163Sshin{ 116055163Sshin u_char *cp = (u_char *)ip6, nh; 116155163Sshin int hlen; 116255163Sshin 116355163Sshin if (cp + sizeof(*ip6) >= lim) 116455163Sshin return(NULL); 116555163Sshin 116655163Sshin nh = ip6->ip6_nxt; 116755163Sshin cp += sizeof(struct ip6_hdr); 116855163Sshin 116955163Sshin while(lim - cp >= 8) { 117055163Sshin switch(nh) { 117155163Sshin case IPPROTO_ESP: 117255163Sshin case IPPROTO_TCP: 117355163Sshin case IPPROTO_ICMPV6: 117455163Sshin return(NULL); 117555163Sshin case IPPROTO_UDP: 117655163Sshin return((struct udphdr *)cp); 117755163Sshin case IPPROTO_FRAGMENT: 117855163Sshin hlen = sizeof(struct ip6_frag); 117955163Sshin nh = ((struct ip6_frag *)cp)->ip6f_nxt; 118055163Sshin break; 118155163Sshin case IPPROTO_AH: 118255163Sshin hlen = (((struct ip6_ext *)cp)->ip6e_len + 2) << 2; 118355163Sshin nh = ((struct ip6_ext *)cp)->ip6e_nxt; 118455163Sshin break; 118555163Sshin default: 118655163Sshin hlen = (((struct ip6_ext *)cp)->ip6e_len + 1) << 3; 118755163Sshin nh = ((struct ip6_ext *)cp)->ip6e_nxt; 118855163Sshin break; 118955163Sshin } 119055163Sshin 119155163Sshin cp += hlen; 119255163Sshin } 119355163Sshin 119455163Sshin return(NULL); 119555163Sshin} 119655163Sshin 119755163Sshinvoid 119855163Sshinprint(mhdr, cc) 119955163Sshin struct msghdr *mhdr; 120055163Sshin int cc; 120155163Sshin{ 120255163Sshin struct sockaddr_in6 *from = (struct sockaddr_in6 *)mhdr->msg_name; 120362637Skris char hbuf[NI_MAXHOST]; 120455163Sshin 120562637Skris if (getnameinfo((struct sockaddr *)from, from->sin6_len, 120662637Skris hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST | niflag) != 0) 120762637Skris strcpy(hbuf, "invalid"); 120862637Skris if (nflag) 120962637Skris Printf(" %s", hbuf); 121062637Skris else if (lflag) 121162637Skris Printf(" %s (%s)", inetname((struct sockaddr *)from), hbuf); 121262637Skris else 121362637Skris Printf(" %s", inetname((struct sockaddr *)from)); 121455163Sshin 121555163Sshin if (verbose) { 121662637Skris#ifdef OLDRAWSOCKET 121762637Skris Printf(" %d bytes to %s", cc, 121862637Skris rcvpktinfo ? inet_ntop(AF_INET6, &rcvpktinfo->ipi6_addr, 121962637Skris hbuf, sizeof(hbuf)) 122062637Skris : "?"); 122162637Skris#else 122255163Sshin Printf(" %d bytes of data to %s", cc, 122362637Skris rcvpktinfo ? inet_ntop(AF_INET6, &rcvpktinfo->ipi6_addr, 122462637Skris hbuf, sizeof(hbuf)) 122562637Skris : "?"); 122662637Skris#endif 122755163Sshin } 122855163Sshin} 122955163Sshin 123055163Sshin/* 123155163Sshin * Subtract 2 timeval structs: out = out - in. 123255163Sshin * Out is assumed to be >= in. 123355163Sshin */ 123455163Sshinvoid 123555163Sshintvsub(out, in) 123655163Sshin register struct timeval *out, *in; 123755163Sshin{ 123855163Sshin if ((out->tv_usec -= in->tv_usec) < 0) { 123955163Sshin out->tv_sec--; 124055163Sshin out->tv_usec += 1000000; 124155163Sshin } 124255163Sshin out->tv_sec -= in->tv_sec; 124355163Sshin} 124455163Sshin 124555163Sshin 124655163Sshin/* 124755163Sshin * Construct an Internet address representation. 124855163Sshin * If the nflag has been supplied, give 124955163Sshin * numeric value, otherwise try for symbolic name. 125055163Sshin */ 125162637Skrisconst char * 125262637Skrisinetname(sa) 125362637Skris struct sockaddr *sa; 125455163Sshin{ 125555163Sshin register char *cp; 125662637Skris static char line[NI_MAXHOST]; 125755163Sshin static char domain[MAXHOSTNAMELEN + 1]; 125855163Sshin static int first = 1; 125955163Sshin 126055163Sshin if (first && !nflag) { 126155163Sshin first = 0; 126255163Sshin if (gethostname(domain, MAXHOSTNAMELEN) == 0 && 126355163Sshin (cp = index(domain, '.'))) 126455163Sshin (void) strcpy(domain, cp + 1); 126555163Sshin else 126655163Sshin domain[0] = 0; 126755163Sshin } 126862637Skris cp = NULL; 126955163Sshin if (!nflag) { 127062637Skris if (getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0, 127162637Skris NI_NAMEREQD) == 0) { 127262637Skris if ((cp = index(line, '.')) && 127355163Sshin !strcmp(cp + 1, domain)) 127455163Sshin *cp = 0; 127562637Skris cp = line; 127655163Sshin } 127755163Sshin } 127855163Sshin if (cp) 127962637Skris return cp; 128062637Skris 128162637Skris if (getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0, 128262637Skris NI_NUMERICHOST | niflag) != 0) 128362637Skris strcpy(line, "invalid"); 128462637Skris return line; 128555163Sshin} 128655163Sshin 128755163Sshinvoid 128855163Sshinusage() 128955163Sshin{ 129055163Sshin (void)fprintf(stderr, 129162637Skris"usage: traceroute6 [-dlnrv] [-f first_hop] [-m max_hops] [-p port#] \n" 129262637Skris" [-q nqueries] [-s src_addr] [-g gateway] [-w wait] host [data size]\n"); 129355163Sshin exit(1); 129455163Sshin} 1295