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