From 63ed9e52558f5ba20c9eda9a86eb9866ecd0a066 Mon Sep 17 00:00:00 2001 From: Keith Bostic Date: Wed, 16 Aug 1989 20:52:50 -0800 Subject: [PATCH] from Cliff Frost SCCS-vsn: sbin/ping/ping.c 4.13 SCCS-vsn: sbin/ping/ping.8 6.4 --- usr/src/sbin/ping/ping.8 | 224 +++++++++-- usr/src/sbin/ping/ping.c | 824 ++++++++++++++++++++++++++++++--------- 2 files changed, 819 insertions(+), 229 deletions(-) diff --git a/usr/src/sbin/ping/ping.8 b/usr/src/sbin/ping/ping.8 index 9cdeb81ee8..f6a216dc2e 100644 --- a/usr/src/sbin/ping/ping.8 +++ b/usr/src/sbin/ping/ping.8 @@ -1,53 +1,61 @@ -.\" Copyright (c) 1985 The Regents of the University of California. -.\" All rights reserved. -.\" -.\" Redistribution and use in source and binary forms are permitted -.\" provided that the above copyright notice and this paragraph are -.\" duplicated in all such forms and that any documentation, -.\" advertising materials, and other materials related to such -.\" distribution and use acknowledge that the software was developed -.\" by the University of California, Berkeley. The name of the -.\" University may not be used to endorse or promote products derived -.\" from this software without specific prior written permission. -.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED -.\" WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. -.\" -.\" @(#)ping.8 6.3 (Berkeley) %G% -.\" -.TH PING 8 "" -.UC 6 +.TH PING 8 "May 25, 1989" .SH NAME ping \- send ICMP ECHO_REQUEST packets to network hosts .SH SYNOPSIS .B /etc/ping [ -.B \-r -] [ -.B \-v -] -.I host -[ -.I packetsize -] [ -.I count -] +.B -dfnqrvR +][ +.B -c +count][ +.B -i +wait][ +.B -l +preload][ +.B -p +pattern][ +.B -s +packetsize] +.B [-h] +.B host .SH DESCRIPTION The DARPA Internet is a large and complex aggregation of network hardware, connected together by gateways. Tracking a single-point hardware or software failure can often be difficult. .I Ping -utilizes the +uses the ICMP protocol's mandatory ECHO_REQUEST datagram to elicit an ICMP ECHO_RESPONSE from a host or gateway. ECHO_REQUEST datagrams (``pings'') have an IP and ICMP header, followed by a \fBstruct timeval\fR, and then an arbitrary number of ``pad'' bytes used to fill out the packet. Default datagram length is 64 bytes, but this may be changed -using the command-line option. +using the \fI-s\fR option. Other options are: .TP +.B \-d +Set the SO_DEBUG option on the socket being used. +.TP +.B \-f +Flood ping. Outputs packets as fast as they come back or one hundred times +per second, whichever is more. For every ECHO_REQUEST sent a period '.' +is printed, while for ever ECHO_REPLY received a backspace is printed. +This provides a rapid display of how many packets are being dropped. +\fIThis can be very hard on a network and should be used with caution.\fR +.TP +.B \-h +You may use this flag to specify the host, or leave it off if the host +is the last token on the command line. +.TP +.B \-n +Numeric output only. No attempt will be made to lookup symbolic +names for host addresses. +.TP +.B \-q +Quiet output. Nothing is displayed except the summary lines at the +beginning and at the end. +.TP .B \-r Bypass the normal routing tables and send directly to a host on an attached network. @@ -55,29 +63,63 @@ If the host is not on a directly-attached network, an error is returned. This option can be used to ping a local host through an interface that has no route through it (e.g., after the interface was dropped by -.IR routed (8C)). +.I routed(8C) +). .TP .B \-v -Verbose output. ICMP packets other than ECHO RESPONSE that are received +Verbose output. ICMP packets other than ECHO_RESPONSE that are received are listed. +.TP +.B \-R +Record Route. Includes the RECORD_ROUTE option in the ECHO_REQUEST +packet and displays the route buffer on returned packets. Note that +the IP header is only large enough for nine such routes. Many hosts +ignore or discard this option. +.TP +.B \-c \fIcount\fR +Stop after receiving \fIcount\fR ECHO_RESPONSE packets. +.TP +.B \-i \fIwait\fR +.br +Wait \fIwait\fR seconds between sending each packet. +The default is to wait for one second between each packet. +This option is incompatible with the \fB-f\fR option. +.TP +.B \-l \fIpreload\fR +If +.I preload +is given, +.I ping +sends that many packets as fast as possible before +falling into its normal mode of behavior. +.TP +.B \-p \fIpattern\fR +You may specify up to 16 "pad" bytes to fill out the packet you send. +This is useful for diagnosing data-dependent problems in a network. +For example, "-p ff" will cause the sent packet to be filled with all ones. +.TP +.B \-s \fIpacketsize\fR +Specifies the number of data bytes to be sent. +The default is 56, which translates into 64 ICMP data bytes when combined +with the 8 bytes of ICMP header data. .PP -When using \fIping\fR for fault isolation, -it should first be run on the local -host, to verify that the local network interface is up and -running. +When using \fIping\fR for fault isolation, it should first be run on the local +host, to verify that the local network interface is up and running. Then, hosts and gateways further and further away should be ``pinged''. -\fIPing\fR sends one datagram per second, and +\fIPing\fR sends one datagram per second (or per \fIwait\fR seconds), and prints one line of output for every ECHO_RESPONSE returned. -No output is produced if there is no response. If an optional .I count is given, only that number of requests is sent. Round-trip times and packet loss statistics are computed. +If duplicate packets are received, they are not included in the +packet loss calculation, although the +round trip time of these packets is used in calculating the +minimun/average/maximum round-trip time numbers. When all responses have been received or the program times out (with a .I count -specified), -or if the program is terminated with a SIGINT, a brief +specified), or if the program is terminated with a SIGINT, a brief summary is displayed. .PP This program is intended for use in network testing, measurement @@ -87,7 +129,105 @@ Because of the load it could impose on the network, it is unwise to use .I ping during normal operations or from automated scripts. +.SH ICMP Packet Details +An IP header without options is 20 bytes. +An ICMP ECHO_REQUEST packet contains an additional 8 bytes worth +of ICMP header followed by an arbitrary amount of data. When a +.I packetsize +is given, this indicated the size of this extra blob of data (the +default is 56). Thus the amount of data received inside of an IP +packet of type ICMP ECHO_REPLY will always be 8 bytes more than +the requested data space (the ICMP header). +.PP +If the data space is at least eight bytes large, +.I ping +uses the first eight bytes of this space to include a timestamp which +it uses in the computation of round trip times. This explains why if +less than eight bytes of pad are requested, no round trip times are given. +.SH Duplicate and Damaged packets +\fIPing\fR will report duplicate and damaged packets. +Duplicate packets should never occur, and seem to be caused by +inappropriate link-level retransmissions. +The author has seen duplicates in many situations and has never known them +to be a good thing, although the presence of low levels of +duplicates may not always be cause for alarm. +Network maintainers ignore them at +their own risk as they have been known to be harbingers of severe +network problems. +.PP +Damaged packets are obviously serious cause for alarm and most likely +indicate broken hardware somewhere in the \fIping\fR packet's path +(in the network or in the hosts). +.SH Trying Different Data Patterns +It should go without saying that the (inter)network layer +\fIshould\fR never treat packets differently depending on the +data contained in the data portion. +Unfortunately, data-dependent problems have been known to sneak into +networks and remain undetected for long periods of time. +In many cases the particular pattern that will have problems is something +that doesn't have "enough" transitions, such as all ones or all zeros, +or a pattern right at the edge, such as almost all zeros. +It isn't necessarily enough to specify a data pattern of all zeros (for +example) on the command line (as in \fB-p 00\fR), because the pattern +that is of interest is at the data link level, and the relationship +between what you type and what the controllers transmit can be +complicated. +.PP +This means that if you have a data-dependent problem you will have +to be prepared to do a lot of testing to find it. +If you are lucky, you may manage to find a file that either can't be sent +across your network or that takes much longer to transfer than other +similar length files. +You can then examine this file for repeated patterns that you can test +using the \fI-p\fR option of \fIping\fR. +.SH TTL Details +The TTL value of an IP packet represents the maximum number of IP routers +that the packet can go through before being thrown away. +In current practice you can expect each router in the Internet to decrement the +TTL field by exactly one. +.PP +The TCP/IP specification says that the TTL field for TCP packets should +be set to 60, but many systems use smaller values (4.3 BSD uses 30, 4.2 used +15). +.PP +The maximum possible value of this field is 255, and most Unix systems set +the TTL field of ICMP ECHO_REQUEST packets to 255. +This is why you will find you can "ping" some hosts, but not reach them with +telnet or ftp. +.PP +In normal operation ping prints the ttl value from the packet it receives. +When a remote system receives a ping packet, it can do one of three things +with the TTL field in its response: +.TP +.B (1) +Not change it; this is what Berkeley Unix systems did until 4.3 BSD tahoe +level releases. +In this case the TTL value in the received packet will be 255 minus the +number of routers in the round-trip path. +.TP +.B (2) +Set it to 255; this is what Berkeley Unix systems have done since the 4.3 +tahoe release. +In this case the TTL value in the received packet will be 255 minus the number +of routers in the path \fIfrom\fR the remote system \fIto\fR the +\fIping\fRing host. +.TP +.B (3) +Set it to some other value. +Some machines use the same value for ICMP packets that they use for +TCP packets, for example either 30 or 60. +Others may use completely wild values. +.SH BUGS +Many Hosts and Gateways ignore the RECORD_ROUTE option. +.PP +The maximum IP header length is too small for options like +RECORD_ROUTE to be completely useful. There's not much that +we can do about that however. +.PP +Flood pinging is not recommended in general, and flood pinging the broadcast +address should only be done under very controlled conditions. .SH AUTHOR Mike Muuss .SH SEE ALSO -netstat(1), ifconfig(8) +netstat(1), +ifconfig(8C) diff --git a/usr/src/sbin/ping/ping.c b/usr/src/sbin/ping/ping.c index 827c6e705e..c6e3c2b3a9 100644 --- a/usr/src/sbin/ping/ping.c +++ b/usr/src/sbin/ping/ping.c @@ -1,30 +1,3 @@ -/* - * Copyright (c) 1987, 1988 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms are permitted - * provided that the above copyright notice and this paragraph are - * duplicated in all such forms and that any documentation, - * advertising materials, and other materials related to such - * distribution and use acknowledge that the software was developed - * by the University of California, Berkeley. The name of the - * University may not be used to endorse or promote products derived - * from this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. - */ - -#ifndef lint -char copyright[] = -"@(#) Copyright (c) 1987, 1988 Regents of the University of California.\n\ - All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)ping.c 4.12 (Berkeley) %G%"; -#endif /* not lint */ - /* * P I N G . C * @@ -36,6 +9,10 @@ static char sccsid[] = "@(#)ping.c 4.12 (Berkeley) %G%"; * U. S. Army Ballistic Research Laboratory * December, 1983 * Modified at Uc Berkeley + * Record Route and verbose headers - Phil Dykstra, BRL, March 1988. + * ttl, duplicate detection - Cliff Frost, UCB, April 1989 + * Pad pattern - Cliff Frost (from Tom Ferrin, UCSF), April 1989 + * Wait for dribbles, option decoding, pkt compare - vjs@sgi.com, May 1989 * * Status - * Public Domain. Distribution Unlimited. @@ -48,6 +25,7 @@ static char sccsid[] = "@(#)ping.c 4.12 (Berkeley) %G%"; #include #include #include +#include #include #include @@ -57,36 +35,67 @@ static char sccsid[] = "@(#)ping.c 4.12 (Berkeley) %G%"; #include #include #include +#include +#include #include #define MAXWAIT 10 /* max time to wait for response, sec. */ -#define MAXPACKET 4096 /* max packet size */ +#define MAXPACKET (65536-60-8) /* max packet size */ +#define VERBOSE 1 /* verbose flag */ +#define QUIET 2 /* quiet flag */ +#define FLOOD 4 /* floodping flag */ +#define RROUTE 8 /* record route flag */ +#define PING_FILLED 16 /* is buffer filled? */ +#define NUMERIC 32 /* don't do gethostbyaddr() calls */ +#define INTERVAL 64 /* did user specify interval? */ +#define NROUTES 9 /* number of record route slots */ #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 #endif -int verbose; -u_char packet[MAXPACKET]; -int options; +/* MAX_DUP_CHK is the number of bits in received table, ie the */ +/* maximum number of received sequence numbers we can keep track of. */ +/* Change 128 to 8192 for complete accuracy... */ + +#define MAX_DUP_CHK 8 * 128 +int mx_dup_ck = MAX_DUP_CHK; +char rcvd_tbl[ MAX_DUP_CHK / 8 ]; +int nrepeats = 0; + +#define A(bit) rcvd_tbl[ (bit>>3) ] /* identify byte in array */ +#define B(bit) ( 1 << (bit & 0x07) ) /* identify bit in byte */ +#define SET(bit) A(bit) |= B(bit) +#define CLR(bit) A(bit) &= (~B(bit)) +#define TST(bit) (A(bit) & B(bit)) + + +char *malloc(); + +u_char *packet; +int packlen; +int i, pingflags = 0, options; extern int errno; int s; /* Socket file descriptor */ struct hostent *hp; /* Pointer to host info */ struct timezone tz; /* leftover */ -struct sockaddr whereto;/* Who to ping */ -int datalen; /* How much data */ +struct sockaddr whereto; /* Who to ping */ +int datalen = 64-8; /* How much data */ -char usage[] = "Usage: ping [-drv] host [data size] [npackets]\n"; +char usage[] = +"Usage: ping [-dfnqrvR][-c count][-i wait][-l preload][-p pattern][-s packetsize][-h] host \n"; char *hostname; char hnamebuf[MAXHOSTNAMELEN]; -char *inet_ntoa(); -int npackets; -int burst = 1; +static u_char outpack[MAXPACKET]; + +int npackets=0; +int preload = 0; /* number of packets to "preload" */ int ntransmitted = 0; /* sequence # for outbound packets = #sent */ int ident; +unsigned interval=1; /* interval between packets */ int nreceived = 0; /* # of packets we got back */ int timing = 0; @@ -94,6 +103,12 @@ int tmin = 999999999; int tmax = 0; int tsum = 0; /* sum of all times, for doing average */ int finish(), catcher(); +int bufspace = 48*1024; +int prefinish(); +char *inet_ntoa(),*strcpy(),*strncpy(),*sprintf(); +char *pr_addr(); +u_long inet_addr(); +char rspace[3+4*NROUTES+1]; /* record route space */ /* * M A I N @@ -102,63 +117,110 @@ main(argc, argv) char *argv[]; { struct sockaddr_in from; - char **av = argv; - char *toaddr = NULL; +/* char **av = argv; */ struct sockaddr_in *to = (struct sockaddr_in *) &whereto; - int on = 1; + int c, k, on = 1, hostind = 0; struct protoent *proto; - - argc--, av++; - while (argc > 0 && *av[0] == '-') { - while (*++av[0]) switch (*av[0]) { + static u_char *datap = &outpack[8+sizeof(struct timeval)]; + extern int optind; + extern char *optarg; + + while ((c = getopt(argc, argv, "c:dfh:i:l:np:qrs:vR")) != EOF) + switch(c) { + case 'c': + npackets = atoi(optarg); + break; case 'd': options |= SO_DEBUG; break; + case 'f': + pingflags |= FLOOD; + break; + case 'h': + hostind = optind-1; + break; + case 'i': /* wait between sending packets */ + interval = atoi(optarg); + if (interval == 0) + interval = 1; + pingflags |= INTERVAL; + break; + case 'l': + preload = atoi(optarg); + break; + case 'n': + pingflags |= NUMERIC; + break; + case 'p': /* fill buffer with user pattern */ + pingflags |= PING_FILLED; + fill((char *)datap, optarg); + break; + case 'q': + pingflags |= QUIET; + break; case 'r': options |= SO_DONTROUTE; break; + case 's': /* size of packet to send */ + datalen = atoi(optarg); + break; case 'v': - verbose++; + pingflags |= VERBOSE; + break; + case 'R': + pingflags |= RROUTE; break; + default: + printf(usage); + exit(1); } - argc--, av++; - } - if( argc < 1) { - printf(usage); - exit(1); + + if (hostind == 0) { + if (optind != argc-1) { + fprintf(stderr, usage); + exit(1); + } else hostind = optind; } - bzero( (char *)&whereto, sizeof(struct sockaddr) ); + bzero((char *)&whereto, sizeof(struct sockaddr) ); to->sin_family = AF_INET; - to->sin_addr.s_addr = inet_addr(av[0]); - if (to->sin_addr.s_addr != -1) { - strcpy(hnamebuf, av[0]); + to->sin_addr.s_addr = inet_addr(argv[hostind]); + if(to->sin_addr.s_addr != (unsigned)-1) { + strcpy(hnamebuf, argv[hostind]); hostname = hnamebuf; } else { - hp = gethostbyname(av[0]); - if (!hp) { - fprintf(stderr, "ping: %s: ", av[0]); - herror((char *)NULL); + hp = gethostbyname(argv[hostind]); + if (hp) { + to->sin_family = hp->h_addrtype; + bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length); + strncpy( hnamebuf, hp->h_name, sizeof(hnamebuf)-1 ); + hostname = hnamebuf; + } else { + printf("%s: unknown host %s\n", argv[0], argv[hostind]); exit(1); } - to->sin_family = hp->h_addrtype; - bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length); - hostname = hp->h_name; - toaddr = inet_ntoa(to->sin_addr.s_addr); } - if( argc >= 2 ) - datalen = atoi( av[1] ); - else - datalen = 64-8; + if ( (pingflags & FLOOD) && (pingflags & INTERVAL) ) { + fprintf(stderr, "ping: -f and -i incompatible options\n"); + exit(1); + } + if (datalen > MAXPACKET) { fprintf(stderr, "ping: packet size too large\n"); exit(1); } - if (datalen >= sizeof(struct timeval)) + if (datalen >= sizeof(struct timeval)) /* can we time 'em? */ timing = 1; - if (argc > 2) - npackets = atoi(av[2]); + packlen = datalen + 60 + 76; /* MAXIP + MAXICMP */ + if( (packet = (u_char *)malloc((unsigned)packlen)) == NULL ) { + fprintf( stderr, "ping: malloc failed\n" ); + exit(1); + } + + if (!(pingflags & PING_FILLED)) { + for( k=8; ksin_family == AF_INET) { + printf("PING %s (%s): %d data bytes\n", hostname, + inet_ntoa(*(struct in_addr *)&to->sin_addr.s_addr), datalen); + } else { + printf("PING %s: %d data bytes\n", hostname, datalen ); + } + /* When pinging the broadcast address, you can get a lot + * of answers. Doing something so evil is useful if you + * are trying to stress the ethernet, or just want to + * fill the arp cache to get some stuff for /etc/ethers. + */ + (void)setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)&bufspace, + sizeof(bufspace)); - signal( SIGINT, finish ); + signal( SIGINT, prefinish ); signal(SIGALRM, catcher); - catcher(); /* start things going */ + /* fire off them quickies */ + for(i=0; i < preload; i++) + pinger(); + + if(!(pingflags & FLOOD)) + catcher(); /* start things going */ for (;;) { - int len = sizeof (packet); - int fromlen; + int fromlen = sizeof (from); int cc; + struct timeval timeout; + int fdmask = 1 << s; - fromlen = sizeof (from); - if ( (cc=recvfrom(s, packet, len, 0, &from, &fromlen)) < 0) { + timeout.tv_sec = 0; + timeout.tv_usec = 10000; + + if(pingflags & FLOOD) { + pinger(); + if( select(32, (fd_set *)&fdmask, (fd_set *)0, (fd_set *)0, &timeout) == 0) + continue; + } + if ( (cc=recvfrom(s, (char *)packet, packlen, 0, (struct sockaddr *)&from, &fromlen)) < 0) { if( errno == EINTR ) continue; perror("ping: recvfrom"); continue; } - pr_pack( packet, cc, &from ); + pr_pack( (char *)packet, cc, &from ); if (npackets && nreceived >= npackets) finish(); } @@ -223,8 +321,9 @@ catcher() int waittime; pinger(); + signal(SIGALRM, catcher); if (npackets == 0 || ntransmitted < npackets) - alarm(1); + alarm(interval); else { if (nreceived) { waittime = 2 * tmax / 1000; @@ -233,7 +332,7 @@ catcher() } else waittime = MAXWAIT; signal(SIGALRM, finish); - alarm(waittime); + alarm((unsigned)waittime); } } @@ -248,11 +347,9 @@ catcher() */ pinger() { - static u_char outpack[MAXPACKET]; register struct icmp *icp = (struct icmp *) outpack; - int i, cc, n; + int i, cc; register struct timeval *tp = (struct timeval *) &outpack[8]; - register u_char *datap = &outpack[8+sizeof(struct timeval)]; icp->icmp_type = ICMP_ECHO; icp->icmp_code = 0; @@ -260,63 +357,29 @@ pinger() icp->icmp_seq = ntransmitted++; icp->icmp_id = ident; /* ID */ + CLR( icp->icmp_seq % mx_dup_ck ); + cc = datalen+8; /* skips ICMP portion */ if (timing) gettimeofday( tp, &tz ); - for( i=8; iicmp_cksum = in_cksum( icp, cc ); + icp->icmp_cksum = in_cksum( (u_short *)icp, cc ); /* cc = sendto(s, msg, len, flags, to, tolen) */ - for (n = 0; n < burst; n++) { - i = sendto(s, outpack, cc, 0, &whereto, sizeof(whereto)); + i = sendto( s, (char *)outpack, cc, 0, &whereto, sizeof(struct sockaddr) ); - if( i < 0 || i != cc ) { - if( i<0 ) perror("sendto"); - printf("ping: wrote %s %d chars, ret=%d\n", - hostname, cc, i ); - fflush(stdout); - } + if( i < 0 || i != cc ) { + if( i<0 ) perror("sendto"); + printf("ping: wrote %s %d chars, ret=%d\n", + hostname, cc, i ); + fflush(stdout); + } + if( pingflags & FLOOD ) { + putchar('.'); + fflush(stdout); } -} - -/* - * P R _ T Y P E - * - * Convert an ICMP "type" field to a printable string. - */ -char * -pr_type( t ) -register int t; -{ - static char *ttab[] = { - "Echo Reply", - "ICMP 1", - "ICMP 2", - "Dest Unreachable", - "Source Quence", - "Redirect", - "ICMP 6", - "ICMP 7", - "Echo", - "ICMP 9", - "ICMP 10", - "Time Exceeded", - "Parameter Problem", - "Timestamp", - "Timestamp Reply", - "Info Request", - "Info Reply" - }; - - if( t < 0 || t > 16 ) - return("OUT-OF-RANGE"); - - return(ttab[t]); } /* @@ -334,59 +397,181 @@ struct sockaddr_in *from; { struct ip *ip; register struct icmp *icp; - register long *lp = (long *) packet; - register int i; + register int i, j; + register u_char *cp,*dp; + static int old_rrlen; + static char old_rr[MAX_IPOPTLEN]; struct timeval tv; struct timeval *tp; - int hlen, triptime; + int hlen, triptime, dupflag; - from->sin_addr.s_addr = ntohl( from->sin_addr.s_addr ); gettimeofday( &tv, &tz ); + /* Check the IP header */ ip = (struct ip *) buf; hlen = ip->ip_hl << 2; - if (cc < hlen + ICMP_MINLEN) { - if (verbose) + if( cc < hlen + ICMP_MINLEN ) { + if( pingflags & VERBOSE ) printf("packet too short (%d bytes) from %s\n", cc, - inet_ntoa(ntohl(from->sin_addr.s_addr))); + inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr)); + fflush(stdout); return; } + + /* Now the ICMP part */ cc -= hlen; icp = (struct icmp *)(buf + hlen); - if( icp->icmp_type != ICMP_ECHOREPLY ) { - if (verbose) { - printf("%d bytes from %s: ", cc, - inet_ntoa(ntohl(from->sin_addr.s_addr))); - printf("icmp_type=%d (%s)\n", - icp->icmp_type, pr_type(icp->icmp_type) ); - for( i=0; i<12; i++) - printf("x%2.2x: x%8.8x\n", i*sizeof(long), *lp++ ); - printf("icmp_code=%d\n", icp->icmp_code ); + if( icp->icmp_type == ICMP_ECHOREPLY ) { + if( icp->icmp_id != ident ) + return; /* 'Twas not our ECHO */ + + nreceived++; + if (timing) { +#ifndef icmp_data + tp = (struct timeval *)&icp->icmp_ip; +#else + tp = (struct timeval *)&icp->icmp_data[0]; +#endif + tvsub( &tv, tp ); + triptime = tv.tv_sec*1000+(tv.tv_usec/1000); + tsum += triptime; + if( triptime < tmin ) + tmin = triptime; + if( triptime > tmax ) + tmax = triptime; } - return; + + if ( TST(icp->icmp_seq%mx_dup_ck) ) { + nrepeats++, nreceived--; + dupflag=1; + } else { + SET(icp->icmp_seq%mx_dup_ck); + dupflag=0; + } + + if( pingflags & QUIET ) + return; + + if( pingflags & FLOOD ) { + putchar('\b'); + fflush(stdout); + } else { + printf("%d bytes from %s: icmp_seq=%d", cc, + inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr), + icp->icmp_seq ); + if ( dupflag ) printf(" DUP!"); + printf(" ttl=%d", ip->ip_ttl); + if (timing) + printf(" time=%d ms", triptime ); + /* check the data */ + cp = (u_char*)&icp->icmp_data[8]; + dp = &outpack[8+sizeof(struct timeval)]; + for (i=8; iicmp_data[0]; + for (i=8; isin_addr.s_addr) ); + pr_icmph( icp ); + } + + /* Display any IP options */ + cp = (u_char *)buf + sizeof(struct ip); + while (hlen > sizeof(struct ip) & (hlen >= 0)) { /* !ANSI C will */ + register unsigned long l; /* force hlen to */ + switch (*cp) { /* unsigned! */ + case IPOPT_EOL: + hlen = 0; + break; + case IPOPT_LSRR: + printf("\nLSRR: "); + hlen -= 2; + j = *++cp; + ++cp; + if (j > IPOPT_MINOFF) for (;;) { + l = *++cp; + l = (l<<8) + *++cp; + l = (l<<8) + *++cp; + l = (l<<8) + *++cp; + if (l == 0) + printf("\t0.0.0.0"); + else + printf("\t%s", pr_addr(ntohl(l))); + hlen -= 4; + j -= 4; + if (j <= IPOPT_MINOFF) + break; + putchar('\n'); + } + break; + case IPOPT_RR: + j = *++cp; /* get length */ + i = *++cp; /* and pointer */ + hlen -= 2; + if (i > j) i = j; + i -= IPOPT_MINOFF; + if (i <= 0) + continue; + if (i == old_rrlen + && cp == (u_char *)buf + sizeof(struct ip) + 2 + && !bcmp((char *)cp, old_rr, i) + && !(pingflags & FLOOD)) { + printf("\t(same route)"); + i = ((i+3)/4)*4; + hlen -= i; + cp += i; + break; + } + old_rrlen = i; + bcopy((char *)cp, old_rr, i); + printf("\nRR: "); + for (;;) { + l = *++cp; + l = (l<<8) + *++cp; + l = (l<<8) + *++cp; + l = (l<<8) + *++cp; + if (l == 0) + printf("\t0.0.0.0"); + else + printf("\t%s", pr_addr(ntohl(l))); + hlen -= 4; + i -= 4; + if (i <= 0) + break; + putchar('\n'); + } + break; + case IPOPT_NOP: + printf("\nNOP"); + break; + default: + printf("\nunknown option %x", *cp); + break; + } + hlen--; + cp++; } - if( icp->icmp_id != ident ) - return; /* 'Twas not our ECHO */ - - tp = (struct timeval *)&icp->icmp_data[0]; - printf("%d bytes from %s: ", cc, - inet_ntoa(ntohl(from->sin_addr.s_addr))); - printf("icmp_seq=%d. ", icp->icmp_seq ); - if (timing) { - tvsub( &tv, tp ); - triptime = tv.tv_sec*1000+(tv.tv_usec/1000); - printf("time=%d. ms\n", triptime ); - tsum += triptime; - if( triptime < tmin ) - tmin = triptime; - if( triptime > tmax ) - tmax = triptime; - } else + if (!(pingflags & FLOOD)) putchar('\n'); - nreceived++; + fflush(stdout); } - /* * I N _ C K S U M * @@ -399,9 +584,8 @@ int len; { register int nleft = len; register u_short *w = addr; - register u_short answer; register int sum = 0; - u_short odd_byte = 0; + u_short answer = 0; /* * Our algorithm is simple, using a 32 bit accumulator (sum), @@ -416,8 +600,8 @@ int len; /* mop up an odd byte, if necessary */ if( nleft == 1 ) { - *(u_char *)(&odd_byte) = *(u_char *)w; - sum += odd_byte; + *(u_char *)(&answer) = *(u_char *)w ; + sum += answer; } /* @@ -446,6 +630,15 @@ register struct timeval *out, *in; out->tv_sec -= in->tv_sec; } +/* On the first SIGINT, allow any outstanding packets to dribble in */ +prefinish() +{ + if (nreceived >= ntransmitted /* quit now if caught up */ + || nreceived == 0) /* or if remote is dead */ + finish(); + signal(SIGINT, finish); /* do this only the 1st time */ + npackets = ntransmitted+1; /* let the normal limit work */ +} /* * F I N I S H * @@ -457,23 +650,280 @@ register struct timeval *out, *in; */ finish() { + putchar('\n'); + fflush(stdout); printf("\n----%s PING Statistics----\n", hostname ); printf("%d packets transmitted, ", ntransmitted ); printf("%d packets received, ", nreceived ); - if (ntransmitted) { - if (nreceived <= ntransmitted) - printf("%d%% packet loss", - (int) (((ntransmitted-nreceived)*100) / ntransmitted)); + if (nrepeats) printf("+%d duplicates, ", nrepeats ); + if (ntransmitted) + if( nreceived > ntransmitted) + printf("-- somebody's printing up packets!"); else - printf("%.2f responses per request", - (float) nreceived / (float) ntransmitted); - } + printf("%d%% packet loss", + (int) (((ntransmitted-nreceived)*100) / + ntransmitted)); printf("\n"); if (nreceived && timing) printf("round-trip (ms) min/avg/max = %d/%d/%d\n", tmin, - tsum / nreceived, + tsum / (nreceived + nrepeats), tmax ); fflush(stdout); exit(0); } + +#if 0 +static char *ttab[] = { + "Echo Reply", /* ip + seq + udata */ + "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */ + "Source Quench", /* IP */ + "Redirect", /* redirect type, gateway, + IP */ + "Echo", + "Time Exceeded", /* transit, frag reassem + IP */ + "Parameter Problem", /* pointer + IP */ + "Timestamp", /* id + seq + three timestamps */ + "Timestamp Reply", /* " */ + "Info Request", /* id + sq */ + "Info Reply" /* " */ +}; +#endif /* 0 */ + +/* + * Print a descriptive string about an ICMP header. + */ +pr_icmph( icp ) +struct icmp *icp; +{ + switch( icp->icmp_type ) { + case ICMP_ECHOREPLY: + printf("Echo Reply\n"); + /* XXX ID + Seq + Data */ + break; + case ICMP_UNREACH: + switch( icp->icmp_code ) { + case ICMP_UNREACH_NET: + printf("Destination Net Unreachable\n"); + break; + case ICMP_UNREACH_HOST: + printf("Destination Host Unreachable\n"); + break; + case ICMP_UNREACH_PROTOCOL: + printf("Destination Protocol Unreachable\n"); + break; + case ICMP_UNREACH_PORT: + printf("Destination Port Unreachable\n"); + break; + case ICMP_UNREACH_NEEDFRAG: + printf("frag needed and DF set\n"); + break; + case ICMP_UNREACH_SRCFAIL: + printf("Source Route Failed\n"); + break; + default: + printf("Dest Unreachable, Bad Code: %d\n", icp->icmp_code ); + break; + } + /* Print returned IP header information */ +#ifndef icmp_data + pr_retip( &icp->icmp_ip ); +#else + pr_retip( (struct ip *)icp->icmp_data ); +#endif + break; + case ICMP_SOURCEQUENCH: + printf("Source Quench\n"); +#ifndef icmp_data + pr_retip( &icp->icmp_ip ); +#else + pr_retip( (struct ip *)icp->icmp_data ); +#endif + break; + case ICMP_REDIRECT: + switch( icp->icmp_code ) { + case ICMP_REDIRECT_NET: + printf("Redirect Network"); + break; + case ICMP_REDIRECT_HOST: + printf("Redirect Host"); + break; + case ICMP_REDIRECT_TOSNET: + printf("Redirect Type of Service and Network"); + break; + case ICMP_REDIRECT_TOSHOST: + printf("Redirect Type of Service and Host"); + break; + default: + printf("Redirect, Bad Code: %d", icp->icmp_code ); + break; + } + printf(" (New addr: 0x%08x)\n", icp->icmp_hun.ih_gwaddr ); +#ifndef icmp_data + pr_retip( &icp->icmp_ip ); +#else + pr_retip( (struct ip *)icp->icmp_data ); +#endif + break; + case ICMP_ECHO: + printf("Echo Request\n"); + /* XXX ID + Seq + Data */ + break; + case ICMP_TIMXCEED: + switch( icp->icmp_code ) { + case ICMP_TIMXCEED_INTRANS: + printf("Time to live exceeded\n"); + break; + case ICMP_TIMXCEED_REASS: + printf("Frag reassembly time exceeded\n"); + break; + default: + printf("Time exceeded, Bad Code: %d\n", icp->icmp_code ); + break; + } +#ifndef icmp_data + pr_retip( &icp->icmp_ip ); +#else + pr_retip( (struct ip *)icp->icmp_data ); +#endif + break; + case ICMP_PARAMPROB: + printf("Parameter problem: pointer = 0x%02x\n", + icp->icmp_hun.ih_pptr ); +#ifndef icmp_data + pr_retip( &icp->icmp_ip ); +#else + pr_retip( (struct ip *)icp->icmp_data ); +#endif + break; + case ICMP_TSTAMP: + printf("Timestamp\n"); + /* XXX ID + Seq + 3 timestamps */ + break; + case ICMP_TSTAMPREPLY: + printf("Timestamp Reply\n"); + /* XXX ID + Seq + 3 timestamps */ + break; + case ICMP_IREQ: + printf("Information Request\n"); + /* XXX ID + Seq */ + break; + case ICMP_IREQREPLY: + printf("Information Reply\n"); + /* XXX ID + Seq */ + break; +#ifdef ICMP_MASKREQ + case ICMP_MASKREQ: + printf("Address Mask Request\n"); + break; +#endif +#ifdef ICMP_MASKREPLY + case ICMP_MASKREPLY: + printf("Address Mask Reply\n"); + break; +#endif + default: + printf("Bad ICMP type: %d\n", icp->icmp_type); + } +} + +/* + * Print an IP header with options. + */ +pr_iph( ip ) +struct ip *ip; +{ + int hlen; + unsigned char *cp; + + hlen = ip->ip_hl << 2; + cp = (unsigned char *)ip + 20; /* point to options */ + + printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n"); + printf(" %1x %1x %02x %04x %04x", + ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id ); + printf(" %1x %04x", ((ip->ip_off)&0xe000)>>13, (ip->ip_off)&0x1fff ); + printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum ); + printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr)); + printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr)); + /* dump and option bytes */ + while( hlen-- > 20 ) { + printf( "%02x", *cp++ ); + } + printf("\n"); +} + +/* + * Return an ascii host address + * as a dotted quad and optionally with a hostname + */ +char * +pr_addr( l ) +unsigned long l; +{ + struct hostent *hp; + static char buf[80]; + + if( (pingflags & NUMERIC) || (hp = gethostbyaddr((char *)&l, 4, AF_INET)) == NULL ) + sprintf( buf, "%s", inet_ntoa(*(struct in_addr *)&l) ); + else + sprintf( buf, "%s (%s)", hp->h_name, inet_ntoa(*(struct in_addr *)&l) ); + + return( buf ); +} + +/* + * Dump some info on a returned (via ICMP) IP packet. + */ +pr_retip( ip ) +struct ip *ip; +{ + int hlen; + unsigned char *cp; + + pr_iph( ip ); + hlen = ip->ip_hl << 2; + cp = (unsigned char *)ip + hlen; + + if( ip->ip_p == 6 ) { + printf( "TCP: from port %d, to port %d (decimal)\n", + (*cp*256+*(cp+1)), (*(cp+2)*256+*(cp+3)) ); + } else if( ip->ip_p == 17 ) { + printf( "UDP: from port %d, to port %d (decimal)\n", + (*cp*256+*(cp+1)), (*(cp+2)*256+*(cp+3)) ); + } +} + +fill(bp, patp) +char *bp, *patp; +{ + register int ii,jj,kk; + char *cp; + int pat[16]; + + for (cp=patp; *cp; cp++) + if (!isxdigit(*cp)) { + printf("\"-p %s\" ???: ", patp); + printf("patterns must be specified as hex digits\n"); + exit(1); + } + + ii = sscanf(patp, + "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", + &pat[0], &pat[1], &pat[2], &pat[3], + &pat[4], &pat[5], &pat[6], &pat[7], + &pat[8], &pat[9], &pat[10], &pat[11], + &pat[12], &pat[13], &pat[14], &pat[15]); + + if (ii > 0) + for (kk=0; kk<=MAXPACKET-(8+ii); kk+=ii) + for (jj=0; jj