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