print "to" address in case multihomed
[unix-history] / usr / src / sbin / ping / ping.c
CommitLineData
9a0e2985 1#ifndef lint
0f0a1a2e 2static char sccsid[] = "@(#)ping.c 4.6 (Berkeley) %G%";
9a0e2985
MK
3#endif
4
3310aa34
MK
5/*
6 * P I N G . C
7 *
8 * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
9 * measure round-trip-delays and packet loss across network paths.
10 *
11 * Author -
12 * Mike Muuss
13 * U. S. Army Ballistic Research Laboratory
14 * December, 1983
9a0e2985 15 * Modified at Uc Berkeley
3310aa34
MK
16 *
17 * Status -
18 * Public Domain. Distribution Unlimited.
19 *
20 * Bugs -
3310aa34
MK
21 * More statistics could always be gathered.
22 * This program has to run SUID to ROOT to access the ICMP socket.
23 */
24
25#include <stdio.h>
26#include <errno.h>
27#include <sys/time.h>
28
29#include <sys/param.h>
30#include <sys/socket.h>
31#include <sys/file.h>
32
33#include <netinet/in_systm.h>
34#include <netinet/in.h>
35#include <netinet/ip.h>
36#include <netinet/ip_icmp.h>
37#include <netdb.h>
38
9a0e2985 39#define MAXWAIT 10 /* max time to wait for response, sec. */
8b11365c
MK
40#define MAXPACKET 4096 /* max packet size */
41#ifndef MAXHOSTNAMELEN
42#define MAXHOSTNAMELEN 64
43#endif
3310aa34 44
9a0e2985 45int verbose;
8b11365c 46u_char packet[MAXPACKET];
3310aa34
MK
47int options;
48extern int errno;
49
50int s; /* Socket file descriptor */
51struct hostent *hp; /* Pointer to host info */
52struct timezone tz; /* leftover */
53
54struct sockaddr whereto;/* Who to ping */
55int datalen; /* How much data */
56
8b11365c 57char usage[] = "Usage: ping [-drv] host [data size] [npackets]\n";
3310aa34
MK
58
59char *hostname;
8b11365c 60char hnamebuf[MAXHOSTNAMELEN];
3310aa34 61
9a0e2985
MK
62int npackets;
63int ntransmitted = 0; /* sequence # for outbound packets = #sent */
3310aa34
MK
64int ident;
65
66int nreceived = 0; /* # of packets we got back */
9a0e2985 67int timing = 0;
3310aa34
MK
68int tmin = 999999999;
69int tmax = 0;
70int tsum = 0; /* sum of all times, for doing average */
9a0e2985 71int finish(), catcher();
3310aa34
MK
72
73/*
74 * M A I N
75 */
76main(argc, argv)
77char *argv[];
78{
79 struct sockaddr_in from;
80 char **av = argv;
81 struct sockaddr_in *to = (struct sockaddr_in *) &whereto;
9a0e2985 82 int on = 1;
8b11365c 83 struct protoent *proto;
9a0e2985
MK
84
85 argc--, av++;
86 while (argc > 0 && *av[0] == '-') {
87 while (*++av[0]) switch (*av[0]) {
88 case 'd':
89 options |= SO_DEBUG;
90 break;
91 case 'r':
92 options |= SO_DONTROUTE;
93 break;
94 case 'v':
95 verbose++;
96 break;
97 }
3310aa34
MK
98 argc--, av++;
99 }
9a0e2985 100 if( argc < 1) {
3310aa34
MK
101 printf(usage);
102 exit(1);
103 }
104
105 bzero( (char *)&whereto, sizeof(struct sockaddr) );
8b11365c
MK
106 to->sin_family = AF_INET;
107 to->sin_addr.s_addr = inet_addr(av[0]);
108 if (to->sin_addr.s_addr != -1) {
109 strcpy(hnamebuf, av[0]);
110 hostname = hnamebuf;
3310aa34 111 } else {
8b11365c
MK
112 hp = gethostbyname(av[0]);
113 if (hp) {
114 to->sin_family = hp->h_addrtype;
115 bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
116 hostname = hp->h_name;
117 } else {
118 printf("%s: unknown host %s\n", argv[0], av[0]);
119 exit(1);
3310aa34 120 }
3310aa34
MK
121 }
122
9a0e2985
MK
123 if( argc >= 2 )
124 datalen = atoi( av[1] );
3310aa34
MK
125 else
126 datalen = 64-8;
8b11365c
MK
127 if (datalen > MAXPACKET) {
128 fprintf(stderr, "ping: packet size too large\n");
129 exit(1);
130 }
9a0e2985
MK
131 if (datalen >= sizeof(struct timeval))
132 timing = 1;
133 if (argc > 2)
134 npackets = atoi(av[2]);
3310aa34
MK
135
136 ident = getpid() & 0xFFFF;
137
8b11365c
MK
138 if ((proto = getprotobyname("icmp")) == NULL) {
139 fprintf(stderr, "icmp: unknown protocol\n");
140 exit(10);
141 }
142 if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
3310aa34 143 perror("ping: socket");
9a0e2985 144 exit(5);
3310aa34 145 }
9a0e2985
MK
146 if (options & SO_DEBUG)
147 setsockopt(s, SOL_SOCKET, SO_DEBUG, &on, sizeof(on));
148 if (options & SO_DONTROUTE)
149 setsockopt(s, SOL_SOCKET, SO_DONTROUTE, &on, sizeof(on));
3310aa34
MK
150
151 printf("PING %s: %d data bytes\n", hostname, datalen );
152
8b11365c 153 setlinebuf( stdout );
3310aa34
MK
154
155 signal( SIGINT, finish );
9a0e2985 156 signal(SIGALRM, catcher);
3310aa34
MK
157
158 catcher(); /* start things going */
159
160 for (;;) {
161 int len = sizeof (packet);
162 int fromlen = sizeof (from);
163 int cc;
164
9a0e2985 165 if ( (cc=recvfrom(s, packet, len, 0, &from, &fromlen)) < 0) {
3310aa34
MK
166 if( errno == EINTR )
167 continue;
168 perror("ping: recvfrom");
169 continue;
170 }
171 pr_pack( packet, cc, &from );
9a0e2985
MK
172 if (npackets && nreceived >= npackets)
173 finish();
3310aa34
MK
174 }
175 /*NOTREACHED*/
176}
177
178/*
179 * C A T C H E R
180 *
181 * This routine causes another PING to be transmitted, and then
182 * schedules another SIGALRM for 1 second from now.
183 *
184 * Bug -
185 * Our sense of time will slowly skew (ie, packets will not be launched
186 * exactly at 1-second intervals). This does not affect the quality
187 * of the delay and loss statistics.
188 */
189catcher()
190{
9a0e2985
MK
191 int waittime;
192
3310aa34 193 pinger();
9a0e2985
MK
194 if (npackets == 0 || ntransmitted < npackets)
195 alarm(1);
196 else {
197 if (nreceived) {
198 waittime = 2 * tmax / 1000;
199 if (waittime == 0)
200 waittime = 1;
201 } else
202 waittime = MAXWAIT;
203 signal(SIGALRM, finish);
204 alarm(waittime);
205 }
3310aa34
MK
206}
207
208/*
209 * P I N G E R
210 *
211 * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet
212 * will be added on by the kernel. The ID field is our UNIX process ID,
213 * and the sequence number is an ascending integer. The first 8 bytes
214 * of the data portion are used to hold a UNIX "timeval" struct in VAX
215 * byte-order, to compute the round-trip time.
216 */
217pinger()
218{
8b11365c 219 static u_char outpack[MAXPACKET];
3310aa34
MK
220 register struct icmp *icp = (struct icmp *) outpack;
221 int i, cc;
222 register struct timeval *tp = (struct timeval *) &outpack[8];
223 register u_char *datap = &outpack[8+sizeof(struct timeval)];
224
225 icp->icmp_type = ICMP_ECHO;
226 icp->icmp_code = 0;
227 icp->icmp_cksum = 0;
228 icp->icmp_seq = ntransmitted++;
229 icp->icmp_id = ident; /* ID */
230
231 cc = datalen+8; /* skips ICMP portion */
232
9a0e2985
MK
233 if (timing)
234 gettimeofday( tp, &tz );
3310aa34
MK
235
236 for( i=8; i<datalen; i++) /* skip 8 for time */
237 *datap++ = i;
238
239 /* Compute ICMP checksum here */
240 icp->icmp_cksum = in_cksum( icp, cc );
241
242 /* cc = sendto(s, msg, len, flags, to, tolen) */
243 i = sendto( s, outpack, cc, 0, &whereto, sizeof(struct sockaddr) );
244
245 if( i < 0 || i != cc ) {
246 if( i<0 ) perror("sendto");
247 printf("ping: wrote %s %d chars, ret=%d\n",
248 hostname, cc, i );
249 fflush(stdout);
250 }
251}
252
253/*
254 * P R _ T Y P E
255 *
256 * Convert an ICMP "type" field to a printable string.
257 */
258char *
259pr_type( t )
260register int t;
261{
262 static char *ttab[] = {
263 "Echo Reply",
264 "ICMP 1",
265 "ICMP 2",
266 "Dest Unreachable",
267 "Source Quence",
268 "Redirect",
269 "ICMP 6",
270 "ICMP 7",
271 "Echo",
272 "ICMP 9",
273 "ICMP 10",
274 "Time Exceeded",
275 "Parameter Problem",
276 "Timestamp",
277 "Timestamp Reply",
278 "Info Request",
279 "Info Reply"
280 };
281
282 if( t < 0 || t > 16 )
283 return("OUT-OF-RANGE");
284
285 return(ttab[t]);
286}
287
288/*
289 * P R _ P A C K
290 *
291 * Print out the packet, if it came from us. This logic is necessary
292 * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
293 * which arrive ('tis only fair). This permits multiple copies of this
294 * program to be run without having intermingled output (or statistics!).
295 */
824f4533
MK
296pr_pack( buf, cc, from )
297char *buf;
3310aa34
MK
298int cc;
299struct sockaddr_in *from;
300{
824f4533
MK
301 struct ip *ip;
302 register struct icmp *icp;
3310aa34
MK
303 register long *lp = (long *) packet;
304 register int i;
305 struct timeval tv;
824f4533
MK
306 struct timeval *tp;
307 int hlen, triptime;
8b11365c 308 char *inet_ntoa();
3310aa34
MK
309
310 from->sin_addr.s_addr = ntohl( from->sin_addr.s_addr );
311 gettimeofday( &tv, &tz );
312
824f4533
MK
313 ip = (struct ip *) buf;
314 hlen = ip->ip_hl << 2;
315 if (cc < hlen + ICMP_MINLEN) {
316 if (verbose)
317 printf("packet too short (%d bytes) from %s\n", cc,
318 inet_ntoa(ntohl(from->sin_addr.s_addr)));
319 return;
320 }
321 cc -= hlen;
322 icp = (struct icmp *)(buf + hlen);
3310aa34 323 if( icp->icmp_type != ICMP_ECHOREPLY ) {
9a0e2985 324 if (verbose) {
8b11365c
MK
325 printf("%d bytes from %s: ", cc,
326 inet_ntoa(ntohl(from->sin_addr.s_addr)));
9a0e2985
MK
327 printf("icmp_type=%d (%s)\n",
328 icp->icmp_type, pr_type(icp->icmp_type) );
329 for( i=0; i<12; i++)
8b11365c 330 printf("x%2.2x: x%8.8x\n", i*sizeof(long), *lp++ );
9a0e2985 331 printf("icmp_code=%d\n", icp->icmp_code );
9a0e2985
MK
332 }
333 return;
3310aa34
MK
334 }
335 if( icp->icmp_id != ident )
336 return; /* 'Twas not our ECHO */
337
824f4533 338 tp = (struct timeval *)&icp->icmp_data[0];
8b11365c
MK
339 printf("%d bytes from %s: ", cc,
340 inet_ntoa(ntohl(from->sin_addr.s_addr)));
3310aa34 341 printf("icmp_seq=%d. ", icp->icmp_seq );
9a0e2985
MK
342 if (timing) {
343 tvsub( &tv, tp );
344 triptime = tv.tv_sec*1000+(tv.tv_usec/1000);
345 printf("time=%d. ms\n", triptime );
346 tsum += triptime;
347 if( triptime < tmin )
348 tmin = triptime;
349 if( triptime > tmax )
350 tmax = triptime;
351 } else
352 putchar('\n');
3310aa34 353 nreceived++;
3310aa34
MK
354}
355
356
357/*
358 * I N _ C K S U M
359 *
8b11365c 360 * Checksum routine for Internet Protocol family headers (C Version)
3310aa34 361 *
3310aa34
MK
362 */
363in_cksum(addr, len)
364u_short *addr;
365int len;
366{
8b11365c
MK
367 register int nleft = len;
368 register u_short *w = addr;
369 register u_short answer;
370 register int sum = 0;
3310aa34
MK
371
372 /*
8b11365c
MK
373 * Our algorithm is simple, using a 32 bit accumulator (sum),
374 * we add sequential 16 bit words to it, and at the end, fold
375 * back all the carry bits from the top 16 bits into the lower
376 * 16 bits.
3310aa34 377 */
8b11365c 378 while( nleft > 1 ) {
3310aa34
MK
379 sum += *w++;
380 nleft -= 2;
381 }
8b11365c
MK
382
383 /* mop up an odd byte, if necessary */
384 if( nleft == 1 )
3310aa34 385 sum += *(u_char *)w;
3310aa34
MK
386
387 /*
8b11365c 388 * add back carry outs from top 16 bits to low 16 bits
3310aa34 389 */
b55680d9
MK
390 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
391 sum += (sum >> 16); /* add carry */
392 answer = ~sum; /* truncate to 16 bits */
8b11365c 393 return (answer);
3310aa34
MK
394}
395
396/*
397 * T V S U B
398 *
399 * Subtract 2 timeval structs: out = out - in.
400 *
401 * Out is assumed to be >= in.
402 */
403tvsub( out, in )
404register struct timeval *out, *in;
405{
406 if( (out->tv_usec -= in->tv_usec) < 0 ) {
407 out->tv_sec--;
408 out->tv_usec += 1000000;
409 }
410 out->tv_sec -= in->tv_sec;
411}
412
413/*
414 * F I N I S H
415 *
416 * Print out statistics, and give up.
417 * Heavily buffered STDIO is used here, so that all the statistics
418 * will be written with 1 sys-write call. This is nice when more
419 * than one copy of the program is running on a terminal; it prevents
420 * the statistics output from becomming intermingled.
421 */
422finish()
423{
3310aa34
MK
424 printf("\n----%s PING Statistics----\n", hostname );
425 printf("%d packets transmitted, ", ntransmitted );
426 printf("%d packets received, ", nreceived );
9a0e2985
MK
427 if (ntransmitted)
428 printf("%d%% packet loss",
3310aa34 429 (int) (((ntransmitted-nreceived)*100) / ntransmitted ) );
9a0e2985
MK
430 printf("\n");
431 if (nreceived && timing)
432 printf("round-trip (ms) min/avg/max = %d/%d/%d\n",
3310aa34
MK
433 tmin,
434 tsum / nreceived,
435 tmax );
436 fflush(stdout);
437 exit(0);
438}