1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
3*0Sstevel@tonic-gate  * Use is subject to license terms.
4*0Sstevel@tonic-gate  */
5*0Sstevel@tonic-gate 
6*0Sstevel@tonic-gate 
7*0Sstevel@tonic-gate /*
8*0Sstevel@tonic-gate  * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997
9*0Sstevel@tonic-gate  *	The Regents of the University of California.  All rights reserved.
10*0Sstevel@tonic-gate  *
11*0Sstevel@tonic-gate  * Redistribution and use in source and binary forms, with or without
12*0Sstevel@tonic-gate  * modification, are permitted provided that: (1) source code distributions
13*0Sstevel@tonic-gate  * retain the above copyright notice and this paragraph in its entirety, (2)
14*0Sstevel@tonic-gate  * distributions including binary code include the above copyright notice and
15*0Sstevel@tonic-gate  * this paragraph in its entirety in the documentation or other materials
16*0Sstevel@tonic-gate  * provided with the distribution, and (3) all advertising materials mentioning
17*0Sstevel@tonic-gate  * features or use of this software display the following acknowledgement:
18*0Sstevel@tonic-gate  * ``This product includes software developed by the University of California,
19*0Sstevel@tonic-gate  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
20*0Sstevel@tonic-gate  * the University nor the names of its contributors may be used to endorse
21*0Sstevel@tonic-gate  * or promote products derived from this software without specific prior
22*0Sstevel@tonic-gate  * written permission.
23*0Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
24*0Sstevel@tonic-gate  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
25*0Sstevel@tonic-gate  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
26*0Sstevel@tonic-gate  *
27*0Sstevel@tonic-gate  *
28*0Sstevel@tonic-gate  * @(#)$Header: traceroute.c,v 1.49 97/06/13 02:30:23 leres Exp $ (LBL)
29*0Sstevel@tonic-gate  */
30*0Sstevel@tonic-gate 
31*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
32*0Sstevel@tonic-gate 
33*0Sstevel@tonic-gate #include <sys/socket.h>
34*0Sstevel@tonic-gate 
35*0Sstevel@tonic-gate #include <stdio.h>
36*0Sstevel@tonic-gate #include <stdlib.h>
37*0Sstevel@tonic-gate #include <ctype.h>
38*0Sstevel@tonic-gate #include <strings.h>
39*0Sstevel@tonic-gate #include <libintl.h>
40*0Sstevel@tonic-gate #include <errno.h>
41*0Sstevel@tonic-gate #include <netdb.h>
42*0Sstevel@tonic-gate 
43*0Sstevel@tonic-gate #include <netinet/in_systm.h>
44*0Sstevel@tonic-gate #include <netinet/in.h>
45*0Sstevel@tonic-gate #include <netinet/ip.h>
46*0Sstevel@tonic-gate #include <netinet/ip_var.h>
47*0Sstevel@tonic-gate #include <netinet/ip_icmp.h>
48*0Sstevel@tonic-gate #include <netinet/udp.h>
49*0Sstevel@tonic-gate #include <netinet/udp_var.h>
50*0Sstevel@tonic-gate #include <netinet/ip6.h>
51*0Sstevel@tonic-gate #include <netinet/icmp6.h>
52*0Sstevel@tonic-gate 
53*0Sstevel@tonic-gate #include <arpa/inet.h>
54*0Sstevel@tonic-gate 
55*0Sstevel@tonic-gate #include <ifaddrlist.h>
56*0Sstevel@tonic-gate #include "traceroute.h"
57*0Sstevel@tonic-gate 
58*0Sstevel@tonic-gate int check_reply6(struct msghdr *, int, int, uchar_t *, uchar_t *);
59*0Sstevel@tonic-gate void *find_ancillary_data(struct msghdr *, int, int);
60*0Sstevel@tonic-gate extern char *inet_name(union any_in_addr *, int);
61*0Sstevel@tonic-gate static int IPv6_hdrlen(ip6_t *, int, uint8_t *);
62*0Sstevel@tonic-gate static char *pr_type6(uchar_t);
63*0Sstevel@tonic-gate void print_addr6(uchar_t *, int, struct sockaddr *);
64*0Sstevel@tonic-gate boolean_t print_icmp_other6(uchar_t, uchar_t);
65*0Sstevel@tonic-gate void send_probe6(int, struct msghdr *, struct ip *, int, int,
66*0Sstevel@tonic-gate     struct timeval *, int);
67*0Sstevel@tonic-gate void set_ancillary_data(struct msghdr *, int, union any_in_addr *, int, uint_t);
68*0Sstevel@tonic-gate struct ip *set_buffers6(int);
69*0Sstevel@tonic-gate static boolean_t update_hoplimit_ancillary_data(struct msghdr *, int);
70*0Sstevel@tonic-gate 
71*0Sstevel@tonic-gate /*
72*0Sstevel@tonic-gate  * prepares the buffer to be sent as an IP datagram
73*0Sstevel@tonic-gate  */
74*0Sstevel@tonic-gate struct ip *
75*0Sstevel@tonic-gate set_buffers6(int plen)
76*0Sstevel@tonic-gate {
77*0Sstevel@tonic-gate 	struct ip *outip;
78*0Sstevel@tonic-gate 	uchar_t *outp;
79*0Sstevel@tonic-gate 	struct udphdr *outudp;
80*0Sstevel@tonic-gate 	struct icmp *outicmp;
81*0Sstevel@tonic-gate 	int optlen = 0;
82*0Sstevel@tonic-gate 
83*0Sstevel@tonic-gate 	outip = (struct ip *)malloc((size_t)plen);
84*0Sstevel@tonic-gate 	if (outip == NULL) {
85*0Sstevel@tonic-gate 		Fprintf(stderr, "%s: malloc: %s\n", prog, strerror(errno));
86*0Sstevel@tonic-gate 		exit(EXIT_FAILURE);
87*0Sstevel@tonic-gate 	}
88*0Sstevel@tonic-gate 
89*0Sstevel@tonic-gate 	if (gw_count > 0) {
90*0Sstevel@tonic-gate 		/* ip6_rthdr0 structure includes one gateway address */
91*0Sstevel@tonic-gate 		optlen = sizeof (struct ip6_rthdr0) +
92*0Sstevel@tonic-gate 		    gw_count * sizeof (struct in6_addr);
93*0Sstevel@tonic-gate 	}
94*0Sstevel@tonic-gate 
95*0Sstevel@tonic-gate 	(void) memset((char *)outip, 0, (size_t)plen);
96*0Sstevel@tonic-gate 	outp = (uchar_t *)(outip + 1);
97*0Sstevel@tonic-gate 
98*0Sstevel@tonic-gate 	if (useicmp) {
99*0Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
100*0Sstevel@tonic-gate 		outicmp = (struct icmp *)outp;
101*0Sstevel@tonic-gate 		outicmp->icmp_type = ICMP6_ECHO_REQUEST;
102*0Sstevel@tonic-gate 		outicmp->icmp_id = htons(ident);
103*0Sstevel@tonic-gate 	} else {
104*0Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
105*0Sstevel@tonic-gate 		outudp = (struct udphdr *)outp;
106*0Sstevel@tonic-gate 		/*
107*0Sstevel@tonic-gate 		 * "source port" is set at bind() call, so we don't do it
108*0Sstevel@tonic-gate 		 * again
109*0Sstevel@tonic-gate 		 */
110*0Sstevel@tonic-gate 		outudp->uh_ulen = htons((ushort_t)(plen -
111*0Sstevel@tonic-gate 		    (sizeof (struct ip6_hdr) + optlen)));
112*0Sstevel@tonic-gate 	}
113*0Sstevel@tonic-gate 
114*0Sstevel@tonic-gate 	return (outip);
115*0Sstevel@tonic-gate }
116*0Sstevel@tonic-gate 
117*0Sstevel@tonic-gate /*
118*0Sstevel@tonic-gate  * Initialize the msghdr for specifying hoplimit, outgoing interface and routing
119*0Sstevel@tonic-gate  * header for the probe packets.
120*0Sstevel@tonic-gate  */
121*0Sstevel@tonic-gate void
122*0Sstevel@tonic-gate set_ancillary_data(struct msghdr *msgp, int hoplimit,
123*0Sstevel@tonic-gate     union any_in_addr *gwIPlist, int gw_cnt, uint_t if_index)
124*0Sstevel@tonic-gate {
125*0Sstevel@tonic-gate 	size_t hoplimit_space;
126*0Sstevel@tonic-gate 	size_t rthdr_space;
127*0Sstevel@tonic-gate 	size_t pktinfo_space;
128*0Sstevel@tonic-gate 	size_t bufspace;
129*0Sstevel@tonic-gate 	struct cmsghdr *cmsgp;
130*0Sstevel@tonic-gate 	uchar_t *cmsg_datap;
131*0Sstevel@tonic-gate 	int i;
132*0Sstevel@tonic-gate 
133*0Sstevel@tonic-gate 	msgp->msg_control = NULL;
134*0Sstevel@tonic-gate 	msgp->msg_controllen = 0;
135*0Sstevel@tonic-gate 
136*0Sstevel@tonic-gate 	/*
137*0Sstevel@tonic-gate 	 * Need to figure out size of buffer needed for ancillary data
138*0Sstevel@tonic-gate 	 * containing routing header and packet info options.
139*0Sstevel@tonic-gate 	 *
140*0Sstevel@tonic-gate 	 * Portable heuristic to compute upper bound on space needed for
141*0Sstevel@tonic-gate 	 * N ancillary data options. It assumes up to _MAX_ALIGNMENT padding
142*0Sstevel@tonic-gate 	 * after both header and data as the worst possible upper bound on space
143*0Sstevel@tonic-gate 	 * consumed by padding.
144*0Sstevel@tonic-gate 	 * It also adds one extra "sizeof (struct cmsghdr)" for the last option.
145*0Sstevel@tonic-gate 	 * This is needed because we would like to use CMSG_NXTHDR() while
146*0Sstevel@tonic-gate 	 * composing the buffer. The CMSG_NXTHDR() macro is designed better for
147*0Sstevel@tonic-gate 	 * parsing than composing the buffer. It requires the pointer it returns
148*0Sstevel@tonic-gate 	 * to leave space in buffer for addressing a cmsghdr and we want to make
149*0Sstevel@tonic-gate 	 * sure it works for us while we skip beyond the last ancillary data
150*0Sstevel@tonic-gate 	 * option.
151*0Sstevel@tonic-gate 	 *
152*0Sstevel@tonic-gate 	 * bufspace[i]  = sizeof(struct cmsghdr) + <pad after header> +
153*0Sstevel@tonic-gate 	 *		<option[i] content length> + <pad after data>;
154*0Sstevel@tonic-gate 	 *
155*0Sstevel@tonic-gate 	 * total_bufspace = bufspace[0] + bufspace[1] + ...
156*0Sstevel@tonic-gate 	 *		    ... + bufspace[N-1] + sizeof (struct cmsghdr);
157*0Sstevel@tonic-gate 	 */
158*0Sstevel@tonic-gate 
159*0Sstevel@tonic-gate 	rthdr_space = 0;
160*0Sstevel@tonic-gate 	pktinfo_space = 0;
161*0Sstevel@tonic-gate 	/* We'll always set the hoplimit of the outgoing packets */
162*0Sstevel@tonic-gate 	hoplimit_space = sizeof (int);
163*0Sstevel@tonic-gate 	bufspace = sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
164*0Sstevel@tonic-gate 	    hoplimit_space + _MAX_ALIGNMENT;
165*0Sstevel@tonic-gate 
166*0Sstevel@tonic-gate 	if (gw_cnt > 0) {
167*0Sstevel@tonic-gate 		rthdr_space = inet6_rth_space(IPV6_RTHDR_TYPE_0, gw_cnt);
168*0Sstevel@tonic-gate 		bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
169*0Sstevel@tonic-gate 		    rthdr_space + _MAX_ALIGNMENT;
170*0Sstevel@tonic-gate 	}
171*0Sstevel@tonic-gate 
172*0Sstevel@tonic-gate 	if (if_index != 0) {
173*0Sstevel@tonic-gate 		pktinfo_space = sizeof (struct in6_pktinfo);
174*0Sstevel@tonic-gate 		bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
175*0Sstevel@tonic-gate 		    pktinfo_space + _MAX_ALIGNMENT;
176*0Sstevel@tonic-gate 	}
177*0Sstevel@tonic-gate 
178*0Sstevel@tonic-gate 	/*
179*0Sstevel@tonic-gate 	 * We need to temporarily set the msgp->msg_controllen to bufspace
180*0Sstevel@tonic-gate 	 * (we will later trim it to actual length used). This is needed because
181*0Sstevel@tonic-gate 	 * CMSG_NXTHDR() uses it to check we have not exceeded the bounds.
182*0Sstevel@tonic-gate 	 */
183*0Sstevel@tonic-gate 	bufspace += sizeof (struct cmsghdr);
184*0Sstevel@tonic-gate 	msgp->msg_controllen = bufspace;
185*0Sstevel@tonic-gate 
186*0Sstevel@tonic-gate 	msgp->msg_control = (struct cmsghdr *)malloc(bufspace);
187*0Sstevel@tonic-gate 	if (msgp->msg_control == NULL) {
188*0Sstevel@tonic-gate 		Fprintf(stderr, "%s: malloc %s\n", prog, strerror(errno));
189*0Sstevel@tonic-gate 		exit(EXIT_FAILURE);
190*0Sstevel@tonic-gate 	}
191*0Sstevel@tonic-gate 	cmsgp = CMSG_FIRSTHDR(msgp);
192*0Sstevel@tonic-gate 
193*0Sstevel@tonic-gate 	/*
194*0Sstevel@tonic-gate 	 * Fill ancillary data. First hoplimit, then rthdr and pktinfo if
195*0Sstevel@tonic-gate 	 * needed.
196*0Sstevel@tonic-gate 	 */
197*0Sstevel@tonic-gate 
198*0Sstevel@tonic-gate 	/* set hoplimit ancillary data */
199*0Sstevel@tonic-gate 	cmsgp->cmsg_level = IPPROTO_IPV6;
200*0Sstevel@tonic-gate 	cmsgp->cmsg_type = IPV6_HOPLIMIT;
201*0Sstevel@tonic-gate 	cmsg_datap = CMSG_DATA(cmsgp);
202*0Sstevel@tonic-gate 	/* LINTED E_BAD_PTR_CAST_ALIGN */
203*0Sstevel@tonic-gate 	*(int *)cmsg_datap = hoplimit;
204*0Sstevel@tonic-gate 	cmsgp->cmsg_len = cmsg_datap + hoplimit_space - (uchar_t *)cmsgp;
205*0Sstevel@tonic-gate 	cmsgp = CMSG_NXTHDR(msgp, cmsgp);
206*0Sstevel@tonic-gate 
207*0Sstevel@tonic-gate 	/* set rthdr ancillary data if needed */
208*0Sstevel@tonic-gate 	if (gw_cnt > 0) {
209*0Sstevel@tonic-gate 		struct ip6_rthdr0 *rthdr0p;
210*0Sstevel@tonic-gate 
211*0Sstevel@tonic-gate 		cmsgp->cmsg_level = IPPROTO_IPV6;
212*0Sstevel@tonic-gate 		cmsgp->cmsg_type = IPV6_RTHDR;
213*0Sstevel@tonic-gate 		cmsg_datap = CMSG_DATA(cmsgp);
214*0Sstevel@tonic-gate 
215*0Sstevel@tonic-gate 		/*
216*0Sstevel@tonic-gate 		 * Initialize rthdr structure
217*0Sstevel@tonic-gate 		 */
218*0Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
219*0Sstevel@tonic-gate 		rthdr0p = (struct ip6_rthdr0 *)cmsg_datap;
220*0Sstevel@tonic-gate 		if (inet6_rth_init(rthdr0p, rthdr_space,
221*0Sstevel@tonic-gate 		    IPV6_RTHDR_TYPE_0, gw_cnt) == NULL) {
222*0Sstevel@tonic-gate 			Fprintf(stderr, "%s: inet6_rth_init failed\n",
223*0Sstevel@tonic-gate 			    prog);
224*0Sstevel@tonic-gate 			exit(EXIT_FAILURE);
225*0Sstevel@tonic-gate 		}
226*0Sstevel@tonic-gate 
227*0Sstevel@tonic-gate 		/*
228*0Sstevel@tonic-gate 		 * Stuff in gateway addresses
229*0Sstevel@tonic-gate 		 */
230*0Sstevel@tonic-gate 		for (i = 0; i < gw_cnt; i++) {
231*0Sstevel@tonic-gate 			if (inet6_rth_add(rthdr0p,
232*0Sstevel@tonic-gate 			    &gwIPlist[i].addr6) == -1) {
233*0Sstevel@tonic-gate 				Fprintf(stderr,
234*0Sstevel@tonic-gate 				    "%s: inet6_rth_add\n", prog);
235*0Sstevel@tonic-gate 				exit(EXIT_FAILURE);
236*0Sstevel@tonic-gate 			}
237*0Sstevel@tonic-gate 		}
238*0Sstevel@tonic-gate 
239*0Sstevel@tonic-gate 		cmsgp->cmsg_len = cmsg_datap + rthdr_space - (uchar_t *)cmsgp;
240*0Sstevel@tonic-gate 		cmsgp = CMSG_NXTHDR(msgp, cmsgp);
241*0Sstevel@tonic-gate 	}
242*0Sstevel@tonic-gate 
243*0Sstevel@tonic-gate 	/* set pktinfo ancillary data if needed */
244*0Sstevel@tonic-gate 	if (if_index != 0) {
245*0Sstevel@tonic-gate 		struct in6_pktinfo *pktinfop;
246*0Sstevel@tonic-gate 
247*0Sstevel@tonic-gate 		cmsgp->cmsg_level = IPPROTO_IPV6;
248*0Sstevel@tonic-gate 		cmsgp->cmsg_type = IPV6_PKTINFO;
249*0Sstevel@tonic-gate 		cmsg_datap = CMSG_DATA(cmsgp);
250*0Sstevel@tonic-gate 
251*0Sstevel@tonic-gate 		/* LINTED E_BAD_PTR_CAST_ALIGN */
252*0Sstevel@tonic-gate 		pktinfop = (struct in6_pktinfo *)cmsg_datap;
253*0Sstevel@tonic-gate 		/*
254*0Sstevel@tonic-gate 		 * We don't know if pktinfop->ipi6_addr is aligned properly,
255*0Sstevel@tonic-gate 		 * therefore let's use bcopy, instead of assignment.
256*0Sstevel@tonic-gate 		 */
257*0Sstevel@tonic-gate 		(void) bcopy(&in6addr_any, &pktinfop->ipi6_addr,
258*0Sstevel@tonic-gate 		sizeof (struct in6_addr));
259*0Sstevel@tonic-gate 
260*0Sstevel@tonic-gate 		/*
261*0Sstevel@tonic-gate 		 *  We can assume pktinfop->ipi6_ifindex is 32 bit aligned.
262*0Sstevel@tonic-gate 		 */
263*0Sstevel@tonic-gate 		pktinfop->ipi6_ifindex = if_index;
264*0Sstevel@tonic-gate 		cmsgp->cmsg_len = cmsg_datap + pktinfo_space - (uchar_t *)cmsgp;
265*0Sstevel@tonic-gate 		cmsgp = CMSG_NXTHDR(msgp, cmsgp);
266*0Sstevel@tonic-gate 	}
267*0Sstevel@tonic-gate 
268*0Sstevel@tonic-gate 	msgp->msg_controllen = (char *)cmsgp - (char *)msgp->msg_control;
269*0Sstevel@tonic-gate }
270*0Sstevel@tonic-gate 
271*0Sstevel@tonic-gate /*
272*0Sstevel@tonic-gate  * Parses the given msg->msg_control to find the IPV6_HOPLIMIT ancillary data
273*0Sstevel@tonic-gate  * and update the hoplimit.
274*0Sstevel@tonic-gate  * Returns _B_FALSE if it can't find IPV6_HOPLIMIT ancillary data, _B_TRUE
275*0Sstevel@tonic-gate  * otherwise.
276*0Sstevel@tonic-gate  */
277*0Sstevel@tonic-gate static boolean_t
278*0Sstevel@tonic-gate update_hoplimit_ancillary_data(struct msghdr *msg, int hoplimit)
279*0Sstevel@tonic-gate {
280*0Sstevel@tonic-gate 	struct cmsghdr *cmsg;
281*0Sstevel@tonic-gate 	int *intp;
282*0Sstevel@tonic-gate 
283*0Sstevel@tonic-gate 	for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
284*0Sstevel@tonic-gate 	    cmsg = CMSG_NXTHDR(msg, cmsg)) {
285*0Sstevel@tonic-gate 		if (cmsg->cmsg_level == IPPROTO_IPV6 &&
286*0Sstevel@tonic-gate 		    cmsg->cmsg_type == IPV6_HOPLIMIT) {
287*0Sstevel@tonic-gate 			/* LINTED E_BAD_PTR_CAST_ALIGN */
288*0Sstevel@tonic-gate 			intp = (int *)(CMSG_DATA(cmsg));
289*0Sstevel@tonic-gate 			*intp = hoplimit;
290*0Sstevel@tonic-gate 			return (_B_TRUE);
291*0Sstevel@tonic-gate 		}
292*0Sstevel@tonic-gate 	}
293*0Sstevel@tonic-gate 
294*0Sstevel@tonic-gate 	return (_B_FALSE);
295*0Sstevel@tonic-gate }
296*0Sstevel@tonic-gate 
297*0Sstevel@tonic-gate /*
298*0Sstevel@tonic-gate  * send a probe packet to the destination
299*0Sstevel@tonic-gate  */
300*0Sstevel@tonic-gate void
301*0Sstevel@tonic-gate send_probe6(int sndsock, struct msghdr *msg6, struct ip *outip, int seq,
302*0Sstevel@tonic-gate     int ttl, struct timeval *tp, int packlen)
303*0Sstevel@tonic-gate {
304*0Sstevel@tonic-gate 	uchar_t *outp;
305*0Sstevel@tonic-gate 	struct icmp *outicmp;
306*0Sstevel@tonic-gate 	struct outdata *outdata;
307*0Sstevel@tonic-gate 	struct iovec iov;
308*0Sstevel@tonic-gate 	int cc;
309*0Sstevel@tonic-gate 	int optlen = 0;
310*0Sstevel@tonic-gate 	int send_size;
311*0Sstevel@tonic-gate 	struct sockaddr_in6 *to6;
312*0Sstevel@tonic-gate 
313*0Sstevel@tonic-gate 	if (gw_count > 0) {
314*0Sstevel@tonic-gate 		/* ip6_rthdr0 structure includes one gateway address */
315*0Sstevel@tonic-gate 		optlen = sizeof (struct ip6_rthdr0) +
316*0Sstevel@tonic-gate 		    gw_count * sizeof (struct in6_addr);
317*0Sstevel@tonic-gate 	}
318*0Sstevel@tonic-gate 
319*0Sstevel@tonic-gate 	send_size = packlen - sizeof (struct ip6_hdr) - optlen;
320*0Sstevel@tonic-gate 
321*0Sstevel@tonic-gate 	/* if using UDP, further discount UDP header size */
322*0Sstevel@tonic-gate 	if (!useicmp)
323*0Sstevel@tonic-gate 		send_size -= sizeof (struct udphdr);
324*0Sstevel@tonic-gate 
325*0Sstevel@tonic-gate 	/* initialize buffer pointers */
326*0Sstevel@tonic-gate 	outp = (uchar_t *)(outip + 1);
327*0Sstevel@tonic-gate 	/* LINTED E_BAD_PTR_CAST_ALIGN */
328*0Sstevel@tonic-gate 	outicmp = (struct icmp *)outp;
329*0Sstevel@tonic-gate 	/* LINTED E_BAD_PTR_CAST_ALIGN */
330*0Sstevel@tonic-gate 	outdata = (struct outdata *)(outp + ICMP6_MINLEN);
331*0Sstevel@tonic-gate 
332*0Sstevel@tonic-gate 	if (!update_hoplimit_ancillary_data(msg6, ttl)) {
333*0Sstevel@tonic-gate 		Fprintf(stderr,
334*0Sstevel@tonic-gate 		    "%s: can't find IPV6_HOPLIMIT ancillary data\n", prog);
335*0Sstevel@tonic-gate 		exit(EXIT_FAILURE);
336*0Sstevel@tonic-gate 	}
337*0Sstevel@tonic-gate 
338*0Sstevel@tonic-gate 	/* Payload */
339*0Sstevel@tonic-gate 	outdata->seq = seq;
340*0Sstevel@tonic-gate 	outdata->ttl = ttl;
341*0Sstevel@tonic-gate 	outdata->tv = *tp;
342*0Sstevel@tonic-gate 
343*0Sstevel@tonic-gate 	if (useicmp) {
344*0Sstevel@tonic-gate 		outicmp->icmp_seq = htons(seq);
345*0Sstevel@tonic-gate 	} else {
346*0Sstevel@tonic-gate 		to6 = (struct sockaddr_in6 *)msg6->msg_name;
347*0Sstevel@tonic-gate 		to6->sin6_port =  htons((port + seq) % (MAX_PORT + 1));
348*0Sstevel@tonic-gate 	}
349*0Sstevel@tonic-gate 
350*0Sstevel@tonic-gate 	iov.iov_base = outp;
351*0Sstevel@tonic-gate 	iov.iov_len = send_size;
352*0Sstevel@tonic-gate 
353*0Sstevel@tonic-gate 	msg6->msg_iov = &iov;
354*0Sstevel@tonic-gate 	msg6->msg_iovlen = 1;
355*0Sstevel@tonic-gate 
356*0Sstevel@tonic-gate 	cc = sendmsg(sndsock, msg6, 0);
357*0Sstevel@tonic-gate 
358*0Sstevel@tonic-gate 	if (cc < 0 || cc != send_size)  {
359*0Sstevel@tonic-gate 		if (cc < 0) {
360*0Sstevel@tonic-gate 			Fprintf(stderr, "%s: sendmsg: %s\n", prog,
361*0Sstevel@tonic-gate 			    strerror(errno));
362*0Sstevel@tonic-gate 		}
363*0Sstevel@tonic-gate 		Printf("%s: wrote %s %d chars, ret=%d\n",
364*0Sstevel@tonic-gate 		    prog, hostname, send_size, cc);
365*0Sstevel@tonic-gate 		(void) fflush(stdout);
366*0Sstevel@tonic-gate 	}
367*0Sstevel@tonic-gate }
368*0Sstevel@tonic-gate 
369*0Sstevel@tonic-gate /*
370*0Sstevel@tonic-gate  * Return a pointer to the ancillary data for the given cmsg_level and
371*0Sstevel@tonic-gate  * cmsg_type.
372*0Sstevel@tonic-gate  * If not found return NULL.
373*0Sstevel@tonic-gate  */
374*0Sstevel@tonic-gate void *
375*0Sstevel@tonic-gate find_ancillary_data(struct msghdr *msg, int cmsg_level, int cmsg_type)
376*0Sstevel@tonic-gate {
377*0Sstevel@tonic-gate 	struct cmsghdr *cmsg;
378*0Sstevel@tonic-gate 
379*0Sstevel@tonic-gate 	for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
380*0Sstevel@tonic-gate 	    cmsg = CMSG_NXTHDR(msg, cmsg)) {
381*0Sstevel@tonic-gate 		if (cmsg->cmsg_level == cmsg_level &&
382*0Sstevel@tonic-gate 		    cmsg->cmsg_type == cmsg_type) {
383*0Sstevel@tonic-gate 			return (CMSG_DATA(cmsg));
384*0Sstevel@tonic-gate 		}
385*0Sstevel@tonic-gate 	}
386*0Sstevel@tonic-gate 	return (NULL);
387*0Sstevel@tonic-gate }
388*0Sstevel@tonic-gate 
389*0Sstevel@tonic-gate /*
390*0Sstevel@tonic-gate  * Check out the reply packet to see if it's what we were expecting.
391*0Sstevel@tonic-gate  * Returns REPLY_GOT_TARGET if the reply comes from the target
392*0Sstevel@tonic-gate  *         REPLY_GOT_GATEWAY if an intermediate gateway sends TIME_EXCEEDED
393*0Sstevel@tonic-gate  *         REPLY_GOT_OTHER for other kinds of unreachables indicating none of
394*0Sstevel@tonic-gate  *	   the above two cases
395*0Sstevel@tonic-gate  *
396*0Sstevel@tonic-gate  * It also sets the icmp type and icmp code values
397*0Sstevel@tonic-gate  */
398*0Sstevel@tonic-gate int
399*0Sstevel@tonic-gate check_reply6(struct msghdr *msg, int cc, int seq, uchar_t *type, uchar_t *code)
400*0Sstevel@tonic-gate {
401*0Sstevel@tonic-gate 	uchar_t *buf = msg->msg_iov->iov_base;
402*0Sstevel@tonic-gate 	struct sockaddr_in6 *from_in6 = (struct sockaddr_in6 *)msg->msg_name;
403*0Sstevel@tonic-gate 	icmp6_t *icp6;
404*0Sstevel@tonic-gate 	ulong_t ip6hdr_len;
405*0Sstevel@tonic-gate 	uint8_t last_hdr;
406*0Sstevel@tonic-gate 	int save_cc = cc;
407*0Sstevel@tonic-gate 	char temp_buf[INET6_ADDRSTRLEN];	/* use for inet_ntop() */
408*0Sstevel@tonic-gate 
409*0Sstevel@tonic-gate 	/* Ignore packets > 64k or control buffers that don't fit */
410*0Sstevel@tonic-gate 	if (msg->msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
411*0Sstevel@tonic-gate 		if (verbose) {
412*0Sstevel@tonic-gate 			Printf("Truncated message: msg_flags 0x%x from %s\n",
413*0Sstevel@tonic-gate 			    msg->msg_flags,
414*0Sstevel@tonic-gate 			    inet_ntop(AF_INET6,
415*0Sstevel@tonic-gate 			    (void *)&(from_in6->sin6_addr),
416*0Sstevel@tonic-gate 			    temp_buf, sizeof (temp_buf)));
417*0Sstevel@tonic-gate 		}
418*0Sstevel@tonic-gate 		return (REPLY_SHORT_PKT);
419*0Sstevel@tonic-gate 	}
420*0Sstevel@tonic-gate 	if (cc < ICMP6_MINLEN) {
421*0Sstevel@tonic-gate 		if (verbose) {
422*0Sstevel@tonic-gate 			Printf("packet too short (%d bytes) from %s\n",
423*0Sstevel@tonic-gate 			    cc,
424*0Sstevel@tonic-gate 			    inet_ntop(AF_INET6,
425*0Sstevel@tonic-gate 			    (void *)&(from_in6->sin6_addr),
426*0Sstevel@tonic-gate 			    temp_buf, sizeof (temp_buf)));
427*0Sstevel@tonic-gate 		}
428*0Sstevel@tonic-gate 		return (REPLY_SHORT_PKT);
429*0Sstevel@tonic-gate 	}
430*0Sstevel@tonic-gate 	/* LINTED E_BAD_PTR_CAST_ALIGN */
431*0Sstevel@tonic-gate 	icp6 = (icmp6_t *)buf;
432*0Sstevel@tonic-gate 	*type = icp6->icmp6_type;
433*0Sstevel@tonic-gate 	*code = icp6->icmp6_code;
434*0Sstevel@tonic-gate 
435*0Sstevel@tonic-gate 	/*
436*0Sstevel@tonic-gate 	 * traceroute interprets only ICMP6_TIME_EXCEED_TRANSIT,
437*0Sstevel@tonic-gate 	 * ICMP6_DST_UNREACH, ICMP6_ECHO_REPLY, ICMP6_PACKET_TOO_BIG and
438*0Sstevel@tonic-gate 	 * ICMP6_PARAMPROB_NEXTHEADER, ignores others
439*0Sstevel@tonic-gate 	 */
440*0Sstevel@tonic-gate 	if ((*type == ICMP6_TIME_EXCEEDED &&
441*0Sstevel@tonic-gate 	    *code == ICMP6_TIME_EXCEED_TRANSIT) ||
442*0Sstevel@tonic-gate 	    *type == ICMP6_DST_UNREACH || *type == ICMP6_ECHO_REPLY ||
443*0Sstevel@tonic-gate 	    *type == ICMP6_PACKET_TOO_BIG ||
444*0Sstevel@tonic-gate 	    (*type == ICMP6_PARAM_PROB &&
445*0Sstevel@tonic-gate 	    *code == ICMP6_PARAMPROB_NEXTHEADER)) {
446*0Sstevel@tonic-gate 		ip6_t *hip6;
447*0Sstevel@tonic-gate 		struct udphdr *up;
448*0Sstevel@tonic-gate 		icmp6_t *hicmp6;
449*0Sstevel@tonic-gate 
450*0Sstevel@tonic-gate 		cc -= ICMP6_MINLEN;
451*0Sstevel@tonic-gate 		hip6 = (ip6_t *)&(icp6->icmp6_data32[1]);
452*0Sstevel@tonic-gate 		last_hdr = hip6->ip6_nxt;
453*0Sstevel@tonic-gate 		ip6hdr_len = IPv6_hdrlen(hip6, cc, &last_hdr);
454*0Sstevel@tonic-gate 
455*0Sstevel@tonic-gate 		cc -= ip6hdr_len;
456*0Sstevel@tonic-gate 		if (useicmp) {
457*0Sstevel@tonic-gate 			if (*type == ICMP6_ECHO_REPLY &&
458*0Sstevel@tonic-gate 			    icp6->icmp6_id == htons(ident) &&
459*0Sstevel@tonic-gate 			    icp6->icmp6_seq == htons(seq)) {
460*0Sstevel@tonic-gate 				return (REPLY_GOT_TARGET);
461*0Sstevel@tonic-gate 			}
462*0Sstevel@tonic-gate 
463*0Sstevel@tonic-gate 			/* LINTED E_BAD_PTR_CAST_ALIGN */
464*0Sstevel@tonic-gate 			hicmp6 = (icmp6_t *)((uchar_t *)hip6 + ip6hdr_len);
465*0Sstevel@tonic-gate 
466*0Sstevel@tonic-gate 			if (ICMP6_MINLEN <= cc &&
467*0Sstevel@tonic-gate 			    last_hdr == IPPROTO_ICMPV6 &&
468*0Sstevel@tonic-gate 			    hicmp6->icmp6_id == htons(ident) &&
469*0Sstevel@tonic-gate 			    hicmp6->icmp6_seq == htons(seq)) {
470*0Sstevel@tonic-gate 				if (*type == ICMP6_TIME_EXCEEDED) {
471*0Sstevel@tonic-gate 					return (REPLY_GOT_GATEWAY);
472*0Sstevel@tonic-gate 				} else {
473*0Sstevel@tonic-gate 					return (REPLY_GOT_OTHER);
474*0Sstevel@tonic-gate 				}
475*0Sstevel@tonic-gate 			}
476*0Sstevel@tonic-gate 		} else {
477*0Sstevel@tonic-gate 			/* LINTED E_BAD_PTR_CAST_ALIGN */
478*0Sstevel@tonic-gate 			up = (struct udphdr *)((uchar_t *)hip6 + ip6hdr_len);
479*0Sstevel@tonic-gate 			/*
480*0Sstevel@tonic-gate 			 * at least 4 bytes of UDP header is required for this
481*0Sstevel@tonic-gate 			 * check
482*0Sstevel@tonic-gate 			 */
483*0Sstevel@tonic-gate 			if (4 <= cc &&
484*0Sstevel@tonic-gate 			    last_hdr == IPPROTO_UDP &&
485*0Sstevel@tonic-gate 			    up->uh_sport == htons(ident) &&
486*0Sstevel@tonic-gate 			    up->uh_dport == htons((port + seq) %
487*0Sstevel@tonic-gate 			    (MAX_PORT + 1))) {
488*0Sstevel@tonic-gate 				if (*type == ICMP6_DST_UNREACH &&
489*0Sstevel@tonic-gate 				    *code == ICMP6_DST_UNREACH_NOPORT) {
490*0Sstevel@tonic-gate 					return (REPLY_GOT_TARGET);
491*0Sstevel@tonic-gate 				} else if (*type == ICMP6_TIME_EXCEEDED) {
492*0Sstevel@tonic-gate 					return (REPLY_GOT_GATEWAY);
493*0Sstevel@tonic-gate 				} else {
494*0Sstevel@tonic-gate 					return (REPLY_GOT_OTHER);
495*0Sstevel@tonic-gate 				}
496*0Sstevel@tonic-gate 			}
497*0Sstevel@tonic-gate 		}
498*0Sstevel@tonic-gate 	}
499*0Sstevel@tonic-gate 
500*0Sstevel@tonic-gate 	if (verbose) {
501*0Sstevel@tonic-gate 		int i, j;
502*0Sstevel@tonic-gate 		uchar_t *lp = (uchar_t *)icp6;
503*0Sstevel@tonic-gate 		struct in6_addr *dst;
504*0Sstevel@tonic-gate 		struct in6_pktinfo *pkti;
505*0Sstevel@tonic-gate 
506*0Sstevel@tonic-gate 		pkti = (struct in6_pktinfo *)find_ancillary_data(msg,
507*0Sstevel@tonic-gate 		    IPPROTO_IPV6, IPV6_PKTINFO);
508*0Sstevel@tonic-gate 		if (pkti == NULL) {
509*0Sstevel@tonic-gate 			Fprintf(stderr,
510*0Sstevel@tonic-gate 			    "%s: can't find IPV6_PKTINFO ancillary data\n",
511*0Sstevel@tonic-gate 			    prog);
512*0Sstevel@tonic-gate 			exit(EXIT_FAILURE);
513*0Sstevel@tonic-gate 		}
514*0Sstevel@tonic-gate 		dst = &pkti->ipi6_addr;
515*0Sstevel@tonic-gate 		cc = save_cc;
516*0Sstevel@tonic-gate 		Printf("\n%d bytes from %s to ", cc,
517*0Sstevel@tonic-gate 		    inet_ntop(AF_INET6, (const void *)&(from_in6->sin6_addr),
518*0Sstevel@tonic-gate 			temp_buf, sizeof (temp_buf)));
519*0Sstevel@tonic-gate 		Printf("%s: icmp type %d (%s) code %d\n",
520*0Sstevel@tonic-gate 		    inet_ntop(AF_INET6, (const void *)dst,
521*0Sstevel@tonic-gate 			temp_buf, sizeof (temp_buf)),
522*0Sstevel@tonic-gate 		    *type, pr_type6(*type), *code);
523*0Sstevel@tonic-gate 		for (i = 0; i < cc; i += 4) {
524*0Sstevel@tonic-gate 			Printf("%2d: x", i);
525*0Sstevel@tonic-gate 			for (j = 0; ((j < 4) && ((i + j) < cc)); j++)
526*0Sstevel@tonic-gate 				Printf("%2.2x", *lp++);
527*0Sstevel@tonic-gate 			(void) putchar('\n');
528*0Sstevel@tonic-gate 		}
529*0Sstevel@tonic-gate 	}
530*0Sstevel@tonic-gate 
531*0Sstevel@tonic-gate 	return (REPLY_SHORT_PKT);
532*0Sstevel@tonic-gate }
533*0Sstevel@tonic-gate 
534*0Sstevel@tonic-gate /*
535*0Sstevel@tonic-gate  * Return the length of the IPv6 related headers (including extension headers)
536*0Sstevel@tonic-gate  */
537*0Sstevel@tonic-gate static int
538*0Sstevel@tonic-gate IPv6_hdrlen(ip6_t *ip6h, int pkt_len, uint8_t *last_hdr_rtrn)
539*0Sstevel@tonic-gate {
540*0Sstevel@tonic-gate 	int length;
541*0Sstevel@tonic-gate 	int exthdrlength;
542*0Sstevel@tonic-gate 	uint8_t nexthdr;
543*0Sstevel@tonic-gate 	uint8_t *whereptr;
544*0Sstevel@tonic-gate 	ip6_hbh_t *hbhhdr;
545*0Sstevel@tonic-gate 	ip6_dest_t *desthdr;
546*0Sstevel@tonic-gate 	ip6_rthdr_t *rthdr;
547*0Sstevel@tonic-gate 	ip6_frag_t *fraghdr;
548*0Sstevel@tonic-gate 	uint8_t	*endptr;
549*0Sstevel@tonic-gate 
550*0Sstevel@tonic-gate 	length = sizeof (ip6_t);
551*0Sstevel@tonic-gate 
552*0Sstevel@tonic-gate 	whereptr = ((uint8_t *)&ip6h[1]); 	/* point to next hdr */
553*0Sstevel@tonic-gate 	endptr = ((uint8_t *)ip6h) + pkt_len;
554*0Sstevel@tonic-gate 
555*0Sstevel@tonic-gate 	nexthdr = ip6h->ip6_nxt;
556*0Sstevel@tonic-gate 	*last_hdr_rtrn = IPPROTO_NONE;
557*0Sstevel@tonic-gate 
558*0Sstevel@tonic-gate 	if (whereptr >= endptr)
559*0Sstevel@tonic-gate 		return (length);
560*0Sstevel@tonic-gate 
561*0Sstevel@tonic-gate 	while (whereptr < endptr) {
562*0Sstevel@tonic-gate 		*last_hdr_rtrn = nexthdr;
563*0Sstevel@tonic-gate 		switch (nexthdr) {
564*0Sstevel@tonic-gate 		case IPPROTO_HOPOPTS:
565*0Sstevel@tonic-gate 			hbhhdr = (ip6_hbh_t *)whereptr;
566*0Sstevel@tonic-gate 			exthdrlength = 8 * (hbhhdr->ip6h_len + 1);
567*0Sstevel@tonic-gate 			if ((uchar_t *)hbhhdr + exthdrlength > endptr)
568*0Sstevel@tonic-gate 				return (length);
569*0Sstevel@tonic-gate 			nexthdr = hbhhdr->ip6h_nxt;
570*0Sstevel@tonic-gate 			length += exthdrlength;
571*0Sstevel@tonic-gate 			break;
572*0Sstevel@tonic-gate 
573*0Sstevel@tonic-gate 		case IPPROTO_DSTOPTS:
574*0Sstevel@tonic-gate 			desthdr = (ip6_dest_t *)whereptr;
575*0Sstevel@tonic-gate 			exthdrlength = 8 * (desthdr->ip6d_len + 1);
576*0Sstevel@tonic-gate 			if ((uchar_t *)desthdr + exthdrlength > endptr)
577*0Sstevel@tonic-gate 				return (length);
578*0Sstevel@tonic-gate 			nexthdr = desthdr->ip6d_nxt;
579*0Sstevel@tonic-gate 			length += exthdrlength;
580*0Sstevel@tonic-gate 			break;
581*0Sstevel@tonic-gate 
582*0Sstevel@tonic-gate 		case IPPROTO_ROUTING:
583*0Sstevel@tonic-gate 			rthdr = (ip6_rthdr_t *)whereptr;
584*0Sstevel@tonic-gate 			exthdrlength = 8 * (rthdr->ip6r_len + 1);
585*0Sstevel@tonic-gate 			if ((uchar_t *)rthdr + exthdrlength > endptr)
586*0Sstevel@tonic-gate 				return (length);
587*0Sstevel@tonic-gate 			nexthdr = rthdr->ip6r_nxt;
588*0Sstevel@tonic-gate 			length += exthdrlength;
589*0Sstevel@tonic-gate 			break;
590*0Sstevel@tonic-gate 
591*0Sstevel@tonic-gate 		case IPPROTO_FRAGMENT:
592*0Sstevel@tonic-gate 			/* LINTED E_BAD_PTR_CAST_ALIGN */
593*0Sstevel@tonic-gate 			fraghdr = (ip6_frag_t *)whereptr;
594*0Sstevel@tonic-gate 			if ((uchar_t *)&fraghdr[1] > endptr)
595*0Sstevel@tonic-gate 				return (length);
596*0Sstevel@tonic-gate 			nexthdr = fraghdr->ip6f_nxt;
597*0Sstevel@tonic-gate 			length += sizeof (struct ip6_frag);
598*0Sstevel@tonic-gate 			break;
599*0Sstevel@tonic-gate 
600*0Sstevel@tonic-gate 		case IPPROTO_NONE:
601*0Sstevel@tonic-gate 		default:
602*0Sstevel@tonic-gate 			return (length);
603*0Sstevel@tonic-gate 		}
604*0Sstevel@tonic-gate 		whereptr = (uint8_t *)ip6h + length;
605*0Sstevel@tonic-gate 	}
606*0Sstevel@tonic-gate 	*last_hdr_rtrn = nexthdr;
607*0Sstevel@tonic-gate 
608*0Sstevel@tonic-gate 	return (length);
609*0Sstevel@tonic-gate }
610*0Sstevel@tonic-gate 
611*0Sstevel@tonic-gate /*
612*0Sstevel@tonic-gate  * convert an ICMP6 "type" field to a printable string.
613*0Sstevel@tonic-gate  */
614*0Sstevel@tonic-gate static char *
615*0Sstevel@tonic-gate pr_type6(uchar_t type)
616*0Sstevel@tonic-gate {
617*0Sstevel@tonic-gate 	static struct icmptype_table ttab6[] = {
618*0Sstevel@tonic-gate 		{ICMP6_DST_UNREACH,		"Dest Unreachable"},
619*0Sstevel@tonic-gate 		{ICMP6_PACKET_TOO_BIG,		"Packet Too Big"},
620*0Sstevel@tonic-gate 		{ICMP6_TIME_EXCEEDED,		"Time Exceeded"},
621*0Sstevel@tonic-gate 		{ICMP6_PARAM_PROB,		"Param Problem"},
622*0Sstevel@tonic-gate 		{ICMP6_ECHO_REQUEST,		"Echo Request"},
623*0Sstevel@tonic-gate 		{ICMP6_ECHO_REPLY,		"Echo Reply"},
624*0Sstevel@tonic-gate 		{MLD_LISTENER_QUERY,		"Multicast Listener Query"},
625*0Sstevel@tonic-gate 		{MLD_LISTENER_REPORT,		"Multicast Listener Report"},
626*0Sstevel@tonic-gate 		{MLD_LISTENER_REDUCTION,	"Multicast Listener Done"},
627*0Sstevel@tonic-gate 		{ND_ROUTER_SOLICIT,		"Router Solicitation"},
628*0Sstevel@tonic-gate 		{ND_ROUTER_ADVERT,		"Router Advertisement"},
629*0Sstevel@tonic-gate 		{ND_NEIGHBOR_SOLICIT,		"Neighbor Solicitation"},
630*0Sstevel@tonic-gate 		{ND_NEIGHBOR_ADVERT,		"Neighbor Advertisement"},
631*0Sstevel@tonic-gate 		{ND_REDIRECT,			"Redirect Message"}
632*0Sstevel@tonic-gate 	};
633*0Sstevel@tonic-gate 	int i = 0;
634*0Sstevel@tonic-gate 
635*0Sstevel@tonic-gate 	for (i = 0; i < A_CNT(ttab6); i++) {
636*0Sstevel@tonic-gate 		if (ttab6[i].type == type)
637*0Sstevel@tonic-gate 			return (ttab6[i].message);
638*0Sstevel@tonic-gate 	}
639*0Sstevel@tonic-gate 
640*0Sstevel@tonic-gate 	return ("OUT-OF-RANGE");
641*0Sstevel@tonic-gate }
642*0Sstevel@tonic-gate 
643*0Sstevel@tonic-gate 
644*0Sstevel@tonic-gate /*
645*0Sstevel@tonic-gate  * print the IPv6 src address of the reply packet
646*0Sstevel@tonic-gate  */
647*0Sstevel@tonic-gate void
648*0Sstevel@tonic-gate print_addr6(uchar_t *buf, int cc, struct sockaddr *from)
649*0Sstevel@tonic-gate {
650*0Sstevel@tonic-gate 	/* LINTED E_BAD_PTR_CAST_ALIGN */
651*0Sstevel@tonic-gate 	struct sockaddr_in6 *from_in6 = (struct sockaddr_in6 *)from;
652*0Sstevel@tonic-gate 	ip6_t *ip;
653*0Sstevel@tonic-gate 	union any_in_addr ip_addr;
654*0Sstevel@tonic-gate 	char *resolved_name;
655*0Sstevel@tonic-gate 	char temp_buf[INET6_ADDRSTRLEN];	/* use for inet_ntop() */
656*0Sstevel@tonic-gate 
657*0Sstevel@tonic-gate 	ip_addr.addr6 = from_in6->sin6_addr;
658*0Sstevel@tonic-gate 
659*0Sstevel@tonic-gate 	/* LINTED E_BAD_PTR_CAST_ALIGN */
660*0Sstevel@tonic-gate 	ip = (ip6_t *)buf;
661*0Sstevel@tonic-gate 
662*0Sstevel@tonic-gate 	(void) inet_ntop(AF_INET6, &(from_in6->sin6_addr), temp_buf,
663*0Sstevel@tonic-gate 	    sizeof (temp_buf));
664*0Sstevel@tonic-gate 	if (!nflag)
665*0Sstevel@tonic-gate 		resolved_name = inet_name(&ip_addr, AF_INET6);
666*0Sstevel@tonic-gate 	/*
667*0Sstevel@tonic-gate 	 * If the IPv6 address cannot be resolved to hostname, inet_name()
668*0Sstevel@tonic-gate 	 * returns the IPv6 address as a string. In that case, we choose not
669*0Sstevel@tonic-gate 	 * to print it twice. This saves us space on display.
670*0Sstevel@tonic-gate 	 */
671*0Sstevel@tonic-gate 	if (nflag || (strcmp(temp_buf, resolved_name) == 0))
672*0Sstevel@tonic-gate 		Printf(" %s", temp_buf);
673*0Sstevel@tonic-gate 	else
674*0Sstevel@tonic-gate 		Printf(" %s (%s)", resolved_name, temp_buf);
675*0Sstevel@tonic-gate 
676*0Sstevel@tonic-gate 	if (verbose) {
677*0Sstevel@tonic-gate 		Printf(" %d bytes to %s", cc, inet_ntop(AF_INET6,
678*0Sstevel@tonic-gate 		    (const void *) &(ip->ip6_dst), temp_buf,
679*0Sstevel@tonic-gate 		    sizeof (temp_buf)));
680*0Sstevel@tonic-gate 	}
681*0Sstevel@tonic-gate }
682*0Sstevel@tonic-gate 
683*0Sstevel@tonic-gate /*
684*0Sstevel@tonic-gate  * ICMP6 messages which doesn't mean we got the target, or we got a gateway, are
685*0Sstevel@tonic-gate  * processed here. It returns _B_TRUE if it's some sort of 'unreachable'.
686*0Sstevel@tonic-gate  */
687*0Sstevel@tonic-gate boolean_t
688*0Sstevel@tonic-gate print_icmp_other6(uchar_t type, uchar_t code)
689*0Sstevel@tonic-gate {
690*0Sstevel@tonic-gate 	boolean_t unreach = _B_FALSE;
691*0Sstevel@tonic-gate 
692*0Sstevel@tonic-gate 	switch (type) {
693*0Sstevel@tonic-gate 
694*0Sstevel@tonic-gate 	/* this corresponds to "ICMP_UNREACH_NEEDFRAG" in ICMP */
695*0Sstevel@tonic-gate 	case ICMP6_PACKET_TOO_BIG:
696*0Sstevel@tonic-gate 		unreach = _B_TRUE;
697*0Sstevel@tonic-gate 		Printf(" !B");
698*0Sstevel@tonic-gate 		break;
699*0Sstevel@tonic-gate 
700*0Sstevel@tonic-gate 	case ICMP6_PARAM_PROB:
701*0Sstevel@tonic-gate 		/* this corresponds to "ICMP_UNREACH_PROTOCOL" in ICMP */
702*0Sstevel@tonic-gate 		if (code == ICMP6_PARAMPROB_NEXTHEADER) {
703*0Sstevel@tonic-gate 			unreach = _B_TRUE;
704*0Sstevel@tonic-gate 			Printf(" !R");
705*0Sstevel@tonic-gate 		}
706*0Sstevel@tonic-gate 		break;
707*0Sstevel@tonic-gate 
708*0Sstevel@tonic-gate 	case ICMP6_DST_UNREACH:
709*0Sstevel@tonic-gate 		switch (code) {
710*0Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_NOPORT:
711*0Sstevel@tonic-gate 			break;
712*0Sstevel@tonic-gate 
713*0Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_NOROUTE:
714*0Sstevel@tonic-gate 			unreach = _B_TRUE;
715*0Sstevel@tonic-gate 			Printf(" !H");
716*0Sstevel@tonic-gate 			break;
717*0Sstevel@tonic-gate 
718*0Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_ADMIN:
719*0Sstevel@tonic-gate 			unreach = _B_TRUE;
720*0Sstevel@tonic-gate 			Printf(" !X");
721*0Sstevel@tonic-gate 			break;
722*0Sstevel@tonic-gate 
723*0Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_ADDR:
724*0Sstevel@tonic-gate 			unreach = _B_TRUE;
725*0Sstevel@tonic-gate 			Printf(" !A");
726*0Sstevel@tonic-gate 			break;
727*0Sstevel@tonic-gate 
728*0Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_NOTNEIGHBOR:
729*0Sstevel@tonic-gate 			unreach = _B_TRUE;
730*0Sstevel@tonic-gate 			Printf(" !E");
731*0Sstevel@tonic-gate 			break;
732*0Sstevel@tonic-gate 
733*0Sstevel@tonic-gate 		default:
734*0Sstevel@tonic-gate 			unreach = _B_TRUE;
735*0Sstevel@tonic-gate 			Printf(" !<%d>", code);
736*0Sstevel@tonic-gate 			break;
737*0Sstevel@tonic-gate 		}
738*0Sstevel@tonic-gate 		break;
739*0Sstevel@tonic-gate 	default:
740*0Sstevel@tonic-gate 		break;
741*0Sstevel@tonic-gate 	}
742*0Sstevel@tonic-gate 
743*0Sstevel@tonic-gate 	return (unreach);
744*0Sstevel@tonic-gate }
745