from Mike Muus
[unix-history] / usr / src / sbin / ping / ping.c
CommitLineData
3310aa34
MK
1/*
2 * P I N G . C
3 *
4 * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
5 * measure round-trip-delays and packet loss across network paths.
6 *
7 * Author -
8 * Mike Muuss
9 * U. S. Army Ballistic Research Laboratory
10 * December, 1983
11 *
12 * Target System -
13 * 4.2 BSD with MIT and BRL fixes to /sys/netinet/ip_icmp.c et.al.
14 *
15 * Status -
16 * Public Domain. Distribution Unlimited.
17 *
18 * Bugs -
19 * Divide by zero if no packets return.
20 * More statistics could always be gathered.
21 * This program has to run SUID to ROOT to access the ICMP socket.
22 */
23
24#include <stdio.h>
25#include <errno.h>
26#include <sys/time.h>
27
28#include <sys/param.h>
29#include <sys/socket.h>
30#include <sys/file.h>
31
32#include <netinet/in_systm.h>
33#include <netinet/in.h>
34#include <netinet/ip.h>
35#include <netinet/ip_icmp.h>
36#include <netdb.h>
37
38char ttyobuf[4096];
39
40u_char packet[1024];
41int options;
42extern int errno;
43
44int s; /* Socket file descriptor */
45struct hostent *hp; /* Pointer to host info */
46struct timezone tz; /* leftover */
47
48struct sockaddr whereto;/* Who to ping */
49int datalen; /* How much data */
50
51char usage[] = "Usage: ping [-d] host [data size]\n";
52
53char *hostname;
54char hnamebuf[64];
55
56int ntransmitted = 1; /* sequence # for outbound packets = #sent */
57int ident;
58
59int nreceived = 0; /* # of packets we got back */
60int tmin = 999999999;
61int tmax = 0;
62int tsum = 0; /* sum of all times, for doing average */
63int finish();
64
65/*
66 * M A I N
67 */
68main(argc, argv)
69char *argv[];
70{
71 struct sockaddr_in from;
72 char **av = argv;
73 struct sockaddr_in *to = (struct sockaddr_in *) &whereto;
74
75 if (argc > 0 && !strcmp(argv[0], "-d")) {
76 options |= SO_DEBUG;
77 argc--, av++;
78 }
79
80 if( argc < 2 || argc > 3 ) {
81 printf(usage);
82 exit(1);
83 }
84
85 bzero( (char *)&whereto, sizeof(struct sockaddr) );
86 hp = gethostbyname(av[1]);
87 if (hp) {
88 to->sin_family = hp->h_addrtype;
89 bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
90 hostname = hp->h_name;
91 } else {
92 to->sin_family = AF_INET;
93 to->sin_addr.s_addr = inet_addr(av[1]);
94 if (to->sin_addr.s_addr == -1) {
95 printf("%s: unknown host %s\n", argv[0], av[1]);
96 return;
97 }
98 strcpy(hnamebuf, argv[1]);
99 hostname = hnamebuf;
100 }
101
102 if( argc == 3 )
103 datalen = atoi( argv[2] );
104 else
105 datalen = 64-8;
106
107 ident = getpid() & 0xFFFF;
108
109 while ((s = socket(AF_INET, SOCK_RAW, 0, 0)) < 0) {
110 perror("ping: socket");
111 sleep(5);
112 }
113
114 printf("PING %s: %d data bytes\n", hostname, datalen );
115
116 setbuffer( stdout, ttyobuf, sizeof(ttyobuf) );
117
118 signal( SIGINT, finish );
119
120 catcher(); /* start things going */
121
122 for (;;) {
123 int len = sizeof (packet);
124 int fromlen = sizeof (from);
125 int cc;
126
127 /* cc = recvfrom(s, buf, len, flags, from, fromlen) */
128 if ( (cc=recvfrom(s, packet, len, 0, &from, fromlen)) < 0) {
129 if( errno == EINTR )
130 continue;
131 perror("ping: recvfrom");
132 continue;
133 }
134 pr_pack( packet, cc, &from );
135 }
136 /*NOTREACHED*/
137}
138
139/*
140 * C A T C H E R
141 *
142 * This routine causes another PING to be transmitted, and then
143 * schedules another SIGALRM for 1 second from now.
144 *
145 * Bug -
146 * Our sense of time will slowly skew (ie, packets will not be launched
147 * exactly at 1-second intervals). This does not affect the quality
148 * of the delay and loss statistics.
149 */
150catcher()
151{
152 signal( SIGALRM, catcher );
153 pinger();
154 alarm(1);
155}
156
157/*
158 * P I N G E R
159 *
160 * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet
161 * will be added on by the kernel. The ID field is our UNIX process ID,
162 * and the sequence number is an ascending integer. The first 8 bytes
163 * of the data portion are used to hold a UNIX "timeval" struct in VAX
164 * byte-order, to compute the round-trip time.
165 */
166pinger()
167{
168 static u_char outpack[1024];
169 register struct icmp *icp = (struct icmp *) outpack;
170 int i, cc;
171 register struct timeval *tp = (struct timeval *) &outpack[8];
172 register u_char *datap = &outpack[8+sizeof(struct timeval)];
173
174 icp->icmp_type = ICMP_ECHO;
175 icp->icmp_code = 0;
176 icp->icmp_cksum = 0;
177 icp->icmp_seq = ntransmitted++;
178 icp->icmp_id = ident; /* ID */
179
180 cc = datalen+8; /* skips ICMP portion */
181
182 gettimeofday( tp, &tz );
183
184 for( i=8; i<datalen; i++) /* skip 8 for time */
185 *datap++ = i;
186
187 /* Compute ICMP checksum here */
188 icp->icmp_cksum = in_cksum( icp, cc );
189
190 /* cc = sendto(s, msg, len, flags, to, tolen) */
191 i = sendto( s, outpack, cc, 0, &whereto, sizeof(struct sockaddr) );
192
193 if( i < 0 || i != cc ) {
194 if( i<0 ) perror("sendto");
195 printf("ping: wrote %s %d chars, ret=%d\n",
196 hostname, cc, i );
197 fflush(stdout);
198 }
199}
200
201/*
202 * P R _ T Y P E
203 *
204 * Convert an ICMP "type" field to a printable string.
205 */
206char *
207pr_type( t )
208register int t;
209{
210 static char *ttab[] = {
211 "Echo Reply",
212 "ICMP 1",
213 "ICMP 2",
214 "Dest Unreachable",
215 "Source Quence",
216 "Redirect",
217 "ICMP 6",
218 "ICMP 7",
219 "Echo",
220 "ICMP 9",
221 "ICMP 10",
222 "Time Exceeded",
223 "Parameter Problem",
224 "Timestamp",
225 "Timestamp Reply",
226 "Info Request",
227 "Info Reply"
228 };
229
230 if( t < 0 || t > 16 )
231 return("OUT-OF-RANGE");
232
233 return(ttab[t]);
234}
235
236/*
237 * P R _ P A C K
238 *
239 * Print out the packet, if it came from us. This logic is necessary
240 * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
241 * which arrive ('tis only fair). This permits multiple copies of this
242 * program to be run without having intermingled output (or statistics!).
243 */
244pr_pack( icp, cc, from )
245register struct icmp *icp;
246int cc;
247struct sockaddr_in *from;
248{
249 register long *lp = (long *) packet;
250 register int i;
251 struct timeval tv;
252 struct timeval *tp = (struct timeval *) &packet[8];
253 int triptime;
254
255 from->sin_addr.s_addr = ntohl( from->sin_addr.s_addr );
256 gettimeofday( &tv, &tz );
257
258 if( icp->icmp_type != ICMP_ECHOREPLY ) {
259 printf("%d bytes from x%x: ", cc, from->sin_addr.s_addr);
260 printf("icmp_type=%d (%s)\n",
261 icp->icmp_type, pr_type(icp->icmp_type) );
262 for( i=0; i<12; i++)
263 printf("x%2.2x: x%8.8x\n", i*sizeof(long), *lp++ );
264 printf("icmp_code=%d\n", icp->icmp_code );
265 }
266 if( icp->icmp_id != ident )
267 return; /* 'Twas not our ECHO */
268
269 printf("%d bytes from x%x: ", cc, from->sin_addr.s_addr);
270 printf("icmp_seq=%d. ", icp->icmp_seq );
271 tvsub( &tv, tp );
272 triptime = tv.tv_sec*1000+(tv.tv_usec/1000);
273 printf("time=%d. ms\n", triptime );
274 nreceived++;
275 tsum += triptime;
276 if( triptime < tmin )
277 tmin = triptime;
278 if( triptime > tmax )
279 tmax = triptime;
280 fflush(stdout);
281}
282
283
284/*
285 * I N _ C K S U M
286 *
287 * Checksum routine for Internet Protocol family headers (VAX Version).
288 *
289 * Shamelessly pilfered from /sys/vax/in_cksum.c, with all the MBUF stuff
290 * ripped out.
291 */
292in_cksum(addr, len)
293u_short *addr;
294int len;
295{
296 register int nleft = len; /* on vax, (user mode), r11 */
297 register int xxx; /* on vax, (user mode), r10 */
298 register u_short *w = addr; /* on vax, known to be r9 */
299 register int sum = 0; /* on vax, known to be r8 */
300
301
302 /*
303 * Force to long boundary so we do longword aligned
304 * memory operations. It is too hard to do byte
305 * adjustment, do only word adjustment.
306 */
307 if (((int)w&0x2) && nleft >= 2) {
308 sum += *w++;
309 nleft -= 2;
310 }
311 /*
312 * Do as much of the checksum as possible 32 bits at at time.
313 * In fact, this loop is unrolled to make overhead from
314 * branches &c small.
315 *
316 * We can do a 16 bit ones complement sum 32 bits at a time
317 * because the 32 bit register is acting as two 16 bit
318 * registers for adding, with carries from the low added
319 * into the high (by normal carry-chaining) and carries
320 * from the high carried into the low on the next word
321 * by use of the adwc instruction. This lets us run
322 * this loop at almost memory speed.
323 *
324 * Here there is the danger of high order carry out, and
325 * we carefully use adwc.
326 */
327 while ((nleft -= 32) >= 0) {
328#undef ADD
329 asm("clrl r0"); /* clears carry */
330#define ADD asm("adwc (r9)+,r8;");
331 ADD; ADD; ADD; ADD; ADD; ADD; ADD; ADD;
332 asm("adwc $0,r8");
333 }
334 nleft += 32;
335 while ((nleft -= 8) >= 0) {
336 asm("clrl r0");
337 ADD; ADD;
338 asm("adwc $0,r8");
339 }
340 nleft += 8;
341 /*
342 * Now eliminate the possibility of carry-out's by
343 * folding back to a 16 bit number (adding high and
344 * low parts together.) Then mop up trailing words
345 * and maybe an odd byte.
346 */
347 { asm("ashl $-16,r8,r0; addw2 r0,r8");
348 asm("adwc $0,r8; movzwl r8,r8"); }
349 while ((nleft -= 2) >= 0) {
350 asm("movzwl (r9)+,r0; addl2 r0,r8");
351 }
352 if (nleft == -1) {
353 sum += *(u_char *)w;
354 }
355
356 /*
357 * Add together high and low parts of sum
358 * and carry to get cksum.
359 * Have to be careful to not drop the last
360 * carry here.
361 */
362 { asm("ashl $-16,r8,r0; addw2 r0,r8; adwc $0,r8");
363 asm("mcoml r8,r8; movzwl r8,r8"); }
364 return (sum);
365}
366
367/*
368 * T V S U B
369 *
370 * Subtract 2 timeval structs: out = out - in.
371 *
372 * Out is assumed to be >= in.
373 */
374tvsub( out, in )
375register struct timeval *out, *in;
376{
377 if( (out->tv_usec -= in->tv_usec) < 0 ) {
378 out->tv_sec--;
379 out->tv_usec += 1000000;
380 }
381 out->tv_sec -= in->tv_sec;
382}
383
384/*
385 * F I N I S H
386 *
387 * Print out statistics, and give up.
388 * Heavily buffered STDIO is used here, so that all the statistics
389 * will be written with 1 sys-write call. This is nice when more
390 * than one copy of the program is running on a terminal; it prevents
391 * the statistics output from becomming intermingled.
392 */
393finish()
394{
395 ntransmitted--; /* we will never hear the last one */
396
397 printf("\n----%s PING Statistics----\n", hostname );
398 printf("%d packets transmitted, ", ntransmitted );
399 printf("%d packets received, ", nreceived );
400 printf("%d%% packet loss\n",
401 (int) (((ntransmitted-nreceived)*100) / ntransmitted ) );
402 printf("round-trip (ms) min/avg/max = %d/%d/%d\n",
403 tmin,
404 tsum / nreceived,
405 tmax );
406 fflush(stdout);
407 exit(0);
408}
409