17c478bd9Sstevel@tonic-gate /*
2*0406ceaaSmeem  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
37c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
47c478bd9Sstevel@tonic-gate  */
57c478bd9Sstevel@tonic-gate 
67c478bd9Sstevel@tonic-gate 
77c478bd9Sstevel@tonic-gate /*
87c478bd9Sstevel@tonic-gate  * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997
97c478bd9Sstevel@tonic-gate  *	The Regents of the University of California.  All rights reserved.
107c478bd9Sstevel@tonic-gate  *
117c478bd9Sstevel@tonic-gate  * Redistribution and use in source and binary forms, with or without
127c478bd9Sstevel@tonic-gate  * modification, are permitted provided that: (1) source code distributions
137c478bd9Sstevel@tonic-gate  * retain the above copyright notice and this paragraph in its entirety, (2)
147c478bd9Sstevel@tonic-gate  * distributions including binary code include the above copyright notice and
157c478bd9Sstevel@tonic-gate  * this paragraph in its entirety in the documentation or other materials
167c478bd9Sstevel@tonic-gate  * provided with the distribution, and (3) all advertising materials mentioning
177c478bd9Sstevel@tonic-gate  * features or use of this software display the following acknowledgement:
187c478bd9Sstevel@tonic-gate  * ``This product includes software developed by the University of California,
197c478bd9Sstevel@tonic-gate  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
207c478bd9Sstevel@tonic-gate  * the University nor the names of its contributors may be used to endorse
217c478bd9Sstevel@tonic-gate  * or promote products derived from this software without specific prior
227c478bd9Sstevel@tonic-gate  * written permission.
237c478bd9Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
247c478bd9Sstevel@tonic-gate  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
257c478bd9Sstevel@tonic-gate  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
267c478bd9Sstevel@tonic-gate  *
277c478bd9Sstevel@tonic-gate  *
287c478bd9Sstevel@tonic-gate  * @(#)$Header: traceroute.c,v 1.49 97/06/13 02:30:23 leres Exp $ (LBL)
297c478bd9Sstevel@tonic-gate  */
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate #include <sys/socket.h>
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate #include <stdio.h>
347c478bd9Sstevel@tonic-gate #include <stdlib.h>
357c478bd9Sstevel@tonic-gate #include <ctype.h>
367c478bd9Sstevel@tonic-gate #include <strings.h>
377c478bd9Sstevel@tonic-gate #include <libintl.h>
387c478bd9Sstevel@tonic-gate #include <errno.h>
397c478bd9Sstevel@tonic-gate #include <netdb.h>
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate #include <netinet/in_systm.h>
427c478bd9Sstevel@tonic-gate #include <netinet/in.h>
437c478bd9Sstevel@tonic-gate #include <netinet/ip.h>
447c478bd9Sstevel@tonic-gate #include <netinet/ip_var.h>
457c478bd9Sstevel@tonic-gate #include <netinet/ip_icmp.h>
467c478bd9Sstevel@tonic-gate #include <netinet/udp.h>
477c478bd9Sstevel@tonic-gate #include <netinet/udp_var.h>
487c478bd9Sstevel@tonic-gate #include <netinet/ip6.h>
497c478bd9Sstevel@tonic-gate #include <netinet/icmp6.h>
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate #include <arpa/inet.h>
527c478bd9Sstevel@tonic-gate 
53*0406ceaaSmeem #include <libinetutil.h>
547c478bd9Sstevel@tonic-gate #include "traceroute.h"
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate int check_reply6(struct msghdr *, int, int, uchar_t *, uchar_t *);
577c478bd9Sstevel@tonic-gate void *find_ancillary_data(struct msghdr *, int, int);
587c478bd9Sstevel@tonic-gate extern char *inet_name(union any_in_addr *, int);
597c478bd9Sstevel@tonic-gate static int IPv6_hdrlen(ip6_t *, int, uint8_t *);
607c478bd9Sstevel@tonic-gate static char *pr_type6(uchar_t);
617c478bd9Sstevel@tonic-gate void print_addr6(uchar_t *, int, struct sockaddr *);
627c478bd9Sstevel@tonic-gate boolean_t print_icmp_other6(uchar_t, uchar_t);
637c478bd9Sstevel@tonic-gate void send_probe6(int, struct msghdr *, struct ip *, int, int,
647c478bd9Sstevel@tonic-gate     struct timeval *, int);
657c478bd9Sstevel@tonic-gate void set_ancillary_data(struct msghdr *, int, union any_in_addr *, int, uint_t);
667c478bd9Sstevel@tonic-gate struct ip *set_buffers6(int);
677c478bd9Sstevel@tonic-gate static boolean_t update_hoplimit_ancillary_data(struct msghdr *, int);
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate /*
707c478bd9Sstevel@tonic-gate  * prepares the buffer to be sent as an IP datagram
717c478bd9Sstevel@tonic-gate  */
727c478bd9Sstevel@tonic-gate struct ip *
set_buffers6(int plen)737c478bd9Sstevel@tonic-gate set_buffers6(int plen)
747c478bd9Sstevel@tonic-gate {
757c478bd9Sstevel@tonic-gate 	struct ip *outip;
767c478bd9Sstevel@tonic-gate 	uchar_t *outp;
777c478bd9Sstevel@tonic-gate 	struct udphdr *outudp;
787c478bd9Sstevel@tonic-gate 	struct icmp *outicmp;
797c478bd9Sstevel@tonic-gate 	int optlen = 0;
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate 	outip = (struct ip *)malloc((size_t)plen);
827c478bd9Sstevel@tonic-gate 	if (outip == NULL) {
837c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: malloc: %s\n", prog, strerror(errno));
847c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
857c478bd9Sstevel@tonic-gate 	}
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate 	if (gw_count > 0) {
887c478bd9Sstevel@tonic-gate 		/* ip6_rthdr0 structure includes one gateway address */
897c478bd9Sstevel@tonic-gate 		optlen = sizeof (struct ip6_rthdr0) +
907c478bd9Sstevel@tonic-gate 		    gw_count * sizeof (struct in6_addr);
917c478bd9Sstevel@tonic-gate 	}
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate 	(void) memset((char *)outip, 0, (size_t)plen);
947c478bd9Sstevel@tonic-gate 	outp = (uchar_t *)(outip + 1);
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate 	if (useicmp) {
977c478bd9Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
987c478bd9Sstevel@tonic-gate 		outicmp = (struct icmp *)outp;
997c478bd9Sstevel@tonic-gate 		outicmp->icmp_type = ICMP6_ECHO_REQUEST;
1007c478bd9Sstevel@tonic-gate 		outicmp->icmp_id = htons(ident);
1017c478bd9Sstevel@tonic-gate 	} else {
1027c478bd9Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
1037c478bd9Sstevel@tonic-gate 		outudp = (struct udphdr *)outp;
1047c478bd9Sstevel@tonic-gate 		/*
1057c478bd9Sstevel@tonic-gate 		 * "source port" is set at bind() call, so we don't do it
1067c478bd9Sstevel@tonic-gate 		 * again
1077c478bd9Sstevel@tonic-gate 		 */
1087c478bd9Sstevel@tonic-gate 		outudp->uh_ulen = htons((ushort_t)(plen -
1097c478bd9Sstevel@tonic-gate 		    (sizeof (struct ip6_hdr) + optlen)));
1107c478bd9Sstevel@tonic-gate 	}
1117c478bd9Sstevel@tonic-gate 
1127c478bd9Sstevel@tonic-gate 	return (outip);
1137c478bd9Sstevel@tonic-gate }
1147c478bd9Sstevel@tonic-gate 
1157c478bd9Sstevel@tonic-gate /*
1167c478bd9Sstevel@tonic-gate  * Initialize the msghdr for specifying hoplimit, outgoing interface and routing
1177c478bd9Sstevel@tonic-gate  * header for the probe packets.
1187c478bd9Sstevel@tonic-gate  */
1197c478bd9Sstevel@tonic-gate void
set_ancillary_data(struct msghdr * msgp,int hoplimit,union any_in_addr * gwIPlist,int gw_cnt,uint_t if_index)1207c478bd9Sstevel@tonic-gate set_ancillary_data(struct msghdr *msgp, int hoplimit,
1217c478bd9Sstevel@tonic-gate     union any_in_addr *gwIPlist, int gw_cnt, uint_t if_index)
1227c478bd9Sstevel@tonic-gate {
1237c478bd9Sstevel@tonic-gate 	size_t hoplimit_space;
1247c478bd9Sstevel@tonic-gate 	size_t rthdr_space;
1257c478bd9Sstevel@tonic-gate 	size_t pktinfo_space;
1267c478bd9Sstevel@tonic-gate 	size_t bufspace;
1277c478bd9Sstevel@tonic-gate 	struct cmsghdr *cmsgp;
1287c478bd9Sstevel@tonic-gate 	uchar_t *cmsg_datap;
1297c478bd9Sstevel@tonic-gate 	int i;
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 	msgp->msg_control = NULL;
1327c478bd9Sstevel@tonic-gate 	msgp->msg_controllen = 0;
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate 	/*
1357c478bd9Sstevel@tonic-gate 	 * Need to figure out size of buffer needed for ancillary data
1367c478bd9Sstevel@tonic-gate 	 * containing routing header and packet info options.
1377c478bd9Sstevel@tonic-gate 	 *
1387c478bd9Sstevel@tonic-gate 	 * Portable heuristic to compute upper bound on space needed for
1397c478bd9Sstevel@tonic-gate 	 * N ancillary data options. It assumes up to _MAX_ALIGNMENT padding
1407c478bd9Sstevel@tonic-gate 	 * after both header and data as the worst possible upper bound on space
1417c478bd9Sstevel@tonic-gate 	 * consumed by padding.
1427c478bd9Sstevel@tonic-gate 	 * It also adds one extra "sizeof (struct cmsghdr)" for the last option.
1437c478bd9Sstevel@tonic-gate 	 * This is needed because we would like to use CMSG_NXTHDR() while
1447c478bd9Sstevel@tonic-gate 	 * composing the buffer. The CMSG_NXTHDR() macro is designed better for
1457c478bd9Sstevel@tonic-gate 	 * parsing than composing the buffer. It requires the pointer it returns
1467c478bd9Sstevel@tonic-gate 	 * to leave space in buffer for addressing a cmsghdr and we want to make
1477c478bd9Sstevel@tonic-gate 	 * sure it works for us while we skip beyond the last ancillary data
1487c478bd9Sstevel@tonic-gate 	 * option.
1497c478bd9Sstevel@tonic-gate 	 *
1507c478bd9Sstevel@tonic-gate 	 * bufspace[i]  = sizeof(struct cmsghdr) + <pad after header> +
1517c478bd9Sstevel@tonic-gate 	 *		<option[i] content length> + <pad after data>;
1527c478bd9Sstevel@tonic-gate 	 *
1537c478bd9Sstevel@tonic-gate 	 * total_bufspace = bufspace[0] + bufspace[1] + ...
1547c478bd9Sstevel@tonic-gate 	 *		    ... + bufspace[N-1] + sizeof (struct cmsghdr);
1557c478bd9Sstevel@tonic-gate 	 */
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate 	rthdr_space = 0;
1587c478bd9Sstevel@tonic-gate 	pktinfo_space = 0;
1597c478bd9Sstevel@tonic-gate 	/* We'll always set the hoplimit of the outgoing packets */
1607c478bd9Sstevel@tonic-gate 	hoplimit_space = sizeof (int);
1617c478bd9Sstevel@tonic-gate 	bufspace = sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
1627c478bd9Sstevel@tonic-gate 	    hoplimit_space + _MAX_ALIGNMENT;
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate 	if (gw_cnt > 0) {
1657c478bd9Sstevel@tonic-gate 		rthdr_space = inet6_rth_space(IPV6_RTHDR_TYPE_0, gw_cnt);
1667c478bd9Sstevel@tonic-gate 		bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
1677c478bd9Sstevel@tonic-gate 		    rthdr_space + _MAX_ALIGNMENT;
1687c478bd9Sstevel@tonic-gate 	}
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate 	if (if_index != 0) {
1717c478bd9Sstevel@tonic-gate 		pktinfo_space = sizeof (struct in6_pktinfo);
1727c478bd9Sstevel@tonic-gate 		bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
1737c478bd9Sstevel@tonic-gate 		    pktinfo_space + _MAX_ALIGNMENT;
1747c478bd9Sstevel@tonic-gate 	}
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate 	/*
1777c478bd9Sstevel@tonic-gate 	 * We need to temporarily set the msgp->msg_controllen to bufspace
1787c478bd9Sstevel@tonic-gate 	 * (we will later trim it to actual length used). This is needed because
1797c478bd9Sstevel@tonic-gate 	 * CMSG_NXTHDR() uses it to check we have not exceeded the bounds.
1807c478bd9Sstevel@tonic-gate 	 */
1817c478bd9Sstevel@tonic-gate 	bufspace += sizeof (struct cmsghdr);
1827c478bd9Sstevel@tonic-gate 	msgp->msg_controllen = bufspace;
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate 	msgp->msg_control = (struct cmsghdr *)malloc(bufspace);
1857c478bd9Sstevel@tonic-gate 	if (msgp->msg_control == NULL) {
1867c478bd9Sstevel@tonic-gate 		Fprintf(stderr, "%s: malloc %s\n", prog, strerror(errno));
1877c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
1887c478bd9Sstevel@tonic-gate 	}
1897c478bd9Sstevel@tonic-gate 	cmsgp = CMSG_FIRSTHDR(msgp);
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate 	/*
1927c478bd9Sstevel@tonic-gate 	 * Fill ancillary data. First hoplimit, then rthdr and pktinfo if
1937c478bd9Sstevel@tonic-gate 	 * needed.
1947c478bd9Sstevel@tonic-gate 	 */
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	/* set hoplimit ancillary data */
1977c478bd9Sstevel@tonic-gate 	cmsgp->cmsg_level = IPPROTO_IPV6;
1987c478bd9Sstevel@tonic-gate 	cmsgp->cmsg_type = IPV6_HOPLIMIT;
1997c478bd9Sstevel@tonic-gate 	cmsg_datap = CMSG_DATA(cmsgp);
2007c478bd9Sstevel@tonic-gate 	/* LINTED E_BAD_PTR_CAST_ALIGN */
2017c478bd9Sstevel@tonic-gate 	*(int *)cmsg_datap = hoplimit;
2027c478bd9Sstevel@tonic-gate 	cmsgp->cmsg_len = cmsg_datap + hoplimit_space - (uchar_t *)cmsgp;
2037c478bd9Sstevel@tonic-gate 	cmsgp = CMSG_NXTHDR(msgp, cmsgp);
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate 	/* set rthdr ancillary data if needed */
2067c478bd9Sstevel@tonic-gate 	if (gw_cnt > 0) {
2077c478bd9Sstevel@tonic-gate 		struct ip6_rthdr0 *rthdr0p;
2087c478bd9Sstevel@tonic-gate 
2097c478bd9Sstevel@tonic-gate 		cmsgp->cmsg_level = IPPROTO_IPV6;
2107c478bd9Sstevel@tonic-gate 		cmsgp->cmsg_type = IPV6_RTHDR;
2117c478bd9Sstevel@tonic-gate 		cmsg_datap = CMSG_DATA(cmsgp);
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 		/*
2147c478bd9Sstevel@tonic-gate 		 * Initialize rthdr structure
2157c478bd9Sstevel@tonic-gate 		 */
2167c478bd9Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
2177c478bd9Sstevel@tonic-gate 		rthdr0p = (struct ip6_rthdr0 *)cmsg_datap;
2187c478bd9Sstevel@tonic-gate 		if (inet6_rth_init(rthdr0p, rthdr_space,
2197c478bd9Sstevel@tonic-gate 		    IPV6_RTHDR_TYPE_0, gw_cnt) == NULL) {
2207c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: inet6_rth_init failed\n",
2217c478bd9Sstevel@tonic-gate 			    prog);
2227c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
2237c478bd9Sstevel@tonic-gate 		}
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate 		/*
2267c478bd9Sstevel@tonic-gate 		 * Stuff in gateway addresses
2277c478bd9Sstevel@tonic-gate 		 */
2287c478bd9Sstevel@tonic-gate 		for (i = 0; i < gw_cnt; i++) {
2297c478bd9Sstevel@tonic-gate 			if (inet6_rth_add(rthdr0p,
2307c478bd9Sstevel@tonic-gate 			    &gwIPlist[i].addr6) == -1) {
2317c478bd9Sstevel@tonic-gate 				Fprintf(stderr,
2327c478bd9Sstevel@tonic-gate 				    "%s: inet6_rth_add\n", prog);
2337c478bd9Sstevel@tonic-gate 				exit(EXIT_FAILURE);
2347c478bd9Sstevel@tonic-gate 			}
2357c478bd9Sstevel@tonic-gate 		}
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 		cmsgp->cmsg_len = cmsg_datap + rthdr_space - (uchar_t *)cmsgp;
2387c478bd9Sstevel@tonic-gate 		cmsgp = CMSG_NXTHDR(msgp, cmsgp);
2397c478bd9Sstevel@tonic-gate 	}
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate 	/* set pktinfo ancillary data if needed */
2427c478bd9Sstevel@tonic-gate 	if (if_index != 0) {
2437c478bd9Sstevel@tonic-gate 		struct in6_pktinfo *pktinfop;
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 		cmsgp->cmsg_level = IPPROTO_IPV6;
2467c478bd9Sstevel@tonic-gate 		cmsgp->cmsg_type = IPV6_PKTINFO;
2477c478bd9Sstevel@tonic-gate 		cmsg_datap = CMSG_DATA(cmsgp);
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
2507c478bd9Sstevel@tonic-gate 		pktinfop = (struct in6_pktinfo *)cmsg_datap;
2517c478bd9Sstevel@tonic-gate 		/*
2527c478bd9Sstevel@tonic-gate 		 * We don't know if pktinfop->ipi6_addr is aligned properly,
2537c478bd9Sstevel@tonic-gate 		 * therefore let's use bcopy, instead of assignment.
2547c478bd9Sstevel@tonic-gate 		 */
2557c478bd9Sstevel@tonic-gate 		(void) bcopy(&in6addr_any, &pktinfop->ipi6_addr,
2567c478bd9Sstevel@tonic-gate 		sizeof (struct in6_addr));
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 		/*
2597c478bd9Sstevel@tonic-gate 		 *  We can assume pktinfop->ipi6_ifindex is 32 bit aligned.
2607c478bd9Sstevel@tonic-gate 		 */
2617c478bd9Sstevel@tonic-gate 		pktinfop->ipi6_ifindex = if_index;
2627c478bd9Sstevel@tonic-gate 		cmsgp->cmsg_len = cmsg_datap + pktinfo_space - (uchar_t *)cmsgp;
2637c478bd9Sstevel@tonic-gate 		cmsgp = CMSG_NXTHDR(msgp, cmsgp);
2647c478bd9Sstevel@tonic-gate 	}
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate 	msgp->msg_controllen = (char *)cmsgp - (char *)msgp->msg_control;
2677c478bd9Sstevel@tonic-gate }
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate /*
2707c478bd9Sstevel@tonic-gate  * Parses the given msg->msg_control to find the IPV6_HOPLIMIT ancillary data
2717c478bd9Sstevel@tonic-gate  * and update the hoplimit.
2727c478bd9Sstevel@tonic-gate  * Returns _B_FALSE if it can't find IPV6_HOPLIMIT ancillary data, _B_TRUE
2737c478bd9Sstevel@tonic-gate  * otherwise.
2747c478bd9Sstevel@tonic-gate  */
2757c478bd9Sstevel@tonic-gate static boolean_t
update_hoplimit_ancillary_data(struct msghdr * msg,int hoplimit)2767c478bd9Sstevel@tonic-gate update_hoplimit_ancillary_data(struct msghdr *msg, int hoplimit)
2777c478bd9Sstevel@tonic-gate {
2787c478bd9Sstevel@tonic-gate 	struct cmsghdr *cmsg;
2797c478bd9Sstevel@tonic-gate 	int *intp;
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate 	for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
2827c478bd9Sstevel@tonic-gate 	    cmsg = CMSG_NXTHDR(msg, cmsg)) {
2837c478bd9Sstevel@tonic-gate 		if (cmsg->cmsg_level == IPPROTO_IPV6 &&
2847c478bd9Sstevel@tonic-gate 		    cmsg->cmsg_type == IPV6_HOPLIMIT) {
2857c478bd9Sstevel@tonic-gate 			/* LINTED E_BAD_PTR_CAST_ALIGN */
2867c478bd9Sstevel@tonic-gate 			intp = (int *)(CMSG_DATA(cmsg));
2877c478bd9Sstevel@tonic-gate 			*intp = hoplimit;
2887c478bd9Sstevel@tonic-gate 			return (_B_TRUE);
2897c478bd9Sstevel@tonic-gate 		}
2907c478bd9Sstevel@tonic-gate 	}
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate 	return (_B_FALSE);
2937c478bd9Sstevel@tonic-gate }
2947c478bd9Sstevel@tonic-gate 
2957c478bd9Sstevel@tonic-gate /*
2967c478bd9Sstevel@tonic-gate  * send a probe packet to the destination
2977c478bd9Sstevel@tonic-gate  */
2987c478bd9Sstevel@tonic-gate void
send_probe6(int sndsock,struct msghdr * msg6,struct ip * outip,int seq,int ttl,struct timeval * tp,int packlen)2997c478bd9Sstevel@tonic-gate send_probe6(int sndsock, struct msghdr *msg6, struct ip *outip, int seq,
3007c478bd9Sstevel@tonic-gate     int ttl, struct timeval *tp, int packlen)
3017c478bd9Sstevel@tonic-gate {
3027c478bd9Sstevel@tonic-gate 	uchar_t *outp;
3037c478bd9Sstevel@tonic-gate 	struct icmp *outicmp;
3047c478bd9Sstevel@tonic-gate 	struct outdata *outdata;
3057c478bd9Sstevel@tonic-gate 	struct iovec iov;
3067c478bd9Sstevel@tonic-gate 	int cc;
3077c478bd9Sstevel@tonic-gate 	int optlen = 0;
3087c478bd9Sstevel@tonic-gate 	int send_size;
3097c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *to6;
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	if (gw_count > 0) {
3127c478bd9Sstevel@tonic-gate 		/* ip6_rthdr0 structure includes one gateway address */
3137c478bd9Sstevel@tonic-gate 		optlen = sizeof (struct ip6_rthdr0) +
3147c478bd9Sstevel@tonic-gate 		    gw_count * sizeof (struct in6_addr);
3157c478bd9Sstevel@tonic-gate 	}
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate 	send_size = packlen - sizeof (struct ip6_hdr) - optlen;
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate 	/* if using UDP, further discount UDP header size */
3207c478bd9Sstevel@tonic-gate 	if (!useicmp)
3217c478bd9Sstevel@tonic-gate 		send_size -= sizeof (struct udphdr);
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 	/* initialize buffer pointers */
3247c478bd9Sstevel@tonic-gate 	outp = (uchar_t *)(outip + 1);
3257c478bd9Sstevel@tonic-gate 	/* LINTED E_BAD_PTR_CAST_ALIGN */
3267c478bd9Sstevel@tonic-gate 	outicmp = (struct icmp *)outp;
3277c478bd9Sstevel@tonic-gate 	/* LINTED E_BAD_PTR_CAST_ALIGN */
3287c478bd9Sstevel@tonic-gate 	outdata = (struct outdata *)(outp + ICMP6_MINLEN);
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate 	if (!update_hoplimit_ancillary_data(msg6, ttl)) {
3317c478bd9Sstevel@tonic-gate 		Fprintf(stderr,
3327c478bd9Sstevel@tonic-gate 		    "%s: can't find IPV6_HOPLIMIT ancillary data\n", prog);
3337c478bd9Sstevel@tonic-gate 		exit(EXIT_FAILURE);
3347c478bd9Sstevel@tonic-gate 	}
3357c478bd9Sstevel@tonic-gate 
3367c478bd9Sstevel@tonic-gate 	/* Payload */
3377c478bd9Sstevel@tonic-gate 	outdata->seq = seq;
3387c478bd9Sstevel@tonic-gate 	outdata->ttl = ttl;
3397c478bd9Sstevel@tonic-gate 	outdata->tv = *tp;
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate 	if (useicmp) {
3427c478bd9Sstevel@tonic-gate 		outicmp->icmp_seq = htons(seq);
3437c478bd9Sstevel@tonic-gate 	} else {
3447c478bd9Sstevel@tonic-gate 		to6 = (struct sockaddr_in6 *)msg6->msg_name;
3457c478bd9Sstevel@tonic-gate 		to6->sin6_port =  htons((port + seq) % (MAX_PORT + 1));
3467c478bd9Sstevel@tonic-gate 	}
3477c478bd9Sstevel@tonic-gate 
3487c478bd9Sstevel@tonic-gate 	iov.iov_base = outp;
3497c478bd9Sstevel@tonic-gate 	iov.iov_len = send_size;
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate 	msg6->msg_iov = &iov;
3527c478bd9Sstevel@tonic-gate 	msg6->msg_iovlen = 1;
3537c478bd9Sstevel@tonic-gate 
3547c478bd9Sstevel@tonic-gate 	cc = sendmsg(sndsock, msg6, 0);
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate 	if (cc < 0 || cc != send_size)  {
3577c478bd9Sstevel@tonic-gate 		if (cc < 0) {
3587c478bd9Sstevel@tonic-gate 			Fprintf(stderr, "%s: sendmsg: %s\n", prog,
3597c478bd9Sstevel@tonic-gate 			    strerror(errno));
3607c478bd9Sstevel@tonic-gate 		}
3617c478bd9Sstevel@tonic-gate 		Printf("%s: wrote %s %d chars, ret=%d\n",
3627c478bd9Sstevel@tonic-gate 		    prog, hostname, send_size, cc);
3637c478bd9Sstevel@tonic-gate 		(void) fflush(stdout);
3647c478bd9Sstevel@tonic-gate 	}
3657c478bd9Sstevel@tonic-gate }
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate /*
3687c478bd9Sstevel@tonic-gate  * Return a pointer to the ancillary data for the given cmsg_level and
3697c478bd9Sstevel@tonic-gate  * cmsg_type.
3707c478bd9Sstevel@tonic-gate  * If not found return NULL.
3717c478bd9Sstevel@tonic-gate  */
3727c478bd9Sstevel@tonic-gate void *
find_ancillary_data(struct msghdr * msg,int cmsg_level,int cmsg_type)3737c478bd9Sstevel@tonic-gate find_ancillary_data(struct msghdr *msg, int cmsg_level, int cmsg_type)
3747c478bd9Sstevel@tonic-gate {
3757c478bd9Sstevel@tonic-gate 	struct cmsghdr *cmsg;
3767c478bd9Sstevel@tonic-gate 
3777c478bd9Sstevel@tonic-gate 	for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
3787c478bd9Sstevel@tonic-gate 	    cmsg = CMSG_NXTHDR(msg, cmsg)) {
3797c478bd9Sstevel@tonic-gate 		if (cmsg->cmsg_level == cmsg_level &&
3807c478bd9Sstevel@tonic-gate 		    cmsg->cmsg_type == cmsg_type) {
3817c478bd9Sstevel@tonic-gate 			return (CMSG_DATA(cmsg));
3827c478bd9Sstevel@tonic-gate 		}
3837c478bd9Sstevel@tonic-gate 	}
3847c478bd9Sstevel@tonic-gate 	return (NULL);
3857c478bd9Sstevel@tonic-gate }
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate /*
3887c478bd9Sstevel@tonic-gate  * Check out the reply packet to see if it's what we were expecting.
3897c478bd9Sstevel@tonic-gate  * Returns REPLY_GOT_TARGET if the reply comes from the target
3907c478bd9Sstevel@tonic-gate  *         REPLY_GOT_GATEWAY if an intermediate gateway sends TIME_EXCEEDED
3917c478bd9Sstevel@tonic-gate  *         REPLY_GOT_OTHER for other kinds of unreachables indicating none of
3927c478bd9Sstevel@tonic-gate  *	   the above two cases
3937c478bd9Sstevel@tonic-gate  *
3947c478bd9Sstevel@tonic-gate  * It also sets the icmp type and icmp code values
3957c478bd9Sstevel@tonic-gate  */
3967c478bd9Sstevel@tonic-gate int
check_reply6(struct msghdr * msg,int cc,int seq,uchar_t * type,uchar_t * code)3977c478bd9Sstevel@tonic-gate check_reply6(struct msghdr *msg, int cc, int seq, uchar_t *type, uchar_t *code)
3987c478bd9Sstevel@tonic-gate {
3997c478bd9Sstevel@tonic-gate 	uchar_t *buf = msg->msg_iov->iov_base;
4007c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *from_in6 = (struct sockaddr_in6 *)msg->msg_name;
4017c478bd9Sstevel@tonic-gate 	icmp6_t *icp6;
4027c478bd9Sstevel@tonic-gate 	ulong_t ip6hdr_len;
4037c478bd9Sstevel@tonic-gate 	uint8_t last_hdr;
4047c478bd9Sstevel@tonic-gate 	int save_cc = cc;
4057c478bd9Sstevel@tonic-gate 	char temp_buf[INET6_ADDRSTRLEN];	/* use for inet_ntop() */
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate 	/* Ignore packets > 64k or control buffers that don't fit */
4087c478bd9Sstevel@tonic-gate 	if (msg->msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
4097c478bd9Sstevel@tonic-gate 		if (verbose) {
4107c478bd9Sstevel@tonic-gate 			Printf("Truncated message: msg_flags 0x%x from %s\n",
4117c478bd9Sstevel@tonic-gate 			    msg->msg_flags,
4127c478bd9Sstevel@tonic-gate 			    inet_ntop(AF_INET6,
4137c478bd9Sstevel@tonic-gate 			    (void *)&(from_in6->sin6_addr),
4147c478bd9Sstevel@tonic-gate 			    temp_buf, sizeof (temp_buf)));
4157c478bd9Sstevel@tonic-gate 		}
4167c478bd9Sstevel@tonic-gate 		return (REPLY_SHORT_PKT);
4177c478bd9Sstevel@tonic-gate 	}
4187c478bd9Sstevel@tonic-gate 	if (cc < ICMP6_MINLEN) {
4197c478bd9Sstevel@tonic-gate 		if (verbose) {
4207c478bd9Sstevel@tonic-gate 			Printf("packet too short (%d bytes) from %s\n",
4217c478bd9Sstevel@tonic-gate 			    cc,
4227c478bd9Sstevel@tonic-gate 			    inet_ntop(AF_INET6,
4237c478bd9Sstevel@tonic-gate 			    (void *)&(from_in6->sin6_addr),
4247c478bd9Sstevel@tonic-gate 			    temp_buf, sizeof (temp_buf)));
4257c478bd9Sstevel@tonic-gate 		}
4267c478bd9Sstevel@tonic-gate 		return (REPLY_SHORT_PKT);
4277c478bd9Sstevel@tonic-gate 	}
4287c478bd9Sstevel@tonic-gate 	/* LINTED E_BAD_PTR_CAST_ALIGN */
4297c478bd9Sstevel@tonic-gate 	icp6 = (icmp6_t *)buf;
4307c478bd9Sstevel@tonic-gate 	*type = icp6->icmp6_type;
4317c478bd9Sstevel@tonic-gate 	*code = icp6->icmp6_code;
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate 	/*
4347c478bd9Sstevel@tonic-gate 	 * traceroute interprets only ICMP6_TIME_EXCEED_TRANSIT,
4357c478bd9Sstevel@tonic-gate 	 * ICMP6_DST_UNREACH, ICMP6_ECHO_REPLY, ICMP6_PACKET_TOO_BIG and
4367c478bd9Sstevel@tonic-gate 	 * ICMP6_PARAMPROB_NEXTHEADER, ignores others
4377c478bd9Sstevel@tonic-gate 	 */
4387c478bd9Sstevel@tonic-gate 	if ((*type == ICMP6_TIME_EXCEEDED &&
4397c478bd9Sstevel@tonic-gate 	    *code == ICMP6_TIME_EXCEED_TRANSIT) ||
4407c478bd9Sstevel@tonic-gate 	    *type == ICMP6_DST_UNREACH || *type == ICMP6_ECHO_REPLY ||
4417c478bd9Sstevel@tonic-gate 	    *type == ICMP6_PACKET_TOO_BIG ||
4427c478bd9Sstevel@tonic-gate 	    (*type == ICMP6_PARAM_PROB &&
4437c478bd9Sstevel@tonic-gate 	    *code == ICMP6_PARAMPROB_NEXTHEADER)) {
4447c478bd9Sstevel@tonic-gate 		ip6_t *hip6;
4457c478bd9Sstevel@tonic-gate 		struct udphdr *up;
4467c478bd9Sstevel@tonic-gate 		icmp6_t *hicmp6;
4477c478bd9Sstevel@tonic-gate 
4487c478bd9Sstevel@tonic-gate 		cc -= ICMP6_MINLEN;
4497c478bd9Sstevel@tonic-gate 		hip6 = (ip6_t *)&(icp6->icmp6_data32[1]);
4507c478bd9Sstevel@tonic-gate 		last_hdr = hip6->ip6_nxt;
4517c478bd9Sstevel@tonic-gate 		ip6hdr_len = IPv6_hdrlen(hip6, cc, &last_hdr);
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate 		cc -= ip6hdr_len;
4547c478bd9Sstevel@tonic-gate 		if (useicmp) {
4557c478bd9Sstevel@tonic-gate 			if (*type == ICMP6_ECHO_REPLY &&
4567c478bd9Sstevel@tonic-gate 			    icp6->icmp6_id == htons(ident) &&
4577c478bd9Sstevel@tonic-gate 			    icp6->icmp6_seq == htons(seq)) {
4587c478bd9Sstevel@tonic-gate 				return (REPLY_GOT_TARGET);
4597c478bd9Sstevel@tonic-gate 			}
4607c478bd9Sstevel@tonic-gate 
4617c478bd9Sstevel@tonic-gate 			/* LINTED E_BAD_PTR_CAST_ALIGN */
4627c478bd9Sstevel@tonic-gate 			hicmp6 = (icmp6_t *)((uchar_t *)hip6 + ip6hdr_len);
4637c478bd9Sstevel@tonic-gate 
4647c478bd9Sstevel@tonic-gate 			if (ICMP6_MINLEN <= cc &&
4657c478bd9Sstevel@tonic-gate 			    last_hdr == IPPROTO_ICMPV6 &&
4667c478bd9Sstevel@tonic-gate 			    hicmp6->icmp6_id == htons(ident) &&
4677c478bd9Sstevel@tonic-gate 			    hicmp6->icmp6_seq == htons(seq)) {
4687c478bd9Sstevel@tonic-gate 				if (*type == ICMP6_TIME_EXCEEDED) {
4697c478bd9Sstevel@tonic-gate 					return (REPLY_GOT_GATEWAY);
4707c478bd9Sstevel@tonic-gate 				} else {
4717c478bd9Sstevel@tonic-gate 					return (REPLY_GOT_OTHER);
4727c478bd9Sstevel@tonic-gate 				}
4737c478bd9Sstevel@tonic-gate 			}
4747c478bd9Sstevel@tonic-gate 		} else {
4757c478bd9Sstevel@tonic-gate 			/* LINTED E_BAD_PTR_CAST_ALIGN */
4767c478bd9Sstevel@tonic-gate 			up = (struct udphdr *)((uchar_t *)hip6 + ip6hdr_len);
4777c478bd9Sstevel@tonic-gate 			/*
4787c478bd9Sstevel@tonic-gate 			 * at least 4 bytes of UDP header is required for this
4797c478bd9Sstevel@tonic-gate 			 * check
4807c478bd9Sstevel@tonic-gate 			 */
4817c478bd9Sstevel@tonic-gate 			if (4 <= cc &&
4827c478bd9Sstevel@tonic-gate 			    last_hdr == IPPROTO_UDP &&
4837c478bd9Sstevel@tonic-gate 			    up->uh_sport == htons(ident) &&
4847c478bd9Sstevel@tonic-gate 			    up->uh_dport == htons((port + seq) %
4857c478bd9Sstevel@tonic-gate 			    (MAX_PORT + 1))) {
4867c478bd9Sstevel@tonic-gate 				if (*type == ICMP6_DST_UNREACH &&
4877c478bd9Sstevel@tonic-gate 				    *code == ICMP6_DST_UNREACH_NOPORT) {
4887c478bd9Sstevel@tonic-gate 					return (REPLY_GOT_TARGET);
4897c478bd9Sstevel@tonic-gate 				} else if (*type == ICMP6_TIME_EXCEEDED) {
4907c478bd9Sstevel@tonic-gate 					return (REPLY_GOT_GATEWAY);
4917c478bd9Sstevel@tonic-gate 				} else {
4927c478bd9Sstevel@tonic-gate 					return (REPLY_GOT_OTHER);
4937c478bd9Sstevel@tonic-gate 				}
4947c478bd9Sstevel@tonic-gate 			}
4957c478bd9Sstevel@tonic-gate 		}
4967c478bd9Sstevel@tonic-gate 	}
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate 	if (verbose) {
4997c478bd9Sstevel@tonic-gate 		int i, j;
5007c478bd9Sstevel@tonic-gate 		uchar_t *lp = (uchar_t *)icp6;
5017c478bd9Sstevel@tonic-gate 		struct in6_addr *dst;
5027c478bd9Sstevel@tonic-gate 		struct in6_pktinfo *pkti;
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate 		pkti = (struct in6_pktinfo *)find_ancillary_data(msg,
5057c478bd9Sstevel@tonic-gate 		    IPPROTO_IPV6, IPV6_PKTINFO);
5067c478bd9Sstevel@tonic-gate 		if (pkti == NULL) {
5077c478bd9Sstevel@tonic-gate 			Fprintf(stderr,
5087c478bd9Sstevel@tonic-gate 			    "%s: can't find IPV6_PKTINFO ancillary data\n",
5097c478bd9Sstevel@tonic-gate 			    prog);
5107c478bd9Sstevel@tonic-gate 			exit(EXIT_FAILURE);
5117c478bd9Sstevel@tonic-gate 		}
5127c478bd9Sstevel@tonic-gate 		dst = &pkti->ipi6_addr;
5137c478bd9Sstevel@tonic-gate 		cc = save_cc;
5147c478bd9Sstevel@tonic-gate 		Printf("\n%d bytes from %s to ", cc,
5157c478bd9Sstevel@tonic-gate 		    inet_ntop(AF_INET6, (const void *)&(from_in6->sin6_addr),
5167c478bd9Sstevel@tonic-gate 			temp_buf, sizeof (temp_buf)));
5177c478bd9Sstevel@tonic-gate 		Printf("%s: icmp type %d (%s) code %d\n",
5187c478bd9Sstevel@tonic-gate 		    inet_ntop(AF_INET6, (const void *)dst,
5197c478bd9Sstevel@tonic-gate 			temp_buf, sizeof (temp_buf)),
5207c478bd9Sstevel@tonic-gate 		    *type, pr_type6(*type), *code);
5217c478bd9Sstevel@tonic-gate 		for (i = 0; i < cc; i += 4) {
5227c478bd9Sstevel@tonic-gate 			Printf("%2d: x", i);
5237c478bd9Sstevel@tonic-gate 			for (j = 0; ((j < 4) && ((i + j) < cc)); j++)
5247c478bd9Sstevel@tonic-gate 				Printf("%2.2x", *lp++);
5257c478bd9Sstevel@tonic-gate 			(void) putchar('\n');
5267c478bd9Sstevel@tonic-gate 		}
5277c478bd9Sstevel@tonic-gate 	}
5287c478bd9Sstevel@tonic-gate 
5297c478bd9Sstevel@tonic-gate 	return (REPLY_SHORT_PKT);
5307c478bd9Sstevel@tonic-gate }
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate /*
5337c478bd9Sstevel@tonic-gate  * Return the length of the IPv6 related headers (including extension headers)
5347c478bd9Sstevel@tonic-gate  */
5357c478bd9Sstevel@tonic-gate static int
IPv6_hdrlen(ip6_t * ip6h,int pkt_len,uint8_t * last_hdr_rtrn)5367c478bd9Sstevel@tonic-gate IPv6_hdrlen(ip6_t *ip6h, int pkt_len, uint8_t *last_hdr_rtrn)
5377c478bd9Sstevel@tonic-gate {
5387c478bd9Sstevel@tonic-gate 	int length;
5397c478bd9Sstevel@tonic-gate 	int exthdrlength;
5407c478bd9Sstevel@tonic-gate 	uint8_t nexthdr;
5417c478bd9Sstevel@tonic-gate 	uint8_t *whereptr;
5427c478bd9Sstevel@tonic-gate 	ip6_hbh_t *hbhhdr;
5437c478bd9Sstevel@tonic-gate 	ip6_dest_t *desthdr;
5447c478bd9Sstevel@tonic-gate 	ip6_rthdr_t *rthdr;
5457c478bd9Sstevel@tonic-gate 	ip6_frag_t *fraghdr;
5467c478bd9Sstevel@tonic-gate 	uint8_t	*endptr;
5477c478bd9Sstevel@tonic-gate 
5487c478bd9Sstevel@tonic-gate 	length = sizeof (ip6_t);
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate 	whereptr = ((uint8_t *)&ip6h[1]); 	/* point to next hdr */
5517c478bd9Sstevel@tonic-gate 	endptr = ((uint8_t *)ip6h) + pkt_len;
5527c478bd9Sstevel@tonic-gate 
5537c478bd9Sstevel@tonic-gate 	nexthdr = ip6h->ip6_nxt;
5547c478bd9Sstevel@tonic-gate 	*last_hdr_rtrn = IPPROTO_NONE;
5557c478bd9Sstevel@tonic-gate 
5567c478bd9Sstevel@tonic-gate 	if (whereptr >= endptr)
5577c478bd9Sstevel@tonic-gate 		return (length);
5587c478bd9Sstevel@tonic-gate 
5597c478bd9Sstevel@tonic-gate 	while (whereptr < endptr) {
5607c478bd9Sstevel@tonic-gate 		*last_hdr_rtrn = nexthdr;
5617c478bd9Sstevel@tonic-gate 		switch (nexthdr) {
5627c478bd9Sstevel@tonic-gate 		case IPPROTO_HOPOPTS:
5637c478bd9Sstevel@tonic-gate 			hbhhdr = (ip6_hbh_t *)whereptr;
5647c478bd9Sstevel@tonic-gate 			exthdrlength = 8 * (hbhhdr->ip6h_len + 1);
5657c478bd9Sstevel@tonic-gate 			if ((uchar_t *)hbhhdr + exthdrlength > endptr)
5667c478bd9Sstevel@tonic-gate 				return (length);
5677c478bd9Sstevel@tonic-gate 			nexthdr = hbhhdr->ip6h_nxt;
5687c478bd9Sstevel@tonic-gate 			length += exthdrlength;
5697c478bd9Sstevel@tonic-gate 			break;
5707c478bd9Sstevel@tonic-gate 
5717c478bd9Sstevel@tonic-gate 		case IPPROTO_DSTOPTS:
5727c478bd9Sstevel@tonic-gate 			desthdr = (ip6_dest_t *)whereptr;
5737c478bd9Sstevel@tonic-gate 			exthdrlength = 8 * (desthdr->ip6d_len + 1);
5747c478bd9Sstevel@tonic-gate 			if ((uchar_t *)desthdr + exthdrlength > endptr)
5757c478bd9Sstevel@tonic-gate 				return (length);
5767c478bd9Sstevel@tonic-gate 			nexthdr = desthdr->ip6d_nxt;
5777c478bd9Sstevel@tonic-gate 			length += exthdrlength;
5787c478bd9Sstevel@tonic-gate 			break;
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate 		case IPPROTO_ROUTING:
5817c478bd9Sstevel@tonic-gate 			rthdr = (ip6_rthdr_t *)whereptr;
5827c478bd9Sstevel@tonic-gate 			exthdrlength = 8 * (rthdr->ip6r_len + 1);
5837c478bd9Sstevel@tonic-gate 			if ((uchar_t *)rthdr + exthdrlength > endptr)
5847c478bd9Sstevel@tonic-gate 				return (length);
5857c478bd9Sstevel@tonic-gate 			nexthdr = rthdr->ip6r_nxt;
5867c478bd9Sstevel@tonic-gate 			length += exthdrlength;
5877c478bd9Sstevel@tonic-gate 			break;
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate 		case IPPROTO_FRAGMENT:
5907c478bd9Sstevel@tonic-gate 			/* LINTED E_BAD_PTR_CAST_ALIGN */
5917c478bd9Sstevel@tonic-gate 			fraghdr = (ip6_frag_t *)whereptr;
5927c478bd9Sstevel@tonic-gate 			if ((uchar_t *)&fraghdr[1] > endptr)
5937c478bd9Sstevel@tonic-gate 				return (length);
5947c478bd9Sstevel@tonic-gate 			nexthdr = fraghdr->ip6f_nxt;
5957c478bd9Sstevel@tonic-gate 			length += sizeof (struct ip6_frag);
5967c478bd9Sstevel@tonic-gate 			break;
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate 		case IPPROTO_NONE:
5997c478bd9Sstevel@tonic-gate 		default:
6007c478bd9Sstevel@tonic-gate 			return (length);
6017c478bd9Sstevel@tonic-gate 		}
6027c478bd9Sstevel@tonic-gate 		whereptr = (uint8_t *)ip6h + length;
6037c478bd9Sstevel@tonic-gate 	}
6047c478bd9Sstevel@tonic-gate 	*last_hdr_rtrn = nexthdr;
6057c478bd9Sstevel@tonic-gate 
6067c478bd9Sstevel@tonic-gate 	return (length);
6077c478bd9Sstevel@tonic-gate }
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate /*
6107c478bd9Sstevel@tonic-gate  * convert an ICMP6 "type" field to a printable string.
6117c478bd9Sstevel@tonic-gate  */
6127c478bd9Sstevel@tonic-gate static char *
pr_type6(uchar_t type)6137c478bd9Sstevel@tonic-gate pr_type6(uchar_t type)
6147c478bd9Sstevel@tonic-gate {
6157c478bd9Sstevel@tonic-gate 	static struct icmptype_table ttab6[] = {
6167c478bd9Sstevel@tonic-gate 		{ICMP6_DST_UNREACH,		"Dest Unreachable"},
6177c478bd9Sstevel@tonic-gate 		{ICMP6_PACKET_TOO_BIG,		"Packet Too Big"},
6187c478bd9Sstevel@tonic-gate 		{ICMP6_TIME_EXCEEDED,		"Time Exceeded"},
6197c478bd9Sstevel@tonic-gate 		{ICMP6_PARAM_PROB,		"Param Problem"},
6207c478bd9Sstevel@tonic-gate 		{ICMP6_ECHO_REQUEST,		"Echo Request"},
6217c478bd9Sstevel@tonic-gate 		{ICMP6_ECHO_REPLY,		"Echo Reply"},
6227c478bd9Sstevel@tonic-gate 		{MLD_LISTENER_QUERY,		"Multicast Listener Query"},
6237c478bd9Sstevel@tonic-gate 		{MLD_LISTENER_REPORT,		"Multicast Listener Report"},
6247c478bd9Sstevel@tonic-gate 		{MLD_LISTENER_REDUCTION,	"Multicast Listener Done"},
6257c478bd9Sstevel@tonic-gate 		{ND_ROUTER_SOLICIT,		"Router Solicitation"},
6267c478bd9Sstevel@tonic-gate 		{ND_ROUTER_ADVERT,		"Router Advertisement"},
6277c478bd9Sstevel@tonic-gate 		{ND_NEIGHBOR_SOLICIT,		"Neighbor Solicitation"},
6287c478bd9Sstevel@tonic-gate 		{ND_NEIGHBOR_ADVERT,		"Neighbor Advertisement"},
6297c478bd9Sstevel@tonic-gate 		{ND_REDIRECT,			"Redirect Message"}
6307c478bd9Sstevel@tonic-gate 	};
6317c478bd9Sstevel@tonic-gate 	int i = 0;
6327c478bd9Sstevel@tonic-gate 
6337c478bd9Sstevel@tonic-gate 	for (i = 0; i < A_CNT(ttab6); i++) {
6347c478bd9Sstevel@tonic-gate 		if (ttab6[i].type == type)
6357c478bd9Sstevel@tonic-gate 			return (ttab6[i].message);
6367c478bd9Sstevel@tonic-gate 	}
6377c478bd9Sstevel@tonic-gate 
6387c478bd9Sstevel@tonic-gate 	return ("OUT-OF-RANGE");
6397c478bd9Sstevel@tonic-gate }
6407c478bd9Sstevel@tonic-gate 
6417c478bd9Sstevel@tonic-gate 
6427c478bd9Sstevel@tonic-gate /*
6437c478bd9Sstevel@tonic-gate  * print the IPv6 src address of the reply packet
6447c478bd9Sstevel@tonic-gate  */
6457c478bd9Sstevel@tonic-gate void
print_addr6(uchar_t * buf,int cc,struct sockaddr * from)6467c478bd9Sstevel@tonic-gate print_addr6(uchar_t *buf, int cc, struct sockaddr *from)
6477c478bd9Sstevel@tonic-gate {
6487c478bd9Sstevel@tonic-gate 	/* LINTED E_BAD_PTR_CAST_ALIGN */
6497c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *from_in6 = (struct sockaddr_in6 *)from;
6507c478bd9Sstevel@tonic-gate 	ip6_t *ip;
6517c478bd9Sstevel@tonic-gate 	union any_in_addr ip_addr;
6527c478bd9Sstevel@tonic-gate 	char *resolved_name;
6537c478bd9Sstevel@tonic-gate 	char temp_buf[INET6_ADDRSTRLEN];	/* use for inet_ntop() */
6547c478bd9Sstevel@tonic-gate 
6557c478bd9Sstevel@tonic-gate 	ip_addr.addr6 = from_in6->sin6_addr;
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate 	/* LINTED E_BAD_PTR_CAST_ALIGN */
6587c478bd9Sstevel@tonic-gate 	ip = (ip6_t *)buf;
6597c478bd9Sstevel@tonic-gate 
6607c478bd9Sstevel@tonic-gate 	(void) inet_ntop(AF_INET6, &(from_in6->sin6_addr), temp_buf,
6617c478bd9Sstevel@tonic-gate 	    sizeof (temp_buf));
6627c478bd9Sstevel@tonic-gate 	if (!nflag)
6637c478bd9Sstevel@tonic-gate 		resolved_name = inet_name(&ip_addr, AF_INET6);
6647c478bd9Sstevel@tonic-gate 	/*
6657c478bd9Sstevel@tonic-gate 	 * If the IPv6 address cannot be resolved to hostname, inet_name()
6667c478bd9Sstevel@tonic-gate 	 * returns the IPv6 address as a string. In that case, we choose not
6677c478bd9Sstevel@tonic-gate 	 * to print it twice. This saves us space on display.
6687c478bd9Sstevel@tonic-gate 	 */
6697c478bd9Sstevel@tonic-gate 	if (nflag || (strcmp(temp_buf, resolved_name) == 0))
6707c478bd9Sstevel@tonic-gate 		Printf(" %s", temp_buf);
6717c478bd9Sstevel@tonic-gate 	else
6727c478bd9Sstevel@tonic-gate 		Printf(" %s (%s)", resolved_name, temp_buf);
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate 	if (verbose) {
6757c478bd9Sstevel@tonic-gate 		Printf(" %d bytes to %s", cc, inet_ntop(AF_INET6,
6767c478bd9Sstevel@tonic-gate 		    (const void *) &(ip->ip6_dst), temp_buf,
6777c478bd9Sstevel@tonic-gate 		    sizeof (temp_buf)));
6787c478bd9Sstevel@tonic-gate 	}
6797c478bd9Sstevel@tonic-gate }
6807c478bd9Sstevel@tonic-gate 
6817c478bd9Sstevel@tonic-gate /*
6827c478bd9Sstevel@tonic-gate  * ICMP6 messages which doesn't mean we got the target, or we got a gateway, are
6837c478bd9Sstevel@tonic-gate  * processed here. It returns _B_TRUE if it's some sort of 'unreachable'.
6847c478bd9Sstevel@tonic-gate  */
6857c478bd9Sstevel@tonic-gate boolean_t
print_icmp_other6(uchar_t type,uchar_t code)6867c478bd9Sstevel@tonic-gate print_icmp_other6(uchar_t type, uchar_t code)
6877c478bd9Sstevel@tonic-gate {
6887c478bd9Sstevel@tonic-gate 	boolean_t unreach = _B_FALSE;
6897c478bd9Sstevel@tonic-gate 
6907c478bd9Sstevel@tonic-gate 	switch (type) {
6917c478bd9Sstevel@tonic-gate 
6927c478bd9Sstevel@tonic-gate 	/* this corresponds to "ICMP_UNREACH_NEEDFRAG" in ICMP */
6937c478bd9Sstevel@tonic-gate 	case ICMP6_PACKET_TOO_BIG:
6947c478bd9Sstevel@tonic-gate 		unreach = _B_TRUE;
6957c478bd9Sstevel@tonic-gate 		Printf(" !B");
6967c478bd9Sstevel@tonic-gate 		break;
6977c478bd9Sstevel@tonic-gate 
6987c478bd9Sstevel@tonic-gate 	case ICMP6_PARAM_PROB:
6997c478bd9Sstevel@tonic-gate 		/* this corresponds to "ICMP_UNREACH_PROTOCOL" in ICMP */
7007c478bd9Sstevel@tonic-gate 		if (code == ICMP6_PARAMPROB_NEXTHEADER) {
7017c478bd9Sstevel@tonic-gate 			unreach = _B_TRUE;
7027c478bd9Sstevel@tonic-gate 			Printf(" !R");
7037c478bd9Sstevel@tonic-gate 		}
7047c478bd9Sstevel@tonic-gate 		break;
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate 	case ICMP6_DST_UNREACH:
7077c478bd9Sstevel@tonic-gate 		switch (code) {
7087c478bd9Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_NOPORT:
7097c478bd9Sstevel@tonic-gate 			break;
7107c478bd9Sstevel@tonic-gate 
7117c478bd9Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_NOROUTE:
7127c478bd9Sstevel@tonic-gate 			unreach = _B_TRUE;
7137c478bd9Sstevel@tonic-gate 			Printf(" !H");
7147c478bd9Sstevel@tonic-gate 			break;
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_ADMIN:
7177c478bd9Sstevel@tonic-gate 			unreach = _B_TRUE;
7187c478bd9Sstevel@tonic-gate 			Printf(" !X");
7197c478bd9Sstevel@tonic-gate 			break;
7207c478bd9Sstevel@tonic-gate 
7217c478bd9Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_ADDR:
7227c478bd9Sstevel@tonic-gate 			unreach = _B_TRUE;
7237c478bd9Sstevel@tonic-gate 			Printf(" !A");
7247c478bd9Sstevel@tonic-gate 			break;
7257c478bd9Sstevel@tonic-gate 
7267c478bd9Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_NOTNEIGHBOR:
7277c478bd9Sstevel@tonic-gate 			unreach = _B_TRUE;
7287c478bd9Sstevel@tonic-gate 			Printf(" !E");
7297c478bd9Sstevel@tonic-gate 			break;
7307c478bd9Sstevel@tonic-gate 
7317c478bd9Sstevel@tonic-gate 		default:
7327c478bd9Sstevel@tonic-gate 			unreach = _B_TRUE;
7337c478bd9Sstevel@tonic-gate 			Printf(" !<%d>", code);
7347c478bd9Sstevel@tonic-gate 			break;
7357c478bd9Sstevel@tonic-gate 		}
7367c478bd9Sstevel@tonic-gate 		break;
7377c478bd9Sstevel@tonic-gate 	default:
7387c478bd9Sstevel@tonic-gate 		break;
7397c478bd9Sstevel@tonic-gate 	}
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate 	return (unreach);
7427c478bd9Sstevel@tonic-gate }
743