home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / sbin / ping / ping.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-05-12  |  24.0 KB  |  979 lines

  1. /*
  2.  * Copyright (c) 1989 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * This code is derived from software contributed to Berkeley by
  6.  * Mike Muuss.
  7.  *
  8.  * Redistribution and use in source and binary forms, with or without
  9.  * modification, are permitted provided that the following conditions
  10.  * are met:
  11.  * 1. Redistributions of source code must retain the above copyright
  12.  *    notice, this list of conditions and the following disclaimer.
  13.  * 2. Redistributions in binary form must reproduce the above copyright
  14.  *    notice, this list of conditions and the following disclaimer in the
  15.  *    documentation and/or other materials provided with the distribution.
  16.  * 3. All advertising materials mentioning features or use of this software
  17.  *    must display the following acknowledgement:
  18.  *    This product includes software developed by the University of
  19.  *    California, Berkeley and its contributors.
  20.  * 4. Neither the name of the University nor the names of its contributors
  21.  *    may be used to endorse or promote products derived from this software
  22.  *    without specific prior written permission.
  23.  *
  24.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  25.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  26.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  27.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  28.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  29.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  30.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  31.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  32.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  33.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  34.  * SUCH DAMAGE.
  35.  */
  36.  
  37. #ifndef lint
  38. char copyright[] =
  39. "@(#) Copyright (c) 1989 The Regents of the University of California.\n\
  40.  All rights reserved.\n";
  41. #endif /* not lint */
  42.  
  43. #ifndef lint
  44. static char sccsid[] = "@(#)ping.c    5.9 (Berkeley) 5/12/91";
  45. #endif /* not lint */
  46.  
  47. /*
  48.  *            P I N G . C
  49.  *
  50.  * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
  51.  * measure round-trip-delays and packet loss across network paths.
  52.  *
  53.  * Author -
  54.  *    Mike Muuss
  55.  *    U. S. Army Ballistic Research Laboratory
  56.  *    December, 1983
  57.  *
  58.  * Status -
  59.  *    Public Domain.  Distribution Unlimited.
  60.  * Bugs -
  61.  *    More statistics could always be gathered.
  62.  *    This program has to run SUID to ROOT to access the ICMP socket.
  63.  */
  64.  
  65. #include <sys/param.h>
  66. #include <sys/socket.h>
  67. #include <sys/file.h>
  68. #include <sys/time.h>
  69. #include <sys/signal.h>
  70.  
  71. #include <netinet/in_systm.h>
  72. #include <netinet/in.h>
  73. #include <netinet/ip.h>
  74. #include <netinet/ip_icmp.h>
  75. #include <netinet/ip_var.h>
  76. #include <netdb.h>
  77. #include <unistd.h>
  78. #include <stdio.h>
  79. #include <ctype.h>
  80. #include <errno.h>
  81. #include <string.h>
  82.  
  83. #define    DEFDATALEN    (64 - 8)    /* default data length */
  84. #define    MAXIPLEN    60
  85. #define    MAXICMPLEN    76
  86. #define    MAXPACKET    (65536 - 60 - 8)/* max packet size */
  87. #define    MAXWAIT        10        /* max seconds to wait for response */
  88. #define    NROUTES        9        /* number of record route slots */
  89.  
  90. #define    A(bit)        rcvd_tbl[(bit)>>3]    /* identify byte in array */
  91. #define    B(bit)        (1 << ((bit) & 0x07))    /* identify bit in byte */
  92. #define    SET(bit)    (A(bit) |= B(bit))
  93. #define    CLR(bit)    (A(bit) &= (~B(bit)))
  94. #define    TST(bit)    (A(bit) & B(bit))
  95.  
  96. /* various options */
  97. int options;
  98. #define    F_FLOOD        0x001
  99. #define    F_INTERVAL    0x002
  100. #define    F_NUMERIC    0x004
  101. #define    F_PINGFILLED    0x008
  102. #define    F_QUIET        0x010
  103. #define    F_RROUTE    0x020
  104. #define    F_SO_DEBUG    0x040
  105. #define    F_SO_DONTROUTE    0x080
  106. #define    F_VERBOSE    0x100
  107.  
  108. /*
  109.  * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum
  110.  * number of received sequence numbers we can keep track of.  Change 128
  111.  * to 8192 for complete accuracy...
  112.  */
  113. #define    MAX_DUP_CHK    (8 * 128)
  114. int mx_dup_ck = MAX_DUP_CHK;
  115. char rcvd_tbl[MAX_DUP_CHK / 8];
  116.  
  117. struct sockaddr whereto;    /* who to ping */
  118. int datalen = DEFDATALEN;
  119. int s;                /* socket file descriptor */
  120. u_char outpack[MAXPACKET];
  121. char BSPACE = '\b';        /* characters written for flood */
  122. char DOT = '.';
  123. char *hostname;
  124. int ident;            /* process id to identify our packets */
  125.  
  126. /* counters */
  127. long npackets;            /* max packets to transmit */
  128. long nreceived;            /* # of packets we got back */
  129. long nrepeats;            /* number of duplicates */
  130. long ntransmitted;        /* sequence # for outbound packets = #sent */
  131. int interval = 1;        /* interval between packets */
  132.  
  133. /* timing */
  134. int timing;            /* flag to do timing */
  135. long tmin = LONG_MAX;        /* minimum round trip time */
  136. long tmax;            /* maximum round trip time */
  137. u_long tsum;            /* sum of all times, for doing average */
  138.  
  139. char *pr_addr();
  140. void catcher(), finish();
  141.  
  142. main(argc, argv)
  143.     int argc;
  144.     char **argv;
  145. {
  146.     extern int errno, optind;
  147.     extern char *optarg;
  148.     struct timeval timeout;
  149.     struct hostent *hp;
  150.     struct sockaddr_in *to;
  151.     struct protoent *proto;
  152.     register int i;
  153.     int ch, fdmask, hold, packlen, preload;
  154.     u_char *datap, *packet;
  155.     char *target, hnamebuf[MAXHOSTNAMELEN], *malloc();
  156. #ifdef IP_OPTIONS
  157.     char rspace[3 + 4 * NROUTES + 1];    /* record route space */
  158. #endif
  159.  
  160.     preload = 0;
  161.     datap = &outpack[8 + sizeof(struct timeval)];
  162.     while ((ch = getopt(argc, argv, "Rc:dfh:i:l:np:qrs:v")) != EOF)
  163.         switch(ch) {
  164.         case 'c':
  165.             npackets = atoi(optarg);
  166.             if (npackets <= 0) {
  167.                 (void)fprintf(stderr,
  168.                     "ping: bad number of packets to transmit.\n");
  169.                 exit(1);
  170.             }
  171.             break;
  172.         case 'd':
  173.             options |= F_SO_DEBUG;
  174.             break;
  175.         case 'f':
  176.             if (getuid()) {
  177.                 (void)fprintf(stderr,
  178.                     "ping: %s\n", strerror(EPERM));
  179.                 exit(1);
  180.             }
  181.             options |= F_FLOOD;
  182.             setbuf(stdout, (char *)NULL);
  183.             break;
  184.         case 'i':        /* wait between sending packets */
  185.             interval = atoi(optarg);
  186.             if (interval <= 0) {
  187.                 (void)fprintf(stderr,
  188.                     "ping: bad timing interval.\n");
  189.                 exit(1);
  190.             }
  191.             options |= F_INTERVAL;
  192.             break;
  193.         case 'l':
  194.             preload = atoi(optarg);
  195.             if (preload < 0) {
  196.                 (void)fprintf(stderr,
  197.                     "ping: bad preload value.\n");
  198.                 exit(1);
  199.             }
  200.             break;
  201.         case 'n':
  202.             options |= F_NUMERIC;
  203.             break;
  204.         case 'p':        /* fill buffer with user pattern */
  205.             options |= F_PINGFILLED;
  206.             fill((char *)datap, optarg);
  207.                 break;
  208.         case 'q':
  209.             options |= F_QUIET;
  210.             break;
  211.         case 'R':
  212.             options |= F_RROUTE;
  213.             break;
  214.         case 'r':
  215.             options |= F_SO_DONTROUTE;
  216.             break;
  217.         case 's':        /* size of packet to send */
  218.             datalen = atoi(optarg);
  219.             if (datalen > MAXPACKET) {
  220.                 (void)fprintf(stderr,
  221.                     "ping: packet size too large.\n");
  222.                 exit(1);
  223.             }
  224.             if (datalen <= 0) {
  225.                 (void)fprintf(stderr,
  226.                     "ping: illegal packet size.\n");
  227.                 exit(1);
  228.             }
  229.             break;
  230.         case 'v':
  231.             options |= F_VERBOSE;
  232.             break;
  233.         default:
  234.             usage();
  235.         }
  236.     argc -= optind;
  237.     argv += optind;
  238.  
  239.     if (argc != 1)
  240.         usage();
  241.     target = *argv;
  242.  
  243.     bzero((char *)&whereto, sizeof(struct sockaddr));
  244.     to = (struct sockaddr_in *)&whereto;
  245.     to->sin_family = AF_INET;
  246.     to->sin_addr.s_addr = inet_addr(target);
  247.     if (to->sin_addr.s_addr != (u_int)-1)
  248.         hostname = target;
  249.     else {
  250.         hp = gethostbyname(target);
  251.         if (!hp) {
  252.             (void)fprintf(stderr,
  253.                 "ping: unknown host %s\n", target);
  254.             exit(1);
  255.         }
  256.         to->sin_family = hp->h_addrtype;
  257.         bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
  258.         (void)strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1);
  259.         hostname = hnamebuf;
  260.     }
  261.  
  262.     if (options & F_FLOOD && options & F_INTERVAL) {
  263.         (void)fprintf(stderr,
  264.             "ping: -f and -i incompatible options.\n");
  265.         exit(1);
  266.     }
  267.  
  268.     if (datalen >= sizeof(struct timeval))    /* can we time transfer */
  269.         timing = 1;
  270.     packlen = datalen + MAXIPLEN + MAXICMPLEN;
  271.     if (!(packet = (u_char *)malloc((u_int)packlen))) {
  272.         (void)fprintf(stderr, "ping: out of memory.\n");
  273.         exit(1);
  274.     }
  275.     if (!(options & F_PINGFILLED))
  276.         for (i = 8; i < datalen; ++i)
  277.             *datap++ = i;
  278.  
  279.     ident = getpid() & 0xFFFF;
  280.  
  281.     if (!(proto = getprotobyname("icmp"))) {
  282.         (void)fprintf(stderr, "ping: unknown protocol icmp.\n");
  283.         exit(1);
  284.     }
  285.     if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
  286.         perror("ping: socket");
  287.         exit(1);
  288.     }
  289.     hold = 1;
  290.     if (options & F_SO_DEBUG)
  291.         (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold,
  292.             sizeof(hold));
  293.     if (options & F_SO_DONTROUTE)
  294.         (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&hold,
  295.             sizeof(hold));
  296.  
  297.     /* record route option */
  298.     if (options & F_RROUTE) {
  299. #ifdef IP_OPTIONS
  300.         rspace[IPOPT_OPTVAL] = IPOPT_RR;
  301.         rspace[IPOPT_OLEN] = sizeof(rspace)-1;
  302.         rspace[IPOPT_OFFSET] = IPOPT_MINOFF;
  303.         if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace,
  304.             sizeof(rspace)) < 0) {
  305.             perror("ping: record route");
  306.             exit(1);
  307.         }
  308. #else
  309.         (void)fprintf(stderr,
  310.           "ping: record route not available in this implementation.\n");
  311.         exit(1);
  312. #endif /* IP_OPTIONS */
  313.     }
  314.  
  315.     /*
  316.      * When pinging the broadcast address, you can get a lot of answers.
  317.      * Doing something so evil is useful if you are trying to stress the
  318.      * ethernet, or just want to fill the arp cache to get some stuff for
  319.      * /etc/ethers.
  320.      */
  321.     hold = 48 * 1024;
  322.     (void)setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold,
  323.         sizeof(hold));
  324.  
  325.     if (to->sin_family == AF_INET)
  326.         (void)printf("PING %s (%s): %d data bytes\n", hostname,
  327.             inet_ntoa(*(struct in_addr *)&to->sin_addr.s_addr),
  328.             datalen);
  329.     else
  330.         (void)printf("PING %s: %d data bytes\n", hostname, datalen);
  331.  
  332.     (void)signal(SIGINT, finish);
  333.     (void)signal(SIGALRM, catcher);
  334.  
  335.     while (preload--)        /* fire off them quickies */
  336.         pinger();
  337.  
  338.     if ((options & F_FLOOD) == 0)
  339.         catcher();        /* start things going */
  340.  
  341.     for (;;) {
  342.         struct sockaddr_in from;
  343.         register int cc;
  344.         int fromlen;
  345.  
  346.         if (options & F_FLOOD) {
  347.             pinger();
  348.             timeout.tv_sec = 0;
  349.             timeout.tv_usec = 10000;
  350.             fdmask = 1 << s;
  351.             if (select(s + 1, (fd_set *)&fdmask, (fd_set *)NULL,
  352.                 (fd_set *)NULL, &timeout) < 1)
  353.                 continue;
  354.         }
  355.         fromlen = sizeof(from);
  356.         if ((cc = recvfrom(s, (char *)packet, packlen, 0,
  357.             (struct sockaddr *)&from, &fromlen)) < 0) {
  358.             if (errno == EINTR)
  359.                 continue;
  360.             perror("ping: recvfrom");
  361.             continue;
  362.         }
  363.         pr_pack((char *)packet, cc, &from);
  364.         if (npackets && nreceived >= npackets)
  365.             break;
  366.     }
  367.     finish();
  368.     /* NOTREACHED */
  369. }
  370.  
  371. /*
  372.  * catcher --
  373.  *    This routine causes another PING to be transmitted, and then
  374.  * schedules another SIGALRM for 1 second from now.
  375.  * 
  376.  * bug --
  377.  *    Our sense of time will slowly skew (i.e., packets will not be
  378.  * launched exactly at 1-second intervals).  This does not affect the
  379.  * quality of the delay and loss statistics.
  380.  */
  381. void
  382. catcher()
  383. {
  384.     int waittime;
  385.  
  386.     pinger();
  387.     (void)signal(SIGALRM, catcher);
  388.     if (!npackets || ntransmitted < npackets)
  389.         alarm((u_int)interval);
  390.     else {
  391.         if (nreceived) {
  392.             waittime = 2 * tmax / 1000;
  393.             if (!waittime)
  394.                 waittime = 1;
  395.         } else
  396.             waittime = MAXWAIT;
  397.         (void)signal(SIGALRM, finish);
  398.         (void)alarm((u_int)waittime);
  399.     }
  400. }
  401.  
  402. /*
  403.  * pinger --
  404.  *     Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
  405.  * will be added on by the kernel.  The ID field is our UNIX process ID,
  406.  * and the sequence number is an ascending integer.  The first 8 bytes
  407.  * of the data portion are used to hold a UNIX "timeval" struct in VAX
  408.  * byte-order, to compute the round-trip time.
  409.  */
  410. pinger()
  411. {
  412.     register struct icmp *icp;
  413.     register int cc;
  414.     int i;
  415.  
  416.     icp = (struct icmp *)outpack;
  417.     icp->icmp_type = ICMP_ECHO;
  418.     icp->icmp_code = 0;
  419.     icp->icmp_cksum = 0;
  420.     icp->icmp_seq = ntransmitted++;
  421.     icp->icmp_id = ident;            /* ID */
  422.  
  423.     CLR(icp->icmp_seq % mx_dup_ck);
  424.  
  425.     if (timing)
  426.         (void)gettimeofday((struct timeval *)&outpack[8],
  427.             (struct timezone *)NULL);
  428.  
  429.     cc = datalen + 8;            /* skips ICMP portion */
  430.  
  431.     /* compute ICMP checksum here */
  432.     icp->icmp_cksum = in_cksum((u_short *)icp, cc);
  433.  
  434.     i = sendto(s, (char *)outpack, cc, 0, &whereto,
  435.         sizeof(struct sockaddr));
  436.  
  437.     if (i < 0 || i != cc)  {
  438.         if (i < 0)
  439.             perror("ping: sendto");
  440.         (void)printf("ping: wrote %s %d chars, ret=%d\n",
  441.             hostname, cc, i);
  442.     }
  443.     if (!(options & F_QUIET) && options & F_FLOOD)
  444.         (void)write(STDOUT_FILENO, &DOT, 1);
  445. }
  446.  
  447. /*
  448.  * pr_pack --
  449.  *    Print out the packet, if it came from us.  This logic is necessary
  450.  * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
  451.  * which arrive ('tis only fair).  This permits multiple copies of this
  452.  * program to be run without having intermingled output (or statistics!).
  453.  */
  454. pr_pack(buf, cc, from)
  455.     char *buf;
  456.     int cc;
  457.     struct sockaddr_in *from;
  458. {
  459.     register struct icmp *icp;
  460.     register u_long l;
  461.     register int i, j;
  462.     register u_char *cp,*dp;
  463.     static int old_rrlen;
  464.     static char old_rr[MAX_IPOPTLEN];
  465.     struct ip *ip;
  466.     struct timeval tv, *tp;
  467.     long triptime;
  468.     int hlen, dupflag;
  469.  
  470.     (void)gettimeofday(&tv, (struct timezone *)NULL);
  471.  
  472.     /* Check the IP header */
  473.     ip = (struct ip *)buf;
  474.     hlen = ip->ip_hl << 2;
  475.     if (cc < hlen + ICMP_MINLEN) {
  476.         if (options & F_VERBOSE)
  477.             (void)fprintf(stderr,
  478.               "ping: packet too short (%d bytes) from %s\n", cc,
  479.               inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr));
  480.         return;
  481.     }
  482.  
  483.     /* Now the ICMP part */
  484.     cc -= hlen;
  485.     icp = (struct icmp *)(buf + hlen);
  486.     if (icp->icmp_type == ICMP_ECHOREPLY) {
  487.         if (icp->icmp_id != ident)
  488.             return;            /* 'Twas not our ECHO */
  489.         ++nreceived;
  490.         if (timing) {
  491. #ifndef icmp_data
  492.             tp = (struct timeval *)&icp->icmp_ip;
  493. #else
  494.             tp = (struct timeval *)icp->icmp_data;
  495. #endif
  496.             tvsub(&tv, tp);
  497.             triptime = tv.tv_sec * 1000 + (tv.tv_usec / 1000);
  498.             tsum += triptime;
  499.             if (triptime < tmin)
  500.                 tmin = triptime;
  501.             if (triptime > tmax)
  502.                 tmax = triptime;
  503.         }
  504.  
  505.         if (TST(icp->icmp_seq % mx_dup_ck)) {
  506.             ++nrepeats;
  507.             --nreceived;
  508.             dupflag = 1;
  509.         } else {
  510.             SET(icp->icmp_seq % mx_dup_ck);
  511.             dupflag = 0;
  512.         }
  513.  
  514.         if (options & F_QUIET)
  515.             return;
  516.  
  517.         if (options & F_FLOOD)
  518.             (void)write(STDOUT_FILENO, &BSPACE, 1);
  519.         else {
  520.             (void)printf("%d bytes from %s: icmp_seq=%u", cc,
  521.                inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr),
  522.                icp->icmp_seq);
  523.             (void)printf(" ttl=%d", ip->ip_ttl);
  524.             if (timing)
  525.                 (void)printf(" time=%ld ms", triptime);
  526.             if (dupflag)
  527.                 (void)printf(" (DUP!)");
  528.             /* check the data */
  529.             cp = (u_char*)&icp->icmp_data[8];
  530.             dp = &outpack[8 + sizeof(struct timeval)];
  531.             for (i = 8; i < datalen; ++i, ++cp, ++dp) {
  532.                 if (*cp != *dp) {
  533.     (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x",
  534.         i, *dp, *cp);
  535.                     cp = (u_char*)&icp->icmp_data[0];
  536.                     for (i = 8; i < datalen; ++i, ++cp) {
  537.                         if ((i % 32) == 8)
  538.                             (void)printf("\n\t");
  539.                         (void)printf("%x ", *cp);
  540.                     }
  541.                     break;
  542.                 }
  543.             }
  544.         }
  545.     } else {
  546.         /* We've got something other than an ECHOREPLY */
  547.         if (!(options & F_VERBOSE))
  548.             return;
  549.         (void)printf("%d bytes from %s: ", cc,
  550.             pr_addr(from->sin_addr.s_addr));
  551.         pr_icmph(icp);
  552.     }
  553.  
  554.     /* Display any IP options */
  555.     cp = (u_char *)buf + sizeof(struct ip);
  556.  
  557.     for (; hlen > (int)sizeof(struct ip); --hlen, ++cp)
  558.         switch (*cp) {
  559.         case IPOPT_EOL:
  560.             hlen = 0;
  561.             break;
  562.         case IPOPT_LSRR:
  563.             (void)printf("\nLSRR: ");
  564.             hlen -= 2;
  565.             j = *++cp;
  566.             ++cp;
  567.             if (j > IPOPT_MINOFF)
  568.                 for (;;) {
  569.                     l = *++cp;
  570.                     l = (l<<8) + *++cp;
  571.                     l = (l<<8) + *++cp;
  572.                     l = (l<<8) + *++cp;
  573.                     if (l == 0)
  574.                         (void)printf("\t0.0.0.0");
  575.                 else
  576.                     (void)printf("\t%s", pr_addr(ntohl(l)));
  577.                 hlen -= 4;
  578.                 j -= 4;
  579.                 if (j <= IPOPT_MINOFF)
  580.                     break;
  581.                 (void)putchar('\n');
  582.             }
  583.             break;
  584.         case IPOPT_RR:
  585.             j = *++cp;        /* get length */
  586.             i = *++cp;        /* and pointer */
  587.             hlen -= 2;
  588.             if (i > j)
  589.                 i = j;
  590.             i -= IPOPT_MINOFF;
  591.             if (i <= 0)
  592.                 continue;
  593.             if (i == old_rrlen
  594.                 && cp == (u_char *)buf + sizeof(struct ip) + 2
  595.                 && !bcmp((char *)cp, old_rr, i)
  596.                 && !(options & F_FLOOD)) {
  597.                 (void)printf("\t(same route)");
  598.                 i = ((i + 3) / 4) * 4;
  599.                 hlen -= i;
  600.                 cp += i;
  601.                 break;
  602.             }
  603.             old_rrlen = i;
  604.             bcopy((char *)cp, old_rr, i);
  605.             (void)printf("\nRR: ");
  606.             for (;;) {
  607.                 l = *++cp;
  608.                 l = (l<<8) + *++cp;
  609.                 l = (l<<8) + *++cp;
  610.                 l = (l<<8) + *++cp;
  611.                 if (l == 0)
  612.                     (void)printf("\t0.0.0.0");
  613.                 else
  614.                     (void)printf("\t%s", pr_addr(ntohl(l)));
  615.                 hlen -= 4;
  616.                 i -= 4;
  617.                 if (i <= 0)
  618.                     break;
  619.                 (void)putchar('\n');
  620.             }
  621.             break;
  622.         case IPOPT_NOP:
  623.             (void)printf("\nNOP");
  624.             break;
  625.         default:
  626.             (void)printf("\nunknown option %x", *cp);
  627.             break;
  628.         }
  629.     if (!(options & F_FLOOD)) {
  630.         (void)putchar('\n');
  631.         (void)fflush(stdout);
  632.     }
  633. }
  634.  
  635. /*
  636.  * in_cksum --
  637.  *    Checksum routine for Internet Protocol family headers (C Version)
  638.  */
  639. in_cksum(addr, len)
  640.     u_short *addr;
  641.     int len;
  642. {
  643.     register int nleft = len;
  644.     register u_short *w = addr;
  645.     register int sum = 0;
  646.     u_short answer = 0;
  647.  
  648.     /*
  649.      * Our algorithm is simple, using a 32 bit accumulator (sum), we add
  650.      * sequential 16 bit words to it, and at the end, fold back all the
  651.      * carry bits from the top 16 bits into the lower 16 bits.
  652.      */
  653.     while (nleft > 1)  {
  654.         sum += *w++;
  655.         nleft -= 2;
  656.     }
  657.  
  658.     /* mop up an odd byte, if necessary */
  659.     if (nleft == 1) {
  660.         *(u_char *)(&answer) = *(u_char *)w ;
  661.         sum += answer;
  662.     }
  663.  
  664.     /* add back carry outs from top 16 bits to low 16 bits */
  665.     sum = (sum >> 16) + (sum & 0xffff);    /* add hi 16 to low 16 */
  666.     sum += (sum >> 16);            /* add carry */
  667.     answer = ~sum;                /* truncate to 16 bits */
  668.     return(answer);
  669. }
  670.  
  671. /*
  672.  * tvsub --
  673.  *    Subtract 2 timeval structs:  out = out - in.  Out is assumed to
  674.  * be >= in.
  675.  */
  676. tvsub(out, in)
  677.     register struct timeval *out, *in;
  678. {
  679.     if ((out->tv_usec -= in->tv_usec) < 0) {
  680.         --out->tv_sec;
  681.         out->tv_usec += 1000000;
  682.     }
  683.     out->tv_sec -= in->tv_sec;
  684. }
  685.  
  686. /*
  687.  * finish --
  688.  *    Print out statistics, and give up.
  689.  */
  690. void
  691. finish()
  692. {
  693.     (void)signal(SIGINT, SIG_IGN);
  694.     (void)putchar('\n');
  695.     (void)fflush(stdout);
  696.     (void)printf("--- %s ping statistics ---\n", hostname);
  697.     (void)printf("%ld packets transmitted, ", ntransmitted);
  698.     (void)printf("%ld packets received, ", nreceived);
  699.     if (nrepeats)
  700.         (void)printf("+%ld duplicates, ", nrepeats);
  701.     if (ntransmitted)
  702.         if (nreceived > ntransmitted)
  703.             (void)printf("-- somebody's printing up packets!");
  704.         else
  705.             (void)printf("%d%% packet loss",
  706.                 (int) (((ntransmitted - nreceived) * 100) /
  707.                 ntransmitted));
  708.     (void)putchar('\n');
  709.     if (nreceived && timing)
  710.         (void)printf("round-trip min/avg/max = %ld/%lu/%ld ms\n",
  711.             tmin, tsum / (nreceived + nrepeats), tmax);
  712.     exit(0);
  713. }
  714.  
  715. #ifdef notdef
  716. static char *ttab[] = {
  717.     "Echo Reply",        /* ip + seq + udata */
  718.     "Dest Unreachable",    /* net, host, proto, port, frag, sr + IP */
  719.     "Source Quench",    /* IP */
  720.     "Redirect",        /* redirect type, gateway, + IP  */
  721.     "Echo",
  722.     "Time Exceeded",    /* transit, frag reassem + IP */
  723.     "Parameter Problem",    /* pointer + IP */
  724.     "Timestamp",        /* id + seq + three timestamps */
  725.     "Timestamp Reply",    /* " */
  726.     "Info Request",        /* id + sq */
  727.     "Info Reply"        /* " */
  728. };
  729. #endif
  730.  
  731. /*
  732.  * pr_icmph --
  733.  *    Print a descriptive string about an ICMP header.
  734.  */
  735. pr_icmph(icp)
  736.     struct icmp *icp;
  737. {
  738.     switch(icp->icmp_type) {
  739.     case ICMP_ECHOREPLY:
  740.         (void)printf("Echo Reply\n");
  741.         /* XXX ID + Seq + Data */
  742.         break;
  743.     case ICMP_UNREACH:
  744.         switch(icp->icmp_code) {
  745.         case ICMP_UNREACH_NET:
  746.             (void)printf("Destination Net Unreachable\n");
  747.             break;
  748.         case ICMP_UNREACH_HOST:
  749.             (void)printf("Destination Host Unreachable\n");
  750.             break;
  751.         case ICMP_UNREACH_PROTOCOL:
  752.             (void)printf("Destination Protocol Unreachable\n");
  753.             break;
  754.         case ICMP_UNREACH_PORT:
  755.             (void)printf("Destination Port Unreachable\n");
  756.             break;
  757.         case ICMP_UNREACH_NEEDFRAG:
  758.             (void)printf("frag needed and DF set\n");
  759.             break;
  760.         case ICMP_UNREACH_SRCFAIL:
  761.             (void)printf("Source Route Failed\n");
  762.             break;
  763.         default:
  764.             (void)printf("Dest Unreachable, Bad Code: %d\n",
  765.                 icp->icmp_code);
  766.             break;
  767.         }
  768.         /* Print returned IP header information */
  769. #ifndef icmp_data
  770.         pr_retip(&icp->icmp_ip);
  771. #else
  772.         pr_retip((struct ip *)icp->icmp_data);
  773. #endif
  774.         break;
  775.     case ICMP_SOURCEQUENCH:
  776.         (void)printf("Source Quench\n");
  777. #ifndef icmp_data
  778.         pr_retip(&icp->icmp_ip);
  779. #else
  780.         pr_retip((struct ip *)icp->icmp_data);
  781. #endif
  782.         break;
  783.     case ICMP_REDIRECT:
  784.         switch(icp->icmp_code) {
  785.         case ICMP_REDIRECT_NET:
  786.             (void)printf("Redirect Network");
  787.             break;
  788.         case ICMP_REDIRECT_HOST:
  789.             (void)printf("Redirect Host");
  790.             break;
  791.         case ICMP_REDIRECT_TOSNET:
  792.             (void)printf("Redirect Type of Service and Network");
  793.             break;
  794.         case ICMP_REDIRECT_TOSHOST:
  795.             (void)printf("Redirect Type of Service and Host");
  796.             break;
  797.         default:
  798.             (void)printf("Redirect, Bad Code: %d", icp->icmp_code);
  799.             break;
  800.         }
  801.         (void)printf("(New addr: 0x%08lx)\n", icp->icmp_gwaddr.s_addr);
  802. #ifndef icmp_data
  803.         pr_retip(&icp->icmp_ip);
  804. #else
  805.         pr_retip((struct ip *)icp->icmp_data);
  806. #endif
  807.         break;
  808.     case ICMP_ECHO:
  809.         (void)printf("Echo Request\n");
  810.         /* XXX ID + Seq + Data */
  811.         break;
  812.     case ICMP_TIMXCEED:
  813.         switch(icp->icmp_code) {
  814.         case ICMP_TIMXCEED_INTRANS:
  815.             (void)printf("Time to live exceeded\n");
  816.             break;
  817.         case ICMP_TIMXCEED_REASS:
  818.             (void)printf("Frag reassembly time exceeded\n");
  819.             break;
  820.         default:
  821.             (void)printf("Time exceeded, Bad Code: %d\n",
  822.                 icp->icmp_code);
  823.             break;
  824.         }
  825. #ifndef icmp_data
  826.         pr_retip(&icp->icmp_ip);
  827. #else
  828.         pr_retip((struct ip *)icp->icmp_data);
  829. #endif
  830.         break;
  831.     case ICMP_PARAMPROB:
  832.         (void)printf("Parameter problem: pointer = 0x%02x\n",
  833.             icp->icmp_hun.ih_pptr);
  834. #ifndef icmp_data
  835.         pr_retip(&icp->icmp_ip);
  836. #else
  837.         pr_retip((struct ip *)icp->icmp_data);
  838. #endif
  839.         break;
  840.     case ICMP_TSTAMP:
  841.         (void)printf("Timestamp\n");
  842.         /* XXX ID + Seq + 3 timestamps */
  843.         break;
  844.     case ICMP_TSTAMPREPLY:
  845.         (void)printf("Timestamp Reply\n");
  846.         /* XXX ID + Seq + 3 timestamps */
  847.         break;
  848.     case ICMP_IREQ:
  849.         (void)printf("Information Request\n");
  850.         /* XXX ID + Seq */
  851.         break;
  852.     case ICMP_IREQREPLY:
  853.         (void)printf("Information Reply\n");
  854.         /* XXX ID + Seq */
  855.         break;
  856. #ifdef ICMP_MASKREQ
  857.     case ICMP_MASKREQ:
  858.         (void)printf("Address Mask Request\n");
  859.         break;
  860. #endif
  861. #ifdef ICMP_MASKREPLY
  862.     case ICMP_MASKREPLY:
  863.         (void)printf("Address Mask Reply\n");
  864.         break;
  865. #endif
  866.     default:
  867.         (void)printf("Bad ICMP type: %d\n", icp->icmp_type);
  868.     }
  869. }
  870.  
  871. /*
  872.  * pr_iph --
  873.  *    Print an IP header with options.
  874.  */
  875. pr_iph(ip)
  876.     struct ip *ip;
  877. {
  878.     int hlen;
  879.     u_char *cp;
  880.  
  881.     hlen = ip->ip_hl << 2;
  882.     cp = (u_char *)ip + 20;        /* point to options */
  883.  
  884.     (void)printf("Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst Data\n");
  885.     (void)printf(" %1x  %1x  %02x %04x %04x",
  886.         ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id);
  887.     (void)printf("   %1x %04x", ((ip->ip_off) & 0xe000) >> 13,
  888.         (ip->ip_off) & 0x1fff);
  889.     (void)printf("  %02x  %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum);
  890.     (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr));
  891.     (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr));
  892.     /* dump and option bytes */
  893.     while (hlen-- > 20) {
  894.         (void)printf("%02x", *cp++);
  895.     }
  896.     (void)putchar('\n');
  897. }
  898.  
  899. /*
  900.  * pr_addr --
  901.  *    Return an ascii host address as a dotted quad and optionally with
  902.  * a hostname.
  903.  */
  904. char *
  905. pr_addr(l)
  906.     u_long l;
  907. {
  908.     struct hostent *hp;
  909.     static char buf[80];
  910.  
  911.     if ((options & F_NUMERIC) ||
  912.         !(hp = gethostbyaddr((char *)&l, 4, AF_INET)))
  913.         (void)sprintf(buf, "%s", inet_ntoa(*(struct in_addr *)&l));
  914.     else
  915.         (void)sprintf(buf, "%s (%s)", hp->h_name,
  916.             inet_ntoa(*(struct in_addr *)&l));
  917.     return(buf);
  918. }
  919.  
  920. /*
  921.  * pr_retip --
  922.  *    Dump some info on a returned (via ICMP) IP packet.
  923.  */
  924. pr_retip(ip)
  925.     struct ip *ip;
  926. {
  927.     int hlen;
  928.     u_char *cp;
  929.  
  930.     pr_iph(ip);
  931.     hlen = ip->ip_hl << 2;
  932.     cp = (u_char *)ip + hlen;
  933.  
  934.     if (ip->ip_p == 6)
  935.         (void)printf("TCP: from port %u, to port %u (decimal)\n",
  936.             (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
  937.     else if (ip->ip_p == 17)
  938.         (void)printf("UDP: from port %u, to port %u (decimal)\n",
  939.             (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3)));
  940. }
  941.  
  942. fill(bp, patp)
  943.     char *bp, *patp;
  944. {
  945.     register int ii, jj, kk;
  946.     int pat[16];
  947.     char *cp;
  948.  
  949.     for (cp = patp; *cp; cp++)
  950.         if (!isxdigit(*cp)) {
  951.             (void)fprintf(stderr,
  952.                 "ping: patterns must be specified as hex digits.\n");
  953.             exit(1);
  954.         }
  955.     ii = sscanf(patp,
  956.         "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x",
  957.         &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6],
  958.         &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12],
  959.         &pat[13], &pat[14], &pat[15]);
  960.  
  961.     if (ii > 0)
  962.         for (kk = 0; kk <= MAXPACKET - (8 + ii); kk += ii)
  963.             for (jj = 0; jj < ii; ++jj)
  964.                 bp[jj + kk] = pat[jj];
  965.     if (!(options & F_QUIET)) {
  966.         (void)printf("PATTERN: 0x");
  967.         for (jj = 0; jj < ii; ++jj)
  968.             (void)printf("%02x", bp[jj] & 0xFF);
  969.         (void)printf("\n");
  970.     }
  971. }
  972.  
  973. usage()
  974. {
  975.     (void)fprintf(stderr,
  976.         "usage: ping [-Rdfnqrv] [-c count] [-i wait] [-l preload]\n\t[-p pattern] [-s packetsize] host\n");
  977.     exit(1);
  978. }
  979.