|
|
1.1 root 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) 6/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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.