10Sstevel@tonic-gate /*
2*1254Smeem * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
30Sstevel@tonic-gate * Use is subject to license terms.
40Sstevel@tonic-gate */
50Sstevel@tonic-gate
60Sstevel@tonic-gate
70Sstevel@tonic-gate /*
80Sstevel@tonic-gate * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997
90Sstevel@tonic-gate * The Regents of the University of California. All rights reserved.
100Sstevel@tonic-gate *
110Sstevel@tonic-gate * Redistribution and use in source and binary forms, with or without
120Sstevel@tonic-gate * modification, are permitted provided that: (1) source code distributions
130Sstevel@tonic-gate * retain the above copyright notice and this paragraph in its entirety, (2)
140Sstevel@tonic-gate * distributions including binary code include the above copyright notice and
150Sstevel@tonic-gate * this paragraph in its entirety in the documentation or other materials
160Sstevel@tonic-gate * provided with the distribution, and (3) all advertising materials mentioning
170Sstevel@tonic-gate * features or use of this software display the following acknowledgement:
180Sstevel@tonic-gate * ``This product includes software developed by the University of California,
190Sstevel@tonic-gate * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
200Sstevel@tonic-gate * the University nor the names of its contributors may be used to endorse
210Sstevel@tonic-gate * or promote products derived from this software without specific prior
220Sstevel@tonic-gate * written permission.
230Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
240Sstevel@tonic-gate * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
250Sstevel@tonic-gate * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
260Sstevel@tonic-gate *
270Sstevel@tonic-gate *
280Sstevel@tonic-gate * @(#)$Header: traceroute.c,v 1.49 97/06/13 02:30:23 leres Exp $ (LBL)
290Sstevel@tonic-gate */
300Sstevel@tonic-gate
310Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
320Sstevel@tonic-gate
330Sstevel@tonic-gate #include <sys/socket.h>
340Sstevel@tonic-gate
350Sstevel@tonic-gate #include <stdio.h>
360Sstevel@tonic-gate #include <stdlib.h>
370Sstevel@tonic-gate #include <ctype.h>
380Sstevel@tonic-gate #include <strings.h>
390Sstevel@tonic-gate #include <libintl.h>
400Sstevel@tonic-gate #include <errno.h>
410Sstevel@tonic-gate #include <netdb.h>
420Sstevel@tonic-gate
430Sstevel@tonic-gate #include <netinet/in_systm.h>
440Sstevel@tonic-gate #include <netinet/in.h>
450Sstevel@tonic-gate #include <netinet/ip.h>
460Sstevel@tonic-gate #include <netinet/ip_var.h>
470Sstevel@tonic-gate #include <netinet/ip_icmp.h>
480Sstevel@tonic-gate #include <netinet/udp.h>
490Sstevel@tonic-gate #include <netinet/udp_var.h>
500Sstevel@tonic-gate #include <netinet/ip6.h>
510Sstevel@tonic-gate #include <netinet/icmp6.h>
520Sstevel@tonic-gate
530Sstevel@tonic-gate #include <arpa/inet.h>
540Sstevel@tonic-gate
55*1254Smeem #include <libinetutil.h>
560Sstevel@tonic-gate #include "traceroute.h"
570Sstevel@tonic-gate
580Sstevel@tonic-gate int check_reply6(struct msghdr *, int, int, uchar_t *, uchar_t *);
590Sstevel@tonic-gate void *find_ancillary_data(struct msghdr *, int, int);
600Sstevel@tonic-gate extern char *inet_name(union any_in_addr *, int);
610Sstevel@tonic-gate static int IPv6_hdrlen(ip6_t *, int, uint8_t *);
620Sstevel@tonic-gate static char *pr_type6(uchar_t);
630Sstevel@tonic-gate void print_addr6(uchar_t *, int, struct sockaddr *);
640Sstevel@tonic-gate boolean_t print_icmp_other6(uchar_t, uchar_t);
650Sstevel@tonic-gate void send_probe6(int, struct msghdr *, struct ip *, int, int,
660Sstevel@tonic-gate struct timeval *, int);
670Sstevel@tonic-gate void set_ancillary_data(struct msghdr *, int, union any_in_addr *, int, uint_t);
680Sstevel@tonic-gate struct ip *set_buffers6(int);
690Sstevel@tonic-gate static boolean_t update_hoplimit_ancillary_data(struct msghdr *, int);
700Sstevel@tonic-gate
710Sstevel@tonic-gate /*
720Sstevel@tonic-gate * prepares the buffer to be sent as an IP datagram
730Sstevel@tonic-gate */
740Sstevel@tonic-gate struct ip *
set_buffers6(int plen)750Sstevel@tonic-gate set_buffers6(int plen)
760Sstevel@tonic-gate {
770Sstevel@tonic-gate struct ip *outip;
780Sstevel@tonic-gate uchar_t *outp;
790Sstevel@tonic-gate struct udphdr *outudp;
800Sstevel@tonic-gate struct icmp *outicmp;
810Sstevel@tonic-gate int optlen = 0;
820Sstevel@tonic-gate
830Sstevel@tonic-gate outip = (struct ip *)malloc((size_t)plen);
840Sstevel@tonic-gate if (outip == NULL) {
850Sstevel@tonic-gate Fprintf(stderr, "%s: malloc: %s\n", prog, strerror(errno));
860Sstevel@tonic-gate exit(EXIT_FAILURE);
870Sstevel@tonic-gate }
880Sstevel@tonic-gate
890Sstevel@tonic-gate if (gw_count > 0) {
900Sstevel@tonic-gate /* ip6_rthdr0 structure includes one gateway address */
910Sstevel@tonic-gate optlen = sizeof (struct ip6_rthdr0) +
920Sstevel@tonic-gate gw_count * sizeof (struct in6_addr);
930Sstevel@tonic-gate }
940Sstevel@tonic-gate
950Sstevel@tonic-gate (void) memset((char *)outip, 0, (size_t)plen);
960Sstevel@tonic-gate outp = (uchar_t *)(outip + 1);
970Sstevel@tonic-gate
980Sstevel@tonic-gate if (useicmp) {
990Sstevel@tonic-gate /* LINTED E_BAD_PTR_CAST_ALIGN */
1000Sstevel@tonic-gate outicmp = (struct icmp *)outp;
1010Sstevel@tonic-gate outicmp->icmp_type = ICMP6_ECHO_REQUEST;
1020Sstevel@tonic-gate outicmp->icmp_id = htons(ident);
1030Sstevel@tonic-gate } else {
1040Sstevel@tonic-gate /* LINTED E_BAD_PTR_CAST_ALIGN */
1050Sstevel@tonic-gate outudp = (struct udphdr *)outp;
1060Sstevel@tonic-gate /*
1070Sstevel@tonic-gate * "source port" is set at bind() call, so we don't do it
1080Sstevel@tonic-gate * again
1090Sstevel@tonic-gate */
1100Sstevel@tonic-gate outudp->uh_ulen = htons((ushort_t)(plen -
1110Sstevel@tonic-gate (sizeof (struct ip6_hdr) + optlen)));
1120Sstevel@tonic-gate }
1130Sstevel@tonic-gate
1140Sstevel@tonic-gate return (outip);
1150Sstevel@tonic-gate }
1160Sstevel@tonic-gate
1170Sstevel@tonic-gate /*
1180Sstevel@tonic-gate * Initialize the msghdr for specifying hoplimit, outgoing interface and routing
1190Sstevel@tonic-gate * header for the probe packets.
1200Sstevel@tonic-gate */
1210Sstevel@tonic-gate void
set_ancillary_data(struct msghdr * msgp,int hoplimit,union any_in_addr * gwIPlist,int gw_cnt,uint_t if_index)1220Sstevel@tonic-gate set_ancillary_data(struct msghdr *msgp, int hoplimit,
1230Sstevel@tonic-gate union any_in_addr *gwIPlist, int gw_cnt, uint_t if_index)
1240Sstevel@tonic-gate {
1250Sstevel@tonic-gate size_t hoplimit_space;
1260Sstevel@tonic-gate size_t rthdr_space;
1270Sstevel@tonic-gate size_t pktinfo_space;
1280Sstevel@tonic-gate size_t bufspace;
1290Sstevel@tonic-gate struct cmsghdr *cmsgp;
1300Sstevel@tonic-gate uchar_t *cmsg_datap;
1310Sstevel@tonic-gate int i;
1320Sstevel@tonic-gate
1330Sstevel@tonic-gate msgp->msg_control = NULL;
1340Sstevel@tonic-gate msgp->msg_controllen = 0;
1350Sstevel@tonic-gate
1360Sstevel@tonic-gate /*
1370Sstevel@tonic-gate * Need to figure out size of buffer needed for ancillary data
1380Sstevel@tonic-gate * containing routing header and packet info options.
1390Sstevel@tonic-gate *
1400Sstevel@tonic-gate * Portable heuristic to compute upper bound on space needed for
1410Sstevel@tonic-gate * N ancillary data options. It assumes up to _MAX_ALIGNMENT padding
1420Sstevel@tonic-gate * after both header and data as the worst possible upper bound on space
1430Sstevel@tonic-gate * consumed by padding.
1440Sstevel@tonic-gate * It also adds one extra "sizeof (struct cmsghdr)" for the last option.
1450Sstevel@tonic-gate * This is needed because we would like to use CMSG_NXTHDR() while
1460Sstevel@tonic-gate * composing the buffer. The CMSG_NXTHDR() macro is designed better for
1470Sstevel@tonic-gate * parsing than composing the buffer. It requires the pointer it returns
1480Sstevel@tonic-gate * to leave space in buffer for addressing a cmsghdr and we want to make
1490Sstevel@tonic-gate * sure it works for us while we skip beyond the last ancillary data
1500Sstevel@tonic-gate * option.
1510Sstevel@tonic-gate *
1520Sstevel@tonic-gate * bufspace[i] = sizeof(struct cmsghdr) + <pad after header> +
1530Sstevel@tonic-gate * <option[i] content length> + <pad after data>;
1540Sstevel@tonic-gate *
1550Sstevel@tonic-gate * total_bufspace = bufspace[0] + bufspace[1] + ...
1560Sstevel@tonic-gate * ... + bufspace[N-1] + sizeof (struct cmsghdr);
1570Sstevel@tonic-gate */
1580Sstevel@tonic-gate
1590Sstevel@tonic-gate rthdr_space = 0;
1600Sstevel@tonic-gate pktinfo_space = 0;
1610Sstevel@tonic-gate /* We'll always set the hoplimit of the outgoing packets */
1620Sstevel@tonic-gate hoplimit_space = sizeof (int);
1630Sstevel@tonic-gate bufspace = sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
1640Sstevel@tonic-gate hoplimit_space + _MAX_ALIGNMENT;
1650Sstevel@tonic-gate
1660Sstevel@tonic-gate if (gw_cnt > 0) {
1670Sstevel@tonic-gate rthdr_space = inet6_rth_space(IPV6_RTHDR_TYPE_0, gw_cnt);
1680Sstevel@tonic-gate bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
1690Sstevel@tonic-gate rthdr_space + _MAX_ALIGNMENT;
1700Sstevel@tonic-gate }
1710Sstevel@tonic-gate
1720Sstevel@tonic-gate if (if_index != 0) {
1730Sstevel@tonic-gate pktinfo_space = sizeof (struct in6_pktinfo);
1740Sstevel@tonic-gate bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
1750Sstevel@tonic-gate pktinfo_space + _MAX_ALIGNMENT;
1760Sstevel@tonic-gate }
1770Sstevel@tonic-gate
1780Sstevel@tonic-gate /*
1790Sstevel@tonic-gate * We need to temporarily set the msgp->msg_controllen to bufspace
1800Sstevel@tonic-gate * (we will later trim it to actual length used). This is needed because
1810Sstevel@tonic-gate * CMSG_NXTHDR() uses it to check we have not exceeded the bounds.
1820Sstevel@tonic-gate */
1830Sstevel@tonic-gate bufspace += sizeof (struct cmsghdr);
1840Sstevel@tonic-gate msgp->msg_controllen = bufspace;
1850Sstevel@tonic-gate
1860Sstevel@tonic-gate msgp->msg_control = (struct cmsghdr *)malloc(bufspace);
1870Sstevel@tonic-gate if (msgp->msg_control == NULL) {
1880Sstevel@tonic-gate Fprintf(stderr, "%s: malloc %s\n", prog, strerror(errno));
1890Sstevel@tonic-gate exit(EXIT_FAILURE);
1900Sstevel@tonic-gate }
1910Sstevel@tonic-gate cmsgp = CMSG_FIRSTHDR(msgp);
1920Sstevel@tonic-gate
1930Sstevel@tonic-gate /*
1940Sstevel@tonic-gate * Fill ancillary data. First hoplimit, then rthdr and pktinfo if
1950Sstevel@tonic-gate * needed.
1960Sstevel@tonic-gate */
1970Sstevel@tonic-gate
1980Sstevel@tonic-gate /* set hoplimit ancillary data */
1990Sstevel@tonic-gate cmsgp->cmsg_level = IPPROTO_IPV6;
2000Sstevel@tonic-gate cmsgp->cmsg_type = IPV6_HOPLIMIT;
2010Sstevel@tonic-gate cmsg_datap = CMSG_DATA(cmsgp);
2020Sstevel@tonic-gate /* LINTED E_BAD_PTR_CAST_ALIGN */
2030Sstevel@tonic-gate *(int *)cmsg_datap = hoplimit;
2040Sstevel@tonic-gate cmsgp->cmsg_len = cmsg_datap + hoplimit_space - (uchar_t *)cmsgp;
2050Sstevel@tonic-gate cmsgp = CMSG_NXTHDR(msgp, cmsgp);
2060Sstevel@tonic-gate
2070Sstevel@tonic-gate /* set rthdr ancillary data if needed */
2080Sstevel@tonic-gate if (gw_cnt > 0) {
2090Sstevel@tonic-gate struct ip6_rthdr0 *rthdr0p;
2100Sstevel@tonic-gate
2110Sstevel@tonic-gate cmsgp->cmsg_level = IPPROTO_IPV6;
2120Sstevel@tonic-gate cmsgp->cmsg_type = IPV6_RTHDR;
2130Sstevel@tonic-gate cmsg_datap = CMSG_DATA(cmsgp);
2140Sstevel@tonic-gate
2150Sstevel@tonic-gate /*
2160Sstevel@tonic-gate * Initialize rthdr structure
2170Sstevel@tonic-gate */
2180Sstevel@tonic-gate /* LINTED E_BAD_PTR_CAST_ALIGN */
2190Sstevel@tonic-gate rthdr0p = (struct ip6_rthdr0 *)cmsg_datap;
2200Sstevel@tonic-gate if (inet6_rth_init(rthdr0p, rthdr_space,
2210Sstevel@tonic-gate IPV6_RTHDR_TYPE_0, gw_cnt) == NULL) {
2220Sstevel@tonic-gate Fprintf(stderr, "%s: inet6_rth_init failed\n",
2230Sstevel@tonic-gate prog);
2240Sstevel@tonic-gate exit(EXIT_FAILURE);
2250Sstevel@tonic-gate }
2260Sstevel@tonic-gate
2270Sstevel@tonic-gate /*
2280Sstevel@tonic-gate * Stuff in gateway addresses
2290Sstevel@tonic-gate */
2300Sstevel@tonic-gate for (i = 0; i < gw_cnt; i++) {
2310Sstevel@tonic-gate if (inet6_rth_add(rthdr0p,
2320Sstevel@tonic-gate &gwIPlist[i].addr6) == -1) {
2330Sstevel@tonic-gate Fprintf(stderr,
2340Sstevel@tonic-gate "%s: inet6_rth_add\n", prog);
2350Sstevel@tonic-gate exit(EXIT_FAILURE);
2360Sstevel@tonic-gate }
2370Sstevel@tonic-gate }
2380Sstevel@tonic-gate
2390Sstevel@tonic-gate cmsgp->cmsg_len = cmsg_datap + rthdr_space - (uchar_t *)cmsgp;
2400Sstevel@tonic-gate cmsgp = CMSG_NXTHDR(msgp, cmsgp);
2410Sstevel@tonic-gate }
2420Sstevel@tonic-gate
2430Sstevel@tonic-gate /* set pktinfo ancillary data if needed */
2440Sstevel@tonic-gate if (if_index != 0) {
2450Sstevel@tonic-gate struct in6_pktinfo *pktinfop;
2460Sstevel@tonic-gate
2470Sstevel@tonic-gate cmsgp->cmsg_level = IPPROTO_IPV6;
2480Sstevel@tonic-gate cmsgp->cmsg_type = IPV6_PKTINFO;
2490Sstevel@tonic-gate cmsg_datap = CMSG_DATA(cmsgp);
2500Sstevel@tonic-gate
2510Sstevel@tonic-gate /* LINTED E_BAD_PTR_CAST_ALIGN */
2520Sstevel@tonic-gate pktinfop = (struct in6_pktinfo *)cmsg_datap;
2530Sstevel@tonic-gate /*
2540Sstevel@tonic-gate * We don't know if pktinfop->ipi6_addr is aligned properly,
2550Sstevel@tonic-gate * therefore let's use bcopy, instead of assignment.
2560Sstevel@tonic-gate */
2570Sstevel@tonic-gate (void) bcopy(&in6addr_any, &pktinfop->ipi6_addr,
2580Sstevel@tonic-gate sizeof (struct in6_addr));
2590Sstevel@tonic-gate
2600Sstevel@tonic-gate /*
2610Sstevel@tonic-gate * We can assume pktinfop->ipi6_ifindex is 32 bit aligned.
2620Sstevel@tonic-gate */
2630Sstevel@tonic-gate pktinfop->ipi6_ifindex = if_index;
2640Sstevel@tonic-gate cmsgp->cmsg_len = cmsg_datap + pktinfo_space - (uchar_t *)cmsgp;
2650Sstevel@tonic-gate cmsgp = CMSG_NXTHDR(msgp, cmsgp);
2660Sstevel@tonic-gate }
2670Sstevel@tonic-gate
2680Sstevel@tonic-gate msgp->msg_controllen = (char *)cmsgp - (char *)msgp->msg_control;
2690Sstevel@tonic-gate }
2700Sstevel@tonic-gate
2710Sstevel@tonic-gate /*
2720Sstevel@tonic-gate * Parses the given msg->msg_control to find the IPV6_HOPLIMIT ancillary data
2730Sstevel@tonic-gate * and update the hoplimit.
2740Sstevel@tonic-gate * Returns _B_FALSE if it can't find IPV6_HOPLIMIT ancillary data, _B_TRUE
2750Sstevel@tonic-gate * otherwise.
2760Sstevel@tonic-gate */
2770Sstevel@tonic-gate static boolean_t
update_hoplimit_ancillary_data(struct msghdr * msg,int hoplimit)2780Sstevel@tonic-gate update_hoplimit_ancillary_data(struct msghdr *msg, int hoplimit)
2790Sstevel@tonic-gate {
2800Sstevel@tonic-gate struct cmsghdr *cmsg;
2810Sstevel@tonic-gate int *intp;
2820Sstevel@tonic-gate
2830Sstevel@tonic-gate for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
2840Sstevel@tonic-gate cmsg = CMSG_NXTHDR(msg, cmsg)) {
2850Sstevel@tonic-gate if (cmsg->cmsg_level == IPPROTO_IPV6 &&
2860Sstevel@tonic-gate cmsg->cmsg_type == IPV6_HOPLIMIT) {
2870Sstevel@tonic-gate /* LINTED E_BAD_PTR_CAST_ALIGN */
2880Sstevel@tonic-gate intp = (int *)(CMSG_DATA(cmsg));
2890Sstevel@tonic-gate *intp = hoplimit;
2900Sstevel@tonic-gate return (_B_TRUE);
2910Sstevel@tonic-gate }
2920Sstevel@tonic-gate }
2930Sstevel@tonic-gate
2940Sstevel@tonic-gate return (_B_FALSE);
2950Sstevel@tonic-gate }
2960Sstevel@tonic-gate
2970Sstevel@tonic-gate /*
2980Sstevel@tonic-gate * send a probe packet to the destination
2990Sstevel@tonic-gate */
3000Sstevel@tonic-gate void
send_probe6(int sndsock,struct msghdr * msg6,struct ip * outip,int seq,int ttl,struct timeval * tp,int packlen)3010Sstevel@tonic-gate send_probe6(int sndsock, struct msghdr *msg6, struct ip *outip, int seq,
3020Sstevel@tonic-gate int ttl, struct timeval *tp, int packlen)
3030Sstevel@tonic-gate {
3040Sstevel@tonic-gate uchar_t *outp;
3050Sstevel@tonic-gate struct icmp *outicmp;
3060Sstevel@tonic-gate struct outdata *outdata;
3070Sstevel@tonic-gate struct iovec iov;
3080Sstevel@tonic-gate int cc;
3090Sstevel@tonic-gate int optlen = 0;
3100Sstevel@tonic-gate int send_size;
3110Sstevel@tonic-gate struct sockaddr_in6 *to6;
3120Sstevel@tonic-gate
3130Sstevel@tonic-gate if (gw_count > 0) {
3140Sstevel@tonic-gate /* ip6_rthdr0 structure includes one gateway address */
3150Sstevel@tonic-gate optlen = sizeof (struct ip6_rthdr0) +
3160Sstevel@tonic-gate gw_count * sizeof (struct in6_addr);
3170Sstevel@tonic-gate }
3180Sstevel@tonic-gate
3190Sstevel@tonic-gate send_size = packlen - sizeof (struct ip6_hdr) - optlen;
3200Sstevel@tonic-gate
3210Sstevel@tonic-gate /* if using UDP, further discount UDP header size */
3220Sstevel@tonic-gate if (!useicmp)
3230Sstevel@tonic-gate send_size -= sizeof (struct udphdr);
3240Sstevel@tonic-gate
3250Sstevel@tonic-gate /* initialize buffer pointers */
3260Sstevel@tonic-gate outp = (uchar_t *)(outip + 1);
3270Sstevel@tonic-gate /* LINTED E_BAD_PTR_CAST_ALIGN */
3280Sstevel@tonic-gate outicmp = (struct icmp *)outp;
3290Sstevel@tonic-gate /* LINTED E_BAD_PTR_CAST_ALIGN */
3300Sstevel@tonic-gate outdata = (struct outdata *)(outp + ICMP6_MINLEN);
3310Sstevel@tonic-gate
3320Sstevel@tonic-gate if (!update_hoplimit_ancillary_data(msg6, ttl)) {
3330Sstevel@tonic-gate Fprintf(stderr,
3340Sstevel@tonic-gate "%s: can't find IPV6_HOPLIMIT ancillary data\n", prog);
3350Sstevel@tonic-gate exit(EXIT_FAILURE);
3360Sstevel@tonic-gate }
3370Sstevel@tonic-gate
3380Sstevel@tonic-gate /* Payload */
3390Sstevel@tonic-gate outdata->seq = seq;
3400Sstevel@tonic-gate outdata->ttl = ttl;
3410Sstevel@tonic-gate outdata->tv = *tp;
3420Sstevel@tonic-gate
3430Sstevel@tonic-gate if (useicmp) {
3440Sstevel@tonic-gate outicmp->icmp_seq = htons(seq);
3450Sstevel@tonic-gate } else {
3460Sstevel@tonic-gate to6 = (struct sockaddr_in6 *)msg6->msg_name;
3470Sstevel@tonic-gate to6->sin6_port = htons((port + seq) % (MAX_PORT + 1));
3480Sstevel@tonic-gate }
3490Sstevel@tonic-gate
3500Sstevel@tonic-gate iov.iov_base = outp;
3510Sstevel@tonic-gate iov.iov_len = send_size;
3520Sstevel@tonic-gate
3530Sstevel@tonic-gate msg6->msg_iov = &iov;
3540Sstevel@tonic-gate msg6->msg_iovlen = 1;
3550Sstevel@tonic-gate
3560Sstevel@tonic-gate cc = sendmsg(sndsock, msg6, 0);
3570Sstevel@tonic-gate
3580Sstevel@tonic-gate if (cc < 0 || cc != send_size) {
3590Sstevel@tonic-gate if (cc < 0) {
3600Sstevel@tonic-gate Fprintf(stderr, "%s: sendmsg: %s\n", prog,
3610Sstevel@tonic-gate strerror(errno));
3620Sstevel@tonic-gate }
3630Sstevel@tonic-gate Printf("%s: wrote %s %d chars, ret=%d\n",
3640Sstevel@tonic-gate prog, hostname, send_size, cc);
3650Sstevel@tonic-gate (void) fflush(stdout);
3660Sstevel@tonic-gate }
3670Sstevel@tonic-gate }
3680Sstevel@tonic-gate
3690Sstevel@tonic-gate /*
3700Sstevel@tonic-gate * Return a pointer to the ancillary data for the given cmsg_level and
3710Sstevel@tonic-gate * cmsg_type.
3720Sstevel@tonic-gate * If not found return NULL.
3730Sstevel@tonic-gate */
3740Sstevel@tonic-gate void *
find_ancillary_data(struct msghdr * msg,int cmsg_level,int cmsg_type)3750Sstevel@tonic-gate find_ancillary_data(struct msghdr *msg, int cmsg_level, int cmsg_type)
3760Sstevel@tonic-gate {
3770Sstevel@tonic-gate struct cmsghdr *cmsg;
3780Sstevel@tonic-gate
3790Sstevel@tonic-gate for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
3800Sstevel@tonic-gate cmsg = CMSG_NXTHDR(msg, cmsg)) {
3810Sstevel@tonic-gate if (cmsg->cmsg_level == cmsg_level &&
3820Sstevel@tonic-gate cmsg->cmsg_type == cmsg_type) {
3830Sstevel@tonic-gate return (CMSG_DATA(cmsg));
3840Sstevel@tonic-gate }
3850Sstevel@tonic-gate }
3860Sstevel@tonic-gate return (NULL);
3870Sstevel@tonic-gate }
3880Sstevel@tonic-gate
3890Sstevel@tonic-gate /*
3900Sstevel@tonic-gate * Check out the reply packet to see if it's what we were expecting.
3910Sstevel@tonic-gate * Returns REPLY_GOT_TARGET if the reply comes from the target
3920Sstevel@tonic-gate * REPLY_GOT_GATEWAY if an intermediate gateway sends TIME_EXCEEDED
3930Sstevel@tonic-gate * REPLY_GOT_OTHER for other kinds of unreachables indicating none of
3940Sstevel@tonic-gate * the above two cases
3950Sstevel@tonic-gate *
3960Sstevel@tonic-gate * It also sets the icmp type and icmp code values
3970Sstevel@tonic-gate */
3980Sstevel@tonic-gate int
check_reply6(struct msghdr * msg,int cc,int seq,uchar_t * type,uchar_t * code)3990Sstevel@tonic-gate check_reply6(struct msghdr *msg, int cc, int seq, uchar_t *type, uchar_t *code)
4000Sstevel@tonic-gate {
4010Sstevel@tonic-gate uchar_t *buf = msg->msg_iov->iov_base;
4020Sstevel@tonic-gate struct sockaddr_in6 *from_in6 = (struct sockaddr_in6 *)msg->msg_name;
4030Sstevel@tonic-gate icmp6_t *icp6;
4040Sstevel@tonic-gate ulong_t ip6hdr_len;
4050Sstevel@tonic-gate uint8_t last_hdr;
4060Sstevel@tonic-gate int save_cc = cc;
4070Sstevel@tonic-gate char temp_buf[INET6_ADDRSTRLEN]; /* use for inet_ntop() */
4080Sstevel@tonic-gate
4090Sstevel@tonic-gate /* Ignore packets > 64k or control buffers that don't fit */
4100Sstevel@tonic-gate if (msg->msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
4110Sstevel@tonic-gate if (verbose) {
4120Sstevel@tonic-gate Printf("Truncated message: msg_flags 0x%x from %s\n",
4130Sstevel@tonic-gate msg->msg_flags,
4140Sstevel@tonic-gate inet_ntop(AF_INET6,
4150Sstevel@tonic-gate (void *)&(from_in6->sin6_addr),
4160Sstevel@tonic-gate temp_buf, sizeof (temp_buf)));
4170Sstevel@tonic-gate }
4180Sstevel@tonic-gate return (REPLY_SHORT_PKT);
4190Sstevel@tonic-gate }
4200Sstevel@tonic-gate if (cc < ICMP6_MINLEN) {
4210Sstevel@tonic-gate if (verbose) {
4220Sstevel@tonic-gate Printf("packet too short (%d bytes) from %s\n",
4230Sstevel@tonic-gate cc,
4240Sstevel@tonic-gate inet_ntop(AF_INET6,
4250Sstevel@tonic-gate (void *)&(from_in6->sin6_addr),
4260Sstevel@tonic-gate temp_buf, sizeof (temp_buf)));
4270Sstevel@tonic-gate }
4280Sstevel@tonic-gate return (REPLY_SHORT_PKT);
4290Sstevel@tonic-gate }
4300Sstevel@tonic-gate /* LINTED E_BAD_PTR_CAST_ALIGN */
4310Sstevel@tonic-gate icp6 = (icmp6_t *)buf;
4320Sstevel@tonic-gate *type = icp6->icmp6_type;
4330Sstevel@tonic-gate *code = icp6->icmp6_code;
4340Sstevel@tonic-gate
4350Sstevel@tonic-gate /*
4360Sstevel@tonic-gate * traceroute interprets only ICMP6_TIME_EXCEED_TRANSIT,
4370Sstevel@tonic-gate * ICMP6_DST_UNREACH, ICMP6_ECHO_REPLY, ICMP6_PACKET_TOO_BIG and
4380Sstevel@tonic-gate * ICMP6_PARAMPROB_NEXTHEADER, ignores others
4390Sstevel@tonic-gate */
4400Sstevel@tonic-gate if ((*type == ICMP6_TIME_EXCEEDED &&
4410Sstevel@tonic-gate *code == ICMP6_TIME_EXCEED_TRANSIT) ||
4420Sstevel@tonic-gate *type == ICMP6_DST_UNREACH || *type == ICMP6_ECHO_REPLY ||
4430Sstevel@tonic-gate *type == ICMP6_PACKET_TOO_BIG ||
4440Sstevel@tonic-gate (*type == ICMP6_PARAM_PROB &&
4450Sstevel@tonic-gate *code == ICMP6_PARAMPROB_NEXTHEADER)) {
4460Sstevel@tonic-gate ip6_t *hip6;
4470Sstevel@tonic-gate struct udphdr *up;
4480Sstevel@tonic-gate icmp6_t *hicmp6;
4490Sstevel@tonic-gate
4500Sstevel@tonic-gate cc -= ICMP6_MINLEN;
4510Sstevel@tonic-gate hip6 = (ip6_t *)&(icp6->icmp6_data32[1]);
4520Sstevel@tonic-gate last_hdr = hip6->ip6_nxt;
4530Sstevel@tonic-gate ip6hdr_len = IPv6_hdrlen(hip6, cc, &last_hdr);
4540Sstevel@tonic-gate
4550Sstevel@tonic-gate cc -= ip6hdr_len;
4560Sstevel@tonic-gate if (useicmp) {
4570Sstevel@tonic-gate if (*type == ICMP6_ECHO_REPLY &&
4580Sstevel@tonic-gate icp6->icmp6_id == htons(ident) &&
4590Sstevel@tonic-gate icp6->icmp6_seq == htons(seq)) {
4600Sstevel@tonic-gate return (REPLY_GOT_TARGET);
4610Sstevel@tonic-gate }
4620Sstevel@tonic-gate
4630Sstevel@tonic-gate /* LINTED E_BAD_PTR_CAST_ALIGN */
4640Sstevel@tonic-gate hicmp6 = (icmp6_t *)((uchar_t *)hip6 + ip6hdr_len);
4650Sstevel@tonic-gate
4660Sstevel@tonic-gate if (ICMP6_MINLEN <= cc &&
4670Sstevel@tonic-gate last_hdr == IPPROTO_ICMPV6 &&
4680Sstevel@tonic-gate hicmp6->icmp6_id == htons(ident) &&
4690Sstevel@tonic-gate hicmp6->icmp6_seq == htons(seq)) {
4700Sstevel@tonic-gate if (*type == ICMP6_TIME_EXCEEDED) {
4710Sstevel@tonic-gate return (REPLY_GOT_GATEWAY);
4720Sstevel@tonic-gate } else {
4730Sstevel@tonic-gate return (REPLY_GOT_OTHER);
4740Sstevel@tonic-gate }
4750Sstevel@tonic-gate }
4760Sstevel@tonic-gate } else {
4770Sstevel@tonic-gate /* LINTED E_BAD_PTR_CAST_ALIGN */
4780Sstevel@tonic-gate up = (struct udphdr *)((uchar_t *)hip6 + ip6hdr_len);
4790Sstevel@tonic-gate /*
4800Sstevel@tonic-gate * at least 4 bytes of UDP header is required for this
4810Sstevel@tonic-gate * check
4820Sstevel@tonic-gate */
4830Sstevel@tonic-gate if (4 <= cc &&
4840Sstevel@tonic-gate last_hdr == IPPROTO_UDP &&
4850Sstevel@tonic-gate up->uh_sport == htons(ident) &&
4860Sstevel@tonic-gate up->uh_dport == htons((port + seq) %
4870Sstevel@tonic-gate (MAX_PORT + 1))) {
4880Sstevel@tonic-gate if (*type == ICMP6_DST_UNREACH &&
4890Sstevel@tonic-gate *code == ICMP6_DST_UNREACH_NOPORT) {
4900Sstevel@tonic-gate return (REPLY_GOT_TARGET);
4910Sstevel@tonic-gate } else if (*type == ICMP6_TIME_EXCEEDED) {
4920Sstevel@tonic-gate return (REPLY_GOT_GATEWAY);
4930Sstevel@tonic-gate } else {
4940Sstevel@tonic-gate return (REPLY_GOT_OTHER);
4950Sstevel@tonic-gate }
4960Sstevel@tonic-gate }
4970Sstevel@tonic-gate }
4980Sstevel@tonic-gate }
4990Sstevel@tonic-gate
5000Sstevel@tonic-gate if (verbose) {
5010Sstevel@tonic-gate int i, j;
5020Sstevel@tonic-gate uchar_t *lp = (uchar_t *)icp6;
5030Sstevel@tonic-gate struct in6_addr *dst;
5040Sstevel@tonic-gate struct in6_pktinfo *pkti;
5050Sstevel@tonic-gate
5060Sstevel@tonic-gate pkti = (struct in6_pktinfo *)find_ancillary_data(msg,
5070Sstevel@tonic-gate IPPROTO_IPV6, IPV6_PKTINFO);
5080Sstevel@tonic-gate if (pkti == NULL) {
5090Sstevel@tonic-gate Fprintf(stderr,
5100Sstevel@tonic-gate "%s: can't find IPV6_PKTINFO ancillary data\n",
5110Sstevel@tonic-gate prog);
5120Sstevel@tonic-gate exit(EXIT_FAILURE);
5130Sstevel@tonic-gate }
5140Sstevel@tonic-gate dst = &pkti->ipi6_addr;
5150Sstevel@tonic-gate cc = save_cc;
5160Sstevel@tonic-gate Printf("\n%d bytes from %s to ", cc,
5170Sstevel@tonic-gate inet_ntop(AF_INET6, (const void *)&(from_in6->sin6_addr),
5180Sstevel@tonic-gate temp_buf, sizeof (temp_buf)));
5190Sstevel@tonic-gate Printf("%s: icmp type %d (%s) code %d\n",
5200Sstevel@tonic-gate inet_ntop(AF_INET6, (const void *)dst,
5210Sstevel@tonic-gate temp_buf, sizeof (temp_buf)),
5220Sstevel@tonic-gate *type, pr_type6(*type), *code);
5230Sstevel@tonic-gate for (i = 0; i < cc; i += 4) {
5240Sstevel@tonic-gate Printf("%2d: x", i);
5250Sstevel@tonic-gate for (j = 0; ((j < 4) && ((i + j) < cc)); j++)
5260Sstevel@tonic-gate Printf("%2.2x", *lp++);
5270Sstevel@tonic-gate (void) putchar('\n');
5280Sstevel@tonic-gate }
5290Sstevel@tonic-gate }
5300Sstevel@tonic-gate
5310Sstevel@tonic-gate return (REPLY_SHORT_PKT);
5320Sstevel@tonic-gate }
5330Sstevel@tonic-gate
5340Sstevel@tonic-gate /*
5350Sstevel@tonic-gate * Return the length of the IPv6 related headers (including extension headers)
5360Sstevel@tonic-gate */
5370Sstevel@tonic-gate static int
IPv6_hdrlen(ip6_t * ip6h,int pkt_len,uint8_t * last_hdr_rtrn)5380Sstevel@tonic-gate IPv6_hdrlen(ip6_t *ip6h, int pkt_len, uint8_t *last_hdr_rtrn)
5390Sstevel@tonic-gate {
5400Sstevel@tonic-gate int length;
5410Sstevel@tonic-gate int exthdrlength;
5420Sstevel@tonic-gate uint8_t nexthdr;
5430Sstevel@tonic-gate uint8_t *whereptr;
5440Sstevel@tonic-gate ip6_hbh_t *hbhhdr;
5450Sstevel@tonic-gate ip6_dest_t *desthdr;
5460Sstevel@tonic-gate ip6_rthdr_t *rthdr;
5470Sstevel@tonic-gate ip6_frag_t *fraghdr;
5480Sstevel@tonic-gate uint8_t *endptr;
5490Sstevel@tonic-gate
5500Sstevel@tonic-gate length = sizeof (ip6_t);
5510Sstevel@tonic-gate
5520Sstevel@tonic-gate whereptr = ((uint8_t *)&ip6h[1]); /* point to next hdr */
5530Sstevel@tonic-gate endptr = ((uint8_t *)ip6h) + pkt_len;
5540Sstevel@tonic-gate
5550Sstevel@tonic-gate nexthdr = ip6h->ip6_nxt;
5560Sstevel@tonic-gate *last_hdr_rtrn = IPPROTO_NONE;
5570Sstevel@tonic-gate
5580Sstevel@tonic-gate if (whereptr >= endptr)
5590Sstevel@tonic-gate return (length);
5600Sstevel@tonic-gate
5610Sstevel@tonic-gate while (whereptr < endptr) {
5620Sstevel@tonic-gate *last_hdr_rtrn = nexthdr;
5630Sstevel@tonic-gate switch (nexthdr) {
5640Sstevel@tonic-gate case IPPROTO_HOPOPTS:
5650Sstevel@tonic-gate hbhhdr = (ip6_hbh_t *)whereptr;
5660Sstevel@tonic-gate exthdrlength = 8 * (hbhhdr->ip6h_len + 1);
5670Sstevel@tonic-gate if ((uchar_t *)hbhhdr + exthdrlength > endptr)
5680Sstevel@tonic-gate return (length);
5690Sstevel@tonic-gate nexthdr = hbhhdr->ip6h_nxt;
5700Sstevel@tonic-gate length += exthdrlength;
5710Sstevel@tonic-gate break;
5720Sstevel@tonic-gate
5730Sstevel@tonic-gate case IPPROTO_DSTOPTS:
5740Sstevel@tonic-gate desthdr = (ip6_dest_t *)whereptr;
5750Sstevel@tonic-gate exthdrlength = 8 * (desthdr->ip6d_len + 1);
5760Sstevel@tonic-gate if ((uchar_t *)desthdr + exthdrlength > endptr)
5770Sstevel@tonic-gate return (length);
5780Sstevel@tonic-gate nexthdr = desthdr->ip6d_nxt;
5790Sstevel@tonic-gate length += exthdrlength;
5800Sstevel@tonic-gate break;
5810Sstevel@tonic-gate
5820Sstevel@tonic-gate case IPPROTO_ROUTING:
5830Sstevel@tonic-gate rthdr = (ip6_rthdr_t *)whereptr;
5840Sstevel@tonic-gate exthdrlength = 8 * (rthdr->ip6r_len + 1);
5850Sstevel@tonic-gate if ((uchar_t *)rthdr + exthdrlength > endptr)
5860Sstevel@tonic-gate return (length);
5870Sstevel@tonic-gate nexthdr = rthdr->ip6r_nxt;
5880Sstevel@tonic-gate length += exthdrlength;
5890Sstevel@tonic-gate break;
5900Sstevel@tonic-gate
5910Sstevel@tonic-gate case IPPROTO_FRAGMENT:
5920Sstevel@tonic-gate /* LINTED E_BAD_PTR_CAST_ALIGN */
5930Sstevel@tonic-gate fraghdr = (ip6_frag_t *)whereptr;
5940Sstevel@tonic-gate if ((uchar_t *)&fraghdr[1] > endptr)
5950Sstevel@tonic-gate return (length);
5960Sstevel@tonic-gate nexthdr = fraghdr->ip6f_nxt;
5970Sstevel@tonic-gate length += sizeof (struct ip6_frag);
5980Sstevel@tonic-gate break;
5990Sstevel@tonic-gate
6000Sstevel@tonic-gate case IPPROTO_NONE:
6010Sstevel@tonic-gate default:
6020Sstevel@tonic-gate return (length);
6030Sstevel@tonic-gate }
6040Sstevel@tonic-gate whereptr = (uint8_t *)ip6h + length;
6050Sstevel@tonic-gate }
6060Sstevel@tonic-gate *last_hdr_rtrn = nexthdr;
6070Sstevel@tonic-gate
6080Sstevel@tonic-gate return (length);
6090Sstevel@tonic-gate }
6100Sstevel@tonic-gate
6110Sstevel@tonic-gate /*
6120Sstevel@tonic-gate * convert an ICMP6 "type" field to a printable string.
6130Sstevel@tonic-gate */
6140Sstevel@tonic-gate static char *
pr_type6(uchar_t type)6150Sstevel@tonic-gate pr_type6(uchar_t type)
6160Sstevel@tonic-gate {
6170Sstevel@tonic-gate static struct icmptype_table ttab6[] = {
6180Sstevel@tonic-gate {ICMP6_DST_UNREACH, "Dest Unreachable"},
6190Sstevel@tonic-gate {ICMP6_PACKET_TOO_BIG, "Packet Too Big"},
6200Sstevel@tonic-gate {ICMP6_TIME_EXCEEDED, "Time Exceeded"},
6210Sstevel@tonic-gate {ICMP6_PARAM_PROB, "Param Problem"},
6220Sstevel@tonic-gate {ICMP6_ECHO_REQUEST, "Echo Request"},
6230Sstevel@tonic-gate {ICMP6_ECHO_REPLY, "Echo Reply"},
6240Sstevel@tonic-gate {MLD_LISTENER_QUERY, "Multicast Listener Query"},
6250Sstevel@tonic-gate {MLD_LISTENER_REPORT, "Multicast Listener Report"},
6260Sstevel@tonic-gate {MLD_LISTENER_REDUCTION, "Multicast Listener Done"},
6270Sstevel@tonic-gate {ND_ROUTER_SOLICIT, "Router Solicitation"},
6280Sstevel@tonic-gate {ND_ROUTER_ADVERT, "Router Advertisement"},
6290Sstevel@tonic-gate {ND_NEIGHBOR_SOLICIT, "Neighbor Solicitation"},
6300Sstevel@tonic-gate {ND_NEIGHBOR_ADVERT, "Neighbor Advertisement"},
6310Sstevel@tonic-gate {ND_REDIRECT, "Redirect Message"}
6320Sstevel@tonic-gate };
6330Sstevel@tonic-gate int i = 0;
6340Sstevel@tonic-gate
6350Sstevel@tonic-gate for (i = 0; i < A_CNT(ttab6); i++) {
6360Sstevel@tonic-gate if (ttab6[i].type == type)
6370Sstevel@tonic-gate return (ttab6[i].message);
6380Sstevel@tonic-gate }
6390Sstevel@tonic-gate
6400Sstevel@tonic-gate return ("OUT-OF-RANGE");
6410Sstevel@tonic-gate }
6420Sstevel@tonic-gate
6430Sstevel@tonic-gate
6440Sstevel@tonic-gate /*
6450Sstevel@tonic-gate * print the IPv6 src address of the reply packet
6460Sstevel@tonic-gate */
6470Sstevel@tonic-gate void
print_addr6(uchar_t * buf,int cc,struct sockaddr * from)6480Sstevel@tonic-gate print_addr6(uchar_t *buf, int cc, struct sockaddr *from)
6490Sstevel@tonic-gate {
6500Sstevel@tonic-gate /* LINTED E_BAD_PTR_CAST_ALIGN */
6510Sstevel@tonic-gate struct sockaddr_in6 *from_in6 = (struct sockaddr_in6 *)from;
6520Sstevel@tonic-gate ip6_t *ip;
6530Sstevel@tonic-gate union any_in_addr ip_addr;
6540Sstevel@tonic-gate char *resolved_name;
6550Sstevel@tonic-gate char temp_buf[INET6_ADDRSTRLEN]; /* use for inet_ntop() */
6560Sstevel@tonic-gate
6570Sstevel@tonic-gate ip_addr.addr6 = from_in6->sin6_addr;
6580Sstevel@tonic-gate
6590Sstevel@tonic-gate /* LINTED E_BAD_PTR_CAST_ALIGN */
6600Sstevel@tonic-gate ip = (ip6_t *)buf;
6610Sstevel@tonic-gate
6620Sstevel@tonic-gate (void) inet_ntop(AF_INET6, &(from_in6->sin6_addr), temp_buf,
6630Sstevel@tonic-gate sizeof (temp_buf));
6640Sstevel@tonic-gate if (!nflag)
6650Sstevel@tonic-gate resolved_name = inet_name(&ip_addr, AF_INET6);
6660Sstevel@tonic-gate /*
6670Sstevel@tonic-gate * If the IPv6 address cannot be resolved to hostname, inet_name()
6680Sstevel@tonic-gate * returns the IPv6 address as a string. In that case, we choose not
6690Sstevel@tonic-gate * to print it twice. This saves us space on display.
6700Sstevel@tonic-gate */
6710Sstevel@tonic-gate if (nflag || (strcmp(temp_buf, resolved_name) == 0))
6720Sstevel@tonic-gate Printf(" %s", temp_buf);
6730Sstevel@tonic-gate else
6740Sstevel@tonic-gate Printf(" %s (%s)", resolved_name, temp_buf);
6750Sstevel@tonic-gate
6760Sstevel@tonic-gate if (verbose) {
6770Sstevel@tonic-gate Printf(" %d bytes to %s", cc, inet_ntop(AF_INET6,
6780Sstevel@tonic-gate (const void *) &(ip->ip6_dst), temp_buf,
6790Sstevel@tonic-gate sizeof (temp_buf)));
6800Sstevel@tonic-gate }
6810Sstevel@tonic-gate }
6820Sstevel@tonic-gate
6830Sstevel@tonic-gate /*
6840Sstevel@tonic-gate * ICMP6 messages which doesn't mean we got the target, or we got a gateway, are
6850Sstevel@tonic-gate * processed here. It returns _B_TRUE if it's some sort of 'unreachable'.
6860Sstevel@tonic-gate */
6870Sstevel@tonic-gate boolean_t
print_icmp_other6(uchar_t type,uchar_t code)6880Sstevel@tonic-gate print_icmp_other6(uchar_t type, uchar_t code)
6890Sstevel@tonic-gate {
6900Sstevel@tonic-gate boolean_t unreach = _B_FALSE;
6910Sstevel@tonic-gate
6920Sstevel@tonic-gate switch (type) {
6930Sstevel@tonic-gate
6940Sstevel@tonic-gate /* this corresponds to "ICMP_UNREACH_NEEDFRAG" in ICMP */
6950Sstevel@tonic-gate case ICMP6_PACKET_TOO_BIG:
6960Sstevel@tonic-gate unreach = _B_TRUE;
6970Sstevel@tonic-gate Printf(" !B");
6980Sstevel@tonic-gate break;
6990Sstevel@tonic-gate
7000Sstevel@tonic-gate case ICMP6_PARAM_PROB:
7010Sstevel@tonic-gate /* this corresponds to "ICMP_UNREACH_PROTOCOL" in ICMP */
7020Sstevel@tonic-gate if (code == ICMP6_PARAMPROB_NEXTHEADER) {
7030Sstevel@tonic-gate unreach = _B_TRUE;
7040Sstevel@tonic-gate Printf(" !R");
7050Sstevel@tonic-gate }
7060Sstevel@tonic-gate break;
7070Sstevel@tonic-gate
7080Sstevel@tonic-gate case ICMP6_DST_UNREACH:
7090Sstevel@tonic-gate switch (code) {
7100Sstevel@tonic-gate case ICMP6_DST_UNREACH_NOPORT:
7110Sstevel@tonic-gate break;
7120Sstevel@tonic-gate
7130Sstevel@tonic-gate case ICMP6_DST_UNREACH_NOROUTE:
7140Sstevel@tonic-gate unreach = _B_TRUE;
7150Sstevel@tonic-gate Printf(" !H");
7160Sstevel@tonic-gate break;
7170Sstevel@tonic-gate
7180Sstevel@tonic-gate case ICMP6_DST_UNREACH_ADMIN:
7190Sstevel@tonic-gate unreach = _B_TRUE;
7200Sstevel@tonic-gate Printf(" !X");
7210Sstevel@tonic-gate break;
7220Sstevel@tonic-gate
7230Sstevel@tonic-gate case ICMP6_DST_UNREACH_ADDR:
7240Sstevel@tonic-gate unreach = _B_TRUE;
7250Sstevel@tonic-gate Printf(" !A");
7260Sstevel@tonic-gate break;
7270Sstevel@tonic-gate
7280Sstevel@tonic-gate case ICMP6_DST_UNREACH_NOTNEIGHBOR:
7290Sstevel@tonic-gate unreach = _B_TRUE;
7300Sstevel@tonic-gate Printf(" !E");
7310Sstevel@tonic-gate break;
7320Sstevel@tonic-gate
7330Sstevel@tonic-gate default:
7340Sstevel@tonic-gate unreach = _B_TRUE;
7350Sstevel@tonic-gate Printf(" !<%d>", code);
7360Sstevel@tonic-gate break;
7370Sstevel@tonic-gate }
7380Sstevel@tonic-gate break;
7390Sstevel@tonic-gate default:
7400Sstevel@tonic-gate break;
7410Sstevel@tonic-gate }
7420Sstevel@tonic-gate
7430Sstevel@tonic-gate return (unreach);
7440Sstevel@tonic-gate }
745