xref: /original-bsd/sbin/ping/ping.c (revision 00986467)
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
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.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1987 Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)ping.c	4.9 (Berkeley) 06/18/88";
26 #endif /* not lint */
27 
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
38  * Modified at Uc Berkeley
39  *
40  * Status -
41  *	Public Domain.  Distribution Unlimited.
42  *
43  * Bugs -
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 
62 #define	MAXWAIT		10	/* max time to wait for response, sec. */
63 #define	MAXPACKET	4096	/* max packet size */
64 #ifndef MAXHOSTNAMELEN
65 #define MAXHOSTNAMELEN	64
66 #endif
67 
68 int	verbose;
69 u_char	packet[MAXPACKET];
70 int	options;
71 extern	int errno;
72 
73 int s;			/* Socket file descriptor */
74 struct hostent *hp;	/* Pointer to host info */
75 struct timezone tz;	/* leftover */
76 
77 struct sockaddr whereto;/* Who to ping */
78 int datalen;		/* How much data */
79 
80 char usage[] = "Usage:  ping [-drv] host [data size] [npackets]\n";
81 
82 char *hostname;
83 char hnamebuf[MAXHOSTNAMELEN];
84 char *inet_ntoa();
85 
86 int npackets;
87 int ntransmitted = 0;		/* sequence # for outbound packets = #sent */
88 int ident;
89 
90 int nreceived = 0;		/* # of packets we got back */
91 int timing = 0;
92 int tmin = 999999999;
93 int tmax = 0;
94 int tsum = 0;			/* sum of all times, for doing average */
95 int finish(), catcher();
96 
97 /*
98  * 			M A I N
99  */
100 main(argc, argv)
101 char *argv[];
102 {
103 	struct sockaddr_in from;
104 	char **av = argv;
105 	char *toaddr = NULL;
106 	struct sockaddr_in *to = (struct sockaddr_in *) &whereto;
107 	int on = 1;
108 	struct protoent *proto;
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 		}
123 		argc--, av++;
124 	}
125 	if( argc < 1)  {
126 		printf(usage);
127 		exit(1);
128 	}
129 
130 	bzero( (char *)&whereto, sizeof(struct sockaddr) );
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;
136 	} else {
137 		hp = gethostbyname(av[0]);
138 		if (hp) {
139 			to->sin_family = hp->h_addrtype;
140 			bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
141 			hostname = hp->h_name;
142 			toaddr = inet_ntoa(to->sin_addr.s_addr);
143 		} else {
144 			printf("%s: unknown host %s\n", argv[0], av[0]);
145 			exit(1);
146 		}
147 	}
148 
149 	if( argc >= 2 )
150 		datalen = atoi( av[1] );
151 	else
152 		datalen = 64-8;
153 	if (datalen > MAXPACKET) {
154 		fprintf(stderr, "ping: packet size too large\n");
155 		exit(1);
156 	}
157 	if (datalen >= sizeof(struct timeval))
158 		timing = 1;
159 	if (argc > 2)
160 		npackets = atoi(av[2]);
161 
162 	ident = getpid() & 0xFFFF;
163 
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) {
169 		perror("ping: socket");
170 		exit(5);
171 	}
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));
176 
177 	printf("PING %s", hostname);
178 	if (toaddr)
179 		printf(" (%s)", toaddr);
180 	printf(": %d data bytes\n", datalen);
181 
182 
183 	setlinebuf( stdout );
184 
185 	signal( SIGINT, finish );
186 	signal(SIGALRM, catcher);
187 
188 	catcher();	/* start things going */
189 
190 	for (;;) {
191 		int len = sizeof (packet);
192 		int fromlen = sizeof (from);
193 		int cc;
194 
195 		if ( (cc=recvfrom(s, packet, len, 0, &from, &fromlen)) < 0) {
196 			if( errno == EINTR )
197 				continue;
198 			perror("ping: recvfrom");
199 			continue;
200 		}
201 		pr_pack( packet, cc, &from );
202 		if (npackets && nreceived >= npackets)
203 			finish();
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  */
219 catcher()
220 {
221 	int waittime;
222 
223 	pinger();
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 	}
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  */
247 pinger()
248 {
249 	static u_char outpack[MAXPACKET];
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 
263 	if (timing)
264 		gettimeofday( tp, &tz );
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  */
288 char *
289 pr_type( t )
290 register 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  */
326 pr_pack( buf, cc, from )
327 char *buf;
328 int cc;
329 struct sockaddr_in *from;
330 {
331 	struct ip *ip;
332 	register struct icmp *icp;
333 	register long *lp = (long *) packet;
334 	register int i;
335 	struct timeval tv;
336 	struct timeval *tp;
337 	int hlen, triptime;
338 
339 	from->sin_addr.s_addr = ntohl( from->sin_addr.s_addr );
340 	gettimeofday( &tv, &tz );
341 
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);
352 	if( icp->icmp_type != ICMP_ECHOREPLY )  {
353 		if (verbose) {
354 			printf("%d bytes from %s: ", cc,
355 				inet_ntoa(ntohl(from->sin_addr.s_addr)));
356 			printf("icmp_type=%d (%s)\n",
357 				icp->icmp_type, pr_type(icp->icmp_type) );
358 			for( i=0; i<12; i++)
359 			    printf("x%2.2x: x%8.8x\n", i*sizeof(long), *lp++ );
360 			printf("icmp_code=%d\n", icp->icmp_code );
361 		}
362 		return;
363 	}
364 	if( icp->icmp_id != ident )
365 		return;			/* 'Twas not our ECHO */
366 
367 	tp = (struct timeval *)&icp->icmp_data[0];
368 	printf("%d bytes from %s: ", cc,
369 		inet_ntoa(ntohl(from->sin_addr.s_addr)));
370 	printf("icmp_seq=%d. ", icp->icmp_seq );
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');
382 	nreceived++;
383 }
384 
385 
386 /*
387  *			I N _ C K S U M
388  *
389  * Checksum routine for Internet Protocol family headers (C Version)
390  *
391  */
392 in_cksum(addr, len)
393 u_short *addr;
394 int len;
395 {
396 	register int nleft = len;
397 	register u_short *w = addr;
398 	register u_short answer;
399 	register int sum = 0;
400 	u_short odd_byte = 0;
401 
402 	/*
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.
407 	 */
408 	while( nleft > 1 )  {
409 		sum += *w++;
410 		nleft -= 2;
411 	}
412 
413 	/* mop up an odd byte, if necessary */
414 	if( nleft == 1 ) {
415 		*(u_char *)(&odd_byte) = *(u_char *)w;
416 		sum += odd_byte;
417 	}
418 
419 	/*
420 	 * add back carry outs from top 16 bits to low 16 bits
421 	 */
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 */
425 	return (answer);
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  */
435 tvsub( out, in )
436 register 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  */
454 finish()
455 {
456 	printf("\n----%s PING Statistics----\n", hostname );
457 	printf("%d packets transmitted, ", ntransmitted );
458 	printf("%d packets received, ", nreceived );
459 	if (ntransmitted)
460 	    printf("%d%% packet loss",
461 		(int) (((ntransmitted-nreceived)*100) / ntransmitted ) );
462 	printf("\n");
463 	if (nreceived && timing)
464 	    printf("round-trip (ms)  min/avg/max = %d/%d/%d\n",
465 		tmin,
466 		tsum / nreceived,
467 		tmax );
468 	fflush(stdout);
469 	exit(0);
470 }
471