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