diff usr/src/cmd/cmd-inet/usr.sbin/ping/ping_aux6.c @ 0:c9caec207d52 b86

Initial porting based on b86
author Koji Uno <koji.uno@sun.com>
date Tue, 02 Jun 2009 18:56:50 +0900
parents
children 1a15d5aaf794
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ping/ping_aux6.c	Tue Jun 02 18:56:50 2009 +0900
@@ -0,0 +1,1085 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
+/*	  All Rights Reserved  	*/
+
+/*
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ */
+
+#pragma ident	"@(#)ping_aux6.c	1.17	06/01/12 SMI"
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/stropts.h>
+#include <sys/file.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <netinet/udp.h>
+#include <netdb.h>
+#include <stdlib.h>
+
+#include <libinetutil.h>
+#include "ping.h"
+
+void check_reply6(struct addrinfo *, struct msghdr *, int, ushort_t);
+extern void find_dstaddr(ushort_t, union any_in_addr *);
+static int IPv6_hdrlen(ip6_t *, int, uint8_t *);
+extern boolean_t is_a_target(struct addrinfo *, union any_in_addr *);
+static void pr_ext_headers(struct msghdr *);
+extern char *pr_name(char *, int);
+extern char *pr_protocol(int);
+static void pr_rthdr(unsigned char *);
+static char *pr_type6(uchar_t);
+extern void schedule_sigalrm();
+extern void send_scheduled_probe();
+extern boolean_t seq_match(ushort_t, int, ushort_t);
+void set_ancillary_data(struct msghdr *, int, union any_in_addr *, int, uint_t);
+extern void sigalrm_handler();
+extern void tvsub(struct timeval *, struct timeval *);
+
+
+/*
+ * Initialize the msghdr for specifying the hoplimit, outgoing interface and
+ * routing header.
+ */
+void
+set_ancillary_data(struct msghdr *msgp, int hoplimit,
+    union any_in_addr *gwIPlist, int gw_cnt, uint_t if_index)
+{
+	size_t hoplimit_space;
+	size_t rthdr_space;
+	size_t pktinfo_space;
+	size_t bufspace;
+	struct cmsghdr *cmsgp;
+	uchar_t *cmsg_datap;
+	static boolean_t first = _B_TRUE;
+	int i;
+
+	if (hoplimit == -1 && gw_cnt == 0 && if_index == 0)
+		return;
+
+	/*
+	 * Need to figure out size of buffer needed for ancillary data
+	 * containing routing header and packet info options.
+	 *
+	 * Portable heuristic to compute upper bound on space needed for
+	 * N ancillary data options. It assumes up to _MAX_ALIGNMENT padding
+	 * after both header and data as the worst possible upper bound on space
+	 * consumed by padding.
+	 * It also adds one extra "sizeof (struct cmsghdr)" for the last option.
+	 * This is needed because we would like to use CMSG_NXTHDR() while
+	 * composing the buffer. The CMSG_NXTHDR() macro is designed better for
+	 * parsing than composing the buffer. It requires the pointer it returns
+	 * to leave space in buffer for addressing a cmsghdr and we want to make
+	 * sure it works for us while we skip beyond the last ancillary data
+	 * option.
+	 *
+	 * bufspace[i]  = sizeof(struct cmsghdr) + <pad after header> +
+	 *		<option[i] content length> + <pad after data>;
+	 *
+	 * total_bufspace = bufspace[0] + bufspace[1] + ...
+	 *		    ... + bufspace[N-1] + sizeof (struct cmsghdr);
+	 */
+
+	rthdr_space = 0;
+	pktinfo_space = 0;
+	hoplimit_space = 0;
+	bufspace = 0;
+
+	if (hoplimit != -1) {
+		hoplimit_space = sizeof (int);
+		bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
+		    hoplimit_space + _MAX_ALIGNMENT;
+	}
+
+	if (gw_cnt > 0) {
+		rthdr_space = inet6_rth_space(IPV6_RTHDR_TYPE_0, gw_cnt);
+		bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
+		    rthdr_space + _MAX_ALIGNMENT;
+	}
+
+	if (if_index != 0) {
+		pktinfo_space = sizeof (struct in6_pktinfo);
+		bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
+		    pktinfo_space + _MAX_ALIGNMENT;
+	}
+
+	/*
+	 * We need to temporarily set the msgp->msg_controllen to bufspace
+	 * (we will later trim it to actual length used). This is needed because
+	 * CMSG_NXTHDR() uses it to check we have not exceeded the bounds.
+	 */
+	bufspace += sizeof (struct cmsghdr);
+	msgp->msg_controllen = bufspace;
+
+	/*
+	 * This function is called more than once only if -l/-S used,
+	 * since we need to modify the middle gateway. So, don't alloc
+	 * new memory, just reuse what msg6 points to.
+	 */
+	if (first) {
+		first = _B_FALSE;
+		msgp->msg_control = (struct cmsghdr *)malloc(bufspace);
+		if (msgp->msg_control == NULL) {
+			Fprintf(stderr, "%s: malloc %s\n",
+			    progname, strerror(errno));
+			exit(EXIT_FAILURE);
+		}
+	};
+	cmsgp = CMSG_FIRSTHDR(msgp);
+
+	/*
+	 * Fill ancillary data. First hoplimit, then rthdr and pktinfo.
+	 */
+
+	/* set hoplimit ancillary data if needed */
+	if (hoplimit != -1) {
+		cmsgp->cmsg_level = IPPROTO_IPV6;
+		cmsgp->cmsg_type = IPV6_HOPLIMIT;
+		cmsg_datap = CMSG_DATA(cmsgp);
+		/* LINTED */
+		*(int *)cmsg_datap = hoplimit;
+		cmsgp->cmsg_len = cmsg_datap + hoplimit_space -
+		    (uchar_t *)cmsgp;
+		cmsgp = CMSG_NXTHDR(msgp, cmsgp);
+	}
+
+	/* set rthdr ancillary data if needed */
+	if (gw_cnt > 0) {
+		struct ip6_rthdr0 *rthdr0p;
+
+		cmsgp->cmsg_level = IPPROTO_IPV6;
+		cmsgp->cmsg_type = IPV6_RTHDR;
+		cmsg_datap = CMSG_DATA(cmsgp);
+
+		/*
+		 * Initialize rthdr structure
+		 */
+		/* LINTED */
+		rthdr0p = (struct ip6_rthdr0 *)cmsg_datap;
+		if (inet6_rth_init(rthdr0p, rthdr_space,
+		    IPV6_RTHDR_TYPE_0, gw_cnt) == NULL) {
+			Fprintf(stderr, "%s: inet6_rth_init failed\n",
+			    progname);
+			exit(EXIT_FAILURE);
+		}
+
+		/*
+		 * Stuff in gateway addresses
+		 */
+		for (i = 0; i < gw_cnt; i++) {
+			if (inet6_rth_add(rthdr0p, &gwIPlist[i].addr6) == -1) {
+				Fprintf(stderr,
+				    "%s: inet6_rth_add\n", progname);
+				exit(EXIT_FAILURE);
+			}
+		}
+
+		cmsgp->cmsg_len = cmsg_datap + rthdr_space - (uchar_t *)cmsgp;
+		cmsgp = CMSG_NXTHDR(msgp, cmsgp);
+	}
+
+	/* set pktinfo ancillary data if needed */
+	if (if_index != 0) {
+		struct in6_pktinfo *pktinfop;
+
+		cmsgp->cmsg_level = IPPROTO_IPV6;
+		cmsgp->cmsg_type = IPV6_PKTINFO;
+		cmsg_datap = CMSG_DATA(cmsgp);
+
+		/* LINTED */
+		pktinfop = (struct in6_pktinfo *)cmsg_datap;
+		/*
+		 * We don't know if pktinfop->ipi6_addr is aligned properly,
+		 * therefore let's use bcopy, instead of assignment.
+		 */
+		(void) bcopy(&in6addr_any, &pktinfop->ipi6_addr,
+		sizeof (struct in6_addr));
+
+		/*
+		 *  We can assume pktinfop->ipi6_ifindex is 32 bit aligned.
+		 */
+		pktinfop->ipi6_ifindex = if_index;
+		cmsgp->cmsg_len = cmsg_datap + pktinfo_space - (uchar_t *)cmsgp;
+		cmsgp = CMSG_NXTHDR(msgp, cmsgp);
+	}
+
+	msgp->msg_controllen = (char *)cmsgp - (char *)msgp->msg_control;
+}
+
+/*
+ * Check out the packet to see if it came from us.  This logic is necessary
+ * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
+ * which arrive ('tis only fair).  This permits multiple copies of this
+ * program to be run without having intermingled output (or statistics!).
+ */
+void
+check_reply6(struct addrinfo *ai_dst, struct msghdr *msg, int cc,
+    ushort_t udp_src_port)
+{
+	struct icmp6_hdr *icmp6;
+	ip6_t *ip6h;
+	nd_redirect_t *nd_rdrct;
+	struct udphdr *up;
+	union any_in_addr dst_addr;
+	uchar_t *buf;
+	int32_t *intp;
+	struct sockaddr_in6 *from6;
+	struct timeval tv;
+	struct timeval *tp;
+	int64_t triptime;
+	boolean_t valid_reply = _B_FALSE;
+	boolean_t reply_matched_current_target;	/* Is the source address of */
+						/* this reply same as where */
+						/* we're sending currently? */
+	boolean_t last_reply_from_targetaddr = _B_FALSE; /* Is this stats, */
+						/* probe all with npackets>0 */
+						/* and we received reply for */
+						/* the last probe sent to */
+						/* targetaddr */
+	uint32_t ip6hdr_len;
+	uint8_t last_hdr;
+	int cc_left;
+	int i;
+	char tmp_buf[INET6_ADDRSTRLEN];
+	static char *unreach6[] = {
+	    "No Route to Destination",
+	    "Communication Administratively Prohibited",
+	    "Not a Neighbor (obsoleted ICMPv6 code)",
+	    "Address Unreachable",
+	    "Port Unreachable"
+	};
+	static char *timexceed6[] = {
+	    "Hop limit exceeded in transit",
+	    "Fragment reassembly time exceeded"
+	};
+	static char *param_prob6[] = {
+	    "Erroneous header field encountered",
+	    "Unrecognized next header type encountered",
+	    "Unrecognized IPv6 option encountered"
+	};
+	boolean_t print_newline = _B_FALSE;
+
+	/* decompose msghdr into useful pieces */
+	buf = (uchar_t *)msg->msg_iov->iov_base;
+	from6 = (struct sockaddr_in6 *)msg->msg_name;
+
+	/* LINTED */
+	intp = (int32_t *)buf;
+
+	/* get time now for most accurate time calculation */
+	(void) gettimeofday(&tv, (struct timezone *)NULL);
+
+	/* Ignore packets > 64k or control buffers that don't fit */
+	if (msg->msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
+		if (verbose) {
+			Printf("Truncated message: msg_flags 0x%x from %s\n",
+			    msg->msg_flags,
+			    pr_name((char *)&from6->sin6_addr, AF_INET6));
+		}
+		return;
+	}
+	if (cc < ICMP6_MINLEN) {
+		if (verbose) {
+			Printf("packet too short (%d bytes) from %s\n", cc,
+			    pr_name((char *)&from6->sin6_addr, AF_INET6));
+		}
+		return;
+	}
+	/* LINTED */
+	icmp6 = (struct icmp6_hdr *)buf;
+	cc_left = cc - ICMP6_MINLEN;
+
+	switch (icmp6->icmp6_type) {
+	case ICMP6_DST_UNREACH:
+		/* LINTED */
+		ip6h = (ip6_t *)((char *)icmp6 + ICMP6_MINLEN);
+		if (cc_left < sizeof (ip6_t)) {
+			if (verbose) {
+				Printf("packet too short (%d bytes) from %s\n",
+				    cc,
+				    pr_name((char *)&from6->sin6_addr,
+					AF_INET6));
+			}
+			return;
+		}
+
+		/*
+		 * Determine the total length of IPv6 header and extension
+		 * headers, also the upper layer header (UDP, TCP, ICMP, etc.)
+		 * following.
+		 */
+		ip6hdr_len = IPv6_hdrlen(ip6h, cc_left, &last_hdr);
+
+		cc_left -= ip6hdr_len;
+
+		/* LINTED */
+		up = (struct udphdr *)((char *)ip6h + ip6hdr_len);
+		if (cc_left < sizeof (struct udphdr)) {
+			if (verbose) {
+				Printf("packet too short (%d bytes) from %s\n",
+				    cc,
+				    pr_name((char *)&from6->sin6_addr,
+					AF_INET6));
+			}
+			return;
+		}
+		cc_left -= sizeof (struct udphdr);
+
+		/* determine if this is *the* reply */
+		if (icmp6->icmp6_code == ICMP6_DST_UNREACH_NOPORT &&
+		    last_hdr == IPPROTO_UDP &&
+		    udp_src_port == up->uh_sport &&
+		    use_udp) {
+			valid_reply = _B_TRUE;
+		} else {
+			valid_reply = _B_FALSE;
+		}
+
+		if (valid_reply) {
+			/*
+			 * For this valid reply, if we are still sending to
+			 * this target IP address, we'd like to do some
+			 * updates to targetaddr, so hold SIGALRMs.
+			 */
+			(void) sighold(SIGALRM);
+			is_alive = _B_TRUE;
+			nreceived++;
+			reply_matched_current_target =
+			    seq_match(current_targetaddr->starting_seq_num,
+				current_targetaddr->num_sent,
+				ntohs(up->uh_dport));
+			if (reply_matched_current_target) {
+				current_targetaddr->got_reply = _B_TRUE;
+				nreceived_last_target++;
+				/*
+				 * Determine if stats, probe-all, and
+				 * npackets != 0, and this is the reply for
+				 * the last probe we sent to current target
+				 * address.
+				 */
+				if (stats && probe_all && npackets > 0 &&
+				    ((current_targetaddr->starting_seq_num +
+				    current_targetaddr->num_probes - 1) %
+				    (MAX_PORT + 1) == ntohs(up->uh_dport)) &&
+				    (current_targetaddr->num_probes ==
+				    current_targetaddr->num_sent))
+					last_reply_from_targetaddr = _B_TRUE;
+			} else {
+				/*
+				 * If it's just probe_all and we just received
+				 * a reply from a target address we were
+				 * probing and had timed out (now we are probing
+				 * some other target address), we ignore
+				 * this reply.
+				 */
+				if (probe_all && !stats) {
+					valid_reply = _B_FALSE;
+					/*
+					 * Only if it's verbose, we get a
+					 * message regarding this reply,
+					 * otherwise we are done here.
+					 */
+					if (!verbose) {
+						(void) sigrelse(SIGALRM);
+						return;
+					}
+				}
+			}
+		}
+
+		if (valid_reply && !stats) {
+			/*
+			 * if we are still sending to the same target address,
+			 * then stop it, because we know it's alive.
+			 */
+			if (reply_matched_current_target) {
+				(void) alarm(0);	/* cancel alarm */
+				(void) sigset(SIGALRM, SIG_IGN);
+				current_targetaddr->probing_done = _B_TRUE;
+			}
+			(void) sigrelse(SIGALRM);
+
+			if (!probe_all) {
+				Printf("%s is alive\n", targethost);
+			} else {
+				(void) inet_ntop(AF_INET6,
+				    (void *)&ip6h->ip6_dst,
+				    tmp_buf, sizeof (tmp_buf));
+				if (nflag) {
+					Printf("%s is alive\n", tmp_buf);
+				} else {
+					Printf("%s (%s) is alive\n",
+					    targethost, tmp_buf);
+				}
+			}
+			if (reply_matched_current_target) {
+				/*
+				 * Let's get things going again, but now
+				 * ping will start sending to next target IP
+				 * address.
+				 */
+				send_scheduled_probe();
+				(void) sigset(SIGALRM, sigalrm_handler);
+				schedule_sigalrm();
+			}
+			return;
+		} else {
+			/*
+			 * If we are not moving to next targetaddr, let's
+			 * release the SIGALRM now. We don't want to stall in
+			 * the middle of probing a targetaddr if the pr_name()
+			 * call (see below) takes longer.
+			 */
+			if (!last_reply_from_targetaddr)
+				(void) sigrelse(SIGALRM);
+			/* else, we'll release it later */
+		}
+
+		dst_addr.addr6 = ip6h->ip6_dst;
+		if (valid_reply) {
+			Printf("%d bytes from %s: ", cc,
+			    pr_name((char *)&from6->sin6_addr, AF_INET6));
+			Printf("udp_port=%d. ", ntohs(up->uh_dport));
+			print_newline = _B_TRUE;
+		} else if (is_a_target(ai_dst, &dst_addr)|| verbose) {
+			if (icmp6->icmp6_code >= A_CNT(unreach6)) {
+				Printf("ICMPv6 %d Unreachable from gateway "
+				    "%s\n", icmp6->icmp6_code,
+				    pr_name((char *)&from6->sin6_addr,
+					AF_INET6));
+			} else {
+				Printf("ICMPv6 %s from gateway %s\n",
+				    unreach6[icmp6->icmp6_code],
+				    pr_name((char *)&from6->sin6_addr,
+					AF_INET6));
+			}
+			Printf(" for %s from %s", pr_protocol(last_hdr),
+			    pr_name((char *)&ip6h->ip6_src, AF_INET6));
+			Printf(" to %s", pr_name((char *)&ip6h->ip6_dst,
+			    AF_INET6));
+			if (last_hdr == IPPROTO_TCP || last_hdr == IPPROTO_UDP)
+				Printf(" port %d ", ntohs(up->uh_dport));
+			print_newline = _B_TRUE;
+		}
+
+		/*
+		 * Update and print the stats, if it's a valid reply and
+		 * contains a timestamp.
+		 */
+		if (valid_reply && datalen >= sizeof (struct timeval) &&
+		    cc_left >= sizeof (struct timeval)) {
+			/* LINTED */
+			tp = (struct timeval *)((char *)up +
+			    sizeof (struct udphdr));
+			(void) tvsub(&tv, tp);
+			triptime = (int64_t)tv.tv_sec * MICROSEC + tv.tv_usec;
+			Printf("time=" TIMEFORMAT " ms", triptime/1000.0);
+			tsum += triptime;
+			tsum2 += triptime*triptime;
+			if (triptime < tmin)
+				tmin = triptime;
+			if (triptime > tmax)
+				tmax = triptime;
+			print_newline = _B_TRUE;
+		}
+		if (print_newline)
+			(void) putchar('\n');
+		/*
+		 * If it's stats, probe-all, npackets > 0, and we received reply
+		 * for the last probe sent to this target address, then we
+		 * don't need to wait anymore, let's move on to next target
+		 * address, now!
+		 */
+		if (last_reply_from_targetaddr) {
+			(void) alarm(0);	/* cancel alarm */
+			current_targetaddr->probing_done = _B_TRUE;
+			(void) sigrelse(SIGALRM);
+			send_scheduled_probe();
+			schedule_sigalrm();
+		}
+		break;
+
+	case ICMP6_PACKET_TOO_BIG:
+		/* LINTED */
+		ip6h = (ip6_t *)((char *)icmp6 + ICMP6_MINLEN);
+		if (cc_left < sizeof (ip6_t)) {
+			if (verbose) {
+				Printf("packet too short (%d bytes) from %s\n",
+				    cc, pr_name((char *)&from6->sin6_addr,
+				    AF_INET6));
+			}
+			return;
+		}
+		ip6hdr_len = IPv6_hdrlen(ip6h, cc_left, &last_hdr);
+
+		dst_addr.addr6 = ip6h->ip6_dst;
+		if (is_a_target(ai_dst, &dst_addr) || verbose) {
+			Printf("ICMPv6 packet too big from %s\n",
+			    pr_name((char *)&from6->sin6_addr, AF_INET6));
+
+			Printf(" for %s from %s", pr_protocol(last_hdr),
+			    pr_name((char *)&ip6h->ip6_src, AF_INET6));
+			Printf(" to %s", pr_name((char *)&ip6h->ip6_dst,
+			    AF_INET6));
+			if ((last_hdr == IPPROTO_TCP ||
+			    last_hdr == IPPROTO_UDP) &&
+			    (cc_left >= (ip6hdr_len + 4))) {
+				/* LINTED */
+				up = (struct udphdr *)
+				    ((char *)ip6h + ip6hdr_len);
+				Printf(" port %d ", ntohs(up->uh_dport));
+			}
+			Printf(" MTU = %d\n", ntohl(icmp6->icmp6_mtu));
+		}
+		break;
+
+	case ICMP6_TIME_EXCEEDED:
+		/* LINTED */
+		ip6h = (ip6_t *)((char *)icmp6 + ICMP6_MINLEN);
+		if (cc_left < sizeof (ip6_t)) {
+			if (verbose) {
+				Printf("packet too short (%d bytes) from %s\n",
+				    cc,
+				    pr_name((char *)&from6->sin6_addr,
+					AF_INET6));
+			}
+			return;
+		}
+		ip6hdr_len = IPv6_hdrlen(ip6h, cc_left, &last_hdr);
+
+		dst_addr.addr6 = ip6h->ip6_dst;
+		if (is_a_target(ai_dst, &dst_addr) || verbose) {
+			if (icmp6->icmp6_code >= A_CNT(timexceed6)) {
+				Printf("ICMPv6 %d time exceeded from %s\n",
+				    icmp6->icmp6_code,
+				    pr_name((char *)&from6->sin6_addr,
+					AF_INET6));
+			} else {
+				Printf("ICMPv6 %s from %s\n",
+				    timexceed6[icmp6->icmp6_code],
+				    pr_name((char *)&from6->sin6_addr,
+					AF_INET6));
+			}
+			Printf(" for %s from %s", pr_protocol(last_hdr),
+			    pr_name((char *)&ip6h->ip6_src, AF_INET6));
+			Printf(" to %s", pr_name((char *)&ip6h->ip6_dst,
+			    AF_INET6));
+			if ((last_hdr == IPPROTO_TCP ||
+			    last_hdr == IPPROTO_UDP) &&
+			    (cc_left >= (ip6hdr_len + 4))) {
+				/* LINTED */
+				up = (struct udphdr *)
+				    ((char *)ip6h + ip6hdr_len);
+				Printf(" port %d", ntohs(up->uh_dport));
+			}
+			(void) putchar('\n');
+		}
+		break;
+
+	case ICMP6_PARAM_PROB:
+		/* LINTED */
+		ip6h = (ip6_t *)((char *)icmp6 + ICMP6_MINLEN);
+		if (cc_left < sizeof (ip6_t)) {
+			if (verbose) {
+				Printf("packet too short (%d bytes) from %s\n",
+				    cc,
+				    pr_name((char *)&from6->sin6_addr,
+					AF_INET6));
+			}
+			return;
+		}
+		ip6hdr_len = IPv6_hdrlen(ip6h, cc_left, &last_hdr);
+
+		dst_addr.addr6 = ip6h->ip6_dst;
+		if (is_a_target(ai_dst, &dst_addr) || verbose) {
+			if (icmp6->icmp6_code >= A_CNT(param_prob6)) {
+				Printf("ICMPv6 %d parameter problem from %s\n",
+				    icmp6->icmp6_code,
+				    pr_name((char *)&from6->sin6_addr,
+					AF_INET6));
+			} else {
+				Printf("ICMPv6 %s from %s\n",
+				    param_prob6[icmp6->icmp6_code],
+				    pr_name((char *)&from6->sin6_addr,
+					AF_INET6));
+			}
+			icmp6->icmp6_pptr = ntohl(icmp6->icmp6_pptr);
+			Printf(" in byte %d", icmp6->icmp6_pptr);
+			if (icmp6->icmp6_pptr <= ip6hdr_len) {
+				Printf(" (value 0x%x)",
+				    *((uchar_t *)ip6h + icmp6->icmp6_pptr));
+			}
+			Printf(" for %s from %s", pr_protocol(last_hdr),
+			    pr_name((char *)&ip6h->ip6_src, AF_INET6));
+			Printf(" to %s", pr_name((char *)&ip6h->ip6_dst,
+			    AF_INET6));
+			if ((last_hdr == IPPROTO_TCP ||
+			    last_hdr == IPPROTO_UDP) &&
+			    (cc_left >= (ip6hdr_len + 4))) {
+				/* LINTED */
+				up = (struct udphdr *)
+				    ((char *)ip6h + ip6hdr_len);
+				Printf(" port %d", ntohs(up->uh_dport));
+			}
+			(void) putchar('\n');
+		}
+		break;
+
+	case ICMP6_ECHO_REQUEST:
+		return;
+
+	case ICMP6_ECHO_REPLY:
+		if (ntohs(icmp6->icmp6_id) == ident) {
+			if (!use_udp)
+				valid_reply = _B_TRUE;
+			else
+				valid_reply = _B_FALSE;
+		} else {
+			return;
+		}
+
+		if (valid_reply) {
+			/*
+			 * For this valid reply, if we are still sending to
+			 * this target IP address, we'd like to do some
+			 * updates to targetaddr, so hold SIGALRMs.
+			 */
+			(void) sighold(SIGALRM);
+			is_alive = _B_TRUE;
+			nreceived++;
+			reply_matched_current_target =
+			    seq_match(current_targetaddr->starting_seq_num,
+				current_targetaddr->num_sent,
+				ntohs(icmp6->icmp6_seq));
+			if (reply_matched_current_target) {
+				current_targetaddr->got_reply = _B_TRUE;
+				nreceived_last_target++;
+				/*
+				 * Determine if stats, probe-all, and
+				 * npackets != 0, and this is the reply for
+				 * the last probe we sent to current target
+				 * address.
+				 */
+				if (stats && probe_all && npackets > 0 &&
+				    ((current_targetaddr->starting_seq_num +
+				    current_targetaddr->num_probes - 1) %
+				    (MAX_ICMP_SEQ + 1) ==
+				    ntohs(icmp6->icmp6_seq)) &&
+				    (current_targetaddr->num_probes ==
+				    current_targetaddr->num_sent))
+					last_reply_from_targetaddr = _B_TRUE;
+			} else {
+				/*
+				 * If it's just probe_all and we just received
+				 * a reply from a target address we were
+				 * probing and had timed out (now we are probing
+				 * some other target address), we ignore
+				 * this reply.
+				 */
+				if (probe_all && !stats) {
+					valid_reply = _B_FALSE;
+					/*
+					 * Only if it's verbose, we get a
+					 * message regarding this reply,
+					 * otherwise we are done here.
+					 */
+					if (!verbose) {
+						(void) sigrelse(SIGALRM);
+						return;
+					}
+				}
+			}
+		}
+
+		if (!stats && valid_reply) {
+			/*
+			 * if we are still sending to the same target address,
+			 * then stop it, because we know it's alive.
+			 */
+			if (reply_matched_current_target) {
+				(void) alarm(0);	/* cancel alarm */
+				(void) sigset(SIGALRM, SIG_IGN);
+				current_targetaddr->probing_done = _B_TRUE;
+			}
+			(void) sigrelse(SIGALRM);
+
+			if (!probe_all) {
+				Printf("%s is alive\n", targethost);
+			} else {
+				/*
+				 * If we are using send_reply, the real
+				 * target address is not the src address of the
+				 * replies. Use icmp_seq to find out where this
+				 * probe was sent to.
+				 */
+				if (send_reply) {
+					(void) find_dstaddr(
+					    ntohs(icmp6->icmp6_seq), &dst_addr);
+					(void) inet_ntop(AF_INET6,
+					    (void *)&dst_addr.addr6,
+					    tmp_buf, sizeof (tmp_buf));
+				} else {
+					(void) inet_ntop(AF_INET6,
+					    (void *)&from6->sin6_addr,
+					    tmp_buf, sizeof (tmp_buf));
+				}
+
+				if (nflag) {
+					Printf("%s is alive\n", tmp_buf);
+				} else {
+					Printf("%s (%s) is alive\n",
+					    targethost, tmp_buf);
+				}
+			}
+			if (reply_matched_current_target) {
+				/*
+				 * Let's get things going again, but now
+				 * ping will start sending to next target IP
+				 * address.
+				 */
+				send_scheduled_probe();
+				(void) sigset(SIGALRM, sigalrm_handler);
+				schedule_sigalrm();
+			}
+			return;
+		} else {
+			/*
+			 * If we are not moving to next targetaddr, let's
+			 * release the SIGALRM now. We don't want to stall in
+			 * the middle of probing a targetaddr if the pr_name()
+			 * call (see below) takes longer.
+			 */
+			if (!last_reply_from_targetaddr)
+				(void) sigrelse(SIGALRM);
+			/* else, we'll release it later */
+		}
+
+		/*
+		 * If we are using send_reply, the real target address is
+		 * not the src address of the replies. Use icmp_seq to find out
+		 * where this probe was sent to.
+		 */
+		if (send_reply) {
+			(void) find_dstaddr(ntohs(icmp6->icmp6_seq), &dst_addr);
+			Printf("%d bytes from %s: ", cc,
+			    pr_name((char *)&dst_addr.addr6,  AF_INET6));
+		} else {
+			Printf("%d bytes from %s: ", cc,
+			    pr_name((char *)&from6->sin6_addr, AF_INET6));
+		}
+		Printf("icmp_seq=%d. ", ntohs(icmp6->icmp6_seq));
+
+		if (valid_reply && datalen >= sizeof (struct timeval) &&
+		    cc_left >= sizeof (struct timeval)) {
+			/* LINTED */
+			tp = (struct timeval *)&icmp6->icmp6_data16[2];
+			(void) tvsub(&tv, tp);
+			triptime = (int64_t)tv.tv_sec * MICROSEC + tv.tv_usec;
+			Printf("time=" TIMEFORMAT " ms", triptime/1000.0);
+			tsum += triptime;
+			tsum2 += triptime*triptime;
+			if (triptime < tmin)
+				tmin = triptime;
+			if (triptime > tmax)
+				tmax = triptime;
+		}
+		(void) putchar('\n');
+		/*
+		 * If it's stats, probe-all, npackets > 0, and we received reply
+		 * for the last probe sent to this target address, then we
+		 * don't need to wait anymore, let's move on to next target
+		 * address, now!
+		 */
+		if (last_reply_from_targetaddr) {
+			(void) alarm(0);	/* cancel alarm */
+			current_targetaddr->probing_done = _B_TRUE;
+			(void) sigrelse(SIGALRM);
+			send_scheduled_probe();
+			schedule_sigalrm();
+		}
+		break;
+
+	case MLD_LISTENER_QUERY:
+	case MLD_LISTENER_REPORT:
+	case MLD_LISTENER_REDUCTION:
+	case ND_ROUTER_SOLICIT:
+	case ND_ROUTER_ADVERT:
+	case ND_NEIGHBOR_SOLICIT:
+	case ND_NEIGHBOR_ADVERT:
+		return;
+
+	case ND_REDIRECT:
+		nd_rdrct = (nd_redirect_t *)icmp6;
+
+		if (cc_left < sizeof (nd_redirect_t) - ICMP6_MINLEN) {
+			if (verbose) {
+				Printf("packet too short (%d bytes) from %s\n",
+				    cc,
+				    pr_name((char *)&from6->sin6_addr,
+					AF_INET6));
+			}
+			return;
+		}
+		dst_addr.addr6 = nd_rdrct->nd_rd_dst;
+		if (is_a_target(ai_dst, &dst_addr) || verbose) {
+			Printf("ICMPv6 redirect from gateway %s\n",
+			    pr_name((char *)&from6->sin6_addr, AF_INET6));
+
+			Printf(" to %s",
+			    pr_name((char *)&nd_rdrct->nd_rd_target, AF_INET6));
+			Printf(" for %s\n",
+			    pr_name((char *)&nd_rdrct->nd_rd_dst, AF_INET6));
+		}
+		break;
+
+	default:
+		if (verbose) {
+			Printf("%d bytes from %s:\n", cc,
+			    pr_name((char *)&from6->sin6_addr, AF_INET6));
+			Printf("icmp6_type=%d (%s) ", icmp6->icmp6_type,
+			    pr_type6(icmp6->icmp6_type));
+			Printf("icmp6_code=%d\n", icmp6->icmp6_code);
+			for (i = 0; i < 12; i++) {
+				Printf("x%2.2x: x%8.8x\n",
+				    i * sizeof (int32_t), *intp++);
+			}
+		}
+		break;
+	}
+
+	/*
+	 * If it's verbose mode and we recv'd ancillary data, print extension
+	 * headers.
+	 */
+	if (verbose && msg->msg_controllen > 0)
+		pr_ext_headers(msg);
+}
+
+/*
+ * Convert an ICMP6 "type" field to a printable string.
+ */
+static char *
+pr_type6(uchar_t icmp6_type)
+{
+	static struct icmptype_table ttab6[] = {
+		{ICMP6_DST_UNREACH,		"Dest Unreachable"},
+		{ICMP6_PACKET_TOO_BIG,		"Packet Too Big"},
+		{ICMP6_TIME_EXCEEDED,		"Time Exceeded"},
+		{ICMP6_PARAM_PROB,		"Parameter Problem"},
+		{ICMP6_ECHO_REQUEST,		"Echo Request"},
+		{ICMP6_ECHO_REPLY,		"Echo Reply"},
+		{MLD_LISTENER_QUERY,		"Multicast Listener Query"},
+		{MLD_LISTENER_REPORT,		"Multicast Listener Report"},
+		{MLD_LISTENER_REDUCTION,	"Multicast Listener Done"},
+		{ND_ROUTER_SOLICIT,		"Router Solicitation"},
+		{ND_ROUTER_ADVERT,		"Router Advertisement"},
+		{ND_NEIGHBOR_SOLICIT,		"Neighbor Solicitation"},
+		{ND_NEIGHBOR_ADVERT,		"Neighbor Advertisement"},
+		{ND_REDIRECT,			"Redirect Message"},
+	};
+	int i;
+
+	for (i = 0; i < A_CNT(ttab6); i++) {
+		if (ttab6[i].type == icmp6_type)
+			return (ttab6[i].message);
+
+	}
+
+	return ("OUT-OF-RANGE");
+}
+
+/*
+ * Return the length of the IPv6 related headers (including extension headers).
+ * It also sets the *last_hdr_rtrn to the first upper layer protocol header
+ * following IPv6 header and extension headers. If print_flag is _B_TRUE, it
+ * prints extension headers.
+ */
+static int
+IPv6_hdrlen(ip6_t *ip6h, int pkt_len, uint8_t *last_hdr_rtrn)
+{
+	int length;
+	int exthdrlength;
+	uint8_t nexthdr;
+	uint8_t *whereptr;
+	ip6_hbh_t *hbhhdr;
+	ip6_dest_t *desthdr;
+	ip6_rthdr_t *rthdr;
+	ip6_frag_t *fraghdr;
+	uint8_t	*endptr;
+
+	length = sizeof (ip6_t);
+
+	whereptr = ((uint8_t *)&ip6h[1]); 	/* point to next hdr */
+	endptr = ((uint8_t *)ip6h) + pkt_len;
+
+	nexthdr = ip6h->ip6_nxt;
+	*last_hdr_rtrn = IPPROTO_NONE;
+
+	if (whereptr >= endptr)
+		return (length);
+
+	while (whereptr < endptr) {
+		*last_hdr_rtrn = nexthdr;
+		switch (nexthdr) {
+		case IPPROTO_HOPOPTS:
+			hbhhdr = (ip6_hbh_t *)whereptr;
+			exthdrlength = 8 * (hbhhdr->ip6h_len + 1);
+			if ((uchar_t *)hbhhdr + exthdrlength > endptr)
+				return (length);
+			nexthdr = hbhhdr->ip6h_nxt;
+			length += exthdrlength;
+			break;
+
+		case IPPROTO_DSTOPTS:
+			desthdr = (ip6_dest_t *)whereptr;
+			exthdrlength = 8 * (desthdr->ip6d_len + 1);
+			if ((uchar_t *)desthdr + exthdrlength > endptr)
+				return (length);
+			nexthdr = desthdr->ip6d_nxt;
+			length += exthdrlength;
+			break;
+
+		case IPPROTO_ROUTING:
+			rthdr = (ip6_rthdr_t *)whereptr;
+			exthdrlength = 8 * (rthdr->ip6r_len + 1);
+			if ((uchar_t *)rthdr + exthdrlength > endptr)
+				return (length);
+			nexthdr = rthdr->ip6r_nxt;
+			length += exthdrlength;
+			break;
+
+		case IPPROTO_FRAGMENT:
+			/* LINTED */
+			fraghdr = (ip6_frag_t *)whereptr;
+			if ((uchar_t *)&fraghdr[1] > endptr)
+				return (length);
+			nexthdr = fraghdr->ip6f_nxt;
+			length += sizeof (struct ip6_frag);
+			break;
+
+		case IPPROTO_NONE:
+		default:
+			return (length);
+		}
+		whereptr = (uint8_t *)ip6h + length;
+	}
+	*last_hdr_rtrn = nexthdr;
+
+	return (length);
+}
+
+/*
+ * Print extension headers
+ */
+static void
+pr_ext_headers(struct msghdr *msg)
+{
+	struct cmsghdr *cmsg;
+
+	Printf("  IPv6 extension headers: ");
+
+	for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
+	    cmsg = CMSG_NXTHDR(msg, cmsg)) {
+		if (cmsg->cmsg_level == IPPROTO_IPV6) {
+			switch (cmsg->cmsg_type) {
+			case IPV6_HOPOPTS:
+				Printf(" <hop-by-hop options>");
+				break;
+
+			case IPV6_DSTOPTS:
+				Printf(" <destination options (after routing"
+				    "header)>");
+				break;
+
+			case IPV6_RTHDRDSTOPTS:
+				Printf(" <destination options (before routing"
+				    "header)>");
+				break;
+
+			case IPV6_RTHDR:
+				pr_rthdr((uchar_t *)CMSG_DATA(cmsg));
+				break;
+
+			default:
+				Printf(" <option type %d>", cmsg->cmsg_type);
+				break;
+			}
+		}
+	}
+	(void) putchar('\n');
+}
+
+/*
+ * Print the routing header 0 information
+ */
+static void
+pr_rthdr(uchar_t *buf)
+{
+	ip6_rthdr_t *rthdr;
+	ip6_rthdr0_t *rthdr0;
+	struct in6_addr *gw_addr;
+	int i, num_addr;
+
+	rthdr = (ip6_rthdr_t *)buf;
+	Printf(" <type %d routing header, segleft %u> ",
+	    rthdr->ip6r_type, rthdr->ip6r_segleft);
+
+	if (rthdr->ip6r_type == 0) {
+		/* LINTED */
+		rthdr0 = (ip6_rthdr0_t *)buf;
+		gw_addr = (struct in6_addr *)(rthdr0 + 1);
+		num_addr = rthdr0->ip6r0_len / 2;
+
+		for (i = 0; i < num_addr; i++) {
+			Printf("%s", pr_name((char *)gw_addr, AF_INET6));
+			if (i == (num_addr - rthdr0->ip6r0_segleft))
+				Printf("(Current)");
+			gw_addr++;
+			if (i != num_addr - 1)
+				Printf(",  ");
+		}
+	}
+}