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