* Copyright (c) 1989 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
static char sccsid
[] = "@(#)ping.c 5.9 (Berkeley) 5/12/91";
* 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
* 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 DEFDATALEN (64 - 8) /* default data length */
#define MAXPACKET (65536 - 60 - 8)/* max packet size */
#define MAXWAIT 10 /* max seconds to wait for response */
#define NROUTES 9 /* number of record route slots */
#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))
#define F_PINGFILLED 0x008
#define F_SO_DONTROUTE 0x080
* MAX_DUP_CHK is the number of bits in received table, i.e. 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];
struct sockaddr whereto
; /* who to ping */
int datalen
= DEFDATALEN
;
int s
; /* socket file descriptor */
u_char outpack
[MAXPACKET
];
char BSPACE
= '\b'; /* characters written for flood */
int ident
; /* process id to identify our packets */
long npackets
; /* max packets to transmit */
long nreceived
; /* # of packets we got back */
long nrepeats
; /* number of duplicates */
long ntransmitted
; /* sequence # for outbound packets = #sent */
int interval
= 1; /* interval between packets */
int timing
; /* flag to do timing */
double tmin
= 1000.0*(double)LONG_MAX
; /* minimum round trip time */
double tmax
; /* maximum round trip time */
double tsum
; /* sum of all times, for doing average */
void catcher(), finish();
extern int errno
, optind
;
int ch
, fdmask
, hold
, packlen
, preload
;
char *target
, hnamebuf
[MAXHOSTNAMELEN
], *malloc();
char rspace
[3 + 4 * NROUTES
+ 1]; /* record route space */
datap
= &outpack
[8 + sizeof(struct timeval
)];
while ((ch
= getopt(argc
, argv
, "Rc:dfh:i:l:np:qrs:v")) != EOF
)
"ping: bad number of packets to transmit.\n");
"ping: %s\n", strerror(EPERM
));
setbuf(stdout
, (char *)NULL
);
case 'i': /* wait between sending packets */
"ping: bad timing interval.\n");
"ping: bad preload value.\n");
case 'p': /* fill buffer with user pattern */
fill((char *)datap
, optarg
);
options
|= F_SO_DONTROUTE
;
case 's': /* size of packet to send */
if (datalen
> MAXPACKET
) {
"ping: packet size too large.\n");
"ping: illegal packet size.\n");
bzero((char *)&whereto
, sizeof(struct sockaddr
));
to
= (struct sockaddr_in
*)&whereto
;
to
->sin_family
= AF_INET
;
to
->sin_addr
.s_addr
= inet_addr(target
);
if (to
->sin_addr
.s_addr
!= (u_int
)-1)
hp
= gethostbyname(target
);
"ping: unknown host %s\n", target
);
to
->sin_family
= hp
->h_addrtype
;
bcopy(hp
->h_addr
, (caddr_t
)&to
->sin_addr
, hp
->h_length
);
(void)strncpy(hnamebuf
, hp
->h_name
, sizeof(hnamebuf
) - 1);
if (options
& F_FLOOD
&& options
& F_INTERVAL
) {
"ping: -f and -i incompatible options.\n");
if (datalen
>= sizeof(struct timeval
)) /* can we time transfer */
packlen
= datalen
+ MAXIPLEN
+ MAXICMPLEN
;
if (!(packet
= (u_char
*)malloc((u_int
)packlen
))) {
(void)fprintf(stderr
, "ping: out of memory.\n");
if (!(options
& F_PINGFILLED
))
for (i
= 8; i
< datalen
; ++i
)
ident
= getpid() & 0xFFFF;
if (!(proto
= getprotobyname("icmp"))) {
(void)fprintf(stderr
, "ping: unknown protocol icmp.\n");
if ((s
= socket(AF_INET
, SOCK_RAW
, proto
->p_proto
)) < 0) {
if (options
& F_SO_DEBUG
)
(void)setsockopt(s
, SOL_SOCKET
, SO_DEBUG
, (char *)&hold
,
if (options
& F_SO_DONTROUTE
)
(void)setsockopt(s
, SOL_SOCKET
, SO_DONTROUTE
, (char *)&hold
,
/* record route option */
if (options
& F_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
,
perror("ping: record route");
"ping: record route not available in this implementation.\n");
* 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
(void)setsockopt(s
, SOL_SOCKET
, SO_RCVBUF
, (char *)&hold
,
if (to
->sin_family
== AF_INET
)
(void)printf("PING %s (%s): %d data bytes\n", hostname
,
inet_ntoa(*(struct in_addr
*)&to
->sin_addr
.s_addr
),
(void)printf("PING %s: %d data bytes\n", hostname
, datalen
);
(void)signal(SIGINT
, finish
);
(void)signal(SIGALRM
, catcher
);
while (preload
--) /* fire off them quickies */
if ((options
& F_FLOOD
) == 0)
catcher(); /* start things going */
if (select(s
+ 1, (fd_set
*)&fdmask
, (fd_set
*)NULL
,
(fd_set
*)NULL
, &timeout
) < 1)
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 (i.e., packets will not be
* launched exactly at 1-second intervals). This does not affect the
* quality of the delay and loss statistics.
(void)signal(SIGALRM
, catcher
);
if (!npackets
|| ntransmitted
< npackets
)
waittime
= 2 * tmax
/ 1000000.0;
(void)signal(SIGALRM
, finish
);
(void)alarm((u_int
)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
;
icp
= (struct icmp
*)outpack
;
icp
->icmp_type
= ICMP_ECHO
;
icp
->icmp_seq
= ntransmitted
++;
icp
->icmp_id
= ident
; /* ID */
CLR(icp
->icmp_seq
% mx_dup_ck
);
(void)gettimeofday((struct timeval
*)&outpack
[8],
(struct timezone
*)NULL
);
cc
= datalen
+ 8; /* skips ICMP portion */
/* compute ICMP checksum here */
icp
->icmp_cksum
= in_cksum((u_short
*)icp
, cc
);
i
= sendto(s
, (char *)outpack
, cc
, 0, &whereto
,
sizeof(struct sockaddr
));
(void)printf("ping: wrote %s %d chars, ret=%d\n",
if (!(options
& F_QUIET
) && options
& F_FLOOD
)
(void)write(STDOUT_FILENO
, &DOT
, 1);
* 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
];
(void)gettimeofday(&tv
, (struct timezone
*)NULL
);
/* Check the IP header */
if (cc
< hlen
+ ICMP_MINLEN
) {
"ping: 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
;
triptime
= (double) (tv
.tv_sec
* 1000000 + tv
.tv_usec
);
if (TST(icp
->icmp_seq
% mx_dup_ck
)) {
SET(icp
->icmp_seq
% mx_dup_ck
);
(void)write(STDOUT_FILENO
, &BSPACE
, 1);
(void)printf("%d bytes from %s: icmp_seq=%u", cc
,
inet_ntoa(*(struct in_addr
*)&from
->sin_addr
.s_addr
),
(void)printf(" ttl=%d", ip
->ip_ttl
);
(void)printf(" time=%.3f ms", triptime
/1000.0);
cp
= (u_char
*)&icp
->icmp_data
[8];
dp
= &outpack
[8 + sizeof(struct timeval
)];
for (i
= 8; i
< datalen
; ++i
, ++cp
, ++dp
) {
(void)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
) {
(void)printf("%x ", *cp
);
/* We've got something other than an ECHOREPLY */
if (!(options
& F_VERBOSE
))
(void)printf("%d bytes from %s: ", cc
,
pr_addr(from
->sin_addr
.s_addr
));
/* Display any IP options */
cp
= (u_char
*)buf
+ sizeof(struct ip
);
for (; hlen
> (int)sizeof(struct ip
); --hlen
, ++cp
)
(void)printf("\nLSRR: ");
(void)printf("\t0.0.0.0");
(void)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
)
&& !(options
& F_FLOOD
)) {
(void)printf("\t(same route)");
bcopy((char *)cp
, old_rr
, i
);
(void)printf("\t0.0.0.0");
(void)printf("\t%s", pr_addr(ntohl(l
)));
(void)printf("\nunknown option %x", *cp
);
if (!(options
& F_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 16 bits.
/* 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
register struct timeval
*out
, *in
;
if ((out
->tv_usec
-= in
->tv_usec
) < 0) {
out
->tv_sec
-= in
->tv_sec
;
* Print out statistics, and give up.
(void)signal(SIGINT
, SIG_IGN
);
(void)printf("--- %s ping statistics ---\n", hostname
);
(void)printf("%ld packets transmitted, ", ntransmitted
);
(void)printf("%ld packets received, ", nreceived
);
(void)printf("+%ld duplicates, ", nrepeats
);
if (nreceived
> ntransmitted
)
(void)printf("-- somebody's printing up packets!");
(void)printf("%d%% packet loss",
(int) (((ntransmitted
- nreceived
) * 100) /
(void)printf("round-trip min/avg/max = %.3f/%.3f/%.3f ms\n",
tmin
/1000.0, tsum
/(nreceived
+ nrepeats
)/1000.0, tmax
/1000.0);
"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.
(void)printf("Echo Reply\n");
/* XXX ID + Seq + Data */
(void)printf("Destination Net Unreachable\n");
(void)printf("Destination Host Unreachable\n");
case ICMP_UNREACH_PROTOCOL
:
(void)printf("Destination Protocol Unreachable\n");
(void)printf("Destination Port Unreachable\n");
case ICMP_UNREACH_NEEDFRAG
:
(void)printf("frag needed and DF set\n");
case ICMP_UNREACH_SRCFAIL
:
(void)printf("Source Route Failed\n");
(void)printf("Dest Unreachable, Bad Code: %d\n",
/* Print returned IP header information */
pr_retip((struct ip
*)icp
->icmp_data
);
(void)printf("Source Quench\n");
pr_retip((struct ip
*)icp
->icmp_data
);
(void)printf("Redirect Network");
(void)printf("Redirect Host");
case ICMP_REDIRECT_TOSNET
:
(void)printf("Redirect Type of Service and Network");
case ICMP_REDIRECT_TOSHOST
:
(void)printf("Redirect Type of Service and Host");
(void)printf("Redirect, Bad Code: %d", icp
->icmp_code
);
(void)printf("(New addr: 0x%08lx)\n", icp
->icmp_gwaddr
.s_addr
);
pr_retip((struct ip
*)icp
->icmp_data
);
(void)printf("Echo Request\n");
/* XXX ID + Seq + Data */
case ICMP_TIMXCEED_INTRANS
:
(void)printf("Time to live exceeded\n");
case ICMP_TIMXCEED_REASS
:
(void)printf("Frag reassembly time exceeded\n");
(void)printf("Time exceeded, Bad Code: %d\n",
pr_retip((struct ip
*)icp
->icmp_data
);
(void)printf("Parameter problem: pointer = 0x%02x\n",
pr_retip((struct ip
*)icp
->icmp_data
);
(void)printf("Timestamp\n");
/* XXX ID + Seq + 3 timestamps */
(void)printf("Timestamp Reply\n");
/* XXX ID + Seq + 3 timestamps */
(void)printf("Information Request\n");
(void)printf("Information Reply\n");
(void)printf("Address Mask Request\n");
(void)printf("Address Mask Reply\n");
(void)printf("Bad ICMP type: %d\n", icp
->icmp_type
);
* Print an IP header with options.
cp
= (u_char
*)ip
+ 20; /* point to options */
(void)printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n");
(void)printf(" %1x %1x %02x %04x %04x",
ip
->ip_v
, ip
->ip_hl
, ip
->ip_tos
, ip
->ip_len
, ip
->ip_id
);
(void)printf(" %1x %04x", ((ip
->ip_off
) & 0xe000) >> 13,
(void)printf(" %02x %02x %04x", ip
->ip_ttl
, ip
->ip_p
, ip
->ip_sum
);
(void)printf(" %s ", inet_ntoa(*(struct in_addr
*)&ip
->ip_src
.s_addr
));
(void)printf(" %s ", inet_ntoa(*(struct in_addr
*)&ip
->ip_dst
.s_addr
));
/* dump and option bytes */
(void)printf("%02x", *cp
++);
* Return an ascii host address as a dotted quad and optionally with
if ((options
& F_NUMERIC
) ||
!(hp
= gethostbyaddr((char *)&l
, 4, AF_INET
)))
(void)sprintf(buf
, "%s", inet_ntoa(*(struct in_addr
*)&l
));
(void)sprintf(buf
, "%s (%s)", hp
->h_name
,
inet_ntoa(*(struct in_addr
*)&l
));
* Dump some info on a returned (via ICMP) IP packet.
cp
= (u_char
*)ip
+ hlen
;
(void)printf("TCP: from port %u, to port %u (decimal)\n",
(*cp
* 256 + *(cp
+ 1)), (*(cp
+ 2) * 256 + *(cp
+ 3)));
(void)printf("UDP: from port %u, to port %u (decimal)\n",
(*cp
* 256 + *(cp
+ 1)), (*(cp
+ 2) * 256 + *(cp
+ 3)));
for (cp
= patp
; *cp
; cp
++)
"ping: 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
)
for (jj
= 0; jj
< ii
; ++jj
)
if (!(options
& F_QUIET
)) {
(void)printf("PATTERN: 0x");
for (jj
= 0; jj
< ii
; ++jj
)
(void)printf("%02x", bp
[jj
] & 0xFF);
"usage: ping [-Rdfnqrv] [-c count] [-i wait] [-l preload]\n\t[-p pattern] [-s packetsize] host\n");