Commit | Line | Data |
---|---|---|
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 | ||
38 | char ttyobuf[4096]; | |
39 | ||
40 | u_char packet[1024]; | |
41 | int options; | |
42 | extern int errno; | |
43 | ||
44 | int s; /* Socket file descriptor */ | |
45 | struct hostent *hp; /* Pointer to host info */ | |
46 | struct timezone tz; /* leftover */ | |
47 | ||
48 | struct sockaddr whereto;/* Who to ping */ | |
49 | int datalen; /* How much data */ | |
50 | ||
51 | char usage[] = "Usage: ping [-d] host [data size]\n"; | |
52 | ||
53 | char *hostname; | |
54 | char hnamebuf[64]; | |
55 | ||
56 | int ntransmitted = 1; /* sequence # for outbound packets = #sent */ | |
57 | int ident; | |
58 | ||
59 | int nreceived = 0; /* # of packets we got back */ | |
60 | int tmin = 999999999; | |
61 | int tmax = 0; | |
62 | int tsum = 0; /* sum of all times, for doing average */ | |
63 | int finish(); | |
64 | ||
65 | /* | |
66 | * M A I N | |
67 | */ | |
68 | main(argc, argv) | |
69 | char *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 | */ | |
150 | catcher() | |
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 | */ | |
166 | pinger() | |
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 | */ | |
206 | char * | |
207 | pr_type( t ) | |
208 | register 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 | */ | |
244 | pr_pack( icp, cc, from ) | |
245 | register struct icmp *icp; | |
246 | int cc; | |
247 | struct 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 | */ | |
292 | in_cksum(addr, len) | |
293 | u_short *addr; | |
294 | int 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 | */ | |
374 | tvsub( out, in ) | |
375 | register 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 | */ | |
393 | finish() | |
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 |