xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/ping/ping_aux6.c (revision 3fe455549728ac525df3be56130ad8e075d645d7)
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  * Copyright 2024 Bill Sommerfeld <sommerfeld@hamachi.org>
33  */
34 
35 /*
36  * Portions of this source code were derived from Berkeley 4.3 BSD
37  * under license from the Regents of the University of California.
38  */
39 
40 #include <stdio.h>
41 #include <string.h>
42 #include <strings.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <signal.h>
47 
48 #include <sys/time.h>
49 #include <sys/param.h>
50 #include <sys/socket.h>
51 #include <sys/sockio.h>
52 #include <sys/stropts.h>
53 #include <sys/file.h>
54 #include <arpa/inet.h>
55 #include <net/if.h>
56 #include <netinet/in_systm.h>
57 #include <netinet/in.h>
58 #include <netinet/ip.h>
59 #include <netinet/ip_icmp.h>
60 #include <netinet/ip6.h>
61 #include <netinet/icmp6.h>
62 #include <netinet/udp.h>
63 #include <netdb.h>
64 #include <stdlib.h>
65 
66 #include <libinetutil.h>
67 #include "ping.h"
68 
69 static int IPv6_hdrlen(ip6_t *, int, uint8_t *);
70 static void pr_ext_headers(struct msghdr *);
71 static void pr_rthdr(unsigned char *);
72 static char *pr_type6(uchar_t);
73 
74 /*
75  * Initialize the msghdr for specifying the hoplimit, outgoing interface and
76  * routing header.
77  */
78 void
79 set_ancillary_data(struct msghdr *msgp, int hoplimit,
80     union any_in_addr *gwIPlist, int gw_cnt, uint_t if_index)
81 {
82 	size_t hoplimit_space;
83 	size_t rthdr_space;
84 	size_t pktinfo_space;
85 	size_t bufspace;
86 	struct cmsghdr *cmsgp;
87 	uchar_t *cmsg_datap;
88 	static boolean_t first = _B_TRUE;
89 	int i;
90 
91 	if (hoplimit == -1 && gw_cnt == 0 && if_index == 0)
92 		return;
93 
94 	/*
95 	 * Need to figure out size of buffer needed for ancillary data
96 	 * containing routing header and packet info options.
97 	 *
98 	 * Portable heuristic to compute upper bound on space needed for
99 	 * N ancillary data options. It assumes up to _MAX_ALIGNMENT padding
100 	 * after both header and data as the worst possible upper bound on space
101 	 * consumed by padding.
102 	 * It also adds one extra "sizeof (struct cmsghdr)" for the last option.
103 	 * This is needed because we would like to use CMSG_NXTHDR() while
104 	 * composing the buffer. The CMSG_NXTHDR() macro is designed better for
105 	 * parsing than composing the buffer. It requires the pointer it returns
106 	 * to leave space in buffer for addressing a cmsghdr and we want to make
107 	 * sure it works for us while we skip beyond the last ancillary data
108 	 * option.
109 	 *
110 	 * bufspace[i]  = sizeof(struct cmsghdr) + <pad after header> +
111 	 *		<option[i] content length> + <pad after data>;
112 	 *
113 	 * total_bufspace = bufspace[0] + bufspace[1] + ...
114 	 *		    ... + bufspace[N-1] + sizeof (struct cmsghdr);
115 	 */
116 
117 	rthdr_space = 0;
118 	pktinfo_space = 0;
119 	hoplimit_space = 0;
120 	bufspace = 0;
121 
122 	if (hoplimit != -1) {
123 		hoplimit_space = sizeof (int);
124 		bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
125 		    hoplimit_space + _MAX_ALIGNMENT;
126 	}
127 
128 	if (gw_cnt > 0) {
129 		rthdr_space = inet6_rth_space(IPV6_RTHDR_TYPE_0, gw_cnt);
130 		bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
131 		    rthdr_space + _MAX_ALIGNMENT;
132 	}
133 
134 	if (if_index != 0) {
135 		pktinfo_space = sizeof (struct in6_pktinfo);
136 		bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
137 		    pktinfo_space + _MAX_ALIGNMENT;
138 	}
139 
140 	/*
141 	 * We need to temporarily set the msgp->msg_controllen to bufspace
142 	 * (we will later trim it to actual length used). This is needed because
143 	 * CMSG_NXTHDR() uses it to check we have not exceeded the bounds.
144 	 */
145 	bufspace += sizeof (struct cmsghdr);
146 	msgp->msg_controllen = bufspace;
147 
148 	/*
149 	 * This function is called more than once only if -l/-S used,
150 	 * since we need to modify the middle gateway. So, don't alloc
151 	 * new memory, just reuse what msg6 points to.
152 	 */
153 	if (first) {
154 		first = _B_FALSE;
155 		msgp->msg_control = (struct cmsghdr *)malloc(bufspace);
156 		if (msgp->msg_control == NULL) {
157 			Fprintf(stderr, "%s: malloc %s\n",
158 			    progname, strerror(errno));
159 			exit(EXIT_FAILURE);
160 		}
161 	};
162 	cmsgp = CMSG_FIRSTHDR(msgp);
163 
164 	/*
165 	 * Fill ancillary data. First hoplimit, then rthdr and pktinfo.
166 	 */
167 
168 	/* set hoplimit ancillary data if needed */
169 	if (hoplimit != -1) {
170 		cmsgp->cmsg_level = IPPROTO_IPV6;
171 		cmsgp->cmsg_type = IPV6_HOPLIMIT;
172 		cmsg_datap = CMSG_DATA(cmsgp);
173 		/* LINTED */
174 		*(int *)cmsg_datap = hoplimit;
175 		cmsgp->cmsg_len = cmsg_datap + hoplimit_space -
176 		    (uchar_t *)cmsgp;
177 		cmsgp = CMSG_NXTHDR(msgp, cmsgp);
178 	}
179 
180 	/* set rthdr ancillary data if needed */
181 	if (gw_cnt > 0) {
182 		struct ip6_rthdr0 *rthdr0p;
183 
184 		cmsgp->cmsg_level = IPPROTO_IPV6;
185 		cmsgp->cmsg_type = IPV6_RTHDR;
186 		cmsg_datap = CMSG_DATA(cmsgp);
187 
188 		/*
189 		 * Initialize rthdr structure
190 		 */
191 		/* LINTED */
192 		rthdr0p = (struct ip6_rthdr0 *)cmsg_datap;
193 		if (inet6_rth_init(rthdr0p, rthdr_space,
194 		    IPV6_RTHDR_TYPE_0, gw_cnt) == NULL) {
195 			Fprintf(stderr, "%s: inet6_rth_init failed\n",
196 			    progname);
197 			exit(EXIT_FAILURE);
198 		}
199 
200 		/*
201 		 * Stuff in gateway addresses
202 		 */
203 		for (i = 0; i < gw_cnt; i++) {
204 			if (inet6_rth_add(rthdr0p, &gwIPlist[i].addr6) == -1) {
205 				Fprintf(stderr,
206 				    "%s: inet6_rth_add\n", progname);
207 				exit(EXIT_FAILURE);
208 			}
209 		}
210 
211 		cmsgp->cmsg_len = cmsg_datap + rthdr_space - (uchar_t *)cmsgp;
212 		cmsgp = CMSG_NXTHDR(msgp, cmsgp);
213 	}
214 
215 	/* set pktinfo ancillary data if needed */
216 	if (if_index != 0) {
217 		struct in6_pktinfo *pktinfop;
218 
219 		cmsgp->cmsg_level = IPPROTO_IPV6;
220 		cmsgp->cmsg_type = IPV6_PKTINFO;
221 		cmsg_datap = CMSG_DATA(cmsgp);
222 
223 		/* LINTED */
224 		pktinfop = (struct in6_pktinfo *)cmsg_datap;
225 		/*
226 		 * We don't know if pktinfop->ipi6_addr is aligned properly,
227 		 * therefore let's use bcopy, instead of assignment.
228 		 */
229 		(void) bcopy(&in6addr_any, &pktinfop->ipi6_addr,
230 		    sizeof (struct in6_addr));
231 
232 		/*
233 		 *  We can assume pktinfop->ipi6_ifindex is 32 bit aligned.
234 		 */
235 		pktinfop->ipi6_ifindex = if_index;
236 		cmsgp->cmsg_len = cmsg_datap + pktinfo_space - (uchar_t *)cmsgp;
237 		cmsgp = CMSG_NXTHDR(msgp, cmsgp);
238 	}
239 
240 	msgp->msg_controllen = (char *)cmsgp - (char *)msgp->msg_control;
241 }
242 
243 /*
244  * Check out the packet to see if it came from us.  This logic is necessary
245  * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
246  * which arrive ('tis only fair).  This permits multiple copies of this
247  * program to be run without having intermingled output (or statistics!).
248  */
249 void
250 check_reply6(struct addrinfo *ai_dst, struct msghdr *msg, int cc,
251     ushort_t udp_src_port)
252 {
253 	struct icmp6_hdr *icmp6;
254 	ip6_t *ip6h;
255 	nd_redirect_t *nd_rdrct;
256 	struct udphdr *up;
257 	union any_in_addr dst_addr;
258 	uchar_t *buf;
259 	int32_t *intp;
260 	struct sockaddr_in6 *from6;
261 	struct timeval tv;
262 	struct timeval *tp;
263 	int64_t triptime;
264 	boolean_t valid_reply = _B_FALSE;
265 	boolean_t reply_matched_current_target = _B_FALSE; /* Is the source */
266 						/* address of this reply same */
267 						/* as where we're sending */
268 						/* currently? */
269 	boolean_t last_reply_from_targetaddr = _B_FALSE; /* Is this stats, */
270 						/* probe all with npackets>0 */
271 						/* and we received reply for */
272 						/* the last probe sent to */
273 						/* targetaddr */
274 	uint32_t ip6hdr_len;
275 	uint8_t last_hdr;
276 	int cc_left;
277 	int i;
278 	char tmp_buf[INET6_ADDRSTRLEN];
279 	static char *unreach6[] = {
280 	    "No Route to Destination",
281 	    "Communication Administratively Prohibited",
282 	    "Not a Neighbor (obsoleted ICMPv6 code)",
283 	    "Address Unreachable",
284 	    "Port Unreachable"
285 	};
286 	static char *timexceed6[] = {
287 	    "Hop limit exceeded in transit",
288 	    "Fragment reassembly time exceeded"
289 	};
290 	static char *param_prob6[] = {
291 	    "Erroneous header field encountered",
292 	    "Unrecognized next header type encountered",
293 	    "Unrecognized IPv6 option encountered"
294 	};
295 	boolean_t print_newline = _B_FALSE;
296 
297 	/* decompose msghdr into useful pieces */
298 	buf = (uchar_t *)msg->msg_iov->iov_base;
299 	from6 = (struct sockaddr_in6 *)msg->msg_name;
300 
301 	/* LINTED */
302 	intp = (int32_t *)buf;
303 
304 	ping_gettime(msg, &tv);
305 
306 	/* Ignore packets > 64k or control buffers that don't fit */
307 	if (msg->msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
308 		if (verbose) {
309 			Printf("Truncated message: msg_flags 0x%x from %s\n",
310 			    msg->msg_flags, pr_name6(from6));
311 		}
312 		return;
313 	}
314 	if (cc < ICMP6_MINLEN) {
315 		if (verbose) {
316 			Printf("packet too short (%d bytes) from %s\n", cc,
317 			    pr_name6(from6));
318 		}
319 		return;
320 	}
321 	/* LINTED */
322 	icmp6 = (struct icmp6_hdr *)buf;
323 	cc_left = cc - ICMP6_MINLEN;
324 
325 	switch (icmp6->icmp6_type) {
326 	case ICMP6_DST_UNREACH:
327 		/* LINTED */
328 		ip6h = (ip6_t *)((char *)icmp6 + ICMP6_MINLEN);
329 		if (cc_left < sizeof (ip6_t)) {
330 			if (verbose) {
331 				Printf("packet too short (%d bytes) from %s\n",
332 				    cc, pr_name6(from6));
333 			}
334 			return;
335 		}
336 
337 		/*
338 		 * Determine the total length of IPv6 header and extension
339 		 * headers, also the upper layer header (UDP, TCP, ICMP, etc.)
340 		 * following.
341 		 */
342 		ip6hdr_len = IPv6_hdrlen(ip6h, cc_left, &last_hdr);
343 
344 		cc_left -= ip6hdr_len;
345 
346 		/* LINTED */
347 		up = (struct udphdr *)((char *)ip6h + ip6hdr_len);
348 		if (cc_left < sizeof (struct udphdr)) {
349 			if (verbose) {
350 				Printf("packet too short (%d bytes) from %s\n",
351 				    cc, pr_name6(from6));
352 			}
353 			return;
354 		}
355 		cc_left -= sizeof (struct udphdr);
356 
357 		/* determine if this is *the* reply */
358 		if (icmp6->icmp6_code == ICMP6_DST_UNREACH_NOPORT &&
359 		    last_hdr == IPPROTO_UDP &&
360 		    udp_src_port == up->uh_sport &&
361 		    use_udp) {
362 			valid_reply = _B_TRUE;
363 		} else {
364 			valid_reply = _B_FALSE;
365 		}
366 
367 		if (valid_reply) {
368 			/*
369 			 * For this valid reply, if we are still sending to
370 			 * this target IP address, we'd like to do some
371 			 * updates to targetaddr, so hold SIGALRMs.
372 			 */
373 			(void) sighold(SIGALRM);
374 			is_alive = _B_TRUE;
375 			nreceived++;
376 			reply_matched_current_target =
377 			    seq_match(current_targetaddr->starting_seq_num,
378 			    current_targetaddr->num_sent,
379 			    ntohs(up->uh_dport));
380 			if (reply_matched_current_target) {
381 				current_targetaddr->got_reply = _B_TRUE;
382 				nreceived_last_target++;
383 				/*
384 				 * Determine if stats, probe-all, and
385 				 * npackets != 0, and this is the reply for
386 				 * the last probe we sent to current target
387 				 * address.
388 				 */
389 				if (stats && probe_all && npackets > 0 &&
390 				    ((current_targetaddr->starting_seq_num +
391 				    current_targetaddr->num_probes - 1) %
392 				    (MAX_PORT + 1) == ntohs(up->uh_dport)) &&
393 				    (current_targetaddr->num_probes ==
394 				    current_targetaddr->num_sent))
395 					last_reply_from_targetaddr = _B_TRUE;
396 			} else {
397 				/*
398 				 * If it's just probe_all and we just received
399 				 * a reply from a target address we were
400 				 * probing and had timed out (now we are probing
401 				 * some other target address), we ignore
402 				 * this reply.
403 				 */
404 				if (probe_all && !stats) {
405 					valid_reply = _B_FALSE;
406 					/*
407 					 * Only if it's verbose, we get a
408 					 * message regarding this reply,
409 					 * otherwise we are done here.
410 					 */
411 					if (!verbose) {
412 						(void) sigrelse(SIGALRM);
413 						return;
414 					}
415 				}
416 			}
417 		}
418 
419 		if (valid_reply && !stats) {
420 			/*
421 			 * if we are still sending to the same target address,
422 			 * then stop it, because we know it's alive.
423 			 */
424 			if (reply_matched_current_target) {
425 				(void) alarm(0);	/* cancel alarm */
426 				(void) sigset(SIGALRM, SIG_IGN);
427 				current_targetaddr->probing_done = _B_TRUE;
428 			}
429 			(void) sigrelse(SIGALRM);
430 
431 			if (!probe_all) {
432 				Printf("%s is alive\n", targethost);
433 			} else {
434 				(void) inet_ntop(AF_INET6,
435 				    (void *)&ip6h->ip6_dst,
436 				    tmp_buf, sizeof (tmp_buf));
437 				if (nflag) {
438 					Printf("%s is alive\n", tmp_buf);
439 				} else {
440 					Printf("%s (%s) is alive\n",
441 					    targethost, tmp_buf);
442 				}
443 			}
444 			if (reply_matched_current_target) {
445 				/*
446 				 * Let's get things going again, but now
447 				 * ping will start sending to next target IP
448 				 * address.
449 				 */
450 				send_scheduled_probe();
451 				(void) sigset(SIGALRM, sigalrm_handler);
452 				schedule_sigalrm();
453 			}
454 			return;
455 		} else {
456 			/*
457 			 * If we are not moving to next targetaddr, let's
458 			 * release the SIGALRM now. We don't want to stall in
459 			 * the middle of probing a targetaddr if the pr_name()
460 			 * call (see below) takes longer.
461 			 */
462 			if (!last_reply_from_targetaddr)
463 				(void) sigrelse(SIGALRM);
464 			/* else, we'll release it later */
465 		}
466 
467 		dst_addr.addr6 = ip6h->ip6_dst;
468 		if (valid_reply) {
469 			Printf("%d bytes from %s: ", cc,
470 			    pr_name6(from6));
471 			Printf("udp_port=%d. ", ntohs(up->uh_dport));
472 			print_newline = _B_TRUE;
473 		} else if (is_a_target(ai_dst, &dst_addr)|| verbose) {
474 			if (icmp6->icmp6_code >= A_CNT(unreach6)) {
475 				Printf("ICMPv6 %d Unreachable from gateway "
476 				    "%s\n", icmp6->icmp6_code,
477 				    pr_name6(from6));
478 			} else {
479 				Printf("ICMPv6 %s from gateway %s\n",
480 				    unreach6[icmp6->icmp6_code],
481 				    pr_name6(from6));
482 			}
483 			Printf(" for %s from %s", pr_protocol(last_hdr),
484 			    pr_name((char *)&ip6h->ip6_src, AF_INET6));
485 			Printf(" to %s", pr_name((char *)&ip6h->ip6_dst,
486 			    AF_INET6));
487 			if (last_hdr == IPPROTO_TCP || last_hdr == IPPROTO_UDP)
488 				Printf(" port %d ", ntohs(up->uh_dport));
489 			print_newline = _B_TRUE;
490 		}
491 
492 		/*
493 		 * Update and print the stats, if it's a valid reply and
494 		 * contains a timestamp.
495 		 */
496 		if (valid_reply && datalen >= sizeof (struct timeval) &&
497 		    cc_left >= sizeof (struct timeval)) {
498 			/* LINTED */
499 			tp = (struct timeval *)((char *)up +
500 			    sizeof (struct udphdr));
501 			(void) tvsub(&tv, tp);
502 			triptime = (int64_t)tv.tv_sec * MICROSEC + tv.tv_usec;
503 			Printf("time=" TIMEFORMAT " ms", triptime/1000.0);
504 			tsum += triptime;
505 			tsum2 += triptime*triptime;
506 			if (triptime < tmin)
507 				tmin = triptime;
508 			if (triptime > tmax)
509 				tmax = triptime;
510 			print_newline = _B_TRUE;
511 		}
512 		if (print_newline)
513 			(void) putchar('\n');
514 		/*
515 		 * If it's stats, probe-all, npackets > 0, and we received reply
516 		 * for the last probe sent to this target address, then we
517 		 * don't need to wait anymore, let's move on to next target
518 		 * address, now!
519 		 */
520 		if (last_reply_from_targetaddr) {
521 			(void) alarm(0);	/* cancel alarm */
522 			current_targetaddr->probing_done = _B_TRUE;
523 			(void) sigrelse(SIGALRM);
524 			send_scheduled_probe();
525 			schedule_sigalrm();
526 		}
527 		break;
528 
529 	case ICMP6_PACKET_TOO_BIG:
530 		/* LINTED */
531 		ip6h = (ip6_t *)((char *)icmp6 + ICMP6_MINLEN);
532 		if (cc_left < sizeof (ip6_t)) {
533 			if (verbose) {
534 				Printf("packet too short (%d bytes) from %s\n",
535 				    cc, pr_name6(from6));
536 			}
537 			return;
538 		}
539 		ip6hdr_len = IPv6_hdrlen(ip6h, cc_left, &last_hdr);
540 
541 		dst_addr.addr6 = ip6h->ip6_dst;
542 		if (is_a_target(ai_dst, &dst_addr) || verbose) {
543 			Printf("ICMPv6 packet too big from %s\n",
544 			    pr_name6(from6));
545 
546 			Printf(" for %s from %s", pr_protocol(last_hdr),
547 			    pr_name((char *)&ip6h->ip6_src, AF_INET6));
548 			Printf(" to %s", pr_name((char *)&ip6h->ip6_dst,
549 			    AF_INET6));
550 			if ((last_hdr == IPPROTO_TCP ||
551 			    last_hdr == IPPROTO_UDP) &&
552 			    (cc_left >= (ip6hdr_len + 4))) {
553 				/* LINTED */
554 				up = (struct udphdr *)
555 				    ((char *)ip6h + ip6hdr_len);
556 				Printf(" port %d ", ntohs(up->uh_dport));
557 			}
558 			Printf(" MTU = %d\n", ntohl(icmp6->icmp6_mtu));
559 		}
560 		break;
561 
562 	case ICMP6_TIME_EXCEEDED:
563 		/* LINTED */
564 		ip6h = (ip6_t *)((char *)icmp6 + ICMP6_MINLEN);
565 		if (cc_left < sizeof (ip6_t)) {
566 			if (verbose) {
567 				Printf("packet too short (%d bytes) from %s\n",
568 				    cc, pr_name6(from6));
569 			}
570 			return;
571 		}
572 		ip6hdr_len = IPv6_hdrlen(ip6h, cc_left, &last_hdr);
573 
574 		dst_addr.addr6 = ip6h->ip6_dst;
575 		if (is_a_target(ai_dst, &dst_addr) || verbose) {
576 			if (icmp6->icmp6_code >= A_CNT(timexceed6)) {
577 				Printf("ICMPv6 %d time exceeded from %s\n",
578 				    icmp6->icmp6_code, pr_name6(from6));
579 			} else {
580 				Printf("ICMPv6 %s from %s\n",
581 				    timexceed6[icmp6->icmp6_code],
582 				    pr_name6(from6));
583 			}
584 			Printf(" for %s from %s", pr_protocol(last_hdr),
585 			    pr_name((char *)&ip6h->ip6_src, AF_INET6));
586 			Printf(" to %s", pr_name((char *)&ip6h->ip6_dst,
587 			    AF_INET6));
588 			if ((last_hdr == IPPROTO_TCP ||
589 			    last_hdr == IPPROTO_UDP) &&
590 			    (cc_left >= (ip6hdr_len + 4))) {
591 				/* LINTED */
592 				up = (struct udphdr *)
593 				    ((char *)ip6h + ip6hdr_len);
594 				Printf(" port %d", ntohs(up->uh_dport));
595 			}
596 			(void) putchar('\n');
597 		}
598 		break;
599 
600 	case ICMP6_PARAM_PROB:
601 		/* LINTED */
602 		ip6h = (ip6_t *)((char *)icmp6 + ICMP6_MINLEN);
603 		if (cc_left < sizeof (ip6_t)) {
604 			if (verbose) {
605 				Printf("packet too short (%d bytes) from %s\n",
606 				    cc, pr_name6(from6));
607 			}
608 			return;
609 		}
610 		ip6hdr_len = IPv6_hdrlen(ip6h, cc_left, &last_hdr);
611 
612 		dst_addr.addr6 = ip6h->ip6_dst;
613 		if (is_a_target(ai_dst, &dst_addr) || verbose) {
614 			if (icmp6->icmp6_code >= A_CNT(param_prob6)) {
615 				Printf("ICMPv6 %d parameter problem from %s\n",
616 				    icmp6->icmp6_code, pr_name6(from6));
617 			} else {
618 				Printf("ICMPv6 %s from %s\n",
619 				    param_prob6[icmp6->icmp6_code],
620 				    pr_name6(from6));
621 			}
622 			icmp6->icmp6_pptr = ntohl(icmp6->icmp6_pptr);
623 			Printf(" in byte %d", icmp6->icmp6_pptr);
624 			if (icmp6->icmp6_pptr <= ip6hdr_len) {
625 				Printf(" (value 0x%x)",
626 				    *((uchar_t *)ip6h + icmp6->icmp6_pptr));
627 			}
628 			Printf(" for %s from %s", pr_protocol(last_hdr),
629 			    pr_name((char *)&ip6h->ip6_src, AF_INET6));
630 			Printf(" to %s", pr_name((char *)&ip6h->ip6_dst,
631 			    AF_INET6));
632 			if ((last_hdr == IPPROTO_TCP ||
633 			    last_hdr == IPPROTO_UDP) &&
634 			    (cc_left >= (ip6hdr_len + 4))) {
635 				/* LINTED */
636 				up = (struct udphdr *)
637 				    ((char *)ip6h + ip6hdr_len);
638 				Printf(" port %d", ntohs(up->uh_dport));
639 			}
640 			(void) putchar('\n');
641 		}
642 		break;
643 
644 	case ICMP6_ECHO_REQUEST:
645 		return;
646 
647 	case ICMP6_ECHO_REPLY:
648 		if (ntohs(icmp6->icmp6_id) == ident) {
649 			if (!use_udp)
650 				valid_reply = _B_TRUE;
651 			else
652 				valid_reply = _B_FALSE;
653 		} else {
654 			return;
655 		}
656 
657 		if (valid_reply) {
658 			/*
659 			 * For this valid reply, if we are still sending to
660 			 * this target IP address, we'd like to do some
661 			 * updates to targetaddr, so hold SIGALRMs.
662 			 */
663 			(void) sighold(SIGALRM);
664 			is_alive = _B_TRUE;
665 			nreceived++;
666 			reply_matched_current_target =
667 			    seq_match(current_targetaddr->starting_seq_num,
668 			    current_targetaddr->num_sent,
669 			    ntohs(icmp6->icmp6_seq));
670 			if (reply_matched_current_target) {
671 				current_targetaddr->got_reply = _B_TRUE;
672 				nreceived_last_target++;
673 				/*
674 				 * Determine if stats, probe-all, and
675 				 * npackets != 0, and this is the reply for
676 				 * the last probe we sent to current target
677 				 * address.
678 				 */
679 				if (stats && probe_all && npackets > 0 &&
680 				    ((current_targetaddr->starting_seq_num +
681 				    current_targetaddr->num_probes - 1) %
682 				    (MAX_ICMP_SEQ + 1) ==
683 				    ntohs(icmp6->icmp6_seq)) &&
684 				    (current_targetaddr->num_probes ==
685 				    current_targetaddr->num_sent))
686 					last_reply_from_targetaddr = _B_TRUE;
687 			} else {
688 				/*
689 				 * If it's just probe_all and we just received
690 				 * a reply from a target address we were
691 				 * probing and had timed out (now we are probing
692 				 * some other target address), we ignore
693 				 * this reply.
694 				 */
695 				if (probe_all && !stats) {
696 					valid_reply = _B_FALSE;
697 					/*
698 					 * Only if it's verbose, we get a
699 					 * message regarding this reply,
700 					 * otherwise we are done here.
701 					 */
702 					if (!verbose) {
703 						(void) sigrelse(SIGALRM);
704 						return;
705 					}
706 				}
707 			}
708 		}
709 
710 		if (!stats && valid_reply) {
711 			/*
712 			 * if we are still sending to the same target address,
713 			 * then stop it, because we know it's alive.
714 			 */
715 			if (reply_matched_current_target) {
716 				(void) alarm(0);	/* cancel alarm */
717 				(void) sigset(SIGALRM, SIG_IGN);
718 				current_targetaddr->probing_done = _B_TRUE;
719 			}
720 			(void) sigrelse(SIGALRM);
721 
722 			if (!probe_all) {
723 				Printf("%s is alive\n", targethost);
724 			} else {
725 				/*
726 				 * If we are using send_reply, the real
727 				 * target address is not the src address of the
728 				 * replies. Use icmp_seq to find out where this
729 				 * probe was sent to.
730 				 */
731 				if (send_reply) {
732 					(void) find_dstaddr(
733 					    ntohs(icmp6->icmp6_seq), &dst_addr);
734 					(void) inet_ntop(AF_INET6,
735 					    (void *)&dst_addr.addr6,
736 					    tmp_buf, sizeof (tmp_buf));
737 				} else {
738 					(void) inet_ntop(AF_INET6,
739 					    (void *)&from6->sin6_addr,
740 					    tmp_buf, sizeof (tmp_buf));
741 				}
742 
743 				if (nflag) {
744 					Printf("%s is alive\n", tmp_buf);
745 				} else {
746 					Printf("%s (%s) is alive\n",
747 					    targethost, tmp_buf);
748 				}
749 			}
750 			if (reply_matched_current_target) {
751 				/*
752 				 * Let's get things going again, but now
753 				 * ping will start sending to next target IP
754 				 * address.
755 				 */
756 				send_scheduled_probe();
757 				(void) sigset(SIGALRM, sigalrm_handler);
758 				schedule_sigalrm();
759 			}
760 			return;
761 		} else {
762 			/*
763 			 * If we are not moving to next targetaddr, let's
764 			 * release the SIGALRM now. We don't want to stall in
765 			 * the middle of probing a targetaddr if the pr_name()
766 			 * call (see below) takes longer.
767 			 */
768 			if (!last_reply_from_targetaddr)
769 				(void) sigrelse(SIGALRM);
770 			/* else, we'll release it later */
771 		}
772 
773 		/*
774 		 * If we are using send_reply, the real target address is
775 		 * not the src address of the replies. Use icmp_seq to find out
776 		 * where this probe was sent to.
777 		 */
778 		if (send_reply) {
779 			(void) find_dstaddr(ntohs(icmp6->icmp6_seq), &dst_addr);
780 			Printf("%d bytes from %s: ", cc,
781 			    pr_name((char *)&dst_addr.addr6,  AF_INET6));
782 		} else {
783 			Printf("%d bytes from %s: ", cc, pr_name6(from6));
784 		}
785 		Printf("icmp_seq=%d. ", ntohs(icmp6->icmp6_seq));
786 
787 		if (valid_reply && datalen >= sizeof (struct timeval) &&
788 		    cc_left >= sizeof (struct timeval)) {
789 			/* LINTED */
790 			tp = (struct timeval *)&icmp6->icmp6_data16[2];
791 			(void) tvsub(&tv, tp);
792 			triptime = (int64_t)tv.tv_sec * MICROSEC + tv.tv_usec;
793 			Printf("time=" TIMEFORMAT " ms", triptime/1000.0);
794 			tsum += triptime;
795 			tsum2 += triptime*triptime;
796 			if (triptime < tmin)
797 				tmin = triptime;
798 			if (triptime > tmax)
799 				tmax = triptime;
800 		}
801 		(void) putchar('\n');
802 		/*
803 		 * If it's stats, probe-all, npackets > 0, and we received reply
804 		 * for the last probe sent to this target address, then we
805 		 * don't need to wait anymore, let's move on to next target
806 		 * address, now!
807 		 */
808 		if (last_reply_from_targetaddr) {
809 			(void) alarm(0);	/* cancel alarm */
810 			current_targetaddr->probing_done = _B_TRUE;
811 			(void) sigrelse(SIGALRM);
812 			send_scheduled_probe();
813 			schedule_sigalrm();
814 		}
815 		break;
816 
817 	case MLD_LISTENER_QUERY:
818 	case MLD_LISTENER_REPORT:
819 	case MLD_LISTENER_REDUCTION:
820 	case ND_ROUTER_SOLICIT:
821 	case ND_ROUTER_ADVERT:
822 	case ND_NEIGHBOR_SOLICIT:
823 	case ND_NEIGHBOR_ADVERT:
824 		return;
825 
826 	case ND_REDIRECT:
827 		nd_rdrct = (nd_redirect_t *)icmp6;
828 
829 		if (cc_left < sizeof (nd_redirect_t) - ICMP6_MINLEN) {
830 			if (verbose) {
831 				Printf("packet too short (%d bytes) from %s\n",
832 				    cc, pr_name6(from6));
833 			}
834 			return;
835 		}
836 		dst_addr.addr6 = nd_rdrct->nd_rd_dst;
837 		if (is_a_target(ai_dst, &dst_addr) || verbose) {
838 			Printf("ICMPv6 redirect from gateway %s\n",
839 			    pr_name6(from6));
840 
841 			Printf(" to %s",
842 			    pr_name((char *)&nd_rdrct->nd_rd_target, AF_INET6));
843 			Printf(" for %s\n",
844 			    pr_name((char *)&nd_rdrct->nd_rd_dst, AF_INET6));
845 		}
846 		break;
847 
848 	default:
849 		if (verbose) {
850 			Printf("%d bytes from %s:\n", cc, pr_name6(from6));
851 			Printf("icmp6_type=%d (%s) ", icmp6->icmp6_type,
852 			    pr_type6(icmp6->icmp6_type));
853 			Printf("icmp6_code=%d\n", icmp6->icmp6_code);
854 			for (i = 0; i < 12; i++) {
855 				Printf("x%2.2x: x%8.8x\n",
856 				    i * sizeof (int32_t), *intp++);
857 			}
858 		}
859 		break;
860 	}
861 
862 	/*
863 	 * If it's verbose mode and we recv'd ancillary data, print extension
864 	 * headers.
865 	 */
866 	if (verbose && msg->msg_controllen > 0)
867 		pr_ext_headers(msg);
868 }
869 
870 /*
871  * Convert an ICMP6 "type" field to a printable string.
872  */
873 static char *
874 pr_type6(uchar_t icmp6_type)
875 {
876 	static struct icmptype_table ttab6[] = {
877 		{ICMP6_DST_UNREACH,		"Dest Unreachable"},
878 		{ICMP6_PACKET_TOO_BIG,		"Packet Too Big"},
879 		{ICMP6_TIME_EXCEEDED,		"Time Exceeded"},
880 		{ICMP6_PARAM_PROB,		"Parameter Problem"},
881 		{ICMP6_ECHO_REQUEST,		"Echo Request"},
882 		{ICMP6_ECHO_REPLY,		"Echo Reply"},
883 		{MLD_LISTENER_QUERY,		"Multicast Listener Query"},
884 		{MLD_LISTENER_REPORT,		"Multicast Listener Report"},
885 		{MLD_LISTENER_REDUCTION,	"Multicast Listener Done"},
886 		{ND_ROUTER_SOLICIT,		"Router Solicitation"},
887 		{ND_ROUTER_ADVERT,		"Router Advertisement"},
888 		{ND_NEIGHBOR_SOLICIT,		"Neighbor Solicitation"},
889 		{ND_NEIGHBOR_ADVERT,		"Neighbor Advertisement"},
890 		{ND_REDIRECT,			"Redirect Message"},
891 	};
892 	int i;
893 
894 	for (i = 0; i < A_CNT(ttab6); i++) {
895 		if (ttab6[i].type == icmp6_type)
896 			return (ttab6[i].message);
897 
898 	}
899 
900 	return ("OUT-OF-RANGE");
901 }
902 
903 /*
904  * Return the length of the IPv6 related headers (including extension headers).
905  * It also sets the *last_hdr_rtrn to the first upper layer protocol header
906  * following IPv6 header and extension headers. If print_flag is _B_TRUE, it
907  * prints extension headers.
908  */
909 static int
910 IPv6_hdrlen(ip6_t *ip6h, int pkt_len, uint8_t *last_hdr_rtrn)
911 {
912 	int length;
913 	int exthdrlength;
914 	uint8_t nexthdr;
915 	uint8_t *whereptr;
916 	ip6_hbh_t *hbhhdr;
917 	ip6_dest_t *desthdr;
918 	ip6_rthdr_t *rthdr;
919 	ip6_frag_t *fraghdr;
920 	uint8_t	*endptr;
921 
922 	length = sizeof (ip6_t);
923 
924 	whereptr = ((uint8_t *)&ip6h[1]);	/* point to next hdr */
925 	endptr = ((uint8_t *)ip6h) + pkt_len;
926 
927 	nexthdr = ip6h->ip6_nxt;
928 	*last_hdr_rtrn = IPPROTO_NONE;
929 
930 	if (whereptr >= endptr)
931 		return (length);
932 
933 	while (whereptr < endptr) {
934 		*last_hdr_rtrn = nexthdr;
935 		switch (nexthdr) {
936 		case IPPROTO_HOPOPTS:
937 			hbhhdr = (ip6_hbh_t *)whereptr;
938 			exthdrlength = 8 * (hbhhdr->ip6h_len + 1);
939 			if ((uchar_t *)hbhhdr + exthdrlength > endptr)
940 				return (length);
941 			nexthdr = hbhhdr->ip6h_nxt;
942 			length += exthdrlength;
943 			break;
944 
945 		case IPPROTO_DSTOPTS:
946 			desthdr = (ip6_dest_t *)whereptr;
947 			exthdrlength = 8 * (desthdr->ip6d_len + 1);
948 			if ((uchar_t *)desthdr + exthdrlength > endptr)
949 				return (length);
950 			nexthdr = desthdr->ip6d_nxt;
951 			length += exthdrlength;
952 			break;
953 
954 		case IPPROTO_ROUTING:
955 			rthdr = (ip6_rthdr_t *)whereptr;
956 			exthdrlength = 8 * (rthdr->ip6r_len + 1);
957 			if ((uchar_t *)rthdr + exthdrlength > endptr)
958 				return (length);
959 			nexthdr = rthdr->ip6r_nxt;
960 			length += exthdrlength;
961 			break;
962 
963 		case IPPROTO_FRAGMENT:
964 			/* LINTED */
965 			fraghdr = (ip6_frag_t *)whereptr;
966 			if ((uchar_t *)&fraghdr[1] > endptr)
967 				return (length);
968 			nexthdr = fraghdr->ip6f_nxt;
969 			length += sizeof (struct ip6_frag);
970 			break;
971 
972 		case IPPROTO_NONE:
973 		default:
974 			return (length);
975 		}
976 		whereptr = (uint8_t *)ip6h + length;
977 	}
978 	*last_hdr_rtrn = nexthdr;
979 
980 	return (length);
981 }
982 
983 /*
984  * Print extension headers
985  */
986 static void
987 pr_ext_headers(struct msghdr *msg)
988 {
989 	struct cmsghdr *cmsg;
990 
991 	Printf("  IPv6 extension headers: ");
992 
993 	for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
994 	    cmsg = CMSG_NXTHDR(msg, cmsg)) {
995 		if (cmsg->cmsg_level == IPPROTO_IPV6) {
996 			switch (cmsg->cmsg_type) {
997 			case IPV6_HOPOPTS:
998 				Printf(" <hop-by-hop options>");
999 				break;
1000 
1001 			case IPV6_DSTOPTS:
1002 				Printf(" <destination options (after routing"
1003 				    "header)>");
1004 				break;
1005 
1006 			case IPV6_RTHDRDSTOPTS:
1007 				Printf(" <destination options (before routing"
1008 				    "header)>");
1009 				break;
1010 
1011 			case IPV6_RTHDR:
1012 				pr_rthdr((uchar_t *)CMSG_DATA(cmsg));
1013 				break;
1014 
1015 			default:
1016 				Printf(" <option type %d>", cmsg->cmsg_type);
1017 				break;
1018 			}
1019 		}
1020 	}
1021 	(void) putchar('\n');
1022 }
1023 
1024 /*
1025  * Print the routing header 0 information
1026  */
1027 static void
1028 pr_rthdr(uchar_t *buf)
1029 {
1030 	ip6_rthdr_t *rthdr;
1031 	ip6_rthdr0_t *rthdr0;
1032 	struct in6_addr *gw_addr;
1033 	int i, num_addr;
1034 
1035 	rthdr = (ip6_rthdr_t *)buf;
1036 	Printf(" <type %d routing header, segleft %u> ",
1037 	    rthdr->ip6r_type, rthdr->ip6r_segleft);
1038 
1039 	if (rthdr->ip6r_type == 0) {
1040 		/* LINTED */
1041 		rthdr0 = (ip6_rthdr0_t *)buf;
1042 		gw_addr = (struct in6_addr *)(rthdr0 + 1);
1043 		num_addr = rthdr0->ip6r0_len / 2;
1044 
1045 		for (i = 0; i < num_addr; i++) {
1046 			Printf("%s", pr_name((char *)gw_addr, AF_INET6));
1047 			if (i == (num_addr - rthdr0->ip6r0_segleft))
1048 				Printf("(Current)");
1049 			gw_addr++;
1050 			if (i != num_addr - 1)
1051 				Printf(",  ");
1052 		}
1053 	}
1054 }
1055