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