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