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
66void check_reply6(struct addrinfo *, struct msghdr *, int, ushort_t);
67extern void find_dstaddr(ushort_t, union any_in_addr *);
68static int IPv6_hdrlen(ip6_t *, int, uint8_t *);
69extern boolean_t is_a_target(struct addrinfo *, union any_in_addr *);
70static void pr_ext_headers(struct msghdr *);
71extern char *pr_name(char *, int);
72extern char *pr_protocol(int);
73static void pr_rthdr(unsigned char *);
74static char *pr_type6(uchar_t);
75extern void schedule_sigalrm();
76extern void send_scheduled_probe();
77extern boolean_t seq_match(ushort_t, int, ushort_t);
78void set_ancillary_data(struct msghdr *, int, union any_in_addr *, int, uint_t);
79extern void sigalrm_handler();
80extern void tvsub(struct timeval *, struct timeval *);
81
82
83/*
84 * Initialize the msghdr for specifying the hoplimit, outgoing interface and
85 * routing header.
86 */
87void
88set_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 */
258void
259check_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 */
904static char *
905pr_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 */
940static int
941IPv6_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 */
1017static void
1018pr_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 */
1058static void
1059pr_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