xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/ping/ping_aux.c (revision 4f364e7c95ee7fd9d5bbeddc1940e92405bb0e72)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * Portions of this source code were derived from Berkeley 4.3 BSD
32  * under license from the Regents of the University of California.
33  */
34 
35 #pragma ident	"%Z%%M%	%I%	%E% SMI"
36 
37 #include <stdio.h>
38 #include <string.h>
39 #include <strings.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <unistd.h>
43 #include <signal.h>
44 
45 #include <sys/time.h>
46 #include <sys/param.h>
47 #include <sys/socket.h>
48 #include <sys/sockio.h>
49 #include <sys/stropts.h>
50 #include <sys/file.h>
51 #include <arpa/inet.h>
52 #include <net/if.h>
53 #include <netinet/in_systm.h>
54 #include <netinet/in.h>
55 #include <netinet/ip.h>
56 #include <netinet/ip_icmp.h>
57 #include <netinet/udp.h>
58 #include <netdb.h>
59 #include <stdlib.h>
60 
61 #include <libinetutil.h>
62 #include "ping.h"
63 
64 
65 /*
66  * IPv4 source routing option.
67  * In order to avoid padding for the alignment of IPv4 addresses, ipsr_addrs
68  * is defined as a 2-D array of uint8_t, instead of 1-D array of struct in_addr.
69  */
70 struct ip_sourceroute {
71 	uint8_t ipsr_code;
72 	uint8_t ipsr_len;
73 	uint8_t ipsr_ptr;
74 	/* up to 9 IPv4 addresses */
75 	uint8_t ipsr_addrs[1][sizeof (struct in_addr)];
76 };
77 
78 void check_reply(struct addrinfo *, struct msghdr *, int, ushort_t);
79 extern void find_dstaddr(ushort_t, union any_in_addr *);
80 extern boolean_t is_a_target(struct addrinfo *, union any_in_addr *);
81 extern char *pr_name(char *, int);
82 static void pr_options(uchar_t *, int);
83 extern char *pr_protocol(int);
84 static void pr_rropt(uchar_t *, int, boolean_t);
85 static void pr_tsopt(uchar_t *, int);
86 static char *pr_type(int);
87 extern void schedule_sigalrm();
88 extern void send_scheduled_probe();
89 extern boolean_t seq_match(ushort_t, int, ushort_t);
90 extern void sigalrm_handler();
91 void set_IPv4_options(int, union any_in_addr *, int, struct in_addr *,
92     struct in_addr *);
93 extern void tvsub(struct timeval *, struct timeval *);
94 
95 /*
96  * Set IPv4 options
97  */
98 void
99 set_IPv4_options(int sock, union any_in_addr *gw_IP_list, int gw_count,
100     struct in_addr *src, struct in_addr *dst)
101 {
102 	int req_size;
103 	char srr[ROUTE_SIZE + 1];
104 	char *bufp;
105 	int optsize = ROUTE_SIZE;
106 	struct ip_sourceroute *srp;
107 	struct ip_timestamp *tsp;
108 	int i;
109 
110 	if (rr_option || ts_option || gw_count > 0) {
111 		bzero(srr, sizeof (srr));
112 		bufp = srr;
113 
114 		if (gw_count > 0) {
115 			/* 3 = 1 (code) + 1 (len) + 1 (ptr) of src route opt. */
116 			req_size = 3 + (sizeof (struct in_addr)) * gw_count;
117 
118 			if (optsize < req_size) {
119 				Fprintf(stderr, "%s: too many IPv4 gateways\n",
120 				    progname);
121 				exit(EXIT_FAILURE);
122 			}
123 
124 			srp = (struct ip_sourceroute *)bufp;
125 			srp->ipsr_code = strict ? IPOPT_SSRR : IPOPT_LSRR;
126 			srp->ipsr_len = req_size;
127 			srp->ipsr_ptr = IPOPT_MINOFF;
128 
129 			for (i = 0; i < gw_count; i++) {
130 				bcopy((char *)&gw_IP_list[i].addr,
131 				    &srp->ipsr_addrs[i],
132 				    sizeof (struct in_addr));
133 			}
134 			optsize -= srp->ipsr_len;
135 			bufp += srp->ipsr_len;
136 		}
137 		/* do we send a timestamp option? */
138 		if (ts_option) {
139 			if (optsize < IPOPT_MINOFF) {
140 				Fprintf(stderr,
141 				    "%s: no room for timestamp option\n",
142 				    progname);
143 				exit(EXIT_FAILURE);
144 			}
145 			/* LINTED */
146 			tsp = (struct ip_timestamp *)bufp;
147 			tsp->ipt_code = IPOPT_TS;
148 			tsp->ipt_len = optsize;
149 			tsp->ipt_ptr = IPOPT_MINOFF + 1;
150 			tsp->ipt_flg = ts_flag & 0x0f;
151 
152 			if (tsp->ipt_flg > IPOPT_TS_TSANDADDR) {
153 				req_size = IPOPT_MINOFF +
154 				    2 * sizeof (struct ipt_ta);
155 				/*
156 				 * Note: BSD/4.X is broken in their check so we
157 				 * have to  bump up this number by at least one.
158 				 */
159 				req_size++;
160 
161 				if (optsize < req_size) {
162 					Fprintf(stderr, "%s: no room for "
163 					    "timestamp option\n", progname);
164 					exit(EXIT_FAILURE);
165 				}
166 
167 				bcopy((char *)dst,
168 				    &tsp->ipt_timestamp.ipt_ta[0].ipt_addr,
169 				    sizeof (struct in_addr));
170 
171 				bcopy((char *)src,
172 				    &tsp->ipt_timestamp.ipt_ta[1].ipt_addr,
173 				    sizeof (struct in_addr));
174 				tsp->ipt_len = req_size;
175 
176 			}
177 			optsize -= tsp->ipt_len;
178 			bufp += tsp->ipt_len;
179 		}
180 		/* do we send a record route option? */
181 		if (rr_option) {
182 			if (optsize < IPOPT_MINOFF) {
183 				Fprintf(stderr,
184 				    "%s: no room for record route option\n",
185 				    progname);
186 				exit(EXIT_FAILURE);
187 
188 			}
189 			/*
190 			 * Format of record route option is same as source
191 			 * route option.
192 			 */
193 			srp = (struct ip_sourceroute *)bufp;
194 			srp->ipsr_code = IPOPT_RR;
195 			srp->ipsr_len = optsize;
196 			srp->ipsr_ptr = IPOPT_MINOFF;
197 
198 			optsize -= srp->ipsr_len;
199 			bufp += srp->ipsr_len;
200 		}
201 		optsize = bufp - srr;
202 		/* Round up to 4 byte boundary */
203 		if (optsize & 0x3)
204 			optsize = (optsize & ~0x3) + 4;
205 		if (setsockopt(sock, IPPROTO_IP, IP_OPTIONS, srr, optsize) <
206 		    0) {
207 			Fprintf(stderr, "%s: setsockopt IP_OPTIONS %s\n",
208 			    progname, strerror(errno));
209 			exit(EXIT_FAILURE);
210 		}
211 	}
212 }
213 
214 /*
215  * Check out the packet to see if it came from us.  This logic is necessary
216  * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
217  * which arrive ('tis only fair).  This permits multiple copies of this
218  * program to be run without having intermingled output (or statistics!).
219  */
220 void
221 check_reply(struct addrinfo *ai_dst, struct msghdr *msg, int cc,
222     ushort_t udp_src_port)
223 {
224 	struct ip *ip;
225 	struct icmp *icp;
226 	struct udphdr *up;
227 	union any_in_addr dst_addr;
228 	uchar_t *buf;
229 	int32_t *intp;
230 	struct sockaddr_in *from;
231 	struct timeval *tp;
232 	struct timeval tv;
233 	int hlen, hlen1;
234 	int64_t triptime;
235 	boolean_t valid_reply = _B_FALSE;
236 	boolean_t reply_matched_current_target;	/* Is the source address of */
237 						/* this reply same as where */
238 						/* we're sending currently? */
239 	boolean_t last_reply_from_targetaddr = _B_FALSE; /* Is this stats, */
240 						/* probe all with npackets>0 */
241 						/* and we received reply for */
242 						/* the last probe sent to */
243 						/* targetaddr */
244 	int cc_left;
245 	char tmp_buf[INET6_ADDRSTRLEN];
246 	static char *unreach[] = {
247 	    "Net Unreachable",
248 	    "Host Unreachable",
249 	    "Protocol Unreachable",
250 	    "Port Unreachable",
251 	    "Fragmentation needed and DF set",
252 	    "Source Route Failed",
253 	    /* The following are from RFC1700 */
254 	    "Net Unknown",
255 	    "Host Unknown",
256 	    "Source Host Isolated",
257 	    "Dest Net Prohibited",
258 	    "Dest Host Prohibited",
259 	    "Net Unreachable for TOS",
260 	    "Host Unreachable for TOS",
261 	    "Communication Administratively Prohibited",
262 	    "Host Precedence Violation",
263 	    "Precedence Cutoff in Effect"
264 	};
265 	static char *redirect[] = {
266 	    "Net",
267 	    "Host",
268 	    "TOS Net",
269 	    "TOS Host"
270 	};
271 	static char *timexceed[] = {
272 	    "Time exceeded in transit",
273 	    "Time exceeded during reassembly"
274 	};
275 	boolean_t print_newline = _B_FALSE;
276 	int i;
277 
278 	/* decompose msghdr into useful pieces */
279 	buf = (uchar_t *)msg->msg_iov->iov_base;
280 	from = (struct sockaddr_in *)msg->msg_name;
281 
282 	/* LINTED */
283 	intp = (int32_t *)buf;
284 
285 	(void) gettimeofday(&tv, (struct timezone *)NULL);
286 
287 	/* LINTED */
288 	ip = (struct ip *)buf;
289 	hlen = ip->ip_hl << 2;
290 
291 	if ((cc < sizeof (struct ip)) || (cc < hlen + ICMP_MINLEN)) {
292 		if (verbose) {
293 			Printf("packet too short (%d bytes) from %s\n", cc,
294 			    pr_name((char *)&from->sin_addr, AF_INET));
295 		}
296 		return;
297 	}
298 
299 	cc -= hlen;
300 	/* LINTED */
301 	icp = (struct icmp *)(buf + hlen);
302 
303 	if (ip->ip_p == 0) {
304 		/*
305 		 * Assume that we are running on a pre-4.3BSD system
306 		 * such as SunOS before 4.0
307 		 */
308 		/* LINTED */
309 		icp = (struct icmp *)buf;
310 	}
311 	cc_left = cc - ICMP_MINLEN;
312 
313 	switch (icp->icmp_type) {
314 	case ICMP_UNREACH:
315 		ip = &icp->icmp_ip;
316 		hlen1 = ip->ip_hl << 2;
317 
318 		/* check if we have enough of the packet to work on */
319 		if ((cc_left < sizeof (struct ip)) ||
320 		    (cc_left < hlen1 + sizeof (struct udphdr))) {
321 			if (verbose) {
322 				Printf("packet too short (%d bytes) from %s\n",
323 				    cc, pr_name((char *)&from->sin_addr,
324 				    AF_INET));
325 			}
326 			return;
327 		}
328 
329 		/* get the UDP packet */
330 		cc_left -= hlen1 + sizeof (struct udphdr);
331 		/* LINTED */
332 		up = (struct udphdr *)((uchar_t *)ip + hlen1);
333 
334 		/* check to see if this is what we sent */
335 		if (icp->icmp_code == ICMP_UNREACH_PORT &&
336 		    ip->ip_p == IPPROTO_UDP &&
337 		    udp_src_port == up->uh_sport &&
338 		    use_udp) {
339 			valid_reply = _B_TRUE;
340 		} else {
341 			valid_reply = _B_FALSE;
342 		}
343 
344 		if (valid_reply) {
345 			/*
346 			 * For this valid reply, if we are still sending to
347 			 * this target IP address, we'd like to do some
348 			 * updates to targetaddr, so hold SIGALRMs.
349 			 */
350 			(void) sighold(SIGALRM);
351 			is_alive = _B_TRUE;
352 			nreceived++;
353 			reply_matched_current_target =
354 			    seq_match(current_targetaddr->starting_seq_num,
355 				current_targetaddr->num_sent,
356 				ntohs(up->uh_dport));
357 			if (reply_matched_current_target) {
358 				current_targetaddr->got_reply = _B_TRUE;
359 				nreceived_last_target++;
360 				/*
361 				 * Determine if stats, probe-all, and
362 				 * npackets != 0, and this is the reply for
363 				 * the last probe we sent to current target
364 				 * address.
365 				 */
366 				if (stats && probe_all && npackets > 0 &&
367 				    ((current_targetaddr->starting_seq_num +
368 				    current_targetaddr->num_probes - 1) %
369 				    (MAX_PORT + 1) == ntohs(up->uh_dport)) &&
370 				    (current_targetaddr->num_probes ==
371 				    current_targetaddr->num_sent))
372 					last_reply_from_targetaddr = _B_TRUE;
373 			} else {
374 				/*
375 				 * If it's just probe_all and we just received
376 				 * a reply from a target address we were
377 				 * probing and had timed out (now we are probing
378 				 * some other target address), we ignore
379 				 * this reply.
380 				 */
381 				if (probe_all && !stats) {
382 					valid_reply = _B_FALSE;
383 					/*
384 					 * Only if it's verbose, we get a
385 					 * message regarding this reply,
386 					 * otherwise we are done here.
387 					 */
388 					if (!verbose) {
389 						(void) sigrelse(SIGALRM);
390 						return;
391 					}
392 				}
393 			}
394 		}
395 
396 		/* stats mode doesn't print 'alive' messages */
397 		if (valid_reply && !stats) {
398 			/*
399 			 * if we are still sending to the same target address,
400 			 * then stop it, because we know it's alive.
401 			 */
402 			if (reply_matched_current_target) {
403 				(void) alarm(0);	/* cancel alarm */
404 				(void) sigset(SIGALRM, SIG_IGN);
405 				current_targetaddr->probing_done = _B_TRUE;
406 			}
407 			(void) sigrelse(SIGALRM);
408 
409 			if (!probe_all) {
410 				Printf("%s is alive\n", targethost);
411 			} else {
412 				(void) inet_ntop(AF_INET, (void *)&ip->ip_dst,
413 				    tmp_buf, sizeof (tmp_buf));
414 
415 				if (nflag) {
416 					Printf("%s is alive\n", tmp_buf);
417 				} else {
418 					Printf("%s (%s) is alive\n",
419 					    targethost, tmp_buf);
420 				}
421 			}
422 			if (reply_matched_current_target) {
423 				/*
424 				 * Let's get things going again, but now
425 				 * ping will start sending to next target IP
426 				 * address.
427 				 */
428 				send_scheduled_probe();
429 				(void) sigset(SIGALRM, sigalrm_handler);
430 				schedule_sigalrm();
431 			}
432 			return;
433 		} else {
434 			/*
435 			 * If we are not moving to next targetaddr, let's
436 			 * release the SIGALRM now. We don't want to stall in
437 			 * the middle of probing a targetaddr if the pr_name()
438 			 * call (see below) takes longer.
439 			 */
440 			if (!last_reply_from_targetaddr)
441 				(void) sigrelse(SIGALRM);
442 			/* else, we'll release it later */
443 		}
444 
445 		dst_addr.addr = ip->ip_dst;
446 		if (valid_reply) {
447 			Printf("%d bytes from %s: ", cc,
448 			    pr_name((char *)&from->sin_addr, AF_INET));
449 			Printf("udp_port=%d. ", ntohs(up->uh_dport));
450 			print_newline = _B_TRUE;
451 		} else if (is_a_target(ai_dst, &dst_addr) || verbose) {
452 			if (icp->icmp_code >= A_CNT(unreach)) {
453 				Printf("ICMP %d Unreachable from gateway %s\n",
454 				    icp->icmp_code,
455 				    pr_name((char *)&from->sin_addr, AF_INET));
456 			} else {
457 				Printf("ICMP %s from gateway %s\n",
458 				    unreach[icp->icmp_code],
459 				    pr_name((char *)&from->sin_addr, AF_INET));
460 			}
461 			Printf(" for %s from %s", pr_protocol(ip->ip_p),
462 			    pr_name((char *)&ip->ip_src, AF_INET));
463 			Printf(" to %s", pr_name((char *)&ip->ip_dst, AF_INET));
464 			if (ip->ip_p == IPPROTO_TCP ||
465 			    ip->ip_p == IPPROTO_UDP) {
466 				Printf(" port %d ", ntohs(up->uh_dport));
467 			}
468 			print_newline = _B_TRUE;
469 		}
470 
471 		/* if we are timing and the reply has a timeval */
472 		if (valid_reply && datalen >= sizeof (struct timeval) &&
473 		    cc_left >= sizeof (struct timeval)) {
474 			/* LINTED */
475 			tp = (struct timeval *)((char *)up +
476 			    sizeof (struct udphdr));
477 			(void) tvsub(&tv, tp);
478 			triptime = (int64_t)tv.tv_sec * MICROSEC + tv.tv_usec;
479 			Printf("time=" TIMEFORMAT " ms", triptime/1000.0);
480 			tsum += triptime;
481 			tsum2 += triptime*triptime;
482 			if (triptime < tmin)
483 				tmin = triptime;
484 			if (triptime > tmax)
485 				tmax = triptime;
486 			print_newline = _B_TRUE;
487 		}
488 		if (print_newline)
489 			(void) putchar('\n');
490 		/*
491 		 * If it's stats, probe-all, npackets > 0, and we received reply
492 		 * for the last probe sent to this target address, then we
493 		 * don't need to wait anymore, let's move on to next target
494 		 * address, now!
495 		 */
496 		if (last_reply_from_targetaddr) {
497 			(void) alarm(0);	/* cancel alarm */
498 			current_targetaddr->probing_done = _B_TRUE;
499 			(void) sigrelse(SIGALRM);
500 			send_scheduled_probe();
501 			schedule_sigalrm();
502 		}
503 		break;
504 
505 	case ICMP_REDIRECT:
506 		if (cc_left < sizeof (struct ip)) {
507 			if (verbose) {
508 				Printf("packet too short (%d bytes) from %s\n",
509 				    cc, pr_name((char *)&from->sin_addr,
510 				    AF_INET));
511 			}
512 			return;
513 		}
514 
515 		ip = &icp->icmp_ip;
516 		dst_addr.addr = ip->ip_dst;
517 		if (is_a_target(ai_dst, &dst_addr) || verbose) {
518 			if (icp->icmp_code >= A_CNT(redirect)) {
519 				Printf("ICMP %d redirect from gateway %s\n",
520 				    icp->icmp_code,
521 				    pr_name((char *)&from->sin_addr, AF_INET));
522 			} else {
523 				Printf("ICMP %s redirect from gateway %s\n",
524 				    redirect[icp->icmp_code],
525 				    pr_name((char *)&from->sin_addr, AF_INET));
526 			}
527 			Printf(" to %s",
528 			    pr_name((char *)&icp->icmp_gwaddr, AF_INET));
529 			Printf(" for %s\n",
530 			    pr_name((char *)&ip->ip_dst, AF_INET));
531 		}
532 		break;
533 
534 	case ICMP_ECHOREPLY:
535 		if (ntohs(icp->icmp_id) == ident) {
536 			if (!use_udp && !use_icmp_ts)
537 				valid_reply = _B_TRUE;
538 			else
539 				valid_reply = _B_FALSE;
540 		} else {
541 			return;
542 		}
543 
544 		if (valid_reply) {
545 			/*
546 			 * For this valid reply, if we are still sending to
547 			 * this target IP address, we'd like to do some
548 			 * updates to targetaddr, so hold SIGALRMs.
549 			 */
550 			(void) sighold(SIGALRM);
551 			is_alive = _B_TRUE;
552 			nreceived++;
553 			reply_matched_current_target =
554 			    seq_match(current_targetaddr->starting_seq_num,
555 				current_targetaddr->num_sent,
556 				ntohs(icp->icmp_seq));
557 			if (reply_matched_current_target) {
558 				current_targetaddr->got_reply = _B_TRUE;
559 				nreceived_last_target++;
560 				/*
561 				 * Determine if stats, probe-all, and
562 				 * npackets != 0, and this is the reply for
563 				 * the last probe we sent to current target
564 				 * address.
565 				 */
566 				if (stats && probe_all && npackets > 0 &&
567 				    ((current_targetaddr->starting_seq_num +
568 				    current_targetaddr->num_probes - 1) %
569 				    (MAX_ICMP_SEQ + 1) ==
570 				    ntohs(icp->icmp_seq)) &&
571 				    (current_targetaddr->num_probes ==
572 				    current_targetaddr->num_sent))
573 					last_reply_from_targetaddr = _B_TRUE;
574 			} else {
575 				/*
576 				 * If it's just probe_all and we just received
577 				 * a reply from a target address we were
578 				 * probing and had timed out (now we are probing
579 				 * some other target address), we ignore
580 				 * this reply.
581 				 */
582 				if (probe_all && !stats) {
583 					valid_reply = _B_FALSE;
584 					/*
585 					 * Only if it's verbose, we get a
586 					 * message regarding this reply,
587 					 * otherwise we are done here.
588 					 */
589 					if (!verbose) {
590 						(void) sigrelse(SIGALRM);
591 						return;
592 					}
593 				}
594 			}
595 		}
596 
597 		if (!stats && valid_reply) {
598 			/*
599 			 * if we are still sending to the same target address,
600 			 * then stop it, because we know it's alive.
601 			 */
602 			if (reply_matched_current_target) {
603 				(void) alarm(0);	/* cancel alarm */
604 				(void) sigset(SIGALRM, SIG_IGN);
605 				current_targetaddr->probing_done = _B_TRUE;
606 			}
607 			(void) sigrelse(SIGALRM);
608 
609 			if (!probe_all) {
610 				Printf("%s is alive\n", targethost);
611 			} else {
612 				/*
613 				 * If we are using send_reply, the real
614 				 * target address is not the src address of the
615 				 * replies. Use icmp_seq to find out where this
616 				 * probe was sent to.
617 				 */
618 				if (send_reply) {
619 					(void) find_dstaddr(
620 					    ntohs(icp->icmp_seq), &dst_addr);
621 					(void) inet_ntop(AF_INET,
622 					    (void *)&dst_addr.addr,
623 					    tmp_buf, sizeof (tmp_buf));
624 				} else {
625 					(void) inet_ntop(AF_INET,
626 					    (void *)&from->sin_addr,
627 					    tmp_buf, sizeof (tmp_buf));
628 				}
629 				if (nflag) {
630 					Printf("%s is alive\n", tmp_buf);
631 				} else {
632 					Printf("%s (%s) is alive\n",
633 					    targethost, tmp_buf);
634 				}
635 			}
636 			if (reply_matched_current_target) {
637 				/*
638 				 * Let's get things going again, but now
639 				 * ping will start sending to next target IP
640 				 * address.
641 				 */
642 				send_scheduled_probe();
643 				(void) sigset(SIGALRM, sigalrm_handler);
644 				schedule_sigalrm();
645 			}
646 			return;
647 		} else {
648 			/*
649 			 * If we are not moving to next targetaddr, let's
650 			 * release the SIGALRM now. We don't want to stall in
651 			 * the middle of probing a targetaddr if the pr_name()
652 			 * call (see below) takes longer.
653 			 */
654 			if (!last_reply_from_targetaddr)
655 				(void) sigrelse(SIGALRM);
656 			/* else, we'll release it later */
657 		}
658 		/*
659 		 * If we are using send_reply, the real target address is
660 		 * not the src address of the replies. Use icmp_seq to find out
661 		 * where this probe was sent to.
662 		 */
663 		if (send_reply) {
664 			(void) find_dstaddr(ntohs(icp->icmp_seq), &dst_addr);
665 			Printf("%d bytes from %s: ", cc,
666 			    pr_name((char *)&dst_addr.addr,  AF_INET));
667 		} else {
668 			Printf("%d bytes from %s: ", cc,
669 			    pr_name((char *)&from->sin_addr, AF_INET));
670 		}
671 		Printf("icmp_seq=%d. ", ntohs(icp->icmp_seq));
672 
673 		if (valid_reply && datalen >= sizeof (struct timeval) &&
674 		    cc_left >= sizeof (struct timeval)) {
675 			/* LINTED */
676 			tp = (struct timeval *)&icp->icmp_data[0];
677 			(void) tvsub(&tv, tp);
678 			triptime = (int64_t)tv.tv_sec * MICROSEC + tv.tv_usec;
679 			Printf("time=" TIMEFORMAT " ms", triptime/1000.0);
680 			tsum += triptime;
681 			tsum2 += triptime*triptime;
682 			if (triptime < tmin)
683 				tmin = triptime;
684 			if (triptime > tmax)
685 				tmax = triptime;
686 		}
687 		(void) putchar('\n');
688 
689 		/*
690 		 * If it's stats, probe-all, npackets > 0, and we received reply
691 		 * for the last probe sent to this target address, then we
692 		 * don't need to wait anymore, let's move on to next target
693 		 * address, now!
694 		 */
695 		if (last_reply_from_targetaddr) {
696 			(void) alarm(0);	/* cancel alarm */
697 			current_targetaddr->probing_done = _B_TRUE;
698 			(void) sigrelse(SIGALRM);
699 			send_scheduled_probe();
700 			schedule_sigalrm();
701 		}
702 		break;
703 
704 	case ICMP_SOURCEQUENCH:
705 		if (cc_left < sizeof (struct ip)) {
706 			if (verbose) {
707 				Printf("packet too short (%d bytes) from %s\n",
708 				    cc, pr_name((char *)&from->sin_addr,
709 				    AF_INET));
710 			}
711 			return;
712 		}
713 		ip = &icp->icmp_ip;
714 		hlen1 = ip->ip_hl << 2;
715 		dst_addr.addr = ip->ip_dst;
716 		if (is_a_target(ai_dst, &dst_addr) || verbose) {
717 			Printf("ICMP Source Quench from %s\n",
718 			    pr_name((char *)&from->sin_addr, AF_INET));
719 			Printf(" for %s from %s", pr_protocol(ip->ip_p),
720 			    pr_name((char *)&ip->ip_src, AF_INET));
721 			Printf(" to %s", pr_name((char *)&ip->ip_dst, AF_INET));
722 
723 			/*
724 			 * if it's a UDP or TCP packet, we need at least first
725 			 * 4 bytes of it to see the src/dst ports
726 			 */
727 			if ((ip->ip_p == IPPROTO_TCP ||
728 			    ip->ip_p == IPPROTO_UDP) &&
729 			    (cc_left >= hlen1 + 4)) {
730 				/* LINTED */
731 				up = (struct udphdr *)((uchar_t *)ip + hlen1);
732 				Printf(" port %d", ntohs(up->uh_dport));
733 			}
734 			(void) putchar('\n');
735 		}
736 		break;
737 
738 	case ICMP_PARAMPROB:
739 		if (cc_left < sizeof (struct ip)) {
740 			if (verbose) {
741 				Printf("packet too short (%d bytes) from %s\n",
742 				    cc, pr_name((char *)&from->sin_addr,
743 				    AF_INET));
744 			}
745 			return;
746 		}
747 		ip = &icp->icmp_ip;
748 		hlen1 = ip->ip_hl << 2;
749 		dst_addr.addr = ip->ip_dst;
750 		if (is_a_target(ai_dst, &dst_addr) || verbose) {
751 			switch (icp->icmp_code) {
752 			case ICMP_PARAMPROB_OPTABSENT:
753 				Printf("ICMP Missing a Required Option "
754 				    "parameter problem from %s\n",
755 				    pr_name((char *)&from->sin_addr, AF_INET));
756 				Printf(" option type = %d", icp->icmp_pptr);
757 				break;
758 			case ICMP_PARAMPROB_BADLENGTH:
759 				Printf("ICMP Bad Length parameter problem "
760 				    "from %s\n",
761 				    pr_name((char *)&from->sin_addr, AF_INET));
762 				Printf(" in byte %d", icp->icmp_pptr);
763 				if (icp->icmp_pptr <= hlen1) {
764 					Printf(" (value 0x%x)",
765 					    *((char *)ip + icp->icmp_pptr));
766 				}
767 				break;
768 			case 0:
769 			default:
770 				Printf("ICMP Parameter Problem from %s\n",
771 				    pr_name((char *)&from->sin_addr, AF_INET));
772 				Printf(" in byte %d", icp->icmp_pptr);
773 				if (icp->icmp_pptr <= hlen1) {
774 					Printf(" (value 0x%x)",
775 					    *((char *)ip + icp->icmp_pptr));
776 				}
777 				break;
778 			}
779 
780 			Printf(" for %s from %s", pr_protocol(ip->ip_p),
781 			    pr_name((char *)&ip->ip_src, AF_INET));
782 			Printf(" to %s", pr_name((char *)&ip->ip_dst, AF_INET));
783 
784 			/*
785 			 * if it's a UDP or TCP packet, we need at least first
786 			 * 4 bytes of it to see the src/dst ports
787 			 */
788 			if ((ip->ip_p == IPPROTO_TCP ||
789 			    ip->ip_p == IPPROTO_UDP) &&
790 			    (cc_left >= hlen1 + 4)) {
791 				/* LINTED */
792 				up = (struct udphdr *)((uchar_t *)ip + hlen1);
793 				Printf(" port %d", ntohs(up->uh_dport));
794 			}
795 			(void) putchar('\n');
796 		}
797 		break;
798 
799 	case ICMP_TIMXCEED:
800 		if (cc_left < sizeof (struct ip)) {
801 			if (verbose) {
802 				Printf("packet too short (%d bytes) from %s\n",
803 				    cc, pr_name((char *)&from->sin_addr,
804 				    AF_INET));
805 			}
806 			return;
807 		}
808 		ip = &icp->icmp_ip;
809 		hlen1 = ip->ip_hl << 2;
810 		dst_addr.addr = ip->ip_dst;
811 		if (is_a_target(ai_dst, &dst_addr) || verbose) {
812 			if (icp->icmp_code >= A_CNT(timexceed)) {
813 				Printf("ICMP %d time exceeded from %s\n",
814 				    icp->icmp_code,
815 				    pr_name((char *)&from->sin_addr, AF_INET));
816 			} else {
817 				Printf("ICMP %s from %s\n",
818 				    timexceed[icp->icmp_code],
819 				    pr_name((char *)&from->sin_addr, AF_INET));
820 			}
821 			Printf(" for %s from %s", pr_protocol(ip->ip_p),
822 			    pr_name((char *)&ip->ip_src, AF_INET));
823 			Printf(" to %s", pr_name((char *)&ip->ip_dst, AF_INET));
824 			if ((ip->ip_p == IPPROTO_TCP ||
825 			    ip->ip_p == IPPROTO_UDP) &&
826 			    (cc_left >= hlen1 + 4)) {
827 				/* LINTED */
828 				up = (struct udphdr *)((uchar_t *)ip + hlen1);
829 				Printf(" port %d", ntohs(up->uh_dport));
830 			}
831 			(void) putchar('\n');
832 		}
833 		break;
834 
835 	case ICMP_TSTAMPREPLY:
836 		/* the packet should have enough space to store timestamps */
837 		if (cc_left < sizeof (struct id_ts)) {
838 			if (verbose) {
839 				Printf("packet too short (%d bytes) from %s\n",
840 				    cc, pr_name((char *)&from->sin_addr,
841 				    AF_INET));
842 			}
843 			return;
844 		}
845 
846 		if (ntohs(icp->icmp_id) == ident) {
847 			if (use_icmp_ts)
848 				valid_reply = _B_TRUE;
849 			else
850 				valid_reply = _B_FALSE;
851 		} else {
852 			return;
853 		}
854 
855 		if (valid_reply) {
856 			/*
857 			 * For this valid reply, if we are still sending to
858 			 * this target IP address, we'd like to do some
859 			 * updates to targetaddr, so hold SIGALRMs.
860 			 */
861 			(void) sighold(SIGALRM);
862 			is_alive = _B_TRUE;
863 			nreceived++;
864 			reply_matched_current_target =
865 			    seq_match(current_targetaddr->starting_seq_num,
866 				current_targetaddr->num_sent,
867 				ntohs(icp->icmp_seq));
868 			if (reply_matched_current_target) {
869 				current_targetaddr->got_reply = _B_TRUE;
870 				nreceived_last_target++;
871 				/*
872 				 * Determine if stats, probe-all, and
873 				 * npackets != 0, and this is the reply for
874 				 * the last probe we sent to current target
875 				 * address.
876 				 */
877 				if (stats && probe_all && npackets > 0 &&
878 				    ((current_targetaddr->starting_seq_num +
879 				    current_targetaddr->num_probes - 1) %
880 				    (MAX_ICMP_SEQ + 1) ==
881 				    ntohs(icp->icmp_seq)) &&
882 				    (current_targetaddr->num_probes ==
883 				    current_targetaddr->num_sent))
884 					last_reply_from_targetaddr = _B_TRUE;
885 			} else {
886 				/*
887 				 * If it's just probe_all and we just received
888 				 * a reply from a target address we were
889 				 * probing and had timed out (now we are probing
890 				 * some other target address), we ignore
891 				 * this reply.
892 				 */
893 				if (probe_all && !stats) {
894 					valid_reply = _B_FALSE;
895 					/*
896 					 * Only if it's verbose, we get a
897 					 * message regarding this reply,
898 					 * otherwise we are done here.
899 					 */
900 					if (!verbose) {
901 						(void) sigrelse(SIGALRM);
902 						return;
903 					}
904 				}
905 			}
906 		}
907 
908 		if (!stats && valid_reply) {
909 			/*
910 			 * if we are still sending to the same target address,
911 			 * then stop it, because we know it's alive.
912 			 */
913 			if (reply_matched_current_target) {
914 				(void) alarm(0);	/* cancel alarm */
915 				(void) sigset(SIGALRM, SIG_IGN);
916 				current_targetaddr->probing_done = _B_TRUE;
917 			}
918 			(void) sigrelse(SIGALRM);
919 
920 			if (!probe_all) {
921 				Printf("%s is alive\n", targethost);
922 			} else {
923 				/*
924 				 * If we are using send_reply, the real
925 				 * target address is not the src address of the
926 				 * replies. Use icmp_seq to find out where this
927 				 * probe was sent to.
928 				 */
929 				if (send_reply) {
930 					(void) find_dstaddr(
931 					    ntohs(icp->icmp_seq), &dst_addr);
932 					(void) inet_ntop(AF_INET,
933 					    (void *)&dst_addr.addr,
934 					    tmp_buf, sizeof (tmp_buf));
935 				} else {
936 					(void) inet_ntop(AF_INET,
937 					    (void *)&from->sin_addr,
938 					    tmp_buf, sizeof (tmp_buf));
939 				}
940 				if (nflag) {
941 					Printf("%s is alive\n", tmp_buf);
942 				} else {
943 					Printf("%s (%s) is alive\n",
944 					    targethost, tmp_buf);
945 				}
946 			}
947 			if (reply_matched_current_target) {
948 				/*
949 				 * Let's get things going again, but now
950 				 * ping will start sending to next target IP
951 				 * address.
952 				 */
953 				send_scheduled_probe();
954 				(void) sigset(SIGALRM, sigalrm_handler);
955 				schedule_sigalrm();
956 			}
957 			return;
958 		} else {
959 			/*
960 			 * If we are not moving to next targetaddr, let's
961 			 * release the SIGALRM now. We don't want to stall in
962 			 * the middle of probing a targetaddr if the pr_name()
963 			 * call (see below) takes longer.
964 			 */
965 			if (!last_reply_from_targetaddr)
966 				(void) sigrelse(SIGALRM);
967 			/* else, we'll release it later */
968 		}
969 
970 		/*
971 		 * If we are using send_reply, the real target address is
972 		 * not the src address of the replies. Use icmp_seq to find out
973 		 * where this probe was sent to.
974 		 */
975 		if (send_reply) {
976 			(void) find_dstaddr(ntohs(icp->icmp_seq), &dst_addr);
977 			Printf("%d bytes from %s: ", cc,
978 			    pr_name((char *)&dst_addr.addr,  AF_INET));
979 		} else {
980 			Printf("%d bytes from %s: ", cc,
981 			    pr_name((char *)&from->sin_addr, AF_INET));
982 		}
983 		Printf("icmp_seq=%d. ", ntohs(icp->icmp_seq));
984 		Printf("orig = %lu, recv = %lu, xmit = %lu ",
985 		    (ulong_t)ntohl(icp->icmp_otime),
986 		    (ulong_t)ntohl(icp->icmp_rtime),
987 		    (ulong_t)ntohl(icp->icmp_ttime));
988 
989 		if (valid_reply) {
990 			/*
991 			 * icp->icmp_otime is the time passed since midnight.
992 			 * Therefore we need to adjust tv value, which is
993 			 * the time passed since Jan 1, 1970.
994 			 */
995 			triptime = (tv.tv_sec % (24LL * 60 * 60)) * MILLISEC +
996 			    (tv.tv_usec / (MICROSEC/MILLISEC));
997 			triptime -= ntohl(icp->icmp_otime);
998 			if (triptime < 0)
999 				triptime += 24LL * 60 * 60 * MILLISEC;
1000 
1001 			Printf("time=%d. ms", (int)triptime);
1002 			triptime *= (MICROSEC/MILLISEC);
1003 			tsum += triptime;
1004 			tsum2 += triptime*triptime;
1005 			if (triptime < tmin)
1006 				tmin = triptime;
1007 			if (triptime > tmax)
1008 				tmax = triptime;
1009 		}
1010 		(void) putchar('\n');
1011 		/*
1012 		 * If it's stats, probe-all, npackets > 0, and we received reply
1013 		 * for the last probe sent to this target address, then we
1014 		 * don't need to wait anymore, let's move on to next target
1015 		 * address, now!
1016 		 */
1017 		if (last_reply_from_targetaddr) {
1018 			(void) alarm(0);	/* cancel alarm */
1019 			current_targetaddr->probing_done = _B_TRUE;
1020 			(void) sigrelse(SIGALRM);
1021 			send_scheduled_probe();
1022 			schedule_sigalrm();
1023 		}
1024 		break;
1025 	case ICMP_ROUTERADVERT:
1026 	case ICMP_ROUTERSOLICIT:
1027 		/* Router discovery messages */
1028 		return;
1029 
1030 	case ICMP_ECHO:
1031 	case ICMP_TSTAMP:
1032 	case ICMP_IREQ:
1033 	case ICMP_MASKREQ:
1034 		/* These were never passed out from the SunOS 4.X kernel. */
1035 		return;
1036 
1037 	case ICMP_IREQREPLY:
1038 	case ICMP_MASKREPLY:
1039 		/* Replies for information and address mask requests */
1040 		return;
1041 
1042 	default:
1043 		if (verbose) {
1044 			Printf("%d bytes from %s:\n", cc,
1045 			    pr_name((char *)&from->sin_addr, AF_INET));
1046 			Printf("icmp_type=%d (%s) ",
1047 			    icp->icmp_type, pr_type(icp->icmp_type));
1048 			Printf("icmp_code=%d\n", icp->icmp_code);
1049 			for (i = 0; i < 12; i++) {
1050 				Printf("x%2.2x: x%8.8x\n",
1051 				    i * sizeof (int32_t), *intp++);
1052 			}
1053 		}
1054 		break;
1055 	}
1056 
1057 	buf += sizeof (struct ip);
1058 	hlen -= sizeof (struct ip);
1059 
1060 	/* if verbose and there exists IP options */
1061 	if (verbose && hlen > 0)
1062 		pr_options((uchar_t *)buf, hlen);
1063 }
1064 
1065 /*
1066  * Print out the ip options.
1067  */
1068 static void
1069 pr_options(uchar_t *opt, int optlength)
1070 {
1071 	int curlength;
1072 
1073 	Printf("  IP options: ");
1074 	while (optlength > 0) {
1075 		curlength = opt[1];
1076 		switch (*opt) {
1077 		case IPOPT_EOL:
1078 			optlength = 0;
1079 			break;
1080 
1081 		case IPOPT_NOP:
1082 			opt++;
1083 			optlength--;
1084 			continue;
1085 
1086 		case IPOPT_RR:
1087 			Printf(" <record route> ");
1088 			pr_rropt(opt, curlength, _B_TRUE);
1089 			break;
1090 
1091 		case IPOPT_TS:
1092 			Printf(" <time stamp> ");
1093 			pr_tsopt(opt, curlength);
1094 			break;
1095 
1096 		case IPOPT_SECURITY:
1097 			Printf(" <security>");
1098 			break;
1099 
1100 		case IPOPT_LSRR:
1101 			Printf(" <loose source route> ");
1102 			pr_rropt(opt, curlength, _B_FALSE);
1103 			break;
1104 
1105 		case IPOPT_SATID:
1106 			Printf(" <stream id>");
1107 			break;
1108 
1109 		case IPOPT_SSRR:
1110 			Printf(" <strict source route> ");
1111 			pr_rropt(opt, curlength, _B_FALSE);
1112 			break;
1113 
1114 		default:
1115 			Printf(" <option %d, len %d>", *opt, curlength);
1116 			break;
1117 		}
1118 		/*
1119 		 * Following most options comes a length field
1120 		 */
1121 		opt += curlength;
1122 		optlength -= curlength;
1123 	}
1124 	(void) putchar('\n');
1125 }
1126 
1127 /*
1128  * Print out a recorded route option. If rrflag is _B_TRUE, it prints record
1129  * route option, otherwise LSRR/SSRR.
1130  */
1131 static void
1132 pr_rropt(uchar_t *opt, int length, boolean_t rrflag)
1133 {
1134 	struct ip_sourceroute *rrp;
1135 	int sr_index = 0;
1136 	struct in_addr addr;
1137 
1138 	rrp = (struct ip_sourceroute *)opt;
1139 
1140 	/* data starts at offset 3 */
1141 	length -= 3;
1142 	while (length > 0) {
1143 		/*
1144 		 * Let's see if we are examining the addr pointed by ipsr_ptr
1145 		 */
1146 		if ((rrp->ipsr_ptr == (sr_index + 1) * sizeof (addr)) &&
1147 		    rrflag) {
1148 			Printf(" (End of record)");
1149 			break;
1150 		}
1151 
1152 		bcopy(&rrp->ipsr_addrs[sr_index], &addr, sizeof (addr));
1153 		Printf("%s", pr_name((char *)&addr, AF_INET));
1154 
1155 		if (rrp->ipsr_ptr == (sr_index + 1) * sizeof (addr)) {
1156 			Printf("(Current)");
1157 		}
1158 
1159 		sr_index++;
1160 
1161 		length -= sizeof (addr);
1162 		if (length > 0)
1163 			Printf(", ");
1164 	}
1165 }
1166 
1167 /*
1168  * Print out a timestamp option.
1169  */
1170 static void
1171 pr_tsopt(uchar_t *opt, int length)
1172 {
1173 	boolean_t address_present;
1174 	boolean_t rrflag;		/* End at current entry? */
1175 	struct ip_timestamp *tsp;
1176 	int ts_index = 0;
1177 	struct in_addr addr;
1178 	size_t data_len;
1179 	int32_t time;
1180 
1181 	/* LINTED */
1182 	tsp = (struct ip_timestamp *)opt;
1183 
1184 	switch (tsp->ipt_flg) {
1185 	case IPOPT_TS_TSONLY:
1186 		address_present = _B_FALSE;
1187 		data_len = sizeof (tsp->ipt_timestamp.ipt_time[0]);
1188 		rrflag = _B_TRUE;
1189 		break;
1190 	case IPOPT_TS_TSANDADDR:
1191 		address_present = _B_TRUE;
1192 		data_len = sizeof (tsp->ipt_timestamp.ipt_ta[0]);
1193 		rrflag = _B_TRUE;
1194 		break;
1195 	case IPOPT_TS_PRESPEC:
1196 	case 3:
1197 		address_present = _B_TRUE;
1198 		data_len = sizeof (tsp->ipt_timestamp.ipt_ta[0]);
1199 		rrflag = _B_FALSE;
1200 		break;
1201 	default:
1202 		Printf("(Bad flag value: 0x%x)", tsp->ipt_flg);
1203 		return;
1204 	}
1205 	if (tsp->ipt_oflw > 0)
1206 		Printf("(Overflow: %d) ", tsp->ipt_oflw);
1207 
1208 	/* data starts at offset 4 */
1209 	length -= 4;
1210 
1211 	while (length > 0) {
1212 		if (length < data_len)
1213 			break;
1214 
1215 		/* the minimum value of ipt_ptr is 5 */
1216 		if ((tsp->ipt_ptr == ts_index * data_len + 5) && rrflag) {
1217 			Printf(" (End of record)");
1218 			break;
1219 		}
1220 		if (address_present) {
1221 			bcopy(&tsp->ipt_timestamp.ipt_ta[ts_index].ipt_addr,
1222 			    &addr, sizeof (addr));
1223 			Printf("%s: ", pr_name((char *)&addr, AF_INET));
1224 			bcopy(&tsp->ipt_timestamp.ipt_ta[ts_index].ipt_time,
1225 			    &time, sizeof (time));
1226 		} else {
1227 			bcopy(&tsp->ipt_timestamp.ipt_time[ts_index],
1228 			    &time, sizeof (time));
1229 		}
1230 		Printf("%d", ntohl(time));
1231 
1232 		if (tsp->ipt_ptr == ts_index * data_len + 5)
1233 			Printf("(Current)");
1234 
1235 		ts_index++;
1236 		length -= data_len;
1237 		if (length > 0)
1238 			Printf(", ");
1239 	}
1240 }
1241 
1242 /*
1243  * Convert an ICMP "type" field to a printable string.
1244  */
1245 static char *
1246 pr_type(int icmp_type)
1247 {
1248 	static struct icmptype_table ttab[] = {
1249 		{ICMP_ECHOREPLY,	"Echo Reply"},
1250 		{1,			"ICMP 1"},
1251 		{2,			"ICMP 2"},
1252 		{ICMP_UNREACH,		"Dest Unreachable"},
1253 		{ICMP_SOURCEQUENCH,	"Source Quench"},
1254 		{ICMP_REDIRECT,		"Redirect"},
1255 		{6,			"ICMP 6"},
1256 		{7,			"ICMP 7"},
1257 		{ICMP_ECHO,		"Echo"},
1258 		{ICMP_ROUTERADVERT,	"Router Advertisement"},
1259 		{ICMP_ROUTERSOLICIT,	"Router Solicitation"},
1260 		{ICMP_TIMXCEED,		"Time Exceeded"},
1261 		{ICMP_PARAMPROB,	"Parameter Problem"},
1262 		{ICMP_TSTAMP,		"Timestamp"},
1263 		{ICMP_TSTAMPREPLY,	"Timestamp Reply"},
1264 		{ICMP_IREQ,		"Info Request"},
1265 		{ICMP_IREQREPLY,	"Info Reply"},
1266 		{ICMP_MASKREQ,		"Netmask Request"},
1267 		{ICMP_MASKREPLY,	"Netmask Reply"}
1268 	};
1269 	int i;
1270 
1271 	for (i = 0; i < A_CNT(ttab); i++) {
1272 		if (ttab[i].type == icmp_type)
1273 			return (ttab[i].message);
1274 	}
1275 
1276 	return ("OUT-OF-RANGE");
1277 }
1278