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