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