10382be72SJeroen Ruigrok/asmodai /* $OpenBSD: traceroute.c,v 1.61 2004/01/26 18:23:51 deraadt Exp $ */
20382be72SJeroen Ruigrok/asmodai /* $NetBSD: traceroute.c,v 1.10 1995/05/21 15:50:45 mycroft Exp $ */
30382be72SJeroen Ruigrok/asmodai
40382be72SJeroen Ruigrok/asmodai /*-
50382be72SJeroen Ruigrok/asmodai * Copyright (c) 1990, 1993
60382be72SJeroen Ruigrok/asmodai * The Regents of the University of California. All rights reserved.
70382be72SJeroen Ruigrok/asmodai *
80382be72SJeroen Ruigrok/asmodai * This code is derived from software contributed to Berkeley by
90382be72SJeroen Ruigrok/asmodai * Van Jacobson.
100382be72SJeroen Ruigrok/asmodai *
110382be72SJeroen Ruigrok/asmodai * Redistribution and use in source and binary forms, with or without
120382be72SJeroen Ruigrok/asmodai * modification, are permitted provided that the following conditions
130382be72SJeroen Ruigrok/asmodai * are met:
140382be72SJeroen Ruigrok/asmodai * 1. Redistributions of source code must retain the above copyright
150382be72SJeroen Ruigrok/asmodai * notice, this list of conditions and the following disclaimer.
160382be72SJeroen Ruigrok/asmodai * 2. Redistributions in binary form must reproduce the above copyright
170382be72SJeroen Ruigrok/asmodai * notice, this list of conditions and the following disclaimer in the
180382be72SJeroen Ruigrok/asmodai * documentation and/or other materials provided with the distribution.
190382be72SJeroen Ruigrok/asmodai * 3. Neither the name of the University nor the names of its contributors
200382be72SJeroen Ruigrok/asmodai * may be used to endorse or promote products derived from this software
210382be72SJeroen Ruigrok/asmodai * without specific prior written permission.
220382be72SJeroen Ruigrok/asmodai *
230382be72SJeroen Ruigrok/asmodai * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
240382be72SJeroen Ruigrok/asmodai * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
250382be72SJeroen Ruigrok/asmodai * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
260382be72SJeroen Ruigrok/asmodai * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
270382be72SJeroen Ruigrok/asmodai * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
280382be72SJeroen Ruigrok/asmodai * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
290382be72SJeroen Ruigrok/asmodai * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
300382be72SJeroen Ruigrok/asmodai * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
310382be72SJeroen Ruigrok/asmodai * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
320382be72SJeroen Ruigrok/asmodai * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
330382be72SJeroen Ruigrok/asmodai * SUCH DAMAGE.
340382be72SJeroen Ruigrok/asmodai *
359dadf159SEirik Nygaard * @(#)traceroute.c 8.1 (Berkeley) 6/6/93
360382be72SJeroen Ruigrok/asmodai */
370382be72SJeroen Ruigrok/asmodai
380382be72SJeroen Ruigrok/asmodai /*
390382be72SJeroen Ruigrok/asmodai * traceroute host - trace the route ip packets follow going to "host".
400382be72SJeroen Ruigrok/asmodai *
410382be72SJeroen Ruigrok/asmodai * Attempt to trace the route an ip packet would follow to some
420382be72SJeroen Ruigrok/asmodai * internet host. We find out intermediate hops by launching probe
430382be72SJeroen Ruigrok/asmodai * packets with a small ttl (time to live) then listening for an
440382be72SJeroen Ruigrok/asmodai * icmp "time exceeded" reply from a gateway. We start our probes
450382be72SJeroen Ruigrok/asmodai * with a ttl of one and increase by one until we get an icmp "port
460382be72SJeroen Ruigrok/asmodai * unreachable" (which means we got to "host") or hit a max (which
470382be72SJeroen Ruigrok/asmodai * defaults to 64 hops & can be changed with the -m flag). Three
480382be72SJeroen Ruigrok/asmodai * probes (change with -q flag) are sent at each ttl setting and a
490382be72SJeroen Ruigrok/asmodai * line is printed showing the ttl, address of the gateway and
500382be72SJeroen Ruigrok/asmodai * round trip time of each probe. If the probe answers come from
510382be72SJeroen Ruigrok/asmodai * different gateways, the address of each responding system will
520382be72SJeroen Ruigrok/asmodai * be printed. If there is no response within a 5 sec. timeout
530382be72SJeroen Ruigrok/asmodai * interval (changed with the -w flag), a "*" is printed for that
540382be72SJeroen Ruigrok/asmodai * probe.
550382be72SJeroen Ruigrok/asmodai *
560382be72SJeroen Ruigrok/asmodai * Probe packets are UDP format. We don't want the destination
570382be72SJeroen Ruigrok/asmodai * host to process them so the destination port is set to an
580382be72SJeroen Ruigrok/asmodai * unlikely value (if some clod on the destination is using that
590382be72SJeroen Ruigrok/asmodai * value, it can be changed with the -p flag).
600382be72SJeroen Ruigrok/asmodai *
610382be72SJeroen Ruigrok/asmodai * A sample use might be:
620382be72SJeroen Ruigrok/asmodai *
630382be72SJeroen Ruigrok/asmodai * [yak 71]% traceroute nis.nsf.net.
640382be72SJeroen Ruigrok/asmodai * traceroute to nis.nsf.net (35.1.1.48), 64 hops max, 56 byte packet
650382be72SJeroen Ruigrok/asmodai * 1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms
660382be72SJeroen Ruigrok/asmodai * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms
670382be72SJeroen Ruigrok/asmodai * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms
680382be72SJeroen Ruigrok/asmodai * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms
690382be72SJeroen Ruigrok/asmodai * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms
700382be72SJeroen Ruigrok/asmodai * 6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms
710382be72SJeroen Ruigrok/asmodai * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms
720382be72SJeroen Ruigrok/asmodai * 8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms
730382be72SJeroen Ruigrok/asmodai * 9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms
740382be72SJeroen Ruigrok/asmodai * 10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms
750382be72SJeroen Ruigrok/asmodai * 11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms
760382be72SJeroen Ruigrok/asmodai *
770382be72SJeroen Ruigrok/asmodai * Note that lines 2 & 3 are the same. This is due to a buggy
780382be72SJeroen Ruigrok/asmodai * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards
790382be72SJeroen Ruigrok/asmodai * packets with a zero ttl.
800382be72SJeroen Ruigrok/asmodai *
810382be72SJeroen Ruigrok/asmodai * A more interesting example is:
820382be72SJeroen Ruigrok/asmodai *
830382be72SJeroen Ruigrok/asmodai * [yak 72]% traceroute allspice.lcs.mit.edu.
840382be72SJeroen Ruigrok/asmodai * traceroute to allspice.lcs.mit.edu (18.26.0.115), 64 hops max
850382be72SJeroen Ruigrok/asmodai * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms
860382be72SJeroen Ruigrok/asmodai * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms
870382be72SJeroen Ruigrok/asmodai * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms
880382be72SJeroen Ruigrok/asmodai * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms
890382be72SJeroen Ruigrok/asmodai * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms
900382be72SJeroen Ruigrok/asmodai * 6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms
910382be72SJeroen Ruigrok/asmodai * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms
920382be72SJeroen Ruigrok/asmodai * 8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms
930382be72SJeroen Ruigrok/asmodai * 9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms
940382be72SJeroen Ruigrok/asmodai * 10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms
950382be72SJeroen Ruigrok/asmodai * 11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms
960382be72SJeroen Ruigrok/asmodai * 12 * * *
970382be72SJeroen Ruigrok/asmodai * 13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms
980382be72SJeroen Ruigrok/asmodai * 14 * * *
990382be72SJeroen Ruigrok/asmodai * 15 * * *
1000382be72SJeroen Ruigrok/asmodai * 16 * * *
1010382be72SJeroen Ruigrok/asmodai * 17 * * *
1020382be72SJeroen Ruigrok/asmodai * 18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms
1030382be72SJeroen Ruigrok/asmodai *
1040382be72SJeroen Ruigrok/asmodai * (I start to see why I'm having so much trouble with mail to
1050382be72SJeroen Ruigrok/asmodai * MIT.) Note that the gateways 12, 14, 15, 16 & 17 hops away
1060382be72SJeroen Ruigrok/asmodai * either don't send ICMP "time exceeded" messages or send them
1070382be72SJeroen Ruigrok/asmodai * with a ttl too small to reach us. 14 - 17 are running the
1080382be72SJeroen Ruigrok/asmodai * MIT C Gateway code that doesn't send "time exceeded"s. God
1090382be72SJeroen Ruigrok/asmodai * only knows what's going on with 12.
1100382be72SJeroen Ruigrok/asmodai *
1110382be72SJeroen Ruigrok/asmodai * The silent gateway 12 in the above may be the result of a bug in
1120382be72SJeroen Ruigrok/asmodai * the 4.[23]BSD network code (and its derivatives): 4.x (x <= 3)
1130382be72SJeroen Ruigrok/asmodai * sends an unreachable message using whatever ttl remains in the
1140382be72SJeroen Ruigrok/asmodai * original datagram. Since, for gateways, the remaining ttl is
1150382be72SJeroen Ruigrok/asmodai * zero, the icmp "time exceeded" is guaranteed to not make it back
1160382be72SJeroen Ruigrok/asmodai * to us. The behavior of this bug is slightly more interesting
1170382be72SJeroen Ruigrok/asmodai * when it appears on the destination system:
1180382be72SJeroen Ruigrok/asmodai *
1190382be72SJeroen Ruigrok/asmodai * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms
1200382be72SJeroen Ruigrok/asmodai * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms
1210382be72SJeroen Ruigrok/asmodai * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms
1220382be72SJeroen Ruigrok/asmodai * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms
1230382be72SJeroen Ruigrok/asmodai * 5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms
1240382be72SJeroen Ruigrok/asmodai * 6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms
1250382be72SJeroen Ruigrok/asmodai * 7 * * *
1260382be72SJeroen Ruigrok/asmodai * 8 * * *
1270382be72SJeroen Ruigrok/asmodai * 9 * * *
1280382be72SJeroen Ruigrok/asmodai * 10 * * *
1290382be72SJeroen Ruigrok/asmodai * 11 * * *
1300382be72SJeroen Ruigrok/asmodai * 12 * * *
1310382be72SJeroen Ruigrok/asmodai * 13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms !
1320382be72SJeroen Ruigrok/asmodai *
1330382be72SJeroen Ruigrok/asmodai * Notice that there are 12 "gateways" (13 is the final
1340382be72SJeroen Ruigrok/asmodai * destination) and exactly the last half of them are "missing".
1350382be72SJeroen Ruigrok/asmodai * What's really happening is that rip (a Sun-3 running Sun OS3.5)
1360382be72SJeroen Ruigrok/asmodai * is using the ttl from our arriving datagram as the ttl in its
1370382be72SJeroen Ruigrok/asmodai * icmp reply. So, the reply will time out on the return path
1380382be72SJeroen Ruigrok/asmodai * (with no notice sent to anyone since icmp's aren't sent for
1390382be72SJeroen Ruigrok/asmodai * icmp's) until we probe with a ttl that's at least twice the path
1400382be72SJeroen Ruigrok/asmodai * length. I.e., rip is really only 7 hops away. A reply that
1410382be72SJeroen Ruigrok/asmodai * returns with a ttl of 1 is a clue this problem exists.
1420382be72SJeroen Ruigrok/asmodai * Traceroute prints a "!" after the time if the ttl is <= 1.
1430382be72SJeroen Ruigrok/asmodai * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or
1440382be72SJeroen Ruigrok/asmodai * non-standard (HPUX) software, expect to see this problem
1450382be72SJeroen Ruigrok/asmodai * frequently and/or take care picking the target host of your
1460382be72SJeroen Ruigrok/asmodai * probes.
1470382be72SJeroen Ruigrok/asmodai *
1480382be72SJeroen Ruigrok/asmodai * Other possible annotations after the time are !H, !N, !P (got a host,
1490382be72SJeroen Ruigrok/asmodai * network or protocol unreachable, respectively), !S or !F (source
1500382be72SJeroen Ruigrok/asmodai * route failed or fragmentation needed -- neither of these should
1510382be72SJeroen Ruigrok/asmodai * ever occur and the associated gateway is busted if you see one). If
1520382be72SJeroen Ruigrok/asmodai * almost all the probes result in some kind of unreachable, traceroute
1530382be72SJeroen Ruigrok/asmodai * will give up and exit.
1540382be72SJeroen Ruigrok/asmodai *
1550382be72SJeroen Ruigrok/asmodai * Notes
1560382be72SJeroen Ruigrok/asmodai * -----
1570382be72SJeroen Ruigrok/asmodai * This program must be run by root or be setuid. (I suggest that
1580382be72SJeroen Ruigrok/asmodai * you *don't* make it setuid -- casual use could result in a lot
1590382be72SJeroen Ruigrok/asmodai * of unnecessary traffic on our poor, congested nets.)
1600382be72SJeroen Ruigrok/asmodai *
1610382be72SJeroen Ruigrok/asmodai * This program requires a kernel mod that does not appear in any
1620382be72SJeroen Ruigrok/asmodai * system available from Berkeley: A raw ip socket using proto
1630382be72SJeroen Ruigrok/asmodai * IPPROTO_RAW must interpret the data sent as an ip datagram (as
1640382be72SJeroen Ruigrok/asmodai * opposed to data to be wrapped in a ip datagram). See the README
1650382be72SJeroen Ruigrok/asmodai * file that came with the source to this program for a description
1660382be72SJeroen Ruigrok/asmodai * of the mods I made to /sys/netinet/raw_ip.c. Your mileage may
1670382be72SJeroen Ruigrok/asmodai * vary. But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE
1680382be72SJeroen Ruigrok/asmodai * MODIFIED TO RUN THIS PROGRAM.
1690382be72SJeroen Ruigrok/asmodai *
1700382be72SJeroen Ruigrok/asmodai * The udp port usage may appear bizarre (well, ok, it is bizarre).
1710382be72SJeroen Ruigrok/asmodai * The problem is that an icmp message only contains 8 bytes of
1720382be72SJeroen Ruigrok/asmodai * data from the original datagram. 8 bytes is the size of a udp
1730382be72SJeroen Ruigrok/asmodai * header so, if we want to associate replies with the original
1740382be72SJeroen Ruigrok/asmodai * datagram, the necessary information must be encoded into the
1750382be72SJeroen Ruigrok/asmodai * udp header (the ip id could be used but there's no way to
1760382be72SJeroen Ruigrok/asmodai * interlock with the kernel's assignment of ip id's and, anyway,
1770382be72SJeroen Ruigrok/asmodai * it would have taken a lot more kernel hacking to allow this
1780382be72SJeroen Ruigrok/asmodai * code to set the ip id). So, to allow two or more users to
1790382be72SJeroen Ruigrok/asmodai * use traceroute simultaneously, we use this task's pid as the
1800382be72SJeroen Ruigrok/asmodai * source port (the high bit is set to move the port number out
1810382be72SJeroen Ruigrok/asmodai * of the "likely" range). To keep track of which probe is being
1820382be72SJeroen Ruigrok/asmodai * replied to (so times and/or hop counts don't get confused by a
1830382be72SJeroen Ruigrok/asmodai * reply that was delayed in transit), we increment the destination
1840382be72SJeroen Ruigrok/asmodai * port number before each probe.
1850382be72SJeroen Ruigrok/asmodai *
1860382be72SJeroen Ruigrok/asmodai * Don't use this as a coding example. I was trying to find a
1870382be72SJeroen Ruigrok/asmodai * routing problem and this code sort-of popped out after 48 hours
1880382be72SJeroen Ruigrok/asmodai * without sleep. I was amazed it ever compiled, much less ran.
1890382be72SJeroen Ruigrok/asmodai *
1900382be72SJeroen Ruigrok/asmodai * I stole the idea for this program from Steve Deering. Since
1910382be72SJeroen Ruigrok/asmodai * the first release, I've learned that had I attended the right
1920382be72SJeroen Ruigrok/asmodai * IETF working group meetings, I also could have stolen it from Guy
1930382be72SJeroen Ruigrok/asmodai * Almes or Matt Mathis. I don't know (or care) who came up with
1940382be72SJeroen Ruigrok/asmodai * the idea first. I envy the originators' perspicacity and I'm
1950382be72SJeroen Ruigrok/asmodai * glad they didn't keep the idea a secret.
1960382be72SJeroen Ruigrok/asmodai *
1970382be72SJeroen Ruigrok/asmodai * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or
1980382be72SJeroen Ruigrok/asmodai * enhancements to the original distribution.
1990382be72SJeroen Ruigrok/asmodai *
2000382be72SJeroen Ruigrok/asmodai * I've hacked up a round-trip-route version of this that works by
2010382be72SJeroen Ruigrok/asmodai * sending a loose-source-routed udp datagram through the destination
2020382be72SJeroen Ruigrok/asmodai * back to yourself. Unfortunately, SO many gateways botch source
2030382be72SJeroen Ruigrok/asmodai * routing, the thing is almost worthless. Maybe one day...
2040382be72SJeroen Ruigrok/asmodai *
2050382be72SJeroen Ruigrok/asmodai * -- Van Jacobson (van@helios.ee.lbl.gov)
2060382be72SJeroen Ruigrok/asmodai * Tue Dec 20 03:50:13 PST 1988
2070382be72SJeroen Ruigrok/asmodai */
2080382be72SJeroen Ruigrok/asmodai
2090382be72SJeroen Ruigrok/asmodai #include <sys/param.h>
2100382be72SJeroen Ruigrok/asmodai #include <sys/time.h>
2110382be72SJeroen Ruigrok/asmodai #include <sys/socket.h>
2120382be72SJeroen Ruigrok/asmodai #include <sys/file.h>
2130382be72SJeroen Ruigrok/asmodai #include <sys/ioctl.h>
2140382be72SJeroen Ruigrok/asmodai #include <sys/sysctl.h>
2150382be72SJeroen Ruigrok/asmodai
2160382be72SJeroen Ruigrok/asmodai #include <netinet/in_systm.h>
2170382be72SJeroen Ruigrok/asmodai #include <netinet/in.h>
2180382be72SJeroen Ruigrok/asmodai #include <netinet/ip.h>
2190382be72SJeroen Ruigrok/asmodai #include <netinet/ip_icmp.h>
2200382be72SJeroen Ruigrok/asmodai #include <netinet/ip_var.h>
2210382be72SJeroen Ruigrok/asmodai #include <netinet/udp.h>
2220382be72SJeroen Ruigrok/asmodai
2230382be72SJeroen Ruigrok/asmodai #include <arpa/inet.h>
2240382be72SJeroen Ruigrok/asmodai
2250382be72SJeroen Ruigrok/asmodai #include <ctype.h>
2260382be72SJeroen Ruigrok/asmodai #include <err.h>
2270382be72SJeroen Ruigrok/asmodai #include <errno.h>
2280382be72SJeroen Ruigrok/asmodai #include <netdb.h>
2290382be72SJeroen Ruigrok/asmodai #include <stdio.h>
2300382be72SJeroen Ruigrok/asmodai #include <stdlib.h>
2310382be72SJeroen Ruigrok/asmodai #include <string.h>
2320382be72SJeroen Ruigrok/asmodai #include <unistd.h>
2330382be72SJeroen Ruigrok/asmodai
2340382be72SJeroen Ruigrok/asmodai #define MAX_LSRR ((MAX_IPOPTLEN - 4) / 4)
2350382be72SJeroen Ruigrok/asmodai
2360382be72SJeroen Ruigrok/asmodai /*
2370382be72SJeroen Ruigrok/asmodai * Format of the data in a (udp) probe packet.
2380382be72SJeroen Ruigrok/asmodai */
2390382be72SJeroen Ruigrok/asmodai struct packetdata {
2400382be72SJeroen Ruigrok/asmodai u_char seq; /* sequence number of this packet */
2410382be72SJeroen Ruigrok/asmodai u_int8_t ttl; /* ttl packet left with */
2420382be72SJeroen Ruigrok/asmodai u_int32_t sec; /* time packet left */
2430382be72SJeroen Ruigrok/asmodai u_int32_t usec;
2440382be72SJeroen Ruigrok/asmodai };
2450382be72SJeroen Ruigrok/asmodai
24646877004SHasso Tepper /*
247dea1b2d2SHasso Tepper * Support for ICMP extensions - RFC4950.
24846877004SHasso Tepper */
24946877004SHasso Tepper #define ICMP_EXT_OFFSET 8 + 128 /* ICMP type, code, checksum (unused)
25046877004SHasso Tepper * + original datagram */
25146877004SHasso Tepper #define ICMP_EXT_VERSION 2
25246877004SHasso Tepper
25346877004SHasso Tepper /* ICMP Extension Header according to RFC4884. */
25446877004SHasso Tepper #define EXT_VERSION(x) (((x) & 0xf0000000) >> 28)
25546877004SHasso Tepper #define EXT_CHECKSUM(x) ((x) & 0x0000ffff)
25646877004SHasso Tepper
25746877004SHasso Tepper /*
25846877004SHasso Tepper * ICMP extensions, object header
25946877004SHasso Tepper */
26046877004SHasso Tepper struct icmp_ext_obj_hdr {
26146877004SHasso Tepper u_short length;
26246877004SHasso Tepper u_char class_num;
26346877004SHasso Tepper #define MPLS_STACK_ENTRY_CLASS 1
26446877004SHasso Tepper u_char c_type;
26546877004SHasso Tepper #define MPLS_STACK_ENTRY_C_TYPE 1
26646877004SHasso Tepper };
26746877004SHasso Tepper
26846877004SHasso Tepper /* MPLS Label Stack Object. */
26946877004SHasso Tepper #define MPLS_LABEL(x) (((x) & 0xfffff000) >> 12)
27046877004SHasso Tepper #define MPLS_EXP(x) (((x) & 0x00000e00) >> 9)
27146877004SHasso Tepper #define MPLS_STACK(x) (((x) & 0x00000100) >> 8)
27246877004SHasso Tepper #define MPLS_TTL(x) ((x) & 0x000000ff)
27346877004SHasso Tepper
274d6410035SSascha Wildner static struct in_addr gateway[MAX_LSRR + 1];
275d6410035SSascha Wildner static int lsrrlen = 0;
276d6410035SSascha Wildner static int32_t sec_perturb;
277d6410035SSascha Wildner static int32_t usec_perturb;
2780382be72SJeroen Ruigrok/asmodai
279d6410035SSascha Wildner static u_char packet[512], *outpacket; /* last inbound (icmp) packet */
2800382be72SJeroen Ruigrok/asmodai
281d6410035SSascha Wildner static void decode_extensions(unsigned char *, int);
282d6410035SSascha Wildner static void dump_packet(void);
283d6410035SSascha Wildner static int wait_for_reply(int, struct sockaddr_in *, struct timeval *);
284d6410035SSascha Wildner static void send_probe(int, u_int8_t, int, struct sockaddr_in *);
285d6410035SSascha Wildner static int packet_ok(u_char *, int, struct sockaddr_in *, int, int);
286d6410035SSascha Wildner static const char *pr_type(u_int8_t);
287d6410035SSascha Wildner static void print(u_char *, int, struct sockaddr_in *);
288d6410035SSascha Wildner static char *inetname(struct in_addr);
289d6410035SSascha Wildner static u_short in_cksum(u_short *, int);
290fe08e20dSSascha Wildner static void usage(void) __dead2;
2910382be72SJeroen Ruigrok/asmodai
292d6410035SSascha Wildner static int s; /* receive (icmp) socket file descriptor */
293d6410035SSascha Wildner static int sndsock; /* send (udp) socket file descriptor */
2940382be72SJeroen Ruigrok/asmodai
295d6410035SSascha Wildner static int datalen; /* How much data */
296d6410035SSascha Wildner static int headerlen; /* How long packet's header is */
2970382be72SJeroen Ruigrok/asmodai
298d6410035SSascha Wildner static char *source = NULL;
299d6410035SSascha Wildner static char *hostname;
3000382be72SJeroen Ruigrok/asmodai
301d6410035SSascha Wildner static int nprobes = 3;
302d6410035SSascha Wildner static u_int8_t max_ttl = IPDEFTTL;
303d6410035SSascha Wildner static u_int8_t first_ttl = 1;
304d6410035SSascha Wildner static u_short ident;
305d6410035SSascha Wildner static u_short port = 32768+666; /* start udp dest port # for probe packets */
306d6410035SSascha Wildner static u_char proto = IPPROTO_UDP;
307d6410035SSascha Wildner static u_int8_t icmp_type = ICMP_ECHO; /* default ICMP code/type */
308d6410035SSascha Wildner static u_char icmp_code = 0;
309d6410035SSascha Wildner static int options; /* socket options */
310d6410035SSascha Wildner static int verbose;
311d6410035SSascha Wildner static int waittime = 5; /* time to wait for response (in seconds) */
312d6410035SSascha Wildner static int nflag; /* print addresses numerically */
313d6410035SSascha Wildner static int dump;
314d6410035SSascha Wildner static int Mflag; /* show MPLS labels if any */
3150382be72SJeroen Ruigrok/asmodai
3160382be72SJeroen Ruigrok/asmodai int
main(int argc,char * argv[])3170382be72SJeroen Ruigrok/asmodai main(int argc, char *argv[])
3180382be72SJeroen Ruigrok/asmodai {
3190382be72SJeroen Ruigrok/asmodai int mib[4] = { CTL_NET, PF_INET, IPPROTO_IP, IPCTL_DEFTTL };
3200382be72SJeroen Ruigrok/asmodai int ttl_flag = 0, incflag = 1, protoset = 0, sump = 0;
3210382be72SJeroen Ruigrok/asmodai int ch, i, lsrr = 0, on = 1, probe, seq = 0, tos = 0;
3220382be72SJeroen Ruigrok/asmodai size_t size = sizeof(max_ttl);
3230382be72SJeroen Ruigrok/asmodai struct sockaddr_in from, to;
3240382be72SJeroen Ruigrok/asmodai struct hostent *hp;
3250382be72SJeroen Ruigrok/asmodai u_int32_t tmprnd;
3260382be72SJeroen Ruigrok/asmodai struct ip *ip;
3270382be72SJeroen Ruigrok/asmodai u_int8_t ttl;
3280382be72SJeroen Ruigrok/asmodai char *ep;
3290382be72SJeroen Ruigrok/asmodai long l;
3300382be72SJeroen Ruigrok/asmodai
3310382be72SJeroen Ruigrok/asmodai if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
3320382be72SJeroen Ruigrok/asmodai err(5, "icmp socket");
3330382be72SJeroen Ruigrok/asmodai if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
3340382be72SJeroen Ruigrok/asmodai err(5, "raw socket");
3350382be72SJeroen Ruigrok/asmodai
3360382be72SJeroen Ruigrok/asmodai /* revoke privs */
3370382be72SJeroen Ruigrok/asmodai seteuid(getuid());
3380382be72SJeroen Ruigrok/asmodai setuid(getuid());
3390382be72SJeroen Ruigrok/asmodai
340*e62ef63cSSascha Wildner sysctl(mib, NELEM(mib), &max_ttl, &size, NULL, 0);
3410382be72SJeroen Ruigrok/asmodai
34246877004SHasso Tepper while ((ch = getopt(argc, argv, "SDIdg:f:m:np:q:rs:t:w:vlP:cM")) != -1)
3430382be72SJeroen Ruigrok/asmodai switch (ch) {
3440382be72SJeroen Ruigrok/asmodai case 'S':
3450382be72SJeroen Ruigrok/asmodai sump = 1;
3460382be72SJeroen Ruigrok/asmodai break;
3470382be72SJeroen Ruigrok/asmodai case 'f':
3480382be72SJeroen Ruigrok/asmodai errno = 0;
3490382be72SJeroen Ruigrok/asmodai ep = NULL;
3500382be72SJeroen Ruigrok/asmodai l = strtol(optarg, &ep, 10);
3510382be72SJeroen Ruigrok/asmodai if (errno || !*optarg || *ep || l < 1 || l > max_ttl)
3520382be72SJeroen Ruigrok/asmodai errx(1, "min ttl must be 1 to %u.", max_ttl);
3530382be72SJeroen Ruigrok/asmodai first_ttl = (u_int8_t)l;
3540382be72SJeroen Ruigrok/asmodai break;
3550382be72SJeroen Ruigrok/asmodai case 'c':
3560382be72SJeroen Ruigrok/asmodai incflag = 0;
3570382be72SJeroen Ruigrok/asmodai break;
3580382be72SJeroen Ruigrok/asmodai case 'd':
3590382be72SJeroen Ruigrok/asmodai options |= SO_DEBUG;
3600382be72SJeroen Ruigrok/asmodai break;
3610382be72SJeroen Ruigrok/asmodai case 'D':
3620382be72SJeroen Ruigrok/asmodai dump = 1;
3630382be72SJeroen Ruigrok/asmodai break;
3640382be72SJeroen Ruigrok/asmodai case 'g':
3650382be72SJeroen Ruigrok/asmodai if (lsrr >= MAX_LSRR)
3660382be72SJeroen Ruigrok/asmodai errx(1, "too many gateways; max %d", MAX_LSRR);
3670382be72SJeroen Ruigrok/asmodai if (inet_aton(optarg, &gateway[lsrr]) == 0) {
3680382be72SJeroen Ruigrok/asmodai hp = gethostbyname(optarg);
369678e8cc6SSascha Wildner if (hp == NULL)
3700382be72SJeroen Ruigrok/asmodai errx(1, "unknown host %s", optarg);
3710382be72SJeroen Ruigrok/asmodai memcpy(&gateway[lsrr], hp->h_addr, hp->h_length);
3720382be72SJeroen Ruigrok/asmodai }
3730382be72SJeroen Ruigrok/asmodai if (++lsrr == 1)
3740382be72SJeroen Ruigrok/asmodai lsrrlen = 4;
3750382be72SJeroen Ruigrok/asmodai lsrrlen += 4;
3760382be72SJeroen Ruigrok/asmodai break;
3770382be72SJeroen Ruigrok/asmodai case 'I':
3780382be72SJeroen Ruigrok/asmodai if (protoset)
3790382be72SJeroen Ruigrok/asmodai errx(1, "protocol already set with -P");
3800382be72SJeroen Ruigrok/asmodai protoset = 1;
3810382be72SJeroen Ruigrok/asmodai proto = IPPROTO_ICMP;
3820382be72SJeroen Ruigrok/asmodai break;
3830382be72SJeroen Ruigrok/asmodai case 'l':
3840382be72SJeroen Ruigrok/asmodai ttl_flag++;
3850382be72SJeroen Ruigrok/asmodai break;
3860382be72SJeroen Ruigrok/asmodai case 'm':
3870382be72SJeroen Ruigrok/asmodai errno = 0;
3880382be72SJeroen Ruigrok/asmodai ep = NULL;
3890382be72SJeroen Ruigrok/asmodai l = strtol(optarg, &ep, 10);
3900382be72SJeroen Ruigrok/asmodai if (errno || !*optarg || *ep || l < first_ttl ||
3910382be72SJeroen Ruigrok/asmodai l > MAXTTL)
3920382be72SJeroen Ruigrok/asmodai errx(1, "max ttl must be %u to %u.", first_ttl,
3930382be72SJeroen Ruigrok/asmodai MAXTTL);
3940382be72SJeroen Ruigrok/asmodai max_ttl = (u_int8_t)l;
3950382be72SJeroen Ruigrok/asmodai break;
39646877004SHasso Tepper case 'M':
39746877004SHasso Tepper Mflag = 1;
39846877004SHasso Tepper break;
3990382be72SJeroen Ruigrok/asmodai case 'n':
4000382be72SJeroen Ruigrok/asmodai nflag++;
4010382be72SJeroen Ruigrok/asmodai break;
4020382be72SJeroen Ruigrok/asmodai case 'p':
4030382be72SJeroen Ruigrok/asmodai errno = 0;
4040382be72SJeroen Ruigrok/asmodai ep = NULL;
4050382be72SJeroen Ruigrok/asmodai l = strtol(optarg, &ep, 10);
4060382be72SJeroen Ruigrok/asmodai if (errno || !*optarg || *ep || l <= 0 || l >= 65536)
4070382be72SJeroen Ruigrok/asmodai errx(1, "port must be >0, <65536.");
4080382be72SJeroen Ruigrok/asmodai port = (int)l;
4090382be72SJeroen Ruigrok/asmodai break;
4100382be72SJeroen Ruigrok/asmodai case 'P':
4110382be72SJeroen Ruigrok/asmodai if (protoset)
4120382be72SJeroen Ruigrok/asmodai errx(1, "protocol already set with -I");
4130382be72SJeroen Ruigrok/asmodai protoset = 1;
4140382be72SJeroen Ruigrok/asmodai errno = 0;
4150382be72SJeroen Ruigrok/asmodai ep = NULL;
4160382be72SJeroen Ruigrok/asmodai l = strtol(optarg, &ep, 10);
4170382be72SJeroen Ruigrok/asmodai if (errno || !*optarg || *ep || l < 1 ||
4180382be72SJeroen Ruigrok/asmodai l >= IPPROTO_MAX) {
4190382be72SJeroen Ruigrok/asmodai struct protoent *pent;
4200382be72SJeroen Ruigrok/asmodai
4210382be72SJeroen Ruigrok/asmodai pent = getprotobyname(optarg);
4220382be72SJeroen Ruigrok/asmodai if (pent)
4230382be72SJeroen Ruigrok/asmodai proto = pent->p_proto;
4240382be72SJeroen Ruigrok/asmodai else
4250382be72SJeroen Ruigrok/asmodai errx(1, "proto must be >=1, or a name.");
4260382be72SJeroen Ruigrok/asmodai } else
4270382be72SJeroen Ruigrok/asmodai proto = (int)l;
4280382be72SJeroen Ruigrok/asmodai break;
4290382be72SJeroen Ruigrok/asmodai case 'q':
4300382be72SJeroen Ruigrok/asmodai errno = 0;
4310382be72SJeroen Ruigrok/asmodai ep = NULL;
4320382be72SJeroen Ruigrok/asmodai l = strtol(optarg, &ep, 10);
4330382be72SJeroen Ruigrok/asmodai if (errno || !*optarg || *ep || l < 1 || l > INT_MAX)
4340382be72SJeroen Ruigrok/asmodai errx(1, "nprobes must be >0.");
4350382be72SJeroen Ruigrok/asmodai nprobes = (int)l;
4360382be72SJeroen Ruigrok/asmodai break;
4370382be72SJeroen Ruigrok/asmodai case 'r':
4380382be72SJeroen Ruigrok/asmodai options |= SO_DONTROUTE;
4390382be72SJeroen Ruigrok/asmodai break;
4400382be72SJeroen Ruigrok/asmodai case 's':
4410382be72SJeroen Ruigrok/asmodai /*
4420382be72SJeroen Ruigrok/asmodai * set the ip source address of the outbound
4430382be72SJeroen Ruigrok/asmodai * probe (e.g., on a multi-homed host).
4440382be72SJeroen Ruigrok/asmodai */
4450382be72SJeroen Ruigrok/asmodai source = optarg;
4460382be72SJeroen Ruigrok/asmodai break;
4470382be72SJeroen Ruigrok/asmodai case 't':
4480382be72SJeroen Ruigrok/asmodai errno = 0;
4490382be72SJeroen Ruigrok/asmodai ep = NULL;
4500382be72SJeroen Ruigrok/asmodai l = strtol(optarg, &ep, 10);
4510382be72SJeroen Ruigrok/asmodai if (errno || !*optarg || *ep || l < 0 || l > 255)
4520382be72SJeroen Ruigrok/asmodai errx(1, "tos must be 0 to 255.");
4530382be72SJeroen Ruigrok/asmodai tos = (int)l;
4540382be72SJeroen Ruigrok/asmodai break;
4550382be72SJeroen Ruigrok/asmodai case 'v':
4560382be72SJeroen Ruigrok/asmodai verbose++;
4570382be72SJeroen Ruigrok/asmodai break;
4580382be72SJeroen Ruigrok/asmodai case 'w':
4590382be72SJeroen Ruigrok/asmodai errno = 0;
4600382be72SJeroen Ruigrok/asmodai ep = NULL;
4610382be72SJeroen Ruigrok/asmodai l = strtol(optarg, &ep, 10);
4620382be72SJeroen Ruigrok/asmodai if (errno || !*optarg || *ep || l <= 1 || l > INT_MAX)
4630382be72SJeroen Ruigrok/asmodai errx(1, "wait must be >1 sec.");
4640382be72SJeroen Ruigrok/asmodai waittime = (int)l;
4650382be72SJeroen Ruigrok/asmodai break;
4660382be72SJeroen Ruigrok/asmodai default:
4670382be72SJeroen Ruigrok/asmodai usage();
4680382be72SJeroen Ruigrok/asmodai }
4690382be72SJeroen Ruigrok/asmodai argc -= optind;
4700382be72SJeroen Ruigrok/asmodai argv += optind;
4710382be72SJeroen Ruigrok/asmodai
4720382be72SJeroen Ruigrok/asmodai if (argc < 1)
4730382be72SJeroen Ruigrok/asmodai usage();
4740382be72SJeroen Ruigrok/asmodai
4750382be72SJeroen Ruigrok/asmodai setlinebuf (stdout);
4760382be72SJeroen Ruigrok/asmodai
47771126e33SSascha Wildner memset(&to, 0, sizeof(struct sockaddr));
4780382be72SJeroen Ruigrok/asmodai to.sin_family = AF_INET;
4790382be72SJeroen Ruigrok/asmodai if (inet_aton(*argv, &to.sin_addr) != 0)
4800382be72SJeroen Ruigrok/asmodai hostname = *argv;
4810382be72SJeroen Ruigrok/asmodai else {
4820382be72SJeroen Ruigrok/asmodai hp = gethostbyname(*argv);
483678e8cc6SSascha Wildner if (hp == NULL)
4840382be72SJeroen Ruigrok/asmodai errx(1, "unknown host %s", *argv);
4850382be72SJeroen Ruigrok/asmodai to.sin_family = hp->h_addrtype;
4860382be72SJeroen Ruigrok/asmodai memcpy(&to.sin_addr, hp->h_addr, hp->h_length);
4870382be72SJeroen Ruigrok/asmodai if ((hostname = strdup(hp->h_name)) == NULL)
4880382be72SJeroen Ruigrok/asmodai err(1, "malloc");
4890382be72SJeroen Ruigrok/asmodai if (hp->h_addr_list[1] != NULL)
4900382be72SJeroen Ruigrok/asmodai warnx("Warning: %s has multiple addresses; using %s",
4910382be72SJeroen Ruigrok/asmodai hostname, inet_ntoa(to.sin_addr));
4920382be72SJeroen Ruigrok/asmodai }
4930382be72SJeroen Ruigrok/asmodai if (*++argv) {
4940382be72SJeroen Ruigrok/asmodai errno = 0;
4950382be72SJeroen Ruigrok/asmodai ep = NULL;
4960382be72SJeroen Ruigrok/asmodai l = strtol(*argv, &ep, 10);
4970382be72SJeroen Ruigrok/asmodai if (errno || !*argv || *ep || l < 0 || l > INT_MAX)
4980382be72SJeroen Ruigrok/asmodai errx(1, "datalen out of range");
4990382be72SJeroen Ruigrok/asmodai datalen = (int)l;
5000382be72SJeroen Ruigrok/asmodai }
5010382be72SJeroen Ruigrok/asmodai
5020382be72SJeroen Ruigrok/asmodai switch (proto) {
5030382be72SJeroen Ruigrok/asmodai case IPPROTO_UDP:
5040382be72SJeroen Ruigrok/asmodai headerlen = (sizeof(struct ip) + lsrrlen +
5050382be72SJeroen Ruigrok/asmodai sizeof(struct udphdr) + sizeof(struct packetdata));
5060382be72SJeroen Ruigrok/asmodai break;
5070382be72SJeroen Ruigrok/asmodai case IPPROTO_ICMP:
5080382be72SJeroen Ruigrok/asmodai headerlen = (sizeof(struct ip) + lsrrlen +
5090382be72SJeroen Ruigrok/asmodai sizeof(struct icmp) + sizeof(struct packetdata));
5100382be72SJeroen Ruigrok/asmodai break;
5110382be72SJeroen Ruigrok/asmodai default:
5120382be72SJeroen Ruigrok/asmodai headerlen = (sizeof(struct ip) + lsrrlen +
5130382be72SJeroen Ruigrok/asmodai sizeof(struct packetdata));
5140382be72SJeroen Ruigrok/asmodai }
5150382be72SJeroen Ruigrok/asmodai
5160382be72SJeroen Ruigrok/asmodai if (datalen < 0 || datalen > IP_MAXPACKET - headerlen)
5170382be72SJeroen Ruigrok/asmodai errx(1, "packet size must be 0 to %d.",
5180382be72SJeroen Ruigrok/asmodai IP_MAXPACKET - headerlen);
5190382be72SJeroen Ruigrok/asmodai
5200382be72SJeroen Ruigrok/asmodai datalen += headerlen;
5210382be72SJeroen Ruigrok/asmodai
5220382be72SJeroen Ruigrok/asmodai outpacket = (u_char *)malloc(datalen);
5239dadf159SEirik Nygaard if (outpacket == NULL)
5240382be72SJeroen Ruigrok/asmodai err(1, "malloc");
52571126e33SSascha Wildner memset(outpacket, 0, datalen);
5260382be72SJeroen Ruigrok/asmodai
5270382be72SJeroen Ruigrok/asmodai ip = (struct ip *)outpacket;
5280382be72SJeroen Ruigrok/asmodai if (lsrr != 0) {
5290382be72SJeroen Ruigrok/asmodai u_char *p = (u_char *)(ip + 1);
5300382be72SJeroen Ruigrok/asmodai
5310382be72SJeroen Ruigrok/asmodai *p++ = IPOPT_NOP;
5320382be72SJeroen Ruigrok/asmodai *p++ = IPOPT_LSRR;
5330382be72SJeroen Ruigrok/asmodai *p++ = lsrrlen - 1;
5340382be72SJeroen Ruigrok/asmodai *p++ = IPOPT_MINOFF;
5350382be72SJeroen Ruigrok/asmodai gateway[lsrr] = to.sin_addr;
5360382be72SJeroen Ruigrok/asmodai for (i = 1; i <= lsrr; i++) {
5370382be72SJeroen Ruigrok/asmodai memcpy(p, &gateway[i], sizeof(struct in_addr));
5380382be72SJeroen Ruigrok/asmodai p += sizeof(struct in_addr);
5390382be72SJeroen Ruigrok/asmodai }
5400382be72SJeroen Ruigrok/asmodai ip->ip_dst = gateway[0];
5410382be72SJeroen Ruigrok/asmodai } else
5420382be72SJeroen Ruigrok/asmodai ip->ip_dst = to.sin_addr;
5430382be72SJeroen Ruigrok/asmodai ip->ip_off = htons(0);
5440382be72SJeroen Ruigrok/asmodai ip->ip_hl = (sizeof(struct ip) + lsrrlen) >> 2;
5450382be72SJeroen Ruigrok/asmodai ip->ip_p = proto;
5460382be72SJeroen Ruigrok/asmodai ip->ip_v = IPVERSION;
5470382be72SJeroen Ruigrok/asmodai ip->ip_tos = tos;
5480382be72SJeroen Ruigrok/asmodai
5490382be72SJeroen Ruigrok/asmodai ident = (getpid() & 0xffff) | 0x8000;
5500382be72SJeroen Ruigrok/asmodai tmprnd = arc4random();
5510382be72SJeroen Ruigrok/asmodai sec_perturb = (tmprnd & 0x80000000) ? -(tmprnd & 0x7ff) :
5520382be72SJeroen Ruigrok/asmodai (tmprnd & 0x7ff);
5530382be72SJeroen Ruigrok/asmodai usec_perturb = arc4random();
5540382be72SJeroen Ruigrok/asmodai
5550382be72SJeroen Ruigrok/asmodai if (options & SO_DEBUG)
55671126e33SSascha Wildner setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof(on));
5570382be72SJeroen Ruigrok/asmodai #ifdef SO_SNDBUF
5580382be72SJeroen Ruigrok/asmodai if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen,
5590382be72SJeroen Ruigrok/asmodai sizeof(datalen)) < 0)
5600382be72SJeroen Ruigrok/asmodai err(6, "SO_SNDBUF");
5610382be72SJeroen Ruigrok/asmodai #endif /* SO_SNDBUF */
5620382be72SJeroen Ruigrok/asmodai #ifdef IP_HDRINCL
5630382be72SJeroen Ruigrok/asmodai if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
5640382be72SJeroen Ruigrok/asmodai sizeof(on)) < 0)
5650382be72SJeroen Ruigrok/asmodai err(6, "IP_HDRINCL");
5660382be72SJeroen Ruigrok/asmodai #endif /* IP_HDRINCL */
5670382be72SJeroen Ruigrok/asmodai if (options & SO_DEBUG)
56871126e33SSascha Wildner setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
5690382be72SJeroen Ruigrok/asmodai (char *)&on, sizeof(on));
5700382be72SJeroen Ruigrok/asmodai if (options & SO_DONTROUTE)
57171126e33SSascha Wildner setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
5720382be72SJeroen Ruigrok/asmodai (char *)&on, sizeof(on));
5730382be72SJeroen Ruigrok/asmodai
5740382be72SJeroen Ruigrok/asmodai if (source) {
57571126e33SSascha Wildner memset(&from, 0, sizeof(struct sockaddr));
5760382be72SJeroen Ruigrok/asmodai from.sin_family = AF_INET;
5770382be72SJeroen Ruigrok/asmodai if (inet_aton(source, &from.sin_addr) == 0)
5780382be72SJeroen Ruigrok/asmodai errx(1, "unknown host %s", source);
5790382be72SJeroen Ruigrok/asmodai ip->ip_src = from.sin_addr;
5800382be72SJeroen Ruigrok/asmodai if (getuid() != 0 &&
5810382be72SJeroen Ruigrok/asmodai (ntohl(from.sin_addr.s_addr) & 0xff000000U) == 0x7f000000U &&
5820382be72SJeroen Ruigrok/asmodai (ntohl(to.sin_addr.s_addr) & 0xff000000U) != 0x7f000000U)
5830382be72SJeroen Ruigrok/asmodai errx(1, "source is on 127/8, destination is not");
5840382be72SJeroen Ruigrok/asmodai
5850382be72SJeroen Ruigrok/asmodai if (getuid() &&
5860382be72SJeroen Ruigrok/asmodai bind(sndsock, (struct sockaddr *)&from, sizeof(from)) < 0)
5870382be72SJeroen Ruigrok/asmodai err(1, "bind");
5880382be72SJeroen Ruigrok/asmodai }
5890382be72SJeroen Ruigrok/asmodai
5900382be72SJeroen Ruigrok/asmodai fprintf(stderr, "traceroute to %s (%s)", hostname,
5910382be72SJeroen Ruigrok/asmodai inet_ntoa(to.sin_addr));
5920382be72SJeroen Ruigrok/asmodai if (source)
5930382be72SJeroen Ruigrok/asmodai fprintf(stderr, " from %s", source);
5940382be72SJeroen Ruigrok/asmodai fprintf(stderr, ", %u hops max, %d byte packets\n", max_ttl, datalen);
59571126e33SSascha Wildner fflush(stderr);
5960382be72SJeroen Ruigrok/asmodai
5970382be72SJeroen Ruigrok/asmodai if (first_ttl > 1)
5980382be72SJeroen Ruigrok/asmodai printf("Skipping %u intermediate hops\n", first_ttl - 1);
5990382be72SJeroen Ruigrok/asmodai
6000382be72SJeroen Ruigrok/asmodai for (ttl = first_ttl; ttl <= max_ttl; ++ttl) {
6010382be72SJeroen Ruigrok/asmodai int got_there = 0, unreachable = 0, timeout = 0, loss;
6023472506cSHasso Tepper int gotlastaddr = 0;
6030382be72SJeroen Ruigrok/asmodai in_addr_t lastaddr = 0;
6040382be72SJeroen Ruigrok/asmodai quad_t dt;
6050382be72SJeroen Ruigrok/asmodai
6060382be72SJeroen Ruigrok/asmodai printf("%2u ", ttl);
6070382be72SJeroen Ruigrok/asmodai for (probe = 0, loss = 0; probe < nprobes; ++probe) {
6080382be72SJeroen Ruigrok/asmodai int cc;
6090382be72SJeroen Ruigrok/asmodai struct timeval t1, t2;
6100382be72SJeroen Ruigrok/asmodai int code;
6110382be72SJeroen Ruigrok/asmodai
61271126e33SSascha Wildner gettimeofday(&t1, NULL);
6130382be72SJeroen Ruigrok/asmodai send_probe(++seq, ttl, incflag, &to);
6140382be72SJeroen Ruigrok/asmodai while ((cc = wait_for_reply(s, &from, &t1))) {
61571126e33SSascha Wildner gettimeofday(&t2, NULL);
6160382be72SJeroen Ruigrok/asmodai if (t2.tv_sec - t1.tv_sec > waittime) {
6170382be72SJeroen Ruigrok/asmodai cc = 0;
6180382be72SJeroen Ruigrok/asmodai break;
6190382be72SJeroen Ruigrok/asmodai }
6200382be72SJeroen Ruigrok/asmodai i = packet_ok(packet, cc, &from, seq, incflag);
6210382be72SJeroen Ruigrok/asmodai /* Skip short packet */
6220382be72SJeroen Ruigrok/asmodai if (i == 0)
6230382be72SJeroen Ruigrok/asmodai continue;
6243472506cSHasso Tepper if (!gotlastaddr ||
6253472506cSHasso Tepper from.sin_addr.s_addr != lastaddr) {
6263472506cSHasso Tepper if (gotlastaddr)
6273472506cSHasso Tepper printf("\n ");
6280382be72SJeroen Ruigrok/asmodai print(packet, cc, &from);
6290382be72SJeroen Ruigrok/asmodai lastaddr = from.sin_addr.s_addr;
6303472506cSHasso Tepper ++gotlastaddr;
6310382be72SJeroen Ruigrok/asmodai }
6320382be72SJeroen Ruigrok/asmodai dt = (quad_t)(t2.tv_sec - t1.tv_sec) * 1000000 +
6330382be72SJeroen Ruigrok/asmodai (quad_t)(t2.tv_usec - t1.tv_usec);
6340382be72SJeroen Ruigrok/asmodai printf(" %u", (u_int)(dt / 1000));
6350382be72SJeroen Ruigrok/asmodai if (dt % 1000)
6360382be72SJeroen Ruigrok/asmodai printf(".%u", (u_int)(dt % 1000));
6370382be72SJeroen Ruigrok/asmodai printf(" ms");
6380382be72SJeroen Ruigrok/asmodai ip = (struct ip *)packet;
6390382be72SJeroen Ruigrok/asmodai if (ttl_flag)
6400382be72SJeroen Ruigrok/asmodai printf(" (%u)", ip->ip_ttl);
6410382be72SJeroen Ruigrok/asmodai if (i == -2) {
6420382be72SJeroen Ruigrok/asmodai #ifndef ARCHAIC
6430382be72SJeroen Ruigrok/asmodai ip = (struct ip *)packet;
6440382be72SJeroen Ruigrok/asmodai if (ip->ip_ttl <= 1)
6450382be72SJeroen Ruigrok/asmodai printf(" !");
6460382be72SJeroen Ruigrok/asmodai #endif
6470382be72SJeroen Ruigrok/asmodai ++got_there;
6480382be72SJeroen Ruigrok/asmodai break;
6490382be72SJeroen Ruigrok/asmodai }
6500382be72SJeroen Ruigrok/asmodai /* time exceeded in transit */
6510382be72SJeroen Ruigrok/asmodai if (i == -1)
6520382be72SJeroen Ruigrok/asmodai break;
6530382be72SJeroen Ruigrok/asmodai code = i - 1;
6540382be72SJeroen Ruigrok/asmodai switch (code) {
6550382be72SJeroen Ruigrok/asmodai case ICMP_UNREACH_PORT:
6560382be72SJeroen Ruigrok/asmodai #ifndef ARCHAIC
6570382be72SJeroen Ruigrok/asmodai ip = (struct ip *)packet;
6580382be72SJeroen Ruigrok/asmodai if (ip->ip_ttl <= 1)
6590382be72SJeroen Ruigrok/asmodai printf(" !");
6600382be72SJeroen Ruigrok/asmodai #endif /* ARCHAIC */
6610382be72SJeroen Ruigrok/asmodai ++got_there;
6620382be72SJeroen Ruigrok/asmodai break;
6630382be72SJeroen Ruigrok/asmodai case ICMP_UNREACH_NET:
6640382be72SJeroen Ruigrok/asmodai ++unreachable;
6650382be72SJeroen Ruigrok/asmodai printf(" !N");
6660382be72SJeroen Ruigrok/asmodai break;
6670382be72SJeroen Ruigrok/asmodai case ICMP_UNREACH_HOST:
6680382be72SJeroen Ruigrok/asmodai ++unreachable;
6690382be72SJeroen Ruigrok/asmodai printf(" !H");
6700382be72SJeroen Ruigrok/asmodai break;
6710382be72SJeroen Ruigrok/asmodai case ICMP_UNREACH_PROTOCOL:
6720382be72SJeroen Ruigrok/asmodai ++got_there;
6730382be72SJeroen Ruigrok/asmodai printf(" !P");
6740382be72SJeroen Ruigrok/asmodai break;
6750382be72SJeroen Ruigrok/asmodai case ICMP_UNREACH_NEEDFRAG:
6760382be72SJeroen Ruigrok/asmodai ++unreachable;
6770382be72SJeroen Ruigrok/asmodai printf(" !F");
6780382be72SJeroen Ruigrok/asmodai break;
6790382be72SJeroen Ruigrok/asmodai case ICMP_UNREACH_SRCFAIL:
6800382be72SJeroen Ruigrok/asmodai ++unreachable;
6810382be72SJeroen Ruigrok/asmodai printf(" !S");
6820382be72SJeroen Ruigrok/asmodai break;
6830382be72SJeroen Ruigrok/asmodai case ICMP_UNREACH_FILTER_PROHIB:
6840382be72SJeroen Ruigrok/asmodai ++unreachable;
6850382be72SJeroen Ruigrok/asmodai printf(" !X");
6860382be72SJeroen Ruigrok/asmodai break;
6870382be72SJeroen Ruigrok/asmodai case ICMP_UNREACH_NET_PROHIB: /*misuse*/
6880382be72SJeroen Ruigrok/asmodai ++unreachable;
6890382be72SJeroen Ruigrok/asmodai printf(" !A");
6900382be72SJeroen Ruigrok/asmodai break;
6910382be72SJeroen Ruigrok/asmodai case ICMP_UNREACH_HOST_PROHIB:
6920382be72SJeroen Ruigrok/asmodai ++unreachable;
6930382be72SJeroen Ruigrok/asmodai printf(" !C");
6940382be72SJeroen Ruigrok/asmodai break;
6950382be72SJeroen Ruigrok/asmodai case ICMP_UNREACH_NET_UNKNOWN:
6960382be72SJeroen Ruigrok/asmodai case ICMP_UNREACH_HOST_UNKNOWN:
6970382be72SJeroen Ruigrok/asmodai ++unreachable;
6980382be72SJeroen Ruigrok/asmodai printf(" !U");
6990382be72SJeroen Ruigrok/asmodai break;
7000382be72SJeroen Ruigrok/asmodai case ICMP_UNREACH_ISOLATED:
7010382be72SJeroen Ruigrok/asmodai ++unreachable;
7020382be72SJeroen Ruigrok/asmodai printf(" !I");
7030382be72SJeroen Ruigrok/asmodai break;
7040382be72SJeroen Ruigrok/asmodai case ICMP_UNREACH_TOSNET:
7050382be72SJeroen Ruigrok/asmodai case ICMP_UNREACH_TOSHOST:
7060382be72SJeroen Ruigrok/asmodai ++unreachable;
7070382be72SJeroen Ruigrok/asmodai printf(" !T");
7080382be72SJeroen Ruigrok/asmodai break;
7090382be72SJeroen Ruigrok/asmodai default:
7100382be72SJeroen Ruigrok/asmodai ++unreachable;
7110382be72SJeroen Ruigrok/asmodai printf(" !<%d>", i - 1);
7120382be72SJeroen Ruigrok/asmodai break;
7130382be72SJeroen Ruigrok/asmodai }
7140382be72SJeroen Ruigrok/asmodai break;
7150382be72SJeroen Ruigrok/asmodai }
7160382be72SJeroen Ruigrok/asmodai if (cc == 0) {
7170382be72SJeroen Ruigrok/asmodai printf(" *");
7180382be72SJeroen Ruigrok/asmodai timeout++;
7190382be72SJeroen Ruigrok/asmodai loss++;
7200382be72SJeroen Ruigrok/asmodai }
72146877004SHasso Tepper else if (cc && probe == nprobes - 1 && Mflag)
72246877004SHasso Tepper decode_extensions(packet, cc);
72371126e33SSascha Wildner fflush(stdout);
7240382be72SJeroen Ruigrok/asmodai }
7250382be72SJeroen Ruigrok/asmodai if (sump)
7260382be72SJeroen Ruigrok/asmodai printf(" (%d%% loss)", (loss * 100) / nprobes);
7270382be72SJeroen Ruigrok/asmodai putchar('\n');
7280382be72SJeroen Ruigrok/asmodai if (got_there || (unreachable && (unreachable + timeout) >= nprobes))
7290382be72SJeroen Ruigrok/asmodai break;
7300382be72SJeroen Ruigrok/asmodai }
7310382be72SJeroen Ruigrok/asmodai exit(0);
7320382be72SJeroen Ruigrok/asmodai }
7330382be72SJeroen Ruigrok/asmodai
734d6410035SSascha Wildner static int
wait_for_reply(int sock,struct sockaddr_in * from,struct timeval * sent)7350382be72SJeroen Ruigrok/asmodai wait_for_reply(int sock, struct sockaddr_in *from, struct timeval *sent)
7360382be72SJeroen Ruigrok/asmodai {
7370382be72SJeroen Ruigrok/asmodai socklen_t fromlen = sizeof (*from);
7380382be72SJeroen Ruigrok/asmodai struct timeval now, wait;
7390382be72SJeroen Ruigrok/asmodai int cc = 0, fdsn;
7400382be72SJeroen Ruigrok/asmodai fd_set *fdsp;
7410382be72SJeroen Ruigrok/asmodai
7420382be72SJeroen Ruigrok/asmodai fdsn = howmany(sock+1, NFDBITS) * sizeof(fd_mask);
7430382be72SJeroen Ruigrok/asmodai if ((fdsp = (fd_set *)malloc(fdsn)) == NULL)
7440382be72SJeroen Ruigrok/asmodai err(1, "malloc");
7450382be72SJeroen Ruigrok/asmodai memset(fdsp, 0, fdsn);
7460382be72SJeroen Ruigrok/asmodai FD_SET(sock, fdsp);
7470382be72SJeroen Ruigrok/asmodai gettimeofday(&now, NULL);
7480382be72SJeroen Ruigrok/asmodai wait.tv_sec = (sent->tv_sec + waittime) - now.tv_sec;
7490382be72SJeroen Ruigrok/asmodai wait.tv_usec = sent->tv_usec - now.tv_usec;
7500382be72SJeroen Ruigrok/asmodai if (wait.tv_usec < 0) {
7510382be72SJeroen Ruigrok/asmodai wait.tv_usec += 1000000;
7520382be72SJeroen Ruigrok/asmodai wait.tv_sec--;
7530382be72SJeroen Ruigrok/asmodai }
7540382be72SJeroen Ruigrok/asmodai if (wait.tv_sec < 0)
7550382be72SJeroen Ruigrok/asmodai wait.tv_sec = wait.tv_usec = 0;
7560382be72SJeroen Ruigrok/asmodai
75760233e58SSascha Wildner if (select(sock+1, fdsp, NULL, NULL, &wait) > 0)
7580382be72SJeroen Ruigrok/asmodai cc = recvfrom(s, (char *)packet, sizeof(packet), 0,
7590382be72SJeroen Ruigrok/asmodai (struct sockaddr *)from, &fromlen);
7600382be72SJeroen Ruigrok/asmodai
7610382be72SJeroen Ruigrok/asmodai free(fdsp);
7620382be72SJeroen Ruigrok/asmodai return (cc);
7630382be72SJeroen Ruigrok/asmodai }
7640382be72SJeroen Ruigrok/asmodai
765d6410035SSascha Wildner static void
decode_extensions(unsigned char * buf,int ip_len)76646877004SHasso Tepper decode_extensions(unsigned char *buf, int ip_len)
76746877004SHasso Tepper {
76846877004SHasso Tepper uint32_t *cmn_hdr;
76946877004SHasso Tepper struct icmp_ext_obj_hdr *obj_hdr;
77046877004SHasso Tepper uint32_t mpls_hdr;
77146877004SHasso Tepper int data_len, obj_len;
77246877004SHasso Tepper struct ip *ip;
77346877004SHasso Tepper
77446877004SHasso Tepper ip = (struct ip *)buf;
77546877004SHasso Tepper
77646877004SHasso Tepper if (ip_len <= (int)(sizeof(struct ip) + ICMP_EXT_OFFSET)) {
77746877004SHasso Tepper /*
77846877004SHasso Tepper * No support for ICMP extensions on this host
77946877004SHasso Tepper */
78046877004SHasso Tepper return;
78146877004SHasso Tepper }
78246877004SHasso Tepper
78346877004SHasso Tepper /*
78446877004SHasso Tepper * Move forward to the start of the ICMP extensions, if present
78546877004SHasso Tepper */
78646877004SHasso Tepper buf += (ip->ip_hl << 2) + ICMP_EXT_OFFSET;
78746877004SHasso Tepper cmn_hdr = (uint32_t *)buf;
78846877004SHasso Tepper
78946877004SHasso Tepper if (EXT_VERSION(ntohl(*cmn_hdr)) != ICMP_EXT_VERSION) {
79046877004SHasso Tepper /*
79146877004SHasso Tepper * Unknown version
79246877004SHasso Tepper */
79346877004SHasso Tepper return;
79446877004SHasso Tepper }
79546877004SHasso Tepper
79646877004SHasso Tepper data_len = ip_len - ((u_char *)cmn_hdr - (u_char *)ip);
79746877004SHasso Tepper
79846877004SHasso Tepper /*
79946877004SHasso Tepper * Check the checksum, cmn_hdr->checksum == 0 means no checksum'ing
80046877004SHasso Tepper * done by sender.
80146877004SHasso Tepper *
80246877004SHasso Tepper * If the checksum is ok, we'll get 0, as the checksum is calculated
80346877004SHasso Tepper * with the checksum field being 0'd.
80446877004SHasso Tepper */
80546877004SHasso Tepper if (EXT_CHECKSUM(ntohl(*cmn_hdr)) &&
80646877004SHasso Tepper in_cksum((u_short *)cmn_hdr, data_len)) {
80746877004SHasso Tepper return;
80846877004SHasso Tepper }
80946877004SHasso Tepper
81046877004SHasso Tepper buf += sizeof(*cmn_hdr);
81146877004SHasso Tepper data_len -= sizeof(*cmn_hdr);
81246877004SHasso Tepper
81346877004SHasso Tepper while (data_len >= (int)sizeof(struct icmp_ext_obj_hdr)) {
81446877004SHasso Tepper unsigned char *nextbuf;
81546877004SHasso Tepper
81646877004SHasso Tepper obj_hdr = (struct icmp_ext_obj_hdr *)buf;
81746877004SHasso Tepper obj_len = ntohs(obj_hdr->length);
81846877004SHasso Tepper
81946877004SHasso Tepper /*
82046877004SHasso Tepper * Sanity check the length field
82146877004SHasso Tepper */
82246877004SHasso Tepper if (obj_len < (int)sizeof(*obj_hdr) || obj_len > data_len)
82346877004SHasso Tepper return;
82446877004SHasso Tepper
82546877004SHasso Tepper /* Object has to be 4-byte aligned. */
82646877004SHasso Tepper if (obj_len & 3)
82746877004SHasso Tepper return;
82846877004SHasso Tepper
82946877004SHasso Tepper nextbuf = buf + obj_len;
83046877004SHasso Tepper data_len -= obj_len;
83146877004SHasso Tepper
83246877004SHasso Tepper /*
83346877004SHasso Tepper * Move past the object header
83446877004SHasso Tepper */
83546877004SHasso Tepper buf += sizeof(struct icmp_ext_obj_hdr);
83646877004SHasso Tepper obj_len -= sizeof(struct icmp_ext_obj_hdr);
83746877004SHasso Tepper
83846877004SHasso Tepper switch (obj_hdr->class_num) {
83946877004SHasso Tepper case MPLS_STACK_ENTRY_CLASS:
84046877004SHasso Tepper switch (obj_hdr->c_type) {
84146877004SHasso Tepper case MPLS_STACK_ENTRY_C_TYPE:
84246877004SHasso Tepper while (obj_len >= (int)sizeof(uint32_t)) {
84346877004SHasso Tepper mpls_hdr = ntohl(*(uint32_t *)buf);
84446877004SHasso Tepper
84546877004SHasso Tepper buf += sizeof(uint32_t);
84646877004SHasso Tepper obj_len -= sizeof(uint32_t);
84746877004SHasso Tepper printf(" [MPLS: Label %d Exp %d]",
84846877004SHasso Tepper MPLS_LABEL(mpls_hdr),
84946877004SHasso Tepper MPLS_EXP(mpls_hdr));
85046877004SHasso Tepper }
85146877004SHasso Tepper if (obj_len > 0) {
85246877004SHasso Tepper /*
85346877004SHasso Tepper * Something went wrong, and we're at
85446877004SHasso Tepper * a unknown offset into the packet,
85546877004SHasso Tepper * ditch the rest of it.
85646877004SHasso Tepper */
85746877004SHasso Tepper return;
85846877004SHasso Tepper }
85946877004SHasso Tepper break;
86046877004SHasso Tepper default:
86146877004SHasso Tepper /*
86246877004SHasso Tepper * Unknown object, skip past it
86346877004SHasso Tepper */
86446877004SHasso Tepper buf = nextbuf;
86546877004SHasso Tepper break;
86646877004SHasso Tepper }
86746877004SHasso Tepper break;
86846877004SHasso Tepper
86946877004SHasso Tepper default:
87046877004SHasso Tepper /*
87146877004SHasso Tepper * Unknown object, skip past it
87246877004SHasso Tepper */
87346877004SHasso Tepper buf = nextbuf;
87446877004SHasso Tepper break;
87546877004SHasso Tepper }
87646877004SHasso Tepper }
87746877004SHasso Tepper }
87846877004SHasso Tepper
879d6410035SSascha Wildner static void
dump_packet(void)8800382be72SJeroen Ruigrok/asmodai dump_packet(void)
8810382be72SJeroen Ruigrok/asmodai {
8820382be72SJeroen Ruigrok/asmodai u_char *p;
8830382be72SJeroen Ruigrok/asmodai int i;
8840382be72SJeroen Ruigrok/asmodai
8850382be72SJeroen Ruigrok/asmodai fprintf(stderr, "packet data:");
8860382be72SJeroen Ruigrok/asmodai for (p = outpacket, i = 0; i < datalen; i++) {
8870382be72SJeroen Ruigrok/asmodai if ((i % 24) == 0)
8880382be72SJeroen Ruigrok/asmodai fprintf(stderr, "\n ");
8890382be72SJeroen Ruigrok/asmodai fprintf(stderr, " %02x", *p++);
8900382be72SJeroen Ruigrok/asmodai }
8910382be72SJeroen Ruigrok/asmodai fprintf(stderr, "\n");
8920382be72SJeroen Ruigrok/asmodai }
8930382be72SJeroen Ruigrok/asmodai
894d6410035SSascha Wildner static void
send_probe(int seq,u_int8_t ttl,int iflag,struct sockaddr_in * to)8950382be72SJeroen Ruigrok/asmodai send_probe(int seq, u_int8_t ttl, int iflag, struct sockaddr_in *to)
8960382be72SJeroen Ruigrok/asmodai {
8970382be72SJeroen Ruigrok/asmodai struct ip *ip = (struct ip *)outpacket;
8980382be72SJeroen Ruigrok/asmodai u_char *p = (u_char *)(ip + 1);
8990382be72SJeroen Ruigrok/asmodai struct udphdr *up = (struct udphdr *)(p + lsrrlen);
9000382be72SJeroen Ruigrok/asmodai struct icmp *icmpp = (struct icmp *)(p + lsrrlen);
9010382be72SJeroen Ruigrok/asmodai struct packetdata *op;
9020382be72SJeroen Ruigrok/asmodai struct timeval tv;
9030382be72SJeroen Ruigrok/asmodai int i;
9040382be72SJeroen Ruigrok/asmodai
90592b9bde7SEirik Nygaard ip->ip_len = datalen;
9060382be72SJeroen Ruigrok/asmodai ip->ip_ttl = ttl;
9070382be72SJeroen Ruigrok/asmodai ip->ip_id = htons(ident+seq);
9080382be72SJeroen Ruigrok/asmodai
9090382be72SJeroen Ruigrok/asmodai switch (proto) {
9100382be72SJeroen Ruigrok/asmodai case IPPROTO_ICMP:
9110382be72SJeroen Ruigrok/asmodai icmpp->icmp_type = icmp_type;
9120382be72SJeroen Ruigrok/asmodai icmpp->icmp_code = icmp_code;
9130382be72SJeroen Ruigrok/asmodai icmpp->icmp_seq = htons(seq);
9140382be72SJeroen Ruigrok/asmodai icmpp->icmp_id = htons(ident);
9150382be72SJeroen Ruigrok/asmodai op = (struct packetdata *)(icmpp + 1);
9160382be72SJeroen Ruigrok/asmodai break;
9170382be72SJeroen Ruigrok/asmodai case IPPROTO_UDP:
9180382be72SJeroen Ruigrok/asmodai up->uh_sport = htons(ident);
9190382be72SJeroen Ruigrok/asmodai if (iflag)
9200382be72SJeroen Ruigrok/asmodai up->uh_dport = htons(port+seq);
9210382be72SJeroen Ruigrok/asmodai else
9220382be72SJeroen Ruigrok/asmodai up->uh_dport = htons(port);
9230382be72SJeroen Ruigrok/asmodai up->uh_ulen = htons((u_short)(datalen - sizeof(struct ip) -
9240382be72SJeroen Ruigrok/asmodai lsrrlen));
9250382be72SJeroen Ruigrok/asmodai up->uh_sum = 0;
9260382be72SJeroen Ruigrok/asmodai op = (struct packetdata *)(up + 1);
9270382be72SJeroen Ruigrok/asmodai break;
9280382be72SJeroen Ruigrok/asmodai default:
9290382be72SJeroen Ruigrok/asmodai op = (struct packetdata *)(ip + 1);
9300382be72SJeroen Ruigrok/asmodai break;
9310382be72SJeroen Ruigrok/asmodai }
9320382be72SJeroen Ruigrok/asmodai op->seq = seq;
9330382be72SJeroen Ruigrok/asmodai op->ttl = ttl;
9340382be72SJeroen Ruigrok/asmodai
9350382be72SJeroen Ruigrok/asmodai /*
9360382be72SJeroen Ruigrok/asmodai * We don't want hostiles snooping the net to get any useful
9370382be72SJeroen Ruigrok/asmodai * information about us. Send the timestamp in network byte order,
9380382be72SJeroen Ruigrok/asmodai * and perturb the timestamp enough that they won't know our
9390382be72SJeroen Ruigrok/asmodai * real clock ticker. We don't want to perturb the time by too
9400382be72SJeroen Ruigrok/asmodai * much: being off by a suspiciously large amount might indicate
9410382be72SJeroen Ruigrok/asmodai * OpenBSD.
9420382be72SJeroen Ruigrok/asmodai *
9430382be72SJeroen Ruigrok/asmodai * The timestamps in the packet are currently unused. If future
9440382be72SJeroen Ruigrok/asmodai * work wants to use them they will have to subtract out the
9450382be72SJeroen Ruigrok/asmodai * perturbation first.
9460382be72SJeroen Ruigrok/asmodai */
94771126e33SSascha Wildner gettimeofday(&tv, NULL);
9480382be72SJeroen Ruigrok/asmodai op->sec = htonl(tv.tv_sec + sec_perturb);
9490382be72SJeroen Ruigrok/asmodai op->usec = htonl((tv.tv_usec + usec_perturb) % 1000000);
9500382be72SJeroen Ruigrok/asmodai
9510382be72SJeroen Ruigrok/asmodai if (proto == IPPROTO_ICMP && icmp_type == ICMP_ECHO) {
9520382be72SJeroen Ruigrok/asmodai icmpp->icmp_cksum = 0;
9530382be72SJeroen Ruigrok/asmodai icmpp->icmp_cksum = in_cksum((u_short *)icmpp,
9540382be72SJeroen Ruigrok/asmodai datalen - sizeof(struct ip) - lsrrlen);
9550382be72SJeroen Ruigrok/asmodai if (icmpp->icmp_cksum == 0)
9560382be72SJeroen Ruigrok/asmodai icmpp->icmp_cksum = 0xffff;
9570382be72SJeroen Ruigrok/asmodai }
9580382be72SJeroen Ruigrok/asmodai
9590382be72SJeroen Ruigrok/asmodai if (dump)
9600382be72SJeroen Ruigrok/asmodai dump_packet();
9610382be72SJeroen Ruigrok/asmodai
9620382be72SJeroen Ruigrok/asmodai i = sendto(sndsock, outpacket, datalen, 0, (struct sockaddr *)to,
9630382be72SJeroen Ruigrok/asmodai sizeof(struct sockaddr_in));
9640382be72SJeroen Ruigrok/asmodai if (i < 0 || i != datalen) {
9650382be72SJeroen Ruigrok/asmodai if (i < 0)
9660382be72SJeroen Ruigrok/asmodai perror("sendto");
9670382be72SJeroen Ruigrok/asmodai printf("traceroute: wrote %s %d chars, ret=%d\n", hostname,
9680382be72SJeroen Ruigrok/asmodai datalen, i);
96971126e33SSascha Wildner fflush(stdout);
9700382be72SJeroen Ruigrok/asmodai }
9710382be72SJeroen Ruigrok/asmodai }
9720382be72SJeroen Ruigrok/asmodai
9739dadf159SEirik Nygaard static const char *ttab[] = {
9740382be72SJeroen Ruigrok/asmodai "Echo Reply",
9750382be72SJeroen Ruigrok/asmodai "ICMP 1",
9760382be72SJeroen Ruigrok/asmodai "ICMP 2",
9770382be72SJeroen Ruigrok/asmodai "Dest Unreachable",
9780382be72SJeroen Ruigrok/asmodai "Source Quench",
9790382be72SJeroen Ruigrok/asmodai "Redirect",
9800382be72SJeroen Ruigrok/asmodai "ICMP 6",
9810382be72SJeroen Ruigrok/asmodai "ICMP 7",
9820382be72SJeroen Ruigrok/asmodai "Echo",
9830382be72SJeroen Ruigrok/asmodai "Router Advert",
9840382be72SJeroen Ruigrok/asmodai "Router Solicit",
9850382be72SJeroen Ruigrok/asmodai "Time Exceeded",
9860382be72SJeroen Ruigrok/asmodai "Param Problem",
9870382be72SJeroen Ruigrok/asmodai "Timestamp",
9880382be72SJeroen Ruigrok/asmodai "Timestamp Reply",
9890382be72SJeroen Ruigrok/asmodai "Info Request",
9900382be72SJeroen Ruigrok/asmodai "Info Reply",
9910382be72SJeroen Ruigrok/asmodai "Mask Request",
9920382be72SJeroen Ruigrok/asmodai "Mask Reply"
9930382be72SJeroen Ruigrok/asmodai };
9940382be72SJeroen Ruigrok/asmodai
9950382be72SJeroen Ruigrok/asmodai /*
9960382be72SJeroen Ruigrok/asmodai * Convert an ICMP "type" field to a printable string.
9970382be72SJeroen Ruigrok/asmodai */
998d6410035SSascha Wildner static const char *
pr_type(u_int8_t t)9990382be72SJeroen Ruigrok/asmodai pr_type(u_int8_t t)
10000382be72SJeroen Ruigrok/asmodai {
10010382be72SJeroen Ruigrok/asmodai if (t > 18)
10020382be72SJeroen Ruigrok/asmodai return ("OUT-OF-RANGE");
10030382be72SJeroen Ruigrok/asmodai return (ttab[t]);
10040382be72SJeroen Ruigrok/asmodai }
10050382be72SJeroen Ruigrok/asmodai
1006d6410035SSascha Wildner static int
packet_ok(u_char * buf,int cc,struct sockaddr_in * from,int seq,int iflag)10070382be72SJeroen Ruigrok/asmodai packet_ok(u_char *buf, int cc, struct sockaddr_in *from, int seq, int iflag)
10080382be72SJeroen Ruigrok/asmodai {
10090382be72SJeroen Ruigrok/asmodai struct icmp *icp;
10100382be72SJeroen Ruigrok/asmodai u_char code;
10110382be72SJeroen Ruigrok/asmodai u_int8_t type;
10120382be72SJeroen Ruigrok/asmodai int hlen;
10130382be72SJeroen Ruigrok/asmodai #ifndef ARCHAIC
10140382be72SJeroen Ruigrok/asmodai struct ip *ip;
10150382be72SJeroen Ruigrok/asmodai
10160382be72SJeroen Ruigrok/asmodai ip = (struct ip *) buf;
10170382be72SJeroen Ruigrok/asmodai hlen = ip->ip_hl << 2;
10180382be72SJeroen Ruigrok/asmodai if (cc < hlen + ICMP_MINLEN) {
10190382be72SJeroen Ruigrok/asmodai if (verbose)
10200382be72SJeroen Ruigrok/asmodai printf("packet too short (%d bytes) from %s\n", cc,
10210382be72SJeroen Ruigrok/asmodai inet_ntoa(from->sin_addr));
10220382be72SJeroen Ruigrok/asmodai return (0);
10230382be72SJeroen Ruigrok/asmodai }
10240382be72SJeroen Ruigrok/asmodai cc -= hlen;
10250382be72SJeroen Ruigrok/asmodai icp = (struct icmp *)(buf + hlen);
10260382be72SJeroen Ruigrok/asmodai #else
10270382be72SJeroen Ruigrok/asmodai icp = (struct icmp *)buf;
10280382be72SJeroen Ruigrok/asmodai #endif /* ARCHAIC */
10290382be72SJeroen Ruigrok/asmodai type = icp->icmp_type;
10300382be72SJeroen Ruigrok/asmodai code = icp->icmp_code;
10310382be72SJeroen Ruigrok/asmodai if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
10320382be72SJeroen Ruigrok/asmodai type == ICMP_UNREACH || type == ICMP_ECHOREPLY) {
10330382be72SJeroen Ruigrok/asmodai struct ip *hip;
10340382be72SJeroen Ruigrok/asmodai struct udphdr *up;
10350382be72SJeroen Ruigrok/asmodai struct icmp *icmpp;
10360382be72SJeroen Ruigrok/asmodai
10370382be72SJeroen Ruigrok/asmodai hip = &icp->icmp_ip;
10380382be72SJeroen Ruigrok/asmodai hlen = hip->ip_hl << 2;
10390382be72SJeroen Ruigrok/asmodai
10400382be72SJeroen Ruigrok/asmodai switch (proto) {
10410382be72SJeroen Ruigrok/asmodai case IPPROTO_ICMP:
10420382be72SJeroen Ruigrok/asmodai if (icmp_type == ICMP_ECHO &&
10430382be72SJeroen Ruigrok/asmodai type == ICMP_ECHOREPLY &&
10440382be72SJeroen Ruigrok/asmodai icp->icmp_id == htons(ident) &&
10450382be72SJeroen Ruigrok/asmodai icp->icmp_seq == htons(seq))
10460382be72SJeroen Ruigrok/asmodai return (-2); /* we got there */
10470382be72SJeroen Ruigrok/asmodai
10480382be72SJeroen Ruigrok/asmodai icmpp = (struct icmp *)((u_char *)hip + hlen);
10490382be72SJeroen Ruigrok/asmodai if (hlen + 8 <= cc && hip->ip_p == IPPROTO_ICMP &&
10500382be72SJeroen Ruigrok/asmodai icmpp->icmp_id == htons(ident) &&
10510382be72SJeroen Ruigrok/asmodai icmpp->icmp_seq == htons(seq))
10520382be72SJeroen Ruigrok/asmodai return (type == ICMP_TIMXCEED? -1 : code + 1);
10530382be72SJeroen Ruigrok/asmodai break;
10540382be72SJeroen Ruigrok/asmodai
10550382be72SJeroen Ruigrok/asmodai case IPPROTO_UDP:
10560382be72SJeroen Ruigrok/asmodai up = (struct udphdr *)((u_char *)hip + hlen);
10570382be72SJeroen Ruigrok/asmodai if (hlen + 12 <= cc && hip->ip_p == proto &&
10580382be72SJeroen Ruigrok/asmodai up->uh_sport == htons(ident) &&
10590382be72SJeroen Ruigrok/asmodai ((iflag && up->uh_dport == htons(port + seq)) ||
10600382be72SJeroen Ruigrok/asmodai (!iflag && up->uh_dport == htons(port))))
10610382be72SJeroen Ruigrok/asmodai return (type == ICMP_TIMXCEED? -1 : code + 1);
10620382be72SJeroen Ruigrok/asmodai break;
10630382be72SJeroen Ruigrok/asmodai default:
10640382be72SJeroen Ruigrok/asmodai /* this is some odd, user specified proto,
10650382be72SJeroen Ruigrok/asmodai * how do we check it?
10660382be72SJeroen Ruigrok/asmodai */
10670382be72SJeroen Ruigrok/asmodai if (hip->ip_p == proto)
10680382be72SJeroen Ruigrok/asmodai return (type == ICMP_TIMXCEED? -1 : code + 1);
10690382be72SJeroen Ruigrok/asmodai }
10700382be72SJeroen Ruigrok/asmodai }
10710382be72SJeroen Ruigrok/asmodai #ifndef ARCHAIC
10720382be72SJeroen Ruigrok/asmodai if (verbose) {
10730382be72SJeroen Ruigrok/asmodai int i;
10740382be72SJeroen Ruigrok/asmodai in_addr_t *lp = (in_addr_t *)&icp->icmp_ip;
10750382be72SJeroen Ruigrok/asmodai
10760382be72SJeroen Ruigrok/asmodai printf("\n%d bytes from %s", cc, inet_ntoa(from->sin_addr));
10770382be72SJeroen Ruigrok/asmodai printf(" to %s", inet_ntoa(ip->ip_dst));
10780382be72SJeroen Ruigrok/asmodai printf(": icmp type %u (%s) code %d\n", type, pr_type(type),
10790382be72SJeroen Ruigrok/asmodai icp->icmp_code);
10800382be72SJeroen Ruigrok/asmodai for (i = 4; i < cc ; i += sizeof(in_addr_t))
10810382be72SJeroen Ruigrok/asmodai printf("%2d: x%8.8lx\n", i, (unsigned long)*lp++);
10820382be72SJeroen Ruigrok/asmodai }
10830382be72SJeroen Ruigrok/asmodai #endif /* ARCHAIC */
10840382be72SJeroen Ruigrok/asmodai return (0);
10850382be72SJeroen Ruigrok/asmodai }
10860382be72SJeroen Ruigrok/asmodai
1087d6410035SSascha Wildner static void
print(u_char * buf,int cc,struct sockaddr_in * from)10880382be72SJeroen Ruigrok/asmodai print(u_char *buf, int cc, struct sockaddr_in *from)
10890382be72SJeroen Ruigrok/asmodai {
10900382be72SJeroen Ruigrok/asmodai struct ip *ip;
10910382be72SJeroen Ruigrok/asmodai int hlen;
10920382be72SJeroen Ruigrok/asmodai
10930382be72SJeroen Ruigrok/asmodai ip = (struct ip *) buf;
10940382be72SJeroen Ruigrok/asmodai hlen = ip->ip_hl << 2;
10950382be72SJeroen Ruigrok/asmodai cc -= hlen;
10960382be72SJeroen Ruigrok/asmodai
10970382be72SJeroen Ruigrok/asmodai if (nflag)
10980382be72SJeroen Ruigrok/asmodai printf(" %s", inet_ntoa(from->sin_addr));
10990382be72SJeroen Ruigrok/asmodai else
11000382be72SJeroen Ruigrok/asmodai printf(" %s (%s)", inetname(from->sin_addr),
11010382be72SJeroen Ruigrok/asmodai inet_ntoa(from->sin_addr));
11020382be72SJeroen Ruigrok/asmodai
11030382be72SJeroen Ruigrok/asmodai if (verbose)
11040382be72SJeroen Ruigrok/asmodai printf(" %d bytes to %s", cc, inet_ntoa (ip->ip_dst));
11050382be72SJeroen Ruigrok/asmodai }
11060382be72SJeroen Ruigrok/asmodai
11070382be72SJeroen Ruigrok/asmodai
11080382be72SJeroen Ruigrok/asmodai /*
11090382be72SJeroen Ruigrok/asmodai * Checksum routine for Internet Protocol family headers (C Version)
11100382be72SJeroen Ruigrok/asmodai */
1111d6410035SSascha Wildner static u_short
in_cksum(u_short * addr,int len)11120382be72SJeroen Ruigrok/asmodai in_cksum(u_short *addr, int len)
11130382be72SJeroen Ruigrok/asmodai {
11140382be72SJeroen Ruigrok/asmodai u_short *w = addr, answer;
11150382be72SJeroen Ruigrok/asmodai int nleft = len, sum = 0;
11160382be72SJeroen Ruigrok/asmodai
11170382be72SJeroen Ruigrok/asmodai /*
11180382be72SJeroen Ruigrok/asmodai * Our algorithm is simple, using a 32 bit accumulator (sum),
11190382be72SJeroen Ruigrok/asmodai * we add sequential 16 bit words to it, and at the end, fold
11200382be72SJeroen Ruigrok/asmodai * back all the carry bits from the top 16 bits into the lower
11210382be72SJeroen Ruigrok/asmodai * 16 bits.
11220382be72SJeroen Ruigrok/asmodai */
11230382be72SJeroen Ruigrok/asmodai while (nleft > 1) {
11240382be72SJeroen Ruigrok/asmodai sum += *w++;
11250382be72SJeroen Ruigrok/asmodai nleft -= 2;
11260382be72SJeroen Ruigrok/asmodai }
11270382be72SJeroen Ruigrok/asmodai
11280382be72SJeroen Ruigrok/asmodai /* mop up an odd byte, if necessary */
11290382be72SJeroen Ruigrok/asmodai if (nleft == 1)
11300382be72SJeroen Ruigrok/asmodai sum += *(u_char *)w;
11310382be72SJeroen Ruigrok/asmodai
11320382be72SJeroen Ruigrok/asmodai /*
11330382be72SJeroen Ruigrok/asmodai * add back carry outs from top 16 bits to low 16 bits
11340382be72SJeroen Ruigrok/asmodai */
11350382be72SJeroen Ruigrok/asmodai sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
11360382be72SJeroen Ruigrok/asmodai sum += (sum >> 16); /* add carry */
11370382be72SJeroen Ruigrok/asmodai answer = ~sum; /* truncate to 16 bits */
11380382be72SJeroen Ruigrok/asmodai return (answer);
11390382be72SJeroen Ruigrok/asmodai }
11400382be72SJeroen Ruigrok/asmodai
11410382be72SJeroen Ruigrok/asmodai /*
11420382be72SJeroen Ruigrok/asmodai * Construct an Internet address representation.
11430382be72SJeroen Ruigrok/asmodai * If the nflag has been supplied, give
11440382be72SJeroen Ruigrok/asmodai * numeric value, otherwise try for symbolic name.
11450382be72SJeroen Ruigrok/asmodai */
1146d6410035SSascha Wildner static char *
inetname(struct in_addr in)11470382be72SJeroen Ruigrok/asmodai inetname(struct in_addr in)
11480382be72SJeroen Ruigrok/asmodai {
11490382be72SJeroen Ruigrok/asmodai static char domain[MAXHOSTNAMELEN], line[MAXHOSTNAMELEN];
11500382be72SJeroen Ruigrok/asmodai static int first = 1;
11510382be72SJeroen Ruigrok/asmodai struct hostent *hp;
11520382be72SJeroen Ruigrok/asmodai char *cp;
11530382be72SJeroen Ruigrok/asmodai
11540382be72SJeroen Ruigrok/asmodai if (first && !nflag) {
11550382be72SJeroen Ruigrok/asmodai first = 0;
11560382be72SJeroen Ruigrok/asmodai if (gethostname(domain, sizeof domain) == 0 &&
11570382be72SJeroen Ruigrok/asmodai (cp = strchr(domain, '.')) != NULL) {
11580382be72SJeroen Ruigrok/asmodai strlcpy(domain, cp + 1, sizeof(domain));
11590382be72SJeroen Ruigrok/asmodai }
11600382be72SJeroen Ruigrok/asmodai }
11610382be72SJeroen Ruigrok/asmodai if (!nflag && in.s_addr != INADDR_ANY) {
116215b85273SSascha Wildner hp = gethostbyaddr(&in, sizeof(in), AF_INET);
11630382be72SJeroen Ruigrok/asmodai if (hp != NULL) {
11640382be72SJeroen Ruigrok/asmodai if ((cp = strchr(hp->h_name, '.')) != NULL &&
11650382be72SJeroen Ruigrok/asmodai strcmp(cp + 1, domain) == 0)
11660382be72SJeroen Ruigrok/asmodai *cp = '\0';
11670382be72SJeroen Ruigrok/asmodai strlcpy(line, hp->h_name, sizeof(line));
11680382be72SJeroen Ruigrok/asmodai return (line);
11690382be72SJeroen Ruigrok/asmodai }
11700382be72SJeroen Ruigrok/asmodai }
11710382be72SJeroen Ruigrok/asmodai return (inet_ntoa(in));
11720382be72SJeroen Ruigrok/asmodai }
11730382be72SJeroen Ruigrok/asmodai
1174d6410035SSascha Wildner static void
usage(void)11750382be72SJeroen Ruigrok/asmodai usage(void)
11760382be72SJeroen Ruigrok/asmodai {
11770382be72SJeroen Ruigrok/asmodai fprintf(stderr,
117846877004SHasso Tepper "usage: %s [-cdDIlMnrSv] [-f first_ttl] [-g gateway_addr] [-m max_ttl]\n"
11790382be72SJeroen Ruigrok/asmodai "\t[-p port] [-P proto] [-q nqueries] [-s src_addr] [-t tos]\n"
11809dadf159SEirik Nygaard "\t[-w waittime] host [packetsize]\n", getprogname());
11810382be72SJeroen Ruigrok/asmodai exit(1);
11820382be72SJeroen Ruigrok/asmodai }
1183