xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_icmp.c (revision 4564:534db075062d)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*4564Swy83408  * Common Development and Distribution License (the "License").
6*4564Swy83408  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*4564Swy83408  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include <stdio.h>
290Sstevel@tonic-gate #include <string.h>
300Sstevel@tonic-gate #include <sys/types.h>
310Sstevel@tonic-gate #include <sys/socket.h>
320Sstevel@tonic-gate #include <net/if.h>
330Sstevel@tonic-gate #include <sys/stropts.h>
340Sstevel@tonic-gate #include <sys/sysmacros.h>
350Sstevel@tonic-gate #include <netinet/in_systm.h>
360Sstevel@tonic-gate #include <netinet/in.h>
370Sstevel@tonic-gate #include <netinet/ip.h>
380Sstevel@tonic-gate #include <netinet/ip_icmp.h>
390Sstevel@tonic-gate #include <netinet/udp.h>
400Sstevel@tonic-gate #include <netinet/tcp.h>
410Sstevel@tonic-gate #include <netinet/icmp6.h>
420Sstevel@tonic-gate #include <netinet/ip6.h>
430Sstevel@tonic-gate #include <inet/ip.h>
440Sstevel@tonic-gate #include <inet/ip6.h>
450Sstevel@tonic-gate #include <arpa/inet.h>
460Sstevel@tonic-gate #include <netdb.h>
470Sstevel@tonic-gate #include "snoop.h"
480Sstevel@tonic-gate #include "snoop_mip.h"
490Sstevel@tonic-gate 
500Sstevel@tonic-gate static void interpret_options(char *, int);
510Sstevel@tonic-gate static void interpret_mldv2qry(icmp6_t *, int);
520Sstevel@tonic-gate static void interpret_mldv2rpt(icmp6_t *, int);
530Sstevel@tonic-gate 
540Sstevel@tonic-gate 
550Sstevel@tonic-gate /* Mobile-IP routines from snoop_mip.c */
560Sstevel@tonic-gate extern void interpret_icmp_mip_ext(uchar_t *, int);
570Sstevel@tonic-gate extern const char *get_mip_adv_desc(uint8_t);
580Sstevel@tonic-gate 
590Sstevel@tonic-gate /* Router advertisement message structure. */
600Sstevel@tonic-gate struct icmp_ra_addr {
610Sstevel@tonic-gate 	uint32_t addr;
620Sstevel@tonic-gate 	uint32_t preference;
630Sstevel@tonic-gate };
640Sstevel@tonic-gate 
650Sstevel@tonic-gate /*ARGSUSED*/
660Sstevel@tonic-gate void
670Sstevel@tonic-gate interpret_icmp(int flags, struct icmp *icmp, int iplen, int ilen)
680Sstevel@tonic-gate {
690Sstevel@tonic-gate 	char *pt, *pc, *px;
700Sstevel@tonic-gate 	char *line;
710Sstevel@tonic-gate 	char buff[67627];	/* Router adv. can have 256 routers ....   */
720Sstevel@tonic-gate 				/* Each router has a name 256 char long .. */
730Sstevel@tonic-gate 	char extbuff[MAXHOSTNAMELEN + 1];
740Sstevel@tonic-gate 	struct udphdr *orig_uhdr;
750Sstevel@tonic-gate 	int num_rtr_addrs = 0;
760Sstevel@tonic-gate 	extern char *prot_nest_prefix;
770Sstevel@tonic-gate 
780Sstevel@tonic-gate 	if (ilen < ICMP_MINLEN)
790Sstevel@tonic-gate 		return;		/* incomplete header */
800Sstevel@tonic-gate 
810Sstevel@tonic-gate 	pt = "Unknown";
820Sstevel@tonic-gate 	pc = "";
830Sstevel@tonic-gate 	px = "";
840Sstevel@tonic-gate 
850Sstevel@tonic-gate 	switch (icmp->icmp_type) {
860Sstevel@tonic-gate 	case ICMP_ECHOREPLY:
870Sstevel@tonic-gate 		pt = "Echo reply";
880Sstevel@tonic-gate 		(void) sprintf(buff, "ID: %d Sequence number: %d",
890Sstevel@tonic-gate 		    ntohs(icmp->icmp_id), ntohs(icmp->icmp_seq));
900Sstevel@tonic-gate 		pc = buff;
910Sstevel@tonic-gate 		break;
920Sstevel@tonic-gate 	case ICMP_UNREACH:
930Sstevel@tonic-gate 		pt = "Destination unreachable";
940Sstevel@tonic-gate 		switch (icmp->icmp_code) {
950Sstevel@tonic-gate 		case ICMP_UNREACH_NET:
960Sstevel@tonic-gate 			if (ilen >= ICMP_ADVLENMIN) {
970Sstevel@tonic-gate 				(void) sprintf(buff, "Net %s unreachable",
980Sstevel@tonic-gate 				    addrtoname(AF_INET,
990Sstevel@tonic-gate 				    &icmp->icmp_ip.ip_dst));
1000Sstevel@tonic-gate 				pc = buff;
1010Sstevel@tonic-gate 			} else {
1020Sstevel@tonic-gate 				pc = "Bad net";
1030Sstevel@tonic-gate 			}
1040Sstevel@tonic-gate 			break;
1050Sstevel@tonic-gate 		case ICMP_UNREACH_HOST:
1060Sstevel@tonic-gate 			if (ilen >= ICMP_ADVLENMIN) {
1070Sstevel@tonic-gate 				(void) sprintf(buff, "Host %s unreachable",
1080Sstevel@tonic-gate 				    addrtoname(AF_INET,
1090Sstevel@tonic-gate 				    &icmp->icmp_ip.ip_dst));
1100Sstevel@tonic-gate 				pc = buff;
1110Sstevel@tonic-gate 			} else {
1120Sstevel@tonic-gate 				pc = "Bad host";
1130Sstevel@tonic-gate 			}
1140Sstevel@tonic-gate 			break;
1150Sstevel@tonic-gate 		case ICMP_UNREACH_PROTOCOL:
1160Sstevel@tonic-gate 			if (ilen >= ICMP_ADVLENMIN) {
1170Sstevel@tonic-gate 				(void) sprintf(buff, "Bad protocol %d",
1180Sstevel@tonic-gate 				    icmp->icmp_ip.ip_p);
1190Sstevel@tonic-gate 				pc = buff;
1200Sstevel@tonic-gate 			} else {
1210Sstevel@tonic-gate 				pc = "Bad protocol";
1220Sstevel@tonic-gate 			}
1230Sstevel@tonic-gate 			break;
1240Sstevel@tonic-gate 		case ICMP_UNREACH_PORT:
1250Sstevel@tonic-gate 			if (ilen >= ICMP_ADVLENMIN) {
1260Sstevel@tonic-gate 				orig_uhdr = (struct udphdr *)((uchar_t *)icmp +
1270Sstevel@tonic-gate 				    ICMP_MINLEN + icmp->icmp_ip.ip_hl * 4);
1280Sstevel@tonic-gate 				switch (icmp->icmp_ip.ip_p) {
1290Sstevel@tonic-gate 				case IPPROTO_TCP:
1300Sstevel@tonic-gate 					(void) sprintf(buff, "TCP port %d"
1310Sstevel@tonic-gate 					    " unreachable",
1320Sstevel@tonic-gate 					    ntohs(orig_uhdr->uh_dport));
1330Sstevel@tonic-gate 					pc = buff;
1340Sstevel@tonic-gate 					break;
1350Sstevel@tonic-gate 				case IPPROTO_UDP:
1360Sstevel@tonic-gate 					(void) sprintf(buff, "UDP port %d"
1370Sstevel@tonic-gate 					    " unreachable",
1380Sstevel@tonic-gate 					    ntohs(orig_uhdr->uh_dport));
1390Sstevel@tonic-gate 					pc = buff;
1400Sstevel@tonic-gate 					break;
1410Sstevel@tonic-gate 				default:
1420Sstevel@tonic-gate 					pc = "Port unreachable";
1430Sstevel@tonic-gate 					break;
1440Sstevel@tonic-gate 				}
1450Sstevel@tonic-gate 			} else {
1460Sstevel@tonic-gate 				pc = "Bad port";
1470Sstevel@tonic-gate 			}
1480Sstevel@tonic-gate 			break;
1490Sstevel@tonic-gate 		case ICMP_UNREACH_NEEDFRAG:
1500Sstevel@tonic-gate 			if (ntohs(icmp->icmp_nextmtu) != 0) {
1510Sstevel@tonic-gate 				(void) sprintf(buff, "Needed to fragment:"
1520Sstevel@tonic-gate 				    " next hop MTU = %d",
1530Sstevel@tonic-gate 				    ntohs(icmp->icmp_nextmtu));
1540Sstevel@tonic-gate 				pc = buff;
1550Sstevel@tonic-gate 			} else {
1560Sstevel@tonic-gate 				pc = "Needed to fragment";
1570Sstevel@tonic-gate 			}
1580Sstevel@tonic-gate 			break;
1590Sstevel@tonic-gate 		case ICMP_UNREACH_SRCFAIL:
1600Sstevel@tonic-gate 			pc = "Source route failed";
1610Sstevel@tonic-gate 			break;
1620Sstevel@tonic-gate 		case ICMP_UNREACH_NET_UNKNOWN:
1630Sstevel@tonic-gate 			pc = "Unknown network";
1640Sstevel@tonic-gate 			break;
1650Sstevel@tonic-gate 		case ICMP_UNREACH_HOST_UNKNOWN:
1660Sstevel@tonic-gate 			pc = "Unknown host";
1670Sstevel@tonic-gate 			break;
1680Sstevel@tonic-gate 		case ICMP_UNREACH_ISOLATED:
1690Sstevel@tonic-gate 			pc = "Source host isolated";
1700Sstevel@tonic-gate 			break;
1710Sstevel@tonic-gate 		case ICMP_UNREACH_NET_PROHIB:
1720Sstevel@tonic-gate 			pc = "Net administratively prohibited";
1730Sstevel@tonic-gate 			break;
1740Sstevel@tonic-gate 		case ICMP_UNREACH_HOST_PROHIB:
1750Sstevel@tonic-gate 			pc = "Host administratively prohibited";
1760Sstevel@tonic-gate 			break;
1770Sstevel@tonic-gate 		case ICMP_UNREACH_TOSNET:
1780Sstevel@tonic-gate 			pc = "Net unreachable for this TOS";
1790Sstevel@tonic-gate 			break;
1800Sstevel@tonic-gate 		case ICMP_UNREACH_TOSHOST:
1810Sstevel@tonic-gate 			pc = "Host unreachable for this TOS";
1820Sstevel@tonic-gate 			break;
1830Sstevel@tonic-gate 		case ICMP_UNREACH_FILTER_PROHIB:
1840Sstevel@tonic-gate 			pc = "Communication administratively prohibited";
1850Sstevel@tonic-gate 			break;
1860Sstevel@tonic-gate 		case ICMP_UNREACH_HOST_PRECEDENCE:
1870Sstevel@tonic-gate 			pc = "Host precedence violation";
1880Sstevel@tonic-gate 			break;
1890Sstevel@tonic-gate 		case ICMP_UNREACH_PRECEDENCE_CUTOFF:
1900Sstevel@tonic-gate 			pc = "Precedence cutoff in effect";
1910Sstevel@tonic-gate 			break;
1920Sstevel@tonic-gate 		default:
1930Sstevel@tonic-gate 			break;
1940Sstevel@tonic-gate 		}
1950Sstevel@tonic-gate 		break;
1960Sstevel@tonic-gate 	case ICMP_SOURCEQUENCH:
1970Sstevel@tonic-gate 		pt = "Packet lost, slow down";
1980Sstevel@tonic-gate 		break;
1990Sstevel@tonic-gate 	case ICMP_REDIRECT:
2000Sstevel@tonic-gate 		pt = "Redirect";
2010Sstevel@tonic-gate 		switch (icmp->icmp_code) {
2020Sstevel@tonic-gate 		case ICMP_REDIRECT_NET:
2030Sstevel@tonic-gate 			pc = "for network";
2040Sstevel@tonic-gate 			break;
2050Sstevel@tonic-gate 		case ICMP_REDIRECT_HOST:
2060Sstevel@tonic-gate 			pc = "for host";
2070Sstevel@tonic-gate 			break;
2080Sstevel@tonic-gate 		case ICMP_REDIRECT_TOSNET:
2090Sstevel@tonic-gate 			pc = "for tos and net";
2100Sstevel@tonic-gate 			break;
2110Sstevel@tonic-gate 		case ICMP_REDIRECT_TOSHOST:
2120Sstevel@tonic-gate 			pc = "for tos and host";
2130Sstevel@tonic-gate 			break;
2140Sstevel@tonic-gate 		default:
2150Sstevel@tonic-gate 			break;
2160Sstevel@tonic-gate 		}
2170Sstevel@tonic-gate 		(void) sprintf(buff, "%s %s to %s",
2180Sstevel@tonic-gate 			pc, addrtoname(AF_INET, &icmp->icmp_ip.ip_dst),
2190Sstevel@tonic-gate 			addrtoname(AF_INET, &icmp->icmp_gwaddr));
2200Sstevel@tonic-gate 		pc = buff;
2210Sstevel@tonic-gate 		break;
2220Sstevel@tonic-gate 	case ICMP_ECHO:
2230Sstevel@tonic-gate 		pt = "Echo request";
2240Sstevel@tonic-gate 		(void) sprintf(buff, "ID: %d Sequence number: %d",
2250Sstevel@tonic-gate 		    ntohs(icmp->icmp_id), ntohs(icmp->icmp_seq));
2260Sstevel@tonic-gate 		pc = buff;
2270Sstevel@tonic-gate 		break;
2280Sstevel@tonic-gate 	case ICMP_ROUTERADVERT:
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate #define	icmp_num_addrs	icmp_hun.ih_rtradv.irt_num_addrs
2310Sstevel@tonic-gate #define	icmp_wpa	icmp_hun.ih_rtradv.irt_wpa
2320Sstevel@tonic-gate #define	icmp_lifetime	icmp_hun.ih_rtradv.irt_lifetime
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate 		pt = "Router advertisement";
2350Sstevel@tonic-gate 		(void) sprintf(buff, "Lifetime %ds [%d]:",
2360Sstevel@tonic-gate 		    ntohs(icmp->icmp_lifetime), icmp->icmp_num_addrs);
2370Sstevel@tonic-gate 		if (icmp->icmp_wpa == 2) {
2380Sstevel@tonic-gate 			struct icmp_ra_addr *ra;
2390Sstevel@tonic-gate 			char ra_buf[MAXHOSTNAMELEN + 32];
2400Sstevel@tonic-gate 			char ra_ext_buf[50];
2410Sstevel@tonic-gate 			struct in_addr sin;
2420Sstevel@tonic-gate 			int icmp_ra_len;
2430Sstevel@tonic-gate 			int i;
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 			/* Cannot trust anything from the network... */
2460Sstevel@tonic-gate 			num_rtr_addrs = MIN((ilen - ICMP_MINLEN) / 8,
2470Sstevel@tonic-gate 			    icmp->icmp_num_addrs);
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate 			ra = (struct icmp_ra_addr *)icmp->icmp_data;
2500Sstevel@tonic-gate 			for (i = 0; i < num_rtr_addrs; i++) {
2510Sstevel@tonic-gate 				sin.s_addr = ra->addr;
2520Sstevel@tonic-gate 				(void) snprintf(ra_buf, sizeof (ra_buf),
2530Sstevel@tonic-gate 				    " {%s %u}",
2540Sstevel@tonic-gate 				    addrtoname(AF_INET, &sin),
2550Sstevel@tonic-gate 				    ntohl(ra->preference));
2560Sstevel@tonic-gate 				if (strlcat(buff, ra_buf, sizeof (buff)) >=
2570Sstevel@tonic-gate 					sizeof (buff)) {
2580Sstevel@tonic-gate 					buff[sizeof (buff) -
2590Sstevel@tonic-gate 					    strlen("<Too Long>)")] = '\0';
2600Sstevel@tonic-gate 					(void) strlcat(buff, "<Too Long>",
2610Sstevel@tonic-gate 						sizeof (buff));
2620Sstevel@tonic-gate 					break;
2630Sstevel@tonic-gate 				}
2640Sstevel@tonic-gate 				ra++;
2650Sstevel@tonic-gate 			}
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate 			icmp_ra_len = ICMP_MINLEN + num_rtr_addrs *
2680Sstevel@tonic-gate 			    sizeof (struct icmp_ra_addr);
2690Sstevel@tonic-gate 			if (ilen > icmp_ra_len) {
2700Sstevel@tonic-gate 				int curr_len = ilen - icmp_ra_len;
2710Sstevel@tonic-gate 				int ocurr_len;
2720Sstevel@tonic-gate 				exthdr_t *exthdr = (exthdr_t *)ra;
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate 				extbuff[0] = '\0';
2750Sstevel@tonic-gate 
2760Sstevel@tonic-gate 				while (curr_len > 0) {
2770Sstevel@tonic-gate 				    /* Append Mobile-IP description */
2780Sstevel@tonic-gate 				    (void) snprintf(ra_ext_buf,
2790Sstevel@tonic-gate 					sizeof (ra_ext_buf), ", %s",
2800Sstevel@tonic-gate 					get_mip_adv_desc(exthdr->type));
2810Sstevel@tonic-gate 				    (void) strlcat(extbuff, ra_ext_buf,
2820Sstevel@tonic-gate 					sizeof (extbuff));
2830Sstevel@tonic-gate 
2840Sstevel@tonic-gate 				    /* Special case for padding */
2850Sstevel@tonic-gate 				    if (exthdr->type ==
2860Sstevel@tonic-gate 					ICMP_ADV_MSG_PADDING_EXT) {
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate 					curr_len--;
2890Sstevel@tonic-gate 					exthdr = (exthdr_t *)
2900Sstevel@tonic-gate 						((char *)exthdr + 1);
2910Sstevel@tonic-gate 					continue;
2920Sstevel@tonic-gate 				    }
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate 				    /* else normal extension */
2950Sstevel@tonic-gate 				    ocurr_len = curr_len;
2960Sstevel@tonic-gate 				    curr_len -= sizeof (*exthdr) +
2970Sstevel@tonic-gate 							exthdr->length;
2980Sstevel@tonic-gate 				    /* detect bad length */
2990Sstevel@tonic-gate 				    if (ocurr_len < curr_len)
3000Sstevel@tonic-gate 						break;
3010Sstevel@tonic-gate 				    exthdr = (exthdr_t *)
3020Sstevel@tonic-gate 						((char *)exthdr +
3030Sstevel@tonic-gate 						sizeof (*exthdr) +
3040Sstevel@tonic-gate 						exthdr->length);
3050Sstevel@tonic-gate 				}
3060Sstevel@tonic-gate 				px = extbuff;
3070Sstevel@tonic-gate 			}
3080Sstevel@tonic-gate 			pc = buff;
3090Sstevel@tonic-gate 		}
3100Sstevel@tonic-gate 		break;
3110Sstevel@tonic-gate 	case ICMP_ROUTERSOLICIT:
3120Sstevel@tonic-gate 		pt = "Router solicitation";
3130Sstevel@tonic-gate 		break;
3140Sstevel@tonic-gate 	case ICMP_TIMXCEED:
3150Sstevel@tonic-gate 		pt = "Time exceeded";
3160Sstevel@tonic-gate 		switch (icmp->icmp_code) {
3170Sstevel@tonic-gate 		case ICMP_TIMXCEED_INTRANS:
3180Sstevel@tonic-gate 			pc = "in transit";
3190Sstevel@tonic-gate 			break;
3200Sstevel@tonic-gate 		case ICMP_TIMXCEED_REASS:
3210Sstevel@tonic-gate 			pc = "in reassembly";
3220Sstevel@tonic-gate 			break;
3230Sstevel@tonic-gate 		default:
3240Sstevel@tonic-gate 			break;
3250Sstevel@tonic-gate 		}
3260Sstevel@tonic-gate 		break;
3270Sstevel@tonic-gate 	case ICMP_PARAMPROB:
3280Sstevel@tonic-gate 		pt = "IP parameter problem";
3290Sstevel@tonic-gate 		switch (icmp->icmp_code) {
3300Sstevel@tonic-gate 		case ICMP_PARAMPROB_OPTABSENT:
3310Sstevel@tonic-gate 			pc = "Required option missing";
3320Sstevel@tonic-gate 			break;
3330Sstevel@tonic-gate 		case ICMP_PARAMPROB_BADLENGTH:
3340Sstevel@tonic-gate 			pc = "Bad length";
3350Sstevel@tonic-gate 			break;
3360Sstevel@tonic-gate 		case 0: /* Should this be the default? */
3370Sstevel@tonic-gate 			(void) sprintf(buff, "Problem at octet %d\n",
3380Sstevel@tonic-gate 			    icmp->icmp_pptr);
3390Sstevel@tonic-gate 			pc = buff;
3400Sstevel@tonic-gate 		default:
3410Sstevel@tonic-gate 			break;
3420Sstevel@tonic-gate 		}
3430Sstevel@tonic-gate 		break;
3440Sstevel@tonic-gate 	case ICMP_TSTAMP:
3450Sstevel@tonic-gate 		pt = "Timestamp request";
3460Sstevel@tonic-gate 		break;
3470Sstevel@tonic-gate 	case ICMP_TSTAMPREPLY:
3480Sstevel@tonic-gate 		pt = "Timestamp reply";
3490Sstevel@tonic-gate 		break;
3500Sstevel@tonic-gate 	case ICMP_IREQ:
3510Sstevel@tonic-gate 		pt = "Information request";
3520Sstevel@tonic-gate 		break;
3530Sstevel@tonic-gate 	case ICMP_IREQREPLY:
3540Sstevel@tonic-gate 		pt = "Information reply";
3550Sstevel@tonic-gate 		break;
3560Sstevel@tonic-gate 	case ICMP_MASKREQ:
3570Sstevel@tonic-gate 		pt = "Address mask request";
3580Sstevel@tonic-gate 		break;
3590Sstevel@tonic-gate 	case ICMP_MASKREPLY:
3600Sstevel@tonic-gate 		pt = "Address mask reply";
3610Sstevel@tonic-gate 		(void) sprintf(buff, "Mask = 0x%x", ntohl(icmp->icmp_mask));
3620Sstevel@tonic-gate 		pc = buff;
3630Sstevel@tonic-gate 		break;
3640Sstevel@tonic-gate 	default:
3650Sstevel@tonic-gate 		break;
3660Sstevel@tonic-gate 	}
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate 	if (flags & F_SUM) {
3690Sstevel@tonic-gate 		line = get_sum_line();
3700Sstevel@tonic-gate 		if (*pc) {
3710Sstevel@tonic-gate 			if (*px) {
3720Sstevel@tonic-gate 				(void) sprintf(line, "ICMP %s (%s)%s",
3730Sstevel@tonic-gate 				    pt, pc, px);
3740Sstevel@tonic-gate 			} else {
3750Sstevel@tonic-gate 				(void) sprintf(line, "ICMP %s (%s)", pt, pc);
3760Sstevel@tonic-gate 			}
3770Sstevel@tonic-gate 		} else {
3780Sstevel@tonic-gate 			(void) sprintf(line, "ICMP %s", pt);
3790Sstevel@tonic-gate 		}
3800Sstevel@tonic-gate 	}
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate 	if (flags & F_DTAIL) {
3830Sstevel@tonic-gate 		show_header("ICMP:  ", "ICMP Header", ilen);
3840Sstevel@tonic-gate 		show_space();
3850Sstevel@tonic-gate 		(void) sprintf(get_line(0, 0), "Type = %d (%s)",
3860Sstevel@tonic-gate 		    icmp->icmp_type, pt);
3870Sstevel@tonic-gate 		if (*pc) {
3880Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0), "Code = %d (%s)",
3890Sstevel@tonic-gate 			    icmp->icmp_code, pc);
3900Sstevel@tonic-gate 		} else {
3910Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0), "Code = %d",
3920Sstevel@tonic-gate 			    icmp->icmp_code);
3930Sstevel@tonic-gate 		}
3940Sstevel@tonic-gate 		(void) sprintf(get_line(0, 0), "Checksum = %x",
3950Sstevel@tonic-gate 		    ntohs(icmp->icmp_cksum));
3960Sstevel@tonic-gate 
3970Sstevel@tonic-gate 		if (icmp->icmp_type == ICMP_UNREACH ||
3980Sstevel@tonic-gate 		    icmp->icmp_type == ICMP_REDIRECT) {
3990Sstevel@tonic-gate 			if (ilen > 28) {
4000Sstevel@tonic-gate 				show_space();
4010Sstevel@tonic-gate 				(void) sprintf(get_line(0, 0),
4020Sstevel@tonic-gate 				    "[ subject header follows ]");
4030Sstevel@tonic-gate 				show_space();
4040Sstevel@tonic-gate 				prot_nest_prefix = "ICMP:";
4050Sstevel@tonic-gate 				(void) interpret_ip(flags,
4060Sstevel@tonic-gate 				    (struct ip *)icmp->icmp_data, 28);
4070Sstevel@tonic-gate 				prot_nest_prefix = "";
4080Sstevel@tonic-gate 			}
4090Sstevel@tonic-gate 		} else if (icmp->icmp_type == ICMP_PARAMPROB) {
4100Sstevel@tonic-gate 			if (ilen > 28) {
4110Sstevel@tonic-gate 				show_space();
4120Sstevel@tonic-gate 				(void) sprintf(get_line(0, 0),
4130Sstevel@tonic-gate 				    "[ subject header follows ]");
4140Sstevel@tonic-gate 				show_space();
4150Sstevel@tonic-gate 				prot_nest_prefix = "ICMP:";
4160Sstevel@tonic-gate 				(void) interpret_ip(flags,
4170Sstevel@tonic-gate 				    (struct ip *)icmp->icmp_data, 28);
4180Sstevel@tonic-gate 				prot_nest_prefix = "";
4190Sstevel@tonic-gate 			}
4200Sstevel@tonic-gate 		} else if (icmp->icmp_type == ICMP_ROUTERADVERT) {
4210Sstevel@tonic-gate 			if (icmp->icmp_wpa == 2) {
4220Sstevel@tonic-gate 				int icmp_ra_len;
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate 				show_space();
4250Sstevel@tonic-gate 				icmp_ra_len = ICMP_MINLEN +
4260Sstevel@tonic-gate 				    num_rtr_addrs *
4270Sstevel@tonic-gate 					sizeof (struct icmp_ra_addr);
4280Sstevel@tonic-gate 				prot_nest_prefix = "";
4290Sstevel@tonic-gate 				if (ilen > icmp_ra_len) {
4300Sstevel@tonic-gate 					interpret_icmp_mip_ext(
4310Sstevel@tonic-gate 					    (uchar_t *)icmp + icmp_ra_len,
4320Sstevel@tonic-gate 					    ilen - icmp_ra_len);
4330Sstevel@tonic-gate 				}
4340Sstevel@tonic-gate 			}
4350Sstevel@tonic-gate 		}
4360Sstevel@tonic-gate 		show_space();
4370Sstevel@tonic-gate 	}
4380Sstevel@tonic-gate }
4390Sstevel@tonic-gate 
4400Sstevel@tonic-gate /*ARGSUSED*/
4410Sstevel@tonic-gate void
4420Sstevel@tonic-gate interpret_icmpv6(flags, icmp6, iplen, ilen)
4430Sstevel@tonic-gate 	int flags;
4440Sstevel@tonic-gate 	icmp6_t *icmp6;
4450Sstevel@tonic-gate 	int iplen, ilen;
4460Sstevel@tonic-gate {
4470Sstevel@tonic-gate 	char *pt, *pc;
4480Sstevel@tonic-gate 	char *line;
4490Sstevel@tonic-gate 	extern char *prot_nest_prefix;
4500Sstevel@tonic-gate 	char addrstr[INET6_ADDRSTRLEN];
4510Sstevel@tonic-gate 	char buff[2048];
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate 	if (ilen < ICMP6_MINLEN)
4540Sstevel@tonic-gate 		return;		/* incomplete header */
4550Sstevel@tonic-gate 
4560Sstevel@tonic-gate 	pt = "Unknown";
4570Sstevel@tonic-gate 	pc = "";
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate 	switch (icmp6->icmp6_type) {
4600Sstevel@tonic-gate 	case ICMP6_DST_UNREACH:
4610Sstevel@tonic-gate 		pt = "Destination unreachable";
4620Sstevel@tonic-gate 		switch (icmp6->icmp6_code) {
4630Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_NOROUTE:
4640Sstevel@tonic-gate 			pc = "No route to destination";
4650Sstevel@tonic-gate 			break;
4660Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_ADMIN:
4670Sstevel@tonic-gate 			pc = "Communication administratively prohibited";
4680Sstevel@tonic-gate 			break;
4690Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_ADDR:
4700Sstevel@tonic-gate 			pc = "Address unreachable";
4710Sstevel@tonic-gate 			break;
4720Sstevel@tonic-gate 		case ICMP6_DST_UNREACH_NOPORT:
4730Sstevel@tonic-gate 			if (ilen >= ICMP6_MINLEN + IPV6_HDR_LEN +
4740Sstevel@tonic-gate 				sizeof (struct udphdr)) {
4750Sstevel@tonic-gate 
4760Sstevel@tonic-gate 				ip6_t *orig_ip6hdr = (ip6_t *)&icmp6[1];
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate 				switch (orig_ip6hdr->ip6_nxt) {
4790Sstevel@tonic-gate 				case IPPROTO_TCP: {
4800Sstevel@tonic-gate 					struct tcphdr *orig_thdr =
4810Sstevel@tonic-gate 					    (struct tcphdr *)&orig_ip6hdr[1];
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate 					(void) sprintf(buff, "TCP port %hu"
4840Sstevel@tonic-gate 					    " unreachable",
4850Sstevel@tonic-gate 					    ntohs(orig_thdr->th_dport));
4860Sstevel@tonic-gate 					pc = buff;
4870Sstevel@tonic-gate 					break;
4880Sstevel@tonic-gate 				    }
4890Sstevel@tonic-gate 				case IPPROTO_UDP: {
4900Sstevel@tonic-gate 					struct udphdr *orig_uhdr =
4910Sstevel@tonic-gate 					    (struct udphdr *)&orig_ip6hdr[1];
4920Sstevel@tonic-gate 
4930Sstevel@tonic-gate 					(void) sprintf(buff, "UDP port %hu"
4940Sstevel@tonic-gate 					    " unreachable",
4950Sstevel@tonic-gate 					    ntohs(orig_uhdr->uh_dport));
4960Sstevel@tonic-gate 					pc = buff;
4970Sstevel@tonic-gate 					break;
4980Sstevel@tonic-gate 				    }
4990Sstevel@tonic-gate 				default:
5000Sstevel@tonic-gate 					pc = "Port unreachable";
5010Sstevel@tonic-gate 					break;
5020Sstevel@tonic-gate 				}
5030Sstevel@tonic-gate 			} else {
5040Sstevel@tonic-gate 				pc = "Bad port";
5050Sstevel@tonic-gate 			}
5060Sstevel@tonic-gate 			break;
5070Sstevel@tonic-gate 		default:
5080Sstevel@tonic-gate 			break;
5090Sstevel@tonic-gate 		}
5100Sstevel@tonic-gate 		break;
5110Sstevel@tonic-gate 	case ICMP6_PACKET_TOO_BIG:
5120Sstevel@tonic-gate 		pt = "Packet too big";
5130Sstevel@tonic-gate 		break;
5140Sstevel@tonic-gate 	case ND_REDIRECT:
5150Sstevel@tonic-gate 		pt = "Redirect";
5160Sstevel@tonic-gate 		break;
5170Sstevel@tonic-gate 	case ICMP6_TIME_EXCEEDED:
5180Sstevel@tonic-gate 		pt = "Time exceeded";
5190Sstevel@tonic-gate 		switch (icmp6->icmp6_code) {
5200Sstevel@tonic-gate 		case ICMP6_TIME_EXCEED_TRANSIT:
5210Sstevel@tonic-gate 			pc = "Hop limit exceeded in transit";
5220Sstevel@tonic-gate 			break;
5230Sstevel@tonic-gate 		case ICMP6_TIME_EXCEED_REASSEMBLY:
5240Sstevel@tonic-gate 			pc = "Fragment reassembly time exceeded";
5250Sstevel@tonic-gate 			break;
5260Sstevel@tonic-gate 		default:
5270Sstevel@tonic-gate 			break;
5280Sstevel@tonic-gate 		}
5290Sstevel@tonic-gate 		break;
5300Sstevel@tonic-gate 	case ICMP6_PARAM_PROB:
5310Sstevel@tonic-gate 		pt = "Parameter problem";
5320Sstevel@tonic-gate 		switch (icmp6->icmp6_code) {
5330Sstevel@tonic-gate 		case ICMP6_PARAMPROB_HEADER:
5340Sstevel@tonic-gate 			pc = "Erroneous header field";
5350Sstevel@tonic-gate 			break;
5360Sstevel@tonic-gate 		case ICMP6_PARAMPROB_NEXTHEADER:
5370Sstevel@tonic-gate 			pc = "Unrecognized next header type";
5380Sstevel@tonic-gate 			break;
5390Sstevel@tonic-gate 		case ICMP6_PARAMPROB_OPTION:
5400Sstevel@tonic-gate 			pc = "Unrecognized IPv6 option";
5410Sstevel@tonic-gate 			break;
5420Sstevel@tonic-gate 		}
5430Sstevel@tonic-gate 		break;
5440Sstevel@tonic-gate 	case ICMP6_ECHO_REQUEST:
5450Sstevel@tonic-gate 		pt = "Echo request";
5460Sstevel@tonic-gate 		(void) sprintf(buff, "ID: %d Sequence number: %d",
5470Sstevel@tonic-gate 		    ntohs(icmp6->icmp6_id), ntohs(icmp6->icmp6_seq));
5480Sstevel@tonic-gate 		pc = buff;
5490Sstevel@tonic-gate 		break;
5500Sstevel@tonic-gate 	case ICMP6_ECHO_REPLY:
5510Sstevel@tonic-gate 		pt = "Echo reply";
5520Sstevel@tonic-gate 		(void) sprintf(buff, "ID: %d Sequence number: %d",
5530Sstevel@tonic-gate 		    ntohs(icmp6->icmp6_id), ntohs(icmp6->icmp6_seq));
5540Sstevel@tonic-gate 		pc = buff;
5550Sstevel@tonic-gate 		break;
5560Sstevel@tonic-gate 	case MLD_LISTENER_QUERY:
5570Sstevel@tonic-gate 		if (ilen == MLD_MINLEN)
5580Sstevel@tonic-gate 			pt = "Group membership query - MLDv1";
5590Sstevel@tonic-gate 		else if (ilen >= MLD_V2_QUERY_MINLEN)
5600Sstevel@tonic-gate 			pt = "Group membership query - MLDv2";
5610Sstevel@tonic-gate 		else
5620Sstevel@tonic-gate 			pt = "Unknown membership query";
5630Sstevel@tonic-gate 		break;
5640Sstevel@tonic-gate 	case MLD_LISTENER_REPORT:
5650Sstevel@tonic-gate 		pt = "Group membership report - MLDv1";
5660Sstevel@tonic-gate 		break;
5670Sstevel@tonic-gate 	case MLD_LISTENER_REDUCTION:
5680Sstevel@tonic-gate 		pt = "Group membership termination - MLDv1";
5690Sstevel@tonic-gate 		break;
5700Sstevel@tonic-gate 	case MLD_V2_LISTENER_REPORT:
5710Sstevel@tonic-gate 		pt = "Group membership report - MLDv2";
5720Sstevel@tonic-gate 		break;
5730Sstevel@tonic-gate 	case ND_ROUTER_SOLICIT:
5740Sstevel@tonic-gate 		pt = "Router solicitation";
5750Sstevel@tonic-gate 		break;
5760Sstevel@tonic-gate 	case ND_ROUTER_ADVERT:
5770Sstevel@tonic-gate 		pt = "Router advertisement";
5780Sstevel@tonic-gate 		break;
5790Sstevel@tonic-gate 	case ND_NEIGHBOR_SOLICIT:
5800Sstevel@tonic-gate 		pt = "Neighbor solicitation";
5810Sstevel@tonic-gate 		break;
5820Sstevel@tonic-gate 	case ND_NEIGHBOR_ADVERT:
5830Sstevel@tonic-gate 		pt = "Neighbor advertisement";
5840Sstevel@tonic-gate 		break;
5850Sstevel@tonic-gate 	default:
5860Sstevel@tonic-gate 		break;
5870Sstevel@tonic-gate 	}
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 	if (flags & F_SUM) {
5900Sstevel@tonic-gate 		line = get_sum_line();
5910Sstevel@tonic-gate 		if (*pc)
5920Sstevel@tonic-gate 			(void) sprintf(line, "ICMPv6 %s (%s)", pt, pc);
5930Sstevel@tonic-gate 		else
5940Sstevel@tonic-gate 			(void) sprintf(line, "ICMPv6 %s", pt);
5950Sstevel@tonic-gate 	}
5960Sstevel@tonic-gate 
5970Sstevel@tonic-gate 	if (flags & F_DTAIL) {
5980Sstevel@tonic-gate 		show_header("ICMPv6:  ", "ICMPv6 Header", ilen);
5990Sstevel@tonic-gate 		show_space();
6000Sstevel@tonic-gate 		(void) sprintf(get_line(0, 0), "Type = %d (%s)",
6010Sstevel@tonic-gate 		    icmp6->icmp6_type, pt);
6020Sstevel@tonic-gate 		if (*pc)
6030Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0), "Code = %d (%s)",
6040Sstevel@tonic-gate 			    icmp6->icmp6_code, pc);
6050Sstevel@tonic-gate 		else
6060Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0), "Code = %d",
6070Sstevel@tonic-gate 			    icmp6->icmp6_code);
6080Sstevel@tonic-gate 		(void) sprintf(get_line(0, 0), "Checksum = %x",
6090Sstevel@tonic-gate 		    ntohs(icmp6->icmp6_cksum));
6100Sstevel@tonic-gate 
6110Sstevel@tonic-gate 		switch (icmp6->icmp6_type) {
6120Sstevel@tonic-gate 		case ICMP6_DST_UNREACH:
6130Sstevel@tonic-gate 			if (ilen > ICMP6_MINLEN + IPV6_HDR_LEN) {
6140Sstevel@tonic-gate 				show_space();
6150Sstevel@tonic-gate 				(void) sprintf(get_line(0, 0),
6160Sstevel@tonic-gate 				    "[ subject header follows ]");
6170Sstevel@tonic-gate 				show_space();
6180Sstevel@tonic-gate 				prot_nest_prefix = "ICMPv6:";
6190Sstevel@tonic-gate 				(void) interpret_ipv6(flags, (ip6_t *)&icmp6[1],
6200Sstevel@tonic-gate 				    ICMP6_MINLEN + IPV6_HDR_LEN);
6210Sstevel@tonic-gate 				prot_nest_prefix = "";
6220Sstevel@tonic-gate 			}
6230Sstevel@tonic-gate 			break;
6240Sstevel@tonic-gate 		case ICMP6_PACKET_TOO_BIG:
6250Sstevel@tonic-gate 			show_space();
6260Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0),
627*4564Swy83408 			    " Packet too big MTU = %d",
628*4564Swy83408 			    ntohl(icmp6->icmp6_mtu));
6290Sstevel@tonic-gate 			show_space();
6300Sstevel@tonic-gate 			break;
6310Sstevel@tonic-gate 		case ND_REDIRECT: {
6320Sstevel@tonic-gate 			nd_redirect_t *rd = (nd_redirect_t *)icmp6;
6330Sstevel@tonic-gate 
6340Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0), "Target address= %s",
6350Sstevel@tonic-gate 			    inet_ntop(AF_INET6, (char *)&rd->nd_rd_target,
6360Sstevel@tonic-gate 			    addrstr, INET6_ADDRSTRLEN));
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0),
6390Sstevel@tonic-gate 			    "Destination address= %s",
6400Sstevel@tonic-gate 			    inet_ntop(AF_INET6, (char *)&rd->nd_rd_dst,
6410Sstevel@tonic-gate 			    addrstr, INET6_ADDRSTRLEN));
6420Sstevel@tonic-gate 			show_space();
6430Sstevel@tonic-gate 			interpret_options((char *)icmp6 + sizeof (*rd),
6440Sstevel@tonic-gate 			    ilen - sizeof (*rd));
6450Sstevel@tonic-gate 			break;
6460Sstevel@tonic-gate 		}
6470Sstevel@tonic-gate 		case ND_NEIGHBOR_SOLICIT: {
6480Sstevel@tonic-gate 			struct nd_neighbor_solicit *ns;
6490Sstevel@tonic-gate 			if (ilen < sizeof (*ns))
6500Sstevel@tonic-gate 				break;
6510Sstevel@tonic-gate 			ns = (struct nd_neighbor_solicit *)icmp6;
6520Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0), "Target node = %s, %s",
6530Sstevel@tonic-gate 			    inet_ntop(AF_INET6, (char *)&ns->nd_ns_target,
6540Sstevel@tonic-gate 			    addrstr, INET6_ADDRSTRLEN),
6550Sstevel@tonic-gate 			    addrtoname(AF_INET6, &ns->nd_ns_target));
6560Sstevel@tonic-gate 			show_space();
6570Sstevel@tonic-gate 			interpret_options((char *)icmp6 + sizeof (*ns),
6580Sstevel@tonic-gate 			    ilen - sizeof (*ns));
6590Sstevel@tonic-gate 			break;
6600Sstevel@tonic-gate 		}
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate 		case ND_NEIGHBOR_ADVERT: {
6630Sstevel@tonic-gate 			struct nd_neighbor_advert *na;
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate 			if (ilen < sizeof (*na))
6660Sstevel@tonic-gate 				break;
6670Sstevel@tonic-gate 			na = (struct nd_neighbor_advert *)icmp6;
6680Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0), "Target node = %s, %s",
6690Sstevel@tonic-gate 			    inet_ntop(AF_INET6, (char *)&na->nd_na_target,
6700Sstevel@tonic-gate 			    addrstr, INET6_ADDRSTRLEN),
6710Sstevel@tonic-gate 			    addrtoname(AF_INET6, &na->nd_na_target));
6720Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0),
6730Sstevel@tonic-gate 			    "Router flag: %s, Solicited flag: %s, "
6740Sstevel@tonic-gate 			    "Override flag: %s",
6750Sstevel@tonic-gate 			    na->nd_na_flags_reserved & ND_NA_FLAG_ROUTER ?
6760Sstevel@tonic-gate 			    "SET" : "NOT SET",
6770Sstevel@tonic-gate 			    na->nd_na_flags_reserved & ND_NA_FLAG_SOLICITED ?
6780Sstevel@tonic-gate 			    "SET" : "NOT SET",
6790Sstevel@tonic-gate 			    na->nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE ?
6800Sstevel@tonic-gate 			    "SET" : "NOT SET");
6810Sstevel@tonic-gate 
6820Sstevel@tonic-gate 			show_space();
6830Sstevel@tonic-gate 			interpret_options((char *)icmp6 + sizeof (*na),
6840Sstevel@tonic-gate 			    ilen - sizeof (*na));
6850Sstevel@tonic-gate 		}
6860Sstevel@tonic-gate 		break;
6870Sstevel@tonic-gate 
6880Sstevel@tonic-gate 		case ND_ROUTER_SOLICIT: {
6890Sstevel@tonic-gate 			if (ilen < sizeof (struct nd_router_solicit))
6900Sstevel@tonic-gate 				break;
6910Sstevel@tonic-gate 			interpret_options(
6920Sstevel@tonic-gate 			    (char *)icmp6 + sizeof (struct nd_router_solicit),
6930Sstevel@tonic-gate 			    ilen - sizeof (struct nd_router_solicit));
6940Sstevel@tonic-gate 			break;
6950Sstevel@tonic-gate 		}
6960Sstevel@tonic-gate 
6970Sstevel@tonic-gate 		case ND_ROUTER_ADVERT: {
6980Sstevel@tonic-gate 			struct nd_router_advert *ra;
6990Sstevel@tonic-gate 
7000Sstevel@tonic-gate 			if (ilen < sizeof (*ra))
7010Sstevel@tonic-gate 				break;
7020Sstevel@tonic-gate 			ra = (struct nd_router_advert *)icmp6;
7030Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0),
7040Sstevel@tonic-gate 			    "Max hops= %d, Router lifetime= %d",
7050Sstevel@tonic-gate 			    ra->nd_ra_curhoplimit,
7060Sstevel@tonic-gate 			    ntohs(ra->nd_ra_router_lifetime));
7070Sstevel@tonic-gate 
7080Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0),
7090Sstevel@tonic-gate 			    "Managed addr conf flag: %s, Other conf flag: %s",
7100Sstevel@tonic-gate 			    ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED ?
7110Sstevel@tonic-gate 			    "SET" : "NOT SET",
7120Sstevel@tonic-gate 			    ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER ?
7130Sstevel@tonic-gate 			    "SET" : "NOT SET");
7140Sstevel@tonic-gate 
7150Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0),
7160Sstevel@tonic-gate 			    "Reachable time: %u, Reachable retrans time %u",
7170Sstevel@tonic-gate 			    ntohl(ra->nd_ra_reachable),
7180Sstevel@tonic-gate 			    ntohl(ra->nd_ra_retransmit));
7190Sstevel@tonic-gate 			show_space();
7200Sstevel@tonic-gate 
7210Sstevel@tonic-gate 			interpret_options((char *)icmp6 + sizeof (*ra),
7220Sstevel@tonic-gate 			    ilen - sizeof (*ra));
7230Sstevel@tonic-gate 			break;
7240Sstevel@tonic-gate 		}
7250Sstevel@tonic-gate 		case ICMP6_PARAM_PROB:
7260Sstevel@tonic-gate 			if (ilen < sizeof (*icmp6))
7270Sstevel@tonic-gate 				break;
7280Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0), "Ptr = %u",
7290Sstevel@tonic-gate 			    ntohl(icmp6->icmp6_pptr));
7300Sstevel@tonic-gate 			show_space();
7310Sstevel@tonic-gate 			break;
7320Sstevel@tonic-gate 
7330Sstevel@tonic-gate 		case MLD_LISTENER_QUERY: {
7340Sstevel@tonic-gate 			struct mld_hdr *mldg = (struct mld_hdr *)icmp6;
7350Sstevel@tonic-gate 
7360Sstevel@tonic-gate 			if (ilen < MLD_MINLEN)
7370Sstevel@tonic-gate 				break;
7380Sstevel@tonic-gate 
7390Sstevel@tonic-gate 			if (ilen >= MLD_V2_QUERY_MINLEN) {
7400Sstevel@tonic-gate 				interpret_mldv2qry(icmp6, ilen);
7410Sstevel@tonic-gate 			} else {
7420Sstevel@tonic-gate 				(void) snprintf(get_line(0, 0),
7430Sstevel@tonic-gate 				    get_line_remain(),
7440Sstevel@tonic-gate 				    "Multicast address= %s",
7450Sstevel@tonic-gate 				    inet_ntop(AF_INET6, mldg->mld_addr.s6_addr,
7460Sstevel@tonic-gate 				    addrstr, INET6_ADDRSTRLEN));
7470Sstevel@tonic-gate 			}
7480Sstevel@tonic-gate 			show_space();
7490Sstevel@tonic-gate 			break;
7500Sstevel@tonic-gate 		}
7510Sstevel@tonic-gate 
7520Sstevel@tonic-gate 		case MLD_LISTENER_REPORT:
7530Sstevel@tonic-gate 		case MLD_LISTENER_REDUCTION: {
7540Sstevel@tonic-gate 			struct mld_hdr *mldg;
7550Sstevel@tonic-gate 
7560Sstevel@tonic-gate 			if (ilen < sizeof (*mldg))
7570Sstevel@tonic-gate 				break;
7580Sstevel@tonic-gate 			mldg = (struct mld_hdr *)icmp6;
7590Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
7600Sstevel@tonic-gate 			    "Multicast address= %s", inet_ntop(AF_INET6,
7610Sstevel@tonic-gate 			    mldg->mld_addr.s6_addr, addrstr, INET6_ADDRSTRLEN));
7620Sstevel@tonic-gate 			show_space();
7630Sstevel@tonic-gate 			break;
7640Sstevel@tonic-gate 		}
7650Sstevel@tonic-gate 
7660Sstevel@tonic-gate 		case MLD_V2_LISTENER_REPORT: {
7670Sstevel@tonic-gate 			interpret_mldv2rpt(icmp6, ilen);
7680Sstevel@tonic-gate 			show_space();
7690Sstevel@tonic-gate 			break;
7700Sstevel@tonic-gate 		}
7710Sstevel@tonic-gate 
7720Sstevel@tonic-gate 		default:
7730Sstevel@tonic-gate 			break;
7740Sstevel@tonic-gate 		}
7750Sstevel@tonic-gate 	}
7760Sstevel@tonic-gate }
7770Sstevel@tonic-gate 
7780Sstevel@tonic-gate static void
7790Sstevel@tonic-gate interpret_options(optc, ilen)
7800Sstevel@tonic-gate 	char *optc;
7810Sstevel@tonic-gate 	int ilen;
7820Sstevel@tonic-gate {
7830Sstevel@tonic-gate #define	PREFIX_OPTION_LENGTH    4
7840Sstevel@tonic-gate #define	MTU_OPTION_LENGTH	1
7850Sstevel@tonic-gate 
7860Sstevel@tonic-gate #define	PREFIX_INFINITY		0xffffffffUL
7870Sstevel@tonic-gate 
7880Sstevel@tonic-gate 	struct nd_opt_hdr *opt;
7890Sstevel@tonic-gate 
7900Sstevel@tonic-gate 	for (; ilen >= sizeof (*opt); ) {
7910Sstevel@tonic-gate 		opt = (struct nd_opt_hdr *)optc;
7920Sstevel@tonic-gate 		if (opt->nd_opt_len == 0)
7930Sstevel@tonic-gate 			return;
7940Sstevel@tonic-gate 		switch (opt->nd_opt_type) {
7950Sstevel@tonic-gate 		case ND_OPT_SOURCE_LINKADDR:
7960Sstevel@tonic-gate 		case ND_OPT_TARGET_LINKADDR:
7970Sstevel@tonic-gate 		{
7980Sstevel@tonic-gate 			struct nd_opt_lla *lopt;
7990Sstevel@tonic-gate 			char	*buf, chbuf[128];
8000Sstevel@tonic-gate 			uint_t	addr_len;
8010Sstevel@tonic-gate 			int	i;
8020Sstevel@tonic-gate 
8030Sstevel@tonic-gate 			if (ilen < (int)opt->nd_opt_len * 8)
8040Sstevel@tonic-gate 				break;
8050Sstevel@tonic-gate 
8060Sstevel@tonic-gate 			buf = chbuf;
8070Sstevel@tonic-gate 
8080Sstevel@tonic-gate 			lopt = (struct nd_opt_lla *)opt;
8090Sstevel@tonic-gate 			if (lopt->nd_opt_lla_type == ND_OPT_SOURCE_LINKADDR) {
8100Sstevel@tonic-gate 				(void) sprintf(get_line(0, 0),
8110Sstevel@tonic-gate 				    "+++ ICMPv6 Source LL Addr option +++");
8120Sstevel@tonic-gate 			} else {
8130Sstevel@tonic-gate 				(void) sprintf(get_line(0, 0),
8140Sstevel@tonic-gate 				    "+++ ICMPv6 Target LL Addr option +++");
8150Sstevel@tonic-gate 			}
8160Sstevel@tonic-gate 
8170Sstevel@tonic-gate 			/*
8180Sstevel@tonic-gate 			 * The option length is in 8 octet units, and
8190Sstevel@tonic-gate 			 * includes the first two bytes (the type and
8200Sstevel@tonic-gate 			 * lenght fields) of the option.
8210Sstevel@tonic-gate 			 */
8220Sstevel@tonic-gate 			addr_len = lopt->nd_opt_lla_len * 8 - 2;
8230Sstevel@tonic-gate 			for (i = 0; i < addr_len; i++) {
8240Sstevel@tonic-gate 				snprintf(buf, sizeof (chbuf) - (buf - chbuf),
8250Sstevel@tonic-gate 				    "%x:", lopt->nd_opt_lla_hdw_addr[i]);
8260Sstevel@tonic-gate 				buf += strlen(buf);
8270Sstevel@tonic-gate 				if (buf >= &chbuf[sizeof (chbuf)]) {
8280Sstevel@tonic-gate 					buf = NULL;
8290Sstevel@tonic-gate 					chbuf[sizeof (chbuf) -
8300Sstevel@tonic-gate 					    strlen("<Too Long>)")] = '\0';
8310Sstevel@tonic-gate 					(void) strlcat(chbuf, "<Too Long>",
8320Sstevel@tonic-gate 						sizeof (chbuf));
8330Sstevel@tonic-gate 					break;
8340Sstevel@tonic-gate 				}
8350Sstevel@tonic-gate 			}
8360Sstevel@tonic-gate 			if (buf)
8370Sstevel@tonic-gate 				*(buf - 1) = '\0'; /* Erase last colon */
8380Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0),
8390Sstevel@tonic-gate 			    "Link Layer address: %s", chbuf);
8400Sstevel@tonic-gate 			show_space();
8410Sstevel@tonic-gate 			break;
8420Sstevel@tonic-gate 		}
8430Sstevel@tonic-gate 		case ND_OPT_MTU: {
8440Sstevel@tonic-gate 			struct nd_opt_mtu *mopt;
8450Sstevel@tonic-gate 			if (opt->nd_opt_len != MTU_OPTION_LENGTH ||
8460Sstevel@tonic-gate 			    ilen < sizeof (struct nd_opt_mtu))
8470Sstevel@tonic-gate 				break;
8480Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0),
8490Sstevel@tonic-gate 			    "+++ ICMPv6 MTU option +++");
8500Sstevel@tonic-gate 			mopt = (struct nd_opt_mtu *)opt;
8510Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0),
8520Sstevel@tonic-gate 			    "MTU = %u ", mopt->nd_opt_mtu_mtu);
8530Sstevel@tonic-gate 			show_space();
8540Sstevel@tonic-gate 			break;
8550Sstevel@tonic-gate 		}
8560Sstevel@tonic-gate 		case ND_OPT_PREFIX_INFORMATION: {
8570Sstevel@tonic-gate 			struct nd_opt_prefix_info *popt;
8580Sstevel@tonic-gate 			char validstr[30];
8590Sstevel@tonic-gate 			char preferredstr[30];
8600Sstevel@tonic-gate 			char prefixstr[INET6_ADDRSTRLEN];
8610Sstevel@tonic-gate 
8620Sstevel@tonic-gate 			if (opt->nd_opt_len != PREFIX_OPTION_LENGTH ||
8630Sstevel@tonic-gate 			    ilen < sizeof (struct nd_opt_prefix_info))
8640Sstevel@tonic-gate 				break;
8650Sstevel@tonic-gate 			popt = (struct nd_opt_prefix_info *)opt;
8660Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0),
8670Sstevel@tonic-gate 			    "+++ ICMPv6 Prefix option +++");
8680Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0),
8690Sstevel@tonic-gate 			    "Prefix length = %d ", popt->nd_opt_pi_prefix_len);
8700Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0),
8710Sstevel@tonic-gate 			    "Onlink flag: %s, Autonomous addr conf flag: %s",
8720Sstevel@tonic-gate 			    popt->nd_opt_pi_flags_reserved &
8730Sstevel@tonic-gate 			    ND_OPT_PI_FLAG_ONLINK ? "SET" : "NOT SET",
8740Sstevel@tonic-gate 			    popt->nd_opt_pi_flags_reserved &
8750Sstevel@tonic-gate 			    ND_OPT_PI_FLAG_AUTO ? "SET" : "NOT SET");
8760Sstevel@tonic-gate 
8770Sstevel@tonic-gate 			if (ntohl(popt->nd_opt_pi_valid_time) ==
8780Sstevel@tonic-gate 			    PREFIX_INFINITY)
8790Sstevel@tonic-gate 				sprintf(validstr, "INFINITY");
8800Sstevel@tonic-gate 			else
8810Sstevel@tonic-gate 				sprintf(validstr, "%lu",
8820Sstevel@tonic-gate 				    ntohl(popt->nd_opt_pi_valid_time));
8830Sstevel@tonic-gate 
8840Sstevel@tonic-gate 			if (ntohl(popt->nd_opt_pi_preferred_time) ==
8850Sstevel@tonic-gate 			    PREFIX_INFINITY)
8860Sstevel@tonic-gate 				sprintf(preferredstr, "INFINITY");
8870Sstevel@tonic-gate 			else
8880Sstevel@tonic-gate 				sprintf(preferredstr, "%lu",
8890Sstevel@tonic-gate 				    ntohl(popt->nd_opt_pi_preferred_time));
8900Sstevel@tonic-gate 
8910Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0),
8920Sstevel@tonic-gate 			    "Valid Lifetime %s, Preferred Lifetime %s",
8930Sstevel@tonic-gate 			    validstr, preferredstr);
8940Sstevel@tonic-gate 			(void) sprintf(get_line(0, 0), "Prefix %s",
8950Sstevel@tonic-gate 			    inet_ntop(AF_INET6,
8960Sstevel@tonic-gate 			    (char *)&popt->nd_opt_pi_prefix, prefixstr,
8970Sstevel@tonic-gate 			    INET6_ADDRSTRLEN));
8980Sstevel@tonic-gate 			show_space();
8990Sstevel@tonic-gate 		}
9000Sstevel@tonic-gate 		default:
9010Sstevel@tonic-gate 			break;
9020Sstevel@tonic-gate 		}
9030Sstevel@tonic-gate 		optc += opt->nd_opt_len * 8;
9040Sstevel@tonic-gate 		ilen -= opt->nd_opt_len * 8;
9050Sstevel@tonic-gate 	}
9060Sstevel@tonic-gate }
9070Sstevel@tonic-gate 
9080Sstevel@tonic-gate static void
9090Sstevel@tonic-gate interpret_mldv2qry(icmp6_t *icmp6, int ilen)
9100Sstevel@tonic-gate {
9110Sstevel@tonic-gate 	mld2q_t *qry;
9120Sstevel@tonic-gate 	in6_addr_t *src;
9130Sstevel@tonic-gate 	int rem = ilen;
9140Sstevel@tonic-gate 	int srccnt;
9150Sstevel@tonic-gate 	char addrstr[INET6_ADDRSTRLEN];
9160Sstevel@tonic-gate 
9170Sstevel@tonic-gate 	if (ilen < sizeof (*qry)) {
9180Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
9190Sstevel@tonic-gate 		    "Malformed MLD Query");
9200Sstevel@tonic-gate 		return;
9210Sstevel@tonic-gate 	}
9220Sstevel@tonic-gate 	qry = (mld2q_t *)icmp6;
9230Sstevel@tonic-gate 	rem -= sizeof (*qry);
9240Sstevel@tonic-gate 	srccnt = ntohs(qry->mld2q_numsrc);
9250Sstevel@tonic-gate 	(void) snprintf(get_line(0, 0), get_line_remain(),
9260Sstevel@tonic-gate 	    "Multicast address= %s", inet_ntop(AF_INET6,
9270Sstevel@tonic-gate 	    &qry->mld2q_addr.s6_addr, addrstr, INET6_ADDRSTRLEN));
9280Sstevel@tonic-gate 	(void) snprintf(get_line(0, 0), get_line_remain(),
9290Sstevel@tonic-gate 	    "%d Source Address%s:", srccnt, (srccnt == 1) ? "" : "es");
9300Sstevel@tonic-gate 
9310Sstevel@tonic-gate 	src = (in6_addr_t *)&qry[1];
9320Sstevel@tonic-gate 	while (srccnt > 0 && rem >= sizeof (*src)) {
9330Sstevel@tonic-gate 		rem -= sizeof (*src);
9340Sstevel@tonic-gate 
9350Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(), "    %s",
9360Sstevel@tonic-gate 		    inet_ntop(AF_INET6, src, addrstr, INET6_ADDRSTRLEN));
9370Sstevel@tonic-gate 
9380Sstevel@tonic-gate 		srccnt--;
9390Sstevel@tonic-gate 		src++;
9400Sstevel@tonic-gate 	}
9410Sstevel@tonic-gate }
9420Sstevel@tonic-gate 
9430Sstevel@tonic-gate #define	MAX_MLDV2_REPORT_TYPE	6
9440Sstevel@tonic-gate 
9450Sstevel@tonic-gate const char *mldv2rpt_types[] = {
9460Sstevel@tonic-gate 	"<unknown>",
9470Sstevel@tonic-gate 	"MODE_IS_INCLUDE",
9480Sstevel@tonic-gate 	"MODE_IS_EXCLUDE",
9490Sstevel@tonic-gate 	"CHANGE_TO_INCLUDE",
9500Sstevel@tonic-gate 	"CHANGE_TO_EXCLUDE",
9510Sstevel@tonic-gate 	"ALLOW_NEW_SOURCES",
9520Sstevel@tonic-gate 	"BLOCK_OLD_SOURCES",
9530Sstevel@tonic-gate };
9540Sstevel@tonic-gate 
9550Sstevel@tonic-gate static void
9560Sstevel@tonic-gate interpret_mldv2rpt(icmp6_t *icmp6, int ilen)
9570Sstevel@tonic-gate {
9580Sstevel@tonic-gate 	mld2r_t *rpt;
9590Sstevel@tonic-gate 	mld2mar_t *mar;
9600Sstevel@tonic-gate 	in6_addr_t *src;
9610Sstevel@tonic-gate 	int rem = ilen, auxlen;
9620Sstevel@tonic-gate 	uint16_t marcnt, srccnt;
9630Sstevel@tonic-gate 	char addrstr[INET6_ADDRSTRLEN];
9640Sstevel@tonic-gate 
9650Sstevel@tonic-gate 	if (ilen < sizeof (*rpt)) {
9660Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
9670Sstevel@tonic-gate 		    "Malformed MLDv2 Report");
9680Sstevel@tonic-gate 		return;
9690Sstevel@tonic-gate 	}
9700Sstevel@tonic-gate 	rpt = (mld2r_t *)icmp6;
9710Sstevel@tonic-gate 	mar = (mld2mar_t *)&rpt[1];
9720Sstevel@tonic-gate 	marcnt = ntohs(rpt->mld2r_nummar);
9730Sstevel@tonic-gate 	(void) snprintf(get_line(0, 0), get_line_remain(),
9740Sstevel@tonic-gate 	    "%d Multicast Address Record%s:", marcnt, (marcnt == 1) ? "" : "s");
9750Sstevel@tonic-gate 	rem -= sizeof (*rpt);
9760Sstevel@tonic-gate 	while (marcnt > 0 && rem >= sizeof (*mar)) {
9770Sstevel@tonic-gate 		rem -= sizeof (*mar);
9780Sstevel@tonic-gate 
9790Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
9800Sstevel@tonic-gate 		    "Multicast address= %s  type = %s", inet_ntop(AF_INET6,
9810Sstevel@tonic-gate 		    &mar->mld2mar_group.s6_addr, addrstr, INET6_ADDRSTRLEN),
9820Sstevel@tonic-gate 		    (mar->mld2mar_type > MAX_MLDV2_REPORT_TYPE) ?
9830Sstevel@tonic-gate 		    "<unknown>" : mldv2rpt_types[mar->mld2mar_type]);
9840Sstevel@tonic-gate 		srccnt = ntohs(mar->mld2mar_numsrc);
9850Sstevel@tonic-gate 		(void) snprintf(get_line(0, 0), get_line_remain(),
9860Sstevel@tonic-gate 		    "%d Source Address%s:", srccnt, (srccnt == 1) ? "" : "es");
9870Sstevel@tonic-gate 
9880Sstevel@tonic-gate 		src = (in6_addr_t *)&mar[1];
9890Sstevel@tonic-gate 		while (srccnt > 0 && rem >= sizeof (*src)) {
9900Sstevel@tonic-gate 			rem -= sizeof (*src);
9910Sstevel@tonic-gate 
9920Sstevel@tonic-gate 			(void) snprintf(get_line(0, 0), get_line_remain(),
9930Sstevel@tonic-gate 			    "    %s", inet_ntop(AF_INET6, src, addrstr,
9940Sstevel@tonic-gate 			    INET6_ADDRSTRLEN));
9950Sstevel@tonic-gate 
9960Sstevel@tonic-gate 			srccnt--;
9970Sstevel@tonic-gate 			src++;
9980Sstevel@tonic-gate 		}
9990Sstevel@tonic-gate 
10000Sstevel@tonic-gate 		marcnt--;
10010Sstevel@tonic-gate 		auxlen = mar->mld2mar_auxlen * 4;
10020Sstevel@tonic-gate 		rem -= auxlen;
10030Sstevel@tonic-gate 		mar = (mld2mar_t *)((uint8_t *)src + auxlen);
10040Sstevel@tonic-gate 	}
10050Sstevel@tonic-gate }
1006