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