* Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
* measure round-trip-delays and packet loss across network paths.
* U. S. Army Ballistic Research Laboratory
* 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
* Public Domain. Distribution Unlimited.
* More statistics could always be gathered.
* This program has to run SUID to ROOT to access the ICMP socket.
#include <netinet/in_systm.h>
#include <netinet/ip_icmp.h>
#include <netinet/ip_var.h>
#define MAXWAIT 10 /* max time to wait for response, sec. */
#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 */
#define MAXHOSTNAMELEN 64
/* 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 ];
#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))
int i
, pingflags
= 0, options
;
int s
; /* Socket file descriptor */
struct hostent
*hp
; /* Pointer to host info */
struct timezone tz
; /* leftover */
struct sockaddr whereto
; /* Who to ping */
int datalen
= 64-8; /* How much data */
"Usage: ping [-dfnqrvR][-c count][-i wait][-l preload][-p pattern][-s packetsize][-h] host \n";
char hnamebuf
[MAXHOSTNAMELEN
];
static u_char outpack
[MAXPACKET
];
int preload
= 0; /* number of packets to "preload" */
int ntransmitted
= 0; /* sequence # for outbound packets = #sent */
unsigned interval
=1; /* interval between packets */
int nreceived
= 0; /* # of packets we got back */
int tsum
= 0; /* sum of all times, for doing average */
char *inet_ntoa(),*strcpy(),*strncpy(),*sprintf();
char rspace
[3+4*NROUTES
+1]; /* record route space */
struct sockaddr_in
*to
= (struct sockaddr_in
*) &whereto
;
int c
, k
, on
= 1, hostind
= 0;
static u_char
*datap
= &outpack
[8+sizeof(struct timeval
)];
while ((c
= getopt(argc
, argv
, "c:dfh:i:l:np:qrs:vR")) != EOF
)
case 'i': /* wait between sending packets */
case 'p': /* fill buffer with user pattern */
pingflags
|= PING_FILLED
;
fill((char *)datap
, optarg
);
case 's': /* size of packet to send */
bzero((char *)&whereto
, sizeof(struct sockaddr
) );
to
->sin_family
= AF_INET
;
to
->sin_addr
.s_addr
= inet_addr(argv
[hostind
]);
if(to
->sin_addr
.s_addr
!= (unsigned)-1) {
strcpy(hnamebuf
, argv
[hostind
]);
hp
= gethostbyname(argv
[hostind
]);
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 );
printf("%s: unknown host %s\n", argv
[0], argv
[hostind
]);
if ( (pingflags
& FLOOD
) && (pingflags
& INTERVAL
) ) {
fprintf(stderr
, "ping: -f and -i incompatible options\n");
if (datalen
> MAXPACKET
) {
fprintf(stderr
, "ping: packet size too large\n");
if (datalen
>= sizeof(struct timeval
)) /* can we time 'em? */
packlen
= datalen
+ 60 + 76; /* MAXIP + MAXICMP */
if( (packet
= (u_char
*)malloc((unsigned)packlen
)) == NULL
) {
fprintf( stderr
, "ping: malloc failed\n" );
if (!(pingflags
& PING_FILLED
)) {
for( k
=8; k
<datalen
; k
++) *datap
++ = k
;
ident
= getpid() & 0xFFFF;
if ((proto
= getprotobyname("icmp")) == NULL
) {
fprintf(stderr
, "icmp: unknown protocol\n");
if ((s
= socket(AF_INET
, SOCK_RAW
, proto
->p_proto
)) < 0) {
if (options
& SO_DEBUG
) {
(void)setsockopt(s
, SOL_SOCKET
, SO_DEBUG
, &on
, sizeof(on
));
if (options
& SO_DONTROUTE
) {
(void)setsockopt(s
, SOL_SOCKET
, SO_DONTROUTE
, &on
, sizeof(on
));
/* Record Route option */
if( pingflags
& RROUTE
) {
rspace
[IPOPT_OPTVAL
] = IPOPT_RR
;
rspace
[IPOPT_OLEN
] = sizeof(rspace
)-1;
rspace
[IPOPT_OFFSET
] = IPOPT_MINOFF
;
if( setsockopt(s
, IPPROTO_IP
, IP_OPTIONS
, rspace
, sizeof(rspace
)) < 0 ) {
perror( "Record route" );
fprintf( stderr
, "ping: record route not available on this machine.\n" );
if(to
->sin_family
== AF_INET
) {
printf("PING %s (%s): %d data bytes\n", hostname
,
inet_ntoa(*(struct in_addr
*)&to
->sin_addr
.s_addr
), datalen
);
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
,
signal( SIGINT
, prefinish
);
signal(SIGALRM
, catcher
);
/* fire off them quickies */
for(i
=0; i
< preload
; i
++)
catcher(); /* start things going */
int fromlen
= sizeof (from
);
if( select(32, (fd_set
*)&fdmask
, (fd_set
*)0, (fd_set
*)0, &timeout
) == 0)
if ( (cc
=recvfrom(s
, (char *)packet
, packlen
, 0, (struct sockaddr
*)&from
, &fromlen
)) < 0) {
perror("ping: recvfrom");
pr_pack( (char *)packet
, cc
, &from
);
if (npackets
&& nreceived
>= npackets
)
* This routine causes another PING to be transmitted, and then
* schedules another SIGALRM for 1 second from now.
* Our sense of time will slowly skew (ie, packets will not be launched
* exactly at 1-second intervals). This does not affect the quality
* of the delay and loss statistics.
signal(SIGALRM
, catcher
);
if (npackets
== 0 || ntransmitted
< npackets
)
waittime
= 2 * tmax
/ 1000;
alarm((unsigned)waittime
);
* Compose and transmit an ICMP ECHO REQUEST packet. The IP packet
* will be added on by the kernel. The ID field is our UNIX process ID,
* and the sequence number is an ascending integer. The first 8 bytes
* of the data portion are used to hold a UNIX "timeval" struct in VAX
* byte-order, to compute the round-trip time.
register struct icmp
*icp
= (struct icmp
*) outpack
;
register struct timeval
*tp
= (struct timeval
*) &outpack
[8];
icp
->icmp_type
= ICMP_ECHO
;
icp
->icmp_seq
= ntransmitted
++;
icp
->icmp_id
= ident
; /* ID */
CLR( icp
->icmp_seq
% mx_dup_ck
);
cc
= datalen
+8; /* skips ICMP portion */
/* Compute ICMP checksum here */
icp
->icmp_cksum
= in_cksum( (u_short
*)icp
, cc
);
/* cc = sendto(s, msg, len, flags, to, tolen) */
i
= sendto( s
, (char *)outpack
, cc
, 0, &whereto
, sizeof(struct sockaddr
) );
if( i
<0 ) perror("sendto");
printf("ping: wrote %s %d chars, ret=%d\n",
if( pingflags
& FLOOD
) {
* Print out the packet, if it came from us. This logic is necessary
* because ALL readers of the ICMP socket get a copy of ALL ICMP packets
* which arrive ('tis only fair). This permits multiple copies of this
* program to be run without having intermingled output (or statistics!).
struct sockaddr_in
*from
;
register struct icmp
*icp
;
static char old_rr
[MAX_IPOPTLEN
];
int hlen
, triptime
, dupflag
;
gettimeofday( &tv
, &tz
);
/* Check the IP header */
if( cc
< hlen
+ ICMP_MINLEN
) {
if( pingflags
& VERBOSE
)
printf("packet too short (%d bytes) from %s\n", cc
,
inet_ntoa(*(struct in_addr
*)&from
->sin_addr
.s_addr
));
icp
= (struct icmp
*)(buf
+ hlen
);
if( icp
->icmp_type
== ICMP_ECHOREPLY
) {
if( icp
->icmp_id
!= ident
)
return; /* 'Twas not our ECHO */
tp
= (struct timeval
*)&icp
->icmp_ip
;
tp
= (struct timeval
*)&icp
->icmp_data
[0];
triptime
= tv
.tv_sec
*1000+(tv
.tv_usec
/1000);
if ( TST(icp
->icmp_seq
%mx_dup_ck
) ) {
SET(icp
->icmp_seq
%mx_dup_ck
);
if( pingflags
& FLOOD
) {
printf("%d bytes from %s: icmp_seq=%d", cc
,
inet_ntoa(*(struct in_addr
*)&from
->sin_addr
.s_addr
),
if ( dupflag
) printf(" DUP!");
printf(" ttl=%d", ip
->ip_ttl
);
printf(" time=%d ms", triptime
);
cp
= (u_char
*)&icp
->icmp_data
[8];
dp
= &outpack
[8+sizeof(struct timeval
)];
for (i
=8; i
<datalen
; i
++, cp
++, dp
++) {
printf("\nwrong data byte #%d should be 0x%x but was 0x%x",
cp
= (u_char
*)&icp
->icmp_data
[0];
for (i
=8; i
<datalen
; i
++, cp
++) {
/* We've got something other than an ECHOREPLY */
if( !(pingflags
& VERBOSE
) )
printf("%d bytes from %s: ",
cc
, pr_addr(from
->sin_addr
.s_addr
) );
/* 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! */
if (j
> IPOPT_MINOFF
) for (;;) {
printf("\t%s", pr_addr(ntohl(l
)));
j
= *++cp
; /* get length */
i
= *++cp
; /* and pointer */
&& cp
== (u_char
*)buf
+ sizeof(struct ip
) + 2
&& !bcmp((char *)cp
, old_rr
, i
)
&& !(pingflags
& FLOOD
)) {
printf("\t(same route)");
bcopy((char *)cp
, old_rr
, i
);
printf("\t%s", pr_addr(ntohl(l
)));
printf("\nunknown option %x", *cp
);
if (!(pingflags
& FLOOD
))
* Checksum routine for Internet Protocol family headers (C Version)
register int nleft
= len
;
register u_short
*w
= addr
;
* Our algorithm is simple, using a 32 bit accumulator (sum),
* we add sequential 16 bit words to it, and at the end, fold
* back all the carry bits from the top 16 bits into the lower
/* mop up an odd byte, if necessary */
*(u_char
*)(&answer
) = *(u_char
*)w
;
* add back carry outs from top 16 bits to low 16 bits
sum
= (sum
>> 16) + (sum
& 0xffff); /* add hi 16 to low 16 */
sum
+= (sum
>> 16); /* add carry */
answer
= ~sum
; /* truncate to 16 bits */
* Subtract 2 timeval structs: out = out - in.
* Out is assumed to be >= in.
register struct timeval
*out
, *in
;
if( (out
->tv_usec
-= in
->tv_usec
) < 0 ) {
out
->tv_sec
-= in
->tv_sec
;
/* On the first SIGINT, allow any outstanding packets to dribble in */
if (nreceived
>= ntransmitted
/* quit now if caught up */
|| nreceived
== 0) /* or if remote is dead */
signal(SIGINT
, finish
); /* do this only the 1st time */
npackets
= ntransmitted
+1; /* let the normal limit work */
* Print out statistics, and give up.
* Heavily buffered STDIO is used here, so that all the statistics
* will be written with 1 sys-write call. This is nice when more
* than one copy of the program is running on a terminal; it prevents
* the statistics output from becomming intermingled.
printf("\n----%s PING Statistics----\n", hostname
);
printf("%d packets transmitted, ", ntransmitted
);
printf("%d packets received, ", nreceived
);
if (nrepeats
) printf("+%d duplicates, ", nrepeats
);
if( nreceived
> ntransmitted
)
printf("-- somebody's printing up packets!");
printf("%d%% packet loss",
(int) (((ntransmitted
-nreceived
)*100) /
printf("round-trip (ms) min/avg/max = %d/%d/%d\n",
tsum
/ (nreceived
+ nrepeats
),
"Echo Reply", /* ip + seq + udata */
"Dest Unreachable", /* net, host, proto, port, frag, sr + IP */
"Source Quench", /* IP */
"Redirect", /* redirect type, gateway, + IP */
"Time Exceeded", /* transit, frag reassem + IP */
"Parameter Problem", /* pointer + IP */
"Timestamp", /* id + seq + three timestamps */
"Timestamp Reply", /* " */
"Info Request", /* id + sq */
* Print a descriptive string about an ICMP header.
switch( icp
->icmp_type
) {
/* XXX ID + Seq + Data */
switch( icp
->icmp_code
) {
printf("Destination Net Unreachable\n");
printf("Destination Host Unreachable\n");
case ICMP_UNREACH_PROTOCOL
:
printf("Destination Protocol Unreachable\n");
printf("Destination Port Unreachable\n");
case ICMP_UNREACH_NEEDFRAG
:
printf("frag needed and DF set\n");
case ICMP_UNREACH_SRCFAIL
:
printf("Source Route Failed\n");
printf("Dest Unreachable, Bad Code: %d\n", icp
->icmp_code
);
/* Print returned IP header information */
pr_retip( &icp
->icmp_ip
);
pr_retip( (struct ip
*)icp
->icmp_data
);
printf("Source Quench\n");
pr_retip( &icp
->icmp_ip
);
pr_retip( (struct ip
*)icp
->icmp_data
);
switch( icp
->icmp_code
) {
printf("Redirect Network");
case ICMP_REDIRECT_TOSNET
:
printf("Redirect Type of Service and Network");
case ICMP_REDIRECT_TOSHOST
:
printf("Redirect Type of Service and Host");
printf("Redirect, Bad Code: %d", icp
->icmp_code
);
printf(" (New addr: 0x%08x)\n", icp
->icmp_hun
.ih_gwaddr
);
pr_retip( &icp
->icmp_ip
);
pr_retip( (struct ip
*)icp
->icmp_data
);
printf("Echo Request\n");
/* XXX ID + Seq + Data */
switch( icp
->icmp_code
) {
case ICMP_TIMXCEED_INTRANS
:
printf("Time to live exceeded\n");
case ICMP_TIMXCEED_REASS
:
printf("Frag reassembly time exceeded\n");
printf("Time exceeded, Bad Code: %d\n", icp
->icmp_code
);
pr_retip( &icp
->icmp_ip
);
pr_retip( (struct ip
*)icp
->icmp_data
);
printf("Parameter problem: pointer = 0x%02x\n",
pr_retip( &icp
->icmp_ip
);
pr_retip( (struct ip
*)icp
->icmp_data
);
/* XXX ID + Seq + 3 timestamps */
printf("Timestamp Reply\n");
/* XXX ID + Seq + 3 timestamps */
printf("Information Request\n");
printf("Information Reply\n");
printf("Address Mask Request\n");
printf("Address Mask Reply\n");
printf("Bad ICMP type: %d\n", icp
->icmp_type
);
* Print an IP header with options.
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 */
* Return an ascii host address
* as a dotted quad and optionally with a hostname
if( (pingflags
& NUMERIC
) || (hp
= gethostbyaddr((char *)&l
, 4, AF_INET
)) == NULL
)
sprintf( buf
, "%s", inet_ntoa(*(struct in_addr
*)&l
) );
sprintf( buf
, "%s (%s)", hp
->h_name
, inet_ntoa(*(struct in_addr
*)&l
) );
* Dump some info on a returned (via ICMP) IP packet.
cp
= (unsigned char *)ip
+ hlen
;
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)) );
printf("\"-p %s\" ???: ", patp
);
printf("patterns must be specified as hex digits\n");
"%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]);
for (kk
=0; kk
<=MAXPACKET
-(8+ii
); kk
+=ii
)
if (!(pingflags
& QUIET
)) {
printf("%02x", bp
[jj
]&0xFF);